Skip to content

[wasm] Restore TARGET_WASM short-circuit in VirtualUnwindCallFrame#129551

Closed
lewing wants to merge 3 commits into
dotnet:mainfrom
lewing:wasm-virtualunwind-shortcircuit
Closed

[wasm] Restore TARGET_WASM short-circuit in VirtualUnwindCallFrame#129551
lewing wants to merge 3 commits into
dotnet:mainfrom
lewing:wasm-virtualunwind-shortcircuit

Conversation

@lewing

@lewing lewing commented Jun 18, 2026

Copy link
Copy Markdown
Member

PR #128423 (Use Virtual IPs in R2R format) removed the TARGET_WASM short-circuit at the top of Thread::VirtualUnwindCallFrame(T_CONTEXT*) on the assumption that all WASM builds now ship R2R unwind info that the function can walk. That is not the case for the interpreter-only WASI corerun: there is no R2R, and the function has no RtlVirtualUnwind equivalent to fall back to.

Restore the assert + early-return-0 (matching the original code from PR #113429) so the function is a hard error instead of corrupting the register display by calling RtlVirtualUnwind on an unrelated context. On WASM EH stack-walking proceeds through the explicit Frame chain (Thread::GetFrame()), so this overload is not reached on the working path; restoring the short-circuit only protects against future bugs that would otherwise show up as silent register-display corruption.

This is defensive and not a fix for the wider interp+EH integration gap on WASI (managed try/catch still aborts in SfiNextWorker at the 'unhandled by runtime' branch; that needs separate work).

PR dotnet#128423 (Use Virtual IPs in R2R format) removed the TARGET_WASM
short-circuit at the top of Thread::VirtualUnwindCallFrame(T_CONTEXT*)
on the assumption that all WASM builds now ship R2R unwind info that the
function can walk. That is not the case for the interpreter-only WASI
corerun: there is no R2R, and the function has no RtlVirtualUnwind
equivalent to fall back to.

Restore the assert + early-return-0 (matching the original code from
PR dotnet#113429) so the function is a hard error instead of corrupting the
register display by calling RtlVirtualUnwind on an unrelated context.
On WASM EH stack-walking proceeds through the explicit Frame chain
(Thread::GetFrame()), so this overload is not reached on the working
path; restoring the short-circuit only protects against future bugs that
would otherwise show up as silent register-display corruption.

This is defensive and not a fix for the wider interp+EH integration gap
on WASI (managed try/catch still aborts in SfiNextWorker at the
'unhandled by runtime' branch; that needs separate work).

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

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

This PR reintroduces a WebAssembly-specific guard in Thread::VirtualUnwindCallFrame(T_CONTEXT*) to avoid attempting to unwind call frames on WASM in unsupported scenarios.

Changes:

  • Adds a TARGET_WASM early-exit path in Thread::VirtualUnwindCallFrame(T_CONTEXT*).
  • Wraps the existing non-WASM implementation under #else and closes it with a trailing #endif.

Comment thread src/coreclr/vm/stackwalk.cpp Outdated
Comment thread src/coreclr/vm/stackwalk.cpp Outdated
Comment thread src/coreclr/vm/stackwalk.cpp Outdated
ARM64_ARG(TADDR * pSpForPacSign /*= NULL*/))
{
#ifdef TARGET_WASM
// Interpreter-only WASM has no R2R unwind info to walk and no

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This is talking about Interpreter-only WASM, but ifdef is TARGET_WASM that includes R2R.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Good catch — fixed in 3c1e28b. Narrowed the guard with if (!ExecutionManager::IsVirtualIP(GetIP(pContext))) so the short-circuit only fires for non-virtual-IP contexts (the interpreter-only case), and R2R-WASM frames flow through to the WASM RtlVirtualUnwind in vm/wasm/helpers.cpp as intended.

Note

AI-generated content by GitHub Copilot CLI.

Co-authored-by: Jan Kotas <jkotas@microsoft.com>
Copilot AI review requested due to automatic review settings June 18, 2026 00:48

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

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

Comment thread src/coreclr/vm/stackwalk.cpp Outdated
Addresses review feedback from @jkotas: the previous guard short-
circuited the entire method for all TARGET_WASM, but CoreCLR has a
working WASM RtlVirtualUnwind in vm/wasm/helpers.cpp that handles
R2R-WASM frames keyed by virtual IPs. Blanket-disabling this method
prevented unwinding in the valid R2R path as well.

Check ExecutionManager::IsVirtualIP(GetIP(pContext)) at entry and only
fail fast when the context's control PC is not a virtual IP — that is
the interpreter-only case (e.g., WASI corerun without R2R) where the
RtlVirtualUnwind body would assert anyway and continuing past it would
corrupt the register display. R2R-WASM contexts now flow through the
regular path and reach the WASM RtlVirtualUnwind as intended.

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

lewing commented Jun 18, 2026

Copy link
Copy Markdown
Member Author

Pushed 3c1e28b addressing the review:

  • Narrowed the #ifdef TARGET_WASM guard with if (!ExecutionManager::IsVirtualIP(GetIP(pContext))). R2R-WASM frames (which carry virtual IPs) now flow through the regular body and reach the WASM RtlVirtualUnwind in vm/wasm/helpers.cpp as @jkotas / Copilot pointed out. Only interpreter-only contexts (no virtual IP) short-circuit.
  • Comment updated to describe the actual condition.

Smoke-tested locally:

  • ./build.sh -s clr -c Release -os wasi → 0 errors / 0 warnings
  • wasmtime run -W exceptions=y corerun HelloWorld.dll → boots, Console.WriteLine works
  • Single-frame try { throw } catch and multi-frame (A → B → C, catch at top) both unwind cleanly — the explicit Frame-chain EH path is unaffected by the narrowed guard.

Note

AI-generated content by GitHub Copilot CLI.

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants