Skip to content

[wasm][R2R] Fix calls through MethodImpl-redirected slots on portable entrypoints - #136050

Merged
lewing merged 5 commits into
mainfrom
lewing-wasi-r2r-uninitialized-element-fix
Oct 20, 2026
Merged

lewing merged 5 commits into
mainfrom
lewing-wasi-r2r-uninitialized-element-fix

Conversation

@lewing

@lewing lewing commented Oct 19, 2026 •

Copy link
Copy Markdown
Member

With a same-type .override, a declaration's vtable slot holds the implementation's entrypoint, so on non-portable targets callers that start from the declaration still reach the implementation through the slot. With portable entrypoints (Wasm), three paths ran or prepared the declaration instead (self_override5):

  • ExternalMethodFixupWorker prestubbed the declaration before reading the slot, publishing the declaration's own R2R body and asserting in Checked. It now reads the slot, prepares the method that owns that entry, and patches the cell with it, like the non-portable branch.
  • ExecuteInterpretedMethodWithArgs_PortableEntryPoint_Complex (R2R → interpreter) prepared and ran the declaration, for example for a delegate bound by name to it. It now maps decl→impl first, as PrepareInterpreterCode already does.
  • The same entry's early path asserted in Checked when the implementation's interpreter code had been stored on the declaration (pre-existing). It now runs as the method that owns that code.

Removes the #134949 quarantine on self_override5.

Contributes to #134949

Scope and validation

The other #134949 failure (overflow0[1-4]_div) occurs only when R2R code runs with an interpreted CoreLib, which review settled is unsupported, so those quarantines stay. CallDescrWorkerInternal's portable path also prestubs without mapping; not reproduced, possible follow-up.

Validated on WASI (Checked runtime, wasmtime) with both an R2R CoreLib and the CI configuration: the MethodImpl tests, 20 CovariantReturns tests and jit64_5 pass under R2R and the interpreter. Not validated: browser and Release runtime builds.

Note

This pull request description was drafted with the help of GitHub Copilot.

… targets

Crossgen2 emitted R2R-to-interpreter thunks only for call-site signatures.
When a JIT helper is backed by a managed CoreLib method that is not in the
image, the method runs in the interpreter, and the helper's import cell can
only reach it through a thunk of the helper's signature. Small composites
that lacked a call site with a matching signature left the helper's
portable entrypoint without native code, so R2R code trapped with
"uninitialized element". Register the thunk at each helper use.

On portable entrypoints, ExternalMethodFixupWorker prestubbed the declared
method even when a MethodImpl in the same type redirected its slot to
another method, tripping the !ShouldCallPrestub() assert. Prepare and call
the MethodImpl target instead. TryGetMultiCallableAddrOfCode likewise
handed out the declared method's entrypoint, so delegates and ldftn ran the
substituted body; return the MethodImpl target's entrypoint, as
FuncPtrStubs does on other platforms.

Remove the WASI R2R quarantines for overflow0[1-4]_div and self_override5.

Fixes #134949

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: fc3c5462-a1aa-4727-b4d2-d43b9bbb16d9
@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
Successfully started running 3 pipeline(s).
13 pipeline(s) were filtered out due to trigger conditions.
There may be pipelines that require an authorized user to comment /azp run to run.

@lewing lewing added the arch-wasm WebAssembly architecture label Oct 19, 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.

@dotnet-policy-service

Copy link
Copy Markdown
Contributor

Tagging subscribers to this area: @dotnet/crossgen-contrib
See info in area-owners.md if you want to be subscribed.

@jkotas

jkotas commented Oct 19, 2026

Copy link
Copy Markdown
Member

When that method isn't in the image, it runs in the interpreter

Can we declare this configuration to be invalid? I do not think it is interesting to chase bugs in configurations where Corelib is interpreted, but the app is R2R.

  • R2R code outside CoreLib can be used only when CoreLib is R2R too
  • CoreLib R2R image must contain code for all JIT helpers

@lewing

lewing commented Oct 19, 2026

Copy link
Copy Markdown
Member Author

When that method isn't in the image, it runs in the interpreter

Can we declare this configuration to be invalid? I do not think it is interesting to chase bugs in configurations where Corelib is interpreted, but the app is R2R.

  • R2R code outside CoreLib can be used only when CoreLib is R2R too
  • CoreLib R2R image must contain code for all JIT helpers

