Skip to content

[WebAssembly] Fix SpillPointers: spill potential pointers live at call sites for Boehm GC correctness#9

Draft
Copilot wants to merge 2 commits intorelease/16.xfrom
copilot/check-wasm-outputs
Draft

[WebAssembly] Fix SpillPointers: spill potential pointers live at call sites for Boehm GC correctness#9
Copilot wants to merge 2 commits intorelease/16.xfrom
copilot/check-wasm-outputs

Conversation

Copy link
Copy Markdown

Copilot AI commented Mar 8, 2026

Three sunspider wasm test cases (regexp-dna, string-unpack-code, string-tagcloud) crash with wasm trap: undefined element: out of bounds table access inside string_buffer_realloc at a call_indirect. The root cause is WebAssemblySpillPointers failing to keep certain heap-allocated pointer values visible to Boehm GC during calls.

Bugs fixed in WebAssemblySpillPointers.cpp

Bug 1 — Wrong liveness condition (primary)

The spill gate was liveAt(CallIdx) && liveAt(CallIdx.getRegSlot()), which only spills registers that survive past the call. This misses registers that are consumed as call arguments (live range ends at getRegSlot()).

The concrete failure: QuickJS computes vtable + 20 as an interior pointer to a GC-managed vtable and passes it directly as a call_indirect argument. It is not needed after the call, so the old check never spilled it. GC fires during the call, cannot trace the vtable through any shadow-stack root, and collects it. On the next access vtable[12] (the function-table index) is garbage → trap.

Fix: drop the second condition; spill any potential pointer live at the call:

// Before
if (LI.liveAt(CallIdx) && LI.liveAt(CallIdx.getRegSlot()))

// After
if (LI.liveAt(CallIdx))

Bug 2 — Missing seeds for integer-typed pointer arguments (defensive)

Only ptr-typed LLVM IR arguments were treated as potential pointer seeds. Runtimes like QuickJS routinely pass GC-traceable pointer values as i32/i64 (e.g., JSValue is a tagged pointer stored as an integer). Such arguments were invisible to the dataflow analysis and never spilled.

Fix: unconditionally seed all i32/i64 ARGUMENT instructions as potential pointers, regardless of the LLVM IR type annotation.

Test update (spill-pointers.ll)

  • Added test_ptr_consumed_by_call: verifies that a pointer passed solely as a call argument (dead after the call) is still spilled — the case the old code missed.
  • Updated test_int_only comment to explain why it still emits no spills: the integer arguments die before the call site, so NeedSpill remains empty even with the broadened seeding.

💬 We'd love your input! Share your thoughts on Copilot coding agent in our 2 minute survey.

… correctness

Co-authored-by: PLUTOFX <110596339+PLUTOFX@users.noreply.github.com>
Copilot AI changed the title [WIP] Investigate failed execution of WebAssembly outputs [WebAssembly] Fix SpillPointers: spill potential pointers live at call sites for Boehm GC correctness Mar 8, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants