Add struct argument support (#175)
Closes #88
- commit
- f163b4c2606b7a2d3089335dc55a07cb42ac18c1
- author
- TotallyGamerJet · · 2024-01-12 12:04 UTC
- committer
- GitHub · · 2024-01-12 12:04 UTC
- parent
- dab77e6
- signature
- GPG Unverified
4AEE18F83AFDEB23The key is unknown
6 files · +1219 -1
M func.go +30 -1
| @@ -52,7 +52,7 @@ func RegisterLibFunc(fptr interface{}, handle uintptr, name string) { | |||
| 52 | 52 | // int64 <=> int64_t | |
| 53 | 53 | // float32 <=> float | |
| 54 | 54 | // float64 <=> double | |
| 55 | - | // struct <=> struct (WIP) | |
| 55 | + | // struct <=> struct (WIP - darwin only) | |
| 56 | 56 | // func <=> C function | |
| 57 | 57 | // unsafe.Pointer, *T <=> void* | |
| 58 | 58 | // []T => void* | |
| @@ -81,6 +81,12 @@ func RegisterLibFunc(fptr interface{}, handle uintptr, name string) { | |||
| 81 | 81 | // using unsafe.Slice. Doing this means that it becomes the responsibility of the caller to care about the lifetime | |
| 82 | 82 | // of the pointer | |
| 83 | 83 | // | |
| 84 | + | // # Structs | |
| 85 | + | // | |
| 86 | + | // Purego can handle the most common structs that have fields of builtin types like int8, uint16, float32, etc. However, | |
| 87 | + | // it does not support aligning fields properly. It is therefore the responsibility of the caller to ensure | |
| 88 | + | // that all padding is added to the Go struct to match the C one. See `BoolStructFn` in struct_test.go for an example. | |
| 89 | + | // | |
| 84 | 90 | // # Example | |
| 85 | 91 | // | |
| 86 | 92 | // All functions below call this C function: | |
| @@ -133,6 +139,23 @@ func RegisterFunc(fptr interface{}, cfn uintptr) { | |||
| 133 | 139 | } else { | |
| 134 | 140 | stack++ | |
| 135 | 141 | } | |
| 142 | + | case reflect.Struct: | |
| 143 | + | if runtime.GOOS != "darwin" || (runtime.GOARCH != "amd64" && runtime.GOARCH != "arm64") { | |
| 144 | + | panic("purego: struct arguments are only supported on darwin amd64 & arm64") | |
| 145 | + | } | |
| 146 | + | if arg.Size() == 0 { | |
| 147 | + | continue | |
| 148 | + | } | |
| 149 | + | addInt := func(u uintptr) { | |
| 150 | + | ints++ | |
| 151 | + | } | |
| 152 | + | addFloat := func(u uintptr) { | |
| 153 | + | floats++ | |
| 154 | + | } | |
| 155 | + | addStack := func(u uintptr) { | |
| 156 | + | stack++ | |
| 157 | + | } | |
| 158 | + | _ = addStruct(reflect.New(arg).Elem(), &ints, &floats, &stack, addInt, addFloat, addStack, nil) | |
| 136 | 159 | default: | |
| 137 | 160 | panic("purego: unsupported kind " + arg.Kind().String()) | |
| 138 | 161 | } | |
| @@ -228,6 +251,8 @@ func RegisterFunc(fptr interface{}, cfn uintptr) { | |||
| 228 | 251 | addFloat(uintptr(math.Float32bits(float32(v.Float())))) | |
| 229 | 252 | case reflect.Float64: | |
| 230 | 253 | addFloat(uintptr(math.Float64bits(v.Float()))) | |
| 254 | + | case reflect.Struct: | |
| 255 | + | keepAlive = addStruct(v, &numInts, &numFloats, &numStack, addInt, addFloat, addStack, keepAlive) | |
| 231 | 256 | default: | |
| 232 | 257 | panic("purego: unsupported kind: " + v.Kind().String()) | |
| 233 | 258 | } | |
| @@ -292,6 +317,10 @@ func RegisterFunc(fptr interface{}, cfn uintptr) { | |||
| 292 | 317 | fn.Set(v) | |
| 293 | 318 | } | |
| 294 | 319 | ||
| 320 | + | func roundUpTo8(val uintptr) uintptr { | |
| 321 | + | return (val + 7) &^ 7 | |
| 322 | + | } | |
| 323 | + | ||
| 295 | 324 | func numOfIntegerRegisters() int { | |
| 296 | 325 | switch runtime.GOARCH { | |
| 297 | 326 | case "arm64": | |
| @@ -52,7 +52,7 @@ func RegisterLibFunc(fptr interface{}, handle uintptr, name string) { | |||||
| 52 | // int64 <=> int64_t | 52 | // int64 <=> int64_t | ||
| 53 | // float32 <=> float | 53 | // float32 <=> float | ||
| 54 | // float64 <=> double | 54 | // float64 <=> double | ||
| 55 | - | // struct <=> struct (WIP) | 55 | + | // struct <=> struct (WIP - darwin only) |
| 56 | // func <=> C function | 56 | // func <=> C function | ||
| 57 | // unsafe.Pointer, *T <=> void* | 57 | // unsafe.Pointer, *T <=> void* | ||
| 58 | // []T => void* | 58 | // []T => void* | ||
| @@ -81,6 +81,12 @@ func RegisterLibFunc(fptr interface{}, handle uintptr, name string) { | |||||
| 81 | // using unsafe.Slice. Doing this means that it becomes the responsibility of the caller to care about the lifetime | 81 | // using unsafe.Slice. Doing this means that it becomes the responsibility of the caller to care about the lifetime | ||
| 82 | // of the pointer | 82 | // of the pointer | ||
| 83 | // | 83 | // | ||
| 84 | + | // # Structs | |||
| 85 | + | // | |||
| 86 | + | // Purego can handle the most common structs that have fields of builtin types like int8, uint16, float32, etc. However, | |||
| 87 | + | // it does not support aligning fields properly. It is therefore the responsibility of the caller to ensure | |||
| 88 | + | // that all padding is added to the Go struct to match the C one. See `BoolStructFn` in struct_test.go for an example. | |||
| 89 | + | // | |||
| 84 | // # Example | 90 | // # Example | ||
| 85 | // | 91 | // | ||
| 86 | // All functions below call this C function: | 92 | // All functions below call this C function: | ||
| @@ -133,6 +139,23 @@ func RegisterFunc(fptr interface{}, cfn uintptr) { | |||||
| 133 | } else { | 139 | } else { | ||
| 134 | stack++ | 140 | stack++ | ||
| 135 | } | 141 | } | ||
| 142 | + | case reflect.Struct: | |||
| 143 | + | if runtime.GOOS != "darwin" || (runtime.GOARCH != "amd64" && runtime.GOARCH != "arm64") { | |||
| 144 | + | panic("purego: struct arguments are only supported on darwin amd64 & arm64") | |||
| 145 | + | } | |||
| 146 | + | if arg.Size() == 0 { | |||
| 147 | + | continue | |||
| 148 | + | } | |||
| 149 | + | addInt := func(u uintptr) { | |||
| 150 | + | ints++ | |||
| 151 | + | } | |||
| 152 | + | addFloat := func(u uintptr) { | |||
| 153 | + | floats++ | |||
| 154 | + | } | |||
| 155 | + | addStack := func(u uintptr) { | |||
| 156 | + | stack++ | |||
| 157 | + | } | |||
| 158 | + | _ = addStruct(reflect.New(arg).Elem(), &ints, &floats, &stack, addInt, addFloat, addStack, nil) | |||
| 136 | default: | 159 | default: | ||
| 137 | panic("purego: unsupported kind " + arg.Kind().String()) | 160 | panic("purego: unsupported kind " + arg.Kind().String()) | ||
| 138 | } | 161 | } | ||
| @@ -228,6 +251,8 @@ func RegisterFunc(fptr interface{}, cfn uintptr) { | |||||
| 228 | addFloat(uintptr(math.Float32bits(float32(v.Float())))) | 251 | addFloat(uintptr(math.Float32bits(float32(v.Float())))) | ||
| 229 | case reflect.Float64: | 252 | case reflect.Float64: | ||
| 230 | addFloat(uintptr(math.Float64bits(v.Float()))) | 253 | addFloat(uintptr(math.Float64bits(v.Float()))) | ||
| 254 | + | case reflect.Struct: | |||
| 255 | + | keepAlive = addStruct(v, &numInts, &numFloats, &numStack, addInt, addFloat, addStack, keepAlive) | |||
| 231 | default: | 256 | default: | ||
| 232 | panic("purego: unsupported kind: " + v.Kind().String()) | 257 | panic("purego: unsupported kind: " + v.Kind().String()) | ||
| 233 | } | 258 | } | ||
| @@ -292,6 +317,10 @@ func RegisterFunc(fptr interface{}, cfn uintptr) { | |||||
| 292 | fn.Set(v) | 317 | fn.Set(v) | ||
| 293 | } | 318 | } | ||
| 294 | 319 | ||||
| 320 | + | func roundUpTo8(val uintptr) uintptr { | |||
| 321 | + | return (val + 7) &^ 7 | |||
| 322 | + | } | |||
| 323 | + | ||||
| 295 | func numOfIntegerRegisters() int { | 324 | func numOfIntegerRegisters() int { | ||
| 296 | switch runtime.GOARCH { | 325 | switch runtime.GOARCH { | ||
| 297 | case "arm64": | 326 | case "arm64": | ||
A struct_amd64.go +203 -0
| @@ -0,0 +1,203 @@ | |||
| 1 | + | // SPDX-License-Identifier: Apache-2.0 | |
| 2 | + | // SPDX-FileCopyrightText: 2024 The Ebitengine Authors | |
| 3 | + | ||
| 4 | + | package purego | |
| 5 | + | ||
| 6 | + | import ( | |
| 7 | + | "math" | |
| 8 | + | "reflect" | |
| 9 | + | ) | |
| 10 | + | ||
| 11 | + | // https://refspecs.linuxbase.org/elf/x86_64-abi-0.99.pdf | |
| 12 | + | // https://gitlab.com/x86-psABIs/x86-64-ABI | |
| 13 | + | // Class determines where the 8 byte value goes. | |
| 14 | + | // Higher value classes win over lower value classes | |
| 15 | + | const ( | |
| 16 | + | _NO_CLASS = 0b0000 | |
| 17 | + | _SSE = 0b0001 | |
| 18 | + | _X87 = 0b0011 // long double not used in Go | |
| 19 | + | _INTEGER = 0b0111 | |
| 20 | + | _MEMORY = 0b1111 | |
| 21 | + | ) | |
| 22 | + | ||
| 23 | + | func addStruct(v reflect.Value, numInts, numFloats, numStack *int, addInt, addFloat, addStack func(uintptr), keepAlive []interface{}) []interface{} { | |
| 24 | + | if v.Type().Size() == 0 { | |
| 25 | + | return keepAlive | |
| 26 | + | } | |
| 27 | + | ||
| 28 | + | // if greater than 64 bytes place on stack | |
| 29 | + | if v.Type().Size() > 8*8 { | |
| 30 | + | placeStack(v, addStack) | |
| 31 | + | return keepAlive | |
| 32 | + | } | |
| 33 | + | var ( | |
| 34 | + | savedNumFloats = *numFloats | |
| 35 | + | savedNumInts = *numInts | |
| 36 | + | savedNumStack = *numStack | |
| 37 | + | ) | |
| 38 | + | placeOnStack := postMerger(v.Type()) || !tryPlaceRegister(v, addFloat, addInt) | |
| 39 | + | if placeOnStack { | |
| 40 | + | // reset any values placed in registers | |
| 41 | + | *numFloats = savedNumFloats | |
| 42 | + | *numInts = savedNumInts | |
| 43 | + | *numStack = savedNumStack | |
| 44 | + | placeStack(v, addStack) | |
| 45 | + | } | |
| 46 | + | return keepAlive | |
| 47 | + | } | |
| 48 | + | ||
| 49 | + | func postMerger(t reflect.Type) bool { | |
| 50 | + | // (c) If the size of the aggregate exceeds two eightbytes and the first eight- byte isn’t SSE or any other | |
| 51 | + | // eightbyte isn’t SSEUP, the whole argument is passed in memory. | |
| 52 | + | if t.Kind() != reflect.Struct { | |
| 53 | + | return false | |
| 54 | + | } | |
| 55 | + | if t.Size() <= 2*8 { | |
| 56 | + | return false | |
| 57 | + | } | |
| 58 | + | first := getFirst(t).Kind() | |
| 59 | + | if first != reflect.Float32 && first != reflect.Float64 { | |
| 60 | + | return false | |
| 61 | + | } | |
| 62 | + | return true | |
| 63 | + | } | |
| 64 | + | ||
| 65 | + | func getFirst(t reflect.Type) reflect.Type { | |
| 66 | + | first := t.Field(0).Type | |
| 67 | + | if first.Kind() == reflect.Struct { | |
| 68 | + | return getFirst(first) | |
| 69 | + | } | |
| 70 | + | return first | |
| 71 | + | } | |
| 72 | + | ||
| 73 | + | func tryPlaceRegister(v reflect.Value, addFloat func(uintptr), addInt func(uintptr)) (ok bool) { | |
| 74 | + | ok = true | |
| 75 | + | var val uint64 | |
| 76 | + | var shift byte // # of bits to shift | |
| 77 | + | var flushed bool | |
| 78 | + | class := _NO_CLASS | |
| 79 | + | var place func(v reflect.Value) | |
| 80 | + | place = func(v reflect.Value) { | |
| 81 | + | var numFields int | |
| 82 | + | if v.Kind() == reflect.Struct { | |
| 83 | + | numFields = v.Type().NumField() | |
| 84 | + | } else { | |
| 85 | + | numFields = v.Type().Len() | |
| 86 | + | } | |
| 87 | + | ||
| 88 | + | for i := 0; i < numFields; i++ { | |
| 89 | + | flushed = false | |
| 90 | + | var f reflect.Value | |
| 91 | + | if v.Kind() == reflect.Struct { | |
| 92 | + | f = v.Field(i) | |
| 93 | + | } else { | |
| 94 | + | f = v.Index(i) | |
| 95 | + | } | |
| 96 | + | if shift >= 64 { | |
| 97 | + | shift = 0 | |
| 98 | + | flushed = true | |
| 99 | + | if class == _SSE { | |
| 100 | + | addFloat(uintptr(val)) | |
| 101 | + | } else { | |
| 102 | + | addInt(uintptr(val)) | |
| 103 | + | } | |
| 104 | + | class = _NO_CLASS | |
| 105 | + | } | |
| 106 | + | switch f.Kind() { | |
| 107 | + | case reflect.Struct: | |
| 108 | + | place(f) | |
| 109 | + | case reflect.Bool: | |
| 110 | + | if f.Bool() { | |
| 111 | + | val |= 1 | |
| 112 | + | } | |
| 113 | + | shift += 8 | |
| 114 | + | class |= _INTEGER | |
| 115 | + | case reflect.Pointer: | |
| 116 | + | ok = false | |
| 117 | + | return | |
| 118 | + | case reflect.Int8: | |
| 119 | + | val |= uint64(f.Int()&0xFF) << shift | |
| 120 | + | shift += 8 | |
| 121 | + | class |= _INTEGER | |
| 122 | + | case reflect.Int16: | |
| 123 | + | val |= uint64(f.Int()&0xFFFF) << shift | |
| 124 | + | shift += 16 | |
| 125 | + | class |= _INTEGER | |
| 126 | + | case reflect.Int32: | |
| 127 | + | val |= uint64(f.Int()&0xFFFF_FFFF) << shift | |
| 128 | + | shift += 32 | |
| 129 | + | class |= _INTEGER | |
| 130 | + | case reflect.Int64: | |
| 131 | + | addInt(uintptr(f.Int())) | |
| 132 | + | shift = 0 | |
| 133 | + | class = _NO_CLASS | |
| 134 | + | flushed = true | |
| 135 | + | case reflect.Uint8: | |
| 136 | + | val |= f.Uint() << shift | |
| 137 | + | shift += 8 | |
| 138 | + | class |= _INTEGER | |
| 139 | + | case reflect.Uint16: | |
| 140 | + | val |= f.Uint() << shift | |
| 141 | + | shift += 16 | |
| 142 | + | class |= _INTEGER | |
| 143 | + | case reflect.Uint32: | |
| 144 | + | val |= f.Uint() << shift | |
| 145 | + | shift += 32 | |
| 146 | + | class |= _INTEGER | |
| 147 | + | case reflect.Uint64: | |
| 148 | + | addInt(uintptr(f.Uint())) | |
| 149 | + | shift = 0 | |
| 150 | + | class = _NO_CLASS | |
| 151 | + | flushed = true | |
| 152 | + | case reflect.Float32: | |
| 153 | + | val |= uint64(math.Float32bits(float32(f.Float()))) << shift | |
| 154 | + | shift += 32 | |
| 155 | + | class |= _SSE | |
| 156 | + | case reflect.Float64: | |
| 157 | + | if v.Type().Size() > 16 { | |
| 158 | + | ok = false | |
| 159 | + | return | |
| 160 | + | } | |
| 161 | + | addFloat(uintptr(math.Float64bits(f.Float()))) | |
| 162 | + | class = _NO_CLASS | |
| 163 | + | flushed = true | |
| 164 | + | case reflect.Array: | |
| 165 | + | place(f) | |
| 166 | + | default: | |
| 167 | + | panic("purego: unsupported kind " + f.Kind().String()) | |
| 168 | + | } | |
| 169 | + | } | |
| 170 | + | } | |
| 171 | + | ||
| 172 | + | place(v) | |
| 173 | + | if !flushed { | |
| 174 | + | if class == _SSE { | |
| 175 | + | addFloat(uintptr(val)) | |
| 176 | + | } else { | |
| 177 | + | addInt(uintptr(val)) | |
| 178 | + | } | |
| 179 | + | } | |
| 180 | + | return ok | |
| 181 | + | } | |
| 182 | + | ||
| 183 | + | func placeStack(v reflect.Value, addStack func(uintptr)) { | |
| 184 | + | for i := 0; i < v.Type().NumField(); i++ { | |
| 185 | + | f := v.Field(i) | |
| 186 | + | switch f.Kind() { | |
| 187 | + | case reflect.Pointer: | |
| 188 | + | addStack(f.Pointer()) | |
| 189 | + | case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: | |
| 190 | + | addStack(uintptr(f.Int())) | |
| 191 | + | case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: | |
| 192 | + | addStack(uintptr(f.Uint())) | |
| 193 | + | case reflect.Float32: | |
| 194 | + | addStack(uintptr(math.Float32bits(float32(f.Float())))) | |
| 195 | + | case reflect.Float64: | |
| 196 | + | addStack(uintptr(math.Float64bits(f.Float()))) | |
| 197 | + | case reflect.Struct: | |
| 198 | + | placeStack(f, addStack) | |
| 199 | + | default: | |
| 200 | + | panic("purego: unsupported kind " + f.Kind().String()) | |
| 201 | + | } | |
| 202 | + | } | |
| 203 | + | } | |
| @@ -0,0 +1,203 @@ | |||||
| 1 | + | // SPDX-License-Identifier: Apache-2.0 | |||
| 2 | + | // SPDX-FileCopyrightText: 2024 The Ebitengine Authors | |||
| 3 | + | ||||
| 4 | + | package purego | |||
| 5 | + | ||||
| 6 | + | import ( | |||
| 7 | + | "math" | |||
| 8 | + | "reflect" | |||
| 9 | + | ) | |||
| 10 | + | ||||
| 11 | + | // https://refspecs.linuxbase.org/elf/x86_64-abi-0.99.pdf | |||
| 12 | + | // https://gitlab.com/x86-psABIs/x86-64-ABI | |||
| 13 | + | // Class determines where the 8 byte value goes. | |||
| 14 | + | // Higher value classes win over lower value classes | |||
| 15 | + | const ( | |||
| 16 | + | _NO_CLASS = 0b0000 | |||
| 17 | + | _SSE = 0b0001 | |||
| 18 | + | _X87 = 0b0011 // long double not used in Go | |||
| 19 | + | _INTEGER = 0b0111 | |||
| 20 | + | _MEMORY = 0b1111 | |||
| 21 | + | ) | |||
| 22 | + | ||||
| 23 | + | func addStruct(v reflect.Value, numInts, numFloats, numStack *int, addInt, addFloat, addStack func(uintptr), keepAlive []interface{}) []interface{} { | |||
| 24 | + | if v.Type().Size() == 0 { | |||
| 25 | + | return keepAlive | |||
| 26 | + | } | |||
| 27 | + | ||||
| 28 | + | // if greater than 64 bytes place on stack | |||
| 29 | + | if v.Type().Size() > 8*8 { | |||
| 30 | + | placeStack(v, addStack) | |||
| 31 | + | return keepAlive | |||
| 32 | + | } | |||
| 33 | + | var ( | |||
| 34 | + | savedNumFloats = *numFloats | |||
| 35 | + | savedNumInts = *numInts | |||
| 36 | + | savedNumStack = *numStack | |||
| 37 | + | ) | |||
| 38 | + | placeOnStack := postMerger(v.Type()) || !tryPlaceRegister(v, addFloat, addInt) | |||
| 39 | + | if placeOnStack { | |||
| 40 | + | // reset any values placed in registers | |||
| 41 | + | *numFloats = savedNumFloats | |||
| 42 | + | *numInts = savedNumInts | |||
| 43 | + | *numStack = savedNumStack | |||
| 44 | + | placeStack(v, addStack) | |||
| 45 | + | } | |||
| 46 | + | return keepAlive | |||
| 47 | + | } | |||
| 48 | + | ||||
| 49 | + | func postMerger(t reflect.Type) bool { | |||
| 50 | + | // (c) If the size of the aggregate exceeds two eightbytes and the first eight- byte isn’t SSE or any other | |||
| 51 | + | // eightbyte isn’t SSEUP, the whole argument is passed in memory. | |||
| 52 | + | if t.Kind() != reflect.Struct { | |||
| 53 | + | return false | |||
| 54 | + | } | |||
| 55 | + | if t.Size() <= 2*8 { | |||
| 56 | + | return false | |||
| 57 | + | } | |||
| 58 | + | first := getFirst(t).Kind() | |||
| 59 | + | if first != reflect.Float32 && first != reflect.Float64 { | |||
| 60 | + | return false | |||
| 61 | + | } | |||
| 62 | + | return true | |||
| 63 | + | } | |||
| 64 | + | ||||
| 65 | + | func getFirst(t reflect.Type) reflect.Type { | |||
| 66 | + | first := t.Field(0).Type | |||
| 67 | + | if first.Kind() == reflect.Struct { | |||
| 68 | + | return getFirst(first) | |||
| 69 | + | } | |||
| 70 | + | return first | |||
| 71 | + | } | |||
| 72 | + | ||||
| 73 | + | func tryPlaceRegister(v reflect.Value, addFloat func(uintptr), addInt func(uintptr)) (ok bool) { | |||
| 74 | + | ok = true | |||
| 75 | + | var val uint64 | |||
| 76 | + | var shift byte // # of bits to shift | |||
| 77 | + | var flushed bool | |||
| 78 | + | class := _NO_CLASS | |||
| 79 | + | var place func(v reflect.Value) | |||
| 80 | + | place = func(v reflect.Value) { | |||
| 81 | + | var numFields int | |||
| 82 | + | if v.Kind() == reflect.Struct { | |||
| 83 | + | numFields = v.Type().NumField() | |||
| 84 | + | } else { | |||
| 85 | + | numFields = v.Type().Len() | |||
| 86 | + | } | |||
| 87 | + | ||||
| 88 | + | for i := 0; i < numFields; i++ { | |||
| 89 | + | flushed = false | |||
| 90 | + | var f reflect.Value | |||
| 91 | + | if v.Kind() == reflect.Struct { | |||
| 92 | + | f = v.Field(i) | |||
| 93 | + | } else { | |||
| 94 | + | f = v.Index(i) | |||
| 95 | + | } | |||
| 96 | + | if shift >= 64 { | |||
| 97 | + | shift = 0 | |||
| 98 | + | flushed = true | |||
| 99 | + | if class == _SSE { | |||
| 100 | + | addFloat(uintptr(val)) | |||
| 101 | + | } else { | |||
| 102 | + | addInt(uintptr(val)) | |||
| 103 | + | } | |||
| 104 | + | class = _NO_CLASS | |||
| 105 | + | } | |||
| 106 | + | switch f.Kind() { | |||
| 107 | + | case reflect.Struct: | |||
| 108 | + | place(f) | |||
| 109 | + | case reflect.Bool: | |||
| 110 | + | if f.Bool() { | |||
| 111 | + | val |= 1 | |||
| 112 | + | } | |||
| 113 | + | shift += 8 | |||
| 114 | + | class |= _INTEGER | |||
| 115 | + | case reflect.Pointer: | |||
| 116 | + | ok = false | |||
| 117 | + | return | |||
| 118 | + | case reflect.Int8: | |||
| 119 | + | val |= uint64(f.Int()&0xFF) << shift | |||
| 120 | + | shift += 8 | |||
| 121 | + | class |= _INTEGER | |||
| 122 | + | case reflect.Int16: | |||
| 123 | + | val |= uint64(f.Int()&0xFFFF) << shift | |||
| 124 | + | shift += 16 | |||
| 125 | + | class |= _INTEGER | |||
| 126 | + | case reflect.Int32: | |||
| 127 | + | val |= uint64(f.Int()&0xFFFF_FFFF) << shift | |||
| 128 | + | shift += 32 | |||
| 129 | + | class |= _INTEGER | |||
| 130 | + | case reflect.Int64: | |||
| 131 | + | addInt(uintptr(f.Int())) | |||
| 132 | + | shift = 0 | |||
| 133 | + | class = _NO_CLASS | |||
| 134 | + | flushed = true | |||
| 135 | + | case reflect.Uint8: | |||
| 136 | + | val |= f.Uint() << shift | |||
| 137 | + | shift += 8 | |||
| 138 | + | class |= _INTEGER | |||
| 139 | + | case reflect.Uint16: | |||
| 140 | + | val |= f.Uint() << shift | |||
| 141 | + | shift += 16 | |||
| 142 | + | class |= _INTEGER | |||
| 143 | + | case reflect.Uint32: | |||
| 144 | + | val |= f.Uint() << shift | |||
| 145 | + | shift += 32 | |||
| 146 | + | class |= _INTEGER | |||
| 147 | + | case reflect.Uint64: | |||
| 148 | + | addInt(uintptr(f.Uint())) | |||
| 149 | + | shift = 0 | |||
| 150 | + | class = _NO_CLASS | |||
| 151 | + | flushed = true | |||
| 152 | + | case reflect.Float32: | |||
| 153 | + | val |= uint64(math.Float32bits(float32(f.Float()))) << shift | |||
| 154 | + | shift += 32 | |||
| 155 | + | class |= _SSE | |||
| 156 | + | case reflect.Float64: | |||
| 157 | + | if v.Type().Size() > 16 { | |||
| 158 | + | ok = false | |||
| 159 | + | return | |||
| 160 | + | } | |||
| 161 | + | addFloat(uintptr(math.Float64bits(f.Float()))) | |||
| 162 | + | class = _NO_CLASS | |||
| 163 | + | flushed = true | |||
| 164 | + | case reflect.Array: | |||
| 165 | + | place(f) | |||
| 166 | + | default: | |||
| 167 | + | panic("purego: unsupported kind " + f.Kind().String()) | |||
| 168 | + | } | |||
| 169 | + | } | |||
| 170 | + | } | |||
| 171 | + | ||||
| 172 | + | place(v) | |||
| 173 | + | if !flushed { | |||
| 174 | + | if class == _SSE { | |||
| 175 | + | addFloat(uintptr(val)) | |||
| 176 | + | } else { | |||
| 177 | + | addInt(uintptr(val)) | |||
| 178 | + | } | |||
| 179 | + | } | |||
| 180 | + | return ok | |||
| 181 | + | } | |||
| 182 | + | ||||
| 183 | + | func placeStack(v reflect.Value, addStack func(uintptr)) { | |||
| 184 | + | for i := 0; i < v.Type().NumField(); i++ { | |||
| 185 | + | f := v.Field(i) | |||
| 186 | + | switch f.Kind() { | |||
| 187 | + | case reflect.Pointer: | |||
| 188 | + | addStack(f.Pointer()) | |||
| 189 | + | case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: | |||
| 190 | + | addStack(uintptr(f.Int())) | |||
| 191 | + | case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: | |||
| 192 | + | addStack(uintptr(f.Uint())) | |||
| 193 | + | case reflect.Float32: | |||
| 194 | + | addStack(uintptr(math.Float32bits(float32(f.Float())))) | |||
| 195 | + | case reflect.Float64: | |||
| 196 | + | addStack(uintptr(math.Float64bits(f.Float()))) | |||
| 197 | + | case reflect.Struct: | |||
| 198 | + | placeStack(f, addStack) | |||
| 199 | + | default: | |||
| 200 | + | panic("purego: unsupported kind " + f.Kind().String()) | |||
| 201 | + | } | |||
| 202 | + | } | |||
| 203 | + | } | |||
A struct_arm64.go +224 -0
| @@ -0,0 +1,224 @@ | |||
| 1 | + | // SPDX-License-Identifier: Apache-2.0 | |
| 2 | + | // SPDX-FileCopyrightText: 2024 The Ebitengine Authors | |
| 3 | + | ||
| 4 | + | package purego | |
| 5 | + | ||
| 6 | + | import ( | |
| 7 | + | "math" | |
| 8 | + | "reflect" | |
| 9 | + | ) | |
| 10 | + | ||
| 11 | + | // https://github.com/ARM-software/abi-aa/blob/main/sysvabi64/sysvabi64.rst | |
| 12 | + | const ( | |
| 13 | + | _NO_CLASS = 0b00 | |
| 14 | + | _FLOAT = 0b01 | |
| 15 | + | _INT = 0b11 | |
| 16 | + | ) | |
| 17 | + | ||
| 18 | + | func 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 | + | ||
| 39 | + | func 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 | + | ||
| 140 | + | func 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 | |
| 156 | + | func 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 | |
| 198 | + | func 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 | + | } | |
| @@ -0,0 +1,224 @@ | |||||
| 1 | + | // SPDX-License-Identifier: Apache-2.0 | |||
| 2 | + | // SPDX-FileCopyrightText: 2024 The Ebitengine Authors | |||
| 3 | + | ||||
| 4 | + | package purego | |||
| 5 | + | ||||
| 6 | + | import ( | |||
| 7 | + | "math" | |||
| 8 | + | "reflect" | |||
| 9 | + | ) | |||
| 10 | + | ||||
| 11 | + | // https://github.com/ARM-software/abi-aa/blob/main/sysvabi64/sysvabi64.rst | |||
| 12 | + | const ( | |||
| 13 | + | _NO_CLASS = 0b00 | |||
| 14 | + | _FLOAT = 0b01 | |||
| 15 | + | _INT = 0b11 | |||
| 16 | + | ) | |||
| 17 | + | ||||
| 18 | + | func 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 | + | ||||
| 39 | + | func 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 | + | ||||
| 140 | + | func 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 | |||
| 156 | + | func 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 | |||
| 198 | + | func 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 | + | } | |||
A struct_other.go +12 -0
| @@ -0,0 +1,12 @@ | |||
| 1 | + | // SPDX-License-Identifier: Apache-2.0 | |
| 2 | + | // SPDX-FileCopyrightText: 2024 The Ebitengine Authors | |
| 3 | + | ||
| 4 | + | //go:build !amd64 && !arm64 | |
| 5 | + | ||
| 6 | + | package purego | |
| 7 | + | ||
| 8 | + | import "reflect" | |
| 9 | + | ||
| 10 | + | func addStruct(v reflect.Value, numInts, numFloats, numStack *int, addInt, addFloat, addStack func(uintptr), keepAlive []interface{}) []interface{} { | |
| 11 | + | panic("purego: struct arguments are not supported") | |
| 12 | + | } | |
| @@ -0,0 +1,12 @@ | |||||
| 1 | + | // SPDX-License-Identifier: Apache-2.0 | |||
| 2 | + | // SPDX-FileCopyrightText: 2024 The Ebitengine Authors | |||
| 3 | + | ||||
| 4 | + | //go:build !amd64 && !arm64 | |||
| 5 | + | ||||
| 6 | + | package purego | |||
| 7 | + | ||||
| 8 | + | import "reflect" | |||
| 9 | + | ||||
| 10 | + | func addStruct(v reflect.Value, numInts, numFloats, numStack *int, addInt, addFloat, addStack func(uintptr), keepAlive []interface{}) []interface{} { | |||
| 11 | + | panic("purego: struct arguments are not supported") | |||
| 12 | + | } | |||
A struct_test.go +431 -0
| @@ -0,0 +1,431 @@ | |||
| 1 | + | // SPDX-License-Identifier: Apache-2.0 | |
| 2 | + | // SPDX-FileCopyrightText: 2024 The Ebitengine Authors | |
| 3 | + | ||
| 4 | + | //go:build darwin && (arm64 || amd64) | |
| 5 | + | ||
| 6 | + | package purego_test | |
| 7 | + | ||
| 8 | + | import ( | |
| 9 | + | "os" | |
| 10 | + | "path/filepath" | |
| 11 | + | "testing" | |
| 12 | + | ||
| 13 | + | "github.com/ebitengine/purego" | |
| 14 | + | ) | |
| 15 | + | ||
| 16 | + | func TestRegisterFunc_structArgs(t *testing.T) { | |
| 17 | + | libFileName := filepath.Join(t.TempDir(), "structtest.so") | |
| 18 | + | t.Logf("Build %v", libFileName) | |
| 19 | + | ||
| 20 | + | if err := buildSharedLib("CC", libFileName, filepath.Join("structtest", "struct_test.c")); err != nil { | |
| 21 | + | t.Fatal(err) | |
| 22 | + | } | |
| 23 | + | defer os.Remove(libFileName) | |
| 24 | + | ||
| 25 | + | lib, err := purego.Dlopen(libFileName, purego.RTLD_NOW|purego.RTLD_GLOBAL) | |
| 26 | + | if err != nil { | |
| 27 | + | t.Fatalf("Dlopen(%q) failed: %v", libFileName, err) | |
| 28 | + | } | |
| 29 | + | ||
| 30 | + | const ( | |
| 31 | + | expectedUnsigned = 0xdeadbeef | |
| 32 | + | expectedSigned = -123 | |
| 33 | + | expectedOdd = 12 + 23 + 46 | |
| 34 | + | expectedLong uint64 = 0xdeadbeefcafebabe | |
| 35 | + | expectedFloat float32 = 10 | |
| 36 | + | expectedDouble float64 = 10 | |
| 37 | + | ) | |
| 38 | + | ||
| 39 | + | { | |
| 40 | + | type Empty struct{} | |
| 41 | + | var NoStruct func(Empty) int64 | |
| 42 | + | purego.RegisterLibFunc(&NoStruct, lib, "NoStruct") | |
| 43 | + | if ret := NoStruct(Empty{}); ret != expectedUnsigned { | |
| 44 | + | t.Fatalf("NoStruct returned %#x wanted %#x", ret, expectedUnsigned) | |
| 45 | + | } | |
| 46 | + | } | |
| 47 | + | { | |
| 48 | + | type EmptyEmpty struct{} | |
| 49 | + | var EmptyEmptyFn func(EmptyEmpty) int64 | |
| 50 | + | purego.RegisterLibFunc(&EmptyEmptyFn, lib, "EmptyEmpty") | |
| 51 | + | if ret := EmptyEmptyFn(EmptyEmpty{}); ret != expectedUnsigned { | |
| 52 | + | t.Fatalf("EmptyEmpty returned %#x wanted %#x", ret, expectedUnsigned) | |
| 53 | + | } | |
| 54 | + | var EmptyEmptyWithReg func(uint32, EmptyEmpty, uint32) int64 | |
| 55 | + | purego.RegisterLibFunc(&EmptyEmptyWithReg, lib, "EmptyEmptyWithReg") | |
| 56 | + | if ret := EmptyEmptyWithReg(0xdead, EmptyEmpty{}, 0xbeef); ret != expectedUnsigned { | |
| 57 | + | t.Fatalf("EmptyEmptyWithReg returned %#x wanted %#x", ret, expectedUnsigned) | |
| 58 | + | } | |
| 59 | + | } | |
| 60 | + | { | |
| 61 | + | type GreaterThan16Bytes struct { | |
| 62 | + | x, y, z *int64 | |
| 63 | + | } | |
| 64 | + | var x, y, z int64 = 0xEF, 0xBE00, 0xDEAD0000 | |
| 65 | + | var GreaterThan16BytesFn func(GreaterThan16Bytes) int64 | |
| 66 | + | purego.RegisterLibFunc(&GreaterThan16BytesFn, lib, "GreaterThan16Bytes") | |
| 67 | + | if ret := GreaterThan16BytesFn(GreaterThan16Bytes{x: &x, y: &y, z: &z}); ret != expectedUnsigned { | |
| 68 | + | t.Fatalf("GreaterThan16Bytes returned %#x wanted %#x", ret, expectedUnsigned) | |
| 69 | + | } | |
| 70 | + | } | |
| 71 | + | { | |
| 72 | + | type GreaterThan16BytesStruct struct { | |
| 73 | + | a struct { | |
| 74 | + | x, y, z *int64 | |
| 75 | + | } | |
| 76 | + | } | |
| 77 | + | var x, y, z int64 = 0xEF, 0xBE00, 0xDEAD0000 | |
| 78 | + | var GreaterThan16BytesStructFn func(GreaterThan16BytesStruct) int64 | |
| 79 | + | purego.RegisterLibFunc(&GreaterThan16BytesStructFn, lib, "GreaterThan16BytesStruct") | |
| 80 | + | if ret := GreaterThan16BytesStructFn(GreaterThan16BytesStruct{a: struct{ x, y, z *int64 }{x: &x, y: &y, z: &z}}); ret != expectedUnsigned { | |
| 81 | + | t.Fatalf("GreaterThan16BytesStructFn returned %#x wanted %#x", ret, expectedUnsigned) | |
| 82 | + | } | |
| 83 | + | } | |
| 84 | + | { | |
| 85 | + | type GreaterThan16Bytes struct { | |
| 86 | + | x, y, z *int64 | |
| 87 | + | } | |
| 88 | + | var x, y, z int64 = 0xEF, 0xBE00, 0xDEAD0000 | |
| 89 | + | var AfterRegisters func(a, b, c, d, e, f, g, h int, bytes GreaterThan16Bytes) int64 | |
| 90 | + | purego.RegisterLibFunc(&AfterRegisters, lib, "AfterRegisters") | |
| 91 | + | if ret := AfterRegisters(0xD0000000, 0xE000000, 0xA00000, 0xD0000, 0xB000, 0xE00, 0xE0, 0xF, GreaterThan16Bytes{x: &x, y: &y, z: &z}); ret != expectedUnsigned { | |
| 92 | + | t.Fatalf("AfterRegisters returned %#x wanted %#x", ret, expectedUnsigned) | |
| 93 | + | } | |
| 94 | + | var BeforeRegisters func(bytes GreaterThan16Bytes, a, b int64) uint64 | |
| 95 | + | z -= 0xFF | |
| 96 | + | purego.RegisterLibFunc(&BeforeRegisters, lib, "BeforeRegisters") | |
| 97 | + | if ret := BeforeRegisters(GreaterThan16Bytes{&x, &y, &z}, 0x0F, 0xF0); ret != expectedUnsigned { | |
| 98 | + | t.Fatalf("BeforeRegisters returned %#x wanted %#x", ret, expectedUnsigned) | |
| 99 | + | } | |
| 100 | + | } | |
| 101 | + | { | |
| 102 | + | type IntLessThan16Bytes struct { | |
| 103 | + | x, y int64 | |
| 104 | + | } | |
| 105 | + | var IntLessThan16BytesFn func(bytes IntLessThan16Bytes) int64 | |
| 106 | + | purego.RegisterLibFunc(&IntLessThan16BytesFn, lib, "IntLessThan16Bytes") | |
| 107 | + | if ret := IntLessThan16BytesFn(IntLessThan16Bytes{0xDEAD0000, 0xBEEF}); ret != expectedUnsigned { | |
| 108 | + | t.Fatalf("IntLessThan16BytesFn returned %#x wanted %#x", ret, expectedUnsigned) | |
| 109 | + | } | |
| 110 | + | } | |
| 111 | + | { | |
| 112 | + | type FloatLessThan16Bytes struct { | |
| 113 | + | x, y float32 | |
| 114 | + | } | |
| 115 | + | var FloatLessThan16BytesFn func(FloatLessThan16Bytes) float32 | |
| 116 | + | purego.RegisterLibFunc(&FloatLessThan16BytesFn, lib, "FloatLessThan16Bytes") | |
| 117 | + | if ret := FloatLessThan16BytesFn(FloatLessThan16Bytes{3, 7}); ret != expectedFloat { | |
| 118 | + | t.Fatalf("FloatLessThan16Bytes returned %f wanted %f", ret, expectedFloat) | |
| 119 | + | } | |
| 120 | + | } | |
| 121 | + | { | |
| 122 | + | type FloatAndInt struct { | |
| 123 | + | x float32 | |
| 124 | + | y int32 | |
| 125 | + | } | |
| 126 | + | var FloatAndIntFn func(FloatAndInt) float32 | |
| 127 | + | purego.RegisterLibFunc(&FloatAndIntFn, lib, "FloatAndInt") | |
| 128 | + | if ret := FloatAndIntFn(FloatAndInt{3, 7}); ret != expectedFloat { | |
| 129 | + | t.Fatalf("FloatAndIntFn returned %f wanted %f", ret, expectedFloat) | |
| 130 | + | } | |
| 131 | + | } | |
| 132 | + | { | |
| 133 | + | type DoubleStruct struct { | |
| 134 | + | x float64 | |
| 135 | + | } | |
| 136 | + | var DoubleStructFn func(DoubleStruct) float64 | |
| 137 | + | purego.RegisterLibFunc(&DoubleStructFn, lib, "DoubleStruct") | |
| 138 | + | if ret := DoubleStructFn(DoubleStruct{10}); ret != expectedDouble { | |
| 139 | + | t.Fatalf("DoubleStruct returned %f wanted %f", ret, expectedDouble) | |
| 140 | + | } | |
| 141 | + | } | |
| 142 | + | { | |
| 143 | + | type TwoDoubleStruct struct { | |
| 144 | + | x, y float64 | |
| 145 | + | } | |
| 146 | + | var TwoDoubleStructFn func(TwoDoubleStruct) float64 | |
| 147 | + | purego.RegisterLibFunc(&TwoDoubleStructFn, lib, "TwoDoubleStruct") | |
| 148 | + | if ret := TwoDoubleStructFn(TwoDoubleStruct{3, 7}); ret != expectedDouble { | |
| 149 | + | t.Fatalf("TwoDoubleStruct returned %f wanted %f", ret, expectedDouble) | |
| 150 | + | } | |
| 151 | + | } | |
| 152 | + | { | |
| 153 | + | type TwoDoubleTwoStruct struct { | |
| 154 | + | x struct { | |
| 155 | + | x, y float64 | |
| 156 | + | } | |
| 157 | + | } | |
| 158 | + | var TwoDoubleTwoStructFn func(TwoDoubleTwoStruct) float64 | |
| 159 | + | purego.RegisterLibFunc(&TwoDoubleTwoStructFn, lib, "TwoDoubleTwoStruct") | |
| 160 | + | if ret := TwoDoubleTwoStructFn(TwoDoubleTwoStruct{x: struct{ x, y float64 }{x: 3, y: 7}}); ret != expectedDouble { | |
| 161 | + | t.Fatalf("TwoDoubleTwoStruct returned %f wanted %f", ret, expectedDouble) | |
| 162 | + | } | |
| 163 | + | } | |
| 164 | + | { | |
| 165 | + | type ThreeDoubleStruct struct { | |
| 166 | + | x, y, z float64 | |
| 167 | + | } | |
| 168 | + | var ThreeDoubleStructFn func(ThreeDoubleStruct) float64 | |
| 169 | + | purego.RegisterLibFunc(&ThreeDoubleStructFn, lib, "ThreeDoubleStruct") | |
| 170 | + | if ret := ThreeDoubleStructFn(ThreeDoubleStruct{1, 3, 6}); ret != expectedDouble { | |
| 171 | + | t.Fatalf("ThreeDoubleStructFn returned %f wanted %f", ret, expectedDouble) | |
| 172 | + | } | |
| 173 | + | } | |
| 174 | + | { | |
| 175 | + | type LargeFloatStruct struct { | |
| 176 | + | a, b, c, d, e, f float64 | |
| 177 | + | } | |
| 178 | + | var LargeFloatStructFn func(LargeFloatStruct) float64 | |
| 179 | + | purego.RegisterLibFunc(&LargeFloatStructFn, lib, "LargeFloatStruct") | |
| 180 | + | if ret := LargeFloatStructFn(LargeFloatStruct{1, 2, 3, 4, 5, -5}); ret != expectedDouble { | |
| 181 | + | t.Fatalf("LargeFloatStructFn returned %f wanted %f", ret, expectedFloat) | |
| 182 | + | } | |
| 183 | + | var LargeFloatStructWithRegs func(a, b, c float64, s LargeFloatStruct) float64 | |
| 184 | + | purego.RegisterLibFunc(&LargeFloatStructWithRegs, lib, "LargeFloatStructWithRegs") | |
| 185 | + | if ret := LargeFloatStructWithRegs(1, -1, 0, LargeFloatStruct{1, 2, 3, 4, 5, -5}); ret != expectedDouble { | |
| 186 | + | t.Fatalf("LargeFloatStructWithRegs returned %f wanted %f", ret, expectedFloat) | |
| 187 | + | } | |
| 188 | + | } | |
| 189 | + | { | |
| 190 | + | type Rect struct { | |
| 191 | + | x, y, w, h float64 | |
| 192 | + | } | |
| 193 | + | var RectangleWithRegs func(a, b, c, d, e float64, rect Rect) float64 | |
| 194 | + | purego.RegisterLibFunc(&RectangleWithRegs, lib, "RectangleWithRegs") | |
| 195 | + | if ret := RectangleWithRegs(1, 2, 3, 4, -2, Rect{1, 2, 3, -4}); ret != expectedDouble { | |
| 196 | + | t.Fatalf("RectangleWithRegs returned %f wanted %f", ret, expectedDouble) | |
| 197 | + | } | |
| 198 | + | var RectangleSubtract func(rect Rect) float64 | |
| 199 | + | purego.RegisterLibFunc(&RectangleSubtract, lib, "RectangleSubtract") | |
| 200 | + | if ret := RectangleSubtract(Rect{15, 5, 3, 7}); ret != expectedDouble { | |
| 201 | + | t.Fatalf("RectangleSubtract returned %f wanted %f", ret, expectedDouble) | |
| 202 | + | } | |
| 203 | + | var Rectangle func(rect Rect) float64 | |
| 204 | + | purego.RegisterLibFunc(&Rectangle, lib, "Rectangle") | |
| 205 | + | if ret := Rectangle(Rect{1, 2, 3, 4}); ret != expectedDouble { | |
| 206 | + | t.Fatalf("Rectangle returned %f wanted %f", ret, expectedFloat) | |
| 207 | + | } | |
| 208 | + | } | |
| 209 | + | { | |
| 210 | + | type FloatArray struct { | |
| 211 | + | a [2]float64 | |
| 212 | + | } | |
| 213 | + | var FloatArrayFn func(rect FloatArray) float64 | |
| 214 | + | purego.RegisterLibFunc(&FloatArrayFn, lib, "FloatArray") | |
| 215 | + | if ret := FloatArrayFn(FloatArray{a: [2]float64{3, 7}}); ret != expectedDouble { | |
| 216 | + | t.Fatalf("FloatArray returned %f wanted %f", ret, expectedFloat) | |
| 217 | + | } | |
| 218 | + | } | |
| 219 | + | { | |
| 220 | + | type UnsignedChar4Bytes struct { | |
| 221 | + | a, b, c, d byte | |
| 222 | + | } | |
| 223 | + | var UnsignedChar4BytesFn func(UnsignedChar4Bytes) uint32 | |
| 224 | + | purego.RegisterLibFunc(&UnsignedChar4BytesFn, lib, "UnsignedChar4Bytes") | |
| 225 | + | if ret := UnsignedChar4BytesFn(UnsignedChar4Bytes{a: 0xDE, b: 0xAD, c: 0xBE, d: 0xEF}); ret != expectedUnsigned { | |
| 226 | + | t.Fatalf("UnsignedChar4BytesFn returned %#x wanted %#x", ret, expectedUnsigned) | |
| 227 | + | } | |
| 228 | + | } | |
| 229 | + | { | |
| 230 | + | type UnsignedChar4BytesStruct struct { | |
| 231 | + | x struct { | |
| 232 | + | a byte | |
| 233 | + | } | |
| 234 | + | y struct { | |
| 235 | + | b byte | |
| 236 | + | } | |
| 237 | + | z struct { | |
| 238 | + | c byte | |
| 239 | + | } | |
| 240 | + | w struct { | |
| 241 | + | d byte | |
| 242 | + | } | |
| 243 | + | } | |
| 244 | + | var UnsignedChar4BytesStructFn func(UnsignedChar4BytesStruct) uint32 | |
| 245 | + | purego.RegisterLibFunc(&UnsignedChar4BytesStructFn, lib, "UnsignedChar4BytesStruct") | |
| 246 | + | if ret := UnsignedChar4BytesStructFn(UnsignedChar4BytesStruct{ | |
| 247 | + | x: struct{ a byte }{a: 0xDE}, | |
| 248 | + | y: struct{ b byte }{b: 0xAD}, | |
| 249 | + | z: struct{ c byte }{c: 0xBE}, | |
| 250 | + | w: struct{ d byte }{d: 0xEF}, | |
| 251 | + | }); ret != expectedUnsigned { | |
| 252 | + | t.Fatalf("UnsignedChar4BytesStructFn returned %#x wanted %#x", ret, expectedUnsigned) | |
| 253 | + | } | |
| 254 | + | } | |
| 255 | + | { | |
| 256 | + | type Short struct { | |
| 257 | + | a, b, c, d uint16 | |
| 258 | + | } | |
| 259 | + | var ShortFn func(Short) uint64 | |
| 260 | + | purego.RegisterLibFunc(&ShortFn, lib, "Short") | |
| 261 | + | if ret := ShortFn(Short{a: 0xDEAD, b: 0xBEEF, c: 0xCAFE, d: 0xBABE}); ret != expectedLong { | |
| 262 | + | t.Fatalf("ShortFn returned %#x wanted %#x", ret, expectedLong) | |
| 263 | + | } | |
| 264 | + | } | |
| 265 | + | { | |
| 266 | + | type Int struct { | |
| 267 | + | a, b uint32 | |
| 268 | + | } | |
| 269 | + | var IntFn func(Int) uint64 | |
| 270 | + | purego.RegisterLibFunc(&IntFn, lib, "Int") | |
| 271 | + | if ret := IntFn(Int{a: 0xDEADBEEF, b: 0xCAFEBABE}); ret != expectedLong { | |
| 272 | + | t.Fatalf("IntFn returned %#x wanted %#x", ret, expectedLong) | |
| 273 | + | } | |
| 274 | + | } | |
| 275 | + | { | |
| 276 | + | type Long struct { | |
| 277 | + | a uint64 | |
| 278 | + | } | |
| 279 | + | var LongFn func(Long) uint64 | |
| 280 | + | purego.RegisterLibFunc(&LongFn, lib, "Long") | |
| 281 | + | if ret := LongFn(Long{a: 0xDEADBEEFCAFEBABE}); ret != expectedLong { | |
| 282 | + | t.Fatalf("LongFn returned %#x wanted %#x", ret, expectedLong) | |
| 283 | + | } | |
| 284 | + | } | |
| 285 | + | { | |
| 286 | + | type Char8Bytes struct { | |
| 287 | + | a, b, c, d, e, f, g, h int8 | |
| 288 | + | } | |
| 289 | + | var Char8BytesFn func(Char8Bytes) int32 | |
| 290 | + | purego.RegisterLibFunc(&Char8BytesFn, lib, "Char8Bytes") | |
| 291 | + | if ret := Char8BytesFn(Char8Bytes{a: -128, b: 127, c: 3, d: -88, e: -3, f: 34, g: -48, h: -20}); ret != expectedSigned { | |
| 292 | + | t.Fatalf("Char8Bytes returned %d wanted %d", ret, expectedSigned) | |
| 293 | + | } | |
| 294 | + | } | |
| 295 | + | { | |
| 296 | + | type Odd struct { | |
| 297 | + | a, b, c byte | |
| 298 | + | } | |
| 299 | + | var OddFn func(Odd) int32 | |
| 300 | + | purego.RegisterLibFunc(&OddFn, lib, "Odd") | |
| 301 | + | if ret := OddFn(Odd{a: 12, b: 23, c: 46}); ret != expectedOdd { | |
| 302 | + | t.Fatalf("OddFn returned %d wanted %d", ret, expectedOdd) | |
| 303 | + | } | |
| 304 | + | } | |
| 305 | + | { | |
| 306 | + | type Char2Short1 struct { | |
| 307 | + | a, b byte | |
| 308 | + | c uint16 | |
| 309 | + | } | |
| 310 | + | var Char2Short1s func(Char2Short1) int32 | |
| 311 | + | purego.RegisterLibFunc(&Char2Short1s, lib, "Char2Short1s") | |
| 312 | + | if ret := Char2Short1s(Char2Short1{a: 12, b: 23, c: 46}); ret != expectedOdd { | |
| 313 | + | t.Fatalf("Char2Short1s returned %d wanted %d", ret, expectedOdd) | |
| 314 | + | } | |
| 315 | + | } | |
| 316 | + | { | |
| 317 | + | type SignedChar2Short1 struct { | |
| 318 | + | a, b int8 | |
| 319 | + | c int16 | |
| 320 | + | } | |
| 321 | + | var SignedChar2Short1Fn func(SignedChar2Short1) int32 | |
| 322 | + | purego.RegisterLibFunc(&SignedChar2Short1Fn, lib, "SignedChar2Short1") | |
| 323 | + | if ret := SignedChar2Short1Fn(SignedChar2Short1{a: 100, b: -23, c: -200}); ret != expectedSigned { | |
| 324 | + | t.Fatalf("SignedChar2Short1Fn returned %d wanted %d", ret, expectedSigned) | |
| 325 | + | } | |
| 326 | + | } | |
| 327 | + | { | |
| 328 | + | type Array4UnsignedChars struct { | |
| 329 | + | a [4]uint8 | |
| 330 | + | } | |
| 331 | + | var Array4UnsignedCharsFn func(chars Array4UnsignedChars) uint32 | |
| 332 | + | purego.RegisterLibFunc(&Array4UnsignedCharsFn, lib, "Array4UnsignedChars") | |
| 333 | + | if ret := Array4UnsignedCharsFn(Array4UnsignedChars{a: [...]uint8{0xDE, 0xAD, 0xBE, 0xEF}}); ret != expectedUnsigned { | |
| 334 | + | t.Fatalf("Array4UnsignedCharsFn returned %#x wanted %#x", ret, expectedUnsigned) | |
| 335 | + | } | |
| 336 | + | } | |
| 337 | + | { | |
| 338 | + | type Array3UnsignedChar struct { | |
| 339 | + | a [3]uint8 | |
| 340 | + | } | |
| 341 | + | var Array3UnsignedChars func(chars Array3UnsignedChar) uint32 | |
| 342 | + | purego.RegisterLibFunc(&Array3UnsignedChars, lib, "Array3UnsignedChars") | |
| 343 | + | if ret := Array3UnsignedChars(Array3UnsignedChar{a: [...]uint8{0xDE, 0xAD, 0xBE}}); ret != expectedUnsigned { | |
| 344 | + | t.Fatalf("Array4UnsignedCharsFn returned %#x wanted %#x", ret, expectedUnsigned) | |
| 345 | + | } | |
| 346 | + | } | |
| 347 | + | { | |
| 348 | + | type Array2UnsignedShort struct { | |
| 349 | + | a [2]uint16 | |
| 350 | + | } | |
| 351 | + | var Array2UnsignedShorts func(chars Array2UnsignedShort) uint32 | |
| 352 | + | purego.RegisterLibFunc(&Array2UnsignedShorts, lib, "Array2UnsignedShorts") | |
| 353 | + | if ret := Array2UnsignedShorts(Array2UnsignedShort{a: [...]uint16{0xDEAD, 0xBEEF}}); ret != expectedUnsigned { | |
| 354 | + | t.Fatalf("Array4UnsignedCharsFn returned %#x wanted %#x", ret, expectedUnsigned) | |
| 355 | + | } | |
| 356 | + | } | |
| 357 | + | { | |
| 358 | + | type Array4Chars struct { | |
| 359 | + | a [4]int8 | |
| 360 | + | } | |
| 361 | + | var Array4CharsFn func(chars Array4Chars) int32 | |
| 362 | + | purego.RegisterLibFunc(&Array4CharsFn, lib, "Array4Chars") | |
| 363 | + | if ret := Array4CharsFn(Array4Chars{a: [...]int8{100, -127, 4, -100}}); ret != expectedSigned { | |
| 364 | + | t.Fatalf("Array4CharsFn returned %#x wanted %#x", ret, expectedSigned) | |
| 365 | + | } | |
| 366 | + | } | |
| 367 | + | { | |
| 368 | + | type Array2Short struct { | |
| 369 | + | a [2]int16 | |
| 370 | + | } | |
| 371 | + | var Array2Shorts func(chars Array2Short) int32 | |
| 372 | + | purego.RegisterLibFunc(&Array2Shorts, lib, "Array2Shorts") | |
| 373 | + | if ret := Array2Shorts(Array2Short{a: [...]int16{-333, 210}}); ret != expectedSigned { | |
| 374 | + | t.Fatalf("Array4Shorts returned %#x wanted %#x", ret, expectedSigned) | |
| 375 | + | } | |
| 376 | + | } | |
| 377 | + | { | |
| 378 | + | type Array3Short struct { | |
| 379 | + | a [3]int16 | |
| 380 | + | } | |
| 381 | + | var Array3Shorts func(chars Array3Short) int32 | |
| 382 | + | purego.RegisterLibFunc(&Array3Shorts, lib, "Array3Shorts") | |
| 383 | + | if ret := Array3Shorts(Array3Short{a: [...]int16{-333, 100, 110}}); ret != expectedSigned { | |
| 384 | + | t.Fatalf("Array4Shorts returned %#x wanted %#x", ret, expectedSigned) | |
| 385 | + | } | |
| 386 | + | } | |
| 387 | + | { | |
| 388 | + | type BoolStruct struct { | |
| 389 | + | b bool | |
| 390 | + | } | |
| 391 | + | var BoolStructFn func(BoolStruct) bool | |
| 392 | + | purego.RegisterLibFunc(&BoolStructFn, lib, "BoolStruct") | |
| 393 | + | if ret := BoolStructFn(BoolStruct{true}); ret != true { | |
| 394 | + | t.Fatalf("BoolStructFn returned %v wanted %v", ret, true) | |
| 395 | + | } | |
| 396 | + | if ret := BoolStructFn(BoolStruct{false}); ret != false { | |
| 397 | + | t.Fatalf("BoolStructFn returned %v wanted %v", ret, false) | |
| 398 | + | } | |
| 399 | + | } | |
| 400 | + | { | |
| 401 | + | type BoolFloat struct { | |
| 402 | + | b bool | |
| 403 | + | _ [3]byte // purego won't do padding for you so make sure it aligns properly with C struct | |
| 404 | + | f float32 | |
| 405 | + | } | |
| 406 | + | var BoolFloatFn func(BoolFloat) float32 | |
| 407 | + | purego.RegisterLibFunc(&BoolFloatFn, lib, "BoolFloat") | |
| 408 | + | if ret := BoolFloatFn(BoolFloat{b: true, f: 10}); ret != expectedFloat { | |
| 409 | + | t.Fatalf("BoolFloatFn returned %f wanted %f", ret, expectedFloat) | |
| 410 | + | } | |
| 411 | + | if ret := BoolFloatFn(BoolFloat{b: false, f: 10}); ret != -expectedFloat { | |
| 412 | + | t.Fatalf("BoolFloatFn returned %f wanted %f", ret, -expectedFloat) | |
| 413 | + | } | |
| 414 | + | } | |
| 415 | + | { | |
| 416 | + | type point struct{ x, y float64 } | |
| 417 | + | type size struct{ width, height float64 } | |
| 418 | + | type Content struct { | |
| 419 | + | point point | |
| 420 | + | size size | |
| 421 | + | } | |
| 422 | + | var InitWithContentRect func(*int, Content, int32, int32, bool) uint64 | |
| 423 | + | purego.RegisterLibFunc(&InitWithContentRect, lib, "InitWithContentRect") | |
| 424 | + | if ret := InitWithContentRect(new(int), | |
| 425 | + | // These numbers are created so that when added together and then divided by 11 it produces 0xdeadbeef | |
| 426 | + | Content{point{x: 41_000_000_000, y: 95_000_000}, size{width: 214_000, height: 149}}, | |
| 427 | + | 15, 4, true); ret != expectedUnsigned { | |
| 428 | + | t.Fatalf("InitWithContentRect returned %d wanted %#x", ret, expectedUnsigned) | |
| 429 | + | } | |
| 430 | + | } | |
| 431 | + | } | |
| @@ -0,0 +1,431 @@ | |||||
| 1 | + | // SPDX-License-Identifier: Apache-2.0 | |||
| 2 | + | // SPDX-FileCopyrightText: 2024 The Ebitengine Authors | |||
| 3 | + | ||||
| 4 | + | //go:build darwin && (arm64 || amd64) | |||
| 5 | + | ||||
| 6 | + | package purego_test | |||
| 7 | + | ||||
| 8 | + | import ( | |||
| 9 | + | "os" | |||
| 10 | + | "path/filepath" | |||
| 11 | + | "testing" | |||
| 12 | + | ||||
| 13 | + | "github.com/ebitengine/purego" | |||
| 14 | + | ) | |||
| 15 | + | ||||
| 16 | + | func TestRegisterFunc_structArgs(t *testing.T) { | |||
| 17 | + | libFileName := filepath.Join(t.TempDir(), "structtest.so") | |||
| 18 | + | t.Logf("Build %v", libFileName) | |||
| 19 | + | ||||
| 20 | + | if err := buildSharedLib("CC", libFileName, filepath.Join("structtest", "struct_test.c")); err != nil { | |||
| 21 | + | t.Fatal(err) | |||
| 22 | + | } | |||
| 23 | + | defer os.Remove(libFileName) | |||
| 24 | + | ||||
| 25 | + | lib, err := purego.Dlopen(libFileName, purego.RTLD_NOW|purego.RTLD_GLOBAL) | |||
| 26 | + | if err != nil { | |||
| 27 | + | t.Fatalf("Dlopen(%q) failed: %v", libFileName, err) | |||
| 28 | + | } | |||
| 29 | + | ||||
| 30 | + | const ( | |||
| 31 | + | expectedUnsigned = 0xdeadbeef | |||
| 32 | + | expectedSigned = -123 | |||
| 33 | + | expectedOdd = 12 + 23 + 46 | |||
| 34 | + | expectedLong uint64 = 0xdeadbeefcafebabe | |||
| 35 | + | expectedFloat float32 = 10 | |||
| 36 | + | expectedDouble float64 = 10 | |||
| 37 | + | ) | |||
| 38 | + | ||||
| 39 | + | { | |||
| 40 | + | type Empty struct{} | |||
| 41 | + | var NoStruct func(Empty) int64 | |||
| 42 | + | purego.RegisterLibFunc(&NoStruct, lib, "NoStruct") | |||
| 43 | + | if ret := NoStruct(Empty{}); ret != expectedUnsigned { | |||
| 44 | + | t.Fatalf("NoStruct returned %#x wanted %#x", ret, expectedUnsigned) | |||
| 45 | + | } | |||
| 46 | + | } | |||
| 47 | + | { | |||
| 48 | + | type EmptyEmpty struct{} | |||
| 49 | + | var EmptyEmptyFn func(EmptyEmpty) int64 | |||
| 50 | + | purego.RegisterLibFunc(&EmptyEmptyFn, lib, "EmptyEmpty") | |||
| 51 | + | if ret := EmptyEmptyFn(EmptyEmpty{}); ret != expectedUnsigned { | |||
| 52 | + | t.Fatalf("EmptyEmpty returned %#x wanted %#x", ret, expectedUnsigned) | |||
| 53 | + | } | |||
| 54 | + | var EmptyEmptyWithReg func(uint32, EmptyEmpty, uint32) int64 | |||
| 55 | + | purego.RegisterLibFunc(&EmptyEmptyWithReg, lib, "EmptyEmptyWithReg") | |||
| 56 | + | if ret := EmptyEmptyWithReg(0xdead, EmptyEmpty{}, 0xbeef); ret != expectedUnsigned { | |||
| 57 | + | t.Fatalf("EmptyEmptyWithReg returned %#x wanted %#x", ret, expectedUnsigned) | |||
| 58 | + | } | |||
| 59 | + | } | |||
| 60 | + | { | |||
| 61 | + | type GreaterThan16Bytes struct { | |||
| 62 | + | x, y, z *int64 | |||
| 63 | + | } | |||
| 64 | + | var x, y, z int64 = 0xEF, 0xBE00, 0xDEAD0000 | |||
| 65 | + | var GreaterThan16BytesFn func(GreaterThan16Bytes) int64 | |||
| 66 | + | purego.RegisterLibFunc(&GreaterThan16BytesFn, lib, "GreaterThan16Bytes") | |||
| 67 | + | if ret := GreaterThan16BytesFn(GreaterThan16Bytes{x: &x, y: &y, z: &z}); ret != expectedUnsigned { | |||
| 68 | + | t.Fatalf("GreaterThan16Bytes returned %#x wanted %#x", ret, expectedUnsigned) | |||
| 69 | + | } | |||
| 70 | + | } | |||
| 71 | + | { | |||
| 72 | + | type GreaterThan16BytesStruct struct { | |||
| 73 | + | a struct { | |||
| 74 | + | x, y, z *int64 | |||
| 75 | + | } | |||
| 76 | + | } | |||
| 77 | + | var x, y, z int64 = 0xEF, 0xBE00, 0xDEAD0000 | |||
| 78 | + | var GreaterThan16BytesStructFn func(GreaterThan16BytesStruct) int64 | |||
| 79 | + | purego.RegisterLibFunc(&GreaterThan16BytesStructFn, lib, "GreaterThan16BytesStruct") | |||
| 80 | + | if ret := GreaterThan16BytesStructFn(GreaterThan16BytesStruct{a: struct{ x, y, z *int64 }{x: &x, y: &y, z: &z}}); ret != expectedUnsigned { | |||
| 81 | + | t.Fatalf("GreaterThan16BytesStructFn returned %#x wanted %#x", ret, expectedUnsigned) | |||
| 82 | + | } | |||
| 83 | + | } | |||
| 84 | + | { | |||
| 85 | + | type GreaterThan16Bytes struct { | |||
| 86 | + | x, y, z *int64 | |||
| 87 | + | } | |||
| 88 | + | var x, y, z int64 = 0xEF, 0xBE00, 0xDEAD0000 | |||
| 89 | + | var AfterRegisters func(a, b, c, d, e, f, g, h int, bytes GreaterThan16Bytes) int64 | |||
| 90 | + | purego.RegisterLibFunc(&AfterRegisters, lib, "AfterRegisters") | |||
| 91 | + | if ret := AfterRegisters(0xD0000000, 0xE000000, 0xA00000, 0xD0000, 0xB000, 0xE00, 0xE0, 0xF, GreaterThan16Bytes{x: &x, y: &y, z: &z}); ret != expectedUnsigned { | |||
| 92 | + | t.Fatalf("AfterRegisters returned %#x wanted %#x", ret, expectedUnsigned) | |||
| 93 | + | } | |||
| 94 | + | var BeforeRegisters func(bytes GreaterThan16Bytes, a, b int64) uint64 | |||
| 95 | + | z -= 0xFF | |||
| 96 | + | purego.RegisterLibFunc(&BeforeRegisters, lib, "BeforeRegisters") | |||
| 97 | + | if ret := BeforeRegisters(GreaterThan16Bytes{&x, &y, &z}, 0x0F, 0xF0); ret != expectedUnsigned { | |||
| 98 | + | t.Fatalf("BeforeRegisters returned %#x wanted %#x", ret, expectedUnsigned) | |||
| 99 | + | } | |||
| 100 | + | } | |||
| 101 | + | { | |||
| 102 | + | type IntLessThan16Bytes struct { | |||
| 103 | + | x, y int64 | |||
| 104 | + | } | |||
| 105 | + | var IntLessThan16BytesFn func(bytes IntLessThan16Bytes) int64 | |||
| 106 | + | purego.RegisterLibFunc(&IntLessThan16BytesFn, lib, "IntLessThan16Bytes") | |||
| 107 | + | if ret := IntLessThan16BytesFn(IntLessThan16Bytes{0xDEAD0000, 0xBEEF}); ret != expectedUnsigned { | |||
| 108 | + | t.Fatalf("IntLessThan16BytesFn returned %#x wanted %#x", ret, expectedUnsigned) | |||
| 109 | + | } | |||
| 110 | + | } | |||
| 111 | + | { | |||
| 112 | + | type FloatLessThan16Bytes struct { | |||
| 113 | + | x, y float32 | |||
| 114 | + | } | |||
| 115 | + | var FloatLessThan16BytesFn func(FloatLessThan16Bytes) float32 | |||
| 116 | + | purego.RegisterLibFunc(&FloatLessThan16BytesFn, lib, "FloatLessThan16Bytes") | |||
| 117 | + | if ret := FloatLessThan16BytesFn(FloatLessThan16Bytes{3, 7}); ret != expectedFloat { | |||
| 118 | + | t.Fatalf("FloatLessThan16Bytes returned %f wanted %f", ret, expectedFloat) | |||
| 119 | + | } | |||
| 120 | + | } | |||
| 121 | + | { | |||
| 122 | + | type FloatAndInt struct { | |||
| 123 | + | x float32 | |||
| 124 | + | y int32 | |||
| 125 | + | } | |||
| 126 | + | var FloatAndIntFn func(FloatAndInt) float32 | |||
| 127 | + | purego.RegisterLibFunc(&FloatAndIntFn, lib, "FloatAndInt") | |||
| 128 | + | if ret := FloatAndIntFn(FloatAndInt{3, 7}); ret != expectedFloat { | |||
| 129 | + | t.Fatalf("FloatAndIntFn returned %f wanted %f", ret, expectedFloat) | |||
| 130 | + | } | |||
| 131 | + | } | |||
| 132 | + | { | |||
| 133 | + | type DoubleStruct struct { | |||
| 134 | + | x float64 | |||
| 135 | + | } | |||
| 136 | + | var DoubleStructFn func(DoubleStruct) float64 | |||
| 137 | + | purego.RegisterLibFunc(&DoubleStructFn, lib, "DoubleStruct") | |||
| 138 | + | if ret := DoubleStructFn(DoubleStruct{10}); ret != expectedDouble { | |||
| 139 | + | t.Fatalf("DoubleStruct returned %f wanted %f", ret, expectedDouble) | |||
| 140 | + | } | |||
| 141 | + | } | |||
| 142 | + | { | |||
| 143 | + | type TwoDoubleStruct struct { | |||
| 144 | + | x, y float64 | |||
| 145 | + | } | |||
| 146 | + | var TwoDoubleStructFn func(TwoDoubleStruct) float64 | |||
| 147 | + | purego.RegisterLibFunc(&TwoDoubleStructFn, lib, "TwoDoubleStruct") | |||
| 148 | + | if ret := TwoDoubleStructFn(TwoDoubleStruct{3, 7}); ret != expectedDouble { | |||
| 149 | + | t.Fatalf("TwoDoubleStruct returned %f wanted %f", ret, expectedDouble) | |||
| 150 | + | } | |||
| 151 | + | } | |||
| 152 | + | { | |||
| 153 | + | type TwoDoubleTwoStruct struct { | |||
| 154 | + | x struct { | |||
| 155 | + | x, y float64 | |||
| 156 | + | } | |||
| 157 | + | } | |||
| 158 | + | var TwoDoubleTwoStructFn func(TwoDoubleTwoStruct) float64 | |||
| 159 | + | purego.RegisterLibFunc(&TwoDoubleTwoStructFn, lib, "TwoDoubleTwoStruct") | |||
| 160 | + | if ret := TwoDoubleTwoStructFn(TwoDoubleTwoStruct{x: struct{ x, y float64 }{x: 3, y: 7}}); ret != expectedDouble { | |||
| 161 | + | t.Fatalf("TwoDoubleTwoStruct returned %f wanted %f", ret, expectedDouble) | |||
| 162 | + | } | |||
| 163 | + | } | |||
| 164 | + | { | |||
| 165 | + | type ThreeDoubleStruct struct { | |||
| 166 | + | x, y, z float64 | |||
| 167 | + | } | |||
| 168 | + | var ThreeDoubleStructFn func(ThreeDoubleStruct) float64 | |||
| 169 | + | purego.RegisterLibFunc(&ThreeDoubleStructFn, lib, "ThreeDoubleStruct") | |||
| 170 | + | if ret := ThreeDoubleStructFn(ThreeDoubleStruct{1, 3, 6}); ret != expectedDouble { | |||
| 171 | + | t.Fatalf("ThreeDoubleStructFn returned %f wanted %f", ret, expectedDouble) | |||
| 172 | + | } | |||
| 173 | + | } | |||
| 174 | + | { | |||
| 175 | + | type LargeFloatStruct struct { | |||
| 176 | + | a, b, c, d, e, f float64 | |||
| 177 | + | } | |||
| 178 | + | var LargeFloatStructFn func(LargeFloatStruct) float64 | |||
| 179 | + | purego.RegisterLibFunc(&LargeFloatStructFn, lib, "LargeFloatStruct") | |||
| 180 | + | if ret := LargeFloatStructFn(LargeFloatStruct{1, 2, 3, 4, 5, -5}); ret != expectedDouble { | |||
| 181 | + | t.Fatalf("LargeFloatStructFn returned %f wanted %f", ret, expectedFloat) | |||
| 182 | + | } | |||
| 183 | + | var LargeFloatStructWithRegs func(a, b, c float64, s LargeFloatStruct) float64 | |||
| 184 | + | purego.RegisterLibFunc(&LargeFloatStructWithRegs, lib, "LargeFloatStructWithRegs") | |||
| 185 | + | if ret := LargeFloatStructWithRegs(1, -1, 0, LargeFloatStruct{1, 2, 3, 4, 5, -5}); ret != expectedDouble { | |||
| 186 | + | t.Fatalf("LargeFloatStructWithRegs returned %f wanted %f", ret, expectedFloat) | |||
| 187 | + | } | |||
| 188 | + | } | |||
| 189 | + | { | |||
| 190 | + | type Rect struct { | |||
| 191 | + | x, y, w, h float64 | |||
| 192 | + | } | |||
| 193 | + | var RectangleWithRegs func(a, b, c, d, e float64, rect Rect) float64 | |||
| 194 | + | purego.RegisterLibFunc(&RectangleWithRegs, lib, "RectangleWithRegs") | |||
| 195 | + | if ret := RectangleWithRegs(1, 2, 3, 4, -2, Rect{1, 2, 3, -4}); ret != expectedDouble { | |||
| 196 | + | t.Fatalf("RectangleWithRegs returned %f wanted %f", ret, expectedDouble) | |||
| 197 | + | } | |||
| 198 | + | var RectangleSubtract func(rect Rect) float64 | |||
| 199 | + | purego.RegisterLibFunc(&RectangleSubtract, lib, "RectangleSubtract") | |||
| 200 | + | if ret := RectangleSubtract(Rect{15, 5, 3, 7}); ret != expectedDouble { | |||
| 201 | + | t.Fatalf("RectangleSubtract returned %f wanted %f", ret, expectedDouble) | |||
| 202 | + | } | |||
| 203 | + | var Rectangle func(rect Rect) float64 | |||
| 204 | + | purego.RegisterLibFunc(&Rectangle, lib, "Rectangle") | |||
| 205 | + | if ret := Rectangle(Rect{1, 2, 3, 4}); ret != expectedDouble { | |||
| 206 | + | t.Fatalf("Rectangle returned %f wanted %f", ret, expectedFloat) | |||
| 207 | + | } | |||
| 208 | + | } | |||
| 209 | + | { | |||
| 210 | + | type FloatArray struct { | |||
| 211 | + | a [2]float64 | |||
| 212 | + | } | |||
| 213 | + | var FloatArrayFn func(rect FloatArray) float64 | |||
| 214 | + | purego.RegisterLibFunc(&FloatArrayFn, lib, "FloatArray") | |||
| 215 | + | if ret := FloatArrayFn(FloatArray{a: [2]float64{3, 7}}); ret != expectedDouble { | |||
| 216 | + | t.Fatalf("FloatArray returned %f wanted %f", ret, expectedFloat) | |||
| 217 | + | } | |||
| 218 | + | } | |||
| 219 | + | { | |||
| 220 | + | type UnsignedChar4Bytes struct { | |||
| 221 | + | a, b, c, d byte | |||
| 222 | + | } | |||
| 223 | + | var UnsignedChar4BytesFn func(UnsignedChar4Bytes) uint32 | |||
| 224 | + | purego.RegisterLibFunc(&UnsignedChar4BytesFn, lib, "UnsignedChar4Bytes") | |||
| 225 | + | if ret := UnsignedChar4BytesFn(UnsignedChar4Bytes{a: 0xDE, b: 0xAD, c: 0xBE, d: 0xEF}); ret != expectedUnsigned { | |||
| 226 | + | t.Fatalf("UnsignedChar4BytesFn returned %#x wanted %#x", ret, expectedUnsigned) | |||
| 227 | + | } | |||
| 228 | + | } | |||
| 229 | + | { | |||
| 230 | + | type UnsignedChar4BytesStruct struct { | |||
| 231 | + | x struct { | |||
| 232 | + | a byte | |||
| 233 | + | } | |||
| 234 | + | y struct { | |||
| 235 | + | b byte | |||
| 236 | + | } | |||
| 237 | + | z struct { | |||
| 238 | + | c byte | |||
| 239 | + | } | |||
| 240 | + | w struct { | |||
| 241 | + | d byte | |||
| 242 | + | } | |||
| 243 | + | } | |||
| 244 | + | var UnsignedChar4BytesStructFn func(UnsignedChar4BytesStruct) uint32 | |||
| 245 | + | purego.RegisterLibFunc(&UnsignedChar4BytesStructFn, lib, "UnsignedChar4BytesStruct") | |||
| 246 | + | if ret := UnsignedChar4BytesStructFn(UnsignedChar4BytesStruct{ | |||
| 247 | + | x: struct{ a byte }{a: 0xDE}, | |||
| 248 | + | y: struct{ b byte }{b: 0xAD}, | |||
| 249 | + | z: struct{ c byte }{c: 0xBE}, | |||
| 250 | + | w: struct{ d byte }{d: 0xEF}, | |||
| 251 | + | }); ret != expectedUnsigned { | |||
| 252 | + | t.Fatalf("UnsignedChar4BytesStructFn returned %#x wanted %#x", ret, expectedUnsigned) | |||
| 253 | + | } | |||
| 254 | + | } | |||
| 255 | + | { | |||
| 256 | + | type Short struct { | |||
| 257 | + | a, b, c, d uint16 | |||
| 258 | + | } | |||
| 259 | + | var ShortFn func(Short) uint64 | |||
| 260 | + | purego.RegisterLibFunc(&ShortFn, lib, "Short") | |||
| 261 | + | if ret := ShortFn(Short{a: 0xDEAD, b: 0xBEEF, c: 0xCAFE, d: 0xBABE}); ret != expectedLong { | |||
| 262 | + | t.Fatalf("ShortFn returned %#x wanted %#x", ret, expectedLong) | |||
| 263 | + | } | |||
| 264 | + | } | |||
| 265 | + | { | |||
| 266 | + | type Int struct { | |||
| 267 | + | a, b uint32 | |||
| 268 | + | } | |||
| 269 | + | var IntFn func(Int) uint64 | |||
| 270 | + | purego.RegisterLibFunc(&IntFn, lib, "Int") | |||
| 271 | + | if ret := IntFn(Int{a: 0xDEADBEEF, b: 0xCAFEBABE}); ret != expectedLong { | |||
| 272 | + | t.Fatalf("IntFn returned %#x wanted %#x", ret, expectedLong) | |||
| 273 | + | } | |||
| 274 | + | } | |||
| 275 | + | { | |||
| 276 | + | type Long struct { | |||
| 277 | + | a uint64 | |||
| 278 | + | } | |||
| 279 | + | var LongFn func(Long) uint64 | |||
| 280 | + | purego.RegisterLibFunc(&LongFn, lib, "Long") | |||
| 281 | + | if ret := LongFn(Long{a: 0xDEADBEEFCAFEBABE}); ret != expectedLong { | |||
| 282 | + | t.Fatalf("LongFn returned %#x wanted %#x", ret, expectedLong) | |||
| 283 | + | } | |||
| 284 | + | } | |||
| 285 | + | { | |||
| 286 | + | type Char8Bytes struct { | |||
| 287 | + | a, b, c, d, e, f, g, h int8 | |||
| 288 | + | } | |||
| 289 | + | var Char8BytesFn func(Char8Bytes) int32 | |||
| 290 | + | purego.RegisterLibFunc(&Char8BytesFn, lib, "Char8Bytes") | |||
| 291 | + | if ret := Char8BytesFn(Char8Bytes{a: -128, b: 127, c: 3, d: -88, e: -3, f: 34, g: -48, h: -20}); ret != expectedSigned { | |||
| 292 | + | t.Fatalf("Char8Bytes returned %d wanted %d", ret, expectedSigned) | |||
| 293 | + | } | |||
| 294 | + | } | |||
| 295 | + | { | |||
| 296 | + | type Odd struct { | |||
| 297 | + | a, b, c byte | |||
| 298 | + | } | |||
| 299 | + | var OddFn func(Odd) int32 | |||
| 300 | + | purego.RegisterLibFunc(&OddFn, lib, "Odd") | |||
| 301 | + | if ret := OddFn(Odd{a: 12, b: 23, c: 46}); ret != expectedOdd { | |||
| 302 | + | t.Fatalf("OddFn returned %d wanted %d", ret, expectedOdd) | |||
| 303 | + | } | |||
| 304 | + | } | |||
| 305 | + | { | |||
| 306 | + | type Char2Short1 struct { | |||
| 307 | + | a, b byte | |||
| 308 | + | c uint16 | |||
| 309 | + | } | |||
| 310 | + | var Char2Short1s func(Char2Short1) int32 | |||
| 311 | + | purego.RegisterLibFunc(&Char2Short1s, lib, "Char2Short1s") | |||
| 312 | + | if ret := Char2Short1s(Char2Short1{a: 12, b: 23, c: 46}); ret != expectedOdd { | |||
| 313 | + | t.Fatalf("Char2Short1s returned %d wanted %d", ret, expectedOdd) | |||
| 314 | + | } | |||
| 315 | + | } | |||
| 316 | + | { | |||
| 317 | + | type SignedChar2Short1 struct { | |||
| 318 | + | a, b int8 | |||
| 319 | + | c int16 | |||
| 320 | + | } | |||
| 321 | + | var SignedChar2Short1Fn func(SignedChar2Short1) int32 | |||
| 322 | + | purego.RegisterLibFunc(&SignedChar2Short1Fn, lib, "SignedChar2Short1") | |||
| 323 | + | if ret := SignedChar2Short1Fn(SignedChar2Short1{a: 100, b: -23, c: -200}); ret != expectedSigned { | |||
| 324 | + | t.Fatalf("SignedChar2Short1Fn returned %d wanted %d", ret, expectedSigned) | |||
| 325 | + | } | |||
| 326 | + | } | |||
| 327 | + | { | |||
| 328 | + | type Array4UnsignedChars struct { | |||
| 329 | + | a [4]uint8 | |||
| 330 | + | } | |||
| 331 | + | var Array4UnsignedCharsFn func(chars Array4UnsignedChars) uint32 | |||
| 332 | + | purego.RegisterLibFunc(&Array4UnsignedCharsFn, lib, "Array4UnsignedChars") | |||
| 333 | + | if ret := Array4UnsignedCharsFn(Array4UnsignedChars{a: [...]uint8{0xDE, 0xAD, 0xBE, 0xEF}}); ret != expectedUnsigned { | |||
| 334 | + | t.Fatalf("Array4UnsignedCharsFn returned %#x wanted %#x", ret, expectedUnsigned) | |||
| 335 | + | } | |||
| 336 | + | } | |||
| 337 | + | { | |||
| 338 | + | type Array3UnsignedChar struct { | |||
| 339 | + | a [3]uint8 | |||
| 340 | + | } | |||
| 341 | + | var Array3UnsignedChars func(chars Array3UnsignedChar) uint32 | |||
| 342 | + | purego.RegisterLibFunc(&Array3UnsignedChars, lib, "Array3UnsignedChars") | |||
| 343 | + | if ret := Array3UnsignedChars(Array3UnsignedChar{a: [...]uint8{0xDE, 0xAD, 0xBE}}); ret != expectedUnsigned { | |||
| 344 | + | t.Fatalf("Array4UnsignedCharsFn returned %#x wanted %#x", ret, expectedUnsigned) | |||
| 345 | + | } | |||
| 346 | + | } | |||
| 347 | + | { | |||
| 348 | + | type Array2UnsignedShort struct { | |||
| 349 | + | a [2]uint16 | |||
| 350 | + | } | |||
| 351 | + | var Array2UnsignedShorts func(chars Array2UnsignedShort) uint32 | |||
| 352 | + | purego.RegisterLibFunc(&Array2UnsignedShorts, lib, "Array2UnsignedShorts") | |||
| 353 | + | if ret := Array2UnsignedShorts(Array2UnsignedShort{a: [...]uint16{0xDEAD, 0xBEEF}}); ret != expectedUnsigned { | |||
| 354 | + | t.Fatalf("Array4UnsignedCharsFn returned %#x wanted %#x", ret, expectedUnsigned) | |||
| 355 | + | } | |||
| 356 | + | } | |||
| 357 | + | { | |||
| 358 | + | type Array4Chars struct { | |||
| 359 | + | a [4]int8 | |||
| 360 | + | } | |||
| 361 | + | var Array4CharsFn func(chars Array4Chars) int32 | |||
| 362 | + | purego.RegisterLibFunc(&Array4CharsFn, lib, "Array4Chars") | |||
| 363 | + | if ret := Array4CharsFn(Array4Chars{a: [...]int8{100, -127, 4, -100}}); ret != expectedSigned { | |||
| 364 | + | t.Fatalf("Array4CharsFn returned %#x wanted %#x", ret, expectedSigned) | |||
| 365 | + | } | |||
| 366 | + | } | |||
| 367 | + | { | |||
| 368 | + | type Array2Short struct { | |||
| 369 | + | a [2]int16 | |||
| 370 | + | } | |||
| 371 | + | var Array2Shorts func(chars Array2Short) int32 | |||
| 372 | + | purego.RegisterLibFunc(&Array2Shorts, lib, "Array2Shorts") | |||
| 373 | + | if ret := Array2Shorts(Array2Short{a: [...]int16{-333, 210}}); ret != expectedSigned { | |||
| 374 | + | t.Fatalf("Array4Shorts returned %#x wanted %#x", ret, expectedSigned) | |||
| 375 | + | } | |||
| 376 | + | } | |||
| 377 | + | { | |||
| 378 | + | type Array3Short struct { | |||
| 379 | + | a [3]int16 | |||
| 380 | + | } | |||
| 381 | + | var Array3Shorts func(chars Array3Short) int32 | |||
| 382 | + | purego.RegisterLibFunc(&Array3Shorts, lib, "Array3Shorts") | |||
| 383 | + | if ret := Array3Shorts(Array3Short{a: [...]int16{-333, 100, 110}}); ret != expectedSigned { | |||
| 384 | + | t.Fatalf("Array4Shorts returned %#x wanted %#x", ret, expectedSigned) | |||
| 385 | + | } | |||
| 386 | + | } | |||
| 387 | + | { | |||
| 388 | + | type BoolStruct struct { | |||
| 389 | + | b bool | |||
| 390 | + | } | |||
| 391 | + | var BoolStructFn func(BoolStruct) bool | |||
| 392 | + | purego.RegisterLibFunc(&BoolStructFn, lib, "BoolStruct") | |||
| 393 | + | if ret := BoolStructFn(BoolStruct{true}); ret != true { | |||
| 394 | + | t.Fatalf("BoolStructFn returned %v wanted %v", ret, true) | |||
| 395 | + | } | |||
| 396 | + | if ret := BoolStructFn(BoolStruct{false}); ret != false { | |||
| 397 | + | t.Fatalf("BoolStructFn returned %v wanted %v", ret, false) | |||
| 398 | + | } | |||
| 399 | + | } | |||
| 400 | + | { | |||
| 401 | + | type BoolFloat struct { | |||
| 402 | + | b bool | |||
| 403 | + | _ [3]byte // purego won't do padding for you so make sure it aligns properly with C struct | |||
| 404 | + | f float32 | |||
| 405 | + | } | |||
| 406 | + | var BoolFloatFn func(BoolFloat) float32 | |||
| 407 | + | purego.RegisterLibFunc(&BoolFloatFn, lib, "BoolFloat") | |||
| 408 | + | if ret := BoolFloatFn(BoolFloat{b: true, f: 10}); ret != expectedFloat { | |||
| 409 | + | t.Fatalf("BoolFloatFn returned %f wanted %f", ret, expectedFloat) | |||
| 410 | + | } | |||
| 411 | + | if ret := BoolFloatFn(BoolFloat{b: false, f: 10}); ret != -expectedFloat { | |||
| 412 | + | t.Fatalf("BoolFloatFn returned %f wanted %f", ret, -expectedFloat) | |||
| 413 | + | } | |||
| 414 | + | } | |||
| 415 | + | { | |||
| 416 | + | type point struct{ x, y float64 } | |||
| 417 | + | type size struct{ width, height float64 } | |||
| 418 | + | type Content struct { | |||
| 419 | + | point point | |||
| 420 | + | size size | |||
| 421 | + | } | |||
| 422 | + | var InitWithContentRect func(*int, Content, int32, int32, bool) uint64 | |||
| 423 | + | purego.RegisterLibFunc(&InitWithContentRect, lib, "InitWithContentRect") | |||
| 424 | + | if ret := InitWithContentRect(new(int), | |||
| 425 | + | // These numbers are created so that when added together and then divided by 11 it produces 0xdeadbeef | |||
| 426 | + | Content{point{x: 41_000_000_000, y: 95_000_000}, size{width: 214_000, height: 149}}, | |||
| 427 | + | 15, 4, true); ret != expectedUnsigned { | |||
| 428 | + | t.Fatalf("InitWithContentRect returned %d wanted %#x", ret, expectedUnsigned) | |||
| 429 | + | } | |||
| 430 | + | } | |||
| 431 | + | } | |||
A structtest/struct_test.c +319 -0
| @@ -0,0 +1,319 @@ | |||
| 1 | + | // SPDX-License-Identifier: Apache-2.0 | |
| 2 | + | // SPDX-FileCopyrightText: 2024 The Ebitengine Authors | |
| 3 | + | ||
| 4 | + | // Empty is empty | |
| 5 | + | struct Empty {}; | |
| 6 | + | ||
| 7 | + | // NoStruct tests that an empty struct doesn't cause issues | |
| 8 | + | unsigned long NoStruct(struct Empty e) { | |
| 9 | + | return 0xdeadbeef; | |
| 10 | + | } | |
| 11 | + | ||
| 12 | + | struct EmptyEmpty { | |
| 13 | + | struct Empty x; | |
| 14 | + | }; | |
| 15 | + | ||
| 16 | + | unsigned long EmptyEmpty(struct EmptyEmpty e) { | |
| 17 | + | return 0xdeadbeef; | |
| 18 | + | } | |
| 19 | + | ||
| 20 | + | unsigned long EmptyEmptyWithReg(unsigned int x, struct EmptyEmpty e, unsigned int y) { | |
| 21 | + | return (x << 16) | y; | |
| 22 | + | } | |
| 23 | + | ||
| 24 | + | // GreaterThan16Bytes is 24 bytes on 64 bit systems | |
| 25 | + | struct GreaterThan16Bytes { | |
| 26 | + | long *x, *y, *z; | |
| 27 | + | }; | |
| 28 | + | ||
| 29 | + | // GreaterThan16Bytes is a basic test for structs bigger than 16 bytes | |
| 30 | + | unsigned long GreaterThan16Bytes(struct GreaterThan16Bytes g) { | |
| 31 | + | return *g.x + *g.y + *g.z; | |
| 32 | + | } | |
| 33 | + | ||
| 34 | + | // AfterRegisters tests to make sure that structs placed on the stack work properly | |
| 35 | + | unsigned long AfterRegisters(long a, long b, long c, long d, long e, long f, long g, long h, struct GreaterThan16Bytes bytes) { | |
| 36 | + | long registers = a + b + c + d + e + f + g + h; | |
| 37 | + | long stack = *bytes.x + *bytes.y + *bytes.z; | |
| 38 | + | if (registers != stack) { | |
| 39 | + | return 0xbadbad; | |
| 40 | + | } | |
| 41 | + | if (stack != 0xdeadbeef) { | |
| 42 | + | return 0xcafebad; | |
| 43 | + | } | |
| 44 | + | return stack; | |
| 45 | + | } | |
| 46 | + | ||
| 47 | + | unsigned long BeforeRegisters(struct GreaterThan16Bytes bytes, long a, long b) { | |
| 48 | + | return *bytes.x + *bytes.y + *bytes.z + a + b; | |
| 49 | + | } | |
| 50 | + | ||
| 51 | + | struct GreaterThan16BytesStruct { | |
| 52 | + | struct { | |
| 53 | + | long *x, *y, *z; | |
| 54 | + | } a ; | |
| 55 | + | }; | |
| 56 | + | ||
| 57 | + | unsigned long GreaterThan16BytesStruct(struct GreaterThan16BytesStruct g) { | |
| 58 | + | return *(g.a.x) + *(g.a.y) + *(g.a.z); | |
| 59 | + | } | |
| 60 | + | ||
| 61 | + | struct IntLessThan16Bytes { | |
| 62 | + | long x, y; | |
| 63 | + | }; | |
| 64 | + | ||
| 65 | + | unsigned long IntLessThan16Bytes(struct IntLessThan16Bytes l) { | |
| 66 | + | return l.x + l.y; | |
| 67 | + | } | |
| 68 | + | ||
| 69 | + | struct FloatLessThan16Bytes { | |
| 70 | + | float x, y; | |
| 71 | + | }; | |
| 72 | + | ||
| 73 | + | float FloatLessThan16Bytes(struct FloatLessThan16Bytes f) { | |
| 74 | + | return f.x + f.y; | |
| 75 | + | } | |
| 76 | + | ||
| 77 | + | struct FloatAndInt { | |
| 78 | + | float x; | |
| 79 | + | int y; | |
| 80 | + | }; | |
| 81 | + | ||
| 82 | + | float FloatAndInt(struct FloatAndInt f) { | |
| 83 | + | return f.x + f.y; | |
| 84 | + | } | |
| 85 | + | ||
| 86 | + | struct DoubleStruct { | |
| 87 | + | double x; | |
| 88 | + | }; | |
| 89 | + | ||
| 90 | + | double DoubleStruct(struct DoubleStruct d) { | |
| 91 | + | return d.x; | |
| 92 | + | } | |
| 93 | + | ||
| 94 | + | struct TwoDoubleStruct { | |
| 95 | + | double x, y; | |
| 96 | + | }; | |
| 97 | + | ||
| 98 | + | double TwoDoubleStruct(struct TwoDoubleStruct d) { | |
| 99 | + | return d.x + d.y; | |
| 100 | + | } | |
| 101 | + | ||
| 102 | + | struct TwoDoubleTwoStruct { | |
| 103 | + | struct { | |
| 104 | + | double x, y; | |
| 105 | + | } s; | |
| 106 | + | }; | |
| 107 | + | ||
| 108 | + | double TwoDoubleTwoStruct(struct TwoDoubleTwoStruct d) { | |
| 109 | + | return d.s.x + d.s.y; | |
| 110 | + | } | |
| 111 | + | ||
| 112 | + | struct ThreeDoubleStruct { | |
| 113 | + | double x, y, z; | |
| 114 | + | }; | |
| 115 | + | ||
| 116 | + | double ThreeDoubleStruct(struct ThreeDoubleStruct d) { | |
| 117 | + | return d.x + d.y + d.z; | |
| 118 | + | } | |
| 119 | + | ||
| 120 | + | struct LargeFloatStruct { | |
| 121 | + | double a, b, c, d, e, f; | |
| 122 | + | }; | |
| 123 | + | ||
| 124 | + | double LargeFloatStruct(struct LargeFloatStruct s) { | |
| 125 | + | return s.a + s.b + s.c + s.d + s.e + s.f; | |
| 126 | + | } | |
| 127 | + | ||
| 128 | + | double LargeFloatStructWithRegs(double a, double b, double c, struct LargeFloatStruct s) { | |
| 129 | + | return a + b + c + s.a + s.b + s.c + s.d + s.e + s.f; | |
| 130 | + | } | |
| 131 | + | ||
| 132 | + | struct Rect { | |
| 133 | + | double x, y, w, h; | |
| 134 | + | }; | |
| 135 | + | ||
| 136 | + | double Rectangle(struct Rect rect) { | |
| 137 | + | return rect.x + rect.y + rect.w + rect.h; | |
| 138 | + | } | |
| 139 | + | ||
| 140 | + | double RectangleSubtract(struct Rect rect) { | |
| 141 | + | return (rect.x + rect.y) - (rect.w + rect.h); | |
| 142 | + | } | |
| 143 | + | ||
| 144 | + | double RectangleWithRegs(double a, double b, double c, double d, double e, struct Rect rect) { | |
| 145 | + | return a + b + c + d + e + rect.x + rect.y + rect.w + rect.h; | |
| 146 | + | } | |
| 147 | + | ||
| 148 | + | struct FloatArray { | |
| 149 | + | double a[2]; | |
| 150 | + | }; | |
| 151 | + | ||
| 152 | + | double FloatArray(struct FloatArray f) { | |
| 153 | + | return f.a[0] + f.a[1]; | |
| 154 | + | } | |
| 155 | + | ||
| 156 | + | struct UnsignedChar4Bytes { | |
| 157 | + | unsigned char a, b, c, d; | |
| 158 | + | }; | |
| 159 | + | ||
| 160 | + | unsigned int UnsignedChar4Bytes(struct UnsignedChar4Bytes b) { | |
| 161 | + | return (((int)b.a)<<24) | (((int)b.b)<<16) | (((int)b.c)<<8) | (((int)b.d)<<0); | |
| 162 | + | } | |
| 163 | + | ||
| 164 | + | struct UnsignedChar4BytesStruct { | |
| 165 | + | struct { | |
| 166 | + | unsigned char a; | |
| 167 | + | } x; | |
| 168 | + | struct { | |
| 169 | + | unsigned char b; | |
| 170 | + | } y; | |
| 171 | + | struct { | |
| 172 | + | unsigned char c; | |
| 173 | + | } z; | |
| 174 | + | struct { | |
| 175 | + | unsigned char d; | |
| 176 | + | } w; | |
| 177 | + | }; | |
| 178 | + | ||
| 179 | + | unsigned int UnsignedChar4BytesStruct(struct UnsignedChar4BytesStruct b) { | |
| 180 | + | return (((int)b.x.a)<<24) | (((int)b.y.b)<<16) | (((int)b.z.c)<<8) | (((int)b.w.d)<<0); | |
| 181 | + | } | |
| 182 | + | ||
| 183 | + | struct Short { | |
| 184 | + | unsigned short a, b, c, d; | |
| 185 | + | }; | |
| 186 | + | ||
| 187 | + | unsigned long Short(struct Short s) { | |
| 188 | + | return (long)s.a << 48 | (long)s.b << 32 | (long)s.c << 16 | (long)s.d << 0; | |
| 189 | + | } | |
| 190 | + | ||
| 191 | + | struct Int { | |
| 192 | + | unsigned int a, b; | |
| 193 | + | }; | |
| 194 | + | ||
| 195 | + | unsigned long Int(struct Int i) { | |
| 196 | + | return (long)i.a << 32 | (long)i.b << 0; | |
| 197 | + | } | |
| 198 | + | ||
| 199 | + | struct Long { | |
| 200 | + | unsigned long a; | |
| 201 | + | }; | |
| 202 | + | ||
| 203 | + | unsigned long Long(struct Long l) { | |
| 204 | + | return l.a; | |
| 205 | + | } | |
| 206 | + | ||
| 207 | + | struct Char8Bytes { | |
| 208 | + | signed char a, b, c, d, e, f, g, h; | |
| 209 | + | }; | |
| 210 | + | ||
| 211 | + | int Char8Bytes(struct Char8Bytes b) { | |
| 212 | + | return (int)b.a + (int)b.b + (int)b.c + (int)b.d + (int)b.e + (int)b.f + (int)b.g + (int)b.h; | |
| 213 | + | } | |
| 214 | + | ||
| 215 | + | struct Odd { | |
| 216 | + | unsigned char a, b, c; | |
| 217 | + | }; | |
| 218 | + | ||
| 219 | + | int Odd(struct Odd o) { | |
| 220 | + | return (int)o.a + (int)o.b + (int)o.c; | |
| 221 | + | } | |
| 222 | + | ||
| 223 | + | struct Char2Short1 { | |
| 224 | + | unsigned char a, b; | |
| 225 | + | unsigned short c; | |
| 226 | + | }; | |
| 227 | + | ||
| 228 | + | int Char2Short1s(struct Char2Short1 s) { | |
| 229 | + | return (int)s.a + (int)s.b + (int)s.c; | |
| 230 | + | } | |
| 231 | + | ||
| 232 | + | struct SignedChar2Short1 { | |
| 233 | + | signed char a, b; | |
| 234 | + | signed short c; | |
| 235 | + | }; | |
| 236 | + | ||
| 237 | + | int SignedChar2Short1(struct SignedChar2Short1 s) { | |
| 238 | + | return s.a + s.b + s.c; | |
| 239 | + | } | |
| 240 | + | ||
| 241 | + | struct Array4UnsignedChars { | |
| 242 | + | unsigned char a[4]; | |
| 243 | + | }; | |
| 244 | + | ||
| 245 | + | unsigned int Array4UnsignedChars(struct Array4UnsignedChars a) { | |
| 246 | + | return (((int)a.a[0])<<24) | (((int)a.a[1])<<16) | (((int)a.a[2])<<8) | (((int)a.a[3])<<0); | |
| 247 | + | } | |
| 248 | + | ||
| 249 | + | struct Array3UnsignedChar { | |
| 250 | + | unsigned char a[3]; | |
| 251 | + | }; | |
| 252 | + | ||
| 253 | + | unsigned int Array3UnsignedChars(struct Array3UnsignedChar a) { | |
| 254 | + | return (((int)a.a[0])<<24) | (((int)a.a[1])<<16) | (((int)a.a[2])<<8) | 0xef; | |
| 255 | + | } | |
| 256 | + | ||
| 257 | + | struct Array2UnsignedShort { | |
| 258 | + | unsigned short a[2]; | |
| 259 | + | }; | |
| 260 | + | ||
| 261 | + | unsigned int Array2UnsignedShorts(struct Array2UnsignedShort a) { | |
| 262 | + | return (((int)a.a[0])<<16) | (((int)a.a[1])<<0); | |
| 263 | + | } | |
| 264 | + | ||
| 265 | + | struct Array4Chars { | |
| 266 | + | char a[4]; | |
| 267 | + | }; | |
| 268 | + | ||
| 269 | + | int Array4Chars(struct Array4Chars a) { | |
| 270 | + | return (int)a.a[0] + (int)a.a[1] + (int)a.a[2] + (int)a.a[3]; | |
| 271 | + | } | |
| 272 | + | ||
| 273 | + | struct Array2Short { | |
| 274 | + | short a[2]; | |
| 275 | + | }; | |
| 276 | + | ||
| 277 | + | int Array2Shorts(struct Array2Short a) { | |
| 278 | + | return (int)a.a[0] + (int)a.a[1]; | |
| 279 | + | } | |
| 280 | + | ||
| 281 | + | struct Array3Short { | |
| 282 | + | short a[3]; | |
| 283 | + | }; | |
| 284 | + | ||
| 285 | + | int Array3Shorts(struct Array3Short a) { | |
| 286 | + | return (int)a.a[0] + (int)a.a[1] + (int)a.a[2]; | |
| 287 | + | } | |
| 288 | + | ||
| 289 | + | struct BoolStruct { | |
| 290 | + | _Bool b; | |
| 291 | + | }; | |
| 292 | + | ||
| 293 | + | _Bool BoolStruct(struct BoolStruct b) { | |
| 294 | + | return b.b; | |
| 295 | + | } | |
| 296 | + | ||
| 297 | + | struct BoolFloat { | |
| 298 | + | _Bool b; | |
| 299 | + | float f; | |
| 300 | + | }; | |
| 301 | + | ||
| 302 | + | float BoolFloat(struct BoolFloat s) { | |
| 303 | + | if (s.b) | |
| 304 | + | return s.f; | |
| 305 | + | return -s.f; | |
| 306 | + | } | |
| 307 | + | ||
| 308 | + | struct Content { | |
| 309 | + | struct { double x, y; } point; | |
| 310 | + | struct { double width, height; } size; | |
| 311 | + | }; | |
| 312 | + | ||
| 313 | + | unsigned long InitWithContentRect(int *win, struct Content c, int style, int backing, _Bool flag) { | |
| 314 | + | if (win == 0) | |
| 315 | + | return 0xBAD; | |
| 316 | + | if (!flag) | |
| 317 | + | return 0xF1A6; // FLAG | |
| 318 | + | return (unsigned long)(c.point.x + c.point.y + c.size.width + c.size.height) / (style - backing); | |
| 319 | + | } | |
| @@ -0,0 +1,319 @@ | |||||
| 1 | + | // SPDX-License-Identifier: Apache-2.0 | |||
| 2 | + | // SPDX-FileCopyrightText: 2024 The Ebitengine Authors | |||
| 3 | + | ||||
| 4 | + | // Empty is empty | |||
| 5 | + | struct Empty {}; | |||
| 6 | + | ||||
| 7 | + | // NoStruct tests that an empty struct doesn't cause issues | |||
| 8 | + | unsigned long NoStruct(struct Empty e) { | |||
| 9 | + | return 0xdeadbeef; | |||
| 10 | + | } | |||
| 11 | + | ||||
| 12 | + | struct EmptyEmpty { | |||
| 13 | + | struct Empty x; | |||
| 14 | + | }; | |||
| 15 | + | ||||
| 16 | + | unsigned long EmptyEmpty(struct EmptyEmpty e) { | |||
| 17 | + | return 0xdeadbeef; | |||
| 18 | + | } | |||
| 19 | + | ||||
| 20 | + | unsigned long EmptyEmptyWithReg(unsigned int x, struct EmptyEmpty e, unsigned int y) { | |||
| 21 | + | return (x << 16) | y; | |||
| 22 | + | } | |||
| 23 | + | ||||
| 24 | + | // GreaterThan16Bytes is 24 bytes on 64 bit systems | |||
| 25 | + | struct GreaterThan16Bytes { | |||
| 26 | + | long *x, *y, *z; | |||
| 27 | + | }; | |||
| 28 | + | ||||
| 29 | + | // GreaterThan16Bytes is a basic test for structs bigger than 16 bytes | |||
| 30 | + | unsigned long GreaterThan16Bytes(struct GreaterThan16Bytes g) { | |||
| 31 | + | return *g.x + *g.y + *g.z; | |||
| 32 | + | } | |||
| 33 | + | ||||
| 34 | + | // AfterRegisters tests to make sure that structs placed on the stack work properly | |||
| 35 | + | unsigned long AfterRegisters(long a, long b, long c, long d, long e, long f, long g, long h, struct GreaterThan16Bytes bytes) { | |||
| 36 | + | long registers = a + b + c + d + e + f + g + h; | |||
| 37 | + | long stack = *bytes.x + *bytes.y + *bytes.z; | |||
| 38 | + | if (registers != stack) { | |||
| 39 | + | return 0xbadbad; | |||
| 40 | + | } | |||
| 41 | + | if (stack != 0xdeadbeef) { | |||
| 42 | + | return 0xcafebad; | |||
| 43 | + | } | |||
| 44 | + | return stack; | |||
| 45 | + | } | |||
| 46 | + | ||||
| 47 | + | unsigned long BeforeRegisters(struct GreaterThan16Bytes bytes, long a, long b) { | |||
| 48 | + | return *bytes.x + *bytes.y + *bytes.z + a + b; | |||
| 49 | + | } | |||
| 50 | + | ||||
| 51 | + | struct GreaterThan16BytesStruct { | |||
| 52 | + | struct { | |||
| 53 | + | long *x, *y, *z; | |||
| 54 | + | } a ; | |||
| 55 | + | }; | |||
| 56 | + | ||||
| 57 | + | unsigned long GreaterThan16BytesStruct(struct GreaterThan16BytesStruct g) { | |||
| 58 | + | return *(g.a.x) + *(g.a.y) + *(g.a.z); | |||
| 59 | + | } | |||
| 60 | + | ||||
| 61 | + | struct IntLessThan16Bytes { | |||
| 62 | + | long x, y; | |||
| 63 | + | }; | |||
| 64 | + | ||||
| 65 | + | unsigned long IntLessThan16Bytes(struct IntLessThan16Bytes l) { | |||
| 66 | + | return l.x + l.y; | |||
| 67 | + | } | |||
| 68 | + | ||||
| 69 | + | struct FloatLessThan16Bytes { | |||
| 70 | + | float x, y; | |||
| 71 | + | }; | |||
| 72 | + | ||||
| 73 | + | float FloatLessThan16Bytes(struct FloatLessThan16Bytes f) { | |||
| 74 | + | return f.x + f.y; | |||
| 75 | + | } | |||
| 76 | + | ||||
| 77 | + | struct FloatAndInt { | |||
| 78 | + | float x; | |||
| 79 | + | int y; | |||
| 80 | + | }; | |||
| 81 | + | ||||
| 82 | + | float FloatAndInt(struct FloatAndInt f) { | |||
| 83 | + | return f.x + f.y; | |||
| 84 | + | } | |||
| 85 | + | ||||
| 86 | + | struct DoubleStruct { | |||
| 87 | + | double x; | |||
| 88 | + | }; | |||
| 89 | + | ||||
| 90 | + | double DoubleStruct(struct DoubleStruct d) { | |||
| 91 | + | return d.x; | |||
| 92 | + | } | |||
| 93 | + | ||||
| 94 | + | struct TwoDoubleStruct { | |||
| 95 | + | double x, y; | |||
| 96 | + | }; | |||
| 97 | + | ||||
| 98 | + | double TwoDoubleStruct(struct TwoDoubleStruct d) { | |||
| 99 | + | return d.x + d.y; | |||
| 100 | + | } | |||
| 101 | + | ||||
| 102 | + | struct TwoDoubleTwoStruct { | |||
| 103 | + | struct { | |||
| 104 | + | double x, y; | |||
| 105 | + | } s; | |||
| 106 | + | }; | |||
| 107 | + | ||||
| 108 | + | double TwoDoubleTwoStruct(struct TwoDoubleTwoStruct d) { | |||
| 109 | + | return d.s.x + d.s.y; | |||
| 110 | + | } | |||
| 111 | + | ||||
| 112 | + | struct ThreeDoubleStruct { | |||
| 113 | + | double x, y, z; | |||
| 114 | + | }; | |||
| 115 | + | ||||
| 116 | + | double ThreeDoubleStruct(struct ThreeDoubleStruct d) { | |||
| 117 | + | return d.x + d.y + d.z; | |||
| 118 | + | } | |||
| 119 | + | ||||
| 120 | + | struct LargeFloatStruct { | |||
| 121 | + | double a, b, c, d, e, f; | |||
| 122 | + | }; | |||
| 123 | + | ||||
| 124 | + | double LargeFloatStruct(struct LargeFloatStruct s) { | |||
| 125 | + | return s.a + s.b + s.c + s.d + s.e + s.f; | |||
| 126 | + | } | |||
| 127 | + | ||||
| 128 | + | double LargeFloatStructWithRegs(double a, double b, double c, struct LargeFloatStruct s) { | |||
| 129 | + | return a + b + c + s.a + s.b + s.c + s.d + s.e + s.f; | |||
| 130 | + | } | |||
| 131 | + | ||||
| 132 | + | struct Rect { | |||
| 133 | + | double x, y, w, h; | |||
| 134 | + | }; | |||
| 135 | + | ||||
| 136 | + | double Rectangle(struct Rect rect) { | |||
| 137 | + | return rect.x + rect.y + rect.w + rect.h; | |||
| 138 | + | } | |||
| 139 | + | ||||
| 140 | + | double RectangleSubtract(struct Rect rect) { | |||
| 141 | + | return (rect.x + rect.y) - (rect.w + rect.h); | |||
| 142 | + | } | |||
| 143 | + | ||||
| 144 | + | double RectangleWithRegs(double a, double b, double c, double d, double e, struct Rect rect) { | |||
| 145 | + | return a + b + c + d + e + rect.x + rect.y + rect.w + rect.h; | |||
| 146 | + | } | |||
| 147 | + | ||||
| 148 | + | struct FloatArray { | |||
| 149 | + | double a[2]; | |||
| 150 | + | }; | |||
| 151 | + | ||||
| 152 | + | double FloatArray(struct FloatArray f) { | |||
| 153 | + | return f.a[0] + f.a[1]; | |||
| 154 | + | } | |||
| 155 | + | ||||
| 156 | + | struct UnsignedChar4Bytes { | |||
| 157 | + | unsigned char a, b, c, d; | |||
| 158 | + | }; | |||
| 159 | + | ||||
| 160 | + | unsigned int UnsignedChar4Bytes(struct UnsignedChar4Bytes b) { | |||
| 161 | + | return (((int)b.a)<<24) | (((int)b.b)<<16) | (((int)b.c)<<8) | (((int)b.d)<<0); | |||
| 162 | + | } | |||
| 163 | + | ||||
| 164 | + | struct UnsignedChar4BytesStruct { | |||
| 165 | + | struct { | |||
| 166 | + | unsigned char a; | |||
| 167 | + | } x; | |||
| 168 | + | struct { | |||
| 169 | + | unsigned char b; | |||
| 170 | + | } y; | |||
| 171 | + | struct { | |||
| 172 | + | unsigned char c; | |||
| 173 | + | } z; | |||
| 174 | + | struct { | |||
| 175 | + | unsigned char d; | |||
| 176 | + | } w; | |||
| 177 | + | }; | |||
| 178 | + | ||||
| 179 | + | unsigned int UnsignedChar4BytesStruct(struct UnsignedChar4BytesStruct b) { | |||
| 180 | + | return (((int)b.x.a)<<24) | (((int)b.y.b)<<16) | (((int)b.z.c)<<8) | (((int)b.w.d)<<0); | |||
| 181 | + | } | |||
| 182 | + | ||||
| 183 | + | struct Short { | |||
| 184 | + | unsigned short a, b, c, d; | |||
| 185 | + | }; | |||
| 186 | + | ||||
| 187 | + | unsigned long Short(struct Short s) { | |||
| 188 | + | return (long)s.a << 48 | (long)s.b << 32 | (long)s.c << 16 | (long)s.d << 0; | |||
| 189 | + | } | |||
| 190 | + | ||||
| 191 | + | struct Int { | |||
| 192 | + | unsigned int a, b; | |||
| 193 | + | }; | |||
| 194 | + | ||||
| 195 | + | unsigned long Int(struct Int i) { | |||
| 196 | + | return (long)i.a << 32 | (long)i.b << 0; | |||
| 197 | + | } | |||
| 198 | + | ||||
| 199 | + | struct Long { | |||
| 200 | + | unsigned long a; | |||
| 201 | + | }; | |||
| 202 | + | ||||
| 203 | + | unsigned long Long(struct Long l) { | |||
| 204 | + | return l.a; | |||
| 205 | + | } | |||
| 206 | + | ||||
| 207 | + | struct Char8Bytes { | |||
| 208 | + | signed char a, b, c, d, e, f, g, h; | |||
| 209 | + | }; | |||
| 210 | + | ||||
| 211 | + | int Char8Bytes(struct Char8Bytes b) { | |||
| 212 | + | return (int)b.a + (int)b.b + (int)b.c + (int)b.d + (int)b.e + (int)b.f + (int)b.g + (int)b.h; | |||
| 213 | + | } | |||
| 214 | + | ||||
| 215 | + | struct Odd { | |||
| 216 | + | unsigned char a, b, c; | |||
| 217 | + | }; | |||
| 218 | + | ||||
| 219 | + | int Odd(struct Odd o) { | |||
| 220 | + | return (int)o.a + (int)o.b + (int)o.c; | |||
| 221 | + | } | |||
| 222 | + | ||||
| 223 | + | struct Char2Short1 { | |||
| 224 | + | unsigned char a, b; | |||
| 225 | + | unsigned short c; | |||
| 226 | + | }; | |||
| 227 | + | ||||
| 228 | + | int Char2Short1s(struct Char2Short1 s) { | |||
| 229 | + | return (int)s.a + (int)s.b + (int)s.c; | |||
| 230 | + | } | |||
| 231 | + | ||||
| 232 | + | struct SignedChar2Short1 { | |||
| 233 | + | signed char a, b; | |||
| 234 | + | signed short c; | |||
| 235 | + | }; | |||
| 236 | + | ||||
| 237 | + | int SignedChar2Short1(struct SignedChar2Short1 s) { | |||
| 238 | + | return s.a + s.b + s.c; | |||
| 239 | + | } | |||
| 240 | + | ||||
| 241 | + | struct Array4UnsignedChars { | |||
| 242 | + | unsigned char a[4]; | |||
| 243 | + | }; | |||
| 244 | + | ||||
| 245 | + | unsigned int Array4UnsignedChars(struct Array4UnsignedChars a) { | |||
| 246 | + | return (((int)a.a[0])<<24) | (((int)a.a[1])<<16) | (((int)a.a[2])<<8) | (((int)a.a[3])<<0); | |||
| 247 | + | } | |||
| 248 | + | ||||
| 249 | + | struct Array3UnsignedChar { | |||
| 250 | + | unsigned char a[3]; | |||
| 251 | + | }; | |||
| 252 | + | ||||
| 253 | + | unsigned int Array3UnsignedChars(struct Array3UnsignedChar a) { | |||
| 254 | + | return (((int)a.a[0])<<24) | (((int)a.a[1])<<16) | (((int)a.a[2])<<8) | 0xef; | |||
| 255 | + | } | |||
| 256 | + | ||||
| 257 | + | struct Array2UnsignedShort { | |||
| 258 | + | unsigned short a[2]; | |||
| 259 | + | }; | |||
| 260 | + | ||||
| 261 | + | unsigned int Array2UnsignedShorts(struct Array2UnsignedShort a) { | |||
| 262 | + | return (((int)a.a[0])<<16) | (((int)a.a[1])<<0); | |||
| 263 | + | } | |||
| 264 | + | ||||
| 265 | + | struct Array4Chars { | |||
| 266 | + | char a[4]; | |||
| 267 | + | }; | |||
| 268 | + | ||||
| 269 | + | int Array4Chars(struct Array4Chars a) { | |||
| 270 | + | return (int)a.a[0] + (int)a.a[1] + (int)a.a[2] + (int)a.a[3]; | |||
| 271 | + | } | |||
| 272 | + | ||||
| 273 | + | struct Array2Short { | |||
| 274 | + | short a[2]; | |||
| 275 | + | }; | |||
| 276 | + | ||||
| 277 | + | int Array2Shorts(struct Array2Short a) { | |||
| 278 | + | return (int)a.a[0] + (int)a.a[1]; | |||
| 279 | + | } | |||
| 280 | + | ||||
| 281 | + | struct Array3Short { | |||
| 282 | + | short a[3]; | |||
| 283 | + | }; | |||
| 284 | + | ||||
| 285 | + | int Array3Shorts(struct Array3Short a) { | |||
| 286 | + | return (int)a.a[0] + (int)a.a[1] + (int)a.a[2]; | |||
| 287 | + | } | |||
| 288 | + | ||||
| 289 | + | struct BoolStruct { | |||
| 290 | + | _Bool b; | |||
| 291 | + | }; | |||
| 292 | + | ||||
| 293 | + | _Bool BoolStruct(struct BoolStruct b) { | |||
| 294 | + | return b.b; | |||
| 295 | + | } | |||
| 296 | + | ||||
| 297 | + | struct BoolFloat { | |||
| 298 | + | _Bool b; | |||
| 299 | + | float f; | |||
| 300 | + | }; | |||
| 301 | + | ||||
| 302 | + | float BoolFloat(struct BoolFloat s) { | |||
| 303 | + | if (s.b) | |||
| 304 | + | return s.f; | |||
| 305 | + | return -s.f; | |||
| 306 | + | } | |||
| 307 | + | ||||
| 308 | + | struct Content { | |||
| 309 | + | struct { double x, y; } point; | |||
| 310 | + | struct { double width, height; } size; | |||
| 311 | + | }; | |||
| 312 | + | ||||
| 313 | + | unsigned long InitWithContentRect(int *win, struct Content c, int style, int backing, _Bool flag) { | |||
| 314 | + | if (win == 0) | |||
| 315 | + | return 0xBAD; | |||
| 316 | + | if (!flag) | |||
| 317 | + | return 0xF1A6; // FLAG | |||
| 318 | + | return (unsigned long)(c.point.x + c.point.y + c.size.width + c.size.height) / (style - backing); | |||
| 319 | + | } | |||