Skip to content

purego: pass non-HFA arm64 structs in integer registers - #517

Open
kumagi wants to merge 2 commits into
ebitengine:mainfrom
kumagi:fix/arm64-mixed-struct
Open

purego: pass non-HFA arm64 structs in integer registers#517
kumagi wants to merge 2 commits into
ebitengine:mainfrom
kumagi:fix/arm64-mixed-struct

Conversation

@kumagi

@kumagi kumagi commented Sep 5, 2026

Copy link
Copy Markdown
Contributor

What issue is this addressing?

Closes #522

What type of issue is this addressing?

bug

What this PR does | solves

placeRegistersArm64 routed float/64-bit members straight to FP or integer registers by kind, so mixed structs such as {int64; float64} went out on x0/v0 while AAPCS64 (and our own getCallbackStruct) expect them packed into x0/x1. Copy the in-memory image eightbyte by eightbyte for non-HFA/HVA aggregates of 16 bytes or less.

@hajimehoshi

Copy link
Copy Markdown
Member

Fix the test fail

placeRegistersArm64 routed float/64-bit members straight to FP or
integer registers by kind, so mixed structs such as {int64; float64}
went out on x0/v0 while AAPCS64 (and our own getCallbackStruct)
expect them packed into x0/x1. Copy the in-memory image eightbyte by
eightbyte for non-HFA/HVA aggregates of 16 bytes or less.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

The new packing path can still split a single struct across registers and stack on integer-register overflow, conflicting with the file’s own all-or-nothing overflow handling in getCallbackStruct.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Pull request overview

Adjusts ARM64 (non-Darwin) struct argument register placement to match AAPCS64 for small non-HFA/HVA aggregates, fixing mixed-member structs that were previously split across GPR/FPR.

Changes:

  • Adds a non-HFA/HVA <=16-byte fast path that copies the struct’s in-memory image in 8-byte chunks into integer registers.
  • Avoids routing fields to FP vs integer registers purely by kind for these small non-HFA/HVA aggregates.
File summaries
File Description
struct_arm64.go Packs small non-HFA/HVA aggregates into 1–2 integer-register chunks based on in-memory layout (ARM64 ABI alignment).
Review details
  • Files reviewed: 1/1 changed files
  • Comments generated: 2
  • Review effort level: Lite

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread struct_arm64.go
Comment thread struct_arm64.go
…stack

addStruct packed small non-HFA/HVA structs into integer registers
eightbyte by eightbyte, so when fewer registers than eightbytes
remained, the struct was split across the last register and the stack.
AAPCS64 and getCallbackStruct instead pass such a struct entirely on
the stack, so exhaust the integer registers first to force whole-struct
stack placement (Darwin keeps its splitting convention).

Also add arm64 round-trip tests for struct{int64; double}, including
the register-overflow case, to cover the ebitengine#522 regression.
@kumagi

kumagi commented Sep 6, 2026

Copy link
Copy Markdown
Contributor Author

Addressed both review comments:

  1. Small non-HFA/HVA structs that don't fit entirely in the remaining integer registers are now forced to the stack as a whole. addStruct exhausts the integer registers before placement in that case, mirroring the all-or-nothing rule in getCallbackStruct (AAPCS64). Darwin is unchanged since Apple's ABI does allow splitting a struct between registers and the stack.
  2. Added arm64 round-trip tests for struct{ int64; double } (issue arm64 sends mixed non-HFA structs on x0/v0 instead of x0/x1 #522's case): a plain identity call plus a variant with seven leading int64 arguments that exercises the register-overflow path. Both run as Go→C identity (RegisterLibFunc) and Go→C→Go callback (GoCallbackFunc).

Verified under qemu-aarch64 against real compiled C: the new tests fail on the pre-fix code (float dropped to v0 / struct split across x7+stack) and pass now. The full test suite passes on linux/amd64 and on linux/arm64 (CGO enabled and disabled). Tests should be green.

Comment thread struct_test.go
if ret := fn(expected); ret != expected {
t.Fatalf("IdentityInt64AndDouble returned %+v wanted %+v", ret, expected)
}
if runtime.GOOS != "darwin" {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What happens with the below function on Darwin?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

arm64 sends mixed non-HFA structs on x0/v0 instead of x0/x1

3 participants