From e45c1e2db1b3bae1ff0ee7b1e64be7a1eabe8219 Mon Sep 17 00:00:00 2001 From: Evan Reichard Date: Wed, 22 Jul 2026 08:00:39 -0400 Subject: [PATCH] wasm32: add node-only TB wasm dump tooling for static analysis (E9) --- tcg/tcg.c | 14 +++++++++++++- tcg/wasm32.c | 17 +++++++++++++++++ tcg/wasm32.h | 2 ++ tcg/wasm32/tcg-target.c.inc | 6 ++++++ 4 files changed, 38 insertions(+), 1 deletion(-) diff --git a/tcg/tcg.c b/tcg/tcg.c index 0f6c0f5082..fc8546de0f 100644 --- a/tcg/tcg.c +++ b/tcg/tcg.c @@ -6675,7 +6675,19 @@ int tcg_gen_code(TCGContext *s, TranslationBlock *tb, uint64_t pc_start) } *(uint32_t *)wasm_blob_ptr_base = s->code_ptr - wasm_blob_ptr_base - 4; - // record importing helper functions + { + static __thread int dumped_store, dumped_load; + uint32_t mod_len = *(uint32_t *)wasm_blob_ptr_base; + const uint8_t *mod = wasm_blob_ptr_base + 4; + if (wasm_tb_had_store && !dumped_store) { + dumped_store = 1; + wasm_dump_module(mod, mod_len, "store"); + } else if (wasm_tb_had_load && !wasm_tb_had_store && !dumped_load) { + dumped_load = 1; + wasm_dump_module(mod, mod_len, "load"); + } + } + size_base = (uint32_t*)s->code_ptr; s->code_ptr += 4; memcpy(s->code_ptr, target_helper_funcs, num_helper_funcs * 4); diff --git a/tcg/wasm32.c b/tcg/wasm32.c index 5d131cdb56..ab88ac611c 100644 --- a/tcg/wasm32.c +++ b/tcg/wasm32.c @@ -39,6 +39,23 @@ EMSCRIPTEN_KEEPALIVE int wasm_instantiated_tb_count(void) return qatomic_read(&instantiated_wasm_count); } +EM_JS(void, wasm_dump_module_js, (int ptr, int len, int name), { + try { + const fs = require('fs'); + const bytes = Uint8Array.from(HEAP8.subarray(ptr, ptr + len)); + const fname = '/tmp/tbdump-' + UTF8ToString(name) + '.wasm'; + fs.writeFileSync(fname, bytes); + out('WASM_DUMP wrote ' + fname + ' (' + len + ' bytes)'); + } catch (e) { + /* Browser has no require('fs'); dump is a node-only analysis aid. */ + } +}); + +void wasm_dump_module(const uint8_t *p, int len, const char *name) +{ + wasm_dump_module_js((int)p, len, (int)name); +} + /* Disassemble TCI bytecode. */ int print_insn_tci(bfd_vma addr, disassemble_info *info) { diff --git a/tcg/wasm32.h b/tcg/wasm32.h index a8b525abe3..a4384fc267 100644 --- a/tcg/wasm32.h +++ b/tcg/wasm32.h @@ -41,6 +41,8 @@ void flush_tb_instances(void); void init_wasm32(); +void wasm_dump_module(const uint8_t *p, int len, const char *name); + extern __thread bool wasm_tci_only_tb; #define INSTANTIATE_NUM 1500 diff --git a/tcg/wasm32/tcg-target.c.inc b/tcg/wasm32/tcg-target.c.inc index 265c1f5d96..414a34054c 100644 --- a/tcg/wasm32/tcg-target.c.inc +++ b/tcg/wasm32/tcg-target.c.inc @@ -3362,13 +3362,19 @@ static uint8_t tcg_tci_out_qemu_ldst(TCGContext *s, TCGOpcode opc, const TCGArg insn = deposit32(insn, 0, 8, opc); tcg_tci_out32(s, insn); } +/* Dump tags: instrumentation-only, set during codegen so tcg.c can dump one store-TB / load-TB + * module for static analysis. No effect on emitted code. */ +__thread bool wasm_tb_had_store; +__thread bool wasm_tb_had_load; static void tcg_out_qemu_ld(TCGContext *s, TCGOpcode opc, const TCGArg *args, bool is_64) { + wasm_tb_had_load = true; tcg_tci_out_qemu_ldst(s, opc, args); tcg_wasm_out_qemu_ld(s, args, is_64); } static void tcg_out_qemu_st(TCGContext *s, TCGOpcode opc, const TCGArg *args, bool is_64) { + wasm_tb_had_store = true; tcg_tci_out_qemu_ldst(s, opc, args); tcg_wasm_out_qemu_st(s, args, is_64); /* ponytail: Store-Containing TBs Run on TCI - Upstream qemu-wasm sets this for every guest store.