Files
LithosAnanake/docs/lithosananke/hosted-acceptance-test/README.md
T
Robert Allan JamesandClaude Sonnet 5 4485c3893b Fix riscv64 hosted acceptance-test doc; port 3 clang-surfaced bugs from punch list
Addresses items #1 (partial) and #2 of
docs/working/archive/session-logs/2026-07-24-punch-list.md.

docs/lithosananke/hosted-acceptance-test/README.md:
- riscv64 leg used riscv64-linux-gnu-gcc, which fails to build this tree
  (nanosleep visibility under -std=c99). Replaced with the working
  clang-18 --target=riscv64-linux-gnu --sysroot=/usr/riscv64-linux-gnu
  invocation, verified end-to-end.
- All three arch sections referenced a -c "<script>" flag that has never
  existed in cli.c/main.c. Corrected to the working
  `echo "..." | starforth -s` pattern, verified on all three architectures.
- Updated Prerequisites: qemu-user alone is sufficient (guest binaries are
  static; qemu-user-static provides static *emulators*, not required here).

Source fixes (ported from the old pre-split monorepo's master, commit
4db9946a, where they were made but never carried over to this line):
- src/math_portable.c: `-100LL << 16` is UB (shifting a negative value)
  under clang's -Wshift-negative-value; changed to `-(100LL << 16)`.
- src/physics_pipelining_metrics.c: removed dead q48_mul_q48()
  (-Wunused-function under clang; GCC doesn't flag this by default).
- src/word_source/editor_words.c: removed dead set_scr() (same reason).

These three were required just to get the documented clang build to
compile at all. The punch list's higher-severity item — a genuine
SIGSEGV-causing register-reuse hazard in vm_pop_asm/vm_rpop_asm
(include/vm_asm_opt_riscv64.h) — is intentionally NOT included here; it's
a separate, more careful change and isn't required for this build to
succeed (latent only under clang; this repo's kernel build uses GCC).

Verified: all three hosted builds compile and run correctly (amd64
native, aarch64 via qemu-aarch64, riscv64 via qemu-riscv64), each
printing "3 Goodbye!" for the piped `1 2 + . BYE` script. All three
Makefile.starkernel builds (amd64/aarch64/riscv64) still compile cleanly
with these shared vendored-source changes.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-02 07:25:40 -04:00

173 lines
6.5 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), this correction pass
---
## 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
There is no dedicated `make` target for this yet (unlike `rpi4-cross` for
aarch64) — invoke the cross-compile directly. **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).
```bash
make ARCH=riscv64 \
CC="clang-18 --target=riscv64-linux-gnu --sysroot=/usr/riscv64-linux-gnu" \
TARGET=fastest \
CFLAGS="-std=c11 -pthread -Wall -Werror -Iinclude -Isrc/word_source -Isrc/test_runner/include -DSTRICT_PTR=1 -march=rv64gc -mabi=lp64d -mcmodel=medany -DARCH_RISCV64=1 -O3 -DUSE_ASM_OPT=1 -DUSE_DIRECT_THREADING=1 -DNDEBUG -flto -static" \
LDFLAGS="-flto -s -static -fuse-ld=lld" \
all
echo "1 2 + . BYE" | qemu-riscv64 ./build/riscv64/fastest/starforth -s
```
Note `-fuse-ld=lld` lives in `LDFLAGS`, not folded into `CC` — clang
rejects 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) — is a separate, not-yet-ported fix
and is **not** required just to get this acceptance test's `-O3` build to
compile and pass; it's a latent runtime risk specifically under clang,
tracked separately, not resolved by this correction.