struct_amd64.go RawHistoryFolder
1// SPDX-License-Identifier: Apache-2.0
2// SPDX-FileCopyrightText: 2024 The Ebitengine Authors
3
4package purego
5
6import (
7 "math"
8 "reflect"
9)
10
11// https://refspecs.linuxbase.org/elf/x86_64-abi-0.99.pdf
12// https://gitlab.com/x86-psABIs/x86-64-ABI
13// Class determines where the 8 byte value goes.
14// Higher value classes win over lower value classes
15const (
16 _NO_CLASS = 0b0000
17 _SSE = 0b0001
18 _X87 = 0b0011 // long double not used in Go
19 _INTEGER = 0b0111
20 _MEMORY = 0b1111
21)
22
23func addStruct(v reflect.Value, numInts, numFloats, numStack *int, addInt, addFloat, addStack func(uintptr), keepAlive []interface{}) []interface{} {
24 if v.Type().Size() == 0 {
25 return keepAlive
26 }
27
28 // if greater than 64 bytes place on stack
29 if v.Type().Size() > 8*8 {
30 placeStack(v, addStack)
31 return keepAlive
32 }
33 var (
34 savedNumFloats = *numFloats
35 savedNumInts = *numInts
36 savedNumStack = *numStack
37 )
38 placeOnStack := postMerger(v.Type()) || !tryPlaceRegister(v, addFloat, addInt)
39 if placeOnStack {
40 // reset any values placed in registers
41 *numFloats = savedNumFloats
42 *numInts = savedNumInts
43 *numStack = savedNumStack
44 placeStack(v, addStack)
45 }
46 return keepAlive
47}
48
49func postMerger(t reflect.Type) bool {
50 // (c) If the size of the aggregate exceeds two eightbytes and the first eight- byte isn’t SSE or any other
51 // eightbyte isn’t SSEUP, the whole argument is passed in memory.
52 if t.Kind() != reflect.Struct {
53 return false
54 }
55 if t.Size() <= 2*8 {
56 return false
57 }
58 first := getFirst(t).Kind()
59 if first != reflect.Float32 && first != reflect.Float64 {
60 return false
61 }
62 return true
63}
64
65func getFirst(t reflect.Type) reflect.Type {
66 first := t.Field(0).Type
67 if first.Kind() == reflect.Struct {
68 return getFirst(first)
69 }
70 return first
71}
72
73func tryPlaceRegister(v reflect.Value, addFloat func(uintptr), addInt func(uintptr)) (ok bool) {
74 ok = true
75 var val uint64
76 var shift byte // # of bits to shift
77 var flushed bool
78 class := _NO_CLASS
79 var place func(v reflect.Value)
80 place = func(v reflect.Value) {
81 var numFields int
82 if v.Kind() == reflect.Struct {
83 numFields = v.Type().NumField()
84 } else {
85 numFields = v.Type().Len()
86 }
87
88 for i := 0; i < numFields; i++ {
89 flushed = false
90 var f reflect.Value
91 if v.Kind() == reflect.Struct {
92 f = v.Field(i)
93 } else {
94 f = v.Index(i)
95 }
96 if shift >= 64 {
97 shift = 0
98 flushed = true
99 if class == _SSE {
100 addFloat(uintptr(val))
101 } else {
102 addInt(uintptr(val))
103 }
104 class = _NO_CLASS
105 }
106 switch f.Kind() {
107 case reflect.Struct:
108 place(f)
109 case reflect.Bool:
110 if f.Bool() {
111 val |= 1
112 }
113 shift += 8
114 class |= _INTEGER
115 case reflect.Pointer:
116 ok = false
117 return
118 case reflect.Int8:
119 val |= uint64(f.Int()&0xFF) << shift
120 shift += 8
121 class |= _INTEGER
122 case reflect.Int16:
123 val |= uint64(f.Int()&0xFFFF) << shift
124 shift += 16
125 class |= _INTEGER
126 case reflect.Int32:
127 val |= uint64(f.Int()&0xFFFF_FFFF) << shift
128 shift += 32
129 class |= _INTEGER
130 case reflect.Int64:
131 addInt(uintptr(f.Int()))
132 shift = 0
133 class = _NO_CLASS
134 flushed = true
135 case reflect.Uint8:
136 val |= f.Uint() << shift
137 shift += 8
138 class |= _INTEGER
139 case reflect.Uint16:
140 val |= f.Uint() << shift
141 shift += 16
142 class |= _INTEGER
143 case reflect.Uint32:
144 val |= f.Uint() << shift
145 shift += 32
146 class |= _INTEGER
147 case reflect.Uint64:
148 addInt(uintptr(f.Uint()))
149 shift = 0
150 class = _NO_CLASS
151 flushed = true
152 case reflect.Float32:
153 val |= uint64(math.Float32bits(float32(f.Float()))) << shift
154 shift += 32
155 class |= _SSE
156 case reflect.Float64:
157 if v.Type().Size() > 16 {
158 ok = false
159 return
160 }
161 addFloat(uintptr(math.Float64bits(f.Float())))
162 class = _NO_CLASS
163 flushed = true
164 case reflect.Array:
165 place(f)
166 default:
167 panic("purego: unsupported kind " + f.Kind().String())
168 }
169 }
170 }
171
172 place(v)
173 if !flushed {
174 if class == _SSE {
175 addFloat(uintptr(val))
176 } else {
177 addInt(uintptr(val))
178 }
179 }
180 return ok
181}
182
183func placeStack(v reflect.Value, addStack func(uintptr)) {
184 for i := 0; i < v.Type().NumField(); i++ {
185 f := v.Field(i)
186 switch f.Kind() {
187 case reflect.Pointer:
188 addStack(f.Pointer())
189 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
190 addStack(uintptr(f.Int()))
191 case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
192 addStack(uintptr(f.Uint()))
193 case reflect.Float32:
194 addStack(uintptr(math.Float32bits(float32(f.Float()))))
195 case reflect.Float64:
196 addStack(uintptr(math.Float64bits(f.Float())))
197 case reflect.Struct:
198 placeStack(f, addStack)
199 default:
200 panic("purego: unsupported kind " + f.Kind().String())
201 }
202 }
203}