Skip to content

[Wasm RyuJit] emit virtual IP ranges in the unwind info#128382

Merged
AndyAyersMS merged 8 commits into
dotnet:mainfrom
AndyAyersMS:WasmReportVirtualIPRanges
May 26, 2026
Merged

[Wasm RyuJit] emit virtual IP ranges in the unwind info#128382
AndyAyersMS merged 8 commits into
dotnet:mainfrom
AndyAyersMS:WasmReportVirtualIPRanges

Conversation

@AndyAyersMS

@AndyAyersMS AndyAyersMS commented May 19, 2026

Copy link
Copy Markdown
Member

Extend the per-funclet unwind info to record the length of the virtual IP range. The lowest Virtual IP for a funclet can be found by summing the lengths of all the prior funclets.

The unwind info previously just recorded the size of the fixed part of the frame.

Virtual IP ranges for funclets (and main method) are disjoint.

Data is encoded as ULEB128.

Extend the per-funclet unwind info to record the starting Virtual IP
for the funclet and the length of the virtual IP range.

The unwind info previously just recorded the size of the fixed part
of the frame.

Virtual IP ranges for funclets (and main method) are disjoint.

All data is encoded as ULEB128.
Copilot AI review requested due to automatic review settings May 19, 2026 18:44
@github-actions github-actions Bot added the area-CodeGen-coreclr CLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI label May 19, 2026
@AndyAyersMS

Copy link
Copy Markdown
Member Author

@davidwrighton PTAL
fyi @dotnet/wasm-contrib

@AndyAyersMS

AndyAyersMS commented May 19, 2026

Copy link
Copy Markdown
Member Author

Sample output (from jit dump), for a method with a simple try/finally:

Unwind info for main 0: VIP range [0, 2); frame size 28
Unwind info for funclet 1: VIP range [2, 4); frame size 16

Also note you currently must pass --codegenopt:JitWasmFunclets=1 to crossgen2 to see any funclet codegen reach the host. This is temporary until the host can perform funclet extraction.

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.

Pull request overview

This PR updates the Wasm RyuJIT unwind-info payload to include a per-method/per-funclet Virtual IP (VIP) range alongside the existing frame-size data, and records those VIP ranges during the Wasm virtual-IP phase.

Changes:

  • Extend FuncInfoDsc (Wasm-only) with startVirtualIP / endVirtualIP fields.
  • Record startVirtualIP / endVirtualIP for each function/funclet during fgWasmVirtualIP().
  • Emit unwind info as ULEB128-encoded { frameSize, startVirtualIP, (endVirtualIP - startVirtualIP) } in unwindEmitFunc().

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.

File Description
src/coreclr/jit/unwindwasm.cpp Emits Wasm unwind payload including VIP range (start + delta) encoded as ULEB128.
src/coreclr/jit/fgwasm.cpp Captures per-funclet VIP range boundaries while assigning VIPs.
src/coreclr/jit/compiler.h Stores per-funclet VIP range endpoints in FuncInfoDsc under TARGET_WASM.

Comment thread src/coreclr/jit/unwindwasm.cpp Outdated
Comment thread src/coreclr/jit/unwindwasm.cpp
Comment thread src/coreclr/jit/unwindwasm.cpp
Copilot AI review requested due to automatic review settings May 20, 2026 16:15

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.

Pull request overview

Copilot reviewed 4 out of 4 changed files in this pull request and generated 2 comments.

Comments suppressed due to low confidence (1)

src/coreclr/jit/codegenwasm.cpp:3472

  • This changes the GC header's code length to maxVirtualIP, but the subsequent gcMakeRegPtrTable calls still pass codeSize/prologSize (native code byte offsets) to define interruptible ranges and call sites. Mixing VIP and byte-offset coordinate systems risks producing invalid GC info (e.g., offsets beyond the reported code length). Make the GC header and all reported offsets use the same units.
    unsigned callCnt = 0;

    // First we figure out the encoder ID's for the stack slots and registers.
    gcInfo.gcMakeRegPtrTable(gcInfoEncoder, codeSize, prologSize, GCInfo::MAKE_REG_PTR_MODE_ASSIGN_SLOTS, &callCnt);

Comment thread src/coreclr/jit/unwindwasm.cpp Outdated
Comment thread src/coreclr/jit/codegenwasm.cpp
Comment thread src/coreclr/jit/fgwasm.cpp
Comment thread src/coreclr/jit/unwindwasm.cpp Outdated
Comment thread src/coreclr/jit/unwindwasm.cpp Outdated

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

LGTM aside from copilot and davidw's concerns

Comment thread src/coreclr/jit/unwindwasm.cpp Outdated
Copilot AI review requested due to automatic review settings May 21, 2026 00:55

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.

Pull request overview

Copilot reviewed 4 out of 4 changed files in this pull request and generated 2 comments.

Comment thread src/coreclr/jit/unwindwasm.cpp
Comment thread src/coreclr/jit/unwindwasm.cpp
@AndyAyersMS

Copy link
Copy Markdown
Member Author

Looks like there is some other length field in GC info that needs fixing.

@AndyAyersMS

Copy link
Copy Markdown
Member Author

Looks like there is some other length field in GC info that needs fixing.

