Add runtime async support for saving and reusing continuation instances#125556
Add runtime async support for saving and reusing continuation instances#125556jakobbotsch wants to merge 20 commits intodotnet:mainfrom
Conversation
This reverts commit 8e54df1.
There was a problem hiding this comment.
Pull request overview
This PR introduces an optimization for CoreCLR “runtime async” methods by enabling a single shared continuation layout per async method and reusing the same continuation instance across multiple suspension points, reducing allocations and GC pressure. It updates the continuation flags contract to encode per-suspension-point field offsets (notably for return storage) in Continuation.Flags.
Changes:
- JIT: Build shared continuation layouts across all suspension points and optionally reuse continuation instances (
JitAsyncReuseContinuations). - ABI/contract: Redefine continuation flags to encode exception/context/result slot indices via bitfields.
- BCL: Update
AsyncHelperscontinuation field accessors to decode indices from flags.
Reviewed changes
Copilot reviewed 9 out of 9 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| src/coreclr/vm/object.h | Adjusts continuation object helpers used by the runtime/interpreter to locate data/exception storage. |
| src/coreclr/vm/interpexec.cpp | Updates interpreter suspend/resume handling to use new continuation flag semantics. |
| src/coreclr/jit/jitstd/vector.h | Adds data() const overload needed by new JIT code. |
| src/coreclr/jit/jitconfigvalues.h | Adds JitAsyncReuseContinuations config switch. |
| src/coreclr/jit/async.h | Refactors async transformation to build per-state layouts and shared layout support types. |
| src/coreclr/jit/async.cpp | Implements shared layout creation, continuation reuse logic, and index encoding into flags. |
| src/coreclr/interpreter/compiler.cpp | Updates interpreter continuation layout/flags creation for new encoding scheme. |
| src/coreclr/inc/corinfo.h | Redefines CorInfoContinuationFlags to include index bitfield definitions. |
| src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/AsyncHelpers.CoreCLR.cs | Updates managed Continuation to decode exception/context/result locations from flags. |
Comments suppressed due to low confidence (1)
src/coreclr/vm/interpexec.cpp:4430
- Exception handling on interpreter resumption is gated on a placeholder flag literal (567), and GetExceptionObjectStorage currently returns the wrong slot. This will skip exception rethrow or read garbage. Update to the new index-based encoding (compare decoded exception index against the sentinel mask) and use the decoded offset when loading the exception object.
if (pAsyncSuspendData->flags & /*CORINFO_CONTINUATION_HAS_EXCEPTION */ 567)
{
// Throw exception if needed
OBJECTREF exception = *continuation->GetExceptionObjectStorage();
if (exception != NULL)
{
src/coreclr/interpreter/compiler.cpp
Outdated
| @@ -5949,7 +5949,7 @@ void InterpCompiler::EmitSuspend(const CORINFO_CALL_INFO &callInfo, Continuation | |||
|
|
|||
| if (needsEHHandling) | |||
| { | |||
| flags |= CORINFO_CONTINUATION_HAS_EXCEPTION; | |||
| //flags |= CORINFO_CONTINUATION_HAS_EXCEPTION; | |||
| } | |||
|
|
|||
| suspendData->flags = (CorInfoContinuationFlags)flags; | |||
There was a problem hiding this comment.
Pull request overview
This PR updates the runtime async continuation model to support a single shared continuation layout across suspension points and (optionally) reuse a single continuation instance to reduce allocations/GC pressure.
Changes:
- Reworks continuation
Flagsto encode slot indices (exception/context/result) instead of simple presence bits, and updates VM + BCL consumers accordingly. - Introduces JIT infrastructure to build per-suspension sub-layouts, optionally merge them into a shared layout, and enable continuation reuse via a new config knob.
- Updates interpreter suspension metadata generation to use the new index-encoding scheme.
Reviewed changes
Copilot reviewed 9 out of 9 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| src/coreclr/vm/object.h | Computes result/exception storage addresses using index fields encoded in Flags. |
| src/coreclr/vm/interpexec.cpp | Decodes continuation-context index from flags and uses new exception-storage accessor. |
| src/coreclr/jit/jitstd/vector.h | Adds data() const to support const access patterns. |
| src/coreclr/jit/jitconfigvalues.h | Adds JitAsyncReuseContinuations config (default enabled). |
| src/coreclr/jit/async.h | Adds layout builder/state tracking types and shared-layout support plumbing. |
| src/coreclr/jit/async.cpp | Implements shared-layout creation, per-state suspend/resume emission, and continuation reuse logic. |
| src/coreclr/interpreter/compiler.cpp | Encodes exception/context/result indices into interpreter async suspend flags. |
| src/coreclr/inc/corinfo.h | Redefines CorInfoContinuationFlags to include index bitfields for slots. |
| src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/AsyncHelpers.CoreCLR.cs | Updates managed continuation flag decoding and storage offset computation to match new encoding. |
There was a problem hiding this comment.
Pull request overview
This PR updates CoreCLR runtime-async to support reusing a single continuation instance per async method by introducing a shared continuation layout across all suspension points and encoding per-suspension field offsets (notably return storage) into Continuation.Flags.
Changes:
- Add
JitAsyncReuseContinuationsconfig and JIT support for shared continuation layouts plus continuation instance reuse. - Replace “HAS_*” continuation flags with bitfield-encoded indices for exception/context/result storage, updating VM, JIT, interpreter, and CoreLib consumers.
- Update interpreter suspend/resume plumbing to use the new index encoding when locating continuation fields.
Reviewed changes
Copilot reviewed 8 out of 8 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| src/coreclr/vm/object.h | Switch continuation field address computations to index-based decoding from Flags. |
| src/coreclr/vm/interpexec.cpp | Update interpreter continuation-context and exception handling to use encoded indices. |
| src/coreclr/jit/jitconfigvalues.h | Add JitAsyncReuseContinuations release config knob (default enabled). |
| src/coreclr/jit/async.h | Extend async transformation APIs to support shared layouts and reuse plumbing. |
| src/coreclr/jit/async.cpp | Implement shared layout unioning, reuse control-flow, and encode offsets into Flags. |
| src/coreclr/interpreter/compiler.cpp | Encode exception/context/result indices into suspend-data flags for interpreter async. |
| src/coreclr/inc/corinfo.h | Redefine continuation flags: keep scheduling bits + add index bitfield definitions. |
| src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/AsyncHelpers.CoreCLR.cs | Decode indices from Flags to locate exception/context/result storage; update exception checks. |
There was a problem hiding this comment.
Pull request overview
This PR enables runtime async methods to allocate a single continuation instance and reuse it across multiple suspensions by introducing a shared continuation layout that is compatible with all suspension points. It also updates the continuation Flags contract to encode per-suspension offsets (via indices) for exception/context/result storage, allowing resumptions to locate the correct storage even when the shared layout contains multiple potential return slots.
Changes:
- Introduces shared continuation layout creation (union of locals/optional fields/return slots) to support continuation reuse.
- Reworks continuation
FlagsfromHAS_*presence bits to encoded slot indices for exception/context/result offsets. - Adds a JIT config knob (
JitAsyncReuseContinuations) and updates VM/interpreter/BCL consumers to the new encoding.
Reviewed changes
Copilot reviewed 8 out of 8 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| src/coreclr/vm/object.h | Updates continuation field accessors to decode exception/result storage offsets from encoded indices in Flags. |
| src/coreclr/vm/interpexec.cpp | Adjusts interpreter continuation context handling and exception probing to use encoded index offsets. |
| src/coreclr/jit/jitconfigvalues.h | Adds JitAsyncReuseContinuations configuration switch (default enabled). |
| src/coreclr/jit/async.h | Extends async transformation APIs to support shared layouts and reuse-related state. |
| src/coreclr/jit/async.cpp | Implements shared layout merging, index encoding in flags, and continuation reuse control flow in suspensions/resumptions. |
| src/coreclr/interpreter/compiler.cpp | Encodes exception/context/result indices into suspend flags for interpreter-generated continuations. |
| src/coreclr/inc/corinfo.h | Redefines CorInfoContinuationFlags to include index bitfields instead of HAS_* bits. |
| src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/AsyncHelpers.CoreCLR.cs | Updates managed continuation flag decoding and storage offset computations to match the new index-based encoding. |
This adds support for generating a single shared continuation layout for each runtime async method. The shared continuation layout is compatible with the state that needs to be stored for all suspension points in the function. For that reason it uses more memory than the previous separated continuation layouts.
The benefit is that once a single layout is compatible with all suspension points we can reuse the same continuation instance every time we suspend. That means a single runtime async function only ends up allocating one continuation instance.
On suspension heavy benchmarks this improves performance by about 15% by significantly reducing the amount of garbage generated.
A complication arises for return values. Before this change the continuation object always stored its single possible return value in a known location, and resumption stubs would propagate return values into the caller's continuation at that location. With this change the continuation stores space for all possible types of return values, and the offset to store at changes for every suspension point. To handle that we now encode the offset in
Continuation.Flags.Based on #125497