abi_amd64.go raw
1 /*
2 * Copyright 2022 ByteDance Inc.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 package abi
18
19 import (
20 "fmt"
21 "reflect"
22 "unsafe"
23
24 x64 "github.com/bytedance/sonic/loader/internal/iasm/x86_64"
25 )
26
27 type (
28 Register = x64.Register
29 Register64 = x64.Register64
30 XMMRegister = x64.XMMRegister
31 Program = x64.Program
32 MemoryOperand = x64.MemoryOperand
33 Label = x64.Label
34 )
35
36 var (
37 Ptr = x64.Ptr
38 DefaultArch = x64.DefaultArch
39 CreateLabel = x64.CreateLabel
40 )
41
42 const (
43 RAX = x64.RAX
44 RSP = x64.RSP
45 RBP = x64.RBP
46 R12 = x64.R12
47 R14 = x64.R14
48 R15 = x64.R15
49 )
50
51 const (
52 PtrSize = 8 // pointer size
53 PtrAlign = 8 // pointer alignment
54 )
55
56 var iregOrderC = []Register{
57 x64.RDI,
58 x64.RSI,
59 x64.RDX,
60 x64.RCX,
61 x64.R8,
62 x64.R9,
63 }
64
65 var xregOrderC = []Register{
66 x64.XMM0,
67 x64.XMM1,
68 x64.XMM2,
69 x64.XMM3,
70 x64.XMM4,
71 x64.XMM5,
72 x64.XMM6,
73 x64.XMM7,
74 }
75
76 var (
77 intType = reflect.TypeOf(0)
78 ptrType = reflect.TypeOf(unsafe.Pointer(nil))
79 )
80
81 func (self *Frame) argv(i int) *MemoryOperand {
82 return Ptr(RSP, int32(self.Prev()+self.desc.Args[i].Mem))
83 }
84
85 // spillv is used for growstack spill registers
86 func (self *Frame) spillv(i int) *MemoryOperand {
87 // remain one slot for caller return pc
88 return Ptr(RSP, PtrSize+int32(self.desc.Args[i].Mem))
89 }
90
91 func (self *Frame) retv(i int) *MemoryOperand {
92 return Ptr(RSP, int32(self.Prev()+self.desc.Rets[i].Mem))
93 }
94
95 func (self *Frame) resv(i int) *MemoryOperand {
96 return Ptr(RSP, int32(self.Offs()-uint32((i+1)*PtrSize)))
97 }
98
99 func (self *Frame) emitGrowStack(p *Program, entry *Label) {
100 // spill all register arguments
101 for i, v := range self.desc.Args {
102 if v.InRegister {
103 if v.IsFloat == floatKind64 {
104 p.MOVSD(v.Reg, self.spillv(i))
105 } else if v.IsFloat == floatKind32 {
106 p.MOVSS(v.Reg, self.spillv(i))
107 } else {
108 p.MOVQ(v.Reg, self.spillv(i))
109 }
110 }
111 }
112
113 // call runtime.morestack_noctxt
114 p.MOVQ(F_morestack_noctxt, R12)
115 p.CALLQ(R12)
116 // load all register arguments
117 for i, v := range self.desc.Args {
118 if v.InRegister {
119 if v.IsFloat == floatKind64 {
120 p.MOVSD(self.spillv(i), v.Reg)
121 } else if v.IsFloat == floatKind32 {
122 p.MOVSS(self.spillv(i), v.Reg)
123 } else {
124 p.MOVQ(self.spillv(i), v.Reg)
125 }
126 }
127 }
128
129 // jump back to the function entry
130 p.JMP(entry)
131 }
132
133 func (self *Frame) GrowStackTextSize() uint32 {
134 p := DefaultArch.CreateProgram()
135 // spill all register arguments
136 for i, v := range self.desc.Args {
137 if v.InRegister {
138 if v.IsFloat == floatKind64 {
139 p.MOVSD(v.Reg, self.spillv(i))
140 } else if v.IsFloat == floatKind32 {
141 p.MOVSS(v.Reg, self.spillv(i))
142 } else {
143 p.MOVQ(v.Reg, self.spillv(i))
144 }
145 }
146 }
147
148 // call runtime.morestack_noctxt
149 p.MOVQ(F_morestack_noctxt, R12)
150 p.CALLQ(R12)
151 // load all register arguments
152 for i, v := range self.desc.Args {
153 if v.InRegister {
154 if v.IsFloat == floatKind64 {
155 p.MOVSD(self.spillv(i), v.Reg)
156 } else if v.IsFloat == floatKind32 {
157 p.MOVSS(self.spillv(i), v.Reg)
158 } else {
159 p.MOVQ(self.spillv(i), v.Reg)
160 }
161 }
162 }
163
164 // jump back to the function entry
165 l := CreateLabel("")
166 p.Link(l)
167 p.JMP(l)
168
169 return uint32(len(p.Assemble(0)))
170 }
171
172 func (self *Frame) emitPrologue(p *Program) {
173 p.SUBQ(self.Size(), RSP)
174 p.MOVQ(RBP, Ptr(RSP, int32(self.Offs())))
175 p.LEAQ(Ptr(RSP, int32(self.Offs())), RBP)
176 }
177
178 func (self *Frame) emitEpilogue(p *Program) {
179 p.MOVQ(Ptr(RSP, int32(self.Offs())), RBP)
180 p.ADDQ(self.Size(), RSP)
181 p.RET()
182 }
183
184 func (self *Frame) emitReserveRegs(p *Program) {
185 // spill reserved registers
186 for i, r := range ReservedRegs(self.ccall) {
187 switch r.(type) {
188 case Register64:
189 p.MOVQ(r, self.resv(i))
190 case XMMRegister:
191 p.MOVSD(r, self.resv(i))
192 default:
193 panic(fmt.Sprintf("unsupported register type %t to reserve", r))
194 }
195 }
196 }
197
198 func (self *Frame) emitSpillPtrs(p *Program) {
199 // spill pointer argument registers
200 for i, r := range self.desc.Args {
201 if r.InRegister && r.IsPointer {
202 p.MOVQ(r.Reg, self.argv(i))
203 }
204 }
205 }
206
207 func (self *Frame) emitClearPtrs(p *Program) {
208 // spill pointer argument registers
209 for i, r := range self.desc.Args {
210 if r.InRegister && r.IsPointer {
211 p.MOVQ(int64(0), self.argv(i))
212 }
213 }
214 }
215
216 func (self *Frame) emitCallC(p *Program, addr uintptr) {
217 p.MOVQ(addr, RAX)
218 p.CALLQ(RAX)
219 }
220
221 type floatKind uint8
222
223 const (
224 notFloatKind floatKind = iota
225 floatKind32
226 floatKind64
227 )
228
229 type Parameter struct {
230 InRegister bool
231 IsPointer bool
232 IsFloat floatKind
233 Reg Register
234 Mem uint32
235 Type reflect.Type
236 }
237
238 func mkIReg(vt reflect.Type, reg Register64) (p Parameter) {
239 p.Reg = reg
240 p.Type = vt
241 p.InRegister = true
242 p.IsPointer = isPointer(vt)
243 return
244 }
245
246 func isFloat(vt reflect.Type) floatKind {
247 switch vt.Kind() {
248 case reflect.Float32:
249 return floatKind32
250 case reflect.Float64:
251 return floatKind64
252 default:
253 return notFloatKind
254 }
255 }
256
257 func mkXReg(vt reflect.Type, reg XMMRegister) (p Parameter) {
258 p.Reg = reg
259 p.Type = vt
260 p.InRegister = true
261 p.IsFloat = isFloat(vt)
262 return
263 }
264
265 func mkStack(vt reflect.Type, mem uint32) (p Parameter) {
266 p.Mem = mem
267 p.Type = vt
268 p.InRegister = false
269 p.IsPointer = isPointer(vt)
270 p.IsFloat = isFloat(vt)
271 return
272 }
273
274 func (self Parameter) String() string {
275 if self.InRegister {
276 return fmt.Sprintf("[%%%s, Pointer(%v), Float(%v)]", self.Reg, self.IsPointer, self.IsFloat)
277 } else {
278 return fmt.Sprintf("[%d(FP), Pointer(%v), Float(%v)]", self.Mem, self.IsPointer, self.IsFloat)
279 }
280 }
281
282 func CallC(addr uintptr, fr Frame, maxStack uintptr) []byte {
283 p := DefaultArch.CreateProgram()
284
285 stack := CreateLabel("_stack_grow")
286 entry := CreateLabel("_entry")
287 p.Link(entry)
288 fr.emitStackCheck(p, stack, maxStack)
289 fr.emitPrologue(p)
290 fr.emitReserveRegs(p)
291 fr.emitSpillPtrs(p)
292 fr.emitExchangeArgs(p)
293 fr.emitCallC(p, addr)
294 fr.emitExchangeRets(p)
295 fr.emitRestoreRegs(p)
296 fr.emitEpilogue(p)
297 p.Link(stack)
298 fr.emitGrowStack(p, entry)
299
300 return p.Assemble(0)
301 }
302