Skip to content

Conversation

@eduardo-vp
Copy link
Member

@eduardo-vp eduardo-vp commented Nov 7, 2025

Updated EmitTaskReturningThunk in Native AOT to match EmitTaskReturningThunk in CoreCLR.

@MichalStrehovsky
Copy link
Member

MichalStrehovsky commented Nov 7, 2025

The IL generated in CoreCLR calls methods that are not available in Native AOT, need to figure out how to handle those scenarios.

I added AsyncHelpers.CoreCLR.cs to native AOT corelib in #121398 so we should have those now. Just do a merge from main!

@MichalStrehovsky
Copy link
Member

Once you have this, you should be able to observe this program (same one I pointed you to, but with added Task.Delay) print two numbers, freeze for a second and then crash with an unhandled exception since we need the resumption thunk to continue.

using System;
using System.Threading.Tasks;

public class Async2Void
{
    public static void Main()
    {
        var t = AsyncTestEntryPoint(123, 456);
        t.Wait();
        Console.WriteLine(t.Result);
    }

    private static async Task<int> AsyncTestEntryPoint(int x, int y)
    {
        int result = await OtherAsync(x, y);
        return result;
    }

    private static async Task<int> OtherAsync(int x, int y)
    {
        Console.WriteLine(x);
        Console.WriteLine(y);

        await Task.Delay(1000);

        return x + y;
    }
}

@eduardo-vp
Copy link
Member Author

eduardo-vp commented Nov 11, 2025

Once you have this, you should be able to observe this program (same one I pointed you to, but with added Task.Delay) print two numbers, freeze for a second and then crash with an unhandled exception since we need the resumption thunk to continue.

Unfortunately the program prints the numbers and crashes but doesn't freeze, I'm looking into that.

@eduardo-vp eduardo-vp marked this pull request as ready for review November 11, 2025 00:47
Copilot AI review requested due to automatic review settings November 11, 2025 00:47
Copilot finished reviewing on behalf of eduardo-vp November 11, 2025 00:54
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 updates the EmitTaskReturningThunk method in the Native AOT type system to match the CoreCLR VM implementation. The goal is to generate consistent IL code for async thunk methods across both runtimes.

Key changes:

  • Added support for catch exception regions in the IL emitter infrastructure
  • Implemented full task-returning thunk logic with nested try-catch-finally blocks
  • Added sorting of exception regions to meet ECMA-335 spec requirements
  • Created stub for AsyncCallContinuation intrinsic in NativeAOT

Reviewed Changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
src/coreclr/vm/asyncthunks.cpp Updated comments to clarify that the code should match the managed type system implementation
src/coreclr/tools/Common/TypeSystem/IL/Stubs/ILEmitter.cs Added support for catch exception regions, updated exception region infrastructure to handle multiple types, and added sorting logic for proper exception region ordering
src/coreclr/tools/Common/TypeSystem/IL/Stubs/AsyncThunks.cs Implemented comprehensive task-returning thunk logic with proper exception handling, generic instantiation support, and control flow matching CoreCLR
src/coreclr/nativeaot/System.Private.CoreLib/src/System/StubHelpers.NativeAot.cs Added AsyncCallContinuation intrinsic stub for NativeAOT
src/coreclr/nativeaot/System.Private.CoreLib/src/System.Private.CoreLib.csproj Added new StubHelpers.NativeAot.cs file to the project

Comment on lines 165 to 172
.GetKnownType("System.Threading.Tasks"u8, "ValueTask"u8)
.GetKnownMethod("FromException"u8, null);
}
else
{
fromExceptionMd = context.SystemModule
.GetKnownType("System.Threading.Tasks"u8, "Task"u8)
.GetKnownMethod("FromException"u8, null);
Copy link

Copilot AI Nov 11, 2025

Choose a reason for hiding this comment

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

These calls should invoke AsyncHelpers.ValueTaskFromException and AsyncHelpers.TaskFromException instead of ValueTask.FromException and Task.FromException. The CoreCLR implementation (lines 234-247 in asyncthunks.cpp) calls the private TaskFromException and ValueTaskFromException methods from AsyncHelpers, which handle OperationCanceledException specially by setting the task as canceled rather than faulted. The public Task.FromException and ValueTask.FromException methods don't have this special handling.

Suggested change
.GetKnownType("System.Threading.Tasks"u8, "ValueTask"u8)
.GetKnownMethod("FromException"u8, null);
}
else
{
fromExceptionMd = context.SystemModule
.GetKnownType("System.Threading.Tasks"u8, "Task"u8)
.GetKnownMethod("FromException"u8, null);
.GetKnownType("System.Runtime.CompilerServices"u8, "AsyncHelpers"u8)
.GetKnownMethod("ValueTaskFromException"u8, null);
}
else
{
fromExceptionMd = context.SystemModule
.GetKnownType("System.Runtime.CompilerServices"u8, "AsyncHelpers"u8)
.GetKnownMethod("TaskFromException"u8, null);

Copilot uses AI. Check for mistakes.
Copy link
Member

Choose a reason for hiding this comment

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

This copilot feedback looks relevant.

Comment on lines +74 to +80
var inst = new TypeDesc[asyncMethod.OwningType.Instantiation.Length];
for (int i = 0; i < inst.Length; i++)
{
inst[i] = context.GetSignatureVariable(i, false);
}

var instantiatedType = context.GetInstantiatedType((MetadataType)asyncMethod.OwningType, new Instantiation(inst));
Copy link
Member

Choose a reason for hiding this comment

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

Can this use InstantiateAsOpen helper?

Copy link
Member Author

Choose a reason for hiding this comment

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

I think @MichalStrehovsky mentioned that asyncMethod.OwningType may not be a generic definition so we cannot use the helper

Copy link
Member

Choose a reason for hiding this comment

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

Yeah, first thing InstantiateAsOpen currently does is that it asserts the method is not generic; we could have a generic method here:

Debug.Assert(method.IsMethodDefinition && !method.HasInstantiation);

But looking at it and all the callsites, it looks like it only asserts because we didn't have a need to do this with a generic method before and so it wasn't implemented. We could also just extend it to handle generic methods and call it from here.

@eduardo-vp
Copy link
Member Author

Once you have this, you should be able to observe this program (same one I pointed you to, but with added Task.Delay) print two numbers, freeze for a second and then crash with an unhandled exception since we need the resumption thunk to continue.

The program works as expected now.

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.

4 participants