Skip to content

purego: extend struct argument support to Linux amd64/arm64#361

Merged
hajimehoshi merged 15 commits intoebitengine:mainfrom
tmc:linux-struct-support
Feb 12, 2026
Merged

purego: extend struct argument support to Linux amd64/arm64#361
hajimehoshi merged 15 commits intoebitengine:mainfrom
tmc:linux-struct-support

Conversation

@tmc
Copy link
Contributor

@tmc tmc commented Oct 30, 2025

What issue is this addressing?

Extends struct argument support to Linux amd64 and arm64 platforms.

What type of issue is this addressing?

enhancement

What this PR does | solves

This PR removes the Darwin-only restriction on struct arguments and enables struct-by-value support on Linux for both amd64 and arm64 architectures.

Closes #236, #412

@hajimehoshi
Copy link
Member

Write an issue number this PR works on.

@TotallyGamerJet
Copy link
Collaborator

Write an issue number this PR works on.

I believe this closes #236

@hajimehoshi
Copy link
Member

Thanks. Note that, as #236 is a new feature rather than a bug fix, I'm not going to backport this fix to the stable branch.

func.go Outdated
Copy link
Member

Choose a reason for hiding this comment

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

I think this comment is redundant. Create a utility function like isStructSupportedForRegisterFunc and use it.

func.go Outdated
Copy link
Member

Choose a reason for hiding this comment

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

My suggested function isStructSupportedForRegisterFunc should also be available here

@tmc tmc force-pushed the linux-struct-support branch from 29b5a8f to 3c62bd1 Compare October 31, 2025 18:36
@tmc
Copy link
Contributor Author

tmc commented Oct 31, 2025

rebased and I believe have addressed outstanding comments

Copy link
Member

Choose a reason for hiding this comment

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

This comment is redundant.

Copy link
Member

Choose a reason for hiding this comment

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

This comment is redundant.

struct_test.go Outdated
Copy link
Member

Choose a reason for hiding this comment

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

What is checkptr?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

as in go tool compile -dcheckptr which go test -race sets:
go tool compile -d help

Copy link
Contributor Author

Choose a reason for hiding this comment

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

but I think this can be simplified, 1m.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

hm yeah actually this causes go vet failures like this:

./struct_test.go:510:48: possible misuse of unsafe.Pointer
./struct_test.go:525:37: possible misuse of unsafe.Pointer
./struct_test.go:525:59: possible misuse of unsafe.Pointer

Copy link
Member

@hajimehoshi hajimehoshi left a comment

Choose a reason for hiding this comment

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

LGTM, thanks!

@TotallyGamerJet Please take a look

struct_test.go Outdated
Copy link
Member

Choose a reason for hiding this comment

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

Optional:

const expectedSum = 10 + 20 + 30 + 63

@JupiterRider
Copy link
Contributor

JupiterRider commented Nov 1, 2025

Are there any limitations like nested structs, float32 struct members, or the order of the struct members?

@tmc
Copy link
Contributor Author

tmc commented Nov 2, 2025

Are there any limitations like nested structs, float32 struct members, or the order of the struct members?

I don't think so -- certainly welcome including more edge cases in tests.

JupiterRider added a commit to JupiterRider/purego that referenced this pull request Nov 8, 2025
@JupiterRider
Copy link
Contributor

JupiterRider commented Nov 8, 2025

@tmc I created a new testcase which fails:

=== RUN   TestRegisterFunc_structReturns
    struct_test.go:537: Build /tmp/TestRegisterFunc_structReturns467303311/001/structreturntest.so
    struct_test.go:802: ReturnMixed5 returned {a:0xc00032c1b0 b:1 c:0 d:1088841318} wanted {a:0xc00032c1b0 b:1 c:7.2 d:9}
--- FAIL: TestRegisterFunc_structReturns (0.08s)

I also noticed that string and bool struct members are not supported:

panic: purego: unsupported kind string
panic: purego: unsupported kind bool

@tmc
Copy link
Contributor Author

tmc commented Nov 8, 2025

Thanks, I'll get that incorporated and addressed (at least the bool case).

@tmc
Copy link
Contributor Author

tmc commented Nov 8, 2025

string handling is a bit more involved and I think that should be done in a follow-on commit but I have bool support in now.

@tmc
Copy link
Contributor Author

tmc commented Nov 8, 2025

note: I have string support in https://github.com/tmc/purego/tree/add-string-field-support -- will open a PR for it after this goes in

@pekim
Copy link

pekim commented Feb 6, 2026

I wonder, is there any likelihood of this being merged soon? It was approved over 3 months ago.

I appreciate that there have been lots of other great PRs being reviewed and merged lately, and that this one might not be a priority. But I wonder whether it has been forgotten?

