Skip to content

Reenable compiling runtime-async versions of synchronous task-returning methods in crossgen2 - #129474

Merged
jtschuster merged 17 commits into
dotnet:mainfrom
jtschuster:jtschuster/async-crossgen2-codegen
Jul 3, 2026
Merged

Reenable compiling runtime-async versions of synchronous task-returning methods in crossgen2#129474
jtschuster merged 17 commits into
dotnet:mainfrom
jtschuster:jtschuster/async-crossgen2-codegen

Conversation

@jtschuster

Copy link
Copy Markdown
Member

Builds off #128384 to add crossgen2 support. I just want to validate the work in CI for now.

CopilotAI review requested due to automatic review settings June 16, 2026 17:18
@github-actionsgithub-actionsBot added the area-crossgen2-coreclr only use for closed issues label Jun 16, 2026
@jtschuster

Copy link
Copy Markdown
MemberAuthor

/azp run runtime-coreclr crossgen2-outerloop

@azure-pipelines

Copy link
Copy Markdown
No pipelines are associated with this pull request.

@jtschusterjtschuster added area-ReadyToRun and removed area-crossgen2-coreclr only use for closed issues labels Jun 16, 2026
@dotnet-policy-service

Copy link
Copy Markdown
Contributor

Tagging @dotnet/jit-contrib for JIT-EE GUID update

@jtschuster

Copy link
Copy Markdown
MemberAuthor

/azp run runtime-coreclr crossgen2 outerloop

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines successfully started running 1 pipeline(s).

CopilotAI 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 extends the “runtime async version” work (async calling convention variants of synchronous Task/ValueTask-returning methods) to support crossgen2/ReadyToRun and NativeAOT scenarios by (1) tagging such methods as “async versions” while reusing the original IL, and (2) synthesizing an await/unwrapping call at returns so the async version’s signature matches the original IL behavior.

Changes:

  • Introduces a new JIT/EE interface query (getAwaitReturnCall) plus a new CORINFO_ASYNC_VERSION option to let the JIT compile async-version methods using the non-async IL and wrap return values appropriately.
  • Updates JIT importer + interpreter + AOT toolchain (R2R/NativeAOT IL providers and scanners) to recognize async-version compilation and the new await-return wrapper call.
  • Adds CoreLib helper overloads (AsyncHelpers.TransparentAwaitWithResult*) used by the JIT/EE to implement the return-value unwrap.

Reviewed changes

Copilot reviewed 52 out of 52 changed files in this pull request and generated 4 comments.

