purego: properly handle 64-bit integers on 32-bit arm (#491)
1. Obey 8-byte alignment on stack and even-register placement required by AAPCS. 2. Use two registers (R0, R1) to make function return value. This will work for '386' architecture too. 3. Add test cases. Closes #489 Co-authored-by: Hirador <63920290+Hirador@users.noreply.github.com>
- commit
- 61308c82fac659d9ba60b435697dc08c6cf9eba2
- author
- Vladimir Stolyarov · · 2026-08-11 13:13 UTC
- committer
- GitHub · · 2026-08-11 13:13 UTC
- parent
- ecac7f7
- signature
- GPG Unverified
B5690EEEBB952194The key is unknown
8 files · +216 -16
M func.go +84 -10
| @@ -144,6 +144,7 @@ func RegisterFunc(fptr any, cfn uintptr) { | |||
| 144 | 144 | var ints int | |
| 145 | 145 | var floats int | |
| 146 | 146 | floatArgRegs := numOfFloatRegisters() | |
| 147 | + | ptrSize := unsafe.Sizeof(uintptr(0)) | |
| 147 | 148 | var stack int | |
| 148 | 149 | for i := 0; i < ty.NumIn(); i++ { | |
| 149 | 150 | arg := ty.In(i) | |
| @@ -161,19 +162,34 @@ func RegisterFunc(fptr any, cfn uintptr) { | |||
| 161 | 162 | panic("purego: CDecl must be the first argument") | |
| 162 | 163 | } | |
| 163 | 164 | } | |
| 164 | - | case reflect.String, reflect.Uintptr, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, | |
| 165 | - | reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Pointer, reflect.UnsafePointer, | |
| 165 | + | case reflect.String, reflect.Uintptr, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, | |
| 166 | + | reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Pointer, reflect.UnsafePointer, | |
| 166 | 167 | reflect.Slice, reflect.Bool: | |
| 167 | 168 | if ints < numOfIntegerRegisters() { | |
| 168 | 169 | ints++ | |
| 169 | 170 | } else { | |
| 170 | 171 | stack++ | |
| 171 | 172 | } | |
| 173 | + | case reflect.Uint64, reflect.Int64: | |
| 174 | + | usesSlots := max(1, int(arg.Size()/ptrSize)) | |
| 175 | + | if isARMPaddingNeeded(arg, ints, stack) { | |
| 176 | + | usesSlots++ | |
| 177 | + | } | |
| 178 | + | ||
| 179 | + | if ints < numOfIntegerRegisters() { | |
| 180 | + | ints += usesSlots | |
| 181 | + | } else { | |
| 182 | + | stack += usesSlots | |
| 183 | + | } | |
| 172 | 184 | case reflect.Float32, reflect.Float64: | |
| 185 | + | usesSlots := max(1, int(arg.Size()/ptrSize)) | |
| 186 | + | if isARMFloatPaddingNeeded(arg, floats, stack) { | |
| 187 | + | usesSlots++ | |
| 188 | + | } | |
| 173 | 189 | if floats < floatArgRegs { | |
| 174 | 190 | floats++ | |
| 175 | 191 | } else { | |
| 176 | - | stack++ | |
| 192 | + | stack += usesSlots | |
| 177 | 193 | } | |
| 178 | 194 | case reflect.Struct: | |
| 179 | 195 | ensureStructSupported() | |
| @@ -347,9 +363,22 @@ func RegisterFunc(fptr any, cfn uintptr) { | |||
| 347 | 363 | outType := ty.Out(0) | |
| 348 | 364 | v := reflect.New(outType).Elem() | |
| 349 | 365 | switch outType.Kind() { | |
| 350 | - | case reflect.Uintptr, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: | |
| 366 | + | case reflect.Uint64: | |
| 367 | + | if is32bit { | |
| 368 | + | // high-word is recorded at a2 for 32-bit platforms and 64-bit returns | |
| 369 | + | v.SetUint(uint64(syscall.a1) | (uint64(syscall.a2) << 32)) | |
| 370 | + | } else { | |
| 371 | + | v.SetUint(uint64(syscall.a1)) | |
| 372 | + | } | |
| 373 | + | case reflect.Uintptr, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32: | |
| 351 | 374 | v.SetUint(uint64(syscall.a1)) | |
| 352 | - | case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: | |
| 375 | + | case reflect.Int64: | |
| 376 | + | if is32bit { | |
| 377 | + | v.SetInt(int64(syscall.a1) | (int64(syscall.a2) << 32)) | |
| 378 | + | } else { | |
| 379 | + | v.SetInt(int64(syscall.a1)) | |
| 380 | + | } | |
| 381 | + | case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32: | |
| 353 | 382 | v.SetInt(int64(syscall.a1)) | |
| 354 | 383 | case reflect.Bool: | |
| 355 | 384 | v.SetBool(byte(syscall.a1) != 0) | |
| @@ -415,9 +444,25 @@ func addValue(v reflect.Value, keepAlive []any, addInt func(x uintptr), addFloat | |||
| 415 | 444 | ptr := strings.CString(v.String()) | |
| 416 | 445 | keepAlive = append(keepAlive, ptr) | |
| 417 | 446 | addInt(uintptr(unsafe.Pointer(ptr))) | |
| 418 | - | case reflect.Uintptr, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: | |
| 447 | + | case reflect.Uint64: | |
| 448 | + | if isARMPaddingNeeded(v.Type(), *numInts, *numStack) { | |
| 449 | + | addInt(0) | |
| 450 | + | } | |
| 451 | + | addInt(uintptr(v.Uint())) | |
| 452 | + | if is32bit { | |
| 453 | + | addInt(uintptr(v.Uint() >> 32)) // on 32bit we must add high word too | |
| 454 | + | } | |
| 455 | + | case reflect.Uintptr, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32: | |
| 419 | 456 | addInt(uintptr(v.Uint())) | |
| 420 | - | case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: | |
| 457 | + | case reflect.Int64: | |
| 458 | + | if isARMPaddingNeeded(v.Type(), *numInts, *numStack) { | |
| 459 | + | addInt(0) | |
| 460 | + | } | |
| 461 | + | addInt(uintptr(v.Int())) | |
| 462 | + | if is32bit { | |
| 463 | + | addInt(uintptr(v.Int() >> 32)) | |
| 464 | + | } | |
| 465 | + | case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32: | |
| 421 | 466 | addInt(uintptr(v.Int())) | |
| 422 | 467 | case reflect.Pointer, reflect.UnsafePointer, reflect.Slice: | |
| 423 | 468 | // There is no need to keepAlive this pointer separately because it is kept alive in the args variable | |
| @@ -442,12 +487,16 @@ func addValue(v reflect.Value, keepAlive []any, addInt func(x uintptr), addFloat | |||
| 442 | 487 | addFloat(uintptr(math.Float32bits(float32(v.Float())))) | |
| 443 | 488 | } | |
| 444 | 489 | case reflect.Float64: | |
| 490 | + | bits := math.Float64bits(v.Float()) | |
| 491 | + | if isARMFloatPaddingNeeded(v.Type(), *numFloats, *numStack) { | |
| 492 | + | // if floats are spilled onto stack on ARM than we must follow AAPCS C.7 | |
| 493 | + | addFloat(0) | |
| 494 | + | } | |
| 445 | 495 | if is32bit { | |
| 446 | - | bits := math.Float64bits(v.Float()) | |
| 447 | 496 | addFloat(uintptr(bits)) | |
| 448 | 497 | addFloat(uintptr(bits >> 32)) | |
| 449 | 498 | } else { | |
| 450 | - | addFloat(uintptr(math.Float64bits(v.Float()))) | |
| 499 | + | addFloat(uintptr(bits)) | |
| 451 | 500 | } | |
| 452 | 501 | case reflect.Struct: | |
| 453 | 502 | keepAlive = addStruct(v, numInts, numFloats, numStack, addInt, addFloat, addStack, keepAlive) | |
| @@ -603,7 +652,6 @@ func estimateStackBytes(ty reflect.Type) int { | |||
| 603 | 652 | } else if !usesInt && numFloats < numOfFloatRegisters() { | |
| 604 | 653 | numFloats++ | |
| 605 | 654 | } else { | |
| 606 | - | // Goes to stack - accumulate total bytes | |
| 607 | 655 | stackBytes += size | |
| 608 | 656 | } | |
| 609 | 657 | } | |
| @@ -613,3 +661,29 @@ func estimateStackBytes(ty reflect.Type) int { | |||
| 613 | 661 | } | |
| 614 | 662 | return stackBytes | |
| 615 | 663 | } | |
| 664 | + | ||
| 665 | + | func isARMPaddingNeeded(ty reflect.Type, numInts, numStack int) bool { | |
| 666 | + | // ARM EABI (AAPCS): 8-byte-aligned types (int64/uint64) start on an | |
| 667 | + | // even core register (C.3); if they then spill, the stack slot is | |
| 668 | + | // 8-byte aligned too (C.7). | |
| 669 | + | // https://github.com/ARM-software/abi-aa/blob/main/aapcs32/aapcs32.rst#6111handling-values-larger-than-32-bits | |
| 670 | + | if runtime.GOARCH != "arm" || ty.Size() != 8 { | |
| 671 | + | return false | |
| 672 | + | } | |
| 673 | + | if numInts >= 0 && numInts < numOfIntegerRegisters() { | |
| 674 | + | return numInts%2 != 0 | |
| 675 | + | } | |
| 676 | + | return numStack%2 != 0 | |
| 677 | + | } | |
| 678 | + | ||
| 679 | + | func isARMFloatPaddingNeeded(ty reflect.Type, numFloats, numStack int) bool { | |
| 680 | + | if runtime.GOARCH != "arm" || ty.Size() != 8 { | |
| 681 | + | return false | |
| 682 | + | } | |
| 683 | + | if numFloats >= 0 && numFloats < numOfFloatRegisters() { | |
| 684 | + | // float registers are 64bit so alignment never needed for args in registers | |
| 685 | + | return false | |
| 686 | + | } | |
| 687 | + | // Only check if AAPCS C.7 is applicable here | |
| 688 | + | return numStack%2 != 0 | |
| 689 | + | } | |
| @@ -144,6 +144,7 @@ func RegisterFunc(fptr any, cfn uintptr) { | |||||
| 144 | var ints int | 144 | var ints int | ||
| 145 | var floats int | 145 | var floats int | ||
| 146 | floatArgRegs := numOfFloatRegisters() | 146 | floatArgRegs := numOfFloatRegisters() | ||
| 147 | + | ptrSize := unsafe.Sizeof(uintptr(0)) | |||
| 147 | var stack int | 148 | var stack int | ||
| 148 | for i := 0; i < ty.NumIn(); i++ { | 149 | for i := 0; i < ty.NumIn(); i++ { | ||
| 149 | arg := ty.In(i) | 150 | arg := ty.In(i) | ||
| @@ -161,19 +162,34 @@ func RegisterFunc(fptr any, cfn uintptr) { | |||||
| 161 | panic("purego: CDecl must be the first argument") | 162 | panic("purego: CDecl must be the first argument") | ||
| 162 | } | 163 | } | ||
| 163 | } | 164 | } | ||
| 164 | - | case reflect.String, reflect.Uintptr, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, | 165 | + | case reflect.String, reflect.Uintptr, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, |
| 165 | - | reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Pointer, reflect.UnsafePointer, | 166 | + | reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Pointer, reflect.UnsafePointer, |
| 166 | reflect.Slice, reflect.Bool: | 167 | reflect.Slice, reflect.Bool: | ||
| 167 | if ints < numOfIntegerRegisters() { | 168 | if ints < numOfIntegerRegisters() { | ||
| 168 | ints++ | 169 | ints++ | ||
| 169 | } else { | 170 | } else { | ||
| 170 | stack++ | 171 | stack++ | ||
| 171 | } | 172 | } | ||
| 173 | + | case reflect.Uint64, reflect.Int64: | |||
| 174 | + | usesSlots := max(1, int(arg.Size()/ptrSize)) | |||
| 175 | + | if isARMPaddingNeeded(arg, ints, stack) { | |||
| 176 | + | usesSlots++ | |||
| 177 | + | } | |||
| 178 | + | ||||
| 179 | + | if ints < numOfIntegerRegisters() { | |||
| 180 | + | ints += usesSlots | |||
| 181 | + | } else { | |||
| 182 | + | stack += usesSlots | |||
| 183 | + | } | |||
| 172 | case reflect.Float32, reflect.Float64: | 184 | case reflect.Float32, reflect.Float64: | ||
| 185 | + | usesSlots := max(1, int(arg.Size()/ptrSize)) | |||
| 186 | + | if isARMFloatPaddingNeeded(arg, floats, stack) { | |||
| 187 | + | usesSlots++ | |||
| 188 | + | } | |||
| 173 | if floats < floatArgRegs { | 189 | if floats < floatArgRegs { | ||
| 174 | floats++ | 190 | floats++ | ||
| 175 | } else { | 191 | } else { | ||
| 176 | - | stack++ | 192 | + | stack += usesSlots |
| 177 | } | 193 | } | ||
| 178 | case reflect.Struct: | 194 | case reflect.Struct: | ||
| 179 | ensureStructSupported() | 195 | ensureStructSupported() | ||
| @@ -347,9 +363,22 @@ func RegisterFunc(fptr any, cfn uintptr) { | |||||
| 347 | outType := ty.Out(0) | 363 | outType := ty.Out(0) | ||
| 348 | v := reflect.New(outType).Elem() | 364 | v := reflect.New(outType).Elem() | ||
| 349 | switch outType.Kind() { | 365 | switch outType.Kind() { | ||
| 350 | - | case reflect.Uintptr, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: | 366 | + | case reflect.Uint64: |
| 367 | + | if is32bit { | |||
| 368 | + | // high-word is recorded at a2 for 32-bit platforms and 64-bit returns | |||
| 369 | + | v.SetUint(uint64(syscall.a1) | (uint64(syscall.a2) << 32)) | |||
| 370 | + | } else { | |||
| 371 | + | v.SetUint(uint64(syscall.a1)) | |||
| 372 | + | } | |||
| 373 | + | case reflect.Uintptr, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32: | |||
| 351 | v.SetUint(uint64(syscall.a1)) | 374 | v.SetUint(uint64(syscall.a1)) | ||
| 352 | - | case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: | 375 | + | case reflect.Int64: |
| 376 | + | if is32bit { | |||
| 377 | + | v.SetInt(int64(syscall.a1) | (int64(syscall.a2) << 32)) | |||
| 378 | + | } else { | |||
| 379 | + | v.SetInt(int64(syscall.a1)) | |||
| 380 | + | } | |||
| 381 | + | case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32: | |||
| 353 | v.SetInt(int64(syscall.a1)) | 382 | v.SetInt(int64(syscall.a1)) | ||
| 354 | case reflect.Bool: | 383 | case reflect.Bool: | ||
| 355 | v.SetBool(byte(syscall.a1) != 0) | 384 | v.SetBool(byte(syscall.a1) != 0) | ||
| @@ -415,9 +444,25 @@ func addValue(v reflect.Value, keepAlive []any, addInt func(x uintptr), addFloat | |||||
| 415 | ptr := strings.CString(v.String()) | 444 | ptr := strings.CString(v.String()) | ||
| 416 | keepAlive = append(keepAlive, ptr) | 445 | keepAlive = append(keepAlive, ptr) | ||
| 417 | addInt(uintptr(unsafe.Pointer(ptr))) | 446 | addInt(uintptr(unsafe.Pointer(ptr))) | ||
| 418 | - | case reflect.Uintptr, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: | 447 | + | case reflect.Uint64: |
| 448 | + | if isARMPaddingNeeded(v.Type(), *numInts, *numStack) { | |||
| 449 | + | addInt(0) | |||
| 450 | + | } | |||
| 451 | + | addInt(uintptr(v.Uint())) | |||
| 452 | + | if is32bit { | |||
| 453 | + | addInt(uintptr(v.Uint() >> 32)) // on 32bit we must add high word too | |||
| 454 | + | } | |||
| 455 | + | case reflect.Uintptr, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32: | |||
| 419 | addInt(uintptr(v.Uint())) | 456 | addInt(uintptr(v.Uint())) | ||
| 420 | - | case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: | 457 | + | case reflect.Int64: |
| 458 | + | if isARMPaddingNeeded(v.Type(), *numInts, *numStack) { | |||
| 459 | + | addInt(0) | |||
| 460 | + | } | |||
| 461 | + | addInt(uintptr(v.Int())) | |||
| 462 | + | if is32bit { | |||
| 463 | + | addInt(uintptr(v.Int() >> 32)) | |||
| 464 | + | } | |||
| 465 | + | case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32: | |||
| 421 | addInt(uintptr(v.Int())) | 466 | addInt(uintptr(v.Int())) | ||
| 422 | case reflect.Pointer, reflect.UnsafePointer, reflect.Slice: | 467 | case reflect.Pointer, reflect.UnsafePointer, reflect.Slice: | ||
| 423 | // There is no need to keepAlive this pointer separately because it is kept alive in the args variable | 468 | // There is no need to keepAlive this pointer separately because it is kept alive in the args variable | ||
| @@ -442,12 +487,16 @@ func addValue(v reflect.Value, keepAlive []any, addInt func(x uintptr), addFloat | |||||
| 442 | addFloat(uintptr(math.Float32bits(float32(v.Float())))) | 487 | addFloat(uintptr(math.Float32bits(float32(v.Float())))) | ||
| 443 | } | 488 | } | ||
| 444 | case reflect.Float64: | 489 | case reflect.Float64: | ||
| 490 | + | bits := math.Float64bits(v.Float()) | |||
| 491 | + | if isARMFloatPaddingNeeded(v.Type(), *numFloats, *numStack) { | |||
| 492 | + | // if floats are spilled onto stack on ARM than we must follow AAPCS C.7 | |||
| 493 | + | addFloat(0) | |||
| 494 | + | } | |||
| 445 | if is32bit { | 495 | if is32bit { | ||
| 446 | - | bits := math.Float64bits(v.Float()) | |||
| 447 | addFloat(uintptr(bits)) | 496 | addFloat(uintptr(bits)) | ||
| 448 | addFloat(uintptr(bits >> 32)) | 497 | addFloat(uintptr(bits >> 32)) | ||
| 449 | } else { | 498 | } else { | ||
| 450 | - | addFloat(uintptr(math.Float64bits(v.Float()))) | 499 | + | addFloat(uintptr(bits)) |
| 451 | } | 500 | } | ||
| 452 | case reflect.Struct: | 501 | case reflect.Struct: | ||
| 453 | keepAlive = addStruct(v, numInts, numFloats, numStack, addInt, addFloat, addStack, keepAlive) | 502 | keepAlive = addStruct(v, numInts, numFloats, numStack, addInt, addFloat, addStack, keepAlive) | ||
| @@ -603,7 +652,6 @@ func estimateStackBytes(ty reflect.Type) int { | |||||
| 603 | } else if !usesInt && numFloats < numOfFloatRegisters() { | 652 | } else if !usesInt && numFloats < numOfFloatRegisters() { | ||
| 604 | numFloats++ | 653 | numFloats++ | ||
| 605 | } else { | 654 | } else { | ||
| 606 | - | // Goes to stack - accumulate total bytes | |||
| 607 | stackBytes += size | 655 | stackBytes += size | ||
| 608 | } | 656 | } | ||
| 609 | } | 657 | } | ||
| @@ -613,3 +661,29 @@ func estimateStackBytes(ty reflect.Type) int { | |||||
| 613 | } | 661 | } | ||
| 614 | return stackBytes | 662 | return stackBytes | ||
| 615 | } | 663 | } | ||
| 664 | + | ||||
| 665 | + | func isARMPaddingNeeded(ty reflect.Type, numInts, numStack int) bool { | |||
| 666 | + | // ARM EABI (AAPCS): 8-byte-aligned types (int64/uint64) start on an | |||
| 667 | + | // even core register (C.3); if they then spill, the stack slot is | |||
| 668 | + | // 8-byte aligned too (C.7). | |||
| 669 | + | // https://github.com/ARM-software/abi-aa/blob/main/aapcs32/aapcs32.rst#6111handling-values-larger-than-32-bits | |||
| 670 | + | if runtime.GOARCH != "arm" || ty.Size() != 8 { | |||
| 671 | + | return false | |||
| 672 | + | } | |||
| 673 | + | if numInts >= 0 && numInts < numOfIntegerRegisters() { | |||
| 674 | + | return numInts%2 != 0 | |||
| 675 | + | } | |||
| 676 | + | return numStack%2 != 0 | |||
| 677 | + | } | |||
| 678 | + | ||||
| 679 | + | func isARMFloatPaddingNeeded(ty reflect.Type, numFloats, numStack int) bool { | |||
| 680 | + | if runtime.GOARCH != "arm" || ty.Size() != 8 { | |||
| 681 | + | return false | |||
| 682 | + | } | |||
| 683 | + | if numFloats >= 0 && numFloats < numOfFloatRegisters() { | |||
| 684 | + | // float registers are 64bit so alignment never needed for args in registers | |||
| 685 | + | return false | |||
| 686 | + | } | |||
| 687 | + | // Only check if AAPCS C.7 is applicable here | |||
| 688 | + | return numStack%2 != 0 | |||
| 689 | + | } | |||
M func_test.go +61 -1
| @@ -7,11 +7,13 @@ import ( | |||
| 7 | 7 | "bytes" | |
| 8 | 8 | "errors" | |
| 9 | 9 | "fmt" | |
| 10 | + | "math" | |
| 10 | 11 | "os" | |
| 11 | 12 | "os/exec" | |
| 12 | 13 | "path/filepath" | |
| 13 | 14 | "reflect" | |
| 14 | 15 | "runtime" | |
| 16 | + | "strconv" | |
| 15 | 17 | "strings" | |
| 16 | 18 | "sync" | |
| 17 | 19 | "testing" | |
| @@ -402,6 +404,63 @@ func TestABI_ArgumentPassing(t *testing.T) { | |||
| 402 | 404 | }, | |
| 403 | 405 | want: "1:2:3:4:5:6:7:8:9:10.0", | |
| 404 | 406 | }, | |
| 407 | + | { | |
| 408 | + | // check if unaligned 64bit argument and 64bit returned value is properly passed via registers | |
| 409 | + | // arm-specific but must work everywhere | |
| 410 | + | name: "arm_int64_unaligned_in_registers", | |
| 411 | + | fn: new(func(uintptr, int64) int64), | |
| 412 | + | cFn: "arm_int64_unaligned_in_registers", | |
| 413 | + | call: func(f any) string { | |
| 414 | + | fn := *(f).(*func(x uintptr, y int64) int64) | |
| 415 | + | return strconv.FormatInt(fn(456, math.MaxInt32+1500), 10) | |
| 416 | + | }, | |
| 417 | + | want: strconv.FormatInt(456*123+math.MaxInt32+1500, 10), | |
| 418 | + | }, | |
| 419 | + | { | |
| 420 | + | // check if unaligned 64bit argument and 64bit returned value is properly passed via stack | |
| 421 | + | // arm-specific but must work everywhere | |
| 422 | + | name: "arm_int64_unaligned_on_stack", | |
| 423 | + | fn: new(func(uintptr, uintptr, uintptr, uintptr, uintptr, int64) int64), | |
| 424 | + | cFn: "arm_int64_unaligned_on_stack", | |
| 425 | + | call: func(f any) string { | |
| 426 | + | fn := *(f).(*func(a1, a2, a3, a4, a5 uintptr, a6 int64) int64) | |
| 427 | + | return strconv.FormatInt(fn(12, 34, 56, 78, 90, math.MaxInt32+1500), 10) | |
| 428 | + | }, | |
| 429 | + | want: strconv.FormatInt(12*1+34*2+56*3+78*4+90*5+math.MaxInt32+1500, 10), | |
| 430 | + | }, | |
| 431 | + | { | |
| 432 | + | // check if unaligned 64bit argument and 64bit returned value is properly passed via stack when it's occupied by floats | |
| 433 | + | // arm-specific but must work everywhere | |
| 434 | + | name: "arm_int64_unaligned_on_stack_after_floats", | |
| 435 | + | fn: new(func( | |
| 436 | + | uintptr, uintptr, uintptr, uintptr, | |
| 437 | + | float32, float32, float32, float32, | |
| 438 | + | float32, float32, float32, float32, | |
| 439 | + | float32, float32, float32, float32, | |
| 440 | + | float32, float32, float32, float32, | |
| 441 | + | float32, int64, | |
| 442 | + | ) int64), | |
| 443 | + | cFn: "arm_int64_unaligned_on_stack_after_floats", | |
| 444 | + | call: func(f any) string { | |
| 445 | + | fn := *(f).(*func( | |
| 446 | + | a1, a2, a3, a4 uintptr, | |
| 447 | + | f1, f2, f3, f4 float32, | |
| 448 | + | f5, f6, f7, f8 float32, | |
| 449 | + | f9, f10, f11, f12 float32, | |
| 450 | + | f13, f14, f15, f16 float32, | |
| 451 | + | f17 float32, a5 int64, | |
| 452 | + | ) int64) | |
| 453 | + | return strconv.FormatInt(fn( | |
| 454 | + | 12, 34, 56, 78, | |
| 455 | + | 0, 0, 0, 0, | |
| 456 | + | 0, 0, 0, 0, | |
| 457 | + | 0, 0, 0, 0, | |
| 458 | + | 0, 0, 0, 0, | |
| 459 | + | 0, math.MaxInt32+1500, | |
| 460 | + | ), 10) | |
| 461 | + | }, | |
| 462 | + | want: strconv.FormatInt(12*1+34*2+56*3+78*4+math.MaxInt32+1500, 10), | |
| 463 | + | }, | |
| 405 | 464 | } | |
| 406 | 465 | ||
| 407 | 466 | for _, tt := range tests { | |
| @@ -409,7 +468,8 @@ func TestABI_ArgumentPassing(t *testing.T) { | |||
| 409 | 468 | if tt.name == "20_int32" && runtime.GOARCH == "ppc64le" { | |
| 410 | 469 | t.Skip("ppc64le retains the 15-argument limit") | |
| 411 | 470 | } | |
| 412 | - | if tt.name == "10_float32" && (runtime.GOARCH == "loong64" || runtime.GOARCH == "ppc64le" || runtime.GOARCH == "riscv64" || runtime.GOARCH == "s390x") { | |
| 471 | + | if (tt.name == "10_float32" || tt.name == "arm_int64_unaligned_on_stack_after_floats") && | |
| 472 | + | (runtime.GOARCH == "loong64" || runtime.GOARCH == "ppc64le" || runtime.GOARCH == "riscv64" || runtime.GOARCH == "s390x") { | |
| 413 | 473 | t.Skip("float32 stack arguments not yet supported on this platform") | |
| 414 | 474 | } | |
| 415 | 475 | // Struct tests require Darwin ARM64 or AMD64 | |
| @@ -7,11 +7,13 @@ import ( | |||||
| 7 | "bytes" | 7 | "bytes" | ||
| 8 | "errors" | 8 | "errors" | ||
| 9 | "fmt" | 9 | "fmt" | ||
| 10 | + | "math" | |||
| 10 | "os" | 11 | "os" | ||
| 11 | "os/exec" | 12 | "os/exec" | ||
| 12 | "path/filepath" | 13 | "path/filepath" | ||
| 13 | "reflect" | 14 | "reflect" | ||
| 14 | "runtime" | 15 | "runtime" | ||
| 16 | + | "strconv" | |||
| 15 | "strings" | 17 | "strings" | ||
| 16 | "sync" | 18 | "sync" | ||
| 17 | "testing" | 19 | "testing" | ||
| @@ -402,6 +404,63 @@ func TestABI_ArgumentPassing(t *testing.T) { | |||||
| 402 | }, | 404 | }, | ||
| 403 | want: "1:2:3:4:5:6:7:8:9:10.0", | 405 | want: "1:2:3:4:5:6:7:8:9:10.0", | ||
| 404 | }, | 406 | }, | ||
| 407 | + | { | |||
| 408 | + | // check if unaligned 64bit argument and 64bit returned value is properly passed via registers | |||
| 409 | + | // arm-specific but must work everywhere | |||
| 410 | + | name: "arm_int64_unaligned_in_registers", | |||
| 411 | + | fn: new(func(uintptr, int64) int64), | |||
| 412 | + | cFn: "arm_int64_unaligned_in_registers", | |||
| 413 | + | call: func(f any) string { | |||
| 414 | + | fn := *(f).(*func(x uintptr, y int64) int64) | |||
| 415 | + | return strconv.FormatInt(fn(456, math.MaxInt32+1500), 10) | |||
| 416 | + | }, | |||
| 417 | + | want: strconv.FormatInt(456*123+math.MaxInt32+1500, 10), | |||
| 418 | + | }, | |||
| 419 | + | { | |||
| 420 | + | // check if unaligned 64bit argument and 64bit returned value is properly passed via stack | |||
| 421 | + | // arm-specific but must work everywhere | |||
| 422 | + | name: "arm_int64_unaligned_on_stack", | |||
| 423 | + | fn: new(func(uintptr, uintptr, uintptr, uintptr, uintptr, int64) int64), | |||
| 424 | + | cFn: "arm_int64_unaligned_on_stack", | |||
| 425 | + | call: func(f any) string { | |||
| 426 | + | fn := *(f).(*func(a1, a2, a3, a4, a5 uintptr, a6 int64) int64) | |||
| 427 | + | return strconv.FormatInt(fn(12, 34, 56, 78, 90, math.MaxInt32+1500), 10) | |||
| 428 | + | }, | |||
| 429 | + | want: strconv.FormatInt(12*1+34*2+56*3+78*4+90*5+math.MaxInt32+1500, 10), | |||
| 430 | + | }, | |||
| 431 | + | { | |||
| 432 | + | // check if unaligned 64bit argument and 64bit returned value is properly passed via stack when it's occupied by floats | |||
| 433 | + | // arm-specific but must work everywhere | |||
| 434 | + | name: "arm_int64_unaligned_on_stack_after_floats", | |||
| 435 | + | fn: new(func( | |||
| 436 | + | uintptr, uintptr, uintptr, uintptr, | |||
| 437 | + | float32, float32, float32, float32, | |||
| 438 | + | float32, float32, float32, float32, | |||
| 439 | + | float32, float32, float32, float32, | |||
| 440 | + | float32, float32, float32, float32, | |||
| 441 | + | float32, int64, | |||
| 442 | + | ) int64), | |||
| 443 | + | cFn: "arm_int64_unaligned_on_stack_after_floats", | |||
| 444 | + | call: func(f any) string { | |||
| 445 | + | fn := *(f).(*func( | |||
| 446 | + | a1, a2, a3, a4 uintptr, | |||
| 447 | + | f1, f2, f3, f4 float32, | |||
| 448 | + | f5, f6, f7, f8 float32, | |||
| 449 | + | f9, f10, f11, f12 float32, | |||
| 450 | + | f13, f14, f15, f16 float32, | |||
| 451 | + | f17 float32, a5 int64, | |||
| 452 | + | ) int64) | |||
| 453 | + | return strconv.FormatInt(fn( | |||
| 454 | + | 12, 34, 56, 78, | |||
| 455 | + | 0, 0, 0, 0, | |||
| 456 | + | 0, 0, 0, 0, | |||
| 457 | + | 0, 0, 0, 0, | |||
| 458 | + | 0, 0, 0, 0, | |||
| 459 | + | 0, math.MaxInt32+1500, | |||
| 460 | + | ), 10) | |||
| 461 | + | }, | |||
| 462 | + | want: strconv.FormatInt(12*1+34*2+56*3+78*4+math.MaxInt32+1500, 10), | |||
| 463 | + | }, | |||
| 405 | } | 464 | } | ||
| 406 | 465 | ||||
| 407 | for _, tt := range tests { | 466 | for _, tt := range tests { | ||
| @@ -409,7 +468,8 @@ func TestABI_ArgumentPassing(t *testing.T) { | |||||
| 409 | if tt.name == "20_int32" && runtime.GOARCH == "ppc64le" { | 468 | if tt.name == "20_int32" && runtime.GOARCH == "ppc64le" { | ||
| 410 | t.Skip("ppc64le retains the 15-argument limit") | 469 | t.Skip("ppc64le retains the 15-argument limit") | ||
| 411 | } | 470 | } | ||
| 412 | - | if tt.name == "10_float32" && (runtime.GOARCH == "loong64" || runtime.GOARCH == "ppc64le" || runtime.GOARCH == "riscv64" || runtime.GOARCH == "s390x") { | 471 | + | if (tt.name == "10_float32" || tt.name == "arm_int64_unaligned_on_stack_after_floats") && |
| 472 | + | (runtime.GOARCH == "loong64" || runtime.GOARCH == "ppc64le" || runtime.GOARCH == "riscv64" || runtime.GOARCH == "s390x") { | |||
| 413 | t.Skip("float32 stack arguments not yet supported on this platform") | 473 | t.Skip("float32 stack arguments not yet supported on this platform") | ||
| 414 | } | 474 | } | ||
| 415 | // Struct tests require Darwin ARM64 or AMD64 | 475 | // Struct tests require Darwin ARM64 or AMD64 | ||
M sys_unix_arm.s +2 -0
| @@ -51,6 +51,7 @@ TEXT callbackasm1(SB), NOSPLIT|NOFRAME, $0 | |||
| 51 | 51 | MOVW R4, 52(R13) | |
| 52 | 52 | MOVW $0, R4 | |
| 53 | 53 | MOVW R4, 56(R13) | |
| 54 | + | MOVW R4, 60(R13) // high word of a 64-bit return | |
| 54 | 55 | ||
| 55 | 56 | // Call crosscall2(fn, frame, 0, ctxt) | |
| 56 | 57 | MOVW ·callbackWrap_call(SB), R0 | |
| @@ -63,6 +64,7 @@ TEXT callbackasm1(SB), NOSPLIT|NOFRAME, $0 | |||
| 63 | 64 | ||
| 64 | 65 | // Get result | |
| 65 | 66 | MOVW 56(R13), R0 | |
| 67 | + | MOVW 60(R13), R1 // high word of a 64-bit return | |
| 66 | 68 | ||
| 67 | 69 | // Restore float registers | |
| 68 | 70 | MOVD 64(R13), F0 | |
| @@ -51,6 +51,7 @@ TEXT callbackasm1(SB), NOSPLIT|NOFRAME, $0 | |||||
| 51 | MOVW R4, 52(R13) | 51 | MOVW R4, 52(R13) | ||
| 52 | MOVW $0, R4 | 52 | MOVW $0, R4 | ||
| 53 | MOVW R4, 56(R13) | 53 | MOVW R4, 56(R13) | ||
| 54 | + | MOVW R4, 60(R13) // high word of a 64-bit return | |||
| 54 | 55 | ||||
| 55 | // Call crosscall2(fn, frame, 0, ctxt) | 56 | // Call crosscall2(fn, frame, 0, ctxt) | ||
| 56 | MOVW ·callbackWrap_call(SB), R0 | 57 | MOVW ·callbackWrap_call(SB), R0 | ||
| @@ -63,6 +64,7 @@ TEXT callbackasm1(SB), NOSPLIT|NOFRAME, $0 | |||||
| 63 | 64 | ||||
| 64 | // Get result | 65 | // Get result | ||
| 65 | MOVW 56(R13), R0 | 66 | MOVW 56(R13), R0 | ||
| 67 | + | MOVW 60(R13), R1 // high word of a 64-bit return | |||
| 66 | 68 | ||||
| 67 | // Restore float registers | 69 | // Restore float registers | ||
| 68 | MOVD 64(R13), F0 | 70 | MOVD 64(R13), F0 | ||
M syscall_notstackargs.go +14 -0
| @@ -39,3 +39,17 @@ func (c *callbackArgs) stackFrame() unsafe.Pointer { | |||
| 39 | 39 | func (c *callbackArgs) intFrame() unsafe.Pointer { | |
| 40 | 40 | return nil | |
| 41 | 41 | } | |
| 42 | + | ||
| 43 | + | func (c *callbackArgs) setInt64Result(result int64) { | |
| 44 | + | c.result[0] = uintptr(result) | |
| 45 | + | if unsafe.Sizeof(uintptr(0)) == 4 { | |
| 46 | + | c.result[1] = uintptr(result >> 32) | |
| 47 | + | } | |
| 48 | + | } | |
| 49 | + | ||
| 50 | + | func (c *callbackArgs) setUint64Result(result uint64) { | |
| 51 | + | c.result[0] = uintptr(result) | |
| 52 | + | if unsafe.Sizeof(uintptr(0)) == 4 { | |
| 53 | + | c.result[1] = uintptr(result >> 32) | |
| 54 | + | } | |
| 55 | + | } | |
| @@ -39,3 +39,17 @@ func (c *callbackArgs) stackFrame() unsafe.Pointer { | |||||
| 39 | func (c *callbackArgs) intFrame() unsafe.Pointer { | 39 | func (c *callbackArgs) intFrame() unsafe.Pointer { | ||
| 40 | return nil | 40 | return nil | ||
| 41 | } | 41 | } | ||
| 42 | + | ||||
| 43 | + | func (c *callbackArgs) setInt64Result(result int64) { | |||
| 44 | + | c.result[0] = uintptr(result) | |||
| 45 | + | if unsafe.Sizeof(uintptr(0)) == 4 { | |||
| 46 | + | c.result[1] = uintptr(result >> 32) | |||
| 47 | + | } | |||
| 48 | + | } | |||
| 49 | + | ||||
| 50 | + | func (c *callbackArgs) setUint64Result(result uint64) { | |||
| 51 | + | c.result[0] = uintptr(result) | |||
| 52 | + | if unsafe.Sizeof(uintptr(0)) == 4 { | |||
| 53 | + | c.result[1] = uintptr(result >> 32) | |||
| 54 | + | } | |||
| 55 | + | } | |||
M syscall_stackargs_ppc64le.go +8 -0
| @@ -28,3 +28,11 @@ func (c *callbackArgs) stackFrame() unsafe.Pointer { | |||
| 28 | 28 | func (c *callbackArgs) intFrame() unsafe.Pointer { | |
| 29 | 29 | return nil | |
| 30 | 30 | } | |
| 31 | + | ||
| 32 | + | func (c *callbackArgs) setInt64Result(result int64) { | |
| 33 | + | c.result[0] = uintptr(result) | |
| 34 | + | } | |
| 35 | + | ||
| 36 | + | func (c *callbackArgs) setUint64Result(result uint64) { | |
| 37 | + | c.result[0] = uintptr(result) | |
| 38 | + | } | |
| @@ -28,3 +28,11 @@ func (c *callbackArgs) stackFrame() unsafe.Pointer { | |||||
| 28 | func (c *callbackArgs) intFrame() unsafe.Pointer { | 28 | func (c *callbackArgs) intFrame() unsafe.Pointer { | ||
| 29 | return nil | 29 | return nil | ||
| 30 | } | 30 | } | ||
| 31 | + | ||||
| 32 | + | func (c *callbackArgs) setInt64Result(result int64) { | |||
| 33 | + | c.result[0] = uintptr(result) | |||
| 34 | + | } | |||
| 35 | + | ||||
| 36 | + | func (c *callbackArgs) setUint64Result(result uint64) { | |||
| 37 | + | c.result[0] = uintptr(result) | |||
| 38 | + | } | |||
M syscall_stackargs_s390x.go +8 -0
| @@ -28,3 +28,11 @@ func (c *callbackArgs) stackFrame() unsafe.Pointer { | |||
| 28 | 28 | func (c *callbackArgs) intFrame() unsafe.Pointer { | |
| 29 | 29 | return nil | |
| 30 | 30 | } | |
| 31 | + | ||
| 32 | + | func (c *callbackArgs) setInt64Result(result int64) { | |
| 33 | + | c.result[0] = uintptr(result) | |
| 34 | + | } | |
| 35 | + | ||
| 36 | + | func (c *callbackArgs) setUint64Result(result uint64) { | |
| 37 | + | c.result[0] = uintptr(result) | |
| 38 | + | } | |
| @@ -28,3 +28,11 @@ func (c *callbackArgs) stackFrame() unsafe.Pointer { | |||||
| 28 | func (c *callbackArgs) intFrame() unsafe.Pointer { | 28 | func (c *callbackArgs) intFrame() unsafe.Pointer { | ||
| 29 | return nil | 29 | return nil | ||
| 30 | } | 30 | } | ||
| 31 | + | ||||
| 32 | + | func (c *callbackArgs) setInt64Result(result int64) { | |||
| 33 | + | c.result[0] = uintptr(result) | |||
| 34 | + | } | |||
| 35 | + | ||||
| 36 | + | func (c *callbackArgs) setUint64Result(result uint64) { | |||
| 37 | + | c.result[0] = uintptr(result) | |||
| 38 | + | } | |||
M syscall_unix.go +18 -5
| @@ -162,11 +162,10 @@ func callbackWrap(a *callbackArgs) { | |||
| 162 | 162 | stackByteOffset := uintptr(0) | |
| 163 | 163 | for i := range args { | |
| 164 | 164 | // slots is the number of pointer-sized slots the argument takes | |
| 165 | - | var slots int | |
| 166 | 165 | inType := fnType.In(i) | |
| 166 | + | slots := int((inType.Size() + ptrSize - 1) / ptrSize) | |
| 167 | 167 | switch inType.Kind() { | |
| 168 | 168 | case reflect.Float32, reflect.Float64: | |
| 169 | - | slots = int((fnType.In(i).Size() + ptrSize - 1) / ptrSize) | |
| 170 | 169 | if floatsN+slots > numOfFloatRegisters() { | |
| 171 | 170 | if isDarwin && runtime.GOARCH == "arm64" { | |
| 172 | 171 | // Darwin ARM64: read from packed stack with proper alignment | |
| @@ -183,6 +182,10 @@ func callbackWrap(a *callbackArgs) { | |||
| 183 | 182 | args[i] = reflect.NewAt(inType, unsafe.Pointer(&stackFrame[stackSlot])).Elem() | |
| 184 | 183 | } | |
| 185 | 184 | stackSlot += slots | |
| 185 | + | } else if isARMFloatPaddingNeeded(inType, -1, stackSlot) { | |
| 186 | + | stackSlot++ | |
| 187 | + | args[i] = reflect.NewAt(inType, unsafe.Pointer(&frame[stackSlot])).Elem() | |
| 188 | + | stackSlot += slots | |
| 186 | 189 | } else { | |
| 187 | 190 | args[i] = reflect.NewAt(inType, unsafe.Pointer(&frame[stackSlot])).Elem() | |
| 188 | 191 | stackSlot += slots | |
| @@ -211,7 +214,9 @@ func callbackWrap(a *callbackArgs) { | |||
| 211 | 214 | args[i] = getCallbackStruct(inType, a.args, &floatsN, &intsN, &stackSlot, &stackByteOffset) | |
| 212 | 215 | continue | |
| 213 | 216 | default: | |
| 214 | - | slots = int((inType.Size() + ptrSize - 1) / ptrSize) | |
| 217 | + | if isARMPaddingNeeded(inType, -1, intsN) { | |
| 218 | + | intsN++ | |
| 219 | + | } | |
| 215 | 220 | if intsN+slots > numOfIntegerRegisters() { | |
| 216 | 221 | if isDarwin && runtime.GOARCH == "arm64" { | |
| 217 | 222 | // Darwin ARM64: read from packed stack with proper alignment | |
| @@ -225,6 +230,10 @@ func callbackWrap(a *callbackArgs) { | |||
| 225 | 230 | args[i] = reflect.NewAt(inType, unsafe.Pointer(&stackFrame[stackSlot])).Elem() | |
| 226 | 231 | } | |
| 227 | 232 | stackSlot += slots | |
| 233 | + | } else if isARMPaddingNeeded(inType, -1, stackSlot) { | |
| 234 | + | stackSlot++ | |
| 235 | + | args[i] = reflect.NewAt(inType, unsafe.Pointer(&frame[stackSlot])).Elem() | |
| 236 | + | stackSlot += slots | |
| 228 | 237 | } else { | |
| 229 | 238 | args[i] = reflect.NewAt(inType, unsafe.Pointer(&frame[stackSlot])).Elem() | |
| 230 | 239 | stackSlot += slots | |
| @@ -249,9 +258,13 @@ func callbackWrap(a *callbackArgs) { | |||
| 249 | 258 | ret := fn.Call(args) | |
| 250 | 259 | if len(ret) > 0 { | |
| 251 | 260 | switch k := ret[0].Kind(); k { | |
| 252 | - | case reflect.Uint, reflect.Uint64, reflect.Uint32, reflect.Uint16, reflect.Uint8, reflect.Uintptr: | |
| 261 | + | case reflect.Uint64: | |
| 262 | + | a.setUint64Result(ret[0].Uint()) | |
| 263 | + | case reflect.Uint, reflect.Uint32, reflect.Uint16, reflect.Uint8, reflect.Uintptr: | |
| 253 | 264 | a.result[0] = uintptr(ret[0].Uint()) | |
| 254 | - | case reflect.Int, reflect.Int64, reflect.Int32, reflect.Int16, reflect.Int8: | |
| 265 | + | case reflect.Int64: | |
| 266 | + | a.setInt64Result(ret[0].Int()) | |
| 267 | + | case reflect.Int, reflect.Int32, reflect.Int16, reflect.Int8: | |
| 255 | 268 | a.result[0] = uintptr(ret[0].Int()) | |
| 256 | 269 | case reflect.Bool: | |
| 257 | 270 | if ret[0].Bool() { | |
| @@ -162,11 +162,10 @@ func callbackWrap(a *callbackArgs) { | |||||
| 162 | stackByteOffset := uintptr(0) | 162 | stackByteOffset := uintptr(0) | ||
| 163 | for i := range args { | 163 | for i := range args { | ||
| 164 | // slots is the number of pointer-sized slots the argument takes | 164 | // slots is the number of pointer-sized slots the argument takes | ||
| 165 | - | var slots int | |||
| 166 | inType := fnType.In(i) | 165 | inType := fnType.In(i) | ||
| 166 | + | slots := int((inType.Size() + ptrSize - 1) / ptrSize) | |||
| 167 | switch inType.Kind() { | 167 | switch inType.Kind() { | ||
| 168 | case reflect.Float32, reflect.Float64: | 168 | case reflect.Float32, reflect.Float64: | ||
| 169 | - | slots = int((fnType.In(i).Size() + ptrSize - 1) / ptrSize) | |||
| 170 | if floatsN+slots > numOfFloatRegisters() { | 169 | if floatsN+slots > numOfFloatRegisters() { | ||
| 171 | if isDarwin && runtime.GOARCH == "arm64" { | 170 | if isDarwin && runtime.GOARCH == "arm64" { | ||
| 172 | // Darwin ARM64: read from packed stack with proper alignment | 171 | // Darwin ARM64: read from packed stack with proper alignment | ||
| @@ -183,6 +182,10 @@ func callbackWrap(a *callbackArgs) { | |||||
| 183 | args[i] = reflect.NewAt(inType, unsafe.Pointer(&stackFrame[stackSlot])).Elem() | 182 | args[i] = reflect.NewAt(inType, unsafe.Pointer(&stackFrame[stackSlot])).Elem() | ||
| 184 | } | 183 | } | ||
| 185 | stackSlot += slots | 184 | stackSlot += slots | ||
| 185 | + | } else if isARMFloatPaddingNeeded(inType, -1, stackSlot) { | |||
| 186 | + | stackSlot++ | |||
| 187 | + | args[i] = reflect.NewAt(inType, unsafe.Pointer(&frame[stackSlot])).Elem() | |||
| 188 | + | stackSlot += slots | |||
| 186 | } else { | 189 | } else { | ||
| 187 | args[i] = reflect.NewAt(inType, unsafe.Pointer(&frame[stackSlot])).Elem() | 190 | args[i] = reflect.NewAt(inType, unsafe.Pointer(&frame[stackSlot])).Elem() | ||
| 188 | stackSlot += slots | 191 | stackSlot += slots | ||
| @@ -211,7 +214,9 @@ func callbackWrap(a *callbackArgs) { | |||||
| 211 | args[i] = getCallbackStruct(inType, a.args, &floatsN, &intsN, &stackSlot, &stackByteOffset) | 214 | args[i] = getCallbackStruct(inType, a.args, &floatsN, &intsN, &stackSlot, &stackByteOffset) | ||
| 212 | continue | 215 | continue | ||
| 213 | default: | 216 | default: | ||
| 214 | - | slots = int((inType.Size() + ptrSize - 1) / ptrSize) | 217 | + | if isARMPaddingNeeded(inType, -1, intsN) { |
| 218 | + | intsN++ | |||
| 219 | + | } | |||
| 215 | if intsN+slots > numOfIntegerRegisters() { | 220 | if intsN+slots > numOfIntegerRegisters() { | ||
| 216 | if isDarwin && runtime.GOARCH == "arm64" { | 221 | if isDarwin && runtime.GOARCH == "arm64" { | ||
| 217 | // Darwin ARM64: read from packed stack with proper alignment | 222 | // Darwin ARM64: read from packed stack with proper alignment | ||
| @@ -225,6 +230,10 @@ func callbackWrap(a *callbackArgs) { | |||||
| 225 | args[i] = reflect.NewAt(inType, unsafe.Pointer(&stackFrame[stackSlot])).Elem() | 230 | args[i] = reflect.NewAt(inType, unsafe.Pointer(&stackFrame[stackSlot])).Elem() | ||
| 226 | } | 231 | } | ||
| 227 | stackSlot += slots | 232 | stackSlot += slots | ||
| 233 | + | } else if isARMPaddingNeeded(inType, -1, stackSlot) { | |||
| 234 | + | stackSlot++ | |||
| 235 | + | args[i] = reflect.NewAt(inType, unsafe.Pointer(&frame[stackSlot])).Elem() | |||
| 236 | + | stackSlot += slots | |||
| 228 | } else { | 237 | } else { | ||
| 229 | args[i] = reflect.NewAt(inType, unsafe.Pointer(&frame[stackSlot])).Elem() | 238 | args[i] = reflect.NewAt(inType, unsafe.Pointer(&frame[stackSlot])).Elem() | ||
| 230 | stackSlot += slots | 239 | stackSlot += slots | ||
| @@ -249,9 +258,13 @@ func callbackWrap(a *callbackArgs) { | |||||
| 249 | ret := fn.Call(args) | 258 | ret := fn.Call(args) | ||
| 250 | if len(ret) > 0 { | 259 | if len(ret) > 0 { | ||
| 251 | switch k := ret[0].Kind(); k { | 260 | switch k := ret[0].Kind(); k { | ||
| 252 | - | case reflect.Uint, reflect.Uint64, reflect.Uint32, reflect.Uint16, reflect.Uint8, reflect.Uintptr: | 261 | + | case reflect.Uint64: |
| 262 | + | a.setUint64Result(ret[0].Uint()) | |||
| 263 | + | case reflect.Uint, reflect.Uint32, reflect.Uint16, reflect.Uint8, reflect.Uintptr: | |||
| 253 | a.result[0] = uintptr(ret[0].Uint()) | 264 | a.result[0] = uintptr(ret[0].Uint()) | ||
| 254 | - | case reflect.Int, reflect.Int64, reflect.Int32, reflect.Int16, reflect.Int8: | 265 | + | case reflect.Int64: |
| 266 | + | a.setInt64Result(ret[0].Int()) | |||
| 267 | + | case reflect.Int, reflect.Int32, reflect.Int16, reflect.Int8: | |||
| 255 | a.result[0] = uintptr(ret[0].Int()) | 268 | a.result[0] = uintptr(ret[0].Int()) | ||
| 256 | case reflect.Bool: | 269 | case reflect.Bool: | ||
| 257 | if ret[0].Bool() { | 270 | if ret[0].Bool() { | ||
M testdata/abitest/abi_test.c +21 -0
| @@ -167,3 +167,24 @@ double stack_32_mixed_int_float( | |||
| 167 | 167 | f9 * 25 + f10 * 26 + f11 * 27 + f12 * 28 + | |
| 168 | 168 | f13 * 29 + f14 * 30 + f15 * 31 + f16 * 32; | |
| 169 | 169 | } | |
| 170 | + | ||
| 171 | + | int64_t arm_int64_unaligned_in_registers(uintptr_t a1, int64_t a2) { | |
| 172 | + | return (int64_t)a1 * 123 + a2; | |
| 173 | + | } | |
| 174 | + | ||
| 175 | + | int64_t arm_int64_unaligned_on_stack(uintptr_t a1, uintptr_t a2, uintptr_t a3, uintptr_t a4, uintptr_t a5, int64_t a6) { | |
| 176 | + | return (int64_t)a1 * 1 + (int64_t)a2 * 2 + (int64_t)a3 * 3 + (int64_t)a4 * 4 + | |
| 177 | + | (int64_t)a5 * 5 + a6; | |
| 178 | + | } | |
| 179 | + | ||
| 180 | + | int64_t arm_int64_unaligned_on_stack_after_floats( | |
| 181 | + | uintptr_t a1, uintptr_t a2, uintptr_t a3, uintptr_t a4, /* these args are going to integer registers */ | |
| 182 | + | float f1, float f2, float f3, float f4, /* these args are going to float registers */ | |
| 183 | + | float f5, float f6, float f7, float f8, | |
| 184 | + | float f9, float f10, float f11, float f12, | |
| 185 | + | float f13, float f14, float f15, float f16, | |
| 186 | + | float f17, /* this is a first arg on stack */ | |
| 187 | + | int64_t a5 /* this arg must be padded */ | |
| 188 | + | ) { | |
| 189 | + | return (int64_t)a1 * 1 + (int64_t)a2 * 2 + (int64_t)a3 * 3 + (int64_t)a4 * 4 + a5; | |
| 190 | + | } | |
| @@ -167,3 +167,24 @@ double stack_32_mixed_int_float( | |||||
| 167 | f9 * 25 + f10 * 26 + f11 * 27 + f12 * 28 + | 167 | f9 * 25 + f10 * 26 + f11 * 27 + f12 * 28 + | ||
| 168 | f13 * 29 + f14 * 30 + f15 * 31 + f16 * 32; | 168 | f13 * 29 + f14 * 30 + f15 * 31 + f16 * 32; | ||
| 169 | } | 169 | } | ||
| 170 | + | ||||
| 171 | + | int64_t arm_int64_unaligned_in_registers(uintptr_t a1, int64_t a2) { | |||
| 172 | + | return (int64_t)a1 * 123 + a2; | |||
| 173 | + | } | |||
| 174 | + | ||||
| 175 | + | int64_t arm_int64_unaligned_on_stack(uintptr_t a1, uintptr_t a2, uintptr_t a3, uintptr_t a4, uintptr_t a5, int64_t a6) { | |||
| 176 | + | return (int64_t)a1 * 1 + (int64_t)a2 * 2 + (int64_t)a3 * 3 + (int64_t)a4 * 4 + | |||
| 177 | + | (int64_t)a5 * 5 + a6; | |||
| 178 | + | } | |||
| 179 | + | ||||
| 180 | + | int64_t arm_int64_unaligned_on_stack_after_floats( | |||
| 181 | + | uintptr_t a1, uintptr_t a2, uintptr_t a3, uintptr_t a4, /* these args are going to integer registers */ | |||
| 182 | + | float f1, float f2, float f3, float f4, /* these args are going to float registers */ | |||
| 183 | + | float f5, float f6, float f7, float f8, | |||
| 184 | + | float f9, float f10, float f11, float f12, | |||
| 185 | + | float f13, float f14, float f15, float f16, | |||
| 186 | + | float f17, /* this is a first arg on stack */ | |||
| 187 | + | int64_t a5 /* this arg must be padded */ | |||
| 188 | + | ) { | |||
| 189 | + | return (int64_t)a1 * 1 + (int64_t)a2 * 2 + (int64_t)a3 * 3 + (int64_t)a4 * 4 + a5; | |||
| 190 | + | } | |||