Skip to content
Open
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
28 changes: 23 additions & 5 deletions struct_arm64.go
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,11 @@ func addStruct(v reflect.Value, numInts, numFloats, numStack *int, addInt, addFl
*numFloats = numOfFloatRegisters()
} else if hva && *numInts+numABIFields(v.Type()) > numOfIntegerRegisters() {
*numInts = numOfIntegerRegisters()
} else if !hfa && !hva && !isDarwin && *numInts+int(roundUpTo8(size)/8) > numOfIntegerRegisters() {
// Not enough integer registers for all of the eightbytes,
// so the whole struct goes on the stack (AAPCS64). Darwin
// makes the same decision in shouldBundleStackArgs.
*numInts = numOfIntegerRegisters()
}

placeRegisters(v, addFloat, addInt)
Expand All @@ -107,6 +112,18 @@ func placeRegisters(v reflect.Value, addFloat func(uintptr), addInt func(uintptr
}

func placeRegistersArm64(v reflect.Value, addFloat func(uintptr), addInt func(uintptr)) {
// A non-HFA/HVA composite of 16 bytes or less is passed as
// consecutive chunks of its in-memory image, not routed by
// member kind (AAPCS64; mirrors getCallbackStruct).
if !isHFA(v.Type()) && !isHVA(v.Type()) && v.Type().Size() <= 16 {
if !v.CanAddr() {
tmp := reflect.New(v.Type()).Elem()
tmp.Set(v)
v = tmp
}
copyStruct8ByteChunks(v.Addr().UnsafePointer(), v.Type().Size(), addInt)
return
}
var val uint64
var shift byte
var flushed bool
Expand Down Expand Up @@ -310,12 +327,13 @@ func isHVA(t reflect.Type) bool {
}
}

// copyStruct8ByteChunks copies struct memory in 8-byte chunks to the provided callback.
// This is used for Darwin ARM64's byte-level packing of non-HFA/HVA structs.
// copyStruct8ByteChunks copies struct memory in 8-byte chunks to the provided
// callback. The final partial chunk is read byte-by-byte so that nothing beyond
// the value's allocation is touched, and is zero-extended.
func copyStruct8ByteChunks(ptr unsafe.Pointer, size uintptr, addChunk func(uintptr)) {
if !isDarwin {
panic("purego: should only be called on darwin")
}
// Darwin's byte-level packing and the AAPCS64 rule for non-HFA/HVA
// composites of 16 bytes or less both pass consecutive chunks of the
// in-memory image, so the two paths share this helper.
for offset := uintptr(0); offset < size; offset += 8 {
var chunk uintptr
remaining := size - offset
Expand Down
29 changes: 29 additions & 0 deletions struct_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -853,6 +853,35 @@ func TestRegisterFunc_structArgs(t *testing.T) {
t.Fatalf("IdentityFloatAndInt returned %+v wanted %+v", ret, expected)
}
}
{
// A mixed {int64; float64}: on arm64 both eightbytes
// must reach the callee in integer registers, never
// x0/v0 (AAPCS64); other ABIs split them by class.
type Int64AndDouble struct {
_ structs.HostLayout
A int64
B float64
}
var fn func(Int64AndDouble) Int64AndDouble
register(&fn, lib, "IdentityInt64AndDouble", func(s Int64AndDouble) Int64AndDouble {
return s
})
expected := Int64AndDouble{A: -1234, B: 5.25}
if ret := fn(expected); ret != expected {
t.Fatalf("IdentityInt64AndDouble returned %+v wanted %+v", ret, expected)
}
if runtime.GOARCH == "arm64" {
// Only x7 is left, so the whole struct goes
// on the stack, also on Darwin.
var fn func(int64, int64, int64, int64, int64, int64, int64, Int64AndDouble) Int64AndDouble
register(&fn, lib, "IdentityInt64AndDoubleAfterRegisters", func(a, b, c, d, e, f, g int64, s Int64AndDouble) Int64AndDouble {
return s
})
if ret := fn(1, 2, 3, 4, 5, 6, 7, expected); ret != expected {
t.Fatalf("IdentityInt64AndDoubleAfterRegisters returned %+v wanted %+v", ret, expected)
}
}
}
{
// Struct > 16 bytes: hidden pointer on amd64, pointer in int register on arm64.
type ThreeInt64 struct {
Expand Down
13 changes: 13 additions & 0 deletions testdata/structtest/struct_test.c
Original file line number Diff line number Diff line change
Expand Up @@ -417,6 +417,19 @@ struct FloatAndInt IdentityFloatAndInt(struct FloatAndInt s) {
return s;
}

struct Int64AndDouble {
int64_t a;
double b;
};

struct Int64AndDouble IdentityInt64AndDouble(struct Int64AndDouble s) {
return s;
}

struct Int64AndDouble IdentityInt64AndDoubleAfterRegisters(int64_t a, int64_t b, int64_t c, int64_t d, int64_t e, int64_t f, int64_t g, struct Int64AndDouble s) {
return s;
}

struct ThreeInt64 {
int64_t a, b, c;
};
Expand Down
Loading