abi_regabi_amd64.go raw
1 //go:build go1.17
2 // +build go1.17
3
4 /*
5 * Copyright 2022 ByteDance Inc.
6 *
7 * Licensed under the Apache License, Version 2.0 (the "License");
8 * you may not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
10 *
11 * http://www.apache.org/licenses/LICENSE-2.0
12 *
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
18 */
19
20 /** Go Internal ABI implementation
21 *
22 * This module implements the function layout algorithm described by the Go internal ABI.
23 * See https://github.com/golang/go/blob/master/src/cmd/compile/abi-internal.md for more info.
24 */
25
26 package abi
27
28 import (
29 "fmt"
30 "reflect"
31
32 x64 "github.com/bytedance/sonic/loader/internal/iasm/x86_64"
33 )
34
35 /** Frame Structure of the Generated Function
36 FP +------------------------------+
37 | . . . |
38 | 2nd reg argument spill space |
39 + 1st reg argument spill space |
40 | <pointer-sized alignment> |
41 | . . . |
42 | 2nd stack-assigned result |
43 + 1st stack-assigned result |
44 | <pointer-sized alignment> |
45 | . . . |
46 | 2nd stack-assigned argument |
47 | 1st stack-assigned argument |
48 | stack-assigned receiver |
49 prev() +------------------------------+ (Previous Frame)
50 Return PC |
51 size() -------------------------------|
52 Saved RBP |
53 offs() -------------------------------|
54 1th Reserved Registers |
55 -------------------------------|
56 2th Reserved Registers |
57 -------------------------------|
58 Local Variables |
59 RSP -------------------------------|↓ lower addresses
60 */
61
62 const zeroRegGo = x64.XMM15
63
64 var iregOrderGo = [...]Register64{
65 x64.RAX, // RDI
66 x64.RBX, // RSI
67 x64.RCX, // RDX
68 x64.RDI, // RCX
69 x64.RSI, // R8
70 x64.R8, // R9
71 x64.R9,
72 x64.R10,
73 x64.R11,
74 }
75
76 var xregOrderGo = [...]XMMRegister{
77 x64.XMM0,
78 x64.XMM1,
79 x64.XMM2,
80 x64.XMM3,
81 x64.XMM4,
82 x64.XMM5,
83 x64.XMM6,
84 x64.XMM7,
85 x64.XMM8,
86 x64.XMM9,
87 x64.XMM10,
88 x64.XMM11,
89 x64.XMM12,
90 x64.XMM13,
91 x64.XMM14,
92 }
93
94 func ReservedRegs(callc bool) []Register {
95 if callc {
96 return nil
97 }
98 return []Register{
99 R14, // current goroutine
100 R15, // GOT reference
101 }
102 }
103
104 type stackAlloc struct {
105 s uint32
106 i int
107 x int
108 }
109
110 func (self *stackAlloc) reset() {
111 self.i, self.x = 0, 0
112 }
113
114 func (self *stackAlloc) ireg(vt reflect.Type) (p Parameter) {
115 p = mkIReg(vt, iregOrderGo[self.i])
116 self.i++
117 return
118 }
119
120 func (self *stackAlloc) xreg(vt reflect.Type) (p Parameter) {
121 p = mkXReg(vt, xregOrderGo[self.x])
122 self.x++
123 return
124 }
125
126 func (self *stackAlloc) stack(vt reflect.Type) (p Parameter) {
127 p = mkStack(vt, self.s)
128 self.s += uint32(vt.Size())
129 return
130 }
131
132 func (self *stackAlloc) spill(n uint32, a int) uint32 {
133 self.s = alignUp(self.s, a) + n
134 return self.s
135 }
136
137 func (self *stackAlloc) alloc(p []Parameter, vt reflect.Type) []Parameter {
138 nb := vt.Size()
139 vk := vt.Kind()
140
141 /* zero-sized objects are allocated on stack */
142 if nb == 0 {
143 return append(p, mkStack(intType, self.s))
144 }
145
146 /* check for value type */
147 switch vk {
148 case reflect.Bool:
149 return self.valloc(p, reflect.TypeOf(false))
150 case reflect.Int:
151 return self.valloc(p, intType)
152 case reflect.Int8:
153 return self.valloc(p, reflect.TypeOf(int8(0)))
154 case reflect.Int16:
155 return self.valloc(p, reflect.TypeOf(int16(0)))
156 case reflect.Int32:
157 return self.valloc(p, reflect.TypeOf(uint32(0)))
158 case reflect.Int64:
159 return self.valloc(p, reflect.TypeOf(int64(0)))
160 case reflect.Uint:
161 return self.valloc(p, reflect.TypeOf(uint(0)))
162 case reflect.Uint8:
163 return self.valloc(p, reflect.TypeOf(uint8(0)))
164 case reflect.Uint16:
165 return self.valloc(p, reflect.TypeOf(uint16(0)))
166 case reflect.Uint32:
167 return self.valloc(p, reflect.TypeOf(uint32(0)))
168 case reflect.Uint64:
169 return self.valloc(p, reflect.TypeOf(uint64(0)))
170 case reflect.Uintptr:
171 return self.valloc(p, reflect.TypeOf(uintptr(0)))
172 case reflect.Float32:
173 return self.valloc(p, reflect.TypeOf(float32(0)))
174 case reflect.Float64:
175 return self.valloc(p, reflect.TypeOf(float64(0)))
176 case reflect.Complex64:
177 panic("abi: go117: not implemented: complex64")
178 case reflect.Complex128:
179 panic("abi: go117: not implemented: complex128")
180 case reflect.Array:
181 panic("abi: go117: not implemented: arrays")
182 case reflect.Chan:
183 return self.valloc(p, reflect.TypeOf((chan int)(nil)))
184 case reflect.Func:
185 return self.valloc(p, reflect.TypeOf((func())(nil)))
186 case reflect.Map:
187 return self.valloc(p, reflect.TypeOf((map[int]int)(nil)))
188 case reflect.Ptr:
189 return self.valloc(p, reflect.TypeOf((*int)(nil)))
190 case reflect.UnsafePointer:
191 return self.valloc(p, ptrType)
192 case reflect.Interface:
193 return self.valloc(p, ptrType, ptrType)
194 case reflect.Slice:
195 return self.valloc(p, ptrType, intType, intType)
196 case reflect.String:
197 return self.valloc(p, ptrType, intType)
198 case reflect.Struct:
199 panic("abi: go117: not implemented: structs")
200 default:
201 panic("abi: invalid value type")
202 }
203 }
204
205 func (self *stackAlloc) valloc(p []Parameter, vts ...reflect.Type) []Parameter {
206 for _, vt := range vts {
207 enum := isFloat(vt)
208 if enum != notFloatKind && self.x < len(xregOrderGo) {
209 p = append(p, self.xreg(vt))
210 } else if enum == notFloatKind && self.i < len(iregOrderGo) {
211 p = append(p, self.ireg(vt))
212 } else {
213 p = append(p, self.stack(vt))
214 }
215 }
216 return p
217 }
218
219 func NewFunctionLayout(ft reflect.Type) FunctionLayout {
220 var sa stackAlloc
221 var fn FunctionLayout
222
223 /* assign every arguments */
224 for i := 0; i < ft.NumIn(); i++ {
225 fn.Args = sa.alloc(fn.Args, ft.In(i))
226 }
227
228 /* reset the register counter, and add a pointer alignment field */
229 sa.reset()
230
231 /* assign every return value */
232 for i := 0; i < ft.NumOut(); i++ {
233 fn.Rets = sa.alloc(fn.Rets, ft.Out(i))
234 }
235
236 sa.spill(0, PtrAlign)
237
238 /* assign spill slots */
239 for i := 0; i < len(fn.Args); i++ {
240 if fn.Args[i].InRegister {
241 fn.Args[i].Mem = sa.spill(PtrSize, PtrAlign) - PtrSize
242 }
243 }
244
245 /* add the final pointer alignment field */
246 fn.FP = sa.spill(0, PtrAlign)
247 return fn
248 }
249
250 func (self *Frame) emitExchangeArgs(p *Program) {
251 iregArgs := make([]Parameter, 0, len(self.desc.Args))
252 xregArgs := 0
253 for _, v := range self.desc.Args {
254 if v.InRegister {
255 if v.IsFloat != notFloatKind {
256 xregArgs += 1
257 } else {
258 iregArgs = append(iregArgs, v)
259 }
260 } else {
261 panic("not support stack-assgined arguments now")
262 }
263 }
264 if xregArgs > len(xregOrderC) {
265 panic("too many arguments, only support at most 8 integer register arguments now")
266 }
267
268 switch len(iregArgs) {
269 case 0, 1, 2, 3:
270 {
271 //Fast-Path: when arguments count are less than four, just exchange the registers
272 for i := 0; i < len(iregArgs); i++ {
273 p.MOVQ(iregOrderGo[i], iregOrderC[i])
274 }
275 }
276 case 4, 5, 6:
277 {
278 // need to spill 3th ~ regArgs registers before exchange
279 for i := 3; i < len(iregArgs); i++ {
280 arg := iregArgs[i]
281 // pointer args have already been spilled
282 if !arg.IsPointer {
283 p.MOVQ(iregOrderGo[i], Ptr(RSP, int32(self.Prev()+arg.Mem)))
284 }
285 }
286 p.MOVQ(iregOrderGo[0], iregOrderC[0])
287 p.MOVQ(iregOrderGo[1], iregOrderC[1])
288 p.MOVQ(iregOrderGo[2], iregOrderC[2])
289 for i := 3; i < len(iregArgs); i++ {
290 arg := iregArgs[i]
291 p.MOVQ(Ptr(RSP, int32(self.Prev()+arg.Mem)), iregOrderC[i])
292 }
293 }
294 default:
295 panic("too many arguments, only support at most 6 integer register arguments now")
296 }
297 }
298
299 func (self *Frame) emitStackCheck(p *Program, to *Label, maxStack uintptr) {
300 p.LEAQ(Ptr(RSP, int32(-(self.Size()+uint32(maxStack)))), R12)
301 p.CMPQ(Ptr(R14, _G_stackguard0), R12)
302 p.JBE(to)
303 }
304
305 func (self *Frame) StackCheckTextSize() uint32 {
306 p := DefaultArch.CreateProgram()
307 p.LEAQ(Ptr(RSP, int32(-(self.Size()))), R12)
308 p.CMPQ(Ptr(R14, _G_stackguard0), R12)
309 to := CreateLabel("")
310 p.Link(to)
311 p.JBE(to)
312 return uint32(len(p.Assemble(0)))
313 }
314
315 func (self *Frame) emitExchangeRets(p *Program) {
316 if len(self.desc.Rets) > 1 {
317 panic("too many results, only support one result now")
318 }
319 // store result
320 if len(self.desc.Rets) == 1 && !self.desc.Rets[0].InRegister {
321 if self.desc.Rets[0].IsFloat == floatKind64 {
322 p.MOVSD(xregOrderC[0], self.retv(0))
323 } else if self.desc.Rets[0].IsFloat == floatKind32 {
324 p.MOVSS(xregOrderC[0], self.retv(0))
325 } else {
326 p.MOVQ(RAX, self.retv(0))
327 }
328 }
329 }
330
331 func (self *Frame) emitRestoreRegs(p *Program) {
332 // load reserved registers
333 for i, r := range ReservedRegs(self.ccall) {
334 switch r.(type) {
335 case Register64:
336 p.MOVQ(self.resv(i), r)
337 case XMMRegister:
338 p.MOVSD(self.resv(i), r)
339 default:
340 panic(fmt.Sprintf("unsupported register type %t to reserve", r))
341 }
342 }
343 // zero xmm15 for go abi
344 p.XORPS(zeroRegGo, zeroRegGo)
345 }
346