detached dd97be5

purego: add float32 and float64 support to callbacks (#120)

This commit adds support for float32 and float64 arguments in C callbacks. It also adds a test to check the maximum number of float32, float64 and mixed arguments a callback can have and that it is callback properly by purego. There is also some formatting done to the assembly.

It also fixes a bug in RegisterFunc which was converting float32 into float64 when the C function expected a float32 argument. This gave unpredictable results. It also makes the behavior of SyscallN more inline with what is expected when calling it with float arguments. It no longer expects them to be float64 and it is up to the caller to convert either float32 or float64 into uintptr using the math package. SyscallN will then properly move it into the correct register
commit
dd97be5f9f5776756eb0b02c25aa097ea27da1f9
author
TotallyGamerJet · · 2023-04-03 14:41 UTC
committer
GitHub · · 2023-04-03 14:41 UTC
parent
bfe9e39
signature
GPG Unverified4AEE18F83AFDEB23 The key is unknown
7 files · +171 -47
M callback_test.go +79 -0
@@ -72,3 +72,82 @@ func buildSharedLib(libFile string, sources ...string) error {
7272
7373 return nil
7474 }
75+
76+func TestNewCallbackFloat64(t *testing.T) {
77+ // This tests the maximum number of arguments a function to NewCallback can take
78+ const (
79+ expectCbTotal = -3
80+ expectedCbTotalF = float64(36)
81+ )
82+ var cbTotal int
83+ var cbTotalF float64
84+ imp := purego.NewCallback(func(a1, a2, a3, a4, a5, a6, a7, a8, a9 int,
85+ f1, f2, f3, f4, f5, f6, f7, f8 float64) {
86+ cbTotal = a1 + a2 + a3 + a4 + a5 + a6 + a7 + a8 + a9
87+ cbTotalF = f1 + f2 + f3 + f4 + f5 + f6 + f7 + f8
88+ })
89+ var fn func(a1, a2, a3, a4, a5, a6, a7, a8, a9 int,
90+ f1, f2, f3, f4, f5, f6, f7, f8 float64)
91+ purego.RegisterFunc(&fn, imp)
92+ fn(1, 2, -3, 4, -5, 6, -7, 8, -9,
93+ 1, 2, 3, 4, 5, 6, 7, 8)
94+
95+ if cbTotal != expectCbTotal {
96+ t.Errorf("cbTotal not correct got %d but wanted %d", cbTotal, expectCbTotal)
97+ }
98+ if cbTotalF != expectedCbTotalF {
99+ t.Errorf("cbTotalF not correct got %f but wanted %f", cbTotalF, expectedCbTotalF)
100+ }
101+}
102+
103+func TestNewCallbackFloat32(t *testing.T) {
104+ // This tests the maximum number of float32 arguments a function to NewCallback can take
105+ const (
106+ expectCbTotal = 6
107+ expectedCbTotalF = float32(45)
108+ )
109+ var cbTotal int
110+ var cbTotalF float32
111+ imp := purego.NewCallback(func(a1, a2, a3, a4, a5, a6, a7, a8 int,
112+ f1, f2, f3, f4, f5, f6, f7, f8, f9 float32) {
113+ cbTotal = a1 + a2 + a3 + a4 + a5 + a6 + a7 + a8
114+ cbTotalF = f1 + f2 + f3 + f4 + f5 + f6 + f7 + f8 + f9
115+ })
116+ var fn func(a1, a2, a3, a4, a5, a6, a7, a8 int,
117+ f1, f2, f3, f4, f5, f6, f7, f8, f9 float32)
118+ purego.RegisterFunc(&fn, imp)
119+ fn(1, 2, -3, 4, -5, 6, -7, 8,
120+ 1, 2, 3, 4, 5, 6, 7, 8, 9)
121+
122+ if cbTotal != expectCbTotal {
123+ t.Errorf("cbTotal not correct got %d but wanted %d", cbTotal, expectCbTotal)
124+ }
125+ if cbTotalF != expectedCbTotalF {
126+ t.Errorf("cbTotalF not correct got %f but wanted %f", cbTotalF, expectedCbTotalF)
127+ }
128+}
129+
130+func TestNewCallbackFloat32AndFloat64(t *testing.T) {
131+ // This tests that calling a function with a mix of float32 and float64 arguments works
132+ const (
133+ expectedCbTotalF32 = float32(30)
134+ expectedCbTotalF64 = float64(15)
135+ )
136+ var cbTotalF32 float32
137+ var cbTotalF64 float64
138+ imp := purego.NewCallback(func(f1, f2, f3 float32, f4, f5, f6 float64, f7, f8, f9 float32) {
139+ cbTotalF32 = f1 + f2 + f3 + f7 + f8 + f9
140+ cbTotalF64 = f4 + f5 + f6
141+
142+ })
143+ var fn func(f1, f2, f3 float32, f4, f5, f6 float64, f7, f8, f9 float32)
144+ purego.RegisterFunc(&fn, imp)
145+ fn(1, 2, 3, 4, 5, 6, 7, 8, 9)
146+
147+ if cbTotalF32 != expectedCbTotalF32 {
148+ t.Errorf("cbTotalF32 not correct got %f but wanted %f", cbTotalF32, expectedCbTotalF32)
149+ }
150+ if cbTotalF64 != expectedCbTotalF64 {
151+ t.Errorf("cbTotalF64 not correct got %f but wanted %f", cbTotalF64, expectedCbTotalF64)
152+ }
153+}
@@ -72,3 +72,82 @@ func buildSharedLib(libFile string, sources ...string) error {
72 72
73 return nil73 return nil
74 }74 }
75+
76+func TestNewCallbackFloat64(t *testing.T) {
77+ // This tests the maximum number of arguments a function to NewCallback can take
78+ const (
79+ expectCbTotal = -3
80+ expectedCbTotalF = float64(36)
81+ )
82+ var cbTotal int
83+ var cbTotalF float64
84+ imp := purego.NewCallback(func(a1, a2, a3, a4, a5, a6, a7, a8, a9 int,
85+ f1, f2, f3, f4, f5, f6, f7, f8 float64) {
86+ cbTotal = a1 + a2 + a3 + a4 + a5 + a6 + a7 + a8 + a9
87+ cbTotalF = f1 + f2 + f3 + f4 + f5 + f6 + f7 + f8
88+ })
89+ var fn func(a1, a2, a3, a4, a5, a6, a7, a8, a9 int,
90+ f1, f2, f3, f4, f5, f6, f7, f8 float64)
91+ purego.RegisterFunc(&fn, imp)
92+ fn(1, 2, -3, 4, -5, 6, -7, 8, -9,
93+ 1, 2, 3, 4, 5, 6, 7, 8)
94+
95+ if cbTotal != expectCbTotal {
96+ t.Errorf("cbTotal not correct got %d but wanted %d", cbTotal, expectCbTotal)
97+ }
98+ if cbTotalF != expectedCbTotalF {
99+ t.Errorf("cbTotalF not correct got %f but wanted %f", cbTotalF, expectedCbTotalF)
100+ }
101+}
102+
103+func TestNewCallbackFloat32(t *testing.T) {
104+ // This tests the maximum number of float32 arguments a function to NewCallback can take
105+ const (
106+ expectCbTotal = 6
107+ expectedCbTotalF = float32(45)
108+ )
109+ var cbTotal int
110+ var cbTotalF float32
111+ imp := purego.NewCallback(func(a1, a2, a3, a4, a5, a6, a7, a8 int,
112+ f1, f2, f3, f4, f5, f6, f7, f8, f9 float32) {
113+ cbTotal = a1 + a2 + a3 + a4 + a5 + a6 + a7 + a8
114+ cbTotalF = f1 + f2 + f3 + f4 + f5 + f6 + f7 + f8 + f9
115+ })
116+ var fn func(a1, a2, a3, a4, a5, a6, a7, a8 int,
117+ f1, f2, f3, f4, f5, f6, f7, f8, f9 float32)
118+ purego.RegisterFunc(&fn, imp)
119+ fn(1, 2, -3, 4, -5, 6, -7, 8,
120+ 1, 2, 3, 4, 5, 6, 7, 8, 9)
121+
122+ if cbTotal != expectCbTotal {
123+ t.Errorf("cbTotal not correct got %d but wanted %d", cbTotal, expectCbTotal)
124+ }
125+ if cbTotalF != expectedCbTotalF {
126+ t.Errorf("cbTotalF not correct got %f but wanted %f", cbTotalF, expectedCbTotalF)
127+ }
128+}
129+
130+func TestNewCallbackFloat32AndFloat64(t *testing.T) {
131+ // This tests that calling a function with a mix of float32 and float64 arguments works
132+ const (
133+ expectedCbTotalF32 = float32(30)
134+ expectedCbTotalF64 = float64(15)
135+ )
136+ var cbTotalF32 float32
137+ var cbTotalF64 float64
138+ imp := purego.NewCallback(func(f1, f2, f3 float32, f4, f5, f6 float64, f7, f8, f9 float32) {
139+ cbTotalF32 = f1 + f2 + f3 + f7 + f8 + f9
140+ cbTotalF64 = f4 + f5 + f6
141+
142+ })
143+ var fn func(f1, f2, f3 float32, f4, f5, f6 float64, f7, f8, f9 float32)
144+ purego.RegisterFunc(&fn, imp)
145+ fn(1, 2, 3, 4, 5, 6, 7, 8, 9)
146+
147+ if cbTotalF32 != expectedCbTotalF32 {
148+ t.Errorf("cbTotalF32 not correct got %f but wanted %f", cbTotalF32, expectedCbTotalF32)
149+ }
150+ if cbTotalF64 != expectedCbTotalF64 {
151+ t.Errorf("cbTotalF64 not correct got %f but wanted %f", cbTotalF64, expectedCbTotalF64)
152+ }
153+}
M func.go +13 -5
@@ -99,7 +99,7 @@ func RegisterFunc(fptr interface{}, cfn uintptr) {
9999 stack++
100100 }
101101 case reflect.Float32, reflect.Float64:
102- if floats < 8 {
102+ if floats < numOfFloats {
103103 floats++
104104 } else {
105105 stack++
@@ -108,7 +108,8 @@ func RegisterFunc(fptr interface{}, cfn uintptr) {
108108 panic("purego: unsupported kind " + arg.Kind().String())
109109 }
110110 }
111- if ints+stack > maxArgs || floats+stack > maxArgs {
111+ sizeOfStack := maxArgs - numOfIntegerRegisters()
112+ if stack > sizeOfStack {
112113 panic("purego: too many arguments")
113114 }
114115 }
@@ -127,7 +128,7 @@ func RegisterFunc(fptr interface{}, cfn uintptr) {
127128 }
128129 var sysargs [maxArgs]uintptr
129130 var stack = sysargs[numOfIntegerRegisters():]
130- var floats [8]float64
131+ var floats [numOfFloats]uintptr
131132 var numInts int
132133 var numFloats int
133134 var numStack int
@@ -168,9 +169,16 @@ func RegisterFunc(fptr interface{}, cfn uintptr) {
168169 } else {
169170 addInt(0)
170171 }
171- case reflect.Float32, reflect.Float64:
172+ case reflect.Float32:
173+ if numFloats < len(floats) {
174+ floats[numFloats] = uintptr(math.Float32bits(float32(v.Float())))
175+ numFloats++
176+ } else {
177+ addStack(uintptr(math.Float32bits(float32(v.Float()))))
178+ }
179+ case reflect.Float64:
172180 if numFloats < len(floats) {
173- floats[numFloats] = v.Float()
181+ floats[numFloats] = uintptr(math.Float64bits(v.Float()))
174182 numFloats++
175183 } else {
176184 addStack(uintptr(math.Float64bits(v.Float())))
@@ -99,7 +99,7 @@ func RegisterFunc(fptr interface{}, cfn uintptr) {
99 stack++99 stack++
100 }100 }
101 case reflect.Float32, reflect.Float64:101 case reflect.Float32, reflect.Float64:
102- if floats < 8 {102+ if floats < numOfFloats {
103 floats++103 floats++
104 } else {104 } else {
105 stack++105 stack++
@@ -108,7 +108,8 @@ func RegisterFunc(fptr interface{}, cfn uintptr) {
108 panic("purego: unsupported kind " + arg.Kind().String())108 panic("purego: unsupported kind " + arg.Kind().String())
109 }109 }
110 }110 }
111- if ints+stack > maxArgs || floats+stack > maxArgs {111+ sizeOfStack := maxArgs - numOfIntegerRegisters()
112+ if stack > sizeOfStack {
112 panic("purego: too many arguments")113 panic("purego: too many arguments")
113 }114 }
114 }115 }
@@ -127,7 +128,7 @@ func RegisterFunc(fptr interface{}, cfn uintptr) {
127 }128 }
128 var sysargs [maxArgs]uintptr129 var sysargs [maxArgs]uintptr
129 var stack = sysargs[numOfIntegerRegisters():]130 var stack = sysargs[numOfIntegerRegisters():]
130- var floats [8]float64131+ var floats [numOfFloats]uintptr
131 var numInts int132 var numInts int
132 var numFloats int133 var numFloats int
133 var numStack int134 var numStack int
@@ -168,9 +169,16 @@ func RegisterFunc(fptr interface{}, cfn uintptr) {
168 } else {169 } else {
169 addInt(0)170 addInt(0)
170 }171 }
171- case reflect.Float32, reflect.Float64:172+ case reflect.Float32:
173+ if numFloats < len(floats) {
174+ floats[numFloats] = uintptr(math.Float32bits(float32(v.Float())))
175+ numFloats++
176+ } else {
177+ addStack(uintptr(math.Float32bits(float32(v.Float()))))
178+ }
179+ case reflect.Float64:
172 if numFloats < len(floats) {180 if numFloats < len(floats) {
173- floats[numFloats] = v.Float()181+ floats[numFloats] = uintptr(math.Float64bits(v.Float()))
174 numFloats++182 numFloats++
175 } else {183 } else {
176 addStack(uintptr(math.Float64bits(v.Float())))184 addStack(uintptr(math.Float64bits(v.Float())))
M sys_amd64.s +18 -10
@@ -80,15 +80,23 @@ TEXT callbackasm1(SB), NOSPLIT, $0
8080
8181 MOVQ 0(SP), R10 // get the return SP so that we can align register args with stack args
8282
83- // make space for first six arguments below the frame
84- ADJSP $6*8, SP
85- MOVQ DI, 8(SP)
86- MOVQ SI, 16(SP)
87- MOVQ DX, 24(SP)
88- MOVQ CX, 32(SP)
89- MOVQ R8, 40(SP)
90- MOVQ R9, 48(SP)
91- LEAQ 8(SP), R8 // R8 = address of args vector
83+ // make space for first six int and 8 float arguments below the frame
84+ ADJSP $14*8, SP
85+ MOVSD X0, (1*8)(SP)
86+ MOVSD X1, (2*8)(SP)
87+ MOVSD X2, (3*8)(SP)
88+ MOVSD X3, (4*8)(SP)
89+ MOVSD X4, (5*8)(SP)
90+ MOVSD X5, (6*8)(SP)
91+ MOVSD X6, (7*8)(SP)
92+ MOVSD X7, (8*8)(SP)
93+ MOVQ DI, (9*8)(SP)
94+ MOVQ SI, (10*8)(SP)
95+ MOVQ DX, (11*8)(SP)
96+ MOVQ CX, (12*8)(SP)
97+ MOVQ R8, (13*8)(SP)
98+ MOVQ R9, (14*8)(SP)
99+ LEAQ 8(SP), R8 // R8 = address of args vector
92100
93101 MOVQ R10, 0(SP) // push the stack pointer below registers
94102
@@ -128,7 +136,7 @@ TEXT callbackasm1(SB), NOSPLIT, $0
128136
129137 MOVQ 0(SP), R10 // get the SP back
130138
131- ADJSP $-6*8, SP // remove arguments
139+ ADJSP $-14*8, SP // remove arguments
132140
133141 MOVQ R10, 0(SP)
134142
@@ -80,15 +80,23 @@ TEXT callbackasm1(SB), NOSPLIT, $0
80 80
81 MOVQ 0(SP), R10 // get the return SP so that we can align register args with stack args81 MOVQ 0(SP), R10 // get the return SP so that we can align register args with stack args
82 82
83- // make space for first six arguments below the frame83+ // make space for first six int and 8 float arguments below the frame
84- ADJSP $6*8, SP84+ ADJSP $14*8, SP
85- MOVQ DI, 8(SP)85+ MOVSD X0, (1*8)(SP)
86- MOVQ SI, 16(SP)86+ MOVSD X1, (2*8)(SP)
87- MOVQ DX, 24(SP)87+ MOVSD X2, (3*8)(SP)
88- MOVQ CX, 32(SP)88+ MOVSD X3, (4*8)(SP)
89- MOVQ R8, 40(SP)89+ MOVSD X4, (5*8)(SP)
90- MOVQ R9, 48(SP)90+ MOVSD X5, (6*8)(SP)
91- LEAQ 8(SP), R8 // R8 = address of args vector91+ MOVSD X6, (7*8)(SP)
92+ MOVSD X7, (8*8)(SP)
93+ MOVQ DI, (9*8)(SP)
94+ MOVQ SI, (10*8)(SP)
95+ MOVQ DX, (11*8)(SP)
96+ MOVQ CX, (12*8)(SP)
97+ MOVQ R8, (13*8)(SP)
98+ MOVQ R9, (14*8)(SP)
99+ LEAQ 8(SP), R8 // R8 = address of args vector
92 100
93 MOVQ R10, 0(SP) // push the stack pointer below registers101 MOVQ R10, 0(SP) // push the stack pointer below registers
94 102
@@ -128,7 +136,7 @@ TEXT callbackasm1(SB), NOSPLIT, $0
128 136
129 MOVQ 0(SP), R10 // get the SP back137 MOVQ 0(SP), R10 // get the SP back
130 138
131- ADJSP $-6*8, SP // remove arguments139+ ADJSP $-14*8, SP // remove arguments
132 140
133 MOVQ R10, 0(SP)141 MOVQ R10, 0(SP)
134 142
M sys_arm64.s +21 -13
@@ -69,26 +69,34 @@ TEXT callbackasm1(SB), NOSPLIT|NOFRAME, $0
6969 // On entry, the trampoline in zcallback_darwin_arm64.s left
7070 // the callback index in R12 (which is volatile in the C ABI).
7171
72- // Save callback register arguments R0-R7.
72+ // Save callback register arguments R0-R7 and F0-F7.
7373 // We do this at the top of the frame so they're contiguous with stack arguments.
74- SUB $(8*8), RSP, R14
75- STP (R0, R1), (0*8)(R14)
76- STP (R2, R3), (2*8)(R14)
77- STP (R4, R5), (4*8)(R14)
78- STP (R6, R7), (6*8)(R14)
74+ SUB $(16*8), RSP, R14
75+ FMOVD F0, (0*8)(R14)
76+ FMOVD F1, (1*8)(R14)
77+ FMOVD F2, (2*8)(R14)
78+ FMOVD F3, (3*8)(R14)
79+ FMOVD F4, (4*8)(R14)
80+ FMOVD F5, (5*8)(R14)
81+ FMOVD F6, (6*8)(R14)
82+ FMOVD F7, (7*8)(R14)
83+ STP (R0, R1), (8*8)(R14)
84+ STP (R2, R3), (10*8)(R14)
85+ STP (R4, R5), (12*8)(R14)
86+ STP (R6, R7), (14*8)(R14)
7987
8088 // Adjust SP by frame size.
8189 // crosscall2 clobbers FP in the frame record so only save/restore SP.
82- SUB $(28*8), RSP
90+ SUB $(28*8), RSP
8391 MOVD R30, (RSP)
8492
8593 // Create a struct callbackArgs on our stack.
86- ADD $(callbackArgs__size + 3*8), RSP, R13
87- MOVD R12, callbackArgs_index(R13) // callback index
94+ ADD $(callbackArgs__size + 3*8), RSP, R13
95+ MOVD R12, callbackArgs_index(R13) // callback index
8896 MOVD R14, R0
89- MOVD R0, callbackArgs_args(R13) // address of args vector
97+ MOVD R0, callbackArgs_args(R13) // address of args vector
9098 MOVD $0, R0
91- MOVD R0, callbackArgs_result(R13) // result
99+ MOVD R0, callbackArgs_result(R13) // result
92100
93101 // Move parameters into registers
94102 // Get the ABIInternal function pointer
@@ -101,11 +109,11 @@ TEXT callbackasm1(SB), NOSPLIT|NOFRAME, $0
101109 BL crosscall2(SB)
102110
103111 // Get callback result.
104- ADD $(callbackArgs__size + 3*8), RSP, R13
112+ ADD $(callbackArgs__size + 3*8), RSP, R13
105113 MOVD callbackArgs_result(R13), R0
106114
107115 // Restore SP
108116 MOVD (RSP), R30
109- ADD $(28*8), RSP
117+ ADD $(28*8), RSP
110118
111119 RET
@@ -69,26 +69,34 @@ TEXT callbackasm1(SB), NOSPLIT|NOFRAME, $0
69 // On entry, the trampoline in zcallback_darwin_arm64.s left69 // On entry, the trampoline in zcallback_darwin_arm64.s left
70 // the callback index in R12 (which is volatile in the C ABI).70 // the callback index in R12 (which is volatile in the C ABI).
71 71
72- // Save callback register arguments R0-R7.72+ // Save callback register arguments R0-R7 and F0-F7.
73 // We do this at the top of the frame so they're contiguous with stack arguments.73 // We do this at the top of the frame so they're contiguous with stack arguments.
74- SUB $(8*8), RSP, R1474+ SUB $(16*8), RSP, R14
75- STP (R0, R1), (0*8)(R14)75+ FMOVD F0, (0*8)(R14)
76- STP (R2, R3), (2*8)(R14)76+ FMOVD F1, (1*8)(R14)
77- STP (R4, R5), (4*8)(R14)77+ FMOVD F2, (2*8)(R14)
78- STP (R6, R7), (6*8)(R14)78+ FMOVD F3, (3*8)(R14)
79+ FMOVD F4, (4*8)(R14)
80+ FMOVD F5, (5*8)(R14)
81+ FMOVD F6, (6*8)(R14)
82+ FMOVD F7, (7*8)(R14)
83+ STP (R0, R1), (8*8)(R14)
84+ STP (R2, R3), (10*8)(R14)
85+ STP (R4, R5), (12*8)(R14)
86+ STP (R6, R7), (14*8)(R14)
79 87
80 // Adjust SP by frame size.88 // Adjust SP by frame size.
81 // crosscall2 clobbers FP in the frame record so only save/restore SP.89 // crosscall2 clobbers FP in the frame record so only save/restore SP.
82- SUB $(28*8), RSP90+ SUB $(28*8), RSP
83 MOVD R30, (RSP)91 MOVD R30, (RSP)
84 92
85 // Create a struct callbackArgs on our stack.93 // Create a struct callbackArgs on our stack.
86- ADD $(callbackArgs__size + 3*8), RSP, R1394+ ADD $(callbackArgs__size + 3*8), RSP, R13
87- MOVD R12, callbackArgs_index(R13) // callback index95+ MOVD R12, callbackArgs_index(R13) // callback index
88 MOVD R14, R096 MOVD R14, R0
89- MOVD R0, callbackArgs_args(R13) // address of args vector97+ MOVD R0, callbackArgs_args(R13) // address of args vector
90 MOVD $0, R098 MOVD $0, R0
91- MOVD R0, callbackArgs_result(R13) // result99+ MOVD R0, callbackArgs_result(R13) // result
92 100
93 // Move parameters into registers101 // Move parameters into registers
94 // Get the ABIInternal function pointer102 // Get the ABIInternal function pointer
@@ -101,11 +109,11 @@ TEXT callbackasm1(SB), NOSPLIT|NOFRAME, $0
101 BL crosscall2(SB)109 BL crosscall2(SB)
102 110
103 // Get callback result.111 // Get callback result.
104- ADD $(callbackArgs__size + 3*8), RSP, R13112+ ADD $(callbackArgs__size + 3*8), RSP, R13
105 MOVD callbackArgs_result(R13), R0113 MOVD callbackArgs_result(R13), R0
106 114
107 // Restore SP115 // Restore SP
108 MOVD (RSP), R30116 MOVD (RSP), R30
109- ADD $(28*8), RSP117+ ADD $(28*8), RSP
110 118
111 RET119 RET
M syscall.go +4 -1
@@ -5,7 +5,10 @@
55
66 package purego
77
8-const maxArgs = 9
8+const (
9+ maxArgs = 9
10+ numOfFloats = 8 // arm64 and amd64 both have 8 float registers
11+)
912
1013 // SyscallN takes fn, a C function pointer and a list of arguments as uintptr.
1114 // There is an internal maximum number of arguments that SyscallN can take. It panics
@@ -5,7 +5,10 @@
5 5
6 package purego6 package purego
7 7
8-const maxArgs = 98+const (
9+ maxArgs = 9
10+ numOfFloats = 8 // arm64 and amd64 both have 8 float registers
11+)
9 12
10 // SyscallN takes fn, a C function pointer and a list of arguments as uintptr.13 // SyscallN takes fn, a C function pointer and a list of arguments as uintptr.
11 // There is an internal maximum number of arguments that SyscallN can take. It panics14 // There is an internal maximum number of arguments that SyscallN can take. It panics
M syscall_cgo_linux.go +1 -1
@@ -16,7 +16,7 @@ var syscall9XABI0 = uintptr(cgo.Syscall9XABI0)
1616 // this is only here to make the assembly files happy :)
1717 type syscall9Args struct {
1818 fn, a1, a2, a3, a4, a5, a6, a7, a8, a9 uintptr
19- f1, f2, f3, f4, f5, f6, f7, f8 float64
19+ f1, f2, f3, f4, f5, f6, f7, f8 uintptr
2020 r1, r2, err uintptr
2121 }
2222
@@ -16,7 +16,7 @@ var syscall9XABI0 = uintptr(cgo.Syscall9XABI0)
16 // this is only here to make the assembly files happy :)16 // this is only here to make the assembly files happy :)
17 type syscall9Args struct {17 type syscall9Args struct {
18 fn, a1, a2, a3, a4, a5, a6, a7, a8, a9 uintptr18 fn, a1, a2, a3, a4, a5, a6, a7, a8, a9 uintptr
19- f1, f2, f3, f4, f5, f6, f7, f8 float6419+ f1, f2, f3, f4, f5, f6, f7, f8 uintptr
20 r1, r2, err uintptr20 r1, r2, err uintptr
21 }21 }
22 22
M syscall_sysv.go +35 -17
@@ -6,7 +6,6 @@
66 package purego
77
88 import (
9- "math"
109 "reflect"
1110 "runtime"
1211 "sync"
@@ -17,16 +16,14 @@ var syscall9XABI0 uintptr
1716
1817 type syscall9Args struct {
1918 fn, a1, a2, a3, a4, a5, a6, a7, a8, a9 uintptr
20- f1, f2, f3, f4, f5, f6, f7, f8 float64
19+ f1, f2, f3, f4, f5, f6, f7, f8 uintptr
2120 r1, r2, err uintptr
2221 }
2322
2423 //go:nosplit
2524 func syscall_syscall9X(fn, a1, a2, a3, a4, a5, a6, a7, a8, a9 uintptr) (r1, r2, err uintptr) {
2625 args := syscall9Args{fn, a1, a2, a3, a4, a5, a6, a7, a8, a9,
27- math.Float64frombits(uint64(a1)), math.Float64frombits(uint64(a2)), math.Float64frombits(uint64(a3)),
28- math.Float64frombits(uint64(a4)), math.Float64frombits(uint64(a5)), math.Float64frombits(uint64(a6)),
29- math.Float64frombits(uint64(a7)), math.Float64frombits(uint64(a8)),
26+ a1, a2, a3, a4, a5, a6, a7, a8,
3027 r1, r2, err}
3128 runtime_cgocall(syscall9XABI0, unsafe.Pointer(&args))
3229 return args.r1, args.r2, args.err
@@ -61,15 +58,13 @@ type callbackArgs struct {
6158 index uintptr
6259 // args points to the argument block.
6360 //
64- // For cdecl and stdcall, all arguments are on the stack.
61+ // The structure of the arguments goes
62+ // float registers followed by the
63+ // integer registers followed by the stack.
6564 //
66- // For fastcall, the trampoline spills register arguments to
67- // the reserved spill slots below the stack arguments,
68- // resulting in a layout equivalent to stdcall.
69- //
70- // For arm, the trampoline stores the register arguments just
71- // below the stack arguments, so again we can treat it as one
72- // big stack arguments frame.
65+ // This variable is treated as a continuous
66+ // block of memory containing all of the arguments
67+ // for this callback.
7368 args unsafe.Pointer
7469 // Below are out-args from callbackWrap
7570 result uintptr
@@ -84,8 +79,7 @@ func compileCallback(fn interface{}) uintptr {
8479 for i := 0; i < ty.NumIn(); i++ {
8580 in := ty.In(i)
8681 switch in.Kind() {
87- case reflect.Struct, reflect.Float32, reflect.Float64,
88- reflect.Interface, reflect.Func, reflect.Slice,
82+ case reflect.Struct, reflect.Interface, reflect.Func, reflect.Slice,
8983 reflect.Chan, reflect.Complex64, reflect.Complex128,
9084 reflect.String, reflect.Map, reflect.Invalid:
9185 panic("purego: unsupported argument type: " + in.Kind().String())
@@ -136,9 +130,33 @@ func callbackWrap(a *callbackArgs) {
136130 fnType := fn.Type()
137131 args := make([]reflect.Value, fnType.NumIn())
138132 frame := (*[callbackMaxFrame]uintptr)(a.args)
133+ var floatsN int // floatsN represents the number of float arguments processed
134+ var intsN int // intsN represents the number of integer arguments processed
135+ // stack points to the index into frame of the current stack element.
136+ // The stack begins after the float and integer registers.
137+ var stack = numOfIntegerRegisters() + numOfFloats
139138 for i := range args {
140- //TODO: support float32 and float64
141- args[i] = reflect.NewAt(fnType.In(i), unsafe.Pointer(&frame[i])).Elem()
139+ var pos int
140+ switch fnType.In(i).Kind() {
141+ case reflect.Float32, reflect.Float64:
142+ if floatsN >= numOfFloats {
143+ pos = stack
144+ stack++
145+ } else {
146+ pos = floatsN
147+ }
148+ floatsN++
149+ default:
150+ if intsN >= numOfIntegerRegisters() {
151+ pos = stack
152+ stack++
153+ } else {
154+ // the integers begin after the floats in frame
155+ pos = intsN + numOfFloats
156+ }
157+ intsN++
158+ }
159+ args[i] = reflect.NewAt(fnType.In(i), unsafe.Pointer(&frame[pos])).Elem()
142160 }
143161 ret := fn.Call(args)
144162 if len(ret) > 0 {
@@ -6,7 +6,6 @@
6 package purego6 package purego
7 7
8 import (8 import (
9- "math"
10 "reflect"9 "reflect"
11 "runtime"10 "runtime"
12 "sync"11 "sync"
@@ -17,16 +16,14 @@ var syscall9XABI0 uintptr
17 16
18 type syscall9Args struct {17 type syscall9Args struct {
19 fn, a1, a2, a3, a4, a5, a6, a7, a8, a9 uintptr18 fn, a1, a2, a3, a4, a5, a6, a7, a8, a9 uintptr
20- f1, f2, f3, f4, f5, f6, f7, f8 float6419+ f1, f2, f3, f4, f5, f6, f7, f8 uintptr
21 r1, r2, err uintptr20 r1, r2, err uintptr
22 }21 }
23 22
24 //go:nosplit23 //go:nosplit
25 func syscall_syscall9X(fn, a1, a2, a3, a4, a5, a6, a7, a8, a9 uintptr) (r1, r2, err uintptr) {24 func syscall_syscall9X(fn, a1, a2, a3, a4, a5, a6, a7, a8, a9 uintptr) (r1, r2, err uintptr) {
26 args := syscall9Args{fn, a1, a2, a3, a4, a5, a6, a7, a8, a9,25 args := syscall9Args{fn, a1, a2, a3, a4, a5, a6, a7, a8, a9,
27- math.Float64frombits(uint64(a1)), math.Float64frombits(uint64(a2)), math.Float64frombits(uint64(a3)),26+ a1, a2, a3, a4, a5, a6, a7, a8,
28- math.Float64frombits(uint64(a4)), math.Float64frombits(uint64(a5)), math.Float64frombits(uint64(a6)),
29- math.Float64frombits(uint64(a7)), math.Float64frombits(uint64(a8)),
30 r1, r2, err}27 r1, r2, err}
31 runtime_cgocall(syscall9XABI0, unsafe.Pointer(&args))28 runtime_cgocall(syscall9XABI0, unsafe.Pointer(&args))
32 return args.r1, args.r2, args.err29 return args.r1, args.r2, args.err
@@ -61,15 +58,13 @@ type callbackArgs struct {
61 index uintptr58 index uintptr
62 // args points to the argument block.59 // args points to the argument block.
63 //60 //
64- // For cdecl and stdcall, all arguments are on the stack.61+ // The structure of the arguments goes
62+ // float registers followed by the
63+ // integer registers followed by the stack.
65 //64 //
66- // For fastcall, the trampoline spills register arguments to65+ // This variable is treated as a continuous
67- // the reserved spill slots below the stack arguments,66+ // block of memory containing all of the arguments
68- // resulting in a layout equivalent to stdcall.67+ // for this callback.
69- //
70- // For arm, the trampoline stores the register arguments just
71- // below the stack arguments, so again we can treat it as one
72- // big stack arguments frame.
73 args unsafe.Pointer68 args unsafe.Pointer
74 // Below are out-args from callbackWrap69 // Below are out-args from callbackWrap
75 result uintptr70 result uintptr
@@ -84,8 +79,7 @@ func compileCallback(fn interface{}) uintptr {
84 for i := 0; i < ty.NumIn(); i++ {79 for i := 0; i < ty.NumIn(); i++ {
85 in := ty.In(i)80 in := ty.In(i)
86 switch in.Kind() {81 switch in.Kind() {
87- case reflect.Struct, reflect.Float32, reflect.Float64,82+ case reflect.Struct, reflect.Interface, reflect.Func, reflect.Slice,
88- reflect.Interface, reflect.Func, reflect.Slice,
89 reflect.Chan, reflect.Complex64, reflect.Complex128,83 reflect.Chan, reflect.Complex64, reflect.Complex128,
90 reflect.String, reflect.Map, reflect.Invalid:84 reflect.String, reflect.Map, reflect.Invalid:
91 panic("purego: unsupported argument type: " + in.Kind().String())85 panic("purego: unsupported argument type: " + in.Kind().String())
@@ -136,9 +130,33 @@ func callbackWrap(a *callbackArgs) {
136 fnType := fn.Type()130 fnType := fn.Type()
137 args := make([]reflect.Value, fnType.NumIn())131 args := make([]reflect.Value, fnType.NumIn())
138 frame := (*[callbackMaxFrame]uintptr)(a.args)132 frame := (*[callbackMaxFrame]uintptr)(a.args)
133+ var floatsN int // floatsN represents the number of float arguments processed
134+ var intsN int // intsN represents the number of integer arguments processed
135+ // stack points to the index into frame of the current stack element.
136+ // The stack begins after the float and integer registers.
137+ var stack = numOfIntegerRegisters() + numOfFloats
139 for i := range args {138 for i := range args {
140- //TODO: support float32 and float64139+ var pos int
141- args[i] = reflect.NewAt(fnType.In(i), unsafe.Pointer(&frame[i])).Elem()140+ switch fnType.In(i).Kind() {
141+ case reflect.Float32, reflect.Float64:
142+ if floatsN >= numOfFloats {
143+ pos = stack
144+ stack++
145+ } else {
146+ pos = floatsN
147+ }
148+ floatsN++
149+ default:
150+ if intsN >= numOfIntegerRegisters() {
151+ pos = stack
152+ stack++
153+ } else {
154+ // the integers begin after the floats in frame
155+ pos = intsN + numOfFloats
156+ }
157+ intsN++
158+ }
159+ args[i] = reflect.NewAt(fnType.In(i), unsafe.Pointer(&frame[pos])).Elem()
142 }160 }
143 ret := fn.Call(args)161 ret := fn.Call(args)
144 if len(ret) > 0 {162 if len(ret) > 0 {

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