[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

@AndyAyersMSAndyAyersMS 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.
CopilotAI review requested due to automatic review settings May 19, 2026 18:44
@github-actionsgithub-actionsBot 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
MemberAuthor

@davidwrighton PTAL
fyi @dotnet/wasm-contrib

@AndyAyersMS

AndyAyersMS commented May 19, 2026

Copy link
Copy Markdown
MemberAuthor

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.

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

FileDescription
src/coreclr/jit/unwindwasm.cppEmits Wasm unwind payload including VIP range (start + delta) encoded as ULEB128.
src/coreclr/jit/fgwasm.cppCaptures per-funclet VIP range boundaries while assigning VIPs.
src/coreclr/jit/compiler.hStores per-funclet VIP range endpoints in FuncInfoDsc under TARGET_WASM.

Comment threadsrc/coreclr/jit/unwindwasm.cpp Outdated
Comment threadsrc/coreclr/jit/unwindwasm.cpp
Comment threadsrc/coreclr/jit/unwindwasm.cpp
CopilotAI review requested due to automatic review settings May 20, 2026 16:15

CopilotAI 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 threadsrc/coreclr/jit/unwindwasm.cpp Outdated
Comment threadsrc/coreclr/jit/codegenwasm.cpp
Comment threadsrc/coreclr/jit/fgwasm.cpp
Comment threadsrc/coreclr/jit/unwindwasm.cpp Outdated
Comment threadsrc/coreclr/jit/unwindwasm.cpp Outdated
kg
kg approved these changes May 20, 2026

@kgkg 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 threadsrc/coreclr/jit/unwindwasm.cpp Outdated
CopilotAI review requested due to automatic review settings May 21, 2026 00:55

CopilotAI 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 threadsrc/coreclr/jit/unwindwasm.cpp
Comment threadsrc/coreclr/jit/unwindwasm.cpp
@AndyAyersMS

Copy link
Copy Markdown
MemberAuthor

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

@AndyAyersMS

Copy link
Copy Markdown
MemberAuthor

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
MemberAuthor

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
MemberAuthor

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.

@adamperlinadamperlin 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
MemberAuthor

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

@davidwrightondavidwrighton 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 threadsrc/coreclr/jit/codegenwasm.cpp Outdated
@am11am11 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.

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

CopilotAI 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 threadsrc/coreclr/jit/morph.cpp Outdated
Comment threadsrc/coreclr/jit/codegenwasm.cpp Outdated
@AndyAyersMS
AndyAyersMS merged commit 9071409 into dotnet:mainMay 26, 2026
136 of 139 checks passed
@dotnet-milestone-botdotnet-milestone-botBot added this to the 11.0-preview6 milestone May 27, 2026
@github-actionsgithub-actionsBot locked and limited conversation to collaborators Jun 27, 2026
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

arch-wasmWebAssembly architecturearea-CodeGen-coreclrCLR 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

@AndyAyersMS@jkotas@kg@adamperlin@davidwrighton@am11
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
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

@AndyAyersMSAndyAyersMS 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.
CopilotAI review requested due to automatic review settings May 19, 2026 18:44
@github-actionsgithub-actionsBot 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
MemberAuthor

@davidwrighton PTAL
fyi @dotnet/wasm-contrib

@AndyAyersMS

AndyAyersMS commented May 19, 2026

Copy link
Copy Markdown
MemberAuthor

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.

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

FileDescription
src/coreclr/jit/unwindwasm.cppEmits Wasm unwind payload including VIP range (start + delta) encoded as ULEB128.
src/coreclr/jit/fgwasm.cppCaptures per-funclet VIP range boundaries while assigning VIPs.
src/coreclr/jit/compiler.hStores per-funclet VIP range endpoints in FuncInfoDsc under TARGET_WASM.

Comment threadsrc/coreclr/jit/unwindwasm.cpp Outdated
Comment threadsrc/coreclr/jit/unwindwasm.cpp
Comment threadsrc/coreclr/jit/unwindwasm.cpp
CopilotAI review requested due to automatic review settings May 20, 2026 16:15

CopilotAI 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 threadsrc/coreclr/jit/unwindwasm.cpp Outdated
Comment threadsrc/coreclr/jit/codegenwasm.cpp
Comment threadsrc/coreclr/jit/fgwasm.cpp
Comment threadsrc/coreclr/jit/unwindwasm.cpp Outdated
Comment threadsrc/coreclr/jit/unwindwasm.cpp Outdated
kg
kg approved these changes May 20, 2026

@kgkg 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 threadsrc/coreclr/jit/unwindwasm.cpp Outdated
CopilotAI review requested due to automatic review settings May 21, 2026 00:55

CopilotAI 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 threadsrc/coreclr/jit/unwindwasm.cpp
Comment threadsrc/coreclr/jit/unwindwasm.cpp
@AndyAyersMS

Copy link
Copy Markdown
MemberAuthor

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

@AndyAyersMS

Copy link
Copy Markdown
MemberAuthor

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
MemberAuthor

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
MemberAuthor

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.

@adamperlinadamperlin 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
MemberAuthor

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

@davidwrightondavidwrighton 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 threadsrc/coreclr/jit/codegenwasm.cpp Outdated
@am11am11 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.

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

CopilotAI 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 threadsrc/coreclr/jit/morph.cpp Outdated
Comment threadsrc/coreclr/jit/codegenwasm.cpp Outdated
@AndyAyersMS
AndyAyersMS merged commit 9071409 into dotnet:mainMay 26, 2026
136 of 139 checks passed
@dotnet-milestone-botdotnet-milestone-botBot added this to the 11.0-preview6 milestone May 27, 2026
@github-actionsgithub-actionsBot locked and limited conversation to collaborators Jun 27, 2026
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

arch-wasmWebAssembly architecturearea-CodeGen-coreclrCLR 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

@AndyAyersMS@jkotas@kg@adamperlin@davidwrighton@am11
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
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

@AndyAyersMSAndyAyersMS 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.
CopilotAI review requested due to automatic review settings May 19, 2026 18:44
@github-actionsgithub-actionsBot 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
MemberAuthor

@davidwrighton PTAL
fyi @dotnet/wasm-contrib

@AndyAyersMS

AndyAyersMS commented May 19, 2026

Copy link
Copy Markdown
MemberAuthor

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.

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

FileDescription
src/coreclr/jit/unwindwasm.cppEmits Wasm unwind payload including VIP range (start + delta) encoded as ULEB128.
src/coreclr/jit/fgwasm.cppCaptures per-funclet VIP range boundaries while assigning VIPs.
src/coreclr/jit/compiler.hStores per-funclet VIP range endpoints in FuncInfoDsc under TARGET_WASM.

Comment threadsrc/coreclr/jit/unwindwasm.cpp Outdated
Comment threadsrc/coreclr/jit/unwindwasm.cpp
Comment threadsrc/coreclr/jit/unwindwasm.cpp
CopilotAI review requested due to automatic review settings May 20, 2026 16:15

CopilotAI 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 threadsrc/coreclr/jit/unwindwasm.cpp Outdated
Comment threadsrc/coreclr/jit/codegenwasm.cpp
Comment threadsrc/coreclr/jit/fgwasm.cpp
Comment threadsrc/coreclr/jit/unwindwasm.cpp Outdated
Comment threadsrc/coreclr/jit/unwindwasm.cpp Outdated
kg
kg approved these changes May 20, 2026

@kgkg 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 threadsrc/coreclr/jit/unwindwasm.cpp Outdated
CopilotAI review requested due to automatic review settings May 21, 2026 00:55

CopilotAI 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 threadsrc/coreclr/jit/unwindwasm.cpp
Comment threadsrc/coreclr/jit/unwindwasm.cpp
@AndyAyersMS

Copy link
Copy Markdown
MemberAuthor

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

@AndyAyersMS

Copy link
Copy Markdown
MemberAuthor

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
MemberAuthor

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
MemberAuthor

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.

@adamperlinadamperlin 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
MemberAuthor

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

@davidwrightondavidwrighton 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 threadsrc/coreclr/jit/codegenwasm.cpp Outdated
@am11am11 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.

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

CopilotAI 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 threadsrc/coreclr/jit/morph.cpp Outdated
Comment threadsrc/coreclr/jit/codegenwasm.cpp Outdated
@AndyAyersMS
AndyAyersMS merged commit 9071409 into dotnet:mainMay 26, 2026
136 of 139 checks passed
@dotnet-milestone-botdotnet-milestone-botBot added this to the 11.0-preview6 milestone May 27, 2026
@github-actionsgithub-actionsBot locked and limited conversation to collaborators Jun 27, 2026
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

arch-wasmWebAssembly architecturearea-CodeGen-coreclrCLR 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

@AndyAyersMS@jkotas@kg@adamperlin@davidwrighton@am11
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
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

@AndyAyersMSAndyAyersMS 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.
CopilotAI review requested due to automatic review settings May 19, 2026 18:44
@github-actionsgithub-actionsBot 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
MemberAuthor

@davidwrighton PTAL
fyi @dotnet/wasm-contrib

@AndyAyersMS

AndyAyersMS commented May 19, 2026

Copy link
Copy Markdown
MemberAuthor

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.

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

FileDescription
src/coreclr/jit/unwindwasm.cppEmits Wasm unwind payload including VIP range (start + delta) encoded as ULEB128.
src/coreclr/jit/fgwasm.cppCaptures per-funclet VIP range boundaries while assigning VIPs.
src/coreclr/jit/compiler.hStores per-funclet VIP range endpoints in FuncInfoDsc under TARGET_WASM.

Comment threadsrc/coreclr/jit/unwindwasm.cpp Outdated
Comment threadsrc/coreclr/jit/unwindwasm.cpp
Comment threadsrc/coreclr/jit/unwindwasm.cpp
CopilotAI review requested due to automatic review settings May 20, 2026 16:15

CopilotAI 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 threadsrc/coreclr/jit/unwindwasm.cpp Outdated
Comment threadsrc/coreclr/jit/codegenwasm.cpp
Comment threadsrc/coreclr/jit/fgwasm.cpp
Comment threadsrc/coreclr/jit/unwindwasm.cpp Outdated
Comment threadsrc/coreclr/jit/unwindwasm.cpp Outdated
kg
kg approved these changes May 20, 2026

@kgkg 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 threadsrc/coreclr/jit/unwindwasm.cpp Outdated
CopilotAI review requested due to automatic review settings May 21, 2026 00:55

CopilotAI 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 threadsrc/coreclr/jit/unwindwasm.cpp
Comment threadsrc/coreclr/jit/unwindwasm.cpp
@AndyAyersMS

Copy link
Copy Markdown
MemberAuthor

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

@AndyAyersMS

Copy link
Copy Markdown
MemberAuthor

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
MemberAuthor

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
MemberAuthor

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.

@adamperlinadamperlin 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
MemberAuthor

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

@davidwrightondavidwrighton 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 threadsrc/coreclr/jit/codegenwasm.cpp Outdated
@am11am11 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.

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

CopilotAI 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 threadsrc/coreclr/jit/morph.cpp Outdated
Comment threadsrc/coreclr/jit/codegenwasm.cpp Outdated
@AndyAyersMS
AndyAyersMS merged commit 9071409 into dotnet:mainMay 26, 2026
136 of 139 checks passed
@dotnet-milestone-botdotnet-milestone-botBot added this to the 11.0-preview6 milestone May 27, 2026
@github-actionsgithub-actionsBot locked and limited conversation to collaborators Jun 27, 2026
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

arch-wasmWebAssembly architecturearea-CodeGen-coreclrCLR 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

@AndyAyersMS@jkotas@kg@adamperlin@davidwrighton@am11
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
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

@AndyAyersMSAndyAyersMS 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.
CopilotAI review requested due to automatic review settings May 19, 2026 18:44
@github-actionsgithub-actionsBot 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
MemberAuthor

@davidwrighton PTAL
fyi @dotnet/wasm-contrib

@AndyAyersMS

AndyAyersMS commented May 19, 2026

Copy link
Copy Markdown
MemberAuthor

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.

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

FileDescription
src/coreclr/jit/unwindwasm.cppEmits Wasm unwind payload including VIP range (start + delta) encoded as ULEB128.
src/coreclr/jit/fgwasm.cppCaptures per-funclet VIP range boundaries while assigning VIPs.
src/coreclr/jit/compiler.hStores per-funclet VIP range endpoints in FuncInfoDsc under TARGET_WASM.

Comment threadsrc/coreclr/jit/unwindwasm.cpp Outdated
Comment threadsrc/coreclr/jit/unwindwasm.cpp
Comment threadsrc/coreclr/jit/unwindwasm.cpp
CopilotAI review requested due to automatic review settings May 20, 2026 16:15

CopilotAI 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 threadsrc/coreclr/jit/unwindwasm.cpp Outdated
Comment threadsrc/coreclr/jit/codegenwasm.cpp
Comment threadsrc/coreclr/jit/fgwasm.cpp
Comment threadsrc/coreclr/jit/unwindwasm.cpp Outdated
Comment threadsrc/coreclr/jit/unwindwasm.cpp Outdated
kg
kg approved these changes May 20, 2026

@kgkg 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 threadsrc/coreclr/jit/unwindwasm.cpp Outdated
CopilotAI review requested due to automatic review settings May 21, 2026 00:55

CopilotAI 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 threadsrc/coreclr/jit/unwindwasm.cpp
Comment threadsrc/coreclr/jit/unwindwasm.cpp
@AndyAyersMS

Copy link
Copy Markdown
MemberAuthor

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

@AndyAyersMS

Copy link
Copy Markdown
MemberAuthor

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
MemberAuthor

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
MemberAuthor

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.

@adamperlinadamperlin 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
MemberAuthor

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

@davidwrightondavidwrighton 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 threadsrc/coreclr/jit/codegenwasm.cpp Outdated
@am11am11 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.

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

CopilotAI 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 threadsrc/coreclr/jit/morph.cpp Outdated
Comment threadsrc/coreclr/jit/codegenwasm.cpp Outdated
@AndyAyersMS
AndyAyersMS merged commit 9071409 into dotnet:mainMay 26, 2026
136 of 139 checks passed
@dotnet-milestone-botdotnet-milestone-botBot added this to the 11.0-preview6 milestone May 27, 2026
@github-actionsgithub-actionsBot locked and limited conversation to collaborators Jun 27, 2026
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

arch-wasmWebAssembly architecturearea-CodeGen-coreclrCLR 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

@AndyAyersMS@jkotas@kg@adamperlin@davidwrighton@am11
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
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

@AndyAyersMSAndyAyersMS 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.
CopilotAI review requested due to automatic review settings May 19, 2026 18:44
@github-actionsgithub-actionsBot 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
MemberAuthor

@davidwrighton PTAL
fyi @dotnet/wasm-contrib

@AndyAyersMS

AndyAyersMS commented May 19, 2026

Copy link
Copy Markdown
MemberAuthor

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.

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

FileDescription
src/coreclr/jit/unwindwasm.cppEmits Wasm unwind payload including VIP range (start + delta) encoded as ULEB128.
src/coreclr/jit/fgwasm.cppCaptures per-funclet VIP range boundaries while assigning VIPs.
src/coreclr/jit/compiler.hStores per-funclet VIP range endpoints in FuncInfoDsc under TARGET_WASM.

Comment threadsrc/coreclr/jit/unwindwasm.cpp Outdated
Comment threadsrc/coreclr/jit/unwindwasm.cpp
Comment threadsrc/coreclr/jit/unwindwasm.cpp
CopilotAI review requested due to automatic review settings May 20, 2026 16:15

CopilotAI 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 threadsrc/coreclr/jit/unwindwasm.cpp Outdated
Comment threadsrc/coreclr/jit/codegenwasm.cpp
Comment threadsrc/coreclr/jit/fgwasm.cpp
Comment threadsrc/coreclr/jit/unwindwasm.cpp Outdated
Comment threadsrc/coreclr/jit/unwindwasm.cpp Outdated
kg
kg approved these changes May 20, 2026

@kgkg 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 threadsrc/coreclr/jit/unwindwasm.cpp Outdated
CopilotAI review requested due to automatic review settings May 21, 2026 00:55

CopilotAI 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 threadsrc/coreclr/jit/unwindwasm.cpp
Comment threadsrc/coreclr/jit/unwindwasm.cpp
@AndyAyersMS

Copy link
Copy Markdown
MemberAuthor

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

@AndyAyersMS

Copy link
Copy Markdown
MemberAuthor

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
MemberAuthor

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
MemberAuthor

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.

@adamperlinadamperlin 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
MemberAuthor

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

@davidwrightondavidwrighton 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 threadsrc/coreclr/jit/codegenwasm.cpp Outdated
@am11am11 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.

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

CopilotAI 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 threadsrc/coreclr/jit/morph.cpp Outdated
Comment threadsrc/coreclr/jit/codegenwasm.cpp Outdated
@AndyAyersMS
AndyAyersMS merged commit 9071409 into dotnet:mainMay 26, 2026
136 of 139 checks passed
@dotnet-milestone-botdotnet-milestone-botBot added this to the 11.0-preview6 milestone May 27, 2026
@github-actionsgithub-actionsBot locked and limited conversation to collaborators Jun 27, 2026
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

arch-wasmWebAssembly architecturearea-CodeGen-coreclrCLR 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

@AndyAyersMS@jkotas@kg@adamperlin@davidwrighton@am11
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
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

@AndyAyersMSAndyAyersMS 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.
CopilotAI review requested due to automatic review settings May 19, 2026 18:44
@github-actionsgithub-actionsBot 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
MemberAuthor

@davidwrighton PTAL
fyi @dotnet/wasm-contrib

@AndyAyersMS

AndyAyersMS commented May 19, 2026

Copy link
Copy Markdown
MemberAuthor

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.

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

FileDescription
src/coreclr/jit/unwindwasm.cppEmits Wasm unwind payload including VIP range (start + delta) encoded as ULEB128.
src/coreclr/jit/fgwasm.cppCaptures per-funclet VIP range boundaries while assigning VIPs.
src/coreclr/jit/compiler.hStores per-funclet VIP range endpoints in FuncInfoDsc under TARGET_WASM.

Comment threadsrc/coreclr/jit/unwindwasm.cpp Outdated
Comment threadsrc/coreclr/jit/unwindwasm.cpp
Comment threadsrc/coreclr/jit/unwindwasm.cpp
CopilotAI review requested due to automatic review settings May 20, 2026 16:15

CopilotAI 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 threadsrc/coreclr/jit/unwindwasm.cpp Outdated
Comment threadsrc/coreclr/jit/codegenwasm.cpp
Comment threadsrc/coreclr/jit/fgwasm.cpp
Comment threadsrc/coreclr/jit/unwindwasm.cpp Outdated
Comment threadsrc/coreclr/jit/unwindwasm.cpp Outdated
kg
kg approved these changes May 20, 2026

@kgkg 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 threadsrc/coreclr/jit/unwindwasm.cpp Outdated
CopilotAI review requested due to automatic review settings May 21, 2026 00:55

CopilotAI 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 threadsrc/coreclr/jit/unwindwasm.cpp
Comment threadsrc/coreclr/jit/unwindwasm.cpp
@AndyAyersMS

Copy link
Copy Markdown
MemberAuthor

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

@AndyAyersMS

Copy link
Copy Markdown
MemberAuthor

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
MemberAuthor

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
MemberAuthor

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.

@adamperlinadamperlin 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
MemberAuthor

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

@davidwrightondavidwrighton 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 threadsrc/coreclr/jit/codegenwasm.cpp Outdated
@am11am11 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.

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

CopilotAI 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 threadsrc/coreclr/jit/morph.cpp Outdated
Comment threadsrc/coreclr/jit/codegenwasm.cpp Outdated
@AndyAyersMS
AndyAyersMS merged commit 9071409 into dotnet:mainMay 26, 2026
136 of 139 checks passed
@dotnet-milestone-botdotnet-milestone-botBot added this to the 11.0-preview6 milestone May 27, 2026
@github-actionsgithub-actionsBot locked and limited conversation to collaborators Jun 27, 2026
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

arch-wasmWebAssembly architecturearea-CodeGen-coreclrCLR 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

@AndyAyersMS@jkotas@kg@adamperlin@davidwrighton@am11
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
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

@AndyAyersMSAndyAyersMS 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.
CopilotAI review requested due to automatic review settings May 19, 2026 18:44
@github-actionsgithub-actionsBot 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
MemberAuthor

@davidwrighton PTAL
fyi @dotnet/wasm-contrib

@AndyAyersMS

AndyAyersMS commented May 19, 2026

Copy link
Copy Markdown
MemberAuthor

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.

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

FileDescription
src/coreclr/jit/unwindwasm.cppEmits Wasm unwind payload including VIP range (start + delta) encoded as ULEB128.
src/coreclr/jit/fgwasm.cppCaptures per-funclet VIP range boundaries while assigning VIPs.
src/coreclr/jit/compiler.hStores per-funclet VIP range endpoints in FuncInfoDsc under TARGET_WASM.

Comment threadsrc/coreclr/jit/unwindwasm.cpp Outdated
Comment threadsrc/coreclr/jit/unwindwasm.cpp
Comment threadsrc/coreclr/jit/unwindwasm.cpp
CopilotAI review requested due to automatic review settings May 20, 2026 16:15

CopilotAI 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 threadsrc/coreclr/jit/unwindwasm.cpp Outdated
Comment threadsrc/coreclr/jit/codegenwasm.cpp
Comment threadsrc/coreclr/jit/fgwasm.cpp
Comment threadsrc/coreclr/jit/unwindwasm.cpp Outdated
Comment threadsrc/coreclr/jit/unwindwasm.cpp Outdated
kg
kg approved these changes May 20, 2026

@kgkg 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 threadsrc/coreclr/jit/unwindwasm.cpp Outdated
CopilotAI review requested due to automatic review settings May 21, 2026 00:55

CopilotAI 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 threadsrc/coreclr/jit/unwindwasm.cpp
Comment threadsrc/coreclr/jit/unwindwasm.cpp
@AndyAyersMS

Copy link
Copy Markdown
MemberAuthor

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

@AndyAyersMS

Copy link
Copy Markdown
MemberAuthor

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
MemberAuthor

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
MemberAuthor

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.

@adamperlinadamperlin 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
MemberAuthor

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

@davidwrightondavidwrighton 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 threadsrc/coreclr/jit/codegenwasm.cpp Outdated
@am11am11 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.

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

CopilotAI 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 threadsrc/coreclr/jit/morph.cpp Outdated
Comment threadsrc/coreclr/jit/codegenwasm.cpp Outdated
@AndyAyersMS
AndyAyersMS merged commit 9071409 into dotnet:mainMay 26, 2026
136 of 139 checks passed
@dotnet-milestone-botdotnet-milestone-botBot added this to the 11.0-preview6 milestone May 27, 2026
@github-actionsgithub-actionsBot locked and limited conversation to collaborators Jun 27, 2026
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

arch-wasmWebAssembly architecturearea-CodeGen-coreclrCLR 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

@AndyAyersMS@jkotas@kg@adamperlin@davidwrighton@am11