detached 6df3e6f

purego: support returning structs from callbacks (#436)

Add setStruct implementations for all architectures to enable
callbacks created with NewCallback to return struct values.

Updates #225

Co-authored-by: TotallyGamerJet <TotallyGamerJet@users.noreply.github.com>
commit
6df3e6fdbb63f831bb1496a9dae6521a45729c67
author
Hajime Hoshi · · 2026-03-10 03:36 UTC
committer
GitHub · · 2026-03-10 03:36 UTC
parent
81d8c41
signature
GPG UnverifiedB5690EEEBB952194 The key is unknown
18 files · +617 -351
M func.go +5 -5
@@ -173,7 +173,7 @@ func RegisterFunc(fptr any, cfn uintptr) {
173173 stack++
174174 }
175175 case reflect.Struct:
176- ensureStructSupportedForRegisterFunc()
176+ ensureStructSupported()
177177 if arg.Size() == 0 {
178178 continue
179179 }
@@ -192,7 +192,7 @@ func RegisterFunc(fptr any, cfn uintptr) {
192192 }
193193 }
194194 if ty.NumOut() == 1 && ty.Out(0).Kind() == reflect.Struct {
195- ensureStructSupportedForRegisterFunc()
195+ ensureStructSupported()
196196 outType := ty.Out(0)
197197 checkStructFieldsSupported(outType)
198198 if runtime.GOARCH == "amd64" && outType.Size() > maxRegAllocStructSize {
@@ -490,12 +490,12 @@ func checkStructFieldsSupported(ty reflect.Type) {
490490 }
491491 }
492492
493-func ensureStructSupportedForRegisterFunc() {
493+func ensureStructSupported() {
494494 if runtime.GOARCH != "amd64" && runtime.GOARCH != "arm64" {
495- panic("purego: struct arguments are only supported on amd64 and arm64")
495+ panic("purego: struct arguments/returns are only supported on amd64 and arm64")
496496 }
497497 if runtime.GOOS != "darwin" && runtime.GOOS != "linux" {
498- panic("purego: struct arguments are only supported on darwin and linux")
498+ panic("purego: struct arguments/returns are only supported on darwin and linux")
499499 }
500500 }
501501
@@ -173,7 +173,7 @@ func RegisterFunc(fptr any, cfn uintptr) {
173 stack++173 stack++
174 }174 }
175 case reflect.Struct:175 case reflect.Struct:
176- ensureStructSupportedForRegisterFunc()176+ ensureStructSupported()
177 if arg.Size() == 0 {177 if arg.Size() == 0 {
178 continue178 continue
179 }179 }
@@ -192,7 +192,7 @@ func RegisterFunc(fptr any, cfn uintptr) {
192 }192 }
193 }193 }
194 if ty.NumOut() == 1 && ty.Out(0).Kind() == reflect.Struct {194 if ty.NumOut() == 1 && ty.Out(0).Kind() == reflect.Struct {
195- ensureStructSupportedForRegisterFunc()195+ ensureStructSupported()
196 outType := ty.Out(0)196 outType := ty.Out(0)
197 checkStructFieldsSupported(outType)197 checkStructFieldsSupported(outType)
198 if runtime.GOARCH == "amd64" && outType.Size() > maxRegAllocStructSize {198 if runtime.GOARCH == "amd64" && outType.Size() > maxRegAllocStructSize {
@@ -490,12 +490,12 @@ func checkStructFieldsSupported(ty reflect.Type) {
490 }490 }
491 }491 }
492 492
493-func ensureStructSupportedForRegisterFunc() {493+func ensureStructSupported() {
494 if runtime.GOARCH != "amd64" && runtime.GOARCH != "arm64" {494 if runtime.GOARCH != "amd64" && runtime.GOARCH != "arm64" {
495- panic("purego: struct arguments are only supported on amd64 and arm64")495+ panic("purego: struct arguments/returns are only supported on amd64 and arm64")
496 }496 }
497 if runtime.GOOS != "darwin" && runtime.GOOS != "linux" {497 if runtime.GOOS != "darwin" && runtime.GOOS != "linux" {
498- panic("purego: struct arguments are only supported on darwin and linux")498+ panic("purego: struct arguments/returns are only supported on darwin and linux")
499 }499 }
500 }500 }
501 501
M objc/objc_block_darwin_test.go +1 -1
@@ -59,7 +59,7 @@ func ExampleInvokeBlock() {
5959 &vector{X: 6.7, Y: 8.9, Z: 0.1},
6060 )
6161
62- fmt.Println(*result, err)
62+ fmt.Printf("{%.2f %.2f %.2f} %v\n", result.X, result.Y, result.Z, err)
6363 // Output: {-39.82 30.14 -14.52} <nil>
6464 }
6565
@@ -59,7 +59,7 @@ func ExampleInvokeBlock() {
59 &vector{X: 6.7, Y: 8.9, Z: 0.1},59 &vector{X: 6.7, Y: 8.9, Z: 0.1},
60 )60 )
61 61
62- fmt.Println(*result, err)62+ fmt.Printf("{%.2f %.2f %.2f} %v\n", result.X, result.Y, result.Z, err)
63 // Output: {-39.82 30.14 -14.52} <nil>63 // Output: {-39.82 30.14 -14.52} <nil>
64 }64 }
65 65
M struct_386.go +4 -0
@@ -39,3 +39,7 @@ func collectStackArgs(args []reflect.Value, startIdx int, numInts, numFloats int
3939 func bundleStackArgs(stackArgs []reflect.Value, addStack func(uintptr)) {
4040 panic("purego: bundleStackArgs should not be called on 386")
4141 }
42+
43+func setStruct(a *callbackArgs, ret reflect.Value) {
44+ panic("purego: struct returns are not supported on 386")
45+}
@@ -39,3 +39,7 @@ func collectStackArgs(args []reflect.Value, startIdx int, numInts, numFloats int
39 func bundleStackArgs(stackArgs []reflect.Value, addStack func(uintptr)) {39 func bundleStackArgs(stackArgs []reflect.Value, addStack func(uintptr)) {
40 panic("purego: bundleStackArgs should not be called on 386")40 panic("purego: bundleStackArgs should not be called on 386")
41 }41 }
42+
43+func setStruct(a *callbackArgs, ret reflect.Value) {
44+ panic("purego: struct returns are not supported on 386")
45+}
M struct_amd64.go +84 -0
@@ -284,3 +284,87 @@ func collectStackArgs(args []reflect.Value, startIdx int, numInts, numFloats int
284284 func bundleStackArgs(stackArgs []reflect.Value, addStack func(uintptr)) {
285285 panic("purego: bundleStackArgs should not be called on amd64")
286286 }
287+
288+func setStruct(a *callbackArgs, ret reflect.Value) {
289+ outSize := ret.Type().Size()
290+ switch {
291+ case outSize == 0:
292+ return
293+ case outSize <= 16:
294+ // Copy the struct's raw bytes (including padding) into a buffer.
295+ var buf [2]uintptr
296+ reflect.NewAt(ret.Type(), unsafe.Pointer(&buf[0])).Elem().Set(ret)
297+ // Classify each eightbyte by the SysV ABI rules (§3.2.3, rule 4d
298+ // of https://refspecs.linuxbase.org/elf/x86_64-abi-0.99.pdf).
299+ // INTEGER wins over SSE for mixed eightbytes. Place INTEGER eightbytes in
300+ // result[0]/result[1] (AX/DX) and SSE eightbytes in result[2]/result[3]
301+ // (XMM0/XMM1).
302+ // Assign each eightbyte to the next available register of the
303+ // appropriate class. The ABI counts integer (AX, DX) and SSE
304+ // (XMM0, XMM1) return registers independently.
305+ var numInts int
306+ var numFloats int
307+ for i := 0; i < 2 && uintptr(i)*8 < outSize; i++ {
308+ class := classifyEightbyte(ret.Type(), uintptr(i)*8, uintptr(i)*8+8)
309+ if class == _SSE {
310+ switch numFloats {
311+ case 0:
312+ a.result[2] = buf[i]
313+ case 1:
314+ a.result[3] = buf[i]
315+ }
316+ numFloats++
317+ } else {
318+ switch numInts {
319+ case 0:
320+ a.result[0] = buf[i]
321+ case 1:
322+ a.result[1] = buf[i]
323+ }
324+ numInts++
325+ }
326+ }
327+ default:
328+ // Structs > 16 bytes are returned by hidden pointer.
329+ // a.result[0] contains the pointer passed by the caller in RDI.
330+ // Write the struct through this pointer.
331+ reflect.NewAt(ret.Type(), *(*unsafe.Pointer)(unsafe.Pointer(&a.result[0]))).Elem().Set(ret)
332+ }
333+}
334+
335+// classifyEightbyte returns the SysV ABI class for the byte range [start, end)
336+// within a type, by examining all scalar fields that overlap that range.
337+func classifyEightbyte(t reflect.Type, start, end uintptr) int {
338+ return doClassifyEightbyte(t, 0, start, end)
339+}
340+
341+func doClassifyEightbyte(t reflect.Type, base, start, end uintptr) int {
342+ switch t.Kind() {
343+ case reflect.Struct:
344+ class := _NO_CLASS
345+ for i := 0; i < t.NumField(); i++ {
346+ f := t.Field(i)
347+ class |= doClassifyEightbyte(f.Type, base+f.Offset, start, end)
348+ }
349+ return class
350+ case reflect.Array:
351+ class := _NO_CLASS
352+ elemSize := t.Elem().Size()
353+ for i := 0; i < t.Len(); i++ {
354+ class |= doClassifyEightbyte(t.Elem(), base+uintptr(i)*elemSize, start, end)
355+ }
356+ return class
357+ default:
358+ fStart := base
359+ fEnd := base + t.Size()
360+ if fStart >= end || fEnd <= start {
361+ return _NO_CLASS
362+ }
363+ switch t.Kind() {
364+ case reflect.Float32, reflect.Float64:
365+ return _SSE
366+ default:
367+ return _INTEGER
368+ }
369+ }
370+}
@@ -284,3 +284,87 @@ func collectStackArgs(args []reflect.Value, startIdx int, numInts, numFloats int
284 func bundleStackArgs(stackArgs []reflect.Value, addStack func(uintptr)) {284 func bundleStackArgs(stackArgs []reflect.Value, addStack func(uintptr)) {
285 panic("purego: bundleStackArgs should not be called on amd64")285 panic("purego: bundleStackArgs should not be called on amd64")
286 }286 }
287+
288+func setStruct(a *callbackArgs, ret reflect.Value) {
289+ outSize := ret.Type().Size()
290+ switch {
291+ case outSize == 0:
292+ return
293+ case outSize <= 16:
294+ // Copy the struct's raw bytes (including padding) into a buffer.
295+ var buf [2]uintptr
296+ reflect.NewAt(ret.Type(), unsafe.Pointer(&buf[0])).Elem().Set(ret)
297+ // Classify each eightbyte by the SysV ABI rules (§3.2.3, rule 4d
298+ // of https://refspecs.linuxbase.org/elf/x86_64-abi-0.99.pdf).
299+ // INTEGER wins over SSE for mixed eightbytes. Place INTEGER eightbytes in
300+ // result[0]/result[1] (AX/DX) and SSE eightbytes in result[2]/result[3]
301+ // (XMM0/XMM1).
302+ // Assign each eightbyte to the next available register of the
303+ // appropriate class. The ABI counts integer (AX, DX) and SSE
304+ // (XMM0, XMM1) return registers independently.
305+ var numInts int
306+ var numFloats int
307+ for i := 0; i < 2 && uintptr(i)*8 < outSize; i++ {
308+ class := classifyEightbyte(ret.Type(), uintptr(i)*8, uintptr(i)*8+8)
309+ if class == _SSE {
310+ switch numFloats {
311+ case 0:
312+ a.result[2] = buf[i]
313+ case 1:
314+ a.result[3] = buf[i]
315+ }
316+ numFloats++
317+ } else {
318+ switch numInts {
319+ case 0:
320+ a.result[0] = buf[i]
321+ case 1:
322+ a.result[1] = buf[i]
323+ }
324+ numInts++
325+ }
326+ }
327+ default:
328+ // Structs > 16 bytes are returned by hidden pointer.
329+ // a.result[0] contains the pointer passed by the caller in RDI.
330+ // Write the struct through this pointer.
331+ reflect.NewAt(ret.Type(), *(*unsafe.Pointer)(unsafe.Pointer(&a.result[0]))).Elem().Set(ret)
332+ }
333+}
334+
335+// classifyEightbyte returns the SysV ABI class for the byte range [start, end)
336+// within a type, by examining all scalar fields that overlap that range.
337+func classifyEightbyte(t reflect.Type, start, end uintptr) int {
338+ return doClassifyEightbyte(t, 0, start, end)
339+}
340+
341+func doClassifyEightbyte(t reflect.Type, base, start, end uintptr) int {
342+ switch t.Kind() {
343+ case reflect.Struct:
344+ class := _NO_CLASS
345+ for i := 0; i < t.NumField(); i++ {
346+ f := t.Field(i)
347+ class |= doClassifyEightbyte(f.Type, base+f.Offset, start, end)
348+ }
349+ return class
350+ case reflect.Array:
351+ class := _NO_CLASS
352+ elemSize := t.Elem().Size()
353+ for i := 0; i < t.Len(); i++ {
354+ class |= doClassifyEightbyte(t.Elem(), base+uintptr(i)*elemSize, start, end)
355+ }
356+ return class
357+ default:
358+ fStart := base
359+ fEnd := base + t.Size()
360+ if fStart >= end || fEnd <= start {
361+ return _NO_CLASS
362+ }
363+ switch t.Kind() {
364+ case reflect.Float32, reflect.Float64:
365+ return _SSE
366+ default:
367+ return _INTEGER
368+ }
369+ }
370+}
M struct_arm.go +4 -0
@@ -83,3 +83,7 @@ func collectStackArgs(args []reflect.Value, startIdx int, numInts, numFloats int
8383 func bundleStackArgs(stackArgs []reflect.Value, addStack func(uintptr)) {
8484 panic("purego: bundleStackArgs should not be called on arm")
8585 }
86+
87+func setStruct(a *callbackArgs, ret reflect.Value) {
88+ panic("purego: struct returns are not supported on arm")
89+}
@@ -83,3 +83,7 @@ func collectStackArgs(args []reflect.Value, startIdx int, numInts, numFloats int
83 func bundleStackArgs(stackArgs []reflect.Value, addStack func(uintptr)) {83 func bundleStackArgs(stackArgs []reflect.Value, addStack func(uintptr)) {
84 panic("purego: bundleStackArgs should not be called on arm")84 panic("purego: bundleStackArgs should not be called on arm")
85 }85 }
86+
87+func setStruct(a *callbackArgs, ret reflect.Value) {
88+ panic("purego: struct returns are not supported on arm")
89+}
M struct_arm64.go +50 -6
@@ -24,7 +24,7 @@ func getStruct(outType reflect.Type, syscall syscall15Args) (v reflect.Value) {
2424 if isAllFloats, numFields := isAllSameFloat(outType); isAllFloats {
2525 r1 = syscall.f1
2626 if numFields == 2 {
27- r1 = syscall.f2<<32 | syscall.f1
27+ r1 = syscall.f2<<32 | syscall.f1&math.MaxUint32
2828 }
2929 }
3030 return reflect.NewAt(outType, unsafe.Pointer(&struct{ a uintptr }{r1})).Elem()
@@ -33,16 +33,16 @@ func getStruct(outType reflect.Type, syscall syscall15Args) (v reflect.Value) {
3333 if isAllFloats, numFields := isAllSameFloat(outType); isAllFloats {
3434 switch numFields {
3535 case 4:
36- r1 = syscall.f2<<32 | syscall.f1
37- r2 = syscall.f4<<32 | syscall.f3
36+ r1 = syscall.f2<<32 | syscall.f1&math.MaxUint32
37+ r2 = syscall.f4<<32 | syscall.f3&math.MaxUint32
3838 case 3:
39- r1 = syscall.f2<<32 | syscall.f1
39+ r1 = syscall.f2<<32 | syscall.f1&math.MaxUint32
4040 r2 = syscall.f3
4141 case 2:
4242 r1 = syscall.f1
4343 r2 = syscall.f2
4444 default:
45- panic("unreachable")
45+ panic("not reached")
4646 }
4747 }
4848 return reflect.NewAt(outType, unsafe.Pointer(&struct{ a, b uintptr }{r1, r2})).Elem()
@@ -54,7 +54,7 @@ func getStruct(outType reflect.Type, syscall syscall15Args) (v reflect.Value) {
5454 case 3:
5555 return reflect.NewAt(outType, unsafe.Pointer(&struct{ a, b, c uintptr }{syscall.f1, syscall.f2, syscall.f3})).Elem()
5656 default:
57- panic("unreachable")
57+ panic("not reached")
5858 }
5959 }
6060 // create struct from the Go pointer created in arm64_r8
@@ -547,3 +547,47 @@ func bundleStackArgs(stackArgs []reflect.Value, addStack func(uintptr)) {
547547 size := structType.Size()
548548 copyStruct8ByteChunks(ptr, size, addStack)
549549 }
550+
551+func setStruct(a *callbackArgs, ret reflect.Value) {
552+ outSize := ret.Type().Size()
553+ switch {
554+ case outSize == 0:
555+ return
556+ case outSize <= 8:
557+ reflect.NewAt(ret.Type(), unsafe.Pointer(&a.result)).Elem().Set(ret)
558+ if isAllFloats, numFields := isAllSameFloat(ret.Type()); isAllFloats && numFields == 2 {
559+ a.result[1] = a.result[0] >> 32 // expanding two float32s into a.result[0] and a.result[1]
560+ a.result[0] &= math.MaxUint32 // clear the top bits since they contain the second argument
561+ }
562+ return
563+ case outSize <= 16:
564+ reflect.NewAt(ret.Type(), unsafe.Pointer(&a.result)).Elem().Set(ret)
565+ if isAllFloats, numFields := isAllSameFloat(ret.Type()); isAllFloats {
566+ switch numFields {
567+ case 4:
568+ a.result[3] = a.result[1] >> 32
569+ a.result[2] = a.result[1] & math.MaxUint32
570+ a.result[1] = a.result[0] >> 32
571+ a.result[0] &= math.MaxUint32
572+ case 3:
573+ a.result[2] = a.result[1] & math.MaxUint32
574+ a.result[1] = a.result[0] >> 32
575+ a.result[0] &= math.MaxUint32
576+ case 2:
577+ // two float64s are already in a.result[0] and a.result[1]
578+ default:
579+ panic("not reached")
580+ }
581+ }
582+ return
583+ default:
584+ if isAllFloats, numFields := isAllSameFloat(ret.Type()); isAllFloats && numFields <= 4 {
585+ reflect.NewAt(ret.Type(), unsafe.Pointer(&a.result)).Elem().Set(ret)
586+ return
587+ }
588+ // The caller passed the address to place the return struct,
589+ // so copy the Go struct into the provided memory.
590+ reflect.NewAt(ret.Type(), *(*unsafe.Pointer)(unsafe.Pointer(&a.result[0]))).Elem().Set(ret)
591+ return
592+ }
593+}
@@ -24,7 +24,7 @@ func getStruct(outType reflect.Type, syscall syscall15Args) (v reflect.Value) {
24 if isAllFloats, numFields := isAllSameFloat(outType); isAllFloats {24 if isAllFloats, numFields := isAllSameFloat(outType); isAllFloats {
25 r1 = syscall.f125 r1 = syscall.f1
26 if numFields == 2 {26 if numFields == 2 {
27- r1 = syscall.f2<<32 | syscall.f127+ r1 = syscall.f2<<32 | syscall.f1&math.MaxUint32
28 }28 }
29 }29 }
30 return reflect.NewAt(outType, unsafe.Pointer(&struct{ a uintptr }{r1})).Elem()30 return reflect.NewAt(outType, unsafe.Pointer(&struct{ a uintptr }{r1})).Elem()
@@ -33,16 +33,16 @@ func getStruct(outType reflect.Type, syscall syscall15Args) (v reflect.Value) {
33 if isAllFloats, numFields := isAllSameFloat(outType); isAllFloats {33 if isAllFloats, numFields := isAllSameFloat(outType); isAllFloats {
34 switch numFields {34 switch numFields {
35 case 4:35 case 4:
36- r1 = syscall.f2<<32 | syscall.f136+ r1 = syscall.f2<<32 | syscall.f1&math.MaxUint32
37- r2 = syscall.f4<<32 | syscall.f337+ r2 = syscall.f4<<32 | syscall.f3&math.MaxUint32
38 case 3:38 case 3:
39- r1 = syscall.f2<<32 | syscall.f139+ r1 = syscall.f2<<32 | syscall.f1&math.MaxUint32
40 r2 = syscall.f340 r2 = syscall.f3
41 case 2:41 case 2:
42 r1 = syscall.f142 r1 = syscall.f1
43 r2 = syscall.f243 r2 = syscall.f2
44 default:44 default:
45- panic("unreachable")45+ panic("not reached")
46 }46 }
47 }47 }
48 return reflect.NewAt(outType, unsafe.Pointer(&struct{ a, b uintptr }{r1, r2})).Elem()48 return reflect.NewAt(outType, unsafe.Pointer(&struct{ a, b uintptr }{r1, r2})).Elem()
@@ -54,7 +54,7 @@ func getStruct(outType reflect.Type, syscall syscall15Args) (v reflect.Value) {
54 case 3:54 case 3:
55 return reflect.NewAt(outType, unsafe.Pointer(&struct{ a, b, c uintptr }{syscall.f1, syscall.f2, syscall.f3})).Elem()55 return reflect.NewAt(outType, unsafe.Pointer(&struct{ a, b, c uintptr }{syscall.f1, syscall.f2, syscall.f3})).Elem()
56 default:56 default:
57- panic("unreachable")57+ panic("not reached")
58 }58 }
59 }59 }
60 // create struct from the Go pointer created in arm64_r860 // create struct from the Go pointer created in arm64_r8
@@ -547,3 +547,47 @@ func bundleStackArgs(stackArgs []reflect.Value, addStack func(uintptr)) {
547 size := structType.Size()547 size := structType.Size()
548 copyStruct8ByteChunks(ptr, size, addStack)548 copyStruct8ByteChunks(ptr, size, addStack)
549 }549 }
550+
551+func setStruct(a *callbackArgs, ret reflect.Value) {
552+ outSize := ret.Type().Size()
553+ switch {
554+ case outSize == 0:
555+ return
556+ case outSize <= 8:
557+ reflect.NewAt(ret.Type(), unsafe.Pointer(&a.result)).Elem().Set(ret)
558+ if isAllFloats, numFields := isAllSameFloat(ret.Type()); isAllFloats && numFields == 2 {
559+ a.result[1] = a.result[0] >> 32 // expanding two float32s into a.result[0] and a.result[1]
560+ a.result[0] &= math.MaxUint32 // clear the top bits since they contain the second argument
561+ }
562+ return
563+ case outSize <= 16:
564+ reflect.NewAt(ret.Type(), unsafe.Pointer(&a.result)).Elem().Set(ret)
565+ if isAllFloats, numFields := isAllSameFloat(ret.Type()); isAllFloats {
566+ switch numFields {
567+ case 4:
568+ a.result[3] = a.result[1] >> 32
569+ a.result[2] = a.result[1] & math.MaxUint32
570+ a.result[1] = a.result[0] >> 32
571+ a.result[0] &= math.MaxUint32
572+ case 3:
573+ a.result[2] = a.result[1] & math.MaxUint32
574+ a.result[1] = a.result[0] >> 32
575+ a.result[0] &= math.MaxUint32
576+ case 2:
577+ // two float64s are already in a.result[0] and a.result[1]
578+ default:
579+ panic("not reached")
580+ }
581+ }
582+ return
583+ default:
584+ if isAllFloats, numFields := isAllSameFloat(ret.Type()); isAllFloats && numFields <= 4 {
585+ reflect.NewAt(ret.Type(), unsafe.Pointer(&a.result)).Elem().Set(ret)
586+ return
587+ }
588+ // The caller passed the address to place the return struct,
589+ // so copy the Go struct into the provided memory.
590+ reflect.NewAt(ret.Type(), *(*unsafe.Pointer)(unsafe.Pointer(&a.result[0]))).Elem().Set(ret)
591+ return
592+ }
593+}
M struct_loong64.go +5 -1
@@ -37,7 +37,7 @@ func getStruct(outType reflect.Type, syscall syscall15Args) (v reflect.Value) {
3737 r1 = syscall.f1
3838 r2 = syscall.f2
3939 default:
40- panic("unreachable")
40+ panic("not reached")
4141 }
4242 }
4343 return reflect.NewAt(outType, unsafe.Pointer(&struct{ a, b uintptr }{r1, r2})).Elem()
@@ -211,3 +211,7 @@ func collectStackArgs(args []reflect.Value, startIdx int, numInts, numFloats int
211211 func bundleStackArgs(stackArgs []reflect.Value, addStack func(uintptr)) {
212212 panic("purego: bundleStackArgs should not be called on loong64")
213213 }
214+
215+func setStruct(a *callbackArgs, ret reflect.Value) {
216+ panic("purego: struct returns are not supported on loong64")
217+}
@@ -37,7 +37,7 @@ func getStruct(outType reflect.Type, syscall syscall15Args) (v reflect.Value) {
37 r1 = syscall.f137 r1 = syscall.f1
38 r2 = syscall.f238 r2 = syscall.f2
39 default:39 default:
40- panic("unreachable")40+ panic("not reached")
41 }41 }
42 }42 }
43 return reflect.NewAt(outType, unsafe.Pointer(&struct{ a, b uintptr }{r1, r2})).Elem()43 return reflect.NewAt(outType, unsafe.Pointer(&struct{ a, b uintptr }{r1, r2})).Elem()
@@ -211,3 +211,7 @@ func collectStackArgs(args []reflect.Value, startIdx int, numInts, numFloats int
211 func bundleStackArgs(stackArgs []reflect.Value, addStack func(uintptr)) {211 func bundleStackArgs(stackArgs []reflect.Value, addStack func(uintptr)) {
212 panic("purego: bundleStackArgs should not be called on loong64")212 panic("purego: bundleStackArgs should not be called on loong64")
213 }213 }
214+
215+func setStruct(a *callbackArgs, ret reflect.Value) {
216+ panic("purego: struct returns are not supported on loong64")
217+}
M struct_ppc64le.go +5 -1
@@ -139,5 +139,9 @@ func collectStackArgs(
139139 }
140140
141141 func bundleStackArgs(stackArgs []reflect.Value, addStack func(uintptr)) {
142- panic("bundleStackArgs not supported on PPC64LE")
142+ panic("purego: bundleStackArgs should not be called on ppc64le")
143+}
144+
145+func setStruct(a *callbackArgs, ret reflect.Value) {
146+ panic("purego: struct returns are not supported on ppc64le")
143147 }
@@ -139,5 +139,9 @@ func collectStackArgs(
139 }139 }
140 140
141 func bundleStackArgs(stackArgs []reflect.Value, addStack func(uintptr)) {141 func bundleStackArgs(stackArgs []reflect.Value, addStack func(uintptr)) {
142- panic("bundleStackArgs not supported on PPC64LE")142+ panic("purego: bundleStackArgs should not be called on ppc64le")
143+}
144+
145+func setStruct(a *callbackArgs, ret reflect.Value) {
146+ panic("purego: struct returns are not supported on ppc64le")
143 }147 }
M struct_riscv64.go +5 -1
@@ -139,5 +139,9 @@ func collectStackArgs(
139139 }
140140
141141 func bundleStackArgs(stackArgs []reflect.Value, addStack func(uintptr)) {
142- panic("bundleStackArgs not supported on RISCV64")
142+ panic("purego: bundleStackArgs should not be called on riscv64")
143+}
144+
145+func setStruct(a *callbackArgs, ret reflect.Value) {
146+ panic("purego: struct returns are not supported on riscv64")
143147 }
@@ -139,5 +139,9 @@ func collectStackArgs(
139 }139 }
140 140
141 func bundleStackArgs(stackArgs []reflect.Value, addStack func(uintptr)) {141 func bundleStackArgs(stackArgs []reflect.Value, addStack func(uintptr)) {
142- panic("bundleStackArgs not supported on RISCV64")142+ panic("purego: bundleStackArgs should not be called on riscv64")
143+}
144+
145+func setStruct(a *callbackArgs, ret reflect.Value) {
146+ panic("purego: struct returns are not supported on riscv64")
143 }147 }
M struct_s390x.go +5 -1
@@ -139,5 +139,9 @@ func collectStackArgs(
139139 }
140140
141141 func bundleStackArgs(stackArgs []reflect.Value, addStack func(uintptr)) {
142- panic("bundleStackArgs not supported on S390X")
142+ panic("purego: bundleStackArgs should not be called on s390x")
143+}
144+
145+func setStruct(a *callbackArgs, ret reflect.Value) {
146+ panic("purego: struct returns are not supported on s390x")
143147 }
@@ -139,5 +139,9 @@ func collectStackArgs(
139 }139 }
140 140
141 func bundleStackArgs(stackArgs []reflect.Value, addStack func(uintptr)) {141 func bundleStackArgs(stackArgs []reflect.Value, addStack func(uintptr)) {
142- panic("bundleStackArgs not supported on S390X")142+ panic("purego: bundleStackArgs should not be called on s390x")
143+}
144+
145+func setStruct(a *callbackArgs, ret reflect.Value) {
146+ panic("purego: struct returns are not supported on s390x")
143 }147 }
M struct_test.go +395 -315
@@ -9,6 +9,7 @@ import (
99 "math"
1010 "os"
1111 "path/filepath"
12+ "reflect"
1213 "runtime"
1314 "testing"
1415 "unsafe"
@@ -532,6 +533,41 @@ func TestRegisterFunc_structArgs(t *testing.T) {
532533 }
533534 }
534535
536+// TODO: this could use the iter.Seq interface when purego supports Go 1.23
537+func nextFieldFn(v reflect.Value) func() (reflect.Value, bool) {
538+ var fieldIndex int
539+ var tracker func() (reflect.Value, bool)
540+ return func() (reflect.Value, bool) {
541+ if v.NumField() == 0 {
542+ return reflect.Value{}, false
543+ }
544+ if tracker != nil {
545+ if field, ok := tracker(); ok {
546+ return field, ok
547+ }
548+ tracker = nil
549+ }
550+ for fieldIndex < v.NumField() {
551+ if v.Type().Field(fieldIndex).Name == "_" {
552+ fieldIndex++
553+ continue
554+ }
555+ field := v.Field(fieldIndex)
556+ fieldIndex++
557+ if field.Kind() == reflect.Struct {
558+ tracker = nextFieldFn(field)
559+ if inner, ok := tracker(); ok {
560+ return inner, ok
561+ }
562+ tracker = nil
563+ } else {
564+ return field, true
565+ }
566+ }
567+ return reflect.Value{}, false
568+ }
569+}
570+
535571 func TestRegisterFunc_structReturns(t *testing.T) {
536572 libFileName := filepath.Join(t.TempDir(), "structreturntest.so")
537573 t.Logf("Build %v", libFileName)
@@ -545,321 +581,365 @@ func TestRegisterFunc_structReturns(t *testing.T) {
545581 if err != nil {
546582 t.Fatalf("Dlopen(%q) failed: %v", libFileName, err)
547583 }
584+ implementations := []struct {
585+ name string
586+ register func(fptr any, handle uintptr, name string)
587+ }{
588+ {
589+ name: "RegisterLibFunc",
590+ register: purego.RegisterLibFunc,
591+ },
592+ {
593+ name: "GoCallbackFunc",
594+ register: func(fptr any, _ uintptr, _ string) {
595+ fn := reflect.MakeFunc(reflect.TypeOf(fptr).Elem(), func(args []reflect.Value) []reflect.Value {
596+ retType := reflect.TypeOf(fptr).Elem().Out(0)
597+ ret := reflect.New(retType).Elem()
598+ next := nextFieldFn(ret)
599+ for _, a := range args {
600+ field, ok := next()
601+ if !ok {
602+ panic("purego: no more fields")
603+ }
604+ field.Set(a)
605+ }
606+ return []reflect.Value{ret}
607+ })
608+ purego.RegisterFunc(fptr, purego.NewCallback(fn.Interface()))
609+ },
610+ },
611+ }
612+ for _, imp := range implementations {
613+ imp := imp
614+ t.Run(imp.name, func(t *testing.T) {
615+ register := imp.register
616+ {
617+ type Empty struct{}
618+ var ReturnEmpty func() Empty
619+ register(&ReturnEmpty, lib, "ReturnEmpty")
620+ ret := ReturnEmpty()
621+ _ = ret
622+ }
623+ {
624+ type inner struct{ A int16 }
625+ type StructInStruct struct {
626+ A inner
627+ B inner
628+ C inner
629+ }
630+ var ReturnStructInStruct func(a, b, c int16) StructInStruct
631+ register(&ReturnStructInStruct, lib, "ReturnStructInStruct")
632+ expected := StructInStruct{inner{^int16(0)}, inner{2}, inner{3}}
633+ if ret := ReturnStructInStruct(^int16(0), 2, 3); ret != expected {
634+ t.Fatalf("StructInStruct returned %+v wanted %+v", ret, expected)
635+ }
636+ }
637+ {
638+ type ThreeShorts struct{ A, B, C int16 }
639+ var ReturnThreeShorts func(a, b, c int16) ThreeShorts
640+ register(&ReturnThreeShorts, lib, "ReturnThreeShorts")
641+ expected := ThreeShorts{^int16(0), 2, 3}
642+ if ret := ReturnThreeShorts(^int16(0), 2, 3); ret != expected {
643+ t.Fatalf("ReturnThreeShorts returned %+v wanted %+v", ret, expected)
644+ }
645+ }
646+ {
647+ type FourShorts struct{ A, B, C, D int16 }
648+ var ReturnFourShorts func(a, b, c, d int16) FourShorts
649+ register(&ReturnFourShorts, lib, "ReturnFourShorts")
650+ expected := FourShorts{^int16(0), 2, 3, 4}
651+ if ret := ReturnFourShorts(^int16(0), 2, 3, 4); ret != expected {
652+ t.Fatalf("ReturnFourShorts returned %+v wanted %+v", ret, expected)
653+ }
654+ }
655+ {
656+ type OneLong struct{ A int64 }
657+ var ReturnOneLong func(a int64) OneLong
658+ register(&ReturnOneLong, lib, "ReturnOneLong")
659+ expected := OneLong{5}
660+ if ret := ReturnOneLong(5); ret != expected {
661+ t.Fatalf("ReturnOneLong returned %+v wanted %+v", ret, expected)
662+ }
663+ }
664+ {
665+ type TwoLongs struct{ A, B int64 }
666+ var ReturnTwoLongs func(a, b int64) TwoLongs
667+ register(&ReturnTwoLongs, lib, "ReturnTwoLongs")
668+ expected := TwoLongs{1, 2}
669+ if ret := ReturnTwoLongs(1, 2); ret != expected {
670+ t.Fatalf("ReturnTwoLongs returned %+v wanted %+v", ret, expected)
671+ }
672+ }
673+ {
674+ type ThreeLongs struct{ A, B, C int64 }
675+ var ReturnThreeLongs func(a, b, c int64) ThreeLongs
676+ register(&ReturnThreeLongs, lib, "ReturnThreeLongs")
677+ expected := ThreeLongs{1, 2, 3}
678+ if ret := ReturnThreeLongs(1, 2, 3); ret != expected {
679+ t.Fatalf("ReturnThreeLongs returned %+v wanted %+v", ret, expected)
680+ }
681+ }
682+ {
683+ type OneFloat struct{ A float32 }
684+ var ReturnOneFloat func(a float32) OneFloat
685+ register(&ReturnOneFloat, lib, "ReturnOneFloat")
686+ expected := OneFloat{1}
687+ if ret := ReturnOneFloat(1); ret != expected {
688+ t.Fatalf("ReturnOneFloat returned %+v wanted %+v", ret, expected)
689+ }
690+ }
691+ {
692+ type TwoFloats struct{ A, B float32 }
693+ var ReturnTwoFloats func(a, b float32) TwoFloats
694+ register(&ReturnTwoFloats, lib, "ReturnTwoFloats")
695+ expected := TwoFloats{5, 2}
696+ if ret := ReturnTwoFloats(5, 2); ret != expected {
697+ t.Fatalf("ReturnTwoFloats returned %+v wanted %+v", ret, expected)
698+ }
699+ }
700+ {
701+ type ThreeFloats struct{ A, B, C float32 }
702+ var ReturnThreeFloats func(a, b, c float32) ThreeFloats
703+ register(&ReturnThreeFloats, lib, "ReturnThreeFloats")
704+ expected := ThreeFloats{1, 2, 3}
705+ if ret := ReturnThreeFloats(1, 2, 3); ret != expected {
706+ t.Fatalf("ReturnThreeFloats returned %+v wanted %+v", ret, expected)
707+ }
708+ }
709+ {
710+ type FourFloats struct{ A, B, C, D float32 }
711+ var ReturnFourFloats func(a, b, c, d float32) FourFloats
712+ register(&ReturnFourFloats, lib, "ReturnFourFloats")
713+ expected := FourFloats{1, 2, 3, 4}
714+ if ret := ReturnFourFloats(1, 2, 3, 4); ret != expected {
715+ t.Fatalf("ReturnFourFloats returned %+v wanted %+v", ret, expected)
716+ }
717+ }
718+ {
719+ type OneDouble struct{ A float64 }
720+ var ReturnOneDouble func(a float64) OneDouble
721+ register(&ReturnOneDouble, lib, "ReturnOneDouble")
722+ expected := OneDouble{1}
723+ if ret := ReturnOneDouble(1); ret != expected {
724+ t.Fatalf("ReturnOneDouble returned %+v wanted %+v", ret, expected)
725+ }
726+ }
727+ {
728+ type TwoDoubles struct{ A, B float64 }
729+ var ReturnTwoDoubles func(a, b float64) TwoDoubles
730+ register(&ReturnTwoDoubles, lib, "ReturnTwoDoubles")
731+ expected := TwoDoubles{7, 3}
732+ if ret := ReturnTwoDoubles(7, 3); ret != expected {
733+ t.Fatalf("ReturnTwoDoubles returned %+v wanted %+v", ret, expected)
734+ }
735+ }
736+ {
737+ type ThreeDoubles struct{ A, B, C float64 }
738+ var ReturnThreeDoubles func(a, b, c float64) ThreeDoubles
739+ register(&ReturnThreeDoubles, lib, "ReturnThreeDoubles")
740+ expected := ThreeDoubles{1, 2, 3}
741+ if ret := ReturnThreeDoubles(1, 2, 3); ret != expected {
742+ t.Fatalf("ReturnThreeDoubles returned %+v wanted %+v", ret, expected)
743+ }
744+ }
745+ {
746+ type FourDoubles struct{ A, B, C, D float64 }
747+ var ReturnFourDoubles func(a, b, c, d float64) FourDoubles
748+ register(&ReturnFourDoubles, lib, "ReturnFourDoubles")
749+ expected := FourDoubles{1, 2, 3, 4}
750+ if ret := ReturnFourDoubles(1, 2, 3, 4); ret != expected {
751+ t.Fatalf("ReturnFourDoubles returned %+v wanted %+v", ret, expected)
752+ }
753+ }
754+ {
755+ type FourDoublesInternal struct {
756+ F struct{ A, B float64 }
757+ G struct{ C, D float64 }
758+ }
759+ var ReturnFourDoublesInternal func(a, b, c, d float64) FourDoublesInternal
760+ register(&ReturnFourDoublesInternal, lib, "ReturnFourDoublesInternal")
761+ expected := FourDoublesInternal{F: struct{ A, B float64 }{A: 1, B: 2}, G: struct{ C, D float64 }{C: 3, D: 4}}
762+ if ret := ReturnFourDoublesInternal(1, 2, 3, 4); ret != expected {
763+ t.Fatalf("ReturnFourDoublesInternal returned %+v wanted %+v", ret, expected)
764+ }
765+ }
766+ {
767+ type FiveDoubles struct{ A, B, C, D, E float64 }
768+ var ReturnFiveDoubles func(a, b, c, d, e float64) FiveDoubles
769+ register(&ReturnFiveDoubles, lib, "ReturnFiveDoubles")
770+ expected := FiveDoubles{1, 2, 3, 4, 5}
771+ if ret := ReturnFiveDoubles(1, 2, 3, 4, 5); ret != expected {
772+ t.Fatalf("ReturnFiveDoubles returned %+v wanted %+v", ret, expected)
773+ }
774+ }
775+ {
776+ type OneFloatOneDouble struct {
777+ A float32
778+ _ float32
779+ B float64
780+ }
781+ var ReturnOneFloatOneDouble func(a float32, b float64) OneFloatOneDouble
782+ register(&ReturnOneFloatOneDouble, lib, "ReturnOneFloatOneDouble")
783+ expected := OneFloatOneDouble{A: 1, B: 2}
784+ if ret := ReturnOneFloatOneDouble(1, 2); ret != expected {
785+ t.Fatalf("ReturnOneFloatOneDouble returned %+v wanted %+v", ret, expected)
786+ }
787+ }
788+ {
789+ type OneDoubleOneFloat struct {
790+ A float64
791+ B float32
792+ }
793+ var ReturnOneDoubleOneFloat func(a float64, b float32) OneDoubleOneFloat
794+ register(&ReturnOneDoubleOneFloat, lib, "ReturnOneDoubleOneFloat")
795+ expected := OneDoubleOneFloat{1, 2}
796+ if ret := ReturnOneDoubleOneFloat(1, 2); ret != expected {
797+ t.Fatalf("ReturnOneDoubleOneFloat returned %+v wanted %+v", ret, expected)
798+ }
799+ }
800+ {
801+ type Unaligned1 struct {
802+ A int8
803+ _ [1]int8
804+ B int16
805+ _ [1]int32
806+ C int64
807+ }
808+ var ReturnUnaligned1 func(a int8, b int16, c int64) Unaligned1
809+ register(&ReturnUnaligned1, lib, "ReturnUnaligned1")
810+ expected := Unaligned1{A: 1, B: 2, C: 3}
811+ if ret := ReturnUnaligned1(1, 2, 3); ret != expected {
812+ t.Fatalf("ReturnUnaligned1 returned %+v wanted %+v", ret, expected)
813+ }
814+ }
548815
549- {
550- type Empty struct{}
551- var ReturnEmpty func() Empty
552- purego.RegisterLibFunc(&ReturnEmpty, lib, "ReturnEmpty")
553- ret := ReturnEmpty()
554- _ = ret
555- }
556- {
557- type inner struct{ a int16 }
558- type StructInStruct struct {
559- a inner
560- b inner
561- c inner
562- }
563- var ReturnStructInStruct func(a, b, c int16) StructInStruct
564- purego.RegisterLibFunc(&ReturnStructInStruct, lib, "ReturnStructInStruct")
565- expected := StructInStruct{inner{^int16(0)}, inner{2}, inner{3}}
566- if ret := ReturnStructInStruct(^int16(0), 2, 3); ret != expected {
567- t.Fatalf("StructInStruct returned %+v wanted %+v", ret, expected)
568- }
569- }
570- {
571- type ThreeShorts struct{ a, b, c int16 }
572- var ReturnThreeShorts func(a, b, c int16) ThreeShorts
573- purego.RegisterLibFunc(&ReturnThreeShorts, lib, "ReturnThreeShorts")
574- expected := ThreeShorts{^int16(0), 2, 3}
575- if ret := ReturnThreeShorts(^int16(0), 2, 3); ret != expected {
576- t.Fatalf("ReturnThreeShorts returned %+v wanted %+v", ret, expected)
577- }
578- }
579- {
580- type FourShorts struct{ a, b, c, d int16 }
581- var ReturnFourShorts func(a, b, c, d int16) FourShorts
582- purego.RegisterLibFunc(&ReturnFourShorts, lib, "ReturnFourShorts")
583- expected := FourShorts{^int16(0), 2, 3, 4}
584- if ret := ReturnFourShorts(^int16(0), 2, 3, 4); ret != expected {
585- t.Fatalf("ReturnFourShorts returned %+v wanted %+v", ret, expected)
586- }
587- }
588- {
589- type OneLong struct{ a int64 }
590- var ReturnOneLong func(a int64) OneLong
591- purego.RegisterLibFunc(&ReturnOneLong, lib, "ReturnOneLong")
592- expected := OneLong{5}
593- if ret := ReturnOneLong(5); ret != expected {
594- t.Fatalf("ReturnOneLong returned %+v wanted %+v", ret, expected)
595- }
596- }
597- {
598- type TwoLongs struct{ a, b int64 }
599- var ReturnTwoLongs func(a, b int64) TwoLongs
600- purego.RegisterLibFunc(&ReturnTwoLongs, lib, "ReturnTwoLongs")
601- expected := TwoLongs{1, 2}
602- if ret := ReturnTwoLongs(1, 2); ret != expected {
603- t.Fatalf("ReturnTwoLongs returned %+v wanted %+v", ret, expected)
604- }
605- }
606- {
607- type ThreeLongs struct{ a, b, c int64 }
608- var ReturnThreeLongs func(a, b, c int64) ThreeLongs
609- purego.RegisterLibFunc(&ReturnThreeLongs, lib, "ReturnThreeLongs")
610- expected := ThreeLongs{1, 2, 3}
611- if ret := ReturnThreeLongs(1, 2, 3); ret != expected {
612- t.Fatalf("ReturnThreeLongs returned %+v wanted %+v", ret, expected)
613- }
614- }
615- {
616- type OneFloat struct{ a float32 }
617- var ReturnOneFloat func(a float32) OneFloat
618- purego.RegisterLibFunc(&ReturnOneFloat, lib, "ReturnOneFloat")
619- expected := OneFloat{1}
620- if ret := ReturnOneFloat(1); ret != expected {
621- t.Fatalf("ReturnOneFloat returned %+v wanted %+v", ret, expected)
622- }
623- }
624- {
625- type TwoFloats struct{ a, b float32 }
626- var ReturnTwoFloats func(a, b float32) TwoFloats
627- purego.RegisterLibFunc(&ReturnTwoFloats, lib, "ReturnTwoFloats")
628- expected := TwoFloats{3, 10}
629- if ret := ReturnTwoFloats(5, 2); ret != expected {
630- t.Fatalf("ReturnTwoFloats returned %+v wanted %+v", ret, expected)
631- }
632- }
633- {
634- type ThreeFloats struct{ a, b, c float32 }
635- var ReturnThreeFloats func(a, b, c float32) ThreeFloats
636- purego.RegisterLibFunc(&ReturnThreeFloats, lib, "ReturnThreeFloats")
637- expected := ThreeFloats{1, 2, 3}
638- if ret := ReturnThreeFloats(1, 2, 3); ret != expected {
639- t.Fatalf("ReturnThreeFloats returned %+v wanted %+v", ret, expected)
640- }
641- }
642- {
643- type OneDouble struct{ a float64 }
644- var ReturnOneDouble func(a float64) OneDouble
645- purego.RegisterLibFunc(&ReturnOneDouble, lib, "ReturnOneDouble")
646- expected := OneDouble{1}
647- if ret := ReturnOneDouble(1); ret != expected {
648- t.Fatalf("ReturnOneDouble returned %+v wanted %+v", ret, expected)
649- }
650- }
651- {
652- type TwoDoubles struct{ a, b float64 }
653- var ReturnTwoDoubles func(a, b float64) TwoDoubles
654- purego.RegisterLibFunc(&ReturnTwoDoubles, lib, "ReturnTwoDoubles")
655- expected := TwoDoubles{1, 2}
656- if ret := ReturnTwoDoubles(1, 2); ret != expected {
657- t.Fatalf("ReturnTwoDoubles returned %+v wanted %+v", ret, expected)
658- }
659- }
660- {
661- type ThreeDoubles struct{ a, b, c float64 }
662- var ReturnThreeDoubles func(a, b, c float64) ThreeDoubles
663- purego.RegisterLibFunc(&ReturnThreeDoubles, lib, "ReturnThreeDoubles")
664- expected := ThreeDoubles{1, 2, 3}
665- if ret := ReturnThreeDoubles(1, 2, 3); ret != expected {
666- t.Fatalf("ReturnThreeDoubles returned %+v wanted %+v", ret, expected)
667- }
668- }
669- {
670- type FourDoubles struct{ a, b, c, d float64 }
671- var ReturnFourDoubles func(a, b, c, d float64) FourDoubles
672- purego.RegisterLibFunc(&ReturnFourDoubles, lib, "ReturnFourDoubles")
673- expected := FourDoubles{1, 2, 3, 4}
674- if ret := ReturnFourDoubles(1, 2, 3, 4); ret != expected {
675- t.Fatalf("ReturnFourDoubles returned %+v wanted %+v", ret, expected)
676- }
677- }
678- {
679- type FourDoublesInternal struct {
680- f struct{ a, b float64 }
681- g struct{ c, d float64 }
682- }
683- var ReturnFourDoublesInternal func(a, b, c, d float64) FourDoublesInternal
684- purego.RegisterLibFunc(&ReturnFourDoublesInternal, lib, "ReturnFourDoublesInternal")
685- expected := FourDoublesInternal{f: struct{ a, b float64 }{a: 1, b: 2}, g: struct{ c, d float64 }{c: 3, d: 4}}
686- if ret := ReturnFourDoublesInternal(1, 2, 3, 4); ret != expected {
687- t.Fatalf("ReturnFourDoublesInternal returned %+v wanted %+v", ret, expected)
688- }
689- }
690- {
691- type FiveDoubles struct{ a, b, c, d, e float64 }
692- var ReturnFiveDoubles func(a, b, c, d, e float64) FiveDoubles
693- purego.RegisterLibFunc(&ReturnFiveDoubles, lib, "ReturnFiveDoubles")
694- expected := FiveDoubles{1, 2, 3, 4, 5}
695- if ret := ReturnFiveDoubles(1, 2, 3, 4, 5); ret != expected {
696- t.Fatalf("ReturnFiveDoubles returned %+v wanted %+v", ret, expected)
697- }
698- }
699- {
700- type OneFloatOneDouble struct {
701- a float32
702- _ float32
703- b float64
704- }
705- var ReturnOneFloatOneDouble func(a float32, b float64) OneFloatOneDouble
706- purego.RegisterLibFunc(&ReturnOneFloatOneDouble, lib, "ReturnOneFloatOneDouble")
707- expected := OneFloatOneDouble{a: 1, b: 2}
708- if ret := ReturnOneFloatOneDouble(1, 2); ret != expected {
709- t.Fatalf("ReturnOneFloatOneDouble returned %+v wanted %+v", ret, expected)
710- }
711- }
712- {
713- type OneDoubleOneFloat struct {
714- a float64
715- b float32
716- }
717- var ReturnOneDoubleOneFloat func(a float64, b float32) OneDoubleOneFloat
718- purego.RegisterLibFunc(&ReturnOneDoubleOneFloat, lib, "ReturnOneDoubleOneFloat")
719- expected := OneDoubleOneFloat{1, 2}
720- if ret := ReturnOneDoubleOneFloat(1, 2); ret != expected {
721- t.Fatalf("ReturnOneDoubleOneFloat returned %+v wanted %+v", ret, expected)
722- }
723- }
724- {
725- type Unaligned1 struct {
726- a int8
727- _ [1]int8
728- b int16
729- _ [1]int32
730- c int64
731- }
732- var ReturnUnaligned1 func(a int8, b int16, c int64) Unaligned1
733- purego.RegisterLibFunc(&ReturnUnaligned1, lib, "ReturnUnaligned1")
734- expected := Unaligned1{a: 1, b: 2, c: 3}
735- if ret := ReturnUnaligned1(1, 2, 3); ret != expected {
736- t.Fatalf("ReturnUnaligned1 returned %+v wanted %+v", ret, expected)
737- }
738- }
739- {
740- type Mixed1 struct {
741- a float32
742- b int32
743- }
744- var ReturnMixed1 func(a float32, b int32) Mixed1
745- purego.RegisterLibFunc(&ReturnMixed1, lib, "ReturnMixed1")
746- expected := Mixed1{1, 2}
747- if ret := ReturnMixed1(1, 2); ret != expected {
748- t.Fatalf("ReturnMixed1 returned %+v wanted %+v", ret, expected)
749- }
750- }
751- {
752- type Mixed2 struct {
753- a float32
754- b int32
755- c float32
756- d int32
757- }
758- var ReturnMixed2 func(a float32, b int32, c float32, d int32) Mixed2
759- purego.RegisterLibFunc(&ReturnMixed2, lib, "ReturnMixed2")
760- expected := Mixed2{1, 2, 3, 4}
761- if ret := ReturnMixed2(1, 2, 3, 4); ret != expected {
762- t.Fatalf("ReturnMixed2 returned %+v wanted %+v", ret, expected)
763- }
764- }
765- {
766- type Mixed3 struct {
767- a float32
768- b uint32
769- c float64
770- }
771- var ReturnMixed3 func(a float32, b uint32, c float64) Mixed3
772- purego.RegisterLibFunc(&ReturnMixed3, lib, "ReturnMixed3")
773- expected := Mixed3{1, 2, 3}
774- if ret := ReturnMixed3(1, 2, 3); ret != expected {
775- t.Fatalf("ReturnMixed3 returned %+v wanted %+v", ret, expected)
776- }
777- }
778- {
779- type Mixed4 struct {
780- a float64
781- b uint32
782- c float32
783- }
784- var ReturnMixed4 func(a float64, b uint32, c float32) Mixed4
785- purego.RegisterLibFunc(&ReturnMixed4, lib, "ReturnMixed4")
786- expected := Mixed4{1, 2, 3}
787- if ret := ReturnMixed4(1, 2, 3); ret != expected {
788- t.Fatalf("ReturnMixed4 returned %+v wanted %+v", ret, expected)
789- }
790- }
791- {
792- type Mixed5 struct {
793- a *int64
794- b int32
795- c float32
796- d int32
797- }
798- var ReturnMixed5 func(a *int64, b int32, c float32, d int32) Mixed5
799- purego.RegisterLibFunc(&ReturnMixed5, lib, "ReturnMixed5")
800- ptr := new(int64)
801- expected := Mixed5{ptr, 1, 7.2, 9}
802- if ret := ReturnMixed5(ptr, 1, 7.2, 9); ret != expected {
803- t.Fatalf("ReturnMixed5 returned %+v wanted %+v", ret, expected)
804- }
805- runtime.KeepAlive(ptr)
806- }
807- {
808- type Mixed5 struct {
809- a *int64
810- b int32
811- c float32
812- d int32
813- }
814- var IdentityMixed5 func(m Mixed5) Mixed5
815- purego.RegisterLibFunc(&IdentityMixed5, lib, "IdentityMixed5")
816- ptr := new(int64)
817- expected := Mixed5{ptr, 1, 7.2, 9}
818- if ret := IdentityMixed5(expected); ret != expected {
819- t.Fatalf("IdentityMixed5 returned %+v wanted %+v", ret, expected)
820- }
821- runtime.KeepAlive(ptr)
822- }
823- {
824- type SmallBool struct {
825- a bool
826- b int32
827- c int64
828- }
829- var ReturnSmallBool func(a bool, b int32, c int64) SmallBool
830- purego.RegisterLibFunc(&ReturnSmallBool, lib, "ReturnSmallBool")
831- expected := SmallBool{true, 42, 123456789}
832- if ret := ReturnSmallBool(true, 42, 123456789); ret != expected {
833- t.Fatalf("ReturnSmallBool returned %+v wanted %+v", ret, expected)
834- }
835- }
836- {
837- type LargeBool struct {
838- a bool
839- b int32
840- c int64
841- d int64
842- }
843- var ReturnLargeBool func(a bool, b int32, c int64, d int64) LargeBool
844- purego.RegisterLibFunc(&ReturnLargeBool, lib, "ReturnLargeBool")
845- expected := LargeBool{false, -99, 987654321, 111222333444}
846- if ret := ReturnLargeBool(false, -99, 987654321, 111222333444); ret != expected {
847- t.Fatalf("ReturnLargeBool returned %+v wanted %+v", ret, expected)
848- }
849- }
850- {
851- type Ptr1 struct {
852- a *int64
853- b unsafe.Pointer
854- }
855- var ReturnPtr1 func(a *int64, b unsafe.Pointer) Ptr1
856- purego.RegisterLibFunc(&ReturnPtr1, lib, "ReturnPtr1")
857- a, b := new(int64), new(struct{})
858- expected := Ptr1{a, unsafe.Pointer(b)}
859- if ret := ReturnPtr1(a, unsafe.Pointer(b)); ret != expected {
860- t.Fatalf("ReturnPtr1 returned %+v wanted %+v", ret, expected)
861- }
862- runtime.KeepAlive(a)
863- runtime.KeepAlive(b)
816+ {
817+ type Mixed1 struct {
818+ A float32
819+ B int32
820+ }
821+ var ReturnMixed1 func(a float32, b int32) Mixed1
822+ register(&ReturnMixed1, lib, "ReturnMixed1")
823+ expected := Mixed1{1, 2}
824+ if ret := ReturnMixed1(1, 2); ret != expected {
825+ t.Fatalf("ReturnMixed1 returned %+v wanted %+v", ret, expected)
826+ }
827+ }
828+ {
829+ type Mixed2 struct {
830+ A float32
831+ B int32
832+ C float32
833+ D int32
834+ }
835+ var ReturnMixed2 func(a float32, b int32, c float32, d int32) Mixed2
836+ register(&ReturnMixed2, lib, "ReturnMixed2")
837+ expected := Mixed2{1, 2, 3, 4}
838+ if ret := ReturnMixed2(1, 2, 3, 4); ret != expected {
839+ t.Fatalf("ReturnMixed2 returned %+v wanted %+v", ret, expected)
840+ }
841+ }
842+ {
843+ type Mixed3 struct {
844+ A float32
845+ B uint32
846+ C float64
847+ }
848+ var ReturnMixed3 func(a float32, b uint32, c float64) Mixed3
849+ register(&ReturnMixed3, lib, "ReturnMixed3")
850+ expected := Mixed3{1, 2, 3}
851+ if ret := ReturnMixed3(1, 2, 3); ret != expected {
852+ t.Fatalf("ReturnMixed3 returned %+v wanted %+v", ret, expected)
853+ }
854+ }
855+ {
856+ type Mixed4 struct {
857+ A float64
858+ B uint32
859+ C float32
860+ }
861+ var ReturnMixed4 func(a float64, b uint32, c float32) Mixed4
862+ register(&ReturnMixed4, lib, "ReturnMixed4")
863+ expected := Mixed4{1, 2, 3}
864+ if ret := ReturnMixed4(1, 2, 3); ret != expected {
865+ t.Fatalf("ReturnMixed4 returned %+v wanted %+v", ret, expected)
866+ }
867+ }
868+ {
869+ type Mixed5 struct {
870+ A *int64
871+ B int32
872+ C float32
873+ D int32
874+ }
875+ var ReturnMixed5 func(a *int64, b int32, c float32, d int32) Mixed5
876+ register(&ReturnMixed5, lib, "ReturnMixed5")
877+ ptr := new(int64)
878+ expected := Mixed5{ptr, 1, 7.2, 9}
879+ if ret := ReturnMixed5(ptr, 1, 7.2, 9); ret != expected {
880+ t.Fatalf("ReturnMixed5 returned %+v wanted %+v", ret, expected)
881+ }
882+ runtime.KeepAlive(ptr)
883+ }
884+ {
885+ type Mixed5 struct {
886+ A *int64
887+ B int32
888+ C float32
889+ D int32
890+ }
891+ var IdentityMixed5 func(m Mixed5) Mixed5
892+ // TODO: when struct args are supported in NewCallback use register() instead
893+ purego.RegisterLibFunc(&IdentityMixed5, lib, "IdentityMixed5")
894+ ptr := new(int64)
895+ expected := Mixed5{ptr, 1, 7.2, 9}
896+ if ret := IdentityMixed5(expected); ret != expected {
897+ t.Fatalf("IdentityMixed5 returned %+v wanted %+v", ret, expected)
898+ }
899+ runtime.KeepAlive(ptr)
900+ }
901+ {
902+ type SmallBool struct {
903+ A bool
904+ B int32
905+ C int64
906+ }
907+ var ReturnSmallBool func(a bool, b int32, c int64) SmallBool
908+ register(&ReturnSmallBool, lib, "ReturnSmallBool")
909+ expected := SmallBool{true, 42, 123456789}
910+ if ret := ReturnSmallBool(true, 42, 123456789); ret != expected {
911+ t.Fatalf("ReturnSmallBool returned %+v wanted %+v", ret, expected)
912+ }
913+ }
914+ {
915+ type LargeBool struct {
916+ A bool
917+ B int32
918+ C int64
919+ D int64
920+ }
921+ var ReturnLargeBool func(a bool, b int32, c int64, d int64) LargeBool
922+ register(&ReturnLargeBool, lib, "ReturnLargeBool")
923+ expected := LargeBool{false, -99, 987654321, 111222333444}
924+ if ret := ReturnLargeBool(false, -99, 987654321, 111222333444); ret != expected {
925+ t.Fatalf("ReturnLargeBool returned %+v wanted %+v", ret, expected)
926+ }
927+ }
928+ {
929+ type Ptr1 struct {
930+ a *int64
931+ b unsafe.Pointer
932+ }
933+ var ReturnPtr1 func(a *int64, b unsafe.Pointer) Ptr1
934+ purego.RegisterLibFunc(&ReturnPtr1, lib, "ReturnPtr1")
935+ a, b := new(int64), new(struct{})
936+ expected := Ptr1{a, unsafe.Pointer(b)}
937+ if ret := ReturnPtr1(a, unsafe.Pointer(b)); ret != expected {
938+ t.Fatalf("ReturnPtr1 returned %+v wanted %+v", ret, expected)
939+ }
940+ runtime.KeepAlive(a)
941+ runtime.KeepAlive(b)
942+ }
943+ })
864944 }
865945 }
@@ -9,6 +9,7 @@ import (
9 "math"9 "math"
10 "os"10 "os"
11 "path/filepath"11 "path/filepath"
12+ "reflect"
12 "runtime"13 "runtime"
13 "testing"14 "testing"
14 "unsafe"15 "unsafe"
@@ -532,6 +533,41 @@ func TestRegisterFunc_structArgs(t *testing.T) {
532 }533 }
533 }534 }
534 535
536+// TODO: this could use the iter.Seq interface when purego supports Go 1.23
537+func nextFieldFn(v reflect.Value) func() (reflect.Value, bool) {
538+ var fieldIndex int
539+ var tracker func() (reflect.Value, bool)
540+ return func() (reflect.Value, bool) {
541+ if v.NumField() == 0 {
542+ return reflect.Value{}, false
543+ }
544+ if tracker != nil {
545+ if field, ok := tracker(); ok {
546+ return field, ok
547+ }
548+ tracker = nil
549+ }
550+ for fieldIndex < v.NumField() {
551+ if v.Type().Field(fieldIndex).Name == "_" {
552+ fieldIndex++
553+ continue
554+ }
555+ field := v.Field(fieldIndex)
556+ fieldIndex++
557+ if field.Kind() == reflect.Struct {
558+ tracker = nextFieldFn(field)
559+ if inner, ok := tracker(); ok {
560+ return inner, ok
561+ }
562+ tracker = nil
563+ } else {
564+ return field, true
565+ }
566+ }
567+ return reflect.Value{}, false
568+ }
569+}
570+
535 func TestRegisterFunc_structReturns(t *testing.T) {571 func TestRegisterFunc_structReturns(t *testing.T) {
536 libFileName := filepath.Join(t.TempDir(), "structreturntest.so")572 libFileName := filepath.Join(t.TempDir(), "structreturntest.so")
537 t.Logf("Build %v", libFileName)573 t.Logf("Build %v", libFileName)
@@ -545,321 +581,365 @@ func TestRegisterFunc_structReturns(t *testing.T) {
545 if err != nil {581 if err != nil {
546 t.Fatalf("Dlopen(%q) failed: %v", libFileName, err)582 t.Fatalf("Dlopen(%q) failed: %v", libFileName, err)
547 }583 }
584+ implementations := []struct {
585+ name string
586+ register func(fptr any, handle uintptr, name string)
587+ }{
588+ {
589+ name: "RegisterLibFunc",
590+ register: purego.RegisterLibFunc,
591+ },
592+ {
593+ name: "GoCallbackFunc",
594+ register: func(fptr any, _ uintptr, _ string) {
595+ fn := reflect.MakeFunc(reflect.TypeOf(fptr).Elem(), func(args []reflect.Value) []reflect.Value {
596+ retType := reflect.TypeOf(fptr).Elem().Out(0)
597+ ret := reflect.New(retType).Elem()
598+ next := nextFieldFn(ret)
599+ for _, a := range args {
600+ field, ok := next()
601+ if !ok {
602+ panic("purego: no more fields")
603+ }
604+ field.Set(a)
605+ }
606+ return []reflect.Value{ret}
607+ })
608+ purego.RegisterFunc(fptr, purego.NewCallback(fn.Interface()))
609+ },
610+ },
611+ }
612+ for _, imp := range implementations {
613+ imp := imp
614+ t.Run(imp.name, func(t *testing.T) {
615+ register := imp.register
616+ {
617+ type Empty struct{}
618+ var ReturnEmpty func() Empty
619+ register(&ReturnEmpty, lib, "ReturnEmpty")
620+ ret := ReturnEmpty()
621+ _ = ret
622+ }
623+ {
624+ type inner struct{ A int16 }
625+ type StructInStruct struct {
626+ A inner
627+ B inner
628+ C inner
629+ }
630+ var ReturnStructInStruct func(a, b, c int16) StructInStruct
631+ register(&ReturnStructInStruct, lib, "ReturnStructInStruct")
632+ expected := StructInStruct{inner{^int16(0)}, inner{2}, inner{3}}
633+ if ret := ReturnStructInStruct(^int16(0), 2, 3); ret != expected {
634+ t.Fatalf("StructInStruct returned %+v wanted %+v", ret, expected)
635+ }
636+ }
637+ {
638+ type ThreeShorts struct{ A, B, C int16 }
639+ var ReturnThreeShorts func(a, b, c int16) ThreeShorts
640+ register(&ReturnThreeShorts, lib, "ReturnThreeShorts")
641+ expected := ThreeShorts{^int16(0), 2, 3}
642+ if ret := ReturnThreeShorts(^int16(0), 2, 3); ret != expected {
643+ t.Fatalf("ReturnThreeShorts returned %+v wanted %+v", ret, expected)
644+ }
645+ }
646+ {
647+ type FourShorts struct{ A, B, C, D int16 }
648+ var ReturnFourShorts func(a, b, c, d int16) FourShorts
649+ register(&ReturnFourShorts, lib, "ReturnFourShorts")
650+ expected := FourShorts{^int16(0), 2, 3, 4}
651+ if ret := ReturnFourShorts(^int16(0), 2, 3, 4); ret != expected {
652+ t.Fatalf("ReturnFourShorts returned %+v wanted %+v", ret, expected)
653+ }
654+ }
655+ {
656+ type OneLong struct{ A int64 }
657+ var ReturnOneLong func(a int64) OneLong
658+ register(&ReturnOneLong, lib, "ReturnOneLong")
659+ expected := OneLong{5}
660+ if ret := ReturnOneLong(5); ret != expected {
661+ t.Fatalf("ReturnOneLong returned %+v wanted %+v", ret, expected)
662+ }
663+ }
664+ {
665+ type TwoLongs struct{ A, B int64 }
666+ var ReturnTwoLongs func(a, b int64) TwoLongs
667+ register(&ReturnTwoLongs, lib, "ReturnTwoLongs")
668+ expected := TwoLongs{1, 2}
669+ if ret := ReturnTwoLongs(1, 2); ret != expected {
670+ t.Fatalf("ReturnTwoLongs returned %+v wanted %+v", ret, expected)
671+ }
672+ }
673+ {
674+ type ThreeLongs struct{ A, B, C int64 }
675+ var ReturnThreeLongs func(a, b, c int64) ThreeLongs
676+ register(&ReturnThreeLongs, lib, "ReturnThreeLongs")
677+ expected := ThreeLongs{1, 2, 3}
678+ if ret := ReturnThreeLongs(1, 2, 3); ret != expected {
679+ t.Fatalf("ReturnThreeLongs returned %+v wanted %+v", ret, expected)
680+ }
681+ }
682+ {
683+ type OneFloat struct{ A float32 }
684+ var ReturnOneFloat func(a float32) OneFloat
685+ register(&ReturnOneFloat, lib, "ReturnOneFloat")
686+ expected := OneFloat{1}
687+ if ret := ReturnOneFloat(1); ret != expected {
688+ t.Fatalf("ReturnOneFloat returned %+v wanted %+v", ret, expected)
689+ }
690+ }
691+ {
692+ type TwoFloats struct{ A, B float32 }
693+ var ReturnTwoFloats func(a, b float32) TwoFloats
694+ register(&ReturnTwoFloats, lib, "ReturnTwoFloats")
695+ expected := TwoFloats{5, 2}
696+ if ret := ReturnTwoFloats(5, 2); ret != expected {
697+ t.Fatalf("ReturnTwoFloats returned %+v wanted %+v", ret, expected)
698+ }
699+ }
700+ {
701+ type ThreeFloats struct{ A, B, C float32 }
702+ var ReturnThreeFloats func(a, b, c float32) ThreeFloats
703+ register(&ReturnThreeFloats, lib, "ReturnThreeFloats")
704+ expected := ThreeFloats{1, 2, 3}
705+ if ret := ReturnThreeFloats(1, 2, 3); ret != expected {
706+ t.Fatalf("ReturnThreeFloats returned %+v wanted %+v", ret, expected)
707+ }
708+ }
709+ {
710+ type FourFloats struct{ A, B, C, D float32 }
711+ var ReturnFourFloats func(a, b, c, d float32) FourFloats
712+ register(&ReturnFourFloats, lib, "ReturnFourFloats")
713+ expected := FourFloats{1, 2, 3, 4}
714+ if ret := ReturnFourFloats(1, 2, 3, 4); ret != expected {
715+ t.Fatalf("ReturnFourFloats returned %+v wanted %+v", ret, expected)
716+ }
717+ }
718+ {
719+ type OneDouble struct{ A float64 }
720+ var ReturnOneDouble func(a float64) OneDouble
721+ register(&ReturnOneDouble, lib, "ReturnOneDouble")
722+ expected := OneDouble{1}
723+ if ret := ReturnOneDouble(1); ret != expected {
724+ t.Fatalf("ReturnOneDouble returned %+v wanted %+v", ret, expected)
725+ }
726+ }
727+ {
728+ type TwoDoubles struct{ A, B float64 }
729+ var ReturnTwoDoubles func(a, b float64) TwoDoubles
730+ register(&ReturnTwoDoubles, lib, "ReturnTwoDoubles")
731+ expected := TwoDoubles{7, 3}
732+ if ret := ReturnTwoDoubles(7, 3); ret != expected {
733+ t.Fatalf("ReturnTwoDoubles returned %+v wanted %+v", ret, expected)
734+ }
735+ }
736+ {
737+ type ThreeDoubles struct{ A, B, C float64 }
738+ var ReturnThreeDoubles func(a, b, c float64) ThreeDoubles
739+ register(&ReturnThreeDoubles, lib, "ReturnThreeDoubles")
740+ expected := ThreeDoubles{1, 2, 3}
741+ if ret := ReturnThreeDoubles(1, 2, 3); ret != expected {
742+ t.Fatalf("ReturnThreeDoubles returned %+v wanted %+v", ret, expected)
743+ }
744+ }
745+ {
746+ type FourDoubles struct{ A, B, C, D float64 }
747+ var ReturnFourDoubles func(a, b, c, d float64) FourDoubles
748+ register(&ReturnFourDoubles, lib, "ReturnFourDoubles")
749+ expected := FourDoubles{1, 2, 3, 4}
750+ if ret := ReturnFourDoubles(1, 2, 3, 4); ret != expected {
751+ t.Fatalf("ReturnFourDoubles returned %+v wanted %+v", ret, expected)
752+ }
753+ }
754+ {
755+ type FourDoublesInternal struct {
756+ F struct{ A, B float64 }
757+ G struct{ C, D float64 }
758+ }
759+ var ReturnFourDoublesInternal func(a, b, c, d float64) FourDoublesInternal
760+ register(&ReturnFourDoublesInternal, lib, "ReturnFourDoublesInternal")
761+ expected := FourDoublesInternal{F: struct{ A, B float64 }{A: 1, B: 2}, G: struct{ C, D float64 }{C: 3, D: 4}}
762+ if ret := ReturnFourDoublesInternal(1, 2, 3, 4); ret != expected {
763+ t.Fatalf("ReturnFourDoublesInternal returned %+v wanted %+v", ret, expected)
764+ }
765+ }
766+ {
767+ type FiveDoubles struct{ A, B, C, D, E float64 }
768+ var ReturnFiveDoubles func(a, b, c, d, e float64) FiveDoubles
769+ register(&ReturnFiveDoubles, lib, "ReturnFiveDoubles")
770+ expected := FiveDoubles{1, 2, 3, 4, 5}
771+ if ret := ReturnFiveDoubles(1, 2, 3, 4, 5); ret != expected {
772+ t.Fatalf("ReturnFiveDoubles returned %+v wanted %+v", ret, expected)
773+ }
774+ }
775+ {
776+ type OneFloatOneDouble struct {
777+ A float32
778+ _ float32
779+ B float64
780+ }
781+ var ReturnOneFloatOneDouble func(a float32, b float64) OneFloatOneDouble
782+ register(&ReturnOneFloatOneDouble, lib, "ReturnOneFloatOneDouble")
783+ expected := OneFloatOneDouble{A: 1, B: 2}
784+ if ret := ReturnOneFloatOneDouble(1, 2); ret != expected {
785+ t.Fatalf("ReturnOneFloatOneDouble returned %+v wanted %+v", ret, expected)
786+ }
787+ }
788+ {
789+ type OneDoubleOneFloat struct {
790+ A float64
791+ B float32
792+ }
793+ var ReturnOneDoubleOneFloat func(a float64, b float32) OneDoubleOneFloat
794+ register(&ReturnOneDoubleOneFloat, lib, "ReturnOneDoubleOneFloat")
795+ expected := OneDoubleOneFloat{1, 2}
796+ if ret := ReturnOneDoubleOneFloat(1, 2); ret != expected {
797+ t.Fatalf("ReturnOneDoubleOneFloat returned %+v wanted %+v", ret, expected)
798+ }
799+ }
800+ {
801+ type Unaligned1 struct {
802+ A int8
803+ _ [1]int8
804+ B int16
805+ _ [1]int32
806+ C int64
807+ }
808+ var ReturnUnaligned1 func(a int8, b int16, c int64) Unaligned1
809+ register(&ReturnUnaligned1, lib, "ReturnUnaligned1")
810+ expected := Unaligned1{A: 1, B: 2, C: 3}
811+ if ret := ReturnUnaligned1(1, 2, 3); ret != expected {
812+ t.Fatalf("ReturnUnaligned1 returned %+v wanted %+v", ret, expected)
813+ }
814+ }
548 815
549- {816+ {
550- type Empty struct{}817+ type Mixed1 struct {
551- var ReturnEmpty func() Empty818+ A float32
552- purego.RegisterLibFunc(&ReturnEmpty, lib, "ReturnEmpty")819+ B int32
553- ret := ReturnEmpty()820+ }
554- _ = ret821+ var ReturnMixed1 func(a float32, b int32) Mixed1
555- }822+ register(&ReturnMixed1, lib, "ReturnMixed1")
556- {823+ expected := Mixed1{1, 2}
557- type inner struct{ a int16 }824+ if ret := ReturnMixed1(1, 2); ret != expected {
558- type StructInStruct struct {825+ t.Fatalf("ReturnMixed1 returned %+v wanted %+v", ret, expected)
559- a inner826+ }
560- b inner827+ }
561- c inner828+ {
562- }829+ type Mixed2 struct {
563- var ReturnStructInStruct func(a, b, c int16) StructInStruct830+ A float32
564- purego.RegisterLibFunc(&ReturnStructInStruct, lib, "ReturnStructInStruct")831+ B int32
565- expected := StructInStruct{inner{^int16(0)}, inner{2}, inner{3}}832+ C float32
566- if ret := ReturnStructInStruct(^int16(0), 2, 3); ret != expected {833+ D int32
567- t.Fatalf("StructInStruct returned %+v wanted %+v", ret, expected)834+ }
568- }835+ var ReturnMixed2 func(a float32, b int32, c float32, d int32) Mixed2
569- }836+ register(&ReturnMixed2, lib, "ReturnMixed2")
570- {837+ expected := Mixed2{1, 2, 3, 4}
571- type ThreeShorts struct{ a, b, c int16 }838+ if ret := ReturnMixed2(1, 2, 3, 4); ret != expected {
572- var ReturnThreeShorts func(a, b, c int16) ThreeShorts839+ t.Fatalf("ReturnMixed2 returned %+v wanted %+v", ret, expected)
573- purego.RegisterLibFunc(&ReturnThreeShorts, lib, "ReturnThreeShorts")840+ }
574- expected := ThreeShorts{^int16(0), 2, 3}841+ }
575- if ret := ReturnThreeShorts(^int16(0), 2, 3); ret != expected {842+ {
576- t.Fatalf("ReturnThreeShorts returned %+v wanted %+v", ret, expected)843+ type Mixed3 struct {
577- }844+ A float32
578- }845+ B uint32
579- {846+ C float64
580- type FourShorts struct{ a, b, c, d int16 }847+ }
581- var ReturnFourShorts func(a, b, c, d int16) FourShorts848+ var ReturnMixed3 func(a float32, b uint32, c float64) Mixed3
582- purego.RegisterLibFunc(&ReturnFourShorts, lib, "ReturnFourShorts")849+ register(&ReturnMixed3, lib, "ReturnMixed3")
583- expected := FourShorts{^int16(0), 2, 3, 4}850+ expected := Mixed3{1, 2, 3}
584- if ret := ReturnFourShorts(^int16(0), 2, 3, 4); ret != expected {851+ if ret := ReturnMixed3(1, 2, 3); ret != expected {
585- t.Fatalf("ReturnFourShorts returned %+v wanted %+v", ret, expected)852+ t.Fatalf("ReturnMixed3 returned %+v wanted %+v", ret, expected)
586- }853+ }
587- }854+ }
588- {855+ {
589- type OneLong struct{ a int64 }856+ type Mixed4 struct {
590- var ReturnOneLong func(a int64) OneLong857+ A float64
591- purego.RegisterLibFunc(&ReturnOneLong, lib, "ReturnOneLong")858+ B uint32
592- expected := OneLong{5}859+ C float32
593- if ret := ReturnOneLong(5); ret != expected {860+ }
594- t.Fatalf("ReturnOneLong returned %+v wanted %+v", ret, expected)861+ var ReturnMixed4 func(a float64, b uint32, c float32) Mixed4
595- }862+ register(&ReturnMixed4, lib, "ReturnMixed4")
596- }863+ expected := Mixed4{1, 2, 3}
597- {864+ if ret := ReturnMixed4(1, 2, 3); ret != expected {
598- type TwoLongs struct{ a, b int64 }865+ t.Fatalf("ReturnMixed4 returned %+v wanted %+v", ret, expected)
599- var ReturnTwoLongs func(a, b int64) TwoLongs866+ }
600- purego.RegisterLibFunc(&ReturnTwoLongs, lib, "ReturnTwoLongs")867+ }
601- expected := TwoLongs{1, 2}868+ {
602- if ret := ReturnTwoLongs(1, 2); ret != expected {869+ type Mixed5 struct {
603- t.Fatalf("ReturnTwoLongs returned %+v wanted %+v", ret, expected)870+ A *int64
604- }871+ B int32
605- }872+ C float32
606- {873+ D int32
607- type ThreeLongs struct{ a, b, c int64 }874+ }
608- var ReturnThreeLongs func(a, b, c int64) ThreeLongs875+ var ReturnMixed5 func(a *int64, b int32, c float32, d int32) Mixed5
609- purego.RegisterLibFunc(&ReturnThreeLongs, lib, "ReturnThreeLongs")876+ register(&ReturnMixed5, lib, "ReturnMixed5")
610- expected := ThreeLongs{1, 2, 3}877+ ptr := new(int64)
611- if ret := ReturnThreeLongs(1, 2, 3); ret != expected {878+ expected := Mixed5{ptr, 1, 7.2, 9}
612- t.Fatalf("ReturnThreeLongs returned %+v wanted %+v", ret, expected)879+ if ret := ReturnMixed5(ptr, 1, 7.2, 9); ret != expected {
613- }880+ t.Fatalf("ReturnMixed5 returned %+v wanted %+v", ret, expected)
614- }881+ }
615- {882+ runtime.KeepAlive(ptr)
616- type OneFloat struct{ a float32 }883+ }
617- var ReturnOneFloat func(a float32) OneFloat884+ {
618- purego.RegisterLibFunc(&ReturnOneFloat, lib, "ReturnOneFloat")885+ type Mixed5 struct {
619- expected := OneFloat{1}886+ A *int64
620- if ret := ReturnOneFloat(1); ret != expected {887+ B int32
621- t.Fatalf("ReturnOneFloat returned %+v wanted %+v", ret, expected)888+ C float32
622- }889+ D int32
623- }890+ }
624- {891+ var IdentityMixed5 func(m Mixed5) Mixed5
625- type TwoFloats struct{ a, b float32 }892+ // TODO: when struct args are supported in NewCallback use register() instead
626- var ReturnTwoFloats func(a, b float32) TwoFloats893+ purego.RegisterLibFunc(&IdentityMixed5, lib, "IdentityMixed5")
627- purego.RegisterLibFunc(&ReturnTwoFloats, lib, "ReturnTwoFloats")894+ ptr := new(int64)
628- expected := TwoFloats{3, 10}895+ expected := Mixed5{ptr, 1, 7.2, 9}
629- if ret := ReturnTwoFloats(5, 2); ret != expected {896+ if ret := IdentityMixed5(expected); ret != expected {
630- t.Fatalf("ReturnTwoFloats returned %+v wanted %+v", ret, expected)897+ t.Fatalf("IdentityMixed5 returned %+v wanted %+v", ret, expected)
631- }898+ }
632- }899+ runtime.KeepAlive(ptr)
633- {900+ }
634- type ThreeFloats struct{ a, b, c float32 }901+ {
635- var ReturnThreeFloats func(a, b, c float32) ThreeFloats902+ type SmallBool struct {
636- purego.RegisterLibFunc(&ReturnThreeFloats, lib, "ReturnThreeFloats")903+ A bool
637- expected := ThreeFloats{1, 2, 3}904+ B int32
638- if ret := ReturnThreeFloats(1, 2, 3); ret != expected {905+ C int64
639- t.Fatalf("ReturnThreeFloats returned %+v wanted %+v", ret, expected)906+ }
640- }907+ var ReturnSmallBool func(a bool, b int32, c int64) SmallBool
641- }908+ register(&ReturnSmallBool, lib, "ReturnSmallBool")
642- {909+ expected := SmallBool{true, 42, 123456789}
643- type OneDouble struct{ a float64 }910+ if ret := ReturnSmallBool(true, 42, 123456789); ret != expected {
644- var ReturnOneDouble func(a float64) OneDouble911+ t.Fatalf("ReturnSmallBool returned %+v wanted %+v", ret, expected)
645- purego.RegisterLibFunc(&ReturnOneDouble, lib, "ReturnOneDouble")912+ }
646- expected := OneDouble{1}913+ }
647- if ret := ReturnOneDouble(1); ret != expected {914+ {
648- t.Fatalf("ReturnOneDouble returned %+v wanted %+v", ret, expected)915+ type LargeBool struct {
649- }916+ A bool
650- }917+ B int32
651- {918+ C int64
652- type TwoDoubles struct{ a, b float64 }919+ D int64
653- var ReturnTwoDoubles func(a, b float64) TwoDoubles920+ }
654- purego.RegisterLibFunc(&ReturnTwoDoubles, lib, "ReturnTwoDoubles")921+ var ReturnLargeBool func(a bool, b int32, c int64, d int64) LargeBool
655- expected := TwoDoubles{1, 2}922+ register(&ReturnLargeBool, lib, "ReturnLargeBool")
656- if ret := ReturnTwoDoubles(1, 2); ret != expected {923+ expected := LargeBool{false, -99, 987654321, 111222333444}
657- t.Fatalf("ReturnTwoDoubles returned %+v wanted %+v", ret, expected)924+ if ret := ReturnLargeBool(false, -99, 987654321, 111222333444); ret != expected {
658- }925+ t.Fatalf("ReturnLargeBool returned %+v wanted %+v", ret, expected)
659- }926+ }
660- {927+ }
661- type ThreeDoubles struct{ a, b, c float64 }928+ {
662- var ReturnThreeDoubles func(a, b, c float64) ThreeDoubles929+ type Ptr1 struct {
663- purego.RegisterLibFunc(&ReturnThreeDoubles, lib, "ReturnThreeDoubles")930+ a *int64
664- expected := ThreeDoubles{1, 2, 3}931+ b unsafe.Pointer
665- if ret := ReturnThreeDoubles(1, 2, 3); ret != expected {932+ }
666- t.Fatalf("ReturnThreeDoubles returned %+v wanted %+v", ret, expected)933+ var ReturnPtr1 func(a *int64, b unsafe.Pointer) Ptr1
667- }934+ purego.RegisterLibFunc(&ReturnPtr1, lib, "ReturnPtr1")
668- }935+ a, b := new(int64), new(struct{})
669- {936+ expected := Ptr1{a, unsafe.Pointer(b)}
670- type FourDoubles struct{ a, b, c, d float64 }937+ if ret := ReturnPtr1(a, unsafe.Pointer(b)); ret != expected {
671- var ReturnFourDoubles func(a, b, c, d float64) FourDoubles938+ t.Fatalf("ReturnPtr1 returned %+v wanted %+v", ret, expected)
672- purego.RegisterLibFunc(&ReturnFourDoubles, lib, "ReturnFourDoubles")939+ }
673- expected := FourDoubles{1, 2, 3, 4}940+ runtime.KeepAlive(a)
674- if ret := ReturnFourDoubles(1, 2, 3, 4); ret != expected {941+ runtime.KeepAlive(b)
675- t.Fatalf("ReturnFourDoubles returned %+v wanted %+v", ret, expected)942+ }
676- }943+ })
677- }
678- {
679- type FourDoublesInternal struct {
680- f struct{ a, b float64 }
681- g struct{ c, d float64 }
682- }
683- var ReturnFourDoublesInternal func(a, b, c, d float64) FourDoublesInternal
684- purego.RegisterLibFunc(&ReturnFourDoublesInternal, lib, "ReturnFourDoublesInternal")
685- expected := FourDoublesInternal{f: struct{ a, b float64 }{a: 1, b: 2}, g: struct{ c, d float64 }{c: 3, d: 4}}
686- if ret := ReturnFourDoublesInternal(1, 2, 3, 4); ret != expected {
687- t.Fatalf("ReturnFourDoublesInternal returned %+v wanted %+v", ret, expected)
688- }
689- }
690- {
691- type FiveDoubles struct{ a, b, c, d, e float64 }
692- var ReturnFiveDoubles func(a, b, c, d, e float64) FiveDoubles
693- purego.RegisterLibFunc(&ReturnFiveDoubles, lib, "ReturnFiveDoubles")
694- expected := FiveDoubles{1, 2, 3, 4, 5}
695- if ret := ReturnFiveDoubles(1, 2, 3, 4, 5); ret != expected {
696- t.Fatalf("ReturnFiveDoubles returned %+v wanted %+v", ret, expected)
697- }
698- }
699- {
700- type OneFloatOneDouble struct {
701- a float32
702- _ float32
703- b float64
704- }
705- var ReturnOneFloatOneDouble func(a float32, b float64) OneFloatOneDouble
706- purego.RegisterLibFunc(&ReturnOneFloatOneDouble, lib, "ReturnOneFloatOneDouble")
707- expected := OneFloatOneDouble{a: 1, b: 2}
708- if ret := ReturnOneFloatOneDouble(1, 2); ret != expected {
709- t.Fatalf("ReturnOneFloatOneDouble returned %+v wanted %+v", ret, expected)
710- }
711- }
712- {
713- type OneDoubleOneFloat struct {
714- a float64
715- b float32
716- }
717- var ReturnOneDoubleOneFloat func(a float64, b float32) OneDoubleOneFloat
718- purego.RegisterLibFunc(&ReturnOneDoubleOneFloat, lib, "ReturnOneDoubleOneFloat")
719- expected := OneDoubleOneFloat{1, 2}
720- if ret := ReturnOneDoubleOneFloat(1, 2); ret != expected {
721- t.Fatalf("ReturnOneDoubleOneFloat returned %+v wanted %+v", ret, expected)
722- }
723- }
724- {
725- type Unaligned1 struct {
726- a int8
727- _ [1]int8
728- b int16
729- _ [1]int32
730- c int64
731- }
732- var ReturnUnaligned1 func(a int8, b int16, c int64) Unaligned1
733- purego.RegisterLibFunc(&ReturnUnaligned1, lib, "ReturnUnaligned1")
734- expected := Unaligned1{a: 1, b: 2, c: 3}
735- if ret := ReturnUnaligned1(1, 2, 3); ret != expected {
736- t.Fatalf("ReturnUnaligned1 returned %+v wanted %+v", ret, expected)
737- }
738- }
739- {
740- type Mixed1 struct {
741- a float32
742- b int32
743- }
744- var ReturnMixed1 func(a float32, b int32) Mixed1
745- purego.RegisterLibFunc(&ReturnMixed1, lib, "ReturnMixed1")
746- expected := Mixed1{1, 2}
747- if ret := ReturnMixed1(1, 2); ret != expected {
748- t.Fatalf("ReturnMixed1 returned %+v wanted %+v", ret, expected)
749- }
750- }
751- {
752- type Mixed2 struct {
753- a float32
754- b int32
755- c float32
756- d int32
757- }
758- var ReturnMixed2 func(a float32, b int32, c float32, d int32) Mixed2
759- purego.RegisterLibFunc(&ReturnMixed2, lib, "ReturnMixed2")
760- expected := Mixed2{1, 2, 3, 4}
761- if ret := ReturnMixed2(1, 2, 3, 4); ret != expected {
762- t.Fatalf("ReturnMixed2 returned %+v wanted %+v", ret, expected)
763- }
764- }
765- {
766- type Mixed3 struct {
767- a float32
768- b uint32
769- c float64
770- }
771- var ReturnMixed3 func(a float32, b uint32, c float64) Mixed3
772- purego.RegisterLibFunc(&ReturnMixed3, lib, "ReturnMixed3")
773- expected := Mixed3{1, 2, 3}
774- if ret := ReturnMixed3(1, 2, 3); ret != expected {
775- t.Fatalf("ReturnMixed3 returned %+v wanted %+v", ret, expected)
776- }
777- }
778- {
779- type Mixed4 struct {
780- a float64
781- b uint32
782- c float32
783- }
784- var ReturnMixed4 func(a float64, b uint32, c float32) Mixed4
785- purego.RegisterLibFunc(&ReturnMixed4, lib, "ReturnMixed4")
786- expected := Mixed4{1, 2, 3}
787- if ret := ReturnMixed4(1, 2, 3); ret != expected {
788- t.Fatalf("ReturnMixed4 returned %+v wanted %+v", ret, expected)
789- }
790- }
791- {
792- type Mixed5 struct {
793- a *int64
794- b int32
795- c float32
796- d int32
797- }
798- var ReturnMixed5 func(a *int64, b int32, c float32, d int32) Mixed5
799- purego.RegisterLibFunc(&ReturnMixed5, lib, "ReturnMixed5")
800- ptr := new(int64)
801- expected := Mixed5{ptr, 1, 7.2, 9}
802- if ret := ReturnMixed5(ptr, 1, 7.2, 9); ret != expected {
803- t.Fatalf("ReturnMixed5 returned %+v wanted %+v", ret, expected)
804- }
805- runtime.KeepAlive(ptr)
806- }
807- {
808- type Mixed5 struct {
809- a *int64
810- b int32
811- c float32
812- d int32
813- }
814- var IdentityMixed5 func(m Mixed5) Mixed5
815- purego.RegisterLibFunc(&IdentityMixed5, lib, "IdentityMixed5")
816- ptr := new(int64)
817- expected := Mixed5{ptr, 1, 7.2, 9}
818- if ret := IdentityMixed5(expected); ret != expected {
819- t.Fatalf("IdentityMixed5 returned %+v wanted %+v", ret, expected)
820- }
821- runtime.KeepAlive(ptr)
822- }
823- {
824- type SmallBool struct {
825- a bool
826- b int32
827- c int64
828- }
829- var ReturnSmallBool func(a bool, b int32, c int64) SmallBool
830- purego.RegisterLibFunc(&ReturnSmallBool, lib, "ReturnSmallBool")
831- expected := SmallBool{true, 42, 123456789}
832- if ret := ReturnSmallBool(true, 42, 123456789); ret != expected {
833- t.Fatalf("ReturnSmallBool returned %+v wanted %+v", ret, expected)
834- }
835- }
836- {
837- type LargeBool struct {
838- a bool
839- b int32
840- c int64
841- d int64
842- }
843- var ReturnLargeBool func(a bool, b int32, c int64, d int64) LargeBool
844- purego.RegisterLibFunc(&ReturnLargeBool, lib, "ReturnLargeBool")
845- expected := LargeBool{false, -99, 987654321, 111222333444}
846- if ret := ReturnLargeBool(false, -99, 987654321, 111222333444); ret != expected {
847- t.Fatalf("ReturnLargeBool returned %+v wanted %+v", ret, expected)
848- }
849- }
850- {
851- type Ptr1 struct {
852- a *int64
853- b unsafe.Pointer
854- }
855- var ReturnPtr1 func(a *int64, b unsafe.Pointer) Ptr1
856- purego.RegisterLibFunc(&ReturnPtr1, lib, "ReturnPtr1")
857- a, b := new(int64), new(struct{})
858- expected := Ptr1{a, unsafe.Pointer(b)}
859- if ret := ReturnPtr1(a, unsafe.Pointer(b)); ret != expected {
860- t.Fatalf("ReturnPtr1 returned %+v wanted %+v", ret, expected)
861- }
862- runtime.KeepAlive(a)
863- runtime.KeepAlive(b)
864 }944 }
865 }945 }
M sys_amd64.s +7 -2
@@ -140,7 +140,7 @@ TEXT callbackasm1(SB), NOSPLIT|NOFRAME, $0
140140 SUBQ $(24+callbackArgs__size), SP
141141 MOVQ AX, (24+callbackArgs_index)(SP) // callback index
142142 MOVQ R8, (24+callbackArgs_args)(SP) // address of args vector
143- MOVQ $0, (24+callbackArgs_result)(SP) // result
143+ MOVQ DI, (24+callbackArgs_result)(SP) // result
144144 LEAQ 24(SP), AX // take the address of callbackArgs
145145
146146 // Call cgocallback, which will call callbackWrap(frame).
@@ -153,7 +153,12 @@ TEXT callbackasm1(SB), NOSPLIT|NOFRAME, $0
153153
154154 // Get callback result.
155155 MOVQ (24+callbackArgs_result)(SP), AX
156- ADDQ $(24+callbackArgs__size), SP // remove callbackArgs struct
156+ MOVQ (24+callbackArgs_result+8)(SP), DX
157+ // Restore XMM0/XMM1 from result[2]/result[3] for struct eightbytes
158+ // classified as SSE by the SysV ABI.
159+ MOVQ (24+callbackArgs_result+16)(SP), X0
160+ MOVQ (24+callbackArgs_result+24)(SP), X1
161+ ADDQ $(24+callbackArgs__size), SP // remove callbackArgs struct
157162
158163 POP_REGS_HOST_TO_ABI0()
159164
@@ -140,7 +140,7 @@ TEXT callbackasm1(SB), NOSPLIT|NOFRAME, $0
140 SUBQ $(24+callbackArgs__size), SP140 SUBQ $(24+callbackArgs__size), SP
141 MOVQ AX, (24+callbackArgs_index)(SP) // callback index141 MOVQ AX, (24+callbackArgs_index)(SP) // callback index
142 MOVQ R8, (24+callbackArgs_args)(SP) // address of args vector142 MOVQ R8, (24+callbackArgs_args)(SP) // address of args vector
143- MOVQ $0, (24+callbackArgs_result)(SP) // result143+ MOVQ DI, (24+callbackArgs_result)(SP) // result
144 LEAQ 24(SP), AX // take the address of callbackArgs144 LEAQ 24(SP), AX // take the address of callbackArgs
145 145
146 // Call cgocallback, which will call callbackWrap(frame).146 // Call cgocallback, which will call callbackWrap(frame).
@@ -153,7 +153,12 @@ TEXT callbackasm1(SB), NOSPLIT|NOFRAME, $0
153 153
154 // Get callback result.154 // Get callback result.
155 MOVQ (24+callbackArgs_result)(SP), AX155 MOVQ (24+callbackArgs_result)(SP), AX
156- ADDQ $(24+callbackArgs__size), SP // remove callbackArgs struct156+ MOVQ (24+callbackArgs_result+8)(SP), DX
157+ // Restore XMM0/XMM1 from result[2]/result[3] for struct eightbytes
158+ // classified as SSE by the SysV ABI.
159+ MOVQ (24+callbackArgs_result+16)(SP), X0
160+ MOVQ (24+callbackArgs_result+24)(SP), X1
161+ ADDQ $(24+callbackArgs__size), SP // remove callbackArgs struct
157 162
158 POP_REGS_HOST_TO_ABI0()163 POP_REGS_HOST_TO_ABI0()
159 164
M sys_unix_arm64.s +8 -3
@@ -47,7 +47,7 @@ TEXT callbackasm1(SB), NOSPLIT|NOFRAME, $0
4747 MOVD $(callbackArgs__size)(RSP), R13
4848 MOVD R12, callbackArgs_index(R13) // callback index
4949 MOVD R14, callbackArgs_args(R13) // address of args vector
50- MOVD ZR, callbackArgs_result(R13) // result
50+ MOVD R8, callbackArgs_result(R13) // result
5151
5252 // Move parameters into registers
5353 // Get the ABIInternal function pointer
@@ -60,8 +60,13 @@ TEXT callbackasm1(SB), NOSPLIT|NOFRAME, $0
6060 BL crosscall2(SB)
6161
6262 // Get callback result.
63- MOVD $(callbackArgs__size)(RSP), R13
64- MOVD callbackArgs_result(R13), R0
63+ MOVD $(callbackArgs__size)(RSP), R13
64+ MOVD (callbackArgs_result+0)(R13), R0
65+ MOVD (callbackArgs_result+8)(R13), R1
66+ FMOVD (callbackArgs_result+0)(R13), F0
67+ FMOVD (callbackArgs_result+8)(R13), F1
68+ FMOVD (callbackArgs_result+16)(R13), F2
69+ FMOVD (callbackArgs_result+24)(R13), F3
6570
6671 // Restore LR and R27
6772 LDP 0(RSP), (R27, R30)
@@ -47,7 +47,7 @@ TEXT callbackasm1(SB), NOSPLIT|NOFRAME, $0
47 MOVD $(callbackArgs__size)(RSP), R1347 MOVD $(callbackArgs__size)(RSP), R13
48 MOVD R12, callbackArgs_index(R13) // callback index48 MOVD R12, callbackArgs_index(R13) // callback index
49 MOVD R14, callbackArgs_args(R13) // address of args vector49 MOVD R14, callbackArgs_args(R13) // address of args vector
50- MOVD ZR, callbackArgs_result(R13) // result50+ MOVD R8, callbackArgs_result(R13) // result
51 51
52 // Move parameters into registers52 // Move parameters into registers
53 // Get the ABIInternal function pointer53 // Get the ABIInternal function pointer
@@ -60,8 +60,13 @@ TEXT callbackasm1(SB), NOSPLIT|NOFRAME, $0
60 BL crosscall2(SB)60 BL crosscall2(SB)
61 61
62 // Get callback result.62 // Get callback result.
63- MOVD $(callbackArgs__size)(RSP), R1363+ MOVD $(callbackArgs__size)(RSP), R13
64- MOVD callbackArgs_result(R13), R064+ MOVD (callbackArgs_result+0)(R13), R0
65+ MOVD (callbackArgs_result+8)(R13), R1
66+ FMOVD (callbackArgs_result+0)(R13), F0
67+ FMOVD (callbackArgs_result+8)(R13), F1
68+ FMOVD (callbackArgs_result+16)(R13), F2
69+ FMOVD (callbackArgs_result+24)(R13), F3
65 70
66 // Restore LR and R2771 // Restore LR and R27
67 LDP 0(RSP), (R27, R30)72 LDP 0(RSP), (R27, R30)
M sys_unix_loong64.s +9 -5
@@ -30,7 +30,10 @@ TEXT callbackasm1(SB), NOSPLIT|NOFRAME, $0
3030 MOVV R11, 120(R14)
3131
3232 // Adjust SP by frame size.
33- SUBV $(22*8), R3
33+ // Layout: R1(8) + R30(8) + callbackArgs(48) = 64 bytes below R14,
34+ // plus 128 bytes of saved registers above = 192 = 24*8.
35+ // callbackArgs is placed at SP+16, ending at SP+64 = R14.
36+ SUBV $(24*8), R3
3437
3538 // It is important to save R30 because the go assembler
3639 // uses it for move instructions for a variable.
@@ -47,8 +50,9 @@ TEXT callbackasm1(SB), NOSPLIT|NOFRAME, $0
4750 MOVV R1, 0(R3)
4851 MOVV R30, 8(R3)
4952
50- // Create a struct callbackArgs on our stack.
51- MOVV $(callbackArgs__size)(R3), R13
53+ // Create a struct callbackArgs on our stack at SP+16
54+ // (right after the saved R1 and R30).
55+ MOVV $16(R3), R13
5256 MOVV R12, callbackArgs_index(R13) // callback index
5357 MOVV R14, callbackArgs_args(R13) // address of args vector
5458 MOVV $0, callbackArgs_result(R13) // result
@@ -64,12 +68,12 @@ TEXT callbackasm1(SB), NOSPLIT|NOFRAME, $0
6468 JAL crosscall2(SB)
6569
6670 // Get callback result.
67- MOVV $(callbackArgs__size)(R3), R13
71+ MOVV $16(R3), R13
6872 MOVV callbackArgs_result(R13), R4
6973
7074 // Restore LR and R30
7175 MOVV 0(R3), R1
7276 MOVV 8(R3), R30
73- ADDV $(22*8), R3
77+ ADDV $(24*8), R3
7478
7579 RET
@@ -30,7 +30,10 @@ TEXT callbackasm1(SB), NOSPLIT|NOFRAME, $0
30 MOVV R11, 120(R14)30 MOVV R11, 120(R14)
31 31
32 // Adjust SP by frame size.32 // Adjust SP by frame size.
33- SUBV $(22*8), R333+ // Layout: R1(8) + R30(8) + callbackArgs(48) = 64 bytes below R14,
34+ // plus 128 bytes of saved registers above = 192 = 24*8.
35+ // callbackArgs is placed at SP+16, ending at SP+64 = R14.
36+ SUBV $(24*8), R3
34 37
35 // It is important to save R30 because the go assembler38 // It is important to save R30 because the go assembler
36 // uses it for move instructions for a variable.39 // uses it for move instructions for a variable.
@@ -47,8 +50,9 @@ TEXT callbackasm1(SB), NOSPLIT|NOFRAME, $0
47 MOVV R1, 0(R3)50 MOVV R1, 0(R3)
48 MOVV R30, 8(R3)51 MOVV R30, 8(R3)
49 52
50- // Create a struct callbackArgs on our stack.53+ // Create a struct callbackArgs on our stack at SP+16
51- MOVV $(callbackArgs__size)(R3), R1354+ // (right after the saved R1 and R30).
55+ MOVV $16(R3), R13
52 MOVV R12, callbackArgs_index(R13) // callback index56 MOVV R12, callbackArgs_index(R13) // callback index
53 MOVV R14, callbackArgs_args(R13) // address of args vector57 MOVV R14, callbackArgs_args(R13) // address of args vector
54 MOVV $0, callbackArgs_result(R13) // result58 MOVV $0, callbackArgs_result(R13) // result
@@ -64,12 +68,12 @@ TEXT callbackasm1(SB), NOSPLIT|NOFRAME, $0
64 JAL crosscall2(SB)68 JAL crosscall2(SB)
65 69
66 // Get callback result.70 // Get callback result.
67- MOVV $(callbackArgs__size)(R3), R1371+ MOVV $16(R3), R13
68 MOVV callbackArgs_result(R13), R472 MOVV callbackArgs_result(R13), R4
69 73
70 // Restore LR and R3074 // Restore LR and R30
71 MOVV 0(R3), R175 MOVV 0(R3), R1
72 MOVV 8(R3), R3076 MOVV 8(R3), R30
73- ADDV $(22*8), R377+ ADDV $(24*8), R3
74 78
75 RET79 RET
M syscall_notstackargs.go +1 -1
@@ -29,7 +29,7 @@ type callbackArgs struct {
2929 // for this callback.
3030 args unsafe.Pointer
3131 // Below are out-args from callbackWrap
32- result uintptr
32+ result [4]uintptr
3333 }
3434
3535 func (c *callbackArgs) stackFrame() unsafe.Pointer {
@@ -29,7 +29,7 @@ type callbackArgs struct {
29 // for this callback.29 // for this callback.
30 args unsafe.Pointer30 args unsafe.Pointer
31 // Below are out-args from callbackWrap31 // Below are out-args from callbackWrap
32- result uintptr32+ result [4]uintptr
33 }33 }
34 34
35 func (c *callbackArgs) stackFrame() unsafe.Pointer {35 func (c *callbackArgs) stackFrame() unsafe.Pointer {
M syscall_stackargs.go +1 -1
@@ -29,7 +29,7 @@ type callbackArgs struct {
2929 // for this callback.
3030 args unsafe.Pointer
3131 // Below are out-args from callbackWrap
32- result uintptr
32+ result [1]uintptr
3333 // stackArgs points to stack-passed arguments for architectures where
3434 // they can't be made contiguous with register args (e.g., ppc64le).
3535 // On other architectures, this is nil and stack args are read from
@@ -29,7 +29,7 @@ type callbackArgs struct {
29 // for this callback.29 // for this callback.
30 args unsafe.Pointer30 args unsafe.Pointer
31 // Below are out-args from callbackWrap31 // Below are out-args from callbackWrap
32- result uintptr32+ result [1]uintptr
33 // stackArgs points to stack-passed arguments for architectures where33 // stackArgs points to stack-passed arguments for architectures where
34 // they can't be made contiguous with register args (e.g., ppc64le).34 // they can't be made contiguous with register args (e.g., ppc64le).
35 // On other architectures, this is nil and stack args are read from35 // On other architectures, this is nil and stack args are read from
M syscall_unix.go +18 -7
@@ -76,6 +76,7 @@ func compileCallback(fn any) uintptr {
7676 if i == 0 && in.AssignableTo(reflect.TypeOf(CDecl{})) {
7777 continue
7878 }
79+ ensureStructSupported()
7980 fallthrough
8081 case reflect.Interface, reflect.Func, reflect.Slice,
8182 reflect.Chan, reflect.Complex64, reflect.Complex128,
@@ -89,7 +90,7 @@ output:
8990 switch ty.Out(0).Kind() {
9091 case reflect.Pointer, reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
9192 reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr,
92- reflect.Bool, reflect.UnsafePointer:
93+ reflect.Bool, reflect.UnsafePointer, reflect.Struct:
9394 break output
9495 }
9596 panic("purego: unsupported return type: " + ty.String())
@@ -142,6 +143,14 @@ func callbackWrap(a *callbackArgs) {
142143 // This distinction matters on ARM32 where float64 uses 2 slots (32-bit registers).
143144 var floatsN int
144145 var intsN int
146+ // On amd64/loong64/ppc64le/riscv64/s390x, when returning a struct larger than
147+ // maxRegAllocStructSize, the caller passes a hidden pointer in the first integer
148+ // register. Skip it to avoid misreading it as the first function argument.
149+ if (runtime.GOARCH == "amd64" || runtime.GOARCH == "loong64" || runtime.GOARCH == "ppc64le" || runtime.GOARCH == "riscv64" || runtime.GOARCH == "s390x") &&
150+ fnType.NumOut() == 1 && fnType.Out(0).Kind() == reflect.Struct &&
151+ fnType.Out(0).Size() > maxRegAllocStructSize {
152+ intsN = 1
153+ }
145154 // stackSlot points to the index into frame (or stackFrame) of the current stack element.
146155 // When stackFrame is nil, stack begins after float and integer registers in frame.
147156 // When stackFrame is not nil (ppc64le), stackSlot indexes into stackFrame starting at 0.
@@ -225,19 +234,21 @@ func callbackWrap(a *callbackArgs) {
225234 if len(ret) > 0 {
226235 switch k := ret[0].Kind(); k {
227236 case reflect.Uint, reflect.Uint64, reflect.Uint32, reflect.Uint16, reflect.Uint8, reflect.Uintptr:
228- a.result = uintptr(ret[0].Uint())
237+ a.result[0] = uintptr(ret[0].Uint())
229238 case reflect.Int, reflect.Int64, reflect.Int32, reflect.Int16, reflect.Int8:
230- a.result = uintptr(ret[0].Int())
239+ a.result[0] = uintptr(ret[0].Int())
231240 case reflect.Bool:
232241 if ret[0].Bool() {
233- a.result = 1
242+ a.result[0] = 1
234243 } else {
235- a.result = 0
244+ a.result[0] = 0
236245 }
237246 case reflect.Pointer:
238- a.result = ret[0].Pointer()
247+ a.result[0] = ret[0].Pointer()
239248 case reflect.UnsafePointer:
240- a.result = ret[0].Pointer()
249+ a.result[0] = ret[0].Pointer()
250+ case reflect.Struct:
251+ setStruct(a, ret[0])
241252 default:
242253 panic("purego: unsupported kind: " + k.String())
243254 }
@@ -76,6 +76,7 @@ func compileCallback(fn any) uintptr {
76 if i == 0 && in.AssignableTo(reflect.TypeOf(CDecl{})) {76 if i == 0 && in.AssignableTo(reflect.TypeOf(CDecl{})) {
77 continue77 continue
78 }78 }
79+ ensureStructSupported()
79 fallthrough80 fallthrough
80 case reflect.Interface, reflect.Func, reflect.Slice,81 case reflect.Interface, reflect.Func, reflect.Slice,
81 reflect.Chan, reflect.Complex64, reflect.Complex128,82 reflect.Chan, reflect.Complex64, reflect.Complex128,
@@ -89,7 +90,7 @@ output:
89 switch ty.Out(0).Kind() {90 switch ty.Out(0).Kind() {
90 case reflect.Pointer, reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,91 case reflect.Pointer, reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
91 reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr,92 reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr,
92- reflect.Bool, reflect.UnsafePointer:93+ reflect.Bool, reflect.UnsafePointer, reflect.Struct:
93 break output94 break output
94 }95 }
95 panic("purego: unsupported return type: " + ty.String())96 panic("purego: unsupported return type: " + ty.String())
@@ -142,6 +143,14 @@ func callbackWrap(a *callbackArgs) {
142 // This distinction matters on ARM32 where float64 uses 2 slots (32-bit registers).143 // This distinction matters on ARM32 where float64 uses 2 slots (32-bit registers).
143 var floatsN int144 var floatsN int
144 var intsN int145 var intsN int
146+ // On amd64/loong64/ppc64le/riscv64/s390x, when returning a struct larger than
147+ // maxRegAllocStructSize, the caller passes a hidden pointer in the first integer
148+ // register. Skip it to avoid misreading it as the first function argument.
149+ if (runtime.GOARCH == "amd64" || runtime.GOARCH == "loong64" || runtime.GOARCH == "ppc64le" || runtime.GOARCH == "riscv64" || runtime.GOARCH == "s390x") &&
150+ fnType.NumOut() == 1 && fnType.Out(0).Kind() == reflect.Struct &&
151+ fnType.Out(0).Size() > maxRegAllocStructSize {
152+ intsN = 1
153+ }
145 // stackSlot points to the index into frame (or stackFrame) of the current stack element.154 // stackSlot points to the index into frame (or stackFrame) of the current stack element.
146 // When stackFrame is nil, stack begins after float and integer registers in frame.155 // When stackFrame is nil, stack begins after float and integer registers in frame.
147 // When stackFrame is not nil (ppc64le), stackSlot indexes into stackFrame starting at 0.156 // When stackFrame is not nil (ppc64le), stackSlot indexes into stackFrame starting at 0.
@@ -225,19 +234,21 @@ func callbackWrap(a *callbackArgs) {
225 if len(ret) > 0 {234 if len(ret) > 0 {
226 switch k := ret[0].Kind(); k {235 switch k := ret[0].Kind(); k {
227 case reflect.Uint, reflect.Uint64, reflect.Uint32, reflect.Uint16, reflect.Uint8, reflect.Uintptr:236 case reflect.Uint, reflect.Uint64, reflect.Uint32, reflect.Uint16, reflect.Uint8, reflect.Uintptr:
228- a.result = uintptr(ret[0].Uint())237+ a.result[0] = uintptr(ret[0].Uint())
229 case reflect.Int, reflect.Int64, reflect.Int32, reflect.Int16, reflect.Int8:238 case reflect.Int, reflect.Int64, reflect.Int32, reflect.Int16, reflect.Int8:
230- a.result = uintptr(ret[0].Int())239+ a.result[0] = uintptr(ret[0].Int())
231 case reflect.Bool:240 case reflect.Bool:
232 if ret[0].Bool() {241 if ret[0].Bool() {
233- a.result = 1242+ a.result[0] = 1
234 } else {243 } else {
235- a.result = 0244+ a.result[0] = 0
236 }245 }
237 case reflect.Pointer:246 case reflect.Pointer:
238- a.result = ret[0].Pointer()247+ a.result[0] = ret[0].Pointer()
239 case reflect.UnsafePointer:248 case reflect.UnsafePointer:
240- a.result = ret[0].Pointer()249+ a.result[0] = ret[0].Pointer()
250+ case reflect.Struct:
251+ setStruct(a, ret[0])
241 default:252 default:
242 panic("purego: unsupported kind: " + k.String())253 panic("purego: unsupported kind: " + k.String())
243 }254 }
M testdata/structtest/structreturn_test.c +10 -1
@@ -75,7 +75,7 @@ struct TwoFloats{
7575 };
7676
7777 struct TwoFloats ReturnTwoFloats(float a, float b) {
78- struct TwoFloats e = {a-b, a*b};
78+ struct TwoFloats e = {a, b};
7979 return e;
8080 }
8181
@@ -88,6 +88,15 @@ struct ThreeFloats ReturnThreeFloats(float a, float b, float c) {
8888 return e;
8989 }
9090
91+struct FourFloats{
92+ float a, b, c, d;
93+};
94+
95+struct FourFloats ReturnFourFloats(float a, float b, float c, float d) {
96+ struct FourFloats e = {a, b, c, d};
97+ return e;
98+}
99+
91100 struct OneFloat ReturnOneFloat(float a) {
92101 struct OneFloat e = {a};
93102 return e;
@@ -75,7 +75,7 @@ struct TwoFloats{
75 };75 };
76 76
77 struct TwoFloats ReturnTwoFloats(float a, float b) {77 struct TwoFloats ReturnTwoFloats(float a, float b) {
78- struct TwoFloats e = {a-b, a*b};78+ struct TwoFloats e = {a, b};
79 return e;79 return e;
80 }80 }
81 81
@@ -88,6 +88,15 @@ struct ThreeFloats ReturnThreeFloats(float a, float b, float c) {
88 return e;88 return e;
89 }89 }
90 90
91+struct FourFloats{
92+ float a, b, c, d;
93+};
94+
95+struct FourFloats ReturnFourFloats(float a, float b, float c, float d) {
96+ struct FourFloats e = {a, b, c, d};
97+ return e;
98+}
99+
91 struct OneFloat ReturnOneFloat(float a) {100 struct OneFloat ReturnOneFloat(float a) {
92 struct OneFloat e = {a};101 struct OneFloat e = {a};
93 return e;102 return e;

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 1

Tags 102

↑↓ moveenter openesc close