Skip to content

JIT/wasm: pass type-context arg for LDVIRTFTN calls#129903

Open
AndyAyersMS wants to merge 2 commits into
dotnet:mainfrom
AndyAyersMS:wasm-ldvirtftn-typearg
Open

JIT/wasm: pass type-context arg for LDVIRTFTN calls#129903
AndyAyersMS wants to merge 2 commits into
dotnet:mainfrom
AndyAyersMS:wasm-ldvirtftn-typearg

Conversation

@AndyAyersMS

Copy link
Copy Markdown
Member

On wasm, virtual stub dispatch is unsupported, so all virtual calls are routed through LDVIRTFTN (CORINFO_HELP_VIRTUAL_FUNC_PTR). That importer path reaches the call epilogue via goto DEVIRT, jumping over the shared code that appends the hidden instantiation ("generic context") argument for calls that need one.

Some callees reached this way still require that hidden argument -- most notably the multidimensional-array Address accessor, which is marked CORINFO_CALLCONV_PARAMTYPE to capture the callsite element type for its store-type check. With the argument missing, the emitted wasm call_indirect signature is one parameter short of the callee (and its R2R-to-interpreter thunk), so the call traps at runtime. (Get/Set take no type arg, and rank <= GT_ARR_MAX_RANK array accessors are inlined, so only the non-inlined Address path was affected.)

This is the wasm-R2R subset of #129622.

Fix: in the wasm LDVIRTFTN path, add the type-context argument when sig->hasTypeArg(). The hidden-arg computation is factored out of the existing hasTypeArg() handling into a shared helper impGetInstParamArg used by both call sites.

Validation

  • x64 SuperPMI asmdiffs over all collections (~2.7M contexts): no diffs -- the helper extraction is behavior-neutral, and the new code is #ifdef TARGET_WASM.
  • Local wasm Pri-1 R2R test sweep: +1242 passing tests and 14 of 16 wrapper hard-crashes resolved, with no regressions (the remaining failures are pre-existing EH/infra issues unrelated to this change).

Wasm routes all virtual calls through LDVIRTFTN, whose import path dropped
the hidden instantiation arg some callees need (e.g. the array Address
accessor), so the call_indirect signature was a param short and trapped.
Add the arg in the wasm path; factor the shared logic into impGetInstParamArg.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings June 26, 2026 18:15
@github-actions github-actions Bot added the area-CodeGen-coreclr CLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI label Jun 26, 2026
@dotnet-policy-service

Copy link
Copy Markdown
Contributor

Tagging subscribers to this area: @JulieLeeMSFT, @jakobbotsch
See info in area-owners.md if you want to be subscribed.

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

Fixes missing hidden instantiation (“type context” / CORINFO_CALLCONV_PARAMTYPE) argument insertion for Wasm virtual-call lowering that routes through the LDVIRTFTN (CORINFO_HELP_VIRTUAL_FUNC_PTR) importer path, by factoring the inst-param computation into a shared helper and using it from the Wasm LDVIRTFTN path.

Changes:

  • Factor shared instantiation-parameter argument computation into Compiler::impGetInstParamArg.
  • On TARGET_WASM, insert the instantiation parameter for LDVIRTFTN-based virtual dispatch when sig->hasTypeArg().
  • Update compiler.h with the new helper declaration.

Reviewed changes

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

File Description
src/coreclr/jit/importercalls.cpp Adds impGetInstParamArg helper and uses it to insert the instantiation param in the Wasm LDVIRTFTN import path.
src/coreclr/jit/compiler.h Declares Compiler::impGetInstParamArg.

Comment on lines +598 to +610
if (sig->hasTypeArg())
{
GenTree* wasmInstParam =
impGetInstParamArg(pResolvedToken, callInfo, exactContextHnd, exactContextNeedsRuntimeLookup,
clsFlags, isReadonlyCall);
if (wasmInstParam == nullptr)
{
assert(compDonotInline());
return TYP_UNDEF;
}

call->AsCall()->gtArgs.InsertInstParam(this, wasmInstParam);
}
@AndyAyersMS AndyAyersMS requested a review from adamperlin June 26, 2026 18:26
@AndyAyersMS

Copy link
Copy Markdown
Member Author

@adamperlin PTAL
fyi @dotnet/wasm-contrib

Inching closer to 100% pass on the JIT subtree Pri1 tests.

Mirror the shared hasTypeArg() handling in the wasm LDVIRTFTN path: consume
and clear lvaNextCallGenericContext before computing the type-context arg, so
an explicit generic context is not ignored or leaked to a later call.

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

Copy link
Copy Markdown
Member Author

Good catch. In practice SetNextCallGenericContext is emitted only right before a calli (shared-generic function-pointer dispatch), and on wasm a calli is imported via impImportIndirectCall and reaches the shared hasTypeArg() handling rather than this LDVIRTFTN path, so lvaNextCallGenericContext wouldn't be set here today. That said, the robustness concern is fair -- I've updated the wasm LDVIRTFTN block to honor and clear lvaNextCallGenericContext exactly like the shared path before falling back to the shared helper (feffdc1).

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

Labels

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants