wasm-emu: document state materialization probes (E20-E22)

This commit is contained in:
2026-07-23 06:59:21 -04:00
parent 373d5c2352
commit 1406ab6d26
4 changed files with 64 additions and 1 deletions
+58
View File
@@ -616,3 +616,61 @@ vCPU execution state/translation, not an unmapped legitimate pointer.
Production default remains blanket store-TCI. The next useful probe is not more
IO-thread DRAM logging; it is targeted vCPU-state/TLB-entry instrumentation at
the failing translated block or around async exit handling.
## E20-E22: state materialization probes
E11 had already identified the first live divergence after arming at open:
`pc=0x4201d6ea` in `utf8NextCodepoint()`, where TCI had applied the prologue
`sp -= 16` to `CPUArchState` but compiled-live still had stale `env->gpr[2]`.
The disassembly confirms `0x4201d6d6..0x4201d6e8` is exactly the function
prologue and first branch:
```text
4201d6d6: addi sp,sp,-16
4201d6d8: sw s2,0(sp)
...
4201d6e4: lbu s1,0(s2)
4201d6e8: beqz s1,4201d7a8
4201d6ea: mv s0,a0
```
### E20: no-chain boundary probe
Added opt-in `?stateflush=open`, armed by the browser harness immediately before
opening `example.txt`. It reuses the existing `wasm_diff_needs_nochain_at(pc)`
app-flash window to force app TBs to return to C without running the expensive
compiled-vs-TCI differential shadow.
Result with `?nostoretci&stateflush=open`: original corruption still reproduced
(`Invalid read 0x3FCB80A6`, `MEPC 0x4201e210`). Returning to C between app TBs
is not enough; the wasm backend does not materialize `CPUArchState` merely by
leaving a compiled TB.
### E21/E22: blind helper spill attempts are invalid
Tested a direct backend fix candidate: spill TCG global-mem temps before C helper
calls so slow-path load/store helpers and exceptions see coherent architectural
state.
- spilling all global-mem temps before+after helpers caused an earlier invalid
read around `0x07585d82`;
- spilling before helpers only caused the same failure;
- narrowing to RISC-V integer GPR globals (`xN/...`) still caused immediate
invalid writes around `0x075f....`.
Conclusion: helper-boundary materialization is still the right suspect, but it
must respect TCG liveness/state. Blind backend spills at helper emission can
write stale globals back into `CPUArchState` and make corruption worse. The fix
belongs in the wasm backend's handling of TCG sync/liveness around calls (or in
targeted generated `sync_i32/i64` ops), not in an unconditional helper wrapper
spill.
### Revised next step
Trace the TCG liveness/sync ops for the divergent prologue TB and the later
slow-path helper TB: verify whether `la_global_sync()` marks `x2/sp` `TS_MEM`
across the relevant call/branch, and whether the wasm backend emits the
corresponding store. The observed bug is now specifically: **compiled globals and
`CPUArchState` are allowed to diverge, and the wasm backend exposes stale env to
helper/exception-visible code when store TBs stay compiled.**
+2
View File
@@ -169,6 +169,8 @@ try:
driver.execute_script('window.xteinkArmTcgDiff2 && window.xteinkArmTcgDiff2()')
if 'ramrace=open' in url:
driver.execute_script('window.xteinkEnableRamrace && window.xteinkEnableRamrace()')
if 'stateflush=open' in url:
driver.execute_script('window.xteinkEnableStateflush && window.xteinkEnableStateflush()')
open_start = time.monotonic()
press(confirm, 'opening example.txt')
open_seconds = time.monotonic() - open_start
+3
View File
@@ -498,6 +498,9 @@ async function boot(file, variant, setStatus) {
if (params.get('ramrace') === 'open') {
window.xteinkEnableRamrace = () => options._wasm_enable_ramrace_trace();
} else if (params.has('ramrace')) options._wasm_enable_ramrace_trace();
if (params.get('stateflush') === 'open') {
window.xteinkEnableStateflush = () => options._wasm_stateflush_enable();
} else if (params.has('stateflush')) options._wasm_stateflush_enable();
if (params.has('sthash')) options._wasm_set_store_tci_hash(Number(params.get('sthash')) || 1);
if (params.has('stcmask')) options._wasm_set_store_bisect(Number(params.get('stcw')) || 0, Number(params.get('stcmask')) >>> 0, (Number(params.get('stcval')) || 0) >>> 0);
options.FS.mkdirTree('/bios');