Skip to content

Compact Wasm ReadyToRun GC and unwind metadata - #135821

Open
davidwrighton wants to merge 25 commits into
dotnet:mainfrom
davidwrighton:split/wasm-gc-metadata
Open

davidwrighton wants to merge 25 commits into
dotnet:mainfrom
davidwrighton:split/wasm-gc-metadata

Conversation

@davidwrighton

@davidwrighton davidwrighton commented Oct 14, 2026 •

Copy link
Copy Markdown
Member

Description

Compact GC metadata on platforms using the shared GC-info codec, and reduce Wasm ReadyToRun GC/unwind overhead. This started as an extraction from #135553; the slot-format improvements now extend beyond Wasm.

  • On non-Wasm platforms using the shared codec, group register, tracked-stack, and untracked-stack descriptors by descending flags: pinned interior, pinned object, interior, ordinary object. Encode flags before locations, shrinking from two bits to one and then zero as earlier groups become impossible. Reset descriptor state between tracked and untracked sections.
  • Encode the first register in each flag group absolutely, then gap-minus-one deltas with bounded variable-length integers. Keep x64 APX R16–R31 representable in the encoding, without enabling APX allocation or context support.
  • Order stack slots by flags, then SP before frame-relative, then signed offset. Replace the two-bit stack base with one bit: SP-relative or frame-relative; caller-SP-relative slots are not represented in v6. Use signed absolute offsets at flag/base changes and unsigned deltas within each group. Non-Wasm offset normalization and encoding bases are unchanged.
  • Apply the compact stack-slot encoding to the interpreter, omit its always-zero register-presence bit, and retain its separate header encoding and tracked-root support. Legacy x86 GCInfo keeps its separate, unchanged encoding.
  • Wasm slot tables omit both the register-presence bit and per-slot base. Roots are implicitly frame-relative, in four-byte pointer units. XOR the pinned flag before sorting/encoding so common pinned object roots occupy the implicit-zero flag group.
  • Pack Wasm unwind frame size / 16 and virtual-IP span / 2 into one ULEB128 with a 4/4/4/4 low-bit interleave, retaining 24 bits per field.
  • Omit Wasm prolog-size and stack-base-register fields entirely: the normalized prolog size is implicitly one when needed, and the frame register is implicitly normalized zero. Set the implied frame base consistently even for frameless methods. Wasm slim headers imply zero safe points and omit the count; fat headers retain the explicit count.
  • Wasm ReadyToRun continues to report only untracked roots: retain its zero tracked-slot count and specialize the runtime decoder to skip tracked-root/lifetime parsing. Interpreter and non-Wasm tracked-root support remains.
  • Tune Wasm integer bases for whole-image Brotli size: code length 5, untracked-slot count 7, safe-point count 3, reverse-P/Invoke frame offset 4. Wasm stack-offset and delta bases are both 3.

The safepoint-state and chunked-liveness wire formats are unchanged: no changes to chunk sizes, chunk pointers, bitmap/RLE masks, final-state bits, or per-slot transition lists. The transition-count, chunk-pointer, and deduplication alternatives discussed during investigation are not implemented by this PR.

Update the native encoder/decoder, JIT/runtime unwind handling, ReadyToRun reader, cDAC, golden fixtures, and format documentation together. The runtime uses one GCInfo format. cDAC and R2RDump retain older non-Wasm decoding; all legacy Wasm decoding paths and fixtures are removed.

The original extraction came from commits d65c910ebcb and 8fd135cd964. This PR does not include two-phase compilation, direct calls, managed frame-identity changes, runtime-function byte planes, or eager reconstruction allocation. The separate byte-plane opportunity remains #135796.

cDAC GCInfo decoding and SOS interface

  • Managed GCInfo decoder: decode the v6 flags-first slot tables, bounded register encodings, one-bit SP/frame bases, and grouped stack-offset deltas. Retain the non-Wasm v5 decoding path for older runtimes. Interpreter decoding uses the new compact slot tables while preserving its separate headers and tracked-root support; the separate legacy x86 encoding remains unchanged.
  • Wasm GCInfo: mirror the native v6 layout, including implicit frame/prolog header fields, zero safe points in slim headers, omitted register-presence and slot-base bits, four-byte stack-offset units, the pinned-flag XOR, and tuned integer bases. Legacy Wasm decoding is not retained.
  • Wasm stack walking and metadata sizing: update both the cDAC unwinder and ExecutionManager's unwind-data sizing for the single ULEB128 containing the interleaved frame size and virtual-IP span. Share a bounded ULEB128 reader that rejects malformed values. Update the GCInfo, ExecutionManager, and StackWalk contract documentation and corresponding decoder/unwinder fixtures.

