Skip to content

Preserve DWARF local indices through local reordering - #9199

Open
cpunion wants to merge 7 commits into
WebAssembly:mainfrom
cpunion:codex/dwarf-local-indices-20261003
Open

cpunion wants to merge 7 commits into
WebAssembly:mainfrom
cpunion:codex/dwarf-local-indices-20261003

Conversation

@cpunion

@cpunion cpunion commented Oct 3, 2026 •

Copy link
Copy Markdown
Contributor

Problem

ReorderLocals updates Wasm instructions but leaves DW_OP_WASM_location operands pointing at the old local indices. Execution remains correct, but the debugger can silently show another variable's value.

extern int touch(int);
volatile int observed;
__attribute__((noinline)) int run(void) {
  int a = touch(11);
  int b = touch(22);
  int c = touch(33);
  observed = a;
  observed = b;
  observed = c;
  observed = b;
  observed = b;
  return a + b + c; // Inspect a, b, c here after wasm-opt -O1 -g.
}
Check Before After
GDB / LLDB: a, b, c 22, 11, 33 11, 22, 33
Program result 66 66
Deleted local Can point at an unrelated surviving local Unavailable

The interactive comparison includes #9194 on both sides to separate this bug from the independent line-table problems. The DWARF change is based on main (5d31e56e); its regression tests do not depend on the other DWARF PRs.

CI dependency: temporarily includes Thomas Lively's approved FuzzTest update from #9394. It fixes the uintptr_t error in Abseil that also fails on main. This dependency can be dropped by rebasing once #9394 lands.

Fix

Compose input-to-IR local mappings across reordering and coalescing, then apply the writer's final binary-local mapping to DWARF locations. This also handles the coalescing performed internally by Asyncify. Coalescing's existing DWARF-invalidating status remains: repairing indices does not repair changed variable live ranges.

Rewrite inline expressions and location lists, including shared suffixes, nested expressions and expression-level DIE/type references. If an operand grows across a LEB boundary, relocate the affected list offsets, DIE references, CU lengths and public-name tables. Collection, expression/layout relocation and public-table updates are separate stages. With unchanged CU address sizes, the common same-width case skips the .debug_info layout pass; expressions without DIE references are not decoded again on subsequent iterations. Instruction boundaries use a small ordered vector instead of per-instruction hash allocations. No optimization is disabled.

The implementation is in wasm-debug.cpp. Input list offsets retain the original CU address width; emitted list offsets and DIE layout use the final width. Expressions being rewritten reject truncated operands.

Validation

  • All 19 focused DWARF tests pass. Coverage includes repeated passes, StackIR, removed locals, coalescing, LEB boundaries, legacy forms, short-form overflow, multiple CUs, shared list heads/suffixes, expression-level references, Memory64 lowering and truncated operands. The new suffix and expression-reference checks also reproduce failures in the initial PR implementation, before this follow-up fix. Independent LLVM verification is used when available; typed operations unsupported by that verifier still have exact operand and structural assertions.
  • All 102 Python tests and the wasm-opt pass suite pass; 455 C++ tests pass, with one existing Linux-only size test skipped on macOS. The Python run excludes V8-dependent checks because local d8 is not V8. The full build and these automated checks were rerun after rebasing onto current main on November 14.
  • TinyGo output passes LLVM DWARF verification; O1 + Asyncify runs and prints {"name":"dwarf","value":42}. GDB on Linux arm64 and LLDB on macOS arm64 read the correct values in the combined verification above.

TinyGo sample on the original main baseline (82439ad0), seven alternating warmed runs on macOS arm64; timings are medians, not evidence of a speedup:

Configuration Output bytes, before → after wasm-opt time, before → after
O1 + Asyncify, no debug 1,791,971 → 1,791,971 727.4 → 730.6 ms
O1, debug 1,912,371 → 1,912,371 353.0 → 362.6 ms
O1 + Asyncify, debug 2,185,951 → 2,185,947 924.5 → 884.0 ms

