FABRIC.md: 4.4g stays open (reorder not viable), add urgent item 4.6 (-O0 kernel build)

Attempted 4.4g's console_fb_init() reorder twice this session: once bare,
once with the fb_scroll_rows() volatile fix applied. Both stalled boot
indefinitely (12,000+ lines logged, still running after 3 minutes vs. a
normal few-second boot) instead of completing. Root cause traced past the
scroll fix to Makefile.starkernel building the kernel at -O0 -- no
optimization flag has ever been configured there, verified against the
file's full git history (17 commits, only ever one unrelated host-tool -O2
line). Both the kernel's own vendored hosted Makefile and the standalone
StarForth repo's Makefile default to -O2 (up to -O3/-flto on faster
targets); Makefile.starkernel was written fresh for the bare-metal target
and never got that ladder.

Documented as new item 4.6: enabling optimization is not a safe drop-in
change on its own. Found one confirmed, isolated correctness hazard first --
TimeTrustState.ticks (timer.h:90) is written directly in ISR context on all
three architectures (heartbeat.c:163) and read directly by mainline
(heartbeat.c:200-202, including a busy-wait in kernel_main.c:880) without
being volatile, unlike every other ISR-shared global checked
(g_spurious_count, g_plic_claim_count, g_pending_counter/g_pending_valid/
g_adaptive_period_ns are all correctly volatile already). Reverted the
console_fb_init() reorder itself (uncommitted, so a plain git restore) --
4.4g stays open pending 4.6.

Both the reorder attempts' logs (stalled, never reached ok>) and this
session's routine three-arch artifacts (capsules/BLOCK_MAP.md, disk/
artemis.img, DOE CSV) are committed as audit trail per CLAUDE.md.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Robert Allan James
2026-08-11 09:42:35 -04:00
co-authored by Claude Sonnet 5
parent e211707e2d
commit b42308762b
8 changed files with 43558 additions and 21 deletions
+67
View File
@@ -5333,6 +5333,73 @@ document and committing that amendment as its own item.*
- [ ] **4.5 — Artemis last.** It works today; it is the thing that cannot be broken.
*Refs:* §10.
- [ ] **4.6 — URGENT, flagged by Captain Bob 2026-08-11: the kernel build has never used any
compiler optimization.** Found while investigating why moving `console_fb_init()` earlier
in boot (4.4g) stalled boot indefinitely (12,700+ lines logged, still running after 3
minutes vs. a normal few-second boot). Cross-cutting — affects the entire kernel, not just
Console work — recorded here because it was found during Phase 4 and blocks 4.4g, but it
is its own problem.
**Finding:** `Makefile.starkernel`'s `COMMON_CFLAGS` (and every `ARCH_CFLAGS` block —
amd64, aarch64, riscv64) specifies no `-O` flag at all, so the kernel and loader compile at
GCC/Clang's default `-O0`. This was verified by reading the file directly, not assumed.
**Not a regression — checked, not assumed.** Captain Bob's recollection was that `-O2` was
already in use; that recollection is correct for the *other* two Makefiles in this
ecosystem, just not this one. `git log --all -p -- Makefile.starkernel` (17 commits, full
history) shows the only optimization flag ever present in this file is one unrelated host
build-tool line (`cc ... -O2`, for `mkcapsule`-adjacent tooling, not the kernel itself),
unchanged since it was added. `Makefile.starkernel` itself has had no `-O` flag on the
kernel/loader `CFLAGS` since its very first commit, `a5ed8c3` ("Initial commit —
LithosAnanke kernel", 2026-08-01) — this was true from day one of this file, not something
that regressed later.
**Where the impression came from — both siblings do use it:**
- `/home/rajames/CLionProjects/StarForth/Makefile` (the standalone StarForth repo):
`TARGET_CFLAGS_standard` defaults to `-O2 -flto=auto -fuse-linker-plugin -DNDEBUG`;
`fast`/`fastest`/`turbo` targets go to `-O3` plus `-DUSE_ASM_OPT=1`.
- This repo's own vendored hosted `Makefile` (root-level, builds the plain hosted
`starforth` binary per CLAUDE.md) carries the *same* `TARGET_CFLAGS_standard`/`fast`/
`fastest`/`turbo` ladder — it was copied over intact when the VM was vendored into
LithosAnanke.
- `Makefile.starkernel` is a separate file, written fresh for the bare-metal kernel target
(`a5ed8c3`) rather than derived from either of the above, and the `-O` ladder simply
never got carried into it. `include/vm_asm_opt.h`/`vm_asm_opt_arm64.h`/
`vm_asm_opt_riscv64.h` document a hand-optimized-assembly VM path with suggested flags
including `-O3`/`-flto` and a `USE_ASM_OPT` macro — but that macro is never referenced
anywhere in `Makefile.starkernel` or any `Kconfig*` file, so it was never wired in either;
those headers are aspirational documentation for a path that doesn't exist in this build.
**Why this isn't a trivial one-line fix — a real, confirmed correctness hazard exists.**
At `-O0` every memory access is a genuine load/store with no caching across calls or
inlining, so interrupt-shared state "just works" by accident even without `volatile`. Under
optimization that accident stops protecting you. A sweep of the ISR-adjacent globals found
the codebase is otherwise disciplined about this — `g_sk_fault_word`, `g_spurious_count`
(amd64 `interrupts.c:59`), `g_plic_claim_count` (riscv64 `interrupts.c:47`),
`g_pending_counter`/`g_pending_valid`/`g_adaptive_period_ns` (`heartbeat.c:57-63`, the
documented ISR→mainline top/bottom-half handoff) are all correctly declared `volatile`
but one field was missed: `TimeTrustState.ticks` (`include/starkernel/timer.h:90`, a plain
`uint64_t`, not `volatile`) is incremented directly in interrupt context
(`heartbeat.c:163`, `heartbeat_tick()`, called from the timer ISR on **all three
architectures** — amd64 `interrupts.c:342`, aarch64 `interrupts.c:89`, riscv64
`interrupts.c:80`) and read directly by mainline code via `heartbeat_ticks()`
(`heartbeat.c:200-202`), including in a busy-wait loop at `kernel_main.c:880`
(`while (heartbeat_ticks() - wait_start < 3 ...)`). At `-O0` this works. Under optimization,
if the compiler inlines `heartbeat_ticks()` into that loop, it can legally prove (per the C
abstract machine, which does not model asynchronous interrupts) that nothing in the loop
body writes `g_heartbeat.ticks`, hoist the read out entirely, and turn that loop into either
an instant no-op or an infinite spin — on all three architectures at once, since the pattern
is shared. This is a real, specific, isolated bug (one field, not a sprawling unknown), not
a hypothetical one — but it is unaudited, and enabling `-O2` before fixing it would ship a
live regression.
**Scope, not yet started.** Two things needed before optimization can be turned on: (1) fix
`TimeTrustState.ticks` specifically (mark `volatile`, or move it into the existing
`g_pending_counter`-style handoff), and (2) do a fuller sweep for any other
ISR-written/mainline-read state that isn't already `volatile` — the one confirmed instance
came from investigating a single busy-wait loop found by accident (4.4g), not from an
exhaustive audit, so absence of further evidence here is not evidence of absence. Then
three-arch re-verification of the whole boot sequence at whatever `-O` level is chosen,
since a kernel that has only ever been built and accepted at `-O0` has no track record at
any other optimization level. Not started; report per CLAUDE.md's "identify, don't fix
unprompted" rule until Captain Bob scopes and authorizes the actual work.
*Refs:* discovered via 4.4g; independent of Console work.
---
## 25.6 Phase 5 — Verification and measurement