Complete the previously declared ISOSDacInterface18 implementation in cDAC: expose it through COM and implement GetGCInfoHeader, GetGCInfoInterruptibleRanges, GetGCInfoSafePoints, and GetGCInfoSlotLifetimes using the existing contracts. Select the interpreter decoder for interpreted methods and the platform decoder otherwise. Preserve the two-call sized-buffer protocol, initialize valid outputs on failure, and return explicit HRESULTs for invalid inputs and contract errors. This allows SOS's existing Interface18 path to use managed decoding when the updated cDAC is selected.

The bridge normalizes internal decoder stack-base values to the existing, version-independent API meaning: 0=SP, 1=SOSGCInfoHeader.StackBaseRegister, ignored for register slots. This matches the existing SOS formatter across supported platforms and GCInfo versions; no diagnostics-side change is needed. Unsupported internal stack-base values return an error rather than being exposed or silently interpreted as SP. Document these stable values in both the native IDL and managed interface declaration.

Standalone format compatibility

This draft uses ReadyToRun 34.0 / GCInfo version 6, with synchronized CoreCLR, compiler, and NativeAOT format constants. The CoreCLR minimum ReadyToRun version is 34 on every architecture. Older R2R code is disabled, with IL fallback where available; runtime decoders always use GCInfo v6 rather than selecting an encoding from the image version.

cDAC continues to decode non-Wasm GCInfo v5 for older runtimes, and R2RDump retains historical non-Wasm formats and their version mapping. R2R 33.0/33.1 maps to GCInfo v5; R2R 34.0 and later maps to v6. Wasm has only the current GC/unwind encoding; the managed reader rejects Wasm images older than R2R 34 before caching or decoding their headers.

The latest merge incorporates upstream main 1f07b2634a8, which uses R2R 33.1 and GCInfo v5. Its contextual GCRefMap changes (33.0) and Wasm profiling helper (33.1) are upstream changes, not additions made by this PR. Moving this PR's compact GC/unwind format to 34.0 avoids assigning different layouts to upstream's version 33.

Regular/composite reader regressions cover patched versions 31, 32, 33, and 34: older Wasm images are rejected, repeated accesses cannot bypass validation, and native inspection retains older-header support. That native tooling support does not relax the runtime's new minimum.

Historical measurements and limitations

The following previously recorded, isolated retail ReadyToRun CoreLib measurements used the same IL/PGO inputs within each architecture, optimized retail code generation, and default function order. Wasm comparisons used --parallelism:1: parallel compilation varied a small writable-data region, so all compression controls and the selected result were rebuilt serially.

These measurements were captured at b89730723e6, before the subsequent upstream merges and runtime compatibility cleanup. Image sizes and compression results have not been remeasured for the current R2R 34.0 merge.

Historical Wasm: upstream baseline versus measured variant

The baseline is upstream main 6f6de27e4095e296b4aea472fbd2fb30455d01be. Compression covers the entire same uncompressed images, not an independently compressed GC region.

Measurement Upstream baseline Measured Brotli-tuned variant Savings
CoreLib image 21,763,615 21,705,759 57,856
Unique GC payload 99,384 46,500 52,884
GC/unwind table 109,005 51,158 57,847
Brotli, quality 11 / window 22 / generic 3,869,228 3,851,964 17,264 (0.446%)
gzip, level 9 5,601,073 5,589,809 11,264
Zstd, level 3 5,374,767 5,365,920 8,847
Zstd, level 19 4,152,104 4,146,753 5,351

All sizes are bytes. gzip used zlib 1.3.1 with mtime=0 and no filename; Zstd used 1.5.7 without a dictionary or checksum. GC/unwind savings are included in image savings, not additive. Native-code node size was 11,729,435 bytes in both measured images.

The Brotli winner was selected from 30 full-image candidates: it saved another 530 Brotli bytes versus the untuned format. The raw-size-optimal alternative made the image 10,160 bytes smaller but Brotli 4,583 bytes larger than the selected result.

The production cross-JIT at that measurement point reproduced the selected image byte-for-byte, SHA-256 f7c1d41a1a70315bcc386ac7156b63aa61c0f53b75afc67226107a047850fd13. Recompression reproduced 3,851,964 Brotli bytes, and decoded GC metadata matched for 52,559 methods.

Historical x64: original native slot encoding versus measured variant

This control isolates the original native slot encoder using the same retail cross-JIT objects and IL/PGO inputs; it is not a separate freshly rebuilt upstream-main native image.

