1// SPDX-License-Identifier: Apache-2.0
2// SPDX-FileCopyrightText: 2025 The Ebitengine Authors
3
4package purego
5
6import (
7 "math"
8 "reflect"
9 "unsafe"
10)
11
12func getStruct(outType reflect.Type, syscall syscall15Args) (v reflect.Value) {
13 outSize := outType.Size()
14 switch {
15 case outSize == 0:
16 return reflect.New(outType).Elem()
17 case outSize <= 8:
18 r1 := syscall.a1
19 if isAllFloats, numFields := isAllSameFloat(outType); isAllFloats {
20 r1 = syscall.f1
21 if numFields == 2 {
22 r1 = packFloat32Pair(syscall.f1, syscall.f2)
23 }
24 }
25 return reflect.NewAt(outType, unsafe.Pointer(&struct{ a uintptr }{r1})).Elem()
26 case outSize <= 16:
27 r1, r2 := syscall.a1, syscall.a2
28 if isAllFloats, numFields := isAllSameFloat(outType); isAllFloats {
29 switch numFields {
30 case 4:
31 r1 = packFloat32Pair(syscall.f1, syscall.f2)
32 r2 = packFloat32Pair(syscall.f3, syscall.f4)
33 case 3:
34 r1 = packFloat32Pair(syscall.f1, syscall.f2)
35 r2 = syscall.f3
36 case 2:
37 r1 = syscall.f1
38 r2 = syscall.f2
39 default:
40 panic("not reached")
41 }
42 }
43 return reflect.NewAt(outType, unsafe.Pointer(&struct{ a, b uintptr }{r1, r2})).Elem()
44 default:
45 // create struct from the Go pointer created above
46 // weird pointer dereference to circumvent go vet
47 return reflect.NewAt(outType, *(*unsafe.Pointer)(unsafe.Pointer(&syscall.a1))).Elem()
48 }
49}
50
51const (
52 _NO_CLASS = 0b00
53 _FLOAT = 0b01
54 _INT = 0b11
55)
56
57func addStruct(v reflect.Value, numInts, numFloats, numStack *int, addInt, addFloat, addStack func(uintptr), keepAlive []any) []any {
58 if v.Type().Size() == 0 {
59 return keepAlive
60 }
61
62 if size := v.Type().Size(); size <= 16 {
63 placeRegisters(v, addFloat, addInt)
64 } else {
65 keepAlive = placeStack(v, keepAlive, addInt)
66 }
67 return keepAlive // the struct was allocated so don't panic
68}
69
70func placeRegisters(v reflect.Value, addFloat func(uintptr), addInt func(uintptr)) {
71 var val uint64
72 var shift byte
73 var flushed bool
74 class := _NO_CLASS
75 var place func(v reflect.Value)
76 place = func(v reflect.Value) {
77 var numFields int
78 if v.Kind() == reflect.Struct {
79 numFields = v.Type().NumField()
80 } else {
81 numFields = v.Type().Len()
82 }
83 for k := 0; k < numFields; k++ {
84 flushed = false
85 var f reflect.Value
86 if v.Kind() == reflect.Struct {
87 f = v.Field(k)
88 } else {
89 f = v.Index(k)
90 }
91 align := byte(f.Type().Align()*8 - 1)
92 shift = (shift + align) &^ align
93 if shift >= 64 {
94 flushPackedWord(&val, &shift, &flushed, class == _FLOAT, addFloat, addInt)
95 class = _NO_CLASS
96 }
97 switch f.Type().Kind() {
98 case reflect.Struct:
99 place(f)
100 case reflect.Bool:
101 if f.Bool() {
102 val |= 1 << shift
103 }
104 shift += 8
105 class |= _INT
106 case reflect.Uint8:
107 val |= f.Uint() << shift
108 shift += 8
109 class |= _INT
110 case reflect.Uint16:
111 val |= f.Uint() << shift
112 shift += 16
113 class |= _INT
114 case reflect.Uint32:
115 val |= f.Uint() << shift
116 shift += 32
117 class |= _INT
118 case reflect.Uint64, reflect.Uint, reflect.Uintptr:
119 addInt(uintptr(f.Uint()))
120 shift = 0
121 flushed = true
122 class = _NO_CLASS
123 case reflect.Int8:
124 val |= uint64(f.Int()&0xFF) << shift
125 shift += 8
126 class |= _INT
127 case reflect.Int16:
128 val |= uint64(f.Int()&0xFFFF) << shift
129 shift += 16
130 class |= _INT
131 case reflect.Int32:
132 val |= uint64(f.Int()&0xFFFF_FFFF) << shift
133 shift += 32
134 class |= _INT
135 case reflect.Int64, reflect.Int:
136 addInt(uintptr(f.Int()))
137 shift = 0
138 flushed = true
139 class = _NO_CLASS
140 case reflect.Float32:
141 if class == _FLOAT {
142 addFloat(uintptr(val))
143 val = 0
144 shift = 0
145 }
146 val |= uint64(math.Float32bits(float32(f.Float()))) << shift
147 shift += 32
148 class |= _FLOAT
149 case reflect.Float64:
150 addFloat(uintptr(math.Float64bits(float64(f.Float()))))
151 shift = 0
152 flushed = true
153 class = _NO_CLASS
154 case reflect.Ptr, reflect.UnsafePointer:
155 addInt(f.Pointer())
156 shift = 0
157 flushed = true
158 class = _NO_CLASS
159 case reflect.Array:
160 place(f)
161 default:
162 panic("purego: unsupported kind " + f.Kind().String())
163 }
164 }
165 }
166 place(v)
167 if !flushed {
168 if class == _FLOAT {
169 addFloat(uintptr(val))
170 } else {
171 addInt(uintptr(val))
172 }
173 }
174}
175
176func placeStack(v reflect.Value, keepAlive []any, addInt func(uintptr)) []any {
177 // Struct is too big to be placed in registers.
178 // Copy to heap and place the pointer in register
179 ptrStruct := reflect.New(v.Type())
180 ptrStruct.Elem().Set(v)
181 ptr := ptrStruct.Elem().Addr().UnsafePointer()
182 keepAlive = append(keepAlive, ptr)
183 addInt(uintptr(ptr))
184 return keepAlive
185}
186
187// shouldBundleStackArgs always returns false on loong64
188// since C-style stack argument bundling is only needed on Darwin ARM64.
189func shouldBundleStackArgs(v reflect.Value, numInts, numFloats int) bool {
190 return false
191}
192
193// structFitsInRegisters is not used on loong64.
194func structFitsInRegisters(val reflect.Value, tempNumInts, tempNumFloats int) (bool, int, int) {
195 panic("purego: structFitsInRegisters should not be called on loong64")
196}
197
198// collectStackArgs is not used on loong64.
199func collectStackArgs(args []reflect.Value, startIdx int, numInts, numFloats int,
200 keepAlive []any, addInt, addFloat, addStack func(uintptr),
201 pNumInts, pNumFloats, pNumStack *int) ([]reflect.Value, []any) {
202 panic("purego: collectStackArgs should not be called on loong64")
203}
204
205// bundleStackArgs is not used on loong64.
206func bundleStackArgs(stackArgs []reflect.Value, addStack func(uintptr)) {
207 panic("purego: bundleStackArgs should not be called on loong64")
208}
209
210func getCallbackStruct(inType reflect.Type, frame unsafe.Pointer, floatsN *int, intsN *int, stackSlot *int, stackByteOffset *uintptr) reflect.Value {
211 panic("purego: struct callback arguments are not supported on loong64")
212}
213
214func setStruct(a *callbackArgs, ret reflect.Value) {
215 panic("purego: struct returns are not supported on loong64")
216}