Skip to content

[wip][wasm] Preserve R2R variable debug information - #133086

Draft
lewing wants to merge 9 commits into
dotnet:mainfrom
lewing:lewing-r2r-wasm-debug-info
Draft

[wip][wasm] Preserve R2R variable debug information#133086
lewing wants to merge 9 commits into
dotnet:mainfrom
lewing:lewing-r2r-wasm-debug-info

Conversation

@lewing

@lewing lewing commented Sep 2, 2026

Copy link
Copy Markdown
Member

Summary

Preserve variable debug information produced by RyuJIT for ReadyToRun WebAssembly code.

  • Re-enable JIT scope information now that wasm virtual registers carry an encodable local index and value type.
  • Discover untracked variable scopes independently of IL block emission order so relooper reordering and duplication do not lose or duplicate ranges.
  • Preserve packed wasm local register values for integer, floating-point, vector, and exception-reference locals.
  • Decode those packed register values in R2RDump.
  • Add wasm R2R coverage for local ranges and f64 register locations.

This is a draft because it establishes the producer-side foundation for subsequent cDAC and NativeAOT wasm debugging work.

Stack

Depends on #132650. GitHub cannot target a fork-only branch from an upstream PR, so the draft currently targets main and includes the #132650 commits. That portion will disappear after #132650 merges.

Validation

  • The focused wasm R2R suite passes all 8 selected test cases.
  • Restoring the previous TARGET_WASM scope-info disable makes WasmWebcilModule fail because no local variable ranges are present.
  • Forced minopts compilation of System.Enum.TryParse with non-monotonic relooper block order emits 16 variable ranges for 10 variables.
  • R2RDump validates and decodes the resulting wasm/WebCIL image.

Note

This pull request was created with the assistance of GitHub Copilot.

lewing and others added 5 commits August 21, 2026 23:11
R2RDump could not read a wasm/webcil composite R2R image, and crossgen2
--map crashed on one, leaving no output image at all.

R2RDump: WebcilImageReader.TryGetReadyToRunHeader required CorFlags.ILLibrary
before consulting the CLI header's ManagedNativeHeader directory, and always
reported isComposite=false. A composite's COR header has no ILLibrary flag -
CopiedCorHeaderNode only ORs it in on the _module != null path - so the header
was never found and R2RDump threw "The file is not a ReadyToRun image".
Webcil has no export table, so unlike PE a composite cannot publish an
RTR_HEADER export; both single-assembly and composite webcil images locate the
header through ManagedNativeHeader, which is what WebcilDecoder::FindReadyToRunHeader
already does in the runtime. Locate the header the same way and derive
isComposite from the absence of ILLibrary, matching PE's polarity.

R2RDump: NativeCuckooFilter validated 16 byte alignment of the file offsets of
the AttributePresence section. The runtime validates the section's RVA and size
instead (NativeCuckooFilter in nativeformatreader.h). The distinction is latent
on PE, where a file offset is congruent to its RVA modulo 16 because section and
file alignment are both multiples of 16, but a webcil payload embedded in a wasm
wrapper does not start at a 16 byte aligned file offset, so every file offset is
shifted and the check failed on a correctly aligned image. Validate the RVA and
the filter length instead, which is exactly equivalent to the previous check on
PE and correct for webcil. The RVA is taken as a constructor parameter so the
invariant stays with the type that depends on it. The runtime additionally
requires the filter size to be a power of two; that is deliberately not ported,
since refusing to dump an otherwise readable filter is less useful than dumping
it.

crossgen2: WebCilObjectWriter never populated the output section layout, unlike
the PE, COFF, ELF and Mach-O writers. MapFileBuilder indexes that list by
section index, so --map threw ArgumentOutOfRangeException and, because
ReadyToRunObjectWriter deletes the output on failure, no image was emitted.
Record one entry per section so --map and image emission can coexist. Section
lengths come from the stream length rather than the padded on-disk size, as in
the other writers. ComputeChecksums is overridden to reject checksum
relocations, which nothing emits for wasm today: the recorded file positions are
relative to the webcil segment rather than to the output file, so the base
implementation would patch the checksum into the wrong place.

Adds two regression tests: WasmCompositeModule compiles a wasm composite and
asserts the reader recognizes it, and CuckooFilterAlignmentIsValidatedAgainstTheRva
covers a filter whose RVA is aligned but whose file offset is not.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
The equivalent support has landed on main with correct post-shrink offsets.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 827f138b-e780-418d-933b-5171054acad8
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 827f138b-e780-418d-933b-5171054acad8
Validate the filter RVA at the r2rdump call site while retaining size validation in the existing public constructor.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 827f138b-e780-418d-933b-5171054acad8
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 51b56d7e-45e5-464d-8c2b-78c744680ff8
@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
Successfully started running 5 pipeline(s).
11 pipeline(s) were filtered out due to trigger conditions.
There may be pipelines that require an authorized user to comment /azp run to run.

@lewing lewing changed the title [wasm] Preserve R2R variable debug information [wip][wasm] Preserve R2R variable debug information Sep 2, 2026
@lewing lewing added the arch-wasm WebAssembly architecture label Sep 2, 2026
@lewing
lewing requested a review from davidwrighton September 2, 2026 16:26
Comment thread src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/NativeHashtable.cs Outdated
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 51b56d7e-45e5-464d-8c2b-78c744680ff8
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 51b56d7e-45e5-464d-8c2b-78c744680ff8
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 51b56d7e-45e5-464d-8c2b-78c744680ff8

# Conflicts:
#	src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCases/R2RTestSuites.cs
@lewing
lewing requested a review from AndyAyersMS September 3, 2026 17:26

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

Does this handle describing locals that are on the linear stack? Last I looked those SP/FP based encodings were wonky for Wasm.

Note the exact Wasm locals that hold SP/FP can vary and may even vary between main method and its funclets, which I doubt we can encode.

An easy test for this is to look at any GC-typed local, these are always on the linear stack.

@davidwrighton

Copy link
Copy Markdown
Member

@lewing, what value does this debugging data provide? Given my understanding that debugging is likely to only be supported on mostly interpreted scenarios, why should we make this correct for R2R scenarios. (I expect that nativeaot will make good use of this data, so I think overall its goodness, but I don't see why its particularly useful for R2RDump.)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

arch-wasm WebAssembly architecture area-ReadyToRun

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants