From b3ffe97fb30fa2f72be4a678a9130edc90a85a27 Mon Sep 17 00:00:00 2001 From: Evan Reichard Date: Wed, 22 Jul 2026 18:16:01 -0400 Subject: [PATCH] tcg/wasm32: add normal-speed store-TCI bypass and windowed diff2 arming support Add an opt-in wasm_disable_store_tci() probe so browser tests can run compiled stores at normal speed without the differential harness. Also let E11 request no-chain only for TBs inside its PC window, avoiding the global no-chain watchdog artifact while preserving valid per-window comparisons. This keeps production store-TCI unchanged unless explicitly disabled. --- accel/tcg/cpu-exec.c | 15 +++++++++++++-- tcg/wasm32.c | 16 ++++++++++++++++ tcg/wasm32.h | 4 ++++ tcg/wasm32/tcg-target.c.inc | 6 ++++-- 4 files changed, 37 insertions(+), 4 deletions(-) diff --git a/accel/tcg/cpu-exec.c b/accel/tcg/cpu-exec.c index 7cce2e3eb3..5fdc8d5347 100644 --- a/accel/tcg/cpu-exec.c +++ b/accel/tcg/cpu-exec.c @@ -177,8 +177,9 @@ uint32_t curr_cflags(CPUState *cpu) } #if defined(EMSCRIPTEN) && !defined(CONFIG_TCG_INTERPRETER) - /* Differential mode runs one TB per dispatch so wasm_cur_tb_pc stays exact. */ - if (wasm_diff_on()) { + /* E10 needs one TB per dispatch so wasm_cur_tb_pc stays exact; E11 keeps + * chaining live to avoid watchdog-only slowdowns before the armed window. */ + if (wasm_diff_needs_nochain()) { cflags |= CF_NO_GOTO_TB | CF_NO_GOTO_PTR; } #endif @@ -427,6 +428,11 @@ const void *HELPER(lookup_tb_ptr)(CPUArchState *env) cpu_get_tb_cpu_state(env, &pc, &cs_base, &flags); cflags = curr_cflags(cpu); +#if defined(EMSCRIPTEN) && !defined(CONFIG_TCG_INTERPRETER) + if (wasm_diff_needs_nochain_at(pc)) { + cflags |= CF_NO_GOTO_TB | CF_NO_GOTO_PTR; + } +#endif if (check_for_breakpoints(cpu, pc, &cflags)) { cpu_loop_exit(cpu); } @@ -989,6 +995,11 @@ cpu_exec_loop(CPUState *cpu, SyncClocks *sc) cflags = cpu->cflags_next_tb; if (cflags == -1) { cflags = curr_cflags(cpu); +#if defined(EMSCRIPTEN) && !defined(CONFIG_TCG_INTERPRETER) + if (wasm_diff_needs_nochain_at(pc)) { + cflags |= CF_NO_GOTO_TB | CF_NO_GOTO_PTR; + } +#endif } else { cpu->cflags_next_tb = -1; } diff --git a/tcg/wasm32.c b/tcg/wasm32.c index 438b83d5d1..11cf7f0767 100644 --- a/tcg/wasm32.c +++ b/tcg/wasm32.c @@ -1314,6 +1314,12 @@ static __thread uint8_t *diff_tlb_c[NB_MMU_MODES]; static __thread uint8_t *diff_fulltlb_c[NB_MMU_MODES]; int wasm_diff_mode; +int wasm_store_tci_disabled; + +EMSCRIPTEN_KEEPALIVE void wasm_disable_store_tci(void) +{ + wasm_store_tci_disabled = 1; +} EMSCRIPTEN_KEEPALIVE void wasm_diff_enable(void) { @@ -1332,6 +1338,16 @@ int wasm_diff_on(void) return wasm_diff_enabled; } +int wasm_diff_needs_nochain(void) +{ + return wasm_diff_mode == 1; +} + +int wasm_diff_needs_nochain_at(uint64_t pc) +{ + return wasm_diff_mode == 2 && pc >= DIFF_PC_LO && pc < DIFF_PC_HI; +} + void wasm_diff_register_tb(const void *rw_ptr, const void *rx_ptr, uint64_t pc, bool has_store, bool safe_shadow) { diff --git a/tcg/wasm32.h b/tcg/wasm32.h index c0b7ef5ce7..298b56f8db 100644 --- a/tcg/wasm32.h +++ b/tcg/wasm32.h @@ -47,9 +47,13 @@ void wasm_dump_module(const uint8_t *p, int len, const char *name); * wasm_cur_tb_pc is the guest PC of the TB about to run, set by cpu_tb_exec. */ extern __thread uint64_t wasm_cur_tb_pc; extern int wasm_diff_mode; +extern int wasm_store_tci_disabled; +void wasm_disable_store_tci(void); void wasm_diff_enable(void); void wasm_diff_enable2(void); int wasm_diff_on(void); +int wasm_diff_needs_nochain(void); +int wasm_diff_needs_nochain_at(uint64_t pc); void wasm_diff_register_tb(const void *rw_ptr, const void *rx_ptr, uint64_t pc, bool has_store, bool safe_shadow); bool wasm_diff_lookup_store_tb(const void *tb_ptr, uint64_t *pc); diff --git a/tcg/wasm32/tcg-target.c.inc b/tcg/wasm32/tcg-target.c.inc index 70e73226af..ed27f23d8d 100644 --- a/tcg/wasm32/tcg-target.c.inc +++ b/tcg/wasm32/tcg-target.c.inc @@ -3385,8 +3385,10 @@ static void tcg_out_qemu_st(TCGContext *s, TCGOpcode opc, const TCGArg *args, bo * instances, routing stores through the helper, and skipping replayed helpers. Whole-TB gating * (the backend's only knob) cannot dissect it further; the fix needs generated-wasm inspection or * per-op TCI. Ceiling: store-TBs interpreted (perf cost, ~30s book open). */ - /* Mode 2 (E11) needs the real buggy compiled-store path live, so leave stores compiled there. */ - if (wasm_diff_mode != 2) { + /* Mode 2 (E11) needs the real buggy compiled-store path live, so leave stores compiled there. + * E12 probe: wasm_store_tci_disabled lets a normal-speed build run compiled stores to test + * whether the corruption workaround is still needed after later fixes. */ + if (wasm_diff_mode != 2 && !wasm_store_tci_disabled) { wasm_tci_only_tb = true; } }