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