Measurement Original native slot encoding Measured variant Savings
CoreLib image 17,615,872 17,608,704 7,168
Unique GC payload 531,836 524,561 7,275
GC/unwind table 873,624 866,484 7,140

Native-code node size was 5,820,297 bytes in both measured x64 images. The measured Arm64 image was 18,969,600 bytes, with 408,767 bytes of GC payload and a 777,512-byte GC/unwind table. Wasm-specific header omissions and tuning left those x64/Arm64 GC blobs and image sizes unchanged; decoded-metadata comparisons covered 55,245 x64 and 55,271 Arm64 methods.

No new native-runtime image comparison or throughput/startup benefit is claimed.

Validation

Post-merge validation (the full runtime/library builds preceded the cDAC-only Interface18 follow-up; cDAC was rebuilt afterward):

  • Linux x64 Checked runtime / Release libraries build: passed, zero warnings/errors.
  • Full browser/Wasm Release runtime and libraries build: passed, zero warnings/errors.
  • Rebuilt GcInfoDecoderTests: 1,898 passed per target, on linux/x64 Checked and browser/wasm Release.
  • Rebuilt format-boundary tests: 6 passed per target, covering regular/composite header rejection and the preserved R2R 33 GCRefMap token encoding.
  • Linux x64 Checked cDAC NativeAOT build/publish: passed after implementing Interface18 and correcting its stack-base normalization.
  • Rebuilt cDAC GCInfoTests, StackWalkTests, WasmUnwinderTests, ExecutionManagerTests, FunctionTableAccessTests, and IXCLRDataProcessTests: 628 passed, including R2R 33 -> GCInfo v5 and R2R 34 -> v6.
  • The GCInfo class contains 28 new interface regression cases, covering all four methods, interpreter/platform selection, optional header fields, empty/undersized/oversized buffers, errors, unsupported stack bases, and native COM vtable calls. Existing real v5/v6 decoder fixtures also check the normalized API output. Four interface-discovery cases were verified to fail without the implementation; 16 stack-base cases and both unsupported-base cases were verified to fail without the normalization correction.
  • All selected tests had zero failures and zero skips. Runner summaries and available result XML were checked to confirm counts and selection.
  • Inspected the rebuilt Linux CoreLib: R2R 34.0, with GCInfo v6 on all four queried CheckIo methods.
  • git diff --check upstream/main: passed.
Exact build/test commands and results
# Passed: Linux x64 Checked runtime and Release libraries
./build.sh clr+libs -rc Checked -lc Release

# Passed: browser/Wasm Release runtime and libraries
./build.sh clr+libs -os browser -c Release

# Passed: 1,898 Linux x64 GCInfo decoder cases
./dotnet.sh build src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/ILCompiler.ReadyToRun.Tests.csproj '-t:Build;Test' -c Checked -p:RuntimeConfiguration=Checked -p:LibrariesConfiguration=Release -p:TestCaptureOutput=false '-p:TestRunnerAdditionalArguments=-notrait category=failing -class ILCompiler.ReadyToRun.Tests.GcInfoDecoderTests'

# Passed: 6 Linux x64 format-boundary cases
./dotnet.sh build src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/ILCompiler.ReadyToRun.Tests.csproj '-t:Build;Test' -c Checked -p:RuntimeConfiguration=Checked -p:LibrariesConfiguration=Release -p:TestCaptureOutput=false '-p:TestRunnerAdditionalArguments=-notrait category=failing -method ILCompiler.ReadyToRun.Tests.TestCases.R2RTestSuites.WasmGcEncodingRejectsOlderReadyToRunHeaders -method ILCompiler.ReadyToRun.Tests.TestCases.R2RTestSuites.GCRefMapTokensFollowFormatVersion'

# Passed: 1,898 browser/Wasm GCInfo decoder cases
./dotnet.sh build src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/ILCompiler.ReadyToRun.Tests.csproj '-t:Build;Test' -c Release -p:RuntimeConfiguration=Release -p:LibrariesConfiguration=Release -p:TargetOS=browser -p:TargetArchitecture=wasm -p:TestCaptureOutput=false '-p:TestRunnerAdditionalArguments=-notrait category=failing -class ILCompiler.ReadyToRun.Tests.GcInfoDecoderTests'

# Passed: 6 browser/Wasm format-boundary cases
./dotnet.sh build src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/ILCompiler.ReadyToRun.Tests.csproj '-t:Build;Test' -c Release -p:RuntimeConfiguration=Release -p:LibrariesConfiguration=Release -p:TargetOS=browser -p:TargetArchitecture=wasm -p:TestCaptureOutput=false '-p:TestRunnerAdditionalArguments=-notrait category=failing -method ILCompiler.ReadyToRun.Tests.TestCases.R2RTestSuites.WasmGcEncodingRejectsOlderReadyToRunHeaders -method ILCompiler.ReadyToRun.Tests.TestCases.R2RTestSuites.GCRefMapTokensFollowFormatVersion'

