Skip to content

Async instantiating and unboxing thunks#122126

Merged
MichalStrehovsky merged 8 commits intodotnet:mainfrom
MichalStrehovsky:fix121781
Feb 12, 2026
Merged

Async instantiating and unboxing thunks#122126
MichalStrehovsky merged 8 commits intodotnet:mainfrom
MichalStrehovsky:fix121781

Conversation

@MichalStrehovsky
Copy link
Member

@MichalStrehovsky MichalStrehovsky commented Dec 2, 2025

We need to:

  • Compile the unboxing/instantiating thunk as CORJIT_FLAG_ASYNC when the target is AsyncCall
  • Use the TailAwait intrinsic to just return the suspended state from the called method instead of trying to suspend the instantiating thunk too.

Also includes a JIT change from #124175.

Fixes #121781.

Cc @dotnet/ilc-contrib @jakobbotsch

@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.

@VSadov
Copy link
Member

VSadov commented Dec 2, 2025

(Making it AsyncCall complicates things because then we need to assume a pointless resumption thunk for it too and that breaks some of the illusions we're making when compiling these).

See some discussion on this matter in #121971

An instantiating/unboxing thunk for an async method must be an async method because the thunk needs to direct-call (aka await) another async method. Technically the resume stub is not pointless as the thunk may suspend, produce continuation, and later something needs to resume the thunk.
We also need to GC-report the continuation return, if the thunk does return a continuation.

Logically you are correct though. The thunk has no purpose after making the inner call.
(in sync case the thunk may host the return buffer for the callee, but if callee has suspended, it would be unused)

We could have a kind of tail-call-like optimization for the thunks where in case of suspension the thunk just passes the callee continuation up to the caller (and thus removes itself from continuation chain and from resume sequence altogether).
We do not have that right now.

@VSadov
Copy link
Member

VSadov commented Dec 2, 2025

We do not have that right now.

Maybe you can hack it up by emitting something like:

// < do the inner call>

// fetch the callee`s continuation, if any
Continuation c = AsyncCallContinuation();
if (c != null)
{
      // return calee's continuation to the caller.
      AsyncSuspend(c);
      // UNREACHABLE	
}

return result;

This has not been tried, so it is a big YMMV.
Also, I think you would still need to mark the unboxing stub as AsyncCall, but you may get away with not providing a resume.

@dotnet-policy-service
Copy link
Contributor

Draft Pull Request was automatically closed for 30 days of inactivity. Please let us know if you'd like to reopen it.

MichalStrehovsky and others added 7 commits February 10, 2026 14:09
Can be used to instruct the JIT that an upcoming await should not create
an explicit suspension point. This can be used to optimize
instantiating/unboxing stubs and will simplify the NAOT side of the
implementation of those.
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 enables NativeAOT compilation/testing for async instantiating and unboxing thunks by introducing a “tail await” mechanism in the JIT and using it from generated thunks, then unblocking the corresponding async tests.

Changes:

  • Unblocked inst-unbox-thunks async tests on NativeAOT by removing ActiveIssue annotations.
  • Added AsyncHelpers.TailAwait intrinsic and JIT support to mark the next await as a “tail await” (no new suspension point; return callee continuation).
  • Updated NativeAOT thunk IL emission + JIT flags plumbing so instantiating/unboxing thunks can call async targets.

Reviewed changes

Copilot reviewed 13 out of 13 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
src/tests/async/inst-unbox-thunks/inst-unbox-thunks.cs Removes NativeAOT ActiveIssue gates so the scenarios run in CI.
src/coreclr/vm/asyncthunks.cpp Comment update to reference AsyncHelpers.AsyncCallContinuation().
src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/CompilerTypeSystemContext.InterfaceThunks.cs Emits TailAwait before calling async DIM targets; adds helpers to identify DIM instantiation thunks.
src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/CompilerTypeSystemContext.BoxedTypes.cs Emits TailAwait before calling async “special” unboxing thunk targets (generic unboxing thunk path).
src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs Sets CORJIT_FLAG_ASYNC when compiling certain thunk methods whose targets are async calls (NativeAOT path).
src/coreclr/jit/namedintrinsiclist.h Registers the new AsyncHelpers.TailAwait named intrinsic.
src/coreclr/jit/importercalls.cpp Recognizes/imports TailAwait intrinsic and tags the next async call as a tail await.
src/coreclr/jit/gentree.h Adds AsyncCallInfo.IsTailAwait.
src/coreclr/jit/compiler.h Adds m_nextAwaitIsTail state for the importer.
src/coreclr/jit/async.h Adds tail-await transformation helpers and refactors some async-transform helpers to be lazily initialized.
src/coreclr/jit/async.cpp Implements tail-await transformation and refactors shared return/local creation.
src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/AsyncHelpers.CoreCLR.cs Adds AsyncHelpers.TailAwait intrinsic declaration.
docs/design/coreclr/botr/runtime-async-codegen.md Updates doc text to refer to AsyncHelpers.AsyncCallContinuation() instead of StubHelpers.

@MichalStrehovsky MichalStrehovsky marked this pull request as ready for review February 10, 2026 08:36
Copy link
Member

@jakobbotsch jakobbotsch left a comment

Choose a reason for hiding this comment

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

LGTM, but @EgorBo can you PTAL on the JIT parts too?

@jakobbotsch jakobbotsch requested a review from EgorBo February 10, 2026 13:27
@MichalStrehovsky
Copy link
Member Author

/azp run runtime-nativeaot-outerloop

@azure-pipelines
Copy link

Azure Pipelines successfully started running 1 pipeline(s).

@MichalStrehovsky
Copy link
Member Author

/ba-g ICU seems to be missing on an ARM32 machine?

@MichalStrehovsky MichalStrehovsky merged commit 6fbc0b6 into dotnet:main Feb 12, 2026
147 of 157 checks passed
@MichalStrehovsky MichalStrehovsky deleted the fix121781 branch February 12, 2026 06:14
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.

Unboxing+instantiating thunks

5 participants