Files
sx/examples/ffi-02-small-struct.sx
agra 3855f2351e ffi: move test-companion .c/.h next to their .sx (drop vendors/ namespace)
vendors/ is a third-party namespace (stb_image, kb_text_shape, etc.);
test fixtures don't belong there. The .c/.h companion files for the
Phase-0 FFI baselines now sit alongside the .sx that drives them in
examples/, with matching basenames:

  examples/ffi-01-primitives.{sx,c,h}    <- was vendors/ffi_primitives/
  examples/ffi-02-small-struct.{sx,c,h}  <- was vendors/ffi_structs/
  examples/ffi-03-large-struct.{sx,c,h}  <- was vendors/ffi_large_struct/
  examples/ffi-04-fp-struct.{sx,c,h}     <- was vendors/ffi_fp_struct/
  examples/ffi-05-string-args.{sx,c,h}   <- was vendors/ffi_strings/
  examples/ffi-06-callback.{sx,c,h}      <- was vendors/ffi_callback/
  examples/101-ffi-medium-struct.{sx,c}  <- was vendors/ffi_medium_struct/

`#source` / `#include` paths in the .sx files become bare filenames
(no prefix) since imports.zig's base_dir resolution finds them
relative to the importing .sx file's directory.

`library/vendors/sx_ffi_resolve_test/` stays put — that one's the
whole point: regression coverage for the stdlib-search branch of
the resolution chain, so it must live where ONLY that branch can
find it.

94/94 regression tests pass.
2026-05-19 11:54:36 +03:00

76 lines
3.4 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// 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. 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)
//
// 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 default), losing the
// by-value ABI. The hand-written #foreign decls below keep sx's
// struct types end-to-end.
#import c {
#source "ffi-02-small-struct.c";
};
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;
ffi_vec2_sum :: (v: Vec2) -> f32 #foreign;
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);
w := ffi_vec2_swap(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);
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));
// ── 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;
}