# Passed: cDAC NativeAOT build/publish after Interface18 and stack-base normalization
./build.sh tools.cdac -c Checked -rc Checked -lc Release

# Passed: 628 cDAC cases, including all 55 GCInfo cases and COM interface regressions
./dotnet.sh build src/native/managed/cdac/tests/UnitTests/Microsoft.Diagnostics.DataContractReader.Tests.csproj '-t:Build;Test' -c Checked -p:RuntimeConfiguration=Checked -p:LibrariesConfiguration=Release -p:TestCaptureOutput=false '-p:TestRunnerAdditionalArguments=-class Microsoft.Diagnostics.DataContractReader.Tests.GCInfoTests -class Microsoft.Diagnostics.DataContractReader.Tests.StackWalkTests -class Microsoft.Diagnostics.DataContractReader.Tests.WasmUnwinderTests -class Microsoft.Diagnostics.DataContractReader.Tests.ExecutionManager.ExecutionManagerTests -class Microsoft.Diagnostics.DataContractReader.Tests.FunctionTableAccessTests -class Microsoft.Diagnostics.DataContractReader.Tests.IXCLRDataProcessTests'

# Passed: R2R 34.0 header and GCInfo v6 on four CheckIo methods
./dotnet.sh artifacts/bin/coreclr/linux.x64.Checked/R2RDump/R2RDump.dll --in artifacts/bin/coreclr/linux.x64.Checked/System.Private.CoreLib.dll --header --gc --query CheckIo

# Passed: PR diff whitespace check
git diff --check upstream/main

Validation remains scoped: full native/Wasm runtime suites, live browser/SOS execution, and end-to-end GC-stress execution have not been run for this final state. This remains a draft.

Note

This PR description was generated with GitHub Copilot.

davidwrighton and others added 3 commits October 14, 2026 21:32
Specialize register-free codecs, use implicit frame-relative pointer-scaled slots, enable slim headers, encode flags-first offset runs, and normalize unwind frames to 16-byte units.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Retain and consume the zero tracked-slot and safepoint counts, while specializing away tracked descriptors and lifetime parsing. Keep interpreter and native-target tracked-root support.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Use ReadyToRun 33.0 to distinguish the compact codec from older images and the separate proposed 32.0 frame-identity format. Reject older Wasm images in single-file and composite loading and in the managed reader before decoding. Keep native minimum-version handling unchanged. Add cross-target header compatibility regressions and document the encoding.

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

azure-pipelines Bot commented Oct 14, 2026 •

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

@dotnet-policy-service

Copy link
Copy Markdown
Contributor

Tagging subscribers to this area: @dotnet/crossgen-contrib
See info in area-owners.md if you want to be subscribed.

davidwrighton and others added 4 commits October 20, 2026 21:39
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Generalize shrinking slot flags and bounded register encoding, pack Wasm unwind data, and omit redundant Wasm header fields. Select the whole-image Brotli-winning integer bases while retaining version-aware readers and focused decoder coverage.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Resolve the StackWalk documentation conflict by retaining packed GC/unwind metadata alongside upstream's handle-based unwind lookup.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Require ReadyToRun 33 and GCInfo v6 on every runtime target. Remove legacy Wasm header and slot decoding while preserving older native formats in cDAC, R2R inspection tools, and the SOS decoder. Add v5 native and interpreter fixtures and update the format documentation.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
davidwrighton and others added 3 commits October 22, 2026 21:50
Invert the Wasm pinned bit for slot sorting and encoding so pinned object roots sort last and omit repeated flags. Update native, ReadyToRun, and cDAC readers, regression coverage, and format documentation while retaining non-Wasm and interpreter encoding.

Fresh same-input retail CoreLib measurements: image 20,891,864 -> 20,890,520 bytes; GC/unwind table 25,802 -> 24,448 bytes; whole-image Brotli q11/window22 3,806,455 -> 3,804,636 bytes. Decoded GC metadata matches for 52,559 methods. These use current IL rather than the historical measurement inputs.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Select InterpreterGcInfoDecoder for reverse-P/Invoke detection, generic-context prolog checks, and interruptible-range enumeration on interpreter frames. Keep native frame decoding unchanged.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 798665fd-8e9d-46c8-b56c-843764cdb3d1
Set the Wasm GC encoder's frame register even when optimization removes the linear-memory frame, preserving the compact header invariant.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 798665fd-8e9d-46c8-b56c-843764cdb3d1
Preserve both the Wasm GC header-version tests and upstream's type-token fixup tests when resolving R2RTestSuites.cs. Rebuilt browser cross-components and passed all six cases of both theories.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 798665fd-8e9d-46c8-b56c-843764cdb3d1
davidwrighton added a commit that referenced this pull request Nov 3, 2026
…36553)

<!-- -->

## Summary

Use `InterpreterGcInfoDecoder` rather than the target-native
`GcInfoDecoder` when inspecting an interpreted frame in:

- `SfiNextWorker`, for reverse-P/Invoke detection during exception
propagation.
- `SafeToReportGenericParamContext`, for the generic-context prolog
length.
- `FindFirstInterruptiblePoint`, for the GC-reporting location when
unwinding between funclets.

Interpreter GC metadata has different encoding parameters and
normalization from native metadata. Select the decoder using the frame's
code manager, guarded by `FEATURE_INTERPRETER`, and preserve the
existing decoder for native frames.

Found while investigating #135821. This PR is
independently based on `main` and contains no Wasm metadata-format or
code-generation changes. The decoder mismatch predates that work; an
observable release-mode iOS failure has not been demonstrated.

## Validation

- `./build.sh clr.runtime -rc Checked` on this standalone branch:
passed, zero warnings and errors.
- Session-local caught-exception, generic-GC, and uncaught
reverse-P/Invoke probes on this standalone branch: passed with normal
execution and with `DOTNET_Interpreter='*' DOTNET_ReadyToRun=0`. The
uncaught probe terminated as expected and retained
`MessageFromMyException`.
- On the Wasm metadata branch, the reverse-P/Invoke probe reproduced the
missing-message/assertion failure before this fix and preserved the
exception message afterward; the checked browser runtime build also
passed. This is supplementary evidence, not a standalone-branch browser
test.
- `git diff upstream/main...HEAD --check`: passed.
- No new checked-in regression test; existing
`WasmBuildAppTest.ExceptionFromMain` caught the EH regression.
- iOS execution and the full test suites were not run.

> [!NOTE]
> This pull request description and code changes were generated with
GitHub Copilot.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 798665fd-8e9d-46c8-b56c-843764cdb3d1
davidwrighton and others added 3 commits November 3, 2026 22:41
Preserve upstream ReadyToRun 33.0 contextual GCRefMaps and 33.1 Wasm profiling support. Allocate ReadyToRun 34.0 to GCInfo v6, synchronize runtime/compiler/reader version boundaries, and retain the upstream cDAC test additions alongside compact Wasm fixtures.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Expose the existing GCInfo inspection interface through the cDAC COM object. Forward header, interruptible-range, safe-point, and slot-lifetime queries to the appropriate platform or interpreter decoder, with sized-buffer and HRESULT handling.

Add regression coverage for interface discovery, unmanaged vtable calls, field mappings, buffer sizing, and failure outputs. Validate the NativeAOT cDAC build and 626 focused tests.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Keep ISOSDacInterface18 stack-base semantics independent of decoder enums and GCInfo versions. Document the existing API values and cover v5/v6 decoding, native COM output, and unsupported bases.

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

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 Changes recommended

The v5 interpreter decoder can silently miss GC roots, Wasm reverse-P/Invoke offsets are incorrectly scaled, and the GCInfo contract still describes obsolete encoding.

3 open issues
What changed in this PR

This PR introduces GCInfo v6 and ReadyToRun 34.0 to reduce shared GC metadata size and Wasm GC/unwind overhead, coordinating the compiler, runtime, diagnostic readers, and format documentation.

Changes:

  • Adds compact flags-first slot encoding and packed Wasm unwind data, with synchronized format versions.
  • Updates native and managed decoders and implements cDAC’s existing ISOSDacInterface18 GC-info methods.
  • Adds decoding, compatibility, unwinding, and COM-interface regression coverage.
File Description
src/​native/​managed/​cdac/​tests/​UnitTests/​WasmUnwinderTests.cs Tests packed unwind fields and malformed values.
src/​native/​managed/​cdac/​tests/​UnitTests/​StackWalkTests.cs Updates Wasm header and unwind fixtures.
src/​native/​managed/​cdac/​tests/​UnitTests/​GCInfoTests.cs Adds decoder and SOS interface regression tests.
src/​native/​managed/​cdac/​tests/​UnitTests/​ExecutionManager/​ExecutionManagerTests.cs Tests v6 mapping and packed unwind sizing.
src/​native/​managed/​cdac/​Microsoft.Diagnostics.DataContractReader.Legacy/​SOSDacImpl.cs Implements four Interface18 GC-info methods.
src/​native/​managed/​cdac/​Microsoft.Diagnostics.DataContractReader.Legacy/​ISOSDacInterface.cs Documents stable stack-base meanings.
src/​native/​managed/​cdac/​Microsoft.Diagnostics.DataContractReader.Contracts/​Contracts/​StackWalk/​Context/​Wasm/​WasmUnwinder.cs Extracts frame sizes from packed unwind data.
src/​native/​managed/​cdac/​Microsoft.Diagnostics.DataContractReader.Contracts/​Contracts/​GCInfo/​X86/​GCInfo.cs Accepts version 6 for unchanged x86 encoding.
src/​native/​managed/​cdac/​Microsoft.Diagnostics.DataContractReader.Contracts/​Contracts/​GCInfo/​PlatformTraits/​WasmGCInfoTraits.cs Defines compact Wasm decoding traits.
src/​native/​managed/​cdac/​Microsoft.Diagnostics.DataContractReader.Contracts/​Contracts/​GCInfo/​PlatformTraits/​InterpreterGCInfoTraits.cs Marks interpreter slots as register-free.
src/​native/​managed/​cdac/​Microsoft.Diagnostics.DataContractReader.Contracts/​Contracts/​GCInfo/​PlatformTraits/​IGCInfoTraits.cs Adds shared compact-format trait defaults.
src/​native/​managed/​cdac/​Microsoft.Diagnostics.DataContractReader.Contracts/​Contracts/​GCInfo/​PlatformTraits/​ARMGCInfoTraits.cs Defines ARM’s bounded register width.
src/​native/​managed/​cdac/​Microsoft.Diagnostics.DataContractReader.Contracts/​Contracts/​GCInfo/​GCInfoDecoder.cs Decodes compact headers and v6 slot tables.
src/​native/​managed/​cdac/​Microsoft.Diagnostics.DataContractReader.Contracts/​Contracts/​ExecutionManager/​Helpers/​UnwindDataSize.cs Shares bounded Wasm ULEB128 reading.
src/​native/​managed/​cdac/​Microsoft.Diagnostics.DataContractReader.Contracts/​Contracts/​ExecutionManager/​ExecutionManagerCore.ReadyToRunJitManager.cs Maps ReadyToRun 34 onward to GCInfo v6.
src/​coreclr/​vm/​wasm/​helpers.cpp Uses packed unwind data during stack walking.
src/​coreclr/​vm/​readytoruninfo.cpp Reads packed unwind values and virtual-IP spans.
src/​coreclr/​vm/​proftoeeinterfaceimpl.cpp Uses the current GCInfo version for profiling.
src/​coreclr/​vm/​gcinfodecoder.cpp Decodes compact slots and specializes Wasm handling.
src/​coreclr/​vm/​gc_unwind_x86.inl Uses the current GCInfo version for x86 unwinding.
src/​coreclr/​vm/​codeman.h Removes runtime image-version mapping declaration.
src/​coreclr/​vm/​codeman.cpp Updates unwind sizing and runtime GCInfo version selection.
src/​coreclr/​tools/​Common/​Internal/​Runtime/​ModuleHeaders.cs Advances compiler format constants to 34.0.
src/​coreclr/​tools/​aot/​ILCompiler.Reflection.ReadyToRun/​Wasm32/​UnwindInfo.cs Decodes packed frame size and virtual-IP span.
src/​coreclr/​tools/​aot/​ILCompiler.Reflection.ReadyToRun/​ReadyToRunReader.cs Rejects older Wasm headers before caching them.
src/​coreclr/​tools/​aot/​ILCompiler.Reflection.ReadyToRun/​ReadyToRunMethod.cs Preserves historical mappings while adding v6.
src/​coreclr/​tools/​aot/​ILCompiler.Reflection.ReadyToRun/​NativeReader.cs Returns 64-bit ULEB128 values.
src/​coreclr/​tools/​aot/​ILCompiler.Reflection.ReadyToRun/​GCInfoTypes.cs Updates register widths and Wasm normalization.
src/​coreclr/​tools/​aot/​ILCompiler.Reflection.ReadyToRun/​Amd64/​GcSlotTable.cs Reads v6 slots while retaining legacy decoding.
src/​coreclr/​tools/​aot/​ILCompiler.Reflection.ReadyToRun/​Amd64/​GcInfo.cs Reads compact Wasm headers and implicit fields.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun/​Compiler/​DependencyAnalysis/​ReadyToRun/​RuntimeFunctionsTableNode.cs Extracts virtual-IP spans from packed unwind blobs.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun.Tests/​TestCases/​R2RTestSuites.cs Adds regular/composite header compatibility tests.
src/​coreclr/​tools/​aot/​ILCompiler.ReadyToRun.Tests/​GcInfoDecoderTests.cs Adds slot, header, and unwind decoding tests.
src/​coreclr/​nativeaot/​Runtime/​inc/​ModuleHeaders.h Synchronizes NativeAOT format constants.
src/​coreclr/​jit/​unwindwasm.cpp Emits interleaved Wasm unwind fields.
src/​coreclr/​jit/​gcencode.cpp Requires explicit stack-slot bases.
src/​coreclr/​jit/​codegenwasm.cpp Sets the implicit Wasm frame register consistently.
src/​coreclr/​inc/​sospriv.idl Documents version-independent stack-base values.
src/​coreclr/​inc/​readytorun.h Raises current and minimum ReadyToRun versions.
src/​coreclr/​inc/​gcinfotypes.h Defines compact-format platform traits.
src/​coreclr/​inc/​gcinfoencoder.h Updates encoder declarations and format comments.
src/​coreclr/​inc/​gcinfodecoder.h Adds compact slot-reading helpers and version handling.
src/​coreclr/​inc/​gcinfo.h Advances GCInfo to v6 and simplifies runtime selection.
src/​coreclr/​inc/​clrnt.h Adds shared packed-unwind extraction.
src/​coreclr/​gcinfo/​gcinfoencoder.cpp Emits grouped slots and compact Wasm headers.
docs/​design/​datacontracts/​StackWalk.md Documents packed Wasm unwinding.
docs/​design/​datacontracts/​reader-version-history.md Updates the supported ReadyToRun minimum.
docs/​design/​datacontracts/​GCInfo.md Documents compact Wasm headers and encoding parameters.
docs/​design/​datacontracts/​ExecutionManager.md Updates historical GCInfo version mappings.
docs/​design/​coreclr/​botr/​readytorun-format.md Specifies v6 slots, packed unwind data, and compatibility.

Review effort: Balanced

private IEnumerable<DecodePoints> DecodeSlotTable()
{
if (_reader.ReadBits(1, ref _bitOffset) != 0)
if (TTraits.HAS_REGISTER_SLOTS && _reader.ReadBits(1, ref _bitOffset) != 0)

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.

This seems valid

[Theory]
[InlineData(new byte[] { 0x09, 0x08, 0x20, 0x00 }, 8, uint.MaxValue, -1, 0u)]
[InlineData(new byte[] { 0x01, 0x09, 0x01, 0x00 }, -1, 1u, -1, 0u)]
[InlineData(new byte[] { 0x01, 0x0A, 0x03, 0x00 }, -1, uint.MaxValue, 3, 0u)]
Comment on lines +215 to +217
The tables and decoding pseudocode below describe the common layout. Wasm platform GCInfo
version 6 omits `UsingStackBaseRegister`, `StackBaseRegister`, and `PrologSize` as described
above, and omits `NumSafePoints` only for slim headers. Its fat `HeaderFlags` occupy nine
private IEnumerable<DecodePoints> DecodeSlotTable()
{
if (_reader.ReadBits(1, ref _bitOffset) != 0)
if (TTraits.HAS_REGISTER_SLOTS && _reader.ReadBits(1, ref _bitOffset) != 0)

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.

This seems valid

yield break;
}

private void DecodeStackSlots(uint count, bool isUntracked)

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.

It may be nice to note that this method and ReadRegisterValue are for the v6 GCInfo decoding.

}
}

if (TTraits.USES_COMPACT_WASM_HEADER || _gcVersion >= 6)

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 WASM support gcinfo version below 6? If not, is the TTraits.USES_COMPACT_WASM_HEADER in this guard required?

Comment thread src/coreclr/inc/gcinfo.h Outdated
Comment on lines +49 to +50
if (IsRuntimeVersionAtLeast(11))
return 5;
return 6;

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.

Is this intended to be backported to net11? I was under the impression this would be for 12.