This is going to be more complicated than I thought. We need to track the Virtual IPs per instruction group so we can properly describe no-GC regions, and we need to ensure we have appropriate Virtual IP updates for these regions.

Or else we need to ensure we don't create any no-GC regions.

Or else convince ourselves that these regions cannot span calls and so we can just ignore them for GC reporting purposes on Wasm, at least for now, since the only viable GC safepoints are at calls.

@jkotas

jkotas commented May 21, 2026

Copy link
Copy Markdown
Member

convince ourselves that these regions cannot span calls and so we can just ignore them for GC reporting purposes on Wasm, at least for now,

I think this is valid simplifying assumption to make for wasm.

We should be able to omit reporting fully interruptible GC information (including no-GC regions) on any platform that requires explicit GC polls and does not support suspension via execution "redirection".

@AndyAyersMS

Copy link
Copy Markdown
Member Author

I started down the path of disabling fully interruptible GC for Wasm, but there are some missing parts that will require more work. Perhaps we can defer it a bit to unblock consumption of the data we're preoducing here

  • We removed the ability for the JIT to insert general GC polls in Remove code for GC Poll marking and insertion. #42664. We'll need to revive this for Wasm, and think about whether we can tolerate call per iteration for tight (call-free) loops or will want to fix them so we're not polling every iteration.
  • The JIT code indicates that GC reporting for methods with EH requires fully interruptible GC (as execution can stop most anywhere for implicit exceptions). That is not true on Wasm but there are perhaps corresponding changes needed on the runtime side if we relax this. Or maybe it falls out since the funclet and main method virtual IPs will always be at safe points.

@AndyAyersMS

Copy link
Copy Markdown
Member Author

Locally I hit an assert in debug SPC which I thought we had fixed:

Single method repro args:--singlemethodtypename "System.BitConverter" --singlemethodname "SingleToInt32Bits" --singlemethodindex 1
C:\repos\runtime3\src\coreclr\jit\codegenwasm.cpp:1813
Assertion failed 'NYI_WASM: Contained bitcast operands' in 'System.BitConverter:SingleToInt32Bits(float):int' during 'Generate code' (IL size 7; hash 0x810ee8cc; MinOpts)

working around this (and any subsequent NYIs) via

--codegenopt:JitWasmNyiToR2RUnsupported=1

SPC finishes crossgen cleanly.

@jkotas

jkotas commented May 21, 2026

Copy link
Copy Markdown
Member

We'll need to revive this for Wasm, and think about whether we can tolerate call per iteration for tight (call-free) loops or will want to fix them so we're not polling every iteration.

We may be able to get by without explicit gc polls for single threaded wasm, for MVP at least. The explicit gc polls should not be require for single-threaded runtime to work. The gc can be only triggered from the one thread and there are no other threads to suspend for the gc.

The explicit gc polls should be only required for scenarios like managed debugger attach to a process that is stuck inside a long running loop in AOT compiled code. I think it is P2 - I believe that it does not work with Mono either.

@kg

kg commented May 22, 2026

Copy link
Copy Markdown
Contributor

Locally I hit an assert in debug SPC which I thought we had fixed:

Single method repro args:--singlemethodtypename "System.BitConverter" --singlemethodname "SingleToInt32Bits" --singlemethodindex 1
C:\repos\runtime3\src\coreclr\jit\codegenwasm.cpp:1813
Assertion failed 'NYI_WASM: Contained bitcast operands' in 'System.BitConverter:SingleToInt32Bits(float):int' during 'Generate code' (IL size 7; hash 0x810ee8cc; MinOpts)

working around this (and any subsequent NYIs) via

--codegenopt:JitWasmNyiToR2RUnsupported=1

SPC finishes crossgen cleanly.

I'll look into this bitcast issue.

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

This LGTM from what I understand. I don't have enough context to comment on the GC-related discussion here.

@kg

kg commented May 22, 2026

Copy link
Copy Markdown
Contributor

edit: wrong tab

kg added a commit that referenced this pull request May 22, 2026
@AndyAyersMS

Copy link
Copy Markdown
Member Author

Going to wait for @davidwrighton to confirm this actually works before merging.

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

I won't be able to actually test this work before next Wednesday, but it now looks good to me (modulo the multiply by two issue on gc code size). I think it's in good enough state to merge.

Comment thread src/coreclr/jit/codegenwasm.cpp Outdated
@am11 am11 added the arch-wasm WebAssembly architecture label May 23, 2026
@dotnet-policy-service

Copy link
Copy Markdown
Contributor

Tagging subscribers to 'arch-wasm': @lewing, @pavelsavara
See info in area-owners.md if you want to be subscribed.

Copilot AI review requested due to automatic review settings May 26, 2026 16:19

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.

Pull request overview

Copilot reviewed 7 out of 7 changed files in this pull request and generated 2 comments.

Comment thread src/coreclr/jit/morph.cpp Outdated
Comment thread src/coreclr/jit/codegenwasm.cpp Outdated
@AndyAyersMS AndyAyersMS merged commit 9071409 into dotnet:main May 26, 2026
136 of 139 checks passed
@dotnet-milestone-bot dotnet-milestone-bot Bot added this to the 11.0-preview6 milestone May 27, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

arch-wasm WebAssembly architecture area-CodeGen-coreclr CLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants