purego: Add struct return support for darwin amd64 and arm64 (#213)
Add support for returning structs who's fields are floats or integer or struct or arrays containing floats or integers Closes #195
- commit
- 9ef47a2286c0919e2d0129570e89caadfffe882e
- author
- TotallyGamerJet · · 2024-02-27 03:44 UTC
- committer
- GitHub · · 2024-02-27 03:44 UTC
- parent
- be6f20b
- signature
- GPG Unverified
B5690EEEBB952194The key is unknown - refs
- tag: v0.7.0-alpha.2
11 files · +719 -35
M func.go +86 -20
| @@ -6,6 +6,7 @@ | |||
| 6 | 6 | package purego | |
| 7 | 7 | ||
| 8 | 8 | import ( | |
| 9 | + | "fmt" | |
| 9 | 10 | "math" | |
| 10 | 11 | "reflect" | |
| 11 | 12 | "runtime" | |
| @@ -163,6 +164,18 @@ func RegisterFunc(fptr interface{}, cfn uintptr) { | |||
| 163 | 164 | panic("purego: unsupported kind " + arg.Kind().String()) | |
| 164 | 165 | } | |
| 165 | 166 | } | |
| 167 | + | if ty.NumOut() == 1 && ty.Out(0).Kind() == reflect.Struct { | |
| 168 | + | if runtime.GOOS != "darwin" { | |
| 169 | + | panic("purego: struct return values only supported on darwin arm64 & amd64") | |
| 170 | + | } | |
| 171 | + | outType := ty.Out(0) | |
| 172 | + | checkStructFieldsSupported(outType) | |
| 173 | + | if runtime.GOARCH == "amd64" && outType.Size() > maxRegAllocStructSize { | |
| 174 | + | // on amd64 if struct is bigger than 16 bytes allocate the return struct | |
| 175 | + | // and pass it in as a hidden first argument. | |
| 176 | + | ints++ | |
| 177 | + | } | |
| 178 | + | } | |
| 166 | 179 | sizeOfStack := maxArgs - numOfIntegerRegisters() | |
| 167 | 180 | if stack > sizeOfStack { | |
| 168 | 181 | panic("purego: too many arguments") | |
| @@ -229,6 +242,21 @@ func RegisterFunc(fptr interface{}, cfn uintptr) { | |||
| 229 | 242 | runtime.KeepAlive(keepAlive) | |
| 230 | 243 | runtime.KeepAlive(args) | |
| 231 | 244 | }() | |
| 245 | + | var syscall syscall15Args | |
| 246 | + | if ty.NumOut() == 1 && ty.Out(0).Kind() == reflect.Struct { | |
| 247 | + | outType := ty.Out(0) | |
| 248 | + | if runtime.GOARCH == "amd64" && outType.Size() > maxRegAllocStructSize { | |
| 249 | + | val := reflect.New(outType) | |
| 250 | + | keepAlive = append(keepAlive, val) | |
| 251 | + | addInt(val.Pointer()) | |
| 252 | + | } else if runtime.GOARCH == "arm64" && outType.Size() > maxRegAllocStructSize { | |
| 253 | + | if !isAllSameFloat(outType) || outType.NumField() > 4 { | |
| 254 | + | val := reflect.New(outType) | |
| 255 | + | keepAlive = append(keepAlive, val) | |
| 256 | + | syscall.arm64_r8 = val.Pointer() | |
| 257 | + | } | |
| 258 | + | } | |
| 259 | + | } | |
| 232 | 260 | for _, v := range args { | |
| 233 | 261 | switch v.Kind() { | |
| 234 | 262 | case reflect.String: | |
| @@ -260,25 +288,23 @@ func RegisterFunc(fptr interface{}, cfn uintptr) { | |||
| 260 | 288 | panic("purego: unsupported kind: " + v.Kind().String()) | |
| 261 | 289 | } | |
| 262 | 290 | } | |
| 263 | - | // TODO: support structs | |
| 264 | - | var r1, r2 uintptr | |
| 265 | 291 | if runtime.GOARCH == "arm64" || runtime.GOOS != "windows" { | |
| 266 | 292 | // Use the normal arm64 calling convention even on Windows | |
| 267 | - | syscall := syscall15Args{ | |
| 293 | + | syscall = syscall15Args{ | |
| 268 | 294 | cfn, | |
| 269 | 295 | sysargs[0], sysargs[1], sysargs[2], sysargs[3], sysargs[4], sysargs[5], | |
| 270 | 296 | sysargs[6], sysargs[7], sysargs[8], sysargs[9], sysargs[10], sysargs[11], | |
| 271 | 297 | sysargs[12], sysargs[13], sysargs[14], | |
| 272 | 298 | floats[0], floats[1], floats[2], floats[3], floats[4], floats[5], floats[6], floats[7], | |
| 273 | - | 0, 0, 0, | |
| 299 | + | 0, 0, 0, syscall.arm64_r8, | |
| 274 | 300 | } | |
| 275 | 301 | runtime_cgocall(syscall15XABI0, unsafe.Pointer(&syscall)) | |
| 276 | - | r1, r2 = syscall.r1, syscall.r2 | |
| 277 | 302 | } else { | |
| 278 | 303 | // This is a fallback for Windows amd64, 386, and arm. Note this may not support floats | |
| 279 | - | r1, r2, _ = syscall_syscall15X(cfn, sysargs[0], sysargs[1], sysargs[2], sysargs[3], sysargs[4], | |
| 304 | + | syscall.a1, syscall.a2, _ = syscall_syscall15X(cfn, sysargs[0], sysargs[1], sysargs[2], sysargs[3], sysargs[4], | |
| 280 | 305 | sysargs[5], sysargs[6], sysargs[7], sysargs[8], sysargs[9], sysargs[10], sysargs[11], | |
| 281 | 306 | sysargs[12], sysargs[13], sysargs[14]) | |
| 307 | + | syscall.f1 = syscall.a2 // on amd64 a2 stores the float return. On 32bit platforms floats aren't support | |
| 282 | 308 | } | |
| 283 | 309 | if ty.NumOut() == 0 { | |
| 284 | 310 | return nil | |
| @@ -287,31 +313,33 @@ func RegisterFunc(fptr interface{}, cfn uintptr) { | |||
| 287 | 313 | v := reflect.New(outType).Elem() | |
| 288 | 314 | switch outType.Kind() { | |
| 289 | 315 | case reflect.Uintptr, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: | |
| 290 | - | v.SetUint(uint64(r1)) | |
| 316 | + | v.SetUint(uint64(syscall.a1)) | |
| 291 | 317 | case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: | |
| 292 | - | v.SetInt(int64(r1)) | |
| 318 | + | v.SetInt(int64(syscall.a1)) | |
| 293 | 319 | case reflect.Bool: | |
| 294 | - | v.SetBool(byte(r1) != 0) | |
| 320 | + | v.SetBool(byte(syscall.a1) != 0) | |
| 295 | 321 | case reflect.UnsafePointer: | |
| 296 | 322 | // We take the address and then dereference it to trick go vet from creating a possible miss-use of unsafe.Pointer | |
| 297 | - | v.SetPointer(*(*unsafe.Pointer)(unsafe.Pointer(&r1))) | |
| 323 | + | v.SetPointer(*(*unsafe.Pointer)(unsafe.Pointer(&syscall.a1))) | |
| 298 | 324 | case reflect.Ptr: | |
| 299 | - | // It is safe to have the address of r1 not escape because it is immediately dereferenced with .Elem() | |
| 300 | - | v = reflect.NewAt(outType, runtime_noescape(unsafe.Pointer(&r1))).Elem() | |
| 325 | + | // It is safe to have the address of syscall.r1 not escape because it is immediately dereferenced with .Elem() | |
| 326 | + | v = reflect.NewAt(outType, runtime_noescape(unsafe.Pointer(&syscall.a1))).Elem() | |
| 301 | 327 | case reflect.Func: | |
| 302 | 328 | // wrap this C function in a nicely typed Go function | |
| 303 | 329 | v = reflect.New(outType) | |
| 304 | - | RegisterFunc(v.Interface(), r1) | |
| 330 | + | RegisterFunc(v.Interface(), syscall.a1) | |
| 305 | 331 | case reflect.String: | |
| 306 | - | v.SetString(strings.GoString(r1)) | |
| 332 | + | v.SetString(strings.GoString(syscall.a1)) | |
| 307 | 333 | case reflect.Float32: | |
| 308 | - | // NOTE: r2 is only the floating return value on 64bit platforms. | |
| 309 | - | // On 32bit platforms r2 is the upper part of a 64bit return. | |
| 310 | - | v.SetFloat(float64(math.Float32frombits(uint32(r2)))) | |
| 334 | + | // NOTE: syscall.r2 is only the floating return value on 64bit platforms. | |
| 335 | + | // On 32bit platforms syscall.r2 is the upper part of a 64bit return. | |
| 336 | + | v.SetFloat(float64(math.Float32frombits(uint32(syscall.f1)))) | |
| 311 | 337 | case reflect.Float64: | |
| 312 | - | // NOTE: r2 is only the floating return value on 64bit platforms. | |
| 313 | - | // On 32bit platforms r2 is the upper part of a 64bit return. | |
| 314 | - | v.SetFloat(math.Float64frombits(uint64(r2))) | |
| 338 | + | // NOTE: syscall.r2 is only the floating return value on 64bit platforms. | |
| 339 | + | // On 32bit platforms syscall.r2 is the upper part of a 64bit return. | |
| 340 | + | v.SetFloat(math.Float64frombits(uint64(syscall.f1))) | |
| 341 | + | case reflect.Struct: | |
| 342 | + | v = getStruct(outType, syscall) | |
| 315 | 343 | default: | |
| 316 | 344 | panic("purego: unsupported return kind: " + outType.Kind().String()) | |
| 317 | 345 | } | |
| @@ -320,6 +348,44 @@ func RegisterFunc(fptr interface{}, cfn uintptr) { | |||
| 320 | 348 | fn.Set(v) | |
| 321 | 349 | } | |
| 322 | 350 | ||
| 351 | + | // maxRegAllocStructSize is the biggest a struct can be while still fitting in registers. | |
| 352 | + | // if it is bigger than this than enough space must be allocated on the heap and then passed into | |
| 353 | + | // the function as the first parameter on amd64 or in R8 on arm64. | |
| 354 | + | const maxRegAllocStructSize = 16 | |
| 355 | + | ||
| 356 | + | func isAllSameFloat(ty reflect.Type) bool { | |
| 357 | + | first := ty.Field(0).Type.Kind() | |
| 358 | + | if first != reflect.Float32 && first != reflect.Float64 { | |
| 359 | + | return false | |
| 360 | + | } | |
| 361 | + | for i := 0; i < ty.NumField(); i++ { | |
| 362 | + | f := ty.Field(i) | |
| 363 | + | if f.Type.Kind() != first { | |
| 364 | + | return false | |
| 365 | + | } | |
| 366 | + | } | |
| 367 | + | return true | |
| 368 | + | } | |
| 369 | + | ||
| 370 | + | func checkStructFieldsSupported(ty reflect.Type) { | |
| 371 | + | for i := 0; i < ty.NumField(); i++ { | |
| 372 | + | f := ty.Field(i).Type | |
| 373 | + | if f.Kind() == reflect.Array { | |
| 374 | + | f = f.Elem() | |
| 375 | + | } else if f.Kind() == reflect.Struct { | |
| 376 | + | checkStructFieldsSupported(f) | |
| 377 | + | continue | |
| 378 | + | } | |
| 379 | + | switch f.Kind() { | |
| 380 | + | case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, | |
| 381 | + | reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, | |
| 382 | + | reflect.Uintptr, reflect.Ptr, reflect.UnsafePointer, reflect.Float64, reflect.Float32: | |
| 383 | + | default: | |
| 384 | + | panic(fmt.Sprintf("purego: struct field type %s is not supported", f)) | |
| 385 | + | } | |
| 386 | + | } | |
| 387 | + | } | |
| 388 | + | ||
| 323 | 389 | const is32bit = unsafe.Sizeof(uintptr(0)) == 4 | |
| 324 | 390 | ||
| 325 | 391 | func roundUpTo8(val uintptr) uintptr { | |
| @@ -6,6 +6,7 @@ | |||||
| 6 | package purego | 6 | package purego | ||
| 7 | 7 | ||||
| 8 | import ( | 8 | import ( | ||
| 9 | + | "fmt" | |||
| 9 | "math" | 10 | "math" | ||
| 10 | "reflect" | 11 | "reflect" | ||
| 11 | "runtime" | 12 | "runtime" | ||
| @@ -163,6 +164,18 @@ func RegisterFunc(fptr interface{}, cfn uintptr) { | |||||
| 163 | panic("purego: unsupported kind " + arg.Kind().String()) | 164 | panic("purego: unsupported kind " + arg.Kind().String()) | ||
| 164 | } | 165 | } | ||
| 165 | } | 166 | } | ||
| 167 | + | if ty.NumOut() == 1 && ty.Out(0).Kind() == reflect.Struct { | |||
| 168 | + | if runtime.GOOS != "darwin" { | |||
| 169 | + | panic("purego: struct return values only supported on darwin arm64 & amd64") | |||
| 170 | + | } | |||
| 171 | + | outType := ty.Out(0) | |||
| 172 | + | checkStructFieldsSupported(outType) | |||
| 173 | + | if runtime.GOARCH == "amd64" && outType.Size() > maxRegAllocStructSize { | |||
| 174 | + | // on amd64 if struct is bigger than 16 bytes allocate the return struct | |||
| 175 | + | // and pass it in as a hidden first argument. | |||
| 176 | + | ints++ | |||
| 177 | + | } | |||
| 178 | + | } | |||
| 166 | sizeOfStack := maxArgs - numOfIntegerRegisters() | 179 | sizeOfStack := maxArgs - numOfIntegerRegisters() | ||
| 167 | if stack > sizeOfStack { | 180 | if stack > sizeOfStack { | ||
| 168 | panic("purego: too many arguments") | 181 | panic("purego: too many arguments") | ||
| @@ -229,6 +242,21 @@ func RegisterFunc(fptr interface{}, cfn uintptr) { | |||||
| 229 | runtime.KeepAlive(keepAlive) | 242 | runtime.KeepAlive(keepAlive) | ||
| 230 | runtime.KeepAlive(args) | 243 | runtime.KeepAlive(args) | ||
| 231 | }() | 244 | }() | ||
| 245 | + | var syscall syscall15Args | |||
| 246 | + | if ty.NumOut() == 1 && ty.Out(0).Kind() == reflect.Struct { | |||
| 247 | + | outType := ty.Out(0) | |||
| 248 | + | if runtime.GOARCH == "amd64" && outType.Size() > maxRegAllocStructSize { | |||
| 249 | + | val := reflect.New(outType) | |||
| 250 | + | keepAlive = append(keepAlive, val) | |||
| 251 | + | addInt(val.Pointer()) | |||
| 252 | + | } else if runtime.GOARCH == "arm64" && outType.Size() > maxRegAllocStructSize { | |||
| 253 | + | if !isAllSameFloat(outType) || outType.NumField() > 4 { | |||
| 254 | + | val := reflect.New(outType) | |||
| 255 | + | keepAlive = append(keepAlive, val) | |||
| 256 | + | syscall.arm64_r8 = val.Pointer() | |||
| 257 | + | } | |||
| 258 | + | } | |||
| 259 | + | } | |||
| 232 | for _, v := range args { | 260 | for _, v := range args { | ||
| 233 | switch v.Kind() { | 261 | switch v.Kind() { | ||
| 234 | case reflect.String: | 262 | case reflect.String: | ||
| @@ -260,25 +288,23 @@ func RegisterFunc(fptr interface{}, cfn uintptr) { | |||||
| 260 | panic("purego: unsupported kind: " + v.Kind().String()) | 288 | panic("purego: unsupported kind: " + v.Kind().String()) | ||
| 261 | } | 289 | } | ||
| 262 | } | 290 | } | ||
| 263 | - | // TODO: support structs | |||
| 264 | - | var r1, r2 uintptr | |||
| 265 | if runtime.GOARCH == "arm64" || runtime.GOOS != "windows" { | 291 | if runtime.GOARCH == "arm64" || runtime.GOOS != "windows" { | ||
| 266 | // Use the normal arm64 calling convention even on Windows | 292 | // Use the normal arm64 calling convention even on Windows | ||
| 267 | - | syscall := syscall15Args{ | 293 | + | syscall = syscall15Args{ |
| 268 | cfn, | 294 | cfn, | ||
| 269 | sysargs[0], sysargs[1], sysargs[2], sysargs[3], sysargs[4], sysargs[5], | 295 | sysargs[0], sysargs[1], sysargs[2], sysargs[3], sysargs[4], sysargs[5], | ||
| 270 | sysargs[6], sysargs[7], sysargs[8], sysargs[9], sysargs[10], sysargs[11], | 296 | sysargs[6], sysargs[7], sysargs[8], sysargs[9], sysargs[10], sysargs[11], | ||
| 271 | sysargs[12], sysargs[13], sysargs[14], | 297 | sysargs[12], sysargs[13], sysargs[14], | ||
| 272 | floats[0], floats[1], floats[2], floats[3], floats[4], floats[5], floats[6], floats[7], | 298 | floats[0], floats[1], floats[2], floats[3], floats[4], floats[5], floats[6], floats[7], | ||
| 273 | - | 0, 0, 0, | 299 | + | 0, 0, 0, syscall.arm64_r8, |
| 274 | } | 300 | } | ||
| 275 | runtime_cgocall(syscall15XABI0, unsafe.Pointer(&syscall)) | 301 | runtime_cgocall(syscall15XABI0, unsafe.Pointer(&syscall)) | ||
| 276 | - | r1, r2 = syscall.r1, syscall.r2 | |||
| 277 | } else { | 302 | } else { | ||
| 278 | // This is a fallback for Windows amd64, 386, and arm. Note this may not support floats | 303 | // This is a fallback for Windows amd64, 386, and arm. Note this may not support floats | ||
| 279 | - | r1, r2, _ = syscall_syscall15X(cfn, sysargs[0], sysargs[1], sysargs[2], sysargs[3], sysargs[4], | 304 | + | syscall.a1, syscall.a2, _ = syscall_syscall15X(cfn, sysargs[0], sysargs[1], sysargs[2], sysargs[3], sysargs[4], |
| 280 | sysargs[5], sysargs[6], sysargs[7], sysargs[8], sysargs[9], sysargs[10], sysargs[11], | 305 | sysargs[5], sysargs[6], sysargs[7], sysargs[8], sysargs[9], sysargs[10], sysargs[11], | ||
| 281 | sysargs[12], sysargs[13], sysargs[14]) | 306 | sysargs[12], sysargs[13], sysargs[14]) | ||
| 307 | + | syscall.f1 = syscall.a2 // on amd64 a2 stores the float return. On 32bit platforms floats aren't support | |||
| 282 | } | 308 | } | ||
| 283 | if ty.NumOut() == 0 { | 309 | if ty.NumOut() == 0 { | ||
| 284 | return nil | 310 | return nil | ||
| @@ -287,31 +313,33 @@ func RegisterFunc(fptr interface{}, cfn uintptr) { | |||||
| 287 | v := reflect.New(outType).Elem() | 313 | v := reflect.New(outType).Elem() | ||
| 288 | switch outType.Kind() { | 314 | switch outType.Kind() { | ||
| 289 | case reflect.Uintptr, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: | 315 | case reflect.Uintptr, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: | ||
| 290 | - | v.SetUint(uint64(r1)) | 316 | + | v.SetUint(uint64(syscall.a1)) |
| 291 | case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: | 317 | case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: | ||
| 292 | - | v.SetInt(int64(r1)) | 318 | + | v.SetInt(int64(syscall.a1)) |
| 293 | case reflect.Bool: | 319 | case reflect.Bool: | ||
| 294 | - | v.SetBool(byte(r1) != 0) | 320 | + | v.SetBool(byte(syscall.a1) != 0) |
| 295 | case reflect.UnsafePointer: | 321 | case reflect.UnsafePointer: | ||
| 296 | // We take the address and then dereference it to trick go vet from creating a possible miss-use of unsafe.Pointer | 322 | // We take the address and then dereference it to trick go vet from creating a possible miss-use of unsafe.Pointer | ||
| 297 | - | v.SetPointer(*(*unsafe.Pointer)(unsafe.Pointer(&r1))) | 323 | + | v.SetPointer(*(*unsafe.Pointer)(unsafe.Pointer(&syscall.a1))) |
| 298 | case reflect.Ptr: | 324 | case reflect.Ptr: | ||
| 299 | - | // It is safe to have the address of r1 not escape because it is immediately dereferenced with .Elem() | 325 | + | // It is safe to have the address of syscall.r1 not escape because it is immediately dereferenced with .Elem() |
| 300 | - | v = reflect.NewAt(outType, runtime_noescape(unsafe.Pointer(&r1))).Elem() | 326 | + | v = reflect.NewAt(outType, runtime_noescape(unsafe.Pointer(&syscall.a1))).Elem() |
| 301 | case reflect.Func: | 327 | case reflect.Func: | ||
| 302 | // wrap this C function in a nicely typed Go function | 328 | // wrap this C function in a nicely typed Go function | ||
| 303 | v = reflect.New(outType) | 329 | v = reflect.New(outType) | ||
| 304 | - | RegisterFunc(v.Interface(), r1) | 330 | + | RegisterFunc(v.Interface(), syscall.a1) |
| 305 | case reflect.String: | 331 | case reflect.String: | ||
| 306 | - | v.SetString(strings.GoString(r1)) | 332 | + | v.SetString(strings.GoString(syscall.a1)) |
| 307 | case reflect.Float32: | 333 | case reflect.Float32: | ||
| 308 | - | // NOTE: r2 is only the floating return value on 64bit platforms. | 334 | + | // NOTE: syscall.r2 is only the floating return value on 64bit platforms. |
| 309 | - | // On 32bit platforms r2 is the upper part of a 64bit return. | 335 | + | // On 32bit platforms syscall.r2 is the upper part of a 64bit return. |
| 310 | - | v.SetFloat(float64(math.Float32frombits(uint32(r2)))) | 336 | + | v.SetFloat(float64(math.Float32frombits(uint32(syscall.f1)))) |
| 311 | case reflect.Float64: | 337 | case reflect.Float64: | ||
| 312 | - | // NOTE: r2 is only the floating return value on 64bit platforms. | 338 | + | // NOTE: syscall.r2 is only the floating return value on 64bit platforms. |
| 313 | - | // On 32bit platforms r2 is the upper part of a 64bit return. | 339 | + | // On 32bit platforms syscall.r2 is the upper part of a 64bit return. |
| 314 | - | v.SetFloat(math.Float64frombits(uint64(r2))) | 340 | + | v.SetFloat(math.Float64frombits(uint64(syscall.f1))) |
| 341 | + | case reflect.Struct: | |||
| 342 | + | v = getStruct(outType, syscall) | |||
| 315 | default: | 343 | default: | ||
| 316 | panic("purego: unsupported return kind: " + outType.Kind().String()) | 344 | panic("purego: unsupported return kind: " + outType.Kind().String()) | ||
| 317 | } | 345 | } | ||
| @@ -320,6 +348,44 @@ func RegisterFunc(fptr interface{}, cfn uintptr) { | |||||
| 320 | fn.Set(v) | 348 | fn.Set(v) | ||
| 321 | } | 349 | } | ||
| 322 | 350 | ||||
| 351 | + | // maxRegAllocStructSize is the biggest a struct can be while still fitting in registers. | |||
| 352 | + | // if it is bigger than this than enough space must be allocated on the heap and then passed into | |||
| 353 | + | // the function as the first parameter on amd64 or in R8 on arm64. | |||
| 354 | + | const maxRegAllocStructSize = 16 | |||
| 355 | + | ||||
| 356 | + | func isAllSameFloat(ty reflect.Type) bool { | |||
| 357 | + | first := ty.Field(0).Type.Kind() | |||
| 358 | + | if first != reflect.Float32 && first != reflect.Float64 { | |||
| 359 | + | return false | |||
| 360 | + | } | |||
| 361 | + | for i := 0; i < ty.NumField(); i++ { | |||
| 362 | + | f := ty.Field(i) | |||
| 363 | + | if f.Type.Kind() != first { | |||
| 364 | + | return false | |||
| 365 | + | } | |||
| 366 | + | } | |||
| 367 | + | return true | |||
| 368 | + | } | |||
| 369 | + | ||||
| 370 | + | func checkStructFieldsSupported(ty reflect.Type) { | |||
| 371 | + | for i := 0; i < ty.NumField(); i++ { | |||
| 372 | + | f := ty.Field(i).Type | |||
| 373 | + | if f.Kind() == reflect.Array { | |||
| 374 | + | f = f.Elem() | |||
| 375 | + | } else if f.Kind() == reflect.Struct { | |||
| 376 | + | checkStructFieldsSupported(f) | |||
| 377 | + | continue | |||
| 378 | + | } | |||
| 379 | + | switch f.Kind() { | |||
| 380 | + | case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, | |||
| 381 | + | reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, | |||
| 382 | + | reflect.Uintptr, reflect.Ptr, reflect.UnsafePointer, reflect.Float64, reflect.Float32: | |||
| 383 | + | default: | |||
| 384 | + | panic(fmt.Sprintf("purego: struct field type %s is not supported", f)) | |||
| 385 | + | } | |||
| 386 | + | } | |||
| 387 | + | } | |||
| 388 | + | ||||
| 323 | const is32bit = unsafe.Sizeof(uintptr(0)) == 4 | 389 | const is32bit = unsafe.Sizeof(uintptr(0)) == 4 | ||
| 324 | 390 | ||||
| 325 | func roundUpTo8(val uintptr) uintptr { | 391 | func roundUpTo8(val uintptr) uintptr { | ||
M struct_amd64.go +65 -0
| @@ -6,8 +6,73 @@ package purego | |||
| 6 | 6 | import ( | |
| 7 | 7 | "math" | |
| 8 | 8 | "reflect" | |
| 9 | + | "unsafe" | |
| 9 | 10 | ) | |
| 10 | 11 | ||
| 12 | + | func getStruct(outType reflect.Type, syscall syscall15Args) (v reflect.Value) { | |
| 13 | + | outSize := outType.Size() | |
| 14 | + | switch { | |
| 15 | + | case outSize == 0: | |
| 16 | + | return reflect.New(outType).Elem() | |
| 17 | + | case outSize <= 8: | |
| 18 | + | if isAllFloats(outType) { | |
| 19 | + | // 2 float32s or 1 float64s are return in the float register | |
| 20 | + | return reflect.NewAt(outType, unsafe.Pointer(&struct{ a uintptr }{syscall.f1})).Elem() | |
| 21 | + | } | |
| 22 | + | // up to 8 bytes is returned in RAX | |
| 23 | + | return reflect.NewAt(outType, unsafe.Pointer(&struct{ a uintptr }{syscall.a1})).Elem() | |
| 24 | + | case outSize <= 16: | |
| 25 | + | r1, r2 := syscall.a1, syscall.a2 | |
| 26 | + | if isAllFloats(outType) { | |
| 27 | + | r1 = syscall.f1 | |
| 28 | + | r2 = syscall.f2 | |
| 29 | + | } else { | |
| 30 | + | // check first 8 bytes if it's floats | |
| 31 | + | hasFirstFloat := false | |
| 32 | + | f1 := outType.Field(0).Type | |
| 33 | + | if f1.Kind() == reflect.Float64 || f1.Kind() == reflect.Float32 && outType.Field(1).Type.Kind() == reflect.Float32 { | |
| 34 | + | r1 = syscall.f1 | |
| 35 | + | hasFirstFloat = true | |
| 36 | + | } | |
| 37 | + | ||
| 38 | + | // find index of the field that starts the second 8 bytes | |
| 39 | + | var i int | |
| 40 | + | for i = 0; i < outType.NumField(); i++ { | |
| 41 | + | if outType.Field(i).Offset == 8 { | |
| 42 | + | break | |
| 43 | + | } | |
| 44 | + | } | |
| 45 | + | ||
| 46 | + | // check last 8 bytes if they are floats | |
| 47 | + | f1 = outType.Field(i).Type | |
| 48 | + | if f1.Kind() == reflect.Float64 || f1.Kind() == reflect.Float32 && i+1 == outType.NumField() { | |
| 49 | + | r2 = syscall.f1 | |
| 50 | + | } else if hasFirstFloat { | |
| 51 | + | // if the first field was a float then that means the second integer field | |
| 52 | + | // comes from the first integer register | |
| 53 | + | r2 = syscall.a1 | |
| 54 | + | } | |
| 55 | + | } | |
| 56 | + | return reflect.NewAt(outType, unsafe.Pointer(&struct{ a, b uintptr }{r1, r2})).Elem() | |
| 57 | + | default: | |
| 58 | + | // create struct from the Go pointer created above | |
| 59 | + | // weird pointer dereference to circumvent go vet | |
| 60 | + | return reflect.NewAt(outType, *(*unsafe.Pointer)(unsafe.Pointer(&syscall.a1))).Elem() | |
| 61 | + | } | |
| 62 | + | } | |
| 63 | + | ||
| 64 | + | func isAllFloats(ty reflect.Type) bool { | |
| 65 | + | for i := 0; i < ty.NumField(); i++ { | |
| 66 | + | f := ty.Field(i) | |
| 67 | + | switch f.Type.Kind() { | |
| 68 | + | case reflect.Float64, reflect.Float32: | |
| 69 | + | default: | |
| 70 | + | return false | |
| 71 | + | } | |
| 72 | + | } | |
| 73 | + | return true | |
| 74 | + | } | |
| 75 | + | ||
| 11 | 76 | // https://refspecs.linuxbase.org/elf/x86_64-abi-0.99.pdf | |
| 12 | 77 | // https://gitlab.com/x86-psABIs/x86-64-ABI | |
| 13 | 78 | // Class determines where the 8 byte value goes. | |
| @@ -6,8 +6,73 @@ package purego | |||||
| 6 | import ( | 6 | import ( | ||
| 7 | "math" | 7 | "math" | ||
| 8 | "reflect" | 8 | "reflect" | ||
| 9 | + | "unsafe" | |||
| 9 | ) | 10 | ) | ||
| 10 | 11 | ||||
| 12 | + | func getStruct(outType reflect.Type, syscall syscall15Args) (v reflect.Value) { | |||
| 13 | + | outSize := outType.Size() | |||
| 14 | + | switch { | |||
| 15 | + | case outSize == 0: | |||
| 16 | + | return reflect.New(outType).Elem() | |||
| 17 | + | case outSize <= 8: | |||
| 18 | + | if isAllFloats(outType) { | |||
| 19 | + | // 2 float32s or 1 float64s are return in the float register | |||
| 20 | + | return reflect.NewAt(outType, unsafe.Pointer(&struct{ a uintptr }{syscall.f1})).Elem() | |||
| 21 | + | } | |||
| 22 | + | // up to 8 bytes is returned in RAX | |||
| 23 | + | return reflect.NewAt(outType, unsafe.Pointer(&struct{ a uintptr }{syscall.a1})).Elem() | |||
| 24 | + | case outSize <= 16: | |||
| 25 | + | r1, r2 := syscall.a1, syscall.a2 | |||
| 26 | + | if isAllFloats(outType) { | |||
| 27 | + | r1 = syscall.f1 | |||
| 28 | + | r2 = syscall.f2 | |||
| 29 | + | } else { | |||
| 30 | + | // check first 8 bytes if it's floats | |||
| 31 | + | hasFirstFloat := false | |||
| 32 | + | f1 := outType.Field(0).Type | |||
| 33 | + | if f1.Kind() == reflect.Float64 || f1.Kind() == reflect.Float32 && outType.Field(1).Type.Kind() == reflect.Float32 { | |||
| 34 | + | r1 = syscall.f1 | |||
| 35 | + | hasFirstFloat = true | |||
| 36 | + | } | |||
| 37 | + | ||||
| 38 | + | // find index of the field that starts the second 8 bytes | |||
| 39 | + | var i int | |||
| 40 | + | for i = 0; i < outType.NumField(); i++ { | |||
| 41 | + | if outType.Field(i).Offset == 8 { | |||
| 42 | + | break | |||
| 43 | + | } | |||
| 44 | + | } | |||
| 45 | + | ||||
| 46 | + | // check last 8 bytes if they are floats | |||
| 47 | + | f1 = outType.Field(i).Type | |||
| 48 | + | if f1.Kind() == reflect.Float64 || f1.Kind() == reflect.Float32 && i+1 == outType.NumField() { | |||
| 49 | + | r2 = syscall.f1 | |||
| 50 | + | } else if hasFirstFloat { | |||
| 51 | + | // if the first field was a float then that means the second integer field | |||
| 52 | + | // comes from the first integer register | |||
| 53 | + | r2 = syscall.a1 | |||
| 54 | + | } | |||
| 55 | + | } | |||
| 56 | + | return reflect.NewAt(outType, unsafe.Pointer(&struct{ a, b uintptr }{r1, r2})).Elem() | |||
| 57 | + | default: | |||
| 58 | + | // create struct from the Go pointer created above | |||
| 59 | + | // weird pointer dereference to circumvent go vet | |||
| 60 | + | return reflect.NewAt(outType, *(*unsafe.Pointer)(unsafe.Pointer(&syscall.a1))).Elem() | |||
| 61 | + | } | |||
| 62 | + | } | |||
| 63 | + | ||||
| 64 | + | func isAllFloats(ty reflect.Type) bool { | |||
| 65 | + | for i := 0; i < ty.NumField(); i++ { | |||
| 66 | + | f := ty.Field(i) | |||
| 67 | + | switch f.Type.Kind() { | |||
| 68 | + | case reflect.Float64, reflect.Float32: | |||
| 69 | + | default: | |||
| 70 | + | return false | |||
| 71 | + | } | |||
| 72 | + | } | |||
| 73 | + | return true | |||
| 74 | + | } | |||
| 75 | + | ||||
| 11 | // https://refspecs.linuxbase.org/elf/x86_64-abi-0.99.pdf | 76 | // https://refspecs.linuxbase.org/elf/x86_64-abi-0.99.pdf | ||
| 12 | // https://gitlab.com/x86-psABIs/x86-64-ABI | 77 | // https://gitlab.com/x86-psABIs/x86-64-ABI | ||
| 13 | // Class determines where the 8 byte value goes. | 78 | // Class determines where the 8 byte value goes. | ||
M struct_arm64.go +50 -0
| @@ -6,8 +6,58 @@ package purego | |||
| 6 | 6 | import ( | |
| 7 | 7 | "math" | |
| 8 | 8 | "reflect" | |
| 9 | + | "unsafe" | |
| 9 | 10 | ) | |
| 10 | 11 | ||
| 12 | + | func getStruct(outType reflect.Type, syscall syscall15Args) (v reflect.Value) { | |
| 13 | + | outSize := outType.Size() | |
| 14 | + | switch { | |
| 15 | + | case outSize == 0: | |
| 16 | + | return reflect.New(outType).Elem() | |
| 17 | + | case outSize <= 8: | |
| 18 | + | r1 := syscall.a1 | |
| 19 | + | if isAllSameFloat(outType) { | |
| 20 | + | r1 = syscall.f1 | |
| 21 | + | if outType.NumField() == 2 { | |
| 22 | + | r1 = syscall.f2<<32 | syscall.f1 | |
| 23 | + | } | |
| 24 | + | } | |
| 25 | + | return reflect.NewAt(outType, unsafe.Pointer(&struct{ a uintptr }{r1})).Elem() | |
| 26 | + | case outSize <= 16: | |
| 27 | + | r1, r2 := syscall.a1, syscall.a2 | |
| 28 | + | if isAllSameFloat(outType) { | |
| 29 | + | switch outType.NumField() { | |
| 30 | + | case 4: | |
| 31 | + | r1 = syscall.f2<<32 | syscall.f1 | |
| 32 | + | r2 = syscall.f4<<32 | syscall.f3 | |
| 33 | + | case 3: | |
| 34 | + | r1 = syscall.f2<<32 | syscall.f1 | |
| 35 | + | r2 = syscall.f3 | |
| 36 | + | case 2: | |
| 37 | + | r1 = syscall.f1 | |
| 38 | + | r2 = syscall.f2 | |
| 39 | + | default: | |
| 40 | + | panic("unreachable") | |
| 41 | + | } | |
| 42 | + | } | |
| 43 | + | return reflect.NewAt(outType, unsafe.Pointer(&struct{ a, b uintptr }{r1, r2})).Elem() | |
| 44 | + | default: | |
| 45 | + | if isAllSameFloat(outType) && outType.NumField() <= 4 { | |
| 46 | + | switch outType.NumField() { | |
| 47 | + | case 4: | |
| 48 | + | return reflect.NewAt(outType, unsafe.Pointer(&struct{ a, b, c, d uintptr }{syscall.f1, syscall.f2, syscall.f3, syscall.f4})).Elem() | |
| 49 | + | case 3: | |
| 50 | + | return reflect.NewAt(outType, unsafe.Pointer(&struct{ a, b, c uintptr }{syscall.f1, syscall.f2, syscall.f3})).Elem() | |
| 51 | + | default: | |
| 52 | + | panic("unreachable") | |
| 53 | + | } | |
| 54 | + | } | |
| 55 | + | // create struct from the Go pointer created in arm64_r8 | |
| 56 | + | // weird pointer dereference to circumvent go vet | |
| 57 | + | return reflect.NewAt(outType, *(*unsafe.Pointer)(unsafe.Pointer(&syscall.arm64_r8))).Elem() | |
| 58 | + | } | |
| 59 | + | } | |
| 60 | + | ||
| 11 | 61 | // https://github.com/ARM-software/abi-aa/blob/main/sysvabi64/sysvabi64.rst | |
| 12 | 62 | const ( | |
| 13 | 63 | _NO_CLASS = 0b00 | |
| @@ -6,8 +6,58 @@ package purego | |||||
| 6 | import ( | 6 | import ( | ||
| 7 | "math" | 7 | "math" | ||
| 8 | "reflect" | 8 | "reflect" | ||
| 9 | + | "unsafe" | |||
| 9 | ) | 10 | ) | ||
| 10 | 11 | ||||
| 12 | + | func getStruct(outType reflect.Type, syscall syscall15Args) (v reflect.Value) { | |||
| 13 | + | outSize := outType.Size() | |||
| 14 | + | switch { | |||
| 15 | + | case outSize == 0: | |||
| 16 | + | return reflect.New(outType).Elem() | |||
| 17 | + | case outSize <= 8: | |||
| 18 | + | r1 := syscall.a1 | |||
| 19 | + | if isAllSameFloat(outType) { | |||
| 20 | + | r1 = syscall.f1 | |||
| 21 | + | if outType.NumField() == 2 { | |||
| 22 | + | r1 = syscall.f2<<32 | syscall.f1 | |||
| 23 | + | } | |||
| 24 | + | } | |||
| 25 | + | return reflect.NewAt(outType, unsafe.Pointer(&struct{ a uintptr }{r1})).Elem() | |||
| 26 | + | case outSize <= 16: | |||
| 27 | + | r1, r2 := syscall.a1, syscall.a2 | |||
| 28 | + | if isAllSameFloat(outType) { | |||
| 29 | + | switch outType.NumField() { | |||
| 30 | + | case 4: | |||
| 31 | + | r1 = syscall.f2<<32 | syscall.f1 | |||
| 32 | + | r2 = syscall.f4<<32 | syscall.f3 | |||
| 33 | + | case 3: | |||
| 34 | + | r1 = syscall.f2<<32 | syscall.f1 | |||
| 35 | + | r2 = syscall.f3 | |||
| 36 | + | case 2: | |||
| 37 | + | r1 = syscall.f1 | |||
| 38 | + | r2 = syscall.f2 | |||
| 39 | + | default: | |||
| 40 | + | panic("unreachable") | |||
| 41 | + | } | |||
| 42 | + | } | |||
| 43 | + | return reflect.NewAt(outType, unsafe.Pointer(&struct{ a, b uintptr }{r1, r2})).Elem() | |||
| 44 | + | default: | |||
| 45 | + | if isAllSameFloat(outType) && outType.NumField() <= 4 { | |||
| 46 | + | switch outType.NumField() { | |||
| 47 | + | case 4: | |||
| 48 | + | return reflect.NewAt(outType, unsafe.Pointer(&struct{ a, b, c, d uintptr }{syscall.f1, syscall.f2, syscall.f3, syscall.f4})).Elem() | |||
| 49 | + | case 3: | |||
| 50 | + | return reflect.NewAt(outType, unsafe.Pointer(&struct{ a, b, c uintptr }{syscall.f1, syscall.f2, syscall.f3})).Elem() | |||
| 51 | + | default: | |||
| 52 | + | panic("unreachable") | |||
| 53 | + | } | |||
| 54 | + | } | |||
| 55 | + | // create struct from the Go pointer created in arm64_r8 | |||
| 56 | + | // weird pointer dereference to circumvent go vet | |||
| 57 | + | return reflect.NewAt(outType, *(*unsafe.Pointer)(unsafe.Pointer(&syscall.arm64_r8))).Elem() | |||
| 58 | + | } | |||
| 59 | + | } | |||
| 60 | + | ||||
| 11 | // https://github.com/ARM-software/abi-aa/blob/main/sysvabi64/sysvabi64.rst | 61 | // https://github.com/ARM-software/abi-aa/blob/main/sysvabi64/sysvabi64.rst | ||
| 12 | const ( | 62 | const ( | ||
| 13 | _NO_CLASS = 0b00 | 63 | _NO_CLASS = 0b00 | ||
M struct_other.go +4 -0
| @@ -10,3 +10,7 @@ import "reflect" | |||
| 10 | 10 | func addStruct(v reflect.Value, numInts, numFloats, numStack *int, addInt, addFloat, addStack func(uintptr), keepAlive []interface{}) []interface{} { | |
| 11 | 11 | panic("purego: struct arguments are not supported") | |
| 12 | 12 | } | |
| 13 | + | ||
| 14 | + | func getStruct(outType reflect.Type, syscall syscall15Args) (v reflect.Value) { | |
| 15 | + | panic("purego: struct returns are not supported") | |
| 16 | + | } | |
| @@ -10,3 +10,7 @@ import "reflect" | |||||
| 10 | func addStruct(v reflect.Value, numInts, numFloats, numStack *int, addInt, addFloat, addStack func(uintptr), keepAlive []interface{}) []interface{} { | 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") | 11 | panic("purego: struct arguments are not supported") | ||
| 12 | } | 12 | } | ||
| 13 | + | ||||
| 14 | + | func getStruct(outType reflect.Type, syscall syscall15Args) (v reflect.Value) { | |||
| 15 | + | panic("purego: struct returns are not supported") | |||
| 16 | + | } | |||
M struct_test.go +263 -0
| @@ -8,7 +8,9 @@ package purego_test | |||
| 8 | 8 | import ( | |
| 9 | 9 | "os" | |
| 10 | 10 | "path/filepath" | |
| 11 | + | "runtime" | |
| 11 | 12 | "testing" | |
| 13 | + | "unsafe" | |
| 12 | 14 | ||
| 13 | 15 | "github.com/ebitengine/purego" | |
| 14 | 16 | ) | |
| @@ -439,3 +441,264 @@ func TestRegisterFunc_structArgs(t *testing.T) { | |||
| 439 | 441 | } | |
| 440 | 442 | } | |
| 441 | 443 | } | |
| 444 | + | ||
| 445 | + | func TestRegisterFunc_structReturns(t *testing.T) { | |
| 446 | + | libFileName := filepath.Join(t.TempDir(), "structreturntest.so") | |
| 447 | + | t.Logf("Build %v", libFileName) | |
| 448 | + | ||
| 449 | + | if err := buildSharedLib("CC", libFileName, filepath.Join("testdata", "structtest", "structreturn_test.c")); err != nil { | |
| 450 | + | t.Fatal(err) | |
| 451 | + | } | |
| 452 | + | defer os.Remove(libFileName) | |
| 453 | + | ||
| 454 | + | lib, err := purego.Dlopen(libFileName, purego.RTLD_NOW|purego.RTLD_GLOBAL) | |
| 455 | + | if err != nil { | |
| 456 | + | t.Fatalf("Dlopen(%q) failed: %v", libFileName, err) | |
| 457 | + | } | |
| 458 | + | ||
| 459 | + | { | |
| 460 | + | type Empty struct{} | |
| 461 | + | var ReturnEmpty func() Empty | |
| 462 | + | purego.RegisterLibFunc(&ReturnEmpty, lib, "ReturnEmpty") | |
| 463 | + | ret := ReturnEmpty() | |
| 464 | + | _ = ret | |
| 465 | + | } | |
| 466 | + | { | |
| 467 | + | type inner struct{ a int16 } | |
| 468 | + | type StructInStruct struct { | |
| 469 | + | a inner | |
| 470 | + | b inner | |
| 471 | + | c inner | |
| 472 | + | } | |
| 473 | + | var ReturnStructInStruct func(a, b, c int16) StructInStruct | |
| 474 | + | purego.RegisterLibFunc(&ReturnStructInStruct, lib, "ReturnStructInStruct") | |
| 475 | + | expected := StructInStruct{inner{^int16(0)}, inner{2}, inner{3}} | |
| 476 | + | if ret := ReturnStructInStruct(^int16(0), 2, 3); ret != expected { | |
| 477 | + | t.Fatalf("StructInStruct returned %+v wanted %+v", ret, expected) | |
| 478 | + | } | |
| 479 | + | } | |
| 480 | + | { | |
| 481 | + | type ThreeShorts struct{ a, b, c int16 } | |
| 482 | + | var ReturnThreeShorts func(a, b, c int16) ThreeShorts | |
| 483 | + | purego.RegisterLibFunc(&ReturnThreeShorts, lib, "ReturnThreeShorts") | |
| 484 | + | expected := ThreeShorts{^int16(0), 2, 3} | |
| 485 | + | if ret := ReturnThreeShorts(^int16(0), 2, 3); ret != expected { | |
| 486 | + | t.Fatalf("ReturnThreeShorts returned %+v wanted %+v", ret, expected) | |
| 487 | + | } | |
| 488 | + | } | |
| 489 | + | { | |
| 490 | + | type FourShorts struct{ a, b, c, d int16 } | |
| 491 | + | var ReturnFourShorts func(a, b, c, d int16) FourShorts | |
| 492 | + | purego.RegisterLibFunc(&ReturnFourShorts, lib, "ReturnFourShorts") | |
| 493 | + | expected := FourShorts{^int16(0), 2, 3, 4} | |
| 494 | + | if ret := ReturnFourShorts(^int16(0), 2, 3, 4); ret != expected { | |
| 495 | + | t.Fatalf("ReturnFourShorts returned %+v wanted %+v", ret, expected) | |
| 496 | + | } | |
| 497 | + | } | |
| 498 | + | { | |
| 499 | + | type OneLong struct{ a int64 } | |
| 500 | + | var ReturnOneLong func(a int64) OneLong | |
| 501 | + | purego.RegisterLibFunc(&ReturnOneLong, lib, "ReturnOneLong") | |
| 502 | + | expected := OneLong{5} | |
| 503 | + | if ret := ReturnOneLong(5); ret != expected { | |
| 504 | + | t.Fatalf("ReturnOneLong returned %+v wanted %+v", ret, expected) | |
| 505 | + | } | |
| 506 | + | } | |
| 507 | + | { | |
| 508 | + | type TwoLongs struct{ a, b int64 } | |
| 509 | + | var ReturnTwoLongs func(a, b int64) TwoLongs | |
| 510 | + | purego.RegisterLibFunc(&ReturnTwoLongs, lib, "ReturnTwoLongs") | |
| 511 | + | expected := TwoLongs{1, 2} | |
| 512 | + | if ret := ReturnTwoLongs(1, 2); ret != expected { | |
| 513 | + | t.Fatalf("ReturnTwoLongs returned %+v wanted %+v", ret, expected) | |
| 514 | + | } | |
| 515 | + | } | |
| 516 | + | { | |
| 517 | + | type ThreeLongs struct{ a, b, c int64 } | |
| 518 | + | var ReturnThreeLongs func(a, b, c int64) ThreeLongs | |
| 519 | + | purego.RegisterLibFunc(&ReturnThreeLongs, lib, "ReturnThreeLongs") | |
| 520 | + | expected := ThreeLongs{1, 2, 3} | |
| 521 | + | if ret := ReturnThreeLongs(1, 2, 3); ret != expected { | |
| 522 | + | t.Fatalf("ReturnThreeLongs returned %+v wanted %+v", ret, expected) | |
| 523 | + | } | |
| 524 | + | } | |
| 525 | + | { | |
| 526 | + | type OneFloat struct{ a float32 } | |
| 527 | + | var ReturnOneFloat func(a float32) OneFloat | |
| 528 | + | purego.RegisterLibFunc(&ReturnOneFloat, lib, "ReturnOneFloat") | |
| 529 | + | expected := OneFloat{1} | |
| 530 | + | if ret := ReturnOneFloat(1); ret != expected { | |
| 531 | + | t.Fatalf("ReturnOneFloat returned %+v wanted %+v", ret, expected) | |
| 532 | + | } | |
| 533 | + | } | |
| 534 | + | { | |
| 535 | + | type TwoFloats struct{ a, b float32 } | |
| 536 | + | var ReturnTwoFloats func(a, b float32) TwoFloats | |
| 537 | + | purego.RegisterLibFunc(&ReturnTwoFloats, lib, "ReturnTwoFloats") | |
| 538 | + | expected := TwoFloats{3, 10} | |
| 539 | + | if ret := ReturnTwoFloats(5, 2); ret != expected { | |
| 540 | + | t.Fatalf("ReturnTwoFloats returned %+v wanted %+v", ret, expected) | |
| 541 | + | } | |
| 542 | + | } | |
| 543 | + | { | |
| 544 | + | type ThreeFloats struct{ a, b, c float32 } | |
| 545 | + | var ReturnThreeFloats func(a, b, c float32) ThreeFloats | |
| 546 | + | purego.RegisterLibFunc(&ReturnThreeFloats, lib, "ReturnThreeFloats") | |
| 547 | + | expected := ThreeFloats{1, 2, 3} | |
| 548 | + | if ret := ReturnThreeFloats(1, 2, 3); ret != expected { | |
| 549 | + | t.Fatalf("ReturnThreeFloats returned %+v wanted %+v", ret, expected) | |
| 550 | + | } | |
| 551 | + | } | |
| 552 | + | { | |
| 553 | + | type OneDouble struct{ a float64 } | |
| 554 | + | var ReturnOneDouble func(a float64) OneDouble | |
| 555 | + | purego.RegisterLibFunc(&ReturnOneDouble, lib, "ReturnOneDouble") | |
| 556 | + | expected := OneDouble{1} | |
| 557 | + | if ret := ReturnOneDouble(1); ret != expected { | |
| 558 | + | t.Fatalf("ReturnOneDouble returned %+v wanted %+v", ret, expected) | |
| 559 | + | } | |
| 560 | + | } | |
| 561 | + | { | |
| 562 | + | type TwoDoubles struct{ a, b float64 } | |
| 563 | + | var ReturnTwoDoubles func(a, b float64) TwoDoubles | |
| 564 | + | purego.RegisterLibFunc(&ReturnTwoDoubles, lib, "ReturnTwoDoubles") | |
| 565 | + | expected := TwoDoubles{1, 2} | |
| 566 | + | if ret := ReturnTwoDoubles(1, 2); ret != expected { | |
| 567 | + | t.Fatalf("ReturnTwoDoubles returned %+v wanted %+v", ret, expected) | |
| 568 | + | } | |
| 569 | + | } | |
| 570 | + | { | |
| 571 | + | type ThreeDoubles struct{ a, b, c float64 } | |
| 572 | + | var ReturnThreeDoubles func(a, b, c float64) ThreeDoubles | |
| 573 | + | purego.RegisterLibFunc(&ReturnThreeDoubles, lib, "ReturnThreeDoubles") | |
| 574 | + | expected := ThreeDoubles{1, 2, 3} | |
| 575 | + | if ret := ReturnThreeDoubles(1, 2, 3); ret != expected { | |
| 576 | + | t.Fatalf("ReturnThreeDoubles returned %+v wanted %+v", ret, expected) | |
| 577 | + | } | |
| 578 | + | } | |
| 579 | + | { | |
| 580 | + | type FourDoubles struct{ a, b, c, d float64 } | |
| 581 | + | var ReturnFourDoubles func(a, b, c, d float64) FourDoubles | |
| 582 | + | purego.RegisterLibFunc(&ReturnFourDoubles, lib, "ReturnFourDoubles") | |
| 583 | + | expected := FourDoubles{1, 2, 3, 4} | |
| 584 | + | if ret := ReturnFourDoubles(1, 2, 3, 4); ret != expected { | |
| 585 | + | t.Fatalf("ReturnFourDoubles returned %+v wanted %+v", ret, expected) | |
| 586 | + | } | |
| 587 | + | } | |
| 588 | + | { | |
| 589 | + | type FiveDoubles struct{ a, b, c, d, e float64 } | |
| 590 | + | var ReturnFiveDoubles func(a, b, c, d, e float64) FiveDoubles | |
| 591 | + | purego.RegisterLibFunc(&ReturnFiveDoubles, lib, "ReturnFiveDoubles") | |
| 592 | + | expected := FiveDoubles{1, 2, 3, 4, 5} | |
| 593 | + | if ret := ReturnFiveDoubles(1, 2, 3, 4, 5); ret != expected { | |
| 594 | + | t.Fatalf("ReturnFiveDoubles returned %+v wanted %+v", ret, expected) | |
| 595 | + | } | |
| 596 | + | } | |
| 597 | + | { | |
| 598 | + | type OneFloatOneDouble struct { | |
| 599 | + | a float32 | |
| 600 | + | _ float32 | |
| 601 | + | b float64 | |
| 602 | + | } | |
| 603 | + | var ReturnOneFloatOneDouble func(a float32, b float64) OneFloatOneDouble | |
| 604 | + | purego.RegisterLibFunc(&ReturnOneFloatOneDouble, lib, "ReturnOneFloatOneDouble") | |
| 605 | + | expected := OneFloatOneDouble{a: 1, b: 2} | |
| 606 | + | if ret := ReturnOneFloatOneDouble(1, 2); ret != expected { | |
| 607 | + | t.Fatalf("ReturnOneFloatOneDouble returned %+v wanted %+v", ret, expected) | |
| 608 | + | } | |
| 609 | + | } | |
| 610 | + | { | |
| 611 | + | type OneDoubleOneFloat struct { | |
| 612 | + | a float64 | |
| 613 | + | b float32 | |
| 614 | + | } | |
| 615 | + | var ReturnOneDoubleOneFloat func(a float64, b float32) OneDoubleOneFloat | |
| 616 | + | purego.RegisterLibFunc(&ReturnOneDoubleOneFloat, lib, "ReturnOneDoubleOneFloat") | |
| 617 | + | expected := OneDoubleOneFloat{1, 2} | |
| 618 | + | if ret := ReturnOneDoubleOneFloat(1, 2); ret != expected { | |
| 619 | + | t.Fatalf("ReturnOneDoubleOneFloat returned %+v wanted %+v", ret, expected) | |
| 620 | + | } | |
| 621 | + | } | |
| 622 | + | { | |
| 623 | + | type Unaligned1 struct { | |
| 624 | + | a int8 | |
| 625 | + | _ [1]int8 | |
| 626 | + | b int16 | |
| 627 | + | _ [1]int32 | |
| 628 | + | c int64 | |
| 629 | + | } | |
| 630 | + | var ReturnUnaligned1 func(a int8, b int16, c int64) Unaligned1 | |
| 631 | + | purego.RegisterLibFunc(&ReturnUnaligned1, lib, "ReturnUnaligned1") | |
| 632 | + | expected := Unaligned1{a: 1, b: 2, c: 3} | |
| 633 | + | if ret := ReturnUnaligned1(1, 2, 3); ret != expected { | |
| 634 | + | t.Fatalf("ReturnUnaligned1 returned %+v wanted %+v", ret, expected) | |
| 635 | + | } | |
| 636 | + | } | |
| 637 | + | { | |
| 638 | + | type Mixed1 struct { | |
| 639 | + | a float32 | |
| 640 | + | b int32 | |
| 641 | + | } | |
| 642 | + | var ReturnMixed1 func(a float32, b int32) Mixed1 | |
| 643 | + | purego.RegisterLibFunc(&ReturnMixed1, lib, "ReturnMixed1") | |
| 644 | + | expected := Mixed1{1, 2} | |
| 645 | + | if ret := ReturnMixed1(1, 2); ret != expected { | |
| 646 | + | t.Fatalf("ReturnMixed1 returned %+v wanted %+v", ret, expected) | |
| 647 | + | } | |
| 648 | + | } | |
| 649 | + | { | |
| 650 | + | type Mixed2 struct { | |
| 651 | + | a float32 | |
| 652 | + | b int32 | |
| 653 | + | c float32 | |
| 654 | + | d int32 | |
| 655 | + | } | |
| 656 | + | var ReturnMixed2 func(a float32, b int32, c float32, d int32) Mixed2 | |
| 657 | + | purego.RegisterLibFunc(&ReturnMixed2, lib, "ReturnMixed2") | |
| 658 | + | expected := Mixed2{1, 2, 3, 4} | |
| 659 | + | if ret := ReturnMixed2(1, 2, 3, 4); ret != expected { | |
| 660 | + | t.Fatalf("ReturnMixed2 returned %+v wanted %+v", ret, expected) | |
| 661 | + | } | |
| 662 | + | } | |
| 663 | + | { | |
| 664 | + | type Mixed3 struct { | |
| 665 | + | a float32 | |
| 666 | + | b uint32 | |
| 667 | + | c float64 | |
| 668 | + | } | |
| 669 | + | var ReturnMixed3 func(a float32, b uint32, c float64) Mixed3 | |
| 670 | + | purego.RegisterLibFunc(&ReturnMixed3, lib, "ReturnMixed3") | |
| 671 | + | expected := Mixed3{1, 2, 3} | |
| 672 | + | if ret := ReturnMixed3(1, 2, 3); ret != expected { | |
| 673 | + | t.Fatalf("ReturnMixed3 returned %+v wanted %+v", ret, expected) | |
| 674 | + | } | |
| 675 | + | } | |
| 676 | + | { | |
| 677 | + | type Mixed4 struct { | |
| 678 | + | a float64 | |
| 679 | + | b uint32 | |
| 680 | + | c float32 | |
| 681 | + | } | |
| 682 | + | var ReturnMixed4 func(a float64, b uint32, c float32) Mixed4 | |
| 683 | + | purego.RegisterLibFunc(&ReturnMixed4, lib, "ReturnMixed4") | |
| 684 | + | expected := Mixed4{1, 2, 3} | |
| 685 | + | if ret := ReturnMixed4(1, 2, 3); ret != expected { | |
| 686 | + | t.Fatalf("ReturnMixed4 returned %+v wanted %+v", ret, expected) | |
| 687 | + | } | |
| 688 | + | } | |
| 689 | + | { | |
| 690 | + | type Ptr1 struct { | |
| 691 | + | a *int64 | |
| 692 | + | b unsafe.Pointer | |
| 693 | + | } | |
| 694 | + | var ReturnPtr1 func(a *int64, b unsafe.Pointer) Ptr1 | |
| 695 | + | purego.RegisterLibFunc(&ReturnPtr1, lib, "ReturnPtr1") | |
| 696 | + | a, b := new(int64), new(struct{}) | |
| 697 | + | expected := Ptr1{a, unsafe.Pointer(b)} | |
| 698 | + | if ret := ReturnPtr1(a, unsafe.Pointer(b)); ret != expected { | |
| 699 | + | t.Fatalf("ReturnPtr1 returned %+v wanted %+v", ret, expected) | |
| 700 | + | } | |
| 701 | + | runtime.KeepAlive(a) | |
| 702 | + | runtime.KeepAlive(b) | |
| 703 | + | } | |
| 704 | + | } | |
| @@ -8,7 +8,9 @@ package purego_test | |||||
| 8 | import ( | 8 | import ( | ||
| 9 | "os" | 9 | "os" | ||
| 10 | "path/filepath" | 10 | "path/filepath" | ||
| 11 | + | "runtime" | |||
| 11 | "testing" | 12 | "testing" | ||
| 13 | + | "unsafe" | |||
| 12 | 14 | ||||
| 13 | "github.com/ebitengine/purego" | 15 | "github.com/ebitengine/purego" | ||
| 14 | ) | 16 | ) | ||
| @@ -439,3 +441,264 @@ func TestRegisterFunc_structArgs(t *testing.T) { | |||||
| 439 | } | 441 | } | ||
| 440 | } | 442 | } | ||
| 441 | } | 443 | } | ||
| 444 | + | ||||
| 445 | + | func TestRegisterFunc_structReturns(t *testing.T) { | |||
| 446 | + | libFileName := filepath.Join(t.TempDir(), "structreturntest.so") | |||
| 447 | + | t.Logf("Build %v", libFileName) | |||
| 448 | + | ||||
| 449 | + | if err := buildSharedLib("CC", libFileName, filepath.Join("testdata", "structtest", "structreturn_test.c")); err != nil { | |||
| 450 | + | t.Fatal(err) | |||
| 451 | + | } | |||
| 452 | + | defer os.Remove(libFileName) | |||
| 453 | + | ||||
| 454 | + | lib, err := purego.Dlopen(libFileName, purego.RTLD_NOW|purego.RTLD_GLOBAL) | |||
| 455 | + | if err != nil { | |||
| 456 | + | t.Fatalf("Dlopen(%q) failed: %v", libFileName, err) | |||
| 457 | + | } | |||
| 458 | + | ||||
| 459 | + | { | |||
| 460 | + | type Empty struct{} | |||
| 461 | + | var ReturnEmpty func() Empty | |||
| 462 | + | purego.RegisterLibFunc(&ReturnEmpty, lib, "ReturnEmpty") | |||
| 463 | + | ret := ReturnEmpty() | |||
| 464 | + | _ = ret | |||
| 465 | + | } | |||
| 466 | + | { | |||
| 467 | + | type inner struct{ a int16 } | |||
| 468 | + | type StructInStruct struct { | |||
| 469 | + | a inner | |||
| 470 | + | b inner | |||
| 471 | + | c inner | |||
| 472 | + | } | |||
| 473 | + | var ReturnStructInStruct func(a, b, c int16) StructInStruct | |||
| 474 | + | purego.RegisterLibFunc(&ReturnStructInStruct, lib, "ReturnStructInStruct") | |||
| 475 | + | expected := StructInStruct{inner{^int16(0)}, inner{2}, inner{3}} | |||
| 476 | + | if ret := ReturnStructInStruct(^int16(0), 2, 3); ret != expected { | |||
| 477 | + | t.Fatalf("StructInStruct returned %+v wanted %+v", ret, expected) | |||
| 478 | + | } | |||
| 479 | + | } | |||
| 480 | + | { | |||
| 481 | + | type ThreeShorts struct{ a, b, c int16 } | |||
| 482 | + | var ReturnThreeShorts func(a, b, c int16) ThreeShorts | |||
| 483 | + | purego.RegisterLibFunc(&ReturnThreeShorts, lib, "ReturnThreeShorts") | |||
| 484 | + | expected := ThreeShorts{^int16(0), 2, 3} | |||
| 485 | + | if ret := ReturnThreeShorts(^int16(0), 2, 3); ret != expected { | |||
| 486 | + | t.Fatalf("ReturnThreeShorts returned %+v wanted %+v", ret, expected) | |||
| 487 | + | } | |||
| 488 | + | } | |||
| 489 | + | { | |||
| 490 | + | type FourShorts struct{ a, b, c, d int16 } | |||
| 491 | + | var ReturnFourShorts func(a, b, c, d int16) FourShorts | |||
| 492 | + | purego.RegisterLibFunc(&ReturnFourShorts, lib, "ReturnFourShorts") | |||
| 493 | + | expected := FourShorts{^int16(0), 2, 3, 4} | |||
| 494 | + | if ret := ReturnFourShorts(^int16(0), 2, 3, 4); ret != expected { | |||
| 495 | + | t.Fatalf("ReturnFourShorts returned %+v wanted %+v", ret, expected) | |||
| 496 | + | } | |||
| 497 | + | } | |||
| 498 | + | { | |||
| 499 | + | type OneLong struct{ a int64 } | |||
| 500 | + | var ReturnOneLong func(a int64) OneLong | |||
| 501 | + | purego.RegisterLibFunc(&ReturnOneLong, lib, "ReturnOneLong") | |||
| 502 | + | expected := OneLong{5} | |||
| 503 | + | if ret := ReturnOneLong(5); ret != expected { | |||
| 504 | + | t.Fatalf("ReturnOneLong returned %+v wanted %+v", ret, expected) | |||
| 505 | + | } | |||
| 506 | + | } | |||
| 507 | + | { | |||
| 508 | + | type TwoLongs struct{ a, b int64 } | |||
| 509 | + | var ReturnTwoLongs func(a, b int64) TwoLongs | |||
| 510 | + | purego.RegisterLibFunc(&ReturnTwoLongs, lib, "ReturnTwoLongs") | |||
| 511 | + | expected := TwoLongs{1, 2} | |||
| 512 | + | if ret := ReturnTwoLongs(1, 2); ret != expected { | |||
| 513 | + | t.Fatalf("ReturnTwoLongs returned %+v wanted %+v", ret, expected) | |||
| 514 | + | } | |||
| 515 | + | } | |||
| 516 | + | { | |||
| 517 | + | type ThreeLongs struct{ a, b, c int64 } | |||
| 518 | + | var ReturnThreeLongs func(a, b, c int64) ThreeLongs | |||
| 519 | + | purego.RegisterLibFunc(&ReturnThreeLongs, lib, "ReturnThreeLongs") | |||
| 520 | + | expected := ThreeLongs{1, 2, 3} | |||
| 521 | + | if ret := ReturnThreeLongs(1, 2, 3); ret != expected { | |||
| 522 | + | t.Fatalf("ReturnThreeLongs returned %+v wanted %+v", ret, expected) | |||
| 523 | + | } | |||
| 524 | + | } | |||
| 525 | + | { | |||
| 526 | + | type OneFloat struct{ a float32 } | |||
| 527 | + | var ReturnOneFloat func(a float32) OneFloat | |||
| 528 | + | purego.RegisterLibFunc(&ReturnOneFloat, lib, "ReturnOneFloat") | |||
| 529 | + | expected := OneFloat{1} | |||
| 530 | + | if ret := ReturnOneFloat(1); ret != expected { | |||
| 531 | + | t.Fatalf("ReturnOneFloat returned %+v wanted %+v", ret, expected) | |||
| 532 | + | } | |||
| 533 | + | } | |||
| 534 | + | { | |||
| 535 | + | type TwoFloats struct{ a, b float32 } | |||
| 536 | + | var ReturnTwoFloats func(a, b float32) TwoFloats | |||
| 537 | + | purego.RegisterLibFunc(&ReturnTwoFloats, lib, "ReturnTwoFloats") | |||
| 538 | + | expected := TwoFloats{3, 10} | |||
| 539 | + | if ret := ReturnTwoFloats(5, 2); ret != expected { | |||
| 540 | + | t.Fatalf("ReturnTwoFloats returned %+v wanted %+v", ret, expected) | |||
| 541 | + | } | |||
| 542 | + | } | |||
| 543 | + | { | |||
| 544 | + | type ThreeFloats struct{ a, b, c float32 } | |||
| 545 | + | var ReturnThreeFloats func(a, b, c float32) ThreeFloats | |||
| 546 | + | purego.RegisterLibFunc(&ReturnThreeFloats, lib, "ReturnThreeFloats") | |||
| 547 | + | expected := ThreeFloats{1, 2, 3} | |||
| 548 | + | if ret := ReturnThreeFloats(1, 2, 3); ret != expected { | |||
| 549 | + | t.Fatalf("ReturnThreeFloats returned %+v wanted %+v", ret, expected) | |||
| 550 | + | } | |||
| 551 | + | } | |||
| 552 | + | { | |||
| 553 | + | type OneDouble struct{ a float64 } | |||
| 554 | + | var ReturnOneDouble func(a float64) OneDouble | |||
| 555 | + | purego.RegisterLibFunc(&ReturnOneDouble, lib, "ReturnOneDouble") | |||
| 556 | + | expected := OneDouble{1} | |||
| 557 | + | if ret := ReturnOneDouble(1); ret != expected { | |||
| 558 | + | t.Fatalf("ReturnOneDouble returned %+v wanted %+v", ret, expected) | |||
| 559 | + | } | |||
| 560 | + | } | |||
| 561 | + | { | |||
| 562 | + | type TwoDoubles struct{ a, b float64 } | |||
| 563 | + | var ReturnTwoDoubles func(a, b float64) TwoDoubles | |||
| 564 | + | purego.RegisterLibFunc(&ReturnTwoDoubles, lib, "ReturnTwoDoubles") | |||
| 565 | + | expected := TwoDoubles{1, 2} | |||
| 566 | + | if ret := ReturnTwoDoubles(1, 2); ret != expected { | |||
| 567 | + | t.Fatalf("ReturnTwoDoubles returned %+v wanted %+v", ret, expected) | |||
| 568 | + | } | |||
| 569 | + | } | |||
| 570 | + | { | |||
| 571 | + | type ThreeDoubles struct{ a, b, c float64 } | |||
| 572 | + | var ReturnThreeDoubles func(a, b, c float64) ThreeDoubles | |||
| 573 | + | purego.RegisterLibFunc(&ReturnThreeDoubles, lib, "ReturnThreeDoubles") | |||
| 574 | + | expected := ThreeDoubles{1, 2, 3} | |||
| 575 | + | if ret := ReturnThreeDoubles(1, 2, 3); ret != expected { | |||
| 576 | + | t.Fatalf("ReturnThreeDoubles returned %+v wanted %+v", ret, expected) | |||
| 577 | + | } | |||
| 578 | + | } | |||
| 579 | + | { | |||
| 580 | + | type FourDoubles struct{ a, b, c, d float64 } | |||
| 581 | + | var ReturnFourDoubles func(a, b, c, d float64) FourDoubles | |||
| 582 | + | purego.RegisterLibFunc(&ReturnFourDoubles, lib, "ReturnFourDoubles") | |||
| 583 | + | expected := FourDoubles{1, 2, 3, 4} | |||
| 584 | + | if ret := ReturnFourDoubles(1, 2, 3, 4); ret != expected { | |||
| 585 | + | t.Fatalf("ReturnFourDoubles returned %+v wanted %+v", ret, expected) | |||
| 586 | + | } | |||
| 587 | + | } | |||
| 588 | + | { | |||
| 589 | + | type FiveDoubles struct{ a, b, c, d, e float64 } | |||
| 590 | + | var ReturnFiveDoubles func(a, b, c, d, e float64) FiveDoubles | |||
| 591 | + | purego.RegisterLibFunc(&ReturnFiveDoubles, lib, "ReturnFiveDoubles") | |||
| 592 | + | expected := FiveDoubles{1, 2, 3, 4, 5} | |||
| 593 | + | if ret := ReturnFiveDoubles(1, 2, 3, 4, 5); ret != expected { | |||
| 594 | + | t.Fatalf("ReturnFiveDoubles returned %+v wanted %+v", ret, expected) | |||
| 595 | + | } | |||
| 596 | + | } | |||
| 597 | + | { | |||
| 598 | + | type OneFloatOneDouble struct { | |||
| 599 | + | a float32 | |||
| 600 | + | _ float32 | |||
| 601 | + | b float64 | |||
| 602 | + | } | |||
| 603 | + | var ReturnOneFloatOneDouble func(a float32, b float64) OneFloatOneDouble | |||
| 604 | + | purego.RegisterLibFunc(&ReturnOneFloatOneDouble, lib, "ReturnOneFloatOneDouble") | |||
| 605 | + | expected := OneFloatOneDouble{a: 1, b: 2} | |||
| 606 | + | if ret := ReturnOneFloatOneDouble(1, 2); ret != expected { | |||
| 607 | + | t.Fatalf("ReturnOneFloatOneDouble returned %+v wanted %+v", ret, expected) | |||
| 608 | + | } | |||
| 609 | + | } | |||
| 610 | + | { | |||
| 611 | + | type OneDoubleOneFloat struct { | |||
| 612 | + | a float64 | |||
| 613 | + | b float32 | |||
| 614 | + | } | |||
| 615 | + | var ReturnOneDoubleOneFloat func(a float64, b float32) OneDoubleOneFloat | |||
| 616 | + | purego.RegisterLibFunc(&ReturnOneDoubleOneFloat, lib, "ReturnOneDoubleOneFloat") | |||
| 617 | + | expected := OneDoubleOneFloat{1, 2} | |||
| 618 | + | if ret := ReturnOneDoubleOneFloat(1, 2); ret != expected { | |||
| 619 | + | t.Fatalf("ReturnOneDoubleOneFloat returned %+v wanted %+v", ret, expected) | |||
| 620 | + | } | |||
| 621 | + | } | |||
| 622 | + | { | |||
| 623 | + | type Unaligned1 struct { | |||
| 624 | + | a int8 | |||
| 625 | + | _ [1]int8 | |||
| 626 | + | b int16 | |||
| 627 | + | _ [1]int32 | |||
| 628 | + | c int64 | |||
| 629 | + | } | |||
| 630 | + | var ReturnUnaligned1 func(a int8, b int16, c int64) Unaligned1 | |||
| 631 | + | purego.RegisterLibFunc(&ReturnUnaligned1, lib, "ReturnUnaligned1") | |||
| 632 | + | expected := Unaligned1{a: 1, b: 2, c: 3} | |||
| 633 | + | if ret := ReturnUnaligned1(1, 2, 3); ret != expected { | |||
| 634 | + | t.Fatalf("ReturnUnaligned1 returned %+v wanted %+v", ret, expected) | |||
| 635 | + | } | |||
| 636 | + | } | |||
| 637 | + | { | |||
| 638 | + | type Mixed1 struct { | |||
| 639 | + | a float32 | |||
| 640 | + | b int32 | |||
| 641 | + | } | |||
| 642 | + | var ReturnMixed1 func(a float32, b int32) Mixed1 | |||
| 643 | + | purego.RegisterLibFunc(&ReturnMixed1, lib, "ReturnMixed1") | |||
| 644 | + | expected := Mixed1{1, 2} | |||
| 645 | + | if ret := ReturnMixed1(1, 2); ret != expected { | |||
| 646 | + | t.Fatalf("ReturnMixed1 returned %+v wanted %+v", ret, expected) | |||
| 647 | + | } | |||
| 648 | + | } | |||
| 649 | + | { | |||
| 650 | + | type Mixed2 struct { | |||
| 651 | + | a float32 | |||
| 652 | + | b int32 | |||
| 653 | + | c float32 | |||
| 654 | + | d int32 | |||
| 655 | + | } | |||
| 656 | + | var ReturnMixed2 func(a float32, b int32, c float32, d int32) Mixed2 | |||
| 657 | + | purego.RegisterLibFunc(&ReturnMixed2, lib, "ReturnMixed2") | |||
| 658 | + | expected := Mixed2{1, 2, 3, 4} | |||
| 659 | + | if ret := ReturnMixed2(1, 2, 3, 4); ret != expected { | |||
| 660 | + | t.Fatalf("ReturnMixed2 returned %+v wanted %+v", ret, expected) | |||
| 661 | + | } | |||
| 662 | + | } | |||
| 663 | + | { | |||
| 664 | + | type Mixed3 struct { | |||
| 665 | + | a float32 | |||
| 666 | + | b uint32 | |||
| 667 | + | c float64 | |||
| 668 | + | } | |||
| 669 | + | var ReturnMixed3 func(a float32, b uint32, c float64) Mixed3 | |||
| 670 | + | purego.RegisterLibFunc(&ReturnMixed3, lib, "ReturnMixed3") | |||
| 671 | + | expected := Mixed3{1, 2, 3} | |||
| 672 | + | if ret := ReturnMixed3(1, 2, 3); ret != expected { | |||
| 673 | + | t.Fatalf("ReturnMixed3 returned %+v wanted %+v", ret, expected) | |||
| 674 | + | } | |||
| 675 | + | } | |||
| 676 | + | { | |||
| 677 | + | type Mixed4 struct { | |||
| 678 | + | a float64 | |||
| 679 | + | b uint32 | |||
| 680 | + | c float32 | |||
| 681 | + | } | |||
| 682 | + | var ReturnMixed4 func(a float64, b uint32, c float32) Mixed4 | |||
| 683 | + | purego.RegisterLibFunc(&ReturnMixed4, lib, "ReturnMixed4") | |||
| 684 | + | expected := Mixed4{1, 2, 3} | |||
| 685 | + | if ret := ReturnMixed4(1, 2, 3); ret != expected { | |||
| 686 | + | t.Fatalf("ReturnMixed4 returned %+v wanted %+v", ret, expected) | |||
| 687 | + | } | |||
| 688 | + | } | |||
| 689 | + | { | |||
| 690 | + | type Ptr1 struct { | |||
| 691 | + | a *int64 | |||
| 692 | + | b unsafe.Pointer | |||
| 693 | + | } | |||
| 694 | + | var ReturnPtr1 func(a *int64, b unsafe.Pointer) Ptr1 | |||
| 695 | + | purego.RegisterLibFunc(&ReturnPtr1, lib, "ReturnPtr1") | |||
| 696 | + | a, b := new(int64), new(struct{}) | |||
| 697 | + | expected := Ptr1{a, unsafe.Pointer(b)} | |||
| 698 | + | if ret := ReturnPtr1(a, unsafe.Pointer(b)); ret != expected { | |||
| 699 | + | t.Fatalf("ReturnPtr1 returned %+v wanted %+v", ret, expected) | |||
| 700 | + | } | |||
| 701 | + | runtime.KeepAlive(a) | |||
| 702 | + | runtime.KeepAlive(b) | |||
| 703 | + | } | |||
| 704 | + | } | |||
M sys_amd64.s +4 -2
| @@ -86,8 +86,10 @@ TEXT syscall15X(SB), NOSPLIT|NOFRAME, $0 | |||
| 86 | 86 | CALL R10 | |
| 87 | 87 | ||
| 88 | 88 | MOVQ PTR_ADDRESS(BP), DI // get the pointer back | |
| 89 | - | MOVQ AX, syscall15Args_r1(DI) // r1 | |
| 90 | - | MOVQ X0, syscall15Args_r2(DI) // r2 | |
| 89 | + | MOVQ AX, syscall15Args_a1(DI) // r1 | |
| 90 | + | MOVQ DX, syscall15Args_a2(DI) // r3 | |
| 91 | + | MOVQ X0, syscall15Args_f1(DI) // f1 | |
| 92 | + | MOVQ X1, syscall15Args_f2(DI) // f2 | |
| 91 | 93 | ||
| 92 | 94 | XORL AX, AX // no error (it's ignored anyway) | |
| 93 | 95 | ADDQ $STACK_SIZE, SP | |
| @@ -86,8 +86,10 @@ TEXT syscall15X(SB), NOSPLIT|NOFRAME, $0 | |||||
| 86 | CALL R10 | 86 | CALL R10 | ||
| 87 | 87 | ||||
| 88 | MOVQ PTR_ADDRESS(BP), DI // get the pointer back | 88 | MOVQ PTR_ADDRESS(BP), DI // get the pointer back | ||
| 89 | - | MOVQ AX, syscall15Args_r1(DI) // r1 | 89 | + | MOVQ AX, syscall15Args_a1(DI) // r1 |
| 90 | - | MOVQ X0, syscall15Args_r2(DI) // r2 | 90 | + | MOVQ DX, syscall15Args_a2(DI) // r3 |
| 91 | + | MOVQ X0, syscall15Args_f1(DI) // f1 | |||
| 92 | + | MOVQ X1, syscall15Args_f2(DI) // f2 | |||
| 91 | 93 | ||||
| 92 | XORL AX, AX // no error (it's ignored anyway) | 94 | XORL AX, AX // no error (it's ignored anyway) | ||
| 93 | ADDQ $STACK_SIZE, SP | 95 | ADDQ $STACK_SIZE, SP | ||
M sys_arm64.s +19 -12
| @@ -51,14 +51,15 @@ TEXT syscall15X(SB), NOSPLIT, $0 | |||
| 51 | 51 | FMOVD syscall15Args_f7(R9), F6 // f7 | |
| 52 | 52 | FMOVD syscall15Args_f8(R9), F7 // f8 | |
| 53 | 53 | ||
| 54 | - | MOVD syscall15Args_a1(R9), R0 // a1 | |
| 55 | - | MOVD syscall15Args_a2(R9), R1 // a2 | |
| 56 | - | MOVD syscall15Args_a3(R9), R2 // a3 | |
| 57 | - | MOVD syscall15Args_a4(R9), R3 // a4 | |
| 58 | - | MOVD syscall15Args_a5(R9), R4 // a5 | |
| 59 | - | MOVD syscall15Args_a6(R9), R5 // a6 | |
| 60 | - | MOVD syscall15Args_a7(R9), R6 // a7 | |
| 61 | - | MOVD syscall15Args_a8(R9), R7 // a8 | |
| 54 | + | MOVD syscall15Args_a1(R9), R0 // a1 | |
| 55 | + | MOVD syscall15Args_a2(R9), R1 // a2 | |
| 56 | + | MOVD syscall15Args_a3(R9), R2 // a3 | |
| 57 | + | MOVD syscall15Args_a4(R9), R3 // a4 | |
| 58 | + | MOVD syscall15Args_a5(R9), R4 // a5 | |
| 59 | + | MOVD syscall15Args_a6(R9), R5 // a6 | |
| 60 | + | MOVD syscall15Args_a7(R9), R6 // a7 | |
| 61 | + | MOVD syscall15Args_a8(R9), R7 // a8 | |
| 62 | + | MOVD syscall15Args_arm64_r8(R9), R8 // r8 | |
| 62 | 63 | ||
| 63 | 64 | MOVD syscall15Args_a9(R9), R10 | |
| 64 | 65 | MOVD R10, 0(RSP) // push a9 onto stack | |
| @@ -78,8 +79,14 @@ TEXT syscall15X(SB), NOSPLIT, $0 | |||
| 78 | 79 | MOVD syscall15Args_fn(R9), R10 // fn | |
| 79 | 80 | BL (R10) | |
| 80 | 81 | ||
| 81 | - | MOVD PTR_ADDRESS(RSP), R2 // pop structure pointer | |
| 82 | - | ADD $STACK_SIZE, RSP | |
| 83 | - | MOVD R0, syscall15Args_r1(R2) // save r1 | |
| 84 | - | FMOVD F0, syscall15Args_r2(R2) // save r2 | |
| 82 | + | MOVD PTR_ADDRESS(RSP), R2 // pop structure pointer | |
| 83 | + | ADD $STACK_SIZE, RSP | |
| 84 | + | ||
| 85 | + | MOVD R0, syscall15Args_a1(R2) // save r1 | |
| 86 | + | MOVD R1, syscall15Args_a2(R2) // save r3 | |
| 87 | + | FMOVD F0, syscall15Args_f1(R2) // save f0 | |
| 88 | + | FMOVD F1, syscall15Args_f2(R2) // save f1 | |
| 89 | + | FMOVD F2, syscall15Args_f3(R2) // save f2 | |
| 90 | + | FMOVD F3, syscall15Args_f4(R2) // save f3 | |
| 91 | + | ||
| 85 | 92 | RET | |
| @@ -51,14 +51,15 @@ TEXT syscall15X(SB), NOSPLIT, $0 | |||||
| 51 | FMOVD syscall15Args_f7(R9), F6 // f7 | 51 | FMOVD syscall15Args_f7(R9), F6 // f7 | ||
| 52 | FMOVD syscall15Args_f8(R9), F7 // f8 | 52 | FMOVD syscall15Args_f8(R9), F7 // f8 | ||
| 53 | 53 | ||||
| 54 | - | MOVD syscall15Args_a1(R9), R0 // a1 | 54 | + | MOVD syscall15Args_a1(R9), R0 // a1 |
| 55 | - | MOVD syscall15Args_a2(R9), R1 // a2 | 55 | + | MOVD syscall15Args_a2(R9), R1 // a2 |
| 56 | - | MOVD syscall15Args_a3(R9), R2 // a3 | 56 | + | MOVD syscall15Args_a3(R9), R2 // a3 |
| 57 | - | MOVD syscall15Args_a4(R9), R3 // a4 | 57 | + | MOVD syscall15Args_a4(R9), R3 // a4 |
| 58 | - | MOVD syscall15Args_a5(R9), R4 // a5 | 58 | + | MOVD syscall15Args_a5(R9), R4 // a5 |
| 59 | - | MOVD syscall15Args_a6(R9), R5 // a6 | 59 | + | MOVD syscall15Args_a6(R9), R5 // a6 |
| 60 | - | MOVD syscall15Args_a7(R9), R6 // a7 | 60 | + | MOVD syscall15Args_a7(R9), R6 // a7 |
| 61 | - | MOVD syscall15Args_a8(R9), R7 // a8 | 61 | + | MOVD syscall15Args_a8(R9), R7 // a8 |
| 62 | + | MOVD syscall15Args_arm64_r8(R9), R8 // r8 | |||
| 62 | 63 | ||||
| 63 | MOVD syscall15Args_a9(R9), R10 | 64 | MOVD syscall15Args_a9(R9), R10 | ||
| 64 | MOVD R10, 0(RSP) // push a9 onto stack | 65 | MOVD R10, 0(RSP) // push a9 onto stack | ||
| @@ -78,8 +79,14 @@ TEXT syscall15X(SB), NOSPLIT, $0 | |||||
| 78 | MOVD syscall15Args_fn(R9), R10 // fn | 79 | MOVD syscall15Args_fn(R9), R10 // fn | ||
| 79 | BL (R10) | 80 | BL (R10) | ||
| 80 | 81 | ||||
| 81 | - | MOVD PTR_ADDRESS(RSP), R2 // pop structure pointer | 82 | + | MOVD PTR_ADDRESS(RSP), R2 // pop structure pointer |
| 82 | - | ADD $STACK_SIZE, RSP | 83 | + | ADD $STACK_SIZE, RSP |
| 83 | - | MOVD R0, syscall15Args_r1(R2) // save r1 | 84 | + | |
| 84 | - | FMOVD F0, syscall15Args_r2(R2) // save r2 | 85 | + | MOVD R0, syscall15Args_a1(R2) // save r1 |
| 86 | + | MOVD R1, syscall15Args_a2(R2) // save r3 | |||
| 87 | + | FMOVD F0, syscall15Args_f1(R2) // save f0 | |||
| 88 | + | FMOVD F1, syscall15Args_f2(R2) // save f1 | |||
| 89 | + | FMOVD F2, syscall15Args_f3(R2) // save f2 | |||
| 90 | + | FMOVD F3, syscall15Args_f4(R2) // save f3 | |||
| 91 | + | ||||
| 85 | RET | 92 | RET | ||
M syscall_cgo_linux.go +1 -0
| @@ -18,6 +18,7 @@ type syscall15Args struct { | |||
| 18 | 18 | fn, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15 uintptr | |
| 19 | 19 | f1, f2, f3, f4, f5, f6, f7, f8 uintptr | |
| 20 | 20 | r1, r2, err uintptr | |
| 21 | + | arm64_r8 uintptr | |
| 21 | 22 | } | |
| 22 | 23 | ||
| 23 | 24 | //go:nosplit | |
| @@ -18,6 +18,7 @@ type syscall15Args struct { | |||||
| 18 | fn, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15 uintptr | 18 | fn, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15 uintptr | ||
| 19 | f1, f2, f3, f4, f5, f6, f7, f8 uintptr | 19 | f1, f2, f3, f4, f5, f6, f7, f8 uintptr | ||
| 20 | r1, r2, err uintptr | 20 | r1, r2, err uintptr | ||
| 21 | + | arm64_r8 uintptr | |||
| 21 | } | 22 | } | ||
| 22 | 23 | ||||
| 23 | //go:nosplit | 24 | //go:nosplit | ||
M syscall_sysv.go +2 -1
| @@ -18,6 +18,7 @@ type syscall15Args struct { | |||
| 18 | 18 | fn, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15 uintptr | |
| 19 | 19 | f1, f2, f3, f4, f5, f6, f7, f8 uintptr | |
| 20 | 20 | r1, r2, err uintptr | |
| 21 | + | arm64_r8 uintptr | |
| 21 | 22 | } | |
| 22 | 23 | ||
| 23 | 24 | //go:nosplit | |
| @@ -25,7 +26,7 @@ func syscall_syscall15X(fn, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a | |||
| 25 | 26 | args := syscall15Args{ | |
| 26 | 27 | fn, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, | |
| 27 | 28 | a1, a2, a3, a4, a5, a6, a7, a8, | |
| 28 | - | r1, r2, err, | |
| 29 | + | r1, r2, err, 0, | |
| 29 | 30 | } | |
| 30 | 31 | runtime_cgocall(syscall15XABI0, unsafe.Pointer(&args)) | |
| 31 | 32 | return args.r1, args.r2, args.err | |
| @@ -18,6 +18,7 @@ type syscall15Args struct { | |||||
| 18 | fn, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15 uintptr | 18 | fn, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15 uintptr | ||
| 19 | f1, f2, f3, f4, f5, f6, f7, f8 uintptr | 19 | f1, f2, f3, f4, f5, f6, f7, f8 uintptr | ||
| 20 | r1, r2, err uintptr | 20 | r1, r2, err uintptr | ||
| 21 | + | arm64_r8 uintptr | |||
| 21 | } | 22 | } | ||
| 22 | 23 | ||||
| 23 | //go:nosplit | 24 | //go:nosplit | ||
| @@ -25,7 +26,7 @@ func syscall_syscall15X(fn, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a | |||||
| 25 | args := syscall15Args{ | 26 | args := syscall15Args{ | ||
| 26 | fn, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, | 27 | fn, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, | ||
| 27 | a1, a2, a3, a4, a5, a6, a7, a8, | 28 | a1, a2, a3, a4, a5, a6, a7, a8, | ||
| 28 | - | r1, r2, err, | 29 | + | r1, r2, err, 0, |
| 29 | } | 30 | } | ||
| 30 | runtime_cgocall(syscall15XABI0, unsafe.Pointer(&args)) | 31 | runtime_cgocall(syscall15XABI0, unsafe.Pointer(&args)) | ||
| 31 | return args.r1, args.r2, args.err | 32 | return args.r1, args.r2, args.err | ||
M syscall_windows.go +1 -0
| @@ -16,6 +16,7 @@ type syscall15Args struct { | |||
| 16 | 16 | fn, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15 uintptr | |
| 17 | 17 | f1, f2, f3, f4, f5, f6, f7, f8 uintptr | |
| 18 | 18 | r1, r2, err uintptr | |
| 19 | + | arm64_r8 uintptr | |
| 19 | 20 | } | |
| 20 | 21 | ||
| 21 | 22 | func syscall_syscall15X(fn, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15 uintptr) (r1, r2, err uintptr) { | |
| @@ -16,6 +16,7 @@ type syscall15Args struct { | |||||
| 16 | fn, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15 uintptr | 16 | fn, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15 uintptr | ||
| 17 | f1, f2, f3, f4, f5, f6, f7, f8 uintptr | 17 | f1, f2, f3, f4, f5, f6, f7, f8 uintptr | ||
| 18 | r1, r2, err uintptr | 18 | r1, r2, err uintptr | ||
| 19 | + | arm64_r8 uintptr | |||
| 19 | } | 20 | } | ||
| 20 | 21 | ||||
| 21 | func syscall_syscall15X(fn, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15 uintptr) (r1, r2, err uintptr) { | 22 | func syscall_syscall15X(fn, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15 uintptr) (r1, r2, err uintptr) { | ||
A testdata/structtest/structreturn_test.c +224 -0
| @@ -0,0 +1,224 @@ | |||
| 1 | + | // SPDX-License-Identifier: Apache-2.0 | |
| 2 | + | // SPDX-FileCopyrightText: 2024 The Ebitengine Authors | |
| 3 | + | ||
| 4 | + | #include <stdint.h> | |
| 5 | + | ||
| 6 | + | struct Empty{}; | |
| 7 | + | ||
| 8 | + | struct Empty ReturnEmpty() { | |
| 9 | + | struct Empty e = {}; | |
| 10 | + | return e; | |
| 11 | + | } | |
| 12 | + | ||
| 13 | + | struct StructInStruct{ | |
| 14 | + | struct{ int16_t a; } a; | |
| 15 | + | struct{ int16_t b; } b; | |
| 16 | + | struct{ int16_t c; } c; | |
| 17 | + | }; | |
| 18 | + | ||
| 19 | + | struct StructInStruct ReturnStructInStruct(int16_t a, int16_t b, int16_t c) { | |
| 20 | + | struct StructInStruct e = {{a}, {b}, {c}}; | |
| 21 | + | return e; | |
| 22 | + | } | |
| 23 | + | ||
| 24 | + | struct ThreeShorts{ | |
| 25 | + | int16_t a, b, c; | |
| 26 | + | }; | |
| 27 | + | ||
| 28 | + | struct ThreeShorts ReturnThreeShorts(int16_t a, int16_t b, int16_t c) { | |
| 29 | + | struct ThreeShorts e = {a, b, c}; | |
| 30 | + | return e; | |
| 31 | + | } | |
| 32 | + | ||
| 33 | + | struct FourShorts{ | |
| 34 | + | int16_t a, b, c, d; | |
| 35 | + | }; | |
| 36 | + | ||
| 37 | + | struct FourShorts ReturnFourShorts(int16_t a, int16_t b, int16_t c, int16_t d) { | |
| 38 | + | struct FourShorts e = {a, b, c, d}; | |
| 39 | + | return e; | |
| 40 | + | } | |
| 41 | + | ||
| 42 | + | struct OneLong{ | |
| 43 | + | int64_t a; | |
| 44 | + | }; | |
| 45 | + | ||
| 46 | + | struct OneLong ReturnOneLong(int64_t a) { | |
| 47 | + | struct OneLong e = {a}; | |
| 48 | + | return e; | |
| 49 | + | } | |
| 50 | + | ||
| 51 | + | struct TwoLongs{ | |
| 52 | + | int64_t a, b; | |
| 53 | + | }; | |
| 54 | + | ||
| 55 | + | struct TwoLongs ReturnTwoLongs(int64_t a, int64_t b) { | |
| 56 | + | struct TwoLongs e = {a, b}; | |
| 57 | + | return e; | |
| 58 | + | } | |
| 59 | + | ||
| 60 | + | struct ThreeLongs{ | |
| 61 | + | int64_t a, b, c; | |
| 62 | + | }; | |
| 63 | + | ||
| 64 | + | struct ThreeLongs ReturnThreeLongs(int64_t a, int64_t b, int64_t c) { | |
| 65 | + | struct ThreeLongs e = {a, b, c}; | |
| 66 | + | return e; | |
| 67 | + | } | |
| 68 | + | ||
| 69 | + | struct OneFloat{ | |
| 70 | + | float a; | |
| 71 | + | }; | |
| 72 | + | ||
| 73 | + | struct TwoFloats{ | |
| 74 | + | float a, b; | |
| 75 | + | }; | |
| 76 | + | ||
| 77 | + | struct TwoFloats ReturnTwoFloats(float a, float b) { | |
| 78 | + | struct TwoFloats e = {a-b, a*b}; | |
| 79 | + | return e; | |
| 80 | + | } | |
| 81 | + | ||
| 82 | + | struct ThreeFloats{ | |
| 83 | + | float a, b, c; | |
| 84 | + | }; | |
| 85 | + | ||
| 86 | + | struct ThreeFloats ReturnThreeFloats(float a, float b, float c) { | |
| 87 | + | struct ThreeFloats e = {a, b, c}; | |
| 88 | + | return e; | |
| 89 | + | } | |
| 90 | + | ||
| 91 | + | struct OneFloat ReturnOneFloat(float a) { | |
| 92 | + | struct OneFloat e = {a}; | |
| 93 | + | return e; | |
| 94 | + | } | |
| 95 | + | ||
| 96 | + | struct OneDouble{ | |
| 97 | + | double a; | |
| 98 | + | }; | |
| 99 | + | ||
| 100 | + | struct OneDouble ReturnOneDouble(double a) { | |
| 101 | + | struct OneDouble e = {a}; | |
| 102 | + | return e; | |
| 103 | + | } | |
| 104 | + | ||
| 105 | + | struct TwoDoubles{ | |
| 106 | + | double a, b; | |
| 107 | + | }; | |
| 108 | + | ||
| 109 | + | struct TwoDoubles ReturnTwoDoubles(double a, double b) { | |
| 110 | + | struct TwoDoubles e = {a, b}; | |
| 111 | + | return e; | |
| 112 | + | } | |
| 113 | + | ||
| 114 | + | struct ThreeDoubles{ | |
| 115 | + | double a, b, c; | |
| 116 | + | }; | |
| 117 | + | ||
| 118 | + | struct ThreeDoubles ReturnThreeDoubles(double a, double b, double c) { | |
| 119 | + | struct ThreeDoubles e = {a, b, c}; | |
| 120 | + | return e; | |
| 121 | + | } | |
| 122 | + | ||
| 123 | + | struct FourDoubles{ | |
| 124 | + | double a, b, c, d; | |
| 125 | + | }; | |
| 126 | + | ||
| 127 | + | struct FourDoubles ReturnFourDoubles(double a, double b, double c, double d) { | |
| 128 | + | struct FourDoubles e = {a, b, c, d}; | |
| 129 | + | return e; | |
| 130 | + | } | |
| 131 | + | ||
| 132 | + | struct FiveDoubles{ | |
| 133 | + | double a, b, c, d, e; | |
| 134 | + | }; | |
| 135 | + | ||
| 136 | + | struct FiveDoubles ReturnFiveDoubles(double a, double b, double c, double d, double e) { | |
| 137 | + | struct FiveDoubles s = {a, b, c, d, e}; | |
| 138 | + | return s; | |
| 139 | + | } | |
| 140 | + | ||
| 141 | + | struct OneFloatOneDouble{ | |
| 142 | + | float a; | |
| 143 | + | double b; | |
| 144 | + | }; | |
| 145 | + | ||
| 146 | + | struct OneFloatOneDouble ReturnOneFloatOneDouble(float a, double b) { | |
| 147 | + | struct OneFloatOneDouble e = {a, b}; | |
| 148 | + | return e; | |
| 149 | + | } | |
| 150 | + | ||
| 151 | + | struct OneDoubleOneFloat{ | |
| 152 | + | double a; | |
| 153 | + | float b; | |
| 154 | + | }; | |
| 155 | + | ||
| 156 | + | struct OneDoubleOneFloat ReturnOneDoubleOneFloat(double a, float b) { | |
| 157 | + | struct OneDoubleOneFloat e = {a, b}; | |
| 158 | + | return e; | |
| 159 | + | } | |
| 160 | + | ||
| 161 | + | struct Unaligned1{ | |
| 162 | + | int8_t a; | |
| 163 | + | int16_t b; | |
| 164 | + | int64_t c; | |
| 165 | + | }; | |
| 166 | + | ||
| 167 | + | struct Unaligned1 ReturnUnaligned1(int8_t a, int16_t b, int64_t c) { | |
| 168 | + | struct Unaligned1 e = {a, b, c}; | |
| 169 | + | return e; | |
| 170 | + | } | |
| 171 | + | ||
| 172 | + | struct Mixed1{ | |
| 173 | + | float a; | |
| 174 | + | int32_t b; | |
| 175 | + | }; | |
| 176 | + | ||
| 177 | + | struct Mixed1 ReturnMixed1(float a, int32_t b) { | |
| 178 | + | struct Mixed1 e = {a, b}; | |
| 179 | + | return e; | |
| 180 | + | } | |
| 181 | + | ||
| 182 | + | struct Mixed2{ | |
| 183 | + | float a; | |
| 184 | + | int32_t b; | |
| 185 | + | float c; | |
| 186 | + | int32_t d; | |
| 187 | + | }; | |
| 188 | + | ||
| 189 | + | struct Mixed2 ReturnMixed2(float a, int32_t b, float c, int32_t d) { | |
| 190 | + | struct Mixed2 e = {a, b, c, d}; | |
| 191 | + | return e; | |
| 192 | + | } | |
| 193 | + | ||
| 194 | + | struct Mixed3{ | |
| 195 | + | float a; | |
| 196 | + | uint32_t b; | |
| 197 | + | double c; | |
| 198 | + | }; | |
| 199 | + | ||
| 200 | + | struct Mixed3 ReturnMixed3(float a, uint32_t b, double c) { | |
| 201 | + | struct Mixed3 s = {a, b, c}; | |
| 202 | + | return s; | |
| 203 | + | } | |
| 204 | + | ||
| 205 | + | struct Mixed4{ | |
| 206 | + | double a; | |
| 207 | + | uint32_t b; | |
| 208 | + | float c; | |
| 209 | + | }; | |
| 210 | + | ||
| 211 | + | struct Mixed4 ReturnMixed4(double a, uint32_t b, float c) { | |
| 212 | + | struct Mixed4 s = {a, b, c}; | |
| 213 | + | return s; | |
| 214 | + | } | |
| 215 | + | ||
| 216 | + | struct Ptr1{ | |
| 217 | + | int64_t *a; | |
| 218 | + | void *b; | |
| 219 | + | }; | |
| 220 | + | ||
| 221 | + | struct Ptr1 ReturnPtr1(int64_t *a, void *b) { | |
| 222 | + | struct Ptr1 s = {a, b}; | |
| 223 | + | return s; | |
| 224 | + | } | |
| @@ -0,0 +1,224 @@ | |||||
| 1 | + | // SPDX-License-Identifier: Apache-2.0 | |||
| 2 | + | // SPDX-FileCopyrightText: 2024 The Ebitengine Authors | |||
| 3 | + | ||||
| 4 | + | #include <stdint.h> | |||
| 5 | + | ||||
| 6 | + | struct Empty{}; | |||
| 7 | + | ||||
| 8 | + | struct Empty ReturnEmpty() { | |||
| 9 | + | struct Empty e = {}; | |||
| 10 | + | return e; | |||
| 11 | + | } | |||
| 12 | + | ||||
| 13 | + | struct StructInStruct{ | |||
| 14 | + | struct{ int16_t a; } a; | |||
| 15 | + | struct{ int16_t b; } b; | |||
| 16 | + | struct{ int16_t c; } c; | |||
| 17 | + | }; | |||
| 18 | + | ||||
| 19 | + | struct StructInStruct ReturnStructInStruct(int16_t a, int16_t b, int16_t c) { | |||
| 20 | + | struct StructInStruct e = {{a}, {b}, {c}}; | |||
| 21 | + | return e; | |||
| 22 | + | } | |||
| 23 | + | ||||
| 24 | + | struct ThreeShorts{ | |||
| 25 | + | int16_t a, b, c; | |||
| 26 | + | }; | |||
| 27 | + | ||||
| 28 | + | struct ThreeShorts ReturnThreeShorts(int16_t a, int16_t b, int16_t c) { | |||
| 29 | + | struct ThreeShorts e = {a, b, c}; | |||
| 30 | + | return e; | |||
| 31 | + | } | |||
| 32 | + | ||||
| 33 | + | struct FourShorts{ | |||
| 34 | + | int16_t a, b, c, d; | |||
| 35 | + | }; | |||
| 36 | + | ||||
| 37 | + | struct FourShorts ReturnFourShorts(int16_t a, int16_t b, int16_t c, int16_t d) { | |||
| 38 | + | struct FourShorts e = {a, b, c, d}; | |||
| 39 | + | return e; | |||
| 40 | + | } | |||
| 41 | + | ||||
| 42 | + | struct OneLong{ | |||
| 43 | + | int64_t a; | |||
| 44 | + | }; | |||
| 45 | + | ||||
| 46 | + | struct OneLong ReturnOneLong(int64_t a) { | |||
| 47 | + | struct OneLong e = {a}; | |||
| 48 | + | return e; | |||
| 49 | + | } | |||
| 50 | + | ||||
| 51 | + | struct TwoLongs{ | |||
| 52 | + | int64_t a, b; | |||
| 53 | + | }; | |||
| 54 | + | ||||
| 55 | + | struct TwoLongs ReturnTwoLongs(int64_t a, int64_t b) { | |||
| 56 | + | struct TwoLongs e = {a, b}; | |||
| 57 | + | return e; | |||
| 58 | + | } | |||
| 59 | + | ||||
| 60 | + | struct ThreeLongs{ | |||
| 61 | + | int64_t a, b, c; | |||
| 62 | + | }; | |||
| 63 | + | ||||
| 64 | + | struct ThreeLongs ReturnThreeLongs(int64_t a, int64_t b, int64_t c) { | |||
| 65 | + | struct ThreeLongs e = {a, b, c}; | |||
| 66 | + | return e; | |||
| 67 | + | } | |||
| 68 | + | ||||
| 69 | + | struct OneFloat{ | |||
| 70 | + | float a; | |||
| 71 | + | }; | |||
| 72 | + | ||||
| 73 | + | struct TwoFloats{ | |||
| 74 | + | float a, b; | |||
| 75 | + | }; | |||
| 76 | + | ||||
| 77 | + | struct TwoFloats ReturnTwoFloats(float a, float b) { | |||
| 78 | + | struct TwoFloats e = {a-b, a*b}; | |||
| 79 | + | return e; | |||
| 80 | + | } | |||
| 81 | + | ||||
| 82 | + | struct ThreeFloats{ | |||
| 83 | + | float a, b, c; | |||
| 84 | + | }; | |||
| 85 | + | ||||
| 86 | + | struct ThreeFloats ReturnThreeFloats(float a, float b, float c) { | |||
| 87 | + | struct ThreeFloats e = {a, b, c}; | |||
| 88 | + | return e; | |||
| 89 | + | } | |||
| 90 | + | ||||
| 91 | + | struct OneFloat ReturnOneFloat(float a) { | |||
| 92 | + | struct OneFloat e = {a}; | |||
| 93 | + | return e; | |||
| 94 | + | } | |||
| 95 | + | ||||
| 96 | + | struct OneDouble{ | |||
| 97 | + | double a; | |||
| 98 | + | }; | |||
| 99 | + | ||||
| 100 | + | struct OneDouble ReturnOneDouble(double a) { | |||
| 101 | + | struct OneDouble e = {a}; | |||
| 102 | + | return e; | |||
| 103 | + | } | |||
| 104 | + | ||||
| 105 | + | struct TwoDoubles{ | |||
| 106 | + | double a, b; | |||
| 107 | + | }; | |||
| 108 | + | ||||
| 109 | + | struct TwoDoubles ReturnTwoDoubles(double a, double b) { | |||
| 110 | + | struct TwoDoubles e = {a, b}; | |||
| 111 | + | return e; | |||
| 112 | + | } | |||
| 113 | + | ||||
| 114 | + | struct ThreeDoubles{ | |||
| 115 | + | double a, b, c; | |||
| 116 | + | }; | |||
| 117 | + | ||||
| 118 | + | struct ThreeDoubles ReturnThreeDoubles(double a, double b, double c) { | |||
| 119 | + | struct ThreeDoubles e = {a, b, c}; | |||
| 120 | + | return e; | |||
| 121 | + | } | |||
| 122 | + | ||||
| 123 | + | struct FourDoubles{ | |||
| 124 | + | double a, b, c, d; | |||
| 125 | + | }; | |||
| 126 | + | ||||
| 127 | + | struct FourDoubles ReturnFourDoubles(double a, double b, double c, double d) { | |||
| 128 | + | struct FourDoubles e = {a, b, c, d}; | |||
| 129 | + | return e; | |||
| 130 | + | } | |||
| 131 | + | ||||
| 132 | + | struct FiveDoubles{ | |||
| 133 | + | double a, b, c, d, e; | |||
| 134 | + | }; | |||
| 135 | + | ||||
| 136 | + | struct FiveDoubles ReturnFiveDoubles(double a, double b, double c, double d, double e) { | |||
| 137 | + | struct FiveDoubles s = {a, b, c, d, e}; | |||
| 138 | + | return s; | |||
| 139 | + | } | |||
| 140 | + | ||||
| 141 | + | struct OneFloatOneDouble{ | |||
| 142 | + | float a; | |||
| 143 | + | double b; | |||
| 144 | + | }; | |||
| 145 | + | ||||
| 146 | + | struct OneFloatOneDouble ReturnOneFloatOneDouble(float a, double b) { | |||
| 147 | + | struct OneFloatOneDouble e = {a, b}; | |||
| 148 | + | return e; | |||
| 149 | + | } | |||
| 150 | + | ||||
| 151 | + | struct OneDoubleOneFloat{ | |||
| 152 | + | double a; | |||
| 153 | + | float b; | |||
| 154 | + | }; | |||
| 155 | + | ||||
| 156 | + | struct OneDoubleOneFloat ReturnOneDoubleOneFloat(double a, float b) { | |||
| 157 | + | struct OneDoubleOneFloat e = {a, b}; | |||
| 158 | + | return e; | |||
| 159 | + | } | |||
| 160 | + | ||||
| 161 | + | struct Unaligned1{ | |||
| 162 | + | int8_t a; | |||
| 163 | + | int16_t b; | |||
| 164 | + | int64_t c; | |||
| 165 | + | }; | |||
| 166 | + | ||||
| 167 | + | struct Unaligned1 ReturnUnaligned1(int8_t a, int16_t b, int64_t c) { | |||
| 168 | + | struct Unaligned1 e = {a, b, c}; | |||
| 169 | + | return e; | |||
| 170 | + | } | |||
| 171 | + | ||||
| 172 | + | struct Mixed1{ | |||
| 173 | + | float a; | |||
| 174 | + | int32_t b; | |||
| 175 | + | }; | |||
| 176 | + | ||||
| 177 | + | struct Mixed1 ReturnMixed1(float a, int32_t b) { | |||
| 178 | + | struct Mixed1 e = {a, b}; | |||
| 179 | + | return e; | |||
| 180 | + | } | |||
| 181 | + | ||||
| 182 | + | struct Mixed2{ | |||
| 183 | + | float a; | |||
| 184 | + | int32_t b; | |||
| 185 | + | float c; | |||
| 186 | + | int32_t d; | |||
| 187 | + | }; | |||
| 188 | + | ||||
| 189 | + | struct Mixed2 ReturnMixed2(float a, int32_t b, float c, int32_t d) { | |||
| 190 | + | struct Mixed2 e = {a, b, c, d}; | |||
| 191 | + | return e; | |||
| 192 | + | } | |||
| 193 | + | ||||
| 194 | + | struct Mixed3{ | |||
| 195 | + | float a; | |||
| 196 | + | uint32_t b; | |||
| 197 | + | double c; | |||
| 198 | + | }; | |||
| 199 | + | ||||
| 200 | + | struct Mixed3 ReturnMixed3(float a, uint32_t b, double c) { | |||
| 201 | + | struct Mixed3 s = {a, b, c}; | |||
| 202 | + | return s; | |||
| 203 | + | } | |||
| 204 | + | ||||
| 205 | + | struct Mixed4{ | |||
| 206 | + | double a; | |||
| 207 | + | uint32_t b; | |||
| 208 | + | float c; | |||
| 209 | + | }; | |||
| 210 | + | ||||
| 211 | + | struct Mixed4 ReturnMixed4(double a, uint32_t b, float c) { | |||
| 212 | + | struct Mixed4 s = {a, b, c}; | |||
| 213 | + | return s; | |||
| 214 | + | } | |||
| 215 | + | ||||
| 216 | + | struct Ptr1{ | |||
| 217 | + | int64_t *a; | |||
| 218 | + | void *b; | |||
| 219 | + | }; | |||
| 220 | + | ||||
| 221 | + | struct Ptr1 ReturnPtr1(int64_t *a, void *b) { | |||
| 222 | + | struct Ptr1 s = {a, b}; | |||
| 223 | + | return s; | |||
| 224 | + | } | |||