From f62725f86452eec3ef2b1307215efd9e62c68d25 Mon Sep 17 00:00:00 2001
From: Eduardo Velarde <eduardo.velarde@pucp.pe>
Date: Thu, 6 Nov 2025 19:57:53 -0800
Subject: [PATCH 01/13] Update EmitTaskReturningThunk in Native AOT

---
 .../Common/TypeSystem/IL/Stubs/AsyncThunks.cs | 240 +++++++++++++++++-
 .../Common/TypeSystem/IL/Stubs/ILEmitter.cs   |  28 +-
 src/coreclr/vm/asyncthunks.cpp                |   5 +-
 3 files changed, 259 insertions(+), 14 deletions(-)

diff --git a/src/coreclr/tools/Common/TypeSystem/IL/Stubs/AsyncThunks.cs b/src/coreclr/tools/Common/TypeSystem/IL/Stubs/AsyncThunks.cs
index 0f048327cfdc4d..86e6ebbd143b4e 100644
--- a/src/coreclr/tools/Common/TypeSystem/IL/Stubs/AsyncThunks.cs
+++ b/src/coreclr/tools/Common/TypeSystem/IL/Stubs/AsyncThunks.cs
@@ -1,12 +1,14 @@
 // Licensed to the .NET Foundation under one or more agreements.
 // The .NET Foundation licenses this file to you under the MIT license.
 
+using System;
 using Internal.TypeSystem;
 
 namespace Internal.IL.Stubs
 {
     public static class AsyncThunkILEmitter
     {
+        // The emitted code matches method EmitTaskReturningThunk in CoreCLR VM.
         public static MethodIL EmitTaskReturningThunk(MethodDesc taskReturningMethod, MethodDesc asyncMethod)
         {
             TypeSystemContext context = taskReturningMethod.Context;
@@ -14,24 +16,242 @@ public static MethodIL EmitTaskReturningThunk(MethodDesc taskReturningMethod, Me
             var emitter = new ILEmitter();
             var codestream = emitter.NewCodeStream();
 
-            // TODO: match EmitTaskReturningThunk in CoreCLR VM
-
             MethodSignature sig = asyncMethod.Signature;
-            int numParams = (sig.IsStatic || sig.IsExplicitThis) ? sig.Length : sig.Length + 1;
-            for (int i = 0; i < numParams; i++)
-                codestream.EmitLdArg(i);
+            TypeDesc returnType = sig.ReturnType;
+
+            MetadataType md = taskReturningMethod.Signature.ReturnType as MetadataType;
+            ReadOnlySpan<byte> name = md.Name;
+            bool isValueTask = name.SequenceEqual("ValueTask"u8) || name.SequenceEqual("ValueTask`1"u8);
+
+            TypeDesc logicalReturnType = null;
+            ILLocalVariable logicalResultLocal = 0;
+            if (returnType.HasInstantiation)
+            {
+                // The return type is either Task<T> or ValueTask<T>, exactly one generic argument
+                logicalReturnType = returnType.Instantiation[0];
+                logicalResultLocal = emitter.NewLocal(logicalReturnType);
+            }
+
+            ILLocalVariable returnTaskLocal = emitter.NewLocal(returnType);
+
+            // TODO: Fix this (ExecutionAndSyncBlockStore is not available in Native AOT).
+
+            // TypeDesc executionAndSyncBlockStoreType = context.SystemModule.GetKnownType("System.Runtime.CompilerServices"u8, "ExecutionAndSyncBlockStore"u8);
+            // ILLocalVariable executionAndSyncBlockStoreLocal = emitter.NewLocal(executionAndSyncBlockStoreType);
+
+            ILCodeLabel returnTaskLabel = emitter.NewCodeLabel();
+            ILCodeLabel suspendedLabel = emitter.NewCodeLabel();
+            ILCodeLabel finishedLabel = emitter.NewCodeLabel();
 
-            codestream.Emit(ILOpcode.call, emitter.NewToken(asyncMethod));
+            // codestream.EmitLdLoca(executionAndSyncBlockStoreLocal);
+            // codestream.Emit(ILOpcode.call, emitter.NewToken(executionAndSyncBlockStoreType.GetKnownMethod("Push"u8, null)));
 
-            if (sig.ReturnType.IsVoid)
+            ILExceptionRegionBuilder tryFinallyRegion = emitter.NewFinallyRegion();
             {
-                codestream.Emit(ILOpcode.call, emitter.NewToken(context.SystemModule.GetKnownType("System.Threading.Tasks"u8, "Task"u8).GetKnownMethod("get_CompletedTask"u8, null)));
+                codestream.BeginTry(tryFinallyRegion);
+                codestream.Emit(ILOpcode.nop);
+                ILExceptionRegionBuilder tryCatchRegion = emitter.NewCatchRegion();
+                {
+                    codestream.BeginTry(tryCatchRegion);
+
+                    int localArg = 0;
+                    if (sig.IsExplicitThis)
+                    {
+                        codestream.EmitLdArg(localArg++);
+                    }
+
+                    for (int iArg = 0; iArg < sig.Length; iArg++)
+                    {
+                        codestream.EmitLdArg(localArg++);
+                    }
+
+                    if (asyncMethod.OwningType.HasInstantiation)
+                    {
+                        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));
+                        asyncMethod = context.GetMethodForInstantiatedType(asyncMethod, instantiatedType);
+                    }
+
+                    if (asyncMethod.HasInstantiation)
+                    {
+                        var inst = new TypeDesc[asyncMethod.Instantiation.Length];
+                        for (int i = 0; i < inst.Length; i++)
+                        {
+                            inst[i] = context.GetSignatureVariable(i, true);
+                        }
+                        asyncMethod = asyncMethod.MakeInstantiatedMethod(new Instantiation(inst));
+                    }
+
+                    codestream.Emit(ILOpcode.call, emitter.NewToken(asyncMethod));
+
+                    if (logicalReturnType != null)
+                    {
+                        codestream.EmitStLoc(logicalResultLocal);
+                    }
+
+                    // TODO: Fix this (AsyncCallContinuation is not available in Native AOT).
+
+                    //MethodDesc asyncCallContinuationMd = context.SystemModule
+                    //                            .GetKnownType("System.StubHelpers"u8, "StubHelpers"u8)
+                    //                            .GetKnownMethod("AsyncCallContinuation"u8, null);
+
+                    //codestream.Emit(ILOpcode.call, emitter.NewToken(asyncCallContinuationMd));
+
+                    codestream.Emit(ILOpcode.brfalse, finishedLabel);
+                    codestream.Emit(ILOpcode.leave, suspendedLabel);
+                    codestream.EmitLabel(finishedLabel);
+
+                    if (logicalReturnType != null)
+                    {
+                        codestream.EmitLdLoc(logicalResultLocal);
+
+                        MethodDesc fromResultMethod;
+                        if (isValueTask)
+                        {
+                            fromResultMethod = context.SystemModule
+                                .GetKnownType("System.Threading.Tasks"u8, "ValueTask`1"u8)
+                                .GetKnownMethod("FromResult"u8, null)
+                                .MakeInstantiatedMethod(new Instantiation(logicalReturnType));
+                        }
+                        else
+                        {
+                            fromResultMethod = context.SystemModule
+                                .GetKnownType("System.Threading.Tasks"u8, "Task"u8)
+                                .GetKnownMethod("FromResult"u8, null)
+                                .MakeInstantiatedMethod(new Instantiation(logicalReturnType));
+                        }
+
+                        codestream.Emit(ILOpcode.call, emitter.NewToken(fromResultMethod));
+                    }
+                    else
+                    {
+                        MethodDesc getCompletedTaskMethod;
+                        if (isValueTask)
+                        {
+                            getCompletedTaskMethod = context.SystemModule
+                                .GetKnownType("System.Threading.Tasks"u8, "ValueTask"u8)
+                                .GetKnownMethod("get_CompletedTask"u8, null);
+                        }
+                        else
+                        {
+                            getCompletedTaskMethod = context.SystemModule
+                                .GetKnownType("System.Threading.Tasks"u8, "Task"u8)
+                                .GetKnownMethod("get_CompletedTask"u8, null);
+                        }
+                        codestream.Emit(ILOpcode.call, emitter.NewToken(getCompletedTaskMethod));
+                    }
+
+                    codestream.EmitLdLoc(returnTaskLocal);
+                    codestream.Emit(ILOpcode.leave, returnTaskLabel);
+
+                    codestream.EndTry(tryCatchRegion);
+                }
+                // Catch
+                {
+                    codestream.BeginHandler(tryCatchRegion);
+
+                    MethodDesc fromExceptionMd;
+                    if (logicalReturnType != null)
+                    {
+                        // Generate: returnType.FromException<T>(Exception)
+                        if (isValueTask)
+                        {
+                            fromExceptionMd = context.SystemModule
+                                .GetKnownType("System.Threading.Tasks"u8, "ValueTask`1"u8)
+                                .GetKnownMethod("FromException"u8, null);
+                        }
+                        else
+                        {
+                            fromExceptionMd = context.SystemModule
+                                .GetKnownType("System.Threading.Tasks"u8, "Task`1"u8)
+                                .GetKnownMethod("FromException"u8, null);
+                        }
+                        fromExceptionMd = fromExceptionMd.MakeInstantiatedMethod(new Instantiation(logicalReturnType));
+                    }
+                    else
+                    {
+                        // Generate: returnType.FromException(Exception)
+                        if (isValueTask)
+                        {
+                            fromExceptionMd = context.SystemModule
+                                .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);
+                        }
+                    }
+
+                    codestream.Emit(ILOpcode.call, emitter.NewToken(fromExceptionMd));
+                    codestream.EmitStLoc(returnTaskLocal);
+                    codestream.Emit(ILOpcode.leave, returnTaskLabel);
+                    codestream.EndHandler(tryCatchRegion);
+                }
+
+                codestream.EmitLabel(suspendedLabel);
+
+                // TODO: Fix this (Finalize returning thunks are not available in Native AOT).
+
+                //MethodDesc finalizeTaskReturningThunkMd;
+
+                //if (logicalReturnType != null)
+                //{
+                //    if (isValueTask)
+                //    {
+                //        finalizeTaskReturningThunkMd = context.SystemModule
+                //            .GetKnownType("System.Runtime.CompilerServices"u8, "AsyncHelpers"u8)
+                //            .GetKnownMethod("FinalizeValueTaskReturningThunk`1"u8, null);
+                //    }
+                //    else
+                //    {
+                //        finalizeTaskReturningThunkMd = context.SystemModule
+                //            .GetKnownType("System.Runtime.CompilerServices"u8, "AsyncHelpers"u8)
+                //            .GetKnownMethod("FinalizeTaskReturningThunk`1"u8, null);
+                //    }
+                //    finalizeTaskReturningThunkMd = finalizeTaskReturningThunkMd.MakeInstantiatedMethod(new Instantiation(logicalReturnType));
+                //}
+                //else
+                //{
+                //    if (isValueTask)
+                //    {
+                //        finalizeTaskReturningThunkMd = context.SystemModule
+                //            .GetKnownType("System.Runtime.CompilerServices"u8, "AsyncHelpers"u8)
+                //            .GetKnownMethod("FinalizeValueTaskReturningThunk"u8, null);
+                //    }
+                //    else
+                //    {
+                //        finalizeTaskReturningThunkMd = context.SystemModule
+                //            .GetKnownType("System.Runtime.CompilerServices"u8, "AsyncHelpers"u8)
+                //            .GetKnownMethod("FinalizeTaskReturningThunk"u8, null);
+                //    }
+                //}
+                //codestream.Emit(ILOpcode.call, emitter.NewToken(finalizeTaskReturningThunkMd));
+                codestream.EmitStLoc(returnTaskLocal);
+                codestream.Emit(ILOpcode.leave, returnTaskLabel);
+
+                codestream.EndTry(tryFinallyRegion);
             }
-            else
+            //
             {
-                codestream.Emit(ILOpcode.call, emitter.NewToken(context.SystemModule.GetKnownType("System.Threading.Tasks"u8, "Task"u8).GetKnownMethod("FromResult"u8, null).MakeInstantiatedMethod(sig.ReturnType)));
+                codestream.BeginHandler(tryFinallyRegion);
+
+                // TODO: Fix this (ExecutionAndSyncBlockStore is not available in Native AOT).
+
+                // codestream.EmitLdLoca(executionAndSyncBlockStoreLocal);
+                // codestream.Emit(ILOpcode.call, emitter.NewToken(executionAndSyncBlockStoreType.GetKnownMethod("Pop"u8, null)));
+                codestream.EndHandler(tryFinallyRegion);
             }
 
+            codestream.EmitLabel(returnTaskLabel);
+            codestream.EmitLdLoc(returnTaskLocal);
             codestream.Emit(ILOpcode.ret);
 
             return emitter.Link(taskReturningMethod);
diff --git a/src/coreclr/tools/Common/TypeSystem/IL/Stubs/ILEmitter.cs b/src/coreclr/tools/Common/TypeSystem/IL/Stubs/ILEmitter.cs
index 3625b6397f5d85..bfffd099ee60b3 100644
--- a/src/coreclr/tools/Common/TypeSystem/IL/Stubs/ILEmitter.cs
+++ b/src/coreclr/tools/Common/TypeSystem/IL/Stubs/ILEmitter.cs
@@ -669,6 +669,7 @@ public class ILEmitter
         private ArrayBuilder<ILCodeStream> _codeStreams;
         private ArrayBuilder<LocalVariableDefinition> _locals;
         private ArrayBuilder<object> _tokens;
+        private ArrayBuilder<ILExceptionRegionBuilder> _catchRegions;
         private ArrayBuilder<ILExceptionRegionBuilder> _finallyRegions;
 
         public ILEmitter()
@@ -727,6 +728,14 @@ public ILCodeLabel NewCodeLabel()
             return newLabel;
         }
 
