GV

GeezerVison

The World Wide Web, restored to 800 pixels of glory.

Home|About|Gaming cabinet|Archives|How it works|Starter sites

On this site

  • Sign in
  • ActionsCI/CD and automation
  • CodespacesCloud development environments
  • All Features
  • Documentation
  • Training
  • Certifications
  • Build with Copilot
  • Customize Copilot
  • Integrations
  • Trending repos
  • GitHub Sponsors
  • Developer articles
  • Enterprises

This view

  • Original page
  • New address

GeezerVison is showing github.com — Preserve DWARF local indices through local reordering by cpunion · Pull Request #9199 · WebAssembly/binaryen

Conversation

@cpunion

@cpunion cpunion commented Oct 3, 2026 •
edited
Loading

Uh oh!

There was an error while loading. Please reload this page.

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. This PR is based directly on main (5e56913e); its automated regression tests need no other PR.

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. 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.

Validation

  • All 17 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 and expression-level references. 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 100 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 12.
  • 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.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
@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 added 4 commits November 12, 2026 06:41
@cpunion @codex
Preserve DWARF variable locations when reordering locals
0fd7388
@cpunion @codex
Compose Asyncify local mappings and cover relocation boundaries
b04e993
@cpunion @codex
Handle relocated expression call widths and CU indexing
62b27f7
@cpunion @codex
Repair shared DWARF list suffixes and simplify relocation phases
abfeadc
@cpunion
cpunion force-pushed the codex/dwarf-local-indices-20261003 branch from bb1bd09 to abfeadc Compare November 11, 2026 22:47
tlively
tlively reviewed Nov 12, 2026
View reviewed changes

@tlively tlively left a comment

Copy link
Copy Markdown
Member

There was a problem hiding this comment.

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.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
Comment thread src/wasm/dwarf-locals.h
}
}

static bool isLocationList(llvm::dwarf::Attribute attr,

@tlively tlively Nov 12, 2026

Copy link
Copy Markdown
Member

There was a problem hiding this comment.

Choose a reason for hiding this comment

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

We shouldn't be defining static functions in a header. Can all the implementation here be moved to wasm-debug.cpp or a new .cpp file?

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
Comment thread src/wasm/wasm-debug.cpp

LocationUpdater locationUpdater(wasm, newLocations);

DwarfLocalRewriter(wasm, data, *info.context, newLocations).run();

@tlively tlively Nov 12, 2026

Copy link
Copy Markdown
Member

There was a problem hiding this comment.

Choose a reason for hiding this comment

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

Gemini claims it is a bug to run this before updateCompileUnits below. updateCompileUnits updates AddrSize after Memory64Lowering, and I guess DwarfLocalRewriter should run after that update.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
Comment thread src/wasm/dwarf-locals.h
Comment on lines +216 to +218
if (!op.extract(
bytes, unit->getVersion(), unit->getAddressByteSize(), start) ||
op.getEndOffset() > input.size()) {

@tlively tlively Nov 12, 2026

Copy link
Copy Markdown
Member

There was a problem hiding this comment.

Choose a reason for hiding this comment

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

Gemini claims this does not handle truncated DWARF expression operands. op.extract will return true in this case. I don't know how important that is to handle.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions

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

Reviewers

@tlively tlively tlively left review comments

At least 1 approving review is required to merge this pull request.

Uh oh!

There was an error while loading. Please reload this page.

Assignees

No one assigned

Labels

None yet

Projects

None yet

Milestone

No milestone

Development

Successfully merging this pull request may close these issues.

Uh oh!

There was an error while loading. Please reload this page.

2 participants

@cpunion @tlively
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
You are visitor 01402520 — GeezerVison does not store the pages you look up.
Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required.