v0.9.1 b9d062c

struct_arm64.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	"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 = syscall.f2<<32 | syscall.f1
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 = syscall.f2<<32 | syscall.f1
32				r2 = syscall.f4<<32 | syscall.f3
33			case 3:
34				r1 = syscall.f2<<32 | syscall.f1
35				r2 = syscall.f3
36			case 2:
37				r1 = syscall.f1
38				r2 = syscall.f2
39			default:
40				panic("unreachable")
41			}
42		}
43		return reflect.NewAt(outType, unsafe.Pointer(&struct{ a, b uintptr }{r1, r2})).Elem()
44	default:
45		if isAllFloats, numFields := isAllSameFloat(outType); isAllFloats && numFields <= 4 {
46			switch numFields {
47			case 4:
48				return reflect.NewAt(outType, unsafe.Pointer(&struct{ a, b, c, d uintptr }{syscall.f1, syscall.f2, syscall.f3, syscall.f4})).Elem()
49			case 3:
50				return reflect.NewAt(outType, unsafe.Pointer(&struct{ a, b, c uintptr }{syscall.f1, syscall.f2, syscall.f3})).Elem()
51			default:
52				panic("unreachable")
53			}
54		}
55		// create struct from the Go pointer created in arm64_r8
56		// weird pointer dereference to circumvent go vet
57		return reflect.NewAt(outType, *(*unsafe.Pointer)(unsafe.Pointer(&syscall.arm64_r8))).Elem()
58	}
59}
60
61// https://github.com/ARM-software/abi-aa/blob/main/sysvabi64/sysvabi64.rst
62const (
63	_NO_CLASS = 0b00
64	_FLOAT    = 0b01
65	_INT      = 0b11
66)
67
68func addStruct(v reflect.Value, numInts, numFloats, numStack *int, addInt, addFloat, addStack func(uintptr), keepAlive []any) []any {
69	if v.Type().Size() == 0 {
70		return keepAlive
71	}
72
73	if hva, hfa, size := isHVA(v.Type()), isHFA(v.Type()), v.Type().Size(); hva || hfa || size <= 16 {
74		// if this doesn't fit entirely in registers then
75		// each element goes onto the stack
76		if hfa && *numFloats+v.NumField() > numOfFloatRegisters {
77			*numFloats = numOfFloatRegisters
78		} else if hva && *numInts+v.NumField() > numOfIntegerRegisters() {
79			*numInts = numOfIntegerRegisters()
80		}
81
82		placeRegisters(v, addFloat, addInt)
83	} else {
84		keepAlive = placeStack(v, keepAlive, addInt)
85	}
86	return keepAlive // the struct was allocated so don't panic
87}
88
89func placeRegisters(v reflect.Value, addFloat func(uintptr), addInt func(uintptr)) {
90	var val uint64
91	var shift byte
92	var flushed bool
93	class := _NO_CLASS
94	var place func(v reflect.Value)
95	place = func(v reflect.Value) {
96		var numFields int
97		if v.Kind() == reflect.Struct {
98			numFields = v.Type().NumField()
99		} else {
100			numFields = v.Type().Len()
101		}
102		for k := 0; k < numFields; k++ {
103			flushed = false
104			var f reflect.Value
105			if v.Kind() == reflect.Struct {
106				f = v.Field(k)
107			} else {
108				f = v.Index(k)
109			}
110			align := byte(f.Type().Align()*8 - 1)
111			shift = (shift + align) &^ align
112			if shift >= 64 {
113				shift = 0
114				flushed = true
115				if class == _FLOAT {
116					addFloat(uintptr(val))
117				} else {
118					addInt(uintptr(val))
119				}
120				val = 0
121				class = _NO_CLASS
122			}
123			switch f.Type().Kind() {
124			case reflect.Struct:
125				place(f)
126			case reflect.Bool:
127				if f.Bool() {
128					val |= 1 << shift
129				}
130				shift += 8
131				class |= _INT
132			case reflect.Uint8:
133				val |= f.Uint() << shift
134				shift += 8
135				class |= _INT
136			case reflect.Uint16:
137				val |= f.Uint() << shift
138				shift += 16
139				class |= _INT
140			case reflect.Uint32:
141				val |= f.Uint() << shift
142				shift += 32
143				class |= _INT
144			case reflect.Uint64, reflect.Uint, reflect.Uintptr:
145				addInt(uintptr(f.Uint()))
146				shift = 0
147				flushed = true
148				class = _NO_CLASS
149			case reflect.Int8:
150				val |= uint64(f.Int()&0xFF) << shift
151				shift += 8
152				class |= _INT
153			case reflect.Int16:
154				val |= uint64(f.Int()&0xFFFF) << shift
155				shift += 16
156				class |= _INT
157			case reflect.Int32:
158				val |= uint64(f.Int()&0xFFFF_FFFF) << shift
159				shift += 32
160				class |= _INT
161			case reflect.Int64, reflect.Int:
162				addInt(uintptr(f.Int()))
163				shift = 0
164				flushed = true
165				class = _NO_CLASS
166			case reflect.Float32:
167				if class == _FLOAT {
168					addFloat(uintptr(val))
169					val = 0
170					shift = 0
171				}
172				val |= uint64(math.Float32bits(float32(f.Float()))) << shift
173				shift += 32
174				class |= _FLOAT
175			case reflect.Float64:
176				addFloat(uintptr(math.Float64bits(float64(f.Float()))))
177				shift = 0
178				flushed = true
179				class = _NO_CLASS
180			case reflect.Ptr:
181				addInt(f.Pointer())
182				shift = 0
183				flushed = true
184				class = _NO_CLASS
185			case reflect.Array:
186				place(f)
187			default:
188				panic("purego: unsupported kind " + f.Kind().String())
189			}
190		}
191	}
192	place(v)
193	if !flushed {
194		if class == _FLOAT {
195			addFloat(uintptr(val))
196		} else {
197			addInt(uintptr(val))
198		}
199	}
200}
201
202func placeStack(v reflect.Value, keepAlive []any, addInt func(uintptr)) []any {
203	// Struct is too big to be placed in registers.
204	// Copy to heap and place the pointer in register
205	ptrStruct := reflect.New(v.Type())
206	ptrStruct.Elem().Set(v)
207	ptr := ptrStruct.Elem().Addr().UnsafePointer()
208	keepAlive = append(keepAlive, ptr)
209	addInt(uintptr(ptr))
210	return keepAlive
211}
212
213// isHFA reports a Homogeneous Floating-point Aggregate (HFA) which is a Fundamental Data Type that is a
214// Floating-Point type and at most four uniquely addressable members (5.9.5.1 in [Arm64 Calling Convention]).
215// This type of struct will be placed more compactly than the individual fields.
216//
217// [Arm64 Calling Convention]: https://github.com/ARM-software/abi-aa/blob/main/sysvabi64/sysvabi64.rst
218func isHFA(t reflect.Type) bool {
219	// round up struct size to nearest 8 see section B.4
220	structSize := roundUpTo8(t.Size())
221	if structSize == 0 || t.NumField() > 4 {
222		return false
223	}
224	first := t.Field(0)
225	switch first.Type.Kind() {
226	case reflect.Float32, reflect.Float64:
227		firstKind := first.Type.Kind()
228		for i := 0; i < t.NumField(); i++ {
229			if t.Field(i).Type.Kind() != firstKind {
230				return false
231			}
232		}
233		return true
234	case reflect.Array:
235		switch first.Type.Elem().Kind() {
236		case reflect.Float32, reflect.Float64:
237			return true
238		default:
239			return false
240		}
241	case reflect.Struct:
242		for i := 0; i < first.Type.NumField(); i++ {
243			if !isHFA(first.Type) {
244				return false
245			}
246		}
247		return true
248	default:
249		return false
250	}
251}
252
253// isHVA reports a Homogeneous Aggregate with a Fundamental Data Type that is a Short-Vector type
254// and at most four uniquely addressable members (5.9.5.2 in [Arm64 Calling Convention]).
255// A short vector is a machine type that is composed of repeated instances of one fundamental integral or
256// floating-point type. It may be 8 or 16 bytes in total size (5.4 in [Arm64 Calling Convention]).
257// This type of struct will be placed more compactly than the individual fields.
258//
259// [Arm64 Calling Convention]: https://github.com/ARM-software/abi-aa/blob/main/sysvabi64/sysvabi64.rst
260func isHVA(t reflect.Type) bool {
261	// round up struct size to nearest 8 see section B.4
262	structSize := roundUpTo8(t.Size())
263	if structSize == 0 || (structSize != 8 && structSize != 16) {
264		return false
265	}
266	first := t.Field(0)
267	switch first.Type.Kind() {
268	case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Int8, reflect.Int16, reflect.Int32:
269		firstKind := first.Type.Kind()
270		for i := 0; i < t.NumField(); i++ {
271			if t.Field(i).Type.Kind() != firstKind {
272				return false
273			}
274		}
275		return true
276	case reflect.Array:
277		switch first.Type.Elem().Kind() {
278		case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Int8, reflect.Int16, reflect.Int32:
279			return true
280		default:
281			return false
282		}
283	default:
284		return false
285	}
286}

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 1

Tags 102

↑↓ moveenter openesc close