Stripped outputs are byte-identical in all three configurations. This fixes local-index relocation, not all remaining DWARF limitations or variable live-range transformations.

Developed with AI assistance; the validation above was executed locally.

@cpunion
cpunion requested a review from a team as a code owner October 3, 2026 04:07
@cpunion
cpunion requested review from tlively and removed request for a team October 3, 2026 04:07
@cpunion
cpunion force-pushed the codex/dwarf-local-indices-20261003 branch from bb1bd09 to abfeadc Compare November 11, 2026 22:47

@tlively tlively left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the PR. I'll remove myself as a reviewer because I won't be able to review this effectively due to lack of DWARF expertise. As an alternative approach, you could skip passes that reorder locals, e.g. with --skip-pass=coalesce-locals.

I've left some notes below about bugs Gemini found, though.

Comment thread src/wasm/dwarf-locals.h Resolved
Comment thread src/wasm/wasm-debug.cpp Resolved
Comment thread src/wasm/dwarf-locals.h Resolved
@cpunion
cpunion force-pushed the codex/dwarf-local-indices-20261003 branch from abfeadc to ca64016 Compare November 13, 2026 21:35
@cpunion

cpunion commented Nov 13, 2026

Copy link
Copy Markdown
Contributor Author

Thanks for the review. Fixed both bugs and moved the implementation into wasm-debug.cpp in ca64016. I kept remapping so ReorderLocals remains available with DWARF.

Rebased onto current main. All 102 Python tests, 455 C++ tests and the wasm-opt pass suite pass. The new Memory64 and truncated-operand regressions fail before the fixes and pass after them.

tlively and others added 7 commits November 14, 2026 08:40
GitHub Actions recently updated `ubuntu-latest` to Ubuntu 26.04, which ships GCC 15's `libstdc++` headers without transitive `<cstdint>` includes. The previously pinned FuzzTest submodule fetched `abseil-cpp` `20240116.0`, which failed to compile under GCC 15's `libstdc++` due to a missing `uintptr_t` declaration in `absl/container/internal/container_memory.h`.

Update `third_party/fuzztest` to the `2026-06-29` release tag (which updates `abseil-cpp` to `20260526.0`) along with two compatibility fixes:

1. Inherit `std::variant` constructors via `using variant::variant;` in the `std::variant` wrapper structs in `test/gtest/type-domains.h`, as FuzzTest's `VariantOfImpl::GetValue` now constructs custom variant types using `std::in_place_index<I>`.

2. Restore Binaryen's `CMAKE_CXX_STANDARD` (`20`) when configuring Abseil via `CMAKE_PROJECT_absl_INCLUDE` in `third_party/CMakeLists.txt`. FuzzTest's `CMakeLists.txt` unconditionally sets `CMAKE_CXX_STANDARD` to `17`, which otherwise builds `libabsl_status` with C++17 `absl::SourceLocation` while `binaryen-unittests` compiles with C++20 `std::source_location`, causing an ABI link mismatch. With `ABSL_PROPAGATE_CXX_STD` enabled, configuring Abseil with C++20 also propagates `-std=c++20` to FuzzTest.
Keep input and output address widths separate when relocating location lists and DIEs. Reject truncated operands in both custom and vendored decoding paths, and cover both issues with regressions. Move the rewriter implementation into wasm-debug.cpp.
@cpunion
cpunion force-pushed the codex/dwarf-local-indices-20261003 branch from cec031d to 3ef7fa3 Compare November 14, 2026 00:43
@cpunion

cpunion commented Nov 14, 2026 •

Copy link
Copy Markdown
Contributor Author

The failed fuzztest job hits the same Abseil uintptr_t error on main. Rebased on current main and included Thomas Lively's approved fix from #9394, preserving authorship. The DWARF changes are unchanged.

All 17 jobs in the new CI run now pass, including clang with fuzztest. Local tests and lint also pass. The temporary CI dependency is noted in the PR body.

This branch has not been deployed

No deployments
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