commit dbed4e112a29d1493ee3b7a73a44ba7e33956cf3
parent 5ac31d5d4d6a900f1dd1aeb053a70f2930021437
Author: Ryan Sepassi <rsepassi@gmail.com>
Date: Wed, 17 Jun 2026 01:28:55 -0700
doc(ARM32): all toy reds closed (423/0/2); record §3 toolchain partial
The five residual edge reds + inline-asm case 20 are fixed (two were
misdiagnosed: 123/O0 = wide-value-in-scratch, 140/O1 = indexed-aggregate-move,
both fixed in shared codegen). §3: kit-as self-builds the coroutine rt layer
(save/restore verified under qemu); AEABI (GNU numeric local labels), the full
coro runtime fault, the kit-compiled reset stub, and .ARM.attributes remain.
Diffstat:
| M | doc/plan/ARM32.md | | | 78 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------------------ |
1 file changed, 60 insertions(+), 18 deletions(-)
diff --git a/doc/plan/ARM32.md b/doc/plan/ARM32.md
@@ -1,5 +1,19 @@
# Plan: 32-bit ARM (`arm-none-eabi`, ARMv7-M / ARMv7E-M, Thumb-2)
+## Status — 2026-06-17 — all toy reds closed; §3 toolchain partial
+
+`make test-cross TARGET=freestanding-arm32 DEPTH=full` (toy X) is **423/0/2** —
+every residual red is fixed (the 2 skips are the aarch64-only privileged-
+baremetal case). `test-smoke-arm32` is 4/4. The five edge reds + inline-asm
+case 20 were each fixed correct-by-construction (see "Residual reds" below; two
+were misdiagnosed in the plan — 123/O0 was a wide-value-in-scratch bug and
+140/O1 an indexed-aggregate-move bug, both fixed in the shared codegen so they
+benefit every 32-bit target). The §3 toolchain is now PARTIAL: `kit as` self-
+builds the coroutine rt layer (save/restore asm verified under qemu), with the
+AEABI alias layer (GNU numeric local labels), the full coro runtime fault, the
+kit-compiled reset stub, and `.ARM.attributes` remaining. See the §3 status and
+"Remaining work" below.
+
## Status — 2026-06-16 — Phase 2b — memory/frame/ABI correctness landed
The arm32 backend is a working -O0/-O1 Cortex-M C toolchain: it compiles, links
@@ -33,26 +47,54 @@ board; the toy corpus went from 355/425 to ~418/425.**
- **Atomics** — CAS/RMW use the pointer's own register (an LR-staged operand no
longer aliases the base).
-### Residual reds (toy DEPTH=full) — see "Remaining work"
-- **112_many_function_type_params/O0**, **123_spec_demo/O0** — register-starved
- -O0 edge: an indirect call with a large outgoing stack window needs the callee
- held across both stack-arg staging (IP) and the address resolver (LR); fix is
- to spill the callee to a frame slot and reload it just before BLX. (-O1 works.)
-- **124_atomic_word_ops/O1** — the -O1 atomic CAS is register-hungry (base + 4
- operands + STREX status r4); needs an explicit operand-aware scratch budget.
-- **140_fp_callee_save_bottom_frame/O1** — soft-float f64 callee-save across a
- call at -O1 (no d-regs on soft-float; an integer-pair-spill detail).
-- **154_frame_return_address/O1** — `@frame_address`/`@return_address` walk the
- r7 chain, but the -O1 prologue's single STMDB does not anchor r7 at a
- `{caller_r7, lr}` pair (callee-saves interleave). Needs the fp-at-pair prologue
- (split the callee-save push from the {r7,lr} push), which also fixes backtraces.
-- **20_cg_api_inline_asm_full** — still on the minimal template runner; route
- `asm_block` through the descriptor-driven `asm.c` (Remaining work §2).
+### Residual reds — ALL CLOSED (2026-06-17); toy DEPTH=full arm32 = 423/0/2
+The toy corpus is fully green (the 2 skips are the aarch64-only privileged-
+baremetal case). Each red was fixed correct-by-construction, not patched:
+- **112/123 O0 indirect call + large stack window** — the indirect callee held
+ in LR was clobbered by large-offset arg-stage address staging (LR is the
+ address scratch). Spill the callee to a frame home before staging when the
+ call has stack args; reload into IP just before BLX (native.c). *123's O0
+ crash was actually a SEPARATE wide-value bug:* a wide (i64) switch scrutinee
+ materialized into the lone NDT scratch ip(r12) put its high lane in r13/sp.
+ Fixed in the shared NDT (span-aware scratch acquire keeps a wide value off a
+ reserved high lane) — cg/native_direct_target.c.
+- **124 O1 atomic CAS/RMW** — fixed r4/r5 STREX temps could alias an
+ optimizer-allocated operand. Operand-aware borrow picks callee-saved temps
+ clear of base + all operands (native.c arm_atomic_borrow).
+- **140 O1 "f64 callee-save"** — was actually an i64/i32-array miscompile: a
+ "large" (8-byte) indexed load on a 32-bit target routes through emit_agg_move,
+ which did not legalize its address, so a folded array index was dropped (every
+ element aliased element 0). Fixed by legalizing both agg-move addresses
+ (opt/pass_native_emit.c) — matches the scalar load/store path.
+- **154 O1 @frame_address / @return_address** — fp-at-pair split prologue: push
+ callee-saves ABOVE a tight {r7,lr} pair so r7 anchors the frame record (also
+ fixes backtraces); saved_block_bytes / [r7,#-off] offsets unchanged (native.c).
+- **20 inline asm (case 20)** — bind immediate ("i") and memory ("m") operands at
+ -O0 and -O1 (native.c). (Empty templates run on the existing minimal runner;
+ routing real mnemonics through the descriptor-driven asm.c stays a follow-on.)
+
+### §3 toolchain — PARTIAL (2026-06-17)
+`kit as` is substantially hardened toward self-building the full rt:
+- **Assembler/CPP for hand-written asm**: cmp/mov modified-immediate; SP-relative
+ add/str/ldr incl. the 32-bit T3 form for a high transfer register; UDF; `;`
+ GNU statement separator; `#imm`-in-macro under `__ASSEMBLER__`; `__ELF__` +
+ empty `__USER_LABEL_PREFIX__` (so compiler-rt's assembly.h takes the ELF path,
+ not COFF); `__ARM_FP` left undefined on soft-float.
+- **Coro layer self-builds** (`RT_arm-eabi-thumb2_CORO = arm32`): coro/arm32.c +
+ coro/coro.c assemble/compile with kit; the save/restore asm is verified correct
+ under qemu (setjmp/longjmp round-trips at -O0/-O1). **Known issue:** the full
+ coro_init/resume/yield path still faults on bare-metal arm32 (a coro/coro.c-
+ level static-stack/TLS layout interaction surfaced now that the layer builds;
+ the switch/trampoline disassemble byte-correct).
### Still TODO (Remaining work, below)
-- §2 full inline asm via the assembler.
-- §3 grow `kit as` to self-build the **full** rt (AEABI alias + coro) with no
- exclusions, and the **kit-compiled** Cortex-M reset stub; `.ARM.attributes`.
+- §3 **AEABI alias layer self-build** — blocked on GNU numeric local labels
+ (`1:`/`1f`/`1b`) in `kit as`, which the soft-float compare helpers
+ (DEFINE_AEABI_DCMP/FCMP) use. Everything else in aeabi_thumb2.S now assembles
+ with kit (the `;`/`#imm`/`__ELF__`/SP-addressing fixes above).
+- §3 the full coro runtime fault (above), the **kit-compiled** Cortex-M reset
+ stub, and `.ARM.attributes` emission.
+- §2 route `asm_block` real mnemonics through the descriptor-driven `asm.c`.
(Minor: the disassembler renders shifted-MOV forms — LSR/ASR `mov.w rd,rs` — as a
plain `mov`, losing the shift in listings. Cosmetic; the encodings are correct.)