Skip to content

Conversation

@MichalStrehovsky
Copy link
Member

@MichalStrehovsky MichalStrehovsky commented Nov 5, 2025

The same program as in #121295 still works, but we can newly report async helpers as Intrinsic/Async.

This implements things around suspension/resumption. Most of this change is around handling continuation types.

Continuation types are synthetic types (created in the compiler) that derive from Continuation in the CoreLib. The JIT requests these based on the shape of locals it needs to preserve. What we get from the JIT in CorInfoImpl is size of the type and a pointer map (1011001 - non-zero means "GC pointer is here"). What we need to generate is a MethodTable for a type that derives from Continuation and has the specified GC layout after fields inherited from Continuation.

We already have a similar thing in the compiler ("MethodTable" we use for GC statics), so we're reusing the same approach. The MethodTable is pretty restricted and not the whole MethodTable API surface on it is available at runtime. However, it implements enough that the GC can operate on it.

The two new types are AsyncContinuationType. This is a type system MetadataType that represents the continuation pointer map. Then we have AsyncContinuationEETypeNode which is the thing that gets emitted into the output file - the MethodTable itself.

The resumption stub is just a stubbed out TODO for someone else to look into for now.

Cc @dotnet/ilc-contrib

The same program as in dotnet#121295 still works, but we can newly report async helpers as Intrinsic/Async.

This implements things around suspension/resumption. Most of this change is around handling continuation types.

Continuation types are synthetic types (created in the compiler) that derive from `Continuation` in the CoreLib. The JIT requests these based on the shape of locals it needs to preserve. What we get from the JIT in CorInfoImpl is size of the type and a pointer map (1011001 - non-zero means "GC pointer is here"). What we need to generate is a `MethodTable` for a type that derives from `Continuation` and has the specified GC layout after fields inherited from `Continuation`.

We already have a similar thing in the compiler ("`MethodTable`" we use for GC statics), however because we need to derive from `Continuation` and presumably need to have a working vtable (with Equals/GetHashCode/ToString), we can't use this. So we emit a normal `MethodTable` for a synthetic type. Maybe we could optimize this to the one without vtable in the future.

The synthetic type is a `MetadataType` that reports `Continuation` as the base type. It has instance fields of type `object` or `nint`, based on what we need. Because the standard type layout algorithm (the thing that assigns offsets to fields) would attempt to group GC fields together (it's a layout that works better for the GC), we also have a custom layouting algorithm that lays out these fields sequentially.

The resumption stub is just a stubbed out TODO for someone else to look into for now.
@dotnet-policy-service
Copy link
Contributor

Tagging subscribers to this area: @agocke, @MichalStrehovsky, @jkotas
See info in area-owners.md if you want to be subscribed.

Copy link
Contributor

Copilot AI left a 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 support for async/await functionality in NativeAOT by implementing continuation type handling and async resumption stubs. The changes enable the JIT compiler to properly handle async methods in ahead-of-time compilation scenarios.

  • Extends async support from CoreCLR to NativeAOT by unifying the preprocessor directives
  • Implements continuation type generation with GC pointer mapping for async state storage
  • Adds async resumption stub infrastructure for handling async method suspension/resumption

Reviewed Changes

Copilot reviewed 12 out of 12 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
AsyncHelpers.cs Extends conditional compilation to include NativeAOT alongside CoreCLR and removes duplicate NATIVEAOT-specific implementations
AsyncHelpers.CoreCLR.cs Adds NativeAOT support for continuation allocation using RuntimeImports.RhNewObject
CorInfoImpl.RyuJit.cs Implements continuation allocation helper and fixed entry point retrieval for async methods
CorInfoImpl.ReadyToRun.cs Adds placeholder for getFunctionFixedEntryPoint in ReadyToRun compilation
ILCompiler.Compiler.csproj Adds new AsyncResumptionStub source files to the build
CompilerTypeSystemContext.Aot.cs Registers continuation type field layout algorithm and hashtable
AsyncResumptionStub.cs Defines async resumption stub method that throws NotSupportedException as placeholder
AsyncResumptionStub.Sorting.cs Implements sorting and comparison for AsyncResumptionStub
AsyncResumptionStub.Mangling.cs Defines name mangling for async resumption stubs
CorInfoImpl.cs Implements getContinuationType and getAsyncResumptionStub for NativeAOT
CompilerTypeSystemContext.Async.cs Implements ContinuationType and ContinuationTypeFieldLayoutAlgorithm for async state storage
System.Private.CoreLib.csproj Includes AsyncHelpers.CoreCLR.cs in NativeAOT build

@agocke
Copy link
Member

agocke commented Nov 5, 2025

Can we add a design doc for this to BotR? (Or, better yet, can copilot add it 😄)

@jkotas
Copy link
Member

jkotas commented Nov 5, 2025

presumably need to have a working vtable (with Equals/GetHashCode/ToString)

Nothing in the runtime should depend a working vtable for these. I think it would be fine to start without a working vtable for these in NAOT and see whether we run into problems.

For CoreCLR, a working vtable should be only a problem for managed debuggers. Continuation types will need some special-casing in managed debuggers since they do not have metadata token and there is no reasonable way to fix that. Dropping vtable may require some more special-casing.

cc @rcj1

@VSadov
Copy link
Member

VSadov commented Nov 5, 2025

For CoreCLR, a working vtable should be only a problem for managed debuggers.

Right. One thing I noticed that before the switch to "flat" continuations the infrastructure code was mostly debuggable in VS. Now VS just crashes. I do not know for sure, but strongly suspect it happens once it sees a continuation instance and digs into its type.
The managed infrastructure code is now in a printf-debuggable state, so it is a bit of a nuisance.

I kind of think, perhaps it is good if these are closer to real types where possible, unless it makes good savings.

@VSadov
Copy link
Member

VSadov commented Nov 5, 2025

I wonder if the APIs that debuggers* use could just report that it has the base Continuation type, or something like that?

--
*By debuggers I mean currently shipping debuggers here.

@jkotas
Copy link
Member

jkotas commented Nov 5, 2025

I kind of think, perhaps it is good if these are closer to real types where possible, unless it makes good savings.

Real types have metadata tokens and metadata. I do not see a reasonable way to create metadata tokens for these.

If these types do not have metadata tokens, they are not real types and they will need to be special-cased in the diagnostic tooling.

the APIs that debuggers* use could just report that it has the base Continuation type

It may be a good strategy for special-casing in some places, but it will be still problematic for code that expects 1:1 mapping between types and metadata tokens (for non-generic types).

@jkotas
Copy link
Member

jkotas commented Nov 5, 2025

Can we add a design doc for this to BotR?

We should have a general runtime implementation design doc for runtime async (not specific to NAOT). @VSadov Do we have one?

@VSadov
Copy link
Member

VSadov commented Nov 5, 2025

Can we add a design doc for this to BotR?

We should have a general runtime implementation design doc for runtime async (not specific to NAOT). @VSadov Do we have one?

We have specs for the public surface:
https://github.com/dotnet/runtime/blob/main/docs/design/specs/runtime-async.md

BOTR has the ABI spec:

But no general implementation design document. Mostly because the design was changing.

We had a document that touches on design highlights in the runtimelab although that could be a bit obsolete vs. the current state. It makes sense to bring that part over as a starting point. (after adjusting for the current state).

I will look into that.

@MichalStrehovsky
Copy link
Member Author

I've switched this over to use the barebone MethodTable approach. The top-post is updated.

Copy link
Member

@jkotas jkotas left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants