diff --git a/docs/wasm-inflate-panic.md b/docs/wasm-inflate-panic.md index c2fc838..027c365 100644 --- a/docs/wasm-inflate-panic.md +++ b/docs/wasm-inflate-panic.md @@ -149,6 +149,45 @@ race** in the compiled restartable-block store path, not a codegen typo — and is exactly why upstream forces every store-TB to TCI. Fixing it is architectural (rewind/instance-safe compiled stores), not a one-op correction. +### E4-E7 — deterministic minimal repro, and what the bug is NOT +With a per-op gate in `tcg_out_qemu_st` (compile only store-TBs matching a +criterion, TCI the rest) plus targeted instrumentation, each run rebuilt + +browser-tested against the exact 1.4.1 firmware: + +| experiment | change | result | +|-----------|--------|--------| +| E4 lifecycle | never `removeFunction` retired TB instances (pin all) | **crash** | +| E5 helper-only | force every store through the softmmu helper (no direct store codegen) | **crash** | +| E6 width: byte only | compile TBs whose store is 8-bit | **pass** | +| E6 width: 16 only | 16-bit | **pass** | +| E6 width: 32 only | 32-bit | **pass** | +| E6 width: byte+16 | | **pass** | +| E6 width: 16+32 | | **pass** | +| E6 width: **byte+32** | | **crash** | +| E7 bytewise-32 | emit 32-bit store as four 8-bit stores, compile all | **crash** | +| precheck | skip store helper on rewind when DONE_FLAG already set | **crash** | +| nochain | `-d nochain` (disable TB chaining), compile all stores | **crash** | +| determinism | rerun the crashing all-compiled build 3x | **crash x3, same gen 16, same MEPC** | + +**Established:** the crash is *deterministic* (same point every run) but depends +on the exact compiled-TB set. The minimal reproducer is a byte-store TB **and** +a 32-bit-store TB compiled together; it is *counter-monotonic* — additionally +compiling 16-bit-store TBs hides it. + +**Ruled out:** single miscompiled TB (E1-E3), a specific memory region +(inflate/ROM/IRAM/flash halves), TB chaining (nochain), instance/function +lifecycle & `removeFunction` reuse (E4 pin), the store *data* codegen itself +(E5 helper-only and E7 bytewise both crash), and store double-execution on +rewind (precheck to skip the replayed helper still crashes). + +**Localized to:** the restartable-block rewind/register bookkeeping that wraps a +store op (shared in structure with loads, which survive because re-running a +load is harmless). The counter-monotonic width behavior points at an +interaction whose trigger shifts with which TBs are promoted, not at any single +store's emitted bytes. Next concrete step: dump the generated wasm for a +crashing store-TB and `wasm2wat` it to read the BLOCK_PTR/guard sequence +directly against a passing one. + ### Next options 1. **Ship E1 as a guarded workaround** — reader works now; measure real navigation/page-turn perf before judging it unacceptable. diff --git a/tests/browser_raw_sd.py b/tests/browser_raw_sd.py index ed4665d..b15adac 100644 --- a/tests/browser_raw_sd.py +++ b/tests/browser_raw_sd.py @@ -37,6 +37,7 @@ def page_state(): generation: Number(document.querySelector('#frame-generation')?.value || -1), runtimeErrors: window.__xteinkRuntimeErrors || [], wasmHeap: window.__xteinkModule?.HEAPU8?.byteLength || 0, + instantiatedTbs: window.__xteinkModule?._wasm_instantiated_tb_count?.() || 0, }; ''') except WebDriverException as error: @@ -46,6 +47,7 @@ def page_state(): 'generation': -1, 'runtimeErrors': [str(error)], 'wasmHeap': 0, + 'instantiatedTbs': 0, } diff --git a/third_party/qemu b/third_party/qemu index 725845d..3c064ae 160000 --- a/third_party/qemu +++ b/third_party/qemu @@ -1 +1 @@ -Subproject commit 725845d0d8cd9254ce6a9e81721281e9c43f2aa0 +Subproject commit 3c064ae6c9f9ca5e707ad3c51a8df1023429c23e