/* Embedding-ABI M3 two-record-param round-trip C host (own + borrow * via a compile-time -DBORROW switch). Backfill: M3 shipped fixtures * only ever push a *scalar* per-tick sample; this host pushes a * single-ctor all-scalar *record* `Tick` as the per-call payload — * the actual minimal data-server binding shape. Frozen value layout * (design/contracts/0006-frozen-value-layout.md): * * State (24-byte payload): * p - 8 .. p uint64_t refcount header (set to 1 by ailang_rc_alloc) * p + 0 int64_t constructor tag (single ctor -> 0) * p + 8 IEEE-754 double field 0 = Float acc * p + 16 int64_t field 1 = Int n * Tick (8-byte payload): * p - 8 .. p uint64_t refcount header (set to 1 by ailang_rc_alloc) * p + 0 int64_t constructor tag (single ctor -> 0) * p + 8 IEEE-754 double field 0 = Float px * * own : the kernel consumes BOTH `(own (con State))` and * `(own (con Tick))` inputs (drop at return); the host must NOT * touch/dec either after the call. * borrow: `State` is `(own ...)` (kernel-consumed, as own); `Tick` is * `(borrow (con Tick))` — the kernel does NOT consume it; the * host retains the Tick and dec's it itself each iter. * In both modes the return is host-owned and host-freed. */ #include #include #include #include typedef struct ailang_ctx ailang_ctx_t; extern ailang_ctx_t *ailang_ctx_new(void); extern void ailang_ctx_free(ailang_ctx_t *); extern void *ailang_rc_alloc(size_t); extern void ailang_rc_dec(void *); extern void *backtest_step_tick(ailang_ctx_t *ctx, void *st, void *tick); static void *make_state(double acc, int64_t n) { void *p = ailang_rc_alloc(8 + 2 * 8); /* header=1 set by runtime */ *(int64_t *)((char *)p + 0) = 0; /* single-ctor tag = 0 */ memcpy((char *)p + 8, &acc, 8); /* Float acc @ 8 */ *(int64_t *)((char *)p + 16) = n; /* Int n @ 16 */ return p; } static void *make_tick(double px) { void *p = ailang_rc_alloc(8 + 1 * 8); /* header=1 set by runtime */ *(int64_t *)((char *)p + 0) = 0; /* single-ctor tag = 0 */ memcpy((char *)p + 8, &px, 8); /* Float px @ 8 */ return p; } int main(void) { ailang_ctx_t *ctx = ailang_ctx_new(); void *st = make_state(0.0, 0); double expected = 0.0; for (int i = 0; i < 1000000; i++) { double px = (double)(i & 7); expected += px; void *tick = make_tick(px); void *next = backtest_step_tick(ctx, st, tick); #ifdef BORROW ailang_rc_dec(tick); /* borrow: host retained Tick, frees it */ #endif /* own: kernel consumed `st` AND `tick`; do NOT dec them here. */ st = next; /* return is host-owned */ } double acc; memcpy(&acc, (char *)st + 8, 8); int64_t n = *(int64_t *)((char *)st + 16); assert(n == 1000000); assert(acc == expected); ailang_rc_dec(st); /* host frees the final return */ ailang_ctx_free(ctx); /* AILANG_RC_STATS readback fires here */ return 0; }