v0.11.0 11ec78d

struct_ppc64le.go RawHistoryFolder

1// SPDX-License-Identifier: Apache-2.0
2// SPDX-FileCopyrightText: 2026 The Ebitengine Authors
3
4package purego
5
6import (
7	"math"
8	"reflect"
9	"unsafe"
10)
11
12// ppc64leLeaf is a scalar member of a flattened aggregate at byte offset offset.
13type ppc64leLeaf struct {
14	kind   reflect.Kind
15	offset uintptr
16}
17
18// ppc64leFlatten appends the scalar leaves of t to leaves, expanding nested
19// structs and arrays.
20func ppc64leFlatten(t reflect.Type, base uintptr, leaves *[]ppc64leLeaf) {
21	switch t.Kind() {
22	case reflect.Struct:
23		for i := range t.NumField() {
24			f := t.Field(i)
25			if f.Name == "_" {
26				// Blank fields are padding, not counted members.
27				continue
28			}
29			ppc64leFlatten(f.Type, base+f.Offset, leaves)
30		}
31	case reflect.Array:
32		elem := t.Elem()
33		for i := range t.Len() {
34			ppc64leFlatten(elem, base+uintptr(i)*elem.Size(), leaves)
35		}
36	default:
37		*leaves = append(*leaves, ppc64leLeaf{kind: t.Kind(), offset: base})
38	}
39}
40
41// ppc64leClassifyHFA reports whether t is a homogeneous floating-point
42// aggregate: one to eight members all of the same floating-point type.
43func ppc64leClassifyHFA(t reflect.Type) (leaves []ppc64leLeaf, isHFA bool) {
44	ppc64leFlatten(t, 0, &leaves)
45	if len(leaves) == 0 || len(leaves) > 8 {
46		return leaves, false
47	}
48	first := leaves[0].kind
49	if first != reflect.Float32 && first != reflect.Float64 {
50		return leaves, false
51	}
52	for _, l := range leaves {
53		if l.kind != first {
54			return leaves, false
55		}
56	}
57	return leaves, true
58}
59
60// structReturnInMemory reports whether a struct return value is returned through
61// a caller-allocated hidden pointer rather than in registers. A homogeneous
62// floating-point aggregate is returned in the floating-point registers whatever
63// its size.
64func structReturnInMemory(outType reflect.Type) bool {
65	if outType.Size() <= maxRegAllocStructSize {
66		return false
67	}
68	_, isHFA := ppc64leClassifyHFA(outType)
69	return !isHFA
70}
71
72func addStruct(v reflect.Value, numInts, numFloats, numStack *int, addInt, addFloat, addStack func(uintptr), keepAlive []any) []any {
73	size := v.Type().Size()
74	if size == 0 {
75		return keepAlive
76	}
77
78	var ptr unsafe.Pointer
79	if v.CanAddr() {
80		ptr = v.Addr().UnsafePointer()
81	} else {
82		tmp := reflect.New(v.Type())
83		tmp.Elem().Set(v)
84		ptr = tmp.UnsafePointer()
85		keepAlive = append(keepAlive, tmp.Interface())
86	}
87
88	// An HFA passes each member in its own floating-point register.
89	if leaves, isHFA := ppc64leClassifyHFA(v.Type()); isHFA {
90		for _, l := range leaves {
91			src := unsafe.Add(ptr, l.offset)
92			if l.kind == reflect.Float32 {
93				// Single precision occupies the register in double format.
94				addFloat(uintptr(math.Float64bits(float64(*(*float32)(src)))))
95			} else {
96				addFloat(uintptr(*(*uint64)(src)))
97			}
98		}
99	}
100
101	// Every aggregate also occupies its size rounded up to doublewords in the
102	// GPRs r3-r10 / parameter save area: shadow (skipped) for an HFA, the value
103	// otherwise.
104	for off := uintptr(0); off < size; off += 8 {
105		if remaining := size - off; remaining >= 8 {
106			addInt(*(*uintptr)(unsafe.Add(ptr, off)))
107		} else {
108			var word [8]byte
109			copy(word[:], unsafe.Slice((*byte)(unsafe.Add(ptr, off)), int(remaining)))
110			addInt(*(*uintptr)(unsafe.Pointer(&word[0])))
111		}
112	}
113
114	return keepAlive
115}
116
117func getStruct(outType reflect.Type, syscall syscallArgs) reflect.Value {
118	if outType.Size() == 0 {
119		return reflect.New(outType).Elem()
120	}
121
122	// An HFA returns each member in its own floating-point register.
123	if leaves, isHFA := ppc64leClassifyHFA(outType); isHFA {
124		floatRegs := [8]uintptr{
125			syscall.f1, syscall.f2, syscall.f3, syscall.f4,
126			syscall.f5, syscall.f6, syscall.f7, syscall.f8,
127		}
128		ret := reflect.New(outType)
129		base := ret.UnsafePointer()
130		for i, l := range leaves {
131			dst := unsafe.Add(base, l.offset)
132			if l.kind == reflect.Float32 {
133				// The register holds the value in double format.
134				*(*float32)(dst) = float32(math.Float64frombits(uint64(floatRegs[i])))
135			} else {
136				*(*uint64)(dst) = uint64(floatRegs[i])
137			}
138		}
139		return ret.Elem()
140	}
141
142	if outType.Size() > 16 {
143		// Returned via a caller-allocated buffer whose pointer comes back in r3.
144		return reflect.NewAt(outType, *(*unsafe.Pointer)(unsafe.Pointer(&syscall.a1))).Elem()
145	}
146
147	// Otherwise returned in r3 and r4.
148	var buf [16]byte
149	*(*uintptr)(unsafe.Pointer(&buf[0])) = syscall.a1
150	if outType.Size() > 8 {
151		*(*uintptr)(unsafe.Pointer(&buf[8])) = syscall.a2
152	}
153	return reflect.NewAt(outType, unsafe.Pointer(&buf[0])).Elem()
154}
155
156// shouldBundleStackArgs always returns false on ppc64le
157// since C-style stack argument bundling is only needed on Darwin ARM64.
158func shouldBundleStackArgs(v reflect.Value, numInts, numFloats int) bool {
159	return false
160}
161
162// collectStackArgs is not used on ppc64le.
163func collectStackArgs(args []reflect.Value, startIdx int, numInts, numFloats int,
164	keepAlive []any, addInt, addFloat, addStack func(uintptr),
165	pNumInts, pNumFloats, pNumStack *int) ([]reflect.Value, []any) {
166	panic("purego: collectStackArgs should not be called on ppc64le")
167}
168
169// bundleStackArgs is not used on ppc64le.
170func bundleStackArgs(stackArgs []reflect.Value, addStack func(uintptr)) {
171	panic("purego: bundleStackArgs should not be called on ppc64le")
172}
173
174func getCallbackStruct(inType reflect.Type, frame unsafe.Pointer, floatsN *int, intsN *int, stackSlot *int, stackByteOffset *uintptr) reflect.Value {
175	panic("purego: struct callback arguments are not supported on ppc64le")
176}
177
178func setStruct(a *callbackArgs, ret reflect.Value) {
179	panic("purego: struct returns are not supported on ppc64le")
180}

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