backend-lang 0.4.0

Native backend: instruction selection from ir-lang, SSA register allocation with stack maps, and x86-64 emission through isa-lang.
Documentation
# backend-lang - Roadmap

> Path from scaffold to a stable 1.0. Hard parts are front-loaded; each phase has hard exit criteria.
> Master plan: ../_lexersketch/ROADMAP.md and ../_lexersketch/NEW-LIBS.md
>
> **Anti-deferral rule:** no listed hard task moves to a later phase unless this file records the move and the reason.

## v0.1.0 - Scaffold (DONE)
Compiles, CI green, structure correct, no domain logic.
- [x] Manifest, README, CHANGELOG, REPS, dual license, CI, deny, clippy, rustfmt, DIRECTIVES, ROADMAP.

## v0.2.0 - Foundation (DONE)
- [x] Machine IR, target-description model, ABI/frame model; parallel-move resolver with property tests.
- [x] Emission of physical-register MIR through isa-lang, proven end to end (MIR → isa-lang →
      object-lang → a running Linux process).

Delivered:
- **Machine IR** (`mir`): `Function` of blocks of target instructions; virtual registers typed
  by class, physical registers, operands (register, immediate, `[base + index*scale + disp]`,
  stack slot with offset, function symbol, condition code, branch target with arguments);
  per-instruction implicit uses, definitions, and clobbers (`Effects`); block parameters; local
  and incoming stack slots; dense 32-bit ids for every table; construction-time operand checks;
  `verify` (terminators, branch arities and classes, SSA single definition) in linear time; a
  deterministic text form. Nothing recursive.
- **Block parameters, not phis** (decided here): the same information as phi nodes, kept on the
  edge as the parallel copy it is. ir-lang already uses block parameters (isel maps them one to
  one), an SSA register allocator resolves each edge with the same resolver, and there is no phi
  list to keep in sync with predecessor order.
- **Target description, backend half** (`target`): registers, classes (with allocatable sets and
  spill sizes), and the instruction table as data: per row the operand rules (definitions, uses
  with accepted shapes and immediate ranges, two-address ties, conditions, branch targets), the
  implicit uses/definitions, and clobbers. The flags are a physical register; an instruction
  that may leave some flags unchanged (`inc`/`dec`, shifts by a possibly-zero count, `bt`) is
  modelled as also reading them. isa-lang 0.2 does not carry implicit operands yet (its ROADMAP
  v0.5), so this layer records them; a test cross-checks every row's flag effects and fixed
  registers against isa-lang's own data.
- **x86-64 description** (`x86_64`): 33 registers (16 GPR, 16 XMM, flags), classes `gpr`/`vec`/
  `flags`, 91 instruction rows (moves, loads, stores, `lea`, the ALU at 32 and 64 bits, compare,
  test, `bt`, unary ops, multiply/divide with `rdx:rax`, sign extension, shifts by immediate and
  `cl`, `setcc`, `cmov`, `jmp`, `jcc`, `call`, `ret`, `ud2`, `int3`, `nop`, `syscall`), each
  mapped to the isa-lang instruction it encodes as.
- **ABIs** (`target::abi`): SysV AMD64 and Win64: argument and return registers (separate
  counters vs positional), callee-saved sets including Win64's `xmm6`-`xmm15`, call clobbers,
  stack alignment, shadow space, red zone, probe interval; `assign_args`/`assign_rets`.
- **Frame model** (`target::frame`): callee-saved pushes (vector registers into 16-byte frame
  slots), locals by alignment, outgoing area with shadow space, 16-byte alignment at calls, the
  SysV red zone for small leaves, page-by-page probes (unrolled up to four pages, a counted loop
  beyond), and the prologue/epilogue as a step plan.
- **Parallel-move resolver** (`moves`): any permutation, cycles of every length, fan-out,
  register↔slot, immediates; cycles broken for free by an existing fan-out copy, by `k - 1` swaps
  (x86-64 `xchg`), or through a scratch location (a free register, else a scratch slot);
  memory-to-memory moves and unstorable immediates through a free register, else a borrowed one
  saved to a victim slot. Minimal step counts checked against a reference; `O(n log n)` (one
  sort), buffers reused.
- **Emission** (`x86_64::Emitter`, Tier-1 `emit`): verified physical-register functions, frame
  prologues/epilogues, every edge's parallel copy (critical edges split by branching around the
  taken edge's moves), calls between functions by name, function addresses by RIP-relative
  `lea`, branch relaxation by isa-lang.
