dotnet / dotnet/runtime

WASM JIT: inline P/Invoke transition frame emits PHYSREG(REG_NA) for caller SP/FP, asserting in codegen

Open
#130,951 6 comments 0 reactions 1 assignee Claimed by @AndyAyersMS View on GitHub
arch-wasm area-CodeGen-coreclr
Dominant language
C#
Stars
18.3k
Forks
5.6k
PR merge metrics
PR metrics pending

Description

## Summary

The experimental WASM JIT (`clrjit_universal_wasm`) hits an assert on every method that requires an inline P/Invoke transition frame. The inline-frame lowering captures the caller''s hardware SP/FP into the `InlinedCallFrame`, but WASM has no such registers, so the emitted `GT_PHYSREG` nodes carry `REG_NA` and codegen asserts.

```
Assert failure: genIsValidReg(tree->gtSrcReg)
genCodeForPhysReg (codegenwasm.cpp)
```

Representative failing shape (JitDump, `System.ConsolePal:get_OutputEncoding`):

```
CALL CORINFO_HELP_INIT_PINVOKE_FRAME
LCL_ADDR V03 PInvokeFrame
t14 = PHYSREG int NA
```

## Root cause

The target-independent inline P/Invoke transition frame writes the caller SP/FP into the `InlinedCallFrame` for the VM stackwalk/unwind contract:

- `Lowering::InsertPInvokeMethodProlog` — `PhysReg(REG_SPBASE)` → `m_pCallSiteSP` (`lower.cpp:6848`); `PhysReg(REG_FPBASE)` → `m_pCalleeSavedEBP` (`lower.cpp:6863`)
- `Lowering::InsertPInvokeCallProlog` — `PhysReg(REG_SPBASE)` → `m_pCallSiteSP` (`lower.cpp:7069`)

On WASM `REG_SPBASE == REG_FPBASE == REG_NA` (`targetwasm.h`), so the resulting `GT_PHYSREG` nodes are invalid and `genCodeForPhysReg` asserts. These sites are guarded for x86/arm32 but not WASM, and `compMethodRequiresPInvokeFrame()` returns `true`, so WASM takes the full inline-frame path (`Lowering::DoPhase`, `lower.cpp:8926`).

## Why this needs a design decision (not a mechanical fix)

These slots feed the VM `InlinedCallFrame` stackwalk/unwind contract. WASM has no hardware SP/FP; the shadow-stack pointer (`lvaWasmSpArg`) is the nearest analogue, but whether the inline-frame transition mechanism applies to WASM at all — and what the runtime expects in `m_pCallSiteSP` / `m_pCalleeSavedEBP` for unwinding — is a WASM interop design decision. A blind codegen fix (e.g. substituting `lvaWasmSpArg` for SP and stubbing FP) would be guessing at the VM contract.

## Impact

In an SPMI replay of the WASM altjit over `libraries_tests_no_tiered_compilation` + `coreclr_tests`, this bucket accounts for ~4,912 asserting contexts, making it one of the larger remaining WASM codegen gaps. Tracking for the WASM interop owners.

> [!NOTE]
> This issue was authored with the assistance of GitHub Copilot.

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.