iter embedding-abi-m2.1 (PARTIAL 4/9 + Boss spec-defect repair): ctx ABI + de-globalisation

Tasks 1-4 land fully review-green and are the cohesive shippable
subset (the per-thread embedding ABI, fully wired + proven):
- T1: FIXED-FIRST alloc-guard baseline pin (RED captured -> GREEN at T4).
- T2: runtime/rc.c gains ailang_ctx_t {alloc_count,free_count} +
  ailang_ctx_new/_free + __thread __ail_tls_ctx; the two increment
  sites become 'if (_ctx) _ctx->... else g_rc_...'. g_rc_* statics +
  ailang_rc_stats_atexit + the constructor RETAINED VERBATIM as the
  null-ctx (single-threaded executable) fallback. rc_accounting_tsan.c
  + driver: per-ctx 8x200000 tsan-clean (de-globalisation positive proof).
- T3: codegen Target::StaticLib forwarder gains a leading 'ptr %ctx'
  + one '@__ail_tls_ctx = external thread_local global ptr' decl +
  TLS save/store/restore around the BYTE-UNCHANGED internal call;
  _adapter/_clos + internal arg vector untouched (M1 decision held);
  doc-comment provisionality narrowed to the value/record layout.
- T4: build_staticlib rejects --alloc != rc (RC-only swarm artefact).

Boss adjudication of the orchestrator's Task-5 BLOCKED (spec-defect,
correctly surfaced not hacked): swarm.c's -DSHARED_CTX negative
control is structurally impossible — examples/embed_backtest_step.ail
is a non-allocating scalar kernel that never writes a ctx field, and
__ail_tls_ctx is __thread, so a shared-ctx scalar swarm has no
shared-memory write to race on (the spec's OWN item-1 honesty point).
The de-globalisation teeth belong to the item-1 rc_accounting harness
(shared ctx genuinely races on ctx->alloc_count; orchestrator
independently confirmed tsan exit 66). Spec amended in lockstep
(Goal coherent-stop, must-fail-axis item 2, Testing item 3,
Acceptance) so item 3's negative control is item-1's by the
de-globalisation-proof / capability-demo split; plan Task 5
restructured (swarm.c per-ctx capability demo only; negative-control
standing test relocated into the item-1 harness driver) + Task 3's
plan-transcription error (@ail_backtest_step ->
@ail_embed_backtest_step_step) corrected. This is a Boss
consistency-repair of an internally-contradictory clause whose intent
is already correctly realised in the same spec — not a redesign; no
brainstorm bounce. Task-5 working files discarded (corrected plan
reproduces them; a structurally-RED test must not enter main).
INDEX.md + final journal land at iter completion (re-dispatch [5,9]).
This commit is contained in:
2026-05-18 17:16:40 +02:00
parent b3388c8350
commit c9a84b33b3
11 changed files with 494 additions and 85 deletions
+18 -5
View File
@@ -171,7 +171,11 @@ pub enum Target {
/// `--emit=staticlib`: suppress the `@main` trampoline AND the
/// `MissingEntryMain` shape-check (a kernel module is a library);
/// emit one external C entrypoint per `(export …)` fn forwarding
/// to `@ail_<module>_<fn>`. Provisional signature until M3.
/// to the internal `@ail_<module>_<def>`. M2: the entrypoint takes
/// a mandatory leading `ptr %ctx` (per-thread embedding context)
/// published via `@__ail_tls_ctx` around the unchanged internal
/// call. The ctx-threaded C signature is the M2 shape; only the
/// value/record layout remains provisional until M3.
StaticLib,
}
@@ -606,6 +610,11 @@ fn lower_workspace_inner(ws: &Workspace, alloc: AllocStrategy, target: Target) -
// the internal callee takes the scalars positionally
// (confirmed against `emit_fn`'s signature emission,
// `lib.rs:1087`).
// M2: the per-thread embedding ctx slot. Defined by
// runtime/rc.c (__thread ailang_ctx_t *__ail_tls_ctx);
// the forwarder publishes its explicit ctx param here for
// the synchronous duration of the internal call.
out.push_str("\n@__ail_tls_ctx = external thread_local global ptr\n");
for (mname, m) in &ws.modules {
for d in &m.defs {
if let Def::Fn(f) = d {
@@ -617,17 +626,21 @@ fn lower_workspace_inner(ws: &Workspace, alloc: AllocStrategy, target: Target) -
(check-side gate should have rejected it)"))),
};
let cret = llvm_scalar(&ret_ty);
let params: Vec<String> = param_tys.iter().enumerate()
.map(|(i, t)| format!("{} %a{i}", llvm_scalar(t)))
.collect();
let args: Vec<String> = param_tys.iter().enumerate()
.map(|(i, t)| format!("{} %a{i}", llvm_scalar(t)))
.collect();
let mut sig_params = vec!["ptr %ctx".to_string()];
sig_params.extend(
param_tys.iter().enumerate()
.map(|(i, t)| format!("{} %a{i}", llvm_scalar(t))));
out.push_str(&format!(
"\ndefine {cret} @{sym}({}) {{\n \
%saved = load ptr, ptr @__ail_tls_ctx\n \
store ptr %ctx, ptr @__ail_tls_ctx\n \
%r = call {cret} @ail_{mname}_{}({})\n \
store ptr %saved, ptr @__ail_tls_ctx\n \
ret {cret} %r\n}}\n",
params.join(", "),
sig_params.join(", "),
f.name,
args.join(", "),
));