diff --git a/examples/ffi-02-small-struct.sx b/examples/ffi-02-small-struct.sx index 5687551..fa05994 100644 --- a/examples/ffi-02-small-struct.sx +++ b/examples/ffi-02-small-struct.sx @@ -1,27 +1,30 @@ // Phase 0 baseline (PLAN-FFI.md step 0.2): small structs (≤16 bytes) -// passed by value into a C `#foreign` fn and returned by value. Two -// shapes that exercise different aggregate ABI paths today: -// Vec2 — 8 bytes, two f32 (float register pair on AAPCS64) -// Vec4f — 16 bytes, four f32 (HFA — homogeneous float aggregate) +// passed by value into a C `#foreign` fn and returned by value. Four +// shapes that exercise distinct aggregate ABI paths: +// Vec2 — 8 B, two f32 (register pair, float) +// Vec4f — 16 B, four f32 (HFA — homogeneous float aggregate) +// Pair64 — 16 B, two s64 (9..16 B int — [2 x i64] coercion slot) +// Quad32 — 16 B, four s32 (9..16 B int — same slot as Pair64) // -// 16-byte integer-only structs (e.g. `{ s64, s64 }`, `{ s32, s32, s32, s32 }`) -// are *not* covered here: sx's `#foreign` decl currently lowers them -// as `[2 x i64]` while the call site uses the struct type, tripping -// the LLVM verifier. Repro pinned in `examples/issue-0036.sx`; once -// that bug closes, fold those shapes back into this baseline. +// Pair64 / Quad32 were originally excluded (LLVM verifier rejected the +// struct<->[2 x i64] type mismatch — see git history for issue-0036); +// folded back in once emit_llvm.zig's coerceArg learned to bridge +// struct <-> array via the abi.struct2arr / abi.arr2struct branches. #import "modules/std.sx"; // `#source` only — c_import would rewrite struct-typed params/returns -// in the .h to *void (its "struct/opaque pointer → *void" default), -// which would link but pass through the wrong ABI. The sx declarations -// below match the C signatures exactly. +// in the .h to *void (its struct/opaque pointer default), losing the +// by-value ABI. The hand-written #foreign decls below keep sx's +// struct types end-to-end. #import c { #source "vendors/ffi_structs/ffi_structs.c"; }; -Vec2 :: struct { x: f32; y: f32; } -Vec4f :: struct { x: f32; y: f32; z: f32; w: f32; } +Vec2 :: struct { x: f32; y: f32; } +Vec4f :: struct { x: f32; y: f32; z: f32; w: f32; } +Pair64 :: struct { a: s64; b: s64; } +Quad32 :: struct { a: s32; b: s32; c: s32; d: s32; } ffi_vec2_make :: (x: f32, y: f32) -> Vec2 #foreign; ffi_vec2_swap :: (v: Vec2) -> Vec2 #foreign; @@ -31,20 +34,42 @@ ffi_vec4f_make :: (x: f32, y: f32, z: f32, w: f32) -> Vec4f #foreign; ffi_vec4f_reverse :: (v: Vec4f) -> Vec4f #foreign; ffi_vec4f_sum :: (v: Vec4f) -> f32 #foreign; +ffi_pair64_make :: (a: s64, b: s64) -> Pair64 #foreign; +ffi_pair64_swap :: (p: Pair64) -> Pair64 #foreign; +ffi_pair64_sum :: (p: Pair64) -> s64 #foreign; + +ffi_quad32_make :: (a: s32, b: s32, c: s32, d: s32) -> Quad32 #foreign; +ffi_quad32_reverse :: (q: Quad32) -> Quad32 #foreign; +ffi_quad32_sum :: (q: Quad32) -> s32 #foreign; + main :: () -> s32 { // ── Vec2 (8 bytes, float pair) ───────────────────────────────── v := ffi_vec2_make(1.5, 2.5); - print("vec2 make = ({}, {})\n", v.x, v.y); + print("vec2 make = ({}, {})\n", v.x, v.y); w := ffi_vec2_swap(v); - print("vec2 swap = ({}, {})\n", w.x, w.y); - print("vec2 sum = {}\n", ffi_vec2_sum(v)); + print("vec2 swap = ({}, {})\n", w.x, w.y); + print("vec2 sum = {}\n", ffi_vec2_sum(v)); // ── Vec4f (16 bytes, HFA) ────────────────────────────────────── f := ffi_vec4f_make(1.0, 2.0, 3.0, 4.0); - print("vec4f make = ({}, {}, {}, {})\n", f.x, f.y, f.z, f.w); + print("vec4f make = ({}, {}, {}, {})\n", f.x, f.y, f.z, f.w); g := ffi_vec4f_reverse(f); - print("vec4f rev = ({}, {}, {}, {})\n", g.x, g.y, g.z, g.w); - print("vec4f sum = {}\n", ffi_vec4f_sum(f)); + print("vec4f rev = ({}, {}, {}, {})\n", g.x, g.y, g.z, g.w); + print("vec4f sum = {}\n", ffi_vec4f_sum(f)); + + // ── Pair64 (16 bytes, 2×s64 — [2 x i64] coercion path) ───────── + p := ffi_pair64_make(100, 200); + print("pair64 make = ({}, {})\n", p.a, p.b); + pp := ffi_pair64_swap(p); + print("pair64 swap = ({}, {})\n", pp.a, pp.b); + print("pair64 sum = {}\n", ffi_pair64_sum(p)); + + // ── Quad32 (16 bytes, 4×s32 — same coercion path as Pair64) ──── + q := ffi_quad32_make(10, 20, 30, 40); + print("quad32 make = ({}, {}, {}, {})\n", q.a, q.b, q.c, q.d); + r := ffi_quad32_reverse(q); + print("quad32 rev = ({}, {}, {}, {})\n", r.a, r.b, r.c, r.d); + print("quad32 sum = {}\n", ffi_quad32_sum(q)); 0; } diff --git a/tests/expected/ffi-02-small-struct.txt b/tests/expected/ffi-02-small-struct.txt index a29959e..c12bcef 100644 --- a/tests/expected/ffi-02-small-struct.txt +++ b/tests/expected/ffi-02-small-struct.txt @@ -1,6 +1,12 @@ -vec2 make = (1.500000, 2.500000) -vec2 swap = (2.500000, 1.500000) -vec2 sum = 4.000000 -vec4f make = (1.000000, 2.000000, 3.000000, 4.000000) -vec4f rev = (4.000000, 3.000000, 2.000000, 1.000000) -vec4f sum = 10.000000 +vec2 make = (1.500000, 2.500000) +vec2 swap = (2.500000, 1.500000) +vec2 sum = 4.000000 +vec4f make = (1.000000, 2.000000, 3.000000, 4.000000) +vec4f rev = (4.000000, 3.000000, 2.000000, 1.000000) +vec4f sum = 10.000000 +pair64 make = (100, 200) +pair64 swap = (200, 100) +pair64 sum = 300 +quad32 make = (10, 20, 30, 40) +quad32 rev = (40, 30, 20, 10) +quad32 sum = 100 diff --git a/vendors/ffi_structs/ffi_structs.c b/vendors/ffi_structs/ffi_structs.c index 73728a2..a5d5610 100644 --- a/vendors/ffi_structs/ffi_structs.c +++ b/vendors/ffi_structs/ffi_structs.c @@ -27,3 +27,31 @@ Vec4f ffi_vec4f_reverse(Vec4f v) { float ffi_vec4f_sum(Vec4f v) { return v.x + v.y + v.z + v.w; } + +Pair64 ffi_pair64_make(long long a, long long b) { + Pair64 r = { a, b }; + return r; +} + +Pair64 ffi_pair64_swap(Pair64 p) { + Pair64 r = { p.b, p.a }; + return r; +} + +long long ffi_pair64_sum(Pair64 p) { + return p.a + p.b; +} + +Quad32 ffi_quad32_make(int a, int b, int c, int d) { + Quad32 r = { a, b, c, d }; + return r; +} + +Quad32 ffi_quad32_reverse(Quad32 q) { + Quad32 r = { q.d, q.c, q.b, q.a }; + return r; +} + +int ffi_quad32_sum(Quad32 q) { + return q.a + q.b + q.c + q.d; +} diff --git a/vendors/ffi_structs/ffi_structs.h b/vendors/ffi_structs/ffi_structs.h index 35b7077..02e5f56 100644 --- a/vendors/ffi_structs/ffi_structs.h +++ b/vendors/ffi_structs/ffi_structs.h @@ -1,18 +1,29 @@ -// FFI struct-marshalling baselines. Two shapes covered today: -// Vec2 — 8 bytes (two f32) — register pair, float path -// Vec4f — 16 bytes (four f32) — homogeneous float aggregate (HFA) -// Declared here so the .c has a header to include; the sx side -// imports via `#source` only and re-declares the structs natively -// (c_import currently rewrites struct-typed params/returns to *void, -// which loses the by-value ABI). +// FFI struct-marshalling baselines covering four aggregate ABI slots: +// Vec2 — 8 B, two f32 — register-pair (float) path +// Vec4f — 16 B, four f32 — HFA (homogeneous float aggregate) +// Pair64 — 16 B, two s64 — 9..16 B int ABI ([2 x i64] coercion) +// Quad32 — 16 B, four s32 — 9..16 B int ABI ([2 x i64] coercion) +// Declared here so the .c has a header to include; sx side imports +// via `#source` only and re-declares the structs natively (c_import +// rewrites struct-typed params/returns to *void). -typedef struct { float x; float y; } Vec2; -typedef struct { float x; float y; float z; float w; } Vec4f; +typedef struct { float x; float y; } Vec2; +typedef struct { float x; float y; float z; float w; } Vec4f; +typedef struct { long long a; long long b; } Pair64; +typedef struct { int a; int b; int c; int d; } Quad32; -Vec2 ffi_vec2_make (float x, float y); -Vec2 ffi_vec2_swap (Vec2 v); -float ffi_vec2_sum (Vec2 v); +Vec2 ffi_vec2_make (float x, float y); +Vec2 ffi_vec2_swap (Vec2 v); +float ffi_vec2_sum (Vec2 v); -Vec4f ffi_vec4f_make (float x, float y, float z, float w); -Vec4f ffi_vec4f_reverse(Vec4f v); -float ffi_vec4f_sum (Vec4f v); +Vec4f ffi_vec4f_make (float x, float y, float z, float w); +Vec4f ffi_vec4f_reverse(Vec4f v); +float ffi_vec4f_sum (Vec4f v); + +Pair64 ffi_pair64_make (long long a, long long b); +Pair64 ffi_pair64_swap (Pair64 p); +long long ffi_pair64_sum (Pair64 p); + +Quad32 ffi_quad32_make (int a, int b, int c, int d); +Quad32 ffi_quad32_reverse(Quad32 q); +int ffi_quad32_sum (Quad32 q);