1// SPDX-License-Identifier: Apache-2.0
2// SPDX-FileCopyrightText: 2026 The Ebitengine Authors
3
4package purego
5
6import (
7 "reflect"
8 "unsafe"
9)
10
11// structReturnInMemory reports whether a struct return value is returned through
12// a caller-allocated hidden pointer passed as the first integer argument.
13// Aggregates larger than two eightbytes are returned in memory.
14func structReturnInMemory(outType reflect.Type) bool {
15 return outType.Size() > maxRegAllocStructSize
16}
17
18func getStruct(outType reflect.Type, syscall syscallArgs) reflect.Value {
19 outSize := outType.Size()
20
21 switch {
22 case outSize == 0:
23 return reflect.New(outType).Elem()
24
25 case outSize <= 16:
26 // Reconstruct from registers by copying raw bytes
27 var buf [16]byte
28
29 // Integer registers
30 *(*uintptr)(unsafe.Pointer(&buf[0])) = syscall.a1
31 if outSize > 8 {
32 *(*uintptr)(unsafe.Pointer(&buf[8])) = syscall.a2
33 }
34
35 // Homogeneous float aggregates override integer regs
36 if isAllFloats, numFields := isAllSameFloat(outType); isAllFloats {
37 if abiField(outType, 0).Type.Kind() == reflect.Float32 {
38 // float32 values in FP regs
39 f := []uintptr{syscall.f1, syscall.f2, syscall.f3, syscall.f4}
40 for i := 0; i < numFields; i++ {
41 *(*uint32)(unsafe.Pointer(&buf[i*4])) = uint32(f[i])
42 }
43 } else {
44 // float64: whole register value is valid
45 *(*uintptr)(unsafe.Pointer(&buf[0])) = syscall.f1
46 if outSize > 8 {
47 *(*uintptr)(unsafe.Pointer(&buf[8])) = syscall.f2
48 }
49 }
50 }
51
52 return reflect.NewAt(outType, unsafe.Pointer(&buf[0])).Elem()
53
54 default:
55 // Returned indirectly via pointer in a1
56 ptr := *(*unsafe.Pointer)(unsafe.Pointer(&syscall.a1))
57 return reflect.NewAt(outType, ptr).Elem()
58 }
59}
60
61func addStruct(
62 v reflect.Value,
63 numInts, numFloats, numStack *int,
64 addInt, addFloat, addStack func(uintptr),
65 keepAlive []any,
66) []any {
67 size := v.Type().Size()
68 if size == 0 {
69 return keepAlive
70 }
71
72 if size <= 16 {
73 return placeSmallAggregateS390X(v, addFloat, addInt, keepAlive)
74 }
75
76 return placeStack(v, keepAlive, addInt)
77}
78
79func placeSmallAggregateS390X(
80 v reflect.Value,
81 addFloat, addInt func(uintptr),
82 keepAlive []any,
83) []any {
84 size := v.Type().Size()
85
86 var ptr unsafe.Pointer
87 if v.CanAddr() {
88 ptr = v.Addr().UnsafePointer()
89 } else {
90 tmp := reflect.New(v.Type())
91 tmp.Elem().Set(v)
92 ptr = tmp.UnsafePointer()
93 keepAlive = append(keepAlive, tmp.Interface())
94 }
95
96 var buf [16]byte
97 src := unsafe.Slice((*byte)(ptr), size)
98 copy(buf[:], src)
99
100 w0 := *(*uintptr)(unsafe.Pointer(&buf[0]))
101 w1 := uintptr(0)
102 if size > 8 {
103 w1 = *(*uintptr)(unsafe.Pointer(&buf[8]))
104 }
105
106 if isFloats, _ := isAllSameFloat(v.Type()); isFloats {
107 addFloat(w0)
108 if size > 8 {
109 addFloat(w1)
110 }
111 } else {
112 addInt(w0)
113 if size > 8 {
114 addInt(w1)
115 }
116 }
117
118 return keepAlive
119}
120
121// placeStack is a fallback for structs that are too large to fit in registers
122func placeStack(v reflect.Value, keepAlive []any, addInt func(uintptr)) []any {
123 if v.CanAddr() {
124 addInt(v.Addr().Pointer())
125 return keepAlive
126 }
127 ptr := reflect.New(v.Type())
128 ptr.Elem().Set(v)
129 addInt(ptr.Pointer())
130 return append(keepAlive, ptr.Interface())
131}
132
133func shouldBundleStackArgs(v reflect.Value, numInts, numFloats int) bool {
134 // S390X does not bundle stack args
135 return false
136}
137
138func collectStackArgs(
139 args []reflect.Value,
140 i, numInts, numFloats int,
141 keepAlive []any,
142 addInt, addFloat, addStack func(uintptr),
143 numIntsPtr, numFloatsPtr, numStackPtr *int,
144) ([]reflect.Value, []any) {
145 return nil, keepAlive
146}
147
148func bundleStackArgs(stackArgs []reflect.Value, addStack func(uintptr)) {
149 panic("purego: bundleStackArgs should not be called on s390x")
150}
151
152func getCallbackStruct(inType reflect.Type, frame unsafe.Pointer, floatsN *int, intsN *int, stackSlot *int, stackByteOffset *uintptr) reflect.Value {
153 panic("purego: struct callback arguments are not supported on s390x")
154}
155
156func setStruct(a *callbackArgs, ret reflect.Value) {
157 panic("purego: struct returns are not supported on s390x")
158}