Skip to content

Fix composite R2R token corruption for devirtualized async-variant callees#129053

Merged
jtschuster merged 1 commit into
dotnet:mainfrom
jtschuster:jtschuster/shiny-potato
Jun 6, 2026
Merged

Fix composite R2R token corruption for devirtualized async-variant callees#129053
jtschuster merged 1 commit into
dotnet:mainfrom
jtschuster:jtschuster/shiny-potato

Conversation

@jtschuster
Copy link
Copy Markdown
Member

In composite ReadyToRun, a runtime-async caller that awaits a devirtualized call to a non-runtime-async virtual method reaches token resolution with the callee's compiler-generated async thunk. GetTypicalMethodDefinition() on that variant returns the AsyncMethodVariant rather than the underlying EcmaMethod, so _HandleToModuleToken skipped the methoddef fast path and fell through to the faux-IL token path, indexing the thunk's small synthetic token table with the callee's real methoddef token. This causes an IndexOutOfRangeException (usually), or a subtly corrupted reference.

Insert GetPrimaryMethodDesc() before GetTypicalMethodDefinition() in the devirtualized target case we can get the underlying EcmaMethod and its Module. The MetadataToken comes from resolveVirtualMethod which should return a token that is valid in the EcmaMethod's Module.

Add regression tests in ILCompiler.ReadyToRun.Tests (composite, two-assembly) and src/tests/async (single-assembly via [RuntimeAsyncMethodGeneration(false)]). Both abort crossgen2 without the fix and pass with it.

Fixes #128413

…llees

In composite ReadyToRun, a runtime-async caller that awaits a devirtualized
call to a non-runtime-async virtual method reaches token resolution with the
callee's compiler-synthesized async-variant MethodDesc. GetTypicalMethodDefinition()
on that variant returns the variant itself rather than the underlying EcmaMethod,
so _HandleToModuleToken skipped the methoddef fast path and fell through to the
faux-IL token path, indexing the thunk's small synthetic token table with the
callee's real methoddef token. This corrupts token resolution (out-of-bounds for
larger rows, mis-resolution for low rows) and aborts crossgen2.

Insert GetPrimaryMethodDesc() before GetTypicalMethodDefinition() so synthetic
wrappers unwrap to the underlying EcmaMethod, matching the idiom already used at
the sibling resolver sites. The token and module are then sourced consistently
from that EcmaMethod.

Add regression tests in ILCompiler.ReadyToRun.Tests (composite, two-assembly)
and src/tests/async (single-assembly via [RuntimeAsyncMethodGeneration(false)]).
Both abort crossgen2 without the fix and pass with it.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@jtschuster jtschuster self-assigned this Jun 5, 2026
Copilot AI review requested due to automatic review settings June 5, 2026 20:39
@github-actions github-actions Bot added the area-crossgen2-coreclr only use for closed issues label Jun 5, 2026
@jtschuster jtschuster added area-ReadyToRun and removed area-crossgen2-coreclr only use for closed issues labels Jun 5, 2026
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

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 fixes a composite ReadyToRun (crossgen2) token-resolution failure when a runtime-async caller awaits a devirtualized call whose resolved callee is a compiler-synthesized async-variant thunk, by unwrapping to the primary (underlying ECMA) method before resolving module/token. It also adds regression coverage in both ILCompiler.ReadyToRun.Tests (composite, multi-assembly) and src/tests/async (single-assembly).

Changes:

  • Update R2R JIT interface token handling to unwrap synthetic MethodDesc wrappers via GetPrimaryMethodDesc() before GetTypicalMethodDefinition().
  • Add a new composite runtime-async devirtualization regression test case (two-assembly) in ILCompiler.ReadyToRun.Tests.
  • Add a new src/tests/async test project covering devirtualization to inherited non-runtime-async virtual methods.

Reviewed changes

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

Show a summary per file
File Description
src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs Unwrap synthetic method wrappers before resolving methoddef tokens to avoid faux-token-table indexing issues.
src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCases/R2RTestSuites.cs Adds a new composite runtime-async devirtualization test case and validation assertions.
src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCases/RuntimeAsync/Dependencies/AsyncDevirtNonAsyncCalleeLib.cs New dependency library source for the composite regression scenario (non-runtime-async callee).
src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCases/RuntimeAsync/CompositeAsyncDevirtNonAsyncCalleeMain.cs New runtime-async “main” source that awaits the devirtualized inherited virtual methods.
src/tests/async/devirtualize-inherited-nonasync/devirtualize-inherited-nonasync.csproj New async test project wrapper (single source compile include).
src/tests/async/devirtualize-inherited-nonasync/devirtualize-inherited-nonasync.cs New async regression test exercising devirtualization to inherited non-runtime-async virtual methods.

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

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

ASP.NET Core Web App shows Unhandled ILCompiler IndexOutOfRangeException error when property group PublishReadyToRun=true in .NET 11 Preview 5

4 participants