diff --git a/examples/ffi-01-primitives.sx b/examples/ffi-01-primitives.sx new file mode 100644 index 0000000..cfbdad7 --- /dev/null +++ b/examples/ffi-01-primitives.sx @@ -0,0 +1,49 @@ +// Phase 0 baseline (PLAN-FFI.md step 0.1): every primitive type passed +// in/out of a C `#foreign` fn via `#import c { #include / #source }`. +// Locks today's parameter + return ABI so Phase 1's lowering changes +// (`#objc_call` / `#jni_call`) can't silently regress us. +// +// Note: c_import maps `signed char` to sx `u8` (see c_import.zig:541) +// — narrow signed-char roundtrips are still surfaced here so any future +// mapping change shows up as a snapshot diff. + +#import "modules/std.sx"; + +#import c { + #include "vendors/ffi_primitives/ffi_primitives.h"; + #source "vendors/ffi_primitives/ffi_primitives.c"; +}; + +main :: () -> s32 { + // Signed roundtrips + print("ffi_id_int(-42) = {}\n", ffi_id_int(0 - 42)); + print("ffi_id_short(-1234) = {}\n", ffi_id_short(0 - 1234)); + print("ffi_id_s64(huge) = {}\n", ffi_id_s64(9000000000000000000)); + + // Unsigned roundtrips + print("ffi_id_uint(0xDEADBEEF) = {}\n", ffi_id_uint(0xDEADBEEF)); + print("ffi_id_ushort(0xFFFF) = {}\n", ffi_id_ushort(0xFFFF)); + print("ffi_id_u64(0x7FEE...) = {}\n", ffi_id_u64(0x7FEEDFACECAFEBEE)); + + // Narrow char roundtrips (both map to u8 in current sx) + print("ffi_id_schar(127) = {}\n", ffi_id_schar(127)); + print("ffi_id_uchar(255) = {}\n", ffi_id_uchar(255)); + + // Floating point + print("ffi_id_f32(3.5) = {}\n", ffi_id_f32(3.5)); + print("ffi_id_f64(1.5) = {}\n", ffi_id_f64(1.5)); + + // Pointer roundtrip — proves *void survives the boundary + sentinel : s32 = 42; + p_in : *void = xx @sentinel; + p_out := ffi_id_ptr(p_in); + addr_in : u64 = xx p_in; + addr_out : u64 = xx p_out; + print("ffi_id_ptr roundtrip = {}\n", addr_in == addr_out); + + // Arithmetic helpers — exercise multi-arg ABI + print("ffi_add_int(7, 8) = {}\n", ffi_add_int(7, 8)); + print("ffi_add_double(0.25, 0.75) = {}\n", ffi_add_double(0.25, 0.75)); + + 0; +} diff --git a/tests/expected/ffi-01-primitives.exit b/tests/expected/ffi-01-primitives.exit new file mode 100644 index 0000000..573541a --- /dev/null +++ b/tests/expected/ffi-01-primitives.exit @@ -0,0 +1 @@ +0 diff --git a/tests/expected/ffi-01-primitives.txt b/tests/expected/ffi-01-primitives.txt new file mode 100644 index 0000000..0f98f39 --- /dev/null +++ b/tests/expected/ffi-01-primitives.txt @@ -0,0 +1,13 @@ +ffi_id_int(-42) = -42 +ffi_id_short(-1234) = -1234 +ffi_id_s64(huge) = 9000000000000000000 +ffi_id_uint(0xDEADBEEF) = 3735928559 +ffi_id_ushort(0xFFFF) = 65535 +ffi_id_u64(0x7FEE...) = 9218551421072305134 +ffi_id_schar(127) = 127 +ffi_id_uchar(255) = 255 +ffi_id_f32(3.5) = 3.500000 +ffi_id_f64(1.5) = 1.500000 +ffi_id_ptr roundtrip = true +ffi_add_int(7, 8) = 15 +ffi_add_double(0.25, 0.75) = 1.000000 diff --git a/vendors/ffi_primitives/ffi_primitives.c b/vendors/ffi_primitives/ffi_primitives.c new file mode 100644 index 0000000..fd35710 --- /dev/null +++ b/vendors/ffi_primitives/ffi_primitives.c @@ -0,0 +1,16 @@ +#include "ffi_primitives.h" + +int ffi_id_int (int v) { return v; } +unsigned int ffi_id_uint (unsigned int v) { return v; } +short ffi_id_short (short v) { return v; } +unsigned short ffi_id_ushort(unsigned short v) { return v; } +long long ffi_id_s64 (long long v) { return v; } +unsigned long long ffi_id_u64 (unsigned long long v) { return v; } +signed char ffi_id_schar (signed char v) { return v; } +unsigned char ffi_id_uchar (unsigned char v) { return v; } +float ffi_id_f32 (float v) { return v; } +double ffi_id_f64 (double v) { return v; } +void * ffi_id_ptr (void * v) { return v; } + +int ffi_add_int (int a, int b) { return a + b; } +double ffi_add_double(double a, double b) { return a + b; } diff --git a/vendors/ffi_primitives/ffi_primitives.h b/vendors/ffi_primitives/ffi_primitives.h new file mode 100644 index 0000000..44b111f --- /dev/null +++ b/vendors/ffi_primitives/ffi_primitives.h @@ -0,0 +1,20 @@ +// FFI baseline test helpers — one trivial roundtrip per primitive C +// type so the sx-side test can verify both the parameter ABI and the +// return-value ABI per type. Locking these in BEFORE the Phase 1 +// `#objc_call` / `#jni_call` work so any future lowering change that +// silently regresses primitive marshalling shows up here. + +int ffi_id_int (int v); +unsigned int ffi_id_uint (unsigned int v); +short ffi_id_short (short v); +unsigned short ffi_id_ushort(unsigned short v); +long long ffi_id_s64 (long long v); +unsigned long long ffi_id_u64 (unsigned long long v); +signed char ffi_id_schar (signed char v); +unsigned char ffi_id_uchar (unsigned char v); +float ffi_id_f32 (float v); +double ffi_id_f64 (double v); +void * ffi_id_ptr (void * v); + +int ffi_add_int (int a, int b); +double ffi_add_double(double a, double b);