Reenable compiling runtime-async versions of synchronous task-returning methods in crossgen2#129474
Conversation
|
/azp run runtime-coreclr crossgen2-outerloop |
|
No pipelines are associated with this pull request. |
|
Tagging @dotnet/jit-contrib for JIT-EE GUID update |
|
/azp run runtime-coreclr crossgen2 outerloop |
|
Azure Pipelines successfully started running 1 pipeline(s). |
There was a problem hiding this comment.
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 newCORINFO_ASYNC_VERSIONoption 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
| File | Description |
|---|---|
| src/tests/nativeaot/SmokeTests/UnitTests/UnitTests.csproj | Removes explicit runtime-async=on feature flag from NativeAOT smoke test project. |
| src/tests/async/reflection/reflection.cs | Adds an async test variant for unsafe accessor invocation paths. |
| src/libraries/Directory.Build.targets | Removes conditional runtime-async=on feature injection for libraries builds. |
| src/coreclr/vm/prestub.cpp | Adjusts async-variant IL header resolution logic for new async-version handling. |
| src/coreclr/vm/method.inl | Refines IsDiagnosticsHidden behavior for async thunk/async-version scenarios. |
| src/coreclr/vm/method.hpp | Adds SupportsAsyncVersionCodegen() and updates IL-header availability rules for async thunks. |
| src/coreclr/vm/metasig.h | Adds metasig definitions for the new TransparentAwaitWithResult* overloads. |
| src/coreclr/vm/jitinterface.h | Adds helpers for runtime lookup construction and await-call lookup support. |
| src/coreclr/vm/jitinterface.cpp | Implements getAwaitReturnCall, factors runtime lookup finishing, and adjusts IL sourcing for async versions. |
| src/coreclr/vm/corelib.h | Adds CoreLib binder entries for TransparentAwaitWithResult* overloads. |
| src/coreclr/vm/asyncthunks.cpp | Removes legacy async thunk emitter path; keeps return-dropping thunk generation. |
| src/coreclr/tools/superpmi/superpmi/icorjitinfo.cpp | Plumbs getAwaitReturnCall through SuperPMI replay. |
| src/coreclr/tools/superpmi/superpmi-shim-simple/icorjitinfo_generated.cpp | Adds shim forwarding for getAwaitReturnCall. |
| src/coreclr/tools/superpmi/superpmi-shim-counter/icorjitinfo_generated.cpp | Adds shim counting + forwarding for getAwaitReturnCall. |
| src/coreclr/tools/superpmi/superpmi-shim-collector/icorjitinfo.cpp | Records/replays getAwaitReturnCall in collector shim. |
| src/coreclr/tools/superpmi/superpmi-shared/spmirecordhelper.h | Makes restore helpers take const& and updates lookup restore signatures. |
| src/coreclr/tools/superpmi/superpmi-shared/methodcontext.h | Adds MethodContext packet + APIs for getAwaitReturnCall. |
| src/coreclr/tools/superpmi/superpmi-shared/methodcontext.cpp | Implements recording/replay plumbing for getAwaitReturnCall. |
| src/coreclr/tools/superpmi/superpmi-shared/lwmlist.h | Adds LWM entry for GetAwaitReturnCall. |
| src/coreclr/tools/superpmi/superpmi-shared/agnostic.h | Adds agnostic representations for CORINFO_LOOKUP and await-return results. |
| src/coreclr/tools/Common/TypeSystem/IL/Stubs/AsyncThunks.cs | Removes managed-side async thunk emitter path now replaced by async-version codegen. |
| src/coreclr/tools/Common/TypeSystem/IL/NativeAotILProvider.cs | Wraps IL for async variants generically via AsyncMethodIL instead of specialized Ecma-only wrapper. |
| src/coreclr/tools/Common/TypeSystem/IL/EcmaMethodIL.cs | Updates EcmaMethodILScope to accept MethodDesc to support async variants. |
| src/coreclr/tools/Common/JitInterface/ThunkGenerator/ThunkInput.txt | Adds getAwaitReturnCall to thunk-generator input. |
| src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs | Adds CORINFO_ASYNC_VERSION option bit. |
| src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs | Implements toolchain-side getAwaitReturnCall and sets CORINFO_ASYNC_VERSION option when needed. |
| src/coreclr/tools/Common/JitInterface/CorInfoImpl_generated.cs | Plumbs getAwaitReturnCall through generated callback surface. |
| src/coreclr/tools/Common/Compiler/MethodExtensions.cs | Adds SupportsAsyncVersionCodegen extension helper for toolchain decisions. |
| src/coreclr/tools/Common/Compiler/AsyncMethodVariant.cs | Adds RequiresSaveRestoreOfAsyncContexts helper for dependency reporting decisions. |
| src/coreclr/tools/aot/jitinterface/jitinterface_generated.h | Plumbs getAwaitReturnCall into the AOT jitinterface wrapper. |
| src/coreclr/tools/aot/ILCompiler.RyuJit/JitInterface/CorInfoImpl.RyuJit.cs | Refactors ComputeLookup to accept explicit “isInlining” and adjusts behavior accordingly. |
| src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs | Updates type check to the renamed AsyncMethodIL. |
| src/coreclr/tools/aot/ILCompiler.ReadyToRun/IL/ReadyToRunILProvider.cs | Replaces AsyncEcmaMethodIL with AsyncMethodIL wrapper delegating to wrapped IL. |
| src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunCodegenCompilation.cs | Seeds tokens for TransparentAwaitWithResult* overloads used by synthesized calls. |
| src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ExternalReferenceTokenManager.cs | Ensures token availability for methods/types with type instantiation. |
| src/coreclr/tools/aot/ILCompiler.Compiler/IL/ILImporter.Scanner.cs | Tracks tail-await patterns for async-version methods and adds dependencies for return-wrapping await calls. |
| src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/AsyncMaskingILProvider.cs | Adjusts masking logic to apply to async variants regardless of IsAsync. |
| src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj | Removes explicit runtime-async=on features injection. |
| src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/AsyncHelpers.CoreCLR.cs | Adds TransparentAwaitWithResult* helper overloads used by the synthesized await-return call. |
| src/coreclr/nativeaot/System.Private.CoreLib/src/System.Private.CoreLib.csproj | Removes runtime-async=on features injection (and normalizes leading BOM/whitespace). |
| src/coreclr/jit/importercalls.cpp | Adjusts async call setup for async-version inlining/tail-await semantics. |
| src/coreclr/jit/importer.cpp | Adds async-version recognition, tail-await flagging, and return-value await wrapping via getAwaitReturnCall. |
| src/coreclr/jit/ICorJitInfo_wrapper_generated.hpp | Adds wrapper for getAwaitReturnCall. |
| src/coreclr/jit/ICorJitInfo_names_generated.h | Adds API name entry for getAwaitReturnCall. |
| src/coreclr/jit/compiler.h | Adds PREFIX_IS_ASYNC_VERSION_TAIL_AWAIT, impWrapTopOfStackInAwait, and compIsAsyncVersion. |
| src/coreclr/jit/compiler.cpp | Prints diagnostics when compiling an async-version method. |
| src/coreclr/interpreter/compiler.h | Adds async-version tracking and await-wrapping helpers to interpreter compiler. |
| src/coreclr/interpreter/compiler.cpp | Implements async-version return wrapping and related peephole recognition in interpreter path. |
| src/coreclr/inc/jiteeversionguid.h | Updates JIT/EE interface version GUID for the new interface method. |
| src/coreclr/inc/icorjitinfoimpl_generated.h | Adds getAwaitReturnCall to the EE’s generated ICorJitInfo implementation surface. |
| src/coreclr/inc/corinfo.h | Adds CORINFO_ASYNC_VERSION and the getAwaitReturnCall interface method. |
| eng/testing/tests.targets | Removes conditional runtime-async=on feature injection for test builds. |
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>
19a8732 to
e881f61
Compare
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
|
/azp run runtime-coreclr crossgen2 outerloop |
|
Azure Pipelines successfully started running 1 pipeline(s). |
jakobbotsch
left a comment
There was a problem hiding this comment.
LGTM! Thanks for helping on this.
…ter/async-crossgen2-codegen-v2
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>
|
/azp run runtime-coreclr crossgen2 outerloop |
|
Azure Pipelines successfully started running 1 pipeline(s). |
|
This test should also be reenabled: runtime/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCases/R2RTestSuites.cs Lines 1178 to 1185 in 92a20b9 |
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>
Builds off #128384 to add crossgen2 support. I just want to validate the work in CI for now.