v0.7.0-alpha.1 0789956

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)
10
11// https://github.com/ARM-software/abi-aa/blob/main/sysvabi64/sysvabi64.rst
12const (
13	_NO_CLASS = 0b00
14	_FLOAT    = 0b01
15	_INT      = 0b11
16)
17
18func addStruct(v reflect.Value, numInts, numFloats, numStack *int, addInt, addFloat, addStack func(uintptr), keepAlive []interface{}) []interface{} {
19	if v.Type().Size() == 0 {
20		return keepAlive
21	}
22
23	if hva, hfa, size := isHVA(v.Type()), isHFA(v.Type()), v.Type().Size(); hva || hfa || size <= 16 {
24		// if this doesn't fit entirely in registers then
25		// each element goes onto the stack
26		if hfa && *numFloats+v.NumField() > numOfFloats {
27			*numFloats = numOfFloats
28		} else if hva && *numInts+v.NumField() > numOfIntegerRegisters() {
29			*numInts = numOfIntegerRegisters()
30		}
31
32		placeRegisters(v, addFloat, addInt)
33	} else {
34		keepAlive = placeStack(v, keepAlive, addInt)
35	}
36	return keepAlive // the struct was allocated so don't panic
37}
38
39func placeRegisters(v reflect.Value, addFloat func(uintptr), addInt func(uintptr)) {
40	var val uint64
41	var shift byte
42	var flushed bool
43	class := _NO_CLASS
44	var place func(v reflect.Value)
45	place = func(v reflect.Value) {
46		var numFields int
47		if v.Kind() == reflect.Struct {
48			numFields = v.Type().NumField()
49		} else {
50			numFields = v.Type().Len()
51		}
52		for k := 0; k < numFields; k++ {
53			flushed = false
54			var f reflect.Value
55			if v.Kind() == reflect.Struct {
56				f = v.Field(k)
57			} else {
58				f = v.Index(k)
59			}
60			if shift >= 64 {
61				shift = 0
62				flushed = true
63				if class == _FLOAT {
64					addFloat(uintptr(val))
65				} else {
66					addInt(uintptr(val))
67				}
68			}
69			switch f.Type().Kind() {
70			case reflect.Struct:
71				place(f)
72			case reflect.Bool:
73				if f.Bool() {
74					val |= 1
75				}
76				shift += 8
77				class |= _INT
78			case reflect.Uint8:
79				val |= f.Uint() << shift
80				shift += 8
81				class |= _INT
82			case reflect.Uint16:
83				val |= f.Uint() << shift
84				shift += 16
85				class |= _INT
86			case reflect.Uint32:
87				val |= f.Uint() << shift
88				shift += 32
89				class |= _INT
90			case reflect.Uint64:
91				addInt(uintptr(f.Uint()))
92				shift = 0
93				flushed = true
94			case reflect.Int8:
95				val |= uint64(f.Int()&0xFF) << shift
96				shift += 8
97				class |= _INT
98			case reflect.Int16:
99				val |= uint64(f.Int()&0xFFFF) << shift
100				shift += 16
101				class |= _INT
102			case reflect.Int32:
103				val |= uint64(f.Int()&0xFFFF_FFFF) << shift
104				shift += 32
105				class |= _INT
106			case reflect.Int64:
107				addInt(uintptr(f.Int()))
108				shift = 0
109				flushed = true
110			case reflect.Float32:
111				if class == _FLOAT {
112					addFloat(uintptr(val))
113					val = 0
114					shift = 0
115				}
116				val |= uint64(math.Float32bits(float32(f.Float()))) << shift
117				shift += 32
118				class |= _FLOAT
119			case reflect.Float64:
120				addFloat(uintptr(math.Float64bits(float64(f.Float()))))
121				shift = 0
122				flushed = true
123			case reflect.Array:
124				place(f)
125			default:
126				panic("purego: unsupported kind " + f.Kind().String())
127			}
128		}
129	}
130	place(v)
131	if !flushed {
132		if class == _FLOAT {
133			addFloat(uintptr(val))
134		} else {
135			addInt(uintptr(val))
136		}
137	}
138}
139
140func placeStack(v reflect.Value, keepAlive []interface{}, addInt func(uintptr)) []interface{} {
141	// Struct is too big to be placed in registers.
142	// Copy to heap and place the pointer in register
143	ptrStruct := reflect.New(v.Type())
144	ptrStruct.Elem().Set(v)
145	ptr := ptrStruct.Elem().Addr().UnsafePointer()
146	keepAlive = append(keepAlive, ptr)
147	addInt(uintptr(ptr))
148	return keepAlive
149}
150
151// isHFA reports a Homogeneous Floating-point Aggregate (HFA) which is a Fundamental Data Type that is a
152// Floating-Point type and at most four uniquely addressable members (5.9.5.1 in [Arm64 Calling Convention]).
153// This type of struct will be placed more compactly than the individual fields.
154//
155// [Arm64 Calling Convention]: https://github.com/ARM-software/abi-aa/blob/main/sysvabi64/sysvabi64.rst
156func isHFA(t reflect.Type) bool {
157	// round up struct size to nearest 8 see section B.4
158	structSize := roundUpTo8(t.Size())
159	if structSize == 0 || t.NumField() > 4 {
160		return false
161	}
162	first := t.Field(0)
163	switch first.Type.Kind() {
164	case reflect.Float32, reflect.Float64:
165		firstKind := first.Type.Kind()
166		for i := 0; i < t.NumField(); i++ {
167			if t.Field(i).Type.Kind() != firstKind {
168				return false
169			}
170		}
171		return true
172	case reflect.Array:
173		switch first.Type.Elem().Kind() {
174		case reflect.Float32, reflect.Float64:
175			return true
176		default:
177			return false
178		}
179	case reflect.Struct:
180		for i := 0; i < first.Type.NumField(); i++ {
181			if !isHFA(first.Type) {
182				return false
183			}
184		}
185		return true
186	default:
187		return false
188	}
189}
190
191// isHVA reports a Homogeneous Aggregate with a Fundamental Data Type that is a Short-Vector type
192// and at most four uniquely addressable members (5.9.5.2 in [Arm64 Calling Convention]).
193// A short vector is a machine type that is composed of repeated instances of one fundamental integral or
194// floating-point type. It may be 8 or 16 bytes in total size (5.4 in [Arm64 Calling Convention]).
195// This type of struct will be placed more compactly than the individual fields.
196//
197// [Arm64 Calling Convention]: https://github.com/ARM-software/abi-aa/blob/main/sysvabi64/sysvabi64.rst
198func isHVA(t reflect.Type) bool {
199	// round up struct size to nearest 8 see section B.4
200	structSize := roundUpTo8(t.Size())
201	if structSize == 0 || (structSize != 8 && structSize != 16) {
202		return false
203	}
204	first := t.Field(0)
205	switch first.Type.Kind() {
206	case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Int8, reflect.Int16, reflect.Int32:
207		firstKind := first.Type.Kind()
208		for i := 0; i < t.NumField(); i++ {
209			if t.Field(i).Type.Kind() != firstKind {
210				return false
211			}
212		}
213		return true
214	case reflect.Array:
215		switch first.Type.Elem().Kind() {
216		case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Int8, reflect.Int16, reflect.Int32:
217			return true
218		default:
219			return false
220		}
221	default:
222		return false
223	}
224}

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