Show a summary per file
FileDescription
src/tests/nativeaot/SmokeTests/UnitTests/UnitTests.csprojRemoves explicit runtime-async=on feature flag from NativeAOT smoke test project.
src/tests/async/reflection/reflection.csAdds an async test variant for unsafe accessor invocation paths.
src/libraries/Directory.Build.targetsRemoves conditional runtime-async=on feature injection for libraries builds.
src/coreclr/vm/prestub.cppAdjusts async-variant IL header resolution logic for new async-version handling.
src/coreclr/vm/method.inlRefines IsDiagnosticsHidden behavior for async thunk/async-version scenarios.
src/coreclr/vm/method.hppAdds SupportsAsyncVersionCodegen() and updates IL-header availability rules for async thunks.
src/coreclr/vm/metasig.hAdds metasig definitions for the new TransparentAwaitWithResult* overloads.
src/coreclr/vm/jitinterface.hAdds helpers for runtime lookup construction and await-call lookup support.
src/coreclr/vm/jitinterface.cppImplements getAwaitReturnCall, factors runtime lookup finishing, and adjusts IL sourcing for async versions.
src/coreclr/vm/corelib.hAdds CoreLib binder entries for TransparentAwaitWithResult* overloads.
src/coreclr/vm/asyncthunks.cppRemoves legacy async thunk emitter path; keeps return-dropping thunk generation.
src/coreclr/tools/superpmi/superpmi/icorjitinfo.cppPlumbs getAwaitReturnCall through SuperPMI replay.
src/coreclr/tools/superpmi/superpmi-shim-simple/icorjitinfo_generated.cppAdds shim forwarding for getAwaitReturnCall.
src/coreclr/tools/superpmi/superpmi-shim-counter/icorjitinfo_generated.cppAdds shim counting + forwarding for getAwaitReturnCall.
src/coreclr/tools/superpmi/superpmi-shim-collector/icorjitinfo.cppRecords/replays getAwaitReturnCall in collector shim.
src/coreclr/tools/superpmi/superpmi-shared/spmirecordhelper.hMakes restore helpers take const& and updates lookup restore signatures.
src/coreclr/tools/superpmi/superpmi-shared/methodcontext.hAdds MethodContext packet + APIs for getAwaitReturnCall.
src/coreclr/tools/superpmi/superpmi-shared/methodcontext.cppImplements recording/replay plumbing for getAwaitReturnCall.
src/coreclr/tools/superpmi/superpmi-shared/lwmlist.hAdds LWM entry for GetAwaitReturnCall.
src/coreclr/tools/superpmi/superpmi-shared/agnostic.hAdds agnostic representations for CORINFO_LOOKUP and await-return results.
src/coreclr/tools/Common/TypeSystem/IL/Stubs/AsyncThunks.csRemoves managed-side async thunk emitter path now replaced by async-version codegen.
src/coreclr/tools/Common/TypeSystem/IL/NativeAotILProvider.csWraps IL for async variants generically via AsyncMethodIL instead of specialized Ecma-only wrapper.
src/coreclr/tools/Common/TypeSystem/IL/EcmaMethodIL.csUpdates EcmaMethodILScope to accept MethodDesc to support async variants.
src/coreclr/tools/Common/JitInterface/ThunkGenerator/ThunkInput.txtAdds getAwaitReturnCall to thunk-generator input.
src/coreclr/tools/Common/JitInterface/CorInfoTypes.csAdds CORINFO_ASYNC_VERSION option bit.
src/coreclr/tools/Common/JitInterface/CorInfoImpl.csImplements toolchain-side getAwaitReturnCall and sets CORINFO_ASYNC_VERSION option when needed.
src/coreclr/tools/Common/JitInterface/CorInfoImpl_generated.csPlumbs getAwaitReturnCall through generated callback surface.
src/coreclr/tools/Common/Compiler/MethodExtensions.csAdds SupportsAsyncVersionCodegen extension helper for toolchain decisions.
src/coreclr/tools/Common/Compiler/AsyncMethodVariant.csAdds RequiresSaveRestoreOfAsyncContexts helper for dependency reporting decisions.
src/coreclr/tools/aot/jitinterface/jitinterface_generated.hPlumbs getAwaitReturnCall into the AOT jitinterface wrapper.
src/coreclr/tools/aot/ILCompiler.RyuJit/JitInterface/CorInfoImpl.RyuJit.csRefactors ComputeLookup to accept explicit “isInlining” and adjusts behavior accordingly.
src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.csUpdates type check to the renamed AsyncMethodIL.
src/coreclr/tools/aot/ILCompiler.ReadyToRun/IL/ReadyToRunILProvider.csReplaces AsyncEcmaMethodIL with AsyncMethodIL wrapper delegating to wrapped IL.
src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunCodegenCompilation.csSeeds tokens for TransparentAwaitWithResult* overloads used by synthesized calls.
src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ExternalReferenceTokenManager.csEnsures token availability for methods/types with type instantiation.
src/coreclr/tools/aot/ILCompiler.Compiler/IL/ILImporter.Scanner.csTracks tail-await patterns for async-version methods and adds dependencies for return-wrapping await calls.
src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/AsyncMaskingILProvider.csAdjusts masking logic to apply to async variants regardless of IsAsync.
src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csprojRemoves explicit runtime-async=on features injection.
src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/AsyncHelpers.CoreCLR.csAdds TransparentAwaitWithResult* helper overloads used by the synthesized await-return call.
src/coreclr/nativeaot/System.Private.CoreLib/src/System.Private.CoreLib.csprojRemoves runtime-async=on features injection (and normalizes leading BOM/whitespace).
src/coreclr/jit/importercalls.cppAdjusts async call setup for async-version inlining/tail-await semantics.
src/coreclr/jit/importer.cppAdds async-version recognition, tail-await flagging, and return-value await wrapping via getAwaitReturnCall.
src/coreclr/jit/ICorJitInfo_wrapper_generated.hppAdds wrapper for getAwaitReturnCall.
src/coreclr/jit/ICorJitInfo_names_generated.hAdds API name entry for getAwaitReturnCall.
src/coreclr/jit/compiler.hAdds PREFIX_IS_ASYNC_VERSION_TAIL_AWAIT, impWrapTopOfStackInAwait, and compIsAsyncVersion.
src/coreclr/jit/compiler.cppPrints diagnostics when compiling an async-version method.
src/coreclr/interpreter/compiler.hAdds async-version tracking and await-wrapping helpers to interpreter compiler.
src/coreclr/interpreter/compiler.cppImplements async-version return wrapping and related peephole recognition in interpreter path.
src/coreclr/inc/jiteeversionguid.hUpdates JIT/EE interface version GUID for the new interface method.
src/coreclr/inc/icorjitinfoimpl_generated.hAdds getAwaitReturnCall to the EE’s generated ICorJitInfo implementation surface.
src/coreclr/inc/corinfo.hAdds CORINFO_ASYNC_VERSION and the getAwaitReturnCall interface method.
eng/testing/tests.targetsRemoves conditional runtime-async=on feature injection for test builds.

Comment threadsrc/coreclr/jit/importer.cpp
Comment threadsrc/coreclr/inc/corinfo.h
Comment threadsrc/coreclr/tools/Common/TypeSystem/IL/EcmaMethodIL.cs
jtschusterand others added 3 commits June 22, 2026 15:07
Currently crossgen2 skips methods where SupportsAsyncVersionCodegen() is
true (IsAsyncVariant() && IsAsyncThunk()), emitting a wrapper thunk instead
of compiling the real async body. This enables full R2R compilation of them:
- CorInfoImpl.ReadyToRun.cs: remove the SupportsAsyncVersionCodegen() skip in
ShouldSkipCompilation.
- CorInfoImpl.cs Get_CORINFO_METHOD_INFO: set CORINFO_ASYNC_VERSION for R2R too
(was NativeAOT-only) so the JIT runs the async-version import path.
- CorInfoImpl.cs getAwaitReturnCall: implement the R2R path (was returning null).
For the generic await case, emit a MethodDictionary ReadyToRunHelper const
lookup; the runtime-determined case defers to the runtime JIT (TODO-Async).
- ExternalReferenceTokenManager / ReadyToRunCodegenCompilation: pre-seed manifest
tokens for the AsyncHelpers.TransparentAwaitWithResult overloads, which the JIT
synthesizes calls to in getAwaitReturnCall and which therefore have no IL token
in the caller.
Validated: full async test tree with --runcrossgen2tests (87 passed, 0 failed,
1 pre-existing skip); covariant-returns crossgens to a valid R2R image.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Per review feedback: seed the TransparentAwaitWithResult overloads in the
same place the getAsyncInfo references are seeded (AddNecessaryAsyncReferences)
instead of a separate ExternalReferenceTokenManager method. They are folded
into the single batched EnsureDefTokensAreAvailable call.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…licit signatures
Per review feedback: instead of enumerating AsyncHelpers methods by name, add the
four TransparentAwaitWithResult overloads to the requiredMethods list using
GetKnownMethod with explicit MethodSignatures, mirroring how CorInfoImpl.getAwaitReturnCall
constructs them (Task/ValueTask and their generic forms).
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
CopilotAI review requested due to automatic review settings June 25, 2026 17:48
@jtschuster
jtschusterforce-pushed the jtschuster/async-crossgen2-codegen branch from 19a8732 to e881f61CompareJune 25, 2026 17:51

CopilotAI 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 3 out of 3 changed files in this pull request and generated no new comments.

CopilotAI review requested due to automatic review settings June 25, 2026 18:00

CopilotAI 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 3 out of 3 changed files in this pull request and generated no new comments.

Comment threadsrc/coreclr/tools/Common/JitInterface/CorInfoImpl.cs Outdated
CopilotAI review requested due to automatic review settings June 30, 2026 17:06
@jtschuster

Copy link
Copy Markdown
MemberAuthor

/azp run runtime-coreclr crossgen2 outerloop

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines successfully started running 1 pipeline(s).

@jtschusterjtschuster changed the title [No Review] Test crossgen2 compiling runtime async versions of synchronous task-returning methodsReenable compiling runtime-async versions of synchronous task-returning methods in crossgen2Jun 30, 2026
@jtschuster
jtschuster marked this pull request as ready for review June 30, 2026 17:09

CopilotAI 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 6 out of 6 changed files in this pull request and generated no new comments.

Comment threadsrc/coreclr/tools/Common/JitInterface/CorInfoImpl.cs

@jakobbotschjakobbotsch left a comment

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.

LGTM! Thanks for helping on this.

The await-return call synthesized in impWrapTopOfStackInAwait
(AsyncHelpers.TransparentAwaitWithResult) never had its Ready-to-Run
entry point set, unlike the seven sibling synthesized async calls in
async.cpp. As a result IsR2RRelativeIndir() was false for it, so on
arm64 fgMorphCall omitted the WellKnownArg::R2RIndirectionCell (x11)
argument that the ReadyToRun DelayLoad helpers require. At runtime the
arm64 DelayLoad_Helper then read a stale x11 as pCell, tripping the
GetDataRva "Data pointer is outside of loaded image" assert. x64 is
unaffected because it recovers the indirection cell by disassembling
the call site rather than from a register.
Fix: give the synthesized await call its R2R entry point explicitly by
reusing the existing async.cpp helper SetCallEntrypointForR2R (exposed
via a prototype in compiler.h, matching the dumpConvertedVarSet
precedent). The helper self-guards on FEATURE_READYTORUN /
IsReadyToRun(), so the importer call site needs no extra guard and the
change is a no-op on x64.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
@jtschuster

Copy link
Copy Markdown
MemberAuthor

/azp run runtime-coreclr crossgen2 outerloop

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines successfully started running 1 pipeline(s).

@jakobbotsch

Copy link
Copy Markdown
Member

This test should also be reenabled:

/// <summary>
/// Composite + runtime-async caller awaiting a NON-runtime-async virtual callee that the JIT
/// devirtualizes to a sealed receiver. Resolving the callee's async-variant thunk must unwrap it
/// to the underlying EcmaMethod.
/// </summary>
[Fact]
[ActiveIssue("https://github.com/dotnet/runtime/issues/129524")]
publicvoidCompositeAsyncDevirtNonAsyncCallee()

The callee virtuals are trivial no-op thunks. Under --optimize the JIT
devirtualizes and inlines the async-variant thunk into the caller, so no
standalone async-variant callee body is emitted and the test's
HasAsyncVariant check fails. Mark both virtuals [MethodImpl(NoInlining)]
so crossgen2 emits the callee's async-variant thunk as a standalone
method, exercising the devirtualized-method token resolution path this
test targets. Remove the [ActiveIssue(129524)] now that it passes.
Also apply a whitespace-only jit-format realignment in importer.cpp.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
CopilotAI review requested due to automatic review settings July 2, 2026 22:28

CopilotAI 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 10 out of 10 changed files in this pull request and generated no new comments.

Comment threadsrc/coreclr/jit/compiler.h
@jtschuster
jtschuster merged commit 2c05d04 into dotnet:mainJul 3, 2026
135 of 137 checks passed
@dotnet-milestone-botdotnet-milestone-botBot added this to the 11.0-preview7 milestone Jul 4, 2026
eiriktsarpalis pushed a commit that referenced this pull request Jul 15, 2026
…ng methods in crossgen2 (#129474)
Adds crossgen2 support for async version codegen:
- Remove the check that skipped compiling async variants in crossgen2
- Give JIT-synthesized await-return calls a proper R2R entrypoint
- Pre-seed tokens for the TransparentAwaitWithResult overloads
- Re-enable the CompositeAsyncDevirtNonAsyncCallee test
---------
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@github-actionsgithub-actionsBot locked and limited conversation to collaborators Aug 4, 2026
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

Reenable compiling async variants of non-async functions in crossgen2

3 participants

@jtschuster@jakobbotsch