Files
AILang/docs/journals/2026-05-12-iter-hs.3.md
T
Brummel 1cf281b217 iter hs.3: heap-Str runtime additions — str_alloc + int_to_str + float_to_str
Append three new symbols to runtime/str.c: a private str_alloc(uint64_t)
slab helper that allocates [rc_header | len | bytes... | NUL] via
ailang_rc_alloc and writes the len prefix, plus two extern formatters
ailang_int_to_str(int64_t) and ailang_float_to_str(double) that compose
str_alloc with snprintf("%lld") / snprintf("%g"). Defensive abort() on
truncation; 64-byte stack buffer comfortably oversized for either
formatter.

The extern declaration of ailang_rc_alloc carries __attribute__((weak)).
First regression sweep without it surfaced 11 e2e link failures under
--alloc=gc and --alloc=bump: public T-visible symbols (int_to_str /
float_to_str) pull their transitive callees (rc_alloc) into every
binary's symbol set, but rc.c is not linked under gc/bump in hs.3.
The weak attribute makes the cross-allocator link safe in isolation;
under rc the strong definition wins as usual. Retained after hs.4
(when rc.c becomes unconditionally linked) as a no-op that keeps
str.c link-safe in isolation.

No IR-side caller wired yet — hs.4 lands the IR-header declare lines,
the int_to_str/float_to_str codegen lowering, the checker install,
and the unconditional rc.c link.

cargo test --workspace + cross_lang.py + compile_check.py + check.py
all green on re-sweep.
2026-05-12 18:03:46 +02:00

5.4 KiB

iter hs.3 — Heap-Str runtime additions

Date: 2026-05-12 Started from: 187d04814d Status: DONE Tasks completed: 2 of 2

Summary

Third iter of the heap-Str ABI milestone. Single-file additive change to runtime/str.c: three new symbols (private static char *str_alloc(uint64_t) slab helper plus two extern formatters ailang_int_to_str(int64_t) and ailang_float_to_str(double)), the supporting <stdint.h> / <stdio.h> / <stdlib.h> includes, and an extern void *ailang_rc_alloc(size_t) declaration. No IR-side caller is wired yet — hs.4 will land codegen + checker + linker work that makes the formatters live builtins.

One deviation from the plan: the extern declaration of ailang_rc_alloc was promoted from plain-external to __attribute__((weak)). The plan's assertion that the new symbols sit "present-but-unreferenced" across all --alloc strategies turned out to be wrong at the link layer — ailang_int_to_str / ailang_float_to_str are publicly-visible T symbols, so the linker preserves them and their transitive callees (including ailang_rc_alloc) in every binary regardless of program-level usage. Under --alloc=gc and --alloc=bump the build pipeline does not link rc.c, so the unresolved-symbol error fires at link time and took out 11 e2e tests on the first regression sweep. The weak attribute makes the reference link-safe in isolation: under gc/bump it resolves to NULL (never dereferenced — no IR caller exists yet); under rc the strong definition wins as usual. The attribute is retained after hs.4 unconditionally links rc.c (where it becomes a no-op) to keep runtime/str.c link-safe in isolation, matching the spec's "single-file abstraction" framing of the runtime split.

Acceptance verified: cargo test --workspace (zero failures across the full test surface), bench/cross_lang.py (25/25 metrics stable, exit 0), bench/compile_check.py (24/24 metrics stable, exit 0), bench/check.py (zero regressed, exit 0; same five latency.explicit_at_rc.* improvements outside tolerance that audit-cma and audit-ms have already documented as not-coupled-to-milestone — baseline left untouched per established practice). clang -c -O2 -Wall -Werror on runtime/str.c standalone also exits 0 with no warnings.

Per-task notes

  • iter hs.3.1: appended three #include lines (stdint.h, stdio.h, stdlib.h), one weak extern void *ailang_rc_alloc declaration, and three new function bodies (str_alloc, ailang_int_to_str, ailang_float_to_str) to runtime/str.c. Compile-check via clang -c -O2 -Wall -Werror green; nm symbol-table inspection: 4 public T symbols (ail_str_eq, ail_str_compare, ailang_int_to_str, ailang_float_to_str), str_alloc absent at -O2 (inlined into both callers — only visible as t str_alloc at -O0; static-linkage invariant preserved), ailang_rc_alloc shown as w (weak undefined).
  • iter hs.3.2: ran the four regression gates. First-pass sweep surfaced the 11-test linker failure under --alloc=gc/bump caused by the plan's "rc_alloc reference is harmless if unreferenced" assumption; weak-attribute repair re-ran clean. All four gates green on the re-sweep.

Concerns

  • The plan's Task 1 Step 7 nm-output expected-line block ("U ailang_rc_alloc" and "0000000000000000 t str_alloc") is inaccurate at the optimisation level the plan itself specifies (-O2): the grep regex does not match ailang_rc_alloc, and clang -O2 inlines the 2-line str_alloc helper into both callers so no t str_alloc symbol survives the compilation unit. Both deviations are -O2 consequences, not bugs — the semantically meaningful invariants (str_alloc is static and absent from T; ailang_rc_alloc is a non-T undefined reference) both hold. A future plan author writing nm-output checks should inspect at -O0 for symbol presence and at -O2 for what actually ships, or relax the expected text from literal-match to invariant-check (e.g. "no T str_alloc", "ailang_rc_alloc is U or w").
  • The plan's core "no build-pipeline change in hs.3" boundary held, but the supporting claim "symbols are present in every binary regardless of allocator choice" was false at the link layer. The weak-attribute repair makes it true at the cost of one extra attribute and a long-form doc-comment explaining the cross- allocator linking story. A future plan that defers a build- pipeline change should explicitly call out cross-translation-unit symbol dependencies as a gating concern.

Known debt

  • hs.4 will wire ailang_int_to_str / ailang_float_to_str from the IR side and unconditionally link rc.c; at that point the __attribute__((weak)) on the ailang_rc_alloc declaration becomes a no-op (strong definitions always satisfy weak references). The attribute is intentionally retained — see the doc-comment in runtime/str.c — to keep the translation unit link-safe in isolation, matching the parent spec's framing of runtime/str.c as the heap-Str ABI's single point of contact.

Files touched

  • runtime/str.c — three new functions appended after ail_str_compare, plus three new #include lines and one weak extern declaration after the existing include block. Existing ail_str_eq / ail_str_compare bodies and the file's top comment block untouched.

Stats

bench/orchestrator-stats/2026-05-12-iter-hs.3.json