JIT: Refactor async-to-sync call optimization#124798
JIT: Refactor async-to-sync call optimization#124798jakobbotsch wants to merge 11 commits intodotnet:mainfrom
Conversation
- Introduce `getAsyncOtherVariant` JIT-EE API that returns the other async variant of an async/task-returning method - Remove the `AwaitVirtual` token kind that was used to allow the VM to tell the JIT to prefer a task-returning instead of async variant - Implement the same optimization on the JIT side instead using the `getAsyncOtherVariant`, when we notice that a call will be direct Fix dotnet#124545
|
Tagging subscribers to this area: @JulieLeeMSFT, @jakobbotsch |
There was a problem hiding this comment.
Pull request overview
Refactors the async-to-sync call optimization by removing VM-driven “prefer task-returning for direct calls” token handling and moving the decision logic into the JIT using a new JIT↔EE API (getAsyncOtherVariant).
Changes:
- Add
ICorJitInfo::getAsyncOtherVariantAPI and plumb it through VM, JIT wrappers, NativeAOT JIT interface, and SuperPMI record/replay. - Remove
CORINFO_TOKENKIND_AwaitVirtualand simplify await token resolution to always request the async variant from the VM. - Teach the JIT importer to switch back to the synchronous Task-returning call for direct calls when the async variant is a thunk.
Reviewed changes
Copilot reviewed 24 out of 24 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
| src/coreclr/vm/jitinterface.cpp | Removes AwaitVirtual-specific resolution logic; adds EE implementation of getAsyncOtherVariant. |
| src/coreclr/jit/importer.cpp | Implements JIT-side “avoid thunk for direct calls” logic using getAsyncOtherVariant. |
| src/coreclr/jit/ee_il_dll.hpp | Replaces combine(...) helper with CORINFO_CALLINFO_FLAGS `operator |
| src/coreclr/inc/corinfo.h | Removes CORINFO_TOKENKIND_AwaitVirtual; adds ICorStaticInfo::getAsyncOtherVariant. |
| src/coreclr/inc/icorjitinfoimpl_generated.h | Declares the new EE-side virtual method override. |
| src/coreclr/inc/jiteeversionguid.h | Updates the JIT/EE interface version GUID for the API shape change. |
| src/coreclr/jit/ICorJitInfo_wrapper_generated.hpp | Adds wrapper forwarding for getAsyncOtherVariant. |
| src/coreclr/jit/ICorJitInfo_names_generated.h | Adds name for the new API. |
| src/coreclr/tools/superpmi/superpmi/icorjitinfo.cpp | Adds SuperPMI ICJI forwarding/recording hook. |
| src/coreclr/tools/superpmi/superpmi-shim-simple/icorjitinfo_generated.cpp | Adds shim forwarding for the new API. |
| src/coreclr/tools/superpmi/superpmi-shim-counter/icorjitinfo_generated.cpp | Adds shim call counting + forwarding for the new API. |
| src/coreclr/tools/superpmi/superpmi-shim-collector/icorjitinfo.cpp | Records/replays getAsyncOtherVariant including variantIsThunk. |
| src/coreclr/tools/superpmi/superpmi-shared/methodcontext.h | Adds record/replay declarations + packet ID for getAsyncOtherVariant. |
| src/coreclr/tools/superpmi/superpmi-shared/methodcontext.cpp | Implements record/replay storage for getAsyncOtherVariant. |
| src/coreclr/tools/superpmi/superpmi-shared/lwmlist.h | Adds LWM map entry for GetAsyncOtherVariant. |
| src/coreclr/tools/Common/JitInterface/ThunkGenerator/ThunkInput.txt | Adds new API to thunk generator inputs. |
| src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs | Removes CORINFO_TOKENKIND_AwaitVirtual from managed enum mirror. |
| src/coreclr/tools/Common/JitInterface/CorInfoImpl_generated.cs | Adds unmanaged callback plumbing for getAsyncOtherVariant. |
| src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs | Updates await-token handling; adds (currently stubbed) getAsyncOtherVariant managed implementation. |
| src/coreclr/tools/aot/jitinterface/jitinterface_generated.h | Adds callback pointer + wrapper method for getAsyncOtherVariant. |
| src/coreclr/tools/aot/ILCompiler.Compiler/IL/ILImporter.Scanner.cs | Removes NativeAOT scanner-side “avoid thunk for direct calls” logic. |
| src/coreclr/interpreter/compiler.cpp | Removes AwaitVirtual usage in interpreter async-call peep token resolution. |
| src/coreclr/tools/aot/ilc.slnx | Adds ILLink.CodeFixProvider project to the solution. |
| src/coreclr/tools/aot/crossgen2.slnx | Adds ILLink.CodeFixProvider project to the solution. |
janvorli
left a comment
There was a problem hiding this comment.
LGTM (I haven't looked at the JIT changes), I have just few nits.
| // non-async task-returning call. There is no reason to create | ||
| // and go through the thunk. | ||
| ResolveToken(token, CORINFO_TOKENKIND_Method, &resolvedCallToken); | ||
| m_ip = origIP + 5; |
There was a problem hiding this comment.
It seems we don't need to save the origIP as at this point the m_ip would always be the same as origIP, unless I am mising something.
There was a problem hiding this comment.
m_ip gets changed by AsyncCallPeeps.FindAndApplyPeep above -- this is undoing the change when we decide to not actually dispatch through the async call. Let me add a comment.
getAsyncOtherVariantJIT-EE API that returns the other async variant of an async/task-returning methodAwaitVirtualtoken kind that was used to allow the VM to tell the JIT to prefer a task-returning instead of async variantgetAsyncOtherVariant, when we notice that a call will be directActiveIssueasync EH tests under interpreter against Crash inToString_Asynctest on Apple mobile with CoreCLR R2R and interpreter #124044Fix #124545