+        // For now, only catches exceptions of type Exception.
+        public ILExceptionRegionBuilder NewCatchRegion()
+        {
+            var region = new ILExceptionRegionBuilder();
+            _catchRegions.Add(region);
+            return region;
+        }
+
         public ILExceptionRegionBuilder NewFinallyRegion()
         {
             var region = new ILExceptionRegionBuilder();
@@ -782,18 +791,33 @@ public MethodIL Link(MethodDesc owningMethod)
 
             ILExceptionRegion[] exceptionRegions = null;
 
-            int numberOfExceptionRegions = _finallyRegions.Count;
+            int numberOfExceptionRegions = _catchRegions.Count + _finallyRegions.Count;
             if (numberOfExceptionRegions > 0)
             {
                 exceptionRegions = new ILExceptionRegion[numberOfExceptionRegions];
 
+                TypeDesc exceptionType = owningMethod.Context.SystemModule.GetKnownType("System"u8, "Exception"u8);
+
+                int exceptionTypeToken = (int)NewToken(exceptionType);
+
+                for (int i = 0; i < _catchRegions.Count; i++)
+                {
+                    ILExceptionRegionBuilder region = _catchRegions[i];
+
+                    Debug.Assert(region.IsDefined);
+
+                    exceptionRegions[i] = new ILExceptionRegion(ILExceptionRegionKind.Catch,
+                        region.TryOffset, region.TryLength, region.HandlerOffset, region.HandlerLength,
+                        classToken: exceptionTypeToken, filterOffset: 0);
+                }
+
                 for (int i = 0; i < _finallyRegions.Count; i++)
                 {
                     ILExceptionRegionBuilder region = _finallyRegions[i];
 
                     Debug.Assert(region.IsDefined);
 
-                    exceptionRegions[i] = new ILExceptionRegion(ILExceptionRegionKind.Finally,
+                    exceptionRegions[_catchRegions.Count + i] = new ILExceptionRegion(ILExceptionRegionKind.Finally,
                         region.TryOffset, region.TryLength, region.HandlerOffset, region.HandlerLength,
                         classToken: 0, filterOffset: 0);
                 }
diff --git a/src/coreclr/vm/asyncthunks.cpp b/src/coreclr/vm/asyncthunks.cpp
index fcb0c06d059936..559d2d60230723 100644
--- a/src/coreclr/vm/asyncthunks.cpp
+++ b/src/coreclr/vm/asyncthunks.cpp
@@ -58,7 +58,8 @@ bool MethodDesc::TryGenerateAsyncThunk(DynamicResolver** resolver, COR_ILMETHOD_
     return true;
 }
 
-// provided an async method, emits a Task-returning wrapper.
+// Provided an async method, emits a Task-returning wrapper.
+// The emitted code matches method EmitTaskReturningThunk in the Managed Type System.
 void MethodDesc::EmitTaskReturningThunk(MethodDesc* pAsyncOtherVariant, MetaSig& thunkMsig, ILStubLinker* pSL)
 {
     _ASSERTE(!pAsyncOtherVariant->IsAsyncThunkMethod());
@@ -132,7 +133,7 @@ void MethodDesc::EmitTaskReturningThunk(MethodDesc* pAsyncOtherVariant, MetaSig&
             }
 
             int token;
-            _ASSERTE(!pAsyncOtherVariant->IsWrapperStub());
+            _ASSERTE(!pAsyncOtherVariant->IsWrapperStub()); // Would it be better to move the assertion to the top of this method?
             if (pAsyncOtherVariant->HasClassOrMethodInstantiation())
             {
                 // For generic code emit generic signatures.

From 44f757f408ba6db5bb543e0a160400972f8fc95d Mon Sep 17 00:00:00 2001
From: Eduardo Velarde <32459232+eduardo-vp@users.noreply.github.com>
Date: Fri, 7 Nov 2025 16:07:46 -0800
Subject: [PATCH 02/13] Update
 src/coreclr/tools/Common/TypeSystem/IL/Stubs/AsyncThunks.cs
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Co-authored-by: Michal Strehovský <MichalStrehovsky@users.noreply.github.com>
---
 src/coreclr/tools/Common/TypeSystem/IL/Stubs/AsyncThunks.cs | 4 +---
 1 file changed, 1 insertion(+), 3 deletions(-)

diff --git a/src/coreclr/tools/Common/TypeSystem/IL/Stubs/AsyncThunks.cs b/src/coreclr/tools/Common/TypeSystem/IL/Stubs/AsyncThunks.cs
index 86e6ebbd143b4e..4db9af5ac0f1a8 100644
--- a/src/coreclr/tools/Common/TypeSystem/IL/Stubs/AsyncThunks.cs
+++ b/src/coreclr/tools/Common/TypeSystem/IL/Stubs/AsyncThunks.cs
@@ -19,9 +19,7 @@ public static MethodIL EmitTaskReturningThunk(MethodDesc taskReturningMethod, Me
             MethodSignature sig = asyncMethod.Signature;
             TypeDesc returnType = sig.ReturnType;
 
-            MetadataType md = taskReturningMethod.Signature.ReturnType as MetadataType;
-            ReadOnlySpan<byte> name = md.Name;
-            bool isValueTask = name.SequenceEqual("ValueTask"u8) || name.SequenceEqual("ValueTask`1"u8);
+            bool isValueTask = returnType.IsValueType;
 
             TypeDesc logicalReturnType = null;
             ILLocalVariable logicalResultLocal = 0;

From 960be0aeb3477694bdfeae2aba35424a29d0324a Mon Sep 17 00:00:00 2001
From: Eduardo Velarde <32459232+eduardo-vp@users.noreply.github.com>
Date: Fri, 7 Nov 2025 16:08:00 -0800
Subject: [PATCH 03/13] Update
 src/coreclr/tools/Common/TypeSystem/IL/Stubs/AsyncThunks.cs
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Co-authored-by: Michal Strehovský <MichalStrehovsky@users.noreply.github.com>
---
 src/coreclr/tools/Common/TypeSystem/IL/Stubs/AsyncThunks.cs | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/coreclr/tools/Common/TypeSystem/IL/Stubs/AsyncThunks.cs b/src/coreclr/tools/Common/TypeSystem/IL/Stubs/AsyncThunks.cs
index 4db9af5ac0f1a8..c156d6a364e2f7 100644
--- a/src/coreclr/tools/Common/TypeSystem/IL/Stubs/AsyncThunks.cs
+++ b/src/coreclr/tools/Common/TypeSystem/IL/Stubs/AsyncThunks.cs
@@ -53,7 +53,7 @@ public static MethodIL EmitTaskReturningThunk(MethodDesc taskReturningMethod, Me
                     codestream.BeginTry(tryCatchRegion);
 
                     int localArg = 0;
-                    if (sig.IsExplicitThis)
+                    if (!sig.IsStatic)
                     {
                         codestream.EmitLdArg(localArg++);
                     }

From 745f01d7c74039f3190fd932bdad80b18dfec4be Mon Sep 17 00:00:00 2001
From: Eduardo Velarde <32459232+eduardo-vp@users.noreply.github.com>
Date: Fri, 7 Nov 2025 16:08:56 -0800
Subject: [PATCH 04/13] Update
 src/coreclr/tools/Common/TypeSystem/IL/Stubs/AsyncThunks.cs
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Co-authored-by: Michal Strehovský <MichalStrehovsky@users.noreply.github.com>
---
 src/coreclr/tools/Common/TypeSystem/IL/Stubs/AsyncThunks.cs | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/coreclr/tools/Common/TypeSystem/IL/Stubs/AsyncThunks.cs b/src/coreclr/tools/Common/TypeSystem/IL/Stubs/AsyncThunks.cs
index c156d6a364e2f7..d35e675b8409f2 100644
--- a/src/coreclr/tools/Common/TypeSystem/IL/Stubs/AsyncThunks.cs
+++ b/src/coreclr/tools/Common/TypeSystem/IL/Stubs/AsyncThunks.cs
@@ -144,7 +144,7 @@ public static MethodIL EmitTaskReturningThunk(MethodDesc taskReturningMethod, Me
                         codestream.Emit(ILOpcode.call, emitter.NewToken(getCompletedTaskMethod));
                     }
 
-                    codestream.EmitLdLoc(returnTaskLocal);
+                    codestream.EmitStLoc(returnTaskLocal);
                     codestream.Emit(ILOpcode.leave, returnTaskLabel);
 
                     codestream.EndTry(tryCatchRegion);

From 2f1cbd3dc4734f3f9bf28271fd0eb921839dffed Mon Sep 17 00:00:00 2001
From: Eduardo Velarde <32459232+eduardo-vp@users.noreply.github.com>
Date: Fri, 7 Nov 2025 16:09:47 -0800
Subject: [PATCH 05/13] Update
 src/coreclr/tools/Common/TypeSystem/IL/Stubs/AsyncThunks.cs
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Co-authored-by: Michal Strehovský <MichalStrehovsky@users.noreply.github.com>
---
 src/coreclr/tools/Common/TypeSystem/IL/Stubs/AsyncThunks.cs | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/coreclr/tools/Common/TypeSystem/IL/Stubs/AsyncThunks.cs b/src/coreclr/tools/Common/TypeSystem/IL/Stubs/AsyncThunks.cs
index d35e675b8409f2..ebd4cea8e9ec13 100644
--- a/src/coreclr/tools/Common/TypeSystem/IL/Stubs/AsyncThunks.cs
+++ b/src/coreclr/tools/Common/TypeSystem/IL/Stubs/AsyncThunks.cs
@@ -160,7 +160,7 @@ public static MethodIL EmitTaskReturningThunk(MethodDesc taskReturningMethod, Me
                         if (isValueTask)
                         {
                             fromExceptionMd = context.SystemModule
-                                .GetKnownType("System.Threading.Tasks"u8, "ValueTask`1"u8)
+                                .GetKnownType("System.Threading.Tasks"u8, "ValueTask"u8)
                                 .GetKnownMethod("FromException"u8, null);
                         }
                         else

From df40ec47933a6a1cf4fe439f086ee56ad4087657 Mon Sep 17 00:00:00 2001
From: Eduardo Velarde <32459232+eduardo-vp@users.noreply.github.com>
Date: Fri, 7 Nov 2025 16:09:56 -0800
Subject: [PATCH 06/13] Update
 src/coreclr/tools/Common/TypeSystem/IL/Stubs/AsyncThunks.cs
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Co-authored-by: Michal Strehovský <MichalStrehovsky@users.noreply.github.com>
---
 src/coreclr/tools/Common/TypeSystem/IL/Stubs/AsyncThunks.cs | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/coreclr/tools/Common/TypeSystem/IL/Stubs/AsyncThunks.cs b/src/coreclr/tools/Common/TypeSystem/IL/Stubs/AsyncThunks.cs
index ebd4cea8e9ec13..401974d913a9ff 100644
--- a/src/coreclr/tools/Common/TypeSystem/IL/Stubs/AsyncThunks.cs
+++ b/src/coreclr/tools/Common/TypeSystem/IL/Stubs/AsyncThunks.cs
@@ -166,7 +166,7 @@ public static MethodIL EmitTaskReturningThunk(MethodDesc taskReturningMethod, Me
                         else
                         {
                             fromExceptionMd = context.SystemModule
-                                .GetKnownType("System.Threading.Tasks"u8, "Task`1"u8)
+                                .GetKnownType("System.Threading.Tasks"u8, "Task"u8)
                                 .GetKnownMethod("FromException"u8, null);
                         }
                         fromExceptionMd = fromExceptionMd.MakeInstantiatedMethod(new Instantiation(logicalReturnType));

From 0f3719bc6059502a087ac1663122d2b9d693c5e9 Mon Sep 17 00:00:00 2001
From: Eduardo Velarde <32459232+eduardo-vp@users.noreply.github.com>
Date: Fri, 7 Nov 2025 16:10:27 -0800
Subject: [PATCH 07/13] Update
 src/coreclr/tools/Common/TypeSystem/IL/Stubs/AsyncThunks.cs
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Co-authored-by: Michal Strehovský <MichalStrehovsky@users.noreply.github.com>
---
 src/coreclr/tools/Common/TypeSystem/IL/Stubs/AsyncThunks.cs | 1 +
 1 file changed, 1 insertion(+)

diff --git a/src/coreclr/tools/Common/TypeSystem/IL/Stubs/AsyncThunks.cs b/src/coreclr/tools/Common/TypeSystem/IL/Stubs/AsyncThunks.cs
index 401974d913a9ff..1b3c71a17d9aa2 100644
--- a/src/coreclr/tools/Common/TypeSystem/IL/Stubs/AsyncThunks.cs
+++ b/src/coreclr/tools/Common/TypeSystem/IL/Stubs/AsyncThunks.cs
@@ -245,6 +245,7 @@ public static MethodIL EmitTaskReturningThunk(MethodDesc taskReturningMethod, Me
 
                 // codestream.EmitLdLoca(executionAndSyncBlockStoreLocal);
                 // codestream.Emit(ILOpcode.call, emitter.NewToken(executionAndSyncBlockStoreType.GetKnownMethod("Pop"u8, null)));
+                codestream.Emit(ILOpcode.endfinally);
                 codestream.EndHandler(tryFinallyRegion);
             }
 

From af508ff3cf35b93833da54c8915f94cce9650db7 Mon Sep 17 00:00:00 2001
From: Eduardo Velarde <32459232+eduardo-vp@users.noreply.github.com>
Date: Fri, 7 Nov 2025 16:10:35 -0800
Subject: [PATCH 08/13] Update
 src/coreclr/tools/Common/TypeSystem/IL/Stubs/AsyncThunks.cs
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Co-authored-by: Michal Strehovský <MichalStrehovsky@users.noreply.github.com>
---
 src/coreclr/tools/Common/TypeSystem/IL/Stubs/AsyncThunks.cs | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/coreclr/tools/Common/TypeSystem/IL/Stubs/AsyncThunks.cs b/src/coreclr/tools/Common/TypeSystem/IL/Stubs/AsyncThunks.cs
index 1b3c71a17d9aa2..d663be84256cec 100644
--- a/src/coreclr/tools/Common/TypeSystem/IL/Stubs/AsyncThunks.cs
+++ b/src/coreclr/tools/Common/TypeSystem/IL/Stubs/AsyncThunks.cs
@@ -16,7 +16,7 @@ public static MethodIL EmitTaskReturningThunk(MethodDesc taskReturningMethod, Me
             var emitter = new ILEmitter();
             var codestream = emitter.NewCodeStream();
 
-            MethodSignature sig = asyncMethod.Signature;
+            MethodSignature sig = taskReturningMethod.Signature;
             TypeDesc returnType = sig.ReturnType;
 
             bool isValueTask = returnType.IsValueType;

From c10f45891d6edb235410e27682f968a94698c34d Mon Sep 17 00:00:00 2001
From: Eduardo Velarde <eduardo.velarde@pucp.pe>
Date: Mon, 10 Nov 2025 16:44:03 -0800
Subject: [PATCH 09/13] Address PR feedback

---
 .../src/System.Private.CoreLib.csproj         |   1 +
 .../src/System/StubHelpers.NativeAot.cs       |  10 ++
 .../Common/TypeSystem/IL/Stubs/AsyncThunks.cs | 132 +++++++-----------
 .../Common/TypeSystem/IL/Stubs/ILEmitter.cs   |  54 +++----
 src/coreclr/vm/asyncthunks.cpp                |   2 +-
 5 files changed, 93 insertions(+), 106 deletions(-)
 create mode 100644 src/coreclr/nativeaot/System.Private.CoreLib/src/System/StubHelpers.NativeAot.cs

diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System.Private.CoreLib.csproj b/src/coreclr/nativeaot/System.Private.CoreLib/src/System.Private.CoreLib.csproj
index 01e53975c4819b..b5329c522da12c 100644
--- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System.Private.CoreLib.csproj
+++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System.Private.CoreLib.csproj
@@ -244,6 +244,7 @@
     <Compile Include="System\Runtime\CompilerServices\AnalysisCharacteristicAttribute.cs" />
     <Compile Include="System\Runtime\CompilerServices\ClassConstructorRunner.cs" />
     <Compile Include="System\Runtime\CompilerServices\RuntimeHelpers.NativeAot.cs" />
+    <Compile Include="System\StubHelpers.NativeAot.cs" />
   </ItemGroup>
   <ItemGroup Condition="'$(TargetsWindows)'=='true'">
     <Compile Include="$(CommonPath)\Interop\Windows\Kernel32\Interop.ExitProcess.cs">
diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/StubHelpers.NativeAot.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/StubHelpers.NativeAot.cs
new file mode 100644
index 00000000000000..a20f3186b4d27b
--- /dev/null
+++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/StubHelpers.NativeAot.cs
@@ -0,0 +1,10 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+namespace System.StubHelpers
+{
+    internal static partial class StubHelpers
+    {
+        internal static object? AsyncCallContinuation() => throw new Exception(); // Unconditionally expanded intrinsic
+    }
+}
diff --git a/src/coreclr/tools/Common/TypeSystem/IL/Stubs/AsyncThunks.cs b/src/coreclr/tools/Common/TypeSystem/IL/Stubs/AsyncThunks.cs
index d663be84256cec..2d6986ee3fce86 100644
--- a/src/coreclr/tools/Common/TypeSystem/IL/Stubs/AsyncThunks.cs
+++ b/src/coreclr/tools/Common/TypeSystem/IL/Stubs/AsyncThunks.cs
@@ -8,6 +8,11 @@ namespace Internal.IL.Stubs
 {
     public static class AsyncThunkILEmitter
     {
+        // Emits a thunk that wraps an async method to return a Task or ValueTask.
+        // The thunk calls the async method, and if it completes synchronously,
+        // it returns a completed Task/ValueTask. If the async method suspends,
+        // it calls FinalizeTaskReturningThunk/FinalizeValueTaskReturningThunk method to get the Task/ValueTask.
+
         // The emitted code matches method EmitTaskReturningThunk in CoreCLR VM.
         public static MethodIL EmitTaskReturningThunk(MethodDesc taskReturningMethod, MethodDesc asyncMethod)
         {
@@ -32,23 +37,24 @@ public static MethodIL EmitTaskReturningThunk(MethodDesc taskReturningMethod, Me
 
             ILLocalVariable returnTaskLocal = emitter.NewLocal(returnType);
 
-            // TODO: Fix this (ExecutionAndSyncBlockStore is not available in Native AOT).
+            // TODO: Check if store/restore is responsibility of the async callee
+            // after https://github.com/dotnet/runtime/pull/121448 gets merged.
 
-            // TypeDesc executionAndSyncBlockStoreType = context.SystemModule.GetKnownType("System.Runtime.CompilerServices"u8, "ExecutionAndSyncBlockStore"u8);
-            // ILLocalVariable executionAndSyncBlockStoreLocal = emitter.NewLocal(executionAndSyncBlockStoreType);
+            TypeDesc executionAndSyncBlockStoreType = context.SystemModule.GetKnownType("System.Runtime.CompilerServices"u8, "ExecutionAndSyncBlockStore"u8);
+            ILLocalVariable executionAndSyncBlockStoreLocal = emitter.NewLocal(executionAndSyncBlockStoreType);
 
             ILCodeLabel returnTaskLabel = emitter.NewCodeLabel();
             ILCodeLabel suspendedLabel = emitter.NewCodeLabel();
             ILCodeLabel finishedLabel = emitter.NewCodeLabel();
 
-            // codestream.EmitLdLoca(executionAndSyncBlockStoreLocal);
-            // codestream.Emit(ILOpcode.call, emitter.NewToken(executionAndSyncBlockStoreType.GetKnownMethod("Push"u8, null)));
+            codestream.EmitLdLoca(executionAndSyncBlockStoreLocal);
+            codestream.Emit(ILOpcode.call, emitter.NewToken(executionAndSyncBlockStoreType.GetKnownMethod("Push"u8, null)));
 
             ILExceptionRegionBuilder tryFinallyRegion = emitter.NewFinallyRegion();
             {
                 codestream.BeginTry(tryFinallyRegion);
                 codestream.Emit(ILOpcode.nop);
-                ILExceptionRegionBuilder tryCatchRegion = emitter.NewCatchRegion();
+                ILExceptionRegionBuilder tryCatchRegion = emitter.NewCatchRegion(context.GetWellKnownType(WellKnownType.Object));
                 {
                     codestream.BeginTry(tryCatchRegion);
 
@@ -92,13 +98,12 @@ public static MethodIL EmitTaskReturningThunk(MethodDesc taskReturningMethod, Me
                         codestream.EmitStLoc(logicalResultLocal);
                     }
 
-                    // TODO: Fix this (AsyncCallContinuation is not available in Native AOT).
+                    MethodDesc asyncCallContinuationMd = context.SystemModule
+                                                .GetKnownType("System.StubHelpers"u8, "StubHelpers"u8)
+                                                .GetKnownMethod("AsyncCallContinuation"u8, null);
 
-                    //MethodDesc asyncCallContinuationMd = context.SystemModule
-                    //                            .GetKnownType("System.StubHelpers"u8, "StubHelpers"u8)
-                    //                            .GetKnownMethod("AsyncCallContinuation"u8, null);
+                    codestream.Emit(ILOpcode.call, emitter.NewToken(asyncCallContinuationMd));
 
-                    //codestream.Emit(ILOpcode.call, emitter.NewToken(asyncCallContinuationMd));
 
                     codestream.Emit(ILOpcode.brfalse, finishedLabel);
                     codestream.Emit(ILOpcode.leave, suspendedLabel);
@@ -154,38 +159,22 @@ public static MethodIL EmitTaskReturningThunk(MethodDesc taskReturningMethod, Me
                     codestream.BeginHandler(tryCatchRegion);
 
                     MethodDesc fromExceptionMd;
-                    if (logicalReturnType != null)
+                    if (isValueTask)
                     {
-                        // Generate: returnType.FromException<T>(Exception)
-                        if (isValueTask)
-                        {
-                            fromExceptionMd = context.SystemModule
-                                .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);
-                        }
-                        fromExceptionMd = fromExceptionMd.MakeInstantiatedMethod(new Instantiation(logicalReturnType));
+                        fromExceptionMd = context.SystemModule
+                            .GetKnownType("System.Threading.Tasks"u8, "ValueTask"u8)
+                            .GetKnownMethod("FromException"u8, null);
                     }
                     else
                     {
-                        // Generate: returnType.FromException(Exception)
-                        if (isValueTask)
-                        {
-                            fromExceptionMd = context.SystemModule
-                                .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);
-                        }
+                        fromExceptionMd = context.SystemModule
+                            .GetKnownType("System.Threading.Tasks"u8, "Task"u8)
+                            .GetKnownMethod("FromException"u8, null);
+                    }
+
+                    if (logicalReturnType != null)
+                    {
+                        fromExceptionMd = fromExceptionMd.MakeInstantiatedMethod(new Instantiation(logicalReturnType));
                     }
 
                     codestream.Emit(ILOpcode.call, emitter.NewToken(fromExceptionMd));
@@ -196,55 +185,38 @@ public static MethodIL EmitTaskReturningThunk(MethodDesc taskReturningMethod, Me
 
                 codestream.EmitLabel(suspendedLabel);
 
-                // TODO: Fix this (Finalize returning thunks are not available in Native AOT).
-
-                //MethodDesc finalizeTaskReturningThunkMd;
-
-                //if (logicalReturnType != null)
-                //{
-                //    if (isValueTask)
-                //    {
-                //        finalizeTaskReturningThunkMd = context.SystemModule
-                //            .GetKnownType("System.Runtime.CompilerServices"u8, "AsyncHelpers"u8)
-                //            .GetKnownMethod("FinalizeValueTaskReturningThunk`1"u8, null);
-                //    }
-                //    else
-                //    {
-                //        finalizeTaskReturningThunkMd = context.SystemModule
-                //            .GetKnownType("System.Runtime.CompilerServices"u8, "AsyncHelpers"u8)
-                //            .GetKnownMethod("FinalizeTaskReturningThunk`1"u8, null);
-                //    }
-                //    finalizeTaskReturningThunkMd = finalizeTaskReturningThunkMd.MakeInstantiatedMethod(new Instantiation(logicalReturnType));
-                //}
-                //else
-                //{
-                //    if (isValueTask)
-                //    {
-                //        finalizeTaskReturningThunkMd = context.SystemModule
-                //            .GetKnownType("System.Runtime.CompilerServices"u8, "AsyncHelpers"u8)
-                //            .GetKnownMethod("FinalizeValueTaskReturningThunk"u8, null);
-                //    }
-                //    else
-                //    {
-                //        finalizeTaskReturningThunkMd = context.SystemModule
-                //            .GetKnownType("System.Runtime.CompilerServices"u8, "AsyncHelpers"u8)
-                //            .GetKnownMethod("FinalizeTaskReturningThunk"u8, null);
-                //    }
-                //}
-                //codestream.Emit(ILOpcode.call, emitter.NewToken(finalizeTaskReturningThunkMd));
+                MethodDesc finalizeTaskReturningThunkMd;
+
+                if (isValueTask)
+                {
+                    finalizeTaskReturningThunkMd = context.SystemModule
+                        .GetKnownType("System.Runtime.CompilerServices"u8, "AsyncHelpers"u8)
+                        .GetKnownMethod("FinalizeValueTaskReturningThunk"u8, null);
+                }
+                else
+                {
+                    finalizeTaskReturningThunkMd = context.SystemModule
+                        .GetKnownType("System.Runtime.CompilerServices"u8, "AsyncHelpers"u8)
+                        .GetKnownMethod("FinalizeTaskReturningThunk"u8, null);
+                }
+
+                if (logicalReturnType != null)
+                {
+                    finalizeTaskReturningThunkMd = finalizeTaskReturningThunkMd.MakeInstantiatedMethod(new Instantiation(logicalReturnType));
+                }
+
+                codestream.Emit(ILOpcode.call, emitter.NewToken(finalizeTaskReturningThunkMd));
                 codestream.EmitStLoc(returnTaskLocal);
                 codestream.Emit(ILOpcode.leave, returnTaskLabel);
 
                 codestream.EndTry(tryFinallyRegion);
             }
-            //
+
             {
                 codestream.BeginHandler(tryFinallyRegion);
 
-                // TODO: Fix this (ExecutionAndSyncBlockStore is not available in Native AOT).
-
-                // codestream.EmitLdLoca(executionAndSyncBlockStoreLocal);
-                // codestream.Emit(ILOpcode.call, emitter.NewToken(executionAndSyncBlockStoreType.GetKnownMethod("Pop"u8, null)));
+                codestream.EmitLdLoca(executionAndSyncBlockStoreLocal);
+                codestream.Emit(ILOpcode.call, emitter.NewToken(executionAndSyncBlockStoreType.GetKnownMethod("Pop"u8, null)));
                 codestream.Emit(ILOpcode.endfinally);
                 codestream.EndHandler(tryFinallyRegion);
             }
diff --git a/src/coreclr/tools/Common/TypeSystem/IL/Stubs/ILEmitter.cs b/src/coreclr/tools/Common/TypeSystem/IL/Stubs/ILEmitter.cs
index bfffd099ee60b3..c671df85517578 100644
--- a/src/coreclr/tools/Common/TypeSystem/IL/Stubs/ILEmitter.cs
+++ b/src/coreclr/tools/Common/TypeSystem/IL/Stubs/ILEmitter.cs
@@ -517,8 +517,15 @@ public class ILExceptionRegionBuilder
         internal ILCodeStream _endHandlerStream;
         internal int _endHandlerOffset;
 
-        internal ILExceptionRegionBuilder()
+        internal ILExceptionRegionKind _exceptionRegionKind;
+        internal TypeDesc _catchExceptionType;
+
+        internal ILExceptionRegionBuilder(ILExceptionRegionKind exceptionRegionKind, TypeDesc catchExceptionType = null)
         {
+            _exceptionRegionKind = exceptionRegionKind;
+            _catchExceptionType = catchExceptionType;
+            Debug.Assert((exceptionRegionKind == ILExceptionRegionKind.Catch && catchExceptionType != null)
+                || (exceptionRegionKind != ILExceptionRegionKind.Catch && catchExceptionType == null));
         }
 
         internal int TryOffset => _beginTryStream.RelativeToAbsoluteOffset(_beginTryOffset);
@@ -669,8 +676,7 @@ public class ILEmitter
         private ArrayBuilder<ILCodeStream> _codeStreams;
         private ArrayBuilder<LocalVariableDefinition> _locals;
         private ArrayBuilder<object> _tokens;
-        private ArrayBuilder<ILExceptionRegionBuilder> _catchRegions;
-        private ArrayBuilder<ILExceptionRegionBuilder> _finallyRegions;
+        private ArrayBuilder<ILExceptionRegionBuilder> _exceptionRegions;
 
         public ILEmitter()
         {
@@ -729,17 +735,17 @@ public ILCodeLabel NewCodeLabel()
         }
 
         // For now, only catches exceptions of type Exception.
-        public ILExceptionRegionBuilder NewCatchRegion()
+        public ILExceptionRegionBuilder NewCatchRegion(TypeDesc exceptionType)
         {
-            var region = new ILExceptionRegionBuilder();
-            _catchRegions.Add(region);
+            var region = new ILExceptionRegionBuilder(ILExceptionRegionKind.Catch, exceptionType);
+            _exceptionRegions.Add(region);
             return region;
         }
 
         public ILExceptionRegionBuilder NewFinallyRegion()
         {
-            var region = new ILExceptionRegionBuilder();
-            _finallyRegions.Add(region);
+            var region = new ILExceptionRegionBuilder(ILExceptionRegionKind.Finally);
+            _exceptionRegions.Add(region);
             return region;
         }
 
@@ -791,36 +797,34 @@ public MethodIL Link(MethodDesc owningMethod)
 
             ILExceptionRegion[] exceptionRegions = null;
 
-            int numberOfExceptionRegions = _catchRegions.Count + _finallyRegions.Count;
+            int numberOfExceptionRegions = _exceptionRegions.Count;
             if (numberOfExceptionRegions > 0)
             {
                 exceptionRegions = new ILExceptionRegion[numberOfExceptionRegions];
-
-                TypeDesc exceptionType = owningMethod.Context.SystemModule.GetKnownType("System"u8, "Exception"u8);
-
-                int exceptionTypeToken = (int)NewToken(exceptionType);
-
-                for (int i = 0; i < _catchRegions.Count; i++)
+                for (int i = 0; i < _exceptionRegions.Count; i++)
                 {
-                    ILExceptionRegionBuilder region = _catchRegions[i];
+                    ILExceptionRegionBuilder region = _exceptionRegions[i];
 
                     Debug.Assert(region.IsDefined);
 
-                    exceptionRegions[i] = new ILExceptionRegion(ILExceptionRegionKind.Catch,
+                    int exceptionTypeToken = (region._catchExceptionType != null) ? (int)NewToken(region._catchExceptionType) : 0;
+
+                    exceptionRegions[i] = new ILExceptionRegion(region._exceptionRegionKind,
                         region.TryOffset, region.TryLength, region.HandlerOffset, region.HandlerLength,
                         classToken: exceptionTypeToken, filterOffset: 0);
                 }
 
-                for (int i = 0; i < _finallyRegions.Count; i++)
+                // Sort exception regions so that innermost (most nested) regions come first
+                // as this is required by the spec.
+                // Innermost regions have higher TryOffset and smaller TryLength.
+                Array.Sort(exceptionRegions, (a, b) =>
                 {
-                    ILExceptionRegionBuilder region = _finallyRegions[i];
+                    int offsetComparison = b.TryOffset.CompareTo(a.TryOffset);
+                    if (offsetComparison != 0)
+                        return offsetComparison;
 
-                    Debug.Assert(region.IsDefined);
-
-                    exceptionRegions[_catchRegions.Count + i] = new ILExceptionRegion(ILExceptionRegionKind.Finally,
-                        region.TryOffset, region.TryLength, region.HandlerOffset, region.HandlerLength,
-                        classToken: 0, filterOffset: 0);
-                }
+                    return a.TryLength.CompareTo(b.TryLength);
+                });
             }
 
             var result = new ILStubMethodIL(owningMethod, ilInstructions, _locals.ToArray(), _tokens.ToArray(), exceptionRegions, debugInfo);
diff --git a/src/coreclr/vm/asyncthunks.cpp b/src/coreclr/vm/asyncthunks.cpp
index ed80604e0302f7..9fbca119ed1bff 100644
--- a/src/coreclr/vm/asyncthunks.cpp
+++ b/src/coreclr/vm/asyncthunks.cpp
@@ -133,7 +133,7 @@ void MethodDesc::EmitTaskReturningThunk(MethodDesc* pAsyncOtherVariant, MetaSig&
             }
 
             int token;
-            _ASSERTE(!pAsyncOtherVariant->IsWrapperStub()); // Would it be better to move the assertion to the top of this method?
+            _ASSERTE(!pAsyncOtherVariant->IsWrapperStub());
             if (pAsyncOtherVariant->HasClassOrMethodInstantiation())
             {
                 // For generic code emit generic signatures.

From 67c25f3ba3406df64fc1de521b9dd6893563baaf Mon Sep 17 00:00:00 2001
From: Eduardo Velarde <eduardo.velarde@pucp.pe>
Date: Tue, 11 Nov 2025 01:48:55 -0800
Subject: [PATCH 10/13] PR feedback

---
 .../src/System.Private.CoreLib.csproj         |  1 -
 .../src/System/StubHelpers.NativeAot.cs       | 10 ------
 .../Common/TypeSystem/IL/Stubs/AsyncThunks.cs | 34 +++++++++++--------
 3 files changed, 19 insertions(+), 26 deletions(-)
 delete mode 100644 src/coreclr/nativeaot/System.Private.CoreLib/src/System/StubHelpers.NativeAot.cs

diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System.Private.CoreLib.csproj b/src/coreclr/nativeaot/System.Private.CoreLib/src/System.Private.CoreLib.csproj
index b5329c522da12c..01e53975c4819b 100644
--- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System.Private.CoreLib.csproj
+++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System.Private.CoreLib.csproj
@@ -244,7 +244,6 @@
     <Compile Include="System\Runtime\CompilerServices\AnalysisCharacteristicAttribute.cs" />
     <Compile Include="System\Runtime\CompilerServices\ClassConstructorRunner.cs" />
     <Compile Include="System\Runtime\CompilerServices\RuntimeHelpers.NativeAot.cs" />
-    <Compile Include="System\StubHelpers.NativeAot.cs" />
   </ItemGroup>
   <ItemGroup Condition="'$(TargetsWindows)'=='true'">
     <Compile Include="$(CommonPath)\Interop\Windows\Kernel32\Interop.ExitProcess.cs">
diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/StubHelpers.NativeAot.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/StubHelpers.NativeAot.cs
deleted file mode 100644
index a20f3186b4d27b..00000000000000
--- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/StubHelpers.NativeAot.cs
+++ /dev/null
@@ -1,10 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-
-namespace System.StubHelpers
-{
-    internal static partial class StubHelpers
-    {
-        internal static object? AsyncCallContinuation() => throw new Exception(); // Unconditionally expanded intrinsic
-    }
-}
diff --git a/src/coreclr/tools/Common/TypeSystem/IL/Stubs/AsyncThunks.cs b/src/coreclr/tools/Common/TypeSystem/IL/Stubs/AsyncThunks.cs
index a82e744119143c..220cd09c34e0e3 100644
--- a/src/coreclr/tools/Common/TypeSystem/IL/Stubs/AsyncThunks.cs
+++ b/src/coreclr/tools/Common/TypeSystem/IL/Stubs/AsyncThunks.cs
@@ -99,12 +99,11 @@ public static MethodIL EmitTaskReturningThunk(MethodDesc taskReturningMethod, Me
                     }
 
                     MethodDesc asyncCallContinuationMd = context.SystemModule
-                                                .GetKnownType("System.StubHelpers"u8, "StubHelpers"u8)
+                                                .GetKnownType("System.Runtime.CompilerServices"u8, "AsyncHelpers"u8)
                                                 .GetKnownMethod("AsyncCallContinuation"u8, null);
 
                     codestream.Emit(ILOpcode.call, emitter.NewToken(asyncCallContinuationMd));
 
-
                     codestream.Emit(ILOpcode.brfalse, finishedLabel);
                     codestream.Emit(ILOpcode.leave, suspendedLabel);
                     codestream.EmitLabel(finishedLabel);
@@ -158,20 +157,25 @@ public static MethodIL EmitTaskReturningThunk(MethodDesc taskReturningMethod, Me
                 {
                     codestream.BeginHandler(tryCatchRegion);
 
-                    MethodDesc fromExceptionMd;
-                    if (isValueTask)
-                    {
-                        fromExceptionMd = context.SystemModule
-                            .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);
-                    }
+                    TypeDesc exceptionType = context.GetWellKnownType(WellKnownType.Exception);
+
+                    // For generic methods (Task<T>/ValueTask<T>), the signature uses a method type parameter (!!0)
+                    // For non-generic methods (Task/ValueTask), the signature uses the concrete return type
+                    TypeDesc fromExceptionSignatureReturnType = logicalReturnType != null
+                        ? ((MetadataType)returnType.GetTypeDefinition()).MakeInstantiatedType(context.GetSignatureVariable(0, true))
+                        : returnType;
+
+                    MethodSignature fromExceptionSignature = new MethodSignature(
+                        MethodSignatureFlags.Static,
+                        genericParameterCount: logicalReturnType != null ? 1 : 0,
+                        returnType: fromExceptionSignatureReturnType,
+                        parameters: new[] { exceptionType });
+
+                    MethodDesc fromExceptionMd = context.SystemModule
+                        .GetKnownType("System.Threading.Tasks"u8, isValueTask ? "ValueTask"u8 : "Task"u8)
+                        .GetKnownMethod("FromException"u8, fromExceptionSignature);
 
+                    // Instantiate the generic method if we have a generic return type (Task<T> or ValueTask<T>)
                     if (logicalReturnType != null)
                     {
                         fromExceptionMd = fromExceptionMd.MakeInstantiatedMethod(new Instantiation(logicalReturnType));

From a86fbf73530ad66ebb86297f99833393cad79b84 Mon Sep 17 00:00:00 2001
From: Eduardo Velarde <eduardo.velarde@pucp.pe>
Date: Tue, 11 Nov 2025 01:52:26 -0800
Subject: [PATCH 11/13] Nit

---
 src/coreclr/tools/Common/TypeSystem/IL/Stubs/ILEmitter.cs | 1 -
 1 file changed, 1 deletion(-)

diff --git a/src/coreclr/tools/Common/TypeSystem/IL/Stubs/ILEmitter.cs b/src/coreclr/tools/Common/TypeSystem/IL/Stubs/ILEmitter.cs
index c671df85517578..10d15a976b614b 100644
--- a/src/coreclr/tools/Common/TypeSystem/IL/Stubs/ILEmitter.cs
+++ b/src/coreclr/tools/Common/TypeSystem/IL/Stubs/ILEmitter.cs
@@ -734,7 +734,6 @@ public ILCodeLabel NewCodeLabel()
             return newLabel;
         }
 
-        // For now, only catches exceptions of type Exception.
         public ILExceptionRegionBuilder NewCatchRegion(TypeDesc exceptionType)
         {
             var region = new ILExceptionRegionBuilder(ILExceptionRegionKind.Catch, exceptionType);

From 49447467e369e32109495cdd2d137e7c749b1aec Mon Sep 17 00:00:00 2001
From: Eduardo Velarde <eduardo.velarde@pucp.pe>
Date: Tue, 11 Nov 2025 15:15:25 -0800
Subject: [PATCH 12/13] PR feedback

---
 .../Common/TypeSystem/IL/Stubs/AsyncThunks.cs | 49 +++++++++----------
 1 file changed, 24 insertions(+), 25 deletions(-)

diff --git a/src/coreclr/tools/Common/TypeSystem/IL/Stubs/AsyncThunks.cs b/src/coreclr/tools/Common/TypeSystem/IL/Stubs/AsyncThunks.cs
index 220cd09c34e0e3..f485567734a1a0 100644
--- a/src/coreclr/tools/Common/TypeSystem/IL/Stubs/AsyncThunks.cs
+++ b/src/coreclr/tools/Common/TypeSystem/IL/Stubs/AsyncThunks.cs
@@ -71,13 +71,7 @@ public static MethodIL EmitTaskReturningThunk(MethodDesc taskReturningMethod, Me
 
                     if (asyncMethod.OwningType.HasInstantiation)
                     {
-                        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));
+                        var instantiatedType = (InstantiatedType)TypeSystemHelpers.InstantiateAsOpen(asyncMethod.OwningType);
                         asyncMethod = context.GetMethodForInstantiatedType(asyncMethod, instantiatedType);
                     }
 
@@ -159,26 +153,31 @@ public static MethodIL EmitTaskReturningThunk(MethodDesc taskReturningMethod, Me
 
                     TypeDesc exceptionType = context.GetWellKnownType(WellKnownType.Exception);
 
-                    // For generic methods (Task<T>/ValueTask<T>), the signature uses a method type parameter (!!0)
-                    // For non-generic methods (Task/ValueTask), the signature uses the concrete return type
-                    TypeDesc fromExceptionSignatureReturnType = logicalReturnType != null
-                        ? ((MetadataType)returnType.GetTypeDefinition()).MakeInstantiatedType(context.GetSignatureVariable(0, true))
-                        : returnType;
-
-                    MethodSignature fromExceptionSignature = new MethodSignature(
-                        MethodSignatureFlags.Static,
-                        genericParameterCount: logicalReturnType != null ? 1 : 0,
-                        returnType: fromExceptionSignatureReturnType,
-                        parameters: new[] { exceptionType });
-
-                    MethodDesc fromExceptionMd = context.SystemModule
-                        .GetKnownType("System.Threading.Tasks"u8, isValueTask ? "ValueTask"u8 : "Task"u8)
-                        .GetKnownMethod("FromException"u8, fromExceptionSignature);
-
-                    // Instantiate the generic method if we have a generic return type (Task<T> or ValueTask<T>)
+                    MethodDesc fromExceptionMd;
                     if (logicalReturnType != null)
                     {
-                        fromExceptionMd = fromExceptionMd.MakeInstantiatedMethod(new Instantiation(logicalReturnType));
+                        MethodSignature fromExceptionSignature = new MethodSignature(
+                            MethodSignatureFlags.Static,
+                            genericParameterCount: 1,
+                            returnType: ((MetadataType)returnType.GetTypeDefinition()).MakeInstantiatedType(context.GetSignatureVariable(0, true)),
+                            parameters: new[] { exceptionType });
+
+                        fromExceptionMd = context.SystemModule
+                            .GetKnownType("System.Runtime.CompilerServices"u8, "AsyncHelpers"u8)
+                            .GetKnownMethod(isValueTask ? "ValueTaskFromException"u8 : "TaskFromException"u8, fromExceptionSignature)
+                            .MakeInstantiatedMethod(new Instantiation(logicalReturnType));
+                    }
+                    else
+                    {
+                        MethodSignature fromExceptionSignature = new MethodSignature(
+                            MethodSignatureFlags.Static,
+                            genericParameterCount: 0,
+                            returnType: returnType,
+                            parameters: new[] { exceptionType });
+
+                        fromExceptionMd = context.SystemModule
+                            .GetKnownType("System.Runtime.CompilerServices"u8, "AsyncHelpers"u8)
+                            .GetKnownMethod(isValueTask ? "ValueTaskFromException"u8 : "TaskFromException"u8, fromExceptionSignature);
                     }
 
                     codestream.Emit(ILOpcode.call, emitter.NewToken(fromExceptionMd));

From 869e6707e0e2852ea218c833c9fe9deb55664201 Mon Sep 17 00:00:00 2001
From: Eduardo Velarde <eduardo.velarde@pucp.pe>
Date: Wed, 12 Nov 2025 13:53:57 -0800
Subject: [PATCH 13/13] PR feedback

---
 .../Common/TypeSystem/IL/Stubs/AsyncThunks.cs | 38 +++++++++++--------
 1 file changed, 23 insertions(+), 15 deletions(-)

diff --git a/src/coreclr/tools/Common/TypeSystem/IL/Stubs/AsyncThunks.cs b/src/coreclr/tools/Common/TypeSystem/IL/Stubs/AsyncThunks.cs
index f485567734a1a0..fd8d45455c7a72 100644
--- a/src/coreclr/tools/Common/TypeSystem/IL/Stubs/AsyncThunks.cs
+++ b/src/coreclr/tools/Common/TypeSystem/IL/Stubs/AsyncThunks.cs
@@ -37,9 +37,6 @@ public static MethodIL EmitTaskReturningThunk(MethodDesc taskReturningMethod, Me
 
             ILLocalVariable returnTaskLocal = emitter.NewLocal(returnType);
 
-            // TODO: Check if store/restore is responsibility of the async callee
-            // after https://github.com/dotnet/runtime/pull/121448 gets merged.
-
             TypeDesc executionAndSyncBlockStoreType = context.SystemModule.GetKnownType("System.Runtime.CompilerServices"u8, "ExecutionAndSyncBlockStore"u8);
             ILLocalVariable executionAndSyncBlockStoreLocal = emitter.NewLocal(executionAndSyncBlockStoreType);
 
@@ -110,7 +107,7 @@ public static MethodIL EmitTaskReturningThunk(MethodDesc taskReturningMethod, Me
                         if (isValueTask)
                         {
                             fromResultMethod = context.SystemModule
-                                .GetKnownType("System.Threading.Tasks"u8, "ValueTask`1"u8)
+                                .GetKnownType("System.Threading.Tasks"u8, "ValueTask"u8)
                                 .GetKnownMethod("FromResult"u8, null)
                                 .MakeInstantiatedMethod(new Instantiation(logicalReturnType));
                         }
@@ -160,7 +157,8 @@ public static MethodIL EmitTaskReturningThunk(MethodDesc taskReturningMethod, Me
                             MethodSignatureFlags.Static,
                             genericParameterCount: 1,
                             returnType: ((MetadataType)returnType.GetTypeDefinition()).MakeInstantiatedType(context.GetSignatureVariable(0, true)),
-                            parameters: new[] { exceptionType });
+                            parameters: new[] { exceptionType }
+                        );
 
                         fromExceptionMd = context.SystemModule
                             .GetKnownType("System.Runtime.CompilerServices"u8, "AsyncHelpers"u8)
@@ -173,7 +171,8 @@ public static MethodIL EmitTaskReturningThunk(MethodDesc taskReturningMethod, Me
                             MethodSignatureFlags.Static,
                             genericParameterCount: 0,
                             returnType: returnType,
-                            parameters: new[] { exceptionType });
+                            parameters: new[] { exceptionType }
+                        );
 
                         fromExceptionMd = context.SystemModule
                             .GetKnownType("System.Runtime.CompilerServices"u8, "AsyncHelpers"u8)
@@ -189,23 +188,32 @@ public static MethodIL EmitTaskReturningThunk(MethodDesc taskReturningMethod, Me
                 codestream.EmitLabel(suspendedLabel);
 
                 MethodDesc finalizeTaskReturningThunkMd;
-
-                if (isValueTask)
+                if (logicalReturnType != null)
                 {
+                    MethodSignature finalizeReturningThunkSignature = new MethodSignature(
+                        MethodSignatureFlags.Static,
+                        genericParameterCount: 1,
+                        returnType: ((MetadataType)returnType.GetTypeDefinition()).MakeInstantiatedType(context.GetSignatureVariable(0, true)),
+                        parameters: Array.Empty<TypeDesc>()
+                    );
+
                     finalizeTaskReturningThunkMd = context.SystemModule
                         .GetKnownType("System.Runtime.CompilerServices"u8, "AsyncHelpers"u8)
-                        .GetKnownMethod("FinalizeValueTaskReturningThunk"u8, null);
+                        .GetKnownMethod(isValueTask ? "FinalizeValueTaskReturningThunk"u8 : "FinalizeTaskReturningThunk"u8, finalizeReturningThunkSignature)
+                        .MakeInstantiatedMethod(new Instantiation(logicalReturnType));
                 }
                 else
                 {
+                    MethodSignature finalizeReturningThunkSignature = new MethodSignature(
+                        MethodSignatureFlags.Static,
+                        genericParameterCount: 0,
+                        returnType: returnType,
+                        parameters: Array.Empty<TypeDesc>()
+                    );
+
                     finalizeTaskReturningThunkMd = context.SystemModule
                         .GetKnownType("System.Runtime.CompilerServices"u8, "AsyncHelpers"u8)
-                        .GetKnownMethod("FinalizeTaskReturningThunk"u8, null);
-                }
-
-                if (logicalReturnType != null)
-                {
-                    finalizeTaskReturningThunkMd = finalizeTaskReturningThunkMd.MakeInstantiatedMethod(new Instantiation(logicalReturnType));
+                        .GetKnownMethod(isValueTask ? "FinalizeValueTaskReturningThunk"u8 : "FinalizeTaskReturningThunk"u8, finalizeReturningThunkSignature);
                 }
 
                 codestream.Emit(ILOpcode.call, emitter.NewToken(finalizeTaskReturningThunkMd));
