makepad/libs/relax/compiler/tests/cabi.rs
Admin b22a714840 makepad run: the Rust JIT (libs/relax) and makepad-remote
The JIT that runs makepad apps from source: lexer, front end, type checker,
lazy borrow checker (Gandalf; `--check-first` opts in), codegen, session and
warm/cold caches keyed by compiler stamp, provided crates bound late,
startup images, live patching. User-facing name is `makepad run` (binary
`makepad`, MAKEPAD_RUN_* env, ~/.makepad-run); libs/relax stays the internal
library name. `makepad check` reports rustc-style diagnostics with rustc
error codes and checks a whole workspace (`--workspace`). Runs on macOS,
Linux, Windows and Android. makepad-remote: one tool schema for apps and the
WM (app/1): grabs, captures, traces, frame profiles and tweaks.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-13 12:26:21 +02:00

206 lines
6.1 KiB
Rust

// C ABI: calls into C with scalars, floats, narrow ints, struct by value and struct
// returns (packed ints, float pairs, HFAs, > 16-byte aggregates), variadics with stack
// arguments, C calling back into Relax (`extern "C"` bodies), and Relax calling its
// own `extern "C"` fns with every argument shape. Output matches rustc (tests/cabi.rs
// built with `rustc --test`).
extern "C" {
fn snprintf(buf: *mut u8, n: usize, fmt: *const u8, ...) -> i32;
fn fabs(x: f64) -> f64;
fn fabsf(x: f32) -> f32;
fn ldexp(x: f64, e: i32) -> f64;
fn abs(x: i32) -> i32;
fn qsort(base: *mut u8, n: usize, w: usize, cmp: extern "C" fn(*const u8, *const u8) -> i32);
fn bsearch(key: *const u8, base: *const u8, n: usize, w: usize, cmp: extern "C" fn(*const u8, *const u8) -> i32) -> *const u8;
fn div(a: i32, b: i32) -> DivT;
fn ldiv(a: i64, b: i64) -> LDivT;
fn cabs(z: Cplx) -> f64;
fn cexp(z: Cplx) -> Cplx;
fn cabsf(z: CplxF) -> f32;
fn csqrtf(z: CplxF) -> CplxF;
fn inet_ntoa(a: InAddr) -> *const u8;
fn strlen(s: *const u8) -> usize;
}
#[repr(C)]
#[derive(Clone, Copy)]
struct DivT {
quot: i32,
rem: i32,
}
#[repr(C)]
#[derive(Clone, Copy)]
struct LDivT {
quot: i64,
rem: i64,
}
#[repr(C)]
#[derive(Clone, Copy)]
struct Cplx {
re: f64,
im: f64,
}
#[repr(C)]
#[derive(Clone, Copy)]
struct CplxF {
re: f32,
im: f32,
}
#[repr(C)]
#[derive(Clone, Copy)]
struct InAddr {
s_addr: u32,
}
#[repr(C)]
#[derive(Clone, Copy)]
struct Big {
a: i64,
b: f64,
c: i32,
d: u8,
e: f32,
}
#[repr(C)]
#[derive(Clone, Copy)]
struct Hfa3 {
x: f32,
y: f32,
z: f32,
}
#[repr(C)]
#[derive(Clone, Copy)]
struct Mixed {
i: i32,
f: f32,
l: i64,
}
#[repr(C)]
#[derive(Clone, Copy)]
struct Bytes3 {
a: u8,
b: u8,
c: u8,
}
#[repr(C)]
#[derive(Clone, Copy)]
struct Rect {
x: f64,
y: f64,
w: f64,
h: f64,
}
fn hash_bytes(p: *const u8, n: usize) -> u64 {
let mut h: u64 = 0xcbf29ce484222325;
let mut i = 0;
while i < n {
h = (h ^ unsafe { *((p as usize + i) as *const u8) } as u64).wrapping_mul(0x100000001b3);
i += 1;
}
h
}
extern "C" fn cmp_i32(a: *const u8, b: *const u8) -> i32 {
let x = unsafe { *(a as *const i32) };
let y = unsafe { *(b as *const i32) };
if x < y {
-1
} else if x > y {
1
} else {
0
}
}
// `extern "C"` bodies with every argument and result shape (called directly below and
// by C through qsort/bsearch above)
extern "C" fn c_big(a: Big, k: i32) -> Big {
Big { a: a.a * k as i64, b: a.b + 0.5, c: a.c - k, d: (a.d as u32 + 1) as u8, e: a.e * 2.0 }
}
extern "C" fn c_hfa(h: Hfa3, s: f32) -> Hfa3 {
Hfa3 { x: h.x * s, y: h.y + s, z: h.z - s }
}
extern "C" fn c_mixed(m: Mixed, b: Bytes3) -> Mixed {
Mixed { i: m.i + b.a as i32, f: m.f * b.b as f32, l: m.l - b.c as i64 }
}
extern "C" fn c_rect(r: Rect, dx: f64, dy: f64) -> Rect {
Rect { x: r.x + dx, y: r.y + dy, w: r.w - 2.0 * dx, h: r.h - 2.0 * dy }
}
extern "C" fn c_narrow(a: u8, b: i8, c: u16, d: i16, e: u32, f: i32, g: bool) -> i64 {
a as i64 + b as i64 * 3 + c as i64 * 5 + d as i64 * 7 + e as i64 * 11 + f as i64 * 13 + g as i64 * 17
}
extern "C" fn c_many(a: i64, b: f64, c: i64, d: f64, e: i64, f: f64, g: i64, h: f64, i: i64, j: f64, k: i64, l: f64, m: i64, n: f64, o: i64, p: f64, q: u8, r: f32, s: i16, t: f64, u: Mixed, v: Hfa3) -> f64 {
(a + c + e + g + i + k + m + o + q as i64 + s as i64 + u.l) as f64 + b + d + f + h + j + l + n + p + r as f64 + t + u.f as f64 + v.x as f64 + v.z as f64
}
#[test]
fn floats_and_narrow() {
let a = unsafe { fabs(-2.5) };
let b = unsafe { fabsf(-1.5) };
let c = unsafe { ldexp(1.5, -3) };
let d = unsafe { abs(-7) };
println!("floats {} {} {} {}", a, b, c, d);
println!("narrow {}", c_narrow(250, -3, 60000, -300, 4000000000, -5, true));
}
#[test]
fn variadic_stack() {
let mut buf = [0u8; 160];
let n = unsafe {
snprintf(&mut buf[0] as *mut u8, 160, "%d %s %.3f %ld %d %d %d %d %.1f %d %d|\0" as *const str as *const u8, 7i32, "hey\0" as *const str as *const u8, 3.25f64, 123456789012i64, 1i32, 2i32, 3i32, 4i32, 0.5f64, 5i32, 6i32)
};
println!("snprintf n={} h={}", n, hash_bytes(&buf[0] as *const u8, n as usize));
}
#[test]
fn struct_returns_from_c() {
let d = unsafe { div(7, -2) };
let l = unsafe { ldiv(-17, 5) };
println!("div {} {} ldiv {} {}", d.quot, d.rem, l.quot, l.rem);
let z = Cplx { re: 3.0, im: 4.0 };
let e = unsafe { cexp(Cplx { re: 0.0, im: 1.0 }) };
let zf = unsafe { csqrtf(CplxF { re: -4.0, im: 0.0 }) };
println!("cabs {} cexp {:.6} {:.6} cabsf {} csqrtf {} {}", unsafe { cabs(z) }, e.re, e.im, unsafe { cabsf(CplxF { re: 6.0, im: 8.0 }) }, zf.re, zf.im);
let s = unsafe { inet_ntoa(InAddr { s_addr: 0x0100_007f }) };
let n = unsafe { strlen(s) };
println!("inet_ntoa len {} h={}", n, hash_bytes(s, n));
}
#[test]
fn callbacks_from_c() {
let mut v = [5i32, 3, 9, 1, 7, -4, 12, 0];
unsafe { qsort(&mut v[0] as *mut i32 as *mut u8, 8, 4, cmp_i32) };
println!("sorted {} {} {} {} {} {} {} {}", v[0], v[1], v[2], v[3], v[4], v[5], v[6], v[7]);
let key = 7i32;
let p = unsafe { bsearch(&key as *const i32 as *const u8, &v[0] as *const i32 as *const u8, 8, 4, cmp_i32) };
let idx = (p as usize - (&v[0] as *const i32 as usize)) / 4;
println!("bsearch idx {}", idx);
}
#[test]
fn extern_c_bodies() {
let b = c_big(Big { a: 3, b: 1.25, c: 10, d: 255, e: 1.5 }, 4);
println!("big {} {} {} {} {}", b.a, b.b, b.c, b.d, b.e);
let h = c_hfa(Hfa3 { x: 1.0, y: 2.0, z: 3.0 }, 0.5);
println!("hfa {} {} {}", h.x, h.y, h.z);
let m = c_mixed(Mixed { i: 1, f: 2.5, l: 100 }, Bytes3 { a: 3, b: 4, c: 5 });
println!("mixed {} {} {}", m.i, m.f, m.l);
let r = c_rect(Rect { x: 1.0, y: 2.0, w: 30.0, h: 40.0 }, 1.5, 2.5);
println!("rect {} {} {} {}", r.x, r.y, r.w, r.h);
let t = c_many(1, 0.5, 2, 0.25, 3, 0.125, 4, 1.0, 5, 2.0, 6, 3.0, 7, 4.0, 8, 5.0, 200, 0.75, -9, 6.0, Mixed { i: 0, f: 0.5, l: 1000 }, Hfa3 { x: 0.25, y: 9.0, z: 0.5 });
println!("many {}", t);
}