diff --git a/examples/ffi-03-large-struct.sx b/examples/ffi-03-large-struct.sx new file mode 100644 index 0000000..bdc2c90 --- /dev/null +++ b/examples/ffi-03-large-struct.sx @@ -0,0 +1,53 @@ +// Phase 0 baseline (PLAN-FFI.md step 0.3): structs >16 bytes passed +// by value into a C `#foreign` fn and returned by value. Exercises +// the byval-pointer ABI path — the caller copies the struct onto its +// stack and hands a pointer to the callee; on AAPCS64 the return +// uses the indirect `x8` register; on SysV AMD64 the return is a +// hidden first arg pointer that the caller allocates. +// +// Distinct from the register-pair / [2 x i64] / HFA paths the +// small-struct baseline (ffi-02) covers. The two relevant emit_llvm +// helpers are `needsByval` (returns true when size > 16) and +// `materializeByvalArg` (alloca + store + pass pointer). +// +// Big24 — 24 B, three s64 +// Big48 — 48 B, six s64 + +#import "modules/std.sx"; + +#import c { + #source "vendors/ffi_large_struct/ffi_large_struct.c"; +}; + +Big24 :: struct { a: s64; b: s64; c: s64; } +Big48 :: struct { + a: s64; b: s64; c: s64; + d: s64; e: s64; f: s64; +} + +ffi_big24_make :: (a: s64, b: s64, c: s64) -> Big24 #foreign; +ffi_big24_rotate :: (v: Big24) -> Big24 #foreign; +ffi_big24_sum :: (v: Big24) -> s64 #foreign; + +ffi_big48_make :: (a: s64, b: s64, c: s64, + d: s64, e: s64, f: s64) -> Big48 #foreign; +ffi_big48_reverse :: (v: Big48) -> Big48 #foreign; +ffi_big48_sum :: (v: Big48) -> s64 #foreign; + +main :: () -> s32 { + // ── Big24 (24 bytes, byval pointer) ──────────────────────────── + v := ffi_big24_make(1, 2, 3); + print("big24 make = ({}, {}, {})\n", v.a, v.b, v.c); + w := ffi_big24_rotate(v); + print("big24 rotate = ({}, {}, {})\n", w.a, w.b, w.c); + print("big24 sum = {}\n", ffi_big24_sum(v)); + + // ── Big48 (48 bytes, byval pointer) ──────────────────────────── + x := ffi_big48_make(10, 20, 30, 40, 50, 60); + print("big48 make = ({}, {}, {}, {}, {}, {})\n", x.a, x.b, x.c, x.d, x.e, x.f); + y := ffi_big48_reverse(x); + print("big48 reverse = ({}, {}, {}, {}, {}, {})\n", y.a, y.b, y.c, y.d, y.e, y.f); + print("big48 sum = {}\n", ffi_big48_sum(x)); + + 0; +} diff --git a/tests/expected/ffi-03-large-struct.exit b/tests/expected/ffi-03-large-struct.exit new file mode 100644 index 0000000..573541a --- /dev/null +++ b/tests/expected/ffi-03-large-struct.exit @@ -0,0 +1 @@ +0 diff --git a/tests/expected/ffi-03-large-struct.txt b/tests/expected/ffi-03-large-struct.txt new file mode 100644 index 0000000..1480661 --- /dev/null +++ b/tests/expected/ffi-03-large-struct.txt @@ -0,0 +1,6 @@ +big24 make = (1, 2, 3) +big24 rotate = (3, 1, 2) +big24 sum = 6 +big48 make = (10, 20, 30, 40, 50, 60) +big48 reverse = (60, 50, 40, 30, 20, 10) +big48 sum = 210 diff --git a/vendors/ffi_large_struct/ffi_large_struct.c b/vendors/ffi_large_struct/ffi_large_struct.c new file mode 100644 index 0000000..552066b --- /dev/null +++ b/vendors/ffi_large_struct/ffi_large_struct.c @@ -0,0 +1,30 @@ +#include "ffi_large_struct.h" + +Big24 ffi_big24_make(long long a, long long b, long long c) { + Big24 r = { a, b, c }; + return r; +} + +Big24 ffi_big24_rotate(Big24 v) { + Big24 r = { v.c, v.a, v.b }; + return r; +} + +long long ffi_big24_sum(Big24 v) { + return v.a + v.b + v.c; +} + +Big48 ffi_big48_make(long long a, long long b, long long c, + long long d, long long e, long long f) { + Big48 r = { a, b, c, d, e, f }; + return r; +} + +Big48 ffi_big48_reverse(Big48 v) { + Big48 r = { v.f, v.e, v.d, v.c, v.b, v.a }; + return r; +} + +long long ffi_big48_sum(Big48 v) { + return v.a + v.b + v.c + v.d + v.e + v.f; +} diff --git a/vendors/ffi_large_struct/ffi_large_struct.h b/vendors/ffi_large_struct/ffi_large_struct.h new file mode 100644 index 0000000..69f888b --- /dev/null +++ b/vendors/ffi_large_struct/ffi_large_struct.h @@ -0,0 +1,22 @@ +// FFI large-struct (>16 B) by-value roundtrips. These route through +// the byval-pointer ABI path (caller copies onto its stack, hands the +// callee a pointer; on AAPCS64 a separate `x8` indirect-return +// register; on SysV AMD64 a hidden first arg). Distinct from the +// register-pair / [2 x i64] / HFA paths the small-struct baseline +// covers — locking those in here keeps the byval path honest. +// +// Big24 — 24 B, three s64 +// Big48 — 48 B, six s64 + +typedef struct { long long a; long long b; long long c; } Big24; +typedef struct { long long a; long long b; long long c; + long long d; long long e; long long f; } Big48; + +Big24 ffi_big24_make (long long a, long long b, long long c); +Big24 ffi_big24_rotate(Big24 v); +long long ffi_big24_sum (Big24 v); + +Big48 ffi_big48_make (long long a, long long b, long long c, + long long d, long long e, long long f); +Big48 ffi_big48_reverse(Big48 v); +long long ffi_big48_sum (Big48 v);