- **Proof:** an x86-64 interpreter over isa-lang's decoder in the test suite runs emitted
  programs and random jump/branch edges on every platform; three hand-built programs (Fibonacci
  with parallel-copy edges and a stack-slot parameter, recursive factorial across two functions,
  a mix of the table with an indirect call) are wrapped by object-lang 0.2 into static ELF
  executables and run on Linux x86-64 (exit status checked, ETXTBSY retried); every opcode over
  913 operand shapes; frame and resolver properties; hostile construction never panics.
- Benchmarks at 100,000 instructions (build, verify, emit, print) and the resolver from 4 to
  100,000 moves.

Dependency wiring (decided here, recorded per the anti-deferral rule):
- **isa-lang 0.2: wired** (crates.io). Every byte of the encoding is isa-lang's; the emitter
  drives its `Assembler` (labels, relaxation, call and RIP fixups).
- **object-lang 0.2: dev-dependency only.** The library produces code bytes; wrapping them into
  an executable is the AOT driver's job (aot-lang 2.0). The end-to-end test uses it.
- **ir-lang: not wired.** Instruction selection consumes ir-lang 2.0 (calls, memory,
  aggregates); 1.x lacks them (ISSUES H05), so isel waits for it (see v0.5.0 below).
- **pager-lang: not wired.** Making code executable is the JIT's job (jit-lang 2.0).

Moved out of v0.2.0, with reasons:
- **Instruction selection from ir-lang** → v0.5.0: it needs ir-lang 2.0, which is not released.
  The task for 0.2 scoped it out explicitly; MIR is built directly until then. *Delivered in
  v0.3.0, against ir-lang 2.0.0-alpha.1.*
- **Dominance checking in `verify`** → v0.5.0, with the SSA register allocator whose
  precondition it is (it needs the dominator tree the allocator builds anyway). *Delivered in
  v0.3.0 as `Function::verify_ssa`.*
- **Vector-register code (XMM moves, Win64 `xmm6`-`xmm15` saves) and floating point** → v0.5.0:
  isa-lang 0.2 encodes no SSE. The registers, classes, ABIs, and frame plan already include them;
  emission reports `EmitError::Unencodable`.
- **`syscall` as an isa-lang form** → isa-lang 0.5 (requested there): emitted as raw bytes now.
- **Implicit operands as isa-lang data** → isa-lang 0.5; then the cross-check here compares the
  full implicit sets, not only flags and fixed registers.
- **Aggregates by value, variadic calls, 128-bit integers in the ABIs** → v0.5.0, with call
  lowering from ir-lang 2.0's aggregates.
- **Unwind data** (Win64 `.pdata`/`.xdata`, DWARF CFI) generated from `FrameLayout` → v0.5.0,
  when object-lang carries those sections; external-symbol relocations and function alignment
  need isa-lang 0.5's assembler relocations and alignment directive.
- **The NOML reader for target descriptions** (TARGET-DESC §13) → v0.5.0, with the pattern
  tables that are its first second consumer.

## v0.3.0 - Instruction selection, register allocation, emission (DONE)
ir-lang 2.0 → MIR → x86-64 bytes for the integer, control-flow, memory, call, and
checked-arithmetic subset, differential-tested against ir-lang's interpreter.
Exit criteria:
- [x] Instruction selection for every OPS integer operation under every policy ir-lang
      2.0.0-alpha.1 can carry, comparisons, select, conversions, memory, calls, tail calls,
      switches, `check`, `invoke`, and `resume`.
- [x] SSA register allocation with spilling, rematerialization, coalescing, edge parallel
      copies through the resolver, per-ABI callee-saved handling, and exact stack maps.
- [x] A module compiles to one code buffer with symbols, call-site records, data, and the
      relocations object-lang 0.2 writes; internal calls resolved.
- [x] Differential harness (test interpreter everywhere, native on x86-64 Linux and
      Windows), allocator properties, stack-map exactness, compile and code-quality benches.

Delivered:
- **Tier-1 path** (`compile`, `compile_function`, `Options`, `Compiled`, `DataObject`,
  `CompileError`): validate the module, select, allocate, and emit every defined function into
  one buffer; globals become `DataObject`s with their `Abs64` relocations; imports, globals, and
  the runtime entry points (`trap_symbol`, `gc_poll_symbol`, `resume_symbol`) stay relocations;
  `imports()`/`exports()` list the symbols a linker needs.
