Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ Tier 2 platforms are supported by PureGo on a best-effort basis. Critical bugs o

- **Android**: 386<sup>1,3</sup>, arm<sup>1,3</sup>
- **FreeBSD**: amd64<sup>3,4</sup>, arm64<sup>3,4</sup>
- **Linux**: 386<sup>3</sup>, arm<sup>3</sup>, loong64<sup>2</sup>, ppc64le<sup>3</sup>, riscv64<sup>3</sup>, s390x<sup>3, 5</sup>
- **Linux**: 386<sup>3</sup>, arm<sup>3</sup>, loong64<sup>2</sup>, ppc64le<sup>2</sup>, riscv64<sup>3</sup>, s390x<sup>3, 5</sup>
- **NetBSD**: amd64<sup>3,4</sup>, arm64<sup>3,4</sup>
- **Windows**: 386<sup>3,6</sup>, arm<sup>3,6,7</sup>

Expand Down
6 changes: 4 additions & 2 deletions export_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,12 @@

package purego

import "reflect"

// MaxArgs re-exports maxArgs for external tests.
const MaxArgs = maxArgs

// StructReturnInMemory re-exports structReturnInMemory for external tests.
func StructReturnInMemory(size uintptr) bool {
return structReturnInMemory(size)
func StructReturnInMemory(outType reflect.Type) bool {
return structReturnInMemory(outType)
}
18 changes: 11 additions & 7 deletions func.go
Original file line number Diff line number Diff line change
Expand Up @@ -199,7 +199,7 @@ func RegisterFunc(fptr any, cfn uintptr) {
ensureStructSupported()
outType := ty.Out(0)
checkStructFieldsSupported(outType)
if structReturnInMemory(outType.Size()) {
if structReturnInMemory(outType) {
// A struct returned in memory is allocated by the caller and its
// pointer is passed as a hidden first integer argument. When the
// integer registers are already full, prepending it spills a
Expand Down Expand Up @@ -292,7 +292,7 @@ func RegisterFunc(fptr any, cfn uintptr) {
var arm64_r8 uintptr
if ty.NumOut() == 1 && ty.Out(0).Kind() == reflect.Struct {
outType := ty.Out(0)
if structReturnInMemory(outType.Size()) {
if structReturnInMemory(outType) {
// The caller allocates the return value and passes its pointer
// as a hidden first integer argument.
val := reflect.New(outType)
Expand Down Expand Up @@ -431,10 +431,14 @@ func addValue(v reflect.Value, keepAlive []any, addInt func(x uintptr), addFloat
addInt(0)
}
case reflect.Float32:
// On S390X big-endian, float32 goes in upper 32 bits of 64-bit FP register
if runtime.GOARCH == "s390x" {
switch runtime.GOARCH {
case "ppc64le":
// A single-precision argument occupies a floating-point register in double format on Power.
addFloat(uintptr(math.Float64bits(v.Float())))
case "s390x":
// S390X big-endian: float32 goes in the upper 32 bits of the 64-bit FP register.
addFloat(uintptr(math.Float32bits(float32(v.Float()))) << 32)
} else {
default:
addFloat(uintptr(math.Float32bits(float32(v.Float()))))
}
case reflect.Float64:
Expand Down Expand Up @@ -510,9 +514,9 @@ func checkStructFieldsSupported(ty reflect.Type) {
// a C function is unsupported on the current platform.
func ensureStructSupported() {
switch runtime.GOARCH {
case "amd64", "arm64", "loong64":
case "amd64", "arm64", "loong64", "ppc64le":
default:
panic("purego: struct arguments/returns are only supported on amd64, arm64, and loong64")
panic("purego: struct arguments/returns are only supported on amd64, arm64, loong64, and ppc64le")
}
switch runtime.GOOS {
case "android", "darwin", "ios", "linux", "windows":
Expand Down
2 changes: 1 addition & 1 deletion func_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -604,7 +604,7 @@ func TestABI_StructReturnHiddenPointer(t *testing.T) {
// than sysargs holds and RegisterFunc must reject the registration.
type bigStruct struct{ A, B, C uint64 } // larger than two eightbytes

if !purego.StructReturnInMemory(reflect.TypeFor[bigStruct]().Size()) {
if !purego.StructReturnInMemory(reflect.TypeFor[bigStruct]()) {
t.Skipf("GOARCH=%s does not return large structs via a hidden integer argument", runtime.GOARCH)
}

Expand Down
5 changes: 3 additions & 2 deletions struct_amd64.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ import (
// structReturnInMemory reports whether a struct return value of the given size
// is returned through a caller-allocated hidden pointer passed as the first
// integer argument (true) rather than in registers (false).
func structReturnInMemory(size uintptr) bool {
func structReturnInMemory(outType reflect.Type) bool {
size := outType.Size()
if size == 0 {
return false
}
Expand All @@ -38,7 +39,7 @@ func getStruct(outType reflect.Type, syscall syscallArgs) (v reflect.Value) {
switch {
case outSize == 0:
return reflect.New(outType).Elem()
case structReturnInMemory(outSize):
case structReturnInMemory(outType):
// Returned through the caller-allocated hidden pointer, which the
// callee also returns in RAX.
return reflect.NewAt(outType, *(*unsafe.Pointer)(unsafe.Pointer(&syscall.a1))).Elem()
Expand Down
2 changes: 1 addition & 1 deletion struct_arm.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ func addStruct(v reflect.Value, numInts, numFloats, numStack *int, addInt, addFl
// structReturnInMemory always reports false on arm: an indirect struct return
// is recovered from the pointer the callee leaves in a1 (see getStruct) rather
// than through a caller-allocated hidden first argument.
func structReturnInMemory(size uintptr) bool {
func structReturnInMemory(reflect.Type) bool {
return false
}

Expand Down
2 changes: 1 addition & 1 deletion struct_arm64.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import (
// structReturnInMemory always reports false on arm64: a struct returned in
// memory is passed through the dedicated indirect result register (R8), not as
// an ordinary integer argument, so it is handled separately.
func structReturnInMemory(size uintptr) bool {
func structReturnInMemory(reflect.Type) bool {
return false
}

Expand Down
10 changes: 5 additions & 5 deletions struct_loong64.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,11 +72,11 @@ func loong64Classify(t reflect.Type) (leaves []loong64Leaf, useFP bool) {
}
}

// structReturnInMemory reports whether a struct return value of the given size
// is returned through a caller-allocated hidden pointer passed as the first
// integer argument. Aggregates larger than two eightbytes are returned in memory.
func structReturnInMemory(size uintptr) bool {
return size > maxRegAllocStructSize
// structReturnInMemory reports whether a struct return value is returned through
// a caller-allocated hidden pointer passed as the first integer argument.
// Aggregates larger than two eightbytes are returned in memory.
func structReturnInMemory(outType reflect.Type) bool {
return outType.Size() > maxRegAllocStructSize
}

func getStruct(outType reflect.Type, syscall syscallArgs) reflect.Value {
Expand Down
8 changes: 4 additions & 4 deletions struct_other.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,10 @@ func getStruct(outType reflect.Type, syscall syscallArgs) reflect.Value {
panic("purego: struct returns are not supported on this architecture")
}

// structReturnInMemory reports whether a struct return value of the given size
// is returned through a caller-allocated hidden pointer. Structs are unsupported
// on this architecture, so it always reports false.
func structReturnInMemory(size uintptr) bool {
// structReturnInMemory reports whether a struct return value is returned through
// a caller-allocated hidden pointer. Structs are unsupported on this
// architecture, so it always reports false.
func structReturnInMemory(reflect.Type) bool {
return false
}

Expand Down
Loading
Loading