# iter hs.2 — Static-Str sentinel-slot retrofit

**Date:** 2026-05-12
**Started from:** 51a096cf8354362783d8caf2ec4b35179d935a7a
**Status:** DONE
**Tasks completed:** 3 of 3

## Summary

Second iter of the heap-Str ABI milestone. Surgical codegen-side
retrofit of hs.1's static-`Str` global layout: per the amended parent
spec (`docs/specs/2026-05-12-heap-str-abi.md`) the sentinel rc-header
slot drops. Language `Str` literals now emit as
`<{ i64, [N+1 x i8] }> <{ i64 N, [N+1 x i8] c"…\00" }>` instead of
the hs.1 shape `<{ i64, i64, [N+1 x i8] }> <{ i64 -1, i64 N, …}>`. The
explicit `len` field becomes the first (and only non-byte) field on
which the IR-`Str` pointer lands via constexpr-GEP `i32 0, i32 0`. The
`+8 GEP` consumer-side calls (`@puts`, `@ail_str_eq`, `@ail_str_compare`,
inline `@strcmp`) do **not** change — the bytes still sit 8 bytes past
the `len`-field. Static-Str pointers are kept out of `ailang_rc_dec`
at the codegen level via move-tracking (iter 18d.3) and non-escape
lowering (iter 18b), so no header slot is needed at the global.

Change surface: three format-string sites and two doc-comments in
`crates/ailang-codegen/src/lib.rs`, rename + assertion update of two
IR-shape tests in the same crate's test module, plus snapshot
regeneration of `crates/ail/tests/snapshots/hello.ll`. No runtime
changes, no checker changes, no new fixtures. Static-Str globals are
now 8 bytes smaller per literal.

Acceptance verified end-to-end: `cargo test --workspace` green (every
test result `ok`, 0 failures across the workspace), `bench/cross_lang.py`
exit 0 (byte-identical stdout across all `ail/c` benchmark pairs),
`bench/compile_check.py` exit 0, `bench/check.py` exit 0 within the
known `latency.explicit_at_rc.*` nondeterminism tolerance.

## Per-task notes

- iter hs.2.1: renamed `static_str_global_uses_packed_struct_with_sentinel_and_len`
  → `static_str_global_uses_packed_struct_with_len`; updated its
  doc-comment and assertion substring (struct loses leading `i64, `,
  initialiser loses leading `i64 -1, `). Updated assertion in
  `static_str_callsite_pointer_is_payload_via_constexpr_gep` (name
  preserved — "payload" still describes the `len`-field-as-pointer-target;
  struct loses leading `i64, `, GEP indices `i32 0, i32 1` → `i32 0, i32 0`).
  RED confirmed: both tests fail against hs.1 emission with the expected
  assertion-substring mismatch. The four `+8 GEP` tests stayed green.
- iter hs.2.2: flipped the global-emission loop format string at
  `lib.rs:471` to the 2-field shape; flipped both `Literal::Str` arms
  (`emit_const_def` ~ `lib.rs:933`, `lower_term` ~ `lib.rs:1255`) to
  emit constexpr-GEP `i32 0, i32 0` onto the 2-field struct type;
  updated the two doc-comments on the `str_literals` field
  (`lib.rs:561`) and on `intern_str_literal` (`lib.rs:2596`) to the
  amended-spec wording. All five edits exactly per plan text. Static_str_
  tests GREEN, `_calls_` tests GREEN, full `cargo test -p ailang-codegen`
  GREEN (33 tests).
- iter hs.2.3: regenerated `crates/ail/tests/snapshots/hello.ll` via
  `UPDATE_SNAPSHOTS=1 cargo test -p ail --test ir_snapshot hello`. The
  two affected lines (the `@.str_hello_str_0` global and its constexpr-
  GEP at `%v1`) flipped to the new shape; the outer `i64 8` byte offset
  preserved. Workspace `cargo test --workspace` green; `cross_lang.py`,
  `compile_check.py`, `check.py` all green.

## Concerns

(none)

## Known debt

(none)

## Files touched

- `crates/ailang-codegen/src/lib.rs` — three format strings, two
  doc-comments, two test names + assertions + doc-comments
- `crates/ail/tests/snapshots/hello.ll` — regenerated snapshot
  (2 lines content change, byte offsets preserved)

## Stats

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