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:
co-authored by
Claude Sonnet 5
parent
e211707e2d
commit
b42308762b
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user