- **Instruction selection** (`isel::select`, `IselError`): blocks and block parameters map one
  to one. Narrow values live in the low bits of a 64-bit register with the upper bits
  unspecified, extended by their signedness wherever the upper bits matter; `bool` is always
  0 or 1. Every integer operation (`add sub mul div rem floor_div floor_mod and or xor shl shr
  min max neg abs not`) under `wrap`, `trap` (a per-function trap block per OPS code), and
  `error` (inside `check`, branching to the error edge with the OPS code as `err`): flag
  checks at 32/64 bits, exact 32-bit arithmetic plus a range check at 8/16 bits, `u32`/`u64`
  multiply overflow from the high half, division guarded for zero and `MIN / -1` before the
  processor can fault, floor variants by `cmov` fix-ups, shifts masked or range-checked.
  Comparisons fuse into their branch or `select` (`cmov`) when used once in the same block.
  Conversions `int_cast` (checked), `zext`, `sext`, `narrow`, `bitcast`, `bool_to_int`,
  `ptr_to_int`/`int_to_ptr`/`ref_to_ptr`, and `char_from_u32` (`check` only). Memory: loads and
  stores of every integer width (alignment needs no instruction on x86-64), stack slots
  (alignment above 16 by rounding the address up inside an enlarged slot), `field_addr`,
  `elem_addr` (scaled `lea` or a multiply), `ptr_offset`, `global_addr`, `func_addr`, constant
  offsets folded into memory operands, `memcpy`, `memmove` (direction by address order),
  `memset`, atomic loads and stores (`xchg` for `seq_cst` stores), `atomic_rmw xchg`, and
  `fence seq_cst` (a locked `xchg`). Calls direct and indirect, with stack arguments, results
  in registers, and 8/16-bit values extended at the boundary; tail calls (direct and through
  `r11`) whose stack arguments fit the caller's incoming area; `switch` as a bounds-checked jump
  table when the cases are dense (`Options::jump_table_min_cases`, `jump_table_max_spread`),
  else a balanced compare tree; `safepoint` as a call to the GC poll; the conventions `fast`,
  `c`, `sysv64`, and `win64`.
- **`invoke` and landing pads, minimal**: the call records its landing pad (MIR `CallSite`,
  emitted as `CallSiteRecord::landing_pad`); the unwinder is expected to enter the pad's stub
  with the frame as it was at the call (callee-saved registers restored) and the payload in
  `rax`; the stub (`landingpad`) reloads the relocated references from their root slots and
  jumps to the IR landing pad with `exn` and `resI`. `resume` calls `resume_symbol`. What is
  missing is listed under v0.5.0.
- **SSA register allocation** (`regalloc::allocate`, `allocate_with`, `Options`,
  `Allocation`, `Location`, `RegAllocError`): liveness by SSA path exploration with a budget;
  constraints from one backward walk (last uses, physical registers live after each
  instruction, registers written during each value's life); coloring in dominance order with no
  interference graph; coalescing by affinity classes over copies, ties, and edges, plus hints
  toward fixed registers (selection orders commutative operands so that one used only there is
  the tied one);
  spill-everywhere with constants and addresses rematerialized at their uses, other values
  stored once and read from the slot directly where the instruction takes a memory operand,
  else through a short-lived reload temporary; iteration to a fixed point with a guaranteed
  final round (`MAX_ROUNDS`). Callee-saved registers are ordinary registers; a value live
  across a call can only take one, and the frame saves exactly those written.
- **Stack maps** (IR.md §9 statepoints): each GC reference in a safepoint's list is stored into
  a root slot before the call and reloaded after it as the relocated value, so no reference is
  in a register while a collector runs; the slots are the call's stack map
  (`CallSiteRecord::gc_slots`, offsets from `rsp` at the call).
- **MIR**: `Function::verify_ssa` (dominance in linear time: dominators by Lengauer–Tarjan,
  then O(1) interval tests on the dominator tree), call-site records (`set_call_site`,
  `call_site`, `call_sites`, `CallSite`), `MirError::{NotACall, UndefinedVReg, NotDominated}`;
  x86-64 rows `tailcall`, `jmptbl`, `landingpad`, `xchg8/16/32/64`;
  `InstKind::{TailCall, Switch}`.
- **Emission**: `Emitter::declare_external` and relocations (`Reloc`, `RelocKind::{Call32,
  Pc32, Abs64}`) for symbols defined elsewhere; `Program::{functions, relocations}`;
  `EmittedFunction` (offset, size, frame, call-site records); jump tables placed after their
  function; `EmitError::{Constraint, ExternalConflict}`.
- **Proof**: a random generator of valid, UB-free ir-lang 2.0 programs (every integer op and
  policy, checks with error edges, loops whose back edges permute their parameters, dense and
  sparse switches, slots and globals through field and element addresses, block memory
  operations, direct and indirect calls, tail calls, high pressure) compiled and run against
  ir-lang's interpreter, in the test interpreter under SysV and Win64 everywhere and natively
  in-process on x86-64 Linux and Windows; OPS conformance on edge values for every op × type ×
  policy; allocator assignments checked point by point by an independent liveness; a moving
  collector that walks real frames by the call-site records; a minimal unwinder for landing
  pads; modules written as ELF relocatables by object-lang and linked into a static executable
  that runs on Linux (ETXTBSY retried).

Dependency wiring (decided here, recorded per the anti-deferral rule):
- **ir-lang `=2.0.0-alpha.1`: wired** (crates.io, pinned exactly because it is a pre-release).
  Instruction selection reads it; `ir_lang` is re-exported. Its `interp` feature is a
  dev-dependency only (the oracle).
- **isa-lang 0.2: wired**, unchanged.
- **object-lang 0.2: dev-dependency only**: the relocations `Compiled` reports are the ones an
  ELF relocatable carries; the link test writes and reads them back.

Moved out of v0.3.0, with reasons:
- **`shift = saturate`** → when ir-lang carries it: OPS v2 defines it, but ir-lang
  2.0.0-alpha.1's `Shift` has only `error` and `mask`, so no ir-lang program can ask for it and
  no oracle can check it. The shift lowering already range-checks the amount; saturation is a
  `cmov` on the same compare. *Delivered in v0.4.0, against ir-lang 2.0.0-alpha.3.*
- **Floating point and SIMD** (types, operations, conversions, arguments, loads) → v0.5.0:
  isa-lang 0.2 encodes no SSE (ISSUES P11). Refused as `IselError::Unsupported`.
- **`atomic_rmw` other than `xchg`, and `cmpxchg`** → v0.5.0: they need `lock`-prefixed forms
  isa-lang 0.2 does not encode (an addition to ISSUES P11). Refused.
- **Unwind tables** (Win64 `.pdata`/`.xdata`, DWARF CFI, an LSDA mapping call sites to landing
  pads) and a personality routine → v0.5.0: object-lang 0.2 carries no such sections. The
  call-site records and `FrameLayout` are their input; the tests use their own minimal unwinder.
- **More results than return registers** (three under SysV, two under Win64) → v0.5.0 with
  aggregate and sret lowering. Refused.
- **Tail calls needing more stack-argument space than the caller received** → v0.5.0 (the
  `fast` convention reserving a fixed area). Refused.
- **Compile throughput (target ≥1M IR instructions/s)**: not met reliably on this machine;
  measured numbers are in the release notes. The allocator reruns liveness and coloring for
  every spill round (two or three on large functions); an incremental later round is v0.9.0
  work. *Delivered in v0.4.0 (ISSUES P32): liveness is analysed once and updated per round.*

## v0.4.0 - ir-lang 2.0.0-alpha.3, OPS v2, compile throughput (DONE)
The native path from irgen-lang to machine code (ISSUES P37), and the part
of P32 that fits: OPS v2 selected and checked, allocation without a
liveness analysis per spill round.
Exit criteria:
- [x] `ir-lang = "=2.0.0-alpha.3"` as the dependency and (with `interp`) the
      dev-dependency; alpha.2's and alpha.3's changes handled (`pow`,
      `Shift::Saturate`, `can_error(policy, ty)`, the P36 negative-exponent
      rule, new `BuildError` variants, statepoints from
      `insert_statepoints`, block parameters from the variables API).
- [x] Integer `pow` under `wrap`, `trap`, and `error`, with its `check`
      error edge and code 6; float `pow` bit-identical to `ls_pow`;
      saturating shifts, negative amounts on the error edge; each checked
      natively against ir-lang's interpreter.
- [x] Compile throughput at least 1M IR instructions per second on the
      existing benchmark (`compile_100k`), measured before and after.
- [x] Differential tests over OPS v2, programs built through the variables
      API, and statepoint-carrying functions; hostile and huge inputs.

Delivered:
- **ir-lang 2.0.0-alpha.3.** Selection follows `BinaryOp::can_error(policy,
  ty)`: a signed `pow` and a signed saturating shift arrive only as the
  operation of a `check` (ir-lang refuses them as plain instructions), and
  the backend lowers whatever failure they raise to that edge, never to the
  trap block. `Overflow::Trap` still traps (code 1) on an unrepresentable
  power; a negative exponent is the `NegativeExponent` error (code 6) under
  every policy (ISSUES P36). The new `BuildError` variants are the builder's
  and never reach the backend (it reads finished modules). `insert_statepoints`
  output (GC lists on calls, relocated references as call results, `resN` on
  both edges of an `invoke`, block parameters where relocated versions meet)
  needed no change: it is the statepoint form 0.3 already selected, now
  tested as produced. The variables API's block parameters are ordinary block
  parameters.
- **Integer `pow`** (`isel`): repeated squaring, a square taken only while a
  higher exponent bit remains so every product is a factor of the result.
  A constant exponent unrolls (at most 64 squarings and 64
  multiplications); a run-time one loops over its bits (four blocks, at most
  64 iterations). `wrap` multiplies at 64 bits (the low bits are exact at
  every width); `trap`/`error` check every product: `imul` overflow at 64
  bits, `mul`'s for `u64`, and 8/16/32-bit types computed exactly in 64 bits
  with a range check at the end.
- **Float `pow`** (`isel`, `Options::{pow_f64_symbol, pow_f32_symbol,
  with_pow_runtime}`): a call to the runtime's routine, which is ir-lang's
  `ls_pow` (for `f32`, the widened operands, rounded once). Chosen over
  compiling `ls_pow`'s algorithm here because that algorithm is IEEE double
  arithmetic throughout (double-double `log` and `exp`), and isa-lang 0.2
  encodes no SSE: compiled now, it would be a software-float emulation of
  every `+ - * /` it does, thousands of instructions per call, replaced
  wholesale when SSE arrives. The runtime routine is `ls_pow` itself, so the
  bits are identical by construction, and the tests bind exactly that.
  The operands and the result travel as bit patterns in integer registers
  under the C convention, because floats live in general-purpose registers
  in this release (next item).
- **Floats as bit patterns**: constants, `bitcast` to and from the integer of
  the same width, loads, stores (atomic ones included), `select`, block
  parameters, and edge arguments carry a float's bits in a general-purpose
  register, so a float can reach `pow` and come back. Float arithmetic,
  comparisons, int/float conversions, and float arguments and results stay
  refused (v0.5.0, isa-lang SSE).
- **Saturating shifts** (`Shift::Saturate`): a constant amount folds (in
  range: an immediate shift; at or past the width: zero, or a shift by
  `w - 1` for an arithmetic right shift); a run-time amount is tested for
  sign (signed types: the `ShiftOutOfRange` edge), then `cmov`s pick zero
  (left and logical right shifts) or clamp the count to `w - 1` (arithmetic
  right shifts).
- **Allocation throughput** (`regalloc`): a use index built once (each
  value's definition and use positions), liveness analysed once, affinities
  collected once; each spill round reports what it changed and the live
  sets, last uses, fixed-register facts, and affinities are updated in place
  (exact: spill code reads and writes no physical register, every spilled
  value leaves every live-in set, each temporary lives only before its
  user). Spill rewriting visits only the spilled values' uses, and only
  blocks that received code are rebuilt (in O(1) per instruction, not a
  binary search). Per-instruction physical effects are recorded by the
  analysis for the coloring instead of recomputed each round, segment
  constraint masks are memoised per (open, close) pair, and the final
  substitution rewrites operands in place. Numbers in the release notes.
- **Tests**: OPS v2 conformance on edge values (`pow` under three policies,
  saturating shifts, by run-time and by every edge-value constant) in the
  test interpreter under both conventions and natively; float `pow` against
  `ls_pow` bit for bit (special values, subnormals, NaN payloads, random
  magnitudes; run-time and constant exponents; `f64` and `f32`); the random
  program generator extended with `pow`, saturating shifts (plain and
  `check`ed), and float `pow` through constants, `select`, memory, and
  joins; a variables-API program generator (unsealed loop headers, joins,
  unassigned reads, `check`ed OPS v2 with error edges assigning variables,
  calls) against the interpreter in the test interpreter and natively;
  `insert_statepoints` programs under the moving collector (and natively
  with a non-moving one), and on `invoke`s through the minimal unwinder;
  hostile and huge inputs (`tests/hostile.rs`: any OPS v2 operation with any
  constant or run-time operands compiles, terminates, and agrees; all-ones
  constant exponents unroll to bounded code; a 300,000-instruction function;
  20,000 nested branches; a 2,000-parameter join; 10,000 `pow` loops in one
  function; the live budget exact to the entry and unaffected by spill
  rounds); a `check` whose error edge an optimizer proved dead points at a
  shared block ending in `unreachable` (opt-lang 0.3's shape): it selects to
  `ud2` and runs against the interpreter in the test interpreter and
  natively.
- **Dominance checked in the index walk**: the allocator's precondition
  (`Function::verify_ssa`'s rule) is checked while the use index is built,
  one walk instead of two more; a property test holds the allocator's errors
  equal to `verify_ssa`'s on hostile MIR.

Dependency wiring (decided here, recorded per the anti-deferral rule):
- **ir-lang `=2.0.0-alpha.3`: wired** (crates.io, pinned exactly while it is
  a pre-release; was `=2.0.0-alpha.1`), and its `interp` feature as the
  test oracle. `backend_lang::ir_lang` re-exports it, so the pin is part of
  this crate's API.
- **isa-lang 0.2: wired**, unchanged. **object-lang 0.2: dev-dependency**,
  unchanged.
- **irgen-lang 0.2.0: not a dev-dependency, and cannot be.** It pins
  `ir-lang = "=2.0.0-alpha.2"`; this crate pins `=2.0.0-alpha.3`, and cargo
  allows one version of a package per semver-compatible range (both are
  `2.0.0` pre-releases), so resolution fails ("failed to select a version
  for `ir-lang` ... all possible versions conflict"). The HIR → ir →
  machine code end-to-end test therefore lives with irgen-lang 0.3 on
  alpha.3 (ISSUES P37's plan); here, the variables API and
  `insert_statepoints` (what irgen uses to build its output) are exercised
  directly.

Moved out of v0.4.0, with reasons:
- **Live-range splitting** → v0.5.0, as P32 planned: the 1M/s target is met
  without it on `compile_100k`, and splitting changes code quality, not the
  allocator's asymptotics. The high-pressure benchmark (`compile_pressure`)
  remains below 1M IR instructions/s (release notes): its cost is the live
  sets themselves (dozens of values live through thousands of blocks), which
  any live-set liveness pays; splitting with interval-based liveness is the
  fix and belongs with it.
- **Compiling `ls_pow` instead of calling it** → with SSE (v0.5.0), if
  inlining ever pays: see the float `pow` item above.
- Everything 0.3 moved to v0.5.0 stays there (floats and SIMD in vector
  registers, unwind tables, PIC, `lock`-prefixed atomics, aggregates and
  sret, AArch64).

## v0.5.0 - Implementation
- [ ] AArch64: isa-lang AArch64 forms, the AArch64 description, AAPCS64 (`aapcs64`), the
      frame model (`stp`/`ldp`, frame record), selection, and i-cache coherence for the JIT.
- [ ] Floating point and SIMD: XMM rows and Win64 vector saves (isa-lang 0.5 SSE, ISSUES
      P11), float selection (OPS §4 exactly: `min`/`max` NaN and signed-zero rules, `round`
      ties away, `float_to_int` saturate/error), float arguments and results; floats move
      from general-purpose registers (0.4's bit-pattern carrying) to XMM, and the float
      `pow` runtime call to the XMM convention or inline `ls_pow`.
- [ ] Live-range splitting (moved from 0.4, ISSUES P32), with interval liveness, so the
      high-pressure benchmark (`compile_pressure`) also reaches 1M IR instructions/s.
- [ ] Unwinding: Win64 `.pdata`/`.xdata` and DWARF CFI from `FrameLayout`, an LSDA from the
      call-site records, a personality routine; `invoke` and landing pads complete.
- [ ] PIC: GOT-relative addressing of imported data and PLT calls (`R_X86_64_GOTPCREL`,
      `PLT32`), so modules link into shared objects; today data is reached PC-relative and
      calls are direct `rel32`.
- [ ] `lock`-prefixed atomics (`atomic_rmw` beyond `xchg`, `cmpxchg`).
- [ ] Aggregates by value, sret, more results than return registers, varargs, and i128 in
      the ABIs; the NOML target reader.

## v0.9.0 - Hardening
- [ ] Differential testing vs the VM; fuzzing; compile-speed benchmarks.

## v1.0.0 - Stable
- [ ] Frozen after jit-lang 2.0 and aot-lang 2.0 run on it (D18).