kit

kit
git clone https://git.ryansepassi.com/git/kit.git
Log | Files | Refs | README

commit b40b8fb7be7f3af9feab1fcdb703b8715f854e37
parent fb361a2c5e14ae76e5020dc11420bca9d19d4a83
Author: Ryan Sepassi <rsepassi@gmail.com>
Date:   Tue, 16 Jun 2026 19:50:47 -0700

doc(ARM32): remaining-work checklist — design correctly, do not patch

Reframe the residual work as a design-focused checklist. The headline: the
recurring Phase-2 scratch/offset bugs (incl. the now-known i64/wide-store-to-
global miscompile) are all symptoms of an un-designed memory-access/addressing/
scratch layer, so §1 calls for a single correct-by-construction memory lowering
rather than another spot patch. Honestly downgrades the status from 'all
implemented' and records the i64-global-store correctness gap.

Diffstat:
Mdoc/plan/ARM32.md | 103++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------------
1 file changed, 86 insertions(+), 17 deletions(-)

diff --git a/doc/plan/ARM32.md b/doc/plan/ARM32.md @@ -1,9 +1,13 @@ # Plan: 32-bit ARM (`arm-none-eabi`, ARMv7-M / ARMv7E-M, Thumb-2) -## Status — 2026-06-16 — Phase 2 (full backend) LANDED +## Status — 2026-06-16 — Phase 2 (full backend) — core landed; checklist below -The arm32 backend is now a complete -O0/-O1 Cortex-M C toolchain. All of the -Phase-2 inventory below is implemented and qemu-validated: +The arm32 backend is a working -O0/-O1 Cortex-M C toolchain: it compiles, links +(both `kit ld` and `ld.lld`), and runs the full `test-smoke-arm32` set and most +of the cross corpus. The core inventory below is implemented and qemu-validated. +It is **not yet a complete, correctly-designed backend across the board** — the +"Remaining work" checklist records what is left, and (this is the point) those +items must be **designed and implemented correctly, not patched in piecemeal**: - **-O1 known-frame path** (`func_begin_known_frame`, callee-save PUSH/POP, slim leaf tier) + large frames/offsets staged through IP. The cross lane runs at @@ -12,7 +16,10 @@ Phase-2 inventory below is implemented and qemu-validated: `_MOVT_ABS` (scaled-index folded into `load_addr`); `load_const`/`load_label_addr`. - **i64 + soft-double (wide8)** — AAPCS even-register pair alignment, per-lane call/return passing, 8-byte `load_const`; mul/div/shift + all `double`/`float` - via the kit-built runtime's libgcc helpers (`__udivdi3`/`__adddf3`/…). + via the kit-built runtime's libgcc helpers (`__udivdi3`/`__adddf3`/…). Correct + for locals, args, returns, and i64 global *reads*; **i64/wide stores to a + global/static address (and an i64 struct field at a non-zero offset) are still + miscompiled** — Remaining work §1. - **Aggregates / varargs / atomics / inline asm** — `copy_bytes`/`set_bytes`, INDIRECT struct-by-value params/args/returns, AAPCS32 `va_*` (8-byte-aligned), LDREX/STREX + DMB ≤4-byte atomics (8-byte → spinlock libcall), a minimal @@ -35,19 +42,81 @@ Phase-2 inventory below is implemented and qemu-validated: `sym = .` after a glob) landed alongside. `make test-cross TARGET=freestanding-arm32 DEPTH=full` (toy X) runs 355/425 -green; the ~70 reds are left RED on purpose (not skipped), and break down as: -tail calls (~14 cases — `arm_emit_call` panics on `CG_CALL_TAIL`), wide-type -(i64) hint intrinsics (`@expect`/scalar-intrinsic preserve-type), the -single-thread TLS limitation below (threadlocal mutate/multi), `@memmove`, full -inline-asm, and a couple of arch-specific cases (e.g. the aa64 privileged hints -cross-compiled onto arm). - -Remaining follow-ons (intentional, documented): **tail calls**; the **AEABI alias -layer + coroutine asm** in the kit-self-built rt (needs broader kit-as coverage); -`@memmove` + the full inline-asm assembler; **wide (i64) hint intrinsics**; -hard-float (FPv4-SP); DSP/ARMv7E-M; ARMv6-M/Thumb-1; full `-mcpu`/`-mfpu` -parsing; `.ARM.attributes`; a real **multi-threaded TLS** path; `-O0` code -density. +green; the ~70 reds are left RED on purpose (not skipped) and map onto the +checklist below. + +## Remaining work — design these correctly; do NOT keep patching + +Phase 2 surfaced a clear lesson. Several separate correctness bugs — a branch +placeholder addend, two scratch-register clobbers (`str lr,[lr]`; +ip-resident-base clobber), and an incomplete wide-access split — all lived in the +**memory-access / addressing / scratch layer** and were each fixed cell-by-cell. +The matrix that layer must cover — + + {base: frame | reg | global} × {offset: in-range | out-of-range} + × {width: 1/2/4/8} × {scratch aliases neither the transfer reg nor a live base} + +— was never designed as a whole, so each new combination found a new hole (the +i64/wide-store-to-global bug in §1 is the latest). **The items below must be done +as coherent, correct-by-construction designs, not as more spot patches.** Each +names the design, not a workaround. + +### 1. Correctness — the wide / addressing memory layer (highest priority) +- [ ] **i64/wide stores to global/static addresses, and an i64 struct field at a + non-zero offset, are miscompiled** (i64 global *reads* work; *writes* do + not). Located: the 8-byte split's high lane + the GLOBAL-base + address/offset + scratch selection are not uniform across base kinds. +- [ ] **Design:** one memory-access lowering, correct by construction — + (a) a single address-resolver that maps any `NativeAddr` × byte offset × + width to a legal `(base, in-range-offset)`, or materializes the full + effective address once into a *reserved* scratch, choosing a scratch that + provably aliases neither the transfer register `rt` nor `base`; + (b) a uniform wide-N decomposition over the register pair whose high-lane + address is computed correctly for frame / reg / **global**; + (c) the scratch-reservation invariant stated and `assert`-enforced in one + place. This retires the ad-hoc `lr`/`ip` rescue paths added during Phase 2. + +### 2. Functional features — real implementations, not stubs +- [ ] **Tail / sibling calls** (`arm_emit_call` panics on `CG_CALL_TAIL`) — + AAPCS tail-call lowering (epilogue-before-jump, in-place arg shuffle, + `BX`/`B.W` to the callee) covering musttail / sret / variadic forms. +- [ ] **`@memmove`** — overlap-safe, direction-aware copy (compare dst vs src), + sharing the granule engine with `copy_bytes`. +- [ ] **Varargs edge cases** — mixed int/fp/i64, stack-arg + result-on-stack: + complete `va_arg` over the unified arg window, validated against the + GP-save-area layout (no per-width special cases). +- [ ] **Wide (i64) hint intrinsics** — `@expect`/`assume_aligned` on 8-byte + values currently move only the low lane; fold them through the wide path. +- [ ] **Full inline asm** — route `asm_block` through the real descriptor-driven + assembler instead of the minimal template runner. + +### 3. Toolchain / runtime completeness +- [ ] **kit-self-built rt with no exclusions** — grow the descriptor-driven + assembler to cover the M-profile system + hand-written-asm idioms, then + re-enable the AEABI alias layer + coroutine register-switch asm (`AEABI`/ + `CORO` in `mk/rt.mk` + the driver's `kRtSrcArm32`), which are clang-only now. +- [ ] **Multi-threaded TLS** — replace single-thread TLS-as-static with a real + variant-I tp-relative model (software thread pointer + reset-stub seed). + +### 4. Profiles / ABIs — new variants via the clean `variant` seam, not forks +- [ ] **Hard-float** (FPv4-SP, `arm-none-eabihf`): VFP codegen + the softfp/hard + float-abi axis (`double` stays soft on FPv4-SP). +- [ ] **DSP / ARMv7E-M** saturating + packed-SIMD behind the `dsp` feature. +- [ ] **ARMv6-M / Thumb-1** (no IT / MOVW-MOVT / hardware divide / 32-bit Thumb). +- [ ] Full **`-mcpu`/`-march`/`-mfpu`/`-mfloat-abi`** parsing + the complete + `__ARM_FEATURE_*` predefined-macro set. + +### 5. Polish (correctness-neutral, do last) +- [ ] `.ARM.attributes` (Tag_ABI build attributes) for external-toolchain interop. +- [ ] 16-bit-encoding density + shifted-register operands (deferred peephole); + `-O0` code density toward tcc. +- [ ] The kit-compiled Cortex-M reset stub (needs kit-`as` for MSR/MRS/BKPT). + +### Exit criterion +`make test-cross TARGET=freestanding-arm32 DEPTH=full` is green with no red +except cases genuinely inapplicable to the target (each carrying a `.arm32.skip` +sidecar with a reason), and `make rt-arm-eabi-thumb2` builds the **full** runtime +(AEABI + coro included) with kit's own assembler. ## Status — 2026-06-16 — Phase 1 (walking skeleton) LANDED