Resolves item #3 of docs/working/archive/session-logs/2026-07-24-punch-list.md ("riscv64 hosted build isn't reachable via plain make"), the last open item from that list. Decided against options (b) chasing GCC's riscv64 nanosleep-visibility failure at its root (undiagnosed, open-ended) and (c) leaving it manual — instead wired the already-verified clang recipe (commit4485c38/e287334) into the Makefile, mirroring the existing rpi4-cross pattern. Makefile: new riscv64-clang target. CFLAGS deliberately does not reuse $(BASE_CFLAGS) (hardcodes -std=c99); clang needs -std=c11 -pthread here instead. Registered in `make help` and .PHONY. docs/lithosananke/hosted-acceptance-test/README.md: riscv64 section now points at `make riscv64-clang` instead of the long manual invocation. Updated Background section and commit list to reflect that all three punch-list items touching this doc (#1 asm fix, #2 doc command, #3 make target) are now resolved. Verified: `make riscv64-clang` produces a binary with identical results to the manual command it replaces (965 passed / 0 failed, "ALL IMPLEMENTED TESTS PASSED!", "3 Goodbye!" for the piped acceptance script). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
179 lines
6.8 KiB
Markdown
179 lines
6.8 KiB
Markdown
# Hosted StarForth — 3-Architecture Acceptance Test
|
|
|
|
**Date**: 2026-07-24 (original), corrected 2026-08-02
|
|
**Branch**: `master`
|
|
**Commits**: `edced063` (original block-subsystem fix), `4485c38` (doc +
|
|
3 clang-surfaced bug fixes), `e287334` (asm register-reuse fix), this pass
|
|
(`riscv64-clang` Makefile target)
|
|
|
|
---
|
|
|
|
## What this verifies
|
|
|
|
The **hosted** StarForth VM (`make`, not `Makefile.starkernel`) is a static
|
|
binary. This test confirms it starts cleanly — reaches the `ok>` REPL prompt
|
|
with no errors — on all three architectures the project targets:
|
|
|
|
- **amd64** — native execution on the build host
|
|
- **aarch64** — cross-compiled, run under QEMU user-mode emulation
|
|
- **riscv64** — cross-compiled, run under QEMU user-mode emulation
|
|
|
|
This is distinct from the kernel (`Makefile.starkernel`) 3-arch acceptance
|
|
test documented in
|
|
[`../amd64-isr-fix/README.md`](../amd64-isr-fix/README.md), which boots a
|
|
full UEFI bare-metal image under system-mode QEMU. This test only exercises
|
|
the hosted Linux binary, using QEMU **user-mode** emulation to run a
|
|
foreign-architecture static binary directly on the x86_64 host — no kernel,
|
|
no UEFI firmware, no system emulation.
|
|
|
|
---
|
|
|
|
## Prerequisites
|
|
|
|
Cross toolchains, QEMU user-mode emulation, and clang (needed for the
|
|
riscv64 leg — see below):
|
|
|
|
```bash
|
|
sudo apt-get install -y \
|
|
gcc-aarch64-linux-gnu \
|
|
gcc-riscv64-linux-gnu \
|
|
clang-18 lld-18 \
|
|
qemu-user
|
|
```
|
|
|
|
`qemu-user` alone is enough — the binaries built below are statically
|
|
linked (`-static`), so the non-static `qemu-<arch>` emulators run them
|
|
directly with no sysroot. (The `qemu-user-static` package, if you see it
|
|
referenced elsewhere, provides statically-linked *emulators*; it's not
|
|
required just because the *guest* binary is static.)
|
|
|
|
---
|
|
|
|
## Invocation note
|
|
|
|
None of the three binaries accept a `-c "<script>"` flag — earlier
|
|
revisions of this doc showed one, but `src/cli.c`/`src/main.c` have never
|
|
had one in this repo's history. Pipe the script via stdin with `-s`
|
|
(script/silent mode) instead. Startup always runs the full internal POST
|
|
test suite before the REPL takes stdin, so expect substantial test-suite
|
|
output before your script's result appears at the very end.
|
|
|
|
---
|
|
|
|
## amd64 — native
|
|
|
|
```bash
|
|
make clean && make
|
|
echo "1 2 + . BYE" | ./build/amd64/standard/starforth -s
|
|
```
|
|
|
|
Expect `3 Goodbye!` at the end of the output, then a clean exit. No
|
|
emulation needed — this is the build host's native architecture.
|
|
|
|
---
|
|
|
|
## aarch64 — cross-compile + QEMU user-mode
|
|
|
|
```bash
|
|
make rpi4-cross
|
|
echo "1 2 + . BYE" | qemu-aarch64 ./build/raspi/fastest/starforth -s
|
|
```
|
|
|
|
`make rpi4-cross` cross-compiles with `aarch64-linux-gnu-gcc`
|
|
(`ARCH=raspi`, `TARGET=fastest`, `-static`); see `Makefile:547-554`. The
|
|
output binary is statically linked, so `qemu-aarch64` can execute it
|
|
directly with no sysroot.
|
|
|
|
---
|
|
|
|
## riscv64 — cross-compile + QEMU user-mode
|
|
|
|
```bash
|
|
make riscv64-clang
|
|
echo "1 2 + . BYE" | qemu-riscv64 ./build/riscv64/fastest/starforth -s
|
|
```
|
|
|
|
`make riscv64-clang` cross-compiles with `clang-18` (`ARCH=riscv64`,
|
|
`TARGET=fastest`, `-static`); see `Makefile:566-579`. **This leg must use
|
|
clang, not GCC**: `riscv64-linux-gnu-gcc` fails to build this tree
|
|
(`nanosleep` visibility failure under `-std=c99`, unrelated to any project
|
|
change — root cause not yet chased down, see
|
|
`docs/working/archive/session-logs/2026-07-24-punch-list.md` item #3). The
|
|
target's `CFLAGS` deliberately does not reuse the Makefile's
|
|
`$(BASE_CFLAGS)` (which hardcodes `-std=c99`) — clang needs `-std=c11
|
|
-pthread` here instead, plus `-fuse-ld=lld` in `LDFLAGS` (folding it into
|
|
`CC` makes clang reject it as an unused argument during `-c`/compile-only
|
|
invocations under `-Werror`).
|
|
|
|
---
|
|
|
|
## Pass criteria
|
|
|
|
For each architecture:
|
|
|
|
1. Build completes with no errors.
|
|
2. `blk_subsys_init` succeeds — no `"Failed to initialize block subsystem"`
|
|
error. (This was broken on every architecture, including amd64, prior to
|
|
`edced063` — see below.)
|
|
3. The internal POST suite runs (substantial log output — expected, not a
|
|
failure by itself) and the piped `1 2 + . BYE` script prints `3 Goodbye!`
|
|
at the end.
|
|
|
|
---
|
|
|
|
## Background: the block-subsystem bug this depends on
|
|
|
|
Before `edced063`, the hosted VM could not start on **any** architecture on
|
|
this branch. `src/main.c` allocated a hardcoded `blk_ram[1024 * 1024]`
|
|
buffer, stale relative to `BLK_RAM_BLOCKS=2080` (`include/block_subsystem.h`)
|
|
— a deliberate redesign that moved the RAM/ramdrive boundary to physical
|
|
block 2080 (user-visible LBN 2048), per `BLK_FORTH_SYS_RESERVED=32`. The
|
|
kernel-side buffer in `src/starkernel/vm/bootstrap/sk_vm_bootstrap.c` already
|
|
derived its size correctly from the shared constant; `main.c` was the one
|
|
remaining hardcoded holdout. The fix:
|
|
|
|
```c
|
|
#include "block_subsystem.h"
|
|
...
|
|
static uint8_t blk_ram[BLK_RAM_BLOCKS * BLK_FORTH_SIZE]; /* was: blk_ram[1024 * 1024] */
|
|
```
|
|
|
|
This is why the acceptance test above is meaningful on this branch
|
|
specifically: it did not pass before this fix, on any of the three
|
|
architectures.
|
|
|
|
---
|
|
|
|
## Background: the riscv64 clang build (2026-08-02 correction)
|
|
|
|
The riscv64 command above was broken in this doc from 2026-07-24 until this
|
|
correction — `docs/working/archive/session-logs/2026-07-24-punch-list.md`
|
|
(item #2) recorded that the working clang invocation had been found and
|
|
fixed in the old pre-split monorepo's `master` branch's `.claude/CLAUDE.md`,
|
|
but the correction was never copied into this doc. Verifying it end-to-end
|
|
for this correction surfaced that the clang build also needs three small
|
|
source fixes from that same punch list (item #1), previously ported to the
|
|
old monorepo's `master` but not to this line:
|
|
|
|
- `src/math_portable.c` — `-100LL << 16` is UB (shifting a negative value)
|
|
under `-Wshift-negative-value`; changed to `-(100LL << 16)`.
|
|
- `src/physics_pipelining_metrics.c` — removed dead `q48_mul_q48()`
|
|
(`-Wunused-function` under clang; GCC doesn't catch this by default).
|
|
- `src/word_source/editor_words.c` — removed dead `set_scr()` (same reason).
|
|
|
|
The punch list's highest-priority item — a genuine SIGSEGV-causing
|
|
register-reuse hazard in `include/vm_asm_opt_riscv64.h`'s `vm_pop_asm`/
|
|
`vm_rpop_asm` (clang's register allocator can pick the same register for a
|
|
memory operand referenced both before and after a write-back, corrupting
|
|
the result; GCC happens not to hit it) was *not* required just to get this
|
|
acceptance test's `-O3` build to compile and pass, but has since been
|
|
fixed too (commit `e287334`) — and turned out not to be merely a latent
|
|
risk: it was silently corrupting 10 `CASE.*` control-flow tests under this
|
|
exact build (`955`/`10 failed` → `965`/`0 failed`, `ALL IMPLEMENTED TESTS
|
|
PASSED!`, with nothing else changed).
|
|
|
|
Item #3 of the same punch list — no `make` target for this leg, only the
|
|
long manual invocation — is also resolved as of this pass: `make
|
|
riscv64-clang` wires in the same recipe verified above directly into the
|
|
Makefile.
|