Yeah that is probably the better choice, this is mostly a synthetic test problem due to the runtime tests being difficult to produce in r2r composite mode. Right now apple mobile runtime tests only r2r CoreLib, and wasi only r2rs the test assembly. I'm double checking and will update shortly.

Managed JIT helpers only lack an R2R-to-interpreter thunk when CoreLib is
interpreted while other code is R2R. Treat that configuration as
unsupported (R2R code outside CoreLib requires an R2R CoreLib that contains
all JIT helpers) instead of emitting helper thunks into every image.

Restore the WASI R2R quarantines for overflow0[1-4]_div, which hit this in
the current runtime-test lane where CoreLib is interpreted. The MethodImpl
fixes are independent of that configuration and remain.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: fc3c5462-a1aa-4727-b4d2-d43b9bbb16d9
@lewing lewing changed the title [wasi][R2R] Fix traps calling interpreted managed helpers and MethodImpl targets [wasm][R2R] Fix calls through MethodImpl-redirected slots on portable entrypoints Oct 19, 2026
@lewing

lewing commented Oct 19, 2026

Copy link
Copy Markdown
Member Author

Agreed, and I've updated the PR accordingly. The crossgen2 helper-thunk change is gone, and the overflow*_div quarantines are back. What's left are the two MethodImpl fixes in ExternalMethodFixupWorker and TryGetMultiCallableAddrOfCode. Those don't depend on the CoreLib configuration: self_override5 hits the same !ShouldCallPrestub() assert with an R2R CoreLib, and passes with these changes.

Follow-ups:

  • WASI test lane. It currently compiles only the test assemblies, because WASI has no runtime linking and the composer can merge just one R2R image into corerun. I have a working prototype that merges the prebuilt R2R System.Private.CoreLib.wasm alongside the test composite. That puts tests on R2R CoreLib without recompiling CoreLib per test.
  • Browser publishing. It can currently produce this configuration: PublishReadyToRunExclude of System.Private.CoreLib.dll (non-composite is the default on browser) publishes an IL CoreLib next to R2R app code. With only the app R2R, that hits the same null-funcref trap. I'll add an SDK error for excluding CoreLib from ReadyToRun on wasm, so the unsupported configuration can't be produced.

Note

This comment was drafted with the help of GitHub Copilot.

Comment thread src/coreclr/vm/method.cpp Outdated
Comment thread src/coreclr/vm/prestub.cpp Outdated
lewing and others added 2 commits October 19, 2026 13:09
Address review feedback on the portable-entrypoint MethodImpl handling:

- ExternalMethodFixupWorker: like the non-portable branch, call through
  pMD's slot (GetMethodEntryPoint) and prepare the method that owns the
  slot's entry, instead of mapping pMD with MapMethodDeclToMethodImpl.
- Drop the TryGetMultiCallableAddrOfCode change. Non-portable targets hand
  out the declared method's own entrypoint there too; the redirection
  happens when ThePreStub jumps to DoBackpatch's result, the slot target.
  The portable equivalent is the R2R-to-interpreter entry, which now maps
  decl to impl (as the interpreter's PrepareInterpreterCode does) before
  preparing and running the method, instead of ignoring the redirection.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: fc3c5462-a1aa-4727-b4d2-d43b9bbb16d9
When the interpreter reaches a declaration whose slot a same-type MethodImpl
redirects, PrepareInterpreterCode stores the implementation's interpreter
code on the declaration. A later call from R2R code through the
declaration's portable entrypoint took ExecuteInterpretedMethodWithArgs_
PortableEntryPoint_Complex's early path with that code, and in Checked
builds asserted that the declaration's entrypoint had interpreter data.
Run as the method that owns the interpreter code instead.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: fc3c5462-a1aa-4727-b4d2-d43b9bbb16d9
Comment thread src/coreclr/vm/prestub.cpp Outdated
Comment thread src/coreclr/vm/prestub.cpp Outdated
Apply review suggestions: when the method already has interpreter code, run as
the method that code was compiled for; otherwise prestub.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: fc3c5462-a1aa-4727-b4d2-d43b9bbb16d9
@lewing

lewing commented Oct 20, 2026

Copy link
Copy Markdown
Member Author

/ba-g All failures unrelated: JIT_r is #135966; TlsSessionTests is #136096; System.Runtime.Tests timeouts on wasm are the ControlledExecutionTests regression fixed by #136116.

@lewing
lewing merged commit 046f365 into main Oct 20, 2026
107 of 109 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

arch-wasm WebAssembly architecture area-ReadyToRun

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants