Skip to content

purego: support structs on Windows amd64/arm64#465

Open
hajimehoshi wants to merge 1 commit into
ebitengine:mainfrom
hajimehoshi:claude/eager-mcnulty-e8a278
Open

purego: support structs on Windows amd64/arm64#465
hajimehoshi wants to merge 1 commit into
ebitengine:mainfrom
hajimehoshi:claude/eager-mcnulty-e8a278

Conversation

@hajimehoshi

Copy link
Copy Markdown
Member

What issue is this addressing?

Closes #237

What type of issue is this addressing?

feature

What this PR does | solves

Adds support for passing and returning C structs by value on Windows
for forward calls (Go calling C), on both amd64 and arm64.
Previously RegisterFunc/RegisterLibFunc panicked with
struct arguments/returns are only supported on darwin and linux on
Windows, so callers had to manually decompose structs into uintptrs
or pass pointers.

amd64 (Win64 ABI)

Windows amd64 goes through the standard library syscall.SyscallN,
which only carries positional uintptr arguments. This is sufficient
because the Win64 struct ABI never uses XMM registers for struct
fields:

  • Arguments: aggregates of exactly 1, 2, 4, or 8 bytes are passed
    by value in a single integer slot; all other sizes are passed as a
    pointer to a caller-allocated copy.
  • Returns: 1/2/4/8-byte aggregates come back in RAX; every other
    size is returned through a caller-allocated hidden pointer (which the
    callee also returns in RAX).

This is implemented as a small Win64-specific branch in
struct_amd64.go plus an amd64StructReturnInMemory helper, since the
existing System V eightbyte-classification path cannot be reused.

arm64 (AAPCS64)

Windows arm64 already runs through purego's own assembly
(sys_arm64.s includes windows and loads X0–X7/F0–F7/R8/stack),
and the AAPCS64 placement logic in struct_arm64.go has no GOOS
constraint. Windows arm64 follows AAPCS64 for structs in non-variadic
functions, so only the support gate needed to be opened — no new ABI
code.

Not supported: structs in callbacks

Passing or returning structs in callbacks created with NewCallback
remains unsupported on Windows, because Windows callbacks go through
the standard library syscall.NewCallback, which cannot carry struct
values. This is documented in the RegisterFunc godoc and as a README
support note.

Testing

  • struct_test.go now builds on Windows (amd64/arm64); the callback
    sub-implementation is skipped there, exercising only forward calls.
  • The CI matrix already runs go test ./... on windows-latest
    (amd64, with gcc), so the forward-call struct tests are exercised
    there automatically. Windows arm64 is compile-checked only (no arm64
    Windows runner).
  • Verified cross-compilation for windows amd64/arm64/386 and all
    existing targets, and that the existing struct tests still pass on
    darwin/arm64.

Notes for reviewers

  • Win64 requires by-pointer struct copies to be 16-byte aligned;
    reflect.New aligns to the type (often 8). This is fine for
    scalar-field structs in practice but is a candidate follow-up if a
    vector-type case ever appears.
  • The README support-note footnotes were renumbered into reading order
    to accommodate the two new struct notes.
  • iOS and Android are documented as not supporting structs (their
    runtime.GOOS is ios/android, not darwin/linux); enabling
    them is left as a separate change.

🤖 Generated with Claude Code

@hajimehoshi hajimehoshi force-pushed the claude/eager-mcnulty-e8a278 branch 2 times, most recently from a81e3a8 to 01d358a Compare June 16, 2026 18:16
Copilot AI review requested due to automatic review settings June 16, 2026 18:16

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.

Pull request overview

Adds Windows support for passing/returning C structs by value for forward calls (Go → C) on amd64 and arm64, and updates tests/docs accordingly.

Changes:

  • Enable struct argument/return support gating for Windows in RegisterFunc and add Win64-specific struct ABI handling on amd64.
  • Update struct tests to run on Windows and use a cross-platform library loader helper.
  • Update README/platform notes and public docs to reflect Windows struct support limitations (no struct callbacks on Windows).

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
func.go Extends struct support gating to Windows; adds amd64StructReturnInMemory helper and adjusts empty-struct arg handling on Windows.
struct_amd64.go Implements Win64-specific struct argument passing and struct return reconstruction logic.
struct_test.go Enables Windows builds for struct tests; switches to internal/load and skips callback-based struct testing on Windows.
testdata/structtest/struct_test.c Normalizes integer/pointer types for consistent ABI sizing across platforms (incl. Windows).
README.md Updates support matrix/footnotes to document Windows struct support for forward calls and callback limitation.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread testdata/structtest/struct_test.c
Comment thread testdata/structtest/struct_test.c

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.

Pull request overview

Copilot reviewed 5 out of 5 changed files in this pull request and generated 3 comments.

Comment thread struct_amd64.go
Comment thread testdata/structtest/struct_test.c
Comment thread struct_test.go Outdated
@hajimehoshi hajimehoshi force-pushed the claude/eager-mcnulty-e8a278 branch from b365dec to 7c7a011 Compare June 16, 2026 18:58
@hajimehoshi

Copy link
Copy Markdown
Member Author

@TotallyGamerJet PTAL

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.

Pull request overview

Copilot reviewed 5 out of 5 changed files in this pull request and generated 2 comments.

Comment thread struct_test.go Outdated
Comment thread struct_test.go Outdated

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.

Pull request overview

Copilot reviewed 5 out of 5 changed files in this pull request and generated no new comments.

@gdams

gdams commented Jun 19, 2026

Copy link
Copy Markdown
Contributor

FYI if you add windows-11-arm to the GitHub actions testing matrix you can test on native Windows arm64

@hajimehoshi hajimehoshi force-pushed the claude/eager-mcnulty-e8a278 branch from 9027911 to a717c8d Compare June 19, 2026 12:10
@hajimehoshi

Copy link
Copy Markdown
Member Author

FYI if you add windows-11-arm to the GitHub actions testing matrix you can test on native Windows arm64

Thanks, done

Enable passing and returning C structs by value on Windows for
forward calls (Go calling C).

On amd64 the Win64 ABI is used: aggregates of exactly 1, 2, 4, or 8
bytes are passed by value in a single integer slot and returned in
RAX, while all other sizes are passed and returned through a
caller-allocated pointer. This maps cleanly onto the positional
syscall.SyscallN path and needs no float-register handling, because
Win64 never passes struct fields in XMM registers. Unlike the System
V ABI, an empty struct argument still consumes a slot on Win64.

On arm64 the existing AAPCS64 path already runs on Windows via the
shared assembly, so only the support gate is opened.

Passing or returning structs in callbacks created with NewCallback
is not supported on Windows, because they go through the standard
library syscall.NewCallback, which cannot carry struct values.

The struct test C library used `long` for values that must be 64-bit,
which is only 32-bit on Windows (LLP64) and overflowed the 64-bit
shift expressions and shrank struct sizes. Replace it with
width-correct stdint types: int64_t/uint64_t for Go's fixed-width
int64/uint64, and intptr_t/uintptr_t for Go's word-sized int/uint, so
the library is correct under both LP64 and LLP64.

Closes ebitengine#237

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@hajimehoshi

hajimehoshi commented Jun 19, 2026

Copy link
Copy Markdown
Member Author

Unfortunately, the msys tools on GitHub Windows Arm are not for Arm64 but for x86_64, then they don't work. I'll make a following PR later after this PR. I'll revert adding Windows Arm from this PR.

@hajimehoshi hajimehoshi force-pushed the claude/eager-mcnulty-e8a278 branch from a717c8d to fd6436b Compare June 19, 2026 12:18
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.

Struct support for Windows

3 participants