-
Notifications
You must be signed in to change notification settings - Fork 5.3k
Add AsyncHelpers.SetAsyncCallContinuationArg intrinsic #121506
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
|
Tagging subscribers to this area: @JulieLeeMSFT, @jakobbotsch |
|
Can you switch CoreCLR to use this intrinsic to validate that it actually works? |
There was a problem hiding this 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 adds a new SetAsyncCallContinuationArg intrinsic to support more efficient async resumption stubs in crossgen and ILC. The intrinsic allows resumption stubs to emit regular calls to async methods instead of using indirect calls (ldftn+calli or ldc+calli) by storing the continuation argument in a local variable that gets passed as a hidden parameter.
Key changes:
- Adds the
SetAsyncCallContinuationArgintrinsic to the JIT's intrinsic catalog - Implements JIT support to store continuation arguments in a temporary variable and pass them to async calls
- Updates the async liveness analysis to exclude the new temporary variable from capture requirements
Reviewed Changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| src/coreclr/jit/namedintrinsiclist.h | Adds enum entry for the new intrinsic |
| src/coreclr/jit/importercalls.cpp | Implements intrinsic expansion and modifies async call argument passing to use stored continuation |
| src/coreclr/jit/compiler.h | Declares the compiler field for tracking the continuation argument variable |
| src/coreclr/jit/async.cpp | Excludes the continuation argument variable from async capture |
| src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/AsyncHelpers.CoreCLR.cs | Defines the new intrinsic method and changes AsyncCallContinuation visibility from private to internal |
| GenTree* arg = lvaNextAsyncCallContArgVar != BAD_VAR_NUM | ||
| ? (GenTree*)gtNewLclVarNode(lvaNextAsyncCallContArgVar) | ||
| : gtNewNull(); |
Copilot
AI
Nov 10, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The conditional logic to determine the async continuation argument is duplicated (appears twice at lines 878-880 and 897-899). Consider extracting this logic into a helper method like GetAsyncContinuationArg() to avoid duplication and improve maintainability.
src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/AsyncHelpers.CoreCLR.cs
Show resolved
Hide resolved
…ter/runtime into SetAsyncCallContinuation
| pCode->EmitLDIND_I(); | ||
| } | ||
|
|
||
| pCode->EmitCALLI(pCode->GetSigToken(calliSig.GetRawSig(), calliSig.GetRawSigLen()), numArgs, msig.IsReturnTypeVoid() ? 0 : 1); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
calliSig can be deleted.
| if (ncv.GetOptimizationTier() == NativeCodeVersion::OptimizationTier1OSR) | ||
| { | ||
| #ifdef FEATURE_ON_STACK_REPLACEMENT | ||
| // The OSR version needs to resume in the tier0 version. The tier0 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This special-case needs to move to GetCallInfo or getFunctionEntryPoint
… is a resumption stub and the callee is async.
|
This PR seems superseded by #121799 |
Adds a SetAsyncCallContinuationArg intrinsic to be used in async resumption stubs created by crossgen and ilc. This would enable the resumption stubs to emit a regular call to the async method rather than ldftn+calli or ldc+calli. The runtime stashes the continuation to be passed as a hidden parameter to the next async call.