@hajimehoshi
Copy link
Member

@tmc doesn't this require rebasing?

@TotallyGamerJet ping

tmc added 7 commits February 7, 2026 10:51
Expands struct argument and return value support from Darwin-only to both
Darwin and Linux on amd64 and arm64 architectures. This change enables
purego to handle struct marshalling on additional platforms while
maintaining the existing safety checks for unsupported architectures.

Updates platform validation logic to check both GOOS and GOARCH, ensuring
struct support is only enabled on tested platform combinations. The build
constraints for struct tests are updated accordingly to include Linux.

Adds test coverage for pointer wrapper structs to validate proper handling
of single and multiple pointer fields within structs, ensuring correct
register allocation and marshalling across the expanded platform support.
Addresses platform-specific differences in struct argument tests between
Darwin and Linux by using consistent data types and avoiding sign
extension issues.

Changes the C struct field from int8_t to char for better cross-platform
consistency, and updates test values to use all positive numbers to avoid
sign extension differences between platforms. Also simplifies the build
constraint logic for better readability.

These changes ensure struct tests pass reliably on both Darwin and Linux
while maintaining the same test coverage.
Refactors RegisterFunc to use a shared isStructSupportedForRegisterFunc helper
function instead of duplicating the platform validation logic for both struct
arguments and return values. This improves code maintainability while
preserving the same validation behavior.
Changes the Array4Chars struct field from char to signed char to ensure
consistent signedness across platforms. The signedness of plain char is
implementation-defined and can vary between compilers and platforms,
potentially causing test inconsistencies.

Using signed char makes the signedness explicit and ensures consistent
behavior in struct marshalling tests across Darwin and Linux platforms.
tmc added 5 commits February 7, 2026 10:51
Removes descriptive comments from PointerWrapper and TwoPointers struct
tests in both Go and C code as the test purpose is clear from context
and variable names.
Replaces dynamic memory allocation with constant uintptr values in
PointerWrapper and TwoPointers tests. This eliminates the need for
runtime.KeepAlive calls and makes the tests more deterministic by
using fixed pointer values instead of heap-allocated objects.
Uses stack-allocated variables instead of uintptr constants to fix go vet
warnings about misuse of unsafe.Pointer. The new approach is simpler than
heap allocation (no runtime.KeepAlive needed) while still being correct.
Extends struct field type support to include boolean fields by adding
reflect.Bool to the list of supported types in checkStructFieldsSupported.

Adds comprehensive test coverage for boolean fields in struct returns,
including Mixed5 (with pointer and boolean), SmallBool (small struct with
boolean), and LargeBool (large struct with boolean) test cases. The C
implementations validate proper marshalling of boolean values across
different struct layouts and sizes.
Make the expectedSum derivation clearer by expressing it as the sum of
its component values rather than the precomputed result.
@tmc
Copy link
Contributor Author

tmc commented Feb 7, 2026

@tmc doesn't this require rebasing?

@TotallyGamerJet ping

Rebasing, will push shortly.

@tmc tmc force-pushed the linux-struct-support branch from a82375f to a9cb6fe Compare February 7, 2026 19:08
The arm64 implementation was already fixed but loong64 was missed.
@tmc
Copy link
Contributor Author

tmc commented Feb 7, 2026

noticed reflect.UnsafePointer was missed on loong64, fixed.

@JupiterRider
Copy link
Contributor

@tmc
Yo, please add this test: JupiterRider@fb82e31

It will fail

…t/float fields

From JupiterRider feedback on PR ebitengine#361. Passes on darwin/arm64 but
reported to fail on Linux.
@tmc
Copy link
Contributor Author

tmc commented Feb 8, 2026

@tmc Yo, please add this test: JupiterRider@fb82e31

It will fail

Thanks, confirmed and looking into this.

placeStack was decomposing struct fields into separate stack slots,
which broke the x86-64 ABI for structs with mixed-type fields sharing
an eightbyte (e.g. int32 + float32 at adjacent offsets). The ABI
requires structs passed on the stack to preserve their in-memory
layout. Copy raw eightbyte chunks instead.

Fixes IdentityMixed5 (ptr, int32, float32, int32) on linux/amd64
where the float32 bits were misplaced into the adjacent int32 field.
Copy link
Member

@hajimehoshi hajimehoshi left a comment

Choose a reason for hiding this comment

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

Still LGTM

Copy link
Collaborator

@TotallyGamerJet TotallyGamerJet left a comment

Choose a reason for hiding this comment

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

LGTM

@hajimehoshi hajimehoshi merged commit 1a5155f into ebitengine:main Feb 12, 2026
46 checks passed
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 on Linux

5 participants

Comments