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 Unverified
4AEE18F83AFDEB23The key is unknown
7 files · +171 -47
M callback_test.go +79 -0
| @@ -72,3 +72,82 @@ func buildSharedLib(libFile string, sources ...string) error { | |||
| 72 | 72 | ||
| 73 | 73 | 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 | + | } | |
| @@ -72,3 +72,82 @@ func buildSharedLib(libFile string, sources ...string) error { | |||||
| 72 | 72 | ||||
| 73 | return nil | 73 | 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) { | |||
| 99 | 99 | stack++ | |
| 100 | 100 | } | |
| 101 | 101 | case reflect.Float32, reflect.Float64: | |
| 102 | - | if floats < 8 { | |
| 102 | + | if floats < numOfFloats { | |
| 103 | 103 | floats++ | |
| 104 | 104 | } else { | |
| 105 | 105 | stack++ | |
| @@ -108,7 +108,8 @@ func RegisterFunc(fptr interface{}, cfn uintptr) { | |||
| 108 | 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 | 113 | panic("purego: too many arguments") | |
| 113 | 114 | } | |
| 114 | 115 | } | |
| @@ -127,7 +128,7 @@ func RegisterFunc(fptr interface{}, cfn uintptr) { | |||
| 127 | 128 | } | |
| 128 | 129 | var sysargs [maxArgs]uintptr | |
| 129 | 130 | var stack = sysargs[numOfIntegerRegisters():] | |
| 130 | - | var floats [8]float64 | |
| 131 | + | var floats [numOfFloats]uintptr | |
| 131 | 132 | var numInts int | |
| 132 | 133 | var numFloats int | |
| 133 | 134 | var numStack int | |
| @@ -168,9 +169,16 @@ func RegisterFunc(fptr interface{}, cfn uintptr) { | |||
| 168 | 169 | } else { | |
| 169 | 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 | 180 | if numFloats < len(floats) { | |
| 173 | - | floats[numFloats] = v.Float() | |
| 181 | + | floats[numFloats] = uintptr(math.Float64bits(v.Float())) | |
| 174 | 182 | numFloats++ | |
| 175 | 183 | } else { | |
| 176 | 184 | 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]uintptr | 129 | var sysargs [maxArgs]uintptr | ||
| 129 | var stack = sysargs[numOfIntegerRegisters():] | 130 | var stack = sysargs[numOfIntegerRegisters():] | ||
| 130 | - | var floats [8]float64 | 131 | + | var floats [numOfFloats]uintptr |
| 131 | var numInts int | 132 | var numInts int | ||
| 132 | var numFloats int | 133 | var numFloats int | ||
| 133 | var numStack int | 134 | 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 | |||
| 80 | 80 | ||
| 81 | 81 | 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 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 | |
| 92 | 100 | ||
| 93 | 101 | MOVQ R10, 0(SP) // push the stack pointer below registers | |
| 94 | 102 | ||
| @@ -128,7 +136,7 @@ TEXT callbackasm1(SB), NOSPLIT, $0 | |||
| 128 | 136 | ||
| 129 | 137 | MOVQ 0(SP), R10 // get the SP back | |
| 130 | 138 | ||
| 131 | - | ADJSP $-6*8, SP // remove arguments | |
| 139 | + | ADJSP $-14*8, SP // remove arguments | |
| 132 | 140 | ||
| 133 | 141 | MOVQ R10, 0(SP) | |
| 134 | 142 | ||
| @@ -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 args | 81 | 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 frame | 83 | + | // make space for first six int and 8 float arguments below the frame |
| 84 | - | ADJSP $6*8, SP | 84 | + | 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 vector | 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 | |||
| 92 | 100 | ||||
| 93 | MOVQ R10, 0(SP) // push the stack pointer below registers | 101 | 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 back | 137 | MOVQ 0(SP), R10 // get the SP back | ||
| 130 | 138 | ||||
| 131 | - | ADJSP $-6*8, SP // remove arguments | 139 | + | 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 | |||
| 69 | 69 | // On entry, the trampoline in zcallback_darwin_arm64.s left | |
| 70 | 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 | 73 | // 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) | |
| 79 | 87 | ||
| 80 | 88 | // Adjust SP by frame size. | |
| 81 | 89 | // crosscall2 clobbers FP in the frame record so only save/restore SP. | |
| 82 | - | SUB $(28*8), RSP | |
| 90 | + | SUB $(28*8), RSP | |
| 83 | 91 | MOVD R30, (RSP) | |
| 84 | 92 | ||
| 85 | 93 | // 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 | |
| 88 | 96 | MOVD R14, R0 | |
| 89 | - | MOVD R0, callbackArgs_args(R13) // address of args vector | |
| 97 | + | MOVD R0, callbackArgs_args(R13) // address of args vector | |
| 90 | 98 | MOVD $0, R0 | |
| 91 | - | MOVD R0, callbackArgs_result(R13) // result | |
| 99 | + | MOVD R0, callbackArgs_result(R13) // result | |
| 92 | 100 | ||
| 93 | 101 | // Move parameters into registers | |
| 94 | 102 | // Get the ABIInternal function pointer | |
| @@ -101,11 +109,11 @@ TEXT callbackasm1(SB), NOSPLIT|NOFRAME, $0 | |||
| 101 | 109 | BL crosscall2(SB) | |
| 102 | 110 | ||
| 103 | 111 | // Get callback result. | |
| 104 | - | ADD $(callbackArgs__size + 3*8), RSP, R13 | |
| 112 | + | ADD $(callbackArgs__size + 3*8), RSP, R13 | |
| 105 | 113 | MOVD callbackArgs_result(R13), R0 | |
| 106 | 114 | ||
| 107 | 115 | // Restore SP | |
| 108 | 116 | MOVD (RSP), R30 | |
| 109 | - | ADD $(28*8), RSP | |
| 117 | + | ADD $(28*8), RSP | |
| 110 | 118 | ||
| 111 | 119 | RET | |
| @@ -69,26 +69,34 @@ TEXT callbackasm1(SB), NOSPLIT|NOFRAME, $0 | |||||
| 69 | // On entry, the trampoline in zcallback_darwin_arm64.s left | 69 | // 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, R14 | 74 | + | 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), RSP | 90 | + | 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, R13 | 94 | + | ADD $(callbackArgs__size + 3*8), RSP, R13 |
| 87 | - | MOVD R12, callbackArgs_index(R13) // callback index | 95 | + | MOVD R12, callbackArgs_index(R13) // callback index |
| 88 | MOVD R14, R0 | 96 | MOVD R14, R0 | ||
| 89 | - | MOVD R0, callbackArgs_args(R13) // address of args vector | 97 | + | MOVD R0, callbackArgs_args(R13) // address of args vector |
| 90 | MOVD $0, R0 | 98 | MOVD $0, R0 | ||
| 91 | - | MOVD R0, callbackArgs_result(R13) // result | 99 | + | MOVD R0, callbackArgs_result(R13) // result |
| 92 | 100 | ||||
| 93 | // Move parameters into registers | 101 | // Move parameters into registers | ||
| 94 | // Get the ABIInternal function pointer | 102 | // 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, R13 | 112 | + | ADD $(callbackArgs__size + 3*8), RSP, R13 |
| 105 | MOVD callbackArgs_result(R13), R0 | 113 | MOVD callbackArgs_result(R13), R0 | ||
| 106 | 114 | ||||
| 107 | // Restore SP | 115 | // Restore SP | ||
| 108 | MOVD (RSP), R30 | 116 | MOVD (RSP), R30 | ||
| 109 | - | ADD $(28*8), RSP | 117 | + | ADD $(28*8), RSP |
| 110 | 118 | ||||
| 111 | RET | 119 | RET | ||
M syscall.go +4 -1
| @@ -5,7 +5,10 @@ | |||
| 5 | 5 | ||
| 6 | 6 | package purego | |
| 7 | 7 | ||
| 8 | - | const maxArgs = 9 | |
| 8 | + | const ( | |
| 9 | + | maxArgs = 9 | |
| 10 | + | numOfFloats = 8 // arm64 and amd64 both have 8 float registers | |
| 11 | + | ) | |
| 9 | 12 | ||
| 10 | 13 | // SyscallN takes fn, a C function pointer and a list of arguments as uintptr. | |
| 11 | 14 | // There is an internal maximum number of arguments that SyscallN can take. It panics | |
| @@ -5,7 +5,10 @@ | |||||
| 5 | 5 | ||||
| 6 | package purego | 6 | package purego | ||
| 7 | 7 | ||||
| 8 | - | const maxArgs = 9 | 8 | + | 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 panics | 14 | // 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) | |||
| 16 | 16 | // this is only here to make the assembly files happy :) | |
| 17 | 17 | type syscall9Args struct { | |
| 18 | 18 | 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 | |
| 20 | 20 | r1, r2, err uintptr | |
| 21 | 21 | } | |
| 22 | 22 | ||
| @@ -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 uintptr | 18 | 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 |
| 20 | r1, r2, err uintptr | 20 | r1, r2, err uintptr | ||
| 21 | } | 21 | } | ||
| 22 | 22 | ||||
M syscall_sysv.go +35 -17
| @@ -6,7 +6,6 @@ | |||
| 6 | 6 | package purego | |
| 7 | 7 | ||
| 8 | 8 | import ( | |
| 9 | - | "math" | |
| 10 | 9 | "reflect" | |
| 11 | 10 | "runtime" | |
| 12 | 11 | "sync" | |
| @@ -17,16 +16,14 @@ var syscall9XABI0 uintptr | |||
| 17 | 16 | ||
| 18 | 17 | type syscall9Args struct { | |
| 19 | 18 | 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 | |
| 21 | 20 | r1, r2, err uintptr | |
| 22 | 21 | } | |
| 23 | 22 | ||
| 24 | 23 | //go:nosplit | |
| 25 | 24 | func syscall_syscall9X(fn, a1, a2, a3, a4, a5, a6, a7, a8, a9 uintptr) (r1, r2, err uintptr) { | |
| 26 | 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)), | |
| 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, | |
| 30 | 27 | r1, r2, err} | |
| 31 | 28 | runtime_cgocall(syscall9XABI0, unsafe.Pointer(&args)) | |
| 32 | 29 | return args.r1, args.r2, args.err | |
| @@ -61,15 +58,13 @@ type callbackArgs struct { | |||
| 61 | 58 | index uintptr | |
| 62 | 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 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. | |
| 73 | 68 | args unsafe.Pointer | |
| 74 | 69 | // Below are out-args from callbackWrap | |
| 75 | 70 | result uintptr | |
| @@ -84,8 +79,7 @@ func compileCallback(fn interface{}) uintptr { | |||
| 84 | 79 | for i := 0; i < ty.NumIn(); i++ { | |
| 85 | 80 | in := ty.In(i) | |
| 86 | 81 | 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, | |
| 89 | 83 | reflect.Chan, reflect.Complex64, reflect.Complex128, | |
| 90 | 84 | reflect.String, reflect.Map, reflect.Invalid: | |
| 91 | 85 | panic("purego: unsupported argument type: " + in.Kind().String()) | |
| @@ -136,9 +130,33 @@ func callbackWrap(a *callbackArgs) { | |||
| 136 | 130 | fnType := fn.Type() | |
| 137 | 131 | args := make([]reflect.Value, fnType.NumIn()) | |
| 138 | 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 | 138 | 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() | |
| 142 | 160 | } | |
| 143 | 161 | ret := fn.Call(args) | |
| 144 | 162 | if len(ret) > 0 { | |
| @@ -6,7 +6,6 @@ | |||||
| 6 | package purego | 6 | 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 uintptr | 18 | 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 |
| 21 | r1, r2, err uintptr | 20 | r1, r2, err uintptr | ||
| 22 | } | 21 | } | ||
| 23 | 22 | ||||
| 24 | //go:nosplit | 23 | //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.err | 29 | return args.r1, args.r2, args.err | ||
| @@ -61,15 +58,13 @@ type callbackArgs struct { | |||||
| 61 | index uintptr | 58 | 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 to | 65 | + | // 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.Pointer | 68 | args unsafe.Pointer | ||
| 74 | // Below are out-args from callbackWrap | 69 | // Below are out-args from callbackWrap | ||
| 75 | result uintptr | 70 | 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 float64 | 139 | + | 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 { | ||