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>
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
0fa9bd730e
commit
4485c3893b
@@ -1,8 +1,8 @@
|
|||||||
# Hosted StarForth — 3-Architecture Acceptance Test
|
# Hosted StarForth — 3-Architecture Acceptance Test
|
||||||
|
|
||||||
**Date**: 2026-07-24
|
**Date**: 2026-07-24 (original), corrected 2026-08-02
|
||||||
**Branch**: `lithosananke`
|
**Branch**: `master`
|
||||||
**Commit**: `edced063` (block-subsystem buffer-sizing fix that made this pass on all three architectures)
|
**Commits**: `edced063` (original block-subsystem fix), this correction pass
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -20,35 +20,53 @@ This is distinct from the kernel (`Makefile.starkernel`) 3-arch acceptance
|
|||||||
test documented in
|
test documented in
|
||||||
[`../amd64-isr-fix/README.md`](../amd64-isr-fix/README.md), which boots a
|
[`../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
|
full UEFI bare-metal image under system-mode QEMU. This test only exercises
|
||||||
the hosted Linux binary, using QEMU **user-mode** emulation
|
the hosted Linux binary, using QEMU **user-mode** emulation to run a
|
||||||
(`qemu-aarch64-static` / `qemu-riscv64-static`) to run a foreign-architecture
|
foreign-architecture static binary directly on the x86_64 host — no kernel,
|
||||||
static binary directly on the x86_64 host — no kernel, no UEFI firmware, no
|
no UEFI firmware, no system emulation.
|
||||||
system emulation.
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Prerequisites
|
## Prerequisites
|
||||||
|
|
||||||
Cross toolchains and QEMU user-mode emulators:
|
Cross toolchains, QEMU user-mode emulation, and clang (needed for the
|
||||||
|
riscv64 leg — see below):
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
sudo apt-get install -y \
|
sudo apt-get install -y \
|
||||||
gcc-aarch64-linux-gnu \
|
gcc-aarch64-linux-gnu \
|
||||||
gcc-riscv64-linux-gnu \
|
gcc-riscv64-linux-gnu \
|
||||||
qemu-user qemu-user-static
|
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
|
## amd64 — native
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
make clean && make
|
make clean && make
|
||||||
./build/amd64/standard/starforth -c "1 2 + . BYE"
|
echo "1 2 + . BYE" | ./build/amd64/standard/starforth -s
|
||||||
```
|
```
|
||||||
|
|
||||||
Expect `3` printed and a clean exit. No emulation needed — this is the build
|
Expect `3 Goodbye!` at the end of the output, then a clean exit. No
|
||||||
host's native architecture.
|
emulation needed — this is the build host's native architecture.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -56,12 +74,12 @@ host's native architecture.
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
make rpi4-cross
|
make rpi4-cross
|
||||||
qemu-aarch64-static ./build/raspi/fastest/starforth -c "1 2 + . BYE"
|
echo "1 2 + . BYE" | qemu-aarch64 ./build/raspi/fastest/starforth -s
|
||||||
```
|
```
|
||||||
|
|
||||||
`make rpi4-cross` cross-compiles with `aarch64-linux-gnu-gcc`
|
`make rpi4-cross` cross-compiles with `aarch64-linux-gnu-gcc`
|
||||||
(`ARCH=raspi`, `TARGET=fastest`, `-static`); see `Makefile:547-554`. The
|
(`ARCH=raspi`, `TARGET=fastest`, `-static`); see `Makefile:547-554`. The
|
||||||
output binary is statically linked, so `qemu-aarch64-static` can execute it
|
output binary is statically linked, so `qemu-aarch64` can execute it
|
||||||
directly with no sysroot.
|
directly with no sysroot.
|
||||||
|
|
||||||
---
|
---
|
||||||
@@ -69,17 +87,25 @@ directly with no sysroot.
|
|||||||
## riscv64 — cross-compile + QEMU user-mode
|
## riscv64 — cross-compile + QEMU user-mode
|
||||||
|
|
||||||
There is no dedicated `make` target for this yet (unlike `rpi4-cross` for
|
There is no dedicated `make` target for this yet (unlike `rpi4-cross` for
|
||||||
aarch64) — invoke the cross-compile directly:
|
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
|
```bash
|
||||||
make ARCH=riscv64 CC=riscv64-linux-gnu-gcc TARGET=fastest \
|
make ARCH=riscv64 \
|
||||||
CFLAGS="-std=c99 -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" \
|
CC="clang-18 --target=riscv64-linux-gnu --sysroot=/usr/riscv64-linux-gnu" \
|
||||||
LDFLAGS="-flto -s -static" \
|
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
|
all
|
||||||
|
|
||||||
qemu-riscv64-static ./build/riscv64/fastest/starforth -c "1 2 + . BYE"
|
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
|
## Pass criteria
|
||||||
@@ -90,8 +116,9 @@ For each architecture:
|
|||||||
2. `blk_subsys_init` succeeds — no `"Failed to initialize block subsystem"`
|
2. `blk_subsys_init` succeeds — no `"Failed to initialize block subsystem"`
|
||||||
error. (This was broken on every architecture, including amd64, prior to
|
error. (This was broken on every architecture, including amd64, prior to
|
||||||
`edced063` — see below.)
|
`edced063` — see below.)
|
||||||
3. `1 2 + . BYE` prints `3` and exits cleanly, or an interactive run reaches
|
3. The internal POST suite runs (substantial log output — expected, not a
|
||||||
the `ok>` prompt.
|
failure by itself) and the piped `1 2 + . BYE` script prints `3 Goodbye!`
|
||||||
|
at the end.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -112,6 +139,34 @@ remaining hardcoded holdout. The fix:
|
|||||||
static uint8_t blk_ram[BLK_RAM_BLOCKS * BLK_FORTH_SIZE]; /* was: blk_ram[1024 * 1024] */
|
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 `lithosananke`
|
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
|
specifically: it did not pass before this fix, on any of the three
|
||||||
architectures.
|
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.
|
||||||
|
|||||||
+1
-1
@@ -94,7 +94,7 @@ static int64_t exp_q48(int64_t x) {
|
|||||||
/* Handle extremes */
|
/* Handle extremes */
|
||||||
if (x == 0) return Q48;
|
if (x == 0) return Q48;
|
||||||
if (x > (20LL << 16)) return Q48 << 8; /* Approximate e^20 */
|
if (x > (20LL << 16)) return Q48 << 8; /* Approximate e^20 */
|
||||||
if (x < (-100LL << 16)) return 0; /* Underflow to zero */
|
if (x < -(100LL << 16)) return 0; /* Underflow to zero */
|
||||||
|
|
||||||
/* Taylor series: exp(x) = Σ x^n / n! (in fixed-point) */
|
/* Taylor series: exp(x) = Σ x^n / n! (in fixed-point) */
|
||||||
int64_t result = Q48; /* Start with 1 */
|
int64_t result = Q48; /* Start with 1 */
|
||||||
|
|||||||
@@ -116,28 +116,6 @@ static inline int64_t q48_div_u64(int64_t a_q48, uint64_t b) {
|
|||||||
return a_q48 / (int64_t)b;
|
return a_q48 / (int64_t)b;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Multiply two Q48.16 values, returning a Q48.16 result.
|
|
||||||
*
|
|
||||||
* Computes @p a × @p b and right-shifts by 16 to cancel the extra scaling
|
|
||||||
* factor introduced by multiplying two Q48.16 numbers:
|
|
||||||
* @code
|
|
||||||
* (a × b) >> 16 → Q48.16
|
|
||||||
* @endcode
|
|
||||||
* This is the standard fixed-point multiply for the same format on both
|
|
||||||
* operands. Overflow is possible if both operands are large (the intermediate
|
|
||||||
* product can reach 2^(48+48+16) = 2^112 in the worst case, which overflows
|
|
||||||
* @c int64_t). In practice the operands are probabilities (0.0–1.0) or
|
|
||||||
* latency fractions that keep the product well within int64 range.
|
|
||||||
*
|
|
||||||
* @param a First Q48.16 operand.
|
|
||||||
* @param b Second Q48.16 operand.
|
|
||||||
* @return @p a × @p b in Q48.16.
|
|
||||||
*/
|
|
||||||
static inline int64_t q48_mul_q48(int64_t a, int64_t b) {
|
|
||||||
return (a * b) >> 16;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ============================================================================
|
/* ============================================================================
|
||||||
* Transition Metrics Implementation
|
* Transition Metrics Implementation
|
||||||
* ============================================================================
|
* ============================================================================
|
||||||
|
|||||||
@@ -59,14 +59,6 @@ static inline cell_t current_scr(VM *vm) {
|
|||||||
return vm_load_cell(vm, vm->scr_addr);
|
return vm_load_cell(vm, vm->scr_addr);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @brief Set current screen number in VM
|
|
||||||
* @param vm Pointer to VM instance
|
|
||||||
* @param blk Screen number to set
|
|
||||||
*/
|
|
||||||
static inline void set_scr(VM *vm, cell_t blk) {
|
|
||||||
vm_store_cell(vm, vm->scr_addr, blk);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @brief Get pointer to specific line in a screen
|
/** @brief Get pointer to specific line in a screen
|
||||||
* @param vm Pointer to VM instance
|
* @param vm Pointer to VM instance
|
||||||
* @param scr Screen number (1-based)
|
* @param scr Screen number (1-based)
|
||||||
|
|||||||
Reference in New Issue
Block a user