Skip to content

wasm: preserve Memory64 DWARF through Binaryen rewrites - #4

Merged
cpunion merged 3 commits into
xgo-dev:llgofrom
cpunion:codex/llgo-memory64-dwarf
Sep 24, 2026
Merged

cpunion merged 3 commits into
xgo-dev:llgofrom
cpunion:codex/llgo-memory64-dwarf

Conversation

@cpunion

@cpunion cpunion commented Sep 23, 2026 •

Copy link
Copy Markdown
Collaborator

Memory64 modules with DWARF lose valid line and range information when Binaryen rewrites them. For example, wasm-emscripten-finalize --dwarf turns a linked LLGo J64 module that passes llvm-dwarfdump --verify into one with 5,259 mismatching address size ... expected 0x08 found 0x04 diagnostics. This blocks the debug acceptance gate in xgo-dev/llgo#2632 even with the earlier scope-range repair in this branch.

Use the module's 4- or 8-byte address width when reading and emitting DWARF line addresses, range entries, location entries, and dead-address markers. Keep the DWARF32/64 initial-length format separate from target address width. The review follow-up also preserves DWARF64 line-table prefixes, encodes line discriminators as ULEB128, guards .debug_loc emission without compile units, and rejects out-of-range 64-bit addresses before mapping them through 32-bit Wasm binary offsets.

Validation:

  • Built all Binaryen tools with LLVM DWARF support; all eight DWARF Python unit tests pass, including Memory64 roundtrip/Asyncify, DWARF64 line-table and discriminator, high-bit .debug_loc, and out-of-range .debug_ranges cases.
  • llvm-dwarfdump --verify passes on the checked-in Memory64 fixture after roundtrip and Asyncify, and on the synthetic DWARF64 line table.
  • LLGo Go+C++ debug programs built with patched wasm-opt and wasm-emscripten-finalize pass final-artifact llvm-dwarfdump --verify and run under Node at J64 O0/O2. J32 O0 and W32 O0 passed before the review follow-up.
  • The broader local Binaryen unit suite reaches an unrelated test_cluster_fuzz failure because the installed Android D8 is selected as a JavaScript engine. This PR's DWARF tests pass.

Scope: this PR preserves debug information on linked Memory64 modules through finalize, roundtrip and Asyncify. The separate --memory64-lowering path still needs DIE-reference relocation when changing compilation-unit address width.

Base: llgo.

@fennoai fennoai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Review: memory64 DWARF support

Thanks for tackling 64-bit address support in the DWARF rewriter. The core approach is clean — getAddressSize, the width-aware allOnesAddress helper, threading addressSize/rangeEntrySize through the range/loc passes, and widening DWARFYAML::Loc to uint64_t are all reasonable. Tests cover both --roundtrip and --asyncify.

A few correctness issues remain, mostly around places where the 64-bit path was only partially applied. Inline comments below.

Body-level notes (no single reliable diff line):

  • DWARF64 line-table length advance vs. emitter output. In updateDebugLines (src/wasm/wasm-debug.cpp:809-811) the new comment/code advance newLocation by 12 for DWARF64 line tables, but the line-table emitter (third_party/llvm-project/DWARFEmitter.cpp:362) unconditionally writes a 4-byte length prefix (writeInteger((uint32_t)Size, ...)) with no DWARF64 escape path. If a DWARF64 line table is ever reached, newLocation over-advances by 8 bytes and corrupts subsequent .debug_line offsets (DW_AT_stmt_list). Since this PR targets memory64 (DWARF32 format), this is likely not hit by the added test, but the two sides should agree — either emit a real DWARF64 length or note the path as unsupported.

  • BinaryLocation remains uint32_t (src/wasm.h:2308). updateRanges/readDIEAddressRanges widen locals to uint64_t but truncate back via BinaryLocation(...) before calling getNewStart/getNewEnd/isTombstone. For wasm64 this is acceptable only because DWARF code addresses are wasm binary offsets (<4 GiB); it is not sound for arbitrary 64-bit address values and reads as fragile. A brief comment stating that assumption (or widening the key type) would help future readers.

Findings without inline locations

  • src/wasm/wasm-debug.cpp:340: Discriminator opcode length is wrong for memory64. makeItem(DW_LNE_set_discriminator, 5) hardcodes ExtLen = 5 (1 subopcode + 4 data bytes), but the emitter writes Op.Data using the CU's address width — DW_LNE_set_discriminator shares the DW_LNE_set_address case in DWARFEmitter.cpp:309-312 and uses AddrSize bytes. On a wasm64 module (AddrSize == 8) the actual content is 1 + 8 = 9 bytes while ExtLen still declares 5, producing a malformed extended opcode. Note the sibling DW_LNE_set_address above was correctly parameterized to 1 + addressSize in this same PR; this one was missed. Consider makeItem(DW_LNE_set_discriminator, 1 + addressSize) (and the stale comment // len = 1 (subopcode) + 4 (wasm32 address) — a discriminator isn't an address).

Comment thread third_party/llvm-project/DWARFEmitter.cpp Outdated
Comment thread src/wasm/wasm-debug.cpp Outdated
@cpunion

cpunion commented Sep 24, 2026

Copy link
Copy Markdown
Collaborator Author

Addressed the remaining review findings in b5f893d:

  • DWARF64 line tables now emit a real 12-byte initial-length field and retain DWARF64 format after recomputing lengths; the parser also uses the 12-byte prefix when finding the table end.
  • BinaryLocation remains a 32-bit offset into the Wasm binary. Range parsing keeps the encoded 64-bit values, and mapping rejects addresses above UINT32_MAX instead of truncating them.
  • DW_LNE_set_discriminator uses ULEB128 in DWARF, independent of address width. I fixed the vendored parser and emitter plus the calculated extended-opcode length, rather than using an 8-byte address operand.

The new DWARF64/discriminator, location-marker and out-of-range-range regressions pass; llvm-dwarfdump --verify passes on the resulting Memory64 output. The eight focused DWARF tests and LLGo J64 O0/O2 final-artifact checks pass locally. The PR description now records the separate --memory64-lowering DIE-reference limitation found during the additional check.

@cpunion
cpunion merged commit 9d2d8c5 into xgo-dev:llgo Sep 24, 2026
24 checks passed
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.

1 participant