davidwrighton and others added 8 commits November 4, 2026 19:00
Decode the JIT's interleaved unwind fields before emitting NativeAOT xdata and pdata. This fixes the Checked WASI frame-size assertion exposed by NativeAOT test compilation.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Consume the pre-v6 register-presence bit even when interpreter traits prohibit register slots. Correct the v5 interpreter fixture so it detects lost stack roots.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Report byte offsets consistently with other special stack slots and the native decoder. Cover both positive and negative Wasm offsets.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Describe shrinking flags-first descriptors, bounded register integers, compact stack bases, grouped offsets, and version-specific count bits. Keep the non-Wasm v5 algorithms explicitly labeled as legacy.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Wasm platform GCInfo supports only v6, so its traits need not override the version check used by the other platforms.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Preserve GCInfo v5 for .NET 11 and the existing mappings for earlier runtimes.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Correct architecture-specific frame registers, transition scaling and range mapping, indirect live-state payload sizes, ARM64 EnC header parsing, and lazy cDAC outgoing-area decoding. Add regressions using the existing v5 format.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Add a separate stack-parameter-area decode point for GetHeader while keeping ReversePInvoke aligned with the native decoder. Cover early stopping for generic-context lookups and slim headers.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
davidwrighton added a commit to dotnet/diagnostics that referenced this pull request Nov 4, 2026
## Summary
- Print the GC info address before choosing the ISOSDacInterface18 or
legacy decoding path. Previously, successful Interface18 decoding
returned before the address was printed.
- Preserve the existing address format and legacy decoding behavior,
printing the address exactly once.
- Extend the existing GCInfo test to assert that MethodDesc and
instruction-pointer inputs report the same GC info address.

Discovered while working on
dotnet/runtime#135821.

## Validation
- Repository build succeeded with system CMake 3.28.3: `./build.sh
/p:WarningsNotAsErrors=NU1903`. The override retains an existing
ILCompiler package advisory as a warning; no package or build
configuration changes are included.
- Focused `SOS.Tests.CodeInfoTests.GcInfo_ReportsEncoding` test passed
for both legacy DAC and cDAC on .NET 11 (2 cases).
- The available cDAC does not implement ISOSDacInterface18, so these
runs verify existing-path compatibility, not Interface18 execution. The
fix prints the address from the already-retrieved code header before
querying Interface18, ensuring its successful early return cannot omit
that output. End-to-end verification with an implementing cDAC remains
outstanding.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: d6045f2a-f938-4892-bdfa-016e6e58fc35
davidwrighton added a commit that referenced this pull request Nov 4, 2026
<!-- -->

## Summary

Fix seven pre-existing GCInfo reader defects without changing the
on-disk format, GCInfo/R2R version, encoder, or Wasm-specific behavior:

- Decode LoongArch64's normalized frame pointer as register 22, not
register 2.
- Decode RISC-V64's normalized stack pointer as register 2, not register
9, while retaining support for the older nonzero encoding.
- Denormalize fully interruptible transition offsets before combining
them with byte-based ranges.
- Map transitions correctly at half-open range boundaries and across
multiple skipped ranges, including methods without explicit
interruptible ranges.
- Measure indirect safe-point payloads using the furthest referenced
bitmap end, rather than the last bitmap visited. Cover both ordinary and
RLE bitmaps.
- Consume ARM64's additional EnC fixed-frame-size field so subsequent
fields remain aligned. This is a latent parser issue; ordinary crossgen2
compilation does not enable EnC.
- Decode cDAC's outgoing/scratch parameter area before returning a
header, using a separate stop point that preserves the native
reverse-P/Invoke decoding boundary.

The PR contains only diagnostic/tooling decoder fixes and regression
tests, based on `main`. It does not depend on the GCInfo v6 work in
#135821.

## Validation

Added 36 R2R regression cases and seven cDAC cases using the existing
GCInfo v5 format. Before applying the original fixes, the new tests
demonstrated 28 R2R failures and five cDAC failures. Additional review
regressions verify that generic-context lookup does not read the
outgoing area and that slim-header decoding does not read the safe-point
count.

Both commands below were run successfully on this isolated, main-based
branch with zero build warnings or errors:

```sh
./dotnet.sh build src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/ILCompiler.ReadyToRun.Tests.csproj -c Checked /p:RuntimeConfiguration=Checked /p:LibrariesConfiguration=Release /p:CoreCLRConfiguration=Checked /t:Build,Test
```

Result: **179 passed, 0 failed, 15 existing skips**, including all 36
new regression cases.

```sh
./dotnet.sh build src/native/managed/cdac/tests/UnitTests/Microsoft.Diagnostics.DataContractReader.Tests.csproj -c Checked /p:RuntimeConfiguration=Checked /p:LibrariesConfiguration=Release /t:Build,Test
```

Result: **3,969 passed, 0 failed, 0 skipped**, including all seven new
regression cases.

The fixtures exercise AMD64, ARM32, ARM64, LoongArch64, and RISC-V64
decoding on Linux x64; they are not native execution tests on those
other architectures.

> [!NOTE]
> This PR and its description were prepared with GitHub Copilot.

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…asm-gc-metadata

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

# Conflicts:
#	src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/GCInfoTypes.cs

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants