JIT: Intrinsify Task/ValueTask factory methods#129810
Draft
jakobbotsch wants to merge 1 commit into
Draft
Conversation
Intrinsify and recognize the following methods when used directly in runtime async variants: - `Task.FromResult` - `Task.CompletedTask` - `ValueTask.FromResult` - `ValueTask.CompletedTask` - `new ValueTask()` - `ValueTask`-typed `default` - `new ValueTask<T>(T value)`
Contributor
|
Tagging subscribers to this area: @JulieLeeMSFT, @jakobbotsch |
Contributor
There was a problem hiding this comment.
Pull request overview
This PR extends the CoreCLR JIT’s “runtime async” optimization pipeline by marking common Task/ValueTask factory helpers as intrinsics and teaching the importer to fold/avoid AsyncHelpers.TransparentAwaitWithResult in more cases, reducing call/alloc overhead in optimized async-return paths.
Changes:
- Mark
Task.CompletedTask,Task.FromResult,ValueTask.CompletedTask,ValueTask.FromResult, andValueTask<TResult>(TResult)with[Intrinsic]to enable JIT recognition. - Add new JIT
NamedIntrinsicIDs plus importer expansion logic for async-callconvFromResult/CompletedTask, and add a newimpFoldAwaitedTopOfStackfast-path for certain ValueTask return patterns. - Improve debug local naming by labeling
ASYNC_CONTINUATION_ILNUMlocals asAsyncCont.
Reviewed changes
Copilot reviewed 7 out of 7 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
| src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/ValueTask.cs | Adds [Intrinsic] to ValueTask.CompletedTask, ValueTask.FromResult, and ValueTask<TResult>(TResult) ctor to enable JIT recognition. |
| src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/Task.cs | Adds [Intrinsic] to Task.CompletedTask getter and Task.FromResult to enable async-callconv intrinsic expansion. |
| src/coreclr/jit/namedintrinsiclist.h | Introduces new NamedIntrinsic entries for Task/ValueTask factory methods used by the importer. |
| src/coreclr/jit/importercalls.cpp | Implements async-callconv intrinsic expansion/folding for FromResult/CompletedTask, and extends intrinsic lookup for Task/ValueTask methods. |
| src/coreclr/jit/importer.cpp | Refactors return/await wrapping logic and adds impFoldAwaitedTopOfStack to avoid unnecessary TransparentAwaitWithResult calls. |
| src/coreclr/jit/gentree.cpp | Improves local name printing by labeling async continuation locals as AsyncCont. |
| src/coreclr/jit/compiler.h | Adds the impFoldAwaitedTopOfStack declaration. |
Comment on lines
+11796
to
+11798
| // Returns: | ||
| // True if the top of stack was folded and pushed on the stack. | ||
| // |
| impPushOnStack(gtNewLclVarNode(returnLcl), typeInfo(TYP_STRUCT)); | ||
| } | ||
|
|
||
| JITDUMP("Optimized \"return new ValueTask() to return default\n"); |
|
|
||
| StackEntry se = impPopStack(); | ||
| impPushOnStack(value, se.seTypeInfo); | ||
| JITDUMP("Optimized \"return new ValueTask(value) to return value directly:\n"); |
Comment on lines
+11846
to
+11863
| impPopStack(); | ||
| if (info.compRetType == TYP_VOID) | ||
| { | ||
| lastTree->gtBashToNOP(); | ||
| } | ||
| else if (info.compRetType != TYP_STRUCT) | ||
| { | ||
| impPushOnStack(gtNewZeroConNode(info.compRetType), typeInfo(info.compRetType)); | ||
| lastTree->gtBashToNOP(); | ||
| } | ||
| else | ||
| { | ||
| unsigned returnLcl = lvaGrabTemp(true DEBUGARG("Return temp")); | ||
| lvaSetStruct(returnLcl, info.compMethodInfo->args.retTypeClass, false); | ||
| // Reuse the ValueTask zeroing | ||
| lastTree->AsLclVar()->SetLclNum(returnLcl); | ||
| impPushOnStack(gtNewLclVarNode(returnLcl), typeInfo(TYP_STRUCT)); | ||
| } |
Comment on lines
+5099
to
+5109
| case NI_System_Threading_Tasks_Task_FromResult: | ||
| case NI_System_Threading_Tasks_ValueTask_FromResult: | ||
| { | ||
| if ((sig->callConv & CORINFO_CALLCONV_ASYNCCALL) == 0) | ||
| { | ||
| break; | ||
| } | ||
|
|
||
| CORINFO_CLASS_HANDLE typeHnd = sig->sigInst.methInst[0]; | ||
| ClassLayout* layout = nullptr; | ||
| var_types type = TypeHandleToVarType(typeHnd, &layout); |
Comment on lines
+11669
to
+11683
| else if (strcmp(className, "ValueTask") == 0) | ||
| { | ||
| if (strcmp(methodName, "FromResult") == 0) | ||
| { | ||
| result = NI_System_Threading_Tasks_ValueTask_FromResult; | ||
| } | ||
| else if (strcmp(methodName, "get_CompletedTask") == 0) | ||
| { | ||
| result = NI_System_Threading_Tasks_ValueTask_get_CompletedTask; | ||
| } | ||
| else if (strcmp(methodName, ".ctor") == 0) | ||
| { | ||
| result = NI_System_Threading_Tasks_ValueTask__ctor; | ||
| } | ||
| } |
This was referenced Jun 25, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Intrinsify and recognize the following methods when used directly in runtime async context:
Task.FromResultTask.CompletedTaskValueTask.FromResultValueTask.CompletedTasknew ValueTask()ValueTask-typeddefaultnew ValueTask<T>(T value)Example
Before:
After: