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.
This commit is contained in:
13
examples/101-ffi-medium-struct.c
Normal file
13
examples/101-ffi-medium-struct.c
Normal file
@@ -0,0 +1,13 @@
|
||||
// Companion to examples/101-ffi-medium-struct.sx — a single
|
||||
// roundtrip through a 16-byte integer-only struct. Pinned in a
|
||||
// dedicated example because integer aggregates in this size class
|
||||
// route through emit_llvm.zig's `[2 x i64]` ABI coercion slot, a
|
||||
// different path from the small (≤8 B) integer struct, the 16-byte
|
||||
// HFA, and the >16 B byval-pointer cases.
|
||||
|
||||
typedef struct { long long a; long long b; } Pair64;
|
||||
|
||||
Pair64 ffi_pair64_swap(Pair64 p) {
|
||||
Pair64 r = { p.b, p.a };
|
||||
return r;
|
||||
}
|
||||
@@ -17,7 +17,7 @@
|
||||
#import "modules/std.sx";
|
||||
|
||||
#import c {
|
||||
#source "vendors/ffi_medium_struct/ffi_medium_struct.c";
|
||||
#source "101-ffi-medium-struct.c";
|
||||
};
|
||||
|
||||
Pair64 :: struct { a: s64; b: s64; }
|
||||
|
||||
16
examples/ffi-01-primitives.c
Normal file
16
examples/ffi-01-primitives.c
Normal file
@@ -0,0 +1,16 @@
|
||||
#include "ffi-01-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; }
|
||||
20
examples/ffi-01-primitives.h
Normal file
20
examples/ffi-01-primitives.h
Normal file
@@ -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);
|
||||
@@ -10,8 +10,8 @@
|
||||
#import "modules/std.sx";
|
||||
|
||||
#import c {
|
||||
#include "vendors/ffi_primitives/ffi_primitives.h";
|
||||
#source "vendors/ffi_primitives/ffi_primitives.c";
|
||||
#include "ffi-01-primitives.h";
|
||||
#source "ffi-01-primitives.c";
|
||||
};
|
||||
|
||||
main :: () -> s32 {
|
||||
|
||||
57
examples/ffi-02-small-struct.c
Normal file
57
examples/ffi-02-small-struct.c
Normal file
@@ -0,0 +1,57 @@
|
||||
#include "ffi-02-small-struct.h"
|
||||
|
||||
Vec2 ffi_vec2_make(float x, float y) {
|
||||
Vec2 r = { x, y };
|
||||
return r;
|
||||
}
|
||||
|
||||
Vec2 ffi_vec2_swap(Vec2 v) {
|
||||
Vec2 r = { v.y, v.x };
|
||||
return r;
|
||||
}
|
||||
|
||||
float ffi_vec2_sum(Vec2 v) {
|
||||
return v.x + v.y;
|
||||
}
|
||||
|
||||
Vec4f ffi_vec4f_make(float x, float y, float z, float w) {
|
||||
Vec4f r = { x, y, z, w };
|
||||
return r;
|
||||
}
|
||||
|
||||
Vec4f ffi_vec4f_reverse(Vec4f v) {
|
||||
Vec4f r = { v.w, v.z, v.y, v.x };
|
||||
return r;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
29
examples/ffi-02-small-struct.h
Normal file
29
examples/ffi-02-small-struct.h
Normal file
@@ -0,0 +1,29 @@
|
||||
// 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 { 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);
|
||||
|
||||
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);
|
||||
@@ -18,7 +18,7 @@
|
||||
// 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";
|
||||
#source "ffi-02-small-struct.c";
|
||||
};
|
||||
|
||||
Vec2 :: struct { x: f32; y: f32; }
|
||||
|
||||
30
examples/ffi-03-large-struct.c
Normal file
30
examples/ffi-03-large-struct.c
Normal file
@@ -0,0 +1,30 @@
|
||||
#include "ffi-03-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;
|
||||
}
|
||||
22
examples/ffi-03-large-struct.h
Normal file
22
examples/ffi-03-large-struct.h
Normal file
@@ -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);
|
||||
@@ -16,7 +16,7 @@
|
||||
#import "modules/std.sx";
|
||||
|
||||
#import c {
|
||||
#source "vendors/ffi_large_struct/ffi_large_struct.c";
|
||||
#source "ffi-03-large-struct.c";
|
||||
};
|
||||
|
||||
Big24 :: struct { a: s64; b: s64; c: s64; }
|
||||
|
||||
29
examples/ffi-04-fp-struct.c
Normal file
29
examples/ffi-04-fp-struct.c
Normal file
@@ -0,0 +1,29 @@
|
||||
#include "ffi-04-fp-struct.h"
|
||||
|
||||
FQuad ffi_fquad_make(float a, float b, float c, float d) {
|
||||
FQuad r = { a, b, c, d };
|
||||
return r;
|
||||
}
|
||||
|
||||
FQuad ffi_fquad_reverse(FQuad v) {
|
||||
FQuad r = { v.d, v.c, v.b, v.a };
|
||||
return r;
|
||||
}
|
||||
|
||||
float ffi_fquad_sum(FQuad v) {
|
||||
return v.a + v.b + v.c + v.d;
|
||||
}
|
||||
|
||||
DQuad ffi_dquad_make(double a, double b, double c, double d) {
|
||||
DQuad r = { a, b, c, d };
|
||||
return r;
|
||||
}
|
||||
|
||||
DQuad ffi_dquad_reverse(DQuad v) {
|
||||
DQuad r = { v.d, v.c, v.b, v.a };
|
||||
return r;
|
||||
}
|
||||
|
||||
double ffi_dquad_sum(DQuad v) {
|
||||
return v.a + v.b + v.c + v.d;
|
||||
}
|
||||
20
examples/ffi-04-fp-struct.h
Normal file
20
examples/ffi-04-fp-struct.h
Normal file
@@ -0,0 +1,20 @@
|
||||
// Focused FP-aggregate (HFA) FFI baselines. Distinct from the int-aggregate
|
||||
// register-coercion paths because all-float / all-double structs of ≤4 fields
|
||||
// stay as struct values in LLVM and are passed/returned via the float
|
||||
// register file (AAPCS64 v0..v3; SysV AMD64 xmm0..xmm7). This was the
|
||||
// `UIEdgeInsets`-as-f32-vs-f64 landmine — pinned here so a future ABI rule
|
||||
// change that wrecks the FP path fails this test directly.
|
||||
//
|
||||
// FQuad — 16 B, four float (small HFA; same slot as Vec4f)
|
||||
// DQuad — 32 B, four double (UIEdgeInsets-shape HFA)
|
||||
|
||||
typedef struct { float a; float b; float c; float d; } FQuad;
|
||||
typedef struct { double a; double b; double c; double d; } DQuad;
|
||||
|
||||
FQuad ffi_fquad_make (float a, float b, float c, float d);
|
||||
FQuad ffi_fquad_reverse(FQuad v);
|
||||
float ffi_fquad_sum (FQuad v);
|
||||
|
||||
DQuad ffi_dquad_make (double a, double b, double c, double d);
|
||||
DQuad ffi_dquad_reverse(DQuad v);
|
||||
double ffi_dquad_sum (DQuad v);
|
||||
@@ -16,7 +16,7 @@
|
||||
#import "modules/std.sx";
|
||||
|
||||
#import c {
|
||||
#source "vendors/ffi_fp_struct/ffi_fp_struct.c";
|
||||
#source "ffi-04-fp-struct.c";
|
||||
};
|
||||
|
||||
FQuad :: struct { a: f32; b: f32; c: f32; d: f32; }
|
||||
|
||||
25
examples/ffi-05-string-args.c
Normal file
25
examples/ffi-05-string-args.c
Normal file
@@ -0,0 +1,25 @@
|
||||
#include "ffi-05-string-args.h"
|
||||
|
||||
int ffi_strlen(const char *s) {
|
||||
int n = 0;
|
||||
while (s[n] != 0) n++;
|
||||
return n;
|
||||
}
|
||||
|
||||
int ffi_first_byte(const char *s) {
|
||||
return (int)(unsigned char)s[0];
|
||||
}
|
||||
|
||||
int ffi_sum_bytes(const unsigned char *buf, int len) {
|
||||
int total = 0;
|
||||
for (int i = 0; i < len; i++) total += buf[i];
|
||||
return total;
|
||||
}
|
||||
|
||||
void ffi_write_byte(unsigned char *buf, int idx, unsigned char val) {
|
||||
buf[idx] = val;
|
||||
}
|
||||
|
||||
const char* ffi_static_greeting(void) {
|
||||
return "hello from C";
|
||||
}
|
||||
14
examples/ffi-05-string-args.h
Normal file
14
examples/ffi-05-string-args.h
Normal file
@@ -0,0 +1,14 @@
|
||||
// String / byte-pointer FFI baselines. Covers the three shapes
|
||||
// callers actually use at the sx ↔ C boundary:
|
||||
// - null-terminated `[:0]u8` (C-style string)
|
||||
// - raw byte pointer `[*]u8` + length (slice-style)
|
||||
// - sx `string` decayed to `ptr` (the slice-decay branch
|
||||
// in coerceArg pulls .ptr)
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
int ffi_strlen (const char *s);
|
||||
int ffi_first_byte (const char *s);
|
||||
int ffi_sum_bytes (const unsigned char *buf, int len);
|
||||
void ffi_write_byte (unsigned char *buf, int idx, unsigned char val);
|
||||
const char* ffi_static_greeting(void);
|
||||
@@ -11,7 +11,7 @@
|
||||
#import "modules/std.sx";
|
||||
|
||||
#import c {
|
||||
#source "vendors/ffi_strings/ffi_strings.c";
|
||||
#source "ffi-05-string-args.c";
|
||||
};
|
||||
|
||||
ffi_strlen :: (s: [:0]u8) -> s32 #foreign;
|
||||
|
||||
9
examples/ffi-06-callback.c
Normal file
9
examples/ffi-06-callback.c
Normal file
@@ -0,0 +1,9 @@
|
||||
#include "ffi-06-callback.h"
|
||||
|
||||
int ffi_apply_callback(int (*cb)(int), int value) {
|
||||
return cb(value);
|
||||
}
|
||||
|
||||
int ffi_apply_callback2(int (*cb)(void *ctx, int v), void *ctx, int v) {
|
||||
return cb(ctx, v);
|
||||
}
|
||||
13
examples/ffi-06-callback.h
Normal file
13
examples/ffi-06-callback.h
Normal file
@@ -0,0 +1,13 @@
|
||||
// C-to-sx callback FFI baseline. C takes a function pointer + a value,
|
||||
// invokes the callback with the value, and returns whatever the callback
|
||||
// returned. Mirrors the `app->onInputEvent` pattern in
|
||||
// library/modules/platform/android.sx where sx installs a handler that
|
||||
// native_app_glue invokes from its input-event loop.
|
||||
|
||||
int ffi_apply_callback(int (*cb)(int), int value);
|
||||
|
||||
// Two-arg variant — the actual chess-on-Android shape:
|
||||
// the callback receives a pointer + a value (mirrors
|
||||
// onInputEvent(app, event) where both are opaque pointers from
|
||||
// the C caller's point of view).
|
||||
int ffi_apply_callback2(int (*cb)(void *ctx, int v), void *ctx, int v);
|
||||
@@ -13,7 +13,7 @@
|
||||
#import "modules/std.sx";
|
||||
|
||||
#import c {
|
||||
#source "vendors/ffi_callback/ffi_callback.c";
|
||||
#source "ffi-06-callback.c";
|
||||
};
|
||||
|
||||
ffi_apply_callback :: (cb: (s32) -> s32, value: s32) -> s32 #foreign;
|
||||
|
||||
Reference in New Issue
Block a user