From 0beb3f0fad2cd98332fddf63d42f541f57ae9390 Mon Sep 17 00:00:00 2001
From: David Wrighton <davidwr@microsoft.com>
Date: Mon, 18 Sep 2023 12:05:08 -0700
Subject: [PATCH 001/203] More updates

---
 src/coreclr/vm/array.cpp              |   2 +-
 src/coreclr/vm/class.cpp              |   4 +-
 src/coreclr/vm/clsload.cpp            |   2 +-
 src/coreclr/vm/dynamicmethod.cpp      |   2 +-
 src/coreclr/vm/genmeth.cpp            |  10 +
 src/coreclr/vm/method.cpp             |  42 +++-
 src/coreclr/vm/method.hpp             |  44 +++-
 src/coreclr/vm/methodtablebuilder.cpp | 339 ++++++++++++++++++++++++--
 src/coreclr/vm/methodtablebuilder.h   |  57 ++++-
 9 files changed, 454 insertions(+), 48 deletions(-)

diff --git a/src/coreclr/vm/array.cpp b/src/coreclr/vm/array.cpp
index 3670c7e8446bda..7a2bb145faadbe 100644
--- a/src/coreclr/vm/array.cpp
+++ b/src/coreclr/vm/array.cpp
@@ -500,7 +500,7 @@ MethodTable* Module::CreateArrayMethodTable(TypeHandle elemTypeHnd, CorElementTy
                             + 3;        // for rank specific Get, Set, Address
 
         MethodDescChunk * pChunks = MethodDescChunk::CreateChunk(pAllocator->GetHighFrequencyHeap(),
-                            dwMethodDescs, mcArray, FALSE /* fNonVtableSlot*/, FALSE /* fNativeCodeSlot */,
+                            dwMethodDescs, mcArray, FALSE /* fNonVtableSlot*/, FALSE /* fNativeCodeSlot */, FALSE /* IsAsyncThunkMethod */,
                             pMT, pamTracker);
         pClass->SetChunks(pChunks);
 
diff --git a/src/coreclr/vm/class.cpp b/src/coreclr/vm/class.cpp
index 0062632396c955..296c6686529dbd 100644
--- a/src/coreclr/vm/class.cpp
+++ b/src/coreclr/vm/class.cpp
@@ -767,6 +767,7 @@ HRESULT EEClass::AddMethodDesc(
                                                             classification,
                                                             TRUE, // fNonVtableSlot
                                                             TRUE, // fNativeCodeSlot
+                                                            FALSE, /* IsAsyncThunkMethod */
                                                             pMT,
                                                             &dummyAmTracker);
 
@@ -814,7 +815,8 @@ HRESULT EEClass::AddMethodDesc(
                                 TRUE,   // fEnC
                                 0,      // RVA - non-zero only for NDirect
                                 pImport,
-                                NULL
+                                NULL,
+                                Signature()
                                 COMMA_INDEBUG(debug_szMethodName)
                                 COMMA_INDEBUG(pMT->GetDebugClassName())
                                 COMMA_INDEBUG(NULL)
diff --git a/src/coreclr/vm/clsload.cpp b/src/coreclr/vm/clsload.cpp
index 06775e004629d6..8df5691cbafbe7 100644
--- a/src/coreclr/vm/clsload.cpp
+++ b/src/coreclr/vm/clsload.cpp
@@ -3083,7 +3083,7 @@ TypeHandle ClassLoader::PublishType(TypeKey *pTypeKey, TypeHandle typeHnd)
         {
             MethodDesc * pMD = it.GetMethodDesc();
             CONSISTENCY_CHECK(pMD != NULL && pMD->GetMethodTable() == pMT);
-            if (!pMD->IsUnboxingStub())
+            if (!pMD->IsUnboxingStub() && (!pMD->IsEEImpl() || pMD->GetMethodTable()->IsDelegate()))
             {
                 pModule->EnsuredStoreMethodDef(pMD->GetMemberDef(), pMD);
             }
diff --git a/src/coreclr/vm/dynamicmethod.cpp b/src/coreclr/vm/dynamicmethod.cpp
index 6bb94a6144c769..a7499e0708fac8 100644
--- a/src/coreclr/vm/dynamicmethod.cpp
+++ b/src/coreclr/vm/dynamicmethod.cpp
@@ -162,7 +162,7 @@ void DynamicMethodTable::AddMethodsToList()
     // allocate as many chunks as needed to hold the methods
     //
     MethodDescChunk* pChunk = MethodDescChunk::CreateChunk(pHeap, 0 /* one chunk of maximum size */,
-        mcDynamic, TRUE /* fNonVtableSlot */, TRUE /* fNativeCodeSlot */, m_pMethodTable, &amt);
+        mcDynamic, TRUE /* fNonVtableSlot */, TRUE /* fNativeCodeSlot */, FALSE /* IsAsyncThunkMethod */, m_pMethodTable, &amt);
     if (m_DynamicMethodList) RETURN;
 
     int methodCount = pChunk->GetCount();
diff --git a/src/coreclr/vm/genmeth.cpp b/src/coreclr/vm/genmeth.cpp
index 96bbc53a25e484..029e379305e4c2 100644
--- a/src/coreclr/vm/genmeth.cpp
+++ b/src/coreclr/vm/genmeth.cpp
@@ -91,6 +91,7 @@ static MethodDesc* CreateMethodDesc(LoaderAllocator *pAllocator,
                                      classification,
                                      TRUE /* fNonVtableSlot*/,
                                      fNativeCodeSlot,
+                                     pTemplateMD->IsAsyncThunkMethod(),
                                      pMT,
                                      pamTracker);
 
@@ -117,6 +118,10 @@ static MethodDesc* CreateMethodDesc(LoaderAllocator *pAllocator,
     {
         pMD->SetIsIntrinsic();
     }
+    if (pTemplateMD->IsAsyncThunkMethod())
+    {
+        pMD->SetIsAsyncThunkMethod();
+    }
 
 #ifdef FEATURE_METADATA_UPDATER
     if (pTemplateMD->IsEnCAddedMethod())
@@ -128,6 +133,11 @@ static MethodDesc* CreateMethodDesc(LoaderAllocator *pAllocator,
     pMD->SetMemberDef(token);
     pMD->SetSlot(pTemplateMD->GetSlot());
 
+    if (pTemplateMD->IsAsyncThunkMethod())
+    {
+        *pMD->GetAddrOfAsyncThunkData() = pTemplateMD->GetAsyncThunkData();
+    }
+
 #ifdef _DEBUG
     //<NICE> more info here</NICE>
     pMD->m_pszDebugMethodSignature = "<generic method signature>";
diff --git a/src/coreclr/vm/method.cpp b/src/coreclr/vm/method.cpp
index e34ee89ac8ae58..63b099dc54db58 100644
--- a/src/coreclr/vm/method.cpp
+++ b/src/coreclr/vm/method.cpp
@@ -82,6 +82,7 @@ const BYTE MethodDesc::s_ClassificationSizeTable[] = {
     // This extended part of the table is used for faster MethodDesc size lookup.
     // We index using optional slot flags into it
     METHOD_DESC_SIZES(sizeof(NonVtableSlot)),
+
     METHOD_DESC_SIZES(sizeof(MethodImpl)),
     METHOD_DESC_SIZES(sizeof(NonVtableSlot) + sizeof(MethodImpl)),
 
@@ -90,17 +91,14 @@ const BYTE MethodDesc::s_ClassificationSizeTable[] = {
     METHOD_DESC_SIZES(sizeof(MethodImpl) + sizeof(NativeCodeSlot)),
     METHOD_DESC_SIZES(sizeof(NonVtableSlot) + sizeof(MethodImpl) + sizeof(NativeCodeSlot)),
 
-#ifdef FEATURE_COMINTEROP
-    METHOD_DESC_SIZES(sizeof(ComPlusCallInfo)),
-    METHOD_DESC_SIZES(sizeof(NonVtableSlot) + sizeof(ComPlusCallInfo)),
-    METHOD_DESC_SIZES(sizeof(MethodImpl) + sizeof(ComPlusCallInfo)),
-    METHOD_DESC_SIZES(sizeof(NonVtableSlot) + sizeof(MethodImpl) + sizeof(ComPlusCallInfo)),
-
-    METHOD_DESC_SIZES(sizeof(NativeCodeSlot) + sizeof(ComPlusCallInfo)),
-    METHOD_DESC_SIZES(sizeof(NonVtableSlot) + sizeof(NativeCodeSlot) + sizeof(ComPlusCallInfo)),
-    METHOD_DESC_SIZES(sizeof(MethodImpl) + sizeof(NativeCodeSlot) + sizeof(ComPlusCallInfo)),
-    METHOD_DESC_SIZES(sizeof(NonVtableSlot) + sizeof(MethodImpl) + sizeof(NativeCodeSlot) + sizeof(ComPlusCallInfo))
-#endif
+    METHOD_DESC_SIZES(sizeof(AsyncThunkData)),
+    METHOD_DESC_SIZES(sizeof(NonVtableSlot) + sizeof(AsyncThunkData)),
+    METHOD_DESC_SIZES(sizeof(MethodImpl) + sizeof(AsyncThunkData)),
+    METHOD_DESC_SIZES(sizeof(NonVtableSlot) + sizeof(MethodImpl) + sizeof(AsyncThunkData)),
+    METHOD_DESC_SIZES(sizeof(NativeCodeSlot) + sizeof(AsyncThunkData)),
+    METHOD_DESC_SIZES(sizeof(NonVtableSlot) + sizeof(NativeCodeSlot) + sizeof(AsyncThunkData)),
+    METHOD_DESC_SIZES(sizeof(MethodImpl) + sizeof(NativeCodeSlot) + sizeof(AsyncThunkData)),
+    METHOD_DESC_SIZES(sizeof(NonVtableSlot) + sizeof(MethodImpl) + sizeof(NativeCodeSlot) + sizeof(AsyncThunkData)),
 };
 
 #ifndef FEATURE_COMINTEROP
@@ -135,7 +133,8 @@ SIZE_T MethodDesc::SizeOf()
         (mdcClassification
         | mdcHasNonVtableSlot
         | mdcMethodImpl
-        | mdcHasNativeCodeSlot)];
+        | mdcHasNativeCodeSlot
+        | mdcIsAsyncThunkMethod)];
 
     return size;
 }
@@ -978,6 +977,18 @@ PTR_PCODE MethodDesc::GetAddrOfNativeCodeSlot()
     return (PTR_PCODE)(dac_cast<TADDR>(this) + size);
 }
 
+//*******************************************************************************
+AsyncThunkData* MethodDesc::GetAddrOfAsyncThunkData()
+{
+    WRAPPER_NO_CONTRACT;
+
+    _ASSERTE(IsAsyncThunkMethod());
+
+    SIZE_T size = s_ClassificationSizeTable[m_wFlags & (mdcClassification | mdcHasNonVtableSlot |  mdcMethodImpl | mdcHasNativeCodeSlot)];
+
+    return (AsyncThunkData*)(dac_cast<TADDR>(this) + size);
+}
+
 //*******************************************************************************
 BOOL MethodDesc::IsVoid()
 {
@@ -1689,7 +1700,7 @@ MethodDesc* MethodDesc::StripMethodInstantiation()
 
 //*******************************************************************************
 MethodDescChunk *MethodDescChunk::CreateChunk(LoaderHeap *pHeap, DWORD methodDescCount,
-    DWORD classification, BOOL fNonVtableSlot, BOOL fNativeCodeSlot, MethodTable *pInitialMT, AllocMemTracker *pamTracker)
+    DWORD classification, BOOL fNonVtableSlot, BOOL fNativeCodeSlot, BOOL fAsyncThunkData, MethodTable *pInitialMT, AllocMemTracker *pamTracker)
 {
     CONTRACT(MethodDescChunk *)
     {
@@ -1713,6 +1724,9 @@ MethodDescChunk *MethodDescChunk::CreateChunk(LoaderHeap *pHeap, DWORD methodDes
     if (fNativeCodeSlot)
         oneSize += sizeof(MethodDesc::NativeCodeSlot);
 
+    if (fAsyncThunkData)
+        oneSize += sizeof(AsyncThunkData);
+
     _ASSERTE((oneSize & MethodDesc::ALIGNMENT_MASK) == 0);
 
     DWORD maxMethodDescsPerChunk = (DWORD)(MethodDescChunk::MaxSizeOfMethodDescs / oneSize);
@@ -1753,6 +1767,8 @@ MethodDescChunk *MethodDescChunk::CreateChunk(LoaderHeap *pHeap, DWORD methodDes
                 pMD->SetHasNonVtableSlot();
             if (fNativeCodeSlot)
                 pMD->SetHasNativeCodeSlot();
+            if (fAsyncThunkData)
+                pMD->SetIsAsyncThunkMethod();
 
             _ASSERTE(pMD->SizeOf() == oneSize);
 
diff --git a/src/coreclr/vm/method.hpp b/src/coreclr/vm/method.hpp
index 411129c6c6393e..21cf16c3e61f9f 100644
--- a/src/coreclr/vm/method.hpp
+++ b/src/coreclr/vm/method.hpp
@@ -51,11 +51,25 @@ GVAL_DECL(TADDR, g_MiniMetaDataBuffAddress);
 
 EXTERN_C VOID STDCALL NDirectImportThunk();
 
-#define METHOD_TOKEN_REMAINDER_BIT_COUNT 12
+#define METHOD_TOKEN_REMAINDER_BIT_COUNT 13
 #define METHOD_TOKEN_REMAINDER_MASK ((1 << METHOD_TOKEN_REMAINDER_BIT_COUNT) - 1)
 #define METHOD_TOKEN_RANGE_BIT_COUNT (24 - METHOD_TOKEN_REMAINDER_BIT_COUNT)
 #define METHOD_TOKEN_RANGE_MASK ((1 << METHOD_TOKEN_RANGE_BIT_COUNT) - 1)
 
+enum class AsyncThunkType
+{
+    NotAThunk,
+    TaskToAsync,
+    AsyncToTask
+};
+
+struct AsyncThunkData
+{
+    AsyncThunkType type;
+    Signature sig;
+};
+
+
 //=============================================================
 // Splits methoddef token into two pieces for
 // storage inside a methoddesc.
@@ -138,8 +152,8 @@ enum MethodDescClassification
     // Has slot for native code
     mdcHasNativeCodeSlot                = 0x0020,
 
-    // Method was added via Edit And Continue
-    mdcEnCAddedMethod                   = 0x0040,
+    // IsAsyncThunkMethod
+    mdcIsAsyncThunkMethod               = 0x0040,
 
     // Method is static
     mdcStatic                           = 0x0080,
@@ -1384,6 +1398,8 @@ class MethodDesc
     BOOL SetNativeCodeInterlocked(PCODE addr, PCODE pExpected = NULL);
 
     PTR_PCODE GetAddrOfNativeCodeSlot();
+    AsyncThunkData *GetAddrOfAsyncThunkData();
+    const AsyncThunkData& GetAsyncThunkData() { _ASSERTE(IsAsyncThunkMethod()); return *GetAddrOfAsyncThunkData(); }
 
     BOOL MayHaveNativeCode();
 
@@ -1622,8 +1638,11 @@ class MethodDesc
     enum {
         // There are flags available for use here (currently 4 flags bits are available); however, new bits are hard to come by, so any new flags bits should
         // have a fairly strong justification for existence.
-        enum_flag3_TokenRemainderMask                       = 0x0FFF, // This must equal METHOD_TOKEN_REMAINDER_MASK calculated higher in this file.
+        enum_flag3_TokenRemainderMask                       = 0x07FF, // This must equal METHOD_TOKEN_REMAINDER_MASK calculated higher in this file.
                                                                       // for this method.
+        // Method was added via Edit And Continue
+        enum_flag3_EnCAddedMethod                           = 0x0800,
+
         // enum_flag3_HasPrecode implies that enum_flag3_HasStableEntryPoint is set.
         enum_flag3_HasStableEntryPoint                      = 0x1000,   // The method entrypoint is stable (either precode or actual code)
         enum_flag3_HasPrecode                               = 0x2000,   // Precode has been allocated for this method
@@ -1683,17 +1702,29 @@ class MethodDesc
         m_wFlags |= mdcHasNativeCodeSlot;
     }
 
+    inline BOOL IsAsyncThunkMethod()
+    {
+        LIMITED_METHOD_DAC_CONTRACT;
+        return (m_wFlags & mdcIsAsyncThunkMethod) != 0;
+    }
+
+    inline void SetIsAsyncThunkMethod()
+    {
+        LIMITED_METHOD_CONTRACT;
+        m_wFlags |= mdcIsAsyncThunkMethod;
+    }
+
 #ifdef FEATURE_METADATA_UPDATER
     inline BOOL IsEnCAddedMethod()
     {
         LIMITED_METHOD_DAC_CONTRACT;
-        return (m_wFlags & mdcEnCAddedMethod) != 0;
+        return (m_wFlags3AndTokenRemainder & enum_flag3_EnCAddedMethod) != 0;
     }
 
     inline void SetIsEnCAddedMethod()
     {
         LIMITED_METHOD_CONTRACT;
-        m_wFlags |= mdcEnCAddedMethod;
+        m_wFlags3AndTokenRemainder |= enum_flag3_EnCAddedMethod;
     }
 #else
     inline BOOL IsEnCAddedMethod()
@@ -2143,6 +2174,7 @@ class MethodDescChunk
                                         DWORD classification,
                                         BOOL fNonVtableSlot,
                                         BOOL fNativeCodeSlot,
+                                        BOOL fAsyncThunkData,
                                         MethodTable *initialMT,
                                         class AllocMemTracker *pamTracker);
 
diff --git a/src/coreclr/vm/methodtablebuilder.cpp b/src/coreclr/vm/methodtablebuilder.cpp
index 44ddcf546064bb..f19b584842bf6c 100644
--- a/src/coreclr/vm/methodtablebuilder.cpp
+++ b/src/coreclr/vm/methodtablebuilder.cpp
@@ -994,9 +994,14 @@ MethodTableBuilder::bmtRTMethod::bmtRTMethod(
     MethodDesc *    pMD)
     : m_pOwningType(pOwningType),
       m_pMD(pMD),
-      m_methodSig(pMD->GetModule(),
-                  pMD->GetMemberDef(),
-                  &pOwningType->GetSubstitution())
+      m_methodSig(pMD->IsEEImpl()  // Handle Async2 methods
+       ? MethodSignature(pMD->GetModule(),
+                         pMD->GetMemberDef(),
+                         pMD->GetSignature(),
+                         &pOwningType->GetSubstitution())
+       : MethodSignature(pMD->GetModule(),
+                         pMD->GetMemberDef(),
+                         &pOwningType->GetSubstitution()))
 {
     CONTRACTL
     {
@@ -1038,6 +1043,39 @@ MethodTableBuilder::bmtMDMethod::bmtMDMethod(
         }
         CONTRACTL_END;
     }
+
+    MethodTableBuilder::bmtMDMethod::bmtMDMethod(
+    bmtMDType * pOwningType,
+    mdMethodDef tok,
+    DWORD dwDeclAttrs,
+    DWORD dwImplAttrs,
+    DWORD dwRVA,
+    Signature sig,
+    MethodClassification type,
+    METHOD_IMPL_TYPE implType)
+    : m_pOwningType(pOwningType),
+      m_dwDeclAttrs(dwDeclAttrs),
+      m_dwImplAttrs(dwImplAttrs),
+      m_dwRVA(dwRVA),
+      m_type(type),
+      m_implType(implType),
+      m_methodSig(pOwningType->GetModule(),
+                  tok,
+                  sig,
+                  &pOwningType->GetSubstitution()),
+      m_pMD(NULL),
+      m_pUnboxedMD(NULL),
+      m_slotIndex(INVALID_SLOT_INDEX),
+      m_unboxedSlotIndex(INVALID_SLOT_INDEX)
+    {
+        CONTRACTL
+        {
+            THROWS;
+            GC_TRIGGERS;
+            MODE_ANY;
+        }
+        CONTRACTL_END;
+    }
 //*******************************************************************************
 void
 MethodTableBuilder::ImportParentMethods()
@@ -2644,6 +2682,179 @@ HRESULT MethodTableBuilder::FindMethodDeclarationForMethodImpl(
 #pragma warning(push)
 #pragma warning(disable:21000) // Suppress PREFast warning about overly large function
 #endif // _PREFAST_
+
+enum class AsyncTaskMethod
+{
+    TaskReturningMethod,
+    Async2Method,
+    Async2MethodThatCannotBeImplementedByTask,
+    NormalMethod
+};
+
+void GetNameOfTypeDefOrRef(Module* pModule, mdToken tk, LPCSTR* pName, LPCSTR* pNamespace)
+{
+    *pName = "";
+    *pNamespace = "";
+    if (TypeFromToken(tk) == mdtTypeDef)
+    {
+        IfFailThrow(pModule->GetMDImport()->GetNameOfTypeDef(tk, pName, pNamespace));
+    }
+    else if (TypeFromToken(tk) == mdtTypeRef)
+    {
+        IfFailThrow(pModule->GetMDImport()->GetNameOfTypeRef(tk, pName, pNamespace));
+    }
+}
+
+bool IsTypeDefOrRefImplementedInSystemModule(Module* pModule, mdToken tk)
+{
+    if (TypeFromToken(tk) == mdtTypeDef)
+    {
+        if (pModule->IsSystem())
+        {
+            return true;
+        }
+    }
+    else if (TypeFromToken(tk) == mdtTypeRef)
+    {
+        mdToken tkTypeDef;
+        Module* pModuleOfTypeDef;
+
+        ClassLoader::ResolveTokenToTypeDefThrowing(pModule, tk, &pModuleOfTypeDef, &tkTypeDef);
+        if (pModuleOfTypeDef->IsSystem())
+        {
+            return true;
+        }
+    }
+
+    return false;
+}
+
+bool IsTypeDefOrRefAByRefStruct(Module* pModule, mdToken tk)
+{
+    if (TypeFromToken(tk) == mdtTypeRef)
+    {
+        if(!ClassLoader::ResolveTokenToTypeDefThrowing(pModule, tk, &pModule, &tk))
+        {
+            return false;
+        }
+    }
+
+    HRESULT hr = pModule->GetCustomAttribute(tk, 
+        WellKnownAttribute::IsByRefLike,
+        NULL, NULL);
+
+    if (hr == S_OK)
+        return true;
+    
+    return false;
+}
+
+AsyncTaskMethod ClassifyAsyncMethod(SigPointer sig, Module* pModule, ULONG* offsetOfAsyncDetails)
+{
+    PCCOR_SIGNATURE initialSig = sig.GetPtr();
+    uint32_t data;
+    IfFailThrow(sig.GetCallingConvInfo(&data));
+    if (data & IMAGE_CEE_CS_CALLCONV_GENERIC)
+    {
+        // Skip over generic argument count
+        IfFailThrow(sig.GetData(&data));
+    }
+
+    // Return argument count
+    IfFailThrow(sig.GetData(&data));
+
+    // Now we should be parsing the return type
+
+    // If the first custommodifier is a MOD_OPT to CallConvAsync2Call
+    // Then this is a async2 function
+    CorElementType elemType;
+    *offsetOfAsyncDetails = (ULONG)(sig.GetPtr() - initialSig);
+    IfFailThrow(sig.GetElemType(&elemType));
+    LPCSTR name, _namespace;
+    mdToken tk;
+    while ((elemType == ELEMENT_TYPE_CMOD_OPT) || (elemType == ELEMENT_TYPE_CMOD_REQD))
+    {
+        IfFailThrow(sig.GetToken(&tk));
+        GetNameOfTypeDefOrRef(pModule, tk, &name, &_namespace);
+        *offsetOfAsyncDetails = (ULONG)(sig.GetPtr() - initialSig);
+        IfFailThrow(sig.GetElemType(&elemType));
+        if (strcmp(name, "Task`1") == 0 && strcmp(_namespace, "System.Threading.Tasks") == 0 && IsTypeDefOrRefImplementedInSystemModule(pModule, tk))
+        {
+            // This must have been the last CMOD before the element type, and the element type MUST be one which can be expressed structurally as a generic parameter
+            switch (elemType)
+            {
+                case ELEMENT_TYPE_CLASS:
+                case ELEMENT_TYPE_VALUETYPE:
+                case ELEMENT_TYPE_STRING:
+                case ELEMENT_TYPE_OBJECT:
+                case ELEMENT_TYPE_I:
+                case ELEMENT_TYPE_U:
+                case ELEMENT_TYPE_I1:
+                case ELEMENT_TYPE_U1:
+                case ELEMENT_TYPE_I2:
+                case ELEMENT_TYPE_U2:
+                case ELEMENT_TYPE_I4:
+                case ELEMENT_TYPE_U4:
+                case ELEMENT_TYPE_I8:
+                case ELEMENT_TYPE_U8:
+                case ELEMENT_TYPE_R4:
+                case ELEMENT_TYPE_R8:
+                case ELEMENT_TYPE_BOOLEAN:
+                case ELEMENT_TYPE_CHAR:
+                case ELEMENT_TYPE_ARRAY:
+                case ELEMENT_TYPE_SZARRAY:
+                case ELEMENT_TYPE_VAR:
+                case ELEMENT_TYPE_MVAR:
+                    return AsyncTaskMethod::Async2Method;
+                case ELEMENT_TYPE_TYPEDBYREF:
+                    return AsyncTaskMethod::Async2MethodThatCannotBeImplementedByTask;
+                case ELEMENT_TYPE_GENERICINST:
+                    IfFailThrow(sig.GetElemType(&elemType));
+                    if (elemType == ELEMENT_TYPE_CLASS)
+                    {
+                        return AsyncTaskMethod::Async2Method;
+                    }
+                    else
+                    {
+                        IfFailThrow(sig.GetToken(&tk));
+                        if (IsTypeDefOrRefAByRefStruct(pModule, tk))
+                        {
+                            return AsyncTaskMethod::Async2MethodThatCannotBeImplementedByTask;
+                        }
+                        else
+                        {
+                            return AsyncTaskMethod::Async2Method;
+                        }
+                    }
+                default:
+                    ThrowHR(COR_E_BADIMAGEFORMAT);
+            }
+        }
+    }
+
+    if (elemType == ELEMENT_TYPE_GENERICINST)
+    {
+        IfFailThrow(sig.GetElemType(&elemType));
+        if (elemType == ELEMENT_TYPE_VALUETYPE)
+        {
+            return AsyncTaskMethod::NormalMethod; // Task<T> is a class, so we can't be that if we get here
+        }
+        IfFailThrow(sig.GetToken(&tk));
+        IfFailThrow(sig.GetData(&data));
+        if (data == 1)
+        {
+            // This might be System.Threading.Tasks.Task`1
+            GetNameOfTypeDefOrRef(pModule, tk, &name, &_namespace);
+            if (strcmp(name, "Task`1") == 0 && strcmp(_namespace, "System.Threading.Tasks") == 0)
+            {
+                if (IsTypeDefOrRefImplementedInSystemModule(pModule, tk))
+                    return AsyncTaskMethod::TaskReturningMethod;
+            }
+        }
+    }
+    return AsyncTaskMethod::NormalMethod;
+}
+
 //---------------------------------------------------------------------------------------
 //
 // Used by BuildMethodTable
@@ -2699,7 +2910,7 @@ MethodTableBuilder::EnumerateClassMethods()
     if ((DWORD)MAX_SLOT_INDEX <= cMethAndGaps)
         BuildMethodTableThrowException(IDS_CLASSLOAD_TOO_MANY_METHODS);
 
-    bmtMethod->m_cMaxDeclaredMethods = (SLOT_INDEX)cMethAndGaps;
+    bmtMethod->m_cMaxDeclaredMethods = (SLOT_INDEX)cMethAndGaps * 2;
     bmtMethod->m_cDeclaredMethods = 0;
     bmtMethod->m_rgDeclaredMethods = new (GetStackingAllocator())
         bmtMDMethod *[bmtMethod->m_cMaxDeclaredMethods];
@@ -2728,7 +2939,7 @@ MethodTableBuilder::EnumerateClassMethods()
         {
             BuildMethodTableThrowException(BFA_METHOD_TOKEN_OUT_OF_RANGE);
         }
-        if (!bmtProp->fNoSanityChecks && FAILED(pMDInternalImport->GetSigOfMethodDef(tok, &cMemberSignature, &pMemberSignature)))
+        if (FAILED(pMDInternalImport->GetSigOfMethodDef(tok, &cMemberSignature, &pMemberSignature)))
         {
             BuildMethodTableThrowException(hr, BFA_BAD_SIGNATURE, mdMethodDefNil);
         }
@@ -2774,6 +2985,12 @@ MethodTableBuilder::EnumerateClassMethods()
             }
         }
 
+        SigParser sig(pMemberSignature, cMemberSignature);
+        ULONG offsetOfAsyncDetails = 0;
+        AsyncTaskMethod asyncMethodType;
+        
+        asyncMethodType = IsDelegate() ? AsyncTaskMethod::NormalMethod : ClassifyAsyncMethod(sig, GetModule(), &offsetOfAsyncDetails);
+
         bool hasGenericMethodArgsComputed;
         bool hasGenericMethodArgs = this->GetModule()->m_pMethodIsGenericMap->IsGeneric(tok, &hasGenericMethodArgsComputed);
         if (!hasGenericMethodArgsComputed)
@@ -3310,28 +3527,79 @@ MethodTableBuilder::EnumerateClassMethods()
         // Create a new bmtMDMethod representing this method and add it to the
         // declared method list.
         //
+        for (int insertCount = 0; insertCount < 2; insertCount++)
+        {
+            bmtMDMethod * pNewMethod;
+            if (insertCount == 0)
+            {
+                pNewMethod = new (GetStackingAllocator()) bmtMDMethod(
+                    bmtInternal->pType,
+                    tok,
+                    dwMemberAttrs,
+                    dwImplFlags,
+                    dwMethodRVA,
+                    type,
+                    implType);
+            }
+            else
+            {
+                ULONG cAsyncThunkMemberSignature = cMemberSignature;
 
-        bmtMDMethod * pNewMethod = new (GetStackingAllocator()) bmtMDMethod(
-            bmtInternal->pType,
-            tok,
-            dwMemberAttrs,
-            dwImplFlags,
-            dwMethodRVA,
-            type,
-            implType);
+                if (asyncMethodType == AsyncTaskMethod::Async2Method)
+                    cAsyncThunkMemberSignature += 1;
+                else
+                    cAsyncThunkMemberSignature -= 1;
 
-        bmtMethod->AddDeclaredMethod(pNewMethod);
+                BYTE* pNewMemberSignature = AllocateFromHighFrequencyHeap(S_SIZE_T(cAsyncThunkMemberSignature));
+                ULONG tokenLen = CorSigUncompressedDataSize(&pMemberSignature[offsetOfAsyncDetails + 1]);
+                ULONG initialCopyLen = offsetOfAsyncDetails + 1 + tokenLen;
+                memcpy(pNewMemberSignature, pMemberSignature, initialCopyLen);
 
-        //
-        // Update the count of the various types of methods.
-        //
+                if (asyncMethodType == AsyncTaskMethod::Async2Method)
+                {
+                    // Replace the ELEMENT_TYPE_CMOD with ELEMENT_TYPE_GENERICINST, and then add a 1 after the token which refers to Task<T>
+                    pNewMemberSignature[offsetOfAsyncDetails] = ELEMENT_TYPE_GENERICINST;
+                    pNewMemberSignature[initialCopyLen] = 1;
+                    memcpy(pNewMemberSignature + initialCopyLen + 1, pMemberSignature + initialCopyLen, cMemberSignature - initialCopyLen);
+                }
+                else
+                {
+                    // Replace the ELEMENT_TYPE_GENERICINST with ELEMENT_TYPE_CMOD_OPT, and then remove the 1 which specifies the generic arg count for Task<T>
+                    pNewMemberSignature[offsetOfAsyncDetails] = ELEMENT_TYPE_CMOD_OPT;
+                    memcpy(pNewMemberSignature + initialCopyLen, pMemberSignature + initialCopyLen + 1, cMemberSignature - (initialCopyLen + 1));
+                }
+
+                Signature newMemberSig(pNewMemberSignature, cAsyncThunkMemberSignature);
+                pNewMethod = new (GetStackingAllocator()) bmtMDMethod(
+                    bmtInternal->pType,
+                    tok,
+                    dwMemberAttrs,
+                    dwImplFlags,
+                    dwMethodRVA,
+                    newMemberSig,
+                    type,
+                    implType);
+            }
 
-        bmtVT->dwMaxVtableSize++;
+            bmtMethod->AddDeclaredMethod(pNewMethod);
 
-        // Increment the number of non-abstract declared methods
-        if (!IsMdAbstract(dwMemberAttrs))
-        {
-            bmtMethod->dwNumDeclaredNonAbstractMethods++;
+            //
+            // Update the count of the various types of methods.
+            //
+
+            bmtVT->dwMaxVtableSize++;
+
+            // Increment the number of non-abstract declared methods
+            if (!IsMdAbstract(dwMemberAttrs))
+            {
+                bmtMethod->dwNumDeclaredNonAbstractMethods++;
+            }
+
+            // Normal methods only insert a single method
+            if ((asyncMethodType == AsyncTaskMethod::NormalMethod) || (asyncMethodType == AsyncTaskMethod::Async2MethodThatCannotBeImplementedByTask))
+            {
+                break;
+            }
         }
     }
 
@@ -5162,6 +5430,9 @@ MethodTableBuilder::InitNewMethodDesc(
     if (NeedsNativeCodeSlot(pMethod))
         pNewMD->SetHasNativeCodeSlot();
 
+    if (pMethod->GetAsyncThunkType() != AsyncThunkType::NotAThunk)
+        pNewMD->SetIsAsyncThunkMethod();
+
     // Now we know the classification we can allocate the correct type of
     // method desc and perform any classification specific initialization.
 
@@ -5189,6 +5460,12 @@ MethodTableBuilder::InitNewMethodDesc(
     strcpy_s((char *) pszDebugMethodNameCopy, len, pszDebugMethodName);
 #endif // _DEBUG
 
+    Signature sig;
+    if (pMethod->GetAsyncThunkType() != AsyncThunkType::NotAThunk)
+    {
+        sig = pMethod->GetMethodSignature().GetSignatureClass();
+    }
+
     // Do the init specific to each classification of MethodDesc & assign some common fields
     InitMethodDesc(pNewMD,
                    pMethod->GetMethodType(),
@@ -5198,7 +5475,9 @@ MethodTableBuilder::InitNewMethodDesc(
                    FALSE,    // fEnC
                    pMethod->GetRVA(),
                    GetMDImport(),
-                   pName
+                   pName,
+                   sig,
+                   pMethod->GetAsyncThunkType()
                    COMMA_INDEBUG(pszDebugMethodNameCopy)
                    COMMA_INDEBUG(GetDebugClassName())
                    COMMA_INDEBUG("") // FIX this happens on global methods, give better info
@@ -6070,7 +6349,9 @@ MethodTableBuilder::InitMethodDesc(
     BOOL                fEnC,
     DWORD               RVA,        // Only needed for NDirect case
     IMDInternalImport * pIMDII,     // Needed for NDirect, EEImpl(Delegate) cases
-    LPCSTR              pMethodName // Only needed for mcEEImpl (Delegate) case
+    LPCSTR              pMethodName, // Only needed for mcEEImpl (Delegate) case
+    Signature           sig, // Only needed for the Async2 Thunk case
+    AsyncThunkType      thunkType
     COMMA_INDEBUG(LPCUTF8 pszDebugMethodName)
     COMMA_INDEBUG(LPCUTF8 pszDebugClassName)
     COMMA_INDEBUG(LPCUTF8 pszDebugMethodSignature)
@@ -6249,6 +6530,13 @@ MethodTableBuilder::InitMethodDesc(
     else
         pNewMD->m_pszDebugMethodSignature = pszDebugMethodSignature;
 #endif // _DEBUG
+
+    if (thunkType != AsyncThunkType::NotAThunk)
+    {
+        AsyncThunkData* pThunkData = pNewMD->GetAddrOfAsyncThunkData();
+        pThunkData->sig = sig;
+        pThunkData->type = thunkType;
+    }
 } // MethodTableBuilder::InitMethodDesc
 
 //*******************************************************************************
@@ -6973,6 +7261,9 @@ VOID MethodTableBuilder::AllocAndInitMethodDescs()
 
         if (NeedsNativeCodeSlot(*it))
             size += sizeof(MethodDesc::NativeCodeSlot);
+        
+        if (it->GetAsyncThunkType() != AsyncThunkType::NotAThunk)
+            size += sizeof(AsyncThunkData);
 
         // See comment in AllocAndInitMethodDescChunk
         if (NeedsTightlyBoundUnboxingStub(*it))
diff --git a/src/coreclr/vm/methodtablebuilder.h b/src/coreclr/vm/methodtablebuilder.h
index 29889045b44795..a83402618e3ad3 100644
--- a/src/coreclr/vm/methodtablebuilder.h
+++ b/src/coreclr/vm/methodtablebuilder.h
@@ -674,6 +674,31 @@ class MethodTableBuilder
                 INDEBUG(CheckGetMethodAttributes();)
             }
 
+        //-----------------------------------------------------------------------------------------
+        // This constructor can be used with hard-coded signatures that are used for
+        // locating .ctor and .cctor methods.
+        MethodSignature(
+            Module *             pModule,
+            mdToken              tok,
+            Signature            sig,
+            const Substitution * pSubst)
+            : m_pModule(pModule),
+              m_tok(tok),
+              m_szName(NULL),
+              m_pSig(sig.GetRawSig()),
+              m_cSig(sig.GetRawSigLen()),
+              m_pSubst(pSubst),
+              m_nameHash(INVALID_NAME_HASH)
+            {
+                CONTRACTL {
+                    PRECONDITION(CheckPointer(pModule));
+                    PRECONDITION(TypeFromToken(tok) == mdtMethodDef ||
+                                 TypeFromToken(tok) == mdtMemberRef);
+                    PRECONDITION(CheckPointer(m_pSig));
+                    PRECONDITION(m_cSig != 0);
+                } CONTRACTL_END;
+            }
+
         //-----------------------------------------------------------------------------------------
         // This constructor can be used with hard-coded signatures that are used for
         // locating .ctor and .cctor methods.
@@ -745,6 +770,11 @@ class MethodTableBuilder
         GetSignature() const
             { WRAPPER_NO_CONTRACT; CheckGetMethodAttributes(); return m_pSig; }
 
+        //-----------------------------------------------------------------------------------------
+        // Returns the metadata signature for the method.
+        inline Signature GetSignatureClass() const
+            { WRAPPER_NO_CONTRACT; CheckGetMethodAttributes(); return Signature(m_pSig, (ULONG)m_cSig); }
+
         //-----------------------------------------------------------------------------------------
         // Returns the signature length.
         inline size_t
@@ -916,6 +946,22 @@ class MethodTableBuilder
             MethodClassification type,
             METHOD_IMPL_TYPE implType);
 
+        //-----------------------------------------------------------------------------------------
+        // Constructor. This takes all the information already extracted from metadata interface
+        // because the place that creates these types already has this data. Alternatively,
+        // a constructor could be written to take a token and metadata scope instead. Also,
+        // it might be interesting to move MethodClassification and METHOD_IMPL_TYPE to setter functions.
+        bmtMDMethod(
+            bmtMDType * pOwningType,
+            mdMethodDef tok,
+            DWORD dwDeclAttrs,
+            DWORD dwImplAttrs,
+            DWORD dwRVA,
+            Signature sig,
+            AsyncThunkType thunkType,
+            MethodClassification type,
+            METHOD_IMPL_TYPE implType);
+
         //-----------------------------------------------------------------------------------------
         // Returns the type that owns the *declaration* of this method. This makes sure that a
         // method can be properly interpreted in the context of substitutions at any time.
@@ -1016,6 +1062,12 @@ class MethodTableBuilder
         GetRVA() const
             { LIMITED_METHOD_CONTRACT; return m_dwRVA; }
 
+        AsyncThunkType GetAsyncThunkType() const
+        {
+            LIMITED_METHOD_CONTRACT;
+            return m_asyncThunkType;
+        }
+
     private:
         //-----------------------------------------------------------------------------------------
         bmtMDType *       m_pOwningType;
@@ -1024,6 +1076,7 @@ class MethodTableBuilder
         DWORD             m_dwImplAttrs;
         DWORD             m_dwRVA;
         MethodClassification  m_type;               // Specific MethodDesc flavour
+        AsyncThunkType m_asyncThunkType;
         METHOD_IMPL_TYPE  m_implType;           // Whether or not the method is a methodImpl body
         MethodSignature   m_methodSig;
 
@@ -2613,7 +2666,9 @@ class MethodTableBuilder
         BOOL                fEnC,
         DWORD               RVA,          // Only needed for NDirect case
         IMDInternalImport * pIMDII,  // Needed for NDirect, EEImpl(Delegate) cases
-        LPCSTR              pMethodName // Only needed for mcEEImpl (Delegate) case
+        LPCSTR              pMethodName, // Only needed for mcEEImpl (Delegate) case
+        Signature           sig, // Only needed for the async thunk (Async2 Thunk) case
+        AsyncThunkType      thunkType
         COMMA_INDEBUG(LPCUTF8             pszDebugMethodName)
         COMMA_INDEBUG(LPCUTF8             pszDebugClassName)
         COMMA_INDEBUG(LPCUTF8             pszDebugMethodSignature));

From 783754de771547aa26cd85f9428b32c08ab383f3 Mon Sep 17 00:00:00 2001
From: David Wrighton <davidwr@microsoft.com>
Date: Mon, 18 Sep 2023 13:30:07 -0700
Subject: [PATCH 002/203] more tweaks

---
 src/coreclr/vm/method.hpp | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/coreclr/vm/method.hpp b/src/coreclr/vm/method.hpp
index 21cf16c3e61f9f..9a9931d26b00a8 100644
--- a/src/coreclr/vm/method.hpp
+++ b/src/coreclr/vm/method.hpp
@@ -51,7 +51,7 @@ GVAL_DECL(TADDR, g_MiniMetaDataBuffAddress);
 
 EXTERN_C VOID STDCALL NDirectImportThunk();
 
-#define METHOD_TOKEN_REMAINDER_BIT_COUNT 13
+#define METHOD_TOKEN_REMAINDER_BIT_COUNT 11
 #define METHOD_TOKEN_REMAINDER_MASK ((1 << METHOD_TOKEN_REMAINDER_BIT_COUNT) - 1)
 #define METHOD_TOKEN_RANGE_BIT_COUNT (24 - METHOD_TOKEN_REMAINDER_BIT_COUNT)
 #define METHOD_TOKEN_RANGE_MASK ((1 << METHOD_TOKEN_RANGE_BIT_COUNT) - 1)
@@ -2157,7 +2157,7 @@ class MethodDescChunk
     friend class CheckAsmOffsets;
 
     enum {
-        enum_flag_TokenRangeMask                           = 0x0FFF, // This must equal METHOD_TOKEN_RANGE_MASK calculated higher in this file
+        enum_flag_TokenRangeMask                           = 0x1FFF, // This must equal METHOD_TOKEN_RANGE_MASK calculated higher in this file
                                                                      // These are separate to allow the flags space available and used to be obvious here
                                                                      // and for the logic that splits the token to be algorithmically generated based on the
                                                                      // #define

From 8c43a9a211a9a182e8019dc1aea70883ce1e8e4c Mon Sep 17 00:00:00 2001
From: David Wrighton <davidwr@microsoft.com>
Date: Mon, 18 Sep 2023 15:40:04 -0700
Subject: [PATCH 003/203] It builds

---
 src/coreclr/vm/class.cpp              |  3 ++-
 src/coreclr/vm/ilstubcache.cpp        |  1 +
 src/coreclr/vm/methodtablebuilder.cpp | 11 +++++++++++
 3 files changed, 14 insertions(+), 1 deletion(-)

diff --git a/src/coreclr/vm/class.cpp b/src/coreclr/vm/class.cpp
index 296c6686529dbd..cda2d4ebd18f87 100644
--- a/src/coreclr/vm/class.cpp
+++ b/src/coreclr/vm/class.cpp
@@ -816,7 +816,8 @@ HRESULT EEClass::AddMethodDesc(
                                 0,      // RVA - non-zero only for NDirect
                                 pImport,
                                 NULL,
-                                Signature()
+                                Signature(),
+                                AsyncThunkType::NotAThunk
                                 COMMA_INDEBUG(debug_szMethodName)
                                 COMMA_INDEBUG(pMT->GetDebugClassName())
                                 COMMA_INDEBUG(NULL)
diff --git a/src/coreclr/vm/ilstubcache.cpp b/src/coreclr/vm/ilstubcache.cpp
index 4c955e322bb73e..c467281ba830f7 100644
--- a/src/coreclr/vm/ilstubcache.cpp
+++ b/src/coreclr/vm/ilstubcache.cpp
@@ -185,6 +185,7 @@ MethodDesc* ILStubCache::CreateNewMethodDesc(LoaderHeap* pCreationHeap, MethodTa
                                                            mcDynamic,
                                                            TRUE /* fNonVtableSlot */,
                                                            TRUE /* fNativeCodeSlot */,
+                                                           FALSE /* IsAsyncThunkMethod */,
                                                            pMT,
                                                            pamTracker);
 
diff --git a/src/coreclr/vm/methodtablebuilder.cpp b/src/coreclr/vm/methodtablebuilder.cpp
index f19b584842bf6c..4bf429f6f85b97 100644
--- a/src/coreclr/vm/methodtablebuilder.cpp
+++ b/src/coreclr/vm/methodtablebuilder.cpp
@@ -1026,6 +1026,7 @@ MethodTableBuilder::bmtMDMethod::bmtMDMethod(
       m_dwImplAttrs(dwImplAttrs),
       m_dwRVA(dwRVA),
       m_type(type),
+      m_asyncThunkType(AsyncThunkType::NotAThunk),
       m_implType(implType),
       m_methodSig(pOwningType->GetModule(),
                   tok,
@@ -1051,6 +1052,7 @@ MethodTableBuilder::bmtMDMethod::bmtMDMethod(
     DWORD dwImplAttrs,
     DWORD dwRVA,
     Signature sig,
+    AsyncThunkType thunkType,
     MethodClassification type,
     METHOD_IMPL_TYPE implType)
     : m_pOwningType(pOwningType),
@@ -1058,6 +1060,7 @@ MethodTableBuilder::bmtMDMethod::bmtMDMethod(
       m_dwImplAttrs(dwImplAttrs),
       m_dwRVA(dwRVA),
       m_type(type),
+      m_asyncThunkType(thunkType),
       m_implType(implType),
       m_methodSig(pOwningType->GetModule(),
                   tok,
@@ -3544,11 +3547,18 @@ MethodTableBuilder::EnumerateClassMethods()
             else
             {
                 ULONG cAsyncThunkMemberSignature = cMemberSignature;
+                AsyncThunkType thunkType;
 
                 if (asyncMethodType == AsyncTaskMethod::Async2Method)
+                {
                     cAsyncThunkMemberSignature += 1;
+                    thunkType = AsyncThunkType::AsyncToTask;
+                }
                 else
+                {
                     cAsyncThunkMemberSignature -= 1;
+                    thunkType = AsyncThunkType::TaskToAsync;
+                }
 
                 BYTE* pNewMemberSignature = AllocateFromHighFrequencyHeap(S_SIZE_T(cAsyncThunkMemberSignature));
                 ULONG tokenLen = CorSigUncompressedDataSize(&pMemberSignature[offsetOfAsyncDetails + 1]);
@@ -3577,6 +3587,7 @@ MethodTableBuilder::EnumerateClassMethods()
                     dwImplFlags,
                     dwMethodRVA,
                     newMemberSig,
+                    thunkType,
                     type,
                     implType);
             }

From 545509cd35208eaad1b1166481c01fbde6a8e7e3 Mon Sep 17 00:00:00 2001
From: David Wrighton <davidwr@microsoft.com>
Date: Tue, 19 Sep 2023 16:17:17 -0700
Subject: [PATCH 004/203] ItCompilesButConvertingFromAsyncVariantsIsntRight

---
 src/coreclr/debug/ee/functioninfo.cpp |  2 +-
 src/coreclr/inc/corcompile.h          |  1 +
 src/coreclr/vm/clrtocomcall.cpp       | 10 +++++
 src/coreclr/vm/clsload.cpp            |  2 +-
 src/coreclr/vm/commodule.cpp          |  4 +-
 src/coreclr/vm/commtmemberinfomap.cpp |  7 ++++
 src/coreclr/vm/comtoclrcall.cpp       |  5 ++-
 src/coreclr/vm/dispatchinfo.cpp       | 15 +++++++
 src/coreclr/vm/dllimport.cpp          | 22 +++++++++++
 src/coreclr/vm/encee.cpp              |  1 +
 src/coreclr/vm/genericdict.cpp        | 17 ++++++++
 src/coreclr/vm/genmeth.cpp            | 56 ++++++++++++++++++---------
 src/coreclr/vm/instmethhash.cpp       | 10 ++++-
 src/coreclr/vm/instmethhash.h         |  3 +-
 src/coreclr/vm/interoputil.cpp        |  7 ++++
 src/coreclr/vm/jitinterface.cpp       |  4 ++
 src/coreclr/vm/memberload.cpp         |  1 +
 src/coreclr/vm/method.cpp             | 10 ++++-
 src/coreclr/vm/method.hpp             | 17 +++++++-
 src/coreclr/vm/methoditer.cpp         | 19 ++++++++-
 src/coreclr/vm/methoditer.h           |  4 ++
 src/coreclr/vm/methodtable.cpp        | 56 ++++++++++++++++++++++++---
 src/coreclr/vm/methodtable.h          |  3 +-
 src/coreclr/vm/methodtablebuilder.cpp | 13 ++++++-
 src/coreclr/vm/methodtablebuilder.h   | 27 ++++++++++++-
 src/coreclr/vm/multicorejit.cpp       |  6 +++
 src/coreclr/vm/readytoruninfo.cpp     |  8 ++++
 src/coreclr/vm/zapsig.cpp             |  5 ++-
 28 files changed, 295 insertions(+), 40 deletions(-)

diff --git a/src/coreclr/debug/ee/functioninfo.cpp b/src/coreclr/debug/ee/functioninfo.cpp
index fad9ac786ba714..91c5753ea432a2 100644
--- a/src/coreclr/debug/ee/functioninfo.cpp
+++ b/src/coreclr/debug/ee/functioninfo.cpp
@@ -2035,7 +2035,7 @@ void DebuggerMethodInfo::CreateDJIsForNativeBlobs(AppDomain * pAppDomain, Module
         // have DJIs for every verision of a method that was EnCed.
         // This also handles the possibility of getting the same methoddesc back from the iterator.
         // It also lets EnC + generics play nice together (including if an generic method was EnC-ed)
-        LoadedMethodDescIterator it(pAppDomain, m_module, m_token);
+        LoadedMethodDescIterator it(pAppDomain, m_module, m_token, /* fIsAsyncThunk */false); // TODO! Debugger doesn't handle async thunks now
         CollectibleAssemblyHolder<DomainAssembly *> pDomainAssembly;
         while (it.Next(pDomainAssembly.This()))
         {
diff --git a/src/coreclr/inc/corcompile.h b/src/coreclr/inc/corcompile.h
index e4baf3423fca28..f819dec6d3feb6 100644
--- a/src/coreclr/inc/corcompile.h
+++ b/src/coreclr/inc/corcompile.h
@@ -182,6 +182,7 @@ enum EncodeMethodSigFlags
     ENCODE_METHOD_SIG_Constrained               = 0x20,
     ENCODE_METHOD_SIG_OwnerType                 = 0x40,
     ENCODE_METHOD_SIG_UpdateContext             = 0x80,
+    ENCODE_METHOD_SIG_AsyncThunk                = 0x100,
 };
 
 enum EncodeFieldSigFlags
diff --git a/src/coreclr/vm/clrtocomcall.cpp b/src/coreclr/vm/clrtocomcall.cpp
index c604a6c8a90116..d415db47f5febe 100644
--- a/src/coreclr/vm/clrtocomcall.cpp
+++ b/src/coreclr/vm/clrtocomcall.cpp
@@ -230,6 +230,11 @@ I4ARRAYREF SetUpWrapperInfo(MethodDesc *pMD)
 
         GCX_PREEMP();
 
+        if (pMD->IsAsyncThunkMethod())
+        {
+            ThrowHR(COR_E_NOTSUPPORTED);
+        }
+
         // Collects ParamDef information in an indexed array where element 0 represents
         // the return type.
         mdParamDef *params = (mdParamDef*)_alloca((numArgs+1) * sizeof(mdParamDef));
@@ -504,6 +509,11 @@ UINT32 CLRToCOMLateBoundWorker(
     LPCUTF8 strMemberName;
     ULONG uSemantic;
 
+    if (pItfMD->IsAsyncThunkMethod())
+    {
+        ThrowHR(COR_E_NOTSUPPORTED);
+    }
+
     // See if there is property information for this member.
     hr = pItfMT->GetModule()->GetPropertyInfoForMethodDef(pItfMD->GetMemberDef(), &propToken, &strMemberName, &uSemantic);
     if (hr != S_OK)
diff --git a/src/coreclr/vm/clsload.cpp b/src/coreclr/vm/clsload.cpp
index 8df5691cbafbe7..2fd13442f2ae1a 100644
--- a/src/coreclr/vm/clsload.cpp
+++ b/src/coreclr/vm/clsload.cpp
@@ -3083,7 +3083,7 @@ TypeHandle ClassLoader::PublishType(TypeKey *pTypeKey, TypeHandle typeHnd)
         {
             MethodDesc * pMD = it.GetMethodDesc();
             CONSISTENCY_CHECK(pMD != NULL && pMD->GetMethodTable() == pMT);
-            if (!pMD->IsUnboxingStub() && (!pMD->IsEEImpl() || pMD->GetMethodTable()->IsDelegate()))
+            if (!pMD->IsUnboxingStub() && !pMD->IsAsyncThunkMethod())
             {
                 pModule->EnsuredStoreMethodDef(pMD->GetMemberDef(), pMD);
             }
diff --git a/src/coreclr/vm/commodule.cpp b/src/coreclr/vm/commodule.cpp
index 46e66b5e4a6d75..6096eb74409fc3 100644
--- a/src/coreclr/vm/commodule.cpp
+++ b/src/coreclr/vm/commodule.cpp
@@ -302,7 +302,7 @@ extern "C" INT32 QCALLTYPE ModuleBuilder_GetMemberRefOfMethodInfo(QCall::ModuleH
         COMPlusThrow(kNotSupportedException);
     }
 
-    if (pMeth->GetMethodTable()->GetModule() == pModule)
+    if ((pMeth->GetMethodTable()->GetModule() == pModule) && !pMeth->IsAsyncThunkMethod())
     {
         // If the passed in method is defined in the same module, just return the MethodDef token
         memberRefE = pMeth->GetMemberDef();
@@ -317,7 +317,7 @@ extern "C" INT32 QCALLTYPE ModuleBuilder_GetMemberRefOfMethodInfo(QCall::ModuleH
 
         ULONG           cbComSig;
         PCCOR_SIGNATURE pvComSig;
-        IfFailThrow(pMeth->GetMDImport()->GetSigOfMethodDef(pMeth->GetMemberDef(), &cbComSig, &pvComSig));
+        pMeth->GetSig(&pvComSig, &cbComSig);
 
         // Translate the method sig into this scope
         Assembly * pRefedAssembly = pMeth->GetModule()->GetAssembly();
diff --git a/src/coreclr/vm/commtmemberinfomap.cpp b/src/coreclr/vm/commtmemberinfomap.cpp
index 8bc185e9d81a4c..015aab987d5aa4 100644
--- a/src/coreclr/vm/commtmemberinfomap.cpp
+++ b/src/coreclr/vm/commtmemberinfomap.cpp
@@ -688,6 +688,9 @@ void ComMTMemberInfoMap::GetMethodPropsForMeth(
     // Generally don't munge function into a getter.
     rProps[ix].bFunction2Getter = FALSE;
 
+    if (pMeth->IsAsyncThunkMethod())
+        ThrowHR(COR_E_NOTSUPPORTED);
+
     // See if there is property information for this member.
     hr = pMeth->GetModule()->GetPropertyInfoForMethodDef(pMeth->GetMemberDef(), &pd, &pPropName, &uSemantic);
     IfFailThrow(hr);
@@ -1604,6 +1607,10 @@ void ComMTMemberInfoMap::PopulateMemberHashtable()
 
             // We are dealing with a method.
             MethodDesc *pMD = pProps->pMeth;
+            if (pMD->IsAsyncThunkMethod())
+            {
+                ThrowHR(COR_E_NOTSUPPORTED); // Probably this isn't right, and instead should be a skip, but a throw makes it easier to find if this is wrong
+            }
             EEModuleTokenPair Key(pMD->GetMemberDef(), pMD->GetModule());
             m_TokenToComMTMethodPropsMap.InsertValue(&Key, (HashDatum)pProps);
         }
diff --git a/src/coreclr/vm/comtoclrcall.cpp b/src/coreclr/vm/comtoclrcall.cpp
index 215b6c61bb5812..74a68831ce759b 100644
--- a/src/coreclr/vm/comtoclrcall.cpp
+++ b/src/coreclr/vm/comtoclrcall.cpp
@@ -1047,7 +1047,10 @@ void ComCallMethodDesc::InitNativeInfo()
 
             MethodTable * pMT = pMD->GetMethodTable();
             IMDInternalImport * pInternalImport = pMT->GetMDImport();
-
+            if (pMD->IsAsyncThunkMethod())
+            {
+                ThrowHR(COR_E_NOTSUPPORTED);
+            }
             mdMethodDef md = pMD->GetMemberDef();
 
             ULONG ulCodeRVA;
diff --git a/src/coreclr/vm/dispatchinfo.cpp b/src/coreclr/vm/dispatchinfo.cpp
index eb0c83f7a6ce56..673aa783d532de 100644
--- a/src/coreclr/vm/dispatchinfo.cpp
+++ b/src/coreclr/vm/dispatchinfo.cpp
@@ -448,7 +448,13 @@ ComMTMethodProps * DispatchMemberInfo::GetMemberProps(OBJECTREF MemberInfoObj, C
             ARG_SLOT GetMethodHandleArg = ObjToArgSlot(MemberInfoObj);
             MethodDesc* pMeth = (MethodDesc*) getMethodHandle.Call_RetLPVOID(&GetMethodHandleArg);
             if (pMeth)
+            {
+                if (pMeth->IsAsyncThunkMethod())
+                {
+                    ThrowHR(COR_E_NOTSUPPORTED);
+                }
                 pMemberProps = pMemberMap->GetMethodProps(pMeth->GetMemberDef(), pMeth->GetModule());
+            }
         }
         else if (CoreLibBinder::IsClass(pMemberInfoClass, CLASS__RT_FIELD_INFO))
         {
@@ -844,6 +850,10 @@ void DispatchMemberInfo::SetUpMethodMarshalerInfo(MethodDesc *pMD, BOOL bReturnV
     //
     // Initialize the parameter definition enum.
     //
+    if (pMD->IsAsyncThunkMethod())
+    {
+        ThrowHR(COR_E_NOTSUPPORTED);
+    }
     hEnumParams.EnumInit(mdtParamDef, pMD->GetMemberDef());
 
     //
@@ -2579,6 +2589,11 @@ bool DispatchInfo::IsPropertyAccessorVisible(bool fIsSetter, OBJECTREF* pMemberI
         // Check to see if the new method is a property accessor.
         mdToken tkMember = mdTokenNil;
         MethodTable *pDeclaringMT = pMDForProperty->GetMethodTable();
+        if (pMDForProperty->IsAsyncThunkMethod())
+        {
+            return false;
+        }
+        
         if (pMDForProperty->GetModule()->GetPropertyInfoForMethodDef(pMDForProperty->GetMemberDef(), &tkMember, NULL, NULL) == S_OK)
         {
             if (IsMemberVisibleFromCom(pDeclaringMT, tkMember, pMDForProperty->GetMemberDef()))
diff --git a/src/coreclr/vm/dllimport.cpp b/src/coreclr/vm/dllimport.cpp
index c0f90289a01682..a21796d7b1d5d9 100644
--- a/src/coreclr/vm/dllimport.cpp
+++ b/src/coreclr/vm/dllimport.cpp
@@ -105,6 +105,8 @@ StubSigDesc::StubSigDesc(MethodDesc *pMD)
     m_sig           = pMD->GetSignature();
     m_pModule       = pMD->GetModule();         // Used for token resolution.
 
+    _ASSERTE(!pMD->IsAsyncThunkMethod());
+
     m_tkMethodDef = pMD->GetMemberDef();
     SigTypeContext::InitTypeContext(pMD, &m_typeContext);
     m_pMetadataModule = pMD->GetModule();
@@ -132,6 +134,7 @@ StubSigDesc::StubSigDesc(MethodDesc* pMD, const Signature& sig, Module* pModule)
 
     if (pMD != NULL)
     {
+        _ASSERTE(!pMD->IsAsyncThunkMethod());
         m_tkMethodDef = pMD->GetMemberDef();
         SigTypeContext::InitTypeContext(pMD, &m_typeContext);
         m_pMetadataModule = pMD->GetModule();
@@ -1055,7 +1058,10 @@ class ILStubState : public StubState
 
         DWORD dwToken = 0;
         if (pTargetMD)
+        {
+            _ASSERTE(!pTargetMD->IsAsyncThunkMethod());
             dwToken = pTargetMD->GetMemberDef();
+        }
 
 
         //
@@ -2713,6 +2719,10 @@ void PInvokeStaticSigInfo::DllImportInit(
     IMDInternalImport  *pInternalImport = pMD->GetMDImport();
     CorPinvokeMap mappingFlags = pmMaxValue;
     mdModuleRef modref = mdModuleRefNil;
+    if (pMD->IsAsyncThunkMethod())
+    {
+        ThrowHR(COR_E_NOTSUPPORTED);
+    }
     if (FAILED(pInternalImport->GetPinvokeMap(pMD->GetMemberDef(), (DWORD*)&mappingFlags, ppEntryPointName, &modref)))
     {
         InitCallConv(CallConvWinApiSentinel, pMD);
@@ -2998,6 +3008,10 @@ namespace
         CorInfoCallConvExtension callConvLocal;
         IMDInternalImport* pInternalImport = pMD->GetMDImport();
         CorPinvokeMap mappingFlags = pmMaxValue;
+        if (pMD->IsAsyncThunkMethod())
+        {
+            ThrowHR(COR_E_NOTSUPPORTED);
+        }
         HRESULT hr = pInternalImport->GetPinvokeMap(pMD->GetMemberDef(), (DWORD*)&mappingFlags, NULL /*pszImportName*/, NULL /*pmrImportDLL*/);
         if (FAILED(hr))
             return false;
@@ -3262,6 +3276,10 @@ BOOL NDirect::MarshalingRequired(
     mdMethodDef methodToken = mdMethodDefNil;
     if (pMD != NULL)
     {
+        if (pMD->IsAsyncThunkMethod())
+        {
+            ThrowHR(COR_E_NOTSUPPORTED);
+        }
         methodToken = pMD->GetMemberDef();
     }
     CollateParamTokens(pMDImport, methodToken, numArgs - 1, pParamTokenArray);
@@ -6051,6 +6069,10 @@ PCODE GetILStubForCalli(VASigCookie *pVASigCookie, MethodDesc *pMD)
     {
         PInvokeStaticSigInfo sigInfo(pMD);
 
+        if (pMD->IsAsyncThunkMethod())
+        {
+            ThrowHR(COR_E_NOTSUPPORTED);
+        }
         md = pMD->GetMemberDef();
         nlFlags = sigInfo.GetLinkFlags();
         nlType  = sigInfo.GetCharSet();
diff --git a/src/coreclr/vm/encee.cpp b/src/coreclr/vm/encee.cpp
index eef50b18936a2d..3c9054c002f6f5 100644
--- a/src/coreclr/vm/encee.cpp
+++ b/src/coreclr/vm/encee.cpp
@@ -343,6 +343,7 @@ HRESULT EditAndContinueModule::UpdateMethod(MethodDesc *pMethod)
             appDomain,
             module,
             tkMethod,
+            pMethod->IsAsyncThunkMethod(),
             AssemblyIterationFlags(kIncludeLoaded | kIncludeExecution));
         CollectibleAssemblyHolder<DomainAssembly *> pDomainAssembly;
         while (it.Next(pDomainAssembly.This()))
diff --git a/src/coreclr/vm/genericdict.cpp b/src/coreclr/vm/genericdict.cpp
index 09dcda4b11ebc3..abd477d0401ec1 100644
--- a/src/coreclr/vm/genericdict.cpp
+++ b/src/coreclr/vm/genericdict.cpp
@@ -891,6 +891,7 @@ Dictionary::PopulateEntry(
 
             uint32_t methodSlot = -1;
             BOOL fRequiresDispatchStub = 0;
+            BOOL isAsyncThunk = 0;
 
             if (isReadyToRunModule)
             {
@@ -902,6 +903,7 @@ Dictionary::PopulateEntry(
                 isInstantiatingStub = ((methodFlags & ENCODE_METHOD_SIG_InstantiatingStub) != 0) || (kind == MethodEntrySlot);
                 isUnboxingStub = ((methodFlags & ENCODE_METHOD_SIG_UnboxingStub) != 0);
                 fMethodNeedsInstantiation = ((methodFlags & ENCODE_METHOD_SIG_MethodInstantiation) != 0);
+                isAsyncThunk = ((methodFlags & ENCODE_METHOD_SIG_AsyncThunk) != 0);
 
                 if (methodFlags & ENCODE_METHOD_SIG_OwnerType)
                 {
@@ -952,6 +954,10 @@ Dictionary::PopulateEntry(
                         _ASSERTE(pZapSigContext->pInfoModule->IsFullModule());
                         pMethod = MemberLoader::GetMethodDescFromMethodDef(static_cast<Module*>(pZapSigContext->pInfoModule), TokenFromRid(rid, mdtMethodDef), FALSE);
                     }
+                    if (isAsyncThunk)
+                    {
+                        pMethod = pMethod->GetAsyncOtherVariant();
+                    }
                 }
 
                 if (ownerType.IsNull())
@@ -995,6 +1001,7 @@ Dictionary::PopulateEntry(
                 isInstantiatingStub = ((methodFlags & ENCODE_METHOD_SIG_InstantiatingStub) != 0);
                 isUnboxingStub = ((methodFlags & ENCODE_METHOD_SIG_UnboxingStub) != 0);
                 fMethodNeedsInstantiation = ((methodFlags & ENCODE_METHOD_SIG_MethodInstantiation) != 0);
+                isAsyncThunk = ((methodFlags & ENCODE_METHOD_SIG_AsyncThunk) != 0);
 
                 if ((methodFlags & ENCODE_METHOD_SIG_SlotInsteadOfToken) != 0)
                 {
@@ -1036,11 +1043,19 @@ Dictionary::PopulateEntry(
 
                     // The RID map should have been filled out if we fully loaded the class
                     pMethod = pMethodDefMT->GetModule()->LookupMethodDef(token);
+
+                    if (isAsyncThunk)
+                    {
+                        pMethod = pMethod->GetAsyncOtherVariant();
+                    }
+
                     _ASSERTE(pMethod != NULL);
                     pMethod->CheckRestore();
                 }
             }
 
+            _ASSERTE((!!isAsyncThunk) == pMethod->IsAsyncThunkMethod());
+
             if (fRequiresDispatchStub)
             {
                 // Generate a dispatch stub and store it in the dictionary.
@@ -1116,6 +1131,8 @@ Dictionary::PopulateEntry(
                 inst,
                 (!isInstantiatingStub && !isUnboxingStub));
 
+            _ASSERTE((!!isAsyncThunk) == pMethod->IsAsyncThunkMethod());
+
             if (kind == ConstrainedMethodEntrySlot)
             {
                 if (isReadyToRunModule)
diff --git a/src/coreclr/vm/genmeth.cpp b/src/coreclr/vm/genmeth.cpp
index 029e379305e4c2..021d9e48016595 100644
--- a/src/coreclr/vm/genmeth.cpp
+++ b/src/coreclr/vm/genmeth.cpp
@@ -245,6 +245,7 @@ static MethodDesc * FindTightlyBoundWrappedMethodDesc_DEBUG(MethodDesc * pMD)
 
     mdMethodDef methodDef = pMD->GetMemberDef();
     Module *pModule = pMD->GetModule();
+    bool isAsyncThunkMethod = pMD->IsAsyncThunkMethod();
 
     MethodTable::MethodIterator it(pMD->GetCanonicalMethodTable());
     it.MoveToEnd();
@@ -255,7 +256,8 @@ static MethodDesc * FindTightlyBoundWrappedMethodDesc_DEBUG(MethodDesc * pMD)
 
             if (pCurMethod && !pCurMethod->IsUnboxingStub()) {
                 if ((pCurMethod->GetMemberDef() == methodDef)  &&
-                    (pCurMethod->GetModule() == pModule))
+                    (pCurMethod->GetModule() == pModule) &&
+                    (pCurMethod->IsAsyncThunkMethod() == isAsyncThunkMethod))
                 {
                     return pCurMethod;
                 }
@@ -283,6 +285,7 @@ static MethodDesc * FindTightlyBoundUnboxingStub_DEBUG(MethodDesc * pMD)
 
     mdMethodDef methodDef = pMD->GetMemberDef();
     Module *pModule = pMD->GetModule();
+    bool isAsyncThunkMethod = pMD->IsAsyncThunkMethod();
 
     MethodTable::MethodIterator it(pMD->GetCanonicalMethodTable());
     it.MoveToEnd();
@@ -291,7 +294,8 @@ static MethodDesc * FindTightlyBoundUnboxingStub_DEBUG(MethodDesc * pMD)
             MethodDesc* pCurMethod = it.GetMethodDesc();
             if (pCurMethod && pCurMethod->IsUnboxingStub()) {
                 if ((pCurMethod->GetMemberDef() == methodDef) &&
-                    (pCurMethod->GetModule() == pModule)) {
+                    (pCurMethod->GetModule() == pModule) &&
+                    (pCurMethod->IsAsyncThunkMethod() == isAsyncThunkMethod)) {
                     return pCurMethod;
                 }
             }
@@ -357,7 +361,8 @@ InstantiatedMethodDesc::NewInstantiatedMethodDesc(MethodTable *pExactMT,
         pNewMD = FindLoadedInstantiatedMethodDesc(pExactMT,
                                                   pGenericMDescInRepMT->GetMemberDef(),
                                                   methodInst,
-                                                  getWrappedCode);
+                                                  getWrappedCode,
+                                                  pGenericMDescInRepMT->IsAsyncThunkMethod());
 
         // Crst goes out of scope here
         // We don't need to hold the crst while we build the MethodDesc, but we reacquire it later
@@ -476,7 +481,8 @@ InstantiatedMethodDesc::NewInstantiatedMethodDesc(MethodTable *pExactMT,
             InstantiatedMethodDesc *pOldMD = FindLoadedInstantiatedMethodDesc(pExactMT,
                                                       pGenericMDescInRepMT->GetMemberDef(),
                                                       methodInst,
-                                                      getWrappedCode);
+                                                      getWrappedCode,
+                                                      pGenericMDescInRepMT->IsAsyncThunkMethod());
 
             if (pOldMD == NULL)
             {
@@ -550,7 +556,8 @@ InstantiatedMethodDesc::FindOrCreateExactClassMethod(MethodTable *pExactMT,
     InstantiatedMethodDesc *pInstMD = FindLoadedInstantiatedMethodDesc(pExactMT,
                                                                        pCanonicalMD->GetMemberDef(),
                                                                        Instantiation(),
-                                                                       FALSE);
+                                                                       FALSE,
+                                                                       pCanonicalMD->IsAsyncThunkMethod());
 
     if (pInstMD == NULL)
     {
@@ -572,7 +579,8 @@ InstantiatedMethodDesc*
 InstantiatedMethodDesc::FindLoadedInstantiatedMethodDesc(MethodTable *pExactOrRepMT,
                                                          mdMethodDef methodDef,
                                                          Instantiation methodInst,
-                                                         BOOL getWrappedCode)
+                                                         BOOL getWrappedCode,
+                                                         BOOL asyncThunk)
 {
     CONTRACT(InstantiatedMethodDesc *)
     {
@@ -606,7 +614,8 @@ InstantiatedMethodDesc::FindLoadedInstantiatedMethodDesc(MethodTable *pExactOrRe
                                                   methodDef,
                                                   FALSE /* not forceBoxedEntryPoint */,
                                                   methodInst,
-                                                  getWrappedCode);
+                                                  getWrappedCode,
+                                                  asyncThunk);
 
     if (resultMD != NULL)
        RETURN((InstantiatedMethodDesc*) resultMD);
@@ -731,6 +740,7 @@ MethodDesc::FindOrCreateAssociatedMethodDesc(MethodDesc* pDefMD,
                                              BOOL allowInstParam,
                                              BOOL forceRemotableMethod,
                                              BOOL allowCreate,
+                                             AsyncVariantLookup asyncVariantLookup,
                                              ClassLoadLevel level)
 {
     CONTRACT(MethodDesc*)
@@ -767,7 +777,8 @@ MethodDesc::FindOrCreateAssociatedMethodDesc(MethodDesc* pDefMD,
     if (!pDefMD->HasClassOrMethodInstantiation() &&
         methodInst.IsEmpty() &&
         !forceBoxedEntryPoint &&
-        !pDefMD->IsUnboxingStub())
+        !pDefMD->IsUnboxingStub() && 
+        asyncVariantLookup == AsyncVariantLookup::MatchingAsyncVariant)
     {
         // Make sure that pDefMD->GetMethodTable() and pExactMT are related types even
         // if we took the fast path.
@@ -796,7 +807,7 @@ MethodDesc::FindOrCreateAssociatedMethodDesc(MethodDesc* pDefMD,
         COMPlusThrowHR(COR_E_TYPELOAD);
     }
 
-    if (pDefMD->HasClassOrMethodInstantiation() || !methodInst.IsEmpty())
+    if (pDefMD->HasClassOrMethodInstantiation() || !methodInst.IsEmpty() || asyncVariantLookup == AsyncVariantLookup::AsyncOtherVariant)
     {
         // General checks related to generics: arity (if any) must match and generic method
         // instantiation (if any) must be well-formed.
@@ -806,7 +817,7 @@ MethodDesc::FindOrCreateAssociatedMethodDesc(MethodDesc* pDefMD,
             COMPlusThrowHR(COR_E_BADIMAGEFORMAT);
         }
 
-        pMDescInCanonMT = pExactMT->GetCanonicalMethodTable()->GetParallelMethodDesc(pDefMD);
+        pMDescInCanonMT = pExactMT->GetCanonicalMethodTable()->GetParallelMethodDesc(pDefMD, asyncVariantLookup);
 
         if (!allowCreate && !pMDescInCanonMT->GetMethodTable()->IsFullyLoaded())
         {
@@ -882,7 +893,8 @@ MethodDesc::FindOrCreateAssociatedMethodDesc(MethodDesc* pDefMD,
                                                methodDef,
                                                TRUE /* forceBoxedEntryPoint */,
                                                Instantiation(),
-                                               FALSE /* no inst param */);
+                                               FALSE /* no inst param */,
+                                               pMDescInCanonMT->IsAsyncThunkMethod());
 
             // If we didn't find it then create it...
             if (!pResultMD)
@@ -900,7 +912,8 @@ MethodDesc::FindOrCreateAssociatedMethodDesc(MethodDesc* pDefMD,
                                                    methodDef,
                                                    TRUE,
                                                    Instantiation(),
-                                                   FALSE);
+                                                   FALSE,
+                                                   pMDescInCanonMT->IsAsyncThunkMethod());
                 if (pResultMD == NULL)
                 {
                     AllocMemTracker amt;
@@ -945,7 +958,8 @@ MethodDesc::FindOrCreateAssociatedMethodDesc(MethodDesc* pDefMD,
                                                methodDef,
                                                TRUE, /* forceBoxedEntryPoint */
                                                methodInst,
-                                               FALSE /* no inst param */);
+                                               FALSE /* no inst param */,
+                                               pMDescInCanonMT->IsAsyncThunkMethod());
 
             if (!pResultMD)
             {
@@ -962,11 +976,12 @@ MethodDesc::FindOrCreateAssociatedMethodDesc(MethodDesc* pDefMD,
                                                                  pExactMT,
                                                                  FALSE /* not Unboxing */,
                                                                  methodInst,
-                                                                 FALSE);
+                                                                 FALSE, FALSE, TRUE, asyncVariantLookup);
 
                 _ASSERTE(pNonUnboxingStub->GetClassification() == mcInstantiated);
                 _ASSERTE(!pNonUnboxingStub->RequiresInstArg());
                 _ASSERTE(!pNonUnboxingStub->IsUnboxingStub());
+                _ASSERTE(pNonUnboxingStub->IsAsyncThunkMethod() == pMDescInCanonMT->IsAsyncThunkMethod());
 
                 // Enter the critical section *after* we've found or created the non-unboxing instantiating stub (else we'd have a race,
                 // and its possible that the non-unboxing instantiating stub may be in a different loader module than pLoaderModule
@@ -978,7 +993,8 @@ MethodDesc::FindOrCreateAssociatedMethodDesc(MethodDesc* pDefMD,
                                                    methodDef,
                                                    TRUE, /* forceBoxedEntryPoint */
                                                    methodInst,
-                                                   FALSE /* no inst param */);
+                                                   FALSE /* no inst param */,
+                                                   pNonUnboxingStub->IsAsyncThunkMethod());
 
                 if (pResultMD == NULL)
                 {
@@ -1118,7 +1134,8 @@ MethodDesc::FindOrCreateAssociatedMethodDesc(MethodDesc* pDefMD,
                 InstantiatedMethodDesc::FindLoadedInstantiatedMethodDesc(pExactMT->GetCanonicalMethodTable(),
                                                                          methodDef,
                                                                          Instantiation(repInst, methodInst.GetNumArgs()),
-                                                                         TRUE);
+                                                                         TRUE,
+                                                                         pMDescInCanonMT->IsAsyncThunkMethod());
 
             // No - so create one.
             if (pInstMD == NULL)
@@ -1142,7 +1159,8 @@ MethodDesc::FindOrCreateAssociatedMethodDesc(MethodDesc* pDefMD,
                 InstantiatedMethodDesc::FindLoadedInstantiatedMethodDesc(pExactMT,
                                                                          methodDef,
                                                                          methodInst,
-                                                                         FALSE);
+                                                                         FALSE,
+                                                                         pMDescInCanonMT->IsAsyncThunkMethod());
 
             // No - so create one.  Go fetch the shared one first
             if (pInstMD == NULL)
@@ -1161,6 +1179,7 @@ MethodDesc::FindOrCreateAssociatedMethodDesc(MethodDesc* pDefMD,
                                                                           /* allowInstParam */ TRUE,
                                                                           /* forceRemotableMethod */ FALSE,
                                                                           /* allowCreate */ TRUE,
+                                                                          asyncVariantLookup,
                                                                           /* level */ level);
 
                 _ASSERTE(pWrappedMD->IsSharedByGenericInstantiations());
@@ -1181,7 +1200,8 @@ MethodDesc::FindOrCreateAssociatedMethodDesc(MethodDesc* pDefMD,
                 InstantiatedMethodDesc::FindLoadedInstantiatedMethodDesc(pExactMT,
                                                                          methodDef,
                                                                          methodInst,
-                                                                         FALSE);
+                                                                         FALSE,
+                                                                         pMDescInCanonMT->IsAsyncThunkMethod());
 
             // No - so create one.
             if (pInstMD == NULL)
diff --git a/src/coreclr/vm/instmethhash.cpp b/src/coreclr/vm/instmethhash.cpp
index 2cb27b86fa9df4..ac563cb7c498fe 100644
--- a/src/coreclr/vm/instmethhash.cpp
+++ b/src/coreclr/vm/instmethhash.cpp
@@ -129,7 +129,8 @@ MethodDesc* InstMethodHashTable::FindMethodDesc(TypeHandle declaringType,
                                                 mdMethodDef token,
                                                 BOOL unboxingStub,
                                                 Instantiation inst,
-                                                BOOL getSharedNotStub)
+                                                BOOL getSharedNotStub,
+                                                bool isAsyncThunk)
 {
     CONTRACTL
     {
@@ -171,6 +172,11 @@ MethodDesc* InstMethodHashTable::FindMethodDesc(TypeHandle declaringType,
             continue;  // Next iteration of the for loop
         }
 
+        if (pMD->IsAsyncThunkMethod() != isAsyncThunk)
+        {
+            continue;
+        }
+
         if (!inst.IsEmpty())
         {
             Instantiation candidateInst = pMD->GetMethodInstantiation();
@@ -213,7 +219,7 @@ BOOL InstMethodHashTable::ContainsMethodDesc(MethodDesc* pMD)
 
     return FindMethodDesc(
         pMD->GetMethodTable(), pMD->GetMemberDef(), pMD->IsUnboxingStub(),
-        pMD->GetMethodInstantiation(), pMD->RequiresInstArg()) != NULL;
+        pMD->GetMethodInstantiation(), pMD->RequiresInstArg(), pMD->IsAsyncThunkMethod()) != NULL;
 }
 
 #endif // #ifndef DACCESS_COMPILE
diff --git a/src/coreclr/vm/instmethhash.h b/src/coreclr/vm/instmethhash.h
index 153bbe4740846b..8d03d000c3d5ff 100644
--- a/src/coreclr/vm/instmethhash.h
+++ b/src/coreclr/vm/instmethhash.h
@@ -109,7 +109,8 @@ class InstMethodHashTable : public DacEnumerableHashTable<InstMethodHashTable, I
                                mdMethodDef token,
                                BOOL unboxingStub,
                                Instantiation inst,
-                               BOOL getSharedNotStub);
+                               BOOL getSharedNotStub,
+                               bool isAsyncThunk);
 
     BOOL ContainsMethodDesc(MethodDesc* pMD);
 
diff --git a/src/coreclr/vm/interoputil.cpp b/src/coreclr/vm/interoputil.cpp
index d9e5d43759626a..fbb70bc806182f 100644
--- a/src/coreclr/vm/interoputil.cpp
+++ b/src/coreclr/vm/interoputil.cpp
@@ -2345,6 +2345,10 @@ ULONG GetStringizedClassItfDef(TypeHandle InterfaceType, CQuickArray<BYTE> &rDef
                 {
                     pDeclaringMT = pProps->pMeth->GetMethodTable();
                     tkMb = pProps->pMeth->GetMemberDef();
+                    if (pProps->pMeth->IsAsyncThunkMethod())
+                    {
+                        ThrowHR(COR_E_NOTSUPPORTED);
+                    }
                     cbCur = GetStringizedMethodDef(pDeclaringMT, tkMb, rDef, cbCur);
                 }
                 else
@@ -2555,6 +2559,9 @@ BOOL IsMethodVisibleFromCom(MethodDesc *pMD)
     mdProperty  pd;
     LPCUTF8     pPropName;
     ULONG       uSemantic;
+    if (pMD->IsAsyncThunkMethod())
+        return false;
+        
     mdMethodDef md = pMD->GetMemberDef();
 
     // See if there is property information for this member.
diff --git a/src/coreclr/vm/jitinterface.cpp b/src/coreclr/vm/jitinterface.cpp
index 9471239e25a87a..57eb2a0f04c296 100644
--- a/src/coreclr/vm/jitinterface.cpp
+++ b/src/coreclr/vm/jitinterface.cpp
@@ -3353,6 +3353,10 @@ void CEEInfo::ComputeRuntimeLookupForSharedGenericToken(DictionaryEntryKind entr
 
                 methodFlags |= ENCODE_METHOD_SIG_SlotInsteadOfToken;
             }
+            if (pTemplateMD->IsAsyncThunkMethod())
+            {
+                methodFlags |= ENCODE_METHOD_SIG_AsyncThunk;
+            }
 
             sigBuilder.AppendData(methodFlags);
 
diff --git a/src/coreclr/vm/memberload.cpp b/src/coreclr/vm/memberload.cpp
index a329702e3a56c0..08ea7703c85a40 100644
--- a/src/coreclr/vm/memberload.cpp
+++ b/src/coreclr/vm/memberload.cpp
@@ -781,6 +781,7 @@ MemberLoader::GetMethodDescFromMemberDefOrRefOrSpec(
         allowInstParam,
         /* forceRemotableMethod */ FALSE,
         /* allowCreate */ TRUE,
+        AsyncVariantLookup::MatchingAsyncVariant,
         /* level */ owningTypeLoadLevel);
 } // MemberLoader::GetMethodDescFromMemberDefOrRefOrSpec
 
diff --git a/src/coreclr/vm/method.cpp b/src/coreclr/vm/method.cpp
index 63b099dc54db58..bf1ad0ed150b1a 100644
--- a/src/coreclr/vm/method.cpp
+++ b/src/coreclr/vm/method.cpp
@@ -405,6 +405,13 @@ void MethodDesc::GetSig(PCCOR_SIGNATURE *ppSig, DWORD *pcSig)
             return;
         }
     }
+    if (IsAsyncThunkMethod())
+    {
+        Signature sig = GetAsyncThunkData().sig;
+        *ppSig = sig.GetRawSig();
+        *pcSig = sig.GetRawSigLen();
+        return;
+    }
 
     GetSigFromMetadata(GetMDImport(), ppSig, pcSig);
     PREFIX_ASSUME(*ppSig != NULL);
@@ -433,6 +440,7 @@ void MethodDesc::GetSigFromMetadata(IMDInternalImport * importer,
     }
     CONTRACTL_END
 
+    _ASSERTE(!IsAsyncThunkMethod());
     if (FAILED(importer->GetSigOfMethodDef(GetMemberDef(), pcSig, ppSig)))
     {   // Class loader already asked for signature, so this should always succeed (unless there's a
         // bug or a new code path)
@@ -680,7 +688,7 @@ BOOL MethodDesc::HasSameMethodDefAs(MethodDesc * pMD)
     if (this == pMD)
         return TRUE;
 
-    return (GetMemberDef() == pMD->GetMemberDef()) && (GetModule() == pMD->GetModule());
+    return (GetMemberDef() == pMD->GetMemberDef()) && (GetModule() == pMD->GetModule() && pMD->IsAsyncThunkMethod() == IsAsyncThunkMethod());
 }
 
 //*******************************************************************************
diff --git a/src/coreclr/vm/method.hpp b/src/coreclr/vm/method.hpp
index 9a9931d26b00a8..f4ae0b4342b028 100644
--- a/src/coreclr/vm/method.hpp
+++ b/src/coreclr/vm/method.hpp
@@ -184,6 +184,12 @@ enum MethodDescClassification
 
 #define METHOD_MAX_RVA                          0x7FFFFFFF
 
+enum class AsyncVariantLookup
+{
+    MatchingAsyncVariant = 0,
+    AsyncOtherVariant
+};
+
 
 // The size of this structure needs to be a multiple of MethodDesc::ALIGNMENT
 //
@@ -1524,6 +1530,7 @@ class MethodDesc
                                                         BOOL allowInstParam,
                                                         BOOL forceRemotableMethod = FALSE,
                                                         BOOL allowCreate = TRUE,
+                                                        AsyncVariantLookup variantLookup = AsyncVariantLookup::MatchingAsyncVariant,
                                                         ClassLoadLevel level = CLASS_LOADED);
 
     // Normalize methoddesc for reflection
@@ -1531,6 +1538,11 @@ class MethodDesc
                                                                      TypeHandle instType,
                                                                      Instantiation methodInst);
 
+    MethodDesc* GetAsyncOtherVariant()
+    {
+        return GetMethodTable()->GetParallelMethodDesc(this, AsyncVariantLookup::AsyncOtherVariant);
+    }
+
     // True if a MD is an funny BoxedEntryPointStub (not from the method table) or
     // an MD for a generic instantiation...In other words the MethodDescs and the
     // MethodTable are guaranteed to be "tightly-knit", i.e. if one is present in
@@ -1702,7 +1714,7 @@ class MethodDesc
         m_wFlags |= mdcHasNativeCodeSlot;
     }
 
-    inline BOOL IsAsyncThunkMethod()
+    inline bool IsAsyncThunkMethod()
     {
         LIMITED_METHOD_DAC_CONTRACT;
         return (m_wFlags & mdcIsAsyncThunkMethod) != 0;
@@ -3430,7 +3442,8 @@ class InstantiatedMethodDesc final : public MethodDesc
     static InstantiatedMethodDesc* FindLoadedInstantiatedMethodDesc(MethodTable *pMT,
                                                                     mdMethodDef methodDef,
                                                                     Instantiation methodInst,
-                                                                    BOOL getSharedNotStub);
+                                                                    BOOL getSharedNotStub,
+                                                                    BOOL asyncThunk);
 
 private:
 
diff --git a/src/coreclr/vm/methoditer.cpp b/src/coreclr/vm/methoditer.cpp
index 82def6a1bec50a..14387754ed40f3 100644
--- a/src/coreclr/vm/methoditer.cpp
+++ b/src/coreclr/vm/methoditer.cpp
@@ -57,6 +57,17 @@ BOOL LoadedMethodDescIterator::Next(
         return FALSE;
     }
 
+    if (m_fIsAsyncThunk)
+    {
+        m_mainMD = m_mainMD->GetMethodTable()->GetParallelMethodDesc(m_mainMD, AsyncVariantLookup::AsyncOtherVariant);
+
+        if (m_mainMD == NULL)
+        {
+            *pDomainAssemblyHolder = NULL;
+            return FALSE;
+        }
+    }
+
     // Needs to work w/ non-generic methods too.
     if (!m_mainMD->HasClassOrMethodInstantiation())
     {
@@ -146,6 +157,8 @@ BOOL LoadedMethodDescIterator::Next(
             goto ADVANCE_METHOD;
         if (m_methodIteratorEntry->GetMethod()->GetMemberDef() != m_md)
             goto ADVANCE_METHOD;
+        if (m_methodIteratorEntry->GetMethod()->IsAsyncThunkMethod() != m_fIsAsyncThunk)
+            goto ADVANCE_METHOD;
     }
     else if (m_startedNonGenericMethod)
     {
@@ -213,6 +226,7 @@ LoadedMethodDescIterator::Start(
     AppDomain * pAppDomain,
     Module *pModule,
     mdMethodDef md,
+    bool fIsAsyncThunk,
     AssemblyIterationFlags assemblyIterationFlags)
 {
     CONTRACTL
@@ -224,6 +238,8 @@ LoadedMethodDescIterator::Start(
     }
     CONTRACTL_END;
 
+    m_fIsAsyncThunk = fIsAsyncThunk;
+
     m_assemIterationFlags = assemblyIterationFlags;
     m_mainMD = NULL;
     m_module = pModule;
@@ -244,7 +260,7 @@ LoadedMethodDescIterator::Start(
     mdMethodDef     md,
     MethodDesc      *pMethodDesc)
 {
-    Start(pAppDomain, pModule, md);
+    Start(pAppDomain, pModule, md, pMethodDesc->IsAsyncThunkMethod());
     m_mainMD = pMethodDesc;
 }
 
@@ -255,4 +271,5 @@ LoadedMethodDescIterator::LoadedMethodDescIterator(void)
     m_module = NULL;
     m_md = mdTokenNil;
     m_pAppDomain = NULL;
+    m_fIsAsyncThunk = false;
 }
diff --git a/src/coreclr/vm/methoditer.h b/src/coreclr/vm/methoditer.h
index fdc49e2ce59710..5017b6c8991629 100644
--- a/src/coreclr/vm/methoditer.h
+++ b/src/coreclr/vm/methoditer.h
@@ -34,6 +34,8 @@ class LoadedMethodDescIterator
     mdMethodDef  m_md;
     MethodDesc * m_mainMD;
     AppDomain *  m_pAppDomain;
+    bool         m_fIsAsyncThunk;
+
 
     // The following hold the state of the iteration....
     // Yes we iterate everything for the moment - we need
@@ -68,6 +70,7 @@ class LoadedMethodDescIterator
     void Start(AppDomain * pAppDomain,
                Module *pModule,
                mdMethodDef md,
+               bool fIsAsyncThunk,
                AssemblyIterationFlags assemIterationFlags = (AssemblyIterationFlags)(kIncludeLoaded | kIncludeExecution));
     void Start(AppDomain * pAppDomain, Module *pModule, mdMethodDef md, MethodDesc *pDesc);
 
@@ -75,6 +78,7 @@ class LoadedMethodDescIterator
         AppDomain * pAppDomain,
         Module *pModule,
         mdMethodDef md,
+        bool fIsAsyncThunk,
         AssemblyIterationFlags assemblyIterationFlags = (AssemblyIterationFlags)(kIncludeLoaded | kIncludeExecution))
     {
         LIMITED_METHOD_CONTRACT;
diff --git a/src/coreclr/vm/methodtable.cpp b/src/coreclr/vm/methodtable.cpp
index 32fb17a629d80c..55b5b63a2c8778 100644
--- a/src/coreclr/vm/methodtable.cpp
+++ b/src/coreclr/vm/methodtable.cpp
@@ -6423,6 +6423,7 @@ namespace
                 FALSE,                  // allowInstParam
                 TRUE,                   // forceRemoteableMethod
                 TRUE,                   // allowCreate
+                AsyncVariantLookup::MatchingAsyncVariant,
                 level                   // level
             );
         }
@@ -8720,7 +8721,8 @@ namespace
         {
             MethodDesc* pMD = it.GetMethodDesc();
             if (pMD->GetMemberDef() == tkMethod
-                && pMD->GetModule() == mod)
+                && pMD->GetModule() == mod
+                && pMD->IsAsyncThunkMethod() == pDefMD->IsAsyncThunkMethod())
             {
                 return pMD;
             }
@@ -8730,7 +8732,7 @@ namespace
     }
 }
 
-MethodDesc* MethodTable::GetParallelMethodDesc(MethodDesc* pDefMD)
+MethodDesc* MethodTable::GetParallelMethodDesc(MethodDesc* pDefMD, AsyncVariantLookup asyncVariantLookup)
 {
     CONTRACTL
     {
@@ -8740,12 +8742,36 @@ MethodDesc* MethodTable::GetParallelMethodDesc(MethodDesc* pDefMD)
     }
     CONTRACTL_END;
 
+    if (asyncVariantLookup == AsyncVariantLookup::MatchingAsyncVariant)
+    {
 #ifdef FEATURE_METADATA_UPDATER
-    if (pDefMD->IsEnCAddedMethod())
-        return GetParallelMethodDescForEnC(this, pDefMD);
+        if (pDefMD->IsEnCAddedMethod())
+            return GetParallelMethodDescForEnC(this, pDefMD);
 #endif // FEATURE_METADATA_UPDATER
 
-    return GetMethodDescForSlot(pDefMD->GetSlot());
+        return GetMethodDescForSlot(pDefMD->GetSlot());
+    }
+    else
+    {
+        // Slow path for finding the asyncThunk (or not the AsyncThunk)
+        // This could be optimized with some trickery around slot numbers, but doing so is ... confusing, so I'm not implementing this yet
+        mdMethodDef tkMethod = pDefMD->GetMemberDef();
+        Module* mod = pDefMD->GetModule();
+        bool isAsyncThunkMethod = pDefMD->IsAsyncThunkMethod();
+
+        MethodTable::IntroducedMethodIterator it(this);
+        for (; it.IsValid(); it.Next())
+        {
+            MethodDesc* pMD = it.GetMethodDesc();
+            if (pMD->GetMemberDef() == tkMethod
+                && pMD->GetModule() == mod
+                && pMD->IsAsyncThunkMethod() != isAsyncThunkMethod)
+            {
+                return pMD;
+            }
+        }
+        return NULL;
+    }
 }
 
 #ifndef DACCESS_COMPILE
@@ -9112,9 +9138,21 @@ MethodTable::TryResolveVirtualStaticMethodOnThisType(MethodTable* pInterfaceType
         {
             COMPlusThrow(kTypeLoadException, E_FAIL);
         }
+
+        bool differsByAsyncVariant = false;
         if (!pMethodDecl->HasSameMethodDefAs(pInterfaceMD))
         {
-            continue;
+            if (pMethodDecl->GetMemberDef() == pInterfaceMD->GetMemberDef() && 
+                pMethodDecl->GetModule() == pInterfaceMD->GetModule() &&
+                pMethodDecl->IsAsyncThunkMethod() != pInterfaceMD->IsAsyncThunkMethod())
+            {
+                differsByAsyncVariant = true;
+                pMethodDecl = pMethodDecl->GetAsyncOtherVariant();
+            }
+            else
+            {
+                continue;
+            }
         }
 
         // Spec requires that all body token for MethodImpls that refer to static virtual implementation methods must be MethodDef tokens.
@@ -9140,6 +9178,11 @@ MethodTable::TryResolveVirtualStaticMethodOnThisType(MethodTable* pInterfaceType
             COMPlusThrow(kTypeLoadException, E_FAIL);
         }
 
+        if (differsByAsyncVariant)
+        {
+            pMethodImpl = pMethodImpl->GetAsyncOtherVariant();
+        }
+
         if (!verifyImplemented && instantiateMethodParameters)
         {
             pMethodImpl = pMethodImpl->FindOrCreateAssociatedMethodDesc(
@@ -9150,6 +9193,7 @@ MethodTable::TryResolveVirtualStaticMethodOnThisType(MethodTable* pInterfaceType
                 /* allowInstParam */ FALSE,
                 /* forceRemotableMethod */ FALSE,
                 /* allowCreate */ TRUE,
+                AsyncVariantLookup::MatchingAsyncVariant,
                 /* level */ level);
         }
         if (pMethodImpl != nullptr)
diff --git a/src/coreclr/vm/methodtable.h b/src/coreclr/vm/methodtable.h
index 9c06faf6c43236..fd4b2a7e45eab1 100644
--- a/src/coreclr/vm/methodtable.h
+++ b/src/coreclr/vm/methodtable.h
@@ -63,6 +63,7 @@ class ClassFactoryBase;
 #endif // FEATURE_COMINTEROP_UNMANAGED_ACTIVATION
 class ArgDestination;
 enum class WellKnownAttribute : DWORD;
+enum class AsyncVariantLookup;
 
 enum class ResolveVirtualStaticMethodFlags
 {
@@ -1451,7 +1452,7 @@ class MethodTable
     MethodTable * GetRestoredSlotMT(DWORD slot);
 
     // Used to map methods on the same slot between instantiations.
-    MethodDesc * GetParallelMethodDesc(MethodDesc * pDefMD);
+    MethodDesc * GetParallelMethodDesc(MethodDesc * pDefMD, AsyncVariantLookup asyncVariantLookup = (AsyncVariantLookup)0);
 
     //-------------------------------------------------------------------
     // BoxedEntryPoint MethodDescs.
diff --git a/src/coreclr/vm/methodtablebuilder.cpp b/src/coreclr/vm/methodtablebuilder.cpp
index 4bf429f6f85b97..c0ae96533160ca 100644
--- a/src/coreclr/vm/methodtablebuilder.cpp
+++ b/src/coreclr/vm/methodtablebuilder.cpp
@@ -994,7 +994,7 @@ MethodTableBuilder::bmtRTMethod::bmtRTMethod(
     MethodDesc *    pMD)
     : m_pOwningType(pOwningType),
       m_pMD(pMD),
-      m_methodSig(pMD->IsEEImpl()  // Handle Async2 methods
+      m_methodSig(pMD->IsAsyncThunkMethod()
        ? MethodSignature(pMD->GetModule(),
                          pMD->GetMemberDef(),
                          pMD->GetSignature(),
@@ -3530,6 +3530,7 @@ MethodTableBuilder::EnumerateClassMethods()
         // Create a new bmtMDMethod representing this method and add it to the
         // declared method list.
         //
+        bmtMDMethod *pDeclaredMethod = NULL;
         for (int insertCount = 0; insertCount < 2; insertCount++)
         {
             bmtMDMethod * pNewMethod;
@@ -3543,6 +3544,7 @@ MethodTableBuilder::EnumerateClassMethods()
                     dwMethodRVA,
                     type,
                     implType);
+                pDeclaredMethod = pNewMethod;
             }
             else
             {
@@ -3590,6 +3592,9 @@ MethodTableBuilder::EnumerateClassMethods()
                     thunkType,
                     type,
                     implType);
+
+                pNewMethod->SetAsyncOtherVariant(pDeclaredMethod);
+                pDeclaredMethod->SetAsyncOtherVariant(pNewMethod);
             }
 
             bmtMethod->AddDeclaredMethod(pNewMethod);
@@ -6221,6 +6226,12 @@ MethodTableBuilder::ProcessMethodImpls()
                         BuildMethodTableThrowException(IDS_CLASSLOAD_MI_DECLARATIONNOTFOUND, it.Token());
                     }
 
+                    // When working with the thunk, we want the opposite type of async method
+                    if (it->GetAsyncThunkType() != AsyncThunkType::NotAThunk)
+                    {
+                        declMethod = declMethod.GetAsyncOtherVariant();;
+                    }
+
                     if (!IsMdVirtual(declMethod.GetDeclAttrs()))
                     {   // Make sure the decl is virtual
                         BuildMethodTableThrowException(IDS_CLASSLOAD_MI_MUSTBEVIRTUAL, it.Token());
diff --git a/src/coreclr/vm/methodtablebuilder.h b/src/coreclr/vm/methodtablebuilder.h
index a83402618e3ad3..4e967d4cf26c63 100644
--- a/src/coreclr/vm/methodtablebuilder.h
+++ b/src/coreclr/vm/methodtablebuilder.h
@@ -676,7 +676,7 @@ class MethodTableBuilder
 
         //-----------------------------------------------------------------------------------------
         // This constructor can be used with hard-coded signatures that are used for
-        // locating .ctor and .cctor methods.
+        // representing async thunk methods
         MethodSignature(
             Module *             pModule,
             mdToken              tok,
@@ -1068,6 +1068,9 @@ class MethodTableBuilder
             return m_asyncThunkType;
         }
 
+        bmtMDMethod *     GetAsyncOtherVariant() const { return m_asyncOtherVariant; }
+        void              SetAsyncOtherVariant(bmtMDMethod* pAsyncOtherVariant) { m_asyncOtherVariant = pAsyncOtherVariant; }
+
     private:
         //-----------------------------------------------------------------------------------------
         bmtMDType *       m_pOwningType;
@@ -1079,6 +1082,7 @@ class MethodTableBuilder
         AsyncThunkType m_asyncThunkType;
         METHOD_IMPL_TYPE  m_implType;           // Whether or not the method is a methodImpl body
         MethodSignature   m_methodSig;
+        bmtMDMethod*      m_asyncOtherVariant = NULL;
 
         MethodDesc *      m_pMD;                // MethodDesc created and assigned to this method
         MethodDesc *      m_pUnboxedMD;         // Unboxing MethodDesc if this is a virtual method on a valuetype
@@ -1208,6 +1212,27 @@ class MethodTableBuilder
         MethodDesc *
         GetMethodDesc() const;
 
+        bmtMethodHandle GetAsyncOtherVariant() const
+        {
+            if (IsRTMethod())
+            {
+                bmtRTMethod *pRTMethod = AsRTMethod();
+                MethodDesc *pMD = pRTMethod->GetMethodDesc();
+                MethodDesc *pRTOtherMethod = pMD->GetAsyncOtherVariant();
+                _ASSERTE(pRTOtherMethod != NULL);
+                _ASSERTE(pRTOtherMethod->IsAsyncThunkMethod() || pRTMethod->GetMethodDesc()->IsAsyncThunkMethod());
+                _ASSERTE(FALSE);
+                return bmtMethodHandle((bmtRTMethod*)NULL); // TODO! Fix this to do the right thing here
+            }
+            else
+            {
+                bmtMDMethod* pMDOtherVariant = AsMDMethod()->GetAsyncOtherVariant();
+                _ASSERTE(pMDOtherVariant != NULL);
+                _ASSERTE(pMDOtherVariant->GetAsyncThunkType() != AsyncThunkType::NotAThunk || AsMDMethod()->GetAsyncThunkType() != AsyncThunkType::NotAThunk);
+                return pMDOtherVariant;
+            }
+        }
+
     protected:
         //-----------------------------------------------------------------------------------------
         static const UINT_PTR RTMETHOD_FLAG = 0x1;
diff --git a/src/coreclr/vm/multicorejit.cpp b/src/coreclr/vm/multicorejit.cpp
index d052cbeb3d9031..b3017f914eb4e5 100644
--- a/src/coreclr/vm/multicorejit.cpp
+++ b/src/coreclr/vm/multicorejit.cpp
@@ -398,6 +398,12 @@ HRESULT MulticoreJitRecorder::WriteOutput(IStream * pStream)
         }
 
         MethodDesc * pMethod = m_JitInfoArray[i].GetMethodDescAndClean();
+        if (pMethod->IsAsyncThunkMethod())
+        {
+            // TODO consider adding support for async thunks in the future
+            skipped++;
+            continue;
+        }
 
         if (m_JitInfoArray[i].IsGenericMethodInfo())
         {
diff --git a/src/coreclr/vm/readytoruninfo.cpp b/src/coreclr/vm/readytoruninfo.cpp
index e75373db8855aa..be8f814344e3b3 100644
--- a/src/coreclr/vm/readytoruninfo.cpp
+++ b/src/coreclr/vm/readytoruninfo.cpp
@@ -945,6 +945,8 @@ static bool SigMatchesMethodDesc(MethodDesc* pMD, SigPointer &sig, ModuleBase *
 {
     STANDARD_VM_CONTRACT;
 
+    _ASSERTE(!pMD->IsAsyncThunkMethod());
+
     ModuleBase *pOrigModule = pModule;
     ZapSig::Context    zapSigContext(pModule, (void *)pModule, ZapSig::NormalTokens);
     ZapSig::Context *  pZapSigContext = &zapSigContext;
@@ -1053,6 +1055,9 @@ bool ReadyToRunInfo::GetPgoInstrumentationData(MethodDesc * pMD, BYTE** pAllocat
     if (ReadyToRunCodeDisabled())
         return false;
 
+    if (pMD->IsAsyncThunkMethod())
+        return false;
+
     if (m_pgoInstrumentationDataHashtable.IsNull())
         return false;
 
@@ -1125,6 +1130,9 @@ PCODE ReadyToRunInfo::GetEntryPoint(MethodDesc * pMD, PrepareCodeConfig* pConfig
     if (ReadyToRunCodeDisabled())
         goto done;
 
+    if (pMD->IsAsyncThunkMethod())
+        goto done;
+
     ETW::MethodLog::GetR2RGetEntryPointStart(pMD);
 
     uint offset;
diff --git a/src/coreclr/vm/zapsig.cpp b/src/coreclr/vm/zapsig.cpp
index bbbab9a51c84d4..8b8c6a3cda5b28 100644
--- a/src/coreclr/vm/zapsig.cpp
+++ b/src/coreclr/vm/zapsig.cpp
@@ -927,12 +927,13 @@ MethodDesc *ZapSig::DecodeMethod(ModuleBase *pInfoModule,
     // in non-generic structs.
     BOOL isInstantiatingStub = (methodFlags & ENCODE_METHOD_SIG_InstantiatingStub);
     BOOL isUnboxingStub = (methodFlags & ENCODE_METHOD_SIG_UnboxingStub);
+    bool isAsyncThunk = (methodFlags & ENCODE_METHOD_SIG_AsyncThunk) != 0;
 
     pMethod = MethodDesc::FindOrCreateAssociatedMethodDesc(pMethod, thOwner.GetMethodTable(),
                                                             isUnboxingStub,
                                                             inst,
                                                             !(isInstantiatingStub || isUnboxingStub) && !actualOwnerRequired,
-                                                            actualOwnerRequired);
+                                                            actualOwnerRequired, TRUE, isAsyncThunk == pMethod->IsAsyncThunkMethod() ? AsyncVariantLookup::MatchingAsyncVariant : AsyncVariantLookup::AsyncOtherVariant);
 
     if (methodFlags & ENCODE_METHOD_SIG_Constrained)
     {
@@ -1236,6 +1237,8 @@ BOOL ZapSig::EncodeMethod(
         methodFlags |= ENCODE_METHOD_SIG_InstantiatingStub;
     if (fMethodNeedsInstantiation)
         methodFlags |= ENCODE_METHOD_SIG_MethodInstantiation;
+    if (pMethod->IsAsyncThunkMethod())
+        methodFlags |= ENCODE_METHOD_SIG_AsyncThunk;
 
     // Assume that the owner type is going to be needed
     methodFlags |= ENCODE_METHOD_SIG_OwnerType;

From 20eb76eeda51bf0f9bc32bf462762f53904f4de8 Mon Sep 17 00:00:00 2001
From: David Wrighton <davidwr@microsoft.com>
Date: Wed, 20 Sep 2023 14:05:20 -0700
Subject: [PATCH 005/203] It builds and it should work... but doesn't

---
 src/coreclr/vm/methodtablebuilder.cpp | 46 ++++++++++++++++++++-------
 src/coreclr/vm/methodtablebuilder.h   | 21 ++++++++----
 2 files changed, 48 insertions(+), 19 deletions(-)

diff --git a/src/coreclr/vm/methodtablebuilder.cpp b/src/coreclr/vm/methodtablebuilder.cpp
index c0ae96533160ca..d0f64636802283 100644
--- a/src/coreclr/vm/methodtablebuilder.cpp
+++ b/src/coreclr/vm/methodtablebuilder.cpp
@@ -5775,7 +5775,7 @@ MethodTableBuilder::PlaceVirtualMethods()
 // that the name+signature corresponds to. Used by ProcessMethodImpls and ProcessInexactMethodImpls
 // Always returns the first match that it finds. Affects the ambiguities in code:#ProcessInexactMethodImpls_Ambiguities
 MethodTableBuilder::bmtMethodHandle
-MethodTableBuilder::FindDeclMethodOnInterfaceEntry(bmtInterfaceEntry *pItfEntry, MethodSignature &declSig, bool searchForStaticMethods)
+MethodTableBuilder::FindDeclMethodOnInterfaceEntry(bmtInterfaceEntry *pItfEntry, MethodSignature &declSig, AsyncVariantLookup variantLookup, bool searchForStaticMethods)
 {
     STANDARD_VM_CONTRACT;
 
@@ -5812,6 +5812,26 @@ MethodTableBuilder::FindDeclMethodOnInterfaceEntry(bmtInterfaceEntry *pItfEntry,
         }
     }
 
+    if (variantLookup == AsyncVariantLookup::AsyncOtherVariant)
+    {
+        bmtRTMethod* declRTMethod = declMethod.AsRTMethod();
+        bool foundOtherVariant = false;
+        for (; !slotIt.AtEnd(); slotIt.Next())
+        {
+            bmtRTMethod* slotDeclMethod = slotIt->Decl().AsRTMethod();
+
+            if ((slotDeclMethod->GetOwningType() == declRTMethod->GetOwningType()) &&
+                (slotDeclMethod->GetMethodDesc()->GetMethodTable() == declRTMethod->GetMethodDesc()->GetMethodTable()) &&
+                (slotDeclMethod->GetMethodDesc()->GetMemberDef() == declRTMethod->GetMethodDesc()->GetMemberDef()) &&
+                (slotDeclMethod->GetMethodDesc()->IsAsyncThunkMethod() == declRTMethod->GetMethodDesc()->IsAsyncThunkMethod()))
+            {
+                declMethod = slotIt->Decl();
+            }
+        }
+
+        _ASSERTE(foundOtherVariant);
+    }
+
     return declMethod;
 }
 
@@ -5880,6 +5900,8 @@ MethodTableBuilder::ProcessInexactMethodImpls()
             continue;
         }
 
+        AsyncVariantLookup asyncVariantOfDeclToFind = it->GetAsyncThunkType() == AsyncThunkType::NotAThunk ? AsyncVariantLookup::MatchingAsyncVariant : AsyncVariantLookup::AsyncOtherVariant;
+
         // If this method serves as the BODY of a MethodImpl specification, then
         // we should iterate all the MethodImpl's for this class and see just how many
         // of them this method participates in as the BODY.
@@ -5934,7 +5956,7 @@ MethodTableBuilder::ProcessInexactMethodImpls()
                 pItfEntry = &bmtInterface->pInterfaceMap[i];
 
                 // Search for declmethod on this interface
-                declMethod = FindDeclMethodOnInterfaceEntry(pItfEntry, declSig);
+                declMethod = FindDeclMethodOnInterfaceEntry(pItfEntry, declSig, asyncVariantOfDeclToFind);
 
                 // If we didn't find a match, continue on to next interface in the equivalence set
                 if (declMethod.IsNull())
@@ -6021,6 +6043,8 @@ MethodTableBuilder::ProcessMethodImpls()
             continue;
         }
 
+        AsyncVariantLookup asyncVariantOfDeclToFind = it->GetAsyncThunkType() == AsyncThunkType::NotAThunk ? AsyncVariantLookup::MatchingAsyncVariant : AsyncVariantLookup::AsyncOtherVariant;
+
         // If this method serves as the BODY of a MethodImpl specification, then
         // we should iterate all the MethodImpl's for this class and see just how many
         // of them this method participates in as the BODY.
@@ -6062,7 +6086,7 @@ MethodTableBuilder::ProcessMethodImpls()
                         }
 
                         CONSISTENCY_CHECK(TypeFromToken(mdDecl) == mdtMethodDef);
-                        declMethod = bmtMethod->FindDeclaredMethodByToken(mdDecl);
+                        declMethod = bmtMethod->FindDeclaredMethodByToken(mdDecl, asyncVariantOfDeclToFind);
                     }
                     else
                     {   // We can't call GetDescFromMemberDefOrRef here because this
@@ -6196,12 +6220,12 @@ MethodTableBuilder::ProcessMethodImpls()
                                 }
 
                                 // 3. Find the matching method.
-                                declMethod = FindDeclMethodOnInterfaceEntry(pItfEntry, declSig, isVirtualStaticOverride); // Search for statics when the impl is non-virtual
+                                declMethod = FindDeclMethodOnInterfaceEntry(pItfEntry, declSig, asyncVariantOfDeclToFind, isVirtualStaticOverride); // Search for statics when the impl is non-virtual
                             }
                             else
                             {
                                 GetHalfBakedClass()->SetHasVTableMethodImpl();
-                                declMethod = FindDeclMethodOnClassInHierarchy(it, pDeclMT, declSig);
+                                declMethod = FindDeclMethodOnClassInHierarchy(it, pDeclMT, declSig, asyncVariantOfDeclToFind);
                             }
 
                             if (declMethod.IsNull())
@@ -6226,12 +6250,6 @@ MethodTableBuilder::ProcessMethodImpls()
                         BuildMethodTableThrowException(IDS_CLASSLOAD_MI_DECLARATIONNOTFOUND, it.Token());
                     }
 
-                    // When working with the thunk, we want the opposite type of async method
-                    if (it->GetAsyncThunkType() != AsyncThunkType::NotAThunk)
-                    {
-                        declMethod = declMethod.GetAsyncOtherVariant();;
-                    }
-
                     if (!IsMdVirtual(declMethod.GetDeclAttrs()))
                     {   // Make sure the decl is virtual
                         BuildMethodTableThrowException(IDS_CLASSLOAD_MI_MUSTBEVIRTUAL, it.Token());
@@ -6258,7 +6276,7 @@ MethodTableBuilder::ProcessMethodImpls()
 }
 
 
-MethodTableBuilder::bmtMethodHandle MethodTableBuilder::FindDeclMethodOnClassInHierarchy(const DeclaredMethodIterator& it, MethodTable * pDeclMT, MethodSignature &declSig)
+MethodTableBuilder::bmtMethodHandle MethodTableBuilder::FindDeclMethodOnClassInHierarchy(const DeclaredMethodIterator& it, MethodTable * pDeclMT, MethodSignature &declSig, AsyncVariantLookup variantLookup)
 {
     bmtRTType * pDeclType = NULL;
     bmtMethodHandle declMethod;
@@ -6344,7 +6362,11 @@ MethodTableBuilder::bmtMethodHandle MethodTableBuilder::FindDeclMethodOnClassInH
                         FALSE,
                         iPass == 0 ? &newVisited : NULL))
                     {
+                        if (variantLookup == AsyncVariantLookup::AsyncOtherVariant)
+                            pCurMD = pCurMD->GetAsyncOtherVariant();
+
                         declMethod = (*bmtParent->pSlotTable)[pCurMD->GetSlot()].Decl();
+
                         break;
                     }
                 }
diff --git a/src/coreclr/vm/methodtablebuilder.h b/src/coreclr/vm/methodtablebuilder.h
index 4e967d4cf26c63..4b34c5df88702c 100644
--- a/src/coreclr/vm/methodtablebuilder.h
+++ b/src/coreclr/vm/methodtablebuilder.h
@@ -1212,7 +1212,7 @@ class MethodTableBuilder
         MethodDesc *
         GetMethodDesc() const;
 
-        bmtMethodHandle GetAsyncOtherVariant() const
+/*        bmtMethodHandle GetAsyncOtherVariant() const
         {
             if (IsRTMethod())
             {
@@ -1222,7 +1222,6 @@ class MethodTableBuilder
                 _ASSERTE(pRTOtherMethod != NULL);
                 _ASSERTE(pRTOtherMethod->IsAsyncThunkMethod() || pRTMethod->GetMethodDesc()->IsAsyncThunkMethod());
                 _ASSERTE(FALSE);
-                return bmtMethodHandle((bmtRTMethod*)NULL); // TODO! Fix this to do the right thing here
             }
             else
             {
@@ -1231,7 +1230,7 @@ class MethodTableBuilder
                 _ASSERTE(pMDOtherVariant->GetAsyncThunkType() != AsyncThunkType::NotAThunk || AsMDMethod()->GetAsyncThunkType() != AsyncThunkType::NotAThunk);
                 return pMDOtherVariant;
             }
-        }
+        }*/
 
     protected:
         //-----------------------------------------------------------------------------------------
@@ -1997,14 +1996,22 @@ class MethodTableBuilder
         // Searches the declared methods for a method with a token value equal to tok.
         bmtMDMethod *
         FindDeclaredMethodByToken(
-            mdMethodDef tok)
+            mdMethodDef tok, AsyncVariantLookup variantLookup)
         {
             LIMITED_METHOD_CONTRACT;
             for (SLOT_INDEX i = 0; i < m_cDeclaredMethods; ++i)
             {
                 if ((*this)[i]->GetMethodSignature().GetToken() == tok)
                 {
-                    return (*this)[i];
+                    auto result = (*this)[i];
+                    if (variantLookup == AsyncVariantLookup::AsyncOtherVariant)
+                    {
+                        return result->GetAsyncOtherVariant();
+                    }
+                    else
+                    {
+                        return result;
+                    }
                 }
             }
             return NULL;
@@ -2770,13 +2777,13 @@ class MethodTableBuilder
     // Find the decl method on a given interface entry that matches the method name+signature specified
     // If none is found, return a null method handle
     bmtMethodHandle
-    FindDeclMethodOnInterfaceEntry(bmtInterfaceEntry *pItfEntry, MethodSignature &declSig, bool searchForStaticMethods = false);
+    FindDeclMethodOnInterfaceEntry(bmtInterfaceEntry *pItfEntry, MethodSignature &declSig, AsyncVariantLookup variantLookup,  bool searchForStaticMethods = false);
 
     // --------------------------------------------------------------------------------------------
     // Find the decl method within the class hierarchy method name+signature specified
     // If none is found, return a null method handle
     bmtMethodHandle
-    FindDeclMethodOnClassInHierarchy(const DeclaredMethodIterator& it, MethodTable * pDeclMT, MethodSignature &declSig);
+    FindDeclMethodOnClassInHierarchy(const DeclaredMethodIterator& it, MethodTable * pDeclMT, MethodSignature &declSig, AsyncVariantLookup variantLookup);
 
     // --------------------------------------------------------------------------------------------
     // Throws if an entry already exists that has been MethodImpl'd. Adds the interface slot and

From ad0bd7148842c43f480545258d4e234733de4c83 Mon Sep 17 00:00:00 2001
From: David Wrighton <davidwr@microsoft.com>
Date: Wed, 20 Sep 2023 14:56:22 -0700
Subject: [PATCH 006/203] Oops pMethod can be NULL

---
 src/coreclr/vm/genericdict.cpp | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/src/coreclr/vm/genericdict.cpp b/src/coreclr/vm/genericdict.cpp
index abd477d0401ec1..9d07cb7ec7de8c 100644
--- a/src/coreclr/vm/genericdict.cpp
+++ b/src/coreclr/vm/genericdict.cpp
@@ -1054,7 +1054,6 @@ Dictionary::PopulateEntry(
                 }
             }
 
-            _ASSERTE((!!isAsyncThunk) == pMethod->IsAsyncThunkMethod());
 
             if (fRequiresDispatchStub)
             {
@@ -1087,6 +1086,8 @@ Dictionary::PopulateEntry(
                 break;
             }
 
+            _ASSERTE((!!isAsyncThunk) == pMethod->IsAsyncThunkMethod());
+
             Instantiation inst;
 
             // Instantiate the method if needed, or create a stub to a static method in a generic class.

From 8d416cbc5477d9a180caa922681ff598c7bcb36e Mon Sep 17 00:00:00 2001
From: David Wrighton <davidwr@microsoft.com>
Date: Thu, 21 Sep 2023 11:04:03 -0700
Subject: [PATCH 007/203] We are able to load Task<T> methods which are virtual
 again, next to actually write some "async" and start breaking things again

---
 src/coreclr/vm/methodtablebuilder.cpp | 75 ++++++++++++++++++++-------
 1 file changed, 56 insertions(+), 19 deletions(-)

diff --git a/src/coreclr/vm/methodtablebuilder.cpp b/src/coreclr/vm/methodtablebuilder.cpp
index d0f64636802283..6e67ca5ba377ec 100644
--- a/src/coreclr/vm/methodtablebuilder.cpp
+++ b/src/coreclr/vm/methodtablebuilder.cpp
@@ -916,22 +916,26 @@ MethodTableBuilder::MethodSignature::GetMethodAttributes() const
     STANDARD_VM_CONTRACT;
 
     IMDInternalImport * pIMD = GetModule()->GetMDImport();
+    DWORD cSig;
+    PCCOR_SIGNATURE pSig;
     if (TypeFromToken(GetToken()) == mdtMethodDef)
     {
-        DWORD cSig;
-        if (FAILED(pIMD->GetNameAndSigOfMethodDef(GetToken(), &m_pSig, &cSig, &m_szName)))
+        if (FAILED(pIMD->GetNameAndSigOfMethodDef(GetToken(), &pSig, &cSig, &m_szName)))
         {   // We have empty name or signature on error, do nothing
         }
-        m_cSig = static_cast<size_t>(cSig);
     }
     else
     {
         CONSISTENCY_CHECK(TypeFromToken(m_tok) == mdtMemberRef);
-        DWORD cSig;
-        if (FAILED(pIMD->GetNameAndSigOfMemberRef(GetToken(), &m_pSig, &cSig, &m_szName)))
+        if (FAILED(pIMD->GetNameAndSigOfMemberRef(GetToken(), &pSig, &cSig, &m_szName)))
         {   // We have empty name or signature on error, do nothing
         }
+    }
+    // Don't overwrite signature that may have already been provided for AsyncThunk method
+    if (m_cSig == 0)
+    {
         m_cSig = static_cast<size_t>(cSig);
+        m_pSig = pSig;
     }
 }
 
@@ -3550,35 +3554,68 @@ MethodTableBuilder::EnumerateClassMethods()
             {
                 ULONG cAsyncThunkMemberSignature = cMemberSignature;
                 AsyncThunkType thunkType;
+                ULONG originalTokenOffsetFromAsyncDetailsOffset;
+                ULONG newTokenOffsetFromAsyncDetailsOffset;
+                ULONG originalPrefixSize;
+                ULONG originalSuffixSize;
+                ULONG newSuffixSize;
+                ULONG newPrefixSize;
 
                 if (asyncMethodType == AsyncTaskMethod::Async2Method)
                 {
-                    cAsyncThunkMemberSignature += 1;
+                    cAsyncThunkMemberSignature += 2;
+                    originalTokenOffsetFromAsyncDetailsOffset = 1;
+                    newTokenOffsetFromAsyncDetailsOffset = 2;
                     thunkType = AsyncThunkType::AsyncToTask;
+                    originalPrefixSize = 1;
+                    newPrefixSize = 2;
+                    originalSuffixSize = 0;
+                    newSuffixSize = 1;
                 }
                 else
                 {
-                    cAsyncThunkMemberSignature -= 1;
+                    cAsyncThunkMemberSignature -= 2;
+                    originalTokenOffsetFromAsyncDetailsOffset = 2;
+                    newTokenOffsetFromAsyncDetailsOffset = 1;
                     thunkType = AsyncThunkType::TaskToAsync;
+                    originalPrefixSize = 2;
+                    newPrefixSize = 1;
+                    originalSuffixSize = 1;
+                    newSuffixSize = 0;
                 }
 
                 BYTE* pNewMemberSignature = AllocateFromHighFrequencyHeap(S_SIZE_T(cAsyncThunkMemberSignature));
-                ULONG tokenLen = CorSigUncompressedDataSize(&pMemberSignature[offsetOfAsyncDetails + 1]);
-                ULONG initialCopyLen = offsetOfAsyncDetails + 1 + tokenLen;
+                ULONG tokenLen = CorSigUncompressedDataSize(&pMemberSignature[offsetOfAsyncDetails + originalTokenOffsetFromAsyncDetailsOffset]);
+                ULONG originalTokenOffset = offsetOfAsyncDetails + originalTokenOffsetFromAsyncDetailsOffset;
+                ULONG newTokenOffset = offsetOfAsyncDetails + newTokenOffsetFromAsyncDetailsOffset;
+                ULONG originalRemainingSigOffset = offsetOfAsyncDetails + originalPrefixSize + tokenLen + originalSuffixSize;
+                ULONG newRemainingSigOffset = offsetOfAsyncDetails + newPrefixSize + tokenLen + newSuffixSize;
+
+                ULONG initialCopyLen = offsetOfAsyncDetails;
                 memcpy(pNewMemberSignature, pMemberSignature, initialCopyLen);
+                memcpy(pNewMemberSignature + newTokenOffset, pMemberSignature + originalTokenOffset, tokenLen);
+
+                _ASSERTE((cMemberSignature - originalRemainingSigOffset) == (cAsyncThunkMemberSignature - newRemainingSigOffset));
+                memcpy(pNewMemberSignature + newRemainingSigOffset, pMemberSignature + originalRemainingSigOffset, cMemberSignature - originalRemainingSigOffset);
 
                 if (asyncMethodType == AsyncTaskMethod::Async2Method)
                 {
+                    // Incoming sig will look something like ... E_T_CMOD_OPT <TokenOfTask> E_T_I4 ....
+                    // And needs to be translated to E_T_GENERICINST E_T_CLASS <TokenOfTask> 1 E_T_I4
+
                     // Replace the ELEMENT_TYPE_CMOD with ELEMENT_TYPE_GENERICINST, and then add a 1 after the token which refers to Task<T>
+
                     pNewMemberSignature[offsetOfAsyncDetails] = ELEMENT_TYPE_GENERICINST;
-                    pNewMemberSignature[initialCopyLen] = 1;
-                    memcpy(pNewMemberSignature + initialCopyLen + 1, pMemberSignature + initialCopyLen, cMemberSignature - initialCopyLen);
+                    pNewMemberSignature[offsetOfAsyncDetails + 1] = ELEMENT_TYPE_CLASS;
+                    pNewMemberSignature[newRemainingSigOffset - 1] = 1;
                 }
                 else
                 {
+                    // Incoming sig will look something like ... E_T_GENERICINST E_T_CLASS <TokenOfTask> 1 E_T_I4 ....
+                    // And needs to be translated to E_T_CMOD_OPT <TokenOfTask> E_T_I4
+
                     // Replace the ELEMENT_TYPE_GENERICINST with ELEMENT_TYPE_CMOD_OPT, and then remove the 1 which specifies the generic arg count for Task<T>
                     pNewMemberSignature[offsetOfAsyncDetails] = ELEMENT_TYPE_CMOD_OPT;
-                    memcpy(pNewMemberSignature + initialCopyLen, pMemberSignature + initialCopyLen + 1, cMemberSignature - (initialCopyLen + 1));
                 }
 
                 Signature newMemberSig(pNewMemberSignature, cAsyncThunkMemberSignature);
@@ -6564,6 +6601,13 @@ MethodTableBuilder::InitMethodDesc(
 #endif // !_DEBUG
         pNewMD->SetSynchronized();
 
+    if (thunkType != AsyncThunkType::NotAThunk)
+    {
+        AsyncThunkData* pThunkData = pNewMD->GetAddrOfAsyncThunkData();
+        pThunkData->sig = sig;
+        pThunkData->type = thunkType;
+    }
+
 #ifdef _DEBUG
     pNewMD->m_pszDebugMethodName = (LPUTF8)pszDebugMethodName;
     pNewMD->m_pszDebugClassName  = (LPUTF8)pszDebugClassName;
@@ -6574,13 +6618,6 @@ MethodTableBuilder::InitMethodDesc(
     else
         pNewMD->m_pszDebugMethodSignature = pszDebugMethodSignature;
 #endif // _DEBUG
-
-    if (thunkType != AsyncThunkType::NotAThunk)
-    {
-        AsyncThunkData* pThunkData = pNewMD->GetAddrOfAsyncThunkData();
-        pThunkData->sig = sig;
-        pThunkData->type = thunkType;
-    }
 } // MethodTableBuilder::InitMethodDesc
 
 //*******************************************************************************

From 0fbd60cec88b7c2f8638ced817b94aca95d7b584 Mon Sep 17 00:00:00 2001
From: David Wrighton <davidwr@microsoft.com>
Date: Thu, 21 Sep 2023 13:54:00 -0700
Subject: [PATCH 008/203] Add some simple benchmarks of the existing situation

---
 .../Loader/async/syncfibonacci-with-yields.cs | 48 +++++++++++++++++++
 .../async/syncfibonacci-with-yields.csproj    |  8 ++++
 .../async/syncfibonacci-without-yields.cs     | 48 +++++++++++++++++++
 .../async/syncfibonacci-without-yields.csproj |  8 ++++
 .../taskbased-asyncfibonacci-with-yields.cs   | 45 +++++++++++++++++
 ...askbased-asyncfibonacci-with-yields.csproj |  8 ++++
 ...taskbased-asyncfibonacci-without-yields.cs | 41 ++++++++++++++++
 ...based-asyncfibonacci-without-yields.csproj |  8 ++++
 ...luetaskbased-asyncfibonacci-with-yields.cs | 46 ++++++++++++++++++
 ...askbased-asyncfibonacci-with-yields.csproj |  8 ++++
 ...taskbased-asyncfibonacci-without-yields.cs | 41 ++++++++++++++++
 ...based-asyncfibonacci-without-yields.csproj |  8 ++++
 12 files changed, 317 insertions(+)
 create mode 100644 src/tests/Loader/async/syncfibonacci-with-yields.cs
 create mode 100644 src/tests/Loader/async/syncfibonacci-with-yields.csproj
 create mode 100644 src/tests/Loader/async/syncfibonacci-without-yields.cs
 create mode 100644 src/tests/Loader/async/syncfibonacci-without-yields.csproj
 create mode 100644 src/tests/Loader/async/taskbased-asyncfibonacci-with-yields.cs
 create mode 100644 src/tests/Loader/async/taskbased-asyncfibonacci-with-yields.csproj
 create mode 100644 src/tests/Loader/async/taskbased-asyncfibonacci-without-yields.cs
 create mode 100644 src/tests/Loader/async/taskbased-asyncfibonacci-without-yields.csproj
 create mode 100644 src/tests/Loader/async/valuetaskbased-asyncfibonacci-with-yields.cs
 create mode 100644 src/tests/Loader/async/valuetaskbased-asyncfibonacci-with-yields.csproj
 create mode 100644 src/tests/Loader/async/valuetaskbased-asyncfibonacci-without-yields.cs
 create mode 100644 src/tests/Loader/async/valuetaskbased-asyncfibonacci-without-yields.csproj

diff --git a/src/tests/Loader/async/syncfibonacci-with-yields.cs b/src/tests/Loader/async/syncfibonacci-with-yields.cs
new file mode 100644
index 00000000000000..9d668c9568367d
--- /dev/null
+++ b/src/tests/Loader/async/syncfibonacci-with-yields.cs
@@ -0,0 +1,48 @@
+// 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 System.Diagnostics;
+using System.Threading;
+using System.Runtime.CompilerServices;
+
+const uint Threshold = 1_000;
+
+for (int i = 0; i < 10; i++)
+{
+    var sw = new Stopwatch();
+    sw.Start();
+    uint result = A(100_000_000);
+    Console.WriteLine($"{sw.ElapsedMilliseconds} ms result={result}");
+}
+
+static uint A(uint n)
+{
+    uint result = n;
+    for (uint i = 0; i < n; i++)
+        result = B(result);
+    return result;
+}
+
+static uint B(uint n)
+{
+    uint result = n;
+
+    result = result * 1_999_999_981;
+    if (result < Threshold)
+        MyYield();
+
+    result = result * 1_999_999_981;
+    if (result < Threshold)
+        MyYield();
+
+    result = result * 1_999_999_981;
+    if (result < Threshold)
+        MyYield();
+
+    return result;
+}
+
+// Workaround for inlining of Thread.Yield inflating the caller's frame.
+[MethodImpl(MethodImplOptions.NoInlining)]
+static void MyYield() => Thread.Yield();
\ No newline at end of file
diff --git a/src/tests/Loader/async/syncfibonacci-with-yields.csproj b/src/tests/Loader/async/syncfibonacci-with-yields.csproj
new file mode 100644
index 00000000000000..92548675762273
--- /dev/null
+++ b/src/tests/Loader/async/syncfibonacci-with-yields.csproj
@@ -0,0 +1,8 @@
+<Project Sdk="Microsoft.NET.Sdk">
+  <PropertyGroup>
+    <CLRTestPriority>1</CLRTestPriority>
+  </PropertyGroup>
+  <ItemGroup>
+    <Compile Include="syncfibonacci-with-yields.cs" />
+  </ItemGroup>
+</Project>
diff --git a/src/tests/Loader/async/syncfibonacci-without-yields.cs b/src/tests/Loader/async/syncfibonacci-without-yields.cs
new file mode 100644
index 00000000000000..9d668c9568367d
--- /dev/null
+++ b/src/tests/Loader/async/syncfibonacci-without-yields.cs
@@ -0,0 +1,48 @@
+// 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 System.Diagnostics;
+using System.Threading;
+using System.Runtime.CompilerServices;
+
+const uint Threshold = 1_000;
+
+for (int i = 0; i < 10; i++)
+{
+    var sw = new Stopwatch();
+    sw.Start();
+    uint result = A(100_000_000);
+    Console.WriteLine($"{sw.ElapsedMilliseconds} ms result={result}");
+}
+
+static uint A(uint n)
+{
+    uint result = n;
+    for (uint i = 0; i < n; i++)
+        result = B(result);
+    return result;
+}
+
+static uint B(uint n)
+{
+    uint result = n;
+
+    result = result * 1_999_999_981;
+    if (result < Threshold)
+        MyYield();
+
+    result = result * 1_999_999_981;
+    if (result < Threshold)
+        MyYield();
+
+    result = result * 1_999_999_981;
+    if (result < Threshold)
+        MyYield();
+
+    return result;
+}
+
+// Workaround for inlining of Thread.Yield inflating the caller's frame.
+[MethodImpl(MethodImplOptions.NoInlining)]
+static void MyYield() => Thread.Yield();
\ No newline at end of file
diff --git a/src/tests/Loader/async/syncfibonacci-without-yields.csproj b/src/tests/Loader/async/syncfibonacci-without-yields.csproj
new file mode 100644
index 00000000000000..b2d4498b9b2347
--- /dev/null
+++ b/src/tests/Loader/async/syncfibonacci-without-yields.csproj
@@ -0,0 +1,8 @@
+<Project Sdk="Microsoft.NET.Sdk">
+  <PropertyGroup>
+    <CLRTestPriority>1</CLRTestPriority>
+  </PropertyGroup>
+  <ItemGroup>
+    <Compile Include="syncfibonacci-without-yields.cs" />
+  </ItemGroup>
+</Project>
diff --git a/src/tests/Loader/async/taskbased-asyncfibonacci-with-yields.cs b/src/tests/Loader/async/taskbased-asyncfibonacci-with-yields.cs
new file mode 100644
index 00000000000000..8fe98070a93f52
--- /dev/null
+++ b/src/tests/Loader/async/taskbased-asyncfibonacci-with-yields.cs
@@ -0,0 +1,45 @@
+// 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 System.Threading.Tasks;
+﻿using System.Diagnostics;
+using System.Runtime.CompilerServices;
+
+const uint Threshold = 1_000;
+
+for (int i = 0; i < 10; i++)
+{
+    var sw = new Stopwatch();
+    sw.Start();
+    uint result = await A(100_000_000);
+    Console.WriteLine($"{sw.ElapsedMilliseconds} ms result={result}");
+}
+
+return 100;
+
+static async Task<uint> A(uint n)
+{
+    uint result = n;
+    for (uint i = 0; i < n; i++)
+        result = await B(result);
+    return result;
+}
+
+static async Task<uint> B(uint n){
+    uint result = n;
+
+    result = result * 1_999_999_981;
+    if (result < Threshold)
+        await Task.Yield();
+
+    result = result * 1_999_999_981;
+    if (result < Threshold)
+        await Task.Yield();
+
+    result = result * 1_999_999_981;
+    if (result < Threshold)
+        await Task.Yield();
+
+    return result;
+}
\ No newline at end of file
diff --git a/src/tests/Loader/async/taskbased-asyncfibonacci-with-yields.csproj b/src/tests/Loader/async/taskbased-asyncfibonacci-with-yields.csproj
new file mode 100644
index 00000000000000..0ce5b85c6dda70
--- /dev/null
+++ b/src/tests/Loader/async/taskbased-asyncfibonacci-with-yields.csproj
@@ -0,0 +1,8 @@
+<Project Sdk="Microsoft.NET.Sdk">
+  <PropertyGroup>
+    <CLRTestPriority>1</CLRTestPriority>
+  </PropertyGroup>
+  <ItemGroup>
+    <Compile Include="taskbased-asyncfibonacci-with-yields.cs" />
+  </ItemGroup>
+</Project>
diff --git a/src/tests/Loader/async/taskbased-asyncfibonacci-without-yields.cs b/src/tests/Loader/async/taskbased-asyncfibonacci-without-yields.cs
new file mode 100644
index 00000000000000..409586183a849f
--- /dev/null
+++ b/src/tests/Loader/async/taskbased-asyncfibonacci-without-yields.cs
@@ -0,0 +1,41 @@
+// 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 System.Diagnostics;
+using System.Runtime.CompilerServices;
+using System.Threading.Tasks;
+
+const uint Threshold = 1_000;
+
+for (int i = 0; i < 10; i++)
+{
+    var sw = new Stopwatch();
+    sw.Start();
+    uint result = await A(100_000_000);
+    Console.WriteLine($"{sw.ElapsedMilliseconds} ms result={result}");
+}
+
+return 100;
+
+static async Task<uint> A(uint n)
+{
+    uint result = n;
+    for (uint i = 0; i < n; i++)
+        result = await B(result);
+    return result;
+}
+
+#pragma warning disable CS1998
+static async Task<uint> B(uint n)
+{
+    uint result = n;
+
+    result = result * 1_999_999_981;
+
+    result = result * 1_999_999_981;
+
+    result = result * 1_999_999_981;
+
+    return result;
+}
diff --git a/src/tests/Loader/async/taskbased-asyncfibonacci-without-yields.csproj b/src/tests/Loader/async/taskbased-asyncfibonacci-without-yields.csproj
new file mode 100644
index 00000000000000..42d69ec59b1cb5
--- /dev/null
+++ b/src/tests/Loader/async/taskbased-asyncfibonacci-without-yields.csproj
@@ -0,0 +1,8 @@
+<Project Sdk="Microsoft.NET.Sdk">
+  <PropertyGroup>
+    <CLRTestPriority>1</CLRTestPriority>
+  </PropertyGroup>
+  <ItemGroup>
+    <Compile Include="taskbased-asyncfibonacci-without-yields.cs" />
+  </ItemGroup>
+</Project>
diff --git a/src/tests/Loader/async/valuetaskbased-asyncfibonacci-with-yields.cs b/src/tests/Loader/async/valuetaskbased-asyncfibonacci-with-yields.cs
new file mode 100644
index 00000000000000..29f4d892cb41dc
--- /dev/null
+++ b/src/tests/Loader/async/valuetaskbased-asyncfibonacci-with-yields.cs
@@ -0,0 +1,46 @@
+// 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 System.Threading.Tasks;
+﻿using System.Diagnostics;
+using System.Runtime.CompilerServices;
+
+const uint Threshold = 1_000;
+
+for (int i = 0; i < 10; i++)
+{
+    var sw = new Stopwatch();
+    sw.Start();
+    uint result = await A(100_000_000);
+    Console.WriteLine($"{sw.ElapsedMilliseconds} ms result={result}");
+}
+
+return 100;
+
+static async ValueTask<uint> A(uint n)
+{
+    uint result = n;
+    for (uint i = 0; i < n; i++)
+        result = await B(result);
+    return result;
+}
+
+static async ValueTask<uint> B(uint n)
+{
+    uint result = n;
+
+    result = result * 1_999_999_981;
+    if (result < Threshold)
+        await Task.Yield();
+
+    result = result * 1_999_999_981;
+    if (result < Threshold)
+        await Task.Yield();
+
+    result = result * 1_999_999_981;
+    if (result < Threshold)
+        await Task.Yield();
+
+    return result;
+}
\ No newline at end of file
diff --git a/src/tests/Loader/async/valuetaskbased-asyncfibonacci-with-yields.csproj b/src/tests/Loader/async/valuetaskbased-asyncfibonacci-with-yields.csproj
new file mode 100644
index 00000000000000..07ce3d1c562473
--- /dev/null
+++ b/src/tests/Loader/async/valuetaskbased-asyncfibonacci-with-yields.csproj
@@ -0,0 +1,8 @@
+<Project Sdk="Microsoft.NET.Sdk">
+  <PropertyGroup>
+    <CLRTestPriority>1</CLRTestPriority>
+  </PropertyGroup>
+  <ItemGroup>
+    <Compile Include="valuetaskbased-asyncfibonacci-with-yields.cs" />
+  </ItemGroup>
+</Project>
diff --git a/src/tests/Loader/async/valuetaskbased-asyncfibonacci-without-yields.cs b/src/tests/Loader/async/valuetaskbased-asyncfibonacci-without-yields.cs
new file mode 100644
index 00000000000000..ce28a34ed45d49
--- /dev/null
+++ b/src/tests/Loader/async/valuetaskbased-asyncfibonacci-without-yields.cs
@@ -0,0 +1,41 @@
+// 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 System.Diagnostics;
+using System.Runtime.CompilerServices;
+using System.Threading.Tasks;
+
+const uint Threshold = 1_000;
+
+for (int i = 0; i < 10; i++)
+{
+    var sw = new Stopwatch();
+    sw.Start();
+    uint result = await A(100_000_000);
+    Console.WriteLine($"{sw.ElapsedMilliseconds} ms result={result}");
+}
+
+return 100;
+
+static async ValueTask<uint> A(uint n)
+{
+    uint result = n;
+    for (uint i = 0; i < n; i++)
+        result = await B(result);
+    return result;
+}
+
+#pragma warning disable CS1998
+static async ValueTask<uint> B(uint n)
+{
+    uint result = n;
+
+    result = result * 1_999_999_981;
+
+    result = result * 1_999_999_981;
+
+    result = result * 1_999_999_981;
+
+    return result;
+}
diff --git a/src/tests/Loader/async/valuetaskbased-asyncfibonacci-without-yields.csproj b/src/tests/Loader/async/valuetaskbased-asyncfibonacci-without-yields.csproj
new file mode 100644
index 00000000000000..d13c6b884d0595
--- /dev/null
+++ b/src/tests/Loader/async/valuetaskbased-asyncfibonacci-without-yields.csproj
@@ -0,0 +1,8 @@
+<Project Sdk="Microsoft.NET.Sdk">
+  <PropertyGroup>
+    <CLRTestPriority>1</CLRTestPriority>
+  </PropertyGroup>
+  <ItemGroup>
+    <Compile Include="valuetaskbased-asyncfibonacci-without-yields.cs" />
+  </ItemGroup>
+</Project>

From 3270fdf67fcc15e825f97245d06fececf6ab18c6 Mon Sep 17 00:00:00 2001
From: David Wrighton <davidwr@microsoft.com>
Date: Thu, 21 Sep 2023 16:35:06 -0700
Subject: [PATCH 009/203] Add Test case for synchronous behavior

---
 ...ntimetask-asyncfibonacci-without-yields.il | 377 ++++++++++++++++++
 ...etask-asyncfibonacci-without-yields.ilproj |   8 +
 .../async/syncfibonacci-without-yields.cs     |  10 -
 3 files changed, 385 insertions(+), 10 deletions(-)
 create mode 100644 src/tests/Loader/async/runtimetask-asyncfibonacci-without-yields.il
 create mode 100644 src/tests/Loader/async/runtimetask-asyncfibonacci-without-yields.ilproj

diff --git a/src/tests/Loader/async/runtimetask-asyncfibonacci-without-yields.il b/src/tests/Loader/async/runtimetask-asyncfibonacci-without-yields.il
new file mode 100644
index 00000000000000..b48f72bb7ea9f8
--- /dev/null
+++ b/src/tests/Loader/async/runtimetask-asyncfibonacci-without-yields.il
@@ -0,0 +1,377 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+
+//  .NET IL Disassembler.  Version 9.0.0-dev
+
+
+
+// Metadata version: v4.0.30319
+.assembly extern System.Runtime
+{
+  .publickeytoken = (B0 3F 5F 7F 11 D5 0A 3A )                         // .?_....:
+  .ver 9:0:0:0
+}
+.assembly extern System.Console
+{
+  .publickeytoken = (B0 3F 5F 7F 11 D5 0A 3A )                         // .?_....:
+  .ver 9:0:0:0
+}
+.assembly 'runtimetask-asyncfibonacci-without-yields'
+{
+  .custom instance void [System.Runtime]System.Runtime.CompilerServices.CompilationRelaxationsAttribute::.ctor(int32) = ( 01 00 08 00 00 00 00 00 ) 
+  .custom instance void [System.Runtime]System.Runtime.CompilerServices.RuntimeCompatibilityAttribute::.ctor() = ( 01 00 01 00 54 02 16 57 72 61 70 4E 6F 6E 45 78   // ....T..WrapNonEx
+                                                                                                                   63 65 70 74 69 6F 6E 54 68 72 6F 77 73 01 )       // ceptionThrows.
+
+  // --- The following custom attribute is added automatically, do not uncomment -------
+  //  .custom instance void [System.Runtime]System.Diagnostics.DebuggableAttribute::.ctor(valuetype [System.Runtime]System.Diagnostics.DebuggableAttribute/DebuggingModes) = ( 01 00 07 01 00 00 00 00 ) 
+
+  .hash algorithm 0x00008004
+  .ver 0:0:0:0
+}
+.module 'runtimetask-asyncfibonacci-without-yields.dll'
+// MVID: {ca42840a-c801-47a9-8471-ea986ae2c60d}
+.custom instance void [System.Runtime]System.Runtime.CompilerServices.RefSafetyRulesAttribute::.ctor(int32) = ( 01 00 0B 00 00 00 00 00 ) 
+.imagebase 0x00400000
+.file alignment 0x00000200
+.stackreserve 0x00100000
+.subsystem 0x0003       // WINDOWS_CUI
+.corflags 0x00000001    //  ILONLY
+// Image base: 0x000001E00BDE0000
+
+
+// =============== CLASS MEMBERS DECLARATION ===================
+
+.class private auto ansi beforefieldinit Program
+       extends [System.Runtime]System.Object
+{
+  .class auto ansi sealed nested private beforefieldinit '<<Main>$>d__0'
+         extends [System.Runtime]System.Object
+         implements [System.Runtime]System.Runtime.CompilerServices.IAsyncStateMachine
+  {
+    .custom instance void [System.Runtime]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) 
+    .field public int32 '<>1__state'
+    .field public valuetype [System.Runtime]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<int32> '<>t__builder'
+    .field public string[] args
+    .field private int32 '<i>5__1'
+    .field private class [System.Runtime]System.Diagnostics.Stopwatch '<sw>5__2'
+    .field private uint32 '<result>5__3'
+    .field private uint32 '<>s__4'
+    .field private valuetype [System.Runtime]System.Runtime.CompilerServices.TaskAwaiter`1<uint32> '<>u__1'
+    .method public hidebysig specialname rtspecialname 
+            instance void  .ctor() cil managed
+    {
+      // Code size       8 (0x8)
+      .maxstack  8
+      IL_0000:  ldarg.0
+      IL_0001:  call       instance void [System.Runtime]System.Object::.ctor()
+      IL_0006:  nop
+      IL_0007:  ret
+    } // end of method '<<Main>$>d__0'::.ctor
+
+    .method private hidebysig newslot virtual final 
+            instance void  MoveNext() cil managed
+    {
+      .override [System.Runtime]System.Runtime.CompilerServices.IAsyncStateMachine::MoveNext
+      // Code size       334 (0x14e)
+      .maxstack  3
+      .locals init (int32 V_0,
+               int32 V_1,
+               valuetype [System.Runtime]System.Runtime.CompilerServices.TaskAwaiter`1<uint32> V_2,
+               class Program/'<<Main>$>d__0' V_3,
+               valuetype [System.Runtime]System.Runtime.CompilerServices.DefaultInterpolatedStringHandler V_4,
+               int32 V_5,
+               bool V_6,
+               class [System.Runtime]System.Exception V_7)
+      IL_0000:  ldarg.0
+      IL_0001:  ldfld      int32 Program/'<<Main>$>d__0'::'<>1__state'
+      IL_0006:  stloc.0
+      .try
+      {
+        IL_0007:  ldloc.0
+        IL_0008:  brfalse.s  IL_000c
+
+        IL_000a:  br.s       IL_000e
+
+        IL_000c:  br.s       IL_0072
+
+        IL_000e:  ldarg.0
+        IL_000f:  ldc.i4.0
+        IL_0010:  stfld      int32 Program/'<<Main>$>d__0'::'<i>5__1'
+        IL_0015:  br         IL_0106
+
+        IL_001a:  nop
+        IL_001b:  ldarg.0
+        IL_001c:  newobj     instance void [System.Runtime]System.Diagnostics.Stopwatch::.ctor()
+        IL_0021:  stfld      class [System.Runtime]System.Diagnostics.Stopwatch Program/'<<Main>$>d__0'::'<sw>5__2'
+        IL_0026:  ldarg.0
+        IL_0027:  ldfld      class [System.Runtime]System.Diagnostics.Stopwatch Program/'<<Main>$>d__0'::'<sw>5__2'
+        IL_002c:  callvirt   instance void [System.Runtime]System.Diagnostics.Stopwatch::Start()
+        IL_0031:  nop
+        IL_0032:  ldc.i4     0x5f5e100
+        IL_0037:  call       class [System.Runtime]System.Threading.Tasks.Task`1<uint32> Program::'<<Main>$>g__A|0_0'(uint32)
+        IL_003c:  callvirt   instance valuetype [System.Runtime]System.Runtime.CompilerServices.TaskAwaiter`1<!0> class [System.Runtime]System.Threading.Tasks.Task`1<uint32>::GetAwaiter()
+        IL_0041:  stloc.2
+        IL_0042:  ldloca.s   V_2
+        IL_0044:  call       instance bool valuetype [System.Runtime]System.Runtime.CompilerServices.TaskAwaiter`1<uint32>::get_IsCompleted()
+        IL_0049:  brtrue.s   IL_008e
+
+        IL_004b:  ldarg.0
+        IL_004c:  ldc.i4.0
+        IL_004d:  dup
+        IL_004e:  stloc.0
+        IL_004f:  stfld      int32 Program/'<<Main>$>d__0'::'<>1__state'
+        IL_0054:  ldarg.0
+        IL_0055:  ldloc.2
+        IL_0056:  stfld      valuetype [System.Runtime]System.Runtime.CompilerServices.TaskAwaiter`1<uint32> Program/'<<Main>$>d__0'::'<>u__1'
+        IL_005b:  ldarg.0
+        IL_005c:  stloc.3
+        IL_005d:  ldarg.0
+        IL_005e:  ldflda     valuetype [System.Runtime]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<int32> Program/'<<Main>$>d__0'::'<>t__builder'
+        IL_0063:  ldloca.s   V_2
+        IL_0065:  ldloca.s   V_3
+        IL_0067:  call       instance void valuetype [System.Runtime]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<int32>::AwaitUnsafeOnCompleted<valuetype [System.Runtime]System.Runtime.CompilerServices.TaskAwaiter`1<uint32>,class Program/'<<Main>$>d__0'>(!!0&,
+                                                                                                                                                                                                                                                                            !!1&)
+        IL_006c:  nop
+        IL_006d:  leave      IL_014d
+
+        IL_0072:  ldarg.0
+        IL_0073:  ldfld      valuetype [System.Runtime]System.Runtime.CompilerServices.TaskAwaiter`1<uint32> Program/'<<Main>$>d__0'::'<>u__1'
+        IL_0078:  stloc.2
+        IL_0079:  ldarg.0
+        IL_007a:  ldflda     valuetype [System.Runtime]System.Runtime.CompilerServices.TaskAwaiter`1<uint32> Program/'<<Main>$>d__0'::'<>u__1'
+        IL_007f:  initobj    valuetype [System.Runtime]System.Runtime.CompilerServices.TaskAwaiter`1<uint32>
+        IL_0085:  ldarg.0
+        IL_0086:  ldc.i4.m1
+        IL_0087:  dup
+        IL_0088:  stloc.0
+        IL_0089:  stfld      int32 Program/'<<Main>$>d__0'::'<>1__state'
+        IL_008e:  ldarg.0
+        IL_008f:  ldloca.s   V_2
+        IL_0091:  call       instance !0 valuetype [System.Runtime]System.Runtime.CompilerServices.TaskAwaiter`1<uint32>::GetResult()
+        IL_0096:  stfld      uint32 Program/'<<Main>$>d__0'::'<>s__4'
+        IL_009b:  ldarg.0
+        IL_009c:  ldarg.0
+        IL_009d:  ldfld      uint32 Program/'<<Main>$>d__0'::'<>s__4'
+        IL_00a2:  stfld      uint32 Program/'<<Main>$>d__0'::'<result>5__3'
+        IL_00a7:  ldc.i4.s   11
+        IL_00a9:  ldc.i4.2
+        IL_00aa:  newobj     instance void [System.Runtime]System.Runtime.CompilerServices.DefaultInterpolatedStringHandler::.ctor(int32,
+                                                                                                                                   int32)
+        IL_00af:  stloc.s    V_4
+        IL_00b1:  ldloca.s   V_4
+        IL_00b3:  ldarg.0
+        IL_00b4:  ldfld      class [System.Runtime]System.Diagnostics.Stopwatch Program/'<<Main>$>d__0'::'<sw>5__2'
+        IL_00b9:  callvirt   instance int64 [System.Runtime]System.Diagnostics.Stopwatch::get_ElapsedMilliseconds()
+        IL_00be:  call       instance void [System.Runtime]System.Runtime.CompilerServices.DefaultInterpolatedStringHandler::AppendFormatted<int64>(!!0)
+        IL_00c3:  nop
+        IL_00c4:  ldloca.s   V_4
+        IL_00c6:  ldstr      " ms result="
+        IL_00cb:  call       instance void [System.Runtime]System.Runtime.CompilerServices.DefaultInterpolatedStringHandler::AppendLiteral(string)
+        IL_00d0:  nop
+        IL_00d1:  ldloca.s   V_4
+        IL_00d3:  ldarg.0
+        IL_00d4:  ldfld      uint32 Program/'<<Main>$>d__0'::'<result>5__3'
+        IL_00d9:  call       instance void [System.Runtime]System.Runtime.CompilerServices.DefaultInterpolatedStringHandler::AppendFormatted<uint32>(!!0)
+        IL_00de:  nop
+        IL_00df:  ldloca.s   V_4
+        IL_00e1:  call       instance string [System.Runtime]System.Runtime.CompilerServices.DefaultInterpolatedStringHandler::ToStringAndClear()
+        IL_00e6:  call       void [System.Console]System.Console::WriteLine(string)
+        IL_00eb:  nop
+        IL_00ec:  nop
+        IL_00ed:  ldarg.0
+        IL_00ee:  ldnull
+        IL_00ef:  stfld      class [System.Runtime]System.Diagnostics.Stopwatch Program/'<<Main>$>d__0'::'<sw>5__2'
+        IL_00f4:  ldarg.0
+        IL_00f5:  ldfld      int32 Program/'<<Main>$>d__0'::'<i>5__1'
+        IL_00fa:  stloc.s    V_5
+        IL_00fc:  ldarg.0
+        IL_00fd:  ldloc.s    V_5
+        IL_00ff:  ldc.i4.1
+        IL_0100:  add
+        IL_0101:  stfld      int32 Program/'<<Main>$>d__0'::'<i>5__1'
+        IL_0106:  ldarg.0
+        IL_0107:  ldfld      int32 Program/'<<Main>$>d__0'::'<i>5__1'
+        IL_010c:  ldc.i4.s   10
+        IL_010e:  clt
+        IL_0110:  stloc.s    V_6
+        IL_0112:  ldloc.s    V_6
+        IL_0114:  brtrue     IL_001a
+
+        IL_0119:  ldc.i4.s   100
+        IL_011b:  stloc.1
+        IL_011c:  leave.s    IL_0138
+
+      }  // end .try
+      catch [System.Runtime]System.Exception 
+      {
+        IL_011e:  stloc.s    V_7
+        IL_0120:  ldarg.0
+        IL_0121:  ldc.i4.s   -2
+        IL_0123:  stfld      int32 Program/'<<Main>$>d__0'::'<>1__state'
+        IL_0128:  ldarg.0
+        IL_0129:  ldflda     valuetype [System.Runtime]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<int32> Program/'<<Main>$>d__0'::'<>t__builder'
+        IL_012e:  ldloc.s    V_7
+        IL_0130:  call       instance void valuetype [System.Runtime]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<int32>::SetException(class [System.Runtime]System.Exception)
+        IL_0135:  nop
+        IL_0136:  leave.s    IL_014d
+
+      }  // end handler
+      IL_0138:  ldarg.0
+      IL_0139:  ldc.i4.s   -2
+      IL_013b:  stfld      int32 Program/'<<Main>$>d__0'::'<>1__state'
+      IL_0140:  ldarg.0
+      IL_0141:  ldflda     valuetype [System.Runtime]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<int32> Program/'<<Main>$>d__0'::'<>t__builder'
+      IL_0146:  ldloc.1
+      IL_0147:  call       instance void valuetype [System.Runtime]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<int32>::SetResult(!0)
+      IL_014c:  nop
+      IL_014d:  ret
+    } // end of method '<<Main>$>d__0'::MoveNext
+
+    .method private hidebysig newslot virtual final 
+            instance void  SetStateMachine(class [System.Runtime]System.Runtime.CompilerServices.IAsyncStateMachine stateMachine) cil managed
+    {
+      .custom instance void [System.Runtime]System.Diagnostics.DebuggerHiddenAttribute::.ctor() = ( 01 00 00 00 ) 
+      .override [System.Runtime]System.Runtime.CompilerServices.IAsyncStateMachine::SetStateMachine
+      // Code size       1 (0x1)
+      .maxstack  8
+      IL_0000:  ret
+    } // end of method '<<Main>$>d__0'::SetStateMachine
+
+  } // end of class '<<Main>$>d__0'
+
+  .method private hidebysig static class [System.Runtime]System.Threading.Tasks.Task`1<int32> 
+          '<Main>$'(string[] args) cil managed
+  {
+    .custom instance void [System.Runtime]System.Runtime.CompilerServices.AsyncStateMachineAttribute::.ctor(class [System.Runtime]System.Type) = ( 01 00 15 50 72 6F 67 72 61 6D 2B 3C 3C 4D 61 69   // ...Program+<<Mai
+                                                                                                                                                   6E 3E 24 3E 64 5F 5F 30 00 00 )                   // n>$>d__0..
+    .custom instance void [System.Runtime]System.Diagnostics.DebuggerStepThroughAttribute::.ctor() = ( 01 00 00 00 ) 
+    // Code size       57 (0x39)
+    .maxstack  2
+    .locals init (class Program/'<<Main>$>d__0' V_0)
+    IL_0000:  newobj     instance void Program/'<<Main>$>d__0'::.ctor()
+    IL_0005:  stloc.0
+    IL_0006:  ldloc.0
+    IL_0007:  call       valuetype [System.Runtime]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<!0> valuetype [System.Runtime]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<int32>::Create()
+    IL_000c:  stfld      valuetype [System.Runtime]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<int32> Program/'<<Main>$>d__0'::'<>t__builder'
+    IL_0011:  ldloc.0
+    IL_0012:  ldarg.0
+    IL_0013:  stfld      string[] Program/'<<Main>$>d__0'::args
+    IL_0018:  ldloc.0
+    IL_0019:  ldc.i4.m1
+    IL_001a:  stfld      int32 Program/'<<Main>$>d__0'::'<>1__state'
+    IL_001f:  ldloc.0
+    IL_0020:  ldflda     valuetype [System.Runtime]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<int32> Program/'<<Main>$>d__0'::'<>t__builder'
+    IL_0025:  ldloca.s   V_0
+    IL_0027:  call       instance void valuetype [System.Runtime]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<int32>::Start<class Program/'<<Main>$>d__0'>(!!0&)
+    IL_002c:  nop
+    IL_002d:  ldloc.0
+    IL_002e:  ldflda     valuetype [System.Runtime]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<int32> Program/'<<Main>$>d__0'::'<>t__builder'
+    IL_0033:  call       instance class [System.Runtime]System.Threading.Tasks.Task`1<!0> valuetype [System.Runtime]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<int32>::get_Task()
+    IL_0038:  ret
+  } // end of method Program::'<Main>$'
+
+  .method public hidebysig specialname rtspecialname 
+          instance void  .ctor() cil managed
+  {
+    // Code size       8 (0x8)
+    .maxstack  8
+    IL_0000:  ldarg.0
+    IL_0001:  call       instance void [System.Runtime]System.Object::.ctor()
+    IL_0006:  nop
+    IL_0007:  ret
+  } // end of method Program::.ctor
+
+  .method private hidebysig specialname static 
+          int32  '<Main>'(string[] args) cil managed
+  {
+    .entrypoint
+    .custom instance void [System.Runtime]System.Diagnostics.DebuggerStepThroughAttribute::.ctor() = ( 01 00 00 00 ) 
+    // Code size       20 (0x14)
+    .maxstack  1
+    .locals init (valuetype [System.Runtime]System.Runtime.CompilerServices.TaskAwaiter`1<int32> V_0)
+    IL_0000:  ldarg.0
+    IL_0001:  call       class [System.Runtime]System.Threading.Tasks.Task`1<int32> Program::'<Main>$'(string[])
+    IL_0006:  callvirt   instance valuetype [System.Runtime]System.Runtime.CompilerServices.TaskAwaiter`1<!0> class [System.Runtime]System.Threading.Tasks.Task`1<int32>::GetAwaiter()
+    IL_000b:  stloc.0
+    IL_000c:  ldloca.s   V_0
+    IL_000e:  call       instance !0 valuetype [System.Runtime]System.Runtime.CompilerServices.TaskAwaiter`1<int32>::GetResult()
+    IL_0013:  ret
+  } // end of method Program::'<Main>'
+
+  .method assembly hidebysig static uint32 modopt([System.Runtime]System.Threading.Tasks.Task`1)
+          '<<Main>$>g__A|0_0'(uint32 n) cil managed
+  {
+    .custom instance void [System.Runtime]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) 
+    // Code size       32 (0x20)
+    .maxstack  2
+    .locals init (uint32 V_0,
+             uint32 V_1,
+             bool V_2,
+             uint32 V_3)
+    IL_0000:  nop
+    IL_0001:  ldarg.0
+    IL_0002:  stloc.0
+    IL_0003:  ldc.i4.0
+    IL_0004:  stloc.1
+    IL_0005:  br.s       IL_0012
+
+    IL_0007:  ldloc.0
+    IL_0008:  call       uint32 modopt([System.Runtime]System.Threading.Tasks.Task`1) Program::'<<Main>$>g__B|0_1'(uint32)
+    IL_000d:  stloc.0
+    IL_000e:  ldloc.1
+    IL_000f:  ldc.i4.1
+    IL_0010:  add
+    IL_0011:  stloc.1
+    IL_0012:  ldloc.1
+    IL_0013:  ldarg.0
+    IL_0014:  clt.un
+    IL_0016:  stloc.2
+    IL_0017:  ldloc.2
+    IL_0018:  brtrue.s   IL_0007
+
+    IL_001a:  ldloc.0
+    IL_001b:  stloc.3
+    IL_001c:  br.s       IL_001e
+
+    IL_001e:  ldloc.3
+    IL_001f:  ret
+  } // end of method Program::'<<Main>$>g__A|0_0'
+
+  .method assembly hidebysig static uint32 modopt([System.Runtime]System.Threading.Tasks.Task`1)
+          '<<Main>$>g__B|0_1'(uint32 n) cil managed
+  {
+    .custom instance void [System.Runtime]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) 
+    // Code size       33 (0x21)
+    .maxstack  2
+    .locals init (uint32 V_0,
+             uint32 V_1)
+    IL_0000:  nop
+    IL_0001:  ldarg.0
+    IL_0002:  stloc.0
+    IL_0003:  ldloc.0
+    IL_0004:  ldc.i4     0x773593ed
+    IL_0009:  mul
+    IL_000a:  stloc.0
+    IL_000b:  ldloc.0
+    IL_000c:  ldc.i4     0x773593ed
+    IL_0011:  mul
+    IL_0012:  stloc.0
+    IL_0013:  ldloc.0
+    IL_0014:  ldc.i4     0x773593ed
+    IL_0019:  mul
+    IL_001a:  stloc.0
+    IL_001b:  ldloc.0
+    IL_001c:  stloc.1
+    IL_001d:  br.s       IL_001f
+
+    IL_001f:  ldloc.1
+    IL_0020:  ret
+  } // end of method Program::'<<Main>$>g__B|0_1'
+
+} // end of class Program
+
+
+// =============================================================
+
+// *********** DISASSEMBLY COMPLETE ***********************
diff --git a/src/tests/Loader/async/runtimetask-asyncfibonacci-without-yields.ilproj b/src/tests/Loader/async/runtimetask-asyncfibonacci-without-yields.ilproj
new file mode 100644
index 00000000000000..bd17dd2bad5a7e
--- /dev/null
+++ b/src/tests/Loader/async/runtimetask-asyncfibonacci-without-yields.ilproj
@@ -0,0 +1,8 @@
+<Project Sdk="Microsoft.NET.Sdk.IL">
+  <PropertyGroup>
+    <CLRTestPriority>1</CLRTestPriority>
+  </PropertyGroup>
+  <ItemGroup>
+    <Compile Include="runtimetask-asyncfibonacci-without-yields.il" />
+  </ItemGroup>
+</Project>
diff --git a/src/tests/Loader/async/syncfibonacci-without-yields.cs b/src/tests/Loader/async/syncfibonacci-without-yields.cs
index 9d668c9568367d..9f9c480660f038 100644
--- a/src/tests/Loader/async/syncfibonacci-without-yields.cs
+++ b/src/tests/Loader/async/syncfibonacci-without-yields.cs
@@ -29,20 +29,10 @@ static uint B(uint n)
     uint result = n;
 
     result = result * 1_999_999_981;
-    if (result < Threshold)
-        MyYield();
 
     result = result * 1_999_999_981;
-    if (result < Threshold)
-        MyYield();
 
     result = result * 1_999_999_981;
-    if (result < Threshold)
-        MyYield();
 
     return result;
 }
-
-// Workaround for inlining of Thread.Yield inflating the caller's frame.
-[MethodImpl(MethodImplOptions.NoInlining)]
-static void MyYield() => Thread.Yield();
\ No newline at end of file

From 84ae54db03ae64ee8df60978be2ecf9dec0e1530 Mon Sep 17 00:00:00 2001
From: David Wrighton <davidwr@microsoft.com>
Date: Thu, 21 Sep 2023 17:29:31 -0700
Subject: [PATCH 010/203] Now we should be forcing transient lookup which wont'
 work

---
 src/coreclr/vm/method.cpp | 3 +++
 src/coreclr/vm/method.hpp | 4 ++--
 2 files changed, 5 insertions(+), 2 deletions(-)

diff --git a/src/coreclr/vm/method.cpp b/src/coreclr/vm/method.cpp
index bf1ad0ed150b1a..f1f0d70368d8ad 100644
--- a/src/coreclr/vm/method.cpp
+++ b/src/coreclr/vm/method.cpp
@@ -1090,6 +1090,9 @@ COR_ILMETHOD* MethodDesc::GetILHeader(BOOL fAllowOverrides /*=FALSE*/)
     }
     CONTRACTL_END
 
+    if (IsRuntimeSupplied())
+        return NULL;
+
     Module *pModule = GetModule();
 
     // Always pickup 'permanent' overrides like reflection emit, EnC, etc.
diff --git a/src/coreclr/vm/method.hpp b/src/coreclr/vm/method.hpp
index f4ae0b4342b028..09f8836f3ef080 100644
--- a/src/coreclr/vm/method.hpp
+++ b/src/coreclr/vm/method.hpp
@@ -611,7 +611,7 @@ class MethodDesc
     {
         LIMITED_METHOD_DAC_CONTRACT;
         return mcFCall == GetClassification()
-            || mcArray == GetClassification();
+            || mcArray == GetClassification() && !IsAsyncThunkMethod();
     }
 
     inline DWORD IsArray() const
@@ -804,7 +804,7 @@ class MethodDesc
             MODE_ANY;
         }
         CONTRACTL_END;
-        return IsIL() && !IsUnboxingStub() && GetRVA();
+        return IsIL() && !IsUnboxingStub() && GetRVA() && !IsRuntimeSupplied();
     }
 
     COR_ILMETHOD* GetILHeader(BOOL fAllowOverrides = FALSE);

From 84f7148e30b27f1d05106602a890c2ff8d5cc46f Mon Sep 17 00:00:00 2001
From: David Wrighton <davidwr@microsoft.com>
Date: Fri, 22 Sep 2023 10:06:05 -0700
Subject: [PATCH 011/203] Fix method classification for async2 methods

---
 src/coreclr/vm/methodtablebuilder.cpp | 17 ++++++++++++-----
 1 file changed, 12 insertions(+), 5 deletions(-)

diff --git a/src/coreclr/vm/methodtablebuilder.cpp b/src/coreclr/vm/methodtablebuilder.cpp
index 6e67ca5ba377ec..59f4c00971976e 100644
--- a/src/coreclr/vm/methodtablebuilder.cpp
+++ b/src/coreclr/vm/methodtablebuilder.cpp
@@ -2708,7 +2708,7 @@ void GetNameOfTypeDefOrRef(Module* pModule, mdToken tk, LPCSTR* pName, LPCSTR* p
     }
     else if (TypeFromToken(tk) == mdtTypeRef)
     {
-        IfFailThrow(pModule->GetMDImport()->GetNameOfTypeRef(tk, pName, pNamespace));
+        IfFailThrow(pModule->GetMDImport()->GetNameOfTypeRef(tk, pNamespace, pName));
     }
 }
 
@@ -2776,16 +2776,21 @@ AsyncTaskMethod ClassifyAsyncMethod(SigPointer sig, Module* pModule, ULONG* offs
     // Then this is a async2 function
     CorElementType elemType;
     *offsetOfAsyncDetails = (ULONG)(sig.GetPtr() - initialSig);
-    IfFailThrow(sig.GetElemType(&elemType));
+    BYTE elemTypeByte;
+    IfFailThrow(sig.GetByte(&elemTypeByte)); // Don't use GetElemType as it skips custom modifiers
+    elemType = (CorElementType)elemTypeByte;
+
     LPCSTR name, _namespace;
     mdToken tk;
     while ((elemType == ELEMENT_TYPE_CMOD_OPT) || (elemType == ELEMENT_TYPE_CMOD_REQD))
     {
+        CorElementType elemTypeCmod = elemType;
+
         IfFailThrow(sig.GetToken(&tk));
         GetNameOfTypeDefOrRef(pModule, tk, &name, &_namespace);
-        *offsetOfAsyncDetails = (ULONG)(sig.GetPtr() - initialSig);
-        IfFailThrow(sig.GetElemType(&elemType));
-        if (strcmp(name, "Task`1") == 0 && strcmp(_namespace, "System.Threading.Tasks") == 0 && IsTypeDefOrRefImplementedInSystemModule(pModule, tk))
+        IfFailThrow(sig.GetByte(&elemTypeByte)); // Don't use GetElemType as it skips custom modifiers
+        elemType = (CorElementType)elemTypeByte;
+        if (strcmp(name, "Task`1") == 0 && strcmp(_namespace, "System.Threading.Tasks") == 0 && IsTypeDefOrRefImplementedInSystemModule(pModule, tk) && (elemTypeCmod == ELEMENT_TYPE_CMOD_OPT))
         {
             // This must have been the last CMOD before the element type, and the element type MUST be one which can be expressed structurally as a generic parameter
             switch (elemType)
@@ -2837,6 +2842,8 @@ AsyncTaskMethod ClassifyAsyncMethod(SigPointer sig, Module* pModule, ULONG* offs
                     ThrowHR(COR_E_BADIMAGEFORMAT);
             }
         }
+
+        *offsetOfAsyncDetails = (ULONG)(sig.GetPtr() - initialSig) - 1;
     }
 
     if (elemType == ELEMENT_TYPE_GENERICINST)

From 51e20116fbcbc37dc2a149b6d9a289cef67e0949 Mon Sep 17 00:00:00 2001
From: David Wrighton <davidwr@microsoft.com>
Date: Fri, 22 Sep 2023 10:49:43 -0700
Subject: [PATCH 012/203] We are now triggering the transient IL compilation
 for the thunks

---
 src/coreclr/vm/method.hpp | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/src/coreclr/vm/method.hpp b/src/coreclr/vm/method.hpp
index 09f8836f3ef080..5fe3b0c4181caf 100644
--- a/src/coreclr/vm/method.hpp
+++ b/src/coreclr/vm/method.hpp
@@ -611,7 +611,8 @@ class MethodDesc
     {
         LIMITED_METHOD_DAC_CONTRACT;
         return mcFCall == GetClassification()
-            || mcArray == GetClassification() && !IsAsyncThunkMethod();
+            || mcArray == GetClassification()
+            || IsAsyncThunkMethod();
     }
 
     inline DWORD IsArray() const

From 6fc166a00b2e9a4cdcf06892e2aa8d842a7988b2 Mon Sep 17 00:00:00 2001
From: David Wrighton <davidwr@microsoft.com>
Date: Fri, 22 Sep 2023 16:13:32 -0700
Subject: [PATCH 013/203] Calling Sample Works!  Now to implement an actual
 async implementation and see if suspension works

NOTE: No methods with generic parameters will work with the current implementation, generics may require a bunch of infrastructure work, separate from the async work
---
 src/coreclr/vm/corelib.h                      |   8 +
 src/coreclr/vm/jitinterface.cpp               |   2 +-
 src/coreclr/vm/metasig.h                      |   3 +
 src/coreclr/vm/method.cpp                     |   6 +-
 src/coreclr/vm/method.hpp                     |  14 +-
 src/coreclr/vm/methodtablebuilder.cpp         |   9 -
 src/coreclr/vm/namespace.h                    |   1 +
 src/coreclr/vm/prestub.cpp                    | 179 ++++++++++++++++++
 src/coreclr/vm/stubgen.cpp                    |  19 +-
 .../src/System/Threading/Tasks/Future.cs      |  11 ++
 10 files changed, 238 insertions(+), 14 deletions(-)

diff --git a/src/coreclr/vm/corelib.h b/src/coreclr/vm/corelib.h
index 0dd79fb124f820..ba23da832bd834 100644
--- a/src/coreclr/vm/corelib.h
+++ b/src/coreclr/vm/corelib.h
@@ -350,6 +350,14 @@ DEFINE_METHOD(TYPE_INIT_EXCEPTION,  STR_EX_CTOR,            .ctor,
 DEFINE_CLASS(THREAD_START_EXCEPTION,Threading,              ThreadStartException)
 DEFINE_METHOD(THREAD_START_EXCEPTION,EX_CTOR,               .ctor,                      IM_Exception_RetVoid)
 
+DEFINE_CLASS(TASK_1, Tasks, Task`1)
+
+DEFINE_CLASS(RUNTIME_TASK_STATE_1, Tasks, RuntimeTaskState`1)
+DEFINE_METHOD(RUNTIME_TASK_STATE_1, PUSH, Push, IM_RetVoid)
+DEFINE_METHOD(RUNTIME_TASK_STATE_1, POP, Pop, IM_RetVoid)
+DEFINE_METHOD(RUNTIME_TASK_STATE_1, FROM_EXCEPTION, FromException, IM_Exception_RetTaskOfT)
+DEFINE_METHOD(RUNTIME_TASK_STATE_1, FROM_RESULT, FromResult, IM_T_RetTaskOfT)
+
 DEFINE_CLASS(TYPE_HANDLE,           System,                 RuntimeTypeHandle)
 DEFINE_CLASS(RT_TYPE_HANDLE,        System,                 RuntimeTypeHandle)
 DEFINE_METHOD(RT_TYPE_HANDLE,       PVOID_CTOR,             .ctor,                      IM_RuntimeType_RetVoid)
diff --git a/src/coreclr/vm/jitinterface.cpp b/src/coreclr/vm/jitinterface.cpp
index 57eb2a0f04c296..d577d15c37c36a 100644
--- a/src/coreclr/vm/jitinterface.cpp
+++ b/src/coreclr/vm/jitinterface.cpp
@@ -7665,7 +7665,7 @@ static void getMethodInfoHelper(
     }
     else
     {
-        if (!ftn->TryGenerateUnsafeAccessor(&cxt.TransientResolver, &cxt.Header))
+        if (!ftn->TryGenerateTransientILImplementation(&cxt.TransientResolver, &cxt.Header))
             ThrowHR(COR_E_BADIMAGEFORMAT);
 
         scopeHnd = cxt.CreateScopeHandle();
diff --git a/src/coreclr/vm/metasig.h b/src/coreclr/vm/metasig.h
index dc2173f0440211..dff10ce40b8d49 100644
--- a/src/coreclr/vm/metasig.h
+++ b/src/coreclr/vm/metasig.h
@@ -574,6 +574,9 @@ DEFINE_METASIG_T(IM(Dec_RetVoid, g(DECIMAL), v))
 DEFINE_METASIG_T(IM(Currency_RetVoid, g(CURRENCY), v))
 DEFINE_METASIG_T(SM(RefDec_RetVoid, r(g(DECIMAL)), v))
 
+DEFINE_METASIG_T(IM(Exception_RetTaskOfT, C(EXCEPTION), GI(C(TASK_1), 1, G(0))))
+DEFINE_METASIG_T(IM(T_RetTaskOfT, G(0), GI(C(TASK_1), 1, G(0))))
+
 DEFINE_METASIG(GM(RefT_T_T_RetT, IMAGE_CEE_CS_CALLCONV_DEFAULT, 1, r(M(0)) M(0) M(0), M(0)))
 DEFINE_METASIG(SM(RefObject_Object_Object_RetObject, r(j) j j, j))
 
diff --git a/src/coreclr/vm/method.cpp b/src/coreclr/vm/method.cpp
index f1f0d70368d8ad..f8d5bf993a1f02 100644
--- a/src/coreclr/vm/method.cpp
+++ b/src/coreclr/vm/method.cpp
@@ -3066,7 +3066,11 @@ bool MethodDesc::DetermineAndSetIsEligibleForTieredCompilation()
         !IsWrapperStub() &&
 
         // Functions with NoOptimization or AggressiveOptimization don't participate in tiering
-        !IsJitOptimizationLevelRequested())
+        !IsJitOptimizationLevelRequested() &&
+
+        // Tiering the async thunk methods doesn't make sense
+        !IsAsyncThunkMethod()
+        )
     {
         m_wFlags3AndTokenRemainder |= enum_flag3_IsEligibleForTieredCompilation;
         _ASSERTE(IsVersionable());
diff --git a/src/coreclr/vm/method.hpp b/src/coreclr/vm/method.hpp
index 5fe3b0c4181caf..7b0274e6057049 100644
--- a/src/coreclr/vm/method.hpp
+++ b/src/coreclr/vm/method.hpp
@@ -1541,6 +1541,7 @@ class MethodDesc
 
     MethodDesc* GetAsyncOtherVariant()
     {
+        // TODO This should be FindOrCreateAssociatedMethodDesc with some set of params
         return GetMethodTable()->GetParallelMethodDesc(this, AsyncVariantLookup::AsyncOtherVariant);
     }
 
@@ -1866,8 +1867,10 @@ class MethodDesc
     PCODE JitCompileCodeLockedEventWrapper(PrepareCodeConfig* pConfig, JitListLockEntry* pEntry);
     PCODE JitCompileCodeLocked(PrepareCodeConfig* pConfig, COR_ILMETHOD_DECODER* pilHeader, JitListLockEntry* pLockEntry, ULONG* pSizeOfCode);
 
-public:
+    bool TryGenerateAsyncThunk(DynamicResolver** resolver, COR_ILMETHOD_DECODER** methodILDecoder);
     bool TryGenerateUnsafeAccessor(DynamicResolver** resolver, COR_ILMETHOD_DECODER** methodILDecoder);
+public:
+    bool TryGenerateTransientILImplementation(DynamicResolver** resolver, COR_ILMETHOD_DECODER** methodILDecoder);
 #endif // DACCESS_COMPILE
 
 #ifdef HAVE_GCCOVER
@@ -3641,6 +3644,15 @@ ReadyToRunStandaloneMethodMetadata* GetReadyToRunStandaloneMethodMetadata(Method
 void InitReadyToRunStandaloneMethodMetadata();
 #endif // FEATURE_READYTORUN
 
+enum class AsyncTaskMethod
+{
+    TaskReturningMethod,
+    Async2Method,
+    Async2MethodThatCannotBeImplementedByTask,
+    NormalMethod
+};
+AsyncTaskMethod ClassifyAsyncMethod(SigPointer sig, Module* pModule, ULONG* offsetOfAsyncDetails);
+
 #include "method.inl"
 
 #endif // !_METHOD_H
diff --git a/src/coreclr/vm/methodtablebuilder.cpp b/src/coreclr/vm/methodtablebuilder.cpp
index 59f4c00971976e..c29c2f2554daa4 100644
--- a/src/coreclr/vm/methodtablebuilder.cpp
+++ b/src/coreclr/vm/methodtablebuilder.cpp
@@ -2690,14 +2690,6 @@ HRESULT MethodTableBuilder::FindMethodDeclarationForMethodImpl(
 #pragma warning(disable:21000) // Suppress PREFast warning about overly large function
 #endif // _PREFAST_
 
-enum class AsyncTaskMethod
-{
-    TaskReturningMethod,
-    Async2Method,
-    Async2MethodThatCannotBeImplementedByTask,
-    NormalMethod
-};
-
 void GetNameOfTypeDefOrRef(Module* pModule, mdToken tk, LPCSTR* pName, LPCSTR* pNamespace)
 {
     *pName = "";
@@ -7528,7 +7520,6 @@ MethodTableBuilder::NeedsNativeCodeSlot(bmtMDMethod * pMDMethod)
 {
     LIMITED_METHOD_CONTRACT;
 
-
 #ifdef FEATURE_TIERED_COMPILATION
     // Keep in-sync with MethodDesc::DetermineAndSetIsEligibleForTieredCompilation()
     if ((g_pConfig->TieredCompilation() &&
diff --git a/src/coreclr/vm/namespace.h b/src/coreclr/vm/namespace.h
index 8b51f9ca623915..da1e97180d8769 100644
--- a/src/coreclr/vm/namespace.h
+++ b/src/coreclr/vm/namespace.h
@@ -16,6 +16,7 @@
 #define g_RuntimeNS         g_SystemNS ".Runtime"
 #define g_IONS              g_SystemNS ".IO"
 #define g_ThreadingNS       g_SystemNS ".Threading"
+#define g_TasksNS           g_ThreadingNS ".Tasks"
 #define g_CollectionsNS     g_SystemNS ".Collections"
 #define g_ResourcesNS       g_SystemNS ".Resources"
 #define g_DiagnosticsNS     g_SystemNS ".Diagnostics"
diff --git a/src/coreclr/vm/prestub.cpp b/src/coreclr/vm/prestub.cpp
index 90768a47b51578..c3ae6666c1622d 100644
--- a/src/coreclr/vm/prestub.cpp
+++ b/src/coreclr/vm/prestub.cpp
@@ -1428,6 +1428,185 @@ namespace
     }
 }
 
+bool MethodDesc::TryGenerateTransientILImplementation(DynamicResolver** resolver, COR_ILMETHOD_DECODER** methodILDecoder)
+{
+    if (TryGenerateAsyncThunk(resolver, methodILDecoder))
+    {
+        return true;
+    }
+
+    if (TryGenerateUnsafeAccessor(resolver, methodILDecoder))
+    {
+        return true;
+    }
+
+    return false;
+}
+
+bool MethodDesc::TryGenerateAsyncThunk(DynamicResolver** resolver, COR_ILMETHOD_DECODER** methodILDecoder)
+{
+    STANDARD_VM_CONTRACT;
+    _ASSERTE(resolver != NULL);
+    _ASSERTE(methodILDecoder != NULL);
+    _ASSERTE(*resolver == NULL && *methodILDecoder == NULL);
+    _ASSERTE(IsIL());
+    _ASSERTE(GetRVA() == 0);
+
+    if (!IsAsyncThunkMethod())
+    {
+        return false;
+    }
+
+    // Create a MetaSig for the given method's sig. (Easier than
+    // picking the sig apart ourselves.)
+    PCCOR_SIGNATURE pCallSig;
+    DWORD cbCallSigSize;
+
+    GetSig(&pCallSig, &cbCallSigSize);
+
+    if (pCallSig == NULL)
+    {
+        _ASSERTE(FALSE);
+        return false;
+    }
+
+    ULONG offsetOfAsyncDetailsUnused;
+    AsyncTaskMethod asyncType = ClassifyAsyncMethod(GetSigPointer(), GetModule(), &offsetOfAsyncDetailsUnused);
+    if (asyncType != AsyncTaskMethod::TaskReturningMethod)
+    {
+        // Async to task helpers not yet implemented
+        _ASSERTE(FALSE);
+        return false;
+    }
+
+    MethodDesc *pAsyncOtherVariant = this->GetAsyncOtherVariant();
+
+    MetaSig msig(pCallSig, cbCallSigSize, this->GetModule(), NULL, MetaSig::sigMember);
+
+    TypeHandle thTaskRet = msig.GetRetTypeHandleThrowing();
+    TypeHandle thLogicalRetType = thTaskRet.GetMethodTable()->GetInstantiation()[0];
+    MethodTable* pMTRuntimeTaskStateOpen = CoreLibBinder::GetClass(CLASS__RUNTIME_TASK_STATE_1);
+
+    MethodTable* pMTRuntimeTaskState = ClassLoader::LoadGenericInstantiationThrowing(pMTRuntimeTaskStateOpen->GetModule(), pMTRuntimeTaskStateOpen->GetCl(), Instantiation(&thLogicalRetType, 1)).GetMethodTable();
+    NewHolder<ILStubResolver> ilResolver = new ILStubResolver();
+
+        // Initialize the resolver target details.
+    ilResolver->SetStubMethodDesc(this);
+    ilResolver->SetStubTargetMethodDesc(pAsyncOtherVariant);
+
+    // [TODO] Handle generics
+    SigTypeContext emptyContext;
+    ILStubLinker sl(
+        GetModule(),
+        GetSignature(),
+        &emptyContext,
+        pAsyncOtherVariant,
+        (ILStubLinkerFlags)ILSTUB_LINKER_FLAG_NONE);
+
+    ILCodeStream* pCode = sl.NewCodeStream(ILStubLinker::kDispatch);
+    LocalDesc rtsLocalDesc(pMTRuntimeTaskState);
+    DWORD rtsLocal = pCode->NewLocal(rtsLocalDesc);
+
+    LocalDesc resultLocalDesc(thLogicalRetType);
+    DWORD resultLocal = pCode->NewLocal(resultLocalDesc);
+
+    LocalDesc exceptionLocalDesc(CoreLibBinder::GetClass(CLASS__EXCEPTION));
+    DWORD exceptionLocal = pCode->NewLocal(exceptionLocalDesc);
+
+    LocalDesc returnLocalDesc(thTaskRet);
+    DWORD returnLocal = pCode->NewLocal(returnLocalDesc);
+
+    auto pNoExceptionLabel = pCode->NewCodeLabel();
+    auto pReturnResultLabel = pCode->NewCodeLabel();
+
+    pCode->EmitLDLOCA(rtsLocal);
+    pCode->EmitINITOBJ(pCode->GetToken(pMTRuntimeTaskState));
+    pCode->EmitLDLOCA(resultLocal);
+    pCode->EmitINITOBJ(pCode->GetToken(thLogicalRetType));
+    pCode->EmitLDLOCA(exceptionLocal);
+    pCode->EmitINITOBJ(pCode->GetToken(CoreLibBinder::GetClass(CLASS__EXCEPTION)));
+    pCode->EmitLDLOCA(returnLocal);
+    pCode->EmitINITOBJ(pCode->GetToken(thTaskRet));
+    pCode->EmitLDLOCA(rtsLocal);
+    pCode->EmitCALL(pCode->GetToken(pMTRuntimeTaskState->GetParallelMethodDesc(CoreLibBinder::GetMethod(METHOD__RUNTIME_TASK_STATE_1__PUSH))), 1, 0);
+    {
+        pCode->BeginTryBlock();
+        pCode->EmitNOP("Separate try blocks");
+        {
+            pCode->BeginTryBlock();
+
+            DWORD localArg = 0;
+            if (msig.HasThis())
+            {
+                // Struct async thunks not yet implemented
+                _ASSERTE(!this->GetMethodTable()->IsValueType());
+                pCode->EmitLDARG(localArg++);
+            }
+            for (UINT iArg = 0; iArg < msig.NumFixedArgs(); iArg++)
+            {
+                pCode->EmitLDARG(localArg++);
+            }
+            pCode->EmitCALL(pCode->GetToken(pAsyncOtherVariant), localArg, 1);
+            pCode->EmitSTLOC(resultLocal);
+            pCode->EmitLEAVE(pNoExceptionLabel);
+            pCode->EndTryBlock();
+        }
+        // Catch
+        {
+            pCode->BeginCatchBlock(pCode->GetToken(CoreLibBinder::GetClass(CLASS__EXCEPTION)));
+            pCode->EmitSTLOC(exceptionLocal);
+            pCode->EmitLDLOCA(rtsLocal);
+            pCode->EmitLDLOC(exceptionLocal);
+            pCode->EmitCALL(pCode->GetToken(pMTRuntimeTaskState->GetParallelMethodDesc(CoreLibBinder::GetMethod(METHOD__RUNTIME_TASK_STATE_1__FROM_EXCEPTION))), 2, 1);
+            pCode->EmitSTLOC(returnLocal);
+            pCode->EmitLEAVE(pReturnResultLabel);
+            pCode->EndCatchBlock();
+        }
+        pCode->EmitLabel(pNoExceptionLabel);
+        pCode->EmitLDLOCA(rtsLocal);
+        pCode->EmitLDLOC(resultLocal);
+        pCode->EmitCALL(pCode->GetToken(pMTRuntimeTaskState->GetParallelMethodDesc(CoreLibBinder::GetMethod(METHOD__RUNTIME_TASK_STATE_1__FROM_RESULT))), 2, 1);
+        pCode->EmitSTLOC(returnLocal);
+        pCode->EmitLEAVE(pReturnResultLabel);
+        pCode->EndTryBlock();
+    }
+    // Finally
+    {
+        pCode->BeginFinallyBlock();
+        pCode->EmitLDLOCA(rtsLocal);
+        pCode->EmitCALL(pCode->GetToken(pMTRuntimeTaskState->GetParallelMethodDesc(CoreLibBinder::GetMethod(METHOD__RUNTIME_TASK_STATE_1__POP))), 1, 0);
+        pCode->EmitENDFINALLY();
+        pCode->EndFinallyBlock();
+    }
+    pCode->EmitLabel(pReturnResultLabel);
+    pCode->EmitLDLOC(returnLocal);
+    pCode->EmitRET();
+
+    // Generate all IL associated data for JIT
+    {
+        UINT maxStack;
+        size_t cbCode = sl.Link(&maxStack);
+        DWORD cbSig = sl.GetLocalSigSize();
+
+        COR_ILMETHOD_DECODER* pILHeader = ilResolver->AllocGeneratedIL(cbCode, cbSig, maxStack);
+        BYTE* pbBuffer = (BYTE*)pILHeader->Code;
+        BYTE* pbLocalSig = (BYTE*)pILHeader->LocalVarSig;
+        _ASSERTE(cbSig == pILHeader->cbLocalVarSig);
+        sl.GenerateCode(pbBuffer, cbCode);
+        sl.GetLocalSig(pbLocalSig, cbSig);
+
+        // Store the token lookup map
+        ilResolver->SetTokenLookupMap(sl.GetTokenLookupMap());
+        ilResolver->SetJitFlags(CORJIT_FLAGS(CORJIT_FLAGS::CORJIT_FLAG_IL_STUB));
+
+        *resolver = (DynamicResolver*)ilResolver;
+        *methodILDecoder = pILHeader;
+    }
+
+    ilResolver.SuppressRelease();
+    return true;
+}
+
 bool MethodDesc::TryGenerateUnsafeAccessor(DynamicResolver** resolver, COR_ILMETHOD_DECODER** methodILDecoder)
 {
     STANDARD_VM_CONTRACT;
diff --git a/src/coreclr/vm/stubgen.cpp b/src/coreclr/vm/stubgen.cpp
index 78e7fb621c90d5..7c8be127608431 100644
--- a/src/coreclr/vm/stubgen.cpp
+++ b/src/coreclr/vm/stubgen.cpp
@@ -173,6 +173,21 @@ void ILCodeStream::Emit(ILInstrEnum instr, INT16 iStackDelta, UINT_PTR uArg)
     pInstrBuffer[idxCurInstr].uInstruction = static_cast<UINT16>(instr);
     pInstrBuffer[idxCurInstr].iStackDelta = iStackDelta;
     pInstrBuffer[idxCurInstr].uArg = uArg;
+
+    if(m_buildingEHClauses.GetCount() > 0)
+    {
+        ILStubEHClauseBuilder& clause = m_buildingEHClauses[m_buildingEHClauses.GetCount() - 1];
+
+        if (clause.tryBeginLabel != NULL && clause.tryEndLabel != NULL &&
+             clause.handlerBeginLabel != NULL && clause.kind == ILStubEHClause::kTypedCatch)
+        {
+            if (clause.handlerBeginLabel->m_idxLabeledInstruction == idxCurInstr)
+            {
+                // Catch clauses start with an exception on the stack
+                pInstrBuffer[idxCurInstr].iStackDelta++;
+            }
+        }
+    }
 }
 
 ILCodeLabel* ILStubLinker::NewCodeLabel()
@@ -819,10 +834,10 @@ size_t ILStubLinker::Link(UINT* puMaxStack)
     }
 
 #ifdef _DEBUG
-    if (fStackUnderflow)
+    if (fStackUnderflow || this->GetNumEHClauses() != 0)
     {
         LogILStub(CORJIT_FLAGS());
-        CONSISTENCY_CHECK_MSG(false, "IL stack underflow! -- see logging output");
+//        CONSISTENCY_CHECK_MSG(false, "IL stack underflow! -- see logging output");
     }
 #endif // _DEBUG
 
diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/Future.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/Future.cs
index 827d6915dd3252..19f359a333383a 100644
--- a/src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/Future.cs
+++ b/src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/Future.cs
@@ -15,6 +15,17 @@
 
 namespace System.Threading.Tasks
 {
+#pragma warning disable CA1822
+    // Used to implement Runtime implemented Task suspension handling
+    internal struct RuntimeTaskState<TResult>
+    {
+        public void Push() {}
+        public void Pop() {}
+        public Task<TResult> FromResult(TResult result) { return Task.FromResult(result); }
+        public Task<TResult> FromException(Exception e) { return Task.FromException<TResult>(e); }
+    }
+#pragma warning restore CA1822
+
     /// <summary>
     /// Represents an asynchronous operation that produces a result at some time in the future.
     /// </summary>

From 0b10933e1a2eebeb453f05ec4e6e52b912bc09e4 Mon Sep 17 00:00:00 2001
From: David Wrighton <davidwr@microsoft.com>
Date: Mon, 25 Sep 2023 14:56:10 -0700
Subject: [PATCH 014/203] WithYield runtime task test

---
 .../runtimetask-asyncfibonacci-with-yields.il | 535 ++++++++++++++++++
 ...timetask-asyncfibonacci-with-yields.ilproj |   8 +
 2 files changed, 543 insertions(+)
 create mode 100644 src/tests/Loader/async/runtimetask-asyncfibonacci-with-yields.il
 create mode 100644 src/tests/Loader/async/runtimetask-asyncfibonacci-with-yields.ilproj

diff --git a/src/tests/Loader/async/runtimetask-asyncfibonacci-with-yields.il b/src/tests/Loader/async/runtimetask-asyncfibonacci-with-yields.il
new file mode 100644
index 00000000000000..2d8008483407e1
--- /dev/null
+++ b/src/tests/Loader/async/runtimetask-asyncfibonacci-with-yields.il
@@ -0,0 +1,535 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+
+//  .NET IL Disassembler.  Version 9.0.0-dev
+
+
+
+// Metadata version: v4.0.30319
+.assembly extern System.Runtime
+{
+  .publickeytoken = (B0 3F 5F 7F 11 D5 0A 3A )                         // .?_....:
+  .ver 9:0:0:0
+}
+.assembly extern System.Console
+{
+  .publickeytoken = (B0 3F 5F 7F 11 D5 0A 3A )                         // .?_....:
+  .ver 9:0:0:0
+}
+.assembly 'runtimetask-asyncfibonacci-with-yields'
+{
+  .custom instance void [System.Runtime]System.Runtime.CompilerServices.CompilationRelaxationsAttribute::.ctor(int32) = ( 01 00 08 00 00 00 00 00 ) 
+  .custom instance void [System.Runtime]System.Runtime.CompilerServices.RuntimeCompatibilityAttribute::.ctor() = ( 01 00 01 00 54 02 16 57 72 61 70 4E 6F 6E 45 78   // ....T..WrapNonEx
+                                                                                                                   63 65 70 74 69 6F 6E 54 68 72 6F 77 73 01 )       // ceptionThrows.
+
+  // --- The following custom attribute is added automatically, do not uncomment -------
+  //  .custom instance void [System.Runtime]System.Diagnostics.DebuggableAttribute::.ctor(valuetype [System.Runtime]System.Diagnostics.DebuggableAttribute/DebuggingModes) = ( 01 00 07 01 00 00 00 00 ) 
+
+  .hash algorithm 0x00008004
+  .ver 0:0:0:0
+}
+.module 'runtimetask-asyncfibonacci-with-yields.dll'
+// MVID: {ca42840a-c801-47a9-8471-ea986ae2c60d}
+.custom instance void [System.Runtime]System.Runtime.CompilerServices.RefSafetyRulesAttribute::.ctor(int32) = ( 01 00 0B 00 00 00 00 00 ) 
+.imagebase 0x00400000
+.file alignment 0x00000200
+.stackreserve 0x00100000
+.subsystem 0x0003       // WINDOWS_CUI
+.corflags 0x00000001    //  ILONLY
+// Image base: 0x000001E00BDE0000
+
+
+// =============== CLASS MEMBERS DECLARATION ===================
+
+.class private auto ansi beforefieldinit Program
+       extends [System.Runtime]System.Object
+{
+  .class auto ansi sealed nested private beforefieldinit '<<Main>$>d__0'
+         extends [System.Runtime]System.Object
+         implements [System.Runtime]System.Runtime.CompilerServices.IAsyncStateMachine
+  {
+    .custom instance void [System.Runtime]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) 
+    .field public int32 '<>1__state'
+    .field public valuetype [System.Runtime]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<int32> '<>t__builder'
+    .field public string[] args
+    .field private int32 '<i>5__1'
+    .field private class [System.Runtime]System.Diagnostics.Stopwatch '<sw>5__2'
+    .field private uint32 '<result>5__3'
+    .field private uint32 '<>s__4'
+    .field private valuetype [System.Runtime]System.Runtime.CompilerServices.TaskAwaiter`1<uint32> '<>u__1'
+    .method public hidebysig specialname rtspecialname 
+            instance void  .ctor() cil managed
+    {
+      // Code size       8 (0x8)
+      .maxstack  8
+      IL_0000:  ldarg.0
+      IL_0001:  call       instance void [System.Runtime]System.Object::.ctor()
+      IL_0006:  nop
+      IL_0007:  ret
+    } // end of method '<<Main>$>d__0'::.ctor
+
+    .method private hidebysig newslot virtual final 
+            instance void  MoveNext() cil managed
+    {
+      .override [System.Runtime]System.Runtime.CompilerServices.IAsyncStateMachine::MoveNext
+      // Code size       334 (0x14e)
+      .maxstack  3
+      .locals init (int32 V_0,
+               int32 V_1,
+               valuetype [System.Runtime]System.Runtime.CompilerServices.TaskAwaiter`1<uint32> V_2,
+               class Program/'<<Main>$>d__0' V_3,
+               valuetype [System.Runtime]System.Runtime.CompilerServices.DefaultInterpolatedStringHandler V_4,
+               int32 V_5,
+               bool V_6,
+               class [System.Runtime]System.Exception V_7)
+      IL_0000:  ldarg.0
+      IL_0001:  ldfld      int32 Program/'<<Main>$>d__0'::'<>1__state'
+      IL_0006:  stloc.0
+      .try
+      {
+        IL_0007:  ldloc.0
+        IL_0008:  brfalse.s  IL_000c
+
+        IL_000a:  br.s       IL_000e
+
+        IL_000c:  br.s       IL_0072
+
+        IL_000e:  ldarg.0
+        IL_000f:  ldc.i4.0
+        IL_0010:  stfld      int32 Program/'<<Main>$>d__0'::'<i>5__1'
+        IL_0015:  br         IL_0106
+
+        IL_001a:  nop
+        IL_001b:  ldarg.0
+        IL_001c:  newobj     instance void [System.Runtime]System.Diagnostics.Stopwatch::.ctor()
+        IL_0021:  stfld      class [System.Runtime]System.Diagnostics.Stopwatch Program/'<<Main>$>d__0'::'<sw>5__2'
+        IL_0026:  ldarg.0
+        IL_0027:  ldfld      class [System.Runtime]System.Diagnostics.Stopwatch Program/'<<Main>$>d__0'::'<sw>5__2'
+        IL_002c:  callvirt   instance void [System.Runtime]System.Diagnostics.Stopwatch::Start()
+        IL_0031:  nop
+        IL_0032:  ldc.i4     0x5f5e100
+        IL_0037:  call       class [System.Runtime]System.Threading.Tasks.Task`1<uint32> Program::'<<Main>$>g__A|0_0'(uint32)
+        IL_003c:  callvirt   instance valuetype [System.Runtime]System.Runtime.CompilerServices.TaskAwaiter`1<!0> class [System.Runtime]System.Threading.Tasks.Task`1<uint32>::GetAwaiter()
+        IL_0041:  stloc.2
+        IL_0042:  ldloca.s   V_2
+        IL_0044:  call       instance bool valuetype [System.Runtime]System.Runtime.CompilerServices.TaskAwaiter`1<uint32>::get_IsCompleted()
+        IL_0049:  brtrue.s   IL_008e
+
+        IL_004b:  ldarg.0
+        IL_004c:  ldc.i4.0
+        IL_004d:  dup
+        IL_004e:  stloc.0
+        IL_004f:  stfld      int32 Program/'<<Main>$>d__0'::'<>1__state'
+        IL_0054:  ldarg.0
+        IL_0055:  ldloc.2
+        IL_0056:  stfld      valuetype [System.Runtime]System.Runtime.CompilerServices.TaskAwaiter`1<uint32> Program/'<<Main>$>d__0'::'<>u__1'
+        IL_005b:  ldarg.0
+        IL_005c:  stloc.3
+        IL_005d:  ldarg.0
+        IL_005e:  ldflda     valuetype [System.Runtime]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<int32> Program/'<<Main>$>d__0'::'<>t__builder'
+        IL_0063:  ldloca.s   V_2
+        IL_0065:  ldloca.s   V_3
+        IL_0067:  call       instance void valuetype [System.Runtime]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<int32>::AwaitUnsafeOnCompleted<valuetype [System.Runtime]System.Runtime.CompilerServices.TaskAwaiter`1<uint32>,class Program/'<<Main>$>d__0'>(!!0&,
+                                                                                                                                                                                                                                                                            !!1&)
+        IL_006c:  nop
+        IL_006d:  leave      IL_014d
+
+        IL_0072:  ldarg.0
+        IL_0073:  ldfld      valuetype [System.Runtime]System.Runtime.CompilerServices.TaskAwaiter`1<uint32> Program/'<<Main>$>d__0'::'<>u__1'
+        IL_0078:  stloc.2
+        IL_0079:  ldarg.0
+        IL_007a:  ldflda     valuetype [System.Runtime]System.Runtime.CompilerServices.TaskAwaiter`1<uint32> Program/'<<Main>$>d__0'::'<>u__1'
+        IL_007f:  initobj    valuetype [System.Runtime]System.Runtime.CompilerServices.TaskAwaiter`1<uint32>
+        IL_0085:  ldarg.0
+        IL_0086:  ldc.i4.m1
+        IL_0087:  dup
+        IL_0088:  stloc.0
+        IL_0089:  stfld      int32 Program/'<<Main>$>d__0'::'<>1__state'
+        IL_008e:  ldarg.0
+        IL_008f:  ldloca.s   V_2
+        IL_0091:  call       instance !0 valuetype [System.Runtime]System.Runtime.CompilerServices.TaskAwaiter`1<uint32>::GetResult()
+        IL_0096:  stfld      uint32 Program/'<<Main>$>d__0'::'<>s__4'
+        IL_009b:  ldarg.0
+        IL_009c:  ldarg.0
+        IL_009d:  ldfld      uint32 Program/'<<Main>$>d__0'::'<>s__4'
+        IL_00a2:  stfld      uint32 Program/'<<Main>$>d__0'::'<result>5__3'
+        IL_00a7:  ldc.i4.s   11
+        IL_00a9:  ldc.i4.2
+        IL_00aa:  newobj     instance void [System.Runtime]System.Runtime.CompilerServices.DefaultInterpolatedStringHandler::.ctor(int32,
+                                                                                                                                   int32)
+        IL_00af:  stloc.s    V_4
+        IL_00b1:  ldloca.s   V_4
+        IL_00b3:  ldarg.0
+        IL_00b4:  ldfld      class [System.Runtime]System.Diagnostics.Stopwatch Program/'<<Main>$>d__0'::'<sw>5__2'
+        IL_00b9:  callvirt   instance int64 [System.Runtime]System.Diagnostics.Stopwatch::get_ElapsedMilliseconds()
+        IL_00be:  call       instance void [System.Runtime]System.Runtime.CompilerServices.DefaultInterpolatedStringHandler::AppendFormatted<int64>(!!0)
+        IL_00c3:  nop
+        IL_00c4:  ldloca.s   V_4
+        IL_00c6:  ldstr      " ms result="
+        IL_00cb:  call       instance void [System.Runtime]System.Runtime.CompilerServices.DefaultInterpolatedStringHandler::AppendLiteral(string)
+        IL_00d0:  nop
+        IL_00d1:  ldloca.s   V_4
+        IL_00d3:  ldarg.0
+        IL_00d4:  ldfld      uint32 Program/'<<Main>$>d__0'::'<result>5__3'
+        IL_00d9:  call       instance void [System.Runtime]System.Runtime.CompilerServices.DefaultInterpolatedStringHandler::AppendFormatted<uint32>(!!0)
+        IL_00de:  nop
+        IL_00df:  ldloca.s   V_4
+        IL_00e1:  call       instance string [System.Runtime]System.Runtime.CompilerServices.DefaultInterpolatedStringHandler::ToStringAndClear()
+        IL_00e6:  call       void [System.Console]System.Console::WriteLine(string)
+        IL_00eb:  nop
+        IL_00ec:  nop
+        IL_00ed:  ldarg.0
+        IL_00ee:  ldnull
+        IL_00ef:  stfld      class [System.Runtime]System.Diagnostics.Stopwatch Program/'<<Main>$>d__0'::'<sw>5__2'
+        IL_00f4:  ldarg.0
+        IL_00f5:  ldfld      int32 Program/'<<Main>$>d__0'::'<i>5__1'
+        IL_00fa:  stloc.s    V_5
+        IL_00fc:  ldarg.0
+        IL_00fd:  ldloc.s    V_5
+        IL_00ff:  ldc.i4.1
+        IL_0100:  add
+        IL_0101:  stfld      int32 Program/'<<Main>$>d__0'::'<i>5__1'
+        IL_0106:  ldarg.0
+        IL_0107:  ldfld      int32 Program/'<<Main>$>d__0'::'<i>5__1'
+        IL_010c:  ldc.i4.s   10
+        IL_010e:  clt
+        IL_0110:  stloc.s    V_6
+        IL_0112:  ldloc.s    V_6
+        IL_0114:  brtrue     IL_001a
+
+        IL_0119:  ldc.i4.s   100
+        IL_011b:  stloc.1
+        IL_011c:  leave.s    IL_0138
+
+      }  // end .try
+      catch [System.Runtime]System.Exception 
+      {
+        IL_011e:  stloc.s    V_7
+        IL_0120:  ldarg.0
+        IL_0121:  ldc.i4.s   -2
+        IL_0123:  stfld      int32 Program/'<<Main>$>d__0'::'<>1__state'
+        IL_0128:  ldarg.0
+        IL_0129:  ldflda     valuetype [System.Runtime]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<int32> Program/'<<Main>$>d__0'::'<>t__builder'
+        IL_012e:  ldloc.s    V_7
+        IL_0130:  call       instance void valuetype [System.Runtime]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<int32>::SetException(class [System.Runtime]System.Exception)
+        IL_0135:  nop
+        IL_0136:  leave.s    IL_014d
+
+      }  // end handler
+      IL_0138:  ldarg.0
+      IL_0139:  ldc.i4.s   -2
+      IL_013b:  stfld      int32 Program/'<<Main>$>d__0'::'<>1__state'
+      IL_0140:  ldarg.0
+      IL_0141:  ldflda     valuetype [System.Runtime]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<int32> Program/'<<Main>$>d__0'::'<>t__builder'
+      IL_0146:  ldloc.1
+      IL_0147:  call       instance void valuetype [System.Runtime]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<int32>::SetResult(!0)
+      IL_014c:  nop
+      IL_014d:  ret
+    } // end of method '<<Main>$>d__0'::MoveNext
+
+    .method private hidebysig newslot virtual final 
+            instance void  SetStateMachine(class [System.Runtime]System.Runtime.CompilerServices.IAsyncStateMachine stateMachine) cil managed
+    {
+      .custom instance void [System.Runtime]System.Diagnostics.DebuggerHiddenAttribute::.ctor() = ( 01 00 00 00 ) 
+      .override [System.Runtime]System.Runtime.CompilerServices.IAsyncStateMachine::SetStateMachine
+      // Code size       1 (0x1)
+      .maxstack  8
+      IL_0000:  ret
+    } // end of method '<<Main>$>d__0'::SetStateMachine
+
+  } // end of class '<<Main>$>d__0'
+
+  .method private hidebysig static class [System.Runtime]System.Threading.Tasks.Task`1<int32> 
+          '<Main>$'(string[] args) cil managed
+  {
+    .custom instance void [System.Runtime]System.Runtime.CompilerServices.AsyncStateMachineAttribute::.ctor(class [System.Runtime]System.Type) = ( 01 00 15 50 72 6F 67 72 61 6D 2B 3C 3C 4D 61 69   // ...Program+<<Mai
+                                                                                                                                                   6E 3E 24 3E 64 5F 5F 30 00 00 )                   // n>$>d__0..
+    .custom instance void [System.Runtime]System.Diagnostics.DebuggerStepThroughAttribute::.ctor() = ( 01 00 00 00 ) 
+    // Code size       57 (0x39)
+    .maxstack  2
+    .locals init (class Program/'<<Main>$>d__0' V_0)
+    IL_0000:  newobj     instance void Program/'<<Main>$>d__0'::.ctor()
+    IL_0005:  stloc.0
+    IL_0006:  ldloc.0
+    IL_0007:  call       valuetype [System.Runtime]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<!0> valuetype [System.Runtime]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<int32>::Create()
+    IL_000c:  stfld      valuetype [System.Runtime]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<int32> Program/'<<Main>$>d__0'::'<>t__builder'
+    IL_0011:  ldloc.0
+    IL_0012:  ldarg.0
+    IL_0013:  stfld      string[] Program/'<<Main>$>d__0'::args
+    IL_0018:  ldloc.0
+    IL_0019:  ldc.i4.m1
+    IL_001a:  stfld      int32 Program/'<<Main>$>d__0'::'<>1__state'
+    IL_001f:  ldloc.0
+    IL_0020:  ldflda     valuetype [System.Runtime]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<int32> Program/'<<Main>$>d__0'::'<>t__builder'
+    IL_0025:  ldloca.s   V_0
+    IL_0027:  call       instance void valuetype [System.Runtime]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<int32>::Start<class Program/'<<Main>$>d__0'>(!!0&)
+    IL_002c:  nop
+    IL_002d:  ldloc.0
+    IL_002e:  ldflda     valuetype [System.Runtime]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<int32> Program/'<<Main>$>d__0'::'<>t__builder'
+    IL_0033:  call       instance class [System.Runtime]System.Threading.Tasks.Task`1<!0> valuetype [System.Runtime]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<int32>::get_Task()
+    IL_0038:  ret
+  } // end of method Program::'<Main>$'
+
+  .method public hidebysig specialname rtspecialname 
+          instance void  .ctor() cil managed
+  {
+    // Code size       8 (0x8)
+    .maxstack  8
+    IL_0000:  ldarg.0
+    IL_0001:  call       instance void [System.Runtime]System.Object::.ctor()
+    IL_0006:  nop
+    IL_0007:  ret
+  } // end of method Program::.ctor
+
+  .method private hidebysig specialname static 
+          int32  '<Main>'(string[] args) cil managed
+  {
+    .entrypoint
+    .custom instance void [System.Runtime]System.Diagnostics.DebuggerStepThroughAttribute::.ctor() = ( 01 00 00 00 ) 
+    // Code size       20 (0x14)
+    .maxstack  1
+    .locals init (valuetype [System.Runtime]System.Runtime.CompilerServices.TaskAwaiter`1<int32> V_0)
+    IL_0000:  ldarg.0
+    IL_0001:  call       class [System.Runtime]System.Threading.Tasks.Task`1<int32> Program::'<Main>$'(string[])
+    IL_0006:  callvirt   instance valuetype [System.Runtime]System.Runtime.CompilerServices.TaskAwaiter`1<!0> class [System.Runtime]System.Threading.Tasks.Task`1<int32>::GetAwaiter()
+    IL_000b:  stloc.0
+    IL_000c:  ldloca.s   V_0
+    IL_000e:  call       instance !0 valuetype [System.Runtime]System.Runtime.CompilerServices.TaskAwaiter`1<int32>::GetResult()
+    IL_0013:  ret
+  } // end of method Program::'<Main>'
+
+  .method assembly hidebysig static uint32 modopt([System.Runtime]System.Threading.Tasks.Task`1)
+          '<<Main>$>g__A|0_0'(uint32 n) cil managed
+  {
+    .custom instance void [System.Runtime]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) 
+    // Code size       32 (0x20)
+    .maxstack  2
+    .locals init (uint32 V_0,
+             uint32 V_1,
+             bool V_2,
+             uint32 V_3)
+    IL_0000:  nop
+    IL_0001:  ldarg.0
+    IL_0002:  stloc.0
+    IL_0003:  ldc.i4.0
+    IL_0004:  stloc.1
+    IL_0005:  br.s       IL_0012
+
+    IL_0007:  ldloc.0
+    IL_0008:  call       uint32 modopt([System.Runtime]System.Threading.Tasks.Task`1) Program::'<<Main>$>g__B|0_1'(uint32)
+    IL_000d:  stloc.0
+    IL_000e:  ldloc.1
+    IL_000f:  ldc.i4.1
+    IL_0010:  add
+    IL_0011:  stloc.1
+    IL_0012:  ldloc.1
+    IL_0013:  ldarg.0
+    IL_0014:  clt.un
+    IL_0016:  stloc.2
+    IL_0017:  ldloc.2
+    IL_0018:  brtrue.s   IL_0007
+
+    IL_001a:  ldloc.0
+    IL_001b:  stloc.3
+    IL_001c:  br.s       IL_001e
+
+    IL_001e:  ldloc.3
+    IL_001f:  ret
+  } // end of method Program::'<<Main>$>g__A|0_0'
+
+  .method assembly hidebysig static uint32 modopt([System.Runtime]System.Threading.Tasks.Task`1)
+          '<<Main>$>g__B|0_1'(uint32 n) cil managed
+  {
+  .custom instance void [System.Runtime]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) 
+  // Code size       89 (0x59)
+  .maxstack  2
+  .locals init (uint32 V_0,
+           bool V_1,
+           bool V_2,
+           bool V_3,
+           uint32 V_4)
+  IL_0000:  nop
+  IL_0001:  ldarg.0
+  IL_0002:  stloc.0
+  IL_0003:  ldloc.0
+  IL_0004:  ldc.i4     0x773593ed
+  IL_0009:  mul
+  IL_000a:  stloc.0
+  IL_000b:  ldloc.0
+  IL_000c:  ldc.i4     0x3e8
+  IL_0011:  clt.un
+  IL_0013:  stloc.1
+  IL_0014:  ldloc.1
+  IL_0015:  brfalse.s  IL_001d
+  IL_0017:  call       int modopt([System.Runtime]System.Threading.Tasks.Task`1) Program::'<<Main>$>g__MyYield|0_2'()
+  IL_001c:  pop
+  IL_001d:  ldloc.0
+  IL_001e:  ldc.i4     0x773593ed
+  IL_0023:  mul
+  IL_0024:  stloc.0
+  IL_0025:  ldloc.0
+  IL_0026:  ldc.i4     0x3e8
+  IL_002b:  clt.un
+  IL_002d:  stloc.2
+  IL_002e:  ldloc.2
+  IL_002f:  brfalse.s  IL_0037
+  IL_0031:  call       int modopt([System.Runtime]System.Threading.Tasks.Task`1) Program::'<<Main>$>g__MyYield|0_2'()
+  IL_0036:  pop
+  IL_0037:  ldloc.0
+  IL_0038:  ldc.i4     0x773593ed
+  IL_003d:  mul
+  IL_003e:  stloc.0
+  IL_003f:  ldloc.0
+  IL_0040:  ldc.i4     0x3e8
+  IL_0045:  clt.un
+  IL_0047:  stloc.3
+  IL_0048:  ldloc.3
+  IL_0049:  brfalse.s  IL_0051
+  IL_004b:  call       int modopt([System.Runtime]System.Threading.Tasks.Task`1) Program::'<<Main>$>g__MyYield|0_2'()
+  IL_0050:  pop
+  IL_0051:  ldloc.0
+  IL_0052:  stloc.s    V_4
+  IL_0054:  br.s       IL_0056
+  IL_0056:  ldloc.s    V_4
+  IL_0058:  ret
+  } // end of method Program::'<<Main>$>g__B|0_1'
+
+.class auto ansi sealed nested private beforefieldinit '<<<Main>$>g__MyYield|0_2>d'
+       extends [System.Runtime]System.ValueType
+       implements [System.Runtime]System.Runtime.CompilerServices.IAsyncStateMachine
+{
+  .custom instance void [System.Runtime]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) 
+  .field public int32 '<>1__state'
+  .field public valuetype [System.Runtime]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<int32> '<>t__builder'
+  .field private valuetype [System.Runtime]System.Runtime.CompilerServices.YieldAwaitable/YieldAwaiter '<>u__1'
+
+.method private hidebysig newslot virtual final 
+        instance void  MoveNext() cil managed
+{
+  .override [System.Runtime]System.Runtime.CompilerServices.IAsyncStateMachine::MoveNext
+  // Code size       150 (0x96)
+  .maxstack  3
+  .locals init (int32 V_0,
+           int32 V_1,
+           valuetype [System.Runtime]System.Runtime.CompilerServices.YieldAwaitable/YieldAwaiter V_2,
+           valuetype [System.Runtime]System.Runtime.CompilerServices.YieldAwaitable V_3,
+           class [System.Runtime]System.Exception V_4)
+  IL_0000:  ldarg.0
+  IL_0001:  ldfld      int32 Program/'<<<Main>$>g__MyYield|0_2>d'::'<>1__state'
+  IL_0006:  stloc.0
+  .try
+  {
+    IL_0007:  ldloc.0
+    IL_0008:  brfalse.s  IL_0041
+    IL_000a:  call       valuetype [System.Runtime]System.Runtime.CompilerServices.YieldAwaitable [System.Runtime]System.Threading.Tasks.Task::Yield()
+    IL_000f:  stloc.3
+    IL_0010:  ldloca.s   V_3
+    IL_0012:  call       instance valuetype [System.Runtime]System.Runtime.CompilerServices.YieldAwaitable/YieldAwaiter [System.Runtime]System.Runtime.CompilerServices.YieldAwaitable::GetAwaiter()
+    IL_0017:  stloc.2
+    IL_0018:  ldloca.s   V_2
+    IL_001a:  call       instance bool [System.Runtime]System.Runtime.CompilerServices.YieldAwaitable/YieldAwaiter::get_IsCompleted()
+    IL_001f:  brtrue.s   IL_005d
+    IL_0021:  ldarg.0
+    IL_0022:  ldc.i4.0
+    IL_0023:  dup
+    IL_0024:  stloc.0
+    IL_0025:  stfld      int32 Program/'<<<Main>$>g__MyYield|0_2>d'::'<>1__state'
+    IL_002a:  ldarg.0
+    IL_002b:  ldloc.2
+    IL_002c:  stfld      valuetype [System.Runtime]System.Runtime.CompilerServices.YieldAwaitable/YieldAwaiter Program/'<<<Main>$>g__MyYield|0_2>d'::'<>u__1'
+    IL_0031:  ldarg.0
+    IL_0032:  ldflda     valuetype [System.Runtime]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<int32> Program/'<<<Main>$>g__MyYield|0_2>d'::'<>t__builder'
+    IL_0037:  ldloca.s   V_2
+    IL_0039:  ldarg.0
+    IL_003a:  call       instance void valuetype [System.Runtime]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<int32>::AwaitUnsafeOnCompleted<valuetype [System.Runtime]System.Runtime.CompilerServices.YieldAwaitable/YieldAwaiter,valuetype Program/'<<<Main>$>g__MyYield|0_2>d'>(!!0&,
+                                                                                                                                                                                                                                                                                               !!1&)
+    IL_003f:  leave.s    IL_0095
+    IL_0041:  ldarg.0
+    IL_0042:  ldfld      valuetype [System.Runtime]System.Runtime.CompilerServices.YieldAwaitable/YieldAwaiter Program/'<<<Main>$>g__MyYield|0_2>d'::'<>u__1'
+    IL_0047:  stloc.2
+    IL_0048:  ldarg.0
+    IL_0049:  ldflda     valuetype [System.Runtime]System.Runtime.CompilerServices.YieldAwaitable/YieldAwaiter Program/'<<<Main>$>g__MyYield|0_2>d'::'<>u__1'
+    IL_004e:  initobj    [System.Runtime]System.Runtime.CompilerServices.YieldAwaitable/YieldAwaiter
+    IL_0054:  ldarg.0
+    IL_0055:  ldc.i4.m1
+    IL_0056:  dup
+    IL_0057:  stloc.0
+    IL_0058:  stfld      int32 Program/'<<<Main>$>g__MyYield|0_2>d'::'<>1__state'
+    IL_005d:  ldloca.s   V_2
+    IL_005f:  call       instance void [System.Runtime]System.Runtime.CompilerServices.YieldAwaitable/YieldAwaiter::GetResult()
+    IL_0064:  ldc.i4.0
+    IL_0065:  stloc.1
+    IL_0066:  leave.s    IL_0081
+  }  // end .try
+  catch [System.Runtime]System.Exception 
+  {
+    IL_0068:  stloc.s    V_4
+    IL_006a:  ldarg.0
+    IL_006b:  ldc.i4.s   -2
+    IL_006d:  stfld      int32 Program/'<<<Main>$>g__MyYield|0_2>d'::'<>1__state'
+    IL_0072:  ldarg.0
+    IL_0073:  ldflda     valuetype [System.Runtime]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<int32> Program/'<<<Main>$>g__MyYield|0_2>d'::'<>t__builder'
+    IL_0078:  ldloc.s    V_4
+    IL_007a:  call       instance void valuetype [System.Runtime]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<int32>::SetException(class [System.Runtime]System.Exception)
+    IL_007f:  leave.s    IL_0095
+  }  // end handler
+  IL_0081:  ldarg.0
+  IL_0082:  ldc.i4.s   -2
+  IL_0084:  stfld      int32 Program/'<<<Main>$>g__MyYield|0_2>d'::'<>1__state'
+  IL_0089:  ldarg.0
+  IL_008a:  ldflda     valuetype [System.Runtime]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<int32> Program/'<<<Main>$>g__MyYield|0_2>d'::'<>t__builder'
+  IL_008f:  ldloc.1
+  IL_0090:  call       instance void valuetype [System.Runtime]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<int32>::SetResult(!0)
+  IL_0095:  ret
+} // end of method '<<<Main>$>g__MyYield|0_2>d'::MoveNext
+.method private hidebysig newslot virtual final 
+        instance void  SetStateMachine(class [System.Runtime]System.Runtime.CompilerServices.IAsyncStateMachine stateMachine) cil managed
+{
+  .custom instance void [System.Runtime]System.Diagnostics.DebuggerHiddenAttribute::.ctor() = ( 01 00 00 00 ) 
+  .param [1]
+  .custom instance void System.Runtime.CompilerServices.NullableAttribute::.ctor(uint8) = ( 01 00 01 00 00 ) 
+  .override [System.Runtime]System.Runtime.CompilerServices.IAsyncStateMachine::SetStateMachine
+  // Code size       13 (0xd)
+  .maxstack  8
+  IL_0000:  ldarg.0
+  IL_0001:  ldflda     valuetype [System.Runtime]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<int32> Program/'<<<Main>$>g__MyYield|0_2>d'::'<>t__builder'
+  IL_0006:  ldarg.1
+  IL_0007:  call       instance void valuetype [System.Runtime]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<int32>::SetStateMachine(class [System.Runtime]System.Runtime.CompilerServices.IAsyncStateMachine)
+  IL_000c:  ret
+} // end of method '<<<Main>$>g__MyYield|0_2>d'::SetStateMachine
+} // end of class '<<<Main>$>g__MyYield|0_2>d'
+
+.method assembly hidebysig static class [System.Runtime]System.Threading.Tasks.Task`1<int32> 
+        '<<Main>$>g__MyYield|0_2'() cil managed
+{
+  .custom instance void System.Runtime.CompilerServices.NullableContextAttribute::.ctor(uint8) = ( 01 00 01 00 00 ) 
+  .custom instance void [System.Runtime]System.Runtime.CompilerServices.AsyncStateMachineAttribute::.ctor(class [System.Runtime]System.Type) = ( 01 00 22 50 72 6F 67 72 61 6D 2B 3C 3C 3C 4D 61   // .."Program+<<<Ma
+                                                                                                                                                 69 6E 3E 24 3E 67 5F 5F 4D 79 59 69 65 6C 64 7C   // in>$>g__MyYield|
+                                                                                                                                                 30 5F 32 3E 64 00 00 )                            // 0_2>d..
+  .custom instance void [System.Runtime]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) 
+  // Code size       47 (0x2f)
+  .maxstack  2
+  .locals init (valuetype Program/'<<<Main>$>g__MyYield|0_2>d' V_0)
+  IL_0000:  ldloca.s   V_0
+  IL_0002:  call       valuetype [System.Runtime]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<!0> valuetype [System.Runtime]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<int32>::Create()
+  IL_0007:  stfld      valuetype [System.Runtime]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<int32> Program/'<<<Main>$>g__MyYield|0_2>d'::'<>t__builder'
+  IL_000c:  ldloca.s   V_0
+  IL_000e:  ldc.i4.m1
+  IL_000f:  stfld      int32 Program/'<<<Main>$>g__MyYield|0_2>d'::'<>1__state'
+  IL_0014:  ldloca.s   V_0
+  IL_0016:  ldflda     valuetype [System.Runtime]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<int32> Program/'<<<Main>$>g__MyYield|0_2>d'::'<>t__builder'
+  IL_001b:  ldloca.s   V_0
+  IL_001d:  call       instance void valuetype [System.Runtime]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<int32>::Start<valuetype Program/'<<<Main>$>g__MyYield|0_2>d'>(!!0&)
+  IL_0022:  ldloca.s   V_0
+  IL_0024:  ldflda     valuetype [System.Runtime]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<int32> Program/'<<<Main>$>g__MyYield|0_2>d'::'<>t__builder'
+  IL_0029:  call       instance class [System.Runtime]System.Threading.Tasks.Task`1<!0> valuetype [System.Runtime]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<int32>::get_Task()
+  IL_002e:  ret
+} // end of method Program::'<<Main>$>g__MyYield|0_2'
+
+} // end of class Program
+
+
+// =============================================================
+
+// *********** DISASSEMBLY COMPLETE ***********************
diff --git a/src/tests/Loader/async/runtimetask-asyncfibonacci-with-yields.ilproj b/src/tests/Loader/async/runtimetask-asyncfibonacci-with-yields.ilproj
new file mode 100644
index 00000000000000..bd17dd2bad5a7e
--- /dev/null
+++ b/src/tests/Loader/async/runtimetask-asyncfibonacci-with-yields.ilproj
@@ -0,0 +1,8 @@
+<Project Sdk="Microsoft.NET.Sdk.IL">
+  <PropertyGroup>
+    <CLRTestPriority>1</CLRTestPriority>
+  </PropertyGroup>
+  <ItemGroup>
+    <Compile Include="runtimetask-asyncfibonacci-without-yields.il" />
+  </ItemGroup>
+</Project>

From 34a2219a664a0789fc968af9b0f4ff14a4bccdc0 Mon Sep 17 00:00:00 2001
From: David Wrighton <davidwr@microsoft.com>
Date: Tue, 26 Sep 2023 10:20:26 -0700
Subject: [PATCH 015/203] - Add async to Task stub - Public apis for directly
 awaiting from async2 code - Update test case to use the new apis and now the
 code is lovely and simple

---
 src/coreclr/vm/corelib.h                      |   8 +
 src/coreclr/vm/metasig.h                      |   2 +
 src/coreclr/vm/prestub.cpp                    | 176 ++++++++++--------
 .../Strategies/FileStreamHelpers.Windows.cs   |   2 +-
 .../ConfiguredValueTaskAwaitable.cs           |   4 +-
 .../CompilerServices/INotifyCompletion.cs     |  28 +++
 .../CompilerServices/RuntimeHelpers.cs        | 161 ++++++++++++++++
 .../Runtime/CompilerServices/TaskAwaiter.cs   |   8 +-
 .../CompilerServices/ValueTaskAwaiter.cs      |   4 +-
 .../CompilerServices/YieldAwaitable.cs        |   2 +-
 .../System.Runtime/ref/System.Runtime.cs      |  42 ++++-
 .../runtimetask-asyncfibonacci-with-yields.il | 144 +-------------
 12 files changed, 351 insertions(+), 230 deletions(-)

diff --git a/src/coreclr/vm/corelib.h b/src/coreclr/vm/corelib.h
index ba23da832bd834..f06a9aa6847a9d 100644
--- a/src/coreclr/vm/corelib.h
+++ b/src/coreclr/vm/corelib.h
@@ -351,6 +351,7 @@ DEFINE_CLASS(THREAD_START_EXCEPTION,Threading,              ThreadStartException
 DEFINE_METHOD(THREAD_START_EXCEPTION,EX_CTOR,               .ctor,                      IM_Exception_RetVoid)
 
 DEFINE_CLASS(TASK_1, Tasks, Task`1)
+DEFINE_METHOD(TASK_1, GET_AWAITER, GetAwaiter, NoSig)
 
 DEFINE_CLASS(RUNTIME_TASK_STATE_1, Tasks, RuntimeTaskState`1)
 DEFINE_METHOD(RUNTIME_TASK_STATE_1, PUSH, Push, IM_RetVoid)
@@ -358,6 +359,8 @@ DEFINE_METHOD(RUNTIME_TASK_STATE_1, POP, Pop, IM_RetVoid)
 DEFINE_METHOD(RUNTIME_TASK_STATE_1, FROM_EXCEPTION, FromException, IM_Exception_RetTaskOfT)
 DEFINE_METHOD(RUNTIME_TASK_STATE_1, FROM_RESULT, FromResult, IM_T_RetTaskOfT)
 
+DEFINE_CLASS(TASK_AWAITER_1, CompilerServices, TaskAwaiter`1)
+
 DEFINE_CLASS(TYPE_HANDLE,           System,                 RuntimeTypeHandle)
 DEFINE_CLASS(RT_TYPE_HANDLE,        System,                 RuntimeTypeHandle)
 DEFINE_METHOD(RT_TYPE_HANDLE,       PVOID_CTOR,             .ctor,                      IM_RuntimeType_RetVoid)
@@ -675,6 +678,11 @@ DEFINE_METHOD(RUNTIME_HELPERS,      ALLOC_TAILCALL_ARG_BUFFER, AllocTailCallArgB
 DEFINE_METHOD(RUNTIME_HELPERS,      GET_TAILCALL_INFO,      GetTailCallInfo, NoSig)
 DEFINE_METHOD(RUNTIME_HELPERS,      DISPATCH_TAILCALLS,     DispatchTailCalls,          NoSig)
 
+DEFINE_METHOD(RUNTIME_HELPERS,      GET_OR_CREATE_RESUMPTION_DELEGATE, GetOrCreateResumptionDelegate, NoSig)
+DEFINE_METHOD(RUNTIME_HELPERS,      ABORT_SUSPEND, AbortSuspend, NoSig)
+DEFINE_METHOD(RUNTIME_HELPERS,      SUSPEND_IF_SUSPENSION_NOT_ABORTED, SuspendIfSuspensionNotAborted, NoSig)
+DEFINE_METHOD(RUNTIME_HELPERS,      UNSAFE_AWAIT_AWAITER_FROM_RUNTIME_ASYNC_2, UnsafeAwaitAwaiterFromRuntimeAsync, GM_U_RetT)
+
 DEFINE_CLASS(UNSAFE,                CompilerServices,       Unsafe)
 DEFINE_METHOD(UNSAFE,               AS_POINTER,             AsPointer, NoSig)
 DEFINE_METHOD(UNSAFE,               BYREF_IS_NULL,          IsNullRef, NoSig)
diff --git a/src/coreclr/vm/metasig.h b/src/coreclr/vm/metasig.h
index dff10ce40b8d49..b0bdd9ccce04bb 100644
--- a/src/coreclr/vm/metasig.h
+++ b/src/coreclr/vm/metasig.h
@@ -619,6 +619,8 @@ DEFINE_METASIG(SM(PtrByte_RetStr, P(b), s))
 DEFINE_METASIG(SM(Str_RetPtrByte, s, P(b)))
 DEFINE_METASIG(SM(PtrByte_RetVoid, P(b), v))
 
+DEFINE_METASIG(GM(U_RetT, IMAGE_CEE_CS_CALLCONV_DEFAULT, 1, M(1), M(0)))
+
 // Undefine macros in case we include the file again in the compilation unit
 
 #undef  DEFINE_METASIG
diff --git a/src/coreclr/vm/prestub.cpp b/src/coreclr/vm/prestub.cpp
index c3ae6666c1622d..759117e95203b8 100644
--- a/src/coreclr/vm/prestub.cpp
+++ b/src/coreclr/vm/prestub.cpp
@@ -1472,24 +1472,9 @@ bool MethodDesc::TryGenerateAsyncThunk(DynamicResolver** resolver, COR_ILMETHOD_
 
     ULONG offsetOfAsyncDetailsUnused;
     AsyncTaskMethod asyncType = ClassifyAsyncMethod(GetSigPointer(), GetModule(), &offsetOfAsyncDetailsUnused);
-    if (asyncType != AsyncTaskMethod::TaskReturningMethod)
-    {
-        // Async to task helpers not yet implemented
-        _ASSERTE(FALSE);
-        return false;
-    }
-
     MethodDesc *pAsyncOtherVariant = this->GetAsyncOtherVariant();
-
     MetaSig msig(pCallSig, cbCallSigSize, this->GetModule(), NULL, MetaSig::sigMember);
-
-    TypeHandle thTaskRet = msig.GetRetTypeHandleThrowing();
-    TypeHandle thLogicalRetType = thTaskRet.GetMethodTable()->GetInstantiation()[0];
-    MethodTable* pMTRuntimeTaskStateOpen = CoreLibBinder::GetClass(CLASS__RUNTIME_TASK_STATE_1);
-
-    MethodTable* pMTRuntimeTaskState = ClassLoader::LoadGenericInstantiationThrowing(pMTRuntimeTaskStateOpen->GetModule(), pMTRuntimeTaskStateOpen->GetCl(), Instantiation(&thLogicalRetType, 1)).GetMethodTable();
     NewHolder<ILStubResolver> ilResolver = new ILStubResolver();
-
         // Initialize the resolver target details.
     ilResolver->SetStubMethodDesc(this);
     ilResolver->SetStubTargetMethodDesc(pAsyncOtherVariant);
@@ -1504,83 +1489,128 @@ bool MethodDesc::TryGenerateAsyncThunk(DynamicResolver** resolver, COR_ILMETHOD_
         (ILStubLinkerFlags)ILSTUB_LINKER_FLAG_NONE);
 
     ILCodeStream* pCode = sl.NewCodeStream(ILStubLinker::kDispatch);
-    LocalDesc rtsLocalDesc(pMTRuntimeTaskState);
-    DWORD rtsLocal = pCode->NewLocal(rtsLocalDesc);
 
-    LocalDesc resultLocalDesc(thLogicalRetType);
-    DWORD resultLocal = pCode->NewLocal(resultLocalDesc);
 
-    LocalDesc exceptionLocalDesc(CoreLibBinder::GetClass(CLASS__EXCEPTION));
-    DWORD exceptionLocal = pCode->NewLocal(exceptionLocalDesc);
+    if (asyncType == AsyncTaskMethod::TaskReturningMethod)
+    {
+        TypeHandle thTaskRet = msig.GetRetTypeHandleThrowing();
+        TypeHandle thLogicalRetType = thTaskRet.GetMethodTable()->GetInstantiation()[0];
+        MethodTable* pMTRuntimeTaskStateOpen = CoreLibBinder::GetClass(CLASS__RUNTIME_TASK_STATE_1);
+        MethodTable* pMTRuntimeTaskState = ClassLoader::LoadGenericInstantiationThrowing(pMTRuntimeTaskStateOpen->GetModule(), pMTRuntimeTaskStateOpen->GetCl(), Instantiation(&thLogicalRetType, 1)).GetMethodTable();
 
-    LocalDesc returnLocalDesc(thTaskRet);
-    DWORD returnLocal = pCode->NewLocal(returnLocalDesc);
+        LocalDesc rtsLocalDesc(pMTRuntimeTaskState);
+        DWORD rtsLocal = pCode->NewLocal(rtsLocalDesc);
 
-    auto pNoExceptionLabel = pCode->NewCodeLabel();
-    auto pReturnResultLabel = pCode->NewCodeLabel();
+        LocalDesc resultLocalDesc(thLogicalRetType);
+        DWORD resultLocal = pCode->NewLocal(resultLocalDesc);
 
-    pCode->EmitLDLOCA(rtsLocal);
-    pCode->EmitINITOBJ(pCode->GetToken(pMTRuntimeTaskState));
-    pCode->EmitLDLOCA(resultLocal);
-    pCode->EmitINITOBJ(pCode->GetToken(thLogicalRetType));
-    pCode->EmitLDLOCA(exceptionLocal);
-    pCode->EmitINITOBJ(pCode->GetToken(CoreLibBinder::GetClass(CLASS__EXCEPTION)));
-    pCode->EmitLDLOCA(returnLocal);
-    pCode->EmitINITOBJ(pCode->GetToken(thTaskRet));
-    pCode->EmitLDLOCA(rtsLocal);
-    pCode->EmitCALL(pCode->GetToken(pMTRuntimeTaskState->GetParallelMethodDesc(CoreLibBinder::GetMethod(METHOD__RUNTIME_TASK_STATE_1__PUSH))), 1, 0);
-    {
-        pCode->BeginTryBlock();
-        pCode->EmitNOP("Separate try blocks");
+        LocalDesc exceptionLocalDesc(CoreLibBinder::GetClass(CLASS__EXCEPTION));
+        DWORD exceptionLocal = pCode->NewLocal(exceptionLocalDesc);
+
+        LocalDesc returnLocalDesc(thTaskRet);
+        DWORD returnLocal = pCode->NewLocal(returnLocalDesc);
+
+        auto pNoExceptionLabel = pCode->NewCodeLabel();
+        auto pReturnResultLabel = pCode->NewCodeLabel();
+
+        pCode->EmitLDLOCA(rtsLocal);
+        pCode->EmitINITOBJ(pCode->GetToken(pMTRuntimeTaskState));
+        pCode->EmitLDLOCA(resultLocal);
+        pCode->EmitINITOBJ(pCode->GetToken(thLogicalRetType));
+        pCode->EmitLDLOCA(exceptionLocal);
+        pCode->EmitINITOBJ(pCode->GetToken(CoreLibBinder::GetClass(CLASS__EXCEPTION)));
+        pCode->EmitLDLOCA(returnLocal);
+        pCode->EmitINITOBJ(pCode->GetToken(thTaskRet));
+        pCode->EmitLDLOCA(rtsLocal);
+        pCode->EmitCALL(pCode->GetToken(pMTRuntimeTaskState->GetParallelMethodDesc(CoreLibBinder::GetMethod(METHOD__RUNTIME_TASK_STATE_1__PUSH))), 1, 0);
         {
             pCode->BeginTryBlock();
-
-            DWORD localArg = 0;
-            if (msig.HasThis())
+            pCode->EmitNOP("Separate try blocks");
             {
-                // Struct async thunks not yet implemented
-                _ASSERTE(!this->GetMethodTable()->IsValueType());
-                pCode->EmitLDARG(localArg++);
+                pCode->BeginTryBlock();
+
+                DWORD localArg = 0;
+                if (msig.HasThis())
+                {
+                    // Struct async thunks not yet implemented
+                    _ASSERTE(!this->GetMethodTable()->IsValueType());
+                    pCode->EmitLDARG(localArg++);
+                }
+                for (UINT iArg = 0; iArg < msig.NumFixedArgs(); iArg++)
+                {
+                    pCode->EmitLDARG(localArg++);
+                }
+                pCode->EmitCALL(pCode->GetToken(pAsyncOtherVariant), localArg, 1);
+                pCode->EmitSTLOC(resultLocal);
+                pCode->EmitLEAVE(pNoExceptionLabel);
+                pCode->EndTryBlock();
             }
-            for (UINT iArg = 0; iArg < msig.NumFixedArgs(); iArg++)
+            // Catch
             {
-                pCode->EmitLDARG(localArg++);
+                pCode->BeginCatchBlock(pCode->GetToken(CoreLibBinder::GetClass(CLASS__EXCEPTION)));
+                pCode->EmitSTLOC(exceptionLocal);
+                pCode->EmitLDLOCA(rtsLocal);
+                pCode->EmitLDLOC(exceptionLocal);
+                pCode->EmitCALL(pCode->GetToken(pMTRuntimeTaskState->GetParallelMethodDesc(CoreLibBinder::GetMethod(METHOD__RUNTIME_TASK_STATE_1__FROM_EXCEPTION))), 2, 1);
+                pCode->EmitSTLOC(returnLocal);
+                pCode->EmitLEAVE(pReturnResultLabel);
+                pCode->EndCatchBlock();
             }
-            pCode->EmitCALL(pCode->GetToken(pAsyncOtherVariant), localArg, 1);
-            pCode->EmitSTLOC(resultLocal);
-            pCode->EmitLEAVE(pNoExceptionLabel);
+            pCode->EmitLabel(pNoExceptionLabel);
+            pCode->EmitLDLOCA(rtsLocal);
+            pCode->EmitLDLOC(resultLocal);
+            pCode->EmitCALL(pCode->GetToken(pMTRuntimeTaskState->GetParallelMethodDesc(CoreLibBinder::GetMethod(METHOD__RUNTIME_TASK_STATE_1__FROM_RESULT))), 2, 1);
+            pCode->EmitSTLOC(returnLocal);
+            pCode->EmitLEAVE(pReturnResultLabel);
             pCode->EndTryBlock();
         }
-        // Catch
+        // Finally
         {
-            pCode->BeginCatchBlock(pCode->GetToken(CoreLibBinder::GetClass(CLASS__EXCEPTION)));
-            pCode->EmitSTLOC(exceptionLocal);
+            pCode->BeginFinallyBlock();
             pCode->EmitLDLOCA(rtsLocal);
-            pCode->EmitLDLOC(exceptionLocal);
-            pCode->EmitCALL(pCode->GetToken(pMTRuntimeTaskState->GetParallelMethodDesc(CoreLibBinder::GetMethod(METHOD__RUNTIME_TASK_STATE_1__FROM_EXCEPTION))), 2, 1);
-            pCode->EmitSTLOC(returnLocal);
-            pCode->EmitLEAVE(pReturnResultLabel);
-            pCode->EndCatchBlock();
+            pCode->EmitCALL(pCode->GetToken(pMTRuntimeTaskState->GetParallelMethodDesc(CoreLibBinder::GetMethod(METHOD__RUNTIME_TASK_STATE_1__POP))), 1, 0);
+            pCode->EmitENDFINALLY();
+            pCode->EndFinallyBlock();
         }
-        pCode->EmitLabel(pNoExceptionLabel);
-        pCode->EmitLDLOCA(rtsLocal);
-        pCode->EmitLDLOC(resultLocal);
-        pCode->EmitCALL(pCode->GetToken(pMTRuntimeTaskState->GetParallelMethodDesc(CoreLibBinder::GetMethod(METHOD__RUNTIME_TASK_STATE_1__FROM_RESULT))), 2, 1);
-        pCode->EmitSTLOC(returnLocal);
-        pCode->EmitLEAVE(pReturnResultLabel);
-        pCode->EndTryBlock();
+        pCode->EmitLabel(pReturnResultLabel);
+        pCode->EmitLDLOC(returnLocal);
+        pCode->EmitRET();
     }
-    // Finally
+    else
     {
-        pCode->BeginFinallyBlock();
-        pCode->EmitLDLOCA(rtsLocal);
-        pCode->EmitCALL(pCode->GetToken(pMTRuntimeTaskState->GetParallelMethodDesc(CoreLibBinder::GetMethod(METHOD__RUNTIME_TASK_STATE_1__POP))), 1, 0);
-        pCode->EmitENDFINALLY();
-        pCode->EndFinallyBlock();
+        _ASSERTE(asyncType == AsyncTaskMethod::Async2Method);
+// Implement IL that is effectively the following
+/*
+{
+    // Call target method and await
+    return RuntimeHelpers.UnsafeAwaitAwaiterFromRuntimeAsync<AwaiterType, resultType>(Thunk(arg).GetAwaiter());
+}
+*/
+        TypeHandle thLogicalRetType = msig.GetRetTypeHandleThrowing();
+        MethodTable* pMTTaskOpen = CoreLibBinder::GetClass(CLASS__TASK_1);
+        MethodTable* pMTTask = ClassLoader::LoadGenericInstantiationThrowing(pMTTaskOpen->GetModule(), pMTTaskOpen->GetCl(), Instantiation(&thLogicalRetType, 1)).GetMethodTable();
+        MethodTable* pMTTaskAwaiterOpen = CoreLibBinder::GetClass(CLASS__TASK_AWAITER_1);
+        TypeHandle thTaskAwaiter = ClassLoader::LoadGenericInstantiationThrowing(pMTTaskAwaiterOpen->GetModule(), pMTTaskAwaiterOpen->GetCl(), Instantiation(&thLogicalRetType, 1));
+        DWORD localArg = 0;
+        if (msig.HasThis())
+        {
+            pCode->EmitLDARG(localArg++);
+        }
+        for (UINT iArg = 0; iArg < msig.NumFixedArgs(); iArg++)
+        {
+            pCode->EmitLDARG(localArg++);
+        }
+        pCode->EmitCALL(pCode->GetToken(pAsyncOtherVariant), localArg, 1);
+        pCode->EmitCALLVIRT(pCode->GetToken(pMTTask->GetParallelMethodDesc(CoreLibBinder::GetMethod(METHOD__TASK_1__GET_AWAITER))), 1, 1);
+        MethodDesc *pMagicAwaitFunc = CoreLibBinder::GetMethod(METHOD__RUNTIME_HELPERS__UNSAFE_AWAIT_AWAITER_FROM_RUNTIME_ASYNC_2);
+        TypeHandle thInstantiations[] {thLogicalRetType, thTaskAwaiter};
+
+        pMagicAwaitFunc = FindOrCreateAssociatedMethodDesc(pMagicAwaitFunc, pMagicAwaitFunc->GetMethodTable(), FALSE, Instantiation(thInstantiations, 2), FALSE);
+
+        pCode->EmitCALL(pCode->GetToken(pMagicAwaitFunc), 1, 1);
+
+        pCode->EmitRET();
     }
-    pCode->EmitLabel(pReturnResultLabel);
-    pCode->EmitLDLOC(returnLocal);
-    pCode->EmitRET();
 
     // Generate all IL associated data for JIT
     {
diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/FileStreamHelpers.Windows.cs b/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/FileStreamHelpers.Windows.cs
index ce1dedaa8889d8..9abfdf66571bf3 100644
--- a/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/FileStreamHelpers.Windows.cs
+++ b/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/FileStreamHelpers.Windows.cs
@@ -310,7 +310,7 @@ internal static async Task AsyncModeCopyToAsync(SafeFileHandle handle, bool canS
         }
 
         /// <summary>Used by AsyncWindowsFileStreamStrategy.CopyToAsync to enable awaiting the result of an overlapped I/O operation with minimal overhead.</summary>
-        private sealed unsafe class AsyncCopyToAwaitable : ICriticalNotifyCompletion
+        private sealed unsafe class AsyncCopyToAwaitable : ICriticalNotifyCompletion2
         {
             /// <summary>Sentinel object used to indicate that the I/O operation has completed before being awaited.</summary>
             private static readonly Action s_sentinel = () => { };
diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/ConfiguredValueTaskAwaitable.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/ConfiguredValueTaskAwaitable.cs
index 1700fb0cca8aa6..9a1e250a7b5a87 100644
--- a/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/ConfiguredValueTaskAwaitable.cs
+++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/ConfiguredValueTaskAwaitable.cs
@@ -27,7 +27,7 @@ public readonly struct ConfiguredValueTaskAwaitable
 
         /// <summary>Provides an awaiter for a <see cref="ConfiguredValueTaskAwaitable"/>.</summary>
         [StructLayout(LayoutKind.Auto)]
-        public readonly struct ConfiguredValueTaskAwaiter : ICriticalNotifyCompletion, IStateMachineBoxAwareAwaiter
+        public readonly struct ConfiguredValueTaskAwaiter : ICriticalNotifyCompletion2, IStateMachineBoxAwareAwaiter
         {
             /// <summary>The value being awaited.</summary>
             private readonly ValueTask _value;
@@ -132,7 +132,7 @@ public readonly struct ConfiguredValueTaskAwaitable<TResult>
 
         /// <summary>Provides an awaiter for a <see cref="ConfiguredValueTaskAwaitable{TResult}"/>.</summary>
         [StructLayout(LayoutKind.Auto)]
-        public readonly struct ConfiguredValueTaskAwaiter : ICriticalNotifyCompletion, IStateMachineBoxAwareAwaiter
+        public readonly struct ConfiguredValueTaskAwaiter : ICriticalNotifyCompletion2<TResult>, IStateMachineBoxAwareAwaiter
         {
             /// <summary>The value being awaited.</summary>
             private readonly ValueTask<TResult> _value;
diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/INotifyCompletion.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/INotifyCompletion.cs
index ff00892da828f8..d97d93ed869db3 100644
--- a/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/INotifyCompletion.cs
+++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/INotifyCompletion.cs
@@ -32,4 +32,32 @@ public interface ICriticalNotifyCompletion : INotifyCompletion
         /// <remarks>Unlike OnCompleted, UnsafeOnCompleted need not propagate ExecutionContext information.</remarks>
         void UnsafeOnCompleted(Action continuation);
     }
+
+    /// <summary>
+    /// Represents an awaiter used to schedule continuations when an await operation completes. Provides access to the complete api surface for awaiting
+    /// </summary>
+    public interface ICriticalNotifyCompletion2 : ICriticalNotifyCompletion
+    {
+        bool IsCompleted { get; }
+        void GetResult();
+    }
+    public interface ICriticalNotifyCompletion2<TResult> : ICriticalNotifyCompletion
+    {
+        bool IsCompleted { get; }
+        TResult GetResult();
+    }
+
+    /// <summary>
+    /// Represents an awaiter used to schedule continuations when an await operation completes. Provides access to the complete api surface for awaiting
+    /// </summary>
+    public interface INotifyCompletion2 : INotifyCompletion
+    {
+        bool IsCompleted { get; }
+        void GetResult();
+    }
+    public interface INotifyCompletion2<TResult> : INotifyCompletion
+    {
+        bool IsCompleted { get; }
+        TResult GetResult();
+    }
 }
diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.cs
index 47014ec5d718f5..0665687bace867 100644
--- a/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.cs
+++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.cs
@@ -115,5 +115,166 @@ internal static bool IsPrimitiveType(this CorElementType et)
         [Intrinsic]
         internal static bool IsKnownConstant(int t) => false;
 #pragma warning restore IDE0060
+
+        // TODO, this method should be marked so that it is only callable from a runtime async method
+        public static TResult UnsafeAwaitAwaiterFromRuntimeAsync<TResult, TAwaiter>(TAwaiter awaiter) where TAwaiter : ICriticalNotifyCompletion2<TResult>
+        {
+            if (!awaiter.IsCompleted)
+            {
+                // Create resumption delegate, wrapping task, and create tasklets to represent each stack frame on the stack.
+                // RuntimeTaskSuspender.GetOrCreateResumptionDelegate() works like a POSIX fork call in that calls to it will return a
+                // delegate if they are the initial call to GetOrCreateResumptionDelegate, but once the thread is resumed,
+                // it will resume with a return value of null.
+                Action? resumption = RuntimeHelpers.GetOrCreateResumptionDelegate();
+                if (resumption != null)
+                {
+                    // We are trying to suspend
+                    bool threwException = true;
+                    try
+                    {
+                        // Call the UnsafeOnCompleted api under a try block, as registering the suspension may cause
+                        // an exception to occur.
+                        awaiter.UnsafeOnCompleted(resumption);
+                        threwException = false;
+                    }
+                    finally
+                    {
+                        // If UnsafeOnCompleted itself threw, we should bubble the error up, but we need
+                        // to destroy any allocated tasklets that were created as part of the GetOrCreateResumptionDelegate api
+                        // as that state will never be useable.
+                        if (threwException)
+                            RuntimeHelpers.AbortSuspend();
+                    }
+                    // If we reach here, the only way that we actually run follow on code is for the continuation to actually run,
+                    // and return from GetOrCreateResumptionDelegate with a null return value.
+                    RuntimeHelpers.SuspendIfSuspensionNotAborted();
+                }
+            }
+
+            // Get the result from the awaiter, or throw the exception stored in the Task
+            return awaiter.GetResult();
+        }
+
+        // TODO, this method should be marked so that it is only callable from a runtime async method
+        public static TResult AwaitAwaiterFromRuntimeAsync<TResult, TAwaiter>(TAwaiter awaiter) where TAwaiter : INotifyCompletion2<TResult>
+        {
+            if (!awaiter.IsCompleted)
+            {
+                // Create resumption delegate, wrapping task, and create tasklets to represent each stack frame on the stack.
+                // RuntimeTaskSuspender.GetOrCreateResumptionDelegate() works like a POSIX fork call in that calls to it will return a
+                // delegate if they are the initial call to GetOrCreateResumptionDelegate, but once the thread is resumed,
+                // it will resume with a return value of null.
+                Action? resumption = RuntimeHelpers.GetOrCreateResumptionDelegate();
+                if (resumption != null)
+                {
+                    // We are trying to suspend
+                    bool threwException = true;
+                    try
+                    {
+                        // Call the OnCompleted api under a try block, as registering the suspension may cause
+                        // an exception to occur.
+                        awaiter.OnCompleted(resumption);
+                        threwException = false;
+                    }
+                    finally
+                    {
+                        // If OnCompleted itself threw, we should bubble the error up, but we need
+                        // to destroy any allocated tasklets that were created as part of the GetOrCreateResumptionDelegate api
+                        // as that state will never be useable.
+                        if (threwException)
+                            RuntimeHelpers.AbortSuspend();
+                    }
+                    // If we reach here, the only way that we actually run follow on code is for the continuation to actually run,
+                    // and return from GetOrCreateResumptionDelegate with a null return value.
+                    RuntimeHelpers.SuspendIfSuspensionNotAborted();
+                }
+            }
+
+            // Get the result from the awaiter, or throw the exception stored in the Task
+            return awaiter.GetResult();
+
+        }
+
+        // TODO, this method should be marked so that it is only callable from a runtime async method
+        public static void UnsafeAwaitAwaiterFromRuntimeAsync<TAwaiter>(TAwaiter awaiter) where TAwaiter : ICriticalNotifyCompletion2
+        {
+            if (!awaiter.IsCompleted)
+            {
+                // Create resumption delegate, wrapping task, and create tasklets to represent each stack frame on the stack.
+                // RuntimeTaskSuspender.GetOrCreateResumptionDelegate() works like a POSIX fork call in that calls to it will return a
+                // delegate if they are the initial call to GetOrCreateResumptionDelegate, but once the thread is resumed,
+                // it will resume with a return value of null.
+                Action? resumption = RuntimeHelpers.GetOrCreateResumptionDelegate();
+                if (resumption != null)
+                {
+                    // We are trying to suspend
+                    bool threwException = true;
+                    try
+                    {
+                        // Call the UnsafeOnCompleted api under a try block, as registering the suspension may cause
+                        // an exception to occur.
+                        awaiter.UnsafeOnCompleted(resumption);
+                        threwException = false;
+                    }
+                    finally
+                    {
+                        // If UnsafeOnCompleted itself threw, we should bubble the error up, but we need
+                        // to destroy any allocated tasklets that were created as part of the GetOrCreateResumptionDelegate api
+                        // as that state will never be useable.
+                        if (threwException)
+                            RuntimeHelpers.AbortSuspend();
+                    }
+                    // If we reach here, the only way that we actually run follow on code is for the continuation to actually run,
+                    // and return from GetOrCreateResumptionDelegate with a null return value.
+                    RuntimeHelpers.SuspendIfSuspensionNotAborted();
+                }
+            }
+
+            // Get the result from the awaiter, or throw the exception stored in the Task
+            awaiter.GetResult();
+        }
+
+        // TODO, this method should be marked so that it is only callable from a runtime async method
+        public static void AwaitAwaiterFromRuntimeAsync<TAwaiter>(TAwaiter awaiter) where TAwaiter : INotifyCompletion2
+        {
+            if (!awaiter.IsCompleted)
+            {
+                // Create resumption delegate, wrapping task, and create tasklets to represent each stack frame on the stack.
+                // RuntimeTaskSuspender.GetOrCreateResumptionDelegate() works like a POSIX fork call in that calls to it will return a
+                // delegate if they are the initial call to GetOrCreateResumptionDelegate, but once the thread is resumed,
+                // it will resume with a return value of null.
+                Action? resumption = RuntimeHelpers.GetOrCreateResumptionDelegate();
+                if (resumption != null)
+                {
+                    // We are trying to suspend
+                    bool threwException = true;
+                    try
+                    {
+                        // Call the OnCompleted api under a try block, as registering the suspension may cause
+                        // an exception to occur.
+                        awaiter.OnCompleted(resumption);
+                        threwException = false;
+                    }
+                    finally
+                    {
+                        // If OnCompleted itself threw, we should bubble the error up, but we need
+                        // to destroy any allocated tasklets that were created as part of the GetOrCreateResumptionDelegate api
+                        // as that state will never be useable.
+                        if (threwException)
+                            RuntimeHelpers.AbortSuspend();
+                    }
+                    // If we reach here, the only way that we actually run follow on code is for the continuation to actually run,
+                    // and return from GetOrCreateResumptionDelegate with a null return value.
+                    RuntimeHelpers.SuspendIfSuspensionNotAborted();
+                }
+            }
+
+            // Get the result from the awaiter, or throw the exception stored in the Task
+            awaiter.GetResult();
+        }
+
+        private static void SuspendIfSuspensionNotAborted() {}
+        private static Action? GetOrCreateResumptionDelegate() { return null; }
+        private static void AbortSuspend() {}
     }
 }
diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/TaskAwaiter.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/TaskAwaiter.cs
index 0e3f0ec71b6b90..8a5b77ee05c9bc 100644
--- a/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/TaskAwaiter.cs
+++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/TaskAwaiter.cs
@@ -17,7 +17,7 @@
 namespace System.Runtime.CompilerServices
 {
     /// <summary>Provides an awaiter for awaiting a <see cref="Task"/>.</summary>
-    public readonly struct TaskAwaiter : ICriticalNotifyCompletion, ITaskAwaiter
+    public readonly struct TaskAwaiter : ICriticalNotifyCompletion2, ITaskAwaiter
     {
         // WARNING: Unsafe.As is used to access the generic TaskAwaiter<> as TaskAwaiter.
         // Its layout must remain the same.
@@ -283,7 +283,7 @@ private static Action OutputWaitEtwEvents(Task task, Action continuation)
     }
 
     /// <summary>Provides an awaiter for awaiting a <see cref="Task{TResult}"/>.</summary>
-    public readonly struct TaskAwaiter<TResult> : ICriticalNotifyCompletion, ITaskAwaiter
+    public readonly struct TaskAwaiter<TResult> : ICriticalNotifyCompletion2<TResult>, ITaskAwaiter
     {
         // WARNING: Unsafe.As is used to access TaskAwaiter<> as the non-generic TaskAwaiter.
         // Its layout must remain the same.
@@ -376,7 +376,7 @@ public ConfiguredTaskAwaiter GetAwaiter()
 
         /// <summary>Provides an awaiter for a <see cref="ConfiguredTaskAwaitable"/>.</summary>
         /// <remarks>This type is intended for compiler use only.</remarks>
-        public readonly struct ConfiguredTaskAwaiter : ICriticalNotifyCompletion, IConfiguredTaskAwaiter
+        public readonly struct ConfiguredTaskAwaiter : ICriticalNotifyCompletion2, IConfiguredTaskAwaiter
         {
             // WARNING: Unsafe.As is used to access the generic ConfiguredTaskAwaiter as this.
             // Its layout must remain the same.
@@ -458,7 +458,7 @@ public ConfiguredTaskAwaiter GetAwaiter()
 
         /// <summary>Provides an awaiter for a <see cref="ConfiguredTaskAwaitable{TResult}"/>.</summary>
         /// <remarks>This type is intended for compiler use only.</remarks>
-        public readonly struct ConfiguredTaskAwaiter : ICriticalNotifyCompletion, IConfiguredTaskAwaiter
+        public readonly struct ConfiguredTaskAwaiter : ICriticalNotifyCompletion2<TResult>, IConfiguredTaskAwaiter
         {
             // WARNING: Unsafe.As is used to access this as the non-generic ConfiguredTaskAwaiter.
             // Its layout must remain the same.
diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/ValueTaskAwaiter.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/ValueTaskAwaiter.cs
index c242ad3f2ad203..ba5aa6f10872a7 100644
--- a/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/ValueTaskAwaiter.cs
+++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/ValueTaskAwaiter.cs
@@ -9,7 +9,7 @@
 namespace System.Runtime.CompilerServices
 {
     /// <summary>Provides an awaiter for a <see cref="ValueTask"/>.</summary>
-    public readonly struct ValueTaskAwaiter : ICriticalNotifyCompletion, IStateMachineBoxAwareAwaiter
+    public readonly struct ValueTaskAwaiter : ICriticalNotifyCompletion2, IStateMachineBoxAwareAwaiter
     {
         /// <summary>Shim used to invoke an <see cref="Action"/> passed as the state argument to a <see cref="Action{Object}"/>.</summary>
         internal static readonly Action<object?> s_invokeActionDelegate = static state =>
@@ -104,7 +104,7 @@ void IStateMachineBoxAwareAwaiter.AwaitUnsafeOnCompleted(IAsyncStateMachineBox b
     }
 
     /// <summary>Provides an awaiter for a <see cref="ValueTask{TResult}"/>.</summary>
-    public readonly struct ValueTaskAwaiter<TResult> : ICriticalNotifyCompletion, IStateMachineBoxAwareAwaiter
+    public readonly struct ValueTaskAwaiter<TResult> : ICriticalNotifyCompletion2<TResult>, IStateMachineBoxAwareAwaiter
     {
         /// <summary>The value being awaited.</summary>
         private readonly ValueTask<TResult> _value;
diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/YieldAwaitable.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/YieldAwaitable.cs
index d9bea6410babbb..5ddd4fd00f3628 100644
--- a/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/YieldAwaitable.cs
+++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/YieldAwaitable.cs
@@ -43,7 +43,7 @@ public readonly struct YieldAwaitable
 
         /// <summary>Provides an awaiter that switches into a target environment.</summary>
         /// <remarks>This type is intended for compiler use only.</remarks>
-        public readonly struct YieldAwaiter : ICriticalNotifyCompletion, IStateMachineBoxAwareAwaiter
+        public readonly struct YieldAwaiter : ICriticalNotifyCompletion2, IStateMachineBoxAwareAwaiter
         {
             /// <summary>Gets whether a yield is not required.</summary>
             /// <remarks>This property is intended for compiler user rather than use directly in code.</remarks>
diff --git a/src/libraries/System.Runtime/ref/System.Runtime.cs b/src/libraries/System.Runtime/ref/System.Runtime.cs
index 5dd254b9701232..b7e9ba512a1ec1 100644
--- a/src/libraries/System.Runtime/ref/System.Runtime.cs
+++ b/src/libraries/System.Runtime/ref/System.Runtime.cs
@@ -12699,7 +12699,7 @@ public readonly partial struct ConfiguredTaskAwaitable
         private readonly object _dummy;
         private readonly int _dummyPrimitive;
         public System.Runtime.CompilerServices.ConfiguredTaskAwaitable.ConfiguredTaskAwaiter GetAwaiter() { throw null; }
-        public readonly partial struct ConfiguredTaskAwaiter : System.Runtime.CompilerServices.ICriticalNotifyCompletion, System.Runtime.CompilerServices.INotifyCompletion
+        public readonly partial struct ConfiguredTaskAwaiter : System.Runtime.CompilerServices.ICriticalNotifyCompletion2, System.Runtime.CompilerServices.INotifyCompletion
         {
             private readonly object _dummy;
             private readonly int _dummyPrimitive;
@@ -12714,7 +12714,7 @@ public readonly partial struct ConfiguredTaskAwaitable<TResult>
         private readonly object _dummy;
         private readonly int _dummyPrimitive;
         public System.Runtime.CompilerServices.ConfiguredTaskAwaitable<TResult>.ConfiguredTaskAwaiter GetAwaiter() { throw null; }
-        public readonly partial struct ConfiguredTaskAwaiter : System.Runtime.CompilerServices.ICriticalNotifyCompletion, System.Runtime.CompilerServices.INotifyCompletion
+        public readonly partial struct ConfiguredTaskAwaiter : System.Runtime.CompilerServices.ICriticalNotifyCompletion2<TResult>, System.Runtime.CompilerServices.INotifyCompletion
         {
             private readonly object _dummy;
             private readonly int _dummyPrimitive;
@@ -12729,7 +12729,7 @@ public readonly partial struct ConfiguredValueTaskAwaitable
         private readonly object _dummy;
         private readonly int _dummyPrimitive;
         public System.Runtime.CompilerServices.ConfiguredValueTaskAwaitable.ConfiguredValueTaskAwaiter GetAwaiter() { throw null; }
-        public readonly partial struct ConfiguredValueTaskAwaiter : System.Runtime.CompilerServices.ICriticalNotifyCompletion, System.Runtime.CompilerServices.INotifyCompletion
+        public readonly partial struct ConfiguredValueTaskAwaiter : System.Runtime.CompilerServices.ICriticalNotifyCompletion2, System.Runtime.CompilerServices.INotifyCompletion
         {
             private readonly object _dummy;
             private readonly int _dummyPrimitive;
@@ -12744,7 +12744,7 @@ public readonly partial struct ConfiguredValueTaskAwaitable<TResult>
         private readonly object _dummy;
         private readonly int _dummyPrimitive;
         public System.Runtime.CompilerServices.ConfiguredValueTaskAwaitable<TResult>.ConfiguredValueTaskAwaiter GetAwaiter() { throw null; }
-        public readonly partial struct ConfiguredValueTaskAwaiter : System.Runtime.CompilerServices.ICriticalNotifyCompletion, System.Runtime.CompilerServices.INotifyCompletion
+        public readonly partial struct ConfiguredValueTaskAwaiter : System.Runtime.CompilerServices.ICriticalNotifyCompletion2<TResult>, System.Runtime.CompilerServices.INotifyCompletion
         {
             private readonly object _dummy;
             private readonly int _dummyPrimitive;
@@ -12859,6 +12859,16 @@ public partial interface ICriticalNotifyCompletion : System.Runtime.CompilerServ
     {
         void UnsafeOnCompleted(System.Action continuation);
     }
+    public partial interface ICriticalNotifyCompletion2 : ICriticalNotifyCompletion
+    {
+        bool IsCompleted { get; }
+        void GetResult();
+    }
+    public partial interface ICriticalNotifyCompletion2<TResult> : ICriticalNotifyCompletion
+    {
+        bool IsCompleted { get; }
+        TResult GetResult();
+    }
     [System.AttributeUsageAttribute(System.AttributeTargets.Property, Inherited=true)]
     public sealed partial class IndexerNameAttribute : System.Attribute
     {
@@ -12868,6 +12878,16 @@ public partial interface INotifyCompletion
     {
         void OnCompleted(System.Action continuation);
     }
+    public partial interface INotifyCompletion2 : INotifyCompletion
+    {
+        bool IsCompleted { get; }
+        void GetResult();
+    }
+    public partial interface INotifyCompletion2<TResult> : INotifyCompletion
+    {
+        bool IsCompleted { get; }
+        TResult GetResult();
+    }
     [System.AttributeUsageAttribute(System.AttributeTargets.Assembly, AllowMultiple=true, Inherited=false)]
     public sealed partial class InternalsVisibleToAttribute : System.Attribute
     {
@@ -13109,6 +13129,10 @@ public static void RunModuleConstructor(System.ModuleHandle module) { }
         public static bool TryEnsureSufficientExecutionStack() { throw null; }
         public delegate void CleanupCode(object? userData, bool exceptionThrown);
         public delegate void TryCode(object? userData);
+        public static TResult UnsafeAwaitAwaiterFromRuntimeAsync<TResult, TAwaiter>(TAwaiter awaiter) where TAwaiter : ICriticalNotifyCompletion2<TResult> { throw null; }
+        public static void UnsafeAwaitAwaiterFromRuntimeAsync<TAwaiter>(TAwaiter awaiter) where TAwaiter : ICriticalNotifyCompletion2 { }
+        public static TResult AwaitAwaiterFromRuntimeAsync<TResult, TAwaiter>(TAwaiter awaiter) where TAwaiter : INotifyCompletion2<TResult> { throw null; }
+        public static void AwaitAwaiterFromRuntimeAsync<TAwaiter>(TAwaiter awaiter) where TAwaiter : INotifyCompletion2 { }
     }
     public sealed partial class RuntimeWrappedException : System.Exception
     {
@@ -13171,7 +13195,7 @@ public SwitchExpressionException(string? message, System.Exception? innerExcepti
         [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)]
         public override void GetObjectData(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) { }
     }
-    public readonly partial struct TaskAwaiter : System.Runtime.CompilerServices.ICriticalNotifyCompletion, System.Runtime.CompilerServices.INotifyCompletion
+    public readonly partial struct TaskAwaiter : System.Runtime.CompilerServices.ICriticalNotifyCompletion2, System.Runtime.CompilerServices.INotifyCompletion
     {
         private readonly object _dummy;
         private readonly int _dummyPrimitive;
@@ -13180,7 +13204,7 @@ public void GetResult() { }
         public void OnCompleted(System.Action continuation) { }
         public void UnsafeOnCompleted(System.Action continuation) { }
     }
-    public readonly partial struct TaskAwaiter<TResult> : System.Runtime.CompilerServices.ICriticalNotifyCompletion, System.Runtime.CompilerServices.INotifyCompletion
+    public readonly partial struct TaskAwaiter<TResult> : System.Runtime.CompilerServices.ICriticalNotifyCompletion2<TResult>, System.Runtime.CompilerServices.INotifyCompletion
     {
         private readonly object _dummy;
         private readonly int _dummyPrimitive;
@@ -13297,7 +13321,7 @@ public sealed partial class UnsafeValueTypeAttribute : System.Attribute
     {
         public UnsafeValueTypeAttribute() { }
     }
-    public readonly partial struct ValueTaskAwaiter : System.Runtime.CompilerServices.ICriticalNotifyCompletion, System.Runtime.CompilerServices.INotifyCompletion
+    public readonly partial struct ValueTaskAwaiter : System.Runtime.CompilerServices.ICriticalNotifyCompletion2, System.Runtime.CompilerServices.INotifyCompletion
     {
         private readonly object _dummy;
         private readonly int _dummyPrimitive;
@@ -13306,7 +13330,7 @@ public void GetResult() { }
         public void OnCompleted(System.Action continuation) { }
         public void UnsafeOnCompleted(System.Action continuation) { }
     }
-    public readonly partial struct ValueTaskAwaiter<TResult> : System.Runtime.CompilerServices.ICriticalNotifyCompletion, System.Runtime.CompilerServices.INotifyCompletion
+    public readonly partial struct ValueTaskAwaiter<TResult> : System.Runtime.CompilerServices.ICriticalNotifyCompletion2<TResult>, System.Runtime.CompilerServices.INotifyCompletion
     {
         private readonly object _dummy;
         private readonly int _dummyPrimitive;
@@ -13318,7 +13342,7 @@ public void UnsafeOnCompleted(System.Action continuation) { }
     public readonly partial struct YieldAwaitable
     {
         public System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter GetAwaiter() { throw null; }
-        public readonly partial struct YieldAwaiter : System.Runtime.CompilerServices.ICriticalNotifyCompletion, System.Runtime.CompilerServices.INotifyCompletion
+        public readonly partial struct YieldAwaiter : System.Runtime.CompilerServices.ICriticalNotifyCompletion2, System.Runtime.CompilerServices.INotifyCompletion
         {
             public bool IsCompleted { get { throw null; } }
             public void GetResult() { }
diff --git a/src/tests/Loader/async/runtimetask-asyncfibonacci-with-yields.il b/src/tests/Loader/async/runtimetask-asyncfibonacci-with-yields.il
index 2d8008483407e1..bfe9aece7c9a77 100644
--- a/src/tests/Loader/async/runtimetask-asyncfibonacci-with-yields.il
+++ b/src/tests/Loader/async/runtimetask-asyncfibonacci-with-yields.il
@@ -362,8 +362,8 @@
   IL_0013:  stloc.1
   IL_0014:  ldloc.1
   IL_0015:  brfalse.s  IL_001d
-  IL_0017:  call       int modopt([System.Runtime]System.Threading.Tasks.Task`1) Program::'<<Main>$>g__MyYield|0_2'()
-  IL_001c:  pop
+  IL_0017:  call       valuetype [System.Runtime]System.Runtime.CompilerServices.YieldAwaitable [System.Runtime]System.Threading.Tasks.Task::Yield()
+  IL_001c:  call       void [System.Runtime]System.Runtime.CompilerServices.RuntimeHelpers::UnsafeAwaitAwaiterFromRuntimeAsync<valuetype [System.Runtime]System.Runtime.CompilerServices.YieldAwaitable>(!!0)
   IL_001d:  ldloc.0
   IL_001e:  ldc.i4     0x773593ed
   IL_0023:  mul
@@ -374,8 +374,8 @@
   IL_002d:  stloc.2
   IL_002e:  ldloc.2
   IL_002f:  brfalse.s  IL_0037
-  IL_0031:  call       int modopt([System.Runtime]System.Threading.Tasks.Task`1) Program::'<<Main>$>g__MyYield|0_2'()
-  IL_0036:  pop
+  IL_0031:  call       valuetype [System.Runtime]System.Runtime.CompilerServices.YieldAwaitable [System.Runtime]System.Threading.Tasks.Task::Yield()
+  IL_0036:  call       void [System.Runtime]System.Runtime.CompilerServices.RuntimeHelpers::UnsafeAwaitAwaiterFromRuntimeAsync<valuetype [System.Runtime]System.Runtime.CompilerServices.YieldAwaitable>(!!0)
   IL_0037:  ldloc.0
   IL_0038:  ldc.i4     0x773593ed
   IL_003d:  mul
@@ -386,8 +386,8 @@
   IL_0047:  stloc.3
   IL_0048:  ldloc.3
   IL_0049:  brfalse.s  IL_0051
-  IL_004b:  call       int modopt([System.Runtime]System.Threading.Tasks.Task`1) Program::'<<Main>$>g__MyYield|0_2'()
-  IL_0050:  pop
+  IL_004b:  call       valuetype [System.Runtime]System.Runtime.CompilerServices.YieldAwaitable [System.Runtime]System.Threading.Tasks.Task::Yield()
+  IL_0050:  call       void [System.Runtime]System.Runtime.CompilerServices.RuntimeHelpers::UnsafeAwaitAwaiterFromRuntimeAsync<valuetype [System.Runtime]System.Runtime.CompilerServices.YieldAwaitable>(!!0)
   IL_0051:  ldloc.0
   IL_0052:  stloc.s    V_4
   IL_0054:  br.s       IL_0056
@@ -395,138 +395,6 @@
   IL_0058:  ret
   } // end of method Program::'<<Main>$>g__B|0_1'
 
-.class auto ansi sealed nested private beforefieldinit '<<<Main>$>g__MyYield|0_2>d'
-       extends [System.Runtime]System.ValueType
-       implements [System.Runtime]System.Runtime.CompilerServices.IAsyncStateMachine
-{
-  .custom instance void [System.Runtime]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) 
-  .field public int32 '<>1__state'
-  .field public valuetype [System.Runtime]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<int32> '<>t__builder'
-  .field private valuetype [System.Runtime]System.Runtime.CompilerServices.YieldAwaitable/YieldAwaiter '<>u__1'
-
-.method private hidebysig newslot virtual final 
-        instance void  MoveNext() cil managed
-{
-  .override [System.Runtime]System.Runtime.CompilerServices.IAsyncStateMachine::MoveNext
-  // Code size       150 (0x96)
-  .maxstack  3
-  .locals init (int32 V_0,
-           int32 V_1,
-           valuetype [System.Runtime]System.Runtime.CompilerServices.YieldAwaitable/YieldAwaiter V_2,
-           valuetype [System.Runtime]System.Runtime.CompilerServices.YieldAwaitable V_3,
-           class [System.Runtime]System.Exception V_4)
-  IL_0000:  ldarg.0
-  IL_0001:  ldfld      int32 Program/'<<<Main>$>g__MyYield|0_2>d'::'<>1__state'
-  IL_0006:  stloc.0
-  .try
-  {
-    IL_0007:  ldloc.0
-    IL_0008:  brfalse.s  IL_0041
-    IL_000a:  call       valuetype [System.Runtime]System.Runtime.CompilerServices.YieldAwaitable [System.Runtime]System.Threading.Tasks.Task::Yield()
-    IL_000f:  stloc.3
-    IL_0010:  ldloca.s   V_3
-    IL_0012:  call       instance valuetype [System.Runtime]System.Runtime.CompilerServices.YieldAwaitable/YieldAwaiter [System.Runtime]System.Runtime.CompilerServices.YieldAwaitable::GetAwaiter()
-    IL_0017:  stloc.2
-    IL_0018:  ldloca.s   V_2
-    IL_001a:  call       instance bool [System.Runtime]System.Runtime.CompilerServices.YieldAwaitable/YieldAwaiter::get_IsCompleted()
-    IL_001f:  brtrue.s   IL_005d
-    IL_0021:  ldarg.0
-    IL_0022:  ldc.i4.0
-    IL_0023:  dup
-    IL_0024:  stloc.0
-    IL_0025:  stfld      int32 Program/'<<<Main>$>g__MyYield|0_2>d'::'<>1__state'
-    IL_002a:  ldarg.0
-    IL_002b:  ldloc.2
-    IL_002c:  stfld      valuetype [System.Runtime]System.Runtime.CompilerServices.YieldAwaitable/YieldAwaiter Program/'<<<Main>$>g__MyYield|0_2>d'::'<>u__1'
-    IL_0031:  ldarg.0
-    IL_0032:  ldflda     valuetype [System.Runtime]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<int32> Program/'<<<Main>$>g__MyYield|0_2>d'::'<>t__builder'
-    IL_0037:  ldloca.s   V_2
-    IL_0039:  ldarg.0
-    IL_003a:  call       instance void valuetype [System.Runtime]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<int32>::AwaitUnsafeOnCompleted<valuetype [System.Runtime]System.Runtime.CompilerServices.YieldAwaitable/YieldAwaiter,valuetype Program/'<<<Main>$>g__MyYield|0_2>d'>(!!0&,
-                                                                                                                                                                                                                                                                                               !!1&)
-    IL_003f:  leave.s    IL_0095
-    IL_0041:  ldarg.0
-    IL_0042:  ldfld      valuetype [System.Runtime]System.Runtime.CompilerServices.YieldAwaitable/YieldAwaiter Program/'<<<Main>$>g__MyYield|0_2>d'::'<>u__1'
-    IL_0047:  stloc.2
-    IL_0048:  ldarg.0
-    IL_0049:  ldflda     valuetype [System.Runtime]System.Runtime.CompilerServices.YieldAwaitable/YieldAwaiter Program/'<<<Main>$>g__MyYield|0_2>d'::'<>u__1'
-    IL_004e:  initobj    [System.Runtime]System.Runtime.CompilerServices.YieldAwaitable/YieldAwaiter
-    IL_0054:  ldarg.0
-    IL_0055:  ldc.i4.m1
-    IL_0056:  dup
-    IL_0057:  stloc.0
-    IL_0058:  stfld      int32 Program/'<<<Main>$>g__MyYield|0_2>d'::'<>1__state'
-    IL_005d:  ldloca.s   V_2
-    IL_005f:  call       instance void [System.Runtime]System.Runtime.CompilerServices.YieldAwaitable/YieldAwaiter::GetResult()
-    IL_0064:  ldc.i4.0
-    IL_0065:  stloc.1
-    IL_0066:  leave.s    IL_0081
-  }  // end .try
-  catch [System.Runtime]System.Exception 
-  {
-    IL_0068:  stloc.s    V_4
-    IL_006a:  ldarg.0
-    IL_006b:  ldc.i4.s   -2
-    IL_006d:  stfld      int32 Program/'<<<Main>$>g__MyYield|0_2>d'::'<>1__state'
-    IL_0072:  ldarg.0
-    IL_0073:  ldflda     valuetype [System.Runtime]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<int32> Program/'<<<Main>$>g__MyYield|0_2>d'::'<>t__builder'
-    IL_0078:  ldloc.s    V_4
-    IL_007a:  call       instance void valuetype [System.Runtime]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<int32>::SetException(class [System.Runtime]System.Exception)
-    IL_007f:  leave.s    IL_0095
-  }  // end handler
-  IL_0081:  ldarg.0
-  IL_0082:  ldc.i4.s   -2
-  IL_0084:  stfld      int32 Program/'<<<Main>$>g__MyYield|0_2>d'::'<>1__state'
-  IL_0089:  ldarg.0
-  IL_008a:  ldflda     valuetype [System.Runtime]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<int32> Program/'<<<Main>$>g__MyYield|0_2>d'::'<>t__builder'
-  IL_008f:  ldloc.1
-  IL_0090:  call       instance void valuetype [System.Runtime]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<int32>::SetResult(!0)
-  IL_0095:  ret
-} // end of method '<<<Main>$>g__MyYield|0_2>d'::MoveNext
-.method private hidebysig newslot virtual final 
-        instance void  SetStateMachine(class [System.Runtime]System.Runtime.CompilerServices.IAsyncStateMachine stateMachine) cil managed
-{
-  .custom instance void [System.Runtime]System.Diagnostics.DebuggerHiddenAttribute::.ctor() = ( 01 00 00 00 ) 
-  .param [1]
-  .custom instance void System.Runtime.CompilerServices.NullableAttribute::.ctor(uint8) = ( 01 00 01 00 00 ) 
-  .override [System.Runtime]System.Runtime.CompilerServices.IAsyncStateMachine::SetStateMachine
-  // Code size       13 (0xd)
-  .maxstack  8
-  IL_0000:  ldarg.0
-  IL_0001:  ldflda     valuetype [System.Runtime]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<int32> Program/'<<<Main>$>g__MyYield|0_2>d'::'<>t__builder'
-  IL_0006:  ldarg.1
-  IL_0007:  call       instance void valuetype [System.Runtime]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<int32>::SetStateMachine(class [System.Runtime]System.Runtime.CompilerServices.IAsyncStateMachine)
-  IL_000c:  ret
-} // end of method '<<<Main>$>g__MyYield|0_2>d'::SetStateMachine
-} // end of class '<<<Main>$>g__MyYield|0_2>d'
-
-.method assembly hidebysig static class [System.Runtime]System.Threading.Tasks.Task`1<int32> 
-        '<<Main>$>g__MyYield|0_2'() cil managed
-{
-  .custom instance void System.Runtime.CompilerServices.NullableContextAttribute::.ctor(uint8) = ( 01 00 01 00 00 ) 
-  .custom instance void [System.Runtime]System.Runtime.CompilerServices.AsyncStateMachineAttribute::.ctor(class [System.Runtime]System.Type) = ( 01 00 22 50 72 6F 67 72 61 6D 2B 3C 3C 3C 4D 61   // .."Program+<<<Ma
-                                                                                                                                                 69 6E 3E 24 3E 67 5F 5F 4D 79 59 69 65 6C 64 7C   // in>$>g__MyYield|
-                                                                                                                                                 30 5F 32 3E 64 00 00 )                            // 0_2>d..
-  .custom instance void [System.Runtime]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) 
-  // Code size       47 (0x2f)
-  .maxstack  2
-  .locals init (valuetype Program/'<<<Main>$>g__MyYield|0_2>d' V_0)
-  IL_0000:  ldloca.s   V_0
-  IL_0002:  call       valuetype [System.Runtime]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<!0> valuetype [System.Runtime]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<int32>::Create()
-  IL_0007:  stfld      valuetype [System.Runtime]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<int32> Program/'<<<Main>$>g__MyYield|0_2>d'::'<>t__builder'
-  IL_000c:  ldloca.s   V_0
-  IL_000e:  ldc.i4.m1
-  IL_000f:  stfld      int32 Program/'<<<Main>$>g__MyYield|0_2>d'::'<>1__state'
-  IL_0014:  ldloca.s   V_0
-  IL_0016:  ldflda     valuetype [System.Runtime]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<int32> Program/'<<<Main>$>g__MyYield|0_2>d'::'<>t__builder'
-  IL_001b:  ldloca.s   V_0
-  IL_001d:  call       instance void valuetype [System.Runtime]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<int32>::Start<valuetype Program/'<<<Main>$>g__MyYield|0_2>d'>(!!0&)
-  IL_0022:  ldloca.s   V_0
-  IL_0024:  ldflda     valuetype [System.Runtime]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<int32> Program/'<<<Main>$>g__MyYield|0_2>d'::'<>t__builder'
-  IL_0029:  call       instance class [System.Runtime]System.Threading.Tasks.Task`1<!0> valuetype [System.Runtime]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<int32>::get_Task()
-  IL_002e:  ret
-} // end of method Program::'<<Main>$>g__MyYield|0_2'
-
 } // end of class Program
 
 

From 38b2087687b478d14255af963a03255e842815ad Mon Sep 17 00:00:00 2001
From: David Wrighton <davidwr@microsoft.com>
Date: Wed, 27 Sep 2023 14:33:30 -0700
Subject: [PATCH 016/203] Runtime handled tasks markdown for experiment

---
 docs/design/features/runtime-handled-tasks.md | 244 ++++++++++++++++++
 1 file changed, 244 insertions(+)
 create mode 100644 docs/design/features/runtime-handled-tasks.md

diff --git a/docs/design/features/runtime-handled-tasks.md b/docs/design/features/runtime-handled-tasks.md
new file mode 100644
index 00000000000000..49bc2983affb01
--- /dev/null
+++ b/docs/design/features/runtime-handled-tasks.md
@@ -0,0 +1,244 @@
+# Runtime Handled Tasks Experiment
+
+The .NET Runtime has historically supported a programming model of developing async programs through use of the System.Threading.Tasks library provided by the runtime as well as the Roslyn async method support, which interacts with that tasks library to make it simple to write the callback based state machines that the runtime can use. Over the years several attempts have been made to optimize the performance and other characteristics of this scheme, but fundamentally those arrangements have relied on either tweaks to the core library's code base or adjustments to the IL code generated. This experiment seeks to identify what improvements to the async model can be achieved by moving the state machine computation and handling to being handled internally in the runtime.
+
+## Experimental Goals
+
+1. Find out if runtime generated async state machines are substantially better in performance than the existing async state machine model
+2. Find out if we can implement a variation on async which has some subtle semantic behavior changes, but remains compatible with existing code which is written in the original style
+
+## Experimental Approach
+
+1. Develop new semantics which allow the runtime to implement state machines as part of the runtime instead of relying on the existing state machine generation.
+2. Develop 1 or more implementations of the runtime which implement said semantics
+3. Develop a version of the Roslyn compiler which is capable of building code using the new semantics
+4. Develop a set of microbenchmarks for measuring the different performance characteristics of the new model
+5. Develop a set of application scenario benchmarks
+
+Throughput this document, any references to the term async2 refer exclusively to the new model.
+
+## New Semantics
+
+This section is a work in progress, but describes the current expected semantic changes.
+
+### Specification of a method as following async2 semantics
+
+To specify that a method follows async2 rules, a custom modifier to either `System.Threading.Tasks.Task`, ``System.Threading.Tasks.Task`1``, `System.Threading.Tasks.ValueTask`, or ``System.Threading.Tasks.ValueTask`1`` shall be placed as the last custom modifier before the return type in the signature of a method.
+
+### Flowing `SyncronizationContext` and `ExecutionContext` and other behavior such as AsyncLocal
+Unlike traditional C# compiler generated async, async2 methods shall not save/restore `SyncronizationContext` and `ExecutionContext` at function boundaries. Instead, changes to them can be observed by their callers (as long as the caller is not a Task to async2 thunk).
+
+#### Integration with `SyncronizationContext` and resumption
+
+A new attribute `System.Runtime.CompilerServices.ConfigureAwaitAttribute` will be defined. It can be applied at the assembly/module/type/method level. It shall be defined as 
+```
+namespace System.Runtime.CompilerServices
+{
+    class ConfigureAwaitAttribute : Attribute
+    {
+        public ConfigureAwaitAttribute(bool continueOnCapturedContext) { ContinueOnCapturedContext = continueOnCapturedContext; }
+        public bool ContinueOnCapturedContext { get; }
+    }
+}
+```
+
+The behavior of this attribute shall be to apply configure await semantics for resuming suspended frames of execution. Notably, though, the semantics of exactly which `SynchronizationContext` to use is subtly different from that of traditional async. In traditional async since each async method saves/restores the `SynchronizationContext` an async method which suspended at an ConfigureAwait(true) await point awaiting async method `A` will always continue on the Synchronization context that existed before the call to async method `A`, where in the async2 model the context used will be that which is current just before calling into method `A`, but in the async2 model, the SynchronizationContext will be the context that was current just before the return statement of method `A`. In practice, we expect this to be a distinction without a difference, as all known intentional uses of `SyncrhonizationContext` are implemented via a pattern where the context is set in a function, and restored via a try/finally to its original state before returning from the function.
+
+#### Implications of flowing `ExecutionContext` and async locals
+
+`ExecutionContext` is used to represent the current values of the `AsyncLocal<T>`. This feature is colloquially known as async locals. The semantics of these locals is surprising to developers today, in that while any function can modify the current state of an aync local, if an async function returns, any mutations to the async local are lost. In contrast, this proposal changes to the model such that when an async2 function returns, the async local state is **not** reverted to its previous state.
+
+### Integration with the existing api surface of Task and ValueTask based apis
+
+Any MethodDef that is an async2 based method can also be called as a `Task`/`ValueTask` returning method. Likewise any MethodDef which returns `Task` or `ValueTask` can be called via an async2 entrypoint. In addition, MethodImpl records and virtual method override rules shall be adjusted so that if an interface or base type defines a virtual method using a signature of `Task`/`ValueTask` then it can be overriden via a method that is an async2 api. The same is true vice versa. For overrides implemented via `MethodImpl` the MethodImpl must describe the Body and Decl using signatures which are either both async2 or `Task`/`ValueTask` returning, but the MethodImpl will also apply to the other async variant. These overrides are permitted to be interleaved, so for example
+
+```
+class Base
+{
+    public virtual async Task<int> Method() { ... }
+}
+class Derived : Base
+{
+    public override async2 int Method() { ... }
+}
+
+class MoreDerived : Derived
+{
+    public override async Task<int> Method() { ... }
+}
+```
+
+Any attempt to call an entrypoint will resolve to either an implementation which matches the signature, or in case of an async variant mismatch, will resolve to a runtime generated thunk which will call into the developer provided implementation.
+
+Methods that return Task via generics do not follow this rule. For instance
+```
+class MyGeneric<T>
+{
+    T DoSomething() { ... }
+}
+```
+
+`MyGeneric<Task>.DoSomething()` is not callable via an async2 entrypoint.
+
+#### Thunk from an async2 api surface to Task based implementation
+
+A thunk from an async2 api surface to a Task based implementation will have the following psuedocode, if a legal thunk can be made.
+
+```
+async2 ReturnType ThunkAsync(ParameterType param1, ParameterType2 param2, ...)
+{
+    return RuntimeHelpers.UnsafeAwaitAwaiterFromRuntimeAsync<TaskAwaiter<ReturnType>, ReturnType>(TargetMethod(param1, param2, ...));
+}
+```
+
+If any of the parameter types to the target method are ref parameters, or ref structures, then the Thunk generated will be 
+```
+async2 ReturnType ThunkAsync(ParameterType param1, ParameterType2 param2, ...)
+{
+    throw new InvalidProgramException();
+}
+```
+
+#### Thunk from the Task api surface to async2 based implementation
+
+If any of the parameter types to the target method are ref parameters, or ref structures, then any thunk invoked will throw `InvalidProgramException`. Otherwise it will perform whatever runtime actions are necessary to transition to the async2 implementation, and return the appropriate Task object type on return.
+
+Thunks are required to save and restore the `ExecutionContext` and `SynchronizationContext` such that callers of thunks cannot observe changes to them.
+
+### Specifying custom scheduling of async2
+Async2 shall integrate with `TaskScheduler.Current`.
+
+### Semantics of async2 based IL code
+
+#### IL Semantics
+
+Async2 IL is broadly similar to the existing IL semantics with the following restrictions.
+
+1. Usage of the `localloc` instruction is forbidden
+3. The `ldloca` and `ldarga` instructions are redefined to return managed pointers instead of pointers.
+4. A pinning local cannot be validly used with a local variable.
+5. As an initial restriction that is not fundamental, the `tail.` prefix is not permitted
+
+Notably, the design permits byrefs and ref structs to be used within async2 methods.
+
+#### EH Semantics
+No call to a method with an async2 modreq will be permitted in a finally, fault, filter, or catch clause. If such a thing exists, the program is invalid.
+
+The StackTrace of a exception thrown within an async2 method shall include any async2 frames which are logically on the call stack up until the thunk which transfers execution to the root async2 method.
+
+### Api surface for interacting with non Task/ValueTask apis which async from within async2 code
+
+The runtime shall provide the following apis
+
+```
+        public interface ICriticalNotifyCompletion2 : ICriticalNotifyCompletion
+        {
+            bool IsCompleted { get; }
+            void GetResult();
+        }
+        public interface ICriticalNotifyCompletion2<TResult> : ICriticalNotifyCompletion
+        {
+            bool IsCompleted { get; }
+            TResult GetResult();
+        }
+        public interface INotifyCompletion2 : INotifyCompletion
+        {
+            bool IsCompleted { get; }
+            void GetResult();
+        }
+        public interface INotifyCompletion2<TResult> : INotifyCompletion
+        {
+            bool IsCompleted { get; }
+            TResult GetResult();
+        }
+
+        public static async2 void AwaitAwaiterFromRuntimeAsync<TAwaiter>(TAwaiter awaiter) where TAwaiter : INotifyCompletion2
+        public static async2 TResult AwaitAwaiterFromRuntimeAsync<TResult, TAwaiter>(TAwaiter awaiter) where TAwaiter : INotifyCompletion2<TResult>
+        public static async2 void UnsafeAwaitAwaiterFromRuntimeAsync<TAwaiter>(TAwaiter awaiter) where TAwaiter : ICriticalNotifyCompletion2
+        public static async2 TResult UnsafeAwaitAwaiterFromRuntimeAsync<TResult, TAwaiter>(TAwaiter awaiter) where TAwaiter : ICriticalNotifyCompletion2<TResult>
+
+```
+As well as define that `Task`/`ValueTask` and other awaiters provide awaiters provided implement the `INotifyCompletion2`/`ICriticalNotification2` interfaces. The AwaitAwaiterFromRuntimeAsync provides a means for code generators to await on anything that is awaitable today with a simple set of IL.
+
+### Utilizing async2 based code from non-async2 based code
+
+Non-async2 code is never permitted to directly invoke async2 code. Instead any and all calls into async2 functions shall be done by means of one of the thunks.
+
+### Interaction with Reflection
+
+Async2 methods will not be visible in reflection via the `Type.GetMethod`, `TypeInfo.DeclaredMethods`, `TypeInfo.DeclaredMembers` , or `Type.GetInterfaceMap()` apis. 
+
+`Type.GetMembers` and `Type.GetMethods` will be able to find the async2 methods if and only if the `BindingFlags.Async2Visible` flag is set.
+
+`Type.GetMemberWithSameMetadataDefinitionAs` will find the matching async variant method.
+
+### Modification of existing async infrastructure
+
+The only known modifications are to add the various new `INotifyCompletion2` and `INotifyCompletion3` interfaces to the awaitable types in the core libraries. 
+
+## Implementation of async2 design 1
+
+TBD, design in progress... there are some notes, but this is very incomplete at this moment.
+
+The general design is to leverage the notion that a normal code generated function at a function call point is effectively a state machine. The index for resumption is the IP that returning to the function will set, and the stackframe + saved registers are the current state of the state machine. I believe we can achieve performance comparable to synchronous code with this scheme for non-suspended scenarios, and we may achieve acceptable overall performance as an async mechanism if suspension is relatively rare.
+
+#### Thunk from the Task api surface to async2 based implementation
+
+A thunk from the Task api surface to an async2 based implementation will have the following psuedocode if it is not required to throw `InvalidProgramException`
+
+```
+Task<ReturnType> ThunkAsync(ParameterType param1, ParameterType2 param2, ...)
+{
+    // A variant on this helper type will be defined for each type of `Task`/`ValueTask`/`Task<TResult>`/`ValueTask<TResult>`
+    System.Runtime.CompilerServices.RuntimeTaskState<ReturnType> runtimeTaskState = new;
+
+    // If the Task is parameterized there shall be a return type to work with.
+    ReturnType result;
+
+    runtimeTaskState.Push();
+    try
+    {
+        try
+        {
+            // NOTE there is a special case for instance methods on valuetypes. For that case,
+            // the thunk will construct a boxed instance of the this value, and then invoke the method
+            // on that.
+            result = TargetMethod(param1, param2, ...);
+        }
+        catch (Exception ex)
+        {
+            return runtimeTaskState.FromException(ex);
+        }
+        return runtimeTaskState.FromResult(result);
+    }
+    finally
+    {
+        runtimeTaskState.Pop();
+    }
+}
+```
+
+## C# Language changes
+
+TBD
+
+Major identified concerns are
+
+1. What about code which today looks like `public async Task<Task<Task<int>>> Method()` What would the new syntax be?
+2. It is desirable to be able to implement methods that return `ValueTask` as well as just `Task` with an async2 method. How do we distinguish?
+3. There is an existing concept for `async void` methods. Does that model need to exist in the new system.
+
+## Microbenchmarks
+
+1. Async computation of fibonacci via recursive algorithm, with and without various amounts of yielding
+2. Suspension with stacks of various depths
+3. Suspension with stacks where the operation of the task operates in a sawtooth pattern. For instance, suspend first at depth 20, then reach depth 30, return to depth 10 and suspend, reach depth 30, return to depth 10 and suspend, etc. Measure the impact of suspending with greater and lesser depths.
+4. Measure the performance of thunks to async2 functions which do effectively nothing.
+5. Benchmark the effect of long lived suspended state vs short lived suspended state
+6. Benchmark the performance of EH at various depths.
+
+## Scenario benchmarks
+
+1. Serialization/Deserialization of JSON data to an asynchronous stream
+2. ASP.NET servicing of requests where must of the pipeline is converted to the async2 model
+

From b7b00ded3d7bc6d1f909d837a19ffc7a8b02e77c Mon Sep 17 00:00:00 2001
From: David Wrighton <davidwr@microsoft.com>
Date: Wed, 27 Sep 2023 17:06:52 -0700
Subject: [PATCH 017/203] Writeup managed side of runtime driven suspension.
 Need to write details into doc, but managed code may have the right logic  :)

---
 docs/design/features/runtime-handled-tasks.md |   7 +
 .../CompilerServices/RuntimeHelpers.cs        | 306 +++++++++++++++++-
 .../src/System/Threading/Tasks/Future.cs      |  19 +-
 3 files changed, 326 insertions(+), 6 deletions(-)

diff --git a/docs/design/features/runtime-handled-tasks.md b/docs/design/features/runtime-handled-tasks.md
index 49bc2983affb01..64accff939822f 100644
--- a/docs/design/features/runtime-handled-tasks.md
+++ b/docs/design/features/runtime-handled-tasks.md
@@ -182,6 +182,13 @@ TBD, design in progress... there are some notes, but this is very incomplete at
 
 The general design is to leverage the notion that a normal code generated function at a function call point is effectively a state machine. The index for resumption is the IP that returning to the function will set, and the stackframe + saved registers are the current state of the state machine. I believe we can achieve performance comparable to synchronous code with this scheme for non-suspended scenarios, and we may achieve acceptable overall performance as an async mechanism if suspension is relatively rare.
 
+#### JIT Code changes
+1. Do not allow InlinedCallFrames to be linked to the frame chain across a suspend (Or disable p/invoke inlining in these methods)
+2. Report frame pointers as ByRef
+3. Report all pointers to locals as ByRef
+4. (If possible) tweak codegen for return processing to not use the address returned in RAX when doing byref returns
+5. Force the AwaitAwaiterFromRuntimeAsync to be treated as an async2 method even though it isn't marked as such (due to compiler limitations)
+
 #### Thunk from the Task api surface to async2 based implementation
 
 A thunk from the Task api surface to an async2 based implementation will have the following psuedocode if it is not required to throw `InvalidProgramException`
diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.cs
index 0665687bace867..b4530098b67919 100644
--- a/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.cs
+++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.cs
@@ -3,6 +3,8 @@
 
 using System.Runtime.InteropServices;
 using System.Reflection;
+using System.Threading;
+using System.Threading.Tasks;
 
 namespace System.Runtime.CompilerServices
 {
@@ -273,8 +275,306 @@ public static void AwaitAwaiterFromRuntimeAsync<TAwaiter>(TAwaiter awaiter) wher
             awaiter.GetResult();
         }
 
-        private static void SuspendIfSuspensionNotAborted() {}
-        private static Action? GetOrCreateResumptionDelegate() { return null; }
-        private static void AbortSuspend() {}
+        [ThreadStatic]
+        private static unsafe void* t_asyncData;
+
+        internal struct RuntimeAsyncReturnValue
+        {
+            public RuntimeAsyncReturnValue(object? obj)
+            {
+                _obj = obj;
+                _ptr = IntPtr.Zero;
+                _isObj = true;
+            }
+            public RuntimeAsyncReturnValue(IntPtr ptr)
+            {
+                _obj = null;
+                _ptr = ptr;
+                _isObj = false;
+            }
+            public IntPtr _ptr;
+            public object? _obj;
+            public bool _isObj;
+        }
+
+        internal abstract unsafe class RuntimeAsyncMaintainedData
+        {
+            public Action? _resumption;
+            public Exception? _exception;
+            public bool _suspendActive;
+            public bool _initialTaskEntry = true;
+            public bool _completed;
+            public byte _dummy;
+            public bool _abortSuspend;
+
+            public Tasklet *_nextTasklet;
+            public Tasklet* _oldTaskletNext;
+
+            public RuntimeAsyncReturnValue _retValue;
+            public virtual ref byte GetReturnPointer() { return ref _dummy; }
+
+            public Task? _task;
+            public abstract Task GetTask();
+        }
+
+
+        // These are all implemented by the same assembly helper that will setup the tasklet in its new home on the stack
+        // and then tail-call into it. We will need a different entrypoint name for each type of register based return that can happen
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        internal static extern unsafe object ResumeTaskletReferenceReturn(Tasklet* pTasklet, ref RuntimeAsyncReturnValue retValue);
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        internal static extern unsafe IntPtr ResumeTaskletIntegerRegisterReturn(Tasklet* pTasklet, ref RuntimeAsyncReturnValue retValue);
+
+        internal sealed class RuntimeAsyncMaintainedData<T> : RuntimeAsyncMaintainedData, ICriticalNotifyCompletion
+        {
+            public RuntimeAsyncMaintainedData()
+            {
+                _task = CompletionTask();
+                _resumption = ResumptionFunc;
+            }
+
+            public unsafe void ResumptionFunc()
+            {
+                if (HasCurrentAsyncDataFrame() && GetCurrentAsyncDataFrame()._maintainedData == this)
+                {
+                    _abortSuspend = true;
+                    return;
+                }
+                // Once we perform a resumption we no longer need to worry about handling the ultimate return data from the run of Tasklets
+                _initialTaskEntry = false;
+
+                int collectiveStackAllocsPerformed = 0;
+
+                try
+                {
+                    AsyncDataFrame dataFrame = new AsyncDataFrame(this);
+                    PushAsyncData(ref dataFrame);
+                    try
+                    {
+                        while (_nextTasklet != null)
+                        {
+                            int maxStackNeeded = _nextTasklet->GetMaxStackNeeded();
+                            if (maxStackNeeded > collectiveStackAllocsPerformed)
+                            {
+#pragma warning disable CA2014
+                                // This won't stack overflow unless MaxStackNeeded is actually too high, as the extra allocation is controlled by collectiveStackAllocsPerformed
+                                // TODO This is doing terrible things with the ABI, so we may need to be more careful here
+                                int stackToAlloc = maxStackNeeded - collectiveStackAllocsPerformed;
+                                byte *pStackAlloc = stackalloc byte[stackToAlloc];
+                                collectiveStackAllocsPerformed += stackToAlloc;
+#pragma warning restore CA2014
+// The optimizer does nothing with variable sized StackAlloc KeepStackAllocAlive(pStackAlloc);
+                            }
+
+                            try
+                            {
+                                switch (_nextTasklet->taskletReturnType)
+                                {
+                                    case TaskletReturnType.ObjectReference:
+                                        _retValue = new RuntimeAsyncReturnValue(ResumeTaskletReferenceReturn(_nextTasklet, ref _retValue));
+                                        break;
+                                    case TaskletReturnType.Integer:
+                                        _retValue = new RuntimeAsyncReturnValue(ResumeTaskletIntegerRegisterReturn(_nextTasklet, ref _retValue));
+                                        break;
+                                    case TaskletReturnType.ByReference:
+                                        throw new NotImplementedException(); // This will be awkward (but not impossible) to implement. Hold off for now
+                                }
+                            }
+                            finally
+                            {
+                                DeleteTasklet(_nextTasklet);
+                            }
+                            _nextTasklet = _nextTasklet->pTaskletNextInStack;
+                        }
+                    }
+                    finally
+                    {
+                        PopAsyncData();
+                    }
+                }
+                catch (Exception e)
+                {
+                    SetException(e);
+                }
+            }
+
+            private Action? _taskResumer;
+            private T? _returnData;
+            public override ref byte GetReturnPointer()
+            {
+                return ref Unsafe.As<T, byte>(ref _returnData!);
+            }
+
+            public RuntimeAsyncMaintainedData<T> GetAwaiter()
+            {
+                return this;
+            }
+
+            public bool IsCompleted => _completed;
+
+            public override Task GetTask()
+            {
+                return _task!;
+            }
+
+            public void SetException(Exception exception)
+            {
+                _exception = exception;
+                _completed = true;
+                _taskResumer!();
+            }
+
+            public void SetResultDone()
+            {
+                _completed = true;
+                _taskResumer!();
+            }
+
+            public void OnCompleted(Action resumer) { throw new NotSupportedException(); }
+            public void UnsafeOnCompleted(Action resumer) { _taskResumer = resumer; }
+            public T? GetResult() { if (_exception != null) throw _exception; return _returnData; }
+
+            private async Task<T?> CompletionTask()
+            {
+                return await this;
+            }
+        }
+
+        internal unsafe struct AsyncDataFrame
+        {
+            public AsyncDataFrame(RuntimeAsyncMaintainedData maintainedData)
+            {
+                _maintainedData = maintainedData;
+                _crawlMark = StackCrawlMark.LookForMe;
+                _next = null;
+                _createRuntimeMaintainedData = null;
+            }
+
+            public AsyncDataFrame(Func<RuntimeAsyncMaintainedData> getMaintainedData)
+            {
+                _maintainedData = null;
+                _crawlMark = StackCrawlMark.LookForMe;
+                _next = null;
+                _createRuntimeMaintainedData = getMaintainedData;
+            }
+
+            public StackCrawlMark _crawlMark;
+            public void* _next;
+            public Func<RuntimeAsyncMaintainedData>? _createRuntimeMaintainedData;
+            public RuntimeAsyncMaintainedData? _maintainedData;
+        }
+
+        internal enum TaskletReturnType
+        {
+            // These return types are OS/architecture specific. For instance, Arm64 supports returning structs in a register pair
+            Integer,
+            ObjectReference,
+            ByReference
+        }
+        internal unsafe struct Tasklet
+        {
+            public Tasklet* pTaskletNextInStack;
+            public Tasklet* pTaskletNextInLiveList;
+            public Tasklet* pTaskletPrevInLiveList;
+            public byte* pStackData;
+            public byte* pStackDataInfo;
+            public int maxStackNeeded;
+            public TaskletReturnType taskletReturnType;
+
+            public int GetMaxStackNeeded() { return maxStackNeeded; }
+        }
+
+        internal static unsafe void PushAsyncData(ref AsyncDataFrame asyncData)
+        {
+            asyncData._next = t_asyncData;
+            t_asyncData = Unsafe.AsPointer(ref asyncData);
+        }
+
+        internal static unsafe void PopAsyncData()
+        {
+            t_asyncData = Unsafe.AsRef<AsyncDataFrame>(t_asyncData)._next;
+        }
+
+        internal static unsafe bool HasCurrentAsyncDataFrame()
+        {
+            return t_asyncData != null;
+        }
+
+        private static unsafe ref AsyncDataFrame GetCurrentAsyncDataFrame()
+        {
+            return ref Unsafe.AsRef<AsyncDataFrame>(t_asyncData);
+        }
+
+        // Capture stack into a series of tasklets (one per stack frame)
+        // These tasklets hold the stack data for a particular frame, as well as the contents of the saved registers as needed by that frame, GC data for reporting the frame, and data for restoring the frame.
+        // To make this work.
+        // 1. All addresses of locals are to be used as byrefs
+        // 2. Frame pointers are to be reported as byrefs
+        // 3. Return values are to be returned by reference in all cases where the return value is not a simple object return or return of a simple value in the return value register (this makes the resumption function reasonable to write. Notably, floating point, and ref return will be returned by reference as well as generalized struct return, and return which would normally involve multiple return value registers)
+        // 4. There are to be no refs to the outermost caller function exceptn for the valuetype return address (methods which begin on an instance valuetype will have the thunk box the valuetype and the runtime async method on the boxed instance)
+        [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "RuntimeSuspension_CaptureTasklets")]
+        private static unsafe partial Tasklet *CaptureCurrentStackIntoTasklets(StackCrawlMarkHandle stackMarkTop, ref byte returnValueHandle, [MarshalAs(UnmanagedType.U1)] bool useReturnValueHandle, void* taskAsyncData, out Tasklet* lastTasklet);
+
+        [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "RuntimeSuspension_DeleteTasklet")]
+        private static unsafe partial void DeleteTasklet(Tasklet *tasklet);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        internal static extern void UnwindToFunctionWithAsyncFrame(ref AsyncDataFrame dataFrame);
+
+        private static void SuspendIfSuspensionNotAborted()
+        {
+            ref AsyncDataFrame asyncFrame = ref GetCurrentAsyncDataFrame();
+            RuntimeAsyncMaintainedData maintainedData = asyncFrame._maintainedData!;
+            if (maintainedData._abortSuspend)
+            {
+                AbortSuspend();
+            }
+            else
+            {
+                UnwindToFunctionWithAsyncFrame(ref asyncFrame);
+            }
+        }
+
+        [System.Security.DynamicSecurityMethod] // Methods containing StackCrawlMark local var has to be marked DynamicSecurityMethod
+        private static unsafe Action? GetOrCreateResumptionDelegate()
+        {
+            StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
+            ref AsyncDataFrame asyncFrame = ref GetCurrentAsyncDataFrame();
+
+            asyncFrame._maintainedData ??= asyncFrame._createRuntimeMaintainedData!();
+
+            RuntimeAsyncMaintainedData maintainedData = asyncFrame._maintainedData;
+
+            Tasklet* lastTasklet = null;
+            Tasklet* nextTaskletInStack = CaptureCurrentStackIntoTasklets(new StackCrawlMarkHandle(ref stackMark), ref maintainedData.GetReturnPointer(), maintainedData._initialTaskEntry, t_asyncData, out lastTasklet);
+            if (nextTaskletInStack == null)
+                throw new OutOfMemoryException();
+
+            maintainedData._oldTaskletNext = maintainedData._nextTasklet;
+            lastTasklet->pTaskletNextInStack = maintainedData._nextTasklet;
+            maintainedData._nextTasklet = nextTaskletInStack;
+
+            maintainedData._abortSuspend = false;
+            maintainedData._suspendActive = true;
+
+            return maintainedData._resumption;
+        }
+
+        private static unsafe void AbortSuspend()
+        {
+            ref AsyncDataFrame asyncFrame = ref GetCurrentAsyncDataFrame();
+            RuntimeAsyncMaintainedData maintainedData = asyncFrame._maintainedData!;
+
+            Tasklet* pTaskletCur = maintainedData._nextTasklet;
+            while (pTaskletCur != maintainedData._oldTaskletNext)
+            {
+                Tasklet* pTaskletPrev = pTaskletCur;
+                pTaskletCur = pTaskletPrev;
+                DeleteTasklet(pTaskletPrev);
+            }
+            maintainedData._nextTasklet = maintainedData._oldTaskletNext;
+            maintainedData._oldTaskletNext = null;
+            maintainedData._suspendActive = false;
+        }
     }
 }
diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/Future.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/Future.cs
index 19f359a333383a..71a771fb23a9c3 100644
--- a/src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/Future.cs
+++ b/src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/Future.cs
@@ -19,9 +19,22 @@ namespace System.Threading.Tasks
     // Used to implement Runtime implemented Task suspension handling
     internal struct RuntimeTaskState<TResult>
     {
-        public void Push() {}
-        public void Pop() {}
-        public Task<TResult> FromResult(TResult result) { return Task.FromResult(result); }
+        private RuntimeHelpers.AsyncDataFrame dataFrame;
+
+        public void Push()
+        {
+            dataFrame = new RuntimeHelpers.AsyncDataFrame(()=> new RuntimeHelpers.RuntimeAsyncMaintainedData<TResult>());
+            RuntimeHelpers.PushAsyncData(ref dataFrame);
+        }
+
+        public void Pop() { RuntimeHelpers.PopAsyncData(); }
+        public Task<TResult> FromResult(TResult result)
+        {
+            if (dataFrame._maintainedData == null || !dataFrame._maintainedData._suspendActive)
+                return Task.FromResult(result);
+            return (Task<TResult>)dataFrame._maintainedData.GetTask();
+        }
+
         public Task<TResult> FromException(Exception e) { return Task.FromException<TResult>(e); }
     }
 #pragma warning restore CA1822

From 8353bd8945904960dee7096fd8d4efcf4449a5bc Mon Sep 17 00:00:00 2001
From: David Wrighton <davidwr@microsoft.com>
Date: Thu, 28 Sep 2023 12:00:12 -0700
Subject: [PATCH 018/203] Add the logic to try to flow ExecutionContext and
 SynchronizationContext as appropriate

---
 .../CompilerServices/RuntimeHelpers.cs        | 45 ++++++++++++++++++-
 .../src/System/Threading/Tasks/Future.cs      |  2 +-
 2 files changed, 44 insertions(+), 3 deletions(-)

diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.cs
index b4530098b67919..2cb3b1bfa388dc 100644
--- a/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.cs
+++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.cs
@@ -307,6 +307,10 @@ internal abstract unsafe class RuntimeAsyncMaintainedData
             public byte _dummy;
             public bool _abortSuspend;
 
+            public ExecutionContext? _executionCtx;
+            public SynchronizationContext? _syncCtx;
+
+
             public Tasklet *_nextTasklet;
             public Tasklet* _oldTaskletNext;
 
@@ -351,6 +355,16 @@ public unsafe void ResumptionFunc()
                     PushAsyncData(ref dataFrame);
                     try
                     {
+                        // PushAsyncData will fill in the value of _previousExecutionCtx and _previousSyncCtx
+                        if (_syncCtx != dataFrame._previousSyncCtx)
+                        {
+                            dataFrame._currentThread._synchronizationContext = _syncCtx;
+                        }
+                        if (_executionCtx != dataFrame._previousExecutionCtx)
+                        {
+                            ExecutionContext.RestoreChangedContextToThread(dataFrame._currentThread, _executionCtx, dataFrame._previousExecutionCtx);
+                        }
+
                         while (_nextTasklet != null)
                         {
                             int maxStackNeeded = _nextTasklet->GetMaxStackNeeded();
@@ -389,7 +403,7 @@ public unsafe void ResumptionFunc()
                     }
                     finally
                     {
-                        PopAsyncData();
+                        PopAsyncData(ref dataFrame);
                     }
                 }
                 catch (Exception e)
@@ -462,6 +476,13 @@ public AsyncDataFrame(Func<RuntimeAsyncMaintainedData> getMaintainedData)
             public void* _next;
             public Func<RuntimeAsyncMaintainedData>? _createRuntimeMaintainedData;
             public RuntimeAsyncMaintainedData? _maintainedData;
+            public Thread _currentThread = Thread.CurrentThread;
+
+            // Store current ExecutionContext and SynchronizationContext as "previousXxx".
+            // This allows us to restore them and undo any Context changes made in stateMachine.MoveNext
+            // so that they won't "leak" out of the first await.
+            public ExecutionContext? _previousExecutionCtx;
+            public SynchronizationContext? _previousSyncCtx;
         }
 
         internal enum TaskletReturnType
@@ -487,12 +508,28 @@ internal unsafe struct Tasklet
         internal static unsafe void PushAsyncData(ref AsyncDataFrame asyncData)
         {
             asyncData._next = t_asyncData;
+            asyncData._previousExecutionCtx = asyncData._currentThread._executionContext;
+            asyncData._previousSyncCtx = asyncData._currentThread._synchronizationContext;
+
             t_asyncData = Unsafe.AsPointer(ref asyncData);
         }
 
-        internal static unsafe void PopAsyncData()
+        internal static unsafe void PopAsyncData(ref AsyncDataFrame asyncData)
         {
             t_asyncData = Unsafe.AsRef<AsyncDataFrame>(t_asyncData)._next;
+
+            // The common case is that these have not changed, so avoid the cost of a write barrier if not needed.
+            if (asyncData._previousSyncCtx != asyncData._currentThread._synchronizationContext)
+            {
+                // Restore changed SynchronizationContext back to previous
+                asyncData._currentThread._synchronizationContext = asyncData._previousSyncCtx;
+            }
+
+            ExecutionContext? currentExecutionCtx = asyncData._currentThread._executionContext;
+            if (asyncData._previousExecutionCtx != currentExecutionCtx)
+            {
+                ExecutionContext.RestoreChangedContextToThread(asyncData._currentThread, asyncData._previousExecutionCtx, currentExecutionCtx);
+            }
         }
 
         internal static unsafe bool HasCurrentAsyncDataFrame()
@@ -557,6 +594,10 @@ private static void SuspendIfSuspensionNotAborted()
             maintainedData._abortSuspend = false;
             maintainedData._suspendActive = true;
 
+            maintainedData._executionCtx = asyncFrame._currentThread._executionContext;
+            maintainedData._syncCtx = asyncFrame._currentThread._synchronizationContext;
+
+
             return maintainedData._resumption;
         }
 
diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/Future.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/Future.cs
index 71a771fb23a9c3..69f8bb93534906 100644
--- a/src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/Future.cs
+++ b/src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/Future.cs
@@ -27,7 +27,7 @@ public void Push()
             RuntimeHelpers.PushAsyncData(ref dataFrame);
         }
 
-        public void Pop() { RuntimeHelpers.PopAsyncData(); }
+        public void Pop() { RuntimeHelpers.PopAsyncData(ref dataFrame); }
         public Task<TResult> FromResult(TResult result)
         {
             if (dataFrame._maintainedData == null || !dataFrame._maintainedData._suspendActive)

From 2aac618e5bc204668f8502daa5d39a8112e20f15 Mon Sep 17 00:00:00 2001
From: David Wrighton <davidwr@microsoft.com>
Date: Mon, 9 Oct 2023 13:00:27 -0700
Subject: [PATCH 019/203] It compiles, but doesn't link

---
 src/coreclr/inc/corhlprpriv.h                 |  10 +
 src/coreclr/inc/regdisp.h                     |   7 +
 src/coreclr/vm/CMakeLists.txt                 |   2 +
 src/coreclr/vm/corelib.cpp                    |   2 +
 src/coreclr/vm/ecalllist.h                    |   3 +
 src/coreclr/vm/method.hpp                     |   2 +
 src/coreclr/vm/qcallentrypoints.cpp           |   4 +
 .../vm/restoreregs_for_runtimesuspension.h    |  22 +
 src/coreclr/vm/runtimesuspension.cpp          | 502 ++++++++++++++++++
 src/coreclr/vm/runtimesuspension.h            |  14 +
 .../CompilerServices/RuntimeHelpers.cs        |  18 +-
 11 files changed, 580 insertions(+), 6 deletions(-)
 create mode 100644 src/coreclr/vm/restoreregs_for_runtimesuspension.h
 create mode 100644 src/coreclr/vm/runtimesuspension.cpp
 create mode 100644 src/coreclr/vm/runtimesuspension.h

diff --git a/src/coreclr/inc/corhlprpriv.h b/src/coreclr/inc/corhlprpriv.h
index ffa249543329e3..4c9f31b3223128 100644
--- a/src/coreclr/inc/corhlprpriv.h
+++ b/src/coreclr/inc/corhlprpriv.h
@@ -576,6 +576,16 @@ class CQuickArrayList : protected CQuickArray<T>
         return m_curSize;
     }
 
+    T* Ptr()
+    {
+        return (T*) CQuickBytesBase::Ptr();
+    }
+
+    const T* Ptr() const
+    {
+        return (T*) CQuickBytesBase::Ptr();
+    }
+
     void Shrink()
     {
         CQuickArray<T>::Shrink(m_curSize);
diff --git a/src/coreclr/inc/regdisp.h b/src/coreclr/inc/regdisp.h
index c3b4b4431bec54..48dd82a1db6519 100644
--- a/src/coreclr/inc/regdisp.h
+++ b/src/coreclr/inc/regdisp.h
@@ -270,6 +270,13 @@ inline BOOL IsInCalleesFrames(REGDISPLAY *display, LPVOID stackPointer)
     return stackPointer < ((LPVOID)(display->SP));
 }
 
+inline BOOL IsInCurrentFrame(REGDISPLAY *display, LPVOID stackPointer)
+{
+    LIMITED_METHOD_CONTRACT;
+    _ASSERTE(display->IsCallerContextValid);
+    return stackPointer < ((LPVOID)(::GetSP(display->pCallerContext)));
+}
+
 inline TADDR GetRegdisplayStackMark(REGDISPLAY *display)
 {
 #if defined(TARGET_AMD64)
diff --git a/src/coreclr/vm/CMakeLists.txt b/src/coreclr/vm/CMakeLists.txt
index ce94e6323da855..73a22a2e60f2a9 100644
--- a/src/coreclr/vm/CMakeLists.txt
+++ b/src/coreclr/vm/CMakeLists.txt
@@ -359,6 +359,7 @@ set(VM_SOURCES_WKS
     reflectclasswriter.cpp
     reflectioninvocation.cpp
     runtimehandles.cpp
+    runtimesuspension.cpp
     safehandle.cpp
     simplerwlock.cpp
     stackingallocator.cpp
@@ -458,6 +459,7 @@ set(VM_HEADERS_WKS
     reflectclasswriter.h
     reflectioninvocation.h
     runtimehandles.h
+    runtimesuspension.h
     simplerwlock.hpp
     stackingallocator.h
     stringliteralmap.h
diff --git a/src/coreclr/vm/corelib.cpp b/src/coreclr/vm/corelib.cpp
index b6ae49b2a1d1ca..22869b70e15cf4 100644
--- a/src/coreclr/vm/corelib.cpp
+++ b/src/coreclr/vm/corelib.cpp
@@ -77,6 +77,8 @@
 
 #include "tailcallhelp.h"
 
+#include "runtimesuspension.h"
+
 ///////////////////////////////////////////////////////////////////////////////
 //
 // Hardcoded Meta-Sig
diff --git a/src/coreclr/vm/ecalllist.h b/src/coreclr/vm/ecalllist.h
index 2f1f27bdf3eee3..2c1a596f8cfbd0 100644
--- a/src/coreclr/vm/ecalllist.h
+++ b/src/coreclr/vm/ecalllist.h
@@ -557,6 +557,9 @@ FCFuncStart(gRuntimeHelpers)
     FCFuncElement("AllocTailCallArgBuffer", TailCallHelp::AllocTailCallArgBuffer)
     FCFuncElement("GetTailCallInfo", TailCallHelp::GetTailCallInfo)
     FCFuncElement("Box", JIT_Box)
+    FCFuncElement("UnwindToFunctionWithAsyncFrame", RuntimeSuspension_UnwindToFunctionWithAsyncFrame)
+    FCFuncElement("ResumeTaskletReferenceReturn", RuntimeSuspension_ResumeTaskletReferenceReturn)
+    FCFuncElement("ResumeTaskletIntegerRegisterReturn", RuntimeSuspension_ResumeTaskletIntegerRegisterReturn)
 FCFuncEnd()
 
 FCFuncStart(gMethodTableFuncs)
diff --git a/src/coreclr/vm/method.hpp b/src/coreclr/vm/method.hpp
index 7b0274e6057049..d16182d8e54273 100644
--- a/src/coreclr/vm/method.hpp
+++ b/src/coreclr/vm/method.hpp
@@ -1722,6 +1722,8 @@ class MethodDesc
         return (m_wFlags & mdcIsAsyncThunkMethod) != 0;
     }
 
+    bool IsAsync2Method();
+
     inline void SetIsAsyncThunkMethod()
     {
         LIMITED_METHOD_CONTRACT;
diff --git a/src/coreclr/vm/qcallentrypoints.cpp b/src/coreclr/vm/qcallentrypoints.cpp
index 3682a4cef1691d..8f5df385e1b17c 100644
--- a/src/coreclr/vm/qcallentrypoints.cpp
+++ b/src/coreclr/vm/qcallentrypoints.cpp
@@ -76,8 +76,12 @@
 
 #include "exceptionhandlingqcalls.h"
 
+#include "runtimesuspension.h"
+
 static const Entry s_QCall[] =
 {
+    DllImportEntry(RuntimeSuspension_DeleteTasklet)
+    DllImportEntry(RuntimeSuspension_CaptureTasklets)
     DllImportEntry(Enum_GetValuesAndNames)
     DllImportEntry(DebugDebugger_Launch)
     DllImportEntry(DebugDebugger_Log)
diff --git a/src/coreclr/vm/restoreregs_for_runtimesuspension.h b/src/coreclr/vm/restoreregs_for_runtimesuspension.h
new file mode 100644
index 00000000000000..689be7bf3fbf0e
--- /dev/null
+++ b/src/coreclr/vm/restoreregs_for_runtimesuspension.h
@@ -0,0 +1,22 @@
+#if defined(TARGET_AMD64)
+    DISCOVER_RESTORED_REG(Rbx)
+    DISCOVER_RESTORED_REG(Rbp)
+    DISCOVER_RESTORED_REG(Rdi)
+    DISCOVER_RESTORED_REG(Rsi)
+    DISCOVER_RESTORED_REG(R12)
+    DISCOVER_RESTORED_REG(R13)
+    DISCOVER_RESTORED_REG(R14)
+    DISCOVER_RESTORED_REG(R15)
+    DISCOVER_RESTORED_REG(Xmm6)
+    DISCOVER_RESTORED_REG(Xmm7)
+    DISCOVER_RESTORED_REG(Xmm8)
+    DISCOVER_RESTORED_REG(Xmm9)
+    DISCOVER_RESTORED_REG(Xmm10)
+    DISCOVER_RESTORED_REG(Xmm11)
+    DISCOVER_RESTORED_REG(Xmm12)
+    DISCOVER_RESTORED_REG(Xmm13)
+    DISCOVER_RESTORED_REG(Xmm14)
+    DISCOVER_RESTORED_REG(Xmm15)
+#else
+    PORTABILITY_ASSERT();
+#endif
diff --git a/src/coreclr/vm/runtimesuspension.cpp b/src/coreclr/vm/runtimesuspension.cpp
new file mode 100644
index 00000000000000..fdfc254368e348
--- /dev/null
+++ b/src/coreclr/vm/runtimesuspension.cpp
@@ -0,0 +1,502 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+#include "common.h"
+#include "runtimesuspension.h"
+#include "siginfo.hpp"
+
+enum class TaskletReturnType : int32_t
+{
+    // These return types are OS/architecture specific. For instance, Arm64 supports returning structs in a register pair. This is also incomplete and doesn't handle floating point, vector registers, etc.
+    Integer,
+    ObjectReference,
+    ByReference
+};
+
+enum RegisterToRestore
+{
+    Rbx,
+    Rbp,
+    Rdi,
+    Rsi,
+    R12,
+    R13,
+    R14,
+    R15,
+    Xmm6,
+    Xmm7,
+    Xmm8,
+    Xmm9,
+    Xmm10,
+    Xmm11,
+    Xmm12,
+    Xmm13,
+    Xmm14,
+    Xmm15,
+    ReturnRegisters, // End sequence marker. Associated offset is for the return register, but the actual return value is stashed after the register data
+};
+
+struct RegRestore
+{
+    RegisterToRestore reg;
+    uint32_t offset;
+};
+
+struct StackDataInfo
+{
+    void CleanupStackDataInfo()
+    {
+        if (ByRefOffsets != NULL)
+            free(ByRefOffsets);
+        if (ObjectRefOffsets != NULL)
+            free(ByRefOffsets);
+        if (RegistersToRestore != NULL)
+            free(ByRefOffsets);
+    }
+    uint32_t StackRequirement;
+    uint32_t UnrecordedDataSize; // From the restored RSP to the data chunk, how many bytes are skipped
+    uint32_t StackDataSize;
+    uint32_t ReturnAddressOffset;
+    int32_t* ByRefOffsets = NULL; // Negative numbers indicate pinned byrefs
+    uint32_t cByRefs;
+    uint32_t* ObjectRefOffsets = NULL;
+    uint32_t cObjectRefs;
+    RegRestore* RegistersToRestore = NULL;
+};
+
+struct Tasklet
+{
+    Tasklet* pTaskletNextInStack;
+    Tasklet* pTaskletNextInLiveList;
+    Tasklet* pTaskletPrevInLiveList;
+    uint8_t* pStackData;
+    uintptr_t restoreIPAddress;
+    StackDataInfo* pStackDataInfo;
+    TaskletReturnType taskletReturnType;
+};
+
+struct RuntimeAsyncReturnValue
+{
+    uintptr_t _ptr;
+    uintptr_t _obj;
+    TaskletReturnType _returnType;
+};
+
+struct ByRefAdjustment
+{
+    uint8_t* pOldLocation;
+    uint32_t Size;
+    uintptr_t Adjustment;
+};
+
+bool RelocAtAddress(ByRefAdjustment* pAdjustment, uintptr_t* pData);
+void RelocAtAddress(ByRefAdjustment* pAdjustment, uint8_t* pDataToAdjustAddress)
+{
+    uintptr_t *pData = (uintptr_t*)pDataToAdjustAddress;
+    RelocAtAddress(pAdjustment, pData);
+}
+
+bool RelocAtAddress(ByRefAdjustment* pAdjustment, uintptr_t* pData)
+{
+    if ((*pData >= (uintptr_t)pAdjustment->pOldLocation) && ((*pData - (uintptr_t)pAdjustment->pOldLocation) < (uintptr_t)pAdjustment->Size))
+    {
+        *pData += pAdjustment->Adjustment;
+        return true;
+    }
+    return false;
+}
+
+struct RestoreFunctionLocals
+{
+    // Anonymous union of data for return value registers
+    union 
+    {
+        uintptr_t integerRegister;
+    };
+    uintptr_t AddressInMethodToRestoreTo;
+private:
+    uint8_t *pFutureRSPLocation;
+    uintptr_t ReturnAddress;
+public:
+    uint8_t *GetFutureRSPLocation() { return pFutureRSPLocation; }  // TODO This should be some simple math off of the this pointer
+    uintptr_t GetReturnAddress() { return ReturnAddress; }  // TODO This should be some simple math off of the this pointer
+};
+
+// The actual restoration function which needs to be written in assembly, sets up enough of a frame on top of the current RSP to call this function
+// Then, once this function returns, iterates through the 
+extern "C" RegRestore* PlatformIndependentRestore(Tasklet* tasklet, RuntimeAsyncReturnValue* returnValueToFillIn, RestoreFunctionLocals *restoreLocals)
+{
+    // Compute what needs to be adjusted in terms of byrefs for 
+    ByRefAdjustment adjustment;
+    adjustment.pOldLocation = tasklet->pStackData;
+    adjustment.Size = tasklet->pStackDataInfo->StackDataSize;
+
+    uint8_t* pNewLocation = restoreLocals->GetFutureRSPLocation() + tasklet->pStackDataInfo->UnrecordedDataSize;
+    adjustment.Adjustment = ((uintptr_t)pNewLocation) - ((uintptr_t)adjustment.pOldLocation);
+
+    // Adjust all pointers to the stack data that we are about to move, both in this frame, and in the caller frames
+    Tasklet *pTaskletToAdjustByrefsOn = tasklet;
+    do
+    {
+        uint32_t cByRefs = pTaskletToAdjustByrefsOn->pStackDataInfo->cByRefs;
+        uint32_t* byRefOffsets = (uint32_t*)pTaskletToAdjustByrefsOn->pStackDataInfo->ByRefOffsets;
+        uint8_t* taskletData = pTaskletToAdjustByrefsOn->pStackData;
+        for (uint32_t iByRef = 0; iByRef < cByRefs; iByRef++)
+        {
+            RelocAtAddress(&adjustment, taskletData + byRefOffsets[iByRef]);
+        }
+        pTaskletToAdjustByrefsOn = pTaskletToAdjustByrefsOn->pTaskletNextInStack;
+    } while (pTaskletToAdjustByrefsOn != NULL);
+    
+    StackDataInfo *pStackDataInfo = tasklet->pStackDataInfo;
+
+    // Copy most of the memory
+    memcpy(restoreLocals->GetFutureRSPLocation() + pStackDataInfo->UnrecordedDataSize, tasklet->pStackData, pStackDataInfo->StackDataSize);
+
+    // Update the return address on the stack
+    *(uintptr_t *)(restoreLocals->GetFutureRSPLocation() + pStackDataInfo->ReturnAddressOffset) = restoreLocals->GetReturnAddress();
+
+    restoreLocals->AddressInMethodToRestoreTo = tasklet->restoreIPAddress;
+
+    switch (returnValueToFillIn->_returnType)
+    {
+        case TaskletReturnType::Integer:
+            restoreLocals->integerRegister = returnValueToFillIn->_ptr;
+            break;
+        case TaskletReturnType::ObjectReference:
+            restoreLocals->integerRegister = returnValueToFillIn->_obj;
+            break;
+
+        default:
+            // Not yet implemented
+            _ASSERTE(FALSE);
+    }
+
+    return pStackDataInfo->RegistersToRestore;
+
+    // The rest of the code will do something like...
+    // This is written for X86 which is ... not the initial target
+/*
+    mov ecx, [eax + offsetof(pStackData)]
+    mov edx, [eax + offsetof(pStackDataInfo)]
+    mov edx, [eax + offsetof(RegistersToRestore)] ; EDX now points at a list of registers to restore, terminated by the RegisterToRestore::ReturnRegisters value
+    add ecx, [edx + offsetof(StackDataSize)] ; ECX now points at the data to copy into the various restored register
+    ; We have eax as a scratch register if we need it
+        .data
+tbl     dq      ReturnRegisters, CheckEBX, CheckESI, ...            ;table 
+        .code
+RegRestoreLoop:
+    mov eax, [edx]
+    jmp [tbl + eax*4]
+ReturnRegisters:
+    mov eax, [esp-X]
+    mov edx, [esp-X+4]
+    MOVDQU xmm0, [esp-X]
+    mov ecx, [esp-Y] ; Load address of where to jmp into ecx
+    add esp, Z
+    jmp ecx
+    // Repeat for every saved register type
+CheckEBX:
+    cmp EBX, [edx]
+    mov eax, [edx+4]
+    mov [edx + 4], ebx
+    mov ebx, eax
+    add edx, 8
+    jmp RegRestoreLoop
+
+CheckRBX:
+
+    ends with
+    cmp 
+*/
+}
+
+struct TaskletCaptureData
+{
+    bool inRunOfAsyncMethods;
+    StackCrawlMark* stackMark;
+    Tasklet* firstTasklet = NULL;
+    Tasklet* lastTasklet = NULL;
+    uintptr_t stackLimit;
+    uintptr_t stackToIgnoreFromPreviousFrame = 0; // Portions of the stack which have been copied into a previous frame
+    uintptr_t returnStructSize = 0;
+    CQuickArrayList<uintptr_t *> ActiveByRefsToStack;
+    void AddCopiedByRef(uintptr_t currentStackTop, uintptr_t *newByRef)
+    {
+        if ((*newByRef >= currentStackTop) && (*newByRef < stackLimit))
+        {
+            ActiveByRefsToStack.Push(newByRef);
+        }
+    }
+
+    void ApplyByRefRelocsToNewlyAllocatedTasklet(ByRefAdjustment adjustment)
+    {
+        if (ActiveByRefsToStack.Size() > 0)
+        {
+            for (SIZE_T iByRef = ActiveByRefsToStack.Size() - 1; iByRef >= 0; iByRef--)
+            {
+                if (RelocAtAddress(&adjustment, ActiveByRefsToStack[iByRef]))
+                {
+                    // ByRef was reloc'd, and therefore doesn't need to be handled anymore
+                    ActiveByRefsToStack[iByRef] = ActiveByRefsToStack[ActiveByRefsToStack.Size() - 1];
+                    ActiveByRefsToStack.Pop();
+                }
+            }
+        }
+    }
+};
+
+struct RuntimeSuspensionEnumData
+{
+    RuntimeSuspensionEnumData(const CQuickArrayList<RegRestore>& restoreRegLocations, REGDISPLAY* pRD_) :
+        RestoreRegLocations(restoreRegLocations),
+        pRD(pRD_)
+    {
+    }
+
+    CQuickArrayList<uint32_t> ObjectRefOffsets;
+    CQuickArrayList<int32_t> ByRefOffsets;
+    const CQuickArrayList<RegRestore>& RestoreRegLocations;
+    REGDISPLAY* pRD;
+
+    uint32_t GetOffsetForReg(RegisterToRestore nonVolatileReg)
+    {
+        for (SIZE_T i = 0; i < RestoreRegLocations.Size(); i++)
+        {
+            if (RestoreRegLocations[i].reg == nonVolatileReg)
+            {
+                return RestoreRegLocations[i].offset;
+            }
+        }
+        _ASSERTE(!"Attempting to get offset for reg which doesn't have an offset recorded!");
+        return 0;
+    }
+};
+
+StackWalkAction CaptureTaskletsCore(CrawlFrame* pCf, VOID* data)
+{
+    CONTRACTL
+    {
+        THROWS;
+        GC_TRIGGERS;
+        MODE_COOPERATIVE;
+        INJECT_FAULT(COMPlusThrowOM(););
+    }
+    CONTRACTL_END;
+
+
+    MethodDesc *pFunc = pCf->GetFunction();
+
+    /* We asked to be called back only for functions */
+    _ASSERTE(pFunc);
+
+    TaskletCaptureData* taskletCaptureData = (TaskletCaptureData*) data;
+    if (taskletCaptureData->firstTasklet == NULL)
+    {
+        if (!IsInCurrentFrame(pCf->GetRegisterSet(), taskletCaptureData->stackMark))
+        {
+            // Move to next frame, we haven't found anything
+            return SWA_CONTINUE;
+        }
+    }
+    else if (!pCf->GetFunction()->IsAsync2Method())
+    {
+        // We must be in the wrapper thunk
+        _ASSERTE(pCf->GetFunction()->IsAsyncThunkMethod());
+
+        if (taskletCaptureData->ActiveByRefsToStack.Size() != 0)
+        {
+            // We must have to deal with a return value managed as a byref
+            _ASSERTE(!"This is not yet implemented, the thought is to require that the return address be stored somewhere with a pointer to the start of the return value, and then scan the ActiveByRefsToStack to find the lowest address, and treat that as where to find the start of the return buffer. Or maybe we should record it somewhere?");
+        }
+        return SWA_ABORT;
+    }
+    
+
+    uint8_t* pTopOfStackInFunction = (uint8_t*)pCf->GetRegisterSet()->SP;
+    uint32_t sizeofArgStack = (uint32_t)pCf->GetFunction()->SizeOfArgStack();
+    uint8_t* pBottomOfStackInFunction = (uint8_t*)::GetSP(pCf->GetRegisterSet()->pCallerContext) + sizeofArgStack;
+    uint32_t sizeofEntireMeaningfulStack = (uint32_t)(pBottomOfStackInFunction - pTopOfStackInFunction);
+    sizeofEntireMeaningfulStack -= (uint32_t)taskletCaptureData->stackToIgnoreFromPreviousFrame;
+
+    uint8_t *pStackData = (uint8_t*)malloc(sizeofEntireMeaningfulStack);
+    memcpy(pStackData, pTopOfStackInFunction + taskletCaptureData->stackToIgnoreFromPreviousFrame, sizeofEntireMeaningfulStack);
+
+    ByRefAdjustment byrefAdjustment;
+    byrefAdjustment.pOldLocation = pTopOfStackInFunction + taskletCaptureData->stackToIgnoreFromPreviousFrame;
+    byrefAdjustment.Adjustment = ((uintptr_t)pStackData) - ((uintptr_t)byrefAdjustment.pOldLocation);
+    byrefAdjustment.Size = sizeofEntireMeaningfulStack;
+
+    StackDataInfo stackDataInfo;
+    stackDataInfo.StackRequirement = (uint32_t)(::GetSP(pCf->GetRegisterSet()->pCallerContext) - pCf->GetRegisterSet()->SP);
+    stackDataInfo.StackDataSize = sizeofEntireMeaningfulStack;
+
+#if defined(TARGET_X86) || defined(TARGET_AMD64)
+    uintptr_t returnAddressLocation = (uintptr_t) (EECodeManager::GetCallerSp(pCf->GetRegisterSet()) - sizeof(void*));
+#elif defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64)
+    uintptr_t returnAddressLocation = (uintptr_t) pCf->GetRegisterSet()->pCallerContextPointers->Ra;
+#elif defined(TARGET_ARM) || defined(TARGET_ARM64)
+    uintptr_t returnAddressLocation = (uintptr_t) pCf->GetRegisterSet()->pCallerContextPointers->Lr;
+#else
+    PORTABILITY_ASSERT("Platform NYI");
+#endif
+    stackDataInfo.ReturnAddressOffset = (uint32_t)(returnAddressLocation - (uintptr_t)pCf->GetRegisterSet()->SP);
+
+    CQuickArrayList<RegRestore> savedRegRestoreData;
+
+#define DISCOVER_RESTORED_REG(REGNAME) if (pCf->GetRegisterSet()->pCallerContextPointers->REGNAME != pCf->GetRegisterSet()->pCurrentContextPointers->REGNAME) { RegRestore restoreData; restoreData.reg = RegisterToRestore::REGNAME; restoreData.offset = (uint32_t)((uint8_t*)pCf->GetRegisterSet()->pCallerContextPointers->REGNAME - (uint8_t*)pCf->GetRegisterSet()->SP); savedRegRestoreData.Push(restoreData); }
+#include "restoreregs_for_runtimesuspension.h"
+#undef DISCOVER_RESTORED_REG
+
+    RegRestore returnData;
+    returnData.reg = RegisterToRestore::ReturnRegisters;
+    returnData.offset = 0;
+    savedRegRestoreData.Push(returnData);
+
+
+    ICodeManager * pCM = pCf->GetCodeManager();
+    _ASSERTE(pCM != NULL);
+
+    unsigned flags = pCf->GetCodeManagerFlags();
+
+    GCEnumCallback enumGCRefs = []( 
+        LPVOID          hCallback,      // callback data
+        OBJECTREF*      pObject,        // address of object-reference we are reporting
+        uint32_t        flags )
+    {
+        RuntimeSuspensionEnumData *pEnumData = (RuntimeSuspensionEnumData*)hCallback;
+
+        // Determine if pObject points at a non-volatile register slot
+        uint32_t offset;
+
+        if (false) {}
+#define DISCOVER_RESTORED_REG(REGNAME) else if ((void*)pEnumData->pRD->pCallerContextPointers->REGNAME == (void*)pObject) { offset = pEnumData->GetOffsetForReg(RegisterToRestore::REGNAME); }
+#include "restoreregs_for_runtimesuspension.h"
+#undef DISCOVER_RESTORED_REG
+        else
+        {
+            // Must be within the stack frame itself
+            offset = (uint32_t)((uint8_t*)pObject - (uint8_t*)pEnumData->pRD->SP);
+        }
+
+        bool isInterior = !!(flags & GC_CALL_INTERIOR);
+        bool isPinned = !!(flags & GC_CALL_PINNED);
+        _ASSERTE(!isPinned || isInterior);
+
+        if (!isInterior)
+        {
+            pEnumData->ObjectRefOffsets.Push(offset);
+        }
+        else
+        {
+            int32_t byRefOffset = (int32_t)offset;
+            if (isPinned)
+            {
+                byRefOffset = -byRefOffset;
+            }
+
+            pEnumData->ByRefOffsets.Push(offset);
+        }
+    };
+
+    RuntimeSuspensionEnumData enumData(savedRegRestoreData, pCf->GetRegisterSet());
+
+    pCM->EnumGcRefs(pCf->GetRegisterSet(),
+                    pCf->GetCodeInfo(),
+                    flags,
+                    enumGCRefs,
+                    &enumData,
+                    NO_OVERRIDE_OFFSET);
+
+    stackDataInfo.ByRefOffsets = (int32_t*)malloc(enumData.ByRefOffsets.Size() * sizeof(int32_t));
+    memcpy(stackDataInfo.ByRefOffsets, enumData.ByRefOffsets.Ptr(), enumData.ByRefOffsets.Size() * sizeof(int32_t));
+    stackDataInfo.ObjectRefOffsets = (uint32_t*)malloc(enumData.ObjectRefOffsets.Size() * sizeof(uint32_t));
+    memcpy(stackDataInfo.ObjectRefOffsets, enumData.ObjectRefOffsets.Ptr(), enumData.ObjectRefOffsets.Size() * sizeof(uint32_t));
+    stackDataInfo.RegistersToRestore = (RegRestore*)malloc(savedRegRestoreData.Size() * sizeof(RegRestore));
+    memcpy(stackDataInfo.RegistersToRestore, savedRegRestoreData.Ptr(), savedRegRestoreData.Size() * sizeof(RegRestore));
+
+    StackDataInfo *pStackDataInfo = (StackDataInfo*)malloc(sizeof(StackDataInfo));
+    *pStackDataInfo = stackDataInfo;
+
+    MetaSig msig(pCf->GetFunction());
+    ArgIterator argit(&msig);
+    TaskletReturnType taskletReturnType;
+#if TARGET_AMD64
+    if (argit.HasRetBuffArg())
+    {
+        taskletReturnType = TaskletReturnType::ByReference; // In this case, on Windows AMD64 the abi specifies that the return value in RAX is the address of the ret buffer
+    }
+    else
+    {
+        TypeHandle thRet = msig.GetRetTypeHandleThrowing();
+        CorElementType corElementType = thRet.GetInternalCorElementType();
+        if (thRet.IsTypeDesc())
+        {
+            taskletReturnType = TaskletReturnType::Integer;
+        }
+        else
+        {
+            if (thRet.AsMethodTable()->IsValueType() || thRet.AsMethodTable()->ContainsPointers())
+            {
+                taskletReturnType = TaskletReturnType::ObjectReference;
+            }
+            else
+            {
+                // These asserts don't check all the FP ret cases, but they cover at least some of it
+                _ASSERTE(thRet.GetInternalCorElementType() != ELEMENT_TYPE_R4);
+                _ASSERTE(thRet.GetInternalCorElementType() != ELEMENT_TYPE_R8);
+                taskletReturnType = TaskletReturnType::Integer;
+            }
+        }
+    }
+#else
+PORTABILIT_ASSERT()
+#endif
+
+    Tasklet *pTasklet = (Tasklet*)malloc(sizeof(Tasklet));
+    memset(pTasklet, 0, sizeof(Tasklet));
+    pTasklet->pStackData = pStackData;
+    pTasklet->restoreIPAddress = (uintptr_t)GetControlPC(pCf->GetRegisterSet());
+    pTasklet->pStackDataInfo;
+    pTasklet->taskletReturnType = taskletReturnType;
+
+    if (taskletCaptureData->firstTasklet == NULL)
+    {
+        taskletCaptureData->firstTasklet = pTasklet;
+        taskletCaptureData->lastTasklet = pTasklet;
+    }
+    else
+    {
+        taskletCaptureData->lastTasklet->pTaskletNextInStack = pTasklet;
+        taskletCaptureData->lastTasklet = pTasklet;
+    }
+    
+    _ASSERTE(!"Needs to implement doing byref update of the data already captured.. maybe?");
+
+    return SWA_CONTINUE;
+}
+
+
+extern "C" Tasklet* QCALLTYPE RuntimeSuspension_CaptureTasklets(QCall::StackCrawlMarkHandle stackMark, uint8_t* returnValue, uint8_t useReturnValueHandle, void* taskAsyncData, Tasklet** lastTasklet)
+{
+    GCX_COOP();
+
+    TaskletCaptureData cdata;
+    cdata.stackMark = stackMark;
+    GetThread()->StackWalkFrames(CaptureTaskletsCore, &cdata, FUNCTIONSONLY);
+
+    // We should have captured a full stack here. 
+    *lastTasklet = cdata.lastTasklet;
+    _ASSERTE(FALSE);
+    return cdata.firstTasklet;
+}
+
+extern "C" void QCALLTYPE RuntimeSuspension_DeleteTasklet(Tasklet* tasklet)
+{
+    // Well, leaking isn't a good plan in the long term, but for now it'll be fine
+}
+
+EXTERN_C FCDECL1(void, RuntimeSuspension_UnwindToFunctionWithAsyncFrame, AsyncDataFrame* frame)
+{
+    _ASSERTE(!"Not Yet Implemented");
+}
diff --git a/src/coreclr/vm/runtimesuspension.h b/src/coreclr/vm/runtimesuspension.h
new file mode 100644
index 00000000000000..f48c583d33c44a
--- /dev/null
+++ b/src/coreclr/vm/runtimesuspension.h
@@ -0,0 +1,14 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+struct Tasklet;
+struct AsyncDataFrame;
+struct RuntimeAsyncReturnValue;
+
+extern "C" Tasklet* QCALLTYPE RuntimeSuspension_CaptureTasklets(QCall::StackCrawlMarkHandle stackMark, uint8_t* returnValue, uint8_t useReturnValueHandle, void* taskAsyncData, Tasklet** lastTasklet);
+extern "C" void QCALLTYPE RuntimeSuspension_DeleteTasklet(Tasklet* tasklet);
+
+EXTERN_C FCDECL1(void, RuntimeSuspension_UnwindToFunctionWithAsyncFrame, AsyncDataFrame* frame);
+
+EXTERN_C FCDECL2(Object*, RuntimeSuspension_ResumeTaskletReferenceReturn, Tasklet* tasklet, RuntimeAsyncReturnValue *resumptionReturnValue);
+EXTERN_C FCDECL2(Object*, RuntimeSuspension_ResumeTaskletIntegerRegisterReturn, Tasklet* tasklet, RuntimeAsyncReturnValue *resumptionReturnValue);
diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.cs
index 2cb3b1bfa388dc..6b1fca886dae69 100644
--- a/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.cs
+++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.cs
@@ -284,17 +284,17 @@ public RuntimeAsyncReturnValue(object? obj)
             {
                 _obj = obj;
                 _ptr = IntPtr.Zero;
-                _isObj = true;
+                _returnType = TaskletReturnType.ObjectReference;
             }
             public RuntimeAsyncReturnValue(IntPtr ptr)
             {
                 _obj = null;
                 _ptr = ptr;
-                _isObj = false;
+                _returnType = TaskletReturnType.Integer;
             }
             public IntPtr _ptr;
             public object? _obj;
-            public bool _isObj;
+            public TaskletReturnType _returnType;
         }
 
         internal abstract unsafe class RuntimeAsyncMaintainedData
@@ -492,17 +492,23 @@ internal enum TaskletReturnType
             ObjectReference,
             ByReference
         }
+
+        internal struct StackDataInfo
+        {
+            public int StackRequirement;
+            // And native has a bunch of other fields that managed does not use.
+        }
         internal unsafe struct Tasklet
         {
             public Tasklet* pTaskletNextInStack;
             public Tasklet* pTaskletNextInLiveList;
             public Tasklet* pTaskletPrevInLiveList;
             public byte* pStackData;
-            public byte* pStackDataInfo;
-            public int maxStackNeeded;
+            public IntPtr restoreIPAddress;
+            public StackDataInfo* pStackDataInfo;
             public TaskletReturnType taskletReturnType;
 
-            public int GetMaxStackNeeded() { return maxStackNeeded; }
+            public int GetMaxStackNeeded() { return pStackDataInfo->StackRequirement; }
         }
 
         internal static unsafe void PushAsyncData(ref AsyncDataFrame asyncData)

From 0e82446313ec76fe9418a7952ef074534649be7e Mon Sep 17 00:00:00 2001
From: David Wrighton <davidwr@microsoft.com>
Date: Mon, 9 Oct 2023 14:47:33 -0700
Subject: [PATCH 020/203] C++ portion builds

---
 src/coreclr/vm/array.cpp              |  2 +-
 src/coreclr/vm/class.cpp              |  2 +-
 src/coreclr/vm/dynamicmethod.cpp      |  2 +-
 src/coreclr/vm/genmeth.cpp            | 10 +++----
 src/coreclr/vm/ilstubcache.cpp        |  2 +-
 src/coreclr/vm/method.cpp             | 36 +++++++++++-----------
 src/coreclr/vm/method.hpp             | 43 ++++++++++++++++++---------
 src/coreclr/vm/methodtablebuilder.cpp | 40 ++++++++++++++-----------
 src/coreclr/vm/methodtablebuilder.h   | 40 ++++++++++---------------
 9 files changed, 94 insertions(+), 83 deletions(-)

diff --git a/src/coreclr/vm/array.cpp b/src/coreclr/vm/array.cpp
index 7a2bb145faadbe..d5a7de115517d1 100644
--- a/src/coreclr/vm/array.cpp
+++ b/src/coreclr/vm/array.cpp
@@ -500,7 +500,7 @@ MethodTable* Module::CreateArrayMethodTable(TypeHandle elemTypeHnd, CorElementTy
                             + 3;        // for rank specific Get, Set, Address
 
         MethodDescChunk * pChunks = MethodDescChunk::CreateChunk(pAllocator->GetHighFrequencyHeap(),
-                            dwMethodDescs, mcArray, FALSE /* fNonVtableSlot*/, FALSE /* fNativeCodeSlot */, FALSE /* IsAsyncThunkMethod */,
+                            dwMethodDescs, mcArray, FALSE /* fNonVtableSlot*/, FALSE /* fNativeCodeSlot */, FALSE /* HasAsyncMethodData */,
                             pMT, pamTracker);
         pClass->SetChunks(pChunks);
 
diff --git a/src/coreclr/vm/class.cpp b/src/coreclr/vm/class.cpp
index cda2d4ebd18f87..c3337890683ef0 100644
--- a/src/coreclr/vm/class.cpp
+++ b/src/coreclr/vm/class.cpp
@@ -817,7 +817,7 @@ HRESULT EEClass::AddMethodDesc(
                                 pImport,
                                 NULL,
                                 Signature(),
-                                AsyncThunkType::NotAThunk
+                                AsyncMethodType::NotAsync
                                 COMMA_INDEBUG(debug_szMethodName)
                                 COMMA_INDEBUG(pMT->GetDebugClassName())
                                 COMMA_INDEBUG(NULL)
diff --git a/src/coreclr/vm/dynamicmethod.cpp b/src/coreclr/vm/dynamicmethod.cpp
index a7499e0708fac8..290eadde211387 100644
--- a/src/coreclr/vm/dynamicmethod.cpp
+++ b/src/coreclr/vm/dynamicmethod.cpp
@@ -162,7 +162,7 @@ void DynamicMethodTable::AddMethodsToList()
     // allocate as many chunks as needed to hold the methods
     //
     MethodDescChunk* pChunk = MethodDescChunk::CreateChunk(pHeap, 0 /* one chunk of maximum size */,
-        mcDynamic, TRUE /* fNonVtableSlot */, TRUE /* fNativeCodeSlot */, FALSE /* IsAsyncThunkMethod */, m_pMethodTable, &amt);
+        mcDynamic, TRUE /* fNonVtableSlot */, TRUE /* fNativeCodeSlot */, FALSE /* HasAsyncMethodData */, m_pMethodTable, &amt);
     if (m_DynamicMethodList) RETURN;
 
     int methodCount = pChunk->GetCount();
diff --git a/src/coreclr/vm/genmeth.cpp b/src/coreclr/vm/genmeth.cpp
index 021d9e48016595..8cb4eb34a75fc8 100644
--- a/src/coreclr/vm/genmeth.cpp
+++ b/src/coreclr/vm/genmeth.cpp
@@ -91,7 +91,7 @@ static MethodDesc* CreateMethodDesc(LoaderAllocator *pAllocator,
                                      classification,
                                      TRUE /* fNonVtableSlot*/,
                                      fNativeCodeSlot,
-                                     pTemplateMD->IsAsyncThunkMethod(),
+                                     pTemplateMD->HasAsyncMethodData(),
                                      pMT,
                                      pamTracker);
 
@@ -118,9 +118,9 @@ static MethodDesc* CreateMethodDesc(LoaderAllocator *pAllocator,
     {
         pMD->SetIsIntrinsic();
     }
-    if (pTemplateMD->IsAsyncThunkMethod())
+    if (pTemplateMD->HasAsyncMethodData())
     {
-        pMD->SetIsAsyncThunkMethod();
+        pMD->SetHasAsyncMethodData();
     }
 
 #ifdef FEATURE_METADATA_UPDATER
@@ -133,9 +133,9 @@ static MethodDesc* CreateMethodDesc(LoaderAllocator *pAllocator,
     pMD->SetMemberDef(token);
     pMD->SetSlot(pTemplateMD->GetSlot());
 
-    if (pTemplateMD->IsAsyncThunkMethod())
+    if (pTemplateMD->HasAsyncMethodData())
     {
-        *pMD->GetAddrOfAsyncThunkData() = pTemplateMD->GetAsyncThunkData();
+        *pMD->GetAddrOfAsyncMethodData() = pTemplateMD->GetAsyncMethodData();
     }
 
 #ifdef _DEBUG
diff --git a/src/coreclr/vm/ilstubcache.cpp b/src/coreclr/vm/ilstubcache.cpp
index c467281ba830f7..cb029595955016 100644
--- a/src/coreclr/vm/ilstubcache.cpp
+++ b/src/coreclr/vm/ilstubcache.cpp
@@ -185,7 +185,7 @@ MethodDesc* ILStubCache::CreateNewMethodDesc(LoaderHeap* pCreationHeap, MethodTa
                                                            mcDynamic,
                                                            TRUE /* fNonVtableSlot */,
                                                            TRUE /* fNativeCodeSlot */,
-                                                           FALSE /* IsAsyncThunkMethod */,
+                                                           FALSE /* HasAsyncMethodData */,
                                                            pMT,
                                                            pamTracker);
 
diff --git a/src/coreclr/vm/method.cpp b/src/coreclr/vm/method.cpp
index f8d5bf993a1f02..803301dd19749b 100644
--- a/src/coreclr/vm/method.cpp
+++ b/src/coreclr/vm/method.cpp
@@ -91,14 +91,14 @@ const BYTE MethodDesc::s_ClassificationSizeTable[] = {
     METHOD_DESC_SIZES(sizeof(MethodImpl) + sizeof(NativeCodeSlot)),
     METHOD_DESC_SIZES(sizeof(NonVtableSlot) + sizeof(MethodImpl) + sizeof(NativeCodeSlot)),
 
-    METHOD_DESC_SIZES(sizeof(AsyncThunkData)),
-    METHOD_DESC_SIZES(sizeof(NonVtableSlot) + sizeof(AsyncThunkData)),
-    METHOD_DESC_SIZES(sizeof(MethodImpl) + sizeof(AsyncThunkData)),
-    METHOD_DESC_SIZES(sizeof(NonVtableSlot) + sizeof(MethodImpl) + sizeof(AsyncThunkData)),
-    METHOD_DESC_SIZES(sizeof(NativeCodeSlot) + sizeof(AsyncThunkData)),
-    METHOD_DESC_SIZES(sizeof(NonVtableSlot) + sizeof(NativeCodeSlot) + sizeof(AsyncThunkData)),
-    METHOD_DESC_SIZES(sizeof(MethodImpl) + sizeof(NativeCodeSlot) + sizeof(AsyncThunkData)),
-    METHOD_DESC_SIZES(sizeof(NonVtableSlot) + sizeof(MethodImpl) + sizeof(NativeCodeSlot) + sizeof(AsyncThunkData)),
+    METHOD_DESC_SIZES(sizeof(AsyncMethodData)),
+    METHOD_DESC_SIZES(sizeof(NonVtableSlot) + sizeof(AsyncMethodData)),
+    METHOD_DESC_SIZES(sizeof(MethodImpl) + sizeof(AsyncMethodData)),
+    METHOD_DESC_SIZES(sizeof(NonVtableSlot) + sizeof(MethodImpl) + sizeof(AsyncMethodData)),
+    METHOD_DESC_SIZES(sizeof(NativeCodeSlot) + sizeof(AsyncMethodData)),
+    METHOD_DESC_SIZES(sizeof(NonVtableSlot) + sizeof(NativeCodeSlot) + sizeof(AsyncMethodData)),
+    METHOD_DESC_SIZES(sizeof(MethodImpl) + sizeof(NativeCodeSlot) + sizeof(AsyncMethodData)),
+    METHOD_DESC_SIZES(sizeof(NonVtableSlot) + sizeof(MethodImpl) + sizeof(NativeCodeSlot) + sizeof(AsyncMethodData)),
 };
 
 #ifndef FEATURE_COMINTEROP
@@ -134,7 +134,7 @@ SIZE_T MethodDesc::SizeOf()
         | mdcHasNonVtableSlot
         | mdcMethodImpl
         | mdcHasNativeCodeSlot
-        | mdcIsAsyncThunkMethod)];
+        | mdcHasAsyncMethodData)];
 
     return size;
 }
@@ -407,7 +407,7 @@ void MethodDesc::GetSig(PCCOR_SIGNATURE *ppSig, DWORD *pcSig)
     }
     if (IsAsyncThunkMethod())
     {
-        Signature sig = GetAsyncThunkData().sig;
+        Signature sig = GetAsyncMethodData().sig;
         *ppSig = sig.GetRawSig();
         *pcSig = sig.GetRawSigLen();
         return;
@@ -986,15 +986,15 @@ PTR_PCODE MethodDesc::GetAddrOfNativeCodeSlot()
 }
 
 //*******************************************************************************
-AsyncThunkData* MethodDesc::GetAddrOfAsyncThunkData()
+AsyncMethodData* MethodDesc::GetAddrOfAsyncMethodData()
 {
     WRAPPER_NO_CONTRACT;
 
-    _ASSERTE(IsAsyncThunkMethod());
+    _ASSERTE(IsAsyncThunkMethod() || IsAsync2Method());
 
     SIZE_T size = s_ClassificationSizeTable[m_wFlags & (mdcClassification | mdcHasNonVtableSlot |  mdcMethodImpl | mdcHasNativeCodeSlot)];
 
-    return (AsyncThunkData*)(dac_cast<TADDR>(this) + size);
+    return (AsyncMethodData*)(dac_cast<TADDR>(this) + size);
 }
 
 //*******************************************************************************
@@ -1711,7 +1711,7 @@ MethodDesc* MethodDesc::StripMethodInstantiation()
 
 //*******************************************************************************
 MethodDescChunk *MethodDescChunk::CreateChunk(LoaderHeap *pHeap, DWORD methodDescCount,
-    DWORD classification, BOOL fNonVtableSlot, BOOL fNativeCodeSlot, BOOL fAsyncThunkData, MethodTable *pInitialMT, AllocMemTracker *pamTracker)
+    DWORD classification, BOOL fNonVtableSlot, BOOL fNativeCodeSlot, BOOL fAsyncMethodData, MethodTable *pInitialMT, AllocMemTracker *pamTracker)
 {
     CONTRACT(MethodDescChunk *)
     {
@@ -1735,8 +1735,8 @@ MethodDescChunk *MethodDescChunk::CreateChunk(LoaderHeap *pHeap, DWORD methodDes
     if (fNativeCodeSlot)
         oneSize += sizeof(MethodDesc::NativeCodeSlot);
 
-    if (fAsyncThunkData)
-        oneSize += sizeof(AsyncThunkData);
+    if (fAsyncMethodData)
+        oneSize += sizeof(AsyncMethodData);
 
     _ASSERTE((oneSize & MethodDesc::ALIGNMENT_MASK) == 0);
 
@@ -1778,8 +1778,8 @@ MethodDescChunk *MethodDescChunk::CreateChunk(LoaderHeap *pHeap, DWORD methodDes
                 pMD->SetHasNonVtableSlot();
             if (fNativeCodeSlot)
                 pMD->SetHasNativeCodeSlot();
-            if (fAsyncThunkData)
-                pMD->SetIsAsyncThunkMethod();
+            if (fAsyncMethodData)
+                pMD->SetHasAsyncMethodData();
 
             _ASSERTE(pMD->SizeOf() == oneSize);
 
diff --git a/src/coreclr/vm/method.hpp b/src/coreclr/vm/method.hpp
index d16182d8e54273..7fc41ad768568e 100644
--- a/src/coreclr/vm/method.hpp
+++ b/src/coreclr/vm/method.hpp
@@ -56,16 +56,17 @@ EXTERN_C VOID STDCALL NDirectImportThunk();
 #define METHOD_TOKEN_RANGE_BIT_COUNT (24 - METHOD_TOKEN_REMAINDER_BIT_COUNT)
 #define METHOD_TOKEN_RANGE_MASK ((1 << METHOD_TOKEN_RANGE_BIT_COUNT) - 1)
 
-enum class AsyncThunkType
+enum class AsyncMethodType
 {
-    NotAThunk,
+    NotAsync,
     TaskToAsync,
-    AsyncToTask
+    AsyncToTask,
+    Async
 };
 
-struct AsyncThunkData
+struct AsyncMethodData
 {
-    AsyncThunkType type;
+    AsyncMethodType type;
     Signature sig;
 };
 
@@ -152,8 +153,8 @@ enum MethodDescClassification
     // Has slot for native code
     mdcHasNativeCodeSlot                = 0x0020,
 
-    // IsAsyncThunkMethod
-    mdcIsAsyncThunkMethod               = 0x0040,
+    // HasAsyncMethodData
+    mdcHasAsyncMethodData               = 0x0040,
 
     // Method is static
     mdcStatic                           = 0x0080,
@@ -1405,8 +1406,8 @@ class MethodDesc
     BOOL SetNativeCodeInterlocked(PCODE addr, PCODE pExpected = NULL);
 
     PTR_PCODE GetAddrOfNativeCodeSlot();
-    AsyncThunkData *GetAddrOfAsyncThunkData();
-    const AsyncThunkData& GetAsyncThunkData() { _ASSERTE(IsAsyncThunkMethod()); return *GetAddrOfAsyncThunkData(); }
+    AsyncMethodData *GetAddrOfAsyncMethodData();
+    const AsyncMethodData& GetAsyncMethodData() { _ASSERTE(IsAsyncThunkMethod()); return *GetAddrOfAsyncMethodData(); }
 
     BOOL MayHaveNativeCode();
 
@@ -1719,15 +1720,29 @@ class MethodDesc
     inline bool IsAsyncThunkMethod()
     {
         LIMITED_METHOD_DAC_CONTRACT;
-        return (m_wFlags & mdcIsAsyncThunkMethod) != 0;
+        if (!HasAsyncMethodData())
+            return false;
+        auto asyncType = GetAddrOfAsyncMethodData()->type;
+        return asyncType == AsyncMethodType::AsyncToTask || asyncType == AsyncMethodType::TaskToAsync;
     }
 
-    bool IsAsync2Method();
+    inline bool IsAsync2Method()
+    {
+        if (!HasAsyncMethodData())
+            return false;
+        auto asyncType = GetAddrOfAsyncMethodData()->type;
+        return asyncType == AsyncMethodType::Async || asyncType == AsyncMethodType::AsyncToTask;
+    }
+
+    inline bool HasAsyncMethodData()
+    {
+        return (m_wFlags & mdcHasAsyncMethodData) != 0;
+    }
 
-    inline void SetIsAsyncThunkMethod()
+    inline void SetHasAsyncMethodData()
     {
         LIMITED_METHOD_CONTRACT;
-        m_wFlags |= mdcIsAsyncThunkMethod;
+        m_wFlags |= mdcHasAsyncMethodData;
     }
 
 #ifdef FEATURE_METADATA_UPDATER
@@ -2192,7 +2207,7 @@ class MethodDescChunk
                                         DWORD classification,
                                         BOOL fNonVtableSlot,
                                         BOOL fNativeCodeSlot,
-                                        BOOL fAsyncThunkData,
+                                        BOOL fAsyncMethodData,
                                         MethodTable *initialMT,
                                         class AllocMemTracker *pamTracker);
 
diff --git a/src/coreclr/vm/methodtablebuilder.cpp b/src/coreclr/vm/methodtablebuilder.cpp
index c29c2f2554daa4..bdffbf4cb19028 100644
--- a/src/coreclr/vm/methodtablebuilder.cpp
+++ b/src/coreclr/vm/methodtablebuilder.cpp
@@ -1030,7 +1030,7 @@ MethodTableBuilder::bmtMDMethod::bmtMDMethod(
       m_dwImplAttrs(dwImplAttrs),
       m_dwRVA(dwRVA),
       m_type(type),
-      m_asyncThunkType(AsyncThunkType::NotAThunk),
+      m_asyncMethodType(AsyncMethodType::NotAsync),
       m_implType(implType),
       m_methodSig(pOwningType->GetModule(),
                   tok,
@@ -1056,7 +1056,7 @@ MethodTableBuilder::bmtMDMethod::bmtMDMethod(
     DWORD dwImplAttrs,
     DWORD dwRVA,
     Signature sig,
-    AsyncThunkType thunkType,
+    AsyncMethodType thunkType,
     MethodClassification type,
     METHOD_IMPL_TYPE implType)
     : m_pOwningType(pOwningType),
@@ -1064,7 +1064,7 @@ MethodTableBuilder::bmtMDMethod::bmtMDMethod(
       m_dwImplAttrs(dwImplAttrs),
       m_dwRVA(dwRVA),
       m_type(type),
-      m_asyncThunkType(thunkType),
+      m_asyncMethodType(thunkType),
       m_implType(implType),
       m_methodSig(pOwningType->GetModule(),
                   tok,
@@ -3547,12 +3547,18 @@ MethodTableBuilder::EnumerateClassMethods()
                     dwMethodRVA,
                     type,
                     implType);
+
+                if ((asyncMethodType == AsyncTaskMethod::Async2MethodThatCannotBeImplementedByTask) || (asyncMethodType == AsyncTaskMethod::Async2Method))
+                {
+                    pNewMethod->SetIsAsync2Method();
+                }
+
                 pDeclaredMethod = pNewMethod;
             }
             else
             {
                 ULONG cAsyncThunkMemberSignature = cMemberSignature;
-                AsyncThunkType thunkType;
+                AsyncMethodType thunkType;
                 ULONG originalTokenOffsetFromAsyncDetailsOffset;
                 ULONG newTokenOffsetFromAsyncDetailsOffset;
                 ULONG originalPrefixSize;
@@ -3565,7 +3571,7 @@ MethodTableBuilder::EnumerateClassMethods()
                     cAsyncThunkMemberSignature += 2;
                     originalTokenOffsetFromAsyncDetailsOffset = 1;
                     newTokenOffsetFromAsyncDetailsOffset = 2;
-                    thunkType = AsyncThunkType::AsyncToTask;
+                    thunkType = AsyncMethodType::AsyncToTask;
                     originalPrefixSize = 1;
                     newPrefixSize = 2;
                     originalSuffixSize = 0;
@@ -3576,7 +3582,7 @@ MethodTableBuilder::EnumerateClassMethods()
                     cAsyncThunkMemberSignature -= 2;
                     originalTokenOffsetFromAsyncDetailsOffset = 2;
                     newTokenOffsetFromAsyncDetailsOffset = 1;
-                    thunkType = AsyncThunkType::TaskToAsync;
+                    thunkType = AsyncMethodType::TaskToAsync;
                     originalPrefixSize = 2;
                     newPrefixSize = 1;
                     originalSuffixSize = 1;
@@ -5482,8 +5488,8 @@ MethodTableBuilder::InitNewMethodDesc(
     if (NeedsNativeCodeSlot(pMethod))
         pNewMD->SetHasNativeCodeSlot();
 
-    if (pMethod->GetAsyncThunkType() != AsyncThunkType::NotAThunk)
-        pNewMD->SetIsAsyncThunkMethod();
+    if (pMethod->GetAsyncMethodType() != AsyncMethodType::NotAsync)
+        pNewMD->SetHasAsyncMethodData();
 
     // Now we know the classification we can allocate the correct type of
     // method desc and perform any classification specific initialization.
@@ -5513,7 +5519,7 @@ MethodTableBuilder::InitNewMethodDesc(
 #endif // _DEBUG
 
     Signature sig;
-    if (pMethod->GetAsyncThunkType() != AsyncThunkType::NotAThunk)
+    if (pMethod->IsAsyncThunk())
     {
         sig = pMethod->GetMethodSignature().GetSignatureClass();
     }
@@ -5529,7 +5535,7 @@ MethodTableBuilder::InitNewMethodDesc(
                    GetMDImport(),
                    pName,
                    sig,
-                   pMethod->GetAsyncThunkType()
+                   pMethod->GetAsyncMethodType()
                    COMMA_INDEBUG(pszDebugMethodNameCopy)
                    COMMA_INDEBUG(GetDebugClassName())
                    COMMA_INDEBUG("") // FIX this happens on global methods, give better info
@@ -5936,7 +5942,7 @@ MethodTableBuilder::ProcessInexactMethodImpls()
             continue;
         }
 
-        AsyncVariantLookup asyncVariantOfDeclToFind = it->GetAsyncThunkType() == AsyncThunkType::NotAThunk ? AsyncVariantLookup::MatchingAsyncVariant : AsyncVariantLookup::AsyncOtherVariant;
+        AsyncVariantLookup asyncVariantOfDeclToFind = !it->IsAsyncThunk() ? AsyncVariantLookup::MatchingAsyncVariant : AsyncVariantLookup::AsyncOtherVariant;
 
         // If this method serves as the BODY of a MethodImpl specification, then
         // we should iterate all the MethodImpl's for this class and see just how many
@@ -6079,7 +6085,7 @@ MethodTableBuilder::ProcessMethodImpls()
             continue;
         }
 
-        AsyncVariantLookup asyncVariantOfDeclToFind = it->GetAsyncThunkType() == AsyncThunkType::NotAThunk ? AsyncVariantLookup::MatchingAsyncVariant : AsyncVariantLookup::AsyncOtherVariant;
+        AsyncVariantLookup asyncVariantOfDeclToFind = !it->IsAsyncThunk() ? AsyncVariantLookup::MatchingAsyncVariant : AsyncVariantLookup::AsyncOtherVariant;
 
         // If this method serves as the BODY of a MethodImpl specification, then
         // we should iterate all the MethodImpl's for this class and see just how many
@@ -6431,7 +6437,7 @@ MethodTableBuilder::InitMethodDesc(
     IMDInternalImport * pIMDII,     // Needed for NDirect, EEImpl(Delegate) cases
     LPCSTR              pMethodName, // Only needed for mcEEImpl (Delegate) case
     Signature           sig, // Only needed for the Async2 Thunk case
-    AsyncThunkType      thunkType
+    AsyncMethodType      thunkType
     COMMA_INDEBUG(LPCUTF8 pszDebugMethodName)
     COMMA_INDEBUG(LPCUTF8 pszDebugClassName)
     COMMA_INDEBUG(LPCUTF8 pszDebugMethodSignature)
@@ -6600,9 +6606,9 @@ MethodTableBuilder::InitMethodDesc(
 #endif // !_DEBUG
         pNewMD->SetSynchronized();
 
-    if (thunkType != AsyncThunkType::NotAThunk)
+    if (thunkType != AsyncMethodType::NotAsync)
     {
-        AsyncThunkData* pThunkData = pNewMD->GetAddrOfAsyncThunkData();
+        AsyncMethodData* pThunkData = pNewMD->GetAddrOfAsyncMethodData();
         pThunkData->sig = sig;
         pThunkData->type = thunkType;
     }
@@ -7342,8 +7348,8 @@ VOID MethodTableBuilder::AllocAndInitMethodDescs()
         if (NeedsNativeCodeSlot(*it))
             size += sizeof(MethodDesc::NativeCodeSlot);
         
-        if (it->GetAsyncThunkType() != AsyncThunkType::NotAThunk)
-            size += sizeof(AsyncThunkData);
+        if (it->GetAsyncMethodType() != AsyncMethodType::NotAsync)
+            size += sizeof(AsyncMethodData);
 
         // See comment in AllocAndInitMethodDescChunk
         if (NeedsTightlyBoundUnboxingStub(*it))
diff --git a/src/coreclr/vm/methodtablebuilder.h b/src/coreclr/vm/methodtablebuilder.h
index 4b34c5df88702c..4392e6417a980d 100644
--- a/src/coreclr/vm/methodtablebuilder.h
+++ b/src/coreclr/vm/methodtablebuilder.h
@@ -958,7 +958,7 @@ class MethodTableBuilder
             DWORD dwImplAttrs,
             DWORD dwRVA,
             Signature sig,
-            AsyncThunkType thunkType,
+            AsyncMethodType thunkType,
             MethodClassification type,
             METHOD_IMPL_TYPE implType);
 
@@ -1062,10 +1062,20 @@ class MethodTableBuilder
         GetRVA() const
             { LIMITED_METHOD_CONTRACT; return m_dwRVA; }
 
-        AsyncThunkType GetAsyncThunkType() const
+        bool IsAsyncThunk() const
+        {
+            return GetAsyncMethodType() == AsyncMethodType::AsyncToTask || GetAsyncMethodType() == AsyncMethodType::TaskToAsync;
+        }
+
+        void SetIsAsync2Method()
+        {
+            m_asyncMethodType = AsyncMethodType::Async;
+        }
+
+        AsyncMethodType GetAsyncMethodType() const
         {
             LIMITED_METHOD_CONTRACT;
-            return m_asyncThunkType;
+            return m_asyncMethodType;
         }
 
         bmtMDMethod *     GetAsyncOtherVariant() const { return m_asyncOtherVariant; }
@@ -1079,7 +1089,7 @@ class MethodTableBuilder
         DWORD             m_dwImplAttrs;
         DWORD             m_dwRVA;
         MethodClassification  m_type;               // Specific MethodDesc flavour
-        AsyncThunkType m_asyncThunkType;
+        AsyncMethodType m_asyncMethodType;
         METHOD_IMPL_TYPE  m_implType;           // Whether or not the method is a methodImpl body
         MethodSignature   m_methodSig;
         bmtMDMethod*      m_asyncOtherVariant = NULL;
@@ -1212,26 +1222,6 @@ class MethodTableBuilder
         MethodDesc *
         GetMethodDesc() const;
 
-/*        bmtMethodHandle GetAsyncOtherVariant() const
-        {
-            if (IsRTMethod())
-            {
-                bmtRTMethod *pRTMethod = AsRTMethod();
-                MethodDesc *pMD = pRTMethod->GetMethodDesc();
-                MethodDesc *pRTOtherMethod = pMD->GetAsyncOtherVariant();
-                _ASSERTE(pRTOtherMethod != NULL);
-                _ASSERTE(pRTOtherMethod->IsAsyncThunkMethod() || pRTMethod->GetMethodDesc()->IsAsyncThunkMethod());
-                _ASSERTE(FALSE);
-            }
-            else
-            {
-                bmtMDMethod* pMDOtherVariant = AsMDMethod()->GetAsyncOtherVariant();
-                _ASSERTE(pMDOtherVariant != NULL);
-                _ASSERTE(pMDOtherVariant->GetAsyncThunkType() != AsyncThunkType::NotAThunk || AsMDMethod()->GetAsyncThunkType() != AsyncThunkType::NotAThunk);
-                return pMDOtherVariant;
-            }
-        }*/
-
     protected:
         //-----------------------------------------------------------------------------------------
         static const UINT_PTR RTMETHOD_FLAG = 0x1;
@@ -2700,7 +2690,7 @@ class MethodTableBuilder
         IMDInternalImport * pIMDII,  // Needed for NDirect, EEImpl(Delegate) cases
         LPCSTR              pMethodName, // Only needed for mcEEImpl (Delegate) case
         Signature           sig, // Only needed for the async thunk (Async2 Thunk) case
-        AsyncThunkType      thunkType
+        AsyncMethodType      thunkType
         COMMA_INDEBUG(LPCUTF8             pszDebugMethodName)
         COMMA_INDEBUG(LPCUTF8             pszDebugClassName)
         COMMA_INDEBUG(LPCUTF8             pszDebugMethodSignature));

From 718aa39bab9c8ed205a68687b10054c01fb3cedc Mon Sep 17 00:00:00 2001
From: David Wrighton <davidwr@microsoft.com>
Date: Wed, 11 Oct 2023 15:39:28 -0700
Subject: [PATCH 021/203] In theory, this should work?

---
 src/coreclr/vm/amd64/AsmHelpers.asm           | 177 +++++++++++++++++-
 src/coreclr/vm/runtimesuspension.cpp          |  37 ++--
 .../CompilerServices/RuntimeHelpers.cs        |   2 +-
 3 files changed, 201 insertions(+), 15 deletions(-)

diff --git a/src/coreclr/vm/amd64/AsmHelpers.asm b/src/coreclr/vm/amd64/AsmHelpers.asm
index aa1c443cf56f1c..6abe642d1e7e1a 100644
--- a/src/coreclr/vm/amd64/AsmHelpers.asm
+++ b/src/coreclr/vm/amd64/AsmHelpers.asm
@@ -682,5 +682,180 @@ NESTED_END OnCallCountThresholdReachedStub, _TEXT
 
 endif ; FEATURE_TIERED_COMPILATION
 
-        end
+extern PlatformIndependentRestore:proc
+
+regRestoreCase macro RegisterName
+Check&RegisterName&:
+    mov edx, [rax+4]
+    mov RegisterName, [rcx+rdx]
+    add rax, 8
+    jmp RegRestoreLoop
+        endm
+
+regRestoreCaseXmm macro RegisterName
+Check&RegisterName&:
+    mov edx, [rax+4]
+    movdqu RegisterName, [rcx+rdx]
+    add rax, 8
+    jmp RegRestoreLoop
+        endm
 
+NESTED_ENTRY RuntimeSuspension_ResumeTaskletReferenceReturn, _TEXT
+        alloc_stack             48h
+    END_PROLOGUE
+
+        lea r8, [rsp+28h]
+
+        ; Zero out integerRegister and AddressInMethodToRestoreTo
+        ; Probably not needed in production code, but convenient for now
+        xor rax, rax
+        mov [r8], rax
+        mov [r8+8h], rax
+
+        ; Fill in pFutureRSPLocation
+        lea rax, [rsp+50]
+        mov [r8+10h], rax
+
+        ; Capture return address
+        mov rax, [rax]
+        mov [r8+18h], rax
+
+        call PlatformIndependentRestore
+        ; RAX now points at the RegRestore array
+        mov rcx, [rsp+28h + 10h]
+        ; RCX now has the value of the future RSP
+
+        .data
+switchTable     qword    Checkrbx, Checkrbp, Checkrdi, Checkrsi, Checkr12, Checkr13, Checkr14, Checkr15, Checkxmm6, Checkxmm7, Checkxmm8, Checkxmm9, Checkxmm10, Checkxmm11, Checkxmm12, Checkxmm13, Checkxmm14, Checkxmm15, ReturnRegisters
+        .code
+RegRestoreLoop:
+    mov edx, [rax]
+    ; edx now has the value of the reg field of the RegRestore structure
+    lea r8, [switchTable]
+    mov r8, [r8 + rdx*8]
+    jmp r8
+ReturnRegisters:
+    mov rax, [rsp+28h + 0h]
+    MOVDQU xmm0, [rsp+28h + 0h]
+    mov rdx, [rsp+28h + 0h] ; Load address of where to jmp into ecx
+    mov rsp, rcx
+    jmp rdx
+regRestoreCase rbx
+regRestoreCase rbp
+regRestoreCase rdi
+regRestoreCase rsi
+regRestoreCase r12
+regRestoreCase r13
+regRestoreCase r14
+regRestoreCase r15
+regRestoreCaseXmm xmm6
+regRestoreCaseXmm xmm7
+regRestoreCaseXmm xmm8
+regRestoreCaseXmm xmm9
+regRestoreCaseXmm xmm10
+regRestoreCaseXmm xmm11
+regRestoreCaseXmm xmm12
+regRestoreCaseXmm xmm13
+regRestoreCaseXmm xmm14
+regRestoreCaseXmm xmm15
+NESTED_END RuntimeSuspension_ResumeTaskletReferenceReturn, _TEXT
+
+LEAF_ENTRY RuntimeSuspension_ResumeTaskletIntegerRegisterReturn, _TEXT
+        jmp RuntimeSuspension_ResumeTaskletReferenceReturn
+LEAF_END RuntimeSuspension_ResumeTaskletIntegerRegisterReturn, _TEXT
+
+unwindRegCase macro RegisterName
+Unwind&RegisterName&:
+    mov edx, [r8+4]
+    mov RegisterName, [rsp+rdx]
+    add r8, 8
+    jmp RegUnwindLoop
+        endm
+
+unwindRegCaseXmm macro RegisterName
+Unwind&RegisterName&:
+    mov edx, [r8+4]
+    movdqu RegisterName, [rsp+rdx]
+    add r8, 8
+    jmp RegUnwindLoop
+        endm
+
+LEAF_ENTRY RuntimeSuspension_UnwindToFunctionWithAsyncFrame, _TEXT
+
+;    // Psuedocode...
+;    Tasklet* pCurTasklet = topTasklet;
+;    uintptr_t curSP = SP + offset;
+;    uintptr_t ip = NULL;
+;    for (uint32_t i = 0; i < count; i++)
+;    {
+;        RegRestore* pRegRestore = pCurTasklet->pStackDataInfo->RegistersToRestore;
+;        while (pRegRestore->reg != RegisterToRestore::ReturnRegisters)
+;        {
+;            RestoreReg(pRegRestore);
+;        }
+;        ip = *(uintptr_t*)(curSP + pCurTasklet->pStackDataInfo->ReturnAddressOffset);
+;        curSP += pCurTasklet->pStackDataInfo->StackRequirement;
+;    }
+;    Jmp ip
+
+
+; rcx is pCurTasklet
+; rdx is count of tasklets to unwind
+; rsp is current stack pointer
+; rax is the putative new ip
+; rax is also the scratch Register
+; r10 is second scratch register
+; r8 is pRegRestore
+; r9 is pStackDataInfo
+        pop rax; Pop off saved IP, so that we start with the right SP set_frame
+NewTaskletUnwind:
+        mov r9, [rcx+28h] ; Load r9 with the pStackDataInfo value on the current tasklet
+        mov r8, [r9+30h] ; Load r8 with the pRegRestore value
+
+        .data
+unwindSwitchTable     qword    Unwindrbx, Unwindrbp, Unwindrdi, Unwindrsi, Unwindr12, Unwindr13, Unwindr14, Unwindr15, Unwindxmm6, Unwindxmm7, Unwindxmm8, Unwindxmm9, Unwindxmm10, Unwindxmm11, Unwindxmm12, Unwindxmm13, Unwindxmm14, Unwindxmm15, UnwindReturnRegisters
+        .code
+RegUnwindLoop:
+        mov eax, [r8]
+        ; eax now has the value of the reg field of the RegRestore structure
+        lea r10, [unwindSwitchTable]
+        mov r10, [r10 + rax*8]
+        jmp r10
+unwindRegCase rbx
+unwindRegCase rbp
+unwindRegCase rdi
+unwindRegCase rsi
+unwindRegCase r12
+unwindRegCase r13
+unwindRegCase r14
+unwindRegCase r15
+unwindRegCaseXmm xmm6
+unwindRegCaseXmm xmm7
+unwindRegCaseXmm xmm8
+unwindRegCaseXmm xmm9
+unwindRegCaseXmm xmm10
+unwindRegCaseXmm xmm11
+unwindRegCaseXmm xmm12
+unwindRegCaseXmm xmm13
+unwindRegCaseXmm xmm14
+unwindRegCaseXmm xmm15
+UnwindReturnRegisters:
+        ; Load return address into RAX in case we are done
+        mov eax, [r9 + 0ch] ; Put ReturnAddressOffset into rax
+        mov rax, [rsp+rax]
+        
+        ; Adjust rsp to callers rsp
+        mov r10, [r9] ; Load StackDataInfo.StackRequirement
+        sub r10, [r9 + 18h] ; Subtract off the CbArgs (stack args amount)
+        add rsp, r10 ; Adjust rsp as requested
+
+        mov rcx, [rcx] ; Adjust to callers tasklet
+        ; Adjust count of tasklets to unwind
+        sub edx, 1
+        jnz NewTaskletUnwind
+TaskletUnwindDone:
+        mov rcx, rax ; Move return address into rcx
+        xor rax, rax ; Clear return register in case it is supposed to be an object reference or something  -- TODO, note that for structure returns this will mean we need to slightly adjust the calling convention so that we don't rely on this being a pointer to the return buffer
+        jmp rcx
+LEAF_END RuntimeSuspension_UnwindToFunctionWithAsyncFrame, _TEXT
+        end
\ No newline at end of file
diff --git a/src/coreclr/vm/runtimesuspension.cpp b/src/coreclr/vm/runtimesuspension.cpp
index fdfc254368e348..61f626bd236e80 100644
--- a/src/coreclr/vm/runtimesuspension.cpp
+++ b/src/coreclr/vm/runtimesuspension.cpp
@@ -57,10 +57,11 @@ struct StackDataInfo
     uint32_t UnrecordedDataSize; // From the restored RSP to the data chunk, how many bytes are skipped
     uint32_t StackDataSize;
     uint32_t ReturnAddressOffset;
-    int32_t* ByRefOffsets = NULL; // Negative numbers indicate pinned byrefs
     uint32_t cByRefs;
-    uint32_t* ObjectRefOffsets = NULL;
     uint32_t cObjectRefs;
+    uint32_t CbArgs;
+    int32_t* ByRefOffsets = NULL; // Negative numbers indicate pinned byrefs
+    uint32_t* ObjectRefOffsets = NULL;
     RegRestore* RegistersToRestore = NULL;
 };
 
@@ -123,7 +124,7 @@ struct RestoreFunctionLocals
 };
 
 // The actual restoration function which needs to be written in assembly, sets up enough of a frame on top of the current RSP to call this function
-// Then, once this function returns, iterates through the 
+// Then, once this function returns, iterates through the RegRestore to change over the saved registers to the new values
 extern "C" RegRestore* PlatformIndependentRestore(Tasklet* tasklet, RuntimeAsyncReturnValue* returnValueToFillIn, RestoreFunctionLocals *restoreLocals)
 {
     // Compute what needs to be adjusted in terms of byrefs for 
@@ -248,9 +249,13 @@ struct TaskletCaptureData
 
 struct RuntimeSuspensionEnumData
 {
-    RuntimeSuspensionEnumData(const CQuickArrayList<RegRestore>& restoreRegLocations, REGDISPLAY* pRD_) :
+    RuntimeSuspensionEnumData(const CQuickArrayList<RegRestore>& restoreRegLocations, REGDISPLAY* pRD_, TaskletCaptureData *taskletCaptureData, CrawlFrame *_pCf, StackDataInfo* _pStackDataInfo, uint8_t* _pStackData) :
         RestoreRegLocations(restoreRegLocations),
-        pRD(pRD_)
+        pRD(pRD_),
+        pTaskletCaptureData(taskletCaptureData),
+        pCf(_pCf),
+        pStackDataInfo(_pStackDataInfo),
+        pStackData(_pStackData)
     {
     }
 
@@ -258,6 +263,10 @@ struct RuntimeSuspensionEnumData
     CQuickArrayList<int32_t> ByRefOffsets;
     const CQuickArrayList<RegRestore>& RestoreRegLocations;
     REGDISPLAY* pRD;
+    TaskletCaptureData *pTaskletCaptureData;
+    CrawlFrame *pCf;
+    StackDataInfo* pStackDataInfo;
+    uint8_t* pStackData;
 
     uint32_t GetOffsetForReg(RegisterToRestore nonVolatileReg)
     {
@@ -328,8 +337,9 @@ StackWalkAction CaptureTaskletsCore(CrawlFrame* pCf, VOID* data)
     byrefAdjustment.Size = sizeofEntireMeaningfulStack;
 
     StackDataInfo stackDataInfo;
-    stackDataInfo.StackRequirement = (uint32_t)(::GetSP(pCf->GetRegisterSet()->pCallerContext) - pCf->GetRegisterSet()->SP);
+    stackDataInfo.StackRequirement = (uint32_t)(::GetSP(pCf->GetRegisterSet()->pCallerContext) - pCf->GetRegisterSet()->SP) + sizeofArgStack;
     stackDataInfo.StackDataSize = sizeofEntireMeaningfulStack;
+    stackDataInfo.UnrecordedDataSize = (uint32_t)taskletCaptureData->stackToIgnoreFromPreviousFrame
 
 #if defined(TARGET_X86) || defined(TARGET_AMD64)
     uintptr_t returnAddressLocation = (uintptr_t) (EECodeManager::GetCallerSp(pCf->GetRegisterSet()) - sizeof(void*));
@@ -390,6 +400,8 @@ StackWalkAction CaptureTaskletsCore(CrawlFrame* pCf, VOID* data)
         else
         {
             int32_t byRefOffset = (int32_t)offset;
+
+            pEnumData->pTaskletCaptureData->AddCopiedByRef(pEnumData->pCf->GetRegisterSet()->SP, (uintptr_t*)(pEnumData->pStackData + offset - pEnumData->pStackDataInfo->UnrecordedDataSize));
             if (isPinned)
             {
                 byRefOffset = -byRefOffset;
@@ -399,7 +411,7 @@ StackWalkAction CaptureTaskletsCore(CrawlFrame* pCf, VOID* data)
         }
     };
 
-    RuntimeSuspensionEnumData enumData(savedRegRestoreData, pCf->GetRegisterSet());
+    RuntimeSuspensionEnumData enumData(savedRegRestoreData, pCf->GetRegisterSet(), taskletCaptureData, pCf, &stackDataInfo, pStackData);
 
     pCM->EnumGcRefs(pCf->GetRegisterSet(),
                     pCf->GetCodeInfo(),
@@ -453,6 +465,8 @@ StackWalkAction CaptureTaskletsCore(CrawlFrame* pCf, VOID* data)
 PORTABILIT_ASSERT()
 #endif
 
+
+
     Tasklet *pTasklet = (Tasklet*)malloc(sizeof(Tasklet));
     memset(pTasklet, 0, sizeof(Tasklet));
     pTasklet->pStackData = pStackData;
@@ -471,7 +485,8 @@ PORTABILIT_ASSERT()
         taskletCaptureData->lastTasklet = pTasklet;
     }
     
-    _ASSERTE(!"Needs to implement doing byref update of the data already captured.. maybe?");
+    // Apply byrefs to newly allocated stuff
+    taskletCaptureData->ApplyByRefRelocsToNewlyAllocatedTasklet(byrefAdjustment);
 
     return SWA_CONTINUE;
 }
@@ -483,6 +498,7 @@ extern "C" Tasklet* QCALLTYPE RuntimeSuspension_CaptureTasklets(QCall::StackCraw
 
     TaskletCaptureData cdata;
     cdata.stackMark = stackMark;
+    cdata.stackLimit = (uintptr_t)taskAsyncData;
     GetThread()->StackWalkFrames(CaptureTaskletsCore, &cdata, FUNCTIONSONLY);
 
     // We should have captured a full stack here. 
@@ -495,8 +511,3 @@ extern "C" void QCALLTYPE RuntimeSuspension_DeleteTasklet(Tasklet* tasklet)
 {
     // Well, leaking isn't a good plan in the long term, but for now it'll be fine
 }
-
-EXTERN_C FCDECL1(void, RuntimeSuspension_UnwindToFunctionWithAsyncFrame, AsyncDataFrame* frame)
-{
-    _ASSERTE(!"Not Yet Implemented");
-}
diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.cs
index 6b1fca886dae69..ea4def03effee9 100644
--- a/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.cs
+++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.cs
@@ -472,10 +472,10 @@ public AsyncDataFrame(Func<RuntimeAsyncMaintainedData> getMaintainedData)
                 _createRuntimeMaintainedData = getMaintainedData;
             }
 
+            public RuntimeAsyncMaintainedData? _maintainedData;
             public StackCrawlMark _crawlMark;
             public void* _next;
             public Func<RuntimeAsyncMaintainedData>? _createRuntimeMaintainedData;
-            public RuntimeAsyncMaintainedData? _maintainedData;
             public Thread _currentThread = Thread.CurrentThread;
 
             // Store current ExecutionContext and SynchronizationContext as "previousXxx".

From 6d1e7b7a49cfba8594581d63bd4c3f6d8bdf314a Mon Sep 17 00:00:00 2001
From: David Wrighton <davidwr@microsoft.com>
Date: Thu, 12 Oct 2023 11:29:45 -0700
Subject: [PATCH 022/203] native part builds

---
 src/coreclr/vm/runtimesuspension.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/coreclr/vm/runtimesuspension.cpp b/src/coreclr/vm/runtimesuspension.cpp
index 61f626bd236e80..2d910e512057d2 100644
--- a/src/coreclr/vm/runtimesuspension.cpp
+++ b/src/coreclr/vm/runtimesuspension.cpp
@@ -339,7 +339,7 @@ StackWalkAction CaptureTaskletsCore(CrawlFrame* pCf, VOID* data)
     StackDataInfo stackDataInfo;
     stackDataInfo.StackRequirement = (uint32_t)(::GetSP(pCf->GetRegisterSet()->pCallerContext) - pCf->GetRegisterSet()->SP) + sizeofArgStack;
     stackDataInfo.StackDataSize = sizeofEntireMeaningfulStack;
-    stackDataInfo.UnrecordedDataSize = (uint32_t)taskletCaptureData->stackToIgnoreFromPreviousFrame
+    stackDataInfo.UnrecordedDataSize = (uint32_t)taskletCaptureData->stackToIgnoreFromPreviousFrame;
 
 #if defined(TARGET_X86) || defined(TARGET_AMD64)
     uintptr_t returnAddressLocation = (uintptr_t) (EECodeManager::GetCallerSp(pCf->GetRegisterSet()) - sizeof(void*));

From d20bb17a71a1d90dfda093f1b60242dade31e8fd Mon Sep 17 00:00:00 2001
From: Vladimir Sadov <vsadov@microsoft.com>
Date: Thu, 12 Oct 2023 18:29:29 +0000
Subject: [PATCH 023/203] Merged PR 34323: make things build

make things build
---
 .../src/CompatibilitySuppressions.xml         | 40 +++++++++++++++++++
 .../CompilerServices/RuntimeHelpers.cs        |  3 ++
 .../src/System/Threading/Tasks/Future.cs      |  3 ++
 3 files changed, 46 insertions(+)

diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/CompatibilitySuppressions.xml b/src/coreclr/nativeaot/System.Private.CoreLib/src/CompatibilitySuppressions.xml
index d3fbd808a6d691..376c3ecb1ee8d7 100644
--- a/src/coreclr/nativeaot/System.Private.CoreLib/src/CompatibilitySuppressions.xml
+++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/CompatibilitySuppressions.xml
@@ -1,6 +1,10 @@
 ﻿<?xml version="1.0" encoding="utf-8"?>
 <!-- https://learn.microsoft.com/en-us/dotnet/fundamentals/package-validation/diagnostic-ids -->
 <Suppressions xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
+  <Suppression>
+    <DiagnosticId>CP0001</DiagnosticId>
+    <Target>T:Internal.Console</Target>
+  </Suppression>
   <Suppression>
     <DiagnosticId>CP0001</DiagnosticId>
     <Target>T:Internal.DeveloperExperience.DeveloperExperience</Target>
@@ -869,6 +873,10 @@
     <DiagnosticId>CP0001</DiagnosticId>
     <Target>T:System.Runtime.CompilerServices.ForceLazyDictionaryAttribute</Target>
   </Suppression>
+  <Suppression>
+    <DiagnosticId>CP0001</DiagnosticId>
+    <Target>T:System.Runtime.CompilerServices.ICastable</Target>
+  </Suppression>
   <Suppression>
     <DiagnosticId>CP0001</DiagnosticId>
     <Target>T:System.Runtime.CompilerServices.StaticClassConstructionContext</Target>
@@ -929,6 +937,14 @@
     <DiagnosticId>CP0001</DiagnosticId>
     <Target>T:System.Threading.LockHolder</Target>
   </Suppression>
+  <Suppression>
+    <DiagnosticId>CP0002</DiagnosticId>
+    <Target>F:System.Resources.ResourceManager.BaseNameField</Target>
+  </Suppression>
+  <Suppression>
+    <DiagnosticId>CP0002</DiagnosticId>
+    <Target>F:System.Resources.ResourceSet.Reader</Target>
+  </Suppression>
   <Suppression>
     <DiagnosticId>CP0002</DiagnosticId>
     <Target>M:System.ModuleHandle.#ctor(System.Reflection.Module)</Target>
@@ -945,6 +961,30 @@
     <DiagnosticId>CP0002</DiagnosticId>
     <Target>M:System.TypedReference.get_IsNull</Target>
   </Suppression>
+  <Suppression>
+    <DiagnosticId>CP0002</DiagnosticId>
+    <Target>M:System.Runtime.CompilerServices.RuntimeHelpers.AwaitAwaiterFromRuntimeAsync``1(``0)</Target>
+    <Left>ref/net9.0/System.Private.CoreLib.dll</Left>
+    <Right>lib/net9.0/System.Private.CoreLib.dll</Right>
+  </Suppression>
+  <Suppression>
+    <DiagnosticId>CP0002</DiagnosticId>
+    <Target>M:System.Runtime.CompilerServices.RuntimeHelpers.AwaitAwaiterFromRuntimeAsync``2(``1)</Target>
+    <Left>ref/net9.0/System.Private.CoreLib.dll</Left>
+    <Right>lib/net9.0/System.Private.CoreLib.dll</Right>
+  </Suppression>
+  <Suppression>
+    <DiagnosticId>CP0002</DiagnosticId>
+    <Target>M:System.Runtime.CompilerServices.RuntimeHelpers.UnsafeAwaitAwaiterFromRuntimeAsync``1(``0)</Target>
+    <Left>ref/net9.0/System.Private.CoreLib.dll</Left>
+    <Right>lib/net9.0/System.Private.CoreLib.dll</Right>
+  </Suppression>
+  <Suppression>
+    <DiagnosticId>CP0002</DiagnosticId>
+    <Target>M:System.Runtime.CompilerServices.RuntimeHelpers.UnsafeAwaitAwaiterFromRuntimeAsync``2(``1)</Target>
+    <Left>ref/net9.0/System.Private.CoreLib.dll</Left>
+    <Right>lib/net9.0/System.Private.CoreLib.dll</Right>
+  </Suppression>
   <Suppression>
     <DiagnosticId>CP0014</DiagnosticId>
     <Target>M:System.Runtime.CompilerServices.RuntimeHelpers.GetObjectValue(System.Object)-&gt;object?:[T:System.Diagnostics.CodeAnalysis.NotNullIfNotNullAttribute]</Target>
diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.cs
index ea4def03effee9..e8f89f00833555 100644
--- a/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.cs
+++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.cs
@@ -118,6 +118,8 @@ internal static bool IsPrimitiveType(this CorElementType et)
         internal static bool IsKnownConstant(int t) => false;
 #pragma warning restore IDE0060
 
+#if !NATIVEAOT
+
         // TODO, this method should be marked so that it is only callable from a runtime async method
         public static TResult UnsafeAwaitAwaiterFromRuntimeAsync<TResult, TAwaiter>(TAwaiter awaiter) where TAwaiter : ICriticalNotifyCompletion2<TResult>
         {
@@ -623,5 +625,6 @@ private static unsafe void AbortSuspend()
             maintainedData._oldTaskletNext = null;
             maintainedData._suspendActive = false;
         }
+#endif
     }
 }
diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/Future.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/Future.cs
index 69f8bb93534906..394609486f3c3c 100644
--- a/src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/Future.cs
+++ b/src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/Future.cs
@@ -15,6 +15,8 @@
 
 namespace System.Threading.Tasks
 {
+
+#if !NATIVEAOT
 #pragma warning disable CA1822
     // Used to implement Runtime implemented Task suspension handling
     internal struct RuntimeTaskState<TResult>
@@ -38,6 +40,7 @@ public Task<TResult> FromResult(TResult result)
         public Task<TResult> FromException(Exception e) { return Task.FromException<TResult>(e); }
     }
 #pragma warning restore CA1822
+#endif
 
     /// <summary>
     /// Represents an asynchronous operation that produces a result at some time in the future.

From aee9b9f17c335c347ed9f557212763f348ce4fe2 Mon Sep 17 00:00:00 2001
From: David Wrighton <davidwr@microsoft.com>
Date: Fri, 13 Oct 2023 14:28:54 -0700
Subject: [PATCH 024/203] It successfully captures... and unwind doesn't quite
 work

---
 src/coreclr/inc/eetwain.h                     |  1 +
 src/coreclr/vm/eetwain.cpp                    | 12 ++-
 src/coreclr/vm/gcinfodecoder.cpp              |  2 +-
 src/coreclr/vm/method.cpp                     |  2 +-
 src/coreclr/vm/methodtablebuilder.cpp         |  4 +-
 src/coreclr/vm/runtimesuspension.cpp          | 35 +++++--
 src/coreclr/vm/runtimesuspension.h            |  2 +-
 .../CompilerServices/RuntimeHelpers.cs        | 97 +++++++++++++------
 .../src/System/Threading/Tasks/Future.cs      |  2 +-
 .../runtimetask-asyncfibonacci-with-yields.il | 18 +++-
 ...timetask-asyncfibonacci-with-yields.ilproj |  2 +-
 11 files changed, 125 insertions(+), 52 deletions(-)

diff --git a/src/coreclr/inc/eetwain.h b/src/coreclr/inc/eetwain.h
index 9beca3f3729007..be07459dc6e3fe 100644
--- a/src/coreclr/inc/eetwain.h
+++ b/src/coreclr/inc/eetwain.h
@@ -103,6 +103,7 @@ enum ICodeManagerFlags
     NoReportUntracked
                     =   0x0080, // EnumGCRefs/EnumerateLiveSlots should *not* include
                                 // any untracked slots
+    NoGcDecoderValidation    =   0x0100, // Turn off GCDecoder validation
 };
 
 //*****************************************************************************
diff --git a/src/coreclr/vm/eetwain.cpp b/src/coreclr/vm/eetwain.cpp
index b0886fcadebee4..cd3a21434bcf08 100644
--- a/src/coreclr/vm/eetwain.cpp
+++ b/src/coreclr/vm/eetwain.cpp
@@ -5327,9 +5327,19 @@ bool EECodeManager::EnumGcRefs( PREGDISPLAY     pRD,
     reportScratchSlots = (flags & ActiveStackFrame) != 0;
 
 
+    GcInfoDecoderFlags decoderFlags;
+    if (flags & NoGcDecoderValidation)
+    {
+        decoderFlags = GcInfoDecoderFlags (DECODE_GC_LIFETIMES | DECODE_SECURITY_OBJECT | DECODE_VARARG | DECODE_NO_VALIDATION);
+    }
+    else
+    {
+        decoderFlags = GcInfoDecoderFlags (DECODE_GC_LIFETIMES | DECODE_SECURITY_OBJECT | DECODE_VARARG);
+    }
+
     GcInfoDecoder gcInfoDecoder(
                         gcInfoToken,
-                        GcInfoDecoderFlags (DECODE_GC_LIFETIMES | DECODE_SECURITY_OBJECT | DECODE_VARARG),
+                        decoderFlags,
                         curOffs
                         );
 
diff --git a/src/coreclr/vm/gcinfodecoder.cpp b/src/coreclr/vm/gcinfodecoder.cpp
index d738f8fb67a909..8ea647e376074e 100644
--- a/src/coreclr/vm/gcinfodecoder.cpp
+++ b/src/coreclr/vm/gcinfodecoder.cpp
@@ -55,7 +55,7 @@
 
 #ifndef LOG_PIPTR
 #define LOG_PIPTR(pObjRef, gcFlags, hCallBack)                                                                                                  \
-    {                                                                                                                                           \
+    if (!(m_Flags & DECODE_NO_VALIDATION)) {                                                                                                                                           \
         GCCONTEXT* pGCCtx = (GCCONTEXT*)(hCallBack);                                                                                            \
         if (pGCCtx->sc->promotion)                                                                                                              \
         {                                                                                                                                       \
diff --git a/src/coreclr/vm/method.cpp b/src/coreclr/vm/method.cpp
index 803301dd19749b..3935c0d5c842ed 100644
--- a/src/coreclr/vm/method.cpp
+++ b/src/coreclr/vm/method.cpp
@@ -990,7 +990,7 @@ AsyncMethodData* MethodDesc::GetAddrOfAsyncMethodData()
 {
     WRAPPER_NO_CONTRACT;
 
-    _ASSERTE(IsAsyncThunkMethod() || IsAsync2Method());
+    _ASSERTE(HasAsyncMethodData());
 
     SIZE_T size = s_ClassificationSizeTable[m_wFlags & (mdcClassification | mdcHasNonVtableSlot |  mdcMethodImpl | mdcHasNativeCodeSlot)];
 
diff --git a/src/coreclr/vm/methodtablebuilder.cpp b/src/coreclr/vm/methodtablebuilder.cpp
index bdffbf4cb19028..7419681a103dea 100644
--- a/src/coreclr/vm/methodtablebuilder.cpp
+++ b/src/coreclr/vm/methodtablebuilder.cpp
@@ -3571,7 +3571,7 @@ MethodTableBuilder::EnumerateClassMethods()
                     cAsyncThunkMemberSignature += 2;
                     originalTokenOffsetFromAsyncDetailsOffset = 1;
                     newTokenOffsetFromAsyncDetailsOffset = 2;
-                    thunkType = AsyncMethodType::AsyncToTask;
+                    thunkType = AsyncMethodType::TaskToAsync;
                     originalPrefixSize = 1;
                     newPrefixSize = 2;
                     originalSuffixSize = 0;
@@ -3582,7 +3582,7 @@ MethodTableBuilder::EnumerateClassMethods()
                     cAsyncThunkMemberSignature -= 2;
                     originalTokenOffsetFromAsyncDetailsOffset = 2;
                     newTokenOffsetFromAsyncDetailsOffset = 1;
-                    thunkType = AsyncMethodType::TaskToAsync;
+                    thunkType = AsyncMethodType::AsyncToTask;
                     originalPrefixSize = 2;
                     newPrefixSize = 1;
                     originalSuffixSize = 1;
diff --git a/src/coreclr/vm/runtimesuspension.cpp b/src/coreclr/vm/runtimesuspension.cpp
index 2d910e512057d2..54dbf000abb829 100644
--- a/src/coreclr/vm/runtimesuspension.cpp
+++ b/src/coreclr/vm/runtimesuspension.cpp
@@ -215,6 +215,7 @@ tbl     dq      ReturnRegisters, CheckEBX, CheckESI, ...            ;table
 struct TaskletCaptureData
 {
     bool inRunOfAsyncMethods;
+    int framesCaptured = 0;
     StackCrawlMark* stackMark;
     Tasklet* firstTasklet = NULL;
     Tasklet* lastTasklet = NULL;
@@ -226,7 +227,8 @@ struct TaskletCaptureData
     {
         if ((*newByRef >= currentStackTop) && (*newByRef < stackLimit))
         {
-            ActiveByRefsToStack.Push(newByRef);
+            ActiveByRefsToStack.PushNoThrow(newByRef);
+            // TODO PushNoThrow has a return value
         }
     }
 
@@ -287,7 +289,7 @@ StackWalkAction CaptureTaskletsCore(CrawlFrame* pCf, VOID* data)
     CONTRACTL
     {
         THROWS;
-        GC_TRIGGERS;
+;        GC_NOTRIGGER;
         MODE_COOPERATIVE;
         INJECT_FAULT(COMPlusThrowOM(););
     }
@@ -380,7 +382,7 @@ StackWalkAction CaptureTaskletsCore(CrawlFrame* pCf, VOID* data)
         uint32_t offset;
 
         if (false) {}
-#define DISCOVER_RESTORED_REG(REGNAME) else if ((void*)pEnumData->pRD->pCallerContextPointers->REGNAME == (void*)pObject) { offset = pEnumData->GetOffsetForReg(RegisterToRestore::REGNAME); }
+#define DISCOVER_RESTORED_REG(REGNAME) else if ((void*)pEnumData->pRD->pCurrentContextPointers->REGNAME == (void*)pObject) { offset = pEnumData->GetOffsetForReg(RegisterToRestore::REGNAME); }
 #include "restoreregs_for_runtimesuspension.h"
 #undef DISCOVER_RESTORED_REG
         else
@@ -395,7 +397,8 @@ StackWalkAction CaptureTaskletsCore(CrawlFrame* pCf, VOID* data)
 
         if (!isInterior)
         {
-            pEnumData->ObjectRefOffsets.Push(offset);
+            pEnumData->ObjectRefOffsets.PushNoThrow(offset);
+            // TODO PushNoThrow has a return value
         }
         else
         {
@@ -407,7 +410,8 @@ StackWalkAction CaptureTaskletsCore(CrawlFrame* pCf, VOID* data)
                 byRefOffset = -byRefOffset;
             }
 
-            pEnumData->ByRefOffsets.Push(offset);
+            pEnumData->ByRefOffsets.PushNoThrow(offset);
+            // TODO PushNoThrow has a return value
         }
     };
 
@@ -415,7 +419,7 @@ StackWalkAction CaptureTaskletsCore(CrawlFrame* pCf, VOID* data)
 
     pCM->EnumGcRefs(pCf->GetRegisterSet(),
                     pCf->GetCodeInfo(),
-                    flags,
+                    flags | NoGcDecoderValidation,
                     enumGCRefs,
                     &enumData,
                     NO_OVERRIDE_OFFSET);
@@ -427,6 +431,10 @@ StackWalkAction CaptureTaskletsCore(CrawlFrame* pCf, VOID* data)
     stackDataInfo.RegistersToRestore = (RegRestore*)malloc(savedRegRestoreData.Size() * sizeof(RegRestore));
     memcpy(stackDataInfo.RegistersToRestore, savedRegRestoreData.Ptr(), savedRegRestoreData.Size() * sizeof(RegRestore));
 
+    stackDataInfo.cByRefs = (uint32_t)enumData.ByRefOffsets.Size();
+    stackDataInfo.cObjectRefs = (uint32_t)enumData.ObjectRefOffsets.Size();
+    stackDataInfo.CbArgs = sizeofArgStack;
+
     StackDataInfo *pStackDataInfo = (StackDataInfo*)malloc(sizeof(StackDataInfo));
     *pStackDataInfo = stackDataInfo;
 
@@ -448,7 +456,7 @@ StackWalkAction CaptureTaskletsCore(CrawlFrame* pCf, VOID* data)
         }
         else
         {
-            if (thRet.AsMethodTable()->IsValueType() || thRet.AsMethodTable()->ContainsPointers())
+            if (thRet.AsMethodTable()->IsValueType() && thRet.AsMethodTable()->ContainsPointers())
             {
                 taskletReturnType = TaskletReturnType::ObjectReference;
             }
@@ -471,7 +479,7 @@ PORTABILIT_ASSERT()
     memset(pTasklet, 0, sizeof(Tasklet));
     pTasklet->pStackData = pStackData;
     pTasklet->restoreIPAddress = (uintptr_t)GetControlPC(pCf->GetRegisterSet());
-    pTasklet->pStackDataInfo;
+    pTasklet->pStackDataInfo = pStackDataInfo;
     pTasklet->taskletReturnType = taskletReturnType;
 
     if (taskletCaptureData->firstTasklet == NULL)
@@ -484,6 +492,7 @@ PORTABILIT_ASSERT()
         taskletCaptureData->lastTasklet->pTaskletNextInStack = pTasklet;
         taskletCaptureData->lastTasklet = pTasklet;
     }
+    taskletCaptureData->framesCaptured++;
     
     // Apply byrefs to newly allocated stuff
     taskletCaptureData->ApplyByRefRelocsToNewlyAllocatedTasklet(byrefAdjustment);
@@ -492,10 +501,15 @@ PORTABILIT_ASSERT()
 }
 
 
-extern "C" Tasklet* QCALLTYPE RuntimeSuspension_CaptureTasklets(QCall::StackCrawlMarkHandle stackMark, uint8_t* returnValue, uint8_t useReturnValueHandle, void* taskAsyncData, Tasklet** lastTasklet)
+extern "C" Tasklet* QCALLTYPE RuntimeSuspension_CaptureTasklets(QCall::StackCrawlMarkHandle stackMark, uint8_t* returnValue, uint8_t useReturnValueHandle, void* taskAsyncData, Tasklet** lastTasklet, int32_t* pFramesCaptured)
 {
     GCX_COOP();
 
+    BEGINFORBIDGC();
+#ifdef _DEBUG
+    GCForbidLoaderUseHolder forbidLoaderUse;
+#endif
+
     TaskletCaptureData cdata;
     cdata.stackMark = stackMark;
     cdata.stackLimit = (uintptr_t)taskAsyncData;
@@ -503,7 +517,8 @@ extern "C" Tasklet* QCALLTYPE RuntimeSuspension_CaptureTasklets(QCall::StackCraw
 
     // We should have captured a full stack here. 
     *lastTasklet = cdata.lastTasklet;
-    _ASSERTE(FALSE);
+    *pFramesCaptured = cdata.framesCaptured;
+    ENDFORBIDGC();
     return cdata.firstTasklet;
 }
 
diff --git a/src/coreclr/vm/runtimesuspension.h b/src/coreclr/vm/runtimesuspension.h
index f48c583d33c44a..d06e7a149d7720 100644
--- a/src/coreclr/vm/runtimesuspension.h
+++ b/src/coreclr/vm/runtimesuspension.h
@@ -5,7 +5,7 @@ struct Tasklet;
 struct AsyncDataFrame;
 struct RuntimeAsyncReturnValue;
 
-extern "C" Tasklet* QCALLTYPE RuntimeSuspension_CaptureTasklets(QCall::StackCrawlMarkHandle stackMark, uint8_t* returnValue, uint8_t useReturnValueHandle, void* taskAsyncData, Tasklet** lastTasklet);
+extern "C" Tasklet* QCALLTYPE RuntimeSuspension_CaptureTasklets(QCall::StackCrawlMarkHandle stackMark, uint8_t* returnValue, uint8_t useReturnValueHandle, void* taskAsyncData, Tasklet** lastTasklet, int32_t* pFramesCaptured);
 extern "C" void QCALLTYPE RuntimeSuspension_DeleteTasklet(Tasklet* tasklet);
 
 EXTERN_C FCDECL1(void, RuntimeSuspension_UnwindToFunctionWithAsyncFrame, AsyncDataFrame* frame);
diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.cs
index e8f89f00833555..9c504bfa6cdd92 100644
--- a/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.cs
+++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.cs
@@ -125,11 +125,13 @@ public static TResult UnsafeAwaitAwaiterFromRuntimeAsync<TResult, TAwaiter>(TAwa
         {
             if (!awaiter.IsCompleted)
             {
+                StackCrawlMark stackMark = StackCrawlMark.LookForMe;
+
                 // Create resumption delegate, wrapping task, and create tasklets to represent each stack frame on the stack.
                 // RuntimeTaskSuspender.GetOrCreateResumptionDelegate() works like a POSIX fork call in that calls to it will return a
                 // delegate if they are the initial call to GetOrCreateResumptionDelegate, but once the thread is resumed,
                 // it will resume with a return value of null.
-                Action? resumption = RuntimeHelpers.GetOrCreateResumptionDelegate();
+                Action? resumption = RuntimeHelpers.GetOrCreateResumptionDelegate(ref stackMark);
                 if (resumption != null)
                 {
                     // We are trying to suspend
@@ -151,7 +153,17 @@ public static TResult UnsafeAwaitAwaiterFromRuntimeAsync<TResult, TAwaiter>(TAwa
                     }
                     // If we reach here, the only way that we actually run follow on code is for the continuation to actually run,
                     // and return from GetOrCreateResumptionDelegate with a null return value.
-                    RuntimeHelpers.SuspendIfSuspensionNotAborted();
+                    ref AsyncDataFrame asyncFrame = ref GetCurrentAsyncDataFrame();
+                    RuntimeAsyncMaintainedData maintainedData = asyncFrame._maintainedData!;
+                    if (maintainedData._abortSuspend)
+                    {
+                        AbortSuspend();
+                    }
+                    else
+                    {
+                        // This function must be called from the same function that has the stackmark in it.
+                        unsafe { UnwindToFunctionWithAsyncFrame(maintainedData._nextTasklet, maintainedData._suspendActive); }
+                    }
                 }
             }
 
@@ -164,11 +176,13 @@ public static TResult AwaitAwaiterFromRuntimeAsync<TResult, TAwaiter>(TAwaiter a
         {
             if (!awaiter.IsCompleted)
             {
+                StackCrawlMark stackMark = StackCrawlMark.LookForMe;
+
                 // Create resumption delegate, wrapping task, and create tasklets to represent each stack frame on the stack.
                 // RuntimeTaskSuspender.GetOrCreateResumptionDelegate() works like a POSIX fork call in that calls to it will return a
                 // delegate if they are the initial call to GetOrCreateResumptionDelegate, but once the thread is resumed,
                 // it will resume with a return value of null.
-                Action? resumption = RuntimeHelpers.GetOrCreateResumptionDelegate();
+                Action? resumption = RuntimeHelpers.GetOrCreateResumptionDelegate(ref stackMark);
                 if (resumption != null)
                 {
                     // We are trying to suspend
@@ -190,7 +204,17 @@ public static TResult AwaitAwaiterFromRuntimeAsync<TResult, TAwaiter>(TAwaiter a
                     }
                     // If we reach here, the only way that we actually run follow on code is for the continuation to actually run,
                     // and return from GetOrCreateResumptionDelegate with a null return value.
-                    RuntimeHelpers.SuspendIfSuspensionNotAborted();
+                    ref AsyncDataFrame asyncFrame = ref GetCurrentAsyncDataFrame();
+                    RuntimeAsyncMaintainedData maintainedData = asyncFrame._maintainedData!;
+                    if (maintainedData._abortSuspend)
+                    {
+                        AbortSuspend();
+                    }
+                    else
+                    {
+                        // This function must be called from the same function that has the stackmark in it.
+                        unsafe { UnwindToFunctionWithAsyncFrame(maintainedData._nextTasklet, maintainedData._suspendActive); }
+                    }
                 }
             }
 
@@ -204,11 +228,13 @@ public static void UnsafeAwaitAwaiterFromRuntimeAsync<TAwaiter>(TAwaiter awaiter
         {
             if (!awaiter.IsCompleted)
             {
+                StackCrawlMark stackMark = StackCrawlMark.LookForMe;
+
                 // Create resumption delegate, wrapping task, and create tasklets to represent each stack frame on the stack.
                 // RuntimeTaskSuspender.GetOrCreateResumptionDelegate() works like a POSIX fork call in that calls to it will return a
                 // delegate if they are the initial call to GetOrCreateResumptionDelegate, but once the thread is resumed,
                 // it will resume with a return value of null.
-                Action? resumption = RuntimeHelpers.GetOrCreateResumptionDelegate();
+                Action? resumption = RuntimeHelpers.GetOrCreateResumptionDelegate(ref stackMark);
                 if (resumption != null)
                 {
                     // We are trying to suspend
@@ -230,7 +256,17 @@ public static void UnsafeAwaitAwaiterFromRuntimeAsync<TAwaiter>(TAwaiter awaiter
                     }
                     // If we reach here, the only way that we actually run follow on code is for the continuation to actually run,
                     // and return from GetOrCreateResumptionDelegate with a null return value.
-                    RuntimeHelpers.SuspendIfSuspensionNotAborted();
+                    ref AsyncDataFrame asyncFrame = ref GetCurrentAsyncDataFrame();
+                    RuntimeAsyncMaintainedData maintainedData = asyncFrame._maintainedData!;
+                    if (maintainedData._abortSuspend)
+                    {
+                        AbortSuspend();
+                    }
+                    else
+                    {
+                        // This function must be called from the same function that has the stackmark in it.
+                        unsafe { UnwindToFunctionWithAsyncFrame(maintainedData._nextTasklet, maintainedData._suspendActive); }
+                    }
                 }
             }
 
@@ -243,11 +279,13 @@ public static void AwaitAwaiterFromRuntimeAsync<TAwaiter>(TAwaiter awaiter) wher
         {
             if (!awaiter.IsCompleted)
             {
+                StackCrawlMark stackMark = StackCrawlMark.LookForMe;
+
                 // Create resumption delegate, wrapping task, and create tasklets to represent each stack frame on the stack.
                 // RuntimeTaskSuspender.GetOrCreateResumptionDelegate() works like a POSIX fork call in that calls to it will return a
                 // delegate if they are the initial call to GetOrCreateResumptionDelegate, but once the thread is resumed,
                 // it will resume with a return value of null.
-                Action? resumption = RuntimeHelpers.GetOrCreateResumptionDelegate();
+                Action? resumption = RuntimeHelpers.GetOrCreateResumptionDelegate(ref stackMark);
                 if (resumption != null)
                 {
                     // We are trying to suspend
@@ -269,7 +307,17 @@ public static void AwaitAwaiterFromRuntimeAsync<TAwaiter>(TAwaiter awaiter) wher
                     }
                     // If we reach here, the only way that we actually run follow on code is for the continuation to actually run,
                     // and return from GetOrCreateResumptionDelegate with a null return value.
-                    RuntimeHelpers.SuspendIfSuspensionNotAborted();
+                    ref AsyncDataFrame asyncFrame = ref GetCurrentAsyncDataFrame();
+                    RuntimeAsyncMaintainedData maintainedData = asyncFrame._maintainedData!;
+                    if (maintainedData._abortSuspend)
+                    {
+                        AbortSuspend();
+                    }
+                    else
+                    {
+                        // This function must be called from the same function that has the stackmark in it.
+                        unsafe { UnwindToFunctionWithAsyncFrame(maintainedData._nextTasklet, maintainedData._suspendActive); }
+                    }
                 }
             }
 
@@ -303,7 +351,7 @@ internal abstract unsafe class RuntimeAsyncMaintainedData
         {
             public Action? _resumption;
             public Exception? _exception;
-            public bool _suspendActive;
+            public int _suspendActive;
             public bool _initialTaskEntry = true;
             public bool _completed;
             public byte _dummy;
@@ -346,6 +394,10 @@ public unsafe void ResumptionFunc()
                     _abortSuspend = true;
                     return;
                 }
+
+                // Suspension has finished and we are resuming
+                _suspendActive = 0;
+
                 // Once we perform a resumption we no longer need to worry about handling the ultimate return data from the run of Tasklets
                 _initialTaskEntry = false;
 
@@ -558,32 +610,17 @@ private static unsafe ref AsyncDataFrame GetCurrentAsyncDataFrame()
         // 3. Return values are to be returned by reference in all cases where the return value is not a simple object return or return of a simple value in the return value register (this makes the resumption function reasonable to write. Notably, floating point, and ref return will be returned by reference as well as generalized struct return, and return which would normally involve multiple return value registers)
         // 4. There are to be no refs to the outermost caller function exceptn for the valuetype return address (methods which begin on an instance valuetype will have the thunk box the valuetype and the runtime async method on the boxed instance)
         [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "RuntimeSuspension_CaptureTasklets")]
-        private static unsafe partial Tasklet *CaptureCurrentStackIntoTasklets(StackCrawlMarkHandle stackMarkTop, ref byte returnValueHandle, [MarshalAs(UnmanagedType.U1)] bool useReturnValueHandle, void* taskAsyncData, out Tasklet* lastTasklet);
+        private static unsafe partial Tasklet *CaptureCurrentStackIntoTasklets(StackCrawlMarkHandle stackMarkTop, ref byte returnValueHandle, [MarshalAs(UnmanagedType.U1)] bool useReturnValueHandle, void* taskAsyncData, out Tasklet* lastTasklet, out int framesCaptured);
 
         [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "RuntimeSuspension_DeleteTasklet")]
         private static unsafe partial void DeleteTasklet(Tasklet *tasklet);
 
         [MethodImpl(MethodImplOptions.InternalCall)]
-        internal static extern void UnwindToFunctionWithAsyncFrame(ref AsyncDataFrame dataFrame);
-
-        private static void SuspendIfSuspensionNotAborted()
-        {
-            ref AsyncDataFrame asyncFrame = ref GetCurrentAsyncDataFrame();
-            RuntimeAsyncMaintainedData maintainedData = asyncFrame._maintainedData!;
-            if (maintainedData._abortSuspend)
-            {
-                AbortSuspend();
-            }
-            else
-            {
-                UnwindToFunctionWithAsyncFrame(ref asyncFrame);
-            }
-        }
+        internal static extern unsafe void UnwindToFunctionWithAsyncFrame(Tasklet *topTasklet, nint framesToUnwind);
 
         [System.Security.DynamicSecurityMethod] // Methods containing StackCrawlMark local var has to be marked DynamicSecurityMethod
-        private static unsafe Action? GetOrCreateResumptionDelegate()
+        private static unsafe Action? GetOrCreateResumptionDelegate(ref StackCrawlMark stackMark)
         {
-            StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
             ref AsyncDataFrame asyncFrame = ref GetCurrentAsyncDataFrame();
 
             asyncFrame._maintainedData ??= asyncFrame._createRuntimeMaintainedData!();
@@ -591,7 +628,7 @@ private static void SuspendIfSuspensionNotAborted()
             RuntimeAsyncMaintainedData maintainedData = asyncFrame._maintainedData;
 
             Tasklet* lastTasklet = null;
-            Tasklet* nextTaskletInStack = CaptureCurrentStackIntoTasklets(new StackCrawlMarkHandle(ref stackMark), ref maintainedData.GetReturnPointer(), maintainedData._initialTaskEntry, t_asyncData, out lastTasklet);
+            Tasklet* nextTaskletInStack = CaptureCurrentStackIntoTasklets(new StackCrawlMarkHandle(ref stackMark), ref maintainedData.GetReturnPointer(), maintainedData._initialTaskEntry, t_asyncData, out lastTasklet, out var framesCaptured);
             if (nextTaskletInStack == null)
                 throw new OutOfMemoryException();
 
@@ -600,7 +637,7 @@ private static void SuspendIfSuspensionNotAborted()
             maintainedData._nextTasklet = nextTaskletInStack;
 
             maintainedData._abortSuspend = false;
-            maintainedData._suspendActive = true;
+            maintainedData._suspendActive = framesCaptured;
 
             maintainedData._executionCtx = asyncFrame._currentThread._executionContext;
             maintainedData._syncCtx = asyncFrame._currentThread._synchronizationContext;
@@ -623,7 +660,7 @@ private static unsafe void AbortSuspend()
             }
             maintainedData._nextTasklet = maintainedData._oldTaskletNext;
             maintainedData._oldTaskletNext = null;
-            maintainedData._suspendActive = false;
+            maintainedData._suspendActive = 0;
         }
 #endif
     }
diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/Future.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/Future.cs
index 394609486f3c3c..4d1917ddcfe202 100644
--- a/src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/Future.cs
+++ b/src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/Future.cs
@@ -32,7 +32,7 @@ public void Push()
         public void Pop() { RuntimeHelpers.PopAsyncData(ref dataFrame); }
         public Task<TResult> FromResult(TResult result)
         {
-            if (dataFrame._maintainedData == null || !dataFrame._maintainedData._suspendActive)
+            if (dataFrame._maintainedData == null || (dataFrame._maintainedData._suspendActive == 0))
                 return Task.FromResult(result);
             return (Task<TResult>)dataFrame._maintainedData.GetTask();
         }
diff --git a/src/tests/Loader/async/runtimetask-asyncfibonacci-with-yields.il b/src/tests/Loader/async/runtimetask-asyncfibonacci-with-yields.il
index bfe9aece7c9a77..b081a484996394 100644
--- a/src/tests/Loader/async/runtimetask-asyncfibonacci-with-yields.il
+++ b/src/tests/Loader/async/runtimetask-asyncfibonacci-with-yields.il
@@ -348,7 +348,8 @@
            bool V_1,
            bool V_2,
            bool V_3,
-           uint32 V_4)
+           uint32 V_4,
+           valuetype [System.Runtime]System.Runtime.CompilerServices.YieldAwaitable V_5)
   IL_0000:  nop
   IL_0001:  ldarg.0
   IL_0002:  stloc.0
@@ -363,7 +364,10 @@
   IL_0014:  ldloc.1
   IL_0015:  brfalse.s  IL_001d
   IL_0017:  call       valuetype [System.Runtime]System.Runtime.CompilerServices.YieldAwaitable [System.Runtime]System.Threading.Tasks.Task::Yield()
-  IL_001c:  call       void [System.Runtime]System.Runtime.CompilerServices.RuntimeHelpers::UnsafeAwaitAwaiterFromRuntimeAsync<valuetype [System.Runtime]System.Runtime.CompilerServices.YieldAwaitable>(!!0)
+            stloc 5
+            ldloca 5
+            call       instance valuetype [System.Runtime]System.Runtime.CompilerServices.YieldAwaitable/YieldAwaiter [System.Runtime]System.Runtime.CompilerServices.YieldAwaitable::GetAwaiter()
+  IL_001c:  call       void [System.Runtime]System.Runtime.CompilerServices.RuntimeHelpers::UnsafeAwaitAwaiterFromRuntimeAsync<valuetype [System.Runtime]System.Runtime.CompilerServices.YieldAwaitable/YieldAwaiter>(!!0)
   IL_001d:  ldloc.0
   IL_001e:  ldc.i4     0x773593ed
   IL_0023:  mul
@@ -375,7 +379,10 @@
   IL_002e:  ldloc.2
   IL_002f:  brfalse.s  IL_0037
   IL_0031:  call       valuetype [System.Runtime]System.Runtime.CompilerServices.YieldAwaitable [System.Runtime]System.Threading.Tasks.Task::Yield()
-  IL_0036:  call       void [System.Runtime]System.Runtime.CompilerServices.RuntimeHelpers::UnsafeAwaitAwaiterFromRuntimeAsync<valuetype [System.Runtime]System.Runtime.CompilerServices.YieldAwaitable>(!!0)
+            stloc 5
+            ldloca 5
+            call       instance valuetype [System.Runtime]System.Runtime.CompilerServices.YieldAwaitable/YieldAwaiter [System.Runtime]System.Runtime.CompilerServices.YieldAwaitable::GetAwaiter()
+  IL_0036:  call       void [System.Runtime]System.Runtime.CompilerServices.RuntimeHelpers::UnsafeAwaitAwaiterFromRuntimeAsync<valuetype [System.Runtime]System.Runtime.CompilerServices.YieldAwaitable/YieldAwaiter>(!!0)
   IL_0037:  ldloc.0
   IL_0038:  ldc.i4     0x773593ed
   IL_003d:  mul
@@ -387,7 +394,10 @@
   IL_0048:  ldloc.3
   IL_0049:  brfalse.s  IL_0051
   IL_004b:  call       valuetype [System.Runtime]System.Runtime.CompilerServices.YieldAwaitable [System.Runtime]System.Threading.Tasks.Task::Yield()
-  IL_0050:  call       void [System.Runtime]System.Runtime.CompilerServices.RuntimeHelpers::UnsafeAwaitAwaiterFromRuntimeAsync<valuetype [System.Runtime]System.Runtime.CompilerServices.YieldAwaitable>(!!0)
+            stloc 5
+            ldloca 5
+            call       instance valuetype [System.Runtime]System.Runtime.CompilerServices.YieldAwaitable/YieldAwaiter [System.Runtime]System.Runtime.CompilerServices.YieldAwaitable::GetAwaiter()
+  IL_0050:  call       void [System.Runtime]System.Runtime.CompilerServices.RuntimeHelpers::UnsafeAwaitAwaiterFromRuntimeAsync<valuetype [System.Runtime]System.Runtime.CompilerServices.YieldAwaitable/YieldAwaiter>(!!0)
   IL_0051:  ldloc.0
   IL_0052:  stloc.s    V_4
   IL_0054:  br.s       IL_0056
diff --git a/src/tests/Loader/async/runtimetask-asyncfibonacci-with-yields.ilproj b/src/tests/Loader/async/runtimetask-asyncfibonacci-with-yields.ilproj
index bd17dd2bad5a7e..24afb2e5b72dc1 100644
--- a/src/tests/Loader/async/runtimetask-asyncfibonacci-with-yields.ilproj
+++ b/src/tests/Loader/async/runtimetask-asyncfibonacci-with-yields.ilproj
@@ -3,6 +3,6 @@
     <CLRTestPriority>1</CLRTestPriority>
   </PropertyGroup>
   <ItemGroup>
-    <Compile Include="runtimetask-asyncfibonacci-without-yields.il" />
+    <Compile Include="runtimetask-asyncfibonacci-with-yields.il" />
   </ItemGroup>
 </Project>

From df91ac6d663bd6cfe075a835f696648b41df82ff Mon Sep 17 00:00:00 2001
From: David Wrighton <davidwr@microsoft.com>
Date: Tue, 17 Oct 2023 15:06:52 -0700
Subject: [PATCH 025/203] Basic unwind support mostly works. Expected that it
 crashes unpredictably in the presence of Suspend operations, but other stuff
 seems to work. Now to rework Task generation to avoid multithreaded races

---
 src/coreclr/debug/di/module.cpp               |  4 +-
 src/coreclr/utilcode/collections.cpp          |  7 +++
 src/coreclr/vm/amd64/AsmHelpers.asm           | 60 +++++++++++++------
 src/coreclr/vm/method.cpp                     |  6 +-
 src/coreclr/vm/method.hpp                     |  5 +-
 src/coreclr/vm/runtimesuspension.cpp          | 41 +++++++++++--
 .../CompilerServices/RuntimeHelpers.cs        | 13 ++--
 7 files changed, 103 insertions(+), 33 deletions(-)

diff --git a/src/coreclr/debug/di/module.cpp b/src/coreclr/debug/di/module.cpp
index ca8314db533922..1f4216908637ad 100644
--- a/src/coreclr/debug/di/module.cpp
+++ b/src/coreclr/debug/di/module.cpp
@@ -4250,12 +4250,12 @@ HRESULT CordbNativeCode::GetILToNativeMapping(ULONG32                    cMap,
         LoadNativeInfo();
 
         SequencePoints * pSeqPts = GetSequencePoints();
-        DebuggerILToNativeMap * rgMapInt = pSeqPts->GetMapAddr();
         ULONG32 cMapIntCount = pSeqPts->GetEntryCount();
 
         // If they gave us space to copy into...
-        if (map != NULL)
+        if (map != NULL && cMapIntCount != 0)
         {
+            DebuggerILToNativeMap * rgMapInt = pSeqPts->GetMapAddr();
             // Only copy as much as either they gave us or we have to copy.
             ULONG32 cMapToCopy = min(cMap, cMapIntCount);
 
diff --git a/src/coreclr/utilcode/collections.cpp b/src/coreclr/utilcode/collections.cpp
index ed5271fde77f85..3f8513d38fc535 100644
--- a/src/coreclr/utilcode/collections.cpp
+++ b/src/coreclr/utilcode/collections.cpp
@@ -268,6 +268,13 @@ BYTE *CHashTable::FindNextEntry(        // The next entry, or0 for end of list.
         if (psSrch->iNext != UINT32_MAX)
         {
             psEntry = EntryPtr(psSrch->iNext);
+#ifdef DACCESS_COMPILE
+            // If this happens, it will trigger an infinite loop. Don't block the debugger on this
+            if (psSrch->iNext == psEntry->iNext)
+            {
+                return 0;
+            }
+#endif
             psSrch->iNext = psEntry->iNext;
             return ((BYTE *) psEntry);
         }
diff --git a/src/coreclr/vm/amd64/AsmHelpers.asm b/src/coreclr/vm/amd64/AsmHelpers.asm
index 6abe642d1e7e1a..7b1120ece20830 100644
--- a/src/coreclr/vm/amd64/AsmHelpers.asm
+++ b/src/coreclr/vm/amd64/AsmHelpers.asm
@@ -684,10 +684,15 @@ endif ; FEATURE_TIERED_COMPILATION
 
 extern PlatformIndependentRestore:proc
 
+; regRestore swaps whats in the register for the value in the restoration location. The idea is that the
+; value in the restoration location is the value that has been stored away for use in this function
+; and the current value of the saved register needs to be available once the function returns.
 regRestoreCase macro RegisterName
 Check&RegisterName&:
     mov edx, [rax+4]
+    mov r10, RegisterName
     mov RegisterName, [rcx+rdx]
+    mov [rcx+rdx], r10
     add rax, 8
     jmp RegRestoreLoop
         endm
@@ -695,16 +700,25 @@ Check&RegisterName&:
 regRestoreCaseXmm macro RegisterName
 Check&RegisterName&:
     mov edx, [rax+4]
+    movsd xmm0, RegisterName
     movdqu RegisterName, [rcx+rdx]
+    movdqu [rcx+rdx], xmm0
     add rax, 8
     jmp RegRestoreLoop
         endm
 
 NESTED_ENTRY RuntimeSuspension_ResumeTaskletReferenceReturn, _TEXT
-        alloc_stack             48h
+        push_nonvol_reg rbp  ; We capture RBP to create a stack frame so that we can safely adjust the RSP register, This means that since we always capture this, async2 functions must ALWAYS be compiled with a frame pointer.
+        alloc_stack             50h
+        set_frame rbp, 50h
     END_PROLOGUE
+        mov r10, [rcx + 8h*5] ; Load pStackDataInfo from Tasklet*
+        mov rax, [r10] ; Load StackRequirement from StackDataInfo
+        mov r10d, [r10 + 4h*6] ; Load CbArgs from StackDataInfo
+        sub rax, r10          ; Compute size of Caller SP to Callee SP
+        sub rsp, rax          ; Allocate enough space for Callee stack
 
-        lea r8, [rsp+28h]
+        lea r8, [rsp+20h]
 
         ; Zero out integerRegister and AddressInMethodToRestoreTo
         ; Probably not needed in production code, but convenient for now
@@ -713,20 +727,24 @@ NESTED_ENTRY RuntimeSuspension_ResumeTaskletReferenceReturn, _TEXT
         mov [r8+8h], rax
 
         ; Fill in pFutureRSPLocation
-        lea rax, [rsp+50]
+        lea rax, [rsp+60h]
         mov [r8+10h], rax
 
         ; Capture return address
-        mov rax, [rax]
+        mov rax, [rbp + 8h]
         mov [r8+18h], rax
 
+        ; Capture old rbp so that the stack overwrite doesn't stomp it
+        mov rax, [rbp] 
+        mov [rsp+40h], rax
+
         call PlatformIndependentRestore
         ; RAX now points at the RegRestore array
-        mov rcx, [rsp+28h + 10h]
+        mov rcx, [rsp+20h + 10h]
         ; RCX now has the value of the future RSP
 
         .data
-switchTable     qword    Checkrbx, Checkrbp, Checkrdi, Checkrsi, Checkr12, Checkr13, Checkr14, Checkr15, Checkxmm6, Checkxmm7, Checkxmm8, Checkxmm9, Checkxmm10, Checkxmm11, Checkxmm12, Checkxmm13, Checkxmm14, Checkxmm15, ReturnRegisters
+switchTable     qword    Checkrbx, Checkrbp, Checkrdi, Checkrsi, Checkr12, Checkr13, Checkr14, Checkr15, Checkxmm6, Checkxmm7, Checkxmm8, Checkxmm9, Checkxmm10, Checkxmm11, Checkxmm12, Checkxmm13, Checkxmm14, Checkxmm15, ReturnRegisters, ReturnRegistersNoFrame
         .code
 RegRestoreLoop:
     mov edx, [rax]
@@ -734,14 +752,22 @@ RegRestoreLoop:
     lea r8, [switchTable]
     mov r8, [r8 + rdx*8]
     jmp r8
+ReturnRegistersNoFrame:
+    mov rbp, [rsp + 40h] ; Capture RBP old value from the stored location instead of from the current register (this is because we actually use RBP as part of this helper thunk function)
 ReturnRegisters:
-    mov rax, [rsp+28h + 0h]
-    MOVDQU xmm0, [rsp+28h + 0h]
-    mov rdx, [rsp+28h + 0h] ; Load address of where to jmp into ecx
+    mov rax, [rsp+20h + 0h]
+    MOVDQU xmm0, [rsp+20h + 0h]
+    mov rdx, [rsp+20h + 8h] ; Load address of where to jmp into ecx
     mov rsp, rcx
     jmp rdx
+Checkrbp:
+    mov edx, [rax+4]
+    mov r10, [rsp + 40h] ; Capture RBP old value from the stored location instead of from the current register (this is because we actually use RBP as part of this helper thunk function)
+    mov rbp, [rcx+rdx]
+    mov [rcx+rdx], r10
+    add rax, 8
+    jmp RegRestoreLoop
 regRestoreCase rbx
-regRestoreCase rbp
 regRestoreCase rdi
 regRestoreCase rsi
 regRestoreCase r12
@@ -766,16 +792,16 @@ LEAF_END RuntimeSuspension_ResumeTaskletIntegerRegisterReturn, _TEXT
 
 unwindRegCase macro RegisterName
 Unwind&RegisterName&:
-    mov edx, [r8+4]
-    mov RegisterName, [rsp+rdx]
+    mov eax, [r8+4]
+    mov RegisterName, [rsp+rax]
     add r8, 8
     jmp RegUnwindLoop
         endm
 
 unwindRegCaseXmm macro RegisterName
 Unwind&RegisterName&:
-    mov edx, [r8+4]
-    movdqu RegisterName, [rsp+rdx]
+    mov eax, [r8+4]
+    movdqu RegisterName, [rsp+rax]
     add r8, 8
     jmp RegUnwindLoop
         endm
@@ -813,7 +839,7 @@ NewTaskletUnwind:
         mov r8, [r9+30h] ; Load r8 with the pRegRestore value
 
         .data
-unwindSwitchTable     qword    Unwindrbx, Unwindrbp, Unwindrdi, Unwindrsi, Unwindr12, Unwindr13, Unwindr14, Unwindr15, Unwindxmm6, Unwindxmm7, Unwindxmm8, Unwindxmm9, Unwindxmm10, Unwindxmm11, Unwindxmm12, Unwindxmm13, Unwindxmm14, Unwindxmm15, UnwindReturnRegisters
+unwindSwitchTable     qword    Unwindrbx, Unwindrbp, Unwindrdi, Unwindrsi, Unwindr12, Unwindr13, Unwindr14, Unwindr15, Unwindxmm6, Unwindxmm7, Unwindxmm8, Unwindxmm9, Unwindxmm10, Unwindxmm11, Unwindxmm12, Unwindxmm13, Unwindxmm14, Unwindxmm15, UnwindReturnRegisters, UnwindReturnRegisters
         .code
 RegUnwindLoop:
         mov eax, [r8]
@@ -845,8 +871,8 @@ UnwindReturnRegisters:
         mov rax, [rsp+rax]
         
         ; Adjust rsp to callers rsp
-        mov r10, [r9] ; Load StackDataInfo.StackRequirement
-        sub r10, [r9 + 18h] ; Subtract off the CbArgs (stack args amount)
+        mov r10d, [r9] ; Load StackDataInfo.StackRequirement
+        sub r10d, [r9 + 18h] ; Subtract off the CbArgs (stack args amount)
         add rsp, r10 ; Adjust rsp as requested
 
         mov rcx, [rcx] ; Adjust to callers tasklet
diff --git a/src/coreclr/vm/method.cpp b/src/coreclr/vm/method.cpp
index 3935c0d5c842ed..bd15803076869a 100644
--- a/src/coreclr/vm/method.cpp
+++ b/src/coreclr/vm/method.cpp
@@ -407,7 +407,7 @@ void MethodDesc::GetSig(PCCOR_SIGNATURE *ppSig, DWORD *pcSig)
     }
     if (IsAsyncThunkMethod())
     {
-        Signature sig = GetAsyncMethodData().sig;
+        Signature sig = GetAddrOfAsyncMethodData()->sig;
         *ppSig = sig.GetRawSig();
         *pcSig = sig.GetRawSigLen();
         return;
@@ -986,7 +986,7 @@ PTR_PCODE MethodDesc::GetAddrOfNativeCodeSlot()
 }
 
 //*******************************************************************************
-AsyncMethodData* MethodDesc::GetAddrOfAsyncMethodData()
+PTR_AsyncMethodData MethodDesc::GetAddrOfAsyncMethodData()
 {
     WRAPPER_NO_CONTRACT;
 
@@ -994,7 +994,7 @@ AsyncMethodData* MethodDesc::GetAddrOfAsyncMethodData()
 
     SIZE_T size = s_ClassificationSizeTable[m_wFlags & (mdcClassification | mdcHasNonVtableSlot |  mdcMethodImpl | mdcHasNativeCodeSlot)];
 
-    return (AsyncMethodData*)(dac_cast<TADDR>(this) + size);
+    return dac_cast<PTR_AsyncMethodData>(dac_cast<TADDR>(this) + size);
 }
 
 //*******************************************************************************
diff --git a/src/coreclr/vm/method.hpp b/src/coreclr/vm/method.hpp
index 7fc41ad768568e..25c16852c12986 100644
--- a/src/coreclr/vm/method.hpp
+++ b/src/coreclr/vm/method.hpp
@@ -70,6 +70,7 @@ struct AsyncMethodData
     Signature sig;
 };
 
+typedef DPTR(struct AsyncMethodData) PTR_AsyncMethodData;
 
 //=============================================================
 // Splits methoddef token into two pieces for
@@ -1406,8 +1407,10 @@ class MethodDesc
     BOOL SetNativeCodeInterlocked(PCODE addr, PCODE pExpected = NULL);
 
     PTR_PCODE GetAddrOfNativeCodeSlot();
-    AsyncMethodData *GetAddrOfAsyncMethodData();
+    PTR_AsyncMethodData GetAddrOfAsyncMethodData();
+#ifndef DACCESS_COMPILE
     const AsyncMethodData& GetAsyncMethodData() { _ASSERTE(IsAsyncThunkMethod()); return *GetAddrOfAsyncMethodData(); }
+#endif
 
     BOOL MayHaveNativeCode();
 
diff --git a/src/coreclr/vm/runtimesuspension.cpp b/src/coreclr/vm/runtimesuspension.cpp
index 54dbf000abb829..af35884f42bbdf 100644
--- a/src/coreclr/vm/runtimesuspension.cpp
+++ b/src/coreclr/vm/runtimesuspension.cpp
@@ -34,6 +34,7 @@ enum RegisterToRestore
     Xmm14,
     Xmm15,
     ReturnRegisters, // End sequence marker. Associated offset is for the return register, but the actual return value is stashed after the register data
+    ReturnRegistersNoFrame, // End sequence marker. Associated offset is for the return register, but the actual return value is stashed after the register data
 };
 
 struct RegRestore
@@ -236,8 +237,10 @@ struct TaskletCaptureData
     {
         if (ActiveByRefsToStack.Size() > 0)
         {
-            for (SIZE_T iByRef = ActiveByRefsToStack.Size() - 1; iByRef >= 0; iByRef--)
+            for (SIZE_T iByRef = ActiveByRefsToStack.Size(); iByRef > 0;)
             {
+                iByRef--;
+
                 if (RelocAtAddress(&adjustment, ActiveByRefsToStack[iByRef]))
                 {
                     // ByRef was reloc'd, and therefore doesn't need to be handled anymore
@@ -313,7 +316,7 @@ StackWalkAction CaptureTaskletsCore(CrawlFrame* pCf, VOID* data)
     else if (!pCf->GetFunction()->IsAsync2Method())
     {
         // We must be in the wrapper thunk
-        _ASSERTE(pCf->GetFunction()->IsAsyncThunkMethod());
+        _ASSERTE(pCf->GetFunction()->IsAsyncThunkMethod() || (strcmp(pCf->GetFunction()->GetName(), "ResumptionFunc") == 0));
 
         if (taskletCaptureData->ActiveByRefsToStack.Size() != 0)
         {
@@ -355,13 +358,33 @@ StackWalkAction CaptureTaskletsCore(CrawlFrame* pCf, VOID* data)
     stackDataInfo.ReturnAddressOffset = (uint32_t)(returnAddressLocation - (uintptr_t)pCf->GetRegisterSet()->SP);
 
     CQuickArrayList<RegRestore> savedRegRestoreData;
+    bool hasRBPFrame = false;
+
+
+// Find restored reg, and record its location in the frame, so that it can be properly restored. Then update the copied value to be the "current" value of the register, not what we saved off on entry to the function
+#define DISCOVER_RESTORED_REG(REGNAME) \
+    if (pCf->GetRegisterSet()->pCallerContextPointers->REGNAME != pCf->GetRegisterSet()->pCurrentContextPointers->REGNAME)                                                                                         \
+    {                                                                                                                                                                                                              \
+        if ((void*)&pCf->GetRegisterSet()->pCallerContextPointers->REGNAME == (void*)&pCf->GetRegisterSet()->pCallerContextPointers->Rbp)                                                                          \
+            hasRBPFrame = true;                                                                                                                                                                                    \
+        RegRestore restoreData; restoreData.reg = RegisterToRestore::REGNAME;                                                                                                                                      \
+        restoreData.offset = (uint32_t)((uint8_t*)pCf->GetRegisterSet()->pCallerContextPointers->REGNAME - (uint8_t*)pCf->GetRegisterSet()->SP); savedRegRestoreData.Push(restoreData);                            \
+        memcpy((pStackData - taskletCaptureData->stackToIgnoreFromPreviousFrame) + restoreData.offset, &pCf->GetRegisterSet()->pCurrentContext->REGNAME, sizeof(pCf->GetRegisterSet()->pCurrentContext->REGNAME)); \
+    }
+
 
-#define DISCOVER_RESTORED_REG(REGNAME) if (pCf->GetRegisterSet()->pCallerContextPointers->REGNAME != pCf->GetRegisterSet()->pCurrentContextPointers->REGNAME) { RegRestore restoreData; restoreData.reg = RegisterToRestore::REGNAME; restoreData.offset = (uint32_t)((uint8_t*)pCf->GetRegisterSet()->pCallerContextPointers->REGNAME - (uint8_t*)pCf->GetRegisterSet()->SP); savedRegRestoreData.Push(restoreData); }
 #include "restoreregs_for_runtimesuspension.h"
 #undef DISCOVER_RESTORED_REG
 
     RegRestore returnData;
-    returnData.reg = RegisterToRestore::ReturnRegisters;
+    if (hasRBPFrame)
+    {
+        returnData.reg = RegisterToRestore::ReturnRegisters;
+    }
+    else
+    {
+        returnData.reg = RegisterToRestore::ReturnRegistersNoFrame;
+    }
     returnData.offset = 0;
     savedRegRestoreData.Push(returnData);
 
@@ -424,6 +447,16 @@ StackWalkAction CaptureTaskletsCore(CrawlFrame* pCf, VOID* data)
                     &enumData,
                     NO_OVERRIDE_OFFSET);
 
+// HACK for frame pointer handling
+    for (int iRegister = 0; iRegister < savedRegRestoreData.Size(); iRegister++)
+    {
+        if (savedRegRestoreData[iRegister].reg == RegisterToRestore::Rbp)
+        {
+            // If we have Rbp as a saved register, report it as a byref, as its probably the frame pointer and thus in need of adjusting
+            enumGCRefs((void*)&enumData, (OBJECTREF*)(pCf->GetRegisterSet()->SP + savedRegRestoreData[iRegister].offset), GC_CALL_INTERIOR);
+        }
+    }
+
     stackDataInfo.ByRefOffsets = (int32_t*)malloc(enumData.ByRefOffsets.Size() * sizeof(int32_t));
     memcpy(stackDataInfo.ByRefOffsets, enumData.ByRefOffsets.Ptr(), enumData.ByRefOffsets.Size() * sizeof(int32_t));
     stackDataInfo.ObjectRefOffsets = (uint32_t*)malloc(enumData.ObjectRefOffsets.Size() * sizeof(uint32_t));
diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.cs
index 9c504bfa6cdd92..9ac93227268ce0 100644
--- a/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.cs
+++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.cs
@@ -421,7 +421,9 @@ public unsafe void ResumptionFunc()
 
                         while (_nextTasklet != null)
                         {
-                            int maxStackNeeded = _nextTasklet->GetMaxStackNeeded();
+                            Tasklet* pCurTasklet = _nextTasklet;
+                            int maxStackNeeded = pCurTasklet->GetMaxStackNeeded();
+                            _nextTasklet = pCurTasklet->pTaskletNextInStack;
                             if (maxStackNeeded > collectiveStackAllocsPerformed)
                             {
 #pragma warning disable CA2014
@@ -436,13 +438,13 @@ public unsafe void ResumptionFunc()
 
                             try
                             {
-                                switch (_nextTasklet->taskletReturnType)
+                                switch (pCurTasklet->taskletReturnType)
                                 {
                                     case TaskletReturnType.ObjectReference:
-                                        _retValue = new RuntimeAsyncReturnValue(ResumeTaskletReferenceReturn(_nextTasklet, ref _retValue));
+                                        _retValue = new RuntimeAsyncReturnValue(ResumeTaskletReferenceReturn(pCurTasklet, ref _retValue));
                                         break;
                                     case TaskletReturnType.Integer:
-                                        _retValue = new RuntimeAsyncReturnValue(ResumeTaskletIntegerRegisterReturn(_nextTasklet, ref _retValue));
+                                        _retValue = new RuntimeAsyncReturnValue(ResumeTaskletIntegerRegisterReturn(pCurTasklet, ref _retValue));
                                         break;
                                     case TaskletReturnType.ByReference:
                                         throw new NotImplementedException(); // This will be awkward (but not impossible) to implement. Hold off for now
@@ -450,9 +452,8 @@ public unsafe void ResumptionFunc()
                             }
                             finally
                             {
-                                DeleteTasklet(_nextTasklet);
+                                DeleteTasklet(pCurTasklet);
                             }
-                            _nextTasklet = _nextTasklet->pTaskletNextInStack;
                         }
                     }
                     finally

From 56178c396b33cbd7cb09ca6eb99fe5f64445331d Mon Sep 17 00:00:00 2001
From: David Wrighton <davidwr@microsoft.com>
Date: Tue, 17 Oct 2023 16:04:20 -0700
Subject: [PATCH 026/203] It runs? Doesn't handle return value correctly, but
 runs for the right number of iterations, I think

---
 .../CompilerServices/RuntimeHelpers.cs        | 162 ++++--------------
 1 file changed, 32 insertions(+), 130 deletions(-)

diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.cs
index 9ac93227268ce0..b9d65c9f4f585e 100644
--- a/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.cs
+++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.cs
@@ -134,36 +134,13 @@ public static TResult UnsafeAwaitAwaiterFromRuntimeAsync<TResult, TAwaiter>(TAwa
                 Action? resumption = RuntimeHelpers.GetOrCreateResumptionDelegate(ref stackMark);
                 if (resumption != null)
                 {
-                    // We are trying to suspend
-                    bool threwException = true;
-                    try
-                    {
-                        // Call the UnsafeOnCompleted api under a try block, as registering the suspension may cause
-                        // an exception to occur.
-                        awaiter.UnsafeOnCompleted(resumption);
-                        threwException = false;
-                    }
-                    finally
-                    {
-                        // If UnsafeOnCompleted itself threw, we should bubble the error up, but we need
-                        // to destroy any allocated tasklets that were created as part of the GetOrCreateResumptionDelegate api
-                        // as that state will never be useable.
-                        if (threwException)
-                            RuntimeHelpers.AbortSuspend();
-                    }
                     // If we reach here, the only way that we actually run follow on code is for the continuation to actually run,
                     // and return from GetOrCreateResumptionDelegate with a null return value.
                     ref AsyncDataFrame asyncFrame = ref GetCurrentAsyncDataFrame();
                     RuntimeAsyncMaintainedData maintainedData = asyncFrame._maintainedData!;
-                    if (maintainedData._abortSuspend)
-                    {
-                        AbortSuspend();
-                    }
-                    else
-                    {
-                        // This function must be called from the same function that has the stackmark in it.
-                        unsafe { UnwindToFunctionWithAsyncFrame(maintainedData._nextTasklet, maintainedData._suspendActive); }
-                    }
+                    maintainedData._awaiter = awaiter;
+                    // This function must be called from the same function that has the stackmark in it.
+                    unsafe { UnwindToFunctionWithAsyncFrame(maintainedData._nextTasklet, maintainedData._suspendActive); }
                 }
             }
 
@@ -185,42 +162,18 @@ public static TResult AwaitAwaiterFromRuntimeAsync<TResult, TAwaiter>(TAwaiter a
                 Action? resumption = RuntimeHelpers.GetOrCreateResumptionDelegate(ref stackMark);
                 if (resumption != null)
                 {
-                    // We are trying to suspend
-                    bool threwException = true;
-                    try
-                    {
-                        // Call the OnCompleted api under a try block, as registering the suspension may cause
-                        // an exception to occur.
-                        awaiter.OnCompleted(resumption);
-                        threwException = false;
-                    }
-                    finally
-                    {
-                        // If OnCompleted itself threw, we should bubble the error up, but we need
-                        // to destroy any allocated tasklets that were created as part of the GetOrCreateResumptionDelegate api
-                        // as that state will never be useable.
-                        if (threwException)
-                            RuntimeHelpers.AbortSuspend();
-                    }
                     // If we reach here, the only way that we actually run follow on code is for the continuation to actually run,
                     // and return from GetOrCreateResumptionDelegate with a null return value.
                     ref AsyncDataFrame asyncFrame = ref GetCurrentAsyncDataFrame();
                     RuntimeAsyncMaintainedData maintainedData = asyncFrame._maintainedData!;
-                    if (maintainedData._abortSuspend)
-                    {
-                        AbortSuspend();
-                    }
-                    else
-                    {
-                        // This function must be called from the same function that has the stackmark in it.
-                        unsafe { UnwindToFunctionWithAsyncFrame(maintainedData._nextTasklet, maintainedData._suspendActive); }
-                    }
+                    maintainedData._awaiter = awaiter;
+                    // This function must be called from the same function that has the stackmark in it.
+                    unsafe { UnwindToFunctionWithAsyncFrame(maintainedData._nextTasklet, maintainedData._suspendActive); }
                 }
             }
 
             // Get the result from the awaiter, or throw the exception stored in the Task
             return awaiter.GetResult();
-
         }
 
         // TODO, this method should be marked so that it is only callable from a runtime async method
@@ -237,36 +190,13 @@ public static void UnsafeAwaitAwaiterFromRuntimeAsync<TAwaiter>(TAwaiter awaiter
                 Action? resumption = RuntimeHelpers.GetOrCreateResumptionDelegate(ref stackMark);
                 if (resumption != null)
                 {
-                    // We are trying to suspend
-                    bool threwException = true;
-                    try
-                    {
-                        // Call the UnsafeOnCompleted api under a try block, as registering the suspension may cause
-                        // an exception to occur.
-                        awaiter.UnsafeOnCompleted(resumption);
-                        threwException = false;
-                    }
-                    finally
-                    {
-                        // If UnsafeOnCompleted itself threw, we should bubble the error up, but we need
-                        // to destroy any allocated tasklets that were created as part of the GetOrCreateResumptionDelegate api
-                        // as that state will never be useable.
-                        if (threwException)
-                            RuntimeHelpers.AbortSuspend();
-                    }
                     // If we reach here, the only way that we actually run follow on code is for the continuation to actually run,
                     // and return from GetOrCreateResumptionDelegate with a null return value.
                     ref AsyncDataFrame asyncFrame = ref GetCurrentAsyncDataFrame();
                     RuntimeAsyncMaintainedData maintainedData = asyncFrame._maintainedData!;
-                    if (maintainedData._abortSuspend)
-                    {
-                        AbortSuspend();
-                    }
-                    else
-                    {
-                        // This function must be called from the same function that has the stackmark in it.
-                        unsafe { UnwindToFunctionWithAsyncFrame(maintainedData._nextTasklet, maintainedData._suspendActive); }
-                    }
+                    maintainedData._awaiter = awaiter;
+                    // This function must be called from the same function that has the stackmark in it.
+                    unsafe { UnwindToFunctionWithAsyncFrame(maintainedData._nextTasklet, maintainedData._suspendActive); }
                 }
             }
 
@@ -288,36 +218,13 @@ public static void AwaitAwaiterFromRuntimeAsync<TAwaiter>(TAwaiter awaiter) wher
                 Action? resumption = RuntimeHelpers.GetOrCreateResumptionDelegate(ref stackMark);
                 if (resumption != null)
                 {
-                    // We are trying to suspend
-                    bool threwException = true;
-                    try
-                    {
-                        // Call the OnCompleted api under a try block, as registering the suspension may cause
-                        // an exception to occur.
-                        awaiter.OnCompleted(resumption);
-                        threwException = false;
-                    }
-                    finally
-                    {
-                        // If OnCompleted itself threw, we should bubble the error up, but we need
-                        // to destroy any allocated tasklets that were created as part of the GetOrCreateResumptionDelegate api
-                        // as that state will never be useable.
-                        if (threwException)
-                            RuntimeHelpers.AbortSuspend();
-                    }
                     // If we reach here, the only way that we actually run follow on code is for the continuation to actually run,
                     // and return from GetOrCreateResumptionDelegate with a null return value.
                     ref AsyncDataFrame asyncFrame = ref GetCurrentAsyncDataFrame();
                     RuntimeAsyncMaintainedData maintainedData = asyncFrame._maintainedData!;
-                    if (maintainedData._abortSuspend)
-                    {
-                        AbortSuspend();
-                    }
-                    else
-                    {
-                        // This function must be called from the same function that has the stackmark in it.
-                        unsafe { UnwindToFunctionWithAsyncFrame(maintainedData._nextTasklet, maintainedData._suspendActive); }
-                    }
+                    maintainedData._awaiter = awaiter;
+                    // This function must be called from the same function that has the stackmark in it.
+                    unsafe { UnwindToFunctionWithAsyncFrame(maintainedData._nextTasklet, maintainedData._suspendActive); }
                 }
             }
 
@@ -351,11 +258,11 @@ internal abstract unsafe class RuntimeAsyncMaintainedData
         {
             public Action? _resumption;
             public Exception? _exception;
+            public INotifyCompletion? _awaiter;
             public int _suspendActive;
             public bool _initialTaskEntry = true;
             public bool _completed;
             public byte _dummy;
-            public bool _abortSuspend;
 
             public ExecutionContext? _executionCtx;
             public SynchronizationContext? _syncCtx;
@@ -389,12 +296,6 @@ public RuntimeAsyncMaintainedData()
 
             public unsafe void ResumptionFunc()
             {
-                if (HasCurrentAsyncDataFrame() && GetCurrentAsyncDataFrame()._maintainedData == this)
-                {
-                    _abortSuspend = true;
-                    return;
-                }
-
                 // Suspension has finished and we are resuming
                 _suspendActive = 0;
 
@@ -412,6 +313,7 @@ public unsafe void ResumptionFunc()
                         // PushAsyncData will fill in the value of _previousExecutionCtx and _previousSyncCtx
                         if (_syncCtx != dataFrame._previousSyncCtx)
                         {
+                            // TODO: Possibly we should marshal to the right sync context? I dunno. We'll figure this out
                             dataFrame._currentThread._synchronizationContext = _syncCtx;
                         }
                         if (_executionCtx != dataFrame._previousExecutionCtx)
@@ -454,6 +356,12 @@ public unsafe void ResumptionFunc()
                             {
                                 DeleteTasklet(pCurTasklet);
                             }
+
+                            if (_suspendActive != 0)
+                            {
+                                // TODO: we should capture the sync and execution contexts at this point
+                                break;
+                            }
                         }
                     }
                     finally
@@ -465,6 +373,15 @@ public unsafe void ResumptionFunc()
                 {
                     SetException(e);
                 }
+
+                if (_suspendActive != 0)
+                {
+                    // We suspended again. Just register to return to this function, and return
+                    _awaiter!.OnCompleted(_resumption!);
+                    return;
+                }
+                // We are actually done. Set the final result
+                SetResultDone();
             }
 
             private Action? _taskResumer;
@@ -483,6 +400,7 @@ public RuntimeAsyncMaintainedData<T> GetAwaiter()
 
             public override Task GetTask()
             {
+                _awaiter!.OnCompleted(_resumption!);
                 return _task!;
             }
 
@@ -593,11 +511,13 @@ internal static unsafe void PopAsyncData(ref AsyncDataFrame asyncData)
             }
         }
 
+        [MethodImpl(MethodImplOptions.NoInlining)]
         internal static unsafe bool HasCurrentAsyncDataFrame()
         {
             return t_asyncData != null;
         }
 
+        [MethodImpl(MethodImplOptions.NoInlining)]
         private static unsafe ref AsyncDataFrame GetCurrentAsyncDataFrame()
         {
             return ref Unsafe.AsRef<AsyncDataFrame>(t_asyncData);
@@ -637,7 +557,6 @@ private static unsafe ref AsyncDataFrame GetCurrentAsyncDataFrame()
             lastTasklet->pTaskletNextInStack = maintainedData._nextTasklet;
             maintainedData._nextTasklet = nextTaskletInStack;
 
-            maintainedData._abortSuspend = false;
             maintainedData._suspendActive = framesCaptured;
 
             maintainedData._executionCtx = asyncFrame._currentThread._executionContext;
@@ -646,23 +565,6 @@ private static unsafe ref AsyncDataFrame GetCurrentAsyncDataFrame()
 
             return maintainedData._resumption;
         }
-
-        private static unsafe void AbortSuspend()
-        {
-            ref AsyncDataFrame asyncFrame = ref GetCurrentAsyncDataFrame();
-            RuntimeAsyncMaintainedData maintainedData = asyncFrame._maintainedData!;
-
-            Tasklet* pTaskletCur = maintainedData._nextTasklet;
-            while (pTaskletCur != maintainedData._oldTaskletNext)
-            {
-                Tasklet* pTaskletPrev = pTaskletCur;
-                pTaskletCur = pTaskletPrev;
-                DeleteTasklet(pTaskletPrev);
-            }
-            maintainedData._nextTasklet = maintainedData._oldTaskletNext;
-            maintainedData._oldTaskletNext = null;
-            maintainedData._suspendActive = 0;
-        }
 #endif
     }
 }

From 52f272b1b8ede7651e6c1d7de5cbfc55c1929875 Mon Sep 17 00:00:00 2001
From: David Wrighton <davidwr@microsoft.com>
Date: Tue, 17 Oct 2023 16:31:18 -0700
Subject: [PATCH 027/203] Free memory properly

---
 src/coreclr/vm/runtimesuspension.cpp | 9 ++++++---
 1 file changed, 6 insertions(+), 3 deletions(-)

diff --git a/src/coreclr/vm/runtimesuspension.cpp b/src/coreclr/vm/runtimesuspension.cpp
index af35884f42bbdf..4b61c75bc408cb 100644
--- a/src/coreclr/vm/runtimesuspension.cpp
+++ b/src/coreclr/vm/runtimesuspension.cpp
@@ -50,9 +50,9 @@ struct StackDataInfo
         if (ByRefOffsets != NULL)
             free(ByRefOffsets);
         if (ObjectRefOffsets != NULL)
-            free(ByRefOffsets);
+            free(ObjectRefOffsets);
         if (RegistersToRestore != NULL)
-            free(ByRefOffsets);
+            free(RegistersToRestore);
     }
     uint32_t StackRequirement;
     uint32_t UnrecordedDataSize; // From the restored RSP to the data chunk, how many bytes are skipped
@@ -557,5 +557,8 @@ extern "C" Tasklet* QCALLTYPE RuntimeSuspension_CaptureTasklets(QCall::StackCraw
 
 extern "C" void QCALLTYPE RuntimeSuspension_DeleteTasklet(Tasklet* tasklet)
 {
-    // Well, leaking isn't a good plan in the long term, but for now it'll be fine
+    tasklet->pStackDataInfo->CleanupStackDataInfo();
+    free(tasklet->pStackData);
+    free(tasklet->pStackDataInfo);
+    free(tasklet);
 }

From 204633eea51948f087092648389985e65cd6af90 Mon Sep 17 00:00:00 2001
From: Vladimir Sadov <vsadov@microsoft.com>
Date: Wed, 18 Oct 2023 18:33:41 +0000
Subject: [PATCH 028/203] Merged PR 34523: remove DEFINE_METHOD for no longer
 existing methods

remove DEFINE_METHOD for no longer existing methods
---
 src/coreclr/vm/corelib.h | 2 --
 1 file changed, 2 deletions(-)

diff --git a/src/coreclr/vm/corelib.h b/src/coreclr/vm/corelib.h
index f06a9aa6847a9d..42be7407625842 100644
--- a/src/coreclr/vm/corelib.h
+++ b/src/coreclr/vm/corelib.h
@@ -679,8 +679,6 @@ DEFINE_METHOD(RUNTIME_HELPERS,      GET_TAILCALL_INFO,      GetTailCallInfo, NoS
 DEFINE_METHOD(RUNTIME_HELPERS,      DISPATCH_TAILCALLS,     DispatchTailCalls,          NoSig)
 
 DEFINE_METHOD(RUNTIME_HELPERS,      GET_OR_CREATE_RESUMPTION_DELEGATE, GetOrCreateResumptionDelegate, NoSig)
-DEFINE_METHOD(RUNTIME_HELPERS,      ABORT_SUSPEND, AbortSuspend, NoSig)
-DEFINE_METHOD(RUNTIME_HELPERS,      SUSPEND_IF_SUSPENSION_NOT_ABORTED, SuspendIfSuspensionNotAborted, NoSig)
 DEFINE_METHOD(RUNTIME_HELPERS,      UNSAFE_AWAIT_AWAITER_FROM_RUNTIME_ASYNC_2, UnsafeAwaitAwaiterFromRuntimeAsync, GM_U_RetT)
 
 DEFINE_CLASS(UNSAFE,                CompilerServices,       Unsafe)

From f30fa3f8af9e3befd2f752914c7753f1e35cedaf Mon Sep 17 00:00:00 2001
From: David Wrighton <davidwr@microsoft.com>
Date: Wed, 18 Oct 2023 13:57:16 -0700
Subject: [PATCH 029/203] Fix returns within async2 tasks... returning value to
 outer task is still not working

---
 src/coreclr/vm/amd64/AsmHelpers.asm                          | 5 +++++
 src/coreclr/vm/runtimesuspension.cpp                         | 2 +-
 .../src/System/Runtime/CompilerServices/RuntimeHelpers.cs    | 3 ++-
 3 files changed, 8 insertions(+), 2 deletions(-)

diff --git a/src/coreclr/vm/amd64/AsmHelpers.asm b/src/coreclr/vm/amd64/AsmHelpers.asm
index 7b1120ece20830..286b871f21f26e 100644
--- a/src/coreclr/vm/amd64/AsmHelpers.asm
+++ b/src/coreclr/vm/amd64/AsmHelpers.asm
@@ -708,6 +708,10 @@ Check&RegisterName&:
         endm
 
 NESTED_ENTRY RuntimeSuspension_ResumeTaskletReferenceReturn, _TEXT
+; On entry rcx is Tasklet* 
+;          rdx is a pointer to the return value structure
+;
+;          r10 and r8 are used as scratch registers
         push_nonvol_reg rbp  ; We capture RBP to create a stack frame so that we can safely adjust the RSP register, This means that since we always capture this, async2 functions must ALWAYS be compiled with a frame pointer.
         alloc_stack             50h
         set_frame rbp, 50h
@@ -738,6 +742,7 @@ NESTED_ENTRY RuntimeSuspension_ResumeTaskletReferenceReturn, _TEXT
         mov rax, [rbp] 
         mov [rsp+40h], rax
 
+        ; Note that neither rcx or rdx has been modified from entry to this point, and are passed straight through to the PlatformIndependentRestore function
         call PlatformIndependentRestore
         ; RAX now points at the RegRestore array
         mov rcx, [rsp+20h + 10h]
diff --git a/src/coreclr/vm/runtimesuspension.cpp b/src/coreclr/vm/runtimesuspension.cpp
index 4b61c75bc408cb..c41d979f96cbfb 100644
--- a/src/coreclr/vm/runtimesuspension.cpp
+++ b/src/coreclr/vm/runtimesuspension.cpp
@@ -79,8 +79,8 @@ struct Tasklet
 
 struct RuntimeAsyncReturnValue
 {
-    uintptr_t _ptr;
     uintptr_t _obj;
+    uintptr_t _ptr;
     TaskletReturnType _returnType;
 };
 
diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.cs
index b9d65c9f4f585e..90e03ea13c4c02 100644
--- a/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.cs
+++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.cs
@@ -249,8 +249,8 @@ public RuntimeAsyncReturnValue(IntPtr ptr)
                 _ptr = ptr;
                 _returnType = TaskletReturnType.Integer;
             }
-            public IntPtr _ptr;
             public object? _obj;
+            public IntPtr _ptr;
             public TaskletReturnType _returnType;
         }
 
@@ -556,6 +556,7 @@ private static unsafe ref AsyncDataFrame GetCurrentAsyncDataFrame()
             maintainedData._oldTaskletNext = maintainedData._nextTasklet;
             lastTasklet->pTaskletNextInStack = maintainedData._nextTasklet;
             maintainedData._nextTasklet = nextTaskletInStack;
+            maintainedData._retValue = default(RuntimeAsyncReturnValue);
 
             maintainedData._suspendActive = framesCaptured;
 

From 19b5c9935dbfaac1562f7dfbf0aa544682e2ee4b Mon Sep 17 00:00:00 2001
From: David Wrighton <davidwr@microsoft.com>
Date: Wed, 18 Oct 2023 14:14:37 -0700
Subject: [PATCH 030/203] Set the return value on output from this whole thing!
 The example works!

---
 .../Runtime/CompilerServices/RuntimeHelpers.cs      | 13 +++++++++++++
 1 file changed, 13 insertions(+)

diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.cs
index 90e03ea13c4c02..f4eda7aff82683 100644
--- a/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.cs
+++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.cs
@@ -413,6 +413,19 @@ public void SetException(Exception exception)
 
             public void SetResultDone()
             {
+                switch (_retValue._returnType)
+                {
+                    case TaskletReturnType.Integer:
+                        _returnData = Unsafe.As<IntPtr, T>(ref _retValue._ptr);
+                        Thread.MemoryBarrier();
+                        break;
+                    case TaskletReturnType.ObjectReference:
+                        _returnData = Unsafe.As<object, T>(ref _retValue!._obj!)!;
+                        break;
+                    default:
+                        // Other possibiilities not yet implemented
+                        throw new NotImplementedException();
+                }
                 _completed = true;
                 _taskResumer!();
             }

From 41744475b82e33494b9032f76ceb74963570ae1f Mon Sep 17 00:00:00 2001
From: David Wrighton <davidwr@microsoft.com>
Date: Thu, 19 Oct 2023 12:28:17 -0700
Subject: [PATCH 031/203] Fix for code which uses the 32 byte parameter area on
 X64

---
 src/coreclr/vm/runtimesuspension.cpp | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/src/coreclr/vm/runtimesuspension.cpp b/src/coreclr/vm/runtimesuspension.cpp
index c41d979f96cbfb..ec919dbf0eb6f3 100644
--- a/src/coreclr/vm/runtimesuspension.cpp
+++ b/src/coreclr/vm/runtimesuspension.cpp
@@ -329,6 +329,9 @@ StackWalkAction CaptureTaskletsCore(CrawlFrame* pCf, VOID* data)
 
     uint8_t* pTopOfStackInFunction = (uint8_t*)pCf->GetRegisterSet()->SP;
     uint32_t sizeofArgStack = (uint32_t)pCf->GetFunction()->SizeOfArgStack();
+#ifdef TARGET_AMD64  // AND Target windows
+    sizeofArgStack = max(sizeofArgStack, 32); // On Windows X64 there is always a parameter area of 32 bytes, which is for use by the called function
+#endif
     uint8_t* pBottomOfStackInFunction = (uint8_t*)::GetSP(pCf->GetRegisterSet()->pCallerContext) + sizeofArgStack;
     uint32_t sizeofEntireMeaningfulStack = (uint32_t)(pBottomOfStackInFunction - pTopOfStackInFunction);
     sizeofEntireMeaningfulStack -= (uint32_t)taskletCaptureData->stackToIgnoreFromPreviousFrame;

From 989c4cb5cc53f66448b02b05de9308028d47baf7 Mon Sep 17 00:00:00 2001
From: David Wrighton <davidwr@microsoft.com>
Date: Thu, 19 Oct 2023 16:55:00 -0700
Subject: [PATCH 032/203] Add corjit flags and corinfo for the new async work

---
 src/coreclr/inc/corinfo.h             |  2 ++
 src/coreclr/inc/corjitflags.h         |  1 +
 src/coreclr/vm/jitinterface.cpp       | 11 +++++++++++
 src/coreclr/vm/methodtablebuilder.cpp | 14 ++++++++++++--
 4 files changed, 26 insertions(+), 2 deletions(-)

diff --git a/src/coreclr/inc/corinfo.h b/src/coreclr/inc/corinfo.h
index 4941bf56f7d68a..97e0d385753ce3 100644
--- a/src/coreclr/inc/corinfo.h
+++ b/src/coreclr/inc/corinfo.h
@@ -755,6 +755,7 @@ enum CorInfoCallConv
     CORINFO_CALLCONV_HASTHIS    = 0x20,
     CORINFO_CALLCONV_EXPLICITTHIS=0x40,
     CORINFO_CALLCONV_PARAMTYPE  = 0x80,     // Passed last. Same as CORINFO_GENERICS_CTXT_FROM_PARAMTYPEARG
+    CORINFO_CALLCONV_ASYNCCALL  = 0x100,    // Is this a call to an async function?
 };
 
 // Represents the calling conventions supported with the extensible calling convention syntax
@@ -1137,6 +1138,7 @@ struct CORINFO_SIG_INFO
     unsigned            totalILArgs()       { return (numArgs + (hasImplicitThis() ? 1 : 0)); }
     bool                isVarArg()          { return ((getCallConv() == CORINFO_CALLCONV_VARARG) || (getCallConv() == CORINFO_CALLCONV_NATIVEVARARG)); }
     bool                hasTypeArg()        { return ((callConv & CORINFO_CALLCONV_PARAMTYPE) != 0); }
+    bool                isAsyncCall()       { return ((callConv & CORINFO_CALLCONV_ASYNCCALL) != 0); }
 };
 
 struct CORINFO_METHOD_INFO
diff --git a/src/coreclr/inc/corjitflags.h b/src/coreclr/inc/corjitflags.h
index dce9a9f00b3998..e1c569e46ad499 100644
--- a/src/coreclr/inc/corjitflags.h
+++ b/src/coreclr/inc/corjitflags.h
@@ -68,6 +68,7 @@ class CORJIT_FLAGS
         CORJIT_FLAG_VECTOR512_THROTTLING    = 31, // On x86/x64, 512-bit vector usage may incur CPU frequency throttling
 #endif
 
+        CORJIT_FLAG_RUNTIMEASYNCFUNCTION    = 32, // Generate Code for use as an async2 function
     };
 
     CORJIT_FLAGS()
diff --git a/src/coreclr/vm/jitinterface.cpp b/src/coreclr/vm/jitinterface.cpp
index d577d15c37c36a..6b4dba089e19e6 100644
--- a/src/coreclr/vm/jitinterface.cpp
+++ b/src/coreclr/vm/jitinterface.cpp
@@ -460,6 +460,8 @@ enum ConvToJitSigFlags : int
 // localSig     - Is it a local variables declaration, or a method signature (with return type, etc).
 // contextType  - The type with any instantiaton information
 //
+AsyncTaskMethod ClassifyAsyncMethodCore(SigPointer sig, Module* pModule, PCCOR_SIGNATURE initialSig, ULONG* offsetOfAsyncDetails);
+
 static void ConvToJitSig(
     PCCOR_SIGNATURE       pSig,
     DWORD                 cbSig,
@@ -544,6 +546,12 @@ static void ConvToJitSig(
         sigRet->retType = CEEInfo::asCorInfoType(type, typeHnd, &sigRet->retTypeClass);
         sigRet->retTypeSigClass = CORINFO_CLASS_HANDLE(typeHnd.AsPtr());
 
+        auto asyncMethodClassification = ClassifyAsyncMethodCore(sig, module, NULL, NULL);
+        if ((asyncMethodClassification == AsyncTaskMethod::Async2Method) || (asyncMethodClassification == AsyncTaskMethod::Async2Method))
+        {
+            sigRet->callConv = (CorInfoCallConv)(sigRet->callConv | CORINFO_CALLCONV_ASYNCCALL);
+        }
+
         IfFailThrow(sig.SkipExactlyOne());  // must to a skip so we skip any class tokens associated with the return type
         _ASSERTE(sigRet->retType < CORINFO_TYPE_COUNT);
 
@@ -12601,6 +12609,9 @@ static CORJIT_FLAGS GetCompileFlags(PrepareCodeConfig* prepareConfig, MethodDesc
     //
     flags.Add(CEEInfo::GetBaseCompileFlags(ftn));
 
+    if (ftn->IsAsync2Method())
+        flags.Add(CORJIT_FLAGS::CORJIT_FLAG_RUNTIMEASYNCFUNCTION);
+
     //
     // Get CPU specific flags
     //
diff --git a/src/coreclr/vm/methodtablebuilder.cpp b/src/coreclr/vm/methodtablebuilder.cpp
index 7419681a103dea..db8788bdcc6ae4 100644
--- a/src/coreclr/vm/methodtablebuilder.cpp
+++ b/src/coreclr/vm/methodtablebuilder.cpp
@@ -2748,6 +2748,8 @@ bool IsTypeDefOrRefAByRefStruct(Module* pModule, mdToken tk)
     return false;
 }
 
+AsyncTaskMethod ClassifyAsyncMethodCore(SigPointer sig, Module* pModule, PCCOR_SIGNATURE initialSig, ULONG* offsetOfAsyncDetails);
+
 AsyncTaskMethod ClassifyAsyncMethod(SigPointer sig, Module* pModule, ULONG* offsetOfAsyncDetails)
 {
     PCCOR_SIGNATURE initialSig = sig.GetPtr();
@@ -2761,13 +2763,20 @@ AsyncTaskMethod ClassifyAsyncMethod(SigPointer sig, Module* pModule, ULONG* offs
 
     // Return argument count
     IfFailThrow(sig.GetData(&data));
+    return ClassifyAsyncMethodCore(sig, pModule, initialSig, offsetOfAsyncDetails);
+}
+
+AsyncTaskMethod ClassifyAsyncMethodCore(SigPointer sig, Module* pModule, PCCOR_SIGNATURE initialSig, ULONG* offsetOfAsyncDetails)
+{
+    uint32_t data;
 
     // Now we should be parsing the return type
 
     // If the first custommodifier is a MOD_OPT to CallConvAsync2Call
     // Then this is a async2 function
     CorElementType elemType;
-    *offsetOfAsyncDetails = (ULONG)(sig.GetPtr() - initialSig);
+    if (offsetOfAsyncDetails != NULL)
+        *offsetOfAsyncDetails = (ULONG)(sig.GetPtr() - initialSig);
     BYTE elemTypeByte;
     IfFailThrow(sig.GetByte(&elemTypeByte)); // Don't use GetElemType as it skips custom modifiers
     elemType = (CorElementType)elemTypeByte;
@@ -2835,7 +2844,8 @@ AsyncTaskMethod ClassifyAsyncMethod(SigPointer sig, Module* pModule, ULONG* offs
             }
         }
 
-        *offsetOfAsyncDetails = (ULONG)(sig.GetPtr() - initialSig) - 1;
+        if (offsetOfAsyncDetails != NULL)
+            *offsetOfAsyncDetails = (ULONG)(sig.GetPtr() - initialSig) - 1;
     }
 
     if (elemType == ELEMENT_TYPE_GENERICINST)

From 9c7a44461fd3a7a96cab2c382c0e03dbfd564b8e Mon Sep 17 00:00:00 2001
From: David Wrighton <davidwr@microsoft.com>
Date: Fri, 20 Oct 2023 09:58:42 -0700
Subject: [PATCH 033/203] Add test case compiled with the new compiler

---
 .../async/async2fibonacci-with-yields.cs      | 61 +++++++++++++++++++
 .../async/async2fibonacci-with-yields.csproj  |  8 +++
 2 files changed, 69 insertions(+)
 create mode 100644 src/tests/Loader/async/async2fibonacci-with-yields.cs
 create mode 100644 src/tests/Loader/async/async2fibonacci-with-yields.csproj

diff --git a/src/tests/Loader/async/async2fibonacci-with-yields.cs b/src/tests/Loader/async/async2fibonacci-with-yields.cs
new file mode 100644
index 00000000000000..5e2c09a4926ce8
--- /dev/null
+++ b/src/tests/Loader/async/async2fibonacci-with-yields.cs
@@ -0,0 +1,61 @@
+// 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 System.Threading.Tasks;
+using System.Diagnostics;
+
+class Async2FibonacceWithYields
+{
+    const uint Threshold = 1_000;
+    static bool done;
+
+    public static void Main()
+    {
+        AsyncEntry().Wait();
+    }
+
+    public static async Task AsyncEntry()
+    {
+        for (int i = 0; i < 10 && !done; i++)
+        {
+            var sw = new Stopwatch();
+            sw.Start();
+            uint result = await A(100_000_000);
+            Console.WriteLine($"{sw.ElapsedMilliseconds} ms result={result}");
+        }
+    }
+
+    static async2 uint A(uint n)
+    {
+        uint result = n;
+        for (uint i = 0; i < n && !done; i++)
+            result = await B(result);
+        return result;
+    }
+
+    static async2 uint B(uint n)
+    {
+        uint result = n;
+
+        result = result * 1_999_999_981;
+        if (result < Threshold)
+        {
+            await Task.Yield();
+        }
+
+        result = result * 1_999_999_981;
+        if (result < Threshold)
+        {
+            await Task.Yield();
+        }
+
+        result = result * 1_999_999_981;
+        if (result < Threshold)
+        {
+            await Task.Yield();
+        }
+
+        return result;
+    }
+}
diff --git a/src/tests/Loader/async/async2fibonacci-with-yields.csproj b/src/tests/Loader/async/async2fibonacci-with-yields.csproj
new file mode 100644
index 00000000000000..f67acfa29a9565
--- /dev/null
+++ b/src/tests/Loader/async/async2fibonacci-with-yields.csproj
@@ -0,0 +1,8 @@
+<Project Sdk="Microsoft.NET.Sdk">
+  <PropertyGroup>
+    <CLRTestPriority>1</CLRTestPriority>
+  </PropertyGroup>
+  <ItemGroup>
+    <Compile Include="async2fibonacci-with-yields.cs" />
+  </ItemGroup>
+</Project>

From bd38a81e04c1ba12aba336b0bf589140123e262e Mon Sep 17 00:00:00 2001
From: David Wrighton <davidwr@microsoft.com>
Date: Fri, 20 Oct 2023 10:19:55 -0700
Subject: [PATCH 034/203] Fix issue exposed by merging to latest main bits

---
 src/coreclr/vm/runtimesuspension.cpp | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/src/coreclr/vm/runtimesuspension.cpp b/src/coreclr/vm/runtimesuspension.cpp
index ec919dbf0eb6f3..ed5c5629177a35 100644
--- a/src/coreclr/vm/runtimesuspension.cpp
+++ b/src/coreclr/vm/runtimesuspension.cpp
@@ -305,6 +305,14 @@ StackWalkAction CaptureTaskletsCore(CrawlFrame* pCf, VOID* data)
     _ASSERTE(pFunc);
 
     TaskletCaptureData* taskletCaptureData = (TaskletCaptureData*) data;
+
+    // Ignore any frames before we get to the interesting methods
+    if (!pCf->IsFrameless())
+    {
+        _ASSERTE(taskletCaptureData->firstTasklet == NULL); // This should only happen for cases before we get to interesting components of the stack
+        return SWA_CONTINUE;
+    }
+
     if (taskletCaptureData->firstTasklet == NULL)
     {
         if (!IsInCurrentFrame(pCf->GetRegisterSet(), taskletCaptureData->stackMark))

From 41aaae82df198fd7b2b9b1183f7ecdc5a9a47e99 Mon Sep 17 00:00:00 2001
From: David Wrighton <davidwr@microsoft.com>
Date: Fri, 20 Oct 2023 16:18:48 -0700
Subject: [PATCH 035/203] Add GC Support to unwind based async implementation

---
 src/coreclr/vm/amd64/AsmHelpers.asm  | 15 ++++--
 src/coreclr/vm/ceemain.cpp           |  3 +-
 src/coreclr/vm/gcenv.ee.cpp          |  7 +++
 src/coreclr/vm/runtimesuspension.cpp | 78 ++++++++++++++++++++++++++++
 src/coreclr/vm/runtimesuspension.h   |  5 ++
 5 files changed, 104 insertions(+), 4 deletions(-)

diff --git a/src/coreclr/vm/amd64/AsmHelpers.asm b/src/coreclr/vm/amd64/AsmHelpers.asm
index 286b871f21f26e..2473822036c140 100644
--- a/src/coreclr/vm/amd64/AsmHelpers.asm
+++ b/src/coreclr/vm/amd64/AsmHelpers.asm
@@ -683,6 +683,7 @@ NESTED_END OnCallCountThresholdReachedStub, _TEXT
 endif ; FEATURE_TIERED_COMPILATION
 
 extern PlatformIndependentRestore:proc
+extern ForceThisThreadHasNoHijackForUnwind:proc
 
 ; regRestore swaps whats in the register for the value in the restoration location. The idea is that the
 ; value in the restoration location is the value that has been stored away for use in this function
@@ -811,7 +812,7 @@ Unwind&RegisterName&:
     jmp RegUnwindLoop
         endm
 
-LEAF_ENTRY RuntimeSuspension_UnwindToFunctionWithAsyncFrame, _TEXT
+NESTED_ENTRY RuntimeSuspension_UnwindToFunctionWithAsyncFrame, _TEXT
 
 ;    // Psuedocode...
 ;    Tasklet* pCurTasklet = topTasklet;
@@ -829,6 +830,14 @@ LEAF_ENTRY RuntimeSuspension_UnwindToFunctionWithAsyncFrame, _TEXT
 ;    }
 ;    Jmp ip
 
+    alloc_stack             38h
+    END_PROLOGUE
+    mov [rsp+20h], rcx
+    mov [rsp+28h], rdx
+    call ForceThisThreadHasNoHijackForUnwind
+    mov rcx, [rsp+20h]
+    mov rdx, [rsp+28h]
+    add rsp, 40h
 
 ; rcx is pCurTasklet
 ; rdx is count of tasklets to unwind
@@ -838,7 +847,6 @@ LEAF_ENTRY RuntimeSuspension_UnwindToFunctionWithAsyncFrame, _TEXT
 ; r10 is second scratch register
 ; r8 is pRegRestore
 ; r9 is pStackDataInfo
-        pop rax; Pop off saved IP, so that we start with the right SP set_frame
 NewTaskletUnwind:
         mov r9, [rcx+28h] ; Load r9 with the pStackDataInfo value on the current tasklet
         mov r8, [r9+30h] ; Load r8 with the pRegRestore value
@@ -888,5 +896,6 @@ TaskletUnwindDone:
         mov rcx, rax ; Move return address into rcx
         xor rax, rax ; Clear return register in case it is supposed to be an object reference or something  -- TODO, note that for structure returns this will mean we need to slightly adjust the calling convention so that we don't rely on this being a pointer to the return buffer
         jmp rcx
-LEAF_END RuntimeSuspension_UnwindToFunctionWithAsyncFrame, _TEXT
+NESTED_END RuntimeSuspension_UnwindToFunctionWithAsyncFrame, _TEXT
+
         end
\ No newline at end of file
diff --git a/src/coreclr/vm/ceemain.cpp b/src/coreclr/vm/ceemain.cpp
index c5eaacd0c168ee..bfdd1f6dd085e5 100644
--- a/src/coreclr/vm/ceemain.cpp
+++ b/src/coreclr/vm/ceemain.cpp
@@ -206,6 +206,7 @@
 #endif // FEATURE_GDBJIT
 
 #include "genanalysis.h"
+#include "runtimesuspension.h"
 
 static int GetThreadUICultureId(_Out_ LocaleIDValue* pLocale);  // TODO: This shouldn't use the LCID.  We should rely on name instead
 
@@ -693,7 +694,7 @@ void EEStartupHelper()
 
         Frame::Init();
 
-
+        InitializeTasklets();
 
 
 #ifdef LOGGING
diff --git a/src/coreclr/vm/gcenv.ee.cpp b/src/coreclr/vm/gcenv.ee.cpp
index 31bfab32e755f2..108ac06eaad801 100644
--- a/src/coreclr/vm/gcenv.ee.cpp
+++ b/src/coreclr/vm/gcenv.ee.cpp
@@ -26,6 +26,8 @@
 #include "genanalysis.h"
 #include "eventpipeadapter.h"
 
+#include "runtimesuspension.h"
+
 // Finalizes a weak reference directly.
 extern void FinalizeWeakReference(Object* obj);
 
@@ -323,6 +325,11 @@ void GCToEEInterface::GcScanRoots(promote_func* fn, int condemned, int max_gen,
             SystemDomain::EnumAllStaticGCRefs(fn, sc);
         }
     }
+
+#ifndef DACCESS_COMPILE
+// TODO Make tasklet reporting DAC friendly
+    IterateTaskletsForGC(fn, sc);
+#endif
 }
 
 void GCToEEInterface::GcStartWork (int condemned, int max_gen)
diff --git a/src/coreclr/vm/runtimesuspension.cpp b/src/coreclr/vm/runtimesuspension.cpp
index ed5c5629177a35..6b2c51a44d2aa5 100644
--- a/src/coreclr/vm/runtimesuspension.cpp
+++ b/src/coreclr/vm/runtimesuspension.cpp
@@ -525,6 +525,7 @@ PORTABILIT_ASSERT()
     pTasklet->restoreIPAddress = (uintptr_t)GetControlPC(pCf->GetRegisterSet());
     pTasklet->pStackDataInfo = pStackDataInfo;
     pTasklet->taskletReturnType = taskletReturnType;
+    RegisterTasklet(pTasklet);
 
     if (taskletCaptureData->firstTasklet == NULL)
     {
@@ -568,8 +569,85 @@ extern "C" Tasklet* QCALLTYPE RuntimeSuspension_CaptureTasklets(QCall::StackCraw
 
 extern "C" void QCALLTYPE RuntimeSuspension_DeleteTasklet(Tasklet* tasklet)
 {
+    UnregisterTasklet(tasklet);
     tasklet->pStackDataInfo->CleanupStackDataInfo();
     free(tasklet->pStackData);
     free(tasklet->pStackDataInfo);
     free(tasklet);
 }
+
+void RegisterTasklet(Tasklet* pTasklet);
+void InitializeTasklets();
+void UnregisterTasklet(Tasklet* pTasklet);
+
+Tasklet *g_pTaskletSentinel = NULL;
+
+CrstStatic g_taskletCrst;
+
+void InitializeTasklets()
+{
+    g_taskletCrst.Init(CrstLeafLock, CRST_UNSAFE_ANYMODE);
+
+}
+
+void RegisterTasklet(Tasklet* pTasklet)
+{
+    CrstHolder crstHolder(&g_taskletCrst);
+    if (g_pTaskletSentinel == NULL)
+    {
+        g_pTaskletSentinel = (Tasklet*)malloc(sizeof(Tasklet));
+        memset(g_pTaskletSentinel, 0, sizeof(Tasklet));
+        g_pTaskletSentinel->pTaskletNextInLiveList = g_pTaskletSentinel;
+        g_pTaskletSentinel->pTaskletPrevInLiveList = g_pTaskletSentinel;
+    }
+
+    pTasklet->pTaskletNextInLiveList = g_pTaskletSentinel->pTaskletNextInLiveList;
+    pTasklet->pTaskletPrevInLiveList = g_pTaskletSentinel;
+    g_pTaskletSentinel->pTaskletNextInLiveList = pTasklet;
+    pTasklet->pTaskletNextInLiveList->pTaskletPrevInLiveList = pTasklet;
+}
+
+void UnregisterTasklet(Tasklet* pTasklet)
+{
+    CrstHolder crstHolder(&g_taskletCrst);
+    pTasklet->pTaskletPrevInLiveList->pTaskletNextInLiveList = pTasklet->pTaskletNextInLiveList;
+    pTasklet->pTaskletNextInLiveList->pTaskletPrevInLiveList = pTasklet->pTaskletPrevInLiveList;
+}
+
+void IterateTaskletsForGC(promote_func* pCallback, ScanContext* sc)
+{
+    CrstHolder crstHolder(&g_taskletCrst);
+    Tasklet *pCurTasklet = g_pTaskletSentinel->pTaskletNextInLiveList;
+    while (pCurTasklet != g_pTaskletSentinel)
+    {
+        // Report GC pointers
+        auto pStackDataInfo = pCurTasklet->pStackDataInfo;
+        uint8_t *pLogicalRSP = pCurTasklet->pStackData - pStackDataInfo->UnrecordedDataSize;
+        uint32_t iRef;
+        for (iRef = 0; iRef < pStackDataInfo->cObjectRefs; iRef++)
+        {
+            pCallback((PTR_PTR_Object)(pLogicalRSP + pStackDataInfo->ObjectRefOffsets[iRef]), sc, 0);
+        }
+        
+        for (iRef = 0; iRef < pStackDataInfo->cObjectRefs; iRef++)
+        {
+            int32_t offset = pStackDataInfo->ByRefOffsets[iRef];
+            uint32_t flags = GC_CALL_INTERIOR;
+            if (offset < 0)
+            {
+                offset = -offset;
+                flags |= GC_CALL_PINNED;
+            }
+
+            pCallback((PTR_PTR_Object)(pLogicalRSP + offset), sc, flags);
+        }
+
+        pCurTasklet = pCurTasklet->pTaskletNextInLiveList;
+    }
+}
+
+extern "C" void ForceThisThreadHasNoHijackForUnwind()
+{
+    // If there is a hijack of a frame in place and we unwind, who knows what's going to happen. This code just forces the hijack to disappear. The runtime should put it back later if it matters.
+    GetThread()->UnhijackThread();
+}
diff --git a/src/coreclr/vm/runtimesuspension.h b/src/coreclr/vm/runtimesuspension.h
index d06e7a149d7720..4f9b23f01d65a7 100644
--- a/src/coreclr/vm/runtimesuspension.h
+++ b/src/coreclr/vm/runtimesuspension.h
@@ -12,3 +12,8 @@ EXTERN_C FCDECL1(void, RuntimeSuspension_UnwindToFunctionWithAsyncFrame, AsyncDa
 
 EXTERN_C FCDECL2(Object*, RuntimeSuspension_ResumeTaskletReferenceReturn, Tasklet* tasklet, RuntimeAsyncReturnValue *resumptionReturnValue);
 EXTERN_C FCDECL2(Object*, RuntimeSuspension_ResumeTaskletIntegerRegisterReturn, Tasklet* tasklet, RuntimeAsyncReturnValue *resumptionReturnValue);
+
+void RegisterTasklet(Tasklet* pTasklet);
+void InitializeTasklets();
+void UnregisterTasklet(Tasklet* pTasklet);
+void IterateTaskletsForGC(promote_func* pCallback, ScanContext* sc);

From 18d7cddd729af7ffd46bb46fb72f34fafba9e011 Mon Sep 17 00:00:00 2001
From: Jakob Botsch Nielsen <janielsen@microsoft.com>
Date: Fri, 20 Oct 2023 21:15:38 +0000
Subject: [PATCH 036/203] Merged PR 34691: Prototype for generating state
 machines in JIT for async

---
 ...async-with-jit-generated-state-machines.md | 481 +++++++++
 .../RuntimeHelpers.CoreCLR.cs                 |  16 +
 .../src/System/StubHelpers.cs                 |   3 +
 src/coreclr/inc/clrconfigvalues.h             |   5 +
 src/coreclr/inc/corinfo.h                     |  25 +
 src/coreclr/inc/icorjitinfoimpl_generated.h   |   5 +
 src/coreclr/inc/jiteeversionguid.h            |  10 +-
 src/coreclr/inc/jithelpers.h                  |   2 +
 src/coreclr/jit/CMakeLists.txt                |   2 +
 src/coreclr/jit/ICorJitInfo_names_generated.h |   2 +
 .../jit/ICorJitInfo_wrapper_generated.hpp     |  16 +
 src/coreclr/jit/async.cpp                     | 999 ++++++++++++++++++
 src/coreclr/jit/async.h                       |  59 ++
 src/coreclr/jit/block.cpp                     |   4 +
 src/coreclr/jit/block.h                       |   5 +-
 src/coreclr/jit/codegen.h                     |   2 +
 src/coreclr/jit/codegencommon.cpp             |  25 +
 src/coreclr/jit/codegenxarch.cpp              |  20 +
 src/coreclr/jit/compiler.cpp                  |   5 +
 src/coreclr/jit/compiler.h                    |  26 +
 src/coreclr/jit/compiler.hpp                  |   8 +
 src/coreclr/jit/compmemkind.h                 |   1 +
 src/coreclr/jit/compphases.h                  |   1 +
 src/coreclr/jit/fgdiagnostic.cpp              |  10 +
 src/coreclr/jit/forwardsub.cpp                |   2 +-
 src/coreclr/jit/gentree.cpp                   |  24 +
 src/coreclr/jit/gentree.h                     |   6 +
 src/coreclr/jit/gtlist.h                      |   3 +
 src/coreclr/jit/importer.cpp                  |   1 +
 src/coreclr/jit/importercalls.cpp             |  74 +-
 src/coreclr/jit/jitconfigvalues.h             |   3 +
 src/coreclr/jit/jitee.h                       |   1 +
 src/coreclr/jit/lclvars.cpp                   |  94 +-
 src/coreclr/jit/lir.h                         |  14 +
 src/coreclr/jit/liveness.cpp                  |   1 +
 src/coreclr/jit/lsraxarch.cpp                 |   6 +
 src/coreclr/jit/morph.cpp                     |   2 +
 src/coreclr/jit/namedintrinsiclist.h          |   1 +
 src/coreclr/jit/targetamd64.h                 |   3 +
 src/coreclr/jit/targetarm.h                   |   3 +
 src/coreclr/jit/targetarm64.h                 |   3 +
 src/coreclr/jit/targetx86.h                   |   3 +
 src/coreclr/jit/valuenum.cpp                  |   6 +-
 .../Common/JitInterface/CorInfoHelpFunc.cs    |   2 +
 .../tools/Common/JitInterface/CorInfoImpl.cs  |  15 +
 .../JitInterface/CorInfoImpl_generated.cs     | 149 +--
 .../tools/Common/JitInterface/CorInfoTypes.cs |  13 +
 .../ThunkGenerator/ThunkInput.txt             |   3 +
 .../aot/jitinterface/jitinterface_generated.h |  18 +
 .../superpmi-shim-collector/icorjitinfo.cpp   |  11 +
 .../icorjitinfo_generated.cpp                 |  13 +
 .../icorjitinfo_generated.cpp                 |  11 +
 .../tools/superpmi/superpmi/icorjitinfo.cpp   |  12 +
 src/coreclr/vm/appdomain.cpp                  |   1 +
 src/coreclr/vm/corelib.h                      |  11 +-
 src/coreclr/vm/dllimport.h                    |   7 +
 src/coreclr/vm/ecall.cpp                      |   9 +
 src/coreclr/vm/ecall.h                        |   2 +
 src/coreclr/vm/ilstubcache.cpp                |   6 +
 src/coreclr/vm/jitinterface.cpp               | 251 ++++-
 src/coreclr/vm/method.hpp                     |   2 +
 src/coreclr/vm/prestub.cpp                    |   2 +
 src/coreclr/vm/stubgen.cpp                    |  10 +
 src/coreclr/vm/stubgen.h                      |   2 +
 64 files changed, 2433 insertions(+), 99 deletions(-)
 create mode 100644 docs/design/features/async-with-jit-generated-state-machines.md
 create mode 100644 src/coreclr/jit/async.cpp
 create mode 100644 src/coreclr/jit/async.h

diff --git a/docs/design/features/async-with-jit-generated-state-machines.md b/docs/design/features/async-with-jit-generated-state-machines.md
new file mode 100644
index 00000000000000..56b65068ce30b1
--- /dev/null
+++ b/docs/design/features/async-with-jit-generated-state-machines.md
@@ -0,0 +1,481 @@
+# Async with JIT generated state machines
+
+## Introduction
+
+This document describes a potential way to move the async transformation from Roslyn to the JIT.
+The approach described here has the JIT transform async methods into a state machine in a way that such async methods can both suspend and be resumed.
+As a goal, the design strives to make modifications such that the impact on the JIT's codegen of the synchronous path is minimized.
+
+To achieve that, we design around a more or less standard call/return stack pattern in the synchronous case, and then build on top of it the necessary infrastructure to handle resumption.
+
+## Dispatching continuations
+
+The main complication happens when continuations are to be dispatched.
+When that occurs the stack logically inverts compared to the normal synchronous call: the deepest frame's continuation has to be called first, and once it returns, it has to ensure that it calls the next continuation.
+There are multiple issues that the machinery today has to handle around this:
+1. It has to move continuations to other stack frames to ensure the stack is not exhausted
+2. It has to propagate exceptions thrown inside later frames to the next continuations; it cannot rely on normal EH to propagate exceptions, since the logical callers are no longer on the stack.
+
+I expect that 2. has some impact on the quality of the JIT generated code since the JIT can be more conservative inside EH constructs; however, the impact may not be that high given that much of the state is not expected to be live in the handler.
+
+To solve both issues we propose using an an async dispatcher.
+The idea is to move the responsibility of invoking follow-up continuations from the async method to the async dispatcher.
+This is very similar to the current tailcall via helpers approach.
+
+The async dispatcher will look something like (details omitted, full version of this is given below):
+
+```csharp
+enum Suspended
+{
+    Yes,
+    No,
+}
+
+struct Continuation
+{
+    public object Next;
+    public delegate*<object, Exception, Suspended> Resume;
+    public int State;
+}
+
+public static void DispatchContinuations(object boxedContinuation, Exception exToPropagate)
+{
+    do
+    {
+        ref Continuation continuation = ref Unsafe.Unbox<Continuation>(boxedContinuation);
+        try
+        {
+            if (continuation.Resume(boxedContinuation, exToPropagate) == Suspended.Yes)
+            {
+                break;
+            }
+        }
+        catch (Exception ex)
+        {
+            exToPropagate = ex;
+        }
+
+        boxedContinuation = continuation.Next;
+    } while (boxedContinuation != null);
+}
+```
+
+The first time an asynchronous continuation is resumed it is expected that such a dispatcher is set up on the stack frame by the code doing the actual asynchronous resumption (for example, a socket receive callback).
+
+The JIT must set up all the stuff to make the above dispatching work.
+1. It has to generate code to create a continuation when suspension happens.
+   Logically, the continuation captures the live state of the function that is suspending, including some way to figure out where in the function suspension happened.
+   Furthermore, the continuation must somehow be registered with the callee to know what to call when it is resuming.
+   
+2. It has to generate code to allow resuming from a continuation.
+   This involves:
+   1. Determining where in the function control needs to be transferred back to.
+      It may not always be possible to directly transfer control back, in particular when the function has EH, in which case we can employ a "switchboard" approach at the beginning of each `try` (OSR already must do this).
+   2. Based on the above, determining what we expect to have been saved in the continuation and restoring registers/stack slots from it.
+   3. If any resumption exception is present, invoking necessary EH constructs based on it. 
+      The simple way to do this is just to rethrow the exception after transferring control back to the suspension point.
+      Optimizations here may be possible, for example by having the dispatcher skip continuations without EH.
+
+The continuation also saves space for the return value of the callee.
+
+## Resumption stubs
+We expect to split the second step above into two components.
+1. A part that is handled by the JIT as part of generating the async method and that becomes part of that function's codegen.
+   We'd like to ensure this part has minimal negative impact on the CQ of the async method to not harm the common synchronous path.
+   This code will involve reloading registers and stack locations, and transferring control back to the right place in the function.
+   It will mean the async methods have a special calling convention to identify whether they are being resumed or whether they are being invoked normally.
+2. The second part is an IL stub created by the runtime on request of the JIT.
+   The continuations created by the JIT will point at such a resumption stub.
+   The resumption stubs are necessary because of the following:
+   1. When the function is resumed, something must allocate stack space for the arguments and set registers containing arguments to sensible values, for stack scanning/GC purposes
+   2. If the resumed function returns normally, then something must propagate the return value back to the next continuation, if required
+   3. Since the async method has a special calling convention for resumption, something needs to know how to call it specially and where to pass the continuation.
+
+## Continuation layout
+The `Continuation` struct shown above will be a prefix of the continuation struct created by the JIT.
+For example, for the following case:
+
+```csharp
+static async2 void Foo(int a, int b)
+{
+    BarReturn ret = await Bar();
+    b += ret.val + a;
+    int ret2 = await Baz();
+    Console.WriteLine(b);
+}
+
+// uninlined
+static async2 BarReturn Bar()
+{
+    return new BarReturn {val = 0};
+}
+
+// uninlined
+static async2 int Baz()
+{
+    await Yield();
+    return 0;
+}
+```
+
+The runtime/JIT would create these continuation structures:
+
+```csharp
+struct ContinuationFoo1
+{
+    public object Next;
+    public delegate*<ref ContinuationFoo1, Exception, object> Resume;
+    public int State;
+    public BarReturn Returned;
+    public int a;
+    public int b;
+}
+
+struct ContinuationFoo2
+{
+    public object Next;
+    public delegate*<ref ContinuationFoo2, Exception, object> Resume;
+    public int State;
+    public int Returned;
+    // int a; not necessary since a is not live here
+    public int b;
+}
+
+struct ContinuationBaz1
+{
+    public object Next;
+    public delegate*<ref ContinuationBaz1, Exception, object> Resume;
+    public int State;
+}
+```
+
+and the resumption stubs may look like:
+```csharp
+object ResumeFoo1(object continuation, Exception exToPropagate)
+{
+    object newContinuation = null;
+    // No return value to propagate back since 'Foo' returns void
+    RuntimeHelpers.Async2Call(ref newContinuation);
+    Foo();
+    return newContinuation;
+}
+
+object ResumeBaz1(object continuation, Exception exToPropagate)
+{
+    object newContinuation = null;
+    RuntimeHelpers.Async2Call(ref newContinuation);
+    int bazResult = Baz();
+    if (newContinuation == null)
+    {
+        // Points to ContinuationFoo2 always in the example above
+        Unsafe.Unbox<ContinuationWithResult<int>>(continuation.Next).Result = bazResult;
+    }
+
+    return newContinuation;
+}
+
+struct ContinuationWithResult<T>
+{
+    public object Next;
+    public delegate*<ref ContinuationWithResult<T>, Exception, object> Resume;
+    public int State;
+    public T Result;
+}
+```
+
+The continuations contain GC pointers, so we will need some way to create these types dynamically, with their corresponding GC info.
+They will be lifted to the heap by boxing.
+
+## Custom calling convention
+
+To call the new async methods, a custom calling convention is necessary for two reasons:
+1. The async method needs the continuation in case it is being resumed, or null in case it isn't
+2. The async method returns its boxed continuation to the caller in case it suspended.
+
+For 1 we can model it after generic contexts by adding another argument, or we can use a special register (this might be problematic on arm32).
+For 2 we expect to return the box in a separate register.
+
+`RuntimeHelpers.CallAsync2` used above is a JIT intrinsic that allows IL code to interface with the new calling convention.
+It has signature:
+```csharp
+[Intrinsic]
+internal static T CallAsync2<T>(T callResult, ref Continuation continuation, out object newContinuation)
+```
+
+IL that calls this intrinsic is only considered valid if the first argument was pushed as a direct result of a `call`, `calli` or `callvirt` instruction.
+
+To interface with the new calling convention on the return side we also provide `RuntimeHelpers.Async2Return`:
+```csharp
+[Intrinsic]
+internal static T Async2Return<T>(T result, object continuation);
+
+[Intrinsic]
+internal static void Async2Return(object continuation);
+```
+
+`RuntimeHelpers.Async2Return` must appear right before a `ret` IL instruction.
+
+Note that normal returns in an async2 method are equivalent to `return RuntimeHelpers.Async2Return(result, null)`.`
+
+## Synchronous calls
+For synchronous calls we pay the following costs:
+* On entry to the function, we must check if there is a continuation, meaning that we are resuming.
+  This cost can probably be avoided in common cases if we build some support for generating multiple entry points to a function in the JIT/VM.
+* To call an async method we must pass a zero continuation.
+* On return of an async method we must check whether it suspended or not.
+* For functions with suspension points inside a a `try` block, we may pay additional cost at the beginning of every `try` and `finally` block.
+  These costs may be avoidable with more JIT work.
+
+## Interoperating with async2 calls
+To integrate async2 with the existing ecosystem we need to be able to make calls between async2 and async1 methods.
+Async1 in this case refers to anything the C# compiler would consider awaitable; however, here we reduce the scope and consider only `Task`, `Task<T>`, `ValueTask` and `ValueTask<T>`.
+
+Calling async1 methods is done through the runtime functions described in [David's document](https://dev.azure.com/dnceng/internal/_git/dotnet-runtime?path=/docs/design/features/runtime-handled-tasks.md&version=GBdev/davidwr/async2-experiment&_a=preview):
+```csharp
+public interface ICriticalNotifyCompletion2 : ICriticalNotifyCompletion
+{
+    bool IsCompleted { get; }
+    void GetResult();
+}
+public interface ICriticalNotifyCompletion2<TResult> : ICriticalNotifyCompletion
+{
+    bool IsCompleted { get; }
+    TResult GetResult();
+}
+public interface INotifyCompletion2 : INotifyCompletion
+{
+    bool IsCompleted { get; }
+    void GetResult();
+}
+public interface INotifyCompletion2<TResult> : INotifyCompletion
+{
+    bool IsCompleted { get; }
+    TResult GetResult();
+}
+
+public static async2 void AwaitAwaiterFromRuntimeAsync<TAwaiter>(TAwaiter awaiter) where TAwaiter : INotifyCompletion2
+public static async2 TResult AwaitAwaiterFromRuntimeAsync<TResult, TAwaiter>(TAwaiter awaiter) where TAwaiter : INotifyCompletion2<TResult>
+public static async2 void UnsafeAwaitAwaiterFromRuntimeAsync<TAwaiter>(TAwaiter awaiter) where TAwaiter : ICriticalNotifyCompletion2
+public static async2 TResult UnsafeAwaitAwaiterFromRuntimeAsync<TResult, TAwaiter>(TAwaiter awaiter) where TAwaiter : ICriticalNotifyCompletion2<TResult>
+```
+
+Calling an async2 method follows the same principles as in David's document, i.e. a thunk is created to wrap it in a Task<T>.
+
+When a function suspends the JIT will link all continuations into a chain as the call stack unwinds.
+To make this thread safe with `INotifyCompletion` we must delay calling `OnCompleted` until the continuations have all been linked.
+Thus, the caller will be responsible for doing this; it is either done by the dispatcher, or done as part of the async1 -> async2 thunk.
+
+The following shows an example of what the async2 -> async1 adapting layer may look like.
+It conceptually just stores the awaiter and prepares for the caller to call `OnCompleted`.
+
+```csharp
+internal struct RuntimeAsyncAwaitState
+{
+    public Async2DispatcherBase Dispatcher;
+    public object SentinelContinuation;
+}
+
+[ThreadStatic]
+public static RuntimeAsyncAwaitState t_runtimeAsyncAwait;
+
+public static async2 TResult UnsafeAwaitAwaiterFromRuntimeAsync<TResult, TAwaiter>(TAwaiter awaiter) where TAwaiter : ICriticalNotifyCompletion2<TResult>
+{
+    if (awaiter.IsCompleted)
+        return awaiter.GetResult();
+
+    ref RuntimeAsyncAwaitState runtimeAsyncAwait = ref t_runtimeAsyncAwait;
+
+    if (runtimeAsyncAwait.SentinelContinuation == null)
+        runtimeAsyncAwait.SentinelContinuation = new Continuation();
+
+    runtimeAsyncAwait.Dispatcher = new Async2Dispatcher<TResult, TAwaiter>(awaiter);
+    return RuntimeHelpers.Async2Return(default, runtimeAsyncAwait.SentinelContinuation);
+}
+
+internal abstract class Async2DispatcherBase
+{
+    public abstract void Setup(object continuation);
+}
+
+internal sealed class Async2Dispatcher<TResult, TAwaiter> : Async2DispatcherBase where TAwaiter : ICriticalNotifyCompletion2<TResult>
+{
+    private readonly object _continuation;
+    private readonly TAwaiter _awaiter;
+
+    public Async2Dispatcher(TAwaiter awaiter)
+    {
+        _awaiter = awaiter;
+    }
+
+    public override void Setup(object continuation)
+    {
+        _continuation = continuation;
+        _awaiter.UnsafeOnCompleted(Run);
+    }
+
+    private void Run()
+    {
+        // Omitted
+    }
+}
+```
+
+The function returns a sentinel continuation which the JIT will write the first continuation into such that the caller knows where the chain starts.
+
+The remaining part is to describe what the async1 -> async2 adapter layer looks like.
+It consists of the following:
+* The thunk that calls an async2 method and produces a Task<T>
+* The `Async2Dispatcher.Run` method that is the callback of the asynchronous operation
+* The final implementation of the dispatcher
+
+Let us start with the thunk:
+```csharp
+public static Task<T> ThunkAsync<T>(ParameterType1 param1, ParameterType2 param2, ...)
+{
+    object boxedContinuation;
+    T result;
+    try
+    {
+        result = RuntimeHelpers.CallAsync2(TargetMethod(param1, param2, ...), ref Unsafe.NullRef<Continuation>(), out boxedContinuation);
+    }
+    catch (Exception ex)
+    {
+        return Task.FromException(ex);
+    }
+
+    if (boxedContinuation == null)
+        return Task.FromResult(result);
+
+    // Link in a final continuation that fires the TCS.
+    TaskCompletionSource<T> tcs = new();
+    ref Continuation continuation = ref Unsafe.Unbox<Continuation>(boxedContinuation);
+    continuation.Next = new TaskCompletionSourceContinuation<T>
+    {
+        Resume = &ResumeTaskCompletionSource<T>,
+        TCS = tcs,
+    };
+
+    // Now call OnCompleted on the awaiter we hit in the leaf.
+    ref RuntimeAsyncAwaitState runtimeAsyncAwait = ref t_runtimeAsyncAwait;
+    object headContinuation = Unsafe.Unbox<Continuation>(runtimeAsyncAwait.SentinelContinuation).Next;
+    runtimeAsyncAwait.Dispatcher.Setup(headContinuation);
+    return tcs.Task;
+}
+
+private static object ResumeTaskCompletionSource<T>(ref TaskCompletionSourceContinuation<T> continuation, Exception ex)
+{
+    if (ex == null)
+    {
+        continuation.TCS.SetResult(continuation.Result);
+    }
+    else
+    {
+        continuation.TCS.SetException(ex);
+    }
+
+    return null;
+}
+
+public struct TaskCompletionSourceContinuation<T>
+{
+    public object Next;
+    public delegate*<ref TaskCompletionSourceContinuation<T>, Exception, object> Resume;
+    public int State;
+    public T Result;
+    public TaskCompletionSource<T> TCS;
+}
+```
+The thunk has a fast path in case the async2 method finished synchronously.
+If not then the function creates a TCS and links in a synthetic continuation that will invoke this TCS.
+It retrieves the head continuation (which is the successor of the sentinel continuation that was saved in the leaf), and finally it calls `Async2DispatcherBase.Setup` which is responsible for actually calling `OnCompleted` on the saved awaiter.
+
+The implementation of `Async2Dispatcher<TResult, TAwaiter>.Run` was omitted above.
+This is the first function called by the asynchronous callback, and it needs to propagate the result into the continuation and start running the dispatcher.
+```csharp
+internal sealed class Async2Dispatcher<TResult, TAwaiter> : Async2DispatcherBase where TAwaiter : ICriticalNotifyCompletion2<TResult>
+{
+    private readonly object _continuation;
+    private readonly TAwaiter _awaiter;
+
+    private void Run()
+    {
+        Exception exToPropagate = null;
+        try
+        {
+            TResult result = _awaiter.GetResult();
+            Unsafe.Unbox<ContinuationWithResult<TResult>>(_continuation).Result = result;
+        }
+        catch (Exception ex)
+        {
+            exToPropagate = ex;
+        }
+
+        RuntimeHelpers.DispatchContinuations(_continuation, exToPropagate);
+    }
+}
+```
+
+Finally, the dispatcher must be expanded to also handle registering the continuation chain for the asynchronous callback, in case a continuation suspends one more time:
+```csharp
+public static void DispatchContinuations(object boxedContinuation, Exception exToPropagate)
+{
+    do
+    {
+        ref Continuation continuation = ref Unsafe.Unbox<Continuation>(boxedContinuation);
+        try
+        {
+            object newContinuation = continuation.Resume(ref continuation, exToPropagate);
+
+            if (newContinuation != null)
+            {
+                Unsafe.Unbox<Continuation>(newContinuation).Next = continuation.Next;
+
+                ref RuntimeAsyncAwaitState runtimeAsyncAwait = ref t_runtimeAsyncAwait;
+                object headContinuation = Unsafe.Unbox<Continuation>(runtimeAsyncAwait.SentinelContinuation).Next;
+                runtimeAsyncAwait.Dispatcher.Setup(headContinuation);
+                return;
+            }
+        }
+        catch (Exception ex)
+        {
+            exToPropagate = ex;
+        }
+
+        boxedContinuation = continuation.Next;
+    } while (boxedContinuation != null);
+}
+```
+It also needs to transfer the next continuation stored in the previous (now invoked continuation) to the newly returned one.
+
+## Thoughts/questions
+
+1. Is it better to allocate a "mega"-continuation and reuse it? It will mean less type loader traffic, but more stuff kept on the heap overall.
+It will also allow us to skip transferring the "Next" from the previous continuation when a resumed continuation suspends again
+2. We know address-exposed locals become unexposed at suspension points.
+How do we make use of this? Likely we need a custom liveness pass.
+
+## Required JIT work
+
+1. A pass that walks the IR and finds all awaited async calls.
+   At these points we must identify live state (so run some kind of liveness) and generate code to create continuations in case the callee suspends.
+   We must also create the IR that restores the live state from the continuation, and throw the exception.
+3. Prolog work to handle "resume" vs "start".
+4. Work to handle suspension and resumption within `try`
+
+Some things to consider:
+* Should the pass happen early or late? Early means less live state, but likely impact on CQ.
+  In particular the resumption code is going to create a lot of synthetic control flow that I expect would have significant CQ impact.
+  Doing it late will probably make it harder to introduce the boxing and generate the registration IR.
+  Also, doing it later means more chance for optimizations to create illegal constructs (e.g. local addresses that span suspension points).
+* Doing it late may allow us to simply jump directly into nested `try` blocks and exit `try` blocks without invoking finallies, though I am not sure about that.
+* We need to be able to introduce control flow around awaited async calls.
+  If done early we can use `gtSplitTree`.
+  If done late we need to track LIR edges and introduce temps.
+* How do we represent the new calls internally? They effectively define two things (return value and the "suspended").
+  ~~We can probably cheat and consider these as always defining an additional pseudo-local.~~ No we cannot, because encoding that in LSRA and codegen is a nightmare when there is no tree to attach the RPs to.
+* How about if the JIT creates local addresses that span across awaits? Do we need to represent the calls as opaque defs of all live state? That seems conservative and might not even fully avoid the problem.
+
+## Optional JIT work
+
+1. Support for generating multiple different entry points.
+2. Deal with the fact that liveness of address exposed locals is going to be very conservative.
\ No newline at end of file
diff --git a/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs
index a931499ac7e2dc..5ee4958fa2a91d 100644
--- a/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs
+++ b/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs
@@ -380,6 +380,13 @@ private static unsafe void DispatchTailCalls(
                 }
             }
         }
+
+        private static object AllocContinuation(object prevContinuation, nuint numGCRefs, nuint dataSize)
+        {
+            object newContinuation = new Continuation { Data = new byte[dataSize], GCData = new object[numGCRefs] };
+            Unsafe.Unbox<Continuation>(prevContinuation).Next = newContinuation;
+            return newContinuation;
+        }
     }
     // Helper class to assist with unsafe pinning of arbitrary objects.
     // It's used by VM code.
@@ -660,4 +667,13 @@ internal unsafe struct TailCallTls
         public PortableTailCallFrame* Frame;
         public IntPtr ArgBuffer;
     }
+
+    internal unsafe struct Continuation
+    {
+        public object Next;
+        public delegate*<ref Continuation, Exception, object> Resume;
+        public uint State;
+        public byte[] Data;
+        public object GCData;
+    }
 }
diff --git a/src/coreclr/System.Private.CoreLib/src/System/StubHelpers.cs b/src/coreclr/System.Private.CoreLib/src/System/StubHelpers.cs
index 431c2f724a02ba..18f9073c982326 100644
--- a/src/coreclr/System.Private.CoreLib/src/System/StubHelpers.cs
+++ b/src/coreclr/System.Private.CoreLib/src/System/StubHelpers.cs
@@ -1296,5 +1296,8 @@ internal static void CheckStringLength(uint length)
         [Intrinsic]
         [MethodImpl(MethodImplOptions.InternalCall)]
         internal static extern IntPtr NextCallReturnAddress();
+
+        [Intrinsic]
+        internal static object? Async2CallContinuation() => null;
     }  // class StubHelpers
 }
diff --git a/src/coreclr/inc/clrconfigvalues.h b/src/coreclr/inc/clrconfigvalues.h
index c556d45c495894..1ee66824c6a233 100644
--- a/src/coreclr/inc/clrconfigvalues.h
+++ b/src/coreclr/inc/clrconfigvalues.h
@@ -800,6 +800,11 @@ RETAIL_CONFIG_DWORD_INFO(EXTERNAL_EnableArm64Rcpc,              W("EnableArm64Rc
 RETAIL_CONFIG_DWORD_INFO(EXTERNAL_EnableArm64Rcpc2,             W("EnableArm64Rcpc2"),          1, "Allows Arm64 Rcpc2+ hardware intrinsics to be disabled")
 #endif
 
+///
+/// Runtime async
+///
+RETAIL_CONFIG_DWORD_INFO(EXTERNAL_RuntimeAsyncViaJitGeneratedStateMachines, W("RuntimeAsyncViaJitGeneratedStateMachines"), 0, "Use JIT generated state machines instead of unwinding-based runtime async")
+
 ///
 /// Uncategorized
 ///
diff --git a/src/coreclr/inc/corinfo.h b/src/coreclr/inc/corinfo.h
index 97e0d385753ce3..610fd5112a4f1c 100644
--- a/src/coreclr/inc/corinfo.h
+++ b/src/coreclr/inc/corinfo.h
@@ -662,6 +662,8 @@ enum CorInfoHelpFunc
     CORINFO_HELP_VALIDATE_INDIRECT_CALL,    // CFG: Validate function pointer
     CORINFO_HELP_DISPATCH_INDIRECT_CALL,    // CFG: Validate and dispatch to pointer
 
+    CORINFO_HELP_ALLOC_CONTINUATION,
+
     CORINFO_HELP_COUNT,
 };
 
@@ -957,6 +959,7 @@ enum CorInfoGCType
 enum CorInfoClassId
 {
     CLASSID_SYSTEM_OBJECT,
+    CLASSID_SYSTEM_BYTE,
     CLASSID_TYPED_BYREF,
     CLASSID_TYPE_HANDLE,
     CLASSID_FIELD_HANDLE,
@@ -1820,6 +1823,22 @@ struct CORINFO_EE_INFO
     CORINFO_OS  osType;
 };
 
+struct CORINFO_ASYNC2_INFO
+{
+    // Class handle for System.Runtime.CompilerServices.Continuation
+    CORINFO_CLASS_HANDLE continuationClsHnd;
+    // 'Next' field
+    CORINFO_FIELD_HANDLE continuationNextFldHnd;
+    // 'Resume' field
+    CORINFO_FIELD_HANDLE continuationResumeFldHnd;
+    // 'State' field
+    CORINFO_FIELD_HANDLE continuationStateFldHnd;
+    // 'Data' field
+    CORINFO_FIELD_HANDLE continuationDataFldHnd;
+    // 'GCData' field
+    CORINFO_FIELD_HANDLE continuationGCDataFldHnd;
+};
+
 // Flags passed from JIT to runtime.
 enum CORINFO_GET_TAILCALL_HELPERS_FLAGS
 {
@@ -2997,6 +3016,10 @@ class ICorStaticInfo
     // Returns name of the JIT timer log
     virtual const char16_t *getJitTimeLogFilename() = 0;
 
+    virtual void getAsync2Info(
+        CORINFO_ASYNC2_INFO* pAsync2InfoOut
+    ) = 0;
+
     /*********************************************************************************/
     //
     // Diagnostic methods
@@ -3329,6 +3352,8 @@ class ICorDynamicInfo : public ICorStaticInfo
             CORINFO_TAILCALL_HELPERS* pResult
             ) = 0;
 
+    virtual CORINFO_METHOD_HANDLE getAsyncResumptionStub() = 0;
+
     // Optionally, convert calli to regular method call. This is for PInvoke argument marshalling.
     virtual bool convertPInvokeCalliToCall(
             CORINFO_RESOLVED_TOKEN *    pResolvedToken,
diff --git a/src/coreclr/inc/icorjitinfoimpl_generated.h b/src/coreclr/inc/icorjitinfoimpl_generated.h
index 4fd79ef9303c71..a00826e252434e 100644
--- a/src/coreclr/inc/icorjitinfoimpl_generated.h
+++ b/src/coreclr/inc/icorjitinfoimpl_generated.h
@@ -470,6 +470,9 @@ bool runWithSPMIErrorTrap(
 void getEEInfo(
           CORINFO_EE_INFO* pEEInfoOut) override;
 
+void getAsync2Info(
+          CORINFO_ASYNC2_INFO* pAsync2InfoOut) override;
+
 const char16_t* getJitTimeLogFilename() override;
 
 mdMethodDef getMethodDefFromMethod(
@@ -634,6 +637,8 @@ bool getTailCallHelpers(
           CORINFO_GET_TAILCALL_HELPERS_FLAGS flags,
           CORINFO_TAILCALL_HELPERS* pResult) override;
 
+CORINFO_METHOD_HANDLE getAsyncResumptionStub() override;
+
 bool convertPInvokeCalliToCall(
           CORINFO_RESOLVED_TOKEN* pResolvedToken,
           bool mustConvert) override;
diff --git a/src/coreclr/inc/jiteeversionguid.h b/src/coreclr/inc/jiteeversionguid.h
index 70960ff577ee8a..b394ac0f49109a 100644
--- a/src/coreclr/inc/jiteeversionguid.h
+++ b/src/coreclr/inc/jiteeversionguid.h
@@ -43,11 +43,11 @@ typedef const GUID *LPCGUID;
 #define GUID_DEFINED
 #endif // !GUID_DEFINED
 
-constexpr GUID JITEEVersionIdentifier = { /* a2974440-e8ee-4d95-9e6e-799a330be1a0 */
-    0xa2974440,
-    0xe8ee,
-    0x4d95,
-    {0x9e, 0x6e, 0x79, 0x9a, 0x33, 0x0b, 0xe1, 0xa0}
+constexpr GUID JITEEVersionIdentifier = { /* 6834d668-2cd6-491e-9e13-28675a28da97 */
+    0x6834d668,
+    0x2cd6,
+    0x491e,
+    {0x9e, 0x13, 0x28, 0x67, 0x5a, 0x28, 0xda, 0x97}
   };
   
 //////////////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/src/coreclr/inc/jithelpers.h b/src/coreclr/inc/jithelpers.h
index 06d09a1b5e15ab..698938dafbc10a 100644
--- a/src/coreclr/inc/jithelpers.h
+++ b/src/coreclr/inc/jithelpers.h
@@ -359,6 +359,8 @@
     JITHELPER(CORINFO_HELP_DISPATCH_INDIRECT_CALL, NULL, CORINFO_HELP_SIG_REG_ONLY)
 #endif
 
+    DYNAMICJITHELPER(CORINFO_HELP_ALLOC_CONTINUATION, NULL, CORINFO_HELP_SIG_REG_ONLY)
+
 #undef JITHELPER
 #undef DYNAMICJITHELPER
 #undef JITHELPER
diff --git a/src/coreclr/jit/CMakeLists.txt b/src/coreclr/jit/CMakeLists.txt
index c2d873aca7bd1b..9c6a864a26e805 100644
--- a/src/coreclr/jit/CMakeLists.txt
+++ b/src/coreclr/jit/CMakeLists.txt
@@ -90,6 +90,7 @@ endif(CLR_CMAKE_TARGET_WIN32)
 
 set( JIT_SOURCES
   alloc.cpp
+  async.cpp
   assertionprop.cpp
   bitset.cpp
   block.cpp
@@ -281,6 +282,7 @@ set( JIT_HEADERS
   ../inc/corjithost.h
   _typeinfo.h
   alloc.h
+  async.h
   arraystack.h
   bitset.h
   layout.h
diff --git a/src/coreclr/jit/ICorJitInfo_names_generated.h b/src/coreclr/jit/ICorJitInfo_names_generated.h
index fcc982f7b0be6a..3d9aaefe1c1f9a 100644
--- a/src/coreclr/jit/ICorJitInfo_names_generated.h
+++ b/src/coreclr/jit/ICorJitInfo_names_generated.h
@@ -116,6 +116,7 @@ DEF_CLR_API(getHFAType)
 DEF_CLR_API(runWithErrorTrap)
 DEF_CLR_API(runWithSPMIErrorTrap)
 DEF_CLR_API(getEEInfo)
+DEF_CLR_API(getAsync2Info)
 DEF_CLR_API(getJitTimeLogFilename)
 DEF_CLR_API(getMethodDefFromMethod)
 DEF_CLR_API(printMethodName)
@@ -155,6 +156,7 @@ DEF_CLR_API(getFieldThreadLocalStoreID)
 DEF_CLR_API(GetDelegateCtor)
 DEF_CLR_API(MethodCompileComplete)
 DEF_CLR_API(getTailCallHelpers)
+DEF_CLR_API(getAsyncResumptionStub)
 DEF_CLR_API(convertPInvokeCalliToCall)
 DEF_CLR_API(notifyInstructionSetUsage)
 DEF_CLR_API(updateEntryPointForTailCall)
diff --git a/src/coreclr/jit/ICorJitInfo_wrapper_generated.hpp b/src/coreclr/jit/ICorJitInfo_wrapper_generated.hpp
index 88cca4f6f8cff3..f846ef774416e4 100644
--- a/src/coreclr/jit/ICorJitInfo_wrapper_generated.hpp
+++ b/src/coreclr/jit/ICorJitInfo_wrapper_generated.hpp
@@ -1107,6 +1107,14 @@ void WrapICorJitInfo::getEEInfo(
     API_LEAVE(getEEInfo);
 }
 
+void WrapICorJitInfo::getAsync2Info(
+          CORINFO_ASYNC2_INFO* pAsync2InfoOut)
+{
+    API_ENTER(getAsync2Info);
+    wrapHnd->getAsync2Info(pAsync2InfoOut);
+    API_LEAVE(getAsync2Info);
+}
+
 const char16_t* WrapICorJitInfo::getJitTimeLogFilename()
 {
     API_ENTER(getJitTimeLogFilename);
@@ -1497,6 +1505,14 @@ bool WrapICorJitInfo::getTailCallHelpers(
     return temp;
 }
 
+CORINFO_METHOD_HANDLE WrapICorJitInfo::getAsyncResumptionStub()
+{
+    API_ENTER(getAsyncResumptionStub);
+    CORINFO_METHOD_HANDLE temp = wrapHnd->getAsyncResumptionStub();
+    API_LEAVE(getAsyncResumptionStub);
+    return temp;
+}
+
 bool WrapICorJitInfo::convertPInvokeCalliToCall(
           CORINFO_RESOLVED_TOKEN* pResolvedToken,
           bool mustConvert)
diff --git a/src/coreclr/jit/async.cpp b/src/coreclr/jit/async.cpp
new file mode 100644
index 00000000000000..3d94d6fb5ee36f
--- /dev/null
+++ b/src/coreclr/jit/async.cpp
@@ -0,0 +1,999 @@
+#include "jitpch.h"
+#include "jitstd/algorithm.h"
+#include "async.h"
+
+PhaseStatus Compiler::TransformAsync2()
+{
+    assert(compIsAsync2());
+
+    Async2Transformation transformation(this);
+    return transformation.Run();
+}
+
+PhaseStatus Async2Transformation::Run()
+{
+    ArrayStack<BasicBlock*> worklist(m_comp->getAllocator(CMK_Async2));
+
+    for (BasicBlock* block : m_comp->Blocks())
+    {
+        for (GenTree* tree : LIR::AsRange(block))
+        {
+            if (tree->IsCall() && tree->AsCall()->IsAsync2())
+            {
+                JITDUMP(FMT_BB " contains await(s)\n", block->bbNum);
+                worklist.Push(block);
+                break;
+            }
+        }
+    }
+
+    JITDUMP("Found %d blocks with awaits\n", worklist.Height());
+
+    if (worklist.Height() <= 0)
+    {
+        return PhaseStatus::MODIFIED_NOTHING;
+    }
+
+    m_resumeStub = m_comp->info.compCompHnd->getAsyncResumptionStub();
+    m_comp->info.compCompHnd->getFunctionFixedEntryPoint(m_resumeStub, false, &m_resumeStubLookup);
+
+    m_returnedContinuationVar = m_comp->lvaGrabTemp(false DEBUGARG("returned continuation"));
+    m_comp->lvaGetDesc(m_returnedContinuationVar)->lvType = TYP_REF;
+    m_newContinuationVar                                  = m_comp->lvaGrabTemp(false DEBUGARG("new continuation"));
+    m_comp->lvaGetDesc(m_newContinuationVar)->lvType      = TYP_REF;
+
+    m_comp->info.compCompHnd->getAsync2Info(&m_async2Info);
+    m_objectClsHnd = m_comp->info.compCompHnd->getBuiltinClass(CLASSID_SYSTEM_OBJECT);
+    m_byteClsHnd   = m_comp->info.compCompHnd->getBuiltinClass(CLASSID_SYSTEM_BYTE);
+
+    m_comp->lvaComputeRefCounts(true, false);
+
+    m_comp->fgLocalVarLiveness();
+    m_livenessLvaCount = m_comp->lvaCount;
+
+    VarSetOps::AssignNoCopy(m_comp, m_comp->compCurLife, VarSetOps::MakeEmpty(m_comp));
+
+    TreeLifeUpdater<false>   life(m_comp);
+    jitstd::vector<GenTree*> defs(m_comp->getAllocator(CMK_Async2));
+
+    for (int i = 0; i < worklist.Height(); i++)
+    {
+        assert(defs.size() == 0);
+
+        BasicBlock* block = worklist.Bottom(i);
+        VarSetOps::Assign(m_comp, m_comp->compCurLife, block->bbLiveIn);
+
+        bool any;
+        do
+        {
+            any = false;
+            for (GenTree* tree : LIR::AsRange(block))
+            {
+                tree->VisitOperands([&defs](GenTree* op) {
+                    if (op->IsValue())
+                    {
+                        for (size_t i = defs.size(); i > 0; i--)
+                        {
+                            if (op == defs[i - 1])
+                            {
+                                defs[i - 1] = defs[defs.size() - 1];
+                                defs.erase(defs.begin() + (defs.size() - 1), defs.end());
+                                break;
+                            }
+                        }
+                    }
+
+                    return GenTree::VisitResult::Continue;
+                });
+
+                life.UpdateLife(tree);
+
+                if (tree->IsCall() && tree->AsCall()->IsAsync2())
+                {
+                    Transform(block, tree->AsCall(), defs, &block);
+                    defs.clear();
+                    any = true;
+                    break;
+                }
+
+                if (tree->IsValue() && !tree->IsUnusedValue())
+                {
+                    defs.push_back(tree);
+                }
+            }
+        } while (any);
+    }
+
+    CreateResumptionSwitch();
+
+    return PhaseStatus::MODIFIED_EVERYTHING;
+}
+
+void Async2Transformation::Transform(BasicBlock*               block,
+                                     GenTreeCall*              call,
+                                     jitstd::vector<GenTree*>& defs,
+                                     BasicBlock**              pRemainder)
+{
+#ifdef DEBUG
+    if (m_comp->verbose)
+    {
+        printf("Processing call [%06u]\n", Compiler::dspTreeID(call));
+        printf("  %zu live LIR edges\n", defs.size());
+
+        if (defs.size() > 0)
+        {
+            const char* sep = "    ";
+            for (GenTree* tree : defs)
+            {
+                printf("%s[%06u] (%s)", sep, Compiler::dspTreeID(tree), varTypeName(tree->TypeGet()));
+                sep = ", ";
+            }
+
+            printf("\n");
+        }
+    }
+#endif
+
+    m_liveLocals.clear();
+
+    for (unsigned lclNum = 0; lclNum < m_livenessLvaCount; lclNum++)
+    {
+        if (!IsLive(lclNum))
+        {
+            continue;
+        }
+
+        m_liveLocals.push_back(LiveLocalInfo(lclNum));
+    }
+
+    LiftLIREdges(block, call, defs, m_liveLocals);
+
+#ifdef DEBUG
+    if (m_comp->verbose)
+    {
+        printf("  %zu live locals\n", m_liveLocals.size());
+
+        if (m_liveLocals.size() > 0)
+        {
+            const char* sep = "    ";
+            for (LiveLocalInfo& inf : m_liveLocals)
+            {
+                printf("%sV%02u (%s)", sep, inf.LclNum, varTypeName(m_comp->lvaGetDesc(inf.LclNum)->TypeGet()));
+                sep = ", ";
+            }
+
+            printf("\n");
+        }
+    }
+#endif
+
+    for (LiveLocalInfo& inf : m_liveLocals)
+    {
+        LclVarDsc* dsc = m_comp->lvaGetDesc(inf.LclNum);
+
+        if ((dsc->TypeGet() == TYP_STRUCT) || dsc->IsImplicitByRef())
+        {
+            ClassLayout* layout = dsc->GetLayout();
+            assert(!layout->HasGCByRef());
+
+            if (layout->IsBlockLayout())
+            {
+                inf.Alignment   = 1;
+                inf.DataSize    = layout->GetSize();
+                inf.GCDataCount = 0;
+            }
+            else
+            {
+                inf.Alignment = m_comp->info.compCompHnd->getClassAlignmentRequirement(layout->GetClassHandle());
+                if ((layout->GetGCPtrCount() * TARGET_POINTER_SIZE) == layout->GetSize())
+                {
+                    inf.DataSize = 0;
+                }
+                else
+                {
+                    inf.DataSize = layout->GetSize();
+                }
+
+                inf.GCDataCount = layout->GetGCPtrCount();
+            }
+        }
+        else if (dsc->TypeGet() == TYP_REF)
+        {
+            inf.Alignment   = TARGET_POINTER_SIZE;
+            inf.DataSize    = 0;
+            inf.GCDataCount = 1;
+        }
+        else
+        {
+            assert(dsc->TypeGet() != TYP_BYREF);
+
+            inf.Alignment   = genTypeAlignments[dsc->TypeGet()];
+            inf.DataSize    = genTypeSize(dsc);
+            inf.GCDataCount = 0;
+        }
+    }
+
+    jitstd::sort(m_liveLocals.begin(), m_liveLocals.end(), [](const LiveLocalInfo& lhs, const LiveLocalInfo& rhs) {
+        if (lhs.Alignment == rhs.Alignment)
+        {
+            // Prefer lowest local num first for same alignment.
+            return lhs.LclNum < rhs.LclNum;
+        }
+
+        // Otherwise prefer highest alignment first.
+        return lhs.Alignment > rhs.Alignment;
+    });
+
+    unsigned dataSize = 0;
+    // Result value is currently always stored boxed in the continuation as it
+    // needs to be written by the resumption stub and by the asynchronous
+    // callbacks. If we specialized the continuation type we could avoid this.
+    unsigned gcRefsCount = call->gtReturnType != TYP_VOID ? 1 : 0;
+
+    for (LiveLocalInfo& inf : m_liveLocals)
+    {
+        dataSize = roundUp(dataSize, inf.Alignment);
+
+        inf.DataOffset  = dataSize;
+        inf.GCDataIndex = gcRefsCount;
+
+        dataSize += inf.DataSize;
+        gcRefsCount += inf.GCDataCount;
+    }
+
+#ifdef DEBUG
+    if (m_comp->verbose)
+    {
+        printf("  Continuation layout (%u bytes, %u GC pointers):\n", dataSize, gcRefsCount);
+        for (LiveLocalInfo& inf : m_liveLocals)
+        {
+            printf("    +%03u (GC@+%02u) V%02u: %u bytes, %u GC pointers\n", inf.DataOffset, inf.GCDataIndex,
+                   inf.LclNum, inf.DataSize, inf.GCDataCount);
+        }
+    }
+#endif
+
+    unsigned stateNum = (unsigned)m_resumptionBBs.size();
+    JITDUMP("  Assigned state %u\n", stateNum);
+
+    GenTreeLclVarCommon* storeResultNode = nullptr;
+    GenTree*             insertAfter     = call;
+
+    if (!call->TypeIs(TYP_VOID) && !call->IsUnusedValue())
+    {
+        assert(call->gtNext != nullptr);
+        if (!call->gtNext->OperIsLocalStore() || (call->gtNext->Data() != call))
+        {
+            LIR::Use use;
+            bool     gotUse = LIR::AsRange(block).TryGetUse(call, &use);
+            assert(gotUse);
+
+            use.ReplaceWithLclVar(m_comp);
+        }
+
+        assert(call->gtNext->OperIsLocalStore() && (call->gtNext->Data() == call));
+        storeResultNode = call->gtNext->AsLclVarCommon();
+        insertAfter     = call->gtNext;
+    }
+
+    GenTree* continuationPhysReg = m_comp->gtNewPhysRegNode(REG_ASYNC_CONTINUATION_RET, TYP_REF);
+    GenTree* storeContinuation   = m_comp->gtNewStoreLclVarNode(m_returnedContinuationVar, continuationPhysReg);
+    LIR::AsRange(block).InsertAfter(insertAfter, continuationPhysReg, storeContinuation);
+
+    GenTree* null                 = m_comp->gtNewNull();
+    GenTree* returnedContinuation = m_comp->gtNewLclvNode(m_returnedContinuationVar, TYP_REF);
+    GenTree* neNull               = m_comp->gtNewOperNode(GT_NE, TYP_INT, returnedContinuation, null);
+    GenTree* jtrue                = m_comp->gtNewOperNode(GT_JTRUE, TYP_VOID, neNull);
+
+    LIR::AsRange(block).InsertAfter(storeContinuation, null, returnedContinuation, neNull, jtrue);
+    BasicBlock* remainder = m_comp->fgSplitBlockAfterNode(block, jtrue);
+    *pRemainder           = remainder;
+
+    assert(block->KindIs(BBJ_NONE) && block->NextIs(remainder));
+
+    if (m_lastSuspensionBB == nullptr)
+    {
+        m_lastSuspensionBB = m_comp->fgLastBBInMainFunction();
+    }
+
+    BasicBlock* retBB = m_comp->fgNewBBafter(BBJ_RETURN, m_lastSuspensionBB, true);
+    retBB->bbSetRunRarely();
+    m_lastSuspensionBB = retBB;
+
+    block->SetJumpKindAndTarget(BBJ_COND, retBB DEBUGARG(m_comp));
+
+    m_comp->fgAddRefPred(retBB, block);
+
+    // Allocate continuation
+    returnedContinuation           = m_comp->gtNewLclvNode(m_returnedContinuationVar, TYP_REF);
+    GenTree*     gcRefsCountNode   = m_comp->gtNewIconNode((ssize_t)gcRefsCount, TYP_I_IMPL);
+    GenTree*     dataSizeNode      = m_comp->gtNewIconNode((ssize_t)dataSize, TYP_I_IMPL);
+    GenTreeCall* allocContinuation = m_comp->gtNewHelperCallNode(CORINFO_HELP_ALLOC_CONTINUATION, TYP_REF,
+                                                                 returnedContinuation, gcRefsCountNode, dataSizeNode);
+
+    m_comp->compCurBB = retBB;
+    m_comp->fgMorphTree(allocContinuation);
+
+    LIR::AsRange(retBB).InsertAtEnd(LIR::SeqTree(m_comp, allocContinuation));
+
+    GenTree* storeNewContinuation = m_comp->gtNewStoreLclVarNode(m_newContinuationVar, allocContinuation);
+    LIR::AsRange(retBB).InsertAtEnd(storeNewContinuation);
+
+    // Fill in 'Resume'
+    GenTree* newContinuation = m_comp->gtNewLclvNode(m_newContinuationVar, TYP_REF);
+    unsigned resumeOffset =
+        TARGET_POINTER_SIZE + m_comp->info.compCompHnd->getFieldOffset(m_async2Info.continuationResumeFldHnd);
+    GenTree* resumeStubAddr = CreateResumptionStubAddrTree();
+    GenTree* storeResume    = StoreAtOffset(newContinuation, resumeOffset, resumeStubAddr);
+    LIR::AsRange(retBB).InsertAtEnd(LIR::SeqTree(m_comp, storeResume));
+
+    // Fill in 'state'
+    newContinuation = m_comp->gtNewLclvNode(m_newContinuationVar, TYP_REF);
+    unsigned stateOffset =
+        TARGET_POINTER_SIZE + m_comp->info.compCompHnd->getFieldOffset(m_async2Info.continuationStateFldHnd);
+    GenTree* stateNumNode = m_comp->gtNewIconNode((ssize_t)stateNum, TYP_I_IMPL);
+    GenTree* store        = StoreAtOffset(newContinuation, stateOffset, stateNumNode);
+    LIR::AsRange(retBB).InsertAtEnd(LIR::SeqTree(m_comp, store));
+
+    // Fill in GC pointers
+    if (gcRefsCount > 0)
+    {
+        unsigned objectArrLclNum = GetGCDataArrayVar();
+
+        newContinuation = m_comp->gtNewLclvNode(m_newContinuationVar, TYP_REF);
+        unsigned gcDataOffset =
+            TARGET_POINTER_SIZE + m_comp->info.compCompHnd->getFieldOffset(m_async2Info.continuationGCDataFldHnd);
+        GenTree* gcDataInd             = LoadFromOffset(newContinuation, gcDataOffset, TYP_REF);
+        GenTree* storeAllocedObjectArr = m_comp->gtNewStoreLclVarNode(objectArrLclNum, gcDataInd);
+        LIR::AsRange(retBB).InsertAtEnd(LIR::SeqTree(m_comp, storeAllocedObjectArr));
+
+        for (LiveLocalInfo& inf : m_liveLocals)
+        {
+            if (inf.GCDataCount <= 0)
+            {
+                continue;
+            }
+
+            LclVarDsc* dsc = m_comp->lvaGetDesc(inf.LclNum);
+            if (dsc->TypeGet() == TYP_REF)
+            {
+                GenTree* value     = m_comp->gtNewLclvNode(inf.LclNum, TYP_REF);
+                GenTree* objectArr = m_comp->gtNewLclvNode(objectArrLclNum, TYP_REF);
+                GenTree* store =
+                    StoreAtOffset(objectArr, OFFSETOF__CORINFO_Array__data + (inf.GCDataIndex * TARGET_POINTER_SIZE),
+                                  value);
+                LIR::AsRange(retBB).InsertAtEnd(LIR::SeqTree(m_comp, store));
+            }
+            else
+            {
+                assert((dsc->TypeGet() == TYP_STRUCT) || dsc->IsImplicitByRef());
+                ClassLayout* layout   = dsc->GetLayout();
+                unsigned     numSlots = layout->GetSlotCount();
+                for (unsigned i = 0; i < numSlots; i++)
+                {
+                    var_types gcPtrType = layout->GetGCPtrType(i);
+                    assert((gcPtrType == TYP_I_IMPL) || (gcPtrType == TYP_REF));
+                    if (gcPtrType != TYP_REF)
+                    {
+                        continue;
+                    }
+
+                    GenTree* value;
+                    if (dsc->IsImplicitByRef())
+                    {
+                        GenTree* baseAddr = m_comp->gtNewLclvNode(inf.LclNum, dsc->TypeGet());
+                        value             = LoadFromOffset(baseAddr, i * TARGET_POINTER_SIZE, TYP_REF);
+                    }
+                    else
+                    {
+                        value = m_comp->gtNewLclFldNode(inf.LclNum, TYP_REF, i * TARGET_POINTER_SIZE);
+                    }
+
+                    GenTree* objectArr = m_comp->gtNewLclvNode(objectArrLclNum, TYP_REF);
+                    unsigned offset    = OFFSETOF__CORINFO_Array__data + ((inf.GCDataIndex + i) * TARGET_POINTER_SIZE);
+                    GenTree* store     = StoreAtOffset(objectArr, offset, value);
+                    LIR::AsRange(retBB).InsertAtEnd(LIR::SeqTree(m_comp, store));
+
+                    if (inf.DataSize > 0)
+                    {
+                        // Null out the GC field in preparation of storing the rest.
+                        GenTree* null = m_comp->gtNewNull();
+
+                        if (dsc->IsImplicitByRef())
+                        {
+                            GenTree* baseAddr = m_comp->gtNewLclvNode(inf.LclNum, dsc->TypeGet());
+                            store             = StoreAtOffset(baseAddr, i * TARGET_POINTER_SIZE, null);
+                        }
+                        else
+                        {
+                            store = m_comp->gtNewStoreLclFldNode(inf.LclNum, TYP_REF, i * TARGET_POINTER_SIZE, null);
+                        }
+
+                        LIR::AsRange(retBB).InsertAtEnd(LIR::SeqTree(m_comp, store));
+                    }
+                }
+
+                m_comp->lvaSetVarDoNotEnregister(inf.LclNum DEBUGARG(DoNotEnregisterReason::LocalField));
+            }
+        }
+    }
+
+    // Store data in byte[]
+    if (dataSize > 0)
+    {
+        unsigned byteArrLclNum = GetDataArrayVar();
+
+        GenTree* newContinuation = m_comp->gtNewLclvNode(m_newContinuationVar, TYP_REF);
+        unsigned dataOffset =
+            TARGET_POINTER_SIZE + m_comp->info.compCompHnd->getFieldOffset(m_async2Info.continuationDataFldHnd);
+        GenTree* dataOffsetNode      = m_comp->gtNewIconNode((ssize_t)dataOffset, TYP_I_IMPL);
+        GenTree* dataAddr            = m_comp->gtNewOperNode(GT_ADD, TYP_BYREF, newContinuation, dataOffsetNode);
+        GenTree* dataInd             = m_comp->gtNewIndir(TYP_REF, dataAddr, GTF_IND_NONFAULTING);
+        GenTree* storeAllocedByteArr = m_comp->gtNewStoreLclVarNode(byteArrLclNum, dataInd);
+        LIR::AsRange(retBB).InsertAtEnd(newContinuation, dataOffsetNode, dataAddr, dataInd, storeAllocedByteArr);
+
+        // Fill in data
+        for (LiveLocalInfo& inf : m_liveLocals)
+        {
+            if (inf.DataSize <= 0)
+            {
+                continue;
+            }
+
+            LclVarDsc* dsc = m_comp->lvaGetDesc(inf.LclNum);
+
+            GenTree* byteArr = m_comp->gtNewLclvNode(byteArrLclNum, TYP_REF);
+            unsigned offset  = OFFSETOF__CORINFO_Array__data + inf.DataOffset;
+
+            GenTree* value;
+            if (dsc->IsImplicitByRef())
+            {
+                GenTree* baseAddr = m_comp->gtNewLclvNode(inf.LclNum, dsc->TypeGet());
+                value             = m_comp->gtNewBlkIndir(dsc->GetLayout(), baseAddr, GTF_IND_NONFAULTING);
+            }
+            else
+            {
+                value = m_comp->gtNewLclvNode(inf.LclNum, genActualType(dsc->TypeGet()));
+            }
+
+            GenTree* store;
+            if ((dsc->TypeGet() == TYP_STRUCT) || dsc->IsImplicitByRef())
+            {
+                GenTree* cns  = m_comp->gtNewIconNode((ssize_t)offset, TYP_I_IMPL);
+                GenTree* addr = m_comp->gtNewOperNode(GT_ADD, TYP_BYREF, byteArr, cns);
+                // This is to heap, but all GC refs are nulled out already, so we can skip the write barrier.
+                // TODO-CQ: Backend does not care about GTF_IND_TGT_NOT_HEAP for STORE_BLK.
+                store = m_comp->gtNewStoreBlkNode(dsc->GetLayout(), addr, value,
+                                                  GTF_IND_NONFAULTING | GTF_IND_TGT_NOT_HEAP);
+            }
+            else
+            {
+                store = StoreAtOffset(byteArr, offset, value);
+            }
+
+            LIR::AsRange(retBB).InsertAtEnd(LIR::SeqTree(m_comp, store));
+        }
+    }
+
+    newContinuation = m_comp->gtNewLclvNode(m_newContinuationVar, TYP_REF);
+    GenTree* ret    = m_comp->gtNewOperNode(GT_RETURN_SUSPEND, TYP_VOID, newContinuation);
+    LIR::AsRange(retBB).InsertAtEnd(newContinuation, ret);
+
+    JITDUMP("  Created suspension BB " FMT_BB "\n", retBB->bbNum);
+
+    BasicBlock* prevResumptionBB;
+    if (m_resumptionBBs.size() == 0)
+    {
+        prevResumptionBB = m_comp->fgLastBBInMainFunction();
+    }
+    else
+    {
+        prevResumptionBB = m_resumptionBBs[m_resumptionBBs.size() - 1];
+    }
+
+    BasicBlock* resumeBB = m_comp->fgNewBBafter(BBJ_ALWAYS, prevResumptionBB, true, remainder);
+    resumeBB->bbSetRunRarely();
+    resumeBB->bbFlags |= BBF_ASYNC_RESUMPTION;
+
+    m_comp->fgAddRefPred(remainder, resumeBB);
+
+    if (dataSize > 0)
+    {
+        unsigned byteArrLclNum = GetDataArrayVar();
+
+        GenTree* newContinuation = m_comp->gtNewLclvNode(m_newContinuationVar, TYP_REF);
+        unsigned dataOffset =
+            TARGET_POINTER_SIZE + m_comp->info.compCompHnd->getFieldOffset(m_async2Info.continuationDataFldHnd);
+        GenTree* dataOffsetNode      = m_comp->gtNewIconNode((ssize_t)dataOffset, TYP_I_IMPL);
+        GenTree* dataAddr            = m_comp->gtNewOperNode(GT_ADD, TYP_BYREF, newContinuation, dataOffsetNode);
+        GenTree* dataInd             = m_comp->gtNewIndir(TYP_REF, dataAddr, GTF_IND_NONFAULTING);
+        GenTree* storeAllocedByteArr = m_comp->gtNewStoreLclVarNode(byteArrLclNum, dataInd);
+        LIR::AsRange(resumeBB).InsertAtEnd(newContinuation, dataOffsetNode, dataAddr, dataInd, storeAllocedByteArr);
+
+        // Copy data
+        for (LiveLocalInfo& inf : m_liveLocals)
+        {
+            if (inf.DataSize <= 0)
+            {
+                continue;
+            }
+
+            LclVarDsc* dsc = m_comp->lvaGetDesc(inf.LclNum);
+
+            GenTree* byteArr = m_comp->gtNewLclvNode(byteArrLclNum, TYP_REF);
+            unsigned offset  = OFFSETOF__CORINFO_Array__data + inf.DataOffset;
+            GenTree* cns     = m_comp->gtNewIconNode((ssize_t)offset, TYP_I_IMPL);
+            GenTree* addr    = m_comp->gtNewOperNode(GT_ADD, TYP_BYREF, byteArr, cns);
+
+            GenTree* value;
+            if ((dsc->TypeGet() == TYP_STRUCT) || dsc->IsImplicitByRef())
+            {
+                value = m_comp->gtNewBlkIndir(dsc->GetLayout(), addr, GTF_IND_NONFAULTING);
+            }
+            else
+            {
+                value = m_comp->gtNewIndir(dsc->TypeGet(), addr, GTF_IND_NONFAULTING);
+            }
+
+            GenTree* store;
+            if (dsc->IsImplicitByRef())
+            {
+                GenTree* baseAddr = m_comp->gtNewLclvNode(inf.LclNum, dsc->TypeGet());
+                // TODO-CQ: Incoming data has no non-null GC refs, so this does not need write barriers.
+                // Backend does not handle GTF_IND_TGT_NOT_HEAP for STORE_BLK.
+                store = m_comp->gtNewStoreBlkNode(dsc->GetLayout(), baseAddr, value,
+                                                  GTF_IND_NONFAULTING | GTF_IND_TGT_NOT_HEAP);
+            }
+            else
+            {
+                store = m_comp->gtNewStoreLclVarNode(inf.LclNum, value);
+            }
+
+            LIR::AsRange(resumeBB).InsertAtEnd(LIR::SeqTree(m_comp, store));
+        }
+    }
+
+    if (gcRefsCount > 0)
+    {
+        unsigned objectArrLclNum = GetGCDataArrayVar();
+
+        newContinuation = m_comp->gtNewLclvNode(m_newContinuationVar, TYP_REF);
+        unsigned gcDataOffset =
+            TARGET_POINTER_SIZE + m_comp->info.compCompHnd->getFieldOffset(m_async2Info.continuationGCDataFldHnd);
+        GenTree* gcDataOffsetNode      = m_comp->gtNewIconNode((ssize_t)gcDataOffset, TYP_I_IMPL);
+        GenTree* gcDataAddr            = m_comp->gtNewOperNode(GT_ADD, TYP_BYREF, newContinuation, gcDataOffsetNode);
+        GenTree* gcDataInd             = m_comp->gtNewIndir(TYP_REF, gcDataAddr, GTF_IND_NONFAULTING);
+        GenTree* storeAllocedObjectArr = m_comp->gtNewStoreLclVarNode(objectArrLclNum, gcDataInd);
+        LIR::AsRange(resumeBB).InsertAtEnd(newContinuation, gcDataOffsetNode, gcDataAddr, gcDataInd,
+                                           storeAllocedObjectArr);
+
+        // Copy GC pointers
+        for (LiveLocalInfo& inf : m_liveLocals)
+        {
+            if (inf.GCDataCount <= 0)
+            {
+                continue;
+            }
+
+            LclVarDsc* dsc = m_comp->lvaGetDesc(inf.LclNum);
+            if (dsc->TypeGet() == TYP_REF)
+            {
+                GenTree* objectArr = m_comp->gtNewLclvNode(objectArrLclNum, TYP_REF);
+                unsigned offset    = OFFSETOF__CORINFO_Array__data + (inf.GCDataIndex * TARGET_POINTER_SIZE);
+                GenTree* value     = LoadFromOffset(objectArr, offset, TYP_REF);
+                GenTree* store     = m_comp->gtNewStoreLclVarNode(inf.LclNum, value);
+
+                LIR::AsRange(resumeBB).InsertAtEnd(LIR::SeqTree(m_comp, store));
+            }
+            else
+            {
+                assert((dsc->TypeGet() == TYP_STRUCT) || dsc->IsImplicitByRef());
+                ClassLayout* layout   = dsc->GetLayout();
+                unsigned     numSlots = layout->GetSlotCount();
+                for (unsigned i = 0; i < numSlots; i++)
+                {
+                    var_types gcPtrType = layout->GetGCPtrType(i);
+                    assert((gcPtrType == TYP_I_IMPL) || (gcPtrType == TYP_REF));
+                    if (gcPtrType != TYP_REF)
+                    {
+                        continue;
+                    }
+
+                    GenTree* objectArr = m_comp->gtNewLclvNode(objectArrLclNum, TYP_REF);
+                    unsigned offset    = OFFSETOF__CORINFO_Array__data + ((inf.GCDataIndex + i) * TARGET_POINTER_SIZE);
+                    GenTree* value     = LoadFromOffset(objectArr, offset, TYP_REF);
+                    GenTree* store;
+                    if (dsc->IsImplicitByRef())
+                    {
+                        GenTree* baseAddr = m_comp->gtNewLclvNode(inf.LclNum, dsc->TypeGet());
+                        store             = StoreAtOffset(baseAddr, i * TARGET_POINTER_SIZE, value);
+                        // Implicit byref args are never on heap today, skip write barriers.
+                        // TODO-CQ: Remove this once all implicit byrefs are TYP_I_IMPL typed.
+                        store->gtFlags |= GTF_IND_TGT_NOT_HEAP;
+                    }
+                    else
+                    {
+                        store = m_comp->gtNewStoreLclFldNode(inf.LclNum, TYP_REF, i * TARGET_POINTER_SIZE, value);
+                    }
+
+                    LIR::AsRange(resumeBB).InsertAtEnd(LIR::SeqTree(m_comp, store));
+                }
+            }
+        }
+
+        // Copy call return value.
+        if (storeResultNode != nullptr)
+        {
+            GenTree* objectArr = m_comp->gtNewLclvNode(objectArrLclNum, TYP_REF);
+            GenTree* resultBox = LoadFromOffset(objectArr, OFFSETOF__CORINFO_Array__data, TYP_REF);
+
+            LclVarDsc* resultLcl = m_comp->lvaGetDesc(storeResultNode);
+            assert((resultLcl->TypeGet() == TYP_STRUCT) == storeResultNode->TypeIs(TYP_STRUCT));
+
+            // TODO-TP: We can use liveness to avoid generating a lot of this IR.
+            if (storeResultNode->TypeIs(TYP_STRUCT))
+            {
+                if (m_comp->lvaGetPromotionType(resultLcl) != Compiler::PROMOTION_TYPE_INDEPENDENT)
+                {
+                    GenTree* boxDataOffset = m_comp->gtNewIconNode(TARGET_POINTER_SIZE, TYP_I_IMPL);
+                    GenTree* boxDataAddr   = m_comp->gtNewOperNode(GT_ADD, TYP_BYREF, resultBox, boxDataOffset);
+                    GenTree* boxData =
+                        m_comp->gtNewBlkIndir(storeResultNode->GetLayout(m_comp), boxDataAddr, GTF_IND_NONFAULTING);
+                    if (storeResultNode->OperIs(GT_STORE_LCL_VAR))
+                    {
+                        store = m_comp->gtNewStoreLclVarNode(storeResultNode->GetLclNum(), boxData);
+                    }
+                    else
+                    {
+                        store = m_comp->gtNewStoreLclFldNode(storeResultNode->GetLclNum(), TYP_STRUCT,
+                                                             storeResultNode->GetLayout(m_comp),
+                                                             storeResultNode->GetLclOffs(), boxData);
+                    }
+
+                    LIR::AsRange(resumeBB).InsertAtEnd(LIR::SeqTree(m_comp, store));
+                }
+                else
+                {
+                    if (resultLcl->lvFieldCnt > 1)
+                    {
+                        unsigned resultBoxVar   = GetResultBoxVar();
+                        GenTree* storeResultBox = m_comp->gtNewStoreLclVarNode(resultBoxVar, resultBox);
+                        LIR::AsRange(resumeBB).InsertAtEnd(LIR::SeqTree(m_comp, storeResultBox));
+
+                        resultBox = m_comp->gtNewLclVarNode(resultBoxVar, TYP_REF);
+                    }
+
+                    assert(storeResultNode->OperIs(GT_STORE_LCL_VAR));
+                    for (unsigned i = 0; i < resultLcl->lvFieldCnt; i++)
+                    {
+                        unsigned   fieldLclNum = resultLcl->lvFieldLclStart + i;
+                        LclVarDsc* fieldDsc    = m_comp->lvaGetDesc(fieldLclNum);
+
+                        unsigned fldOffset = TARGET_POINTER_SIZE + fieldDsc->lvFldOffset;
+                        GenTree* value     = LoadFromOffset(resultBox, fldOffset, fieldDsc->TypeGet());
+                        GenTree* store     = m_comp->gtNewStoreLclVarNode(fieldLclNum, value);
+                        LIR::AsRange(resumeBB).InsertAtEnd(LIR::SeqTree(m_comp, store));
+
+                        if (i + 1 != resultLcl->lvFieldCnt)
+                        {
+                            resultBox = m_comp->gtCloneExpr(resultBox);
+                        }
+                    }
+                }
+            }
+            else
+            {
+                GenTree* value;
+                if (call->gtReturnType == TYP_REF)
+                {
+                    value = resultBox;
+                }
+                else
+                {
+                    value = LoadFromOffset(resultBox, TARGET_POINTER_SIZE, call->gtReturnType);
+                }
+
+                if (storeResultNode->OperIs(GT_STORE_LCL_VAR))
+                {
+                    store = m_comp->gtNewStoreLclVarNode(storeResultNode->GetLclNum(), value);
+                }
+                else
+                {
+                    store = m_comp->gtNewStoreLclFldNode(storeResultNode->GetLclNum(), storeResultNode->TypeGet(),
+                                                         storeResultNode->GetLclOffs(), value);
+                }
+
+                LIR::AsRange(resumeBB).InsertAtEnd(LIR::SeqTree(m_comp, store));
+            }
+        }
+    }
+
+    m_resumptionBBs.push_back(resumeBB);
+    JITDUMP("  Created resumption BB " FMT_BB "\n", resumeBB->bbNum);
+}
+
+GenTreeIndir* Async2Transformation::LoadFromOffset(GenTree* base, unsigned offset, var_types type)
+{
+    assert(base->TypeIs(TYP_REF, TYP_BYREF, TYP_I_IMPL));
+    GenTree*      cns      = m_comp->gtNewIconNode((ssize_t)offset, TYP_I_IMPL);
+    var_types     addrType = base->TypeIs(TYP_I_IMPL) ? TYP_I_IMPL : TYP_BYREF;
+    GenTree*      addr     = m_comp->gtNewOperNode(GT_ADD, addrType, base, cns);
+    GenTreeIndir* load     = m_comp->gtNewIndir(type, addr, GTF_IND_NONFAULTING);
+    return load;
+}
+
+GenTreeStoreInd* Async2Transformation::StoreAtOffset(GenTree* base, unsigned offset, GenTree* value)
+{
+    assert(base->TypeIs(TYP_REF, TYP_BYREF, TYP_I_IMPL));
+    GenTree*         cns      = m_comp->gtNewIconNode((ssize_t)offset, TYP_I_IMPL);
+    var_types        addrType = base->TypeIs(TYP_I_IMPL) ? TYP_I_IMPL : TYP_BYREF;
+    GenTree*         addr     = m_comp->gtNewOperNode(GT_ADD, addrType, base, cns);
+    GenTreeStoreInd* store    = m_comp->gtNewStoreIndNode(value->TypeGet(), addr, value, GTF_IND_NONFAULTING);
+    return store;
+}
+
+bool Async2Transformation::IsLive(unsigned lclNum)
+{
+#if FEATURE_FIXED_OUT_ARGS
+    if (lclNum == m_comp->lvaOutgoingArgSpaceVar)
+    {
+        return false;
+    }
+#endif
+
+    LclVarDsc* dsc = m_comp->lvaGetDesc(lclNum);
+    if (dsc->lvRefCnt(RCS_NORMAL) == 0)
+    {
+        return false;
+    }
+
+    Compiler::lvaPromotionType promoType = m_comp->lvaGetPromotionType(dsc);
+    if (promoType == Compiler::PROMOTION_TYPE_INDEPENDENT)
+    {
+        // Independently promoted structs are handled only through their
+        // fields.
+        return false;
+    }
+
+    if (promoType == Compiler::PROMOTION_TYPE_DEPENDENT)
+    {
+        // Dependently promoted structs are handled only through the base
+        // struct local.
+        //
+        // A dependently promoted struct is live either if it has significant
+        // padding (since we do not track liveness for the significant
+        // padding), or if all its fields are dead.
+
+        if (dsc->lvAnySignificantPadding)
+        {
+            // We could technically only save the padding and the live fields
+            // in this case, but that's a lot of complexity for not a lot of
+            // gain.
+            return true;
+        }
+
+        for (unsigned i = 0; i < dsc->lvFieldCnt; i++)
+        {
+            LclVarDsc* fieldDsc = m_comp->lvaGetDesc(dsc->lvFieldLclStart + i);
+            if (!fieldDsc->lvTracked || VarSetOps::IsMember(m_comp, m_comp->compCurLife, fieldDsc->lvVarIndex))
+            {
+                return true;
+            }
+        }
+    }
+
+    if (dsc->lvIsStructField && (m_comp->lvaGetParentPromotionType(dsc) == Compiler::PROMOTION_TYPE_DEPENDENT))
+    {
+        return false;
+    }
+
+    return !dsc->lvTracked || VarSetOps::IsMember(m_comp, m_comp->compCurLife, dsc->lvVarIndex);
+}
+
+unsigned Async2Transformation::GetDataArrayVar()
+{
+    if (m_dataArrayVar == BAD_VAR_NUM)
+    {
+        m_dataArrayVar                             = m_comp->lvaGrabTemp(false DEBUGARG("byte[] for continuation"));
+        m_comp->lvaGetDesc(m_dataArrayVar)->lvType = TYP_REF;
+    }
+
+    return m_dataArrayVar;
+}
+
+unsigned Async2Transformation::GetGCDataArrayVar()
+{
+    if (m_gcDataArrayVar == BAD_VAR_NUM)
+    {
+        m_gcDataArrayVar                             = m_comp->lvaGrabTemp(false DEBUGARG("object[] for continuation"));
+        m_comp->lvaGetDesc(m_gcDataArrayVar)->lvType = TYP_REF;
+    }
+
+    return m_gcDataArrayVar;
+}
+
+unsigned Async2Transformation::GetResultBoxVar()
+{
+    if (m_resultBoxVar == BAD_VAR_NUM)
+    {
+        m_resultBoxVar                             = m_comp->lvaGrabTemp(false DEBUGARG("object for result box"));
+        m_comp->lvaGetDesc(m_resultBoxVar)->lvType = TYP_REF;
+    }
+
+    return m_resultBoxVar;
+}
+
+GenTree* Async2Transformation::CreateResumptionStubAddrTree()
+{
+    switch (m_resumeStubLookup.accessType)
+    {
+        case IAT_VALUE:
+        {
+            return CreateFunctionTargetAddr(m_resumeStub, m_resumeStubLookup);
+        }
+        case IAT_PVALUE:
+        {
+            GenTree* tree = CreateFunctionTargetAddr(m_resumeStub, m_resumeStubLookup);
+            tree          = m_comp->gtNewIndir(TYP_I_IMPL, tree, GTF_IND_NONFAULTING | GTF_IND_INVARIANT);
+            return tree;
+        }
+        case IAT_PPVALUE:
+        {
+            noway_assert(!"Unexpected IAT_PPVALUE");
+            return nullptr;
+        }
+        case IAT_RELPVALUE:
+        {
+            GenTree* addr = CreateFunctionTargetAddr(m_resumeStub, m_resumeStubLookup);
+            GenTree* tree = CreateFunctionTargetAddr(m_resumeStub, m_resumeStubLookup);
+            tree          = m_comp->gtNewIndir(TYP_I_IMPL, tree, GTF_IND_NONFAULTING | GTF_IND_INVARIANT);
+            tree          = m_comp->gtNewOperNode(GT_ADD, TYP_I_IMPL, tree, addr);
+            return tree;
+        }
+        default:
+        {
+            noway_assert(!"Bad accessType");
+            return nullptr;
+        }
+    }
+}
+
+GenTree* Async2Transformation::CreateFunctionTargetAddr(CORINFO_METHOD_HANDLE       methHnd,
+                                                        const CORINFO_CONST_LOOKUP& lookup)
+{
+    GenTree* con = m_comp->gtNewIconHandleNode((size_t)lookup.addr, GTF_ICON_FTN_ADDR);
+    INDEBUG(con->AsIntCon()->gtTargetHandle = (size_t)methHnd);
+    return con;
+}
+
+void Async2Transformation::LiftLIREdges(BasicBlock*                    block,
+                                        GenTree*                       beyond,
+                                        jitstd::vector<GenTree*>&      defs,
+                                        jitstd::vector<LiveLocalInfo>& liveLocals)
+{
+    if (defs.size() <= 0)
+    {
+        return;
+    }
+
+    for (GenTree* tree : defs)
+    {
+        // TODO-CQ: Breaks our recognition of how the call is stored.
+        // if (tree->OperIs(GT_LCL_VAR))
+        //{
+        //    LclVarDsc* dsc = m_comp->lvaGetDesc(tree->AsLclVarCommon());
+        //    if (!dsc->IsAddressExposed())
+        //    {
+        //        // No interference by IR invariants.
+        //        LIR::AsRange(block).Remove(tree);
+        //        LIR::AsRange(block).InsertAfter(beyond, tree);
+        //        continue;
+        //    }
+        //}
+
+        LIR::Use use;
+        bool     gotUse = LIR::AsRange(block).TryGetUse(tree, &use);
+        assert(gotUse); // Defs list should not contain unused values.
+
+        unsigned newLclNum = use.ReplaceWithLclVar(m_comp);
+        liveLocals.push_back(LiveLocalInfo(newLclNum));
+        GenTree* newUse = use.Def();
+        LIR::AsRange(block).Remove(newUse);
+        LIR::AsRange(block).InsertBefore(use.User(), newUse);
+    }
+}
+
+void Async2Transformation::CreateResumptionSwitch()
+{
+    BasicBlock* newEntryBB = m_comp->bbNewBasicBlock(BBJ_NONE);
+    JITDUMP("New entry BB: " FMT_BB "\n", newEntryBB->bbNum);
+
+    if (m_comp->fgFirstBB->hasProfileWeight())
+    {
+        newEntryBB->inheritWeight(m_comp->fgFirstBB);
+    }
+    m_comp->fgFirstBB->bbRefs--;
+
+    FlowEdge* edge = m_comp->fgAddRefPred(m_comp->fgFirstBB, newEntryBB);
+    edge->setLikelihood(1);
+
+    m_comp->fgInsertBBbefore(m_comp->fgFirstBB, newEntryBB);
+
+    // If previous first BB was a scratch BB, then we must add a new scratch BB
+    // to create IR before the switch.
+    m_comp->fgFirstBBScratch = nullptr;
+
+    GenTree* continuationArg = m_comp->gtNewLclvNode(m_comp->lvaAsyncContinuationArg, TYP_REF);
+    GenTree* null            = m_comp->gtNewNull();
+    GenTree* neNull          = m_comp->gtNewOperNode(GT_NE, TYP_INT, continuationArg, null);
+    GenTree* jtrue           = m_comp->gtNewOperNode(GT_JTRUE, TYP_VOID, neNull);
+    LIR::AsRange(newEntryBB).InsertAtEnd(continuationArg, null, neNull, jtrue);
+
+    if (m_resumptionBBs.size() == 1)
+    {
+        newEntryBB->SetJumpKindAndTarget(BBJ_COND, m_resumptionBBs[0] DEBUGARG(m_comp));
+        m_comp->fgAddRefPred(m_resumptionBBs[0], newEntryBB);
+    }
+    else if (m_resumptionBBs.size() == 2)
+    {
+        BasicBlock* condBB = m_comp->fgNewBBbefore(BBJ_COND, m_resumptionBBs[0], true);
+        condBB->bbSetRunRarely();
+        newEntryBB->SetJumpKindAndTarget(BBJ_COND, condBB DEBUGARG(m_comp));
+
+        JITDUMP("New cond BB: " FMT_BB "\n", condBB->bbNum);
+
+        m_comp->fgAddRefPred(condBB, newEntryBB);
+        continuationArg = m_comp->gtNewLclvNode(m_comp->lvaAsyncContinuationArg, TYP_REF);
+        unsigned stateOffset =
+            TARGET_POINTER_SIZE + m_comp->info.compCompHnd->getFieldOffset(m_async2Info.continuationStateFldHnd);
+        GenTree* stateOffsetNode = m_comp->gtNewIconNode((ssize_t)stateOffset, TYP_I_IMPL);
+        GenTree* stateAddr       = m_comp->gtNewOperNode(GT_ADD, TYP_BYREF, continuationArg, stateOffsetNode);
+        GenTree* stateInd        = m_comp->gtNewIndir(TYP_INT, stateAddr, GTF_IND_NONFAULTING);
+        GenTree* zero            = m_comp->gtNewZeroConNode(TYP_INT);
+        GenTree* stateNeZero     = m_comp->gtNewOperNode(GT_NE, TYP_INT, stateInd, zero);
+        GenTree* jtrue           = m_comp->gtNewOperNode(GT_JTRUE, TYP_VOID, stateNeZero);
+
+        LIR::AsRange(condBB).InsertAtEnd(continuationArg, stateOffsetNode, stateAddr, stateInd, zero, stateNeZero,
+                                         jtrue);
+        condBB->SetJumpDest(m_resumptionBBs[1]);
+
+        m_comp->fgAddRefPred(m_resumptionBBs[0], condBB);
+        m_comp->fgAddRefPred(m_resumptionBBs[1], condBB);
+    }
+    else
+    {
+        BasicBlock* switchBB = m_comp->fgNewBBbefore(BBJ_SWITCH, m_resumptionBBs[0], true);
+        switchBB->bbSetRunRarely();
+        newEntryBB->SetJumpKindAndTarget(BBJ_COND, switchBB DEBUGARG(m_comp));
+
+        JITDUMP("New switch BB: " FMT_BB "\n", switchBB->bbNum);
+
+        m_comp->fgAddRefPred(switchBB, newEntryBB);
+
+        continuationArg = m_comp->gtNewLclvNode(m_comp->lvaAsyncContinuationArg, TYP_REF);
+        unsigned stateOffset =
+            TARGET_POINTER_SIZE + m_comp->info.compCompHnd->getFieldOffset(m_async2Info.continuationStateFldHnd);
+        GenTree* stateOffsetNode = m_comp->gtNewIconNode((ssize_t)stateOffset, TYP_I_IMPL);
+        GenTree* stateAddr       = m_comp->gtNewOperNode(GT_ADD, TYP_BYREF, continuationArg, stateOffsetNode);
+        GenTree* stateInd        = m_comp->gtNewIndir(TYP_INT, stateAddr, GTF_IND_NONFAULTING);
+        GenTree* switchNode      = m_comp->gtNewOperNode(GT_SWITCH, TYP_VOID, stateInd);
+
+        LIR::AsRange(switchBB).InsertAtEnd(continuationArg, stateOffsetNode, stateAddr, stateInd, switchNode);
+
+        m_comp->fgHasSwitch = true;
+
+        //// Default case. TODO-CQ: Support bbsHasDefault = false before lowering.
+        m_resumptionBBs.push_back(m_resumptionBBs[0]);
+        BBswtDesc* swtDesc     = new (m_comp, CMK_BasicBlock) BBswtDesc;
+        swtDesc->bbsCount      = (unsigned)m_resumptionBBs.size();
+        swtDesc->bbsHasDefault = true;
+        swtDesc->bbsDstTab     = m_resumptionBBs.data();
+
+        switchBB->SetSwitchKindAndTarget(swtDesc);
+
+        for (BasicBlock* bb : m_resumptionBBs)
+        {
+            m_comp->fgAddRefPred(bb, switchBB);
+        }
+    }
+}
diff --git a/src/coreclr/jit/async.h b/src/coreclr/jit/async.h
new file mode 100644
index 00000000000000..2bc6838eb6ce6b
--- /dev/null
+++ b/src/coreclr/jit/async.h
@@ -0,0 +1,59 @@
+class Async2Transformation
+{
+    struct LiveLocalInfo
+    {
+        unsigned LclNum;
+        unsigned Alignment;
+        unsigned DataOffset;
+        unsigned DataSize;
+        unsigned GCDataIndex;
+        unsigned GCDataCount;
+
+        explicit LiveLocalInfo(unsigned lclNum) : LclNum(lclNum)
+        {
+        }
+    };
+
+    Compiler*                     m_comp;
+    unsigned                      m_livenessLvaCount = 0;
+    jitstd::vector<LiveLocalInfo> m_liveLocals;
+    CORINFO_ASYNC2_INFO           m_async2Info;
+    CORINFO_CLASS_HANDLE          m_objectClsHnd;
+    CORINFO_CLASS_HANDLE          m_byteClsHnd;
+    jitstd::vector<BasicBlock*>   m_resumptionBBs;
+    CORINFO_METHOD_HANDLE         m_resumeStub = NO_METHOD_HANDLE;
+    CORINFO_CONST_LOOKUP          m_resumeStubLookup;
+    unsigned                      m_returnedContinuationVar = BAD_VAR_NUM;
+    unsigned                      m_newContinuationVar      = BAD_VAR_NUM;
+    unsigned                      m_dataArrayVar            = BAD_VAR_NUM;
+    unsigned                      m_gcDataArrayVar          = BAD_VAR_NUM;
+    unsigned                      m_resultBoxVar            = BAD_VAR_NUM;
+    BasicBlock*                   m_lastSuspensionBB        = nullptr;
+
+    void LiftLIREdges(BasicBlock*                    block,
+                      GenTree*                       beyond,
+                      jitstd::vector<GenTree*>&      defs,
+                      jitstd::vector<LiveLocalInfo>& liveLocals);
+    bool IsLive(unsigned lclNum);
+    void Transform(BasicBlock* block, GenTreeCall* call, jitstd::vector<GenTree*>& defs, BasicBlock** remainder);
+
+    GenTreeIndir* LoadFromOffset(GenTree* base, unsigned offset, var_types type);
+    GenTreeStoreInd* StoreAtOffset(GenTree* base, unsigned offset, GenTree* value);
+
+    unsigned GetDataArrayVar();
+    unsigned GetGCDataArrayVar();
+    unsigned GetResultBoxVar();
+
+    GenTree* CreateResumptionStubAddrTree();
+    GenTree* CreateFunctionTargetAddr(CORINFO_METHOD_HANDLE methHnd, const CORINFO_CONST_LOOKUP& lookup);
+
+    void CreateResumptionSwitch();
+
+public:
+    Async2Transformation(Compiler* comp)
+        : m_comp(comp), m_liveLocals(comp->getAllocator(CMK_Async2)), m_resumptionBBs(comp->getAllocator(CMK_Async2))
+    {
+    }
+
+    PhaseStatus Run();
+};
diff --git a/src/coreclr/jit/block.cpp b/src/coreclr/jit/block.cpp
index 2407afea595717..caf7b6c5ce7166 100644
--- a/src/coreclr/jit/block.cpp
+++ b/src/coreclr/jit/block.cpp
@@ -521,6 +521,10 @@ void BasicBlock::dspFlags()
     {
         printf("mdarr ");
     }
+    if (bbFlags & BBF_ASYNC_RESUMPTION)
+    {
+        printf("resume ");
+    }
 }
 
 /*****************************************************************************
diff --git a/src/coreclr/jit/block.h b/src/coreclr/jit/block.h
index ecdce490528f61..6926bef3b6fa1e 100644
--- a/src/coreclr/jit/block.h
+++ b/src/coreclr/jit/block.h
@@ -440,6 +440,7 @@ enum BasicBlockFlags : unsigned __int64
     BBF_TAILCALL_SUCCESSOR             = MAKE_BBFLAG(40), // BB has pred that has potential tail call
     BBF_RECURSIVE_TAILCALL             = MAKE_BBFLAG(41), // Block has recursive tailcall that may turn into a loop
     BBF_NO_CSE_IN                      = MAKE_BBFLAG(42), // Block should kill off any incoming CSE
+    BBF_ASYNC_RESUMPTION               = MAKE_BBFLAG(43), // Block is a resumption block in an async method
 
     // The following are sets of flags.
 
@@ -461,7 +462,7 @@ enum BasicBlockFlags : unsigned __int64
     // For example, the top block might or might not have BBF_GC_SAFE_POINT,
     // but we assume it does not have BBF_GC_SAFE_POINT any more.
 
-    BBF_SPLIT_LOST = BBF_GC_SAFE_POINT | BBF_HAS_JMP | BBF_KEEP_BBJ_ALWAYS | BBF_CLONED_FINALLY_END | BBF_RECURSIVE_TAILCALL,
+    BBF_SPLIT_LOST = BBF_GC_SAFE_POINT | BBF_HAS_JMP | BBF_KEEP_BBJ_ALWAYS | BBF_CLONED_FINALLY_END | BBF_RECURSIVE_TAILCALL | BBF_ASYNC_RESUMPTION,
 
     // Flags gained by the bottom block when a block is split.
     // Note, this is a conservative guess.
@@ -469,7 +470,7 @@ enum BasicBlockFlags : unsigned __int64
     // TODO: Should BBF_RUN_RARELY be added to BBF_SPLIT_GAINED ?
 
     BBF_SPLIT_GAINED = BBF_DONT_REMOVE | BBF_HAS_JMP | BBF_BACKWARD_JUMP | BBF_HAS_IDX_LEN | BBF_HAS_MD_IDX_LEN | BBF_PROF_WEIGHT | \
-                       BBF_HAS_NEWOBJ | BBF_KEEP_BBJ_ALWAYS | BBF_CLONED_FINALLY_END | BBF_HAS_NULLCHECK | BBF_HAS_HISTOGRAM_PROFILE | BBF_HAS_MDARRAYREF,
+                       BBF_HAS_NEWOBJ | BBF_KEEP_BBJ_ALWAYS | BBF_CLONED_FINALLY_END | BBF_HAS_NULLCHECK | BBF_HAS_HISTOGRAM_PROFILE | BBF_HAS_MDARRAYREF | BBF_ASYNC_RESUMPTION,
 
     // Flags that must be propagated to a new block if code is copied from a block to a new block. These are flags that
     // limit processing of a block if the code in question doesn't exist. This is conservative; we might not
diff --git a/src/coreclr/jit/codegen.h b/src/coreclr/jit/codegen.h
index 7e15e7f6526150..437db845d163ec 100644
--- a/src/coreclr/jit/codegen.h
+++ b/src/coreclr/jit/codegen.h
@@ -1152,6 +1152,7 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
     void genCodeForCpBlkHelper(GenTreeBlk* cpBlkNode);
 #endif
     void genCodeForPhysReg(GenTreePhysReg* tree);
+    void genCodeForAsyncContinuation(GenTree* tree);
     void genCodeForNullCheck(GenTreeIndir* tree);
     void genCodeForCmpXchg(GenTreeCmpXchg* tree);
     void genCodeForReuseVal(GenTree* treeNode);
@@ -1285,6 +1286,7 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
 #endif // TARGET_ARM64 || TARGET_LOONGARCH64 || TARGET_RISCV64
 
     void genReturn(GenTree* treeNode);
+    void genReturnSuspend(GenTreeUnOp* treeNode);
 
 #ifdef TARGET_XARCH
     void genStackPointerConstantAdjustment(ssize_t spDelta, bool trackSpAdjustments);
diff --git a/src/coreclr/jit/codegencommon.cpp b/src/coreclr/jit/codegencommon.cpp
index 83270d95b2564c..254808c18ff1c9 100644
--- a/src/coreclr/jit/codegencommon.cpp
+++ b/src/coreclr/jit/codegencommon.cpp
@@ -7608,6 +7608,11 @@ void CodeGen::genReturn(GenTree* treeNode)
         }
     }
 
+    if (treeNode->OperIs(GT_RETURN) && compiler->compIsAsync2StateMachine())
+    {
+        instGen_Set_Reg_To_Zero(EA_PTRSIZE, REG_ASYNC_CONTINUATION_RET);
+    }
+
 #ifdef PROFILING_SUPPORTED
     // !! Note !!
     // TODO-AMD64-Unix: If the profiler hook is implemented on *nix, make sure for 2 register returned structs
@@ -7690,6 +7695,26 @@ void CodeGen::genReturn(GenTree* treeNode)
 #endif // defined(DEBUG) && defined(TARGET_XARCH)
 }
 
+void CodeGen::genReturnSuspend(GenTreeUnOp* treeNode)
+{
+    GenTree* op = treeNode->gtGetOp1();
+    assert(op->TypeIs(TYP_REF));
+
+    regNumber reg = genConsumeReg(op);
+    inst_Mov_Extend(TYP_REF, /* srcInReg */ true, REG_ASYNC_CONTINUATION_RET, reg, /* canSkip */ true);
+
+    ReturnTypeDesc retTypeDesc = compiler->compRetTypeDesc;
+    unsigned       numRetRegs  = retTypeDesc.GetReturnRegCount();
+    for (unsigned i = 0; i < numRetRegs; i++)
+    {
+        if (varTypeIsGC(retTypeDesc.GetReturnRegType(i)))
+        {
+            regNumber returnReg = retTypeDesc.GetABIReturnReg(i);
+            instGen_Set_Reg_To_Zero(EA_PTRSIZE, returnReg);
+        }
+    }
+}
+
 //------------------------------------------------------------------------
 // isStructReturn: Returns whether the 'treeNode' is returning a struct.
 //
diff --git a/src/coreclr/jit/codegenxarch.cpp b/src/coreclr/jit/codegenxarch.cpp
index e9d52419bb52bf..91c90ce8ff97c7 100644
--- a/src/coreclr/jit/codegenxarch.cpp
+++ b/src/coreclr/jit/codegenxarch.cpp
@@ -1924,6 +1924,10 @@ void CodeGen::genCodeForTreeNode(GenTree* treeNode)
             genReturn(treeNode);
             break;
 
+        case GT_RETURN_SUSPEND:
+            genReturnSuspend(treeNode->AsUnOp());
+            break;
+
         case GT_LEA:
             // If we are here, it is the case where there is an LEA that cannot be folded into a parent instruction.
             genLeaInstruction(treeNode->AsAddrMode());
@@ -2103,6 +2107,9 @@ void CodeGen::genCodeForTreeNode(GenTree* treeNode)
             genConsumeReg(treeNode);
             break;
 
+        case GT_ASYNC_CONTINUATION:
+            break;
+
 #if !defined(FEATURE_EH_FUNCLETS)
         case GT_END_LFIN:
 
@@ -4520,6 +4527,19 @@ void CodeGen::genCodeForPhysReg(GenTreePhysReg* tree)
     genProduceReg(tree);
 }
 
+void CodeGen::genCodeForAsyncContinuation(GenTree* tree)
+{
+    assert(tree->OperIs(GT_ASYNC_CONTINUATION));
+
+    var_types targetType = tree->TypeGet();
+    regNumber targetReg  = tree->GetRegNum();
+
+    inst_Mov(targetType, targetReg, REG_ASYNC_CONTINUATION_RET, /* canSkip */ true);
+    genTransferRegGCState(targetReg, REG_ASYNC_CONTINUATION_RET);
+
+    genProduceReg(tree);
+}
+
 //---------------------------------------------------------------------
 // genCodeForNullCheck - generate code for a GT_NULLCHECK node
 //
diff --git a/src/coreclr/jit/compiler.cpp b/src/coreclr/jit/compiler.cpp
index 233667b24e632d..67466abfe6b037 100644
--- a/src/coreclr/jit/compiler.cpp
+++ b/src/coreclr/jit/compiler.cpp
@@ -5099,6 +5099,11 @@ void Compiler::compCompile(void** methodCodePtr, uint32_t* methodCodeSize, JitFl
     }
 #endif // TARGET_ARM
 
+    if (compIsAsync2StateMachine())
+    {
+        DoPhase(this, PHASE_ASYNC2, &Compiler::TransformAsync2);
+    }
+
     // Assign registers to variables, etc.
 
     // Create LinearScan before Lowering, so that Lowering can call LinearScan methods
diff --git a/src/coreclr/jit/compiler.h b/src/coreclr/jit/compiler.h
index 44437870ae655b..5d1fef53083443 100644
--- a/src/coreclr/jit/compiler.h
+++ b/src/coreclr/jit/compiler.h
@@ -703,6 +703,15 @@ class LclVarDsc
     }
 #endif // FEATURE_MULTIREG_ARGS
 
+    bool IsImplicitByRef()
+    {
+#if FEATURE_IMPLICIT_BYREFS
+        return lvIsImplicitByRef;
+#else
+        return false;
+#endif
+    }
+
     CorInfoHFAElemType GetLvHfaElemKind() const
     {
 #ifdef FEATURE_HFA_FIELDS_PRESENT
@@ -3361,6 +3370,9 @@ class Compiler
     // where it is used to detect tail-call chains.
     unsigned lvaRetAddrVar;
 
+    // Variable representing async continuation argument passed.
+    unsigned lvaAsyncContinuationArg;
+
 #if defined(DEBUG) && defined(TARGET_XARCH)
 
     unsigned lvaReturnSpCheck; // Stores SP to confirm it is not corrupted on return.
@@ -3475,6 +3487,7 @@ class Compiler
     void lvaInitUserArgs(InitVarDscInfo* varDscInfo, unsigned skipArgs, unsigned takeArgs);
     void lvaInitGenericsCtxt(InitVarDscInfo* varDscInfo);
     void lvaInitVarArgsHandle(InitVarDscInfo* varDscInfo);
+    void lvaInitAsyncContinuation(InitVarDscInfo* varDscInfo);
 
     void lvaInitVarDsc(LclVarDsc*              varDsc,
                        unsigned                varNum,
@@ -4901,6 +4914,8 @@ class Compiler
     PhaseStatus placeLoopAlignInstructions();
 #endif
 
+    PhaseStatus TransformAsync2();
+
     // This field keep the R2R helper call that would be inserted to trigger the constructor
     // of the static class. It is set as nongc or gc static base if they are imported, so
     // CSE can eliminate the repeated call, or the chepeast helper function that triggers it.
@@ -10207,6 +10222,16 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
 #endif // TARGET_AMD64
     }
 
+    bool compIsAsync2() const
+    {
+        return opts.jitFlags->IsSet(JitFlags::JIT_FLAG_RUNTIMEASYNCFUNCTION);
+    }
+
+    bool compIsAsync2StateMachine() const
+    {
+        return compIsAsync2() && (JitConfig.RuntimeAsyncViaJitGeneratedStateMachines() != 0);
+    }
+
     //------------------------------------------------------------------------
     // compMethodReturnsMultiRegRetType: Does this method return a multi-reg value?
     //
@@ -11182,6 +11207,7 @@ class GenTreeVisitor
 
             // Leaf nodes
             case GT_CATCH_ARG:
+            case GT_ASYNC_CONTINUATION:
             case GT_LABEL:
             case GT_FTN_ADDR:
             case GT_RET_EXPR:
diff --git a/src/coreclr/jit/compiler.hpp b/src/coreclr/jit/compiler.hpp
index 47c9007ddee4fc..d21ba8abd40462 100644
--- a/src/coreclr/jit/compiler.hpp
+++ b/src/coreclr/jit/compiler.hpp
@@ -2779,6 +2779,12 @@ inline unsigned Compiler::compMapILargNum(unsigned ILargNum)
         assert(ILargNum < info.compLocalsCount); // compLocals count already adjusted.
     }
 
+    if (ILargNum >= (unsigned)lvaAsyncContinuationArg)
+    {
+        ILargNum++;
+        assert(ILargNum < info.compLocalsCount); // compLocals count already adjusted.
+    }
+
     if (ILargNum >= (unsigned)lvaVarargsHandleArg)
     {
         ILargNum++;
@@ -4440,6 +4446,7 @@ void GenTree::VisitOperands(TVisitor visitor)
         case GT_LCL_FLD:
         case GT_LCL_ADDR:
         case GT_CATCH_ARG:
+        case GT_ASYNC_CONTINUATION:
         case GT_LABEL:
         case GT_FTN_ADDR:
         case GT_RET_EXPR:
@@ -4514,6 +4521,7 @@ void GenTree::VisitOperands(TVisitor visitor)
         case GT_RETURNTRAP:
         case GT_KEEPALIVE:
         case GT_INC_SATURATE:
+        case GT_RETURN_SUSPEND:
             visitor(this->AsUnOp()->gtOp1);
             return;
 
diff --git a/src/coreclr/jit/compmemkind.h b/src/coreclr/jit/compmemkind.h
index 645a6b44f80ee2..cf49ab2e885773 100644
--- a/src/coreclr/jit/compmemkind.h
+++ b/src/coreclr/jit/compmemkind.h
@@ -59,6 +59,7 @@ CompMemKindMacro(TailMergeThrows)
 CompMemKindMacro(EarlyProp)
 CompMemKindMacro(ZeroInit)
 CompMemKindMacro(Pgo)
+CompMemKindMacro(Async2)
 //clang-format on
 
 #undef CompMemKindMacro
diff --git a/src/coreclr/jit/compphases.h b/src/coreclr/jit/compphases.h
index 12cc374546e438..f97f831002721a 100644
--- a/src/coreclr/jit/compphases.h
+++ b/src/coreclr/jit/compphases.h
@@ -103,6 +103,7 @@ CompPhaseNameMacro(PHASE_CREATE_THROW_HELPERS,       "Create throw helper blocks
 CompPhaseNameMacro(PHASE_DETERMINE_FIRST_COLD_BLOCK, "Determine first cold block",     false, -1, true)
 CompPhaseNameMacro(PHASE_RATIONALIZE,                "Rationalize IR",                 false, -1, false)
 
+CompPhaseNameMacro(PHASE_ASYNC2,                     "Transform async2",               false, -1, true)
 CompPhaseNameMacro(PHASE_LCLVARLIVENESS,             "Local var liveness",             true,  -1, false)
 CompPhaseNameMacro(PHASE_LCLVARLIVENESS_INIT,        "Local var liveness init",        false, PHASE_LCLVARLIVENESS, false)
 CompPhaseNameMacro(PHASE_LCLVARLIVENESS_PERBLOCK,    "Per block local var liveness",   false, PHASE_LCLVARLIVENESS, false)
diff --git a/src/coreclr/jit/fgdiagnostic.cpp b/src/coreclr/jit/fgdiagnostic.cpp
index 835c6f401fe9b6..c0c911d99d2dff 100644
--- a/src/coreclr/jit/fgdiagnostic.cpp
+++ b/src/coreclr/jit/fgdiagnostic.cpp
@@ -2654,6 +2654,11 @@ bool BBPredsChecker::CheckEhTryDsc(BasicBlock* block, BasicBlock* blockPred, EHb
         return true;
     }
 
+    if ((blockPred->bbFlags & BBF_ASYNC_RESUMPTION) != 0)
+    {
+        return true;
+    }
+
     printf("Jump into the middle of try region: " FMT_BB " branches to " FMT_BB "\n", blockPred->bbNum, block->bbNum);
     assert(!"Jump into middle of try region");
     return false;
@@ -3194,6 +3199,7 @@ void Compiler::fgDebugCheckFlags(GenTree* tree)
             break;
 
         case GT_CATCH_ARG:
+        case GT_ASYNC_CONTINUATION:
             expectedFlags |= GTF_ORDER_SIDEEFF;
             break;
 
@@ -3485,6 +3491,10 @@ void Compiler::fgDebugCheckNodeLinks(BasicBlock* block, Statement* stmt)
                 // The root of the tree should have GTF_ORDER_SIDEEFF set
                 noway_assert(stmt->GetRootNode()->gtFlags & GTF_ORDER_SIDEEFF);
             }
+            else if (tree->OperIs(GT_ASYNC_CONTINUATION))
+            {
+                assert(tree->gtFlags & GTF_ORDER_SIDEEFF);
+            }
         }
 
         if (tree->OperIsUnary() && tree->AsOp()->gtOp1)
diff --git a/src/coreclr/jit/forwardsub.cpp b/src/coreclr/jit/forwardsub.cpp
index 7c1b0316153dfc..a16c57de858fd7 100644
--- a/src/coreclr/jit/forwardsub.cpp
+++ b/src/coreclr/jit/forwardsub.cpp
@@ -498,7 +498,7 @@ bool Compiler::fgForwardSubStatement(Statement* stmt)
     // Can't substitute GT_LCLHEAP.
     //
     // Don't substitute a no return call (trips up morph in some cases).
-    if (fwdSubNode->OperIs(GT_CATCH_ARG, GT_LCLHEAP))
+    if (fwdSubNode->OperIs(GT_CATCH_ARG, GT_LCLHEAP, GT_ASYNC_CONTINUATION))
     {
         JITDUMP(" tree to sub is catch arg, or lcl heap\n");
         return false;
diff --git a/src/coreclr/jit/gentree.cpp b/src/coreclr/jit/gentree.cpp
index 585ffa86802104..9e8ffb5b06c1c1 100644
--- a/src/coreclr/jit/gentree.cpp
+++ b/src/coreclr/jit/gentree.cpp
@@ -1733,6 +1733,9 @@ void CallArgs::AddedWellKnownArg(WellKnownArg arg)
         case WellKnownArg::RetBuffer:
             m_hasRetBuffer = true;
             break;
+        case WellKnownArg::AsyncContinuation:
+            m_hasAsyncContinuation = true;
+            break;
         default:
             break;
     }
@@ -1756,6 +1759,10 @@ void CallArgs::RemovedWellKnownArg(WellKnownArg arg)
             assert(FindWellKnownArg(arg) == nullptr);
             m_hasRetBuffer = false;
             break;
+        case WellKnownArg::AsyncContinuation:
+            assert(FindWellKnownArg(arg) == nullptr);
+            m_hasAsyncContinuation = false;
+            break;
         default:
             break;
     }
@@ -2354,6 +2361,11 @@ bool GenTreeCall::HasSideEffects(Compiler* compiler, bool ignoreExceptions, bool
            (!helperProperties.IsAllocator(helper) || ((gtCallMoreFlags & GTF_CALL_M_ALLOC_SIDE_EFFECTS) != 0));
 }
 
+bool GenTreeCall::IsAsync2() const
+{
+    return gtArgs.HasAsyncContinuation();
+}
+
 //-------------------------------------------------------------------------
 // HasNonStandardAddedArgs: Return true if the method has non-standard args added to the call
 // argument list during argument morphing (fgMorphArgs), e.g., passed in R10 or R11 on AMD64.
@@ -6419,6 +6431,7 @@ bool GenTree::TryGetUse(GenTree* operand, GenTree*** pUse)
         case GT_LCL_FLD:
         case GT_LCL_ADDR:
         case GT_CATCH_ARG:
+        case GT_ASYNC_CONTINUATION:
         case GT_LABEL:
         case GT_FTN_ADDR:
         case GT_RET_EXPR:
@@ -7152,6 +7165,7 @@ bool GenTree::OperSupportsOrderingSideEffect() const
         case GT_CMPXCHG:
         case GT_MEMORYBARRIER:
         case GT_CATCH_ARG:
+        case GT_ASYNC_CONTINUATION:
             return true;
         default:
             return false;
@@ -9209,6 +9223,7 @@ GenTree* Compiler::gtCloneExpr(
                 goto DONE;
 
             case GT_CATCH_ARG:
+            case GT_ASYNC_CONTINUATION:
             case GT_NO_OP:
             case GT_LABEL:
                 copy = new (this, oper) GenTree(oper, tree->gtType);
@@ -10071,6 +10086,7 @@ GenTreeUseEdgeIterator::GenTreeUseEdgeIterator(GenTree* node)
         case GT_LCL_FLD:
         case GT_LCL_ADDR:
         case GT_CATCH_ARG:
+        case GT_ASYNC_CONTINUATION:
         case GT_LABEL:
         case GT_FTN_ADDR:
         case GT_RET_EXPR:
@@ -10135,6 +10151,7 @@ GenTreeUseEdgeIterator::GenTreeUseEdgeIterator(GenTree* node)
         case GT_PUTARG_SPLIT:
 #endif // FEATURE_ARG_SPLIT
         case GT_RETURNTRAP:
+        case GT_RETURN_SUSPEND:
             m_edge = &m_node->AsUnOp()->gtOp1;
             assert(*m_edge != nullptr);
             m_advance = &GenTreeUseEdgeIterator::Terminate;
@@ -11618,6 +11635,10 @@ void Compiler::gtGetLclVarNameInfo(unsigned lclNum, const char** ilKindOut, cons
         {
             ilName = "this";
         }
+        else if (lclNum == lvaAsyncContinuationArg)
+        {
+            ilName = "AsyncCont";
+        }
         else
         {
             ilKind = "arg";
@@ -12145,6 +12166,7 @@ void Compiler::gtDispLeaf(GenTree* tree, IndentStack* indentStack)
         case GT_START_PREEMPTGC:
         case GT_PROF_HOOK:
         case GT_CATCH_ARG:
+        case GT_ASYNC_CONTINUATION:
         case GT_MEMORYBARRIER:
         case GT_PINVOKE_PROLOG:
         case GT_JMPTABLE:
@@ -12910,6 +12932,8 @@ const char* Compiler::gtGetWellKnownArgNameForArgMsg(WellKnownArg arg)
             return "va cookie";
         case WellKnownArg::InstParam:
             return "gctx";
+        case WellKnownArg::AsyncContinuation:
+            return "async";
         case WellKnownArg::RetBuffer:
             return "retbuf";
         case WellKnownArg::PInvokeFrame:
diff --git a/src/coreclr/jit/gentree.h b/src/coreclr/jit/gentree.h
index 0430fe885c1144..34441aabe02820 100644
--- a/src/coreclr/jit/gentree.h
+++ b/src/coreclr/jit/gentree.h
@@ -4354,6 +4354,7 @@ enum class WellKnownArg
     ThisPointer,
     VarArgsCookie,
     InstParam,
+    AsyncContinuation,
     RetBuffer,
     PInvokeFrame,
     SecretStubParam,
@@ -4711,6 +4712,7 @@ class CallArgs
 #endif
     bool m_hasThisPointer : 1;
     bool m_hasRetBuffer : 1;
+    bool m_hasAsyncContinuation : 1;
     bool m_isVarArgs : 1;
     bool m_abiInformationDetermined : 1;
     // True if we have one or more register arguments.
@@ -4757,6 +4759,7 @@ class CallArgs
     CallArg* InsertAfter(Compiler* comp, CallArg* after, const NewCallArg& arg);
     CallArg* InsertAfterUnchecked(Compiler* comp, CallArg* after, const NewCallArg& arg);
     CallArg* InsertInstParam(Compiler* comp, GenTree* node);
+    CallArg* InsertAsyncContinuationParam(Compiler* comp, GenTree* node);
     CallArg* InsertAfterThisOrFirst(Compiler* comp, const NewCallArg& arg);
     void PushLateBack(CallArg* arg);
     void Remove(CallArg* arg);
@@ -4785,6 +4788,7 @@ class CallArgs
     // clang-format off
     bool HasThisPointer() const { return m_hasThisPointer; }
     bool HasRetBuffer() const { return m_hasRetBuffer; }
+    bool HasAsyncContinuation() const { return m_hasAsyncContinuation; }
     bool IsVarArgs() const { return m_isVarArgs; }
     void SetIsVarArgs() { m_isVarArgs = true; }
     void ClearIsVarArgs() { m_isVarArgs = false; }
@@ -4979,6 +4983,8 @@ struct GenTreeCall final : public GenTree
 #endif
     }
 
+    bool IsAsync2() const;
+
     //---------------------------------------------------------------------------
     // GetRegNumByIdx: get i'th return register allocated to this call node.
     //
diff --git a/src/coreclr/jit/gtlist.h b/src/coreclr/jit/gtlist.h
index e88c447d3774db..7d3e07485fa4ea 100644
--- a/src/coreclr/jit/gtlist.h
+++ b/src/coreclr/jit/gtlist.h
@@ -33,6 +33,7 @@ GTNODE(LCL_ADDR         , GenTreeLclFld      ,0,0,GTK_LEAF)             // local
 //-----------------------------------------------------------------------------
 
 GTNODE(CATCH_ARG        , GenTree            ,0,0,GTK_LEAF)             // Exception object in a catch block
+GTNODE(ASYNC_CONTINUATION, GenTree            ,0,0,GTK_LEAF)             // Returned continuation by an async2 call
 GTNODE(LABEL            , GenTree            ,0,0,GTK_LEAF)             // Jump-target
 GTNODE(JMP              , GenTreeVal         ,0,0,GTK_LEAF|GTK_NOVALUE) // Jump to another function
 GTNODE(FTN_ADDR         , GenTreeFptrVal     ,0,0,GTK_LEAF)             // Address of a function
@@ -279,6 +280,8 @@ GTNODE(RETURN           , GenTreeOp          ,0,1,GTK_UNOP|GTK_NOVALUE)
 GTNODE(SWITCH           , GenTreeOp          ,0,1,GTK_UNOP|GTK_NOVALUE)
 GTNODE(NO_OP            , GenTree            ,0,0,GTK_LEAF|GTK_NOVALUE) // A NOP that cannot be deleted.
 
+GTNODE(RETURN_SUSPEND   , GenTreeOp          ,0,1,GTK_UNOP|GTK_NOVALUE) // Return a continuation in an async2 method
+
 GTNODE(START_NONGC      , GenTree            ,0,0,GTK_LEAF|GTK_NOVALUE|DBK_NOTHIR) // Starts a new instruction group that will be non-gc interruptible.
 GTNODE(START_PREEMPTGC  , GenTree            ,0,0,GTK_LEAF|GTK_NOVALUE|DBK_NOTHIR) // Starts a new instruction group where preemptive GC is enabled.
 GTNODE(PROF_HOOK        , GenTree            ,0,0,GTK_LEAF|GTK_NOVALUE|DBK_NOTHIR) // Profiler Enter/Leave/TailCall hook.
diff --git a/src/coreclr/jit/importer.cpp b/src/coreclr/jit/importer.cpp
index 0b24cbda69bbae..28a826da7761a2 100644
--- a/src/coreclr/jit/importer.cpp
+++ b/src/coreclr/jit/importer.cpp
@@ -12870,6 +12870,7 @@ void Compiler::impInlineInitVars(InlineInfo* pInlineInfo)
                 break;
             case WellKnownArg::RetBuffer:
             case WellKnownArg::InstParam:
+            case WellKnownArg::AsyncContinuation:
                 // These do not appear in the table of inline arg info; do not include them
                 continue;
             default:
diff --git a/src/coreclr/jit/importercalls.cpp b/src/coreclr/jit/importercalls.cpp
index 6006a48a86f2af..15f2c58159f594 100644
--- a/src/coreclr/jit/importercalls.cpp
+++ b/src/coreclr/jit/importercalls.cpp
@@ -96,7 +96,8 @@ var_types Compiler::impImportCall(OPCODE                  opcode,
     bool bIntrinsicImported = false;
 
     CORINFO_SIG_INFO calliSig;
-    NewCallArg       extraArg;
+    GenTree*         varArgsCookie = nullptr;
+    GenTree*         instParam     = nullptr;
 
     /*-------------------------------------------------------------------------
      * First create the call node
@@ -715,12 +716,10 @@ var_types Compiler::impImportCall(OPCODE                  opcode,
         }
     }
 
-    /*-------------------------------------------------------------------------
-     * Create the argument list
-     */
+    // Now create the argument list.
 
     //-------------------------------------------------------------------------
-    // Special case - for varargs we have an implicit last argument
+    // Special case - for varargs we have an extra argument
 
     if ((sig->callConv & CORINFO_CALLCONV_MASK) == CORINFO_CALLCONV_VARARG)
     {
@@ -735,9 +734,7 @@ var_types Compiler::impImportCall(OPCODE                  opcode,
 
         varCookie = info.compCompHnd->getVarArgsHandle(sig, &pVarCookie);
         assert((!varCookie) != (!pVarCookie));
-        GenTree* cookieNode = gtNewIconEmbHndNode(varCookie, pVarCookie, GTF_ICON_VARG_HDL, sig);
-        assert(extraArg.Node == nullptr);
-        extraArg = NewCallArg::Primitive(cookieNode).WellKnown(WellKnownArg::VarArgsCookie);
+        varArgsCookie = gtNewIconEmbHndNode(varCookie, pVarCookie, GTF_ICON_VARG_HDL, sig);
     }
 
     //-------------------------------------------------------------------------
@@ -757,7 +754,7 @@ var_types Compiler::impImportCall(OPCODE                  opcode,
     // We also set the exact type context associated with the call so we can
     // inline the call correctly later on.
 
-    if (sig->callConv & CORINFO_CALLCONV_PARAMTYPE)
+    if (sig->hasTypeArg())
     {
         assert(call->AsCall()->gtCallType == CT_USER_FUNC);
         if (clsHnd == nullptr)
@@ -767,8 +764,7 @@ var_types Compiler::impImportCall(OPCODE                  opcode,
 
         assert(opcode != CEE_CALLI);
 
-        GenTree* instParam;
-        bool     runtimeLookup;
+        bool runtimeLookup;
 
         // Instantiated generic method
         if (((SIZE_T)exactContextHnd & CORINFO_CONTEXTFLAGS_MASK) == CORINFO_CONTEXTFLAGS_METHOD)
@@ -858,9 +854,6 @@ var_types Compiler::impImportCall(OPCODE                  opcode,
                 }
             }
         }
-
-        assert(extraArg.Node == nullptr);
-        extraArg = NewCallArg::Primitive(instParam).WellKnown(WellKnownArg::InstParam);
     }
 
     if ((opcode == CEE_NEWOBJ) && ((clsFlags & CORINFO_FLG_DELEGATE) != 0))
@@ -882,18 +875,50 @@ var_types Compiler::impImportCall(OPCODE                  opcode,
     // The main group of arguments
 
     impPopCallArgs(sig, call->AsCall());
-    if (extraArg.Node != nullptr)
+
+    // Extra args
+    if ((instParam != nullptr) || sig->isAsyncCall() || (varArgsCookie != nullptr))
     {
         if (Target::g_tgtArgOrder == Target::ARG_ORDER_R2L)
         {
-            call->AsCall()->gtArgs.PushFront(this, extraArg);
+            if (varArgsCookie != nullptr)
+            {
+                call->AsCall()->gtArgs.PushFront(this, NewCallArg::Primitive(varArgsCookie, TYP_REF)
+                                                           .WellKnown(WellKnownArg::VarArgsCookie));
+            }
+
+            if (compIsAsync2StateMachine() && sig->isAsyncCall())
+            {
+                call->AsCall()->gtArgs.PushFront(this, NewCallArg::Primitive(gtNewNull(), TYP_REF)
+                                                           .WellKnown(WellKnownArg::AsyncContinuation));
+            }
+
+            if (instParam != nullptr)
+            {
+                call->AsCall()->gtArgs.PushFront(this,
+                                                 NewCallArg::Primitive(instParam).WellKnown(WellKnownArg::InstParam));
+            }
         }
         else
         {
-            call->AsCall()->gtArgs.PushBack(this, extraArg);
-        }
+            if (instParam != nullptr)
+            {
+                call->AsCall()->gtArgs.PushBack(this,
+                                                NewCallArg::Primitive(instParam).WellKnown(WellKnownArg::InstParam));
+            }
 
-        call->gtFlags |= extraArg.Node->gtFlags & GTF_GLOB_EFFECT;
+            if (compIsAsync2StateMachine() && sig->isAsyncCall())
+            {
+                call->AsCall()->gtArgs.PushBack(this, NewCallArg::Primitive(gtNewNull(), TYP_REF)
+                                                          .WellKnown(WellKnownArg::AsyncContinuation));
+            }
+
+            if (varArgsCookie != nullptr)
+            {
+                call->AsCall()->gtArgs.PushBack(this, NewCallArg::Primitive(varArgsCookie, TYP_REF)
+                                                          .WellKnown(WellKnownArg::VarArgsCookie));
+            }
+        }
     }
 
     //-------------------------------------------------------------------------
@@ -2536,6 +2561,13 @@ GenTree* Compiler::impIntrinsic(GenTree*                newobjThis,
         return new (this, GT_LABEL) GenTree(GT_LABEL, TYP_I_IMPL);
     }
 
+    if (ni == NI_System_StubHelpers_Async2CallContinuation)
+    {
+        GenTree* node = new (this, GT_ASYNC_CONTINUATION) GenTree(GT_ASYNC_CONTINUATION, TYP_REF);
+        node->SetHasOrderingSideEffect();
+        return node;
+    }
+
     bool betterToExpand = false;
 
     // Allow some lighweight intrinsics in Tier0 which can improve throughput
@@ -9173,6 +9205,10 @@ NamedIntrinsic Compiler::lookupNamedIntrinsic(CORINFO_METHOD_HANDLE method)
                     {
                         result = NI_System_StubHelpers_NextCallReturnAddress;
                     }
+                    else if (strcmp(methodName, "Async2CallContinuation") == 0)
+                    {
+                        result = NI_System_StubHelpers_Async2CallContinuation;
+                    }
                 }
             }
             else if (strcmp(namespaceName, "Text") == 0)
diff --git a/src/coreclr/jit/jitconfigvalues.h b/src/coreclr/jit/jitconfigvalues.h
index 8483bf25bc4465..1cdd96279a9deb 100644
--- a/src/coreclr/jit/jitconfigvalues.h
+++ b/src/coreclr/jit/jitconfigvalues.h
@@ -653,6 +653,9 @@ CONFIG_INTEGER(JitEnableHeadTailMerge, W("JitEnableHeadTailMerge"), 1)
 // Enable physical promotion
 CONFIG_INTEGER(JitEnablePhysicalPromotion, W("JitEnablePhysicalPromotion"), 1)
 
+// Should JIT generate state machines for async2 methods?
+CONFIG_INTEGER(RuntimeAsyncViaJitGeneratedStateMachines, W("RuntimeAsyncViaJitGeneratedStateMachines"), 0)
+
 #if defined(DEBUG)
 // JitFunctionFile: Name of a file that contains a list of functions. If the currently compiled function is in the
 // file, certain other JIT config variables will be active. If the currently compiled function is not in the file,
diff --git a/src/coreclr/jit/jitee.h b/src/coreclr/jit/jitee.h
index 27963ac356efb5..525d7960ade0da 100644
--- a/src/coreclr/jit/jitee.h
+++ b/src/coreclr/jit/jitee.h
@@ -47,6 +47,7 @@ class JitFlags
 #if defined(TARGET_XARCH)
         JIT_FLAG_VECTOR512_THROTTLING    = 31, // On Xarch, 512-bit vector usage may incur CPU frequency throttling
 #endif
+        JIT_FLAG_RUNTIMEASYNCFUNCTION    = 32, // Generate code for use as an async2 function
 
         // Note: the mcs tool uses the currently unused upper flags bits when outputting SuperPMI MC file flags.
         // See EXTRA_JIT_FLAGS and spmidumphelper.cpp. Currently, these are bits 56 through 63. If they overlap,
diff --git a/src/coreclr/jit/lclvars.cpp b/src/coreclr/jit/lclvars.cpp
index adf993d37b70c5..95289c66beff6b 100644
--- a/src/coreclr/jit/lclvars.cpp
+++ b/src/coreclr/jit/lclvars.cpp
@@ -63,11 +63,12 @@ void Compiler::lvaInit()
 #ifdef TARGET_X86
     lvaVarargsBaseOfStkArgs = BAD_VAR_NUM;
 #endif // TARGET_X86
-    lvaVarargsHandleArg = BAD_VAR_NUM;
-    lvaStubArgumentVar  = BAD_VAR_NUM;
-    lvaArg0Var          = BAD_VAR_NUM;
-    lvaMonAcquired      = BAD_VAR_NUM;
-    lvaRetAddrVar       = BAD_VAR_NUM;
+    lvaVarargsHandleArg     = BAD_VAR_NUM;
+    lvaStubArgumentVar      = BAD_VAR_NUM;
+    lvaArg0Var              = BAD_VAR_NUM;
+    lvaMonAcquired          = BAD_VAR_NUM;
+    lvaRetAddrVar           = BAD_VAR_NUM;
+    lvaAsyncContinuationArg = BAD_VAR_NUM;
 
     lvaInlineeReturnSpillTemp = BAD_VAR_NUM;
 
@@ -97,19 +98,19 @@ void Compiler::lvaInitTypeRef()
 {
 
     /* x86 args look something like this:
-        [this ptr] [hidden return buffer] [declared arguments]* [generic context] [var arg cookie]
+        [this ptr] [hidden return buffer] [declared arguments]* [generic context] [async continuation] [var arg cookie]
 
        x64 is closer to the native ABI:
-        [this ptr] [hidden return buffer] [generic context] [var arg cookie] [declared arguments]*
+        [this ptr] [hidden return buffer] [generic context] [async continuation] [var arg cookie] [declared arguments]*
         (Note: prior to .NET Framework 4.5.1 for Windows 8.1 (but not .NET Framework 4.5.1 "downlevel"),
         the "hidden return buffer" came before the "this ptr". Now, the "this ptr" comes first. This
         is different from the C++ order, where the "hidden return buffer" always comes first.)
 
        ARM and ARM64 are the same as the current x64 convention:
-        [this ptr] [hidden return buffer] [generic context] [var arg cookie] [declared arguments]*
+        [this ptr] [hidden return buffer] [generic context] [async continuation] [var arg cookie] [declared arguments]*
 
        Key difference:
-           The var arg cookie and generic context are swapped with respect to the user arguments
+           The var arg cookie, generic context and async continuations are swapped with respect to the user arguments
     */
 
     /* Set compArgsCount and compLocalsCount */
@@ -184,6 +185,11 @@ void Compiler::lvaInitTypeRef()
         info.compTypeCtxtArg = BAD_VAR_NUM;
     }
 
+    if (compIsAsync2StateMachine())
+    {
+        info.compArgsCount++;
+    }
+
     lvaCount = info.compLocalsCount = info.compArgsCount + info.compMethodInfo->locals.numArgs;
 
     info.compILlocalsCount = info.compILargsCount + info.compMethodInfo->locals.numArgs;
@@ -419,6 +425,8 @@ void Compiler::lvaInitArgs(InitVarDscInfo* varDscInfo)
     // and shared generic struct instance methods
     lvaInitGenericsCtxt(varDscInfo);
 
+    lvaInitAsyncContinuation(varDscInfo);
+
     /* If the method is varargs, process the varargs cookie */
     lvaInitVarArgsHandle(varDscInfo);
 #endif
@@ -432,6 +440,8 @@ void Compiler::lvaInitArgs(InitVarDscInfo* varDscInfo)
     // and shared generic struct instance methods
     lvaInitGenericsCtxt(varDscInfo);
 
+    lvaInitAsyncContinuation(varDscInfo);
+
     /* If the method is varargs, process the varargs cookie */
     lvaInitVarArgsHandle(varDscInfo);
 #endif
@@ -1354,6 +1364,58 @@ void Compiler::lvaInitGenericsCtxt(InitVarDscInfo* varDscInfo)
     }
 }
 
+void Compiler::lvaInitAsyncContinuation(InitVarDscInfo* varDscInfo)
+{
+    if (!compIsAsync2StateMachine())
+    {
+        return;
+    }
+
+    lvaAsyncContinuationArg = varDscInfo->varNum;
+    LclVarDsc* varDsc       = varDscInfo->varDsc;
+    varDsc->lvType          = TYP_REF;
+    varDsc->lvIsParam       = 1;
+
+#ifdef DEBUG
+    varDsc->lvReason = "Async continuation arg";
+#endif
+
+    if (varDscInfo->canEnreg(TYP_REF))
+    {
+        // Passed in register
+
+        varDsc->lvIsRegArg = 1;
+        varDsc->SetArgReg(genMapRegArgNumToRegNum(varDscInfo->regArgNum(TYP_INT), varDsc->TypeGet()));
+#if FEATURE_MULTIREG_ARGS
+        varDsc->SetOtherArgReg(REG_NA);
+#endif
+        varDsc->lvOnFrame = true; // The final home for this incoming register might be our local stack frame
+
+        varDscInfo->intRegArgNum++;
+
+        JITDUMP("'AsyncContinuation' passed in register %s\n", getRegName(varDsc->GetArgReg()));
+    }
+    else
+    {
+// We need to mark these as being on the stack, as this is not done elsewhere in the case that canEnreg
+// returns false.
+#if FEATURE_FASTTAILCALL
+        varDsc->SetStackOffset(varDscInfo->stackArgSize);
+        varDscInfo->stackArgSize += TARGET_POINTER_SIZE;
+#endif // FEATURE_FASTTAILCALL
+    }
+
+    compArgSize += TARGET_POINTER_SIZE;
+
+#if defined(TARGET_X86)
+    if (info.compIsVarArgs)
+        varDsc->SetStackOffset(compArgSize);
+#endif // TARGET_X86
+
+    varDscInfo->varNum++;
+    varDscInfo->varDsc++;
+}
+
 /*****************************************************************************/
 void Compiler::lvaInitVarArgsHandle(InitVarDscInfo* varDscInfo)
 {
@@ -5503,6 +5565,13 @@ void Compiler::lvaAssignVirtualFrameOffsetsToArgs()
                                                    argOffs UNIX_AMD64_ABI_ONLY_ARG(&callerArgOffset));
     }
 
+    if (compIsAsync2StateMachine())
+    {
+        noway_assert(lclNum == lvaAsyncContinuationArg);
+        argOffs = lvaAssignVirtualFrameOffsetToArg(lclNum++, REGSIZE_BYTES,
+                                                   argOffs UNIX_AMD64_ABI_ONLY_ARG(&callerArgOffset));
+    }
+
     if (info.compIsVarArgs)
     {
         argOffs = lvaAssignVirtualFrameOffsetToArg(lclNum++, REGSIZE_BYTES,
@@ -5612,6 +5681,13 @@ void Compiler::lvaAssignVirtualFrameOffsetsToArgs()
                                                    argOffs UNIX_AMD64_ABI_ONLY_ARG(&callerArgOffset));
     }
 
+    if (compIsAsync2StateMachine())
+    {
+        noway_assert(lclNum == lvaAsyncContinuationArg);
+        argOffs = lvaAssignVirtualFrameOffsetToArg(lclNum++, REGSIZE_BYTES,
+                                                   argOffs UNIX_AMD64_ABI_ONLY_ARG(&callerArgOffset));
+    }
+
     if (info.compIsVarArgs)
     {
         argOffs = lvaAssignVirtualFrameOffsetToArg(lclNum++, REGSIZE_BYTES,
diff --git a/src/coreclr/jit/lir.h b/src/coreclr/jit/lir.h
index 9b4f940bc0ae38..d2c23bc0bca891 100644
--- a/src/coreclr/jit/lir.h
+++ b/src/coreclr/jit/lir.h
@@ -280,6 +280,20 @@ class LIR final
         void InsertAtBeginning(Range&& range);
         void InsertAtEnd(Range&& range);
 
+        template <typename... Trees>
+        void InsertAtBeginning(GenTree* tree, Trees&&... rest)
+        {
+            InsertAtBeginning(std::forward<Trees>(rest)...);
+            InsertAtBeginning(tree);
+        }
+
+        template <typename... Trees>
+        void InsertAtEnd(GenTree* tree, Trees&&... rest)
+        {
+            InsertAtEnd(tree);
+            InsertAtEnd(std::forward<Trees>(rest)...);
+        }
+
         void Remove(GenTree* node, bool markOperandsUnused = false);
         Range Remove(GenTree* firstNode, GenTree* lastNode);
         Range Remove(ReadOnlyRange&& range);
diff --git a/src/coreclr/jit/liveness.cpp b/src/coreclr/jit/liveness.cpp
index 3c914b2875f689..aefdcbdc57e6c7 100644
--- a/src/coreclr/jit/liveness.cpp
+++ b/src/coreclr/jit/liveness.cpp
@@ -2025,6 +2025,7 @@ void Compiler::fgComputeLifeLIR(VARSET_TP& life, BasicBlock* block, VARSET_VALAR
             case GT_JCC:
             case GT_JTRUE:
             case GT_RETURN:
+            case GT_RETURN_SUSPEND:
             case GT_SWITCH:
             case GT_RETFILT:
             case GT_START_NONGC:
diff --git a/src/coreclr/jit/lsraxarch.cpp b/src/coreclr/jit/lsraxarch.cpp
index 3257b3c04b216b..92fe1d29157829 100644
--- a/src/coreclr/jit/lsraxarch.cpp
+++ b/src/coreclr/jit/lsraxarch.cpp
@@ -578,6 +578,12 @@ int LinearScan::BuildNode(GenTree* tree)
             BuildDef(tree, RBM_EXCEPTION_OBJECT);
             break;
 
+        case GT_ASYNC_CONTINUATION:
+            srcCount = 0;
+            assert(dstCount == 1);
+            BuildDef(tree, RBM_ASYNC_CONTINUATION_RET);
+            break;
+
 #if !defined(FEATURE_EH_FUNCLETS)
         case GT_END_LFIN:
             srcCount = 0;
diff --git a/src/coreclr/jit/morph.cpp b/src/coreclr/jit/morph.cpp
index 85539583800a6a..1c4c78b3f8bf62 100644
--- a/src/coreclr/jit/morph.cpp
+++ b/src/coreclr/jit/morph.cpp
@@ -741,6 +741,8 @@ const char* getWellKnownArgName(WellKnownArg arg)
             return "VarArgsCookie";
         case WellKnownArg::InstParam:
             return "InstParam";
+        case WellKnownArg::AsyncContinuation:
+            return "AsyncContinuation";
         case WellKnownArg::RetBuffer:
             return "RetBuffer";
         case WellKnownArg::PInvokeFrame:
diff --git a/src/coreclr/jit/namedintrinsiclist.h b/src/coreclr/jit/namedintrinsiclist.h
index 0973a45571f498..cd91d7413e09e6 100644
--- a/src/coreclr/jit/namedintrinsiclist.h
+++ b/src/coreclr/jit/namedintrinsiclist.h
@@ -93,6 +93,7 @@ enum NamedIntrinsic : unsigned short
     NI_System_RuntimeType_get_TypeHandle,
     NI_System_StubHelpers_GetStubContext,
     NI_System_StubHelpers_NextCallReturnAddress,
+    NI_System_StubHelpers_Async2CallContinuation,
 
     NI_Array_Address,
     NI_Array_Get,
diff --git a/src/coreclr/jit/targetamd64.h b/src/coreclr/jit/targetamd64.h
index 4abe71984b57cf..dc4b957b4062ec 100644
--- a/src/coreclr/jit/targetamd64.h
+++ b/src/coreclr/jit/targetamd64.h
@@ -539,6 +539,9 @@
   #define REG_VALIDATE_INDIRECT_CALL_ADDR REG_RCX
   #define REG_DISPATCH_INDIRECT_CALL_ADDR REG_RAX
 
+  #define REG_ASYNC_CONTINUATION_RET REG_RCX
+  #define RBM_ASYNC_CONTINUATION_RET RBM_RCX
+
   // What sort of reloc do we use for [disp32] address mode
   #define IMAGE_REL_BASED_DISP32   IMAGE_REL_BASED_REL32
 
diff --git a/src/coreclr/jit/targetarm.h b/src/coreclr/jit/targetarm.h
index 3fa00cacbf4700..f6421f989c47d4 100644
--- a/src/coreclr/jit/targetarm.h
+++ b/src/coreclr/jit/targetarm.h
@@ -247,6 +247,9 @@
   #define RBM_VALIDATE_INDIRECT_CALL_TRASH (RBM_INT_CALLEE_TRASH)
   #define REG_VALIDATE_INDIRECT_CALL_ADDR REG_R0
 
+  #define REG_ASYNC_CONTINUATION_RET REG_R3
+  #define RBM_ASYNC_CONTINUATION_RET RBM_R3
+
   #define REG_FPBASE               REG_R11
   #define RBM_FPBASE               RBM_R11
   #define STR_FPBASE               "r11"
diff --git a/src/coreclr/jit/targetarm64.h b/src/coreclr/jit/targetarm64.h
index a454b47608bb37..410019b8cca682 100644
--- a/src/coreclr/jit/targetarm64.h
+++ b/src/coreclr/jit/targetarm64.h
@@ -256,6 +256,9 @@
   #define REG_VALIDATE_INDIRECT_CALL_ADDR REG_R15
   #define REG_DISPATCH_INDIRECT_CALL_ADDR REG_R9
 
+  #define REG_ASYNC_CONTINUATION_RET REG_R8
+  #define RBM_ASYNC_CONTINUATION_RET RBM_R8
+
   #define REG_FPBASE               REG_FP
   #define RBM_FPBASE               RBM_FP
   #define STR_FPBASE               "fp"
diff --git a/src/coreclr/jit/targetx86.h b/src/coreclr/jit/targetx86.h
index 60b2f7793f435b..e8c412bd6d5d03 100644
--- a/src/coreclr/jit/targetx86.h
+++ b/src/coreclr/jit/targetx86.h
@@ -300,6 +300,9 @@
   #define RBM_VALIDATE_INDIRECT_CALL_TRASH (RBM_INT_CALLEE_TRASH & ~RBM_ECX)
   #define REG_VALIDATE_INDIRECT_CALL_ADDR REG_ECX
 
+  #define REG_ASYNC_CONTINUATION_RET REG_ECX
+  #define RBM_ASYNC_CONTINUATION_RET RBM_ECX
+
   #define REG_FPBASE               REG_EBP
   #define RBM_FPBASE               RBM_EBP
   #define STR_FPBASE               "ebp"
diff --git a/src/coreclr/jit/valuenum.cpp b/src/coreclr/jit/valuenum.cpp
index aea80a8ca48edd..cfe3793c895adf 100644
--- a/src/coreclr/jit/valuenum.cpp
+++ b/src/coreclr/jit/valuenum.cpp
@@ -9375,7 +9375,8 @@ static genTreeOps genTreeOpsIllegalAsVNFunc[] = {GT_IND, // When we do heap memo
                                                  GT_BITCAST,           // Needs to encode the target type.
 
                                                  // These control-flow operations need no values.
-                                                 GT_JTRUE, GT_RETURN, GT_SWITCH, GT_RETFILT, GT_CKFINITE};
+                                                 GT_JTRUE, GT_RETURN, GT_RETURN_SUSPEND, GT_SWITCH, GT_RETFILT,
+                                                 GT_CKFINITE};
 
 void ValueNumStore::ValidateValueNumStoreStatics()
 {
@@ -11226,7 +11227,8 @@ void Compiler::fgValueNumberTree(GenTree* tree)
             break;
 
             case GT_CATCH_ARG:
-                // We know nothing about the value of a caught expression.
+            case GT_ASYNC_CONTINUATION:
+                // We know nothing about the value of these.
                 tree->gtVNPair.SetBoth(vnStore->VNForExpr(compCurBB, tree->TypeGet()));
                 break;
 
diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoHelpFunc.cs b/src/coreclr/tools/Common/JitInterface/CorInfoHelpFunc.cs
index 7837e8c24f832f..74b0f72798d6de 100644
--- a/src/coreclr/tools/Common/JitInterface/CorInfoHelpFunc.cs
+++ b/src/coreclr/tools/Common/JitInterface/CorInfoHelpFunc.cs
@@ -304,6 +304,8 @@ which is the right helper to use to allocate an object of a given type. */
         CORINFO_HELP_VALIDATE_INDIRECT_CALL,    // CFG: Validate function pointer
         CORINFO_HELP_DISPATCH_INDIRECT_CALL,    // CFG: Validate and dispatch to pointer
 
+        CORINFO_HELP_ALLOC_CONTINUATION,
+
         CORINFO_HELP_COUNT,
     }
 }
diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs
index 9d98650b91bbb9..4a26f5ccf2fcc5 100644
--- a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs
+++ b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs
@@ -2722,6 +2722,9 @@ private CorInfoInitClassResult initClass(CORINFO_FIELD_STRUCT_* field, CORINFO_M
                 case CorInfoClassId.CLASSID_SYSTEM_OBJECT:
                     return ObjectToHandle(_compilation.TypeSystemContext.GetWellKnownType(WellKnownType.Object));
 
+                case CorInfoClassId.CLASSID_SYSTEM_BYTE:
+                    return ObjectToHandle(_compilation.TypeSystemContext.GetWellKnownType(WellKnownType.Byte));
+
                 case CorInfoClassId.CLASSID_TYPED_BYREF:
                     return ObjectToHandle(_compilation.TypeSystemContext.GetWellKnownType(WellKnownType.TypedReference));
 
@@ -3243,6 +3246,11 @@ private void getEEInfo(ref CORINFO_EE_INFO pEEInfoOut)
             pEEInfoOut.osType = TargetToOs(_compilation.NodeFactory.Target);
         }
 
+        private void getAsync2Info(ref CORINFO_ASYNC2_INFO pAsync2InfoOut)
+        {
+            throw new NotImplementedException();
+        }
+
 #pragma warning disable CA1822 // Mark members as static
         private char* getJitTimeLogFilename()
 #pragma warning restore CA1822 // Mark members as static
@@ -3542,6 +3550,13 @@ private bool getTailCallHelpers(ref CORINFO_RESOLVED_TOKEN callToken, CORINFO_SI
 #endif
         }
 
+#pragma warning disable CA1822 // Mark members as static
+        private CORINFO_METHOD_STRUCT_* getAsyncResumptionStub()
+#pragma warning restore CA1822 // Mark members as static
+        {
+            return null;
+        }
+
         private byte[] _code;
         private byte[] _coldCode;
         private int _codeAlignment;
diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoImpl_generated.cs b/src/coreclr/tools/Common/JitInterface/CorInfoImpl_generated.cs
index 8d467b5880d9bc..04499066324aa0 100644
--- a/src/coreclr/tools/Common/JitInterface/CorInfoImpl_generated.cs
+++ b/src/coreclr/tools/Common/JitInterface/CorInfoImpl_generated.cs
@@ -1666,6 +1666,20 @@ private static void _getEEInfo(IntPtr thisHandle, IntPtr* ppException, CORINFO_E
             }
         }
 
+        [UnmanagedCallersOnly]
+        private static void _getAsync2Info(IntPtr thisHandle, IntPtr* ppException, CORINFO_ASYNC2_INFO* pAsync2InfoOut)
+        {
+            var _this = GetThis(thisHandle);
+            try
+            {
+                _this.getAsync2Info(ref *pAsync2InfoOut);
+            }
+            catch (Exception ex)
+            {
+                *ppException = _this.AllocException(ex);
+            }
+        }
+
         [UnmanagedCallersOnly]
         private static char* _getJitTimeLogFilename(IntPtr thisHandle, IntPtr* ppException)
         {
@@ -2243,6 +2257,21 @@ private static byte _getTailCallHelpers(IntPtr thisHandle, IntPtr* ppException,
             }
         }
 
+        [UnmanagedCallersOnly]
+        private static CORINFO_METHOD_STRUCT_* _getAsyncResumptionStub(IntPtr thisHandle, IntPtr* ppException)
+        {
+            var _this = GetThis(thisHandle);
+            try
+            {
+                return _this.getAsyncResumptionStub();
+            }
+            catch (Exception ex)
+            {
+                *ppException = _this.AllocException(ex);
+                return default;
+            }
+        }
+
         [UnmanagedCallersOnly]
         private static byte _convertPInvokeCalliToCall(IntPtr thisHandle, IntPtr* ppException, CORINFO_RESOLVED_TOKEN* pResolvedToken, byte mustConvert)
         {
@@ -2522,7 +2551,7 @@ private static uint _getJitFlags(IntPtr thisHandle, IntPtr* ppException, CORJIT_
 
         private static IntPtr GetUnmanagedCallbacks()
         {
-            void** callbacks = (void**)Marshal.AllocCoTaskMem(sizeof(IntPtr) * 170);
+            void** callbacks = (void**)Marshal.AllocCoTaskMem(sizeof(IntPtr) * 172);
 
             callbacks[0] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_METHOD_STRUCT_*, byte>)&_isIntrinsic;
             callbacks[1] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_METHOD_STRUCT_*, uint>)&_getMethodAttribs;
@@ -2636,64 +2665,66 @@ private static IntPtr GetUnmanagedCallbacks()
             callbacks[109] = (delegate* unmanaged<IntPtr, IntPtr*, void*, void*, byte>)&_runWithErrorTrap;
             callbacks[110] = (delegate* unmanaged<IntPtr, IntPtr*, void*, void*, byte>)&_runWithSPMIErrorTrap;
             callbacks[111] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_EE_INFO*, void>)&_getEEInfo;
-            callbacks[112] = (delegate* unmanaged<IntPtr, IntPtr*, char*>)&_getJitTimeLogFilename;
-            callbacks[113] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_METHOD_STRUCT_*, mdToken>)&_getMethodDefFromMethod;
-            callbacks[114] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_METHOD_STRUCT_*, byte*, UIntPtr, UIntPtr*, UIntPtr>)&_printMethodName;
-            callbacks[115] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_METHOD_STRUCT_*, byte**, byte**, byte**, byte*>)&_getMethodNameFromMetadata;
-            callbacks[116] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_METHOD_STRUCT_*, uint>)&_getMethodHash;
-            callbacks[117] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_CLASS_STRUCT_*, SYSTEMV_AMD64_CORINFO_STRUCT_REG_PASSING_DESCRIPTOR*, byte>)&_getSystemVAmd64PassStructInRegisterDescriptor;
-            callbacks[118] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_CLASS_STRUCT_*, uint>)&_getLoongArch64PassStructInRegisterFlags;
-            callbacks[119] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_CLASS_STRUCT_*, uint>)&_getRISCV64PassStructInRegisterFlags;
-            callbacks[120] = (delegate* unmanaged<IntPtr, IntPtr*, void**, uint>)&_getThreadTLSIndex;
-            callbacks[121] = (delegate* unmanaged<IntPtr, IntPtr*, void**, int*>)&_getAddrOfCaptureThreadGlobal;
-            callbacks[122] = (delegate* unmanaged<IntPtr, IntPtr*, CorInfoHelpFunc, void**, void*>)&_getHelperFtn;
-            callbacks[123] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_METHOD_STRUCT_*, CORINFO_CONST_LOOKUP*, CORINFO_ACCESS_FLAGS, void>)&_getFunctionEntryPoint;
-            callbacks[124] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_METHOD_STRUCT_*, byte, CORINFO_CONST_LOOKUP*, void>)&_getFunctionFixedEntryPoint;
-            callbacks[125] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_METHOD_STRUCT_*, void**, void*>)&_getMethodSync;
-            callbacks[126] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_MODULE_STRUCT_*, CorInfoHelpFunc>)&_getLazyStringLiteralHelper;
-            callbacks[127] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_MODULE_STRUCT_*, void**, CORINFO_MODULE_STRUCT_*>)&_embedModuleHandle;
-            callbacks[128] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_CLASS_STRUCT_*, void**, CORINFO_CLASS_STRUCT_*>)&_embedClassHandle;
-            callbacks[129] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_METHOD_STRUCT_*, void**, CORINFO_METHOD_STRUCT_*>)&_embedMethodHandle;
-            callbacks[130] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_FIELD_STRUCT_*, void**, CORINFO_FIELD_STRUCT_*>)&_embedFieldHandle;
-            callbacks[131] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_RESOLVED_TOKEN*, byte, CORINFO_GENERICHANDLE_RESULT*, void>)&_embedGenericHandle;
-            callbacks[132] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_METHOD_STRUCT_*, CORINFO_LOOKUP_KIND*, void>)&_getLocationOfThisType;
-            callbacks[133] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_METHOD_STRUCT_*, CORINFO_CONST_LOOKUP*, void>)&_getAddressOfPInvokeTarget;
-            callbacks[134] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_SIG_INFO*, void**, void*>)&_GetCookieForPInvokeCalliSig;
-            callbacks[135] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_SIG_INFO*, byte>)&_canGetCookieForPInvokeCalliSig;
-            callbacks[136] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_METHOD_STRUCT_*, CORINFO_JUST_MY_CODE_HANDLE_**, CORINFO_JUST_MY_CODE_HANDLE_*>)&_getJustMyCodeHandle;
-            callbacks[137] = (delegate* unmanaged<IntPtr, IntPtr*, bool*, void**, bool*, void>)&_GetProfilingHandle;
-            callbacks[138] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_RESOLVED_TOKEN*, CORINFO_RESOLVED_TOKEN*, CORINFO_METHOD_STRUCT_*, CORINFO_CALLINFO_FLAGS, CORINFO_CALL_INFO*, void>)&_getCallInfo;
-            callbacks[139] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_CLASS_STRUCT_*, void**, uint>)&_getClassDomainID;
-            callbacks[140] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_FIELD_STRUCT_*, byte*, int, int, byte, byte>)&_getStaticFieldContent;
-            callbacks[141] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_OBJECT_STRUCT_*, byte*, int, int, byte>)&_getObjectContent;
-            callbacks[142] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_FIELD_STRUCT_*, byte*, CORINFO_CLASS_STRUCT_*>)&_getStaticFieldCurrentClass;
-            callbacks[143] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_SIG_INFO*, void**, IntPtr>)&_getVarArgsHandle;
-            callbacks[144] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_SIG_INFO*, byte>)&_canGetVarArgsHandle;
-            callbacks[145] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_MODULE_STRUCT_*, mdToken, void**, InfoAccessType>)&_constructStringLiteral;
-            callbacks[146] = (delegate* unmanaged<IntPtr, IntPtr*, void**, InfoAccessType>)&_emptyStringLiteral;
-            callbacks[147] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_FIELD_STRUCT_*, void**, uint>)&_getFieldThreadLocalStoreID;
-            callbacks[148] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_METHOD_STRUCT_*, CORINFO_CLASS_STRUCT_*, CORINFO_METHOD_STRUCT_*, DelegateCtorArgs*, CORINFO_METHOD_STRUCT_*>)&_GetDelegateCtor;
-            callbacks[149] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_METHOD_STRUCT_*, void>)&_MethodCompileComplete;
-            callbacks[150] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_RESOLVED_TOKEN*, CORINFO_SIG_INFO*, CORINFO_GET_TAILCALL_HELPERS_FLAGS, CORINFO_TAILCALL_HELPERS*, byte>)&_getTailCallHelpers;
-            callbacks[151] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_RESOLVED_TOKEN*, byte, byte>)&_convertPInvokeCalliToCall;
-            callbacks[152] = (delegate* unmanaged<IntPtr, IntPtr*, InstructionSet, byte, byte>)&_notifyInstructionSetUsage;
-            callbacks[153] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_CONST_LOOKUP*, void>)&_updateEntryPointForTailCall;
-            callbacks[154] = (delegate* unmanaged<IntPtr, IntPtr*, AllocMemArgs*, void>)&_allocMem;
-            callbacks[155] = (delegate* unmanaged<IntPtr, IntPtr*, byte, byte, uint, void>)&_reserveUnwindInfo;
-            callbacks[156] = (delegate* unmanaged<IntPtr, IntPtr*, byte*, byte*, uint, uint, uint, byte*, CorJitFuncKind, void>)&_allocUnwindInfo;
-            callbacks[157] = (delegate* unmanaged<IntPtr, IntPtr*, UIntPtr, void*>)&_allocGCInfo;
-            callbacks[158] = (delegate* unmanaged<IntPtr, IntPtr*, uint, void>)&_setEHcount;
-            callbacks[159] = (delegate* unmanaged<IntPtr, IntPtr*, uint, CORINFO_EH_CLAUSE*, void>)&_setEHinfo;
-            callbacks[160] = (delegate* unmanaged<IntPtr, IntPtr*, uint, byte*, IntPtr, byte>)&_logMsg;
-            callbacks[161] = (delegate* unmanaged<IntPtr, IntPtr*, byte*, int, byte*, int>)&_doAssert;
-            callbacks[162] = (delegate* unmanaged<IntPtr, IntPtr*, CorJitResult, void>)&_reportFatalError;
-            callbacks[163] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_METHOD_STRUCT_*, PgoInstrumentationSchema**, uint*, byte**, PgoSource*, HRESULT>)&_getPgoInstrumentationResults;
-            callbacks[164] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_METHOD_STRUCT_*, PgoInstrumentationSchema*, uint, byte**, HRESULT>)&_allocPgoInstrumentationBySchema;
-            callbacks[165] = (delegate* unmanaged<IntPtr, IntPtr*, uint, CORINFO_SIG_INFO*, CORINFO_METHOD_STRUCT_*, void>)&_recordCallSite;
-            callbacks[166] = (delegate* unmanaged<IntPtr, IntPtr*, void*, void*, void*, ushort, int, void>)&_recordRelocation;
-            callbacks[167] = (delegate* unmanaged<IntPtr, IntPtr*, void*, ushort>)&_getRelocTypeHint;
-            callbacks[168] = (delegate* unmanaged<IntPtr, IntPtr*, uint>)&_getExpectedTargetArchitecture;
-            callbacks[169] = (delegate* unmanaged<IntPtr, IntPtr*, CORJIT_FLAGS*, uint, uint>)&_getJitFlags;
+            callbacks[112] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_ASYNC2_INFO*, void>)&_getAsync2Info;
+            callbacks[113] = (delegate* unmanaged<IntPtr, IntPtr*, char*>)&_getJitTimeLogFilename;
+            callbacks[114] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_METHOD_STRUCT_*, mdToken>)&_getMethodDefFromMethod;
+            callbacks[115] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_METHOD_STRUCT_*, byte*, UIntPtr, UIntPtr*, UIntPtr>)&_printMethodName;
+            callbacks[116] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_METHOD_STRUCT_*, byte**, byte**, byte**, byte*>)&_getMethodNameFromMetadata;
+            callbacks[117] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_METHOD_STRUCT_*, uint>)&_getMethodHash;
+            callbacks[118] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_CLASS_STRUCT_*, SYSTEMV_AMD64_CORINFO_STRUCT_REG_PASSING_DESCRIPTOR*, byte>)&_getSystemVAmd64PassStructInRegisterDescriptor;
+            callbacks[119] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_CLASS_STRUCT_*, uint>)&_getLoongArch64PassStructInRegisterFlags;
+            callbacks[120] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_CLASS_STRUCT_*, uint>)&_getRISCV64PassStructInRegisterFlags;
+            callbacks[121] = (delegate* unmanaged<IntPtr, IntPtr*, void**, uint>)&_getThreadTLSIndex;
+            callbacks[122] = (delegate* unmanaged<IntPtr, IntPtr*, void**, int*>)&_getAddrOfCaptureThreadGlobal;
+            callbacks[123] = (delegate* unmanaged<IntPtr, IntPtr*, CorInfoHelpFunc, void**, void*>)&_getHelperFtn;
+            callbacks[124] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_METHOD_STRUCT_*, CORINFO_CONST_LOOKUP*, CORINFO_ACCESS_FLAGS, void>)&_getFunctionEntryPoint;
+            callbacks[125] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_METHOD_STRUCT_*, byte, CORINFO_CONST_LOOKUP*, void>)&_getFunctionFixedEntryPoint;
+            callbacks[126] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_METHOD_STRUCT_*, void**, void*>)&_getMethodSync;
+            callbacks[127] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_MODULE_STRUCT_*, CorInfoHelpFunc>)&_getLazyStringLiteralHelper;
+            callbacks[128] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_MODULE_STRUCT_*, void**, CORINFO_MODULE_STRUCT_*>)&_embedModuleHandle;
+            callbacks[129] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_CLASS_STRUCT_*, void**, CORINFO_CLASS_STRUCT_*>)&_embedClassHandle;
+            callbacks[130] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_METHOD_STRUCT_*, void**, CORINFO_METHOD_STRUCT_*>)&_embedMethodHandle;
+            callbacks[131] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_FIELD_STRUCT_*, void**, CORINFO_FIELD_STRUCT_*>)&_embedFieldHandle;
+            callbacks[132] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_RESOLVED_TOKEN*, byte, CORINFO_GENERICHANDLE_RESULT*, void>)&_embedGenericHandle;
+            callbacks[133] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_METHOD_STRUCT_*, CORINFO_LOOKUP_KIND*, void>)&_getLocationOfThisType;
+            callbacks[134] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_METHOD_STRUCT_*, CORINFO_CONST_LOOKUP*, void>)&_getAddressOfPInvokeTarget;
+            callbacks[135] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_SIG_INFO*, void**, void*>)&_GetCookieForPInvokeCalliSig;
+            callbacks[136] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_SIG_INFO*, byte>)&_canGetCookieForPInvokeCalliSig;
+            callbacks[137] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_METHOD_STRUCT_*, CORINFO_JUST_MY_CODE_HANDLE_**, CORINFO_JUST_MY_CODE_HANDLE_*>)&_getJustMyCodeHandle;
+            callbacks[138] = (delegate* unmanaged<IntPtr, IntPtr*, bool*, void**, bool*, void>)&_GetProfilingHandle;
+            callbacks[139] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_RESOLVED_TOKEN*, CORINFO_RESOLVED_TOKEN*, CORINFO_METHOD_STRUCT_*, CORINFO_CALLINFO_FLAGS, CORINFO_CALL_INFO*, void>)&_getCallInfo;
+            callbacks[140] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_CLASS_STRUCT_*, void**, uint>)&_getClassDomainID;
+            callbacks[141] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_FIELD_STRUCT_*, byte*, int, int, byte, byte>)&_getStaticFieldContent;
+            callbacks[142] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_OBJECT_STRUCT_*, byte*, int, int, byte>)&_getObjectContent;
+            callbacks[143] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_FIELD_STRUCT_*, byte*, CORINFO_CLASS_STRUCT_*>)&_getStaticFieldCurrentClass;
+            callbacks[144] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_SIG_INFO*, void**, IntPtr>)&_getVarArgsHandle;
+            callbacks[145] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_SIG_INFO*, byte>)&_canGetVarArgsHandle;
+            callbacks[146] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_MODULE_STRUCT_*, mdToken, void**, InfoAccessType>)&_constructStringLiteral;
+            callbacks[147] = (delegate* unmanaged<IntPtr, IntPtr*, void**, InfoAccessType>)&_emptyStringLiteral;
+            callbacks[148] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_FIELD_STRUCT_*, void**, uint>)&_getFieldThreadLocalStoreID;
+            callbacks[149] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_METHOD_STRUCT_*, CORINFO_CLASS_STRUCT_*, CORINFO_METHOD_STRUCT_*, DelegateCtorArgs*, CORINFO_METHOD_STRUCT_*>)&_GetDelegateCtor;
+            callbacks[150] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_METHOD_STRUCT_*, void>)&_MethodCompileComplete;
+            callbacks[151] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_RESOLVED_TOKEN*, CORINFO_SIG_INFO*, CORINFO_GET_TAILCALL_HELPERS_FLAGS, CORINFO_TAILCALL_HELPERS*, byte>)&_getTailCallHelpers;
+            callbacks[152] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_METHOD_STRUCT_*>)&_getAsyncResumptionStub;
+            callbacks[153] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_RESOLVED_TOKEN*, byte, byte>)&_convertPInvokeCalliToCall;
+            callbacks[154] = (delegate* unmanaged<IntPtr, IntPtr*, InstructionSet, byte, byte>)&_notifyInstructionSetUsage;
+            callbacks[155] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_CONST_LOOKUP*, void>)&_updateEntryPointForTailCall;
+            callbacks[156] = (delegate* unmanaged<IntPtr, IntPtr*, AllocMemArgs*, void>)&_allocMem;
+            callbacks[157] = (delegate* unmanaged<IntPtr, IntPtr*, byte, byte, uint, void>)&_reserveUnwindInfo;
+            callbacks[158] = (delegate* unmanaged<IntPtr, IntPtr*, byte*, byte*, uint, uint, uint, byte*, CorJitFuncKind, void>)&_allocUnwindInfo;
+            callbacks[159] = (delegate* unmanaged<IntPtr, IntPtr*, UIntPtr, void*>)&_allocGCInfo;
+            callbacks[160] = (delegate* unmanaged<IntPtr, IntPtr*, uint, void>)&_setEHcount;
+            callbacks[161] = (delegate* unmanaged<IntPtr, IntPtr*, uint, CORINFO_EH_CLAUSE*, void>)&_setEHinfo;
+            callbacks[162] = (delegate* unmanaged<IntPtr, IntPtr*, uint, byte*, IntPtr, byte>)&_logMsg;
+            callbacks[163] = (delegate* unmanaged<IntPtr, IntPtr*, byte*, int, byte*, int>)&_doAssert;
+            callbacks[164] = (delegate* unmanaged<IntPtr, IntPtr*, CorJitResult, void>)&_reportFatalError;
+            callbacks[165] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_METHOD_STRUCT_*, PgoInstrumentationSchema**, uint*, byte**, PgoSource*, HRESULT>)&_getPgoInstrumentationResults;
+            callbacks[166] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_METHOD_STRUCT_*, PgoInstrumentationSchema*, uint, byte**, HRESULT>)&_allocPgoInstrumentationBySchema;
+            callbacks[167] = (delegate* unmanaged<IntPtr, IntPtr*, uint, CORINFO_SIG_INFO*, CORINFO_METHOD_STRUCT_*, void>)&_recordCallSite;
+            callbacks[168] = (delegate* unmanaged<IntPtr, IntPtr*, void*, void*, void*, ushort, int, void>)&_recordRelocation;
+            callbacks[169] = (delegate* unmanaged<IntPtr, IntPtr*, void*, ushort>)&_getRelocTypeHint;
+            callbacks[170] = (delegate* unmanaged<IntPtr, IntPtr*, uint>)&_getExpectedTargetArchitecture;
+            callbacks[171] = (delegate* unmanaged<IntPtr, IntPtr*, CORJIT_FLAGS*, uint, uint>)&_getJitFlags;
 
             return (IntPtr)callbacks;
         }
diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs b/src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs
index 47e7e6bb7dd710..f259748f8fea70 100644
--- a/src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs
+++ b/src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs
@@ -469,6 +469,7 @@ public enum CorInfoGCType
     public enum CorInfoClassId
     {
         CLASSID_SYSTEM_OBJECT,
+        CLASSID_SYSTEM_BYTE,
         CLASSID_TYPED_BYREF,
         CLASSID_TYPE_HANDLE,
         CLASSID_FIELD_HANDLE,
@@ -870,6 +871,18 @@ public struct InlinedCallFrameInfo
         public CORINFO_OS osType;
     }
 
+    public unsafe struct CORINFO_ASYNC2_INFO
+    {
+        // Class handle for System.Runtime.CompilerServices.Continuation
+        public CORINFO_CLASS_STRUCT_* continuationClsHnd;
+        // 'Next' field
+        public CORINFO_FIELD_STRUCT_* continuationNextFldHnd;
+        // 'Data' field
+        public CORINFO_FIELD_STRUCT_* continuationDataFldHnd;
+        // 'GCData' field
+        public CORINFO_FIELD_STRUCT_* continuationGCDataFldHnd;
+    }
+
     // Flags passed from JIT to runtime.
     public enum CORINFO_GET_TAILCALL_HELPERS_FLAGS
     {
diff --git a/src/coreclr/tools/Common/JitInterface/ThunkGenerator/ThunkInput.txt b/src/coreclr/tools/Common/JitInterface/ThunkGenerator/ThunkInput.txt
index bc620591465f48..0f99ca869de5c7 100644
--- a/src/coreclr/tools/Common/JitInterface/ThunkGenerator/ThunkInput.txt
+++ b/src/coreclr/tools/Common/JitInterface/ThunkGenerator/ThunkInput.txt
@@ -82,6 +82,7 @@ CORINFO_SIG_INFO*
 CORINFO_RESOLVED_TOKEN*,ref CORINFO_RESOLVED_TOKEN
 CORINFO_RESOLVED_TOKEN_PTR,CORINFO_RESOLVED_TOKEN*,CORINFO_RESOLVED_TOKEN*,CORINFO_RESOLVED_TOKEN*
 CORINFO_EE_INFO*,ref CORINFO_EE_INFO
+CORINFO_ASYNC2_INFO*,ref CORINFO_ASYNC2_INFO
 CORINFO_TAILCALL_HELPERS*,ref CORINFO_TAILCALL_HELPERS
 CORINFO_GENERICHANDLE_RESULT*,ref CORINFO_GENERICHANDLE_RESULT
 CORINFO_METHOD_INFO*,CORINFO_METHOD_INFO*
@@ -273,6 +274,7 @@ FUNCTIONS
     [ManualNativeWrapper] bool runWithErrorTrap(ICorJitInfo::errorTrapFunction function, void* parameter);
     [ManualNativeWrapper] bool runWithSPMIErrorTrap(ICorJitInfo::errorTrapFunction function, void* parameter);
     void getEEInfo(CORINFO_EE_INFO* pEEInfoOut);
+    void getAsync2Info(CORINFO_ASYNC2_INFO* pAsync2InfoOut);
     const char16_t * getJitTimeLogFilename();
     mdMethodDef getMethodDefFromMethod(CORINFO_METHOD_HANDLE hMethod);
     size_t printMethodName(CORINFO_METHOD_HANDLE ftn, char* buffer, size_t bufferSize, size_t* pRequiredBufferSize)
@@ -312,6 +314,7 @@ FUNCTIONS
     CORINFO_METHOD_HANDLE GetDelegateCtor(CORINFO_METHOD_HANDLE  methHnd, CORINFO_CLASS_HANDLE   clsHnd, CORINFO_METHOD_HANDLE  targetMethodHnd, DelegateCtorArgs *     pCtorData);
     void MethodCompileComplete(CORINFO_METHOD_HANDLE methHnd);
     bool getTailCallHelpers(CORINFO_RESOLVED_TOKEN* callToken, CORINFO_SIG_INFO* sig, CORINFO_GET_TAILCALL_HELPERS_FLAGS flags, CORINFO_TAILCALL_HELPERS* pResult);
+    CORINFO_METHOD_HANDLE getAsyncResumptionStub();
     bool convertPInvokeCalliToCall(CORINFO_RESOLVED_TOKEN * pResolvedToken, bool mustConvert);
     bool notifyInstructionSetUsage(CORINFO_InstructionSet instructionSet,bool supportEnabled);
     void updateEntryPointForTailCall(CORINFO_CONST_LOOKUP* entryPoint);
diff --git a/src/coreclr/tools/aot/jitinterface/jitinterface_generated.h b/src/coreclr/tools/aot/jitinterface/jitinterface_generated.h
index 2ec781cb7f7b50..737037285279fb 100644
--- a/src/coreclr/tools/aot/jitinterface/jitinterface_generated.h
+++ b/src/coreclr/tools/aot/jitinterface/jitinterface_generated.h
@@ -123,6 +123,7 @@ struct JitInterfaceCallbacks
     bool (* runWithErrorTrap)(void * thisHandle, CorInfoExceptionClass** ppException, ICorJitInfo::errorTrapFunction function, void* parameter);
     bool (* runWithSPMIErrorTrap)(void * thisHandle, CorInfoExceptionClass** ppException, ICorJitInfo::errorTrapFunction function, void* parameter);
     void (* getEEInfo)(void * thisHandle, CorInfoExceptionClass** ppException, CORINFO_EE_INFO* pEEInfoOut);
+    void (* getAsync2Info)(void * thisHandle, CorInfoExceptionClass** ppException, CORINFO_ASYNC2_INFO* pAsync2InfoOut);
     const char16_t* (* getJitTimeLogFilename)(void * thisHandle, CorInfoExceptionClass** ppException);
     mdMethodDef (* getMethodDefFromMethod)(void * thisHandle, CorInfoExceptionClass** ppException, CORINFO_METHOD_HANDLE hMethod);
     size_t (* printMethodName)(void * thisHandle, CorInfoExceptionClass** ppException, CORINFO_METHOD_HANDLE ftn, char* buffer, size_t bufferSize, size_t* pRequiredBufferSize);
@@ -162,6 +163,7 @@ struct JitInterfaceCallbacks
     CORINFO_METHOD_HANDLE (* GetDelegateCtor)(void * thisHandle, CorInfoExceptionClass** ppException, CORINFO_METHOD_HANDLE methHnd, CORINFO_CLASS_HANDLE clsHnd, CORINFO_METHOD_HANDLE targetMethodHnd, DelegateCtorArgs* pCtorData);
     void (* MethodCompileComplete)(void * thisHandle, CorInfoExceptionClass** ppException, CORINFO_METHOD_HANDLE methHnd);
     bool (* getTailCallHelpers)(void * thisHandle, CorInfoExceptionClass** ppException, CORINFO_RESOLVED_TOKEN* callToken, CORINFO_SIG_INFO* sig, CORINFO_GET_TAILCALL_HELPERS_FLAGS flags, CORINFO_TAILCALL_HELPERS* pResult);
+    CORINFO_METHOD_HANDLE (* getAsyncResumptionStub)(void * thisHandle, CorInfoExceptionClass** ppException);
     bool (* convertPInvokeCalliToCall)(void * thisHandle, CorInfoExceptionClass** ppException, CORINFO_RESOLVED_TOKEN* pResolvedToken, bool mustConvert);
     bool (* notifyInstructionSetUsage)(void * thisHandle, CorInfoExceptionClass** ppException, CORINFO_InstructionSet instructionSet, bool supportEnabled);
     void (* updateEntryPointForTailCall)(void * thisHandle, CorInfoExceptionClass** ppException, CORINFO_CONST_LOOKUP* entryPoint);
@@ -1279,6 +1281,14 @@ class JitInterfaceWrapper : public ICorJitInfo
     if (pException != nullptr) throw pException;
 }
 
+    virtual void getAsync2Info(
+          CORINFO_ASYNC2_INFO* pAsync2InfoOut)
+{
+    CorInfoExceptionClass* pException = nullptr;
+    _callbacks->getAsync2Info(_thisHandle, &pException, pAsync2InfoOut);
+    if (pException != nullptr) throw pException;
+}
+
     virtual const char16_t* getJitTimeLogFilename()
 {
     CorInfoExceptionClass* pException = nullptr;
@@ -1669,6 +1679,14 @@ class JitInterfaceWrapper : public ICorJitInfo
     return temp;
 }
 
+    virtual CORINFO_METHOD_HANDLE getAsyncResumptionStub()
+{
+    CorInfoExceptionClass* pException = nullptr;
+    CORINFO_METHOD_HANDLE temp = _callbacks->getAsyncResumptionStub(_thisHandle, &pException);
+    if (pException != nullptr) throw pException;
+    return temp;
+}
+
     virtual bool convertPInvokeCalliToCall(
           CORINFO_RESOLVED_TOKEN* pResolvedToken,
           bool mustConvert)
diff --git a/src/coreclr/tools/superpmi/superpmi-shim-collector/icorjitinfo.cpp b/src/coreclr/tools/superpmi/superpmi-shim-collector/icorjitinfo.cpp
index 90ad83f09b0a4b..b8f58508824110 100644
--- a/src/coreclr/tools/superpmi/superpmi-shim-collector/icorjitinfo.cpp
+++ b/src/coreclr/tools/superpmi/superpmi-shim-collector/icorjitinfo.cpp
@@ -1306,6 +1306,11 @@ void interceptor_ICJI::getEEInfo(CORINFO_EE_INFO* pEEInfoOut)
     mc->recGetEEInfo(pEEInfoOut);
 }
 
+void interceptor_ICJI::getAsync2Info(CORINFO_ASYNC2_INFO* pAsync2Info)
+{
+    // TODO
+}
+
 // Returns name of the JIT timer log
 const char16_t* interceptor_ICJI::getJitTimeLogFilename()
 {
@@ -1723,6 +1728,12 @@ bool interceptor_ICJI::getTailCallHelpers(
     return result;
 }
 
+CORINFO_METHOD_HANDLE interceptor_ICJI::getAsyncResumptionStub()
+{
+    // TODO
+    return NULL;
+}
+
 void interceptor_ICJI::updateEntryPointForTailCall(CORINFO_CONST_LOOKUP* entryPoint)
 {
     mc->cr->AddCall("updateEntryPointForTailCall");
diff --git a/src/coreclr/tools/superpmi/superpmi-shim-counter/icorjitinfo_generated.cpp b/src/coreclr/tools/superpmi/superpmi-shim-counter/icorjitinfo_generated.cpp
index ef01fa5977d75d..16c50ee49523c6 100644
--- a/src/coreclr/tools/superpmi/superpmi-shim-counter/icorjitinfo_generated.cpp
+++ b/src/coreclr/tools/superpmi/superpmi-shim-counter/icorjitinfo_generated.cpp
@@ -909,6 +909,13 @@ void interceptor_ICJI::getEEInfo(
     original_ICorJitInfo->getEEInfo(pEEInfoOut);
 }
 
+void interceptor_ICJI::getAsync2Info(
+          CORINFO_ASYNC2_INFO* pAsync2InfoOut)
+{
+    mcs->AddCall("getAsync2Info");
+    original_ICorJitInfo->getAsync2Info(pAsync2InfoOut);
+}
+
 const char16_t* interceptor_ICJI::getJitTimeLogFilename()
 {
     mcs->AddCall("getJitTimeLogFilename");
@@ -1229,6 +1236,12 @@ bool interceptor_ICJI::getTailCallHelpers(
     return original_ICorJitInfo->getTailCallHelpers(callToken, sig, flags, pResult);
 }
 
+CORINFO_METHOD_HANDLE interceptor_ICJI::getAsyncResumptionStub()
+{
+    mcs->AddCall("getAsyncResumptionStub");
+    return original_ICorJitInfo->getAsyncResumptionStub();
+}
+
 bool interceptor_ICJI::convertPInvokeCalliToCall(
           CORINFO_RESOLVED_TOKEN* pResolvedToken,
           bool mustConvert)
diff --git a/src/coreclr/tools/superpmi/superpmi-shim-simple/icorjitinfo_generated.cpp b/src/coreclr/tools/superpmi/superpmi-shim-simple/icorjitinfo_generated.cpp
index 8dd3ebb08d92ef..68268b4d419ad8 100644
--- a/src/coreclr/tools/superpmi/superpmi-shim-simple/icorjitinfo_generated.cpp
+++ b/src/coreclr/tools/superpmi/superpmi-shim-simple/icorjitinfo_generated.cpp
@@ -797,6 +797,12 @@ void interceptor_ICJI::getEEInfo(
     original_ICorJitInfo->getEEInfo(pEEInfoOut);
 }
 
+void interceptor_ICJI::getAsync2Info(
+          CORINFO_ASYNC2_INFO* pAsync2InfoOut)
+{
+    original_ICorJitInfo->getAsync2Info(pAsync2InfoOut);
+}
+
 const char16_t* interceptor_ICJI::getJitTimeLogFilename()
 {
     return original_ICorJitInfo->getJitTimeLogFilename();
@@ -1078,6 +1084,11 @@ bool interceptor_ICJI::getTailCallHelpers(
     return original_ICorJitInfo->getTailCallHelpers(callToken, sig, flags, pResult);
 }
 
+CORINFO_METHOD_HANDLE interceptor_ICJI::getAsyncResumptionStub()
+{
+    return original_ICorJitInfo->getAsyncResumptionStub();
+}
+
 bool interceptor_ICJI::convertPInvokeCalliToCall(
           CORINFO_RESOLVED_TOKEN* pResolvedToken,
           bool mustConvert)
diff --git a/src/coreclr/tools/superpmi/superpmi/icorjitinfo.cpp b/src/coreclr/tools/superpmi/superpmi/icorjitinfo.cpp
index 7e7f3e67416c85..f476ee8186bd16 100644
--- a/src/coreclr/tools/superpmi/superpmi/icorjitinfo.cpp
+++ b/src/coreclr/tools/superpmi/superpmi/icorjitinfo.cpp
@@ -1117,6 +1117,11 @@ void MyICJI::getEEInfo(CORINFO_EE_INFO* pEEInfoOut)
     jitInstance->mc->repGetEEInfo(pEEInfoOut);
 }
 
+void MyICJI::getAsync2Info(CORINFO_ASYNC2_INFO* pAsync2Info)
+{
+    // TODO
+}
+
 // Returns name of the JIT timer log
 const char16_t* MyICJI::getJitTimeLogFilename()
 {
@@ -1465,6 +1470,13 @@ bool MyICJI::getTailCallHelpers(
     return jitInstance->mc->repGetTailCallHelpers(callToken, sig, flags, pResult);
 }
 
+CORINFO_METHOD_HANDLE MyICJI::getAsyncResumptionStub()
+{
+    jitInstance->mc->cr->AddCall("getAsyncResumptionStub");
+    // TODO
+    return NULL;
+}
+
 bool MyICJI::convertPInvokeCalliToCall(CORINFO_RESOLVED_TOKEN* pResolvedToken, bool fMustConvert)
 {
     jitInstance->mc->cr->AddCall("convertPInvokeCalliToCall");
diff --git a/src/coreclr/vm/appdomain.cpp b/src/coreclr/vm/appdomain.cpp
index 51aec1b9e1c69d..6301eb126e96e2 100644
--- a/src/coreclr/vm/appdomain.cpp
+++ b/src/coreclr/vm/appdomain.cpp
@@ -1356,6 +1356,7 @@ void SystemDomain::LoadBaseSystemClasses()
         // initialize cast cache here.
         CastCache::Initialize();
         ECall::PopulateManagedCastHelpers();
+        ECall::PopulateAsyncHelpers();
 
         // used by IsImplicitInterfaceOfSZArray
         CoreLibBinder::GetClass(CLASS__IENUMERABLEGENERIC);
diff --git a/src/coreclr/vm/corelib.h b/src/coreclr/vm/corelib.h
index 42be7407625842..14e4ed9800424c 100644
--- a/src/coreclr/vm/corelib.h
+++ b/src/coreclr/vm/corelib.h
@@ -676,7 +676,8 @@ DEFINE_METHOD(RUNTIME_HELPERS,      ENUM_EQUALS,            EnumEquals, NoSig)
 DEFINE_METHOD(RUNTIME_HELPERS,      ENUM_COMPARE_TO,        EnumCompareTo, NoSig)
 DEFINE_METHOD(RUNTIME_HELPERS,      ALLOC_TAILCALL_ARG_BUFFER, AllocTailCallArgBuffer,  SM_Int_IntPtr_RetIntPtr)
 DEFINE_METHOD(RUNTIME_HELPERS,      GET_TAILCALL_INFO,      GetTailCallInfo, NoSig)
-DEFINE_METHOD(RUNTIME_HELPERS,      DISPATCH_TAILCALLS,     DispatchTailCalls,          NoSig)
+DEFINE_METHOD(RUNTIME_HELPERS,      DISPATCH_TAILCALLS,     DispatchTailCalls, NoSig)
+DEFINE_METHOD(RUNTIME_HELPERS,      ALLOC_CONTINUATION,     AllocContinuation, NoSig)
 
 DEFINE_METHOD(RUNTIME_HELPERS,      GET_OR_CREATE_RESUMPTION_DELEGATE, GetOrCreateResumptionDelegate, NoSig)
 DEFINE_METHOD(RUNTIME_HELPERS,      UNSAFE_AWAIT_AWAITER_FROM_RUNTIME_ASYNC_2, UnsafeAwaitAwaiterFromRuntimeAsync, GM_U_RetT)
@@ -756,6 +757,13 @@ DEFINE_CLASS_U(CompilerServices,           TailCallTls,           TailCallTls)
 DEFINE_FIELD_U(Frame,                      TailCallTls,           m_frame)
 DEFINE_FIELD_U(ArgBuffer,                  TailCallTls,           m_argBuffer)
 
+DEFINE_CLASS(CONTINUATION,              CompilerServices,   Continuation)
+DEFINE_FIELD(CONTINUATION,              NEXT,               Next)
+DEFINE_FIELD(CONTINUATION,              RESUME,             Resume)
+DEFINE_FIELD(CONTINUATION,              STATE,              State)
+DEFINE_FIELD(CONTINUATION,              DATA,               Data)
+DEFINE_FIELD(CONTINUATION,              GCDATA,             GCData)
+
 DEFINE_CLASS(RUNTIME_WRAPPED_EXCEPTION, CompilerServices,   RuntimeWrappedException)
 DEFINE_METHOD(RUNTIME_WRAPPED_EXCEPTION, OBJ_CTOR,          .ctor,                      IM_Obj_RetVoid)
 DEFINE_FIELD(RUNTIME_WRAPPED_EXCEPTION, WRAPPED_EXCEPTION,  _wrappedException)
@@ -972,6 +980,7 @@ DEFINE_METHOD(STUBHELPERS,          VALIDATE_BYREF,                     Validate
 DEFINE_METHOD(STUBHELPERS,          GET_STUB_CONTEXT,                   GetStubContext,                 SM_RetIntPtr)
 DEFINE_METHOD(STUBHELPERS,          LOG_PINNED_ARGUMENT,                LogPinnedArgument,              SM_IntPtr_IntPtr_RetVoid)
 DEFINE_METHOD(STUBHELPERS,          NEXT_CALL_RETURN_ADDRESS,           NextCallReturnAddress,          SM_RetIntPtr)
+DEFINE_METHOD(STUBHELPERS,          ASYNC2_CALL_CONTINUATION,           Async2CallContinuation,         SM_RetObj)
 DEFINE_METHOD(STUBHELPERS,          SAFE_HANDLE_ADD_REF,    SafeHandleAddRef,           SM_SafeHandle_RefBool_RetIntPtr)
 DEFINE_METHOD(STUBHELPERS,          SAFE_HANDLE_RELEASE,    SafeHandleRelease,          SM_SafeHandle_RetVoid)
 
diff --git a/src/coreclr/vm/dllimport.h b/src/coreclr/vm/dllimport.h
index 256b950799336e..11853bd5e7d0c2 100644
--- a/src/coreclr/vm/dllimport.h
+++ b/src/coreclr/vm/dllimport.h
@@ -195,6 +195,7 @@ enum ILStubTypes
     ILSTUB_TAILCALL_STOREARGS            = 0x80000008,
     ILSTUB_TAILCALL_CALLTARGET           = 0x80000009,
     ILSTUB_STATIC_VIRTUAL_DISPATCH_STUB  = 0x8000000A,
+    ILSTUB_ASYNC_RESUME                  = 0x8000000B,
 };
 
 #ifdef FEATURE_COMINTEROP
@@ -234,6 +235,7 @@ inline bool SF_IsInstantiatingStub      (DWORD dwStubFlags) { LIMITED_METHOD_CON
 #endif
 inline bool SF_IsTailCallStoreArgsStub  (DWORD dwStubFlags) { LIMITED_METHOD_CONTRACT; return (dwStubFlags == ILSTUB_TAILCALL_STOREARGS); }
 inline bool SF_IsTailCallCallTargetStub (DWORD dwStubFlags) { LIMITED_METHOD_CONTRACT; return (dwStubFlags == ILSTUB_TAILCALL_CALLTARGET); }
+inline bool SF_IsAsyncResumeStub        (DWORD dwStubFlags) { LIMITED_METHOD_CONTRACT; return (dwStubFlags == ILSTUB_ASYNC_RESUME); }
 
 inline bool SF_IsCOMStub               (DWORD dwStubFlags) { LIMITED_METHOD_CONTRACT; return COM_ONLY(dwStubFlags < NDIRECTSTUB_FL_INVALID && 0 != (dwStubFlags & NDIRECTSTUB_FL_COM)); }
 inline bool SF_IsCOMLateBoundStub      (DWORD dwStubFlags) { LIMITED_METHOD_CONTRACT; return COM_ONLY(dwStubFlags < NDIRECTSTUB_FL_INVALID && 0 != (dwStubFlags & NDIRECTSTUB_FL_COMLATEBOUND)); }
@@ -255,6 +257,11 @@ inline bool SF_IsSharedStub(DWORD dwStubFlags)
         return false;
     }
 
+    if (SF_IsAsyncResumeStub(dwStubFlags))
+    {
+        return false;
+    }
+
     return true;
 }
 
diff --git a/src/coreclr/vm/ecall.cpp b/src/coreclr/vm/ecall.cpp
index 37ac50d124f6f6..6f33f0b2af4f91 100644
--- a/src/coreclr/vm/ecall.cpp
+++ b/src/coreclr/vm/ecall.cpp
@@ -146,6 +146,15 @@ void ECall::PopulateManagedCastHelpers()
     SetJitHelperFunction(CORINFO_HELP_LDELEMA_REF, pDest);
 }
 
+void ECall::PopulateAsyncHelpers()
+{
+    STANDARD_VM_CONTRACT;
+
+    MethodDesc* pMD = CoreLibBinder::GetMethod((BinderMethodID)(METHOD__RUNTIME_HELPERS__ALLOC_CONTINUATION));
+    PCODE pDest = pMD->GetMultiCallableAddrOfCode();
+    SetJitHelperFunction(CORINFO_HELP_ALLOC_CONTINUATION, pDest);
+}
+
 static CrstStatic gFCallLock;
 
 // This variable is used to force the compiler not to tailcall a function.
diff --git a/src/coreclr/vm/ecall.h b/src/coreclr/vm/ecall.h
index bc9d63ae467137..fb888f8fcee456 100644
--- a/src/coreclr/vm/ecall.h
+++ b/src/coreclr/vm/ecall.h
@@ -96,6 +96,8 @@ class ECall
 
         static void PopulateManagedCastHelpers();
 
+        static void PopulateAsyncHelpers();
+
 #ifdef DACCESS_COMPILE
         // Enumerates all gFCallMethods for minidumps.
         static void EnumFCallMethods();
diff --git a/src/coreclr/vm/ilstubcache.cpp b/src/coreclr/vm/ilstubcache.cpp
index cb029595955016..e078895b0338d2 100644
--- a/src/coreclr/vm/ilstubcache.cpp
+++ b/src/coreclr/vm/ilstubcache.cpp
@@ -160,6 +160,7 @@ namespace
             case DynamicMethodDesc::StubTailCallStoreArgs:  return "IL_STUB_StoreTailCallArgs";
             case DynamicMethodDesc::StubTailCallCallTarget: return "IL_STUB_CallTailCallTarget";
             case DynamicMethodDesc::StubVirtualStaticMethodDispatch: return "IL_STUB_bVirtualStaticMethodDispatch";
+            case DynamicMethodDesc::StubAsyncResume:        return "IL_STUB_AsyncResume";
             default:
                 UNREACHABLE_MSG("Unknown stub type");
         }
@@ -278,6 +279,11 @@ MethodDesc* ILStubCache::CreateNewMethodDesc(LoaderHeap* pCreationHeap, MethodTa
         pMD->SetILStubType(DynamicMethodDesc::StubTailCallCallTarget);
     }
     else
+    if (SF_IsAsyncResumeStub(dwStubFlags))
+    {
+        pMD->SetILStubType(DynamicMethodDesc::StubAsyncResume);
+    }
+    else
 #ifdef FEATURE_COMINTEROP
     if (SF_IsCOMStub(dwStubFlags))
     {
diff --git a/src/coreclr/vm/jitinterface.cpp b/src/coreclr/vm/jitinterface.cpp
index 6b4dba089e19e6..d3489493352c0f 100644
--- a/src/coreclr/vm/jitinterface.cpp
+++ b/src/coreclr/vm/jitinterface.cpp
@@ -4202,6 +4202,9 @@ CORINFO_CLASS_HANDLE CEEInfo::getBuiltinClass(CorInfoClassId classId)
     case CLASSID_SYSTEM_OBJECT:
         result = CORINFO_CLASS_HANDLE(g_pObjectClass);
         break;
+    case CLASSID_SYSTEM_BYTE:
+        result = CORINFO_CLASS_HANDLE(CoreLibBinder::GetClass(CLASS__BYTE));
+        break;
     case CLASSID_TYPED_BYREF:
         result = CORINFO_CLASS_HANDLE(g_TypedReferenceMT);
         break;
@@ -10037,6 +10040,22 @@ void CEEInfo::getEEInfo(CORINFO_EE_INFO *pEEInfoOut)
     EE_TO_JIT_TRANSITION();
 }
 
+void CEEInfo::getAsync2Info(CORINFO_ASYNC2_INFO* pAsync2InfoOut)
+{
+    CONTRACTL {
+        THROWS;
+        GC_TRIGGERS;
+        MODE_PREEMPTIVE;
+    } CONTRACTL_END;
+
+    pAsync2InfoOut->continuationClsHnd = CORINFO_CLASS_HANDLE(CoreLibBinder::GetClass(CLASS__CONTINUATION));
+    pAsync2InfoOut->continuationNextFldHnd = CORINFO_FIELD_HANDLE(CoreLibBinder::GetField(FIELD__CONTINUATION__NEXT));
+    pAsync2InfoOut->continuationResumeFldHnd = CORINFO_FIELD_HANDLE(CoreLibBinder::GetField(FIELD__CONTINUATION__RESUME));
+    pAsync2InfoOut->continuationStateFldHnd = CORINFO_FIELD_HANDLE(CoreLibBinder::GetField(FIELD__CONTINUATION__STATE));
+    pAsync2InfoOut->continuationDataFldHnd = CORINFO_FIELD_HANDLE(CoreLibBinder::GetField(FIELD__CONTINUATION__DATA));
+    pAsync2InfoOut->continuationGCDataFldHnd = CORINFO_FIELD_HANDLE(CoreLibBinder::GetField(FIELD__CONTINUATION__GCDATA));
+}
+
 const char16_t * CEEInfo::getJitTimeLogFilename()
 {
     CONTRACTL {
@@ -10604,7 +10623,8 @@ void* CEEJitInfo::getHelperFtn(CorInfoHelpFunc    ftnNum,         /* IN  */
             dynamicFtnNum == DYNAMIC_CORINFO_HELP_CHKCASTCLASS_SPECIAL ||
             dynamicFtnNum == DYNAMIC_CORINFO_HELP_UNBOX ||
             dynamicFtnNum == DYNAMIC_CORINFO_HELP_ARRADDR_ST ||
-            dynamicFtnNum == DYNAMIC_CORINFO_HELP_LDELEMA_REF)
+            dynamicFtnNum == DYNAMIC_CORINFO_HELP_LDELEMA_REF ||
+            dynamicFtnNum == DYNAMIC_CORINFO_HELP_ALLOC_CONTINUATION)
         {
             Precode* pPrecode = Precode::GetPrecodeFromEntryPoint((PCODE)hlpDynamicFuncTable[dynamicFtnNum].pfnHelper);
             _ASSERTE(pPrecode->GetType() == PRECODE_FIXUP);
@@ -14172,6 +14192,235 @@ bool CEEInfo::getTailCallHelpers(CORINFO_RESOLVED_TOKEN* callToken,
     return success;
 }
 
+static Signature AllocateSignature(LoaderAllocator* alloc, SigBuilder& sigBuilder)
+{
+    DWORD sigLen;
+    PCCOR_SIGNATURE builderSig = (PCCOR_SIGNATURE)sigBuilder.GetSignature(&sigLen);
+    AllocMemTracker pamTracker;
+    PVOID newBlob = pamTracker.Track(alloc->GetHighFrequencyHeap()->AllocMem(S_SIZE_T(sigLen)));
+    memcpy(newBlob, builderSig, sigLen);
+
+    pamTracker.SuppressRelease();
+    return Signature((PCCOR_SIGNATURE)newBlob, sigLen);
+}
+
+static Signature BuildResumptionStubSignature(LoaderAllocator* alloc)
+{
+    SigBuilder sigBuilder;
+    sigBuilder.AppendByte(IMAGE_CEE_CS_CALLCONV_DEFAULT);
+    sigBuilder.AppendData(2); // 2 arguments
+    sigBuilder.AppendElementType(ELEMENT_TYPE_OBJECT); // return type
+    sigBuilder.AppendElementType(ELEMENT_TYPE_OBJECT); // continuation
+    sigBuilder.AppendElementType(ELEMENT_TYPE_OBJECT); // exception
+
+    return AllocateSignature(alloc, sigBuilder);
+}
+
+static Signature BuildResumptionStubCalliSignature(MetaSig& msig, LoaderAllocator* alloc)
+{
+    unsigned numArgs = 0;
+    if (msig.HasThis())
+    {
+        numArgs++;
+    }
+
+    if ((msig.GetCallingConvention() & CORINFO_CALLCONV_PARAMTYPE) != 0)
+    {
+        numArgs++;
+    }
+
+    numArgs++; // Continuation
+
+    numArgs += msig.NumFixedArgs();
+
+    SigBuilder sigBuilder;
+    sigBuilder.AppendByte(IMAGE_CEE_CS_CALLCONV_DEFAULT);
+    sigBuilder.AppendData(numArgs);
+
+    auto appendTypeHandle = [&](TypeHandle th) {
+        _ASSERTE(!th.IsByRef());
+        CorElementType ty = th.GetSignatureCorElementType();
+        if (CorTypeInfo::IsObjRef(ty))
+        {
+            // Especially to normalize System.__Canon.
+            sigBuilder.AppendElementType(ELEMENT_TYPE_OBJECT);
+        }
+        else if (CorTypeInfo::IsPrimitiveType(ty))
+        {
+            sigBuilder.AppendElementType(ty);
+        }
+        else
+        {
+            sigBuilder.AppendElementType(ELEMENT_TYPE_INTERNAL);
+            sigBuilder.AppendPointer(th.AsPtr());
+        }
+        };
+
+    appendTypeHandle(msig.GetRetTypeHandleThrowing()); // return type
+    if (msig.HasThis())
+    {
+        sigBuilder.AppendElementType(ELEMENT_TYPE_OBJECT);
+    }
+#ifndef TARGET_X86
+    if ((msig.GetCallingConvention() & CORINFO_CALLCONV_PARAMTYPE) != 0)
+    {
+        sigBuilder.AppendElementType(ELEMENT_TYPE_I);
+    }
+
+    sigBuilder.AppendElementType(ELEMENT_TYPE_OBJECT); // continuation
+#endif
+
+    msig.Reset();
+    CorElementType ty;
+    while ((ty = msig.NextArg()) != ELEMENT_TYPE_END)
+    {
+        TypeHandle tyHnd = msig.GetLastTypeHandleThrowing();
+        appendTypeHandle(tyHnd);
+    }
+
+#ifdef TARGET_X86
+    if ((msig.GetCallingConvention() & CORINFO_CALLCONV_PARAMTYPE) != 0)
+    {
+        sigBuilder.AppendElementType(ELEMENT_TYPE_I);
+    }
+
+    sigBuilder.AppendElementType(ELEMENT_TYPE_OBJECT); // continuation
+#endif
+
+    return AllocateSignature(alloc, sigBuilder);
+}
+
+CORINFO_METHOD_HANDLE CEEInfo::getAsyncResumptionStub()
+{
+    CONTRACTL{
+        THROWS;
+        GC_TRIGGERS;
+        MODE_PREEMPTIVE;
+    } CONTRACTL_END;
+
+    MethodDesc* md = m_pMethodBeingCompiled;
+    // Not hard to support, just requires calls through a pointer and passing the
+    _ASSERTE(!md->IsSharedByGenericInstantiations());
+
+    LoaderAllocator* loaderAlloc = md->GetLoaderAllocator();
+
+    Signature stubSig = BuildResumptionStubSignature(md->GetLoaderAllocator());
+
+    MetaSig msig(md);
+    Signature calliSig = BuildResumptionStubCalliSignature(msig, md->GetLoaderAllocator());
+
+    SigTypeContext emptyCtx;
+    ILStubLinker sl(md->GetModule(), stubSig, &emptyCtx, NULL, ILSTUB_LINKER_FLAG_NONE);
+
+    ILCodeStream* pCode = sl.NewCodeStream(ILStubLinker::kDispatch);
+
+    int numArgs = 0;
+
+    if (msig.HasThis())
+    {
+        _ASSERTE(!md->GetMethodTable()->IsValueType());
+        pCode->EmitLDNULL();
+        numArgs++;
+    }
+
+#ifndef TARGET_X86
+    if ((msig.GetCallingConvention() & CORINFO_CALLCONV_PARAMTYPE) != 0)
+    {
+        _ASSERTE(!"Generic context currently unhandled");
+        // Will need JIT to store this in an agreed upon place.
+        numArgs++;
+    }
+
+    // Continuation
+    pCode->EmitLDARG(0);
+    numArgs++;
+#endif
+
+    msig.Reset();
+    CorElementType ty;
+    while ((ty = msig.NextArg()) != ELEMENT_TYPE_END)
+    {
+        TypeHandle tyHnd = msig.GetLastTypeHandleThrowing();
+        DWORD loc = pCode->NewLocal(LocalDesc(tyHnd));
+        pCode->EmitLDLOCA(loc);
+        pCode->EmitINITOBJ(pCode->GetToken(tyHnd));
+        pCode->EmitLDLOC(loc);
+        numArgs++;
+    }
+
+#ifdef TARGET_X86
+    // Continuation
+    pCode->EmitLDARG(0);
+    nmArgs++;
+#endif
+
+    // TODO: This can use indirection of precode slot directly.
+    pCode->EmitLDC(md->GetMultiCallableAddrOfCode(CORINFO_ACCESS_ANY));
+
+    pCode->EmitCALLI(pCode->GetSigToken(calliSig.GetRawSig(), calliSig.GetRawSigLen()), numArgs, msig.IsReturnTypeVoid() ? 0 : 1);
+
+    DWORD resultLoc = UINT_MAX;
+    if (!msig.IsReturnTypeVoid())
+    {
+        resultLoc = pCode->NewLocal(LocalDesc(msig.GetRetTypeHandleThrowing()));
+        pCode->EmitSTLOC(resultLoc);
+    }
+
+    DWORD newContinuationLoc = pCode->NewLocal(ELEMENT_TYPE_OBJECT);
+    pCode->EmitCALL(METHOD__STUBHELPERS__ASYNC2_CALL_CONTINUATION, 0, 1);
+    pCode->EmitSTLOC(newContinuationLoc);
+
+    if (!msig.IsReturnTypeVoid())
+    {
+        ILCodeLabel* noResult = pCode->NewCodeLabel();
+        pCode->EmitLDLOC(newContinuationLoc);
+        pCode->EmitBRTRUE(noResult);
+
+        // Load 'next' of current continuation
+        pCode->EmitLDARG(0);
+        pCode->EmitCALL(METHOD__RUNTIME_HELPERS__GET_RAW_DATA, 1, 1);
+        pCode->EmitLDFLD(FIELD__CONTINUATION__NEXT);
+
+        // Load 'gcdata' of next continuation
+        pCode->EmitCALL(METHOD__RUNTIME_HELPERS__GET_RAW_DATA, 1, 1);
+        pCode->EmitLDFLD(FIELD__CONTINUATION__GCDATA);
+
+        // Now we have the GC array. At the first index is the result.
+        pCode->EmitLDC(0);
+
+        // Box the result.
+        pCode->EmitLDLOC(resultLoc);
+        pCode->EmitBOX(pCode->GetToken(msig.GetRetTypeHandleThrowing()));
+
+        // Finally store it.
+        pCode->EmitSTELEM_REF();
+
+        pCode->EmitLabel(noResult);
+    }
+
+    pCode->EmitLDLOC(newContinuationLoc);
+    pCode->EmitRET();
+
+    MethodDesc* result =
+        ILStubCache::CreateAndLinkNewILStubMethodDesc(
+            md->GetLoaderAllocator(),
+            md->GetLoaderModule()->GetILStubCache()->GetOrCreateStubMethodTable(md->GetLoaderModule()),
+            ILSTUB_ASYNC_RESUME,
+            md->GetModule(),
+            stubSig.GetRawSig(), stubSig.GetRawSigLen(),
+            &emptyCtx,
+            &sl);
+
+#ifdef _DEBUG
+    LOG((LF_STUBS, LL_INFO1000, "ASYNC: Resumption stub created\n"));
+    sl.LogILStub(CORJIT_FLAGS());
+#endif
+
+    result->DoPrestub(NULL);
+
+    return CORINFO_METHOD_HANDLE(result);
+}
+
 bool CEEInfo::convertPInvokeCalliToCall(CORINFO_RESOLVED_TOKEN * pResolvedToken, bool fMustConvert)
 {
     return false;
diff --git a/src/coreclr/vm/method.hpp b/src/coreclr/vm/method.hpp
index 25c16852c12986..18f1fb92c87689 100644
--- a/src/coreclr/vm/method.hpp
+++ b/src/coreclr/vm/method.hpp
@@ -2527,6 +2527,8 @@ class DynamicMethodDesc : public StoredSigMethodDesc
 
         StubVirtualStaticMethodDispatch,
 
+        StubAsyncResume,
+
         StubLast
     };
 
diff --git a/src/coreclr/vm/prestub.cpp b/src/coreclr/vm/prestub.cpp
index 759117e95203b8..4948dddee95087 100644
--- a/src/coreclr/vm/prestub.cpp
+++ b/src/coreclr/vm/prestub.cpp
@@ -1470,6 +1470,8 @@ bool MethodDesc::TryGenerateAsyncThunk(DynamicResolver** resolver, COR_ILMETHOD_
         return false;
     }
 
+    _ASSERTE(CLRConfig::GetConfigValue(CLRConfig::EXTERNAL_RuntimeAsyncViaJitGeneratedStateMachines) == 0);
+
     ULONG offsetOfAsyncDetailsUnused;
     AsyncTaskMethod asyncType = ClassifyAsyncMethod(GetSigPointer(), GetModule(), &offsetOfAsyncDetailsUnused);
     MethodDesc *pAsyncOtherVariant = this->GetAsyncOtherVariant();
diff --git a/src/coreclr/vm/stubgen.cpp b/src/coreclr/vm/stubgen.cpp
index 7c8be127608431..4ed482b410a8f6 100644
--- a/src/coreclr/vm/stubgen.cpp
+++ b/src/coreclr/vm/stubgen.cpp
@@ -1192,6 +1192,11 @@ void ILCodeStream::EmitBNE_UN(ILCodeLabel* pCodeLabel)
     WRAPPER_NO_CONTRACT;
     Emit(CEE_BNE_UN, -2, (UINT_PTR)pCodeLabel);
 }
+void ILCodeStream::EmitBOX(int token)
+{
+    WRAPPER_NO_CONTRACT;
+    Emit(CEE_BOX, 0, token);
+}
 void ILCodeStream::EmitBR(ILCodeLabel* pCodeLabel)
 {
     WRAPPER_NO_CONTRACT;
@@ -1827,6 +1832,11 @@ void ILCodeStream::EmitUNALIGNED(BYTE alignment)
     Emit(CEE_UNALIGNED, 0, alignment);
 }
 
+void ILCodeStream::EmitUNBOX(int token)
+{
+    WRAPPER_NO_CONTRACT;
+    Emit(CEE_UNBOX, 0, token);
+}
 
 void ILCodeStream::EmitNEWOBJ(BinderMethodID id, int numInArgs)
 {
diff --git a/src/coreclr/vm/stubgen.h b/src/coreclr/vm/stubgen.h
index 595de649220cc5..81d8d1130d5e45 100644
--- a/src/coreclr/vm/stubgen.h
+++ b/src/coreclr/vm/stubgen.h
@@ -708,6 +708,7 @@ class ILCodeStream
     void EmitBLE_UN     (ILCodeLabel* pCodeLabel);
     void EmitBLT        (ILCodeLabel* pCodeLabel);
     void EmitBNE_UN     (ILCodeLabel* pCodeLabel);
+    void EmitBOX        (int token);
     void EmitBR         (ILCodeLabel* pCodeLabel);
     void EmitBREAK      ();
     void EmitBRFALSE    (ILCodeLabel* pCodeLabel);
@@ -799,6 +800,7 @@ class ILCodeStream
     void EmitSUB        ();
     void EmitTHROW      ();
     void EmitUNALIGNED  (BYTE alignment);
+    void EmitUNBOX      (int token);
 
     // Overloads to simplify common usage patterns
     void EmitNEWOBJ     (BinderMethodID id, int numInArgs);

From 4cc1f1882b7d29a9bfc441f5cbd4ad9df09d29fc Mon Sep 17 00:00:00 2001
From: David Wrighton <davidwr@microsoft.com>
Date: Mon, 23 Oct 2023 17:47:21 +0000
Subject: [PATCH 037/203] Merged PR 34735: Simplify the async2 api surface

---
 docs/design/features/runtime-handled-tasks.md |  31 +---
 .../src/CompatibilitySuppressions.xml         |  12 --
 .../Strategies/FileStreamHelpers.Windows.cs   |   2 +-
 .../ConfiguredValueTaskAwaitable.cs           |   4 +-
 .../CompilerServices/INotifyCompletion.cs     |  28 ----
 .../CompilerServices/RuntimeHelpers.cs        | 135 +++++-------------
 .../Runtime/CompilerServices/TaskAwaiter.cs   |   8 +-
 .../CompilerServices/ValueTaskAwaiter.cs      |   4 +-
 .../CompilerServices/YieldAwaitable.cs        |   2 +-
 .../System.Runtime/ref/System.Runtime.cs      |  44 ++----
 .../runtimetask-asyncfibonacci-with-yields.il |  30 +++-
 11 files changed, 83 insertions(+), 217 deletions(-)

diff --git a/docs/design/features/runtime-handled-tasks.md b/docs/design/features/runtime-handled-tasks.md
index 64accff939822f..5402dde90ddb47 100644
--- a/docs/design/features/runtime-handled-tasks.md
+++ b/docs/design/features/runtime-handled-tasks.md
@@ -131,34 +131,11 @@ The StackTrace of a exception thrown within an async2 method shall include any a
 The runtime shall provide the following apis
 
 ```
-        public interface ICriticalNotifyCompletion2 : ICriticalNotifyCompletion
-        {
-            bool IsCompleted { get; }
-            void GetResult();
-        }
-        public interface ICriticalNotifyCompletion2<TResult> : ICriticalNotifyCompletion
-        {
-            bool IsCompleted { get; }
-            TResult GetResult();
-        }
-        public interface INotifyCompletion2 : INotifyCompletion
-        {
-            bool IsCompleted { get; }
-            void GetResult();
-        }
-        public interface INotifyCompletion2<TResult> : INotifyCompletion
-        {
-            bool IsCompleted { get; }
-            TResult GetResult();
-        }
-
-        public static async2 void AwaitAwaiterFromRuntimeAsync<TAwaiter>(TAwaiter awaiter) where TAwaiter : INotifyCompletion2
-        public static async2 TResult AwaitAwaiterFromRuntimeAsync<TResult, TAwaiter>(TAwaiter awaiter) where TAwaiter : INotifyCompletion2<TResult>
-        public static async2 void UnsafeAwaitAwaiterFromRuntimeAsync<TAwaiter>(TAwaiter awaiter) where TAwaiter : ICriticalNotifyCompletion2
-        public static async2 TResult UnsafeAwaitAwaiterFromRuntimeAsync<TResult, TAwaiter>(TAwaiter awaiter) where TAwaiter : ICriticalNotifyCompletion2<TResult>
-
+        public static async2 void AwaitAwaiterFromRuntimeAsync<TAwaiter>(TAwaiter awaiter) where TAwaiter : INotifyCompletion
+        public static async2 void UnsafeAwaitAwaiterFromRuntimeAsync<TAwaiter>(TAwaiter awaiter) where TAwaiter : ICriticalNotifyCompletion
 ```
-As well as define that `Task`/`ValueTask` and other awaiters provide awaiters provided implement the `INotifyCompletion2`/`ICriticalNotification2` interfaces. The AwaitAwaiterFromRuntimeAsync provides a means for code generators to await on anything that is awaitable today with a simple set of IL.
+
+Any IL in an async2 method which needs to await will follow the existing C# model of calling `IsCompleted` and `GetResult()` and in the middle instead of using the existing patterns for suspension shall call one of the above helper methods.
 
 ### Utilizing async2 based code from non-async2 based code
 
diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/CompatibilitySuppressions.xml b/src/coreclr/nativeaot/System.Private.CoreLib/src/CompatibilitySuppressions.xml
index 376c3ecb1ee8d7..9c7bffdba74162 100644
--- a/src/coreclr/nativeaot/System.Private.CoreLib/src/CompatibilitySuppressions.xml
+++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/CompatibilitySuppressions.xml
@@ -967,24 +967,12 @@
     <Left>ref/net9.0/System.Private.CoreLib.dll</Left>
     <Right>lib/net9.0/System.Private.CoreLib.dll</Right>
   </Suppression>
-  <Suppression>
-    <DiagnosticId>CP0002</DiagnosticId>
-    <Target>M:System.Runtime.CompilerServices.RuntimeHelpers.AwaitAwaiterFromRuntimeAsync``2(``1)</Target>
-    <Left>ref/net9.0/System.Private.CoreLib.dll</Left>
-    <Right>lib/net9.0/System.Private.CoreLib.dll</Right>
-  </Suppression>
   <Suppression>
     <DiagnosticId>CP0002</DiagnosticId>
     <Target>M:System.Runtime.CompilerServices.RuntimeHelpers.UnsafeAwaitAwaiterFromRuntimeAsync``1(``0)</Target>
     <Left>ref/net9.0/System.Private.CoreLib.dll</Left>
     <Right>lib/net9.0/System.Private.CoreLib.dll</Right>
   </Suppression>
-  <Suppression>
-    <DiagnosticId>CP0002</DiagnosticId>
-    <Target>M:System.Runtime.CompilerServices.RuntimeHelpers.UnsafeAwaitAwaiterFromRuntimeAsync``2(``1)</Target>
-    <Left>ref/net9.0/System.Private.CoreLib.dll</Left>
-    <Right>lib/net9.0/System.Private.CoreLib.dll</Right>
-  </Suppression>
   <Suppression>
     <DiagnosticId>CP0014</DiagnosticId>
     <Target>M:System.Runtime.CompilerServices.RuntimeHelpers.GetObjectValue(System.Object)-&gt;object?:[T:System.Diagnostics.CodeAnalysis.NotNullIfNotNullAttribute]</Target>
diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/FileStreamHelpers.Windows.cs b/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/FileStreamHelpers.Windows.cs
index 9abfdf66571bf3..ce1dedaa8889d8 100644
--- a/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/FileStreamHelpers.Windows.cs
+++ b/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/FileStreamHelpers.Windows.cs
@@ -310,7 +310,7 @@ internal static async Task AsyncModeCopyToAsync(SafeFileHandle handle, bool canS
         }
 
         /// <summary>Used by AsyncWindowsFileStreamStrategy.CopyToAsync to enable awaiting the result of an overlapped I/O operation with minimal overhead.</summary>
-        private sealed unsafe class AsyncCopyToAwaitable : ICriticalNotifyCompletion2
+        private sealed unsafe class AsyncCopyToAwaitable : ICriticalNotifyCompletion
         {
             /// <summary>Sentinel object used to indicate that the I/O operation has completed before being awaited.</summary>
             private static readonly Action s_sentinel = () => { };
diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/ConfiguredValueTaskAwaitable.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/ConfiguredValueTaskAwaitable.cs
index 9a1e250a7b5a87..1700fb0cca8aa6 100644
--- a/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/ConfiguredValueTaskAwaitable.cs
+++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/ConfiguredValueTaskAwaitable.cs
@@ -27,7 +27,7 @@ public readonly struct ConfiguredValueTaskAwaitable
 
         /// <summary>Provides an awaiter for a <see cref="ConfiguredValueTaskAwaitable"/>.</summary>
         [StructLayout(LayoutKind.Auto)]
-        public readonly struct ConfiguredValueTaskAwaiter : ICriticalNotifyCompletion2, IStateMachineBoxAwareAwaiter
+        public readonly struct ConfiguredValueTaskAwaiter : ICriticalNotifyCompletion, IStateMachineBoxAwareAwaiter
         {
             /// <summary>The value being awaited.</summary>
             private readonly ValueTask _value;
@@ -132,7 +132,7 @@ public readonly struct ConfiguredValueTaskAwaitable<TResult>
 
         /// <summary>Provides an awaiter for a <see cref="ConfiguredValueTaskAwaitable{TResult}"/>.</summary>
         [StructLayout(LayoutKind.Auto)]
-        public readonly struct ConfiguredValueTaskAwaiter : ICriticalNotifyCompletion2<TResult>, IStateMachineBoxAwareAwaiter
+        public readonly struct ConfiguredValueTaskAwaiter : ICriticalNotifyCompletion, IStateMachineBoxAwareAwaiter
         {
             /// <summary>The value being awaited.</summary>
             private readonly ValueTask<TResult> _value;
diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/INotifyCompletion.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/INotifyCompletion.cs
index d97d93ed869db3..ff00892da828f8 100644
--- a/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/INotifyCompletion.cs
+++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/INotifyCompletion.cs
@@ -32,32 +32,4 @@ public interface ICriticalNotifyCompletion : INotifyCompletion
         /// <remarks>Unlike OnCompleted, UnsafeOnCompleted need not propagate ExecutionContext information.</remarks>
         void UnsafeOnCompleted(Action continuation);
     }
-
-    /// <summary>
-    /// Represents an awaiter used to schedule continuations when an await operation completes. Provides access to the complete api surface for awaiting
-    /// </summary>
-    public interface ICriticalNotifyCompletion2 : ICriticalNotifyCompletion
-    {
-        bool IsCompleted { get; }
-        void GetResult();
-    }
-    public interface ICriticalNotifyCompletion2<TResult> : ICriticalNotifyCompletion
-    {
-        bool IsCompleted { get; }
-        TResult GetResult();
-    }
-
-    /// <summary>
-    /// Represents an awaiter used to schedule continuations when an await operation completes. Provides access to the complete api surface for awaiting
-    /// </summary>
-    public interface INotifyCompletion2 : INotifyCompletion
-    {
-        bool IsCompleted { get; }
-        void GetResult();
-    }
-    public interface INotifyCompletion2<TResult> : INotifyCompletion
-    {
-        bool IsCompleted { get; }
-        TResult GetResult();
-    }
 }
diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.cs
index f4eda7aff82683..3bb06376095e03 100644
--- a/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.cs
+++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.cs
@@ -119,117 +119,46 @@ internal static bool IsPrimitiveType(this CorElementType et)
 #pragma warning restore IDE0060
 
 #if !NATIVEAOT
-
-        // TODO, this method should be marked so that it is only callable from a runtime async method
-        public static TResult UnsafeAwaitAwaiterFromRuntimeAsync<TResult, TAwaiter>(TAwaiter awaiter) where TAwaiter : ICriticalNotifyCompletion2<TResult>
+        public static void AwaitAwaiterFromRuntimeAsync<TAwaiter>(TAwaiter awaiter) where TAwaiter : INotifyCompletion
         {
-            if (!awaiter.IsCompleted)
+            StackCrawlMark stackMark = StackCrawlMark.LookForMe;
+
+            // Create resumption delegate, wrapping task, and create tasklets to represent each stack frame on the stack.
+            // RuntimeTaskSuspender.GetOrCreateResumptionDelegate() works like a POSIX fork call in that calls to it will return a
+            // delegate if they are the initial call to GetOrCreateResumptionDelegate, but once the thread is resumed,
+            // it will resume with a return value of null.
+            Action? resumption = RuntimeHelpers.GetOrCreateResumptionDelegate(ref stackMark);
+            if (resumption != null)
             {
-                StackCrawlMark stackMark = StackCrawlMark.LookForMe;
-
-                // Create resumption delegate, wrapping task, and create tasklets to represent each stack frame on the stack.
-                // RuntimeTaskSuspender.GetOrCreateResumptionDelegate() works like a POSIX fork call in that calls to it will return a
-                // delegate if they are the initial call to GetOrCreateResumptionDelegate, but once the thread is resumed,
-                // it will resume with a return value of null.
-                Action? resumption = RuntimeHelpers.GetOrCreateResumptionDelegate(ref stackMark);
-                if (resumption != null)
-                {
-                    // If we reach here, the only way that we actually run follow on code is for the continuation to actually run,
-                    // and return from GetOrCreateResumptionDelegate with a null return value.
-                    ref AsyncDataFrame asyncFrame = ref GetCurrentAsyncDataFrame();
-                    RuntimeAsyncMaintainedData maintainedData = asyncFrame._maintainedData!;
-                    maintainedData._awaiter = awaiter;
-                    // This function must be called from the same function that has the stackmark in it.
-                    unsafe { UnwindToFunctionWithAsyncFrame(maintainedData._nextTasklet, maintainedData._suspendActive); }
-                }
+                // If we reach here, the only way that we actually run follow on code is for the continuation to actually run,
+                // and return from GetOrCreateResumptionDelegate with a null return value.
+                ref AsyncDataFrame asyncFrame = ref GetCurrentAsyncDataFrame();
+                RuntimeAsyncMaintainedData maintainedData = asyncFrame._maintainedData!;
+                maintainedData._awaiter = awaiter;
+                // This function must be called from the same function that has the stackmark in it.
+                unsafe { UnwindToFunctionWithAsyncFrame(maintainedData._nextTasklet, maintainedData._suspendActive); }
             }
-
-            // Get the result from the awaiter, or throw the exception stored in the Task
-            return awaiter.GetResult();
         }
 
-        // TODO, this method should be marked so that it is only callable from a runtime async method
-        public static TResult AwaitAwaiterFromRuntimeAsync<TResult, TAwaiter>(TAwaiter awaiter) where TAwaiter : INotifyCompletion2<TResult>
+        public static void UnsafeAwaitAwaiterFromRuntimeAsync<TAwaiter>(TAwaiter awaiter) where TAwaiter : ICriticalNotifyCompletion
         {
-            if (!awaiter.IsCompleted)
+            StackCrawlMark stackMark = StackCrawlMark.LookForMe;
+
+            // Create resumption delegate, wrapping task, and create tasklets to represent each stack frame on the stack.
+            // RuntimeTaskSuspender.GetOrCreateResumptionDelegate() works like a POSIX fork call in that calls to it will return a
+            // delegate if they are the initial call to GetOrCreateResumptionDelegate, but once the thread is resumed,
+            // it will resume with a return value of null.
+            Action? resumption = RuntimeHelpers.GetOrCreateResumptionDelegate(ref stackMark);
+            if (resumption != null)
             {
-                StackCrawlMark stackMark = StackCrawlMark.LookForMe;
-
-                // Create resumption delegate, wrapping task, and create tasklets to represent each stack frame on the stack.
-                // RuntimeTaskSuspender.GetOrCreateResumptionDelegate() works like a POSIX fork call in that calls to it will return a
-                // delegate if they are the initial call to GetOrCreateResumptionDelegate, but once the thread is resumed,
-                // it will resume with a return value of null.
-                Action? resumption = RuntimeHelpers.GetOrCreateResumptionDelegate(ref stackMark);
-                if (resumption != null)
-                {
-                    // If we reach here, the only way that we actually run follow on code is for the continuation to actually run,
-                    // and return from GetOrCreateResumptionDelegate with a null return value.
-                    ref AsyncDataFrame asyncFrame = ref GetCurrentAsyncDataFrame();
-                    RuntimeAsyncMaintainedData maintainedData = asyncFrame._maintainedData!;
-                    maintainedData._awaiter = awaiter;
-                    // This function must be called from the same function that has the stackmark in it.
-                    unsafe { UnwindToFunctionWithAsyncFrame(maintainedData._nextTasklet, maintainedData._suspendActive); }
-                }
+                // If we reach here, the only way that we actually run follow on code is for the continuation to actually run,
+                // and return from GetOrCreateResumptionDelegate with a null return value.
+                ref AsyncDataFrame asyncFrame = ref GetCurrentAsyncDataFrame();
+                RuntimeAsyncMaintainedData maintainedData = asyncFrame._maintainedData!;
+                maintainedData._awaiter = awaiter;
+                // This function must be called from the same function that has the stackmark in it.
+                unsafe { UnwindToFunctionWithAsyncFrame(maintainedData._nextTasklet, maintainedData._suspendActive); }
             }
-
-            // Get the result from the awaiter, or throw the exception stored in the Task
-            return awaiter.GetResult();
-        }
-
-        // TODO, this method should be marked so that it is only callable from a runtime async method
-        public static void UnsafeAwaitAwaiterFromRuntimeAsync<TAwaiter>(TAwaiter awaiter) where TAwaiter : ICriticalNotifyCompletion2
-        {
-            if (!awaiter.IsCompleted)
-            {
-                StackCrawlMark stackMark = StackCrawlMark.LookForMe;
-
-                // Create resumption delegate, wrapping task, and create tasklets to represent each stack frame on the stack.
-                // RuntimeTaskSuspender.GetOrCreateResumptionDelegate() works like a POSIX fork call in that calls to it will return a
-                // delegate if they are the initial call to GetOrCreateResumptionDelegate, but once the thread is resumed,
-                // it will resume with a return value of null.
-                Action? resumption = RuntimeHelpers.GetOrCreateResumptionDelegate(ref stackMark);
-                if (resumption != null)
-                {
-                    // If we reach here, the only way that we actually run follow on code is for the continuation to actually run,
-                    // and return from GetOrCreateResumptionDelegate with a null return value.
-                    ref AsyncDataFrame asyncFrame = ref GetCurrentAsyncDataFrame();
-                    RuntimeAsyncMaintainedData maintainedData = asyncFrame._maintainedData!;
-                    maintainedData._awaiter = awaiter;
-                    // This function must be called from the same function that has the stackmark in it.
-                    unsafe { UnwindToFunctionWithAsyncFrame(maintainedData._nextTasklet, maintainedData._suspendActive); }
-                }
-            }
-
-            // Get the result from the awaiter, or throw the exception stored in the Task
-            awaiter.GetResult();
-        }
-
-        // TODO, this method should be marked so that it is only callable from a runtime async method
-        public static void AwaitAwaiterFromRuntimeAsync<TAwaiter>(TAwaiter awaiter) where TAwaiter : INotifyCompletion2
-        {
-            if (!awaiter.IsCompleted)
-            {
-                StackCrawlMark stackMark = StackCrawlMark.LookForMe;
-
-                // Create resumption delegate, wrapping task, and create tasklets to represent each stack frame on the stack.
-                // RuntimeTaskSuspender.GetOrCreateResumptionDelegate() works like a POSIX fork call in that calls to it will return a
-                // delegate if they are the initial call to GetOrCreateResumptionDelegate, but once the thread is resumed,
-                // it will resume with a return value of null.
-                Action? resumption = RuntimeHelpers.GetOrCreateResumptionDelegate(ref stackMark);
-                if (resumption != null)
-                {
-                    // If we reach here, the only way that we actually run follow on code is for the continuation to actually run,
-                    // and return from GetOrCreateResumptionDelegate with a null return value.
-                    ref AsyncDataFrame asyncFrame = ref GetCurrentAsyncDataFrame();
-                    RuntimeAsyncMaintainedData maintainedData = asyncFrame._maintainedData!;
-                    maintainedData._awaiter = awaiter;
-                    // This function must be called from the same function that has the stackmark in it.
-                    unsafe { UnwindToFunctionWithAsyncFrame(maintainedData._nextTasklet, maintainedData._suspendActive); }
-                }
-            }
-
-            // Get the result from the awaiter, or throw the exception stored in the Task
-            awaiter.GetResult();
         }
 
         [ThreadStatic]
diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/TaskAwaiter.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/TaskAwaiter.cs
index 8a5b77ee05c9bc..0e3f0ec71b6b90 100644
--- a/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/TaskAwaiter.cs
+++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/TaskAwaiter.cs
@@ -17,7 +17,7 @@
 namespace System.Runtime.CompilerServices
 {
     /// <summary>Provides an awaiter for awaiting a <see cref="Task"/>.</summary>
-    public readonly struct TaskAwaiter : ICriticalNotifyCompletion2, ITaskAwaiter
+    public readonly struct TaskAwaiter : ICriticalNotifyCompletion, ITaskAwaiter
     {
         // WARNING: Unsafe.As is used to access the generic TaskAwaiter<> as TaskAwaiter.
         // Its layout must remain the same.
@@ -283,7 +283,7 @@ private static Action OutputWaitEtwEvents(Task task, Action continuation)
     }
 
     /// <summary>Provides an awaiter for awaiting a <see cref="Task{TResult}"/>.</summary>
-    public readonly struct TaskAwaiter<TResult> : ICriticalNotifyCompletion2<TResult>, ITaskAwaiter
+    public readonly struct TaskAwaiter<TResult> : ICriticalNotifyCompletion, ITaskAwaiter
     {
         // WARNING: Unsafe.As is used to access TaskAwaiter<> as the non-generic TaskAwaiter.
         // Its layout must remain the same.
@@ -376,7 +376,7 @@ public ConfiguredTaskAwaiter GetAwaiter()
 
         /// <summary>Provides an awaiter for a <see cref="ConfiguredTaskAwaitable"/>.</summary>
         /// <remarks>This type is intended for compiler use only.</remarks>
-        public readonly struct ConfiguredTaskAwaiter : ICriticalNotifyCompletion2, IConfiguredTaskAwaiter
+        public readonly struct ConfiguredTaskAwaiter : ICriticalNotifyCompletion, IConfiguredTaskAwaiter
         {
             // WARNING: Unsafe.As is used to access the generic ConfiguredTaskAwaiter as this.
             // Its layout must remain the same.
@@ -458,7 +458,7 @@ public ConfiguredTaskAwaiter GetAwaiter()
 
         /// <summary>Provides an awaiter for a <see cref="ConfiguredTaskAwaitable{TResult}"/>.</summary>
         /// <remarks>This type is intended for compiler use only.</remarks>
-        public readonly struct ConfiguredTaskAwaiter : ICriticalNotifyCompletion2<TResult>, IConfiguredTaskAwaiter
+        public readonly struct ConfiguredTaskAwaiter : ICriticalNotifyCompletion, IConfiguredTaskAwaiter
         {
             // WARNING: Unsafe.As is used to access this as the non-generic ConfiguredTaskAwaiter.
             // Its layout must remain the same.
diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/ValueTaskAwaiter.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/ValueTaskAwaiter.cs
index ba5aa6f10872a7..c242ad3f2ad203 100644
--- a/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/ValueTaskAwaiter.cs
+++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/ValueTaskAwaiter.cs
@@ -9,7 +9,7 @@
 namespace System.Runtime.CompilerServices
 {
     /// <summary>Provides an awaiter for a <see cref="ValueTask"/>.</summary>
-    public readonly struct ValueTaskAwaiter : ICriticalNotifyCompletion2, IStateMachineBoxAwareAwaiter
+    public readonly struct ValueTaskAwaiter : ICriticalNotifyCompletion, IStateMachineBoxAwareAwaiter
     {
         /// <summary>Shim used to invoke an <see cref="Action"/> passed as the state argument to a <see cref="Action{Object}"/>.</summary>
         internal static readonly Action<object?> s_invokeActionDelegate = static state =>
@@ -104,7 +104,7 @@ void IStateMachineBoxAwareAwaiter.AwaitUnsafeOnCompleted(IAsyncStateMachineBox b
     }
 
     /// <summary>Provides an awaiter for a <see cref="ValueTask{TResult}"/>.</summary>
-    public readonly struct ValueTaskAwaiter<TResult> : ICriticalNotifyCompletion2<TResult>, IStateMachineBoxAwareAwaiter
+    public readonly struct ValueTaskAwaiter<TResult> : ICriticalNotifyCompletion, IStateMachineBoxAwareAwaiter
     {
         /// <summary>The value being awaited.</summary>
         private readonly ValueTask<TResult> _value;
diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/YieldAwaitable.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/YieldAwaitable.cs
index 5ddd4fd00f3628..d9bea6410babbb 100644
--- a/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/YieldAwaitable.cs
+++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/YieldAwaitable.cs
@@ -43,7 +43,7 @@ public readonly struct YieldAwaitable
 
         /// <summary>Provides an awaiter that switches into a target environment.</summary>
         /// <remarks>This type is intended for compiler use only.</remarks>
-        public readonly struct YieldAwaiter : ICriticalNotifyCompletion2, IStateMachineBoxAwareAwaiter
+        public readonly struct YieldAwaiter : ICriticalNotifyCompletion, IStateMachineBoxAwareAwaiter
         {
             /// <summary>Gets whether a yield is not required.</summary>
             /// <remarks>This property is intended for compiler user rather than use directly in code.</remarks>
diff --git a/src/libraries/System.Runtime/ref/System.Runtime.cs b/src/libraries/System.Runtime/ref/System.Runtime.cs
index b7e9ba512a1ec1..72ef20958c50aa 100644
--- a/src/libraries/System.Runtime/ref/System.Runtime.cs
+++ b/src/libraries/System.Runtime/ref/System.Runtime.cs
@@ -12699,7 +12699,7 @@ public readonly partial struct ConfiguredTaskAwaitable
         private readonly object _dummy;
         private readonly int _dummyPrimitive;
         public System.Runtime.CompilerServices.ConfiguredTaskAwaitable.ConfiguredTaskAwaiter GetAwaiter() { throw null; }
-        public readonly partial struct ConfiguredTaskAwaiter : System.Runtime.CompilerServices.ICriticalNotifyCompletion2, System.Runtime.CompilerServices.INotifyCompletion
+        public readonly partial struct ConfiguredTaskAwaiter : System.Runtime.CompilerServices.ICriticalNotifyCompletion, System.Runtime.CompilerServices.INotifyCompletion
         {
             private readonly object _dummy;
             private readonly int _dummyPrimitive;
@@ -12714,7 +12714,7 @@ public readonly partial struct ConfiguredTaskAwaitable<TResult>
         private readonly object _dummy;
         private readonly int _dummyPrimitive;
         public System.Runtime.CompilerServices.ConfiguredTaskAwaitable<TResult>.ConfiguredTaskAwaiter GetAwaiter() { throw null; }
-        public readonly partial struct ConfiguredTaskAwaiter : System.Runtime.CompilerServices.ICriticalNotifyCompletion2<TResult>, System.Runtime.CompilerServices.INotifyCompletion
+        public readonly partial struct ConfiguredTaskAwaiter : System.Runtime.CompilerServices.ICriticalNotifyCompletion, System.Runtime.CompilerServices.INotifyCompletion
         {
             private readonly object _dummy;
             private readonly int _dummyPrimitive;
@@ -12729,7 +12729,7 @@ public readonly partial struct ConfiguredValueTaskAwaitable
         private readonly object _dummy;
         private readonly int _dummyPrimitive;
         public System.Runtime.CompilerServices.ConfiguredValueTaskAwaitable.ConfiguredValueTaskAwaiter GetAwaiter() { throw null; }
-        public readonly partial struct ConfiguredValueTaskAwaiter : System.Runtime.CompilerServices.ICriticalNotifyCompletion2, System.Runtime.CompilerServices.INotifyCompletion
+        public readonly partial struct ConfiguredValueTaskAwaiter : System.Runtime.CompilerServices.ICriticalNotifyCompletion, System.Runtime.CompilerServices.INotifyCompletion
         {
             private readonly object _dummy;
             private readonly int _dummyPrimitive;
@@ -12744,7 +12744,7 @@ public readonly partial struct ConfiguredValueTaskAwaitable<TResult>
         private readonly object _dummy;
         private readonly int _dummyPrimitive;
         public System.Runtime.CompilerServices.ConfiguredValueTaskAwaitable<TResult>.ConfiguredValueTaskAwaiter GetAwaiter() { throw null; }
-        public readonly partial struct ConfiguredValueTaskAwaiter : System.Runtime.CompilerServices.ICriticalNotifyCompletion2<TResult>, System.Runtime.CompilerServices.INotifyCompletion
+        public readonly partial struct ConfiguredValueTaskAwaiter : System.Runtime.CompilerServices.ICriticalNotifyCompletion, System.Runtime.CompilerServices.INotifyCompletion
         {
             private readonly object _dummy;
             private readonly int _dummyPrimitive;
@@ -12859,16 +12859,6 @@ public partial interface ICriticalNotifyCompletion : System.Runtime.CompilerServ
     {
         void UnsafeOnCompleted(System.Action continuation);
     }
-    public partial interface ICriticalNotifyCompletion2 : ICriticalNotifyCompletion
-    {
-        bool IsCompleted { get; }
-        void GetResult();
-    }
-    public partial interface ICriticalNotifyCompletion2<TResult> : ICriticalNotifyCompletion
-    {
-        bool IsCompleted { get; }
-        TResult GetResult();
-    }
     [System.AttributeUsageAttribute(System.AttributeTargets.Property, Inherited=true)]
     public sealed partial class IndexerNameAttribute : System.Attribute
     {
@@ -12878,16 +12868,6 @@ public partial interface INotifyCompletion
     {
         void OnCompleted(System.Action continuation);
     }
-    public partial interface INotifyCompletion2 : INotifyCompletion
-    {
-        bool IsCompleted { get; }
-        void GetResult();
-    }
-    public partial interface INotifyCompletion2<TResult> : INotifyCompletion
-    {
-        bool IsCompleted { get; }
-        TResult GetResult();
-    }
     [System.AttributeUsageAttribute(System.AttributeTargets.Assembly, AllowMultiple=true, Inherited=false)]
     public sealed partial class InternalsVisibleToAttribute : System.Attribute
     {
@@ -13129,10 +13109,8 @@ public static void RunModuleConstructor(System.ModuleHandle module) { }
         public static bool TryEnsureSufficientExecutionStack() { throw null; }
         public delegate void CleanupCode(object? userData, bool exceptionThrown);
         public delegate void TryCode(object? userData);
-        public static TResult UnsafeAwaitAwaiterFromRuntimeAsync<TResult, TAwaiter>(TAwaiter awaiter) where TAwaiter : ICriticalNotifyCompletion2<TResult> { throw null; }
-        public static void UnsafeAwaitAwaiterFromRuntimeAsync<TAwaiter>(TAwaiter awaiter) where TAwaiter : ICriticalNotifyCompletion2 { }
-        public static TResult AwaitAwaiterFromRuntimeAsync<TResult, TAwaiter>(TAwaiter awaiter) where TAwaiter : INotifyCompletion2<TResult> { throw null; }
-        public static void AwaitAwaiterFromRuntimeAsync<TAwaiter>(TAwaiter awaiter) where TAwaiter : INotifyCompletion2 { }
+        public static void UnsafeAwaitAwaiterFromRuntimeAsync<TAwaiter>(TAwaiter awaiter) where TAwaiter : ICriticalNotifyCompletion { }
+        public static void AwaitAwaiterFromRuntimeAsync<TAwaiter>(TAwaiter awaiter) where TAwaiter : INotifyCompletion { }
     }
     public sealed partial class RuntimeWrappedException : System.Exception
     {
@@ -13195,7 +13173,7 @@ public SwitchExpressionException(string? message, System.Exception? innerExcepti
         [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)]
         public override void GetObjectData(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) { }
     }
-    public readonly partial struct TaskAwaiter : System.Runtime.CompilerServices.ICriticalNotifyCompletion2, System.Runtime.CompilerServices.INotifyCompletion
+    public readonly partial struct TaskAwaiter : System.Runtime.CompilerServices.ICriticalNotifyCompletion, System.Runtime.CompilerServices.INotifyCompletion
     {
         private readonly object _dummy;
         private readonly int _dummyPrimitive;
@@ -13204,7 +13182,7 @@ public void GetResult() { }
         public void OnCompleted(System.Action continuation) { }
         public void UnsafeOnCompleted(System.Action continuation) { }
     }
-    public readonly partial struct TaskAwaiter<TResult> : System.Runtime.CompilerServices.ICriticalNotifyCompletion2<TResult>, System.Runtime.CompilerServices.INotifyCompletion
+    public readonly partial struct TaskAwaiter<TResult> : System.Runtime.CompilerServices.ICriticalNotifyCompletion, System.Runtime.CompilerServices.INotifyCompletion
     {
         private readonly object _dummy;
         private readonly int _dummyPrimitive;
@@ -13321,7 +13299,7 @@ public sealed partial class UnsafeValueTypeAttribute : System.Attribute
     {
         public UnsafeValueTypeAttribute() { }
     }
-    public readonly partial struct ValueTaskAwaiter : System.Runtime.CompilerServices.ICriticalNotifyCompletion2, System.Runtime.CompilerServices.INotifyCompletion
+    public readonly partial struct ValueTaskAwaiter : System.Runtime.CompilerServices.ICriticalNotifyCompletion, System.Runtime.CompilerServices.INotifyCompletion
     {
         private readonly object _dummy;
         private readonly int _dummyPrimitive;
@@ -13330,7 +13308,7 @@ public void GetResult() { }
         public void OnCompleted(System.Action continuation) { }
         public void UnsafeOnCompleted(System.Action continuation) { }
     }
-    public readonly partial struct ValueTaskAwaiter<TResult> : System.Runtime.CompilerServices.ICriticalNotifyCompletion2<TResult>, System.Runtime.CompilerServices.INotifyCompletion
+    public readonly partial struct ValueTaskAwaiter<TResult> : System.Runtime.CompilerServices.ICriticalNotifyCompletion, System.Runtime.CompilerServices.INotifyCompletion
     {
         private readonly object _dummy;
         private readonly int _dummyPrimitive;
@@ -13342,7 +13320,7 @@ public void UnsafeOnCompleted(System.Action continuation) { }
     public readonly partial struct YieldAwaitable
     {
         public System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter GetAwaiter() { throw null; }
-        public readonly partial struct YieldAwaiter : System.Runtime.CompilerServices.ICriticalNotifyCompletion2, System.Runtime.CompilerServices.INotifyCompletion
+        public readonly partial struct YieldAwaiter : System.Runtime.CompilerServices.ICriticalNotifyCompletion, System.Runtime.CompilerServices.INotifyCompletion
         {
             public bool IsCompleted { get { throw null; } }
             public void GetResult() { }
diff --git a/src/tests/Loader/async/runtimetask-asyncfibonacci-with-yields.il b/src/tests/Loader/async/runtimetask-asyncfibonacci-with-yields.il
index b081a484996394..97e602741eb53a 100644
--- a/src/tests/Loader/async/runtimetask-asyncfibonacci-with-yields.il
+++ b/src/tests/Loader/async/runtimetask-asyncfibonacci-with-yields.il
@@ -349,7 +349,8 @@
            bool V_2,
            bool V_3,
            uint32 V_4,
-           valuetype [System.Runtime]System.Runtime.CompilerServices.YieldAwaitable V_5)
+           valuetype [System.Runtime]System.Runtime.CompilerServices.YieldAwaitable V_5,
+           valuetype [System.Runtime]System.Runtime.CompilerServices.YieldAwaitable/YieldAwaiter V_6)
   IL_0000:  nop
   IL_0001:  ldarg.0
   IL_0002:  stloc.0
@@ -367,7 +368,14 @@
             stloc 5
             ldloca 5
             call       instance valuetype [System.Runtime]System.Runtime.CompilerServices.YieldAwaitable/YieldAwaiter [System.Runtime]System.Runtime.CompilerServices.YieldAwaitable::GetAwaiter()
-  IL_001c:  call       void [System.Runtime]System.Runtime.CompilerServices.RuntimeHelpers::UnsafeAwaitAwaiterFromRuntimeAsync<valuetype [System.Runtime]System.Runtime.CompilerServices.YieldAwaitable/YieldAwaiter>(!!0)
+            stloc 6
+            ldloca     6
+            call instance bool [System.Runtime]System.Runtime.CompilerServices.YieldAwaitable/YieldAwaiter::get_IsCompleted()
+            brtrue IL_FirstAwaitFinished
+            ldloc 6
+            call       void [System.Runtime]System.Runtime.CompilerServices.RuntimeHelpers::UnsafeAwaitAwaiterFromRuntimeAsync<valuetype [System.Runtime]System.Runtime.CompilerServices.YieldAwaitable/YieldAwaiter>(!!0)
+  IL_FirstAwaitFinished: ldloca 6
+            call instance void [System.Runtime]System.Runtime.CompilerServices.YieldAwaitable/YieldAwaiter::GetResult()
   IL_001d:  ldloc.0
   IL_001e:  ldc.i4     0x773593ed
   IL_0023:  mul
@@ -382,7 +390,14 @@
             stloc 5
             ldloca 5
             call       instance valuetype [System.Runtime]System.Runtime.CompilerServices.YieldAwaitable/YieldAwaiter [System.Runtime]System.Runtime.CompilerServices.YieldAwaitable::GetAwaiter()
-  IL_0036:  call       void [System.Runtime]System.Runtime.CompilerServices.RuntimeHelpers::UnsafeAwaitAwaiterFromRuntimeAsync<valuetype [System.Runtime]System.Runtime.CompilerServices.YieldAwaitable/YieldAwaiter>(!!0)
+            stloc 6
+            ldloca     6
+            call instance bool [System.Runtime]System.Runtime.CompilerServices.YieldAwaitable/YieldAwaiter::get_IsCompleted()
+            brtrue IL_SecondAwaitFinished
+            ldloc 6
+            call       void [System.Runtime]System.Runtime.CompilerServices.RuntimeHelpers::UnsafeAwaitAwaiterFromRuntimeAsync<valuetype [System.Runtime]System.Runtime.CompilerServices.YieldAwaitable/YieldAwaiter>(!!0)
+  IL_SecondAwaitFinished: ldloca 6
+            call instance void [System.Runtime]System.Runtime.CompilerServices.YieldAwaitable/YieldAwaiter::GetResult()
   IL_0037:  ldloc.0
   IL_0038:  ldc.i4     0x773593ed
   IL_003d:  mul
@@ -397,7 +412,14 @@
             stloc 5
             ldloca 5
             call       instance valuetype [System.Runtime]System.Runtime.CompilerServices.YieldAwaitable/YieldAwaiter [System.Runtime]System.Runtime.CompilerServices.YieldAwaitable::GetAwaiter()
-  IL_0050:  call       void [System.Runtime]System.Runtime.CompilerServices.RuntimeHelpers::UnsafeAwaitAwaiterFromRuntimeAsync<valuetype [System.Runtime]System.Runtime.CompilerServices.YieldAwaitable/YieldAwaiter>(!!0)
+            stloc 6
+            ldloca     6
+            call instance bool [System.Runtime]System.Runtime.CompilerServices.YieldAwaitable/YieldAwaiter::get_IsCompleted()
+            brtrue IL_ThirdAwaitFinished
+            ldloc 6
+            call       void [System.Runtime]System.Runtime.CompilerServices.RuntimeHelpers::UnsafeAwaitAwaiterFromRuntimeAsync<valuetype [System.Runtime]System.Runtime.CompilerServices.YieldAwaitable/YieldAwaiter>(!!0)
+  IL_ThirdAwaitFinished: ldloca 6
+            call instance void [System.Runtime]System.Runtime.CompilerServices.YieldAwaitable/YieldAwaiter::GetResult()
   IL_0051:  ldloc.0
   IL_0052:  stloc.s    V_4
   IL_0054:  br.s       IL_0056

From 7645bfda4588c47eba3eb5c1b74ef80875a75ed0 Mon Sep 17 00:00:00 2001
From: David Wrighton <davidwr@microsoft.com>
Date: Mon, 23 Oct 2023 22:38:54 +0000
Subject: [PATCH 038/203] Merged PR 34743: Update compiler and such

---
 .gitignore                                    |  3 +
 NuGet.config                                  |  2 +
 README.md                                     |  3 +
 buildroslynnugets.cmd                         | 31 +++++++
 eng/Versions.props                            |  6 +-
 src/coreclr/vm/runtimesuspension.cpp          | 11 +--
 .../async/async2fibonacci-with-yields.cs      |  6 +-
 .../async/async2fibonacci-with-yields.csproj  |  1 +
 ...timetask-asyncfibonacci-with-yields.ilproj |  1 +
 ...etask-asyncfibonacci-without-yields.ilproj |  1 +
 .../Loader/async/syncfibonacci-with-yields.cs | 83 +++++++++++--------
 .../async/syncfibonacci-with-yields.csproj    |  1 +
 .../async/syncfibonacci-without-yields.cs     | 65 ++++++++-------
 .../async/syncfibonacci-without-yields.csproj |  1 +
 .../taskbased-asyncfibonacci-with-yields.cs   | 80 ++++++++++--------
 ...askbased-asyncfibonacci-with-yields.csproj |  1 +
 ...taskbased-asyncfibonacci-without-yields.cs | 74 ++++++++++-------
 ...based-asyncfibonacci-without-yields.csproj |  1 +
 ...luetaskbased-asyncfibonacci-with-yields.cs | 81 ++++++++++--------
 ...askbased-asyncfibonacci-with-yields.csproj |  1 +
 ...taskbased-asyncfibonacci-without-yields.cs | 74 ++++++++++-------
 ...based-asyncfibonacci-without-yields.csproj |  1 +
 22 files changed, 317 insertions(+), 211 deletions(-)
 create mode 100644 buildroslynnugets.cmd

diff --git a/.gitignore b/.gitignore
index 71943be3a350db..c67ea693e48854 100644
--- a/.gitignore
+++ b/.gitignore
@@ -11,6 +11,9 @@ syntax: glob
 .packages
 .tools
 
+# nuget packages for custom roslyn
+roslynpackages
+
 # User-specific files
 *.suo
 *.user
diff --git a/NuGet.config b/NuGet.config
index 4ec29de552c2b6..79e9bf75849489 100644
--- a/NuGet.config
+++ b/NuGet.config
@@ -22,6 +22,8 @@
     <add key="dotnet8-transport" value="https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet8-transport/nuget/v3/index.json" />
     <add key="dotnet9" value="https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet9/nuget/v3/index.json" />
     <add key="dotnet9-transport" value="https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet9-transport/nuget/v3/index.json" />
+
+    <add key="async-compiler" value="roslynpackages" />
   </packageSources>
   <disabledPackageSources>
     <clear />
diff --git a/README.md b/README.md
index ea640f804eb1d4..698c2da19e9534 100644
--- a/README.md
+++ b/README.md
@@ -1,5 +1,8 @@
 # .NET Runtime
 
+In order to build in this repo, you must have set up a Roslyn repo parallel to this repo with the name dotnet-roslyn, and it must have a remote called AzDo which has the updated compiler in it. Then you must run buildroslynnugets.cmd to create a local copy of the compiler for use in the repo. The equivalent work has not yet been done for running on Unix-like platforms.
+
+
 [![Build Status](https://dev.azure.com/dnceng-public/public/_apis/build/status/dotnet/runtime/runtime?branchName=main)](https://dev.azure.com/dnceng-public/public/_build/latest?definitionId=129&branchName=main)
 [![Help Wanted](https://img.shields.io/github/issues/dotnet/runtime/help%20wanted?style=flat-square&color=%232EA043&label=help%20wanted)](https://github.com/dotnet/runtime/labels/help%20wanted)
 [![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/dotnet/runtime)
diff --git a/buildroslynnugets.cmd b/buildroslynnugets.cmd
new file mode 100644
index 00000000000000..14b5c47699b78d
--- /dev/null
+++ b/buildroslynnugets.cmd
@@ -0,0 +1,31 @@
+setlocal ENABLEEXTENSIONS
+pushd %~dp0
+
+set ASYNC_SUFFIX=async-2
+
+cd ..
+pushd dotnet-roslyn
+
+git fetch AzDo dev/vsadov/a2
+rem when updating this, make sure to update the ASYNC_SUFFIX above and the versions.props file
+git checkout 1c35e4f42732cfd58bb2a56bbbd85d8200497fa2
+
+call restore.cmd
+call build.cmd -c release
+
+call dotnet pack src\NuGet\Microsoft.Net.Compilers.Toolset\AnyCpu\Microsoft.Net.Compilers.Toolset.Package.csproj --version-suffix %ASYNC_SUFFIX%
+call dotnet pack src\Workspaces\CSharp\Portable\Microsoft.CodeAnalysis.CSharp.Workspaces.csproj --version-suffix %ASYNC_SUFFIX%
+call dotnet pack src\Compilers\CSharp\Portable\Microsoft.CodeAnalysis.CSharp.csproj --version-suffix %ASYNC_SUFFIX%
+call dotnet pack src\Compilers\Core\Portable\Microsoft.CodeAnalysis.csproj --version-suffix %ASYNC_SUFFIX%
+call dotnet pack src\Workspaces\Core\Portable\Microsoft.CodeAnalysis.Workspaces.csproj --version-suffix %ASYNC_SUFFIX%
+
+pushd %~dp0
+
+md roslynpackages
+
+copy ..\dotnet-roslyn\artifacts\packages\Release\Shipping\Microsoft.Net.Compilers.Toolset.4.9.0-%ASYNC_SUFFIX%.nupkg roslynpackages
+copy ..\dotnet-roslyn\artifacts\packages\Release\Shipping\Microsoft.CodeAnalysis.Workspaces.Common.4.9.0-%ASYNC_SUFFIX%.nupkg roslynpackages
+copy ..\dotnet-roslyn\artifacts\packages\Release\Shipping\Microsoft.CodeAnalysis.CSharp.Workspaces.4.9.0-%ASYNC_SUFFIX%.nupkg roslynpackages
+copy ..\dotnet-roslyn\artifacts\packages\Release\Shipping\Microsoft.CodeAnalysis.CSharp.4.9.0-%ASYNC_SUFFIX%.nupkg roslynpackages
+copy ..\dotnet-roslyn\artifacts\packages\Release\Shipping\Microsoft.CodeAnalysis.Common.4.9.0-%ASYNC_SUFFIX%.nupkg roslynpackages
+
diff --git a/eng/Versions.props b/eng/Versions.props
index 4d0a53ae2308ec..6e7df86ae6af93 100644
--- a/eng/Versions.props
+++ b/eng/Versions.props
@@ -39,9 +39,9 @@
       Any tools that contribute to the design-time experience should use the MicrosoftCodeAnalysisVersion_LatestVS property above to ensure
       they do not break the local dev experience.
     -->
-    <MicrosoftCodeAnalysisCSharpVersion>4.9.0-1.23517.15</MicrosoftCodeAnalysisCSharpVersion>
-    <MicrosoftCodeAnalysisVersion>4.9.0-1.23517.15</MicrosoftCodeAnalysisVersion>
-    <MicrosoftNetCompilersToolsetVersion>4.9.0-1.23517.15</MicrosoftNetCompilersToolsetVersion>
+    <MicrosoftCodeAnalysisCSharpVersion>4.9.0-async-2</MicrosoftCodeAnalysisCSharpVersion>
+    <MicrosoftCodeAnalysisVersion>4.9.0-async-2</MicrosoftCodeAnalysisVersion>
+    <MicrosoftNetCompilersToolsetVersion>4.9.0-async-2</MicrosoftNetCompilersToolsetVersion>
   </PropertyGroup>
   <!--
     For source generator support we need to target multiple versions of Roslyn in order to be able to run on older versions of Roslyn.
diff --git a/src/coreclr/vm/runtimesuspension.cpp b/src/coreclr/vm/runtimesuspension.cpp
index 6b2c51a44d2aa5..4492ac7a343207 100644
--- a/src/coreclr/vm/runtimesuspension.cpp
+++ b/src/coreclr/vm/runtimesuspension.cpp
@@ -588,18 +588,15 @@ void InitializeTasklets()
 {
     g_taskletCrst.Init(CrstLeafLock, CRST_UNSAFE_ANYMODE);
 
+    g_pTaskletSentinel = (Tasklet*)malloc(sizeof(Tasklet));
+    memset(g_pTaskletSentinel, 0, sizeof(Tasklet));
+    g_pTaskletSentinel->pTaskletNextInLiveList = g_pTaskletSentinel;
+    g_pTaskletSentinel->pTaskletPrevInLiveList = g_pTaskletSentinel;
 }
 
 void RegisterTasklet(Tasklet* pTasklet)
 {
     CrstHolder crstHolder(&g_taskletCrst);
-    if (g_pTaskletSentinel == NULL)
-    {
-        g_pTaskletSentinel = (Tasklet*)malloc(sizeof(Tasklet));
-        memset(g_pTaskletSentinel, 0, sizeof(Tasklet));
-        g_pTaskletSentinel->pTaskletNextInLiveList = g_pTaskletSentinel;
-        g_pTaskletSentinel->pTaskletPrevInLiveList = g_pTaskletSentinel;
-    }
 
     pTasklet->pTaskletNextInLiveList = g_pTaskletSentinel->pTaskletNextInLiveList;
     pTasklet->pTaskletPrevInLiveList = g_pTaskletSentinel;
diff --git a/src/tests/Loader/async/async2fibonacci-with-yields.cs b/src/tests/Loader/async/async2fibonacci-with-yields.cs
index 5e2c09a4926ce8..2ee89ea4f2d1ab 100644
--- a/src/tests/Loader/async/async2fibonacci-with-yields.cs
+++ b/src/tests/Loader/async/async2fibonacci-with-yields.cs
@@ -4,13 +4,15 @@
 using System;
 using System.Threading.Tasks;
 using System.Diagnostics;
+using Xunit;
 
-class Async2FibonacceWithYields
+public class Async2FibonacceWithYields
 {
     const uint Threshold = 1_000;
     static bool done;
 
-    public static void Main()
+    [Fact]
+    public static void Test()
     {
         AsyncEntry().Wait();
     }
diff --git a/src/tests/Loader/async/async2fibonacci-with-yields.csproj b/src/tests/Loader/async/async2fibonacci-with-yields.csproj
index f67acfa29a9565..2b9dfb186b22cd 100644
--- a/src/tests/Loader/async/async2fibonacci-with-yields.csproj
+++ b/src/tests/Loader/async/async2fibonacci-with-yields.csproj
@@ -1,6 +1,7 @@
 <Project Sdk="Microsoft.NET.Sdk">
   <PropertyGroup>
     <CLRTestPriority>1</CLRTestPriority>
+    <RequiresProcessIsolation>true</RequiresProcessIsolation>
   </PropertyGroup>
   <ItemGroup>
     <Compile Include="async2fibonacci-with-yields.cs" />
diff --git a/src/tests/Loader/async/runtimetask-asyncfibonacci-with-yields.ilproj b/src/tests/Loader/async/runtimetask-asyncfibonacci-with-yields.ilproj
index 24afb2e5b72dc1..6e1d7c1bd8dc60 100644
--- a/src/tests/Loader/async/runtimetask-asyncfibonacci-with-yields.ilproj
+++ b/src/tests/Loader/async/runtimetask-asyncfibonacci-with-yields.ilproj
@@ -1,6 +1,7 @@
 <Project Sdk="Microsoft.NET.Sdk.IL">
   <PropertyGroup>
     <CLRTestPriority>1</CLRTestPriority>
+    <RequiresProcessIsolation>true</RequiresProcessIsolation>
   </PropertyGroup>
   <ItemGroup>
     <Compile Include="runtimetask-asyncfibonacci-with-yields.il" />
diff --git a/src/tests/Loader/async/runtimetask-asyncfibonacci-without-yields.ilproj b/src/tests/Loader/async/runtimetask-asyncfibonacci-without-yields.ilproj
index bd17dd2bad5a7e..7b346fab866782 100644
--- a/src/tests/Loader/async/runtimetask-asyncfibonacci-without-yields.ilproj
+++ b/src/tests/Loader/async/runtimetask-asyncfibonacci-without-yields.ilproj
@@ -1,6 +1,7 @@
 <Project Sdk="Microsoft.NET.Sdk.IL">
   <PropertyGroup>
     <CLRTestPriority>1</CLRTestPriority>
+    <RequiresProcessIsolation>true</RequiresProcessIsolation>
   </PropertyGroup>
   <ItemGroup>
     <Compile Include="runtimetask-asyncfibonacci-without-yields.il" />
diff --git a/src/tests/Loader/async/syncfibonacci-with-yields.cs b/src/tests/Loader/async/syncfibonacci-with-yields.cs
index 9d668c9568367d..e8a76958b2bd89 100644
--- a/src/tests/Loader/async/syncfibonacci-with-yields.cs
+++ b/src/tests/Loader/async/syncfibonacci-with-yields.cs
@@ -4,45 +4,56 @@
 using System;
 ﻿using System.Diagnostics;
 using System.Threading;
+using System.Threading.Tasks;
 using System.Runtime.CompilerServices;
+using Xunit;
 
-const uint Threshold = 1_000;
-
-for (int i = 0; i < 10; i++)
-{
-    var sw = new Stopwatch();
-    sw.Start();
-    uint result = A(100_000_000);
-    Console.WriteLine($"{sw.ElapsedMilliseconds} ms result={result}");
-}
-
-static uint A(uint n)
-{
-    uint result = n;
-    for (uint i = 0; i < n; i++)
-        result = B(result);
-    return result;
-}
-
-static uint B(uint n)
+public class SyncFibonacciWithYields
 {
-    uint result = n;
-
-    result = result * 1_999_999_981;
-    if (result < Threshold)
-        MyYield();
-
-    result = result * 1_999_999_981;
-    if (result < Threshold)
-        MyYield();
-
-    result = result * 1_999_999_981;
-    if (result < Threshold)
-        MyYield();
+    const uint Threshold = 1_000;
+
+    [Fact]
+    public static void Test()
+    {
+        for (int i = 0; i < 10; i++)
+        {
+            var sw = new Stopwatch();
+            sw.Start();
+            uint result = A(100_000_000);
+            Console.WriteLine($"{sw.ElapsedMilliseconds} ms result={result}");
+        }
+    }
+
+    static uint A(uint n)
+    {
+        uint result = n;
+        for (uint i = 0; i < n; i++)
+            result = B(result);
+        return result;
+    }
+
+    static uint B(uint n)
+    {
+        uint result = n;
+
+        result = result * 1_999_999_981;
+        if (result < Threshold)
+            MyYield();
+
+        result = result * 1_999_999_981;
+        if (result < Threshold)
+            MyYield();
+
+        result = result * 1_999_999_981;
+        if (result < Threshold)
+            MyYield();
+
+        return result;
+    }
+
+    // Workaround for inlining of Thread.Yield inflating the caller's frame.
+    [MethodImpl(MethodImplOptions.NoInlining)]
+    static void MyYield() => Thread.Yield();
 
-    return result;
 }
 
-// Workaround for inlining of Thread.Yield inflating the caller's frame.
-[MethodImpl(MethodImplOptions.NoInlining)]
-static void MyYield() => Thread.Yield();
\ No newline at end of file
diff --git a/src/tests/Loader/async/syncfibonacci-with-yields.csproj b/src/tests/Loader/async/syncfibonacci-with-yields.csproj
index 92548675762273..d0f5bcb4f93392 100644
--- a/src/tests/Loader/async/syncfibonacci-with-yields.csproj
+++ b/src/tests/Loader/async/syncfibonacci-with-yields.csproj
@@ -1,6 +1,7 @@
 <Project Sdk="Microsoft.NET.Sdk">
   <PropertyGroup>
     <CLRTestPriority>1</CLRTestPriority>
+    <RequiresProcessIsolation>true</RequiresProcessIsolation>
   </PropertyGroup>
   <ItemGroup>
     <Compile Include="syncfibonacci-with-yields.cs" />
diff --git a/src/tests/Loader/async/syncfibonacci-without-yields.cs b/src/tests/Loader/async/syncfibonacci-without-yields.cs
index 9f9c480660f038..27c428ab57313a 100644
--- a/src/tests/Loader/async/syncfibonacci-without-yields.cs
+++ b/src/tests/Loader/async/syncfibonacci-without-yields.cs
@@ -4,35 +4,44 @@
 using System;
 ﻿using System.Diagnostics;
 using System.Threading;
+using System.Threading.Tasks;
 using System.Runtime.CompilerServices;
+using Xunit;
 
-const uint Threshold = 1_000;
-
-for (int i = 0; i < 10; i++)
-{
-    var sw = new Stopwatch();
-    sw.Start();
-    uint result = A(100_000_000);
-    Console.WriteLine($"{sw.ElapsedMilliseconds} ms result={result}");
-}
-
-static uint A(uint n)
+public class SyncFibonacciWithoutYields
 {
-    uint result = n;
-    for (uint i = 0; i < n; i++)
-        result = B(result);
-    return result;
-}
-
-static uint B(uint n)
-{
-    uint result = n;
-
-    result = result * 1_999_999_981;
-
-    result = result * 1_999_999_981;
-
-    result = result * 1_999_999_981;
-
-    return result;
+    const uint Threshold = 1_000;
+
+    [Fact]
+    public static void Test()
+    {
+        for (int i = 0; i < 10; i++)
+        {
+            var sw = new Stopwatch();
+            sw.Start();
+            uint result = A(100_000_000);
+            Console.WriteLine($"{sw.ElapsedMilliseconds} ms result={result}");
+        }
+    }
+
+    static uint A(uint n)
+    {
+        uint result = n;
+        for (uint i = 0; i < n; i++)
+            result = B(result);
+        return result;
+    }
+
+    static uint B(uint n)
+    {
+        uint result = n;
+
+        result = result * 1_999_999_981;
+
+        result = result * 1_999_999_981;
+
+        result = result * 1_999_999_981;
+
+        return result;
+    }
 }
diff --git a/src/tests/Loader/async/syncfibonacci-without-yields.csproj b/src/tests/Loader/async/syncfibonacci-without-yields.csproj
index b2d4498b9b2347..9cf8b76da52921 100644
--- a/src/tests/Loader/async/syncfibonacci-without-yields.csproj
+++ b/src/tests/Loader/async/syncfibonacci-without-yields.csproj
@@ -1,6 +1,7 @@
 <Project Sdk="Microsoft.NET.Sdk">
   <PropertyGroup>
     <CLRTestPriority>1</CLRTestPriority>
+    <RequiresProcessIsolation>true</RequiresProcessIsolation>
   </PropertyGroup>
   <ItemGroup>
     <Compile Include="syncfibonacci-without-yields.cs" />
diff --git a/src/tests/Loader/async/taskbased-asyncfibonacci-with-yields.cs b/src/tests/Loader/async/taskbased-asyncfibonacci-with-yields.cs
index 8fe98070a93f52..c000811184fb17 100644
--- a/src/tests/Loader/async/taskbased-asyncfibonacci-with-yields.cs
+++ b/src/tests/Loader/async/taskbased-asyncfibonacci-with-yields.cs
@@ -5,41 +5,51 @@
 using System.Threading.Tasks;
 ﻿using System.Diagnostics;
 using System.Runtime.CompilerServices;
+using Xunit;
 
-const uint Threshold = 1_000;
-
-for (int i = 0; i < 10; i++)
+public class TaskBasedAsyncFibonacciWithYields
 {
-    var sw = new Stopwatch();
-    sw.Start();
-    uint result = await A(100_000_000);
-    Console.WriteLine($"{sw.ElapsedMilliseconds} ms result={result}");
-}
-
-return 100;
-
-static async Task<uint> A(uint n)
-{
-    uint result = n;
-    for (uint i = 0; i < n; i++)
-        result = await B(result);
-    return result;
-}
-
-static async Task<uint> B(uint n){
-    uint result = n;
-
-    result = result * 1_999_999_981;
-    if (result < Threshold)
-        await Task.Yield();
-
-    result = result * 1_999_999_981;
-    if (result < Threshold)
-        await Task.Yield();
-
-    result = result * 1_999_999_981;
-    if (result < Threshold)
-        await Task.Yield();
-
-    return result;
+    const uint Threshold = 1_000;
+
+    [Fact]
+    public static int Test() { return AsyncMain().Result; }
+    
+    static async Task<int> AsyncMain()
+    {
+        for (int i = 0; i < 10; i++)
+        {
+            var sw = new Stopwatch();
+            sw.Start();
+            uint result = await A(100_000_000);
+            Console.WriteLine($"{sw.ElapsedMilliseconds} ms result={result}");
+        }
+
+        return 100;
+    }
+
+    static async Task<uint> A(uint n)
+    {
+        uint result = n;
+        for (uint i = 0; i < n; i++)
+            result = await B(result);
+        return result;
+    }
+
+    static async Task<uint> B(uint n){
+        uint result = n;
+
+        result = result * 1_999_999_981;
+        if (result < Threshold)
+            await Task.Yield();
+
+        result = result * 1_999_999_981;
+        if (result < Threshold)
+            await Task.Yield();
+
+        result = result * 1_999_999_981;
+        if (result < Threshold)
+            await Task.Yield();
+
+        return result;
+    }
 }
\ No newline at end of file
diff --git a/src/tests/Loader/async/taskbased-asyncfibonacci-with-yields.csproj b/src/tests/Loader/async/taskbased-asyncfibonacci-with-yields.csproj
index 0ce5b85c6dda70..c52fc0b7296764 100644
--- a/src/tests/Loader/async/taskbased-asyncfibonacci-with-yields.csproj
+++ b/src/tests/Loader/async/taskbased-asyncfibonacci-with-yields.csproj
@@ -1,6 +1,7 @@
 <Project Sdk="Microsoft.NET.Sdk">
   <PropertyGroup>
     <CLRTestPriority>1</CLRTestPriority>
+    <RequiresProcessIsolation>true</RequiresProcessIsolation>
   </PropertyGroup>
   <ItemGroup>
     <Compile Include="taskbased-asyncfibonacci-with-yields.cs" />
diff --git a/src/tests/Loader/async/taskbased-asyncfibonacci-without-yields.cs b/src/tests/Loader/async/taskbased-asyncfibonacci-without-yields.cs
index 409586183a849f..44312d4f7319b3 100644
--- a/src/tests/Loader/async/taskbased-asyncfibonacci-without-yields.cs
+++ b/src/tests/Loader/async/taskbased-asyncfibonacci-without-yields.cs
@@ -5,37 +5,47 @@
 ﻿using System.Diagnostics;
 using System.Runtime.CompilerServices;
 using System.Threading.Tasks;
+using Xunit;
 
-const uint Threshold = 1_000;
-
-for (int i = 0; i < 10; i++)
-{
-    var sw = new Stopwatch();
-    sw.Start();
-    uint result = await A(100_000_000);
-    Console.WriteLine($"{sw.ElapsedMilliseconds} ms result={result}");
-}
-
-return 100;
-
-static async Task<uint> A(uint n)
-{
-    uint result = n;
-    for (uint i = 0; i < n; i++)
-        result = await B(result);
-    return result;
-}
-
-#pragma warning disable CS1998
-static async Task<uint> B(uint n)
+public class TaskBasedAsyncFibonacciWithoutYields
 {
-    uint result = n;
-
-    result = result * 1_999_999_981;
-
-    result = result * 1_999_999_981;
-
-    result = result * 1_999_999_981;
-
-    return result;
-}
+    const uint Threshold = 1_000;
+
+    [Fact]
+    public static int Test() { return AsyncMain().Result; }
+    
+    static async Task<int> AsyncMain()
+    {
+        for (int i = 0; i < 10; i++)
+        {
+            var sw = new Stopwatch();
+            sw.Start();
+            uint result = await A(100_000_000);
+            Console.WriteLine($"{sw.ElapsedMilliseconds} ms result={result}");
+        }
+
+        return 100;
+    }
+
+    static async Task<uint> A(uint n)
+    {
+        uint result = n;
+        for (uint i = 0; i < n; i++)
+            result = await B(result);
+        return result;
+    }
+
+    #pragma warning disable CS1998
+    static async Task<uint> B(uint n)
+    {
+        uint result = n;
+
+        result = result * 1_999_999_981;
+
+        result = result * 1_999_999_981;
+
+        result = result * 1_999_999_981;
+
+        return result;
+    }
+}
\ No newline at end of file
diff --git a/src/tests/Loader/async/taskbased-asyncfibonacci-without-yields.csproj b/src/tests/Loader/async/taskbased-asyncfibonacci-without-yields.csproj
index 42d69ec59b1cb5..7be4aaefd9f51d 100644
--- a/src/tests/Loader/async/taskbased-asyncfibonacci-without-yields.csproj
+++ b/src/tests/Loader/async/taskbased-asyncfibonacci-without-yields.csproj
@@ -1,6 +1,7 @@
 <Project Sdk="Microsoft.NET.Sdk">
   <PropertyGroup>
     <CLRTestPriority>1</CLRTestPriority>
+    <RequiresProcessIsolation>true</RequiresProcessIsolation>
   </PropertyGroup>
   <ItemGroup>
     <Compile Include="taskbased-asyncfibonacci-without-yields.cs" />
diff --git a/src/tests/Loader/async/valuetaskbased-asyncfibonacci-with-yields.cs b/src/tests/Loader/async/valuetaskbased-asyncfibonacci-with-yields.cs
index 29f4d892cb41dc..12828c937e4036 100644
--- a/src/tests/Loader/async/valuetaskbased-asyncfibonacci-with-yields.cs
+++ b/src/tests/Loader/async/valuetaskbased-asyncfibonacci-with-yields.cs
@@ -5,42 +5,51 @@
 using System.Threading.Tasks;
 ﻿using System.Diagnostics;
 using System.Runtime.CompilerServices;
+using Xunit;
 
-const uint Threshold = 1_000;
-
-for (int i = 0; i < 10; i++)
-{
-    var sw = new Stopwatch();
-    sw.Start();
-    uint result = await A(100_000_000);
-    Console.WriteLine($"{sw.ElapsedMilliseconds} ms result={result}");
-}
-
-return 100;
-
-static async ValueTask<uint> A(uint n)
+public class ValueTaskBasedAsyncFibonacciWithYields
 {
-    uint result = n;
-    for (uint i = 0; i < n; i++)
-        result = await B(result);
-    return result;
+    const uint Threshold = 1_000;
+
+    [Fact]
+    public static int Test() { return AsyncMain().Result; }
+    
+    static async Task<int> AsyncMain()
+    {
+        for (int i = 0; i < 10; i++)
+        {
+            var sw = new Stopwatch();
+            sw.Start();
+            uint result = await A(100_000_000);
+            Console.WriteLine($"{sw.ElapsedMilliseconds} ms result={result}");
+        }
+
+        return 100;
+    }
+
+    static async ValueTask<uint> A(uint n)
+    {
+        uint result = n;
+        for (uint i = 0; i < n; i++)
+            result = await B(result);
+        return result;
+    }
+
+    static async ValueTask<uint> B(uint n){
+        uint result = n;
+
+        result = result * 1_999_999_981;
+        if (result < Threshold)
+            await Task.Yield();
+
+        result = result * 1_999_999_981;
+        if (result < Threshold)
+            await Task.Yield();
+
+        result = result * 1_999_999_981;
+        if (result < Threshold)
+            await Task.Yield();
+
+        return result;
+    }
 }
-
-static async ValueTask<uint> B(uint n)
-{
-    uint result = n;
-
-    result = result * 1_999_999_981;
-    if (result < Threshold)
-        await Task.Yield();
-
-    result = result * 1_999_999_981;
-    if (result < Threshold)
-        await Task.Yield();
-
-    result = result * 1_999_999_981;
-    if (result < Threshold)
-        await Task.Yield();
-
-    return result;
-}
\ No newline at end of file
diff --git a/src/tests/Loader/async/valuetaskbased-asyncfibonacci-with-yields.csproj b/src/tests/Loader/async/valuetaskbased-asyncfibonacci-with-yields.csproj
index 07ce3d1c562473..33bb31ea09bd0c 100644
--- a/src/tests/Loader/async/valuetaskbased-asyncfibonacci-with-yields.csproj
+++ b/src/tests/Loader/async/valuetaskbased-asyncfibonacci-with-yields.csproj
@@ -1,6 +1,7 @@
 <Project Sdk="Microsoft.NET.Sdk">
   <PropertyGroup>
     <CLRTestPriority>1</CLRTestPriority>
+    <RequiresProcessIsolation>true</RequiresProcessIsolation>
   </PropertyGroup>
   <ItemGroup>
     <Compile Include="valuetaskbased-asyncfibonacci-with-yields.cs" />
diff --git a/src/tests/Loader/async/valuetaskbased-asyncfibonacci-without-yields.cs b/src/tests/Loader/async/valuetaskbased-asyncfibonacci-without-yields.cs
index ce28a34ed45d49..b27d19a8b15691 100644
--- a/src/tests/Loader/async/valuetaskbased-asyncfibonacci-without-yields.cs
+++ b/src/tests/Loader/async/valuetaskbased-asyncfibonacci-without-yields.cs
@@ -5,37 +5,47 @@
 ﻿using System.Diagnostics;
 using System.Runtime.CompilerServices;
 using System.Threading.Tasks;
+using Xunit;
 
-const uint Threshold = 1_000;
-
-for (int i = 0; i < 10; i++)
-{
-    var sw = new Stopwatch();
-    sw.Start();
-    uint result = await A(100_000_000);
-    Console.WriteLine($"{sw.ElapsedMilliseconds} ms result={result}");
-}
-
-return 100;
-
-static async ValueTask<uint> A(uint n)
-{
-    uint result = n;
-    for (uint i = 0; i < n; i++)
-        result = await B(result);
-    return result;
-}
-
-#pragma warning disable CS1998
-static async ValueTask<uint> B(uint n)
+public class ValueTaskBasedAsyncFibonacciWithoutYields
 {
-    uint result = n;
-
-    result = result * 1_999_999_981;
-
-    result = result * 1_999_999_981;
-
-    result = result * 1_999_999_981;
-
-    return result;
-}
+    const uint Threshold = 1_000;
+
+    [Fact]
+    public static int Test() { return AsyncMain().Result; }
+    
+    static async ValueTask<int> AsyncMain()
+    {
+        for (int i = 0; i < 10; i++)
+        {
+            var sw = new Stopwatch();
+            sw.Start();
+            uint result = await A(100_000_000);
+            Console.WriteLine($"{sw.ElapsedMilliseconds} ms result={result}");
+        }
+
+        return 100;
+    }
+
+    static async ValueTask<uint> A(uint n)
+    {
+        uint result = n;
+        for (uint i = 0; i < n; i++)
+            result = await B(result);
+        return result;
+    }
+
+    #pragma warning disable CS1998
+    static async ValueTask<uint> B(uint n)
+    {
+        uint result = n;
+
+        result = result * 1_999_999_981;
+
+        result = result * 1_999_999_981;
+
+        result = result * 1_999_999_981;
+
+        return result;
+    }
+}
\ No newline at end of file
diff --git a/src/tests/Loader/async/valuetaskbased-asyncfibonacci-without-yields.csproj b/src/tests/Loader/async/valuetaskbased-asyncfibonacci-without-yields.csproj
index d13c6b884d0595..043a2cb45456ac 100644
--- a/src/tests/Loader/async/valuetaskbased-asyncfibonacci-without-yields.csproj
+++ b/src/tests/Loader/async/valuetaskbased-asyncfibonacci-without-yields.csproj
@@ -1,6 +1,7 @@
 <Project Sdk="Microsoft.NET.Sdk">
   <PropertyGroup>
     <CLRTestPriority>1</CLRTestPriority>
+    <RequiresProcessIsolation>true</RequiresProcessIsolation>
   </PropertyGroup>
   <ItemGroup>
     <Compile Include="valuetaskbased-asyncfibonacci-without-yields.cs" />

From 9e4389b07213c47acaa4e542fe205f1291527b37 Mon Sep 17 00:00:00 2001
From: Jakob Botsch Nielsen <janielsen@microsoft.com>
Date: Mon, 23 Oct 2023 23:00:23 +0000
Subject: [PATCH 039/203] Merged PR 34707: Get basic thunk working for JIT
 state machines

Also fixes EH in the thunks, and propagates the async calling convention around more.

Basic synchronous calls work now...
---
 src/coreclr/jit/async.cpp          |   2 +
 src/coreclr/jit/compiler.h         |   5 +
 src/coreclr/jit/gentree.cpp        |   1 +
 src/coreclr/vm/binder.cpp          |   6 +
 src/coreclr/vm/callingconvention.h |  12 +
 src/coreclr/vm/corelib.cpp         |   6 +
 src/coreclr/vm/corelib.h           |   5 +
 src/coreclr/vm/eeconfig.cpp        |   2 +
 src/coreclr/vm/eeconfig.h          |   4 +
 src/coreclr/vm/frames.cpp          |  15 +-
 src/coreclr/vm/jitinterface.cpp    |   3 +
 src/coreclr/vm/metasig.h           |   7 +-
 src/coreclr/vm/method.hpp          |  27 +-
 src/coreclr/vm/prestub.cpp         | 429 +++++++++++++++++++----------
 src/coreclr/vm/siginfo.cpp         |  15 +-
 src/coreclr/vm/siginfo.hpp         |  25 +-
 16 files changed, 400 insertions(+), 164 deletions(-)

diff --git a/src/coreclr/jit/async.cpp b/src/coreclr/jit/async.cpp
index 3d94d6fb5ee36f..84e9cea1cdba79 100644
--- a/src/coreclr/jit/async.cpp
+++ b/src/coreclr/jit/async.cpp
@@ -780,6 +780,8 @@ bool Async2Transformation::IsLive(unsigned lclNum)
                 return true;
             }
         }
+
+        return false;
     }
 
     if (dsc->lvIsStructField && (m_comp->lvaGetParentPromotionType(dsc) == Compiler::PROMOTION_TYPE_DEPENDENT))
diff --git a/src/coreclr/jit/compiler.h b/src/coreclr/jit/compiler.h
index 5d1fef53083443..0df3b1b4b237f4 100644
--- a/src/coreclr/jit/compiler.h
+++ b/src/coreclr/jit/compiler.h
@@ -10232,6 +10232,11 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
         return compIsAsync2() && (JitConfig.RuntimeAsyncViaJitGeneratedStateMachines() != 0);
     }
 
+    bool compIsAsync2ViaUnwinding() const
+    {
+        return compIsAsync2() && (JitConfig.RuntimeAsyncViaJitGeneratedStateMachines() == 0);
+    }
+
     //------------------------------------------------------------------------
     // compMethodReturnsMultiRegRetType: Does this method return a multi-reg value?
     //
diff --git a/src/coreclr/jit/gentree.cpp b/src/coreclr/jit/gentree.cpp
index 9e8ffb5b06c1c1..4d8eb99561d0da 100644
--- a/src/coreclr/jit/gentree.cpp
+++ b/src/coreclr/jit/gentree.cpp
@@ -1482,6 +1482,7 @@ CallArgs::CallArgs()
 #endif
     , m_hasThisPointer(false)
     , m_hasRetBuffer(false)
+    , m_hasAsyncContinuation(false)
     , m_isVarArgs(false)
     , m_abiInformationDetermined(false)
     , m_hasRegArgs(false)
diff --git a/src/coreclr/vm/binder.cpp b/src/coreclr/vm/binder.cpp
index 23b14a34ee43b6..383407054ef247 100644
--- a/src/coreclr/vm/binder.cpp
+++ b/src/coreclr/vm/binder.cpp
@@ -437,6 +437,12 @@ void CoreLibBinder::BuildConvertedSignature(const BYTE* pSig, SigBuilder * pSigB
         argCount = 0;
     }
 
+    if ((callConv & IMAGE_CEE_CS_CALLCONV_GENERIC) != 0)
+    {
+        unsigned genericArgCount = *pSig++;
+        pSigBuilder->AppendData(genericArgCount);
+    }
+
     // <= because we want to include the return value or the field
     for (unsigned i = 0; i <= argCount; i++) {
         if (ConvertType(pSig, pSigBuilder))
diff --git a/src/coreclr/vm/callingconvention.h b/src/coreclr/vm/callingconvention.h
index 1c1f27ffc25293..fb59251144516a 100644
--- a/src/coreclr/vm/callingconvention.h
+++ b/src/coreclr/vm/callingconvention.h
@@ -2289,6 +2289,18 @@ class ArgIteratorBase
         return m_pSig->IsVarArg() || m_pSig->IsTreatAsVarArg();
     }
 
+    BOOL IsAsyncCall()
+    {
+        LIMITED_METHOD_CONTRACT;
+        return m_pSig->IsAsyncCall();
+    }
+
+    BOOL HasAsyncContinuation()
+    {
+        LIMITED_METHOD_CONTRACT;
+        return m_pSig->HasAsyncContinuation();
+    }
+
     DWORD NumFixedArgs()
     {
         LIMITED_METHOD_CONTRACT;
diff --git a/src/coreclr/vm/corelib.cpp b/src/coreclr/vm/corelib.cpp
index 22869b70e15cf4..5688e601906313 100644
--- a/src/coreclr/vm/corelib.cpp
+++ b/src/coreclr/vm/corelib.cpp
@@ -164,6 +164,11 @@ enum _gsigc {
     const BYTE gsige_IM_ ## varname[] = { (BYTE) -gsigl_IM_ ## varname,         \
         IMAGE_CEE_CS_CALLCONV_HASTHIS, gsigc_IM_ ## varname, retval args };
 
+#define _GM(varname, conv, n, args, retval) extern const BYTE gsige_GM_ ## varname[];    \
+    HardCodedMetaSig gsig_GM_ ## varname = { gsige_GM_ ## varname };              \
+    const BYTE gsige_GM_ ## varname[] = { (BYTE) -gsigl_GM_ ## varname,                        \
+        conv | IMAGE_CEE_CS_CALLCONV_GENERIC, n, gsigc_GM_ ## varname, retval args };
+
 #define _Fld(varname, val) extern const BYTE gsige_Fld_ ## varname[];           \
     HardCodedMetaSig gsig_Fld_ ## varname = { gsige_Fld_ ## varname };          \
     const BYTE gsige_Fld_ ## varname[] = { (BYTE) -gsigl_Fld_ ## varname,       \
@@ -173,6 +178,7 @@ enum _gsigc {
 
 #undef _SM
 #undef _IM
+#undef _GM
 #undef _Fld
 
 #ifdef _DEBUG
diff --git a/src/coreclr/vm/corelib.h b/src/coreclr/vm/corelib.h
index 14e4ed9800424c..0397b11ca8d683 100644
--- a/src/coreclr/vm/corelib.h
+++ b/src/coreclr/vm/corelib.h
@@ -353,6 +353,11 @@ DEFINE_METHOD(THREAD_START_EXCEPTION,EX_CTOR,               .ctor,
 DEFINE_CLASS(TASK_1, Tasks, Task`1)
 DEFINE_METHOD(TASK_1, GET_AWAITER, GetAwaiter, NoSig)
 
+DEFINE_CLASS(TASK, Tasks, Task)
+DEFINE_METHOD(TASK, FROM_EXCEPTION_T, FromException, GM_Exception_RetTaskOfT)
+DEFINE_METHOD(TASK, FROM_RESULT_T, FromResult, GM_T_RetTaskOfT)
+DEFINE_METHOD(TASK, GET_COMPLETED_TASK, get_CompletedTask, SM_RetTask)
+
 DEFINE_CLASS(RUNTIME_TASK_STATE_1, Tasks, RuntimeTaskState`1)
 DEFINE_METHOD(RUNTIME_TASK_STATE_1, PUSH, Push, IM_RetVoid)
 DEFINE_METHOD(RUNTIME_TASK_STATE_1, POP, Pop, IM_RetVoid)
diff --git a/src/coreclr/vm/eeconfig.cpp b/src/coreclr/vm/eeconfig.cpp
index 66a7fc6aea1acb..b268ead119785c 100644
--- a/src/coreclr/vm/eeconfig.cpp
+++ b/src/coreclr/vm/eeconfig.cpp
@@ -816,6 +816,8 @@ HRESULT EEConfig::sync()
 
     backpatchEntryPointSlots = CLRConfig::GetConfigValue(CLRConfig::UNSUPPORTED_BackpatchEntryPointSlots) != 0;
 
+    runtimeAsyncViaJitGeneratedStateMachines = CLRConfig::GetConfigValue(CLRConfig::EXTERNAL_RuntimeAsyncViaJitGeneratedStateMachines) != 0;
+
 #if defined(FEATURE_GDBJIT) && defined(_DEBUG)
     {
         LPWSTR pszGDBJitElfDumpW = NULL;
diff --git a/src/coreclr/vm/eeconfig.h b/src/coreclr/vm/eeconfig.h
index 9c850714883c4d..bfe7a2a28aa956 100644
--- a/src/coreclr/vm/eeconfig.h
+++ b/src/coreclr/vm/eeconfig.h
@@ -114,6 +114,8 @@ class EEConfig
 
     bool          BackpatchEntryPointSlots() const { LIMITED_METHOD_CONTRACT; return backpatchEntryPointSlots; }
 
+    bool          RuntimeAsyncViaJitGeneratedStateMachines() const { LIMITED_METHOD_CONTRACT; return runtimeAsyncViaJitGeneratedStateMachines; }
+
 #if defined(FEATURE_GDBJIT) && defined(_DEBUG)
     inline bool ShouldDumpElfOnMethod(LPCUTF8 methodName) const
     {
@@ -685,6 +687,8 @@ class EEConfig
 #if defined(FEATURE_GDBJIT_FRAME)
     bool fGDBJitEmitDebugFrame;
 #endif
+
+    bool runtimeAsyncViaJitGeneratedStateMachines;
 public:
 
     enum BitForMask {
diff --git a/src/coreclr/vm/frames.cpp b/src/coreclr/vm/frames.cpp
index 90c21e54aa813b..093a0f18749999 100644
--- a/src/coreclr/vm/frames.cpp
+++ b/src/coreclr/vm/frames.cpp
@@ -1342,6 +1342,9 @@ void TransitionFrame::PromoteCallerStack(promote_func* fn, ScanContext* sc)
         if (pFunction->RequiresInstArg() && !SuppressParamTypeArg())
             msig.SetHasParamTypeArg();
 
+        if (pFunction->IsAsync2Method())
+            msig.SetIsAsyncCall();
+
         PromoteCallerStackHelper (fn, sc, pFunction, &msig);
     }
     else
@@ -2247,9 +2250,17 @@ void ComputeCallRefMap(MethodDesc* pMD,
     // See code:getMethodSigInternal
     //
     assert(!isDispatchCell || !pMD->RequiresInstArg() || pMD->GetMethodTable()->IsInterface());
-    if (pMD->RequiresInstArg() && !isDispatchCell)
+    if (!isDispatchCell)
     {
-        msig.SetHasParamTypeArg();
+        if (pMD->RequiresInstArg())
+        {
+            msig.SetHasParamTypeArg();
+        }
+
+        if (pMD->IsAsync2Method())
+        {
+            msig.SetIsAsyncCall();
+        }
     }
 
     ArgIterator argit(&msig);
diff --git a/src/coreclr/vm/jitinterface.cpp b/src/coreclr/vm/jitinterface.cpp
index d3489493352c0f..a427f4eb396f97 100644
--- a/src/coreclr/vm/jitinterface.cpp
+++ b/src/coreclr/vm/jitinterface.cpp
@@ -12840,6 +12840,9 @@ PCODE UnsafeJitFunction(PrepareCodeConfig* config,
     CORINFO_METHOD_INFO methodInfo;
     getMethodInfoHelper(cxt, &methodInfo);
 
+    if (ILHeader == nullptr)
+        ILHeader = cxt.Header;
+
     // If it's generic then we can only enter through an instantiated MethodDesc
     _ASSERTE(!ftn->IsGenericMethodDefinition());
 
diff --git a/src/coreclr/vm/metasig.h b/src/coreclr/vm/metasig.h
index b0bdd9ccce04bb..9616fe0e45e2a3 100644
--- a/src/coreclr/vm/metasig.h
+++ b/src/coreclr/vm/metasig.h
@@ -76,7 +76,7 @@
 #endif
 #define SM(varname, args, retval)       METASIG_BODY( SM_ ## varname, args retval )
 #define IM(varname, args, retval)       METASIG_BODY( IM_ ## varname, args retval )
-#define GM(varname, n, conv, args, retval) METASIG_BODY( GM_ ## varname, args retval )
+#define GM(varname, conv, n, args, retval) METASIG_BODY( GM_ ## varname, args retval )
 #define Fld(varname, val)               METASIG_BODY( Fld_ ## varname, val )
 #endif
 
@@ -577,6 +577,11 @@ DEFINE_METASIG_T(SM(RefDec_RetVoid, r(g(DECIMAL)), v))
 DEFINE_METASIG_T(IM(Exception_RetTaskOfT, C(EXCEPTION), GI(C(TASK_1), 1, G(0))))
 DEFINE_METASIG_T(IM(T_RetTaskOfT, G(0), GI(C(TASK_1), 1, G(0))))
 
+DEFINE_METASIG_T(GM(Exception_RetTaskOfT, IMAGE_CEE_CS_CALLCONV_DEFAULT, 1, C(EXCEPTION), GI(C(TASK_1), 1, M(0))))
+DEFINE_METASIG_T(GM(T_RetTaskOfT, IMAGE_CEE_CS_CALLCONV_DEFAULT, 1, M(0), GI(C(TASK_1), 1, M(0))))
+
+DEFINE_METASIG_T(SM(RetTask, , C(TASK)))
+
 DEFINE_METASIG(GM(RefT_T_T_RetT, IMAGE_CEE_CS_CALLCONV_DEFAULT, 1, r(M(0)) M(0) M(0), M(0)))
 DEFINE_METASIG(SM(RefObject_Object_Object_RetObject, r(j) j j, j))
 
diff --git a/src/coreclr/vm/method.hpp b/src/coreclr/vm/method.hpp
index 18f1fb92c87689..d48d31ea75e4b9 100644
--- a/src/coreclr/vm/method.hpp
+++ b/src/coreclr/vm/method.hpp
@@ -192,6 +192,13 @@ enum class AsyncVariantLookup
     AsyncOtherVariant
 };
 
+enum class AsyncTaskMethod
+{
+    TaskReturningMethod,
+    Async2Method,
+    Async2MethodThatCannotBeImplementedByTask,
+    NormalMethod
+};
 
 // The size of this structure needs to be a multiple of MethodDesc::ALIGNMENT
 //
@@ -1734,7 +1741,14 @@ class MethodDesc
         if (!HasAsyncMethodData())
             return false;
         auto asyncType = GetAddrOfAsyncMethodData()->type;
-        return asyncType == AsyncMethodType::Async || asyncType == AsyncMethodType::AsyncToTask;
+
+        return asyncType == AsyncMethodType::Async ||
+               (asyncType == AsyncMethodType::AsyncToTask && !g_pConfig->RuntimeAsyncViaJitGeneratedStateMachines());
+    }
+
+    inline bool RequiresAsyncContinuationArg()
+    {
+        return IsAsync2Method() && g_pConfig->RuntimeAsyncViaJitGeneratedStateMachines();
     }
 
     inline bool HasAsyncMethodData()
@@ -1889,7 +1903,11 @@ class MethodDesc
 
     bool TryGenerateAsyncThunk(DynamicResolver** resolver, COR_ILMETHOD_DECODER** methodILDecoder);
     bool TryGenerateUnsafeAccessor(DynamicResolver** resolver, COR_ILMETHOD_DECODER** methodILDecoder);
+    void EmitUnwindingBasedRuntimeAsyncThunk(MethodDesc* pAsyncOtherVariant, MetaSig& msig, ILStubLinker* pSL);
+    void EmitJitStateMachineBasedRuntimeAsyncThunk(MethodDesc* pAsyncOtherVariant, MetaSig& thunkMsig, ILStubLinker* pSL);
+    void EmitAsync2MethodThunk(MethodDesc* pAsyncOtherVariant, MetaSig& msig, ILStubLinker* pSL);
 public:
+    static void CreateDerivedTargetSigWithExtraParams(MetaSig& msig, SigBuilder* stubSigBuilder);
     bool TryGenerateTransientILImplementation(DynamicResolver** resolver, COR_ILMETHOD_DECODER** methodILDecoder);
 #endif // DACCESS_COMPILE
 
@@ -3666,13 +3684,6 @@ ReadyToRunStandaloneMethodMetadata* GetReadyToRunStandaloneMethodMetadata(Method
 void InitReadyToRunStandaloneMethodMetadata();
 #endif // FEATURE_READYTORUN
 
-enum class AsyncTaskMethod
-{
-    TaskReturningMethod,
-    Async2Method,
-    Async2MethodThatCannotBeImplementedByTask,
-    NormalMethod
-};
 AsyncTaskMethod ClassifyAsyncMethod(SigPointer sig, Module* pModule, ULONG* offsetOfAsyncDetails);
 
 #include "method.inl"
diff --git a/src/coreclr/vm/prestub.cpp b/src/coreclr/vm/prestub.cpp
index 4948dddee95087..d055e430aa4db4 100644
--- a/src/coreclr/vm/prestub.cpp
+++ b/src/coreclr/vm/prestub.cpp
@@ -1457,29 +1457,10 @@ bool MethodDesc::TryGenerateAsyncThunk(DynamicResolver** resolver, COR_ILMETHOD_
         return false;
     }
 
-    // Create a MetaSig for the given method's sig. (Easier than
-    // picking the sig apart ourselves.)
-    PCCOR_SIGNATURE pCallSig;
-    DWORD cbCallSigSize;
-
-    GetSig(&pCallSig, &cbCallSigSize);
-
-    if (pCallSig == NULL)
-    {
-        _ASSERTE(FALSE);
-        return false;
-    }
-
-    _ASSERTE(CLRConfig::GetConfigValue(CLRConfig::EXTERNAL_RuntimeAsyncViaJitGeneratedStateMachines) == 0);
-
     ULONG offsetOfAsyncDetailsUnused;
     AsyncTaskMethod asyncType = ClassifyAsyncMethod(GetSigPointer(), GetModule(), &offsetOfAsyncDetailsUnused);
     MethodDesc *pAsyncOtherVariant = this->GetAsyncOtherVariant();
-    MetaSig msig(pCallSig, cbCallSigSize, this->GetModule(), NULL, MetaSig::sigMember);
-    NewHolder<ILStubResolver> ilResolver = new ILStubResolver();
-        // Initialize the resolver target details.
-    ilResolver->SetStubMethodDesc(this);
-    ilResolver->SetStubTargetMethodDesc(pAsyncOtherVariant);
+    MetaSig msig(this);
 
     // [TODO] Handle generics
     SigTypeContext emptyContext;
@@ -1490,153 +1471,280 @@ bool MethodDesc::TryGenerateAsyncThunk(DynamicResolver** resolver, COR_ILMETHOD_
         pAsyncOtherVariant,
         (ILStubLinkerFlags)ILSTUB_LINKER_FLAG_NONE);
 
-    ILCodeStream* pCode = sl.NewCodeStream(ILStubLinker::kDispatch);
+    if (asyncType == AsyncTaskMethod::TaskReturningMethod)
+    {
+        if (!g_pConfig->RuntimeAsyncViaJitGeneratedStateMachines())
+        {
+            EmitUnwindingBasedRuntimeAsyncThunk(pAsyncOtherVariant, msig, &sl);
+        }
+        else
+        {
+            EmitJitStateMachineBasedRuntimeAsyncThunk(pAsyncOtherVariant, msig, &sl);
+        }
+    }
+    else
+    {
+        _ASSERTE(asyncType == AsyncTaskMethod::Async2Method);
+        EmitAsync2MethodThunk(pAsyncOtherVariant, msig, &sl);
+    }
 
+    NewHolder<ILStubResolver> ilResolver = new ILStubResolver();
+    // Initialize the resolver target details.
+    ilResolver->SetStubMethodDesc(this);
+    ilResolver->SetStubTargetMethodDesc(pAsyncOtherVariant);
 
-    if (asyncType == AsyncTaskMethod::TaskReturningMethod)
+    // Generate all IL associated data for JIT
     {
-        TypeHandle thTaskRet = msig.GetRetTypeHandleThrowing();
-        TypeHandle thLogicalRetType = thTaskRet.GetMethodTable()->GetInstantiation()[0];
-        MethodTable* pMTRuntimeTaskStateOpen = CoreLibBinder::GetClass(CLASS__RUNTIME_TASK_STATE_1);
-        MethodTable* pMTRuntimeTaskState = ClassLoader::LoadGenericInstantiationThrowing(pMTRuntimeTaskStateOpen->GetModule(), pMTRuntimeTaskStateOpen->GetCl(), Instantiation(&thLogicalRetType, 1)).GetMethodTable();
+        UINT maxStack;
+        size_t cbCode = sl.Link(&maxStack);
+        DWORD cbSig = sl.GetLocalSigSize();
 
-        LocalDesc rtsLocalDesc(pMTRuntimeTaskState);
-        DWORD rtsLocal = pCode->NewLocal(rtsLocalDesc);
+        COR_ILMETHOD_DECODER* pILHeader = ilResolver->AllocGeneratedIL(cbCode, cbSig, maxStack);
+        BYTE* pbBuffer = (BYTE*)pILHeader->Code;
+        BYTE* pbLocalSig = (BYTE*)pILHeader->LocalVarSig;
+        _ASSERTE(cbSig == pILHeader->cbLocalVarSig);
 
-        LocalDesc resultLocalDesc(thLogicalRetType);
-        DWORD resultLocal = pCode->NewLocal(resultLocalDesc);
+        size_t numEH = sl.GetNumEHClauses();
+        if (numEH > 0)
+        {
+            sl.WriteEHClauses(ilResolver->AllocEHSect(numEH));
+        }
 
-        LocalDesc exceptionLocalDesc(CoreLibBinder::GetClass(CLASS__EXCEPTION));
-        DWORD exceptionLocal = pCode->NewLocal(exceptionLocalDesc);
+        sl.GenerateCode(pbBuffer, cbCode);
+        sl.GetLocalSig(pbLocalSig, cbSig);
 
-        LocalDesc returnLocalDesc(thTaskRet);
-        DWORD returnLocal = pCode->NewLocal(returnLocalDesc);
+        // Store the token lookup map
+        ilResolver->SetTokenLookupMap(sl.GetTokenLookupMap());
+        ilResolver->SetJitFlags(CORJIT_FLAGS(CORJIT_FLAGS::CORJIT_FLAG_IL_STUB));
 
-        auto pNoExceptionLabel = pCode->NewCodeLabel();
-        auto pReturnResultLabel = pCode->NewCodeLabel();
+        *resolver = (DynamicResolver*)ilResolver;
+        *methodILDecoder = pILHeader;
+    }
 
-        pCode->EmitLDLOCA(rtsLocal);
-        pCode->EmitINITOBJ(pCode->GetToken(pMTRuntimeTaskState));
-        pCode->EmitLDLOCA(resultLocal);
-        pCode->EmitINITOBJ(pCode->GetToken(thLogicalRetType));
-        pCode->EmitLDLOCA(exceptionLocal);
-        pCode->EmitINITOBJ(pCode->GetToken(CoreLibBinder::GetClass(CLASS__EXCEPTION)));
-        pCode->EmitLDLOCA(returnLocal);
-        pCode->EmitINITOBJ(pCode->GetToken(thTaskRet));
-        pCode->EmitLDLOCA(rtsLocal);
-        pCode->EmitCALL(pCode->GetToken(pMTRuntimeTaskState->GetParallelMethodDesc(CoreLibBinder::GetMethod(METHOD__RUNTIME_TASK_STATE_1__PUSH))), 1, 0);
+    ilResolver.SuppressRelease();
+    return true;
+}
+
+void MethodDesc::EmitUnwindingBasedRuntimeAsyncThunk(MethodDesc* pAsyncOtherVariant, MetaSig& msig, ILStubLinker* pSL)
+{
+    ILCodeStream* pCode = pSL->NewCodeStream(ILStubLinker::kDispatch);
+
+    TypeHandle thTaskRet = msig.GetRetTypeHandleThrowing();
+    TypeHandle thLogicalRetType = thTaskRet.GetMethodTable()->GetInstantiation()[0];
+    MethodTable* pMTRuntimeTaskStateOpen = CoreLibBinder::GetClass(CLASS__RUNTIME_TASK_STATE_1);
+    MethodTable* pMTRuntimeTaskState = ClassLoader::LoadGenericInstantiationThrowing(pMTRuntimeTaskStateOpen->GetModule(), pMTRuntimeTaskStateOpen->GetCl(), Instantiation(&thLogicalRetType, 1)).GetMethodTable();
+
+    LocalDesc rtsLocalDesc(pMTRuntimeTaskState);
+    DWORD rtsLocal = pCode->NewLocal(rtsLocalDesc);
+
+    LocalDesc resultLocalDesc(thLogicalRetType);
+    DWORD resultLocal = pCode->NewLocal(resultLocalDesc);
+
+    LocalDesc exceptionLocalDesc(CoreLibBinder::GetClass(CLASS__EXCEPTION));
+    DWORD exceptionLocal = pCode->NewLocal(exceptionLocalDesc);
+
+    LocalDesc returnLocalDesc(thTaskRet);
+    DWORD returnLocal = pCode->NewLocal(returnLocalDesc);
+
+    auto pNoExceptionLabel = pCode->NewCodeLabel();
+    auto pReturnResultLabel = pCode->NewCodeLabel();
+
+    pCode->EmitLDLOCA(rtsLocal);
+    pCode->EmitINITOBJ(pCode->GetToken(pMTRuntimeTaskState));
+    pCode->EmitLDLOCA(resultLocal);
+    pCode->EmitINITOBJ(pCode->GetToken(thLogicalRetType));
+    pCode->EmitLDLOCA(exceptionLocal);
+    pCode->EmitINITOBJ(pCode->GetToken(CoreLibBinder::GetClass(CLASS__EXCEPTION)));
+    pCode->EmitLDLOCA(returnLocal);
+    pCode->EmitINITOBJ(pCode->GetToken(thTaskRet));
+    pCode->EmitLDLOCA(rtsLocal);
+    pCode->EmitCALL(pCode->GetToken(pMTRuntimeTaskState->GetParallelMethodDesc(CoreLibBinder::GetMethod(METHOD__RUNTIME_TASK_STATE_1__PUSH))), 1, 0);
+    {
+        pCode->BeginTryBlock();
+        pCode->EmitNOP("Separate try blocks");
         {
             pCode->BeginTryBlock();
-            pCode->EmitNOP("Separate try blocks");
-            {
-                pCode->BeginTryBlock();
 
-                DWORD localArg = 0;
-                if (msig.HasThis())
-                {
-                    // Struct async thunks not yet implemented
-                    _ASSERTE(!this->GetMethodTable()->IsValueType());
-                    pCode->EmitLDARG(localArg++);
-                }
-                for (UINT iArg = 0; iArg < msig.NumFixedArgs(); iArg++)
-                {
-                    pCode->EmitLDARG(localArg++);
-                }
-                pCode->EmitCALL(pCode->GetToken(pAsyncOtherVariant), localArg, 1);
-                pCode->EmitSTLOC(resultLocal);
-                pCode->EmitLEAVE(pNoExceptionLabel);
-                pCode->EndTryBlock();
+            DWORD localArg = 0;
+            if (msig.HasThis())
+            {
+                // Struct async thunks not yet implemented
+                _ASSERTE(!this->GetMethodTable()->IsValueType());
+                pCode->EmitLDARG(localArg++);
             }
-            // Catch
+            for (UINT iArg = 0; iArg < msig.NumFixedArgs(); iArg++)
             {
-                pCode->BeginCatchBlock(pCode->GetToken(CoreLibBinder::GetClass(CLASS__EXCEPTION)));
-                pCode->EmitSTLOC(exceptionLocal);
-                pCode->EmitLDLOCA(rtsLocal);
-                pCode->EmitLDLOC(exceptionLocal);
-                pCode->EmitCALL(pCode->GetToken(pMTRuntimeTaskState->GetParallelMethodDesc(CoreLibBinder::GetMethod(METHOD__RUNTIME_TASK_STATE_1__FROM_EXCEPTION))), 2, 1);
-                pCode->EmitSTLOC(returnLocal);
-                pCode->EmitLEAVE(pReturnResultLabel);
-                pCode->EndCatchBlock();
+                pCode->EmitLDARG(localArg++);
             }
-            pCode->EmitLabel(pNoExceptionLabel);
-            pCode->EmitLDLOCA(rtsLocal);
-            pCode->EmitLDLOC(resultLocal);
-            pCode->EmitCALL(pCode->GetToken(pMTRuntimeTaskState->GetParallelMethodDesc(CoreLibBinder::GetMethod(METHOD__RUNTIME_TASK_STATE_1__FROM_RESULT))), 2, 1);
-            pCode->EmitSTLOC(returnLocal);
-            pCode->EmitLEAVE(pReturnResultLabel);
+            pCode->EmitCALL(pCode->GetToken(pAsyncOtherVariant), localArg, 1);
+            pCode->EmitSTLOC(resultLocal);
+            pCode->EmitLEAVE(pNoExceptionLabel);
             pCode->EndTryBlock();
         }
-        // Finally
+        // Catch
         {
-            pCode->BeginFinallyBlock();
+            pCode->BeginCatchBlock(pCode->GetToken(CoreLibBinder::GetClass(CLASS__EXCEPTION)));
+            pCode->EmitSTLOC(exceptionLocal);
             pCode->EmitLDLOCA(rtsLocal);
-            pCode->EmitCALL(pCode->GetToken(pMTRuntimeTaskState->GetParallelMethodDesc(CoreLibBinder::GetMethod(METHOD__RUNTIME_TASK_STATE_1__POP))), 1, 0);
-            pCode->EmitENDFINALLY();
-            pCode->EndFinallyBlock();
+            pCode->EmitLDLOC(exceptionLocal);
+            pCode->EmitCALL(pCode->GetToken(pMTRuntimeTaskState->GetParallelMethodDesc(CoreLibBinder::GetMethod(METHOD__RUNTIME_TASK_STATE_1__FROM_EXCEPTION))), 2, 1);
+            pCode->EmitSTLOC(returnLocal);
+            pCode->EmitLEAVE(pReturnResultLabel);
+            pCode->EndCatchBlock();
         }
-        pCode->EmitLabel(pReturnResultLabel);
-        pCode->EmitLDLOC(returnLocal);
-        pCode->EmitRET();
+        pCode->EmitLabel(pNoExceptionLabel);
+        pCode->EmitLDLOCA(rtsLocal);
+        pCode->EmitLDLOC(resultLocal);
+        pCode->EmitCALL(pCode->GetToken(pMTRuntimeTaskState->GetParallelMethodDesc(CoreLibBinder::GetMethod(METHOD__RUNTIME_TASK_STATE_1__FROM_RESULT))), 2, 1);
+        pCode->EmitSTLOC(returnLocal);
+        pCode->EmitLEAVE(pReturnResultLabel);
+        pCode->EndTryBlock();
     }
-    else
+    // Finally
     {
-        _ASSERTE(asyncType == AsyncTaskMethod::Async2Method);
-// Implement IL that is effectively the following
-/*
-{
-    // Call target method and await
-    return RuntimeHelpers.UnsafeAwaitAwaiterFromRuntimeAsync<AwaiterType, resultType>(Thunk(arg).GetAwaiter());
+        pCode->BeginFinallyBlock();
+        pCode->EmitLDLOCA(rtsLocal);
+        pCode->EmitCALL(pCode->GetToken(pMTRuntimeTaskState->GetParallelMethodDesc(CoreLibBinder::GetMethod(METHOD__RUNTIME_TASK_STATE_1__POP))), 1, 0);
+        pCode->EmitENDFINALLY();
+        pCode->EndFinallyBlock();
+    }
+    pCode->EmitLabel(pReturnResultLabel);
+    pCode->EmitLDLOC(returnLocal);
+    pCode->EmitRET();
 }
-*/
-        TypeHandle thLogicalRetType = msig.GetRetTypeHandleThrowing();
-        MethodTable* pMTTaskOpen = CoreLibBinder::GetClass(CLASS__TASK_1);
-        MethodTable* pMTTask = ClassLoader::LoadGenericInstantiationThrowing(pMTTaskOpen->GetModule(), pMTTaskOpen->GetCl(), Instantiation(&thLogicalRetType, 1)).GetMethodTable();
-        MethodTable* pMTTaskAwaiterOpen = CoreLibBinder::GetClass(CLASS__TASK_AWAITER_1);
-        TypeHandle thTaskAwaiter = ClassLoader::LoadGenericInstantiationThrowing(pMTTaskAwaiterOpen->GetModule(), pMTTaskAwaiterOpen->GetCl(), Instantiation(&thLogicalRetType, 1));
+
+void MethodDesc::EmitJitStateMachineBasedRuntimeAsyncThunk(MethodDesc* pAsyncOtherVariant, MetaSig& thunkMsig, ILStubLinker* pSL)
+{
+    ILCodeStream* pCode = pSL->NewCodeStream(ILStubLinker::kDispatch);
+
+    MetaSig asyncMsig(pAsyncOtherVariant);
+    SigBuilder targetSig;
+    CreateDerivedTargetSigWithExtraParams(asyncMsig, &targetSig);
+
+    DWORD cbTargetSig;
+    PCCOR_SIGNATURE pTargetSig = (PCCOR_SIGNATURE)targetSig.GetSignature(&cbTargetSig);
+
+    unsigned boxedContinuationLcl = pCode->NewLocal(ELEMENT_TYPE_OBJECT);
+
+    TypeHandle thTaskRet = thunkMsig.GetRetTypeHandleThrowing();
+    LocalDesc returnLocalDesc(thTaskRet);
+    DWORD returnLocal = pCode->NewLocal(returnLocalDesc);
+
+    TypeHandle thLogicalRetType;
+    DWORD logicalResultLocal = UINT_MAX;
+    if (thTaskRet.GetNumGenericArgs() > 0)
+    {
+        thLogicalRetType = thTaskRet.GetMethodTable()->GetInstantiation()[0];
+        logicalResultLocal = pCode->NewLocal(LocalDesc(thLogicalRetType));
+    }
+
+    LocalDesc exceptionLocalDesc(CoreLibBinder::GetClass(CLASS__EXCEPTION));
+    DWORD exceptionLocal = pCode->NewLocal(exceptionLocalDesc);
+
+    ILCodeLabel* pNoExceptionLabel = pCode->NewCodeLabel();
+    ILCodeLabel* pReturnResultLabel = pCode->NewCodeLabel();
+    ILCodeLabel* pSuspendedLabel = pCode->NewCodeLabel();
+
+    {
+        pCode->BeginTryBlock();
+
         DWORD localArg = 0;
-        if (msig.HasThis())
+        if (asyncMsig.HasThis())
         {
             pCode->EmitLDARG(localArg++);
         }
-        for (UINT iArg = 0; iArg < msig.NumFixedArgs(); iArg++)
+        if ((asyncMsig.GetCallingConventionInfo() & CORINFO_CALLCONV_PARAMTYPE) != 0)
         {
-            pCode->EmitLDARG(localArg++);
+            _ASSERTE(!"Cannot handle generic context");
         }
-        pCode->EmitCALL(pCode->GetToken(pAsyncOtherVariant), localArg, 1);
-        pCode->EmitCALLVIRT(pCode->GetToken(pMTTask->GetParallelMethodDesc(CoreLibBinder::GetMethod(METHOD__TASK_1__GET_AWAITER))), 1, 1);
-        MethodDesc *pMagicAwaitFunc = CoreLibBinder::GetMethod(METHOD__RUNTIME_HELPERS__UNSAFE_AWAIT_AWAITER_FROM_RUNTIME_ASYNC_2);
-        TypeHandle thInstantiations[] {thLogicalRetType, thTaskAwaiter};
+        _ASSERTE((asyncMsig.GetCallingConventionInfo() & CORINFO_CALLCONV_ASYNCCALL) != 0);
 
-        pMagicAwaitFunc = FindOrCreateAssociatedMethodDesc(pMagicAwaitFunc, pMagicAwaitFunc->GetMethodTable(), FALSE, Instantiation(thInstantiations, 2), FALSE);
+        pCode->EmitLDNULL(); // Async continuation; not resuming
 
-        pCode->EmitCALL(pCode->GetToken(pMagicAwaitFunc), 1, 1);
+        for (UINT iArg = 0; iArg < asyncMsig.NumFixedArgs(); iArg++)
+        {
+            pCode->EmitLDARG(localArg++);
+        }
 
-        pCode->EmitRET();
+        // TODO: This and instantiating stubs can directly load the target from the precode slot
+        pCode->EmitLDC(pAsyncOtherVariant->GetMultiCallableAddrOfCode(CORINFO_ACCESS_ANY));
+        pCode->EmitCALLI(pCode->GetSigToken(pTargetSig, cbTargetSig), localArg + 2, logicalResultLocal != UINT_MAX ? 1 : 0);
+        if (logicalResultLocal != UINT_MAX)
+            pCode->EmitSTLOC(logicalResultLocal);
+        pCode->EmitCALL(METHOD__STUBHELPERS__ASYNC2_CALL_CONTINUATION, 0, 1);
+        pCode->EmitSTLOC(boxedContinuationLcl);
+        pCode->EmitLEAVE(pNoExceptionLabel);
+        pCode->EndTryBlock();
+    }
+    // Catch
+    {
+        pCode->BeginCatchBlock(pCode->GetToken(CoreLibBinder::GetClass(CLASS__EXCEPTION)));
+        MethodDesc* md = CoreLibBinder::GetMethod(METHOD__TASK__FROM_EXCEPTION_T);
+        md = FindOrCreateAssociatedMethodDesc(md, md->GetMethodTable(), FALSE, Instantiation(&thLogicalRetType, 1), FALSE);
+        pCode->EmitCALL(pCode->GetToken(md), 1, 1);
+        pCode->EmitSTLOC(returnLocal);
+        pCode->EmitLEAVE(pReturnResultLabel);
+        pCode->EndCatchBlock();
     }
 
-    // Generate all IL associated data for JIT
+    pCode->EmitLabel(pNoExceptionLabel);
+    pCode->EmitLDLOC(boxedContinuationLcl);
+    pCode->EmitBRTRUE(pSuspendedLabel);
+    if (logicalResultLocal != UINT_MAX)
     {
-        UINT maxStack;
-        size_t cbCode = sl.Link(&maxStack);
-        DWORD cbSig = sl.GetLocalSigSize();
+        pCode->EmitLDLOC(logicalResultLocal);
+        MethodDesc* md = CoreLibBinder::GetMethod(METHOD__TASK__FROM_RESULT_T);
+        md = FindOrCreateAssociatedMethodDesc(md, md->GetMethodTable(), FALSE, Instantiation(&thLogicalRetType, 1), FALSE);
+        pCode->EmitCALL(pCode->GetToken(md), 1, 1);
+    }
+    else
+    {
+        pCode->EmitCALL(METHOD__TASK__GET_COMPLETED_TASK, 0, 1);
+    }
 
-        COR_ILMETHOD_DECODER* pILHeader = ilResolver->AllocGeneratedIL(cbCode, cbSig, maxStack);
-        BYTE* pbBuffer = (BYTE*)pILHeader->Code;
-        BYTE* pbLocalSig = (BYTE*)pILHeader->LocalVarSig;
-        _ASSERTE(cbSig == pILHeader->cbLocalVarSig);
-        sl.GenerateCode(pbBuffer, cbCode);
-        sl.GetLocalSig(pbLocalSig, cbSig);
+    pCode->EmitSTLOC(returnLocal);
+    pCode->EmitLabel(pReturnResultLabel);
+    pCode->EmitLDLOC(returnLocal);
+    pCode->EmitRET();
 
-        // Store the token lookup map
-        ilResolver->SetTokenLookupMap(sl.GetTokenLookupMap());
-        ilResolver->SetJitFlags(CORJIT_FLAGS(CORJIT_FLAGS::CORJIT_FLAG_IL_STUB));
+    pCode->EmitLabel(pSuspendedLabel);
+    pCode->EmitLDNULL();
+    pCode->EmitRET();
+}
 
-        *resolver = (DynamicResolver*)ilResolver;
-        *methodILDecoder = pILHeader;
+void MethodDesc::EmitAsync2MethodThunk(MethodDesc* pAsyncOtherVariant, MetaSig& msig, ILStubLinker* pSL)
+{
+    // Implement IL that is effectively the following
+    /*
+    {
+        // Call target method and await
+        return RuntimeHelpers.UnsafeAwaitAwaiterFromRuntimeAsync<AwaiterType, resultType>(Thunk(arg).GetAwaiter());
     }
+    */
+    ILCodeStream* pCode = pSL->NewCodeStream(ILStubLinker::kDispatch);
+    TypeHandle thLogicalRetType = msig.GetRetTypeHandleThrowing();
+    MethodTable* pMTTaskOpen = CoreLibBinder::GetClass(CLASS__TASK_1);
+    MethodTable* pMTTask = ClassLoader::LoadGenericInstantiationThrowing(pMTTaskOpen->GetModule(), pMTTaskOpen->GetCl(), Instantiation(&thLogicalRetType, 1)).GetMethodTable();
+    MethodTable* pMTTaskAwaiterOpen = CoreLibBinder::GetClass(CLASS__TASK_AWAITER_1);
+    TypeHandle thTaskAwaiter = ClassLoader::LoadGenericInstantiationThrowing(pMTTaskAwaiterOpen->GetModule(), pMTTaskAwaiterOpen->GetCl(), Instantiation(&thLogicalRetType, 1));
+    DWORD localArg = 0;
+    if (msig.HasThis())
+    {
+        pCode->EmitLDARG(localArg++);
+    }
+    for (UINT iArg = 0; iArg < msig.NumFixedArgs(); iArg++)
+    {
+        pCode->EmitLDARG(localArg++);
+    }
+    pCode->EmitCALL(pCode->GetToken(pAsyncOtherVariant), localArg, 1);
+    pCode->EmitCALLVIRT(pCode->GetToken(pMTTask->GetParallelMethodDesc(CoreLibBinder::GetMethod(METHOD__TASK_1__GET_AWAITER))), 1, 1);
+    MethodDesc* pMagicAwaitFunc = CoreLibBinder::GetMethod(METHOD__RUNTIME_HELPERS__UNSAFE_AWAIT_AWAITER_FROM_RUNTIME_ASYNC_2);
+    TypeHandle thInstantiations[]{ thLogicalRetType, thTaskAwaiter };
 
-    ilResolver.SuppressRelease();
-    return true;
+    pMagicAwaitFunc = FindOrCreateAssociatedMethodDesc(pMagicAwaitFunc, pMagicAwaitFunc->GetMethodTable(), FALSE, Instantiation(thInstantiations, 2), FALSE);
+
+    pCode->EmitCALL(pCode->GetToken(pMagicAwaitFunc), 1, 1);
+    pCode->EmitRET();
 }
 
 bool MethodDesc::TryGenerateUnsafeAccessor(DynamicResolver** resolver, COR_ILMETHOD_DECODER** methodILDecoder)
@@ -2095,36 +2203,47 @@ PrepareCodeConfigBuffer::PrepareCodeConfigBuffer(NativeCodeVersion codeVersion)
 
 #endif //FEATURE_CODE_VERSIONING
 
-#ifdef FEATURE_INSTANTIATINGSTUB_AS_IL
-
-// CreateInstantiatingILStubTargetSig:
-// This method is used to create the signature of the target of the ILStub
-// for instantiating and unboxing stubs, when/where we need to introduce a generic context.
-// And since the generic context is a hidden parameter, we're creating a signature that
-// looks like non-generic but has one additional parameter right after the thisptr
-void CreateInstantiatingILStubTargetSig(MethodDesc *pBaseMD,
-                                        SigTypeContext &typeContext,
-                                        SigBuilder *stubSigBuilder)
+// CreateDerivedTargetSigWithExtraParams:
+// This method is used to create the signature of the target of the ILStub for
+// instantiating, unboxing, and async thunk stubs, when/where we need to
+// introduce a generic context/async continuation.
+// And since the generic context/async continuations are hidden parameters,
+// we're creating a signature that looks like non-generic but with additional
+// parameters right after the thisptr
+void MethodDesc::CreateDerivedTargetSigWithExtraParams(MetaSig& msig, SigBuilder *stubSigBuilder)
 {
     STANDARD_VM_CONTRACT;
 
-    MetaSig msig(pBaseMD);
     BYTE callingConvention = IMAGE_CEE_CS_CALLCONV_DEFAULT;
     if (msig.HasThis())
         callingConvention |= IMAGE_CEE_CS_CALLCONV_HASTHIS;
     // CallingConvention
     stubSigBuilder->AppendByte(callingConvention);
 
+    unsigned numArgs = msig.NumFixedArgs();
+    bool hasInstParam = (msig.GetCallingConventionInfo() & CORINFO_CALLCONV_PARAMTYPE) != 0;
+    if (hasInstParam)
+        numArgs++;
+    if (msig.HasAsyncContinuation())
+        numArgs++;
     // ParamCount
-    stubSigBuilder->AppendData(msig.NumFixedArgs() + 1); // +1 is for context param
+    stubSigBuilder->AppendData(numArgs); // +1 is for context param
 
     // Return type
     SigPointer pReturn = msig.GetReturnProps();
-    pReturn.ConvertToInternalExactlyOne(msig.GetModule(), &typeContext, stubSigBuilder);
+    pReturn.ConvertToInternalExactlyOne(msig.GetModule(), msig.GetSigTypeContext(), stubSigBuilder);
 
 #ifndef TARGET_X86
-    // The hidden context parameter
-    stubSigBuilder->AppendElementType(ELEMENT_TYPE_I);
+    if (hasInstParam)
+    {
+        // The hidden context parameter
+        stubSigBuilder->AppendElementType(ELEMENT_TYPE_I);
+    }
+
+    if (msig.HasAsyncContinuation())
+    {
+        stubSigBuilder->AppendElementType(ELEMENT_TYPE_OBJECT);
+    }
 #endif // !TARGET_X86
 
     // Copy rest of the arguments
@@ -2132,15 +2251,25 @@ void CreateInstantiatingILStubTargetSig(MethodDesc *pBaseMD,
     SigPointer pArgs = msig.GetArgProps();
     for (unsigned i = 0; i < msig.NumFixedArgs(); i++)
     {
-        pArgs.ConvertToInternalExactlyOne(msig.GetModule(), &typeContext, stubSigBuilder);
+        pArgs.ConvertToInternalExactlyOne(msig.GetModule(), msig.GetSigTypeContext(), stubSigBuilder);
     }
 
 #ifdef TARGET_X86
-    // The hidden context parameter
-    stubSigBuilder->AppendElementType(ELEMENT_TYPE_I);
+    if (hasInstParam)
+    {
+        // The hidden context parameter
+        stubSigBuilder->AppendElementType(ELEMENT_TYPE_I);
+    }
+
+    if (hasAsyncCont)
+    {
+        stubSigBuilder->AppendElementType(ELEMENT_TYPE_OBJECT);
+    }
 #endif // TARGET_X86
 }
 
+#ifdef FEATURE_INSTANTIATINGSTUB_AS_IL
+
 Stub * CreateUnboxingILStubForSharedGenericValueTypeMethods(MethodDesc* pTargetMD)
 {
 
@@ -2168,7 +2297,7 @@ Stub * CreateUnboxingILStubForSharedGenericValueTypeMethods(MethodDesc* pTargetM
 
     // 1. Build the new signature
     SigBuilder stubSigBuilder;
-    CreateInstantiatingILStubTargetSig(pTargetMD, typeContext, &stubSigBuilder);
+    MethodDesc::CreateDerivedTargetSigWithExtraParams(msig, &stubSigBuilder);
 
     // 2. Emit the method body
     mdToken tokRawData = pCode->GetToken(CoreLibBinder::GetField(FIELD__RAW_DATA__DATA));
@@ -2275,7 +2404,7 @@ Stub * CreateInstantiatingILStub(MethodDesc* pTargetMD, void* pHiddenArg)
 
     // 1. Build the new signature
     SigBuilder stubSigBuilder;
-    CreateInstantiatingILStubTargetSig(pTargetMD, typeContext, &stubSigBuilder);
+    MethodDesc::CreateDerivedTargetSigWithExtraParams(msig, &stubSigBuilder);
 
     // 2. Emit the method body
     if (msig.HasThis())
@@ -2296,6 +2425,12 @@ Stub * CreateInstantiatingILStub(MethodDesc* pTargetMD, void* pHiddenArg)
     // InstantiatingStub
     pCode->EmitLDC((TADDR)pHiddenArg);
 
+    // 2.3.2 Push the async continuation
+    if (msig.HasAsyncContinuation())
+    {
+        pCode->EmitLDNULL();
+    }
+
 #if !defined(TARGET_X86)
     // 2.4 Push the rest of the arguments for not x86
     for (unsigned i = 0; i < msig.NumFixedArgs();i++)
diff --git a/src/coreclr/vm/siginfo.cpp b/src/coreclr/vm/siginfo.cpp
index eb35f3a372538f..62638400b3e6aa 100644
--- a/src/coreclr/vm/siginfo.cpp
+++ b/src/coreclr/vm/siginfo.cpp
@@ -137,7 +137,7 @@ unsigned GetSizeForCorElementType(CorElementType etyp)
 
 #ifndef DACCESS_COMPILE
 
-void SigPointer::ConvertToInternalExactlyOne(Module* pSigModule, SigTypeContext *pTypeContext, SigBuilder * pSigBuilder, BOOL bSkipCustomModifier)
+void SigPointer::ConvertToInternalExactlyOne(Module* pSigModule, const SigTypeContext *pTypeContext, SigBuilder * pSigBuilder, BOOL bSkipCustomModifier)
 {
     CONTRACTL
     {
@@ -324,7 +324,7 @@ void SigPointer::ConvertToInternalExactlyOne(Module* pSigModule, SigTypeContext
     }
 }
 
-void SigPointer::ConvertToInternalSignature(Module* pSigModule, SigTypeContext *pTypeContext, SigBuilder * pSigBuilder, BOOL bSkipCustomModifier)
+void SigPointer::ConvertToInternalSignature(Module* pSigModule, const SigTypeContext *pTypeContext, SigBuilder * pSigBuilder, BOOL bSkipCustomModifier)
 {
     CONTRACTL
     {
@@ -663,6 +663,8 @@ MetaSig::MetaSig(MethodDesc *pMD, Instantiation classInst, Instantiation methodI
 
     if (pMD->RequiresInstArg())
         SetHasParamTypeArg();
+    if (pMD->IsAsync2Method())
+        SetIsAsyncCall();
 }
 
 MetaSig::MetaSig(MethodDesc *pMD, TypeHandle declaringType)
@@ -684,6 +686,8 @@ MetaSig::MetaSig(MethodDesc *pMD, TypeHandle declaringType)
 
     if (pMD->RequiresInstArg())
         SetHasParamTypeArg();
+    if (pMD->IsAsync2Method())
+        SetIsAsyncCall();
 }
 
 #ifdef _DEBUG
@@ -5108,6 +5112,13 @@ void ReportPointersFromValueTypeArg(promote_func *fn, ScanContext *sc, PTR_Metho
     ReportPointersFromValueType(fn, sc, pMT, pSrc->GetDestinationAddress());
 }
 
+BOOL MetaSig::HasAsyncContinuation()
+{
+    LIMITED_METHOD_CONTRACT;
+
+    return IsAsyncCall() && g_pConfig->RuntimeAsyncViaJitGeneratedStateMachines();
+}
+
 //------------------------------------------------------------------
 // Perform type-specific GC promotion on the value (based upon the
 // last type retrieved by NextArg()).
diff --git a/src/coreclr/vm/siginfo.hpp b/src/coreclr/vm/siginfo.hpp
index a0ec6b3d4a26c2..0085cf761293bc 100644
--- a/src/coreclr/vm/siginfo.hpp
+++ b/src/coreclr/vm/siginfo.hpp
@@ -125,8 +125,8 @@ class SigPointer : public SigParser
     //=========================================================================
 
 
-        void ConvertToInternalExactlyOne(Module* pSigModule, SigTypeContext *pTypeContext, SigBuilder * pSigBuilder, BOOL bSkipCustomModifier = TRUE);
-        void ConvertToInternalSignature(Module* pSigModule, SigTypeContext *pTypeContext, SigBuilder * pSigBuilder, BOOL bSkipCustomModifier = TRUE);
+        void ConvertToInternalExactlyOne(Module* pSigModule, const SigTypeContext *pTypeContext, SigBuilder * pSigBuilder, BOOL bSkipCustomModifier = TRUE);
+        void ConvertToInternalSignature(Module* pSigModule, const SigTypeContext *pTypeContext, SigBuilder * pSigBuilder, BOOL bSkipCustomModifier = TRUE);
 
 
     //=========================================================================
@@ -681,7 +681,7 @@ class MetaSig
         // Returns the calling convention & flags (see IMAGE_CEE_CS_CALLCONV_*
         // defines in cor.h)
         //----------------------------------------------------------
-        BYTE GetCallingConventionInfo()
+        USHORT GetCallingConventionInfo()
         {
             LIMITED_METHOD_DAC_CONTRACT;
 
@@ -727,6 +727,17 @@ class MetaSig
             return GetCallingConvention() == IMAGE_CEE_CS_CALLCONV_VARARG;
         }
 
+        //----------------------------------------------------------
+        // Is it an async call?
+        //----------------------------------------------------------
+        BOOL IsAsyncCall()
+        {
+            LIMITED_METHOD_CONTRACT;
+            return m_CallConv & CORINFO_CALLCONV_ASYNCCALL;
+        }
+
+        BOOL HasAsyncContinuation();
+
         //----------------------------------------------------------
         // Is vararg?
         //----------------------------------------------------------
@@ -1085,6 +1096,12 @@ class MetaSig
             m_CallConv |= CORINFO_CALLCONV_PARAMTYPE;
         }
 
+        void SetIsAsyncCall()
+        {
+            LIMITED_METHOD_CONTRACT;
+            m_CallConv |= CORINFO_CALLCONV_ASYNCCALL;
+        }
+
         void SetTreatAsVarArg()
         {
             LIMITED_METHOD_CONTRACT;
@@ -1122,7 +1139,7 @@ class MetaSig
 
         CorElementType  m_corNormalizedRetType;
         BYTE            m_flags;
-        BYTE            m_CallConv;
+        USHORT          m_CallConv;
 };  // class MetaSig
 
 BOOL IsTypeRefOrDef(LPCSTR szClassName, Module *pModule, mdToken token);

From 6d1091bd172cc1354cefbd3e217c1b3c152a34f8 Mon Sep 17 00:00:00 2001
From: David Wrighton <davidwr@microsoft.com>
Date: Mon, 23 Oct 2023 16:15:54 -0700
Subject: [PATCH 040/203] Add test case with new logic which doesn't actually
 yield, but has all the various yield points in there

---
 .../async/async2fibonacci-without-yields.cs   | 63 +++++++++++++++++++
 .../async2fibonacci-without-yields.csproj     |  9 +++
 2 files changed, 72 insertions(+)
 create mode 100644 src/tests/Loader/async/async2fibonacci-without-yields.cs
 create mode 100644 src/tests/Loader/async/async2fibonacci-without-yields.csproj

diff --git a/src/tests/Loader/async/async2fibonacci-without-yields.cs b/src/tests/Loader/async/async2fibonacci-without-yields.cs
new file mode 100644
index 00000000000000..fe665e702d5fc6
--- /dev/null
+++ b/src/tests/Loader/async/async2fibonacci-without-yields.cs
@@ -0,0 +1,63 @@
+// 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 System.Threading.Tasks;
+using System.Diagnostics;
+using Xunit;
+
+public class Async2FibonacceWithoutYields
+{
+    const uint Threshold = 1_000;
+    static bool doYields = false;
+
+    [Fact]
+    public static void Test()
+    {
+        AsyncEntry().Wait();
+    }
+
+    public static async Task AsyncEntry()
+    {
+        for (int i = 0; i < 10; i++)
+        {
+            var sw = new Stopwatch();
+            sw.Start();
+            uint result = await A(100_000_000);
+            Console.WriteLine($"{sw.ElapsedMilliseconds} ms result={result}");
+        }
+    }
+
+    static async2 uint A(uint n)
+    {
+        uint result = n;
+        for (uint i = 0; i < n; i++)
+            result = await B(result);
+        return result;
+    }
+
+    static async2 uint B(uint n)
+    {
+        uint result = n;
+
+        result = result * 1_999_999_981;
+        if ((result < Threshold) && doYields)
+        {
+            await Task.Yield();
+        }
+
+        result = result * 1_999_999_981;
+        if ((result < Threshold) && doYields)
+        {
+            await Task.Yield();
+        }
+
+        result = result * 1_999_999_981;
+        if ((result < Threshold) && doYields)
+        {
+            await Task.Yield();
+        }
+
+        return result;
+    }
+}
diff --git a/src/tests/Loader/async/async2fibonacci-without-yields.csproj b/src/tests/Loader/async/async2fibonacci-without-yields.csproj
new file mode 100644
index 00000000000000..338f2bcb6e97a5
--- /dev/null
+++ b/src/tests/Loader/async/async2fibonacci-without-yields.csproj
@@ -0,0 +1,9 @@
+<Project Sdk="Microsoft.NET.Sdk">
+  <PropertyGroup>
+    <CLRTestPriority>1</CLRTestPriority>
+    <RequiresProcessIsolation>true</RequiresProcessIsolation>
+  </PropertyGroup>
+  <ItemGroup>
+    <Compile Include="async2fibonacci-without-yields.cs" />
+  </ItemGroup>
+</Project>

From 669eb4485f3d28bc28b5efc972641fff94e99b7d Mon Sep 17 00:00:00 2001
From: David Wrighton <davidwr@microsoft.com>
Date: Mon, 23 Oct 2023 16:42:10 -0700
Subject: [PATCH 041/203] Closer to correct implementation of thunk which uses
 UnsafeAwaitAwaiterFromRuntimeAsync

---
 src/coreclr/vm/corelib.h   |  4 +++-
 src/coreclr/vm/metasig.h   |  2 +-
 src/coreclr/vm/prestub.cpp | 33 +++++++++++++++++++++++++++------
 3 files changed, 31 insertions(+), 8 deletions(-)

diff --git a/src/coreclr/vm/corelib.h b/src/coreclr/vm/corelib.h
index 0397b11ca8d683..0af14fc068c474 100644
--- a/src/coreclr/vm/corelib.h
+++ b/src/coreclr/vm/corelib.h
@@ -365,6 +365,8 @@ DEFINE_METHOD(RUNTIME_TASK_STATE_1, FROM_EXCEPTION, FromException, IM_Exception_
 DEFINE_METHOD(RUNTIME_TASK_STATE_1, FROM_RESULT, FromResult, IM_T_RetTaskOfT)
 
 DEFINE_CLASS(TASK_AWAITER_1, CompilerServices, TaskAwaiter`1)
+DEFINE_METHOD(TASK_AWAITER_1, GET_ISCOMPLETED, get_IsCompleted, NoSig)
+DEFINE_METHOD(TASK_AWAITER_1, GET_RESULT, GetResult, NoSig)
 
 DEFINE_CLASS(TYPE_HANDLE,           System,                 RuntimeTypeHandle)
 DEFINE_CLASS(RT_TYPE_HANDLE,        System,                 RuntimeTypeHandle)
@@ -685,7 +687,7 @@ DEFINE_METHOD(RUNTIME_HELPERS,      DISPATCH_TAILCALLS,     DispatchTailCalls, N
 DEFINE_METHOD(RUNTIME_HELPERS,      ALLOC_CONTINUATION,     AllocContinuation, NoSig)
 
 DEFINE_METHOD(RUNTIME_HELPERS,      GET_OR_CREATE_RESUMPTION_DELEGATE, GetOrCreateResumptionDelegate, NoSig)
-DEFINE_METHOD(RUNTIME_HELPERS,      UNSAFE_AWAIT_AWAITER_FROM_RUNTIME_ASYNC_2, UnsafeAwaitAwaiterFromRuntimeAsync, GM_U_RetT)
+DEFINE_METHOD(RUNTIME_HELPERS,      UNSAFE_AWAIT_AWAITER_FROM_RUNTIME_ASYNC_1, UnsafeAwaitAwaiterFromRuntimeAsync, GM_T_RetVoid)
 
 DEFINE_CLASS(UNSAFE,                CompilerServices,       Unsafe)
 DEFINE_METHOD(UNSAFE,               AS_POINTER,             AsPointer, NoSig)
diff --git a/src/coreclr/vm/metasig.h b/src/coreclr/vm/metasig.h
index 9616fe0e45e2a3..ee0a74b54d2374 100644
--- a/src/coreclr/vm/metasig.h
+++ b/src/coreclr/vm/metasig.h
@@ -624,7 +624,7 @@ DEFINE_METASIG(SM(PtrByte_RetStr, P(b), s))
 DEFINE_METASIG(SM(Str_RetPtrByte, s, P(b)))
 DEFINE_METASIG(SM(PtrByte_RetVoid, P(b), v))
 
-DEFINE_METASIG(GM(U_RetT, IMAGE_CEE_CS_CALLCONV_DEFAULT, 1, M(1), M(0)))
+DEFINE_METASIG(GM(T_RetVoid, IMAGE_CEE_CS_CALLCONV_DEFAULT, 1, M(0), v))
 
 // Undefine macros in case we include the file again in the compilation unit
 
diff --git a/src/coreclr/vm/prestub.cpp b/src/coreclr/vm/prestub.cpp
index d055e430aa4db4..d8c8dfa37e799b 100644
--- a/src/coreclr/vm/prestub.cpp
+++ b/src/coreclr/vm/prestub.cpp
@@ -1717,8 +1717,13 @@ void MethodDesc::EmitAsync2MethodThunk(MethodDesc* pAsyncOtherVariant, MetaSig&
     // Implement IL that is effectively the following
     /*
     {
-        // Call target method and await
-        return RuntimeHelpers.UnsafeAwaitAwaiterFromRuntimeAsync<AwaiterType, resultType>(Thunk(arg).GetAwaiter());
+        var awaiter = Thunk(arg).GetAwaiter();
+        if (!awaiter.IsCompleted)
+        {
+            // Magic function which will suspend the current run of async methods
+            RuntimeHelpers.UnsafeAwaitAwaiterFromRuntimeAsync<AwaiterType>(awaiter);
+        }
+        return awaiter.GetResult();
     }
     */
     ILCodeStream* pCode = pSL->NewCodeStream(ILStubLinker::kDispatch);
@@ -1728,6 +1733,12 @@ void MethodDesc::EmitAsync2MethodThunk(MethodDesc* pAsyncOtherVariant, MetaSig&
     MethodTable* pMTTaskAwaiterOpen = CoreLibBinder::GetClass(CLASS__TASK_AWAITER_1);
     TypeHandle thTaskAwaiter = ClassLoader::LoadGenericInstantiationThrowing(pMTTaskAwaiterOpen->GetModule(), pMTTaskAwaiterOpen->GetCl(), Instantiation(&thLogicalRetType, 1));
     DWORD localArg = 0;
+
+    ILCodeLabel* pGetResultLabel = pCode->NewCodeLabel();
+
+    LocalDesc awaiterLocalDesc(CoreLibBinder::GetClass(CLASS__EXCEPTION));
+    DWORD awaiterLocal = pCode->NewLocal(awaiterLocalDesc);
+
     if (msig.HasThis())
     {
         pCode->EmitLDARG(localArg++);
@@ -1738,12 +1749,22 @@ void MethodDesc::EmitAsync2MethodThunk(MethodDesc* pAsyncOtherVariant, MetaSig&
     }
     pCode->EmitCALL(pCode->GetToken(pAsyncOtherVariant), localArg, 1);
     pCode->EmitCALLVIRT(pCode->GetToken(pMTTask->GetParallelMethodDesc(CoreLibBinder::GetMethod(METHOD__TASK_1__GET_AWAITER))), 1, 1);
-    MethodDesc* pMagicAwaitFunc = CoreLibBinder::GetMethod(METHOD__RUNTIME_HELPERS__UNSAFE_AWAIT_AWAITER_FROM_RUNTIME_ASYNC_2);
-    TypeHandle thInstantiations[]{ thLogicalRetType, thTaskAwaiter };
+    pCode->EmitSTLOC(awaiterLocal);
+    pCode->EmitLDLOCA(awaiterLocal);
+    pCode->EmitCALL(pCode->GetToken(thTaskAwaiter.GetMethodTable()->GetParallelMethodDesc(CoreLibBinder::GetMethod(METHOD__TASK_AWAITER_1__GET_ISCOMPLETED))), 1, 1);
+    pCode->EmitBRTRUE(pGetResultLabel);
+    pCode->EmitLDLOC(awaiterLocal);
+    MethodDesc* pMagicAwaitFunc = CoreLibBinder::GetMethod(METHOD__RUNTIME_HELPERS__UNSAFE_AWAIT_AWAITER_FROM_RUNTIME_ASYNC_1);
+    TypeHandle thInstantiations[]{ thTaskAwaiter };
+
+    pMagicAwaitFunc = FindOrCreateAssociatedMethodDesc(pMagicAwaitFunc, pMagicAwaitFunc->GetMethodTable(), FALSE, Instantiation(thInstantiations, 1), FALSE);
+
+    pCode->EmitCALL(pCode->GetToken(pMagicAwaitFunc), 1, 0);
+    pCode->EmitLabel(pGetResultLabel);
 
-    pMagicAwaitFunc = FindOrCreateAssociatedMethodDesc(pMagicAwaitFunc, pMagicAwaitFunc->GetMethodTable(), FALSE, Instantiation(thInstantiations, 2), FALSE);
+    pCode->EmitLDLOCA(awaiterLocal);
+    pCode->EmitCALL(pCode->GetToken(thTaskAwaiter.GetMethodTable()->GetParallelMethodDesc(CoreLibBinder::GetMethod(METHOD__TASK_AWAITER_1__GET_RESULT))), 1, 1);
 
-    pCode->EmitCALL(pCode->GetToken(pMagicAwaitFunc), 1, 1);
     pCode->EmitRET();
 }
 

From 34897d7c9401e412857a2c98c4de2cb7c0fa7a19 Mon Sep 17 00:00:00 2001
From: Jakob Botsch Nielsen <janielsen@microsoft.com>
Date: Tue, 24 Oct 2023 16:34:00 +0000
Subject: [PATCH 042/203] Merged PR 34763: Get suspension working with jit
 state machines

---
 .../RuntimeHelpers.CoreCLR.cs                 |  86 ++++++++++++-
 src/coreclr/jit/async.cpp                     |  40 ++++--
 src/coreclr/jit/async.h                       |   2 +-
 src/coreclr/jit/compiler.h                    |   1 +
 src/coreclr/jit/fgstmt.cpp                    |   1 +
 src/coreclr/jit/gentree.cpp                   |  12 +-
 src/coreclr/jit/gentree.h                     |   1 +
 src/coreclr/jit/gtlist.h                      |   3 +
 src/coreclr/jit/importercalls.cpp             |  51 +++++++-
 src/coreclr/jit/lower.cpp                     |  13 ++
 src/coreclr/jit/lower.h                       |   1 +
 src/coreclr/jit/lsra.cpp                      |   2 +
 src/coreclr/jit/lsra.h                        |   4 +
 src/coreclr/jit/lsrabuild.cpp                 |  21 ++++
 src/coreclr/jit/lsraxarch.cpp                 |   5 +
 src/coreclr/jit/morph.cpp                     |   1 +
 src/coreclr/jit/namedintrinsiclist.h          |   4 +
 src/coreclr/jit/valuenum.cpp                  |   1 +
 src/coreclr/vm/corelib.h                      |   1 +
 src/coreclr/vm/metasig.h                      |   1 +
 src/coreclr/vm/methodtablebuilder.cpp         |   1 +
 src/coreclr/vm/prestub.cpp                    |   6 +-
 .../CompilerServices/RuntimeHelpers.cs        | 114 ++++++++++++------
 23 files changed, 312 insertions(+), 60 deletions(-)

diff --git a/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs
index 5ee4958fa2a91d..c716980c657cdf 100644
--- a/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs
+++ b/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs
@@ -9,6 +9,7 @@
 using System.Runtime.Serialization;
 using System.Runtime.Versioning;
 using System.Threading;
+using System.Threading.Tasks;
 
 namespace System.Runtime.CompilerServices
 {
@@ -387,7 +388,84 @@ private static object AllocContinuation(object prevContinuation, nuint numGCRefs
             Unsafe.Unbox<Continuation>(prevContinuation).Next = newContinuation;
             return newContinuation;
         }
+
+        private struct RuntimeAsyncAwaitState
+        {
+            public object? SentinelContinuation;
+            public INotifyCompletion? Notifier;
+        }
+
+        [ThreadStatic]
+        private static RuntimeAsyncAwaitState t_runtimeAsyncAwaitState;
+
+        [Intrinsic]
+        private static void SuspendAsync2(object continuation) => throw new UnreachableException();
+
+        private static Task<T> FinalizeTaskReturningThunk<T>(object boxedContinuation)
+        {
+            TaskCompletionSource<T> tcs = new();
+            ref RuntimeAsyncAwaitState state = ref t_runtimeAsyncAwaitState;
+            ref Continuation continuation = ref Unsafe.Unbox<Continuation>(boxedContinuation);
+            object[] gcData = new object[2];
+            gcData[1] = tcs;
+            continuation.Next = new Continuation
+            {
+                Resume = &ResumeTaskCompletionSource<T>,
+                GCData = gcData,
+            };
+
+            object headContinuation = Unsafe.Unbox<Continuation>(state.SentinelContinuation!).Next!;
+            state.Notifier!.OnCompleted(() => DispatchContinuations(headContinuation));
+            return tcs.Task;
+        }
+
+        private static object? ResumeTaskCompletionSource<T>(object boxedContinuation, Exception? exception)
+        {
+            ref Continuation continuation = ref Unsafe.Unbox<Continuation>(boxedContinuation);
+            var tcs = (TaskCompletionSource<T>)continuation.GCData![1];
+            if (exception == null)
+            {
+                tcs.SetResult((T)continuation.GCData![0]);
+            }
+            else
+            {
+                tcs.SetException(exception);
+            }
+
+            return null;
+        }
+
+        private static unsafe void DispatchContinuations(object? boxedContinuation)
+        {
+            Exception? exToPropagate = null;
+            do
+            {
+                ref Continuation continuation = ref Unsafe.Unbox<Continuation>(boxedContinuation!);
+                object? newContinuation = null;
+                try
+                {
+                    newContinuation = continuation.Resume(boxedContinuation!, exToPropagate);
+                }
+                catch (Exception ex)
+                {
+                    exToPropagate = ex;
+                }
+
+                if (newContinuation != null)
+                {
+                    Unsafe.Unbox<Continuation>(newContinuation).Next = continuation.Next;
+
+                    ref RuntimeAsyncAwaitState state = ref t_runtimeAsyncAwaitState;
+                    object headContinuation = Unsafe.Unbox<Continuation>(state.SentinelContinuation!).Next!;
+                    state.Notifier!.OnCompleted(() => DispatchContinuations(headContinuation));
+                    return;
+                }
+
+                boxedContinuation = continuation.Next;
+            } while (boxedContinuation != null);
+        }
     }
+
     // Helper class to assist with unsafe pinning of arbitrary objects.
     // It's used by VM code.
     [NonVersionable] // This only applies to field layout
@@ -670,10 +748,10 @@ internal unsafe struct TailCallTls
 
     internal unsafe struct Continuation
     {
-        public object Next;
-        public delegate*<ref Continuation, Exception, object> Resume;
+        public object? Next;
+        public delegate*<object, Exception?, object?> Resume;
         public uint State;
-        public byte[] Data;
-        public object GCData;
+        public byte[]? Data;
+        public object[]? GCData;
     }
 }
diff --git a/src/coreclr/jit/async.cpp b/src/coreclr/jit/async.cpp
index 84e9cea1cdba79..26900f33a034e0 100644
--- a/src/coreclr/jit/async.cpp
+++ b/src/coreclr/jit/async.cpp
@@ -88,9 +88,9 @@ PhaseStatus Async2Transformation::Run()
 
                 life.UpdateLife(tree);
 
-                if (tree->IsCall() && tree->AsCall()->IsAsync2())
+                if (tree->IsCall() && tree->AsCall()->IsAsync2() && !tree->AsCall()->IsTailCall())
                 {
-                    Transform(block, tree->AsCall(), defs, &block);
+                    Transform(block, tree->AsCall(), defs, life, &block);
                     defs.clear();
                     any = true;
                     break;
@@ -112,6 +112,7 @@ PhaseStatus Async2Transformation::Run()
 void Async2Transformation::Transform(BasicBlock*               block,
                                      GenTreeCall*              call,
                                      jitstd::vector<GenTree*>& defs,
+                                     TreeLifeUpdater<false>&   life,
                                      BasicBlock**              pRemainder)
 {
 #ifdef DEBUG
@@ -270,15 +271,22 @@ void Async2Transformation::Transform(BasicBlock*               block,
 
             use.ReplaceWithLclVar(m_comp);
         }
+        else
+        {
+            // We will split after the store, but we still have to update liveness for it.
+            life.UpdateLife(call->gtNext);
+        }
 
         assert(call->gtNext->OperIsLocalStore() && (call->gtNext->Data() == call));
         storeResultNode = call->gtNext->AsLclVarCommon();
         insertAfter     = call->gtNext;
     }
 
-    GenTree* continuationPhysReg = m_comp->gtNewPhysRegNode(REG_ASYNC_CONTINUATION_RET, TYP_REF);
-    GenTree* storeContinuation   = m_comp->gtNewStoreLclVarNode(m_returnedContinuationVar, continuationPhysReg);
-    LIR::AsRange(block).InsertAfter(insertAfter, continuationPhysReg, storeContinuation);
+    GenTree* continuationArg = new (m_comp, GT_ASYNC_CONTINUATION) GenTree(GT_ASYNC_CONTINUATION, TYP_REF);
+    continuationArg->SetHasOrderingSideEffect();
+
+    GenTree* storeContinuation   = m_comp->gtNewStoreLclVarNode(m_returnedContinuationVar, continuationArg);
+    LIR::AsRange(block).InsertAfter(insertAfter, continuationArg, storeContinuation);
 
     GenTree* null                 = m_comp->gtNewNull();
     GenTree* returnedContinuation = m_comp->gtNewLclvNode(m_returnedContinuationVar, TYP_REF);
@@ -501,7 +509,7 @@ void Async2Transformation::Transform(BasicBlock*               block,
     {
         unsigned byteArrLclNum = GetDataArrayVar();
 
-        GenTree* newContinuation = m_comp->gtNewLclvNode(m_newContinuationVar, TYP_REF);
+        GenTree* newContinuation = m_comp->gtNewLclvNode(m_comp->lvaAsyncContinuationArg, TYP_REF);
         unsigned dataOffset =
             TARGET_POINTER_SIZE + m_comp->info.compCompHnd->getFieldOffset(m_async2Info.continuationDataFldHnd);
         GenTree* dataOffsetNode      = m_comp->gtNewIconNode((ssize_t)dataOffset, TYP_I_IMPL);
@@ -557,7 +565,7 @@ void Async2Transformation::Transform(BasicBlock*               block,
     {
         unsigned objectArrLclNum = GetGCDataArrayVar();
 
-        newContinuation = m_comp->gtNewLclvNode(m_newContinuationVar, TYP_REF);
+        newContinuation = m_comp->gtNewLclvNode(m_comp->lvaAsyncContinuationArg, TYP_REF);
         unsigned gcDataOffset =
             TARGET_POINTER_SIZE + m_comp->info.compCompHnd->getFieldOffset(m_async2Info.continuationGCDataFldHnd);
         GenTree* gcDataOffsetNode      = m_comp->gtNewIconNode((ssize_t)gcDataOffset, TYP_I_IMPL);
@@ -747,6 +755,15 @@ bool Async2Transformation::IsLive(unsigned lclNum)
         return false;
     }
 
+    if ((dsc->TypeGet() == TYP_BYREF) || ((dsc->TypeGet() == TYP_STRUCT) && dsc->GetLayout()->HasGCByRef()))
+    {
+        // Even if these are address exposed we expect them to be dead at
+        // suspension points. TODO: It would be good to somehow verify these
+        // aren't obviously live, if the JIT creates live ranges that span a
+        // suspension point then this makes it quite hard to diagnose that.
+        return false;
+    }
+
     Compiler::lvaPromotionType promoType = m_comp->lvaGetPromotionType(dsc);
     if (promoType == Compiler::PROMOTION_TYPE_INDEPENDENT)
     {
@@ -938,13 +955,16 @@ void Async2Transformation::CreateResumptionSwitch()
     }
     else if (m_resumptionBBs.size() == 2)
     {
-        BasicBlock* condBB = m_comp->fgNewBBbefore(BBJ_COND, m_resumptionBBs[0], true);
+        BasicBlock* condBB = m_comp->fgNewBBbefore(BBJ_COND, m_resumptionBBs[0], true, m_resumptionBBs[1]);
         condBB->bbSetRunRarely();
         newEntryBB->SetJumpKindAndTarget(BBJ_COND, condBB DEBUGARG(m_comp));
 
         JITDUMP("New cond BB: " FMT_BB "\n", condBB->bbNum);
 
         m_comp->fgAddRefPred(condBB, newEntryBB);
+        m_comp->fgAddRefPred(m_resumptionBBs[0], condBB);
+        m_comp->fgAddRefPred(m_resumptionBBs[1], condBB);
+
         continuationArg = m_comp->gtNewLclvNode(m_comp->lvaAsyncContinuationArg, TYP_REF);
         unsigned stateOffset =
             TARGET_POINTER_SIZE + m_comp->info.compCompHnd->getFieldOffset(m_async2Info.continuationStateFldHnd);
@@ -957,10 +977,6 @@ void Async2Transformation::CreateResumptionSwitch()
 
         LIR::AsRange(condBB).InsertAtEnd(continuationArg, stateOffsetNode, stateAddr, stateInd, zero, stateNeZero,
                                          jtrue);
-        condBB->SetJumpDest(m_resumptionBBs[1]);
-
-        m_comp->fgAddRefPred(m_resumptionBBs[0], condBB);
-        m_comp->fgAddRefPred(m_resumptionBBs[1], condBB);
     }
     else
     {
diff --git a/src/coreclr/jit/async.h b/src/coreclr/jit/async.h
index 2bc6838eb6ce6b..37e79563511efe 100644
--- a/src/coreclr/jit/async.h
+++ b/src/coreclr/jit/async.h
@@ -35,7 +35,7 @@ class Async2Transformation
                       jitstd::vector<GenTree*>&      defs,
                       jitstd::vector<LiveLocalInfo>& liveLocals);
     bool IsLive(unsigned lclNum);
-    void Transform(BasicBlock* block, GenTreeCall* call, jitstd::vector<GenTree*>& defs, BasicBlock** remainder);
+    void Transform(BasicBlock* block, GenTreeCall* call, jitstd::vector<GenTree*>& defs, TreeLifeUpdater<false>& life, BasicBlock** remainder);
 
     GenTreeIndir* LoadFromOffset(GenTree* base, unsigned offset, var_types type);
     GenTreeStoreInd* StoreAtOffset(GenTree* base, unsigned offset, GenTree* value);
diff --git a/src/coreclr/jit/compiler.h b/src/coreclr/jit/compiler.h
index 0df3b1b4b237f4..fb6d2fe11f411d 100644
--- a/src/coreclr/jit/compiler.h
+++ b/src/coreclr/jit/compiler.h
@@ -11283,6 +11283,7 @@ class GenTreeVisitor
             case GT_NOP:
             case GT_FIELD_ADDR:
             case GT_RETURN:
+            case GT_RETURN_SUSPEND:
             case GT_RETFILT:
             case GT_RUNTIMELOOKUP:
             case GT_ARR_ADDR:
diff --git a/src/coreclr/jit/fgstmt.cpp b/src/coreclr/jit/fgstmt.cpp
index fead5b82e0b347..8bec07ed2ca365 100644
--- a/src/coreclr/jit/fgstmt.cpp
+++ b/src/coreclr/jit/fgstmt.cpp
@@ -538,6 +538,7 @@ inline bool OperIsControlFlow(genTreeOps oper)
 
         case GT_RETURN:
         case GT_RETFILT:
+        case GT_RETURN_SUSPEND:
 #if !defined(FEATURE_EH_FUNCLETS)
         case GT_END_LFIN:
 #endif // !FEATURE_EH_FUNCLETS
diff --git a/src/coreclr/jit/gentree.cpp b/src/coreclr/jit/gentree.cpp
index 4d8eb99561d0da..cd5ed56638a563 100644
--- a/src/coreclr/jit/gentree.cpp
+++ b/src/coreclr/jit/gentree.cpp
@@ -2364,7 +2364,7 @@ bool GenTreeCall::HasSideEffects(Compiler* compiler, bool ignoreExceptions, bool
 
 bool GenTreeCall::IsAsync2() const
 {
-    return gtArgs.HasAsyncContinuation();
+    return gtIsAsyncCall;
 }
 
 //-------------------------------------------------------------------------
@@ -6492,6 +6492,7 @@ bool GenTree::TryGetUse(GenTree* operand, GenTree*** pUse)
         case GT_NOP:
         case GT_RETURN:
         case GT_RETFILT:
+        case GT_RETURN_SUSPEND:
         case GT_BSWAP:
         case GT_BSWAP16:
         case GT_KEEPALIVE:
@@ -6809,9 +6810,9 @@ bool GenTree::OperRequiresCallFlag(Compiler* comp) const
     switch (gtOper)
     {
         case GT_CALL:
-            return true;
-
         case GT_KEEPALIVE:
+        case GT_ASYNC_CONTINUATION:
+        case GT_RETURN_SUSPEND:
             return true;
 
         case GT_INTRINSIC:
@@ -7108,6 +7109,8 @@ bool GenTree::OperRequiresGlobRefFlag(Compiler* comp) const
         case GT_CMPXCHG:
         case GT_MEMORYBARRIER:
         case GT_KEEPALIVE:
+        case GT_ASYNC_CONTINUATION:
+        case GT_RETURN_SUSPEND:
             return true;
 
         case GT_CALL:
@@ -7167,6 +7170,7 @@ bool GenTree::OperSupportsOrderingSideEffect() const
         case GT_MEMORYBARRIER:
         case GT_CATCH_ARG:
         case GT_ASYNC_CONTINUATION:
+        case GT_RETURN_SUSPEND:
             return true;
         default:
             return false;
@@ -8092,6 +8096,7 @@ GenTreeCall* Compiler::gtNewCallNode(gtCallTypes           callType,
     node->gtRetClsHnd       = nullptr;
     node->gtControlExpr     = nullptr;
     node->gtCallMoreFlags   = GTF_CALL_M_EMPTY;
+    node->gtIsAsyncCall = false;
     node->gtInlineInfoCount = 0;
 
     if (callType == CT_INDIRECT)
@@ -9751,6 +9756,7 @@ GenTreeCall* Compiler::gtCloneExprCallHelper(GenTreeCall* tree,
         copy->gtInlineInfoCount     = tree->gtInlineInfoCount;
     }
 
+    copy->gtIsAsyncCall = tree->gtIsAsyncCall;
     copy->gtCallType   = tree->gtCallType;
     copy->gtReturnType = tree->gtReturnType;
 
diff --git a/src/coreclr/jit/gentree.h b/src/coreclr/jit/gentree.h
index 34441aabe02820..d0090619e4a2a6 100644
--- a/src/coreclr/jit/gentree.h
+++ b/src/coreclr/jit/gentree.h
@@ -5582,6 +5582,7 @@ struct GenTreeCall final : public GenTree
     var_types        gtReturnType : 5; // exact return type
 
     uint8_t gtInlineInfoCount; // number of inline candidates for the given call
+    bool gtIsAsyncCall;
 
     CORINFO_CLASS_HANDLE gtRetClsHnd; // The return type handle of the call if it is a struct; always available
     union {
diff --git a/src/coreclr/jit/gtlist.h b/src/coreclr/jit/gtlist.h
index 7d3e07485fa4ea..2a8e2ab8fdf2ae 100644
--- a/src/coreclr/jit/gtlist.h
+++ b/src/coreclr/jit/gtlist.h
@@ -280,6 +280,9 @@ GTNODE(RETURN           , GenTreeOp          ,0,1,GTK_UNOP|GTK_NOVALUE)
 GTNODE(SWITCH           , GenTreeOp          ,0,1,GTK_UNOP|GTK_NOVALUE)
 GTNODE(NO_OP            , GenTree            ,0,0,GTK_LEAF|GTK_NOVALUE) // A NOP that cannot be deleted.
 
+// Suspend an async2 method, returning a continuation.
+// Before lowering this is a seemingly normal TYP_VOID node with a lot of side effects (GTF_CALL | GTF_GLOB_REF | GTF_ORDER_SIDEEFF).
+// Lowering then removes all successor nodes and leaves it as the terminator node.
 GTNODE(RETURN_SUSPEND   , GenTreeOp          ,0,1,GTK_UNOP|GTK_NOVALUE) // Return a continuation in an async2 method
 
 GTNODE(START_NONGC      , GenTree            ,0,0,GTK_LEAF|GTK_NOVALUE|DBK_NOTHIR) // Starts a new instruction group that will be non-gc interruptible.
diff --git a/src/coreclr/jit/importercalls.cpp b/src/coreclr/jit/importercalls.cpp
index 15f2c58159f594..0fa5fc679fa56b 100644
--- a/src/coreclr/jit/importercalls.cpp
+++ b/src/coreclr/jit/importercalls.cpp
@@ -237,7 +237,7 @@ var_types Compiler::impImportCall(OPCODE                  opcode,
             if (call != nullptr)
             {
 #ifdef FEATURE_READYTORUN
-                if (call->OperGet() == GT_INTRINSIC)
+                if (call->OperIs(GT_INTRINSIC))
                 {
                     if (opts.IsReadyToRun())
                     {
@@ -518,6 +518,16 @@ var_types Compiler::impImportCall(OPCODE                  opcode,
         {
             call->AsCall()->gtCallMoreFlags |= GTF_CALL_M_SPECIAL_INTRINSIC;
         }
+
+        // Temporary hack since these functions have to be recognizes as async2
+        // calls in JIT generated state machines only.
+        if (compIsAsync2StateMachine() &&
+            ((ni == NI_System_Runtime_CompilerServices_RuntimeHelpers_AwaitAwaiterFromRuntimeAsync) ||
+             (ni == NI_System_Runtime_CompilerServices_RuntimeHelpers_UnsafeAwaitAwaiterFromRuntimeAsync)))
+        {
+            assert((call != nullptr) && call->OperIs(GT_CALL));
+            call->AsCall()->gtIsAsyncCall = true;
+        }
     }
     assert(sig);
     assert(clsHnd || (opcode == CEE_CALLI)); // We're never verifying for CALLI, so this is not set.
@@ -716,6 +726,11 @@ var_types Compiler::impImportCall(OPCODE                  opcode,
         }
     }
 
+    if (sig->isAsyncCall())
+    {
+        call->AsCall()->gtIsAsyncCall = true;
+    }
+
     // Now create the argument list.
 
     //-------------------------------------------------------------------------
@@ -887,7 +902,7 @@ var_types Compiler::impImportCall(OPCODE                  opcode,
                                                            .WellKnown(WellKnownArg::VarArgsCookie));
             }
 
-            if (compIsAsync2StateMachine() && sig->isAsyncCall())
+            if (call->AsCall()->gtIsAsyncCall && compIsAsync2StateMachine())
             {
                 call->AsCall()->gtArgs.PushFront(this, NewCallArg::Primitive(gtNewNull(), TYP_REF)
                                                            .WellKnown(WellKnownArg::AsyncContinuation));
@@ -907,7 +922,7 @@ var_types Compiler::impImportCall(OPCODE                  opcode,
                                                 NewCallArg::Primitive(instParam).WellKnown(WellKnownArg::InstParam));
             }
 
-            if (compIsAsync2StateMachine() && sig->isAsyncCall())
+            if (call->AsCall()->gtIsAsyncCall && compIsAsync2StateMachine())
             {
                 call->AsCall()->gtArgs.PushBack(this, NewCallArg::Primitive(gtNewNull(), TYP_REF)
                                                           .WellKnown(WellKnownArg::AsyncContinuation));
@@ -2565,9 +2580,23 @@ GenTree* Compiler::impIntrinsic(GenTree*                newobjThis,
     {
         GenTree* node = new (this, GT_ASYNC_CONTINUATION) GenTree(GT_ASYNC_CONTINUATION, TYP_REF);
         node->SetHasOrderingSideEffect();
+        node->gtFlags |= GTF_CALL | GTF_GLOB_REF;
+        return node;
+    }
+
+    if (ni == NI_System_Runtime_CompilerServices_RuntimeHelpers_SuspendAsync2)
+    {
+        GenTree* node = gtNewOperNode(GT_RETURN_SUSPEND, TYP_VOID, impPopStack().val);
+        node->SetHasOrderingSideEffect();
+        node->gtFlags |= GTF_CALL | GTF_GLOB_REF;
         return node;
     }
 
+    if (ni == NI_System_Runtime_CompilerServices_RuntimeHelpers_get_RuntimeAsyncViaJitGeneratedStateMachines)
+    {
+        return gtNewIconNode(JitConfig.RuntimeAsyncViaJitGeneratedStateMachines() != 0 ? 1 : 0);
+    }
+
     bool betterToExpand = false;
 
     // Allow some lighweight intrinsics in Tier0 which can improve throughput
@@ -8989,6 +9018,22 @@ NamedIntrinsic Compiler::lookupNamedIntrinsic(CORINFO_METHOD_HANDLE method)
                         {
                             result = NI_System_Runtime_CompilerServices_RuntimeHelpers_IsKnownConstant;
                         }
+                        else if (strcmp(methodName, "AwaitAwaiterFromRuntimeAsync") == 0)
+                        {
+                            result = NI_System_Runtime_CompilerServices_RuntimeHelpers_AwaitAwaiterFromRuntimeAsync;
+                        }
+                        else if (strcmp(methodName, "UnsafeAwaitAwaiterFromRuntimeAsync") == 0)
+                        {
+                            result = NI_System_Runtime_CompilerServices_RuntimeHelpers_UnsafeAwaitAwaiterFromRuntimeAsync;
+                        }
+                        else if (strcmp(methodName, "SuspendAsync2") == 0)
+                        {
+                            result = NI_System_Runtime_CompilerServices_RuntimeHelpers_SuspendAsync2;
+                        }
+                        else if (strcmp(methodName, "get_RuntimeAsyncViaJitGeneratedStateMachines") == 0)
+                        {
+                            result = NI_System_Runtime_CompilerServices_RuntimeHelpers_get_RuntimeAsyncViaJitGeneratedStateMachines;
+                        }
                     }
                     else if (strcmp(className, "Unsafe") == 0)
                     {
diff --git a/src/coreclr/jit/lower.cpp b/src/coreclr/jit/lower.cpp
index 35677c2bad83fe..6d8a90858fab39 100644
--- a/src/coreclr/jit/lower.cpp
+++ b/src/coreclr/jit/lower.cpp
@@ -524,6 +524,10 @@ GenTree* Lowering::LowerNode(GenTree* node)
             LowerRet(node->AsUnOp());
             break;
 
+        case GT_RETURN_SUSPEND:
+            LowerReturnSuspend(node);
+            break;
+
         case GT_RETURNTRAP:
             ContainCheckReturnTrap(node->AsOp());
             break;
@@ -4678,6 +4682,15 @@ void Lowering::LowerRetSingleRegStructLclVar(GenTreeUnOp* ret)
     }
 }
 
+void Lowering::LowerReturnSuspend(GenTree* node)
+{
+    assert(node->OperIs(GT_RETURN_SUSPEND));
+    while (BlockRange().LastNode() != node)
+    {
+        BlockRange().Remove(BlockRange().LastNode(), true);
+    }
+}
+
 //----------------------------------------------------------------------------------------------
 // LowerCallStruct: Lowers a call node that returns a struct.
 //
diff --git a/src/coreclr/jit/lower.h b/src/coreclr/jit/lower.h
index 157ed28e70fbb9..314e20af259ca9 100644
--- a/src/coreclr/jit/lower.h
+++ b/src/coreclr/jit/lower.h
@@ -155,6 +155,7 @@ class Lowering final : public Phase
     void LowerStoreLocCommon(GenTreeLclVarCommon* lclVar);
     void LowerRetStruct(GenTreeUnOp* ret);
     void LowerRetSingleRegStructLclVar(GenTreeUnOp* ret);
+    void LowerReturnSuspend(GenTree* retSuspend);
     void LowerCallStruct(GenTreeCall* call);
     void LowerStoreSingleRegCallStruct(GenTreeBlk* store);
 #if !defined(WINDOWS_AMD64_ABI)
diff --git a/src/coreclr/jit/lsra.cpp b/src/coreclr/jit/lsra.cpp
index 391682418ed1e2..34f05670e22713 100644
--- a/src/coreclr/jit/lsra.cpp
+++ b/src/coreclr/jit/lsra.cpp
@@ -5168,6 +5168,8 @@ void LinearScan::allocateRegisters()
                 }
                 clearRegBusyUntilKill(regRecord->regNum);
                 INDEBUG(dumpLsraAllocationEvent(LSRA_EVENT_KEPT_ALLOCATION, nullptr, regRecord->regNum));
+                if (currentRefPosition.busyUntilNextKill)
+                    setRegBusyUntilKill(regRecord->regNum, regRecord->registerType);
                 continue;
             }
         }
diff --git a/src/coreclr/jit/lsra.h b/src/coreclr/jit/lsra.h
index d0f7aeb8369695..246c6854a8279f 100644
--- a/src/coreclr/jit/lsra.h
+++ b/src/coreclr/jit/lsra.h
@@ -2500,6 +2500,9 @@ class RefPosition
     unsigned char skipSaveRestore : 1;
 #endif
 
+    // For a phys reg, indicates that it should be marked as busy until the next time it is killed.
+    unsigned char busyUntilNextKill : 1;
+
 #ifdef DEBUG
     // Minimum number registers that needs to be ensured while
     // constraining candidates for this ref position under
@@ -2543,6 +2546,7 @@ class RefPosition
         , isLocalDefUse(false)
         , delayRegFree(false)
         , outOfOrder(false)
+        , busyUntilNextKill(false)
 #ifdef DEBUG
         , minRegCandidateCount(1)
         , rpNum(0)
diff --git a/src/coreclr/jit/lsrabuild.cpp b/src/coreclr/jit/lsrabuild.cpp
index 7d9803c645799d..bdba9cef9fc72d 100644
--- a/src/coreclr/jit/lsrabuild.cpp
+++ b/src/coreclr/jit/lsrabuild.cpp
@@ -1231,6 +1231,27 @@ bool LinearScan::buildKillPositionsForNode(GenTree* tree, LsraLocation currentLo
         insertedKills = true;
     }
 
+    if (tree->IsCall() && tree->AsCall()->IsAsync2())
+    {
+        // Async2 calls return an async continuation argument in a separate
+        // register. Since we do not have a flexible representation for
+        // multiple definitions (multi-reg support is tied into promotion) we
+        // have to utilize a hack here to make it work. We expect the return
+        // value to be consumed by an upcoming ASYNC_CONTINUATION node, but we
+        // must take care not to overwrite the register until we get to that
+        // node. To accomplish that we mark the register as "busy until next
+        // kill" when we see the call's kill, and then we have
+        // ASYNC_CONTINUATION insert its own kill to free up the register
+        // again.
+
+        assert((killMask & RBM_ASYNC_CONTINUATION_RET) != 0);
+
+        RegRecord* asyncRetRegRec = getRegisterRecord(REG_ASYNC_CONTINUATION_RET);
+        RefPosition* pos = asyncRetRegRec->lastRefPosition;
+        assert(pos->refType == RefTypeKill);
+        pos->busyUntilNextKill = true;
+    }
+
     return insertedKills;
 }
 
diff --git a/src/coreclr/jit/lsraxarch.cpp b/src/coreclr/jit/lsraxarch.cpp
index 92fe1d29157829..cd792cb6d9af4b 100644
--- a/src/coreclr/jit/lsraxarch.cpp
+++ b/src/coreclr/jit/lsraxarch.cpp
@@ -581,6 +581,11 @@ int LinearScan::BuildNode(GenTree* tree)
         case GT_ASYNC_CONTINUATION:
             srcCount = 0;
             assert(dstCount == 1);
+            // We kill the continuation arg here to communicate to the
+            // selection phase that the argument is no longer busy. This is a
+            // hack to make sure we do not overwrite the continuation between
+            // the call and this node.
+            addRefsForPhysRegMask(RBM_ASYNC_CONTINUATION_RET, currentLoc, RefTypeKill, true);
             BuildDef(tree, RBM_ASYNC_CONTINUATION_RET);
             break;
 
diff --git a/src/coreclr/jit/morph.cpp b/src/coreclr/jit/morph.cpp
index 1c4c78b3f8bf62..9a18213f91243c 100644
--- a/src/coreclr/jit/morph.cpp
+++ b/src/coreclr/jit/morph.cpp
@@ -193,6 +193,7 @@ GenTree* Compiler::fgMorphIntoHelperCall(GenTree* tree, int helper, bool morphAr
     call->gtRetClsHnd     = nullptr;
     call->gtCallMoreFlags = GTF_CALL_M_EMPTY;
     call->gtControlExpr   = nullptr;
+    call->gtIsAsyncCall = false;
     call->ClearInlineInfo();
 #ifdef UNIX_X86_ABI
     call->gtFlags |= GTF_CALL_POP_ARGS;
diff --git a/src/coreclr/jit/namedintrinsiclist.h b/src/coreclr/jit/namedintrinsiclist.h
index cd91d7413e09e6..80b5f744efd2fc 100644
--- a/src/coreclr/jit/namedintrinsiclist.h
+++ b/src/coreclr/jit/namedintrinsiclist.h
@@ -108,6 +108,10 @@ enum NamedIntrinsic : unsigned short
     NI_System_Runtime_CompilerServices_RuntimeHelpers_CreateSpan,
     NI_System_Runtime_CompilerServices_RuntimeHelpers_InitializeArray,
     NI_System_Runtime_CompilerServices_RuntimeHelpers_IsKnownConstant,
+    NI_System_Runtime_CompilerServices_RuntimeHelpers_AwaitAwaiterFromRuntimeAsync,
+    NI_System_Runtime_CompilerServices_RuntimeHelpers_UnsafeAwaitAwaiterFromRuntimeAsync,
+    NI_System_Runtime_CompilerServices_RuntimeHelpers_SuspendAsync2,
+    NI_System_Runtime_CompilerServices_RuntimeHelpers_get_RuntimeAsyncViaJitGeneratedStateMachines,
 
     NI_System_Runtime_InteropService_MemoryMarshal_GetArrayDataReference,
 
diff --git a/src/coreclr/jit/valuenum.cpp b/src/coreclr/jit/valuenum.cpp
index cfe3793c895adf..992bdeb59c5f0d 100644
--- a/src/coreclr/jit/valuenum.cpp
+++ b/src/coreclr/jit/valuenum.cpp
@@ -11634,6 +11634,7 @@ void Compiler::fgValueNumberTree(GenTree* tree)
                     case GT_SWITCH:
                     case GT_RETURN:
                     case GT_RETFILT:
+                    case GT_RETURN_SUSPEND:
                     case GT_NULLCHECK:
                         if (tree->gtGetOp1() != nullptr)
                         {
diff --git a/src/coreclr/vm/corelib.h b/src/coreclr/vm/corelib.h
index 0af14fc068c474..e0bf62ea9cdb00 100644
--- a/src/coreclr/vm/corelib.h
+++ b/src/coreclr/vm/corelib.h
@@ -685,6 +685,7 @@ DEFINE_METHOD(RUNTIME_HELPERS,      ALLOC_TAILCALL_ARG_BUFFER, AllocTailCallArgB
 DEFINE_METHOD(RUNTIME_HELPERS,      GET_TAILCALL_INFO,      GetTailCallInfo, NoSig)
 DEFINE_METHOD(RUNTIME_HELPERS,      DISPATCH_TAILCALLS,     DispatchTailCalls, NoSig)
 DEFINE_METHOD(RUNTIME_HELPERS,      ALLOC_CONTINUATION,     AllocContinuation, NoSig)
+DEFINE_METHOD(RUNTIME_HELPERS,      FINALIZE_TASK_RETURNING_THUNK, FinalizeTaskReturningThunk, GM_Obj_RetTaskOfT)
 
 DEFINE_METHOD(RUNTIME_HELPERS,      GET_OR_CREATE_RESUMPTION_DELEGATE, GetOrCreateResumptionDelegate, NoSig)
 DEFINE_METHOD(RUNTIME_HELPERS,      UNSAFE_AWAIT_AWAITER_FROM_RUNTIME_ASYNC_1, UnsafeAwaitAwaiterFromRuntimeAsync, GM_T_RetVoid)
diff --git a/src/coreclr/vm/metasig.h b/src/coreclr/vm/metasig.h
index ee0a74b54d2374..15d27e8651390f 100644
--- a/src/coreclr/vm/metasig.h
+++ b/src/coreclr/vm/metasig.h
@@ -625,6 +625,7 @@ DEFINE_METASIG(SM(Str_RetPtrByte, s, P(b)))
 DEFINE_METASIG(SM(PtrByte_RetVoid, P(b), v))
 
 DEFINE_METASIG(GM(T_RetVoid, IMAGE_CEE_CS_CALLCONV_DEFAULT, 1, M(0), v))
+DEFINE_METASIG_T(GM(Obj_RetTaskOfT, IMAGE_CEE_CS_CALLCONV_DEFAULT, 1, j, GI(C(TASK_1), 1, M(0))))
 
 // Undefine macros in case we include the file again in the compilation unit
 
diff --git a/src/coreclr/vm/methodtablebuilder.cpp b/src/coreclr/vm/methodtablebuilder.cpp
index db8788bdcc6ae4..da588ef3bde293 100644
--- a/src/coreclr/vm/methodtablebuilder.cpp
+++ b/src/coreclr/vm/methodtablebuilder.cpp
@@ -2796,6 +2796,7 @@ AsyncTaskMethod ClassifyAsyncMethodCore(SigPointer sig, Module* pModule, PCCOR_S
             // This must have been the last CMOD before the element type, and the element type MUST be one which can be expressed structurally as a generic parameter
             switch (elemType)
             {
+                case ELEMENT_TYPE_VOID:
                 case ELEMENT_TYPE_CLASS:
                 case ELEMENT_TYPE_VALUETYPE:
                 case ELEMENT_TYPE_STRING:
diff --git a/src/coreclr/vm/prestub.cpp b/src/coreclr/vm/prestub.cpp
index d8c8dfa37e799b..3ef95eca9c61d7 100644
--- a/src/coreclr/vm/prestub.cpp
+++ b/src/coreclr/vm/prestub.cpp
@@ -1708,7 +1708,11 @@ void MethodDesc::EmitJitStateMachineBasedRuntimeAsyncThunk(MethodDesc* pAsyncOth
     pCode->EmitRET();
 
     pCode->EmitLabel(pSuspendedLabel);
-    pCode->EmitLDNULL();
+
+    MethodDesc* md = CoreLibBinder::GetMethod(METHOD__RUNTIME_HELPERS__FINALIZE_TASK_RETURNING_THUNK);
+    md = FindOrCreateAssociatedMethodDesc(md, md->GetMethodTable(), FALSE, Instantiation(&thLogicalRetType, 1), FALSE);
+    pCode->EmitLDLOC(boxedContinuationLcl);
+    pCode->EmitCALL(pCode->GetToken(md), 1, 1);
     pCode->EmitRET();
 }
 
diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.cs
index 3bb06376095e03..62d08f76054e30 100644
--- a/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.cs
+++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.cs
@@ -119,46 +119,88 @@ internal static bool IsPrimitiveType(this CorElementType et)
 #pragma warning restore IDE0060
 
 #if !NATIVEAOT
+        [Intrinsic]
         public static void AwaitAwaiterFromRuntimeAsync<TAwaiter>(TAwaiter awaiter) where TAwaiter : INotifyCompletion
         {
-            StackCrawlMark stackMark = StackCrawlMark.LookForMe;
-
-            // Create resumption delegate, wrapping task, and create tasklets to represent each stack frame on the stack.
-            // RuntimeTaskSuspender.GetOrCreateResumptionDelegate() works like a POSIX fork call in that calls to it will return a
-            // delegate if they are the initial call to GetOrCreateResumptionDelegate, but once the thread is resumed,
-            // it will resume with a return value of null.
-            Action? resumption = RuntimeHelpers.GetOrCreateResumptionDelegate(ref stackMark);
-            if (resumption != null)
+            if (RuntimeAsyncViaJitGeneratedStateMachines)
             {
-                // If we reach here, the only way that we actually run follow on code is for the continuation to actually run,
-                // and return from GetOrCreateResumptionDelegate with a null return value.
-                ref AsyncDataFrame asyncFrame = ref GetCurrentAsyncDataFrame();
-                RuntimeAsyncMaintainedData maintainedData = asyncFrame._maintainedData!;
-                maintainedData._awaiter = awaiter;
-                // This function must be called from the same function that has the stackmark in it.
-                unsafe { UnwindToFunctionWithAsyncFrame(maintainedData._nextTasklet, maintainedData._suspendActive); }
+                ref RuntimeAsyncAwaitState state = ref t_runtimeAsyncAwaitState;
+                object? sentinelContinuation = state.SentinelContinuation;
+                if (sentinelContinuation == null)
+                    state.SentinelContinuation = sentinelContinuation = default(Continuation);
+
+                state.Notifier = awaiter;
+                SuspendAsync2(sentinelContinuation);
+                return;
+            }
+            else
+            {
+                StackCrawlMark stackMark = StackCrawlMark.LookForMe;
+
+                // Create resumption delegate, wrapping task, and create tasklets to represent each stack frame on the stack.
+                // RuntimeTaskSuspender.GetOrCreateResumptionDelegate() works like a POSIX fork call in that calls to it will return a
+                // delegate if they are the initial call to GetOrCreateResumptionDelegate, but once the thread is resumed,
+                // it will resume with a return value of null.
+                Action? resumption = RuntimeHelpers.GetOrCreateResumptionDelegate(ref stackMark);
+                if (resumption != null)
+                {
+                    // If we reach here, the only way that we actually run follow on code is for the continuation to actually run,
+                    // and return from GetOrCreateResumptionDelegate with a null return value.
+                    ref AsyncDataFrame asyncFrame = ref GetCurrentAsyncDataFrame();
+                    RuntimeAsyncMaintainedData maintainedData = asyncFrame._maintainedData!;
+                    maintainedData._awaiter = awaiter;
+                    // This function must be called from the same function that has the stackmark in it.
+                    unsafe { UnwindToFunctionWithAsyncFrame(maintainedData._nextTasklet, maintainedData._suspendActive); }
+                }
             }
         }
 
+        // Marked intrinsic since for JIT state machines this needs to be
+        // recognizes as an async2 call.
+        [Intrinsic]
         public static void UnsafeAwaitAwaiterFromRuntimeAsync<TAwaiter>(TAwaiter awaiter) where TAwaiter : ICriticalNotifyCompletion
         {
-            StackCrawlMark stackMark = StackCrawlMark.LookForMe;
-
-            // Create resumption delegate, wrapping task, and create tasklets to represent each stack frame on the stack.
-            // RuntimeTaskSuspender.GetOrCreateResumptionDelegate() works like a POSIX fork call in that calls to it will return a
-            // delegate if they are the initial call to GetOrCreateResumptionDelegate, but once the thread is resumed,
-            // it will resume with a return value of null.
-            Action? resumption = RuntimeHelpers.GetOrCreateResumptionDelegate(ref stackMark);
-            if (resumption != null)
+            if (RuntimeAsyncViaJitGeneratedStateMachines)
             {
-                // If we reach here, the only way that we actually run follow on code is for the continuation to actually run,
-                // and return from GetOrCreateResumptionDelegate with a null return value.
-                ref AsyncDataFrame asyncFrame = ref GetCurrentAsyncDataFrame();
-                RuntimeAsyncMaintainedData maintainedData = asyncFrame._maintainedData!;
-                maintainedData._awaiter = awaiter;
-                // This function must be called from the same function that has the stackmark in it.
-                unsafe { UnwindToFunctionWithAsyncFrame(maintainedData._nextTasklet, maintainedData._suspendActive); }
+                ref RuntimeAsyncAwaitState state = ref t_runtimeAsyncAwaitState;
+                object? sentinelContinuation = state.SentinelContinuation;
+                if (sentinelContinuation == null)
+                    state.SentinelContinuation = sentinelContinuation = default(Continuation);
+
+                state.Notifier = awaiter;
+                SuspendAsync2(sentinelContinuation);
+                return;
             }
+            else
+            {
+                StackCrawlMark stackMark = StackCrawlMark.LookForMe;
+
+                // Create resumption delegate, wrapping task, and create tasklets to represent each stack frame on the stack.
+                // RuntimeTaskSuspender.GetOrCreateResumptionDelegate() works like a POSIX fork call in that calls to it will return a
+                // delegate if they are the initial call to GetOrCreateResumptionDelegate, but once the thread is resumed,
+                // it will resume with a return value of null.
+                Action? resumption = RuntimeHelpers.GetOrCreateResumptionDelegate(ref stackMark);
+                if (resumption != null)
+                {
+                    // If we reach here, the only way that we actually run follow on code is for the continuation to actually run,
+                    // and return from GetOrCreateResumptionDelegate with a null return value.
+                    ref AsyncDataFrame asyncFrame = ref GetCurrentAsyncDataFrame();
+                    RuntimeAsyncMaintainedData maintainedData = asyncFrame._maintainedData!;
+                    maintainedData._awaiter = awaiter;
+                    // This function must be called from the same function that has the stackmark in it.
+                    unsafe { UnwindToFunctionWithAsyncFrame(maintainedData._nextTasklet, maintainedData._suspendActive); }
+                }
+            }
+        }
+
+        // Could probably be a static readonly bool computing the environment
+        // variable, but recognizing it as an intrinsic avoids having to deal
+        // with having a static constructor that could interfere with perf
+        // measurements.
+        private static bool RuntimeAsyncViaJitGeneratedStateMachines
+        {
+            [Intrinsic]
+            get => false;
         }
 
         [ThreadStatic]
@@ -197,7 +239,7 @@ internal abstract unsafe class RuntimeAsyncMaintainedData
             public SynchronizationContext? _syncCtx;
 
 
-            public Tasklet *_nextTasklet;
+            public Tasklet* _nextTasklet;
             public Tasklet* _oldTaskletNext;
 
             public RuntimeAsyncReturnValue _retValue;
@@ -261,10 +303,10 @@ public unsafe void ResumptionFunc()
                                 // This won't stack overflow unless MaxStackNeeded is actually too high, as the extra allocation is controlled by collectiveStackAllocsPerformed
                                 // TODO This is doing terrible things with the ABI, so we may need to be more careful here
                                 int stackToAlloc = maxStackNeeded - collectiveStackAllocsPerformed;
-                                byte *pStackAlloc = stackalloc byte[stackToAlloc];
+                                byte* pStackAlloc = stackalloc byte[stackToAlloc];
                                 collectiveStackAllocsPerformed += stackToAlloc;
 #pragma warning restore CA2014
-// The optimizer does nothing with variable sized StackAlloc KeepStackAllocAlive(pStackAlloc);
+                                // The optimizer does nothing with variable sized StackAlloc KeepStackAllocAlive(pStackAlloc);
                             }
 
                             try
@@ -473,13 +515,13 @@ private static unsafe ref AsyncDataFrame GetCurrentAsyncDataFrame()
         // 3. Return values are to be returned by reference in all cases where the return value is not a simple object return or return of a simple value in the return value register (this makes the resumption function reasonable to write. Notably, floating point, and ref return will be returned by reference as well as generalized struct return, and return which would normally involve multiple return value registers)
         // 4. There are to be no refs to the outermost caller function exceptn for the valuetype return address (methods which begin on an instance valuetype will have the thunk box the valuetype and the runtime async method on the boxed instance)
         [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "RuntimeSuspension_CaptureTasklets")]
-        private static unsafe partial Tasklet *CaptureCurrentStackIntoTasklets(StackCrawlMarkHandle stackMarkTop, ref byte returnValueHandle, [MarshalAs(UnmanagedType.U1)] bool useReturnValueHandle, void* taskAsyncData, out Tasklet* lastTasklet, out int framesCaptured);
+        private static unsafe partial Tasklet* CaptureCurrentStackIntoTasklets(StackCrawlMarkHandle stackMarkTop, ref byte returnValueHandle, [MarshalAs(UnmanagedType.U1)] bool useReturnValueHandle, void* taskAsyncData, out Tasklet* lastTasklet, out int framesCaptured);
 
         [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "RuntimeSuspension_DeleteTasklet")]
-        private static unsafe partial void DeleteTasklet(Tasklet *tasklet);
+        private static unsafe partial void DeleteTasklet(Tasklet* tasklet);
 
         [MethodImpl(MethodImplOptions.InternalCall)]
-        internal static extern unsafe void UnwindToFunctionWithAsyncFrame(Tasklet *topTasklet, nint framesToUnwind);
+        internal static extern unsafe void UnwindToFunctionWithAsyncFrame(Tasklet* topTasklet, nint framesToUnwind);
 
         [System.Security.DynamicSecurityMethod] // Methods containing StackCrawlMark local var has to be marked DynamicSecurityMethod
         private static unsafe Action? GetOrCreateResumptionDelegate(ref StackCrawlMark stackMark)

From 2fd80295891ccecd57e23966d325d59ff467811c Mon Sep 17 00:00:00 2001
From: David Wrighton <davidwr@microsoft.com>
Date: Tue, 24 Oct 2023 13:21:05 -0700
Subject: [PATCH 043/203] Fixes to make it all build and run properly

- Jakob's transform only works on optimized code right now, so force that for all the tests (they are benchmarks anyways, so... faster is always better)
- Check for null
- Fix missing check for state machine compilation in lsra
- Change tests to be part of the pri0 build, so that the test build script builds them
---
 src/coreclr/jit/compiler.cpp                             | 2 ++
 src/coreclr/jit/compiler.h                               | 2 +-
 src/coreclr/jit/lsrabuild.cpp                            | 2 +-
 .../System/Runtime/CompilerServices/RuntimeHelpers.cs    | 8 ++++++++
 .../Loader/async/async2fibonacci-with-yields.csproj      | 2 +-
 .../Loader/async/async2fibonacci-without-yields.csproj   | 2 +-
 src/tests/Loader/async/asynctests.csproj                 | 9 +++++++++
 .../async/runtimetask-asyncfibonacci-with-yields.ilproj  | 1 -
 .../runtimetask-asyncfibonacci-without-yields.ilproj     | 1 -
 src/tests/Loader/async/syncfibonacci-with-yields.csproj  | 2 +-
 .../Loader/async/syncfibonacci-without-yields.csproj     | 2 +-
 .../async/taskbased-asyncfibonacci-with-yields.csproj    | 2 +-
 .../async/taskbased-asyncfibonacci-without-yields.csproj | 2 +-
 .../valuetaskbased-asyncfibonacci-with-yields.csproj     | 2 +-
 .../valuetaskbased-asyncfibonacci-without-yields.csproj  | 2 +-
 15 files changed, 29 insertions(+), 12 deletions(-)
 create mode 100644 src/tests/Loader/async/asynctests.csproj

diff --git a/src/coreclr/jit/compiler.cpp b/src/coreclr/jit/compiler.cpp
index 67466abfe6b037..eef0f686797982 100644
--- a/src/coreclr/jit/compiler.cpp
+++ b/src/coreclr/jit/compiler.cpp
@@ -1984,6 +1984,8 @@ void Compiler::compInit(ArenaAllocator*       pAlloc,
     compUsesThrowHelper = false;
 
     m_preferredInitCctor = CORINFO_HELP_UNDEF;
+
+    m_pLinearScan = nullptr;
 }
 
 /*****************************************************************************
diff --git a/src/coreclr/jit/compiler.h b/src/coreclr/jit/compiler.h
index fb6d2fe11f411d..da002eb9006a7b 100644
--- a/src/coreclr/jit/compiler.h
+++ b/src/coreclr/jit/compiler.h
@@ -4930,7 +4930,7 @@ class Compiler
 
     bool backendRequiresLocalVarLifetimes()
     {
-        return !opts.MinOpts() || m_pLinearScan->willEnregisterLocalVars();
+        return !opts.MinOpts() || (m_pLinearScan != NULL && m_pLinearScan->willEnregisterLocalVars());
     }
 
     void fgLocalVarLiveness();
diff --git a/src/coreclr/jit/lsrabuild.cpp b/src/coreclr/jit/lsrabuild.cpp
index bdba9cef9fc72d..fee2b0ed4f4a64 100644
--- a/src/coreclr/jit/lsrabuild.cpp
+++ b/src/coreclr/jit/lsrabuild.cpp
@@ -1231,7 +1231,7 @@ bool LinearScan::buildKillPositionsForNode(GenTree* tree, LsraLocation currentLo
         insertedKills = true;
     }
 
-    if (tree->IsCall() && tree->AsCall()->IsAsync2())
+    if (tree->IsCall() && tree->AsCall()->IsAsync2() && compiler->compIsAsync2StateMachine())
     {
         // Async2 calls return an async continuation argument in a separate
         // register. Since we do not have a flexible representation for
diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.cs
index 62d08f76054e30..cddd07e4ef4bfb 100644
--- a/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.cs
+++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.cs
@@ -6,6 +6,12 @@
 using System.Threading;
 using System.Threading.Tasks;
 
+namespace System.Runtime
+{
+    internal sealed class BypassReadyToRunAttribute : Attribute {}
+}
+
+
 namespace System.Runtime.CompilerServices
 {
     public static partial class RuntimeHelpers
@@ -120,6 +126,7 @@ internal static bool IsPrimitiveType(this CorElementType et)
 
 #if !NATIVEAOT
         [Intrinsic]
+        [BypassReadyToRun]
         public static void AwaitAwaiterFromRuntimeAsync<TAwaiter>(TAwaiter awaiter) where TAwaiter : INotifyCompletion
         {
             if (RuntimeAsyncViaJitGeneratedStateMachines)
@@ -158,6 +165,7 @@ public static void AwaitAwaiterFromRuntimeAsync<TAwaiter>(TAwaiter awaiter) wher
         // Marked intrinsic since for JIT state machines this needs to be
         // recognizes as an async2 call.
         [Intrinsic]
+        [BypassReadyToRun]
         public static void UnsafeAwaitAwaiterFromRuntimeAsync<TAwaiter>(TAwaiter awaiter) where TAwaiter : ICriticalNotifyCompletion
         {
             if (RuntimeAsyncViaJitGeneratedStateMachines)
diff --git a/src/tests/Loader/async/async2fibonacci-with-yields.csproj b/src/tests/Loader/async/async2fibonacci-with-yields.csproj
index 2b9dfb186b22cd..6780013a730b11 100644
--- a/src/tests/Loader/async/async2fibonacci-with-yields.csproj
+++ b/src/tests/Loader/async/async2fibonacci-with-yields.csproj
@@ -1,7 +1,7 @@
 <Project Sdk="Microsoft.NET.Sdk">
   <PropertyGroup>
-    <CLRTestPriority>1</CLRTestPriority>
     <RequiresProcessIsolation>true</RequiresProcessIsolation>
+    <Optimize>True</Optimize>
   </PropertyGroup>
   <ItemGroup>
     <Compile Include="async2fibonacci-with-yields.cs" />
diff --git a/src/tests/Loader/async/async2fibonacci-without-yields.csproj b/src/tests/Loader/async/async2fibonacci-without-yields.csproj
index 338f2bcb6e97a5..6630b675c0921d 100644
--- a/src/tests/Loader/async/async2fibonacci-without-yields.csproj
+++ b/src/tests/Loader/async/async2fibonacci-without-yields.csproj
@@ -1,7 +1,7 @@
 <Project Sdk="Microsoft.NET.Sdk">
   <PropertyGroup>
-    <CLRTestPriority>1</CLRTestPriority>
     <RequiresProcessIsolation>true</RequiresProcessIsolation>
+    <Optimize>True</Optimize>
   </PropertyGroup>
   <ItemGroup>
     <Compile Include="async2fibonacci-without-yields.cs" />
diff --git a/src/tests/Loader/async/asynctests.csproj b/src/tests/Loader/async/asynctests.csproj
new file mode 100644
index 00000000000000..be189042ee44c3
--- /dev/null
+++ b/src/tests/Loader/async/asynctests.csproj
@@ -0,0 +1,9 @@
+<Project Sdk="Microsoft.NET.Sdk">
+  <ItemGroup>
+    <MergedWrapperProjectReference Include="*/**/*.??proj" />
+    <MergedWrapperProjectReference Include="*.??proj" />
+    <MergedWrapperProjectReference Remove="$(MSBuildProjectName).csproj" />
+  </ItemGroup>
+
+  <Import Project="$(TestSourceDir)MergedTestRunner.targets" />
+</Project>
diff --git a/src/tests/Loader/async/runtimetask-asyncfibonacci-with-yields.ilproj b/src/tests/Loader/async/runtimetask-asyncfibonacci-with-yields.ilproj
index 6e1d7c1bd8dc60..a14484d84b2945 100644
--- a/src/tests/Loader/async/runtimetask-asyncfibonacci-with-yields.ilproj
+++ b/src/tests/Loader/async/runtimetask-asyncfibonacci-with-yields.ilproj
@@ -1,6 +1,5 @@
 <Project Sdk="Microsoft.NET.Sdk.IL">
   <PropertyGroup>
-    <CLRTestPriority>1</CLRTestPriority>
     <RequiresProcessIsolation>true</RequiresProcessIsolation>
   </PropertyGroup>
   <ItemGroup>
diff --git a/src/tests/Loader/async/runtimetask-asyncfibonacci-without-yields.ilproj b/src/tests/Loader/async/runtimetask-asyncfibonacci-without-yields.ilproj
index 7b346fab866782..37984d9ea7a9d4 100644
--- a/src/tests/Loader/async/runtimetask-asyncfibonacci-without-yields.ilproj
+++ b/src/tests/Loader/async/runtimetask-asyncfibonacci-without-yields.ilproj
@@ -1,6 +1,5 @@
 <Project Sdk="Microsoft.NET.Sdk.IL">
   <PropertyGroup>
-    <CLRTestPriority>1</CLRTestPriority>
     <RequiresProcessIsolation>true</RequiresProcessIsolation>
   </PropertyGroup>
   <ItemGroup>
diff --git a/src/tests/Loader/async/syncfibonacci-with-yields.csproj b/src/tests/Loader/async/syncfibonacci-with-yields.csproj
index d0f5bcb4f93392..be97c05717f3b3 100644
--- a/src/tests/Loader/async/syncfibonacci-with-yields.csproj
+++ b/src/tests/Loader/async/syncfibonacci-with-yields.csproj
@@ -1,7 +1,7 @@
 <Project Sdk="Microsoft.NET.Sdk">
   <PropertyGroup>
-    <CLRTestPriority>1</CLRTestPriority>
     <RequiresProcessIsolation>true</RequiresProcessIsolation>
+    <Optimize>True</Optimize>
   </PropertyGroup>
   <ItemGroup>
     <Compile Include="syncfibonacci-with-yields.cs" />
diff --git a/src/tests/Loader/async/syncfibonacci-without-yields.csproj b/src/tests/Loader/async/syncfibonacci-without-yields.csproj
index 9cf8b76da52921..9b3605f2a5e292 100644
--- a/src/tests/Loader/async/syncfibonacci-without-yields.csproj
+++ b/src/tests/Loader/async/syncfibonacci-without-yields.csproj
@@ -1,7 +1,7 @@
 <Project Sdk="Microsoft.NET.Sdk">
   <PropertyGroup>
-    <CLRTestPriority>1</CLRTestPriority>
     <RequiresProcessIsolation>true</RequiresProcessIsolation>
+    <Optimize>True</Optimize>
   </PropertyGroup>
   <ItemGroup>
     <Compile Include="syncfibonacci-without-yields.cs" />
diff --git a/src/tests/Loader/async/taskbased-asyncfibonacci-with-yields.csproj b/src/tests/Loader/async/taskbased-asyncfibonacci-with-yields.csproj
index c52fc0b7296764..cce296b679dc3d 100644
--- a/src/tests/Loader/async/taskbased-asyncfibonacci-with-yields.csproj
+++ b/src/tests/Loader/async/taskbased-asyncfibonacci-with-yields.csproj
@@ -1,7 +1,7 @@
 <Project Sdk="Microsoft.NET.Sdk">
   <PropertyGroup>
-    <CLRTestPriority>1</CLRTestPriority>
     <RequiresProcessIsolation>true</RequiresProcessIsolation>
+    <Optimize>True</Optimize>
   </PropertyGroup>
   <ItemGroup>
     <Compile Include="taskbased-asyncfibonacci-with-yields.cs" />
diff --git a/src/tests/Loader/async/taskbased-asyncfibonacci-without-yields.csproj b/src/tests/Loader/async/taskbased-asyncfibonacci-without-yields.csproj
index 7be4aaefd9f51d..26a1f092bdceb1 100644
--- a/src/tests/Loader/async/taskbased-asyncfibonacci-without-yields.csproj
+++ b/src/tests/Loader/async/taskbased-asyncfibonacci-without-yields.csproj
@@ -1,7 +1,7 @@
 <Project Sdk="Microsoft.NET.Sdk">
   <PropertyGroup>
-    <CLRTestPriority>1</CLRTestPriority>
     <RequiresProcessIsolation>true</RequiresProcessIsolation>
+    <Optimize>True</Optimize>
   </PropertyGroup>
   <ItemGroup>
     <Compile Include="taskbased-asyncfibonacci-without-yields.cs" />
diff --git a/src/tests/Loader/async/valuetaskbased-asyncfibonacci-with-yields.csproj b/src/tests/Loader/async/valuetaskbased-asyncfibonacci-with-yields.csproj
index 33bb31ea09bd0c..3edcf08d0eb05f 100644
--- a/src/tests/Loader/async/valuetaskbased-asyncfibonacci-with-yields.csproj
+++ b/src/tests/Loader/async/valuetaskbased-asyncfibonacci-with-yields.csproj
@@ -1,7 +1,7 @@
 <Project Sdk="Microsoft.NET.Sdk">
   <PropertyGroup>
-    <CLRTestPriority>1</CLRTestPriority>
     <RequiresProcessIsolation>true</RequiresProcessIsolation>
+    <Optimize>True</Optimize>
   </PropertyGroup>
   <ItemGroup>
     <Compile Include="valuetaskbased-asyncfibonacci-with-yields.cs" />
diff --git a/src/tests/Loader/async/valuetaskbased-asyncfibonacci-without-yields.csproj b/src/tests/Loader/async/valuetaskbased-asyncfibonacci-without-yields.csproj
index 043a2cb45456ac..4d381cc234d7c6 100644
--- a/src/tests/Loader/async/valuetaskbased-asyncfibonacci-without-yields.csproj
+++ b/src/tests/Loader/async/valuetaskbased-asyncfibonacci-without-yields.csproj
@@ -1,7 +1,7 @@
 <Project Sdk="Microsoft.NET.Sdk">
   <PropertyGroup>
-    <CLRTestPriority>1</CLRTestPriority>
     <RequiresProcessIsolation>true</RequiresProcessIsolation>
+    <Optimize>True</Optimize>
   </PropertyGroup>
   <ItemGroup>
     <Compile Include="valuetaskbased-asyncfibonacci-without-yields.cs" />

From 51a3a87e787c2516d9bd1b819414886187859150 Mon Sep 17 00:00:00 2001
From: Vladimir Sadov <vsadov@microsoft.com>
Date: Thu, 26 Oct 2023 17:23:42 +0000
Subject: [PATCH 044/203] Merged PR 34776: replaces couple IL tests with
 trivial C# variants, adds a test for object capture, and a fix to make it
 pass

- replaces couple IL tests with trivial C# variants,
- adds a test for object capture,
- and a GC hole fix to make object capture pass
- picks roslyn support for async2 void
- adds a test for async2 void
- adds a test for async2 object
---
 buildroslynnugets.cmd                         |   4 +-
 eng/Versions.props                            |   6 +-
 src/coreclr/vm/runtimesuspension.cpp          |   2 +-
 src/tests/Loader/async/async2-with-yields.cs  |  52 +++
 ...ields.ilproj => async2-with-yields.csproj} |   2 +-
 .../Loader/async/async2-without-yields.cs     |  43 ++
 ...ds.ilproj => async2-without-yields.csproj} |   2 +-
 src/tests/Loader/async/async2object.cs        |  28 ++
 src/tests/Loader/async/async2object.csproj    |   8 +
 .../Loader/async/async2objectsCaptured.cs     |  44 ++
 .../Loader/async/async2objectsCaptured.csproj |   8 +
 src/tests/Loader/async/async2void.cs          |  33 ++
 src/tests/Loader/async/async2void.csproj      |   8 +
 .../runtimetask-asyncfibonacci-with-yields.il | 435 ------------------
 ...ntimetask-asyncfibonacci-without-yields.il | 377 ---------------
 15 files changed, 232 insertions(+), 820 deletions(-)
 create mode 100644 src/tests/Loader/async/async2-with-yields.cs
 rename src/tests/Loader/async/{runtimetask-asyncfibonacci-with-yields.ilproj => async2-with-yields.csproj} (72%)
 create mode 100644 src/tests/Loader/async/async2-without-yields.cs
 rename src/tests/Loader/async/{runtimetask-asyncfibonacci-without-yields.ilproj => async2-without-yields.csproj} (71%)
 create mode 100644 src/tests/Loader/async/async2object.cs
 create mode 100644 src/tests/Loader/async/async2object.csproj
 create mode 100644 src/tests/Loader/async/async2objectsCaptured.cs
 create mode 100644 src/tests/Loader/async/async2objectsCaptured.csproj
 create mode 100644 src/tests/Loader/async/async2void.cs
 create mode 100644 src/tests/Loader/async/async2void.csproj
 delete mode 100644 src/tests/Loader/async/runtimetask-asyncfibonacci-with-yields.il
 delete mode 100644 src/tests/Loader/async/runtimetask-asyncfibonacci-without-yields.il

diff --git a/buildroslynnugets.cmd b/buildroslynnugets.cmd
index 14b5c47699b78d..c03c1914cf4678 100644
--- a/buildroslynnugets.cmd
+++ b/buildroslynnugets.cmd
@@ -1,14 +1,14 @@
 setlocal ENABLEEXTENSIONS
 pushd %~dp0
 
-set ASYNC_SUFFIX=async-2
+set ASYNC_SUFFIX=async-3
 
 cd ..
 pushd dotnet-roslyn
 
 git fetch AzDo dev/vsadov/a2
 rem when updating this, make sure to update the ASYNC_SUFFIX above and the versions.props file
-git checkout 1c35e4f42732cfd58bb2a56bbbd85d8200497fa2
+git checkout 0d06d1f93092dc3112fcefdf8957e11ed3ea4e31
 
 call restore.cmd
 call build.cmd -c release
diff --git a/eng/Versions.props b/eng/Versions.props
index 6e7df86ae6af93..4d47b03429f47e 100644
--- a/eng/Versions.props
+++ b/eng/Versions.props
@@ -39,9 +39,9 @@
       Any tools that contribute to the design-time experience should use the MicrosoftCodeAnalysisVersion_LatestVS property above to ensure
       they do not break the local dev experience.
     -->
-    <MicrosoftCodeAnalysisCSharpVersion>4.9.0-async-2</MicrosoftCodeAnalysisCSharpVersion>
-    <MicrosoftCodeAnalysisVersion>4.9.0-async-2</MicrosoftCodeAnalysisVersion>
-    <MicrosoftNetCompilersToolsetVersion>4.9.0-async-2</MicrosoftNetCompilersToolsetVersion>
+    <MicrosoftCodeAnalysisCSharpVersion>4.9.0-async-3</MicrosoftCodeAnalysisCSharpVersion>
+    <MicrosoftCodeAnalysisVersion>4.9.0-async-3</MicrosoftCodeAnalysisVersion>
+    <MicrosoftNetCompilersToolsetVersion>4.9.0-async-3</MicrosoftNetCompilersToolsetVersion>
   </PropertyGroup>
   <!--
     For source generator support we need to target multiple versions of Roslyn in order to be able to run on older versions of Roslyn.
diff --git a/src/coreclr/vm/runtimesuspension.cpp b/src/coreclr/vm/runtimesuspension.cpp
index 4492ac7a343207..577ee0b04f12df 100644
--- a/src/coreclr/vm/runtimesuspension.cpp
+++ b/src/coreclr/vm/runtimesuspension.cpp
@@ -626,7 +626,7 @@ void IterateTaskletsForGC(promote_func* pCallback, ScanContext* sc)
             pCallback((PTR_PTR_Object)(pLogicalRSP + pStackDataInfo->ObjectRefOffsets[iRef]), sc, 0);
         }
         
-        for (iRef = 0; iRef < pStackDataInfo->cObjectRefs; iRef++)
+        for (iRef = 0; iRef < pStackDataInfo->cByRefs; iRef++)
         {
             int32_t offset = pStackDataInfo->ByRefOffsets[iRef];
             uint32_t flags = GC_CALL_INTERIOR;
diff --git a/src/tests/Loader/async/async2-with-yields.cs b/src/tests/Loader/async/async2-with-yields.cs
new file mode 100644
index 00000000000000..acaaf5ce987be6
--- /dev/null
+++ b/src/tests/Loader/async/async2-with-yields.cs
@@ -0,0 +1,52 @@
+// 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 System.Threading.Tasks;
+using System.Diagnostics;
+using Xunit;
+
+public class Async2FibonacceWithYields
+{
+    internal static async2 int B(int n)
+    {
+        int num = 1;
+        await Task.Yield();
+
+        num *= 10;
+        await Task.Yield();
+
+        num *= 10;
+        await Task.Yield();
+
+        return num;
+    }
+
+    internal static async2 int A(int n)
+    {
+        int num = n;
+        for (int num2 = 0; num2 < n; num2++)
+        {
+            num = await B(num);
+        }
+
+        return num;
+    }
+
+    private static async Task<int> AsyncEntry()
+    {
+        int result = 0;
+        for (int i = 0; i < 10; i++)
+        {
+            result = await A(100);
+        }
+
+        return result;
+    }
+
+    [Fact]
+    public static int Test()
+    {
+        return AsyncEntry().Result;
+    }
+}
diff --git a/src/tests/Loader/async/runtimetask-asyncfibonacci-with-yields.ilproj b/src/tests/Loader/async/async2-with-yields.csproj
similarity index 72%
rename from src/tests/Loader/async/runtimetask-asyncfibonacci-with-yields.ilproj
rename to src/tests/Loader/async/async2-with-yields.csproj
index a14484d84b2945..afefd86ece8373 100644
--- a/src/tests/Loader/async/runtimetask-asyncfibonacci-with-yields.ilproj
+++ b/src/tests/Loader/async/async2-with-yields.csproj
@@ -3,6 +3,6 @@
     <RequiresProcessIsolation>true</RequiresProcessIsolation>
   </PropertyGroup>
   <ItemGroup>
-    <Compile Include="runtimetask-asyncfibonacci-with-yields.il" />
+    <Compile Include="async2-with-yields.cs" />
   </ItemGroup>
 </Project>
diff --git a/src/tests/Loader/async/async2-without-yields.cs b/src/tests/Loader/async/async2-without-yields.cs
new file mode 100644
index 00000000000000..ac3f0fc3f731e5
--- /dev/null
+++ b/src/tests/Loader/async/async2-without-yields.cs
@@ -0,0 +1,43 @@
+// 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 System.Threading.Tasks;
+using System.Diagnostics;
+using Xunit;
+
+public class Async2FibonacceWithoutYields
+{
+    internal static async2 int B(int n)
+    {
+        return 100;
+    }
+
+    internal static async2 int A(int n)
+    {
+        int num = n;
+        for (int num2 = 0; num2 < n; num2++)
+        {
+            num = await B(num);
+        }
+
+        return num;
+    }
+
+    private static async Task<int> AsyncEntry()
+    {
+        int result = 0;
+        for (int i = 0; i < 10; i++)
+        {
+            result = await A(100);
+        }
+
+        return result;
+    }
+
+    [Fact]
+    public static int Test()
+    {
+        return AsyncEntry().Result;
+    }
+}
diff --git a/src/tests/Loader/async/runtimetask-asyncfibonacci-without-yields.ilproj b/src/tests/Loader/async/async2-without-yields.csproj
similarity index 71%
rename from src/tests/Loader/async/runtimetask-asyncfibonacci-without-yields.ilproj
rename to src/tests/Loader/async/async2-without-yields.csproj
index 37984d9ea7a9d4..1e584bac999039 100644
--- a/src/tests/Loader/async/runtimetask-asyncfibonacci-without-yields.ilproj
+++ b/src/tests/Loader/async/async2-without-yields.csproj
@@ -3,6 +3,6 @@
     <RequiresProcessIsolation>true</RequiresProcessIsolation>
   </PropertyGroup>
   <ItemGroup>
-    <Compile Include="runtimetask-asyncfibonacci-without-yields.il" />
+    <Compile Include="async2-without-yields.cs" />
   </ItemGroup>
 </Project>
diff --git a/src/tests/Loader/async/async2object.cs b/src/tests/Loader/async/async2object.cs
new file mode 100644
index 00000000000000..81fc0119c6a775
--- /dev/null
+++ b/src/tests/Loader/async/async2object.cs
@@ -0,0 +1,28 @@
+// 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 System.Runtime.CompilerServices;
+using System.Threading.Tasks;
+using Xunit;
+
+public class Async2Void
+{
+    [Fact]
+    public static int TestEntryPoint()
+    {
+        return (int)AsyncTestEntryPoint(100).Result;
+    }
+
+    private static async Task<object> AsyncTestEntryPoint(int arg)
+    {
+        return await ObjMethod(arg);
+    }
+
+    [MethodImpl(MethodImplOptions.NoInlining)]
+    private static async2 object ObjMethod(int arg)
+    {
+        await Task.Yield();
+        return arg;
+    }
+}
diff --git a/src/tests/Loader/async/async2object.csproj b/src/tests/Loader/async/async2object.csproj
new file mode 100644
index 00000000000000..49568b4a12ac83
--- /dev/null
+++ b/src/tests/Loader/async/async2object.csproj
@@ -0,0 +1,8 @@
+<Project Sdk="Microsoft.NET.Sdk.IL">
+  <PropertyGroup>
+    <RequiresProcessIsolation>true</RequiresProcessIsolation>
+  </PropertyGroup>
+  <ItemGroup>
+    <Compile Include="async2object.cs" />
+  </ItemGroup>
+</Project>
diff --git a/src/tests/Loader/async/async2objectsCaptured.cs b/src/tests/Loader/async/async2objectsCaptured.cs
new file mode 100644
index 00000000000000..f2a6a795fcbc98
--- /dev/null
+++ b/src/tests/Loader/async/async2objectsCaptured.cs
@@ -0,0 +1,44 @@
+// 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 System.Threading.Tasks;
+using System.Diagnostics;
+using Xunit;
+
+public class Async2ObjectsWithYields
+{
+    internal static async2 int A(object n)
+    {
+        // use string equality so that JIT would not think of hoisting "(int)n"
+        // also to produce some amout of garbage
+        if (n.ToString() != 0.ToString())
+        {
+            return await A((int)n - 1) + (int)n;
+        }
+
+        await Task.Yield();
+        return 0;
+    }
+
+    private static async Task<int> AsyncEntry()
+    {
+        object result = 0;
+        for (int i = 0; i < 20; i++)
+        {
+            var tsk = A(i);
+            await Task.Yield();
+            GC.Collect();
+            result = await tsk;
+        }
+
+        // the result should be 20 * (20 - 1) => 190
+        return (int)result - 90;
+    }
+
+    [Fact]
+    public static int Test()
+    {
+        return (int)AsyncEntry().Result;
+    }
+}
diff --git a/src/tests/Loader/async/async2objectsCaptured.csproj b/src/tests/Loader/async/async2objectsCaptured.csproj
new file mode 100644
index 00000000000000..16bdb6543d40b1
--- /dev/null
+++ b/src/tests/Loader/async/async2objectsCaptured.csproj
@@ -0,0 +1,8 @@
+<Project Sdk="Microsoft.NET.Sdk.IL">
+  <PropertyGroup>
+    <RequiresProcessIsolation>true</RequiresProcessIsolation>
+  </PropertyGroup>
+  <ItemGroup>
+    <Compile Include="async2objectsCaptured.cs" />
+  </ItemGroup>
+</Project>
diff --git a/src/tests/Loader/async/async2void.cs b/src/tests/Loader/async/async2void.cs
new file mode 100644
index 00000000000000..b645fa6789d2f2
--- /dev/null
+++ b/src/tests/Loader/async/async2void.cs
@@ -0,0 +1,33 @@
+// 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 System.Runtime.CompilerServices;
+using System.Threading.Tasks;
+using Xunit;
+
+public class Async2Void
+{
+    [Fact]
+    public static void TestEntryPoint()
+    {
+        int[] arr = new int[1];
+        AsyncTestEntryPoint(arr, 0).Wait();
+        Assert.Equal(199_990_000, arr[0]);
+    }
+
+    private static async Task AsyncTestEntryPoint(int[] arr, int index)
+    {
+        await HoistedByref(arr, index);
+    }
+
+    [MethodImpl(MethodImplOptions.NoInlining)]
+    private static async2 void HoistedByref(int[] arr, int index)
+    {
+        for (int i = 0; i < 20000; i++)
+        {
+            arr[index] += i;
+            await Task.Yield();
+        }
+    }
+}
diff --git a/src/tests/Loader/async/async2void.csproj b/src/tests/Loader/async/async2void.csproj
new file mode 100644
index 00000000000000..78d25e41af6595
--- /dev/null
+++ b/src/tests/Loader/async/async2void.csproj
@@ -0,0 +1,8 @@
+<Project Sdk="Microsoft.NET.Sdk.IL">
+  <PropertyGroup>
+    <RequiresProcessIsolation>true</RequiresProcessIsolation>
+  </PropertyGroup>
+  <ItemGroup>
+    <Compile Include="async2void.cs" />
+  </ItemGroup>
+</Project>
diff --git a/src/tests/Loader/async/runtimetask-asyncfibonacci-with-yields.il b/src/tests/Loader/async/runtimetask-asyncfibonacci-with-yields.il
deleted file mode 100644
index 97e602741eb53a..00000000000000
--- a/src/tests/Loader/async/runtimetask-asyncfibonacci-with-yields.il
+++ /dev/null
@@ -1,435 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-
-
-//  .NET IL Disassembler.  Version 9.0.0-dev
-
-
-
-// Metadata version: v4.0.30319
-.assembly extern System.Runtime
-{
-  .publickeytoken = (B0 3F 5F 7F 11 D5 0A 3A )                         // .?_....:
-  .ver 9:0:0:0
-}
-.assembly extern System.Console
-{
-  .publickeytoken = (B0 3F 5F 7F 11 D5 0A 3A )                         // .?_....:
-  .ver 9:0:0:0
-}
-.assembly 'runtimetask-asyncfibonacci-with-yields'
-{
-  .custom instance void [System.Runtime]System.Runtime.CompilerServices.CompilationRelaxationsAttribute::.ctor(int32) = ( 01 00 08 00 00 00 00 00 ) 
-  .custom instance void [System.Runtime]System.Runtime.CompilerServices.RuntimeCompatibilityAttribute::.ctor() = ( 01 00 01 00 54 02 16 57 72 61 70 4E 6F 6E 45 78   // ....T..WrapNonEx
-                                                                                                                   63 65 70 74 69 6F 6E 54 68 72 6F 77 73 01 )       // ceptionThrows.
-
-  // --- The following custom attribute is added automatically, do not uncomment -------
-  //  .custom instance void [System.Runtime]System.Diagnostics.DebuggableAttribute::.ctor(valuetype [System.Runtime]System.Diagnostics.DebuggableAttribute/DebuggingModes) = ( 01 00 07 01 00 00 00 00 ) 
-
-  .hash algorithm 0x00008004
-  .ver 0:0:0:0
-}
-.module 'runtimetask-asyncfibonacci-with-yields.dll'
-// MVID: {ca42840a-c801-47a9-8471-ea986ae2c60d}
-.custom instance void [System.Runtime]System.Runtime.CompilerServices.RefSafetyRulesAttribute::.ctor(int32) = ( 01 00 0B 00 00 00 00 00 ) 
-.imagebase 0x00400000
-.file alignment 0x00000200
-.stackreserve 0x00100000
-.subsystem 0x0003       // WINDOWS_CUI
-.corflags 0x00000001    //  ILONLY
-// Image base: 0x000001E00BDE0000
-
-
-// =============== CLASS MEMBERS DECLARATION ===================
-
-.class private auto ansi beforefieldinit Program
-       extends [System.Runtime]System.Object
-{
-  .class auto ansi sealed nested private beforefieldinit '<<Main>$>d__0'
-         extends [System.Runtime]System.Object
-         implements [System.Runtime]System.Runtime.CompilerServices.IAsyncStateMachine
-  {
-    .custom instance void [System.Runtime]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) 
-    .field public int32 '<>1__state'
-    .field public valuetype [System.Runtime]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<int32> '<>t__builder'
-    .field public string[] args
-    .field private int32 '<i>5__1'
-    .field private class [System.Runtime]System.Diagnostics.Stopwatch '<sw>5__2'
-    .field private uint32 '<result>5__3'
-    .field private uint32 '<>s__4'
-    .field private valuetype [System.Runtime]System.Runtime.CompilerServices.TaskAwaiter`1<uint32> '<>u__1'
-    .method public hidebysig specialname rtspecialname 
-            instance void  .ctor() cil managed
-    {
-      // Code size       8 (0x8)
-      .maxstack  8
-      IL_0000:  ldarg.0
-      IL_0001:  call       instance void [System.Runtime]System.Object::.ctor()
-      IL_0006:  nop
-      IL_0007:  ret
-    } // end of method '<<Main>$>d__0'::.ctor
-
-    .method private hidebysig newslot virtual final 
-            instance void  MoveNext() cil managed
-    {
-      .override [System.Runtime]System.Runtime.CompilerServices.IAsyncStateMachine::MoveNext
-      // Code size       334 (0x14e)
-      .maxstack  3
-      .locals init (int32 V_0,
-               int32 V_1,
-               valuetype [System.Runtime]System.Runtime.CompilerServices.TaskAwaiter`1<uint32> V_2,
-               class Program/'<<Main>$>d__0' V_3,
-               valuetype [System.Runtime]System.Runtime.CompilerServices.DefaultInterpolatedStringHandler V_4,
-               int32 V_5,
-               bool V_6,
-               class [System.Runtime]System.Exception V_7)
-      IL_0000:  ldarg.0
-      IL_0001:  ldfld      int32 Program/'<<Main>$>d__0'::'<>1__state'
-      IL_0006:  stloc.0
-      .try
-      {
-        IL_0007:  ldloc.0
-        IL_0008:  brfalse.s  IL_000c
-
-        IL_000a:  br.s       IL_000e
-
-        IL_000c:  br.s       IL_0072
-
-        IL_000e:  ldarg.0
-        IL_000f:  ldc.i4.0
-        IL_0010:  stfld      int32 Program/'<<Main>$>d__0'::'<i>5__1'
-        IL_0015:  br         IL_0106
-
-        IL_001a:  nop
-        IL_001b:  ldarg.0
-        IL_001c:  newobj     instance void [System.Runtime]System.Diagnostics.Stopwatch::.ctor()
-        IL_0021:  stfld      class [System.Runtime]System.Diagnostics.Stopwatch Program/'<<Main>$>d__0'::'<sw>5__2'
-        IL_0026:  ldarg.0
-        IL_0027:  ldfld      class [System.Runtime]System.Diagnostics.Stopwatch Program/'<<Main>$>d__0'::'<sw>5__2'
-        IL_002c:  callvirt   instance void [System.Runtime]System.Diagnostics.Stopwatch::Start()
-        IL_0031:  nop
-        IL_0032:  ldc.i4     0x5f5e100
-        IL_0037:  call       class [System.Runtime]System.Threading.Tasks.Task`1<uint32> Program::'<<Main>$>g__A|0_0'(uint32)
-        IL_003c:  callvirt   instance valuetype [System.Runtime]System.Runtime.CompilerServices.TaskAwaiter`1<!0> class [System.Runtime]System.Threading.Tasks.Task`1<uint32>::GetAwaiter()
-        IL_0041:  stloc.2
-        IL_0042:  ldloca.s   V_2
-        IL_0044:  call       instance bool valuetype [System.Runtime]System.Runtime.CompilerServices.TaskAwaiter`1<uint32>::get_IsCompleted()
-        IL_0049:  brtrue.s   IL_008e
-
-        IL_004b:  ldarg.0
-        IL_004c:  ldc.i4.0
-        IL_004d:  dup
-        IL_004e:  stloc.0
-        IL_004f:  stfld      int32 Program/'<<Main>$>d__0'::'<>1__state'
-        IL_0054:  ldarg.0
-        IL_0055:  ldloc.2
-        IL_0056:  stfld      valuetype [System.Runtime]System.Runtime.CompilerServices.TaskAwaiter`1<uint32> Program/'<<Main>$>d__0'::'<>u__1'
-        IL_005b:  ldarg.0
-        IL_005c:  stloc.3
-        IL_005d:  ldarg.0
-        IL_005e:  ldflda     valuetype [System.Runtime]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<int32> Program/'<<Main>$>d__0'::'<>t__builder'
-        IL_0063:  ldloca.s   V_2
-        IL_0065:  ldloca.s   V_3
-        IL_0067:  call       instance void valuetype [System.Runtime]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<int32>::AwaitUnsafeOnCompleted<valuetype [System.Runtime]System.Runtime.CompilerServices.TaskAwaiter`1<uint32>,class Program/'<<Main>$>d__0'>(!!0&,
-                                                                                                                                                                                                                                                                            !!1&)
-        IL_006c:  nop
-        IL_006d:  leave      IL_014d
-
-        IL_0072:  ldarg.0
-        IL_0073:  ldfld      valuetype [System.Runtime]System.Runtime.CompilerServices.TaskAwaiter`1<uint32> Program/'<<Main>$>d__0'::'<>u__1'
-        IL_0078:  stloc.2
-        IL_0079:  ldarg.0
-        IL_007a:  ldflda     valuetype [System.Runtime]System.Runtime.CompilerServices.TaskAwaiter`1<uint32> Program/'<<Main>$>d__0'::'<>u__1'
-        IL_007f:  initobj    valuetype [System.Runtime]System.Runtime.CompilerServices.TaskAwaiter`1<uint32>
-        IL_0085:  ldarg.0
-        IL_0086:  ldc.i4.m1
-        IL_0087:  dup
-        IL_0088:  stloc.0
-        IL_0089:  stfld      int32 Program/'<<Main>$>d__0'::'<>1__state'
-        IL_008e:  ldarg.0
-        IL_008f:  ldloca.s   V_2
-        IL_0091:  call       instance !0 valuetype [System.Runtime]System.Runtime.CompilerServices.TaskAwaiter`1<uint32>::GetResult()
-        IL_0096:  stfld      uint32 Program/'<<Main>$>d__0'::'<>s__4'
-        IL_009b:  ldarg.0
-        IL_009c:  ldarg.0
-        IL_009d:  ldfld      uint32 Program/'<<Main>$>d__0'::'<>s__4'
-        IL_00a2:  stfld      uint32 Program/'<<Main>$>d__0'::'<result>5__3'
-        IL_00a7:  ldc.i4.s   11
-        IL_00a9:  ldc.i4.2
-        IL_00aa:  newobj     instance void [System.Runtime]System.Runtime.CompilerServices.DefaultInterpolatedStringHandler::.ctor(int32,
-                                                                                                                                   int32)
-        IL_00af:  stloc.s    V_4
-        IL_00b1:  ldloca.s   V_4
-        IL_00b3:  ldarg.0
-        IL_00b4:  ldfld      class [System.Runtime]System.Diagnostics.Stopwatch Program/'<<Main>$>d__0'::'<sw>5__2'
-        IL_00b9:  callvirt   instance int64 [System.Runtime]System.Diagnostics.Stopwatch::get_ElapsedMilliseconds()
-        IL_00be:  call       instance void [System.Runtime]System.Runtime.CompilerServices.DefaultInterpolatedStringHandler::AppendFormatted<int64>(!!0)
-        IL_00c3:  nop
-        IL_00c4:  ldloca.s   V_4
-        IL_00c6:  ldstr      " ms result="
-        IL_00cb:  call       instance void [System.Runtime]System.Runtime.CompilerServices.DefaultInterpolatedStringHandler::AppendLiteral(string)
-        IL_00d0:  nop
-        IL_00d1:  ldloca.s   V_4
-        IL_00d3:  ldarg.0
-        IL_00d4:  ldfld      uint32 Program/'<<Main>$>d__0'::'<result>5__3'
-        IL_00d9:  call       instance void [System.Runtime]System.Runtime.CompilerServices.DefaultInterpolatedStringHandler::AppendFormatted<uint32>(!!0)
-        IL_00de:  nop
-        IL_00df:  ldloca.s   V_4
-        IL_00e1:  call       instance string [System.Runtime]System.Runtime.CompilerServices.DefaultInterpolatedStringHandler::ToStringAndClear()
-        IL_00e6:  call       void [System.Console]System.Console::WriteLine(string)
-        IL_00eb:  nop
-        IL_00ec:  nop
-        IL_00ed:  ldarg.0
-        IL_00ee:  ldnull
-        IL_00ef:  stfld      class [System.Runtime]System.Diagnostics.Stopwatch Program/'<<Main>$>d__0'::'<sw>5__2'
-        IL_00f4:  ldarg.0
-        IL_00f5:  ldfld      int32 Program/'<<Main>$>d__0'::'<i>5__1'
-        IL_00fa:  stloc.s    V_5
-        IL_00fc:  ldarg.0
-        IL_00fd:  ldloc.s    V_5
-        IL_00ff:  ldc.i4.1
-        IL_0100:  add
-        IL_0101:  stfld      int32 Program/'<<Main>$>d__0'::'<i>5__1'
-        IL_0106:  ldarg.0
-        IL_0107:  ldfld      int32 Program/'<<Main>$>d__0'::'<i>5__1'
-        IL_010c:  ldc.i4.s   10
-        IL_010e:  clt
-        IL_0110:  stloc.s    V_6
-        IL_0112:  ldloc.s    V_6
-        IL_0114:  brtrue     IL_001a
-
-        IL_0119:  ldc.i4.s   100
-        IL_011b:  stloc.1
-        IL_011c:  leave.s    IL_0138
-
-      }  // end .try
-      catch [System.Runtime]System.Exception 
-      {
-        IL_011e:  stloc.s    V_7
-        IL_0120:  ldarg.0
-        IL_0121:  ldc.i4.s   -2
-        IL_0123:  stfld      int32 Program/'<<Main>$>d__0'::'<>1__state'
-        IL_0128:  ldarg.0
-        IL_0129:  ldflda     valuetype [System.Runtime]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<int32> Program/'<<Main>$>d__0'::'<>t__builder'
-        IL_012e:  ldloc.s    V_7
-        IL_0130:  call       instance void valuetype [System.Runtime]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<int32>::SetException(class [System.Runtime]System.Exception)
-        IL_0135:  nop
-        IL_0136:  leave.s    IL_014d
-
-      }  // end handler
-      IL_0138:  ldarg.0
-      IL_0139:  ldc.i4.s   -2
-      IL_013b:  stfld      int32 Program/'<<Main>$>d__0'::'<>1__state'
-      IL_0140:  ldarg.0
-      IL_0141:  ldflda     valuetype [System.Runtime]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<int32> Program/'<<Main>$>d__0'::'<>t__builder'
-      IL_0146:  ldloc.1
-      IL_0147:  call       instance void valuetype [System.Runtime]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<int32>::SetResult(!0)
-      IL_014c:  nop
-      IL_014d:  ret
-    } // end of method '<<Main>$>d__0'::MoveNext
-
-    .method private hidebysig newslot virtual final 
-            instance void  SetStateMachine(class [System.Runtime]System.Runtime.CompilerServices.IAsyncStateMachine stateMachine) cil managed
-    {
-      .custom instance void [System.Runtime]System.Diagnostics.DebuggerHiddenAttribute::.ctor() = ( 01 00 00 00 ) 
-      .override [System.Runtime]System.Runtime.CompilerServices.IAsyncStateMachine::SetStateMachine
-      // Code size       1 (0x1)
-      .maxstack  8
-      IL_0000:  ret
-    } // end of method '<<Main>$>d__0'::SetStateMachine
-
-  } // end of class '<<Main>$>d__0'
-
-  .method private hidebysig static class [System.Runtime]System.Threading.Tasks.Task`1<int32> 
-          '<Main>$'(string[] args) cil managed
-  {
-    .custom instance void [System.Runtime]System.Runtime.CompilerServices.AsyncStateMachineAttribute::.ctor(class [System.Runtime]System.Type) = ( 01 00 15 50 72 6F 67 72 61 6D 2B 3C 3C 4D 61 69   // ...Program+<<Mai
-                                                                                                                                                   6E 3E 24 3E 64 5F 5F 30 00 00 )                   // n>$>d__0..
-    .custom instance void [System.Runtime]System.Diagnostics.DebuggerStepThroughAttribute::.ctor() = ( 01 00 00 00 ) 
-    // Code size       57 (0x39)
-    .maxstack  2
-    .locals init (class Program/'<<Main>$>d__0' V_0)
-    IL_0000:  newobj     instance void Program/'<<Main>$>d__0'::.ctor()
-    IL_0005:  stloc.0
-    IL_0006:  ldloc.0
-    IL_0007:  call       valuetype [System.Runtime]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<!0> valuetype [System.Runtime]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<int32>::Create()
-    IL_000c:  stfld      valuetype [System.Runtime]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<int32> Program/'<<Main>$>d__0'::'<>t__builder'
-    IL_0011:  ldloc.0
-    IL_0012:  ldarg.0
-    IL_0013:  stfld      string[] Program/'<<Main>$>d__0'::args
-    IL_0018:  ldloc.0
-    IL_0019:  ldc.i4.m1
-    IL_001a:  stfld      int32 Program/'<<Main>$>d__0'::'<>1__state'
-    IL_001f:  ldloc.0
-    IL_0020:  ldflda     valuetype [System.Runtime]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<int32> Program/'<<Main>$>d__0'::'<>t__builder'
-    IL_0025:  ldloca.s   V_0
-    IL_0027:  call       instance void valuetype [System.Runtime]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<int32>::Start<class Program/'<<Main>$>d__0'>(!!0&)
-    IL_002c:  nop
-    IL_002d:  ldloc.0
-    IL_002e:  ldflda     valuetype [System.Runtime]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<int32> Program/'<<Main>$>d__0'::'<>t__builder'
-    IL_0033:  call       instance class [System.Runtime]System.Threading.Tasks.Task`1<!0> valuetype [System.Runtime]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<int32>::get_Task()
-    IL_0038:  ret
-  } // end of method Program::'<Main>$'
-
-  .method public hidebysig specialname rtspecialname 
-          instance void  .ctor() cil managed
-  {
-    // Code size       8 (0x8)
-    .maxstack  8
-    IL_0000:  ldarg.0
-    IL_0001:  call       instance void [System.Runtime]System.Object::.ctor()
-    IL_0006:  nop
-    IL_0007:  ret
-  } // end of method Program::.ctor
-
-  .method private hidebysig specialname static 
-          int32  '<Main>'(string[] args) cil managed
-  {
-    .entrypoint
-    .custom instance void [System.Runtime]System.Diagnostics.DebuggerStepThroughAttribute::.ctor() = ( 01 00 00 00 ) 
-    // Code size       20 (0x14)
-    .maxstack  1
-    .locals init (valuetype [System.Runtime]System.Runtime.CompilerServices.TaskAwaiter`1<int32> V_0)
-    IL_0000:  ldarg.0
-    IL_0001:  call       class [System.Runtime]System.Threading.Tasks.Task`1<int32> Program::'<Main>$'(string[])
-    IL_0006:  callvirt   instance valuetype [System.Runtime]System.Runtime.CompilerServices.TaskAwaiter`1<!0> class [System.Runtime]System.Threading.Tasks.Task`1<int32>::GetAwaiter()
-    IL_000b:  stloc.0
-    IL_000c:  ldloca.s   V_0
-    IL_000e:  call       instance !0 valuetype [System.Runtime]System.Runtime.CompilerServices.TaskAwaiter`1<int32>::GetResult()
-    IL_0013:  ret
-  } // end of method Program::'<Main>'
-
-  .method assembly hidebysig static uint32 modopt([System.Runtime]System.Threading.Tasks.Task`1)
-          '<<Main>$>g__A|0_0'(uint32 n) cil managed
-  {
-    .custom instance void [System.Runtime]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) 
-    // Code size       32 (0x20)
-    .maxstack  2
-    .locals init (uint32 V_0,
-             uint32 V_1,
-             bool V_2,
-             uint32 V_3)
-    IL_0000:  nop
-    IL_0001:  ldarg.0
-    IL_0002:  stloc.0
-    IL_0003:  ldc.i4.0
-    IL_0004:  stloc.1
-    IL_0005:  br.s       IL_0012
-
-    IL_0007:  ldloc.0
-    IL_0008:  call       uint32 modopt([System.Runtime]System.Threading.Tasks.Task`1) Program::'<<Main>$>g__B|0_1'(uint32)
-    IL_000d:  stloc.0
-    IL_000e:  ldloc.1
-    IL_000f:  ldc.i4.1
-    IL_0010:  add
-    IL_0011:  stloc.1
-    IL_0012:  ldloc.1
-    IL_0013:  ldarg.0
-    IL_0014:  clt.un
-    IL_0016:  stloc.2
-    IL_0017:  ldloc.2
-    IL_0018:  brtrue.s   IL_0007
-
-    IL_001a:  ldloc.0
-    IL_001b:  stloc.3
-    IL_001c:  br.s       IL_001e
-
-    IL_001e:  ldloc.3
-    IL_001f:  ret
-  } // end of method Program::'<<Main>$>g__A|0_0'
-
-  .method assembly hidebysig static uint32 modopt([System.Runtime]System.Threading.Tasks.Task`1)
-          '<<Main>$>g__B|0_1'(uint32 n) cil managed
-  {
-  .custom instance void [System.Runtime]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) 
-  // Code size       89 (0x59)
-  .maxstack  2
-  .locals init (uint32 V_0,
-           bool V_1,
-           bool V_2,
-           bool V_3,
-           uint32 V_4,
-           valuetype [System.Runtime]System.Runtime.CompilerServices.YieldAwaitable V_5,
-           valuetype [System.Runtime]System.Runtime.CompilerServices.YieldAwaitable/YieldAwaiter V_6)
-  IL_0000:  nop
-  IL_0001:  ldarg.0
-  IL_0002:  stloc.0
-  IL_0003:  ldloc.0
-  IL_0004:  ldc.i4     0x773593ed
-  IL_0009:  mul
-  IL_000a:  stloc.0
-  IL_000b:  ldloc.0
-  IL_000c:  ldc.i4     0x3e8
-  IL_0011:  clt.un
-  IL_0013:  stloc.1
-  IL_0014:  ldloc.1
-  IL_0015:  brfalse.s  IL_001d
-  IL_0017:  call       valuetype [System.Runtime]System.Runtime.CompilerServices.YieldAwaitable [System.Runtime]System.Threading.Tasks.Task::Yield()
-            stloc 5
-            ldloca 5
-            call       instance valuetype [System.Runtime]System.Runtime.CompilerServices.YieldAwaitable/YieldAwaiter [System.Runtime]System.Runtime.CompilerServices.YieldAwaitable::GetAwaiter()
-            stloc 6
-            ldloca     6
-            call instance bool [System.Runtime]System.Runtime.CompilerServices.YieldAwaitable/YieldAwaiter::get_IsCompleted()
-            brtrue IL_FirstAwaitFinished
-            ldloc 6
-            call       void [System.Runtime]System.Runtime.CompilerServices.RuntimeHelpers::UnsafeAwaitAwaiterFromRuntimeAsync<valuetype [System.Runtime]System.Runtime.CompilerServices.YieldAwaitable/YieldAwaiter>(!!0)
-  IL_FirstAwaitFinished: ldloca 6
-            call instance void [System.Runtime]System.Runtime.CompilerServices.YieldAwaitable/YieldAwaiter::GetResult()
-  IL_001d:  ldloc.0
-  IL_001e:  ldc.i4     0x773593ed
-  IL_0023:  mul
-  IL_0024:  stloc.0
-  IL_0025:  ldloc.0
-  IL_0026:  ldc.i4     0x3e8
-  IL_002b:  clt.un
-  IL_002d:  stloc.2
-  IL_002e:  ldloc.2
-  IL_002f:  brfalse.s  IL_0037
-  IL_0031:  call       valuetype [System.Runtime]System.Runtime.CompilerServices.YieldAwaitable [System.Runtime]System.Threading.Tasks.Task::Yield()
-            stloc 5
-            ldloca 5
-            call       instance valuetype [System.Runtime]System.Runtime.CompilerServices.YieldAwaitable/YieldAwaiter [System.Runtime]System.Runtime.CompilerServices.YieldAwaitable::GetAwaiter()
-            stloc 6
-            ldloca     6
-            call instance bool [System.Runtime]System.Runtime.CompilerServices.YieldAwaitable/YieldAwaiter::get_IsCompleted()
-            brtrue IL_SecondAwaitFinished
-            ldloc 6
-            call       void [System.Runtime]System.Runtime.CompilerServices.RuntimeHelpers::UnsafeAwaitAwaiterFromRuntimeAsync<valuetype [System.Runtime]System.Runtime.CompilerServices.YieldAwaitable/YieldAwaiter>(!!0)
-  IL_SecondAwaitFinished: ldloca 6
-            call instance void [System.Runtime]System.Runtime.CompilerServices.YieldAwaitable/YieldAwaiter::GetResult()
-  IL_0037:  ldloc.0
-  IL_0038:  ldc.i4     0x773593ed
-  IL_003d:  mul
-  IL_003e:  stloc.0
-  IL_003f:  ldloc.0
-  IL_0040:  ldc.i4     0x3e8
-  IL_0045:  clt.un
-  IL_0047:  stloc.3
-  IL_0048:  ldloc.3
-  IL_0049:  brfalse.s  IL_0051
-  IL_004b:  call       valuetype [System.Runtime]System.Runtime.CompilerServices.YieldAwaitable [System.Runtime]System.Threading.Tasks.Task::Yield()
-            stloc 5
-            ldloca 5
-            call       instance valuetype [System.Runtime]System.Runtime.CompilerServices.YieldAwaitable/YieldAwaiter [System.Runtime]System.Runtime.CompilerServices.YieldAwaitable::GetAwaiter()
-            stloc 6
-            ldloca     6
-            call instance bool [System.Runtime]System.Runtime.CompilerServices.YieldAwaitable/YieldAwaiter::get_IsCompleted()
-            brtrue IL_ThirdAwaitFinished
-            ldloc 6
-            call       void [System.Runtime]System.Runtime.CompilerServices.RuntimeHelpers::UnsafeAwaitAwaiterFromRuntimeAsync<valuetype [System.Runtime]System.Runtime.CompilerServices.YieldAwaitable/YieldAwaiter>(!!0)
-  IL_ThirdAwaitFinished: ldloca 6
-            call instance void [System.Runtime]System.Runtime.CompilerServices.YieldAwaitable/YieldAwaiter::GetResult()
-  IL_0051:  ldloc.0
-  IL_0052:  stloc.s    V_4
-  IL_0054:  br.s       IL_0056
-  IL_0056:  ldloc.s    V_4
-  IL_0058:  ret
-  } // end of method Program::'<<Main>$>g__B|0_1'
-
-} // end of class Program
-
-
-// =============================================================
-
-// *********** DISASSEMBLY COMPLETE ***********************
diff --git a/src/tests/Loader/async/runtimetask-asyncfibonacci-without-yields.il b/src/tests/Loader/async/runtimetask-asyncfibonacci-without-yields.il
deleted file mode 100644
index b48f72bb7ea9f8..00000000000000
--- a/src/tests/Loader/async/runtimetask-asyncfibonacci-without-yields.il
+++ /dev/null
@@ -1,377 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-
-
-//  .NET IL Disassembler.  Version 9.0.0-dev
-
-
-
-// Metadata version: v4.0.30319
-.assembly extern System.Runtime
-{
-  .publickeytoken = (B0 3F 5F 7F 11 D5 0A 3A )                         // .?_....:
-  .ver 9:0:0:0
-}
-.assembly extern System.Console
-{
-  .publickeytoken = (B0 3F 5F 7F 11 D5 0A 3A )                         // .?_....:
-  .ver 9:0:0:0
-}
-.assembly 'runtimetask-asyncfibonacci-without-yields'
-{
-  .custom instance void [System.Runtime]System.Runtime.CompilerServices.CompilationRelaxationsAttribute::.ctor(int32) = ( 01 00 08 00 00 00 00 00 ) 
-  .custom instance void [System.Runtime]System.Runtime.CompilerServices.RuntimeCompatibilityAttribute::.ctor() = ( 01 00 01 00 54 02 16 57 72 61 70 4E 6F 6E 45 78   // ....T..WrapNonEx
-                                                                                                                   63 65 70 74 69 6F 6E 54 68 72 6F 77 73 01 )       // ceptionThrows.
-
-  // --- The following custom attribute is added automatically, do not uncomment -------
-  //  .custom instance void [System.Runtime]System.Diagnostics.DebuggableAttribute::.ctor(valuetype [System.Runtime]System.Diagnostics.DebuggableAttribute/DebuggingModes) = ( 01 00 07 01 00 00 00 00 ) 
-
-  .hash algorithm 0x00008004
-  .ver 0:0:0:0
-}
-.module 'runtimetask-asyncfibonacci-without-yields.dll'
-// MVID: {ca42840a-c801-47a9-8471-ea986ae2c60d}
-.custom instance void [System.Runtime]System.Runtime.CompilerServices.RefSafetyRulesAttribute::.ctor(int32) = ( 01 00 0B 00 00 00 00 00 ) 
-.imagebase 0x00400000
-.file alignment 0x00000200
-.stackreserve 0x00100000
-.subsystem 0x0003       // WINDOWS_CUI
-.corflags 0x00000001    //  ILONLY
-// Image base: 0x000001E00BDE0000
-
-
-// =============== CLASS MEMBERS DECLARATION ===================
-
-.class private auto ansi beforefieldinit Program
-       extends [System.Runtime]System.Object
-{
-  .class auto ansi sealed nested private beforefieldinit '<<Main>$>d__0'
-         extends [System.Runtime]System.Object
-         implements [System.Runtime]System.Runtime.CompilerServices.IAsyncStateMachine
-  {
-    .custom instance void [System.Runtime]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) 
-    .field public int32 '<>1__state'
-    .field public valuetype [System.Runtime]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<int32> '<>t__builder'
-    .field public string[] args
-    .field private int32 '<i>5__1'
-    .field private class [System.Runtime]System.Diagnostics.Stopwatch '<sw>5__2'
-    .field private uint32 '<result>5__3'
-    .field private uint32 '<>s__4'
-    .field private valuetype [System.Runtime]System.Runtime.CompilerServices.TaskAwaiter`1<uint32> '<>u__1'
-    .method public hidebysig specialname rtspecialname 
-            instance void  .ctor() cil managed
-    {
-      // Code size       8 (0x8)
-      .maxstack  8
-      IL_0000:  ldarg.0
-      IL_0001:  call       instance void [System.Runtime]System.Object::.ctor()
-      IL_0006:  nop
-      IL_0007:  ret
-    } // end of method '<<Main>$>d__0'::.ctor
-
-    .method private hidebysig newslot virtual final 
-            instance void  MoveNext() cil managed
-    {
-      .override [System.Runtime]System.Runtime.CompilerServices.IAsyncStateMachine::MoveNext
-      // Code size       334 (0x14e)
-      .maxstack  3
-      .locals init (int32 V_0,
-               int32 V_1,
-               valuetype [System.Runtime]System.Runtime.CompilerServices.TaskAwaiter`1<uint32> V_2,
-               class Program/'<<Main>$>d__0' V_3,
-               valuetype [System.Runtime]System.Runtime.CompilerServices.DefaultInterpolatedStringHandler V_4,
-               int32 V_5,
-               bool V_6,
-               class [System.Runtime]System.Exception V_7)
-      IL_0000:  ldarg.0
-      IL_0001:  ldfld      int32 Program/'<<Main>$>d__0'::'<>1__state'
-      IL_0006:  stloc.0
-      .try
-      {
-        IL_0007:  ldloc.0
-        IL_0008:  brfalse.s  IL_000c
-
-        IL_000a:  br.s       IL_000e
-
-        IL_000c:  br.s       IL_0072
-
-        IL_000e:  ldarg.0
-        IL_000f:  ldc.i4.0
-        IL_0010:  stfld      int32 Program/'<<Main>$>d__0'::'<i>5__1'
-        IL_0015:  br         IL_0106
-
-        IL_001a:  nop
-        IL_001b:  ldarg.0
-        IL_001c:  newobj     instance void [System.Runtime]System.Diagnostics.Stopwatch::.ctor()
-        IL_0021:  stfld      class [System.Runtime]System.Diagnostics.Stopwatch Program/'<<Main>$>d__0'::'<sw>5__2'
-        IL_0026:  ldarg.0
-        IL_0027:  ldfld      class [System.Runtime]System.Diagnostics.Stopwatch Program/'<<Main>$>d__0'::'<sw>5__2'
-        IL_002c:  callvirt   instance void [System.Runtime]System.Diagnostics.Stopwatch::Start()
-        IL_0031:  nop
-        IL_0032:  ldc.i4     0x5f5e100
-        IL_0037:  call       class [System.Runtime]System.Threading.Tasks.Task`1<uint32> Program::'<<Main>$>g__A|0_0'(uint32)
-        IL_003c:  callvirt   instance valuetype [System.Runtime]System.Runtime.CompilerServices.TaskAwaiter`1<!0> class [System.Runtime]System.Threading.Tasks.Task`1<uint32>::GetAwaiter()
-        IL_0041:  stloc.2
-        IL_0042:  ldloca.s   V_2
-        IL_0044:  call       instance bool valuetype [System.Runtime]System.Runtime.CompilerServices.TaskAwaiter`1<uint32>::get_IsCompleted()
-        IL_0049:  brtrue.s   IL_008e
-
-        IL_004b:  ldarg.0
-        IL_004c:  ldc.i4.0
-        IL_004d:  dup
-        IL_004e:  stloc.0
-        IL_004f:  stfld      int32 Program/'<<Main>$>d__0'::'<>1__state'
-        IL_0054:  ldarg.0
-        IL_0055:  ldloc.2
-        IL_0056:  stfld      valuetype [System.Runtime]System.Runtime.CompilerServices.TaskAwaiter`1<uint32> Program/'<<Main>$>d__0'::'<>u__1'
-        IL_005b:  ldarg.0
-        IL_005c:  stloc.3
-        IL_005d:  ldarg.0
-        IL_005e:  ldflda     valuetype [System.Runtime]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<int32> Program/'<<Main>$>d__0'::'<>t__builder'
-        IL_0063:  ldloca.s   V_2
-        IL_0065:  ldloca.s   V_3
-        IL_0067:  call       instance void valuetype [System.Runtime]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<int32>::AwaitUnsafeOnCompleted<valuetype [System.Runtime]System.Runtime.CompilerServices.TaskAwaiter`1<uint32>,class Program/'<<Main>$>d__0'>(!!0&,
-                                                                                                                                                                                                                                                                            !!1&)
-        IL_006c:  nop
-        IL_006d:  leave      IL_014d
-
-        IL_0072:  ldarg.0
-        IL_0073:  ldfld      valuetype [System.Runtime]System.Runtime.CompilerServices.TaskAwaiter`1<uint32> Program/'<<Main>$>d__0'::'<>u__1'
-        IL_0078:  stloc.2
-        IL_0079:  ldarg.0
-        IL_007a:  ldflda     valuetype [System.Runtime]System.Runtime.CompilerServices.TaskAwaiter`1<uint32> Program/'<<Main>$>d__0'::'<>u__1'
-        IL_007f:  initobj    valuetype [System.Runtime]System.Runtime.CompilerServices.TaskAwaiter`1<uint32>
-        IL_0085:  ldarg.0
-        IL_0086:  ldc.i4.m1
-        IL_0087:  dup
-        IL_0088:  stloc.0
-        IL_0089:  stfld      int32 Program/'<<Main>$>d__0'::'<>1__state'
-        IL_008e:  ldarg.0
-        IL_008f:  ldloca.s   V_2
-        IL_0091:  call       instance !0 valuetype [System.Runtime]System.Runtime.CompilerServices.TaskAwaiter`1<uint32>::GetResult()
-        IL_0096:  stfld      uint32 Program/'<<Main>$>d__0'::'<>s__4'
-        IL_009b:  ldarg.0
-        IL_009c:  ldarg.0
-        IL_009d:  ldfld      uint32 Program/'<<Main>$>d__0'::'<>s__4'
-        IL_00a2:  stfld      uint32 Program/'<<Main>$>d__0'::'<result>5__3'
-        IL_00a7:  ldc.i4.s   11
-        IL_00a9:  ldc.i4.2
-        IL_00aa:  newobj     instance void [System.Runtime]System.Runtime.CompilerServices.DefaultInterpolatedStringHandler::.ctor(int32,
-                                                                                                                                   int32)
-        IL_00af:  stloc.s    V_4
-        IL_00b1:  ldloca.s   V_4
-        IL_00b3:  ldarg.0
-        IL_00b4:  ldfld      class [System.Runtime]System.Diagnostics.Stopwatch Program/'<<Main>$>d__0'::'<sw>5__2'
-        IL_00b9:  callvirt   instance int64 [System.Runtime]System.Diagnostics.Stopwatch::get_ElapsedMilliseconds()
-        IL_00be:  call       instance void [System.Runtime]System.Runtime.CompilerServices.DefaultInterpolatedStringHandler::AppendFormatted<int64>(!!0)
-        IL_00c3:  nop
-        IL_00c4:  ldloca.s   V_4
-        IL_00c6:  ldstr      " ms result="
-        IL_00cb:  call       instance void [System.Runtime]System.Runtime.CompilerServices.DefaultInterpolatedStringHandler::AppendLiteral(string)
-        IL_00d0:  nop
-        IL_00d1:  ldloca.s   V_4
-        IL_00d3:  ldarg.0
-        IL_00d4:  ldfld      uint32 Program/'<<Main>$>d__0'::'<result>5__3'
-        IL_00d9:  call       instance void [System.Runtime]System.Runtime.CompilerServices.DefaultInterpolatedStringHandler::AppendFormatted<uint32>(!!0)
-        IL_00de:  nop
-        IL_00df:  ldloca.s   V_4
-        IL_00e1:  call       instance string [System.Runtime]System.Runtime.CompilerServices.DefaultInterpolatedStringHandler::ToStringAndClear()
-        IL_00e6:  call       void [System.Console]System.Console::WriteLine(string)
-        IL_00eb:  nop
-        IL_00ec:  nop
-        IL_00ed:  ldarg.0
-        IL_00ee:  ldnull
-        IL_00ef:  stfld      class [System.Runtime]System.Diagnostics.Stopwatch Program/'<<Main>$>d__0'::'<sw>5__2'
-        IL_00f4:  ldarg.0
-        IL_00f5:  ldfld      int32 Program/'<<Main>$>d__0'::'<i>5__1'
-        IL_00fa:  stloc.s    V_5
-        IL_00fc:  ldarg.0
-        IL_00fd:  ldloc.s    V_5
-        IL_00ff:  ldc.i4.1
-        IL_0100:  add
-        IL_0101:  stfld      int32 Program/'<<Main>$>d__0'::'<i>5__1'
-        IL_0106:  ldarg.0
-        IL_0107:  ldfld      int32 Program/'<<Main>$>d__0'::'<i>5__1'
-        IL_010c:  ldc.i4.s   10
-        IL_010e:  clt
-        IL_0110:  stloc.s    V_6
-        IL_0112:  ldloc.s    V_6
-        IL_0114:  brtrue     IL_001a
-
-        IL_0119:  ldc.i4.s   100
-        IL_011b:  stloc.1
-        IL_011c:  leave.s    IL_0138
-
-      }  // end .try
-      catch [System.Runtime]System.Exception 
-      {
-        IL_011e:  stloc.s    V_7
-        IL_0120:  ldarg.0
-        IL_0121:  ldc.i4.s   -2
-        IL_0123:  stfld      int32 Program/'<<Main>$>d__0'::'<>1__state'
-        IL_0128:  ldarg.0
-        IL_0129:  ldflda     valuetype [System.Runtime]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<int32> Program/'<<Main>$>d__0'::'<>t__builder'
-        IL_012e:  ldloc.s    V_7
-        IL_0130:  call       instance void valuetype [System.Runtime]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<int32>::SetException(class [System.Runtime]System.Exception)
-        IL_0135:  nop
-        IL_0136:  leave.s    IL_014d
-
-      }  // end handler
-      IL_0138:  ldarg.0
-      IL_0139:  ldc.i4.s   -2
-      IL_013b:  stfld      int32 Program/'<<Main>$>d__0'::'<>1__state'
-      IL_0140:  ldarg.0
-      IL_0141:  ldflda     valuetype [System.Runtime]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<int32> Program/'<<Main>$>d__0'::'<>t__builder'
-      IL_0146:  ldloc.1
-      IL_0147:  call       instance void valuetype [System.Runtime]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<int32>::SetResult(!0)
-      IL_014c:  nop
-      IL_014d:  ret
-    } // end of method '<<Main>$>d__0'::MoveNext
-
-    .method private hidebysig newslot virtual final 
-            instance void  SetStateMachine(class [System.Runtime]System.Runtime.CompilerServices.IAsyncStateMachine stateMachine) cil managed
-    {
-      .custom instance void [System.Runtime]System.Diagnostics.DebuggerHiddenAttribute::.ctor() = ( 01 00 00 00 ) 
-      .override [System.Runtime]System.Runtime.CompilerServices.IAsyncStateMachine::SetStateMachine
-      // Code size       1 (0x1)
-      .maxstack  8
-      IL_0000:  ret
-    } // end of method '<<Main>$>d__0'::SetStateMachine
-
-  } // end of class '<<Main>$>d__0'
-
-  .method private hidebysig static class [System.Runtime]System.Threading.Tasks.Task`1<int32> 
-          '<Main>$'(string[] args) cil managed
-  {
-    .custom instance void [System.Runtime]System.Runtime.CompilerServices.AsyncStateMachineAttribute::.ctor(class [System.Runtime]System.Type) = ( 01 00 15 50 72 6F 67 72 61 6D 2B 3C 3C 4D 61 69   // ...Program+<<Mai
-                                                                                                                                                   6E 3E 24 3E 64 5F 5F 30 00 00 )                   // n>$>d__0..
-    .custom instance void [System.Runtime]System.Diagnostics.DebuggerStepThroughAttribute::.ctor() = ( 01 00 00 00 ) 
-    // Code size       57 (0x39)
-    .maxstack  2
-    .locals init (class Program/'<<Main>$>d__0' V_0)
-    IL_0000:  newobj     instance void Program/'<<Main>$>d__0'::.ctor()
-    IL_0005:  stloc.0
-    IL_0006:  ldloc.0
-    IL_0007:  call       valuetype [System.Runtime]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<!0> valuetype [System.Runtime]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<int32>::Create()
-    IL_000c:  stfld      valuetype [System.Runtime]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<int32> Program/'<<Main>$>d__0'::'<>t__builder'
-    IL_0011:  ldloc.0
-    IL_0012:  ldarg.0
-    IL_0013:  stfld      string[] Program/'<<Main>$>d__0'::args
-    IL_0018:  ldloc.0
-    IL_0019:  ldc.i4.m1
-    IL_001a:  stfld      int32 Program/'<<Main>$>d__0'::'<>1__state'
-    IL_001f:  ldloc.0
-    IL_0020:  ldflda     valuetype [System.Runtime]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<int32> Program/'<<Main>$>d__0'::'<>t__builder'
-    IL_0025:  ldloca.s   V_0
-    IL_0027:  call       instance void valuetype [System.Runtime]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<int32>::Start<class Program/'<<Main>$>d__0'>(!!0&)
-    IL_002c:  nop
-    IL_002d:  ldloc.0
-    IL_002e:  ldflda     valuetype [System.Runtime]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<int32> Program/'<<Main>$>d__0'::'<>t__builder'
-    IL_0033:  call       instance class [System.Runtime]System.Threading.Tasks.Task`1<!0> valuetype [System.Runtime]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<int32>::get_Task()
-    IL_0038:  ret
-  } // end of method Program::'<Main>$'
-
-  .method public hidebysig specialname rtspecialname 
-          instance void  .ctor() cil managed
-  {
-    // Code size       8 (0x8)
-    .maxstack  8
-    IL_0000:  ldarg.0
-    IL_0001:  call       instance void [System.Runtime]System.Object::.ctor()
-    IL_0006:  nop
-    IL_0007:  ret
-  } // end of method Program::.ctor
-
-  .method private hidebysig specialname static 
-          int32  '<Main>'(string[] args) cil managed
-  {
-    .entrypoint
-    .custom instance void [System.Runtime]System.Diagnostics.DebuggerStepThroughAttribute::.ctor() = ( 01 00 00 00 ) 
-    // Code size       20 (0x14)
-    .maxstack  1
-    .locals init (valuetype [System.Runtime]System.Runtime.CompilerServices.TaskAwaiter`1<int32> V_0)
-    IL_0000:  ldarg.0
-    IL_0001:  call       class [System.Runtime]System.Threading.Tasks.Task`1<int32> Program::'<Main>$'(string[])
-    IL_0006:  callvirt   instance valuetype [System.Runtime]System.Runtime.CompilerServices.TaskAwaiter`1<!0> class [System.Runtime]System.Threading.Tasks.Task`1<int32>::GetAwaiter()
-    IL_000b:  stloc.0
-    IL_000c:  ldloca.s   V_0
-    IL_000e:  call       instance !0 valuetype [System.Runtime]System.Runtime.CompilerServices.TaskAwaiter`1<int32>::GetResult()
-    IL_0013:  ret
-  } // end of method Program::'<Main>'
-
-  .method assembly hidebysig static uint32 modopt([System.Runtime]System.Threading.Tasks.Task`1)
-          '<<Main>$>g__A|0_0'(uint32 n) cil managed
-  {
-    .custom instance void [System.Runtime]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) 
-    // Code size       32 (0x20)
-    .maxstack  2
-    .locals init (uint32 V_0,
-             uint32 V_1,
-             bool V_2,
-             uint32 V_3)
-    IL_0000:  nop
-    IL_0001:  ldarg.0
-    IL_0002:  stloc.0
-    IL_0003:  ldc.i4.0
-    IL_0004:  stloc.1
-    IL_0005:  br.s       IL_0012
-
-    IL_0007:  ldloc.0
-    IL_0008:  call       uint32 modopt([System.Runtime]System.Threading.Tasks.Task`1) Program::'<<Main>$>g__B|0_1'(uint32)
-    IL_000d:  stloc.0
-    IL_000e:  ldloc.1
-    IL_000f:  ldc.i4.1
-    IL_0010:  add
-    IL_0011:  stloc.1
-    IL_0012:  ldloc.1
-    IL_0013:  ldarg.0
-    IL_0014:  clt.un
-    IL_0016:  stloc.2
-    IL_0017:  ldloc.2
-    IL_0018:  brtrue.s   IL_0007
-
-    IL_001a:  ldloc.0
-    IL_001b:  stloc.3
-    IL_001c:  br.s       IL_001e
-
-    IL_001e:  ldloc.3
-    IL_001f:  ret
-  } // end of method Program::'<<Main>$>g__A|0_0'
-
-  .method assembly hidebysig static uint32 modopt([System.Runtime]System.Threading.Tasks.Task`1)
-          '<<Main>$>g__B|0_1'(uint32 n) cil managed
-  {
-    .custom instance void [System.Runtime]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) 
-    // Code size       33 (0x21)
-    .maxstack  2
-    .locals init (uint32 V_0,
-             uint32 V_1)
-    IL_0000:  nop
-    IL_0001:  ldarg.0
-    IL_0002:  stloc.0
-    IL_0003:  ldloc.0
-    IL_0004:  ldc.i4     0x773593ed
-    IL_0009:  mul
-    IL_000a:  stloc.0
-    IL_000b:  ldloc.0
-    IL_000c:  ldc.i4     0x773593ed
-    IL_0011:  mul
-    IL_0012:  stloc.0
-    IL_0013:  ldloc.0
-    IL_0014:  ldc.i4     0x773593ed
-    IL_0019:  mul
-    IL_001a:  stloc.0
-    IL_001b:  ldloc.0
-    IL_001c:  stloc.1
-    IL_001d:  br.s       IL_001f
-
-    IL_001f:  ldloc.1
-    IL_0020:  ret
-  } // end of method Program::'<<Main>$>g__B|0_1'
-
-} // end of class Program
-
-
-// =============================================================
-
-// *********** DISASSEMBLY COMPLETE ***********************

From 5caa51e022723a25d900c55d57e8b9ef712511b8 Mon Sep 17 00:00:00 2001
From: Jakob Botsch Nielsen <janielsen@microsoft.com>
Date: Thu, 26 Oct 2023 17:30:33 +0000
Subject: [PATCH 045/203] Merged PR 34773: Get MinOpts and OSR working with JIT
 state machines

---
 src/coreclr/inc/corinfo.h                     |   1 +
 src/coreclr/inc/jithelpers.h                  |   1 +
 src/coreclr/inc/patchpointinfo.h              |  13 +
 src/coreclr/jit/async.cpp                     | 359 +++++++++++++-----
 src/coreclr/jit/async.h                       |   9 +-
 src/coreclr/jit/block.h                       |  10 +
 src/coreclr/jit/compiler.h                    |   2 +-
 src/coreclr/jit/fgbasic.cpp                   |   2 +
 src/coreclr/jit/gentree.cpp                   |   6 +-
 src/coreclr/jit/gentree.h                     |   2 +-
 src/coreclr/jit/importercalls.cpp             |  19 +-
 src/coreclr/jit/lsrabuild.cpp                 |   4 +-
 src/coreclr/jit/morph.cpp                     |   2 +-
 src/coreclr/vm/codeversion.cpp                |  21 +-
 src/coreclr/vm/codeversion.h                  |   4 +-
 src/coreclr/vm/jithelpers.cpp                 | 143 +++++++
 src/coreclr/vm/jitinterface.cpp               |  38 +-
 src/coreclr/vm/jitinterface.h                 |   4 +-
 src/coreclr/vm/method.hpp                     |   1 +
 src/coreclr/vm/prestub.cpp                    |   6 +
 .../CompilerServices/RuntimeHelpers.cs        |   2 +
 21 files changed, 530 insertions(+), 119 deletions(-)

diff --git a/src/coreclr/inc/corinfo.h b/src/coreclr/inc/corinfo.h
index 610fd5112a4f1c..5001e2038070e7 100644
--- a/src/coreclr/inc/corinfo.h
+++ b/src/coreclr/inc/corinfo.h
@@ -663,6 +663,7 @@ enum CorInfoHelpFunc
     CORINFO_HELP_DISPATCH_INDIRECT_CALL,    // CFG: Validate and dispatch to pointer
 
     CORINFO_HELP_ALLOC_CONTINUATION,
+    CORINFO_HELP_RESUME_OSR,
 
     CORINFO_HELP_COUNT,
 };
diff --git a/src/coreclr/inc/jithelpers.h b/src/coreclr/inc/jithelpers.h
index 698938dafbc10a..264a1a8f29fc65 100644
--- a/src/coreclr/inc/jithelpers.h
+++ b/src/coreclr/inc/jithelpers.h
@@ -360,6 +360,7 @@
 #endif
 
     DYNAMICJITHELPER(CORINFO_HELP_ALLOC_CONTINUATION, NULL, CORINFO_HELP_SIG_REG_ONLY)
+    JITHELPER(CORINFO_HELP_RESUME_OSR, JIT_ResumeOSR, CORINFO_HELP_SIG_REG_ONLY)
 
 #undef JITHELPER
 #undef DYNAMICJITHELPER
diff --git a/src/coreclr/inc/patchpointinfo.h b/src/coreclr/inc/patchpointinfo.h
index 02b9fd89f338ae..859044ecd144ef 100644
--- a/src/coreclr/inc/patchpointinfo.h
+++ b/src/coreclr/inc/patchpointinfo.h
@@ -38,6 +38,7 @@ struct PatchpointInfo
     void Initialize(unsigned localCount, int totalFrameSize)
     {
         m_calleeSaveRegisters     = 0;
+        m_tier0Version            = NULL;
         m_totalFrameSize          = totalFrameSize;
         m_numberOfLocals          = localCount;
         m_genericContextArgOffset = -1;
@@ -50,6 +51,7 @@ struct PatchpointInfo
     void Copy(const PatchpointInfo* original)
     {
         m_calleeSaveRegisters = original->m_calleeSaveRegisters;
+        m_tier0Version = original->m_tier0Version;
         m_genericContextArgOffset = original->m_genericContextArgOffset;
         m_keptAliveThisOffset = original->m_keptAliveThisOffset;
         m_securityCookieOffset = original->m_securityCookieOffset;
@@ -173,6 +175,16 @@ struct PatchpointInfo
         m_calleeSaveRegisters = registerMask;
     }
 
+    PCODE GetTier0EntryPoint() const
+    {
+        return m_tier0Version;
+    }
+
+    void SetTier0EntryPoint(PCODE ip)
+    {
+        m_tier0Version = ip;
+    }
+
 private:
     enum
     {
@@ -181,6 +193,7 @@ struct PatchpointInfo
     };
 
     uint64_t m_calleeSaveRegisters;
+    PCODE    m_tier0Version;
     unsigned m_numberOfLocals;
     int      m_totalFrameSize;
     int      m_genericContextArgOffset;
diff --git a/src/coreclr/jit/async.cpp b/src/coreclr/jit/async.cpp
index 26900f33a034e0..cb92fc3b12331a 100644
--- a/src/coreclr/jit/async.cpp
+++ b/src/coreclr/jit/async.cpp
@@ -10,6 +10,162 @@ PhaseStatus Compiler::TransformAsync2()
     return transformation.Run();
 }
 
+class AsyncLiveness
+{
+    Compiler*              m_comp;
+    bool                   m_hasLiveness;
+    TreeLifeUpdater<false> m_updater;
+    unsigned               m_numVars;
+
+public:
+    AsyncLiveness(Compiler* comp, bool hasLiveness)
+        : m_comp(comp), m_hasLiveness(hasLiveness), m_updater(comp), m_numVars(comp->lvaCount)
+    {
+    }
+
+    void StartBlock(BasicBlock* block)
+    {
+        if (!m_hasLiveness)
+            return;
+
+        VarSetOps::Assign(m_comp, m_comp->compCurLife, block->bbLiveIn);
+    }
+
+    void Update(GenTree* node)
+    {
+        if (!m_hasLiveness)
+            return;
+
+        m_updater.UpdateLife(node);
+    }
+
+    bool IsLocalCaptureUnnecessary(unsigned lclNum)
+    {
+#if FEATURE_FIXED_OUT_ARGS
+        if (lclNum == m_comp->lvaOutgoingArgSpaceVar)
+        {
+            return true;
+        }
+#endif
+
+        if (lclNum == m_comp->lvaGSSecurityCookie)
+        {
+            // Initialized in prolog
+            return true;
+        }
+
+#ifdef FEATURE_EH_FUNCLETS
+        if (lclNum == m_comp->lvaPSPSym)
+        {
+            // Initialized in prolog
+            return true;
+        }
+#else
+        if (lclNum == m_comp->lvaShadowSPslotsVar)
+        {
+            // Only expected to be live in handlers
+            return true;
+        }
+#endif
+
+        if (lclNum == m_comp->lvaRetAddrVar)
+        {
+            return true;
+        }
+
+        if (lclNum == m_comp->lvaAsyncContinuationArg)
+        {
+            return true;
+        }
+
+        return false;
+    }
+
+    bool IsLive(unsigned lclNum)
+    {
+        if (IsLocalCaptureUnnecessary(lclNum))
+        {
+            return false;
+        }
+
+        LclVarDsc* dsc = m_comp->lvaGetDesc(lclNum);
+
+        if ((dsc->TypeGet() == TYP_BYREF) || ((dsc->TypeGet() == TYP_STRUCT) && dsc->GetLayout()->HasGCByRef()))
+        {
+            // Even if these are address exposed we expect them to be dead at
+            // suspension points. TODO: It would be good to somehow verify these
+            // aren't obviously live, if the JIT creates live ranges that span a
+            // suspension point then this makes it quite hard to diagnose that.
+            return false;
+        }
+
+        if (!m_hasLiveness)
+        {
+            return true;
+        }
+
+        if (dsc->lvRefCnt(RCS_NORMAL) == 0)
+        {
+            return false;
+        }
+
+        Compiler::lvaPromotionType promoType = m_comp->lvaGetPromotionType(dsc);
+        if (promoType == Compiler::PROMOTION_TYPE_INDEPENDENT)
+        {
+            // Independently promoted structs are handled only through their
+            // fields.
+            return false;
+        }
+
+        if (promoType == Compiler::PROMOTION_TYPE_DEPENDENT)
+        {
+            // Dependently promoted structs are handled only through the base
+            // struct local.
+            //
+            // A dependently promoted struct is live either if it has significant
+            // padding (since we do not track liveness for the significant
+            // padding), or if all its fields are dead.
+
+            if (dsc->lvAnySignificantPadding)
+            {
+                // We could technically only save the padding and the live fields
+                // in this case, but that's a lot of complexity for not a lot of
+                // gain.
+                return true;
+            }
+
+            for (unsigned i = 0; i < dsc->lvFieldCnt; i++)
+            {
+                LclVarDsc* fieldDsc = m_comp->lvaGetDesc(dsc->lvFieldLclStart + i);
+                if (!fieldDsc->lvTracked || VarSetOps::IsMember(m_comp, m_comp->compCurLife, fieldDsc->lvVarIndex))
+                {
+                    return true;
+                }
+            }
+
+            return false;
+        }
+
+        if (dsc->lvIsStructField && (m_comp->lvaGetParentPromotionType(dsc) == Compiler::PROMOTION_TYPE_DEPENDENT))
+        {
+            return false;
+        }
+
+        return !dsc->lvTracked || VarSetOps::IsMember(m_comp, m_comp->compCurLife, dsc->lvVarIndex);
+    }
+
+    void GetLiveLocals(jitstd::vector<Async2Transformation::LiveLocalInfo>& liveLocals)
+    {
+        for (unsigned lclNum = 0; lclNum < m_numVars; lclNum++)
+        {
+            if (IsLive(lclNum))
+            {
+                liveLocals.push_back(Async2Transformation::LiveLocalInfo(lclNum));
+            }
+        }
+    }
+};
+
 PhaseStatus Async2Transformation::Run()
 {
     ArrayStack<BasicBlock*> worklist(m_comp->getAllocator(CMK_Async2));
@@ -46,14 +202,15 @@ PhaseStatus Async2Transformation::Run()
     m_objectClsHnd = m_comp->info.compCompHnd->getBuiltinClass(CLASSID_SYSTEM_OBJECT);
     m_byteClsHnd   = m_comp->info.compCompHnd->getBuiltinClass(CLASSID_SYSTEM_BYTE);
 
-    m_comp->lvaComputeRefCounts(true, false);
-
-    m_comp->fgLocalVarLiveness();
-    m_livenessLvaCount = m_comp->lvaCount;
+    if (m_comp->opts.OptimizationEnabled())
+    {
+        m_comp->lvaComputeRefCounts(true, false);
+        m_comp->fgLocalVarLiveness();
+        VarSetOps::AssignNoCopy(m_comp, m_comp->compCurLife, VarSetOps::MakeEmpty(m_comp));
+    }
 
-    VarSetOps::AssignNoCopy(m_comp, m_comp->compCurLife, VarSetOps::MakeEmpty(m_comp));
+    AsyncLiveness liveness(m_comp, m_comp->opts.OptimizationEnabled());
 
-    TreeLifeUpdater<false>   life(m_comp);
     jitstd::vector<GenTree*> defs(m_comp->getAllocator(CMK_Async2));
 
     for (int i = 0; i < worklist.Height(); i++)
@@ -61,7 +218,7 @@ PhaseStatus Async2Transformation::Run()
         assert(defs.size() == 0);
 
         BasicBlock* block = worklist.Bottom(i);
-        VarSetOps::Assign(m_comp, m_comp->compCurLife, block->bbLiveIn);
+        liveness.StartBlock(block);
 
         bool any;
         do
@@ -86,11 +243,11 @@ PhaseStatus Async2Transformation::Run()
                     return GenTree::VisitResult::Continue;
                 });
 
-                life.UpdateLife(tree);
+                liveness.Update(tree);
 
                 if (tree->IsCall() && tree->AsCall()->IsAsync2() && !tree->AsCall()->IsTailCall())
                 {
-                    Transform(block, tree->AsCall(), defs, life, &block);
+                    Transform(block, tree->AsCall(), defs, liveness, &block);
                     defs.clear();
                     any = true;
                     break;
@@ -109,11 +266,8 @@ PhaseStatus Async2Transformation::Run()
     return PhaseStatus::MODIFIED_EVERYTHING;
 }
 
-void Async2Transformation::Transform(BasicBlock*               block,
-                                     GenTreeCall*              call,
-                                     jitstd::vector<GenTree*>& defs,
-                                     TreeLifeUpdater<false>&   life,
-                                     BasicBlock**              pRemainder)
+void Async2Transformation::Transform(
+    BasicBlock* block, GenTreeCall* call, jitstd::vector<GenTree*>& defs, AsyncLiveness& life, BasicBlock** pRemainder)
 {
 #ifdef DEBUG
     if (m_comp->verbose)
@@ -137,16 +291,7 @@ void Async2Transformation::Transform(BasicBlock*               block,
 
     m_liveLocals.clear();
 
-    for (unsigned lclNum = 0; lclNum < m_livenessLvaCount; lclNum++)
-    {
-        if (!IsLive(lclNum))
-        {
-            continue;
-        }
-
-        m_liveLocals.push_back(LiveLocalInfo(lclNum));
-    }
-
+    life.GetLiveLocals(m_liveLocals);
     LiftLIREdges(block, call, defs, m_liveLocals);
 
 #ifdef DEBUG
@@ -225,11 +370,19 @@ void Async2Transformation::Transform(BasicBlock*               block,
         return lhs.Alignment > rhs.Alignment;
     });
 
-    unsigned dataSize = 0;
+    unsigned dataSize    = 0;
+    unsigned gcRefsCount = 0;
+
     // Result value is currently always stored boxed in the continuation as it
     // needs to be written by the resumption stub and by the asynchronous
     // callbacks. If we specialized the continuation type we could avoid this.
-    unsigned gcRefsCount = call->gtReturnType != TYP_VOID ? 1 : 0;
+    if (call->gtReturnType != TYP_VOID)
+        gcRefsCount++;
+
+    // For OSR, we store the patchpoint information at the beginning of the data
+    // (null in the tier0 version):
+    if (m_comp->doesMethodHavePatchpoints() || m_comp->opts.IsOSR())
+        dataSize += sizeof(int);
 
     for (LiveLocalInfo& inf : m_liveLocals)
     {
@@ -274,7 +427,7 @@ void Async2Transformation::Transform(BasicBlock*               block,
         else
         {
             // We will split after the store, but we still have to update liveness for it.
-            life.UpdateLife(call->gtNext);
+            life.Update(call->gtNext);
         }
 
         assert(call->gtNext->OperIsLocalStore() && (call->gtNext->Data() == call));
@@ -285,7 +438,7 @@ void Async2Transformation::Transform(BasicBlock*               block,
     GenTree* continuationArg = new (m_comp, GT_ASYNC_CONTINUATION) GenTree(GT_ASYNC_CONTINUATION, TYP_REF);
     continuationArg->SetHasOrderingSideEffect();
 
-    GenTree* storeContinuation   = m_comp->gtNewStoreLclVarNode(m_returnedContinuationVar, continuationArg);
+    GenTree* storeContinuation = m_comp->gtNewStoreLclVarNode(m_returnedContinuationVar, continuationArg);
     LIR::AsRange(block).InsertAfter(insertAfter, continuationArg, storeContinuation);
 
     GenTree* null                 = m_comp->gtNewNull();
@@ -440,6 +593,20 @@ void Async2Transformation::Transform(BasicBlock*               block,
         GenTree* storeAllocedByteArr = m_comp->gtNewStoreLclVarNode(byteArrLclNum, dataInd);
         LIR::AsRange(retBB).InsertAtEnd(newContinuation, dataOffsetNode, dataAddr, dataInd, storeAllocedByteArr);
 
+        if (m_comp->doesMethodHavePatchpoints() || m_comp->opts.IsOSR())
+        {
+            GenTree* ilOffsetToStore;
+            if (m_comp->doesMethodHavePatchpoints())
+                ilOffsetToStore = m_comp->gtNewIconNode(-1);
+            else
+                ilOffsetToStore = m_comp->gtNewIconNode((int)m_comp->info.compILEntry);
+
+            GenTree* byteArr               = m_comp->gtNewLclvNode(byteArrLclNum, TYP_REF);
+            unsigned offset                = OFFSETOF__CORINFO_Array__data;
+            GenTree* storePatchpointOffset = StoreAtOffset(byteArr, offset, ilOffsetToStore);
+            LIR::AsRange(retBB).InsertAtEnd(LIR::SeqTree(m_comp, storePatchpointOffset));
+        }
+
         // Fill in data
         for (LiveLocalInfo& inf : m_liveLocals)
         {
@@ -740,75 +907,6 @@ GenTreeStoreInd* Async2Transformation::StoreAtOffset(GenTree* base, unsigned off
     return store;
 }
 
-bool Async2Transformation::IsLive(unsigned lclNum)
-{
-#if FEATURE_FIXED_OUT_ARGS
-    if (lclNum == m_comp->lvaOutgoingArgSpaceVar)
-    {
-        return false;
-    }
-#endif
-
-    LclVarDsc* dsc = m_comp->lvaGetDesc(lclNum);
-    if (dsc->lvRefCnt(RCS_NORMAL) == 0)
-    {
-        return false;
-    }
-
-    if ((dsc->TypeGet() == TYP_BYREF) || ((dsc->TypeGet() == TYP_STRUCT) && dsc->GetLayout()->HasGCByRef()))
-    {
-        // Even if these are address exposed we expect them to be dead at
-        // suspension points. TODO: It would be good to somehow verify these
-        // aren't obviously live, if the JIT creates live ranges that span a
-        // suspension point then this makes it quite hard to diagnose that.
-        return false;
-    }
-
-    Compiler::lvaPromotionType promoType = m_comp->lvaGetPromotionType(dsc);
-    if (promoType == Compiler::PROMOTION_TYPE_INDEPENDENT)
-    {
-        // Independently promoted structs are handled only through their
-        // fields.
-        return false;
-    }
-
-    if (promoType == Compiler::PROMOTION_TYPE_DEPENDENT)
-    {
-        // Dependently promoted structs are handled only through the base
-        // struct local.
-        //
-        // A dependently promoted struct is live either if it has significant
-        // padding (since we do not track liveness for the significant
-        // padding), or if all its fields are dead.
-
-        if (dsc->lvAnySignificantPadding)
-        {
-            // We could technically only save the padding and the live fields
-            // in this case, but that's a lot of complexity for not a lot of
-            // gain.
-            return true;
-        }
-
-        for (unsigned i = 0; i < dsc->lvFieldCnt; i++)
-        {
-            LclVarDsc* fieldDsc = m_comp->lvaGetDesc(dsc->lvFieldLclStart + i);
-            if (!fieldDsc->lvTracked || VarSetOps::IsMember(m_comp, m_comp->compCurLife, fieldDsc->lvVarIndex))
-            {
-                return true;
-            }
-        }
-
-        return false;
-    }
-
-    if (dsc->lvIsStructField && (m_comp->lvaGetParentPromotionType(dsc) == Compiler::PROMOTION_TYPE_DEPENDENT))
-    {
-        return false;
-    }
-
-    return !dsc->lvTracked || VarSetOps::IsMember(m_comp, m_comp->compCurLife, dsc->lvVarIndex);
-}
-
 unsigned Async2Transformation::GetDataArrayVar()
 {
     if (m_dataArrayVar == BAD_VAR_NUM)
@@ -1014,4 +1112,71 @@ void Async2Transformation::CreateResumptionSwitch()
             m_comp->fgAddRefPred(bb, switchBB);
         }
     }
+
+    if (m_comp->doesMethodHavePatchpoints())
+    {
+        // If we have patchpoints then first check if we need to resume in the OSR version.
+        BasicBlock* callHelperBB = m_comp->fgNewBBafter(BBJ_THROW, m_comp->fgLastBBInMainFunction(), true);
+
+        BasicBlock* checkILOffsetBB = m_comp->fgNewBBbefore(BBJ_COND, newEntryBB->GetJumpDest(), true, callHelperBB);
+        m_comp->fgRemoveRefPred(newEntryBB->GetJumpDest(), newEntryBB);
+        newEntryBB->SetJumpDest(checkILOffsetBB);
+
+        m_comp->fgAddRefPred(checkILOffsetBB, newEntryBB);
+        m_comp->fgAddRefPred(checkILOffsetBB->Next(), checkILOffsetBB);
+        m_comp->fgAddRefPred(checkILOffsetBB->GetJumpDest(), checkILOffsetBB);
+
+        // We need to dispatch to the OSR version if the IL offset is non-negative.
+        continuationArg = m_comp->gtNewLclvNode(m_comp->lvaAsyncContinuationArg, TYP_REF);
+        unsigned offsetOfData =
+            TARGET_POINTER_SIZE + m_comp->info.compCompHnd->getFieldOffset(m_async2Info.continuationDataFldHnd);
+        GenTree* dataArr          = LoadFromOffset(continuationArg, offsetOfData, TYP_REF);
+        unsigned offsetOfIlOffset = OFFSETOF__CORINFO_Array__data;
+        GenTree* ilOffset         = LoadFromOffset(dataArr, offsetOfIlOffset, TYP_INT);
+        unsigned ilOffsetLclNum   = m_comp->lvaGrabTemp(false DEBUGARG("IL offset for tier0 OSR method"));
+        m_comp->lvaGetDesc(ilOffsetLclNum)->lvType = TYP_INT;
+        GenTree* storeIlOffset                     = m_comp->gtNewStoreLclVarNode(ilOffsetLclNum, ilOffset);
+        LIR::AsRange(checkILOffsetBB).InsertAtEnd(LIR::SeqTree(m_comp, storeIlOffset));
+
+        ilOffset        = m_comp->gtNewLclvNode(ilOffsetLclNum, TYP_INT);
+        GenTree* zero   = m_comp->gtNewIconNode(0);
+        GenTree* geZero = m_comp->gtNewOperNode(GT_GE, TYP_INT, ilOffset, zero);
+        GenTree* jtrue  = m_comp->gtNewOperNode(GT_JTRUE, TYP_VOID, geZero);
+        LIR::AsRange(checkILOffsetBB).InsertAtEnd(ilOffset, zero, geZero, jtrue);
+
+        ilOffset                = m_comp->gtNewLclvNode(ilOffsetLclNum, TYP_INT);
+        GenTreeCall* callHelper = m_comp->gtNewHelperCallNode(CORINFO_HELP_RESUME_OSR, TYP_VOID, ilOffset);
+        callHelper->gtCallMoreFlags |= GTF_CALL_M_DOES_NOT_RETURN;
+
+        m_comp->compCurBB = callHelperBB;
+        m_comp->fgMorphTree(callHelper);
+
+        LIR::AsRange(callHelperBB).InsertAtEnd(LIR::SeqTree(m_comp, callHelper));
+    }
+    else if (m_comp->opts.IsOSR())
+    {
+        // If the tier-0 version resumed and then transitioned to the OSR
+        // version by normal means then we will see a non-zero continuation
+        // here that belongs to the tier0 method. In that case we should just
+        // ignore it, so create a BB that jumps back.
+        BasicBlock* checkILOffsetBB =
+            m_comp->fgNewBBbefore(BBJ_COND, newEntryBB->GetJumpDest(), true, newEntryBB->Next());
+        m_comp->fgRemoveRefPred(newEntryBB->GetJumpDest(), newEntryBB);
+        newEntryBB->SetJumpDest(checkILOffsetBB);
+
+        m_comp->fgAddRefPred(checkILOffsetBB, newEntryBB);
+        m_comp->fgAddRefPred(checkILOffsetBB->Next(), checkILOffsetBB);
+        m_comp->fgAddRefPred(checkILOffsetBB->GetJumpDest(), checkILOffsetBB);
+
+        continuationArg = m_comp->gtNewLclvNode(m_comp->lvaAsyncContinuationArg, TYP_REF);
+        unsigned offsetOfData =
+            TARGET_POINTER_SIZE + m_comp->info.compCompHnd->getFieldOffset(m_async2Info.continuationDataFldHnd);
+        GenTree* dataArr          = LoadFromOffset(continuationArg, offsetOfData, TYP_REF);
+        unsigned offsetOfIlOffset = OFFSETOF__CORINFO_Array__data;
+        GenTree* ilOffset         = LoadFromOffset(dataArr, offsetOfIlOffset, TYP_INT);
+        GenTree* zero             = m_comp->gtNewIconNode(0);
+        GenTree* ltZero           = m_comp->gtNewOperNode(GT_LT, TYP_INT, ilOffset, zero);
+        GenTree* jtrue            = m_comp->gtNewOperNode(GT_JTRUE, TYP_VOID, ltZero);
+        LIR::AsRange(checkILOffsetBB).InsertAtEnd(LIR::SeqTree(m_comp, jtrue));
+    }
 }
diff --git a/src/coreclr/jit/async.h b/src/coreclr/jit/async.h
index 37e79563511efe..6a584fd944b1cb 100644
--- a/src/coreclr/jit/async.h
+++ b/src/coreclr/jit/async.h
@@ -1,5 +1,7 @@
 class Async2Transformation
 {
+    friend class AsyncLiveness;
+
     struct LiveLocalInfo
     {
         unsigned LclNum;
@@ -15,7 +17,6 @@ class Async2Transformation
     };
 
     Compiler*                     m_comp;
-    unsigned                      m_livenessLvaCount = 0;
     jitstd::vector<LiveLocalInfo> m_liveLocals;
     CORINFO_ASYNC2_INFO           m_async2Info;
     CORINFO_CLASS_HANDLE          m_objectClsHnd;
@@ -35,7 +36,11 @@ class Async2Transformation
                       jitstd::vector<GenTree*>&      defs,
                       jitstd::vector<LiveLocalInfo>& liveLocals);
     bool IsLive(unsigned lclNum);
-    void Transform(BasicBlock* block, GenTreeCall* call, jitstd::vector<GenTree*>& defs, TreeLifeUpdater<false>& life, BasicBlock** remainder);
+    void Transform(BasicBlock*               block,
+                   GenTreeCall*              call,
+                   jitstd::vector<GenTree*>& defs,
+                   class AsyncLiveness&      life,
+                   BasicBlock**              remainder);
 
     GenTreeIndir* LoadFromOffset(GenTree* base, unsigned offset, var_types type);
     GenTreeStoreInd* StoreAtOffset(GenTree* base, unsigned offset, GenTree* value);
diff --git a/src/coreclr/jit/block.h b/src/coreclr/jit/block.h
index 6926bef3b6fa1e..3b22642e0363de 100644
--- a/src/coreclr/jit/block.h
+++ b/src/coreclr/jit/block.h
@@ -981,6 +981,16 @@ struct BasicBlock : private LIR::Range
         m_firstNode = tree;
     }
 
+    GenTree* GetLastLIRNode() const
+    {
+        return m_lastNode;
+    }
+
+    void SetLastLIRNode(GenTree* tree)
+    {
+        m_lastNode = tree;
+    }
+
     EntryState* bbEntryState; // verifier tracked state of all entries in stack.
 
 #define NO_BASE_TMP UINT_MAX // base# to use when we have none
diff --git a/src/coreclr/jit/compiler.h b/src/coreclr/jit/compiler.h
index da002eb9006a7b..fb6d2fe11f411d 100644
--- a/src/coreclr/jit/compiler.h
+++ b/src/coreclr/jit/compiler.h
@@ -4930,7 +4930,7 @@ class Compiler
 
     bool backendRequiresLocalVarLifetimes()
     {
-        return !opts.MinOpts() || (m_pLinearScan != NULL && m_pLinearScan->willEnregisterLocalVars());
+        return !opts.MinOpts() || m_pLinearScan->willEnregisterLocalVars();
     }
 
     void fgLocalVarLiveness();
diff --git a/src/coreclr/jit/fgbasic.cpp b/src/coreclr/jit/fgbasic.cpp
index d6ba319a9631ab..161bc1ee7ef184 100644
--- a/src/coreclr/jit/fgbasic.cpp
+++ b/src/coreclr/jit/fgbasic.cpp
@@ -4968,7 +4968,9 @@ BasicBlock* Compiler::fgSplitBlockAtBeginning(BasicBlock* curr)
     if (curr->IsLIR())
     {
         newBlock->SetFirstLIRNode(curr->GetFirstLIRNode());
+        newBlock->SetLastLIRNode(curr->GetLastLIRNode());
         curr->SetFirstLIRNode(nullptr);
+        curr->SetLastLIRNode(nullptr);
     }
     else
     {
diff --git a/src/coreclr/jit/gentree.cpp b/src/coreclr/jit/gentree.cpp
index cd5ed56638a563..6245645c748a6f 100644
--- a/src/coreclr/jit/gentree.cpp
+++ b/src/coreclr/jit/gentree.cpp
@@ -8096,7 +8096,7 @@ GenTreeCall* Compiler::gtNewCallNode(gtCallTypes           callType,
     node->gtRetClsHnd       = nullptr;
     node->gtControlExpr     = nullptr;
     node->gtCallMoreFlags   = GTF_CALL_M_EMPTY;
-    node->gtIsAsyncCall = false;
+    node->gtIsAsyncCall     = false;
     node->gtInlineInfoCount = 0;
 
     if (callType == CT_INDIRECT)
@@ -9757,8 +9757,8 @@ GenTreeCall* Compiler::gtCloneExprCallHelper(GenTreeCall* tree,
     }
 
     copy->gtIsAsyncCall = tree->gtIsAsyncCall;
-    copy->gtCallType   = tree->gtCallType;
-    copy->gtReturnType = tree->gtReturnType;
+    copy->gtCallType    = tree->gtCallType;
+    copy->gtReturnType  = tree->gtReturnType;
 
 #if FEATURE_MULTIREG_RET
     copy->gtReturnTypeDesc = tree->gtReturnTypeDesc;
diff --git a/src/coreclr/jit/gentree.h b/src/coreclr/jit/gentree.h
index d0090619e4a2a6..240acd1e7e9f2a 100644
--- a/src/coreclr/jit/gentree.h
+++ b/src/coreclr/jit/gentree.h
@@ -5582,7 +5582,7 @@ struct GenTreeCall final : public GenTree
     var_types        gtReturnType : 5; // exact return type
 
     uint8_t gtInlineInfoCount; // number of inline candidates for the given call
-    bool gtIsAsyncCall;
+    bool    gtIsAsyncCall;
 
     CORINFO_CLASS_HANDLE gtRetClsHnd; // The return type handle of the call if it is a struct; always available
     union {
diff --git a/src/coreclr/jit/importercalls.cpp b/src/coreclr/jit/importercalls.cpp
index 0fa5fc679fa56b..ab7b37449de2c5 100644
--- a/src/coreclr/jit/importercalls.cpp
+++ b/src/coreclr/jit/importercalls.cpp
@@ -519,7 +519,7 @@ var_types Compiler::impImportCall(OPCODE                  opcode,
             call->AsCall()->gtCallMoreFlags |= GTF_CALL_M_SPECIAL_INTRINSIC;
         }
 
-        // Temporary hack since these functions have to be recognizes as async2
+        // Temporary hack since these functions have to be recognized as async2
         // calls in JIT generated state machines only.
         if (compIsAsync2StateMachine() &&
             ((ni == NI_System_Runtime_CompilerServices_RuntimeHelpers_AwaitAwaiterFromRuntimeAsync) ||
@@ -2597,6 +2597,17 @@ GenTree* Compiler::impIntrinsic(GenTree*                newobjThis,
         return gtNewIconNode(JitConfig.RuntimeAsyncViaJitGeneratedStateMachines() != 0 ? 1 : 0);
     }
 
+    if ((ni == NI_System_Runtime_CompilerServices_RuntimeHelpers_AwaitAwaiterFromRuntimeAsync) ||
+        (ni == NI_System_Runtime_CompilerServices_RuntimeHelpers_UnsafeAwaitAwaiterFromRuntimeAsync))
+    {
+        // These are marked intrinsics simply to mark the call node as async,
+        // which the caller will do. Make sure we keep pIntrinsicName assigned
+        // (it would be overridden if we left this up to the rest of this
+        // function).
+        *pIntrinsicName = ni;
+        return nullptr;
+    }
+
     bool betterToExpand = false;
 
     // Allow some lighweight intrinsics in Tier0 which can improve throughput
@@ -9024,7 +9035,8 @@ NamedIntrinsic Compiler::lookupNamedIntrinsic(CORINFO_METHOD_HANDLE method)
                         }
                         else if (strcmp(methodName, "UnsafeAwaitAwaiterFromRuntimeAsync") == 0)
                         {
-                            result = NI_System_Runtime_CompilerServices_RuntimeHelpers_UnsafeAwaitAwaiterFromRuntimeAsync;
+                            result =
+                                NI_System_Runtime_CompilerServices_RuntimeHelpers_UnsafeAwaitAwaiterFromRuntimeAsync;
                         }
                         else if (strcmp(methodName, "SuspendAsync2") == 0)
                         {
@@ -9032,7 +9044,8 @@ NamedIntrinsic Compiler::lookupNamedIntrinsic(CORINFO_METHOD_HANDLE method)
                         }
                         else if (strcmp(methodName, "get_RuntimeAsyncViaJitGeneratedStateMachines") == 0)
                         {
-                            result = NI_System_Runtime_CompilerServices_RuntimeHelpers_get_RuntimeAsyncViaJitGeneratedStateMachines;
+                            result =
+                                NI_System_Runtime_CompilerServices_RuntimeHelpers_get_RuntimeAsyncViaJitGeneratedStateMachines;
                         }
                     }
                     else if (strcmp(className, "Unsafe") == 0)
diff --git a/src/coreclr/jit/lsrabuild.cpp b/src/coreclr/jit/lsrabuild.cpp
index fee2b0ed4f4a64..1be72493fce185 100644
--- a/src/coreclr/jit/lsrabuild.cpp
+++ b/src/coreclr/jit/lsrabuild.cpp
@@ -1246,8 +1246,8 @@ bool LinearScan::buildKillPositionsForNode(GenTree* tree, LsraLocation currentLo
 
         assert((killMask & RBM_ASYNC_CONTINUATION_RET) != 0);
 
-        RegRecord* asyncRetRegRec = getRegisterRecord(REG_ASYNC_CONTINUATION_RET);
-        RefPosition* pos = asyncRetRegRec->lastRefPosition;
+        RegRecord*   asyncRetRegRec = getRegisterRecord(REG_ASYNC_CONTINUATION_RET);
+        RefPosition* pos            = asyncRetRegRec->lastRefPosition;
         assert(pos->refType == RefTypeKill);
         pos->busyUntilNextKill = true;
     }
diff --git a/src/coreclr/jit/morph.cpp b/src/coreclr/jit/morph.cpp
index 9a18213f91243c..b74257a6f61255 100644
--- a/src/coreclr/jit/morph.cpp
+++ b/src/coreclr/jit/morph.cpp
@@ -193,7 +193,7 @@ GenTree* Compiler::fgMorphIntoHelperCall(GenTree* tree, int helper, bool morphAr
     call->gtRetClsHnd     = nullptr;
     call->gtCallMoreFlags = GTF_CALL_M_EMPTY;
     call->gtControlExpr   = nullptr;
-    call->gtIsAsyncCall = false;
+    call->gtIsAsyncCall   = false;
     call->ClearInlineInfo();
 #ifdef UNIX_X86_ABI
     call->gtFlags |= GTF_CALL_POP_ARGS;
diff --git a/src/coreclr/vm/codeversion.cpp b/src/coreclr/vm/codeversion.cpp
index bc7937ad37263d..dbd928f668aa84 100644
--- a/src/coreclr/vm/codeversion.cpp
+++ b/src/coreclr/vm/codeversion.cpp
@@ -85,6 +85,12 @@ PCODE NativeCodeVersionNode::GetNativeCode() const
     return m_pNativeCode;
 }
 
+PTR_PCODE NativeCodeVersionNode::GetNativeCodeSlot() const
+{
+    LIMITED_METHOD_DAC_CONTRACT;
+    return dac_cast<PTR_PCODE>(&m_pNativeCode);
+}
+
 ReJITID NativeCodeVersionNode::GetILVersionId() const
 {
     LIMITED_METHOD_DAC_CONTRACT;
@@ -227,6 +233,19 @@ PCODE NativeCodeVersion::GetNativeCode() const
     }
 }
 
+PTR_PCODE NativeCodeVersion::GetNativeCodeSlot() const
+{
+    LIMITED_METHOD_DAC_CONTRACT;
+    if (m_storageKind == StorageKind::Explicit)
+    {
+        return AsNode()->GetNativeCodeSlot();
+    }
+    else
+    {
+        return GetMethodDesc()->GetAddrOfSlot();
+    }
+}
+
 ReJITID NativeCodeVersion::GetILCodeVersionId() const
 {
     LIMITED_METHOD_DAC_CONTRACT;
@@ -368,7 +387,7 @@ void NativeCodeVersion::SetOptimizationTier(OptimizationTier tier)
 
 #ifdef FEATURE_ON_STACK_REPLACEMENT
 
-PatchpointInfo * NativeCodeVersion::GetOSRInfo(unsigned * ilOffset)
+PatchpointInfo * NativeCodeVersion::GetOSRInfo(unsigned * ilOffset) const
 {
     LIMITED_METHOD_DAC_CONTRACT;
     if (m_storageKind == StorageKind::Explicit)
diff --git a/src/coreclr/vm/codeversion.h b/src/coreclr/vm/codeversion.h
index 7b536fb071a62d..ef9c291c64f451 100644
--- a/src/coreclr/vm/codeversion.h
+++ b/src/coreclr/vm/codeversion.h
@@ -61,6 +61,7 @@ class NativeCodeVersion
     NativeCodeVersionId GetVersionId() const;
     BOOL IsDefaultVersion() const;
     PCODE GetNativeCode() const;
+    PTR_PCODE GetNativeCodeSlot() const;
 
 #ifdef FEATURE_CODE_VERSIONING
     ILCodeVersion GetILCodeVersion() const;
@@ -90,7 +91,7 @@ class NativeCodeVersion
 #endif // FEATURE_TIERED_COMPILATION
 
 #ifdef FEATURE_ON_STACK_REPLACEMENT
-    PatchpointInfo * GetOSRInfo(unsigned * iloffset);
+    PatchpointInfo * GetOSRInfo(unsigned * iloffset) const;
 #endif // FEATURE_ON_STACK_REPLACEMENT
 
 #ifdef HAVE_GCCOVER
@@ -266,6 +267,7 @@ class NativeCodeVersionNode
     PTR_MethodDesc GetMethodDesc() const;
     NativeCodeVersionId GetVersionId() const;
     PCODE GetNativeCode() const;
+    PTR_PCODE GetNativeCodeSlot() const;
     ReJITID GetILVersionId() const;
     ILCodeVersion GetILCodeVersion() const;
     BOOL IsActiveChildVersion() const;
diff --git a/src/coreclr/vm/jithelpers.cpp b/src/coreclr/vm/jithelpers.cpp
index 3156e9807ec7a5..aed56aaee72d7b 100644
--- a/src/coreclr/vm/jithelpers.cpp
+++ b/src/coreclr/vm/jithelpers.cpp
@@ -51,6 +51,7 @@
 #include "onstackreplacement.h"
 #include "pgo.h"
 #include "pgo_formatprocessing.h"
+#include "patchpointinfo.h"
 
 #ifndef FEATURE_EH_FUNCLETS
 #include "excep.h"
@@ -5548,6 +5549,148 @@ void JIT_Patchpoint(int* counter, int ilOffset)
     ::SetLastError(dwLastError);
 }
 
+void JIT_ResumeOSR(unsigned ilOffset)
+{
+    STATIC_CONTRACT_GC_NOTRIGGER;
+    STATIC_CONTRACT_MODE_COOPERATIVE;
+
+    // BEGIN_PRESERVE_LAST_ERROR;
+    DWORD dwLastError = ::GetLastError();
+    PCODE ip = (PCODE)_ReturnAddress();
+
+    Thread *pThread = GetThread();
+
+#ifdef FEATURE_HIJACK
+    // We can't crawl the stack of a thread that currently has a hijack pending
+    // (since the hijack routine won't be recognized by any code manager). So we
+    // Undo any hijack, the EE will re-attempt it later.
+    pThread->UnhijackThread();
+#endif
+
+    // Find context for the original method
+    CONTEXT *pFrameContext = NULL;
+#if defined(TARGET_WINDOWS) && defined(TARGET_AMD64)
+    DWORD contextSize = 0;
+    ULONG64 xStateCompactionMask = 0;
+    DWORD contextFlags = CONTEXT_FULL;
+    if (Thread::AreCetShadowStacksEnabled())
+    {
+        xStateCompactionMask = XSTATE_MASK_CET_U;
+        contextFlags |= CONTEXT_XSTATE;
+    }
+
+    // The initialize call should fail but return contextSize
+    BOOL success = g_pfnInitializeContext2 ?
+        g_pfnInitializeContext2(NULL, contextFlags, NULL, &contextSize, xStateCompactionMask) :
+        InitializeContext(NULL, contextFlags, NULL, &contextSize);
+
+    _ASSERTE(!success && (GetLastError() == ERROR_INSUFFICIENT_BUFFER));
+
+    PVOID pBuffer = _alloca(contextSize);
+    success = g_pfnInitializeContext2 ?
+        g_pfnInitializeContext2(pBuffer, contextFlags, &pFrameContext, &contextSize, xStateCompactionMask) :
+        InitializeContext(pBuffer, contextFlags, &pFrameContext, &contextSize);
+    _ASSERTE(success);
+#else // TARGET_WINDOWS && TARGET_AMD64
+    CONTEXT frameContext;
+    frameContext.ContextFlags = CONTEXT_FULL;
+    pFrameContext = &frameContext;
+#endif // TARGET_WINDOWS && TARGET_AMD64
+
+    // Find context for the original method
+    RtlCaptureContext(pFrameContext);
+
+#if defined(TARGET_WINDOWS) && defined(TARGET_AMD64)
+    if (Thread::AreCetShadowStacksEnabled())
+    {
+        pFrameContext->ContextFlags |= CONTEXT_XSTATE;
+        SetXStateFeaturesMask(pFrameContext, xStateCompactionMask);
+        SetSSP(pFrameContext, _rdsspq());
+    }
+#endif // TARGET_WINDOWS && TARGET_AMD64
+
+    // Walk back to the original method frame
+    pThread->VirtualUnwindToFirstManagedCallFrame(pFrameContext);
+
+    // Remember original method FP and SP because new method will inherit them.
+    UINT_PTR currentSP = GetSP(pFrameContext);
+    UINT_PTR currentFP = GetFP(pFrameContext);
+
+    // Now unwind back to the original method caller frame.
+    EECodeInfo callerCodeInfo(GetIP(pFrameContext));
+    ULONG_PTR establisherFrame = 0;
+    PVOID handlerData = NULL;
+    RtlVirtualUnwind(UNW_FLAG_NHANDLER, callerCodeInfo.GetModuleBase(), GetIP(pFrameContext), callerCodeInfo.GetFunctionEntry(),
+        pFrameContext, &handlerData, &establisherFrame, NULL);
+
+    // Now, set FP and SP back to the values they had just before this helper was called,
+    // since the new method must have access to the original method frame.
+    //
+    // TODO: if we access the patchpointInfo here, we can read out the FP-SP delta from there and
+    // use that to adjust the stack, likely saving some stack space.
+
+#if defined(TARGET_AMD64)
+    // If calls push the return address, we need to simulate that here, so the OSR
+    // method sees the "expected" SP misalgnment on entry.
+    _ASSERTE(currentSP % 16 == 0);
+    currentSP -= 8;
+
+#if defined(TARGET_WINDOWS)
+    DWORD64 ssp = GetSSP(pFrameContext);
+    if (ssp != 0)
+    {
+        SetSSP(pFrameContext, ssp - 8);
+    }
+#endif // TARGET_WINDOWS
+
+    pFrameContext->Rbp = currentFP;
+#endif // TARGET_AMD64
+
+    SetSP(pFrameContext, currentSP);
+
+    EECodeInfo codeInfo(ip);
+    MethodDesc* pMD = codeInfo.GetMethodDesc();
+    LoaderAllocator* allocator = pMD->GetLoaderAllocator();
+    OnStackReplacementManager* manager = allocator->GetOnStackReplacementManager();
+    PerPatchpointInfo* ppInfo = manager->GetPerPatchpointInfo(ip);
+
+    PCODE osrMethodCode = ppInfo->m_osrMethodCode;
+    if (osrMethodCode == NULL)
+    {
+        PCODE tier0EntryAddr = (PCODE)codeInfo.GetStartAddress();
+        CodeVersionManager* codeVersionManager = pMD->GetCodeVersionManager();
+        CodeVersionManager::LockHolder lock;
+        NativeCodeVersionCollection nativeCodeVersions = codeVersionManager->GetNativeCodeVersions(pMD);
+        for (NativeCodeVersionIterator itr = nativeCodeVersions.Begin(), end = nativeCodeVersions.End(); itr != end; itr++)
+        {
+            if (itr->GetOptimizationTier() == NativeCodeVersion::OptimizationTier1OSR)
+            {
+                unsigned versionILOffset;
+                PatchpointInfo* ppi = itr->GetOSRInfo(&versionILOffset);
+                if (ppi->GetTier0EntryPoint() == tier0EntryAddr && versionILOffset == ilOffset)
+                {
+                    osrMethodCode = itr->GetNativeCode();
+                    break;
+                }
+            }
+        }
+
+        ppInfo->m_osrMethodCode = osrMethodCode;
+    }
+
+    LOG((LF_TIEREDCOMPILATION, LL_INFO1000, "JIT_ResumeOSR: patchpoint 0x%p TRANSITION to ip 0x%p\n", ip, osrMethodCode));
+    _ASSERTE(osrMethodCode != NULL);
+
+    // Install new entry point as IP
+    SetIP(pFrameContext, osrMethodCode);
+
+    // This method doesn't return normally so we have to manually restore things.
+    ::SetLastError(dwLastError);
+
+    // Transition!
+    ClrRestoreNonvolatileContext(pFrameContext);
+}
+
 // Jit helper invoked at a partial compilation patchpoint.
 //
 // Similar to Jit_Patchpoint, but invoked when execution
diff --git a/src/coreclr/vm/jitinterface.cpp b/src/coreclr/vm/jitinterface.cpp
index a427f4eb396f97..8503c54e6d9eba 100644
--- a/src/coreclr/vm/jitinterface.cpp
+++ b/src/coreclr/vm/jitinterface.cpp
@@ -64,6 +64,7 @@
 #endif
 
 #include "tailcallhelp.h"
+#include "patchpointinfo.h"
 
 #ifdef TARGET_WINDOWS
 EXTERN_C uint32_t _tls_index;
@@ -10942,7 +10943,7 @@ PatchpointInfo* CEEJitInfo::getOSRInfo(unsigned* ilOffset)
     return result;
 }
 
-void CEEJitInfo::CompressDebugInfo()
+void CEEJitInfo::CompressDebugInfo(PCODE nativeEntry)
 {
     CONTRACTL {
         THROWS;
@@ -10966,6 +10967,9 @@ void CEEJitInfo::CompressDebugInfo()
     if ((m_iOffsetMapping == 0) && (m_iNativeVarInfo == 0) && (patchpointInfo == NULL) && (m_numInlineTreeNodes == 0) && (m_numRichOffsetMappings == 0))
         return;
 
+    if (patchpointInfo != NULL)
+        patchpointInfo->SetTier0EntryPoint(nativeEntry);
+
     JIT_TO_EE_TRANSITION();
 
     EX_TRY
@@ -12458,7 +12462,7 @@ CorJitResult invokeCompileMethodHelper(EEJitManager *jitMgr,
     //
     if (SUCCEEDED(ret) && !((CEEJitInfo*)comp)->JitAgain())
     {
-        ((CEEJitInfo*)comp)->CompressDebugInfo();
+        ((CEEJitInfo*)comp)->CompressDebugInfo((PCODE)*nativeEntry);
 
 #ifdef FEATURE_INTERPRETER
         // We do this cleanup in the prestub, where we know whether the method
@@ -14293,7 +14297,7 @@ static Signature BuildResumptionStubCalliSignature(MetaSig& msig, LoaderAllocato
     return AllocateSignature(alloc, sigBuilder);
 }
 
-CORINFO_METHOD_HANDLE CEEInfo::getAsyncResumptionStub()
+CORINFO_METHOD_HANDLE CEEJitInfo::getAsyncResumptionStub()
 {
     CONTRACTL{
         THROWS;
@@ -14302,7 +14306,6 @@ CORINFO_METHOD_HANDLE CEEInfo::getAsyncResumptionStub()
     } CONTRACTL_END;
 
     MethodDesc* md = m_pMethodBeingCompiled;
-    // Not hard to support, just requires calls through a pointer and passing the
     _ASSERTE(!md->IsSharedByGenericInstantiations());
 
     LoaderAllocator* loaderAlloc = md->GetLoaderAllocator();
@@ -14357,8 +14360,25 @@ CORINFO_METHOD_HANDLE CEEInfo::getAsyncResumptionStub()
     nmArgs++;
 #endif
 
-    // TODO: This can use indirection of precode slot directly.
-    pCode->EmitLDC(md->GetMultiCallableAddrOfCode(CORINFO_ACCESS_ANY));
+    // Resumption stubs are uniquely coupled to the code version (since the
+    // continuation is), so we need to make sure we always keep calling the
+    // same version here.
+    PrepareCodeConfig* config = GetThread()->GetCurrentPrepareCodeConfig();
+    NativeCodeVersion ncv = config->GetCodeVersion();
+    if (ncv.GetOptimizationTier() == NativeCodeVersion::OptimizationTier1OSR)
+    {
+        // The OSR version needs to resume in the tier0 version. The tier0
+        // version will handle setting up the frame that the OSR version
+        // expects and then delegating back into the OSR version (knowing to do
+        // so through information stored in the continuation).
+        _ASSERTE(m_pPatchpointInfoFromRuntime != NULL);
+        pCode->EmitLDC((DWORD_PTR)m_pPatchpointInfoFromRuntime->GetTier0EntryPoint());
+    }
+    else
+    {
+        pCode->EmitLDC((DWORD_PTR)config->GetNativeCodeSlot());
+        pCode->EmitLDIND_I();
+    }
 
     pCode->EmitCALLI(pCode->GetSigToken(calliSig.GetRawSig(), calliSig.GetRawSigLen()), numArgs, msig.IsReturnTypeVoid() ? 0 : 1);
 
@@ -14610,6 +14630,12 @@ PatchpointInfo* CEEInfo::getOSRInfo(unsigned* ilOffset)
     UNREACHABLE();      // only called on derived class.
 }
 
+CORINFO_METHOD_HANDLE CEEInfo::getAsyncResumptionStub()
+{
+    LIMITED_METHOD_CONTRACT;
+    UNREACHABLE();      // only called on derived class.
+}
+
 void* CEEInfo::getHelperFtn(CorInfoHelpFunc    ftnNum,         /* IN  */
                             void **            ppIndirection)  /* OUT */
 {
diff --git a/src/coreclr/vm/jitinterface.h b/src/coreclr/vm/jitinterface.h
index 49350450a3d625..8cf2ab64f1f833 100644
--- a/src/coreclr/vm/jitinterface.h
+++ b/src/coreclr/vm/jitinterface.h
@@ -869,7 +869,7 @@ class CEEJitInfo : public CEEInfo
                        ULONG32 cMap, ICorDebugInfo::OffsetMapping *pMap) override final;
     void setVars(CORINFO_METHOD_HANDLE ftn, ULONG32 cVars,
                  ICorDebugInfo::NativeVarInfo *vars) override final;
-    void CompressDebugInfo();
+    void CompressDebugInfo(PCODE nativeEntry);
 
     void reportRichMappings(
         ICorDebugInfo::InlineTreeNode*    inlineTreeNodes,
@@ -904,6 +904,8 @@ class CEEJitInfo : public CEEInfo
     void setPatchpointInfo(PatchpointInfo* patchpointInfo) override final;
     PatchpointInfo* getOSRInfo(unsigned* ilOffset) override final;
 
+    virtual CORINFO_METHOD_HANDLE getAsyncResumptionStub() override final;
+
 protected :
 
     void WriteCodeBytes();
diff --git a/src/coreclr/vm/method.hpp b/src/coreclr/vm/method.hpp
index d48d31ea75e4b9..fa381ec6400209 100644
--- a/src/coreclr/vm/method.hpp
+++ b/src/coreclr/vm/method.hpp
@@ -1943,6 +1943,7 @@ class PrepareCodeConfig
     BOOL MayUsePrecompiledCode();
     virtual PCODE IsJitCancellationRequested();
     virtual BOOL SetNativeCode(PCODE pCode, PCODE * ppAlternateCodeToUse);
+    virtual PTR_PCODE GetNativeCodeSlot();
     virtual COR_ILMETHOD* GetILHeader();
     virtual CORJIT_FLAGS GetJitCompilationFlags();
 #ifdef FEATURE_ON_STACK_REPLACEMENT
diff --git a/src/coreclr/vm/prestub.cpp b/src/coreclr/vm/prestub.cpp
index 3ef95eca9c61d7..8d5f93c22a6630 100644
--- a/src/coreclr/vm/prestub.cpp
+++ b/src/coreclr/vm/prestub.cpp
@@ -1984,6 +1984,12 @@ BOOL PrepareCodeConfig::SetNativeCode(PCODE pCode, PCODE * ppAlternateCodeToUse)
     return FALSE;
 }
 
+PTR_PCODE PrepareCodeConfig::GetNativeCodeSlot()
+{
+    LIMITED_METHOD_CONTRACT;
+    return m_nativeCodeVersion.GetNativeCodeSlot();
+}
+
 COR_ILMETHOD* PrepareCodeConfig::GetILHeader()
 {
     STANDARD_VM_CONTRACT;
diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.cs
index cddd07e4ef4bfb..e9e3930277e327 100644
--- a/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.cs
+++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.cs
@@ -126,6 +126,7 @@ internal static bool IsPrimitiveType(this CorElementType et)
 
 #if !NATIVEAOT
         [Intrinsic]
+        [MethodImpl(MethodImplOptions.NoInlining)]
         [BypassReadyToRun]
         public static void AwaitAwaiterFromRuntimeAsync<TAwaiter>(TAwaiter awaiter) where TAwaiter : INotifyCompletion
         {
@@ -166,6 +167,7 @@ public static void AwaitAwaiterFromRuntimeAsync<TAwaiter>(TAwaiter awaiter) wher
         // recognizes as an async2 call.
         [Intrinsic]
         [BypassReadyToRun]
+        [MethodImpl(MethodImplOptions.NoInlining)]
         public static void UnsafeAwaitAwaiterFromRuntimeAsync<TAwaiter>(TAwaiter awaiter) where TAwaiter : ICriticalNotifyCompletion
         {
             if (RuntimeAsyncViaJitGeneratedStateMachines)

From b7baed9f1eb059c71b6d3405c1fbd8e37ab03618 Mon Sep 17 00:00:00 2001
From: David Wrighton <davidwr@microsoft.com>
Date: Thu, 26 Oct 2023 20:11:30 +0000
Subject: [PATCH 046/203] Merged PR 34838: Upgrade the markdown doc with a
 somewhat better description of the unwinder approach

---
 docs/design/features/runtime-handled-tasks.md | 60 +++++++++++++++----
 1 file changed, 49 insertions(+), 11 deletions(-)

diff --git a/docs/design/features/runtime-handled-tasks.md b/docs/design/features/runtime-handled-tasks.md
index 5402dde90ddb47..eaa71634745ea6 100644
--- a/docs/design/features/runtime-handled-tasks.md
+++ b/docs/design/features/runtime-handled-tasks.md
@@ -149,24 +149,19 @@ Async2 methods will not be visible in reflection via the `Type.GetMethod`, `Type
 
 `Type.GetMemberWithSameMetadataDefinitionAs` will find the matching async variant method.
 
-### Modification of existing async infrastructure
 
-The only known modifications are to add the various new `INotifyCompletion2` and `INotifyCompletion3` interfaces to the awaitable types in the core libraries. 
-
-## Implementation of async2 design 1
-
-TBD, design in progress... there are some notes, but this is very incomplete at this moment.
+## Implementation of Unwinder based async2 implementation
 
 The general design is to leverage the notion that a normal code generated function at a function call point is effectively a state machine. The index for resumption is the IP that returning to the function will set, and the stackframe + saved registers are the current state of the state machine. I believe we can achieve performance comparable to synchronous code with this scheme for non-suspended scenarios, and we may achieve acceptable overall performance as an async mechanism if suspension is relatively rare.
 
-#### JIT Code changes
+### JIT Code changes
 1. Do not allow InlinedCallFrames to be linked to the frame chain across a suspend (Or disable p/invoke inlining in these methods)
 2. Report frame pointers as ByRef
 3. Report all pointers to locals as ByRef
-4. (If possible) tweak codegen for return processing to not use the address returned in RAX when doing byref returns
-5. Force the AwaitAwaiterFromRuntimeAsync to be treated as an async2 method even though it isn't marked as such (due to compiler limitations)
+4. Force the AwaitAwaiterFromRuntimeAsync to be treated as an async2 method even though it isn't marked as such (due to compiler limitations)
+5. Do not allow the JIT to hold onto a thread static base pointer across await points.
 
-#### Thunk from the Task api surface to async2 based implementation
+### Thunk from the Task api surface to async2 based implementation
 
 A thunk from the Task api surface to an async2 based implementation will have the following psuedocode if it is not required to throw `InvalidProgramException`
 
@@ -202,9 +197,52 @@ Task<ReturnType> ThunkAsync(ParameterType param1, ParameterType2 param2, ...)
 }
 ```
 
+### Implementation of `AwaitAwaiterFromRuntimeAsync`
+The implementation of this method captures every stack frame from itself down to the `ThunkAsync` function or `ResumptionFunc`. Each one of these stack frames is packaged up into a structure known as a `Tasklet`. These `Tasklet` objects are registered with the GC so that they will be properly reported, but that reporting is not based on normal GC reporting, but is instead a special codebase. Primarily this need to treat them as special is done so that ByRef pointers can be handled correctly. Once all of the stack frames are captured, then the set of unwound `Tasklet` and the awaiter object are placed in a thread local variable for use by the `ThunkAsync` or `ResumptionFunc` functions. Finally, the actual stack is unwound to the `ThunkAsync`/`ResumptionFunc` frame, which detects that the operation was suspended and does the appropriate thing. In the ThunkAsync implementation, this is done by the runtimeTaskState.FromResult method, which determines if it should ignore the result it acquired from the `TargetMethod` and instead just create a task to return. Finally, a delegate to `ResumptionFunc` is passed to the awaiter.
+
+`Tasklet` structures are designed to hold the data of a frame, the instruction pointer where execution should resume, the current state of any callee preserved registers that are used by the function, and layout/unwind information for finding GC data/and preserved register locations within the frame. Of particular interest is the handling for preserved registers. In the normal case of execution of JIT generated code, when a function needs to use a callee preserved register, it will generate code to store the current value of the register during the prolog of the method, and restore it during the epilog of the method. This unwinder based approach reverses that approach so that the `Tasklet` copy of the stack frame stores the "current" value of the register, and it is reported to the GC in that location when the `Tasklet` goes through GC reporting. The other detail of particular interest is that the data necessary to describe the locations of these preserved registers turns out to be exactly the data necessary to unwind a stack frame.
+
+The implementation of unwinding used to return back to the `ThunkAsync` method is currently written in a highly optimized assembly path which walks the list of preserved register locations, and resets them.
+
+Current implementation of this unwinder scheme use the standard CoreCLR unwinder and GC information reporter to build data structures that describe the unwind/GC data at a particular instruction pointer address, but in theory this should be amenable to a high performance cache.
+
+### Implementation of `ResumptionFunc`
+The dispatcher takes a collection of Tasklets, and resumes the one at the top of the stack, and if it returns, will pop it off the stack and execute the next one. It shall maintain structures such that the EH stack walker can find the list of stack frames that are still held in `Tasklet`s. In addition it shall be responsible for ensuring that the correct `ExecutionContext`/`SynchronizationContext` is maintained, and any `Tasklet` is resumed using the appropriate scheduler provided to the runtime. This implementation today is partial, but the basic structure is present to do so.
+
+#### Resumption of a Tasklet
+
+Once the `ResumptionFunc` has setup the global state to have the correct bits in it, it will call into a carefully crafted assembly stub (called `ResumeTaskletIntegerRegisterReturn` or `ResumeTaskletReferenceReturn`) which takes a `Tasklet*` as input as well as a pointer to a structure which holds the current return value that should be restored to the return value registers. This assembly stub shall copy the `Tasklet`'s stack frame onto the stack, swap any values in the preserved register locations with the current value of the preserved registers, and then tail jump to the instruction pointer held in the `Tasklet`. This resumption process was chosen to ensure that:
+1. The return address on the shadow CET stack does not need any updates upon `Tasklet` resumption.
+2. The runtime only resumes a single method at a time, which improves efficiency in the somewhat common case where there is a deep stack which has a loop in it.
+3. Once resumed, the stack appears as a normal stack to all stackwalking operations.
+
+### Expected characteristics of this approach
+1. The performance of code which does not suspend is effectively the same as normal synchronous code. (confirmed)
+2. The performance of code which suspends a lot is highly impacted by the cost of performing unwind, and somewhat by resumption cost. (under investigation)
+3. The cost of GC while `Tasklet`s exist is dependent on the number of live `Tasklet` objects. This is almost certainly a fairly high cost. (under investigation)
+4. This approach allows development of async code which uses ref parameters/locals and ByRefLike structures. (confirmed)
+
+## Shared implementation between Unwinder and JIT focussed implementation of runtime tasks
+
+It turns out that due to the api design of how async2 code interacts with the existing await pattern in IL, the thunk from async2 to existing Tasks and ValueTasks is the same for all designs.
+
+### Thunk from the async2 surface to Task api implementation
+
+```
+async2 ReturnType Thunk(ParameterType param1, ParameterType2 param2, ...)
+{
+    var awaiter = TargetMethod(param1, param2, ...).GetAwaiter();
+    if (!awaiter.IsCompleted)
+    {
+        RuntimeHelpers.UnsafeAwaitAwaiterFromRuntimeAsync(awaiter);
+    }
+    return awaiter.GetResult();
+}
+```
+
 ## C# Language changes
 
-TBD
+TBD, and not part of this experiment. We have built something. It is strictly for demonstrating that code CAN be generated.
 
 Major identified concerns are
 

From 604e50f997f799f3819e2f4331edcb08cf0bc5e2 Mon Sep 17 00:00:00 2001
From: Jakob Botsch Nielsen <janielsen@microsoft.com>
Date: Fri, 27 Oct 2023 06:56:06 +0000
Subject: [PATCH 047/203] Merged PR 34841: Make BYREF CSEs unavailable across
 suspension points

This ensures we do not CSE a byref across a suspension point in the JIT SM prototype, since it cannot deal with live byrefs during suspension.

Also add a bit of logging, and switch tests to use MSBuildProjectName instead of explicit file names.
---
 src/coreclr/jit/codegencommon.cpp             |   9 ++
 src/coreclr/jit/compiler.cpp                  |   9 ++
 src/coreclr/jit/compiler.h                    |   1 +
 src/coreclr/jit/importercalls.cpp             |   1 +
 src/coreclr/jit/optcse.cpp                    | 107 +++++++++++++++++-
 .../Loader/async/async2-with-yields.csproj    |   2 +-
 .../Loader/async/async2-without-yields.csproj |   2 +-
 .../async/async2fibonacci-with-yields.csproj  |   2 +-
 .../async2fibonacci-without-yields.csproj     |   2 +-
 src/tests/Loader/async/async2object.csproj    |   2 +-
 .../Loader/async/async2objectsCaptured.csproj |   2 +-
 src/tests/Loader/async/async2void.csproj      |   2 +-
 .../Loader/async/cse-array-index-byref.cs     |  34 ++++++
 .../Loader/async/cse-array-index-byref.csproj |   9 ++
 .../async/syncfibonacci-with-yields.csproj    |   2 +-
 .../async/syncfibonacci-without-yields.csproj |   2 +-
 ...askbased-asyncfibonacci-with-yields.csproj |   2 +-
 ...based-asyncfibonacci-without-yields.csproj |   2 +-
 ...askbased-asyncfibonacci-with-yields.csproj |   2 +-
 ...based-asyncfibonacci-without-yields.csproj |   2 +-
 20 files changed, 182 insertions(+), 14 deletions(-)
 create mode 100644 src/tests/Loader/async/cse-array-index-byref.cs
 create mode 100644 src/tests/Loader/async/cse-array-index-byref.csproj

diff --git a/src/coreclr/jit/codegencommon.cpp b/src/coreclr/jit/codegencommon.cpp
index 254808c18ff1c9..58bc365dc89406 100644
--- a/src/coreclr/jit/codegencommon.cpp
+++ b/src/coreclr/jit/codegencommon.cpp
@@ -1807,6 +1807,15 @@ void CodeGen::genGenerateMachineCode()
             printf("; OSR variant for entry point 0x%x\n", compiler->info.compILEntry);
         }
 
+        if (compiler->compIsAsync2StateMachine())
+        {
+            printf("; async2 state machine\n");
+        }
+        else if (compiler->compIsAsync2ViaUnwinding())
+        {
+            printf("; async2 via unwinding\n");
+        }
+
         if ((compiler->opts.compFlags & CLFLG_MAXOPT) == CLFLG_MAXOPT)
         {
             printf("; optimized code\n");
diff --git a/src/coreclr/jit/compiler.cpp b/src/coreclr/jit/compiler.cpp
index eef0f686797982..771f5531fb773a 100644
--- a/src/coreclr/jit/compiler.cpp
+++ b/src/coreclr/jit/compiler.cpp
@@ -3335,6 +3335,15 @@ void Compiler::compInitOptions(JitFlags* jitFlags)
         {
             printf("OPTIONS: Jit invoked for ngen\n");
         }
+
+        if (compIsAsync2StateMachine())
+        {
+            printf("OPTIONS: compilation is an async2 state machine\n");
+        }
+        else if (compIsAsync2ViaUnwinding())
+        {
+            printf("OPTIONS: compilation is an async2 function (via unwinding)\n");
+        }
     }
 #endif
 
diff --git a/src/coreclr/jit/compiler.h b/src/coreclr/jit/compiler.h
index fb6d2fe11f411d..295537ee9a8d33 100644
--- a/src/coreclr/jit/compiler.h
+++ b/src/coreclr/jit/compiler.h
@@ -6859,6 +6859,7 @@ class Compiler
     void optPrintCSEDataFlowSet(EXPSET_VALARG_TP cseDataFlowSet, bool includeBits = true);
 
     EXPSET_TP cseCallKillsMask; // Computed once - A mask that is used to kill available CSEs at callsites
+    EXPSET_TP cseAsyncKillsMask; // Computed once - A mask that is used to kill available BYREF CSEs at async suspension points
 
     /* Generic list of nodes - used by the CSE logic */
 
diff --git a/src/coreclr/jit/importercalls.cpp b/src/coreclr/jit/importercalls.cpp
index ab7b37449de2c5..81eccd3c2c71a0 100644
--- a/src/coreclr/jit/importercalls.cpp
+++ b/src/coreclr/jit/importercalls.cpp
@@ -527,6 +527,7 @@ var_types Compiler::impImportCall(OPCODE                  opcode,
         {
             assert((call != nullptr) && call->OperIs(GT_CALL));
             call->AsCall()->gtIsAsyncCall = true;
+            JITDUMP("Marking [%06u] as a special-case async call\n", dspTreeID(call));
         }
     }
     assert(sig);
diff --git a/src/coreclr/jit/optcse.cpp b/src/coreclr/jit/optcse.cpp
index 9ad785a1257dc1..452ddbe86a24d1 100644
--- a/src/coreclr/jit/optcse.cpp
+++ b/src/coreclr/jit/optcse.cpp
@@ -1036,6 +1036,105 @@ void Compiler::optValnumCSE_InitDataFlow()
         }
     }
 
+    if (compIsAsync2StateMachine())
+    {
+        bool anyAsyncKills = false;
+        cseAsyncKillsMask  = BitVecOps::MakeFull(cseLivenessTraits);
+        for (unsigned inx = 1; inx <= optCSECandidateCount; inx++)
+        {
+            CSEdsc* dsc = optCSEtab[inx - 1];
+            assert(dsc->csdIndex == inx);
+            bool isByRef = false;
+            if (dsc->csdTree->TypeIs(TYP_BYREF))
+            {
+                isByRef = true;
+            }
+            else if (dsc->csdTree->TypeIs(TYP_STRUCT))
+            {
+                ClassLayout* layout = dsc->csdTree->GetLayout(this);
+                isByRef             = layout->HasGCByRef();
+            }
+
+            if (isByRef)
+            {
+                // We generate a bit pattern like: 1111111100111100 where there
+                // are 0s only for the byref CSEs.
+                BitVecOps::RemoveElemD(cseLivenessTraits, cseAsyncKillsMask, getCSEAvailBit(inx));
+                BitVecOps::RemoveElemD(cseLivenessTraits, cseAsyncKillsMask, getCSEAvailCrossCallBit(inx));
+                anyAsyncKills = true;
+            }
+        }
+
+        if (anyAsyncKills)
+        {
+            for (BasicBlock* block : Blocks())
+            {
+                Statement* asyncCallStmt = nullptr;
+                GenTree*   asyncCall     = nullptr;
+                // Find last async call in block
+                Statement* stmt = block->lastStmt();
+                if (stmt == nullptr)
+                {
+                    continue;
+                }
+
+                while (asyncCall == nullptr)
+                {
+                    if ((stmt->GetRootNode()->gtFlags & GTF_CALL) != 0)
+                    {
+                        for (GenTree* tree = stmt->GetRootNode(); tree != nullptr; tree = tree->gtPrev)
+                        {
+                            if (tree->IsCall() && tree->AsCall()->IsAsync2())
+                            {
+                                asyncCallStmt = stmt;
+                                asyncCall     = tree;
+                                break;
+                            }
+                        }
+                    }
+
+                    if (stmt == block->firstStmt())
+                        break;
+
+                    stmt = stmt->GetPrevStmt();
+                }
+
+                if (asyncCall == nullptr)
+                {
+                    continue;
+                }
+
+                // This block has a suspension point. Make all BYREF CSEs unavailable.
+                BitVecOps::IntersectionD(cseLivenessTraits, block->bbCseGen, cseAsyncKillsMask);
+                BitVecOps::IntersectionD(cseLivenessTraits, block->bbCseOut, cseAsyncKillsMask);
+
+                // Now make all byref CSEs after the suspension point available.
+                Statement* curStmt = asyncCallStmt;
+                GenTree*   curTree = asyncCall;
+                while (true)
+                {
+                    do
+                    {
+                        if (IS_CSE_INDEX(curTree->gtCSEnum))
+                        {
+                            unsigned CSEnum = GET_CSE_INDEX(curTree->gtCSEnum);
+                            BitVecOps::AddElemD(cseLivenessTraits, block->bbCseGen, getCSEAvailBit(CSEnum));
+                            BitVecOps::AddElemD(cseLivenessTraits, block->bbCseOut, getCSEAvailBit(CSEnum));
+                        }
+
+                        curTree = curTree->gtNext;
+                    } while (curTree != nullptr);
+
+                    curStmt = curStmt->GetNextStmt();
+                    if (curStmt == nullptr)
+                        break;
+
+                    curTree = curStmt->GetTreeList();
+                }
+            }
+        }
+    }
+
     for (BasicBlock* const block : Blocks())
     {
         // If the block doesn't contains a call then skip it...
@@ -1637,7 +1736,7 @@ void Compiler::optValnumCSE_Availability()
                 // kill all of the cseAvailCrossCallBit for each CSE whenever we see a GT_CALL (unless the call
                 // generates a CSE).
                 //
-                if (tree->OperGet() == GT_CALL)
+                if (tree->OperIs(GT_CALL))
                 {
                     // Check for the common case of an already empty available_cses set
                     // and thus nothing needs to be killed
@@ -1655,6 +1754,12 @@ void Compiler::optValnumCSE_Availability()
                             //
                             BitVecOps::IntersectionD(cseLivenessTraits, available_cses, cseCallKillsMask);
 
+                            // In async state machines, make all byref CSEs unavailable after suspension points.
+                            if (tree->AsCall()->IsAsync2() && compIsAsync2StateMachine())
+                            {
+                                BitVecOps::IntersectionD(cseLivenessTraits, available_cses, cseAsyncKillsMask);
+                            }
+
                             if (isDef)
                             {
                                 // We can have a GT_CALL that produces a CSE,
diff --git a/src/tests/Loader/async/async2-with-yields.csproj b/src/tests/Loader/async/async2-with-yields.csproj
index afefd86ece8373..6607f198fc54fb 100644
--- a/src/tests/Loader/async/async2-with-yields.csproj
+++ b/src/tests/Loader/async/async2-with-yields.csproj
@@ -3,6 +3,6 @@
     <RequiresProcessIsolation>true</RequiresProcessIsolation>
   </PropertyGroup>
   <ItemGroup>
-    <Compile Include="async2-with-yields.cs" />
+    <Compile Include="$(MSBuildProjectName).cs" />
   </ItemGroup>
 </Project>
diff --git a/src/tests/Loader/async/async2-without-yields.csproj b/src/tests/Loader/async/async2-without-yields.csproj
index 1e584bac999039..6607f198fc54fb 100644
--- a/src/tests/Loader/async/async2-without-yields.csproj
+++ b/src/tests/Loader/async/async2-without-yields.csproj
@@ -3,6 +3,6 @@
     <RequiresProcessIsolation>true</RequiresProcessIsolation>
   </PropertyGroup>
   <ItemGroup>
-    <Compile Include="async2-without-yields.cs" />
+    <Compile Include="$(MSBuildProjectName).cs" />
   </ItemGroup>
 </Project>
diff --git a/src/tests/Loader/async/async2fibonacci-with-yields.csproj b/src/tests/Loader/async/async2fibonacci-with-yields.csproj
index 6780013a730b11..4a1973da13c789 100644
--- a/src/tests/Loader/async/async2fibonacci-with-yields.csproj
+++ b/src/tests/Loader/async/async2fibonacci-with-yields.csproj
@@ -4,6 +4,6 @@
     <Optimize>True</Optimize>
   </PropertyGroup>
   <ItemGroup>
-    <Compile Include="async2fibonacci-with-yields.cs" />
+    <Compile Include="$(MSBuildProjectName).cs" />
   </ItemGroup>
 </Project>
diff --git a/src/tests/Loader/async/async2fibonacci-without-yields.csproj b/src/tests/Loader/async/async2fibonacci-without-yields.csproj
index 6630b675c0921d..4a1973da13c789 100644
--- a/src/tests/Loader/async/async2fibonacci-without-yields.csproj
+++ b/src/tests/Loader/async/async2fibonacci-without-yields.csproj
@@ -4,6 +4,6 @@
     <Optimize>True</Optimize>
   </PropertyGroup>
   <ItemGroup>
-    <Compile Include="async2fibonacci-without-yields.cs" />
+    <Compile Include="$(MSBuildProjectName).cs" />
   </ItemGroup>
 </Project>
diff --git a/src/tests/Loader/async/async2object.csproj b/src/tests/Loader/async/async2object.csproj
index 49568b4a12ac83..6607f198fc54fb 100644
--- a/src/tests/Loader/async/async2object.csproj
+++ b/src/tests/Loader/async/async2object.csproj
@@ -3,6 +3,6 @@
     <RequiresProcessIsolation>true</RequiresProcessIsolation>
   </PropertyGroup>
   <ItemGroup>
-    <Compile Include="async2object.cs" />
+    <Compile Include="$(MSBuildProjectName).cs" />
   </ItemGroup>
 </Project>
diff --git a/src/tests/Loader/async/async2objectsCaptured.csproj b/src/tests/Loader/async/async2objectsCaptured.csproj
index 16bdb6543d40b1..6607f198fc54fb 100644
--- a/src/tests/Loader/async/async2objectsCaptured.csproj
+++ b/src/tests/Loader/async/async2objectsCaptured.csproj
@@ -3,6 +3,6 @@
     <RequiresProcessIsolation>true</RequiresProcessIsolation>
   </PropertyGroup>
   <ItemGroup>
-    <Compile Include="async2objectsCaptured.cs" />
+    <Compile Include="$(MSBuildProjectName).cs" />
   </ItemGroup>
 </Project>
diff --git a/src/tests/Loader/async/async2void.csproj b/src/tests/Loader/async/async2void.csproj
index 78d25e41af6595..6607f198fc54fb 100644
--- a/src/tests/Loader/async/async2void.csproj
+++ b/src/tests/Loader/async/async2void.csproj
@@ -3,6 +3,6 @@
     <RequiresProcessIsolation>true</RequiresProcessIsolation>
   </PropertyGroup>
   <ItemGroup>
-    <Compile Include="async2void.cs" />
+    <Compile Include="$(MSBuildProjectName).cs" />
   </ItemGroup>
 </Project>
diff --git a/src/tests/Loader/async/cse-array-index-byref.cs b/src/tests/Loader/async/cse-array-index-byref.cs
new file mode 100644
index 00000000000000..321be16f18fd1f
--- /dev/null
+++ b/src/tests/Loader/async/cse-array-index-byref.cs
@@ -0,0 +1,34 @@
+// 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 System.Runtime.CompilerServices;
+using System.Threading.Tasks;
+using Xunit;
+
+public class CseArrayIndexByref
+{
+    [Fact]
+    public static void TestEntryPoint()
+    {
+        int[] arr = new int[1];
+        AsyncTestEntryPoint(arr, 0).Wait();
+        Assert.Equal(199_990_000, arr[0]);
+    }
+
+    private static async Task AsyncTestEntryPoint(int[] arr, int index)
+    {
+        await HoistedByref(arr, index);
+    }
+
+    [MethodImpl(MethodImplOptions.NoInlining)]
+    private static async2 int HoistedByref(int[] arr, int index)
+    {
+        for (int i = 0; i < 20000; i++)
+        {
+            arr[index] += i;
+            await Task.Yield();
+        }
+        return 0;
+    }
+}
\ No newline at end of file
diff --git a/src/tests/Loader/async/cse-array-index-byref.csproj b/src/tests/Loader/async/cse-array-index-byref.csproj
new file mode 100644
index 00000000000000..501217e4d86892
--- /dev/null
+++ b/src/tests/Loader/async/cse-array-index-byref.csproj
@@ -0,0 +1,9 @@
+<Project Sdk="Microsoft.NET.Sdk">
+  <PropertyGroup>
+    <DebugType>None</DebugType>
+    <Optimize>True</Optimize>
+  </PropertyGroup>
+  <ItemGroup>
+    <Compile Include="$(MSBuildProjectName).cs" />
+  </ItemGroup>
+</Project>
diff --git a/src/tests/Loader/async/syncfibonacci-with-yields.csproj b/src/tests/Loader/async/syncfibonacci-with-yields.csproj
index be97c05717f3b3..4a1973da13c789 100644
--- a/src/tests/Loader/async/syncfibonacci-with-yields.csproj
+++ b/src/tests/Loader/async/syncfibonacci-with-yields.csproj
@@ -4,6 +4,6 @@
     <Optimize>True</Optimize>
   </PropertyGroup>
   <ItemGroup>
-    <Compile Include="syncfibonacci-with-yields.cs" />
+    <Compile Include="$(MSBuildProjectName).cs" />
   </ItemGroup>
 </Project>
diff --git a/src/tests/Loader/async/syncfibonacci-without-yields.csproj b/src/tests/Loader/async/syncfibonacci-without-yields.csproj
index 9b3605f2a5e292..4a1973da13c789 100644
--- a/src/tests/Loader/async/syncfibonacci-without-yields.csproj
+++ b/src/tests/Loader/async/syncfibonacci-without-yields.csproj
@@ -4,6 +4,6 @@
     <Optimize>True</Optimize>
   </PropertyGroup>
   <ItemGroup>
-    <Compile Include="syncfibonacci-without-yields.cs" />
+    <Compile Include="$(MSBuildProjectName).cs" />
   </ItemGroup>
 </Project>
diff --git a/src/tests/Loader/async/taskbased-asyncfibonacci-with-yields.csproj b/src/tests/Loader/async/taskbased-asyncfibonacci-with-yields.csproj
index cce296b679dc3d..4a1973da13c789 100644
--- a/src/tests/Loader/async/taskbased-asyncfibonacci-with-yields.csproj
+++ b/src/tests/Loader/async/taskbased-asyncfibonacci-with-yields.csproj
@@ -4,6 +4,6 @@
     <Optimize>True</Optimize>
   </PropertyGroup>
   <ItemGroup>
-    <Compile Include="taskbased-asyncfibonacci-with-yields.cs" />
+    <Compile Include="$(MSBuildProjectName).cs" />
   </ItemGroup>
 </Project>
diff --git a/src/tests/Loader/async/taskbased-asyncfibonacci-without-yields.csproj b/src/tests/Loader/async/taskbased-asyncfibonacci-without-yields.csproj
index 26a1f092bdceb1..4a1973da13c789 100644
--- a/src/tests/Loader/async/taskbased-asyncfibonacci-without-yields.csproj
+++ b/src/tests/Loader/async/taskbased-asyncfibonacci-without-yields.csproj
@@ -4,6 +4,6 @@
     <Optimize>True</Optimize>
   </PropertyGroup>
   <ItemGroup>
-    <Compile Include="taskbased-asyncfibonacci-without-yields.cs" />
+    <Compile Include="$(MSBuildProjectName).cs" />
   </ItemGroup>
 </Project>
diff --git a/src/tests/Loader/async/valuetaskbased-asyncfibonacci-with-yields.csproj b/src/tests/Loader/async/valuetaskbased-asyncfibonacci-with-yields.csproj
index 3edcf08d0eb05f..4a1973da13c789 100644
--- a/src/tests/Loader/async/valuetaskbased-asyncfibonacci-with-yields.csproj
+++ b/src/tests/Loader/async/valuetaskbased-asyncfibonacci-with-yields.csproj
@@ -4,6 +4,6 @@
     <Optimize>True</Optimize>
   </PropertyGroup>
   <ItemGroup>
-    <Compile Include="valuetaskbased-asyncfibonacci-with-yields.cs" />
+    <Compile Include="$(MSBuildProjectName).cs" />
   </ItemGroup>
 </Project>
diff --git a/src/tests/Loader/async/valuetaskbased-asyncfibonacci-without-yields.csproj b/src/tests/Loader/async/valuetaskbased-asyncfibonacci-without-yields.csproj
index 4d381cc234d7c6..4a1973da13c789 100644
--- a/src/tests/Loader/async/valuetaskbased-asyncfibonacci-without-yields.csproj
+++ b/src/tests/Loader/async/valuetaskbased-asyncfibonacci-without-yields.csproj
@@ -4,6 +4,6 @@
     <Optimize>True</Optimize>
   </PropertyGroup>
   <ItemGroup>
-    <Compile Include="valuetaskbased-asyncfibonacci-without-yields.cs" />
+    <Compile Include="$(MSBuildProjectName).cs" />
   </ItemGroup>
 </Project>

From 7d241b756709b52f1c01cdf6f045e8c983773136 Mon Sep 17 00:00:00 2001
From: Vladimir Sadov <vsadov@microsoft.com>
Date: Sat, 28 Oct 2023 17:37:48 +0000
Subject: [PATCH 048/203] Merged PR 34886: update roslyn reference for
 impement/override support, add a few tests

update roslyn reference for impement/override support, add a few tests
---
 buildroslynnugets.cmd                         |  4 +-
 eng/Versions.props                            |  6 +-
 src/tests/Loader/async/async2implement.cs     | 72 +++++++++++++++
 src/tests/Loader/async/async2implement.csproj |  8 ++
 src/tests/Loader/async/async2override.cs      | 90 +++++++++++++++++++
 src/tests/Loader/async/async2override.csproj  |  8 ++
 6 files changed, 183 insertions(+), 5 deletions(-)
 create mode 100644 src/tests/Loader/async/async2implement.cs
 create mode 100644 src/tests/Loader/async/async2implement.csproj
 create mode 100644 src/tests/Loader/async/async2override.cs
 create mode 100644 src/tests/Loader/async/async2override.csproj

diff --git a/buildroslynnugets.cmd b/buildroslynnugets.cmd
index c03c1914cf4678..5e3e212060ec8f 100644
--- a/buildroslynnugets.cmd
+++ b/buildroslynnugets.cmd
@@ -1,14 +1,14 @@
 setlocal ENABLEEXTENSIONS
 pushd %~dp0
 
-set ASYNC_SUFFIX=async-3
+set ASYNC_SUFFIX=async-4
 
 cd ..
 pushd dotnet-roslyn
 
 git fetch AzDo dev/vsadov/a2
 rem when updating this, make sure to update the ASYNC_SUFFIX above and the versions.props file
-git checkout 0d06d1f93092dc3112fcefdf8957e11ed3ea4e31
+git checkout 05269ce679fef115617a579ce29edd2c5e45b3e6
 
 call restore.cmd
 call build.cmd -c release
diff --git a/eng/Versions.props b/eng/Versions.props
index 4d47b03429f47e..9c729b435ad073 100644
--- a/eng/Versions.props
+++ b/eng/Versions.props
@@ -39,9 +39,9 @@
       Any tools that contribute to the design-time experience should use the MicrosoftCodeAnalysisVersion_LatestVS property above to ensure
       they do not break the local dev experience.
     -->
-    <MicrosoftCodeAnalysisCSharpVersion>4.9.0-async-3</MicrosoftCodeAnalysisCSharpVersion>
-    <MicrosoftCodeAnalysisVersion>4.9.0-async-3</MicrosoftCodeAnalysisVersion>
-    <MicrosoftNetCompilersToolsetVersion>4.9.0-async-3</MicrosoftNetCompilersToolsetVersion>
+    <MicrosoftCodeAnalysisCSharpVersion>4.9.0-async-4</MicrosoftCodeAnalysisCSharpVersion>
+    <MicrosoftCodeAnalysisVersion>4.9.0-async-4</MicrosoftCodeAnalysisVersion>
+    <MicrosoftNetCompilersToolsetVersion>4.9.0-async-4</MicrosoftNetCompilersToolsetVersion>
   </PropertyGroup>
   <!--
     For source generator support we need to target multiple versions of Roslyn in order to be able to run on older versions of Roslyn.
diff --git a/src/tests/Loader/async/async2implement.cs b/src/tests/Loader/async/async2implement.cs
new file mode 100644
index 00000000000000..6eb63e715c7945
--- /dev/null
+++ b/src/tests/Loader/async/async2implement.cs
@@ -0,0 +1,72 @@
+// 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 System.Runtime.CompilerServices;
+using System.Threading.Tasks;
+using Xunit;
+
+public class Async2Implement
+{
+    interface IBase1
+    {
+        public async2 int M1();
+    }
+
+    class Derived1 : IBase1
+    {
+        public async Task<int> M1()
+        {
+            await Task.Yield();
+            return 2;
+        }
+    }
+
+    class Derived1a : IBase1
+    {
+        public async2 int M1()
+        {
+            await Task.Yield();
+            return 3;
+        }
+    }
+
+    interface IBase2
+    {
+        public Task<int> M1();
+    }
+
+    class Derived2 : IBase2
+    {
+        public async2 int M1()
+        {
+            await Task.Yield();
+            return 12;
+        }
+    }
+
+    class Derived2a : IBase2
+    {
+        public Task<int> M1()
+        {
+            await Task.Yield();
+            return 22;
+        }
+    }
+
+    [Fact]
+    public static void TestEntryPoint()
+    {
+        IBase1 b1 = new Derived1();
+        Assert.Equal(2, b1.M1().Result);
+
+        b1 = new Derived1a();
+        Assert.Equal(3, b1.M1().Result);
+
+        IBase2 b2 = new Derived2();
+        Assert.Equal(12, b2.M1().Result);
+
+        b2 = new Derived2a();
+        Assert.Equal(22, b2.M1().Result);
+    }
+}
diff --git a/src/tests/Loader/async/async2implement.csproj b/src/tests/Loader/async/async2implement.csproj
new file mode 100644
index 00000000000000..0f29ed860df43e
--- /dev/null
+++ b/src/tests/Loader/async/async2implement.csproj
@@ -0,0 +1,8 @@
+<Project Sdk="Microsoft.NET.Sdk.IL">
+  <PropertyGroup>
+    <RequiresProcessIsolation>true</RequiresProcessIsolation>
+  </PropertyGroup>
+  <ItemGroup>
+   <Compile Include="$(MSBuildProjectName).cs" />
+  </ItemGroup>
+</Project>
diff --git a/src/tests/Loader/async/async2override.cs b/src/tests/Loader/async/async2override.cs
new file mode 100644
index 00000000000000..bdb74bb2ed4aba
--- /dev/null
+++ b/src/tests/Loader/async/async2override.cs
@@ -0,0 +1,90 @@
+// 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 System.Runtime.CompilerServices;
+using System.Threading.Tasks;
+using Xunit;
+
+public class Async2Override
+{
+    class Base
+    {
+        public virtual async2 int M1()
+        {
+            await Task.Yield();
+            return 1;
+        }
+    }
+
+    class Derived1 : Base
+    {
+        public override async Task<int> M1()
+        {
+            await Task.Yield();
+            return 2;
+        }
+    }
+
+    class Derived2 : Derived1
+    {
+        public override async2 int M1()
+        {
+            await Task.Yield();
+            return 3;
+        }
+    }
+
+
+    class Base1
+    {
+        public virtual async Task<int> M1()
+        {
+            await Task.Yield();
+            return 11;
+        }
+    }
+
+    class Derived11 : Base1
+    {
+        public override async2 int M1()
+        {
+            await Task.Yield();
+            return 12;
+        }
+    }
+
+    class Derived12 : Derived11
+    {
+        public override async Task<int> M1()
+        {
+            await Task.Yield();
+            return 13;
+        }
+    }
+
+
+    [Fact]
+    public static void TestEntryPoint()
+    {
+        Base b = new Derived1();
+        Assert.Equal(2, b.M1().Result);
+
+        b = new Derived2();
+        Assert.Equal(3, b.M1().Result);
+
+        Derived1 d = new Derived2();
+        Assert.Equal(3, d.M1().Result);
+
+
+        Base1 b1 = new Derived11();
+        Assert.Equal(12, b1.M1().Result);
+
+        b1 = new Derived12();
+        Assert.Equal(13, b1.M1().Result);
+
+        Derived11 d1 = new Derived12();
+        Assert.Equal(13, d1.M1().Result);
+
+    }
+}
diff --git a/src/tests/Loader/async/async2override.csproj b/src/tests/Loader/async/async2override.csproj
new file mode 100644
index 00000000000000..0f29ed860df43e
--- /dev/null
+++ b/src/tests/Loader/async/async2override.csproj
@@ -0,0 +1,8 @@
+<Project Sdk="Microsoft.NET.Sdk.IL">
+  <PropertyGroup>
+    <RequiresProcessIsolation>true</RequiresProcessIsolation>
+  </PropertyGroup>
+  <ItemGroup>
+   <Compile Include="$(MSBuildProjectName).cs" />
+  </ItemGroup>
+</Project>

From fe0f8bbe355cce62f2175e3238217eab4592b5fd Mon Sep 17 00:00:00 2001
From: Jakob Botsch Nielsen <janielsen@microsoft.com>
Date: Mon, 30 Oct 2023 08:59:54 +0000
Subject: [PATCH 049/203] Merged PR 34865: Make Continuation a class

There is no reason for this to be a struct given that we currently just
reuse the same type for every continuation, and it is always just
boxed immediately.

Also give resumption stubs a more meaningful name, and stop jitting them proactively.
---
 .../RuntimeHelpers.CoreCLR.cs                 | 42 +++++++------
 .../src/System/StubHelpers.cs                 |  2 +-
 src/coreclr/jit/async.cpp                     | 60 ++++++++-----------
 src/coreclr/vm/corelib.h                      |  4 +-
 src/coreclr/vm/jitinterface.cpp               | 31 ++++++++--
 src/coreclr/vm/metasig.h                      |  3 +-
 src/coreclr/vm/method.hpp                     |  6 ++
 src/coreclr/vm/prestub.cpp                    |  8 +--
 .../CompilerServices/RuntimeHelpers.cs        |  8 +--
 9 files changed, 89 insertions(+), 75 deletions(-)

diff --git a/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs
index c716980c657cdf..8c1884533f7d1f 100644
--- a/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs
+++ b/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs
@@ -382,16 +382,16 @@ private static unsafe void DispatchTailCalls(
             }
         }
 
-        private static object AllocContinuation(object prevContinuation, nuint numGCRefs, nuint dataSize)
+        private static Continuation AllocContinuation(Continuation prevContinuation, nuint numGCRefs, nuint dataSize)
         {
-            object newContinuation = new Continuation { Data = new byte[dataSize], GCData = new object[numGCRefs] };
-            Unsafe.Unbox<Continuation>(prevContinuation).Next = newContinuation;
+            Continuation newContinuation = new Continuation { Data = new byte[dataSize], GCData = new object[numGCRefs] };
+            prevContinuation.Next = newContinuation;
             return newContinuation;
         }
 
         private struct RuntimeAsyncAwaitState
         {
-            public object? SentinelContinuation;
+            public Continuation? SentinelContinuation;
             public INotifyCompletion? Notifier;
         }
 
@@ -399,13 +399,12 @@ private struct RuntimeAsyncAwaitState
         private static RuntimeAsyncAwaitState t_runtimeAsyncAwaitState;
 
         [Intrinsic]
-        private static void SuspendAsync2(object continuation) => throw new UnreachableException();
+        private static void SuspendAsync2(Continuation continuation) => throw new UnreachableException();
 
-        private static Task<T> FinalizeTaskReturningThunk<T>(object boxedContinuation)
+        private static Task<T> FinalizeTaskReturningThunk<T>(Continuation continuation)
         {
             TaskCompletionSource<T> tcs = new();
             ref RuntimeAsyncAwaitState state = ref t_runtimeAsyncAwaitState;
-            ref Continuation continuation = ref Unsafe.Unbox<Continuation>(boxedContinuation);
             object[] gcData = new object[2];
             gcData[1] = tcs;
             continuation.Next = new Continuation
@@ -414,14 +413,13 @@ private static Task<T> FinalizeTaskReturningThunk<T>(object boxedContinuation)
                 GCData = gcData,
             };
 
-            object headContinuation = Unsafe.Unbox<Continuation>(state.SentinelContinuation!).Next!;
+            Continuation headContinuation = state.SentinelContinuation!.Next!;
             state.Notifier!.OnCompleted(() => DispatchContinuations(headContinuation));
             return tcs.Task;
         }
 
-        private static object? ResumeTaskCompletionSource<T>(object boxedContinuation, Exception? exception)
+        private static Continuation? ResumeTaskCompletionSource<T>(Continuation continuation, Exception? exception)
         {
-            ref Continuation continuation = ref Unsafe.Unbox<Continuation>(boxedContinuation);
             var tcs = (TaskCompletionSource<T>)continuation.GCData![1];
             if (exception == null)
             {
@@ -435,16 +433,17 @@ private static Task<T> FinalizeTaskReturningThunk<T>(object boxedContinuation)
             return null;
         }
 
-        private static unsafe void DispatchContinuations(object? boxedContinuation)
+        private static unsafe void DispatchContinuations(Continuation? continuation)
         {
+            Debug.Assert(continuation != null);
+
             Exception? exToPropagate = null;
             do
             {
-                ref Continuation continuation = ref Unsafe.Unbox<Continuation>(boxedContinuation!);
-                object? newContinuation = null;
+                Continuation? newContinuation = null;
                 try
                 {
-                    newContinuation = continuation.Resume(boxedContinuation!, exToPropagate);
+                    newContinuation = continuation.Resume(continuation, exToPropagate);
                 }
                 catch (Exception ex)
                 {
@@ -453,16 +452,15 @@ private static unsafe void DispatchContinuations(object? boxedContinuation)
 
                 if (newContinuation != null)
                 {
-                    Unsafe.Unbox<Continuation>(newContinuation).Next = continuation.Next;
-
+                    newContinuation.Next = continuation.Next;
                     ref RuntimeAsyncAwaitState state = ref t_runtimeAsyncAwaitState;
-                    object headContinuation = Unsafe.Unbox<Continuation>(state.SentinelContinuation!).Next!;
+                    Continuation headContinuation = state.SentinelContinuation!.Next!;
                     state.Notifier!.OnCompleted(() => DispatchContinuations(headContinuation));
                     return;
                 }
 
-                boxedContinuation = continuation.Next;
-            } while (boxedContinuation != null);
+                continuation = continuation.Next;
+            } while (continuation != null);
         }
     }
 
@@ -746,10 +744,10 @@ internal unsafe struct TailCallTls
         public IntPtr ArgBuffer;
     }
 
-    internal unsafe struct Continuation
+    internal sealed unsafe class Continuation
     {
-        public object? Next;
-        public delegate*<object, Exception?, object?> Resume;
+        public Continuation? Next;
+        public delegate*<Continuation, Exception?, Continuation?> Resume;
         public uint State;
         public byte[]? Data;
         public object[]? GCData;
diff --git a/src/coreclr/System.Private.CoreLib/src/System/StubHelpers.cs b/src/coreclr/System.Private.CoreLib/src/System/StubHelpers.cs
index 18f9073c982326..0bebbf61d42474 100644
--- a/src/coreclr/System.Private.CoreLib/src/System/StubHelpers.cs
+++ b/src/coreclr/System.Private.CoreLib/src/System/StubHelpers.cs
@@ -1298,6 +1298,6 @@ internal static void CheckStringLength(uint length)
         internal static extern IntPtr NextCallReturnAddress();
 
         [Intrinsic]
-        internal static object? Async2CallContinuation() => null;
+        internal static Continuation? Async2CallContinuation() => null;
     }  // class StubHelpers
 }
diff --git a/src/coreclr/jit/async.cpp b/src/coreclr/jit/async.cpp
index cb92fc3b12331a..ca19accfdbf30f 100644
--- a/src/coreclr/jit/async.cpp
+++ b/src/coreclr/jit/async.cpp
@@ -482,16 +482,14 @@ void Async2Transformation::Transform(
 
     // Fill in 'Resume'
     GenTree* newContinuation = m_comp->gtNewLclvNode(m_newContinuationVar, TYP_REF);
-    unsigned resumeOffset =
-        TARGET_POINTER_SIZE + m_comp->info.compCompHnd->getFieldOffset(m_async2Info.continuationResumeFldHnd);
-    GenTree* resumeStubAddr = CreateResumptionStubAddrTree();
-    GenTree* storeResume    = StoreAtOffset(newContinuation, resumeOffset, resumeStubAddr);
+    unsigned resumeOffset    = m_comp->info.compCompHnd->getFieldOffset(m_async2Info.continuationResumeFldHnd);
+    GenTree* resumeStubAddr  = CreateResumptionStubAddrTree();
+    GenTree* storeResume     = StoreAtOffset(newContinuation, resumeOffset, resumeStubAddr);
     LIR::AsRange(retBB).InsertAtEnd(LIR::SeqTree(m_comp, storeResume));
 
     // Fill in 'state'
-    newContinuation = m_comp->gtNewLclvNode(m_newContinuationVar, TYP_REF);
-    unsigned stateOffset =
-        TARGET_POINTER_SIZE + m_comp->info.compCompHnd->getFieldOffset(m_async2Info.continuationStateFldHnd);
+    newContinuation       = m_comp->gtNewLclvNode(m_newContinuationVar, TYP_REF);
+    unsigned stateOffset  = m_comp->info.compCompHnd->getFieldOffset(m_async2Info.continuationStateFldHnd);
     GenTree* stateNumNode = m_comp->gtNewIconNode((ssize_t)stateNum, TYP_I_IMPL);
     GenTree* store        = StoreAtOffset(newContinuation, stateOffset, stateNumNode);
     LIR::AsRange(retBB).InsertAtEnd(LIR::SeqTree(m_comp, store));
@@ -501,10 +499,9 @@ void Async2Transformation::Transform(
     {
         unsigned objectArrLclNum = GetGCDataArrayVar();
 
-        newContinuation = m_comp->gtNewLclvNode(m_newContinuationVar, TYP_REF);
-        unsigned gcDataOffset =
-            TARGET_POINTER_SIZE + m_comp->info.compCompHnd->getFieldOffset(m_async2Info.continuationGCDataFldHnd);
-        GenTree* gcDataInd             = LoadFromOffset(newContinuation, gcDataOffset, TYP_REF);
+        newContinuation       = m_comp->gtNewLclvNode(m_newContinuationVar, TYP_REF);
+        unsigned gcDataOffset = m_comp->info.compCompHnd->getFieldOffset(m_async2Info.continuationGCDataFldHnd);
+        GenTree* gcDataInd    = LoadFromOffset(newContinuation, gcDataOffset, TYP_REF);
         GenTree* storeAllocedObjectArr = m_comp->gtNewStoreLclVarNode(objectArrLclNum, gcDataInd);
         LIR::AsRange(retBB).InsertAtEnd(LIR::SeqTree(m_comp, storeAllocedObjectArr));
 
@@ -584,9 +581,8 @@ void Async2Transformation::Transform(
     {
         unsigned byteArrLclNum = GetDataArrayVar();
 
-        GenTree* newContinuation = m_comp->gtNewLclvNode(m_newContinuationVar, TYP_REF);
-        unsigned dataOffset =
-            TARGET_POINTER_SIZE + m_comp->info.compCompHnd->getFieldOffset(m_async2Info.continuationDataFldHnd);
+        GenTree* newContinuation     = m_comp->gtNewLclvNode(m_newContinuationVar, TYP_REF);
+        unsigned dataOffset          = m_comp->info.compCompHnd->getFieldOffset(m_async2Info.continuationDataFldHnd);
         GenTree* dataOffsetNode      = m_comp->gtNewIconNode((ssize_t)dataOffset, TYP_I_IMPL);
         GenTree* dataAddr            = m_comp->gtNewOperNode(GT_ADD, TYP_BYREF, newContinuation, dataOffsetNode);
         GenTree* dataInd             = m_comp->gtNewIndir(TYP_REF, dataAddr, GTF_IND_NONFAULTING);
@@ -676,9 +672,8 @@ void Async2Transformation::Transform(
     {
         unsigned byteArrLclNum = GetDataArrayVar();
 
-        GenTree* newContinuation = m_comp->gtNewLclvNode(m_comp->lvaAsyncContinuationArg, TYP_REF);
-        unsigned dataOffset =
-            TARGET_POINTER_SIZE + m_comp->info.compCompHnd->getFieldOffset(m_async2Info.continuationDataFldHnd);
+        GenTree* newContinuation     = m_comp->gtNewLclvNode(m_comp->lvaAsyncContinuationArg, TYP_REF);
+        unsigned dataOffset          = m_comp->info.compCompHnd->getFieldOffset(m_async2Info.continuationDataFldHnd);
         GenTree* dataOffsetNode      = m_comp->gtNewIconNode((ssize_t)dataOffset, TYP_I_IMPL);
         GenTree* dataAddr            = m_comp->gtNewOperNode(GT_ADD, TYP_BYREF, newContinuation, dataOffsetNode);
         GenTree* dataInd             = m_comp->gtNewIndir(TYP_REF, dataAddr, GTF_IND_NONFAULTING);
@@ -732,12 +727,11 @@ void Async2Transformation::Transform(
     {
         unsigned objectArrLclNum = GetGCDataArrayVar();
 
-        newContinuation = m_comp->gtNewLclvNode(m_comp->lvaAsyncContinuationArg, TYP_REF);
-        unsigned gcDataOffset =
-            TARGET_POINTER_SIZE + m_comp->info.compCompHnd->getFieldOffset(m_async2Info.continuationGCDataFldHnd);
-        GenTree* gcDataOffsetNode      = m_comp->gtNewIconNode((ssize_t)gcDataOffset, TYP_I_IMPL);
-        GenTree* gcDataAddr            = m_comp->gtNewOperNode(GT_ADD, TYP_BYREF, newContinuation, gcDataOffsetNode);
-        GenTree* gcDataInd             = m_comp->gtNewIndir(TYP_REF, gcDataAddr, GTF_IND_NONFAULTING);
+        newContinuation           = m_comp->gtNewLclvNode(m_comp->lvaAsyncContinuationArg, TYP_REF);
+        unsigned gcDataOffset     = m_comp->info.compCompHnd->getFieldOffset(m_async2Info.continuationGCDataFldHnd);
+        GenTree* gcDataOffsetNode = m_comp->gtNewIconNode((ssize_t)gcDataOffset, TYP_I_IMPL);
+        GenTree* gcDataAddr       = m_comp->gtNewOperNode(GT_ADD, TYP_BYREF, newContinuation, gcDataOffsetNode);
+        GenTree* gcDataInd        = m_comp->gtNewIndir(TYP_REF, gcDataAddr, GTF_IND_NONFAULTING);
         GenTree* storeAllocedObjectArr = m_comp->gtNewStoreLclVarNode(objectArrLclNum, gcDataInd);
         LIR::AsRange(resumeBB).InsertAtEnd(newContinuation, gcDataOffsetNode, gcDataAddr, gcDataInd,
                                            storeAllocedObjectArr);
@@ -1063,9 +1057,8 @@ void Async2Transformation::CreateResumptionSwitch()
         m_comp->fgAddRefPred(m_resumptionBBs[0], condBB);
         m_comp->fgAddRefPred(m_resumptionBBs[1], condBB);
 
-        continuationArg = m_comp->gtNewLclvNode(m_comp->lvaAsyncContinuationArg, TYP_REF);
-        unsigned stateOffset =
-            TARGET_POINTER_SIZE + m_comp->info.compCompHnd->getFieldOffset(m_async2Info.continuationStateFldHnd);
+        continuationArg          = m_comp->gtNewLclvNode(m_comp->lvaAsyncContinuationArg, TYP_REF);
+        unsigned stateOffset     = m_comp->info.compCompHnd->getFieldOffset(m_async2Info.continuationStateFldHnd);
         GenTree* stateOffsetNode = m_comp->gtNewIconNode((ssize_t)stateOffset, TYP_I_IMPL);
         GenTree* stateAddr       = m_comp->gtNewOperNode(GT_ADD, TYP_BYREF, continuationArg, stateOffsetNode);
         GenTree* stateInd        = m_comp->gtNewIndir(TYP_INT, stateAddr, GTF_IND_NONFAULTING);
@@ -1086,9 +1079,8 @@ void Async2Transformation::CreateResumptionSwitch()
 
         m_comp->fgAddRefPred(switchBB, newEntryBB);
 
-        continuationArg = m_comp->gtNewLclvNode(m_comp->lvaAsyncContinuationArg, TYP_REF);
-        unsigned stateOffset =
-            TARGET_POINTER_SIZE + m_comp->info.compCompHnd->getFieldOffset(m_async2Info.continuationStateFldHnd);
+        continuationArg          = m_comp->gtNewLclvNode(m_comp->lvaAsyncContinuationArg, TYP_REF);
+        unsigned stateOffset     = m_comp->info.compCompHnd->getFieldOffset(m_async2Info.continuationStateFldHnd);
         GenTree* stateOffsetNode = m_comp->gtNewIconNode((ssize_t)stateOffset, TYP_I_IMPL);
         GenTree* stateAddr       = m_comp->gtNewOperNode(GT_ADD, TYP_BYREF, continuationArg, stateOffsetNode);
         GenTree* stateInd        = m_comp->gtNewIndir(TYP_INT, stateAddr, GTF_IND_NONFAULTING);
@@ -1127,9 +1119,8 @@ void Async2Transformation::CreateResumptionSwitch()
         m_comp->fgAddRefPred(checkILOffsetBB->GetJumpDest(), checkILOffsetBB);
 
         // We need to dispatch to the OSR version if the IL offset is non-negative.
-        continuationArg = m_comp->gtNewLclvNode(m_comp->lvaAsyncContinuationArg, TYP_REF);
-        unsigned offsetOfData =
-            TARGET_POINTER_SIZE + m_comp->info.compCompHnd->getFieldOffset(m_async2Info.continuationDataFldHnd);
+        continuationArg           = m_comp->gtNewLclvNode(m_comp->lvaAsyncContinuationArg, TYP_REF);
+        unsigned offsetOfData     = m_comp->info.compCompHnd->getFieldOffset(m_async2Info.continuationDataFldHnd);
         GenTree* dataArr          = LoadFromOffset(continuationArg, offsetOfData, TYP_REF);
         unsigned offsetOfIlOffset = OFFSETOF__CORINFO_Array__data;
         GenTree* ilOffset         = LoadFromOffset(dataArr, offsetOfIlOffset, TYP_INT);
@@ -1168,9 +1159,8 @@ void Async2Transformation::CreateResumptionSwitch()
         m_comp->fgAddRefPred(checkILOffsetBB->Next(), checkILOffsetBB);
         m_comp->fgAddRefPred(checkILOffsetBB->GetJumpDest(), checkILOffsetBB);
 
-        continuationArg = m_comp->gtNewLclvNode(m_comp->lvaAsyncContinuationArg, TYP_REF);
-        unsigned offsetOfData =
-            TARGET_POINTER_SIZE + m_comp->info.compCompHnd->getFieldOffset(m_async2Info.continuationDataFldHnd);
+        continuationArg           = m_comp->gtNewLclvNode(m_comp->lvaAsyncContinuationArg, TYP_REF);
+        unsigned offsetOfData     = m_comp->info.compCompHnd->getFieldOffset(m_async2Info.continuationDataFldHnd);
         GenTree* dataArr          = LoadFromOffset(continuationArg, offsetOfData, TYP_REF);
         unsigned offsetOfIlOffset = OFFSETOF__CORINFO_Array__data;
         GenTree* ilOffset         = LoadFromOffset(dataArr, offsetOfIlOffset, TYP_INT);
diff --git a/src/coreclr/vm/corelib.h b/src/coreclr/vm/corelib.h
index e0bf62ea9cdb00..5e83f69b6c0fb4 100644
--- a/src/coreclr/vm/corelib.h
+++ b/src/coreclr/vm/corelib.h
@@ -685,7 +685,7 @@ DEFINE_METHOD(RUNTIME_HELPERS,      ALLOC_TAILCALL_ARG_BUFFER, AllocTailCallArgB
 DEFINE_METHOD(RUNTIME_HELPERS,      GET_TAILCALL_INFO,      GetTailCallInfo, NoSig)
 DEFINE_METHOD(RUNTIME_HELPERS,      DISPATCH_TAILCALLS,     DispatchTailCalls, NoSig)
 DEFINE_METHOD(RUNTIME_HELPERS,      ALLOC_CONTINUATION,     AllocContinuation, NoSig)
-DEFINE_METHOD(RUNTIME_HELPERS,      FINALIZE_TASK_RETURNING_THUNK, FinalizeTaskReturningThunk, GM_Obj_RetTaskOfT)
+DEFINE_METHOD(RUNTIME_HELPERS,      FINALIZE_TASK_RETURNING_THUNK, FinalizeTaskReturningThunk, GM_Continuation_RetTaskOfT)
 
 DEFINE_METHOD(RUNTIME_HELPERS,      GET_OR_CREATE_RESUMPTION_DELEGATE, GetOrCreateResumptionDelegate, NoSig)
 DEFINE_METHOD(RUNTIME_HELPERS,      UNSAFE_AWAIT_AWAITER_FROM_RUNTIME_ASYNC_1, UnsafeAwaitAwaiterFromRuntimeAsync, GM_T_RetVoid)
@@ -988,7 +988,7 @@ DEFINE_METHOD(STUBHELPERS,          VALIDATE_BYREF,                     Validate
 DEFINE_METHOD(STUBHELPERS,          GET_STUB_CONTEXT,                   GetStubContext,                 SM_RetIntPtr)
 DEFINE_METHOD(STUBHELPERS,          LOG_PINNED_ARGUMENT,                LogPinnedArgument,              SM_IntPtr_IntPtr_RetVoid)
 DEFINE_METHOD(STUBHELPERS,          NEXT_CALL_RETURN_ADDRESS,           NextCallReturnAddress,          SM_RetIntPtr)
-DEFINE_METHOD(STUBHELPERS,          ASYNC2_CALL_CONTINUATION,           Async2CallContinuation,         SM_RetObj)
+DEFINE_METHOD(STUBHELPERS,          ASYNC2_CALL_CONTINUATION,           Async2CallContinuation,         SM_RetContinuation)
 DEFINE_METHOD(STUBHELPERS,          SAFE_HANDLE_ADD_REF,    SafeHandleAddRef,           SM_SafeHandle_RefBool_RetIntPtr)
 DEFINE_METHOD(STUBHELPERS,          SAFE_HANDLE_RELEASE,    SafeHandleRelease,          SM_SafeHandle_RetVoid)
 
diff --git a/src/coreclr/vm/jitinterface.cpp b/src/coreclr/vm/jitinterface.cpp
index 8503c54e6d9eba..ef51840577dd81 100644
--- a/src/coreclr/vm/jitinterface.cpp
+++ b/src/coreclr/vm/jitinterface.cpp
@@ -14389,7 +14389,7 @@ CORINFO_METHOD_HANDLE CEEJitInfo::getAsyncResumptionStub()
         pCode->EmitSTLOC(resultLoc);
     }
 
-    DWORD newContinuationLoc = pCode->NewLocal(ELEMENT_TYPE_OBJECT);
+    DWORD newContinuationLoc = pCode->NewLocal(LocalDesc(CoreLibBinder::GetClass(CLASS__CONTINUATION)));
     pCode->EmitCALL(METHOD__STUBHELPERS__ASYNC2_CALL_CONTINUATION, 0, 1);
     pCode->EmitSTLOC(newContinuationLoc);
 
@@ -14401,11 +14401,9 @@ CORINFO_METHOD_HANDLE CEEJitInfo::getAsyncResumptionStub()
 
         // Load 'next' of current continuation
         pCode->EmitLDARG(0);
-        pCode->EmitCALL(METHOD__RUNTIME_HELPERS__GET_RAW_DATA, 1, 1);
         pCode->EmitLDFLD(FIELD__CONTINUATION__NEXT);
 
         // Load 'gcdata' of next continuation
-        pCode->EmitCALL(METHOD__RUNTIME_HELPERS__GET_RAW_DATA, 1, 1);
         pCode->EmitLDFLD(FIELD__CONTINUATION__GCDATA);
 
         // Now we have the GC array. At the first index is the result.
@@ -14434,13 +14432,34 @@ CORINFO_METHOD_HANDLE CEEJitInfo::getAsyncResumptionStub()
             &emptyCtx,
             &sl);
 
+    const char* optimizationTierName = nullptr;
+    switch (ncv.GetOptimizationTier())
+    {
+    case NativeCodeVersion::OptimizationTier0: optimizationTierName = "Tier0"; break;
+    case NativeCodeVersion::OptimizationTier1: optimizationTierName = "Tier1"; break;
+    case NativeCodeVersion::OptimizationTier1OSR: optimizationTierName = "Tier1OSR"; break;
+    case NativeCodeVersion::OptimizationTierOptimized: optimizationTierName = "Optimized"; break;
+    case NativeCodeVersion::OptimizationTier0Instrumented: optimizationTierName = "Tier0Instrumented"; break;
+    case NativeCodeVersion::OptimizationTier1Instrumented: optimizationTierName = "Tier1Instrumented"; break;
+    default: optimizationTierName = "UnknownTier"; break;
+    }
+
+    char name[256];
+    int numWritten = sprintf_s(name, "IL_STUB_AsyncResume_%s_%s", m_pMethodBeingCompiled->GetName(), optimizationTierName);
+    if (numWritten != -1)
+    {
+        AllocMemTracker pamTracker;
+        void* allocedMem = pamTracker.Track(m_pMethodBeingCompiled->GetLoaderAllocator()->GetLowFrequencyHeap()->AllocMem(S_SIZE_T(numWritten + 1)));
+        memcpy(allocedMem, name, (size_t)(numWritten + 1));
+        result->AsDynamicMethodDesc()->SetMethodName((LPCUTF8)allocedMem);
+        pamTracker.SuppressRelease();
+    }
+
 #ifdef _DEBUG
-    LOG((LF_STUBS, LL_INFO1000, "ASYNC: Resumption stub created\n"));
+    LOG((LF_STUBS, LL_INFO1000, "ASYNC: Resumption stub %s created\n", name));
     sl.LogILStub(CORJIT_FLAGS());
 #endif
 
-    result->DoPrestub(NULL);
-
     return CORINFO_METHOD_HANDLE(result);
 }
 
diff --git a/src/coreclr/vm/metasig.h b/src/coreclr/vm/metasig.h
index 15d27e8651390f..a960a2088dde31 100644
--- a/src/coreclr/vm/metasig.h
+++ b/src/coreclr/vm/metasig.h
@@ -624,8 +624,9 @@ DEFINE_METASIG(SM(PtrByte_RetStr, P(b), s))
 DEFINE_METASIG(SM(Str_RetPtrByte, s, P(b)))
 DEFINE_METASIG(SM(PtrByte_RetVoid, P(b), v))
 
+DEFINE_METASIG_T(SM(RetContinuation, , C(CONTINUATION)))
 DEFINE_METASIG(GM(T_RetVoid, IMAGE_CEE_CS_CALLCONV_DEFAULT, 1, M(0), v))
-DEFINE_METASIG_T(GM(Obj_RetTaskOfT, IMAGE_CEE_CS_CALLCONV_DEFAULT, 1, j, GI(C(TASK_1), 1, M(0))))
+DEFINE_METASIG_T(GM(Continuation_RetTaskOfT, IMAGE_CEE_CS_CALLCONV_DEFAULT, 1, C(CONTINUATION), GI(C(TASK_1), 1, M(0))))
 
 // Undefine macros in case we include the file again in the compilation unit
 
diff --git a/src/coreclr/vm/method.hpp b/src/coreclr/vm/method.hpp
index fa381ec6400209..656ad1dec80a8b 100644
--- a/src/coreclr/vm/method.hpp
+++ b/src/coreclr/vm/method.hpp
@@ -2617,6 +2617,12 @@ class DynamicMethodDesc : public StoredSigMethodDesc
         return m_pszMethodName;
     }
 
+    void SetMethodName(PTR_CUTF8 name)
+    {
+        LIMITED_METHOD_DAC_CONTRACT;
+        m_pszMethodName = name;
+    }
+
     // Based on the current flags, compute the equivalent as COR metadata.
     WORD GetAttrs() const
     {
diff --git a/src/coreclr/vm/prestub.cpp b/src/coreclr/vm/prestub.cpp
index 8d5f93c22a6630..abc25114fce66c 100644
--- a/src/coreclr/vm/prestub.cpp
+++ b/src/coreclr/vm/prestub.cpp
@@ -1624,7 +1624,7 @@ void MethodDesc::EmitJitStateMachineBasedRuntimeAsyncThunk(MethodDesc* pAsyncOth
     DWORD cbTargetSig;
     PCCOR_SIGNATURE pTargetSig = (PCCOR_SIGNATURE)targetSig.GetSignature(&cbTargetSig);
 
-    unsigned boxedContinuationLcl = pCode->NewLocal(ELEMENT_TYPE_OBJECT);
+    unsigned continuationLocal = pCode->NewLocal(LocalDesc(CoreLibBinder::GetClass(CLASS__CONTINUATION)));
 
     TypeHandle thTaskRet = thunkMsig.GetRetTypeHandleThrowing();
     LocalDesc returnLocalDesc(thTaskRet);
@@ -1672,7 +1672,7 @@ void MethodDesc::EmitJitStateMachineBasedRuntimeAsyncThunk(MethodDesc* pAsyncOth
         if (logicalResultLocal != UINT_MAX)
             pCode->EmitSTLOC(logicalResultLocal);
         pCode->EmitCALL(METHOD__STUBHELPERS__ASYNC2_CALL_CONTINUATION, 0, 1);
-        pCode->EmitSTLOC(boxedContinuationLcl);
+        pCode->EmitSTLOC(continuationLocal);
         pCode->EmitLEAVE(pNoExceptionLabel);
         pCode->EndTryBlock();
     }
@@ -1688,7 +1688,7 @@ void MethodDesc::EmitJitStateMachineBasedRuntimeAsyncThunk(MethodDesc* pAsyncOth
     }
 
     pCode->EmitLabel(pNoExceptionLabel);
-    pCode->EmitLDLOC(boxedContinuationLcl);
+    pCode->EmitLDLOC(continuationLocal);
     pCode->EmitBRTRUE(pSuspendedLabel);
     if (logicalResultLocal != UINT_MAX)
     {
@@ -1711,7 +1711,7 @@ void MethodDesc::EmitJitStateMachineBasedRuntimeAsyncThunk(MethodDesc* pAsyncOth
 
     MethodDesc* md = CoreLibBinder::GetMethod(METHOD__RUNTIME_HELPERS__FINALIZE_TASK_RETURNING_THUNK);
     md = FindOrCreateAssociatedMethodDesc(md, md->GetMethodTable(), FALSE, Instantiation(&thLogicalRetType, 1), FALSE);
-    pCode->EmitLDLOC(boxedContinuationLcl);
+    pCode->EmitLDLOC(continuationLocal);
     pCode->EmitCALL(pCode->GetToken(md), 1, 1);
     pCode->EmitRET();
 }
diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.cs
index e9e3930277e327..284064d1b13640 100644
--- a/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.cs
+++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.cs
@@ -133,9 +133,9 @@ public static void AwaitAwaiterFromRuntimeAsync<TAwaiter>(TAwaiter awaiter) wher
             if (RuntimeAsyncViaJitGeneratedStateMachines)
             {
                 ref RuntimeAsyncAwaitState state = ref t_runtimeAsyncAwaitState;
-                object? sentinelContinuation = state.SentinelContinuation;
+                Continuation? sentinelContinuation = state.SentinelContinuation;
                 if (sentinelContinuation == null)
-                    state.SentinelContinuation = sentinelContinuation = default(Continuation);
+                    state.SentinelContinuation = sentinelContinuation = new Continuation();
 
                 state.Notifier = awaiter;
                 SuspendAsync2(sentinelContinuation);
@@ -173,9 +173,9 @@ public static void UnsafeAwaitAwaiterFromRuntimeAsync<TAwaiter>(TAwaiter awaiter
             if (RuntimeAsyncViaJitGeneratedStateMachines)
             {
                 ref RuntimeAsyncAwaitState state = ref t_runtimeAsyncAwaitState;
-                object? sentinelContinuation = state.SentinelContinuation;
+                Continuation? sentinelContinuation = state.SentinelContinuation;
                 if (sentinelContinuation == null)
-                    state.SentinelContinuation = sentinelContinuation = default(Continuation);
+                    state.SentinelContinuation = sentinelContinuation = new Continuation();
 
                 state.Notifier = awaiter;
                 SuspendAsync2(sentinelContinuation);

From 6449b634216a2bdf89aae9f7ebb4911324b7fbd9 Mon Sep 17 00:00:00 2001
From: Jakob Botsch Nielsen <janielsen@microsoft.com>
Date: Mon, 30 Oct 2023 09:16:13 +0000
Subject: [PATCH 050/203] Merged PR 34889: Implement SPMI support for JIT state
 machine JIT<->EE APIs

FYI @<David Wrighton> @<Vladimir Sadov>
---
 .../tools/superpmi/superpmi-shared/agnostic.h | 10 ++++
 .../tools/superpmi/superpmi-shared/lwmlist.h  |  2 +
 .../superpmi-shared/methodcontext.cpp         | 54 +++++++++++++++++++
 .../superpmi/superpmi-shared/methodcontext.h  | 11 ++++
 .../superpmi-shim-collector/icorjitinfo.cpp   | 10 ++--
 .../tools/superpmi/superpmi/icorjitinfo.cpp   |  6 +--
 6 files changed, 87 insertions(+), 6 deletions(-)

diff --git a/src/coreclr/tools/superpmi/superpmi-shared/agnostic.h b/src/coreclr/tools/superpmi/superpmi-shared/agnostic.h
index 82bcb14d3a169d..6b3c79a9108934 100644
--- a/src/coreclr/tools/superpmi/superpmi-shared/agnostic.h
+++ b/src/coreclr/tools/superpmi/superpmi-shared/agnostic.h
@@ -192,6 +192,16 @@ struct Agnostic_CORINFO_EE_INFO
     DWORD osType;
 };
 
+struct Agnostic_CORINFO_ASYNC2_INFO
+{
+    DWORDLONG continuationClsHnd;
+    DWORDLONG continuationNextFldHnd;
+    DWORDLONG continuationResumeFldHnd;
+    DWORDLONG continuationStateFldHnd;
+    DWORDLONG continuationDataFldHnd;
+    DWORDLONG continuationGCDataFldHnd;
+};
+
 struct Agnostic_GetOSRInfo
 {
     DWORD index;
diff --git a/src/coreclr/tools/superpmi/superpmi-shared/lwmlist.h b/src/coreclr/tools/superpmi/superpmi-shared/lwmlist.h
index c09cd84b059e4d..9b32267c02cbcd 100644
--- a/src/coreclr/tools/superpmi/superpmi-shared/lwmlist.h
+++ b/src/coreclr/tools/superpmi/superpmi-shared/lwmlist.h
@@ -76,6 +76,7 @@ LWM(GetDefaultComparerClass, DWORDLONG, DWORDLONG)
 LWM(GetDefaultEqualityComparerClass, DWORDLONG, DWORDLONG)
 LWM(GetDelegateCtor, Agnostic_GetDelegateCtorIn, Agnostic_GetDelegateCtorOut)
 LWM(GetEEInfo, DWORD, Agnostic_CORINFO_EE_INFO)
+LWM(GetAsync2Info, DWORD, Agnostic_CORINFO_ASYNC2_INFO)
 LWM(GetEHinfo, DLD, Agnostic_CORINFO_EH_CLAUSE)
 LWM(GetStaticFieldContent, DLDDD, DD)
 LWM(GetObjectContent, DLDD, DD)
@@ -127,6 +128,7 @@ LWM(GetSystemVAmd64PassStructInRegisterDescriptor, DWORDLONG, Agnostic_GetSystem
 LWM(GetLoongArch64PassStructInRegisterFlags, DWORDLONG, DWORD)
 LWM(GetRISCV64PassStructInRegisterFlags, DWORDLONG, DWORD)
 LWM(GetTailCallHelpers, Agnostic_GetTailCallHelpers, Agnostic_CORINFO_TAILCALL_HELPERS)
+LWM(GetAsyncResumptionStub, DWORD, DWORDLONG)
 LWM(UpdateEntryPointForTailCall, Agnostic_CORINFO_CONST_LOOKUP, Agnostic_CORINFO_CONST_LOOKUP)
 LWM(GetThreadTLSIndex, DWORD, DLD)
 LWM(GetTokenTypeAsHandle, GetTokenTypeAsHandleValue, DWORDLONG)
diff --git a/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.cpp b/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.cpp
index a7c6d9b9d3fabc..2067399f4d9425 100644
--- a/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.cpp
+++ b/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.cpp
@@ -4319,6 +4319,41 @@ void MethodContext::repGetEEInfo(CORINFO_EE_INFO* pEEInfoOut)
     }
 }
 
+void MethodContext::recGetAsync2Info(const CORINFO_ASYNC2_INFO* pAsync2Info)
+{
+    if (GetAsync2Info == nullptr)
+        GetAsync2Info = new LightWeightMap<DWORD, Agnostic_CORINFO_ASYNC2_INFO>();
+
+    Agnostic_CORINFO_ASYNC2_INFO value;
+    value.continuationClsHnd = CastHandle(pAsync2Info->continuationClsHnd);
+    value.continuationNextFldHnd = CastHandle(pAsync2Info->continuationNextFldHnd);
+    value.continuationResumeFldHnd = CastHandle(pAsync2Info->continuationResumeFldHnd);
+    value.continuationStateFldHnd = CastHandle(pAsync2Info->continuationStateFldHnd);
+    value.continuationDataFldHnd = CastHandle(pAsync2Info->continuationDataFldHnd);
+    value.continuationGCDataFldHnd = CastHandle(pAsync2Info->continuationGCDataFldHnd);
+
+    GetAsync2Info->Add(0, value);
+    DEBUG_REC(dmpGetAsync2Info(0, value));
+}
+void MethodContext::dmpGetAsync2Info(DWORD key, const Agnostic_CORINFO_ASYNC2_INFO& value)
+{
+    printf("GetAsync2Info key %u value contClsHnd-%016" PRIX64 " contNextFldHnd-%016" PRIX64 " contResumeFldHnd-%016" PRIX64
+           " contStateFldHnd-%016" PRIX64 " contDataFldHnd-%016" PRIX64 " contGCDataFldHnd-%016" PRIX64,
+        key, value.continuationClsHnd, value.continuationNextFldHnd, value.continuationResumeFldHnd,
+        value.continuationStateFldHnd, value.continuationDataFldHnd, value.continuationGCDataFldHnd);
+}
+void MethodContext::repGetAsync2Info(CORINFO_ASYNC2_INFO* pAsync2InfoOut)
+{
+    Agnostic_CORINFO_ASYNC2_INFO value = LookupByKeyOrMissNoMessage(GetAsync2Info, 0);
+    pAsync2InfoOut->continuationClsHnd = (CORINFO_CLASS_HANDLE)value.continuationClsHnd;
+    pAsync2InfoOut->continuationNextFldHnd = (CORINFO_FIELD_HANDLE)value.continuationNextFldHnd;
+    pAsync2InfoOut->continuationResumeFldHnd = (CORINFO_FIELD_HANDLE)value.continuationResumeFldHnd;
+    pAsync2InfoOut->continuationStateFldHnd = (CORINFO_FIELD_HANDLE)value.continuationStateFldHnd;
+    pAsync2InfoOut->continuationDataFldHnd = (CORINFO_FIELD_HANDLE)value.continuationDataFldHnd;
+    pAsync2InfoOut->continuationGCDataFldHnd = (CORINFO_FIELD_HANDLE)value.continuationGCDataFldHnd;
+    DEBUG_REP(dmpGetAsync2Info(0, value));
+}
+
 void MethodContext::recGetGSCookie(GSCookie* pCookieVal, GSCookie** ppCookieVal)
 {
     if (GetGSCookie == nullptr)
@@ -6665,6 +6700,25 @@ bool MethodContext::repGetTailCallHelpers(
     return true;
 }
 
+
+void MethodContext::recGetAsyncResumptionStub(CORINFO_METHOD_HANDLE hnd)
+{
+    if (GetAsyncResumptionStub == nullptr)
+        GetAsyncResumptionStub = new LightWeightMap<DWORD, DWORDLONG>();
+
+    GetAsyncResumptionStub->Add(0, CastHandle(hnd));
+    DEBUG_REC(dmpGetAsyncResumptionStub(CastHandle(hnd)));
+}
+void MethodContext::dmpGetAsyncResumptionStub(DWORD key, DWORDLONG hnd)
+{
+    printf("GetAsyncResumptionStub key-%u, value-%016" PRIX64, key, hnd);
+}
+CORINFO_METHOD_HANDLE MethodContext::repGetAsyncResumptionStub()
+{
+    DWORDLONG hnd = LookupByKeyOrMissNoMessage(GetAsyncResumptionStub, 0);
+    return (CORINFO_METHOD_HANDLE)hnd;
+}
+
 void MethodContext::recUpdateEntryPointForTailCall(
     const CORINFO_CONST_LOOKUP& origEntryPoint,
     const CORINFO_CONST_LOOKUP& newEntryPoint)
diff --git a/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.h b/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.h
index 2af725f63f7aec..beab74ccbddd92 100644
--- a/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.h
+++ b/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.h
@@ -545,6 +545,10 @@ class MethodContext
     void dmpGetEEInfo(DWORD key, const Agnostic_CORINFO_EE_INFO& value);
     void repGetEEInfo(CORINFO_EE_INFO* pEEInfoOut);
 
+    void recGetAsync2Info(const CORINFO_ASYNC2_INFO* pAsync2Info);
+    void dmpGetAsync2Info(DWORD key, const Agnostic_CORINFO_ASYNC2_INFO& value);
+    void repGetAsync2Info(CORINFO_ASYNC2_INFO* pAsync2InfoOut);
+
     void recGetGSCookie(GSCookie* pCookieVal, GSCookie** ppCookieVal);
     void dmpGetGSCookie(DWORD key, DLDL value);
     void repGetGSCookie(GSCookie* pCookieVal, GSCookie** ppCookieVal);
@@ -828,6 +832,10 @@ class MethodContext
         CORINFO_GET_TAILCALL_HELPERS_FLAGS flags,
         CORINFO_TAILCALL_HELPERS* pResult);
 
+    void recGetAsyncResumptionStub(CORINFO_METHOD_HANDLE hnd);
+    void dmpGetAsyncResumptionStub(DWORD key, DWORDLONG handle);
+    CORINFO_METHOD_HANDLE repGetAsyncResumptionStub();
+
     void recUpdateEntryPointForTailCall(const CORINFO_CONST_LOOKUP& origEntryPoint, const CORINFO_CONST_LOOKUP& newEntryPoint);
     void dmpUpdateEntryPointForTailCall(const Agnostic_CORINFO_CONST_LOOKUP& origEntryPoint, const Agnostic_CORINFO_CONST_LOOKUP& newEntryPoint);
     void repUpdateEntryPointForTailCall(CORINFO_CONST_LOOKUP* entryPoint);
@@ -1138,6 +1146,9 @@ enum mcPackets
     Packet_GetObjectContent = 210,
     Packet_GetTypeLayout = 211,
     Packet_HaveSameMethodDefinition = 212,
+
+    Packet_GetAsync2Info = 249,
+    Packet_GetAsyncResumptionStub = 250,
 };
 
 void SetDebugDumpVariables();
diff --git a/src/coreclr/tools/superpmi/superpmi-shim-collector/icorjitinfo.cpp b/src/coreclr/tools/superpmi/superpmi-shim-collector/icorjitinfo.cpp
index b8f58508824110..5d38547ea147ac 100644
--- a/src/coreclr/tools/superpmi/superpmi-shim-collector/icorjitinfo.cpp
+++ b/src/coreclr/tools/superpmi/superpmi-shim-collector/icorjitinfo.cpp
@@ -1308,7 +1308,9 @@ void interceptor_ICJI::getEEInfo(CORINFO_EE_INFO* pEEInfoOut)
 
 void interceptor_ICJI::getAsync2Info(CORINFO_ASYNC2_INFO* pAsync2Info)
 {
-    // TODO
+    mc->cr->AddCall("getAsync2Info");
+    original_ICorJitInfo->getAsync2Info(pAsync2Info);
+    mc->recGetAsync2Info(pAsync2Info);
 }
 
 // Returns name of the JIT timer log
@@ -1730,8 +1732,10 @@ bool interceptor_ICJI::getTailCallHelpers(
 
 CORINFO_METHOD_HANDLE interceptor_ICJI::getAsyncResumptionStub()
 {
-    // TODO
-    return NULL;
+    mc->cr->AddCall("getAsyncResumptionStub");
+    CORINFO_METHOD_HANDLE stub = original_ICorJitInfo->getAsyncResumptionStub();
+    mc->recGetAsyncResumptionStub(stub);
+    return stub;
 }
 
 void interceptor_ICJI::updateEntryPointForTailCall(CORINFO_CONST_LOOKUP* entryPoint)
diff --git a/src/coreclr/tools/superpmi/superpmi/icorjitinfo.cpp b/src/coreclr/tools/superpmi/superpmi/icorjitinfo.cpp
index f476ee8186bd16..48b9d5233c592a 100644
--- a/src/coreclr/tools/superpmi/superpmi/icorjitinfo.cpp
+++ b/src/coreclr/tools/superpmi/superpmi/icorjitinfo.cpp
@@ -1119,7 +1119,8 @@ void MyICJI::getEEInfo(CORINFO_EE_INFO* pEEInfoOut)
 
 void MyICJI::getAsync2Info(CORINFO_ASYNC2_INFO* pAsync2Info)
 {
-    // TODO
+    jitInstance->mc->cr->AddCall("getAsync2Info");
+    jitInstance->mc->repGetAsync2Info(pAsync2Info);
 }
 
 // Returns name of the JIT timer log
@@ -1473,8 +1474,7 @@ bool MyICJI::getTailCallHelpers(
 CORINFO_METHOD_HANDLE MyICJI::getAsyncResumptionStub()
 {
     jitInstance->mc->cr->AddCall("getAsyncResumptionStub");
-    // TODO
-    return NULL;
+    return jitInstance->mc->repGetAsyncResumptionStub();;
 }
 
 bool MyICJI::convertPInvokeCalliToCall(CORINFO_RESOLVED_TOKEN* pResolvedToken, bool fMustConvert)

From 00519cf29ef447a5ed71fdae01de40686b644e5b Mon Sep 17 00:00:00 2001
From: Jakob Botsch Nielsen <janielsen@microsoft.com>
Date: Mon, 30 Oct 2023 19:37:07 +0000
Subject: [PATCH 051/203] Merged PR 34910: Update and merge design document for
 JIT generated state machines into David's document

Also delete the old document.
---
 ...async-with-jit-generated-state-machines.md | 481 ------------------
 docs/design/features/runtime-handled-tasks.md | 271 +++++++++-
 2 files changed, 269 insertions(+), 483 deletions(-)
 delete mode 100644 docs/design/features/async-with-jit-generated-state-machines.md

diff --git a/docs/design/features/async-with-jit-generated-state-machines.md b/docs/design/features/async-with-jit-generated-state-machines.md
deleted file mode 100644
index 56b65068ce30b1..00000000000000
--- a/docs/design/features/async-with-jit-generated-state-machines.md
+++ /dev/null
@@ -1,481 +0,0 @@
-# Async with JIT generated state machines
-
-## Introduction
-
-This document describes a potential way to move the async transformation from Roslyn to the JIT.
-The approach described here has the JIT transform async methods into a state machine in a way that such async methods can both suspend and be resumed.
-As a goal, the design strives to make modifications such that the impact on the JIT's codegen of the synchronous path is minimized.
-
-To achieve that, we design around a more or less standard call/return stack pattern in the synchronous case, and then build on top of it the necessary infrastructure to handle resumption.
-
-## Dispatching continuations
-
-The main complication happens when continuations are to be dispatched.
-When that occurs the stack logically inverts compared to the normal synchronous call: the deepest frame's continuation has to be called first, and once it returns, it has to ensure that it calls the next continuation.
-There are multiple issues that the machinery today has to handle around this:
-1. It has to move continuations to other stack frames to ensure the stack is not exhausted
-2. It has to propagate exceptions thrown inside later frames to the next continuations; it cannot rely on normal EH to propagate exceptions, since the logical callers are no longer on the stack.
-
-I expect that 2. has some impact on the quality of the JIT generated code since the JIT can be more conservative inside EH constructs; however, the impact may not be that high given that much of the state is not expected to be live in the handler.
-
-To solve both issues we propose using an an async dispatcher.
-The idea is to move the responsibility of invoking follow-up continuations from the async method to the async dispatcher.
-This is very similar to the current tailcall via helpers approach.
-
-The async dispatcher will look something like (details omitted, full version of this is given below):
-
-```csharp
-enum Suspended
-{
-    Yes,
-    No,
-}
-
-struct Continuation
-{
-    public object Next;
-    public delegate*<object, Exception, Suspended> Resume;
-    public int State;
-}
-
-public static void DispatchContinuations(object boxedContinuation, Exception exToPropagate)
-{
-    do
-    {
-        ref Continuation continuation = ref Unsafe.Unbox<Continuation>(boxedContinuation);
-        try
-        {
-            if (continuation.Resume(boxedContinuation, exToPropagate) == Suspended.Yes)
-            {
-                break;
-            }
-        }
-        catch (Exception ex)
-        {
-            exToPropagate = ex;
-        }
-
-        boxedContinuation = continuation.Next;
-    } while (boxedContinuation != null);
-}
-```
-
-The first time an asynchronous continuation is resumed it is expected that such a dispatcher is set up on the stack frame by the code doing the actual asynchronous resumption (for example, a socket receive callback).
-
-The JIT must set up all the stuff to make the above dispatching work.
-1. It has to generate code to create a continuation when suspension happens.
-   Logically, the continuation captures the live state of the function that is suspending, including some way to figure out where in the function suspension happened.
-   Furthermore, the continuation must somehow be registered with the callee to know what to call when it is resuming.
-   
-2. It has to generate code to allow resuming from a continuation.
-   This involves:
-   1. Determining where in the function control needs to be transferred back to.
-      It may not always be possible to directly transfer control back, in particular when the function has EH, in which case we can employ a "switchboard" approach at the beginning of each `try` (OSR already must do this).
-   2. Based on the above, determining what we expect to have been saved in the continuation and restoring registers/stack slots from it.
-   3. If any resumption exception is present, invoking necessary EH constructs based on it. 
-      The simple way to do this is just to rethrow the exception after transferring control back to the suspension point.
-      Optimizations here may be possible, for example by having the dispatcher skip continuations without EH.
-
-The continuation also saves space for the return value of the callee.
-
-## Resumption stubs
-We expect to split the second step above into two components.
-1. A part that is handled by the JIT as part of generating the async method and that becomes part of that function's codegen.
-   We'd like to ensure this part has minimal negative impact on the CQ of the async method to not harm the common synchronous path.
-   This code will involve reloading registers and stack locations, and transferring control back to the right place in the function.
-   It will mean the async methods have a special calling convention to identify whether they are being resumed or whether they are being invoked normally.
-2. The second part is an IL stub created by the runtime on request of the JIT.
-   The continuations created by the JIT will point at such a resumption stub.
-   The resumption stubs are necessary because of the following:
-   1. When the function is resumed, something must allocate stack space for the arguments and set registers containing arguments to sensible values, for stack scanning/GC purposes
-   2. If the resumed function returns normally, then something must propagate the return value back to the next continuation, if required
-   3. Since the async method has a special calling convention for resumption, something needs to know how to call it specially and where to pass the continuation.
-
-## Continuation layout
-The `Continuation` struct shown above will be a prefix of the continuation struct created by the JIT.
-For example, for the following case:
-
-```csharp
-static async2 void Foo(int a, int b)
-{
-    BarReturn ret = await Bar();
-    b += ret.val + a;
-    int ret2 = await Baz();
-    Console.WriteLine(b);
-}
-
-// uninlined
-static async2 BarReturn Bar()
-{
-    return new BarReturn {val = 0};
-}
-
-// uninlined
-static async2 int Baz()
-{
-    await Yield();
-    return 0;
-}
-```
-
-The runtime/JIT would create these continuation structures:
-
-```csharp
-struct ContinuationFoo1
-{
-    public object Next;
-    public delegate*<ref ContinuationFoo1, Exception, object> Resume;
-    public int State;
-    public BarReturn Returned;
-    public int a;
-    public int b;
-}
-
-struct ContinuationFoo2
-{
-    public object Next;
-    public delegate*<ref ContinuationFoo2, Exception, object> Resume;
-    public int State;
-    public int Returned;
-    // int a; not necessary since a is not live here
-    public int b;
-}
-
-struct ContinuationBaz1
-{
-    public object Next;
-    public delegate*<ref ContinuationBaz1, Exception, object> Resume;
-    public int State;
-}
-```
-
-and the resumption stubs may look like:
-```csharp
-object ResumeFoo1(object continuation, Exception exToPropagate)
-{
-    object newContinuation = null;
-    // No return value to propagate back since 'Foo' returns void
-    RuntimeHelpers.Async2Call(ref newContinuation);
-    Foo();
-    return newContinuation;
-}
-
-object ResumeBaz1(object continuation, Exception exToPropagate)
-{
-    object newContinuation = null;
-    RuntimeHelpers.Async2Call(ref newContinuation);
-    int bazResult = Baz();
-    if (newContinuation == null)
-    {
-        // Points to ContinuationFoo2 always in the example above
-        Unsafe.Unbox<ContinuationWithResult<int>>(continuation.Next).Result = bazResult;
-    }
-
-    return newContinuation;
-}
-
-struct ContinuationWithResult<T>
-{
-    public object Next;
-    public delegate*<ref ContinuationWithResult<T>, Exception, object> Resume;
-    public int State;
-    public T Result;
-}
-```
-
-The continuations contain GC pointers, so we will need some way to create these types dynamically, with their corresponding GC info.
-They will be lifted to the heap by boxing.
-
-## Custom calling convention
-
-To call the new async methods, a custom calling convention is necessary for two reasons:
-1. The async method needs the continuation in case it is being resumed, or null in case it isn't
-2. The async method returns its boxed continuation to the caller in case it suspended.
-
-For 1 we can model it after generic contexts by adding another argument, or we can use a special register (this might be problematic on arm32).
-For 2 we expect to return the box in a separate register.
-
-`RuntimeHelpers.CallAsync2` used above is a JIT intrinsic that allows IL code to interface with the new calling convention.
-It has signature:
-```csharp
-[Intrinsic]
-internal static T CallAsync2<T>(T callResult, ref Continuation continuation, out object newContinuation)
-```
-
-IL that calls this intrinsic is only considered valid if the first argument was pushed as a direct result of a `call`, `calli` or `callvirt` instruction.
-
-To interface with the new calling convention on the return side we also provide `RuntimeHelpers.Async2Return`:
-```csharp
-[Intrinsic]
-internal static T Async2Return<T>(T result, object continuation);
-
-[Intrinsic]
-internal static void Async2Return(object continuation);
-```
-
-`RuntimeHelpers.Async2Return` must appear right before a `ret` IL instruction.
-
-Note that normal returns in an async2 method are equivalent to `return RuntimeHelpers.Async2Return(result, null)`.`
-
-## Synchronous calls
-For synchronous calls we pay the following costs:
-* On entry to the function, we must check if there is a continuation, meaning that we are resuming.
-  This cost can probably be avoided in common cases if we build some support for generating multiple entry points to a function in the JIT/VM.
-* To call an async method we must pass a zero continuation.
-* On return of an async method we must check whether it suspended or not.
-* For functions with suspension points inside a a `try` block, we may pay additional cost at the beginning of every `try` and `finally` block.
-  These costs may be avoidable with more JIT work.
-
-## Interoperating with async2 calls
-To integrate async2 with the existing ecosystem we need to be able to make calls between async2 and async1 methods.
-Async1 in this case refers to anything the C# compiler would consider awaitable; however, here we reduce the scope and consider only `Task`, `Task<T>`, `ValueTask` and `ValueTask<T>`.
-
-Calling async1 methods is done through the runtime functions described in [David's document](https://dev.azure.com/dnceng/internal/_git/dotnet-runtime?path=/docs/design/features/runtime-handled-tasks.md&version=GBdev/davidwr/async2-experiment&_a=preview):
-```csharp
-public interface ICriticalNotifyCompletion2 : ICriticalNotifyCompletion
-{
-    bool IsCompleted { get; }
-    void GetResult();
-}
-public interface ICriticalNotifyCompletion2<TResult> : ICriticalNotifyCompletion
-{
-    bool IsCompleted { get; }
-    TResult GetResult();
-}
-public interface INotifyCompletion2 : INotifyCompletion
-{
-    bool IsCompleted { get; }
-    void GetResult();
-}
-public interface INotifyCompletion2<TResult> : INotifyCompletion
-{
-    bool IsCompleted { get; }
-    TResult GetResult();
-}
-
-public static async2 void AwaitAwaiterFromRuntimeAsync<TAwaiter>(TAwaiter awaiter) where TAwaiter : INotifyCompletion2
-public static async2 TResult AwaitAwaiterFromRuntimeAsync<TResult, TAwaiter>(TAwaiter awaiter) where TAwaiter : INotifyCompletion2<TResult>
-public static async2 void UnsafeAwaitAwaiterFromRuntimeAsync<TAwaiter>(TAwaiter awaiter) where TAwaiter : ICriticalNotifyCompletion2
-public static async2 TResult UnsafeAwaitAwaiterFromRuntimeAsync<TResult, TAwaiter>(TAwaiter awaiter) where TAwaiter : ICriticalNotifyCompletion2<TResult>
-```
-
-Calling an async2 method follows the same principles as in David's document, i.e. a thunk is created to wrap it in a Task<T>.
-
-When a function suspends the JIT will link all continuations into a chain as the call stack unwinds.
-To make this thread safe with `INotifyCompletion` we must delay calling `OnCompleted` until the continuations have all been linked.
-Thus, the caller will be responsible for doing this; it is either done by the dispatcher, or done as part of the async1 -> async2 thunk.
-
-The following shows an example of what the async2 -> async1 adapting layer may look like.
-It conceptually just stores the awaiter and prepares for the caller to call `OnCompleted`.
-
-```csharp
-internal struct RuntimeAsyncAwaitState
-{
-    public Async2DispatcherBase Dispatcher;
-    public object SentinelContinuation;
-}
-
-[ThreadStatic]
-public static RuntimeAsyncAwaitState t_runtimeAsyncAwait;
-
-public static async2 TResult UnsafeAwaitAwaiterFromRuntimeAsync<TResult, TAwaiter>(TAwaiter awaiter) where TAwaiter : ICriticalNotifyCompletion2<TResult>
-{
-    if (awaiter.IsCompleted)
-        return awaiter.GetResult();
-
-    ref RuntimeAsyncAwaitState runtimeAsyncAwait = ref t_runtimeAsyncAwait;
-
-    if (runtimeAsyncAwait.SentinelContinuation == null)
-        runtimeAsyncAwait.SentinelContinuation = new Continuation();
-
-    runtimeAsyncAwait.Dispatcher = new Async2Dispatcher<TResult, TAwaiter>(awaiter);
-    return RuntimeHelpers.Async2Return(default, runtimeAsyncAwait.SentinelContinuation);
-}
-
-internal abstract class Async2DispatcherBase
-{
-    public abstract void Setup(object continuation);
-}
-
-internal sealed class Async2Dispatcher<TResult, TAwaiter> : Async2DispatcherBase where TAwaiter : ICriticalNotifyCompletion2<TResult>
-{
-    private readonly object _continuation;
-    private readonly TAwaiter _awaiter;
-
-    public Async2Dispatcher(TAwaiter awaiter)
-    {
-        _awaiter = awaiter;
-    }
-
-    public override void Setup(object continuation)
-    {
-        _continuation = continuation;
-        _awaiter.UnsafeOnCompleted(Run);
-    }
-
-    private void Run()
-    {
-        // Omitted
-    }
-}
-```
-
-The function returns a sentinel continuation which the JIT will write the first continuation into such that the caller knows where the chain starts.
-
-The remaining part is to describe what the async1 -> async2 adapter layer looks like.
-It consists of the following:
-* The thunk that calls an async2 method and produces a Task<T>
-* The `Async2Dispatcher.Run` method that is the callback of the asynchronous operation
-* The final implementation of the dispatcher
-
-Let us start with the thunk:
-```csharp
-public static Task<T> ThunkAsync<T>(ParameterType1 param1, ParameterType2 param2, ...)
-{
-    object boxedContinuation;
-    T result;
-    try
-    {
-        result = RuntimeHelpers.CallAsync2(TargetMethod(param1, param2, ...), ref Unsafe.NullRef<Continuation>(), out boxedContinuation);
-    }
-    catch (Exception ex)
-    {
-        return Task.FromException(ex);
-    }
-
-    if (boxedContinuation == null)
-        return Task.FromResult(result);
-
-    // Link in a final continuation that fires the TCS.
-    TaskCompletionSource<T> tcs = new();
-    ref Continuation continuation = ref Unsafe.Unbox<Continuation>(boxedContinuation);
-    continuation.Next = new TaskCompletionSourceContinuation<T>
-    {
-        Resume = &ResumeTaskCompletionSource<T>,
-        TCS = tcs,
-    };
-
-    // Now call OnCompleted on the awaiter we hit in the leaf.
-    ref RuntimeAsyncAwaitState runtimeAsyncAwait = ref t_runtimeAsyncAwait;
-    object headContinuation = Unsafe.Unbox<Continuation>(runtimeAsyncAwait.SentinelContinuation).Next;
-    runtimeAsyncAwait.Dispatcher.Setup(headContinuation);
-    return tcs.Task;
-}
-
-private static object ResumeTaskCompletionSource<T>(ref TaskCompletionSourceContinuation<T> continuation, Exception ex)
-{
-    if (ex == null)
-    {
-        continuation.TCS.SetResult(continuation.Result);
-    }
-    else
-    {
-        continuation.TCS.SetException(ex);
-    }
-
-    return null;
-}
-
-public struct TaskCompletionSourceContinuation<T>
-{
-    public object Next;
-    public delegate*<ref TaskCompletionSourceContinuation<T>, Exception, object> Resume;
-    public int State;
-    public T Result;
-    public TaskCompletionSource<T> TCS;
-}
-```
-The thunk has a fast path in case the async2 method finished synchronously.
-If not then the function creates a TCS and links in a synthetic continuation that will invoke this TCS.
-It retrieves the head continuation (which is the successor of the sentinel continuation that was saved in the leaf), and finally it calls `Async2DispatcherBase.Setup` which is responsible for actually calling `OnCompleted` on the saved awaiter.
-
-The implementation of `Async2Dispatcher<TResult, TAwaiter>.Run` was omitted above.
-This is the first function called by the asynchronous callback, and it needs to propagate the result into the continuation and start running the dispatcher.
-```csharp
-internal sealed class Async2Dispatcher<TResult, TAwaiter> : Async2DispatcherBase where TAwaiter : ICriticalNotifyCompletion2<TResult>
-{
-    private readonly object _continuation;
-    private readonly TAwaiter _awaiter;
-
-    private void Run()
-    {
-        Exception exToPropagate = null;
-        try
-        {
-            TResult result = _awaiter.GetResult();
-            Unsafe.Unbox<ContinuationWithResult<TResult>>(_continuation).Result = result;
-        }
-        catch (Exception ex)
-        {
-            exToPropagate = ex;
-        }
-
-        RuntimeHelpers.DispatchContinuations(_continuation, exToPropagate);
-    }
-}
-```
-
-Finally, the dispatcher must be expanded to also handle registering the continuation chain for the asynchronous callback, in case a continuation suspends one more time:
-```csharp
-public static void DispatchContinuations(object boxedContinuation, Exception exToPropagate)
-{
-    do
-    {
-        ref Continuation continuation = ref Unsafe.Unbox<Continuation>(boxedContinuation);
-        try
-        {
-            object newContinuation = continuation.Resume(ref continuation, exToPropagate);
-
-            if (newContinuation != null)
-            {
-                Unsafe.Unbox<Continuation>(newContinuation).Next = continuation.Next;
-
-                ref RuntimeAsyncAwaitState runtimeAsyncAwait = ref t_runtimeAsyncAwait;
-                object headContinuation = Unsafe.Unbox<Continuation>(runtimeAsyncAwait.SentinelContinuation).Next;
-                runtimeAsyncAwait.Dispatcher.Setup(headContinuation);
-                return;
-            }
-        }
-        catch (Exception ex)
-        {
-            exToPropagate = ex;
-        }
-
-        boxedContinuation = continuation.Next;
-    } while (boxedContinuation != null);
-}
-```
-It also needs to transfer the next continuation stored in the previous (now invoked continuation) to the newly returned one.
-
-## Thoughts/questions
-
-1. Is it better to allocate a "mega"-continuation and reuse it? It will mean less type loader traffic, but more stuff kept on the heap overall.
-It will also allow us to skip transferring the "Next" from the previous continuation when a resumed continuation suspends again
-2. We know address-exposed locals become unexposed at suspension points.
-How do we make use of this? Likely we need a custom liveness pass.
-
-## Required JIT work
-
-1. A pass that walks the IR and finds all awaited async calls.
-   At these points we must identify live state (so run some kind of liveness) and generate code to create continuations in case the callee suspends.
-   We must also create the IR that restores the live state from the continuation, and throw the exception.
-3. Prolog work to handle "resume" vs "start".
-4. Work to handle suspension and resumption within `try`
-
-Some things to consider:
-* Should the pass happen early or late? Early means less live state, but likely impact on CQ.
-  In particular the resumption code is going to create a lot of synthetic control flow that I expect would have significant CQ impact.
-  Doing it late will probably make it harder to introduce the boxing and generate the registration IR.
-  Also, doing it later means more chance for optimizations to create illegal constructs (e.g. local addresses that span suspension points).
-* Doing it late may allow us to simply jump directly into nested `try` blocks and exit `try` blocks without invoking finallies, though I am not sure about that.
-* We need to be able to introduce control flow around awaited async calls.
-  If done early we can use `gtSplitTree`.
-  If done late we need to track LIR edges and introduce temps.
-* How do we represent the new calls internally? They effectively define two things (return value and the "suspended").
-  ~~We can probably cheat and consider these as always defining an additional pseudo-local.~~ No we cannot, because encoding that in LSRA and codegen is a nightmare when there is no tree to attach the RPs to.
-* How about if the JIT creates local addresses that span across awaits? Do we need to represent the calls as opaque defs of all live state? That seems conservative and might not even fully avoid the problem.
-
-## Optional JIT work
-
-1. Support for generating multiple different entry points.
-2. Deal with the fact that liveness of address exposed locals is going to be very conservative.
\ No newline at end of file
diff --git a/docs/design/features/runtime-handled-tasks.md b/docs/design/features/runtime-handled-tasks.md
index eaa71634745ea6..f137f6085696d2 100644
--- a/docs/design/features/runtime-handled-tasks.md
+++ b/docs/design/features/runtime-handled-tasks.md
@@ -150,7 +150,7 @@ Async2 methods will not be visible in reflection via the `Type.GetMethod`, `Type
 `Type.GetMemberWithSameMetadataDefinitionAs` will find the matching async variant method.
 
 
-## Implementation of Unwinder based async2 implementation
+## Implementation of Unwinder based async2 prototype
 
 The general design is to leverage the notion that a normal code generated function at a function call point is effectively a state machine. The index for resumption is the IP that returning to the function will set, and the stackframe + saved registers are the current state of the state machine. I believe we can achieve performance comparable to synchronous code with this scheme for non-suspended scenarios, and we may achieve acceptable overall performance as an async mechanism if suspension is relatively rare.
 
@@ -222,7 +222,274 @@ Once the `ResumptionFunc` has setup the global state to have the correct bits in
 3. The cost of GC while `Tasklet`s exist is dependent on the number of live `Tasklet` objects. This is almost certainly a fairly high cost. (under investigation)
 4. This approach allows development of async code which uses ref parameters/locals and ByRefLike structures. (confirmed)
 
-## Shared implementation between Unwinder and JIT focussed implementation of runtime tasks
+## Implementation of prototype based on JIT generated state machines
+
+In the second prototype we leave it up to the JIT to generate the state machines for async2 functions.
+When awaiting an async2 function from within an async2 function the JIT generates suspension code to capture the live state in a continuation.
+In addition, the JIT generates code for each of these suspension points to be able to resume from the continuation that was created.
+
+### Suspension
+
+Communicating suspension is done by returning a non-zero continuation, using a special calling convention.
+The continuation is a normal GC ref so special care must be taken within the JIT and within the VM to handle proper GC reporting of this return value.
+On x64, the continuation is always returned in `rcx`.
+
+When a callee suspends by returning a non-zero continuation the caller itself suspends by creating its own continuation and returning it.
+Additionally, the generated suspension code links the continuations into a linked list.
+
+`Continuation` is defined as a simple class in the BCL:
+```csharp
+internal sealed unsafe class Continuation
+{
+    public Continuation? Next;
+    public delegate*<Continuation, Continuation?> Resume;
+    public uint State;
+    public CorInfoContinuationFlags Flags;
+    public byte[]? Data;
+    public object[]? GCData;
+}
+```
+
+This incurs extra cost on every call to an async2 method in the form of a test for whether the returned continuation is non-zero.
+
+### Resumption
+
+In addition to returning a continuation async2 methods also accept a continuation as an additional parameter.
+The parameter follows the normal calling convention; it is added in the parameter list after the generic context, return buffer or `this` parameter (whichever comes last).
+
+Resuming an async2 method is done by passing a non-zero continuation.
+The continuation must have been created by the same exact native code version that is being resumed.
+
+Passing a zero continuation is the same as starting the state machine.
+The JIT will automatically generate code to pass a zero continuation when calling an async2 method.
+
+When the JIT generates code for an async2 function it requests the runtime to create a resumption stub.
+The suspension code stores the pointer to the resumption stub in the `Continuation.Resume` field.
+
+The resumption stub implements the following:
+1. It calls into the original function passing the non-zero continuation
+2. It passes default values for all arguments, to make sure that the callee has the expected stack frame set up
+3. If the function returns a value, it ensures that the return value is propagated into the next continuation.
+Continuations expect that the next continuation has allocated space for the return value in its `GCData[0]` field.
+Currently return values are always boxed when propagating from one continuation to the next continuation.
+4. If the function returned a new continuation (i.e. it suspended again), the resumption stub returns it back to the caller.
+
+In pseudo-C#:
+```csharp
+static async2 int Foo(int a, int b)
+{
+    ...
+}
+
+static Continuation? IL_STUB_AsyncResume_Foo(Continuation continuation)
+{
+    delegate*<Continuation, int, int, int> foo = &Foo;
+    int result = foo(continuation, 0, 0);
+    Continuation? newContinuation = StubHelpers.Async2CallContinuation();
+
+    if (newContinuation == null)
+    {
+        continuation.Next.GCData[0] = (object)result;
+    }
+
+    return newContinuation;
+}
+```
+
+### Intrinsics used for suspension/resumption
+
+To interact with the async2 calling convention the JIT/VM provides the following intrinsics:
+```csharp
+// Retrieve the continuation returned by an async2 function
+[Intrinsic]
+internal static Continuation? Async2CallContinuation() => null;
+
+// Suspend the current function by immediately returning with a specific non-zero continuation.
+[Intrinsic]
+private static void SuspendAsync2(Continuation continuation) => throw new UnreachableException();
+```
+
+These intrinsics are NOT used by user code, but they are used internally by the async1<->async2 adapter later, which will be described later.
+
+`Async2CallContinuation()` is also used by generated resumption stubs to capture the returned continuation.
+
+Since the continuation is a normal parameter no intrinsic is required to allow the resumption stub to pass the continuation to the target.
+Instead, the resumption stub calls the target by `calli` with a signature that includes the continuation, in the same way as IL instantiating stubs work.
+
+### Handling OSR
+
+There is additional complexity necessary to handle OSR correctly:
+1. OSR expects that the frame was set up by a tier-0 method so resumption directly in an OSR method is not possible.
+Instead, the JIT generates code in the tier0 version to check for an OSR continuation in which case the tier-0 method transitions immediately
+to the OSR method on resumption.
+The resumption stub created for an OSR method thus points to its corresponding tier-0 code version.
+This is currently implemented by storing a link between OSR methods and corresponding tier-0 methods in `PatchpointInfo`.
+2. When resuming in a tier-0 method, if we transition to the OSR method by normal means, the OSR method will see a non-zero continuation that belongs to the tier-0 method.
+The OSR method must ignore the continuation in this case.
+
+### Interop: the async1<->async2 adapter layer
+
+Thunks of the same shapes are provided as for the unwinding prototype.
+
+#### async2 -> async1
+
+The only way to actually begin suspension of a chain of async2 methods happens when calling into an async1 method that suspends.
+As described previously these boundaries are generally expected to be handled by Roslyn by IL codegen that involves a check for `IsCompleted` followed by a call into one of the two `AwaitAwaiterFromRuntimeAsync` variants in the asynchronous case, finally followed by a `GetResult` call.
+The runtime will automatically provide such a stub if an async1 method is called with an async2 signature.
+
+The implementation of `AwaitAwaiterFromRuntimeAsync` is provided by the BCL.
+For the JIT state machines prototype it looks as follows:
+
+```csharp
+private struct RuntimeAsyncAwaitState
+{
+    public Continuation? SentinelContinuation;
+    public INotifyCompletion? Notifier;
+}
+
+[ThreadStatic]
+private static RuntimeAsyncAwaitState t_runtimeAsyncAwaitState;
+
+public static async2 void AwaitAwaiterFromRuntimeAsync<TAwaiter>(TAwaiter awaiter) where TAwaiter : INotifyCompletion
+{
+    ref RuntimeAsyncAwaitState state = ref t_runtimeAsyncAwaitState;
+    Continuation? sentinelContinuation = state.SentinelContinuation;
+    if (sentinelContinuation == null)
+        state.SentinelContinuation = sentinelContinuation = new Continuation();
+
+    state.Notifier = awaiter;
+    SuspendAsync2(sentinelContinuation);
+}
+
+```
+
+The helper starts the suspension by allocating a continuation into TLS, and returning it back to the caller.
+This is going to result in the JIT codegen unwinding through all calling async2 functions and linking all continuations together.
+The end result will be a linked list of continuations where the head of the continuation is available through TLS.
+
+The state stored in TLS is acted on by the async1 -> async2 adapter.
+
+#### async1 -> async2
+The async1 -> async2 adapter, which allows calling an async2 function as if it returns a `Task`, is implemented in the following pseudo-C#:
+
+```csharp
+static Task<int> FooAdapter(int a, int b)
+{
+    int result;
+    Continuation? continuation = null;
+    try
+    {
+        delegate*<Continuation, int, int, int> foo = &Foo;
+        result = foo(null, a, b);
+        continuation = StubHelpers.Async2CallContinuation();
+    }
+    catch (Exception ex)
+    {
+        return Task.FromException<int>(ex);
+    }
+
+    if (continuation == null)
+        return Task.FromResult(result);
+
+    return FinalizeTaskReturningThunk<int>(continuation);
+}
+```
+(TODO: ExecutionContext, SynchronizationContext handling...)
+
+The interesting bit is what happens in the asynchronous case, where we ended up in the async2 -> async1 adapter described above, which stored the head of the continuation into TLS.
+This is handled inside `Task<T> FinalizeTaskReturningThunk<T>(Continuation continuation)`:
+
+```csharp
+private static Task<T> FinalizeTaskReturningThunk<T>(Continuation continuation)
+{
+    TaskCompletionSource<T> tcs = new();
+    ref RuntimeAsyncAwaitState state = ref t_runtimeAsyncAwaitState;
+    object[] gcData = new object[3];
+    gcData[2] = tcs;
+    continuation.Next = new Continuation
+    {
+        Resume = &ResumeTaskCompletionSource<T>,
+        GCData = gcData,
+        Flags = CorInfoContinuationFlags.CORINFO_CONTINUATION_NEEDS_EXCEPTION | CorInfoContinuationFlags.CORINFO_CONTINUATION_RESULT_IN_GCDATA,
+    };
+
+    Continuation headContinuation = state.SentinelContinuation!.Next!;
+    state.Notifier!.OnCompleted(() => DispatchContinuations(headContinuation));
+    return tcs.Task;
+}
+
+private static Continuation? ResumeTaskCompletionSource<T>(Continuation continuation, Exception? exception)
+{
+    var tcs = (TaskCompletionSource<T>)continuation.GCData![2];
+    Exception ex = (Exception)continuation.GCData![1];
+    if (ex == null)
+    {
+        tcs.SetResult((T)continuation.GCData![0]);
+    }
+    else
+    {
+        tcs.SetException(ex);
+    }
+
+    return null;
+}
+```
+A synthetic continuation is linked to the end of the chain; when resumed, this continuation will trigger the returned `Task<T>` to be completed via the `TaskCompletionSource<T>`.
+With the entire continuation set up properly the `OnCompleted` method of the awaiter stored in TLS can be invoked; it queues a callback to the `DispatchContinuations` function with the head continuation retrieved through TLS.
+
+The final piece of the puzzle is what happens when the awaiter actually invokes the callback asynchronously.
+It delegates to `DispatchContinuations`, another function part of the BCL.
+This function repeatedly dispatches the next continuation by calling its resumption stub.
+It also accounts for potential exceptions thrown, and propagates it into the next relevant continuation.
+Finally, it handles the case where a resumed async2 function suspends again.
+
+```csharp
+private static unsafe void DispatchContinuations(Continuation? continuation)
+{
+    Debug.Assert(continuation != null);
+
+    while (true)
+    {
+        Continuation? newContinuation = null;
+        try
+        {
+            newContinuation = continuation.Resume(continuation);
+        }
+        catch (Exception ex)
+        {
+            continuation = UnwindToPossibleHandler(continuation);
+            continuation.GCData![(continuation.Flags & CorInfoContinuationFlags.CORINFO_CONTINUATION_RESULT_IN_GCDATA) != 0 ? 1 : 0] = ex;
+            continue;
+        }
+
+        if (newContinuation != null)
+        {
+            newContinuation.Next = continuation.Next;
+            ref RuntimeAsyncAwaitState state = ref t_runtimeAsyncAwaitState;
+            Continuation headContinuation = state.SentinelContinuation!.Next!;
+            state.Notifier!.OnCompleted(() => DispatchContinuations(headContinuation));
+            return;
+        }
+
+        continuation = continuation.Next;
+        if (continuation == null)
+            break;
+    }
+}
+
+private static Continuation UnwindToPossibleHandler(Continuation continuation)
+{
+    while (true)
+    {
+        Debug.Assert(continuation.Next != null);
+        continuation = continuation.Next;
+        if ((continuation.Flags & CorInfoContinuationFlags.CORINFO_CONTINUATION_NEEDS_EXCEPTION) != 0)
+            return continuation;
+    }
+}
+```
+
+## Shared implementation between Unwinder and JIT focused implementation of runtime tasks
 
 It turns out that due to the api design of how async2 code interacts with the existing await pattern in IL, the thunk from async2 to existing Tasks and ValueTasks is the same for all designs.
 

From 65fa2271e273a313576224630c76f4bc58fb4566 Mon Sep 17 00:00:00 2001
From: Jakob Botsch Nielsen <janielsen@microsoft.com>
Date: Mon, 30 Oct 2023 20:26:36 +0000
Subject: [PATCH 052/203] Merged PR 34897: Implement proper GC reporting for
 async continuation returns

* Make sure the JIT reports the async continuation return register as a
  GC pointer after async calls
* Mark calls preceding the `StubHelpers.Async2CallContinuation()`
  intrinsic as async in lowering. This ensures correct LSRA behavior
  (marking the async ret busy until it has been used) and that we get
  the GC reporting expected.
* Implement proper GC reporting in the VM for partially interruptible
  async2 methods. The support is a bit hacky; it might be something we
  would want to fold into `ReturnKind` in the GC info instead. We
  currently get it through the `MethodDesc` instead. The fact that
  `UnsafeAwaitAwaiterFromRuntimeAsync`/`AwaitAwaiterFromRuntimeAsync`
  are not marked async2 complicates things a bit.
---
 eng/native/configurecompiler.cmake  |  2 +-
 src/coreclr/jit/codegen.h           |  2 ++
 src/coreclr/jit/codegenarm.cpp      | 14 +++++-----
 src/coreclr/jit/codegenarm64.cpp    | 10 +++++---
 src/coreclr/jit/codegenarmarch.cpp  |  7 +++++
 src/coreclr/jit/codegencommon.cpp   |  2 +-
 src/coreclr/jit/codegenlinear.cpp   |  4 +++
 src/coreclr/jit/codegenxarch.cpp    | 10 ++++++++
 src/coreclr/jit/emit.cpp            | 14 +++++++---
 src/coreclr/jit/emit.h              | 12 +++++++++
 src/coreclr/jit/emitarm.cpp         |  5 ++--
 src/coreclr/jit/emitarm.h           | 12 ++++++---
 src/coreclr/jit/emitarm64.cpp       |  6 +++--
 src/coreclr/jit/emitarm64.h         |  7 +++--
 src/coreclr/jit/emitloongarch64.cpp |  5 ++--
 src/coreclr/jit/emitloongarch64.h   |  7 +++--
 src/coreclr/jit/emitriscv64.cpp     |  5 ++--
 src/coreclr/jit/emitriscv64.h       |  7 +++--
 src/coreclr/jit/emitxarch.cpp       | 13 +++++++---
 src/coreclr/jit/emitxarch.h         |  7 +++--
 src/coreclr/jit/gentree.cpp         | 28 ++++++++++++++++++++
 src/coreclr/jit/lower.cpp           | 40 ++++++++++++++++++++++++++---
 src/coreclr/jit/lower.h             |  1 +
 src/coreclr/vm/amd64/AsmHelpers.asm |  8 +++---
 src/coreclr/vm/amd64/cgenamd64.cpp  |  2 +-
 src/coreclr/vm/amd64/cgencpu.h      |  5 ++++
 src/coreclr/vm/corelib.h            |  1 +
 src/coreclr/vm/frames.cpp           | 12 ++++++++-
 src/coreclr/vm/threads.cpp          |  1 +
 src/coreclr/vm/threads.h            |  9 ++++---
 src/coreclr/vm/threadsuspend.cpp    | 40 +++++++++++++++++++++++------
 31 files changed, 239 insertions(+), 59 deletions(-)

diff --git a/eng/native/configurecompiler.cmake b/eng/native/configurecompiler.cmake
index 52aa288c017cd6..e6488f8c216835 100644
--- a/eng/native/configurecompiler.cmake
+++ b/eng/native/configurecompiler.cmake
@@ -872,7 +872,7 @@ if (MSVC)
 
     add_compile_options($<$<AND:$<COMPILE_LANGUAGE:C,CXX,ASM_MASM>,$<BOOL:$<TARGET_PROPERTY:CLR_EH_CONTINUATION>>>:/guard:ehcont>)
     add_link_options($<$<BOOL:$<TARGET_PROPERTY:CLR_EH_CONTINUATION>>:/guard:ehcont>)
-    add_link_options($<$<AND:$<NOT:$<LINK_LANGUAGE:RC>>>:/CETCOMPAT>)
+    #add_link_options($<$<AND:$<NOT:$<LINK_LANGUAGE:RC>>>:/CETCOMPAT>)
   endif (CLR_CMAKE_HOST_ARCH_AMD64 AND NOT CLR_CMAKE_RUNTIME_MONO)
 
   # Statically linked CRT (libcmt[d].lib, libvcruntime[d].lib and libucrt[d].lib) by default. This is done to avoid
diff --git a/src/coreclr/jit/codegen.h b/src/coreclr/jit/codegen.h
index 437db845d163ec..05fe2617ddce61 100644
--- a/src/coreclr/jit/codegen.h
+++ b/src/coreclr/jit/codegen.h
@@ -489,6 +489,7 @@ class CodeGen final : public CodeGenInterface
                      X86_ARG(int argSize),
                      emitAttr              retSize
                      MULTIREG_HAS_SECOND_GC_RET_ONLY_ARG(emitAttr secondRetSize),
+                     bool                  hasAsyncRet,
                      const DebugInfo&      di,
                      regNumber             base,
                      bool                  isJump);
@@ -502,6 +503,7 @@ class CodeGen final : public CodeGenInterface
                           X86_ARG(int argSize),
                           emitAttr              retSize
                           MULTIREG_HAS_SECOND_GC_RET_ONLY_ARG(emitAttr secondRetSize),
+                          bool                  hasAsyncRet,
                           const DebugInfo&      di,
                           bool                  isJump);
     // clang-format on
diff --git a/src/coreclr/jit/codegenarm.cpp b/src/coreclr/jit/codegenarm.cpp
index 2015449fc86642..4480a533af628c 100644
--- a/src/coreclr/jit/codegenarm.cpp
+++ b/src/coreclr/jit/codegenarm.cpp
@@ -1644,8 +1644,9 @@ void CodeGen::genEmitHelperCall(unsigned helper, int argSize, emitAttr retSize,
 
         GetEmitter()->emitIns_Call(emitter::EC_INDIR_R, compiler->eeFindHelper(helper),
                                    INDEBUG_LDISASM_COMMA(nullptr) NULL, // addr
-                                   argSize, retSize, gcInfo.gcVarPtrSetCur, gcInfo.gcRegGCrefSetCur,
-                                   gcInfo.gcRegByrefSetCur, DebugInfo(),
+                                   argSize, retSize,
+                                   false, // hasAsyncRet
+                                   gcInfo.gcVarPtrSetCur, gcInfo.gcRegGCrefSetCur, gcInfo.gcRegByrefSetCur, DebugInfo(),
                                    callTargetReg, // ireg
                                    REG_NA, 0, 0,  // xreg, xmul, disp
                                    false          // isJump
@@ -1654,10 +1655,11 @@ void CodeGen::genEmitHelperCall(unsigned helper, int argSize, emitAttr retSize,
     else
     {
         GetEmitter()->emitIns_Call(emitter::EC_FUNC_TOKEN, compiler->eeFindHelper(helper),
-                                   INDEBUG_LDISASM_COMMA(nullptr) addr, argSize, retSize, gcInfo.gcVarPtrSetCur,
-                                   gcInfo.gcRegGCrefSetCur, gcInfo.gcRegByrefSetCur, DebugInfo(), REG_NA, REG_NA, 0,
-                                   0,    /* ilOffset, ireg, xreg, xmul, disp */
-                                   false /* isJump */
+                                   INDEBUG_LDISASM_COMMA(nullptr) addr, argSize, retSize,
+                                   false, // hasAsyncRet
+                                   gcInfo.gcVarPtrSetCur, gcInfo.gcRegGCrefSetCur, gcInfo.gcRegByrefSetCur, DebugInfo(),
+                                   REG_NA, REG_NA, 0, 0, /* ilOffset, ireg, xreg, xmul, disp */
+                                   false                 /* isJump */
                                    );
     }
 
diff --git a/src/coreclr/jit/codegenarm64.cpp b/src/coreclr/jit/codegenarm64.cpp
index 4d1a2fd36f43fd..96532c1743f06c 100644
--- a/src/coreclr/jit/codegenarm64.cpp
+++ b/src/coreclr/jit/codegenarm64.cpp
@@ -5045,10 +5045,12 @@ void CodeGen::genEmitHelperCall(unsigned helper, int argSize, emitAttr retSize,
     }
 
     GetEmitter()->emitIns_Call(callType, compiler->eeFindHelper(helper), INDEBUG_LDISASM_COMMA(nullptr) addr, argSize,
-                               retSize, EA_UNKNOWN, gcInfo.gcVarPtrSetCur, gcInfo.gcRegGCrefSetCur,
-                               gcInfo.gcRegByrefSetCur, DebugInfo(), callTarget, /* ireg */
-                               REG_NA, 0, 0,                                     /* xreg, xmul, disp */
-                               false                                             /* isJump */
+                               retSize, EA_UNKNOWN,
+                               false, // hasAsyncRet
+                               gcInfo.gcVarPtrSetCur, gcInfo.gcRegGCrefSetCur, gcInfo.gcRegByrefSetCur, DebugInfo(),
+                               callTarget,   /* ireg */
+                               REG_NA, 0, 0, /* xreg, xmul, disp */
+                               false         /* isJump */
                                );
 
     regMaskTP killMask = compiler->compHelperCallKillSet((CorInfoHelpFunc)helper);
diff --git a/src/coreclr/jit/codegenarmarch.cpp b/src/coreclr/jit/codegenarmarch.cpp
index 7bcd66bfb4e0f0..c7354987b76e22 100644
--- a/src/coreclr/jit/codegenarmarch.cpp
+++ b/src/coreclr/jit/codegenarmarch.cpp
@@ -3540,6 +3540,8 @@ void CodeGen::genCallInstruction(GenTreeCall* call)
         }
     }
 #endif // DEBUG
+
+    bool                  hasAsyncRet = call->IsAsync2() && (JitConfig.RuntimeAsyncViaJitGeneratedStateMachines() != 0);
     CORINFO_METHOD_HANDLE methHnd;
     GenTree*              target = getCallTarget(call, &methHnd);
 
@@ -3568,6 +3570,7 @@ void CodeGen::genCallInstruction(GenTreeCall* call)
                     nullptr, // addr
                     retSize
                     MULTIREG_HAS_SECOND_GC_RET_ONLY_ARG(secondRetSize),
+                    hasAsyncRet,
                     di,
                     target->GetRegNum(),
                     call->IsFastTailCall());
@@ -3616,6 +3619,7 @@ void CodeGen::genCallInstruction(GenTreeCall* call)
                         nullptr, // addr
                         retSize
                         MULTIREG_HAS_SECOND_GC_RET_ONLY_ARG(secondRetSize),
+                        hasAsyncRet,
                         di,
                         targetAddrReg,
                         call->IsFastTailCall());
@@ -3664,6 +3668,7 @@ void CodeGen::genCallInstruction(GenTreeCall* call)
                             INDEBUG_LDISASM_COMMA(sigInfo)
                             NULL,
                             retSize,
+                            hasAsyncRet,
                             di,
                             tmpReg,
                             call->IsFastTailCall());
@@ -3679,6 +3684,7 @@ void CodeGen::genCallInstruction(GenTreeCall* call)
                             addr,
                             retSize
                             MULTIREG_HAS_SECOND_GC_RET_ONLY_ARG(secondRetSize),
+                            hasAsyncRet,
                             di,
                             REG_NA,
                             call->IsFastTailCall());
@@ -5637,6 +5643,7 @@ void CodeGen::genFnEpilog(BasicBlock* block)
 #if defined(TARGET_ARM64)
                                        EA_UNKNOWN, // secondRetSize
 #endif
+                                       false,      // hasAsyncRet
                                        gcInfo.gcVarPtrSetCur,
                                        gcInfo.gcRegGCrefSetCur,
                                        gcInfo.gcRegByrefSetCur,
diff --git a/src/coreclr/jit/codegencommon.cpp b/src/coreclr/jit/codegencommon.cpp
index 58bc365dc89406..3eaa082976a804 100644
--- a/src/coreclr/jit/codegencommon.cpp
+++ b/src/coreclr/jit/codegencommon.cpp
@@ -7710,7 +7710,7 @@ void CodeGen::genReturnSuspend(GenTreeUnOp* treeNode)
     assert(op->TypeIs(TYP_REF));
 
     regNumber reg = genConsumeReg(op);
-    inst_Mov_Extend(TYP_REF, /* srcInReg */ true, REG_ASYNC_CONTINUATION_RET, reg, /* canSkip */ true);
+    inst_Mov(TYP_REF, REG_ASYNC_CONTINUATION_RET, reg, /* canSkip */ true);
 
     ReturnTypeDesc retTypeDesc = compiler->compRetTypeDesc;
     unsigned       numRetRegs  = retTypeDesc.GetReturnRegCount();
diff --git a/src/coreclr/jit/codegenlinear.cpp b/src/coreclr/jit/codegenlinear.cpp
index d828c62495b078..56071fde663804 100644
--- a/src/coreclr/jit/codegenlinear.cpp
+++ b/src/coreclr/jit/codegenlinear.cpp
@@ -2287,6 +2287,7 @@ void CodeGen::genEmitCall(int                   callType,
                           X86_ARG(int argSize),
                           emitAttr              retSize
                           MULTIREG_HAS_SECOND_GC_RET_ONLY_ARG(emitAttr secondRetSize),
+                          bool                  hasAsyncRet,
                           const DebugInfo& di,
                           regNumber             base,
                           bool                  isJump)
@@ -2306,6 +2307,7 @@ void CodeGen::genEmitCall(int                   callType,
                                argSize,
                                retSize
                                MULTIREG_HAS_SECOND_GC_RET_ONLY_ARG(secondRetSize),
+                               hasAsyncRet,
                                gcInfo.gcVarPtrSetCur,
                                gcInfo.gcRegGCrefSetCur,
                                gcInfo.gcRegByrefSetCur,
@@ -2325,6 +2327,7 @@ void CodeGen::genEmitCallIndir(int                   callType,
                                X86_ARG(int argSize),
                                emitAttr              retSize
                                MULTIREG_HAS_SECOND_GC_RET_ONLY_ARG(emitAttr secondRetSize),
+                               bool                  hasAsyncRet,
                                const DebugInfo&      di,
                                bool                  isJump)
 {
@@ -2347,6 +2350,7 @@ void CodeGen::genEmitCallIndir(int                   callType,
                                argSize,
                                retSize
                                MULTIREG_HAS_SECOND_GC_RET_ONLY_ARG(secondRetSize),
+                               hasAsyncRet,
                                gcInfo.gcVarPtrSetCur,
                                gcInfo.gcRegGCrefSetCur,
                                gcInfo.gcRegByrefSetCur,
diff --git a/src/coreclr/jit/codegenxarch.cpp b/src/coreclr/jit/codegenxarch.cpp
index 91c90ce8ff97c7..755b7068f23eb3 100644
--- a/src/coreclr/jit/codegenxarch.cpp
+++ b/src/coreclr/jit/codegenxarch.cpp
@@ -6172,6 +6172,7 @@ void CodeGen::genCallInstruction(GenTreeCall* call X86_ARG(target_ssize_t stackA
     }
 #endif // DEBUG
 
+    bool                  hasAsyncRet = call->IsAsync2() && (JitConfig.RuntimeAsyncViaJitGeneratedStateMachines() != 0);
     CORINFO_METHOD_HANDLE methHnd;
     GenTree*              target = getCallTarget(call, &methHnd);
     if (target != nullptr)
@@ -6208,6 +6209,7 @@ void CodeGen::genCallInstruction(GenTreeCall* call X86_ARG(target_ssize_t stackA
                                        argSizeForEmitter,
                                        retSize
                                        MULTIREG_HAS_SECOND_GC_RET_ONLY_ARG(secondRetSize),
+                                       hasAsyncRet,
                                        gcInfo.gcVarPtrSetCur,
                                        gcInfo.gcRegGCrefSetCur,
                                        gcInfo.gcRegByrefSetCur,
@@ -6237,6 +6239,7 @@ void CodeGen::genCallInstruction(GenTreeCall* call X86_ARG(target_ssize_t stackA
                             X86_ARG(argSizeForEmitter),
                             retSize
                             MULTIREG_HAS_SECOND_GC_RET_ONLY_ARG(secondRetSize),
+                            hasAsyncRet,
                             di,
                             REG_NA,
                             call->IsFastTailCall());
@@ -6259,6 +6262,7 @@ void CodeGen::genCallInstruction(GenTreeCall* call X86_ARG(target_ssize_t stackA
                                  X86_ARG(argSizeForEmitter),
                                  retSize
                                  MULTIREG_HAS_SECOND_GC_RET_ONLY_ARG(secondRetSize),
+                                 hasAsyncRet,
                                  di,
                                  call->IsFastTailCall());
                 // clang-format on
@@ -6285,6 +6289,7 @@ void CodeGen::genCallInstruction(GenTreeCall* call X86_ARG(target_ssize_t stackA
                         X86_ARG(argSizeForEmitter),
                         retSize
                         MULTIREG_HAS_SECOND_GC_RET_ONLY_ARG(secondRetSize),
+                        hasAsyncRet,
                         di,
                         target->GetRegNum(),
                         call->IsFastTailCall());
@@ -6312,6 +6317,7 @@ void CodeGen::genCallInstruction(GenTreeCall* call X86_ARG(target_ssize_t stackA
                 0,
                 retSize
                 MULTIREG_HAS_SECOND_GC_RET_ONLY_ARG(secondRetSize),
+                hasAsyncRet,
                 gcInfo.gcVarPtrSetCur,
                 gcInfo.gcRegGCrefSetCur,
                 gcInfo.gcRegByrefSetCur,
@@ -6332,6 +6338,7 @@ void CodeGen::genCallInstruction(GenTreeCall* call X86_ARG(target_ssize_t stackA
                         X86_ARG(argSizeForEmitter),
                         retSize
                         MULTIREG_HAS_SECOND_GC_RET_ONLY_ARG(secondRetSize),
+                        hasAsyncRet,
                         di,
                         REG_NA,
                         call->IsFastTailCall());
@@ -6372,6 +6379,7 @@ void CodeGen::genCallInstruction(GenTreeCall* call X86_ARG(target_ssize_t stackA
                         X86_ARG(argSizeForEmitter),
                         retSize
                         MULTIREG_HAS_SECOND_GC_RET_ONLY_ARG(secondRetSize),
+                        hasAsyncRet,
                         di,
                         REG_NA,
                         call->IsFastTailCall());
@@ -9036,6 +9044,7 @@ void CodeGen::genEmitHelperCall(unsigned helper, int argSize, emitAttr retSize,
                                argSize,
                                retSize
                                MULTIREG_HAS_SECOND_GC_RET_ONLY_ARG(EA_UNKNOWN),
+                               /* hasAsyncRet */ false,
                                gcInfo.gcVarPtrSetCur,
                                gcInfo.gcRegGCrefSetCur,
                                gcInfo.gcRegByrefSetCur,
@@ -10335,6 +10344,7 @@ void CodeGen::genFnEpilog(BasicBlock* block)
                                        0,                                                      // argSize
                                        EA_UNKNOWN                                              // retSize
                                        MULTIREG_HAS_SECOND_GC_RET_ONLY_ARG(EA_UNKNOWN),        // secondRetSize
+                                       /* hasAsyncRet */ false,
                                        gcInfo.gcVarPtrSetCur,
                                        gcInfo.gcRegGCrefSetCur,
                                        gcInfo.gcRegByrefSetCur,
diff --git a/src/coreclr/jit/emit.cpp b/src/coreclr/jit/emit.cpp
index 2de959b0872479..1c5c696bfb189d 100644
--- a/src/coreclr/jit/emit.cpp
+++ b/src/coreclr/jit/emit.cpp
@@ -3618,7 +3618,8 @@ emitter::instrDesc* emitter::emitNewInstrCallInd(int              argCnt,
                                                  regMaskTP        gcrefRegs,
                                                  regMaskTP        byrefRegs,
                                                  emitAttr         retSizeIn
-                                                     MULTIREG_HAS_SECOND_GC_RET_ONLY_ARG(emitAttr secondRetSize))
+                                                     MULTIREG_HAS_SECOND_GC_RET_ONLY_ARG(emitAttr secondRetSize),
+                                                 bool hasAsyncRet)
 {
     emitAttr retSize = (retSizeIn != EA_UNKNOWN) ? retSizeIn : EA_PTRSIZE;
 
@@ -3642,7 +3643,8 @@ emitter::instrDesc* emitter::emitNewInstrCallInd(int              argCnt,
         (argCnt > ID_MAX_SMALL_CNS) || // too many args
         (argCnt < 0)                   // caller pops arguments
                                        // There is a second ref/byref return register.
-        MULTIREG_HAS_SECOND_GC_RET_ONLY(|| EA_IS_GCREF_OR_BYREF(secondRetSize)))
+        MULTIREG_HAS_SECOND_GC_RET_ONLY(|| EA_IS_GCREF_OR_BYREF(secondRetSize)) ||
+        hasAsyncRet)
     {
         instrDescCGCA* id;
 
@@ -3659,6 +3661,7 @@ emitter::instrDesc* emitter::emitNewInstrCallInd(int              argCnt,
 #if MULTIREG_HAS_SECOND_GC_RET
         emitSetSecondRetRegGCType(id, secondRetSize);
 #endif // MULTIREG_HAS_SECOND_GC_RET
+        id->hasAsyncContinuationRet(hasAsyncRet);
 
         return id;
     }
@@ -3701,7 +3704,8 @@ emitter::instrDesc* emitter::emitNewInstrCallDir(int              argCnt,
                                                  regMaskTP        gcrefRegs,
                                                  regMaskTP        byrefRegs,
                                                  emitAttr         retSizeIn
-                                                     MULTIREG_HAS_SECOND_GC_RET_ONLY_ARG(emitAttr secondRetSize))
+                                                     MULTIREG_HAS_SECOND_GC_RET_ONLY_ARG(emitAttr secondRetSize),
+                                                 bool hasAsyncRet)
 {
     emitAttr retSize = (retSizeIn != EA_UNKNOWN) ? retSizeIn : EA_PTRSIZE;
 
@@ -3721,7 +3725,8 @@ emitter::instrDesc* emitter::emitNewInstrCallDir(int              argCnt,
         (argCnt > ID_MAX_SMALL_CNS) ||           // too many args
         (argCnt < 0)                             // caller pops arguments
                                                  // There is a second ref/byref return register.
-        MULTIREG_HAS_SECOND_GC_RET_ONLY(|| EA_IS_GCREF_OR_BYREF(secondRetSize)))
+        MULTIREG_HAS_SECOND_GC_RET_ONLY(|| EA_IS_GCREF_OR_BYREF(secondRetSize)) ||
+        hasAsyncRet)
     {
         instrDescCGCA* id = emitAllocInstrCGCA(retSize);
 
@@ -3738,6 +3743,7 @@ emitter::instrDesc* emitter::emitNewInstrCallDir(int              argCnt,
 #if MULTIREG_HAS_SECOND_GC_RET
         emitSetSecondRetRegGCType(id, secondRetSize);
 #endif // MULTIREG_HAS_SECOND_GC_RET
+        id->hasAsyncContinuationRet(hasAsyncRet);
 
         return id;
     }
diff --git a/src/coreclr/jit/emit.h b/src/coreclr/jit/emit.h
index 94ded1df85fcdf..95090ba04b9d21 100644
--- a/src/coreclr/jit/emit.h
+++ b/src/coreclr/jit/emit.h
@@ -1991,8 +1991,19 @@ class emitter
         {
             _idcSecondRetRegGCType = gctype;
         }
+#endif
+
+        bool hasAsyncContinuationRet() const
+        {
+            return _hasAsyncContinuationRet;
+        }
+        void hasAsyncContinuationRet(bool value)
+        {
+            _hasAsyncContinuationRet = value;
+        }
 
     private:
+#if MULTIREG_HAS_SECOND_GC_RET
         // This member stores the GC-ness of the second register in a 2 register returned struct on System V.
         // It is added to the call struct since it is not needed by the base instrDesc struct, which keeps GC-ness
         // of the first register for the instCall nodes.
@@ -2002,6 +2013,7 @@ class emitter
         // The base struct's member keeping the GC-ness of the first return register is _idGCref.
         GCtype _idcSecondRetRegGCType : 2; // ... GC type for the second return register.
 #endif                                     // MULTIREG_HAS_SECOND_GC_RET
+        bool _hasAsyncContinuationRet : 1;
     };
 
     // TODO-Cleanup: Uses of stack-allocated instrDescs should be refactored to be unnecessary.
diff --git a/src/coreclr/jit/emitarm.cpp b/src/coreclr/jit/emitarm.cpp
index 33ae40ee208ef5..36b89a0926b566 100644
--- a/src/coreclr/jit/emitarm.cpp
+++ b/src/coreclr/jit/emitarm.cpp
@@ -4669,6 +4669,7 @@ void emitter::emitIns_Call(EmitCallType          callType,
                            void*            addr,
                            int              argSize,
                            emitAttr         retSize,
+                           bool             hasAsyncRet,
                            VARSET_VALARG_TP ptrVars,
                            regMaskTP        gcrefRegs,
                            regMaskTP        byrefRegs,
@@ -4740,7 +4741,7 @@ void emitter::emitIns_Call(EmitCallType          callType,
     {
         /* Indirect call, virtual calls */
 
-        id = emitNewInstrCallInd(argCnt, 0 /* disp */, ptrVars, gcrefRegs, byrefRegs, retSize);
+        id = emitNewInstrCallInd(argCnt, 0 /* disp */, ptrVars, gcrefRegs, byrefRegs, retSize, hasAsyncRet);
     }
     else
     {
@@ -4749,7 +4750,7 @@ void emitter::emitIns_Call(EmitCallType          callType,
 
         assert(callType == EC_FUNC_TOKEN);
 
-        id = emitNewInstrCallDir(argCnt, ptrVars, gcrefRegs, byrefRegs, retSize);
+        id = emitNewInstrCallDir(argCnt, ptrVars, gcrefRegs, byrefRegs, retSize, hasAsyncRet);
     }
 
     /* Update the emitter's live GC ref sets */
diff --git a/src/coreclr/jit/emitarm.h b/src/coreclr/jit/emitarm.h
index 245196bfa18346..8f59bf525f545e 100644
--- a/src/coreclr/jit/emitarm.h
+++ b/src/coreclr/jit/emitarm.h
@@ -65,10 +65,15 @@ void emitDispInsHelp(instrDesc* id,
 
 private:
 instrDesc* emitNewInstrCallDir(
-    int argCnt, VARSET_VALARG_TP GCvars, regMaskTP gcrefRegs, regMaskTP byrefRegs, emitAttr retSize);
+    int argCnt, VARSET_VALARG_TP GCvars, regMaskTP gcrefRegs, regMaskTP byrefRegs, emitAttr retSize, bool hasAsyncRet);
 
-instrDesc* emitNewInstrCallInd(
-    int argCnt, ssize_t disp, VARSET_VALARG_TP GCvars, regMaskTP gcrefRegs, regMaskTP byrefRegs, emitAttr retSize);
+instrDesc* emitNewInstrCallInd(int              argCnt,
+                               ssize_t          disp,
+                               VARSET_VALARG_TP GCvars,
+                               regMaskTP        gcrefRegs,
+                               regMaskTP        byrefRegs,
+                               emitAttr         retSize,
+                               bool             hasAsyncRet);
 
 /************************************************************************/
 /*               Private helpers for instruction output                 */
@@ -327,6 +332,7 @@ void emitIns_Call(EmitCallType          callType,
                   void*            addr,
                   int              argSize,
                   emitAttr         retSize,
+                  bool             hasAsyncRet,
                   VARSET_VALARG_TP ptrVars,
                   regMaskTP        gcrefRegs,
                   regMaskTP        byrefRegs,
diff --git a/src/coreclr/jit/emitarm64.cpp b/src/coreclr/jit/emitarm64.cpp
index 5e0b4f2e78a959..3e22ef9bbac3dc 100644
--- a/src/coreclr/jit/emitarm64.cpp
+++ b/src/coreclr/jit/emitarm64.cpp
@@ -8744,6 +8744,7 @@ void emitter::emitIns_Call(EmitCallType          callType,
                            ssize_t          argSize,
                            emitAttr         retSize,
                            emitAttr         secondRetSize,
+                           bool             hasAsyncRet,
                            VARSET_VALARG_TP ptrVars,
                            regMaskTP        gcrefRegs,
                            regMaskTP        byrefRegs,
@@ -8807,7 +8808,8 @@ void emitter::emitIns_Call(EmitCallType          callType,
     {
         /* Indirect call, virtual calls */
 
-        id = emitNewInstrCallInd(argCnt, 0 /* disp */, ptrVars, gcrefRegs, byrefRegs, retSize, secondRetSize);
+        id = emitNewInstrCallInd(argCnt, 0 /* disp */, ptrVars, gcrefRegs, byrefRegs, retSize, secondRetSize,
+                                 hasAsyncRet);
     }
     else
     {
@@ -8816,7 +8818,7 @@ void emitter::emitIns_Call(EmitCallType          callType,
 
         assert(callType == EC_FUNC_TOKEN);
 
-        id = emitNewInstrCallDir(argCnt, ptrVars, gcrefRegs, byrefRegs, retSize, secondRetSize);
+        id = emitNewInstrCallDir(argCnt, ptrVars, gcrefRegs, byrefRegs, retSize, secondRetSize, hasAsyncRet);
     }
 
     /* Update the emitter's live GC ref sets */
diff --git a/src/coreclr/jit/emitarm64.h b/src/coreclr/jit/emitarm64.h
index a971f56f9d2884..f19173aa3e5a82 100644
--- a/src/coreclr/jit/emitarm64.h
+++ b/src/coreclr/jit/emitarm64.h
@@ -59,7 +59,8 @@ instrDesc* emitNewInstrCallDir(int              argCnt,
                                regMaskTP        gcrefRegs,
                                regMaskTP        byrefRegs,
                                emitAttr         retSize,
-                               emitAttr         secondRetSize);
+                               emitAttr         secondRetSize,
+                               bool             hasAsyncRet);
 
 instrDesc* emitNewInstrCallInd(int              argCnt,
                                ssize_t          disp,
@@ -67,7 +68,8 @@ instrDesc* emitNewInstrCallInd(int              argCnt,
                                regMaskTP        gcrefRegs,
                                regMaskTP        byrefRegs,
                                emitAttr         retSize,
-                               emitAttr         secondRetSize);
+                               emitAttr         secondRetSize,
+                               bool             hasAsyncRet);
 
 /************************************************************************/
 /*   enum to allow instruction optimisation to specify register order   */
@@ -933,6 +935,7 @@ void emitIns_Call(EmitCallType          callType,
                   ssize_t          argSize,
                   emitAttr         retSize,
                   emitAttr         secondRetSize,
+                  bool             hasAsyncRet,
                   VARSET_VALARG_TP ptrVars,
                   regMaskTP        gcrefRegs,
                   regMaskTP        byrefRegs,
diff --git a/src/coreclr/jit/emitloongarch64.cpp b/src/coreclr/jit/emitloongarch64.cpp
index 40c4937fe3b6f1..8f9d55b527e2ab 100644
--- a/src/coreclr/jit/emitloongarch64.cpp
+++ b/src/coreclr/jit/emitloongarch64.cpp
@@ -2277,6 +2277,7 @@ void emitter::emitIns_Call(EmitCallType          callType,
                            void*    addr,
                            ssize_t  argSize,
                            emitAttr retSize MULTIREG_HAS_SECOND_GC_RET_ONLY_ARG(emitAttr secondRetSize),
+                           bool             hasAsyncRet,
                            VARSET_VALARG_TP ptrVars,
                            regMaskTP        gcrefRegs,
                            regMaskTP        byrefRegs,
@@ -2343,7 +2344,7 @@ void emitter::emitIns_Call(EmitCallType          callType,
 
         assert(callType == EC_INDIR_R);
 
-        id = emitNewInstrCallInd(argCnt, disp, ptrVars, gcrefRegs, byrefRegs, retSize, secondRetSize);
+        id = emitNewInstrCallInd(argCnt, disp, ptrVars, gcrefRegs, byrefRegs, retSize, secondRetSize, hasAsyncRet);
     }
     else
     {
@@ -2352,7 +2353,7 @@ void emitter::emitIns_Call(EmitCallType          callType,
 
         assert(callType == EC_FUNC_TOKEN);
 
-        id = emitNewInstrCallDir(argCnt, ptrVars, gcrefRegs, byrefRegs, retSize, secondRetSize);
+        id = emitNewInstrCallDir(argCnt, ptrVars, gcrefRegs, byrefRegs, retSize, secondRetSize, hasAsyncRet);
     }
 
     /* Update the emitter's live GC ref sets */
diff --git a/src/coreclr/jit/emitloongarch64.h b/src/coreclr/jit/emitloongarch64.h
index da0452902646c4..3bc4ee086c2595 100644
--- a/src/coreclr/jit/emitloongarch64.h
+++ b/src/coreclr/jit/emitloongarch64.h
@@ -38,14 +38,16 @@ instrDesc* emitNewInstrCallDir(int              argCnt,
                                VARSET_VALARG_TP GCvars,
                                regMaskTP        gcrefRegs,
                                regMaskTP        byrefRegs,
-                               emitAttr retSize MULTIREG_HAS_SECOND_GC_RET_ONLY_ARG(emitAttr secondRetSize));
+                               emitAttr retSize MULTIREG_HAS_SECOND_GC_RET_ONLY_ARG(emitAttr secondRetSize),
+                               bool hasAsyncRet);
 
 instrDesc* emitNewInstrCallInd(int              argCnt,
                                ssize_t          disp,
                                VARSET_VALARG_TP GCvars,
                                regMaskTP        gcrefRegs,
                                regMaskTP        byrefRegs,
-                               emitAttr retSize MULTIREG_HAS_SECOND_GC_RET_ONLY_ARG(emitAttr secondRetSize));
+                               emitAttr retSize MULTIREG_HAS_SECOND_GC_RET_ONLY_ARG(emitAttr secondRetSize),
+                               bool hasAsyncRet);
 
 /************************************************************************/
 /*               Private helpers for instruction output                 */
@@ -220,6 +222,7 @@ void emitIns_Call(EmitCallType          callType,
                   void*    addr,
                   ssize_t  argSize,
                   emitAttr retSize MULTIREG_HAS_SECOND_GC_RET_ONLY_ARG(emitAttr secondRetSize),
+                  bool             hasAsyncRet,
                   VARSET_VALARG_TP ptrVars,
                   regMaskTP        gcrefRegs,
                   regMaskTP        byrefRegs,
diff --git a/src/coreclr/jit/emitriscv64.cpp b/src/coreclr/jit/emitriscv64.cpp
index de9df4abd5c0a3..bf4f501a0f4d2b 100644
--- a/src/coreclr/jit/emitriscv64.cpp
+++ b/src/coreclr/jit/emitriscv64.cpp
@@ -1257,6 +1257,7 @@ void emitter::emitIns_Call(EmitCallType          callType,
                            void*    addr,
                            ssize_t  argSize,
                            emitAttr retSize MULTIREG_HAS_SECOND_GC_RET_ONLY_ARG(emitAttr secondRetSize),
+                           bool             hasAsyncRet,
                            VARSET_VALARG_TP ptrVars,
                            regMaskTP        gcrefRegs,
                            regMaskTP        byrefRegs,
@@ -1323,7 +1324,7 @@ void emitter::emitIns_Call(EmitCallType          callType,
 
         assert(callType == EC_INDIR_R);
 
-        id = emitNewInstrCallInd(argCnt, disp, ptrVars, gcrefRegs, byrefRegs, retSize, secondRetSize);
+        id = emitNewInstrCallInd(argCnt, disp, ptrVars, gcrefRegs, byrefRegs, retSize, secondRetSize, hasAsyncRet);
     }
     else
     {
@@ -1332,7 +1333,7 @@ void emitter::emitIns_Call(EmitCallType          callType,
 
         assert(callType == EC_FUNC_TOKEN);
 
-        id = emitNewInstrCallDir(argCnt, ptrVars, gcrefRegs, byrefRegs, retSize, secondRetSize);
+        id = emitNewInstrCallDir(argCnt, ptrVars, gcrefRegs, byrefRegs, retSize, secondRetSize, hasAsyncRet);
     }
 
     /* Update the emitter's live GC ref sets */
diff --git a/src/coreclr/jit/emitriscv64.h b/src/coreclr/jit/emitriscv64.h
index ce207755efc4d2..1473c739baf137 100644
--- a/src/coreclr/jit/emitriscv64.h
+++ b/src/coreclr/jit/emitriscv64.h
@@ -44,14 +44,16 @@ instrDesc* emitNewInstrCallDir(int              argCnt,
                                VARSET_VALARG_TP GCvars,
                                regMaskTP        gcrefRegs,
                                regMaskTP        byrefRegs,
-                               emitAttr retSize MULTIREG_HAS_SECOND_GC_RET_ONLY_ARG(emitAttr secondRetSize));
+                               emitAttr retSize MULTIREG_HAS_SECOND_GC_RET_ONLY_ARG(emitAttr secondRetSize),
+                               bool hasAsyncRet);
 
 instrDesc* emitNewInstrCallInd(int              argCnt,
                                ssize_t          disp,
                                VARSET_VALARG_TP GCvars,
                                regMaskTP        gcrefRegs,
                                regMaskTP        byrefRegs,
-                               emitAttr retSize MULTIREG_HAS_SECOND_GC_RET_ONLY_ARG(emitAttr secondRetSize));
+                               emitAttr retSize MULTIREG_HAS_SECOND_GC_RET_ONLY_ARG(emitAttr secondRetSize),
+                               bool hasAsyncRet);
 
 /************************************************************************/
 /*               Private helpers for instruction output                 */
@@ -243,6 +245,7 @@ void emitIns_Call(EmitCallType          callType,
                   void*    addr,
                   ssize_t  argSize,
                   emitAttr retSize MULTIREG_HAS_SECOND_GC_RET_ONLY_ARG(emitAttr secondRetSize),
+                  bool             hasAsyncRet,
                   VARSET_VALARG_TP ptrVars,
                   regMaskTP        gcrefRegs,
                   regMaskTP        byrefRegs,
diff --git a/src/coreclr/jit/emitxarch.cpp b/src/coreclr/jit/emitxarch.cpp
index 7e962c2e0c7cbe..0cc6461e121252 100644
--- a/src/coreclr/jit/emitxarch.cpp
+++ b/src/coreclr/jit/emitxarch.cpp
@@ -9466,6 +9466,7 @@ void emitter::emitIns_Call(EmitCallType          callType,
                            ssize_t               argSize,
                            emitAttr              retSize
                            MULTIREG_HAS_SECOND_GC_RET_ONLY_ARG(emitAttr secondRetSize),
+                           bool                  hasAsyncRet,
                            VARSET_VALARG_TP      ptrVars,
                            regMaskTP             gcrefRegs,
                            regMaskTP             byrefRegs,
@@ -9539,7 +9540,7 @@ void emitter::emitIns_Call(EmitCallType          callType,
         /* Indirect call, virtual calls */
 
         id = emitNewInstrCallInd(argCnt, disp, ptrVars, gcrefRegs, byrefRegs,
-                                 retSize MULTIREG_HAS_SECOND_GC_RET_ONLY_ARG(secondRetSize));
+                                 retSize MULTIREG_HAS_SECOND_GC_RET_ONLY_ARG(secondRetSize), hasAsyncRet);
     }
     else
     {
@@ -9549,7 +9550,7 @@ void emitter::emitIns_Call(EmitCallType          callType,
         assert(callType == EC_FUNC_TOKEN || callType == EC_FUNC_TOKEN_INDIR);
 
         id = emitNewInstrCallDir(argCnt, ptrVars, gcrefRegs, byrefRegs,
-                                 retSize MULTIREG_HAS_SECOND_GC_RET_ONLY_ARG(secondRetSize));
+                                 retSize MULTIREG_HAS_SECOND_GC_RET_ONLY_ARG(secondRetSize), hasAsyncRet);
     }
 
     /* Update the emitter's live GC ref sets */
@@ -16435,11 +16436,11 @@ size_t emitter::emitOutputInstr(insGroup* ig, instrDesc* id, BYTE** dp)
                 byrefRegs |= RBM_EAX;
             }
 
-#ifdef UNIX_AMD64_ABI
             // If is a multi-register return method is called, mark RDX appropriately (for System V AMD64).
             if (id->idIsLargeCall())
             {
                 instrDescCGCA* idCall = (instrDescCGCA*)id;
+#ifdef UNIX_AMD64_ABI
                 if (idCall->idSecondGCref() == GCT_GCREF)
                 {
                     gcrefRegs |= RBM_RDX;
@@ -16448,8 +16449,12 @@ size_t emitter::emitOutputInstr(insGroup* ig, instrDesc* id, BYTE** dp)
                 {
                     byrefRegs |= RBM_RDX;
                 }
-            }
 #endif // UNIX_AMD64_ABI
+                if (idCall->hasAsyncContinuationRet())
+                {
+                    gcrefRegs |= RBM_ASYNC_CONTINUATION_RET;
+                }
+            }
 
             // If the GC register set has changed, report the new set
             if (gcrefRegs != emitThisGCrefRegs)
diff --git a/src/coreclr/jit/emitxarch.h b/src/coreclr/jit/emitxarch.h
index 82a83dd0f2e53c..610f23ec4bbcea 100644
--- a/src/coreclr/jit/emitxarch.h
+++ b/src/coreclr/jit/emitxarch.h
@@ -480,14 +480,16 @@ instrDesc* emitNewInstrCallDir(int              argCnt,
                                VARSET_VALARG_TP GCvars,
                                regMaskTP        gcrefRegs,
                                regMaskTP        byrefRegs,
-                               emitAttr retSize MULTIREG_HAS_SECOND_GC_RET_ONLY_ARG(emitAttr secondRetSize));
+                               emitAttr retSize MULTIREG_HAS_SECOND_GC_RET_ONLY_ARG(emitAttr secondRetSize),
+                               bool hasAsyncRet);
 
 instrDesc* emitNewInstrCallInd(int              argCnt,
                                ssize_t          disp,
                                VARSET_VALARG_TP GCvars,
                                regMaskTP        gcrefRegs,
                                regMaskTP        byrefRegs,
-                               emitAttr retSize MULTIREG_HAS_SECOND_GC_RET_ONLY_ARG(emitAttr secondRetSize));
+                               emitAttr retSize MULTIREG_HAS_SECOND_GC_RET_ONLY_ARG(emitAttr secondRetSize),
+                               bool hasAsyncRet);
 
 void emitGetInsCns(const instrDesc* id, CnsVal* cv) const;
 ssize_t emitGetInsAmdCns(const instrDesc* id, CnsVal* cv) const;
@@ -838,6 +840,7 @@ void emitIns_Call(EmitCallType          callType,
                   ssize_t               argSize,
                   emitAttr              retSize
                   MULTIREG_HAS_SECOND_GC_RET_ONLY_ARG(emitAttr secondRetSize),
+                  bool                  hasAsyncRet,
                   VARSET_VALARG_TP      ptrVars,
                   regMaskTP             gcrefRegs,
                   regMaskTP             byrefRegs,
diff --git a/src/coreclr/jit/gentree.cpp b/src/coreclr/jit/gentree.cpp
index 6245645c748a6f..eb727fb095e056 100644
--- a/src/coreclr/jit/gentree.cpp
+++ b/src/coreclr/jit/gentree.cpp
@@ -2362,6 +2362,34 @@ bool GenTreeCall::HasSideEffects(Compiler* compiler, bool ignoreExceptions, bool
            (!helperProperties.IsAllocator(helper) || ((gtCallMoreFlags & GTF_CALL_M_ALLOC_SIDE_EFFECTS) != 0));
 }
 
+//-------------------------------------------------------------------------
+// IsAsync2: Whether or not this call is to an async2 function.
+//
+// Return Value:
+//   True if so.
+//
+// Remarks:
+//   The exact nature of the async2 calling convention depends on whether
+//   JitConfig.RuntimeAsyncViaJitGeneratedStateMachines is true or not. When
+//   true, it involves passing an async continuation as a separate argument and
+//   returning an async continuation in REG_ASYNC_CONTINUATION_RET.
+//
+//   The async continuation is usually JIT added
+//   (WellKnownArg::AsyncContinuation). This is the case for an async2 method
+//   calling another async2 method by normal means. However, the VM also
+//   creates stubs that call async2 runtimes through calli where the async
+//   continuations are passed explicitly. See
+//   CEEJitInfo::getAsyncResumptionStub and
+//   MethodDesc::EmitJitStateMachineBasedRuntimeAsyncThunk for examples. In
+//   those cases the JIT does not know (and does not need to know) which arg is
+//   the async continuation.
+//
+//   The VM also uses the StubHelpers.Async2CallContinuation() intrinsic in the
+//   stubs discussed above. The JIT must take care in those cases to still mark
+//   the preceding call as an async call; this is required for correct LSRA
+//   behavior and GC reporting around the returned async continuation. This is
+//   currently done in lowering; see LowerAsyncContinuation().
+//
 bool GenTreeCall::IsAsync2() const
 {
     return gtIsAsyncCall;
diff --git a/src/coreclr/jit/lower.cpp b/src/coreclr/jit/lower.cpp
index 6d8a90858fab39..2023b007b0e76c 100644
--- a/src/coreclr/jit/lower.cpp
+++ b/src/coreclr/jit/lower.cpp
@@ -524,10 +524,6 @@ GenTree* Lowering::LowerNode(GenTree* node)
             LowerRet(node->AsUnOp());
             break;
 
-        case GT_RETURN_SUSPEND:
-            LowerReturnSuspend(node);
-            break;
-
         case GT_RETURNTRAP:
             ContainCheckReturnTrap(node->AsOp());
             break;
@@ -691,6 +687,14 @@ GenTree* Lowering::LowerNode(GenTree* node)
             return LowerArrLength(node->AsArrCommon());
             break;
 
+        case GT_ASYNC_CONTINUATION:
+            LowerAsyncContinuation(node);
+            break;
+
+        case GT_RETURN_SUSPEND:
+            LowerReturnSuspend(node);
+            break;
+
         default:
             break;
     }
@@ -4682,6 +4686,34 @@ void Lowering::LowerRetSingleRegStructLclVar(GenTreeUnOp* ret)
     }
 }
 
+void Lowering::LowerAsyncContinuation(GenTree* asyncCont)
+{
+    assert(asyncCont->OperIs(GT_ASYNC_CONTINUATION));
+
+    // When the ASYNC_CONTINUATION was created as a result of
+    // StubHelpers.Async2CallContinuation() the previous call hasn't been
+    // marked as an async call. We need to do that to get the right GC
+    // reporting behavior for the returned async continuation.
+    GenTree* node = asyncCont;
+    while (true)
+    {
+        node = node->gtPrev;
+        noway_assert((node != nullptr) && "Ran out of nodes while looking for call before async continuation");
+
+        if (node->IsCall())
+        {
+            if (!node->AsCall()->IsAsync2())
+            {
+                JITDUMP("Marking the call [%06u] before async continuation [%06u] as an async call\n",
+                        Compiler::dspTreeID(node), Compiler::dspTreeID(asyncCont));
+                node->AsCall()->gtIsAsyncCall = true;
+            }
+
+            break;
+        }
+    }
+}
+
 void Lowering::LowerReturnSuspend(GenTree* node)
 {
     assert(node->OperIs(GT_RETURN_SUSPEND));
diff --git a/src/coreclr/jit/lower.h b/src/coreclr/jit/lower.h
index 314e20af259ca9..1efe7741df1248 100644
--- a/src/coreclr/jit/lower.h
+++ b/src/coreclr/jit/lower.h
@@ -155,6 +155,7 @@ class Lowering final : public Phase
     void LowerStoreLocCommon(GenTreeLclVarCommon* lclVar);
     void LowerRetStruct(GenTreeUnOp* ret);
     void LowerRetSingleRegStructLclVar(GenTreeUnOp* ret);
+    void LowerAsyncContinuation(GenTree* asyncCont);
     void LowerReturnSuspend(GenTree* retSuspend);
     void LowerCallStruct(GenTreeCall* call);
     void LowerStoreSingleRegCallStruct(GenTreeBlk* store);
diff --git a/src/coreclr/vm/amd64/AsmHelpers.asm b/src/coreclr/vm/amd64/AsmHelpers.asm
index 2473822036c140..44711fa585f170 100644
--- a/src/coreclr/vm/amd64/AsmHelpers.asm
+++ b/src/coreclr/vm/amd64/AsmHelpers.asm
@@ -401,10 +401,11 @@ NESTED_ENTRY OnHijackTripThread, _TEXT
         push                rax ; make room for the real return address (Rip)
         push                rdx
         PUSH_CALLEE_SAVED_REGISTERS
+        push_vol_reg        rcx
         push_vol_reg        rax
         mov                 rcx, rsp
 
-        alloc_stack         38h ; make extra room for xmm0, argument home slots and align the SP
+        alloc_stack         30h ; make extra room for xmm0 and argument home slots
         save_xmm128_postrsp xmm0, 20h
 
 
@@ -414,8 +415,9 @@ NESTED_ENTRY OnHijackTripThread, _TEXT
 
         movdqa              xmm0, [rsp + 20h]
 
-        add                 rsp, 38h
+        add                 rsp, 30h
         pop                 rax
+        pop                 rcx
         POP_CALLEE_SAVED_REGISTERS
         pop                 rdx
         ret                 ; return to the correct place, adjusted by our caller
@@ -898,4 +900,4 @@ TaskletUnwindDone:
         jmp rcx
 NESTED_END RuntimeSuspension_UnwindToFunctionWithAsyncFrame, _TEXT
 
-        end
\ No newline at end of file
+        end
diff --git a/src/coreclr/vm/amd64/cgenamd64.cpp b/src/coreclr/vm/amd64/cgenamd64.cpp
index 26c22810260668..6f5e57d14f19ca 100644
--- a/src/coreclr/vm/amd64/cgenamd64.cpp
+++ b/src/coreclr/vm/amd64/cgenamd64.cpp
@@ -298,7 +298,6 @@ void HijackFrame::UpdateRegDisplay(const PREGDISPLAY pRD)
     pRD->pCurrentContextPointers->Rsi = NULL;
     pRD->pCurrentContextPointers->Rdi = NULL;
 #endif
-    pRD->pCurrentContextPointers->Rcx = NULL;
 #ifdef UNIX_AMD64_ABI
     pRD->pCurrentContextPointers->Rdx = (PULONG64)&m_Args->Rdx;
 #else // UNIX_AMD64_ABI
@@ -310,6 +309,7 @@ void HijackFrame::UpdateRegDisplay(const PREGDISPLAY pRD)
     pRD->pCurrentContextPointers->R11 = NULL;
 
     pRD->pCurrentContextPointers->Rax = (PULONG64)&m_Args->Rax;
+    pRD->pCurrentContextPointers->Rcx = (PULONG64)&m_Args->Rcx;
 
     SyncRegDisplayToCurrentContext(pRD);
 
diff --git a/src/coreclr/vm/amd64/cgencpu.h b/src/coreclr/vm/amd64/cgencpu.h
index 5dcaf12dfb8120..5e5e28adbb335f 100644
--- a/src/coreclr/vm/amd64/cgencpu.h
+++ b/src/coreclr/vm/amd64/cgencpu.h
@@ -548,6 +548,11 @@ struct HijackArgs
         ULONG64 ReturnValue[2];
     };
 #endif // !FEATURE_MULTIREG_RETURN
+    union
+    {
+        ULONG64 Rcx;
+        ULONG64 AsyncRet;
+    };
     CalleeSavedRegisters Regs;
 #ifdef TARGET_WINDOWS
     ULONG64 Rsp;
diff --git a/src/coreclr/vm/corelib.h b/src/coreclr/vm/corelib.h
index 5e83f69b6c0fb4..56d11a9248e1cf 100644
--- a/src/coreclr/vm/corelib.h
+++ b/src/coreclr/vm/corelib.h
@@ -689,6 +689,7 @@ DEFINE_METHOD(RUNTIME_HELPERS,      FINALIZE_TASK_RETURNING_THUNK, FinalizeTaskR
 
 DEFINE_METHOD(RUNTIME_HELPERS,      GET_OR_CREATE_RESUMPTION_DELEGATE, GetOrCreateResumptionDelegate, NoSig)
 DEFINE_METHOD(RUNTIME_HELPERS,      UNSAFE_AWAIT_AWAITER_FROM_RUNTIME_ASYNC_1, UnsafeAwaitAwaiterFromRuntimeAsync, GM_T_RetVoid)
+DEFINE_METHOD(RUNTIME_HELPERS,      AWAIT_AWAITER_FROM_RUNTIME_ASYNC_1, AwaitAwaiterFromRuntimeAsync, GM_T_RetVoid)
 
 DEFINE_CLASS(UNSAFE,                CompilerServices,       Unsafe)
 DEFINE_METHOD(UNSAFE,               AS_POINTER,             AsPointer, NoSig)
diff --git a/src/coreclr/vm/frames.cpp b/src/coreclr/vm/frames.cpp
index 093a0f18749999..9761c6ebd2fcc9 100644
--- a/src/coreclr/vm/frames.cpp
+++ b/src/coreclr/vm/frames.cpp
@@ -1191,7 +1191,8 @@ void HijackFrame::GcScanRoots(promote_func *fn, ScanContext* sc)
 {
     LIMITED_METHOD_CONTRACT;
 
-    ReturnKind returnKind = m_Thread->GetHijackReturnKind();
+    bool hasAsyncRet;
+    ReturnKind returnKind = m_Thread->GetHijackReturnKind(&hasAsyncRet);
     _ASSERTE(IsValidReturnKind(returnKind));
 
     int regNo = 0;
@@ -1231,6 +1232,15 @@ void HijackFrame::GcScanRoots(promote_func *fn, ScanContext* sc)
 
         regNo++;
     } while (moreRegisters);
+
+    if (hasAsyncRet)
+    {
+        PTR_PTR_Object objPtr = dac_cast<PTR_PTR_Object>(&m_Args->AsyncRet);
+        LOG((LF_GC, INFO3, "Hijack Frame Promoting Async Continuation Return" FMT_ADDR "to",
+            DBG_ADDR(OBJECTREF_TO_UNCHECKED_OBJECTREF(*objPtr))));
+        (*fn)(objPtr, sc, CHECK_APP_DOMAIN);
+        LOG((LF_GC, INFO3, FMT_ADDR "\n", DBG_ADDR(OBJECTREF_TO_UNCHECKED_OBJECTREF(*objPtr))));
+    }
 }
 
 #endif // FEATURE_HIJACK
diff --git a/src/coreclr/vm/threads.cpp b/src/coreclr/vm/threads.cpp
index fccbcc7bb3bfb1..57a4ca580883ed 100644
--- a/src/coreclr/vm/threads.cpp
+++ b/src/coreclr/vm/threads.cpp
@@ -1629,6 +1629,7 @@ Thread::Thread()
     memset(&m_activityId, 0, sizeof(m_activityId));
 #endif // FEATURE_PERFTRACING
     m_HijackReturnKind = RT_Illegal;
+    m_HijackHasAsyncRet = false;
 
     m_currentPrepareCodeConfig = nullptr;
     m_isInForbidSuspendForDebuggerRegion = false;
diff --git a/src/coreclr/vm/threads.h b/src/coreclr/vm/threads.h
index f69d3df1e7493c..031e86f6d16931 100644
--- a/src/coreclr/vm/threads.h
+++ b/src/coreclr/vm/threads.h
@@ -3144,7 +3144,7 @@ class Thread
 
 private:
 #ifdef FEATURE_HIJACK
-    void    HijackThread(ReturnKind returnKind, ExecutionState *esb);
+    void    HijackThread(ReturnKind returnKind, bool hasAsyncRet, ExecutionState *esb);
 
     VOID        *m_pvHJRetAddr;           // original return address (before hijack)
     VOID       **m_ppvHJRetAddrPtr;       // place we bashed a new return address
@@ -4340,21 +4340,24 @@ class Thread
     // identifies the gc-ness of the return register(s)
 
     ReturnKind m_HijackReturnKind;
+    bool m_HijackHasAsyncRet;
 
 public:
 
-    ReturnKind GetHijackReturnKind()
+    ReturnKind GetHijackReturnKind(bool* hasAsyncRet)
     {
         LIMITED_METHOD_CONTRACT;
 
+        *hasAsyncRet = m_HijackHasAsyncRet;
         return m_HijackReturnKind;
     }
 
-    void SetHijackReturnKind(ReturnKind returnKind)
+    void SetHijackReturnKind(ReturnKind returnKind, bool hasAsyncRet)
     {
         LIMITED_METHOD_CONTRACT;
 
         m_HijackReturnKind = returnKind;
+        m_HijackHasAsyncRet = hasAsyncRet;
     }
 #endif // FEATURE_HIJACK
 
diff --git a/src/coreclr/vm/threadsuspend.cpp b/src/coreclr/vm/threadsuspend.cpp
index 4cf13a424dc722..3a7465fad92c35 100644
--- a/src/coreclr/vm/threadsuspend.cpp
+++ b/src/coreclr/vm/threadsuspend.cpp
@@ -4747,7 +4747,7 @@ struct ExecutionState
 };
 
 // Client is responsible for suspending the thread before calling
-void Thread::HijackThread(ReturnKind returnKind, ExecutionState *esb)
+void Thread::HijackThread(ReturnKind returnKind, bool hasAsyncRet, ExecutionState *esb)
 {
     CONTRACTL {
         NOTHROW;
@@ -4791,7 +4791,7 @@ void Thread::HijackThread(ReturnKind returnKind, ExecutionState *esb)
         return;
     }
 
-    SetHijackReturnKind(returnKind);
+    SetHijackReturnKind(returnKind, hasAsyncRet);
 
     if (m_State & TS_Hijacked)
         UnhijackThread();
@@ -5110,10 +5110,32 @@ void STDCALL OnHijackWorker(HijackArgs * pArgs)
 #endif // HIJACK_NONINTERRUPTIBLE_THREADS
 }
 
-static bool GetReturnAddressHijackInfo(EECodeInfo *pCodeInfo, ReturnKind *pReturnKind)
+static bool IsSpecialCaseAsyncRet(MethodDesc* pMD)
+{
+    // TODO: What's the right way to do this through CoreLibBinder without
+    // causing loading to happen? Also, can we just mark them as async2 in SPC,
+    // or force them to be fully interruptible?
+    LPCUTF8 name = pMD->GetName();
+    return strcmp(name, "UnsafeAwaitAwaiterFromRuntimeAsync") == 0 || strcmp(name, "AwaitAwaiterFromRuntimeAsync") == 0;
+}
+
+static bool GetReturnAddressHijackInfo(EECodeInfo *pCodeInfo, ReturnKind *pReturnKind, bool* hasAsyncRet)
 {
     GCInfoToken gcInfoToken = pCodeInfo->GetGCInfoToken();
-    return pCodeInfo->GetCodeManager()->GetReturnAddressHijackInfo(gcInfoToken, pReturnKind);
+    if (!pCodeInfo->GetCodeManager()->GetReturnAddressHijackInfo(gcInfoToken, pReturnKind))
+        return false;
+
+    // TODO: Expand return kind to include this?
+    *hasAsyncRet = false;
+
+    if (!g_pConfig->RuntimeAsyncViaJitGeneratedStateMachines())
+        return true;
+
+    MethodDesc* pMD = pCodeInfo->GetMethodDesc();
+    *hasAsyncRet = pMD->IsAsync2Method() ||
+        (pMD->IsIntrinsic() && IsSpecialCaseAsyncRet(pMD));
+
+    return true;
 }
 
 #ifndef TARGET_UNIX
@@ -5514,10 +5536,11 @@ BOOL Thread::HandledJITCase()
             EECodeInfo codeInfo(ip);
 
             ReturnKind returnKind;
+            bool hasAsyncRet;
 
-            if (GetReturnAddressHijackInfo(&codeInfo, &returnKind))
+            if (GetReturnAddressHijackInfo(&codeInfo, &returnKind, &hasAsyncRet))
             {
-                HijackThread(returnKind, &esb);
+                HijackThread(returnKind, hasAsyncRet, &esb);
             }
         }
     }
@@ -5965,8 +5988,9 @@ void HandleSuspensionForInterruptedThread(CONTEXT *interruptedContext)
             return;
 
         ReturnKind returnKind;
+        bool hasAsyncRet;
 
-        if (!GetReturnAddressHijackInfo(&codeInfo, &returnKind))
+        if (!GetReturnAddressHijackInfo(&codeInfo, &returnKind, &hasAsyncRet))
         {
             return;
         }
@@ -5980,7 +6004,7 @@ void HandleSuspensionForInterruptedThread(CONTEXT *interruptedContext)
         StackWalkerWalkingThreadHolder threadStackWalking(pThread);
 
         // Hijack the return address to point to the appropriate routine based on the method's return type.
-        pThread->HijackThread(returnKind, &executionState);
+        pThread->HijackThread(returnKind, hasAsyncRet, &executionState);
     }
 }
 

From 15af83ab3d56a0ab6d1a19582c981cf23f19166f Mon Sep 17 00:00:00 2001
From: David Wrighton <davidwr@microsoft.com>
Date: Mon, 30 Oct 2023 20:56:30 +0000
Subject: [PATCH 053/203] Merged PR 34912: Add support for void returning
 Async2 methods, and ValueTask

In addition to that, adjust the abstraction used in managed code to utlize a traditional async method to actually perform suspension so that we get all of the interesting behaviors of the await suspension in terms of setting up ExecutionContext, SyncContext, and correct use of the TaskScheduler scheduling engine.
---
 src/coreclr/vm/corelib.h                      |  26 +-
 src/coreclr/vm/jitinterface.cpp               |   6 +-
 src/coreclr/vm/metasig.h                      |   9 +
 src/coreclr/vm/method.hpp                     |  19 +-
 src/coreclr/vm/methodtablebuilder.cpp         | 202 +++++++++----
 src/coreclr/vm/prestub.cpp                    | 129 ++++++++-
 .../CompilerServices/RuntimeHelpers.cs        | 273 ++++++++----------
 .../src/System/Threading/Tasks/Future.cs      | 167 ++++++++++-
 8 files changed, 594 insertions(+), 237 deletions(-)

diff --git a/src/coreclr/vm/corelib.h b/src/coreclr/vm/corelib.h
index 56d11a9248e1cf..5d3cd995542893 100644
--- a/src/coreclr/vm/corelib.h
+++ b/src/coreclr/vm/corelib.h
@@ -350,6 +350,10 @@ DEFINE_METHOD(TYPE_INIT_EXCEPTION,  STR_EX_CTOR,            .ctor,
 DEFINE_CLASS(THREAD_START_EXCEPTION,Threading,              ThreadStartException)
 DEFINE_METHOD(THREAD_START_EXCEPTION,EX_CTOR,               .ctor,                      IM_Exception_RetVoid)
 
+DEFINE_CLASS(VALUETASK_1, Tasks, ValueTask`1)
+
+DEFINE_CLASS(VALUETASK, Tasks, ValueTask)
+
 DEFINE_CLASS(TASK_1, Tasks, Task`1)
 DEFINE_METHOD(TASK_1, GET_AWAITER, GetAwaiter, NoSig)
 
@@ -359,11 +363,29 @@ DEFINE_METHOD(TASK, FROM_RESULT_T, FromResult, GM_T_RetTaskOfT)
 DEFINE_METHOD(TASK, GET_COMPLETED_TASK, get_CompletedTask, SM_RetTask)
 
 DEFINE_CLASS(RUNTIME_TASK_STATE_1, Tasks, RuntimeTaskState`1)
-DEFINE_METHOD(RUNTIME_TASK_STATE_1, PUSH, Push, IM_RetVoid)
-DEFINE_METHOD(RUNTIME_TASK_STATE_1, POP, Pop, IM_RetVoid)
+DEFINE_METHOD(RUNTIME_TASK_STATE_1, PUSH, Push, NoSig)
+DEFINE_METHOD(RUNTIME_TASK_STATE_1, POP, Pop, NoSig)
 DEFINE_METHOD(RUNTIME_TASK_STATE_1, FROM_EXCEPTION, FromException, IM_Exception_RetTaskOfT)
 DEFINE_METHOD(RUNTIME_TASK_STATE_1, FROM_RESULT, FromResult, IM_T_RetTaskOfT)
 
+DEFINE_CLASS(RUNTIME_TASK_STATE, Tasks, RuntimeTaskState)
+DEFINE_METHOD(RUNTIME_TASK_STATE, PUSH, Push, NoSig)
+DEFINE_METHOD(RUNTIME_TASK_STATE, POP, Pop, NoSig)
+DEFINE_METHOD(RUNTIME_TASK_STATE, FROM_EXCEPTION, FromException, IM_Exception_RetTask)
+DEFINE_METHOD(RUNTIME_TASK_STATE, FROM_RESULT, FromResult, IM_RetTask)
+
+DEFINE_CLASS(RUNTIME_VALUETASK_STATE_1, Tasks, RuntimeValueTaskState`1)
+DEFINE_METHOD(RUNTIME_VALUETASK_STATE_1, PUSH, Push, NoSig)
+DEFINE_METHOD(RUNTIME_VALUETASK_STATE_1, POP, Pop, NoSig)
+DEFINE_METHOD(RUNTIME_VALUETASK_STATE_1, FROM_EXCEPTION, FromException, IM_Exception_RetValueTaskOfT)
+DEFINE_METHOD(RUNTIME_VALUETASK_STATE_1, FROM_RESULT, FromResult, IM_T_RetValueTaskOfT)
+
+DEFINE_CLASS(RUNTIME_VALUETASK_STATE, Tasks, RuntimeValueTaskState)
+DEFINE_METHOD(RUNTIME_VALUETASK_STATE, PUSH, Push, NoSig)
+DEFINE_METHOD(RUNTIME_VALUETASK_STATE, POP, Pop, NoSig)
+DEFINE_METHOD(RUNTIME_VALUETASK_STATE, FROM_EXCEPTION, FromException, IM_Exception_RetValueTask)
+DEFINE_METHOD(RUNTIME_VALUETASK_STATE, FROM_RESULT, FromResult, IM_RetValueTask)
+
 DEFINE_CLASS(TASK_AWAITER_1, CompilerServices, TaskAwaiter`1)
 DEFINE_METHOD(TASK_AWAITER_1, GET_ISCOMPLETED, get_IsCompleted, NoSig)
 DEFINE_METHOD(TASK_AWAITER_1, GET_RESULT, GetResult, NoSig)
diff --git a/src/coreclr/vm/jitinterface.cpp b/src/coreclr/vm/jitinterface.cpp
index ef51840577dd81..92e03ce2db2368 100644
--- a/src/coreclr/vm/jitinterface.cpp
+++ b/src/coreclr/vm/jitinterface.cpp
@@ -461,7 +461,7 @@ enum ConvToJitSigFlags : int
 // localSig     - Is it a local variables declaration, or a method signature (with return type, etc).
 // contextType  - The type with any instantiaton information
 //
-AsyncTaskMethod ClassifyAsyncMethodCore(SigPointer sig, Module* pModule, PCCOR_SIGNATURE initialSig, ULONG* offsetOfAsyncDetails);
+AsyncTaskMethod ClassifyAsyncMethodCore(SigPointer sig, Module* pModule, PCCOR_SIGNATURE initialSig, ULONG* offsetOfAsyncDetails, bool *isValueTask);
 
 static void ConvToJitSig(
     PCCOR_SIGNATURE       pSig,
@@ -547,8 +547,8 @@ static void ConvToJitSig(
         sigRet->retType = CEEInfo::asCorInfoType(type, typeHnd, &sigRet->retTypeClass);
         sigRet->retTypeSigClass = CORINFO_CLASS_HANDLE(typeHnd.AsPtr());
 
-        auto asyncMethodClassification = ClassifyAsyncMethodCore(sig, module, NULL, NULL);
-        if ((asyncMethodClassification == AsyncTaskMethod::Async2Method) || (asyncMethodClassification == AsyncTaskMethod::Async2Method))
+        auto asyncMethodClassification = ClassifyAsyncMethodCore(sig, module, NULL, NULL, NULL);
+        if (IsAsyncTaskMethodAsync2Method(asyncMethodClassification))
         {
             sigRet->callConv = (CorInfoCallConv)(sigRet->callConv | CORINFO_CALLCONV_ASYNCCALL);
         }
diff --git a/src/coreclr/vm/metasig.h b/src/coreclr/vm/metasig.h
index a960a2088dde31..a77b611b000e21 100644
--- a/src/coreclr/vm/metasig.h
+++ b/src/coreclr/vm/metasig.h
@@ -577,6 +577,15 @@ DEFINE_METASIG_T(SM(RefDec_RetVoid, r(g(DECIMAL)), v))
 DEFINE_METASIG_T(IM(Exception_RetTaskOfT, C(EXCEPTION), GI(C(TASK_1), 1, G(0))))
 DEFINE_METASIG_T(IM(T_RetTaskOfT, G(0), GI(C(TASK_1), 1, G(0))))
 
+DEFINE_METASIG_T(IM(Exception_RetTask, C(EXCEPTION), C(TASK)))
+DEFINE_METASIG_T(IM(RetTask, _, C(TASK)))
+
+DEFINE_METASIG_T(IM(Exception_RetValueTaskOfT, C(EXCEPTION), GI(g(VALUETASK_1), 1, G(0))))
+DEFINE_METASIG_T(IM(T_RetValueTaskOfT, G(0), GI(g(VALUETASK_1), 1, G(0))))
+
+DEFINE_METASIG_T(IM(Exception_RetValueTask, C(EXCEPTION), g(VALUETASK)))
+DEFINE_METASIG_T(IM(RetValueTask, _, g(VALUETASK)))
+
 DEFINE_METASIG_T(GM(Exception_RetTaskOfT, IMAGE_CEE_CS_CALLCONV_DEFAULT, 1, C(EXCEPTION), GI(C(TASK_1), 1, M(0))))
 DEFINE_METASIG_T(GM(T_RetTaskOfT, IMAGE_CEE_CS_CALLCONV_DEFAULT, 1, M(0), GI(C(TASK_1), 1, M(0))))
 
diff --git a/src/coreclr/vm/method.hpp b/src/coreclr/vm/method.hpp
index 656ad1dec80a8b..f6d9a7da494523 100644
--- a/src/coreclr/vm/method.hpp
+++ b/src/coreclr/vm/method.hpp
@@ -195,11 +195,28 @@ enum class AsyncVariantLookup
 enum class AsyncTaskMethod
 {
     TaskReturningMethod,
+    TaskNonGenericReturningMethod,
     Async2Method,
     Async2MethodThatCannotBeImplementedByTask,
+    Async2MethodNonGeneric,
     NormalMethod
 };
 
+inline bool IsAsyncTaskMethodNormal(AsyncTaskMethod input)
+{
+    return input == AsyncTaskMethod::NormalMethod;
+}
+
+inline bool IsAsyncTaskMethodAsync2Method(AsyncTaskMethod input)
+{
+    return (input == AsyncTaskMethod::Async2Method) || (input == AsyncTaskMethod::Async2MethodThatCannotBeImplementedByTask) || (input == AsyncTaskMethod::Async2MethodNonGeneric);
+}
+
+inline bool IsAsyncTaskMethodTaskReturningMethod(AsyncTaskMethod input)
+{
+    return (input == AsyncTaskMethod::TaskReturningMethod) || (input == AsyncTaskMethod::TaskNonGenericReturningMethod);
+}
+
 // The size of this structure needs to be a multiple of MethodDesc::ALIGNMENT
 //
 // @GENERICS:
@@ -3691,7 +3708,7 @@ ReadyToRunStandaloneMethodMetadata* GetReadyToRunStandaloneMethodMetadata(Method
 void InitReadyToRunStandaloneMethodMetadata();
 #endif // FEATURE_READYTORUN
 
-AsyncTaskMethod ClassifyAsyncMethod(SigPointer sig, Module* pModule, ULONG* offsetOfAsyncDetails);
+AsyncTaskMethod ClassifyAsyncMethod(SigPointer sig, Module* pModule, ULONG* offsetOfAsyncDetails, bool *pIsValueType);
 
 #include "method.inl"
 
diff --git a/src/coreclr/vm/methodtablebuilder.cpp b/src/coreclr/vm/methodtablebuilder.cpp
index da588ef3bde293..5fe2e00302e69a 100644
--- a/src/coreclr/vm/methodtablebuilder.cpp
+++ b/src/coreclr/vm/methodtablebuilder.cpp
@@ -2748,9 +2748,9 @@ bool IsTypeDefOrRefAByRefStruct(Module* pModule, mdToken tk)
     return false;
 }
 
-AsyncTaskMethod ClassifyAsyncMethodCore(SigPointer sig, Module* pModule, PCCOR_SIGNATURE initialSig, ULONG* offsetOfAsyncDetails);
+AsyncTaskMethod ClassifyAsyncMethodCore(SigPointer sig, Module* pModule, PCCOR_SIGNATURE initialSig, ULONG* offsetOfAsyncDetails, bool *isValueTask);
 
-AsyncTaskMethod ClassifyAsyncMethod(SigPointer sig, Module* pModule, ULONG* offsetOfAsyncDetails)
+AsyncTaskMethod ClassifyAsyncMethod(SigPointer sig, Module* pModule, ULONG* offsetOfAsyncDetails, bool *isValueTask)
 {
     PCCOR_SIGNATURE initialSig = sig.GetPtr();
     uint32_t data;
@@ -2763,12 +2763,19 @@ AsyncTaskMethod ClassifyAsyncMethod(SigPointer sig, Module* pModule, ULONG* offs
 
     // Return argument count
     IfFailThrow(sig.GetData(&data));
-    return ClassifyAsyncMethodCore(sig, pModule, initialSig, offsetOfAsyncDetails);
+    return ClassifyAsyncMethodCore(sig, pModule, initialSig, offsetOfAsyncDetails, isValueTask);
 }
 
-AsyncTaskMethod ClassifyAsyncMethodCore(SigPointer sig, Module* pModule, PCCOR_SIGNATURE initialSig, ULONG* offsetOfAsyncDetails)
+AsyncTaskMethod ClassifyAsyncMethodCore(SigPointer sig, Module* pModule, PCCOR_SIGNATURE initialSig, ULONG* offsetOfAsyncDetails, bool *pIsValueTask)
 {
     uint32_t data;
+    bool dummy = false;
+    if (pIsValueTask == NULL)
+    {
+        pIsValueTask = &dummy;
+    }
+
+    *pIsValueTask = false;
 
     // Now we should be parsing the return type
 
@@ -2791,57 +2798,72 @@ AsyncTaskMethod ClassifyAsyncMethodCore(SigPointer sig, Module* pModule, PCCOR_S
         GetNameOfTypeDefOrRef(pModule, tk, &name, &_namespace);
         IfFailThrow(sig.GetByte(&elemTypeByte)); // Don't use GetElemType as it skips custom modifiers
         elemType = (CorElementType)elemTypeByte;
-        if (strcmp(name, "Task`1") == 0 && strcmp(_namespace, "System.Threading.Tasks") == 0 && IsTypeDefOrRefImplementedInSystemModule(pModule, tk) && (elemTypeCmod == ELEMENT_TYPE_CMOD_OPT))
-        {
-            // This must have been the last CMOD before the element type, and the element type MUST be one which can be expressed structurally as a generic parameter
-            switch (elemType)
-            {
-                case ELEMENT_TYPE_VOID:
-                case ELEMENT_TYPE_CLASS:
-                case ELEMENT_TYPE_VALUETYPE:
-                case ELEMENT_TYPE_STRING:
-                case ELEMENT_TYPE_OBJECT:
-                case ELEMENT_TYPE_I:
-                case ELEMENT_TYPE_U:
-                case ELEMENT_TYPE_I1:
-                case ELEMENT_TYPE_U1:
-                case ELEMENT_TYPE_I2:
-                case ELEMENT_TYPE_U2:
-                case ELEMENT_TYPE_I4:
-                case ELEMENT_TYPE_U4:
-                case ELEMENT_TYPE_I8:
-                case ELEMENT_TYPE_U8:
-                case ELEMENT_TYPE_R4:
-                case ELEMENT_TYPE_R8:
-                case ELEMENT_TYPE_BOOLEAN:
-                case ELEMENT_TYPE_CHAR:
-                case ELEMENT_TYPE_ARRAY:
-                case ELEMENT_TYPE_SZARRAY:
-                case ELEMENT_TYPE_VAR:
-                case ELEMENT_TYPE_MVAR:
-                    return AsyncTaskMethod::Async2Method;
-                case ELEMENT_TYPE_TYPEDBYREF:
-                    return AsyncTaskMethod::Async2MethodThatCannotBeImplementedByTask;
-                case ELEMENT_TYPE_GENERICINST:
-                    IfFailThrow(sig.GetElemType(&elemType));
-                    if (elemType == ELEMENT_TYPE_CLASS)
-                    {
+        if (strcmp(_namespace, "System.Threading.Tasks") == 0 && IsTypeDefOrRefImplementedInSystemModule(pModule, tk) && (elemTypeCmod == ELEMENT_TYPE_CMOD_OPT))
+        {
+            if ((strcmp(name, "Task`1") == 0) || (strcmp(name, "ValueTask`1") == 0))
+            {
+                *pIsValueTask = name[0] == 'V';
+                // This must have been the last CMOD before the element type, and the element type MUST be one which can be expressed structurally as a generic parameter
+                switch (elemType)
+                {
+                    case ELEMENT_TYPE_CLASS:
+                    case ELEMENT_TYPE_VALUETYPE:
+                    case ELEMENT_TYPE_STRING:
+                    case ELEMENT_TYPE_OBJECT:
+                    case ELEMENT_TYPE_I:
+                    case ELEMENT_TYPE_U:
+                    case ELEMENT_TYPE_I1:
+                    case ELEMENT_TYPE_U1:
+                    case ELEMENT_TYPE_I2:
+                    case ELEMENT_TYPE_U2:
+                    case ELEMENT_TYPE_I4:
+                    case ELEMENT_TYPE_U4:
+                    case ELEMENT_TYPE_I8:
+                    case ELEMENT_TYPE_U8:
+                    case ELEMENT_TYPE_R4:
+                    case ELEMENT_TYPE_R8:
+                    case ELEMENT_TYPE_BOOLEAN:
+                    case ELEMENT_TYPE_CHAR:
+                    case ELEMENT_TYPE_ARRAY:
+                    case ELEMENT_TYPE_SZARRAY:
+                    case ELEMENT_TYPE_VAR:
+                    case ELEMENT_TYPE_MVAR:
                         return AsyncTaskMethod::Async2Method;
-                    }
-                    else
-                    {
-                        IfFailThrow(sig.GetToken(&tk));
-                        if (IsTypeDefOrRefAByRefStruct(pModule, tk))
+                    case ELEMENT_TYPE_TYPEDBYREF:
+                        return AsyncTaskMethod::Async2MethodThatCannotBeImplementedByTask;
+                    case ELEMENT_TYPE_GENERICINST:
+                        IfFailThrow(sig.GetElemType(&elemType));
+                        if (elemType == ELEMENT_TYPE_CLASS)
                         {
-                            return AsyncTaskMethod::Async2MethodThatCannotBeImplementedByTask;
+                            return AsyncTaskMethod::Async2Method;
                         }
                         else
                         {
-                            return AsyncTaskMethod::Async2Method;
+                            IfFailThrow(sig.GetToken(&tk));
+                            if (IsTypeDefOrRefAByRefStruct(pModule, tk))
+                            {
+                                return AsyncTaskMethod::Async2MethodThatCannotBeImplementedByTask;
+                            }
+                            else
+                            {
+                                return AsyncTaskMethod::Async2Method;
+                            }
                         }
-                    }
-                default:
+                    default:
+                        ThrowHR(COR_E_BADIMAGEFORMAT);
+                }
+            }
+            else if ((strcmp(name, "Task") == 0) || (strcmp(name, "ValueTask") == 0))
+            {
+                *pIsValueTask = name[0] == 'V';
+                if (elemType == ELEMENT_TYPE_VOID)
+                {
+                    return AsyncTaskMethod::Async2MethodNonGeneric;
+                }
+                else
+                {
                     ThrowHR(COR_E_BADIMAGEFORMAT);
+                }
             }
         }
 
@@ -2862,13 +2884,27 @@ AsyncTaskMethod ClassifyAsyncMethodCore(SigPointer sig, Module* pModule, PCCOR_S
         {
             // This might be System.Threading.Tasks.Task`1
             GetNameOfTypeDefOrRef(pModule, tk, &name, &_namespace);
-            if (strcmp(name, "Task`1") == 0 && strcmp(_namespace, "System.Threading.Tasks") == 0)
+            if (((strcmp(name, "Task`1") == 0) || (strcmp(name, "ValueTask`1") == 0)) && strcmp(_namespace, "System.Threading.Tasks") == 0)
             {
+                *pIsValueTask = name[0] == 'V';
                 if (IsTypeDefOrRefImplementedInSystemModule(pModule, tk))
                     return AsyncTaskMethod::TaskReturningMethod;
             }
         }
     }
+    else if ((elemType == ELEMENT_TYPE_CLASS) || (elemType == ELEMENT_TYPE_VALUETYPE))
+    {
+        IfFailThrow(sig.GetToken(&tk));
+        // This might be System.Threading.Tasks.Task or ValueTask
+        GetNameOfTypeDefOrRef(pModule, tk, &name, &_namespace);
+        if (((strcmp(name, "Task") == 0) || (strcmp(name, "ValueTask") == 0)) && strcmp(_namespace, "System.Threading.Tasks") == 0)
+        {
+            *pIsValueTask = name[0] == 'V';
+            if (IsTypeDefOrRefImplementedInSystemModule(pModule, tk))
+                return AsyncTaskMethod::TaskNonGenericReturningMethod;
+        }
+    }
+
     return AsyncTaskMethod::NormalMethod;
 }
 
@@ -3004,9 +3040,10 @@ MethodTableBuilder::EnumerateClassMethods()
 
         SigParser sig(pMemberSignature, cMemberSignature);
         ULONG offsetOfAsyncDetails = 0;
+        bool isAsyncValueType = false;
         AsyncTaskMethod asyncMethodType;
         
-        asyncMethodType = IsDelegate() ? AsyncTaskMethod::NormalMethod : ClassifyAsyncMethod(sig, GetModule(), &offsetOfAsyncDetails);
+        asyncMethodType = IsDelegate() ? AsyncTaskMethod::NormalMethod : ClassifyAsyncMethod(sig, GetModule(), &offsetOfAsyncDetails, &isAsyncValueType);
 
         bool hasGenericMethodArgsComputed;
         bool hasGenericMethodArgs = this->GetModule()->m_pMethodIsGenericMap->IsGeneric(tok, &hasGenericMethodArgsComputed);
@@ -3559,7 +3596,7 @@ MethodTableBuilder::EnumerateClassMethods()
                     type,
                     implType);
 
-                if ((asyncMethodType == AsyncTaskMethod::Async2MethodThatCannotBeImplementedByTask) || (asyncMethodType == AsyncTaskMethod::Async2Method))
+                if (IsAsyncTaskMethodAsync2Method(asyncMethodType))
                 {
                     pNewMethod->SetIsAsync2Method();
                 }
@@ -3588,6 +3625,28 @@ MethodTableBuilder::EnumerateClassMethods()
                     originalSuffixSize = 0;
                     newSuffixSize = 1;
                 }
+                else if (asyncMethodType == AsyncTaskMethod::Async2MethodNonGeneric)
+                {
+                    cAsyncThunkMemberSignature -= 1;
+                    originalTokenOffsetFromAsyncDetailsOffset = 1;
+                    newTokenOffsetFromAsyncDetailsOffset = 1;
+                    thunkType = AsyncMethodType::TaskToAsync;
+                    originalPrefixSize = 1;
+                    newPrefixSize = 1;
+                    originalSuffixSize = 1;
+                    newSuffixSize = 0;
+                }
+                else if (asyncMethodType == AsyncTaskMethod::TaskNonGenericReturningMethod)
+                {
+                    cAsyncThunkMemberSignature += 1;
+                    originalTokenOffsetFromAsyncDetailsOffset = 1;
+                    newTokenOffsetFromAsyncDetailsOffset = 1;
+                    thunkType = AsyncMethodType::AsyncToTask;
+                    originalPrefixSize = 1;
+                    newPrefixSize = 1;
+                    originalSuffixSize = 0;
+                    newSuffixSize = 1;
+                }
                 else
                 {
                     cAsyncThunkMemberSignature -= 2;
@@ -3614,20 +3673,38 @@ MethodTableBuilder::EnumerateClassMethods()
                 _ASSERTE((cMemberSignature - originalRemainingSigOffset) == (cAsyncThunkMemberSignature - newRemainingSigOffset));
                 memcpy(pNewMemberSignature + newRemainingSigOffset, pMemberSignature + originalRemainingSigOffset, cMemberSignature - originalRemainingSigOffset);
 
+                BYTE elemTypeClassOrValuetype = isAsyncValueType ? (BYTE)ELEMENT_TYPE_VALUETYPE : (BYTE)ELEMENT_TYPE_CLASS;
+
                 if (asyncMethodType == AsyncTaskMethod::Async2Method)
                 {
                     // Incoming sig will look something like ... E_T_CMOD_OPT <TokenOfTask> E_T_I4 ....
-                    // And needs to be translated to E_T_GENERICINST E_T_CLASS <TokenOfTask> 1 E_T_I4
-
+                    // And needs to be translated to E_T_GENERICINST E_T_CLASS/E_T_VALUETYPE <TokenOfTask> 1 E_T_I4
                     // Replace the ELEMENT_TYPE_CMOD with ELEMENT_TYPE_GENERICINST, and then add a 1 after the token which refers to Task<T>
 
                     pNewMemberSignature[offsetOfAsyncDetails] = ELEMENT_TYPE_GENERICINST;
-                    pNewMemberSignature[offsetOfAsyncDetails + 1] = ELEMENT_TYPE_CLASS;
+                    pNewMemberSignature[offsetOfAsyncDetails + 1] = elemTypeClassOrValuetype;
                     pNewMemberSignature[newRemainingSigOffset - 1] = 1;
                 }
+                else if (asyncMethodType == AsyncTaskMethod::Async2MethodNonGeneric)
+                {
+                    // Incoming sig will look like ...   E_T_CMOD_OPT <TokenOfTask> E_T_VOID
+                    // which needs to be translated to E_T_CLASS/E_T_VALUETYPE <TokenOfTask>
+
+                    // Replace the ELEMENT_TYPE_CMOD with ELEMENT_TYPE_CLASS/VALUETYPE
+                    pNewMemberSignature[offsetOfAsyncDetails] = elemTypeClassOrValuetype;
+                }
+                else if (asyncMethodType == AsyncTaskMethod::TaskNonGenericReturningMethod)
+                {
+                    // Incoming sig will look like ... E_T_CLASS/E_T_VALUETYPE <TokenOfTask>
+                    // and needs to be translated to E_T_CMOD_OPT <TokenOfTask> E_T_VOID
+
+                    // Replace the E_T_CLASS/E_T_VALUETYPE with ELEMENT_TYPE_CMOD_OPT, and then add the E_T_VOID
+                    pNewMemberSignature[offsetOfAsyncDetails] = ELEMENT_TYPE_CMOD_OPT;
+                    pNewMemberSignature[newRemainingSigOffset - 1] = ELEMENT_TYPE_VOID;
+                }
                 else
                 {
-                    // Incoming sig will look something like ... E_T_GENERICINST E_T_CLASS <TokenOfTask> 1 E_T_I4 ....
+                    // Incoming sig will look something like ... E_T_GENERICINST E_T_CLASS/E_T_VALUETYPE <TokenOfTask> 1 E_T_I4 ....
                     // And needs to be translated to E_T_CMOD_OPT <TokenOfTask> E_T_I4
 
                     // Replace the ELEMENT_TYPE_GENERICINST with ELEMENT_TYPE_CMOD_OPT, and then remove the 1 which specifies the generic arg count for Task<T>
@@ -7491,6 +7568,21 @@ VOID MethodTableBuilder::AllocAndInitMethodDescChunk(COUNT_T startIndex, COUNT_T
 
             if (bmtGenerics->GetNumGenericArgs() == 0) {
                 pUnboxedMD->SetHasNonVtableSlot();
+
+                // By settings HasNonVTableSlot, the following chunks of data have been shifted around.
+                // This is an example of the fragility noted in the memcpy comment above
+                if (pUnboxedMD->HasNativeCodeSlot())
+                {
+                    *pUnboxedMD->GetAddrOfNativeCodeSlot() = *pMD->GetAddrOfNativeCodeSlot();
+                }
+                if (pUnboxedMD->HasMethodImplSlot())
+                {
+                    *pUnboxedMD->GetMethodImpl() = *pMD->GetMethodImpl();
+                }
+                if (pUnboxedMD->HasAsyncMethodData())
+                {
+                    *pUnboxedMD->GetAddrOfAsyncMethodData() = *pMD->GetAddrOfAsyncMethodData();
+                }
             }
 
             //////////////////////////////////////////////////////////
diff --git a/src/coreclr/vm/prestub.cpp b/src/coreclr/vm/prestub.cpp
index abc25114fce66c..b170843a59da42 100644
--- a/src/coreclr/vm/prestub.cpp
+++ b/src/coreclr/vm/prestub.cpp
@@ -1458,7 +1458,8 @@ bool MethodDesc::TryGenerateAsyncThunk(DynamicResolver** resolver, COR_ILMETHOD_
     }
 
     ULONG offsetOfAsyncDetailsUnused;
-    AsyncTaskMethod asyncType = ClassifyAsyncMethod(GetSigPointer(), GetModule(), &offsetOfAsyncDetailsUnused);
+    bool isValueTypeUnused;
+    AsyncTaskMethod asyncType = ClassifyAsyncMethod(GetSigPointer(), GetModule(), &offsetOfAsyncDetailsUnused, &isValueTypeUnused);
     MethodDesc *pAsyncOtherVariant = this->GetAsyncOtherVariant();
     MetaSig msig(this);
 
@@ -1471,7 +1472,7 @@ bool MethodDesc::TryGenerateAsyncThunk(DynamicResolver** resolver, COR_ILMETHOD_
         pAsyncOtherVariant,
         (ILStubLinkerFlags)ILSTUB_LINKER_FLAG_NONE);
 
-    if (asyncType == AsyncTaskMethod::TaskReturningMethod)
+    if (IsAsyncTaskMethodTaskReturningMethod(asyncType))
     {
         if (!g_pConfig->RuntimeAsyncViaJitGeneratedStateMachines())
         {
@@ -1484,7 +1485,7 @@ bool MethodDesc::TryGenerateAsyncThunk(DynamicResolver** resolver, COR_ILMETHOD_
     }
     else
     {
-        _ASSERTE(asyncType == AsyncTaskMethod::Async2Method);
+        _ASSERTE(IsAsyncTaskMethodAsync2Method(asyncType));
         EmitAsync2MethodThunk(pAsyncOtherVariant, msig, &sl);
     }
 
@@ -1530,15 +1531,87 @@ void MethodDesc::EmitUnwindingBasedRuntimeAsyncThunk(MethodDesc* pAsyncOtherVari
     ILCodeStream* pCode = pSL->NewCodeStream(ILStubLinker::kDispatch);
 
     TypeHandle thTaskRet = msig.GetRetTypeHandleThrowing();
-    TypeHandle thLogicalRetType = thTaskRet.GetMethodTable()->GetInstantiation()[0];
-    MethodTable* pMTRuntimeTaskStateOpen = CoreLibBinder::GetClass(CLASS__RUNTIME_TASK_STATE_1);
-    MethodTable* pMTRuntimeTaskState = ClassLoader::LoadGenericInstantiationThrowing(pMTRuntimeTaskStateOpen->GetModule(), pMTRuntimeTaskStateOpen->GetCl(), Instantiation(&thLogicalRetType, 1)).GetMethodTable();
+    bool isGeneric = thTaskRet.GetMethodTable()->HasInstantiation();
+
+    TypeHandle thLogicalRetType;
+    if (isGeneric)
+        thLogicalRetType = thTaskRet.GetMethodTable()->GetInstantiation()[0];
+    else
+        thLogicalRetType = TypeHandle(CoreLibBinder::GetClass(CLASS__VOID));
+
+    bool isValueTask = thTaskRet.GetMethodTable()->IsValueType();
+
+    BinderClassID runtimeTaskStateClassId;
+    BinderMethodID runtimeTaskStatePush;
+    BinderMethodID runtimeTaskStatePop;
+    BinderMethodID runtimeTaskStateFromResult;
+    BinderMethodID runtimeTaskStateFromException;
+
+    if (isValueTask)
+    {
+        if (isGeneric)
+        {
+            runtimeTaskStateClassId = CLASS__RUNTIME_VALUETASK_STATE_1;
+            runtimeTaskStatePush = METHOD__RUNTIME_VALUETASK_STATE_1__PUSH;
+            runtimeTaskStatePop = METHOD__RUNTIME_VALUETASK_STATE_1__POP;
+            runtimeTaskStateFromResult = METHOD__RUNTIME_VALUETASK_STATE_1__FROM_RESULT;
+            runtimeTaskStateFromException = METHOD__RUNTIME_VALUETASK_STATE_1__FROM_EXCEPTION;
+        }
+        else
+        {
+            runtimeTaskStateClassId = CLASS__RUNTIME_VALUETASK_STATE;
+            runtimeTaskStatePush = METHOD__RUNTIME_VALUETASK_STATE__PUSH;
+            runtimeTaskStatePop = METHOD__RUNTIME_VALUETASK_STATE__POP;
+            runtimeTaskStateFromResult = METHOD__RUNTIME_VALUETASK_STATE__FROM_RESULT;
+            runtimeTaskStateFromException = METHOD__RUNTIME_VALUETASK_STATE__FROM_EXCEPTION;
+        }
+    }
+    else
+    {
+        if (isGeneric)
+        {
+            runtimeTaskStateClassId = CLASS__RUNTIME_TASK_STATE_1;
+            runtimeTaskStatePush = METHOD__RUNTIME_TASK_STATE_1__PUSH;
+            runtimeTaskStatePop = METHOD__RUNTIME_TASK_STATE_1__POP;
+            runtimeTaskStateFromResult = METHOD__RUNTIME_TASK_STATE_1__FROM_RESULT;
+            runtimeTaskStateFromException = METHOD__RUNTIME_TASK_STATE_1__FROM_EXCEPTION;
+        }
+        else
+        {
+            runtimeTaskStateClassId = CLASS__RUNTIME_TASK_STATE;
+            runtimeTaskStatePush = METHOD__RUNTIME_TASK_STATE__PUSH;
+            runtimeTaskStatePop = METHOD__RUNTIME_TASK_STATE__POP;
+            runtimeTaskStateFromResult = METHOD__RUNTIME_TASK_STATE__FROM_RESULT;
+            runtimeTaskStateFromException = METHOD__RUNTIME_TASK_STATE__FROM_EXCEPTION;
+        }
+    }
+
+    MethodTable* pMTRuntimeTaskStateOpen = CoreLibBinder::GetClass(runtimeTaskStateClassId);
+    MethodTable* pMTRuntimeTaskState;
+    
+    if (isGeneric)
+    {
+        pMTRuntimeTaskState = ClassLoader::LoadGenericInstantiationThrowing(pMTRuntimeTaskStateOpen->GetModule(), pMTRuntimeTaskStateOpen->GetCl(), Instantiation(&thLogicalRetType, 1)).GetMethodTable();
+    }
+    else
+    {
+        pMTRuntimeTaskState = pMTRuntimeTaskStateOpen;
+    }
+
 
     LocalDesc rtsLocalDesc(pMTRuntimeTaskState);
     DWORD rtsLocal = pCode->NewLocal(rtsLocalDesc);
 
     LocalDesc resultLocalDesc(thLogicalRetType);
-    DWORD resultLocal = pCode->NewLocal(resultLocalDesc);
+    DWORD resultLocal;
+    if (isGeneric)
+    {
+        resultLocal = pCode->NewLocal(resultLocalDesc);
+    }
+    else
+    {
+        resultLocal = UINT32_MAX;
+    }
 
     LocalDesc exceptionLocalDesc(CoreLibBinder::GetClass(CLASS__EXCEPTION));
     DWORD exceptionLocal = pCode->NewLocal(exceptionLocalDesc);
@@ -1551,14 +1624,21 @@ void MethodDesc::EmitUnwindingBasedRuntimeAsyncThunk(MethodDesc* pAsyncOtherVari
 
     pCode->EmitLDLOCA(rtsLocal);
     pCode->EmitINITOBJ(pCode->GetToken(pMTRuntimeTaskState));
-    pCode->EmitLDLOCA(resultLocal);
-    pCode->EmitINITOBJ(pCode->GetToken(thLogicalRetType));
+    if (isGeneric)
+    {
+        pCode->EmitLDLOCA(resultLocal);
+        pCode->EmitINITOBJ(pCode->GetToken(thLogicalRetType));
+    }
     pCode->EmitLDLOCA(exceptionLocal);
     pCode->EmitINITOBJ(pCode->GetToken(CoreLibBinder::GetClass(CLASS__EXCEPTION)));
     pCode->EmitLDLOCA(returnLocal);
     pCode->EmitINITOBJ(pCode->GetToken(thTaskRet));
     pCode->EmitLDLOCA(rtsLocal);
-    pCode->EmitCALL(pCode->GetToken(pMTRuntimeTaskState->GetParallelMethodDesc(CoreLibBinder::GetMethod(METHOD__RUNTIME_TASK_STATE_1__PUSH))), 1, 0);
+    if (isGeneric)
+        pCode->EmitCALL(pCode->GetToken(pMTRuntimeTaskState->GetParallelMethodDesc(CoreLibBinder::GetMethod(runtimeTaskStatePush))), 1, 0);
+    else
+        pCode->EmitCALL(pCode->GetToken(CoreLibBinder::GetMethod(runtimeTaskStatePush)), 1, 0);
+
     {
         pCode->BeginTryBlock();
         pCode->EmitNOP("Separate try blocks");
@@ -1576,8 +1656,11 @@ void MethodDesc::EmitUnwindingBasedRuntimeAsyncThunk(MethodDesc* pAsyncOtherVari
             {
                 pCode->EmitLDARG(localArg++);
             }
-            pCode->EmitCALL(pCode->GetToken(pAsyncOtherVariant), localArg, 1);
-            pCode->EmitSTLOC(resultLocal);
+            pCode->EmitCALL(pCode->GetToken(pAsyncOtherVariant), localArg, isGeneric ? 1 : 0);
+            if (isGeneric)
+            {
+                pCode->EmitSTLOC(resultLocal);
+            }
             pCode->EmitLEAVE(pNoExceptionLabel);
             pCode->EndTryBlock();
         }
@@ -1587,15 +1670,26 @@ void MethodDesc::EmitUnwindingBasedRuntimeAsyncThunk(MethodDesc* pAsyncOtherVari
             pCode->EmitSTLOC(exceptionLocal);
             pCode->EmitLDLOCA(rtsLocal);
             pCode->EmitLDLOC(exceptionLocal);
-            pCode->EmitCALL(pCode->GetToken(pMTRuntimeTaskState->GetParallelMethodDesc(CoreLibBinder::GetMethod(METHOD__RUNTIME_TASK_STATE_1__FROM_EXCEPTION))), 2, 1);
+            if (isGeneric)
+                pCode->EmitCALL(pCode->GetToken(pMTRuntimeTaskState->GetParallelMethodDesc(CoreLibBinder::GetMethod(runtimeTaskStateFromException))), 2, 1);
+            else
+                pCode->EmitCALL(pCode->GetToken(CoreLibBinder::GetMethod(runtimeTaskStateFromException)), 2, 1);
+
             pCode->EmitSTLOC(returnLocal);
             pCode->EmitLEAVE(pReturnResultLabel);
             pCode->EndCatchBlock();
         }
         pCode->EmitLabel(pNoExceptionLabel);
         pCode->EmitLDLOCA(rtsLocal);
-        pCode->EmitLDLOC(resultLocal);
-        pCode->EmitCALL(pCode->GetToken(pMTRuntimeTaskState->GetParallelMethodDesc(CoreLibBinder::GetMethod(METHOD__RUNTIME_TASK_STATE_1__FROM_RESULT))), 2, 1);
+        if (isGeneric)
+        {
+            pCode->EmitLDLOC(resultLocal);
+            pCode->EmitCALL(pCode->GetToken(pMTRuntimeTaskState->GetParallelMethodDesc(CoreLibBinder::GetMethod(runtimeTaskStateFromResult))), 2, 1);
+        }
+        else
+        {
+            pCode->EmitCALL(pCode->GetToken(CoreLibBinder::GetMethod(runtimeTaskStateFromResult)), 1, 1);
+        }
         pCode->EmitSTLOC(returnLocal);
         pCode->EmitLEAVE(pReturnResultLabel);
         pCode->EndTryBlock();
@@ -1604,7 +1698,10 @@ void MethodDesc::EmitUnwindingBasedRuntimeAsyncThunk(MethodDesc* pAsyncOtherVari
     {
         pCode->BeginFinallyBlock();
         pCode->EmitLDLOCA(rtsLocal);
-        pCode->EmitCALL(pCode->GetToken(pMTRuntimeTaskState->GetParallelMethodDesc(CoreLibBinder::GetMethod(METHOD__RUNTIME_TASK_STATE_1__POP))), 1, 0);
+        if (isGeneric)
+            pCode->EmitCALL(pCode->GetToken(pMTRuntimeTaskState->GetParallelMethodDesc(CoreLibBinder::GetMethod(runtimeTaskStatePop))), 1, 0);
+        else
+            pCode->EmitCALL(pCode->GetToken(CoreLibBinder::GetMethod(runtimeTaskStatePop)), 1, 0);
         pCode->EmitENDFINALLY();
         pCode->EndFinallyBlock();
     }
diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.cs
index 284064d1b13640..bfd71c17d0fd07 100644
--- a/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.cs
+++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.cs
@@ -146,14 +146,13 @@ public static void AwaitAwaiterFromRuntimeAsync<TAwaiter>(TAwaiter awaiter) wher
                 StackCrawlMark stackMark = StackCrawlMark.LookForMe;
 
                 // Create resumption delegate, wrapping task, and create tasklets to represent each stack frame on the stack.
-                // RuntimeTaskSuspender.GetOrCreateResumptionDelegate() works like a POSIX fork call in that calls to it will return a
-                // delegate if they are the initial call to GetOrCreateResumptionDelegate, but once the thread is resumed,
+                // RuntimeTaskSuspender.DoSetupAndCapture() works like a POSIX fork call in that calls to it will return a
+                // true for the initial call to DoSetupAndCapture, but once the thread is resumed,
                 // it will resume with a return value of null.
-                Action? resumption = RuntimeHelpers.GetOrCreateResumptionDelegate(ref stackMark);
-                if (resumption != null)
+                if (RuntimeHelpers.DoSetupAndCapture(ref stackMark))
                 {
                     // If we reach here, the only way that we actually run follow on code is for the continuation to actually run,
-                    // and return from GetOrCreateResumptionDelegate with a null return value.
+                    // and return from DoSetupAndCapture with a false return value
                     ref AsyncDataFrame asyncFrame = ref GetCurrentAsyncDataFrame();
                     RuntimeAsyncMaintainedData maintainedData = asyncFrame._maintainedData!;
                     maintainedData._awaiter = awaiter;
@@ -186,14 +185,13 @@ public static void UnsafeAwaitAwaiterFromRuntimeAsync<TAwaiter>(TAwaiter awaiter
                 StackCrawlMark stackMark = StackCrawlMark.LookForMe;
 
                 // Create resumption delegate, wrapping task, and create tasklets to represent each stack frame on the stack.
-                // RuntimeTaskSuspender.GetOrCreateResumptionDelegate() works like a POSIX fork call in that calls to it will return a
-                // delegate if they are the initial call to GetOrCreateResumptionDelegate, but once the thread is resumed,
-                // it will resume with a return value of null.
-                Action? resumption = RuntimeHelpers.GetOrCreateResumptionDelegate(ref stackMark);
-                if (resumption != null)
+                // RuntimeTaskSuspender.DoSetupAndCapture() works like a POSIX fork call in that calls to it will return a
+                // true for the initial call to DoSetupAndCapture, but once the thread is resumed,
+                // it will resume with a return value of false.
+                if (RuntimeHelpers.DoSetupAndCapture(ref stackMark))
                 {
                     // If we reach here, the only way that we actually run follow on code is for the continuation to actually run,
-                    // and return from GetOrCreateResumptionDelegate with a null return value.
+                    // and return from DoSetupAndCapture with a false return value
                     ref AsyncDataFrame asyncFrame = ref GetCurrentAsyncDataFrame();
                     RuntimeAsyncMaintainedData maintainedData = asyncFrame._maintainedData!;
                     maintainedData._awaiter = awaiter;
@@ -235,9 +233,8 @@ public RuntimeAsyncReturnValue(IntPtr ptr)
             public TaskletReturnType _returnType;
         }
 
-        internal abstract unsafe class RuntimeAsyncMaintainedData
+        internal class RuntimeAsyncMaintainedData
         {
-            public Action? _resumption;
             public Exception? _exception;
             public INotifyCompletion? _awaiter;
             public int _suspendActive;
@@ -245,37 +242,20 @@ internal abstract unsafe class RuntimeAsyncMaintainedData
             public bool _completed;
             public byte _dummy;
 
-            public ExecutionContext? _executionCtx;
-            public SynchronizationContext? _syncCtx;
-
-
-            public Tasklet* _nextTasklet;
-            public Tasklet* _oldTaskletNext;
+            public unsafe Tasklet* _nextTasklet;
+            public unsafe Tasklet* _oldTaskletNext;
 
             public RuntimeAsyncReturnValue _retValue;
             public virtual ref byte GetReturnPointer() { return ref _dummy; }
 
             public Task? _task;
-            public abstract Task GetTask();
-        }
-
 
-        // These are all implemented by the same assembly helper that will setup the tasklet in its new home on the stack
-        // and then tail-call into it. We will need a different entrypoint name for each type of register based return that can happen
-        [MethodImpl(MethodImplOptions.InternalCall)]
-        internal static extern unsafe object ResumeTaskletReferenceReturn(Tasklet* pTasklet, ref RuntimeAsyncReturnValue retValue);
-        [MethodImpl(MethodImplOptions.InternalCall)]
-        internal static extern unsafe IntPtr ResumeTaskletIntegerRegisterReturn(Tasklet* pTasklet, ref RuntimeAsyncReturnValue retValue);
-
-        internal sealed class RuntimeAsyncMaintainedData<T> : RuntimeAsyncMaintainedData, ICriticalNotifyCompletion
-        {
-            public RuntimeAsyncMaintainedData()
+            public virtual Task GetTask()
             {
-                _task = CompletionTask();
-                _resumption = ResumptionFunc;
+                return CompletionTaskReturnVoid();
             }
 
-            public unsafe void ResumptionFunc()
+            public unsafe bool ResumptionFunc()
             {
                 // Suspension has finished and we are resuming
                 _suspendActive = 0;
@@ -285,139 +265,143 @@ public unsafe void ResumptionFunc()
 
                 int collectiveStackAllocsPerformed = 0;
 
+                AsyncDataFrame dataFrame = new AsyncDataFrame(this);
+                PushAsyncData(ref dataFrame);
                 try
                 {
-                    AsyncDataFrame dataFrame = new AsyncDataFrame(this);
-                    PushAsyncData(ref dataFrame);
-                    try
+                    while (_nextTasklet != null)
                     {
-                        // PushAsyncData will fill in the value of _previousExecutionCtx and _previousSyncCtx
-                        if (_syncCtx != dataFrame._previousSyncCtx)
-                        {
-                            // TODO: Possibly we should marshal to the right sync context? I dunno. We'll figure this out
-                            dataFrame._currentThread._synchronizationContext = _syncCtx;
-                        }
-                        if (_executionCtx != dataFrame._previousExecutionCtx)
-                        {
-                            ExecutionContext.RestoreChangedContextToThread(dataFrame._currentThread, _executionCtx, dataFrame._previousExecutionCtx);
-                        }
-
-                        while (_nextTasklet != null)
+                        Tasklet* pCurTasklet = _nextTasklet;
+                        int maxStackNeeded = pCurTasklet->GetMaxStackNeeded();
+                        _nextTasklet = pCurTasklet->pTaskletNextInStack;
+                        if (maxStackNeeded > collectiveStackAllocsPerformed)
                         {
-                            Tasklet* pCurTasklet = _nextTasklet;
-                            int maxStackNeeded = pCurTasklet->GetMaxStackNeeded();
-                            _nextTasklet = pCurTasklet->pTaskletNextInStack;
-                            if (maxStackNeeded > collectiveStackAllocsPerformed)
-                            {
 #pragma warning disable CA2014
-                                // This won't stack overflow unless MaxStackNeeded is actually too high, as the extra allocation is controlled by collectiveStackAllocsPerformed
-                                // TODO This is doing terrible things with the ABI, so we may need to be more careful here
-                                int stackToAlloc = maxStackNeeded - collectiveStackAllocsPerformed;
-                                byte* pStackAlloc = stackalloc byte[stackToAlloc];
-                                collectiveStackAllocsPerformed += stackToAlloc;
+                            // This won't stack overflow unless MaxStackNeeded is actually too high, as the extra allocation is controlled by collectiveStackAllocsPerformed
+                            // TODO This is doing terrible things with the ABI, so we may need to be more careful here
+                            int stackToAlloc = maxStackNeeded - collectiveStackAllocsPerformed;
+                            byte* pStackAlloc = stackalloc byte[stackToAlloc];
+                            collectiveStackAllocsPerformed += stackToAlloc;
 #pragma warning restore CA2014
-                                // The optimizer does nothing with variable sized StackAlloc KeepStackAllocAlive(pStackAlloc);
-                            }
+                            // The optimizer does nothing with variable sized StackAlloc KeepStackAllocAlive(pStackAlloc);
+                        }
 
-                            try
-                            {
-                                switch (pCurTasklet->taskletReturnType)
-                                {
-                                    case TaskletReturnType.ObjectReference:
-                                        _retValue = new RuntimeAsyncReturnValue(ResumeTaskletReferenceReturn(pCurTasklet, ref _retValue));
-                                        break;
-                                    case TaskletReturnType.Integer:
-                                        _retValue = new RuntimeAsyncReturnValue(ResumeTaskletIntegerRegisterReturn(pCurTasklet, ref _retValue));
-                                        break;
-                                    case TaskletReturnType.ByReference:
-                                        throw new NotImplementedException(); // This will be awkward (but not impossible) to implement. Hold off for now
-                                }
-                            }
-                            finally
+                        try
+                        {
+                            switch (pCurTasklet->taskletReturnType)
                             {
-                                DeleteTasklet(pCurTasklet);
+                                case TaskletReturnType.ObjectReference:
+                                    _retValue = new RuntimeAsyncReturnValue(ResumeTaskletReferenceReturn(pCurTasklet, ref _retValue));
+                                    break;
+                                case TaskletReturnType.Integer:
+                                    _retValue = new RuntimeAsyncReturnValue(ResumeTaskletIntegerRegisterReturn(pCurTasklet, ref _retValue));
+                                    break;
+                                case TaskletReturnType.ByReference:
+                                    throw new NotImplementedException(); // This will be awkward (but not impossible) to implement. Hold off for now
                             }
+                        }
+                        finally
+                        {
+                            DeleteTasklet(pCurTasklet);
+                        }
 
-                            if (_suspendActive != 0)
-                            {
-                                // TODO: we should capture the sync and execution contexts at this point
-                                break;
-                            }
+                        if (_suspendActive != 0)
+                        {
+                            // TODO: we should capture the sync and execution contexts at this point
+                            break;
                         }
                     }
-                    finally
-                    {
-                        PopAsyncData(ref dataFrame);
-                    }
                 }
-                catch (Exception e)
+                finally
                 {
-                    SetException(e);
+                    PopAsyncData(ref dataFrame);
                 }
 
-                if (_suspendActive != 0)
-                {
-                    // We suspended again. Just register to return to this function, and return
-                    _awaiter!.OnCompleted(_resumption!);
-                    return;
-                }
-                // We are actually done. Set the final result
-                SetResultDone();
+                return _suspendActive!= 0;
             }
 
-            private Action? _taskResumer;
-            private T? _returnData;
-            public override ref byte GetReturnPointer()
+            protected sealed class TempAwaitable : ICriticalNotifyCompletion
             {
-                return ref Unsafe.As<T, byte>(ref _returnData!);
+                public INotifyCompletion? _awaiter;
+
+                public bool IsCompleted => false;
+
+                public void OnCompleted(Action action)
+                {
+                    _awaiter!.OnCompleted(action);
+                }
+
+                public TempAwaitable GetAwaiter() { return this; }
+
+                public void UnsafeOnCompleted(Action action)
+                {
+                    if (_awaiter is ICriticalNotifyCompletion criticalNotification)
+                    {
+                        criticalNotification.UnsafeOnCompleted(action);
+                    }
+                    else
+                    {
+                        _awaiter!.OnCompleted(action);
+                    }
+                }
+
+                public void GetResult() {}
             }
 
-            public RuntimeAsyncMaintainedData<T> GetAwaiter()
+            private async Task CompletionTaskReturnVoid()
             {
-                return this;
+                TempAwaitable awaitableObject = new TempAwaitable();
+                bool keepProcessing = true;
+                while (keepProcessing)
+                {
+                    awaitableObject._awaiter = _awaiter!;
+                    await awaitableObject;
+                    keepProcessing = ResumptionFunc();
+                }
             }
+        }
 
-            public bool IsCompleted => _completed;
+        // These are all implemented by the same assembly helper that will setup the tasklet in its new home on the stack
+        // and then tail-call into it. We will need a different entrypoint name for each type of register based return that can happen
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        internal static extern unsafe object ResumeTaskletReferenceReturn(Tasklet* pTasklet, ref RuntimeAsyncReturnValue retValue);
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        internal static extern unsafe IntPtr ResumeTaskletIntegerRegisterReturn(Tasklet* pTasklet, ref RuntimeAsyncReturnValue retValue);
 
-            public override Task GetTask()
+        internal sealed class RuntimeAsyncMaintainedData<T> : RuntimeAsyncMaintainedData
+        {
+            private T? _returnData;
+            public override ref byte GetReturnPointer()
             {
-                _awaiter!.OnCompleted(_resumption!);
-                return _task!;
+                return ref Unsafe.As<T, byte>(ref _returnData!);
             }
 
-            public void SetException(Exception exception)
+            public override Task GetTask()
             {
-                _exception = exception;
-                _completed = true;
-                _taskResumer!();
+                return CompletionTask();
             }
 
-            public void SetResultDone()
+            private async Task<T?> CompletionTask()
             {
+                TempAwaitable awaitableObject = new TempAwaitable();
+                bool keepProcessing = true;
+                while (keepProcessing)
+                {
+                    awaitableObject._awaiter = _awaiter!;
+                    await awaitableObject;
+                    keepProcessing = ResumptionFunc();
+                }
+
                 switch (_retValue._returnType)
                 {
                     case TaskletReturnType.Integer:
-                        _returnData = Unsafe.As<IntPtr, T>(ref _retValue._ptr);
-                        Thread.MemoryBarrier();
-                        break;
+                        return Unsafe.As<IntPtr, T>(ref _retValue._ptr);
                     case TaskletReturnType.ObjectReference:
-                        _returnData = Unsafe.As<object, T>(ref _retValue!._obj!)!;
-                        break;
+                        return Unsafe.As<object, T>(ref _retValue!._obj!)!;
                     default:
                         // Other possibiilities not yet implemented
                         throw new NotImplementedException();
                 }
-                _completed = true;
-                _taskResumer!();
-            }
-
-            public void OnCompleted(Action resumer) { throw new NotSupportedException(); }
-            public void UnsafeOnCompleted(Action resumer) { _taskResumer = resumer; }
-            public T? GetResult() { if (_exception != null) throw _exception; return _returnData; }
-
-            private async Task<T?> CompletionTask()
-            {
-                return await this;
             }
         }
 
@@ -443,13 +427,6 @@ public AsyncDataFrame(Func<RuntimeAsyncMaintainedData> getMaintainedData)
             public StackCrawlMark _crawlMark;
             public void* _next;
             public Func<RuntimeAsyncMaintainedData>? _createRuntimeMaintainedData;
-            public Thread _currentThread = Thread.CurrentThread;
-
-            // Store current ExecutionContext and SynchronizationContext as "previousXxx".
-            // This allows us to restore them and undo any Context changes made in stateMachine.MoveNext
-            // so that they won't "leak" out of the first await.
-            public ExecutionContext? _previousExecutionCtx;
-            public SynchronizationContext? _previousSyncCtx;
         }
 
         internal enum TaskletReturnType
@@ -481,28 +458,12 @@ internal unsafe struct Tasklet
         internal static unsafe void PushAsyncData(ref AsyncDataFrame asyncData)
         {
             asyncData._next = t_asyncData;
-            asyncData._previousExecutionCtx = asyncData._currentThread._executionContext;
-            asyncData._previousSyncCtx = asyncData._currentThread._synchronizationContext;
-
             t_asyncData = Unsafe.AsPointer(ref asyncData);
         }
 
         internal static unsafe void PopAsyncData(ref AsyncDataFrame asyncData)
         {
-            t_asyncData = Unsafe.AsRef<AsyncDataFrame>(t_asyncData)._next;
-
-            // The common case is that these have not changed, so avoid the cost of a write barrier if not needed.
-            if (asyncData._previousSyncCtx != asyncData._currentThread._synchronizationContext)
-            {
-                // Restore changed SynchronizationContext back to previous
-                asyncData._currentThread._synchronizationContext = asyncData._previousSyncCtx;
-            }
-
-            ExecutionContext? currentExecutionCtx = asyncData._currentThread._executionContext;
-            if (asyncData._previousExecutionCtx != currentExecutionCtx)
-            {
-                ExecutionContext.RestoreChangedContextToThread(asyncData._currentThread, asyncData._previousExecutionCtx, currentExecutionCtx);
-            }
+            t_asyncData = asyncData._next;
         }
 
         [MethodImpl(MethodImplOptions.NoInlining)]
@@ -534,7 +495,7 @@ private static unsafe ref AsyncDataFrame GetCurrentAsyncDataFrame()
         internal static extern unsafe void UnwindToFunctionWithAsyncFrame(Tasklet* topTasklet, nint framesToUnwind);
 
         [System.Security.DynamicSecurityMethod] // Methods containing StackCrawlMark local var has to be marked DynamicSecurityMethod
-        private static unsafe Action? GetOrCreateResumptionDelegate(ref StackCrawlMark stackMark)
+        private static unsafe bool DoSetupAndCapture(ref StackCrawlMark stackMark)
         {
             ref AsyncDataFrame asyncFrame = ref GetCurrentAsyncDataFrame();
 
@@ -554,11 +515,7 @@ private static unsafe ref AsyncDataFrame GetCurrentAsyncDataFrame()
 
             maintainedData._suspendActive = framesCaptured;
 
-            maintainedData._executionCtx = asyncFrame._currentThread._executionContext;
-            maintainedData._syncCtx = asyncFrame._currentThread._synchronizationContext;
-
-
-            return maintainedData._resumption;
+            return true;
         }
 #endif
     }
diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/Future.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/Future.cs
index 4d1917ddcfe202..ba716e869ec352 100644
--- a/src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/Future.cs
+++ b/src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/Future.cs
@@ -22,14 +22,39 @@ namespace System.Threading.Tasks
     internal struct RuntimeTaskState<TResult>
     {
         private RuntimeHelpers.AsyncDataFrame dataFrame;
+        // Store current ExecutionContext and SynchronizationContext as "previousXxx".
+        // This allows us to restore them and undo any Context changes made in stateMachine.MoveNext
+        // so that they won't "leak" out of the first await.
+        public ExecutionContext? _previousExecutionCtx;
+        public SynchronizationContext? _previousSyncCtx;
+        public Thread _thread;
 
         public void Push()
         {
-            dataFrame = new RuntimeHelpers.AsyncDataFrame(()=> new RuntimeHelpers.RuntimeAsyncMaintainedData<TResult>());
+            dataFrame = new RuntimeHelpers.AsyncDataFrame(()=> new RuntimeHelpers.RuntimeAsyncMaintainedData());
             RuntimeHelpers.PushAsyncData(ref dataFrame);
+            _thread = Thread.CurrentThread;
+            _previousExecutionCtx = _thread._executionContext;
+            _previousSyncCtx = _thread._synchronizationContext;
+        }
+
+        public void Pop()
+        {
+            // The common case is that these have not changed, so avoid the cost of a write barrier if not needed.
+            if (_previousSyncCtx != _thread._synchronizationContext)
+            {
+                // Restore changed SynchronizationContext back to previous
+                _thread._synchronizationContext = _previousSyncCtx;
+            }
+
+            ExecutionContext? currentExecutionCtx = _thread._executionContext;
+            if (_previousExecutionCtx != currentExecutionCtx)
+            {
+                ExecutionContext.RestoreChangedContextToThread(_thread, _previousExecutionCtx, currentExecutionCtx);
+            }
+            RuntimeHelpers.PopAsyncData(ref dataFrame);
         }
 
-        public void Pop() { RuntimeHelpers.PopAsyncData(ref dataFrame); }
         public Task<TResult> FromResult(TResult result)
         {
             if (dataFrame._maintainedData == null || (dataFrame._maintainedData._suspendActive == 0))
@@ -39,6 +64,144 @@ public Task<TResult> FromResult(TResult result)
 
         public Task<TResult> FromException(Exception e) { return Task.FromException<TResult>(e); }
     }
+    // Used to implement Runtime implemented Task suspension handling
+    internal struct RuntimeTaskState
+    {
+        private RuntimeHelpers.AsyncDataFrame dataFrame;
+        // Store current ExecutionContext and SynchronizationContext as "previousXxx".
+        // This allows us to restore them and undo any Context changes made in stateMachine.MoveNext
+        // so that they won't "leak" out of the first await.
+        public ExecutionContext? _previousExecutionCtx;
+        public SynchronizationContext? _previousSyncCtx;
+        public Thread _thread;
+
+        public void Push()
+        {
+            dataFrame = new RuntimeHelpers.AsyncDataFrame(()=> new RuntimeHelpers.RuntimeAsyncMaintainedData());
+            RuntimeHelpers.PushAsyncData(ref dataFrame);
+            _thread = Thread.CurrentThread;
+            _previousExecutionCtx = _thread._executionContext;
+            _previousSyncCtx = _thread._synchronizationContext;
+        }
+
+        public void Pop()
+        {
+            // The common case is that these have not changed, so avoid the cost of a write barrier if not needed.
+            if (_previousSyncCtx != _thread._synchronizationContext)
+            {
+                // Restore changed SynchronizationContext back to previous
+                _thread._synchronizationContext = _previousSyncCtx;
+            }
+
+            ExecutionContext? currentExecutionCtx = _thread._executionContext;
+            if (_previousExecutionCtx != currentExecutionCtx)
+            {
+                ExecutionContext.RestoreChangedContextToThread(_thread, _previousExecutionCtx, currentExecutionCtx);
+            }
+            RuntimeHelpers.PopAsyncData(ref dataFrame);
+        }
+
+        public Task FromResult()
+        {
+            if (dataFrame._maintainedData == null || (dataFrame._maintainedData._suspendActive == 0))
+                return Task.CompletedTask;
+            return dataFrame._maintainedData.GetTask();
+        }
+
+        public Task FromException(Exception e) { return Task.FromException(e); }
+    }
+    // Used to implement Runtime implemented Task suspension handling
+    internal struct RuntimeValueTaskState<TResult>
+    {
+        private RuntimeHelpers.AsyncDataFrame dataFrame;
+        // Store current ExecutionContext and SynchronizationContext as "previousXxx".
+        // This allows us to restore them and undo any Context changes made in stateMachine.MoveNext
+        // so that they won't "leak" out of the first await.
+        public ExecutionContext? _previousExecutionCtx;
+        public SynchronizationContext? _previousSyncCtx;
+        public Thread _thread;
+
+        public void Push()
+        {
+            dataFrame = new RuntimeHelpers.AsyncDataFrame(()=> new RuntimeHelpers.RuntimeAsyncMaintainedData());
+            RuntimeHelpers.PushAsyncData(ref dataFrame);
+            _thread = Thread.CurrentThread;
+            _previousExecutionCtx = _thread._executionContext;
+            _previousSyncCtx = _thread._synchronizationContext;
+        }
+
+        public void Pop()
+        {
+            // The common case is that these have not changed, so avoid the cost of a write barrier if not needed.
+            if (_previousSyncCtx != _thread._synchronizationContext)
+            {
+                // Restore changed SynchronizationContext back to previous
+                _thread._synchronizationContext = _previousSyncCtx;
+            }
+
+            ExecutionContext? currentExecutionCtx = _thread._executionContext;
+            if (_previousExecutionCtx != currentExecutionCtx)
+            {
+                ExecutionContext.RestoreChangedContextToThread(_thread, _previousExecutionCtx, currentExecutionCtx);
+            }
+            RuntimeHelpers.PopAsyncData(ref dataFrame);
+        }
+
+        public ValueTask<TResult> FromResult(TResult result)
+        {
+            if (dataFrame._maintainedData == null || (dataFrame._maintainedData._suspendActive == 0))
+                return ValueTask.FromResult<TResult>(result);
+            return new ValueTask<TResult>((Task<TResult>)dataFrame._maintainedData.GetTask());
+        }
+
+        public ValueTask<TResult> FromException(Exception e) { return ValueTask.FromException<TResult>(e); }
+    }
+    // Used to implement Runtime implemented Task suspension handling
+    internal struct RuntimeValueTaskState
+    {
+        private RuntimeHelpers.AsyncDataFrame dataFrame;
+        // Store current ExecutionContext and SynchronizationContext as "previousXxx".
+        // This allows us to restore them and undo any Context changes made in stateMachine.MoveNext
+        // so that they won't "leak" out of the first await.
+        public ExecutionContext? _previousExecutionCtx;
+        public SynchronizationContext? _previousSyncCtx;
+        public Thread _thread;
+
+        public void Push()
+        {
+            dataFrame = new RuntimeHelpers.AsyncDataFrame(()=> new RuntimeHelpers.RuntimeAsyncMaintainedData());
+            RuntimeHelpers.PushAsyncData(ref dataFrame);
+            _thread = Thread.CurrentThread;
+            _previousExecutionCtx = _thread._executionContext;
+            _previousSyncCtx = _thread._synchronizationContext;
+        }
+
+        public void Pop()
+        {
+            // The common case is that these have not changed, so avoid the cost of a write barrier if not needed.
+            if (_previousSyncCtx != _thread._synchronizationContext)
+            {
+                // Restore changed SynchronizationContext back to previous
+                _thread._synchronizationContext = _previousSyncCtx;
+            }
+
+            ExecutionContext? currentExecutionCtx = _thread._executionContext;
+            if (_previousExecutionCtx != currentExecutionCtx)
+            {
+                ExecutionContext.RestoreChangedContextToThread(_thread, _previousExecutionCtx, currentExecutionCtx);
+            }
+            RuntimeHelpers.PopAsyncData(ref dataFrame);
+        }
+
+        public ValueTask FromResult()
+        {
+            if (dataFrame._maintainedData == null || (dataFrame._maintainedData._suspendActive == 0))
+                return ValueTask.CompletedTask;
+            return new ValueTask(dataFrame._maintainedData.GetTask());
+        }
+
+        public ValueTask FromException(Exception e) { return ValueTask.FromException(e); }
+    }
 #pragma warning restore CA1822
 #endif
 

From 2909b7f3c30fa611455de33c999fac11351be191 Mon Sep 17 00:00:00 2001
From: David Wrighton <davidwr@microsoft.com>
Date: Tue, 31 Oct 2023 09:00:25 +0000
Subject: [PATCH 054/203] Merged PR 34919: Add support for ValueTask<T> and
 Task/ValueTask nongeneric to async2 Jit model

Add support for ValueTask<T> and Task/ValueTask nongeneric to async2 Jit model
- Also rework them to use an async method to do actual suspension/resumption, and store/restore ExecutionContext and SynchronizationContext
- This should allow async locals to work nicely (although I don't yet have tests for those)
---
 .../RuntimeHelpers.CoreCLR.cs                 | 141 +++++++++++++++---
 src/coreclr/vm/corelib.h                      |  16 +-
 src/coreclr/vm/metasig.h                      |   8 +
 src/coreclr/vm/prestub.cpp                    | 129 +++++++++++-----
 4 files changed, 234 insertions(+), 60 deletions(-)

diff --git a/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs
index 8c1884533f7d1f..54ad1749e96d1a 100644
--- a/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs
+++ b/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs
@@ -13,6 +13,39 @@
 
 namespace System.Runtime.CompilerServices
 {
+    internal struct ExecutionAndSyncBlockStore
+    {
+        // Store current ExecutionContext and SynchronizationContext as "previousXxx".
+        // This allows us to restore them and undo any Context changes made in stateMachine.MoveNext
+        // so that they won't "leak" out of the first await.
+        public ExecutionContext? _previousExecutionCtx;
+        public SynchronizationContext? _previousSyncCtx;
+        public Thread _thread;
+
+        public void Push()
+        {
+            _thread = Thread.CurrentThread;
+            _previousExecutionCtx = _thread._executionContext;
+            _previousSyncCtx = _thread._synchronizationContext;
+        }
+
+        public void Pop()
+        {
+            // The common case is that these have not changed, so avoid the cost of a write barrier if not needed.
+            if (_previousSyncCtx != _thread._synchronizationContext)
+            {
+                // Restore changed SynchronizationContext back to previous
+                _thread._synchronizationContext = _previousSyncCtx;
+            }
+
+            ExecutionContext? currentExecutionCtx = _thread._executionContext;
+            if (_previousExecutionCtx != currentExecutionCtx)
+            {
+                ExecutionContext.RestoreChangedContextToThread(_thread, _previousExecutionCtx, currentExecutionCtx);
+            }
+        }
+    }
+
     public static partial class RuntimeHelpers
     {
         [Intrinsic]
@@ -401,48 +434,112 @@ private struct RuntimeAsyncAwaitState
         [Intrinsic]
         private static void SuspendAsync2(Continuation continuation) => throw new UnreachableException();
 
-        private static Task<T> FinalizeTaskReturningThunk<T>(Continuation continuation)
+        private sealed class AwaitableProxy : ICriticalNotifyCompletion
+        {
+            public INotifyCompletion? _notifier;
+
+            public bool IsCompleted => false;
+
+            public void OnCompleted(Action action)
+            {
+                _notifier!.OnCompleted(action);
+            }
+
+            public AwaitableProxy GetAwaiter() { return this; }
+
+            public void UnsafeOnCompleted(Action action)
+            {
+                if (_notifier is ICriticalNotifyCompletion criticalNotification)
+                {
+                    criticalNotification.UnsafeOnCompleted(action);
+                }
+                else
+                {
+                    _notifier!.OnCompleted(action);
+                }
+            }
+
+            public void GetResult() {}
+        }
+
+        private static Continuation GetHeadContinuation(AwaitableProxy awaitableProxy)
         {
-            TaskCompletionSource<T> tcs = new();
             ref RuntimeAsyncAwaitState state = ref t_runtimeAsyncAwaitState;
-            object[] gcData = new object[2];
-            gcData[1] = tcs;
+            awaitableProxy._notifier = state.Notifier;
+            return state.SentinelContinuation!.Next!;
+        }
+
+        private static async Task<T> FinalizeTaskReturningThunk<T>(Continuation continuation)
+        {
+            object[] gcData = new object[1];
             continuation.Next = new Continuation
             {
-                Resume = &ResumeTaskCompletionSource<T>,
+                Resume = null,
                 GCData = gcData,
             };
 
-            Continuation headContinuation = state.SentinelContinuation!.Next!;
-            state.Notifier!.OnCompleted(() => DispatchContinuations(headContinuation));
-            return tcs.Task;
+            AwaitableProxy awaitableProxy = new AwaitableProxy();
+
+            Exception? ex = null;
+            while (true)
+            {
+                Continuation headContinuation = GetHeadContinuation(awaitableProxy);
+                await awaitableProxy;
+                Continuation? finalResult = DispatchContinuations(headContinuation, ref ex);
+                if (finalResult != null)
+                {
+                    if (ex != null)
+                        throw ex;
+                    return (T)continuation.GCData![0];
+                }
+            }
         }
 
-        private static Continuation? ResumeTaskCompletionSource<T>(Continuation continuation, Exception? exception)
+        private static async Task FinalizeTaskReturningThunk(Continuation continuation)
         {
-            var tcs = (TaskCompletionSource<T>)continuation.GCData![1];
-            if (exception == null)
+            object[] gcData = new object[1];
+            continuation.Next = new Continuation
             {
-                tcs.SetResult((T)continuation.GCData![0]);
-            }
-            else
+                Resume = null,
+                GCData = gcData,
+            };
+
+            AwaitableProxy awaitableProxy = new AwaitableProxy();
+
+            Exception? ex = null;
+            while (true)
             {
-                tcs.SetException(exception);
+                Continuation headContinuation = GetHeadContinuation(awaitableProxy);
+                await awaitableProxy;
+                Continuation? finalResult = DispatchContinuations(headContinuation, ref ex);
+                if (finalResult != null)
+                {
+                    if (ex != null)
+                        throw ex;
+                    return;
+                }
             }
-
-            return null;
         }
 
-        private static unsafe void DispatchContinuations(Continuation? continuation)
+        // Return a continuation object if that is the one which has the final result of the Task, in this case exceptionResult MAY be set to an exception, which is the real output of the series of continuations
+        // OR
+        // return NULL to indicate that this isn't yet done.
+        private static unsafe Continuation? DispatchContinuations(Continuation? continuation, ref Exception? exceptionResult)
         {
             Debug.Assert(continuation != null);
 
+            exceptionResult = null;
             Exception? exToPropagate = null;
             do
             {
                 Continuation? newContinuation = null;
                 try
                 {
+                    if (continuation.Resume == null)
+                    {
+                        exceptionResult = exToPropagate;
+                        return continuation; // Return the result containing Continuation
+                    }
                     newContinuation = continuation.Resume(continuation, exToPropagate);
                 }
                 catch (Exception ex)
@@ -453,14 +550,12 @@ private static unsafe void DispatchContinuations(Continuation? continuation)
                 if (newContinuation != null)
                 {
                     newContinuation.Next = continuation.Next;
-                    ref RuntimeAsyncAwaitState state = ref t_runtimeAsyncAwaitState;
-                    Continuation headContinuation = state.SentinelContinuation!.Next!;
-                    state.Notifier!.OnCompleted(() => DispatchContinuations(headContinuation));
-                    return;
+                    return null;
                 }
 
                 continuation = continuation.Next;
-            } while (continuation != null);
+                Debug.Assert(continuation != null);
+            } while (true);
         }
     }
 
diff --git a/src/coreclr/vm/corelib.h b/src/coreclr/vm/corelib.h
index 5d3cd995542893..c377fdfc134567 100644
--- a/src/coreclr/vm/corelib.h
+++ b/src/coreclr/vm/corelib.h
@@ -353,12 +353,17 @@ DEFINE_METHOD(THREAD_START_EXCEPTION,EX_CTOR,               .ctor,
 DEFINE_CLASS(VALUETASK_1, Tasks, ValueTask`1)
 
 DEFINE_CLASS(VALUETASK, Tasks, ValueTask)
+DEFINE_METHOD(VALUETASK, FROM_EXCEPTION, FromException, SM_Exception_RetValueTask)
+DEFINE_METHOD(VALUETASK, FROM_EXCEPTION_1, FromException, GM_Exception_RetValueTaskOfT)
+DEFINE_METHOD(VALUETASK, FROM_RESULT_T, FromResult, GM_T_RetValueTaskOfT)
+DEFINE_METHOD(VALUETASK, GET_COMPLETED_TASK, get_CompletedTask, SM_RetValueTask)
 
 DEFINE_CLASS(TASK_1, Tasks, Task`1)
 DEFINE_METHOD(TASK_1, GET_AWAITER, GetAwaiter, NoSig)
 
 DEFINE_CLASS(TASK, Tasks, Task)
-DEFINE_METHOD(TASK, FROM_EXCEPTION_T, FromException, GM_Exception_RetTaskOfT)
+DEFINE_METHOD(TASK, FROM_EXCEPTION, FromException, SM_Exception_RetTask)
+DEFINE_METHOD(TASK, FROM_EXCEPTION_1, FromException, GM_Exception_RetTaskOfT)
 DEFINE_METHOD(TASK, FROM_RESULT_T, FromResult, GM_T_RetTaskOfT)
 DEFINE_METHOD(TASK, GET_COMPLETED_TASK, get_CompletedTask, SM_RetTask)
 
@@ -695,6 +700,10 @@ DEFINE_CLASS(RESOURCE_MANAGER,      Resources,              ResourceManager)
 DEFINE_CLASS(RTFIELD,               Reflection,             RtFieldInfo)
 DEFINE_METHOD(RTFIELD,              GET_FIELDHANDLE,        GetFieldHandle,            IM_RetIntPtr)
 
+DEFINE_CLASS(EXECUTIONANDSYNCBLOCKSTORE, CompilerServices, ExecutionAndSyncBlockStore)
+DEFINE_METHOD(EXECUTIONANDSYNCBLOCKSTORE, PUSH, Push, NoSig)
+DEFINE_METHOD(EXECUTIONANDSYNCBLOCKSTORE, POP, Pop, NoSig)
+
 DEFINE_CLASS(RUNTIME_HELPERS,       CompilerServices,       RuntimeHelpers)
 DEFINE_METHOD(RUNTIME_HELPERS,      IS_REFERENCE_OR_CONTAINS_REFERENCES, IsReferenceOrContainsReferences, NoSig)
 DEFINE_METHOD(RUNTIME_HELPERS,      IS_BITWISE_EQUATABLE,    IsBitwiseEquatable, NoSig)
@@ -707,7 +716,10 @@ DEFINE_METHOD(RUNTIME_HELPERS,      ALLOC_TAILCALL_ARG_BUFFER, AllocTailCallArgB
 DEFINE_METHOD(RUNTIME_HELPERS,      GET_TAILCALL_INFO,      GetTailCallInfo, NoSig)
 DEFINE_METHOD(RUNTIME_HELPERS,      DISPATCH_TAILCALLS,     DispatchTailCalls, NoSig)
 DEFINE_METHOD(RUNTIME_HELPERS,      ALLOC_CONTINUATION,     AllocContinuation, NoSig)
-DEFINE_METHOD(RUNTIME_HELPERS,      FINALIZE_TASK_RETURNING_THUNK, FinalizeTaskReturningThunk, GM_Continuation_RetTaskOfT)
+DEFINE_METHOD(RUNTIME_HELPERS,      FINALIZE_TASK_RETURNING_THUNK, FinalizeTaskReturningThunk, SM_Continuation_RetTask)
+DEFINE_METHOD(RUNTIME_HELPERS,      FINALIZE_TASK_RETURNING_THUNK_1, FinalizeTaskReturningThunk, GM_Continuation_RetTaskOfT)
+DEFINE_METHOD(RUNTIME_HELPERS,      FINALIZE_VALUETASK_RETURNING_THUNK, FinalizeValueTaskReturningThunk, SM_Continuation_RetValueTask)
+DEFINE_METHOD(RUNTIME_HELPERS,      FINALIZE_VALUETASK_RETURNING_THUNK_1, FinalizeValueTaskReturningThunk, GM_Continuation_RetValueTaskOfT)
 
 DEFINE_METHOD(RUNTIME_HELPERS,      GET_OR_CREATE_RESUMPTION_DELEGATE, GetOrCreateResumptionDelegate, NoSig)
 DEFINE_METHOD(RUNTIME_HELPERS,      UNSAFE_AWAIT_AWAITER_FROM_RUNTIME_ASYNC_1, UnsafeAwaitAwaiterFromRuntimeAsync, GM_T_RetVoid)
diff --git a/src/coreclr/vm/metasig.h b/src/coreclr/vm/metasig.h
index a77b611b000e21..44030a529d9860 100644
--- a/src/coreclr/vm/metasig.h
+++ b/src/coreclr/vm/metasig.h
@@ -588,8 +588,13 @@ DEFINE_METASIG_T(IM(RetValueTask, _, g(VALUETASK)))
 
 DEFINE_METASIG_T(GM(Exception_RetTaskOfT, IMAGE_CEE_CS_CALLCONV_DEFAULT, 1, C(EXCEPTION), GI(C(TASK_1), 1, M(0))))
 DEFINE_METASIG_T(GM(T_RetTaskOfT, IMAGE_CEE_CS_CALLCONV_DEFAULT, 1, M(0), GI(C(TASK_1), 1, M(0))))
+DEFINE_METASIG_T(GM(Exception_RetValueTaskOfT, IMAGE_CEE_CS_CALLCONV_DEFAULT, 1, C(EXCEPTION), GI(g(VALUETASK_1), 1, M(0))))
+DEFINE_METASIG_T(GM(T_RetValueTaskOfT, IMAGE_CEE_CS_CALLCONV_DEFAULT, 1, M(0), GI(g(VALUETASK_1), 1, M(0))))
 
 DEFINE_METASIG_T(SM(RetTask, , C(TASK)))
+DEFINE_METASIG_T(SM(RetValueTask, , g(VALUETASK)))
+DEFINE_METASIG_T(SM(Exception_RetTask, C(EXCEPTION), C(TASK)))
+DEFINE_METASIG_T(SM(Exception_RetValueTask, C(EXCEPTION), g(VALUETASK)))
 
 DEFINE_METASIG(GM(RefT_T_T_RetT, IMAGE_CEE_CS_CALLCONV_DEFAULT, 1, r(M(0)) M(0) M(0), M(0)))
 DEFINE_METASIG(SM(RefObject_Object_Object_RetObject, r(j) j j, j))
@@ -635,7 +640,10 @@ DEFINE_METASIG(SM(PtrByte_RetVoid, P(b), v))
 
 DEFINE_METASIG_T(SM(RetContinuation, , C(CONTINUATION)))
 DEFINE_METASIG(GM(T_RetVoid, IMAGE_CEE_CS_CALLCONV_DEFAULT, 1, M(0), v))
+DEFINE_METASIG_T(SM(Continuation_RetTask, C(CONTINUATION), C(TASK)))
 DEFINE_METASIG_T(GM(Continuation_RetTaskOfT, IMAGE_CEE_CS_CALLCONV_DEFAULT, 1, C(CONTINUATION), GI(C(TASK_1), 1, M(0))))
+DEFINE_METASIG_T(SM(Continuation_RetValueTask, C(CONTINUATION), g(VALUETASK)))
+DEFINE_METASIG_T(GM(Continuation_RetValueTaskOfT, IMAGE_CEE_CS_CALLCONV_DEFAULT, 1, C(CONTINUATION), GI(g(VALUETASK_1), 1, M(0))))
 
 // Undefine macros in case we include the file again in the compilation unit
 
diff --git a/src/coreclr/vm/prestub.cpp b/src/coreclr/vm/prestub.cpp
index b170843a59da42..f9d607c4e26442 100644
--- a/src/coreclr/vm/prestub.cpp
+++ b/src/coreclr/vm/prestub.cpp
@@ -1724,6 +1724,9 @@ void MethodDesc::EmitJitStateMachineBasedRuntimeAsyncThunk(MethodDesc* pAsyncOth
     unsigned continuationLocal = pCode->NewLocal(LocalDesc(CoreLibBinder::GetClass(CLASS__CONTINUATION)));
 
     TypeHandle thTaskRet = thunkMsig.GetRetTypeHandleThrowing();
+
+    bool isValueTask = thTaskRet.GetMethodTable()->IsValueType();
+
     LocalDesc returnLocalDesc(thTaskRet);
     DWORD returnLocal = pCode->NewLocal(returnLocalDesc);
 
@@ -1738,50 +1741,84 @@ void MethodDesc::EmitJitStateMachineBasedRuntimeAsyncThunk(MethodDesc* pAsyncOth
     LocalDesc exceptionLocalDesc(CoreLibBinder::GetClass(CLASS__EXCEPTION));
     DWORD exceptionLocal = pCode->NewLocal(exceptionLocalDesc);
 
+    LocalDesc executionAndSyncBlockStoreLocalDesc(CoreLibBinder::GetClass(CLASS__EXECUTIONANDSYNCBLOCKSTORE));
+    DWORD executionAndSyncBlockStoreLocal = pCode->NewLocal(executionAndSyncBlockStoreLocalDesc);
+
     ILCodeLabel* pNoExceptionLabel = pCode->NewCodeLabel();
     ILCodeLabel* pReturnResultLabel = pCode->NewCodeLabel();
     ILCodeLabel* pSuspendedLabel = pCode->NewCodeLabel();
 
+    pCode->EmitLDLOCA(executionAndSyncBlockStoreLocal);
+    pCode->EmitCALL(pCode->GetToken(CoreLibBinder::GetMethod(METHOD__EXECUTIONANDSYNCBLOCKSTORE__PUSH)), 1, 0);
+
     {
         pCode->BeginTryBlock();
-
-        DWORD localArg = 0;
-        if (asyncMsig.HasThis())
-        {
-            pCode->EmitLDARG(localArg++);
-        }
-        if ((asyncMsig.GetCallingConventionInfo() & CORINFO_CALLCONV_PARAMTYPE) != 0)
+        pCode->EmitNOP("Separate try blocks");
         {
-            _ASSERTE(!"Cannot handle generic context");
-        }
-        _ASSERTE((asyncMsig.GetCallingConventionInfo() & CORINFO_CALLCONV_ASYNCCALL) != 0);
+            pCode->BeginTryBlock();
+
+            DWORD localArg = 0;
+            if (asyncMsig.HasThis())
+            {
+                pCode->EmitLDARG(localArg++);
+            }
+            if ((asyncMsig.GetCallingConventionInfo() & CORINFO_CALLCONV_PARAMTYPE) != 0)
+            {
+                _ASSERTE(!"Cannot handle generic context");
+            }
+            _ASSERTE((asyncMsig.GetCallingConventionInfo() & CORINFO_CALLCONV_ASYNCCALL) != 0);
 
-        pCode->EmitLDNULL(); // Async continuation; not resuming
+            pCode->EmitLDNULL(); // Async continuation; not resuming
+
+            for (UINT iArg = 0; iArg < asyncMsig.NumFixedArgs(); iArg++)
+            {
+                pCode->EmitLDARG(localArg++);
+            }
 
-        for (UINT iArg = 0; iArg < asyncMsig.NumFixedArgs(); iArg++)
+            // TODO: This and instantiating stubs can directly load the target from the precode slot
+            pCode->EmitLDC(pAsyncOtherVariant->GetMultiCallableAddrOfCode(CORINFO_ACCESS_ANY));
+            pCode->EmitCALLI(pCode->GetSigToken(pTargetSig, cbTargetSig), localArg + 2, logicalResultLocal != UINT_MAX ? 1 : 0);
+            if (logicalResultLocal != UINT_MAX)
+                pCode->EmitSTLOC(logicalResultLocal);
+            pCode->EmitCALL(METHOD__STUBHELPERS__ASYNC2_CALL_CONTINUATION, 0, 1);
+            pCode->EmitSTLOC(continuationLocal);
+            pCode->EmitLEAVE(pNoExceptionLabel);
+            pCode->EndTryBlock();
+        }
+        // Catch
         {
-            pCode->EmitLDARG(localArg++);
+            pCode->BeginCatchBlock(pCode->GetToken(CoreLibBinder::GetClass(CLASS__EXCEPTION)));
+            MethodDesc* fromExceptionMD;
+            if (logicalResultLocal != UINT_MAX)
+            {
+                MethodDesc *md;
+                if (isValueTask)
+                    md = CoreLibBinder::GetMethod(METHOD__VALUETASK__FROM_EXCEPTION_1);
+                else
+                    md = CoreLibBinder::GetMethod(METHOD__TASK__FROM_EXCEPTION_1);
+                fromExceptionMD = FindOrCreateAssociatedMethodDesc(md, md->GetMethodTable(), FALSE, Instantiation(&thLogicalRetType, 1), FALSE);
+            }
+            else
+            {
+                if (isValueTask)
+                    fromExceptionMD = CoreLibBinder::GetMethod(METHOD__VALUETASK__FROM_EXCEPTION);
+                else
+                    fromExceptionMD = CoreLibBinder::GetMethod(METHOD__TASK__FROM_EXCEPTION);
+            }
+            pCode->EmitCALL(pCode->GetToken(fromExceptionMD), 1, 1);
+            pCode->EmitSTLOC(returnLocal);
+            pCode->EmitLEAVE(pReturnResultLabel);
+            pCode->EndCatchBlock();
         }
-
-        // TODO: This and instantiating stubs can directly load the target from the precode slot
-        pCode->EmitLDC(pAsyncOtherVariant->GetMultiCallableAddrOfCode(CORINFO_ACCESS_ANY));
-        pCode->EmitCALLI(pCode->GetSigToken(pTargetSig, cbTargetSig), localArg + 2, logicalResultLocal != UINT_MAX ? 1 : 0);
-        if (logicalResultLocal != UINT_MAX)
-            pCode->EmitSTLOC(logicalResultLocal);
-        pCode->EmitCALL(METHOD__STUBHELPERS__ASYNC2_CALL_CONTINUATION, 0, 1);
-        pCode->EmitSTLOC(continuationLocal);
-        pCode->EmitLEAVE(pNoExceptionLabel);
         pCode->EndTryBlock();
     }
-    // Catch
+    //
     {
-        pCode->BeginCatchBlock(pCode->GetToken(CoreLibBinder::GetClass(CLASS__EXCEPTION)));
-        MethodDesc* md = CoreLibBinder::GetMethod(METHOD__TASK__FROM_EXCEPTION_T);
-        md = FindOrCreateAssociatedMethodDesc(md, md->GetMethodTable(), FALSE, Instantiation(&thLogicalRetType, 1), FALSE);
-        pCode->EmitCALL(pCode->GetToken(md), 1, 1);
-        pCode->EmitSTLOC(returnLocal);
-        pCode->EmitLEAVE(pReturnResultLabel);
-        pCode->EndCatchBlock();
+        pCode->BeginFinallyBlock();
+        pCode->EmitLDLOCA(executionAndSyncBlockStoreLocal);
+        pCode->EmitCALL(pCode->GetToken(CoreLibBinder::GetMethod(METHOD__EXECUTIONANDSYNCBLOCKSTORE__POP)), 1, 0);
+        pCode->EmitENDFINALLY();
+        pCode->EndFinallyBlock();
     }
 
     pCode->EmitLabel(pNoExceptionLabel);
@@ -1790,13 +1827,20 @@ void MethodDesc::EmitJitStateMachineBasedRuntimeAsyncThunk(MethodDesc* pAsyncOth
     if (logicalResultLocal != UINT_MAX)
     {
         pCode->EmitLDLOC(logicalResultLocal);
-        MethodDesc* md = CoreLibBinder::GetMethod(METHOD__TASK__FROM_RESULT_T);
+        MethodDesc* md;
+        if (isValueTask)
+            md = CoreLibBinder::GetMethod(METHOD__VALUETASK__FROM_RESULT_T);
+        else
+            md = CoreLibBinder::GetMethod(METHOD__TASK__FROM_RESULT_T);
         md = FindOrCreateAssociatedMethodDesc(md, md->GetMethodTable(), FALSE, Instantiation(&thLogicalRetType, 1), FALSE);
         pCode->EmitCALL(pCode->GetToken(md), 1, 1);
     }
     else
     {
-        pCode->EmitCALL(METHOD__TASK__GET_COMPLETED_TASK, 0, 1);
+        if (isValueTask)
+            pCode->EmitCALL(METHOD__VALUETASK__GET_COMPLETED_TASK, 0, 1);
+        else
+            pCode->EmitCALL(METHOD__TASK__GET_COMPLETED_TASK, 0, 1);
     }
 
     pCode->EmitSTLOC(returnLocal);
@@ -1806,10 +1850,25 @@ void MethodDesc::EmitJitStateMachineBasedRuntimeAsyncThunk(MethodDesc* pAsyncOth
 
     pCode->EmitLabel(pSuspendedLabel);
 
-    MethodDesc* md = CoreLibBinder::GetMethod(METHOD__RUNTIME_HELPERS__FINALIZE_TASK_RETURNING_THUNK);
-    md = FindOrCreateAssociatedMethodDesc(md, md->GetMethodTable(), FALSE, Instantiation(&thLogicalRetType, 1), FALSE);
+    MethodDesc* finalizeTaskReturningThunkMD;
+    if (logicalResultLocal != UINT_MAX)
+    {
+        MethodDesc* md;
+        if (isValueTask)
+            md = CoreLibBinder::GetMethod(METHOD__RUNTIME_HELPERS__FINALIZE_VALUETASK_RETURNING_THUNK_1);
+        else
+            md = CoreLibBinder::GetMethod(METHOD__RUNTIME_HELPERS__FINALIZE_TASK_RETURNING_THUNK_1);
+        finalizeTaskReturningThunkMD = FindOrCreateAssociatedMethodDesc(md, md->GetMethodTable(), FALSE, Instantiation(&thLogicalRetType, 1), FALSE);
+    }
+    else
+    {
+        if (isValueTask)
+            finalizeTaskReturningThunkMD = CoreLibBinder::GetMethod(METHOD__RUNTIME_HELPERS__FINALIZE_VALUETASK_RETURNING_THUNK);
+        else
+            finalizeTaskReturningThunkMD = CoreLibBinder::GetMethod(METHOD__RUNTIME_HELPERS__FINALIZE_TASK_RETURNING_THUNK);
+    }
     pCode->EmitLDLOC(continuationLocal);
-    pCode->EmitCALL(pCode->GetToken(md), 1, 1);
+    pCode->EmitCALL(pCode->GetToken(finalizeTaskReturningThunkMD), 1, 1);
     pCode->EmitRET();
 }
 

From 4aa0bcf146e7cd1849c9029b87a1ad89c56f91d1 Mon Sep 17 00:00:00 2001
From: Jakob Botsch Nielsen <jakob.botsch.nielsen@gmail.com>
Date: Tue, 31 Oct 2023 10:47:33 +0100
Subject: [PATCH 055/203] Fix build failure

---
 .../RuntimeHelpers.CoreCLR.cs                 | 52 +++++++++++++++++++
 src/coreclr/vm/corelib.h                      |  1 -
 2 files changed, 52 insertions(+), 1 deletion(-)

diff --git a/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs
index 54ad1749e96d1a..c33443b72058df 100644
--- a/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs
+++ b/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs
@@ -521,6 +521,58 @@ private static async Task FinalizeTaskReturningThunk(Continuation continuation)
             }
         }
 
+        private static async ValueTask<T> FinalizeValueTaskReturningThunk<T>(Continuation continuation)
+        {
+            object[] gcData = new object[1];
+            continuation.Next = new Continuation
+            {
+                Resume = null,
+                GCData = gcData,
+            };
+
+            AwaitableProxy awaitableProxy = new AwaitableProxy();
+
+            Exception? ex = null;
+            while (true)
+            {
+                Continuation headContinuation = GetHeadContinuation(awaitableProxy);
+                await awaitableProxy;
+                Continuation? finalResult = DispatchContinuations(headContinuation, ref ex);
+                if (finalResult != null)
+                {
+                    if (ex != null)
+                        throw ex;
+                    return (T)continuation.GCData![0];
+                }
+            }
+        }
+
+        private static async ValueTask FinalizeValueTaskReturningThunk(Continuation continuation)
+        {
+            object[] gcData = new object[1];
+            continuation.Next = new Continuation
+            {
+                Resume = null,
+                GCData = gcData,
+            };
+
+            AwaitableProxy awaitableProxy = new AwaitableProxy();
+
+            Exception? ex = null;
+            while (true)
+            {
+                Continuation headContinuation = GetHeadContinuation(awaitableProxy);
+                await awaitableProxy;
+                Continuation? finalResult = DispatchContinuations(headContinuation, ref ex);
+                if (finalResult != null)
+                {
+                    if (ex != null)
+                        throw ex;
+                    return;
+                }
+            }
+        }
+
         // Return a continuation object if that is the one which has the final result of the Task, in this case exceptionResult MAY be set to an exception, which is the real output of the series of continuations
         // OR
         // return NULL to indicate that this isn't yet done.
diff --git a/src/coreclr/vm/corelib.h b/src/coreclr/vm/corelib.h
index c377fdfc134567..3bda6114bc2945 100644
--- a/src/coreclr/vm/corelib.h
+++ b/src/coreclr/vm/corelib.h
@@ -721,7 +721,6 @@ DEFINE_METHOD(RUNTIME_HELPERS,      FINALIZE_TASK_RETURNING_THUNK_1, FinalizeTas
 DEFINE_METHOD(RUNTIME_HELPERS,      FINALIZE_VALUETASK_RETURNING_THUNK, FinalizeValueTaskReturningThunk, SM_Continuation_RetValueTask)
 DEFINE_METHOD(RUNTIME_HELPERS,      FINALIZE_VALUETASK_RETURNING_THUNK_1, FinalizeValueTaskReturningThunk, GM_Continuation_RetValueTaskOfT)
 
-DEFINE_METHOD(RUNTIME_HELPERS,      GET_OR_CREATE_RESUMPTION_DELEGATE, GetOrCreateResumptionDelegate, NoSig)
 DEFINE_METHOD(RUNTIME_HELPERS,      UNSAFE_AWAIT_AWAITER_FROM_RUNTIME_ASYNC_1, UnsafeAwaitAwaiterFromRuntimeAsync, GM_T_RetVoid)
 DEFINE_METHOD(RUNTIME_HELPERS,      AWAIT_AWAITER_FROM_RUNTIME_ASYNC_1, AwaitAwaiterFromRuntimeAsync, GM_T_RetVoid)
 

From ed1e50bb5e393469ed3fe23d022a7c260fde4378 Mon Sep 17 00:00:00 2001
From: Jakob Botsch Nielsen <jakob.botsch.nielsen@gmail.com>
Date: Tue, 31 Oct 2023 10:59:04 +0100
Subject: [PATCH 056/203] Fix test build

---
 src/tests/Loader/async/async2implement.cs | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/tests/Loader/async/async2implement.cs b/src/tests/Loader/async/async2implement.cs
index 6eb63e715c7945..6871f815227949 100644
--- a/src/tests/Loader/async/async2implement.cs
+++ b/src/tests/Loader/async/async2implement.cs
@@ -47,7 +47,7 @@ public async2 int M1()
 
     class Derived2a : IBase2
     {
-        public Task<int> M1()
+        public async Task<int> M1()
         {
             await Task.Yield();
             return 22;

From b3f5b6f199069cf532d933c4cb48ad4a3ea3993b Mon Sep 17 00:00:00 2001
From: Jakob Botsch Nielsen <jakob.botsch.nielsen@gmail.com>
Date: Tue, 31 Oct 2023 11:14:11 +0100
Subject: [PATCH 057/203] Fix getting return value from continuation

---
 .../System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs
index c33443b72058df..ee0c77db898d28 100644
--- a/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs
+++ b/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs
@@ -490,7 +490,7 @@ private static async Task<T> FinalizeTaskReturningThunk<T>(Continuation continua
                 {
                     if (ex != null)
                         throw ex;
-                    return (T)continuation.GCData![0];
+                    return (T)finalResult.GCData![0];
                 }
             }
         }
@@ -542,7 +542,7 @@ private static async ValueTask<T> FinalizeValueTaskReturningThunk<T>(Continuatio
                 {
                     if (ex != null)
                         throw ex;
-                    return (T)continuation.GCData![0];
+                    return (T)finalResult.GCData![0];
                 }
             }
         }

From e7d4a009d7f3d4479ab81f6932bfaeb416ee32bd Mon Sep 17 00:00:00 2001
From: Jakob Botsch Nielsen <janielsen@microsoft.com>
Date: Tue, 31 Oct 2023 18:46:41 +0000
Subject: [PATCH 058/203] Merged PR 34944: Add EH support to the JIT SM
 prototype

---
 .../RuntimeHelpers.CoreCLR.cs                 |  98 ++++++++++-------
 src/coreclr/inc/corinfo.h                     |  17 +++
 src/coreclr/inc/jithelpers.h                  |   1 +
 src/coreclr/jit/async.cpp                     | 104 +++++++++++++++---
 src/coreclr/jit/async.h                       |   2 +
 .../Common/JitInterface/CorInfoHelpFunc.cs    |   1 +
 .../tools/superpmi/superpmi-shared/agnostic.h |   1 +
 .../superpmi-shared/methodcontext.cpp         |   7 +-
 src/coreclr/vm/corelib.h                      |   1 +
 src/coreclr/vm/jithelpers.cpp                 |  34 ++++++
 src/coreclr/vm/jitinterface.cpp               |   4 +-
 src/tests/Loader/async/simple-eh.cs           |  52 +++++++++
 src/tests/Loader/async/simple-eh.csproj       |   9 ++
 13 files changed, 276 insertions(+), 55 deletions(-)
 create mode 100644 src/tests/Loader/async/simple-eh.cs
 create mode 100644 src/tests/Loader/async/simple-eh.csproj

diff --git a/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs
index ee0c77db898d28..f546c02c1c0db6 100644
--- a/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs
+++ b/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs
@@ -471,21 +471,19 @@ private static Continuation GetHeadContinuation(AwaitableProxy awaitableProxy)
 
         private static async Task<T> FinalizeTaskReturningThunk<T>(Continuation continuation)
         {
-            object[] gcData = new object[1];
             continuation.Next = new Continuation
             {
-                Resume = null,
-                GCData = gcData,
+                Flags = CorInfoContinuationFlags.CORINFO_CONTINUATION_RESULT_IN_GCDATA | CorInfoContinuationFlags.CORINFO_CONTINUATION_NEEDS_EXCEPTION,
+                GCData = new object[1],
             };
 
             AwaitableProxy awaitableProxy = new AwaitableProxy();
 
-            Exception? ex = null;
             while (true)
             {
                 Continuation headContinuation = GetHeadContinuation(awaitableProxy);
                 await awaitableProxy;
-                Continuation? finalResult = DispatchContinuations(headContinuation, ref ex);
+                Continuation? finalResult = DispatchContinuations(headContinuation, out Exception? ex);
                 if (finalResult != null)
                 {
                     if (ex != null)
@@ -497,21 +495,18 @@ private static async Task<T> FinalizeTaskReturningThunk<T>(Continuation continua
 
         private static async Task FinalizeTaskReturningThunk(Continuation continuation)
         {
-            object[] gcData = new object[1];
             continuation.Next = new Continuation
             {
-                Resume = null,
-                GCData = gcData,
+                Flags = CorInfoContinuationFlags.CORINFO_CONTINUATION_NEEDS_EXCEPTION,
             };
 
             AwaitableProxy awaitableProxy = new AwaitableProxy();
 
-            Exception? ex = null;
             while (true)
             {
                 Continuation headContinuation = GetHeadContinuation(awaitableProxy);
                 await awaitableProxy;
-                Continuation? finalResult = DispatchContinuations(headContinuation, ref ex);
+                Continuation? finalResult = DispatchContinuations(headContinuation, out Exception? ex);
                 if (finalResult != null)
                 {
                     if (ex != null)
@@ -523,21 +518,19 @@ private static async Task FinalizeTaskReturningThunk(Continuation continuation)
 
         private static async ValueTask<T> FinalizeValueTaskReturningThunk<T>(Continuation continuation)
         {
-            object[] gcData = new object[1];
             continuation.Next = new Continuation
             {
-                Resume = null,
-                GCData = gcData,
+                Flags = CorInfoContinuationFlags.CORINFO_CONTINUATION_RESULT_IN_GCDATA | CorInfoContinuationFlags.CORINFO_CONTINUATION_NEEDS_EXCEPTION,
+                GCData = new object[1],
             };
 
             AwaitableProxy awaitableProxy = new AwaitableProxy();
 
-            Exception? ex = null;
             while (true)
             {
                 Continuation headContinuation = GetHeadContinuation(awaitableProxy);
                 await awaitableProxy;
-                Continuation? finalResult = DispatchContinuations(headContinuation, ref ex);
+                Continuation? finalResult = DispatchContinuations(headContinuation, out Exception? ex);
                 if (finalResult != null)
                 {
                     if (ex != null)
@@ -549,21 +542,15 @@ private static async ValueTask<T> FinalizeValueTaskReturningThunk<T>(Continuatio
 
         private static async ValueTask FinalizeValueTaskReturningThunk(Continuation continuation)
         {
-            object[] gcData = new object[1];
-            continuation.Next = new Continuation
-            {
-                Resume = null,
-                GCData = gcData,
-            };
+            continuation.Next = new Continuation();
 
             AwaitableProxy awaitableProxy = new AwaitableProxy();
 
-            Exception? ex = null;
             while (true)
             {
                 Continuation headContinuation = GetHeadContinuation(awaitableProxy);
                 await awaitableProxy;
-                Continuation? finalResult = DispatchContinuations(headContinuation, ref ex);
+                Continuation? finalResult = DispatchContinuations(headContinuation, out Exception? ex);
                 if (finalResult != null)
                 {
                     if (ex != null)
@@ -573,30 +560,34 @@ private static async ValueTask FinalizeValueTaskReturningThunk(Continuation cont
             }
         }
 
-        // Return a continuation object if that is the one which has the final result of the Task, in this case exceptionResult MAY be set to an exception, which is the real output of the series of continuations
+        // Return a continuation object if that is the one which has the final
+        // result of the Task, in this case exceptionResult MAY be set to an
+        // exception, which is the real output of the series of continuations
         // OR
         // return NULL to indicate that this isn't yet done.
-        private static unsafe Continuation? DispatchContinuations(Continuation? continuation, ref Exception? exceptionResult)
+        private static unsafe Continuation? DispatchContinuations(Continuation? continuation, out Exception? exceptionResult)
         {
             Debug.Assert(continuation != null);
 
             exceptionResult = null;
-            Exception? exToPropagate = null;
-            do
+            while (true)
             {
-                Continuation? newContinuation = null;
+                Continuation? newContinuation;
                 try
                 {
-                    if (continuation.Resume == null)
-                    {
-                        exceptionResult = exToPropagate;
-                        return continuation; // Return the result containing Continuation
-                    }
-                    newContinuation = continuation.Resume(continuation, exToPropagate);
+                    newContinuation = continuation.Resume(continuation);
                 }
                 catch (Exception ex)
                 {
-                    exToPropagate = ex;
+                    continuation = UnwindToPossibleHandler(continuation);
+                    if (continuation.Resume == null)
+                    {
+                        exceptionResult = ex;
+                        return continuation;
+                    }
+
+                    continuation.GCData![(continuation.Flags & CorInfoContinuationFlags.CORINFO_CONTINUATION_RESULT_IN_GCDATA) != 0 ? 1 : 0] = ex;
+                    continue;
                 }
 
                 if (newContinuation != null)
@@ -607,7 +598,23 @@ private static async ValueTask FinalizeValueTaskReturningThunk(Continuation cont
 
                 continuation = continuation.Next;
                 Debug.Assert(continuation != null);
-            } while (true);
+
+                if (continuation.Resume == null)
+                {
+                    return continuation; // Return the result containing Continuation
+                }
+            }
+        }
+
+        private static Continuation UnwindToPossibleHandler(Continuation continuation)
+        {
+            while (true)
+            {
+                Debug.Assert(continuation.Next != null);
+                continuation = continuation.Next;
+                if ((continuation.Flags & CorInfoContinuationFlags.CORINFO_CONTINUATION_NEEDS_EXCEPTION) != 0)
+                    return continuation;
+            }
         }
     }
 
@@ -891,11 +898,28 @@ internal unsafe struct TailCallTls
         public IntPtr ArgBuffer;
     }
 
+    [Flags]
+    internal enum CorInfoContinuationFlags
+    {
+        // Whether or not the continuation expects the result to be boxed and
+        // placed in the GCData array at index 0. Not set if the callee is void.
+        // TODO: In the future, for value types without any GC refs in them, we can
+        // place them at the beginning of the Data array instead to avoid a box.
+        CORINFO_CONTINUATION_RESULT_IN_GCDATA = 1,
+        // If this bit is set the continuation resumes inside a try block and thus
+        // if an exception is being propagated, needs to be resumed. The exception
+        // should be placed at index 0 or 1 depending on whether the continuation
+        // also expects a result.
+        CORINFO_CONTINUATION_NEEDS_EXCEPTION = 2,
+    }
+
+
     internal sealed unsafe class Continuation
     {
         public Continuation? Next;
-        public delegate*<Continuation, Exception?, Continuation?> Resume;
+        public delegate*<Continuation, Continuation?> Resume;
         public uint State;
+        public CorInfoContinuationFlags Flags;
         public byte[]? Data;
         public object[]? GCData;
     }
diff --git a/src/coreclr/inc/corinfo.h b/src/coreclr/inc/corinfo.h
index 5001e2038070e7..26a1b2380e6051 100644
--- a/src/coreclr/inc/corinfo.h
+++ b/src/coreclr/inc/corinfo.h
@@ -455,6 +455,7 @@ enum CorInfoHelpFunc
 
     CORINFO_HELP_THROW,             // Throw an exception object
     CORINFO_HELP_RETHROW,           // Rethrow the currently active exception
+    CORINFO_HELP_THROWEXACT,        // Throw an exception object, preserving stack trace
     CORINFO_HELP_USER_BREAKPOINT,   // For a user program to break to the debugger
     CORINFO_HELP_RNGCHKFAIL,        // array bounds check failed
     CORINFO_HELP_OVERFLOW,          // throw an overflow exception
@@ -1824,6 +1825,20 @@ struct CORINFO_EE_INFO
     CORINFO_OS  osType;
 };
 
+enum CorInfoContinuationFlags
+{
+    // Whether or not the continuation expects the result to be boxed and
+    // placed in the GCData array at index 0. Not set if the callee is void.
+    // TODO: In the future, for value types without any GC refs in them, we can
+    // place them at the beginning of the Data array instead to avoid a box.
+    CORINFO_CONTINUATION_RESULT_IN_GCDATA = 1,
+    // If this bit is set the continuation resumes inside a try block and thus
+    // if an exception is being propagated, needs to be resumed. The exception
+    // should be placed at index 0 or 1 depending on whether the continuation
+    // also expects a result.
+    CORINFO_CONTINUATION_NEEDS_EXCEPTION = 2,
+};
+
 struct CORINFO_ASYNC2_INFO
 {
     // Class handle for System.Runtime.CompilerServices.Continuation
@@ -1834,6 +1849,8 @@ struct CORINFO_ASYNC2_INFO
     CORINFO_FIELD_HANDLE continuationResumeFldHnd;
     // 'State' field
     CORINFO_FIELD_HANDLE continuationStateFldHnd;
+    // 'Flags' field
+    CORINFO_FIELD_HANDLE continuationFlagsFldHnd;
     // 'Data' field
     CORINFO_FIELD_HANDLE continuationDataFldHnd;
     // 'GCData' field
diff --git a/src/coreclr/inc/jithelpers.h b/src/coreclr/inc/jithelpers.h
index 264a1a8f29fc65..b696dd50f59970 100644
--- a/src/coreclr/inc/jithelpers.h
+++ b/src/coreclr/inc/jithelpers.h
@@ -115,6 +115,7 @@
     // Exceptions
     DYNAMICJITHELPER(CORINFO_HELP_THROW,        IL_Throw,           CORINFO_HELP_SIG_REG_ONLY)
     DYNAMICJITHELPER(CORINFO_HELP_RETHROW,      IL_Rethrow,         CORINFO_HELP_SIG_REG_ONLY)
+    DYNAMICJITHELPER(CORINFO_HELP_THROWEXACT,   IL_ThrowExact,      CORINFO_HELP_SIG_REG_ONLY)
     JITHELPER(CORINFO_HELP_USER_BREAKPOINT,     JIT_UserBreakpoint, CORINFO_HELP_SIG_REG_ONLY)
     JITHELPER(CORINFO_HELP_RNGCHKFAIL,          JIT_RngChkFail,     CORINFO_HELP_SIG_REG_ONLY)
     JITHELPER(CORINFO_HELP_OVERFLOW,            JIT_Overflow,       CORINFO_HELP_SIG_REG_ONLY)
diff --git a/src/coreclr/jit/async.cpp b/src/coreclr/jit/async.cpp
index ca19accfdbf30f..1f6812adfa50ab 100644
--- a/src/coreclr/jit/async.cpp
+++ b/src/coreclr/jit/async.cpp
@@ -379,6 +379,10 @@ void Async2Transformation::Transform(
     if (call->gtReturnType != TYP_VOID)
         gcRefsCount++;
 
+    unsigned exceptionGCDataIndex = UINT_MAX;
+    if (block->hasTryIndex())
+        exceptionGCDataIndex = gcRefsCount++;
+
     // For OSR, we store the patchpoint information at the beginning of the data
     // (null in the tier0 version):
     if (m_comp->doesMethodHavePatchpoints() || m_comp->opts.IsOSR())
@@ -457,8 +461,10 @@ void Async2Transformation::Transform(
         m_lastSuspensionBB = m_comp->fgLastBBInMainFunction();
     }
 
-    BasicBlock* retBB = m_comp->fgNewBBafter(BBJ_RETURN, m_lastSuspensionBB, true);
+    BasicBlock* retBB = m_comp->fgNewBBafter(BBJ_RETURN, m_lastSuspensionBB, false);
     retBB->bbSetRunRarely();
+    retBB->clearTryIndex();
+    retBB->clearHndIndex();
     m_lastSuspensionBB = retBB;
 
     block->SetJumpKindAndTarget(BBJ_COND, retBB DEBUGARG(m_comp));
@@ -491,8 +497,21 @@ void Async2Transformation::Transform(
     newContinuation       = m_comp->gtNewLclvNode(m_newContinuationVar, TYP_REF);
     unsigned stateOffset  = m_comp->info.compCompHnd->getFieldOffset(m_async2Info.continuationStateFldHnd);
     GenTree* stateNumNode = m_comp->gtNewIconNode((ssize_t)stateNum, TYP_I_IMPL);
-    GenTree* store        = StoreAtOffset(newContinuation, stateOffset, stateNumNode);
-    LIR::AsRange(retBB).InsertAtEnd(LIR::SeqTree(m_comp, store));
+    GenTree* storeState   = StoreAtOffset(newContinuation, stateOffset, stateNumNode);
+    LIR::AsRange(retBB).InsertAtEnd(LIR::SeqTree(m_comp, storeState));
+
+    // Fill in 'flags'
+    unsigned continuationFlags = 0;
+    if (call->gtReturnType != TYP_VOID)
+        continuationFlags |= CORINFO_CONTINUATION_RESULT_IN_GCDATA;
+    if (block->hasTryIndex())
+        continuationFlags |= CORINFO_CONTINUATION_NEEDS_EXCEPTION;
+
+    newContinuation      = m_comp->gtNewLclvNode(m_newContinuationVar, TYP_REF);
+    unsigned flagsOffset = m_comp->info.compCompHnd->getFieldOffset(m_async2Info.continuationFlagsFldHnd);
+    GenTree* flagsNode   = m_comp->gtNewIconNode((ssize_t)continuationFlags, TYP_INT);
+    GenTree* storeFlags  = StoreAtOffset(newContinuation, flagsOffset, flagsNode);
+    LIR::AsRange(retBB).InsertAtEnd(LIR::SeqTree(m_comp, storeFlags));
 
     // Fill in GC pointers
     if (gcRefsCount > 0)
@@ -664,6 +683,8 @@ void Async2Transformation::Transform(
 
     BasicBlock* resumeBB = m_comp->fgNewBBafter(BBJ_ALWAYS, prevResumptionBB, true, remainder);
     resumeBB->bbSetRunRarely();
+    resumeBB->clearTryIndex();
+    resumeBB->clearHndIndex();
     resumeBB->bbFlags |= BBF_ASYNC_RESUMPTION;
 
     m_comp->fgAddRefPred(remainder, resumeBB);
@@ -790,6 +811,45 @@ void Async2Transformation::Transform(
             }
         }
 
+        BasicBlock* storeResultBB = resumeBB;
+        if (exceptionGCDataIndex != UINT_MAX)
+        {
+            BasicBlock* rethrowExceptionBB = m_comp->fgNewBBinRegion(BBJ_THROW, block, /* jumpDest */ nullptr,
+                                                                     /* runRarely */ true, /* insertAtEnd */ true);
+            resumeBB->SetJumpKindAndTarget(BBJ_COND, rethrowExceptionBB DEBUGARG(m_comp));
+            m_comp->fgAddRefPred(rethrowExceptionBB, resumeBB);
+            m_comp->fgRemoveRefPred(remainder, resumeBB);
+
+            storeResultBB = m_comp->fgNewBBafter(BBJ_ALWAYS, resumeBB, true, remainder);
+            m_comp->fgAddRefPred(remainder, storeResultBB);
+            m_comp->fgAddRefPred(storeResultBB, resumeBB);
+
+            // Check if we have an exception.
+            unsigned exceptionLclNum = GetExceptionVar();
+            GenTree* objectArr       = m_comp->gtNewLclvNode(objectArrLclNum, TYP_REF);
+            unsigned exceptionOffset = OFFSETOF__CORINFO_Array__data + exceptionGCDataIndex * TARGET_POINTER_SIZE;
+            GenTree* exceptionInd    = LoadFromOffset(objectArr, exceptionOffset, TYP_REF);
+            GenTree* storeException  = m_comp->gtNewStoreLclVarNode(exceptionLclNum, exceptionInd);
+            LIR::AsRange(resumeBB).InsertAtEnd(LIR::SeqTree(m_comp, storeException));
+
+            GenTree* exception = m_comp->gtNewLclVarNode(exceptionLclNum, TYP_REF);
+            GenTree* null      = m_comp->gtNewNull();
+            GenTree* neNull    = m_comp->gtNewOperNode(GT_NE, TYP_INT, exception, null);
+            GenTree* jtrue     = m_comp->gtNewOperNode(GT_JTRUE, TYP_VOID, neNull);
+            LIR::AsRange(resumeBB).InsertAtEnd(exception, null, neNull, jtrue);
+
+            exception                     = m_comp->gtNewLclVarNode(exceptionLclNum, TYP_REF);
+            GenTreeCall* rethrowException = m_comp->gtNewHelperCallNode(CORINFO_HELP_THROWEXACT, TYP_VOID, exception);
+
+            m_comp->compCurBB = rethrowExceptionBB;
+            m_comp->fgMorphTree(rethrowException);
+
+            LIR::AsRange(rethrowExceptionBB).InsertAtEnd(LIR::SeqTree(m_comp, rethrowException));
+
+            storeResultBB->bbFlags |= BBF_ASYNC_RESUMPTION;
+            JITDUMP("Added " FMT_BB " to rethrow exception at suspension point\n", rethrowExceptionBB->bbNum);
+        }
+
         // Copy call return value.
         if (storeResultNode != nullptr)
         {
@@ -808,18 +868,19 @@ void Async2Transformation::Transform(
                     GenTree* boxDataAddr   = m_comp->gtNewOperNode(GT_ADD, TYP_BYREF, resultBox, boxDataOffset);
                     GenTree* boxData =
                         m_comp->gtNewBlkIndir(storeResultNode->GetLayout(m_comp), boxDataAddr, GTF_IND_NONFAULTING);
+                    GenTree* storeResult;
                     if (storeResultNode->OperIs(GT_STORE_LCL_VAR))
                     {
-                        store = m_comp->gtNewStoreLclVarNode(storeResultNode->GetLclNum(), boxData);
+                        storeResult = m_comp->gtNewStoreLclVarNode(storeResultNode->GetLclNum(), boxData);
                     }
                     else
                     {
-                        store = m_comp->gtNewStoreLclFldNode(storeResultNode->GetLclNum(), TYP_STRUCT,
-                                                             storeResultNode->GetLayout(m_comp),
-                                                             storeResultNode->GetLclOffs(), boxData);
+                        storeResult = m_comp->gtNewStoreLclFldNode(storeResultNode->GetLclNum(), TYP_STRUCT,
+                                                                   storeResultNode->GetLayout(m_comp),
+                                                                   storeResultNode->GetLclOffs(), boxData);
                     }
 
-                    LIR::AsRange(resumeBB).InsertAtEnd(LIR::SeqTree(m_comp, store));
+                    LIR::AsRange(resumeBB).InsertAtEnd(LIR::SeqTree(m_comp, storeResult));
                 }
                 else
                 {
@@ -862,17 +923,18 @@ void Async2Transformation::Transform(
                     value = LoadFromOffset(resultBox, TARGET_POINTER_SIZE, call->gtReturnType);
                 }
 
+                GenTree* storeResult;
                 if (storeResultNode->OperIs(GT_STORE_LCL_VAR))
                 {
-                    store = m_comp->gtNewStoreLclVarNode(storeResultNode->GetLclNum(), value);
+                    storeResult = m_comp->gtNewStoreLclVarNode(storeResultNode->GetLclNum(), value);
                 }
                 else
                 {
-                    store = m_comp->gtNewStoreLclFldNode(storeResultNode->GetLclNum(), storeResultNode->TypeGet(),
-                                                         storeResultNode->GetLclOffs(), value);
+                    storeResult = m_comp->gtNewStoreLclFldNode(storeResultNode->GetLclNum(), storeResultNode->TypeGet(),
+                                                               storeResultNode->GetLclOffs(), value);
                 }
 
-                LIR::AsRange(resumeBB).InsertAtEnd(LIR::SeqTree(m_comp, store));
+                LIR::AsRange(resumeBB).InsertAtEnd(LIR::SeqTree(m_comp, storeResult));
             }
         }
     }
@@ -927,13 +989,24 @@ unsigned Async2Transformation::GetResultBoxVar()
 {
     if (m_resultBoxVar == BAD_VAR_NUM)
     {
-        m_resultBoxVar                             = m_comp->lvaGrabTemp(false DEBUGARG("object for result box"));
+        m_resultBoxVar = m_comp->lvaGrabTemp(false DEBUGARG("object for resuming result box"));
         m_comp->lvaGetDesc(m_resultBoxVar)->lvType = TYP_REF;
     }
 
     return m_resultBoxVar;
 }
 
+unsigned Async2Transformation::GetExceptionVar()
+{
+    if (m_exceptionVar == BAD_VAR_NUM)
+    {
+        m_exceptionVar = m_comp->lvaGrabTemp(false DEBUGARG("object for resuming exception"));
+        m_comp->lvaGetDesc(m_exceptionVar)->lvType = TYP_REF;
+    }
+
+    return m_exceptionVar;
+}
+
 GenTree* Async2Transformation::CreateResumptionStubAddrTree()
 {
     switch (m_resumeStubLookup.accessType)
@@ -1108,7 +1181,10 @@ void Async2Transformation::CreateResumptionSwitch()
     if (m_comp->doesMethodHavePatchpoints())
     {
         // If we have patchpoints then first check if we need to resume in the OSR version.
-        BasicBlock* callHelperBB = m_comp->fgNewBBafter(BBJ_THROW, m_comp->fgLastBBInMainFunction(), true);
+        BasicBlock* callHelperBB = m_comp->fgNewBBafter(BBJ_THROW, m_comp->fgLastBBInMainFunction(), false);
+        callHelperBB->bbSetRunRarely();
+        callHelperBB->clearTryIndex();
+        callHelperBB->clearHndIndex();
 
         BasicBlock* checkILOffsetBB = m_comp->fgNewBBbefore(BBJ_COND, newEntryBB->GetJumpDest(), true, callHelperBB);
         m_comp->fgRemoveRefPred(newEntryBB->GetJumpDest(), newEntryBB);
diff --git a/src/coreclr/jit/async.h b/src/coreclr/jit/async.h
index 6a584fd944b1cb..a98551646e2ffb 100644
--- a/src/coreclr/jit/async.h
+++ b/src/coreclr/jit/async.h
@@ -29,6 +29,7 @@ class Async2Transformation
     unsigned                      m_dataArrayVar            = BAD_VAR_NUM;
     unsigned                      m_gcDataArrayVar          = BAD_VAR_NUM;
     unsigned                      m_resultBoxVar            = BAD_VAR_NUM;
+    unsigned                      m_exceptionVar            = BAD_VAR_NUM;
     BasicBlock*                   m_lastSuspensionBB        = nullptr;
 
     void LiftLIREdges(BasicBlock*                    block,
@@ -48,6 +49,7 @@ class Async2Transformation
     unsigned GetDataArrayVar();
     unsigned GetGCDataArrayVar();
     unsigned GetResultBoxVar();
+    unsigned GetExceptionVar();
 
     GenTree* CreateResumptionStubAddrTree();
     GenTree* CreateFunctionTargetAddr(CORINFO_METHOD_HANDLE methHnd, const CORINFO_CONST_LOOKUP& lookup);
diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoHelpFunc.cs b/src/coreclr/tools/Common/JitInterface/CorInfoHelpFunc.cs
index 74b0f72798d6de..8aa9f2f6ceaf29 100644
--- a/src/coreclr/tools/Common/JitInterface/CorInfoHelpFunc.cs
+++ b/src/coreclr/tools/Common/JitInterface/CorInfoHelpFunc.cs
@@ -97,6 +97,7 @@ which is the right helper to use to allocate an object of a given type. */
 
         CORINFO_HELP_THROW,             // Throw an exception object
         CORINFO_HELP_RETHROW,           // Rethrow the currently active exception
+        CORINFO_HELP_THROWEXACT,        // Throw an exception object, preserving stack trace
         CORINFO_HELP_USER_BREAKPOINT,   // For a user program to break to the debugger
         CORINFO_HELP_RNGCHKFAIL,        // array bounds check failed
         CORINFO_HELP_OVERFLOW,          // throw an overflow exception
diff --git a/src/coreclr/tools/superpmi/superpmi-shared/agnostic.h b/src/coreclr/tools/superpmi/superpmi-shared/agnostic.h
index 6b3c79a9108934..a2a5f94af643bc 100644
--- a/src/coreclr/tools/superpmi/superpmi-shared/agnostic.h
+++ b/src/coreclr/tools/superpmi/superpmi-shared/agnostic.h
@@ -198,6 +198,7 @@ struct Agnostic_CORINFO_ASYNC2_INFO
     DWORDLONG continuationNextFldHnd;
     DWORDLONG continuationResumeFldHnd;
     DWORDLONG continuationStateFldHnd;
+    DWORDLONG continuationFlagsFldHnd;
     DWORDLONG continuationDataFldHnd;
     DWORDLONG continuationGCDataFldHnd;
 };
diff --git a/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.cpp b/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.cpp
index 2067399f4d9425..d7a260f9e507f9 100644
--- a/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.cpp
+++ b/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.cpp
@@ -4329,6 +4329,7 @@ void MethodContext::recGetAsync2Info(const CORINFO_ASYNC2_INFO* pAsync2Info)
     value.continuationNextFldHnd = CastHandle(pAsync2Info->continuationNextFldHnd);
     value.continuationResumeFldHnd = CastHandle(pAsync2Info->continuationResumeFldHnd);
     value.continuationStateFldHnd = CastHandle(pAsync2Info->continuationStateFldHnd);
+    value.continuationFlagsFldHnd = CastHandle(pAsync2Info->continuationFlagsFldHnd);
     value.continuationDataFldHnd = CastHandle(pAsync2Info->continuationDataFldHnd);
     value.continuationGCDataFldHnd = CastHandle(pAsync2Info->continuationGCDataFldHnd);
 
@@ -4338,9 +4339,10 @@ void MethodContext::recGetAsync2Info(const CORINFO_ASYNC2_INFO* pAsync2Info)
 void MethodContext::dmpGetAsync2Info(DWORD key, const Agnostic_CORINFO_ASYNC2_INFO& value)
 {
     printf("GetAsync2Info key %u value contClsHnd-%016" PRIX64 " contNextFldHnd-%016" PRIX64 " contResumeFldHnd-%016" PRIX64
-           " contStateFldHnd-%016" PRIX64 " contDataFldHnd-%016" PRIX64 " contGCDataFldHnd-%016" PRIX64,
+           " contStateFldHnd-%016" PRIX64 " contFlagsFldHnd-%016" PRIX64 " contDataFldHnd-%016" PRIX64 " contGCDataFldHnd-%016" PRIX64,
         key, value.continuationClsHnd, value.continuationNextFldHnd, value.continuationResumeFldHnd,
-        value.continuationStateFldHnd, value.continuationDataFldHnd, value.continuationGCDataFldHnd);
+        value.continuationStateFldHnd, value.continuationFlagsFldHnd, value.continuationDataFldHnd,
+        value.continuationGCDataFldHnd);
 }
 void MethodContext::repGetAsync2Info(CORINFO_ASYNC2_INFO* pAsync2InfoOut)
 {
@@ -4349,6 +4351,7 @@ void MethodContext::repGetAsync2Info(CORINFO_ASYNC2_INFO* pAsync2InfoOut)
     pAsync2InfoOut->continuationNextFldHnd = (CORINFO_FIELD_HANDLE)value.continuationNextFldHnd;
     pAsync2InfoOut->continuationResumeFldHnd = (CORINFO_FIELD_HANDLE)value.continuationResumeFldHnd;
     pAsync2InfoOut->continuationStateFldHnd = (CORINFO_FIELD_HANDLE)value.continuationStateFldHnd;
+    pAsync2InfoOut->continuationFlagsFldHnd = (CORINFO_FIELD_HANDLE)value.continuationFlagsFldHnd;
     pAsync2InfoOut->continuationDataFldHnd = (CORINFO_FIELD_HANDLE)value.continuationDataFldHnd;
     pAsync2InfoOut->continuationGCDataFldHnd = (CORINFO_FIELD_HANDLE)value.continuationGCDataFldHnd;
     DEBUG_REP(dmpGetAsync2Info(0, value));
diff --git a/src/coreclr/vm/corelib.h b/src/coreclr/vm/corelib.h
index 3bda6114bc2945..693991bf97b5dc 100644
--- a/src/coreclr/vm/corelib.h
+++ b/src/coreclr/vm/corelib.h
@@ -803,6 +803,7 @@ DEFINE_CLASS(CONTINUATION,              CompilerServices,   Continuation)
 DEFINE_FIELD(CONTINUATION,              NEXT,               Next)
 DEFINE_FIELD(CONTINUATION,              RESUME,             Resume)
 DEFINE_FIELD(CONTINUATION,              STATE,              State)
+DEFINE_FIELD(CONTINUATION,              FLAGS,              Flags)
 DEFINE_FIELD(CONTINUATION,              DATA,               Data)
 DEFINE_FIELD(CONTINUATION,              GCDATA,             GCData)
 
diff --git a/src/coreclr/vm/jithelpers.cpp b/src/coreclr/vm/jithelpers.cpp
index aed56aaee72d7b..e55da1c5c633a0 100644
--- a/src/coreclr/vm/jithelpers.cpp
+++ b/src/coreclr/vm/jithelpers.cpp
@@ -4367,6 +4367,40 @@ HCIMPL0(void, IL_Rethrow)
 }
 HCIMPLEND
 
+HCIMPL1(void, IL_ThrowExact, Object* obj)
+{
+    FCALL_CONTRACT;
+
+    /* Make no assumptions about the current machine state */
+    ResetCurrentContext();
+
+    FC_GC_POLL_NOT_NEEDED();    // throws always open up for GC
+
+    HELPER_METHOD_FRAME_BEGIN_ATTRIB_NOPOLL(Frame::FRAME_ATTR_EXCEPTION);    // Set up a frame
+
+    OBJECTREF oref = ObjectToOBJECTREF(obj);
+
+#if defined(_DEBUG) && defined(TARGET_X86)
+    __helperframe.InsureInit(false, NULL);
+    g_ExceptionEIP = (LPVOID)__helperframe.GetReturnAddress();
+#endif // defined(_DEBUG) && defined(TARGET_X86)
+
+    GetThread()->GetExceptionState()->SetRaisingForeignException();
+
+#ifdef FEATURE_EH_FUNCLETS
+    if (g_isNewExceptionHandlingEnabled)
+    {
+        DispatchManagedException(oref);
+        UNREACHABLE();
+    }
+#endif
+
+    RaiseTheExceptionInternalOnly(oref, FALSE);
+
+    HELPER_METHOD_FRAME_END();
+}
+HCIMPLEND
+
 /*********************************************************************/
 HCIMPL0(void, JIT_RngChkFail)
 {
diff --git a/src/coreclr/vm/jitinterface.cpp b/src/coreclr/vm/jitinterface.cpp
index 92e03ce2db2368..8137b64c5e1cfe 100644
--- a/src/coreclr/vm/jitinterface.cpp
+++ b/src/coreclr/vm/jitinterface.cpp
@@ -10053,6 +10053,7 @@ void CEEInfo::getAsync2Info(CORINFO_ASYNC2_INFO* pAsync2InfoOut)
     pAsync2InfoOut->continuationNextFldHnd = CORINFO_FIELD_HANDLE(CoreLibBinder::GetField(FIELD__CONTINUATION__NEXT));
     pAsync2InfoOut->continuationResumeFldHnd = CORINFO_FIELD_HANDLE(CoreLibBinder::GetField(FIELD__CONTINUATION__RESUME));
     pAsync2InfoOut->continuationStateFldHnd = CORINFO_FIELD_HANDLE(CoreLibBinder::GetField(FIELD__CONTINUATION__STATE));
+    pAsync2InfoOut->continuationFlagsFldHnd = CORINFO_FIELD_HANDLE(CoreLibBinder::GetField(FIELD__CONTINUATION__FLAGS));
     pAsync2InfoOut->continuationDataFldHnd = CORINFO_FIELD_HANDLE(CoreLibBinder::GetField(FIELD__CONTINUATION__DATA));
     pAsync2InfoOut->continuationGCDataFldHnd = CORINFO_FIELD_HANDLE(CoreLibBinder::GetField(FIELD__CONTINUATION__GCDATA));
 }
@@ -14215,10 +14216,9 @@ static Signature BuildResumptionStubSignature(LoaderAllocator* alloc)
 {
     SigBuilder sigBuilder;
     sigBuilder.AppendByte(IMAGE_CEE_CS_CALLCONV_DEFAULT);
-    sigBuilder.AppendData(2); // 2 arguments
+    sigBuilder.AppendData(1); // 1 argument
     sigBuilder.AppendElementType(ELEMENT_TYPE_OBJECT); // return type
     sigBuilder.AppendElementType(ELEMENT_TYPE_OBJECT); // continuation
-    sigBuilder.AppendElementType(ELEMENT_TYPE_OBJECT); // exception
 
     return AllocateSignature(alloc, sigBuilder);
 }
diff --git a/src/tests/Loader/async/simple-eh.cs b/src/tests/Loader/async/simple-eh.cs
new file mode 100644
index 00000000000000..49c1312f9b3458
--- /dev/null
+++ b/src/tests/Loader/async/simple-eh.cs
@@ -0,0 +1,52 @@
+// 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 System.Collections.Generic;
+using System.Diagnostics;
+using System.Linq;
+using System.Runtime;
+using System.Runtime.CompilerServices;
+using System.Threading.Tasks;
+using Xunit;
+
+public class Async2SimpleEH
+{
+    [Fact]
+    public static void Test()
+    {
+        Task.Run(AsyncEntry).Wait();
+    }
+
+    public static async Task AsyncEntry()
+    {
+        int result = await Handler();
+        Assert.Equal(42, result);
+    }
+
+    public static async2 int Handler()
+    {
+        try
+        {
+            return await Throw(42);
+        }
+        catch (IntegerException ex)
+        {
+            System.Console.WriteLine(ex);
+            return ex.Value;
+        }
+    }
+
+    [MethodImpl(MethodImplOptions.NoInlining)]
+    public static async2 int Throw(int value)
+    {
+        await Task.Yield();
+        throw new IntegerException(value);
+    }
+
+    public class IntegerException : Exception
+    {
+        public int Value;
+        public IntegerException(int value) => Value = value;
+    }
+}
diff --git a/src/tests/Loader/async/simple-eh.csproj b/src/tests/Loader/async/simple-eh.csproj
new file mode 100644
index 00000000000000..501217e4d86892
--- /dev/null
+++ b/src/tests/Loader/async/simple-eh.csproj
@@ -0,0 +1,9 @@
+<Project Sdk="Microsoft.NET.Sdk">
+  <PropertyGroup>
+    <DebugType>None</DebugType>
+    <Optimize>True</Optimize>
+  </PropertyGroup>
+  <ItemGroup>
+    <Compile Include="$(MSBuildProjectName).cs" />
+  </ItemGroup>
+</Project>

From 92f852f83b014a5cfc47fbecb7d917777d0b2ad3 Mon Sep 17 00:00:00 2001
From: Jakob Botsch Nielsen <janielsen@microsoft.com>
Date: Wed, 1 Nov 2023 20:05:12 +0000
Subject: [PATCH 059/203] Merged PR 35015: Fix type of
 RuntimeAsyncMaintainedData

---
 .../src/System/Threading/Tasks/Future.cs                      | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/Future.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/Future.cs
index ba716e869ec352..08ca31e73db47a 100644
--- a/src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/Future.cs
+++ b/src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/Future.cs
@@ -31,7 +31,7 @@ internal struct RuntimeTaskState<TResult>
 
         public void Push()
         {
-            dataFrame = new RuntimeHelpers.AsyncDataFrame(()=> new RuntimeHelpers.RuntimeAsyncMaintainedData());
+            dataFrame = new RuntimeHelpers.AsyncDataFrame(() => new RuntimeHelpers.RuntimeAsyncMaintainedData<TResult>());
             RuntimeHelpers.PushAsyncData(ref dataFrame);
             _thread = Thread.CurrentThread;
             _previousExecutionCtx = _thread._executionContext;
@@ -123,7 +123,7 @@ internal struct RuntimeValueTaskState<TResult>
 
         public void Push()
         {
-            dataFrame = new RuntimeHelpers.AsyncDataFrame(()=> new RuntimeHelpers.RuntimeAsyncMaintainedData());
+            dataFrame = new RuntimeHelpers.AsyncDataFrame(()=> new RuntimeHelpers.RuntimeAsyncMaintainedData<TResult>());
             RuntimeHelpers.PushAsyncData(ref dataFrame);
             _thread = Thread.CurrentThread;
             _previousExecutionCtx = _thread._executionContext;

From 2fc5a2a01de353086ffb414f6a021506664b2056 Mon Sep 17 00:00:00 2001
From: Vladimir Sadov <vsadov@microsoft.com>
Date: Thu, 2 Nov 2023 02:19:38 +0000
Subject: [PATCH 060/203] Merged PR 35022: test for generics and a roslyn
 update to compile it

test for generics and a roslyn update to compile it
---
 buildroslynnugets.cmd                         |  4 +--
 eng/Versions.props                            |  6 ++--
 src/tests/Loader/async/async2sharedgeneric.cs | 30 +++++++++++++++++++
 .../Loader/async/async2sharedgeneric.csproj   |  8 +++++
 4 files changed, 43 insertions(+), 5 deletions(-)
 create mode 100644 src/tests/Loader/async/async2sharedgeneric.cs
 create mode 100644 src/tests/Loader/async/async2sharedgeneric.csproj

diff --git a/buildroslynnugets.cmd b/buildroslynnugets.cmd
index 5e3e212060ec8f..38f6c7d94e0c98 100644
--- a/buildroslynnugets.cmd
+++ b/buildroslynnugets.cmd
@@ -1,14 +1,14 @@
 setlocal ENABLEEXTENSIONS
 pushd %~dp0
 
-set ASYNC_SUFFIX=async-4
+set ASYNC_SUFFIX=async-5
 
 cd ..
 pushd dotnet-roslyn
 
 git fetch AzDo dev/vsadov/a2
 rem when updating this, make sure to update the ASYNC_SUFFIX above and the versions.props file
-git checkout 05269ce679fef115617a579ce29edd2c5e45b3e6
+git checkout 1d2dccc8ad7d4169112c2f93796a418a1cd9d930
 
 call restore.cmd
 call build.cmd -c release
diff --git a/eng/Versions.props b/eng/Versions.props
index 9c729b435ad073..6f8d5b7ff7aab1 100644
--- a/eng/Versions.props
+++ b/eng/Versions.props
@@ -39,9 +39,9 @@
       Any tools that contribute to the design-time experience should use the MicrosoftCodeAnalysisVersion_LatestVS property above to ensure
       they do not break the local dev experience.
     -->
-    <MicrosoftCodeAnalysisCSharpVersion>4.9.0-async-4</MicrosoftCodeAnalysisCSharpVersion>
-    <MicrosoftCodeAnalysisVersion>4.9.0-async-4</MicrosoftCodeAnalysisVersion>
-    <MicrosoftNetCompilersToolsetVersion>4.9.0-async-4</MicrosoftNetCompilersToolsetVersion>
+    <MicrosoftCodeAnalysisCSharpVersion>4.9.0-async-5</MicrosoftCodeAnalysisCSharpVersion>
+    <MicrosoftCodeAnalysisVersion>4.9.0-async-5</MicrosoftCodeAnalysisVersion>
+    <MicrosoftNetCompilersToolsetVersion>4.9.0-async-5</MicrosoftNetCompilersToolsetVersion>
   </PropertyGroup>
   <!--
     For source generator support we need to target multiple versions of Roslyn in order to be able to run on older versions of Roslyn.
diff --git a/src/tests/Loader/async/async2sharedgeneric.cs b/src/tests/Loader/async/async2sharedgeneric.cs
new file mode 100644
index 00000000000000..42aa7ea0f0e24f
--- /dev/null
+++ b/src/tests/Loader/async/async2sharedgeneric.cs
@@ -0,0 +1,30 @@
+// 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 System.Runtime.CompilerServices;
+using System.Threading.Tasks;
+using Xunit;
+public class Async2SharedGeneric
+{
+    static Type s_type;
+    [Fact]
+    public static void TestEntryPoint()
+    {
+        int[] arr = new int[1];
+        AsyncTestEntryPoint<int>().Wait();
+        Assert.Equal(typeof(int), s_type);
+        AsyncTestEntryPoint<string>().Wait();
+        Assert.Equal(typeof(string), s_type);
+        AsyncTestEntryPoint<object>().Wait();
+        Assert.Equal(typeof(object), s_type);
+    }
+    private static async Task AsyncTestEntryPoint<T>()
+    {
+        await Async2TestEntryPoint<T>();
+    }
+    [MethodImpl(MethodImplOptions.NoInlining)]
+    private static async2 void Async2TestEntryPoint<T>()
+    {
+        s_type = typeof(T);
+    }
+}
diff --git a/src/tests/Loader/async/async2sharedgeneric.csproj b/src/tests/Loader/async/async2sharedgeneric.csproj
new file mode 100644
index 00000000000000..0f29ed860df43e
--- /dev/null
+++ b/src/tests/Loader/async/async2sharedgeneric.csproj
@@ -0,0 +1,8 @@
+<Project Sdk="Microsoft.NET.Sdk.IL">
+  <PropertyGroup>
+    <RequiresProcessIsolation>true</RequiresProcessIsolation>
+  </PropertyGroup>
+  <ItemGroup>
+   <Compile Include="$(MSBuildProjectName).cs" />
+  </ItemGroup>
+</Project>

From 851c7c36fe0745b2ea041f9c4b0fe5519e9e715d Mon Sep 17 00:00:00 2001
From: Jakob Botsch Nielsen <janielsen@microsoft.com>
Date: Thu, 2 Nov 2023 09:15:18 +0000
Subject: [PATCH 061/203] Merged PR 35000: Handle retbufs properly in JIT SM
 prototype

I realized the support for this was never added.
---
 src/coreclr/jit/async.cpp                   | 66 ++++++++++++++++-----
 src/coreclr/jit/importer.cpp                | 56 ++++++++++++++++-
 src/tests/Loader/async/async2-retbuf.cs     | 54 +++++++++++++++++
 src/tests/Loader/async/async2-retbuf.csproj |  8 +++
 4 files changed, 167 insertions(+), 17 deletions(-)
 create mode 100644 src/tests/Loader/async/async2-retbuf.cs
 create mode 100644 src/tests/Loader/async/async2-retbuf.csproj

diff --git a/src/coreclr/jit/async.cpp b/src/coreclr/jit/async.cpp
index 1f6812adfa50ab..4d68dca74b86c4 100644
--- a/src/coreclr/jit/async.cpp
+++ b/src/coreclr/jit/async.cpp
@@ -154,11 +154,11 @@ class AsyncLiveness
         return !dsc->lvTracked || VarSetOps::IsMember(m_comp, m_comp->compCurLife, dsc->lvVarIndex);
     }
 
-    void GetLiveLocals(jitstd::vector<Async2Transformation::LiveLocalInfo>& liveLocals)
+    void GetLiveLocals(jitstd::vector<Async2Transformation::LiveLocalInfo>& liveLocals, unsigned fullyDefinedRetBufLcl)
     {
         for (unsigned lclNum = 0; lclNum < m_numVars; lclNum++)
         {
-            if (IsLive(lclNum))
+            if ((lclNum != fullyDefinedRetBufLcl) && IsLive(lclNum))
             {
                 liveLocals.push_back(Async2Transformation::LiveLocalInfo(lclNum));
             }
@@ -291,7 +291,27 @@ void Async2Transformation::Transform(
 
     m_liveLocals.clear();
 
-    life.GetLiveLocals(m_liveLocals);
+    unsigned fullyDefinedRetBufLcl = BAD_VAR_NUM;
+    CallArg* retbufArg             = call->gtArgs.GetRetBufferArg();
+    if (retbufArg != nullptr)
+    {
+        GenTree* retbuf = retbufArg->GetNode();
+        if (retbuf->IsLclVarAddr())
+        {
+            LclVarDsc*   dsc       = m_comp->lvaGetDesc(retbuf->AsLclVarCommon());
+            ClassLayout* defLayout = m_comp->typGetObjLayout(call->gtRetClsHnd);
+            if (defLayout->GetSize() == dsc->lvExactSize())
+            {
+                // This call fully defines this retbuf. There is no need to
+                // consider it live across the call since it is going to be
+                // overridden anyway.
+                fullyDefinedRetBufLcl = retbuf->AsLclVarCommon()->GetLclNum();
+                JITDUMP("  V%02u is a fully defined retbuf and will not be considered live\n", fullyDefinedRetBufLcl);
+            }
+        }
+    }
+
+    life.GetLiveLocals(m_liveLocals, fullyDefinedRetBufLcl);
     LiftLIREdges(block, call, defs, m_liveLocals);
 
 #ifdef DEBUG
@@ -419,6 +439,7 @@ void Async2Transformation::Transform(
 
     if (!call->TypeIs(TYP_VOID) && !call->IsUnusedValue())
     {
+        assert(retbufArg == nullptr);
         assert(call->gtNext != nullptr);
         if (!call->gtNext->OperIsLocalStore() || (call->gtNext->Data() != call))
         {
@@ -439,6 +460,20 @@ void Async2Transformation::Transform(
         insertAfter     = call->gtNext;
     }
 
+    if (retbufArg != nullptr)
+    {
+        assert(call->TypeIs(TYP_VOID));
+
+        // For async2 methods we always expect retbufs to point to locals. We
+        // ensure this in impStoreStruct. TODO-CQ: We can handle common "direct
+        // assignment" cases, e.g. obj.StructVal = Call(), by seeing if there
+        // is a base TYP_REF and keeping that live. This would avoid
+        // introducing copies in the importer on the synchronous path.
+        noway_assert(retbufArg->GetNode()->OperIs(GT_LCL_ADDR));
+
+        storeResultNode = retbufArg->GetNode()->AsLclVarCommon();
+    }
+
     GenTree* continuationArg = new (m_comp, GT_ASYNC_CONTINUATION) GenTree(GT_ASYNC_CONTINUATION, TYP_REF);
     continuationArg->SetHasOrderingSideEffect();
 
@@ -857,33 +892,36 @@ void Async2Transformation::Transform(
             GenTree* resultBox = LoadFromOffset(objectArr, OFFSETOF__CORINFO_Array__data, TYP_REF);
 
             LclVarDsc* resultLcl = m_comp->lvaGetDesc(storeResultNode);
-            assert((resultLcl->TypeGet() == TYP_STRUCT) == storeResultNode->TypeIs(TYP_STRUCT));
+            assert((retbufArg != nullptr) ||
+                   (resultLcl->TypeGet() == TYP_STRUCT) == storeResultNode->TypeIs(TYP_STRUCT));
 
             // TODO-TP: We can use liveness to avoid generating a lot of this IR.
-            if (storeResultNode->TypeIs(TYP_STRUCT))
+            if ((retbufArg != nullptr) || storeResultNode->TypeIs(TYP_STRUCT))
             {
                 if (m_comp->lvaGetPromotionType(resultLcl) != Compiler::PROMOTION_TYPE_INDEPENDENT)
                 {
-                    GenTree* boxDataOffset = m_comp->gtNewIconNode(TARGET_POINTER_SIZE, TYP_I_IMPL);
-                    GenTree* boxDataAddr   = m_comp->gtNewOperNode(GT_ADD, TYP_BYREF, resultBox, boxDataOffset);
-                    GenTree* boxData =
-                        m_comp->gtNewBlkIndir(storeResultNode->GetLayout(m_comp), boxDataAddr, GTF_IND_NONFAULTING);
-                    GenTree* storeResult;
-                    if (storeResultNode->OperIs(GT_STORE_LCL_VAR))
+                    GenTree*     boxDataOffset = m_comp->gtNewIconNode(TARGET_POINTER_SIZE, TYP_I_IMPL);
+                    GenTree*     boxDataAddr   = m_comp->gtNewOperNode(GT_ADD, TYP_BYREF, resultBox, boxDataOffset);
+                    ClassLayout* returnLayout  = m_comp->typGetObjLayout(call->gtRetClsHnd);
+                    GenTree*     boxData       = m_comp->gtNewBlkIndir(returnLayout, boxDataAddr, GTF_IND_NONFAULTING);
+                    GenTree*     storeResult;
+                    if ((storeResultNode->GetLclOffs() == 0) && (returnLayout->GetSize() == resultLcl->lvExactSize()))
                     {
                         storeResult = m_comp->gtNewStoreLclVarNode(storeResultNode->GetLclNum(), boxData);
                     }
                     else
                     {
-                        storeResult = m_comp->gtNewStoreLclFldNode(storeResultNode->GetLclNum(), TYP_STRUCT,
-                                                                   storeResultNode->GetLayout(m_comp),
-                                                                   storeResultNode->GetLclOffs(), boxData);
+                        storeResult =
+                            m_comp->gtNewStoreLclFldNode(storeResultNode->GetLclNum(), TYP_STRUCT, returnLayout,
+                                                         storeResultNode->GetLclOffs(), boxData);
                     }
 
                     LIR::AsRange(resumeBB).InsertAtEnd(LIR::SeqTree(m_comp, storeResult));
                 }
                 else
                 {
+                    assert(retbufArg == nullptr); // Locals defined through retbufs are never independently promoted.
+
                     if (resultLcl->lvFieldCnt > 1)
                     {
                         unsigned resultBoxVar   = GetResultBoxVar();
diff --git a/src/coreclr/jit/importer.cpp b/src/coreclr/jit/importer.cpp
index 28a826da7761a2..12763ec0358791 100644
--- a/src/coreclr/jit/importer.cpp
+++ b/src/coreclr/jit/importer.cpp
@@ -810,7 +810,20 @@ GenTree* Compiler::impStoreStruct(GenTree*         store,
             // TODO-Bug?: verify if flags matter here
             GenTreeFlags indirFlags = GTF_EMPTY;
             GenTree*     destAddr   = impGetNodeAddr(store, CHECK_SPILL_ALL, &indirFlags);
-            NewCallArg   newArg     = NewCallArg::Primitive(destAddr).WellKnown(wellKnownArgType);
+
+            unsigned retbufSpillLcl = BAD_VAR_NUM;
+            if (!destAddr->OperIs(GT_LCL_ADDR))
+            {
+                retbufSpillLcl = lvaGrabTemp(true DEBUGARG("retbuf for async2 call stored to non-lcl"));
+                lvaGetDesc(retbufSpillLcl)->lvType = store->TypeGet();
+                if (store->TypeIs(TYP_STRUCT))
+                {
+                    lvaSetStruct(retbufSpillLcl, store->GetLayout(this), false);
+                }
+                destAddr = gtNewLclVarAddrNode(retbufSpillLcl, TYP_BYREF);
+            }
+
+            NewCallArg newArg = NewCallArg::Primitive(destAddr).WellKnown(wellKnownArgType);
 
 #if !defined(TARGET_ARM)
             // Unmanaged instance methods on Windows or Unix X86 need the retbuf arg after the first (this) parameter
@@ -891,8 +904,21 @@ GenTree* Compiler::impStoreStruct(GenTree*         store,
             // now returns void, not a struct
             src->gtType = TYP_VOID;
 
-            // return the morphed call node
-            return src;
+            if (retbufSpillLcl != BAD_VAR_NUM)
+            {
+                assert(pAfterStmt == nullptr);
+                impAppendTree(src, curLevel, usedDI);
+
+                store->Data() = gtNewLclVarNode(retbufSpillLcl, genActualType(store));
+                impAppendTree(store, curLevel, usedDI);
+
+                return gtNewNothingNode();
+            }
+            else
+            {
+                // return the morphed call node
+                return src;
+            }
         }
 
 #ifdef UNIX_AMD64_ABI
@@ -914,6 +940,19 @@ GenTree* Compiler::impStoreStruct(GenTree*         store,
             // TODO-Bug?: verify if flags matter here
             GenTreeFlags indirFlags = GTF_EMPTY;
             GenTree*     destAddr   = impGetNodeAddr(store, CHECK_SPILL_ALL, &indirFlags);
+
+            unsigned retbufSpillLcl = BAD_VAR_NUM;
+            if (!destAddr->OperIs(GT_LCL_ADDR))
+            {
+                retbufSpillLcl = lvaGrabTemp(true DEBUGARG("retbuf for async2 call stored to non-lcl"));
+                lvaGetDesc(retbufSpillLcl)->lvType = store->TypeGet();
+                if (store->TypeIs(TYP_STRUCT))
+                {
+                    lvaSetStruct(retbufSpillLcl, store->GetLayout(this), false);
+                }
+                destAddr = gtNewLclVarAddrNode(retbufSpillLcl, TYP_BYREF);
+            }
+
             call->gtArgs.InsertAfterThisOrFirst(this,
                                                 NewCallArg::Primitive(destAddr).WellKnown(WellKnownArg::RetBuffer));
 
@@ -921,6 +960,17 @@ GenTree* Compiler::impStoreStruct(GenTree*         store,
             src->gtType  = TYP_VOID;
             call->gtType = TYP_VOID;
 
+            if (retbufSpillLcl != BAD_VAR_NUM)
+            {
+                assert(pAfterStmt == nullptr);
+                impAppendTree(src, curLevel, usedDI);
+
+                store->Data() = gtNewLclVarNode(retbufSpillLcl, genActualType(store));
+                impAppendTree(store, curLevel, usedDI);
+
+                return gtNewNothingNode();
+            }
+
             // We already have appended the write to 'dest' GT_CALL's args
             // So now we just return an empty node (pruning the GT_RET_EXPR)
             return src;
diff --git a/src/tests/Loader/async/async2-retbuf.cs b/src/tests/Loader/async/async2-retbuf.cs
new file mode 100644
index 00000000000000..a8976504416fe8
--- /dev/null
+++ b/src/tests/Loader/async/async2-retbuf.cs
@@ -0,0 +1,54 @@
+// 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 System.Runtime.CompilerServices;
+using System.Threading.Tasks;
+using Xunit;
+
+public class Async2Retbuf
+{
+    [Fact]
+    public static void TestEntryPoint()
+    {
+        Retbuf(new C()).Wait();
+    }
+
+    [MethodImpl(MethodImplOptions.NoInlining)]
+    private static async2 void Retbuf(C c)
+    {
+        for (int i = 0; i < 20000; i++)
+        {
+            S val = await ReturnsStruct();
+
+            AssertEqual(42, val.A);
+            AssertEqual(4242, val.B);
+            AssertEqual(424242, val.C);
+            AssertEqual(42424242, val.D);
+
+            c.Val = default;
+            c.Val = await ReturnsStruct();
+
+            AssertEqual(42, c.Val.A);
+            AssertEqual(4242, c.Val.B);
+            AssertEqual(424242, c.Val.C);
+            AssertEqual(42424242, c.Val.D);
+        }
+    }
+
+    [MethodImpl(MethodImplOptions.NoInlining)]
+    private static void AssertEqual(long expected, long actual)
+    {
+        Assert.Equal(expected, actual);
+    }
+
+    private static async2 S ReturnsStruct()
+    {
+        await Task.Yield();
+        return new S { A = 42, B = 4242, C = 424242, D = 42424242 };
+    }
+
+    private struct S { public long A, B, C, D; }
+
+    private class C { public S Val; }
+}
diff --git a/src/tests/Loader/async/async2-retbuf.csproj b/src/tests/Loader/async/async2-retbuf.csproj
new file mode 100644
index 00000000000000..9367a79b2edbb1
--- /dev/null
+++ b/src/tests/Loader/async/async2-retbuf.csproj
@@ -0,0 +1,8 @@
+<Project Sdk="Microsoft.NET.Sdk.IL">
+  <PropertyGroup>
+    <Optimize>True</Optimize>
+  </PropertyGroup>
+  <ItemGroup>
+    <Compile Include="$(MSBuildProjectName).cs" />
+  </ItemGroup>
+</Project>

From 335e30275d3866ec4b2e0fca0f07f85b855341d8 Mon Sep 17 00:00:00 2001
From: Jakob Botsch Nielsen <janielsen@microsoft.com>
Date: Thu, 2 Nov 2023 09:37:26 +0000
Subject: [PATCH 062/203] Merged PR 34896: Add "varying yields" benchmark;
 optimize/fix prototypes for it

Add "varying yields" benchmark; optimize/fix prototypes for it

* Add a benchmark called varying-yields. Invoke it as

```
corerun.exe <path to varying-yields.dll> <call stack depth> <suspend iteration interval> [--use-direct-yield].
```

  It measures how many iterations it is able to execute in 500 ms when
  (probabilistically) yielding on every X iteration. The depth parameter
  specifies the depth of the async call stack before iteration begins.
  The suspend iteration interval can be set to 0 to never suspend; 1
  suspends on every iteration, 2 on every second iteration, etc.

  The `--use-direct-yield` argument specifies whether the thread pool is
  used for yielding or whether the continuations passed to OnCompleted
  are invoked synchronously. The latter measures raw
  suspension/resumption performance more faithfully, it seems that the
  thread pool has significant cost in the frequent suspension cases.

  Also add versions of the benchmarks using `Task<T>` and
  `ValueTask<T>`.

* Optimize the JIT state machine implementation to create separate
  temporary locals in suspension/resumption blocks, instead of reusing
  locals. This manually live range splits the locals which improves code
  quality in the rest of the function. In particular, in the
  varying-yields benchmark it removes two out of three callee saves that
  the JIT SM codegen has in `DoYields` compared to the unwinding/normal
  codegen. This reduces the performance gap from 10% when we never
  suspend to 3%.

  Removing the last callee save is possible, but it requires a fix for
  https://github.com/dotnet/runtime/issues/93681 first. I expect that
  performance of the JIT SM and unwinding prototypes would be identical
  in the "never suspending" case after fixing this.
---
 src/coreclr/jit/async.cpp                     |  35 ++-
 .../async/async2-varying-yields-task.csproj   |   9 +
 .../async2-varying-yields-valuetask.csproj    |   9 +
 .../Loader/async/async2-varying-yields.cs     | 218 ++++++++++++++++++
 .../Loader/async/async2-varying-yields.csproj |   8 +
 5 files changed, 260 insertions(+), 19 deletions(-)
 create mode 100644 src/tests/Loader/async/async2-varying-yields-task.csproj
 create mode 100644 src/tests/Loader/async/async2-varying-yields-valuetask.csproj
 create mode 100644 src/tests/Loader/async/async2-varying-yields.cs
 create mode 100644 src/tests/Loader/async/async2-varying-yields.csproj

diff --git a/src/coreclr/jit/async.cpp b/src/coreclr/jit/async.cpp
index 4d68dca74b86c4..f7b93a2502b25f 100644
--- a/src/coreclr/jit/async.cpp
+++ b/src/coreclr/jit/async.cpp
@@ -637,11 +637,9 @@ void Async2Transformation::Transform(
 
         GenTree* newContinuation     = m_comp->gtNewLclvNode(m_newContinuationVar, TYP_REF);
         unsigned dataOffset          = m_comp->info.compCompHnd->getFieldOffset(m_async2Info.continuationDataFldHnd);
-        GenTree* dataOffsetNode      = m_comp->gtNewIconNode((ssize_t)dataOffset, TYP_I_IMPL);
-        GenTree* dataAddr            = m_comp->gtNewOperNode(GT_ADD, TYP_BYREF, newContinuation, dataOffsetNode);
-        GenTree* dataInd             = m_comp->gtNewIndir(TYP_REF, dataAddr, GTF_IND_NONFAULTING);
+        GenTree* dataInd             = LoadFromOffset(newContinuation, dataOffset, TYP_REF);
         GenTree* storeAllocedByteArr = m_comp->gtNewStoreLclVarNode(byteArrLclNum, dataInd);
-        LIR::AsRange(retBB).InsertAtEnd(newContinuation, dataOffsetNode, dataAddr, dataInd, storeAllocedByteArr);
+        LIR::AsRange(retBB).InsertAtEnd(LIR::SeqTree(m_comp, storeAllocedByteArr));
 
         if (m_comp->doesMethodHavePatchpoints() || m_comp->opts.IsOSR())
         {
@@ -730,11 +728,10 @@ void Async2Transformation::Transform(
 
         GenTree* newContinuation     = m_comp->gtNewLclvNode(m_comp->lvaAsyncContinuationArg, TYP_REF);
         unsigned dataOffset          = m_comp->info.compCompHnd->getFieldOffset(m_async2Info.continuationDataFldHnd);
-        GenTree* dataOffsetNode      = m_comp->gtNewIconNode((ssize_t)dataOffset, TYP_I_IMPL);
-        GenTree* dataAddr            = m_comp->gtNewOperNode(GT_ADD, TYP_BYREF, newContinuation, dataOffsetNode);
-        GenTree* dataInd             = m_comp->gtNewIndir(TYP_REF, dataAddr, GTF_IND_NONFAULTING);
+        GenTree* dataInd             = LoadFromOffset(newContinuation, dataOffset, TYP_REF);
         GenTree* storeAllocedByteArr = m_comp->gtNewStoreLclVarNode(byteArrLclNum, dataInd);
-        LIR::AsRange(resumeBB).InsertAtEnd(newContinuation, dataOffsetNode, dataAddr, dataInd, storeAllocedByteArr);
+
+        LIR::AsRange(resumeBB).InsertAtEnd(LIR::SeqTree(m_comp, storeAllocedByteArr));
 
         // Copy data
         for (LiveLocalInfo& inf : m_liveLocals)
@@ -783,14 +780,11 @@ void Async2Transformation::Transform(
     {
         unsigned objectArrLclNum = GetGCDataArrayVar();
 
-        newContinuation           = m_comp->gtNewLclvNode(m_comp->lvaAsyncContinuationArg, TYP_REF);
-        unsigned gcDataOffset     = m_comp->info.compCompHnd->getFieldOffset(m_async2Info.continuationGCDataFldHnd);
-        GenTree* gcDataOffsetNode = m_comp->gtNewIconNode((ssize_t)gcDataOffset, TYP_I_IMPL);
-        GenTree* gcDataAddr       = m_comp->gtNewOperNode(GT_ADD, TYP_BYREF, newContinuation, gcDataOffsetNode);
-        GenTree* gcDataInd        = m_comp->gtNewIndir(TYP_REF, gcDataAddr, GTF_IND_NONFAULTING);
+        newContinuation       = m_comp->gtNewLclvNode(m_comp->lvaAsyncContinuationArg, TYP_REF);
+        unsigned gcDataOffset = m_comp->info.compCompHnd->getFieldOffset(m_async2Info.continuationGCDataFldHnd);
+        GenTree* gcDataInd    = LoadFromOffset(newContinuation, gcDataOffset, TYP_REF);
         GenTree* storeAllocedObjectArr = m_comp->gtNewStoreLclVarNode(objectArrLclNum, gcDataInd);
-        LIR::AsRange(resumeBB).InsertAtEnd(newContinuation, gcDataOffsetNode, gcDataAddr, gcDataInd,
-                                           storeAllocedObjectArr);
+        LIR::AsRange(resumeBB).InsertAtEnd(LIR::SeqTree(m_comp, storeAllocedObjectArr));
 
         // Copy GC pointers
         for (LiveLocalInfo& inf : m_liveLocals)
@@ -1003,7 +997,10 @@ GenTreeStoreInd* Async2Transformation::StoreAtOffset(GenTree* base, unsigned off
 
 unsigned Async2Transformation::GetDataArrayVar()
 {
-    if (m_dataArrayVar == BAD_VAR_NUM)
+    // Create separate locals unless we have many locals in the method for live
+    // range splitting purposes. This helps LSRA to avoid create additional
+    // callee saves that harm the prolog/epilog.
+    if ((m_dataArrayVar == BAD_VAR_NUM) || !m_comp->lvaHaveManyLocals())
     {
         m_dataArrayVar                             = m_comp->lvaGrabTemp(false DEBUGARG("byte[] for continuation"));
         m_comp->lvaGetDesc(m_dataArrayVar)->lvType = TYP_REF;
@@ -1014,7 +1011,7 @@ unsigned Async2Transformation::GetDataArrayVar()
 
 unsigned Async2Transformation::GetGCDataArrayVar()
 {
-    if (m_gcDataArrayVar == BAD_VAR_NUM)
+    if ((m_gcDataArrayVar == BAD_VAR_NUM) || !m_comp->lvaHaveManyLocals())
     {
         m_gcDataArrayVar                             = m_comp->lvaGrabTemp(false DEBUGARG("object[] for continuation"));
         m_comp->lvaGetDesc(m_gcDataArrayVar)->lvType = TYP_REF;
@@ -1025,7 +1022,7 @@ unsigned Async2Transformation::GetGCDataArrayVar()
 
 unsigned Async2Transformation::GetResultBoxVar()
 {
-    if (m_resultBoxVar == BAD_VAR_NUM)
+    if ((m_resultBoxVar == BAD_VAR_NUM) || !m_comp->lvaHaveManyLocals())
     {
         m_resultBoxVar = m_comp->lvaGrabTemp(false DEBUGARG("object for resuming result box"));
         m_comp->lvaGetDesc(m_resultBoxVar)->lvType = TYP_REF;
@@ -1036,7 +1033,7 @@ unsigned Async2Transformation::GetResultBoxVar()
 
 unsigned Async2Transformation::GetExceptionVar()
 {
-    if (m_exceptionVar == BAD_VAR_NUM)
+    if ((m_exceptionVar == BAD_VAR_NUM) || !m_comp->lvaHaveManyLocals())
     {
         m_exceptionVar = m_comp->lvaGrabTemp(false DEBUGARG("object for resuming exception"));
         m_comp->lvaGetDesc(m_exceptionVar)->lvType = TYP_REF;
diff --git a/src/tests/Loader/async/async2-varying-yields-task.csproj b/src/tests/Loader/async/async2-varying-yields-task.csproj
new file mode 100644
index 00000000000000..6eb7985558de71
--- /dev/null
+++ b/src/tests/Loader/async/async2-varying-yields-task.csproj
@@ -0,0 +1,9 @@
+<Project Sdk="Microsoft.NET.Sdk.IL">
+  <PropertyGroup>
+    <Optimize>True</Optimize>
+    <DefineConstants>ASYNC1_TASK;$(DefineConstants)</DefineConstants>
+  </PropertyGroup>
+  <ItemGroup>
+    <Compile Include="async2-varying-yields.cs" />
+  </ItemGroup>
+</Project>
diff --git a/src/tests/Loader/async/async2-varying-yields-valuetask.csproj b/src/tests/Loader/async/async2-varying-yields-valuetask.csproj
new file mode 100644
index 00000000000000..d10972a7e74300
--- /dev/null
+++ b/src/tests/Loader/async/async2-varying-yields-valuetask.csproj
@@ -0,0 +1,9 @@
+<Project Sdk="Microsoft.NET.Sdk.IL">
+  <PropertyGroup>
+    <Optimize>True</Optimize>
+    <DefineConstants>ASYNC1_VALUETASK;$(DefineConstants)</DefineConstants>
+  </PropertyGroup>
+  <ItemGroup>
+    <Compile Include="async2-varying-yields.cs" />
+  </ItemGroup>
+</Project>
diff --git a/src/tests/Loader/async/async2-varying-yields.cs b/src/tests/Loader/async/async2-varying-yields.cs
new file mode 100644
index 00000000000000..317e23d3bc4a3c
--- /dev/null
+++ b/src/tests/Loader/async/async2-varying-yields.cs
@@ -0,0 +1,218 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+//#define ASYNC1_TASK
+//#define ASYNC1_VALUETASK
+
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Linq;
+using System.Runtime;
+using System.Runtime.CompilerServices;
+using System.Threading;
+using System.Threading.Tasks;
+using Xunit;
+
+public class Async2VaryingYields
+{
+    [Fact]
+    public static void TestEntryPoint()
+    {
+        Task.Run(AsyncEntry).Wait();
+    }
+
+    public static async Task AsyncEntry()
+    {
+        string[] args = Environment.GetCommandLineArgs();
+        if (args.Length < 3)
+        {
+            Console.WriteLine("Use: corerun.exe <path to varying-yields.dll> <depth> <suspend frequency> [--use-direct-yield]");
+            return;
+        }
+
+        if (!GCSettings.IsServerGC)
+            Console.WriteLine("*** Warning: Server GC is disabled, set DOTNET_gcServer=1 ***");
+
+        int depth = int.Parse(args[1]);
+        // Yield on average every X calls.
+        int yieldFrequency = int.Parse(args[2]);
+        Benchmark warmupBm = new Benchmark(0.0001);
+        warmupBm.Warmup = true;
+        for (int i = 0; i < 4; i++)
+        {
+            for (int j = 0; j < 40; j++)
+            {
+                await warmupBm.Run(0);
+            }
+
+            // Make sure we tier up...
+            await Task.Delay(500);
+        }
+
+        Console.WriteLine("Warmup done, running benchmark");
+
+        Benchmark bm = new(yieldFrequency == 0 ? 0 : (1 / (double)yieldFrequency));
+
+        List<long> results = new();
+        for (int i = 0; i < 16; i++)
+        {
+            bm.NumYields = 0;
+            long numIters = await bm.Run(depth);
+            Console.WriteLine($"iters={numIters} suspensions={bm.NumYields}");
+            results.Add(numIters);
+        }
+
+        results.Sort();
+        double avg = results.Skip(3).Take(10).Average();
+        Console.WriteLine("Result = {0}", (long)avg);
+    }
+
+    private class Benchmark
+    {
+        private readonly Random _rand = new();
+        private readonly double _yieldProbability;
+        public ulong NumYields;
+        public int Sink;
+        public bool Warmup;
+
+        public Benchmark(double yieldProbability) => _yieldProbability = yieldProbability;
+
+public
+#if ASYNC1_TASK
+        async Task<long>
+#elif ASYNC1_VALUETASK
+        async ValueTask<long>
+#else
+        async2 long
+#endif
+        Run(int depth)
+        {
+            int liveState1 = depth * 3 + (int)(1 / _yieldProbability);
+            int liveState2 = depth;
+            double liveState3 = _yieldProbability;
+
+            if (depth == 0)
+                return await Loop();
+
+            long result = await Run(depth - 1);
+            Sink = (int)liveState1 + (int)liveState2 + (int)(1 / liveState3) + depth;
+            return result;
+        }
+
+private
+#if ASYNC1_TASK
+        async Task<long>
+#elif ASYNC1_VALUETASK
+        async ValueTask<long>
+#else
+        async2 long
+#endif
+        Loop()
+        {
+            int time = Warmup ? 5 : 500;
+
+            Stopwatch timer = Stopwatch.StartNew();
+
+            long numIters = 0;
+            while (timer.ElapsedMilliseconds < time)
+            {
+                for (int i = 0; i < 20; i++)
+                {
+                    numIters += await DoYields();
+                }
+            }
+
+            return numIters;
+        }
+
+private
+#if ASYNC1_TASK
+        async Task<int>
+#elif ASYNC1_VALUETASK
+        async ValueTask<int>
+#else
+        async2 int
+#endif
+        DoYields()
+        {
+            int numIters = 0;
+
+            if (_rand.NextDouble() < _yieldProbability)
+            {
+                if (s_useDirectYield)
+                    await new DirectYieldAwaitable(NumYields);
+                else
+                    await Task.Yield();
+
+                NumYields++;
+            }
+
+            numIters++;
+
+            if (_rand.NextDouble() < _yieldProbability)
+            {
+                if (s_useDirectYield)
+                    await new DirectYieldAwaitable(NumYields);
+                else
+                    await Task.Yield();
+
+                NumYields++;
+            }
+
+            numIters++;
+
+            if (_rand.NextDouble() < _yieldProbability)
+            {
+                if (s_useDirectYield)
+                    await new DirectYieldAwaitable(NumYields);
+                else
+                    await Task.Yield();
+
+                NumYields++;
+            }
+
+            numIters++;
+            return numIters;
+        }
+
+        private static readonly bool s_useDirectYield = Environment.GetCommandLineArgs().Contains("--use-direct-yield");
+
+        private struct DirectYieldAwaitable
+        {
+            private readonly ulong _numYields;
+
+            public DirectYieldAwaitable(ulong numYields) => _numYields = numYields;
+
+            public DirectYieldAwaiter GetAwaiter() => new DirectYieldAwaiter(_numYields);
+
+            public struct DirectYieldAwaiter : ICriticalNotifyCompletion
+            {
+                private readonly ulong _numYields;
+
+                public DirectYieldAwaiter(ulong numYields) => _numYields = numYields;
+
+                public bool IsCompleted => false;
+
+                public void OnCompleted(Action continuation)
+                {
+                    if (_numYields % 512 == 0)
+                        ThreadPool.UnsafeQueueUserWorkItem(static act => act(), continuation, true);
+                    else
+                        continuation();
+                }
+
+                public void UnsafeOnCompleted(Action continuation)
+                {
+                    if (_numYields % 512 == 0)
+                        ThreadPool.UnsafeQueueUserWorkItem(static act => act(), continuation, true);
+                    else
+                        continuation();
+                }
+
+                public void GetResult() { }
+            }
+        }
+
+    }
+}
diff --git a/src/tests/Loader/async/async2-varying-yields.csproj b/src/tests/Loader/async/async2-varying-yields.csproj
new file mode 100644
index 00000000000000..9367a79b2edbb1
--- /dev/null
+++ b/src/tests/Loader/async/async2-varying-yields.csproj
@@ -0,0 +1,8 @@
+<Project Sdk="Microsoft.NET.Sdk.IL">
+  <PropertyGroup>
+    <Optimize>True</Optimize>
+  </PropertyGroup>
+  <ItemGroup>
+    <Compile Include="$(MSBuildProjectName).cs" />
+  </ItemGroup>
+</Project>

From 626de9098e5c9d218976e4f5c7cf30681ddadf5d Mon Sep 17 00:00:00 2001
From: Jakob Botsch Nielsen <janielsen@microsoft.com>
Date: Fri, 3 Nov 2023 08:49:47 +0000
Subject: [PATCH 063/203] Merged PR 35032: Avoid boxing return values in the
 resumed path when unnecessary

Avoid boxing return values in the resumed path when unnecessary

Place these in `Continuation.Data` instead when possible (no GC refs). It is complicated a bit by
the fact that there can already be an OSR IL offset in
`Continuation.Data`, so the return may need to be placed at offset 4;
add a flag to identify this situation. Improves `corerun.exe
varying-yields 0 1 --use-direct-yield` by about 5% according to a few
quick tests (but the variance is quite high).
---
 .../RuntimeHelpers.CoreCLR.cs                 |  98 ++++++-
 src/coreclr/inc/corinfo.h                     |   3 +
 src/coreclr/jit/async.cpp                     | 243 +++++++++++-------
 src/coreclr/jit/async.h                       |   9 +-
 src/coreclr/vm/jitinterface.cpp               |  79 +++++-
 src/tests/Loader/async/async2-retbuf.cs       |  54 ----
 src/tests/Loader/async/async2-returns.cs      |  91 +++++++
 ...c2-retbuf.csproj => async2-returns.csproj} |   0
 .../Loader/async/async2-varying-yields.cs     |  18 +-
 src/tests/Loader/async/simple-eh.cs           |   1 -
 10 files changed, 407 insertions(+), 189 deletions(-)
 delete mode 100644 src/tests/Loader/async/async2-retbuf.cs
 create mode 100644 src/tests/Loader/async/async2-returns.cs
 rename src/tests/Loader/async/{async2-retbuf.csproj => async2-returns.csproj} (100%)

diff --git a/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs
index f546c02c1c0db6..0e10e2a6295280 100644
--- a/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs
+++ b/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs
@@ -471,11 +471,23 @@ private static Continuation GetHeadContinuation(AwaitableProxy awaitableProxy)
 
         private static async Task<T> FinalizeTaskReturningThunk<T>(Continuation continuation)
         {
-            continuation.Next = new Continuation
+            Continuation finalContinuation = new Continuation();
+
+            // Note that the exact location the return value is placed is tied
+            // into getAsyncResumptionStub in the VM, so do not change this
+            // without also changing that code (and the JIT).
+            if (IsReferenceOrContainsReferences<T>())
             {
-                Flags = CorInfoContinuationFlags.CORINFO_CONTINUATION_RESULT_IN_GCDATA | CorInfoContinuationFlags.CORINFO_CONTINUATION_NEEDS_EXCEPTION,
-                GCData = new object[1],
-            };
+                finalContinuation.Flags = CorInfoContinuationFlags.CORINFO_CONTINUATION_RESULT_IN_GCDATA | CorInfoContinuationFlags.CORINFO_CONTINUATION_NEEDS_EXCEPTION;
+                finalContinuation.GCData = new object[1];
+            }
+            else
+            {
+                finalContinuation.Flags = CorInfoContinuationFlags.CORINFO_CONTINUATION_NEEDS_EXCEPTION;
+                finalContinuation.Data = new byte[Unsafe.SizeOf<T>()];
+            }
+
+            continuation.Next = finalContinuation;
 
             AwaitableProxy awaitableProxy = new AwaitableProxy();
 
@@ -486,19 +498,29 @@ private static async Task<T> FinalizeTaskReturningThunk<T>(Continuation continua
                 Continuation? finalResult = DispatchContinuations(headContinuation, out Exception? ex);
                 if (finalResult != null)
                 {
+                    Debug.Assert(finalResult == finalContinuation);
                     if (ex != null)
                         throw ex;
-                    return (T)finalResult.GCData![0];
+
+                    if (IsReferenceOrContainsReferences<T>())
+                    {
+                        return (T)finalResult.GCData![0];
+                    }
+                    else
+                    {
+                        return Unsafe.As<byte, T>(ref finalResult.Data![0]);
+                    }
                 }
             }
         }
 
         private static async Task FinalizeTaskReturningThunk(Continuation continuation)
         {
-            continuation.Next = new Continuation
+            Continuation finalContinuation = new Continuation
             {
                 Flags = CorInfoContinuationFlags.CORINFO_CONTINUATION_NEEDS_EXCEPTION,
             };
+            continuation.Next = finalContinuation;
 
             AwaitableProxy awaitableProxy = new AwaitableProxy();
 
@@ -509,6 +531,7 @@ private static async Task FinalizeTaskReturningThunk(Continuation continuation)
                 Continuation? finalResult = DispatchContinuations(headContinuation, out Exception? ex);
                 if (finalResult != null)
                 {
+                    Debug.Assert(finalResult == finalContinuation);
                     if (ex != null)
                         throw ex;
                     return;
@@ -518,11 +541,23 @@ private static async Task FinalizeTaskReturningThunk(Continuation continuation)
 
         private static async ValueTask<T> FinalizeValueTaskReturningThunk<T>(Continuation continuation)
         {
-            continuation.Next = new Continuation
+            Continuation finalContinuation = new Continuation();
+
+            // Note that the exact location the return value is placed is tied
+            // into getAsyncResumptionStub in the VM, so do not change this
+            // without also changing that code (and the JIT).
+            if (IsReferenceOrContainsReferences<T>())
             {
-                Flags = CorInfoContinuationFlags.CORINFO_CONTINUATION_RESULT_IN_GCDATA | CorInfoContinuationFlags.CORINFO_CONTINUATION_NEEDS_EXCEPTION,
-                GCData = new object[1],
-            };
+                finalContinuation.Flags = CorInfoContinuationFlags.CORINFO_CONTINUATION_RESULT_IN_GCDATA | CorInfoContinuationFlags.CORINFO_CONTINUATION_NEEDS_EXCEPTION;
+                finalContinuation.GCData = new object[1];
+            }
+            else
+            {
+                finalContinuation.Flags = CorInfoContinuationFlags.CORINFO_CONTINUATION_NEEDS_EXCEPTION;
+                finalContinuation.Data = new byte[Unsafe.SizeOf<T>()];
+            }
+
+            continuation.Next = finalContinuation;
 
             AwaitableProxy awaitableProxy = new AwaitableProxy();
 
@@ -533,16 +568,29 @@ private static async ValueTask<T> FinalizeValueTaskReturningThunk<T>(Continuatio
                 Continuation? finalResult = DispatchContinuations(headContinuation, out Exception? ex);
                 if (finalResult != null)
                 {
+                    Debug.Assert(finalResult == finalContinuation);
                     if (ex != null)
                         throw ex;
-                    return (T)finalResult.GCData![0];
+
+                    if (IsReferenceOrContainsReferences<T>())
+                    {
+                        return (T)finalResult.GCData![0];
+                    }
+                    else
+                    {
+                        return Unsafe.As<byte, T>(ref finalResult.Data![0]);
+                    }
                 }
             }
         }
 
         private static async ValueTask FinalizeValueTaskReturningThunk(Continuation continuation)
         {
-            continuation.Next = new Continuation();
+            Continuation finalContinuation = new Continuation
+            {
+                Flags = CorInfoContinuationFlags.CORINFO_CONTINUATION_NEEDS_EXCEPTION,
+            };
+            continuation.Next = finalContinuation;
 
             AwaitableProxy awaitableProxy = new AwaitableProxy();
 
@@ -553,6 +601,7 @@ private static async ValueTask FinalizeValueTaskReturningThunk(Continuation cont
                 Continuation? finalResult = DispatchContinuations(headContinuation, out Exception? ex);
                 if (finalResult != null)
                 {
+                    Debug.Assert(finalResult == finalContinuation);
                     if (ex != null)
                         throw ex;
                     return;
@@ -911,6 +960,9 @@ internal enum CorInfoContinuationFlags
         // should be placed at index 0 or 1 depending on whether the continuation
         // also expects a result.
         CORINFO_CONTINUATION_NEEDS_EXCEPTION = 2,
+        // If this bit is set the continuation has an OSR IL offset saved in the
+        // beginning of 'Data'.
+        CORINFO_CONTINUATION_OSR_IL_OFFSET_IN_DATA = 4,
     }
 
 
@@ -920,6 +972,28 @@ internal sealed unsafe class Continuation
         public delegate*<Continuation, Continuation?> Resume;
         public uint State;
         public CorInfoContinuationFlags Flags;
+
+        // Data and GCData contain the state of the continuation.
+        // Note: The JIT is ultimately responsible for laying out these arrays.
+        // However, other parts of the system depend on the layout to
+        // know where to locate or place various pieces of data:
+        //
+        // 1. Resumption stubs need to know where to place the return value
+        // inside the next continuation. If the return value has GC references
+        // then it is boxed and placed at GCData[0]; otherwise, it is placed
+        // inside Data at offset 0 if
+        // CORINFO_CONTINUATION_OSR_IL_OFFSET_IN_DATA is NOT set and otherwise
+        // at offset 4.
+        //
+        // 2. Likewise, Finalize[Value]TaskReturningThunk needs to know from
+        // where to extract the return value.
+        //
+        // 3. The dispatcher needs to know where to place the exception inside
+        // the next continuation with a handler. Continuations with handlers
+        // have CORINFO_CONTINUATION_NEEDS_EXCEPTION set. The exception is
+        // placed at GCData[0] if CORINFO_CONTINUATION_RESULT_IN_GCDATA is NOT
+        // set, and otherwise at GCData[1].
+        //
         public byte[]? Data;
         public object[]? GCData;
     }
diff --git a/src/coreclr/inc/corinfo.h b/src/coreclr/inc/corinfo.h
index 26a1b2380e6051..fb3e95e40f7e90 100644
--- a/src/coreclr/inc/corinfo.h
+++ b/src/coreclr/inc/corinfo.h
@@ -1837,6 +1837,9 @@ enum CorInfoContinuationFlags
     // should be placed at index 0 or 1 depending on whether the continuation
     // also expects a result.
     CORINFO_CONTINUATION_NEEDS_EXCEPTION = 2,
+    // If this bit is set the continuation has an OSR IL offset saved in the
+    // beginning of 'Data'.
+    CORINFO_CONTINUATION_OSR_IL_OFFSET_IN_DATA = 4,
 };
 
 struct CORINFO_ASYNC2_INFO
diff --git a/src/coreclr/jit/async.cpp b/src/coreclr/jit/async.cpp
index f7b93a2502b25f..75618def1538e8 100644
--- a/src/coreclr/jit/async.cpp
+++ b/src/coreclr/jit/async.cpp
@@ -393,21 +393,50 @@ void Async2Transformation::Transform(
     unsigned dataSize    = 0;
     unsigned gcRefsCount = 0;
 
-    // Result value is currently always stored boxed in the continuation as it
-    // needs to be written by the resumption stub and by the asynchronous
-    // callbacks. If we specialized the continuation type we could avoid this.
-    if (call->gtReturnType != TYP_VOID)
+    // For OSR, we store the transition IL offset at the beginning of the data
+    // (-1 in the tier0 version):
+    if (m_comp->doesMethodHavePatchpoints() || m_comp->opts.IsOSR())
+    {
+        dataSize += sizeof(int);
+    }
+
+    ClassLayout* returnStructLayout = nullptr;
+    unsigned     returnSize         = 0;
+    bool         returnInGCData     = false;
+    if (call->gtReturnType == TYP_STRUCT)
+    {
+        returnStructLayout = m_comp->typGetObjLayout(call->gtRetClsHnd);
+        returnSize         = returnStructLayout->GetSize();
+        returnInGCData     = returnStructLayout->HasGCPtr();
+    }
+    else
+    {
+        returnSize     = genTypeSize(call->gtReturnType);
+        returnInGCData = varTypeIsGC(call->gtReturnType);
+    }
+
+    assert((returnSize > 0) == (call->gtReturnType != TYP_VOID));
+
+    // The return value is always stored:
+    // 1. At index 0 in GCData if it is a TYP_REF or a struct with GC references
+    // 2. At index 0 in Data, for non OSR methods without GC ref returns
+    // 3. At index 4 in Data for OSR methods without GC ref returns. The
+    // continuation flags indicates this scenario with a flag.
+    unsigned returnValDataOffset = UINT_MAX;
+    if (returnInGCData)
+    {
         gcRefsCount++;
+    }
+    else if (returnSize > 0)
+    {
+        returnValDataOffset = dataSize;
+        dataSize += returnSize;
+    }
 
     unsigned exceptionGCDataIndex = UINT_MAX;
     if (block->hasTryIndex())
         exceptionGCDataIndex = gcRefsCount++;
 
-    // For OSR, we store the patchpoint information at the beginning of the data
-    // (null in the tier0 version):
-    if (m_comp->doesMethodHavePatchpoints() || m_comp->opts.IsOSR())
-        dataSize += sizeof(int);
-
     for (LiveLocalInfo& inf : m_liveLocals)
     {
         dataSize = roundUp(dataSize, inf.Alignment);
@@ -537,10 +566,12 @@ void Async2Transformation::Transform(
 
     // Fill in 'flags'
     unsigned continuationFlags = 0;
-    if (call->gtReturnType != TYP_VOID)
+    if (returnInGCData)
         continuationFlags |= CORINFO_CONTINUATION_RESULT_IN_GCDATA;
     if (block->hasTryIndex())
         continuationFlags |= CORINFO_CONTINUATION_NEEDS_EXCEPTION;
+    if (m_comp->doesMethodHavePatchpoints() || m_comp->opts.IsOSR())
+        continuationFlags |= CORINFO_CONTINUATION_OSR_IL_OFFSET_IN_DATA;
 
     newContinuation      = m_comp->gtNewLclvNode(m_newContinuationVar, TYP_REF);
     unsigned flagsOffset = m_comp->info.compCompHnd->getFieldOffset(m_async2Info.continuationFlagsFldHnd);
@@ -722,14 +753,15 @@ void Async2Transformation::Transform(
 
     m_comp->fgAddRefPred(remainder, resumeBB);
 
+    unsigned resumeByteArrLclNum = BAD_VAR_NUM;
     if (dataSize > 0)
     {
-        unsigned byteArrLclNum = GetDataArrayVar();
+        resumeByteArrLclNum = GetDataArrayVar();
 
         GenTree* newContinuation     = m_comp->gtNewLclvNode(m_comp->lvaAsyncContinuationArg, TYP_REF);
         unsigned dataOffset          = m_comp->info.compCompHnd->getFieldOffset(m_async2Info.continuationDataFldHnd);
         GenTree* dataInd             = LoadFromOffset(newContinuation, dataOffset, TYP_REF);
-        GenTree* storeAllocedByteArr = m_comp->gtNewStoreLclVarNode(byteArrLclNum, dataInd);
+        GenTree* storeAllocedByteArr = m_comp->gtNewStoreLclVarNode(resumeByteArrLclNum, dataInd);
 
         LIR::AsRange(resumeBB).InsertAtEnd(LIR::SeqTree(m_comp, storeAllocedByteArr));
 
@@ -743,7 +775,7 @@ void Async2Transformation::Transform(
 
             LclVarDsc* dsc = m_comp->lvaGetDesc(inf.LclNum);
 
-            GenTree* byteArr = m_comp->gtNewLclvNode(byteArrLclNum, TYP_REF);
+            GenTree* byteArr = m_comp->gtNewLclvNode(resumeByteArrLclNum, TYP_REF);
             unsigned offset  = OFFSETOF__CORINFO_Array__data + inf.DataOffset;
             GenTree* cns     = m_comp->gtNewIconNode((ssize_t)offset, TYP_I_IMPL);
             GenTree* addr    = m_comp->gtNewOperNode(GT_ADD, TYP_BYREF, byteArr, cns);
@@ -776,14 +808,15 @@ void Async2Transformation::Transform(
         }
     }
 
+    unsigned resumeObjectArrLclNum = BAD_VAR_NUM;
     if (gcRefsCount > 0)
     {
-        unsigned objectArrLclNum = GetGCDataArrayVar();
+        resumeObjectArrLclNum = GetGCDataArrayVar();
 
         newContinuation       = m_comp->gtNewLclvNode(m_comp->lvaAsyncContinuationArg, TYP_REF);
         unsigned gcDataOffset = m_comp->info.compCompHnd->getFieldOffset(m_async2Info.continuationGCDataFldHnd);
         GenTree* gcDataInd    = LoadFromOffset(newContinuation, gcDataOffset, TYP_REF);
-        GenTree* storeAllocedObjectArr = m_comp->gtNewStoreLclVarNode(objectArrLclNum, gcDataInd);
+        GenTree* storeAllocedObjectArr = m_comp->gtNewStoreLclVarNode(resumeObjectArrLclNum, gcDataInd);
         LIR::AsRange(resumeBB).InsertAtEnd(LIR::SeqTree(m_comp, storeAllocedObjectArr));
 
         // Copy GC pointers
@@ -797,7 +830,7 @@ void Async2Transformation::Transform(
             LclVarDsc* dsc = m_comp->lvaGetDesc(inf.LclNum);
             if (dsc->TypeGet() == TYP_REF)
             {
-                GenTree* objectArr = m_comp->gtNewLclvNode(objectArrLclNum, TYP_REF);
+                GenTree* objectArr = m_comp->gtNewLclvNode(resumeObjectArrLclNum, TYP_REF);
                 unsigned offset    = OFFSETOF__CORINFO_Array__data + (inf.GCDataIndex * TARGET_POINTER_SIZE);
                 GenTree* value     = LoadFromOffset(objectArr, offset, TYP_REF);
                 GenTree* store     = m_comp->gtNewStoreLclVarNode(inf.LclNum, value);
@@ -818,7 +851,7 @@ void Async2Transformation::Transform(
                         continue;
                     }
 
-                    GenTree* objectArr = m_comp->gtNewLclvNode(objectArrLclNum, TYP_REF);
+                    GenTree* objectArr = m_comp->gtNewLclvNode(resumeObjectArrLclNum, TYP_REF);
                     unsigned offset    = OFFSETOF__CORINFO_Array__data + ((inf.GCDataIndex + i) * TARGET_POINTER_SIZE);
                     GenTree* value     = LoadFromOffset(objectArr, offset, TYP_REF);
                     GenTree* store;
@@ -855,7 +888,7 @@ void Async2Transformation::Transform(
 
             // Check if we have an exception.
             unsigned exceptionLclNum = GetExceptionVar();
-            GenTree* objectArr       = m_comp->gtNewLclvNode(objectArrLclNum, TYP_REF);
+            GenTree* objectArr       = m_comp->gtNewLclvNode(resumeObjectArrLclNum, TYP_REF);
             unsigned exceptionOffset = OFFSETOF__CORINFO_Array__data + exceptionGCDataIndex * TARGET_POINTER_SIZE;
             GenTree* exceptionInd    = LoadFromOffset(objectArr, exceptionOffset, TYP_REF);
             GenTree* storeException  = m_comp->gtNewStoreLclVarNode(exceptionLclNum, exceptionInd);
@@ -878,96 +911,113 @@ void Async2Transformation::Transform(
             storeResultBB->bbFlags |= BBF_ASYNC_RESUMPTION;
             JITDUMP("Added " FMT_BB " to rethrow exception at suspension point\n", rethrowExceptionBB->bbNum);
         }
+    }
 
-        // Copy call return value.
-        if (storeResultNode != nullptr)
+    // Copy call return value.
+    if (storeResultNode != nullptr)
+    {
+        GenTree*     resultBase;
+        unsigned     resultOffset;
+        GenTreeFlags resultIndirFlags = GTF_IND_NONFAULTING;
+        if (returnInGCData)
         {
-            GenTree* objectArr = m_comp->gtNewLclvNode(objectArrLclNum, TYP_REF);
-            GenTree* resultBox = LoadFromOffset(objectArr, OFFSETOF__CORINFO_Array__data, TYP_REF);
+            assert(resumeObjectArrLclNum != BAD_VAR_NUM);
+            resultBase = m_comp->gtNewLclvNode(resumeObjectArrLclNum, TYP_REF);
 
-            LclVarDsc* resultLcl = m_comp->lvaGetDesc(storeResultNode);
-            assert((retbufArg != nullptr) ||
-                   (resultLcl->TypeGet() == TYP_STRUCT) == storeResultNode->TypeIs(TYP_STRUCT));
+            if (call->gtReturnType == TYP_STRUCT)
+            {
+                // Boxed struct.
+                resultBase   = LoadFromOffset(resultBase, OFFSETOF__CORINFO_Array__data, TYP_REF);
+                resultOffset = TARGET_POINTER_SIZE; // Offset of data inside box
+            }
+            else
+            {
+                assert(call->gtReturnType == TYP_REF);
+                resultOffset = OFFSETOF__CORINFO_Array__data;
+            }
+        }
+        else
+        {
+            assert(resumeByteArrLclNum != BAD_VAR_NUM);
+            resultBase   = m_comp->gtNewLclvNode(resumeByteArrLclNum, TYP_REF);
+            resultOffset = OFFSETOF__CORINFO_Array__data + returnValDataOffset;
+            if (returnValDataOffset != 0)
+                resultIndirFlags = GTF_IND_UNALIGNED;
+        }
+
+        LclVarDsc* resultLcl = m_comp->lvaGetDesc(storeResultNode);
+        assert((resultLcl->TypeGet() == TYP_STRUCT) == (call->gtReturnType == TYP_STRUCT));
 
-            // TODO-TP: We can use liveness to avoid generating a lot of this IR.
-            if ((retbufArg != nullptr) || storeResultNode->TypeIs(TYP_STRUCT))
+        // TODO-TP: We can use liveness to avoid generating a lot of this IR.
+        if (call->gtReturnType == TYP_STRUCT)
+        {
+            if (m_comp->lvaGetPromotionType(resultLcl) != Compiler::PROMOTION_TYPE_INDEPENDENT)
             {
-                if (m_comp->lvaGetPromotionType(resultLcl) != Compiler::PROMOTION_TYPE_INDEPENDENT)
+                GenTree* resultOffsetNode = m_comp->gtNewIconNode((ssize_t)resultOffset, TYP_I_IMPL);
+                GenTree* resultAddr       = m_comp->gtNewOperNode(GT_ADD, TYP_BYREF, resultBase, resultOffsetNode);
+                GenTree* resultData       = m_comp->gtNewBlkIndir(returnStructLayout, resultAddr, resultIndirFlags);
+                GenTree* storeResult;
+                if ((storeResultNode->GetLclOffs() == 0) &&
+                    ClassLayout::AreCompatible(resultLcl->GetLayout(), returnStructLayout))
                 {
-                    GenTree*     boxDataOffset = m_comp->gtNewIconNode(TARGET_POINTER_SIZE, TYP_I_IMPL);
-                    GenTree*     boxDataAddr   = m_comp->gtNewOperNode(GT_ADD, TYP_BYREF, resultBox, boxDataOffset);
-                    ClassLayout* returnLayout  = m_comp->typGetObjLayout(call->gtRetClsHnd);
-                    GenTree*     boxData       = m_comp->gtNewBlkIndir(returnLayout, boxDataAddr, GTF_IND_NONFAULTING);
-                    GenTree*     storeResult;
-                    if ((storeResultNode->GetLclOffs() == 0) && (returnLayout->GetSize() == resultLcl->lvExactSize()))
-                    {
-                        storeResult = m_comp->gtNewStoreLclVarNode(storeResultNode->GetLclNum(), boxData);
-                    }
-                    else
-                    {
-                        storeResult =
-                            m_comp->gtNewStoreLclFldNode(storeResultNode->GetLclNum(), TYP_STRUCT, returnLayout,
-                                                         storeResultNode->GetLclOffs(), boxData);
-                    }
-
-                    LIR::AsRange(resumeBB).InsertAtEnd(LIR::SeqTree(m_comp, storeResult));
+                    storeResult = m_comp->gtNewStoreLclVarNode(storeResultNode->GetLclNum(), resultData);
                 }
                 else
                 {
-                    assert(retbufArg == nullptr); // Locals defined through retbufs are never independently promoted.
-
-                    if (resultLcl->lvFieldCnt > 1)
-                    {
-                        unsigned resultBoxVar   = GetResultBoxVar();
-                        GenTree* storeResultBox = m_comp->gtNewStoreLclVarNode(resultBoxVar, resultBox);
-                        LIR::AsRange(resumeBB).InsertAtEnd(LIR::SeqTree(m_comp, storeResultBox));
-
-                        resultBox = m_comp->gtNewLclVarNode(resultBoxVar, TYP_REF);
-                    }
-
-                    assert(storeResultNode->OperIs(GT_STORE_LCL_VAR));
-                    for (unsigned i = 0; i < resultLcl->lvFieldCnt; i++)
-                    {
-                        unsigned   fieldLclNum = resultLcl->lvFieldLclStart + i;
-                        LclVarDsc* fieldDsc    = m_comp->lvaGetDesc(fieldLclNum);
-
-                        unsigned fldOffset = TARGET_POINTER_SIZE + fieldDsc->lvFldOffset;
-                        GenTree* value     = LoadFromOffset(resultBox, fldOffset, fieldDsc->TypeGet());
-                        GenTree* store     = m_comp->gtNewStoreLclVarNode(fieldLclNum, value);
-                        LIR::AsRange(resumeBB).InsertAtEnd(LIR::SeqTree(m_comp, store));
-
-                        if (i + 1 != resultLcl->lvFieldCnt)
-                        {
-                            resultBox = m_comp->gtCloneExpr(resultBox);
-                        }
-                    }
+                    storeResult =
+                        m_comp->gtNewStoreLclFldNode(storeResultNode->GetLclNum(), TYP_STRUCT, returnStructLayout,
+                                                     storeResultNode->GetLclOffs(), resultData);
                 }
+
+                LIR::AsRange(resumeBB).InsertAtEnd(LIR::SeqTree(m_comp, storeResult));
             }
             else
             {
-                GenTree* value;
-                if (call->gtReturnType == TYP_REF)
-                {
-                    value = resultBox;
-                }
-                else
-                {
-                    value = LoadFromOffset(resultBox, TARGET_POINTER_SIZE, call->gtReturnType);
-                }
+                assert(retbufArg == nullptr); // Locals defined through retbufs are never independently promoted.
 
-                GenTree* storeResult;
-                if (storeResultNode->OperIs(GT_STORE_LCL_VAR))
+                if ((resultLcl->lvFieldCnt > 1) && !resultBase->OperIsLocal())
                 {
-                    storeResult = m_comp->gtNewStoreLclVarNode(storeResultNode->GetLclNum(), value);
+                    unsigned resultBaseVar   = GetResultBaseVar();
+                    GenTree* storeResultBase = m_comp->gtNewStoreLclVarNode(resultBaseVar, resultBase);
+                    LIR::AsRange(resumeBB).InsertAtEnd(LIR::SeqTree(m_comp, storeResultBase));
+
+                    resultBase = m_comp->gtNewLclVarNode(resultBaseVar, TYP_REF);
                 }
-                else
+
+                assert(storeResultNode->OperIs(GT_STORE_LCL_VAR));
+                for (unsigned i = 0; i < resultLcl->lvFieldCnt; i++)
                 {
-                    storeResult = m_comp->gtNewStoreLclFldNode(storeResultNode->GetLclNum(), storeResultNode->TypeGet(),
-                                                               storeResultNode->GetLclOffs(), value);
+                    unsigned   fieldLclNum = resultLcl->lvFieldLclStart + i;
+                    LclVarDsc* fieldDsc    = m_comp->lvaGetDesc(fieldLclNum);
+
+                    unsigned fldOffset = resultOffset + fieldDsc->lvFldOffset;
+                    GenTree* value     = LoadFromOffset(resultBase, fldOffset, fieldDsc->TypeGet(), resultIndirFlags);
+                    GenTree* store     = m_comp->gtNewStoreLclVarNode(fieldLclNum, value);
+                    LIR::AsRange(resumeBB).InsertAtEnd(LIR::SeqTree(m_comp, store));
+
+                    if (i + 1 != resultLcl->lvFieldCnt)
+                    {
+                        resultBase = m_comp->gtCloneExpr(resultBase);
+                    }
                 }
+            }
+        }
+        else
+        {
+            GenTree* value = LoadFromOffset(resultBase, resultOffset, call->gtReturnType, resultIndirFlags);
 
-                LIR::AsRange(resumeBB).InsertAtEnd(LIR::SeqTree(m_comp, storeResult));
+            GenTree* storeResult;
+            if (storeResultNode->OperIs(GT_STORE_LCL_VAR))
+            {
+                storeResult = m_comp->gtNewStoreLclVarNode(storeResultNode->GetLclNum(), value);
             }
+            else
+            {
+                storeResult = m_comp->gtNewStoreLclFldNode(storeResultNode->GetLclNum(), storeResultNode->TypeGet(),
+                                                           storeResultNode->GetLclOffs(), value);
+            }
+
+            LIR::AsRange(resumeBB).InsertAtEnd(LIR::SeqTree(m_comp, storeResult));
         }
     }
 
@@ -975,13 +1025,16 @@ void Async2Transformation::Transform(
     JITDUMP("  Created resumption BB " FMT_BB "\n", resumeBB->bbNum);
 }
 
-GenTreeIndir* Async2Transformation::LoadFromOffset(GenTree* base, unsigned offset, var_types type)
+GenTreeIndir* Async2Transformation::LoadFromOffset(GenTree*     base,
+                                                   unsigned     offset,
+                                                   var_types    type,
+                                                   GenTreeFlags indirFlags)
 {
     assert(base->TypeIs(TYP_REF, TYP_BYREF, TYP_I_IMPL));
     GenTree*      cns      = m_comp->gtNewIconNode((ssize_t)offset, TYP_I_IMPL);
     var_types     addrType = base->TypeIs(TYP_I_IMPL) ? TYP_I_IMPL : TYP_BYREF;
     GenTree*      addr     = m_comp->gtNewOperNode(GT_ADD, addrType, base, cns);
-    GenTreeIndir* load     = m_comp->gtNewIndir(type, addr, GTF_IND_NONFAULTING);
+    GenTreeIndir* load     = m_comp->gtNewIndir(type, addr, indirFlags);
     return load;
 }
 
@@ -1020,15 +1073,15 @@ unsigned Async2Transformation::GetGCDataArrayVar()
     return m_gcDataArrayVar;
 }
 
-unsigned Async2Transformation::GetResultBoxVar()
+unsigned Async2Transformation::GetResultBaseVar()
 {
-    if ((m_resultBoxVar == BAD_VAR_NUM) || !m_comp->lvaHaveManyLocals())
+    if ((m_resultBaseVar == BAD_VAR_NUM) || !m_comp->lvaHaveManyLocals())
     {
-        m_resultBoxVar = m_comp->lvaGrabTemp(false DEBUGARG("object for resuming result box"));
-        m_comp->lvaGetDesc(m_resultBoxVar)->lvType = TYP_REF;
+        m_resultBaseVar = m_comp->lvaGrabTemp(false DEBUGARG("object for resuming result base"));
+        m_comp->lvaGetDesc(m_resultBaseVar)->lvType = TYP_REF;
     }
 
-    return m_resultBoxVar;
+    return m_resultBaseVar;
 }
 
 unsigned Async2Transformation::GetExceptionVar()
diff --git a/src/coreclr/jit/async.h b/src/coreclr/jit/async.h
index a98551646e2ffb..c9e19c89ebfcc1 100644
--- a/src/coreclr/jit/async.h
+++ b/src/coreclr/jit/async.h
@@ -28,7 +28,7 @@ class Async2Transformation
     unsigned                      m_newContinuationVar      = BAD_VAR_NUM;
     unsigned                      m_dataArrayVar            = BAD_VAR_NUM;
     unsigned                      m_gcDataArrayVar          = BAD_VAR_NUM;
-    unsigned                      m_resultBoxVar            = BAD_VAR_NUM;
+    unsigned                      m_resultBaseVar           = BAD_VAR_NUM;
     unsigned                      m_exceptionVar            = BAD_VAR_NUM;
     BasicBlock*                   m_lastSuspensionBB        = nullptr;
 
@@ -43,12 +43,15 @@ class Async2Transformation
                    class AsyncLiveness&      life,
                    BasicBlock**              remainder);
 
-    GenTreeIndir* LoadFromOffset(GenTree* base, unsigned offset, var_types type);
+    GenTreeIndir* LoadFromOffset(GenTree*     base,
+                                 unsigned     offset,
+                                 var_types    type,
+                                 GenTreeFlags indirFlags = GTF_IND_NONFAULTING);
     GenTreeStoreInd* StoreAtOffset(GenTree* base, unsigned offset, GenTree* value);
 
     unsigned GetDataArrayVar();
     unsigned GetGCDataArrayVar();
-    unsigned GetResultBoxVar();
+    unsigned GetResultBaseVar();
     unsigned GetExceptionVar();
 
     GenTree* CreateResumptionStubAddrTree();
diff --git a/src/coreclr/vm/jitinterface.cpp b/src/coreclr/vm/jitinterface.cpp
index 8137b64c5e1cfe..fbe44befa6223c 100644
--- a/src/coreclr/vm/jitinterface.cpp
+++ b/src/coreclr/vm/jitinterface.cpp
@@ -14383,40 +14383,91 @@ CORINFO_METHOD_HANDLE CEEJitInfo::getAsyncResumptionStub()
     pCode->EmitCALLI(pCode->GetSigToken(calliSig.GetRawSig(), calliSig.GetRawSigLen()), numArgs, msig.IsReturnTypeVoid() ? 0 : 1);
 
     DWORD resultLoc = UINT_MAX;
+    TypeHandle resultTypeHnd;
     if (!msig.IsReturnTypeVoid())
     {
-        resultLoc = pCode->NewLocal(LocalDesc(msig.GetRetTypeHandleThrowing()));
+        resultTypeHnd = msig.GetRetTypeHandleThrowing();
+        resultLoc = pCode->NewLocal(LocalDesc(resultTypeHnd));
         pCode->EmitSTLOC(resultLoc);
     }
 
-    DWORD newContinuationLoc = pCode->NewLocal(LocalDesc(CoreLibBinder::GetClass(CLASS__CONTINUATION)));
+    TypeHandle continuationTypeHnd = CoreLibBinder::GetClass(CLASS__CONTINUATION);
+    DWORD newContinuationLoc = pCode->NewLocal(LocalDesc(continuationTypeHnd));
     pCode->EmitCALL(METHOD__STUBHELPERS__ASYNC2_CALL_CONTINUATION, 0, 1);
     pCode->EmitSTLOC(newContinuationLoc);
 
     if (!msig.IsReturnTypeVoid())
     {
-        ILCodeLabel* noResult = pCode->NewCodeLabel();
+        ILCodeLabel* doneResult = pCode->NewCodeLabel();
         pCode->EmitLDLOC(newContinuationLoc);
-        pCode->EmitBRTRUE(noResult);
+        pCode->EmitBRTRUE(doneResult);
 
         // Load 'next' of current continuation
         pCode->EmitLDARG(0);
         pCode->EmitLDFLD(FIELD__CONTINUATION__NEXT);
 
-        // Load 'gcdata' of next continuation
-        pCode->EmitLDFLD(FIELD__CONTINUATION__GCDATA);
+        // Result is placed in GCData[0] if it has GC references (potentially boxing it).
+        bool isOrContainsGCPointers = false;
+        if (CorTypeInfo::IsObjRef(resultTypeHnd.GetInternalCorElementType()) || (resultTypeHnd.IsValueType() && resultTypeHnd.AsMethodTable()->ContainsPointers()))
+        {
+            // Load 'gcdata' of next continuation
+            pCode->EmitLDFLD(FIELD__CONTINUATION__GCDATA);
+
+            // Now we have the GC array. At the first index is the result.
+            pCode->EmitLDC(0);
+
+            // Box the result.
+            pCode->EmitLDLOC(resultLoc);
+            pCode->EmitBOX(pCode->GetToken(resultTypeHnd));
+
+            // Finally store it.
+            pCode->EmitSTELEM_REF();
+        }
+        else
+        {
+            // Otherwise it goes into Data, either at offset 0 or 4 depending
+            // on CORINFO_CONTINUATION_OSR_IL_OFFSET_IN_DATA.
+            ILCodeLabel* hasOsrILOffset = pCode->NewCodeLabel();
 
-        // Now we have the GC array. At the first index is the result.
-        pCode->EmitLDC(0);
+            unsigned nextContinuationLcl = pCode->NewLocal(LocalDesc(continuationTypeHnd));
+            pCode->EmitSTLOC(nextContinuationLcl);
 
-        // Box the result.
-        pCode->EmitLDLOC(resultLoc);
-        pCode->EmitBOX(pCode->GetToken(msig.GetRetTypeHandleThrowing()));
+            // Load 'flags' of next continuation
+            pCode->EmitLDLOC(nextContinuationLcl);
+            pCode->EmitLDFLD(FIELD__CONTINUATION__FLAGS);
+            pCode->EmitLDC(CORINFO_CONTINUATION_OSR_IL_OFFSET_IN_DATA);
+            pCode->EmitAND();
+            pCode->EmitBRTRUE(hasOsrILOffset);
 
-        // Finally store it.
-        pCode->EmitSTELEM_REF();
+            // Load 'data' of next continuation
+            pCode->EmitLDLOC(nextContinuationLcl);
+            pCode->EmitLDFLD(FIELD__CONTINUATION__DATA);
+            // Load address of array at index 0
+            pCode->EmitLDC(0);
+            pCode->EmitLDELEMA(pCode->GetToken(CoreLibBinder::GetClass(CLASS__BYTE)));
+
+            // Store at index 0.
+            pCode->EmitLDLOC(resultLoc);
+            pCode->EmitSTOBJ(pCode->GetToken(resultTypeHnd));
+
+            pCode->EmitBR(doneResult);
+
+            pCode->EmitLabel(hasOsrILOffset);
+
+            // Load 'data' of next continuation
+            pCode->EmitLDLOC(nextContinuationLcl);
+            pCode->EmitLDFLD(FIELD__CONTINUATION__DATA);
+            // Load address of array at index 4
+            pCode->EmitLDC(4);
+            pCode->EmitLDELEMA(pCode->GetToken(CoreLibBinder::GetClass(CLASS__BYTE)));
+
+            // Store at index 4.
+            pCode->EmitLDLOC(resultLoc);
+            pCode->EmitUNALIGNED(1);
+            pCode->EmitSTOBJ(pCode->GetToken(resultTypeHnd));
+        }
 
-        pCode->EmitLabel(noResult);
+        pCode->EmitLabel(doneResult);
     }
 
     pCode->EmitLDLOC(newContinuationLoc);
diff --git a/src/tests/Loader/async/async2-retbuf.cs b/src/tests/Loader/async/async2-retbuf.cs
deleted file mode 100644
index a8976504416fe8..00000000000000
--- a/src/tests/Loader/async/async2-retbuf.cs
+++ /dev/null
@@ -1,54 +0,0 @@
-// 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 System.Runtime.CompilerServices;
-using System.Threading.Tasks;
-using Xunit;
-
-public class Async2Retbuf
-{
-    [Fact]
-    public static void TestEntryPoint()
-    {
-        Retbuf(new C()).Wait();
-    }
-
-    [MethodImpl(MethodImplOptions.NoInlining)]
-    private static async2 void Retbuf(C c)
-    {
-        for (int i = 0; i < 20000; i++)
-        {
-            S val = await ReturnsStruct();
-
-            AssertEqual(42, val.A);
-            AssertEqual(4242, val.B);
-            AssertEqual(424242, val.C);
-            AssertEqual(42424242, val.D);
-
-            c.Val = default;
-            c.Val = await ReturnsStruct();
-
-            AssertEqual(42, c.Val.A);
-            AssertEqual(4242, c.Val.B);
-            AssertEqual(424242, c.Val.C);
-            AssertEqual(42424242, c.Val.D);
-        }
-    }
-
-    [MethodImpl(MethodImplOptions.NoInlining)]
-    private static void AssertEqual(long expected, long actual)
-    {
-        Assert.Equal(expected, actual);
-    }
-
-    private static async2 S ReturnsStruct()
-    {
-        await Task.Yield();
-        return new S { A = 42, B = 4242, C = 424242, D = 42424242 };
-    }
-
-    private struct S { public long A, B, C, D; }
-
-    private class C { public S Val; }
-}
diff --git a/src/tests/Loader/async/async2-returns.cs b/src/tests/Loader/async/async2-returns.cs
new file mode 100644
index 00000000000000..ae006fb022131c
--- /dev/null
+++ b/src/tests/Loader/async/async2-returns.cs
@@ -0,0 +1,91 @@
+// 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 System.Runtime.CompilerServices;
+using System.Threading.Tasks;
+using Xunit;
+
+public class Async2Returns
+{
+    [Fact]
+    public static void TestEntryPoint()
+    {
+        Returns(new C()).Wait();
+    }
+
+    [MethodImpl(MethodImplOptions.NoInlining)]
+    private static async2 void Returns(C c)
+    {
+        for (int i = 0; i < 20000; i++)
+        {
+            S<long> val = await ReturnsStruct();
+
+            AssertEqual(42, val.A);
+            AssertEqual(4242, val.B);
+            AssertEqual(424242, val.C);
+            AssertEqual(42424242, val.D);
+
+            c.Val = default;
+            c.Val = await ReturnsStruct();
+
+            AssertEqual(42, c.Val.A);
+            AssertEqual(4242, c.Val.B);
+            AssertEqual(424242, c.Val.C);
+            AssertEqual(42424242, c.Val.D);
+
+            S<string> strings = await ReturnsStructGC();
+            AssertEqual("A", strings.A);
+            AssertEqual("B", strings.B);
+            AssertEqual("C", strings.C);
+            AssertEqual("D", strings.D);
+
+            S<byte> bytes = await ReturnsBytes();
+            AssertEqual(4, bytes.A);
+            AssertEqual(40, bytes.B);
+            AssertEqual(42, bytes.C);
+            AssertEqual(45, bytes.D);
+
+            string str = await ReturnsString();
+            AssertEqual("a string!", str);
+        }
+    }
+
+    [MethodImpl(MethodImplOptions.NoInlining)]
+    private static void AssertEqual<T>(T expected, T actual)
+    {
+        Assert.Equal(expected, actual);
+    }
+
+    [MethodImpl(MethodImplOptions.NoInlining)]
+    private static async2 S<long> ReturnsStruct()
+    {
+        await Task.Yield();
+        return new S<long> { A = 42, B = 4242, C = 424242, D = 42424242 };
+    }
+
+    [MethodImpl(MethodImplOptions.NoInlining)]
+    private static async2 S<string> ReturnsStructGC()
+    {
+        await Task.Yield();
+        return new S<string> { A = "A", B = "B", C = "C", D = "D" };
+    }
+
+    [MethodImpl(MethodImplOptions.NoInlining)]
+    private static async2 S<byte> ReturnsBytes()
+    {
+        await Task.Yield();
+        return new S<byte> { A = 4, B = 40, C = 42, D = 45 };
+    }
+
+    [MethodImpl(MethodImplOptions.NoInlining)]
+    private static async2 string ReturnsString()
+    {
+        await Task.Yield();
+        return "a string!";
+    }
+
+    private struct S<T> { public T A, B, C, D; }
+
+    private class C { public S<long> Val; }
+}
diff --git a/src/tests/Loader/async/async2-retbuf.csproj b/src/tests/Loader/async/async2-returns.csproj
similarity index 100%
rename from src/tests/Loader/async/async2-retbuf.csproj
rename to src/tests/Loader/async/async2-returns.csproj
diff --git a/src/tests/Loader/async/async2-varying-yields.cs b/src/tests/Loader/async/async2-varying-yields.cs
index 317e23d3bc4a3c..16744385a5a553 100644
--- a/src/tests/Loader/async/async2-varying-yields.cs
+++ b/src/tests/Loader/async/async2-varying-yields.cs
@@ -24,19 +24,17 @@ public static void TestEntryPoint()
 
     public static async Task AsyncEntry()
     {
-        string[] args = Environment.GetCommandLineArgs();
-        if (args.Length < 3)
-        {
-            Console.WriteLine("Use: corerun.exe <path to varying-yields.dll> <depth> <suspend frequency> [--use-direct-yield]");
-            return;
-        }
-
         if (!GCSettings.IsServerGC)
             Console.WriteLine("*** Warning: Server GC is disabled, set DOTNET_gcServer=1 ***");
 
-        int depth = int.Parse(args[1]);
-        // Yield on average every X calls.
-        int yieldFrequency = int.Parse(args[2]);
+        string[] args = Environment.GetCommandLineArgs();
+        int depth = args.Length > 1 ? int.Parse(args[1]) : 0;
+        Console.WriteLine("Using depth = {0}", depth);
+
+        // Yield on average every X iterations.
+        int yieldFrequency = args.Length > 2 ? int.Parse(args[2]) : 1;
+        Console.WriteLine("Using yield frequency = {0}", yieldFrequency);
+
         Benchmark warmupBm = new Benchmark(0.0001);
         warmupBm.Warmup = true;
         for (int i = 0; i < 4; i++)
diff --git a/src/tests/Loader/async/simple-eh.cs b/src/tests/Loader/async/simple-eh.cs
index 49c1312f9b3458..06a72222bae2f9 100644
--- a/src/tests/Loader/async/simple-eh.cs
+++ b/src/tests/Loader/async/simple-eh.cs
@@ -32,7 +32,6 @@ public static async2 int Handler()
         }
         catch (IntegerException ex)
         {
-            System.Console.WriteLine(ex);
             return ex.Value;
         }
     }

From 1edab00cc69a0933697546c83474c6dcddf06a2f Mon Sep 17 00:00:00 2001
From: Jakob Botsch Nielsen <janielsen@microsoft.com>
Date: Mon, 6 Nov 2023 19:43:48 +0000
Subject: [PATCH 064/203] Merged PR 35049: Call async2 entry from thunks with
 direct "call" IL instruction

Call async2 entry from thunks with direct "call" IL instruction

Ensures we get proper generic lookup handling in the future when we
start supporting generics.

Currently requires that we disable inlining in the thunk due to the use
of the async continuation. It would be possible to allow inlining into
the thunk with more work.
---
 src/coreclr/jit/compiler.cpp      |  3 ++-
 src/coreclr/jit/compiler.h        | 13 +++++++------
 src/coreclr/jit/fginline.cpp      |  8 ++++++++
 src/coreclr/jit/importercalls.cpp |  5 +++--
 src/coreclr/jit/inline.def        |  1 +
 src/coreclr/vm/prestub.cpp        | 15 ++++-----------
 6 files changed, 25 insertions(+), 20 deletions(-)

diff --git a/src/coreclr/jit/compiler.cpp b/src/coreclr/jit/compiler.cpp
index 771f5531fb773a..90018def42f422 100644
--- a/src/coreclr/jit/compiler.cpp
+++ b/src/coreclr/jit/compiler.cpp
@@ -6827,7 +6827,8 @@ int Compiler::compCompileHelper(CORINFO_MODULE_HANDLE classPtr,
 
     info.compPublishStubParam = opts.jitFlags->IsSet(JitFlags::JIT_FLAG_PUBLISH_SECRET_PARAM);
 
-    info.compHasNextCallRetAddr = false;
+    info.compHasNextCallRetAddr    = false;
+    info.compUsesAsyncContinuation = false;
 
     if (opts.IsReversePInvoke())
     {
diff --git a/src/coreclr/jit/compiler.h b/src/coreclr/jit/compiler.h
index 295537ee9a8d33..93cc9f401f0bbc 100644
--- a/src/coreclr/jit/compiler.h
+++ b/src/coreclr/jit/compiler.h
@@ -10087,12 +10087,13 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
         // (2) the code is hot/cold split, and we issued less code than we expected
         // in the cold section (the hot section will always be padded out to compTotalHotCodeSize).
 
-        bool compIsStatic : 1;           // Is the method static (no 'this' pointer)?
-        bool compIsVarArgs : 1;          // Does the method have varargs parameters?
-        bool compInitMem : 1;            // Is the CORINFO_OPT_INIT_LOCALS bit set in the method info options?
-        bool compProfilerCallback : 1;   // JIT inserted a profiler Enter callback
-        bool compPublishStubParam : 1;   // EAX captured in prolog will be available through an intrinsic
-        bool compHasNextCallRetAddr : 1; // The NextCallReturnAddress intrinsic is used.
+        bool compIsStatic : 1;              // Is the method static (no 'this' pointer)?
+        bool compIsVarArgs : 1;             // Does the method have varargs parameters?
+        bool compInitMem : 1;               // Is the CORINFO_OPT_INIT_LOCALS bit set in the method info options?
+        bool compProfilerCallback : 1;      // JIT inserted a profiler Enter callback
+        bool compPublishStubParam : 1;      // EAX captured in prolog will be available through an intrinsic
+        bool compHasNextCallRetAddr : 1;    // The NextCallReturnAddress intrinsic is used.
+        bool compUsesAsyncContinuation : 1; // The Async2CallContinuation intrinsic is used.
 
         var_types compRetType;       // Return type of the method as declared in IL (including SIMD normalization)
         var_types compRetNativeType; // Normalized return type as per target arch ABI
diff --git a/src/coreclr/jit/fginline.cpp b/src/coreclr/jit/fginline.cpp
index 7c40d037d8a99e..2f98cfb4ee515f 100644
--- a/src/coreclr/jit/fginline.cpp
+++ b/src/coreclr/jit/fginline.cpp
@@ -1005,6 +1005,14 @@ void Compiler::fgMorphCallInlineHelper(GenTreeCall* call, InlineResult* result,
         return;
     }
 
+    if (call->gtIsAsyncCall && info.compUsesAsyncContinuation)
+    {
+        // Currently not supported. Could provide a nice perf benefit for
+        // async1 -> async2 thunks if we supported it.
+        result->NoteFatal(InlineObservation::CALLER_ASYNC2_USED_CONTINUATION);
+        return;
+    }
+
     // impMarkInlineCandidate() is expected not to mark tail prefixed calls
     // and recursive tail calls as inline candidates.
     noway_assert(!call->IsTailPrefixedCall());
diff --git a/src/coreclr/jit/importercalls.cpp b/src/coreclr/jit/importercalls.cpp
index 81eccd3c2c71a0..1e8a014d4fc989 100644
--- a/src/coreclr/jit/importercalls.cpp
+++ b/src/coreclr/jit/importercalls.cpp
@@ -903,7 +903,7 @@ var_types Compiler::impImportCall(OPCODE                  opcode,
                                                            .WellKnown(WellKnownArg::VarArgsCookie));
             }
 
-            if (call->AsCall()->gtIsAsyncCall && compIsAsync2StateMachine())
+            if (sig->isAsyncCall() && (JitConfig.RuntimeAsyncViaJitGeneratedStateMachines() != 0))
             {
                 call->AsCall()->gtArgs.PushFront(this, NewCallArg::Primitive(gtNewNull(), TYP_REF)
                                                            .WellKnown(WellKnownArg::AsyncContinuation));
@@ -923,7 +923,7 @@ var_types Compiler::impImportCall(OPCODE                  opcode,
                                                 NewCallArg::Primitive(instParam).WellKnown(WellKnownArg::InstParam));
             }
 
-            if (call->AsCall()->gtIsAsyncCall && compIsAsync2StateMachine())
+            if (sig->isAsyncCall() && (JitConfig.RuntimeAsyncViaJitGeneratedStateMachines() != 0))
             {
                 call->AsCall()->gtArgs.PushBack(this, NewCallArg::Primitive(gtNewNull(), TYP_REF)
                                                           .WellKnown(WellKnownArg::AsyncContinuation));
@@ -2582,6 +2582,7 @@ GenTree* Compiler::impIntrinsic(GenTree*                newobjThis,
         GenTree* node = new (this, GT_ASYNC_CONTINUATION) GenTree(GT_ASYNC_CONTINUATION, TYP_REF);
         node->SetHasOrderingSideEffect();
         node->gtFlags |= GTF_CALL | GTF_GLOB_REF;
+        info.compUsesAsyncContinuation = true;
         return node;
     }
 
diff --git a/src/coreclr/jit/inline.def b/src/coreclr/jit/inline.def
index 9371e655bde1ee..c0962e4454ed93 100644
--- a/src/coreclr/jit/inline.def
+++ b/src/coreclr/jit/inline.def
@@ -113,6 +113,7 @@ INLINE_OBSERVATION(UNSUPPORTED_OPCODE,        bool,   "unsupported opcode",
 INLINE_OBSERVATION(DEBUG_CODEGEN,             bool,   "debug codegen",                        FATAL,       CALLER)
 INLINE_OBSERVATION(IS_JIT_NOINLINE,           bool,   "noinline per JitNoInlineRange",        FATAL,       CALLER)
 INLINE_OBSERVATION(USES_NEXT_CALL_RET_ADDR,   bool,   "uses NextCallReturnAddress intrinsic", FATAL,       CALLER)
+INLINE_OBSERVATION(ASYNC2_USED_CONTINUATION,  bool,   "uses Async2CallContinuation intrinsic",FATAL,       CALLER)
 
 // ------ Caller Information -------
 
diff --git a/src/coreclr/vm/prestub.cpp b/src/coreclr/vm/prestub.cpp
index f9d607c4e26442..1e61d78c318649 100644
--- a/src/coreclr/vm/prestub.cpp
+++ b/src/coreclr/vm/prestub.cpp
@@ -1760,24 +1760,17 @@ void MethodDesc::EmitJitStateMachineBasedRuntimeAsyncThunk(MethodDesc* pAsyncOth
             DWORD localArg = 0;
             if (asyncMsig.HasThis())
             {
+                // Struct async thunks not yet implemented
+                _ASSERTE(!this->GetMethodTable()->IsValueType());
                 pCode->EmitLDARG(localArg++);
             }
-            if ((asyncMsig.GetCallingConventionInfo() & CORINFO_CALLCONV_PARAMTYPE) != 0)
-            {
-                _ASSERTE(!"Cannot handle generic context");
-            }
-            _ASSERTE((asyncMsig.GetCallingConventionInfo() & CORINFO_CALLCONV_ASYNCCALL) != 0);
-
-            pCode->EmitLDNULL(); // Async continuation; not resuming
-
             for (UINT iArg = 0; iArg < asyncMsig.NumFixedArgs(); iArg++)
             {
                 pCode->EmitLDARG(localArg++);
             }
 
-            // TODO: This and instantiating stubs can directly load the target from the precode slot
-            pCode->EmitLDC(pAsyncOtherVariant->GetMultiCallableAddrOfCode(CORINFO_ACCESS_ANY));
-            pCode->EmitCALLI(pCode->GetSigToken(pTargetSig, cbTargetSig), localArg + 2, logicalResultLocal != UINT_MAX ? 1 : 0);
+            pCode->EmitCALL(pCode->GetToken(pAsyncOtherVariant), localArg, logicalResultLocal != UINT_MAX ? 1 : 0);
+
             if (logicalResultLocal != UINT_MAX)
                 pCode->EmitSTLOC(logicalResultLocal);
             pCode->EmitCALL(METHOD__STUBHELPERS__ASYNC2_CALL_CONTINUATION, 0, 1);

From 848d73483672fa757e647cce78e8770ea0e5b443 Mon Sep 17 00:00:00 2001
From: Vladimir Sadov <vsadov@microsoft.com>
Date: Mon, 6 Nov 2023 19:44:32 +0000
Subject: [PATCH 065/203] Merged PR 35055: initial compiler support for
 ValueTask

- bump up the Roslyn reference to the version that supports ValueTask
- add `System.Runtime.CompilerServices.ValueTaskAsyncAttribute`  - we will use it as a marker for ValueTask-returning async2 methods.
This may not be the best syntax, just enough to unblock the work on VM side.
- add a test
test fails with assert in the VM
---
 buildroslynnugets.cmd                         |  4 +--
 eng/Versions.props                            |  6 ++--
 .../CompatibilitySuppressions.xml             |  8 +++++-
 .../src/CompatibilitySuppressions.xml         |  8 +++++-
 .../System.Private.CoreLib.Shared.projitems   |  3 +-
 .../ValueTaskAsyncAttribute.cs                | 18 ++++++++++++
 .../System.Runtime/ref/System.Runtime.cs      |  5 ++++
 src/tests/Loader/async/async2object.cs        |  2 +-
 src/tests/Loader/async/async2valuetask.cs     | 28 +++++++++++++++++++
 src/tests/Loader/async/async2valuetask.csproj |  8 ++++++
 10 files changed, 81 insertions(+), 9 deletions(-)
 create mode 100644 src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/ValueTaskAsyncAttribute.cs
 create mode 100644 src/tests/Loader/async/async2valuetask.cs
 create mode 100644 src/tests/Loader/async/async2valuetask.csproj

diff --git a/buildroslynnugets.cmd b/buildroslynnugets.cmd
index 38f6c7d94e0c98..9a6e68c96776b3 100644
--- a/buildroslynnugets.cmd
+++ b/buildroslynnugets.cmd
@@ -1,14 +1,14 @@
 setlocal ENABLEEXTENSIONS
 pushd %~dp0
 
-set ASYNC_SUFFIX=async-5
+set ASYNC_SUFFIX=async-6
 
 cd ..
 pushd dotnet-roslyn
 
 git fetch AzDo dev/vsadov/a2
 rem when updating this, make sure to update the ASYNC_SUFFIX above and the versions.props file
-git checkout 1d2dccc8ad7d4169112c2f93796a418a1cd9d930
+git checkout facd968bc0e0c2889c78d4f121c5d1d39c3d6566
 
 call restore.cmd
 call build.cmd -c release
diff --git a/eng/Versions.props b/eng/Versions.props
index 6f8d5b7ff7aab1..21897be218e3ca 100644
--- a/eng/Versions.props
+++ b/eng/Versions.props
@@ -39,9 +39,9 @@
       Any tools that contribute to the design-time experience should use the MicrosoftCodeAnalysisVersion_LatestVS property above to ensure
       they do not break the local dev experience.
     -->
-    <MicrosoftCodeAnalysisCSharpVersion>4.9.0-async-5</MicrosoftCodeAnalysisCSharpVersion>
-    <MicrosoftCodeAnalysisVersion>4.9.0-async-5</MicrosoftCodeAnalysisVersion>
-    <MicrosoftNetCompilersToolsetVersion>4.9.0-async-5</MicrosoftNetCompilersToolsetVersion>
+    <MicrosoftCodeAnalysisCSharpVersion>4.9.0-async-6</MicrosoftCodeAnalysisCSharpVersion>
+    <MicrosoftCodeAnalysisVersion>4.9.0-async-6</MicrosoftCodeAnalysisVersion>
+    <MicrosoftNetCompilersToolsetVersion>4.9.0-async-6</MicrosoftNetCompilersToolsetVersion>
   </PropertyGroup>
   <!--
     For source generator support we need to target multiple versions of Roslyn in order to be able to run on older versions of Roslyn.
diff --git a/src/coreclr/System.Private.CoreLib/CompatibilitySuppressions.xml b/src/coreclr/System.Private.CoreLib/CompatibilitySuppressions.xml
index 97b65a2748409e..8ade8e2a551216 100644
--- a/src/coreclr/System.Private.CoreLib/CompatibilitySuppressions.xml
+++ b/src/coreclr/System.Private.CoreLib/CompatibilitySuppressions.xml
@@ -1,6 +1,12 @@
 ﻿<?xml version="1.0" encoding="utf-8"?>
 <!-- https://learn.microsoft.com/en-us/dotnet/fundamentals/package-validation/diagnostic-ids -->
 <Suppressions xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
+  <Suppression>
+    <DiagnosticId>CP0009</DiagnosticId>
+    <Target>T:System.Runtime.CompilerServices.ValueTaskAsyncAttribute</Target>
+    <Left>ref/net9.0/System.Private.CoreLib.dll</Left>
+    <Right>lib/net9.0/System.Private.CoreLib.dll</Right>
+  </Suppression>
   <Suppression>
     <DiagnosticId>CP0015</DiagnosticId>
     <Target>M:System.Diagnostics.Tracing.EventSource.Write``1(System.String,``0):[T:System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute]</Target>
@@ -29,4 +35,4 @@
     <DiagnosticId>CP0015</DiagnosticId>
     <Target>M:System.Diagnostics.Tracing.EventSource.WriteEventWithRelatedActivityIdCore(System.Int32,System.Guid*,System.Int32,System.Diagnostics.Tracing.EventSource.EventData*):[T:System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute]</Target>
   </Suppression>
-</Suppressions>
\ No newline at end of file
+</Suppressions>
diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/CompatibilitySuppressions.xml b/src/coreclr/nativeaot/System.Private.CoreLib/src/CompatibilitySuppressions.xml
index 9c7bffdba74162..88c94b8826a69b 100644
--- a/src/coreclr/nativeaot/System.Private.CoreLib/src/CompatibilitySuppressions.xml
+++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/CompatibilitySuppressions.xml
@@ -973,6 +973,12 @@
     <Left>ref/net9.0/System.Private.CoreLib.dll</Left>
     <Right>lib/net9.0/System.Private.CoreLib.dll</Right>
   </Suppression>
+  <Suppression>
+    <DiagnosticId>CP0009</DiagnosticId>
+    <Target>T:System.Runtime.CompilerServices.ValueTaskAsyncAttribute</Target>
+    <Left>ref/net9.0/System.Private.CoreLib.dll</Left>
+    <Right>lib/net9.0/System.Private.CoreLib.dll</Right>
+  </Suppression>
   <Suppression>
     <DiagnosticId>CP0014</DiagnosticId>
     <Target>M:System.Runtime.CompilerServices.RuntimeHelpers.GetObjectValue(System.Object)-&gt;object?:[T:System.Diagnostics.CodeAnalysis.NotNullIfNotNullAttribute]</Target>
@@ -1005,4 +1011,4 @@
     <DiagnosticId>CP0015</DiagnosticId>
     <Target>M:System.Diagnostics.Tracing.EventSource.WriteEventWithRelatedActivityIdCore(System.Int32,System.Guid*,System.Int32,System.Diagnostics.Tracing.EventSource.EventData*):[T:System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute]</Target>
   </Suppression>
-</Suppressions>
+</Suppressions>
\ No newline at end of file
diff --git a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems
index ac73beb0c03e3c..d8c11d553d55a2 100644
--- a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems
+++ b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems
@@ -890,6 +890,7 @@
     <Compile Include="$(MSBuildThisFileDirectory)System\Runtime\CompilerServices\UnsafeAccessorAttribute.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\Runtime\CompilerServices\UnsafeValueTypeAttribute.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\Runtime\CompilerServices\ValueTaskAwaiter.cs" />
+    <Compile Include="$(MSBuildThisFileDirectory)System\Runtime\CompilerServices\ValueTaskAsyncAttribute.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\Runtime\CompilerServices\YieldAwaitable.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\Runtime\ConstrainedExecution\Cer.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\Runtime\ConstrainedExecution\Consistency.cs" />
@@ -2721,4 +2722,4 @@
     <Compile Include="$(MSBuildThisFileDirectory)System\Numerics\IUnaryPlusOperators.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\Numerics\IUnsignedNumber.cs" />
   </ItemGroup>
-</Project>
\ No newline at end of file
+</Project>
diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/ValueTaskAsyncAttribute.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/ValueTaskAsyncAttribute.cs
new file mode 100644
index 00000000000000..bfe750bf836f7a
--- /dev/null
+++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/ValueTaskAsyncAttribute.cs
@@ -0,0 +1,18 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+namespace System.Runtime.CompilerServices
+{
+    /// <summary>
+    /// Specifies that the async2 function in its promise-returning form should be returning ValueTask/ValueTask`1
+    ///
+    /// The attribute is source-only and is not supposed to be emitted into metadata, where it has no meaning.
+    /// TODO: (vsadov) I have a lot of doubts that this is the best possible design to support ValueTask in async2.
+    ///       While good enough to unblock working with ValueTask async2, we should think more about the syntax.
+    /// </summary>
+    [AttributeUsage(AttributeTargets.Method)]
+    public class ValueTaskAsyncAttribute : Attribute
+    {
+        public ValueTaskAsyncAttribute() { }
+    }
+}
diff --git a/src/libraries/System.Runtime/ref/System.Runtime.cs b/src/libraries/System.Runtime/ref/System.Runtime.cs
index 72ef20958c50aa..54bd77d4fe502a 100644
--- a/src/libraries/System.Runtime/ref/System.Runtime.cs
+++ b/src/libraries/System.Runtime/ref/System.Runtime.cs
@@ -13299,6 +13299,11 @@ public sealed partial class UnsafeValueTypeAttribute : System.Attribute
     {
         public UnsafeValueTypeAttribute() { }
     }
+    [AttributeUsage(AttributeTargets.Method)]
+    public sealed partial class ValueTaskAsyncAttribute : Attribute
+    {
+        public ValueTaskAsyncAttribute() { }
+    }
     public readonly partial struct ValueTaskAwaiter : System.Runtime.CompilerServices.ICriticalNotifyCompletion, System.Runtime.CompilerServices.INotifyCompletion
     {
         private readonly object _dummy;
diff --git a/src/tests/Loader/async/async2object.cs b/src/tests/Loader/async/async2object.cs
index 81fc0119c6a775..aa7381cf074f3c 100644
--- a/src/tests/Loader/async/async2object.cs
+++ b/src/tests/Loader/async/async2object.cs
@@ -6,7 +6,7 @@
 using System.Threading.Tasks;
 using Xunit;
 
-public class Async2Void
+public class Async2Object
 {
     [Fact]
     public static int TestEntryPoint()
diff --git a/src/tests/Loader/async/async2valuetask.cs b/src/tests/Loader/async/async2valuetask.cs
new file mode 100644
index 00000000000000..53d0792da245b6
--- /dev/null
+++ b/src/tests/Loader/async/async2valuetask.cs
@@ -0,0 +1,28 @@
+// 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 System.Runtime.CompilerServices;
+using System.Threading.Tasks;
+using Xunit;
+
+public class Async2valuetask
+{
+    [Fact]
+    public static int TestEntryPoint()
+    {
+        return (int)AsyncTestEntryPoint(100).Result;
+    }
+
+    private static ValueTask<int> AsyncTestEntryPoint(int arg)
+    {
+        return M1(arg);
+    }
+
+    [ValueTaskAsyncAttribute]
+    private static async2 int M1(int arg)
+    {
+        await Task.Yield();
+        return arg;
+    }
+}
diff --git a/src/tests/Loader/async/async2valuetask.csproj b/src/tests/Loader/async/async2valuetask.csproj
new file mode 100644
index 00000000000000..0f29ed860df43e
--- /dev/null
+++ b/src/tests/Loader/async/async2valuetask.csproj
@@ -0,0 +1,8 @@
+<Project Sdk="Microsoft.NET.Sdk.IL">
+  <PropertyGroup>
+    <RequiresProcessIsolation>true</RequiresProcessIsolation>
+  </PropertyGroup>
+  <ItemGroup>
+   <Compile Include="$(MSBuildProjectName).cs" />
+  </ItemGroup>
+</Project>

From 9dce32d44b94b6236bd0696957639bfbcbb6d6bd Mon Sep 17 00:00:00 2001
From: David Wrighton <davidwr@microsoft.com>
Date: Mon, 6 Nov 2023 21:46:57 +0000
Subject: [PATCH 066/203] Merged PR 35075: Fix ValueTask handling for async2

Fix ValueTask handling for async2

When enabling this a week or so ago, I missed the detail that ValueTask is a struct in 1 case.  Now that @<Vladimir Sadov> has added support for testing this at the language level, the issue was easy to fix
---
 src/coreclr/vm/methodtablebuilder.cpp | 10 ++++------
 1 file changed, 4 insertions(+), 6 deletions(-)

diff --git a/src/coreclr/vm/methodtablebuilder.cpp b/src/coreclr/vm/methodtablebuilder.cpp
index 5fe2e00302e69a..6dcd8042459239 100644
--- a/src/coreclr/vm/methodtablebuilder.cpp
+++ b/src/coreclr/vm/methodtablebuilder.cpp
@@ -2874,17 +2874,14 @@ AsyncTaskMethod ClassifyAsyncMethodCore(SigPointer sig, Module* pModule, PCCOR_S
     if (elemType == ELEMENT_TYPE_GENERICINST)
     {
         IfFailThrow(sig.GetElemType(&elemType));
-        if (elemType == ELEMENT_TYPE_VALUETYPE)
-        {
-            return AsyncTaskMethod::NormalMethod; // Task<T> is a class, so we can't be that if we get here
-        }
+        *pIsValueTask = (elemType == ELEMENT_TYPE_VALUETYPE);
         IfFailThrow(sig.GetToken(&tk));
         IfFailThrow(sig.GetData(&data));
         if (data == 1)
         {
             // This might be System.Threading.Tasks.Task`1
             GetNameOfTypeDefOrRef(pModule, tk, &name, &_namespace);
-            if (((strcmp(name, "Task`1") == 0) || (strcmp(name, "ValueTask`1") == 0)) && strcmp(_namespace, "System.Threading.Tasks") == 0)
+            if ((strcmp(name, *pIsValueTask ? "ValueTask`1" : "Task`1") == 0) && strcmp(_namespace, "System.Threading.Tasks") == 0)
             {
                 *pIsValueTask = name[0] == 'V';
                 if (IsTypeDefOrRefImplementedInSystemModule(pModule, tk))
@@ -2895,9 +2892,10 @@ AsyncTaskMethod ClassifyAsyncMethodCore(SigPointer sig, Module* pModule, PCCOR_S
     else if ((elemType == ELEMENT_TYPE_CLASS) || (elemType == ELEMENT_TYPE_VALUETYPE))
     {
         IfFailThrow(sig.GetToken(&tk));
+        *pIsValueTask = (elemType == ELEMENT_TYPE_VALUETYPE);
         // This might be System.Threading.Tasks.Task or ValueTask
         GetNameOfTypeDefOrRef(pModule, tk, &name, &_namespace);
-        if (((strcmp(name, "Task") == 0) || (strcmp(name, "ValueTask") == 0)) && strcmp(_namespace, "System.Threading.Tasks") == 0)
+        if ((strcmp(name, *pIsValueTask ? "ValueTask`1" : "Task`1") == 0) && strcmp(_namespace, "System.Threading.Tasks") == 0)
         {
             *pIsValueTask = name[0] == 'V';
             if (IsTypeDefOrRefImplementedInSystemModule(pModule, tk))

From 00da10fc01920bd6ec836e297411a9b5b6017bc3 Mon Sep 17 00:00:00 2001
From: Jakob Botsch Nielsen <jakob.botsch.nielsen@gmail.com>
Date: Tue, 7 Nov 2023 13:16:45 +0100
Subject: [PATCH 067/203] Fix incorrect resume BB insertion

In the EH case, we were splitting up the resumption BB and its successor
by inserting new resumption BBs.

Also add a bunch of logging.
---
 src/coreclr/jit/async.cpp | 57 ++++++++++++++++++++++++++++-----------
 src/coreclr/jit/async.h   |  1 +
 2 files changed, 42 insertions(+), 16 deletions(-)

diff --git a/src/coreclr/jit/async.cpp b/src/coreclr/jit/async.cpp
index 75618def1538e8..3ea354603a9f84 100644
--- a/src/coreclr/jit/async.cpp
+++ b/src/coreclr/jit/async.cpp
@@ -272,7 +272,7 @@ void Async2Transformation::Transform(
 #ifdef DEBUG
     if (m_comp->verbose)
     {
-        printf("Processing call [%06u]\n", Compiler::dspTreeID(call));
+        printf("Processing call [%06u] in " FMT_BB "\n", Compiler::dspTreeID(call), block->bbNum);
         printf("  %zu live LIR edges\n", defs.size());
 
         if (defs.size() > 0)
@@ -397,6 +397,7 @@ void Async2Transformation::Transform(
     // (-1 in the tier0 version):
     if (m_comp->doesMethodHavePatchpoints() || m_comp->opts.IsOSR())
     {
+        JITDUMP("  Method %s; keeping an IL offset at the beginning of non-GC data\n", m_comp->doesMethodHavePatchpoints() ? "has patchpoints" : "is an OSR method");
         dataSize += sizeof(int);
     }
 
@@ -415,6 +416,8 @@ void Async2Transformation::Transform(
         returnInGCData = varTypeIsGC(call->gtReturnType);
     }
 
+    JITDUMP("  Will store return of type %s, size %u in %sGC data\n", call->gtReturnType == TYP_STRUCT ? returnStructLayout->GetClassName() : varTypeName(call->gtReturnType), returnSize, returnInGCData ? "" : "non-");
+
     assert((returnSize > 0) == (call->gtReturnType != TYP_VOID));
 
     // The return value is always stored:
@@ -431,11 +434,16 @@ void Async2Transformation::Transform(
     {
         returnValDataOffset = dataSize;
         dataSize += returnSize;
+
+        JITDUMP("  at offset %u\n", returnValDataOffset);
     }
 
     unsigned exceptionGCDataIndex = UINT_MAX;
     if (block->hasTryIndex())
+    {
         exceptionGCDataIndex = gcRefsCount++;
+        JITDUMP("  " FMT_BB " is in try region %u; exception will be at GC@+%02u in GC data\n", block->bbNum, exceptionGCDataIndex);
+    }
 
     for (LiveLocalInfo& inf : m_liveLocals)
     {
@@ -518,6 +526,8 @@ void Async2Transformation::Transform(
     BasicBlock* remainder = m_comp->fgSplitBlockAfterNode(block, jtrue);
     *pRemainder           = remainder;
 
+    JITDUMP("  Remainder is " FMT_BB "\n", remainder->bbNum);
+
     assert(block->KindIs(BBJ_NONE) && block->NextIs(remainder));
 
     if (m_lastSuspensionBB == nullptr)
@@ -531,6 +541,8 @@ void Async2Transformation::Transform(
     retBB->clearHndIndex();
     m_lastSuspensionBB = retBB;
 
+    JITDUMP("  Created suspension " FMT_BB " for state %u\n", retBB->bbNum, stateNum);
+
     block->SetJumpKindAndTarget(BBJ_COND, retBB DEBUGARG(m_comp));
 
     m_comp->fgAddRefPred(retBB, block);
@@ -733,23 +745,19 @@ void Async2Transformation::Transform(
     GenTree* ret    = m_comp->gtNewOperNode(GT_RETURN_SUSPEND, TYP_VOID, newContinuation);
     LIR::AsRange(retBB).InsertAtEnd(newContinuation, ret);
 
-    JITDUMP("  Created suspension BB " FMT_BB "\n", retBB->bbNum);
-
-    BasicBlock* prevResumptionBB;
-    if (m_resumptionBBs.size() == 0)
-    {
-        prevResumptionBB = m_comp->fgLastBBInMainFunction();
-    }
-    else
+    if (m_lastResumptionBB == nullptr)
     {
-        prevResumptionBB = m_resumptionBBs[m_resumptionBBs.size() - 1];
+        m_lastResumptionBB = m_comp->fgLastBBInMainFunction();
     }
 
-    BasicBlock* resumeBB = m_comp->fgNewBBafter(BBJ_ALWAYS, prevResumptionBB, true, remainder);
+    BasicBlock* resumeBB = m_comp->fgNewBBafter(BBJ_ALWAYS, m_lastResumptionBB, true, remainder);
     resumeBB->bbSetRunRarely();
     resumeBB->clearTryIndex();
     resumeBB->clearHndIndex();
     resumeBB->bbFlags |= BBF_ASYNC_RESUMPTION;
+    m_lastResumptionBB = resumeBB;
+
+    JITDUMP("  Created resumption " FMT_BB " for state %u\n", resumeBB->bbNum, stateNum);
 
     m_comp->fgAddRefPred(remainder, resumeBB);
 
@@ -876,16 +884,23 @@ void Async2Transformation::Transform(
         BasicBlock* storeResultBB = resumeBB;
         if (exceptionGCDataIndex != UINT_MAX)
         {
+            JITDUMP("  We need to rethrow an exception\n");
             BasicBlock* rethrowExceptionBB = m_comp->fgNewBBinRegion(BBJ_THROW, block, /* jumpDest */ nullptr,
                                                                      /* runRarely */ true, /* insertAtEnd */ true);
+            JITDUMP("  Created " FMT_BB " to rethrow exception on resumption\n", rethrowExceptionBB->bbNum);
             resumeBB->SetJumpKindAndTarget(BBJ_COND, rethrowExceptionBB DEBUGARG(m_comp));
             m_comp->fgAddRefPred(rethrowExceptionBB, resumeBB);
             m_comp->fgRemoveRefPred(remainder, resumeBB);
 
+            JITDUMP("  Resumption " FMT_BB " becomes BBJ_COND to check for non-null exception\n", resumeBB->bbNum);
+
             storeResultBB = m_comp->fgNewBBafter(BBJ_ALWAYS, resumeBB, true, remainder);
+            JITDUMP("  Created " FMT_BB " to store result when resuming with no exception\n", storeResultBB->bbNum);
             m_comp->fgAddRefPred(remainder, storeResultBB);
             m_comp->fgAddRefPred(storeResultBB, resumeBB);
 
+            m_lastResumptionBB = storeResultBB;
+
             // Check if we have an exception.
             unsigned exceptionLclNum = GetExceptionVar();
             GenTree* objectArr       = m_comp->gtNewLclvNode(resumeObjectArrLclNum, TYP_REF);
@@ -909,7 +924,7 @@ void Async2Transformation::Transform(
             LIR::AsRange(rethrowExceptionBB).InsertAtEnd(LIR::SeqTree(m_comp, rethrowException));
 
             storeResultBB->bbFlags |= BBF_ASYNC_RESUMPTION;
-            JITDUMP("Added " FMT_BB " to rethrow exception at suspension point\n", rethrowExceptionBB->bbNum);
+            JITDUMP("  Added " FMT_BB " to rethrow exception at suspension point\n", rethrowExceptionBB->bbNum);
         }
     }
 
@@ -1022,7 +1037,6 @@ void Async2Transformation::Transform(
     }
 
     m_resumptionBBs.push_back(resumeBB);
-    JITDUMP("  Created resumption BB " FMT_BB "\n", resumeBB->bbNum);
 }
 
 GenTreeIndir* Async2Transformation::LoadFromOffset(GenTree*     base,
@@ -1178,7 +1192,6 @@ void Async2Transformation::LiftLIREdges(BasicBlock*                    block,
 void Async2Transformation::CreateResumptionSwitch()
 {
     BasicBlock* newEntryBB = m_comp->bbNewBasicBlock(BBJ_NONE);
-    JITDUMP("New entry BB: " FMT_BB "\n", newEntryBB->bbNum);
 
     if (m_comp->fgFirstBB->hasProfileWeight())
     {
@@ -1189,6 +1202,8 @@ void Async2Transformation::CreateResumptionSwitch()
     FlowEdge* edge = m_comp->fgAddRefPred(m_comp->fgFirstBB, newEntryBB);
     edge->setLikelihood(1);
 
+    JITDUMP("  Inserting new entry " FMT_BB " before old entry " FMT_BB "\n", newEntryBB->bbNum, m_comp->fgFirstBB->bbNum);
+
     m_comp->fgInsertBBbefore(m_comp->fgFirstBB, newEntryBB);
 
     // If previous first BB was a scratch BB, then we must add a new scratch BB
@@ -1203,6 +1218,7 @@ void Async2Transformation::CreateResumptionSwitch()
 
     if (m_resumptionBBs.size() == 1)
     {
+        JITDUMP("  Redirecting entry " FMT_BB " directly to " FMT_BB " as it is the only resumption block\n", newEntryBB->bbNum, m_resumptionBBs[0]->bbNum);
         newEntryBB->SetJumpKindAndTarget(BBJ_COND, m_resumptionBBs[0] DEBUGARG(m_comp));
         m_comp->fgAddRefPred(m_resumptionBBs[0], newEntryBB);
     }
@@ -1212,7 +1228,7 @@ void Async2Transformation::CreateResumptionSwitch()
         condBB->bbSetRunRarely();
         newEntryBB->SetJumpKindAndTarget(BBJ_COND, condBB DEBUGARG(m_comp));
 
-        JITDUMP("New cond BB: " FMT_BB "\n", condBB->bbNum);
+        JITDUMP("  Redirecting entry " FMT_BB " to BBJ_COND " FMT_BB " for resumption with 2 states\n", newEntryBB->bbNum, condBB->bbNum);
 
         m_comp->fgAddRefPred(condBB, newEntryBB);
         m_comp->fgAddRefPred(m_resumptionBBs[0], condBB);
@@ -1236,7 +1252,7 @@ void Async2Transformation::CreateResumptionSwitch()
         switchBB->bbSetRunRarely();
         newEntryBB->SetJumpKindAndTarget(BBJ_COND, switchBB DEBUGARG(m_comp));
 
-        JITDUMP("New switch BB: " FMT_BB "\n", switchBB->bbNum);
+        JITDUMP("  Redirecting entry " FMT_BB " to BBJ_SWITCH " FMT_BB " for resumption with %zu states\n", newEntryBB->bbNum, switchBB->bbNum, m_resumptionBBs.size());
 
         m_comp->fgAddRefPred(switchBB, newEntryBB);
 
@@ -1268,13 +1284,19 @@ void Async2Transformation::CreateResumptionSwitch()
 
     if (m_comp->doesMethodHavePatchpoints())
     {
+        JITDUMP("  Method has patch points...\n");
         // If we have patchpoints then first check if we need to resume in the OSR version.
         BasicBlock* callHelperBB = m_comp->fgNewBBafter(BBJ_THROW, m_comp->fgLastBBInMainFunction(), false);
         callHelperBB->bbSetRunRarely();
         callHelperBB->clearTryIndex();
         callHelperBB->clearHndIndex();
 
+        JITDUMP("    Created " FMT_BB " for transitions back into OSR method\n", callHelperBB->bbNum);
+
         BasicBlock* checkILOffsetBB = m_comp->fgNewBBbefore(BBJ_COND, newEntryBB->GetJumpDest(), true, callHelperBB);
+
+        JITDUMP("    Created " FMT_BB " to check whether we should transition immediately to OSR\n", checkILOffsetBB->bbNum);
+
         m_comp->fgRemoveRefPred(newEntryBB->GetJumpDest(), newEntryBB);
         newEntryBB->SetJumpDest(checkILOffsetBB);
 
@@ -1310,6 +1332,7 @@ void Async2Transformation::CreateResumptionSwitch()
     }
     else if (m_comp->opts.IsOSR())
     {
+        JITDUMP("  Method is an OSR function\n");
         // If the tier-0 version resumed and then transitioned to the OSR
         // version by normal means then we will see a non-zero continuation
         // here that belongs to the tier0 method. In that case we should just
@@ -1319,6 +1342,8 @@ void Async2Transformation::CreateResumptionSwitch()
         m_comp->fgRemoveRefPred(newEntryBB->GetJumpDest(), newEntryBB);
         newEntryBB->SetJumpDest(checkILOffsetBB);
 
+        JITDUMP("    Created " FMT_BB " to check for Tier-0 continuations\n", checkILOffsetBB->bbNum);
+
         m_comp->fgAddRefPred(checkILOffsetBB, newEntryBB);
         m_comp->fgAddRefPred(checkILOffsetBB->Next(), checkILOffsetBB);
         m_comp->fgAddRefPred(checkILOffsetBB->GetJumpDest(), checkILOffsetBB);
diff --git a/src/coreclr/jit/async.h b/src/coreclr/jit/async.h
index c9e19c89ebfcc1..b76330bb272456 100644
--- a/src/coreclr/jit/async.h
+++ b/src/coreclr/jit/async.h
@@ -31,6 +31,7 @@ class Async2Transformation
     unsigned                      m_resultBaseVar           = BAD_VAR_NUM;
     unsigned                      m_exceptionVar            = BAD_VAR_NUM;
     BasicBlock*                   m_lastSuspensionBB        = nullptr;
+    BasicBlock*                   m_lastResumptionBB        = nullptr;
 
     void LiftLIREdges(BasicBlock*                    block,
                       GenTree*                       beyond,

From 55e94103366615cb74a99e5b0aa43dc9b910e36c Mon Sep 17 00:00:00 2001
From: Jakob Botsch Nielsen <jakob.botsch.nielsen@gmail.com>
Date: Tue, 7 Nov 2023 13:17:37 +0100
Subject: [PATCH 068/203] Skip post-async2 functions for GC stress

---
 src/coreclr/vm/gccover.cpp       | 11 +++++++++++
 src/coreclr/vm/threadsuspend.cpp |  2 +-
 2 files changed, 12 insertions(+), 1 deletion(-)

diff --git a/src/coreclr/vm/gccover.cpp b/src/coreclr/vm/gccover.cpp
index 0ce88284163c2b..490debd0221e2f 100644
--- a/src/coreclr/vm/gccover.cpp
+++ b/src/coreclr/vm/gccover.cpp
@@ -261,6 +261,8 @@ void SetupGcCoverage(NativeCodeVersion nativeCodeVersion, BYTE* methodStartPtr)
     SetupAndSprinkleBreakpointsForJittedMethod(nativeCodeVersion, codeStart);
 }
 
+bool IsSpecialCaseAsyncRet(MethodDesc* pMD);
+
 void ReplaceInstrAfterCall(PBYTE instrToReplace, MethodDesc* callMD)
 {
     ReturnKind returnKind = callMD->GetReturnKind(true);
@@ -275,6 +277,15 @@ void ReplaceInstrAfterCall(PBYTE instrToReplace, MethodDesc* callMD)
     }
     _ASSERTE(IsValidReturnKind(returnKind));
 
+    if (g_pConfig->RuntimeAsyncViaJitGeneratedStateMachines())
+    {
+        if (callMD->IsAsync2Method() || (callMD->IsIntrinsic() && IsSpecialCaseAsyncRet(callMD)))
+        {
+            // Cannot encode requirement to protect async ret after call.
+            return;
+        }
+    }
+
     bool ispointerKind = IsPointerReturnKind(returnKind);
 #ifdef TARGET_ARM
     size_t instrLen = GetARMInstructionLength(instrToReplace);
diff --git a/src/coreclr/vm/threadsuspend.cpp b/src/coreclr/vm/threadsuspend.cpp
index 3a7465fad92c35..d231ca26e37bb8 100644
--- a/src/coreclr/vm/threadsuspend.cpp
+++ b/src/coreclr/vm/threadsuspend.cpp
@@ -5110,7 +5110,7 @@ void STDCALL OnHijackWorker(HijackArgs * pArgs)
 #endif // HIJACK_NONINTERRUPTIBLE_THREADS
 }
 
-static bool IsSpecialCaseAsyncRet(MethodDesc* pMD)
+bool IsSpecialCaseAsyncRet(MethodDesc* pMD)
 {
     // TODO: What's the right way to do this through CoreLibBinder without
     // causing loading to happen? Also, can we just mark them as async2 in SPC,

From b9190c605948ea410f679d6667a99c826e04070b Mon Sep 17 00:00:00 2001
From: Jakob Botsch Nielsen <jakob.botsch.nielsen@gmail.com>
Date: Tue, 7 Nov 2023 16:21:27 +0100
Subject: [PATCH 069/203] Run jit-format

---
 src/coreclr/jit/async.cpp | 28 ++++++++++++++++++++--------
 1 file changed, 20 insertions(+), 8 deletions(-)

diff --git a/src/coreclr/jit/async.cpp b/src/coreclr/jit/async.cpp
index 3ea354603a9f84..c20d168dea1539 100644
--- a/src/coreclr/jit/async.cpp
+++ b/src/coreclr/jit/async.cpp
@@ -397,7 +397,8 @@ void Async2Transformation::Transform(
     // (-1 in the tier0 version):
     if (m_comp->doesMethodHavePatchpoints() || m_comp->opts.IsOSR())
     {
-        JITDUMP("  Method %s; keeping an IL offset at the beginning of non-GC data\n", m_comp->doesMethodHavePatchpoints() ? "has patchpoints" : "is an OSR method");
+        JITDUMP("  Method %s; keeping an IL offset at the beginning of non-GC data\n",
+                m_comp->doesMethodHavePatchpoints() ? "has patchpoints" : "is an OSR method");
         dataSize += sizeof(int);
     }
 
@@ -416,7 +417,12 @@ void Async2Transformation::Transform(
         returnInGCData = varTypeIsGC(call->gtReturnType);
     }
 
-    JITDUMP("  Will store return of type %s, size %u in %sGC data\n", call->gtReturnType == TYP_STRUCT ? returnStructLayout->GetClassName() : varTypeName(call->gtReturnType), returnSize, returnInGCData ? "" : "non-");
+    if (returnSize > 0)
+    {
+        JITDUMP("  Will store return of type %s, size %u in %sGC data\n",
+                call->gtReturnType == TYP_STRUCT ? returnStructLayout->GetClassName() : varTypeName(call->gtReturnType),
+                returnSize, returnInGCData ? "" : "non-");
+    }
 
     assert((returnSize > 0) == (call->gtReturnType != TYP_VOID));
 
@@ -442,7 +448,8 @@ void Async2Transformation::Transform(
     if (block->hasTryIndex())
     {
         exceptionGCDataIndex = gcRefsCount++;
-        JITDUMP("  " FMT_BB " is in try region %u; exception will be at GC@+%02u in GC data\n", block->bbNum, exceptionGCDataIndex);
+        JITDUMP("  " FMT_BB " is in try region %u; exception will be at GC@+%02u in GC data\n", block->bbNum,
+                exceptionGCDataIndex);
     }
 
     for (LiveLocalInfo& inf : m_liveLocals)
@@ -1202,7 +1209,8 @@ void Async2Transformation::CreateResumptionSwitch()
     FlowEdge* edge = m_comp->fgAddRefPred(m_comp->fgFirstBB, newEntryBB);
     edge->setLikelihood(1);
 
-    JITDUMP("  Inserting new entry " FMT_BB " before old entry " FMT_BB "\n", newEntryBB->bbNum, m_comp->fgFirstBB->bbNum);
+    JITDUMP("  Inserting new entry " FMT_BB " before old entry " FMT_BB "\n", newEntryBB->bbNum,
+            m_comp->fgFirstBB->bbNum);
 
     m_comp->fgInsertBBbefore(m_comp->fgFirstBB, newEntryBB);
 
@@ -1218,7 +1226,8 @@ void Async2Transformation::CreateResumptionSwitch()
 
     if (m_resumptionBBs.size() == 1)
     {
-        JITDUMP("  Redirecting entry " FMT_BB " directly to " FMT_BB " as it is the only resumption block\n", newEntryBB->bbNum, m_resumptionBBs[0]->bbNum);
+        JITDUMP("  Redirecting entry " FMT_BB " directly to " FMT_BB " as it is the only resumption block\n",
+                newEntryBB->bbNum, m_resumptionBBs[0]->bbNum);
         newEntryBB->SetJumpKindAndTarget(BBJ_COND, m_resumptionBBs[0] DEBUGARG(m_comp));
         m_comp->fgAddRefPred(m_resumptionBBs[0], newEntryBB);
     }
@@ -1228,7 +1237,8 @@ void Async2Transformation::CreateResumptionSwitch()
         condBB->bbSetRunRarely();
         newEntryBB->SetJumpKindAndTarget(BBJ_COND, condBB DEBUGARG(m_comp));
 
-        JITDUMP("  Redirecting entry " FMT_BB " to BBJ_COND " FMT_BB " for resumption with 2 states\n", newEntryBB->bbNum, condBB->bbNum);
+        JITDUMP("  Redirecting entry " FMT_BB " to BBJ_COND " FMT_BB " for resumption with 2 states\n",
+                newEntryBB->bbNum, condBB->bbNum);
 
         m_comp->fgAddRefPred(condBB, newEntryBB);
         m_comp->fgAddRefPred(m_resumptionBBs[0], condBB);
@@ -1252,7 +1262,8 @@ void Async2Transformation::CreateResumptionSwitch()
         switchBB->bbSetRunRarely();
         newEntryBB->SetJumpKindAndTarget(BBJ_COND, switchBB DEBUGARG(m_comp));
 
-        JITDUMP("  Redirecting entry " FMT_BB " to BBJ_SWITCH " FMT_BB " for resumption with %zu states\n", newEntryBB->bbNum, switchBB->bbNum, m_resumptionBBs.size());
+        JITDUMP("  Redirecting entry " FMT_BB " to BBJ_SWITCH " FMT_BB " for resumption with %zu states\n",
+                newEntryBB->bbNum, switchBB->bbNum, m_resumptionBBs.size());
 
         m_comp->fgAddRefPred(switchBB, newEntryBB);
 
@@ -1295,7 +1306,8 @@ void Async2Transformation::CreateResumptionSwitch()
 
         BasicBlock* checkILOffsetBB = m_comp->fgNewBBbefore(BBJ_COND, newEntryBB->GetJumpDest(), true, callHelperBB);
 
-        JITDUMP("    Created " FMT_BB " to check whether we should transition immediately to OSR\n", checkILOffsetBB->bbNum);
+        JITDUMP("    Created " FMT_BB " to check whether we should transition immediately to OSR\n",
+                checkILOffsetBB->bbNum);
 
         m_comp->fgRemoveRefPred(newEntryBB->GetJumpDest(), newEntryBB);
         newEntryBB->SetJumpDest(checkILOffsetBB);

From 6ac1f06a4626fede8d626a1e39fdd9dbd565899f Mon Sep 17 00:00:00 2001
From: Jakob Botsch Nielsen <jakob.botsch.nielsen@gmail.com>
Date: Tue, 7 Nov 2023 16:21:34 +0100
Subject: [PATCH 070/203] Fix mixing up struct slot indices with GC data array
 indices

---
 src/coreclr/jit/async.cpp | 24 ++++++++++++++++--------
 1 file changed, 16 insertions(+), 8 deletions(-)

diff --git a/src/coreclr/jit/async.cpp b/src/coreclr/jit/async.cpp
index c20d168dea1539..80fe8da21096a7 100644
--- a/src/coreclr/jit/async.cpp
+++ b/src/coreclr/jit/async.cpp
@@ -629,8 +629,9 @@ void Async2Transformation::Transform(
             else
             {
                 assert((dsc->TypeGet() == TYP_STRUCT) || dsc->IsImplicitByRef());
-                ClassLayout* layout   = dsc->GetLayout();
-                unsigned     numSlots = layout->GetSlotCount();
+                ClassLayout* layout     = dsc->GetLayout();
+                unsigned     numSlots   = layout->GetSlotCount();
+                unsigned     gcRefIndex = 0;
                 for (unsigned i = 0; i < numSlots; i++)
                 {
                     var_types gcPtrType = layout->GetGCPtrType(i);
@@ -652,10 +653,13 @@ void Async2Transformation::Transform(
                     }
 
                     GenTree* objectArr = m_comp->gtNewLclvNode(objectArrLclNum, TYP_REF);
-                    unsigned offset    = OFFSETOF__CORINFO_Array__data + ((inf.GCDataIndex + i) * TARGET_POINTER_SIZE);
-                    GenTree* store     = StoreAtOffset(objectArr, offset, value);
+                    unsigned offset =
+                        OFFSETOF__CORINFO_Array__data + ((inf.GCDataIndex + gcRefIndex) * TARGET_POINTER_SIZE);
+                    GenTree* store = StoreAtOffset(objectArr, offset, value);
                     LIR::AsRange(retBB).InsertAtEnd(LIR::SeqTree(m_comp, store));
 
+                    gcRefIndex++;
+
                     if (inf.DataSize > 0)
                     {
                         // Null out the GC field in preparation of storing the rest.
@@ -855,8 +859,9 @@ void Async2Transformation::Transform(
             else
             {
                 assert((dsc->TypeGet() == TYP_STRUCT) || dsc->IsImplicitByRef());
-                ClassLayout* layout   = dsc->GetLayout();
-                unsigned     numSlots = layout->GetSlotCount();
+                ClassLayout* layout     = dsc->GetLayout();
+                unsigned     numSlots   = layout->GetSlotCount();
+                unsigned     gcRefIndex = 0;
                 for (unsigned i = 0; i < numSlots; i++)
                 {
                     var_types gcPtrType = layout->GetGCPtrType(i);
@@ -867,8 +872,9 @@ void Async2Transformation::Transform(
                     }
 
                     GenTree* objectArr = m_comp->gtNewLclvNode(resumeObjectArrLclNum, TYP_REF);
-                    unsigned offset    = OFFSETOF__CORINFO_Array__data + ((inf.GCDataIndex + i) * TARGET_POINTER_SIZE);
-                    GenTree* value     = LoadFromOffset(objectArr, offset, TYP_REF);
+                    unsigned offset =
+                        OFFSETOF__CORINFO_Array__data + ((inf.GCDataIndex + gcRefIndex) * TARGET_POINTER_SIZE);
+                    GenTree* value = LoadFromOffset(objectArr, offset, TYP_REF);
                     GenTree* store;
                     if (dsc->IsImplicitByRef())
                     {
@@ -884,6 +890,8 @@ void Async2Transformation::Transform(
                     }
 
                     LIR::AsRange(resumeBB).InsertAtEnd(LIR::SeqTree(m_comp, store));
+
+                    gcRefIndex++;
                 }
             }
         }

From fba68afbb2e9dab5db1e73d6d5017a86138f4418 Mon Sep 17 00:00:00 2001
From: David Wrighton <davidwr@microsoft.com>
Date: Tue, 7 Nov 2023 18:44:35 +0000
Subject: [PATCH 071/203] Merged PR 35077: Add an EH performance benchmark

Its a bit of a mess, but its a configurable test which adds finally and catch performance testing

Note: This doesn't work in the unwinder based prototype (and we're not going to make it work, as it is quite a lot of work, and it ALSO doesn't work in the JIT prototype, but I'd like to see if @<Jakob Botsch Nielsen> could fix it there.
---
 .../Loader/async/async2-eh-microbench.cs      | 299 ++++++++++++++++++
 .../Loader/async/async2-eh-microbench.csproj  |   9 +
 2 files changed, 308 insertions(+)
 create mode 100644 src/tests/Loader/async/async2-eh-microbench.cs
 create mode 100644 src/tests/Loader/async/async2-eh-microbench.csproj

diff --git a/src/tests/Loader/async/async2-eh-microbench.cs b/src/tests/Loader/async/async2-eh-microbench.cs
new file mode 100644
index 00000000000000..a750c194fd21dd
--- /dev/null
+++ b/src/tests/Loader/async/async2-eh-microbench.cs
@@ -0,0 +1,299 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+//#define ASYNC1_TASK
+//#define ASYNC1_VALUETASK
+
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Linq;
+using System.Runtime;
+using System.Runtime.CompilerServices;
+using System.Threading;
+using System.Threading.Tasks;
+using Xunit;
+
+public class Async2EHMicrobench
+{
+    [Fact]
+    public static void TestEntryPoint()
+    {
+        Task.Run(AsyncEntry).Wait();
+    }
+
+    public static async Task AsyncEntry()
+    {
+        if (!GCSettings.IsServerGC)
+            Console.WriteLine("*** Warning: Server GC is disabled, set DOTNET_gcServer=1 ***");
+
+        string[] args = Environment.GetCommandLineArgs();
+        int depth = args.Length > 1 ? int.Parse(args[1]) : 2;
+        Console.WriteLine("Using depth = {0}", depth);
+
+        // Yield N times before throwing
+        int yieldFrequency = args.Length > 2 ? int.Parse(args[2]) : 1;
+        Console.WriteLine("Yielding {0} times before throwing", yieldFrequency);
+
+        // Inject a finally every N frames
+        int finallyRate = args.Length > 3 ? int.Parse(args[3]) : 1000;
+        Console.WriteLine("With a try/finally block every {0} frames", finallyRate);
+
+        Benchmark warmupBm = new Benchmark(5, 5, 2);
+        warmupBm.Warmup = true;
+        for (int i = 0; i < 4; i++)
+        {
+            for (int j = 0; j < 40; j++)
+            {
+                await warmupBm.Run("Async2");
+                await warmupBm.Run("Task");
+                await warmupBm.Run("ValueTask");
+            }
+
+            // Make sure we tier up...
+            await Task.Delay(500);
+        }
+
+        Console.WriteLine("Warmup done, running benchmark");
+        await RunBench(yieldFrequency, depth, finallyRate, "Async2");
+        await RunBench(yieldFrequency, depth, finallyRate, "Task");
+        await RunBench(yieldFrequency, depth, finallyRate, "ValueTask");
+    }
+
+    private static async Task RunBench(int yieldCount, int depth, int finallyRate, string type)
+    {
+
+        Benchmark bm = new(yieldCount, depth, finallyRate);
+        Console.WriteLine($"Running benchmark on '{type}' methods");
+
+        List<long> results = new();
+        for (int i = 0; i < 16; i++)
+        {
+            long numIters = await bm.Run(type);
+            Console.WriteLine($"iters={numIters}");
+            results.Add(numIters);
+        }
+
+        results.Sort();
+        double avg = results.Skip(3).Take(10).Average();
+        Console.WriteLine("Result = {0}", (long)avg);
+    }
+
+    private class Benchmark
+    {
+        private readonly int _yieldCount;
+        private readonly int _depth;
+        private readonly int _finallyRate;
+        public int Sink;
+        public bool Warmup;
+
+        public Benchmark(int yieldCount, int depth, int finallyRate)
+        {
+            _yieldCount = yieldCount;
+            _depth = depth;
+            _finallyRate = finallyRate;
+        }
+
+        public async2 long Run(string type)
+        {
+            if (type == "Async2")
+                return await RunAsync2(_depth);
+            if (type == "Task")
+                return await RunTask(_depth);
+            if (type == "ValueTask")
+                return await RunValueTask(_depth);
+            return 0;
+        }
+
+        public async Task<long> RunTask(int depth)
+        {
+            int liveState1 = depth * 3 + _yieldCount;
+            int liveState2 = depth;
+            double liveState3 = _yieldCount;
+
+            if (depth == 0)
+            {
+                int currentAwaitCount = 0;
+
+                while (currentAwaitCount < _yieldCount)
+                {
+                    currentAwaitCount++;
+                    await Task.Yield();
+                }
+
+                throw new Exception();
+            }
+
+            long result = 0;
+
+            if (depth == _depth)
+            {
+                int time = Warmup ? 5 : 500;
+
+                Stopwatch timer = Stopwatch.StartNew();
+
+                long numIters = 0;
+
+                while (timer.ElapsedMilliseconds < time)
+                {
+                    try
+                    {
+                        result = await RunTask(depth - 1);
+                    }
+                    catch (Exception e)
+                    {
+                    }
+                    numIters++;
+
+                }
+                return numIters;
+            }
+            else if ((depth % _finallyRate) == 0)
+            {
+                try
+                {
+                    result = await RunTask(depth - 1);
+                }
+                finally
+                {
+                    Sink = (int)liveState1 + (int)liveState2 + (int)(1 / liveState3) + depth;
+                }
+            }
+            else
+            {
+                result = await RunTask(depth - 1);
+            }
+
+            Sink = (int)liveState1 + (int)liveState2 + (int)(1 / liveState3) + depth;
+            return result;
+        }
+
+        public async ValueTask<long> RunValueTask(int depth)
+        {
+            int liveState1 = depth * 3 + _yieldCount;
+            int liveState2 = depth;
+            double liveState3 = _yieldCount;
+
+            if (depth == 0)
+            {
+                int currentAwaitCount = 0;
+
+                while (currentAwaitCount < _yieldCount)
+                {
+                    currentAwaitCount++;
+                    await Task.Yield();
+                }
+
+                throw new Exception();
+            }
+
+            long result = 0;
+
+            if (depth == _depth)
+            {
+                int time = Warmup ? 5 : 500;
+
+                Stopwatch timer = Stopwatch.StartNew();
+
+                long numIters = 0;
+
+                while (timer.ElapsedMilliseconds < time)
+                {
+                    try
+                    {
+                        result = await RunValueTask(depth - 1);
+                    }
+                    catch (Exception e)
+                    {
+
+                    }
+                    numIters++;
+
+                }
+                return numIters;
+            }
+            else if ((depth % _finallyRate) == 0)
+            {
+                try
+                {
+                    result = await RunValueTask(depth - 1);
+                }
+                finally
+                {
+                    Sink = (int)liveState1 + (int)liveState2 + (int)(1 / liveState3) + depth;
+                }
+            }
+            else
+            {
+                result = await RunValueTask(depth - 1);
+            }
+
+            Sink = (int)liveState1 + (int)liveState2 + (int)(1 / liveState3) + depth;
+            return result;
+        }
+
+        public async2 long RunAsync2(int depth)
+        {
+            int liveState1 = depth * 3 + _yieldCount;
+            int liveState2 = depth;
+            double liveState3 = _yieldCount;
+
+            if (depth == 0)
+            {
+                int currentAwaitCount = 0;
+
+                while (currentAwaitCount < _yieldCount)
+                {
+                    currentAwaitCount++;
+                    await Task.Yield();
+                }
+
+                throw new Exception();
+            }
+
+            long result = 0;
+
+            if (depth == _depth)
+            {
+                int time = Warmup ? 5 : 500;
+
+                Stopwatch timer = Stopwatch.StartNew();
+
+                long numIters = 0;
+
+                while (timer.ElapsedMilliseconds < time)
+                {
+                    try
+                    {
+                        result = await RunAsync2(depth - 1);
+                    }
+                    catch (Exception e)
+                    {
+
+                    }
+                    numIters++;
+
+                }
+                return numIters;
+            }
+            else if ((depth % _finallyRate) == 0)
+            {
+                try
+                {
+                    result = await RunAsync2(depth - 1);
+                }
+                finally
+                {
+                    Sink = (int)liveState1 + (int)liveState2 + (int)(1 / liveState3) + depth;
+                }
+            }
+            else
+            {
+                result = await RunAsync2(depth - 1);
+            }
+
+            Sink = (int)liveState1 + (int)liveState2 + (int)(1 / liveState3) + depth;
+            return result;
+        }
+    }
+}
diff --git a/src/tests/Loader/async/async2-eh-microbench.csproj b/src/tests/Loader/async/async2-eh-microbench.csproj
new file mode 100644
index 00000000000000..d159a32e762f81
--- /dev/null
+++ b/src/tests/Loader/async/async2-eh-microbench.csproj
@@ -0,0 +1,9 @@
+<Project Sdk="Microsoft.NET.Sdk.IL">
+  <PropertyGroup>
+    <Optimize>True</Optimize>
+    <RequiresProcessIsolation>true</RequiresProcessIsolation>
+  </PropertyGroup>
+  <ItemGroup>
+    <Compile Include="$(MSBuildProjectName).cs" />
+  </ItemGroup>
+</Project>

From 350cf5a917bf8eb57a1a68692976dd85630abbd7 Mon Sep 17 00:00:00 2001
From: Jakob Botsch Nielsen <jakob.botsch.nielsen@gmail.com>
Date: Wed, 8 Nov 2023 13:28:44 +0100
Subject: [PATCH 072/203] Update outdated comment

---
 .../System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs  | 3 ---
 src/coreclr/inc/corinfo.h                                      | 2 --
 2 files changed, 5 deletions(-)

diff --git a/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs
index 0e10e2a6295280..32d0b0c3f60f28 100644
--- a/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs
+++ b/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs
@@ -952,8 +952,6 @@ internal enum CorInfoContinuationFlags
     {
         // Whether or not the continuation expects the result to be boxed and
         // placed in the GCData array at index 0. Not set if the callee is void.
-        // TODO: In the future, for value types without any GC refs in them, we can
-        // place them at the beginning of the Data array instead to avoid a box.
         CORINFO_CONTINUATION_RESULT_IN_GCDATA = 1,
         // If this bit is set the continuation resumes inside a try block and thus
         // if an exception is being propagated, needs to be resumed. The exception
@@ -965,7 +963,6 @@ internal enum CorInfoContinuationFlags
         CORINFO_CONTINUATION_OSR_IL_OFFSET_IN_DATA = 4,
     }
 
-
     internal sealed unsafe class Continuation
     {
         public Continuation? Next;
diff --git a/src/coreclr/inc/corinfo.h b/src/coreclr/inc/corinfo.h
index fb3e95e40f7e90..666f196895093f 100644
--- a/src/coreclr/inc/corinfo.h
+++ b/src/coreclr/inc/corinfo.h
@@ -1829,8 +1829,6 @@ enum CorInfoContinuationFlags
 {
     // Whether or not the continuation expects the result to be boxed and
     // placed in the GCData array at index 0. Not set if the callee is void.
-    // TODO: In the future, for value types without any GC refs in them, we can
-    // place them at the beginning of the Data array instead to avoid a box.
     CORINFO_CONTINUATION_RESULT_IN_GCDATA = 1,
     // If this bit is set the continuation resumes inside a try block and thus
     // if an exception is being propagated, needs to be resumed. The exception

From 6ac06db1823f36dd433c83e6dab7aef62acce82e Mon Sep 17 00:00:00 2001
From: Jakob Botsch Nielsen <jakob.botsch.nielsen@gmail.com>
Date: Wed, 8 Nov 2023 13:29:04 +0100
Subject: [PATCH 073/203] Fix a JITDUMP

---
 src/coreclr/jit/async.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/coreclr/jit/async.cpp b/src/coreclr/jit/async.cpp
index 80fe8da21096a7..bb2bc7b2e981e4 100644
--- a/src/coreclr/jit/async.cpp
+++ b/src/coreclr/jit/async.cpp
@@ -449,7 +449,7 @@ void Async2Transformation::Transform(
     {
         exceptionGCDataIndex = gcRefsCount++;
         JITDUMP("  " FMT_BB " is in try region %u; exception will be at GC@+%02u in GC data\n", block->bbNum,
-                exceptionGCDataIndex);
+                block->getTryIndex(), exceptionGCDataIndex);
     }
 
     for (LiveLocalInfo& inf : m_liveLocals)

From e762f70ecd4d0f9a107c98cfef1275c554d272ca Mon Sep 17 00:00:00 2001
From: Jakob Botsch Nielsen <jakob.botsch.nielsen@gmail.com>
Date: Wed, 8 Nov 2023 15:12:04 +0100
Subject: [PATCH 074/203] Update design document for latest JIT SM changes

---
 docs/design/features/runtime-handled-tasks.md | 150 ++++++------------
 1 file changed, 45 insertions(+), 105 deletions(-)

diff --git a/docs/design/features/runtime-handled-tasks.md b/docs/design/features/runtime-handled-tasks.md
index f137f6085696d2..ff85df913ec31b 100644
--- a/docs/design/features/runtime-handled-tasks.md
+++ b/docs/design/features/runtime-handled-tasks.md
@@ -232,7 +232,7 @@ In addition, the JIT generates code for each of these suspension points to be ab
 
 Communicating suspension is done by returning a non-zero continuation, using a special calling convention.
 The continuation is a normal GC ref so special care must be taken within the JIT and within the VM to handle proper GC reporting of this return value.
-On x64, the continuation is always returned in `rcx`.
+On x64 the continuation is returned in `rcx`.
 
 When a callee suspends by returning a non-zero continuation the caller itself suspends by creating its own continuation and returning it.
 Additionally, the generated suspension code links the continuations into a linked list.
@@ -263,15 +263,13 @@ The continuation must have been created by the same exact native code version th
 Passing a zero continuation is the same as starting the state machine.
 The JIT will automatically generate code to pass a zero continuation when calling an async2 method.
 
-When the JIT generates code for an async2 function it requests the runtime to create a resumption stub.
+When the JIT generates code for an async2 function it asks the runtime to create a resumption stub.
 The suspension code stores the pointer to the resumption stub in the `Continuation.Resume` field.
 
 The resumption stub implements the following:
 1. It calls into the original function passing the non-zero continuation
-2. It passes default values for all arguments, to make sure that the callee has the expected stack frame set up
-3. If the function returns a value, it ensures that the return value is propagated into the next continuation.
-Continuations expect that the next continuation has allocated space for the return value in its `GCData[0]` field.
-Currently return values are always boxed when propagating from one continuation to the next continuation.
+2. It passes default values for all arguments, to make sure that the callee has the expected stack frame set up. If parameters are live, then it is expected that the resumed function restores them from the continuation state.
+3. If the function returns a value it ensures that the return value is propagated into the next continuation at the right location (either its `Data` or `GCData` arrays).
 4. If the function returned a new continuation (i.e. it suspended again), the resumption stub returns it back to the caller.
 
 In pseudo-C#:
@@ -289,7 +287,10 @@ static Continuation? IL_STUB_AsyncResume_Foo(Continuation continuation)
 
     if (newContinuation == null)
     {
-        continuation.Next.GCData[0] = (object)result;
+        // Foo returns an int and is saved in the Data array. If Foo returned a
+        // GC ref or struct with GC refs, it would be boxed and stored in GCData instead.
+        // Exact index depends on some factors; see the code for the details.
+        Unsafe.Write(ref continuation.Next.Data[index], result);
     }
 
     return newContinuation;
@@ -300,7 +301,7 @@ static Continuation? IL_STUB_AsyncResume_Foo(Continuation continuation)
 
 To interact with the async2 calling convention the JIT/VM provides the following intrinsics:
 ```csharp
-// Retrieve the continuation returned by an async2 function
+// Retrieve the continuation returned by a preceding async2 function call
 [Intrinsic]
 internal static Continuation? Async2CallContinuation() => null;
 
@@ -320,12 +321,13 @@ Instead, the resumption stub calls the target by `calli` with a signature that i
 
 There is additional complexity necessary to handle OSR correctly:
 1. OSR expects that the frame was set up by a tier-0 method so resumption directly in an OSR method is not possible.
-Instead, the JIT generates code in the tier0 version to check for an OSR continuation in which case the tier-0 method transitions immediately
-to the OSR method on resumption.
+Instead, the JIT generates code in the tier0 version to check for an OSR continuation in which case the tier-0 method transitions immediately to the OSR method on resumption.
 The resumption stub created for an OSR method thus points to its corresponding tier-0 code version.
 This is currently implemented by storing a link between OSR methods and corresponding tier-0 methods in `PatchpointInfo`.
 2. When resuming in a tier-0 method, if we transition to the OSR method by normal means, the OSR method will see a non-zero continuation that belongs to the tier-0 method.
 The OSR method must ignore the continuation in this case.
+3. OSR continuations store the IL offset in the `Data` array which complicates propagation of return values into the next continuation, as the exact index to use depends on whether or not this IL offset is present in that continuation.
+This introduces a small amount of overhead in resumption stubs (in terms of a flag check).
 
 ### Interop: the async1<->async2 adapter layer
 
@@ -377,16 +379,23 @@ static Task<int> FooAdapter(int a, int b)
 {
     int result;
     Continuation? continuation = null;
+
+    ExecutionAndSyncBlockStore state = new();
+    state.Push();
+
     try
     {
-        delegate*<Continuation, int, int, int> foo = &Foo;
-        result = foo(null, a, b);
+        result = Foo(a, b);
         continuation = StubHelpers.Async2CallContinuation();
     }
     catch (Exception ex)
     {
         return Task.FromException<int>(ex);
     }
+    finally
+    {
+        state.Pop();
+    }
 
     if (continuation == null)
         return Task.FromResult(result);
@@ -394,100 +403,31 @@ static Task<int> FooAdapter(int a, int b)
     return FinalizeTaskReturningThunk<int>(continuation);
 }
 ```
-(TODO: ExecutionContext, SynchronizationContext handling...)
-
-The interesting bit is what happens in the asynchronous case, where we ended up in the async2 -> async1 adapter described above, which stored the head of the continuation into TLS.
-This is handled inside `Task<T> FinalizeTaskReturningThunk<T>(Continuation continuation)`:
-
-```csharp
-private static Task<T> FinalizeTaskReturningThunk<T>(Continuation continuation)
-{
-    TaskCompletionSource<T> tcs = new();
-    ref RuntimeAsyncAwaitState state = ref t_runtimeAsyncAwaitState;
-    object[] gcData = new object[3];
-    gcData[2] = tcs;
-    continuation.Next = new Continuation
-    {
-        Resume = &ResumeTaskCompletionSource<T>,
-        GCData = gcData,
-        Flags = CorInfoContinuationFlags.CORINFO_CONTINUATION_NEEDS_EXCEPTION | CorInfoContinuationFlags.CORINFO_CONTINUATION_RESULT_IN_GCDATA,
-    };
-
-    Continuation headContinuation = state.SentinelContinuation!.Next!;
-    state.Notifier!.OnCompleted(() => DispatchContinuations(headContinuation));
-    return tcs.Task;
-}
-
-private static Continuation? ResumeTaskCompletionSource<T>(Continuation continuation, Exception? exception)
-{
-    var tcs = (TaskCompletionSource<T>)continuation.GCData![2];
-    Exception ex = (Exception)continuation.GCData![1];
-    if (ex == null)
-    {
-        tcs.SetResult((T)continuation.GCData![0]);
-    }
-    else
-    {
-        tcs.SetException(ex);
-    }
-
-    return null;
-}
-```
-A synthetic continuation is linked to the end of the chain; when resumed, this continuation will trigger the returned `Task<T>` to be completed via the `TaskCompletionSource<T>`.
-With the entire continuation set up properly the `OnCompleted` method of the awaiter stored in TLS can be invoked; it queues a callback to the `DispatchContinuations` function with the head continuation retrieved through TLS.
-
-The final piece of the puzzle is what happens when the awaiter actually invokes the callback asynchronously.
-It delegates to `DispatchContinuations`, another function part of the BCL.
-This function repeatedly dispatches the next continuation by calling its resumption stub.
-It also accounts for potential exceptions thrown, and propagates it into the next relevant continuation.
-Finally, it handles the case where a resumed async2 function suspends again.
 
-```csharp
-private static unsafe void DispatchContinuations(Continuation? continuation)
-{
-    Debug.Assert(continuation != null);
-
-    while (true)
-    {
-        Continuation? newContinuation = null;
-        try
-        {
-            newContinuation = continuation.Resume(continuation);
-        }
-        catch (Exception ex)
-        {
-            continuation = UnwindToPossibleHandler(continuation);
-            continuation.GCData![(continuation.Flags & CorInfoContinuationFlags.CORINFO_CONTINUATION_RESULT_IN_GCDATA) != 0 ? 1 : 0] = ex;
-            continue;
-        }
-
-        if (newContinuation != null)
-        {
-            newContinuation.Next = continuation.Next;
-            ref RuntimeAsyncAwaitState state = ref t_runtimeAsyncAwaitState;
-            Continuation headContinuation = state.SentinelContinuation!.Next!;
-            state.Notifier!.OnCompleted(() => DispatchContinuations(headContinuation));
-            return;
-        }
-
-        continuation = continuation.Next;
-        if (continuation == null)
-            break;
-    }
-}
-
-private static Continuation UnwindToPossibleHandler(Continuation continuation)
-{
-    while (true)
-    {
-        Debug.Assert(continuation.Next != null);
-        continuation = continuation.Next;
-        if ((continuation.Flags & CorInfoContinuationFlags.CORINFO_CONTINUATION_NEEDS_EXCEPTION) != 0)
-            return continuation;
-    }
-}
-```
+In the suspended case where `Foo` returns a continuation, we know that the leaf called into the async2 -> async1 adapter described previously, which has made the head of the continuation chain and the leaf awaiter available in TLS.
+
+The remaining work gets handled inside `Task<T> FinalizeTaskReturningThunk<T>(Continuation continuation)`.
+See the code for the details, but logically two things happen:
+1. We link a synthetic continuation to the end of the continuation chain.
+When the last async2 method is resumed and finishes running, it will store its result into the synthetic continuation in the same way as other continuations, making the final result available from there.
+2. We call `OnCompleted` on the awaiter that was saved in TLS.
+The callback passed calls a dispatcher that starts resuming the continuation chain in a loop.
+
+There are complications around proper `ExecutionContext` and `SynchronizationContext` handling.
+We currently implement `FinalizeTaskReturningThunk` as a normal async1 method to get (some of) this handling automatically; see the code for the details.
+
+### Potential future improvements
+* We can avoid allocating a synthetic continuation in the async1 -> async2 case if resumption stubs instead take a pointer for where the return value should be stored.
+The dispatcher should pass the return location of the next continuation if there is one, and otherwise forward a passed-in pointer for the final result.
+* EH is currently handled by rethrowing exceptions at the suspension point.
+It should be possible for the JIT to bypass the repeated VM EH dispatching by directly running the appropriate handlers on resumption.
+* If the JIT always allocates continuations that are large enough for all suspension points within the same method then it should be possible to reuse previously allocated continuations when a resumed function suspends again.
+* With support for multiple entry points to functions we can avoid the overhead on async2 to async2 calls of passing a null continuation, and the overhead of testing this continuation at the beginning of most async2 calls.
+Instead, resumption stubs would resume at a special "resume" entry point that would handle the resumption continuation.
+* Suspending deep async call stacks result in a lot of separate allocations as the call stack unwinds.
+If we passed along the state size on async2 to async2 calls, then it would be possible to allocate the state arrays once in the leaf.
+This would trade off suspension performance for synchronous performance.
+Alternatively we could keep some large `byte[]` and `object[]` arrays in TLS that we stored state in during unwinding, after which we copied the resulting arrays.
 
 ## Shared implementation between Unwinder and JIT focused implementation of runtime tasks
 

From 0828e72d7ab8cc9b1497b7f3e7aaa2f435c1f8f4 Mon Sep 17 00:00:00 2001
From: Jakob Botsch Nielsen <jakob.botsch.nielsen@gmail.com>
Date: Wed, 8 Nov 2023 15:12:35 +0100
Subject: [PATCH 075/203] Remove dead/unnecessary code

---
 src/coreclr/vm/prestub.cpp | 11 ++---------
 1 file changed, 2 insertions(+), 9 deletions(-)

diff --git a/src/coreclr/vm/prestub.cpp b/src/coreclr/vm/prestub.cpp
index 1e61d78c318649..4269fb6f2df3a1 100644
--- a/src/coreclr/vm/prestub.cpp
+++ b/src/coreclr/vm/prestub.cpp
@@ -1714,13 +1714,6 @@ void MethodDesc::EmitJitStateMachineBasedRuntimeAsyncThunk(MethodDesc* pAsyncOth
 {
     ILCodeStream* pCode = pSL->NewCodeStream(ILStubLinker::kDispatch);
 
-    MetaSig asyncMsig(pAsyncOtherVariant);
-    SigBuilder targetSig;
-    CreateDerivedTargetSigWithExtraParams(asyncMsig, &targetSig);
-
-    DWORD cbTargetSig;
-    PCCOR_SIGNATURE pTargetSig = (PCCOR_SIGNATURE)targetSig.GetSignature(&cbTargetSig);
-
     unsigned continuationLocal = pCode->NewLocal(LocalDesc(CoreLibBinder::GetClass(CLASS__CONTINUATION)));
 
     TypeHandle thTaskRet = thunkMsig.GetRetTypeHandleThrowing();
@@ -1758,13 +1751,13 @@ void MethodDesc::EmitJitStateMachineBasedRuntimeAsyncThunk(MethodDesc* pAsyncOth
             pCode->BeginTryBlock();
 
             DWORD localArg = 0;
-            if (asyncMsig.HasThis())
+            if (thunkMsig.HasThis())
             {
                 // Struct async thunks not yet implemented
                 _ASSERTE(!this->GetMethodTable()->IsValueType());
                 pCode->EmitLDARG(localArg++);
             }
-            for (UINT iArg = 0; iArg < asyncMsig.NumFixedArgs(); iArg++)
+            for (UINT iArg = 0; iArg < thunkMsig.NumFixedArgs(); iArg++)
             {
                 pCode->EmitLDARG(localArg++);
             }

From 177c3b2f9fcd5013e9f2c95e20c19ea99558ed35 Mon Sep 17 00:00:00 2001
From: Jakob Botsch Nielsen <jakob.botsch.nielsen@gmail.com>
Date: Wed, 8 Nov 2023 15:12:55 +0100
Subject: [PATCH 076/203] Fix non-generic async1 -> async2 thunks

---
 src/coreclr/vm/methodtablebuilder.cpp | 4 +---
 1 file changed, 1 insertion(+), 3 deletions(-)

diff --git a/src/coreclr/vm/methodtablebuilder.cpp b/src/coreclr/vm/methodtablebuilder.cpp
index 6dcd8042459239..bf238862ca13de 100644
--- a/src/coreclr/vm/methodtablebuilder.cpp
+++ b/src/coreclr/vm/methodtablebuilder.cpp
@@ -2883,7 +2883,6 @@ AsyncTaskMethod ClassifyAsyncMethodCore(SigPointer sig, Module* pModule, PCCOR_S
             GetNameOfTypeDefOrRef(pModule, tk, &name, &_namespace);
             if ((strcmp(name, *pIsValueTask ? "ValueTask`1" : "Task`1") == 0) && strcmp(_namespace, "System.Threading.Tasks") == 0)
             {
-                *pIsValueTask = name[0] == 'V';
                 if (IsTypeDefOrRefImplementedInSystemModule(pModule, tk))
                     return AsyncTaskMethod::TaskReturningMethod;
             }
@@ -2895,9 +2894,8 @@ AsyncTaskMethod ClassifyAsyncMethodCore(SigPointer sig, Module* pModule, PCCOR_S
         *pIsValueTask = (elemType == ELEMENT_TYPE_VALUETYPE);
         // This might be System.Threading.Tasks.Task or ValueTask
         GetNameOfTypeDefOrRef(pModule, tk, &name, &_namespace);
-        if ((strcmp(name, *pIsValueTask ? "ValueTask`1" : "Task`1") == 0) && strcmp(_namespace, "System.Threading.Tasks") == 0)
+        if ((strcmp(name, *pIsValueTask ? "ValueTask" : "Task") == 0) && strcmp(_namespace, "System.Threading.Tasks") == 0)
         {
-            *pIsValueTask = name[0] == 'V';
             if (IsTypeDefOrRefImplementedInSystemModule(pModule, tk))
                 return AsyncTaskMethod::TaskNonGenericReturningMethod;
         }

From 2c4257ab2920adf5cae5af54b227113366601921 Mon Sep 17 00:00:00 2001
From: Jakob Botsch Nielsen <jakob.botsch.nielsen@gmail.com>
Date: Wed, 8 Nov 2023 15:13:05 +0100
Subject: [PATCH 077/203] Add a list of expected test failures

---
 src/tests/Loader/async/expected_failures.txt | 9 +++++++++
 1 file changed, 9 insertions(+)
 create mode 100644 src/tests/Loader/async/expected_failures.txt

diff --git a/src/tests/Loader/async/expected_failures.txt b/src/tests/Loader/async/expected_failures.txt
new file mode 100644
index 00000000000000..5fe6eb747541c8
--- /dev/null
+++ b/src/tests/Loader/async/expected_failures.txt
@@ -0,0 +1,9 @@
+DOTNET_RuntimeAsyncViaJitGeneratedStateMachines=0:
+* Loader\async\async2-returns\async2-returns.dll  (retbuffers)
+* Loader\async\simple-eh\simple-eh.dll (EH unsupported)
+* Loader\async\async2-eh-microbench\async2-eh-microbench.cmd (EH unsupported)
+* Loader\async\async2object\async2object.cmd (this one probably should be passing?)
+* Loader\async\async2sharedgeneric\async2sharedgeneric.dll (generics unsupported)
+
+DOTNET_RuntimeAsyncViaJitGeneratedStateMachines=1:
+* Loader\async\async2sharedgeneric\async2sharedgeneric.dll (generics unsupported)
\ No newline at end of file

From 6c3e6a40be7e1122ba6cb84a90da9da699699ea4 Mon Sep 17 00:00:00 2001
From: David Wrighton <davidwr@microsoft.com>
Date: Thu, 9 Nov 2023 18:43:59 +0000
Subject: [PATCH 078/203] Merged PR 35118: Add a writeup based on the EH
 microbenchmark I wrote up

This has insanely positive data in it. Also, I found a bug in Excel which prevented me from making graphs with raw data in them. *shrug*
---
 .../async-vs-async2-relative-performance.svg  |   1 +
 docs/design/features/perf-of-throwing.svg     |   1 +
 docs/design/features/runtime-handled-tasks.md | 100 ++++++++++++++++--
 ...h-different-number-of-finally-on-stack.svg |   1 +
 .../throwing-relative-performance.svg         |   1 +
 .../Loader/async/async2-eh-microbench.cs      |  40 ++++---
 6 files changed, 123 insertions(+), 21 deletions(-)
 create mode 100644 docs/design/features/async-vs-async2-relative-performance.svg
 create mode 100644 docs/design/features/perf-of-throwing.svg
 create mode 100644 docs/design/features/throw-perf-with-different-number-of-finally-on-stack.svg
 create mode 100644 docs/design/features/throwing-relative-performance.svg

diff --git a/docs/design/features/async-vs-async2-relative-performance.svg b/docs/design/features/async-vs-async2-relative-performance.svg
new file mode 100644
index 00000000000000..8eac56d28f95ec
--- /dev/null
+++ b/docs/design/features/async-vs-async2-relative-performance.svg
@@ -0,0 +1 @@
+<svg width="2271" height="1288" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" overflow="hidden"><defs><clipPath id="clip0"><rect x="292" y="3709" width="2271" height="1288"/></clipPath></defs><g clip-path="url(#clip0)" transform="translate(-292 -3709)"><rect x="293" y="3710" width="2268" height="1285" fill="#FFFFFF"/><g><path d="M625.5 4552.65 2510.5 4552.65M625.5 4417.65 2510.5 4417.65M625.5 4283.64 2510.5 4283.64M625.5 4148.64 2510.5 4148.64M625.5 4014.63 2510.5 4014.63M625.5 3880.5 2510.5 3880.5" stroke="#D9D9D9" stroke-width="3.4375" stroke-linejoin="round" stroke-miterlimit="10" fill="none"/></g><g><path d="M685.022 4569.15 739.024 4569.15 739.024 4687 685.022 4687ZM1062.03 4522.15 1116.04 4522.15 1116.04 4687 1062.03 4687ZM1439.05 4492.15 1493.05 4492.15 1493.05 4687 1439.05 4687ZM1816.06 4407.14 1869.06 4407.14 1869.06 4687 1816.06 4687ZM2192.07 4324.14 2246.07 4324.14 2246.07 4687 2192.07 4687Z" fill="#156082"/></g><g><path d="M753.025 4542.15 807.026 4542.15 807.026 4687 753.025 4687ZM1130.04 4496.15 1184.04 4496.15 1184.04 4687 1130.04 4687ZM1507.05 4426.15 1561.05 4426.15 1561.05 4687 1507.05 4687ZM1884.06 4350.14 1938.06 4350.14 1938.06 4687 1884.06 4687ZM2261.07 4273.14 2315.08 4273.14 2315.08 4687 2261.07 4687Z" fill="#E97132"/></g><g><path d="M822.027 4615.15 875.029 4615.15 875.029 4687 822.027 4687ZM1199.04 4580.15 1252.04 4580.15 1252.04 4687 1199.04 4687ZM1575.05 4546.15 1629.05 4546.15 1629.05 4687 1575.05 4687ZM1952.06 4540.15 2006.07 4540.15 2006.07 4687 1952.06 4687ZM2329.08 4527.15 2383.08 4527.15 2383.08 4687 2329.08 4687Z" fill="#196B24"/></g><g><path d="M890.029 4595.15 944.031 4595.15 944.031 4687 890.029 4687ZM1267.04 4539.15 1321.04 4539.15 1321.04 4687 1267.04 4687ZM1644.05 4439.15 1698.06 4439.15 1698.06 4687 1644.05 4687ZM2021.07 4283.14 2075.07 4283.14 2075.07 4687 2021.07 4687ZM2398.08 3966.13 2451.08 3966.13 2451.08 4687 2398.08 4687Z" fill="#0F9ED5"/></g><g><path d="M0 0 0.000360892 806" stroke="#156082" stroke-width="3.4375" stroke-linejoin="round" stroke-miterlimit="10" fill="none" fill-rule="evenodd" transform="matrix(1 0 0 -1 625.5 4686.5)"/></g><g><path d="M625.5 4686.5 635.5 4686.5M625.5 4659.65 635.5 4659.65M625.5 4632.65 635.5 4632.65M625.5 4605.65 635.5 4605.65M625.5 4579.65 635.5 4579.65M625.5 4552.65 635.5 4552.65M625.5 4525.65 635.5 4525.65M625.5 4498.65 635.5 4498.65M625.5 4471.65 635.5 4471.65M625.5 4444.65 635.5 4444.65M625.5 4417.65 635.5 4417.65M625.5 4390.64 635.5 4390.64M625.5 4363.64 635.5 4363.64M625.5 4337.64 635.5 4337.64M625.5 4310.64 635.5 4310.64M625.5 4283.64 635.5 4283.64M625.5 4256.64 635.5 4256.64M625.5 4229.64 635.5 4229.64M625.5 4202.64 635.5 4202.64M625.5 4175.64 635.5 4175.64M625.5 4148.64 635.5 4148.64M625.5 4122.64 635.5 4122.64M625.5 4095.63 635.5 4095.63M625.5 4068.63 635.5 4068.63M625.5 4041.63 635.5 4041.63M625.5 4014.63 635.5 4014.63M625.5 3987.63 635.5 3987.63M625.5 3960.63 635.5 3960.63M625.5 3933.63 635.5 3933.63M625.5 3906.63 635.5 3906.63M625.5 3880.5 635.5 3880.5" stroke="#156082" stroke-width="3.4375" stroke-linejoin="round" stroke-miterlimit="10" fill="none"/></g><g><path d="M625.5 4686.5 2510.5 4686.5" stroke="#D9D9D9" stroke-width="3.4375" stroke-linejoin="round" stroke-miterlimit="10" fill="none" fill-rule="evenodd"/></g><g><text fill="#595959" font-family="Aptos Narrow,Aptos Narrow_MSFontService,sans-serif" font-weight="400" font-stretch="semi-condensed" font-size="41" transform="matrix(1 0 0 1 483.014 4698)">0.00%</text></g><g><text fill="#595959" font-family="Aptos Narrow,Aptos Narrow_MSFontService,sans-serif" font-weight="400" font-stretch="semi-condensed" font-size="41" transform="matrix(1 0 0 1 441.214 4564)">200.00%</text></g><g><text fill="#595959" font-family="Aptos Narrow,Aptos Narrow_MSFontService,sans-serif" font-weight="400" font-stretch="semi-condensed" font-size="41" transform="matrix(1 0 0 1 441.214 4429)">400.00%</text></g><g><text fill="#595959" font-family="Aptos Narrow,Aptos Narrow_MSFontService,sans-serif" font-weight="400" font-stretch="semi-condensed" font-size="41" transform="matrix(1 0 0 1 441.214 4295)">600.00%</text></g><g><text fill="#595959" font-family="Aptos Narrow,Aptos Narrow_MSFontService,sans-serif" font-weight="400" font-stretch="semi-condensed" font-size="41" transform="matrix(1 0 0 1 441.214 4161)">800.00%</text></g><g><text fill="#595959" font-family="Aptos Narrow,Aptos Narrow_MSFontService,sans-serif" font-weight="400" font-stretch="semi-condensed" font-size="41" transform="matrix(1 0 0 1 420.314 4026)">1000.00%</text></g><g><text fill="#595959" font-family="Aptos Narrow,Aptos Narrow_MSFontService,sans-serif" font-weight="400" font-stretch="semi-condensed" font-size="41" transform="matrix(1 0 0 1 420.314 3892)">1200.00%</text></g><g><text fill="#595959" font-family="Aptos Narrow,Aptos Narrow_MSFontService,sans-serif" font-weight="400" font-stretch="semi-condensed" font-size="41" transform="matrix(1 0 0 1 803.908 4752)">1</text></g><g><text fill="#595959" font-family="Aptos Narrow,Aptos Narrow_MSFontService,sans-serif" font-weight="400" font-stretch="semi-condensed" font-size="41" transform="matrix(1 0 0 1 1180.83 4752)">2</text></g><g><text fill="#595959" font-family="Aptos Narrow,Aptos Narrow_MSFontService,sans-serif" font-weight="400" font-stretch="semi-condensed" font-size="41" transform="matrix(1 0 0 1 1557.76 4752)">4</text></g><g><text fill="#595959" font-family="Aptos Narrow,Aptos Narrow_MSFontService,sans-serif" font-weight="400" font-stretch="semi-condensed" font-size="41" transform="matrix(1 0 0 1 1934.68 4752)">8</text></g><g><text fill="#595959" font-family="Aptos Narrow,Aptos Narrow_MSFontService,sans-serif" font-weight="400" font-stretch="semi-condensed" font-size="41" transform="matrix(1 0 0 1 2301.16 4752)">16</text></g><g><text fill="#595959" font-family="Aptos Narrow,Aptos Narrow_MSFontService,sans-serif" font-weight="400" font-stretch="semi-condensed" font-size="46" transform="matrix(6.12323e-17 -1 1 6.12323e-17 393.616 4776)">iters/s async2  divided by iters/s Task base async</text></g><g><text fill="#595959" font-family="Aptos Narrow,Aptos Narrow_MSFontService,sans-serif" font-weight="400" font-stretch="semi-condensed" font-size="46" transform="matrix(1 0 0 1 1457.12 4820)">Stack Depth</text></g><g><text fill="#595959" font-family="Aptos Narrow,Aptos Narrow_MSFontService,sans-serif" font-weight="400" font-stretch="semi-condensed" font-size="64" transform="matrix(1 0 0 1 804.739 3837)">Relative performance of existing async vs runtime async</text></g><g><rect x="713" y="4917" width="23.0001" height="23" fill="#156082"/></g><g><text fill="#595959" font-family="Aptos Narrow,Aptos Narrow_MSFontService,sans-serif" font-weight="400" font-stretch="semi-condensed" font-size="41" transform="matrix(1 0 0 1 745.163 4940)">Return NoSuspend</text></g><g><rect x="1119" y="4917" width="23" height="23" fill="#E97132"/></g><g><text fill="#595959" font-family="Aptos Narrow,Aptos Narrow_MSFontService,sans-serif" font-weight="400" font-stretch="semi-condensed" font-size="41" transform="matrix(1 0 0 1 1151.53 4940)">Throw NoSuspend</text></g><g><rect x="1515" y="4917" width="23" height="23" fill="#196B24"/></g><g><text fill="#595959" font-family="Aptos Narrow,Aptos Narrow_MSFontService,sans-serif" font-weight="400" font-stretch="semi-condensed" font-size="41" transform="matrix(1 0 0 1 1547.62 4940)">Return Suspend</text></g><g><rect x="1874" y="4917" width="23" height="23" fill="#0F9ED5"/></g><g><text fill="#595959" font-family="Aptos Narrow,Aptos Narrow_MSFontService,sans-serif" font-weight="400" font-stretch="semi-condensed" font-size="41" transform="matrix(1 0 0 1 1906.57 4940)">Throw Suspend</text></g><rect x="293.5" y="3710.5" width="2268" height="1285" stroke="#D9D9D9" stroke-width="3.4375" stroke-linejoin="round" stroke-miterlimit="10" fill="none"/></g></svg>
\ No newline at end of file
diff --git a/docs/design/features/perf-of-throwing.svg b/docs/design/features/perf-of-throwing.svg
new file mode 100644
index 00000000000000..0658a971b015d7
--- /dev/null
+++ b/docs/design/features/perf-of-throwing.svg
@@ -0,0 +1 @@
+<svg width="2010" height="1539" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" overflow="hidden"><defs><clipPath id="clip0"><rect x="3812" y="1330" width="2010" height="1539"/></clipPath></defs><g clip-path="url(#clip0)" transform="translate(-3812 -1330)"><rect x="3813" y="1331" width="2007" height="1536" fill="#FFFFFF"/><g><path d="M4146.5 2389.58 5769.5 2389.58M4146.5 2290.58 5769.5 2290.58M4146.5 2192.57 5769.5 2192.57M4146.5 2093.57 5769.5 2093.57M4146.5 1994.57 5769.5 1994.57M4146.5 1896.56 5769.5 1896.56M4146.5 1797.56 5769.5 1797.56M4146.5 1698.56 5769.5 1698.56M4146.5 1600.55 5769.5 1600.55M4146.5 1501.5 5769.5 1501.5" stroke="#D9D9D9" stroke-width="3.4375" stroke-linejoin="round" stroke-miterlimit="10" fill="none"/></g><g><path d="M4146.5 2488.5 5769.5 2488.5" stroke="#D9D9D9" stroke-width="3.4375" stroke-linejoin="round" stroke-miterlimit="10" fill="none" fill-rule="evenodd"/></g><g><path d="M4262 2181 4494.15 2295.08 4726.15 2374.08 4958.16 2420.08 5189.17 2451.08 5421.18 2469.08 5653 2479" stroke="#156082" stroke-width="10.3125" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" fill="none"/></g><g><path d="M4273.83 2181.96C4273.83 2188.03 4268.91 2192.96 4262.83 2192.96 4256.76 2192.96 4251.83 2188.03 4251.83 2181.96 4251.83 2175.88 4256.76 2170.96 4262.83 2170.96 4268.91 2170.96 4273.83 2175.88 4273.83 2181.96Z" stroke="#156082" stroke-width="3.4375" stroke-linejoin="round" stroke-miterlimit="10" fill="#156082"/><path d="M4504.83 2294.96C4504.83 2301.03 4499.91 2305.96 4493.83 2305.96 4487.76 2305.96 4482.83 2301.03 4482.83 2294.96 4482.83 2288.88 4487.76 2283.96 4493.83 2283.96 4499.91 2283.96 4504.83 2288.88 4504.83 2294.96Z" stroke="#156082" stroke-width="3.4375" stroke-linejoin="round" stroke-miterlimit="10" fill="#156082"/><path d="M4736.83 2374.96C4736.83 2381.03 4731.91 2385.96 4725.83 2385.96 4719.76 2385.96 4714.83 2381.03 4714.83 2374.96 4714.83 2368.88 4719.76 2363.96 4725.83 2363.96 4731.91 2363.96 4736.83 2368.88 4736.83 2374.96Z" stroke="#156082" stroke-width="3.4375" stroke-linejoin="round" stroke-miterlimit="10" fill="#156082"/><path d="M4968.83 2419.96C4968.83 2426.03 4963.91 2430.96 4957.83 2430.96 4951.76 2430.96 4946.83 2426.03 4946.83 2419.96 4946.83 2413.88 4951.76 2408.96 4957.83 2408.96 4963.91 2408.96 4968.83 2413.88 4968.83 2419.96Z" stroke="#156082" stroke-width="3.4375" stroke-linejoin="round" stroke-miterlimit="10" fill="#156082"/><path d="M5200.83 2450.96C5200.83 2457.03 5195.91 2461.96 5189.83 2461.96 5183.76 2461.96 5178.83 2457.03 5178.83 2450.96 5178.83 2444.88 5183.76 2439.96 5189.83 2439.96 5195.91 2439.96 5200.83 2444.88 5200.83 2450.96Z" stroke="#156082" stroke-width="3.4375" stroke-linejoin="round" stroke-miterlimit="10" fill="#156082"/><path d="M5432.83 2469.96C5432.83 2476.03 5427.91 2480.96 5421.83 2480.96 5415.76 2480.96 5410.83 2476.03 5410.83 2469.96 5410.83 2463.88 5415.76 2458.96 5421.83 2458.96 5427.91 2458.96 5432.83 2463.88 5432.83 2469.96Z" stroke="#156082" stroke-width="3.4375" stroke-linejoin="round" stroke-miterlimit="10" fill="#156082"/><path d="M5664.83 2479.96C5664.83 2486.03 5659.91 2490.96 5653.83 2490.96 5647.76 2490.96 5642.83 2486.03 5642.83 2479.96 5642.83 2473.88 5647.76 2468.96 5653.83 2468.96 5659.91 2468.96 5664.83 2473.88 5664.83 2479.96Z" stroke="#156082" stroke-width="3.4375" stroke-linejoin="round" stroke-miterlimit="10" fill="#156082"/></g><g><path d="M4262 2181 4494.15 2296.08 4726.15 2380.08 4958.16 2422.08 5189.17 2451.08 5421.18 2470.08 5653 2479" stroke="#E97132" stroke-width="10.3125" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" fill="none"/></g><g><path d="M4273.83 2180.96C4273.83 2187.03 4268.91 2191.96 4262.83 2191.96 4256.76 2191.96 4251.83 2187.03 4251.83 2180.96 4251.83 2174.88 4256.76 2169.96 4262.83 2169.96 4268.91 2169.96 4273.83 2174.88 4273.83 2180.96Z" stroke="#E97132" stroke-width="3.4375" stroke-linejoin="round" stroke-miterlimit="10" fill="#E97132"/><path d="M4504.83 2296.96C4504.83 2303.03 4499.91 2307.96 4493.83 2307.96 4487.76 2307.96 4482.83 2303.03 4482.83 2296.96 4482.83 2290.88 4487.76 2285.96 4493.83 2285.96 4499.91 2285.96 4504.83 2290.88 4504.83 2296.96Z" stroke="#E97132" stroke-width="3.4375" stroke-linejoin="round" stroke-miterlimit="10" fill="#E97132"/><path d="M4736.83 2379.96C4736.83 2386.03 4731.91 2390.96 4725.83 2390.96 4719.76 2390.96 4714.83 2386.03 4714.83 2379.96 4714.83 2373.88 4719.76 2368.96 4725.83 2368.96 4731.91 2368.96 4736.83 2373.88 4736.83 2379.96Z" stroke="#E97132" stroke-width="3.4375" stroke-linejoin="round" stroke-miterlimit="10" fill="#E97132"/><path d="M4968.83 2421.96C4968.83 2428.03 4963.91 2432.96 4957.83 2432.96 4951.76 2432.96 4946.83 2428.03 4946.83 2421.96 4946.83 2415.88 4951.76 2410.96 4957.83 2410.96 4963.91 2410.96 4968.83 2415.88 4968.83 2421.96Z" stroke="#E97132" stroke-width="3.4375" stroke-linejoin="round" stroke-miterlimit="10" fill="#E97132"/><path d="M5200.83 2450.96C5200.83 2457.03 5195.91 2461.96 5189.83 2461.96 5183.76 2461.96 5178.83 2457.03 5178.83 2450.96 5178.83 2444.88 5183.76 2439.96 5189.83 2439.96 5195.91 2439.96 5200.83 2444.88 5200.83 2450.96Z" stroke="#E97132" stroke-width="3.4375" stroke-linejoin="round" stroke-miterlimit="10" fill="#E97132"/><path d="M5432.83 2469.96C5432.83 2476.03 5427.91 2480.96 5421.83 2480.96 5415.76 2480.96 5410.83 2476.03 5410.83 2469.96 5410.83 2463.88 5415.76 2458.96 5421.83 2458.96 5427.91 2458.96 5432.83 2463.88 5432.83 2469.96Z" stroke="#E97132" stroke-width="3.4375" stroke-linejoin="round" stroke-miterlimit="10" fill="#E97132"/><path d="M5664.83 2479.96C5664.83 2486.03 5659.91 2490.96 5653.83 2490.96 5647.76 2490.96 5642.83 2486.03 5642.83 2479.96 5642.83 2473.88 5647.76 2468.96 5653.83 2468.96 5659.91 2468.96 5664.83 2473.88 5664.83 2479.96Z" stroke="#E97132" stroke-width="3.4375" stroke-linejoin="round" stroke-miterlimit="10" fill="#E97132"/></g><g><path d="M4262 2070.07 4494.15 2064 4726.15 2068.07 4958.16 2076.07 5189.17 2084.07 5421.18 2106.07 5653 2142" stroke="#196B24" stroke-width="10.3125" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" fill="none"/></g><g><path d="M4273.83 2069.96C4273.83 2076.03 4268.91 2080.96 4262.83 2080.96 4256.76 2080.96 4251.83 2076.03 4251.83 2069.96 4251.83 2063.88 4256.76 2058.96 4262.83 2058.96 4268.91 2058.96 4273.83 2063.88 4273.83 2069.96Z" stroke="#196B24" stroke-width="3.4375" stroke-linejoin="round" stroke-miterlimit="10" fill="#196B24"/><path d="M4504.83 2063.96C4504.83 2070.03 4499.91 2074.96 4493.83 2074.96 4487.76 2074.96 4482.83 2070.03 4482.83 2063.96 4482.83 2057.88 4487.76 2052.96 4493.83 2052.96 4499.91 2052.96 4504.83 2057.88 4504.83 2063.96Z" stroke="#196B24" stroke-width="3.4375" stroke-linejoin="round" stroke-miterlimit="10" fill="#196B24"/><path d="M4736.83 2068.96C4736.83 2075.03 4731.91 2079.96 4725.83 2079.96 4719.76 2079.96 4714.83 2075.03 4714.83 2068.96 4714.83 2062.88 4719.76 2057.96 4725.83 2057.96 4731.91 2057.96 4736.83 2062.88 4736.83 2068.96Z" stroke="#196B24" stroke-width="3.4375" stroke-linejoin="round" stroke-miterlimit="10" fill="#196B24"/><path d="M4968.83 2076.96C4968.83 2083.03 4963.91 2087.96 4957.83 2087.96 4951.76 2087.96 4946.83 2083.03 4946.83 2076.96 4946.83 2070.88 4951.76 2065.96 4957.83 2065.96 4963.91 2065.96 4968.83 2070.88 4968.83 2076.96Z" stroke="#196B24" stroke-width="3.4375" stroke-linejoin="round" stroke-miterlimit="10" fill="#196B24"/><path d="M5200.83 2083.96C5200.83 2090.03 5195.91 2094.96 5189.83 2094.96 5183.76 2094.96 5178.83 2090.03 5178.83 2083.96 5178.83 2077.88 5183.76 2072.96 5189.83 2072.96 5195.91 2072.96 5200.83 2077.88 5200.83 2083.96Z" stroke="#196B24" stroke-width="3.4375" stroke-linejoin="round" stroke-miterlimit="10" fill="#196B24"/><path d="M5432.83 2105.96C5432.83 2112.03 5427.91 2116.96 5421.83 2116.96 5415.76 2116.96 5410.83 2112.03 5410.83 2105.96 5410.83 2099.88 5415.76 2094.96 5421.83 2094.96 5427.91 2094.96 5432.83 2099.88 5432.83 2105.96Z" stroke="#196B24" stroke-width="3.4375" stroke-linejoin="round" stroke-miterlimit="10" fill="#196B24"/><path d="M5664.83 2141.96C5664.83 2148.03 5659.91 2152.96 5653.83 2152.96 5647.76 2152.96 5642.83 2148.03 5642.83 2141.96 5642.83 2135.88 5647.76 2130.96 5653.83 2130.96 5659.91 2130.96 5664.83 2135.88 5664.83 2141.96Z" stroke="#196B24" stroke-width="3.4375" stroke-linejoin="round" stroke-miterlimit="10" fill="#196B24"/></g><g><path d="M4262 2061 4494.15 2216.07 4726.15 2336.08 4958.16 2408.08 5189.17 2447.08 5421.18 2468.08 5653 2479" stroke="#0F9ED5" stroke-width="10.3125" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" fill="none"/></g><g><path d="M4273.83 2061.96C4273.83 2068.03 4268.91 2072.96 4262.83 2072.96 4256.76 2072.96 4251.83 2068.03 4251.83 2061.96 4251.83 2055.88 4256.76 2050.96 4262.83 2050.96 4268.91 2050.96 4273.83 2055.88 4273.83 2061.96Z" stroke="#0F9ED5" stroke-width="3.4375" stroke-linejoin="round" stroke-miterlimit="10" fill="#0F9ED5"/><path d="M4504.83 2216.96C4504.83 2223.03 4499.91 2227.96 4493.83 2227.96 4487.76 2227.96 4482.83 2223.03 4482.83 2216.96 4482.83 2210.88 4487.76 2205.96 4493.83 2205.96 4499.91 2205.96 4504.83 2210.88 4504.83 2216.96Z" stroke="#0F9ED5" stroke-width="3.4375" stroke-linejoin="round" stroke-miterlimit="10" fill="#0F9ED5"/><path d="M4736.83 2336.96C4736.83 2343.03 4731.91 2347.96 4725.83 2347.96 4719.76 2347.96 4714.83 2343.03 4714.83 2336.96 4714.83 2330.88 4719.76 2325.96 4725.83 2325.96 4731.91 2325.96 4736.83 2330.88 4736.83 2336.96Z" stroke="#0F9ED5" stroke-width="3.4375" stroke-linejoin="round" stroke-miterlimit="10" fill="#0F9ED5"/><path d="M4968.83 2408.96C4968.83 2415.03 4963.91 2419.96 4957.83 2419.96 4951.76 2419.96 4946.83 2415.03 4946.83 2408.96 4946.83 2402.88 4951.76 2397.96 4957.83 2397.96 4963.91 2397.96 4968.83 2402.88 4968.83 2408.96Z" stroke="#0F9ED5" stroke-width="3.4375" stroke-linejoin="round" stroke-miterlimit="10" fill="#0F9ED5"/><path d="M5200.83 2446.96C5200.83 2453.03 5195.91 2457.96 5189.83 2457.96 5183.76 2457.96 5178.83 2453.03 5178.83 2446.96 5178.83 2440.88 5183.76 2435.96 5189.83 2435.96 5195.91 2435.96 5200.83 2440.88 5200.83 2446.96Z" stroke="#0F9ED5" stroke-width="3.4375" stroke-linejoin="round" stroke-miterlimit="10" fill="#0F9ED5"/><path d="M5432.83 2468.96C5432.83 2475.03 5427.91 2479.96 5421.83 2479.96 5415.76 2479.96 5410.83 2475.03 5410.83 2468.96 5410.83 2462.88 5415.76 2457.96 5421.83 2457.96 5427.91 2457.96 5432.83 2462.88 5432.83 2468.96Z" stroke="#0F9ED5" stroke-width="3.4375" stroke-linejoin="round" stroke-miterlimit="10" fill="#0F9ED5"/><path d="M5664.83 2478.96C5664.83 2485.03 5659.91 2489.96 5653.83 2489.96 5647.76 2489.96 5642.83 2485.03 5642.83 2478.96 5642.83 2472.88 5647.76 2467.96 5653.83 2467.96 5659.91 2467.96 5664.83 2472.88 5664.83 2478.96Z" stroke="#0F9ED5" stroke-width="3.4375" stroke-linejoin="round" stroke-miterlimit="10" fill="#0F9ED5"/></g><g><path d="M4262 2058 4494.15 2225.07 4726.15 2341.08 4958.16 2409.08 5189.17 2448.08 5421.18 2469.08 5653 2479" stroke="#A02B93" stroke-width="10.3125" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" fill="none"/></g><g><path d="M4273.83 2058.96C4273.83 2065.03 4268.91 2069.96 4262.83 2069.96 4256.76 2069.96 4251.83 2065.03 4251.83 2058.96 4251.83 2052.88 4256.76 2047.96 4262.83 2047.96 4268.91 2047.96 4273.83 2052.88 4273.83 2058.96Z" stroke="#A02B93" stroke-width="3.4375" stroke-linejoin="round" stroke-miterlimit="10" fill="#A02B93"/><path d="M4504.83 2224.96C4504.83 2231.03 4499.91 2235.96 4493.83 2235.96 4487.76 2235.96 4482.83 2231.03 4482.83 2224.96 4482.83 2218.88 4487.76 2213.96 4493.83 2213.96 4499.91 2213.96 4504.83 2218.88 4504.83 2224.96Z" stroke="#A02B93" stroke-width="3.4375" stroke-linejoin="round" stroke-miterlimit="10" fill="#A02B93"/><path d="M4736.83 2340.96C4736.83 2347.03 4731.91 2351.96 4725.83 2351.96 4719.76 2351.96 4714.83 2347.03 4714.83 2340.96 4714.83 2334.88 4719.76 2329.96 4725.83 2329.96 4731.91 2329.96 4736.83 2334.88 4736.83 2340.96Z" stroke="#A02B93" stroke-width="3.4375" stroke-linejoin="round" stroke-miterlimit="10" fill="#A02B93"/><path d="M4968.83 2409.96C4968.83 2416.03 4963.91 2420.96 4957.83 2420.96 4951.76 2420.96 4946.83 2416.03 4946.83 2409.96 4946.83 2403.88 4951.76 2398.96 4957.83 2398.96 4963.91 2398.96 4968.83 2403.88 4968.83 2409.96Z" stroke="#A02B93" stroke-width="3.4375" stroke-linejoin="round" stroke-miterlimit="10" fill="#A02B93"/><path d="M5200.83 2447.96C5200.83 2454.03 5195.91 2458.96 5189.83 2458.96 5183.76 2458.96 5178.83 2454.03 5178.83 2447.96 5178.83 2441.88 5183.76 2436.96 5189.83 2436.96 5195.91 2436.96 5200.83 2441.88 5200.83 2447.96Z" stroke="#A02B93" stroke-width="3.4375" stroke-linejoin="round" stroke-miterlimit="10" fill="#A02B93"/><path d="M5432.83 2468.96C5432.83 2475.03 5427.91 2479.96 5421.83 2479.96 5415.76 2479.96 5410.83 2475.03 5410.83 2468.96 5410.83 2462.88 5415.76 2457.96 5421.83 2457.96 5427.91 2457.96 5432.83 2462.88 5432.83 2468.96Z" stroke="#A02B93" stroke-width="3.4375" stroke-linejoin="round" stroke-miterlimit="10" fill="#A02B93"/><path d="M5664.83 2479.96C5664.83 2486.03 5659.91 2490.96 5653.83 2490.96 5647.76 2490.96 5642.83 2486.03 5642.83 2479.96 5642.83 2473.88 5647.76 2468.96 5653.83 2468.96 5659.91 2468.96 5664.83 2473.88 5664.83 2479.96Z" stroke="#A02B93" stroke-width="3.4375" stroke-linejoin="round" stroke-miterlimit="10" fill="#A02B93"/></g><g><path d="M4262 1572 4494.15 1719.06 4726.15 1898.06 4958.16 2086.07 5189.17 2233.07 5421.18 2344.08 5653 2411" stroke="#4EA72E" stroke-width="10.3125" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" fill="none"/></g><g><path d="M4273.83 1571.96C4273.83 1578.03 4268.91 1582.96 4262.83 1582.96 4256.76 1582.96 4251.83 1578.03 4251.83 1571.96 4251.83 1565.88 4256.76 1560.96 4262.83 1560.96 4268.91 1560.96 4273.83 1565.88 4273.83 1571.96Z" stroke="#4EA72E" stroke-width="3.4375" stroke-linejoin="round" stroke-miterlimit="10" fill="#4EA72E"/><path d="M4504.83 1719.96C4504.83 1726.03 4499.91 1730.96 4493.83 1730.96 4487.76 1730.96 4482.83 1726.03 4482.83 1719.96 4482.83 1713.88 4487.76 1708.96 4493.83 1708.96 4499.91 1708.96 4504.83 1713.88 4504.83 1719.96Z" stroke="#4EA72E" stroke-width="3.4375" stroke-linejoin="round" stroke-miterlimit="10" fill="#4EA72E"/><path d="M4736.83 1898.96C4736.83 1905.03 4731.91 1909.96 4725.83 1909.96 4719.76 1909.96 4714.83 1905.03 4714.83 1898.96 4714.83 1892.88 4719.76 1887.96 4725.83 1887.96 4731.91 1887.96 4736.83 1892.88 4736.83 1898.96Z" stroke="#4EA72E" stroke-width="3.4375" stroke-linejoin="round" stroke-miterlimit="10" fill="#4EA72E"/><path d="M4968.83 2086.96C4968.83 2093.03 4963.91 2097.96 4957.83 2097.96 4951.76 2097.96 4946.83 2093.03 4946.83 2086.96 4946.83 2080.88 4951.76 2075.96 4957.83 2075.96 4963.91 2075.96 4968.83 2080.88 4968.83 2086.96Z" stroke="#4EA72E" stroke-width="3.4375" stroke-linejoin="round" stroke-miterlimit="10" fill="#4EA72E"/><path d="M5200.83 2232.96C5200.83 2239.03 5195.91 2243.96 5189.83 2243.96 5183.76 2243.96 5178.83 2239.03 5178.83 2232.96 5178.83 2226.88 5183.76 2221.96 5189.83 2221.96 5195.91 2221.96 5200.83 2226.88 5200.83 2232.96Z" stroke="#4EA72E" stroke-width="3.4375" stroke-linejoin="round" stroke-miterlimit="10" fill="#4EA72E"/><path d="M5432.83 2343.96C5432.83 2350.03 5427.91 2354.96 5421.83 2354.96 5415.76 2354.96 5410.83 2350.03 5410.83 2343.96 5410.83 2337.88 5415.76 2332.96 5421.83 2332.96 5427.91 2332.96 5432.83 2337.88 5432.83 2343.96Z" stroke="#4EA72E" stroke-width="3.4375" stroke-linejoin="round" stroke-miterlimit="10" fill="#4EA72E"/><path d="M5664.83 2410.96C5664.83 2417.03 5659.91 2421.96 5653.83 2421.96 5647.76 2421.96 5642.83 2417.03 5642.83 2410.96 5642.83 2404.88 5647.76 2399.96 5653.83 2399.96 5659.91 2399.96 5664.83 2404.88 5664.83 2410.96Z" stroke="#4EA72E" stroke-width="3.4375" stroke-linejoin="round" stroke-miterlimit="10" fill="#4EA72E"/></g><g><text fill="#595959" font-family="Aptos Narrow,Aptos Narrow_MSFontService,sans-serif" font-weight="400" font-stretch="semi-condensed" font-size="41" transform="matrix(1 0 0 1 4034.64 2500)">0.00</text></g><g><text fill="#595959" font-family="Aptos Narrow,Aptos Narrow_MSFontService,sans-serif" font-weight="400" font-stretch="semi-condensed" font-size="41" transform="matrix(1 0 0 1 3961.21 2401)">5,000.00</text></g><g><text fill="#595959" font-family="Aptos Narrow,Aptos Narrow_MSFontService,sans-serif" font-weight="400" font-stretch="semi-condensed" font-size="41" transform="matrix(1 0 0 1 3940.31 2303)">10,000.00</text></g><g><text fill="#595959" font-family="Aptos Narrow,Aptos Narrow_MSFontService,sans-serif" font-weight="400" font-stretch="semi-condensed" font-size="41" transform="matrix(1 0 0 1 3940.31 2204)">15,000.00</text></g><g><text fill="#595959" font-family="Aptos Narrow,Aptos Narrow_MSFontService,sans-serif" font-weight="400" font-stretch="semi-condensed" font-size="41" transform="matrix(1 0 0 1 3940.31 2105)">20,000.00</text></g><g><text fill="#595959" font-family="Aptos Narrow,Aptos Narrow_MSFontService,sans-serif" font-weight="400" font-stretch="semi-condensed" font-size="41" transform="matrix(1 0 0 1 3940.31 2006)">25,000.00</text></g><g><text fill="#595959" font-family="Aptos Narrow,Aptos Narrow_MSFontService,sans-serif" font-weight="400" font-stretch="semi-condensed" font-size="41" transform="matrix(1 0 0 1 3940.31 1908)">30,000.00</text></g><g><text fill="#595959" font-family="Aptos Narrow,Aptos Narrow_MSFontService,sans-serif" font-weight="400" font-stretch="semi-condensed" font-size="41" transform="matrix(1 0 0 1 3940.31 1809)">35,000.00</text></g><g><text fill="#595959" font-family="Aptos Narrow,Aptos Narrow_MSFontService,sans-serif" font-weight="400" font-stretch="semi-condensed" font-size="41" transform="matrix(1 0 0 1 3940.31 1710)">40,000.00</text></g><g><text fill="#595959" font-family="Aptos Narrow,Aptos Narrow_MSFontService,sans-serif" font-weight="400" font-stretch="semi-condensed" font-size="41" transform="matrix(1 0 0 1 3940.31 1612)">45,000.00</text></g><g><text fill="#595959" font-family="Aptos Narrow,Aptos Narrow_MSFontService,sans-serif" font-weight="400" font-stretch="semi-condensed" font-size="41" transform="matrix(1 0 0 1 3940.31 1513)">50,000.00</text></g><g><text fill="#595959" font-family="Aptos Narrow,Aptos Narrow_MSFontService,sans-serif" font-weight="400" font-stretch="semi-condensed" font-size="41" transform="matrix(1 0 0 1 4251.64 2553)">1</text></g><g><text fill="#595959" font-family="Aptos Narrow,Aptos Narrow_MSFontService,sans-serif" font-weight="400" font-stretch="semi-condensed" font-size="41" transform="matrix(1 0 0 1 4483.51 2553)">2</text></g><g><text fill="#595959" font-family="Aptos Narrow,Aptos Narrow_MSFontService,sans-serif" font-weight="400" font-stretch="semi-condensed" font-size="41" transform="matrix(1 0 0 1 4715.38 2553)">4</text></g><g><text fill="#595959" font-family="Aptos Narrow,Aptos Narrow_MSFontService,sans-serif" font-weight="400" font-stretch="semi-condensed" font-size="41" transform="matrix(1 0 0 1 4947.26 2553)">8</text></g><g><text fill="#595959" font-family="Aptos Narrow,Aptos Narrow_MSFontService,sans-serif" font-weight="400" font-stretch="semi-condensed" font-size="41" transform="matrix(1 0 0 1 5168.68 2553)">16</text></g><g><text fill="#595959" font-family="Aptos Narrow,Aptos Narrow_MSFontService,sans-serif" font-weight="400" font-stretch="semi-condensed" font-size="41" transform="matrix(1 0 0 1 5400.56 2553)">32</text></g><g><text fill="#595959" font-family="Aptos Narrow,Aptos Narrow_MSFontService,sans-serif" font-weight="400" font-stretch="semi-condensed" font-size="41" transform="matrix(1 0 0 1 5632.43 2553)">64</text></g><g><text fill="#595959" font-family="Aptos Narrow,Aptos Narrow_MSFontService,sans-serif" font-weight="400" font-stretch="semi-condensed" font-size="46" transform="matrix(6.12323e-17 -1 1 6.12323e-17 3913.62 2105)">iters/250ms</text></g><g><text fill="#595959" font-family="Aptos Narrow,Aptos Narrow_MSFontService,sans-serif" font-weight="400" font-stretch="semi-condensed" font-size="46" transform="matrix(1 0 0 1 4846.62 2621)">Stack Depth</text></g><g><text fill="#595959" font-family="Aptos Narrow,Aptos Narrow_MSFontService,sans-serif" font-weight="400" font-stretch="semi-condensed" font-size="64" transform="matrix(1 0 0 1 4380.08 1426)">Throw perf at varying  stack depths</text></g><g><path d="M3954 2730 4042 2730" stroke="#156082" stroke-width="10.3125" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" fill="none" fill-rule="evenodd"/></g><g><path d="M4009 2730C4009 2736.07 4004.07 2741 3998 2741 3991.93 2741 3987 2736.07 3987 2730 3987 2723.92 3991.93 2719 3998 2719 4004.07 2719 4009 2723.92 4009 2730Z" stroke="#156082" stroke-width="3.4375" stroke-linejoin="round" stroke-miterlimit="10" fill="#156082"/></g><g><text fill="#595959" font-family="Aptos Narrow,Aptos Narrow_MSFontService,sans-serif" font-weight="400" font-stretch="semi-condensed" font-size="41" transform="matrix(1 0 0 1 4051.76 2742)">Task Throw Suspend</text></g><g><path d="M4539 2730 4627 2730" stroke="#E97132" stroke-width="10.3125" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" fill="none" fill-rule="evenodd"/></g><g><path d="M4593 2730C4593 2736.07 4588.08 2741 4582 2741 4575.92 2741 4571 2736.07 4571 2730 4571 2723.92 4575.92 2719 4582 2719 4588.08 2719 4593 2723.92 4593 2730Z" stroke="#E97132" stroke-width="3.4375" stroke-linejoin="round" stroke-miterlimit="10" fill="#E97132"/></g><g><text fill="#595959" font-family="Aptos Narrow,Aptos Narrow_MSFontService,sans-serif" font-weight="400" font-stretch="semi-condensed" font-size="41" transform="matrix(1 0 0 1 4636.07 2742)">ValueTask Throw Suspend</text></g><g><path d="M5123 2730 5211 2730" stroke="#196B24" stroke-width="10.3125" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" fill="none" fill-rule="evenodd"/></g><g><path d="M5178 2730C5178 2736.07 5173.08 2741 5167 2741 5160.92 2741 5156 2736.07 5156 2730 5156 2723.92 5160.92 2719 5167 2719 5173.08 2719 5178 2723.92 5178 2730Z" stroke="#196B24" stroke-width="3.4375" stroke-linejoin="round" stroke-miterlimit="10" fill="#196B24"/></g><g><text fill="#595959" font-family="Aptos Narrow,Aptos Narrow_MSFontService,sans-serif" font-weight="400" font-stretch="semi-condensed" font-size="41" transform="matrix(1 0 0 1 5220.38 2742)">Async2 Throw Suspend</text></g><g><path d="M3954 2808 4042 2808" stroke="#0F9ED5" stroke-width="10.3125" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" fill="none" fill-rule="evenodd"/></g><g><path d="M4009 2807C4009 2813.07 4004.07 2818 3998 2818 3991.93 2818 3987 2813.07 3987 2807 3987 2800.92 3991.93 2796 3998 2796 4004.07 2796 4009 2800.92 4009 2807Z" stroke="#0F9ED5" stroke-width="3.4375" stroke-linejoin="round" stroke-miterlimit="10" fill="#0F9ED5"/></g><g><text fill="#595959" font-family="Aptos Narrow,Aptos Narrow_MSFontService,sans-serif" font-weight="400" font-stretch="semi-condensed" font-size="41" transform="matrix(1 0 0 1 4051.76 2819)">Task Throw NoSuspend</text></g><g><path d="M4539 2808 4627 2808" stroke="#A02B93" stroke-width="10.3125" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" fill="none" fill-rule="evenodd"/></g><g><path d="M4593 2807C4593 2813.07 4588.08 2818 4582 2818 4575.92 2818 4571 2813.07 4571 2807 4571 2800.92 4575.92 2796 4582 2796 4588.08 2796 4593 2800.92 4593 2807Z" stroke="#A02B93" stroke-width="3.4375" stroke-linejoin="round" stroke-miterlimit="10" fill="#A02B93"/></g><g><text fill="#595959" font-family="Aptos Narrow,Aptos Narrow_MSFontService,sans-serif" font-weight="400" font-stretch="semi-condensed" font-size="41" transform="matrix(1 0 0 1 4636.07 2819)">ValueTask Throw NoSuspend</text></g><g><path d="M5123 2808 5211 2808" stroke="#4EA72E" stroke-width="10.3125" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" fill="none" fill-rule="evenodd"/></g><g><path d="M5178 2807C5178 2813.07 5173.08 2818 5167 2818 5160.92 2818 5156 2813.07 5156 2807 5156 2800.92 5160.92 2796 5167 2796 5173.08 2796 5178 2800.92 5178 2807Z" stroke="#4EA72E" stroke-width="3.4375" stroke-linejoin="round" stroke-miterlimit="10" fill="#4EA72E"/></g><g><text fill="#595959" font-family="Aptos Narrow,Aptos Narrow_MSFontService,sans-serif" font-weight="400" font-stretch="semi-condensed" font-size="41" transform="matrix(1 0 0 1 5220.38 2819)">Async2 Throw NoSuspend</text></g><rect x="3813.5" y="1331.5" width="2007" height="1536" stroke="#D9D9D9" stroke-width="3.4375" stroke-linejoin="round" stroke-miterlimit="10" fill="none"/></g></svg>
\ No newline at end of file
diff --git a/docs/design/features/runtime-handled-tasks.md b/docs/design/features/runtime-handled-tasks.md
index ff85df913ec31b..5a9f3183383207 100644
--- a/docs/design/features/runtime-handled-tasks.md
+++ b/docs/design/features/runtime-handled-tasks.md
@@ -115,11 +115,10 @@ Async2 shall integrate with `TaskScheduler.Current`.
 Async2 IL is broadly similar to the existing IL semantics with the following restrictions.
 
 1. Usage of the `localloc` instruction is forbidden
-3. The `ldloca` and `ldarga` instructions are redefined to return managed pointers instead of pointers.
-4. A pinning local cannot be validly used with a local variable.
-5. As an initial restriction that is not fundamental, the `tail.` prefix is not permitted
-
-Notably, the design permits byrefs and ref structs to be used within async2 methods.
+2. The `ldloca` and `ldarga` instructions are redefined to return managed pointers instead of pointers.
+3. A pinning local cannot be validly used with a local variable.
+. As an initial restriction that is not fundamental, the `tail.` prefix is not permitted
+4. Use of byrefs and ref structs within async2 methods may or may not be acceptable. The currently JIT focussed prototype is designed with the same limitations as the existing async model, and thus does not work with byref data. However, the unwinder approach is able to tolerate byrefs.
 
 #### EH Semantics
 No call to a method with an async2 modreq will be permitted in a finally, fault, filter, or catch clause. If such a thing exists, the program is invalid.
@@ -461,10 +460,97 @@ Major identified concerns are
 
 1. Async computation of fibonacci via recursive algorithm, with and without various amounts of yielding
 2. Suspension with stacks of various depths
-3. Suspension with stacks where the operation of the task operates in a sawtooth pattern. For instance, suspend first at depth 20, then reach depth 30, return to depth 10 and suspend, reach depth 30, return to depth 10 and suspend, etc. Measure the impact of suspending with greater and lesser depths.
+3. Suspension with stacks where the operation of the task operates in a sawtooth pattern.
+For instance, suspend first at depth 20, then reach depth 30, return to depth 10 and suspend, reach depth 30, return to depth 10 and suspend, etc.
+Measure the impact of suspending with greater and lesser depths.
 4. Measure the performance of thunks to async2 functions which do effectively nothing.
 5. Benchmark the effect of long lived suspended state vs short lived suspended state
-6. Benchmark the performance of EH at various depths.
+# Benchmark the performance of EH at various depths.
+
+The performance of EH within the existing async programming model is highly problematic, and has caused customers issues.
+In particular, the impact of async EH performance tends to interfere with responsive performance of applications when failures start ocurring suddenly.
+Since EH is so much slower than standard execution performance, it is known to cause additional downtime as a server which may be capable of handling the load of succesful requests, may run out of available CPU time handling failed requests.
+In addition, EH is commonly used to implement cancellation in async code.
+Thus improving the performance of EH can dramatically improve codebases which make significant use of cancellation.
+The runtime generated async state machine model is showing some substantial improvements to performance.
+
+All of these figures are gathered by using the async-eh-microbench test located at `src/tests/Loader/async/async2-eh-microbench.csproj`.
+This benchmark works by looping until 250 ms has passed and measuring the number of iterations in that time.
+Each iteration works by recursively calling a function until the specified stack depth is achieved (optionally calling through a try/finally block), and then suspending the async method by awaiting `Task.Yield()` or not, and then finally finishing each iteration either by returning or throwing a newly created exception.
+In addition, these experiments use the JIT focused implementation of `async2`. The unwinder based implementation is both significantly slower, and does not support EH.
+
+## Notes on the following graphs
+These benchmarks are gathered by running the async2-eh-microbench in varying configurations.
+- NoSuspend vs Suspend - Suspend indicates that the async function awaits `Task.Yield()` before completing.
+- `Return` vs `Throw` - `Return` indicates that the iteration finishes by returning up the call stack via return instructions, and `Throw` indicates that each iteration terminated via a `throw new Exception()`.
+- `Task` vs `ValueTask` vs `Async2` - The benchmark has 3 functions which are written with the same C# code, but with different syntax using different async implementation strategies.
+With `Task`, the function signature is `async Task<long> RunTask(int depth)`
+With `ValueTask`, the function signature is `async ValueTask<long> RunTask(int depth)`
+With `Task`, the function signature is `async2 long RunTask(int depth)`
+
+When a graph does not specify one of the config options, the graph is a relative performance gathered by dividing the iters/250ms that the benchmark produces between the two options, and then multiplying by 100 to produce a percentage. 
+
+## Relative performance of throwing vs returning cleanly from a function at varying stack depths
+
+![RelativePerf](throwing-relative-performance.svg)
+This graph shows the performance of throwing relative to the performance of returning cleanly from a function with varying stack depths.
+There are 2 remarkable bits of data presented here.
+1. The cost of throwing is extraordinary if the application would otherwise be able to complete synchronously.
+2. The overhead of suspending an async function and transitioning to the thread pool is rather high.
+3. The performance of runtime handled async with regards to throwing is much faster than normal EH. In fact as the stack becomes deeper, the EH model presented by runtime generated async functions does not suffer as much of a performance penalty for walking the stack as our normal EH stack walker does.
+
+## Performance of normal async `async Task<T>` code vs runtime generated async functions
+![PerfOfThrowing](perf-of-throwing.svg)
+In this graph the raw performance of throwing at various stack depths is shown.
+We can see that the performance of `ValueTask` and `Task` is nearly identical, and differs only between whether or not the function suspended or not before throwing.
+In addition, we can see the different performance of throwing with `Async2`.
+Since the design of EH in `Async2` is able to avoid doing expensive operations as it walks a suspended stack, the performance of throwing with various stack depths while suspended is extremely fast, and dominated by the cost of the suspend operation.
+In constrast the non-suspended performance follows the same curve as the `Task` and `ValueTask` model, but is a constant factor faster, as `Async2` produces a smaller call stack, so the runtime's stack walker has substantially less work to do.
+
+![RelativePerf2](async-vs-async2-relative-performance.svg)
+
+In this graph, the performance of runtime async vs classic async is presented for the particular code in use.
+What we can see is that the performance of the new runtime generated async code is always faster for this code than the traditional code generation.
+Numbers above 100% indicate speedups.
+This higher performance is likely due to the relatively large size of the async function used in this benchmark.
+In addition, in this graph, only data out to a depth of 16 is presented, as the performance of throwing with new async at higher stack depths would make looking at the data for other depths difficult.
+(It reaches a level of 3851% of the performance of the traditional async function at a stack depth of 64). Not shown is the performance of `ValueTask` based async code. It is extremely close to that of the `Task` based variant.
+
+
+## Impact of try/finally regions on throw performance
+![ThrowPerfDifferingFinallys](throw-perf-with-different-number-of-finally-on-stack.svg)
+
+Much of the extraordinary performance of EH the runtime generated async prototype depends on the very fast stack walk that occurs when dispatching exceptions.
+Notably, in the runtime generated model we are able to easily skip frames which do not have any handlers in them with a simple flag check.
+This is contrary to IL compiler generated async, where the implementation generates a catch, even if the frame has no try block written in C#.
+
+We can see some very interesting details.
+1. In IL based async the cost of suspending once is dominated by the cost of throwing an exception, and so whether or not the function suspends, does not change the performance.
+2. In the NoSuspend case, for async2 we can see that the presence or abscence of a finally has a fairly small impact on throw performance.
+Most of the cost is taken up by other tasks such as stackwalking, creating the exception object, and the like.
+3. In the case where we DO suspend, the performance is quite different with async2.
+Without many finally blocks, we can see that the performance is really extraordinary, but as each frame gets a finally block, the cost of handling EH approaches that of the traditional async model.
+4. The performance of Task Throw NoSuspend and Task Throw Suspend is effectively the same.
+
+## Raw data from EH microbenchmark
+| Stack Depth | Task Return NoSuspend | ValueTask Return NoSuspend | Async2 Return NoSuspend | Task Throw NoSuspend | ValueTask Throw NoSuspend | Async2 Throw NoSuspend | Task Return Suspend | ValueTask Return Suspend | Async2 Return Suspend | Task Throw Suspend | ValueTask Throw Suspend | Async2 Throw Suspend |
+| ----- | --------------------- | -------------------------- | ----------------------- | -------------------- | ------------------------- | ---------------------- | ------------------- | ------------------------ | --------------------- | ------------------ | ----------------------- | -------------------- |
+| 1     | 7,976,995.00          | 7,994,790.00               | 13,966,110.00           | 21,630.00            | 21,789.00                 | 46,434.00              | 589,057.00          | 574,660.00               | 629,310.00            | 15,563.00          | 15,584.00               | 21,208.00            |
+| 2     | 4,738,359.00          | 5,036,613.00               | 11,573,294.00           | 13,781.00            | 13,359.00                 | 38,975.00              | 390,167.00          | 349,900.00               | 617,464.00            | 9,806.00           | 9,740.00                | 21,514.00            |
+| 4     | 2,884,153.00          | 2,924,123.00               | 8,340,047.00            | 7,717.00             | 7,477.00                  | 29,887.00              | 276,584.00          | 265,590.00               | 577,707.00            | 5,778.00           | 5,495.00                | 21,288.00            |
+| 8     | 1,274,335.00          | 1,324,960.00               | 5,308,395.00            | 4,068.00             | 4,001.00                  | 20,358.00              | 217,067.00          | 198,172.00               | 474,723.00            | 3,476.00           | 3,376.00                | 20,877.00            |
+| 16    | 536,064.00            | 533,731.00                 | 2,888,828.00            | 2,104.00             | 2,063.00                  | 12,941.00              | 132,636.00          | 106,605.00               | 315,191.00            | 1,912.00           | 1,909.00                | 20,496.00            |
+| 32    | 263,823.00            | 249,821.00                 | 1,157,832.00            | 1,032.00             | 989.00                    | 7,319.00               | 71,989.00           | 55,860.00                | 184,699.00            | 973.00             | 949.00                  | 19,379.00            |
+| 64    | 113,187.00            | 114,845.00                 | 488,245.00              | 484.00               | 473.00                    | 3,936.00               | 37,086.00           | 28,365.00                | 98,769.00             | 458.00             | 456.00                  | 17,565.00            |
+
+| Finally blocks on stack | Task Throw NoSuspend | Async2 Throw NoSuspend | Task Throw Suspend | Async2 Throw Suspend |
+| ----------------------- | -------------------- | ---------------------- | ------------------ | -------------------- |
+| 1                       | 1067                 | 7504                   | 965                | 20107                |
+| 2                       | 1032                 | 7441                   | 965                | 12140                |
+| 4                       | 1034                 | 7392                   | 959                | 7306                 |
+| 8                       | 1006                 | 7287                   | 928                | 4268                 |
+| 16                      | 972                  | 7045                   | 912                | 2307                 |
+| 32                      | 872                  | 6961                   | 857                | 1194                 |
 
 ## Scenario benchmarks
 
diff --git a/docs/design/features/throw-perf-with-different-number-of-finally-on-stack.svg b/docs/design/features/throw-perf-with-different-number-of-finally-on-stack.svg
new file mode 100644
index 00000000000000..59d27773c75f47
--- /dev/null
+++ b/docs/design/features/throw-perf-with-different-number-of-finally-on-stack.svg
@@ -0,0 +1 @@
+<svg width="2745" height="1360" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" overflow="hidden"><defs><clipPath id="clip0"><rect x="3101" y="5799" width="2745" height="1360"/></clipPath></defs><g clip-path="url(#clip0)" transform="translate(-3101 -5799)"><rect x="3102" y="5800" width="2742" height="1357" fill="#FFFFFF"/><g><path d="M3371.5 6672.72 5793.5 6672.72M3371.5 6497.71 5793.5 6497.71M3371.5 6321.71 5793.5 6321.71M3371.5 6145.7 5793.5 6145.7M3371.5 5970.5 5793.5 5970.5" stroke="#D9D9D9" stroke-width="3.4375" stroke-linejoin="round" stroke-miterlimit="10" fill="none"/></g><g><path d="M3371.5 6848.5 5793.5 6848.5" stroke="#D9D9D9" stroke-width="3.4375" stroke-linejoin="round" stroke-miterlimit="10" fill="none" fill-rule="evenodd"/></g><g><path d="M3573 6811 3977.13 6812.22 4380.14 6812.22 4784.16 6813.22 5188.17 6814.22 5591 6818" stroke="#156082" stroke-width="10.3125" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" fill="none"/></g><g><path d="M3584.27 6811.71C3584.27 6817.78 3579.35 6822.71 3573.27 6822.71 3567.2 6822.71 3562.27 6817.78 3562.27 6811.71 3562.27 6805.63 3567.2 6800.71 3573.27 6800.71 3579.35 6800.71 3584.27 6805.63 3584.27 6811.71Z" stroke="#156082" stroke-width="3.4375" stroke-linejoin="round" stroke-miterlimit="10" fill="#156082"/><path d="M3987.27 6812.71C3987.27 6818.78 3982.34 6823.71 3976.27 6823.71 3970.2 6823.71 3965.27 6818.78 3965.27 6812.71 3965.27 6806.63 3970.2 6801.71 3976.27 6801.71 3982.34 6801.71 3987.27 6806.63 3987.27 6812.71Z" stroke="#156082" stroke-width="3.4375" stroke-linejoin="round" stroke-miterlimit="10" fill="#156082"/><path d="M4391.27 6812.71C4391.27 6818.78 4386.34 6823.71 4380.27 6823.71 4374.19 6823.71 4369.27 6818.78 4369.27 6812.71 4369.27 6806.63 4374.19 6801.71 4380.27 6801.71 4386.34 6801.71 4391.27 6806.63 4391.27 6812.71Z" stroke="#156082" stroke-width="3.4375" stroke-linejoin="round" stroke-miterlimit="10" fill="#156082"/><path d="M4795.27 6813.71C4795.27 6819.78 4790.35 6824.71 4784.27 6824.71 4778.2 6824.71 4773.27 6819.78 4773.27 6813.71 4773.27 6807.63 4778.2 6802.71 4784.27 6802.71 4790.35 6802.71 4795.27 6807.63 4795.27 6813.71Z" stroke="#156082" stroke-width="3.4375" stroke-linejoin="round" stroke-miterlimit="10" fill="#156082"/><path d="M5198.27 6814.71C5198.27 6820.78 5193.35 6825.71 5187.27 6825.71 5181.2 6825.71 5176.27 6820.78 5176.27 6814.71 5176.27 6808.63 5181.2 6803.71 5187.27 6803.71 5193.35 6803.71 5198.27 6808.63 5198.27 6814.71Z" stroke="#156082" stroke-width="3.4375" stroke-linejoin="round" stroke-miterlimit="10" fill="#156082"/><path d="M5602.27 6818.71C5602.27 6824.78 5597.34 6829.71 5591.27 6829.71 5585.19 6829.71 5580.27 6824.78 5580.27 6818.71 5580.27 6812.63 5585.19 6807.71 5591.27 6807.71 5597.34 6807.71 5602.27 6812.63 5602.27 6818.71Z" stroke="#156082" stroke-width="3.4375" stroke-linejoin="round" stroke-miterlimit="10" fill="#156082"/></g><g><path d="M3573 6585 3977.13 6587.22 4380.14 6589.22 4784.16 6593.22 5188.17 6601.22 5591 6604" stroke="#E97132" stroke-width="10.3125" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" fill="none"/></g><g><path d="M3584.27 6585.71C3584.27 6591.78 3579.35 6596.71 3573.27 6596.71 3567.2 6596.71 3562.27 6591.78 3562.27 6585.71 3562.27 6579.63 3567.2 6574.71 3573.27 6574.71 3579.35 6574.71 3584.27 6579.63 3584.27 6585.71Z" stroke="#E97132" stroke-width="3.4375" stroke-linejoin="round" stroke-miterlimit="10" fill="#E97132"/><path d="M3987.27 6587.71C3987.27 6593.78 3982.34 6598.71 3976.27 6598.71 3970.2 6598.71 3965.27 6593.78 3965.27 6587.71 3965.27 6581.63 3970.2 6576.71 3976.27 6576.71 3982.34 6576.71 3987.27 6581.63 3987.27 6587.71Z" stroke="#E97132" stroke-width="3.4375" stroke-linejoin="round" stroke-miterlimit="10" fill="#E97132"/><path d="M4391.27 6589.71C4391.27 6595.78 4386.34 6600.71 4380.27 6600.71 4374.19 6600.71 4369.27 6595.78 4369.27 6589.71 4369.27 6583.63 4374.19 6578.71 4380.27 6578.71 4386.34 6578.71 4391.27 6583.63 4391.27 6589.71Z" stroke="#E97132" stroke-width="3.4375" stroke-linejoin="round" stroke-miterlimit="10" fill="#E97132"/><path d="M4795.27 6592.71C4795.27 6598.78 4790.35 6603.71 4784.27 6603.71 4778.2 6603.71 4773.27 6598.78 4773.27 6592.71 4773.27 6586.63 4778.2 6581.71 4784.27 6581.71 4790.35 6581.71 4795.27 6586.63 4795.27 6592.71Z" stroke="#E97132" stroke-width="3.4375" stroke-linejoin="round" stroke-miterlimit="10" fill="#E97132"/><path d="M5198.27 6601.71C5198.27 6607.78 5193.35 6612.71 5187.27 6612.71 5181.2 6612.71 5176.27 6607.78 5176.27 6601.71 5176.27 6595.63 5181.2 6590.71 5187.27 6590.71 5193.35 6590.71 5198.27 6595.63 5198.27 6601.71Z" stroke="#E97132" stroke-width="3.4375" stroke-linejoin="round" stroke-miterlimit="10" fill="#E97132"/><path d="M5602.27 6604.71C5602.27 6610.78 5597.34 6615.71 5591.27 6615.71 5585.19 6615.71 5580.27 6610.78 5580.27 6604.71 5580.27 6598.63 5585.19 6593.71 5591.27 6593.71 5597.34 6593.71 5602.27 6598.63 5602.27 6604.71Z" stroke="#E97132" stroke-width="3.4375" stroke-linejoin="round" stroke-miterlimit="10" fill="#E97132"/></g><g><path d="M3573 6815 3977.13 6815 4380.14 6815.22 4784.16 6816.22 5188.17 6817.22 5591 6819" stroke="#196B24" stroke-width="10.3125" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" fill="none"/></g><g><path d="M3584.27 6814.71C3584.27 6820.78 3579.35 6825.71 3573.27 6825.71 3567.2 6825.71 3562.27 6820.78 3562.27 6814.71 3562.27 6808.63 3567.2 6803.71 3573.27 6803.71 3579.35 6803.71 3584.27 6808.63 3584.27 6814.71Z" stroke="#196B24" stroke-width="3.4375" stroke-linejoin="round" stroke-miterlimit="10" fill="#196B24"/><path d="M3987.27 6814.71C3987.27 6820.78 3982.34 6825.71 3976.27 6825.71 3970.2 6825.71 3965.27 6820.78 3965.27 6814.71 3965.27 6808.63 3970.2 6803.71 3976.27 6803.71 3982.34 6803.71 3987.27 6808.63 3987.27 6814.71Z" stroke="#196B24" stroke-width="3.4375" stroke-linejoin="round" stroke-miterlimit="10" fill="#196B24"/><path d="M4391.27 6815.71C4391.27 6821.78 4386.34 6826.71 4380.27 6826.71 4374.19 6826.71 4369.27 6821.78 4369.27 6815.71 4369.27 6809.63 4374.19 6804.71 4380.27 6804.71 4386.34 6804.71 4391.27 6809.63 4391.27 6815.71Z" stroke="#196B24" stroke-width="3.4375" stroke-linejoin="round" stroke-miterlimit="10" fill="#196B24"/><path d="M4795.27 6816.71C4795.27 6822.78 4790.35 6827.71 4784.27 6827.71 4778.2 6827.71 4773.27 6822.78 4773.27 6816.71 4773.27 6810.63 4778.2 6805.71 4784.27 6805.71 4790.35 6805.71 4795.27 6810.63 4795.27 6816.71Z" stroke="#196B24" stroke-width="3.4375" stroke-linejoin="round" stroke-miterlimit="10" fill="#196B24"/><path d="M5198.27 6816.71C5198.27 6822.78 5193.35 6827.71 5187.27 6827.71 5181.2 6827.71 5176.27 6822.78 5176.27 6816.71 5176.27 6810.63 5181.2 6805.71 5187.27 6805.71 5193.35 6805.71 5198.27 6810.63 5198.27 6816.71Z" stroke="#196B24" stroke-width="3.4375" stroke-linejoin="round" stroke-miterlimit="10" fill="#196B24"/><path d="M5602.27 6818.71C5602.27 6824.78 5597.34 6829.71 5591.27 6829.71 5585.19 6829.71 5580.27 6824.78 5580.27 6818.71 5580.27 6812.63 5585.19 6807.71 5591.27 6807.71 5597.34 6807.71 5602.27 6812.63 5602.27 6818.71Z" stroke="#196B24" stroke-width="3.4375" stroke-linejoin="round" stroke-miterlimit="10" fill="#196B24"/></g><g><path d="M3573 6142 3977.13 6422.21 4380.14 6592.22 4784.16 6699.22 5188.17 6768.22 5591 6807" stroke="#0F9ED5" stroke-width="10.3125" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" fill="none"/></g><g><path d="M3584.27 6142.71C3584.27 6148.78 3579.35 6153.71 3573.27 6153.71 3567.2 6153.71 3562.27 6148.78 3562.27 6142.71 3562.27 6136.63 3567.2 6131.71 3573.27 6131.71 3579.35 6131.71 3584.27 6136.63 3584.27 6142.71Z" stroke="#0F9ED5" stroke-width="3.4375" stroke-linejoin="round" stroke-miterlimit="10" fill="#0F9ED5"/><path d="M3987.27 6422.71C3987.27 6428.78 3982.34 6433.71 3976.27 6433.71 3970.2 6433.71 3965.27 6428.78 3965.27 6422.71 3965.27 6416.63 3970.2 6411.71 3976.27 6411.71 3982.34 6411.71 3987.27 6416.63 3987.27 6422.71Z" stroke="#0F9ED5" stroke-width="3.4375" stroke-linejoin="round" stroke-miterlimit="10" fill="#0F9ED5"/><path d="M4391.27 6592.71C4391.27 6598.78 4386.34 6603.71 4380.27 6603.71 4374.19 6603.71 4369.27 6598.78 4369.27 6592.71 4369.27 6586.63 4374.19 6581.71 4380.27 6581.71 4386.34 6581.71 4391.27 6586.63 4391.27 6592.71Z" stroke="#0F9ED5" stroke-width="3.4375" stroke-linejoin="round" stroke-miterlimit="10" fill="#0F9ED5"/><path d="M4795.27 6698.71C4795.27 6704.78 4790.35 6709.71 4784.27 6709.71 4778.2 6709.71 4773.27 6704.78 4773.27 6698.71 4773.27 6692.63 4778.2 6687.71 4784.27 6687.71 4790.35 6687.71 4795.27 6692.63 4795.27 6698.71Z" stroke="#0F9ED5" stroke-width="3.4375" stroke-linejoin="round" stroke-miterlimit="10" fill="#0F9ED5"/><path d="M5198.27 6767.71C5198.27 6773.78 5193.35 6778.71 5187.27 6778.71 5181.2 6778.71 5176.27 6773.78 5176.27 6767.71 5176.27 6761.63 5181.2 6756.71 5187.27 6756.71 5193.35 6756.71 5198.27 6761.63 5198.27 6767.71Z" stroke="#0F9ED5" stroke-width="3.4375" stroke-linejoin="round" stroke-miterlimit="10" fill="#0F9ED5"/><path d="M5602.27 6806.71C5602.27 6812.78 5597.34 6817.71 5591.27 6817.71 5585.19 6817.71 5580.27 6812.78 5580.27 6806.71 5580.27 6800.63 5585.19 6795.71 5591.27 6795.71 5597.34 6795.71 5602.27 6800.63 5602.27 6806.71Z" stroke="#0F9ED5" stroke-width="3.4375" stroke-linejoin="round" stroke-miterlimit="10" fill="#0F9ED5"/></g><g><text fill="#595959" font-family="Aptos Narrow,Aptos Narrow_MSFontService,sans-serif" font-weight="400" font-stretch="semi-condensed" font-size="41" transform="matrix(1 0 0 1 3312.35 6860)">0</text></g><g><text fill="#595959" font-family="Aptos Narrow,Aptos Narrow_MSFontService,sans-serif" font-weight="400" font-stretch="semi-condensed" font-size="41" transform="matrix(1 0 0 1 3249.65 6685)">5000</text></g><g><text fill="#595959" font-family="Aptos Narrow,Aptos Narrow_MSFontService,sans-serif" font-weight="400" font-stretch="semi-condensed" font-size="41" transform="matrix(1 0 0 1 3228.75 6509)">10000</text></g><g><text fill="#595959" font-family="Aptos Narrow,Aptos Narrow_MSFontService,sans-serif" font-weight="400" font-stretch="semi-condensed" font-size="41" transform="matrix(1 0 0 1 3228.75 6333)">15000</text></g><g><text fill="#595959" font-family="Aptos Narrow,Aptos Narrow_MSFontService,sans-serif" font-weight="400" font-stretch="semi-condensed" font-size="41" transform="matrix(1 0 0 1 3228.75 6157)">20000</text></g><g><text fill="#595959" font-family="Aptos Narrow,Aptos Narrow_MSFontService,sans-serif" font-weight="400" font-stretch="semi-condensed" font-size="41" transform="matrix(1 0 0 1 3228.75 5982)">25000</text></g><g><text fill="#595959" font-family="Aptos Narrow,Aptos Narrow_MSFontService,sans-serif" font-weight="400" font-stretch="semi-condensed" font-size="41" transform="matrix(1 0 0 1 3562.72 6914)">1</text></g><g><text fill="#595959" font-family="Aptos Narrow,Aptos Narrow_MSFontService,sans-serif" font-weight="400" font-stretch="semi-condensed" font-size="41" transform="matrix(1 0 0 1 3966.38 6914)">2</text></g><g><text fill="#595959" font-family="Aptos Narrow,Aptos Narrow_MSFontService,sans-serif" font-weight="400" font-stretch="semi-condensed" font-size="41" transform="matrix(1 0 0 1 4370.05 6914)">4</text></g><g><text fill="#595959" font-family="Aptos Narrow,Aptos Narrow_MSFontService,sans-serif" font-weight="400" font-stretch="semi-condensed" font-size="41" transform="matrix(1 0 0 1 4773.72 6914)">8</text></g><g><text fill="#595959" font-family="Aptos Narrow,Aptos Narrow_MSFontService,sans-serif" font-weight="400" font-stretch="semi-condensed" font-size="41" transform="matrix(1 0 0 1 5166.93 6914)">16</text></g><g><text fill="#595959" font-family="Aptos Narrow,Aptos Narrow_MSFontService,sans-serif" font-weight="400" font-stretch="semi-condensed" font-size="41" transform="matrix(1 0 0 1 5570.6 6914)">32</text></g><g><text fill="#595959" font-family="Aptos Narrow,Aptos Narrow_MSFontService,sans-serif" font-weight="400" font-stretch="semi-condensed" font-size="46" transform="matrix(6.12323e-17 -1 1 6.12323e-17 3202.05 6524)">iters/250 ms </text></g><g><text fill="#595959" font-family="Aptos Narrow,Aptos Narrow_MSFontService,sans-serif" font-weight="400" font-stretch="semi-condensed" font-size="46" transform="matrix(1 0 0 1 4188.35 6982)">Number of try/finally blocks thrown through</text></g><g><text fill="#595959" font-family="Aptos Narrow,Aptos Narrow_MSFontService,sans-serif" font-weight="400" font-stretch="semi-condensed" font-size="64" transform="matrix(1 0 0 1 3836.32 5895)">Effect of try/finally handlers on throw performance</text></g><g><path d="M3390 7091 3478 7091" stroke="#156082" stroke-width="10.3125" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" fill="none" fill-rule="evenodd"/></g><g><path d="M3445 7090C3445 7096.07 3440.07 7101 3434 7101 3427.92 7101 3423 7096.07 3423 7090 3423 7083.92 3427.92 7079 3434 7079 3440.07 7079 3445 7083.92 3445 7090Z" stroke="#156082" stroke-width="3.4375" stroke-linejoin="round" stroke-miterlimit="10" fill="#156082"/></g><g><text fill="#595959" font-family="Aptos Narrow,Aptos Narrow_MSFontService,sans-serif" font-weight="400" font-stretch="semi-condensed" font-size="41" transform="matrix(1 0 0 1 3487.76 7102)">Task Throw NoSuspend</text></g><g><path d="M3960 7091 4048 7091" stroke="#E97132" stroke-width="10.3125" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" fill="none" fill-rule="evenodd"/></g><g><path d="M4015.02 7090C4015.02 7096.07 4010.31 7101 4004.5 7101 3998.69 7101 3993.98 7096.07 3993.98 7090 3993.98 7083.92 3998.69 7079 4004.5 7079 4010.31 7079 4015.02 7083.92 4015.02 7090Z" stroke="#E97132" stroke-width="3.4375" stroke-linejoin="round" stroke-miterlimit="10" fill="#E97132"/></g><g><text fill="#595959" font-family="Aptos Narrow,Aptos Narrow_MSFontService,sans-serif" font-weight="400" font-stretch="semi-condensed" font-size="41" transform="matrix(1 0 0 1 4057.34 7102)">Async2 Throw NoSuspend</text></g><g><path d="M4574 7091 4662 7091" stroke="#196B24" stroke-width="10.3125" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" fill="none" fill-rule="evenodd"/></g><g><path d="M4628 7090C4628 7096.07 4623.08 7101 4617 7101 4610.92 7101 4606 7096.07 4606 7090 4606 7083.92 4610.92 7079 4617 7079 4623.08 7079 4628 7083.92 4628 7090Z" stroke="#196B24" stroke-width="3.4375" stroke-linejoin="round" stroke-miterlimit="10" fill="#196B24"/></g><g><text fill="#595959" font-family="Aptos Narrow,Aptos Narrow_MSFontService,sans-serif" font-weight="400" font-stretch="semi-condensed" font-size="41" transform="matrix(1 0 0 1 4671.06 7102)">Task Throw Suspend</text></g><g><path d="M5096 7091 5184 7091" stroke="#0F9ED5" stroke-width="10.3125" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" fill="none" fill-rule="evenodd"/></g><g><path d="M5150 7090C5150 7096.07 5145.08 7101 5139 7101 5132.92 7101 5128 7096.07 5128 7090 5128 7083.92 5132.92 7079 5139 7079 5145.08 7079 5150 7083.92 5150 7090Z" stroke="#0F9ED5" stroke-width="3.4375" stroke-linejoin="round" stroke-miterlimit="10" fill="#0F9ED5"/></g><g><text fill="#595959" font-family="Aptos Narrow,Aptos Narrow_MSFontService,sans-serif" font-weight="400" font-stretch="semi-condensed" font-size="41" transform="matrix(1 0 0 1 5193.23 7102)">Async2 Throw Suspend</text></g><rect x="3102.5" y="5800.5" width="2742" height="1357" stroke="#D9D9D9" stroke-width="3.4375" stroke-linejoin="round" stroke-miterlimit="10" fill="none"/></g></svg>
\ No newline at end of file
diff --git a/docs/design/features/throwing-relative-performance.svg b/docs/design/features/throwing-relative-performance.svg
new file mode 100644
index 00000000000000..521e9086fd6929
--- /dev/null
+++ b/docs/design/features/throwing-relative-performance.svg
@@ -0,0 +1 @@
+<svg width="2893" height="1563" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" overflow="hidden"><defs><clipPath id="clip0"><rect x="736" y="1337" width="2893" height="1563"/></clipPath></defs><g clip-path="url(#clip0)" transform="translate(-736 -1337)"><rect x="737" y="1338" width="2890" height="1560" fill="#FFFFFF"/><g><path d="M1027.5 2481.58 3576.5 2481.58M1027.5 2373.58 3576.5 2373.58M1027.5 2265.57 3576.5 2265.57M1027.5 2157.57 3576.5 2157.57M1027.5 2049.57 3576.5 2049.57M1027.5 1940.56 3576.5 1940.56M1027.5 1832.56 3576.5 1832.56M1027.5 1724.56 3576.5 1724.56M1027.5 1616.55 3576.5 1616.55M1027.5 1508.5 3576.5 1508.5" stroke="#D9D9D9" stroke-width="3.4375" stroke-linejoin="round" stroke-miterlimit="10" fill="none"/></g><g><path d="M1069.04 2575.08 1107.04 2575.08 1107.04 2590 1069.04 2590ZM1433.05 2574.08 1472.05 2574.08 1472.05 2590 1433.05 2590ZM1797.06 2575.08 1836.06 2575.08 1836.06 2590 1797.06 2590ZM2162.07 2572.08 2200.07 2572.08 2200.07 2590 2162.07 2590ZM2526.08 2569.08 2564.08 2569.08 2564.08 2590 2526.08 2590ZM2890.09 2569.08 2928.1 2569.08 2928.1 2590 2890.09 2590ZM3254.11 2567.08 3292.11 2567.08 3292.11 2590 3254.11 2590Z" fill="#156082"/></g><g><path d="M1118.04 2575.08 1156.04 2575.08 1156.04 2590 1118.04 2590ZM1482.05 2575.08 1520.05 2575.08 1520.05 2590 1482.05 2590ZM1846.06 2576.08 1884.06 2576.08 1884.06 2590 1846.06 2590ZM2210.07 2573.08 2248.07 2573.08 2248.07 2590 2210.07 2590ZM2574.08 2569.08 2612.09 2569.08 2612.09 2590 2574.08 2590ZM2938.1 2568.08 2976.1 2568.08 2976.1 2590 2938.1 2590ZM3302.11 2567.08 3340.11 2567.08 3340.11 2590 3302.11 2590Z" fill="#E97132"/></g><g><path d="M1166.04 2572.08 1204.04 2572.08 1204.04 2590 1166.04 2590ZM1530.05 2572.08 1568.05 2572.08 1568.05 2590 1530.05 2590ZM1894.06 2570.08 1933.06 2570.08 1933.06 2590 1894.06 2590ZM2258.07 2569.08 2297.08 2569.08 2297.08 2590 2258.07 2590ZM2623.09 2566.08 2661.09 2566.08 2661.09 2590 2623.09 2590ZM2987.1 2556.08 3025.1 2556.08 3025.1 2590 2987.1 2590ZM3351.11 2546.08 3389.11 2546.08 3389.11 2590 3351.11 2590Z" fill="#196B24"/></g><g><path d="M1215.04 2447.08 1253.04 2447.08 1253.04 2590 1215.04 2590ZM1579.05 2454.08 1617.05 2454.08 1617.05 2590 1579.05 2590ZM1943.06 2477.08 1981.06 2477.08 1981.06 2590 1943.06 2590ZM2307.08 2503.08 2345.08 2503.08 2345.08 2590 2307.08 2590ZM2671.09 2512.08 2709.09 2512.08 2709.09 2590 2671.09 2590ZM3035.1 2517.08 3073.1 2517.08 3073.1 2590 3035.1 2590ZM3399.11 2523.08 3437.11 2523.08 3437.11 2590 3399.11 2590Z" fill="#0F9ED5"/></g><g><path d="M1263.04 2443.08 1301.04 2443.08 1301.04 2590 1263.04 2590ZM1627.05 2439.08 1665.05 2439.08 1665.05 2590 1627.05 2590ZM1991.07 2478.08 2029.07 2478.08 2029.07 2590 1991.07 2590ZM2355.08 2498.08 2394.08 2498.08 2394.08 2590 2355.08 2590ZM2719.09 2493.08 2758.09 2493.08 2758.09 2590 2719.09 2590ZM3084.1 2498.08 3122.1 2498.08 3122.1 2590 3084.1 2590ZM3448.11 2503.08 3486.11 2503.08 3486.11 2590 3448.11 2590Z" fill="#A02B93"/></g><g><path d="M1312.04 2408.08 1350.04 2408.08 1350.04 2590 1312.04 2590ZM1676.05 2401.08 1714.06 2401.08 1714.06 2590 1676.05 2590ZM2040.07 2390.08 2078.07 2390.08 2078.07 2590 2040.07 2590ZM2404.08 2352.08 2442.08 2352.08 2442.08 2590 2404.08 2590ZM2768.09 2238.07 2806.09 2238.07 2806.09 2590 2768.09 2590ZM3132.1 2022.07 3170.1 2022.07 3170.1 2590 3132.1 2590ZM3496.11 1628.05 3534.12 1628.05 3534.12 2590 3496.11 2590Z" fill="#4EA72E"/></g><g><path d="M1027.5 2589.5 3576.5 2589.5" stroke="#D9D9D9" stroke-width="3.4375" stroke-linejoin="round" stroke-miterlimit="10" fill="none" fill-rule="evenodd"/></g><g><text fill="#595959" font-family="Aptos Narrow,Aptos Narrow_MSFontService,sans-serif" font-weight="400" font-stretch="semi-condensed" font-size="41" transform="matrix(1 0 0 1 884.652 2601)">0.00%</text></g><g><text fill="#595959" font-family="Aptos Narrow,Aptos Narrow_MSFontService,sans-serif" font-weight="400" font-stretch="semi-condensed" font-size="41" transform="matrix(1 0 0 1 884.652 2493)">2.00%</text></g><g><text fill="#595959" font-family="Aptos Narrow,Aptos Narrow_MSFontService,sans-serif" font-weight="400" font-stretch="semi-condensed" font-size="41" transform="matrix(1 0 0 1 884.652 2385)">4.00%</text></g><g><text fill="#595959" font-family="Aptos Narrow,Aptos Narrow_MSFontService,sans-serif" font-weight="400" font-stretch="semi-condensed" font-size="41" transform="matrix(1 0 0 1 884.652 2277)">6.00%</text></g><g><text fill="#595959" font-family="Aptos Narrow,Aptos Narrow_MSFontService,sans-serif" font-weight="400" font-stretch="semi-condensed" font-size="41" transform="matrix(1 0 0 1 884.652 2169)">8.00%</text></g><g><text fill="#595959" font-family="Aptos Narrow,Aptos Narrow_MSFontService,sans-serif" font-weight="400" font-stretch="semi-condensed" font-size="41" transform="matrix(1 0 0 1 863.752 2061)">10.00%</text></g><g><text fill="#595959" font-family="Aptos Narrow,Aptos Narrow_MSFontService,sans-serif" font-weight="400" font-stretch="semi-condensed" font-size="41" transform="matrix(1 0 0 1 863.752 1952)">12.00%</text></g><g><text fill="#595959" font-family="Aptos Narrow,Aptos Narrow_MSFontService,sans-serif" font-weight="400" font-stretch="semi-condensed" font-size="41" transform="matrix(1 0 0 1 863.752 1844)">14.00%</text></g><g><text fill="#595959" font-family="Aptos Narrow,Aptos Narrow_MSFontService,sans-serif" font-weight="400" font-stretch="semi-condensed" font-size="41" transform="matrix(1 0 0 1 863.752 1736)">16.00%</text></g><g><text fill="#595959" font-family="Aptos Narrow,Aptos Narrow_MSFontService,sans-serif" font-weight="400" font-stretch="semi-condensed" font-size="41" transform="matrix(1 0 0 1 863.752 1628)">18.00%</text></g><g><text fill="#595959" font-family="Aptos Narrow,Aptos Narrow_MSFontService,sans-serif" font-weight="400" font-stretch="semi-condensed" font-size="41" transform="matrix(1 0 0 1 863.752 1520)">20.00%</text></g><g><text fill="#595959" font-family="Aptos Narrow,Aptos Narrow_MSFontService,sans-serif" font-weight="400" font-stretch="semi-condensed" font-size="41" transform="matrix(1 0 0 1 1199.13 2655)">1</text></g><g><text fill="#595959" font-family="Aptos Narrow,Aptos Narrow_MSFontService,sans-serif" font-weight="400" font-stretch="semi-condensed" font-size="41" transform="matrix(1 0 0 1 1563.21 2655)">2</text></g><g><text fill="#595959" font-family="Aptos Narrow,Aptos Narrow_MSFontService,sans-serif" font-weight="400" font-stretch="semi-condensed" font-size="41" transform="matrix(1 0 0 1 1927.3 2655)">4</text></g><g><text fill="#595959" font-family="Aptos Narrow,Aptos Narrow_MSFontService,sans-serif" font-weight="400" font-stretch="semi-condensed" font-size="41" transform="matrix(1 0 0 1 2291.39 2655)">8</text></g><g><text fill="#595959" font-family="Aptos Narrow,Aptos Narrow_MSFontService,sans-serif" font-weight="400" font-stretch="semi-condensed" font-size="41" transform="matrix(1 0 0 1 2645.03 2655)">16</text></g><g><text fill="#595959" font-family="Aptos Narrow,Aptos Narrow_MSFontService,sans-serif" font-weight="400" font-stretch="semi-condensed" font-size="41" transform="matrix(1 0 0 1 3009.11 2655)">32</text></g><g><text fill="#595959" font-family="Aptos Narrow,Aptos Narrow_MSFontService,sans-serif" font-weight="400" font-stretch="semi-condensed" font-size="41" transform="matrix(1 0 0 1 3373.2 2655)">64</text></g><g><text fill="#595959" font-family="Aptos Narrow,Aptos Narrow_MSFontService,sans-serif" font-weight="400" font-stretch="semi-condensed" font-size="46" transform="matrix(6.12323e-17 -1 1 6.12323e-17 837.054 2472)">iters/s  wirth throw divided by iter/s  with return</text></g><g><text fill="#595959" font-family="Aptos Narrow,Aptos Narrow_MSFontService,sans-serif" font-weight="400" font-stretch="semi-condensed" font-size="46" transform="matrix(1 0 0 1 2190.75 2723)">Stack Depth</text></g><g><text fill="#595959" font-family="Aptos Narrow,Aptos Narrow_MSFontService,sans-serif" font-weight="400" font-stretch="semi-condensed" font-size="64" transform="matrix(1 0 0 1 1509.65 1433)">Relative performance of throwing vs returning cleanly</text></g><g><rect x="1026" y="2820" width="23" height="23" fill="#156082"/></g><g><text fill="#595959" font-family="Aptos Narrow,Aptos Narrow_MSFontService,sans-serif" font-weight="400" font-stretch="semi-condensed" font-size="41" transform="matrix(1 0 0 1 1058.41 2843)">Task NoSuspend</text></g><g><rect x="1404" y="2820" width="23" height="23" fill="#E97132"/></g><g><text fill="#595959" font-family="Aptos Narrow,Aptos Narrow_MSFontService,sans-serif" font-weight="400" font-stretch="semi-condensed" font-size="41" transform="matrix(1 0 0 1 1436.23 2843)">ValueTask NoSuspend</text></g><g><rect x="1875" y="2820" width="22.9996" height="23" fill="#196B24"/></g><g><text fill="#595959" font-family="Aptos Narrow,Aptos Narrow_MSFontService,sans-serif" font-weight="400" font-stretch="semi-condensed" font-size="41" transform="matrix(1 0 0 1 1907.12 2843)">Async2 NoSuspend</text></g><g><rect x="2297" y="2820" width="22" height="23" fill="#0F9ED5"/></g><g><text fill="#595959" font-family="Aptos Narrow,Aptos Narrow_MSFontService,sans-serif" font-weight="400" font-stretch="semi-condensed" font-size="41" transform="matrix(1 0 0 1 2329.08 2843)">Task Suspend</text></g><g><rect x="2627" y="2820" width="23" height="23" fill="#A02B93"/></g><g><text fill="#595959" font-family="Aptos Narrow,Aptos Narrow_MSFontService,sans-serif" font-weight="400" font-stretch="semi-condensed" font-size="41" transform="matrix(1 0 0 1 2659.48 2843)">ValueTask Suspend</text></g><g><rect x="3051" y="2820" width="22" height="23" fill="#4EA72E"/></g><g><text fill="#595959" font-family="Aptos Narrow,Aptos Narrow_MSFontService,sans-serif" font-weight="400" font-stretch="semi-condensed" font-size="41" transform="matrix(1 0 0 1 3082.95 2843)">Async2 Suspend</text></g><rect x="737.5" y="1338.5" width="2890" height="1560" stroke="#D9D9D9" stroke-width="3.4375" stroke-linejoin="round" stroke-miterlimit="10" fill="none"/></g></svg>
\ No newline at end of file
diff --git a/src/tests/Loader/async/async2-eh-microbench.cs b/src/tests/Loader/async/async2-eh-microbench.cs
index a750c194fd21dd..e66bb56ceb5ec2 100644
--- a/src/tests/Loader/async/async2-eh-microbench.cs
+++ b/src/tests/Loader/async/async2-eh-microbench.cs
@@ -39,7 +39,11 @@ public static async Task AsyncEntry()
         int finallyRate = args.Length > 3 ? int.Parse(args[3]) : 1000;
         Console.WriteLine("With a try/finally block every {0} frames", finallyRate);
 
-        Benchmark warmupBm = new Benchmark(5, 5, 2);
+        // Throw or return
+        bool throwOrReturn = args.Length > 4 ? String.Equals(args[4], "throw", StringComparison.OrdinalIgnoreCase) : true;
+        Console.WriteLine($"Which will {(throwOrReturn ? "throw" : "return")} when finished yielding");
+
+        Benchmark warmupBm = new Benchmark(5, 5, 2, throwOrReturn);
         warmupBm.Warmup = true;
         for (int i = 0; i < 4; i++)
         {
@@ -55,22 +59,22 @@ public static async Task AsyncEntry()
         }
 
         Console.WriteLine("Warmup done, running benchmark");
-        await RunBench(yieldFrequency, depth, finallyRate, "Async2");
-        await RunBench(yieldFrequency, depth, finallyRate, "Task");
-        await RunBench(yieldFrequency, depth, finallyRate, "ValueTask");
+        await RunBench(yieldFrequency, depth, finallyRate, throwOrReturn, "Async2");
+        await RunBench(yieldFrequency, depth, finallyRate, throwOrReturn, "Task");
+        await RunBench(yieldFrequency, depth, finallyRate, throwOrReturn, "ValueTask");
     }
 
-    private static async Task RunBench(int yieldCount, int depth, int finallyRate, string type)
+    private static async Task RunBench(int yieldCount, int depth, int finallyRate, bool throwOrReturn, string type)
     {
 
-        Benchmark bm = new(yieldCount, depth, finallyRate);
+        Benchmark bm = new(yieldCount, depth, finallyRate, throwOrReturn);
         Console.WriteLine($"Running benchmark on '{type}' methods");
 
         List<long> results = new();
         for (int i = 0; i < 16; i++)
         {
             long numIters = await bm.Run(type);
-            Console.WriteLine($"iters={numIters}");
+//            Console.WriteLine($"iters={numIters}");
             results.Add(numIters);
         }
 
@@ -84,14 +88,16 @@ private class Benchmark
         private readonly int _yieldCount;
         private readonly int _depth;
         private readonly int _finallyRate;
+        private readonly bool _throwOrReturn;
         public int Sink;
         public bool Warmup;
 
-        public Benchmark(int yieldCount, int depth, int finallyRate)
+        public Benchmark(int yieldCount, int depth, int finallyRate, bool throwOrReturn)
         {
             _yieldCount = yieldCount;
             _depth = depth;
             _finallyRate = finallyRate;
+            _throwOrReturn = throwOrReturn;
         }
 
         public async2 long Run(string type)
@@ -121,14 +127,16 @@ public async Task<long> RunTask(int depth)
                     await Task.Yield();
                 }
 
-                throw new Exception();
+                if (_throwOrReturn)
+                    throw new Exception();
+                return 8375983;
             }
 
             long result = 0;
 
             if (depth == _depth)
             {
-                int time = Warmup ? 5 : 500;
+                int time = Warmup ? 5 : 250;
 
                 Stopwatch timer = Stopwatch.StartNew();
 
@@ -184,14 +192,16 @@ public async ValueTask<long> RunValueTask(int depth)
                     await Task.Yield();
                 }
 
-                throw new Exception();
+                if (_throwOrReturn)
+                    throw new Exception();
+                return 8375983;
             }
 
             long result = 0;
 
             if (depth == _depth)
             {
-                int time = Warmup ? 5 : 500;
+                int time = Warmup ? 5 : 250;
 
                 Stopwatch timer = Stopwatch.StartNew();
 
@@ -248,14 +258,16 @@ public async2 long RunAsync2(int depth)
                     await Task.Yield();
                 }
 
-                throw new Exception();
+                if (_throwOrReturn)
+                    throw new Exception();
+                return 8375983;
             }
 
             long result = 0;
 
             if (depth == _depth)
             {
-                int time = Warmup ? 5 : 500;
+                int time = Warmup ? 5 : 250;
 
                 Stopwatch timer = Stopwatch.StartNew();
 

From 5ba6888b6bc5f3f3ca52e6f7a626d9217afb693c Mon Sep 17 00:00:00 2001
From: Vladimir Sadov <vsadov@microsoft.com>
Date: Mon, 13 Nov 2023 19:29:29 +0000
Subject: [PATCH 079/203] Merged PR 35196: Switching to the new syntax

As discussed, we will now use:

  `public static async2 Task<int> Foo()` syntax.

That is basically the same as async1, just with a different modifier.

As a part of this also couple bugs got fixed, mostly because dealing with generics now uses the same code as async1 uses.
---
 buildroslynnugets.cmd                          |  4 ++--
 eng/Versions.props                             |  6 +++---
 .../CompatibilitySuppressions.xml              |  6 ------
 .../src/CompatibilitySuppressions.xml          |  8 +-------
 .../System.Private.CoreLib.Shared.projitems    |  1 -
 .../ValueTaskAsyncAttribute.cs                 | 18 ------------------
 .../System.Runtime/ref/System.Runtime.cs       |  5 -----
 src/tests/Loader/async/async2-eh-microbench.cs |  4 ++--
 src/tests/Loader/async/async2-returns.cs       | 10 +++++-----
 .../Loader/async/async2-varying-yields.cs      |  6 +++---
 src/tests/Loader/async/async2-with-yields.cs   |  4 ++--
 .../Loader/async/async2-without-yields.cs      |  7 +++++--
 .../async/async2fibonacci-with-yields.cs       |  4 ++--
 .../async/async2fibonacci-without-yields.cs    |  4 ++--
 src/tests/Loader/async/async2implement.cs      |  6 +++---
 src/tests/Loader/async/async2object.cs         |  2 +-
 .../Loader/async/async2objectsCaptured.cs      |  2 +-
 src/tests/Loader/async/async2override.cs       |  6 +++---
 src/tests/Loader/async/async2sharedgeneric.cs  |  6 +++++-
 src/tests/Loader/async/async2valuetask.cs      |  3 +--
 src/tests/Loader/async/async2void.cs           |  2 +-
 .../Loader/async/cse-array-index-byref.cs      |  4 ++--
 src/tests/Loader/async/simple-eh.cs            |  4 ++--
 23 files changed, 46 insertions(+), 76 deletions(-)
 delete mode 100644 src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/ValueTaskAsyncAttribute.cs

diff --git a/buildroslynnugets.cmd b/buildroslynnugets.cmd
index 9a6e68c96776b3..e097dccf488e87 100644
--- a/buildroslynnugets.cmd
+++ b/buildroslynnugets.cmd
@@ -1,14 +1,14 @@
 setlocal ENABLEEXTENSIONS
 pushd %~dp0
 
-set ASYNC_SUFFIX=async-6
+set ASYNC_SUFFIX=async-7
 
 cd ..
 pushd dotnet-roslyn
 
 git fetch AzDo dev/vsadov/a2
 rem when updating this, make sure to update the ASYNC_SUFFIX above and the versions.props file
-git checkout facd968bc0e0c2889c78d4f121c5d1d39c3d6566
+git checkout 046d22fad72ac726d36d93b0e9d186574e77207b
 
 call restore.cmd
 call build.cmd -c release
diff --git a/eng/Versions.props b/eng/Versions.props
index 21897be218e3ca..9b918bbd541c5c 100644
--- a/eng/Versions.props
+++ b/eng/Versions.props
@@ -39,9 +39,9 @@
       Any tools that contribute to the design-time experience should use the MicrosoftCodeAnalysisVersion_LatestVS property above to ensure
       they do not break the local dev experience.
     -->
-    <MicrosoftCodeAnalysisCSharpVersion>4.9.0-async-6</MicrosoftCodeAnalysisCSharpVersion>
-    <MicrosoftCodeAnalysisVersion>4.9.0-async-6</MicrosoftCodeAnalysisVersion>
-    <MicrosoftNetCompilersToolsetVersion>4.9.0-async-6</MicrosoftNetCompilersToolsetVersion>
+    <MicrosoftCodeAnalysisCSharpVersion>4.9.0-async-7</MicrosoftCodeAnalysisCSharpVersion>
+    <MicrosoftCodeAnalysisVersion>4.9.0-async-7</MicrosoftCodeAnalysisVersion>
+    <MicrosoftNetCompilersToolsetVersion>4.9.0-async-7</MicrosoftNetCompilersToolsetVersion>
   </PropertyGroup>
   <!--
     For source generator support we need to target multiple versions of Roslyn in order to be able to run on older versions of Roslyn.
diff --git a/src/coreclr/System.Private.CoreLib/CompatibilitySuppressions.xml b/src/coreclr/System.Private.CoreLib/CompatibilitySuppressions.xml
index 8ade8e2a551216..c5b953e6254f77 100644
--- a/src/coreclr/System.Private.CoreLib/CompatibilitySuppressions.xml
+++ b/src/coreclr/System.Private.CoreLib/CompatibilitySuppressions.xml
@@ -1,12 +1,6 @@
 ﻿<?xml version="1.0" encoding="utf-8"?>
 <!-- https://learn.microsoft.com/en-us/dotnet/fundamentals/package-validation/diagnostic-ids -->
 <Suppressions xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
-  <Suppression>
-    <DiagnosticId>CP0009</DiagnosticId>
-    <Target>T:System.Runtime.CompilerServices.ValueTaskAsyncAttribute</Target>
-    <Left>ref/net9.0/System.Private.CoreLib.dll</Left>
-    <Right>lib/net9.0/System.Private.CoreLib.dll</Right>
-  </Suppression>
   <Suppression>
     <DiagnosticId>CP0015</DiagnosticId>
     <Target>M:System.Diagnostics.Tracing.EventSource.Write``1(System.String,``0):[T:System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute]</Target>
diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/CompatibilitySuppressions.xml b/src/coreclr/nativeaot/System.Private.CoreLib/src/CompatibilitySuppressions.xml
index 88c94b8826a69b..9c7bffdba74162 100644
--- a/src/coreclr/nativeaot/System.Private.CoreLib/src/CompatibilitySuppressions.xml
+++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/CompatibilitySuppressions.xml
@@ -973,12 +973,6 @@
     <Left>ref/net9.0/System.Private.CoreLib.dll</Left>
     <Right>lib/net9.0/System.Private.CoreLib.dll</Right>
   </Suppression>
-  <Suppression>
-    <DiagnosticId>CP0009</DiagnosticId>
-    <Target>T:System.Runtime.CompilerServices.ValueTaskAsyncAttribute</Target>
-    <Left>ref/net9.0/System.Private.CoreLib.dll</Left>
-    <Right>lib/net9.0/System.Private.CoreLib.dll</Right>
-  </Suppression>
   <Suppression>
     <DiagnosticId>CP0014</DiagnosticId>
     <Target>M:System.Runtime.CompilerServices.RuntimeHelpers.GetObjectValue(System.Object)-&gt;object?:[T:System.Diagnostics.CodeAnalysis.NotNullIfNotNullAttribute]</Target>
@@ -1011,4 +1005,4 @@
     <DiagnosticId>CP0015</DiagnosticId>
     <Target>M:System.Diagnostics.Tracing.EventSource.WriteEventWithRelatedActivityIdCore(System.Int32,System.Guid*,System.Int32,System.Diagnostics.Tracing.EventSource.EventData*):[T:System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute]</Target>
   </Suppression>
-</Suppressions>
\ No newline at end of file
+</Suppressions>
diff --git a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems
index d8c11d553d55a2..1415513eb12bfe 100644
--- a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems
+++ b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems
@@ -890,7 +890,6 @@
     <Compile Include="$(MSBuildThisFileDirectory)System\Runtime\CompilerServices\UnsafeAccessorAttribute.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\Runtime\CompilerServices\UnsafeValueTypeAttribute.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\Runtime\CompilerServices\ValueTaskAwaiter.cs" />
-    <Compile Include="$(MSBuildThisFileDirectory)System\Runtime\CompilerServices\ValueTaskAsyncAttribute.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\Runtime\CompilerServices\YieldAwaitable.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\Runtime\ConstrainedExecution\Cer.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\Runtime\ConstrainedExecution\Consistency.cs" />
diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/ValueTaskAsyncAttribute.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/ValueTaskAsyncAttribute.cs
deleted file mode 100644
index bfe750bf836f7a..00000000000000
--- a/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/ValueTaskAsyncAttribute.cs
+++ /dev/null
@@ -1,18 +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.Runtime.CompilerServices
-{
-    /// <summary>
-    /// Specifies that the async2 function in its promise-returning form should be returning ValueTask/ValueTask`1
-    ///
-    /// The attribute is source-only and is not supposed to be emitted into metadata, where it has no meaning.
-    /// TODO: (vsadov) I have a lot of doubts that this is the best possible design to support ValueTask in async2.
-    ///       While good enough to unblock working with ValueTask async2, we should think more about the syntax.
-    /// </summary>
-    [AttributeUsage(AttributeTargets.Method)]
-    public class ValueTaskAsyncAttribute : Attribute
-    {
-        public ValueTaskAsyncAttribute() { }
-    }
-}
diff --git a/src/libraries/System.Runtime/ref/System.Runtime.cs b/src/libraries/System.Runtime/ref/System.Runtime.cs
index 54bd77d4fe502a..72ef20958c50aa 100644
--- a/src/libraries/System.Runtime/ref/System.Runtime.cs
+++ b/src/libraries/System.Runtime/ref/System.Runtime.cs
@@ -13299,11 +13299,6 @@ public sealed partial class UnsafeValueTypeAttribute : System.Attribute
     {
         public UnsafeValueTypeAttribute() { }
     }
-    [AttributeUsage(AttributeTargets.Method)]
-    public sealed partial class ValueTaskAsyncAttribute : Attribute
-    {
-        public ValueTaskAsyncAttribute() { }
-    }
     public readonly partial struct ValueTaskAwaiter : System.Runtime.CompilerServices.ICriticalNotifyCompletion, System.Runtime.CompilerServices.INotifyCompletion
     {
         private readonly object _dummy;
diff --git a/src/tests/Loader/async/async2-eh-microbench.cs b/src/tests/Loader/async/async2-eh-microbench.cs
index e66bb56ceb5ec2..ab4beb0e182b0d 100644
--- a/src/tests/Loader/async/async2-eh-microbench.cs
+++ b/src/tests/Loader/async/async2-eh-microbench.cs
@@ -100,7 +100,7 @@ public Benchmark(int yieldCount, int depth, int finallyRate, bool throwOrReturn)
             _throwOrReturn = throwOrReturn;
         }
 
-        public async2 long Run(string type)
+        public async2 Task<long> Run(string type)
         {
             if (type == "Async2")
                 return await RunAsync2(_depth);
@@ -242,7 +242,7 @@ public async ValueTask<long> RunValueTask(int depth)
             return result;
         }
 
-        public async2 long RunAsync2(int depth)
+        public async2 Task<long> RunAsync2(int depth)
         {
             int liveState1 = depth * 3 + _yieldCount;
             int liveState2 = depth;
diff --git a/src/tests/Loader/async/async2-returns.cs b/src/tests/Loader/async/async2-returns.cs
index ae006fb022131c..174a66c479a14c 100644
--- a/src/tests/Loader/async/async2-returns.cs
+++ b/src/tests/Loader/async/async2-returns.cs
@@ -15,7 +15,7 @@ public static void TestEntryPoint()
     }
 
     [MethodImpl(MethodImplOptions.NoInlining)]
-    private static async2 void Returns(C c)
+    private static async2 Task Returns(C c)
     {
         for (int i = 0; i < 20000; i++)
         {
@@ -58,28 +58,28 @@ private static void AssertEqual<T>(T expected, T actual)
     }
 
     [MethodImpl(MethodImplOptions.NoInlining)]
-    private static async2 S<long> ReturnsStruct()
+    private static async2 Task<S<long>> ReturnsStruct()
     {
         await Task.Yield();
         return new S<long> { A = 42, B = 4242, C = 424242, D = 42424242 };
     }
 
     [MethodImpl(MethodImplOptions.NoInlining)]
-    private static async2 S<string> ReturnsStructGC()
+    private static async2 Task<S<string>> ReturnsStructGC()
     {
         await Task.Yield();
         return new S<string> { A = "A", B = "B", C = "C", D = "D" };
     }
 
     [MethodImpl(MethodImplOptions.NoInlining)]
-    private static async2 S<byte> ReturnsBytes()
+    private static async2 Task<S<byte>> ReturnsBytes()
     {
         await Task.Yield();
         return new S<byte> { A = 4, B = 40, C = 42, D = 45 };
     }
 
     [MethodImpl(MethodImplOptions.NoInlining)]
-    private static async2 string ReturnsString()
+    private static async2 Task<string> ReturnsString()
     {
         await Task.Yield();
         return "a string!";
diff --git a/src/tests/Loader/async/async2-varying-yields.cs b/src/tests/Loader/async/async2-varying-yields.cs
index 16744385a5a553..6edc30e8118dd7 100644
--- a/src/tests/Loader/async/async2-varying-yields.cs
+++ b/src/tests/Loader/async/async2-varying-yields.cs
@@ -82,7 +82,7 @@ async Task<long>
 #elif ASYNC1_VALUETASK
         async ValueTask<long>
 #else
-        async2 long
+        async2 Task<long>
 #endif
         Run(int depth)
         {
@@ -104,7 +104,7 @@ async Task<long>
 #elif ASYNC1_VALUETASK
         async ValueTask<long>
 #else
-        async2 long
+        async2 Task<long>
 #endif
         Loop()
         {
@@ -130,7 +130,7 @@ async Task<int>
 #elif ASYNC1_VALUETASK
         async ValueTask<int>
 #else
-        async2 int
+        async2 Task<int>
 #endif
         DoYields()
         {
diff --git a/src/tests/Loader/async/async2-with-yields.cs b/src/tests/Loader/async/async2-with-yields.cs
index acaaf5ce987be6..2f53a60f7c4fc5 100644
--- a/src/tests/Loader/async/async2-with-yields.cs
+++ b/src/tests/Loader/async/async2-with-yields.cs
@@ -8,7 +8,7 @@
 
 public class Async2FibonacceWithYields
 {
-    internal static async2 int B(int n)
+    internal static async2 Task<int> B(int n)
     {
         int num = 1;
         await Task.Yield();
@@ -22,7 +22,7 @@ internal static async2 int B(int n)
         return num;
     }
 
-    internal static async2 int A(int n)
+    internal static async2 Task<int> A(int n)
     {
         int num = n;
         for (int num2 = 0; num2 < n; num2++)
diff --git a/src/tests/Loader/async/async2-without-yields.cs b/src/tests/Loader/async/async2-without-yields.cs
index ac3f0fc3f731e5..ba1f2aeff0df1f 100644
--- a/src/tests/Loader/async/async2-without-yields.cs
+++ b/src/tests/Loader/async/async2-without-yields.cs
@@ -8,12 +8,15 @@
 
 public class Async2FibonacceWithoutYields
 {
-    internal static async2 int B(int n)
+    //This async method lacks 'await'
+#pragma warning disable 1998
+
+    internal static async2 Task<int> B(int n)
     {
         return 100;
     }
 
-    internal static async2 int A(int n)
+    internal static async2 Task<int> A(int n)
     {
         int num = n;
         for (int num2 = 0; num2 < n; num2++)
diff --git a/src/tests/Loader/async/async2fibonacci-with-yields.cs b/src/tests/Loader/async/async2fibonacci-with-yields.cs
index 2ee89ea4f2d1ab..6df9ddc44445a9 100644
--- a/src/tests/Loader/async/async2fibonacci-with-yields.cs
+++ b/src/tests/Loader/async/async2fibonacci-with-yields.cs
@@ -28,7 +28,7 @@ public static async Task AsyncEntry()
         }
     }
 
-    static async2 uint A(uint n)
+    static async2 Task<uint> A(uint n)
     {
         uint result = n;
         for (uint i = 0; i < n && !done; i++)
@@ -36,7 +36,7 @@ static async2 uint A(uint n)
         return result;
     }
 
-    static async2 uint B(uint n)
+    static async2 Task<uint> B(uint n)
     {
         uint result = n;
 
diff --git a/src/tests/Loader/async/async2fibonacci-without-yields.cs b/src/tests/Loader/async/async2fibonacci-without-yields.cs
index fe665e702d5fc6..bd47c8e869ab02 100644
--- a/src/tests/Loader/async/async2fibonacci-without-yields.cs
+++ b/src/tests/Loader/async/async2fibonacci-without-yields.cs
@@ -28,7 +28,7 @@ public static async Task AsyncEntry()
         }
     }
 
-    static async2 uint A(uint n)
+    static async2 Task<uint> A(uint n)
     {
         uint result = n;
         for (uint i = 0; i < n; i++)
@@ -36,7 +36,7 @@ static async2 uint A(uint n)
         return result;
     }
 
-    static async2 uint B(uint n)
+    static async2 Task<uint> B(uint n)
     {
         uint result = n;
 
diff --git a/src/tests/Loader/async/async2implement.cs b/src/tests/Loader/async/async2implement.cs
index 6871f815227949..bb8607541d1d49 100644
--- a/src/tests/Loader/async/async2implement.cs
+++ b/src/tests/Loader/async/async2implement.cs
@@ -10,7 +10,7 @@ public class Async2Implement
 {
     interface IBase1
     {
-        public async2 int M1();
+        public async2 Task<int> M1();
     }
 
     class Derived1 : IBase1
@@ -24,7 +24,7 @@ public async Task<int> M1()
 
     class Derived1a : IBase1
     {
-        public async2 int M1()
+        public async2 Task<int> M1()
         {
             await Task.Yield();
             return 3;
@@ -38,7 +38,7 @@ interface IBase2
 
     class Derived2 : IBase2
     {
-        public async2 int M1()
+        public async2 Task<int> M1()
         {
             await Task.Yield();
             return 12;
diff --git a/src/tests/Loader/async/async2object.cs b/src/tests/Loader/async/async2object.cs
index aa7381cf074f3c..fb62588652ae3f 100644
--- a/src/tests/Loader/async/async2object.cs
+++ b/src/tests/Loader/async/async2object.cs
@@ -20,7 +20,7 @@ private static async Task<object> AsyncTestEntryPoint(int arg)
     }
 
     [MethodImpl(MethodImplOptions.NoInlining)]
-    private static async2 object ObjMethod(int arg)
+    private static async2 Task<object> ObjMethod(int arg)
     {
         await Task.Yield();
         return arg;
diff --git a/src/tests/Loader/async/async2objectsCaptured.cs b/src/tests/Loader/async/async2objectsCaptured.cs
index f2a6a795fcbc98..197d29f0383d98 100644
--- a/src/tests/Loader/async/async2objectsCaptured.cs
+++ b/src/tests/Loader/async/async2objectsCaptured.cs
@@ -8,7 +8,7 @@
 
 public class Async2ObjectsWithYields
 {
-    internal static async2 int A(object n)
+    internal static async2 Task<int> A(object n)
     {
         // use string equality so that JIT would not think of hoisting "(int)n"
         // also to produce some amout of garbage
diff --git a/src/tests/Loader/async/async2override.cs b/src/tests/Loader/async/async2override.cs
index bdb74bb2ed4aba..4ee5d1446b831a 100644
--- a/src/tests/Loader/async/async2override.cs
+++ b/src/tests/Loader/async/async2override.cs
@@ -10,7 +10,7 @@ public class Async2Override
 {
     class Base
     {
-        public virtual async2 int M1()
+        public virtual async2 Task<int> M1()
         {
             await Task.Yield();
             return 1;
@@ -28,7 +28,7 @@ public override async Task<int> M1()
 
     class Derived2 : Derived1
     {
-        public override async2 int M1()
+        public override async2 Task<int> M1()
         {
             await Task.Yield();
             return 3;
@@ -47,7 +47,7 @@ public virtual async Task<int> M1()
 
     class Derived11 : Base1
     {
-        public override async2 int M1()
+        public override async2 Task<int> M1()
         {
             await Task.Yield();
             return 12;
diff --git a/src/tests/Loader/async/async2sharedgeneric.cs b/src/tests/Loader/async/async2sharedgeneric.cs
index 42aa7ea0f0e24f..992235b1c6dcc0 100644
--- a/src/tests/Loader/async/async2sharedgeneric.cs
+++ b/src/tests/Loader/async/async2sharedgeneric.cs
@@ -22,8 +22,12 @@ private static async Task AsyncTestEntryPoint<T>()
     {
         await Async2TestEntryPoint<T>();
     }
+
+    //This async method lacks 'await'
+#pragma warning disable 1998
+
     [MethodImpl(MethodImplOptions.NoInlining)]
-    private static async2 void Async2TestEntryPoint<T>()
+    private static async2 Task Async2TestEntryPoint<T>()
     {
         s_type = typeof(T);
     }
diff --git a/src/tests/Loader/async/async2valuetask.cs b/src/tests/Loader/async/async2valuetask.cs
index 53d0792da245b6..deff530e9f5d4d 100644
--- a/src/tests/Loader/async/async2valuetask.cs
+++ b/src/tests/Loader/async/async2valuetask.cs
@@ -19,8 +19,7 @@ private static ValueTask<int> AsyncTestEntryPoint(int arg)
         return M1(arg);
     }
 
-    [ValueTaskAsyncAttribute]
-    private static async2 int M1(int arg)
+    private static async2 ValueTask<int> M1(int arg)
     {
         await Task.Yield();
         return arg;
diff --git a/src/tests/Loader/async/async2void.cs b/src/tests/Loader/async/async2void.cs
index b645fa6789d2f2..8271887731a110 100644
--- a/src/tests/Loader/async/async2void.cs
+++ b/src/tests/Loader/async/async2void.cs
@@ -22,7 +22,7 @@ private static async Task AsyncTestEntryPoint(int[] arr, int index)
     }
 
     [MethodImpl(MethodImplOptions.NoInlining)]
-    private static async2 void HoistedByref(int[] arr, int index)
+    private static async2 Task HoistedByref(int[] arr, int index)
     {
         for (int i = 0; i < 20000; i++)
         {
diff --git a/src/tests/Loader/async/cse-array-index-byref.cs b/src/tests/Loader/async/cse-array-index-byref.cs
index 321be16f18fd1f..be99722a054bbb 100644
--- a/src/tests/Loader/async/cse-array-index-byref.cs
+++ b/src/tests/Loader/async/cse-array-index-byref.cs
@@ -22,7 +22,7 @@ private static async Task AsyncTestEntryPoint(int[] arr, int index)
     }
 
     [MethodImpl(MethodImplOptions.NoInlining)]
-    private static async2 int HoistedByref(int[] arr, int index)
+    private static async2 Task<int> HoistedByref(int[] arr, int index)
     {
         for (int i = 0; i < 20000; i++)
         {
@@ -31,4 +31,4 @@ private static async2 int HoistedByref(int[] arr, int index)
         }
         return 0;
     }
-}
\ No newline at end of file
+}
diff --git a/src/tests/Loader/async/simple-eh.cs b/src/tests/Loader/async/simple-eh.cs
index 06a72222bae2f9..8d70af66efc5b4 100644
--- a/src/tests/Loader/async/simple-eh.cs
+++ b/src/tests/Loader/async/simple-eh.cs
@@ -24,7 +24,7 @@ public static async Task AsyncEntry()
         Assert.Equal(42, result);
     }
 
-    public static async2 int Handler()
+    public static async2 Task<int> Handler()
     {
         try
         {
@@ -37,7 +37,7 @@ public static async2 int Handler()
     }
 
     [MethodImpl(MethodImplOptions.NoInlining)]
-    public static async2 int Throw(int value)
+    public static async2 Task<int> Throw(int value)
     {
         await Task.Yield();
         throw new IntegerException(value);

From d9fbc669687a8518acc200dd59af177923e7eab4 Mon Sep 17 00:00:00 2001
From: Vladimir Sadov <vsadov@microsoft.com>
Date: Wed, 15 Nov 2023 01:36:28 +0000
Subject: [PATCH 080/203] Merged PR 35215: Writeup about span/byref and
 runtime-async

---
 docs/design/features/runtime-handled-tasks.md | 37 +++++++++++++++++++
 1 file changed, 37 insertions(+)

diff --git a/docs/design/features/runtime-handled-tasks.md b/docs/design/features/runtime-handled-tasks.md
index 5a9f3183383207..2b1163ba0e0397 100644
--- a/docs/design/features/runtime-handled-tasks.md
+++ b/docs/design/features/runtime-handled-tasks.md
@@ -557,3 +557,40 @@ Without many finally blocks, we can see that the performance is really extraordi
 1. Serialization/Deserialization of JSON data to an asynchronous stream
 2. ASP.NET servicing of requests where must of the pipeline is converted to the async2 model
 
+## On supporting  span/byref variables in runtime-assisted async code.
+
+It is not a secret that inability to use span and byref with async code causes some amount of grief. The async/span impedance in particular appears to be unnecessarily constraining. That is likely because span caters to a wider audience and thus use of span often intersects with async. A simple search brings up a lot of user concerns, workarounds, and asks to relax the constraint at least partially, if possible -
+
+https://stackoverflow.com/questions/57229123/how-to-use-spanbyte-in-async-method
+
+https://stackoverflow.com/questions/63284335/spant-and-async-methods 
+
+[Span<T> in async methods not supported · Issue #27147 · dotnet/roslyn (github.com)] (https://github.com/dotnet/roslyn/issues/27147) 
+
+https://stackoverflow.com/questions/20868103/ref-and-out-arguments-in-async-method 
+
+[Consider relaxing restrictions of async methods in blocks that do not contain `await`. · Issue 1331 · dotnet/csharplang (github.com)](https://github.com/dotnet/csharplang/issues/1331) Lots of discussions here, including insightful comments by Vance. 
+
+The current situation is that C# does not allow byrefs and byref-likes in async methods in any form – be it parameters, locals or spans. The reason for this restriction is inability to capture byrefs as fields of display types. For consistency all use is forbidden, even if it does not require capture.  
+There are few cases were C# supports transient byrefs by the means of decomposing a byref-producing expression into constituent parts and capturing the parts and re-playing at use sites. Example `staticArray[i].Field += await Somehting();`    That allows to handle a subset of cases, but codegen is far from great.
+
+In runtime-assisted async supporting span and byrefs is not a strict “no”, but there are implications. There are roughly two kinds of byrefs that async implementation would need to deal with. They come with distinct challenges.
+
+**Heap-referencing byrefs** are challenging because GC reporting of byrefs is only naturally supported on stack. Any alternative storage will have to implement a scheme for reporting stored variables for marking/updating purposes. The main challenge here is performance. As we expect the number of tasks to be easily in thousands at a time, O(n) algorithms inevitably result in GC pauses. There are ways to mitigate this, some of which were explored and measured as a part of this experiment.
+
+**Stack-referencing byrefs** do not have to be reported to GC, but must be adjusted when containing frame is reactivated as the referent is either in the current frame and thus now lives in a different stack location, or in one of still suspended caller frames and thus not on the stack at all. In the presence of “ref Span<T> param” and similar, updating upon suspending is also necessary to keep chains of byrefs safely walkable.
+
+In general it would not be a tractable programming model from the user perspective, if we allow only one kind of byrefs and not another as distinction may often only be known at run time.  
+Note that the stack/heap-referencing nature of a byref cannot change while the frame is not active. However that is not completely true if “ref Span<T> param” is allowed. In such case a calee may repoint a byref in a caller frame and switch from stack-pointing to heap-pointing.   
+
+**Stack-referencing byrefs, that lead into sync callers** these are unsafe as the sync caller may exit any or all its stack frames before the Task is resumed.
+
+In the current experiment we have looked into two implementing strategies:
+
+**Stack unwinding with 1:1 stack capture for suspended frames.** In this model byrefs/spans that point to heap or other async callers can be supported as there is a mechanism to report byrefs to the GC. There are some performance challenges, but not without solutions. 
+
+**State machine with managed storage for captured variables.** The current implementation does not support capturing byrefs and byref-likes. On the other hand it can utilize regular GC reporting mechanisms, which has many advantages, notably not having O(n) components that need to run in GC pauses. There are some ideas on how byrefs may be captured and converted into {object, offset} at the next GC, so that not to incur continuous O(n) costs.
+
+## On unmanaged pointers.
+We cannot possibly track unmanaged pointers that refer to the stack across suspensions. Disallowing such scenarios would not be a take-back though since currently C# does not allow `await` in unsafe context, therefore await and unmanaged pointers cannot mix.
+

From 11f8bd26a08dd8be8f1375119ef98a2faab91c22 Mon Sep 17 00:00:00 2001
From: David Wrighton <davidwr@microsoft.com>
Date: Thu, 16 Nov 2023 13:01:10 -0800
Subject: [PATCH 081/203] Update buildroslynnugets.cmd script to use public
 github details

---
 buildroslynnugets.cmd | 11 +++++++----
 1 file changed, 7 insertions(+), 4 deletions(-)

diff --git a/buildroslynnugets.cmd b/buildroslynnugets.cmd
index e097dccf488e87..7ae6eb9dca4101 100644
--- a/buildroslynnugets.cmd
+++ b/buildroslynnugets.cmd
@@ -1,14 +1,17 @@
 setlocal ENABLEEXTENSIONS
 pushd %~dp0
-
+set ASYNC_ROSLYN_COMMIT=046d22fad72ac726d36d93b0e9d186574e77207b
 set ASYNC_SUFFIX=async-7
+set ASYNC_ROSLYN_BRANCH=demos/async2-experiment
 
 cd ..
-pushd dotnet-roslyn
+if not exist async-roslyn-repo git clone -b %ASYNC_ROSLYN_BRANCH% -o async_roslyn_remote https://github.com/dotnet/runtimelab.git async-roslyn-repo
+
+pushd async-roslyn-repo
 
-git fetch AzDo dev/vsadov/a2
+git fetch async_roslyn_remote %ASYNC_ROSLYN_BRANCH%
 rem when updating this, make sure to update the ASYNC_SUFFIX above and the versions.props file
-git checkout 046d22fad72ac726d36d93b0e9d186574e77207b
+git checkout %ASYNC_ROSLYN_COMMIT%
 
 call restore.cmd
 call build.cmd -c release

From a6a45ed697a379118dd683d4c4e226126ecfc4d0 Mon Sep 17 00:00:00 2001
From: David Wrighton <davidwr@microsoft.com>
Date: Thu, 16 Nov 2023 14:19:55 -0800
Subject: [PATCH 082/203] Add readme for async2 experiment

---
 README.md | 16 ++++++++++++++++
 1 file changed, 16 insertions(+)

diff --git a/README.md b/README.md
index 698c2da19e9534..8fb13d18b7e160 100644
--- a/README.md
+++ b/README.md
@@ -1,3 +1,19 @@
+# .NET Runtime - Async2 Experiment
+
+This branch contains experimental fork of CoreCLR [.NET runtime](http://github.com/dotnet/runtime) where we explore implementing async directly in the runtime instead of generating state machines in the IL compiler (Roslyn).
+
+## Samples
+
+See the tests in src/tests/loader/async.
+
+## Documentation
+
+- Before building, run the buildroslynugets.cmd script, it will build a variant of the Roslyn compiler that can be used to test this codebase. Otherwise follow the standard developer workflow.
+- [Async Experiment Issue](https://github.com/dotnet/runtimelab/issues/2398)
+- [Design and details](docs\design\features\runtime-handled-tasks.md)
+
+---
+
 # .NET Runtime
 
 In order to build in this repo, you must have set up a Roslyn repo parallel to this repo with the name dotnet-roslyn, and it must have a remote called AzDo which has the updated compiler in it. Then you must run buildroslynnugets.cmd to create a local copy of the compiler for use in the repo. The equivalent work has not yet been done for running on Unix-like platforms.

From 6fec72162e983861b335bc0d92497ba4839509f5 Mon Sep 17 00:00:00 2001
From: David Wrighton <davidwr@microsoft.com>
Date: Thu, 16 Nov 2023 14:36:13 -0800
Subject: [PATCH 083/203] Follow instructions in the Create an experiment
 document

---
 .github/CODEOWNERS    | 104 ------------------------------------------
 Directory.Build.props |   2 +-
 eng/Versions.props    |   2 +-
 3 files changed, 2 insertions(+), 106 deletions(-)
 delete mode 100644 .github/CODEOWNERS

diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS
deleted file mode 100644
index ab341bf12b5d90..00000000000000
--- a/.github/CODEOWNERS
+++ /dev/null
@@ -1,104 +0,0 @@
-# Users referenced in this file will automatically be requested as reviewers for PRs that modify the given paths.
-# See https://help.github.com/articles/about-code-owners/
-
-/src/libraries/Common/src/Interop/                                  @dotnet/platform-deps-team
-/src/libraries/Common/src/System/Net/Http/aspnetcore/               @dotnet/http
-/src/libraries/Common/tests/Tests/System/Net/aspnetcore/            @dotnet/http
-
-# CoreCLR Code Owners
-
-/src/coreclr/inc/corinfo.h @dotnet/jit-contrib
-/src/coreclr/inc/corjit.h @dotnet/jit-contrib
-/src/coreclr/jit/ @dotnet/jit-contrib
-/src/coreclr/nativeaot @MichalStrehovsky
-/src/coreclr/tools/Common @dotnet/crossgen-contrib @MichalStrehovsky
-/src/coreclr/tools/aot @dotnet/crossgen-contrib
-/src/coreclr/tools/aot/ILCompiler.Compiler @MichalStrehovsky
-/src/coreclr/tools/aot/ILCompiler.RyuJit @MichalStrehovsky
-/src/coreclr/tools/aot/ILCompiler.MetadataTransform @MichalStrehovsky
-
-# Mono Code Owners
-
-/src/mono @marek-safar
-
-/src/mono/llvm @vargaz @SamMonoRT
-
-/src/mono/mono/arch @vargaz
-/src/mono/mono/eglib @vargaz @lambdageek
-
-/src/mono/mono/metadata @vargaz @lambdageek @thaystg
-/src/mono/mono/metadata/*-win* @lateralusX @lambdageek
-/src/mono/mono/metadata/handle* @lambdageek @vargaz
-/src/mono/mono/metadata/monitor* @brzvlad @vargaz
-/src/mono/mono/metadata/sgen* @brzvlad @vargaz @lambdageek
-/src/mono/mono/metadata/thread* @lateralusX @lambdageek
-/src/mono/mono/metadata/w32* @lateralusX @lambdageek
-
-/src/mono/mono/eventpipe @lateralusX @lambdageek
-
-/src/mono/mono/mini @vargaz @lambdageek @SamMonoRT
-/src/mono/mono/mini/*cfgdump* @vargaz
-/src/mono/mono/mini/*exceptions* @vargaz @BrzVlad
-/src/mono/mono/mini/*llvm* @vargaz @fanyang-mono
-/src/mono/mono/mini/*ppc* @vargaz
-/src/mono/mono/mini/*profiler* @BrzVlad @lambdageek
-/src/mono/mono/mini/*riscv* @vargaz @lambdageek
-/src/mono/mono/mini/*type-check* @lambdageek
-/src/mono/mono/mini/debugger-agent.c @vargaz @thaystg @lambdageek
-/src/mono/mono/mini/interp/* @BrzVlad @vargaz @kotlarmilos
-/src/mono/mono/mini/interp/*jiterp* @kg
-/src/mono/mono/mini/*simd* @fanyang-mono
-
-/src/mono/mono/profiler @BrzVlad @lambdageek
-/src/mono/mono/sgen @BrzVlad @lambdageek @SamMonoRT
-
-/src/mono/mono/utils @vargaz @lambdageek
-/src/mono/mono/utils/*-win* @lateralusX @lambdageek
-/src/mono/mono/utils/atomic* @vargaz
-/src/mono/mono/utils/mono-hwcap* @vargaz
-/src/mono/mono/utils/mono-mem* @vargaz
-/src/mono/mono/utils/mono-threads* @lambdageek @vargaz
-
-/src/mono/dlls @thaystg @lambdageek
-
-/src/native/public/mono @marek-safar @lambdageek
-
-/src/libraries/sendtohelix-wasm.targets @radical
-/src/mono/wasm @lewing @pavelsavara @kg
-/src/mono/wasm/debugger @thaystg @radical
-/src/mono/wasm/build @radical
-/src/mono/sample/wasm @lewing @pavelsavara
-/src/libraries/System.Runtime.InteropServices.JavaScript @lewing @pavelsavara
-
-/src/mono/nuget/*WebAssembly*/ @lewing @radical
-/src/mono/nuget/*MonoTargets*/ @lewing @radical
-/src/mono/nuget/*BrowserDebugHost*/ @lewing @radical
-/src/mono/nuget/*Workload.Mono.Toolchain*/ @lewing @radical
-/src/mono/nuget/*MonoAOTCompiler*/ @lewing @radical
-
-/src/mono/wasm/Wasm* @radical
-/src/mono/wasm/testassets @radical
-/src/tasks/WasmAppBuilder/ @radical
-/src/tasks/WorkloadBuildTasks/ @radical
-/src/tasks/AotCompilerTask/ @radical
-/src/tasks/WasmBuildTasks/ @radical
-
-/eng/pipelines/**/*wasm* @radical
-
-# ILLink codeowners
-/src/tools/illink/ @marek-safar
-/src/tools/illink/src/analyzer/ @radekdoulik
-/src/tools/illink/src/ILLink.Tasks/ @sbomer
-/src/tools/illink/src/ILLink.RoslynAnalyzer/ @sbomer
-/src/tools/illink/src/linker/ @marek-safar @mrvoorhe
-/src/tools/illink/test/ @marek-safar @mrvoorhe
-
-# Obsoletions / Custom Diagnostics
-
-/docs/project/list-of-diagnostics.md                @jeffhandley
-/src/libraries/Common/src/System/Obsoletions.cs     @jeffhandley
-
-# Area ownership and repo automation
-/docs/area-owners.*                                 @jeffhandley
-/docs/issue*.md                                     @jeffhandley
-/.github/fabricbot.json                             @jeffhandley
diff --git a/Directory.Build.props b/Directory.Build.props
index 3331de8a7f8d50..22e2a505531c03 100644
--- a/Directory.Build.props
+++ b/Directory.Build.props
@@ -336,7 +336,7 @@
 
   <!-- Packaging -->
   <PropertyGroup>
-    <GitHubRepositoryName>runtime</GitHubRepositoryName>
+    <GitHubRepositoryName>runtimelab</GitHubRepositoryName>
     <RepositoryUrl>https://github.com/dotnet/$(GitHubRepositoryName)</RepositoryUrl>
     <PackageProjectUrl>https://dot.net</PackageProjectUrl>
     <Owners>microsoft,dotnetframework</Owners>
diff --git a/eng/Versions.props b/eng/Versions.props
index 9b918bbd541c5c..cc9b47af989700 100644
--- a/eng/Versions.props
+++ b/eng/Versions.props
@@ -10,7 +10,7 @@
     <PackageVersionNet8>8.0.0-rc.2.23469.22</PackageVersionNet8>
     <PackageVersionNet7>7.0.8</PackageVersionNet7>
     <PackageVersionNet6>6.0.$([MSBuild]::Add($([System.Version]::Parse('$(PackageVersionNet7)').Build),11))</PackageVersionNet6>
-    <PreReleaseVersionLabel>alpha</PreReleaseVersionLabel>
+    <PreReleaseVersionLabel>async</PreReleaseVersionLabel>
     <PreReleaseVersionIteration>1</PreReleaseVersionIteration>
     <!-- Enable to remove prerelease label. -->
     <StabilizePackageVersion Condition="'$(StabilizePackageVersion)' == ''">false</StabilizePackageVersion>

From 9495c90d95b6c5f55008d918c0894c7c0c0b9190 Mon Sep 17 00:00:00 2001
From: David Wrighton <davidwr@microsoft.com>
Date: Thu, 16 Nov 2023 14:39:44 -0800
Subject: [PATCH 084/203] Correct issue number for async2 work

---
 README.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/README.md b/README.md
index 8fb13d18b7e160..68f5f910d0f693 100644
--- a/README.md
+++ b/README.md
@@ -9,7 +9,7 @@ See the tests in src/tests/loader/async.
 ## Documentation
 
 - Before building, run the buildroslynugets.cmd script, it will build a variant of the Roslyn compiler that can be used to test this codebase. Otherwise follow the standard developer workflow.
-- [Async Experiment Issue](https://github.com/dotnet/runtimelab/issues/2398)
+- [Async Experiment Issue](https://github.com/dotnet/runtimelab/issues/94620)
 - [Design and details](docs\design\features\runtime-handled-tasks.md)
 
 ---

From 75319abcd7ab71f5d4a44ab8a8be68e8f56a0e75 Mon Sep 17 00:00:00 2001
From: David Wrighton <davidwr@microsoft.com>
Date: Thu, 16 Nov 2023 14:58:30 -0800
Subject: [PATCH 085/203] Fix readme even more

---
 README.md | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/README.md b/README.md
index 68f5f910d0f693..1113fe689cf645 100644
--- a/README.md
+++ b/README.md
@@ -9,8 +9,8 @@ See the tests in src/tests/loader/async.
 ## Documentation
 
 - Before building, run the buildroslynugets.cmd script, it will build a variant of the Roslyn compiler that can be used to test this codebase. Otherwise follow the standard developer workflow.
-- [Async Experiment Issue](https://github.com/dotnet/runtimelab/issues/94620)
-- [Design and details](docs\design\features\runtime-handled-tasks.md)
+- [Async Experiment Issue](https://github.com/dotnet/runtime/issues/94620)
+- [Design and details](docs/design/features/runtime-handled-tasks.md)
 
 ---
 

From c4a2eb3cc10e11804e65d02e3b7ff1c276a2d7c1 Mon Sep 17 00:00:00 2001
From: David Wrighton <davidwr@microsoft.com>
Date: Thu, 16 Nov 2023 15:47:43 -0800
Subject: [PATCH 086/203] Merged PR 35233: Add measurement of async2 logic with
 Execution and SynchronizationContext capture/restore (#2421)

Add measurement of async2 logic with Execution and SynchronizationContext capture/restore
- Attempt to determine if we could fully simulate the current async semantics while still achieving significant performance wins
- TLDR: Yes, we can get a pretty substantial performance gain, but we lose somewhere between 20-30% of what we could gain
---
 ...-vs-async2capture-relative-performance.svg |   1 +
 ...-vs-async2capture-relative-performance.svg |   1 +
 docs/design/features/runtime-handled-tasks.md |  42 +++++--
 .../Loader/async/async2-eh-microbench.cs      | 117 ++++++++++++++++++
 4 files changed, 150 insertions(+), 11 deletions(-)
 create mode 100644 docs/design/features/async-vs-async2capture-relative-performance.svg
 create mode 100644 docs/design/features/async2-vs-async2capture-relative-performance.svg

diff --git a/docs/design/features/async-vs-async2capture-relative-performance.svg b/docs/design/features/async-vs-async2capture-relative-performance.svg
new file mode 100644
index 00000000000000..d9dee0be0a31da
--- /dev/null
+++ b/docs/design/features/async-vs-async2capture-relative-performance.svg
@@ -0,0 +1 @@
+<svg width="2556" height="1486" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" overflow="hidden"><defs><clipPath id="clip0"><rect x="2999" y="7360" width="2556" height="1486"/></clipPath></defs><g clip-path="url(#clip0)" transform="translate(-2999 -7360)"><rect x="3000" y="7361" width="2553" height="1483" fill="#FFFFFF"/><g><path d="M3311.5 8367.77 5502.5 8367.77M3311.5 8200.77 5502.5 8200.77M3311.5 8032.76 5502.5 8032.76M3311.5 7865.76 5502.5 7865.76M3311.5 7698.75 5502.5 7698.75M3311.5 7530.5 5502.5 7530.5" stroke="#D9D9D9" stroke-width="3.4375" stroke-linejoin="round" stroke-miterlimit="10" fill="none"/></g><g><path d="M3380.11 8278.27 3442.11 8278.27 3442.11 8536 3380.11 8536ZM3818.13 8206.27 3881.13 8206.27 3881.13 8536 3818.13 8536ZM4256.14 8161.27 4319.14 8161.27 4319.14 8536 4256.14 8536ZM4694.15 8031.26 4757.16 8031.26 4757.16 8536 4694.15 8536ZM5132.17 7885.26 5195.17 7885.26 5195.17 8536 5132.17 8536Z" fill="#A02B93"/></g><g><path d="M3459.11 8187.27 3522.12 8187.27 3522.12 8536 3459.11 8536ZM3898.13 8076.27 3960.13 8076.27 3960.13 8536 3898.13 8536ZM4336.14 7921.26 4398.14 7921.26 4398.14 8536 4336.14 8536ZM4774.16 7758.25 4836.16 7758.25 4836.16 8536 4774.16 8536ZM5212.17 7582.25 5275.17 7582.25 5275.17 8536 5212.17 8536Z" fill="#156082"/></g><g><path d="M3539.12 8358.27 3601.12 8358.27 3601.12 8536 3539.12 8536ZM3977.13 8280.27 4040.13 8280.27 4040.13 8536 3977.13 8536ZM4415.15 8206.27 4478.15 8206.27 4478.15 8536 4415.15 8536ZM4853.16 8217.27 4916.16 8217.27 4916.16 8536 4853.16 8536ZM5291.17 8187.27 5354.18 8187.27 5354.18 8536 5291.17 8536Z" fill="#E97132"/></g><g><path d="M3618.12 8309.27 3681.12 8309.27 3681.12 8536 3618.12 8536ZM4057.13 8310.27 4119.14 8310.27 4119.14 8536 4057.13 8536ZM4495.15 8325.27 4557.15 8325.27 4557.15 8536 4495.15 8536ZM4933.16 8335.27 4995.16 8335.27 4995.16 8536 4933.16 8536ZM5371.18 8340.27 5434.18 8340.27 5434.18 8536 5371.18 8536Z" fill="#196B24"/></g><g><path d="M0 0 0.000360892 1005" stroke="#156082" stroke-width="3.4375" stroke-linejoin="round" stroke-miterlimit="10" fill="none" fill-rule="evenodd" transform="matrix(1 0 0 -1 3311.5 8535.5)"/></g><g><path d="M3311.5 8535.5 3321.5 8535.5M3311.5 8501.78 3321.5 8501.78M3311.5 8468.78 3321.5 8468.78M3311.5 8434.78 3321.5 8434.78M3311.5 8401.78 3321.5 8401.78M3311.5 8367.77 3321.5 8367.77M3311.5 8334.77 3321.5 8334.77M3311.5 8300.77 3321.5 8300.77M3311.5 8267.77 3321.5 8267.77M3311.5 8233.77 3321.5 8233.77M3311.5 8200.77 3321.5 8200.77M3311.5 8166.77 3321.5 8166.77M3311.5 8133.77 3321.5 8133.77M3311.5 8099.77 3321.5 8099.77M3311.5 8066.76 3321.5 8066.76M3311.5 8032.76 3321.5 8032.76M3311.5 7999.76 3321.5 7999.76M3311.5 7965.76 3321.5 7965.76M3311.5 7932.76 3321.5 7932.76M3311.5 7898.76 3321.5 7898.76M3311.5 7865.76 3321.5 7865.76M3311.5 7831.76 3321.5 7831.76M3311.5 7798.76 3321.5 7798.76M3311.5 7765.75 3321.5 7765.75M3311.5 7731.75 3321.5 7731.75M3311.5 7698.75 3321.5 7698.75M3311.5 7664.75 3321.5 7664.75M3311.5 7631.75 3321.5 7631.75M3311.5 7597.75 3321.5 7597.75M3311.5 7564.75 3321.5 7564.75M3311.5 7530.5 3321.5 7530.5" stroke="#156082" stroke-width="3.4375" stroke-linejoin="round" stroke-miterlimit="10" fill="none"/></g><g><path d="M3311.5 8535.5 5502.5 8535.5" stroke="#D9D9D9" stroke-width="3.4375" stroke-linejoin="round" stroke-miterlimit="10" fill="none" fill-rule="evenodd"/></g><g><text fill="#595959" font-family="Aptos Narrow,Aptos Narrow_MSFontService,sans-serif" font-weight="400" font-stretch="semi-condensed" font-size="41" transform="matrix(1 0 0 1 3168.57 8547)">0.00%</text></g><g><text fill="#595959" font-family="Aptos Narrow,Aptos Narrow_MSFontService,sans-serif" font-weight="400" font-stretch="semi-condensed" font-size="41" transform="matrix(1 0 0 1 3126.77 8380)">100.00%</text></g><g><text fill="#595959" font-family="Aptos Narrow,Aptos Narrow_MSFontService,sans-serif" font-weight="400" font-stretch="semi-condensed" font-size="41" transform="matrix(1 0 0 1 3126.77 8212)">200.00%</text></g><g><text fill="#595959" font-family="Aptos Narrow,Aptos Narrow_MSFontService,sans-serif" font-weight="400" font-stretch="semi-condensed" font-size="41" transform="matrix(1 0 0 1 3126.77 8045)">300.00%</text></g><g><text fill="#595959" font-family="Aptos Narrow,Aptos Narrow_MSFontService,sans-serif" font-weight="400" font-stretch="semi-condensed" font-size="41" transform="matrix(1 0 0 1 3126.77 7877)">400.00%</text></g><g><text fill="#595959" font-family="Aptos Narrow,Aptos Narrow_MSFontService,sans-serif" font-weight="400" font-stretch="semi-condensed" font-size="41" transform="matrix(1 0 0 1 3126.77 7710)">500.00%</text></g><g><text fill="#595959" font-family="Aptos Narrow,Aptos Narrow_MSFontService,sans-serif" font-weight="400" font-stretch="semi-condensed" font-size="41" transform="matrix(1 0 0 1 3126.77 7542)">600.00%</text></g><g><text fill="#595959" font-family="Aptos Narrow,Aptos Narrow_MSFontService,sans-serif" font-weight="400" font-stretch="semi-condensed" font-size="41" transform="matrix(1 0 0 1 3520.09 8600)">1</text></g><g><text fill="#595959" font-family="Aptos Narrow,Aptos Narrow_MSFontService,sans-serif" font-weight="400" font-stretch="semi-condensed" font-size="41" transform="matrix(1 0 0 1 3958.26 8600)">2</text></g><g><text fill="#595959" font-family="Aptos Narrow,Aptos Narrow_MSFontService,sans-serif" font-weight="400" font-stretch="semi-condensed" font-size="41" transform="matrix(1 0 0 1 4396.42 8600)">4</text></g><g><text fill="#595959" font-family="Aptos Narrow,Aptos Narrow_MSFontService,sans-serif" font-weight="400" font-stretch="semi-condensed" font-size="41" transform="matrix(1 0 0 1 4834.59 8600)">8</text></g><g><text fill="#595959" font-family="Aptos Narrow,Aptos Narrow_MSFontService,sans-serif" font-weight="400" font-stretch="semi-condensed" font-size="41" transform="matrix(1 0 0 1 5262.31 8600)">16</text></g><g><text fill="#595959" font-family="Aptos Narrow,Aptos Narrow_MSFontService,sans-serif" font-weight="400" font-stretch="semi-condensed" font-size="46" transform="matrix(6.12323e-17 -1 1 6.12323e-17 3090.91 8613)">iters/s async2 with capture  divided by iters/s Task base async</text></g><g><text fill="#595959" font-family="Aptos Narrow,Aptos Narrow_MSFontService,sans-serif" font-weight="400" font-stretch="semi-condensed" font-size="46" transform="matrix(1 0 0 1 4295.78 8668)">Stack Depth</text></g><g><text fill="#595959" font-family="Aptos Narrow,Aptos Narrow_MSFontService,sans-serif" font-weight="400" font-stretch="semi-condensed" font-size="64" transform="matrix(1 0 0 1 3358.4 7491)">Relative performance of existing async vs runtime async with context capture</text></g><g><rect x="3562" y="8766" width="23" height="23" fill="#A02B93"/></g><g><text fill="#595959" font-family="Aptos Narrow,Aptos Narrow_MSFontService,sans-serif" font-weight="400" font-stretch="semi-condensed" font-size="41" transform="matrix(1 0 0 1 3594.28 8789)">Return NoSuspend</text></g><g><rect x="3968" y="8766" width="23" height="23" fill="#156082"/></g><g><text fill="#595959" font-family="Aptos Narrow,Aptos Narrow_MSFontService,sans-serif" font-weight="400" font-stretch="semi-condensed" font-size="41" transform="matrix(1 0 0 1 4000.65 8789)">Throw NoSuspend</text></g><g><rect x="4364" y="8766" width="23" height="23" fill="#E97132"/></g><g><text fill="#595959" font-family="Aptos Narrow,Aptos Narrow_MSFontService,sans-serif" font-weight="400" font-stretch="semi-condensed" font-size="41" transform="matrix(1 0 0 1 4396.73 8789)">Return Suspend</text></g><g><rect x="4723" y="8766" width="23" height="23" fill="#196B24"/></g><g><text fill="#595959" font-family="Aptos Narrow,Aptos Narrow_MSFontService,sans-serif" font-weight="400" font-stretch="semi-condensed" font-size="41" transform="matrix(1 0 0 1 4755.69 8789)">Throw Suspend</text></g><rect x="3000.5" y="7361.5" width="2553" height="1483" stroke="#D9D9D9" stroke-width="3.4375" stroke-linejoin="round" stroke-miterlimit="10" fill="none"/></g></svg>
\ No newline at end of file
diff --git a/docs/design/features/async2-vs-async2capture-relative-performance.svg b/docs/design/features/async2-vs-async2capture-relative-performance.svg
new file mode 100644
index 00000000000000..3143faacafe81a
--- /dev/null
+++ b/docs/design/features/async2-vs-async2capture-relative-performance.svg
@@ -0,0 +1 @@
+<svg width="2581" height="1308" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" overflow="hidden"><defs><clipPath id="clip0"><rect x="3006" y="8833" width="2581" height="1308"/></clipPath></defs><g clip-path="url(#clip0)" transform="translate(-3006 -8833)"><rect x="3007" y="8834" width="2578" height="1305" fill="#FFFFFF"/><g><path d="M3318.5 9693.82 5534.5 9693.82M3318.5 9555.81 5534.5 9555.81M3318.5 9417.81 5534.5 9417.81M3318.5 9279.8 5534.5 9279.8M3318.5 9141.8 5534.5 9141.8M3318.5 9004.5 5534.5 9004.5" stroke="#D9D9D9" stroke-width="3.4375" stroke-linejoin="round" stroke-miterlimit="10" fill="none"/></g><g><path d="M3388.11 9227.3 3451.11 9227.3 3451.11 9831 3388.11 9831ZM3831.13 9277.3 3894.13 9277.3 3894.13 9831 3831.13 9831ZM4274.14 9299.31 4337.14 9299.31 4337.14 9831 4274.14 9831ZM4717.15 9333.31 4780.16 9333.31 4780.16 9831 4717.15 9831ZM5160.17 9334.31 5224.17 9334.31 5224.17 9831 5160.17 9831Z" fill="#A02B93"/></g><g><path d="M3468.11 9163.3 3531.12 9163.3 3531.12 9831 3468.11 9831ZM3911.13 9163.3 3974.13 9163.3 3974.13 9831 3911.13 9831ZM4354.14 9178.3 4418.14 9178.3 4418.14 9831 4354.14 9831ZM4798.16 9192.3 4861.16 9192.3 4861.16 9831 4798.16 9831ZM5241.17 9193.3 5304.17 9193.3 5304.17 9831 5241.17 9831Z" fill="#156082"/></g><g><path d="M3548.12 9147.3 3612.12 9147.3 3612.12 9831 3548.12 9831ZM3992.13 9167.3 4055.13 9167.3 4055.13 9831 3992.13 9831ZM4435.15 9183.3 4498.15 9183.3 4498.15 9831 4435.15 9831ZM4878.16 9232.3 4941.16 9232.3 4941.16 9831 4878.16 9831ZM5321.17 9228.3 5384.18 9228.3 5384.18 9831 5321.17 9831Z" fill="#E97132"/></g><g><path d="M3629.12 9149.3 3692.12 9149.3 3692.12 9831 3629.12 9831ZM4072.13 9409.31 4135.14 9409.31 4135.14 9831 4072.13 9831ZM4515.15 9596.32 4578.15 9596.32 4578.15 9831 4515.15 9831ZM4958.16 9694.32 5022.16 9694.32 5022.16 9831 4958.16 9831ZM5402.18 9756.32 5465.18 9756.32 5465.18 9831 5402.18 9831Z" fill="#196B24"/></g><g><path d="M0 0 0.000360892 827" stroke="#156082" stroke-width="3.4375" stroke-linejoin="round" stroke-miterlimit="10" fill="none" fill-rule="evenodd" transform="matrix(1 0 0 -1 3318.5 9831.5)"/></g><g><path d="M3318.5 9831.5 3328.5 9831.5M3318.5 9803.82 3328.5 9803.82M3318.5 9776.82 3328.5 9776.82M3318.5 9748.82 3328.5 9748.82M3318.5 9720.82 3328.5 9720.82M3318.5 9693.82 3328.5 9693.82M3318.5 9665.82 3328.5 9665.82M3318.5 9638.82 3328.5 9638.82M3318.5 9610.82 3328.5 9610.82M3318.5 9583.81 3328.5 9583.81M3318.5 9555.81 3328.5 9555.81M3318.5 9527.81 3328.5 9527.81M3318.5 9500.81 3328.5 9500.81M3318.5 9472.81 3328.5 9472.81M3318.5 9445.81 3328.5 9445.81M3318.5 9417.81 3328.5 9417.81M3318.5 9390.81 3328.5 9390.81M3318.5 9362.81 3328.5 9362.81M3318.5 9334.81 3328.5 9334.81M3318.5 9307.81 3328.5 9307.81M3318.5 9279.8 3328.5 9279.8M3318.5 9252.8 3328.5 9252.8M3318.5 9224.8 3328.5 9224.8M3318.5 9197.8 3328.5 9197.8M3318.5 9169.8 3328.5 9169.8M3318.5 9141.8 3328.5 9141.8M3318.5 9114.8 3328.5 9114.8M3318.5 9086.8 3328.5 9086.8M3318.5 9059.8 3328.5 9059.8M3318.5 9031.8 3328.5 9031.8M3318.5 9004.5 3328.5 9004.5" stroke="#156082" stroke-width="3.4375" stroke-linejoin="round" stroke-miterlimit="10" fill="none"/></g><g><path d="M3318.5 9831.5 5534.5 9831.5" stroke="#D9D9D9" stroke-width="3.4375" stroke-linejoin="round" stroke-miterlimit="10" fill="none" fill-rule="evenodd"/></g><g><text fill="#595959" font-family="Aptos Narrow,Aptos Narrow_MSFontService,sans-serif" font-weight="400" font-stretch="semi-condensed" font-size="41" transform="matrix(1 0 0 1 3175.45 9843)">0.00%</text></g><g><text fill="#595959" font-family="Aptos Narrow,Aptos Narrow_MSFontService,sans-serif" font-weight="400" font-stretch="semi-condensed" font-size="41" transform="matrix(1 0 0 1 3154.55 9705)">20.00%</text></g><g><text fill="#595959" font-family="Aptos Narrow,Aptos Narrow_MSFontService,sans-serif" font-weight="400" font-stretch="semi-condensed" font-size="41" transform="matrix(1 0 0 1 3154.55 9567)">40.00%</text></g><g><text fill="#595959" font-family="Aptos Narrow,Aptos Narrow_MSFontService,sans-serif" font-weight="400" font-stretch="semi-condensed" font-size="41" transform="matrix(1 0 0 1 3154.55 9429)">60.00%</text></g><g><text fill="#595959" font-family="Aptos Narrow,Aptos Narrow_MSFontService,sans-serif" font-weight="400" font-stretch="semi-condensed" font-size="41" transform="matrix(1 0 0 1 3154.55 9292)">80.00%</text></g><g><text fill="#595959" font-family="Aptos Narrow,Aptos Narrow_MSFontService,sans-serif" font-weight="400" font-stretch="semi-condensed" font-size="41" transform="matrix(1 0 0 1 3133.65 9154)">100.00%</text></g><g><text fill="#595959" font-family="Aptos Narrow,Aptos Narrow_MSFontService,sans-serif" font-weight="400" font-stretch="semi-condensed" font-size="41" transform="matrix(1 0 0 1 3133.65 9016)">120.00%</text></g><g><text fill="#595959" font-family="Aptos Narrow,Aptos Narrow_MSFontService,sans-serif" font-weight="400" font-stretch="semi-condensed" font-size="41" transform="matrix(1 0 0 1 3529.48 9896)">1</text></g><g><text fill="#595959" font-family="Aptos Narrow,Aptos Narrow_MSFontService,sans-serif" font-weight="400" font-stretch="semi-condensed" font-size="41" transform="matrix(1 0 0 1 3972.69 9896)">2</text></g><g><text fill="#595959" font-family="Aptos Narrow,Aptos Narrow_MSFontService,sans-serif" font-weight="400" font-stretch="semi-condensed" font-size="41" transform="matrix(1 0 0 1 4415.9 9896)">4</text></g><g><text fill="#595959" font-family="Aptos Narrow,Aptos Narrow_MSFontService,sans-serif" font-weight="400" font-stretch="semi-condensed" font-size="41" transform="matrix(1 0 0 1 4859.11 9896)">8</text></g><g><text fill="#595959" font-family="Aptos Narrow,Aptos Narrow_MSFontService,sans-serif" font-weight="400" font-stretch="semi-condensed" font-size="41" transform="matrix(1 0 0 1 5291.87 9896)">16</text></g><g><text fill="#595959" font-family="Aptos Narrow,Aptos Narrow_MSFontService,sans-serif" font-weight="400" font-stretch="semi-condensed" font-size="46" transform="matrix(6.12323e-17 -1 1 6.12323e-17 3106.95 9876)">iters/s async2capture  divided by iters/s async2</text></g><g><text fill="#595959" font-family="Aptos Narrow,Aptos Narrow_MSFontService,sans-serif" font-weight="400" font-stretch="semi-condensed" font-size="46" transform="matrix(1 0 0 1 4315.26 9964)">Stack Depth</text></g><g><text fill="#595959" font-family="Aptos Narrow,Aptos Narrow_MSFontService,sans-serif" font-weight="400" font-stretch="semi-condensed" font-size="64" transform="matrix(1 0 0 1 3315.8 8962)">Relative performance of runtime async vs runtime async with context capture</text></g><g><rect x="3581" y="10062" width="23" height="23" fill="#A02B93"/></g><g><text fill="#595959" font-family="Aptos Narrow,Aptos Narrow_MSFontService,sans-serif" font-weight="400" font-stretch="semi-condensed" font-size="41" transform="matrix(1 0 0 1 3613.76 10085)">Return NoSuspend</text></g><g><rect x="3988" y="10062" width="23" height="23" fill="#156082"/></g><g><text fill="#595959" font-family="Aptos Narrow,Aptos Narrow_MSFontService,sans-serif" font-weight="400" font-stretch="semi-condensed" font-size="41" transform="matrix(1 0 0 1 4020.13 10085)">Throw NoSuspend</text></g><g><rect x="4384" y="10062" width="23" height="23" fill="#E97132"/></g><g><text fill="#595959" font-family="Aptos Narrow,Aptos Narrow_MSFontService,sans-serif" font-weight="400" font-stretch="semi-condensed" font-size="41" transform="matrix(1 0 0 1 4416.21 10085)">Return Suspend</text></g><g><rect x="4743" y="10062" width="23" height="23" fill="#196B24"/></g><g><text fill="#595959" font-family="Aptos Narrow,Aptos Narrow_MSFontService,sans-serif" font-weight="400" font-stretch="semi-condensed" font-size="41" transform="matrix(1 0 0 1 4775.17 10085)">Throw Suspend</text></g><rect x="3007.5" y="8834.5" width="2578" height="1305" stroke="#D9D9D9" stroke-width="3.4375" stroke-linejoin="round" stroke-miterlimit="10" fill="none"/></g></svg>
\ No newline at end of file
diff --git a/docs/design/features/runtime-handled-tasks.md b/docs/design/features/runtime-handled-tasks.md
index 2b1163ba0e0397..1f744e356d8bde 100644
--- a/docs/design/features/runtime-handled-tasks.md
+++ b/docs/design/features/runtime-handled-tasks.md
@@ -483,10 +483,11 @@ In addition, these experiments use the JIT focused implementation of `async2`. T
 These benchmarks are gathered by running the async2-eh-microbench in varying configurations.
 - NoSuspend vs Suspend - Suspend indicates that the async function awaits `Task.Yield()` before completing.
 - `Return` vs `Throw` - `Return` indicates that the iteration finishes by returning up the call stack via return instructions, and `Throw` indicates that each iteration terminated via a `throw new Exception()`.
-- `Task` vs `ValueTask` vs `Async2` - The benchmark has 3 functions which are written with the same C# code, but with different syntax using different async implementation strategies.
+- `Task` vs `ValueTask` vs `Async2` vs `Async2Capture` - The benchmark has 4 functions which are written with the same C# code, but with different syntax using different async implementation strategies.
 With `Task`, the function signature is `async Task<long> RunTask(int depth)`
 With `ValueTask`, the function signature is `async ValueTask<long> RunTask(int depth)`
-With `Task`, the function signature is `async2 long RunTask(int depth)`
+With `Async2`, the function signature is `async2 long RunTask(int depth)`
+With `Async2Capture`, the function signature is `async2 long RunTask(int depth)`, and the body of the function contains a try/finally which saves/restores the Sync and ExecutionContext. This is intended to (imperfectly) measure the cost of adjusting the runtime async model to behave exactly like the existing async model.
 
 When a graph does not specify one of the config options, the graph is a relative performance gathered by dividing the iters/250ms that the benchmark produces between the two options, and then multiplying by 100 to produce a percentage. 
 
@@ -516,6 +517,25 @@ This higher performance is likely due to the relatively large size of the async
 In addition, in this graph, only data out to a depth of 16 is presented, as the performance of throwing with new async at higher stack depths would make looking at the data for other depths difficult.
 (It reaches a level of 3851% of the performance of the traditional async function at a stack depth of 64). Not shown is the performance of `ValueTask` based async code. It is extremely close to that of the `Task` based variant.
 
+## Impact on performance of capturing/restoring ExecutionContext and SynchronizationContext
+
+![RelativePerfWithCapture](async2-vs-async2capture-relative-performance.svg)
+
+The Async2Capture variant of the testing is intended to show the impact that capturing/restoring `ExecutionContext` and `SynchronizationContext` would have on performance of the runtime generated async logic.
+In this graph, taller bars are better, and if capture had no cost, they would all be expected to be at 100%.
+The variant of code does not necessarily represent all of the costs involved in a real implementation, notably, on restore it does not attempt to ensure that the code resumes respecting the correct `SynchronizationContext` or `TaskScheduler` rules, but its probably fairly accurate for NoSuspend measurements.
+In addition, in this data we can see that the cost of the try/finally results in performance unfortunately similar to that of the `async Task` based implementation in the "Throw Suspend" scenario.
+This could likely be optimized to have performance similar to that of the higher performance runtime async model. (As the contents of the finally are well known, and would not require the entire EH machinery to be invoked.)
+
+As we can see from the graph, the performance impact on NoSuspend cases is between 20%-30% for this somewhat complex function.
+This test also fails to measure the impact of losing the ability to inline small helper functions which was expected to be one of the significant wins in practice of the new async logic.
+
+![RelativePerfWithCapture2](async-vs-async2capture-relative-performance.svg)
+
+However, while this is a substantial performance loss relative to the full speed runtime async model, the overall performance of this model relative to our existing model still shows a nice performance win.
+Looking at this graph, the performance win from a fully capturing model is not terrible.
+We may wish to strongly consider implementing a variant of this where individual methods could choose to have capture or not.
+This would allow the new model to be fully drop in, but customers could opt into higher performance/logic which can support inlining.
 
 ## Impact of try/finally regions on throw performance
 ![ThrowPerfDifferingFinallys](throw-perf-with-different-number-of-finally-on-stack.svg)
@@ -533,15 +553,15 @@ Without many finally blocks, we can see that the performance is really extraordi
 4. The performance of Task Throw NoSuspend and Task Throw Suspend is effectively the same.
 
 ## Raw data from EH microbenchmark
-| Stack Depth | Task Return NoSuspend | ValueTask Return NoSuspend | Async2 Return NoSuspend | Task Throw NoSuspend | ValueTask Throw NoSuspend | Async2 Throw NoSuspend | Task Return Suspend | ValueTask Return Suspend | Async2 Return Suspend | Task Throw Suspend | ValueTask Throw Suspend | Async2 Throw Suspend |
-| ----- | --------------------- | -------------------------- | ----------------------- | -------------------- | ------------------------- | ---------------------- | ------------------- | ------------------------ | --------------------- | ------------------ | ----------------------- | -------------------- |
-| 1     | 7,976,995.00          | 7,994,790.00               | 13,966,110.00           | 21,630.00            | 21,789.00                 | 46,434.00              | 589,057.00          | 574,660.00               | 629,310.00            | 15,563.00          | 15,584.00               | 21,208.00            |
-| 2     | 4,738,359.00          | 5,036,613.00               | 11,573,294.00           | 13,781.00            | 13,359.00                 | 38,975.00              | 390,167.00          | 349,900.00               | 617,464.00            | 9,806.00           | 9,740.00                | 21,514.00            |
-| 4     | 2,884,153.00          | 2,924,123.00               | 8,340,047.00            | 7,717.00             | 7,477.00                  | 29,887.00              | 276,584.00          | 265,590.00               | 577,707.00            | 5,778.00           | 5,495.00                | 21,288.00            |
-| 8     | 1,274,335.00          | 1,324,960.00               | 5,308,395.00            | 4,068.00             | 4,001.00                  | 20,358.00              | 217,067.00          | 198,172.00               | 474,723.00            | 3,476.00           | 3,376.00                | 20,877.00            |
-| 16    | 536,064.00            | 533,731.00                 | 2,888,828.00            | 2,104.00             | 2,063.00                  | 12,941.00              | 132,636.00          | 106,605.00               | 315,191.00            | 1,912.00           | 1,909.00                | 20,496.00            |
-| 32    | 263,823.00            | 249,821.00                 | 1,157,832.00            | 1,032.00             | 989.00                    | 7,319.00               | 71,989.00           | 55,860.00                | 184,699.00            | 973.00             | 949.00                  | 19,379.00            |
-| 64    | 113,187.00            | 114,845.00                 | 488,245.00              | 484.00               | 473.00                    | 3,936.00               | 37,086.00           | 28,365.00                | 98,769.00             | 458.00             | 456.00                  | 17,565.00            |
+| Stack Depth | Task Return NoSuspend | ValueTask Return NoSuspend | Async2 Return NoSuspend | Task Throw NoSuspend | ValueTask Throw NoSuspend | Async2 Throw NoSuspend | Task Return Suspend | ValueTask Return Suspend | Async2 Return Suspend | Task Throw Suspend | ValueTask Throw Suspend | Async2 Throw Suspend | Async2Capture Return NoSuspend | Async2Capture Throw NoSuspend | Async2Capture Return Suspend | Async2Capture Throw Suspend |
+| ----- | --------------------- | -------------------------- | ----------------------- | -------------------- | ------------------------- | ---------------------- | ------------------- | ------------------------ | --------------------- | ------------------ | ----------------------- | -------------------- | --------------------- | ------------------ | ----------------------- | -------------------- |
+| 1     | 7,976,995.00          | 7,994,790.00               | 13,966,110.00           | 21,630.00            | 21,789.00                 | 46,434.00              | 589,057.00          | 574,660.00               | 629,310.00            | 15,563.00          | 15,584.00               | 21,208.00            | 12249438.00           | 45002.00           | 624336.00               | 21005.00             |
+| 2     | 4,738,359.00          | 5,036,613.00               | 11,573,294.00           | 13,781.00            | 13,359.00                 | 38,975.00              | 390,167.00          | 349,900.00               | 617,464.00            | 9,806.00           | 9,740.00                | 21,514.00            | 9306011.00            | 37769.00           | 594730.00               | 13163.00             |
+| 4     | 2,884,153.00          | 2,924,123.00               | 8,340,047.00            | 7,717.00             | 7,477.00                  | 29,887.00              | 276,584.00          | 265,590.00               | 577,707.00            | 5,778.00           | 5,495.00                | 21,288.00            | 6438143.00            | 28318.00           | 543293.00               | 7253.00              |
+| 8     | 1,274,335.00          | 1,324,960.00               | 5,308,395.00            | 4,068.00             | 4,001.00                  | 20,358.00              | 217,067.00          | 198,172.00               | 474,723.00            | 3,476.00           | 3,376.00                | 20,877.00            | 3836147.00            | 18871.00           | 412340.00               | 4160.00              |
+| 16    | 536,064.00            | 533,731.00                 | 2,888,828.00            | 2,104.00             | 2,063.00                  | 12,941.00              | 132,636.00          | 106,605.00               | 315,191.00            | 1,912.00           | 1,909.00                | 20,496.00            | 2083270.00            | 11978.00           | 275944.00               | 2234.00              |
+| 32    | 263,823.00            | 249,821.00                 | 1,157,832.00            | 1,032.00             | 989.00                    | 7,319.00               | 71,989.00           | 55,860.00                | 184,699.00            | 973.00             | 949.00                  | 19,379.00            | 861826.00             | 6705.00            | 156610.00               | 1133.00              |
+| 64    | 113,187.00            | 114,845.00                 | 488,245.00              | 484.00               | 473.00                    | 3,936.00               | 37,086.00           | 28,365.00                | 98,769.00             | 458.00             | 456.00                  | 17,565.00            | 393380.00             | 3654.00            | 83977.00                | 593.00               |
 
 | Finally blocks on stack | Task Throw NoSuspend | Async2 Throw NoSuspend | Task Throw Suspend | Async2 Throw Suspend |
 | ----------------------- | -------------------- | ---------------------- | ------------------ | -------------------- |
diff --git a/src/tests/Loader/async/async2-eh-microbench.cs b/src/tests/Loader/async/async2-eh-microbench.cs
index ab4beb0e182b0d..b60d6a9b327201 100644
--- a/src/tests/Loader/async/async2-eh-microbench.cs
+++ b/src/tests/Loader/async/async2-eh-microbench.cs
@@ -50,6 +50,7 @@ public static async Task AsyncEntry()
             for (int j = 0; j < 40; j++)
             {
                 await warmupBm.Run("Async2");
+                await warmupBm.Run("Async2WithContextSaveRestore");
                 await warmupBm.Run("Task");
                 await warmupBm.Run("ValueTask");
             }
@@ -60,6 +61,7 @@ public static async Task AsyncEntry()
 
         Console.WriteLine("Warmup done, running benchmark");
         await RunBench(yieldFrequency, depth, finallyRate, throwOrReturn, "Async2");
+        await RunBench(yieldFrequency, depth, finallyRate, throwOrReturn, "Async2WithContextSaveRestore");
         await RunBench(yieldFrequency, depth, finallyRate, throwOrReturn, "Task");
         await RunBench(yieldFrequency, depth, finallyRate, throwOrReturn, "ValueTask");
     }
@@ -104,6 +106,8 @@ public async2 Task<long> Run(string type)
         {
             if (type == "Async2")
                 return await RunAsync2(_depth);
+            if (type == "Async2WithContextSaveRestore")
+                return await RunAsync2WithContextSaveRestore(_depth);
             if (type == "Task")
                 return await RunTask(_depth);
             if (type == "ValueTask")
@@ -242,6 +246,119 @@ public async ValueTask<long> RunValueTask(int depth)
             return result;
         }
 
+        public class FakeSyncContext {}
+        public class FakeExecContext
+        {
+            [MethodImpl(MethodImplOptions.NoInlining)]
+            public static void RestoreChangedContextToThread(FakeThread thread, FakeExecContext execContext, FakeExecContext newExecContext)
+            {
+            }
+        }
+        public class FakeThread
+        {
+            public FakeSyncContext _syncContext;
+            public FakeExecContext _execContext;
+        }
+        [ThreadStatic]
+        public static FakeThread CurrentThread;
+
+        // This case is used to test the impact of save/restore of the sync and execution context on performance
+        // The intent here is to measure what the performance impact of maintaining the current async semantics with
+        // the new implementation.
+        public async2 Task<long> RunAsync2WithContextSaveRestore(int depth)
+        {
+            FakeThread thread = CurrentThread;
+            if (thread == null)
+            {
+                CurrentThread = new FakeThread();
+                thread = CurrentThread;
+            }
+
+            FakeExecContext? previousExecutionCtx = thread._execContext;
+            FakeSyncContext? previousSyncCtx = thread._syncContext;
+
+            try
+            {
+                int liveState1 = depth * 3 + _yieldCount;
+                int liveState2 = depth;
+                double liveState3 = _yieldCount;
+
+                if (depth == 0)
+                {
+                    int currentAwaitCount = 0;
+
+                    while (currentAwaitCount < _yieldCount)
+                    {
+                        currentAwaitCount++;
+                        await Task.Yield();
+                    }
+
+                    if (_throwOrReturn)
+                        throw new Exception();
+                    return 8375983;
+                }
+
+                long result = 0;
+
+                if (depth == _depth)
+                {
+                    int time = Warmup ? 5 : 250;
+
+                    Stopwatch timer = Stopwatch.StartNew();
+
+                    long numIters = 0;
+
+                    while (timer.ElapsedMilliseconds < time)
+                    {
+                        try
+                        {
+                            result = await RunAsync2WithContextSaveRestore(depth - 1);
+                        }
+                        catch (Exception e)
+                        {
+
+                        }
+                        numIters++;
+
+                    }
+                    return numIters;
+                }
+                else if ((depth % _finallyRate) == 0)
+                {
+                    try
+                    {
+                        result = await RunAsync2WithContextSaveRestore(depth - 1);
+                    }
+                    finally
+                    {
+                        Sink = (int)liveState1 + (int)liveState2 + (int)(1 / liveState3) + depth;
+                    }
+                }
+                else
+                {
+                    result = await RunAsync2WithContextSaveRestore(depth - 1);
+                }
+
+                Sink = (int)liveState1 + (int)liveState2 + (int)(1 / liveState3) + depth;
+                return result;
+            }
+            finally
+            {
+                // The common case is that these have not changed, so avoid the cost of a write barrier if not needed.
+                if (previousSyncCtx != thread._syncContext)
+                {
+                    // Restore changed SynchronizationContext back to previous
+                    thread._syncContext = previousSyncCtx;
+                }
+
+                FakeExecContext? currentExecutionCtx = thread._execContext;
+                if (previousExecutionCtx != currentExecutionCtx)
+                {
+                    FakeExecContext.RestoreChangedContextToThread(thread, previousExecutionCtx, currentExecutionCtx);
+                }
+            }
+        }
+
         public async2 Task<long> RunAsync2(int depth)
         {
             int liveState1 = depth * 3 + _yieldCount;

From 6b47db0c8dbb545c969a0630bb88453724ec52d4 Mon Sep 17 00:00:00 2001
From: David Wrighton <davidwr@microsoft.com>
Date: Thu, 16 Nov 2023 15:50:46 -0800
Subject: [PATCH 087/203] Fix the roslyn build script (#2422)

---
 buildroslynnugets.cmd | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/buildroslynnugets.cmd b/buildroslynnugets.cmd
index 7ae6eb9dca4101..45e868567d6af6 100644
--- a/buildroslynnugets.cmd
+++ b/buildroslynnugets.cmd
@@ -5,7 +5,7 @@ set ASYNC_SUFFIX=async-7
 set ASYNC_ROSLYN_BRANCH=demos/async2-experiment
 
 cd ..
-if not exist async-roslyn-repo git clone -b %ASYNC_ROSLYN_BRANCH% -o async_roslyn_remote https://github.com/dotnet/runtimelab.git async-roslyn-repo
+if not exist async-roslyn-repo git clone -b %ASYNC_ROSLYN_BRANCH% -o async_roslyn_remote https://github.com/dotnet/roslyn.git async-roslyn-repo
 
 pushd async-roslyn-repo
 

From 842ad04659eb79350c818bdb75f2e09b5040fa78 Mon Sep 17 00:00:00 2001
From: Jakob Botsch Nielsen <jakob.botsch.nielsen@gmail.com>
Date: Fri, 17 Nov 2023 13:59:33 +0100
Subject: [PATCH 088/203] Null out TLS head continuation reference

Avoid permanently holding on to a reference to the continuation chain
from the previous suspension in TLS.
---
 .../System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs
index 32d0b0c3f60f28..1dd6834175d8db 100644
--- a/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs
+++ b/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs
@@ -466,7 +466,9 @@ private static Continuation GetHeadContinuation(AwaitableProxy awaitableProxy)
         {
             ref RuntimeAsyncAwaitState state = ref t_runtimeAsyncAwaitState;
             awaitableProxy._notifier = state.Notifier;
-            return state.SentinelContinuation!.Next!;
+            Continuation head = state.SentinelContinuation!.Next!;
+            state.SentinelContinuation.Next = null;
+            return head;
         }
 
         private static async Task<T> FinalizeTaskReturningThunk<T>(Continuation continuation)

From 282218b02b2ecab37c6de61880f8090c094fe9f1 Mon Sep 17 00:00:00 2001
From: Jakob Botsch Nielsen <jakob.botsch.nielsen@gmail.com>
Date: Fri, 17 Nov 2023 14:11:43 +0100
Subject: [PATCH 089/203] Manual CSE

---
 .../Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs       | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs
index 1dd6834175d8db..b22a4b1445b07b 100644
--- a/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs
+++ b/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs
@@ -466,8 +466,9 @@ private static Continuation GetHeadContinuation(AwaitableProxy awaitableProxy)
         {
             ref RuntimeAsyncAwaitState state = ref t_runtimeAsyncAwaitState;
             awaitableProxy._notifier = state.Notifier;
-            Continuation head = state.SentinelContinuation!.Next!;
-            state.SentinelContinuation.Next = null;
+            Continuation sentinelContinuation = state.SentinelContinuation!;
+            Continuation head = sentinelContinuation.Next!;
+            sentinelContinuation.Next = null;
             return head;
         }
 

From 5e9973bc5b639a9131de3a4b7a702c061b9556fa Mon Sep 17 00:00:00 2001
From: Jakob Botsch Nielsen <Jakob.botsch.nielsen@gmail.com>
Date: Fri, 17 Nov 2023 19:13:18 +0100
Subject: [PATCH 090/203] Fix paths in buildroslynnugets.cmd (#2426)

---
 buildroslynnugets.cmd | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/buildroslynnugets.cmd b/buildroslynnugets.cmd
index 45e868567d6af6..1051eda3506e04 100644
--- a/buildroslynnugets.cmd
+++ b/buildroslynnugets.cmd
@@ -26,9 +26,9 @@ pushd %~dp0
 
 md roslynpackages
 
-copy ..\dotnet-roslyn\artifacts\packages\Release\Shipping\Microsoft.Net.Compilers.Toolset.4.9.0-%ASYNC_SUFFIX%.nupkg roslynpackages
-copy ..\dotnet-roslyn\artifacts\packages\Release\Shipping\Microsoft.CodeAnalysis.Workspaces.Common.4.9.0-%ASYNC_SUFFIX%.nupkg roslynpackages
-copy ..\dotnet-roslyn\artifacts\packages\Release\Shipping\Microsoft.CodeAnalysis.CSharp.Workspaces.4.9.0-%ASYNC_SUFFIX%.nupkg roslynpackages
-copy ..\dotnet-roslyn\artifacts\packages\Release\Shipping\Microsoft.CodeAnalysis.CSharp.4.9.0-%ASYNC_SUFFIX%.nupkg roslynpackages
-copy ..\dotnet-roslyn\artifacts\packages\Release\Shipping\Microsoft.CodeAnalysis.Common.4.9.0-%ASYNC_SUFFIX%.nupkg roslynpackages
+copy ..\async-roslyn-repo\artifacts\packages\Release\Shipping\Microsoft.Net.Compilers.Toolset.4.9.0-%ASYNC_SUFFIX%.nupkg roslynpackages
+copy ..\async-roslyn-repo\artifacts\packages\Release\Shipping\Microsoft.CodeAnalysis.Workspaces.Common.4.9.0-%ASYNC_SUFFIX%.nupkg roslynpackages
+copy ..\async-roslyn-repo\artifacts\packages\Release\Shipping\Microsoft.CodeAnalysis.CSharp.Workspaces.4.9.0-%ASYNC_SUFFIX%.nupkg roslynpackages
+copy ..\async-roslyn-repo\artifacts\packages\Release\Shipping\Microsoft.CodeAnalysis.CSharp.4.9.0-%ASYNC_SUFFIX%.nupkg roslynpackages
+copy ..\async-roslyn-repo\artifacts\packages\Release\Shipping\Microsoft.CodeAnalysis.Common.4.9.0-%ASYNC_SUFFIX%.nupkg roslynpackages
 

From 0b1105a8381ab11c46d7147e9ee57c48c94a4449 Mon Sep 17 00:00:00 2001
From: Jakob Botsch Nielsen <Jakob.botsch.nielsen@gmail.com>
Date: Fri, 17 Nov 2023 19:15:05 +0100
Subject: [PATCH 091/203] Switch to JIT prototype by default; add support
 matrix (#2427)

---
 README.md                                     | 18 +++++++++++++++++-
 docs/design/features/runtime-handled-tasks.md |  4 ++++
 src/coreclr/inc/clrconfigvalues.h             |  2 +-
 src/coreclr/jit/jitconfigvalues.h             |  2 +-
 4 files changed, 23 insertions(+), 3 deletions(-)

diff --git a/README.md b/README.md
index 1113fe689cf645..ca549b195ed4ee 100644
--- a/README.md
+++ b/README.md
@@ -2,9 +2,25 @@
 
 This branch contains experimental fork of CoreCLR [.NET runtime](http://github.com/dotnet/runtime) where we explore implementing async directly in the runtime instead of generating state machines in the IL compiler (Roslyn).
 
+## Current status
+
+We support two prototypes: a JIT-based codegen prototype, and an unwinder-only based prototype.
+The prototypes are described in detail in the design document, see below.
+The former prototype is the default; the latter can be switched to by setting `DOTNET_RuntimeAsyncViaJitGeneratedStateMachines=0`. 
+
+Current support in the prototypes looks like the following:
+
+| **Feature/characteristic**               |  **JIT based state machines**  |  **Unwinder based state machines**  |
+|------------------------------------------|:------------------------------:|:-----------------------------------:|
+| **Generics**                             |               ❌               |                  ❌                 |
+| **Byrefs live across suspension points** |               ❌               |                  ✅                 |
+| **Exception handling**                   |               ✅               |                  ❌                 |
+| **Returns via return buffers**           |               ✅               |                  ❌                 |
+
+
 ## Samples
 
-See the tests in src/tests/loader/async.
+See the tests in [src/tests/Loader/async](src/tests/Loader/async).
 
 ## Documentation
 
diff --git a/docs/design/features/runtime-handled-tasks.md b/docs/design/features/runtime-handled-tasks.md
index 1f744e356d8bde..63e529b66512b7 100644
--- a/docs/design/features/runtime-handled-tasks.md
+++ b/docs/design/features/runtime-handled-tasks.md
@@ -153,6 +153,8 @@ Async2 methods will not be visible in reflection via the `Type.GetMethod`, `Type
 
 The general design is to leverage the notion that a normal code generated function at a function call point is effectively a state machine. The index for resumption is the IP that returning to the function will set, and the stackframe + saved registers are the current state of the state machine. I believe we can achieve performance comparable to synchronous code with this scheme for non-suspended scenarios, and we may achieve acceptable overall performance as an async mechanism if suspension is relatively rare.
 
+To enable this prototype, set `DOTNET_RuntimeAsyncViaJitGeneratedStateMachines=0`.
+
 ### JIT Code changes
 1. Do not allow InlinedCallFrames to be linked to the frame chain across a suspend (Or disable p/invoke inlining in these methods)
 2. Report frame pointers as ByRef
@@ -227,6 +229,8 @@ In the second prototype we leave it up to the JIT to generate the state machines
 When awaiting an async2 function from within an async2 function the JIT generates suspension code to capture the live state in a continuation.
 In addition, the JIT generates code for each of these suspension points to be able to resume from the continuation that was created.
 
+This prototype is enabled by default, or when `DOTNET_RuntimeAsyncViaJitGeneratedStateMachines=1`.
+
 ### Suspension
 
 Communicating suspension is done by returning a non-zero continuation, using a special calling convention.
diff --git a/src/coreclr/inc/clrconfigvalues.h b/src/coreclr/inc/clrconfigvalues.h
index 1ee66824c6a233..58fca99ce959e0 100644
--- a/src/coreclr/inc/clrconfigvalues.h
+++ b/src/coreclr/inc/clrconfigvalues.h
@@ -803,7 +803,7 @@ RETAIL_CONFIG_DWORD_INFO(EXTERNAL_EnableArm64Rcpc2,             W("EnableArm64Rc
 ///
 /// Runtime async
 ///
-RETAIL_CONFIG_DWORD_INFO(EXTERNAL_RuntimeAsyncViaJitGeneratedStateMachines, W("RuntimeAsyncViaJitGeneratedStateMachines"), 0, "Use JIT generated state machines instead of unwinding-based runtime async")
+RETAIL_CONFIG_DWORD_INFO(EXTERNAL_RuntimeAsyncViaJitGeneratedStateMachines, W("RuntimeAsyncViaJitGeneratedStateMachines"), 1, "Use JIT generated state machines instead of unwinding-based runtime async")
 
 ///
 /// Uncategorized
diff --git a/src/coreclr/jit/jitconfigvalues.h b/src/coreclr/jit/jitconfigvalues.h
index 1cdd96279a9deb..df90f3bebcea6b 100644
--- a/src/coreclr/jit/jitconfigvalues.h
+++ b/src/coreclr/jit/jitconfigvalues.h
@@ -654,7 +654,7 @@ CONFIG_INTEGER(JitEnableHeadTailMerge, W("JitEnableHeadTailMerge"), 1)
 CONFIG_INTEGER(JitEnablePhysicalPromotion, W("JitEnablePhysicalPromotion"), 1)
 
 // Should JIT generate state machines for async2 methods?
-CONFIG_INTEGER(RuntimeAsyncViaJitGeneratedStateMachines, W("RuntimeAsyncViaJitGeneratedStateMachines"), 0)
+CONFIG_INTEGER(RuntimeAsyncViaJitGeneratedStateMachines, W("RuntimeAsyncViaJitGeneratedStateMachines"), 1)
 
 #if defined(DEBUG)
 // JitFunctionFile: Name of a file that contains a list of functions. If the currently compiled function is in the

From fc15ad6fccd7772767b2c01eafed399286beb05a Mon Sep 17 00:00:00 2001
From: Jakob Botsch Nielsen <jakob.botsch.nielsen@gmail.com>
Date: Fri, 17 Nov 2023 21:28:42 +0100
Subject: [PATCH 092/203] Rename GetHeadContinuation -> UnlinkHeadContinuation

---
 .../Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs
index b22a4b1445b07b..dbf462c1b6e2ac 100644
--- a/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs
+++ b/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs
@@ -462,7 +462,7 @@ public void UnsafeOnCompleted(Action action)
             public void GetResult() {}
         }
 
-        private static Continuation GetHeadContinuation(AwaitableProxy awaitableProxy)
+        private static Continuation UnlinkHeadContinuation(AwaitableProxy awaitableProxy)
         {
             ref RuntimeAsyncAwaitState state = ref t_runtimeAsyncAwaitState;
             awaitableProxy._notifier = state.Notifier;
@@ -496,7 +496,7 @@ private static async Task<T> FinalizeTaskReturningThunk<T>(Continuation continua
 
             while (true)
             {
-                Continuation headContinuation = GetHeadContinuation(awaitableProxy);
+                Continuation headContinuation = UnlinkHeadContinuation(awaitableProxy);
                 await awaitableProxy;
                 Continuation? finalResult = DispatchContinuations(headContinuation, out Exception? ex);
                 if (finalResult != null)
@@ -529,7 +529,7 @@ private static async Task FinalizeTaskReturningThunk(Continuation continuation)
 
             while (true)
             {
-                Continuation headContinuation = GetHeadContinuation(awaitableProxy);
+                Continuation headContinuation = UnlinkHeadContinuation(awaitableProxy);
                 await awaitableProxy;
                 Continuation? finalResult = DispatchContinuations(headContinuation, out Exception? ex);
                 if (finalResult != null)
@@ -566,7 +566,7 @@ private static async ValueTask<T> FinalizeValueTaskReturningThunk<T>(Continuatio
 
             while (true)
             {
-                Continuation headContinuation = GetHeadContinuation(awaitableProxy);
+                Continuation headContinuation = UnlinkHeadContinuation(awaitableProxy);
                 await awaitableProxy;
                 Continuation? finalResult = DispatchContinuations(headContinuation, out Exception? ex);
                 if (finalResult != null)
@@ -599,7 +599,7 @@ private static async ValueTask FinalizeValueTaskReturningThunk(Continuation cont
 
             while (true)
             {
-                Continuation headContinuation = GetHeadContinuation(awaitableProxy);
+                Continuation headContinuation = UnlinkHeadContinuation(awaitableProxy);
                 await awaitableProxy;
                 Continuation? finalResult = DispatchContinuations(headContinuation, out Exception? ex);
                 if (finalResult != null)

From ba3bbb0eda878e98bd5893029945fc52bd05ab87 Mon Sep 17 00:00:00 2001
From: Jakob Botsch Nielsen <Jakob.botsch.nielsen@gmail.com>
Date: Tue, 28 Nov 2023 20:17:09 +0100
Subject: [PATCH 093/203] Fix resumption stub target in the presence of
 backpatching (#2428)

The slot the tier 0 method gets is backpatched to point to the tier 1
method, so allocate a new slot for the target address to ensure that
resumption stubs always resume in the right native code version.

Also fix the size used when storing the state field; we were storing 8
bytes, but the field is only 4 bytes (this wouldn't cause any ill
effects as the flags field comes right after).
---
 src/coreclr/jit/async.cpp       |  2 +-
 src/coreclr/vm/jitinterface.cpp | 20 +++++++++++++++++++-
 src/coreclr/vm/jitinterface.h   |  4 ++++
 3 files changed, 24 insertions(+), 2 deletions(-)

diff --git a/src/coreclr/jit/async.cpp b/src/coreclr/jit/async.cpp
index bb2bc7b2e981e4..2837421957aec9 100644
--- a/src/coreclr/jit/async.cpp
+++ b/src/coreclr/jit/async.cpp
@@ -579,7 +579,7 @@ void Async2Transformation::Transform(
     // Fill in 'state'
     newContinuation       = m_comp->gtNewLclvNode(m_newContinuationVar, TYP_REF);
     unsigned stateOffset  = m_comp->info.compCompHnd->getFieldOffset(m_async2Info.continuationStateFldHnd);
-    GenTree* stateNumNode = m_comp->gtNewIconNode((ssize_t)stateNum, TYP_I_IMPL);
+    GenTree* stateNumNode = m_comp->gtNewIconNode((ssize_t)stateNum, TYP_INT);
     GenTree* storeState   = StoreAtOffset(newContinuation, stateOffset, stateNumNode);
     LIR::AsRange(retBB).InsertAtEnd(LIR::SeqTree(m_comp, storeState));
 
diff --git a/src/coreclr/vm/jitinterface.cpp b/src/coreclr/vm/jitinterface.cpp
index fbe44befa6223c..b759fb90876813 100644
--- a/src/coreclr/vm/jitinterface.cpp
+++ b/src/coreclr/vm/jitinterface.cpp
@@ -10786,6 +10786,16 @@ void CEEJitInfo::WriteCodeBytes()
     }
 }
 
+void CEEJitInfo::PublishFinalCodeAddress(PCODE addr)
+{
+    LIMITED_METHOD_CONTRACT;
+
+    if (m_finalCodeAddressSlot != NULL)
+    {
+        *m_finalCodeAddressSlot = addr;
+    }
+}
+
 /*********************************************************************/
 void CEEJitInfo::BackoutJitData(EEJitManager * jitMgr)
 {
@@ -13082,6 +13092,8 @@ PCODE UnsafeJitFunction(PrepareCodeConfig* config,
         ret |= THUMB_CODE;
 #endif
 
+        jitInfo.PublishFinalCodeAddress(ret);
+
         // We are done
         break;
     }
@@ -14376,7 +14388,13 @@ CORINFO_METHOD_HANDLE CEEJitInfo::getAsyncResumptionStub()
     }
     else
     {
-        pCode->EmitLDC((DWORD_PTR)config->GetNativeCodeSlot());
+        {
+            AllocMemTracker pamTracker;
+            m_finalCodeAddressSlot = (PCODE*)pamTracker.Track(m_pMethodBeingCompiled->GetLoaderAllocator()->GetHighFrequencyHeap()->AllocMem(S_SIZE_T(sizeof(PCODE))));
+            pamTracker.SuppressRelease();
+        }
+
+        pCode->EmitLDC((DWORD_PTR)m_finalCodeAddressSlot);
         pCode->EmitLDIND_I();
     }
 
diff --git a/src/coreclr/vm/jitinterface.h b/src/coreclr/vm/jitinterface.h
index 8cf2ab64f1f833..939712b234d255 100644
--- a/src/coreclr/vm/jitinterface.h
+++ b/src/coreclr/vm/jitinterface.h
@@ -818,6 +818,7 @@ class CEEJitInfo : public CEEInfo
           m_pPatchpointInfoFromRuntime(NULL),
           m_ilOffset(0),
 #endif
+          m_finalCodeAddressSlot(NULL),
           m_gphCache()
     {
         CONTRACTL
@@ -901,6 +902,8 @@ class CEEJitInfo : public CEEInfo
 
     void WriteCode(EEJitManager * jitMgr);
 
+    void PublishFinalCodeAddress(PCODE addr);
+
     void setPatchpointInfo(PatchpointInfo* patchpointInfo) override final;
     PatchpointInfo* getOSRInfo(unsigned* ilOffset) override final;
 
@@ -979,6 +982,7 @@ protected :
     PatchpointInfo        * m_pPatchpointInfoFromRuntime;
     unsigned                m_ilOffset;
 #endif
+    PCODE* m_finalCodeAddressSlot;
 
     // The first time a call is made to CEEJitInfo::GetProfilingHandle() from this thread
     // for this method, these values are filled in.   Thereafter, these values are used

From a691996e923bf47233940eaf9f14148c29e0e56f Mon Sep 17 00:00:00 2001
From: Vladimir Sadov <vsadov@microsoft.com>
Date: Thu, 30 Nov 2023 07:07:36 -0800
Subject: [PATCH 094/203] [Async-2] A few small tweaks on the managed side of
 state-machine based implementation. (#2431)

* A few tweaks
    - made AwaitableProxy a struct
    - unlink Notifier
    - use ExceptionDispatchInfo to rethrow

* rethrow from DispatchContinuations
---
 .../RuntimeHelpers.CoreCLR.cs                 | 60 ++++++++-----------
 1 file changed, 24 insertions(+), 36 deletions(-)

diff --git a/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs
index dbf462c1b6e2ac..c26260014f54b0 100644
--- a/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs
+++ b/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs
@@ -5,6 +5,7 @@
 using System.Diagnostics.CodeAnalysis;
 using System.Reflection;
 using System.Runtime.CompilerServices;
+using System.Runtime.ExceptionServices;
 using System.Runtime.InteropServices;
 using System.Runtime.Serialization;
 using System.Runtime.Versioning;
@@ -434,9 +435,14 @@ private struct RuntimeAsyncAwaitState
         [Intrinsic]
         private static void SuspendAsync2(Continuation continuation) => throw new UnreachableException();
 
-        private sealed class AwaitableProxy : ICriticalNotifyCompletion
+        private struct AwaitableProxy : ICriticalNotifyCompletion
         {
-            public INotifyCompletion? _notifier;
+            private readonly INotifyCompletion _notifier;
+
+            public AwaitableProxy(INotifyCompletion notifier)
+            {
+                _notifier = notifier;
+            }
 
             public bool IsCompleted => false;
 
@@ -462,10 +468,12 @@ public void UnsafeOnCompleted(Action action)
             public void GetResult() {}
         }
 
-        private static Continuation UnlinkHeadContinuation(AwaitableProxy awaitableProxy)
+        private static Continuation UnlinkHeadContinuation(out AwaitableProxy awaitableProxy)
         {
             ref RuntimeAsyncAwaitState state = ref t_runtimeAsyncAwaitState;
-            awaitableProxy._notifier = state.Notifier;
+            awaitableProxy = new AwaitableProxy(state.Notifier!);
+            state.Notifier = null;
+
             Continuation sentinelContinuation = state.SentinelContinuation!;
             Continuation head = sentinelContinuation.Next!;
             sentinelContinuation.Next = null;
@@ -492,19 +500,14 @@ private static async Task<T> FinalizeTaskReturningThunk<T>(Continuation continua
 
             continuation.Next = finalContinuation;
 
-            AwaitableProxy awaitableProxy = new AwaitableProxy();
-
             while (true)
             {
-                Continuation headContinuation = UnlinkHeadContinuation(awaitableProxy);
+                Continuation headContinuation = UnlinkHeadContinuation(out var awaitableProxy);
                 await awaitableProxy;
-                Continuation? finalResult = DispatchContinuations(headContinuation, out Exception? ex);
+                Continuation? finalResult = DispatchContinuations(headContinuation);
                 if (finalResult != null)
                 {
                     Debug.Assert(finalResult == finalContinuation);
-                    if (ex != null)
-                        throw ex;
-
                     if (IsReferenceOrContainsReferences<T>())
                     {
                         return (T)finalResult.GCData![0];
@@ -525,18 +528,14 @@ private static async Task FinalizeTaskReturningThunk(Continuation continuation)
             };
             continuation.Next = finalContinuation;
 
-            AwaitableProxy awaitableProxy = new AwaitableProxy();
-
             while (true)
             {
-                Continuation headContinuation = UnlinkHeadContinuation(awaitableProxy);
+                Continuation headContinuation = UnlinkHeadContinuation(out var awaitableProxy);
                 await awaitableProxy;
-                Continuation? finalResult = DispatchContinuations(headContinuation, out Exception? ex);
+                Continuation? finalResult = DispatchContinuations(headContinuation);
                 if (finalResult != null)
                 {
                     Debug.Assert(finalResult == finalContinuation);
-                    if (ex != null)
-                        throw ex;
                     return;
                 }
             }
@@ -562,19 +561,14 @@ private static async ValueTask<T> FinalizeValueTaskReturningThunk<T>(Continuatio
 
             continuation.Next = finalContinuation;
 
-            AwaitableProxy awaitableProxy = new AwaitableProxy();
-
             while (true)
             {
-                Continuation headContinuation = UnlinkHeadContinuation(awaitableProxy);
+                Continuation headContinuation = UnlinkHeadContinuation(out var awaitableProxy);
                 await awaitableProxy;
-                Continuation? finalResult = DispatchContinuations(headContinuation, out Exception? ex);
+                Continuation? finalResult = DispatchContinuations(headContinuation);
                 if (finalResult != null)
                 {
                     Debug.Assert(finalResult == finalContinuation);
-                    if (ex != null)
-                        throw ex;
-
                     if (IsReferenceOrContainsReferences<T>())
                     {
                         return (T)finalResult.GCData![0];
@@ -595,33 +589,28 @@ private static async ValueTask FinalizeValueTaskReturningThunk(Continuation cont
             };
             continuation.Next = finalContinuation;
 
-            AwaitableProxy awaitableProxy = new AwaitableProxy();
-
             while (true)
             {
-                Continuation headContinuation = UnlinkHeadContinuation(awaitableProxy);
+                Continuation headContinuation = UnlinkHeadContinuation(out var awaitableProxy);
                 await awaitableProxy;
-                Continuation? finalResult = DispatchContinuations(headContinuation, out Exception? ex);
+                Continuation? finalResult = DispatchContinuations(headContinuation);
                 if (finalResult != null)
                 {
                     Debug.Assert(finalResult == finalContinuation);
-                    if (ex != null)
-                        throw ex;
                     return;
                 }
             }
         }
 
         // Return a continuation object if that is the one which has the final
-        // result of the Task, in this case exceptionResult MAY be set to an
-        // exception, which is the real output of the series of continuations
+        // result of the Task, if the real output of the series of continuations was
+        // an exception, it is allowed to propagate out.
         // OR
         // return NULL to indicate that this isn't yet done.
-        private static unsafe Continuation? DispatchContinuations(Continuation? continuation, out Exception? exceptionResult)
+        private static unsafe Continuation? DispatchContinuations(Continuation? continuation)
         {
             Debug.Assert(continuation != null);
 
-            exceptionResult = null;
             while (true)
             {
                 Continuation? newContinuation;
@@ -634,8 +623,7 @@ private static async ValueTask FinalizeValueTaskReturningThunk(Continuation cont
                     continuation = UnwindToPossibleHandler(continuation);
                     if (continuation.Resume == null)
                     {
-                        exceptionResult = ex;
-                        return continuation;
+                        throw;
                     }
 
                     continuation.GCData![(continuation.Flags & CorInfoContinuationFlags.CORINFO_CONTINUATION_RESULT_IN_GCDATA) != 0 ? 1 : 0] = ex;

From 5205ef3ee4741e2c217d58804d0f1a60e863b662 Mon Sep 17 00:00:00 2001
From: Vladimir Sadov <vsadov@microsoft.com>
Date: Tue, 12 Dec 2023 11:32:32 -0800
Subject: [PATCH 095/203] [Async-2]: Tasklet aging (#2425)

* tests for the root scan

* root scan extra API

* aging works

* stack lists

* age by whole stacks

* switch test to new syntax

* Updated the test

* testcase update

* writeup on GC pauses and allocated size
---
 docs/design/features/runtime-handled-tasks.md |  52 ++++
 src/coreclr/gc/env/gcenv.ee.h                 |   4 +
 .../gc/env/gctoeeinterface.standalone.inl     |  10 +
 src/coreclr/gc/gcenv.ee.standalone.inl        |  12 +
 src/coreclr/gc/gcinterface.ee.h               |   6 +
 src/coreclr/gc/gcscan.cpp                     |  15 +-
 src/coreclr/gc/sample/gcenv.ee.cpp            |   8 +
 src/coreclr/inc/clrconfigvalues.h             |   1 +
 src/coreclr/nativeaot/Runtime/gcrhenv.cpp     |   8 +
 src/coreclr/vm/eeconfig.cpp                   |   1 +
 src/coreclr/vm/eeconfig.h                     |   2 +
 src/coreclr/vm/gcenv.ee.cpp                   |  26 +-
 src/coreclr/vm/qcallentrypoints.cpp           |   1 +
 src/coreclr/vm/runtimesuspension.cpp          | 234 +++++++++++++++---
 src/coreclr/vm/runtimesuspension.h            |   6 +-
 .../CompilerServices/RuntimeHelpers.cs        |  30 ++-
 src/tests/Loader/async/async2gcRootsScan.cs   | 211 ++++++++++++++++
 .../Loader/async/async2gcRootsScan.csproj     |   8 +
 18 files changed, 597 insertions(+), 38 deletions(-)
 create mode 100644 src/tests/Loader/async/async2gcRootsScan.cs
 create mode 100644 src/tests/Loader/async/async2gcRootsScan.csproj

diff --git a/docs/design/features/runtime-handled-tasks.md b/docs/design/features/runtime-handled-tasks.md
index 63e529b66512b7..1352da07d81402 100644
--- a/docs/design/features/runtime-handled-tasks.md
+++ b/docs/design/features/runtime-handled-tasks.md
@@ -618,3 +618,55 @@ In the current experiment we have looked into two implementing strategies:
 ## On unmanaged pointers.
 We cannot possibly track unmanaged pointers that refer to the stack across suspensions. Disallowing such scenarios would not be a take-back though since currently C# does not allow `await` in unsafe context, therefore await and unmanaged pointers cannot mix.
 
+## Impact on GC (heap size, pauses).
+
+We have examined the behavior of experimental async2 implementations on GC. For simplicity we looked into behavior of a simple benchmark that ensures 10000 suspended tasks at some point, each containing 100 stack frames with at least 1 object and 1 integer alive across await and thus requiring capture. At the time the tasks are suspended, the benchmark forces a number of Gen0 GCs and measures average time taken by GCs as well as the managed heap size.
+To further reduce the number of involved variables, the benchmark is single-threaded and Workstation GC was used.
+
+The benchmark produces very little garbage when the masurements are performed as the main interest is not on how GC handles allocations, but on how the presence of captured tasks impacts GC.
+
+Our observations (on x64, AMD 5950X):
+
+**===== Async1**:
+
+There is some noise, but a typical Gen0 pause in the above scenario was: 120 microseconds.
+The managed heap size (as reported by `GetGCMemoryInfo.HeapSizeBytes`) is stable at 130 Mb.
+The peak working set (as reported by `Task Manager`) is 148 Mb. 
+
+**=====Async2 (unwinding/stack-capturing based)**:
+
+When GC pauses were measured as-is, the results were very disappointing - **48690 microsecond**. (**~400x worse** than async1 and generally unacceptable for a Gen0 pause)
+
+The reasons for such timings is that the implementation would go through every one of the 1000000 tasklets and report referenced objects as roots. That is time consuming and is also very redundant when GC generational model is considered. Since suspended stacks can't see assignments of new objects, once GC performs en-masse promotion, all the roots owned by these tasks become too old to be of interest for collections that target younger generations until the containing task is resumed.  
+This is a unique challenge of stack-capturing implementation. In async1 suspended tasks are regular heap objects and do not require any special reporting in GC STW phases.
+
+As a solution to this issue, we implemented tasklet aging mechanism, similar to approach used by GC handles. We make note of GC promotions, and increase "min age" of suspended tasks, so that, for example, tasks that only refer to tenured objects do not need to report roots in ephemeral collections.
+
+The taklet aging was an enormous optimization and the pause time in the above benchmark went down to **480 microseconds**. That is **100x improvement** and is not too bad, considering that this is close to the worst case scenario.
+It was still **4x worse** than async1.
+
+Once tasks know their age, root reporting is no longer the dominant factor, but we still need to check age of 10000 tasks, even if the answer is "too old". A further improvement is possible by grouping the suspended tasks in groups, so that age of entire group could be examined.
+
+For the memory consumption, the managed heap is reported to be very small - **28Mb**. Unsurprisingly, since this approach uses native/malloc memory to store captured data.  
+However, the peak working set was seen at **499Mb** that is **3.3x worse** than async1.
+
+The reason for higher memory consumption is that this approach captures the entire stack frame and its registers, which is less eficient than async1, which, based on data-flow analysis, may capture only variables that are live across awaits.
+
+**=====Async2 (JIT state-machine based)**:
+
+The pause times were observed at **100 microseconds**, which is **better than in async1** implementation. 
+Since the root set would be roughly the same as in async1, the difference could be just second-order effects of different heap graph or different distribution of object sizes.
+
+The peak working set was seen at 328Mb. 
+That is higher than async1 and most likely just additional transient allocations in the JIT. As the benchmark has just a few methods, it cannot be method bodies, but may need some follow up to confirm.
+
+Managed allocation impact in this model depends on JIT optimizations as the capture is based on liveness information, which is not available in tier-0 compiled methods. That is expected and could be acceptable as hot methods would not stay in tier-0. Besides it is possible to enable liveness analysis in tier-0 async2 methods, if needed, at about 5% of the estimated JIT cost. 
+It is also possible to improve liveness analysis/information in async2 methods in general - to further improve capturing efficiency.
+
+With `set DOTNET_TieredCompilation=0`
+Managed heap size: **157Mb**   - slightly more than async1
+
+With `set DOTNET_TieredCompilation=1`
+Managed heap size: **reaches 300Mb** in the first couple iterations, but then drops and **stays at 105Mb**, which is better than async1.
+
+ 
\ No newline at end of file
diff --git a/src/coreclr/gc/env/gcenv.ee.h b/src/coreclr/gc/env/gcenv.ee.h
index 8c949492fa1baa..d9b3c470168a1e 100644
--- a/src/coreclr/gc/env/gcenv.ee.h
+++ b/src/coreclr/gc/env/gcenv.ee.h
@@ -45,6 +45,10 @@ class GCToEEInterface
     static void SyncBlockCachePromotionsGranted(int max_gen);
     static uint32_t GetActiveSyncBlockCount();
 
+    // Tasklet management
+    static void TaskletPromotionsGranted(int condemned, int max_gen, ScanContext* sc);
+    static void TaskletDemote(int condemned, int max_gen, ScanContext* sc);
+
     // Thread functions
     static bool IsPreemptiveGCDisabled();
     static bool EnablePreemptiveGC();
diff --git a/src/coreclr/gc/env/gctoeeinterface.standalone.inl b/src/coreclr/gc/env/gctoeeinterface.standalone.inl
index 0b72f3b7269873..e2e1a920109300 100644
--- a/src/coreclr/gc/env/gctoeeinterface.standalone.inl
+++ b/src/coreclr/gc/env/gctoeeinterface.standalone.inl
@@ -64,6 +64,16 @@ namespace standalone
             ::GCToEEInterface::SyncBlockCachePromotionsGranted(max_gen);
         }
 
+        void TaskletPromotionsGranted(int condemned, int max_gen, ScanContext* sc)
+        {
+            ::GCToEEInterface::TaskletPromotionsGranted(condemned, max_gen, sc);
+        }
+
+        void TaskletDemote(int condemned, int max_gen, ScanContext* sc)
+        {
+            ::GCToEEInterface::TaskletDemote(condemned, max_gen, sc);
+        }
+
         uint32_t GetActiveSyncBlockCount()
         {
             return ::GCToEEInterface::GetActiveSyncBlockCount();
diff --git a/src/coreclr/gc/gcenv.ee.standalone.inl b/src/coreclr/gc/gcenv.ee.standalone.inl
index 1751e069c3e43a..e1cb52bf584a7e 100644
--- a/src/coreclr/gc/gcenv.ee.standalone.inl
+++ b/src/coreclr/gc/gcenv.ee.standalone.inl
@@ -90,6 +90,18 @@ inline void GCToEEInterface::SyncBlockCachePromotionsGranted(int max_gen)
     g_theGCToCLR->SyncBlockCachePromotionsGranted(max_gen);
 }
 
+inline void GCToEEInterface::TaskletPromotionsGranted(int condemned, int max_gen, ScanContext* sc)
+{
+    assert(g_theGCToCLR != nullptr);
+    g_theGCToCLR->TaskletPromotionsGranted(condemned, max_gen, sc);
+}
+
+inline void GCToEEInterface::TaskletDemote(int condemned, int max_gen, ScanContext* sc)
+{
+    assert(g_theGCToCLR != nullptr);
+    g_theGCToCLR->TaskletDemote(condemned, max_gen, sc);
+}
+
 inline uint32_t GCToEEInterface::GetActiveSyncBlockCount()
 {
     assert(g_theGCToCLR != nullptr);
diff --git a/src/coreclr/gc/gcinterface.ee.h b/src/coreclr/gc/gcinterface.ee.h
index dbc7e8ac483836..8bad4311146136 100644
--- a/src/coreclr/gc/gcinterface.ee.h
+++ b/src/coreclr/gc/gcinterface.ee.h
@@ -251,6 +251,12 @@ class IGCToCLR {
     virtual
     void SyncBlockCachePromotionsGranted(int max_gen) PURE_VIRTUAL
 
+    virtual
+    void TaskletPromotionsGranted(int condemned, int max_gen, ScanContext* sc) PURE_VIRTUAL
+
+    virtual
+    void TaskletDemote(int condemned, int max_gen, ScanContext* sc) PURE_VIRTUAL
+
     virtual
     uint32_t GetActiveSyncBlockCount() PURE_VIRTUAL
 
diff --git a/src/coreclr/gc/gcscan.cpp b/src/coreclr/gc/gcscan.cpp
index 92ed7f3a868706..87dcc6fd928493 100644
--- a/src/coreclr/gc/gcscan.cpp
+++ b/src/coreclr/gc/gcscan.cpp
@@ -222,16 +222,29 @@ void GCScan::GcDemote (int condemned, int max_gen, ScanContext* sc)
 {
     Ref_RejuvenateHandles (condemned, max_gen, sc);
     if (!IsServerHeap() || sc->thread_number == 0)
+    {
         GCToEEInterface::SyncBlockCacheDemote(max_gen);
+
+        // TODO: we should partition tasklet lists by the core number on server GC
+        //       and do this on multiple threads.
+        //       then "sc" argument will be used.
+        GCToEEInterface::TaskletDemote(condemned, max_gen, sc);
+    }
 }
 
 void GCScan::GcPromotionsGranted (int condemned, int max_gen, ScanContext* sc)
 {
     Ref_AgeHandles(condemned, max_gen, sc);
     if (!IsServerHeap() || sc->thread_number == 0)
+    {
         GCToEEInterface::SyncBlockCachePromotionsGranted(max_gen);
-}
 
+        // TODO: we should partition tasklet lists by the core number on server GC
+        //       and do this on multiple threads.
+        //       then "sc" argument will be used.
+        GCToEEInterface::TaskletPromotionsGranted(condemned, max_gen, sc);
+    }
+}
 
 size_t GCScan::AskForMoreReservedMemory (size_t old_size, size_t need_size)
 {
diff --git a/src/coreclr/gc/sample/gcenv.ee.cpp b/src/coreclr/gc/sample/gcenv.ee.cpp
index 5a29ee63ed7066..66e0dd4f91b51d 100644
--- a/src/coreclr/gc/sample/gcenv.ee.cpp
+++ b/src/coreclr/gc/sample/gcenv.ee.cpp
@@ -225,6 +225,14 @@ void GCToEEInterface::SyncBlockCachePromotionsGranted(int /*max_gen*/)
 {
 }
 
+void GCToEEInterface::TaskletPromotionsGranted(int /*condemned*/, int /*max_gen*/, ScanContext* /*sc*/)
+{
+}
+
+void GCToEEInterface::TaskletDemote(int /*condemned*/, int /*max_gen*/, ScanContext* /*sc*/)
+{
+}
+
 void GCToEEInterface::DiagGCStart(int gen, bool isInduced)
 {
 }
diff --git a/src/coreclr/inc/clrconfigvalues.h b/src/coreclr/inc/clrconfigvalues.h
index 58fca99ce959e0..6792df9ede5f0f 100644
--- a/src/coreclr/inc/clrconfigvalues.h
+++ b/src/coreclr/inc/clrconfigvalues.h
@@ -804,6 +804,7 @@ RETAIL_CONFIG_DWORD_INFO(EXTERNAL_EnableArm64Rcpc2,             W("EnableArm64Rc
 /// Runtime async
 ///
 RETAIL_CONFIG_DWORD_INFO(EXTERNAL_RuntimeAsyncViaJitGeneratedStateMachines, W("RuntimeAsyncViaJitGeneratedStateMachines"), 1, "Use JIT generated state machines instead of unwinding-based runtime async")
+RETAIL_CONFIG_DWORD_INFO(EXTERNAL_TaskletAging, W("TaskletAging"), 1, "Enable tasklet aging when tasklet based async is used")
 
 ///
 /// Uncategorized
diff --git a/src/coreclr/nativeaot/Runtime/gcrhenv.cpp b/src/coreclr/nativeaot/Runtime/gcrhenv.cpp
index 6fa14327affef7..780d3d5e60ba5d 100644
--- a/src/coreclr/nativeaot/Runtime/gcrhenv.cpp
+++ b/src/coreclr/nativeaot/Runtime/gcrhenv.cpp
@@ -654,6 +654,14 @@ void GCToEEInterface::SyncBlockCachePromotionsGranted(int /*max_gen*/)
 {
 }
 
+void GCToEEInterface::TaskletPromotionsGranted(int /*condemned*/, int /*max_gen*/, ScanContext* /*sc*/)
+{
+}
+
+void GCToEEInterface::TaskletDemote(int /*condemned*/, int /*max_gen*/, ScanContext* /*sc*/)
+{
+}
+
 uint32_t GCToEEInterface::GetActiveSyncBlockCount()
 {
     return 0;
diff --git a/src/coreclr/vm/eeconfig.cpp b/src/coreclr/vm/eeconfig.cpp
index b268ead119785c..c2e2904555296d 100644
--- a/src/coreclr/vm/eeconfig.cpp
+++ b/src/coreclr/vm/eeconfig.cpp
@@ -817,6 +817,7 @@ HRESULT EEConfig::sync()
     backpatchEntryPointSlots = CLRConfig::GetConfigValue(CLRConfig::UNSUPPORTED_BackpatchEntryPointSlots) != 0;
 
     runtimeAsyncViaJitGeneratedStateMachines = CLRConfig::GetConfigValue(CLRConfig::EXTERNAL_RuntimeAsyncViaJitGeneratedStateMachines) != 0;
+    taskletAging = CLRConfig::GetConfigValue(CLRConfig::EXTERNAL_TaskletAging) != 0;
 
 #if defined(FEATURE_GDBJIT) && defined(_DEBUG)
     {
diff --git a/src/coreclr/vm/eeconfig.h b/src/coreclr/vm/eeconfig.h
index bfe7a2a28aa956..b66d7eb7609fe9 100644
--- a/src/coreclr/vm/eeconfig.h
+++ b/src/coreclr/vm/eeconfig.h
@@ -115,6 +115,7 @@ class EEConfig
     bool          BackpatchEntryPointSlots() const { LIMITED_METHOD_CONTRACT; return backpatchEntryPointSlots; }
 
     bool          RuntimeAsyncViaJitGeneratedStateMachines() const { LIMITED_METHOD_CONTRACT; return runtimeAsyncViaJitGeneratedStateMachines; }
+    bool          TaskletAging() const { LIMITED_METHOD_CONTRACT; return taskletAging; }
 
 #if defined(FEATURE_GDBJIT) && defined(_DEBUG)
     inline bool ShouldDumpElfOnMethod(LPCUTF8 methodName) const
@@ -689,6 +690,7 @@ class EEConfig
 #endif
 
     bool runtimeAsyncViaJitGeneratedStateMachines;
+    bool taskletAging;
 public:
 
     enum BitForMask {
diff --git a/src/coreclr/vm/gcenv.ee.cpp b/src/coreclr/vm/gcenv.ee.cpp
index 108ac06eaad801..5518b72f8fade9 100644
--- a/src/coreclr/vm/gcenv.ee.cpp
+++ b/src/coreclr/vm/gcenv.ee.cpp
@@ -328,7 +328,7 @@ void GCToEEInterface::GcScanRoots(promote_func* fn, int condemned, int max_gen,
 
 #ifndef DACCESS_COMPILE
 // TODO Make tasklet reporting DAC friendly
-    IterateTaskletsForGC(fn, sc);
+    IterateTaskletsForGC(fn, condemned, sc);
 #endif
 }
 
@@ -426,6 +426,30 @@ void GCToEEInterface::SyncBlockCachePromotionsGranted(int max_gen)
     SyncBlockCache::GetSyncBlockCache()->GCDone(FALSE, max_gen);
 }
 
+void GCToEEInterface::TaskletPromotionsGranted(int condemned, int max_gen, ScanContext* sc)
+{
+    CONTRACTL
+    {
+        NOTHROW;
+        GC_NOTRIGGER;
+    }
+    CONTRACTL_END;
+
+    AgeTasklets(condemned, max_gen, sc);
+}
+
+void GCToEEInterface::TaskletDemote(int condemned, int max_gen, ScanContext* sc)
+{
+    CONTRACTL
+    {
+        NOTHROW;
+        GC_NOTRIGGER;
+    }
+    CONTRACTL_END;
+
+    RejuvenateTasklets(condemned, max_gen, sc);
+}
+
 uint32_t GCToEEInterface::GetActiveSyncBlockCount()
 {
     CONTRACTL
diff --git a/src/coreclr/vm/qcallentrypoints.cpp b/src/coreclr/vm/qcallentrypoints.cpp
index 8f5df385e1b17c..7f224344ce965e 100644
--- a/src/coreclr/vm/qcallentrypoints.cpp
+++ b/src/coreclr/vm/qcallentrypoints.cpp
@@ -80,6 +80,7 @@
 
 static const Entry s_QCall[] =
 {
+    DllImportEntry(RuntimeSuspension_RegisterTasklet)
     DllImportEntry(RuntimeSuspension_DeleteTasklet)
     DllImportEntry(RuntimeSuspension_CaptureTasklets)
     DllImportEntry(Enum_GetValuesAndNames)
diff --git a/src/coreclr/vm/runtimesuspension.cpp b/src/coreclr/vm/runtimesuspension.cpp
index 577ee0b04f12df..da440b97d013f2 100644
--- a/src/coreclr/vm/runtimesuspension.cpp
+++ b/src/coreclr/vm/runtimesuspension.cpp
@@ -75,6 +75,10 @@ struct Tasklet
     uintptr_t restoreIPAddress;
     StackDataInfo* pStackDataInfo;
     TaskletReturnType taskletReturnType;
+    // min generation of all managed objects referred from this frame.
+    // -1 means the frame is a part of actiely executing stack and may have byrefs pointing to it
+    int32_t  minGeneration;
+    Tasklet* pTaskletPrevInStack;
 };
 
 struct RuntimeAsyncReturnValue
@@ -136,20 +140,31 @@ extern "C" RegRestore* PlatformIndependentRestore(Tasklet* tasklet, RuntimeAsync
     uint8_t* pNewLocation = restoreLocals->GetFutureRSPLocation() + tasklet->pStackDataInfo->UnrecordedDataSize;
     adjustment.Adjustment = ((uintptr_t)pNewLocation) - ((uintptr_t)adjustment.pOldLocation);
 
-    // Adjust all pointers to the stack data that we are about to move, both in this frame, and in the caller frames
+    // Adjust all pointers to the stack data that we are about to move.
+    // NB: We only check refs from the current frame.
+    //     If we have references from the caller frames, something has gone horribly bad already.
     Tasklet *pTaskletToAdjustByrefsOn = tasklet;
-    do
+    uint32_t cByRefs = pTaskletToAdjustByrefsOn->pStackDataInfo->cByRefs;
+    uint32_t* byRefOffsets = (uint32_t*)pTaskletToAdjustByrefsOn->pStackDataInfo->ByRefOffsets;
+    uint8_t* taskletData = pTaskletToAdjustByrefsOn->pStackData;
+    for (uint32_t iByRef = 0; iByRef < cByRefs; iByRef++)
     {
-        uint32_t cByRefs = pTaskletToAdjustByrefsOn->pStackDataInfo->cByRefs;
-        uint32_t* byRefOffsets = (uint32_t*)pTaskletToAdjustByrefsOn->pStackDataInfo->ByRefOffsets;
-        uint8_t* taskletData = pTaskletToAdjustByrefsOn->pStackData;
-        for (uint32_t iByRef = 0; iByRef < cByRefs; iByRef++)
+        RelocAtAddress(&adjustment, taskletData + byRefOffsets[iByRef]);
+    }
+
+    // Mark stacklets as "active" so they no longer age.
+    // this is only needed as long as we allow cross-frame byrefs
+    // as such byrefs may allow a calee to chage min age of a caller.
+    if (tasklet->minGeneration >= 0 && tasklet->pStackDataInfo->cByRefs > 0)
+    {
+        Tasklet* pTaskletToMarkActive = tasklet;
+        do
         {
-            RelocAtAddress(&adjustment, taskletData + byRefOffsets[iByRef]);
-        }
-        pTaskletToAdjustByrefsOn = pTaskletToAdjustByrefsOn->pTaskletNextInStack;
-    } while (pTaskletToAdjustByrefsOn != NULL);
-    
+            pTaskletToMarkActive->minGeneration = -1;
+            pTaskletToMarkActive = pTaskletToMarkActive->pTaskletNextInStack;
+        } while (pTaskletToMarkActive != NULL);
+    }
+
     StackDataInfo *pStackDataInfo = tasklet->pStackDataInfo;
 
     // Copy most of the memory
@@ -525,7 +540,6 @@ PORTABILIT_ASSERT()
     pTasklet->restoreIPAddress = (uintptr_t)GetControlPC(pCf->GetRegisterSet());
     pTasklet->pStackDataInfo = pStackDataInfo;
     pTasklet->taskletReturnType = taskletReturnType;
-    RegisterTasklet(pTasklet);
 
     if (taskletCaptureData->firstTasklet == NULL)
     {
@@ -535,6 +549,7 @@ PORTABILIT_ASSERT()
     else
     {
         taskletCaptureData->lastTasklet->pTaskletNextInStack = pTasklet;
+        pTasklet->pTaskletPrevInStack = taskletCaptureData->lastTasklet;
         taskletCaptureData->lastTasklet = pTasklet;
     }
     taskletCaptureData->framesCaptured++;
@@ -569,13 +584,29 @@ extern "C" Tasklet* QCALLTYPE RuntimeSuspension_CaptureTasklets(QCall::StackCraw
 
 extern "C" void QCALLTYPE RuntimeSuspension_DeleteTasklet(Tasklet* tasklet)
 {
-    UnregisterTasklet(tasklet);
+    if (tasklet->pTaskletNextInStack == NULL)
+    {
+        UnregisterTasklet(tasklet);
+    }
+    else
+    {
+        tasklet->pTaskletNextInStack->pTaskletPrevInStack = NULL;
+    }
+
     tasklet->pStackDataInfo->CleanupStackDataInfo();
     free(tasklet->pStackData);
     free(tasklet->pStackDataInfo);
     free(tasklet);
 }
 
+extern "C" void QCALLTYPE RuntimeSuspension_RegisterTasklet(Tasklet * tasklet)
+{
+    // only heads of stack chains are registered.
+    _ASSERTE(tasklet->pTaskletNextInStack == NULL);
+
+    RegisterTasklet(tasklet);
+}
+
 void RegisterTasklet(Tasklet* pTasklet);
 void InitializeTasklets();
 void UnregisterTasklet(Tasklet* pTasklet);
@@ -611,35 +642,174 @@ void UnregisterTasklet(Tasklet* pTasklet)
     pTasklet->pTaskletNextInLiveList->pTaskletPrevInLiveList = pTasklet->pTaskletPrevInLiveList;
 }
 
-void IterateTaskletsForGC(promote_func* pCallback, ScanContext* sc)
+void IterateTaskletsForGC(promote_func* pCallback, int condemned, ScanContext* sc)
 {
     CrstHolder crstHolder(&g_taskletCrst);
-    Tasklet *pCurTasklet = g_pTaskletSentinel->pTaskletNextInLiveList;
-    while (pCurTasklet != g_pTaskletSentinel)
+    for (Tasklet* pCurStack = g_pTaskletSentinel->pTaskletNextInLiveList;
+        pCurStack != g_pTaskletSentinel;
+        pCurStack = pCurStack->pTaskletNextInLiveList)
     {
-        // Report GC pointers
-        auto pStackDataInfo = pCurTasklet->pStackDataInfo;
-        uint8_t *pLogicalRSP = pCurTasklet->pStackData - pStackDataInfo->UnrecordedDataSize;
-        uint32_t iRef;
-        for (iRef = 0; iRef < pStackDataInfo->cObjectRefs; iRef++)
+        if (pCurStack->minGeneration > condemned)
         {
-            pCallback((PTR_PTR_Object)(pLogicalRSP + pStackDataInfo->ObjectRefOffsets[iRef]), sc, 0);
-        }
-        
-        for (iRef = 0; iRef < pStackDataInfo->cByRefs; iRef++)
+            // this stack is too old to be interesting in this GC
+            continue;
+        };
+
+        Tasklet* pCurTasklet = pCurStack;
+        do
         {
-            int32_t offset = pStackDataInfo->ByRefOffsets[iRef];
-            uint32_t flags = GC_CALL_INTERIOR;
-            if (offset < 0)
+            // because of rejuvenation, some tasklets could be older than the head
+            // skip them if too old.
+            if (pCurTasklet->minGeneration > condemned)
+            {
+                // this stack is too old to be interesting in this GC
+                continue;
+            };
+
+            // Report GC pointers
+            auto pStackDataInfo = pCurTasklet->pStackDataInfo;
+            uint8_t *pLogicalRSP = pCurTasklet->pStackData - pStackDataInfo->UnrecordedDataSize;
+            uint32_t iRef;
+            for (iRef = 0; iRef < pStackDataInfo->cObjectRefs; iRef++)
             {
-                offset = -offset;
-                flags |= GC_CALL_PINNED;
+                pCallback((PTR_PTR_Object)(pLogicalRSP + pStackDataInfo->ObjectRefOffsets[iRef]), sc, 0);
             }
 
-            pCallback((PTR_PTR_Object)(pLogicalRSP + offset), sc, flags);
+            for (iRef = 0; iRef < pStackDataInfo->cByRefs; iRef++)
+            {
+                int32_t offset = pStackDataInfo->ByRefOffsets[iRef];
+                uint32_t flags = GC_CALL_INTERIOR;
+                if (offset < 0)
+                {
+                    offset = -offset;
+                    flags |= GC_CALL_PINNED;
+                }
+
+                pCallback((PTR_PTR_Object)(pLogicalRSP + offset), sc, flags);
+            }
+
+            pCurTasklet = pCurTasklet->pTaskletPrevInStack;
+        } while (pCurTasklet != NULL);
+    }
+}
+
+void AgeTasklets(int condemned, int max_gen, ScanContext* sc)
+{
+    if (!g_pConfig->TaskletAging())
+    {
+        return;
+    }
+
+    CrstHolder crstHolder(&g_taskletCrst);
+    for (Tasklet* pCurStack = g_pTaskletSentinel->pTaskletNextInLiveList; pCurStack != g_pTaskletSentinel; pCurStack = pCurStack->pTaskletNextInLiveList)
+    {
+        if (pCurStack->minGeneration > condemned)
+        {
+            // this stack is too old to be interesting in this GC
+            continue;
+        };
+
+        if (pCurStack->minGeneration < 0)
+        {
+            // this stack is active, do not age it
+            continue;
+        }
+
+        Tasklet* pCurTasklet = pCurStack;
+        do
+        {
+             // actually age the tasklet
+            pCurTasklet->minGeneration = min(pCurTasklet->minGeneration + 1, max_gen);
+
+            pCurTasklet = pCurTasklet->pTaskletPrevInStack;
+        } while (pCurTasklet != NULL);
+    }
+}
+
+void RejuvenateTasklets(int condemned, int max_gen, ScanContext* sc)
+{
+    if (!g_pConfig->TaskletAging())
+    {
+        return;
+    }
+
+    CrstHolder crstHolder(&g_taskletCrst);
+    for (Tasklet* pCurStack = g_pTaskletSentinel->pTaskletNextInLiveList;
+        pCurStack != g_pTaskletSentinel;
+        pCurStack = pCurStack->pTaskletNextInLiveList)
+    {
+        if (pCurStack->minGeneration <= 0)
+        {
+            // this stack is as young as it can be
+            continue;
+        };
+
+        if (pCurStack->minGeneration > condemned)
+        {
+            // this tasklet is too old to be interesting in this GC
+            continue;
         }
 
-        pCurTasklet = pCurTasklet->pTaskletNextInLiveList;
+        Tasklet* pCurTasklet = pCurStack;
+        do
+        {
+            // update the minGeneration
+            auto pStackDataInfo = pCurTasklet->pStackDataInfo;
+            uint8_t* pLogicalRSP = pCurTasklet->pStackData - pStackDataInfo->UnrecordedDataSize;
+            uint32_t iRef;
+            for (iRef = 0; iRef < pStackDataInfo->cObjectRefs; iRef++)
+            {
+                auto ppObj = (PTR_PTR_Object)(pLogicalRSP + pStackDataInfo->ObjectRefOffsets[iRef]);
+                if (*ppObj != NULL)
+                {
+                    int objGen = (INT32)GCHeapUtilities::GetGCHeap()->WhichGeneration(*ppObj);
+                    if (objGen < pCurTasklet->minGeneration)
+                    {
+                        pCurTasklet->minGeneration = objGen;
+                        if (objGen == 0)
+                        {
+                            // we are as young as we can be
+                            goto nextTaskLet;
+                        }
+                    }
+                }
+            }
+
+            for (iRef = 0; iRef < pStackDataInfo->cByRefs; iRef++)
+            {
+                int32_t offset = pStackDataInfo->ByRefOffsets[iRef];
+                if (offset < 0)
+                {
+                    offset = -offset;
+                }
+
+                auto ppObj = (PTR_PTR_Object)(pLogicalRSP + offset);
+                int objGen = (INT32)GCHeapUtilities::GetGCHeap()->WhichGeneration(*ppObj);
+                if (*ppObj != NULL)
+                {
+                    if (objGen < pCurTasklet->minGeneration)
+                    {
+                        pCurTasklet->minGeneration = objGen;
+                        if (objGen == 0)
+                        {
+                            // we are as young as we can be
+                            goto nextTaskLet;
+                        }
+                    }
+                }
+            }
+
+        nextTaskLet:
+
+            // make sure the head tasklet is no older than any tasklets in the list
+            // because we will use the age of the head for short-circuiting
+            if (pCurStack->minGeneration > pCurTasklet->minGeneration)
+            {
+                pCurStack->minGeneration = pCurTasklet->minGeneration;
+            }
+
+            pCurTasklet = pCurTasklet->pTaskletPrevInStack;
+        } while (pCurTasklet != NULL);
     }
 }
 
diff --git a/src/coreclr/vm/runtimesuspension.h b/src/coreclr/vm/runtimesuspension.h
index 4f9b23f01d65a7..f864ca2e3bc1ef 100644
--- a/src/coreclr/vm/runtimesuspension.h
+++ b/src/coreclr/vm/runtimesuspension.h
@@ -6,6 +6,7 @@ struct AsyncDataFrame;
 struct RuntimeAsyncReturnValue;
 
 extern "C" Tasklet* QCALLTYPE RuntimeSuspension_CaptureTasklets(QCall::StackCrawlMarkHandle stackMark, uint8_t* returnValue, uint8_t useReturnValueHandle, void* taskAsyncData, Tasklet** lastTasklet, int32_t* pFramesCaptured);
+extern "C" void QCALLTYPE RuntimeSuspension_RegisterTasklet(Tasklet * tasklet);
 extern "C" void QCALLTYPE RuntimeSuspension_DeleteTasklet(Tasklet* tasklet);
 
 EXTERN_C FCDECL1(void, RuntimeSuspension_UnwindToFunctionWithAsyncFrame, AsyncDataFrame* frame);
@@ -16,4 +17,7 @@ EXTERN_C FCDECL2(Object*, RuntimeSuspension_ResumeTaskletIntegerRegisterReturn,
 void RegisterTasklet(Tasklet* pTasklet);
 void InitializeTasklets();
 void UnregisterTasklet(Tasklet* pTasklet);
-void IterateTaskletsForGC(promote_func* pCallback, ScanContext* sc);
+
+void IterateTaskletsForGC(promote_func* pCallback, int condemned, ScanContext* sc);
+void AgeTasklets(int condemned, int max_gen, ScanContext* sc);
+void RejuvenateTasklets(int condemned, int max_gen, ScanContext* sc);
diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.cs
index bfd71c17d0fd07..f4bfa35e89bd5a 100644
--- a/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.cs
+++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.cs
@@ -243,7 +243,6 @@ internal class RuntimeAsyncMaintainedData
             public byte _dummy;
 
             public unsafe Tasklet* _nextTasklet;
-            public unsafe Tasklet* _oldTaskletNext;
 
             public RuntimeAsyncReturnValue _retValue;
             public virtual ref byte GetReturnPointer() { return ref _dummy; }
@@ -451,6 +450,8 @@ internal unsafe struct Tasklet
             public IntPtr restoreIPAddress;
             public StackDataInfo* pStackDataInfo;
             public TaskletReturnType taskletReturnType;
+            public int minGeneration;
+            public Tasklet* pTaskletPrevInStack;
 
             public int GetMaxStackNeeded() { return pStackDataInfo->StackRequirement; }
         }
@@ -491,6 +492,9 @@ private static unsafe ref AsyncDataFrame GetCurrentAsyncDataFrame()
         [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "RuntimeSuspension_DeleteTasklet")]
         private static unsafe partial void DeleteTasklet(Tasklet* tasklet);
 
+        [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "RuntimeSuspension_RegisterTasklet")]
+        private static unsafe partial void RegisterTasklet(Tasklet* tasklet);
+
         [MethodImpl(MethodImplOptions.InternalCall)]
         internal static extern unsafe void UnwindToFunctionWithAsyncFrame(Tasklet* topTasklet, nint framesToUnwind);
 
@@ -508,8 +512,28 @@ private static unsafe bool DoSetupAndCapture(ref StackCrawlMark stackMark)
             if (nextTaskletInStack == null)
                 throw new OutOfMemoryException();
 
-            maintainedData._oldTaskletNext = maintainedData._nextTasklet;
-            lastTasklet->pTaskletNextInStack = maintainedData._nextTasklet;
+            if (maintainedData._nextTasklet == null)
+            {
+                RegisterTasklet(lastTasklet);
+            }
+            else
+            {
+                maintainedData._nextTasklet->pTaskletPrevInStack = lastTasklet;
+                lastTasklet->pTaskletNextInStack = maintainedData._nextTasklet;
+
+                // we are suspending, so change the age of tasklets from -1 (active) to 0 (very young)
+                // NOTE: this is only needed as long as we allow cross-frame byrefs as
+                // that forces us to make age of active tasklets undefined (see comment in PlatformIndependentRestore)
+                // without such byrefs old tasklets could stay old as nothing could change locals of a captured frame.
+                for (Tasklet* current = maintainedData._nextTasklet; current != null; current = current->pTaskletNextInStack)
+                {
+                    if (current->minGeneration == -1)
+                    {
+                        current->minGeneration = 0;
+                    }
+                }
+            }
+
             maintainedData._nextTasklet = nextTaskletInStack;
             maintainedData._retValue = default(RuntimeAsyncReturnValue);
 
diff --git a/src/tests/Loader/async/async2gcRootsScan.cs b/src/tests/Loader/async/async2gcRootsScan.cs
new file mode 100644
index 00000000000000..f703b88c149d58
--- /dev/null
+++ b/src/tests/Loader/async/async2gcRootsScan.cs
@@ -0,0 +1,211 @@
+// 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 System.Threading.Tasks;
+using System.Diagnostics;
+using Xunit;
+
+public class Async2RootReporting
+{
+    private static TaskCompletionSource<int> cs;
+
+
+    static async Task<int> Recursive1(int n)
+    {
+        Task<int> cTask = cs.Task;
+
+        // make some garbage
+        object o1 = n;
+        //object o2 = n;
+        //object o3 = n;
+        //object o4 = n;
+        //object o5 = n;
+        //object o6 = n;
+        //object o7 = n;
+        //object o8 = n;
+        //object o9 = n;
+        //object o10 = n;
+        //object o11 = n;
+        //object o12 = n;
+
+        if (n == 0)
+            return await cTask;
+
+        var result = await Recursive1(n - 1);
+
+        Assert.Equal(n, (int)o1);
+        //Assert.Equal(n, (int)o2);
+        //Assert.Equal(n, (int)o3);
+        //Assert.Equal(n, (int)o4);
+        //Assert.Equal(n, (int)o5);
+        //Assert.Equal(n, (int)o6);
+        //Assert.Equal(n, (int)o7);
+        //Assert.Equal(n, (int)o8);
+        //Assert.Equal(n, (int)o9);
+        //Assert.Equal(n, (int)o10);
+        //Assert.Equal(n, (int)o11);
+        //Assert.Equal(n, (int)o12);
+
+        return result;
+    }
+
+    static async2 Task<int> Recursive2(int n)
+    {
+        Task<int> cTask = cs.Task;
+
+        // make some garbage
+        object o1 = n;
+
+// THIS CAUSES CRASHES IN UNWINDER FLAVOR
+        //object o2 = n;
+        //object o3 = n;
+        //object o4 = n;
+        //object o5 = n;
+        //object o6 = n;
+        //object o7 = n;
+        //object o8 = n;
+        //object o9 = n;
+        //object o10 = n;
+        //object o11 = n;
+        //object o12 = n;
+
+        if (n == 0)
+            return await cTask;
+
+        var result = await Recursive2(n - 1);
+
+        Assert.Equal(n, (int)o1);
+        //Assert.Equal(n, (int)o2);
+        //Assert.Equal(n, (int)o3);
+        //Assert.Equal(n, (int)o4);
+        //Assert.Equal(n, (int)o5);
+        //Assert.Equal(n, (int)o6);
+        //Assert.Equal(n, (int)o7);
+        //Assert.Equal(n, (int)o8);
+        //Assert.Equal(n, (int)o9);
+        //Assert.Equal(n, (int)o10);
+        //Assert.Equal(n, (int)o11);
+        //Assert.Equal(n, (int)o12);
+
+        return result;
+    }
+
+    static System.Collections.Generic.List<object> d = new System.Collections.Generic.List<object>();
+
+    [Fact]
+    public static void Test()
+    {
+        int numStacks = 10000;
+        int stackDepth = 100;
+#if DEBUG
+        int numGCs = 30;
+#else
+        int numGCs = 500;
+#endif
+
+        void Warmup()
+        {
+            for (int k = 0; k < 10000; k++)
+                d.Add(new int[k % 100]);
+
+            d = null;
+
+            for (int k = 0; k < 10; k++)
+            {
+                cs = new TaskCompletionSource<int>();
+                Task[] tasks = new Task[numStacks];
+                for (int i = 0; i < tasks.Length; i++)
+                {
+                    tasks[i] = Recursive1(stackDepth);
+                }
+
+                GC.Collect();
+                cs.SetResult(100);
+                Task.WaitAll(tasks);
+            }
+
+            {
+                cs = new TaskCompletionSource<int>();
+                Task[] tasks = new Task[numStacks];
+                for (int i = 0; i < tasks.Length; i++)
+                {
+                    tasks[i] = Recursive2(stackDepth);
+                }
+
+                GC.Collect();
+                cs.SetResult(100);
+                Task.WaitAll(tasks);
+            }
+        }
+
+        Warmup();
+
+        Console.WriteLine("async-1 ===================== ");
+        var ticks = Environment.TickCount;
+
+        // run a few iterations for 5 seconds total
+        while (Environment.TickCount - ticks < 5000)
+        {
+            cs = new TaskCompletionSource<int>();
+            Task[] tasks = new Task[numStacks];
+            for (int i = 0; i < tasks.Length; i++)
+            {
+                tasks[i] = Recursive1(stackDepth);
+            }
+
+            GC.Collect(0);
+
+            var ticksStart = Stopwatch.GetTimestamp();
+
+            for (int i = 0; i < numGCs; i++)
+            {
+                if (i % 10 == 0)
+                    Console.Write('.');
+
+                GC.Collect(0);
+            }
+
+            var info = GC.GetGCMemoryInfo();
+            Console.Write("memory Mbytes:" + info.HeapSizeBytes / 1000000 + "  ");
+            System.Console.WriteLine("Time per Gen0 GC (microseconds): " + (Stopwatch.GetTimestamp() - ticksStart) * 1000000 / numGCs / Stopwatch.Frequency);
+
+            cs.SetResult(100);
+            Task.WaitAll(tasks);
+        }
+
+        Console.WriteLine("async-2 ===================== ");
+        ticks = Environment.TickCount;
+
+        // run a few iterations for 5 seconds total
+        while (Environment.TickCount - ticks < 5000)
+        {
+            cs = new TaskCompletionSource<int>();
+            Task[] tasks = new Task[numStacks];
+            for (int i = 0; i < tasks.Length; i++)
+            {
+                tasks[i] = Recursive2(stackDepth);
+            }
+
+            GC.Collect(0);
+
+            var ticksStart = Stopwatch.GetTimestamp();
+
+            for (int i = 0; i < numGCs; i++)
+            {
+                if (i % 10 == 0)
+                    Console.Write('.');
+
+                GC.Collect(0);
+            }
+
+            var info = GC.GetGCMemoryInfo();
+            Console.Write("memory Mbytes:" + info.HeapSizeBytes / 1000000 + "  ");
+
+            System.Console.WriteLine("Time per Gen0 GC (microseconds): " + (Stopwatch.GetTimestamp() - ticksStart) * 1000000 / numGCs / Stopwatch.Frequency);
+
+            cs.SetResult(100);
+            Task.WaitAll(tasks);
+        }
+    }
+}
diff --git a/src/tests/Loader/async/async2gcRootsScan.csproj b/src/tests/Loader/async/async2gcRootsScan.csproj
new file mode 100644
index 00000000000000..0f29ed860df43e
--- /dev/null
+++ b/src/tests/Loader/async/async2gcRootsScan.csproj
@@ -0,0 +1,8 @@
+<Project Sdk="Microsoft.NET.Sdk.IL">
+  <PropertyGroup>
+    <RequiresProcessIsolation>true</RequiresProcessIsolation>
+  </PropertyGroup>
+  <ItemGroup>
+   <Compile Include="$(MSBuildProjectName).cs" />
+  </ItemGroup>
+</Project>

From ff668bd2284ea541b1426d09f891aa53ba35bf96 Mon Sep 17 00:00:00 2001
From: Vladimir Sadov <vsadov@microsoft.com>
Date: Sun, 24 Dec 2023 20:55:04 -0800
Subject: [PATCH 096/203] Bump the roslyn commit to a version supporting
 top-level async2 functions. Add a test. (#2470)

---
 buildroslynnugets.cmd                         |  4 +-
 eng/Versions.props                            |  6 +-
 .../async2fibonacci-with-yields-topLevel.cs   | 62 +++++++++++++++++++
 ...sync2fibonacci-with-yields-topLevel.csproj |  9 +++
 4 files changed, 76 insertions(+), 5 deletions(-)
 create mode 100644 src/tests/Loader/async/async2fibonacci-with-yields-topLevel.cs
 create mode 100644 src/tests/Loader/async/async2fibonacci-with-yields-topLevel.csproj

diff --git a/buildroslynnugets.cmd b/buildroslynnugets.cmd
index 1051eda3506e04..b91c2c3160a70c 100644
--- a/buildroslynnugets.cmd
+++ b/buildroslynnugets.cmd
@@ -1,7 +1,7 @@
 setlocal ENABLEEXTENSIONS
 pushd %~dp0
-set ASYNC_ROSLYN_COMMIT=046d22fad72ac726d36d93b0e9d186574e77207b
-set ASYNC_SUFFIX=async-7
+set ASYNC_ROSLYN_COMMIT=4d45a5cfed3654d4d5a079908e3796be6665b18c
+set ASYNC_SUFFIX=async-8
 set ASYNC_ROSLYN_BRANCH=demos/async2-experiment
 
 cd ..
diff --git a/eng/Versions.props b/eng/Versions.props
index cc9b47af989700..1a62fff1dc845d 100644
--- a/eng/Versions.props
+++ b/eng/Versions.props
@@ -39,9 +39,9 @@
       Any tools that contribute to the design-time experience should use the MicrosoftCodeAnalysisVersion_LatestVS property above to ensure
       they do not break the local dev experience.
     -->
-    <MicrosoftCodeAnalysisCSharpVersion>4.9.0-async-7</MicrosoftCodeAnalysisCSharpVersion>
-    <MicrosoftCodeAnalysisVersion>4.9.0-async-7</MicrosoftCodeAnalysisVersion>
-    <MicrosoftNetCompilersToolsetVersion>4.9.0-async-7</MicrosoftNetCompilersToolsetVersion>
+    <MicrosoftCodeAnalysisCSharpVersion>4.9.0-async-8</MicrosoftCodeAnalysisCSharpVersion>
+    <MicrosoftCodeAnalysisVersion>4.9.0-async-8</MicrosoftCodeAnalysisVersion>
+    <MicrosoftNetCompilersToolsetVersion>4.9.0-async-8</MicrosoftNetCompilersToolsetVersion>
   </PropertyGroup>
   <!--
     For source generator support we need to target multiple versions of Roslyn in order to be able to run on older versions of Roslyn.
diff --git a/src/tests/Loader/async/async2fibonacci-with-yields-topLevel.cs b/src/tests/Loader/async/async2fibonacci-with-yields-topLevel.cs
new file mode 100644
index 00000000000000..977064a83740e3
--- /dev/null
+++ b/src/tests/Loader/async/async2fibonacci-with-yields-topLevel.cs
@@ -0,0 +1,62 @@
+// 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 System.Threading.Tasks;
+using System.Diagnostics;
+using Xunit;
+
+const uint Threshold = 1_000;
+bool done = false;
+
+async Task AsyncEntry()
+{
+    for (int i = 0; i < 10 && !done; i++)
+    {
+        var sw = new Stopwatch();
+        sw.Start();
+        uint result = await A(100_000_000);
+        Console.WriteLine($"{sw.ElapsedMilliseconds} ms result={result}");
+    }
+}
+
+async2 Task<uint> A(uint n)
+{
+    uint result = n;
+    for (uint i = 0; i < n && !done; i++)
+        result = await B(result);
+    return result;
+}
+
+async2 Task<uint> B(uint n)
+{
+    uint result = n;
+
+    result = result * 1_999_999_981;
+    if (result < Threshold)
+    {
+        await Task.Yield();
+    }
+
+    result = result * 1_999_999_981;
+    if (result < Threshold)
+    {
+        await Task.Yield();
+    }
+
+    result = result * 1_999_999_981;
+    if (result < Threshold)
+    {
+        await Task.Yield();
+    }
+
+    return result;
+}
+
+int Test()
+{
+    AsyncEntry().Wait();
+    return 100;
+}
+
+return Test();
\ No newline at end of file
diff --git a/src/tests/Loader/async/async2fibonacci-with-yields-topLevel.csproj b/src/tests/Loader/async/async2fibonacci-with-yields-topLevel.csproj
new file mode 100644
index 00000000000000..4a1973da13c789
--- /dev/null
+++ b/src/tests/Loader/async/async2fibonacci-with-yields-topLevel.csproj
@@ -0,0 +1,9 @@
+<Project Sdk="Microsoft.NET.Sdk">
+  <PropertyGroup>
+    <RequiresProcessIsolation>true</RequiresProcessIsolation>
+    <Optimize>True</Optimize>
+  </PropertyGroup>
+  <ItemGroup>
+    <Compile Include="$(MSBuildProjectName).cs" />
+  </ItemGroup>
+</Project>

From 4ba1bc5d0f3b3731d726df6d156d88c9eb7b70ce Mon Sep 17 00:00:00 2001
From: David Wrighton <davidwr@microsoft.com>
Date: Tue, 2 Jan 2024 09:15:27 -0800
Subject: [PATCH 097/203] Benchmark of interaction between async1 and async2
 (#2469)

---
 docs/design/features/async-mincallcost.svg    |   1 +
 docs/design/features/runtime-handled-tasks.md |  41 +-
 .../async/async2-mincallcost-microbench.cs    | 394 ++++++++++++++++++
 .../async2-mincallcost-microbench.csproj      |   9 +
 4 files changed, 438 insertions(+), 7 deletions(-)
 create mode 100644 docs/design/features/async-mincallcost.svg
 create mode 100644 src/tests/Loader/async/async2-mincallcost-microbench.cs
 create mode 100644 src/tests/Loader/async/async2-mincallcost-microbench.csproj

diff --git a/docs/design/features/async-mincallcost.svg b/docs/design/features/async-mincallcost.svg
new file mode 100644
index 00000000000000..ed089be8328256
--- /dev/null
+++ b/docs/design/features/async-mincallcost.svg
@@ -0,0 +1 @@
+<svg width="2099" height="1210" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" overflow="hidden"><defs><clipPath id="clip0"><rect x="1877" y="756" width="2099" height="1210"/></clipPath></defs><g clip-path="url(#clip0)" transform="translate(-1877 -756)"><rect x="1878" y="757" width="2096" height="1207" fill="#FFFFFF"/><g><path d="M2155.5 1573.55 3923.5 1573.55M2155.5 1444.55 3923.5 1444.55M2155.5 1315.54 3923.5 1315.54M2155.5 1185.54 3923.5 1185.54M2155.5 1056.53 3923.5 1056.53M2155.5 927.5 3923.5 927.5" stroke="#D9D9D9" stroke-width="3.4375" stroke-linejoin="round" stroke-miterlimit="10" fill="none"/></g><g><path d="M2223.07 1551.05 2285.07 1551.05 2285.07 1703 2223.07 1703ZM2577.08 1535.05 2638.09 1535.05 2638.09 1703 2577.08 1703ZM2930.1 1552.05 2992.1 1552.05 2992.1 1703 2930.1 1703Z" fill="#156082"/></g><g><path d="M2301.08 1522.05 2363.08 1522.05 2363.08 1703 2301.08 1703ZM2655.09 1514.05 2717.09 1514.05 2717.09 1703 2655.09 1703ZM3008.1 1056.03 3070.1 1056.03 3070.1 1703 3008.1 1703ZM3362.11 1225.04 3424.11 1225.04 3424.11 1703 3362.11 1703ZM3716.12 1418.05 3777.12 1418.05 3777.12 1703 3716.12 1703Z" fill="#E97132"/></g><g><path d="M2380.08 1519.05 2441.08 1519.05 2441.08 1703 2380.08 1703ZM2733.09 1520.05 2795.09 1520.05 2795.09 1703 2733.09 1703ZM3087.1 1056.03 3149.1 1056.03 3149.1 1703 3087.1 1703ZM3440.11 1211.04 3502.11 1211.04 3502.11 1703 3440.11 1703ZM3794.12 1395.05 3856.13 1395.05 3856.13 1703 3794.12 1703Z" fill="#196B24"/></g><g><path d="M2155.5 1702.5 3923.5 1702.5" stroke="#D9D9D9" stroke-width="3.4375" stroke-linejoin="round" stroke-miterlimit="10" fill="none" fill-rule="evenodd"/></g><g><text fill="#595959" font-family="Aptos Narrow,Aptos Narrow_MSFontService,sans-serif" font-weight="400" font-stretch="semi-condensed" font-size="41" transform="matrix(1 0 0 1 2096.46 1714)">0</text></g><g><text fill="#595959" font-family="Aptos Narrow,Aptos Narrow_MSFontService,sans-serif" font-weight="400" font-stretch="semi-condensed" font-size="41" transform="matrix(1 0 0 1 1907.81 1585)">100,000,000</text></g><g><text fill="#595959" font-family="Aptos Narrow,Aptos Narrow_MSFontService,sans-serif" font-weight="400" font-stretch="semi-condensed" font-size="41" transform="matrix(1 0 0 1 1907.81 1456)">200,000,000</text></g><g><text fill="#595959" font-family="Aptos Narrow,Aptos Narrow_MSFontService,sans-serif" font-weight="400" font-stretch="semi-condensed" font-size="41" transform="matrix(1 0 0 1 1907.81 1327)">300,000,000</text></g><g><text fill="#595959" font-family="Aptos Narrow,Aptos Narrow_MSFontService,sans-serif" font-weight="400" font-stretch="semi-condensed" font-size="41" transform="matrix(1 0 0 1 1907.81 1197)">400,000,000</text></g><g><text fill="#595959" font-family="Aptos Narrow,Aptos Narrow_MSFontService,sans-serif" font-weight="400" font-stretch="semi-condensed" font-size="41" transform="matrix(1 0 0 1 1907.81 1068)">500,000,000</text></g><g><text fill="#595959" font-family="Aptos Narrow,Aptos Narrow_MSFontService,sans-serif" font-weight="400" font-stretch="semi-condensed" font-size="41" transform="matrix(1 0 0 1 1907.81 939)">600,000,000</text></g><g><text fill="#595959" font-family="Aptos Narrow,Aptos Narrow_MSFontService,sans-serif" font-weight="400" font-stretch="semi-condensed" font-size="41" transform="matrix(1 0 0 1 2231.48 1768)">Async1 Task</text></g><g><text fill="#595959" font-family="Aptos Narrow,Aptos Narrow_MSFontService,sans-serif" font-weight="400" font-stretch="semi-condensed" font-size="41" transform="matrix(1 0 0 1 2190.44 1818)">returning method</text></g><g><text fill="#595959" font-family="Aptos Narrow,Aptos Narrow_MSFontService,sans-serif" font-weight="400" font-stretch="semi-condensed" font-size="41" transform="matrix(1 0 0 1 2538.53 1768)">Async1 ValueTask</text></g><g><text fill="#595959" font-family="Aptos Narrow,Aptos Narrow_MSFontService,sans-serif" font-weight="400" font-stretch="semi-condensed" font-size="41" transform="matrix(1 0 0 1 2544.01 1818)">returning method</text></g><g><text fill="#595959" font-family="Aptos Narrow,Aptos Narrow_MSFontService,sans-serif" font-weight="400" font-stretch="semi-condensed" font-size="41" transform="matrix(1 0 0 1 2913.56 1768)">Async2 Method</text></g><g><text fill="#595959" font-family="Aptos Narrow,Aptos Narrow_MSFontService,sans-serif" font-weight="400" font-stretch="semi-condensed" font-size="41" transform="matrix(1 0 0 1 3271.67 1768)">Non-inlineable</text></g><g><text fill="#595959" font-family="Aptos Narrow,Aptos Narrow_MSFontService,sans-serif" font-weight="400" font-stretch="semi-condensed" font-size="41" transform="matrix(1 0 0 1 3265.86 1818)">Async2 method</text></g><g><text fill="#595959" font-family="Aptos Narrow,Aptos Narrow_MSFontService,sans-serif" font-weight="400" font-stretch="semi-condensed" font-size="41" transform="matrix(1 0 0 1 3582.15 1768)">Async2 Method with</text></g><g><text fill="#595959" font-family="Aptos Narrow,Aptos Narrow_MSFontService,sans-serif" font-weight="400" font-stretch="semi-condensed" font-size="41" transform="matrix(1 0 0 1 3643.6 1818)">context save</text></g><g><text fill="#595959" font-family="Aptos Narrow,Aptos Narrow_MSFontService,sans-serif" font-weight="400" font-stretch="semi-condensed" font-size="64" transform="matrix(1 0 0 1 2404.26 852)">Iterations/second calls to async methods</text></g><g><rect x="2152" y="1886" width="22" height="22.9996" fill="#156082"/></g><g><text fill="#595959" font-family="Aptos Narrow,Aptos Narrow_MSFontService,sans-serif" font-weight="400" font-stretch="semi-condensed" font-size="41" transform="matrix(1 0 0 1 2183.83 1909)">Async1 Task returning method</text></g><g><rect x="2761" y="1886" width="23" height="22.9996" fill="#E97132"/></g><g><text fill="#595959" font-family="Aptos Narrow,Aptos Narrow_MSFontService,sans-serif" font-weight="400" font-stretch="semi-condensed" font-size="41" transform="matrix(1 0 0 1 2793.7 1909)">Async2 JIT State machine</text></g><g><rect x="3290" y="1886" width="23" height="22.9996" fill="#196B24"/></g><g><text fill="#595959" font-family="Aptos Narrow,Aptos Narrow_MSFontService,sans-serif" font-weight="400" font-stretch="semi-condensed" font-size="41" transform="matrix(1 0 0 1 3322.75 1909)">Async2 Unwinder Model</text></g><rect x="1878.5" y="757.5" width="2096" height="1207" stroke="#D9D9D9" stroke-width="3.4375" stroke-linejoin="round" stroke-miterlimit="10" fill="none"/></g></svg>
\ No newline at end of file
diff --git a/docs/design/features/runtime-handled-tasks.md b/docs/design/features/runtime-handled-tasks.md
index 1352da07d81402..e66b18408cfe17 100644
--- a/docs/design/features/runtime-handled-tasks.md
+++ b/docs/design/features/runtime-handled-tasks.md
@@ -462,13 +462,40 @@ Major identified concerns are
 
 ## Microbenchmarks
 
-1. Async computation of fibonacci via recursive algorithm, with and without various amounts of yielding
-2. Suspension with stacks of various depths
-3. Suspension with stacks where the operation of the task operates in a sawtooth pattern.
-For instance, suspend first at depth 20, then reach depth 30, return to depth 10 and suspend, reach depth 30, return to depth 10 and suspend, etc.
-Measure the impact of suspending with greater and lesser depths.
-4. Measure the performance of thunks to async2 functions which do effectively nothing.
-5. Benchmark the effect of long lived suspended state vs short lived suspended state
+# Benchmark of minimum call cost between Async and Async2 functions
+
+The performance of interaction between the Async and Async2 implementation strategies has come up as a concern in developing a replacement strategy for the existing async model.
+Given that this proposal suggests building a model which allows both to co-exist within the same application, understanding the cost of such calls is important.
+This benchmark counts the iterations per second of non-suspending calls between various methods implemented as either async or async2.
+Data for this benchmark is available for both the JIT generated state machine model, as well as the unwinder based model.
+The data was gathered using the `src/tests/Loader/async/async-mincallcost-microbench.csproj` project.
+
+This benchmark works by an async method calling into another mostly empty async method in a loop.
+The benchmark operates for a fixed period of time, measuring the number of iterations.
+The caller method is always a `Task<long>` returning method, and the mostly empty method, reads a global variable, and then awaits the result of a call to `Task.Yield()` if that global variable is `0`. (The variable never has the value '0'.) 
+
+![MinCallCostGraph](async-mincallcost.svg)
+
+In this graph, you can see that the performance of interaction between the async1 and async2 models are not problematically slow.
+Indeed, the performance of async2 code calling async1 functions is actually greater than the performance of async1 calling async1.
+In addition, the cost to call an async2 function from another async2 function is substantially less (even if the async2 function being called needs to save the context in order to maintain full similarity of behavior with async1.)
+Here we can see the cost of inlining vs not inlining, as there is a meaningful reduction in iteration count.
+In addition, the async2 method with Context Save is also unable to inline at the current time, and also performs work simulating the additional work that the async2 logic would need to implement to fully replicate async1 behavior around async locals, and synchronization context handling.
+
+NOTES: 
+- Cost of calls from async1 method to async2 method are shown for the JIT state machine model only.
+The performance of the calls from async1 to async2 for the unwinder model were substantially slower, although if we choose that as the implementation style it is expected they could be made at least as fast as the state machine model.
+- Measurement of calls from async1 to async 2 non-inlineable and with context save were not measured.
+It is expected that in any final implementation there would be optimizations in place in order to ensure that the cost from async1 to either of these two target methods would be the same as calls to any Async2 method.
+
+# Raw data from mincallcost benchmark
+| Caller | Async1 Task returning method | Async1 ValueTask returning method | Async2 Method | Non-inlineable Async2 method | Async2 Method with context save |
+| ------ | ----- | ------ | ---- | ---- | --- |
+| Async1 Task returning method | 117,232,360 | 129,758,886 | 116,365,186 (StateMachine), 76,280,435 (Unwinder) | N/A | N/A |
+| Async2 JIT State machine | 139,952,617 | 145,961,918 | 500,753,190 | 369,495,926 | 220,148,034 |
+| Async2 Unwinder Model | 142,012,835 | 141,336,005 | 500,148,939 | 380,632,094 | 238,234,481 |
+
+
 # Benchmark the performance of EH at various depths.
 
 The performance of EH within the existing async programming model is highly problematic, and has caused customers issues.
diff --git a/src/tests/Loader/async/async2-mincallcost-microbench.cs b/src/tests/Loader/async/async2-mincallcost-microbench.cs
new file mode 100644
index 00000000000000..d2a64368ca60dc
--- /dev/null
+++ b/src/tests/Loader/async/async2-mincallcost-microbench.cs
@@ -0,0 +1,394 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+//#define ASYNC1_TASK
+//#define ASYNC1_VALUETASK
+
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Linq;
+using System.Runtime;
+using System.Runtime.CompilerServices;
+using System.Threading;
+using System.Threading.Tasks;
+using Xunit;
+
+public class Async2MinCallCostMicrobench
+{
+    [Fact]
+    public static void TestEntryPoint()
+    {
+        Task.Run(AsyncEntry).Wait();
+    }
+
+    public static async Task AsyncEntry()
+    {
+        if (!GCSettings.IsServerGC)
+            Console.WriteLine("*** Warning: Server GC is disabled, set DOTNET_gcServer=1 ***");
+
+        time = 0.5;
+
+        for (int i = 0; i < 4; i++)
+        {
+            for (int j = 0; j < 40; j++)
+            {
+                await RunBench("AsyncCallingAsync");
+                await RunBench("AsyncCallingValueTaskAsync");
+                await RunBench("AsyncCallingAsync2");
+                await RunBench("Async2CallingAsync");
+                await RunBench("Async2CallingValueTaskAsync");
+                await RunBench("Async2CallingAsync2");
+                await RunBench("Async2CallingAsync2NoInlining");
+                await RunBench("Async2CallingAsync2WithContextSave");
+                await RunBench("Sync2CallingSync");
+            }
+
+            // Make sure we tier up...
+            await Task.Delay(500);
+        }
+
+        Console.WriteLine("Warmup done, running benchmark");
+        printResult = true;
+        time = 1000;
+        await RunBench("AsyncCallingAsync");
+        await RunBench("AsyncCallingValueTaskAsync");
+        await RunBench("AsyncCallingAsync2");
+        await RunBench("Async2CallingAsync");
+        await RunBench("Async2CallingValueTaskAsync");
+        await RunBench("Async2CallingAsync2");
+        await RunBench("Async2CallingAsync2NoInlining");
+        await RunBench("Async2CallingAsync2WithContextSave");
+        await RunBench("Sync2CallingSync");
+    }
+
+    static double time = 10.0;
+    static bool printResult = false;
+    private static async Task RunBench(string type)
+    {
+        if (printResult)
+            Console.WriteLine($"Running benchmark on '{type}' methods");
+
+        List<long> results = new();
+        for (int i = 0; i < 16; i++)
+        {
+            long numIters = await Run(type);
+//            Console.WriteLine($"iters={numIters}");
+            results.Add(numIters);
+        }
+
+        results.Sort();
+        double avg = results.Skip(3).Take(10).Average();
+        if (printResult)
+            Console.WriteLine("Result = {0}", (long)avg);
+    }
+
+    private static async Task<long> Run(string type)
+    {
+        if (type == "AsyncCallingAsync")
+            return await AsyncCallingAsync();
+        if (type == "AsyncCallingValueTaskAsync")
+            return await AsyncCallingValueTaskAsync();
+        if (type == "AsyncCallingAsync2")
+            return await AsyncCallingAsync2();
+        if (type == "AsyncCallingYield")
+            return await AsyncCallingYield();
+        if (type == "Async2CallingAsync")
+            return await Async2CallingAsync();
+        if (type == "Async2CallingValueTaskAsync")
+            return await Async2CallingValueTaskAsync();
+        if (type == "Async2CallingAsync2")
+            return await Async2CallingAsync2();
+        if (type == "Async2CallingAsync2NoInlining")
+            return await Async2CallingAsync2NoInlining();
+        if (type == "Async2CallingYield")
+            return await Async2CallingYield();
+        if (type == "Sync2CallingSync")
+            return Sync2CallingSync();
+        if (type == "Async2CallingAsync2WithContextSave")
+            return await Async2CallingAsync2WithContextSave();
+
+        return 0;
+    }
+#pragma warning disable CS1998
+
+    private static async Task<long> AsyncCallingAsync()
+    {
+        Stopwatch timer = Stopwatch.StartNew();
+
+        long numIters = 0;
+
+        while (timer.ElapsedMilliseconds < time)
+        {
+            for (int i = 0; i < 10; i++)
+            {
+                await EmptyAsync();
+            }
+            numIters++;
+        }
+        return numIters * 10;
+    }
+
+    private static async Task<long> AsyncCallingValueTaskAsync()
+    {
+        Stopwatch timer = Stopwatch.StartNew();
+
+        long numIters = 0;
+
+        while (timer.ElapsedMilliseconds < time)
+        {
+            for (int i = 0; i < 10; i++)
+            {
+                await EmptyValueTaskAsync();
+            }
+            numIters++;
+        }
+        return numIters * 10;
+    }
+
+    private static async Task<long> AsyncCallingAsync2()
+    {
+        Stopwatch timer = Stopwatch.StartNew();
+
+        long numIters = 0;
+
+        while (timer.ElapsedMilliseconds < time)
+        {
+            for (int i = 0; i < 10; i++)
+            {
+                await EmptyAsync2();
+            }
+            numIters++;
+        }
+        return numIters * 10;
+    }
+
+    private static async2 Task<long> Async2CallingAsync()
+    {
+        Stopwatch timer = Stopwatch.StartNew();
+
+        long numIters = 0;
+
+        while (timer.ElapsedMilliseconds < time)
+        {
+            for (int i = 0; i < 10; i++)
+            {
+                await EmptyAsync();
+            }
+            numIters++;
+        }
+        return numIters * 10;
+    }
+
+    private static async2 Task<long> Async2CallingValueTaskAsync()
+    {
+        Stopwatch timer = Stopwatch.StartNew();
+
+        long numIters = 0;
+
+        while (timer.ElapsedMilliseconds < time)
+        {
+            for (int i = 0; i < 10; i++)
+            {
+                await EmptyValueTaskAsync();
+            }
+            numIters++;
+        }
+        return numIters * 10;
+    }
+
+    private static async2 Task<long> Async2CallingAsync2()
+    {
+        Stopwatch timer = Stopwatch.StartNew();
+
+        long numIters = 0;
+
+        while (timer.ElapsedMilliseconds < time)
+        {
+            for (int i = 0; i < 10; i++)
+            {
+                await EmptyAsync2();
+            }
+            numIters++;
+        }
+        return numIters * 10;
+    }
+
+    private static async2 Task<long> Async2CallingAsync2NoInlining()
+    {
+        Stopwatch timer = Stopwatch.StartNew();
+
+        long numIters = 0;
+
+        while (timer.ElapsedMilliseconds < time)
+        {
+            for (int i = 0; i < 10; i++)
+            {
+                await EmptyAsync2NoInlining();
+            }
+            numIters++;
+        }
+        return numIters * 10;
+    }
+
+    private static async2 Task<long> Async2CallingAsync2WithContextSave()
+    {
+        FakeThread thread = CurrentThread;
+        if (thread == null)
+        {
+            CurrentThread = new FakeThread();
+            thread = CurrentThread;
+        }
+
+        Stopwatch timer = Stopwatch.StartNew();
+
+        long numIters = 0;
+
+        while (timer.ElapsedMilliseconds < time)
+        {
+            for (int i = 0; i < 10; i++)
+            {
+                await EmptyAsync2WithContextSave();
+            }
+            numIters++;
+        }
+        return numIters * 10;
+    }
+
+    public class FakeSyncContext {}
+    public class FakeExecContext
+    {
+        [MethodImpl(MethodImplOptions.NoInlining)]
+        public static void RestoreChangedContextToThread(FakeThread thread, FakeExecContext execContext, FakeExecContext newExecContext)
+        {
+        }
+    }
+    public class FakeThread
+    {
+        public FakeSyncContext _syncContext;
+        public FakeExecContext _execContext;
+    }
+    [ThreadStatic]
+    public static FakeThread CurrentThread;
+
+
+    private static async Task<long> AsyncCallingYield()
+    {
+        Stopwatch timer = Stopwatch.StartNew();
+
+        long numIters = 0;
+
+        while (timer.ElapsedMilliseconds < time)
+        {
+            for (int i = 0; i < 10; i++)
+            {
+                await Task.Yield();
+            }
+            numIters++;
+        }
+        return numIters * 10;
+    }
+
+    private static async2 Task<long> Async2CallingYield()
+    {
+        Stopwatch timer = Stopwatch.StartNew();
+
+        long numIters = 0;
+
+        while (timer.ElapsedMilliseconds < time)
+        {
+            for (int i = 0; i < 10; i++)
+            {
+                await Task.Yield();
+            }
+            numIters++;
+        }
+        return numIters * 10;
+    }
+
+    private static long Sync2CallingSync()
+    {
+        Stopwatch timer = Stopwatch.StartNew();
+
+        long numIters = 0;
+
+        while (timer.ElapsedMilliseconds < time)
+        {
+            for (int i = 0; i < 10; i++)
+            {
+                EmptyMethod();
+            }
+            numIters++;
+        }
+        return numIters * 10;
+    }
+
+    private static async Task EmptyAsync()
+    {
+        // Add some work that forces the method to be a real async method
+        if (time == 0)
+            await Task.Yield();
+        return;
+    }
+
+    private static async ValueTask EmptyValueTaskAsync()
+    {
+        // Add some work that forces the method to be a real async method
+        if (time == 0)
+            await Task.Yield();
+        return;
+    }
+
+    private static async2 Task EmptyAsync2()
+    {
+        // Add some work that forces the method to be a real async method
+        if (time == 0)
+            await Task.Yield();
+        return;
+    }
+
+    [MethodImpl(MethodImplOptions.NoInlining)]
+    private static async2 Task EmptyAsync2NoInlining()
+    {
+        // Add some work that forces the method to be a real async method
+        if (time == 0)
+            await Task.Yield();
+        return;
+    }
+
+    // This simulates async2 capturing the same amount of state that existing async needs to capture to handle the current semantics around async locals and synchronizationcontext
+    private static async2 Task EmptyAsync2WithContextSave()
+    {
+        FakeThread thread = CurrentThread;
+        FakeExecContext? previousExecutionCtx = thread._execContext;
+        FakeSyncContext? previousSyncCtx = thread._syncContext;
+
+        try
+        {
+            // Add some work that forces the method to be a real async method
+            if (time == 0)
+                await Task.Yield();
+        }
+        finally
+        {
+            // The common case is that these have not changed, so avoid the cost of a write barrier if not needed.
+            if (previousSyncCtx != thread._syncContext)
+            {
+                // Restore changed SynchronizationContext back to previous
+                thread._syncContext = previousSyncCtx;
+            }
+
+            FakeExecContext? currentExecutionCtx = thread._execContext;
+            if (previousExecutionCtx != currentExecutionCtx)
+            {
+                FakeExecContext.RestoreChangedContextToThread(thread, previousExecutionCtx, currentExecutionCtx);
+            }
+        }
+    }
+
+    [MethodImpl(MethodImplOptions.NoInlining)]
+    private static void EmptyMethod()
+    {
+        return;
+    }
+}
diff --git a/src/tests/Loader/async/async2-mincallcost-microbench.csproj b/src/tests/Loader/async/async2-mincallcost-microbench.csproj
new file mode 100644
index 00000000000000..d159a32e762f81
--- /dev/null
+++ b/src/tests/Loader/async/async2-mincallcost-microbench.csproj
@@ -0,0 +1,9 @@
+<Project Sdk="Microsoft.NET.Sdk.IL">
+  <PropertyGroup>
+    <Optimize>True</Optimize>
+    <RequiresProcessIsolation>true</RequiresProcessIsolation>
+  </PropertyGroup>
+  <ItemGroup>
+    <Compile Include="$(MSBuildProjectName).cs" />
+  </ItemGroup>
+</Project>

From c5a199ce91da28146ff15d5273df266722e7c832 Mon Sep 17 00:00:00 2001
From: IS4 <IllidanS4@gmail.com>
Date: Tue, 9 Jan 2024 21:14:25 +0100
Subject: [PATCH 098/203] Fix typo

---
 docs/design/features/runtime-handled-tasks.md | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/docs/design/features/runtime-handled-tasks.md b/docs/design/features/runtime-handled-tasks.md
index e66b18408cfe17..5fa57567626c24 100644
--- a/docs/design/features/runtime-handled-tasks.md
+++ b/docs/design/features/runtime-handled-tasks.md
@@ -25,10 +25,10 @@ This section is a work in progress, but describes the current expected semantic
 
 To specify that a method follows async2 rules, a custom modifier to either `System.Threading.Tasks.Task`, ``System.Threading.Tasks.Task`1``, `System.Threading.Tasks.ValueTask`, or ``System.Threading.Tasks.ValueTask`1`` shall be placed as the last custom modifier before the return type in the signature of a method.
 
-### Flowing `SyncronizationContext` and `ExecutionContext` and other behavior such as AsyncLocal
-Unlike traditional C# compiler generated async, async2 methods shall not save/restore `SyncronizationContext` and `ExecutionContext` at function boundaries. Instead, changes to them can be observed by their callers (as long as the caller is not a Task to async2 thunk).
+### Flowing `SynchronizationContext` and `ExecutionContext` and other behavior such as AsyncLocal
+Unlike traditional C# compiler generated async, async2 methods shall not save/restore `SynchronizationContext` and `ExecutionContext` at function boundaries. Instead, changes to them can be observed by their callers (as long as the caller is not a Task to async2 thunk).
 
-#### Integration with `SyncronizationContext` and resumption
+#### Integration with `SynchronizationContext` and resumption
 
 A new attribute `System.Runtime.CompilerServices.ConfigureAwaitAttribute` will be defined. It can be applied at the assembly/module/type/method level. It shall be defined as 
 ```
@@ -696,4 +696,4 @@ Managed heap size: **157Mb**   - slightly more than async1
 With `set DOTNET_TieredCompilation=1`
 Managed heap size: **reaches 300Mb** in the first couple iterations, but then drops and **stays at 105Mb**, which is better than async1.
 
- 
\ No newline at end of file
+ 

From e69dda51c7d796b8122d0f55b560bc44094a4bec Mon Sep 17 00:00:00 2001
From: Vladimir Sadov <vsadov@microsoft.com>
Date: Tue, 23 Jan 2024 11:00:41 -0800
Subject: [PATCH 099/203] Writeup on C# Language changes in async2 experiment.
 (#2488)

* Writeup on C# Language changes in async2 experiment.

* Apply suggestions from code review

Co-authored-by: Jan Kotas <jkotas@microsoft.com>

* Update docs/design/features/runtime-handled-tasks.md

Co-authored-by: Dan Moseley <danmose@microsoft.com>

* Apply suggestions from code review

Co-authored-by: Dan Moseley <danmose@microsoft.com>

* pr feedback

* Apply suggestions from code review

Co-authored-by: Dan Moseley <danmose@microsoft.com>

* pr feedback

---------

Co-authored-by: Jan Kotas <jkotas@microsoft.com>
Co-authored-by: Dan Moseley <danmose@microsoft.com>
---
 docs/design/features/runtime-handled-tasks.md | 132 +++++++++++++++---
 1 file changed, 113 insertions(+), 19 deletions(-)

diff --git a/docs/design/features/runtime-handled-tasks.md b/docs/design/features/runtime-handled-tasks.md
index 5fa57567626c24..c869d7a2ce4722 100644
--- a/docs/design/features/runtime-handled-tasks.md
+++ b/docs/design/features/runtime-handled-tasks.md
@@ -31,7 +31,7 @@ Unlike traditional C# compiler generated async, async2 methods shall not save/re
 #### Integration with `SynchronizationContext` and resumption
 
 A new attribute `System.Runtime.CompilerServices.ConfigureAwaitAttribute` will be defined. It can be applied at the assembly/module/type/method level. It shall be defined as 
-```
+```cs
 namespace System.Runtime.CompilerServices
 {
     class ConfigureAwaitAttribute : Attribute
@@ -52,14 +52,14 @@ The behavior of this attribute shall be to apply configure await semantics for r
 
 Any MethodDef that is an async2 based method can also be called as a `Task`/`ValueTask` returning method. Likewise any MethodDef which returns `Task` or `ValueTask` can be called via an async2 entrypoint. In addition, MethodImpl records and virtual method override rules shall be adjusted so that if an interface or base type defines a virtual method using a signature of `Task`/`ValueTask` then it can be overriden via a method that is an async2 api. The same is true vice versa. For overrides implemented via `MethodImpl` the MethodImpl must describe the Body and Decl using signatures which are either both async2 or `Task`/`ValueTask` returning, but the MethodImpl will also apply to the other async variant. These overrides are permitted to be interleaved, so for example
 
-```
+```cs
 class Base
 {
     public virtual async Task<int> Method() { ... }
 }
 class Derived : Base
 {
-    public override async2 int Method() { ... }
+    public override async2 Task<int> Method() { ... }
 }
 
 class MoreDerived : Derived
@@ -71,7 +71,7 @@ class MoreDerived : Derived
 Any attempt to call an entrypoint will resolve to either an implementation which matches the signature, or in case of an async variant mismatch, will resolve to a runtime generated thunk which will call into the developer provided implementation.
 
 Methods that return Task via generics do not follow this rule. For instance
-```
+```cs
 class MyGeneric<T>
 {
     T DoSomething() { ... }
@@ -84,16 +84,16 @@ class MyGeneric<T>
 
 A thunk from an async2 api surface to a Task based implementation will have the following psuedocode, if a legal thunk can be made.
 
-```
-async2 ReturnType ThunkAsync(ParameterType param1, ParameterType2 param2, ...)
+```cs
+async2 Task<ReturnType> ThunkAsync(ParameterType param1, ParameterType2 param2, ...)
 {
     return RuntimeHelpers.UnsafeAwaitAwaiterFromRuntimeAsync<TaskAwaiter<ReturnType>, ReturnType>(TargetMethod(param1, param2, ...));
 }
 ```
 
 If any of the parameter types to the target method are ref parameters, or ref structures, then the Thunk generated will be 
-```
-async2 ReturnType ThunkAsync(ParameterType param1, ParameterType2 param2, ...)
+```cs
+async2 Task<ReturnType> ThunkAsync(ParameterType param1, ParameterType2 param2, ...)
 {
     throw new InvalidProgramException();
 }
@@ -130,8 +130,11 @@ The StackTrace of a exception thrown within an async2 method shall include any a
 The runtime shall provide the following apis
 
 ```
-        public static async2 void AwaitAwaiterFromRuntimeAsync<TAwaiter>(TAwaiter awaiter) where TAwaiter : INotifyCompletion
-        public static async2 void UnsafeAwaitAwaiterFromRuntimeAsync<TAwaiter>(TAwaiter awaiter) where TAwaiter : ICriticalNotifyCompletion
+        // public static async2 Task AwaitAwaiterFromRuntimeAsync<TAwaiter>(TAwaiter awaiter) where TAwaiter : INotifyCompletion
+        public static void modopt([System.Runtime]System.Threading.Tasks.Task) AwaitAwaiterFromRuntimeAsync<TAwaiter>(TAwaiter awaiter) where TAwaiter : INotifyCompletion
+
+        // public static async2 Task UnsafeAwaitAwaiterFromRuntimeAsync<TAwaiter>(TAwaiter awaiter) where TAwaiter : ICriticalNotifyCompletion
+        public static void modopt([System.Runtime]System.Threading.Tasks.Task) UnsafeAwaitAwaiterFromRuntimeAsync<TAwaiter>(TAwaiter awaiter) where TAwaiter : ICriticalNotifyCompletion
 ```
 
 Any IL in an async2 method which needs to await will follow the existing C# model of calling `IsCompleted` and `GetResult()` and in the middle instead of using the existing patterns for suspension shall call one of the above helper methods.
@@ -277,7 +280,7 @@ The resumption stub implements the following:
 
 In pseudo-C#:
 ```csharp
-static async2 int Foo(int a, int b)
+static async2 Task<int> Foo(int a, int b)
 {
     ...
 }
@@ -355,7 +358,7 @@ private struct RuntimeAsyncAwaitState
 [ThreadStatic]
 private static RuntimeAsyncAwaitState t_runtimeAsyncAwaitState;
 
-public static async2 void AwaitAwaiterFromRuntimeAsync<TAwaiter>(TAwaiter awaiter) where TAwaiter : INotifyCompletion
+public static async2 Task AwaitAwaiterFromRuntimeAsync<TAwaiter>(TAwaiter awaiter) where TAwaiter : INotifyCompletion
 {
     ref RuntimeAsyncAwaitState state = ref t_runtimeAsyncAwaitState;
     Continuation? sentinelContinuation = state.SentinelContinuation;
@@ -438,8 +441,8 @@ It turns out that due to the api design of how async2 code interacts with the ex
 
 ### Thunk from the async2 surface to Task api implementation
 
-```
-async2 ReturnType Thunk(ParameterType param1, ParameterType2 param2, ...)
+```cs
+async2 Task<ReturnType> Thunk(ParameterType param1, ParameterType2 param2, ...)
 {
     var awaiter = TargetMethod(param1, param2, ...).GetAwaiter();
     if (!awaiter.IsCompleted)
@@ -452,13 +455,104 @@ async2 ReturnType Thunk(ParameterType param1, ParameterType2 param2, ...)
 
 ## C# Language changes
 
-TBD, and not part of this experiment. We have built something. It is strictly for demonstrating that code CAN be generated.
+For the purpose of the experiment we have extended the syntax and semantics of C# language to support async2 methods. It is not the goal for this syntax to become the syntax in the actual implementation, however we will record our experiences with the experimental syntax in case it is useful when designing the actual syntax.
+  
+#### === Syntax v2 (current)
+
+The syntax that is currently in use in the experiment looks like:
+
+```cs
+async2 Task<int> M1(int arg) {}
+
+// meaning in IL 
+int32 modopt([System.Runtime]System.Threading.Tasks.Task`1) M1(int arg)
+```
+
+That is basically the same syntax as used by regular async, except for a different modifier. (The name of the modifier is intentionally `async2`, to indicate that the name is at best a placeholder). The syntax naturally generalizes to:
+- Nongeneric Task-returning.
+```cs
+async2 Task M1(int arg) {}
+
+// meaning in IL 
+void modopt([System.Runtime]System.Threading.Tasks.Task) M1(int arg)
+```
+- ValueTask-returning.
+```cs
+async2 ValueTask<int> M1(int arg) {}
+
+// meaning in IL 
+int32 modopt([System.Runtime]System.Threading.Tasks.ValueTask`1) M1(int arg)
+```
+and combinations of the above.
+
+Internal Roslyn compiler representation of async2 methods is roughly the same as for regular `Task/ValueTask` returning methods, except that the method symbol reports that it is an async2 method thus scenarios that are different for different flavors of async/async2 (i.e. lowering of `await` operator), can work differently. 
+
+In a particular case of lowering await operator, if we notice that both containing and awaited members are async2, we can lower the `await M1()` into a direct call to the `int modopt(..)M1()`
+Conversely, if we see an invocation of `M1()` without await or in a regular, non-async2 member, we lower that into a call to `Task<int> M1()` thunk.
+And when inside an async2 function we see an `await` with an argument that is not an async2, we emit corresponding `GetAwaiter`/`IsCompleted`/`UnsafeAwaitAwaiterFromRuntimeAsync`/`GetResult` sequence.
 
-Major identified concerns are
+For example.
+```cs
+public async2 Task<int> M1()
+{
+    await Task.Yield();
+    return 3;
+}
+```
+
+is lowered to an equivalent of:
+
+```cs
+public int32 modopt([System.Runtime]System.Threading.Tasks.Task`1) M1()
+{
+    YieldAwaitable.YieldAwaiter awaiter = Task.Yield().GetAwaiter();
+    if (!awaiter.IsCompleted)
+    {
+        RuntimeHelpers.UnsafeAwaitAwaiterFromRuntimeAsync<YieldAwaitable.YieldAwaiter>(awaiter);
+    }
+    awaiter.GetResult();
+    return 3;
+}
+```
+
+Operations that are not affected by `async2`, such as generic substitution or Overriding/Hiding/Implementing end up naturally working with async2 methods. Note that these areas are some of the most complex parts of the compiler. It was very beneficial to not having to specialcase async2 in these areas.
+
+One thing to observe is that unlike regular `async`, which is a source-only concept, the async2 survives serialization/deserialization via metadata. 
+Effectively we treat the `int32 modopt([System.Runtime]System.Threading.Tasks.Task'1)` as just a special encoding of `Task<int>` return type with additional property of making the method `async2`.
+
+#### Unresolved concerns.
+None of the following appears to be unresolvable or blocking, we just did not get to these due to time constraints and the scoping of the experiment.
+
+1. There is an existing concept for `async void` methods. Does that model need to exist in the new system?
+1. It looks like there might be a need for async2 flavors of delegates. 
+To the runtime these would look like delegates with async2 `Invoke` method and may "just work". They would need some kind of syntax in C#.
+1. It looks like there might be a need for async2 flavors of method pointers - to represent async2 methods. 
+In the runtime this is representable via the modopt on the return type and, again, might "just work", so this is mostly a matter of picking C# syntax/semantics.
+1. Is there any impact on async enumerable/iterator?  
+While async2 is interoperable with regular async and these interfaces could be used or even implemented by the means of async2 methods, there could be opportunities to employ async2 implementations in the lowering. It is not clear if that could be beneficial without defining/deriving async2 versions of `IAsyncEnumerable`/`IAsyncEnumerator`/`IAsyncDisposable`.
+
+#### ==== Syntax v1 (abandoned)
+
+Initially we assumed a different syntax for async2 methods. It ended up not working well beyond simple scenarios. We will still describe that here for future reference as something that appear to be a syntactical dead-end.
+
+The initial version of the syntax looked like:
+
+```cs
+async2 int M1(int arg) {}
+
+// meaning in IL 
+int32 modopt([System.Runtime]System.Threading.Tasks.Task`1) M1(int arg)
+```
 
-1. What about code which today looks like `public async Task<Task<Task<int>>> Method()` What would the new syntax be?
-2. It is desirable to be able to implement methods that return `ValueTask` as well as just `Task` with an async2 method. How do we distinguish?
-3. There is an existing concept for `async void` methods. Does that model need to exist in the new system.
+While initially favored for being close to the IL representation, it was eventually abandoned due to major problems and inconsistencies.
+- When the method above is called from regular code without awaiting, user gets `Task<int>` result. That is not what it looks in the signature.
+- For the purposes of overriding/hiding/inheriting the method above is equivalent to `Task<int> M1(int arg)`, which is easily observable.
+For example it is illegal to add such method in the same class as invoking `Task<int> M1(int arg)` would become ambiguous. On the other hand overloading a method like `int M1(int arg)` is completely non-conflicting.  
+This was very confusing, both as a programming model and as internal implementation.  
+- Internally, due to duality with `Task<int> M1(int arg)` signature it was often necessary to "lift" the signature into the `Task<int>` form, perform operation there (such as generic substitution or overriding resolution) and then "unlift" the result back into `async2 int` form. It was frequintly a source of subtle bugs.  
+- When we got to interfacing with `ValueTask` there was no good place in the syntax to express a variant of the same method, but with `ValueTask`-based thunk.
+For some time we used an attribute and that was verbose and not symmetrical.  
+Besides, it is valid for the same class to contain two async2 methods that differ only on `Task`/`ValueTask` flavor of the thunk. It would mean that in the source one could have two methods in the same class, differing only by an attribute.  
 
 ## Microbenchmarks
 

From f538fc95464908fd85d7821e90764b6c9b7af4cf Mon Sep 17 00:00:00 2001
From: Jakob Botsch Nielsen <jakob.botsch.nielsen@gmail.com>
Date: Tue, 15 Oct 2024 15:08:51 +0200
Subject: [PATCH 100/203] Adapt SPC and JIT

---
 .../RuntimeHelpers.CoreCLR.cs                 |   2 +
 src/coreclr/jit/async.cpp                     | 137 ++++++++++--------
 src/coreclr/jit/codegencommon.cpp             |   2 +-
 src/coreclr/jit/compiler.h                    |   2 +-
 src/coreclr/jit/fgdiagnostic.cpp              |   2 +-
 src/coreclr/jit/layout.cpp                    |  19 +++
 src/coreclr/jit/layout.h                      |   2 +
 src/coreclr/jit/lclvars.cpp                   |   2 +-
 src/coreclr/jit/lsraxarch.cpp                 |   4 +-
 9 files changed, 102 insertions(+), 70 deletions(-)

diff --git a/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs
index 8ba1e98e1bf864..32d26f440ead7a 100644
--- a/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs
+++ b/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs
@@ -8,6 +8,8 @@
 using System.Runtime.InteropServices;
 using System.Runtime.Serialization;
 using System.Runtime.Versioning;
+using System.Threading;
+using System.Threading.Tasks;
 
 namespace System.Runtime.CompilerServices
 {
diff --git a/src/coreclr/jit/async.cpp b/src/coreclr/jit/async.cpp
index 2837421957aec9..933d41fc75f9e7 100644
--- a/src/coreclr/jit/async.cpp
+++ b/src/coreclr/jit/async.cpp
@@ -54,13 +54,13 @@ class AsyncLiveness
             return true;
         }
 
-#ifdef FEATURE_EH_FUNCLETS
         if (lclNum == m_comp->lvaPSPSym)
         {
             // Initialized in prolog
             return true;
         }
-#else
+
+#ifdef FEATURE_EH_WINDOWS_X86
         if (lclNum == m_comp->lvaShadowSPslotsVar)
         {
             // Only expected to be live in handlers
@@ -122,17 +122,7 @@ class AsyncLiveness
             // Dependently promoted structs are handled only through the base
             // struct local.
             //
-            // A dependently promoted struct is live either if it has significant
-            // padding (since we do not track liveness for the significant
-            // padding), or if all its fields are dead.
-
-            if (dsc->lvAnySignificantPadding)
-            {
-                // We could technically only save the padding and the live fields
-                // in this case, but that's a lot of complexity for not a lot of
-                // gain.
-                return true;
-            }
+            // A dependently promoted struct is live if any of its fields are live.
 
             for (unsigned i = 0; i < dsc->lvFieldCnt; i++)
             {
@@ -535,7 +525,7 @@ void Async2Transformation::Transform(
 
     JITDUMP("  Remainder is " FMT_BB "\n", remainder->bbNum);
 
-    assert(block->KindIs(BBJ_NONE) && block->NextIs(remainder));
+    assert(block->KindIs(BBJ_ALWAYS) && block->TargetIs(remainder));
 
     if (m_lastSuspensionBB == nullptr)
     {
@@ -550,9 +540,8 @@ void Async2Transformation::Transform(
 
     JITDUMP("  Created suspension " FMT_BB " for state %u\n", retBB->bbNum, stateNum);
 
-    block->SetJumpKindAndTarget(BBJ_COND, retBB DEBUGARG(m_comp));
-
-    m_comp->fgAddRefPred(retBB, block);
+    FlowEdge* retBBEdge = m_comp->fgAddRefPred(retBB, block);
+    block->SetCond(retBBEdge, block->GetTargetEdge());
 
     // Allocate continuation
     returnedContinuation           = m_comp->gtNewLclvNode(m_returnedContinuationVar, TYP_REF);
@@ -761,17 +750,18 @@ void Async2Transformation::Transform(
         m_lastResumptionBB = m_comp->fgLastBBInMainFunction();
     }
 
-    BasicBlock* resumeBB = m_comp->fgNewBBafter(BBJ_ALWAYS, m_lastResumptionBB, true, remainder);
+    BasicBlock* resumeBB = m_comp->fgNewBBafter(BBJ_ALWAYS, m_lastResumptionBB, true);
+    FlowEdge* remainderEdge = m_comp->fgAddRefPred(remainder, resumeBB);
+
+    resumeBB->SetTargetEdge(remainderEdge);
     resumeBB->bbSetRunRarely();
     resumeBB->clearTryIndex();
     resumeBB->clearHndIndex();
-    resumeBB->bbFlags |= BBF_ASYNC_RESUMPTION;
+    resumeBB->SetFlags(BBF_ASYNC_RESUMPTION);
     m_lastResumptionBB = resumeBB;
 
     JITDUMP("  Created resumption " FMT_BB " for state %u\n", resumeBB->bbNum, stateNum);
 
-    m_comp->fgAddRefPred(remainder, resumeBB);
-
     unsigned resumeByteArrLclNum = BAD_VAR_NUM;
     if (dataSize > 0)
     {
@@ -896,23 +886,28 @@ void Async2Transformation::Transform(
             }
         }
 
-        BasicBlock* storeResultBB = resumeBB;
         if (exceptionGCDataIndex != UINT_MAX)
         {
             JITDUMP("  We need to rethrow an exception\n");
-            BasicBlock* rethrowExceptionBB = m_comp->fgNewBBinRegion(BBJ_THROW, block, /* jumpDest */ nullptr,
-                                                                     /* runRarely */ true, /* insertAtEnd */ true);
+
+            BasicBlock* rethrowExceptionBB = m_comp->fgNewBBinRegion(BBJ_THROW, block, /* runRarely */ true, /* insertAtEnd */ true);
             JITDUMP("  Created " FMT_BB " to rethrow exception on resumption\n", rethrowExceptionBB->bbNum);
-            resumeBB->SetJumpKindAndTarget(BBJ_COND, rethrowExceptionBB DEBUGARG(m_comp));
-            m_comp->fgAddRefPred(rethrowExceptionBB, resumeBB);
-            m_comp->fgRemoveRefPred(remainder, resumeBB);
 
+            BasicBlock* storeResultBB = m_comp->fgNewBBafter(BBJ_ALWAYS, resumeBB, true);
+            JITDUMP("  Created " FMT_BB " to store result when resuming with no exception\n", storeResultBB->bbNum);
+
+            FlowEdge* rethrowEdge = m_comp->fgAddRefPred(rethrowExceptionBB, resumeBB);
+            FlowEdge* storeResultEdge = m_comp->fgAddRefPred(storeResultBB, resumeBB);
+
+            assert(resumeBB->KindIs(BBJ_ALWAYS));
+
+            resumeBB->SetCond(rethrowEdge, storeResultEdge);
             JITDUMP("  Resumption " FMT_BB " becomes BBJ_COND to check for non-null exception\n", resumeBB->bbNum);
 
-            storeResultBB = m_comp->fgNewBBafter(BBJ_ALWAYS, resumeBB, true, remainder);
-            JITDUMP("  Created " FMT_BB " to store result when resuming with no exception\n", storeResultBB->bbNum);
-            m_comp->fgAddRefPred(remainder, storeResultBB);
-            m_comp->fgAddRefPred(storeResultBB, resumeBB);
+            m_comp->fgRemoveRefPred(remainderEdge);
+            remainderEdge = m_comp->fgAddRefPred(remainder, storeResultBB);
+
+            storeResultBB->SetTargetEdge(remainderEdge);
 
             m_lastResumptionBB = storeResultBB;
 
@@ -938,7 +933,7 @@ void Async2Transformation::Transform(
 
             LIR::AsRange(rethrowExceptionBB).InsertAtEnd(LIR::SeqTree(m_comp, rethrowException));
 
-            storeResultBB->bbFlags |= BBF_ASYNC_RESUMPTION;
+            storeResultBB->SetFlags(BBF_ASYNC_RESUMPTION);
             JITDUMP("  Added " FMT_BB " to rethrow exception at suspension point\n", rethrowExceptionBB->bbNum);
         }
     }
@@ -1206,7 +1201,7 @@ void Async2Transformation::LiftLIREdges(BasicBlock*                    block,
 
 void Async2Transformation::CreateResumptionSwitch()
 {
-    BasicBlock* newEntryBB = m_comp->bbNewBasicBlock(BBJ_NONE);
+    BasicBlock* newEntryBB = BasicBlock::New(m_comp, BBJ_ALWAYS);
 
     if (m_comp->fgFirstBB->hasProfileWeight())
     {
@@ -1214,8 +1209,8 @@ void Async2Transformation::CreateResumptionSwitch()
     }
     m_comp->fgFirstBB->bbRefs--;
 
-    FlowEdge* edge = m_comp->fgAddRefPred(m_comp->fgFirstBB, newEntryBB);
-    edge->setLikelihood(1);
+    FlowEdge* toPrevEntryEdge = m_comp->fgAddRefPred(m_comp->fgFirstBB, newEntryBB);
+    toPrevEntryEdge->setLikelihood(1);
 
     JITDUMP("  Inserting new entry " FMT_BB " before old entry " FMT_BB "\n", newEntryBB->bbNum,
             m_comp->fgFirstBB->bbNum);
@@ -1236,22 +1231,24 @@ void Async2Transformation::CreateResumptionSwitch()
     {
         JITDUMP("  Redirecting entry " FMT_BB " directly to " FMT_BB " as it is the only resumption block\n",
                 newEntryBB->bbNum, m_resumptionBBs[0]->bbNum);
-        newEntryBB->SetJumpKindAndTarget(BBJ_COND, m_resumptionBBs[0] DEBUGARG(m_comp));
-        m_comp->fgAddRefPred(m_resumptionBBs[0], newEntryBB);
+        FlowEdge* resumptionEdge = m_comp->fgAddRefPred(m_resumptionBBs[0], newEntryBB);
+        newEntryBB->SetCond(resumptionEdge, toPrevEntryEdge);
     }
     else if (m_resumptionBBs.size() == 2)
     {
-        BasicBlock* condBB = m_comp->fgNewBBbefore(BBJ_COND, m_resumptionBBs[0], true, m_resumptionBBs[1]);
+        BasicBlock* condBB = m_comp->fgNewBBbefore(BBJ_COND, m_resumptionBBs[0], true);
         condBB->bbSetRunRarely();
-        newEntryBB->SetJumpKindAndTarget(BBJ_COND, condBB DEBUGARG(m_comp));
+
+        FlowEdge* to0 = m_comp->fgAddRefPred(m_resumptionBBs[0], condBB);
+        FlowEdge* to1 = m_comp->fgAddRefPred(m_resumptionBBs[1], condBB);
+        condBB->SetCond(to0, to1);
+
+        FlowEdge* toCond = m_comp->fgAddRefPred(condBB, newEntryBB);
+        newEntryBB->SetCond(toCond, toPrevEntryEdge);
 
         JITDUMP("  Redirecting entry " FMT_BB " to BBJ_COND " FMT_BB " for resumption with 2 states\n",
                 newEntryBB->bbNum, condBB->bbNum);
 
-        m_comp->fgAddRefPred(condBB, newEntryBB);
-        m_comp->fgAddRefPred(m_resumptionBBs[0], condBB);
-        m_comp->fgAddRefPred(m_resumptionBBs[1], condBB);
-
         continuationArg          = m_comp->gtNewLclvNode(m_comp->lvaAsyncContinuationArg, TYP_REF);
         unsigned stateOffset     = m_comp->info.compCompHnd->getFieldOffset(m_async2Info.continuationStateFldHnd);
         GenTree* stateOffsetNode = m_comp->gtNewIconNode((ssize_t)stateOffset, TYP_I_IMPL);
@@ -1268,13 +1265,13 @@ void Async2Transformation::CreateResumptionSwitch()
     {
         BasicBlock* switchBB = m_comp->fgNewBBbefore(BBJ_SWITCH, m_resumptionBBs[0], true);
         switchBB->bbSetRunRarely();
-        newEntryBB->SetJumpKindAndTarget(BBJ_COND, switchBB DEBUGARG(m_comp));
+
+        FlowEdge* toSwitch = m_comp->fgAddRefPred(switchBB, newEntryBB);
+        newEntryBB->SetCond(toSwitch, toPrevEntryEdge);
 
         JITDUMP("  Redirecting entry " FMT_BB " to BBJ_SWITCH " FMT_BB " for resumption with %zu states\n",
                 newEntryBB->bbNum, switchBB->bbNum, m_resumptionBBs.size());
 
-        m_comp->fgAddRefPred(switchBB, newEntryBB);
-
         continuationArg          = m_comp->gtNewLclvNode(m_comp->lvaAsyncContinuationArg, TYP_REF);
         unsigned stateOffset     = m_comp->info.compCompHnd->getFieldOffset(m_async2Info.continuationStateFldHnd);
         GenTree* stateOffsetNode = m_comp->gtNewIconNode((ssize_t)stateOffset, TYP_I_IMPL);
@@ -1291,14 +1288,14 @@ void Async2Transformation::CreateResumptionSwitch()
         BBswtDesc* swtDesc     = new (m_comp, CMK_BasicBlock) BBswtDesc;
         swtDesc->bbsCount      = (unsigned)m_resumptionBBs.size();
         swtDesc->bbsHasDefault = true;
-        swtDesc->bbsDstTab     = m_resumptionBBs.data();
-
-        switchBB->SetSwitchKindAndTarget(swtDesc);
+        swtDesc->bbsDstTab = new (m_comp, CMK_Async2) FlowEdge*[m_resumptionBBs.size()];
 
-        for (BasicBlock* bb : m_resumptionBBs)
+        for (size_t i = 0; i < m_resumptionBBs.size(); i++)
         {
-            m_comp->fgAddRefPred(bb, switchBB);
+            swtDesc->bbsDstTab[i] = m_comp->fgAddRefPred(m_resumptionBBs[i], switchBB);
         }
+
+        switchBB->SetSwitch(swtDesc);
     }
 
     if (m_comp->doesMethodHavePatchpoints())
@@ -1312,17 +1309,21 @@ void Async2Transformation::CreateResumptionSwitch()
 
         JITDUMP("    Created " FMT_BB " for transitions back into OSR method\n", callHelperBB->bbNum);
 
-        BasicBlock* checkILOffsetBB = m_comp->fgNewBBbefore(BBJ_COND, newEntryBB->GetJumpDest(), true, callHelperBB);
+        BasicBlock* onContinuationBB = newEntryBB->GetTrueTarget();
+        BasicBlock* checkILOffsetBB = m_comp->fgNewBBbefore(BBJ_COND, onContinuationBB, true);
 
         JITDUMP("    Created " FMT_BB " to check whether we should transition immediately to OSR\n",
                 checkILOffsetBB->bbNum);
 
-        m_comp->fgRemoveRefPred(newEntryBB->GetJumpDest(), newEntryBB);
-        newEntryBB->SetJumpDest(checkILOffsetBB);
+        // Redirect newEntryBB -> onContinuationBB into newEntryBB -> checkILOffsetBB -> onContinuationBB
+        m_comp->fgRemoveRefPred(newEntryBB->GetTrueEdge());
+
+        FlowEdge* toCheckILOffsetBB = m_comp->fgAddRefPred(checkILOffsetBB, newEntryBB);
+        newEntryBB->SetTrueEdge(toCheckILOffsetBB);
 
-        m_comp->fgAddRefPred(checkILOffsetBB, newEntryBB);
-        m_comp->fgAddRefPred(checkILOffsetBB->Next(), checkILOffsetBB);
-        m_comp->fgAddRefPred(checkILOffsetBB->GetJumpDest(), checkILOffsetBB);
+        FlowEdge* toOnContinuationBB = m_comp->fgAddRefPred(onContinuationBB, checkILOffsetBB);
+        FlowEdge* toCallHelperBB = m_comp->fgAddRefPred(callHelperBB, checkILOffsetBB);
+        checkILOffsetBB->SetCond(toCallHelperBB, toOnContinuationBB);
 
         // We need to dispatch to the OSR version if the IL offset is non-negative.
         continuationArg           = m_comp->gtNewLclvNode(m_comp->lvaAsyncContinuationArg, TYP_REF);
@@ -1357,16 +1358,24 @@ void Async2Transformation::CreateResumptionSwitch()
         // version by normal means then we will see a non-zero continuation
         // here that belongs to the tier0 method. In that case we should just
         // ignore it, so create a BB that jumps back.
+        BasicBlock* onContinuationBB = newEntryBB->GetTrueTarget();
+        BasicBlock* onNoContinuationBB = newEntryBB->GetFalseTarget();
         BasicBlock* checkILOffsetBB =
-            m_comp->fgNewBBbefore(BBJ_COND, newEntryBB->GetJumpDest(), true, newEntryBB->Next());
-        m_comp->fgRemoveRefPred(newEntryBB->GetJumpDest(), newEntryBB);
-        newEntryBB->SetJumpDest(checkILOffsetBB);
+            m_comp->fgNewBBbefore(BBJ_COND, onContinuationBB, true);
 
-        JITDUMP("    Created " FMT_BB " to check for Tier-0 continuations\n", checkILOffsetBB->bbNum);
+        // Switch newEntryBB -> onContinuationBB into newEntryBB -> checkILOffsetBB
+        m_comp->fgRemoveRefPred(newEntryBB->GetTrueEdge());
+        FlowEdge* toCheckILOffset = m_comp->fgAddRefPred(checkILOffsetBB, newEntryBB);
+        newEntryBB->SetTrueEdge(toCheckILOffset);
 
-        m_comp->fgAddRefPred(checkILOffsetBB, newEntryBB);
-        m_comp->fgAddRefPred(checkILOffsetBB->Next(), checkILOffsetBB);
-        m_comp->fgAddRefPred(checkILOffsetBB->GetJumpDest(), checkILOffsetBB);
+        // Make checkILOffsetBB ->(true)  onNoContinuationBB 
+        //                      ->(false) onContinuationBB
+
+        FlowEdge* toOnContinuationBB = m_comp->fgAddRefPred(onContinuationBB, checkILOffsetBB);
+        FlowEdge* toOnNoContinuationBB = m_comp->fgAddRefPred(onNoContinuationBB, checkILOffsetBB);
+        checkILOffsetBB->SetCond(toOnNoContinuationBB, toOnContinuationBB);
+
+        JITDUMP("    Created " FMT_BB " to check for Tier-0 continuations\n", checkILOffsetBB->bbNum);
 
         continuationArg           = m_comp->gtNewLclvNode(m_comp->lvaAsyncContinuationArg, TYP_REF);
         unsigned offsetOfData     = m_comp->info.compCompHnd->getFieldOffset(m_async2Info.continuationDataFldHnd);
diff --git a/src/coreclr/jit/codegencommon.cpp b/src/coreclr/jit/codegencommon.cpp
index fd917153a0354c..628d6be7a690b5 100644
--- a/src/coreclr/jit/codegencommon.cpp
+++ b/src/coreclr/jit/codegencommon.cpp
@@ -7332,7 +7332,7 @@ void CodeGen::genReturnSuspend(GenTreeUnOp* treeNode)
     {
         if (varTypeIsGC(retTypeDesc.GetReturnRegType(i)))
         {
-            regNumber returnReg = retTypeDesc.GetABIReturnReg(i);
+            regNumber returnReg = retTypeDesc.GetABIReturnReg(i, compiler->info.compCallConv);
             instGen_Set_Reg_To_Zero(EA_PTRSIZE, returnReg);
         }
     }
diff --git a/src/coreclr/jit/compiler.h b/src/coreclr/jit/compiler.h
index b0c3e6f1571459..586893abd405d5 100644
--- a/src/coreclr/jit/compiler.h
+++ b/src/coreclr/jit/compiler.h
@@ -4001,7 +4001,7 @@ class Compiler
 #endif
 
     // Variable representing async continuation argument passed.
-    unsigned lvaAsyncContinuationArg;
+    unsigned lvaAsyncContinuationArg = BAD_VAR_NUM;
 
 #if defined(DEBUG) && defined(TARGET_XARCH)
 
diff --git a/src/coreclr/jit/fgdiagnostic.cpp b/src/coreclr/jit/fgdiagnostic.cpp
index 39582111057a0b..4ecaa9f183f275 100644
--- a/src/coreclr/jit/fgdiagnostic.cpp
+++ b/src/coreclr/jit/fgdiagnostic.cpp
@@ -2726,7 +2726,7 @@ bool BBPredsChecker::CheckEhTryDsc(BasicBlock* block, BasicBlock* blockPred, EHb
         return true;
     }
 
-    if ((blockPred->bbFlags & BBF_ASYNC_RESUMPTION) != 0)
+    if (blockPred->HasFlag(BBF_ASYNC_RESUMPTION))
     {
         return true;
     }
diff --git a/src/coreclr/jit/layout.cpp b/src/coreclr/jit/layout.cpp
index 655750718bc734..9da798abf1ccfc 100644
--- a/src/coreclr/jit/layout.cpp
+++ b/src/coreclr/jit/layout.cpp
@@ -420,6 +420,25 @@ void ClassLayout::InitializeGCPtrs(Compiler* compiler)
     INDEBUG(m_gcPtrsInitialized = true;)
 }
 
+bool ClassLayout::HasGCByRef() const
+{
+    if (!HasGCPtr())
+    {
+        return false;
+    }
+
+    unsigned numSlots = GetSlotCount();
+    for (unsigned i = 0; i < numSlots; i++)
+    {
+        if (GetGCPtrType(i) == TYP_BYREF)
+        {
+            return true;
+        }
+    }
+
+    return false;
+}
+
 //------------------------------------------------------------------------
 // IsStackOnly: does the layout represent a block that can never be on the heap?
 //
diff --git a/src/coreclr/jit/layout.h b/src/coreclr/jit/layout.h
index c2c901d1f33c9a..ae81b5eb77c95d 100644
--- a/src/coreclr/jit/layout.h
+++ b/src/coreclr/jit/layout.h
@@ -185,6 +185,8 @@ class ClassLayout
         return m_gcPtrCount != 0;
     }
 
+    bool HasGCByRef() const;
+
     bool IsStackOnly(Compiler* comp) const;
 
     bool IsGCPtr(unsigned slot) const
diff --git a/src/coreclr/jit/lclvars.cpp b/src/coreclr/jit/lclvars.cpp
index c28a80d2ea23a4..b4137741426669 100644
--- a/src/coreclr/jit/lclvars.cpp
+++ b/src/coreclr/jit/lclvars.cpp
@@ -1510,7 +1510,7 @@ void Compiler::lvaInitAsyncContinuation(InitVarDscInfo* varDscInfo)
         // Passed in register
 
         varDsc->lvIsRegArg = 1;
-        varDsc->SetArgReg(genMapRegArgNumToRegNum(varDscInfo->regArgNum(TYP_INT), varDsc->TypeGet()));
+        varDsc->SetArgReg(genMapRegArgNumToRegNum(varDscInfo->regArgNum(TYP_INT), varDsc->TypeGet(), info.compCallConv));
 #if FEATURE_MULTIREG_ARGS
         varDsc->SetOtherArgReg(REG_NA);
 #endif
diff --git a/src/coreclr/jit/lsraxarch.cpp b/src/coreclr/jit/lsraxarch.cpp
index 78460798b92070..eb222cd4f4b743 100644
--- a/src/coreclr/jit/lsraxarch.cpp
+++ b/src/coreclr/jit/lsraxarch.cpp
@@ -608,8 +608,8 @@ int LinearScan::BuildNode(GenTree* tree)
             // selection phase that the argument is no longer busy. This is a
             // hack to make sure we do not overwrite the continuation between
             // the call and this node.
-            addRefsForPhysRegMask(RBM_ASYNC_CONTINUATION_RET, currentLoc, RefTypeKill, true);
-            BuildDef(tree, RBM_ASYNC_CONTINUATION_RET);
+            addKillForRegs(RBM_ASYNC_CONTINUATION_RET, currentLoc);
+            BuildDef(tree, RBM_ASYNC_CONTINUATION_RET.GetIntRegSet());
             break;
 
 #if defined(FEATURE_EH_WINDOWS_X86)

From 16bb36adfc94b2d6a17334ec40befbc7ccb83904 Mon Sep 17 00:00:00 2001
From: Jakob Botsch Nielsen <jakob.botsch.nielsen@gmail.com>
Date: Tue, 15 Oct 2024 15:09:51 +0200
Subject: [PATCH 101/203] mdc -> mdf

---
 src/coreclr/vm/method.cpp | 2 +-
 src/coreclr/vm/method.hpp | 4 ++--
 2 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/src/coreclr/vm/method.cpp b/src/coreclr/vm/method.cpp
index 6d8de6405eee36..3c08681e58c0d2 100644
--- a/src/coreclr/vm/method.cpp
+++ b/src/coreclr/vm/method.cpp
@@ -1161,7 +1161,7 @@ PTR_AsyncMethodData MethodDesc::GetAddrOfAsyncMethodData()
 
     _ASSERTE(HasAsyncMethodData());
 
-    SIZE_T size = s_ClassificationSizeTable[m_wFlags & (mdcClassification | mdcHasNonVtableSlot |  mdcMethodImpl | mdcHasNativeCodeSlot)];
+    SIZE_T size = s_ClassificationSizeTable[m_wFlags & (mdfClassification | mdfHasNonVtableSlot |  mdfMethodImpl | mdfHasNativeCodeSlot)];
 
     return dac_cast<PTR_AsyncMethodData>(dac_cast<TADDR>(this) + size);
 }
diff --git a/src/coreclr/vm/method.hpp b/src/coreclr/vm/method.hpp
index c19c284e1044eb..afb84f347425cb 100644
--- a/src/coreclr/vm/method.hpp
+++ b/src/coreclr/vm/method.hpp
@@ -1840,13 +1840,13 @@ class MethodDesc
 
     inline bool HasAsyncMethodData()
     {
-        return (m_wFlags & mdcHasAsyncMethodData) != 0;
+        return (m_wFlags & mdfHasAsyncMethodData) != 0;
     }
 
     inline void SetHasAsyncMethodData()
     {
         LIMITED_METHOD_CONTRACT;
-        m_wFlags |= mdcHasAsyncMethodData;
+        m_wFlags |= mdfHasAsyncMethodData;
     }
 
 #ifdef FEATURE_METADATA_UPDATER

From 43fd4d9039cde02f42550ae47561b488f997a6d3 Mon Sep 17 00:00:00 2001
From: Jakob Botsch Nielsen <jakob.botsch.nielsen@gmail.com>
Date: Tue, 15 Oct 2024 15:11:50 +0200
Subject: [PATCH 102/203] Rename

---
 src/coreclr/vm/methoditer.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/coreclr/vm/methoditer.cpp b/src/coreclr/vm/methoditer.cpp
index 103ce49e5a256f..e6ac28c17edd44 100644
--- a/src/coreclr/vm/methoditer.cpp
+++ b/src/coreclr/vm/methoditer.cpp
@@ -63,7 +63,7 @@ BOOL LoadedMethodDescIterator::Next(
 
         if (m_mainMD == NULL)
         {
-            *pDomainAssemblyHolder = NULL;
+            *pAssemblyHolder = NULL;
             return FALSE;
         }
     }

From a91a99bb45f3ee8d5f807ee66e5956788411423a Mon Sep 17 00:00:00 2001
From: Jakob Botsch Nielsen <jakob.botsch.nielsen@gmail.com>
Date: Tue, 15 Oct 2024 15:28:09 +0200
Subject: [PATCH 103/203] Regenerate JIT-EE interface

---
 src/coreclr/inc/jiteeversionguid.h            |  10 +-
 .../JitInterface/CorInfoImpl_generated.cs     | 126 +++++++++---------
 2 files changed, 66 insertions(+), 70 deletions(-)

diff --git a/src/coreclr/inc/jiteeversionguid.h b/src/coreclr/inc/jiteeversionguid.h
index 2cb318c73f3f5a..d4a016647aaa33 100644
--- a/src/coreclr/inc/jiteeversionguid.h
+++ b/src/coreclr/inc/jiteeversionguid.h
@@ -43,11 +43,11 @@ typedef const GUID *LPCGUID;
 #define GUID_DEFINED
 #endif // !GUID_DEFINED
 
-constexpr GUID JITEEVersionIdentifier = { /* 6834d668-2cd6-491e-9e13-28675a28da97 */
-    0x6834d668,
-    0x2cd6,
-    0x491e,
-    {0x9e, 0x13, 0x28, 0x67, 0x5a, 0x28, 0xda, 0x97}
+constexpr GUID JITEEVersionIdentifier = { /* ed41a83d-5d0a-4ee8-8d4e-51b49560db82 */
+    0xed41a83d,
+    0x5d0a,
+    0x4ee8,
+    {0x8d, 0x4e, 0x51, 0xb4, 0x95, 0x60, 0xdb, 0x82}
   };
 
 //////////////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoImpl_generated.cs b/src/coreclr/tools/Common/JitInterface/CorInfoImpl_generated.cs
index d261859763473f..a21bfe667dba1c 100644
--- a/src/coreclr/tools/Common/JitInterface/CorInfoImpl_generated.cs
+++ b/src/coreclr/tools/Common/JitInterface/CorInfoImpl_generated.cs
@@ -2637,13 +2637,7 @@ private static uint _getJitFlags(IntPtr thisHandle, IntPtr* ppException, CORJIT_
 
         private static IntPtr GetUnmanagedCallbacks()
         {
-<<<<<<< HEAD
-            void** callbacks = (void**)Marshal.AllocCoTaskMem(sizeof(IntPtr) * 172);
-||||||| 2f900385db3
-            void** callbacks = (void**)Marshal.AllocCoTaskMem(sizeof(IntPtr) * 170);
-=======
-            void** callbacks = (void**)Marshal.AllocCoTaskMem(sizeof(IntPtr) * 176);
->>>>>>> upstream-main/main
+            void** callbacks = (void**)Marshal.AllocCoTaskMem(sizeof(IntPtr) * 178);
 
             callbacks[0] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_METHOD_STRUCT_*, byte>)&_isIntrinsic;
             callbacks[1] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_METHOD_STRUCT_*, byte>)&_notifyMethodInfoUsage;
@@ -2763,64 +2757,66 @@ private static IntPtr GetUnmanagedCallbacks()
             callbacks[115] = (delegate* unmanaged<IntPtr, IntPtr*, void*, void*, byte>)&_runWithErrorTrap;
             callbacks[116] = (delegate* unmanaged<IntPtr, IntPtr*, void*, void*, byte>)&_runWithSPMIErrorTrap;
             callbacks[117] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_EE_INFO*, void>)&_getEEInfo;
-            callbacks[118] = (delegate* unmanaged<IntPtr, IntPtr*, char*>)&_getJitTimeLogFilename;
-            callbacks[119] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_METHOD_STRUCT_*, mdToken>)&_getMethodDefFromMethod;
-            callbacks[120] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_METHOD_STRUCT_*, byte*, UIntPtr, UIntPtr*, UIntPtr>)&_printMethodName;
-            callbacks[121] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_METHOD_STRUCT_*, byte**, byte**, byte**, UIntPtr, byte*>)&_getMethodNameFromMetadata;
-            callbacks[122] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_METHOD_STRUCT_*, uint>)&_getMethodHash;
-            callbacks[123] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_CLASS_STRUCT_*, SYSTEMV_AMD64_CORINFO_STRUCT_REG_PASSING_DESCRIPTOR*, byte>)&_getSystemVAmd64PassStructInRegisterDescriptor;
-            callbacks[124] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_CLASS_STRUCT_*, CORINFO_SWIFT_LOWERING*, void>)&_getSwiftLowering;
-            callbacks[125] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_CLASS_STRUCT_*, CORINFO_FPSTRUCT_LOWERING*, void>)&_getFpStructLowering;
-            callbacks[126] = (delegate* unmanaged<IntPtr, IntPtr*, void**, uint>)&_getThreadTLSIndex;
-            callbacks[127] = (delegate* unmanaged<IntPtr, IntPtr*, void**, int*>)&_getAddrOfCaptureThreadGlobal;
-            callbacks[128] = (delegate* unmanaged<IntPtr, IntPtr*, CorInfoHelpFunc, void**, void*>)&_getHelperFtn;
-            callbacks[129] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_METHOD_STRUCT_*, CORINFO_CONST_LOOKUP*, CORINFO_ACCESS_FLAGS, void>)&_getFunctionEntryPoint;
-            callbacks[130] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_METHOD_STRUCT_*, byte, CORINFO_CONST_LOOKUP*, void>)&_getFunctionFixedEntryPoint;
-            callbacks[131] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_METHOD_STRUCT_*, void**, void*>)&_getMethodSync;
-            callbacks[132] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_MODULE_STRUCT_*, CorInfoHelpFunc>)&_getLazyStringLiteralHelper;
-            callbacks[133] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_MODULE_STRUCT_*, void**, CORINFO_MODULE_STRUCT_*>)&_embedModuleHandle;
-            callbacks[134] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_CLASS_STRUCT_*, void**, CORINFO_CLASS_STRUCT_*>)&_embedClassHandle;
-            callbacks[135] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_METHOD_STRUCT_*, void**, CORINFO_METHOD_STRUCT_*>)&_embedMethodHandle;
-            callbacks[136] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_FIELD_STRUCT_*, void**, CORINFO_FIELD_STRUCT_*>)&_embedFieldHandle;
-            callbacks[137] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_RESOLVED_TOKEN*, byte, CORINFO_METHOD_STRUCT_*, CORINFO_GENERICHANDLE_RESULT*, void>)&_embedGenericHandle;
-            callbacks[138] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_METHOD_STRUCT_*, CORINFO_LOOKUP_KIND*, void>)&_getLocationOfThisType;
-            callbacks[139] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_METHOD_STRUCT_*, CORINFO_CONST_LOOKUP*, void>)&_getAddressOfPInvokeTarget;
-            callbacks[140] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_SIG_INFO*, void**, void*>)&_GetCookieForPInvokeCalliSig;
-            callbacks[141] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_SIG_INFO*, byte>)&_canGetCookieForPInvokeCalliSig;
-            callbacks[142] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_METHOD_STRUCT_*, CORINFO_JUST_MY_CODE_HANDLE_**, CORINFO_JUST_MY_CODE_HANDLE_*>)&_getJustMyCodeHandle;
-            callbacks[143] = (delegate* unmanaged<IntPtr, IntPtr*, bool*, void**, bool*, void>)&_GetProfilingHandle;
-            callbacks[144] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_RESOLVED_TOKEN*, CORINFO_RESOLVED_TOKEN*, CORINFO_METHOD_STRUCT_*, CORINFO_CALLINFO_FLAGS, CORINFO_CALL_INFO*, void>)&_getCallInfo;
-            callbacks[145] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_FIELD_STRUCT_*, byte*, int, int, byte, byte>)&_getStaticFieldContent;
-            callbacks[146] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_OBJECT_STRUCT_*, byte*, int, int, byte>)&_getObjectContent;
-            callbacks[147] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_FIELD_STRUCT_*, byte*, CORINFO_CLASS_STRUCT_*>)&_getStaticFieldCurrentClass;
-            callbacks[148] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_SIG_INFO*, void**, IntPtr>)&_getVarArgsHandle;
-            callbacks[149] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_SIG_INFO*, byte>)&_canGetVarArgsHandle;
-            callbacks[150] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_MODULE_STRUCT_*, mdToken, void**, InfoAccessType>)&_constructStringLiteral;
-            callbacks[151] = (delegate* unmanaged<IntPtr, IntPtr*, void**, InfoAccessType>)&_emptyStringLiteral;
-            callbacks[152] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_FIELD_STRUCT_*, void**, uint>)&_getFieldThreadLocalStoreID;
-            callbacks[153] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_METHOD_STRUCT_*, CORINFO_CLASS_STRUCT_*, CORINFO_METHOD_STRUCT_*, DelegateCtorArgs*, CORINFO_METHOD_STRUCT_*>)&_GetDelegateCtor;
-            callbacks[154] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_METHOD_STRUCT_*, void>)&_MethodCompileComplete;
-            callbacks[155] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_RESOLVED_TOKEN*, CORINFO_SIG_INFO*, CORINFO_GET_TAILCALL_HELPERS_FLAGS, CORINFO_TAILCALL_HELPERS*, byte>)&_getTailCallHelpers;
-            callbacks[156] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_RESOLVED_TOKEN*, byte, byte>)&_convertPInvokeCalliToCall;
-            callbacks[157] = (delegate* unmanaged<IntPtr, IntPtr*, InstructionSet, byte, byte>)&_notifyInstructionSetUsage;
-            callbacks[158] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_CONST_LOOKUP*, void>)&_updateEntryPointForTailCall;
-            callbacks[159] = (delegate* unmanaged<IntPtr, IntPtr*, AllocMemArgs*, void>)&_allocMem;
-            callbacks[160] = (delegate* unmanaged<IntPtr, IntPtr*, byte, byte, uint, void>)&_reserveUnwindInfo;
-            callbacks[161] = (delegate* unmanaged<IntPtr, IntPtr*, byte*, byte*, uint, uint, uint, byte*, CorJitFuncKind, void>)&_allocUnwindInfo;
-            callbacks[162] = (delegate* unmanaged<IntPtr, IntPtr*, UIntPtr, void*>)&_allocGCInfo;
-            callbacks[163] = (delegate* unmanaged<IntPtr, IntPtr*, uint, void>)&_setEHcount;
-            callbacks[164] = (delegate* unmanaged<IntPtr, IntPtr*, uint, CORINFO_EH_CLAUSE*, void>)&_setEHinfo;
-            callbacks[165] = (delegate* unmanaged<IntPtr, IntPtr*, uint, byte*, IntPtr, byte>)&_logMsg;
-            callbacks[166] = (delegate* unmanaged<IntPtr, IntPtr*, byte*, int, byte*, int>)&_doAssert;
-            callbacks[167] = (delegate* unmanaged<IntPtr, IntPtr*, CorJitResult, void>)&_reportFatalError;
-            callbacks[168] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_METHOD_STRUCT_*, PgoInstrumentationSchema**, uint*, byte**, PgoSource*, bool*, HRESULT>)&_getPgoInstrumentationResults;
-            callbacks[169] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_METHOD_STRUCT_*, PgoInstrumentationSchema*, uint, byte**, HRESULT>)&_allocPgoInstrumentationBySchema;
-            callbacks[170] = (delegate* unmanaged<IntPtr, IntPtr*, uint, CORINFO_SIG_INFO*, CORINFO_METHOD_STRUCT_*, void>)&_recordCallSite;
-            callbacks[171] = (delegate* unmanaged<IntPtr, IntPtr*, void*, void*, void*, ushort, int, void>)&_recordRelocation;
-            callbacks[172] = (delegate* unmanaged<IntPtr, IntPtr*, void*, ushort>)&_getRelocTypeHint;
-            callbacks[173] = (delegate* unmanaged<IntPtr, IntPtr*, uint>)&_getExpectedTargetArchitecture;
-            callbacks[174] = (delegate* unmanaged<IntPtr, IntPtr*, CORJIT_FLAGS*, uint, uint>)&_getJitFlags;
-            callbacks[175] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_CLASS_STRUCT_*, CORINFO_METHOD_STRUCT_*>)&_getSpecialCopyHelper;
+            callbacks[118] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_ASYNC2_INFO*, void>)&_getAsync2Info;
+            callbacks[119] = (delegate* unmanaged<IntPtr, IntPtr*, char*>)&_getJitTimeLogFilename;
+            callbacks[120] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_METHOD_STRUCT_*, mdToken>)&_getMethodDefFromMethod;
+            callbacks[121] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_METHOD_STRUCT_*, byte*, UIntPtr, UIntPtr*, UIntPtr>)&_printMethodName;
+            callbacks[122] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_METHOD_STRUCT_*, byte**, byte**, byte**, UIntPtr, byte*>)&_getMethodNameFromMetadata;
+            callbacks[123] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_METHOD_STRUCT_*, uint>)&_getMethodHash;
+            callbacks[124] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_CLASS_STRUCT_*, SYSTEMV_AMD64_CORINFO_STRUCT_REG_PASSING_DESCRIPTOR*, byte>)&_getSystemVAmd64PassStructInRegisterDescriptor;
+            callbacks[125] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_CLASS_STRUCT_*, CORINFO_SWIFT_LOWERING*, void>)&_getSwiftLowering;
+            callbacks[126] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_CLASS_STRUCT_*, CORINFO_FPSTRUCT_LOWERING*, void>)&_getFpStructLowering;
+            callbacks[127] = (delegate* unmanaged<IntPtr, IntPtr*, void**, uint>)&_getThreadTLSIndex;
+            callbacks[128] = (delegate* unmanaged<IntPtr, IntPtr*, void**, int*>)&_getAddrOfCaptureThreadGlobal;
+            callbacks[129] = (delegate* unmanaged<IntPtr, IntPtr*, CorInfoHelpFunc, void**, void*>)&_getHelperFtn;
+            callbacks[130] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_METHOD_STRUCT_*, CORINFO_CONST_LOOKUP*, CORINFO_ACCESS_FLAGS, void>)&_getFunctionEntryPoint;
+            callbacks[131] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_METHOD_STRUCT_*, byte, CORINFO_CONST_LOOKUP*, void>)&_getFunctionFixedEntryPoint;
+            callbacks[132] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_METHOD_STRUCT_*, void**, void*>)&_getMethodSync;
+            callbacks[133] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_MODULE_STRUCT_*, CorInfoHelpFunc>)&_getLazyStringLiteralHelper;
+            callbacks[134] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_MODULE_STRUCT_*, void**, CORINFO_MODULE_STRUCT_*>)&_embedModuleHandle;
+            callbacks[135] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_CLASS_STRUCT_*, void**, CORINFO_CLASS_STRUCT_*>)&_embedClassHandle;
+            callbacks[136] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_METHOD_STRUCT_*, void**, CORINFO_METHOD_STRUCT_*>)&_embedMethodHandle;
+            callbacks[137] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_FIELD_STRUCT_*, void**, CORINFO_FIELD_STRUCT_*>)&_embedFieldHandle;
+            callbacks[138] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_RESOLVED_TOKEN*, byte, CORINFO_METHOD_STRUCT_*, CORINFO_GENERICHANDLE_RESULT*, void>)&_embedGenericHandle;
+            callbacks[139] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_METHOD_STRUCT_*, CORINFO_LOOKUP_KIND*, void>)&_getLocationOfThisType;
+            callbacks[140] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_METHOD_STRUCT_*, CORINFO_CONST_LOOKUP*, void>)&_getAddressOfPInvokeTarget;
+            callbacks[141] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_SIG_INFO*, void**, void*>)&_GetCookieForPInvokeCalliSig;
+            callbacks[142] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_SIG_INFO*, byte>)&_canGetCookieForPInvokeCalliSig;
+            callbacks[143] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_METHOD_STRUCT_*, CORINFO_JUST_MY_CODE_HANDLE_**, CORINFO_JUST_MY_CODE_HANDLE_*>)&_getJustMyCodeHandle;
+            callbacks[144] = (delegate* unmanaged<IntPtr, IntPtr*, bool*, void**, bool*, void>)&_GetProfilingHandle;
+            callbacks[145] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_RESOLVED_TOKEN*, CORINFO_RESOLVED_TOKEN*, CORINFO_METHOD_STRUCT_*, CORINFO_CALLINFO_FLAGS, CORINFO_CALL_INFO*, void>)&_getCallInfo;
+            callbacks[146] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_FIELD_STRUCT_*, byte*, int, int, byte, byte>)&_getStaticFieldContent;
+            callbacks[147] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_OBJECT_STRUCT_*, byte*, int, int, byte>)&_getObjectContent;
+            callbacks[148] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_FIELD_STRUCT_*, byte*, CORINFO_CLASS_STRUCT_*>)&_getStaticFieldCurrentClass;
+            callbacks[149] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_SIG_INFO*, void**, IntPtr>)&_getVarArgsHandle;
+            callbacks[150] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_SIG_INFO*, byte>)&_canGetVarArgsHandle;
+            callbacks[151] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_MODULE_STRUCT_*, mdToken, void**, InfoAccessType>)&_constructStringLiteral;
+            callbacks[152] = (delegate* unmanaged<IntPtr, IntPtr*, void**, InfoAccessType>)&_emptyStringLiteral;
+            callbacks[153] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_FIELD_STRUCT_*, void**, uint>)&_getFieldThreadLocalStoreID;
+            callbacks[154] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_METHOD_STRUCT_*, CORINFO_CLASS_STRUCT_*, CORINFO_METHOD_STRUCT_*, DelegateCtorArgs*, CORINFO_METHOD_STRUCT_*>)&_GetDelegateCtor;
+            callbacks[155] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_METHOD_STRUCT_*, void>)&_MethodCompileComplete;
+            callbacks[156] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_RESOLVED_TOKEN*, CORINFO_SIG_INFO*, CORINFO_GET_TAILCALL_HELPERS_FLAGS, CORINFO_TAILCALL_HELPERS*, byte>)&_getTailCallHelpers;
+            callbacks[157] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_METHOD_STRUCT_*>)&_getAsyncResumptionStub;
+            callbacks[158] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_RESOLVED_TOKEN*, byte, byte>)&_convertPInvokeCalliToCall;
+            callbacks[159] = (delegate* unmanaged<IntPtr, IntPtr*, InstructionSet, byte, byte>)&_notifyInstructionSetUsage;
+            callbacks[160] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_CONST_LOOKUP*, void>)&_updateEntryPointForTailCall;
+            callbacks[161] = (delegate* unmanaged<IntPtr, IntPtr*, AllocMemArgs*, void>)&_allocMem;
+            callbacks[162] = (delegate* unmanaged<IntPtr, IntPtr*, byte, byte, uint, void>)&_reserveUnwindInfo;
+            callbacks[163] = (delegate* unmanaged<IntPtr, IntPtr*, byte*, byte*, uint, uint, uint, byte*, CorJitFuncKind, void>)&_allocUnwindInfo;
+            callbacks[164] = (delegate* unmanaged<IntPtr, IntPtr*, UIntPtr, void*>)&_allocGCInfo;
+            callbacks[165] = (delegate* unmanaged<IntPtr, IntPtr*, uint, void>)&_setEHcount;
+            callbacks[166] = (delegate* unmanaged<IntPtr, IntPtr*, uint, CORINFO_EH_CLAUSE*, void>)&_setEHinfo;
+            callbacks[167] = (delegate* unmanaged<IntPtr, IntPtr*, uint, byte*, IntPtr, byte>)&_logMsg;
+            callbacks[168] = (delegate* unmanaged<IntPtr, IntPtr*, byte*, int, byte*, int>)&_doAssert;
+            callbacks[169] = (delegate* unmanaged<IntPtr, IntPtr*, CorJitResult, void>)&_reportFatalError;
+            callbacks[170] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_METHOD_STRUCT_*, PgoInstrumentationSchema**, uint*, byte**, PgoSource*, bool*, HRESULT>)&_getPgoInstrumentationResults;
+            callbacks[171] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_METHOD_STRUCT_*, PgoInstrumentationSchema*, uint, byte**, HRESULT>)&_allocPgoInstrumentationBySchema;
+            callbacks[172] = (delegate* unmanaged<IntPtr, IntPtr*, uint, CORINFO_SIG_INFO*, CORINFO_METHOD_STRUCT_*, void>)&_recordCallSite;
+            callbacks[173] = (delegate* unmanaged<IntPtr, IntPtr*, void*, void*, void*, ushort, int, void>)&_recordRelocation;
+            callbacks[174] = (delegate* unmanaged<IntPtr, IntPtr*, void*, ushort>)&_getRelocTypeHint;
+            callbacks[175] = (delegate* unmanaged<IntPtr, IntPtr*, uint>)&_getExpectedTargetArchitecture;
+            callbacks[176] = (delegate* unmanaged<IntPtr, IntPtr*, CORJIT_FLAGS*, uint, uint>)&_getJitFlags;
+            callbacks[177] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_CLASS_STRUCT_*, CORINFO_METHOD_STRUCT_*>)&_getSpecialCopyHelper;
 
             return (IntPtr)callbacks;
         }

From b0fe4c59dac2695e5b67011e9d0ed047372a749b Mon Sep 17 00:00:00 2001
From: Jakob Botsch Nielsen <jakob.botsch.nielsen@gmail.com>
Date: Tue, 15 Oct 2024 15:28:32 +0200
Subject: [PATCH 104/203] Last VM fixes

---
 src/coreclr/inc/jithelpers.h         | 2 +-
 src/coreclr/vm/jithelpers.cpp        | 4 ++--
 src/coreclr/vm/jitinterface.cpp      | 2 +-
 src/coreclr/vm/runtimesuspension.cpp | 4 ++--
 4 files changed, 6 insertions(+), 6 deletions(-)

diff --git a/src/coreclr/inc/jithelpers.h b/src/coreclr/inc/jithelpers.h
index 4c4f9f93198171..6a8d383f45c8a4 100644
--- a/src/coreclr/inc/jithelpers.h
+++ b/src/coreclr/inc/jithelpers.h
@@ -338,7 +338,7 @@
     JITHELPER(CORINFO_HELP_DISPATCH_INDIRECT_CALL, NULL, METHOD__NIL)
 #endif
 
-    DYNAMICJITHELPER(CORINFO_HELP_ALLOC_CONTINUATION, NULL, CORINFO_HELP_SIG_REG_ONLY)
+    DYNAMICJITHELPER(CORINFO_HELP_ALLOC_CONTINUATION, NULL, METHOD__NIL)
     JITHELPER(CORINFO_HELP_RESUME_OSR, JIT_ResumeOSR, CORINFO_HELP_SIG_REG_ONLY)
 
 #undef JITHELPER
diff --git a/src/coreclr/vm/jithelpers.cpp b/src/coreclr/vm/jithelpers.cpp
index 5ca5261d671b31..d69ce1c4b885e1 100644
--- a/src/coreclr/vm/jithelpers.cpp
+++ b/src/coreclr/vm/jithelpers.cpp
@@ -3755,7 +3755,7 @@ void JIT_ResumeOSR(unsigned ilOffset)
     DWORD contextSize = 0;
     ULONG64 xStateCompactionMask = 0;
     DWORD contextFlags = CONTEXT_FULL;
-    if (Thread::AreCetShadowStacksEnabled())
+    if (Thread::AreShadowStacksEnabled())
     {
         xStateCompactionMask = XSTATE_MASK_CET_U;
         contextFlags |= CONTEXT_XSTATE;
@@ -3783,7 +3783,7 @@ void JIT_ResumeOSR(unsigned ilOffset)
     RtlCaptureContext(pFrameContext);
 
 #if defined(TARGET_WINDOWS) && defined(TARGET_AMD64)
-    if (Thread::AreCetShadowStacksEnabled())
+    if (Thread::AreShadowStacksEnabled())
     {
         pFrameContext->ContextFlags |= CONTEXT_XSTATE;
         SetXStateFeaturesMask(pFrameContext, xStateCompactionMask);
diff --git a/src/coreclr/vm/jitinterface.cpp b/src/coreclr/vm/jitinterface.cpp
index 323a9c92bb8b58..a329e2d3328702 100644
--- a/src/coreclr/vm/jitinterface.cpp
+++ b/src/coreclr/vm/jitinterface.cpp
@@ -14576,7 +14576,7 @@ CORINFO_METHOD_HANDLE CEEJitInfo::getAsyncResumptionStub()
 
         // Result is placed in GCData[0] if it has GC references (potentially boxing it).
         bool isOrContainsGCPointers = false;
-        if (CorTypeInfo::IsObjRef(resultTypeHnd.GetInternalCorElementType()) || (resultTypeHnd.IsValueType() && resultTypeHnd.AsMethodTable()->ContainsPointers()))
+        if (CorTypeInfo::IsObjRef(resultTypeHnd.GetInternalCorElementType()) || (resultTypeHnd.IsValueType() && resultTypeHnd.AsMethodTable()->ContainsGCPointers()))
         {
             // Load 'gcdata' of next continuation
             pCode->EmitLDFLD(FIELD__CONTINUATION__GCDATA);
diff --git a/src/coreclr/vm/runtimesuspension.cpp b/src/coreclr/vm/runtimesuspension.cpp
index da440b97d013f2..73ae38663dd2a4 100644
--- a/src/coreclr/vm/runtimesuspension.cpp
+++ b/src/coreclr/vm/runtimesuspension.cpp
@@ -353,7 +353,7 @@ StackWalkAction CaptureTaskletsCore(CrawlFrame* pCf, VOID* data)
     uint8_t* pTopOfStackInFunction = (uint8_t*)pCf->GetRegisterSet()->SP;
     uint32_t sizeofArgStack = (uint32_t)pCf->GetFunction()->SizeOfArgStack();
 #ifdef TARGET_AMD64  // AND Target windows
-    sizeofArgStack = max(sizeofArgStack, 32); // On Windows X64 there is always a parameter area of 32 bytes, which is for use by the called function
+    sizeofArgStack = std::max(sizeofArgStack, 32u); // On Windows X64 there is always a parameter area of 32 bytes, which is for use by the called function
 #endif
     uint8_t* pBottomOfStackInFunction = (uint8_t*)::GetSP(pCf->GetRegisterSet()->pCallerContext) + sizeofArgStack;
     uint32_t sizeofEntireMeaningfulStack = (uint32_t)(pBottomOfStackInFunction - pTopOfStackInFunction);
@@ -515,7 +515,7 @@ StackWalkAction CaptureTaskletsCore(CrawlFrame* pCf, VOID* data)
         }
         else
         {
-            if (thRet.AsMethodTable()->IsValueType() && thRet.AsMethodTable()->ContainsPointers())
+            if (thRet.AsMethodTable()->IsValueType() && thRet.AsMethodTable()->ContainsGCPointers())
             {
                 taskletReturnType = TaskletReturnType::ObjectReference;
             }

From 07e8e59b2ac0299113a718ac5073ca9428c0ac79 Mon Sep 17 00:00:00 2001
From: Jakob Botsch Nielsen <jakob.botsch.nielsen@gmail.com>
Date: Tue, 15 Oct 2024 15:28:43 +0200
Subject: [PATCH 105/203] Run API compat suppression generator

---
 .../CompatibilitySuppressions.xml             | 28 ++++++++++
 .../src/CompatibilitySuppressions.xml         | 52 +++++++++++++------
 .../src/CompatibilitySuppressions.xml         |  2 +-
 3 files changed, 65 insertions(+), 17 deletions(-)
 create mode 100644 src/coreclr/System.Private.CoreLib/CompatibilitySuppressions.xml

diff --git a/src/coreclr/System.Private.CoreLib/CompatibilitySuppressions.xml b/src/coreclr/System.Private.CoreLib/CompatibilitySuppressions.xml
new file mode 100644
index 00000000000000..e13316ffc3cfb7
--- /dev/null
+++ b/src/coreclr/System.Private.CoreLib/CompatibilitySuppressions.xml
@@ -0,0 +1,28 @@
+﻿<?xml version="1.0" encoding="utf-8"?>
+<!-- https://learn.microsoft.com/dotnet/fundamentals/package-validation/diagnostic-ids -->
+<Suppressions xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
+  <Suppression>
+    <DiagnosticId>CP0001</DiagnosticId>
+    <Target>T:Internal.Console</Target>
+  </Suppression>
+  <Suppression>
+    <DiagnosticId>CP0002</DiagnosticId>
+    <Target>F:System.Resources.ResourceManager.BaseNameField</Target>
+  </Suppression>
+  <Suppression>
+    <DiagnosticId>CP0002</DiagnosticId>
+    <Target>F:System.Resources.ResourceSet.Reader</Target>
+  </Suppression>
+  <Suppression>
+    <DiagnosticId>CP0002</DiagnosticId>
+    <Target>M:System.String.Trim(System.ReadOnlySpan{System.Char})</Target>
+  </Suppression>
+  <Suppression>
+    <DiagnosticId>CP0002</DiagnosticId>
+    <Target>M:System.String.TrimEnd(System.ReadOnlySpan{System.Char})</Target>
+  </Suppression>
+  <Suppression>
+    <DiagnosticId>CP0002</DiagnosticId>
+    <Target>M:System.String.TrimStart(System.ReadOnlySpan{System.Char})</Target>
+  </Suppression>
+</Suppressions>
\ No newline at end of file
diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/CompatibilitySuppressions.xml b/src/coreclr/nativeaot/System.Private.CoreLib/src/CompatibilitySuppressions.xml
index eb99165343de1f..da5b1f8774ea91 100644
--- a/src/coreclr/nativeaot/System.Private.CoreLib/src/CompatibilitySuppressions.xml
+++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/CompatibilitySuppressions.xml
@@ -57,14 +57,6 @@
     <DiagnosticId>CP0001</DiagnosticId>
     <Target>T:Internal.Metadata.NativeFormat.ConstantBooleanValueHandle</Target>
   </Suppression>
-  <Suppression>
-    <DiagnosticId>CP0001</DiagnosticId>
-    <Target>T:Internal.Metadata.NativeFormat.ConstantEnumValue</Target>
-  </Suppression>
-  <Suppression>
-    <DiagnosticId>CP0001</DiagnosticId>
-    <Target>T:Internal.Metadata.NativeFormat.ConstantEnumValueHandle</Target>
-  </Suppression>
   <Suppression>
     <DiagnosticId>CP0001</DiagnosticId>
     <Target>T:Internal.Metadata.NativeFormat.ConstantByteArray</Target>
@@ -121,6 +113,14 @@
     <DiagnosticId>CP0001</DiagnosticId>
     <Target>T:Internal.Metadata.NativeFormat.ConstantEnumArrayHandle</Target>
   </Suppression>
+  <Suppression>
+    <DiagnosticId>CP0001</DiagnosticId>
+    <Target>T:Internal.Metadata.NativeFormat.ConstantEnumValue</Target>
+  </Suppression>
+  <Suppression>
+    <DiagnosticId>CP0001</DiagnosticId>
+    <Target>T:Internal.Metadata.NativeFormat.ConstantEnumValueHandle</Target>
+  </Suppression>
   <Suppression>
     <DiagnosticId>CP0001</DiagnosticId>
     <Target>T:Internal.Metadata.NativeFormat.ConstantHandleArray</Target>
@@ -753,6 +753,10 @@
     <DiagnosticId>CP0001</DiagnosticId>
     <Target>T:Internal.TypeSystem.LockFreeReaderHashtable`2</Target>
   </Suppression>
+  <Suppression>
+    <DiagnosticId>CP0001</DiagnosticId>
+    <Target>T:Internal.TypeSystem.LockFreeReaderHashtableOfPointers`2</Target>
+  </Suppression>
   <Suppression>
     <DiagnosticId>CP0001</DiagnosticId>
     <Target>T:System.Diagnostics.DebugAnnotations</Target>
@@ -830,8 +834,16 @@
     <Target>T:System.Runtime.CompilerServices.StaticClassConstructionContext</Target>
   </Suppression>
   <Suppression>
-    <DiagnosticId>CP0001</DiagnosticId>
-    <Target>T:Internal.TypeSystem.LockFreeReaderHashtableOfPointers`2</Target>
+    <DiagnosticId>CP0002</DiagnosticId>
+    <Target>F:System.Resources.ResourceManager.BaseNameField</Target>
+  </Suppression>
+  <Suppression>
+    <DiagnosticId>CP0002</DiagnosticId>
+    <Target>F:System.Resources.ResourceSet.Reader</Target>
+  </Suppression>
+  <Suppression>
+    <DiagnosticId>CP0002</DiagnosticId>
+    <Target>M:System.Diagnostics.DiagnosticMethodInfo.#ctor(System.String,System.String,System.String)</Target>
   </Suppression>
   <Suppression>
     <DiagnosticId>CP0002</DiagnosticId>
@@ -839,22 +851,30 @@
   </Suppression>
   <Suppression>
     <DiagnosticId>CP0002</DiagnosticId>
-    <Target>M:System.Threading.Lock.#ctor(System.Boolean)</Target>
+    <Target>M:System.String.Trim(System.ReadOnlySpan{System.Char})</Target>
   </Suppression>
   <Suppression>
     <DiagnosticId>CP0002</DiagnosticId>
-    <Target>M:System.Diagnostics.DiagnosticMethodInfo.#ctor(System.String,System.String,System.String)</Target>
+    <Target>M:System.String.TrimEnd(System.ReadOnlySpan{System.Char})</Target>
+  </Suppression>
+  <Suppression>
+    <DiagnosticId>CP0002</DiagnosticId>
+    <Target>M:System.String.TrimStart(System.ReadOnlySpan{System.Char})</Target>
+  </Suppression>
+  <Suppression>
+    <DiagnosticId>CP0002</DiagnosticId>
+    <Target>M:System.Threading.Lock.#ctor(System.Boolean)</Target>
   </Suppression>
   <Suppression>
     <DiagnosticId>CP0002</DiagnosticId>
     <Target>M:System.Runtime.CompilerServices.RuntimeHelpers.AwaitAwaiterFromRuntimeAsync``1(``0)</Target>
-    <Left>ref/net9.0/System.Private.CoreLib.dll</Left>
-    <Right>lib/net9.0/System.Private.CoreLib.dll</Right>
+    <Left>ref/net10.0/System.Private.CoreLib.dll</Left>
+    <Right>lib/net10.0/System.Private.CoreLib.dll</Right>
   </Suppression>
   <Suppression>
     <DiagnosticId>CP0002</DiagnosticId>
     <Target>M:System.Runtime.CompilerServices.RuntimeHelpers.UnsafeAwaitAwaiterFromRuntimeAsync``1(``0)</Target>
-    <Left>ref/net9.0/System.Private.CoreLib.dll</Left>
-    <Right>lib/net9.0/System.Private.CoreLib.dll</Right>
+    <Left>ref/net10.0/System.Private.CoreLib.dll</Left>
+    <Right>lib/net10.0/System.Private.CoreLib.dll</Right>
   </Suppression>
 </Suppressions>
\ No newline at end of file
diff --git a/src/libraries/System.Collections.Specialized/src/CompatibilitySuppressions.xml b/src/libraries/System.Collections.Specialized/src/CompatibilitySuppressions.xml
index a941952fee7773..67ada8e90c3092 100644
--- a/src/libraries/System.Collections.Specialized/src/CompatibilitySuppressions.xml
+++ b/src/libraries/System.Collections.Specialized/src/CompatibilitySuppressions.xml
@@ -1,6 +1,6 @@
 ﻿<?xml version="1.0" encoding="utf-8"?>
+<!-- https://learn.microsoft.com/dotnet/fundamentals/package-validation/diagnostic-ids -->
 <Suppressions xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
-  <!-- Type exposed publicly in implementation only for serialization purposes. -->
   <Suppression>
     <DiagnosticId>CP0001</DiagnosticId>
     <Target>T:System.Collections.Specialized.ListDictionary.DictionaryNode</Target>

From badb398b2d7056557dd7e58b628c1c78ee60ed84 Mon Sep 17 00:00:00 2001
From: vsadov <8218165+VSadov@users.noreply.github.com>
Date: Wed, 16 Oct 2024 23:16:08 -0700
Subject: [PATCH 106/203] point to rebased Roslyn branch

---
 buildroslynnugets.cmd | 16 ++++++++--------
 eng/Versions.props    | 11 +++--------
 2 files changed, 11 insertions(+), 16 deletions(-)

diff --git a/buildroslynnugets.cmd b/buildroslynnugets.cmd
index b91c2c3160a70c..5e9564528653cc 100644
--- a/buildroslynnugets.cmd
+++ b/buildroslynnugets.cmd
@@ -1,8 +1,8 @@
 setlocal ENABLEEXTENSIONS
 pushd %~dp0
-set ASYNC_ROSLYN_COMMIT=4d45a5cfed3654d4d5a079908e3796be6665b18c
-set ASYNC_SUFFIX=async-8
-set ASYNC_ROSLYN_BRANCH=demos/async2-experiment
+set ASYNC_ROSLYN_COMMIT=8bb20e76b6d7e041a2cd0a3152d95fdff6578050
+set ASYNC_SUFFIX=async-9
+set ASYNC_ROSLYN_BRANCH=demos/async2-experiment1
 
 cd ..
 if not exist async-roslyn-repo git clone -b %ASYNC_ROSLYN_BRANCH% -o async_roslyn_remote https://github.com/dotnet/roslyn.git async-roslyn-repo
@@ -26,9 +26,9 @@ pushd %~dp0
 
 md roslynpackages
 
-copy ..\async-roslyn-repo\artifacts\packages\Release\Shipping\Microsoft.Net.Compilers.Toolset.4.9.0-%ASYNC_SUFFIX%.nupkg roslynpackages
-copy ..\async-roslyn-repo\artifacts\packages\Release\Shipping\Microsoft.CodeAnalysis.Workspaces.Common.4.9.0-%ASYNC_SUFFIX%.nupkg roslynpackages
-copy ..\async-roslyn-repo\artifacts\packages\Release\Shipping\Microsoft.CodeAnalysis.CSharp.Workspaces.4.9.0-%ASYNC_SUFFIX%.nupkg roslynpackages
-copy ..\async-roslyn-repo\artifacts\packages\Release\Shipping\Microsoft.CodeAnalysis.CSharp.4.9.0-%ASYNC_SUFFIX%.nupkg roslynpackages
-copy ..\async-roslyn-repo\artifacts\packages\Release\Shipping\Microsoft.CodeAnalysis.Common.4.9.0-%ASYNC_SUFFIX%.nupkg roslynpackages
+copy ..\async-roslyn-repo\artifacts\packages\Release\Shipping\Microsoft.Net.Compilers.Toolset.4.13.0-%ASYNC_SUFFIX%.nupkg roslynpackages
+copy ..\async-roslyn-repo\artifacts\packages\Release\Shipping\Microsoft.CodeAnalysis.Workspaces.Common.4.13.0-%ASYNC_SUFFIX%.nupkg roslynpackages
+copy ..\async-roslyn-repo\artifacts\packages\Release\Shipping\Microsoft.CodeAnalysis.CSharp.Workspaces.4.13.0-%ASYNC_SUFFIX%.nupkg roslynpackages
+copy ..\async-roslyn-repo\artifacts\packages\Release\Shipping\Microsoft.CodeAnalysis.CSharp.4.13.0-%ASYNC_SUFFIX%.nupkg roslynpackages
+copy ..\async-roslyn-repo\artifacts\packages\Release\Shipping\Microsoft.CodeAnalysis.Common.4.13.0-%ASYNC_SUFFIX%.nupkg roslynpackages
 
diff --git a/eng/Versions.props b/eng/Versions.props
index 6e16489fec57ce..2d051be477e44d 100644
--- a/eng/Versions.props
+++ b/eng/Versions.props
@@ -43,14 +43,9 @@
       Any tools that contribute to the design-time experience should use the MicrosoftCodeAnalysisVersion_LatestVS property above to ensure
       they do not break the local dev experience.
     -->
-    <MicrosoftCodeAnalysisCSharpVersion>4.12.0-3.24476.1</MicrosoftCodeAnalysisCSharpVersion>
-    <MicrosoftCodeAnalysisVersion>4.12.0-3.24476.1</MicrosoftCodeAnalysisVersion>
-    <MicrosoftNetCompilersToolsetVersion>4.12.0-3.24476.1</MicrosoftNetCompilersToolsetVersion>
-    <!--
-    <MicrosoftCodeAnalysisCSharpVersion>4.9.0-async-8</MicrosoftCodeAnalysisCSharpVersion>
-    <MicrosoftCodeAnalysisVersion>4.9.0-async-8</MicrosoftCodeAnalysisVersion>
-    <MicrosoftNetCompilersToolsetVersion>4.9.0-async-8</MicrosoftNetCompilersToolsetVersion>
-    -->
+    <MicrosoftCodeAnalysisCSharpVersion>4.13.0-async-9</MicrosoftCodeAnalysisCSharpVersion>
+    <MicrosoftCodeAnalysisVersion>4.13.0-async-9</MicrosoftCodeAnalysisVersion>
+    <MicrosoftNetCompilersToolsetVersion>4.13.0-async-9</MicrosoftNetCompilersToolsetVersion>
   </PropertyGroup>
   <!--
     For source generator support we need to target multiple versions of Roslyn in order to be able to run on older versions of Roslyn.

From 6996857d915d0678274fc9f6421c8ae72739930d Mon Sep 17 00:00:00 2001
From: vsadov <8218165+VSadov@users.noreply.github.com>
Date: Wed, 16 Oct 2024 23:16:21 -0700
Subject: [PATCH 107/203] fix the build

---
 .../src/System/Net/Http/Headers/HttpHeaders.cs             | 4 ++++
 .../System/Numerics/Tensors/netcore/TensorExtensions.cs    | 2 +-
 .../src/System/Runtime/CompilerServices/QCallHandles.cs    | 7 ++++++-
 3 files changed, 11 insertions(+), 2 deletions(-)

diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/Headers/HttpHeaders.cs b/src/libraries/System.Net.Http/src/System/Net/Http/Headers/HttpHeaders.cs
index 03b5934ea38dc1..fd9ba89e184333 100644
--- a/src/libraries/System.Net.Http/src/System/Net/Http/Headers/HttpHeaders.cs
+++ b/src/libraries/System.Net.Http/src/System/Net/Http/Headers/HttpHeaders.cs
@@ -1165,7 +1165,9 @@ internal static void GetStoreValuesAsStringOrStringArray(HeaderDescriptor descri
                 else
                 {
                     Debug.Assert(length > 1, "The header should have been removed when it became empty");
+#pragma warning disable 8619 // Nullability of reference types ...  does not match
                     values = multiValue = new string[length];
+#pragma warning restore 8619
                 }
 
                 int currentIndex = 0;
@@ -1205,8 +1207,10 @@ internal static int GetStoreValuesIntoStringArray(HeaderDescriptor descriptor, o
                 }
 
                 int currentIndex = 0;
+#pragma warning disable 8620 // Argument of type 'string[]' cannot be used for parameter 'values'
                 ReadStoreValues<object?>(values, info.ParsedAndInvalidValues, descriptor.Parser, ref currentIndex);
                 ReadStoreValues<string?>(values, info.RawValue, null, ref currentIndex);
+#pragma warning restore 8620
                 Debug.Assert(currentIndex == length);
 
                 return length;
diff --git a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorExtensions.cs b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorExtensions.cs
index 42f6a4319b2044..a366c34e937217 100644
--- a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorExtensions.cs
+++ b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorExtensions.cs
@@ -2658,7 +2658,7 @@ public static Tensor<T> PermuteDimensions<T>(this Tensor<T> tensor, params ReadO
 
                 if (dimensions.IsEmpty)
                 {
-                    lengths = tensor._lengths.Reverse().ToArray();
+                    lengths = ((IEnumerable<nint>)tensor._lengths).Reverse().ToArray();
                     permutation = Enumerable.Range(0, tensor.Rank).Reverse().ToArray();
                 }
                 else
diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/QCallHandles.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/QCallHandles.cs
index ea2e95f6d8b29d..d1f4d3a4b2397a 100644
--- a/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/QCallHandles.cs
+++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/QCallHandles.cs
@@ -34,9 +34,14 @@ internal static ObjectHandleOnStack Create<T>(ref T o) where T : class?
         }
     }
 
-    internal ref struct ByteRef
+    internal unsafe ref struct ByteRef
     {
         private ref byte _ref;
+        private ByteRef(ref byte byteRef)
+        {
+            _ref = ref byteRef;
+        }
+
         internal ref byte Get()
         {
             return ref _ref;

From 94d8614e000d299de03b0016b1e0e11cc4002d65 Mon Sep 17 00:00:00 2001
From: Jakob Botsch Nielsen <jakob.botsch.nielsen@gmail.com>
Date: Mon, 21 Oct 2024 11:09:16 +0200
Subject: [PATCH 108/203] Fix AllocContinuation

---
 src/coreclr/inc/jithelpers.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/coreclr/inc/jithelpers.h b/src/coreclr/inc/jithelpers.h
index 45d490bedfa72e..79f3f7a6bd3b75 100644
--- a/src/coreclr/inc/jithelpers.h
+++ b/src/coreclr/inc/jithelpers.h
@@ -342,7 +342,7 @@
     JITHELPER(CORINFO_HELP_DISPATCH_INDIRECT_CALL, NULL, METHOD__NIL)
 #endif
 
-    DYNAMICJITHELPER(CORINFO_HELP_ALLOC_CONTINUATION, NULL, METHOD__NIL)
+    DYNAMICJITHELPER(CORINFO_HELP_ALLOC_CONTINUATION, NULL, METHOD__RUNTIME_HELPERS__ALLOC_CONTINUATION)
     JITHELPER(CORINFO_HELP_RESUME_OSR, JIT_ResumeOSR, CORINFO_HELP_SIG_REG_ONLY)
 
 #undef JITHELPER

From 26924fa3965889d7971fbb5acbee7ad44eba99ba Mon Sep 17 00:00:00 2001
From: Jakob Botsch Nielsen <jakob.botsch.nielsen@gmail.com>
Date: Mon, 21 Oct 2024 11:19:10 +0200
Subject: [PATCH 109/203] Fix recognition of async2 RuntimeHelpers intrinsics

---
 src/coreclr/jit/importercalls.cpp | 36 +++++++++++++++----------------
 1 file changed, 18 insertions(+), 18 deletions(-)

diff --git a/src/coreclr/jit/importercalls.cpp b/src/coreclr/jit/importercalls.cpp
index 1fae2ddbff7795..eb0d36ca1bf0dc 100644
--- a/src/coreclr/jit/importercalls.cpp
+++ b/src/coreclr/jit/importercalls.cpp
@@ -10643,6 +10643,24 @@ NamedIntrinsic Compiler::lookupNamedIntrinsic(CORINFO_METHOD_HANDLE method)
                             {
                                 result = NI_System_Runtime_CompilerServices_RuntimeHelpers_GetMethodTable;
                             }
+                            else if (strcmp(methodName, "AwaitAwaiterFromRuntimeAsync") == 0)
+                            {
+                                result = NI_System_Runtime_CompilerServices_RuntimeHelpers_AwaitAwaiterFromRuntimeAsync;
+                            }
+                            else if (strcmp(methodName, "UnsafeAwaitAwaiterFromRuntimeAsync") == 0)
+                            {
+                                result =
+                                    NI_System_Runtime_CompilerServices_RuntimeHelpers_UnsafeAwaitAwaiterFromRuntimeAsync;
+                            }
+                            else if (strcmp(methodName, "SuspendAsync2") == 0)
+                            {
+                                result = NI_System_Runtime_CompilerServices_RuntimeHelpers_SuspendAsync2;
+                            }
+                            else if (strcmp(methodName, "get_RuntimeAsyncViaJitGeneratedStateMachines") == 0)
+                            {
+                                result =
+                                    NI_System_Runtime_CompilerServices_RuntimeHelpers_get_RuntimeAsyncViaJitGeneratedStateMachines;
+                            }
                         }
                         else if (strcmp(className, "Unsafe") == 0)
                         {
@@ -10751,24 +10769,6 @@ NamedIntrinsic Compiler::lookupNamedIntrinsic(CORINFO_METHOD_HANDLE method)
                                 result = NI_SRCS_UNSAFE_WriteUnaligned;
                             }
                         }
-                        else if (strcmp(methodName, "AwaitAwaiterFromRuntimeAsync") == 0)
-                        {
-                            result = NI_System_Runtime_CompilerServices_RuntimeHelpers_AwaitAwaiterFromRuntimeAsync;
-                        }
-                        else if (strcmp(methodName, "UnsafeAwaitAwaiterFromRuntimeAsync") == 0)
-                        {
-                            result =
-                                NI_System_Runtime_CompilerServices_RuntimeHelpers_UnsafeAwaitAwaiterFromRuntimeAsync;
-                        }
-                        else if (strcmp(methodName, "SuspendAsync2") == 0)
-                        {
-                            result = NI_System_Runtime_CompilerServices_RuntimeHelpers_SuspendAsync2;
-                        }
-                        else if (strcmp(methodName, "get_RuntimeAsyncViaJitGeneratedStateMachines") == 0)
-                        {
-                            result =
-                                NI_System_Runtime_CompilerServices_RuntimeHelpers_get_RuntimeAsyncViaJitGeneratedStateMachines;
-                        }
                     }
                     else if (strcmp(namespaceName, "InteropServices") == 0)
                     {

From 1e6fabc2c77af7019f38b90fcfc7eb952eddfe8f Mon Sep 17 00:00:00 2001
From: Jakob Botsch Nielsen <jakob.botsch.nielsen@gmail.com>
Date: Mon, 21 Oct 2024 11:53:19 +0200
Subject: [PATCH 110/203] Set likelihoods in async transformation

---
 src/coreclr/jit/async.cpp | 32 ++++++++++++++++++++++++++------
 1 file changed, 26 insertions(+), 6 deletions(-)

diff --git a/src/coreclr/jit/async.cpp b/src/coreclr/jit/async.cpp
index 933d41fc75f9e7..00280c4c0dd51a 100644
--- a/src/coreclr/jit/async.cpp
+++ b/src/coreclr/jit/async.cpp
@@ -256,6 +256,10 @@ PhaseStatus Async2Transformation::Run()
     return PhaseStatus::MODIFIED_EVERYTHING;
 }
 
+const weight_t SUSPEND_LIKELIHOOD       = 0.01;
+const weight_t RETHROW_LIKELIHOOD       = 0.01;
+const weight_t RESUME_IN_OSR_LIKELIHOOD = 0.001;
+
 void Async2Transformation::Transform(
     BasicBlock* block, GenTreeCall* call, jitstd::vector<GenTree*>& defs, AsyncLiveness& life, BasicBlock** pRemainder)
 {
@@ -543,6 +547,9 @@ void Async2Transformation::Transform(
     FlowEdge* retBBEdge = m_comp->fgAddRefPred(retBB, block);
     block->SetCond(retBBEdge, block->GetTargetEdge());
 
+    block->GetTrueEdge()->setLikelihood(SUSPEND_LIKELIHOOD);
+    block->GetFalseEdge()->setLikelihood(1 - SUSPEND_LIKELIHOOD);
+
     // Allocate continuation
     returnedContinuation           = m_comp->gtNewLclvNode(m_returnedContinuationVar, TYP_REF);
     GenTree*     gcRefsCountNode   = m_comp->gtNewIconNode((ssize_t)gcRefsCount, TYP_I_IMPL);
@@ -902,6 +909,8 @@ void Async2Transformation::Transform(
             assert(resumeBB->KindIs(BBJ_ALWAYS));
 
             resumeBB->SetCond(rethrowEdge, storeResultEdge);
+            rethrowEdge->setLikelihood(RETHROW_LIKELIHOOD);
+            storeResultEdge->setLikelihood(1 - RETHROW_LIKELIHOOD);
             JITDUMP("  Resumption " FMT_BB " becomes BBJ_COND to check for non-null exception\n", resumeBB->bbNum);
 
             m_comp->fgRemoveRefPred(remainderEdge);
@@ -1227,12 +1236,13 @@ void Async2Transformation::CreateResumptionSwitch()
     GenTree* jtrue           = m_comp->gtNewOperNode(GT_JTRUE, TYP_VOID, neNull);
     LIR::AsRange(newEntryBB).InsertAtEnd(continuationArg, null, neNull, jtrue);
 
+    FlowEdge* resumingEdge;
+
     if (m_resumptionBBs.size() == 1)
     {
         JITDUMP("  Redirecting entry " FMT_BB " directly to " FMT_BB " as it is the only resumption block\n",
                 newEntryBB->bbNum, m_resumptionBBs[0]->bbNum);
-        FlowEdge* resumptionEdge = m_comp->fgAddRefPred(m_resumptionBBs[0], newEntryBB);
-        newEntryBB->SetCond(resumptionEdge, toPrevEntryEdge);
+        resumingEdge = m_comp->fgAddRefPred(m_resumptionBBs[0], newEntryBB);
     }
     else if (m_resumptionBBs.size() == 2)
     {
@@ -1242,9 +1252,10 @@ void Async2Transformation::CreateResumptionSwitch()
         FlowEdge* to0 = m_comp->fgAddRefPred(m_resumptionBBs[0], condBB);
         FlowEdge* to1 = m_comp->fgAddRefPred(m_resumptionBBs[1], condBB);
         condBB->SetCond(to0, to1);
+        to0->setLikelihood(0.5);
+        to1->setLikelihood(0.5);
 
-        FlowEdge* toCond = m_comp->fgAddRefPred(condBB, newEntryBB);
-        newEntryBB->SetCond(toCond, toPrevEntryEdge);
+        resumingEdge = m_comp->fgAddRefPred(condBB, newEntryBB);
 
         JITDUMP("  Redirecting entry " FMT_BB " to BBJ_COND " FMT_BB " for resumption with 2 states\n",
                 newEntryBB->bbNum, condBB->bbNum);
@@ -1266,8 +1277,7 @@ void Async2Transformation::CreateResumptionSwitch()
         BasicBlock* switchBB = m_comp->fgNewBBbefore(BBJ_SWITCH, m_resumptionBBs[0], true);
         switchBB->bbSetRunRarely();
 
-        FlowEdge* toSwitch = m_comp->fgAddRefPred(switchBB, newEntryBB);
-        newEntryBB->SetCond(toSwitch, toPrevEntryEdge);
+        resumingEdge = m_comp->fgAddRefPred(switchBB, newEntryBB);
 
         JITDUMP("  Redirecting entry " FMT_BB " to BBJ_SWITCH " FMT_BB " for resumption with %zu states\n",
                 newEntryBB->bbNum, switchBB->bbNum, m_resumptionBBs.size());
@@ -1290,14 +1300,20 @@ void Async2Transformation::CreateResumptionSwitch()
         swtDesc->bbsHasDefault = true;
         swtDesc->bbsDstTab = new (m_comp, CMK_Async2) FlowEdge*[m_resumptionBBs.size()];
 
+        weight_t stateLikelihood = 1.0 / m_resumptionBBs.size();
         for (size_t i = 0; i < m_resumptionBBs.size(); i++)
         {
             swtDesc->bbsDstTab[i] = m_comp->fgAddRefPred(m_resumptionBBs[i], switchBB);
+            swtDesc->bbsDstTab[i]->setLikelihood(stateLikelihood);
         }
 
         switchBB->SetSwitch(swtDesc);
     }
 
+    newEntryBB->SetCond(resumingEdge, toPrevEntryEdge);
+    resumingEdge->setLikelihood(SUSPEND_LIKELIHOOD);
+    toPrevEntryEdge->setLikelihood(1 - SUSPEND_LIKELIHOOD);
+
     if (m_comp->doesMethodHavePatchpoints())
     {
         JITDUMP("  Method has patch points...\n");
@@ -1324,6 +1340,8 @@ void Async2Transformation::CreateResumptionSwitch()
         FlowEdge* toOnContinuationBB = m_comp->fgAddRefPred(onContinuationBB, checkILOffsetBB);
         FlowEdge* toCallHelperBB = m_comp->fgAddRefPred(callHelperBB, checkILOffsetBB);
         checkILOffsetBB->SetCond(toCallHelperBB, toOnContinuationBB);
+        toCallHelperBB->setLikelihood(RESUME_IN_OSR_LIKELIHOOD);
+        toOnContinuationBB->setLikelihood(1 - RESUME_IN_OSR_LIKELIHOOD);
 
         // We need to dispatch to the OSR version if the IL offset is non-negative.
         continuationArg           = m_comp->gtNewLclvNode(m_comp->lvaAsyncContinuationArg, TYP_REF);
@@ -1374,6 +1392,8 @@ void Async2Transformation::CreateResumptionSwitch()
         FlowEdge* toOnContinuationBB = m_comp->fgAddRefPred(onContinuationBB, checkILOffsetBB);
         FlowEdge* toOnNoContinuationBB = m_comp->fgAddRefPred(onNoContinuationBB, checkILOffsetBB);
         checkILOffsetBB->SetCond(toOnNoContinuationBB, toOnContinuationBB);
+        toOnContinuationBB->setLikelihood(SUSPEND_LIKELIHOOD);
+        toOnNoContinuationBB->setLikelihood(1 - SUSPEND_LIKELIHOOD);
 
         JITDUMP("    Created " FMT_BB " to check for Tier-0 continuations\n", checkILOffsetBB->bbNum);
 

From b0f317983fd00fed91dc786655bdd5debabf4cc4 Mon Sep 17 00:00:00 2001
From: Jakob Botsch Nielsen <jakob.botsch.nielsen@gmail.com>
Date: Mon, 21 Oct 2024 11:53:46 +0200
Subject: [PATCH 111/203] Ensure async continuation is busy

---
 src/coreclr/jit/lsra.cpp        | 10 ++++----
 src/coreclr/jit/lsra.h          |  3 ++-
 src/coreclr/jit/lsraarmarch.cpp |  5 ++++
 src/coreclr/jit/lsrabuild.cpp   | 41 +++++++++++++++------------------
 src/coreclr/jit/lsraxarch.cpp   |  5 ++++
 5 files changed, 36 insertions(+), 28 deletions(-)

diff --git a/src/coreclr/jit/lsra.cpp b/src/coreclr/jit/lsra.cpp
index e1449766cf406e..2c3301913dfc9e 100644
--- a/src/coreclr/jit/lsra.cpp
+++ b/src/coreclr/jit/lsra.cpp
@@ -3855,6 +3855,11 @@ void LinearScan::processKills(RefPosition* killRefPosition)
     regsBusyUntilKill &= ~killRefPosition->getKilledRegisters();
     INDEBUG(dumpLsraAllocationEvent(LSRA_EVENT_KILL_REGS, nullptr, REG_NA, nullptr, NONE,
                                     killRefPosition->getKilledRegisters()));
+
+    if (killRefPosition->busyUntilNextKill)
+    {
+        regsBusyUntilKill |= killRefPosition->getKilledRegisters();
+    }
 }
 
 //------------------------------------------------------------------------
@@ -5737,11 +5742,6 @@ void LinearScan::allocateRegisters()
                 regsInUseNextLocation.AddRegsetForType(currentRefPosition.registerAssignment, regRecord->registerType);
             }
 #endif // SWIFT_SUPPORT
-
-            if (currentRefPosition.busyUntilNextKill)
-            {
-                setRegBusyUntilKill(regRecord->regNum, regRecord->registerType);
-            }
             continue;
         }
 
diff --git a/src/coreclr/jit/lsra.h b/src/coreclr/jit/lsra.h
index 0c75574ba6a920..be62a7b83f92df 100644
--- a/src/coreclr/jit/lsra.h
+++ b/src/coreclr/jit/lsra.h
@@ -1026,7 +1026,7 @@ class LinearScan : public LinearScanInterface
     // insert refpositions representing prolog zero-inits which will be added later
     void insertZeroInitRefPositions();
 
-    void addKillForRegs(regMaskTP mask, LsraLocation currentLoc);
+    RefPosition* addKillForRegs(regMaskTP mask, LsraLocation currentLoc);
 
     void resolveConflictingDefAndUse(Interval* interval, RefPosition* defRefPosition);
 
@@ -1989,6 +1989,7 @@ class LinearScan : public LinearScanInterface
     int  BuildPutArgReg(GenTreeUnOp* node);
     int  BuildCall(GenTreeCall* call);
     void MarkSwiftErrorBusyForCall(GenTreeCall* call);
+    void MarkAsyncContinuationBusyForCall(GenTreeCall* call);
     int  BuildCmp(GenTree* tree);
     int  BuildCmpOperands(GenTree* tree);
     int  BuildBlockStore(GenTreeBlk* blkNode);
diff --git a/src/coreclr/jit/lsraarmarch.cpp b/src/coreclr/jit/lsraarmarch.cpp
index 518ae815f867c8..5058afe6f83d91 100644
--- a/src/coreclr/jit/lsraarmarch.cpp
+++ b/src/coreclr/jit/lsraarmarch.cpp
@@ -302,6 +302,11 @@ int LinearScan::BuildCall(GenTreeCall* call)
     }
 #endif // SWIFT_SUPPORT
 
+    if (call->IsAsync2() && compiler->compIsAsync2StateMachine())
+    {
+        MarkAsyncContinuationBusyForCall(call);
+    }
+
     // No args are placed in registers anymore.
     placedArgRegs      = RBM_NONE;
     numPlacedArgLocals = 0;
diff --git a/src/coreclr/jit/lsrabuild.cpp b/src/coreclr/jit/lsrabuild.cpp
index f9c915dbcf7a21..b8c2ebf8dbc55c 100644
--- a/src/coreclr/jit/lsrabuild.cpp
+++ b/src/coreclr/jit/lsrabuild.cpp
@@ -686,7 +686,7 @@ bool LinearScan::isContainableMemoryOp(GenTree* node)
 //    mask        - the mask (set) of registers.
 //    currentLoc  - the location at which they should be added
 //
-void LinearScan::addKillForRegs(regMaskTP mask, LsraLocation currentLoc)
+RefPosition* LinearScan::addKillForRegs(regMaskTP mask, LsraLocation currentLoc)
 {
     // The mask identifies a set of registers that will be used during
     // codegen. Mark these as modified here, so when we do final frame
@@ -705,6 +705,8 @@ void LinearScan::addKillForRegs(regMaskTP mask, LsraLocation currentLoc)
 
     *killTail = pos;
     killTail  = &pos->nextRefPosition;
+
+    return pos;
 }
 
 //------------------------------------------------------------------------
@@ -1200,27 +1202,6 @@ bool LinearScan::buildKillPositionsForNode(GenTree* tree, LsraLocation currentLo
         insertedKills    = true;
     }
 
-    if (tree->IsCall() && tree->AsCall()->IsAsync2() && compiler->compIsAsync2StateMachine())
-    {
-        // Async2 calls return an async continuation argument in a separate
-        // register. Since we do not have a flexible representation for
-        // multiple definitions (multi-reg support is tied into promotion) we
-        // have to utilize a hack here to make it work. We expect the return
-        // value to be consumed by an upcoming ASYNC_CONTINUATION node, but we
-        // must take care not to overwrite the register until we get to that
-        // node. To accomplish that we mark the register as "busy until next
-        // kill" when we see the call's kill, and then we have
-        // ASYNC_CONTINUATION insert its own kill to free up the register
-        // again.
-
-        assert((killMask & RBM_ASYNC_CONTINUATION_RET) != 0);
-
-        RegRecord*   asyncRetRegRec = getRegisterRecord(REG_ASYNC_CONTINUATION_RET);
-        RefPosition* pos            = asyncRetRegRec->lastRefPosition;
-        assert(pos->refType == RefTypeKill);
-        pos->busyUntilNextKill = true;
-    }
-
     return insertedKills;
 }
 
@@ -4731,3 +4712,19 @@ void LinearScan::MarkSwiftErrorBusyForCall(GenTreeCall* call)
     setDelayFree(swiftErrorRegRecord->lastRefPosition);
 }
 #endif
+
+void LinearScan::MarkAsyncContinuationBusyForCall(GenTreeCall* call)
+{
+    // Async2 calls return an async continuation argument in a separate
+    // register. Since we do not have a flexible representation for
+    // multiple definitions (multi-reg support is tied into promotion) we
+    // have to utilize a hack here to make it work. We expect the return
+    // value to be consumed by an upcoming ASYNC_CONTINUATION node, but we
+    // must take care not to overwrite the register until we get to that
+    // node. To accomplish that we mark the register as "busy until next
+    // kill" when we see the call's kill, and then we have
+    // ASYNC_CONTINUATION insert its own kill to free up the register
+    // again.
+    RefPosition* refPos       = addKillForRegs(RBM_ASYNC_CONTINUATION_RET, currentLoc + 1);
+    refPos->busyUntilNextKill = true;
+}
diff --git a/src/coreclr/jit/lsraxarch.cpp b/src/coreclr/jit/lsraxarch.cpp
index eb222cd4f4b743..348c3d3609dcb4 100644
--- a/src/coreclr/jit/lsraxarch.cpp
+++ b/src/coreclr/jit/lsraxarch.cpp
@@ -1335,6 +1335,11 @@ int LinearScan::BuildCall(GenTreeCall* call)
     }
 #endif // SWIFT_SUPPORT
 
+    if (call->IsAsync2() && compiler->compIsAsync2StateMachine())
+    {
+        MarkAsyncContinuationBusyForCall(call);
+    }
+
     // No args are placed in registers anymore.
     placedArgRegs      = RBM_NONE;
     numPlacedArgLocals = 0;

From 0f73c4086c60440466d7d3dd4db38b47b2bdf2b0 Mon Sep 17 00:00:00 2001
From: Jakob Botsch Nielsen <jakob.botsch.nielsen@gmail.com>
Date: Mon, 21 Oct 2024 12:05:01 +0200
Subject: [PATCH 112/203] Fix recognition of Async2CallContinuation intrinsic

---
 src/coreclr/jit/importercalls.cpp | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/src/coreclr/jit/importercalls.cpp b/src/coreclr/jit/importercalls.cpp
index eb0d36ca1bf0dc..893d435d963234 100644
--- a/src/coreclr/jit/importercalls.cpp
+++ b/src/coreclr/jit/importercalls.cpp
@@ -10903,6 +10903,10 @@ NamedIntrinsic Compiler::lookupNamedIntrinsic(CORINFO_METHOD_HANDLE method)
                         {
                             result = NI_System_StubHelpers_NextCallReturnAddress;
                         }
+                        else if (strcmp(methodName, "Async2CallContinuation") == 0)
+                        {
+                            result = NI_System_StubHelpers_Async2CallContinuation;
+                        }
                     }
                 }
                 else if (strcmp(namespaceName, "Text") == 0)
@@ -10915,10 +10919,6 @@ NamedIntrinsic Compiler::lookupNamedIntrinsic(CORINFO_METHOD_HANDLE method)
                             result = NI_System_Text_UTF8Encoding_UTF8EncodingSealed_ReadUtf8;
                         }
                     }
-                    else if (strcmp(methodName, "Async2CallContinuation") == 0)
-                    {
-                        result = NI_System_StubHelpers_Async2CallContinuation;
-                    }
                 }
                 else if (strcmp(namespaceName, "Threading") == 0)
                 {

From bef94ed5ce5b862e3856636756243cd4e71238df Mon Sep 17 00:00:00 2001
From: Jakob Botsch Nielsen <jakob.botsch.nielsen@gmail.com>
Date: Mon, 21 Oct 2024 12:05:12 +0200
Subject: [PATCH 113/203] Compute DFS tree for liveness

---
 src/coreclr/jit/async.cpp | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/src/coreclr/jit/async.cpp b/src/coreclr/jit/async.cpp
index 00280c4c0dd51a..3e8b039904368f 100644
--- a/src/coreclr/jit/async.cpp
+++ b/src/coreclr/jit/async.cpp
@@ -194,6 +194,11 @@ PhaseStatus Async2Transformation::Run()
 
     if (m_comp->opts.OptimizationEnabled())
     {
+        if (m_comp->m_dfsTree == nullptr)
+        {
+            m_comp->m_dfsTree = m_comp->fgComputeDfs<false>();
+        }
+
         m_comp->lvaComputeRefCounts(true, false);
         m_comp->fgLocalVarLiveness();
         VarSetOps::AssignNoCopy(m_comp, m_comp->compCurLife, VarSetOps::MakeEmpty(m_comp));
@@ -253,6 +258,8 @@ PhaseStatus Async2Transformation::Run()
 
     CreateResumptionSwitch();
 
+    m_comp->fgInvalidateDfsTree();
+
     return PhaseStatus::MODIFIED_EVERYTHING;
 }
 

From 1ba32a4ab993043de2017c3eb35e0b220e3babd2 Mon Sep 17 00:00:00 2001
From: Jakob Botsch Nielsen <jakob.botsch.nielsen@gmail.com>
Date: Mon, 21 Oct 2024 13:59:24 +0200
Subject: [PATCH 114/203] Rename likelihood

---
 src/coreclr/jit/async.cpp | 20 +++++++++++---------
 1 file changed, 11 insertions(+), 9 deletions(-)

diff --git a/src/coreclr/jit/async.cpp b/src/coreclr/jit/async.cpp
index 3e8b039904368f..9c488df4b969a1 100644
--- a/src/coreclr/jit/async.cpp
+++ b/src/coreclr/jit/async.cpp
@@ -263,9 +263,9 @@ PhaseStatus Async2Transformation::Run()
     return PhaseStatus::MODIFIED_EVERYTHING;
 }
 
-const weight_t SUSPEND_LIKELIHOOD       = 0.01;
-const weight_t RETHROW_LIKELIHOOD       = 0.01;
-const weight_t RESUME_IN_OSR_LIKELIHOOD = 0.001;
+const weight_t RESUME_SUSPEND_LIKELIHOOD = 0.01;
+const weight_t RETHROW_LIKELIHOOD        = 0.01;
+const weight_t RESUME_IN_OSR_LIKELIHOOD  = 0.001;
 
 void Async2Transformation::Transform(
     BasicBlock* block, GenTreeCall* call, jitstd::vector<GenTree*>& defs, AsyncLiveness& life, BasicBlock** pRemainder)
@@ -554,8 +554,8 @@ void Async2Transformation::Transform(
     FlowEdge* retBBEdge = m_comp->fgAddRefPred(retBB, block);
     block->SetCond(retBBEdge, block->GetTargetEdge());
 
-    block->GetTrueEdge()->setLikelihood(SUSPEND_LIKELIHOOD);
-    block->GetFalseEdge()->setLikelihood(1 - SUSPEND_LIKELIHOOD);
+    block->GetTrueEdge()->setLikelihood(RESUME_SUSPEND_LIKELIHOOD);
+    block->GetFalseEdge()->setLikelihood(1 - RESUME_SUSPEND_LIKELIHOOD);
 
     // Allocate continuation
     returnedContinuation           = m_comp->gtNewLclvNode(m_returnedContinuationVar, TYP_REF);
@@ -1318,8 +1318,8 @@ void Async2Transformation::CreateResumptionSwitch()
     }
 
     newEntryBB->SetCond(resumingEdge, toPrevEntryEdge);
-    resumingEdge->setLikelihood(SUSPEND_LIKELIHOOD);
-    toPrevEntryEdge->setLikelihood(1 - SUSPEND_LIKELIHOOD);
+    resumingEdge->setLikelihood(RESUME_SUSPEND_LIKELIHOOD);
+    toPrevEntryEdge->setLikelihood(1 - RESUME_SUSPEND_LIKELIHOOD);
 
     if (m_comp->doesMethodHavePatchpoints())
     {
@@ -1343,6 +1343,7 @@ void Async2Transformation::CreateResumptionSwitch()
 
         FlowEdge* toCheckILOffsetBB = m_comp->fgAddRefPred(checkILOffsetBB, newEntryBB);
         newEntryBB->SetTrueEdge(toCheckILOffsetBB);
+        toCheckILOffsetBB->setLikelihood(RESUME_SUSPEND_LIKELIHOOD);
 
         FlowEdge* toOnContinuationBB = m_comp->fgAddRefPred(onContinuationBB, checkILOffsetBB);
         FlowEdge* toCallHelperBB = m_comp->fgAddRefPred(callHelperBB, checkILOffsetBB);
@@ -1392,6 +1393,7 @@ void Async2Transformation::CreateResumptionSwitch()
         m_comp->fgRemoveRefPred(newEntryBB->GetTrueEdge());
         FlowEdge* toCheckILOffset = m_comp->fgAddRefPred(checkILOffsetBB, newEntryBB);
         newEntryBB->SetTrueEdge(toCheckILOffset);
+        toCheckILOffset->setLikelihood(RESUME_SUSPEND_LIKELIHOOD);
 
         // Make checkILOffsetBB ->(true)  onNoContinuationBB 
         //                      ->(false) onContinuationBB
@@ -1399,8 +1401,8 @@ void Async2Transformation::CreateResumptionSwitch()
         FlowEdge* toOnContinuationBB = m_comp->fgAddRefPred(onContinuationBB, checkILOffsetBB);
         FlowEdge* toOnNoContinuationBB = m_comp->fgAddRefPred(onNoContinuationBB, checkILOffsetBB);
         checkILOffsetBB->SetCond(toOnNoContinuationBB, toOnContinuationBB);
-        toOnContinuationBB->setLikelihood(SUSPEND_LIKELIHOOD);
-        toOnNoContinuationBB->setLikelihood(1 - SUSPEND_LIKELIHOOD);
+        toOnContinuationBB->setLikelihood(RESUME_SUSPEND_LIKELIHOOD);
+        toOnNoContinuationBB->setLikelihood(1 - RESUME_SUSPEND_LIKELIHOOD);
 
         JITDUMP("    Created " FMT_BB " to check for Tier-0 continuations\n", checkILOffsetBB->bbNum);
 

From 4ca306bd3c4d0ea2816018d0d380aaafd0ecd434 Mon Sep 17 00:00:00 2001
From: Jakob Botsch Nielsen <jakob.botsch.nielsen@gmail.com>
Date: Mon, 21 Oct 2024 13:59:39 +0200
Subject: [PATCH 115/203] Fix storing results on no EH rethrow

---
 src/coreclr/jit/async.cpp | 12 +++++++-----
 1 file changed, 7 insertions(+), 5 deletions(-)

diff --git a/src/coreclr/jit/async.cpp b/src/coreclr/jit/async.cpp
index 9c488df4b969a1..86ba414a17fd88 100644
--- a/src/coreclr/jit/async.cpp
+++ b/src/coreclr/jit/async.cpp
@@ -832,6 +832,8 @@ void Async2Transformation::Transform(
     }
 
     unsigned resumeObjectArrLclNum = BAD_VAR_NUM;
+    BasicBlock* storeResultBB = resumeBB;
+
     if (gcRefsCount > 0)
     {
         resumeObjectArrLclNum = GetGCDataArrayVar();
@@ -907,7 +909,7 @@ void Async2Transformation::Transform(
             BasicBlock* rethrowExceptionBB = m_comp->fgNewBBinRegion(BBJ_THROW, block, /* runRarely */ true, /* insertAtEnd */ true);
             JITDUMP("  Created " FMT_BB " to rethrow exception on resumption\n", rethrowExceptionBB->bbNum);
 
-            BasicBlock* storeResultBB = m_comp->fgNewBBafter(BBJ_ALWAYS, resumeBB, true);
+            storeResultBB = m_comp->fgNewBBafter(BBJ_ALWAYS, resumeBB, true);
             JITDUMP("  Created " FMT_BB " to store result when resuming with no exception\n", storeResultBB->bbNum);
 
             FlowEdge* rethrowEdge = m_comp->fgAddRefPred(rethrowExceptionBB, resumeBB);
@@ -1010,7 +1012,7 @@ void Async2Transformation::Transform(
                                                      storeResultNode->GetLclOffs(), resultData);
                 }
 
-                LIR::AsRange(resumeBB).InsertAtEnd(LIR::SeqTree(m_comp, storeResult));
+                LIR::AsRange(storeResultBB).InsertAtEnd(LIR::SeqTree(m_comp, storeResult));
             }
             else
             {
@@ -1020,7 +1022,7 @@ void Async2Transformation::Transform(
                 {
                     unsigned resultBaseVar   = GetResultBaseVar();
                     GenTree* storeResultBase = m_comp->gtNewStoreLclVarNode(resultBaseVar, resultBase);
-                    LIR::AsRange(resumeBB).InsertAtEnd(LIR::SeqTree(m_comp, storeResultBase));
+                    LIR::AsRange(storeResultBB).InsertAtEnd(LIR::SeqTree(m_comp, storeResultBase));
 
                     resultBase = m_comp->gtNewLclVarNode(resultBaseVar, TYP_REF);
                 }
@@ -1034,7 +1036,7 @@ void Async2Transformation::Transform(
                     unsigned fldOffset = resultOffset + fieldDsc->lvFldOffset;
                     GenTree* value     = LoadFromOffset(resultBase, fldOffset, fieldDsc->TypeGet(), resultIndirFlags);
                     GenTree* store     = m_comp->gtNewStoreLclVarNode(fieldLclNum, value);
-                    LIR::AsRange(resumeBB).InsertAtEnd(LIR::SeqTree(m_comp, store));
+                    LIR::AsRange(storeResultBB).InsertAtEnd(LIR::SeqTree(m_comp, store));
 
                     if (i + 1 != resultLcl->lvFieldCnt)
                     {
@@ -1058,7 +1060,7 @@ void Async2Transformation::Transform(
                                                            storeResultNode->GetLclOffs(), value);
             }
 
-            LIR::AsRange(resumeBB).InsertAtEnd(LIR::SeqTree(m_comp, storeResult));
+            LIR::AsRange(storeResultBB).InsertAtEnd(LIR::SeqTree(m_comp, storeResult));
         }
     }
 

From a1bdae01c6610d38c1a709bd1dcc9eba3bb237fb Mon Sep 17 00:00:00 2001
From: Jakob Botsch Nielsen <jakob.botsch.nielsen@gmail.com>
Date: Mon, 21 Oct 2024 14:31:19 +0200
Subject: [PATCH 116/203] Fix resuming with 2 states

---
 src/coreclr/jit/async.cpp | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/coreclr/jit/async.cpp b/src/coreclr/jit/async.cpp
index 86ba414a17fd88..46cd7e4c1cabf6 100644
--- a/src/coreclr/jit/async.cpp
+++ b/src/coreclr/jit/async.cpp
@@ -1260,9 +1260,9 @@ void Async2Transformation::CreateResumptionSwitch()
 
         FlowEdge* to0 = m_comp->fgAddRefPred(m_resumptionBBs[0], condBB);
         FlowEdge* to1 = m_comp->fgAddRefPred(m_resumptionBBs[1], condBB);
-        condBB->SetCond(to0, to1);
-        to0->setLikelihood(0.5);
+        condBB->SetCond(to1, to0);
         to1->setLikelihood(0.5);
+        to0->setLikelihood(0.5);
 
         resumingEdge = m_comp->fgAddRefPred(condBB, newEntryBB);
 

From f99ea2bb72b4f35063284788deeebea79414d364 Mon Sep 17 00:00:00 2001
From: Jakob Botsch Nielsen <jakob.botsch.nielsen@gmail.com>
Date: Mon, 21 Oct 2024 14:50:35 +0200
Subject: [PATCH 117/203] Spill only for async2 methods

---
 src/coreclr/jit/importer.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/coreclr/jit/importer.cpp b/src/coreclr/jit/importer.cpp
index 9206c72c3c6431..e3bfc546567487 100644
--- a/src/coreclr/jit/importer.cpp
+++ b/src/coreclr/jit/importer.cpp
@@ -842,7 +842,7 @@ GenTree* Compiler::impStoreStruct(GenTree*         store,
             GenTree*     destAddr   = impGetNodeAddr(store, CHECK_SPILL_ALL, &indirFlags);
 
             unsigned retbufSpillLcl = BAD_VAR_NUM;
-            if (!destAddr->OperIs(GT_LCL_ADDR))
+            if (!destAddr->OperIs(GT_LCL_ADDR) && compIsAsync2StateMachine())
             {
                 retbufSpillLcl = lvaGrabTemp(true DEBUGARG("retbuf for async2 call stored to non-lcl"));
                 lvaGetDesc(retbufSpillLcl)->lvType = store->TypeGet();

From 0647500b0e79dfb2517240f40f00e63ef4b6d2e4 Mon Sep 17 00:00:00 2001
From: Jakob Botsch Nielsen <jakob.botsch.nielsen@gmail.com>
Date: Mon, 21 Oct 2024 15:02:06 +0200
Subject: [PATCH 118/203] Fix type of VA cookie

---
 src/coreclr/jit/importercalls.cpp | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/coreclr/jit/importercalls.cpp b/src/coreclr/jit/importercalls.cpp
index 893d435d963234..b32af241f566f9 100644
--- a/src/coreclr/jit/importercalls.cpp
+++ b/src/coreclr/jit/importercalls.cpp
@@ -919,8 +919,8 @@ var_types Compiler::impImportCall(OPCODE                  opcode,
         {
             if (varArgsCookie != nullptr)
             {
-                call->AsCall()->gtArgs.PushFront(this, NewCallArg::Primitive(varArgsCookie, TYP_REF)
-                                                           .WellKnown(WellKnownArg::VarArgsCookie));
+                call->AsCall()->gtArgs.PushFront(this,
+                                                 NewCallArg::Primitive(varArgsCookie).WellKnown(WellKnownArg::VarArgsCookie));
             }
 
             if (sig->isAsyncCall() && (JitConfig.RuntimeAsyncViaJitGeneratedStateMachines() != 0))

From 083681cc1f673a614ec487cfb9eaf0800a524eb1 Mon Sep 17 00:00:00 2001
From: Vladimir Sadov <vsadov@microsoft.com>
Date: Mon, 28 Oct 2024 09:02:16 -0700
Subject: [PATCH 119/203] Deleting parts of suspension-async flavor. (#2738)

* delete tasklet aging

* delete runtimesuspension.[h|cpp]

* delete Tasklet struct, RuntimeAsyncViaJitGeneratedStateMachines and dependencies

* fix after rebase

* more cleanup for RuntimeTaskState and the like
---
 src/coreclr/gc/env/gcenv.ee.h                 |   4 -
 .../gc/env/gctoeeinterface.standalone.inl     |  10 -
 src/coreclr/gc/gcenv.ee.standalone.inl        |  12 -
 src/coreclr/gc/gcinterface.ee.h               |   6 -
 src/coreclr/gc/gcscan.cpp                     |  14 -
 src/coreclr/gc/sample/gcenv.ee.cpp            |  10 +-
 src/coreclr/inc/clrconfigvalues.h             |   6 -
 src/coreclr/jit/codegenarmarch.cpp            |   2 +-
 src/coreclr/jit/codegencommon.cpp             |  10 +-
 src/coreclr/jit/codegenxarch.cpp              |   2 +-
 src/coreclr/jit/compiler.cpp                  |  10 +-
 src/coreclr/jit/compiler.h                    |  10 -
 src/coreclr/jit/gentree.cpp                   |   4 +-
 src/coreclr/jit/importer.cpp                  |   2 +-
 src/coreclr/jit/importercalls.cpp             |  11 +-
 src/coreclr/jit/jitconfigvalues.h             |   3 -
 src/coreclr/jit/lclvars.cpp                   |   4 +-
 src/coreclr/jit/lsraarmarch.cpp               |   2 +-
 src/coreclr/jit/lsraxarch.cpp                 |   2 +-
 src/coreclr/jit/optcse.cpp                    |   4 +-
 src/coreclr/nativeaot/Runtime/gcenv.ee.cpp    |   8 -
 src/coreclr/vm/CMakeLists.txt                 |   2 -
 src/coreclr/vm/amd64/AsmHelpers.asm           | 216 -----
 src/coreclr/vm/ceemain.cpp                    |   4 -
 src/coreclr/vm/corelib.cpp                    |   2 -
 src/coreclr/vm/corelib.h                      |  24 -
 src/coreclr/vm/ecalllist.h                    |   3 -
 src/coreclr/vm/eeconfig.cpp                   |   3 -
 src/coreclr/vm/eeconfig.h                     |   4 -
 src/coreclr/vm/gccover.cpp                    |   9 +-
 src/coreclr/vm/gcenv.ee.cpp                   |  33 +-
 src/coreclr/vm/method.hpp                     |   6 +-
 src/coreclr/vm/prestub.cpp                    | 193 +----
 src/coreclr/vm/qcallentrypoints.cpp           |   5 -
 src/coreclr/vm/runtimesuspension.cpp          | 820 ------------------
 src/coreclr/vm/runtimesuspension.h            |  23 -
 src/coreclr/vm/siginfo.cpp                    |   2 +-
 src/coreclr/vm/threadsuspend.cpp              |   3 -
 .../CompilerServices/RuntimeHelpers.cs        | 413 +--------
 .../src/System/Threading/Tasks/Future.cs      | 190 ----
 40 files changed, 42 insertions(+), 2049 deletions(-)
 delete mode 100644 src/coreclr/vm/runtimesuspension.cpp
 delete mode 100644 src/coreclr/vm/runtimesuspension.h

diff --git a/src/coreclr/gc/env/gcenv.ee.h b/src/coreclr/gc/env/gcenv.ee.h
index a1094f0c419cdd..865eb2902a8798 100644
--- a/src/coreclr/gc/env/gcenv.ee.h
+++ b/src/coreclr/gc/env/gcenv.ee.h
@@ -45,10 +45,6 @@ class GCToEEInterface
     static void SyncBlockCachePromotionsGranted(int max_gen);
     static uint32_t GetActiveSyncBlockCount();
 
-    // Tasklet management
-    static void TaskletPromotionsGranted(int condemned, int max_gen, ScanContext* sc);
-    static void TaskletDemote(int condemned, int max_gen, ScanContext* sc);
-
     // Thread functions
     static bool IsPreemptiveGCDisabled();
     static bool EnablePreemptiveGC();
diff --git a/src/coreclr/gc/env/gctoeeinterface.standalone.inl b/src/coreclr/gc/env/gctoeeinterface.standalone.inl
index 5d668a643e30b2..429d4f4c0248f6 100644
--- a/src/coreclr/gc/env/gctoeeinterface.standalone.inl
+++ b/src/coreclr/gc/env/gctoeeinterface.standalone.inl
@@ -64,16 +64,6 @@ namespace standalone
             ::GCToEEInterface::SyncBlockCachePromotionsGranted(max_gen);
         }
 
-        void TaskletPromotionsGranted(int condemned, int max_gen, ScanContext* sc)
-        {
-            ::GCToEEInterface::TaskletPromotionsGranted(condemned, max_gen, sc);
-        }
-
-        void TaskletDemote(int condemned, int max_gen, ScanContext* sc)
-        {
-            ::GCToEEInterface::TaskletDemote(condemned, max_gen, sc);
-        }
-
         uint32_t GetActiveSyncBlockCount()
         {
             return ::GCToEEInterface::GetActiveSyncBlockCount();
diff --git a/src/coreclr/gc/gcenv.ee.standalone.inl b/src/coreclr/gc/gcenv.ee.standalone.inl
index ec75cafc8bc65f..898770f717d324 100644
--- a/src/coreclr/gc/gcenv.ee.standalone.inl
+++ b/src/coreclr/gc/gcenv.ee.standalone.inl
@@ -90,18 +90,6 @@ inline void GCToEEInterface::SyncBlockCachePromotionsGranted(int max_gen)
     g_theGCToCLR->SyncBlockCachePromotionsGranted(max_gen);
 }
 
-inline void GCToEEInterface::TaskletPromotionsGranted(int condemned, int max_gen, ScanContext* sc)
-{
-    assert(g_theGCToCLR != nullptr);
-    g_theGCToCLR->TaskletPromotionsGranted(condemned, max_gen, sc);
-}
-
-inline void GCToEEInterface::TaskletDemote(int condemned, int max_gen, ScanContext* sc)
-{
-    assert(g_theGCToCLR != nullptr);
-    g_theGCToCLR->TaskletDemote(condemned, max_gen, sc);
-}
-
 inline uint32_t GCToEEInterface::GetActiveSyncBlockCount()
 {
     assert(g_theGCToCLR != nullptr);
diff --git a/src/coreclr/gc/gcinterface.ee.h b/src/coreclr/gc/gcinterface.ee.h
index 31e07f17067bbc..afc29e837a9146 100644
--- a/src/coreclr/gc/gcinterface.ee.h
+++ b/src/coreclr/gc/gcinterface.ee.h
@@ -251,12 +251,6 @@ class IGCToCLR {
     virtual
     void SyncBlockCachePromotionsGranted(int max_gen) PURE_VIRTUAL
 
-    virtual
-    void TaskletPromotionsGranted(int condemned, int max_gen, ScanContext* sc) PURE_VIRTUAL
-
-    virtual
-    void TaskletDemote(int condemned, int max_gen, ScanContext* sc) PURE_VIRTUAL
-
     virtual
     uint32_t GetActiveSyncBlockCount() PURE_VIRTUAL
 
diff --git a/src/coreclr/gc/gcscan.cpp b/src/coreclr/gc/gcscan.cpp
index 25c966c1d75cdb..95dab9e11c519f 100644
--- a/src/coreclr/gc/gcscan.cpp
+++ b/src/coreclr/gc/gcscan.cpp
@@ -225,28 +225,14 @@ void GCScan::GcDemote (int condemned, int max_gen, ScanContext* sc)
 {
     Ref_RejuvenateHandles (condemned, max_gen, sc);
     if (!IsServerHeap() || sc->thread_number == 0)
-    {
         GCToEEInterface::SyncBlockCacheDemote(max_gen);
-
-        // TODO: we should partition tasklet lists by the core number on server GC
-        //       and do this on multiple threads.
-        //       then "sc" argument will be used.
-        GCToEEInterface::TaskletDemote(condemned, max_gen, sc);
-    }
 }
 
 void GCScan::GcPromotionsGranted (int condemned, int max_gen, ScanContext* sc)
 {
     Ref_AgeHandles(condemned, max_gen, sc);
     if (!IsServerHeap() || sc->thread_number == 0)
-    {
         GCToEEInterface::SyncBlockCachePromotionsGranted(max_gen);
-
-        // TODO: we should partition tasklet lists by the core number on server GC
-        //       and do this on multiple threads.
-        //       then "sc" argument will be used.
-        GCToEEInterface::TaskletPromotionsGranted(condemned, max_gen, sc);
-    }
 }
 
 void GCScan::VerifyHandleTable(int condemned, int max_gen, ScanContext* sc)
diff --git a/src/coreclr/gc/sample/gcenv.ee.cpp b/src/coreclr/gc/sample/gcenv.ee.cpp
index d91d381f5cef17..19ceb56fc16f07 100644
--- a/src/coreclr/gc/sample/gcenv.ee.cpp
+++ b/src/coreclr/gc/sample/gcenv.ee.cpp
@@ -225,14 +225,6 @@ void GCToEEInterface::SyncBlockCachePromotionsGranted(int /*max_gen*/)
 {
 }
 
-void GCToEEInterface::TaskletPromotionsGranted(int /*condemned*/, int /*max_gen*/, ScanContext* /*sc*/)
-{
-}
-
-void GCToEEInterface::TaskletDemote(int /*condemned*/, int /*max_gen*/, ScanContext* /*sc*/)
-{
-}
-
 void GCToEEInterface::DiagGCStart(int gen, bool isInduced)
 {
 }
@@ -374,4 +366,4 @@ void GCToEEInterface::LogErrorToHost(const char *message)
 uint64_t GCToEEInterface::GetThreadOSThreadId(Thread* thread)
 {
     return 0;
-}
\ No newline at end of file
+}
diff --git a/src/coreclr/inc/clrconfigvalues.h b/src/coreclr/inc/clrconfigvalues.h
index b42c218121e917..00d9745e177e7f 100644
--- a/src/coreclr/inc/clrconfigvalues.h
+++ b/src/coreclr/inc/clrconfigvalues.h
@@ -805,12 +805,6 @@ RETAIL_CONFIG_DWORD_INFO(EXTERNAL_EnableArm64Rcpc2,             W("EnableArm64Rc
 RETAIL_CONFIG_DWORD_INFO(EXTERNAL_EnableArm64Sve,               W("EnableArm64Sve"),            1, "Allows Arm64 SVE hardware intrinsics to be disabled")
 #endif
 
-///
-/// Runtime async
-///
-RETAIL_CONFIG_DWORD_INFO(EXTERNAL_RuntimeAsyncViaJitGeneratedStateMachines, W("RuntimeAsyncViaJitGeneratedStateMachines"), 1, "Use JIT generated state machines instead of unwinding-based runtime async")
-RETAIL_CONFIG_DWORD_INFO(EXTERNAL_TaskletAging, W("TaskletAging"), 1, "Enable tasklet aging when tasklet based async is used")
-
 ///
 /// Uncategorized
 ///
diff --git a/src/coreclr/jit/codegenarmarch.cpp b/src/coreclr/jit/codegenarmarch.cpp
index 93265cca63154a..6edd0599f3020b 100644
--- a/src/coreclr/jit/codegenarmarch.cpp
+++ b/src/coreclr/jit/codegenarmarch.cpp
@@ -3519,7 +3519,7 @@ void CodeGen::genCallInstruction(GenTreeCall* call)
     }
 #endif // DEBUG
 
-    bool                  hasAsyncRet = call->IsAsync2() && (JitConfig.RuntimeAsyncViaJitGeneratedStateMachines() != 0);
+    bool                  hasAsyncRet = call->IsAsync2();
     CORINFO_METHOD_HANDLE methHnd;
     GenTree*              target = getCallTarget(call, &methHnd);
 
diff --git a/src/coreclr/jit/codegencommon.cpp b/src/coreclr/jit/codegencommon.cpp
index 628d6be7a690b5..0fa1d3a3c36088 100644
--- a/src/coreclr/jit/codegencommon.cpp
+++ b/src/coreclr/jit/codegencommon.cpp
@@ -1940,13 +1940,9 @@ void CodeGen::genGenerateMachineCode()
             printf("; OSR variant for entry point 0x%x\n", compiler->info.compILEntry);
         }
 
-        if (compiler->compIsAsync2StateMachine())
+        if (compiler->compIsAsync2())
         {
-            printf("; async2 state machine\n");
-        }
-        else if (compiler->compIsAsync2ViaUnwinding())
-        {
-            printf("; async2 via unwinding\n");
+            printf("; async2\n");
         }
 
         if ((compiler->opts.compFlags & CLFLG_MAXOPT) == CLFLG_MAXOPT)
@@ -7204,7 +7200,7 @@ void CodeGen::genReturn(GenTree* treeNode)
         }
     }
 
-    if (treeNode->OperIs(GT_RETURN) && compiler->compIsAsync2StateMachine())
+    if (treeNode->OperIs(GT_RETURN) && compiler->compIsAsync2())
     {
         instGen_Set_Reg_To_Zero(EA_PTRSIZE, REG_ASYNC_CONTINUATION_RET);
     }
diff --git a/src/coreclr/jit/codegenxarch.cpp b/src/coreclr/jit/codegenxarch.cpp
index 338e95897dc195..0a561d86aeb14b 100644
--- a/src/coreclr/jit/codegenxarch.cpp
+++ b/src/coreclr/jit/codegenxarch.cpp
@@ -6326,7 +6326,7 @@ void CodeGen::genCallInstruction(GenTreeCall* call X86_ARG(target_ssize_t stackA
     }
 #endif // DEBUG
 
-    bool                  hasAsyncRet = call->IsAsync2() && (JitConfig.RuntimeAsyncViaJitGeneratedStateMachines() != 0);
+    bool                  hasAsyncRet = call->IsAsync2();
     CORINFO_METHOD_HANDLE methHnd;
     GenTree*              target = getCallTarget(call, &methHnd);
     if (target != nullptr)
diff --git a/src/coreclr/jit/compiler.cpp b/src/coreclr/jit/compiler.cpp
index b1e8f21863492d..3e8ae18bf4a343 100644
--- a/src/coreclr/jit/compiler.cpp
+++ b/src/coreclr/jit/compiler.cpp
@@ -3366,13 +3366,9 @@ void Compiler::compInitOptions(JitFlags* jitFlags)
             printf("OPTIONS: Jit invoked for ngen\n");
         }
 
-        if (compIsAsync2StateMachine())
+        if (compIsAsync2())
         {
-            printf("OPTIONS: compilation is an async2 state machine\n");
-        }
-        else if (compIsAsync2ViaUnwinding())
-        {
-            printf("OPTIONS: compilation is an async2 function (via unwinding)\n");
+            printf("OPTIONS: compilation is an async2\n");
         }
     }
 #endif
@@ -5227,7 +5223,7 @@ void Compiler::compCompile(void** methodCodePtr, uint32_t* methodCodeSize, JitFl
     }
 #endif // TARGET_ARM
 
-    if (compIsAsync2StateMachine())
+    if (compIsAsync2())
     {
         DoPhase(this, PHASE_ASYNC2, &Compiler::TransformAsync2);
     }
diff --git a/src/coreclr/jit/compiler.h b/src/coreclr/jit/compiler.h
index 5eb929c79d21c1..1ddfd15f3a262c 100644
--- a/src/coreclr/jit/compiler.h
+++ b/src/coreclr/jit/compiler.h
@@ -10834,16 +10834,6 @@ class Compiler
         return opts.jitFlags->IsSet(JitFlags::JIT_FLAG_RUNTIMEASYNCFUNCTION);
     }
 
-    bool compIsAsync2StateMachine() const
-    {
-        return compIsAsync2() && (JitConfig.RuntimeAsyncViaJitGeneratedStateMachines() != 0);
-    }
-
-    bool compIsAsync2ViaUnwinding() const
-    {
-        return compIsAsync2() && (JitConfig.RuntimeAsyncViaJitGeneratedStateMachines() == 0);
-    }
-
     //------------------------------------------------------------------------
     // compMethodReturnsMultiRegRetType: Does this method return a multi-reg value?
     //
diff --git a/src/coreclr/jit/gentree.cpp b/src/coreclr/jit/gentree.cpp
index 32017c24f249cd..bd00457ad0bdb2 100644
--- a/src/coreclr/jit/gentree.cpp
+++ b/src/coreclr/jit/gentree.cpp
@@ -2369,9 +2369,7 @@ bool GenTreeCall::HasSideEffects(Compiler* compiler, bool ignoreExceptions, bool
 //   True if so.
 //
 // Remarks:
-//   The exact nature of the async2 calling convention depends on whether
-//   JitConfig.RuntimeAsyncViaJitGeneratedStateMachines is true or not. When
-//   true, it involves passing an async continuation as a separate argument and
+//   async2 involves passing an async continuation as a separate argument and
 //   returning an async continuation in REG_ASYNC_CONTINUATION_RET.
 //
 //   The async continuation is usually JIT added
diff --git a/src/coreclr/jit/importer.cpp b/src/coreclr/jit/importer.cpp
index e3bfc546567487..96d2be874552c0 100644
--- a/src/coreclr/jit/importer.cpp
+++ b/src/coreclr/jit/importer.cpp
@@ -842,7 +842,7 @@ GenTree* Compiler::impStoreStruct(GenTree*         store,
             GenTree*     destAddr   = impGetNodeAddr(store, CHECK_SPILL_ALL, &indirFlags);
 
             unsigned retbufSpillLcl = BAD_VAR_NUM;
-            if (!destAddr->OperIs(GT_LCL_ADDR) && compIsAsync2StateMachine())
+            if (!destAddr->OperIs(GT_LCL_ADDR) && compIsAsync2())
             {
                 retbufSpillLcl = lvaGrabTemp(true DEBUGARG("retbuf for async2 call stored to non-lcl"));
                 lvaGetDesc(retbufSpillLcl)->lvType = store->TypeGet();
diff --git a/src/coreclr/jit/importercalls.cpp b/src/coreclr/jit/importercalls.cpp
index b32af241f566f9..d460e5720d4490 100644
--- a/src/coreclr/jit/importercalls.cpp
+++ b/src/coreclr/jit/importercalls.cpp
@@ -526,7 +526,7 @@ var_types Compiler::impImportCall(OPCODE                  opcode,
 
         // Temporary hack since these functions have to be recognized as async2
         // calls in JIT generated state machines only.
-        if (compIsAsync2StateMachine() &&
+        if (compIsAsync2() &&
             ((ni == NI_System_Runtime_CompilerServices_RuntimeHelpers_AwaitAwaiterFromRuntimeAsync) ||
              (ni == NI_System_Runtime_CompilerServices_RuntimeHelpers_UnsafeAwaitAwaiterFromRuntimeAsync)))
         {
@@ -923,7 +923,7 @@ var_types Compiler::impImportCall(OPCODE                  opcode,
                                                  NewCallArg::Primitive(varArgsCookie).WellKnown(WellKnownArg::VarArgsCookie));
             }
 
-            if (sig->isAsyncCall() && (JitConfig.RuntimeAsyncViaJitGeneratedStateMachines() != 0))
+            if (sig->isAsyncCall())
             {
                 call->AsCall()->gtArgs.PushFront(this, NewCallArg::Primitive(gtNewNull(), TYP_REF)
                                                            .WellKnown(WellKnownArg::AsyncContinuation));
@@ -943,7 +943,7 @@ var_types Compiler::impImportCall(OPCODE                  opcode,
                                                 NewCallArg::Primitive(instParam).WellKnown(WellKnownArg::InstParam));
             }
 
-            if (sig->isAsyncCall() && (JitConfig.RuntimeAsyncViaJitGeneratedStateMachines() != 0))
+            if (sig->isAsyncCall())
             {
                 call->AsCall()->gtArgs.PushBack(this, NewCallArg::Primitive(gtNewNull(), TYP_REF)
                                                           .WellKnown(WellKnownArg::AsyncContinuation));
@@ -3376,11 +3376,6 @@ GenTree* Compiler::impIntrinsic(CORINFO_CLASS_HANDLE    clsHnd,
         return node;
     }
 
-    if (ni == NI_System_Runtime_CompilerServices_RuntimeHelpers_get_RuntimeAsyncViaJitGeneratedStateMachines)
-    {
-        return gtNewIconNode(JitConfig.RuntimeAsyncViaJitGeneratedStateMachines() != 0 ? 1 : 0);
-    }
-
     if ((ni == NI_System_Runtime_CompilerServices_RuntimeHelpers_AwaitAwaiterFromRuntimeAsync) ||
         (ni == NI_System_Runtime_CompilerServices_RuntimeHelpers_UnsafeAwaitAwaiterFromRuntimeAsync))
     {
diff --git a/src/coreclr/jit/jitconfigvalues.h b/src/coreclr/jit/jitconfigvalues.h
index d95c3d44261930..23158d49342dec 100644
--- a/src/coreclr/jit/jitconfigvalues.h
+++ b/src/coreclr/jit/jitconfigvalues.h
@@ -783,9 +783,6 @@ RELEASE_CONFIG_INTEGER(JitEnableStrengthReduction, W("JitEnableStrengthReduction
 // Enable IV optimizations
 RELEASE_CONFIG_INTEGER(JitEnableInductionVariableOpts, W("JitEnableInductionVariableOpts"), 1)
 
-// Should JIT generate state machines for async2 methods?
-RELEASE_CONFIG_INTEGER(RuntimeAsyncViaJitGeneratedStateMachines, W("RuntimeAsyncViaJitGeneratedStateMachines"), 1)
-
 // JitFunctionFile: Name of a file that contains a list of functions. If the currently compiled function is in the
 // file, certain other JIT config variables will be active. If the currently compiled function is not in the file,
 // the specific JIT config variables will not be active.
diff --git a/src/coreclr/jit/lclvars.cpp b/src/coreclr/jit/lclvars.cpp
index b4137741426669..c53ad722a50593 100644
--- a/src/coreclr/jit/lclvars.cpp
+++ b/src/coreclr/jit/lclvars.cpp
@@ -146,7 +146,7 @@ void Compiler::lvaInitTypeRef()
         info.compTypeCtxtArg = BAD_VAR_NUM;
     }
 
-    if (compIsAsync2StateMachine())
+    if (compIsAsync2())
     {
         info.compArgsCount++;
     }
@@ -1491,7 +1491,7 @@ void Compiler::lvaInitGenericsCtxt(InitVarDscInfo* varDscInfo)
 
 void Compiler::lvaInitAsyncContinuation(InitVarDscInfo* varDscInfo)
 {
-    if (!compIsAsync2StateMachine())
+    if (!compIsAsync2())
     {
         return;
     }
diff --git a/src/coreclr/jit/lsraarmarch.cpp b/src/coreclr/jit/lsraarmarch.cpp
index 5058afe6f83d91..6a3d2e8aa23558 100644
--- a/src/coreclr/jit/lsraarmarch.cpp
+++ b/src/coreclr/jit/lsraarmarch.cpp
@@ -302,7 +302,7 @@ int LinearScan::BuildCall(GenTreeCall* call)
     }
 #endif // SWIFT_SUPPORT
 
-    if (call->IsAsync2() && compiler->compIsAsync2StateMachine())
+    if (call->IsAsync2() && compiler->compIsAsync2())
     {
         MarkAsyncContinuationBusyForCall(call);
     }
diff --git a/src/coreclr/jit/lsraxarch.cpp b/src/coreclr/jit/lsraxarch.cpp
index 348c3d3609dcb4..34ca3ca3c4b359 100644
--- a/src/coreclr/jit/lsraxarch.cpp
+++ b/src/coreclr/jit/lsraxarch.cpp
@@ -1335,7 +1335,7 @@ int LinearScan::BuildCall(GenTreeCall* call)
     }
 #endif // SWIFT_SUPPORT
 
-    if (call->IsAsync2() && compiler->compIsAsync2StateMachine())
+    if (call->IsAsync2() && compiler->compIsAsync2())
     {
         MarkAsyncContinuationBusyForCall(call);
     }
diff --git a/src/coreclr/jit/optcse.cpp b/src/coreclr/jit/optcse.cpp
index 2e38330b356d28..6bc1afdbb42b34 100644
--- a/src/coreclr/jit/optcse.cpp
+++ b/src/coreclr/jit/optcse.cpp
@@ -1104,7 +1104,7 @@ void Compiler::optValnumCSE_InitDataFlow()
         }
     }
 
-    if (compIsAsync2StateMachine())
+    if (compIsAsync2())
     {
         bool anyAsyncKills = false;
         cseAsyncKillsMask  = BitVecOps::MakeFull(cseLivenessTraits);
@@ -1825,7 +1825,7 @@ void Compiler::optValnumCSE_Availability()
                             BitVecOps::IntersectionD(cseLivenessTraits, available_cses, cseCallKillsMask);
 
                             // In async state machines, make all byref CSEs unavailable after suspension points.
-                            if (tree->AsCall()->IsAsync2() && compIsAsync2StateMachine())
+                            if (tree->AsCall()->IsAsync2() && compIsAsync2())
                             {
                                 BitVecOps::IntersectionD(cseLivenessTraits, available_cses, cseAsyncKillsMask);
                             }
diff --git a/src/coreclr/nativeaot/Runtime/gcenv.ee.cpp b/src/coreclr/nativeaot/Runtime/gcenv.ee.cpp
index e7fb0a0e52cbe7..0f9aa696d2338d 100644
--- a/src/coreclr/nativeaot/Runtime/gcenv.ee.cpp
+++ b/src/coreclr/nativeaot/Runtime/gcenv.ee.cpp
@@ -192,14 +192,6 @@ void GCToEEInterface::SyncBlockCachePromotionsGranted(int /*max_gen*/)
 {
 }
 
-void GCToEEInterface::TaskletPromotionsGranted(int /*condemned*/, int /*max_gen*/, ScanContext* /*sc*/)
-{
-}
-
-void GCToEEInterface::TaskletDemote(int /*condemned*/, int /*max_gen*/, ScanContext* /*sc*/)
-{
-}
-
 uint32_t GCToEEInterface::GetActiveSyncBlockCount()
 {
     return 0;
diff --git a/src/coreclr/vm/CMakeLists.txt b/src/coreclr/vm/CMakeLists.txt
index 1c711a819dc3de..334faa6f16764d 100644
--- a/src/coreclr/vm/CMakeLists.txt
+++ b/src/coreclr/vm/CMakeLists.txt
@@ -357,7 +357,6 @@ set(VM_SOURCES_WKS
     reflectclasswriter.cpp
     reflectioninvocation.cpp
     runtimehandles.cpp
-    runtimesuspension.cpp
     simplerwlock.cpp
     stackingallocator.cpp
     stringliteralmap.cpp
@@ -455,7 +454,6 @@ set(VM_HEADERS_WKS
     reflectclasswriter.h
     reflectioninvocation.h
     runtimehandles.h
-    runtimesuspension.h
     simplerwlock.hpp
     stackingallocator.h
     stringliteralmap.h
diff --git a/src/coreclr/vm/amd64/AsmHelpers.asm b/src/coreclr/vm/amd64/AsmHelpers.asm
index 5717ae2c26937a..c237c60cd4193d 100644
--- a/src/coreclr/vm/amd64/AsmHelpers.asm
+++ b/src/coreclr/vm/amd64/AsmHelpers.asm
@@ -449,220 +449,4 @@ NESTED_END OnCallCountThresholdReachedStub, _TEXT
 
 endif ; FEATURE_TIERED_COMPILATION
 
-extern PlatformIndependentRestore:proc
-extern ForceThisThreadHasNoHijackForUnwind:proc
-
-; regRestore swaps whats in the register for the value in the restoration location. The idea is that the
-; value in the restoration location is the value that has been stored away for use in this function
-; and the current value of the saved register needs to be available once the function returns.
-regRestoreCase macro RegisterName
-Check&RegisterName&:
-    mov edx, [rax+4]
-    mov r10, RegisterName
-    mov RegisterName, [rcx+rdx]
-    mov [rcx+rdx], r10
-    add rax, 8
-    jmp RegRestoreLoop
-        endm
-
-regRestoreCaseXmm macro RegisterName
-Check&RegisterName&:
-    mov edx, [rax+4]
-    movsd xmm0, RegisterName
-    movdqu RegisterName, [rcx+rdx]
-    movdqu [rcx+rdx], xmm0
-    add rax, 8
-    jmp RegRestoreLoop
-        endm
-
-NESTED_ENTRY RuntimeSuspension_ResumeTaskletReferenceReturn, _TEXT
-; On entry rcx is Tasklet* 
-;          rdx is a pointer to the return value structure
-;
-;          r10 and r8 are used as scratch registers
-        push_nonvol_reg rbp  ; We capture RBP to create a stack frame so that we can safely adjust the RSP register, This means that since we always capture this, async2 functions must ALWAYS be compiled with a frame pointer.
-        alloc_stack             50h
-        set_frame rbp, 50h
-    END_PROLOGUE
-        mov r10, [rcx + 8h*5] ; Load pStackDataInfo from Tasklet*
-        mov rax, [r10] ; Load StackRequirement from StackDataInfo
-        mov r10d, [r10 + 4h*6] ; Load CbArgs from StackDataInfo
-        sub rax, r10          ; Compute size of Caller SP to Callee SP
-        sub rsp, rax          ; Allocate enough space for Callee stack
-
-        lea r8, [rsp+20h]
-
-        ; Zero out integerRegister and AddressInMethodToRestoreTo
-        ; Probably not needed in production code, but convenient for now
-        xor rax, rax
-        mov [r8], rax
-        mov [r8+8h], rax
-
-        ; Fill in pFutureRSPLocation
-        lea rax, [rsp+60h]
-        mov [r8+10h], rax
-
-        ; Capture return address
-        mov rax, [rbp + 8h]
-        mov [r8+18h], rax
-
-        ; Capture old rbp so that the stack overwrite doesn't stomp it
-        mov rax, [rbp] 
-        mov [rsp+40h], rax
-
-        ; Note that neither rcx or rdx has been modified from entry to this point, and are passed straight through to the PlatformIndependentRestore function
-        call PlatformIndependentRestore
-        ; RAX now points at the RegRestore array
-        mov rcx, [rsp+20h + 10h]
-        ; RCX now has the value of the future RSP
-
-        .data
-switchTable     qword    Checkrbx, Checkrbp, Checkrdi, Checkrsi, Checkr12, Checkr13, Checkr14, Checkr15, Checkxmm6, Checkxmm7, Checkxmm8, Checkxmm9, Checkxmm10, Checkxmm11, Checkxmm12, Checkxmm13, Checkxmm14, Checkxmm15, ReturnRegisters, ReturnRegistersNoFrame
-        .code
-RegRestoreLoop:
-    mov edx, [rax]
-    ; edx now has the value of the reg field of the RegRestore structure
-    lea r8, [switchTable]
-    mov r8, [r8 + rdx*8]
-    jmp r8
-ReturnRegistersNoFrame:
-    mov rbp, [rsp + 40h] ; Capture RBP old value from the stored location instead of from the current register (this is because we actually use RBP as part of this helper thunk function)
-ReturnRegisters:
-    mov rax, [rsp+20h + 0h]
-    MOVDQU xmm0, [rsp+20h + 0h]
-    mov rdx, [rsp+20h + 8h] ; Load address of where to jmp into ecx
-    mov rsp, rcx
-    jmp rdx
-Checkrbp:
-    mov edx, [rax+4]
-    mov r10, [rsp + 40h] ; Capture RBP old value from the stored location instead of from the current register (this is because we actually use RBP as part of this helper thunk function)
-    mov rbp, [rcx+rdx]
-    mov [rcx+rdx], r10
-    add rax, 8
-    jmp RegRestoreLoop
-regRestoreCase rbx
-regRestoreCase rdi
-regRestoreCase rsi
-regRestoreCase r12
-regRestoreCase r13
-regRestoreCase r14
-regRestoreCase r15
-regRestoreCaseXmm xmm6
-regRestoreCaseXmm xmm7
-regRestoreCaseXmm xmm8
-regRestoreCaseXmm xmm9
-regRestoreCaseXmm xmm10
-regRestoreCaseXmm xmm11
-regRestoreCaseXmm xmm12
-regRestoreCaseXmm xmm13
-regRestoreCaseXmm xmm14
-regRestoreCaseXmm xmm15
-NESTED_END RuntimeSuspension_ResumeTaskletReferenceReturn, _TEXT
-
-LEAF_ENTRY RuntimeSuspension_ResumeTaskletIntegerRegisterReturn, _TEXT
-        jmp RuntimeSuspension_ResumeTaskletReferenceReturn
-LEAF_END RuntimeSuspension_ResumeTaskletIntegerRegisterReturn, _TEXT
-
-unwindRegCase macro RegisterName
-Unwind&RegisterName&:
-    mov eax, [r8+4]
-    mov RegisterName, [rsp+rax]
-    add r8, 8
-    jmp RegUnwindLoop
-        endm
-
-unwindRegCaseXmm macro RegisterName
-Unwind&RegisterName&:
-    mov eax, [r8+4]
-    movdqu RegisterName, [rsp+rax]
-    add r8, 8
-    jmp RegUnwindLoop
-        endm
-
-NESTED_ENTRY RuntimeSuspension_UnwindToFunctionWithAsyncFrame, _TEXT
-
-;    // Psuedocode...
-;    Tasklet* pCurTasklet = topTasklet;
-;    uintptr_t curSP = SP + offset;
-;    uintptr_t ip = NULL;
-;    for (uint32_t i = 0; i < count; i++)
-;    {
-;        RegRestore* pRegRestore = pCurTasklet->pStackDataInfo->RegistersToRestore;
-;        while (pRegRestore->reg != RegisterToRestore::ReturnRegisters)
-;        {
-;            RestoreReg(pRegRestore);
-;        }
-;        ip = *(uintptr_t*)(curSP + pCurTasklet->pStackDataInfo->ReturnAddressOffset);
-;        curSP += pCurTasklet->pStackDataInfo->StackRequirement;
-;    }
-;    Jmp ip
-
-    alloc_stack             38h
-    END_PROLOGUE
-    mov [rsp+20h], rcx
-    mov [rsp+28h], rdx
-    call ForceThisThreadHasNoHijackForUnwind
-    mov rcx, [rsp+20h]
-    mov rdx, [rsp+28h]
-    add rsp, 40h
-
-; rcx is pCurTasklet
-; rdx is count of tasklets to unwind
-; rsp is current stack pointer
-; rax is the putative new ip
-; rax is also the scratch Register
-; r10 is second scratch register
-; r8 is pRegRestore
-; r9 is pStackDataInfo
-NewTaskletUnwind:
-        mov r9, [rcx+28h] ; Load r9 with the pStackDataInfo value on the current tasklet
-        mov r8, [r9+30h] ; Load r8 with the pRegRestore value
-
-        .data
-unwindSwitchTable     qword    Unwindrbx, Unwindrbp, Unwindrdi, Unwindrsi, Unwindr12, Unwindr13, Unwindr14, Unwindr15, Unwindxmm6, Unwindxmm7, Unwindxmm8, Unwindxmm9, Unwindxmm10, Unwindxmm11, Unwindxmm12, Unwindxmm13, Unwindxmm14, Unwindxmm15, UnwindReturnRegisters, UnwindReturnRegisters
-        .code
-RegUnwindLoop:
-        mov eax, [r8]
-        ; eax now has the value of the reg field of the RegRestore structure
-        lea r10, [unwindSwitchTable]
-        mov r10, [r10 + rax*8]
-        jmp r10
-unwindRegCase rbx
-unwindRegCase rbp
-unwindRegCase rdi
-unwindRegCase rsi
-unwindRegCase r12
-unwindRegCase r13
-unwindRegCase r14
-unwindRegCase r15
-unwindRegCaseXmm xmm6
-unwindRegCaseXmm xmm7
-unwindRegCaseXmm xmm8
-unwindRegCaseXmm xmm9
-unwindRegCaseXmm xmm10
-unwindRegCaseXmm xmm11
-unwindRegCaseXmm xmm12
-unwindRegCaseXmm xmm13
-unwindRegCaseXmm xmm14
-unwindRegCaseXmm xmm15
-UnwindReturnRegisters:
-        ; Load return address into RAX in case we are done
-        mov eax, [r9 + 0ch] ; Put ReturnAddressOffset into rax
-        mov rax, [rsp+rax]
-        
-        ; Adjust rsp to callers rsp
-        mov r10d, [r9] ; Load StackDataInfo.StackRequirement
-        sub r10d, [r9 + 18h] ; Subtract off the CbArgs (stack args amount)
-        add rsp, r10 ; Adjust rsp as requested
-
-        mov rcx, [rcx] ; Adjust to callers tasklet
-        ; Adjust count of tasklets to unwind
-        sub edx, 1
-        jnz NewTaskletUnwind
-TaskletUnwindDone:
-        mov rcx, rax ; Move return address into rcx
-        xor rax, rax ; Clear return register in case it is supposed to be an object reference or something  -- TODO, note that for structure returns this will mean we need to slightly adjust the calling convention so that we don't rely on this being a pointer to the return buffer
-        jmp rcx
-NESTED_END RuntimeSuspension_UnwindToFunctionWithAsyncFrame, _TEXT
-
         end
diff --git a/src/coreclr/vm/ceemain.cpp b/src/coreclr/vm/ceemain.cpp
index d0dede16bc5a9c..1254a83fc2ab1b 100644
--- a/src/coreclr/vm/ceemain.cpp
+++ b/src/coreclr/vm/ceemain.cpp
@@ -210,7 +210,6 @@
 #endif // FEATURE_GDBJIT
 
 #include "genanalysis.h"
-#include "runtimesuspension.h"
 
 static int GetThreadUICultureId(_Out_ LocaleIDValue* pLocale);  // TODO: This shouldn't use the LCID.  We should rely on name instead
 
@@ -735,9 +734,6 @@ void EEStartupHelper()
 
         Frame::Init();
 
-        InitializeTasklets();
-
-
 #ifdef LOGGING
         InitializeLogging();
 #endif
diff --git a/src/coreclr/vm/corelib.cpp b/src/coreclr/vm/corelib.cpp
index b9ff270dd7a6c4..cbdef17fe92055 100644
--- a/src/coreclr/vm/corelib.cpp
+++ b/src/coreclr/vm/corelib.cpp
@@ -72,8 +72,6 @@
 
 #include "tailcallhelp.h"
 
-#include "runtimesuspension.h"
-
 ///////////////////////////////////////////////////////////////////////////////
 //
 // Hardcoded Meta-Sig
diff --git a/src/coreclr/vm/corelib.h b/src/coreclr/vm/corelib.h
index 8b21cbe064f194..4a96b4d5707051 100644
--- a/src/coreclr/vm/corelib.h
+++ b/src/coreclr/vm/corelib.h
@@ -347,30 +347,6 @@ DEFINE_METHOD(TASK, FROM_EXCEPTION_1, FromException, GM_Exception_RetTaskOfT)
 DEFINE_METHOD(TASK, FROM_RESULT_T, FromResult, GM_T_RetTaskOfT)
 DEFINE_METHOD(TASK, GET_COMPLETED_TASK, get_CompletedTask, SM_RetTask)
 
-DEFINE_CLASS(RUNTIME_TASK_STATE_1, Tasks, RuntimeTaskState`1)
-DEFINE_METHOD(RUNTIME_TASK_STATE_1, PUSH, Push, NoSig)
-DEFINE_METHOD(RUNTIME_TASK_STATE_1, POP, Pop, NoSig)
-DEFINE_METHOD(RUNTIME_TASK_STATE_1, FROM_EXCEPTION, FromException, IM_Exception_RetTaskOfT)
-DEFINE_METHOD(RUNTIME_TASK_STATE_1, FROM_RESULT, FromResult, IM_T_RetTaskOfT)
-
-DEFINE_CLASS(RUNTIME_TASK_STATE, Tasks, RuntimeTaskState)
-DEFINE_METHOD(RUNTIME_TASK_STATE, PUSH, Push, NoSig)
-DEFINE_METHOD(RUNTIME_TASK_STATE, POP, Pop, NoSig)
-DEFINE_METHOD(RUNTIME_TASK_STATE, FROM_EXCEPTION, FromException, IM_Exception_RetTask)
-DEFINE_METHOD(RUNTIME_TASK_STATE, FROM_RESULT, FromResult, IM_RetTask)
-
-DEFINE_CLASS(RUNTIME_VALUETASK_STATE_1, Tasks, RuntimeValueTaskState`1)
-DEFINE_METHOD(RUNTIME_VALUETASK_STATE_1, PUSH, Push, NoSig)
-DEFINE_METHOD(RUNTIME_VALUETASK_STATE_1, POP, Pop, NoSig)
-DEFINE_METHOD(RUNTIME_VALUETASK_STATE_1, FROM_EXCEPTION, FromException, IM_Exception_RetValueTaskOfT)
-DEFINE_METHOD(RUNTIME_VALUETASK_STATE_1, FROM_RESULT, FromResult, IM_T_RetValueTaskOfT)
-
-DEFINE_CLASS(RUNTIME_VALUETASK_STATE, Tasks, RuntimeValueTaskState)
-DEFINE_METHOD(RUNTIME_VALUETASK_STATE, PUSH, Push, NoSig)
-DEFINE_METHOD(RUNTIME_VALUETASK_STATE, POP, Pop, NoSig)
-DEFINE_METHOD(RUNTIME_VALUETASK_STATE, FROM_EXCEPTION, FromException, IM_Exception_RetValueTask)
-DEFINE_METHOD(RUNTIME_VALUETASK_STATE, FROM_RESULT, FromResult, IM_RetValueTask)
-
 DEFINE_CLASS(TASK_AWAITER_1, CompilerServices, TaskAwaiter`1)
 DEFINE_METHOD(TASK_AWAITER_1, GET_ISCOMPLETED, get_IsCompleted, NoSig)
 DEFINE_METHOD(TASK_AWAITER_1, GET_RESULT, GetResult, NoSig)
diff --git a/src/coreclr/vm/ecalllist.h b/src/coreclr/vm/ecalllist.h
index 11c3d51d9b2a25..1ea05d6f692d88 100644
--- a/src/coreclr/vm/ecalllist.h
+++ b/src/coreclr/vm/ecalllist.h
@@ -357,9 +357,6 @@ FCFuncStart(gRuntimeHelpers)
     FCFuncElement("GetTailCallInfo", TailCallHelp::GetTailCallInfo)
     FCFuncElement("Box", JIT_Box)
     FCFuncElement("Unbox_Nullable", JIT_Unbox_Nullable)
-    FCFuncElement("UnwindToFunctionWithAsyncFrame", RuntimeSuspension_UnwindToFunctionWithAsyncFrame)
-    FCFuncElement("ResumeTaskletReferenceReturn", RuntimeSuspension_ResumeTaskletReferenceReturn)
-    FCFuncElement("ResumeTaskletIntegerRegisterReturn", RuntimeSuspension_ResumeTaskletIntegerRegisterReturn)
 FCFuncEnd()
 
 FCFuncStart(gMethodTableFuncs)
diff --git a/src/coreclr/vm/eeconfig.cpp b/src/coreclr/vm/eeconfig.cpp
index 872746d56c21d8..779f7a841fc9d4 100644
--- a/src/coreclr/vm/eeconfig.cpp
+++ b/src/coreclr/vm/eeconfig.cpp
@@ -758,9 +758,6 @@ HRESULT EEConfig::sync()
 
     backpatchEntryPointSlots = CLRConfig::GetConfigValue(CLRConfig::UNSUPPORTED_BackpatchEntryPointSlots) != 0;
 
-    runtimeAsyncViaJitGeneratedStateMachines = CLRConfig::GetConfigValue(CLRConfig::EXTERNAL_RuntimeAsyncViaJitGeneratedStateMachines) != 0;
-    taskletAging = CLRConfig::GetConfigValue(CLRConfig::EXTERNAL_TaskletAging) != 0;
-
 #if defined(FEATURE_GDBJIT) && defined(_DEBUG)
     {
         LPWSTR pszGDBJitElfDumpW = NULL;
diff --git a/src/coreclr/vm/eeconfig.h b/src/coreclr/vm/eeconfig.h
index 360425dbb7e258..db35f09f7433e7 100644
--- a/src/coreclr/vm/eeconfig.h
+++ b/src/coreclr/vm/eeconfig.h
@@ -112,9 +112,6 @@ class EEConfig
 
     bool          BackpatchEntryPointSlots() const { LIMITED_METHOD_CONTRACT; return backpatchEntryPointSlots; }
 
-    bool          RuntimeAsyncViaJitGeneratedStateMachines() const { LIMITED_METHOD_CONTRACT; return runtimeAsyncViaJitGeneratedStateMachines; }
-    bool          TaskletAging() const { LIMITED_METHOD_CONTRACT; return taskletAging; }
-
 #if defined(FEATURE_GDBJIT) && defined(_DEBUG)
     inline bool ShouldDumpElfOnMethod(LPCUTF8 methodName) const
     {
@@ -647,7 +644,6 @@ class EEConfig
 #endif
 
     bool runtimeAsyncViaJitGeneratedStateMachines;
-    bool taskletAging;
 public:
 
     enum BitForMask {
diff --git a/src/coreclr/vm/gccover.cpp b/src/coreclr/vm/gccover.cpp
index 2154ae46d7fdc7..4fe4961e86c3a0 100644
--- a/src/coreclr/vm/gccover.cpp
+++ b/src/coreclr/vm/gccover.cpp
@@ -277,13 +277,10 @@ void ReplaceInstrAfterCall(PBYTE instrToReplace, MethodDesc* callMD)
     }
     _ASSERTE(IsValidReturnKind(returnKind));
 
-    if (g_pConfig->RuntimeAsyncViaJitGeneratedStateMachines())
+    if (callMD->IsAsync2Method() || (callMD->IsIntrinsic() && IsSpecialCaseAsyncRet(callMD)))
     {
-        if (callMD->IsAsync2Method() || (callMD->IsIntrinsic() && IsSpecialCaseAsyncRet(callMD)))
-        {
-            // Cannot encode requirement to protect async ret after call.
-            return;
-        }
+        // Cannot encode requirement to protect async ret after call.
+        return;
     }
 
     bool ispointerKind = IsPointerReturnKind(returnKind);
diff --git a/src/coreclr/vm/gcenv.ee.cpp b/src/coreclr/vm/gcenv.ee.cpp
index 643d1dd89fd6eb..48e4ee0fb1c297 100644
--- a/src/coreclr/vm/gcenv.ee.cpp
+++ b/src/coreclr/vm/gcenv.ee.cpp
@@ -26,8 +26,6 @@
 #include "genanalysis.h"
 #include "eventpipeadapter.h"
 
-#include "runtimesuspension.h"
-
 // Finalizes a weak reference directly.
 extern void FinalizeWeakReference(Object* obj);
 
@@ -330,11 +328,6 @@ void GCToEEInterface::GcScanRoots(promote_func* fn, int condemned, int max_gen,
             SystemDomain::EnumAllStaticGCRefs(fn, sc);
         }
     }
-
-#ifndef DACCESS_COMPILE
-// TODO Make tasklet reporting DAC friendly
-    IterateTaskletsForGC(fn, condemned, sc);
-#endif
 }
 
 void GCToEEInterface::GcStartWork (int condemned, int max_gen)
@@ -431,30 +424,6 @@ void GCToEEInterface::SyncBlockCachePromotionsGranted(int max_gen)
     SyncBlockCache::GetSyncBlockCache()->GCDone(FALSE, max_gen);
 }
 
-void GCToEEInterface::TaskletPromotionsGranted(int condemned, int max_gen, ScanContext* sc)
-{
-    CONTRACTL
-    {
-        NOTHROW;
-        GC_NOTRIGGER;
-    }
-    CONTRACTL_END;
-
-    AgeTasklets(condemned, max_gen, sc);
-}
-
-void GCToEEInterface::TaskletDemote(int condemned, int max_gen, ScanContext* sc)
-{
-    CONTRACTL
-    {
-        NOTHROW;
-        GC_NOTRIGGER;
-    }
-    CONTRACTL_END;
-
-    RejuvenateTasklets(condemned, max_gen, sc);
-}
-
 uint32_t GCToEEInterface::GetActiveSyncBlockCount()
 {
     CONTRACTL
@@ -1861,4 +1830,4 @@ void GCToEEInterface::LogErrorToHost(const char *message)
 uint64_t GCToEEInterface::GetThreadOSThreadId(Thread* thread)
 {
     return thread->GetOSThreadId64();
-}
\ No newline at end of file
+}
diff --git a/src/coreclr/vm/method.hpp b/src/coreclr/vm/method.hpp
index d08f31274def96..ea745041e381a7 100644
--- a/src/coreclr/vm/method.hpp
+++ b/src/coreclr/vm/method.hpp
@@ -1829,13 +1829,12 @@ class MethodDesc
             return false;
         auto asyncType = GetAddrOfAsyncMethodData()->type;
 
-        return asyncType == AsyncMethodType::Async ||
-               (asyncType == AsyncMethodType::AsyncToTask && !g_pConfig->RuntimeAsyncViaJitGeneratedStateMachines());
+        return asyncType == AsyncMethodType::Async;
     }
 
     inline bool RequiresAsyncContinuationArg()
     {
-        return IsAsync2Method() && g_pConfig->RuntimeAsyncViaJitGeneratedStateMachines();
+        return IsAsync2Method();
     }
 
     inline bool HasAsyncMethodData()
@@ -1994,7 +1993,6 @@ class MethodDesc
 
     bool TryGenerateAsyncThunk(DynamicResolver** resolver, COR_ILMETHOD_DECODER** methodILDecoder);
     bool TryGenerateUnsafeAccessor(DynamicResolver** resolver, COR_ILMETHOD_DECODER** methodILDecoder);
-    void EmitUnwindingBasedRuntimeAsyncThunk(MethodDesc* pAsyncOtherVariant, MetaSig& msig, ILStubLinker* pSL);
     void EmitJitStateMachineBasedRuntimeAsyncThunk(MethodDesc* pAsyncOtherVariant, MetaSig& thunkMsig, ILStubLinker* pSL);
     void EmitAsync2MethodThunk(MethodDesc* pAsyncOtherVariant, MetaSig& msig, ILStubLinker* pSL);
 public:
diff --git a/src/coreclr/vm/prestub.cpp b/src/coreclr/vm/prestub.cpp
index 5ee7e1b625b5b0..ebf46e71a376e3 100644
--- a/src/coreclr/vm/prestub.cpp
+++ b/src/coreclr/vm/prestub.cpp
@@ -1758,14 +1758,7 @@ bool MethodDesc::TryGenerateAsyncThunk(DynamicResolver** resolver, COR_ILMETHOD_
 
     if (IsAsyncTaskMethodTaskReturningMethod(asyncType))
     {
-        if (!g_pConfig->RuntimeAsyncViaJitGeneratedStateMachines())
-        {
-            EmitUnwindingBasedRuntimeAsyncThunk(pAsyncOtherVariant, msig, &sl);
-        }
-        else
-        {
-            EmitJitStateMachineBasedRuntimeAsyncThunk(pAsyncOtherVariant, msig, &sl);
-        }
+        EmitJitStateMachineBasedRuntimeAsyncThunk(pAsyncOtherVariant, msig, &sl);
     }
     else
     {
@@ -1810,190 +1803,6 @@ bool MethodDesc::TryGenerateAsyncThunk(DynamicResolver** resolver, COR_ILMETHOD_
     return true;
 }
 
-void MethodDesc::EmitUnwindingBasedRuntimeAsyncThunk(MethodDesc* pAsyncOtherVariant, MetaSig& msig, ILStubLinker* pSL)
-{
-    ILCodeStream* pCode = pSL->NewCodeStream(ILStubLinker::kDispatch);
-
-    TypeHandle thTaskRet = msig.GetRetTypeHandleThrowing();
-    bool isGeneric = thTaskRet.GetMethodTable()->HasInstantiation();
-
-    TypeHandle thLogicalRetType;
-    if (isGeneric)
-        thLogicalRetType = thTaskRet.GetMethodTable()->GetInstantiation()[0];
-    else
-        thLogicalRetType = TypeHandle(CoreLibBinder::GetClass(CLASS__VOID));
-
-    bool isValueTask = thTaskRet.GetMethodTable()->IsValueType();
-
-    BinderClassID runtimeTaskStateClassId;
-    BinderMethodID runtimeTaskStatePush;
-    BinderMethodID runtimeTaskStatePop;
-    BinderMethodID runtimeTaskStateFromResult;
-    BinderMethodID runtimeTaskStateFromException;
-
-    if (isValueTask)
-    {
-        if (isGeneric)
-        {
-            runtimeTaskStateClassId = CLASS__RUNTIME_VALUETASK_STATE_1;
-            runtimeTaskStatePush = METHOD__RUNTIME_VALUETASK_STATE_1__PUSH;
-            runtimeTaskStatePop = METHOD__RUNTIME_VALUETASK_STATE_1__POP;
-            runtimeTaskStateFromResult = METHOD__RUNTIME_VALUETASK_STATE_1__FROM_RESULT;
-            runtimeTaskStateFromException = METHOD__RUNTIME_VALUETASK_STATE_1__FROM_EXCEPTION;
-        }
-        else
-        {
-            runtimeTaskStateClassId = CLASS__RUNTIME_VALUETASK_STATE;
-            runtimeTaskStatePush = METHOD__RUNTIME_VALUETASK_STATE__PUSH;
-            runtimeTaskStatePop = METHOD__RUNTIME_VALUETASK_STATE__POP;
-            runtimeTaskStateFromResult = METHOD__RUNTIME_VALUETASK_STATE__FROM_RESULT;
-            runtimeTaskStateFromException = METHOD__RUNTIME_VALUETASK_STATE__FROM_EXCEPTION;
-        }
-    }
-    else
-    {
-        if (isGeneric)
-        {
-            runtimeTaskStateClassId = CLASS__RUNTIME_TASK_STATE_1;
-            runtimeTaskStatePush = METHOD__RUNTIME_TASK_STATE_1__PUSH;
-            runtimeTaskStatePop = METHOD__RUNTIME_TASK_STATE_1__POP;
-            runtimeTaskStateFromResult = METHOD__RUNTIME_TASK_STATE_1__FROM_RESULT;
-            runtimeTaskStateFromException = METHOD__RUNTIME_TASK_STATE_1__FROM_EXCEPTION;
-        }
-        else
-        {
-            runtimeTaskStateClassId = CLASS__RUNTIME_TASK_STATE;
-            runtimeTaskStatePush = METHOD__RUNTIME_TASK_STATE__PUSH;
-            runtimeTaskStatePop = METHOD__RUNTIME_TASK_STATE__POP;
-            runtimeTaskStateFromResult = METHOD__RUNTIME_TASK_STATE__FROM_RESULT;
-            runtimeTaskStateFromException = METHOD__RUNTIME_TASK_STATE__FROM_EXCEPTION;
-        }
-    }
-
-    MethodTable* pMTRuntimeTaskStateOpen = CoreLibBinder::GetClass(runtimeTaskStateClassId);
-    MethodTable* pMTRuntimeTaskState;
-    
-    if (isGeneric)
-    {
-        pMTRuntimeTaskState = ClassLoader::LoadGenericInstantiationThrowing(pMTRuntimeTaskStateOpen->GetModule(), pMTRuntimeTaskStateOpen->GetCl(), Instantiation(&thLogicalRetType, 1)).GetMethodTable();
-    }
-    else
-    {
-        pMTRuntimeTaskState = pMTRuntimeTaskStateOpen;
-    }
-
-
-    LocalDesc rtsLocalDesc(pMTRuntimeTaskState);
-    DWORD rtsLocal = pCode->NewLocal(rtsLocalDesc);
-
-    LocalDesc resultLocalDesc(thLogicalRetType);
-    DWORD resultLocal;
-    if (isGeneric)
-    {
-        resultLocal = pCode->NewLocal(resultLocalDesc);
-    }
-    else
-    {
-        resultLocal = UINT32_MAX;
-    }
-
-    LocalDesc exceptionLocalDesc(CoreLibBinder::GetClass(CLASS__EXCEPTION));
-    DWORD exceptionLocal = pCode->NewLocal(exceptionLocalDesc);
-
-    LocalDesc returnLocalDesc(thTaskRet);
-    DWORD returnLocal = pCode->NewLocal(returnLocalDesc);
-
-    auto pNoExceptionLabel = pCode->NewCodeLabel();
-    auto pReturnResultLabel = pCode->NewCodeLabel();
-
-    pCode->EmitLDLOCA(rtsLocal);
-    pCode->EmitINITOBJ(pCode->GetToken(pMTRuntimeTaskState));
-    if (isGeneric)
-    {
-        pCode->EmitLDLOCA(resultLocal);
-        pCode->EmitINITOBJ(pCode->GetToken(thLogicalRetType));
-    }
-    pCode->EmitLDLOCA(exceptionLocal);
-    pCode->EmitINITOBJ(pCode->GetToken(CoreLibBinder::GetClass(CLASS__EXCEPTION)));
-    pCode->EmitLDLOCA(returnLocal);
-    pCode->EmitINITOBJ(pCode->GetToken(thTaskRet));
-    pCode->EmitLDLOCA(rtsLocal);
-    if (isGeneric)
-        pCode->EmitCALL(pCode->GetToken(pMTRuntimeTaskState->GetParallelMethodDesc(CoreLibBinder::GetMethod(runtimeTaskStatePush))), 1, 0);
-    else
-        pCode->EmitCALL(pCode->GetToken(CoreLibBinder::GetMethod(runtimeTaskStatePush)), 1, 0);
-
-    {
-        pCode->BeginTryBlock();
-        pCode->EmitNOP("Separate try blocks");
-        {
-            pCode->BeginTryBlock();
-
-            DWORD localArg = 0;
-            if (msig.HasThis())
-            {
-                // Struct async thunks not yet implemented
-                _ASSERTE(!this->GetMethodTable()->IsValueType());
-                pCode->EmitLDARG(localArg++);
-            }
-            for (UINT iArg = 0; iArg < msig.NumFixedArgs(); iArg++)
-            {
-                pCode->EmitLDARG(localArg++);
-            }
-            pCode->EmitCALL(pCode->GetToken(pAsyncOtherVariant), localArg, isGeneric ? 1 : 0);
-            if (isGeneric)
-            {
-                pCode->EmitSTLOC(resultLocal);
-            }
-            pCode->EmitLEAVE(pNoExceptionLabel);
-            pCode->EndTryBlock();
-        }
-        // Catch
-        {
-            pCode->BeginCatchBlock(pCode->GetToken(CoreLibBinder::GetClass(CLASS__EXCEPTION)));
-            pCode->EmitSTLOC(exceptionLocal);
-            pCode->EmitLDLOCA(rtsLocal);
-            pCode->EmitLDLOC(exceptionLocal);
-            if (isGeneric)
-                pCode->EmitCALL(pCode->GetToken(pMTRuntimeTaskState->GetParallelMethodDesc(CoreLibBinder::GetMethod(runtimeTaskStateFromException))), 2, 1);
-            else
-                pCode->EmitCALL(pCode->GetToken(CoreLibBinder::GetMethod(runtimeTaskStateFromException)), 2, 1);
-
-            pCode->EmitSTLOC(returnLocal);
-            pCode->EmitLEAVE(pReturnResultLabel);
-            pCode->EndCatchBlock();
-        }
-        pCode->EmitLabel(pNoExceptionLabel);
-        pCode->EmitLDLOCA(rtsLocal);
-        if (isGeneric)
-        {
-            pCode->EmitLDLOC(resultLocal);
-            pCode->EmitCALL(pCode->GetToken(pMTRuntimeTaskState->GetParallelMethodDesc(CoreLibBinder::GetMethod(runtimeTaskStateFromResult))), 2, 1);
-        }
-        else
-        {
-            pCode->EmitCALL(pCode->GetToken(CoreLibBinder::GetMethod(runtimeTaskStateFromResult)), 1, 1);
-        }
-        pCode->EmitSTLOC(returnLocal);
-        pCode->EmitLEAVE(pReturnResultLabel);
-        pCode->EndTryBlock();
-    }
-    // Finally
-    {
-        pCode->BeginFinallyBlock();
-        pCode->EmitLDLOCA(rtsLocal);
-        if (isGeneric)
-            pCode->EmitCALL(pCode->GetToken(pMTRuntimeTaskState->GetParallelMethodDesc(CoreLibBinder::GetMethod(runtimeTaskStatePop))), 1, 0);
-        else
-            pCode->EmitCALL(pCode->GetToken(CoreLibBinder::GetMethod(runtimeTaskStatePop)), 1, 0);
-        pCode->EmitENDFINALLY();
-        pCode->EndFinallyBlock();
-    }
-    pCode->EmitLabel(pReturnResultLabel);
-    pCode->EmitLDLOC(returnLocal);
-    pCode->EmitRET();
-}
-
 void MethodDesc::EmitJitStateMachineBasedRuntimeAsyncThunk(MethodDesc* pAsyncOtherVariant, MetaSig& thunkMsig, ILStubLinker* pSL)
 {
     ILCodeStream* pCode = pSL->NewCodeStream(ILStubLinker::kDispatch);
diff --git a/src/coreclr/vm/qcallentrypoints.cpp b/src/coreclr/vm/qcallentrypoints.cpp
index f7b9cdde105383..356ce7a1792d40 100644
--- a/src/coreclr/vm/qcallentrypoints.cpp
+++ b/src/coreclr/vm/qcallentrypoints.cpp
@@ -73,8 +73,6 @@
 
 #include "exceptionhandlingqcalls.h"
 
-#include "runtimesuspension.h"
-
 static const Entry s_QCall[] =
 {
     DllImportEntry(ArgIterator_Init)
@@ -85,9 +83,6 @@ static const Entry s_QCall[] =
     DllImportEntry(CustomAttribute_ParseAttributeUsageAttribute)
     DllImportEntry(CustomAttribute_CreateCustomAttributeInstance)
     DllImportEntry(CustomAttribute_CreatePropertyOrFieldData)
-    DllImportEntry(RuntimeSuspension_RegisterTasklet)
-    DllImportEntry(RuntimeSuspension_DeleteTasklet)
-    DllImportEntry(RuntimeSuspension_CaptureTasklets)
     DllImportEntry(Enum_GetValuesAndNames)
     DllImportEntry(DebugDebugger_Break)
     DllImportEntry(DebugDebugger_Launch)
diff --git a/src/coreclr/vm/runtimesuspension.cpp b/src/coreclr/vm/runtimesuspension.cpp
deleted file mode 100644
index 73ae38663dd2a4..00000000000000
--- a/src/coreclr/vm/runtimesuspension.cpp
+++ /dev/null
@@ -1,820 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-
-#include "common.h"
-#include "runtimesuspension.h"
-#include "siginfo.hpp"
-
-enum class TaskletReturnType : int32_t
-{
-    // These return types are OS/architecture specific. For instance, Arm64 supports returning structs in a register pair. This is also incomplete and doesn't handle floating point, vector registers, etc.
-    Integer,
-    ObjectReference,
-    ByReference
-};
-
-enum RegisterToRestore
-{
-    Rbx,
-    Rbp,
-    Rdi,
-    Rsi,
-    R12,
-    R13,
-    R14,
-    R15,
-    Xmm6,
-    Xmm7,
-    Xmm8,
-    Xmm9,
-    Xmm10,
-    Xmm11,
-    Xmm12,
-    Xmm13,
-    Xmm14,
-    Xmm15,
-    ReturnRegisters, // End sequence marker. Associated offset is for the return register, but the actual return value is stashed after the register data
-    ReturnRegistersNoFrame, // End sequence marker. Associated offset is for the return register, but the actual return value is stashed after the register data
-};
-
-struct RegRestore
-{
-    RegisterToRestore reg;
-    uint32_t offset;
-};
-
-struct StackDataInfo
-{
-    void CleanupStackDataInfo()
-    {
-        if (ByRefOffsets != NULL)
-            free(ByRefOffsets);
-        if (ObjectRefOffsets != NULL)
-            free(ObjectRefOffsets);
-        if (RegistersToRestore != NULL)
-            free(RegistersToRestore);
-    }
-    uint32_t StackRequirement;
-    uint32_t UnrecordedDataSize; // From the restored RSP to the data chunk, how many bytes are skipped
-    uint32_t StackDataSize;
-    uint32_t ReturnAddressOffset;
-    uint32_t cByRefs;
-    uint32_t cObjectRefs;
-    uint32_t CbArgs;
-    int32_t* ByRefOffsets = NULL; // Negative numbers indicate pinned byrefs
-    uint32_t* ObjectRefOffsets = NULL;
-    RegRestore* RegistersToRestore = NULL;
-};
-
-struct Tasklet
-{
-    Tasklet* pTaskletNextInStack;
-    Tasklet* pTaskletNextInLiveList;
-    Tasklet* pTaskletPrevInLiveList;
-    uint8_t* pStackData;
-    uintptr_t restoreIPAddress;
-    StackDataInfo* pStackDataInfo;
-    TaskletReturnType taskletReturnType;
-    // min generation of all managed objects referred from this frame.
-    // -1 means the frame is a part of actiely executing stack and may have byrefs pointing to it
-    int32_t  minGeneration;
-    Tasklet* pTaskletPrevInStack;
-};
-
-struct RuntimeAsyncReturnValue
-{
-    uintptr_t _obj;
-    uintptr_t _ptr;
-    TaskletReturnType _returnType;
-};
-
-struct ByRefAdjustment
-{
-    uint8_t* pOldLocation;
-    uint32_t Size;
-    uintptr_t Adjustment;
-};
-
-bool RelocAtAddress(ByRefAdjustment* pAdjustment, uintptr_t* pData);
-void RelocAtAddress(ByRefAdjustment* pAdjustment, uint8_t* pDataToAdjustAddress)
-{
-    uintptr_t *pData = (uintptr_t*)pDataToAdjustAddress;
-    RelocAtAddress(pAdjustment, pData);
-}
-
-bool RelocAtAddress(ByRefAdjustment* pAdjustment, uintptr_t* pData)
-{
-    if ((*pData >= (uintptr_t)pAdjustment->pOldLocation) && ((*pData - (uintptr_t)pAdjustment->pOldLocation) < (uintptr_t)pAdjustment->Size))
-    {
-        *pData += pAdjustment->Adjustment;
-        return true;
-    }
-    return false;
-}
-
-struct RestoreFunctionLocals
-{
-    // Anonymous union of data for return value registers
-    union 
-    {
-        uintptr_t integerRegister;
-    };
-    uintptr_t AddressInMethodToRestoreTo;
-private:
-    uint8_t *pFutureRSPLocation;
-    uintptr_t ReturnAddress;
-public:
-    uint8_t *GetFutureRSPLocation() { return pFutureRSPLocation; }  // TODO This should be some simple math off of the this pointer
-    uintptr_t GetReturnAddress() { return ReturnAddress; }  // TODO This should be some simple math off of the this pointer
-};
-
-// The actual restoration function which needs to be written in assembly, sets up enough of a frame on top of the current RSP to call this function
-// Then, once this function returns, iterates through the RegRestore to change over the saved registers to the new values
-extern "C" RegRestore* PlatformIndependentRestore(Tasklet* tasklet, RuntimeAsyncReturnValue* returnValueToFillIn, RestoreFunctionLocals *restoreLocals)
-{
-    // Compute what needs to be adjusted in terms of byrefs for 
-    ByRefAdjustment adjustment;
-    adjustment.pOldLocation = tasklet->pStackData;
-    adjustment.Size = tasklet->pStackDataInfo->StackDataSize;
-
-    uint8_t* pNewLocation = restoreLocals->GetFutureRSPLocation() + tasklet->pStackDataInfo->UnrecordedDataSize;
-    adjustment.Adjustment = ((uintptr_t)pNewLocation) - ((uintptr_t)adjustment.pOldLocation);
-
-    // Adjust all pointers to the stack data that we are about to move.
-    // NB: We only check refs from the current frame.
-    //     If we have references from the caller frames, something has gone horribly bad already.
-    Tasklet *pTaskletToAdjustByrefsOn = tasklet;
-    uint32_t cByRefs = pTaskletToAdjustByrefsOn->pStackDataInfo->cByRefs;
-    uint32_t* byRefOffsets = (uint32_t*)pTaskletToAdjustByrefsOn->pStackDataInfo->ByRefOffsets;
-    uint8_t* taskletData = pTaskletToAdjustByrefsOn->pStackData;
-    for (uint32_t iByRef = 0; iByRef < cByRefs; iByRef++)
-    {
-        RelocAtAddress(&adjustment, taskletData + byRefOffsets[iByRef]);
-    }
-
-    // Mark stacklets as "active" so they no longer age.
-    // this is only needed as long as we allow cross-frame byrefs
-    // as such byrefs may allow a calee to chage min age of a caller.
-    if (tasklet->minGeneration >= 0 && tasklet->pStackDataInfo->cByRefs > 0)
-    {
-        Tasklet* pTaskletToMarkActive = tasklet;
-        do
-        {
-            pTaskletToMarkActive->minGeneration = -1;
-            pTaskletToMarkActive = pTaskletToMarkActive->pTaskletNextInStack;
-        } while (pTaskletToMarkActive != NULL);
-    }
-
-    StackDataInfo *pStackDataInfo = tasklet->pStackDataInfo;
-
-    // Copy most of the memory
-    memcpy(restoreLocals->GetFutureRSPLocation() + pStackDataInfo->UnrecordedDataSize, tasklet->pStackData, pStackDataInfo->StackDataSize);
-
-    // Update the return address on the stack
-    *(uintptr_t *)(restoreLocals->GetFutureRSPLocation() + pStackDataInfo->ReturnAddressOffset) = restoreLocals->GetReturnAddress();
-
-    restoreLocals->AddressInMethodToRestoreTo = tasklet->restoreIPAddress;
-
-    switch (returnValueToFillIn->_returnType)
-    {
-        case TaskletReturnType::Integer:
-            restoreLocals->integerRegister = returnValueToFillIn->_ptr;
-            break;
-        case TaskletReturnType::ObjectReference:
-            restoreLocals->integerRegister = returnValueToFillIn->_obj;
-            break;
-
-        default:
-            // Not yet implemented
-            _ASSERTE(FALSE);
-    }
-
-    return pStackDataInfo->RegistersToRestore;
-
-    // The rest of the code will do something like...
-    // This is written for X86 which is ... not the initial target
-/*
-    mov ecx, [eax + offsetof(pStackData)]
-    mov edx, [eax + offsetof(pStackDataInfo)]
-    mov edx, [eax + offsetof(RegistersToRestore)] ; EDX now points at a list of registers to restore, terminated by the RegisterToRestore::ReturnRegisters value
-    add ecx, [edx + offsetof(StackDataSize)] ; ECX now points at the data to copy into the various restored register
-    ; We have eax as a scratch register if we need it
-        .data
-tbl     dq      ReturnRegisters, CheckEBX, CheckESI, ...            ;table 
-        .code
-RegRestoreLoop:
-    mov eax, [edx]
-    jmp [tbl + eax*4]
-ReturnRegisters:
-    mov eax, [esp-X]
-    mov edx, [esp-X+4]
-    MOVDQU xmm0, [esp-X]
-    mov ecx, [esp-Y] ; Load address of where to jmp into ecx
-    add esp, Z
-    jmp ecx
-    // Repeat for every saved register type
-CheckEBX:
-    cmp EBX, [edx]
-    mov eax, [edx+4]
-    mov [edx + 4], ebx
-    mov ebx, eax
-    add edx, 8
-    jmp RegRestoreLoop
-
-CheckRBX:
-
-    ends with
-    cmp 
-*/
-}
-
-struct TaskletCaptureData
-{
-    bool inRunOfAsyncMethods;
-    int framesCaptured = 0;
-    StackCrawlMark* stackMark;
-    Tasklet* firstTasklet = NULL;
-    Tasklet* lastTasklet = NULL;
-    uintptr_t stackLimit;
-    uintptr_t stackToIgnoreFromPreviousFrame = 0; // Portions of the stack which have been copied into a previous frame
-    uintptr_t returnStructSize = 0;
-    CQuickArrayList<uintptr_t *> ActiveByRefsToStack;
-    void AddCopiedByRef(uintptr_t currentStackTop, uintptr_t *newByRef)
-    {
-        if ((*newByRef >= currentStackTop) && (*newByRef < stackLimit))
-        {
-            ActiveByRefsToStack.PushNoThrow(newByRef);
-            // TODO PushNoThrow has a return value
-        }
-    }
-
-    void ApplyByRefRelocsToNewlyAllocatedTasklet(ByRefAdjustment adjustment)
-    {
-        if (ActiveByRefsToStack.Size() > 0)
-        {
-            for (SIZE_T iByRef = ActiveByRefsToStack.Size(); iByRef > 0;)
-            {
-                iByRef--;
-
-                if (RelocAtAddress(&adjustment, ActiveByRefsToStack[iByRef]))
-                {
-                    // ByRef was reloc'd, and therefore doesn't need to be handled anymore
-                    ActiveByRefsToStack[iByRef] = ActiveByRefsToStack[ActiveByRefsToStack.Size() - 1];
-                    ActiveByRefsToStack.Pop();
-                }
-            }
-        }
-    }
-};
-
-struct RuntimeSuspensionEnumData
-{
-    RuntimeSuspensionEnumData(const CQuickArrayList<RegRestore>& restoreRegLocations, REGDISPLAY* pRD_, TaskletCaptureData *taskletCaptureData, CrawlFrame *_pCf, StackDataInfo* _pStackDataInfo, uint8_t* _pStackData) :
-        RestoreRegLocations(restoreRegLocations),
-        pRD(pRD_),
-        pTaskletCaptureData(taskletCaptureData),
-        pCf(_pCf),
-        pStackDataInfo(_pStackDataInfo),
-        pStackData(_pStackData)
-    {
-    }
-
-    CQuickArrayList<uint32_t> ObjectRefOffsets;
-    CQuickArrayList<int32_t> ByRefOffsets;
-    const CQuickArrayList<RegRestore>& RestoreRegLocations;
-    REGDISPLAY* pRD;
-    TaskletCaptureData *pTaskletCaptureData;
-    CrawlFrame *pCf;
-    StackDataInfo* pStackDataInfo;
-    uint8_t* pStackData;
-
-    uint32_t GetOffsetForReg(RegisterToRestore nonVolatileReg)
-    {
-        for (SIZE_T i = 0; i < RestoreRegLocations.Size(); i++)
-        {
-            if (RestoreRegLocations[i].reg == nonVolatileReg)
-            {
-                return RestoreRegLocations[i].offset;
-            }
-        }
-        _ASSERTE(!"Attempting to get offset for reg which doesn't have an offset recorded!");
-        return 0;
-    }
-};
-
-StackWalkAction CaptureTaskletsCore(CrawlFrame* pCf, VOID* data)
-{
-    CONTRACTL
-    {
-        THROWS;
-;        GC_NOTRIGGER;
-        MODE_COOPERATIVE;
-        INJECT_FAULT(COMPlusThrowOM(););
-    }
-    CONTRACTL_END;
-
-
-    MethodDesc *pFunc = pCf->GetFunction();
-
-    /* We asked to be called back only for functions */
-    _ASSERTE(pFunc);
-
-    TaskletCaptureData* taskletCaptureData = (TaskletCaptureData*) data;
-
-    // Ignore any frames before we get to the interesting methods
-    if (!pCf->IsFrameless())
-    {
-        _ASSERTE(taskletCaptureData->firstTasklet == NULL); // This should only happen for cases before we get to interesting components of the stack
-        return SWA_CONTINUE;
-    }
-
-    if (taskletCaptureData->firstTasklet == NULL)
-    {
-        if (!IsInCurrentFrame(pCf->GetRegisterSet(), taskletCaptureData->stackMark))
-        {
-            // Move to next frame, we haven't found anything
-            return SWA_CONTINUE;
-        }
-    }
-    else if (!pCf->GetFunction()->IsAsync2Method())
-    {
-        // We must be in the wrapper thunk
-        _ASSERTE(pCf->GetFunction()->IsAsyncThunkMethod() || (strcmp(pCf->GetFunction()->GetName(), "ResumptionFunc") == 0));
-
-        if (taskletCaptureData->ActiveByRefsToStack.Size() != 0)
-        {
-            // We must have to deal with a return value managed as a byref
-            _ASSERTE(!"This is not yet implemented, the thought is to require that the return address be stored somewhere with a pointer to the start of the return value, and then scan the ActiveByRefsToStack to find the lowest address, and treat that as where to find the start of the return buffer. Or maybe we should record it somewhere?");
-        }
-        return SWA_ABORT;
-    }
-    
-
-    uint8_t* pTopOfStackInFunction = (uint8_t*)pCf->GetRegisterSet()->SP;
-    uint32_t sizeofArgStack = (uint32_t)pCf->GetFunction()->SizeOfArgStack();
-#ifdef TARGET_AMD64  // AND Target windows
-    sizeofArgStack = std::max(sizeofArgStack, 32u); // On Windows X64 there is always a parameter area of 32 bytes, which is for use by the called function
-#endif
-    uint8_t* pBottomOfStackInFunction = (uint8_t*)::GetSP(pCf->GetRegisterSet()->pCallerContext) + sizeofArgStack;
-    uint32_t sizeofEntireMeaningfulStack = (uint32_t)(pBottomOfStackInFunction - pTopOfStackInFunction);
-    sizeofEntireMeaningfulStack -= (uint32_t)taskletCaptureData->stackToIgnoreFromPreviousFrame;
-
-    uint8_t *pStackData = (uint8_t*)malloc(sizeofEntireMeaningfulStack);
-    memcpy(pStackData, pTopOfStackInFunction + taskletCaptureData->stackToIgnoreFromPreviousFrame, sizeofEntireMeaningfulStack);
-
-    ByRefAdjustment byrefAdjustment;
-    byrefAdjustment.pOldLocation = pTopOfStackInFunction + taskletCaptureData->stackToIgnoreFromPreviousFrame;
-    byrefAdjustment.Adjustment = ((uintptr_t)pStackData) - ((uintptr_t)byrefAdjustment.pOldLocation);
-    byrefAdjustment.Size = sizeofEntireMeaningfulStack;
-
-    StackDataInfo stackDataInfo;
-    stackDataInfo.StackRequirement = (uint32_t)(::GetSP(pCf->GetRegisterSet()->pCallerContext) - pCf->GetRegisterSet()->SP) + sizeofArgStack;
-    stackDataInfo.StackDataSize = sizeofEntireMeaningfulStack;
-    stackDataInfo.UnrecordedDataSize = (uint32_t)taskletCaptureData->stackToIgnoreFromPreviousFrame;
-
-#if defined(TARGET_X86) || defined(TARGET_AMD64)
-    uintptr_t returnAddressLocation = (uintptr_t) (EECodeManager::GetCallerSp(pCf->GetRegisterSet()) - sizeof(void*));
-#elif defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64)
-    uintptr_t returnAddressLocation = (uintptr_t) pCf->GetRegisterSet()->pCallerContextPointers->Ra;
-#elif defined(TARGET_ARM) || defined(TARGET_ARM64)
-    uintptr_t returnAddressLocation = (uintptr_t) pCf->GetRegisterSet()->pCallerContextPointers->Lr;
-#else
-    PORTABILITY_ASSERT("Platform NYI");
-#endif
-    stackDataInfo.ReturnAddressOffset = (uint32_t)(returnAddressLocation - (uintptr_t)pCf->GetRegisterSet()->SP);
-
-    CQuickArrayList<RegRestore> savedRegRestoreData;
-    bool hasRBPFrame = false;
-
-
-// Find restored reg, and record its location in the frame, so that it can be properly restored. Then update the copied value to be the "current" value of the register, not what we saved off on entry to the function
-#define DISCOVER_RESTORED_REG(REGNAME) \
-    if (pCf->GetRegisterSet()->pCallerContextPointers->REGNAME != pCf->GetRegisterSet()->pCurrentContextPointers->REGNAME)                                                                                         \
-    {                                                                                                                                                                                                              \
-        if ((void*)&pCf->GetRegisterSet()->pCallerContextPointers->REGNAME == (void*)&pCf->GetRegisterSet()->pCallerContextPointers->Rbp)                                                                          \
-            hasRBPFrame = true;                                                                                                                                                                                    \
-        RegRestore restoreData; restoreData.reg = RegisterToRestore::REGNAME;                                                                                                                                      \
-        restoreData.offset = (uint32_t)((uint8_t*)pCf->GetRegisterSet()->pCallerContextPointers->REGNAME - (uint8_t*)pCf->GetRegisterSet()->SP); savedRegRestoreData.Push(restoreData);                            \
-        memcpy((pStackData - taskletCaptureData->stackToIgnoreFromPreviousFrame) + restoreData.offset, &pCf->GetRegisterSet()->pCurrentContext->REGNAME, sizeof(pCf->GetRegisterSet()->pCurrentContext->REGNAME)); \
-    }
-
-
-#include "restoreregs_for_runtimesuspension.h"
-#undef DISCOVER_RESTORED_REG
-
-    RegRestore returnData;
-    if (hasRBPFrame)
-    {
-        returnData.reg = RegisterToRestore::ReturnRegisters;
-    }
-    else
-    {
-        returnData.reg = RegisterToRestore::ReturnRegistersNoFrame;
-    }
-    returnData.offset = 0;
-    savedRegRestoreData.Push(returnData);
-
-
-    ICodeManager * pCM = pCf->GetCodeManager();
-    _ASSERTE(pCM != NULL);
-
-    unsigned flags = pCf->GetCodeManagerFlags();
-
-    GCEnumCallback enumGCRefs = []( 
-        LPVOID          hCallback,      // callback data
-        OBJECTREF*      pObject,        // address of object-reference we are reporting
-        uint32_t        flags )
-    {
-        RuntimeSuspensionEnumData *pEnumData = (RuntimeSuspensionEnumData*)hCallback;
-
-        // Determine if pObject points at a non-volatile register slot
-        uint32_t offset;
-
-        if (false) {}
-#define DISCOVER_RESTORED_REG(REGNAME) else if ((void*)pEnumData->pRD->pCurrentContextPointers->REGNAME == (void*)pObject) { offset = pEnumData->GetOffsetForReg(RegisterToRestore::REGNAME); }
-#include "restoreregs_for_runtimesuspension.h"
-#undef DISCOVER_RESTORED_REG
-        else
-        {
-            // Must be within the stack frame itself
-            offset = (uint32_t)((uint8_t*)pObject - (uint8_t*)pEnumData->pRD->SP);
-        }
-
-        bool isInterior = !!(flags & GC_CALL_INTERIOR);
-        bool isPinned = !!(flags & GC_CALL_PINNED);
-        _ASSERTE(!isPinned || isInterior);
-
-        if (!isInterior)
-        {
-            pEnumData->ObjectRefOffsets.PushNoThrow(offset);
-            // TODO PushNoThrow has a return value
-        }
-        else
-        {
-            int32_t byRefOffset = (int32_t)offset;
-
-            pEnumData->pTaskletCaptureData->AddCopiedByRef(pEnumData->pCf->GetRegisterSet()->SP, (uintptr_t*)(pEnumData->pStackData + offset - pEnumData->pStackDataInfo->UnrecordedDataSize));
-            if (isPinned)
-            {
-                byRefOffset = -byRefOffset;
-            }
-
-            pEnumData->ByRefOffsets.PushNoThrow(offset);
-            // TODO PushNoThrow has a return value
-        }
-    };
-
-    RuntimeSuspensionEnumData enumData(savedRegRestoreData, pCf->GetRegisterSet(), taskletCaptureData, pCf, &stackDataInfo, pStackData);
-
-    pCM->EnumGcRefs(pCf->GetRegisterSet(),
-                    pCf->GetCodeInfo(),
-                    flags | NoGcDecoderValidation,
-                    enumGCRefs,
-                    &enumData,
-                    NO_OVERRIDE_OFFSET);
-
-// HACK for frame pointer handling
-    for (int iRegister = 0; iRegister < savedRegRestoreData.Size(); iRegister++)
-    {
-        if (savedRegRestoreData[iRegister].reg == RegisterToRestore::Rbp)
-        {
-            // If we have Rbp as a saved register, report it as a byref, as its probably the frame pointer and thus in need of adjusting
-            enumGCRefs((void*)&enumData, (OBJECTREF*)(pCf->GetRegisterSet()->SP + savedRegRestoreData[iRegister].offset), GC_CALL_INTERIOR);
-        }
-    }
-
-    stackDataInfo.ByRefOffsets = (int32_t*)malloc(enumData.ByRefOffsets.Size() * sizeof(int32_t));
-    memcpy(stackDataInfo.ByRefOffsets, enumData.ByRefOffsets.Ptr(), enumData.ByRefOffsets.Size() * sizeof(int32_t));
-    stackDataInfo.ObjectRefOffsets = (uint32_t*)malloc(enumData.ObjectRefOffsets.Size() * sizeof(uint32_t));
-    memcpy(stackDataInfo.ObjectRefOffsets, enumData.ObjectRefOffsets.Ptr(), enumData.ObjectRefOffsets.Size() * sizeof(uint32_t));
-    stackDataInfo.RegistersToRestore = (RegRestore*)malloc(savedRegRestoreData.Size() * sizeof(RegRestore));
-    memcpy(stackDataInfo.RegistersToRestore, savedRegRestoreData.Ptr(), savedRegRestoreData.Size() * sizeof(RegRestore));
-
-    stackDataInfo.cByRefs = (uint32_t)enumData.ByRefOffsets.Size();
-    stackDataInfo.cObjectRefs = (uint32_t)enumData.ObjectRefOffsets.Size();
-    stackDataInfo.CbArgs = sizeofArgStack;
-
-    StackDataInfo *pStackDataInfo = (StackDataInfo*)malloc(sizeof(StackDataInfo));
-    *pStackDataInfo = stackDataInfo;
-
-    MetaSig msig(pCf->GetFunction());
-    ArgIterator argit(&msig);
-    TaskletReturnType taskletReturnType;
-#if TARGET_AMD64
-    if (argit.HasRetBuffArg())
-    {
-        taskletReturnType = TaskletReturnType::ByReference; // In this case, on Windows AMD64 the abi specifies that the return value in RAX is the address of the ret buffer
-    }
-    else
-    {
-        TypeHandle thRet = msig.GetRetTypeHandleThrowing();
-        CorElementType corElementType = thRet.GetInternalCorElementType();
-        if (thRet.IsTypeDesc())
-        {
-            taskletReturnType = TaskletReturnType::Integer;
-        }
-        else
-        {
-            if (thRet.AsMethodTable()->IsValueType() && thRet.AsMethodTable()->ContainsGCPointers())
-            {
-                taskletReturnType = TaskletReturnType::ObjectReference;
-            }
-            else
-            {
-                // These asserts don't check all the FP ret cases, but they cover at least some of it
-                _ASSERTE(thRet.GetInternalCorElementType() != ELEMENT_TYPE_R4);
-                _ASSERTE(thRet.GetInternalCorElementType() != ELEMENT_TYPE_R8);
-                taskletReturnType = TaskletReturnType::Integer;
-            }
-        }
-    }
-#else
-PORTABILIT_ASSERT()
-#endif
-
-
-
-    Tasklet *pTasklet = (Tasklet*)malloc(sizeof(Tasklet));
-    memset(pTasklet, 0, sizeof(Tasklet));
-    pTasklet->pStackData = pStackData;
-    pTasklet->restoreIPAddress = (uintptr_t)GetControlPC(pCf->GetRegisterSet());
-    pTasklet->pStackDataInfo = pStackDataInfo;
-    pTasklet->taskletReturnType = taskletReturnType;
-
-    if (taskletCaptureData->firstTasklet == NULL)
-    {
-        taskletCaptureData->firstTasklet = pTasklet;
-        taskletCaptureData->lastTasklet = pTasklet;
-    }
-    else
-    {
-        taskletCaptureData->lastTasklet->pTaskletNextInStack = pTasklet;
-        pTasklet->pTaskletPrevInStack = taskletCaptureData->lastTasklet;
-        taskletCaptureData->lastTasklet = pTasklet;
-    }
-    taskletCaptureData->framesCaptured++;
-    
-    // Apply byrefs to newly allocated stuff
-    taskletCaptureData->ApplyByRefRelocsToNewlyAllocatedTasklet(byrefAdjustment);
-
-    return SWA_CONTINUE;
-}
-
-
-extern "C" Tasklet* QCALLTYPE RuntimeSuspension_CaptureTasklets(QCall::StackCrawlMarkHandle stackMark, uint8_t* returnValue, uint8_t useReturnValueHandle, void* taskAsyncData, Tasklet** lastTasklet, int32_t* pFramesCaptured)
-{
-    GCX_COOP();
-
-    BEGINFORBIDGC();
-#ifdef _DEBUG
-    GCForbidLoaderUseHolder forbidLoaderUse;
-#endif
-
-    TaskletCaptureData cdata;
-    cdata.stackMark = stackMark;
-    cdata.stackLimit = (uintptr_t)taskAsyncData;
-    GetThread()->StackWalkFrames(CaptureTaskletsCore, &cdata, FUNCTIONSONLY);
-
-    // We should have captured a full stack here. 
-    *lastTasklet = cdata.lastTasklet;
-    *pFramesCaptured = cdata.framesCaptured;
-    ENDFORBIDGC();
-    return cdata.firstTasklet;
-}
-
-extern "C" void QCALLTYPE RuntimeSuspension_DeleteTasklet(Tasklet* tasklet)
-{
-    if (tasklet->pTaskletNextInStack == NULL)
-    {
-        UnregisterTasklet(tasklet);
-    }
-    else
-    {
-        tasklet->pTaskletNextInStack->pTaskletPrevInStack = NULL;
-    }
-
-    tasklet->pStackDataInfo->CleanupStackDataInfo();
-    free(tasklet->pStackData);
-    free(tasklet->pStackDataInfo);
-    free(tasklet);
-}
-
-extern "C" void QCALLTYPE RuntimeSuspension_RegisterTasklet(Tasklet * tasklet)
-{
-    // only heads of stack chains are registered.
-    _ASSERTE(tasklet->pTaskletNextInStack == NULL);
-
-    RegisterTasklet(tasklet);
-}
-
-void RegisterTasklet(Tasklet* pTasklet);
-void InitializeTasklets();
-void UnregisterTasklet(Tasklet* pTasklet);
-
-Tasklet *g_pTaskletSentinel = NULL;
-
-CrstStatic g_taskletCrst;
-
-void InitializeTasklets()
-{
-    g_taskletCrst.Init(CrstLeafLock, CRST_UNSAFE_ANYMODE);
-
-    g_pTaskletSentinel = (Tasklet*)malloc(sizeof(Tasklet));
-    memset(g_pTaskletSentinel, 0, sizeof(Tasklet));
-    g_pTaskletSentinel->pTaskletNextInLiveList = g_pTaskletSentinel;
-    g_pTaskletSentinel->pTaskletPrevInLiveList = g_pTaskletSentinel;
-}
-
-void RegisterTasklet(Tasklet* pTasklet)
-{
-    CrstHolder crstHolder(&g_taskletCrst);
-
-    pTasklet->pTaskletNextInLiveList = g_pTaskletSentinel->pTaskletNextInLiveList;
-    pTasklet->pTaskletPrevInLiveList = g_pTaskletSentinel;
-    g_pTaskletSentinel->pTaskletNextInLiveList = pTasklet;
-    pTasklet->pTaskletNextInLiveList->pTaskletPrevInLiveList = pTasklet;
-}
-
-void UnregisterTasklet(Tasklet* pTasklet)
-{
-    CrstHolder crstHolder(&g_taskletCrst);
-    pTasklet->pTaskletPrevInLiveList->pTaskletNextInLiveList = pTasklet->pTaskletNextInLiveList;
-    pTasklet->pTaskletNextInLiveList->pTaskletPrevInLiveList = pTasklet->pTaskletPrevInLiveList;
-}
-
-void IterateTaskletsForGC(promote_func* pCallback, int condemned, ScanContext* sc)
-{
-    CrstHolder crstHolder(&g_taskletCrst);
-    for (Tasklet* pCurStack = g_pTaskletSentinel->pTaskletNextInLiveList;
-        pCurStack != g_pTaskletSentinel;
-        pCurStack = pCurStack->pTaskletNextInLiveList)
-    {
-        if (pCurStack->minGeneration > condemned)
-        {
-            // this stack is too old to be interesting in this GC
-            continue;
-        };
-
-        Tasklet* pCurTasklet = pCurStack;
-        do
-        {
-            // because of rejuvenation, some tasklets could be older than the head
-            // skip them if too old.
-            if (pCurTasklet->minGeneration > condemned)
-            {
-                // this stack is too old to be interesting in this GC
-                continue;
-            };
-
-            // Report GC pointers
-            auto pStackDataInfo = pCurTasklet->pStackDataInfo;
-            uint8_t *pLogicalRSP = pCurTasklet->pStackData - pStackDataInfo->UnrecordedDataSize;
-            uint32_t iRef;
-            for (iRef = 0; iRef < pStackDataInfo->cObjectRefs; iRef++)
-            {
-                pCallback((PTR_PTR_Object)(pLogicalRSP + pStackDataInfo->ObjectRefOffsets[iRef]), sc, 0);
-            }
-
-            for (iRef = 0; iRef < pStackDataInfo->cByRefs; iRef++)
-            {
-                int32_t offset = pStackDataInfo->ByRefOffsets[iRef];
-                uint32_t flags = GC_CALL_INTERIOR;
-                if (offset < 0)
-                {
-                    offset = -offset;
-                    flags |= GC_CALL_PINNED;
-                }
-
-                pCallback((PTR_PTR_Object)(pLogicalRSP + offset), sc, flags);
-            }
-
-            pCurTasklet = pCurTasklet->pTaskletPrevInStack;
-        } while (pCurTasklet != NULL);
-    }
-}
-
-void AgeTasklets(int condemned, int max_gen, ScanContext* sc)
-{
-    if (!g_pConfig->TaskletAging())
-    {
-        return;
-    }
-
-    CrstHolder crstHolder(&g_taskletCrst);
-    for (Tasklet* pCurStack = g_pTaskletSentinel->pTaskletNextInLiveList; pCurStack != g_pTaskletSentinel; pCurStack = pCurStack->pTaskletNextInLiveList)
-    {
-        if (pCurStack->minGeneration > condemned)
-        {
-            // this stack is too old to be interesting in this GC
-            continue;
-        };
-
-        if (pCurStack->minGeneration < 0)
-        {
-            // this stack is active, do not age it
-            continue;
-        }
-
-        Tasklet* pCurTasklet = pCurStack;
-        do
-        {
-             // actually age the tasklet
-            pCurTasklet->minGeneration = min(pCurTasklet->minGeneration + 1, max_gen);
-
-            pCurTasklet = pCurTasklet->pTaskletPrevInStack;
-        } while (pCurTasklet != NULL);
-    }
-}
-
-void RejuvenateTasklets(int condemned, int max_gen, ScanContext* sc)
-{
-    if (!g_pConfig->TaskletAging())
-    {
-        return;
-    }
-
-    CrstHolder crstHolder(&g_taskletCrst);
-    for (Tasklet* pCurStack = g_pTaskletSentinel->pTaskletNextInLiveList;
-        pCurStack != g_pTaskletSentinel;
-        pCurStack = pCurStack->pTaskletNextInLiveList)
-    {
-        if (pCurStack->minGeneration <= 0)
-        {
-            // this stack is as young as it can be
-            continue;
-        };
-
-        if (pCurStack->minGeneration > condemned)
-        {
-            // this tasklet is too old to be interesting in this GC
-            continue;
-        }
-
-        Tasklet* pCurTasklet = pCurStack;
-        do
-        {
-            // update the minGeneration
-            auto pStackDataInfo = pCurTasklet->pStackDataInfo;
-            uint8_t* pLogicalRSP = pCurTasklet->pStackData - pStackDataInfo->UnrecordedDataSize;
-            uint32_t iRef;
-            for (iRef = 0; iRef < pStackDataInfo->cObjectRefs; iRef++)
-            {
-                auto ppObj = (PTR_PTR_Object)(pLogicalRSP + pStackDataInfo->ObjectRefOffsets[iRef]);
-                if (*ppObj != NULL)
-                {
-                    int objGen = (INT32)GCHeapUtilities::GetGCHeap()->WhichGeneration(*ppObj);
-                    if (objGen < pCurTasklet->minGeneration)
-                    {
-                        pCurTasklet->minGeneration = objGen;
-                        if (objGen == 0)
-                        {
-                            // we are as young as we can be
-                            goto nextTaskLet;
-                        }
-                    }
-                }
-            }
-
-            for (iRef = 0; iRef < pStackDataInfo->cByRefs; iRef++)
-            {
-                int32_t offset = pStackDataInfo->ByRefOffsets[iRef];
-                if (offset < 0)
-                {
-                    offset = -offset;
-                }
-
-                auto ppObj = (PTR_PTR_Object)(pLogicalRSP + offset);
-                int objGen = (INT32)GCHeapUtilities::GetGCHeap()->WhichGeneration(*ppObj);
-                if (*ppObj != NULL)
-                {
-                    if (objGen < pCurTasklet->minGeneration)
-                    {
-                        pCurTasklet->minGeneration = objGen;
-                        if (objGen == 0)
-                        {
-                            // we are as young as we can be
-                            goto nextTaskLet;
-                        }
-                    }
-                }
-            }
-
-        nextTaskLet:
-
-            // make sure the head tasklet is no older than any tasklets in the list
-            // because we will use the age of the head for short-circuiting
-            if (pCurStack->minGeneration > pCurTasklet->minGeneration)
-            {
-                pCurStack->minGeneration = pCurTasklet->minGeneration;
-            }
-
-            pCurTasklet = pCurTasklet->pTaskletPrevInStack;
-        } while (pCurTasklet != NULL);
-    }
-}
-
-extern "C" void ForceThisThreadHasNoHijackForUnwind()
-{
-    // If there is a hijack of a frame in place and we unwind, who knows what's going to happen. This code just forces the hijack to disappear. The runtime should put it back later if it matters.
-    GetThread()->UnhijackThread();
-}
diff --git a/src/coreclr/vm/runtimesuspension.h b/src/coreclr/vm/runtimesuspension.h
deleted file mode 100644
index f864ca2e3bc1ef..00000000000000
--- a/src/coreclr/vm/runtimesuspension.h
+++ /dev/null
@@ -1,23 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-
-struct Tasklet;
-struct AsyncDataFrame;
-struct RuntimeAsyncReturnValue;
-
-extern "C" Tasklet* QCALLTYPE RuntimeSuspension_CaptureTasklets(QCall::StackCrawlMarkHandle stackMark, uint8_t* returnValue, uint8_t useReturnValueHandle, void* taskAsyncData, Tasklet** lastTasklet, int32_t* pFramesCaptured);
-extern "C" void QCALLTYPE RuntimeSuspension_RegisterTasklet(Tasklet * tasklet);
-extern "C" void QCALLTYPE RuntimeSuspension_DeleteTasklet(Tasklet* tasklet);
-
-EXTERN_C FCDECL1(void, RuntimeSuspension_UnwindToFunctionWithAsyncFrame, AsyncDataFrame* frame);
-
-EXTERN_C FCDECL2(Object*, RuntimeSuspension_ResumeTaskletReferenceReturn, Tasklet* tasklet, RuntimeAsyncReturnValue *resumptionReturnValue);
-EXTERN_C FCDECL2(Object*, RuntimeSuspension_ResumeTaskletIntegerRegisterReturn, Tasklet* tasklet, RuntimeAsyncReturnValue *resumptionReturnValue);
-
-void RegisterTasklet(Tasklet* pTasklet);
-void InitializeTasklets();
-void UnregisterTasklet(Tasklet* pTasklet);
-
-void IterateTaskletsForGC(promote_func* pCallback, int condemned, ScanContext* sc);
-void AgeTasklets(int condemned, int max_gen, ScanContext* sc);
-void RejuvenateTasklets(int condemned, int max_gen, ScanContext* sc);
diff --git a/src/coreclr/vm/siginfo.cpp b/src/coreclr/vm/siginfo.cpp
index 3b22e8b1b3d7c1..009a37e8f8faf8 100644
--- a/src/coreclr/vm/siginfo.cpp
+++ b/src/coreclr/vm/siginfo.cpp
@@ -5286,7 +5286,7 @@ BOOL MetaSig::HasAsyncContinuation()
 {
     LIMITED_METHOD_CONTRACT;
 
-    return IsAsyncCall() && g_pConfig->RuntimeAsyncViaJitGeneratedStateMachines();
+    return IsAsyncCall();
 }
 
 //------------------------------------------------------------------
diff --git a/src/coreclr/vm/threadsuspend.cpp b/src/coreclr/vm/threadsuspend.cpp
index dd1fb86263c5cd..a11157ff158ddf 100644
--- a/src/coreclr/vm/threadsuspend.cpp
+++ b/src/coreclr/vm/threadsuspend.cpp
@@ -4941,9 +4941,6 @@ static bool GetReturnAddressHijackInfo(EECodeInfo *pCodeInfo, ReturnKind *pRetur
     // TODO: Expand return kind to include this?
     *hasAsyncRet = false;
 
-    if (!g_pConfig->RuntimeAsyncViaJitGeneratedStateMachines())
-        return true;
-
     MethodDesc* pMD = pCodeInfo->GetMethodDesc();
     *hasAsyncRet = pMD->IsAsync2Method() ||
         (pMD->IsIntrinsic() && IsSpecialCaseAsyncRet(pMD));
diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.cs
index 1611f48f77fec5..60ae6d753c5c76 100644
--- a/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.cs
+++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.cs
@@ -187,36 +187,14 @@ public static unsafe ReadOnlySpan<T> CreateSpan<T>(RuntimeFieldHandle fldHandle)
         [BypassReadyToRun]
         public static void AwaitAwaiterFromRuntimeAsync<TAwaiter>(TAwaiter awaiter) where TAwaiter : INotifyCompletion
         {
-            if (RuntimeAsyncViaJitGeneratedStateMachines)
-            {
-                ref RuntimeAsyncAwaitState state = ref t_runtimeAsyncAwaitState;
-                Continuation? sentinelContinuation = state.SentinelContinuation;
-                if (sentinelContinuation == null)
-                    state.SentinelContinuation = sentinelContinuation = new Continuation();
-
-                state.Notifier = awaiter;
-                SuspendAsync2(sentinelContinuation);
-                return;
-            }
-            else
-            {
-                StackCrawlMark stackMark = StackCrawlMark.LookForMe;
+            ref RuntimeAsyncAwaitState state = ref t_runtimeAsyncAwaitState;
+            Continuation? sentinelContinuation = state.SentinelContinuation;
+            if (sentinelContinuation == null)
+                state.SentinelContinuation = sentinelContinuation = new Continuation();
 
-                // Create resumption delegate, wrapping task, and create tasklets to represent each stack frame on the stack.
-                // RuntimeTaskSuspender.DoSetupAndCapture() works like a POSIX fork call in that calls to it will return a
-                // true for the initial call to DoSetupAndCapture, but once the thread is resumed,
-                // it will resume with a return value of null.
-                if (RuntimeHelpers.DoSetupAndCapture(ref stackMark))
-                {
-                    // If we reach here, the only way that we actually run follow on code is for the continuation to actually run,
-                    // and return from DoSetupAndCapture with a false return value
-                    ref AsyncDataFrame asyncFrame = ref GetCurrentAsyncDataFrame();
-                    RuntimeAsyncMaintainedData maintainedData = asyncFrame._maintainedData!;
-                    maintainedData._awaiter = awaiter;
-                    // This function must be called from the same function that has the stackmark in it.
-                    unsafe { UnwindToFunctionWithAsyncFrame(maintainedData._nextTasklet, maintainedData._suspendActive); }
-                }
-            }
+            state.Notifier = awaiter;
+            SuspendAsync2(sentinelContinuation);
+            return;
         }
 
         // Marked intrinsic since for JIT state machines this needs to be
@@ -226,377 +204,14 @@ public static void AwaitAwaiterFromRuntimeAsync<TAwaiter>(TAwaiter awaiter) wher
         [MethodImpl(MethodImplOptions.NoInlining)]
         public static void UnsafeAwaitAwaiterFromRuntimeAsync<TAwaiter>(TAwaiter awaiter) where TAwaiter : ICriticalNotifyCompletion
         {
-            if (RuntimeAsyncViaJitGeneratedStateMachines)
-            {
-                ref RuntimeAsyncAwaitState state = ref t_runtimeAsyncAwaitState;
-                Continuation? sentinelContinuation = state.SentinelContinuation;
-                if (sentinelContinuation == null)
-                    state.SentinelContinuation = sentinelContinuation = new Continuation();
-
-                state.Notifier = awaiter;
-                SuspendAsync2(sentinelContinuation);
-                return;
-            }
-            else
-            {
-                StackCrawlMark stackMark = StackCrawlMark.LookForMe;
-
-                // Create resumption delegate, wrapping task, and create tasklets to represent each stack frame on the stack.
-                // RuntimeTaskSuspender.DoSetupAndCapture() works like a POSIX fork call in that calls to it will return a
-                // true for the initial call to DoSetupAndCapture, but once the thread is resumed,
-                // it will resume with a return value of false.
-                if (RuntimeHelpers.DoSetupAndCapture(ref stackMark))
-                {
-                    // If we reach here, the only way that we actually run follow on code is for the continuation to actually run,
-                    // and return from DoSetupAndCapture with a false return value
-                    ref AsyncDataFrame asyncFrame = ref GetCurrentAsyncDataFrame();
-                    RuntimeAsyncMaintainedData maintainedData = asyncFrame._maintainedData!;
-                    maintainedData._awaiter = awaiter;
-                    // This function must be called from the same function that has the stackmark in it.
-                    unsafe { UnwindToFunctionWithAsyncFrame(maintainedData._nextTasklet, maintainedData._suspendActive); }
-                }
-            }
-        }
-
-        // Could probably be a static readonly bool computing the environment
-        // variable, but recognizing it as an intrinsic avoids having to deal
-        // with having a static constructor that could interfere with perf
-        // measurements.
-        private static bool RuntimeAsyncViaJitGeneratedStateMachines
-        {
-            [Intrinsic]
-            get => false;
-        }
-
-        [ThreadStatic]
-        private static unsafe void* t_asyncData;
-
-        internal struct RuntimeAsyncReturnValue
-        {
-            public RuntimeAsyncReturnValue(object? obj)
-            {
-                _obj = obj;
-                _ptr = IntPtr.Zero;
-                _returnType = TaskletReturnType.ObjectReference;
-            }
-            public RuntimeAsyncReturnValue(IntPtr ptr)
-            {
-                _obj = null;
-                _ptr = ptr;
-                _returnType = TaskletReturnType.Integer;
-            }
-            public object? _obj;
-            public IntPtr _ptr;
-            public TaskletReturnType _returnType;
-        }
-
-        internal class RuntimeAsyncMaintainedData
-        {
-            public Exception? _exception;
-            public INotifyCompletion? _awaiter;
-            public int _suspendActive;
-            public bool _initialTaskEntry = true;
-            public bool _completed;
-            public byte _dummy;
-
-            public unsafe Tasklet* _nextTasklet;
-
-            public RuntimeAsyncReturnValue _retValue;
-            public virtual ref byte GetReturnPointer() { return ref _dummy; }
-
-            public Task? _task;
-
-            public virtual Task GetTask()
-            {
-                return CompletionTaskReturnVoid();
-            }
-
-            public unsafe bool ResumptionFunc()
-            {
-                // Suspension has finished and we are resuming
-                _suspendActive = 0;
-
-                // Once we perform a resumption we no longer need to worry about handling the ultimate return data from the run of Tasklets
-                _initialTaskEntry = false;
-
-                int collectiveStackAllocsPerformed = 0;
-
-                AsyncDataFrame dataFrame = new AsyncDataFrame(this);
-                PushAsyncData(ref dataFrame);
-                try
-                {
-                    while (_nextTasklet != null)
-                    {
-                        Tasklet* pCurTasklet = _nextTasklet;
-                        int maxStackNeeded = pCurTasklet->GetMaxStackNeeded();
-                        _nextTasklet = pCurTasklet->pTaskletNextInStack;
-                        if (maxStackNeeded > collectiveStackAllocsPerformed)
-                        {
-#pragma warning disable CA2014
-                            // This won't stack overflow unless MaxStackNeeded is actually too high, as the extra allocation is controlled by collectiveStackAllocsPerformed
-                            // TODO This is doing terrible things with the ABI, so we may need to be more careful here
-                            int stackToAlloc = maxStackNeeded - collectiveStackAllocsPerformed;
-                            byte* pStackAlloc = stackalloc byte[stackToAlloc];
-                            collectiveStackAllocsPerformed += stackToAlloc;
-#pragma warning restore CA2014
-                            // The optimizer does nothing with variable sized StackAlloc KeepStackAllocAlive(pStackAlloc);
-                        }
-
-                        try
-                        {
-                            switch (pCurTasklet->taskletReturnType)
-                            {
-                                case TaskletReturnType.ObjectReference:
-                                    _retValue = new RuntimeAsyncReturnValue(ResumeTaskletReferenceReturn(pCurTasklet, ref _retValue));
-                                    break;
-                                case TaskletReturnType.Integer:
-                                    _retValue = new RuntimeAsyncReturnValue(ResumeTaskletIntegerRegisterReturn(pCurTasklet, ref _retValue));
-                                    break;
-                                case TaskletReturnType.ByReference:
-                                    throw new NotImplementedException(); // This will be awkward (but not impossible) to implement. Hold off for now
-                            }
-                        }
-                        finally
-                        {
-                            DeleteTasklet(pCurTasklet);
-                        }
-
-                        if (_suspendActive != 0)
-                        {
-                            // TODO: we should capture the sync and execution contexts at this point
-                            break;
-                        }
-                    }
-                }
-                finally
-                {
-                    PopAsyncData(ref dataFrame);
-                }
-
-                return _suspendActive!= 0;
-            }
-
-            protected sealed class TempAwaitable : ICriticalNotifyCompletion
-            {
-                public INotifyCompletion? _awaiter;
-
-                public bool IsCompleted => false;
-
-                public void OnCompleted(Action action)
-                {
-                    _awaiter!.OnCompleted(action);
-                }
-
-                public TempAwaitable GetAwaiter() { return this; }
-
-                public void UnsafeOnCompleted(Action action)
-                {
-                    if (_awaiter is ICriticalNotifyCompletion criticalNotification)
-                    {
-                        criticalNotification.UnsafeOnCompleted(action);
-                    }
-                    else
-                    {
-                        _awaiter!.OnCompleted(action);
-                    }
-                }
-
-                public void GetResult() {}
-            }
-
-            private async Task CompletionTaskReturnVoid()
-            {
-                TempAwaitable awaitableObject = new TempAwaitable();
-                bool keepProcessing = true;
-                while (keepProcessing)
-                {
-                    awaitableObject._awaiter = _awaiter!;
-                    await awaitableObject;
-                    keepProcessing = ResumptionFunc();
-                }
-            }
-        }
-
-        // These are all implemented by the same assembly helper that will setup the tasklet in its new home on the stack
-        // and then tail-call into it. We will need a different entrypoint name for each type of register based return that can happen
-        [MethodImpl(MethodImplOptions.InternalCall)]
-        internal static extern unsafe object ResumeTaskletReferenceReturn(Tasklet* pTasklet, ref RuntimeAsyncReturnValue retValue);
-        [MethodImpl(MethodImplOptions.InternalCall)]
-        internal static extern unsafe IntPtr ResumeTaskletIntegerRegisterReturn(Tasklet* pTasklet, ref RuntimeAsyncReturnValue retValue);
-
-        internal sealed class RuntimeAsyncMaintainedData<T> : RuntimeAsyncMaintainedData
-        {
-            private T? _returnData;
-            public override ref byte GetReturnPointer()
-            {
-                return ref Unsafe.As<T, byte>(ref _returnData!);
-            }
-
-            public override Task GetTask()
-            {
-                return CompletionTask();
-            }
-
-            private async Task<T?> CompletionTask()
-            {
-                TempAwaitable awaitableObject = new TempAwaitable();
-                bool keepProcessing = true;
-                while (keepProcessing)
-                {
-                    awaitableObject._awaiter = _awaiter!;
-                    await awaitableObject;
-                    keepProcessing = ResumptionFunc();
-                }
-
-                switch (_retValue._returnType)
-                {
-                    case TaskletReturnType.Integer:
-                        return Unsafe.As<IntPtr, T>(ref _retValue._ptr);
-                    case TaskletReturnType.ObjectReference:
-                        return Unsafe.As<object, T>(ref _retValue!._obj!)!;
-                    default:
-                        // Other possibiilities not yet implemented
-                        throw new NotImplementedException();
-                }
-            }
-        }
-
-        internal unsafe struct AsyncDataFrame
-        {
-            public AsyncDataFrame(RuntimeAsyncMaintainedData maintainedData)
-            {
-                _maintainedData = maintainedData;
-                _crawlMark = StackCrawlMark.LookForMe;
-                _next = null;
-                _createRuntimeMaintainedData = null;
-            }
-
-            public AsyncDataFrame(Func<RuntimeAsyncMaintainedData> getMaintainedData)
-            {
-                _maintainedData = null;
-                _crawlMark = StackCrawlMark.LookForMe;
-                _next = null;
-                _createRuntimeMaintainedData = getMaintainedData;
-            }
-
-            public RuntimeAsyncMaintainedData? _maintainedData;
-            public StackCrawlMark _crawlMark;
-            public void* _next;
-            public Func<RuntimeAsyncMaintainedData>? _createRuntimeMaintainedData;
-        }
-
-        internal enum TaskletReturnType
-        {
-            // These return types are OS/architecture specific. For instance, Arm64 supports returning structs in a register pair
-            Integer,
-            ObjectReference,
-            ByReference
-        }
-
-        internal struct StackDataInfo
-        {
-            public int StackRequirement;
-            // And native has a bunch of other fields that managed does not use.
-        }
-        internal unsafe struct Tasklet
-        {
-            public Tasklet* pTaskletNextInStack;
-            public Tasklet* pTaskletNextInLiveList;
-            public Tasklet* pTaskletPrevInLiveList;
-            public byte* pStackData;
-            public IntPtr restoreIPAddress;
-            public StackDataInfo* pStackDataInfo;
-            public TaskletReturnType taskletReturnType;
-            public int minGeneration;
-            public Tasklet* pTaskletPrevInStack;
-
-            public int GetMaxStackNeeded() { return pStackDataInfo->StackRequirement; }
-        }
-
-        internal static unsafe void PushAsyncData(ref AsyncDataFrame asyncData)
-        {
-            asyncData._next = t_asyncData;
-            t_asyncData = Unsafe.AsPointer(ref asyncData);
-        }
-
-        internal static unsafe void PopAsyncData(ref AsyncDataFrame asyncData)
-        {
-            t_asyncData = asyncData._next;
-        }
-
-        [MethodImpl(MethodImplOptions.NoInlining)]
-        internal static unsafe bool HasCurrentAsyncDataFrame()
-        {
-            return t_asyncData != null;
-        }
-
-        [MethodImpl(MethodImplOptions.NoInlining)]
-        private static unsafe ref AsyncDataFrame GetCurrentAsyncDataFrame()
-        {
-            return ref Unsafe.AsRef<AsyncDataFrame>(t_asyncData);
-        }
-
-        // Capture stack into a series of tasklets (one per stack frame)
-        // These tasklets hold the stack data for a particular frame, as well as the contents of the saved registers as needed by that frame, GC data for reporting the frame, and data for restoring the frame.
-        // To make this work.
-        // 1. All addresses of locals are to be used as byrefs
-        // 2. Frame pointers are to be reported as byrefs
-        // 3. Return values are to be returned by reference in all cases where the return value is not a simple object return or return of a simple value in the return value register (this makes the resumption function reasonable to write. Notably, floating point, and ref return will be returned by reference as well as generalized struct return, and return which would normally involve multiple return value registers)
-        // 4. There are to be no refs to the outermost caller function exceptn for the valuetype return address (methods which begin on an instance valuetype will have the thunk box the valuetype and the runtime async method on the boxed instance)
-        [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "RuntimeSuspension_CaptureTasklets")]
-        private static unsafe partial Tasklet* CaptureCurrentStackIntoTasklets(StackCrawlMarkHandle stackMarkTop, ref byte returnValueHandle, [MarshalAs(UnmanagedType.U1)] bool useReturnValueHandle, void* taskAsyncData, out Tasklet* lastTasklet, out int framesCaptured);
-
-        [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "RuntimeSuspension_DeleteTasklet")]
-        private static unsafe partial void DeleteTasklet(Tasklet* tasklet);
-
-        [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "RuntimeSuspension_RegisterTasklet")]
-        private static unsafe partial void RegisterTasklet(Tasklet* tasklet);
-
-        [MethodImpl(MethodImplOptions.InternalCall)]
-        internal static extern unsafe void UnwindToFunctionWithAsyncFrame(Tasklet* topTasklet, nint framesToUnwind);
-
-        [System.Security.DynamicSecurityMethod] // Methods containing StackCrawlMark local var has to be marked DynamicSecurityMethod
-        private static unsafe bool DoSetupAndCapture(ref StackCrawlMark stackMark)
-        {
-            ref AsyncDataFrame asyncFrame = ref GetCurrentAsyncDataFrame();
-
-            asyncFrame._maintainedData ??= asyncFrame._createRuntimeMaintainedData!();
-
-            RuntimeAsyncMaintainedData maintainedData = asyncFrame._maintainedData;
-
-            Tasklet* lastTasklet = null;
-            Tasklet* nextTaskletInStack = CaptureCurrentStackIntoTasklets(new StackCrawlMarkHandle(ref stackMark), ref maintainedData.GetReturnPointer(), maintainedData._initialTaskEntry, t_asyncData, out lastTasklet, out var framesCaptured);
-            if (nextTaskletInStack == null)
-                throw new OutOfMemoryException();
-
-            if (maintainedData._nextTasklet == null)
-            {
-                RegisterTasklet(lastTasklet);
-            }
-            else
-            {
-                maintainedData._nextTasklet->pTaskletPrevInStack = lastTasklet;
-                lastTasklet->pTaskletNextInStack = maintainedData._nextTasklet;
-
-                // we are suspending, so change the age of tasklets from -1 (active) to 0 (very young)
-                // NOTE: this is only needed as long as we allow cross-frame byrefs as
-                // that forces us to make age of active tasklets undefined (see comment in PlatformIndependentRestore)
-                // without such byrefs old tasklets could stay old as nothing could change locals of a captured frame.
-                for (Tasklet* current = maintainedData._nextTasklet; current != null; current = current->pTaskletNextInStack)
-                {
-                    if (current->minGeneration == -1)
-                    {
-                        current->minGeneration = 0;
-                    }
-                }
-            }
-
-            maintainedData._nextTasklet = nextTaskletInStack;
-            maintainedData._retValue = default(RuntimeAsyncReturnValue);
-
-            maintainedData._suspendActive = framesCaptured;
+            ref RuntimeAsyncAwaitState state = ref t_runtimeAsyncAwaitState;
+            Continuation? sentinelContinuation = state.SentinelContinuation;
+            if (sentinelContinuation == null)
+                state.SentinelContinuation = sentinelContinuation = new Continuation();
 
-            return true;
+            state.Notifier = awaiter;
+            SuspendAsync2(sentinelContinuation);
+            return;
         }
 #endif
     }
diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/Future.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/Future.cs
index 5babb13279f36d..ce4f18b6099baf 100644
--- a/src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/Future.cs
+++ b/src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/Future.cs
@@ -15,196 +15,6 @@
 
 namespace System.Threading.Tasks
 {
-
-#if !NATIVEAOT
-#pragma warning disable CA1822
-    // Used to implement Runtime implemented Task suspension handling
-    internal struct RuntimeTaskState<TResult>
-    {
-        private RuntimeHelpers.AsyncDataFrame dataFrame;
-        // Store current ExecutionContext and SynchronizationContext as "previousXxx".
-        // This allows us to restore them and undo any Context changes made in stateMachine.MoveNext
-        // so that they won't "leak" out of the first await.
-        public ExecutionContext? _previousExecutionCtx;
-        public SynchronizationContext? _previousSyncCtx;
-        public Thread _thread;
-
-        public void Push()
-        {
-            dataFrame = new RuntimeHelpers.AsyncDataFrame(() => new RuntimeHelpers.RuntimeAsyncMaintainedData<TResult>());
-            RuntimeHelpers.PushAsyncData(ref dataFrame);
-            _thread = Thread.CurrentThread;
-            _previousExecutionCtx = _thread._executionContext;
-            _previousSyncCtx = _thread._synchronizationContext;
-        }
-
-        public void Pop()
-        {
-            // The common case is that these have not changed, so avoid the cost of a write barrier if not needed.
-            if (_previousSyncCtx != _thread._synchronizationContext)
-            {
-                // Restore changed SynchronizationContext back to previous
-                _thread._synchronizationContext = _previousSyncCtx;
-            }
-
-            ExecutionContext? currentExecutionCtx = _thread._executionContext;
-            if (_previousExecutionCtx != currentExecutionCtx)
-            {
-                ExecutionContext.RestoreChangedContextToThread(_thread, _previousExecutionCtx, currentExecutionCtx);
-            }
-            RuntimeHelpers.PopAsyncData(ref dataFrame);
-        }
-
-        public Task<TResult> FromResult(TResult result)
-        {
-            if (dataFrame._maintainedData == null || (dataFrame._maintainedData._suspendActive == 0))
-                return Task.FromResult(result);
-            return (Task<TResult>)dataFrame._maintainedData.GetTask();
-        }
-
-        public Task<TResult> FromException(Exception e) { return Task.FromException<TResult>(e); }
-    }
-    // Used to implement Runtime implemented Task suspension handling
-    internal struct RuntimeTaskState
-    {
-        private RuntimeHelpers.AsyncDataFrame dataFrame;
-        // Store current ExecutionContext and SynchronizationContext as "previousXxx".
-        // This allows us to restore them and undo any Context changes made in stateMachine.MoveNext
-        // so that they won't "leak" out of the first await.
-        public ExecutionContext? _previousExecutionCtx;
-        public SynchronizationContext? _previousSyncCtx;
-        public Thread _thread;
-
-        public void Push()
-        {
-            dataFrame = new RuntimeHelpers.AsyncDataFrame(()=> new RuntimeHelpers.RuntimeAsyncMaintainedData());
-            RuntimeHelpers.PushAsyncData(ref dataFrame);
-            _thread = Thread.CurrentThread;
-            _previousExecutionCtx = _thread._executionContext;
-            _previousSyncCtx = _thread._synchronizationContext;
-        }
-
-        public void Pop()
-        {
-            // The common case is that these have not changed, so avoid the cost of a write barrier if not needed.
-            if (_previousSyncCtx != _thread._synchronizationContext)
-            {
-                // Restore changed SynchronizationContext back to previous
-                _thread._synchronizationContext = _previousSyncCtx;
-            }
-
-            ExecutionContext? currentExecutionCtx = _thread._executionContext;
-            if (_previousExecutionCtx != currentExecutionCtx)
-            {
-                ExecutionContext.RestoreChangedContextToThread(_thread, _previousExecutionCtx, currentExecutionCtx);
-            }
-            RuntimeHelpers.PopAsyncData(ref dataFrame);
-        }
-
-        public Task FromResult()
-        {
-            if (dataFrame._maintainedData == null || (dataFrame._maintainedData._suspendActive == 0))
-                return Task.CompletedTask;
-            return dataFrame._maintainedData.GetTask();
-        }
-
-        public Task FromException(Exception e) { return Task.FromException(e); }
-    }
-    // Used to implement Runtime implemented Task suspension handling
-    internal struct RuntimeValueTaskState<TResult>
-    {
-        private RuntimeHelpers.AsyncDataFrame dataFrame;
-        // Store current ExecutionContext and SynchronizationContext as "previousXxx".
-        // This allows us to restore them and undo any Context changes made in stateMachine.MoveNext
-        // so that they won't "leak" out of the first await.
-        public ExecutionContext? _previousExecutionCtx;
-        public SynchronizationContext? _previousSyncCtx;
-        public Thread _thread;
-
-        public void Push()
-        {
-            dataFrame = new RuntimeHelpers.AsyncDataFrame(()=> new RuntimeHelpers.RuntimeAsyncMaintainedData<TResult>());
-            RuntimeHelpers.PushAsyncData(ref dataFrame);
-            _thread = Thread.CurrentThread;
-            _previousExecutionCtx = _thread._executionContext;
-            _previousSyncCtx = _thread._synchronizationContext;
-        }
-
-        public void Pop()
-        {
-            // The common case is that these have not changed, so avoid the cost of a write barrier if not needed.
-            if (_previousSyncCtx != _thread._synchronizationContext)
-            {
-                // Restore changed SynchronizationContext back to previous
-                _thread._synchronizationContext = _previousSyncCtx;
-            }
-
-            ExecutionContext? currentExecutionCtx = _thread._executionContext;
-            if (_previousExecutionCtx != currentExecutionCtx)
-            {
-                ExecutionContext.RestoreChangedContextToThread(_thread, _previousExecutionCtx, currentExecutionCtx);
-            }
-            RuntimeHelpers.PopAsyncData(ref dataFrame);
-        }
-
-        public ValueTask<TResult> FromResult(TResult result)
-        {
-            if (dataFrame._maintainedData == null || (dataFrame._maintainedData._suspendActive == 0))
-                return ValueTask.FromResult<TResult>(result);
-            return new ValueTask<TResult>((Task<TResult>)dataFrame._maintainedData.GetTask());
-        }
-
-        public ValueTask<TResult> FromException(Exception e) { return ValueTask.FromException<TResult>(e); }
-    }
-    // Used to implement Runtime implemented Task suspension handling
-    internal struct RuntimeValueTaskState
-    {
-        private RuntimeHelpers.AsyncDataFrame dataFrame;
-        // Store current ExecutionContext and SynchronizationContext as "previousXxx".
-        // This allows us to restore them and undo any Context changes made in stateMachine.MoveNext
-        // so that they won't "leak" out of the first await.
-        public ExecutionContext? _previousExecutionCtx;
-        public SynchronizationContext? _previousSyncCtx;
-        public Thread _thread;
-
-        public void Push()
-        {
-            dataFrame = new RuntimeHelpers.AsyncDataFrame(()=> new RuntimeHelpers.RuntimeAsyncMaintainedData());
-            RuntimeHelpers.PushAsyncData(ref dataFrame);
-            _thread = Thread.CurrentThread;
-            _previousExecutionCtx = _thread._executionContext;
-            _previousSyncCtx = _thread._synchronizationContext;
-        }
-
-        public void Pop()
-        {
-            // The common case is that these have not changed, so avoid the cost of a write barrier if not needed.
-            if (_previousSyncCtx != _thread._synchronizationContext)
-            {
-                // Restore changed SynchronizationContext back to previous
-                _thread._synchronizationContext = _previousSyncCtx;
-            }
-
-            ExecutionContext? currentExecutionCtx = _thread._executionContext;
-            if (_previousExecutionCtx != currentExecutionCtx)
-            {
-                ExecutionContext.RestoreChangedContextToThread(_thread, _previousExecutionCtx, currentExecutionCtx);
-            }
-            RuntimeHelpers.PopAsyncData(ref dataFrame);
-        }
-
-        public ValueTask FromResult()
-        {
-            if (dataFrame._maintainedData == null || (dataFrame._maintainedData._suspendActive == 0))
-                return ValueTask.CompletedTask;
-            return new ValueTask(dataFrame._maintainedData.GetTask());
-        }
-
-        public ValueTask FromException(Exception e) { return ValueTask.FromException(e); }
-    }
-#pragma warning restore CA1822
-#endif
-
     /// <summary>
     /// Represents an asynchronous operation that produces a result at some time in the future.
     /// </summary>

From cc268ed2f07174fb294ed845020d83d5848183c5 Mon Sep 17 00:00:00 2001
From: Vladimir Sadov <vsadov@microsoft.com>
Date: Mon, 11 Nov 2024 05:45:45 -0800
Subject: [PATCH 120/203] [Runtime Async] Addressing test failures. (#2760)

* bug fix for the determinism test failure

* make benchmark-like tests to be build-only or to run less iterations
---
 src/coreclr/vm/methodtablebuilder.cpp         |  4 +-
 .../Loader/async/async2-eh-microbench.cs      |  6 +-
 .../Loader/async/async2-eh-microbench.csproj  |  2 +
 .../async/async2-mincallcost-microbench.cs    |  6 +-
 .../async2-mincallcost-microbench.csproj      |  2 +
 .../async2fibonacci-with-yields-topLevel.cs   |  5 +-
 .../async/async2fibonacci-with-yields.cs      |  3 +-
 .../async/async2fibonacci-without-yields.cs   |  3 +-
 src/tests/Loader/async/async2gcRootsScan.cs   | 94 +++++++++----------
 .../Loader/async/async2gcRootsScan.csproj     |  3 +
 .../Loader/async/syncfibonacci-with-yields.cs |  5 +-
 .../async/syncfibonacci-with-yields.csproj    |  4 +-
 .../async/syncfibonacci-without-yields.cs     |  5 +-
 .../async/syncfibonacci-without-yields.csproj |  4 +-
 .../taskbased-asyncfibonacci-with-yields.cs   |  5 +-
 ...askbased-asyncfibonacci-with-yields.csproj |  4 +-
 ...taskbased-asyncfibonacci-without-yields.cs |  5 +-
 ...based-asyncfibonacci-without-yields.csproj |  4 +-
 ...luetaskbased-asyncfibonacci-with-yields.cs |  3 +-
 ...askbased-asyncfibonacci-with-yields.csproj |  4 +-
 ...taskbased-asyncfibonacci-without-yields.cs |  5 +-
 ...based-asyncfibonacci-without-yields.csproj |  4 +-
 22 files changed, 103 insertions(+), 77 deletions(-)

diff --git a/src/coreclr/vm/methodtablebuilder.cpp b/src/coreclr/vm/methodtablebuilder.cpp
index 2b9d1ca98f5a6b..3bb9be3c26e28c 100644
--- a/src/coreclr/vm/methodtablebuilder.cpp
+++ b/src/coreclr/vm/methodtablebuilder.cpp
@@ -5942,9 +5942,11 @@ MethodTableBuilder::FindDeclMethodOnInterfaceEntry(bmtInterfaceEntry *pItfEntry,
             if ((slotDeclMethod->GetOwningType() == declRTMethod->GetOwningType()) &&
                 (slotDeclMethod->GetMethodDesc()->GetMethodTable() == declRTMethod->GetMethodDesc()->GetMethodTable()) &&
                 (slotDeclMethod->GetMethodDesc()->GetMemberDef() == declRTMethod->GetMethodDesc()->GetMemberDef()) &&
-                (slotDeclMethod->GetMethodDesc()->IsAsyncThunkMethod() == declRTMethod->GetMethodDesc()->IsAsyncThunkMethod()))
+                (slotDeclMethod->GetMethodDesc()->IsAsyncThunkMethod() != declRTMethod->GetMethodDesc()->IsAsyncThunkMethod()))
             {
                 declMethod = slotIt->Decl();
+                foundOtherVariant = true;
+                break;
             }
         }
 
diff --git a/src/tests/Loader/async/async2-eh-microbench.cs b/src/tests/Loader/async/async2-eh-microbench.cs
index b60d6a9b327201..994e443cf9e386 100644
--- a/src/tests/Loader/async/async2-eh-microbench.cs
+++ b/src/tests/Loader/async/async2-eh-microbench.cs
@@ -16,10 +16,12 @@
 
 public class Async2EHMicrobench
 {
-    [Fact]
-    public static void TestEntryPoint()
+    public static int Main()
     {
         Task.Run(AsyncEntry).Wait();
+
+        Console.WriteLine("Test Passed");
+        return 100;
     }
 
     public static async Task AsyncEntry()
diff --git a/src/tests/Loader/async/async2-eh-microbench.csproj b/src/tests/Loader/async/async2-eh-microbench.csproj
index d159a32e762f81..f7a2511415dff2 100644
--- a/src/tests/Loader/async/async2-eh-microbench.csproj
+++ b/src/tests/Loader/async/async2-eh-microbench.csproj
@@ -2,6 +2,8 @@
   <PropertyGroup>
     <Optimize>True</Optimize>
     <RequiresProcessIsolation>true</RequiresProcessIsolation>
+    <!-- This is a manual test/benchmark -->
+    <CLRTestKind>BuildOnly</CLRTestKind>
   </PropertyGroup>
   <ItemGroup>
     <Compile Include="$(MSBuildProjectName).cs" />
diff --git a/src/tests/Loader/async/async2-mincallcost-microbench.cs b/src/tests/Loader/async/async2-mincallcost-microbench.cs
index d2a64368ca60dc..122994d8061c37 100644
--- a/src/tests/Loader/async/async2-mincallcost-microbench.cs
+++ b/src/tests/Loader/async/async2-mincallcost-microbench.cs
@@ -16,10 +16,12 @@
 
 public class Async2MinCallCostMicrobench
 {
-    [Fact]
-    public static void TestEntryPoint()
+    public static int Main()
     {
         Task.Run(AsyncEntry).Wait();
+
+        Console.WriteLine("Test Passed");
+        return 100;
     }
 
     public static async Task AsyncEntry()
diff --git a/src/tests/Loader/async/async2-mincallcost-microbench.csproj b/src/tests/Loader/async/async2-mincallcost-microbench.csproj
index d159a32e762f81..f7a2511415dff2 100644
--- a/src/tests/Loader/async/async2-mincallcost-microbench.csproj
+++ b/src/tests/Loader/async/async2-mincallcost-microbench.csproj
@@ -2,6 +2,8 @@
   <PropertyGroup>
     <Optimize>True</Optimize>
     <RequiresProcessIsolation>true</RequiresProcessIsolation>
+    <!-- This is a manual test/benchmark -->
+    <CLRTestKind>BuildOnly</CLRTestKind>
   </PropertyGroup>
   <ItemGroup>
     <Compile Include="$(MSBuildProjectName).cs" />
diff --git a/src/tests/Loader/async/async2fibonacci-with-yields-topLevel.cs b/src/tests/Loader/async/async2fibonacci-with-yields-topLevel.cs
index 977064a83740e3..ad1762fbdee49b 100644
--- a/src/tests/Loader/async/async2fibonacci-with-yields-topLevel.cs
+++ b/src/tests/Loader/async/async2fibonacci-with-yields-topLevel.cs
@@ -8,10 +8,11 @@
 
 const uint Threshold = 1_000;
 bool done = false;
+const int iterations = 1;
 
 async Task AsyncEntry()
 {
-    for (int i = 0; i < 10 && !done; i++)
+    for (int i = 0; i < iterations && !done; i++)
     {
         var sw = new Stopwatch();
         sw.Start();
@@ -59,4 +60,4 @@ int Test()
     return 100;
 }
 
-return Test();
\ No newline at end of file
+return Test();
diff --git a/src/tests/Loader/async/async2fibonacci-with-yields.cs b/src/tests/Loader/async/async2fibonacci-with-yields.cs
index 6df9ddc44445a9..9a8e9b505a55c1 100644
--- a/src/tests/Loader/async/async2fibonacci-with-yields.cs
+++ b/src/tests/Loader/async/async2fibonacci-with-yields.cs
@@ -10,6 +10,7 @@ public class Async2FibonacceWithYields
 {
     const uint Threshold = 1_000;
     static bool done;
+    const int iterations = 1;
 
     [Fact]
     public static void Test()
@@ -19,7 +20,7 @@ public static void Test()
 
     public static async Task AsyncEntry()
     {
-        for (int i = 0; i < 10 && !done; i++)
+        for (int i = 0; i < iterations && !done; i++)
         {
             var sw = new Stopwatch();
             sw.Start();
diff --git a/src/tests/Loader/async/async2fibonacci-without-yields.cs b/src/tests/Loader/async/async2fibonacci-without-yields.cs
index bd47c8e869ab02..fc1d1fd7f1f087 100644
--- a/src/tests/Loader/async/async2fibonacci-without-yields.cs
+++ b/src/tests/Loader/async/async2fibonacci-without-yields.cs
@@ -10,6 +10,7 @@ public class Async2FibonacceWithoutYields
 {
     const uint Threshold = 1_000;
     static bool doYields = false;
+    const int iterations = 1;
 
     [Fact]
     public static void Test()
@@ -19,7 +20,7 @@ public static void Test()
 
     public static async Task AsyncEntry()
     {
-        for (int i = 0; i < 10; i++)
+        for (int i = 0; i < iterations; i++)
         {
             var sw = new Stopwatch();
             sw.Start();
diff --git a/src/tests/Loader/async/async2gcRootsScan.cs b/src/tests/Loader/async/async2gcRootsScan.cs
index f703b88c149d58..fffe874af38e50 100644
--- a/src/tests/Loader/async/async2gcRootsScan.cs
+++ b/src/tests/Loader/async/async2gcRootsScan.cs
@@ -17,17 +17,17 @@ static async Task<int> Recursive1(int n)
 
         // make some garbage
         object o1 = n;
-        //object o2 = n;
-        //object o3 = n;
-        //object o4 = n;
-        //object o5 = n;
-        //object o6 = n;
-        //object o7 = n;
-        //object o8 = n;
-        //object o9 = n;
-        //object o10 = n;
-        //object o11 = n;
-        //object o12 = n;
+        object o2 = n;
+        object o3 = n;
+        object o4 = n;
+        object o5 = n;
+        object o6 = n;
+        object o7 = n;
+        object o8 = n;
+        object o9 = n;
+        object o10 = n;
+        object o11 = n;
+        object o12 = n;
 
         if (n == 0)
             return await cTask;
@@ -35,17 +35,17 @@ static async Task<int> Recursive1(int n)
         var result = await Recursive1(n - 1);
 
         Assert.Equal(n, (int)o1);
-        //Assert.Equal(n, (int)o2);
-        //Assert.Equal(n, (int)o3);
-        //Assert.Equal(n, (int)o4);
-        //Assert.Equal(n, (int)o5);
-        //Assert.Equal(n, (int)o6);
-        //Assert.Equal(n, (int)o7);
-        //Assert.Equal(n, (int)o8);
-        //Assert.Equal(n, (int)o9);
-        //Assert.Equal(n, (int)o10);
-        //Assert.Equal(n, (int)o11);
-        //Assert.Equal(n, (int)o12);
+        Assert.Equal(n, (int)o2);
+        Assert.Equal(n, (int)o3);
+        Assert.Equal(n, (int)o4);
+        Assert.Equal(n, (int)o5);
+        Assert.Equal(n, (int)o6);
+        Assert.Equal(n, (int)o7);
+        Assert.Equal(n, (int)o8);
+        Assert.Equal(n, (int)o9);
+        Assert.Equal(n, (int)o10);
+        Assert.Equal(n, (int)o11);
+        Assert.Equal(n, (int)o12);
 
         return result;
     }
@@ -57,18 +57,17 @@ static async2 Task<int> Recursive2(int n)
         // make some garbage
         object o1 = n;
 
-// THIS CAUSES CRASHES IN UNWINDER FLAVOR
-        //object o2 = n;
-        //object o3 = n;
-        //object o4 = n;
-        //object o5 = n;
-        //object o6 = n;
-        //object o7 = n;
-        //object o8 = n;
-        //object o9 = n;
-        //object o10 = n;
-        //object o11 = n;
-        //object o12 = n;
+        object o2 = n;
+        object o3 = n;
+        object o4 = n;
+        object o5 = n;
+        object o6 = n;
+        object o7 = n;
+        object o8 = n;
+        object o9 = n;
+        object o10 = n;
+        object o11 = n;
+        object o12 = n;
 
         if (n == 0)
             return await cTask;
@@ -76,25 +75,24 @@ static async2 Task<int> Recursive2(int n)
         var result = await Recursive2(n - 1);
 
         Assert.Equal(n, (int)o1);
-        //Assert.Equal(n, (int)o2);
-        //Assert.Equal(n, (int)o3);
-        //Assert.Equal(n, (int)o4);
-        //Assert.Equal(n, (int)o5);
-        //Assert.Equal(n, (int)o6);
-        //Assert.Equal(n, (int)o7);
-        //Assert.Equal(n, (int)o8);
-        //Assert.Equal(n, (int)o9);
-        //Assert.Equal(n, (int)o10);
-        //Assert.Equal(n, (int)o11);
-        //Assert.Equal(n, (int)o12);
+        Assert.Equal(n, (int)o2);
+        Assert.Equal(n, (int)o3);
+        Assert.Equal(n, (int)o4);
+        Assert.Equal(n, (int)o5);
+        Assert.Equal(n, (int)o6);
+        Assert.Equal(n, (int)o7);
+        Assert.Equal(n, (int)o8);
+        Assert.Equal(n, (int)o9);
+        Assert.Equal(n, (int)o10);
+        Assert.Equal(n, (int)o11);
+        Assert.Equal(n, (int)o12);
 
         return result;
     }
 
     static System.Collections.Generic.List<object> d = new System.Collections.Generic.List<object>();
 
-    [Fact]
-    public static void Test()
+    public static int Main()
     {
         int numStacks = 10000;
         int stackDepth = 100;
@@ -207,5 +205,7 @@ void Warmup()
             cs.SetResult(100);
             Task.WaitAll(tasks);
         }
+
+        return 100;
     }
 }
diff --git a/src/tests/Loader/async/async2gcRootsScan.csproj b/src/tests/Loader/async/async2gcRootsScan.csproj
index 0f29ed860df43e..b6e7e75bd6ebc6 100644
--- a/src/tests/Loader/async/async2gcRootsScan.csproj
+++ b/src/tests/Loader/async/async2gcRootsScan.csproj
@@ -1,6 +1,9 @@
 <Project Sdk="Microsoft.NET.Sdk.IL">
   <PropertyGroup>
+    <Optimize>True</Optimize>
     <RequiresProcessIsolation>true</RequiresProcessIsolation>
+    <!-- This is a manual test/benchmark -->
+    <CLRTestKind>BuildOnly</CLRTestKind>
   </PropertyGroup>
   <ItemGroup>
    <Compile Include="$(MSBuildProjectName).cs" />
diff --git a/src/tests/Loader/async/syncfibonacci-with-yields.cs b/src/tests/Loader/async/syncfibonacci-with-yields.cs
index e8a76958b2bd89..fa05d09572ef0e 100644
--- a/src/tests/Loader/async/syncfibonacci-with-yields.cs
+++ b/src/tests/Loader/async/syncfibonacci-with-yields.cs
@@ -12,8 +12,7 @@ public class SyncFibonacciWithYields
 {
     const uint Threshold = 1_000;
 
-    [Fact]
-    public static void Test()
+    public static int Main()
     {
         for (int i = 0; i < 10; i++)
         {
@@ -22,6 +21,8 @@ public static void Test()
             uint result = A(100_000_000);
             Console.WriteLine($"{sw.ElapsedMilliseconds} ms result={result}");
         }
+
+        return 100;
     }
 
     static uint A(uint n)
diff --git a/src/tests/Loader/async/syncfibonacci-with-yields.csproj b/src/tests/Loader/async/syncfibonacci-with-yields.csproj
index 4a1973da13c789..c089ac9ebc8bab 100644
--- a/src/tests/Loader/async/syncfibonacci-with-yields.csproj
+++ b/src/tests/Loader/async/syncfibonacci-with-yields.csproj
@@ -1,7 +1,9 @@
 <Project Sdk="Microsoft.NET.Sdk">
   <PropertyGroup>
-    <RequiresProcessIsolation>true</RequiresProcessIsolation>
     <Optimize>True</Optimize>
+    <RequiresProcessIsolation>true</RequiresProcessIsolation>
+    <!-- This is a manual test/benchmark -->
+    <CLRTestKind>BuildOnly</CLRTestKind>
   </PropertyGroup>
   <ItemGroup>
     <Compile Include="$(MSBuildProjectName).cs" />
diff --git a/src/tests/Loader/async/syncfibonacci-without-yields.cs b/src/tests/Loader/async/syncfibonacci-without-yields.cs
index 27c428ab57313a..1b40117c02419b 100644
--- a/src/tests/Loader/async/syncfibonacci-without-yields.cs
+++ b/src/tests/Loader/async/syncfibonacci-without-yields.cs
@@ -12,8 +12,7 @@ public class SyncFibonacciWithoutYields
 {
     const uint Threshold = 1_000;
 
-    [Fact]
-    public static void Test()
+    public static int Main()
     {
         for (int i = 0; i < 10; i++)
         {
@@ -22,6 +21,8 @@ public static void Test()
             uint result = A(100_000_000);
             Console.WriteLine($"{sw.ElapsedMilliseconds} ms result={result}");
         }
+
+        return 100;
     }
 
     static uint A(uint n)
diff --git a/src/tests/Loader/async/syncfibonacci-without-yields.csproj b/src/tests/Loader/async/syncfibonacci-without-yields.csproj
index 4a1973da13c789..c089ac9ebc8bab 100644
--- a/src/tests/Loader/async/syncfibonacci-without-yields.csproj
+++ b/src/tests/Loader/async/syncfibonacci-without-yields.csproj
@@ -1,7 +1,9 @@
 <Project Sdk="Microsoft.NET.Sdk">
   <PropertyGroup>
-    <RequiresProcessIsolation>true</RequiresProcessIsolation>
     <Optimize>True</Optimize>
+    <RequiresProcessIsolation>true</RequiresProcessIsolation>
+    <!-- This is a manual test/benchmark -->
+    <CLRTestKind>BuildOnly</CLRTestKind>
   </PropertyGroup>
   <ItemGroup>
     <Compile Include="$(MSBuildProjectName).cs" />
diff --git a/src/tests/Loader/async/taskbased-asyncfibonacci-with-yields.cs b/src/tests/Loader/async/taskbased-asyncfibonacci-with-yields.cs
index c000811184fb17..11b131f959c92d 100644
--- a/src/tests/Loader/async/taskbased-asyncfibonacci-with-yields.cs
+++ b/src/tests/Loader/async/taskbased-asyncfibonacci-with-yields.cs
@@ -11,8 +11,7 @@ public class TaskBasedAsyncFibonacciWithYields
 {
     const uint Threshold = 1_000;
 
-    [Fact]
-    public static int Test() { return AsyncMain().Result; }
+    public static int Main() { return AsyncMain().Result; }
     
     static async Task<int> AsyncMain()
     {
@@ -52,4 +51,4 @@ static async Task<uint> B(uint n){
 
         return result;
     }
-}
\ No newline at end of file
+}
diff --git a/src/tests/Loader/async/taskbased-asyncfibonacci-with-yields.csproj b/src/tests/Loader/async/taskbased-asyncfibonacci-with-yields.csproj
index 4a1973da13c789..c089ac9ebc8bab 100644
--- a/src/tests/Loader/async/taskbased-asyncfibonacci-with-yields.csproj
+++ b/src/tests/Loader/async/taskbased-asyncfibonacci-with-yields.csproj
@@ -1,7 +1,9 @@
 <Project Sdk="Microsoft.NET.Sdk">
   <PropertyGroup>
-    <RequiresProcessIsolation>true</RequiresProcessIsolation>
     <Optimize>True</Optimize>
+    <RequiresProcessIsolation>true</RequiresProcessIsolation>
+    <!-- This is a manual test/benchmark -->
+    <CLRTestKind>BuildOnly</CLRTestKind>
   </PropertyGroup>
   <ItemGroup>
     <Compile Include="$(MSBuildProjectName).cs" />
diff --git a/src/tests/Loader/async/taskbased-asyncfibonacci-without-yields.cs b/src/tests/Loader/async/taskbased-asyncfibonacci-without-yields.cs
index 44312d4f7319b3..6be313d1986dec 100644
--- a/src/tests/Loader/async/taskbased-asyncfibonacci-without-yields.cs
+++ b/src/tests/Loader/async/taskbased-asyncfibonacci-without-yields.cs
@@ -11,8 +11,7 @@ public class TaskBasedAsyncFibonacciWithoutYields
 {
     const uint Threshold = 1_000;
 
-    [Fact]
-    public static int Test() { return AsyncMain().Result; }
+    public static int Main() { return AsyncMain().Result; }
     
     static async Task<int> AsyncMain()
     {
@@ -48,4 +47,4 @@ static async Task<uint> B(uint n)
 
         return result;
     }
-}
\ No newline at end of file
+}
diff --git a/src/tests/Loader/async/taskbased-asyncfibonacci-without-yields.csproj b/src/tests/Loader/async/taskbased-asyncfibonacci-without-yields.csproj
index 4a1973da13c789..c089ac9ebc8bab 100644
--- a/src/tests/Loader/async/taskbased-asyncfibonacci-without-yields.csproj
+++ b/src/tests/Loader/async/taskbased-asyncfibonacci-without-yields.csproj
@@ -1,7 +1,9 @@
 <Project Sdk="Microsoft.NET.Sdk">
   <PropertyGroup>
-    <RequiresProcessIsolation>true</RequiresProcessIsolation>
     <Optimize>True</Optimize>
+    <RequiresProcessIsolation>true</RequiresProcessIsolation>
+    <!-- This is a manual test/benchmark -->
+    <CLRTestKind>BuildOnly</CLRTestKind>
   </PropertyGroup>
   <ItemGroup>
     <Compile Include="$(MSBuildProjectName).cs" />
diff --git a/src/tests/Loader/async/valuetaskbased-asyncfibonacci-with-yields.cs b/src/tests/Loader/async/valuetaskbased-asyncfibonacci-with-yields.cs
index 12828c937e4036..a288db583b4fbe 100644
--- a/src/tests/Loader/async/valuetaskbased-asyncfibonacci-with-yields.cs
+++ b/src/tests/Loader/async/valuetaskbased-asyncfibonacci-with-yields.cs
@@ -11,8 +11,7 @@ public class ValueTaskBasedAsyncFibonacciWithYields
 {
     const uint Threshold = 1_000;
 
-    [Fact]
-    public static int Test() { return AsyncMain().Result; }
+    public static int Main() { return AsyncMain().Result; }
     
     static async Task<int> AsyncMain()
     {
diff --git a/src/tests/Loader/async/valuetaskbased-asyncfibonacci-with-yields.csproj b/src/tests/Loader/async/valuetaskbased-asyncfibonacci-with-yields.csproj
index 4a1973da13c789..c089ac9ebc8bab 100644
--- a/src/tests/Loader/async/valuetaskbased-asyncfibonacci-with-yields.csproj
+++ b/src/tests/Loader/async/valuetaskbased-asyncfibonacci-with-yields.csproj
@@ -1,7 +1,9 @@
 <Project Sdk="Microsoft.NET.Sdk">
   <PropertyGroup>
-    <RequiresProcessIsolation>true</RequiresProcessIsolation>
     <Optimize>True</Optimize>
+    <RequiresProcessIsolation>true</RequiresProcessIsolation>
+    <!-- This is a manual test/benchmark -->
+    <CLRTestKind>BuildOnly</CLRTestKind>
   </PropertyGroup>
   <ItemGroup>
     <Compile Include="$(MSBuildProjectName).cs" />
diff --git a/src/tests/Loader/async/valuetaskbased-asyncfibonacci-without-yields.cs b/src/tests/Loader/async/valuetaskbased-asyncfibonacci-without-yields.cs
index b27d19a8b15691..bdbb7b0b3647a2 100644
--- a/src/tests/Loader/async/valuetaskbased-asyncfibonacci-without-yields.cs
+++ b/src/tests/Loader/async/valuetaskbased-asyncfibonacci-without-yields.cs
@@ -11,8 +11,7 @@ public class ValueTaskBasedAsyncFibonacciWithoutYields
 {
     const uint Threshold = 1_000;
 
-    [Fact]
-    public static int Test() { return AsyncMain().Result; }
+    public static int Main() { return AsyncMain().Result; }
     
     static async ValueTask<int> AsyncMain()
     {
@@ -48,4 +47,4 @@ static async ValueTask<uint> B(uint n)
 
         return result;
     }
-}
\ No newline at end of file
+}
diff --git a/src/tests/Loader/async/valuetaskbased-asyncfibonacci-without-yields.csproj b/src/tests/Loader/async/valuetaskbased-asyncfibonacci-without-yields.csproj
index 4a1973da13c789..c089ac9ebc8bab 100644
--- a/src/tests/Loader/async/valuetaskbased-asyncfibonacci-without-yields.csproj
+++ b/src/tests/Loader/async/valuetaskbased-asyncfibonacci-without-yields.csproj
@@ -1,7 +1,9 @@
 <Project Sdk="Microsoft.NET.Sdk">
   <PropertyGroup>
-    <RequiresProcessIsolation>true</RequiresProcessIsolation>
     <Optimize>True</Optimize>
+    <RequiresProcessIsolation>true</RequiresProcessIsolation>
+    <!-- This is a manual test/benchmark -->
+    <CLRTestKind>BuildOnly</CLRTestKind>
   </PropertyGroup>
   <ItemGroup>
     <Compile Include="$(MSBuildProjectName).cs" />

From 0a10db8cae9ca24fc034c06db250b5c5df30886c Mon Sep 17 00:00:00 2001
From: Jakob Botsch Nielsen <Jakob.botsch.nielsen@gmail.com>
Date: Tue, 12 Nov 2024 10:49:06 +0100
Subject: [PATCH 121/203] [Runtime Async] Add support for shared generics 
 (#2755)

---
 README.md                                     |   2 +-
 src/coreclr/vm/callhelpers.cpp                |   2 +-
 src/coreclr/vm/gcenv.ee.common.cpp            |   4 +-
 src/coreclr/vm/interpreter.cpp                |   2 +-
 src/coreclr/vm/jitinterface.cpp               |  20 +-
 src/coreclr/vm/method.hpp                     |   7 +-
 src/coreclr/vm/prestub.cpp                    | 181 ++++++++++++++++--
 src/coreclr/vm/siginfo.hpp                    |   9 +
 src/coreclr/vm/stubgen.h                      |   1 -
 src/tests/Loader/async/async2sharedgeneric.cs | 116 +++++++++--
 src/tests/Loader/async/expected_failures.txt  |   2 +-
 11 files changed, 297 insertions(+), 49 deletions(-)

diff --git a/README.md b/README.md
index dd3e832c394d85..a7d5533b6df305 100644
--- a/README.md
+++ b/README.md
@@ -12,7 +12,7 @@ Current support in the prototypes looks like the following:
 
 | **Feature/characteristic**               |  **JIT based state machines**  |  **Unwinder based state machines**  |
 |------------------------------------------|:------------------------------:|:-----------------------------------:|
-| **Generics**                             |               ❌               |                  ❌                 |
+| **Generics**                             |               ✅               |                  ❌                 |
 | **Byrefs live across suspension points** |               ❌               |                  ✅                 |
 | **Exception handling**                   |               ✅               |                  ❌                 |
 | **Returns via return buffers**           |               ✅               |                  ❌                 |
diff --git a/src/coreclr/vm/callhelpers.cpp b/src/coreclr/vm/callhelpers.cpp
index 3906d396c87f54..c3fdfffe3396ae 100644
--- a/src/coreclr/vm/callhelpers.cpp
+++ b/src/coreclr/vm/callhelpers.cpp
@@ -341,7 +341,7 @@ void MethodDescCallSite::CallTargetWorker(const ARG_SLOT *pArguments, ARG_SLOT *
 
 #ifndef FEATURE_INTERPRETER
         _ASSERTE(isCallConv(m_methodSig.GetCallingConvention(), IMAGE_CEE_CS_CALLCONV_DEFAULT));
-        _ASSERTE(!(m_methodSig.GetCallingConventionInfo() & CORINFO_CALLCONV_PARAMTYPE));
+        _ASSERTE(!m_methodSig.HasGenericContextArg());
 #endif
 
 #ifdef DEBUGGING_SUPPORTED
diff --git a/src/coreclr/vm/gcenv.ee.common.cpp b/src/coreclr/vm/gcenv.ee.common.cpp
index 6cbd83dad75bab..8e8ebc981bf2fa 100644
--- a/src/coreclr/vm/gcenv.ee.common.cpp
+++ b/src/coreclr/vm/gcenv.ee.common.cpp
@@ -448,7 +448,7 @@ StackWalkAction GcStackCrawlCallBack(CrawlFrame* pCF, VOID* pData)
                     if (paramContextType == GENERIC_PARAM_CONTEXT_METHODDESC)
                     {
                         MethodDesc *pMDReal = dac_cast<PTR_MethodDesc>(pCF->GetParamTypeArg());
-                        _ASSERTE((pMDReal != NULL) || !pCF->IsFrameless());
+                        _ASSERTE((pMDReal != NULL) || !pCF->IsFrameless() || pMD->IsAsync2Method());
                         if (pMDReal != NULL)
                         {
                             GcReportLoaderAllocator(gcctx->f, gcctx->sc, pMDReal->GetLoaderAllocator());
@@ -457,7 +457,7 @@ StackWalkAction GcStackCrawlCallBack(CrawlFrame* pCF, VOID* pData)
                     else if (paramContextType == GENERIC_PARAM_CONTEXT_METHODTABLE)
                     {
                         MethodTable *pMTReal = dac_cast<PTR_MethodTable>(pCF->GetParamTypeArg());
-                        _ASSERTE((pMTReal != NULL) || !pCF->IsFrameless());
+                        _ASSERTE((pMTReal != NULL) || !pCF->IsFrameless() || pMD->IsAsync2Method());
                         if (pMTReal != NULL)
                         {
                             GcReportLoaderAllocator(gcctx->f, gcctx->sc, pMTReal->GetLoaderAllocator());
diff --git a/src/coreclr/vm/interpreter.cpp b/src/coreclr/vm/interpreter.cpp
index 80a058b3494d96..a64efc88bd9473 100644
--- a/src/coreclr/vm/interpreter.cpp
+++ b/src/coreclr/vm/interpreter.cpp
@@ -970,7 +970,7 @@ CorJitResult Interpreter::GenerateInterpreterStub(CEEInfo* comp,
         totalArgs++;
     }
 
-    if (sig.GetCallingConventionInfo() & CORINFO_CALLCONV_PARAMTYPE)
+    if (sig.HasGenericContextArg())
     {
         _ASSERTE(info->args.callConv & CORINFO_CALLCONV_PARAMTYPE);
         hasGenericsContextArg = true;
diff --git a/src/coreclr/vm/jitinterface.cpp b/src/coreclr/vm/jitinterface.cpp
index 1203da0fdf1cc3..d3b7cd5d437621 100644
--- a/src/coreclr/vm/jitinterface.cpp
+++ b/src/coreclr/vm/jitinterface.cpp
@@ -14374,7 +14374,7 @@ static Signature BuildResumptionStubCalliSignature(MetaSig& msig, LoaderAllocato
         numArgs++;
     }
 
-    if ((msig.GetCallingConvention() & CORINFO_CALLCONV_PARAMTYPE) != 0)
+    if (msig.HasGenericContextArg())
     {
         numArgs++;
     }
@@ -14412,7 +14412,7 @@ static Signature BuildResumptionStubCalliSignature(MetaSig& msig, LoaderAllocato
         sigBuilder.AppendElementType(ELEMENT_TYPE_OBJECT);
     }
 #ifndef TARGET_X86
-    if ((msig.GetCallingConvention() & CORINFO_CALLCONV_PARAMTYPE) != 0)
+    if (msig.HasGenericContextArg())
     {
         sigBuilder.AppendElementType(ELEMENT_TYPE_I);
     }
@@ -14429,7 +14429,7 @@ static Signature BuildResumptionStubCalliSignature(MetaSig& msig, LoaderAllocato
     }
 
 #ifdef TARGET_X86
-    if ((msig.GetCallingConvention() & CORINFO_CALLCONV_PARAMTYPE) != 0)
+    if (msig.HasGenericContextArg())
     {
         sigBuilder.AppendElementType(ELEMENT_TYPE_I);
     }
@@ -14449,7 +14449,6 @@ CORINFO_METHOD_HANDLE CEEJitInfo::getAsyncResumptionStub()
     } CONTRACTL_END;
 
     MethodDesc* md = m_pMethodBeingCompiled;
-    _ASSERTE(!md->IsSharedByGenericInstantiations());
 
     LoaderAllocator* loaderAlloc = md->GetLoaderAllocator();
 
@@ -14473,10 +14472,9 @@ CORINFO_METHOD_HANDLE CEEJitInfo::getAsyncResumptionStub()
     }
 
 #ifndef TARGET_X86
-    if ((msig.GetCallingConvention() & CORINFO_CALLCONV_PARAMTYPE) != 0)
+    if (msig.HasGenericContextArg())
     {
-        _ASSERTE(!"Generic context currently unhandled");
-        // Will need JIT to store this in an agreed upon place.
+        pCode->EmitLDC(0);
         numArgs++;
     }
 
@@ -14498,9 +14496,15 @@ CORINFO_METHOD_HANDLE CEEJitInfo::getAsyncResumptionStub()
     }
 
 #ifdef TARGET_X86
+    if (msig.HasGenericContextArg())
+    {
+        pCode->EmitLDC(0);
+        numArgs++;
+    }
+
     // Continuation
     pCode->EmitLDARG(0);
-    nmArgs++;
+    numArgs++;
 #endif
 
     // Resumption stubs are uniquely coupled to the code version (since the
diff --git a/src/coreclr/vm/method.hpp b/src/coreclr/vm/method.hpp
index ea745041e381a7..505edf0ed383de 100644
--- a/src/coreclr/vm/method.hpp
+++ b/src/coreclr/vm/method.hpp
@@ -1457,7 +1457,7 @@ class MethodDesc
     PTR_PCODE GetAddrOfNativeCodeSlot();
     PTR_AsyncMethodData GetAddrOfAsyncMethodData();
 #ifndef DACCESS_COMPILE
-    const AsyncMethodData& GetAsyncMethodData() { _ASSERTE(IsAsyncThunkMethod()); return *GetAddrOfAsyncMethodData(); }
+    const AsyncMethodData& GetAsyncMethodData() { _ASSERTE(HasAsyncMethodData()); return *GetAddrOfAsyncMethodData(); }
 #endif
 
     BOOL MayHaveNativeCode();
@@ -1615,8 +1615,7 @@ class MethodDesc
 
     MethodDesc* GetAsyncOtherVariant()
     {
-        // TODO This should be FindOrCreateAssociatedMethodDesc with some set of params
-        return GetMethodTable()->GetParallelMethodDesc(this, AsyncVariantLookup::AsyncOtherVariant);
+        return FindOrCreateAssociatedMethodDesc(this, GetMethodTable(), FALSE, GetMethodInstantiation(), TRUE, FALSE, TRUE, AsyncVariantLookup::AsyncOtherVariant);
     }
 
     // True if a MD is an funny BoxedEntryPointStub (not from the method table) or
@@ -1995,6 +1994,8 @@ class MethodDesc
     bool TryGenerateUnsafeAccessor(DynamicResolver** resolver, COR_ILMETHOD_DECODER** methodILDecoder);
     void EmitJitStateMachineBasedRuntimeAsyncThunk(MethodDesc* pAsyncOtherVariant, MetaSig& thunkMsig, ILStubLinker* pSL);
     void EmitAsync2MethodThunk(MethodDesc* pAsyncOtherVariant, MetaSig& msig, ILStubLinker* pSL);
+    SigPointer GetAsync2ThunkResultTypeSig();
+    int GetTokenForGenericMethodCallWithAsyncReturnType(ILCodeStream* pCode, MethodDesc* md);
 public:
     static void CreateDerivedTargetSigWithExtraParams(MetaSig& msig, SigBuilder* stubSigBuilder);
     bool TryGenerateTransientILImplementation(DynamicResolver** resolver, COR_ILMETHOD_DECODER** methodILDecoder);
diff --git a/src/coreclr/vm/prestub.cpp b/src/coreclr/vm/prestub.cpp
index ebf46e71a376e3..459dc754db3768 100644
--- a/src/coreclr/vm/prestub.cpp
+++ b/src/coreclr/vm/prestub.cpp
@@ -1744,15 +1744,17 @@ bool MethodDesc::TryGenerateAsyncThunk(DynamicResolver** resolver, COR_ILMETHOD_
     ULONG offsetOfAsyncDetailsUnused;
     bool isValueTypeUnused;
     AsyncTaskMethod asyncType = ClassifyAsyncMethod(GetSigPointer(), GetModule(), &offsetOfAsyncDetailsUnused, &isValueTypeUnused);
+
     MethodDesc *pAsyncOtherVariant = this->GetAsyncOtherVariant();
+    _ASSERTE(!IsWrapperStub() && !pAsyncOtherVariant->IsWrapperStub());
+
     MetaSig msig(this);
 
-    // [TODO] Handle generics
-    SigTypeContext emptyContext;
+    SigTypeContext sigContext(pAsyncOtherVariant);
     ILStubLinker sl(
         GetModule(),
         GetSignature(),
-        &emptyContext,
+        &sigContext,
         pAsyncOtherVariant,
         (ILStubLinkerFlags)ILSTUB_LINKER_FLAG_NONE);
 
@@ -1850,12 +1852,68 @@ void MethodDesc::EmitJitStateMachineBasedRuntimeAsyncThunk(MethodDesc* pAsyncOth
                 _ASSERTE(!this->GetMethodTable()->IsValueType());
                 pCode->EmitLDARG(localArg++);
             }
+
             for (UINT iArg = 0; iArg < thunkMsig.NumFixedArgs(); iArg++)
             {
                 pCode->EmitLDARG(localArg++);
             }
 
-            pCode->EmitCALL(pCode->GetToken(pAsyncOtherVariant), localArg, logicalResultLocal != UINT_MAX ? 1 : 0);
+            int token;
+            _ASSERTE(!pAsyncOtherVariant->IsWrapperStub());
+            if (pAsyncOtherVariant->HasClassOrMethodInstantiation())
+            {
+                // For generic code emit generic signatures.
+                int typeSigToken = mdTokenNil;
+                if (pAsyncOtherVariant->HasClassInstantiation())
+                {
+                    SigBuilder typeSigBuilder;
+                    typeSigBuilder.AppendElementType(ELEMENT_TYPE_GENERICINST);
+                    typeSigBuilder.AppendElementType(ELEMENT_TYPE_INTERNAL);
+                    // TODO: Encoding potentially shared method tables in
+                    // signatures of tokens seems odd, but this hits assert
+                    // with the typical method table.
+                    typeSigBuilder.AppendPointer(pAsyncOtherVariant->GetMethodTable());
+                    DWORD numClassTypeArgs = pAsyncOtherVariant->GetNumGenericClassArgs();
+                    typeSigBuilder.AppendData(numClassTypeArgs);
+                    for (DWORD i = 0; i < numClassTypeArgs; ++i)
+                    {
+                        typeSigBuilder.AppendElementType(ELEMENT_TYPE_VAR);
+                        typeSigBuilder.AppendData(i);
+                    }
+
+                    DWORD typeSigLen;
+                    PCCOR_SIGNATURE typeSig = (PCCOR_SIGNATURE)typeSigBuilder.GetSignature(&typeSigLen);
+                    typeSigToken = pCode->GetSigToken(typeSig, typeSigLen);
+                }
+
+                if (pAsyncOtherVariant->HasMethodInstantiation())
+                {
+                    SigBuilder methodSigBuilder;
+                    DWORD numMethodTypeArgs = pAsyncOtherVariant->GetNumGenericMethodArgs();
+                    methodSigBuilder.AppendByte(IMAGE_CEE_CS_CALLCONV_GENERICINST);
+                    methodSigBuilder.AppendData(numMethodTypeArgs);
+                    for (DWORD i = 0; i < numMethodTypeArgs; ++i)
+                    {
+                        methodSigBuilder.AppendElementType(ELEMENT_TYPE_MVAR);
+                        methodSigBuilder.AppendData(i);
+                    }
+
+                    DWORD sigLen;
+                    PCCOR_SIGNATURE sig = (PCCOR_SIGNATURE)methodSigBuilder.GetSignature(&sigLen);
+                    int methodSigToken = pCode->GetSigToken(sig, sigLen);
+                    token = pCode->GetToken(pAsyncOtherVariant, typeSigToken, methodSigToken);
+                }
+                else
+                {
+                    token = pCode->GetToken(pAsyncOtherVariant, typeSigToken);
+                }
+            }
+            else
+            {
+                token = pCode->GetToken(pAsyncOtherVariant);
+            }
+
+            pCode->EmitCALL(token, localArg, logicalResultLocal != UINT_MAX ? 1 : 0);
 
             if (logicalResultLocal != UINT_MAX)
                 pCode->EmitSTLOC(logicalResultLocal);
@@ -1867,24 +1925,31 @@ void MethodDesc::EmitJitStateMachineBasedRuntimeAsyncThunk(MethodDesc* pAsyncOth
         // Catch
         {
             pCode->BeginCatchBlock(pCode->GetToken(CoreLibBinder::GetClass(CLASS__EXCEPTION)));
-            MethodDesc* fromExceptionMD;
+
+            int fromExceptionToken;
             if (logicalResultLocal != UINT_MAX)
             {
-                MethodDesc *md;
+                MethodDesc* fromExceptionMD;
                 if (isValueTask)
-                    md = CoreLibBinder::GetMethod(METHOD__VALUETASK__FROM_EXCEPTION_1);
+                    fromExceptionMD = CoreLibBinder::GetMethod(METHOD__VALUETASK__FROM_EXCEPTION_1);
                 else
-                    md = CoreLibBinder::GetMethod(METHOD__TASK__FROM_EXCEPTION_1);
-                fromExceptionMD = FindOrCreateAssociatedMethodDesc(md, md->GetMethodTable(), FALSE, Instantiation(&thLogicalRetType, 1), FALSE);
+                    fromExceptionMD = CoreLibBinder::GetMethod(METHOD__TASK__FROM_EXCEPTION_1);
+
+                fromExceptionMD = FindOrCreateAssociatedMethodDesc(fromExceptionMD, fromExceptionMD->GetMethodTable(), FALSE, Instantiation(&thLogicalRetType, 1), FALSE);
+
+                fromExceptionToken = GetTokenForGenericMethodCallWithAsyncReturnType(pCode, fromExceptionMD);
             }
             else
             {
+                MethodDesc* fromExceptionMD;
                 if (isValueTask)
                     fromExceptionMD = CoreLibBinder::GetMethod(METHOD__VALUETASK__FROM_EXCEPTION);
                 else
                     fromExceptionMD = CoreLibBinder::GetMethod(METHOD__TASK__FROM_EXCEPTION);
+
+                fromExceptionToken = pCode->GetToken(fromExceptionMD);
             }
-            pCode->EmitCALL(pCode->GetToken(fromExceptionMD), 1, 1);
+            pCode->EmitCALL(fromExceptionToken, 1, 1);
             pCode->EmitSTLOC(returnLocal);
             pCode->EmitLEAVE(pReturnResultLabel);
             pCode->EndCatchBlock();
@@ -1912,7 +1977,9 @@ void MethodDesc::EmitJitStateMachineBasedRuntimeAsyncThunk(MethodDesc* pAsyncOth
         else
             md = CoreLibBinder::GetMethod(METHOD__TASK__FROM_RESULT_T);
         md = FindOrCreateAssociatedMethodDesc(md, md->GetMethodTable(), FALSE, Instantiation(&thLogicalRetType, 1), FALSE);
-        pCode->EmitCALL(pCode->GetToken(md), 1, 1);
+
+        int fromResultToken = GetTokenForGenericMethodCallWithAsyncReturnType(pCode, md);
+        pCode->EmitCALL(fromResultToken, 1, 1);
     }
     else
     {
@@ -1929,7 +1996,7 @@ void MethodDesc::EmitJitStateMachineBasedRuntimeAsyncThunk(MethodDesc* pAsyncOth
 
     pCode->EmitLabel(pSuspendedLabel);
 
-    MethodDesc* finalizeTaskReturningThunkMD;
+    int finalizeTaskReturningThunkToken;
     if (logicalResultLocal != UINT_MAX)
     {
         MethodDesc* md;
@@ -1937,20 +2004,100 @@ void MethodDesc::EmitJitStateMachineBasedRuntimeAsyncThunk(MethodDesc* pAsyncOth
             md = CoreLibBinder::GetMethod(METHOD__RUNTIME_HELPERS__FINALIZE_VALUETASK_RETURNING_THUNK_1);
         else
             md = CoreLibBinder::GetMethod(METHOD__RUNTIME_HELPERS__FINALIZE_TASK_RETURNING_THUNK_1);
-        finalizeTaskReturningThunkMD = FindOrCreateAssociatedMethodDesc(md, md->GetMethodTable(), FALSE, Instantiation(&thLogicalRetType, 1), FALSE);
+
+        md = FindOrCreateAssociatedMethodDesc(md, md->GetMethodTable(), FALSE, Instantiation(&thLogicalRetType, 1), FALSE);
+        finalizeTaskReturningThunkToken = GetTokenForGenericMethodCallWithAsyncReturnType(pCode, md);
     }
     else
     {
+        MethodDesc* md;
         if (isValueTask)
-            finalizeTaskReturningThunkMD = CoreLibBinder::GetMethod(METHOD__RUNTIME_HELPERS__FINALIZE_VALUETASK_RETURNING_THUNK);
+            md = CoreLibBinder::GetMethod(METHOD__RUNTIME_HELPERS__FINALIZE_VALUETASK_RETURNING_THUNK);
         else
-            finalizeTaskReturningThunkMD = CoreLibBinder::GetMethod(METHOD__RUNTIME_HELPERS__FINALIZE_TASK_RETURNING_THUNK);
+            md = CoreLibBinder::GetMethod(METHOD__RUNTIME_HELPERS__FINALIZE_TASK_RETURNING_THUNK);
+        finalizeTaskReturningThunkToken = pCode->GetToken(md);
     }
     pCode->EmitLDLOC(continuationLocal);
-    pCode->EmitCALL(pCode->GetToken(finalizeTaskReturningThunkMD), 1, 1);
+    pCode->EmitCALL(finalizeTaskReturningThunkToken, 1, 1);
     pCode->EmitRET();
 }
 
+// Given an async 2 method, return a SigPointer to the unwrapped result type. For
+// example, for async2 Task<T> Foo<T>() this returns the signature representing
+// (MVAR 0). For Task<int>, it returns the signature representing (int).
+SigPointer MethodDesc::GetAsync2ThunkResultTypeSig()
+{
+    _ASSERTE(GetAsyncMethodData().type == AsyncMethodType::TaskToAsync);
+    PCCOR_SIGNATURE pSigRaw;
+    DWORD cSig;
+    if (FAILED(GetMDImport()->GetSigOfMethodDef(GetMemberDef(), &cSig, &pSigRaw)))
+    {
+        _ASSERTE(!"Loaded MethodDesc should not fail to get signature");
+        pSigRaw = NULL;
+        cSig = 0;
+    }
+
+    SigPointer pSig(pSigRaw, cSig);
+    uint32_t callConvInfo;
+    IfFailThrow(pSig.GetCallingConvInfo(&callConvInfo));
+
+    if ((callConvInfo & IMAGE_CEE_CS_CALLCONV_GENERIC) != 0)
+    {
+        // GenParamCount
+        IfFailThrow(pSig.GetData(NULL));
+    }
+
+    // ParamCount
+    IfFailThrow(pSig.GetData(NULL));
+
+    // ReturnType comes now. Skip the modifiers (like async2 modifier).
+    IfFailThrow(pSig.SkipCustomModifiers());
+
+    // Get the start of the return type
+    PCCOR_SIGNATURE returnTypeSig;
+    uint32_t tailLength;
+    pSig.GetSignature(&returnTypeSig, &tailLength);
+
+    // Skip to the end of the return type so we can get the length.
+    IfFailThrow(pSig.SkipExactlyOne());
+
+    PCCOR_SIGNATURE returnTypeSigEnd;
+    pSig.GetSignature(&returnTypeSigEnd, &tailLength);
+
+    return SigPointer(returnTypeSig, (DWORD)(returnTypeSigEnd - returnTypeSig));
+}
+
+// Given a method Foo<T>, return a MethodSpec token for Foo<T> instantiated
+// with the result type from the current async method's return type. For
+// example, if "this" represents async2 Task<List<T>> Foo<T>(), and "md" is
+// Task.FromResult<T>, this returns a MethodSpec representing
+// Task.FromResult<List<T>>.
+int MethodDesc::GetTokenForGenericMethodCallWithAsyncReturnType(ILCodeStream* pCode, MethodDesc* md)
+{
+    if (!md->HasClassOrMethodInstantiation())
+    {
+        return pCode->GetToken(md);
+    }
+
+    // We never get here with a class instantiation currently.
+    _ASSERTE(!md->HasClassInstantiation());
+
+    SigBuilder methodSigBuilder;
+    methodSigBuilder.AppendByte(IMAGE_CEE_CS_CALLCONV_GENERICINST);
+    methodSigBuilder.AppendData(1);
+    SigPointer retTypeSig = GetAsync2ThunkResultTypeSig();
+    PCCOR_SIGNATURE retTypeSigRaw;
+    uint32_t retTypeSigLen;
+    retTypeSig.GetSignature(&retTypeSigRaw, &retTypeSigLen);
+    methodSigBuilder.AppendBlob((const PVOID)retTypeSigRaw, retTypeSigLen);
+
+    DWORD methodSigLen;
+    PCCOR_SIGNATURE methodSig = (PCCOR_SIGNATURE)methodSigBuilder.GetSignature(&methodSigLen);
+    int methodSigToken = pCode->GetSigToken(methodSig, methodSigLen);
+
+    return pCode->GetToken(md, mdTokenNil, methodSigToken);
+}
+
 void MethodDesc::EmitAsync2MethodThunk(MethodDesc* pAsyncOtherVariant, MetaSig& msig, ILStubLinker* pSL)
 {
     // Implement IL that is effectively the following
@@ -2492,7 +2639,7 @@ void MethodDesc::CreateDerivedTargetSigWithExtraParams(MetaSig& msig, SigBuilder
     stubSigBuilder->AppendByte(callingConvention);
 
     unsigned numArgs = msig.NumFixedArgs();
-    bool hasInstParam = (msig.GetCallingConventionInfo() & CORINFO_CALLCONV_PARAMTYPE) != 0;
+    bool hasInstParam = msig.HasGenericContextArg() != FALSE;
     if (hasInstParam)
         numArgs++;
     if (msig.HasAsyncContinuation())
diff --git a/src/coreclr/vm/siginfo.hpp b/src/coreclr/vm/siginfo.hpp
index fbfe1f73094069..79c22a1c3d51b5 100644
--- a/src/coreclr/vm/siginfo.hpp
+++ b/src/coreclr/vm/siginfo.hpp
@@ -729,6 +729,15 @@ class MetaSig
             return GetCallingConvention() == IMAGE_CEE_CS_CALLCONV_VARARG;
         }
 
+        //----------------------------------------------------------
+        // Does it have a generic context argument?
+        //----------------------------------------------------------
+        BOOL HasGenericContextArg()
+        {
+            LIMITED_METHOD_CONTRACT;
+            return m_CallConv & CORINFO_CALLCONV_PARAMTYPE;
+        }
+
         //----------------------------------------------------------
         // Is it an async call?
         //----------------------------------------------------------
diff --git a/src/coreclr/vm/stubgen.h b/src/coreclr/vm/stubgen.h
index f5f57024d48daf..c848b0665008fa 100644
--- a/src/coreclr/vm/stubgen.h
+++ b/src/coreclr/vm/stubgen.h
@@ -434,7 +434,6 @@ class TokenLookupMap
             MODE_ANY;
             GC_NOTRIGGER;
             PRECONDITION(pMD != NULL);
-            PRECONDITION(typeSignature != mdTokenNil);
             PRECONDITION(methodSignature != mdTokenNil);
         }
         CONTRACTL_END;
diff --git a/src/tests/Loader/async/async2sharedgeneric.cs b/src/tests/Loader/async/async2sharedgeneric.cs
index 992235b1c6dcc0..cafb18d1eab974 100644
--- a/src/tests/Loader/async/async2sharedgeneric.cs
+++ b/src/tests/Loader/async/async2sharedgeneric.cs
@@ -6,29 +6,117 @@
 using Xunit;
 public class Async2SharedGeneric
 {
-    static Type s_type;
     [Fact]
     public static void TestEntryPoint()
     {
-        int[] arr = new int[1];
-        AsyncTestEntryPoint<int>().Wait();
-        Assert.Equal(typeof(int), s_type);
-        AsyncTestEntryPoint<string>().Wait();
-        Assert.Equal(typeof(string), s_type);
-        AsyncTestEntryPoint<object>().Wait();
-        Assert.Equal(typeof(object), s_type);
+        Async1EntryPoint<int>(typeof(int), 42).Wait();
+        Async1EntryPoint<string>(typeof(string), "abc").Wait();
+        Async1EntryPoint<object>(typeof(object), "def").Wait();
+
+        Async2EntryPoint<int>(typeof(int), 142).Wait();
+        Async2EntryPoint<string>(typeof(string), "ghi").Wait();
+        Async2EntryPoint<object>(typeof(object), "jkl").Wait();
+    }
+
+    private static async Task Async1EntryPoint<T>(Type t, T value)
+    {
+        await new GenericClass<T>().InstanceMethod(t);
+        await GenericClass<T>.StaticMethod(t);
+        await GenericClass<T>.StaticMethod<T>(t, t);
+        await GenericClass<T>.StaticMethodAsync1(t);
+        await GenericClass<T>.StaticMethodAsync1<T>(t, t);
+        Assert.Equal(value, await GenericClass<T>.StaticReturnClassType(value));
+        Assert.Equal(value, await GenericClass<T>.StaticReturnMethodType<T>(value));
+        Assert.Equal(value, await GenericClass<T>.StaticReturnClassTypeAsync1(value));
+        Assert.Equal(value, await GenericClass<T>.StaticReturnMethodTypeAsync1<T>(value));
+    }
+
+    private static async2 Task Async2EntryPoint<T>(Type t, T value)
+    {
+        await new GenericClass<T>().InstanceMethod(t);
+        await GenericClass<T>.StaticMethod(t);
+        await GenericClass<T>.StaticMethod<T>(t, t);
+        await GenericClass<T>.StaticMethodAsync1(t);
+        await GenericClass<T>.StaticMethodAsync1<T>(t, t);
+        Assert.Equal(value, await GenericClass<T>.StaticReturnClassType(value));
+        Assert.Equal(value, await GenericClass<T>.StaticReturnMethodType<T>(value));
+        Assert.Equal(value, await GenericClass<T>.StaticReturnClassTypeAsync1(value));
+        Assert.Equal(value, await GenericClass<T>.StaticReturnMethodTypeAsync1<T>(value));
+    }
+}
+
+public class GenericClass<T>
+{
+    // 'this' is context
+    [MethodImpl(MethodImplOptions.NoInlining)]
+    public async2 Task InstanceMethod(Type t)
+    {
+        Assert.Equal(typeof(T), t);
+        await Task.Yield();
+        Assert.Equal(typeof(T), t);
+    }
+
+    // Class context
+    [MethodImpl(MethodImplOptions.NoInlining)]
+    public static async2 Task StaticMethod(Type t)
+    {
+        Assert.Equal(typeof(T), t);
+        await Task.Yield();
+        Assert.Equal(typeof(T), t);
     }
-    private static async Task AsyncTestEntryPoint<T>()
+
+    // Method context
+    [MethodImpl(MethodImplOptions.NoInlining)]
+    public static async2 Task StaticMethod<TM>(Type t, Type tm)
     {
-        await Async2TestEntryPoint<T>();
+        Assert.Equal(typeof(T), t);
+        Assert.Equal(typeof(TM), tm);
+        await Task.Yield();
+        Assert.Equal(typeof(T), t);
+        Assert.Equal(typeof(TM), tm);
     }
 
-    //This async method lacks 'await'
-#pragma warning disable 1998
+    // Class context
+    [MethodImpl(MethodImplOptions.NoInlining)]
+    public static async Task StaticMethodAsync1(Type t)
+    {
+        Assert.Equal(typeof(T), t);
+        await Task.Yield();
+        Assert.Equal(typeof(T), t);
+    }
 
+    // Method context
     [MethodImpl(MethodImplOptions.NoInlining)]
-    private static async2 Task Async2TestEntryPoint<T>()
+    public static async Task StaticMethodAsync1<TM>(Type t, Type tm)
+    {
+        Assert.Equal(typeof(T), t);
+        Assert.Equal(typeof(TM), tm);
+        await Task.Yield();
+        Assert.Equal(typeof(T), t);
+        Assert.Equal(typeof(TM), tm);
+    }
+
+    public static async2 Task<T> StaticReturnClassType(T value)
+    {
+        await Task.Yield();
+        return value;
+    }
+
+    public static async2 Task<TM> StaticReturnMethodType<TM>(TM value)
+    {
+        await Task.Yield();
+        return value;
+    }
+
+    public static async Task<T> StaticReturnClassTypeAsync1(T value)
+    {
+        await Task.Yield();
+        return value;
+    }
+
+    public static async Task<TM> StaticReturnMethodTypeAsync1<TM>(TM value)
     {
-        s_type = typeof(T);
+        await Task.Yield();
+        return value;
     }
 }
diff --git a/src/tests/Loader/async/expected_failures.txt b/src/tests/Loader/async/expected_failures.txt
index 5fe6eb747541c8..045d3cfe1eb8ec 100644
--- a/src/tests/Loader/async/expected_failures.txt
+++ b/src/tests/Loader/async/expected_failures.txt
@@ -6,4 +6,4 @@ DOTNET_RuntimeAsyncViaJitGeneratedStateMachines=0:
 * Loader\async\async2sharedgeneric\async2sharedgeneric.dll (generics unsupported)
 
 DOTNET_RuntimeAsyncViaJitGeneratedStateMachines=1:
-* Loader\async\async2sharedgeneric\async2sharedgeneric.dll (generics unsupported)
\ No newline at end of file
+* None
\ No newline at end of file

From 19ce23f0803bbbeabf088126a6ea423d1774435c Mon Sep 17 00:00:00 2001
From: Jakob Botsch Nielsen <Jakob.botsch.nielsen@gmail.com>
Date: Wed, 13 Nov 2024 10:20:59 +0100
Subject: [PATCH 122/203] [RuntimeAsync] Add x86 support (#2767)

The main thing needed is that the JIT32 GC encoder has a hard limit on 4
epilogs, so the JIT needs to share suspension epilogs, and also needs to ensure
we have at most 3 other normal epilogs when we get to the async2 transformation.
---
 src/coreclr/jit/async.cpp        | 95 +++++++++++++++++++++-----------
 src/coreclr/jit/async.h          |  1 +
 src/coreclr/jit/block.cpp        |  5 +-
 src/coreclr/jit/fgdiagnostic.cpp |  8 ++-
 src/coreclr/jit/flowgraph.cpp    | 13 ++++-
 src/coreclr/jit/lclvars.cpp      |  6 +-
 src/coreclr/vm/i386/cgencpu.h    |  6 +-
 src/coreclr/vm/jithelpers.cpp    | 11 +++-
 src/coreclr/vm/jitinterface.cpp  |  4 ++
 src/coreclr/vm/prestub.cpp       |  9 ++-
 10 files changed, 111 insertions(+), 47 deletions(-)

diff --git a/src/coreclr/jit/async.cpp b/src/coreclr/jit/async.cpp
index 46cd7e4c1cabf6..a636af937252f5 100644
--- a/src/coreclr/jit/async.cpp
+++ b/src/coreclr/jit/async.cpp
@@ -192,6 +192,22 @@ PhaseStatus Async2Transformation::Run()
     m_objectClsHnd = m_comp->info.compCompHnd->getBuiltinClass(CLASSID_SYSTEM_OBJECT);
     m_byteClsHnd   = m_comp->info.compCompHnd->getBuiltinClass(CLASSID_SYSTEM_BYTE);
 
+#ifdef JIT32_GCENCODER
+    // Due to a hard cap on epilogs we need a shared return here.
+    m_sharedReturnBB = m_comp->fgNewBBafter(BBJ_RETURN, m_comp->fgLastBBInMainFunction(), false);
+    m_sharedReturnBB->bbSetRunRarely();
+    m_sharedReturnBB->clearTryIndex();
+    m_sharedReturnBB->clearHndIndex();
+
+    GenTree* continuation = m_comp->gtNewLclvNode(m_newContinuationVar, TYP_REF);
+    GenTree* ret = m_comp->gtNewOperNode(GT_RETURN_SUSPEND, TYP_VOID, continuation);
+    LIR::AsRange(m_sharedReturnBB).InsertAtEnd(continuation, ret);
+
+    JITDUMP("Created shared return BB " FMT_BB "\n", m_sharedReturnBB->bbNum);
+
+    DISPRANGE(LIR::AsRange(m_sharedReturnBB));
+#endif
+
     if (m_comp->opts.OptimizationEnabled())
     {
         if (m_comp->m_dfsTree == nullptr)
@@ -418,13 +434,6 @@ void Async2Transformation::Transform(
         returnInGCData = varTypeIsGC(call->gtReturnType);
     }
 
-    if (returnSize > 0)
-    {
-        JITDUMP("  Will store return of type %s, size %u in %sGC data\n",
-                call->gtReturnType == TYP_STRUCT ? returnStructLayout->GetClassName() : varTypeName(call->gtReturnType),
-                returnSize, returnInGCData ? "" : "non-");
-    }
-
     assert((returnSize > 0) == (call->gtReturnType != TYP_VOID));
 
     // The return value is always stored:
@@ -441,9 +450,25 @@ void Async2Transformation::Transform(
     {
         returnValDataOffset = dataSize;
         dataSize += returnSize;
+    }
+
+#ifdef DEBUG
+    if (returnSize > 0)
+    {
+        JITDUMP("  Will store return of type %s, size %u in",
+            call->gtReturnType == TYP_STRUCT ? returnStructLayout->GetClassName() : varTypeName(call->gtReturnType),
+            returnSize);
 
-        JITDUMP("  at offset %u\n", returnValDataOffset);
+        if (returnInGCData)
+        {
+            JITDUMP(" GC data\n");
+        }
+        else
+        {
+            JITDUMP(" non-GC data at offset %u\n", returnValDataOffset);
+        }
     }
+#endif
 
     unsigned exceptionGCDataIndex = UINT_MAX;
     if (block->hasTryIndex())
@@ -543,15 +568,20 @@ void Async2Transformation::Transform(
         m_lastSuspensionBB = m_comp->fgLastBBInMainFunction();
     }
 
-    BasicBlock* retBB = m_comp->fgNewBBafter(BBJ_RETURN, m_lastSuspensionBB, false);
-    retBB->bbSetRunRarely();
-    retBB->clearTryIndex();
-    retBB->clearHndIndex();
-    m_lastSuspensionBB = retBB;
+    BasicBlock* suspendBB = m_comp->fgNewBBafter(BBJ_RETURN, m_lastSuspensionBB, false);
+    suspendBB->bbSetRunRarely();
+    suspendBB->clearTryIndex();
+    suspendBB->clearHndIndex();
+    m_lastSuspensionBB = suspendBB;
+
+    if (m_sharedReturnBB != nullptr)
+    {
+        suspendBB->SetKindAndTargetEdge(BBJ_ALWAYS, m_comp->fgAddRefPred(m_sharedReturnBB, suspendBB));
+    }
 
-    JITDUMP("  Created suspension " FMT_BB " for state %u\n", retBB->bbNum, stateNum);
+    JITDUMP("  Created suspension " FMT_BB " for state %u\n", suspendBB->bbNum, stateNum);
 
-    FlowEdge* retBBEdge = m_comp->fgAddRefPred(retBB, block);
+    FlowEdge* retBBEdge = m_comp->fgAddRefPred(suspendBB, block);
     block->SetCond(retBBEdge, block->GetTargetEdge());
 
     block->GetTrueEdge()->setLikelihood(RESUME_SUSPEND_LIKELIHOOD);
@@ -564,27 +594,27 @@ void Async2Transformation::Transform(
     GenTreeCall* allocContinuation = m_comp->gtNewHelperCallNode(CORINFO_HELP_ALLOC_CONTINUATION, TYP_REF,
                                                                  returnedContinuation, gcRefsCountNode, dataSizeNode);
 
-    m_comp->compCurBB = retBB;
+    m_comp->compCurBB = suspendBB;
     m_comp->fgMorphTree(allocContinuation);
 
-    LIR::AsRange(retBB).InsertAtEnd(LIR::SeqTree(m_comp, allocContinuation));
+    LIR::AsRange(suspendBB).InsertAtEnd(LIR::SeqTree(m_comp, allocContinuation));
 
     GenTree* storeNewContinuation = m_comp->gtNewStoreLclVarNode(m_newContinuationVar, allocContinuation);
-    LIR::AsRange(retBB).InsertAtEnd(storeNewContinuation);
+    LIR::AsRange(suspendBB).InsertAtEnd(storeNewContinuation);
 
     // Fill in 'Resume'
     GenTree* newContinuation = m_comp->gtNewLclvNode(m_newContinuationVar, TYP_REF);
     unsigned resumeOffset    = m_comp->info.compCompHnd->getFieldOffset(m_async2Info.continuationResumeFldHnd);
     GenTree* resumeStubAddr  = CreateResumptionStubAddrTree();
     GenTree* storeResume     = StoreAtOffset(newContinuation, resumeOffset, resumeStubAddr);
-    LIR::AsRange(retBB).InsertAtEnd(LIR::SeqTree(m_comp, storeResume));
+    LIR::AsRange(suspendBB).InsertAtEnd(LIR::SeqTree(m_comp, storeResume));
 
     // Fill in 'state'
     newContinuation       = m_comp->gtNewLclvNode(m_newContinuationVar, TYP_REF);
     unsigned stateOffset  = m_comp->info.compCompHnd->getFieldOffset(m_async2Info.continuationStateFldHnd);
     GenTree* stateNumNode = m_comp->gtNewIconNode((ssize_t)stateNum, TYP_INT);
     GenTree* storeState   = StoreAtOffset(newContinuation, stateOffset, stateNumNode);
-    LIR::AsRange(retBB).InsertAtEnd(LIR::SeqTree(m_comp, storeState));
+    LIR::AsRange(suspendBB).InsertAtEnd(LIR::SeqTree(m_comp, storeState));
 
     // Fill in 'flags'
     unsigned continuationFlags = 0;
@@ -599,7 +629,7 @@ void Async2Transformation::Transform(
     unsigned flagsOffset = m_comp->info.compCompHnd->getFieldOffset(m_async2Info.continuationFlagsFldHnd);
     GenTree* flagsNode   = m_comp->gtNewIconNode((ssize_t)continuationFlags, TYP_INT);
     GenTree* storeFlags  = StoreAtOffset(newContinuation, flagsOffset, flagsNode);
-    LIR::AsRange(retBB).InsertAtEnd(LIR::SeqTree(m_comp, storeFlags));
+    LIR::AsRange(suspendBB).InsertAtEnd(LIR::SeqTree(m_comp, storeFlags));
 
     // Fill in GC pointers
     if (gcRefsCount > 0)
@@ -610,7 +640,7 @@ void Async2Transformation::Transform(
         unsigned gcDataOffset = m_comp->info.compCompHnd->getFieldOffset(m_async2Info.continuationGCDataFldHnd);
         GenTree* gcDataInd    = LoadFromOffset(newContinuation, gcDataOffset, TYP_REF);
         GenTree* storeAllocedObjectArr = m_comp->gtNewStoreLclVarNode(objectArrLclNum, gcDataInd);
-        LIR::AsRange(retBB).InsertAtEnd(LIR::SeqTree(m_comp, storeAllocedObjectArr));
+        LIR::AsRange(suspendBB).InsertAtEnd(LIR::SeqTree(m_comp, storeAllocedObjectArr));
 
         for (LiveLocalInfo& inf : m_liveLocals)
         {
@@ -627,7 +657,7 @@ void Async2Transformation::Transform(
                 GenTree* store =
                     StoreAtOffset(objectArr, OFFSETOF__CORINFO_Array__data + (inf.GCDataIndex * TARGET_POINTER_SIZE),
                                   value);
-                LIR::AsRange(retBB).InsertAtEnd(LIR::SeqTree(m_comp, store));
+                LIR::AsRange(suspendBB).InsertAtEnd(LIR::SeqTree(m_comp, store));
             }
             else
             {
@@ -659,7 +689,7 @@ void Async2Transformation::Transform(
                     unsigned offset =
                         OFFSETOF__CORINFO_Array__data + ((inf.GCDataIndex + gcRefIndex) * TARGET_POINTER_SIZE);
                     GenTree* store = StoreAtOffset(objectArr, offset, value);
-                    LIR::AsRange(retBB).InsertAtEnd(LIR::SeqTree(m_comp, store));
+                    LIR::AsRange(suspendBB).InsertAtEnd(LIR::SeqTree(m_comp, store));
 
                     gcRefIndex++;
 
@@ -678,7 +708,7 @@ void Async2Transformation::Transform(
                             store = m_comp->gtNewStoreLclFldNode(inf.LclNum, TYP_REF, i * TARGET_POINTER_SIZE, null);
                         }
 
-                        LIR::AsRange(retBB).InsertAtEnd(LIR::SeqTree(m_comp, store));
+                        LIR::AsRange(suspendBB).InsertAtEnd(LIR::SeqTree(m_comp, store));
                     }
                 }
 
@@ -696,7 +726,7 @@ void Async2Transformation::Transform(
         unsigned dataOffset          = m_comp->info.compCompHnd->getFieldOffset(m_async2Info.continuationDataFldHnd);
         GenTree* dataInd             = LoadFromOffset(newContinuation, dataOffset, TYP_REF);
         GenTree* storeAllocedByteArr = m_comp->gtNewStoreLclVarNode(byteArrLclNum, dataInd);
-        LIR::AsRange(retBB).InsertAtEnd(LIR::SeqTree(m_comp, storeAllocedByteArr));
+        LIR::AsRange(suspendBB).InsertAtEnd(LIR::SeqTree(m_comp, storeAllocedByteArr));
 
         if (m_comp->doesMethodHavePatchpoints() || m_comp->opts.IsOSR())
         {
@@ -709,7 +739,7 @@ void Async2Transformation::Transform(
             GenTree* byteArr               = m_comp->gtNewLclvNode(byteArrLclNum, TYP_REF);
             unsigned offset                = OFFSETOF__CORINFO_Array__data;
             GenTree* storePatchpointOffset = StoreAtOffset(byteArr, offset, ilOffsetToStore);
-            LIR::AsRange(retBB).InsertAtEnd(LIR::SeqTree(m_comp, storePatchpointOffset));
+            LIR::AsRange(suspendBB).InsertAtEnd(LIR::SeqTree(m_comp, storePatchpointOffset));
         }
 
         // Fill in data
@@ -751,13 +781,16 @@ void Async2Transformation::Transform(
                 store = StoreAtOffset(byteArr, offset, value);
             }
 
-            LIR::AsRange(retBB).InsertAtEnd(LIR::SeqTree(m_comp, store));
+            LIR::AsRange(suspendBB).InsertAtEnd(LIR::SeqTree(m_comp, store));
         }
     }
 
-    newContinuation = m_comp->gtNewLclvNode(m_newContinuationVar, TYP_REF);
-    GenTree* ret    = m_comp->gtNewOperNode(GT_RETURN_SUSPEND, TYP_VOID, newContinuation);
-    LIR::AsRange(retBB).InsertAtEnd(newContinuation, ret);
+    if (suspendBB->KindIs(BBJ_RETURN))
+    {
+        newContinuation = m_comp->gtNewLclvNode(m_newContinuationVar, TYP_REF);
+        GenTree* ret = m_comp->gtNewOperNode(GT_RETURN_SUSPEND, TYP_VOID, newContinuation);
+        LIR::AsRange(suspendBB).InsertAtEnd(newContinuation, ret);
+    }
 
     if (m_lastResumptionBB == nullptr)
     {
diff --git a/src/coreclr/jit/async.h b/src/coreclr/jit/async.h
index b76330bb272456..377ab1a0b56692 100644
--- a/src/coreclr/jit/async.h
+++ b/src/coreclr/jit/async.h
@@ -32,6 +32,7 @@ class Async2Transformation
     unsigned                      m_exceptionVar            = BAD_VAR_NUM;
     BasicBlock*                   m_lastSuspensionBB        = nullptr;
     BasicBlock*                   m_lastResumptionBB        = nullptr;
+    BasicBlock* m_sharedReturnBB                            = nullptr;
 
     void LiftLIREdges(BasicBlock*                    block,
                       GenTree*                       beyond,
diff --git a/src/coreclr/jit/block.cpp b/src/coreclr/jit/block.cpp
index fec2250d73fa46..fd9c54545b6ef8 100644
--- a/src/coreclr/jit/block.cpp
+++ b/src/coreclr/jit/block.cpp
@@ -494,6 +494,7 @@ void BasicBlock::dspFlags() const
         {BBF_HAS_ALIGN, "has-align"},
         {BBF_HAS_MDARRAYREF, "mdarr"},
         {BBF_NEEDS_GCPOLL, "gcpoll"},
+        {BBF_ASYNC_RESUMPTION, "resume"},
     };
 
     bool first = true;
@@ -509,10 +510,6 @@ void BasicBlock::dspFlags() const
             first = false;
         }
     }
-    if (bbFlags & BBF_ASYNC_RESUMPTION)
-    {
-        printf("resume ");
-    }
 }
 
 /*****************************************************************************
diff --git a/src/coreclr/jit/fgdiagnostic.cpp b/src/coreclr/jit/fgdiagnostic.cpp
index 4ecaa9f183f275..ab14613248bd20 100644
--- a/src/coreclr/jit/fgdiagnostic.cpp
+++ b/src/coreclr/jit/fgdiagnostic.cpp
@@ -2726,6 +2726,9 @@ bool BBPredsChecker::CheckEhTryDsc(BasicBlock* block, BasicBlock* blockPred, EHb
         return true;
     }
 
+    // Async resumptions are allowed to jump into try blocks at any point. They
+    // are introduced late enough that the invariant of single entry is no
+    // longer necessary.
     if (blockPred->HasFlag(BBF_ASYNC_RESUMPTION))
     {
         return true;
@@ -3122,7 +3125,8 @@ void Compiler::fgDebugCheckBBlist(bool checkBBNum /* = false */, bool checkBBRef
         // A branch or fall-through to a BBJ_CALLFINALLY block must come from the `try` region associated
         // with the finally block the BBJ_CALLFINALLY is targeting. There is one special case: if the
         // BBJ_CALLFINALLY is the first block of a `try`, then its predecessor can be outside the `try`:
-        // either a branch or fall-through to the first block.
+        // either a branch or fall-through to the first block. Similarly internal resumption blocks for
+        // async2 are allowed to do this as they are introduced late enough that we no longer need the invariant.
         //
         // Note that this IR condition is a choice. It naturally occurs when importing EH constructs.
         // This condition prevents flow optimizations from skipping blocks in a `try` and branching
@@ -3160,7 +3164,7 @@ void Compiler::fgDebugCheckBBlist(bool checkBBNum /* = false */, bool checkBBRef
                 }
                 else
                 {
-                    assert(bbInTryRegions(finallyIndex, block));
+                    assert(bbInTryRegions(finallyIndex, block) || block->HasFlag(BBF_ASYNC_RESUMPTION));
                 }
             }
         }
diff --git a/src/coreclr/jit/flowgraph.cpp b/src/coreclr/jit/flowgraph.cpp
index ee07f03599e878..9cfd9e3291a1be 100644
--- a/src/coreclr/jit/flowgraph.cpp
+++ b/src/coreclr/jit/flowgraph.cpp
@@ -2420,7 +2420,18 @@ PhaseStatus Compiler::fgAddInternal()
         }
         else
         {
-            merger.SetMaxReturns(MergedReturns::ReturnCountHardLimit);
+            unsigned limit = MergedReturns::ReturnCountHardLimit;
+#ifdef JIT32_GCENCODER
+            // For the jit32 GC encoder the limit is an actual hard limit. In
+            // async2 functions we will be introducing another return during
+            // the async2 transformation, so make sure there's a free epilog
+            // for it.
+            if (compIsAsync2())
+            {
+                limit--;
+            }
+#endif
+            merger.SetMaxReturns(limit);
         }
     }
 
diff --git a/src/coreclr/jit/lclvars.cpp b/src/coreclr/jit/lclvars.cpp
index c53ad722a50593..d58445d6c95db8 100644
--- a/src/coreclr/jit/lclvars.cpp
+++ b/src/coreclr/jit/lclvars.cpp
@@ -1499,7 +1499,10 @@ void Compiler::lvaInitAsyncContinuation(InitVarDscInfo* varDscInfo)
     lvaAsyncContinuationArg = varDscInfo->varNum;
     LclVarDsc* varDsc       = varDscInfo->varDsc;
     varDsc->lvType          = TYP_REF;
-    varDsc->lvIsParam       = 1;
+    varDsc->lvIsParam       = true;
+
+     // The final home for this incoming register might be our local stack frame
+    varDsc->lvOnFrame = true;
 
 #ifdef DEBUG
     varDsc->lvReason = "Async continuation arg";
@@ -1514,7 +1517,6 @@ void Compiler::lvaInitAsyncContinuation(InitVarDscInfo* varDscInfo)
 #if FEATURE_MULTIREG_ARGS
         varDsc->SetOtherArgReg(REG_NA);
 #endif
-        varDsc->lvOnFrame = true; // The final home for this incoming register might be our local stack frame
 
         varDscInfo->intRegArgNum++;
 
diff --git a/src/coreclr/vm/i386/cgencpu.h b/src/coreclr/vm/i386/cgencpu.h
index b87527be323331..a6ec982c021e61 100644
--- a/src/coreclr/vm/i386/cgencpu.h
+++ b/src/coreclr/vm/i386/cgencpu.h
@@ -436,7 +436,11 @@ struct HijackArgs
     DWORD Esi;
     DWORD Ebx;
     DWORD Edx;
-    DWORD Ecx;
+    union
+    {
+        DWORD Ecx;
+        size_t AsyncRet;
+    };
     union
     {
         DWORD Eax;
diff --git a/src/coreclr/vm/jithelpers.cpp b/src/coreclr/vm/jithelpers.cpp
index b09e04d2795a61..30612106ef6e63 100644
--- a/src/coreclr/vm/jithelpers.cpp
+++ b/src/coreclr/vm/jithelpers.cpp
@@ -2644,7 +2644,7 @@ HCIMPL1(void, IL_ThrowExact, Object* obj)
     OBJECTREF oref = ObjectToOBJECTREF(obj);
 
 #if defined(_DEBUG) && defined(TARGET_X86)
-    __helperframe.InsureInit(false, NULL);
+    __helperframe.InsureInit(NULL);
     g_ExceptionEIP = (LPVOID)__helperframe.GetReturnAddress();
 #endif // defined(_DEBUG) && defined(TARGET_X86)
 
@@ -3928,6 +3928,15 @@ HCIMPL1(VOID, JIT_PartialCompilationPatchpoint, int ilOffset)
 }
 HCIMPLEND
 
+void JIT_ResumeOSR(unsigned ilOffset)
+{
+    // Stub version if OSR feature is disabled
+    //
+    // Should not be called.
+
+    UNREACHABLE();
+}
+
 #endif // FEATURE_ON_STACK_REPLACEMENT
 
 static unsigned HandleHistogramProfileRand()
diff --git a/src/coreclr/vm/jitinterface.cpp b/src/coreclr/vm/jitinterface.cpp
index d3b7cd5d437621..8bc995514988da 100644
--- a/src/coreclr/vm/jitinterface.cpp
+++ b/src/coreclr/vm/jitinterface.cpp
@@ -14514,12 +14514,16 @@ CORINFO_METHOD_HANDLE CEEJitInfo::getAsyncResumptionStub()
     NativeCodeVersion ncv = config->GetCodeVersion();
     if (ncv.GetOptimizationTier() == NativeCodeVersion::OptimizationTier1OSR)
     {
+#ifdef FEATURE_ON_STACK_REPLACEMENT
         // The OSR version needs to resume in the tier0 version. The tier0
         // version will handle setting up the frame that the OSR version
         // expects and then delegating back into the OSR version (knowing to do
         // so through information stored in the continuation).
         _ASSERTE(m_pPatchpointInfoFromRuntime != NULL);
         pCode->EmitLDC((DWORD_PTR)m_pPatchpointInfoFromRuntime->GetTier0EntryPoint());
+#else
+        _ASSERTE(!"Unexpected optimization tier with OSR disabled");
+#endif
     }
     else
     {
diff --git a/src/coreclr/vm/prestub.cpp b/src/coreclr/vm/prestub.cpp
index 459dc754db3768..ea4ab4f6d6efa8 100644
--- a/src/coreclr/vm/prestub.cpp
+++ b/src/coreclr/vm/prestub.cpp
@@ -2639,8 +2639,7 @@ void MethodDesc::CreateDerivedTargetSigWithExtraParams(MetaSig& msig, SigBuilder
     stubSigBuilder->AppendByte(callingConvention);
 
     unsigned numArgs = msig.NumFixedArgs();
-    bool hasInstParam = msig.HasGenericContextArg() != FALSE;
-    if (hasInstParam)
+    if (msig.HasGenericContextArg())
         numArgs++;
     if (msig.HasAsyncContinuation())
         numArgs++;
@@ -2652,7 +2651,7 @@ void MethodDesc::CreateDerivedTargetSigWithExtraParams(MetaSig& msig, SigBuilder
     pReturn.ConvertToInternalExactlyOne(msig.GetModule(), msig.GetSigTypeContext(), stubSigBuilder);
 
 #ifndef TARGET_X86
-    if (hasInstParam)
+    if (msig.HasGenericContextArg())
     {
         // The hidden context parameter
         stubSigBuilder->AppendElementType(ELEMENT_TYPE_I);
@@ -2673,13 +2672,13 @@ void MethodDesc::CreateDerivedTargetSigWithExtraParams(MetaSig& msig, SigBuilder
     }
 
 #ifdef TARGET_X86
-    if (hasInstParam)
+    if (msig.HasGenericContextArg())
     {
         // The hidden context parameter
         stubSigBuilder->AppendElementType(ELEMENT_TYPE_I);
     }
 
-    if (hasAsyncCont)
+    if (msig.HasAsyncContinuation())
     {
         stubSigBuilder->AppendElementType(ELEMENT_TYPE_OBJECT);
     }

From 0c6694130a7e228d85b67b473b8e7f0fed4fccaf Mon Sep 17 00:00:00 2001
From: Jakob Botsch Nielsen <Jakob.botsch.nielsen@gmail.com>
Date: Thu, 14 Nov 2024 11:12:59 +0100
Subject: [PATCH 123/203] [RuntimeAsync] Clean up and fix async2 tests (#2768)

- Remove the BuildOnly benchmark tests from the test wrapper
- Remove all `RequiresProcessIsolation`, since none of these tests
  should require process isolation (they can be built standalone if
  desired)
- Mark all the tests as optimized, since we get more coverage that way
  (tier0/tier1 testing will cover with and without JIT optimizations)
- Rename all tests to remove the async2 prefix and use a consistent
  style
- Move tests out of Loader subdirectory
---
 src/tests/Loader/async/async2implement.csproj |  8 -------
 src/tests/Loader/async/async2override.csproj  |  8 -------
 .../Loader/async/async2sharedgeneric.csproj   |  8 -------
 src/tests/Loader/async/async2void.csproj      |  8 -------
 src/tests/Loader/async/asynctests.csproj      |  9 --------
 src/tests/async/asynctests.csproj             | 21 +++++++++++++++++++
 .../async/cse-array-index-byref.cs            |  0
 .../async/cse-array-index-byref.csproj        |  1 -
 .../eh-microbench.cs}                         |  0
 .../eh-microbench.csproj}                     |  1 -
 .../{Loader => }/async/expected_failures.txt  |  0
 .../fibonacci-with-yields-toplevel.cs}        |  0
 .../fibonacci-with-yields-toplevel.csproj}    |  3 ++-
 .../fibonacci-with-yields.cs}                 |  0
 .../fibonacci-with-yields.csproj}             |  1 -
 .../fibonacci-without-yields.cs}              |  0
 .../fibonacci-without-yields.csproj}          |  1 -
 .../gc-roots-scan.cs}                         |  0
 .../gc-roots-scan.csproj}                     |  1 -
 .../async2implement.cs => async/implement.cs} |  0
 .../implement.csproj}                         |  2 +-
 .../mincallcost-microbench.cs}                |  0
 .../mincallcost-microbench.csproj}            |  1 -
 .../async/async2object.cs => async/object.cs} |  0
 .../object.csproj}                            |  0
 .../objects-captured.cs}                      |  0
 .../objects-captured.csproj}                  |  0
 .../async2override.cs => async/override.cs}   |  0
 .../override.csproj}                          |  2 +-
 .../async2-returns.cs => async/returns.cs}    |  0
 .../returns.csproj}                           |  2 +-
 .../shared-generic.cs}                        |  0
 .../shared-generic.csproj}                    |  2 +-
 src/tests/{Loader => }/async/simple-eh.cs     |  0
 src/tests/{Loader => }/async/simple-eh.csproj |  1 -
 .../async/syncfibonacci-with-yields.cs        |  0
 .../async/syncfibonacci-with-yields.csproj    |  1 -
 .../async/syncfibonacci-without-yields.cs     |  0
 .../async/syncfibonacci-without-yields.csproj |  1 -
 .../taskbased-asyncfibonacci-with-yields.cs   |  0
 ...askbased-asyncfibonacci-with-yields.csproj |  1 -
 ...taskbased-asyncfibonacci-without-yields.cs |  0
 ...based-asyncfibonacci-without-yields.csproj |  1 -
 .../async2valuetask.cs => async/valuetask.cs} |  0
 .../valuetask.csproj}                         |  2 +-
 ...luetaskbased-asyncfibonacci-with-yields.cs |  0
 ...askbased-asyncfibonacci-with-yields.csproj |  1 -
 ...taskbased-asyncfibonacci-without-yields.cs |  0
 ...based-asyncfibonacci-without-yields.csproj |  1 -
 .../varying-yields-task.csproj}               |  2 +-
 .../varying-yields-valuetask.csproj}          |  2 +-
 .../varying-yields.cs}                        |  0
 src/tests/async/varying-yields.csproj         |  8 +++++++
 .../async/async2void.cs => async/void.cs}     |  0
 src/tests/async/void.csproj                   |  8 +++++++
 .../with-yields.cs}                           |  0
 src/tests/async/with-yields.csproj            |  8 +++++++
 .../without-yields.cs}                        |  0
 src/tests/async/without-yields.csproj         |  8 +++++++
 59 files changed, 62 insertions(+), 62 deletions(-)
 delete mode 100644 src/tests/Loader/async/async2implement.csproj
 delete mode 100644 src/tests/Loader/async/async2override.csproj
 delete mode 100644 src/tests/Loader/async/async2sharedgeneric.csproj
 delete mode 100644 src/tests/Loader/async/async2void.csproj
 delete mode 100644 src/tests/Loader/async/asynctests.csproj
 create mode 100644 src/tests/async/asynctests.csproj
 rename src/tests/{Loader => }/async/cse-array-index-byref.cs (100%)
 rename src/tests/{Loader => }/async/cse-array-index-byref.csproj (85%)
 rename src/tests/{Loader/async/async2-eh-microbench.cs => async/eh-microbench.cs} (100%)
 rename src/tests/{Loader/async/async2-eh-microbench.csproj => async/eh-microbench.csproj} (81%)
 rename src/tests/{Loader => }/async/expected_failures.txt (100%)
 rename src/tests/{Loader/async/async2fibonacci-with-yields-topLevel.cs => async/fibonacci-with-yields-toplevel.cs} (100%)
 rename src/tests/{Loader/async/async2fibonacci-without-yields.csproj => async/fibonacci-with-yields-toplevel.csproj} (64%)
 rename src/tests/{Loader/async/async2fibonacci-with-yields.cs => async/fibonacci-with-yields.cs} (100%)
 rename src/tests/{Loader/async/async2fibonacci-with-yields-topLevel.csproj => async/fibonacci-with-yields.csproj} (75%)
 rename src/tests/{Loader/async/async2fibonacci-without-yields.cs => async/fibonacci-without-yields.cs} (100%)
 rename src/tests/{Loader/async/async2fibonacci-with-yields.csproj => async/fibonacci-without-yields.csproj} (75%)
 rename src/tests/{Loader/async/async2gcRootsScan.cs => async/gc-roots-scan.cs} (100%)
 rename src/tests/{Loader/async/async2gcRootsScan.csproj => async/gc-roots-scan.csproj} (81%)
 rename src/tests/{Loader/async/async2implement.cs => async/implement.cs} (100%)
 rename src/tests/{Loader/async/async2valuetask.csproj => async/implement.csproj} (72%)
 rename src/tests/{Loader/async/async2-mincallcost-microbench.cs => async/mincallcost-microbench.cs} (100%)
 rename src/tests/{Loader/async/async2-mincallcost-microbench.csproj => async/mincallcost-microbench.csproj} (81%)
 rename src/tests/{Loader/async/async2object.cs => async/object.cs} (100%)
 rename src/tests/{Loader/async/async2-returns.csproj => async/object.csproj} (100%)
 rename src/tests/{Loader/async/async2objectsCaptured.cs => async/objects-captured.cs} (100%)
 rename src/tests/{Loader/async/async2-varying-yields.csproj => async/objects-captured.csproj} (100%)
 rename src/tests/{Loader/async/async2override.cs => async/override.cs} (100%)
 rename src/tests/{Loader/async/async2object.csproj => async/override.csproj} (72%)
 rename src/tests/{Loader/async/async2-returns.cs => async/returns.cs} (100%)
 rename src/tests/{Loader/async/async2objectsCaptured.csproj => async/returns.csproj} (72%)
 rename src/tests/{Loader/async/async2sharedgeneric.cs => async/shared-generic.cs} (100%)
 rename src/tests/{Loader/async/async2-without-yields.csproj => async/shared-generic.csproj} (72%)
 rename src/tests/{Loader => }/async/simple-eh.cs (100%)
 rename src/tests/{Loader => }/async/simple-eh.csproj (85%)
 rename src/tests/{Loader => }/async/syncfibonacci-with-yields.cs (100%)
 rename src/tests/{Loader => }/async/syncfibonacci-with-yields.csproj (81%)
 rename src/tests/{Loader => }/async/syncfibonacci-without-yields.cs (100%)
 rename src/tests/{Loader => }/async/syncfibonacci-without-yields.csproj (81%)
 rename src/tests/{Loader => }/async/taskbased-asyncfibonacci-with-yields.cs (100%)
 rename src/tests/{Loader => }/async/taskbased-asyncfibonacci-with-yields.csproj (81%)
 rename src/tests/{Loader => }/async/taskbased-asyncfibonacci-without-yields.cs (100%)
 rename src/tests/{Loader => }/async/taskbased-asyncfibonacci-without-yields.csproj (81%)
 rename src/tests/{Loader/async/async2valuetask.cs => async/valuetask.cs} (100%)
 rename src/tests/{Loader/async/async2-with-yields.csproj => async/valuetask.csproj} (72%)
 rename src/tests/{Loader => }/async/valuetaskbased-asyncfibonacci-with-yields.cs (100%)
 rename src/tests/{Loader => }/async/valuetaskbased-asyncfibonacci-with-yields.csproj (81%)
 rename src/tests/{Loader => }/async/valuetaskbased-asyncfibonacci-without-yields.cs (100%)
 rename src/tests/{Loader => }/async/valuetaskbased-asyncfibonacci-without-yields.csproj (81%)
 rename src/tests/{Loader/async/async2-varying-yields-task.csproj => async/varying-yields-task.csproj} (80%)
 rename src/tests/{Loader/async/async2-varying-yields-valuetask.csproj => async/varying-yields-valuetask.csproj} (81%)
 rename src/tests/{Loader/async/async2-varying-yields.cs => async/varying-yields.cs} (100%)
 create mode 100644 src/tests/async/varying-yields.csproj
 rename src/tests/{Loader/async/async2void.cs => async/void.cs} (100%)
 create mode 100644 src/tests/async/void.csproj
 rename src/tests/{Loader/async/async2-with-yields.cs => async/with-yields.cs} (100%)
 create mode 100644 src/tests/async/with-yields.csproj
 rename src/tests/{Loader/async/async2-without-yields.cs => async/without-yields.cs} (100%)
 create mode 100644 src/tests/async/without-yields.csproj

diff --git a/src/tests/Loader/async/async2implement.csproj b/src/tests/Loader/async/async2implement.csproj
deleted file mode 100644
index 0f29ed860df43e..00000000000000
--- a/src/tests/Loader/async/async2implement.csproj
+++ /dev/null
@@ -1,8 +0,0 @@
-<Project Sdk="Microsoft.NET.Sdk.IL">
-  <PropertyGroup>
-    <RequiresProcessIsolation>true</RequiresProcessIsolation>
-  </PropertyGroup>
-  <ItemGroup>
-   <Compile Include="$(MSBuildProjectName).cs" />
-  </ItemGroup>
-</Project>
diff --git a/src/tests/Loader/async/async2override.csproj b/src/tests/Loader/async/async2override.csproj
deleted file mode 100644
index 0f29ed860df43e..00000000000000
--- a/src/tests/Loader/async/async2override.csproj
+++ /dev/null
@@ -1,8 +0,0 @@
-<Project Sdk="Microsoft.NET.Sdk.IL">
-  <PropertyGroup>
-    <RequiresProcessIsolation>true</RequiresProcessIsolation>
-  </PropertyGroup>
-  <ItemGroup>
-   <Compile Include="$(MSBuildProjectName).cs" />
-  </ItemGroup>
-</Project>
diff --git a/src/tests/Loader/async/async2sharedgeneric.csproj b/src/tests/Loader/async/async2sharedgeneric.csproj
deleted file mode 100644
index 0f29ed860df43e..00000000000000
--- a/src/tests/Loader/async/async2sharedgeneric.csproj
+++ /dev/null
@@ -1,8 +0,0 @@
-<Project Sdk="Microsoft.NET.Sdk.IL">
-  <PropertyGroup>
-    <RequiresProcessIsolation>true</RequiresProcessIsolation>
-  </PropertyGroup>
-  <ItemGroup>
-   <Compile Include="$(MSBuildProjectName).cs" />
-  </ItemGroup>
-</Project>
diff --git a/src/tests/Loader/async/async2void.csproj b/src/tests/Loader/async/async2void.csproj
deleted file mode 100644
index 6607f198fc54fb..00000000000000
--- a/src/tests/Loader/async/async2void.csproj
+++ /dev/null
@@ -1,8 +0,0 @@
-<Project Sdk="Microsoft.NET.Sdk.IL">
-  <PropertyGroup>
-    <RequiresProcessIsolation>true</RequiresProcessIsolation>
-  </PropertyGroup>
-  <ItemGroup>
-    <Compile Include="$(MSBuildProjectName).cs" />
-  </ItemGroup>
-</Project>
diff --git a/src/tests/Loader/async/asynctests.csproj b/src/tests/Loader/async/asynctests.csproj
deleted file mode 100644
index be189042ee44c3..00000000000000
--- a/src/tests/Loader/async/asynctests.csproj
+++ /dev/null
@@ -1,9 +0,0 @@
-<Project Sdk="Microsoft.NET.Sdk">
-  <ItemGroup>
-    <MergedWrapperProjectReference Include="*/**/*.??proj" />
-    <MergedWrapperProjectReference Include="*.??proj" />
-    <MergedWrapperProjectReference Remove="$(MSBuildProjectName).csproj" />
-  </ItemGroup>
-
-  <Import Project="$(TestSourceDir)MergedTestRunner.targets" />
-</Project>
diff --git a/src/tests/async/asynctests.csproj b/src/tests/async/asynctests.csproj
new file mode 100644
index 00000000000000..265fe338158a83
--- /dev/null
+++ b/src/tests/async/asynctests.csproj
@@ -0,0 +1,21 @@
+<Project Sdk="Microsoft.NET.Sdk">
+  <ItemGroup>
+    <MergedWrapperProjectReference Include="*/**/*.??proj" />
+    <MergedWrapperProjectReference Include="*.??proj" />
+    <MergedWrapperProjectReference Remove="$(MSBuildProjectName).csproj" />
+
+    <!-- Remove manual benchmarks from the test wrapper -->
+    <MergedWrapperProjectReference Remove="eh-microbench.csproj" />
+    <MergedWrapperProjectReference Remove="gc-roots-scan.csproj" />
+    <MergedWrapperProjectReference Remove="mincallcost-microbench.csproj" />
+
+    <MergedWrapperProjectReference Remove="syncfibonacci-with-yields.csproj" />
+    <MergedWrapperProjectReference Remove="syncfibonacci-without-yields.csproj" />
+    <MergedWrapperProjectReference Remove="taskbased-asyncfibonacci-with-yields.csproj" />
+    <MergedWrapperProjectReference Remove="taskbased-asyncfibonacci-without-yields.csproj" />
+    <MergedWrapperProjectReference Remove="valuetaskbased-asyncfibonacci-without-yields.csproj" />
+    <MergedWrapperProjectReference Remove="valuetaskbased-asynfibonacci-with-yields.csproj" />
+  </ItemGroup>
+
+  <Import Project="$(TestSourceDir)MergedTestRunner.targets" />
+</Project>
diff --git a/src/tests/Loader/async/cse-array-index-byref.cs b/src/tests/async/cse-array-index-byref.cs
similarity index 100%
rename from src/tests/Loader/async/cse-array-index-byref.cs
rename to src/tests/async/cse-array-index-byref.cs
diff --git a/src/tests/Loader/async/cse-array-index-byref.csproj b/src/tests/async/cse-array-index-byref.csproj
similarity index 85%
rename from src/tests/Loader/async/cse-array-index-byref.csproj
rename to src/tests/async/cse-array-index-byref.csproj
index 501217e4d86892..de6d5e08882e86 100644
--- a/src/tests/Loader/async/cse-array-index-byref.csproj
+++ b/src/tests/async/cse-array-index-byref.csproj
@@ -1,6 +1,5 @@
 <Project Sdk="Microsoft.NET.Sdk">
   <PropertyGroup>
-    <DebugType>None</DebugType>
     <Optimize>True</Optimize>
   </PropertyGroup>
   <ItemGroup>
diff --git a/src/tests/Loader/async/async2-eh-microbench.cs b/src/tests/async/eh-microbench.cs
similarity index 100%
rename from src/tests/Loader/async/async2-eh-microbench.cs
rename to src/tests/async/eh-microbench.cs
diff --git a/src/tests/Loader/async/async2-eh-microbench.csproj b/src/tests/async/eh-microbench.csproj
similarity index 81%
rename from src/tests/Loader/async/async2-eh-microbench.csproj
rename to src/tests/async/eh-microbench.csproj
index f7a2511415dff2..1f1906fd50a23e 100644
--- a/src/tests/Loader/async/async2-eh-microbench.csproj
+++ b/src/tests/async/eh-microbench.csproj
@@ -1,7 +1,6 @@
 <Project Sdk="Microsoft.NET.Sdk.IL">
   <PropertyGroup>
     <Optimize>True</Optimize>
-    <RequiresProcessIsolation>true</RequiresProcessIsolation>
     <!-- This is a manual test/benchmark -->
     <CLRTestKind>BuildOnly</CLRTestKind>
   </PropertyGroup>
diff --git a/src/tests/Loader/async/expected_failures.txt b/src/tests/async/expected_failures.txt
similarity index 100%
rename from src/tests/Loader/async/expected_failures.txt
rename to src/tests/async/expected_failures.txt
diff --git a/src/tests/Loader/async/async2fibonacci-with-yields-topLevel.cs b/src/tests/async/fibonacci-with-yields-toplevel.cs
similarity index 100%
rename from src/tests/Loader/async/async2fibonacci-with-yields-topLevel.cs
rename to src/tests/async/fibonacci-with-yields-toplevel.cs
diff --git a/src/tests/Loader/async/async2fibonacci-without-yields.csproj b/src/tests/async/fibonacci-with-yields-toplevel.csproj
similarity index 64%
rename from src/tests/Loader/async/async2fibonacci-without-yields.csproj
rename to src/tests/async/fibonacci-with-yields-toplevel.csproj
index 4a1973da13c789..14c1fed92f82b6 100644
--- a/src/tests/Loader/async/async2fibonacci-without-yields.csproj
+++ b/src/tests/async/fibonacci-with-yields-toplevel.csproj
@@ -1,6 +1,7 @@
 <Project Sdk="Microsoft.NET.Sdk">
   <PropertyGroup>
-    <RequiresProcessIsolation>true</RequiresProcessIsolation>
+    <!-- Needed for top level statements -->
+    <RequiresProcessIsolation>True</RequiresProcessIsolation>
     <Optimize>True</Optimize>
   </PropertyGroup>
   <ItemGroup>
diff --git a/src/tests/Loader/async/async2fibonacci-with-yields.cs b/src/tests/async/fibonacci-with-yields.cs
similarity index 100%
rename from src/tests/Loader/async/async2fibonacci-with-yields.cs
rename to src/tests/async/fibonacci-with-yields.cs
diff --git a/src/tests/Loader/async/async2fibonacci-with-yields-topLevel.csproj b/src/tests/async/fibonacci-with-yields.csproj
similarity index 75%
rename from src/tests/Loader/async/async2fibonacci-with-yields-topLevel.csproj
rename to src/tests/async/fibonacci-with-yields.csproj
index 4a1973da13c789..de6d5e08882e86 100644
--- a/src/tests/Loader/async/async2fibonacci-with-yields-topLevel.csproj
+++ b/src/tests/async/fibonacci-with-yields.csproj
@@ -1,6 +1,5 @@
 <Project Sdk="Microsoft.NET.Sdk">
   <PropertyGroup>
-    <RequiresProcessIsolation>true</RequiresProcessIsolation>
     <Optimize>True</Optimize>
   </PropertyGroup>
   <ItemGroup>
diff --git a/src/tests/Loader/async/async2fibonacci-without-yields.cs b/src/tests/async/fibonacci-without-yields.cs
similarity index 100%
rename from src/tests/Loader/async/async2fibonacci-without-yields.cs
rename to src/tests/async/fibonacci-without-yields.cs
diff --git a/src/tests/Loader/async/async2fibonacci-with-yields.csproj b/src/tests/async/fibonacci-without-yields.csproj
similarity index 75%
rename from src/tests/Loader/async/async2fibonacci-with-yields.csproj
rename to src/tests/async/fibonacci-without-yields.csproj
index 4a1973da13c789..de6d5e08882e86 100644
--- a/src/tests/Loader/async/async2fibonacci-with-yields.csproj
+++ b/src/tests/async/fibonacci-without-yields.csproj
@@ -1,6 +1,5 @@
 <Project Sdk="Microsoft.NET.Sdk">
   <PropertyGroup>
-    <RequiresProcessIsolation>true</RequiresProcessIsolation>
     <Optimize>True</Optimize>
   </PropertyGroup>
   <ItemGroup>
diff --git a/src/tests/Loader/async/async2gcRootsScan.cs b/src/tests/async/gc-roots-scan.cs
similarity index 100%
rename from src/tests/Loader/async/async2gcRootsScan.cs
rename to src/tests/async/gc-roots-scan.cs
diff --git a/src/tests/Loader/async/async2gcRootsScan.csproj b/src/tests/async/gc-roots-scan.csproj
similarity index 81%
rename from src/tests/Loader/async/async2gcRootsScan.csproj
rename to src/tests/async/gc-roots-scan.csproj
index b6e7e75bd6ebc6..f7972beb1c4a45 100644
--- a/src/tests/Loader/async/async2gcRootsScan.csproj
+++ b/src/tests/async/gc-roots-scan.csproj
@@ -1,7 +1,6 @@
 <Project Sdk="Microsoft.NET.Sdk.IL">
   <PropertyGroup>
     <Optimize>True</Optimize>
-    <RequiresProcessIsolation>true</RequiresProcessIsolation>
     <!-- This is a manual test/benchmark -->
     <CLRTestKind>BuildOnly</CLRTestKind>
   </PropertyGroup>
diff --git a/src/tests/Loader/async/async2implement.cs b/src/tests/async/implement.cs
similarity index 100%
rename from src/tests/Loader/async/async2implement.cs
rename to src/tests/async/implement.cs
diff --git a/src/tests/Loader/async/async2valuetask.csproj b/src/tests/async/implement.csproj
similarity index 72%
rename from src/tests/Loader/async/async2valuetask.csproj
rename to src/tests/async/implement.csproj
index 0f29ed860df43e..1ae294349c376f 100644
--- a/src/tests/Loader/async/async2valuetask.csproj
+++ b/src/tests/async/implement.csproj
@@ -1,6 +1,6 @@
 <Project Sdk="Microsoft.NET.Sdk.IL">
   <PropertyGroup>
-    <RequiresProcessIsolation>true</RequiresProcessIsolation>
+    <Optimize>True</Optimize>
   </PropertyGroup>
   <ItemGroup>
    <Compile Include="$(MSBuildProjectName).cs" />
diff --git a/src/tests/Loader/async/async2-mincallcost-microbench.cs b/src/tests/async/mincallcost-microbench.cs
similarity index 100%
rename from src/tests/Loader/async/async2-mincallcost-microbench.cs
rename to src/tests/async/mincallcost-microbench.cs
diff --git a/src/tests/Loader/async/async2-mincallcost-microbench.csproj b/src/tests/async/mincallcost-microbench.csproj
similarity index 81%
rename from src/tests/Loader/async/async2-mincallcost-microbench.csproj
rename to src/tests/async/mincallcost-microbench.csproj
index f7a2511415dff2..1f1906fd50a23e 100644
--- a/src/tests/Loader/async/async2-mincallcost-microbench.csproj
+++ b/src/tests/async/mincallcost-microbench.csproj
@@ -1,7 +1,6 @@
 <Project Sdk="Microsoft.NET.Sdk.IL">
   <PropertyGroup>
     <Optimize>True</Optimize>
-    <RequiresProcessIsolation>true</RequiresProcessIsolation>
     <!-- This is a manual test/benchmark -->
     <CLRTestKind>BuildOnly</CLRTestKind>
   </PropertyGroup>
diff --git a/src/tests/Loader/async/async2object.cs b/src/tests/async/object.cs
similarity index 100%
rename from src/tests/Loader/async/async2object.cs
rename to src/tests/async/object.cs
diff --git a/src/tests/Loader/async/async2-returns.csproj b/src/tests/async/object.csproj
similarity index 100%
rename from src/tests/Loader/async/async2-returns.csproj
rename to src/tests/async/object.csproj
diff --git a/src/tests/Loader/async/async2objectsCaptured.cs b/src/tests/async/objects-captured.cs
similarity index 100%
rename from src/tests/Loader/async/async2objectsCaptured.cs
rename to src/tests/async/objects-captured.cs
diff --git a/src/tests/Loader/async/async2-varying-yields.csproj b/src/tests/async/objects-captured.csproj
similarity index 100%
rename from src/tests/Loader/async/async2-varying-yields.csproj
rename to src/tests/async/objects-captured.csproj
diff --git a/src/tests/Loader/async/async2override.cs b/src/tests/async/override.cs
similarity index 100%
rename from src/tests/Loader/async/async2override.cs
rename to src/tests/async/override.cs
diff --git a/src/tests/Loader/async/async2object.csproj b/src/tests/async/override.csproj
similarity index 72%
rename from src/tests/Loader/async/async2object.csproj
rename to src/tests/async/override.csproj
index 6607f198fc54fb..9367a79b2edbb1 100644
--- a/src/tests/Loader/async/async2object.csproj
+++ b/src/tests/async/override.csproj
@@ -1,6 +1,6 @@
 <Project Sdk="Microsoft.NET.Sdk.IL">
   <PropertyGroup>
-    <RequiresProcessIsolation>true</RequiresProcessIsolation>
+    <Optimize>True</Optimize>
   </PropertyGroup>
   <ItemGroup>
     <Compile Include="$(MSBuildProjectName).cs" />
diff --git a/src/tests/Loader/async/async2-returns.cs b/src/tests/async/returns.cs
similarity index 100%
rename from src/tests/Loader/async/async2-returns.cs
rename to src/tests/async/returns.cs
diff --git a/src/tests/Loader/async/async2objectsCaptured.csproj b/src/tests/async/returns.csproj
similarity index 72%
rename from src/tests/Loader/async/async2objectsCaptured.csproj
rename to src/tests/async/returns.csproj
index 6607f198fc54fb..9367a79b2edbb1 100644
--- a/src/tests/Loader/async/async2objectsCaptured.csproj
+++ b/src/tests/async/returns.csproj
@@ -1,6 +1,6 @@
 <Project Sdk="Microsoft.NET.Sdk.IL">
   <PropertyGroup>
-    <RequiresProcessIsolation>true</RequiresProcessIsolation>
+    <Optimize>True</Optimize>
   </PropertyGroup>
   <ItemGroup>
     <Compile Include="$(MSBuildProjectName).cs" />
diff --git a/src/tests/Loader/async/async2sharedgeneric.cs b/src/tests/async/shared-generic.cs
similarity index 100%
rename from src/tests/Loader/async/async2sharedgeneric.cs
rename to src/tests/async/shared-generic.cs
diff --git a/src/tests/Loader/async/async2-without-yields.csproj b/src/tests/async/shared-generic.csproj
similarity index 72%
rename from src/tests/Loader/async/async2-without-yields.csproj
rename to src/tests/async/shared-generic.csproj
index 6607f198fc54fb..9367a79b2edbb1 100644
--- a/src/tests/Loader/async/async2-without-yields.csproj
+++ b/src/tests/async/shared-generic.csproj
@@ -1,6 +1,6 @@
 <Project Sdk="Microsoft.NET.Sdk.IL">
   <PropertyGroup>
-    <RequiresProcessIsolation>true</RequiresProcessIsolation>
+    <Optimize>True</Optimize>
   </PropertyGroup>
   <ItemGroup>
     <Compile Include="$(MSBuildProjectName).cs" />
diff --git a/src/tests/Loader/async/simple-eh.cs b/src/tests/async/simple-eh.cs
similarity index 100%
rename from src/tests/Loader/async/simple-eh.cs
rename to src/tests/async/simple-eh.cs
diff --git a/src/tests/Loader/async/simple-eh.csproj b/src/tests/async/simple-eh.csproj
similarity index 85%
rename from src/tests/Loader/async/simple-eh.csproj
rename to src/tests/async/simple-eh.csproj
index 501217e4d86892..de6d5e08882e86 100644
--- a/src/tests/Loader/async/simple-eh.csproj
+++ b/src/tests/async/simple-eh.csproj
@@ -1,6 +1,5 @@
 <Project Sdk="Microsoft.NET.Sdk">
   <PropertyGroup>
-    <DebugType>None</DebugType>
     <Optimize>True</Optimize>
   </PropertyGroup>
   <ItemGroup>
diff --git a/src/tests/Loader/async/syncfibonacci-with-yields.cs b/src/tests/async/syncfibonacci-with-yields.cs
similarity index 100%
rename from src/tests/Loader/async/syncfibonacci-with-yields.cs
rename to src/tests/async/syncfibonacci-with-yields.cs
diff --git a/src/tests/Loader/async/syncfibonacci-with-yields.csproj b/src/tests/async/syncfibonacci-with-yields.csproj
similarity index 81%
rename from src/tests/Loader/async/syncfibonacci-with-yields.csproj
rename to src/tests/async/syncfibonacci-with-yields.csproj
index c089ac9ebc8bab..a7c0f53acc0cb4 100644
--- a/src/tests/Loader/async/syncfibonacci-with-yields.csproj
+++ b/src/tests/async/syncfibonacci-with-yields.csproj
@@ -1,7 +1,6 @@
 <Project Sdk="Microsoft.NET.Sdk">
   <PropertyGroup>
     <Optimize>True</Optimize>
-    <RequiresProcessIsolation>true</RequiresProcessIsolation>
     <!-- This is a manual test/benchmark -->
     <CLRTestKind>BuildOnly</CLRTestKind>
   </PropertyGroup>
diff --git a/src/tests/Loader/async/syncfibonacci-without-yields.cs b/src/tests/async/syncfibonacci-without-yields.cs
similarity index 100%
rename from src/tests/Loader/async/syncfibonacci-without-yields.cs
rename to src/tests/async/syncfibonacci-without-yields.cs
diff --git a/src/tests/Loader/async/syncfibonacci-without-yields.csproj b/src/tests/async/syncfibonacci-without-yields.csproj
similarity index 81%
rename from src/tests/Loader/async/syncfibonacci-without-yields.csproj
rename to src/tests/async/syncfibonacci-without-yields.csproj
index c089ac9ebc8bab..a7c0f53acc0cb4 100644
--- a/src/tests/Loader/async/syncfibonacci-without-yields.csproj
+++ b/src/tests/async/syncfibonacci-without-yields.csproj
@@ -1,7 +1,6 @@
 <Project Sdk="Microsoft.NET.Sdk">
   <PropertyGroup>
     <Optimize>True</Optimize>
-    <RequiresProcessIsolation>true</RequiresProcessIsolation>
     <!-- This is a manual test/benchmark -->
     <CLRTestKind>BuildOnly</CLRTestKind>
   </PropertyGroup>
diff --git a/src/tests/Loader/async/taskbased-asyncfibonacci-with-yields.cs b/src/tests/async/taskbased-asyncfibonacci-with-yields.cs
similarity index 100%
rename from src/tests/Loader/async/taskbased-asyncfibonacci-with-yields.cs
rename to src/tests/async/taskbased-asyncfibonacci-with-yields.cs
diff --git a/src/tests/Loader/async/taskbased-asyncfibonacci-with-yields.csproj b/src/tests/async/taskbased-asyncfibonacci-with-yields.csproj
similarity index 81%
rename from src/tests/Loader/async/taskbased-asyncfibonacci-with-yields.csproj
rename to src/tests/async/taskbased-asyncfibonacci-with-yields.csproj
index c089ac9ebc8bab..a7c0f53acc0cb4 100644
--- a/src/tests/Loader/async/taskbased-asyncfibonacci-with-yields.csproj
+++ b/src/tests/async/taskbased-asyncfibonacci-with-yields.csproj
@@ -1,7 +1,6 @@
 <Project Sdk="Microsoft.NET.Sdk">
   <PropertyGroup>
     <Optimize>True</Optimize>
-    <RequiresProcessIsolation>true</RequiresProcessIsolation>
     <!-- This is a manual test/benchmark -->
     <CLRTestKind>BuildOnly</CLRTestKind>
   </PropertyGroup>
diff --git a/src/tests/Loader/async/taskbased-asyncfibonacci-without-yields.cs b/src/tests/async/taskbased-asyncfibonacci-without-yields.cs
similarity index 100%
rename from src/tests/Loader/async/taskbased-asyncfibonacci-without-yields.cs
rename to src/tests/async/taskbased-asyncfibonacci-without-yields.cs
diff --git a/src/tests/Loader/async/taskbased-asyncfibonacci-without-yields.csproj b/src/tests/async/taskbased-asyncfibonacci-without-yields.csproj
similarity index 81%
rename from src/tests/Loader/async/taskbased-asyncfibonacci-without-yields.csproj
rename to src/tests/async/taskbased-asyncfibonacci-without-yields.csproj
index c089ac9ebc8bab..a7c0f53acc0cb4 100644
--- a/src/tests/Loader/async/taskbased-asyncfibonacci-without-yields.csproj
+++ b/src/tests/async/taskbased-asyncfibonacci-without-yields.csproj
@@ -1,7 +1,6 @@
 <Project Sdk="Microsoft.NET.Sdk">
   <PropertyGroup>
     <Optimize>True</Optimize>
-    <RequiresProcessIsolation>true</RequiresProcessIsolation>
     <!-- This is a manual test/benchmark -->
     <CLRTestKind>BuildOnly</CLRTestKind>
   </PropertyGroup>
diff --git a/src/tests/Loader/async/async2valuetask.cs b/src/tests/async/valuetask.cs
similarity index 100%
rename from src/tests/Loader/async/async2valuetask.cs
rename to src/tests/async/valuetask.cs
diff --git a/src/tests/Loader/async/async2-with-yields.csproj b/src/tests/async/valuetask.csproj
similarity index 72%
rename from src/tests/Loader/async/async2-with-yields.csproj
rename to src/tests/async/valuetask.csproj
index 6607f198fc54fb..9367a79b2edbb1 100644
--- a/src/tests/Loader/async/async2-with-yields.csproj
+++ b/src/tests/async/valuetask.csproj
@@ -1,6 +1,6 @@
 <Project Sdk="Microsoft.NET.Sdk.IL">
   <PropertyGroup>
-    <RequiresProcessIsolation>true</RequiresProcessIsolation>
+    <Optimize>True</Optimize>
   </PropertyGroup>
   <ItemGroup>
     <Compile Include="$(MSBuildProjectName).cs" />
diff --git a/src/tests/Loader/async/valuetaskbased-asyncfibonacci-with-yields.cs b/src/tests/async/valuetaskbased-asyncfibonacci-with-yields.cs
similarity index 100%
rename from src/tests/Loader/async/valuetaskbased-asyncfibonacci-with-yields.cs
rename to src/tests/async/valuetaskbased-asyncfibonacci-with-yields.cs
diff --git a/src/tests/Loader/async/valuetaskbased-asyncfibonacci-with-yields.csproj b/src/tests/async/valuetaskbased-asyncfibonacci-with-yields.csproj
similarity index 81%
rename from src/tests/Loader/async/valuetaskbased-asyncfibonacci-with-yields.csproj
rename to src/tests/async/valuetaskbased-asyncfibonacci-with-yields.csproj
index c089ac9ebc8bab..a7c0f53acc0cb4 100644
--- a/src/tests/Loader/async/valuetaskbased-asyncfibonacci-with-yields.csproj
+++ b/src/tests/async/valuetaskbased-asyncfibonacci-with-yields.csproj
@@ -1,7 +1,6 @@
 <Project Sdk="Microsoft.NET.Sdk">
   <PropertyGroup>
     <Optimize>True</Optimize>
-    <RequiresProcessIsolation>true</RequiresProcessIsolation>
     <!-- This is a manual test/benchmark -->
     <CLRTestKind>BuildOnly</CLRTestKind>
   </PropertyGroup>
diff --git a/src/tests/Loader/async/valuetaskbased-asyncfibonacci-without-yields.cs b/src/tests/async/valuetaskbased-asyncfibonacci-without-yields.cs
similarity index 100%
rename from src/tests/Loader/async/valuetaskbased-asyncfibonacci-without-yields.cs
rename to src/tests/async/valuetaskbased-asyncfibonacci-without-yields.cs
diff --git a/src/tests/Loader/async/valuetaskbased-asyncfibonacci-without-yields.csproj b/src/tests/async/valuetaskbased-asyncfibonacci-without-yields.csproj
similarity index 81%
rename from src/tests/Loader/async/valuetaskbased-asyncfibonacci-without-yields.csproj
rename to src/tests/async/valuetaskbased-asyncfibonacci-without-yields.csproj
index c089ac9ebc8bab..a7c0f53acc0cb4 100644
--- a/src/tests/Loader/async/valuetaskbased-asyncfibonacci-without-yields.csproj
+++ b/src/tests/async/valuetaskbased-asyncfibonacci-without-yields.csproj
@@ -1,7 +1,6 @@
 <Project Sdk="Microsoft.NET.Sdk">
   <PropertyGroup>
     <Optimize>True</Optimize>
-    <RequiresProcessIsolation>true</RequiresProcessIsolation>
     <!-- This is a manual test/benchmark -->
     <CLRTestKind>BuildOnly</CLRTestKind>
   </PropertyGroup>
diff --git a/src/tests/Loader/async/async2-varying-yields-task.csproj b/src/tests/async/varying-yields-task.csproj
similarity index 80%
rename from src/tests/Loader/async/async2-varying-yields-task.csproj
rename to src/tests/async/varying-yields-task.csproj
index 6eb7985558de71..f4098eb1998351 100644
--- a/src/tests/Loader/async/async2-varying-yields-task.csproj
+++ b/src/tests/async/varying-yields-task.csproj
@@ -4,6 +4,6 @@
     <DefineConstants>ASYNC1_TASK;$(DefineConstants)</DefineConstants>
   </PropertyGroup>
   <ItemGroup>
-    <Compile Include="async2-varying-yields.cs" />
+    <Compile Include="varying-yields.cs" />
   </ItemGroup>
 </Project>
diff --git a/src/tests/Loader/async/async2-varying-yields-valuetask.csproj b/src/tests/async/varying-yields-valuetask.csproj
similarity index 81%
rename from src/tests/Loader/async/async2-varying-yields-valuetask.csproj
rename to src/tests/async/varying-yields-valuetask.csproj
index d10972a7e74300..841fdd1bcf8110 100644
--- a/src/tests/Loader/async/async2-varying-yields-valuetask.csproj
+++ b/src/tests/async/varying-yields-valuetask.csproj
@@ -4,6 +4,6 @@
     <DefineConstants>ASYNC1_VALUETASK;$(DefineConstants)</DefineConstants>
   </PropertyGroup>
   <ItemGroup>
-    <Compile Include="async2-varying-yields.cs" />
+    <Compile Include="varying-yields.cs" />
   </ItemGroup>
 </Project>
diff --git a/src/tests/Loader/async/async2-varying-yields.cs b/src/tests/async/varying-yields.cs
similarity index 100%
rename from src/tests/Loader/async/async2-varying-yields.cs
rename to src/tests/async/varying-yields.cs
diff --git a/src/tests/async/varying-yields.csproj b/src/tests/async/varying-yields.csproj
new file mode 100644
index 00000000000000..9367a79b2edbb1
--- /dev/null
+++ b/src/tests/async/varying-yields.csproj
@@ -0,0 +1,8 @@
+<Project Sdk="Microsoft.NET.Sdk.IL">
+  <PropertyGroup>
+    <Optimize>True</Optimize>
+  </PropertyGroup>
+  <ItemGroup>
+    <Compile Include="$(MSBuildProjectName).cs" />
+  </ItemGroup>
+</Project>
diff --git a/src/tests/Loader/async/async2void.cs b/src/tests/async/void.cs
similarity index 100%
rename from src/tests/Loader/async/async2void.cs
rename to src/tests/async/void.cs
diff --git a/src/tests/async/void.csproj b/src/tests/async/void.csproj
new file mode 100644
index 00000000000000..9367a79b2edbb1
--- /dev/null
+++ b/src/tests/async/void.csproj
@@ -0,0 +1,8 @@
+<Project Sdk="Microsoft.NET.Sdk.IL">
+  <PropertyGroup>
+    <Optimize>True</Optimize>
+  </PropertyGroup>
+  <ItemGroup>
+    <Compile Include="$(MSBuildProjectName).cs" />
+  </ItemGroup>
+</Project>
diff --git a/src/tests/Loader/async/async2-with-yields.cs b/src/tests/async/with-yields.cs
similarity index 100%
rename from src/tests/Loader/async/async2-with-yields.cs
rename to src/tests/async/with-yields.cs
diff --git a/src/tests/async/with-yields.csproj b/src/tests/async/with-yields.csproj
new file mode 100644
index 00000000000000..9367a79b2edbb1
--- /dev/null
+++ b/src/tests/async/with-yields.csproj
@@ -0,0 +1,8 @@
+<Project Sdk="Microsoft.NET.Sdk.IL">
+  <PropertyGroup>
+    <Optimize>True</Optimize>
+  </PropertyGroup>
+  <ItemGroup>
+    <Compile Include="$(MSBuildProjectName).cs" />
+  </ItemGroup>
+</Project>
diff --git a/src/tests/Loader/async/async2-without-yields.cs b/src/tests/async/without-yields.cs
similarity index 100%
rename from src/tests/Loader/async/async2-without-yields.cs
rename to src/tests/async/without-yields.cs
diff --git a/src/tests/async/without-yields.csproj b/src/tests/async/without-yields.csproj
new file mode 100644
index 00000000000000..9367a79b2edbb1
--- /dev/null
+++ b/src/tests/async/without-yields.csproj
@@ -0,0 +1,8 @@
+<Project Sdk="Microsoft.NET.Sdk.IL">
+  <PropertyGroup>
+    <Optimize>True</Optimize>
+  </PropertyGroup>
+  <ItemGroup>
+    <Compile Include="$(MSBuildProjectName).cs" />
+  </ItemGroup>
+</Project>

From efb2892e2023aa8434fd6fc6ced4d4c6e71b6da6 Mon Sep 17 00:00:00 2001
From: Jakob Botsch Nielsen <Jakob.botsch.nielsen@gmail.com>
Date: Thu, 14 Nov 2024 11:14:29 +0100
Subject: [PATCH 124/203] [RuntimeAsync] Add support for arm64 (#2769)

Mainly just fixing some missing pieces on the JIT side, and adding x2 as the
register used to return the async continuation.
---
 src/coreclr/jit/codegenarmarch.cpp  |  8 ++++++++
 src/coreclr/jit/codegencommon.cpp   | 14 ++++++++++++++
 src/coreclr/jit/codegenxarch.cpp    | 13 -------------
 src/coreclr/jit/lsraarm.cpp         | 11 +++++++++++
 src/coreclr/jit/lsraarm64.cpp       | 11 +++++++++++
 src/coreclr/jit/lsraloongarch64.cpp | 16 ++++++++++++++++
 src/coreclr/jit/lsrariscv64.cpp     | 16 ++++++++++++++++
 src/coreclr/jit/targetarm64.h       |  4 ++--
 src/coreclr/vm/arm64/asmhelpers.S   | 18 ++++++++++++------
 src/coreclr/vm/arm64/asmhelpers.asm | 18 ++++++++++++------
 src/coreclr/vm/arm64/cgencpu.h      |  6 ++++++
 src/coreclr/vm/arm64/stubs.cpp      |  2 ++
 src/coreclr/vm/jitinterface.cpp     |  2 +-
 13 files changed, 111 insertions(+), 28 deletions(-)

diff --git a/src/coreclr/jit/codegenarmarch.cpp b/src/coreclr/jit/codegenarmarch.cpp
index 6edd0599f3020b..a3013804950cc9 100644
--- a/src/coreclr/jit/codegenarmarch.cpp
+++ b/src/coreclr/jit/codegenarmarch.cpp
@@ -289,6 +289,10 @@ void CodeGen::genCodeForTreeNode(GenTree* treeNode)
             break;
 #endif // SWIFT_SUPPORT
 
+        case GT_RETURN_SUSPEND:
+            genReturnSuspend(treeNode->AsUnOp());
+            break;
+
         case GT_LEA:
             // If we are here, it is the case where there is an LEA that cannot be folded into a parent instruction.
             genLeaInstruction(treeNode->AsAddrMode());
@@ -504,6 +508,10 @@ void CodeGen::genCodeForTreeNode(GenTree* treeNode)
             genConsumeReg(treeNode);
             break;
 
+        case GT_ASYNC_CONTINUATION:
+            genCodeForAsyncContinuation(treeNode);
+            break;
+
         case GT_PINVOKE_PROLOG:
             noway_assert(((gcInfo.gcRegGCrefSetCur | gcInfo.gcRegByrefSetCur) &
                           ~fullIntArgRegMask(compiler->info.compCallConv)) == 0);
diff --git a/src/coreclr/jit/codegencommon.cpp b/src/coreclr/jit/codegencommon.cpp
index 0fa1d3a3c36088..7a494a625b0115 100644
--- a/src/coreclr/jit/codegencommon.cpp
+++ b/src/coreclr/jit/codegencommon.cpp
@@ -7333,6 +7333,20 @@ void CodeGen::genReturnSuspend(GenTreeUnOp* treeNode)
         }
     }
 }
+
+void CodeGen::genCodeForAsyncContinuation(GenTree* tree)
+{
+    assert(tree->OperIs(GT_ASYNC_CONTINUATION));
+
+    var_types targetType = tree->TypeGet();
+    regNumber targetReg  = tree->GetRegNum();
+
+    inst_Mov(targetType, targetReg, REG_ASYNC_CONTINUATION_RET, /* canSkip */ true);
+    genTransferRegGCState(targetReg, REG_ASYNC_CONTINUATION_RET);
+
+    genProduceReg(tree);
+}
+
 //------------------------------------------------------------------------
 // isStructReturn: Returns whether the 'treeNode' is returning a struct.
 //
diff --git a/src/coreclr/jit/codegenxarch.cpp b/src/coreclr/jit/codegenxarch.cpp
index 0a561d86aeb14b..814bab53a9db06 100644
--- a/src/coreclr/jit/codegenxarch.cpp
+++ b/src/coreclr/jit/codegenxarch.cpp
@@ -4707,19 +4707,6 @@ void CodeGen::genCodeForPhysReg(GenTreePhysReg* tree)
     genProduceReg(tree);
 }
 
-void CodeGen::genCodeForAsyncContinuation(GenTree* tree)
-{
-    assert(tree->OperIs(GT_ASYNC_CONTINUATION));
-
-    var_types targetType = tree->TypeGet();
-    regNumber targetReg  = tree->GetRegNum();
-
-    inst_Mov(targetType, targetReg, REG_ASYNC_CONTINUATION_RET, /* canSkip */ true);
-    genTransferRegGCState(targetReg, REG_ASYNC_CONTINUATION_RET);
-
-    genProduceReg(tree);
-}
-
 //---------------------------------------------------------------------
 // genCodeForNullCheck - generate code for a GT_NULLCHECK node
 //
diff --git a/src/coreclr/jit/lsraarm.cpp b/src/coreclr/jit/lsraarm.cpp
index 815f0149aede11..ff5aa8c8f96978 100644
--- a/src/coreclr/jit/lsraarm.cpp
+++ b/src/coreclr/jit/lsraarm.cpp
@@ -631,6 +631,17 @@ int LinearScan::BuildNode(GenTree* tree)
             BuildDef(tree, RBM_EXCEPTION_OBJECT.GetIntRegSet());
             break;
 
+        case GT_ASYNC_CONTINUATION:
+            srcCount = 0;
+            assert(dstCount == 1);
+            // We kill the continuation arg here to communicate to the
+            // selection phase that the argument is no longer busy. This is a
+            // hack to make sure we do not overwrite the continuation between
+            // the call and this node.
+            addKillForRegs(RBM_ASYNC_CONTINUATION_RET, currentLoc);
+            BuildDef(tree, RBM_ASYNC_CONTINUATION_RET.GetIntRegSet());
+            break;
+
         case GT_COPY:
             srcCount = 1;
 #ifdef TARGET_ARM
diff --git a/src/coreclr/jit/lsraarm64.cpp b/src/coreclr/jit/lsraarm64.cpp
index ab86d18d706def..4af401da71b98c 100644
--- a/src/coreclr/jit/lsraarm64.cpp
+++ b/src/coreclr/jit/lsraarm64.cpp
@@ -1314,6 +1314,17 @@ int LinearScan::BuildNode(GenTree* tree)
             BuildDef(tree, RBM_EXCEPTION_OBJECT.GetIntRegSet());
             break;
 
+        case GT_ASYNC_CONTINUATION:
+            srcCount = 0;
+            assert(dstCount == 1);
+            // We kill the continuation arg here to communicate to the
+            // selection phase that the argument is no longer busy. This is a
+            // hack to make sure we do not overwrite the continuation between
+            // the call and this node.
+            addKillForRegs(RBM_ASYNC_CONTINUATION_RET, currentLoc);
+            BuildDef(tree, RBM_ASYNC_CONTINUATION_RET.GetIntRegSet());
+            break;
+
         case GT_INDEX_ADDR:
             assert(dstCount == 1);
             srcCount = BuildBinaryUses(tree->AsOp());
diff --git a/src/coreclr/jit/lsraloongarch64.cpp b/src/coreclr/jit/lsraloongarch64.cpp
index 529e6d8127b670..18d978e6db2ac7 100644
--- a/src/coreclr/jit/lsraloongarch64.cpp
+++ b/src/coreclr/jit/lsraloongarch64.cpp
@@ -563,6 +563,17 @@ int LinearScan::BuildNode(GenTree* tree)
             BuildDef(tree, RBM_EXCEPTION_OBJECT.GetIntRegSet());
             break;
 
+        case GT_ASYNC_CONTINUATION:
+            srcCount = 0;
+            assert(dstCount == 1);
+            // We kill the continuation arg here to communicate to the
+            // selection phase that the argument is no longer busy. This is a
+            // hack to make sure we do not overwrite the continuation between
+            // the call and this node.
+            addKillForRegs(RBM_ASYNC_CONTINUATION_RET, currentLoc);
+            BuildDef(tree, RBM_ASYNC_CONTINUATION_RET.GetIntRegSet());
+            break;
+
         case GT_INDEX_ADDR:
             assert(dstCount == 1);
             srcCount = BuildBinaryUses(tree->AsOp());
@@ -803,6 +814,11 @@ int LinearScan::BuildCall(GenTreeCall* call)
         BuildKills(call, killMask);
     }
 
+    if (call->IsAsync2() && compiler->compIsAsync2())
+    {
+        MarkAsyncContinuationBusyForCall(call);
+    }
+
     // No args are placed in registers anymore.
     placedArgRegs      = RBM_NONE;
     numPlacedArgLocals = 0;
diff --git a/src/coreclr/jit/lsrariscv64.cpp b/src/coreclr/jit/lsrariscv64.cpp
index 1185eac4cea938..564c27c81043c2 100644
--- a/src/coreclr/jit/lsrariscv64.cpp
+++ b/src/coreclr/jit/lsrariscv64.cpp
@@ -705,6 +705,17 @@ int LinearScan::BuildNode(GenTree* tree)
             BuildDef(tree, RBM_EXCEPTION_OBJECT.GetIntRegSet());
             break;
 
+        case GT_ASYNC_CONTINUATION:
+            srcCount = 0;
+            assert(dstCount == 1);
+            // We kill the continuation arg here to communicate to the
+            // selection phase that the argument is no longer busy. This is a
+            // hack to make sure we do not overwrite the continuation between
+            // the call and this node.
+            addKillForRegs(RBM_ASYNC_CONTINUATION_RET, currentLoc);
+            BuildDef(tree, RBM_ASYNC_CONTINUATION_RET.GetIntRegSet());
+            break;
+
         case GT_INDEX_ADDR:
             assert(dstCount == 1);
             srcCount = BuildBinaryUses(tree->AsOp());
@@ -957,6 +968,11 @@ int LinearScan::BuildCall(GenTreeCall* call)
         BuildKills(call, killMask);
     }
 
+    if (call->IsAsync2() && compiler->compIsAsync2())
+    {
+        MarkAsyncContinuationBusyForCall(call);
+    }
+
     // No args are placed in registers anymore.
     placedArgRegs      = RBM_NONE;
     numPlacedArgLocals = 0;
diff --git a/src/coreclr/jit/targetarm64.h b/src/coreclr/jit/targetarm64.h
index 753e93c027b92f..b38e3f5043f906 100644
--- a/src/coreclr/jit/targetarm64.h
+++ b/src/coreclr/jit/targetarm64.h
@@ -267,8 +267,8 @@
   #define REG_VALIDATE_INDIRECT_CALL_ADDR REG_R15
   #define REG_DISPATCH_INDIRECT_CALL_ADDR REG_R9
 
-  #define REG_ASYNC_CONTINUATION_RET REG_R8
-  #define RBM_ASYNC_CONTINUATION_RET RBM_R8
+  #define REG_ASYNC_CONTINUATION_RET REG_R2
+  #define RBM_ASYNC_CONTINUATION_RET RBM_R2
 
   #define REG_FPBASE               REG_FP
   #define RBM_FPBASE               RBM_FP
diff --git a/src/coreclr/vm/arm64/asmhelpers.S b/src/coreclr/vm/arm64/asmhelpers.S
index ac51662c82db4f..d438a03bc18147 100644
--- a/src/coreclr/vm/arm64/asmhelpers.S
+++ b/src/coreclr/vm/arm64/asmhelpers.S
@@ -301,7 +301,7 @@ NESTED_END TheUMEntryPrestub, _TEXT
 // ------------------------------------------------------------------
 // Hijack function for functions which return a scalar type or a struct (value type)
 NESTED_ENTRY OnHijackTripThread, _TEXT, NoHandler
-    PROLOG_SAVE_REG_PAIR_INDEXED   fp, lr, -176
+    PROLOG_SAVE_REG_PAIR_INDEXED   fp, lr, -192
     // Spill callee saved registers
     PROLOG_SAVE_REG_PAIR   x19, x20, 16
     PROLOG_SAVE_REG_PAIR   x21, x22, 32
@@ -312,9 +312,12 @@ NESTED_ENTRY OnHijackTripThread, _TEXT, NoHandler
     // save any integral return value(s)
     stp x0, x1, [sp, #96]
 
+    // save async continuation return value
+    str x2, [sp, #112]
+
     // save any FP/HFA return value(s)
-    stp q0, q1, [sp, #112]
-    stp q2, q3, [sp, #144]
+    stp q0, q1, [sp, #128]
+    stp q2, q3, [sp, #160]
 
     mov x0, sp
     bl C_FUNC(OnHijackWorker)
@@ -322,16 +325,19 @@ NESTED_ENTRY OnHijackTripThread, _TEXT, NoHandler
     // restore any integral return value(s)
     ldp x0, x1, [sp, #96]
 
+    // restore async continuation return value
+    ldr x2, [sp, #112]
+
     // restore any FP/HFA return value(s)
-    ldp q0, q1, [sp, #112]
-    ldp q2, q3, [sp, #144]
+    ldp q0, q1, [sp, #128]
+    ldp q2, q3, [sp, #160]
 
     EPILOG_RESTORE_REG_PAIR   x19, x20, 16
     EPILOG_RESTORE_REG_PAIR   x21, x22, 32
     EPILOG_RESTORE_REG_PAIR   x23, x24, 48
     EPILOG_RESTORE_REG_PAIR   x25, x26, 64
     EPILOG_RESTORE_REG_PAIR   x27, x28, 80
-    EPILOG_RESTORE_REG_PAIR_INDEXED   fp, lr,   176
+    EPILOG_RESTORE_REG_PAIR_INDEXED   fp, lr,   192
     EPILOG_RETURN
 NESTED_END OnHijackTripThread, _TEXT
 
diff --git a/src/coreclr/vm/arm64/asmhelpers.asm b/src/coreclr/vm/arm64/asmhelpers.asm
index 153c7a3dbe9b5d..bbf17c476b78e5 100644
--- a/src/coreclr/vm/arm64/asmhelpers.asm
+++ b/src/coreclr/vm/arm64/asmhelpers.asm
@@ -630,7 +630,7 @@ COMToCLRDispatchHelper_RegSetup
 ; ------------------------------------------------------------------
 ; Hijack function for functions which return a scalar type or a struct (value type)
     NESTED_ENTRY OnHijackTripThread
-    PROLOG_SAVE_REG_PAIR   fp, lr, #-176!
+    PROLOG_SAVE_REG_PAIR   fp, lr, #-192!
     ; Spill callee saved registers
     PROLOG_SAVE_REG_PAIR   x19, x20, #16
     PROLOG_SAVE_REG_PAIR   x21, x22, #32
@@ -641,9 +641,12 @@ COMToCLRDispatchHelper_RegSetup
     ; save any integral return value(s)
     stp x0, x1, [sp, #96]
 
+    ; save async continuation return value
+    str x2, [sp, #112]
+
     ; save any FP/HFA/HVA return value(s)
-    stp q0, q1, [sp, #112]
-    stp q2, q3, [sp, #144]
+    stp q0, q1, [sp, #128]
+    stp q2, q3, [sp, #160]
 
     mov x0, sp
     bl OnHijackWorker
@@ -651,16 +654,19 @@ COMToCLRDispatchHelper_RegSetup
     ; restore any integral return value(s)
     ldp x0, x1, [sp, #96]
 
+    ; restore async continuation return value
+    ldr x2, [sp, #112]
+
     ; restore any FP/HFA/HVA return value(s)
-    ldp q0, q1, [sp, #112]
-    ldp q2, q3, [sp, #144]
+    ldp q0, q1, [sp, #128]
+    ldp q2, q3, [sp, #160]
 
     EPILOG_RESTORE_REG_PAIR   x19, x20, #16
     EPILOG_RESTORE_REG_PAIR   x21, x22, #32
     EPILOG_RESTORE_REG_PAIR   x23, x24, #48
     EPILOG_RESTORE_REG_PAIR   x25, x26, #64
     EPILOG_RESTORE_REG_PAIR   x27, x28, #80
-    EPILOG_RESTORE_REG_PAIR   fp, lr,   #176!
+    EPILOG_RESTORE_REG_PAIR   fp, lr,   #192!
     EPILOG_RETURN
     NESTED_END
 
diff --git a/src/coreclr/vm/arm64/cgencpu.h b/src/coreclr/vm/arm64/cgencpu.h
index f83de42fecc368..20e5e5412fcfb2 100644
--- a/src/coreclr/vm/arm64/cgencpu.h
+++ b/src/coreclr/vm/arm64/cgencpu.h
@@ -558,6 +558,12 @@ struct HijackArgs
         size_t ReturnValue[2];
     };
     union
+    {
+        DWORD64 X2;
+        size_t AsyncRet;
+    };
+    DWORD64 Pad;
+    union
     {
         struct {
              NEON128 Q0;
diff --git a/src/coreclr/vm/arm64/stubs.cpp b/src/coreclr/vm/arm64/stubs.cpp
index 6cf5a7ec845896..48208d3d6106f2 100644
--- a/src/coreclr/vm/arm64/stubs.cpp
+++ b/src/coreclr/vm/arm64/stubs.cpp
@@ -789,6 +789,8 @@ void HijackFrame::UpdateRegDisplay(const PREGDISPLAY pRD, bool updateFloats)
      pRD->pCurrentContext->Sp = PTR_TO_TADDR(m_Args) + s ;
 
      pRD->pCurrentContext->X0 = m_Args->X0;
+     pRD->pCurrentContext->X1 = m_Args->X1;
+     pRD->pCurrentContext->X2 = m_Args->X2;
 
      pRD->pCurrentContext->X19 = m_Args->X19;
      pRD->pCurrentContext->X20 = m_Args->X20;
diff --git a/src/coreclr/vm/jitinterface.cpp b/src/coreclr/vm/jitinterface.cpp
index 8bc995514988da..cb0b7e5340c4fe 100644
--- a/src/coreclr/vm/jitinterface.cpp
+++ b/src/coreclr/vm/jitinterface.cpp
@@ -14653,7 +14653,7 @@ CORINFO_METHOD_HANDLE CEEJitInfo::getAsyncResumptionStub()
     }
 
     char name[256];
-    int numWritten = sprintf_s(name, "IL_STUB_AsyncResume_%s_%s", m_pMethodBeingCompiled->GetName(), optimizationTierName);
+    int numWritten = sprintf_s(name, ARRAY_SIZE(name), "IL_STUB_AsyncResume_%s_%s", m_pMethodBeingCompiled->GetName(), optimizationTierName);
     if (numWritten != -1)
     {
         AllocMemTracker pamTracker;

From 36b43057af0f7a217a8c17ba423cb321213aaeb8 Mon Sep 17 00:00:00 2001
From: Jakob Botsch Nielsen <Jakob.botsch.nielsen@gmail.com>
Date: Thu, 14 Nov 2024 11:17:36 +0100
Subject: [PATCH 125/203] Fix linux-x64 support (#2770)

- Fix Linux version of `OnHijackTripThread`
- Fix some code lost in a bad merge
---
 src/coreclr/jit/codegenxarch.cpp      | 1 +
 src/coreclr/vm/amd64/unixasmhelpers.S | 9 +++++++--
 2 files changed, 8 insertions(+), 2 deletions(-)

diff --git a/src/coreclr/jit/codegenxarch.cpp b/src/coreclr/jit/codegenxarch.cpp
index 814bab53a9db06..eaffa835e1190f 100644
--- a/src/coreclr/jit/codegenxarch.cpp
+++ b/src/coreclr/jit/codegenxarch.cpp
@@ -2208,6 +2208,7 @@ void CodeGen::genCodeForTreeNode(GenTree* treeNode)
             break;
 
         case GT_ASYNC_CONTINUATION:
+            genCodeForAsyncContinuation(treeNode);
             break;
 
 #if defined(FEATURE_EH_WINDOWS_X86)
diff --git a/src/coreclr/vm/amd64/unixasmhelpers.S b/src/coreclr/vm/amd64/unixasmhelpers.S
index 7e404a2a09cf6c..1f7f3a3856e5b1 100644
--- a/src/coreclr/vm/amd64/unixasmhelpers.S
+++ b/src/coreclr/vm/amd64/unixasmhelpers.S
@@ -152,13 +152,16 @@ NESTED_ENTRY OnHijackTripThread, _TEXT, NoHandler
 
     PUSH_CALLEE_SAVED_REGISTERS
 
+    // Push rcx for the async continuation
+    push_register rcx
+    // Push rdx for the second half of the return value
     push_register rdx
     // Push rax again - this is where integer/pointer return values are returned
     push_register rax
 
     mov                 rdi, rsp
 
-    alloc_stack         0x28
+    alloc_stack         0x20
 
     // First float return register
     movdqa              [rsp], xmm0
@@ -171,9 +174,11 @@ NESTED_ENTRY OnHijackTripThread, _TEXT, NoHandler
 
     movdqa              xmm0, [rsp]
     movdqa              xmm1, [rsp+0x10]
-    free_stack          0x28
+    free_stack          0x20
+
     pop_register        rax
     pop_register        rdx
+    pop_register        rcx
 
     POP_CALLEE_SAVED_REGISTERS
     ret

From 0933305379c3bcc68a021e1efe75529389e81020 Mon Sep 17 00:00:00 2001
From: Jakob Botsch Nielsen <Jakob.botsch.nielsen@gmail.com>
Date: Fri, 15 Nov 2024 10:30:11 +0100
Subject: [PATCH 126/203] Add arm32 support (#2773)

- Use `r2` for the async continuation return register
- Skip a prolog/epilog optimization that could cause us to overwrite this register in the epilog

Contributes to #2764
---
 src/coreclr/jit/codegenarm.cpp  | 9 +++++++++
 src/coreclr/jit/lsraarm.cpp     | 1 +
 src/coreclr/jit/targetarm.h     | 4 ++--
 src/coreclr/vm/arm/asmhelpers.S | 6 ++----
 src/coreclr/vm/arm/cgencpu.h    | 6 ++++++
 5 files changed, 20 insertions(+), 6 deletions(-)

diff --git a/src/coreclr/jit/codegenarm.cpp b/src/coreclr/jit/codegenarm.cpp
index 9b8fb642160a3f..7f1b7d5706bc3d 100644
--- a/src/coreclr/jit/codegenarm.cpp
+++ b/src/coreclr/jit/codegenarm.cpp
@@ -2087,7 +2087,16 @@ regMaskTP CodeGen::genStackAllocRegisterMask(unsigned frameSize, regMaskTP maskC
     // We can't do this optimization with callee saved floating point registers because
     // the stack would be allocated in a wrong spot.
     if (maskCalleeSavedFloat != RBM_NONE)
+    {
+        return RBM_NONE;
+    }
+
+    // We similarly skip it for async2 due to the extra async continuation
+    // return that may be overridden by the pop.
+    if (compiler->compIsAsync2())
+    {
         return RBM_NONE;
+    }
 
     // Allocate space for small frames by pushing extra registers. It generates smaller and faster code
     // that extra sub sp,XXX/add sp,XXX.
diff --git a/src/coreclr/jit/lsraarm.cpp b/src/coreclr/jit/lsraarm.cpp
index ff5aa8c8f96978..286dbb00d0cb1f 100644
--- a/src/coreclr/jit/lsraarm.cpp
+++ b/src/coreclr/jit/lsraarm.cpp
@@ -704,6 +704,7 @@ int LinearScan::BuildNode(GenTree* tree)
         case GT_JCC:
         case GT_SETCC:
         case GT_MEMORYBARRIER:
+        case GT_RETURN_SUSPEND:
             srcCount = BuildSimple(tree);
             break;
 
diff --git a/src/coreclr/jit/targetarm.h b/src/coreclr/jit/targetarm.h
index da9bf1a06da171..507ca4422cf7cb 100644
--- a/src/coreclr/jit/targetarm.h
+++ b/src/coreclr/jit/targetarm.h
@@ -251,8 +251,8 @@
   #define RBM_VALIDATE_INDIRECT_CALL_TRASH (RBM_INT_CALLEE_TRASH)
   #define REG_VALIDATE_INDIRECT_CALL_ADDR REG_R0
 
-  #define REG_ASYNC_CONTINUATION_RET REG_R3
-  #define RBM_ASYNC_CONTINUATION_RET RBM_R3
+  #define REG_ASYNC_CONTINUATION_RET REG_R2
+  #define RBM_ASYNC_CONTINUATION_RET RBM_R2
 
   #define REG_FPBASE               REG_R11
   #define RBM_FPBASE               RBM_R11
diff --git a/src/coreclr/vm/arm/asmhelpers.S b/src/coreclr/vm/arm/asmhelpers.S
index 515c6112d2628d..4631168d0df38d 100644
--- a/src/coreclr/vm/arm/asmhelpers.S
+++ b/src/coreclr/vm/arm/asmhelpers.S
@@ -845,22 +845,20 @@ DelayLoad_Helper\suffix:
 // ------------------------------------------------------------------
 // Hijack function for functions which return a value type
         NESTED_ENTRY OnHijackTripThread, _TEXT, NoHandler
-        PROLOG_PUSH "{r0,r4-r11,lr}"
+        PROLOG_PUSH "{r0,r2,r4-r11,lr}"
 
         PROLOG_VPUSH "{d0-d3}"    // saving as d0-d3 can have the floating point return value
         PROLOG_PUSH "{r1}"        // saving as r1 can have partial return value when return is > 32 bits
-        alloc_stack 4             // 8 byte align
 
         CHECK_STACK_ALIGNMENT
 
         add r0, sp, #40
         bl C_FUNC(OnHijackWorker)
 
-        free_stack 4
         EPILOG_POP "{r1}"
         EPILOG_VPOP "{d0-d3}"
 
-        EPILOG_POP "{r0,r4-r11,pc}"
+        EPILOG_POP "{r0,r2,r4-r11,pc}"
         NESTED_END OnHijackTripThread, _TEXT
 #endif
 
diff --git a/src/coreclr/vm/arm/cgencpu.h b/src/coreclr/vm/arm/cgencpu.h
index 712d3454132d38..8cd60e5ed2e551 100644
--- a/src/coreclr/vm/arm/cgencpu.h
+++ b/src/coreclr/vm/arm/cgencpu.h
@@ -980,6 +980,12 @@ struct HijackArgs
                                // this is only used by functions OnHijackWorker()
     };
 
+    union
+    {
+        DWORD R2;
+        size_t AsyncRet;
+    };
+
     //
     // Non-volatile Integer registers
     //

From 81e5c1e1b0fc3b7228493fb89dce1e1ce44fd75f Mon Sep 17 00:00:00 2001
From: Vladimir Sadov <vsadov@microsoft.com>
Date: Mon, 18 Nov 2024 09:03:51 -0800
Subject: [PATCH 127/203] awaits of Task-returning methods from async2 are done
 as thunk calls. (#2781)

* Bump Roslyn version to one that uses thunks when async2 awaits [Task|ValueTask] returning methods

* attempt to make a2->a1 stub to work

* Fix MethodDesc::IsAsync2Method

* Fix signatures for more methods in AsyncToTask thunks

---------

Co-authored-by: Jakob Botsch Nielsen <jakob.botsch.nielsen@gmail.com>
---
 buildroslynnugets.cmd      |   4 +-
 eng/Versions.props         |   6 +-
 src/coreclr/vm/corelib.h   |   5 +
 src/coreclr/vm/method.hpp  |   4 +-
 src/coreclr/vm/prestub.cpp | 204 +++++++++++++++++++++++++++++++++----
 5 files changed, 198 insertions(+), 25 deletions(-)

diff --git a/buildroslynnugets.cmd b/buildroslynnugets.cmd
index 5e9564528653cc..2358e7f4a9f02f 100644
--- a/buildroslynnugets.cmd
+++ b/buildroslynnugets.cmd
@@ -1,7 +1,7 @@
 setlocal ENABLEEXTENSIONS
 pushd %~dp0
-set ASYNC_ROSLYN_COMMIT=8bb20e76b6d7e041a2cd0a3152d95fdff6578050
-set ASYNC_SUFFIX=async-9
+set ASYNC_ROSLYN_COMMIT=fa251ef06afcc9d1c79761a112b89c6341c0ccfa
+set ASYNC_SUFFIX=async-10
 set ASYNC_ROSLYN_BRANCH=demos/async2-experiment1
 
 cd ..
diff --git a/eng/Versions.props b/eng/Versions.props
index 4a117fd3e9ddd9..b573c01eba46d6 100644
--- a/eng/Versions.props
+++ b/eng/Versions.props
@@ -43,9 +43,9 @@
       Any tools that contribute to the design-time experience should use the MicrosoftCodeAnalysisVersion_LatestVS property above to ensure
       they do not break the local dev experience.
     -->
-    <MicrosoftCodeAnalysisCSharpVersion>4.13.0-async-9</MicrosoftCodeAnalysisCSharpVersion>
-    <MicrosoftCodeAnalysisVersion>4.13.0-async-9</MicrosoftCodeAnalysisVersion>
-    <MicrosoftNetCompilersToolsetVersion>4.13.0-async-9</MicrosoftNetCompilersToolsetVersion>
+    <MicrosoftCodeAnalysisCSharpVersion>4.13.0-async-10</MicrosoftCodeAnalysisCSharpVersion>
+    <MicrosoftCodeAnalysisVersion>4.13.0-async-10</MicrosoftCodeAnalysisVersion>
+    <MicrosoftNetCompilersToolsetVersion>4.13.0-async-10</MicrosoftNetCompilersToolsetVersion>
   </PropertyGroup>
   <!--
     For source generator support we need to target multiple versions of Roslyn in order to be able to run on older versions of Roslyn.
diff --git a/src/coreclr/vm/corelib.h b/src/coreclr/vm/corelib.h
index 4a96b4d5707051..5e1917aff9e6cc 100644
--- a/src/coreclr/vm/corelib.h
+++ b/src/coreclr/vm/corelib.h
@@ -346,11 +346,16 @@ DEFINE_METHOD(TASK, FROM_EXCEPTION, FromException, SM_Exception_RetTask)
 DEFINE_METHOD(TASK, FROM_EXCEPTION_1, FromException, GM_Exception_RetTaskOfT)
 DEFINE_METHOD(TASK, FROM_RESULT_T, FromResult, GM_T_RetTaskOfT)
 DEFINE_METHOD(TASK, GET_COMPLETED_TASK, get_CompletedTask, SM_RetTask)
+DEFINE_METHOD(TASK, GET_AWAITER, GetAwaiter, NoSig)
 
 DEFINE_CLASS(TASK_AWAITER_1, CompilerServices, TaskAwaiter`1)
 DEFINE_METHOD(TASK_AWAITER_1, GET_ISCOMPLETED, get_IsCompleted, NoSig)
 DEFINE_METHOD(TASK_AWAITER_1, GET_RESULT, GetResult, NoSig)
 
+DEFINE_CLASS(TASK_AWAITER, CompilerServices, TaskAwaiter)
+DEFINE_METHOD(TASK_AWAITER, GET_ISCOMPLETED, get_IsCompleted, NoSig)
+DEFINE_METHOD(TASK_AWAITER, GET_RESULT, GetResult, NoSig)
+
 DEFINE_CLASS(TYPE_HANDLE,           System,                 RuntimeTypeHandle)
 DEFINE_CLASS(RT_TYPE_HANDLE,        System,                 RuntimeTypeHandle)
 DEFINE_METHOD(RT_TYPE_HANDLE,       PVOID_CTOR,             .ctor,                      IM_RuntimeType_RetVoid)
diff --git a/src/coreclr/vm/method.hpp b/src/coreclr/vm/method.hpp
index 505edf0ed383de..a4e377f1fe728c 100644
--- a/src/coreclr/vm/method.hpp
+++ b/src/coreclr/vm/method.hpp
@@ -1828,7 +1828,7 @@ class MethodDesc
             return false;
         auto asyncType = GetAddrOfAsyncMethodData()->type;
 
-        return asyncType == AsyncMethodType::Async;
+        return asyncType == AsyncMethodType::Async || asyncType == AsyncMethodType::AsyncToTask;
     }
 
     inline bool RequiresAsyncContinuationArg()
@@ -1996,6 +1996,8 @@ class MethodDesc
     void EmitAsync2MethodThunk(MethodDesc* pAsyncOtherVariant, MetaSig& msig, ILStubLinker* pSL);
     SigPointer GetAsync2ThunkResultTypeSig();
     int GetTokenForGenericMethodCallWithAsyncReturnType(ILCodeStream* pCode, MethodDesc* md);
+    int GetTokenForGenericTypeMethodCallWithAsyncReturnType(ILCodeStream* pCode, MethodDesc* md);
+    int GetTokenForAwaitAwaiterInstantiatedOverTaskAwaiterType(ILCodeStream* pCode, TypeHandle taskAwaiterType);
 public:
     static void CreateDerivedTargetSigWithExtraParams(MetaSig& msig, SigBuilder* stubSigBuilder);
     bool TryGenerateTransientILImplementation(DynamicResolver** resolver, COR_ILMETHOD_DECODER** methodILDecoder);
diff --git a/src/coreclr/vm/prestub.cpp b/src/coreclr/vm/prestub.cpp
index ea4ab4f6d6efa8..982df5a6867497 100644
--- a/src/coreclr/vm/prestub.cpp
+++ b/src/coreclr/vm/prestub.cpp
@@ -2027,7 +2027,7 @@ void MethodDesc::EmitJitStateMachineBasedRuntimeAsyncThunk(MethodDesc* pAsyncOth
 // (MVAR 0). For Task<int>, it returns the signature representing (int).
 SigPointer MethodDesc::GetAsync2ThunkResultTypeSig()
 {
-    _ASSERTE(GetAsyncMethodData().type == AsyncMethodType::TaskToAsync);
+    _ASSERTE(GetAsyncMethodData().type == AsyncMethodType::TaskToAsync || GetAsyncMethodData().type == AsyncMethodType::AsyncToTask);
     PCCOR_SIGNATURE pSigRaw;
     DWORD cSig;
     if (FAILED(GetMDImport()->GetSigOfMethodDef(GetMemberDef(), &cSig, &pSigRaw)))
@@ -2098,31 +2098,93 @@ int MethodDesc::GetTokenForGenericMethodCallWithAsyncReturnType(ILCodeStream* pC
     return pCode->GetToken(md, mdTokenNil, methodSigToken);
 }
 
+// Given a method Bar<T>.Foo, return a MethodSpec token for Bar<T>.Foo
+// instantiated with the result type from the current async method's return
+// type. For example, if "this" represents async2 Task<List<T>> Foo<T>(), and
+// "md" is TaskAwaiter<T>.GetResult(), this returns a MethodSpec representing
+// TaskAwaiter<List<T>>.GetResult().
+int MethodDesc::GetTokenForGenericTypeMethodCallWithAsyncReturnType(ILCodeStream* pCode, MethodDesc* md)
+{
+    if (!md->HasClassOrMethodInstantiation())
+    {
+        return pCode->GetToken(md);
+    }
+
+    // We never get here with a method instantiation currently.
+    _ASSERTE(!md->HasMethodInstantiation());
+
+    SigBuilder typeSigBuilder;
+    typeSigBuilder.AppendData(ELEMENT_TYPE_GENERICINST);
+    typeSigBuilder.AppendData(ELEMENT_TYPE_INTERNAL);
+    // TODO: Encoding potentially shared method tables in
+    // signatures of tokens seems odd, but this hits assert
+    // with the typical method table.
+    typeSigBuilder.AppendPointer(md->GetMethodTable());
+    typeSigBuilder.AppendData(1);
+
+    SigPointer retTypeSig = GetAsync2ThunkResultTypeSig();
+    PCCOR_SIGNATURE retTypeSigRaw;
+    uint32_t retTypeSigLen;
+    retTypeSig.GetSignature(&retTypeSigRaw, &retTypeSigLen);
+
+    typeSigBuilder.AppendBlob((const PVOID)retTypeSigRaw, retTypeSigLen);
+
+    DWORD typeSigLen;
+    PCCOR_SIGNATURE typeSig = (PCCOR_SIGNATURE)typeSigBuilder.GetSignature(&typeSigLen);
+    int typeSigToken = pCode->GetSigToken(typeSig, typeSigLen);
+
+    return pCode->GetToken(md, typeSigToken);
+}
+
 void MethodDesc::EmitAsync2MethodThunk(MethodDesc* pAsyncOtherVariant, MetaSig& msig, ILStubLinker* pSL)
 {
     // Implement IL that is effectively the following
     /*
     {
-        var awaiter = Thunk(arg).GetAwaiter();
+        TaskAwaiter<RetType> awaiter = Thunk(arg).GetAwaiter();
         if (!awaiter.IsCompleted)
         {
             // Magic function which will suspend the current run of async methods
-            RuntimeHelpers.UnsafeAwaitAwaiterFromRuntimeAsync<AwaiterType>(awaiter);
+            RuntimeHelpers.UnsafeAwaitAwaiterFromRuntimeAsync<TaskAwaiter<RetType>>(awaiter);
         }
         return awaiter.GetResult();
     }
     */
     ILCodeStream* pCode = pSL->NewCodeStream(ILStubLinker::kDispatch);
-    TypeHandle thLogicalRetType = msig.GetRetTypeHandleThrowing();
-    MethodTable* pMTTaskOpen = CoreLibBinder::GetClass(CLASS__TASK_1);
-    MethodTable* pMTTask = ClassLoader::LoadGenericInstantiationThrowing(pMTTaskOpen->GetModule(), pMTTaskOpen->GetCl(), Instantiation(&thLogicalRetType, 1)).GetMethodTable();
-    MethodTable* pMTTaskAwaiterOpen = CoreLibBinder::GetClass(CLASS__TASK_AWAITER_1);
-    TypeHandle thTaskAwaiter = ClassLoader::LoadGenericInstantiationThrowing(pMTTaskAwaiterOpen->GetModule(), pMTTaskAwaiterOpen->GetCl(), Instantiation(&thLogicalRetType, 1));
-    DWORD localArg = 0;
 
+    TypeHandle thTaskAwaiter;
+    MethodTable* pMTTask;
+    MethodDesc* mdGetAwaiter;
+    MethodDesc* mdIsCompleted;
+    MethodDesc* mdGetResult;
+
+    if (msig.IsReturnTypeVoid())
+    {
+        pMTTask = CoreLibBinder::GetClass(CLASS__TASK);
+        thTaskAwaiter = CoreLibBinder::GetClass(CLASS__TASK_AWAITER);
+        mdGetAwaiter = CoreLibBinder::GetMethod(METHOD__TASK__GET_AWAITER);
+        mdIsCompleted = CoreLibBinder::GetMethod(METHOD__TASK_AWAITER__GET_ISCOMPLETED);
+        mdGetResult = CoreLibBinder::GetMethod(METHOD__TASK_AWAITER__GET_RESULT);
+    }
+    else
+    {
+        TypeHandle thLogicalRetType = msig.GetRetTypeHandleThrowing();
+        MethodTable* pMTTaskOpen = CoreLibBinder::GetClass(CLASS__TASK_1);
+        pMTTask = ClassLoader::LoadGenericInstantiationThrowing(pMTTaskOpen->GetModule(), pMTTaskOpen->GetCl(), Instantiation(&thLogicalRetType, 1)).GetMethodTable();
+        MethodTable* pMTTaskAwaiterOpen = CoreLibBinder::GetClass(CLASS__TASK_AWAITER_1);
+        thTaskAwaiter = ClassLoader::LoadGenericInstantiationThrowing(pMTTaskAwaiterOpen->GetModule(), pMTTaskAwaiterOpen->GetCl(), Instantiation(&thLogicalRetType, 1));
+        mdGetAwaiter = CoreLibBinder::GetMethod(METHOD__TASK_1__GET_AWAITER);
+        mdGetAwaiter = pMTTask->GetParallelMethodDesc(mdGetAwaiter);
+        mdIsCompleted = CoreLibBinder::GetMethod(METHOD__TASK_AWAITER_1__GET_ISCOMPLETED);
+        mdIsCompleted = thTaskAwaiter.GetMethodTable()->GetParallelMethodDesc(mdIsCompleted);
+        mdGetResult = CoreLibBinder::GetMethod(METHOD__TASK_AWAITER_1__GET_RESULT);
+        mdGetResult = thTaskAwaiter.GetMethodTable()->GetParallelMethodDesc(mdGetResult);
+    }
+
+    DWORD localArg = 0;
     ILCodeLabel* pGetResultLabel = pCode->NewCodeLabel();
 
-    LocalDesc awaiterLocalDesc(CoreLibBinder::GetClass(CLASS__EXCEPTION));
+    LocalDesc awaiterLocalDesc(thTaskAwaiter);
     DWORD awaiterLocal = pCode->NewLocal(awaiterLocalDesc);
 
     if (msig.HasThis())
@@ -2133,27 +2195,131 @@ void MethodDesc::EmitAsync2MethodThunk(MethodDesc* pAsyncOtherVariant, MetaSig&
     {
         pCode->EmitLDARG(localArg++);
     }
-    pCode->EmitCALL(pCode->GetToken(pAsyncOtherVariant), localArg, 1);
-    pCode->EmitCALLVIRT(pCode->GetToken(pMTTask->GetParallelMethodDesc(CoreLibBinder::GetMethod(METHOD__TASK_1__GET_AWAITER))), 1, 1);
+
+    int token;
+    _ASSERTE(!pAsyncOtherVariant->IsWrapperStub());
+    if (pAsyncOtherVariant->HasClassOrMethodInstantiation())
+    {
+        // For generic code emit generic signatures.
+        int typeSigToken = mdTokenNil;
+        if (pAsyncOtherVariant->HasClassInstantiation())
+        {
+            SigBuilder typeSigBuilder;
+            typeSigBuilder.AppendElementType(ELEMENT_TYPE_GENERICINST);
+            typeSigBuilder.AppendElementType(ELEMENT_TYPE_INTERNAL);
+            // TODO: Encoding potentially shared method tables in
+            // signatures of tokens seems odd, but this hits assert
+            // with the typical method table.
+            typeSigBuilder.AppendPointer(pAsyncOtherVariant->GetMethodTable());
+            DWORD numClassTypeArgs = pAsyncOtherVariant->GetNumGenericClassArgs();
+            typeSigBuilder.AppendData(numClassTypeArgs);
+            for (DWORD i = 0; i < numClassTypeArgs; ++i)
+            {
+                typeSigBuilder.AppendElementType(ELEMENT_TYPE_VAR);
+                typeSigBuilder.AppendData(i);
+            }
+
+            DWORD typeSigLen;
+            PCCOR_SIGNATURE typeSig = (PCCOR_SIGNATURE)typeSigBuilder.GetSignature(&typeSigLen);
+            typeSigToken = pCode->GetSigToken(typeSig, typeSigLen);
+        }
+
+        if (pAsyncOtherVariant->HasMethodInstantiation())
+        {
+            SigBuilder methodSigBuilder;
+            DWORD numMethodTypeArgs = pAsyncOtherVariant->GetNumGenericMethodArgs();
+            methodSigBuilder.AppendByte(IMAGE_CEE_CS_CALLCONV_GENERICINST);
+            methodSigBuilder.AppendData(numMethodTypeArgs);
+            for (DWORD i = 0; i < numMethodTypeArgs; ++i)
+            {
+                methodSigBuilder.AppendElementType(ELEMENT_TYPE_MVAR);
+                methodSigBuilder.AppendData(i);
+            }
+
+            DWORD sigLen;
+            PCCOR_SIGNATURE sig = (PCCOR_SIGNATURE)methodSigBuilder.GetSignature(&sigLen);
+            int methodSigToken = pCode->GetSigToken(sig, sigLen);
+            token = pCode->GetToken(pAsyncOtherVariant, typeSigToken, methodSigToken);
+        }
+        else
+        {
+            token = pCode->GetToken(pAsyncOtherVariant, typeSigToken);
+        }
+    }
+    else
+    {
+        token = pCode->GetToken(pAsyncOtherVariant);
+    }
+
+    pCode->EmitCALL(token, localArg, 1);
+
+    int getAwaiterToken;
+    int getIsCompletedToken;
+    int getResultToken;
+    if (!msig.IsReturnTypeVoid())
+    {
+        getAwaiterToken = GetTokenForGenericTypeMethodCallWithAsyncReturnType(pCode, mdGetAwaiter);
+        getIsCompletedToken = GetTokenForGenericTypeMethodCallWithAsyncReturnType(pCode, mdIsCompleted);
+        getResultToken = GetTokenForGenericTypeMethodCallWithAsyncReturnType(pCode, mdGetResult);
+    }
+    else 
+    {
+        getAwaiterToken = pCode->GetToken(mdGetAwaiter);
+        getIsCompletedToken = pCode->GetToken(mdIsCompleted);
+        getResultToken = pCode->GetToken(mdGetResult);
+    }
+
+    pCode->EmitCALLVIRT(getAwaiterToken, 1, 1);
     pCode->EmitSTLOC(awaiterLocal);
     pCode->EmitLDLOCA(awaiterLocal);
-    pCode->EmitCALL(pCode->GetToken(thTaskAwaiter.GetMethodTable()->GetParallelMethodDesc(CoreLibBinder::GetMethod(METHOD__TASK_AWAITER_1__GET_ISCOMPLETED))), 1, 1);
+    pCode->EmitCALL(getIsCompletedToken, 1, 1);
     pCode->EmitBRTRUE(pGetResultLabel);
     pCode->EmitLDLOC(awaiterLocal);
-    MethodDesc* pMagicAwaitFunc = CoreLibBinder::GetMethod(METHOD__RUNTIME_HELPERS__UNSAFE_AWAIT_AWAITER_FROM_RUNTIME_ASYNC_1);
-    TypeHandle thInstantiations[]{ thTaskAwaiter };
 
-    pMagicAwaitFunc = FindOrCreateAssociatedMethodDesc(pMagicAwaitFunc, pMagicAwaitFunc->GetMethodTable(), FALSE, Instantiation(thInstantiations, 1), FALSE);
-
-    pCode->EmitCALL(pCode->GetToken(pMagicAwaitFunc), 1, 0);
+    int awaitAwaiterToken = GetTokenForAwaitAwaiterInstantiatedOverTaskAwaiterType(pCode, thTaskAwaiter);
+    pCode->EmitCALL(awaitAwaiterToken, 1, 0);
     pCode->EmitLabel(pGetResultLabel);
 
     pCode->EmitLDLOCA(awaiterLocal);
-    pCode->EmitCALL(pCode->GetToken(thTaskAwaiter.GetMethodTable()->GetParallelMethodDesc(CoreLibBinder::GetMethod(METHOD__TASK_AWAITER_1__GET_RESULT))), 1, 1);
+    pCode->EmitCALL(getResultToken, 1, mdGetResult->IsVoid() ? 0 : 1);
 
     pCode->EmitRET();
 }
 
+// Get a token for RuntimeHelpers.UnsafeAwaitAwaiterFromRuntimeAsync<TaskAwaiter<T>>()
+// with T substituted by the return type of the async method.
+int MethodDesc::GetTokenForAwaitAwaiterInstantiatedOverTaskAwaiterType(ILCodeStream* pCode, TypeHandle taskAwaiterType)
+{
+    MethodDesc* awaitAwaiter = CoreLibBinder::GetMethod(METHOD__RUNTIME_HELPERS__UNSAFE_AWAIT_AWAITER_FROM_RUNTIME_ASYNC_1);
+    TypeHandle thInstantiations[]{ taskAwaiterType };
+    awaitAwaiter = FindOrCreateAssociatedMethodDesc(awaitAwaiter, awaitAwaiter->GetMethodTable(), FALSE, Instantiation(thInstantiations, 1), FALSE);
+
+    if (!taskAwaiterType.IsSharedByGenericInstantiations())
+    {
+        return pCode->GetToken(awaitAwaiter);
+    }
+
+    SigBuilder methodSigBuilder;
+    methodSigBuilder.AppendByte(IMAGE_CEE_CS_CALLCONV_GENERICINST);
+    methodSigBuilder.AppendData(1);
+    SigPointer retTypeSig = GetAsync2ThunkResultTypeSig();
+    PCCOR_SIGNATURE retTypeSigRaw;
+    uint32_t retTypeSigLen;
+    retTypeSig.GetSignature(&retTypeSigRaw, &retTypeSigLen);
+
+    methodSigBuilder.AppendElementType(ELEMENT_TYPE_GENERICINST);
+    methodSigBuilder.AppendElementType(ELEMENT_TYPE_INTERNAL);
+    methodSigBuilder.AppendPointer(taskAwaiterType.GetMethodTable());
+    methodSigBuilder.AppendData(1);
+    methodSigBuilder.AppendBlob((const PVOID)retTypeSigRaw, retTypeSigLen);
+
+    DWORD methodSigLen;
+    PCCOR_SIGNATURE methodSig = (PCCOR_SIGNATURE)methodSigBuilder.GetSignature(&methodSigLen);
+    int methodSigToken = pCode->GetSigToken(methodSig, methodSigLen);
+
+    return pCode->GetToken(awaitAwaiter, mdTokenNil, methodSigToken);
+}
+
 bool MethodDesc::TryGenerateUnsafeAccessor(DynamicResolver** resolver, COR_ILMETHOD_DECODER** methodILDecoder)
 {
     STANDARD_VM_CONTRACT;

From 4e7c4425cb672a384bc48b1436b1b9771c2fbf98 Mon Sep 17 00:00:00 2001
From: Jakob Botsch Nielsen <Jakob.botsch.nielsen@gmail.com>
Date: Tue, 26 Nov 2024 14:57:19 +0100
Subject: [PATCH 128/203] [RuntimeAsync] Add support for async struct instance
 methods (potential implementation 2) (#2805)

This adds support for async2 struct instance calls with the semantics
that any such method makes a copy of "this" and operates on it instead
of operating through the this-pointer.
---
 src/coreclr/inc/corinfo.h       |  2 +
 src/coreclr/jit/compiler.h      | 13 ++++++-
 src/coreclr/jit/fgbasic.cpp     | 23 +++++++++++-
 src/coreclr/jit/fginline.cpp    | 23 +++++++++++-
 src/coreclr/jit/flowgraph.cpp   | 15 ++++++++
 src/coreclr/jit/importer.cpp    | 44 ++++++++++++++++++++--
 src/coreclr/jit/inline.h        |  1 +
 src/coreclr/jit/morph.cpp       |  6 +++
 src/coreclr/vm/jitinterface.cpp | 31 ++++++++++++----
 src/coreclr/vm/method.hpp       | 19 +++++++++-
 src/tests/async/struct.cs       | 65 +++++++++++++++++++++++++++++++++
 src/tests/async/struct.csproj   |  8 ++++
 12 files changed, 234 insertions(+), 16 deletions(-)
 create mode 100644 src/tests/async/struct.cs
 create mode 100644 src/tests/async/struct.csproj

diff --git a/src/coreclr/inc/corinfo.h b/src/coreclr/inc/corinfo.h
index 703e86c00edb8d..abf1fd9147adc2 100644
--- a/src/coreclr/inc/corinfo.h
+++ b/src/coreclr/inc/corinfo.h
@@ -720,6 +720,8 @@ enum CorInfoOptions
                                                CORINFO_GENERICS_CTXT_FROM_METHODDESC |
                                                CORINFO_GENERICS_CTXT_FROM_METHODTABLE),
     CORINFO_GENERICS_CTXT_KEEP_ALIVE        = 0x00000100, // Keep the generics context alive throughout the method even if there is no explicit use, and report its location to the CLR
+    CORINFO_OPT_COPY_STRUCT_INSTANCE        = 0x00000200, // Function is a struct instance method that operates on a copy of "this"
+
 
 };
 
diff --git a/src/coreclr/jit/compiler.h b/src/coreclr/jit/compiler.h
index 1ddfd15f3a262c..63624295a02770 100644
--- a/src/coreclr/jit/compiler.h
+++ b/src/coreclr/jit/compiler.h
@@ -3981,6 +3981,11 @@ class Compiler
                          // However, if there is a "ldarga 0" or "starg 0" in the IL,
                          // we will redirect all "ldarg(a) 0" and "starg 0" to this temp.
 
+     // For struct instance functions with CORINFO_OPT_COPY_STRUCT_INSTANCE
+     // this is the local that has the copy of "this" to which accesses on
+     // "this" are redirected to
+    unsigned lvaThisCopyVar = BAD_VAR_NUM;
+
     unsigned lvaInlineeReturnSpillTemp = BAD_VAR_NUM; // The temp to spill the non-VOID return expression
                                         // in case there are multiple BBJ_RETURN blocks in the inlinee
                                         // or if the inlinee has GC ref locals.
@@ -6454,6 +6459,7 @@ class Compiler
     void fgObserveInlineConstants(OPCODE opcode, const FgStack& stack, bool isInlining);
 
     void fgAdjustForAddressExposedOrWrittenThis();
+    void fgInitializeThisCopyVar();
 
     unsigned fgStressBBProf()
     {
@@ -6901,7 +6907,7 @@ class Compiler
 
     void fgInvokeInlineeCompiler(GenTreeCall* call, InlineResult* result, InlineContext** createdContext);
     void fgInsertInlineeBlocks(InlineInfo* pInlineInfo);
-    void fgInsertInlineeArgument(const InlArgInfo& argInfo, BasicBlock* block, Statement** afterStmt, Statement** newStmt, const DebugInfo& callDI);
+    void fgInsertInlineeArgument(InlineInfo* pInlineInfo, const InlArgInfo& argInfo, BasicBlock* block, Statement** afterStmt, Statement** newStmt, const DebugInfo& callDI);
     Statement* fgInlinePrependStatements(InlineInfo* inlineInfo);
     void fgInlineAppendStatements(InlineInfo* inlineInfo, BasicBlock* block, Statement* stmt);
 
@@ -10834,6 +10840,11 @@ class Compiler
         return opts.jitFlags->IsSet(JitFlags::JIT_FLAG_RUNTIMEASYNCFUNCTION);
     }
 
+    bool compIsStructMethodThatOperatesOnCopy() const
+    {
+        return (info.compMethodInfo->options & CORINFO_OPT_COPY_STRUCT_INSTANCE) != 0;
+    }
+
     //------------------------------------------------------------------------
     // compMethodReturnsMultiRegRetType: Does this method return a multi-reg value?
     //
diff --git a/src/coreclr/jit/fgbasic.cpp b/src/coreclr/jit/fgbasic.cpp
index 207ec73772386b..d8e52030da3d06 100644
--- a/src/coreclr/jit/fgbasic.cpp
+++ b/src/coreclr/jit/fgbasic.cpp
@@ -2562,6 +2562,11 @@ void Compiler::fgFindJumpTargets(const BYTE* codeAddr, IL_OFFSET codeSize, Fixed
         fgAdjustForAddressExposedOrWrittenThis();
     }
 
+    if (compIsStructMethodThatOperatesOnCopy())
+    {
+        fgInitializeThisCopyVar();
+    }
+
     // Now that we've seen the IL, set lvSingleDef for root method
     // locals.
     //
@@ -2599,7 +2604,7 @@ void Compiler::fgFindJumpTargets(const BYTE* codeAddr, IL_OFFSET codeSize, Fixed
 //    change. The original this (info.compThisArg) then remains
 //    unmodified in the method.  fgAddInternal is responsible for
 //    adding the code to copy the initial this into the temp.
-
+//
 void Compiler::fgAdjustForAddressExposedOrWrittenThis()
 {
     LclVarDsc* thisVarDsc = lvaGetDesc(info.compThisArg);
@@ -2632,6 +2637,22 @@ void Compiler::fgAdjustForAddressExposedOrWrittenThis()
     }
 }
 
+
+//------------------------------------------------------------------------
+// fgInitializeThisCopyVar:
+//   Initialize the local used to copy the "this" instance to for struct
+//   methods with CORINFO_OPT_COPY_STRUCT_INSTANCE set.
+//
+void Compiler::fgInitializeThisCopyVar()
+{
+    assert(lvaThisCopyVar == BAD_VAR_NUM);
+    lvaThisCopyVar = lvaGrabTemp(false DEBUGARG("Copy of 'this'"));
+    lvaSetStruct(lvaThisCopyVar, info.compClassHnd, false);
+
+    LclVarDsc* lclDsc = lvaGetDesc(lvaThisCopyVar);
+    lclDsc->lvHasLdAddrOp = 1;
+}
+
 //------------------------------------------------------------------------
 // fgObserveInlineConstants: look for operations that might get optimized
 //   if this method were to be inlined, and report these to the inliner.
diff --git a/src/coreclr/jit/fginline.cpp b/src/coreclr/jit/fginline.cpp
index c04ed41a715c61..f360b35de837cd 100644
--- a/src/coreclr/jit/fginline.cpp
+++ b/src/coreclr/jit/fginline.cpp
@@ -1806,6 +1806,7 @@ void Compiler::fgInsertInlineeBlocks(InlineInfo* pInlineInfo)
 //    callDI    - debug info for the call
 //
 void Compiler::fgInsertInlineeArgument(
+    InlineInfo* inlineInfo,
     const InlArgInfo& argInfo, BasicBlock* block, Statement** afterStmt, Statement** newStmt, const DebugInfo& callDI)
 {
     const bool argIsSingleDef = !argInfo.argHasLdargaOp && !argInfo.argHasStargOp;
@@ -1840,6 +1841,23 @@ void Compiler::fgInsertInlineeArgument(
             argSingleUseNode->ReplaceWith(argNode, this);
             return;
         }
+        else if (argInfo.argIsByRefToCopy)
+        {
+            ClassLayout* layout = typGetObjLayout(inlineInfo->inlineCandidateInfo->clsHandle);
+            unsigned copyOfThisLcl = lvaGrabTemp(false DEBUGARG("Copy of inlinee struct instance"));
+            lvaSetStruct(copyOfThisLcl, layout, false);
+            GenTree* copyBlock = gtNewStoreLclVarNode(copyOfThisLcl, gtNewBlkIndir(layout, argNode));
+            *newStmt = gtNewStmt(copyBlock, callDI);
+            fgInsertStmtAfter(block, *afterStmt, *newStmt);
+            DISPSTMT(*newStmt);
+            *afterStmt = *newStmt;
+
+            GenTree* storeTmp = gtNewTempStore(argInfo.argTmpNum, gtNewLclVarAddrNode(copyOfThisLcl, argNode->TypeGet()));
+            *newStmt = gtNewStmt(storeTmp, callDI);
+            fgInsertStmtAfter(block, *afterStmt, *newStmt);
+            DISPSTMT(*newStmt);
+            *afterStmt = *newStmt;
+        }
         else
         {
             // We're going to assign the argument value to the temp we use for it in the inline body.
@@ -1862,6 +1880,7 @@ void Compiler::fgInsertInlineeArgument(
         noway_assert(!argInfo.argIsUsed || argInfo.argIsInvariant || argInfo.argIsLclVar);
         noway_assert((argInfo.argIsLclVar == 0) ==
                      (argNode->gtOper != GT_LCL_VAR || (argNode->gtFlags & GTF_GLOB_REF)));
+        noway_assert(!argInfo.argIsByRefToCopy);
 
         // If the argument has side effects, append it
         if (argInfo.argHasSideEff)
@@ -2029,7 +2048,7 @@ Statement* Compiler::fgInlinePrependStatements(InlineInfo* inlineInfo)
     // Append the InstParam
     if (inlineInfo->inlInstParamArgInfo != nullptr)
     {
-        fgInsertInlineeArgument(*inlineInfo->inlInstParamArgInfo, block, &afterStmt, &newStmt, callDI);
+        fgInsertInlineeArgument(inlineInfo, *inlineInfo->inlInstParamArgInfo, block, &afterStmt, &newStmt, callDI);
     }
 
     // Treat arguments that had to be assigned to temps
@@ -2038,7 +2057,7 @@ Statement* Compiler::fgInlinePrependStatements(InlineInfo* inlineInfo)
         JITDUMP("\nArguments setup:\n");
         for (unsigned argNum = 0; argNum < inlineInfo->argCnt; argNum++)
         {
-            fgInsertInlineeArgument(inlArgInfo[argNum], block, &afterStmt, &newStmt, callDI);
+            fgInsertInlineeArgument(inlineInfo, inlArgInfo[argNum], block, &afterStmt, &newStmt, callDI);
         }
     }
 
diff --git a/src/coreclr/jit/flowgraph.cpp b/src/coreclr/jit/flowgraph.cpp
index 9cfd9e3291a1be..77c1275dcfe214 100644
--- a/src/coreclr/jit/flowgraph.cpp
+++ b/src/coreclr/jit/flowgraph.cpp
@@ -2365,6 +2365,21 @@ PhaseStatus Compiler::fgAddInternal()
 
             madeChanges = true;
         }
+
+        if (lvaThisCopyVar != BAD_VAR_NUM)
+        {
+            fgEnsureFirstBBisScratch();
+
+            ClassLayout* layout = lvaGetDesc(lvaThisCopyVar)->GetLayout();
+            GenTree* addr = gtNewLclVarNode(info.compThisArg);
+            GenTree* store = gtNewStoreLclVarNode(lvaThisCopyVar, gtNewBlkIndir(layout, addr));
+            Statement* stmt = fgNewStmtAtEnd(fgFirstBB, store);
+
+            JITDUMP("\nCopy \"this\" to V%02u for struct instance method operating on copy\n", lvaThisCopyVar);
+            DISPSTMT(stmt);
+
+            madeChanges = true;
+        }
     }
 
     // Merge return points if required or beneficial
diff --git a/src/coreclr/jit/importer.cpp b/src/coreclr/jit/importer.cpp
index 96d2be874552c0..36f576dcbe8b0a 100644
--- a/src/coreclr/jit/importer.cpp
+++ b/src/coreclr/jit/importer.cpp
@@ -6750,6 +6750,11 @@ void Compiler::impImportBlockCode(BasicBlock* block)
 
                 if (compIsForInlining())
                 {
+                    if ((lclNum == 0) && compIsStructMethodThatOperatesOnCopy())
+                    {
+                        BADCODE("Illegal starg 0 in function");
+                    }
+
                     op1 = impInlineFetchArg(impInlineInfo->inlArgInfo[lclNum], impInlineInfo->lclVarInfo[lclNum]);
                     noway_assert(op1->gtOper == GT_LCL_VAR);
                     lclNum = op1->AsLclVar()->GetLclNum();
@@ -6763,6 +6768,11 @@ void Compiler::impImportBlockCode(BasicBlock* block)
                 if (lclNum == info.compThisArg)
                 {
                     lclNum = lvaArg0Var;
+
+                    if (compIsStructMethodThatOperatesOnCopy())
+                    {
+                        BADCODE("Illegal starg 0 in function");
+                    }
                 }
 
                 // We should have seen this arg write in the prescan
@@ -6962,6 +6972,11 @@ void Compiler::impImportBlockCode(BasicBlock* block)
                         return;
                     }
 
+                    if ((lclNum == 0) && compIsStructMethodThatOperatesOnCopy())
+                    {
+                        BADCODE("Illegal ldarga 0 in function");
+                    }
+
                     op1->ChangeType(TYP_BYREF);
                     op1->SetOper(GT_LCL_ADDR);
                     op1->AsLclFld()->SetLclOffs(0);
@@ -6974,6 +6989,11 @@ void Compiler::impImportBlockCode(BasicBlock* block)
                 if (lclNum == info.compThisArg)
                 {
                     lclNum = lvaArg0Var;
+
+                    if (compIsStructMethodThatOperatesOnCopy())
+                    {
+                        BADCODE("Illegal ldarga 0 in function");
+                    }
                 }
 
                 goto ADRVAR;
@@ -10957,6 +10977,14 @@ void Compiler::impLoadArg(unsigned ilArgNum, IL_OFFSET offset)
         if (lclNum == info.compThisArg)
         {
             lclNum = lvaArg0Var;
+
+            // Redirect to copy in some struct instance methods
+            if (lvaThisCopyVar != BAD_VAR_NUM)
+            {
+                GenTree* lclAddr = gtNewLclVarAddrNode(lvaThisCopyVar, TYP_BYREF);
+                impPushOnStack(lclAddr, verMakeTypeInfoForLocal(lclNum));
+                return;
+            }
         }
 
         impLoadVar(lclNum, offset);
@@ -13294,6 +13322,16 @@ void Compiler::impInlineInitVars(InlineInfo* pInlineInfo)
             return;
         }
 
+        if ((arg.GetWellKnownArg() == WellKnownArg::ThisPointer) && ((methInfo->options & CORINFO_OPT_COPY_STRUCT_INSTANCE) != 0))
+        {
+            // Method call to a struct instance method that operates on a copy.
+            // We will load the instance as part of copying, so set up flags to
+            // indicate that there is a side effect.
+            inlArgInfo[ilArgCnt].argIsByRefToCopy = true;
+            inlArgInfo[ilArgCnt].argHasGlobRef = true;
+            inlArgInfo[ilArgCnt].argHasSideEff = true;
+        }
+
         ilArgCnt++;
     }
 
@@ -13684,7 +13722,7 @@ GenTree* Compiler::impInlineFetchArg(InlArgInfo& argInfo, const InlLclVarInfo& l
 {
     // Cache the relevant arg and lcl info for this argument.
     // We will modify argInfo but not lclVarInfo.
-    const bool      argCanBeModified = argInfo.argHasLdargaOp || argInfo.argHasStargOp;
+    const bool      argCanBeModified = argInfo.argHasLdargaOp || argInfo.argHasStargOp || argInfo.argIsByRefToCopy;
     const var_types lclTyp           = lclInfo.lclTypeInfo;
     GenTree*        op1              = nullptr;
 
@@ -13747,7 +13785,7 @@ GenTree* Compiler::impInlineFetchArg(InlArgInfo& argInfo, const InlLclVarInfo& l
             }
         }
     }
-    else if (argInfo.argIsByRefToStructLocal && !argInfo.argHasStargOp)
+    else if (argInfo.argIsByRefToStructLocal && !argInfo.argHasStargOp && !argInfo.argIsByRefToCopy)
     {
         /* Argument is a by-ref address to a struct, a normed struct, or its field.
            In these cases, don't spill the byref to a local, simply clone the tree and use it.
@@ -13846,7 +13884,7 @@ GenTree* Compiler::impInlineFetchArg(InlArgInfo& argInfo, const InlLclVarInfo& l
             // if it is a struct, because it requires some additional handling.
 
             if ((!varTypeIsStruct(lclTyp) && !argInfo.argHasSideEff && !argInfo.argHasGlobRef &&
-                 !argInfo.argHasCallerLocalRef))
+                 !argInfo.argHasCallerLocalRef && !argInfo.argIsByRefToStructLocal))
             {
                 /* Get a *LARGE* LCL_VAR node */
                 op1 = gtNewLclLNode(tmpNum, genActualType(lclTyp));
diff --git a/src/coreclr/jit/inline.h b/src/coreclr/jit/inline.h
index 8467996715736d..751417fbc751f4 100644
--- a/src/coreclr/jit/inline.h
+++ b/src/coreclr/jit/inline.h
@@ -659,6 +659,7 @@ struct InlArgInfo
     unsigned argIsByRefToStructLocal : 1; // Is this arg an address of a struct local or a normed struct local or a
                                           // field in them?
     unsigned argIsExact : 1;              // Is this arg of an exact class?
+    unsigned argIsByRefToCopy        : 1; // Arg is a reference to a local that will be copied (used when inlining async2 struct instance methods)
 };
 
 // InlLclVarInfo describes inline candidate argument and local variable properties.
diff --git a/src/coreclr/jit/morph.cpp b/src/coreclr/jit/morph.cpp
index 688f0dbc843936..f88379aab58af8 100644
--- a/src/coreclr/jit/morph.cpp
+++ b/src/coreclr/jit/morph.cpp
@@ -5052,6 +5052,12 @@ GenTree* Compiler::fgMorphPotentialTailCall(GenTreeCall* call)
     }
 #endif
 
+    if (compIsAsync2() != call->IsAsync2())
+    {
+        failTailCall("Caller and callee do not agree on async2-ness");
+        return nullptr;
+    }
+
     // We have to ensure to pass the incoming retValBuf as the
     // outgoing one. Using a temp will not do as this function will
     // not regain control to do the copy. This can happen when inlining
diff --git a/src/coreclr/vm/jitinterface.cpp b/src/coreclr/vm/jitinterface.cpp
index cb0b7e5340c4fe..28512bc1728116 100644
--- a/src/coreclr/vm/jitinterface.cpp
+++ b/src/coreclr/vm/jitinterface.cpp
@@ -7604,7 +7604,8 @@ static void getMethodInfoHelper(
     methInfo->options = (CorInfoOptions)(((UINT32)methInfo->options) |
                             ((ftn->AcquiresInstMethodTableFromThis() ? CORINFO_GENERICS_CTXT_FROM_THIS : 0) |
                              (ftn->RequiresInstMethodTableArg() ? CORINFO_GENERICS_CTXT_FROM_METHODTABLE : 0) |
-                             (ftn->RequiresInstMethodDescArg() ? CORINFO_GENERICS_CTXT_FROM_METHODDESC : 0)));
+                             (ftn->RequiresInstMethodDescArg() ? CORINFO_GENERICS_CTXT_FROM_METHODDESC : 0) |
+                             (ftn->IsStructMethodOperatingOnCopy() ? CORINFO_OPT_COPY_STRUCT_INSTANCE : 0)));
 
     // EEJitManager::ResolveEHClause and CrawlFrame::GetExactGenericInstantiations
     // need to be able to get to CORINFO_GENERICS_CTXT_MASK if there are any
@@ -14366,7 +14367,7 @@ static Signature BuildResumptionStubSignature(LoaderAllocator* alloc)
     return AllocateSignature(alloc, sigBuilder);
 }
 
-static Signature BuildResumptionStubCalliSignature(MetaSig& msig, LoaderAllocator* alloc)
+static Signature BuildResumptionStubCalliSignature(MetaSig& msig, MethodTable* mt, LoaderAllocator* alloc)
 {
     unsigned numArgs = 0;
     if (msig.HasThis())
@@ -14384,7 +14385,7 @@ static Signature BuildResumptionStubCalliSignature(MetaSig& msig, LoaderAllocato
     numArgs += msig.NumFixedArgs();
 
     SigBuilder sigBuilder;
-    sigBuilder.AppendByte(IMAGE_CEE_CS_CALLCONV_DEFAULT);
+    sigBuilder.AppendByte(IMAGE_CEE_CS_CALLCONV_DEFAULT | IMAGE_CEE_CS_CALLCONV_HASTHIS | IMAGE_CEE_CS_CALLCONV_EXPLICITTHIS);
     sigBuilder.AppendData(numArgs);
 
     auto appendTypeHandle = [&](TypeHandle th) {
@@ -14409,7 +14410,15 @@ static Signature BuildResumptionStubCalliSignature(MetaSig& msig, LoaderAllocato
     appendTypeHandle(msig.GetRetTypeHandleThrowing()); // return type
     if (msig.HasThis())
     {
-        sigBuilder.AppendElementType(ELEMENT_TYPE_OBJECT);
+        if (mt->IsValueType())
+        {
+            sigBuilder.AppendElementType(ELEMENT_TYPE_BYREF);
+            appendTypeHandle(TypeHandle(mt));
+        }
+        else
+        {
+            sigBuilder.AppendElementType(ELEMENT_TYPE_OBJECT);
+        }
     }
 #ifndef TARGET_X86
     if (msig.HasGenericContextArg())
@@ -14455,7 +14464,7 @@ CORINFO_METHOD_HANDLE CEEJitInfo::getAsyncResumptionStub()
     Signature stubSig = BuildResumptionStubSignature(md->GetLoaderAllocator());
 
     MetaSig msig(md);
-    Signature calliSig = BuildResumptionStubCalliSignature(msig, md->GetLoaderAllocator());
+    Signature calliSig = BuildResumptionStubCalliSignature(msig, md->GetMethodTable(), md->GetLoaderAllocator());
 
     SigTypeContext emptyCtx;
     ILStubLinker sl(md->GetModule(), stubSig, &emptyCtx, NULL, ILSTUB_LINKER_FLAG_NONE);
@@ -14466,8 +14475,16 @@ CORINFO_METHOD_HANDLE CEEJitInfo::getAsyncResumptionStub()
 
     if (msig.HasThis())
     {
-        _ASSERTE(!md->GetMethodTable()->IsValueType());
-        pCode->EmitLDNULL();
+        if (md->GetMethodTable()->IsValueType())
+        {
+            pCode->EmitLDC(0);
+            pCode->EmitCONV_U();
+        }
+        else
+        {
+            pCode->EmitLDNULL();
+        }
+
         numArgs++;
     }
 
diff --git a/src/coreclr/vm/method.hpp b/src/coreclr/vm/method.hpp
index a4e377f1fe728c..09816ef3179ae9 100644
--- a/src/coreclr/vm/method.hpp
+++ b/src/coreclr/vm/method.hpp
@@ -1818,7 +1818,7 @@ class MethodDesc
         LIMITED_METHOD_DAC_CONTRACT;
         if (!HasAsyncMethodData())
             return false;
-        auto asyncType = GetAddrOfAsyncMethodData()->type;
+        AsyncMethodType asyncType = GetAddrOfAsyncMethodData()->type;
         return asyncType == AsyncMethodType::AsyncToTask || asyncType == AsyncMethodType::TaskToAsync;
     }
 
@@ -1826,11 +1826,26 @@ class MethodDesc
     {
         if (!HasAsyncMethodData())
             return false;
-        auto asyncType = GetAddrOfAsyncMethodData()->type;
+        AsyncMethodType asyncType = GetAddrOfAsyncMethodData()->type;
 
         return asyncType == AsyncMethodType::Async || asyncType == AsyncMethodType::AsyncToTask;
     }
 
+    inline bool IsStructMethodOperatingOnCopy()
+    {
+        if (!GetMethodTable()->IsValueType() || IsStatic())
+            return false;
+
+        if (!HasAsyncMethodData())
+            return false;
+
+        // Only user runtimeasync methods operate on copies. runtime-async ->
+        // compiler-async thunks do not.
+        AsyncMethodType asyncType = GetAddrOfAsyncMethodData()->type;
+
+        return asyncType == AsyncMethodType::Async;
+    }
+
     inline bool RequiresAsyncContinuationArg()
     {
         return IsAsync2Method();
diff --git a/src/tests/async/struct.cs b/src/tests/async/struct.cs
new file mode 100644
index 00000000000000..fd73631e9b9d66
--- /dev/null
+++ b/src/tests/async/struct.cs
@@ -0,0 +1,65 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+#pragma warning disable 1998
+
+using System;
+using System.Runtime.CompilerServices;
+using System.Threading.Tasks;
+using Xunit;
+
+public class Async2Struct
+{
+    [Fact]
+    public static void TestEntryPoint()
+    {
+        Async().Wait();
+    }
+
+    private static async2 Task Async()
+    {
+        S s = new S(100);
+        await s.Test();
+        AssertEqual(100, s.Value);
+    }
+
+    [MethodImpl(MethodImplOptions.NoInlining)]
+    private static void AssertEqual(int expected, int val)
+    {
+        Assert.Equal(expected, val);
+    }
+
+    private struct S
+    {
+        public int Value;
+
+        public S(int value) => Value = value;
+
+        public async2 Task Test()
+        {
+            AssertEqual(100, Value);
+            Value++;
+            await InstanceCall();
+            AssertEqual(101, Value);
+
+            await TaskButNotAsync();
+            AssertEqual(102, Value);
+        }
+
+        private async2 Task InstanceCall()
+        {
+            AssertEqual(101, Value);
+            Value++;
+            AssertEqual(102, Value);
+            await Task.Yield();
+            AssertEqual(102, Value);
+        }
+
+        private Task TaskButNotAsync()
+        {
+            AssertEqual(101, Value);
+            Value++;
+            return Task.CompletedTask;
+        }
+    }
+}
diff --git a/src/tests/async/struct.csproj b/src/tests/async/struct.csproj
new file mode 100644
index 00000000000000..9367a79b2edbb1
--- /dev/null
+++ b/src/tests/async/struct.csproj
@@ -0,0 +1,8 @@
+<Project Sdk="Microsoft.NET.Sdk.IL">
+  <PropertyGroup>
+    <Optimize>True</Optimize>
+  </PropertyGroup>
+  <ItemGroup>
+    <Compile Include="$(MSBuildProjectName).cs" />
+  </ItemGroup>
+</Project>

From 4daa761347cfec8b8a0cd1330e4f05b3f1d5a3c1 Mon Sep 17 00:00:00 2001
From: Jakob Botsch Nielsen <Jakob.botsch.nielsen@gmail.com>
Date: Thu, 28 Nov 2024 16:11:02 +0100
Subject: [PATCH 129/203] [RuntimeAsync] Keep `LoaderAllocator` objects alive
 from continuations (#2812)

Add logic to make sure we keep the exposed loader allocator object alive
when a runtime async function suspends. This adds two new JIT helpers
`CORINFO_HELP_ALLOC_CONTINUATION_CLASS` and
`CORINFO_HELP_ALLOC_CONTINUATION_METHOD` that store the
`LoaderAllocator` associated with a class or method into the allocated
continuation before returning it back to the JIT.

There are a few cases where the JIT has to call the new helpers:
1. Any function loaded into a collectible ALC must keep the
   `LoaderAllocator` associated with the method being compiled alive
2. A shared generic function taking a `MethodDesc*` as the generic
   context must keep the `LoaderAllocator` assocated with that
   `MethodDesc*` alive
3. A shared generic function taking a `MethodTable*` as the generic
   context must similarly keep that one alive.
---
 .../RuntimeHelpers.CoreCLR.cs                 | 51 ++++++++++--
 src/coreclr/inc/corinfo.h                     |  5 ++
 src/coreclr/inc/jithelpers.h                  |  2 +
 src/coreclr/jit/async.cpp                     | 54 +++++++++++--
 src/coreclr/jit/async.h                       |  6 ++
 src/coreclr/jit/compiler.h                    |  2 +-
 src/coreclr/jit/compiler.hpp                  |  8 +-
 .../tools/superpmi/superpmi-shared/agnostic.h |  1 +
 .../superpmi-shared/methodcontext.cpp         |  8 +-
 src/coreclr/vm/appdomain.cpp                  |  1 -
 src/coreclr/vm/comutilnative.cpp              |  8 ++
 src/coreclr/vm/comutilnative.h                |  1 +
 src/coreclr/vm/corelib.h                      |  4 +-
 src/coreclr/vm/ecall.cpp                      |  8 --
 src/coreclr/vm/ecall.h                        |  2 -
 src/coreclr/vm/ecalllist.h                    |  1 +
 src/coreclr/vm/jitinterface.cpp               |  1 +
 src/tests/async/collectible-alc.cs            | 78 +++++++++++++++++++
 src/tests/async/collectible-alc.csproj        | 10 +++
 src/tests/issues.targets                      |  6 ++
 20 files changed, 230 insertions(+), 27 deletions(-)
 create mode 100644 src/tests/async/collectible-alc.cs
 create mode 100644 src/tests/async/collectible-alc.csproj

diff --git a/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs
index 13bef8281bc4ab..5cfef1efc91fb9 100644
--- a/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs
+++ b/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs
@@ -583,6 +583,44 @@ private static Continuation AllocContinuation(Continuation prevContinuation, nui
             return newContinuation;
         }
 
+        private static unsafe Continuation AllocContinuationMethod(Continuation prevContinuation, nuint numGCRefs, nuint dataSize, MethodDesc* method)
+        {
+            LoaderAllocator loaderAllocator = RuntimeMethodHandle.GetLoaderAllocator(new RuntimeMethodHandleInternal((IntPtr)method));
+            object?[] gcData;
+            if (loaderAllocator != null)
+            {
+                gcData = new object[numGCRefs + 1];
+                gcData[numGCRefs] = loaderAllocator;
+            }
+            else
+            {
+                gcData = new object[numGCRefs];
+            }
+
+            Continuation newContinuation = new Continuation { Data = new byte[dataSize], GCData = gcData };
+            prevContinuation.Next = newContinuation;
+            return newContinuation;
+        }
+
+        private static unsafe Continuation AllocContinuationClass(Continuation prevContinuation, nuint numGCRefs, nuint dataSize, MethodTable* methodTable)
+        {
+            IntPtr loaderAllocatorHandle = methodTable->GetLoaderAllocatorHandle();
+            object?[] gcData;
+            if (loaderAllocatorHandle != IntPtr.Zero)
+            {
+                gcData = new object[numGCRefs + 1];
+                gcData[numGCRefs] = GCHandle.FromIntPtr(loaderAllocatorHandle).Target;
+            }
+            else
+            {
+                gcData = new object[numGCRefs];
+            }
+
+            Continuation newContinuation = new Continuation { Data = new byte[dataSize], GCData = gcData };
+            prevContinuation.Next = newContinuation;
+            return newContinuation;
+        }
+
         private struct RuntimeAsyncAwaitState
         {
             public Continuation? SentinelContinuation;
@@ -640,7 +678,7 @@ private static Continuation UnlinkHeadContinuation(out AwaitableProxy awaitableP
             return head;
         }
 
-        private static async Task<T> FinalizeTaskReturningThunk<T>(Continuation continuation)
+        private static async Task<T?> FinalizeTaskReturningThunk<T>(Continuation continuation)
         {
             Continuation finalContinuation = new Continuation();
 
@@ -670,7 +708,7 @@ private static async Task<T> FinalizeTaskReturningThunk<T>(Continuation continua
                     Debug.Assert(finalResult == finalContinuation);
                     if (IsReferenceOrContainsReferences<T>())
                     {
-                        return (T)finalResult.GCData![0];
+                        return (T?)finalResult.GCData![0];
                     }
                     else
                     {
@@ -701,7 +739,7 @@ private static async Task FinalizeTaskReturningThunk(Continuation continuation)
             }
         }
 
-        private static async ValueTask<T> FinalizeValueTaskReturningThunk<T>(Continuation continuation)
+        private static async ValueTask<T?> FinalizeValueTaskReturningThunk<T>(Continuation continuation)
         {
             Continuation finalContinuation = new Continuation();
 
@@ -731,7 +769,7 @@ private static async ValueTask<T> FinalizeValueTaskReturningThunk<T>(Continuatio
                     Debug.Assert(finalResult == finalContinuation);
                     if (IsReferenceOrContainsReferences<T>())
                     {
-                        return (T)finalResult.GCData![0];
+                        return (T?)finalResult.GCData![0];
                     }
                     else
                     {
@@ -1148,6 +1186,9 @@ public TypeHandle GetArrayElementTypeHandle()
         /// </summary>
         [MethodImpl(MethodImplOptions.InternalCall)]
         public extern MethodTable* GetMethodTableMatchingParentClass(MethodTable* parent);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        public extern IntPtr GetLoaderAllocatorHandle();
     }
 
     // Subset of src\vm\methodtable.h
@@ -1353,6 +1394,6 @@ internal sealed unsafe class Continuation
         // set, and otherwise at GCData[1].
         //
         public byte[]? Data;
-        public object[]? GCData;
+        public object?[]? GCData;
     }
 }
diff --git a/src/coreclr/inc/corinfo.h b/src/coreclr/inc/corinfo.h
index abf1fd9147adc2..652f484a3bc92e 100644
--- a/src/coreclr/inc/corinfo.h
+++ b/src/coreclr/inc/corinfo.h
@@ -596,6 +596,8 @@ enum CorInfoHelpFunc
     CORINFO_HELP_DISPATCH_INDIRECT_CALL,    // CFG: Validate and dispatch to pointer
 
     CORINFO_HELP_ALLOC_CONTINUATION,
+    CORINFO_HELP_ALLOC_CONTINUATION_METHOD,
+    CORINFO_HELP_ALLOC_CONTINUATION_CLASS,
     CORINFO_HELP_RESUME_OSR,
 
     CORINFO_HELP_COUNT,
@@ -1730,6 +1732,9 @@ struct CORINFO_ASYNC2_INFO
     CORINFO_FIELD_HANDLE continuationDataFldHnd;
     // 'GCData' field
     CORINFO_FIELD_HANDLE continuationGCDataFldHnd;
+    // Whether or not the continuation needs to be alloated through the
+    // helper that also takes a method handle
+    bool continuationsNeedMethodHandle;
 };
 
 // Flags passed from JIT to runtime.
diff --git a/src/coreclr/inc/jithelpers.h b/src/coreclr/inc/jithelpers.h
index 79f3f7a6bd3b75..2d9ab2d2c149d8 100644
--- a/src/coreclr/inc/jithelpers.h
+++ b/src/coreclr/inc/jithelpers.h
@@ -343,6 +343,8 @@
 #endif
 
     DYNAMICJITHELPER(CORINFO_HELP_ALLOC_CONTINUATION, NULL, METHOD__RUNTIME_HELPERS__ALLOC_CONTINUATION)
+    DYNAMICJITHELPER(CORINFO_HELP_ALLOC_CONTINUATION_METHOD, NULL, METHOD__RUNTIME_HELPERS__ALLOC_CONTINUATION_METHOD)
+    DYNAMICJITHELPER(CORINFO_HELP_ALLOC_CONTINUATION_CLASS, NULL, METHOD__RUNTIME_HELPERS__ALLOC_CONTINUATION_CLASS)
     JITHELPER(CORINFO_HELP_RESUME_OSR, JIT_ResumeOSR, CORINFO_HELP_SIG_REG_ONLY)
 
 #undef JITHELPER
diff --git a/src/coreclr/jit/async.cpp b/src/coreclr/jit/async.cpp
index a636af937252f5..c6d7665b1c327a 100644
--- a/src/coreclr/jit/async.cpp
+++ b/src/coreclr/jit/async.cpp
@@ -164,7 +164,7 @@ PhaseStatus Async2Transformation::Run()
     {
         for (GenTree* tree : LIR::AsRange(block))
         {
-            if (tree->IsCall() && tree->AsCall()->IsAsync2())
+            if (tree->IsCall() && tree->AsCall()->IsAsync2() && !tree->AsCall()->IsTailCall())
             {
                 JITDUMP(FMT_BB " contains await(s)\n", block->bbNum);
                 worklist.Push(block);
@@ -588,11 +588,9 @@ void Async2Transformation::Transform(
     block->GetFalseEdge()->setLikelihood(1 - RESUME_SUSPEND_LIKELIHOOD);
 
     // Allocate continuation
-    returnedContinuation           = m_comp->gtNewLclvNode(m_returnedContinuationVar, TYP_REF);
-    GenTree*     gcRefsCountNode   = m_comp->gtNewIconNode((ssize_t)gcRefsCount, TYP_I_IMPL);
-    GenTree*     dataSizeNode      = m_comp->gtNewIconNode((ssize_t)dataSize, TYP_I_IMPL);
-    GenTreeCall* allocContinuation = m_comp->gtNewHelperCallNode(CORINFO_HELP_ALLOC_CONTINUATION, TYP_REF,
-                                                                 returnedContinuation, gcRefsCountNode, dataSizeNode);
+    returnedContinuation = m_comp->gtNewLclvNode(m_returnedContinuationVar, TYP_REF);
+
+    GenTreeCall* allocContinuation = CreateAllocContinuationCall(life, returnedContinuation, gcRefsCount, dataSize);
 
     m_comp->compCurBB = suspendBB;
     m_comp->fgMorphTree(allocContinuation);
@@ -1100,6 +1098,50 @@ void Async2Transformation::Transform(
     m_resumptionBBs.push_back(resumeBB);
 }
 
+GenTreeCall* Async2Transformation::CreateAllocContinuationCall(AsyncLiveness& life, GenTree* prevContinuation, unsigned gcRefsCount, unsigned dataSize)
+{
+    GenTree* gcRefsCountNode = m_comp->gtNewIconNode((ssize_t)gcRefsCount, TYP_I_IMPL);
+    GenTree* dataSizeNode = m_comp->gtNewIconNode((ssize_t)dataSize, TYP_I_IMPL);
+    // If VM requests that we report the method handle, or if we have a shared generic context method handle
+    // that is live here, then we need to call a different helper.
+    GenTree* methodHandleArg = nullptr;
+    GenTree* classHandleArg = nullptr;
+    if (((m_comp->info.compMethodInfo->options & CORINFO_GENERICS_CTXT_FROM_METHODDESC) != 0) && life.IsLive(m_comp->info.compTypeCtxtArg))
+    {
+        methodHandleArg = m_comp->gtNewLclvNode(m_comp->info.compTypeCtxtArg, TYP_I_IMPL);
+    }
+    else if (((m_comp->info.compMethodInfo->options & CORINFO_GENERICS_CTXT_FROM_METHODTABLE) != 0) && life.IsLive(m_comp->info.compTypeCtxtArg))
+    {
+        classHandleArg = m_comp->gtNewLclvNode(m_comp->info.compTypeCtxtArg, TYP_I_IMPL);
+    }
+    else if (m_async2Info.continuationsNeedMethodHandle)
+    {
+        methodHandleArg = m_comp->gtNewIconEmbMethHndNode(m_comp->info.compMethodHnd);
+    }
+
+    if (methodHandleArg != nullptr)
+    {
+        return m_comp->gtNewHelperCallNode(
+            CORINFO_HELP_ALLOC_CONTINUATION_METHOD,
+            TYP_REF,
+            prevContinuation, gcRefsCountNode, dataSizeNode, methodHandleArg);
+    }
+
+    if (classHandleArg != nullptr)
+    {
+        return m_comp->gtNewHelperCallNode(
+            CORINFO_HELP_ALLOC_CONTINUATION_CLASS,
+            TYP_REF,
+            prevContinuation, gcRefsCountNode, dataSizeNode, classHandleArg);
+    }
+
+    return m_comp->gtNewHelperCallNode(
+        CORINFO_HELP_ALLOC_CONTINUATION,
+        TYP_REF,
+        prevContinuation, gcRefsCountNode, dataSizeNode);
+}
+
+
 GenTreeIndir* Async2Transformation::LoadFromOffset(GenTree*     base,
                                                    unsigned     offset,
                                                    var_types    type,
diff --git a/src/coreclr/jit/async.h b/src/coreclr/jit/async.h
index 377ab1a0b56692..313d72cd0952ac 100644
--- a/src/coreclr/jit/async.h
+++ b/src/coreclr/jit/async.h
@@ -45,6 +45,12 @@ class Async2Transformation
                    class AsyncLiveness&      life,
                    BasicBlock**              remainder);
 
+    GenTreeCall* CreateAllocContinuationCall(
+        AsyncLiveness& life,
+        GenTree* prevContinuation,
+        unsigned gcRefsCount,
+        unsigned int dataSize);
+
     GenTreeIndir* LoadFromOffset(GenTree*     base,
                                  unsigned     offset,
                                  var_types    type,
diff --git a/src/coreclr/jit/compiler.h b/src/coreclr/jit/compiler.h
index 63624295a02770..64ae6fb7514477 100644
--- a/src/coreclr/jit/compiler.h
+++ b/src/coreclr/jit/compiler.h
@@ -3106,7 +3106,7 @@ class Compiler
     GenTreeCall* gtNewIndCallNode(GenTree* addr, var_types type, const DebugInfo& di = DebugInfo());
 
     GenTreeCall* gtNewHelperCallNode(
-        unsigned helper, var_types type, GenTree* arg1 = nullptr, GenTree* arg2 = nullptr, GenTree* arg3 = nullptr);
+        unsigned helper, var_types type, GenTree* arg1 = nullptr, GenTree* arg2 = nullptr, GenTree* arg3 = nullptr, GenTree* arg4 = nullptr);
 
     GenTreeCall* gtNewRuntimeLookupHelperCallNode(CORINFO_RUNTIME_LOOKUP* pRuntimeLookup,
                                                   GenTree*                ctxTree,
diff --git a/src/coreclr/jit/compiler.hpp b/src/coreclr/jit/compiler.hpp
index 73fe24fa2f2759..b0cc2f1411606d 100644
--- a/src/coreclr/jit/compiler.hpp
+++ b/src/coreclr/jit/compiler.hpp
@@ -1538,7 +1538,7 @@ inline GenTree* Compiler::gtNewIconEmbFldHndNode(CORINFO_FIELD_HANDLE fldHnd)
 //    New CT_HELPER node
 //
 inline GenTreeCall* Compiler::gtNewHelperCallNode(
-    unsigned helper, var_types type, GenTree* arg1, GenTree* arg2, GenTree* arg3)
+    unsigned helper, var_types type, GenTree* arg1, GenTree* arg2, GenTree* arg3, GenTree* arg4)
 {
     GenTreeCall* const result = gtNewCallNode(CT_HELPER, eeFindHelper(helper), type);
 
@@ -1557,6 +1557,12 @@ inline GenTreeCall* Compiler::gtNewHelperCallNode(
     result->gtInlineObservation = InlineObservation::CALLSITE_IS_CALL_TO_HELPER;
 #endif
 
+    if (arg4 != nullptr)
+    {
+        result->gtArgs.PushFront(this, NewCallArg::Primitive(arg4));
+        result->gtFlags |= arg4->gtFlags & GTF_ALL_EFFECT;
+    }
+
     if (arg3 != nullptr)
     {
         result->gtArgs.PushFront(this, NewCallArg::Primitive(arg3));
diff --git a/src/coreclr/tools/superpmi/superpmi-shared/agnostic.h b/src/coreclr/tools/superpmi/superpmi-shared/agnostic.h
index 5aa4f36079fea5..429fc39794eb8f 100644
--- a/src/coreclr/tools/superpmi/superpmi-shared/agnostic.h
+++ b/src/coreclr/tools/superpmi/superpmi-shared/agnostic.h
@@ -204,6 +204,7 @@ struct Agnostic_CORINFO_ASYNC2_INFO
     DWORDLONG continuationFlagsFldHnd;
     DWORDLONG continuationDataFldHnd;
     DWORDLONG continuationGCDataFldHnd;
+    DWORD continuationsNeedMethodHandle;
 };
 
 struct Agnostic_GetOSRInfo
diff --git a/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.cpp b/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.cpp
index 46d4d5ec22832a..8939671f90c937 100644
--- a/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.cpp
+++ b/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.cpp
@@ -4432,6 +4432,8 @@ void MethodContext::recGetAsync2Info(const CORINFO_ASYNC2_INFO* pAsync2Info)
         GetAsync2Info = new LightWeightMap<DWORD, Agnostic_CORINFO_ASYNC2_INFO>();
 
     Agnostic_CORINFO_ASYNC2_INFO value;
+    ZeroMemory(&value, sizeof(value));
+
     value.continuationClsHnd = CastHandle(pAsync2Info->continuationClsHnd);
     value.continuationNextFldHnd = CastHandle(pAsync2Info->continuationNextFldHnd);
     value.continuationResumeFldHnd = CastHandle(pAsync2Info->continuationResumeFldHnd);
@@ -4439,6 +4441,7 @@ void MethodContext::recGetAsync2Info(const CORINFO_ASYNC2_INFO* pAsync2Info)
     value.continuationFlagsFldHnd = CastHandle(pAsync2Info->continuationFlagsFldHnd);
     value.continuationDataFldHnd = CastHandle(pAsync2Info->continuationDataFldHnd);
     value.continuationGCDataFldHnd = CastHandle(pAsync2Info->continuationGCDataFldHnd);
+    value.continuationsNeedMethodHandle = pAsync2Info->continuationsNeedMethodHandle ? 1 : 0;
 
     GetAsync2Info->Add(0, value);
     DEBUG_REC(dmpGetAsync2Info(0, value));
@@ -4446,10 +4449,10 @@ void MethodContext::recGetAsync2Info(const CORINFO_ASYNC2_INFO* pAsync2Info)
 void MethodContext::dmpGetAsync2Info(DWORD key, const Agnostic_CORINFO_ASYNC2_INFO& value)
 {
     printf("GetAsync2Info key %u value contClsHnd-%016" PRIX64 " contNextFldHnd-%016" PRIX64 " contResumeFldHnd-%016" PRIX64
-           " contStateFldHnd-%016" PRIX64 " contFlagsFldHnd-%016" PRIX64 " contDataFldHnd-%016" PRIX64 " contGCDataFldHnd-%016" PRIX64,
+           " contStateFldHnd-%016" PRIX64 " contFlagsFldHnd-%016" PRIX64 " contDataFldHnd-%016" PRIX64 " contGCDataFldHnd-%016" PRIX64 " contsNeedMethodHandle-%d",
         key, value.continuationClsHnd, value.continuationNextFldHnd, value.continuationResumeFldHnd,
         value.continuationStateFldHnd, value.continuationFlagsFldHnd, value.continuationDataFldHnd,
-        value.continuationGCDataFldHnd);
+        value.continuationGCDataFldHnd, value.continuationsNeedMethodHandle);
 }
 void MethodContext::repGetAsync2Info(CORINFO_ASYNC2_INFO* pAsync2InfoOut)
 {
@@ -4461,6 +4464,7 @@ void MethodContext::repGetAsync2Info(CORINFO_ASYNC2_INFO* pAsync2InfoOut)
     pAsync2InfoOut->continuationFlagsFldHnd = (CORINFO_FIELD_HANDLE)value.continuationFlagsFldHnd;
     pAsync2InfoOut->continuationDataFldHnd = (CORINFO_FIELD_HANDLE)value.continuationDataFldHnd;
     pAsync2InfoOut->continuationGCDataFldHnd = (CORINFO_FIELD_HANDLE)value.continuationGCDataFldHnd;
+    pAsync2InfoOut->continuationsNeedMethodHandle = value.continuationsNeedMethodHandle != 0;
     DEBUG_REP(dmpGetAsync2Info(0, value));
 }
 
diff --git a/src/coreclr/vm/appdomain.cpp b/src/coreclr/vm/appdomain.cpp
index e2414ece75f3c5..ce933c43739a11 100644
--- a/src/coreclr/vm/appdomain.cpp
+++ b/src/coreclr/vm/appdomain.cpp
@@ -1088,7 +1088,6 @@ void SystemDomain::LoadBaseSystemClasses()
         // further loading of nonprimitive types may need casting support.
         // initialize cast cache here.
         CastCache::Initialize();
-        ECall::PopulateAsyncHelpers();
 
         // used by IsImplicitInterfaceOfSZArray
         CoreLibBinder::GetClass(CLASS__IENUMERABLEGENERIC);
diff --git a/src/coreclr/vm/comutilnative.cpp b/src/coreclr/vm/comutilnative.cpp
index 2ef7785294266c..ec38eb4dbeca61 100644
--- a/src/coreclr/vm/comutilnative.cpp
+++ b/src/coreclr/vm/comutilnative.cpp
@@ -1818,6 +1818,14 @@ FCIMPL2(MethodTable*, MethodTableNative::GetMethodTableMatchingParentClass, Meth
 }
 FCIMPLEND
 
+FCIMPL1(OBJECTHANDLE, MethodTableNative::GetLoaderAllocatorHandle, MethodTable *mt)
+{
+    FCALL_CONTRACT;
+
+    return mt->GetLoaderAllocatorObjectHandle();
+}
+FCIMPLEND
+
 extern "C" BOOL QCALLTYPE MethodTable_AreTypesEquivalent(MethodTable* mta, MethodTable* mtb)
 {
     QCALL_CONTRACT;
diff --git a/src/coreclr/vm/comutilnative.h b/src/coreclr/vm/comutilnative.h
index b2acd9a2cb45d9..bb086fb2af55be 100644
--- a/src/coreclr/vm/comutilnative.h
+++ b/src/coreclr/vm/comutilnative.h
@@ -259,6 +259,7 @@ class MethodTableNative {
     static FCDECL1(UINT32, GetNumInstanceFieldBytes, MethodTable* mt);
     static FCDECL1(CorElementType, GetPrimitiveCorElementType, MethodTable* mt);
     static FCDECL2(MethodTable*, GetMethodTableMatchingParentClass, MethodTable* mt, MethodTable* parent);
+    static FCDECL1(OBJECTHANDLE, GetLoaderAllocatorHandle, MethodTable* mt);
 };
 
 extern "C" BOOL QCALLTYPE MethodTable_AreTypesEquivalent(MethodTable* mta, MethodTable* mtb);
diff --git a/src/coreclr/vm/corelib.h b/src/coreclr/vm/corelib.h
index 5e1917aff9e6cc..b92109af83aeec 100644
--- a/src/coreclr/vm/corelib.h
+++ b/src/coreclr/vm/corelib.h
@@ -661,7 +661,9 @@ DEFINE_METHOD(RUNTIME_HELPERS,      ALLOC_TAILCALL_ARG_BUFFER, AllocTailCallArgB
 DEFINE_METHOD(RUNTIME_HELPERS,      GET_TAILCALL_INFO,      GetTailCallInfo, NoSig)
 DEFINE_METHOD(RUNTIME_HELPERS,      DISPATCH_TAILCALLS,     DispatchTailCalls,          NoSig)
 DEFINE_METHOD(RUNTIME_HELPERS,      COPY_CONSTRUCT,         CopyConstruct, NoSig)
-DEFINE_METHOD(RUNTIME_HELPERS,      ALLOC_CONTINUATION,     AllocContinuation, NoSig)
+DEFINE_METHOD(RUNTIME_HELPERS,      ALLOC_CONTINUATION,        AllocContinuation, NoSig)
+DEFINE_METHOD(RUNTIME_HELPERS,      ALLOC_CONTINUATION_METHOD, AllocContinuationMethod, NoSig)
+DEFINE_METHOD(RUNTIME_HELPERS,      ALLOC_CONTINUATION_CLASS,  AllocContinuationClass, NoSig)
 DEFINE_METHOD(RUNTIME_HELPERS,      FINALIZE_TASK_RETURNING_THUNK, FinalizeTaskReturningThunk, SM_Continuation_RetTask)
 DEFINE_METHOD(RUNTIME_HELPERS,      FINALIZE_TASK_RETURNING_THUNK_1, FinalizeTaskReturningThunk, GM_Continuation_RetTaskOfT)
 DEFINE_METHOD(RUNTIME_HELPERS,      FINALIZE_VALUETASK_RETURNING_THUNK, FinalizeValueTaskReturningThunk, SM_Continuation_RetValueTask)
diff --git a/src/coreclr/vm/ecall.cpp b/src/coreclr/vm/ecall.cpp
index 290d66cf299474..4e67d02a543efa 100644
--- a/src/coreclr/vm/ecall.cpp
+++ b/src/coreclr/vm/ecall.cpp
@@ -96,14 +96,6 @@ void ECall::PopulateManagedStringConstructors()
     INDEBUG(fInitialized = true);
 }
 
-void ECall::PopulateAsyncHelpers()
-{
-    STANDARD_VM_CONTRACT;
-
-    MethodDesc* pMD = CoreLibBinder::GetMethod((BinderMethodID)(METHOD__RUNTIME_HELPERS__ALLOC_CONTINUATION));
-    PCODE pDest = pMD->GetMultiCallableAddrOfCode();
-    SetJitHelperFunction(CORINFO_HELP_ALLOC_CONTINUATION, pDest);
-}
 static CrstStatic gFCallLock;
 
 // This variable is used to force the compiler not to tailcall a function.
diff --git a/src/coreclr/vm/ecall.h b/src/coreclr/vm/ecall.h
index 4ea3a5d5ea922f..f5c8df1b33f999 100644
--- a/src/coreclr/vm/ecall.h
+++ b/src/coreclr/vm/ecall.h
@@ -94,8 +94,6 @@ class ECall
 
         static void PopulateManagedStringConstructors();
 
-        static void PopulateAsyncHelpers();
-
 #ifdef DACCESS_COMPILE
         // Enumerates all gFCallMethods for minidumps.
         static void EnumFCallMethods();
diff --git a/src/coreclr/vm/ecalllist.h b/src/coreclr/vm/ecalllist.h
index 1ea05d6f692d88..fb56e3c5a5cd15 100644
--- a/src/coreclr/vm/ecalllist.h
+++ b/src/coreclr/vm/ecalllist.h
@@ -363,6 +363,7 @@ FCFuncStart(gMethodTableFuncs)
     FCFuncElement("GetNumInstanceFieldBytes", MethodTableNative::GetNumInstanceFieldBytes)
     FCFuncElement("GetPrimitiveCorElementType", MethodTableNative::GetPrimitiveCorElementType)
     FCFuncElement("GetMethodTableMatchingParentClass", MethodTableNative::GetMethodTableMatchingParentClass)
+    FCFuncElement("GetLoaderAllocatorHandle", MethodTableNative::GetLoaderAllocatorHandle)
 FCFuncEnd()
 
 FCFuncStart(gStubHelperFuncs)
diff --git a/src/coreclr/vm/jitinterface.cpp b/src/coreclr/vm/jitinterface.cpp
index 28512bc1728116..dea52c7f7bc975 100644
--- a/src/coreclr/vm/jitinterface.cpp
+++ b/src/coreclr/vm/jitinterface.cpp
@@ -10188,6 +10188,7 @@ void CEEInfo::getAsync2Info(CORINFO_ASYNC2_INFO* pAsync2InfoOut)
     pAsync2InfoOut->continuationFlagsFldHnd = CORINFO_FIELD_HANDLE(CoreLibBinder::GetField(FIELD__CONTINUATION__FLAGS));
     pAsync2InfoOut->continuationDataFldHnd = CORINFO_FIELD_HANDLE(CoreLibBinder::GetField(FIELD__CONTINUATION__DATA));
     pAsync2InfoOut->continuationGCDataFldHnd = CORINFO_FIELD_HANDLE(CoreLibBinder::GetField(FIELD__CONTINUATION__GCDATA));
+    pAsync2InfoOut->continuationsNeedMethodHandle = m_pMethodBeingCompiled->GetLoaderAllocator()->CanUnload();
 }
 
 const char16_t * CEEInfo::getJitTimeLogFilename()
diff --git a/src/tests/async/collectible-alc.cs b/src/tests/async/collectible-alc.cs
new file mode 100644
index 00000000000000..eb7e43309db26a
--- /dev/null
+++ b/src/tests/async/collectible-alc.cs
@@ -0,0 +1,78 @@
+// 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 System.Linq;
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.Loader;
+using System.Threading.Tasks;
+using Xunit;
+
+public class Async2CollectibleAlc
+{
+    [Fact]
+    public static void TestEntryPoint()
+    {
+        AsyncEntryPoint().Wait();
+    }
+
+    private static async2 Task AsyncEntryPoint()
+    {
+        WeakReference wr = await CallFooAsyncAndUnload();
+
+        for (int i = 0; i < 10 && wr.IsAlive; i++)
+        {
+            GC.Collect();
+            GC.WaitForPendingFinalizers();
+        }
+
+        Assert.False(wr.IsAlive);
+    }
+
+    [MethodImpl(MethodImplOptions.NoInlining)]
+    private static async2 Task<WeakReference> CallFooAsyncAndUnload()
+    {
+        TaskCompletionSource tcs = new();
+        (Task<string> task, WeakReference wr) = CallFooAsyncInCollectibleALC(tcs.Task);
+        for (int i = 0; i < 10; i++)
+        {
+            GC.Collect();
+            GC.WaitForPendingFinalizers();
+        }
+
+        Assert.True(wr.IsAlive);
+
+        tcs.SetResult();
+        string result = await task;
+        Assert.Equal("done", result);
+        return wr;
+    }
+
+    [MethodImpl(MethodImplOptions.NoInlining)]
+    private static (Task<string>, WeakReference) CallFooAsyncInCollectibleALC(Task task)
+    {
+        CollectibleALC alc = new CollectibleALC();
+        Assembly asm = alc.LoadFromAssemblyPath(Assembly.GetExecutingAssembly().Location);
+
+        MethodInfo[] mis = asm.GetType(nameof(Async2CollectibleAlc)).GetMethods(BindingFlags.Static | BindingFlags.NonPublic);
+        MethodInfo mi = mis.Single(mi => mi.Name == "FooAsync" && mi.ReturnType == typeof(Task<string>));
+        Task<string> resultTask = (Task<string>)mi.Invoke(null, new object[] { new Task[] { task } });
+        alc.Unload();
+        return (resultTask, new WeakReference(alc, trackResurrection: true));
+    }
+
+    // Task[] to work around a compiler bug
+    private static async2 Task<string> FooAsync(Task[] t)
+    {
+        await t[0];
+        return "done";
+    }
+
+    private class CollectibleALC : AssemblyLoadContext
+    {
+        public CollectibleALC() : base(true)
+        {
+        }
+    }
+}
diff --git a/src/tests/async/collectible-alc.csproj b/src/tests/async/collectible-alc.csproj
new file mode 100644
index 00000000000000..3b3ebe1336c1dd
--- /dev/null
+++ b/src/tests/async/collectible-alc.csproj
@@ -0,0 +1,10 @@
+<Project Sdk="Microsoft.NET.Sdk.IL">
+  <PropertyGroup>
+    <!-- For collectible ALC -->
+    <RequiresProcessIsolation>True</RequiresProcessIsolation>
+    <Optimize>True</Optimize>
+  </PropertyGroup>
+  <ItemGroup>
+    <Compile Include="$(MSBuildProjectName).cs" />
+  </ItemGroup>
+</Project>
diff --git a/src/tests/issues.targets b/src/tests/issues.targets
index 1724254d8ad898..82f08f2743fb0e 100644
--- a/src/tests/issues.targets
+++ b/src/tests/issues.targets
@@ -983,6 +983,9 @@
         <ExcludeList Include="$(XunitTestBinBase)/JIT/Regression/JitBlue/Runtime_66624/Runtime_66624/*">
             <Issue>https://github.com/dotnet/runtimelab/issues/155: Collectible assemblies</Issue>
         </ExcludeList>
+        <ExcludeList Include="$(XunitTestBinBase)/async/collectible-alc/collectible-alc/*">
+            <Issue>https://github.com/dotnet/runtimelab/issues/155: Collectible assemblies</Issue>
+        </ExcludeList>
         <ExcludeList Include="$(XunitTestBinBase)/Loader/ContextualReflection/ContextualReflection/*">
             <Issue>https://github.com/dotnet/runtimelab/issues/165</Issue>
         </ExcludeList>
@@ -3130,6 +3133,9 @@
         <ExcludeList Include="$(XunitTestBinBase)/JIT/Regression/JitBlue/Runtime_66624/Runtime_66624/*">
             <Issue>Loads an assembly from file</Issue>
         </ExcludeList>
+        <ExcludeList Include="$(XunitTestBinBase)/async/collectible-alc/collectible-alc/*">
+            <Issue>Loads an assembly from file</Issue>
+        </ExcludeList>
         <ExcludeList Include="$(XunitTestBinBase)/Loader/StartupHooks/**">
             <Issue>Loads an assembly from file</Issue>
         </ExcludeList>

From d38e9535c63f290146b2cc75da4e776b297cc5e8 Mon Sep 17 00:00:00 2001
From: Jakob Botsch Nielsen <Jakob.botsch.nielsen@gmail.com>
Date: Wed, 4 Dec 2024 10:04:54 +0100
Subject: [PATCH 130/203] [RuntimeAsync] Fix support for pinvokes (#2847)

- Make sure async transformation leaves a scratch BB around for the
  pinvoke prolog to be inserted into
- Make sure we properly insert pinvoke epilogs for `GT_RETURN_SUSPEND`
  nodes (needed on x86)
- Ensure the pinvoke frame related locals are not captured by the async
  transformation
---
 src/coreclr/jit/async.cpp      | 29 ++++++++++++++++++----
 src/coreclr/jit/compiler.cpp   |  2 +-
 src/coreclr/jit/lower.cpp      |  6 ++++-
 src/tests/async/pinvoke.cs     | 44 ++++++++++++++++++++++++++++++++++
 src/tests/async/pinvoke.csproj |  8 +++++++
 5 files changed, 83 insertions(+), 6 deletions(-)
 create mode 100644 src/tests/async/pinvoke.cs
 create mode 100644 src/tests/async/pinvoke.csproj

diff --git a/src/coreclr/jit/async.cpp b/src/coreclr/jit/async.cpp
index c6d7665b1c327a..e0726ccc977e33 100644
--- a/src/coreclr/jit/async.cpp
+++ b/src/coreclr/jit/async.cpp
@@ -60,6 +60,16 @@ class AsyncLiveness
             return true;
         }
 
+        if (lclNum == m_comp->info.compLvFrameListRoot)
+        {
+            return true;
+        }
+
+        if (lclNum == m_comp->lvaInlinedPInvokeFrameVar)
+        {
+            return true;
+        }
+
 #ifdef FEATURE_EH_WINDOWS_X86
         if (lclNum == m_comp->lvaShadowSPslotsVar)
         {
@@ -1296,12 +1306,22 @@ void Async2Transformation::CreateResumptionSwitch()
 {
     BasicBlock* newEntryBB = BasicBlock::New(m_comp, BBJ_ALWAYS);
 
+    bool hadScratchBB = m_comp->fgFirstBBisScratch();
+
     if (m_comp->fgFirstBB->hasProfileWeight())
     {
         newEntryBB->inheritWeight(m_comp->fgFirstBB);
     }
     m_comp->fgFirstBB->bbRefs--;
 
+    if (hadScratchBB)
+    {
+        // If previous first BB was a scratch BB then we have to recreate it
+        // after since later phases may be relying on it.
+        // TODO-Cleanup: The later phases should be creating it if they need it.
+        m_comp->fgFirstBBScratch = nullptr;
+    }
+
     FlowEdge* toPrevEntryEdge = m_comp->fgAddRefPred(m_comp->fgFirstBB, newEntryBB);
     toPrevEntryEdge->setLikelihood(1);
 
@@ -1310,10 +1330,6 @@ void Async2Transformation::CreateResumptionSwitch()
 
     m_comp->fgInsertBBbefore(m_comp->fgFirstBB, newEntryBB);
 
-    // If previous first BB was a scratch BB, then we must add a new scratch BB
-    // to create IR before the switch.
-    m_comp->fgFirstBBScratch = nullptr;
-
     GenTree* continuationArg = m_comp->gtNewLclvNode(m_comp->lvaAsyncContinuationArg, TYP_REF);
     GenTree* null            = m_comp->gtNewNull();
     GenTree* neNull          = m_comp->gtNewOperNode(GT_NE, TYP_INT, continuationArg, null);
@@ -1493,4 +1509,9 @@ void Async2Transformation::CreateResumptionSwitch()
         GenTree* jtrue            = m_comp->gtNewOperNode(GT_JTRUE, TYP_VOID, ltZero);
         LIR::AsRange(checkILOffsetBB).InsertAtEnd(LIR::SeqTree(m_comp, jtrue));
     }
+
+    if (hadScratchBB)
+    {
+        m_comp->fgEnsureFirstBBisScratch();
+    }
 }
diff --git a/src/coreclr/jit/compiler.cpp b/src/coreclr/jit/compiler.cpp
index 3e8ae18bf4a343..dca14dfe0937ad 100644
--- a/src/coreclr/jit/compiler.cpp
+++ b/src/coreclr/jit/compiler.cpp
@@ -3368,7 +3368,7 @@ void Compiler::compInitOptions(JitFlags* jitFlags)
 
         if (compIsAsync2())
         {
-            printf("OPTIONS: compilation is an async2\n");
+            printf("OPTIONS: compilation is an async2 state machine\n");
         }
     }
 #endif
diff --git a/src/coreclr/jit/lower.cpp b/src/coreclr/jit/lower.cpp
index e257751c5afc2d..15eea3d18ad933 100644
--- a/src/coreclr/jit/lower.cpp
+++ b/src/coreclr/jit/lower.cpp
@@ -4888,7 +4888,6 @@ void Lowering::LowerRet(GenTreeOp* ret)
         }
     }
 
-    // Method doing PInvokes has exactly one return block unless it has tail calls.
     if (comp->compMethodRequiresPInvokeFrame())
     {
         InsertPInvokeMethodEpilog(comp->compCurBB DEBUGARG(ret));
@@ -5332,6 +5331,11 @@ void Lowering::LowerReturnSuspend(GenTree* node)
     {
         BlockRange().Remove(BlockRange().LastNode(), true);
     }
+
+    if (comp->compMethodRequiresPInvokeFrame())
+    {
+        InsertPInvokeMethodEpilog(comp->compCurBB DEBUGARG(node));
+    }
 }
 
 //----------------------------------------------------------------------------------------------
diff --git a/src/tests/async/pinvoke.cs b/src/tests/async/pinvoke.cs
new file mode 100644
index 00000000000000..df04091cbaf2ff
--- /dev/null
+++ b/src/tests/async/pinvoke.cs
@@ -0,0 +1,44 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Threading.Tasks;
+using Xunit;
+
+public class Async2PInvoke
+{
+    [Fact]
+    public static void TestEntryPoint()
+    {
+        AsyncEntryPoint().Wait();
+    }
+
+    private static async2 Task AsyncEntryPoint()
+    {
+        unsafe
+        {
+            Assert.Equal(5, GetFPtr()());
+        }
+
+        await Task.Yield();
+
+        unsafe
+        {
+            Assert.Equal(5, GetFPtr()());
+        }
+
+        await Task.Yield();
+
+        unsafe
+        {
+            Assert.Equal(5, GetFPtr()());
+        }
+    }
+
+    [MethodImpl(MethodImplOptions.NoInlining)]
+    private static unsafe delegate* unmanaged<int> GetFPtr() => &GetValue;
+
+    [UnmanagedCallersOnly]
+    private static int GetValue() => 5;
+}
diff --git a/src/tests/async/pinvoke.csproj b/src/tests/async/pinvoke.csproj
new file mode 100644
index 00000000000000..9367a79b2edbb1
--- /dev/null
+++ b/src/tests/async/pinvoke.csproj
@@ -0,0 +1,8 @@
+<Project Sdk="Microsoft.NET.Sdk.IL">
+  <PropertyGroup>
+    <Optimize>True</Optimize>
+  </PropertyGroup>
+  <ItemGroup>
+    <Compile Include="$(MSBuildProjectName).cs" />
+  </ItemGroup>
+</Project>

From d95f6eadeeb696071635d9662fbe33f8a0e0d645 Mon Sep 17 00:00:00 2001
From: Jakob Botsch Nielsen <Jakob.botsch.nielsen@gmail.com>
Date: Thu, 5 Dec 2024 12:45:20 +0100
Subject: [PATCH 131/203] [RuntimeAsync] Fix a couple of cases where x86 would
 overwrite ecx on return (#2854)

shared-generics was not working on x86 because the returned continuation
was being overwritten in one particular case. This fixes the issue.
---
 src/coreclr/jit/codegenxarch.cpp | 12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)

diff --git a/src/coreclr/jit/codegenxarch.cpp b/src/coreclr/jit/codegenxarch.cpp
index eaffa835e1190f..d1f84e7ff682fb 100644
--- a/src/coreclr/jit/codegenxarch.cpp
+++ b/src/coreclr/jit/codegenxarch.cpp
@@ -10000,10 +10000,10 @@ void CodeGen::genFnEpilog(BasicBlock* block)
         if (frameSize > 0)
         {
 #ifdef TARGET_X86
-            /* Add 'compiler->compLclFrameSize' to ESP */
-            /* Use pop ECX to increment ESP by 4, unless compiler->compJmpOpUsed is true */
+            // Add 'compiler->compLclFrameSize' to ESP. Use "pop ECX" for that, except in cases
+            // where ECX may contain some state.
 
-            if ((frameSize == TARGET_POINTER_SIZE) && !compiler->compJmpOpUsed)
+            if ((frameSize == TARGET_POINTER_SIZE) && !compiler->compJmpOpUsed && !compiler->compIsAsync2())
             {
                 inst_RV(INS_pop, REG_ECX, TYP_I_IMPL);
                 regSet.verifyRegUsed(REG_ECX);
@@ -10011,8 +10011,8 @@ void CodeGen::genFnEpilog(BasicBlock* block)
             else
 #endif // TARGET_X86
             {
-                /* Add 'compiler->compLclFrameSize' to ESP */
-                /* Generate "add esp, <stack-size>" */
+                // Add 'compiler->compLclFrameSize' to ESP
+                // Generate "add esp, <stack-size>"
                 inst_RV_IV(INS_add, REG_SPBASE, frameSize, EA_PTRSIZE);
             }
         }
@@ -10090,7 +10090,7 @@ void CodeGen::genFnEpilog(BasicBlock* block)
                 // do nothing before popping the callee-saved registers
             }
 #ifdef TARGET_X86
-            else if (compiler->compLclFrameSize == REGSIZE_BYTES)
+            else if ((compiler->compLclFrameSize == REGSIZE_BYTES) && !compiler->compJmpOpUsed && !compiler->compIsAsync2())
             {
                 // "pop ecx" will make ESP point to the callee-saved registers
                 inst_RV(INS_pop, REG_ECX, TYP_I_IMPL);

From 7624a38ae9820b8c034818c5fbef30f09c4a4ab1 Mon Sep 17 00:00:00 2001
From: Vladimir Sadov <vsadov@microsoft.com>
Date: Fri, 13 Dec 2024 19:21:25 -0800
Subject: [PATCH 132/203] [RuntimeAsync] Switch the implementation to the new
 shape of runtime async methods. (#2818)

* update Roslyn ref to one that uses new IL shape

* part1

* IsAsyncThunkMethod --> IsAsyncHelperMethod

* reintroduce IsAsyncThunkMethod

* fix shared generic test

* fix after rebasing

* typo

* clarified a comment

* PR feedback

Co-authored-by: Jakob Botsch Nielsen <Jakob.botsch.nielsen@gmail.com>

* switch to a newer Roslyn prototype

* workaround - moved `GC.Collect/WFPF` into a noinline helper.

* PR feedback

* Revert "workaround - moved `GC.Collect/WFPF` into a noinline helper."

This reverts commit ef4580304b1b9b7ac49a5af09e4d4cb1112ec042.

* reenable tiering for async methods

* more  isThunk-->isHelper  renames

* fixes tiered compilation

* switch to a new ROslyn SHA

* A few fixes after updating to new Roslyn compiler

* add a testcase for PGO/OSR

* PR feedback

* file rename

* 4 iterations / 100 msec delay

---------

Co-authored-by: Jakob Botsch Nielsen <Jakob.botsch.nielsen@gmail.com>
---
 buildroslynnugets.cmd                         |   4 +-
 eng/Versions.props                            |   6 +-
 src/coreclr/inc/corcompile.h                  |   2 +-
 src/coreclr/inc/corhdr.h                      |   5 +-
 src/coreclr/vm/class.cpp                      |   4 +-
 src/coreclr/vm/clrtocomcall.cpp               |   4 +-
 src/coreclr/vm/clsload.cpp                    |   2 +-
 src/coreclr/vm/codeman.cpp                    |   1 +
 src/coreclr/vm/commodule.cpp                  |   2 +-
 src/coreclr/vm/commtmemberinfomap.cpp         |   4 +-
 src/coreclr/vm/comtoclrcall.cpp               |   2 +-
 src/coreclr/vm/dispatchinfo.cpp               |   6 +-
 src/coreclr/vm/dllimport.cpp                  |  14 +-
 src/coreclr/vm/encee.cpp                      |   2 +-
 src/coreclr/vm/genericdict.cpp                |  14 +-
 src/coreclr/vm/genmeth.cpp                    |  30 ++---
 src/coreclr/vm/instmethhash.cpp               |   6 +-
 src/coreclr/vm/instmethhash.h                 |   2 +-
 src/coreclr/vm/interoputil.cpp                |   4 +-
 src/coreclr/vm/jitinterface.cpp               |   4 +-
 src/coreclr/vm/method.cpp                     |   8 +-
 src/coreclr/vm/method.hpp                     |  67 +++++----
 src/coreclr/vm/methoditer.cpp                 |   4 +-
 src/coreclr/vm/methodtable.cpp                |   8 +-
 src/coreclr/vm/methodtablebuilder.cpp         | 127 ++++++++----------
 src/coreclr/vm/methodtablebuilder.h           |  24 ++--
 src/coreclr/vm/multicorejit.cpp               |   4 +-
 src/coreclr/vm/prestub.cpp                    |  50 +++++--
 src/coreclr/vm/readytoruninfo.cpp             |   6 +-
 src/coreclr/vm/zapsig.cpp                     |  10 +-
 .../Nrbf/ArraySinglePrimitiveRecord.cs        |  10 +-
 .../src/System/Numerics/BigInteger.cs         |   2 +-
 .../Json/Document/JsonDocument.MetadataDb.cs  |   2 +-
 src/tests/async/pgo.cs                        |  55 ++++++++
 src/tests/async/pgo.csproj                    |   8 ++
 35 files changed, 302 insertions(+), 201 deletions(-)
 create mode 100644 src/tests/async/pgo.cs
 create mode 100644 src/tests/async/pgo.csproj

diff --git a/buildroslynnugets.cmd b/buildroslynnugets.cmd
index 2358e7f4a9f02f..281ddee98868f3 100644
--- a/buildroslynnugets.cmd
+++ b/buildroslynnugets.cmd
@@ -1,7 +1,7 @@
 setlocal ENABLEEXTENSIONS
 pushd %~dp0
-set ASYNC_ROSLYN_COMMIT=fa251ef06afcc9d1c79761a112b89c6341c0ccfa
-set ASYNC_SUFFIX=async-10
+set ASYNC_ROSLYN_COMMIT=10a5611cb10cd64876a2638664f0740255197e1b
+set ASYNC_SUFFIX=async-11
 set ASYNC_ROSLYN_BRANCH=demos/async2-experiment1
 
 cd ..
diff --git a/eng/Versions.props b/eng/Versions.props
index b573c01eba46d6..6b5bef1844e2b5 100644
--- a/eng/Versions.props
+++ b/eng/Versions.props
@@ -43,9 +43,9 @@
       Any tools that contribute to the design-time experience should use the MicrosoftCodeAnalysisVersion_LatestVS property above to ensure
       they do not break the local dev experience.
     -->
-    <MicrosoftCodeAnalysisCSharpVersion>4.13.0-async-10</MicrosoftCodeAnalysisCSharpVersion>
-    <MicrosoftCodeAnalysisVersion>4.13.0-async-10</MicrosoftCodeAnalysisVersion>
-    <MicrosoftNetCompilersToolsetVersion>4.13.0-async-10</MicrosoftNetCompilersToolsetVersion>
+    <MicrosoftCodeAnalysisCSharpVersion>4.13.0-async-11</MicrosoftCodeAnalysisCSharpVersion>
+    <MicrosoftCodeAnalysisVersion>4.13.0-async-11</MicrosoftCodeAnalysisVersion>
+    <MicrosoftNetCompilersToolsetVersion>4.13.0-async-11</MicrosoftNetCompilersToolsetVersion>
   </PropertyGroup>
   <!--
     For source generator support we need to target multiple versions of Roslyn in order to be able to run on older versions of Roslyn.
diff --git a/src/coreclr/inc/corcompile.h b/src/coreclr/inc/corcompile.h
index 742b05e385d95c..bef0f9812fc6a7 100644
--- a/src/coreclr/inc/corcompile.h
+++ b/src/coreclr/inc/corcompile.h
@@ -180,7 +180,7 @@ enum EncodeMethodSigFlags
     ENCODE_METHOD_SIG_Constrained               = 0x20,
     ENCODE_METHOD_SIG_OwnerType                 = 0x40,
     ENCODE_METHOD_SIG_UpdateContext             = 0x80,
-    ENCODE_METHOD_SIG_AsyncThunk                = 0x100,
+    ENCODE_METHOD_SIG_AsyncHelper               = 0x100,
 };
 
 enum EncodeFieldSigFlags
diff --git a/src/coreclr/inc/corhdr.h b/src/coreclr/inc/corhdr.h
index b0e257a8469b24..327134e72654e0 100644
--- a/src/coreclr/inc/corhdr.h
+++ b/src/coreclr/inc/corhdr.h
@@ -648,13 +648,15 @@ typedef enum CorMethodImpl
     miNoOptimization     =   0x0040,   // Method may not be optimized.
     miAggressiveOptimization = 0x0200, // Method may contain hot code and should be aggressively optimized.
 
+    miAsync              =   0x0400,   // Method requires async state machine rewrite.
+
     // These are the flags that are allowed in MethodImplAttribute's Value
     // property. This should include everything above except the code impl
     // flags (which are used for MethodImplAttribute's MethodCodeType field).
     miUserMask           =   miManagedMask | miForwardRef | miPreserveSig |
                              miInternalCall | miSynchronized |
                              miNoInlining | miAggressiveInlining |
-                             miNoOptimization | miAggressiveOptimization,
+                             miNoOptimization | miAggressiveOptimization | miAsync,
 
     miMaxMethodImplVal   =   0xffff,   // Range check value
 } CorMethodImpl;
@@ -678,6 +680,7 @@ typedef enum CorMethodImpl
 #define IsMiAggressiveInlining(x)           ((x) & miAggressiveInlining)
 #define IsMiNoOptimization(x)               ((x) & miNoOptimization)
 #define IsMiAggressiveOptimization(x)       (((x) & (miAggressiveOptimization | miNoOptimization)) == miAggressiveOptimization)
+#define IsMiAsync(x)                        ((x) & miAsync)
 
 // PinvokeMap attr bits, used by DefinePinvokeMap.
 typedef enum  CorPinvokeMap
diff --git a/src/coreclr/vm/class.cpp b/src/coreclr/vm/class.cpp
index 94022cefc2ed23..d5c72a13ff2678 100644
--- a/src/coreclr/vm/class.cpp
+++ b/src/coreclr/vm/class.cpp
@@ -741,7 +741,7 @@ HRESULT EEClass::AddMethodDesc(
                                                             classification,
                                                             TRUE, // fNonVtableSlot
                                                             TRUE, // fNativeCodeSlot
-                                                            FALSE, /* IsAsyncThunkMethod */
+                                                            FALSE, /* HasAsyncMethodData */
                                                             pMT,
                                                             &dummyAmTracker);
 
@@ -791,7 +791,7 @@ HRESULT EEClass::AddMethodDesc(
                                 pImport,
                                 NULL,
                                 Signature(),
-                                AsyncMethodType::NotAsync
+                                AsyncMethodKind::NotAsync
                                 COMMA_INDEBUG(debug_szMethodName)
                                 COMMA_INDEBUG(pMT->GetDebugClassName())
                                 COMMA_INDEBUG(NULL)
diff --git a/src/coreclr/vm/clrtocomcall.cpp b/src/coreclr/vm/clrtocomcall.cpp
index d8fedc361ed4b5..74a625ba5798cc 100644
--- a/src/coreclr/vm/clrtocomcall.cpp
+++ b/src/coreclr/vm/clrtocomcall.cpp
@@ -230,7 +230,7 @@ I4ARRAYREF SetUpWrapperInfo(MethodDesc *pMD)
 
         GCX_PREEMP();
 
-        if (pMD->IsAsyncThunkMethod())
+        if (pMD->IsAsyncHelperMethod())
         {
             ThrowHR(COR_E_NOTSUPPORTED);
         }
@@ -509,7 +509,7 @@ UINT32 CLRToCOMLateBoundWorker(
     LPCUTF8 strMemberName;
     ULONG uSemantic;
 
-    if (pItfMD->IsAsyncThunkMethod())
+    if (pItfMD->IsAsyncHelperMethod())
     {
         ThrowHR(COR_E_NOTSUPPORTED);
     }
diff --git a/src/coreclr/vm/clsload.cpp b/src/coreclr/vm/clsload.cpp
index 4a2d5321c6941d..3aaf83e0bb42da 100644
--- a/src/coreclr/vm/clsload.cpp
+++ b/src/coreclr/vm/clsload.cpp
@@ -2802,7 +2802,7 @@ TypeHandle ClassLoader::PublishType(const TypeKey *pTypeKey, TypeHandle typeHnd)
         {
             MethodDesc * pMD = it.GetMethodDesc();
             CONSISTENCY_CHECK(pMD != NULL && pMD->GetMethodTable() == pMT);
-            if (!pMD->IsUnboxingStub() && !pMD->IsAsyncThunkMethod())
+            if (!pMD->IsUnboxingStub() && !pMD->IsAsyncHelperMethod())
             {
                 pModule->EnsuredStoreMethodDef(pMD->GetMemberDef(), pMD);
             }
diff --git a/src/coreclr/vm/codeman.cpp b/src/coreclr/vm/codeman.cpp
index fa89ace071698c..dd80c94b611fac 100644
--- a/src/coreclr/vm/codeman.cpp
+++ b/src/coreclr/vm/codeman.cpp
@@ -3892,6 +3892,7 @@ BOOL EEJitManager::JitCodeToMethodInfo(
 
     if (ppMethodDesc)
     {
+
         *ppMethodDesc = pCHdr->GetMethodDesc();
     }
     return TRUE;
diff --git a/src/coreclr/vm/commodule.cpp b/src/coreclr/vm/commodule.cpp
index 2a22573f9167d5..62df7fcd63b0c8 100644
--- a/src/coreclr/vm/commodule.cpp
+++ b/src/coreclr/vm/commodule.cpp
@@ -302,7 +302,7 @@ extern "C" INT32 QCALLTYPE ModuleBuilder_GetMemberRefOfMethodInfo(QCall::ModuleH
         COMPlusThrow(kNotSupportedException);
     }
 
-    if ((pMeth->GetMethodTable()->GetModule() == pModule) && !pMeth->IsAsyncThunkMethod())
+    if ((pMeth->GetMethodTable()->GetModule() == pModule) && !pMeth->IsAsyncHelperMethod())
     {
         // If the passed in method is defined in the same module, just return the MethodDef token
         memberRefE = pMeth->GetMemberDef();
diff --git a/src/coreclr/vm/commtmemberinfomap.cpp b/src/coreclr/vm/commtmemberinfomap.cpp
index b8e86c61b70f91..6c46df238c5528 100644
--- a/src/coreclr/vm/commtmemberinfomap.cpp
+++ b/src/coreclr/vm/commtmemberinfomap.cpp
@@ -688,7 +688,7 @@ void ComMTMemberInfoMap::GetMethodPropsForMeth(
     // Generally don't munge function into a getter.
     rProps[ix].bFunction2Getter = FALSE;
 
-    if (pMeth->IsAsyncThunkMethod())
+    if (pMeth->IsAsyncHelperMethod())
         ThrowHR(COR_E_NOTSUPPORTED);
 
     // See if there is property information for this member.
@@ -1607,7 +1607,7 @@ void ComMTMemberInfoMap::PopulateMemberHashtable()
 
             // We are dealing with a method.
             MethodDesc *pMD = pProps->pMeth;
-            if (pMD->IsAsyncThunkMethod())
+            if (pMD->IsAsyncHelperMethod())
             {
                 ThrowHR(COR_E_NOTSUPPORTED); // Probably this isn't right, and instead should be a skip, but a throw makes it easier to find if this is wrong
             }
diff --git a/src/coreclr/vm/comtoclrcall.cpp b/src/coreclr/vm/comtoclrcall.cpp
index 492adb3a441f66..f2c934df0879d0 100644
--- a/src/coreclr/vm/comtoclrcall.cpp
+++ b/src/coreclr/vm/comtoclrcall.cpp
@@ -987,7 +987,7 @@ void ComCallMethodDesc::InitNativeInfo()
 
             MethodTable * pMT = pMD->GetMethodTable();
             IMDInternalImport * pInternalImport = pMT->GetMDImport();
-            if (pMD->IsAsyncThunkMethod())
+            if (pMD->IsAsyncHelperMethod())
             {
                 ThrowHR(COR_E_NOTSUPPORTED);
             }
diff --git a/src/coreclr/vm/dispatchinfo.cpp b/src/coreclr/vm/dispatchinfo.cpp
index 09df94489debe3..52901c2a525787 100644
--- a/src/coreclr/vm/dispatchinfo.cpp
+++ b/src/coreclr/vm/dispatchinfo.cpp
@@ -449,7 +449,7 @@ ComMTMethodProps * DispatchMemberInfo::GetMemberProps(OBJECTREF MemberInfoObj, C
             MethodDesc* pMeth = (MethodDesc*) getMethodHandle.Call_RetLPVOID(&GetMethodHandleArg);
             if (pMeth)
             {
-                if (pMeth->IsAsyncThunkMethod())
+                if (pMeth->IsAsyncHelperMethod())
                 {
                     ThrowHR(COR_E_NOTSUPPORTED);
                 }
@@ -850,7 +850,7 @@ void DispatchMemberInfo::SetUpMethodMarshalerInfo(MethodDesc *pMD, BOOL bReturnV
     //
     // Initialize the parameter definition enum.
     //
-    if (pMD->IsAsyncThunkMethod())
+    if (pMD->IsAsyncHelperMethod())
     {
         ThrowHR(COR_E_NOTSUPPORTED);
     }
@@ -2588,7 +2588,7 @@ bool DispatchInfo::IsPropertyAccessorVisible(bool fIsSetter, OBJECTREF* pMemberI
 
         // Check to see if the new method is a property accessor.
         mdToken tkMember = mdTokenNil;
-        if (pMDForProperty->IsAsyncThunkMethod())
+        if (pMDForProperty->IsAsyncHelperMethod())
         {
             return false;
         }
diff --git a/src/coreclr/vm/dllimport.cpp b/src/coreclr/vm/dllimport.cpp
index 59a35a32e48b2c..2d80d492b8867a 100644
--- a/src/coreclr/vm/dllimport.cpp
+++ b/src/coreclr/vm/dllimport.cpp
@@ -105,7 +105,7 @@ StubSigDesc::StubSigDesc(MethodDesc *pMD)
     m_sig           = pMD->GetSignature();
     m_pModule       = pMD->GetModule();         // Used for token resolution.
 
-    _ASSERTE(!pMD->IsAsyncThunkMethod());
+    _ASSERTE(!pMD->IsAsyncHelperMethod());
 
     m_tkMethodDef = pMD->GetMemberDef();
     SigTypeContext::InitTypeContext(pMD, &m_typeContext);
@@ -134,7 +134,7 @@ StubSigDesc::StubSigDesc(MethodDesc* pMD, const Signature& sig, Module* pModule,
 
     if (pMD != NULL)
     {
-        _ASSERTE(!pMD->IsAsyncThunkMethod());
+        _ASSERTE(!pMD->IsAsyncHelperMethod());
         m_tkMethodDef = pMD->GetMemberDef();
         SigTypeContext::InitTypeContext(pMD, &m_typeContext);
         m_pMetadataModule = pMD->GetModule();
@@ -1090,7 +1090,7 @@ class ILStubState : public StubState
         DWORD dwToken = 0;
         if (pTargetMD)
         {
-            _ASSERTE(!pTargetMD->IsAsyncThunkMethod());
+            _ASSERTE(!pTargetMD->IsAsyncHelperMethod());
             dwToken = pTargetMD->GetMemberDef();
         }
 
@@ -2716,7 +2716,7 @@ void PInvokeStaticSigInfo::DllImportInit(
     IMDInternalImport  *pInternalImport = pMD->GetMDImport();
     CorPinvokeMap mappingFlags = pmMaxValue;
     mdModuleRef modref = mdModuleRefNil;
-    if (pMD->IsAsyncThunkMethod())
+    if (pMD->IsAsyncHelperMethod())
     {
         ThrowHR(COR_E_NOTSUPPORTED);
     }
@@ -2997,7 +2997,7 @@ namespace
         CorInfoCallConvExtension callConvLocal;
         IMDInternalImport* pInternalImport = pMD->GetMDImport();
         CorPinvokeMap mappingFlags = pmMaxValue;
-        if (pMD->IsAsyncThunkMethod())
+        if (pMD->IsAsyncHelperMethod())
         {
             ThrowHR(COR_E_NOTSUPPORTED);
         }
@@ -3263,7 +3263,7 @@ BOOL NDirect::MarshalingRequired(
     mdMethodDef methodToken = mdMethodDefNil;
     if (pMD != NULL)
     {
-        if (pMD->IsAsyncThunkMethod())
+        if (pMD->IsAsyncHelperMethod())
         {
             ThrowHR(COR_E_NOTSUPPORTED);
         }
@@ -6061,7 +6061,7 @@ PCODE GetILStubForCalli(VASigCookie *pVASigCookie, MethodDesc *pMD)
     {
         PInvokeStaticSigInfo sigInfo(pMD);
 
-        if (pMD->IsAsyncThunkMethod())
+        if (pMD->IsAsyncHelperMethod())
         {
             ThrowHR(COR_E_NOTSUPPORTED);
         }
diff --git a/src/coreclr/vm/encee.cpp b/src/coreclr/vm/encee.cpp
index fb807bdaa10512..338a42df7fcd15 100644
--- a/src/coreclr/vm/encee.cpp
+++ b/src/coreclr/vm/encee.cpp
@@ -342,7 +342,7 @@ HRESULT EditAndContinueModule::UpdateMethod(MethodDesc *pMethod)
             AppDomain::GetCurrentDomain(),
             module,
             tkMethod,
-            pMethod->IsAsyncThunkMethod(),
+            pMethod->IsAsyncHelperMethod(),
             AssemblyIterationFlags(kIncludeLoaded | kIncludeExecution));
         CollectibleAssemblyHolder<Assembly *> pAssembly;
         while (it.Next(pAssembly.This()))
diff --git a/src/coreclr/vm/genericdict.cpp b/src/coreclr/vm/genericdict.cpp
index 69325d4831cada..37eeecb252911d 100644
--- a/src/coreclr/vm/genericdict.cpp
+++ b/src/coreclr/vm/genericdict.cpp
@@ -889,7 +889,7 @@ Dictionary::PopulateEntry(
 
             uint32_t methodSlot = -1;
             BOOL fRequiresDispatchStub = 0;
-            BOOL isAsyncThunk = 0;
+            BOOL isAsyncHelper = 0;
 
             if (isReadyToRunModule)
             {
@@ -901,7 +901,7 @@ Dictionary::PopulateEntry(
                 isInstantiatingStub = ((methodFlags & ENCODE_METHOD_SIG_InstantiatingStub) != 0) || (kind == MethodEntrySlot);
                 isUnboxingStub = ((methodFlags & ENCODE_METHOD_SIG_UnboxingStub) != 0);
                 fMethodNeedsInstantiation = ((methodFlags & ENCODE_METHOD_SIG_MethodInstantiation) != 0);
-                isAsyncThunk = ((methodFlags & ENCODE_METHOD_SIG_AsyncThunk) != 0);
+                isAsyncHelper = ((methodFlags & ENCODE_METHOD_SIG_AsyncHelper) != 0);
 
                 if (methodFlags & ENCODE_METHOD_SIG_OwnerType)
                 {
@@ -952,7 +952,7 @@ Dictionary::PopulateEntry(
                         _ASSERTE(pZapSigContext->pInfoModule->IsFullModule());
                         pMethod = MemberLoader::GetMethodDescFromMethodDef(static_cast<Module*>(pZapSigContext->pInfoModule), TokenFromRid(rid, mdtMethodDef), FALSE);
                     }
-                    if (isAsyncThunk)
+                    if (isAsyncHelper)
                     {
                         pMethod = pMethod->GetAsyncOtherVariant();
                     }
@@ -999,7 +999,7 @@ Dictionary::PopulateEntry(
                 isInstantiatingStub = ((methodFlags & ENCODE_METHOD_SIG_InstantiatingStub) != 0);
                 isUnboxingStub = ((methodFlags & ENCODE_METHOD_SIG_UnboxingStub) != 0);
                 fMethodNeedsInstantiation = ((methodFlags & ENCODE_METHOD_SIG_MethodInstantiation) != 0);
-                isAsyncThunk = ((methodFlags & ENCODE_METHOD_SIG_AsyncThunk) != 0);
+                isAsyncHelper = ((methodFlags & ENCODE_METHOD_SIG_AsyncHelper) != 0);
 
                 if ((methodFlags & ENCODE_METHOD_SIG_SlotInsteadOfToken) != 0)
                 {
@@ -1042,7 +1042,7 @@ Dictionary::PopulateEntry(
                     // The RID map should have been filled out if we fully loaded the class
                     pMethod = pMethodDefMT->GetModule()->LookupMethodDef(token);
 
-                    if (isAsyncThunk)
+                    if (isAsyncHelper)
                     {
                         pMethod = pMethod->GetAsyncOtherVariant();
                     }
@@ -1080,7 +1080,7 @@ Dictionary::PopulateEntry(
                 break;
             }
 
-            _ASSERTE((!!isAsyncThunk) == pMethod->IsAsyncThunkMethod());
+            _ASSERTE((!!isAsyncHelper) == pMethod->IsAsyncHelperMethod());
 
             Instantiation inst;
 
@@ -1126,7 +1126,7 @@ Dictionary::PopulateEntry(
                 inst,
                 (!isInstantiatingStub && !isUnboxingStub));
 
-            _ASSERTE((!!isAsyncThunk) == pMethod->IsAsyncThunkMethod());
+            _ASSERTE((!!isAsyncHelper) == pMethod->IsAsyncHelperMethod());
 
             if (kind == ConstrainedMethodEntrySlot)
             {
diff --git a/src/coreclr/vm/genmeth.cpp b/src/coreclr/vm/genmeth.cpp
index d31016d34e4b8c..518b7e4e2eaf50 100644
--- a/src/coreclr/vm/genmeth.cpp
+++ b/src/coreclr/vm/genmeth.cpp
@@ -246,7 +246,7 @@ static MethodDesc * FindTightlyBoundWrappedMethodDesc_DEBUG(MethodDesc * pMD)
 
     mdMethodDef methodDef = pMD->GetMemberDef();
     Module *pModule = pMD->GetModule();
-    bool isAsyncThunkMethod = pMD->IsAsyncThunkMethod();
+    bool isAsyncHelperMethod = pMD->IsAsyncHelperMethod();
 
     MethodTable::MethodIterator it(pMD->GetCanonicalMethodTable());
     it.MoveToEnd();
@@ -258,7 +258,7 @@ static MethodDesc * FindTightlyBoundWrappedMethodDesc_DEBUG(MethodDesc * pMD)
             if (pCurMethod && !pCurMethod->IsUnboxingStub()) {
                 if ((pCurMethod->GetMemberDef() == methodDef)  &&
                     (pCurMethod->GetModule() == pModule) &&
-                    (pCurMethod->IsAsyncThunkMethod() == isAsyncThunkMethod))
+                    (pCurMethod->IsAsyncHelperMethod() == isAsyncHelperMethod))
                 {
                     return pCurMethod;
                 }
@@ -286,7 +286,7 @@ static MethodDesc * FindTightlyBoundUnboxingStub_DEBUG(MethodDesc * pMD)
 
     mdMethodDef methodDef = pMD->GetMemberDef();
     Module *pModule = pMD->GetModule();
-    bool isAsyncThunkMethod = pMD->IsAsyncThunkMethod();
+    bool isAsyncHelperMethod = pMD->IsAsyncHelperMethod();
 
     MethodTable::MethodIterator it(pMD->GetCanonicalMethodTable());
     it.MoveToEnd();
@@ -296,7 +296,7 @@ static MethodDesc * FindTightlyBoundUnboxingStub_DEBUG(MethodDesc * pMD)
             if (pCurMethod && pCurMethod->IsUnboxingStub()) {
                 if ((pCurMethod->GetMemberDef() == methodDef) &&
                     (pCurMethod->GetModule() == pModule) &&
-                    (pCurMethod->IsAsyncThunkMethod() == isAsyncThunkMethod)) {
+                    (pCurMethod->IsAsyncHelperMethod() == isAsyncHelperMethod)) {
                     return pCurMethod;
                 }
             }
@@ -363,7 +363,7 @@ InstantiatedMethodDesc::NewInstantiatedMethodDesc(MethodTable *pExactMT,
                                                   pGenericMDescInRepMT->GetMemberDef(),
                                                   methodInst,
                                                   getWrappedCode,
-                                                  pGenericMDescInRepMT->IsAsyncThunkMethod());
+                                                  pGenericMDescInRepMT->IsAsyncHelperMethod());
 
         // Crst goes out of scope here
         // We don't need to hold the crst while we build the MethodDesc, but we reacquire it later
@@ -484,7 +484,7 @@ InstantiatedMethodDesc::NewInstantiatedMethodDesc(MethodTable *pExactMT,
                                                       pGenericMDescInRepMT->GetMemberDef(),
                                                       methodInst,
                                                       getWrappedCode,
-                                                      pGenericMDescInRepMT->IsAsyncThunkMethod());
+                                                      pGenericMDescInRepMT->IsAsyncHelperMethod());
 
             if (pOldMD == NULL)
             {
@@ -559,7 +559,7 @@ InstantiatedMethodDesc::FindOrCreateExactClassMethod(MethodTable *pExactMT,
                                                                        pCanonicalMD->GetMemberDef(),
                                                                        Instantiation(),
                                                                        FALSE,
-                                                                       pCanonicalMD->IsAsyncThunkMethod());
+                                                                       pCanonicalMD->IsAsyncHelperMethod());
 
     if (pInstMD == NULL)
     {
@@ -895,7 +895,7 @@ MethodDesc::FindOrCreateAssociatedMethodDesc(MethodDesc* pDefMD,
                                                TRUE /* forceBoxedEntryPoint */,
                                                Instantiation(),
                                                FALSE /* no inst param */,
-                                               pMDescInCanonMT->IsAsyncThunkMethod());
+                                               pMDescInCanonMT->IsAsyncHelperMethod());
 
             // If we didn't find it then create it...
             if (!pResultMD)
@@ -914,7 +914,7 @@ MethodDesc::FindOrCreateAssociatedMethodDesc(MethodDesc* pDefMD,
                                                    TRUE,
                                                    Instantiation(),
                                                    FALSE,
-                                                   pMDescInCanonMT->IsAsyncThunkMethod());
+                                                   pMDescInCanonMT->IsAsyncHelperMethod());
                 if (pResultMD == NULL)
                 {
                     AllocMemTracker amt;
@@ -961,7 +961,7 @@ MethodDesc::FindOrCreateAssociatedMethodDesc(MethodDesc* pDefMD,
                                                TRUE, /* forceBoxedEntryPoint */
                                                methodInst,
                                                FALSE /* no inst param */,
-                                               pMDescInCanonMT->IsAsyncThunkMethod());
+                                               pMDescInCanonMT->IsAsyncHelperMethod());
 
             if (!pResultMD)
             {
@@ -983,7 +983,7 @@ MethodDesc::FindOrCreateAssociatedMethodDesc(MethodDesc* pDefMD,
                 _ASSERTE(pNonUnboxingStub->GetClassification() == mcInstantiated);
                 _ASSERTE(!pNonUnboxingStub->RequiresInstArg());
                 _ASSERTE(!pNonUnboxingStub->IsUnboxingStub());
-                _ASSERTE(pNonUnboxingStub->IsAsyncThunkMethod() == pMDescInCanonMT->IsAsyncThunkMethod());
+                _ASSERTE(pNonUnboxingStub->IsAsyncHelperMethod() == pMDescInCanonMT->IsAsyncHelperMethod());
 
                 // Enter the critical section *after* we've found or created the non-unboxing instantiating stub (else we'd have a race,
                 // and its possible that the non-unboxing instantiating stub may be in a different loader module than pLoaderModule
@@ -996,7 +996,7 @@ MethodDesc::FindOrCreateAssociatedMethodDesc(MethodDesc* pDefMD,
                                                    TRUE, /* forceBoxedEntryPoint */
                                                    methodInst,
                                                    FALSE /* no inst param */,
-                                                   pNonUnboxingStub->IsAsyncThunkMethod());
+                                                   pNonUnboxingStub->IsAsyncHelperMethod());
 
                 if (pResultMD == NULL)
                 {
@@ -1138,7 +1138,7 @@ MethodDesc::FindOrCreateAssociatedMethodDesc(MethodDesc* pDefMD,
                                                                          methodDef,
                                                                          Instantiation(repInst, methodInst.GetNumArgs()),
                                                                          TRUE,
-                                                                         pMDescInCanonMT->IsAsyncThunkMethod());
+                                                                         pMDescInCanonMT->IsAsyncHelperMethod());
 
             // No - so create one.
             if (pInstMD == NULL)
@@ -1163,7 +1163,7 @@ MethodDesc::FindOrCreateAssociatedMethodDesc(MethodDesc* pDefMD,
                                                                          methodDef,
                                                                          methodInst,
                                                                          FALSE,
-                                                                         pMDescInCanonMT->IsAsyncThunkMethod());
+                                                                         pMDescInCanonMT->IsAsyncHelperMethod());
 
             // No - so create one.  Go fetch the shared one first
             if (pInstMD == NULL)
@@ -1204,7 +1204,7 @@ MethodDesc::FindOrCreateAssociatedMethodDesc(MethodDesc* pDefMD,
                                                                          methodDef,
                                                                          methodInst,
                                                                          FALSE,
-                                                                         pMDescInCanonMT->IsAsyncThunkMethod());
+                                                                         pMDescInCanonMT->IsAsyncHelperMethod());
 
             // No - so create one.
             if (pInstMD == NULL)
diff --git a/src/coreclr/vm/instmethhash.cpp b/src/coreclr/vm/instmethhash.cpp
index 853b7dd9e4e4d6..6087a5100b4df6 100644
--- a/src/coreclr/vm/instmethhash.cpp
+++ b/src/coreclr/vm/instmethhash.cpp
@@ -119,7 +119,7 @@ MethodDesc* InstMethodHashTable::FindMethodDesc(TypeHandle declaringType,
                                                 BOOL unboxingStub,
                                                 Instantiation inst,
                                                 BOOL getSharedNotStub,
-                                                bool isAsyncThunk)
+                                                bool isAsyncHelper)
 {
     CONTRACTL
     {
@@ -161,7 +161,7 @@ MethodDesc* InstMethodHashTable::FindMethodDesc(TypeHandle declaringType,
             continue;  // Next iteration of the for loop
         }
 
-        if (pMD->IsAsyncThunkMethod() != isAsyncThunk)
+        if (pMD->IsAsyncHelperMethod() != isAsyncHelper)
         {
             continue;
         }
@@ -208,7 +208,7 @@ BOOL InstMethodHashTable::ContainsMethodDesc(MethodDesc* pMD)
 
     return FindMethodDesc(
         pMD->GetMethodTable(), pMD->GetMemberDef(), pMD->IsUnboxingStub(),
-        pMD->GetMethodInstantiation(), pMD->RequiresInstArg(), pMD->IsAsyncThunkMethod()) != NULL;
+        pMD->GetMethodInstantiation(), pMD->RequiresInstArg(), pMD->IsAsyncHelperMethod()) != NULL;
 }
 
 #endif // #ifndef DACCESS_COMPILE
diff --git a/src/coreclr/vm/instmethhash.h b/src/coreclr/vm/instmethhash.h
index 680070ed6e4f91..66d6a1d8da3b91 100644
--- a/src/coreclr/vm/instmethhash.h
+++ b/src/coreclr/vm/instmethhash.h
@@ -110,7 +110,7 @@ class InstMethodHashTable : public DacEnumerableHashTable<InstMethodHashTable, I
                                BOOL unboxingStub,
                                Instantiation inst,
                                BOOL getSharedNotStub,
-                               bool isAsyncThunk);
+                               bool isAsyncHelper);
 
     BOOL ContainsMethodDesc(MethodDesc* pMD);
 
diff --git a/src/coreclr/vm/interoputil.cpp b/src/coreclr/vm/interoputil.cpp
index 0f3eb8aad54b0f..970cb9137ef122 100644
--- a/src/coreclr/vm/interoputil.cpp
+++ b/src/coreclr/vm/interoputil.cpp
@@ -2301,7 +2301,7 @@ ULONG GetStringizedClassItfDef(TypeHandle InterfaceType, CQuickArray<BYTE> &rDef
                 {
                     pDeclaringMT = pProps->pMeth->GetMethodTable();
                     tkMb = pProps->pMeth->GetMemberDef();
-                    if (pProps->pMeth->IsAsyncThunkMethod())
+                    if (pProps->pMeth->IsAsyncHelperMethod())
                     {
                         ThrowHR(COR_E_NOTSUPPORTED);
                     }
@@ -2515,7 +2515,7 @@ BOOL IsMethodVisibleFromCom(MethodDesc *pMD)
     mdProperty  pd;
     LPCUTF8     pPropName;
     ULONG       uSemantic;
-    if (pMD->IsAsyncThunkMethod())
+    if (pMD->IsAsyncHelperMethod())
         return false;
         
     mdMethodDef md = pMD->GetMemberDef();
diff --git a/src/coreclr/vm/jitinterface.cpp b/src/coreclr/vm/jitinterface.cpp
index dea52c7f7bc975..aaf854b95982c6 100644
--- a/src/coreclr/vm/jitinterface.cpp
+++ b/src/coreclr/vm/jitinterface.cpp
@@ -3218,9 +3218,9 @@ void CEEInfo::ComputeRuntimeLookupForSharedGenericToken(DictionaryEntryKind entr
 
                 methodFlags |= ENCODE_METHOD_SIG_SlotInsteadOfToken;
             }
-            if (pTemplateMD->IsAsyncThunkMethod())
+            if (pTemplateMD->IsAsyncHelperMethod())
             {
-                methodFlags |= ENCODE_METHOD_SIG_AsyncThunk;
+                methodFlags |= ENCODE_METHOD_SIG_AsyncHelper;
             }
 
             sigBuilder.AppendData(methodFlags);
diff --git a/src/coreclr/vm/method.cpp b/src/coreclr/vm/method.cpp
index 3c08681e58c0d2..2d328b2ac3c7b6 100644
--- a/src/coreclr/vm/method.cpp
+++ b/src/coreclr/vm/method.cpp
@@ -447,7 +447,7 @@ void MethodDesc::GetSig(PCCOR_SIGNATURE *ppSig, DWORD *pcSig)
             return;
         }
     }
-    if (IsAsyncThunkMethod())
+    if (IsAsyncHelperMethod())
     {
         Signature sig = GetAddrOfAsyncMethodData()->sig;
         *ppSig = sig.GetRawSig();
@@ -482,7 +482,7 @@ void MethodDesc::GetSigFromMetadata(IMDInternalImport * importer,
     }
     CONTRACTL_END
 
-    _ASSERTE(!IsAsyncThunkMethod());
+    _ASSERTE(!IsAsyncHelperMethod());
     if (FAILED(importer->GetSigOfMethodDef(GetMemberDef(), pcSig, ppSig)))
     {   // Class loader already asked for signature, so this should always succeed (unless there's a
         // bug or a new code path)
@@ -768,7 +768,7 @@ BOOL MethodDesc::HasSameMethodDefAs(MethodDesc * pMD)
     if (this == pMD)
         return TRUE;
 
-    return (GetMemberDef() == pMD->GetMemberDef()) && (GetModule() == pMD->GetModule() && pMD->IsAsyncThunkMethod() == IsAsyncThunkMethod());
+    return (GetMemberDef() == pMD->GetMemberDef()) && (GetModule() == pMD->GetModule() && pMD->IsAsyncHelperMethod() == IsAsyncHelperMethod());
 }
 
 //*******************************************************************************
@@ -1155,7 +1155,7 @@ PTR_PCODE MethodDesc::GetAddrOfNativeCodeSlot()
 }
 
 //*******************************************************************************
-PTR_AsyncMethodData MethodDesc::GetAddrOfAsyncMethodData()
+PTR_AsyncMethodData MethodDesc::GetAddrOfAsyncMethodData() const
 {
     WRAPPER_NO_CONTRACT;
 
diff --git a/src/coreclr/vm/method.hpp b/src/coreclr/vm/method.hpp
index 09816ef3179ae9..b6d4788d1114b3 100644
--- a/src/coreclr/vm/method.hpp
+++ b/src/coreclr/vm/method.hpp
@@ -55,17 +55,27 @@ EXTERN_C VOID STDCALL NDirectImportThunk();
 #define METHOD_TOKEN_RANGE_BIT_COUNT (24 - METHOD_TOKEN_REMAINDER_BIT_COUNT)
 #define METHOD_TOKEN_RANGE_MASK ((1 << METHOD_TOKEN_RANGE_BIT_COUNT) - 1)
 
-enum class AsyncMethodType
+enum class AsyncMethodKind
 {
+    // Regular methods, including Task-returning ones.
+    // Those that do not return Task/ValueTask do not get a thunk sibling
     NotAsync,
-    TaskToAsync,
-    AsyncToTask,
-    Async
+
+    // Task-returning methods marked as async in IL.
+    // The method body forwards to Async2 implementation thunk (method with kind AsyncImplHelper)
+    Async,
+
+    // Synthetic Async2 method that contains the
+    // actual implementation transformed into a resumable state machine
+    AsyncImplHelper,
+
+    // Synthetic Async2 method that forwards to the NotAsync Task-returning method
+    AsyncThunkHelper
 };
 
 struct AsyncMethodData
 {
-    AsyncMethodType type;
+    AsyncMethodKind kind;
     Signature sig;
 };
 
@@ -193,7 +203,6 @@ enum class AsyncTaskMethod
     TaskReturningMethod,
     TaskNonGenericReturningMethod,
     Async2Method,
-    Async2MethodThatCannotBeImplementedByTask,
     Async2MethodNonGeneric,
     NormalMethod
 };
@@ -205,7 +214,7 @@ inline bool IsAsyncTaskMethodNormal(AsyncTaskMethod input)
 
 inline bool IsAsyncTaskMethodAsync2Method(AsyncTaskMethod input)
 {
-    return (input == AsyncTaskMethod::Async2Method) || (input == AsyncTaskMethod::Async2MethodThatCannotBeImplementedByTask) || (input == AsyncTaskMethod::Async2MethodNonGeneric);
+    return (input == AsyncTaskMethod::Async2Method) || (input == AsyncTaskMethod::Async2MethodNonGeneric);
 }
 
 inline bool IsAsyncTaskMethodTaskReturningMethod(AsyncTaskMethod input)
@@ -1455,7 +1464,7 @@ class MethodDesc
     BOOL SetNativeCodeInterlocked(PCODE addr, PCODE pExpected = 0);
 
     PTR_PCODE GetAddrOfNativeCodeSlot();
-    PTR_AsyncMethodData GetAddrOfAsyncMethodData();
+    PTR_AsyncMethodData GetAddrOfAsyncMethodData() const;
 #ifndef DACCESS_COMPILE
     const AsyncMethodData& GetAsyncMethodData() { _ASSERTE(HasAsyncMethodData()); return *GetAddrOfAsyncMethodData(); }
 #endif
@@ -1813,22 +1822,25 @@ class MethodDesc
         m_wFlags |= mdfHasNativeCodeSlot;
     }
 
-    inline bool IsAsyncThunkMethod()
+    inline bool IsAsyncHelperMethod() const
     {
         LIMITED_METHOD_DAC_CONTRACT;
         if (!HasAsyncMethodData())
             return false;
-        AsyncMethodType asyncType = GetAddrOfAsyncMethodData()->type;
-        return asyncType == AsyncMethodType::AsyncToTask || asyncType == AsyncMethodType::TaskToAsync;
+        auto asyncKind = GetAddrOfAsyncMethodData()->kind;
+        return asyncKind == AsyncMethodKind::AsyncThunkHelper ||
+            asyncKind == AsyncMethodKind::AsyncImplHelper;
     }
 
-    inline bool IsAsync2Method()
+    // We use "async2" for runtime async methods that return "Unwrapped" values (i.e. T instead of Task<T>)
+    // The type of promise is typically captured in a modreq.
+    // CONSIDER: We probably need a better name for the concept, but it is hard to beat shortness of "async2"
+    inline bool IsAsync2Method() const
     {
-        if (!HasAsyncMethodData())
-            return false;
-        AsyncMethodType asyncType = GetAddrOfAsyncMethodData()->type;
-
-        return asyncType == AsyncMethodType::Async || asyncType == AsyncMethodType::AsyncToTask;
+        // Right now the only Async2 methods that exist are synthetic helpers.
+        // It is possible to declare an Async2 method directly in IL/Metadata,
+        // but we do not have a scenario for that.
+        return IsAsyncHelperMethod();
     }
 
     inline bool IsStructMethodOperatingOnCopy()
@@ -1839,19 +1851,24 @@ class MethodDesc
         if (!HasAsyncMethodData())
             return false;
 
-        // Only user runtimeasync methods operate on copies. runtime-async ->
-        // compiler-async thunks do not.
-        AsyncMethodType asyncType = GetAddrOfAsyncMethodData()->type;
-
-        return asyncType == AsyncMethodType::Async;
+        // Only async2 methods backed by actual user code operate on copies.
+        // Thunks with runtime-supplied implementation do not.
+        return GetAddrOfAsyncMethodData()->kind == AsyncMethodKind::AsyncImplHelper;
     }
 
-    inline bool RequiresAsyncContinuationArg()
+    // The method is a Task/async2 adapter to an async2/Task implementation
+    inline bool IsAsyncThunkMethod() const
     {
-        return IsAsync2Method();
+        LIMITED_METHOD_DAC_CONTRACT;
+        if (!HasAsyncMethodData())
+            return false;
+
+        auto asyncType = GetAddrOfAsyncMethodData()->kind;
+        return asyncType == AsyncMethodKind::AsyncThunkHelper ||
+            asyncType == AsyncMethodKind::Async;
     }
 
-    inline bool HasAsyncMethodData()
+    inline bool HasAsyncMethodData() const
     {
         return (m_wFlags & mdfHasAsyncMethodData) != 0;
     }
diff --git a/src/coreclr/vm/methoditer.cpp b/src/coreclr/vm/methoditer.cpp
index e6ac28c17edd44..1241941da4f1d8 100644
--- a/src/coreclr/vm/methoditer.cpp
+++ b/src/coreclr/vm/methoditer.cpp
@@ -147,7 +147,7 @@ BOOL LoadedMethodDescIterator::Next(
             goto ADVANCE_METHOD;
         if (m_methodIteratorEntry->GetMethod()->GetMemberDef() != m_md)
             goto ADVANCE_METHOD;
-        if (m_methodIteratorEntry->GetMethod()->IsAsyncThunkMethod() != m_fIsAsyncThunk)
+        if (m_methodIteratorEntry->GetMethod()->IsAsyncHelperMethod() != m_fIsAsyncThunk)
             goto ADVANCE_METHOD;
     }
     else if (m_startedNonGenericMethod)
@@ -250,7 +250,7 @@ LoadedMethodDescIterator::Start(
     mdMethodDef     md,
     MethodDesc      *pMethodDesc)
 {
-    Start(pAppDomain, pModule, md, pMethodDesc->IsAsyncThunkMethod());
+    Start(pAppDomain, pModule, md, pMethodDesc->IsAsyncHelperMethod());
     m_mainMD = pMethodDesc;
 }
 
diff --git a/src/coreclr/vm/methodtable.cpp b/src/coreclr/vm/methodtable.cpp
index d09d130a1e89c3..e7f3229ec4e658 100644
--- a/src/coreclr/vm/methodtable.cpp
+++ b/src/coreclr/vm/methodtable.cpp
@@ -7805,7 +7805,7 @@ namespace
             MethodDesc* pMD = it.GetMethodDesc();
             if (pMD->GetMemberDef() == tkMethod
                 && pMD->GetModule() == mod
-                && pMD->IsAsyncThunkMethod() == pDefMD->IsAsyncThunkMethod())
+                && pMD->IsAsyncHelperMethod() == pDefMD->IsAsyncHelperMethod())
             {
                 return pMD;
             }
@@ -7840,7 +7840,7 @@ MethodDesc* MethodTable::GetParallelMethodDesc(MethodDesc* pDefMD, AsyncVariantL
         // This could be optimized with some trickery around slot numbers, but doing so is ... confusing, so I'm not implementing this yet
         mdMethodDef tkMethod = pDefMD->GetMemberDef();
         Module* mod = pDefMD->GetModule();
-        bool isAsyncThunkMethod = pDefMD->IsAsyncThunkMethod();
+        bool isAsyncHelperMethod = pDefMD->IsAsyncHelperMethod();
 
         MethodTable::IntroducedMethodIterator it(this);
         for (; it.IsValid(); it.Next())
@@ -7848,7 +7848,7 @@ MethodDesc* MethodTable::GetParallelMethodDesc(MethodDesc* pDefMD, AsyncVariantL
             MethodDesc* pMD = it.GetMethodDesc();
             if (pMD->GetMemberDef() == tkMethod
                 && pMD->GetModule() == mod
-                && pMD->IsAsyncThunkMethod() != isAsyncThunkMethod)
+                && pMD->IsAsyncHelperMethod() != isAsyncHelperMethod)
             {
                 return pMD;
             }
@@ -8237,7 +8237,7 @@ MethodTable::TryResolveVirtualStaticMethodOnThisType(MethodTable* pInterfaceType
         {
             if (pMethodDecl->GetMemberDef() == pInterfaceMD->GetMemberDef() && 
                 pMethodDecl->GetModule() == pInterfaceMD->GetModule() &&
-                pMethodDecl->IsAsyncThunkMethod() != pInterfaceMD->IsAsyncThunkMethod())
+                pMethodDecl->IsAsyncHelperMethod() != pInterfaceMD->IsAsyncHelperMethod())
             {
                 differsByAsyncVariant = true;
                 pMethodDecl = pMethodDecl->GetAsyncOtherVariant();
diff --git a/src/coreclr/vm/methodtablebuilder.cpp b/src/coreclr/vm/methodtablebuilder.cpp
index 3bb9be3c26e28c..56537121b278da 100644
--- a/src/coreclr/vm/methodtablebuilder.cpp
+++ b/src/coreclr/vm/methodtablebuilder.cpp
@@ -992,7 +992,7 @@ MethodTableBuilder::bmtRTMethod::bmtRTMethod(
     MethodDesc *    pMD)
     : m_pOwningType(pOwningType),
       m_pMD(pMD),
-      m_methodSig(pMD->IsAsyncThunkMethod()
+      m_methodSig(pMD->IsAsyncHelperMethod()
        ? MethodSignature(pMD->GetModule(),
                          pMD->GetMemberDef(),
                          pMD->GetSignature(),
@@ -1024,7 +1024,7 @@ MethodTableBuilder::bmtMDMethod::bmtMDMethod(
       m_dwImplAttrs(dwImplAttrs),
       m_dwRVA(dwRVA),
       m_type(type),
-      m_asyncMethodType(AsyncMethodType::NotAsync),
+      m_asyncMethodKind(AsyncMethodKind::NotAsync),
       m_implType(implType),
       m_methodSig(pOwningType->GetModule(),
                   tok,
@@ -1050,7 +1050,7 @@ MethodTableBuilder::bmtMDMethod::bmtMDMethod(
     DWORD dwImplAttrs,
     DWORD dwRVA,
     Signature sig,
-    AsyncMethodType thunkType,
+    AsyncMethodKind asyncMethodKind,
     MethodClassification type,
     METHOD_IMPL_TYPE implType)
     : m_pOwningType(pOwningType),
@@ -1058,7 +1058,7 @@ MethodTableBuilder::bmtMDMethod::bmtMDMethod(
       m_dwImplAttrs(dwImplAttrs),
       m_dwRVA(dwRVA),
       m_type(type),
-      m_asyncMethodType(thunkType),
+      m_asyncMethodKind(asyncMethodKind),
       m_implType(implType),
       m_methodSig(pOwningType->GetModule(),
                   tok,
@@ -2773,7 +2773,7 @@ AsyncTaskMethod ClassifyAsyncMethodCore(SigPointer sig, Module* pModule, PCCOR_S
 
     // Now we should be parsing the return type
 
-    // If the first custommodifier is a MOD_OPT to CallConvAsync2Call
+    // If the first custommodifier is a MOD_REQ to CallConvAsync2Call
     // Then this is a async2 function
     CorElementType elemType;
     if (offsetOfAsyncDetails != NULL)
@@ -2792,7 +2792,7 @@ AsyncTaskMethod ClassifyAsyncMethodCore(SigPointer sig, Module* pModule, PCCOR_S
         GetNameOfTypeDefOrRef(pModule, tk, &name, &_namespace);
         IfFailThrow(sig.GetByte(&elemTypeByte)); // Don't use GetElemType as it skips custom modifiers
         elemType = (CorElementType)elemTypeByte;
-        if (strcmp(_namespace, "System.Threading.Tasks") == 0 && IsTypeDefOrRefImplementedInSystemModule(pModule, tk) && (elemTypeCmod == ELEMENT_TYPE_CMOD_OPT))
+        if (strcmp(_namespace, "System.Threading.Tasks") == 0 && IsTypeDefOrRefImplementedInSystemModule(pModule, tk) && (elemTypeCmod == ELEMENT_TYPE_CMOD_REQD))
         {
             if ((strcmp(name, "Task`1") == 0) || (strcmp(name, "ValueTask`1") == 0))
             {
@@ -2824,7 +2824,7 @@ AsyncTaskMethod ClassifyAsyncMethodCore(SigPointer sig, Module* pModule, PCCOR_S
                     case ELEMENT_TYPE_MVAR:
                         return AsyncTaskMethod::Async2Method;
                     case ELEMENT_TYPE_TYPEDBYREF:
-                        return AsyncTaskMethod::Async2MethodThatCannotBeImplementedByTask;
+                        ThrowHR(COR_E_BADIMAGEFORMAT);
                     case ELEMENT_TYPE_GENERICINST:
                         IfFailThrow(sig.GetElemType(&elemType));
                         if (elemType == ELEMENT_TYPE_CLASS)
@@ -2836,7 +2836,7 @@ AsyncTaskMethod ClassifyAsyncMethodCore(SigPointer sig, Module* pModule, PCCOR_S
                             IfFailThrow(sig.GetToken(&tk));
                             if (IsTypeDefOrRefAByRefStruct(pModule, tk))
                             {
-                                return AsyncTaskMethod::Async2MethodThatCannotBeImplementedByTask;
+                                ThrowHR(COR_E_BADIMAGEFORMAT);
                             }
                             else
                             {
@@ -3585,9 +3585,20 @@ MethodTableBuilder::EnumerateClassMethods()
                     type,
                     implType);
 
-                if (IsAsyncTaskMethodAsync2Method(asyncMethodType))
+                if (IsAsyncTaskMethodTaskReturningMethod(asyncMethodType))
                 {
-                    pNewMethod->SetIsAsync2Method();
+                    // ordinary Task-returning method:
+                    //    declare an ordinary method and add a helper thunk with Async2 signature
+                    // 
+                    // IsMiAsync Task-returning method:
+                    //    declare an Async method and add a helper method with the actual implementation
+                    //    the Async method becomes a thunk to the implementation helper.
+                    pNewMethod->SetAsyncMethodKind(IsMiAsync(dwImplFlags) ? AsyncMethodKind::Async : AsyncMethodKind::NotAsync);
+                }
+                else
+                {
+                    _ASSERTE(IsAsyncTaskMethodNormal(asyncMethodType));
+                    pNewMethod->SetAsyncMethodKind(AsyncMethodKind::NotAsync);
                 }
 
                 pDeclaredMethod = pNewMethod;
@@ -3595,7 +3606,7 @@ MethodTableBuilder::EnumerateClassMethods()
             else
             {
                 ULONG cAsyncThunkMemberSignature = cMemberSignature;
-                AsyncMethodType thunkType;
+                AsyncMethodKind asyncKind;
                 ULONG originalTokenOffsetFromAsyncDetailsOffset;
                 ULONG newTokenOffsetFromAsyncDetailsOffset;
                 ULONG originalPrefixSize;
@@ -3603,50 +3614,32 @@ MethodTableBuilder::EnumerateClassMethods()
                 ULONG newSuffixSize;
                 ULONG newPrefixSize;
 
-                if (asyncMethodType == AsyncTaskMethod::Async2Method)
-                {
-                    cAsyncThunkMemberSignature += 2;
-                    originalTokenOffsetFromAsyncDetailsOffset = 1;
-                    newTokenOffsetFromAsyncDetailsOffset = 2;
-                    thunkType = AsyncMethodType::TaskToAsync;
-                    originalPrefixSize = 1;
-                    newPrefixSize = 2;
-                    originalSuffixSize = 0;
-                    newSuffixSize = 1;
-                }
-                else if (asyncMethodType == AsyncTaskMethod::Async2MethodNonGeneric)
-                {
-                    cAsyncThunkMemberSignature -= 1;
-                    originalTokenOffsetFromAsyncDetailsOffset = 1;
-                    newTokenOffsetFromAsyncDetailsOffset = 1;
-                    thunkType = AsyncMethodType::TaskToAsync;
-                    originalPrefixSize = 1;
-                    newPrefixSize = 1;
-                    originalSuffixSize = 1;
-                    newSuffixSize = 0;
-                }
-                else if (asyncMethodType == AsyncTaskMethod::TaskNonGenericReturningMethod)
+                if (asyncMethodType == AsyncTaskMethod::TaskNonGenericReturningMethod)
                 {
                     cAsyncThunkMemberSignature += 1;
                     originalTokenOffsetFromAsyncDetailsOffset = 1;
                     newTokenOffsetFromAsyncDetailsOffset = 1;
-                    thunkType = AsyncMethodType::AsyncToTask;
+                    asyncKind = IsMiAsync(dwImplFlags) ? AsyncMethodKind::AsyncImplHelper : AsyncMethodKind::AsyncThunkHelper;
                     originalPrefixSize = 1;
                     newPrefixSize = 1;
                     originalSuffixSize = 0;
                     newSuffixSize = 1;
                 }
-                else
+                else if (asyncMethodType == AsyncTaskMethod::TaskReturningMethod)
                 {
                     cAsyncThunkMemberSignature -= 2;
                     originalTokenOffsetFromAsyncDetailsOffset = 2;
                     newTokenOffsetFromAsyncDetailsOffset = 1;
-                    thunkType = AsyncMethodType::AsyncToTask;
+                    asyncKind = IsMiAsync(dwImplFlags)? AsyncMethodKind::AsyncImplHelper : AsyncMethodKind::AsyncThunkHelper;
                     originalPrefixSize = 2;
                     newPrefixSize = 1;
                     originalSuffixSize = 1;
                     newSuffixSize = 0;
                 }
+                else
+                {
+                    UNREACHABLE();
+                }
 
                 BYTE* pNewMemberSignature = AllocateFromHighFrequencyHeap(S_SIZE_T(cAsyncThunkMemberSignature));
                 ULONG tokenLen = CorSigUncompressedDataSize(&pMemberSignature[offsetOfAsyncDetails + originalTokenOffsetFromAsyncDetailsOffset]);
@@ -3664,40 +3657,23 @@ MethodTableBuilder::EnumerateClassMethods()
 
                 BYTE elemTypeClassOrValuetype = isAsyncValueType ? (BYTE)ELEMENT_TYPE_VALUETYPE : (BYTE)ELEMENT_TYPE_CLASS;
 
-                if (asyncMethodType == AsyncTaskMethod::Async2Method)
-                {
-                    // Incoming sig will look something like ... E_T_CMOD_OPT <TokenOfTask> E_T_I4 ....
-                    // And needs to be translated to E_T_GENERICINST E_T_CLASS/E_T_VALUETYPE <TokenOfTask> 1 E_T_I4
-                    // Replace the ELEMENT_TYPE_CMOD with ELEMENT_TYPE_GENERICINST, and then add a 1 after the token which refers to Task<T>
-
-                    pNewMemberSignature[offsetOfAsyncDetails] = ELEMENT_TYPE_GENERICINST;
-                    pNewMemberSignature[offsetOfAsyncDetails + 1] = elemTypeClassOrValuetype;
-                    pNewMemberSignature[newRemainingSigOffset - 1] = 1;
-                }
-                else if (asyncMethodType == AsyncTaskMethod::Async2MethodNonGeneric)
-                {
-                    // Incoming sig will look like ...   E_T_CMOD_OPT <TokenOfTask> E_T_VOID
-                    // which needs to be translated to E_T_CLASS/E_T_VALUETYPE <TokenOfTask>
-
-                    // Replace the ELEMENT_TYPE_CMOD with ELEMENT_TYPE_CLASS/VALUETYPE
-                    pNewMemberSignature[offsetOfAsyncDetails] = elemTypeClassOrValuetype;
-                }
-                else if (asyncMethodType == AsyncTaskMethod::TaskNonGenericReturningMethod)
+                if (asyncMethodType == AsyncTaskMethod::TaskNonGenericReturningMethod)
                 {
                     // Incoming sig will look like ... E_T_CLASS/E_T_VALUETYPE <TokenOfTask>
-                    // and needs to be translated to E_T_CMOD_OPT <TokenOfTask> E_T_VOID
+                    // and needs to be translated to ELEMENT_TYPE_CMOD_REQD <TokenOfTask> E_T_VOID
 
-                    // Replace the E_T_CLASS/E_T_VALUETYPE with ELEMENT_TYPE_CMOD_OPT, and then add the E_T_VOID
-                    pNewMemberSignature[offsetOfAsyncDetails] = ELEMENT_TYPE_CMOD_OPT;
+                    // Replace the E_T_CLASS/E_T_VALUETYPE with ELEMENT_TYPE_CMOD_REQD, and then add the E_T_VOID
+                    pNewMemberSignature[offsetOfAsyncDetails] = ELEMENT_TYPE_CMOD_REQD;
                     pNewMemberSignature[newRemainingSigOffset - 1] = ELEMENT_TYPE_VOID;
                 }
                 else
                 {
+                    _ASSERTE(asyncMethodType == AsyncTaskMethod::TaskReturningMethod);
                     // Incoming sig will look something like ... E_T_GENERICINST E_T_CLASS/E_T_VALUETYPE <TokenOfTask> 1 E_T_I4 ....
-                    // And needs to be translated to E_T_CMOD_OPT <TokenOfTask> E_T_I4
+                    // And needs to be translated to ELEMENT_TYPE_CMOD_REQD <TokenOfTask> E_T_I4
 
-                    // Replace the ELEMENT_TYPE_GENERICINST with ELEMENT_TYPE_CMOD_OPT, and then remove the 1 which specifies the generic arg count for Task<T>
-                    pNewMemberSignature[offsetOfAsyncDetails] = ELEMENT_TYPE_CMOD_OPT;
+                    // Replace the ELEMENT_TYPE_GENERICINST with ELEMENT_TYPE_CMOD_REQD, and then remove the 1 which specifies the generic arg count for Task<T>
+                    pNewMemberSignature[offsetOfAsyncDetails] = ELEMENT_TYPE_CMOD_REQD;
                 }
 
                 Signature newMemberSig(pNewMemberSignature, cAsyncThunkMemberSignature);
@@ -3708,7 +3684,7 @@ MethodTableBuilder::EnumerateClassMethods()
                     dwImplFlags,
                     dwMethodRVA,
                     newMemberSig,
-                    thunkType,
+                    asyncKind,
                     type,
                     implType);
 
@@ -3731,7 +3707,7 @@ MethodTableBuilder::EnumerateClassMethods()
             }
 
             // Normal methods only insert a single method
-            if ((asyncMethodType == AsyncTaskMethod::NormalMethod) || (asyncMethodType == AsyncTaskMethod::Async2MethodThatCannotBeImplementedByTask))
+            if (IsAsyncTaskMethodNormal(asyncMethodType))
             {
                 break;
             }
@@ -5565,7 +5541,7 @@ MethodTableBuilder::InitNewMethodDesc(
     if (NeedsNativeCodeSlot(pMethod))
         pNewMD->SetHasNativeCodeSlot();
 
-    if (pMethod->GetAsyncMethodType() != AsyncMethodType::NotAsync)
+    if (pMethod->GetAsyncMethodKind() != AsyncMethodKind::NotAsync)
         pNewMD->SetHasAsyncMethodData();
 
     // Now we know the classification we can allocate the correct type of
@@ -5612,7 +5588,7 @@ MethodTableBuilder::InitNewMethodDesc(
                    GetMDImport(),
                    pName,
                    sig,
-                   pMethod->GetAsyncMethodType()
+                   pMethod->GetAsyncMethodKind()
                    COMMA_INDEBUG(pszDebugMethodNameCopy)
                    COMMA_INDEBUG(GetDebugClassName())
                    COMMA_INDEBUG("") // FIX this happens on global methods, give better info
@@ -5942,7 +5918,7 @@ MethodTableBuilder::FindDeclMethodOnInterfaceEntry(bmtInterfaceEntry *pItfEntry,
             if ((slotDeclMethod->GetOwningType() == declRTMethod->GetOwningType()) &&
                 (slotDeclMethod->GetMethodDesc()->GetMethodTable() == declRTMethod->GetMethodDesc()->GetMethodTable()) &&
                 (slotDeclMethod->GetMethodDesc()->GetMemberDef() == declRTMethod->GetMethodDesc()->GetMemberDef()) &&
-                (slotDeclMethod->GetMethodDesc()->IsAsyncThunkMethod() != declRTMethod->GetMethodDesc()->IsAsyncThunkMethod()))
+                (slotDeclMethod->GetMethodDesc()->IsAsyncHelperMethod() != declRTMethod->GetMethodDesc()->IsAsyncHelperMethod()))
             {
                 declMethod = slotIt->Decl();
                 foundOtherVariant = true;
@@ -6516,7 +6492,7 @@ MethodTableBuilder::InitMethodDesc(
     IMDInternalImport * pIMDII,     // Needed for NDirect, EEImpl(Delegate) cases
     LPCSTR              pMethodName, // Only needed for mcEEImpl (Delegate) case
     Signature           sig, // Only needed for the Async2 Thunk case
-    AsyncMethodType      thunkType
+    AsyncMethodKind     asyncKind
     COMMA_INDEBUG(LPCUTF8 pszDebugMethodName)
     COMMA_INDEBUG(LPCUTF8 pszDebugClassName)
     COMMA_INDEBUG(LPCUTF8 pszDebugMethodSignature)
@@ -6675,11 +6651,18 @@ MethodTableBuilder::InitMethodDesc(
 #endif // !_DEBUG
         pNewMD->SetSynchronized();
 
-    if (thunkType != AsyncMethodType::NotAsync)
+    if (asyncKind != AsyncMethodKind::NotAsync)
     {
         AsyncMethodData* pThunkData = pNewMD->GetAddrOfAsyncMethodData();
-        pThunkData->sig = sig;
-        pThunkData->type = thunkType;
+        pThunkData->kind = asyncKind;
+        if (asyncKind == AsyncMethodKind::AsyncThunkHelper || asyncKind == AsyncMethodKind::AsyncImplHelper)
+        {
+            pThunkData->sig = sig;
+        }
+        else
+        {
+            _ASSERTE(sig.GetRawSig() == NULL);
+        }
     }
 
 #ifdef _DEBUG
@@ -7414,7 +7397,7 @@ VOID MethodTableBuilder::AllocAndInitMethodDescs()
         if (NeedsNativeCodeSlot(*it))
             size += sizeof(MethodDesc::NativeCodeSlot);
         
-        if (it->GetAsyncMethodType() != AsyncMethodType::NotAsync)
+        if (it->GetAsyncMethodKind() != AsyncMethodKind::NotAsync)
             size += sizeof(AsyncMethodData);
 
         // See comment in AllocAndInitMethodDescChunk
diff --git a/src/coreclr/vm/methodtablebuilder.h b/src/coreclr/vm/methodtablebuilder.h
index f6ebe994d84d84..78d07dc4207d89 100644
--- a/src/coreclr/vm/methodtablebuilder.h
+++ b/src/coreclr/vm/methodtablebuilder.h
@@ -673,7 +673,7 @@ class MethodTableBuilder
 
         //-----------------------------------------------------------------------------------------
         // This constructor can be used with hard-coded signatures that are used for
-        // representing async thunk methods
+        // representing async helper methods
         MethodSignature(
             Module *             pModule,
             mdToken              tok,
@@ -956,7 +956,7 @@ class MethodTableBuilder
             DWORD dwImplAttrs,
             DWORD dwRVA,
             Signature sig,
-            AsyncMethodType thunkType,
+            AsyncMethodKind thunkKind,
             MethodClassification type,
             METHOD_IMPL_TYPE implType);
 
@@ -1062,18 +1062,24 @@ class MethodTableBuilder
 
         bool IsAsyncThunk() const
         {
-            return GetAsyncMethodType() == AsyncMethodType::AsyncToTask || GetAsyncMethodType() == AsyncMethodType::TaskToAsync;
+            return GetAsyncMethodKind() == AsyncMethodKind::AsyncThunkHelper ||
+                GetAsyncMethodKind() == AsyncMethodKind::AsyncImplHelper;
         }
 
-        void SetIsAsync2Method()
+        bool IsAsync2() const
         {
-            m_asyncMethodType = AsyncMethodType::Async;
+            return IsAsyncThunk();
         }
 
-        AsyncMethodType GetAsyncMethodType() const
+        void SetAsyncMethodKind(AsyncMethodKind kind)
+        {
+            m_asyncMethodKind = kind;
+        }
+
+        AsyncMethodKind GetAsyncMethodKind() const
         {
             LIMITED_METHOD_CONTRACT;
-            return m_asyncMethodType;
+            return m_asyncMethodKind;
         }
 
         bmtMDMethod *     GetAsyncOtherVariant() const { return m_asyncOtherVariant; }
@@ -1087,7 +1093,7 @@ class MethodTableBuilder
         DWORD             m_dwImplAttrs;
         DWORD             m_dwRVA;
         MethodClassification  m_type;               // Specific MethodDesc flavour
-        AsyncMethodType m_asyncMethodType;
+        AsyncMethodKind   m_asyncMethodKind;
         METHOD_IMPL_TYPE  m_implType;           // Whether or not the method is a methodImpl body
         MethodSignature   m_methodSig;
         bmtMDMethod*      m_asyncOtherVariant = NULL;
@@ -2686,7 +2692,7 @@ class MethodTableBuilder
         IMDInternalImport * pIMDII,  // Needed for NDirect, EEImpl(Delegate) cases
         LPCSTR              pMethodName, // Only needed for mcEEImpl (Delegate) case
         Signature           sig, // Only needed for the async thunk (Async2 Thunk) case
-        AsyncMethodType      thunkType
+        AsyncMethodKind      asyncKind
         COMMA_INDEBUG(LPCUTF8             pszDebugMethodName)
         COMMA_INDEBUG(LPCUTF8             pszDebugClassName)
         COMMA_INDEBUG(LPCUTF8             pszDebugMethodSignature));
diff --git a/src/coreclr/vm/multicorejit.cpp b/src/coreclr/vm/multicorejit.cpp
index f1a3f6ab9f8307..a56aa14a3319b3 100644
--- a/src/coreclr/vm/multicorejit.cpp
+++ b/src/coreclr/vm/multicorejit.cpp
@@ -398,9 +398,9 @@ HRESULT MulticoreJitRecorder::WriteOutput(IStream * pStream)
         }
 
         MethodDesc * pMethod = m_JitInfoArray[i].GetMethodDescAndClean();
-        if (pMethod->IsAsyncThunkMethod())
+        if (pMethod->IsAsyncHelperMethod())
         {
-            // TODO consider adding support for async thunks in the future
+            // TODO consider adding support for async helpers in the future
             skipped++;
             continue;
         }
diff --git a/src/coreclr/vm/prestub.cpp b/src/coreclr/vm/prestub.cpp
index 982df5a6867497..a4ae1f2e958d68 100644
--- a/src/coreclr/vm/prestub.cpp
+++ b/src/coreclr/vm/prestub.cpp
@@ -719,6 +719,15 @@ namespace
 
         COR_ILMETHOD_DECODER* pHeader = NULL;
         COR_ILMETHOD* ilHeader = pConfig->GetILHeader();
+
+        // For async method the methoddef represents a thunk with runtime-provided implementation,
+        // while the default IL logically belongs to the implementation method desc.
+        // If config returned no IL for an implementation method desc, then ask the method desc itself.
+        if (ilHeader == NULL && pMD->IsAsyncHelperMethod() && !pMD->IsAsyncThunkMethod())
+        {
+            ilHeader = pMD->GetILHeader();
+        }
+
         if (ilHeader == NULL)
             return NULL;
 
@@ -1741,10 +1750,6 @@ bool MethodDesc::TryGenerateAsyncThunk(DynamicResolver** resolver, COR_ILMETHOD_
         return false;
     }
 
-    ULONG offsetOfAsyncDetailsUnused;
-    bool isValueTypeUnused;
-    AsyncTaskMethod asyncType = ClassifyAsyncMethod(GetSigPointer(), GetModule(), &offsetOfAsyncDetailsUnused, &isValueTypeUnused);
-
     MethodDesc *pAsyncOtherVariant = this->GetAsyncOtherVariant();
     _ASSERTE(!IsWrapperStub() && !pAsyncOtherVariant->IsWrapperStub());
 
@@ -1758,14 +1763,13 @@ bool MethodDesc::TryGenerateAsyncThunk(DynamicResolver** resolver, COR_ILMETHOD_
         pAsyncOtherVariant,
         (ILStubLinkerFlags)ILSTUB_LINKER_FLAG_NONE);
 
-    if (IsAsyncTaskMethodTaskReturningMethod(asyncType))
+    if (IsAsync2Method())
     {
-        EmitJitStateMachineBasedRuntimeAsyncThunk(pAsyncOtherVariant, msig, &sl);
+        EmitAsync2MethodThunk(pAsyncOtherVariant, msig, &sl);
     }
     else
     {
-        _ASSERTE(IsAsyncTaskMethodAsync2Method(asyncType));
-        EmitAsync2MethodThunk(pAsyncOtherVariant, msig, &sl);
+        EmitJitStateMachineBasedRuntimeAsyncThunk(pAsyncOtherVariant, msig, &sl);
     }
 
     NewHolder<ILStubResolver> ilResolver = new ILStubResolver();
@@ -1807,6 +1811,8 @@ bool MethodDesc::TryGenerateAsyncThunk(DynamicResolver** resolver, COR_ILMETHOD_
 
 void MethodDesc::EmitJitStateMachineBasedRuntimeAsyncThunk(MethodDesc* pAsyncOtherVariant, MetaSig& thunkMsig, ILStubLinker* pSL)
 {
+    _ASSERTE(!pAsyncOtherVariant->IsAsyncThunkMethod());
+
     ILCodeStream* pCode = pSL->NewCodeStream(ILStubLinker::kDispatch);
 
     unsigned continuationLocal = pCode->NewLocal(LocalDesc(CoreLibBinder::GetClass(CLASS__CONTINUATION)));
@@ -2022,12 +2028,12 @@ void MethodDesc::EmitJitStateMachineBasedRuntimeAsyncThunk(MethodDesc* pAsyncOth
     pCode->EmitRET();
 }
 
-// Given an async 2 method, return a SigPointer to the unwrapped result type. For
+// Given an async method, return a SigPointer to the unwrapped result type. For
 // example, for async2 Task<T> Foo<T>() this returns the signature representing
 // (MVAR 0). For Task<int>, it returns the signature representing (int).
 SigPointer MethodDesc::GetAsync2ThunkResultTypeSig()
 {
-    _ASSERTE(GetAsyncMethodData().type == AsyncMethodType::TaskToAsync || GetAsyncMethodData().type == AsyncMethodType::AsyncToTask);
+    _ASSERTE(IsAsyncThunkMethod());
     PCCOR_SIGNATURE pSigRaw;
     DWORD cSig;
     if (FAILED(GetMDImport()->GetSigOfMethodDef(GetMemberDef(), &cSig, &pSigRaw)))
@@ -2053,6 +2059,23 @@ SigPointer MethodDesc::GetAsync2ThunkResultTypeSig()
     // ReturnType comes now. Skip the modifiers (like async2 modifier).
     IfFailThrow(pSig.SkipCustomModifiers());
 
+    CorElementType etype;
+    IfFailThrow(pSig.PeekElemType(&etype));
+
+    // here we should have something Task<retType> or ValueTask<retType>
+    _ASSERTE(etype == ELEMENT_TYPE_GENERICINST);
+
+    // GENERICINST <generic type> <argCnt> <arg1>
+
+    // ELEMENT_TYPE_GENERICINST
+    IfFailThrow(pSig.GetElemType(NULL));
+
+    // Task`1/ValueTask`1
+    IfFailThrow(pSig.SkipExactlyOne());
+
+    // argCnt
+    IfFailThrow(pSig.GetData(NULL));
+
     // Get the start of the return type
     PCCOR_SIGNATURE returnTypeSig;
     uint32_t tailLength;
@@ -2138,10 +2161,13 @@ int MethodDesc::GetTokenForGenericTypeMethodCallWithAsyncReturnType(ILCodeStream
 
 void MethodDesc::EmitAsync2MethodThunk(MethodDesc* pAsyncOtherVariant, MetaSig& msig, ILStubLinker* pSL)
 {
+    _ASSERTE(!pAsyncOtherVariant->IsAsyncThunkMethod());
+    _ASSERTE(!pAsyncOtherVariant->IsVoid());
+
     // Implement IL that is effectively the following
     /*
     {
-        TaskAwaiter<RetType> awaiter = Thunk(arg).GetAwaiter();
+        TaskAwaiter<RetType> awaiter = other(arg).GetAwaiter();
         if (!awaiter.IsCompleted)
         {
             // Magic function which will suspend the current run of async methods
@@ -2789,7 +2815,7 @@ PrepareCodeConfigBuffer::PrepareCodeConfigBuffer(NativeCodeVersion codeVersion)
 
 // CreateDerivedTargetSigWithExtraParams:
 // This method is used to create the signature of the target of the ILStub for
-// instantiating, unboxing, and async thunk stubs, when/where we need to
+// instantiating, unboxing, and async helper stubs, when/where we need to
 // introduce a generic context/async continuation.
 // And since the generic context/async continuations are hidden parameters,
 // we're creating a signature that looks like non-generic but with additional
diff --git a/src/coreclr/vm/readytoruninfo.cpp b/src/coreclr/vm/readytoruninfo.cpp
index 905edf176aca39..7948808fa7705b 100644
--- a/src/coreclr/vm/readytoruninfo.cpp
+++ b/src/coreclr/vm/readytoruninfo.cpp
@@ -962,7 +962,7 @@ static bool SigMatchesMethodDesc(MethodDesc* pMD, SigPointer &sig, ModuleBase *
 {
     STANDARD_VM_CONTRACT;
 
-    _ASSERTE(!pMD->IsAsyncThunkMethod());
+    _ASSERTE(!pMD->IsAsyncHelperMethod());
 
     ModuleBase *pOrigModule = pModule;
     ZapSig::Context    zapSigContext(pModule, (void *)pModule, ZapSig::NormalTokens);
@@ -1072,7 +1072,7 @@ bool ReadyToRunInfo::GetPgoInstrumentationData(MethodDesc * pMD, BYTE** pAllocat
     if (ReadyToRunCodeDisabled())
         return false;
 
-    if (pMD->IsAsyncThunkMethod())
+    if (pMD->IsAsyncHelperMethod())
         return false;
 
     if (m_pgoInstrumentationDataHashtable.IsNull())
@@ -1147,7 +1147,7 @@ PCODE ReadyToRunInfo::GetEntryPoint(MethodDesc * pMD, PrepareCodeConfig* pConfig
     if (ReadyToRunCodeDisabled())
         goto done;
 
-    if (pMD->IsAsyncThunkMethod())
+    if (pMD->IsAsyncHelperMethod())
         goto done;
 
     ETW::MethodLog::GetR2RGetEntryPointStart(pMD);
diff --git a/src/coreclr/vm/zapsig.cpp b/src/coreclr/vm/zapsig.cpp
index 0bd6f65dd87e8e..8aef73bdf9aabc 100644
--- a/src/coreclr/vm/zapsig.cpp
+++ b/src/coreclr/vm/zapsig.cpp
@@ -921,13 +921,15 @@ MethodDesc *ZapSig::DecodeMethod(ModuleBase *pInfoModule,
     // in non-generic structs.
     BOOL isInstantiatingStub = (methodFlags & ENCODE_METHOD_SIG_InstantiatingStub);
     BOOL isUnboxingStub = (methodFlags & ENCODE_METHOD_SIG_UnboxingStub);
-    bool isAsyncThunk = (methodFlags & ENCODE_METHOD_SIG_AsyncThunk) != 0;
+    bool isAsyncHelper = (methodFlags & ENCODE_METHOD_SIG_AsyncHelper) != 0;
 
     pMethod = MethodDesc::FindOrCreateAssociatedMethodDesc(pMethod, thOwner.GetMethodTable(),
                                                             isUnboxingStub,
                                                             inst,
                                                             !(isInstantiatingStub || isUnboxingStub) && !actualOwnerRequired,
-                                                            actualOwnerRequired, TRUE, isAsyncThunk == pMethod->IsAsyncThunkMethod() ? AsyncVariantLookup::MatchingAsyncVariant : AsyncVariantLookup::AsyncOtherVariant);
+                                                            actualOwnerRequired,
+                                                            TRUE,
+                                                            isAsyncHelper == pMethod->IsAsyncHelperMethod() ? AsyncVariantLookup::MatchingAsyncVariant : AsyncVariantLookup::AsyncOtherVariant);
 
     if (methodFlags & ENCODE_METHOD_SIG_Constrained)
     {
@@ -1217,8 +1219,8 @@ BOOL ZapSig::EncodeMethod(
         methodFlags |= ENCODE_METHOD_SIG_InstantiatingStub;
     if (fMethodNeedsInstantiation)
         methodFlags |= ENCODE_METHOD_SIG_MethodInstantiation;
-    if (pMethod->IsAsyncThunkMethod())
-        methodFlags |= ENCODE_METHOD_SIG_AsyncThunk;
+    if (pMethod->IsAsyncHelperMethod())
+        methodFlags |= ENCODE_METHOD_SIG_AsyncHelper;
 
     // Assume that the owner type is going to be needed
     methodFlags |= ENCODE_METHOD_SIG_OwnerType;
diff --git a/src/libraries/System.Formats.Nrbf/src/System/Formats/Nrbf/ArraySinglePrimitiveRecord.cs b/src/libraries/System.Formats.Nrbf/src/System/Formats/Nrbf/ArraySinglePrimitiveRecord.cs
index a13507b97015a0..28616b198fe960 100644
--- a/src/libraries/System.Formats.Nrbf/src/System/Formats/Nrbf/ArraySinglePrimitiveRecord.cs
+++ b/src/libraries/System.Formats.Nrbf/src/System/Formats/Nrbf/ArraySinglePrimitiveRecord.cs
@@ -110,7 +110,7 @@ internal static IReadOnlyList<T> DecodePrimitiveTypes(BinaryReader reader, int c
 
         // It's safe to pre-allocate, as we have ensured there is enough bytes in the stream.
         T[] result = new T[count];
-        Span<byte> resultAsBytes = MemoryMarshal.AsBytes<T>(result);
+        Span<byte> resultAsBytes = MemoryMarshal.AsBytes<T>(result.AsSpan());
 #if NET
         reader.BaseStream.ReadExactly(resultAsBytes);
 #else
@@ -136,7 +136,7 @@ internal static IReadOnlyList<T> DecodePrimitiveTypes(BinaryReader reader, int c
         {
             if (typeof(T) == typeof(short) || typeof(T) == typeof(ushort))
             {
-                Span<short> span = MemoryMarshal.Cast<T, short>(result);
+                Span<short> span = MemoryMarshal.Cast<T, short>(result.AsSpan());
 #if NET
                 BinaryPrimitives.ReverseEndianness(span, span);
 #else
@@ -148,7 +148,7 @@ internal static IReadOnlyList<T> DecodePrimitiveTypes(BinaryReader reader, int c
             }
             else if (typeof(T) == typeof(int) || typeof(T) == typeof(uint) || typeof(T) == typeof(float))
             {
-                Span<int> span = MemoryMarshal.Cast<T, int>(result);
+                Span<int> span = MemoryMarshal.Cast<T, int>(result.AsSpan());
 #if NET
                 BinaryPrimitives.ReverseEndianness(span, span);
 #else
@@ -160,7 +160,7 @@ internal static IReadOnlyList<T> DecodePrimitiveTypes(BinaryReader reader, int c
             }
             else if (typeof(T) == typeof(long) || typeof(T) == typeof(ulong) || typeof(T) == typeof(double))
             {
-                Span<long> span = MemoryMarshal.Cast<T, long>(result);
+                Span<long> span = MemoryMarshal.Cast<T, long>(result.AsSpan());
 #if NET
                 BinaryPrimitives.ReverseEndianness(span, span);
 #else
@@ -176,7 +176,7 @@ internal static IReadOnlyList<T> DecodePrimitiveTypes(BinaryReader reader, int c
         {
             // See DontCastBytesToBooleans test to see what could go wrong.
             bool[] booleans = (bool[])(object)result;
-            resultAsBytes = MemoryMarshal.AsBytes<T>(result);
+            resultAsBytes = MemoryMarshal.AsBytes<T>(result.AsSpan());
             for (int i = 0; i < booleans.Length; i++)
             {
                 // We don't use the bool array to get the value, as an optimizing compiler or JIT could elide this.
diff --git a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs
index e4d8845dd05e81..0581fca63121f3 100644
--- a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs
+++ b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs
@@ -384,7 +384,7 @@ public BigInteger(ReadOnlySpan<byte> value, bool isUnsigned = false, bool isBigE
                     // The bytes parameter is in little-endian byte order.
                     // We can just copy the bytes directly into the uint array.
 
-                    value.Slice(0, wholeUInt32Count * 4).CopyTo(MemoryMarshal.AsBytes<uint>(val));
+                    value.Slice(0, wholeUInt32Count * 4).CopyTo(MemoryMarshal.AsBytes<uint>(val.AsSpan()));
                 }
 
                 // In both of the above cases on big-endian architecture, we need to perform
diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Document/JsonDocument.MetadataDb.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Document/JsonDocument.MetadataDb.cs
index dca61c889c89d8..20cdf3775c0a43 100644
--- a/src/libraries/System.Text.Json/src/System/Text/Json/Document/JsonDocument.MetadataDb.cs
+++ b/src/libraries/System.Text.Json/src/System/Text/Json/Document/JsonDocument.MetadataDb.cs
@@ -381,7 +381,7 @@ internal MetadataDb CopySegment(int startIndex, int endIndex)
                 byte[] newDatabase = new byte[length];
                 _data.AsSpan(startIndex, length).CopyTo(newDatabase);
 
-                Span<int> newDbInts = MemoryMarshal.Cast<byte, int>(newDatabase);
+                Span<int> newDbInts = MemoryMarshal.Cast<byte, int>(newDatabase.AsSpan());
                 int locationOffset = newDbInts[0];
 
                 // Need to nudge one forward to account for the hidden quote on the string.
diff --git a/src/tests/async/pgo.cs b/src/tests/async/pgo.cs
new file mode 100644
index 00000000000000..6e3f14caa7bae1
--- /dev/null
+++ b/src/tests/async/pgo.cs
@@ -0,0 +1,55 @@
+// 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 System.Diagnostics;
+using System.Linq;
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Threading.Tasks;
+using Xunit;
+
+public class Async2Pgo
+{
+    [Fact]
+    public static void EntryPoint()
+    {
+        AsyncEntryPoint().Wait();
+    }
+
+    internal static async2 Task<int> AsyncEntryPoint()
+    {
+        int[] arr = Enumerable.Range(0, 100_000).ToArray();
+
+        int sum = 0;
+        for (int i = 0; i < 4; i++)
+        {
+            for (int j = 0; j < 100; j++)
+                sum += await AggregateDelegateAsync(arr, new AggregateSum(), 0);
+
+            await Task.Delay(100);
+        }
+
+        return sum;
+    }
+
+    private class AggregateSum : I<int>
+    {
+#pragma warning disable CS1998
+        public async2 Task<int> Aggregate(int a, int b) => a + b;
+    }
+    
+    public interface I<T>
+    {
+        public Task<T> Aggregate(T seed, T val);
+    }
+    
+    [MethodImpl(MethodImplOptions.NoInlining)]
+    public static async2 Task<T> AggregateDelegateAsync<T>(T[] arr, I<T> aggregate, T seed)
+    {
+        foreach (T val in arr)
+            seed = await aggregate.Aggregate(seed, val);
+            
+        return seed;
+    }
+}
diff --git a/src/tests/async/pgo.csproj b/src/tests/async/pgo.csproj
new file mode 100644
index 00000000000000..9367a79b2edbb1
--- /dev/null
+++ b/src/tests/async/pgo.csproj
@@ -0,0 +1,8 @@
+<Project Sdk="Microsoft.NET.Sdk.IL">
+  <PropertyGroup>
+    <Optimize>True</Optimize>
+  </PropertyGroup>
+  <ItemGroup>
+    <Compile Include="$(MSBuildProjectName).cs" />
+  </ItemGroup>
+</Project>

From d1a1b71ae3dda1c298a76bb2dc8ead4abd8bdfc2 Mon Sep 17 00:00:00 2001
From: Jakob Botsch Nielsen <jakob.botsch.nielsen@gmail.com>
Date: Tue, 17 Dec 2024 23:26:12 +0100
Subject: [PATCH 133/203] Fixes after merge

---
 src/coreclr/inc/jiteeversionguid.h            |  10 +-
 src/coreclr/jit/async.cpp                     |  15 -
 src/coreclr/jit/flowgraph.cpp                 |   4 +-
 src/coreclr/jit/importer.cpp                  |   2 +-
 .../JitInterface/CorInfoImpl_generated.cs     | 446 +++---------------
 .../aot/jitinterface/jitinterface_generated.h |  20 -
 .../superpmi/superpmi-shared/methodcontext.h  |   1 +
 src/coreclr/vm/comdelegate.cpp                |   2 +-
 .../CompilerServices/RuntimeHelpers.cs        |   5 -
 9 files changed, 69 insertions(+), 436 deletions(-)

diff --git a/src/coreclr/inc/jiteeversionguid.h b/src/coreclr/inc/jiteeversionguid.h
index d4a016647aaa33..db7753db37778b 100644
--- a/src/coreclr/inc/jiteeversionguid.h
+++ b/src/coreclr/inc/jiteeversionguid.h
@@ -43,11 +43,11 @@ typedef const GUID *LPCGUID;
 #define GUID_DEFINED
 #endif // !GUID_DEFINED
 
-constexpr GUID JITEEVersionIdentifier = { /* ed41a83d-5d0a-4ee8-8d4e-51b49560db82 */
-    0xed41a83d,
-    0x5d0a,
-    0x4ee8,
-    {0x8d, 0x4e, 0x51, 0xb4, 0x95, 0x60, 0xdb, 0x82}
+constexpr GUID JITEEVersionIdentifier = { /* 2511e8ef-050b-4a05-bbe6-4bec02b96577 */
+    0x2511e8ef,
+    0x050b,
+    0x4a05,
+    {0xbb, 0xe6, 0x4b, 0xec, 0x02, 0xb9, 0x65, 0x77}
   };
 
 //////////////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/src/coreclr/jit/async.cpp b/src/coreclr/jit/async.cpp
index e0726ccc977e33..e77b25c732fbf0 100644
--- a/src/coreclr/jit/async.cpp
+++ b/src/coreclr/jit/async.cpp
@@ -1306,22 +1306,12 @@ void Async2Transformation::CreateResumptionSwitch()
 {
     BasicBlock* newEntryBB = BasicBlock::New(m_comp, BBJ_ALWAYS);
 
-    bool hadScratchBB = m_comp->fgFirstBBisScratch();
-
     if (m_comp->fgFirstBB->hasProfileWeight())
     {
         newEntryBB->inheritWeight(m_comp->fgFirstBB);
     }
     m_comp->fgFirstBB->bbRefs--;
 
-    if (hadScratchBB)
-    {
-        // If previous first BB was a scratch BB then we have to recreate it
-        // after since later phases may be relying on it.
-        // TODO-Cleanup: The later phases should be creating it if they need it.
-        m_comp->fgFirstBBScratch = nullptr;
-    }
-
     FlowEdge* toPrevEntryEdge = m_comp->fgAddRefPred(m_comp->fgFirstBB, newEntryBB);
     toPrevEntryEdge->setLikelihood(1);
 
@@ -1509,9 +1499,4 @@ void Async2Transformation::CreateResumptionSwitch()
         GenTree* jtrue            = m_comp->gtNewOperNode(GT_JTRUE, TYP_VOID, ltZero);
         LIR::AsRange(checkILOffsetBB).InsertAtEnd(LIR::SeqTree(m_comp, jtrue));
     }
-
-    if (hadScratchBB)
-    {
-        m_comp->fgEnsureFirstBBisScratch();
-    }
 }
diff --git a/src/coreclr/jit/flowgraph.cpp b/src/coreclr/jit/flowgraph.cpp
index 96c84042006b1d..55221f05204618 100644
--- a/src/coreclr/jit/flowgraph.cpp
+++ b/src/coreclr/jit/flowgraph.cpp
@@ -2353,12 +2353,10 @@ PhaseStatus Compiler::fgAddInternal()
 
         if (lvaThisCopyVar != BAD_VAR_NUM)
         {
-            fgEnsureFirstBBisScratch();
-
             ClassLayout* layout = lvaGetDesc(lvaThisCopyVar)->GetLayout();
             GenTree* addr = gtNewLclVarNode(info.compThisArg);
             GenTree* store = gtNewStoreLclVarNode(lvaThisCopyVar, gtNewBlkIndir(layout, addr));
-            Statement* stmt = fgNewStmtAtEnd(fgFirstBB, store);
+            Statement* stmt = fgNewStmtAtBeg(fgFirstBB, store);
 
             JITDUMP("\nCopy \"this\" to V%02u for struct instance method operating on copy\n", lvaThisCopyVar);
             DISPSTMT(stmt);
diff --git a/src/coreclr/jit/importer.cpp b/src/coreclr/jit/importer.cpp
index 086271175d96d9..1015423e9c5586 100644
--- a/src/coreclr/jit/importer.cpp
+++ b/src/coreclr/jit/importer.cpp
@@ -10866,7 +10866,7 @@ void Compiler::impLoadArg(unsigned ilArgNum, IL_OFFSET offset)
             if (lvaThisCopyVar != BAD_VAR_NUM)
             {
                 GenTree* lclAddr = gtNewLclVarAddrNode(lvaThisCopyVar, TYP_BYREF);
-                impPushOnStack(lclAddr, verMakeTypeInfoForLocal(lclNum));
+                impPushOnStack(lclAddr, makeTypeInfoForLocal(lclNum));
                 return;
             }
         }
diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoImpl_generated.cs b/src/coreclr/tools/Common/JitInterface/CorInfoImpl_generated.cs
index 8a1cda46f67583..b760d4eaa71eac 100644
--- a/src/coreclr/tools/Common/JitInterface/CorInfoImpl_generated.cs
+++ b/src/coreclr/tools/Common/JitInterface/CorInfoImpl_generated.cs
@@ -2652,13 +2652,7 @@ private static uint _getJitFlags(IntPtr thisHandle, IntPtr* ppException, CORJIT_
 
         private static IntPtr GetUnmanagedCallbacks()
         {
-<<<<<<< HEAD
-            void** callbacks = (void**)Marshal.AllocCoTaskMem(sizeof(IntPtr) * 178);
-||||||| 741390a6850
-            void** callbacks = (void**)Marshal.AllocCoTaskMem(sizeof(IntPtr) * 176);
-=======
-            void** callbacks = (void**)Marshal.AllocCoTaskMem(sizeof(IntPtr) * 177);
->>>>>>> 040cbe276907174316e2cc07b35814b3069874a6
+            void** callbacks = (void**)Marshal.AllocCoTaskMem(sizeof(IntPtr) * 179);
 
             callbacks[0] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_METHOD_STRUCT_*, byte>)&_isIntrinsic;
             callbacks[1] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_METHOD_STRUCT_*, byte>)&_notifyMethodInfoUsage;
@@ -2678,327 +2672,6 @@ private static IntPtr GetUnmanagedCallbacks()
             callbacks[15] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_METHOD_STRUCT_*, uint*, uint*, bool*, void>)&_getMethodVTableOffset;
             callbacks[16] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_DEVIRTUALIZATION_INFO*, byte>)&_resolveVirtualMethod;
             callbacks[17] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_METHOD_STRUCT_*, bool*, CORINFO_METHOD_STRUCT_*>)&_getUnboxedEntry;
-<<<<<<< HEAD
-            callbacks[18] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_CLASS_STRUCT_*, CORINFO_CLASS_STRUCT_*>)&_getDefaultComparerClass;
-            callbacks[19] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_CLASS_STRUCT_*, CORINFO_CLASS_STRUCT_*>)&_getDefaultEqualityComparerClass;
-            callbacks[20] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_CLASS_STRUCT_*, CORINFO_CLASS_STRUCT_*>)&_getSZArrayHelperEnumeratorClass;
-            callbacks[21] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_RESOLVED_TOKEN*, CORINFO_METHOD_STRUCT_*, CORINFO_GENERICHANDLE_RESULT*, void>)&_expandRawHandleIntrinsic;
-            callbacks[22] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_CLASS_STRUCT_*, byte>)&_isIntrinsicType;
-            callbacks[23] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_METHOD_STRUCT_*, CORINFO_SIG_INFO*, bool*, CorInfoCallConvExtension>)&_getUnmanagedCallConv;
-            callbacks[24] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_METHOD_STRUCT_*, CORINFO_SIG_INFO*, byte>)&_pInvokeMarshalingRequired;
-            callbacks[25] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_CLASS_STRUCT_*, CORINFO_METHOD_STRUCT_*, byte>)&_satisfiesMethodConstraints;
-            callbacks[26] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_METHOD_STRUCT_*, void>)&_methodMustBeLoadedBeforeCodeIsRun;
-            callbacks[27] = (delegate* unmanaged<IntPtr, IntPtr*, IntPtr*, IntPtr**, void>)&_getGSCookie;
-            callbacks[28] = (delegate* unmanaged<IntPtr, IntPtr*, PatchpointInfo*, void>)&_setPatchpointInfo;
-            callbacks[29] = (delegate* unmanaged<IntPtr, IntPtr*, uint*, PatchpointInfo*>)&_getOSRInfo;
-            callbacks[30] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_RESOLVED_TOKEN*, void>)&_resolveToken;
-            callbacks[31] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_MODULE_STRUCT_*, uint, CORINFO_CONTEXT_STRUCT*, CORINFO_SIG_INFO*, void>)&_findSig;
-            callbacks[32] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_MODULE_STRUCT_*, uint, CORINFO_CONTEXT_STRUCT*, CORINFO_SIG_INFO*, void>)&_findCallSiteSig;
-            callbacks[33] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_RESOLVED_TOKEN*, CORINFO_CLASS_STRUCT_*>)&_getTokenTypeAsHandle;
-            callbacks[34] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_MODULE_STRUCT_*, uint, char*, int, int, int>)&_getStringLiteral;
-            callbacks[35] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_OBJECT_STRUCT_*, byte*, UIntPtr, UIntPtr*, UIntPtr>)&_printObjectDescription;
-            callbacks[36] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_CLASS_STRUCT_*, CorInfoType>)&_asCorInfoType;
-            callbacks[37] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_CLASS_STRUCT_*, byte**, byte*>)&_getClassNameFromMetadata;
-            callbacks[38] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_CLASS_STRUCT_*, uint, CORINFO_CLASS_STRUCT_*>)&_getTypeInstantiationArgument;
-            callbacks[39] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_CLASS_STRUCT_*, byte*, UIntPtr, UIntPtr*, UIntPtr>)&_printClassName;
-            callbacks[40] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_CLASS_STRUCT_*, byte>)&_isValueClass;
-            callbacks[41] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_CLASS_STRUCT_*, uint>)&_getClassAttribs;
-            callbacks[42] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_CLASS_STRUCT_*, byte*>)&_getClassAssemblyName;
-            callbacks[43] = (delegate* unmanaged<IntPtr, IntPtr*, UIntPtr, void*>)&_LongLifetimeMalloc;
-            callbacks[44] = (delegate* unmanaged<IntPtr, IntPtr*, void*, void>)&_LongLifetimeFree;
-            callbacks[45] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_CLASS_STRUCT_*, CORINFO_CONST_LOOKUP*, int*, byte>)&_getIsClassInitedFlagAddress;
-            callbacks[46] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_CLASS_STRUCT_*, UIntPtr>)&_getClassThreadStaticDynamicInfo;
-            callbacks[47] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_CLASS_STRUCT_*, UIntPtr>)&_getClassStaticDynamicInfo;
-            callbacks[48] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_CLASS_STRUCT_*, byte, CORINFO_CONST_LOOKUP*, byte>)&_getStaticBaseAddress;
-            callbacks[49] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_CLASS_STRUCT_*, uint>)&_getClassSize;
-            callbacks[50] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_CLASS_STRUCT_*, uint>)&_getHeapClassSize;
-            callbacks[51] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_CLASS_STRUCT_*, byte>)&_canAllocateOnStack;
-            callbacks[52] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_CLASS_STRUCT_*, byte, uint>)&_getClassAlignmentRequirement;
-            callbacks[53] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_CLASS_STRUCT_*, byte*, uint>)&_getClassGClayout;
-            callbacks[54] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_CLASS_STRUCT_*, uint>)&_getClassNumInstanceFields;
-            callbacks[55] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_CLASS_STRUCT_*, int, CORINFO_FIELD_STRUCT_*>)&_getFieldInClass;
-            callbacks[56] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_CLASS_STRUCT_*, CORINFO_TYPE_LAYOUT_NODE*, UIntPtr*, GetTypeLayoutResult>)&_getTypeLayout;
-            callbacks[57] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_METHOD_STRUCT_*, byte*, byte, byte>)&_checkMethodModifier;
-            callbacks[58] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_CLASS_STRUCT_*, bool*, CorInfoHelpFunc>)&_getNewHelper;
-            callbacks[59] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_CLASS_STRUCT_*, CorInfoHelpFunc>)&_getNewArrHelper;
-            callbacks[60] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_RESOLVED_TOKEN*, byte, CorInfoHelpFunc>)&_getCastingHelper;
-            callbacks[61] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_CLASS_STRUCT_*, CorInfoHelpFunc>)&_getSharedCCtorHelper;
-            callbacks[62] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_CLASS_STRUCT_*, CORINFO_CLASS_STRUCT_*>)&_getTypeForBox;
-            callbacks[63] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_CLASS_STRUCT_*, CORINFO_CLASS_STRUCT_*>)&_getTypeForBoxOnStack;
-            callbacks[64] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_CLASS_STRUCT_*, CorInfoHelpFunc>)&_getBoxHelper;
-            callbacks[65] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_CLASS_STRUCT_*, CorInfoHelpFunc>)&_getUnBoxHelper;
-            callbacks[66] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_CLASS_STRUCT_*, CORINFO_OBJECT_STRUCT_*>)&_getRuntimeTypePointer;
-            callbacks[67] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_OBJECT_STRUCT_*, byte>)&_isObjectImmutable;
-            callbacks[68] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_OBJECT_STRUCT_*, int, ushort*, byte>)&_getStringChar;
-            callbacks[69] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_OBJECT_STRUCT_*, CORINFO_CLASS_STRUCT_*>)&_getObjectType;
-            callbacks[70] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_RESOLVED_TOKEN*, CORINFO_LOOKUP_KIND*, CorInfoHelpFunc, CORINFO_METHOD_STRUCT_*, CORINFO_CONST_LOOKUP*, byte>)&_getReadyToRunHelper;
-            callbacks[71] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_RESOLVED_TOKEN*, mdToken, CORINFO_CLASS_STRUCT_*, CORINFO_METHOD_STRUCT_*, CORINFO_LOOKUP*, void>)&_getReadyToRunDelegateCtorHelper;
-            callbacks[72] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_FIELD_STRUCT_*, CORINFO_METHOD_STRUCT_*, CORINFO_CONTEXT_STRUCT*, CorInfoInitClassResult>)&_initClass;
-            callbacks[73] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_CLASS_STRUCT_*, void>)&_classMustBeLoadedBeforeCodeIsRun;
-            callbacks[74] = (delegate* unmanaged<IntPtr, IntPtr*, CorInfoClassId, CORINFO_CLASS_STRUCT_*>)&_getBuiltinClass;
-            callbacks[75] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_CLASS_STRUCT_*, CorInfoType>)&_getTypeForPrimitiveValueClass;
-            callbacks[76] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_CLASS_STRUCT_*, CorInfoType>)&_getTypeForPrimitiveNumericClass;
-            callbacks[77] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_CLASS_STRUCT_*, CORINFO_CLASS_STRUCT_*, byte>)&_canCast;
-            callbacks[78] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_CLASS_STRUCT_*, CORINFO_CLASS_STRUCT_*, TypeCompareState>)&_compareTypesForCast;
-            callbacks[79] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_CLASS_STRUCT_*, CORINFO_CLASS_STRUCT_*, TypeCompareState>)&_compareTypesForEquality;
-            callbacks[80] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_CLASS_STRUCT_*, CORINFO_CLASS_STRUCT_*, byte>)&_isMoreSpecificType;
-            callbacks[81] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_CLASS_STRUCT_*, byte>)&_isExactType;
-            callbacks[82] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_CLASS_STRUCT_*, TypeCompareState>)&_isGenericType;
-            callbacks[83] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_CLASS_STRUCT_*, TypeCompareState>)&_isNullableType;
-            callbacks[84] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_CLASS_STRUCT_*, CORINFO_CLASS_STRUCT_**, TypeCompareState>)&_isEnum;
-            callbacks[85] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_CLASS_STRUCT_*, CORINFO_CLASS_STRUCT_*>)&_getParentType;
-            callbacks[86] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_CLASS_STRUCT_*, CORINFO_CLASS_STRUCT_**, CorInfoType>)&_getChildType;
-            callbacks[87] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_CLASS_STRUCT_*, byte>)&_isSDArray;
-            callbacks[88] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_CLASS_STRUCT_*, uint>)&_getArrayRank;
-            callbacks[89] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_METHOD_STRUCT_*, CorInfoArrayIntrinsic>)&_getArrayIntrinsicID;
-            callbacks[90] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_FIELD_STRUCT_*, uint, void*>)&_getArrayInitializationData;
-            callbacks[91] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_RESOLVED_TOKEN*, CORINFO_METHOD_STRUCT_*, CORINFO_HELPER_DESC*, CorInfoIsAccessAllowedResult>)&_canAccessClass;
-            callbacks[92] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_FIELD_STRUCT_*, byte*, UIntPtr, UIntPtr*, UIntPtr>)&_printFieldName;
-            callbacks[93] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_FIELD_STRUCT_*, CORINFO_CLASS_STRUCT_*>)&_getFieldClass;
-            callbacks[94] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_FIELD_STRUCT_*, CORINFO_CLASS_STRUCT_**, CORINFO_CLASS_STRUCT_*, CorInfoType>)&_getFieldType;
-            callbacks[95] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_FIELD_STRUCT_*, uint>)&_getFieldOffset;
-            callbacks[96] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_RESOLVED_TOKEN*, CORINFO_METHOD_STRUCT_*, CORINFO_ACCESS_FLAGS, CORINFO_FIELD_INFO*, void>)&_getFieldInfo;
-            callbacks[97] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_FIELD_STRUCT_*, byte, uint>)&_getThreadLocalFieldInfo;
-            callbacks[98] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_THREAD_STATIC_BLOCKS_INFO*, void>)&_getThreadLocalStaticBlocksInfo;
-            callbacks[99] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_THREAD_STATIC_INFO_NATIVEAOT*, void>)&_getThreadLocalStaticInfo_NativeAOT;
-            callbacks[100] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_FIELD_STRUCT_*, byte>)&_isFieldStatic;
-            callbacks[101] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_OBJECT_STRUCT_*, int>)&_getArrayOrStringLength;
-            callbacks[102] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_METHOD_STRUCT_*, uint*, uint**, BoundaryTypes*, void>)&_getBoundaries;
-            callbacks[103] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_METHOD_STRUCT_*, uint, OffsetMapping*, void>)&_setBoundaries;
-            callbacks[104] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_METHOD_STRUCT_*, uint*, ILVarInfo**, bool*, void>)&_getVars;
-            callbacks[105] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_METHOD_STRUCT_*, uint, NativeVarInfo*, void>)&_setVars;
-            callbacks[106] = (delegate* unmanaged<IntPtr, IntPtr*, InlineTreeNode*, uint, RichOffsetMapping*, uint, void>)&_reportRichMappings;
-            callbacks[107] = (delegate* unmanaged<IntPtr, IntPtr*, byte*, void*, UIntPtr, void>)&_reportMetadata;
-            callbacks[108] = (delegate* unmanaged<IntPtr, IntPtr*, UIntPtr, void*>)&_allocateArray;
-            callbacks[109] = (delegate* unmanaged<IntPtr, IntPtr*, void*, void>)&_freeArray;
-            callbacks[110] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_ARG_LIST_STRUCT_*, CORINFO_ARG_LIST_STRUCT_*>)&_getArgNext;
-            callbacks[111] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_SIG_INFO*, CORINFO_ARG_LIST_STRUCT_*, CORINFO_CLASS_STRUCT_**, CorInfoTypeWithMod>)&_getArgType;
-            callbacks[112] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_CLASS_STRUCT_*, int, CORINFO_CLASS_STRUCT_**, int>)&_getExactClasses;
-            callbacks[113] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_SIG_INFO*, CORINFO_ARG_LIST_STRUCT_*, CORINFO_CLASS_STRUCT_*>)&_getArgClass;
-            callbacks[114] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_CLASS_STRUCT_*, CorInfoHFAElemType>)&_getHFAType;
-            callbacks[115] = (delegate* unmanaged<IntPtr, IntPtr*, void*, void*, byte>)&_runWithErrorTrap;
-            callbacks[116] = (delegate* unmanaged<IntPtr, IntPtr*, void*, void*, byte>)&_runWithSPMIErrorTrap;
-            callbacks[117] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_EE_INFO*, void>)&_getEEInfo;
-            callbacks[118] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_ASYNC2_INFO*, void>)&_getAsync2Info;
-            callbacks[119] = (delegate* unmanaged<IntPtr, IntPtr*, char*>)&_getJitTimeLogFilename;
-            callbacks[120] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_METHOD_STRUCT_*, mdToken>)&_getMethodDefFromMethod;
-            callbacks[121] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_METHOD_STRUCT_*, byte*, UIntPtr, UIntPtr*, UIntPtr>)&_printMethodName;
-            callbacks[122] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_METHOD_STRUCT_*, byte**, byte**, byte**, UIntPtr, byte*>)&_getMethodNameFromMetadata;
-            callbacks[123] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_METHOD_STRUCT_*, uint>)&_getMethodHash;
-            callbacks[124] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_CLASS_STRUCT_*, SYSTEMV_AMD64_CORINFO_STRUCT_REG_PASSING_DESCRIPTOR*, byte>)&_getSystemVAmd64PassStructInRegisterDescriptor;
-            callbacks[125] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_CLASS_STRUCT_*, CORINFO_SWIFT_LOWERING*, void>)&_getSwiftLowering;
-            callbacks[126] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_CLASS_STRUCT_*, CORINFO_FPSTRUCT_LOWERING*, void>)&_getFpStructLowering;
-            callbacks[127] = (delegate* unmanaged<IntPtr, IntPtr*, void**, uint>)&_getThreadTLSIndex;
-            callbacks[128] = (delegate* unmanaged<IntPtr, IntPtr*, void**, int*>)&_getAddrOfCaptureThreadGlobal;
-            callbacks[129] = (delegate* unmanaged<IntPtr, IntPtr*, CorInfoHelpFunc, void**, void*>)&_getHelperFtn;
-            callbacks[130] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_METHOD_STRUCT_*, CORINFO_CONST_LOOKUP*, CORINFO_ACCESS_FLAGS, void>)&_getFunctionEntryPoint;
-            callbacks[131] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_METHOD_STRUCT_*, byte, CORINFO_CONST_LOOKUP*, void>)&_getFunctionFixedEntryPoint;
-            callbacks[132] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_METHOD_STRUCT_*, void**, void*>)&_getMethodSync;
-            callbacks[133] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_MODULE_STRUCT_*, CorInfoHelpFunc>)&_getLazyStringLiteralHelper;
-            callbacks[134] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_MODULE_STRUCT_*, void**, CORINFO_MODULE_STRUCT_*>)&_embedModuleHandle;
-            callbacks[135] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_CLASS_STRUCT_*, void**, CORINFO_CLASS_STRUCT_*>)&_embedClassHandle;
-            callbacks[136] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_METHOD_STRUCT_*, void**, CORINFO_METHOD_STRUCT_*>)&_embedMethodHandle;
-            callbacks[137] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_FIELD_STRUCT_*, void**, CORINFO_FIELD_STRUCT_*>)&_embedFieldHandle;
-            callbacks[138] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_RESOLVED_TOKEN*, byte, CORINFO_METHOD_STRUCT_*, CORINFO_GENERICHANDLE_RESULT*, void>)&_embedGenericHandle;
-            callbacks[139] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_METHOD_STRUCT_*, CORINFO_LOOKUP_KIND*, void>)&_getLocationOfThisType;
-            callbacks[140] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_METHOD_STRUCT_*, CORINFO_CONST_LOOKUP*, void>)&_getAddressOfPInvokeTarget;
-            callbacks[141] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_SIG_INFO*, void**, void*>)&_GetCookieForPInvokeCalliSig;
-            callbacks[142] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_SIG_INFO*, byte>)&_canGetCookieForPInvokeCalliSig;
-            callbacks[143] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_METHOD_STRUCT_*, CORINFO_JUST_MY_CODE_HANDLE_**, CORINFO_JUST_MY_CODE_HANDLE_*>)&_getJustMyCodeHandle;
-            callbacks[144] = (delegate* unmanaged<IntPtr, IntPtr*, bool*, void**, bool*, void>)&_GetProfilingHandle;
-            callbacks[145] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_RESOLVED_TOKEN*, CORINFO_RESOLVED_TOKEN*, CORINFO_METHOD_STRUCT_*, CORINFO_CALLINFO_FLAGS, CORINFO_CALL_INFO*, void>)&_getCallInfo;
-            callbacks[146] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_FIELD_STRUCT_*, byte*, int, int, byte, byte>)&_getStaticFieldContent;
-            callbacks[147] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_OBJECT_STRUCT_*, byte*, int, int, byte>)&_getObjectContent;
-            callbacks[148] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_FIELD_STRUCT_*, byte*, CORINFO_CLASS_STRUCT_*>)&_getStaticFieldCurrentClass;
-            callbacks[149] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_SIG_INFO*, void**, IntPtr>)&_getVarArgsHandle;
-            callbacks[150] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_SIG_INFO*, byte>)&_canGetVarArgsHandle;
-            callbacks[151] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_MODULE_STRUCT_*, mdToken, void**, InfoAccessType>)&_constructStringLiteral;
-            callbacks[152] = (delegate* unmanaged<IntPtr, IntPtr*, void**, InfoAccessType>)&_emptyStringLiteral;
-            callbacks[153] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_FIELD_STRUCT_*, void**, uint>)&_getFieldThreadLocalStoreID;
-            callbacks[154] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_METHOD_STRUCT_*, CORINFO_CLASS_STRUCT_*, CORINFO_METHOD_STRUCT_*, DelegateCtorArgs*, CORINFO_METHOD_STRUCT_*>)&_GetDelegateCtor;
-            callbacks[155] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_METHOD_STRUCT_*, void>)&_MethodCompileComplete;
-            callbacks[156] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_RESOLVED_TOKEN*, CORINFO_SIG_INFO*, CORINFO_GET_TAILCALL_HELPERS_FLAGS, CORINFO_TAILCALL_HELPERS*, byte>)&_getTailCallHelpers;
-            callbacks[157] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_METHOD_STRUCT_*>)&_getAsyncResumptionStub;
-            callbacks[158] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_RESOLVED_TOKEN*, byte, byte>)&_convertPInvokeCalliToCall;
-            callbacks[159] = (delegate* unmanaged<IntPtr, IntPtr*, InstructionSet, byte, byte>)&_notifyInstructionSetUsage;
-            callbacks[160] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_CONST_LOOKUP*, void>)&_updateEntryPointForTailCall;
-            callbacks[161] = (delegate* unmanaged<IntPtr, IntPtr*, AllocMemArgs*, void>)&_allocMem;
-            callbacks[162] = (delegate* unmanaged<IntPtr, IntPtr*, byte, byte, uint, void>)&_reserveUnwindInfo;
-            callbacks[163] = (delegate* unmanaged<IntPtr, IntPtr*, byte*, byte*, uint, uint, uint, byte*, CorJitFuncKind, void>)&_allocUnwindInfo;
-            callbacks[164] = (delegate* unmanaged<IntPtr, IntPtr*, UIntPtr, void*>)&_allocGCInfo;
-            callbacks[165] = (delegate* unmanaged<IntPtr, IntPtr*, uint, void>)&_setEHcount;
-            callbacks[166] = (delegate* unmanaged<IntPtr, IntPtr*, uint, CORINFO_EH_CLAUSE*, void>)&_setEHinfo;
-            callbacks[167] = (delegate* unmanaged<IntPtr, IntPtr*, uint, byte*, IntPtr, byte>)&_logMsg;
-            callbacks[168] = (delegate* unmanaged<IntPtr, IntPtr*, byte*, int, byte*, int>)&_doAssert;
-            callbacks[169] = (delegate* unmanaged<IntPtr, IntPtr*, CorJitResult, void>)&_reportFatalError;
-            callbacks[170] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_METHOD_STRUCT_*, PgoInstrumentationSchema**, uint*, byte**, PgoSource*, bool*, HRESULT>)&_getPgoInstrumentationResults;
-            callbacks[171] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_METHOD_STRUCT_*, PgoInstrumentationSchema*, uint, byte**, HRESULT>)&_allocPgoInstrumentationBySchema;
-            callbacks[172] = (delegate* unmanaged<IntPtr, IntPtr*, uint, CORINFO_SIG_INFO*, CORINFO_METHOD_STRUCT_*, void>)&_recordCallSite;
-            callbacks[173] = (delegate* unmanaged<IntPtr, IntPtr*, void*, void*, void*, ushort, int, void>)&_recordRelocation;
-            callbacks[174] = (delegate* unmanaged<IntPtr, IntPtr*, void*, ushort>)&_getRelocTypeHint;
-            callbacks[175] = (delegate* unmanaged<IntPtr, IntPtr*, uint>)&_getExpectedTargetArchitecture;
-            callbacks[176] = (delegate* unmanaged<IntPtr, IntPtr*, CORJIT_FLAGS*, uint, uint>)&_getJitFlags;
-            callbacks[177] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_CLASS_STRUCT_*, CORINFO_METHOD_STRUCT_*>)&_getSpecialCopyHelper;
-||||||| 741390a6850
-            callbacks[18] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_CLASS_STRUCT_*, CORINFO_CLASS_STRUCT_*>)&_getDefaultComparerClass;
-            callbacks[19] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_CLASS_STRUCT_*, CORINFO_CLASS_STRUCT_*>)&_getDefaultEqualityComparerClass;
-            callbacks[20] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_CLASS_STRUCT_*, CORINFO_CLASS_STRUCT_*>)&_getSZArrayHelperEnumeratorClass;
-            callbacks[21] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_RESOLVED_TOKEN*, CORINFO_METHOD_STRUCT_*, CORINFO_GENERICHANDLE_RESULT*, void>)&_expandRawHandleIntrinsic;
-            callbacks[22] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_CLASS_STRUCT_*, byte>)&_isIntrinsicType;
-            callbacks[23] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_METHOD_STRUCT_*, CORINFO_SIG_INFO*, bool*, CorInfoCallConvExtension>)&_getUnmanagedCallConv;
-            callbacks[24] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_METHOD_STRUCT_*, CORINFO_SIG_INFO*, byte>)&_pInvokeMarshalingRequired;
-            callbacks[25] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_CLASS_STRUCT_*, CORINFO_METHOD_STRUCT_*, byte>)&_satisfiesMethodConstraints;
-            callbacks[26] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_METHOD_STRUCT_*, void>)&_methodMustBeLoadedBeforeCodeIsRun;
-            callbacks[27] = (delegate* unmanaged<IntPtr, IntPtr*, IntPtr*, IntPtr**, void>)&_getGSCookie;
-            callbacks[28] = (delegate* unmanaged<IntPtr, IntPtr*, PatchpointInfo*, void>)&_setPatchpointInfo;
-            callbacks[29] = (delegate* unmanaged<IntPtr, IntPtr*, uint*, PatchpointInfo*>)&_getOSRInfo;
-            callbacks[30] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_RESOLVED_TOKEN*, void>)&_resolveToken;
-            callbacks[31] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_MODULE_STRUCT_*, uint, CORINFO_CONTEXT_STRUCT*, CORINFO_SIG_INFO*, void>)&_findSig;
-            callbacks[32] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_MODULE_STRUCT_*, uint, CORINFO_CONTEXT_STRUCT*, CORINFO_SIG_INFO*, void>)&_findCallSiteSig;
-            callbacks[33] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_RESOLVED_TOKEN*, CORINFO_CLASS_STRUCT_*>)&_getTokenTypeAsHandle;
-            callbacks[34] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_MODULE_STRUCT_*, uint, char*, int, int, int>)&_getStringLiteral;
-            callbacks[35] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_OBJECT_STRUCT_*, byte*, UIntPtr, UIntPtr*, UIntPtr>)&_printObjectDescription;
-            callbacks[36] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_CLASS_STRUCT_*, CorInfoType>)&_asCorInfoType;
-            callbacks[37] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_CLASS_STRUCT_*, byte**, byte*>)&_getClassNameFromMetadata;
-            callbacks[38] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_CLASS_STRUCT_*, uint, CORINFO_CLASS_STRUCT_*>)&_getTypeInstantiationArgument;
-            callbacks[39] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_CLASS_STRUCT_*, byte*, UIntPtr, UIntPtr*, UIntPtr>)&_printClassName;
-            callbacks[40] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_CLASS_STRUCT_*, byte>)&_isValueClass;
-            callbacks[41] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_CLASS_STRUCT_*, uint>)&_getClassAttribs;
-            callbacks[42] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_CLASS_STRUCT_*, byte*>)&_getClassAssemblyName;
-            callbacks[43] = (delegate* unmanaged<IntPtr, IntPtr*, UIntPtr, void*>)&_LongLifetimeMalloc;
-            callbacks[44] = (delegate* unmanaged<IntPtr, IntPtr*, void*, void>)&_LongLifetimeFree;
-            callbacks[45] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_CLASS_STRUCT_*, CORINFO_CONST_LOOKUP*, int*, byte>)&_getIsClassInitedFlagAddress;
-            callbacks[46] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_CLASS_STRUCT_*, UIntPtr>)&_getClassThreadStaticDynamicInfo;
-            callbacks[47] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_CLASS_STRUCT_*, UIntPtr>)&_getClassStaticDynamicInfo;
-            callbacks[48] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_CLASS_STRUCT_*, byte, CORINFO_CONST_LOOKUP*, byte>)&_getStaticBaseAddress;
-            callbacks[49] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_CLASS_STRUCT_*, uint>)&_getClassSize;
-            callbacks[50] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_CLASS_STRUCT_*, uint>)&_getHeapClassSize;
-            callbacks[51] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_CLASS_STRUCT_*, byte>)&_canAllocateOnStack;
-            callbacks[52] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_CLASS_STRUCT_*, byte, uint>)&_getClassAlignmentRequirement;
-            callbacks[53] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_CLASS_STRUCT_*, byte*, uint>)&_getClassGClayout;
-            callbacks[54] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_CLASS_STRUCT_*, uint>)&_getClassNumInstanceFields;
-            callbacks[55] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_CLASS_STRUCT_*, int, CORINFO_FIELD_STRUCT_*>)&_getFieldInClass;
-            callbacks[56] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_CLASS_STRUCT_*, CORINFO_TYPE_LAYOUT_NODE*, UIntPtr*, GetTypeLayoutResult>)&_getTypeLayout;
-            callbacks[57] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_METHOD_STRUCT_*, byte*, byte, byte>)&_checkMethodModifier;
-            callbacks[58] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_CLASS_STRUCT_*, bool*, CorInfoHelpFunc>)&_getNewHelper;
-            callbacks[59] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_CLASS_STRUCT_*, CorInfoHelpFunc>)&_getNewArrHelper;
-            callbacks[60] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_RESOLVED_TOKEN*, byte, CorInfoHelpFunc>)&_getCastingHelper;
-            callbacks[61] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_CLASS_STRUCT_*, CorInfoHelpFunc>)&_getSharedCCtorHelper;
-            callbacks[62] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_CLASS_STRUCT_*, CORINFO_CLASS_STRUCT_*>)&_getTypeForBox;
-            callbacks[63] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_CLASS_STRUCT_*, CORINFO_CLASS_STRUCT_*>)&_getTypeForBoxOnStack;
-            callbacks[64] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_CLASS_STRUCT_*, CorInfoHelpFunc>)&_getBoxHelper;
-            callbacks[65] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_CLASS_STRUCT_*, CorInfoHelpFunc>)&_getUnBoxHelper;
-            callbacks[66] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_CLASS_STRUCT_*, CORINFO_OBJECT_STRUCT_*>)&_getRuntimeTypePointer;
-            callbacks[67] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_OBJECT_STRUCT_*, byte>)&_isObjectImmutable;
-            callbacks[68] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_OBJECT_STRUCT_*, int, ushort*, byte>)&_getStringChar;
-            callbacks[69] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_OBJECT_STRUCT_*, CORINFO_CLASS_STRUCT_*>)&_getObjectType;
-            callbacks[70] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_RESOLVED_TOKEN*, CORINFO_LOOKUP_KIND*, CorInfoHelpFunc, CORINFO_METHOD_STRUCT_*, CORINFO_CONST_LOOKUP*, byte>)&_getReadyToRunHelper;
-            callbacks[71] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_RESOLVED_TOKEN*, mdToken, CORINFO_CLASS_STRUCT_*, CORINFO_METHOD_STRUCT_*, CORINFO_LOOKUP*, void>)&_getReadyToRunDelegateCtorHelper;
-            callbacks[72] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_FIELD_STRUCT_*, CORINFO_METHOD_STRUCT_*, CORINFO_CONTEXT_STRUCT*, CorInfoInitClassResult>)&_initClass;
-            callbacks[73] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_CLASS_STRUCT_*, void>)&_classMustBeLoadedBeforeCodeIsRun;
-            callbacks[74] = (delegate* unmanaged<IntPtr, IntPtr*, CorInfoClassId, CORINFO_CLASS_STRUCT_*>)&_getBuiltinClass;
-            callbacks[75] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_CLASS_STRUCT_*, CorInfoType>)&_getTypeForPrimitiveValueClass;
-            callbacks[76] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_CLASS_STRUCT_*, CorInfoType>)&_getTypeForPrimitiveNumericClass;
-            callbacks[77] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_CLASS_STRUCT_*, CORINFO_CLASS_STRUCT_*, byte>)&_canCast;
-            callbacks[78] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_CLASS_STRUCT_*, CORINFO_CLASS_STRUCT_*, TypeCompareState>)&_compareTypesForCast;
-            callbacks[79] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_CLASS_STRUCT_*, CORINFO_CLASS_STRUCT_*, TypeCompareState>)&_compareTypesForEquality;
-            callbacks[80] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_CLASS_STRUCT_*, CORINFO_CLASS_STRUCT_*, byte>)&_isMoreSpecificType;
-            callbacks[81] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_CLASS_STRUCT_*, byte>)&_isExactType;
-            callbacks[82] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_CLASS_STRUCT_*, TypeCompareState>)&_isGenericType;
-            callbacks[83] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_CLASS_STRUCT_*, TypeCompareState>)&_isNullableType;
-            callbacks[84] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_CLASS_STRUCT_*, CORINFO_CLASS_STRUCT_**, TypeCompareState>)&_isEnum;
-            callbacks[85] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_CLASS_STRUCT_*, CORINFO_CLASS_STRUCT_*>)&_getParentType;
-            callbacks[86] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_CLASS_STRUCT_*, CORINFO_CLASS_STRUCT_**, CorInfoType>)&_getChildType;
-            callbacks[87] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_CLASS_STRUCT_*, byte>)&_isSDArray;
-            callbacks[88] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_CLASS_STRUCT_*, uint>)&_getArrayRank;
-            callbacks[89] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_METHOD_STRUCT_*, CorInfoArrayIntrinsic>)&_getArrayIntrinsicID;
-            callbacks[90] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_FIELD_STRUCT_*, uint, void*>)&_getArrayInitializationData;
-            callbacks[91] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_RESOLVED_TOKEN*, CORINFO_METHOD_STRUCT_*, CORINFO_HELPER_DESC*, CorInfoIsAccessAllowedResult>)&_canAccessClass;
-            callbacks[92] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_FIELD_STRUCT_*, byte*, UIntPtr, UIntPtr*, UIntPtr>)&_printFieldName;
-            callbacks[93] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_FIELD_STRUCT_*, CORINFO_CLASS_STRUCT_*>)&_getFieldClass;
-            callbacks[94] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_FIELD_STRUCT_*, CORINFO_CLASS_STRUCT_**, CORINFO_CLASS_STRUCT_*, CorInfoType>)&_getFieldType;
-            callbacks[95] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_FIELD_STRUCT_*, uint>)&_getFieldOffset;
-            callbacks[96] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_RESOLVED_TOKEN*, CORINFO_METHOD_STRUCT_*, CORINFO_ACCESS_FLAGS, CORINFO_FIELD_INFO*, void>)&_getFieldInfo;
-            callbacks[97] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_FIELD_STRUCT_*, byte, uint>)&_getThreadLocalFieldInfo;
-            callbacks[98] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_THREAD_STATIC_BLOCKS_INFO*, void>)&_getThreadLocalStaticBlocksInfo;
-            callbacks[99] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_THREAD_STATIC_INFO_NATIVEAOT*, void>)&_getThreadLocalStaticInfo_NativeAOT;
-            callbacks[100] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_FIELD_STRUCT_*, byte>)&_isFieldStatic;
-            callbacks[101] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_OBJECT_STRUCT_*, int>)&_getArrayOrStringLength;
-            callbacks[102] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_METHOD_STRUCT_*, uint*, uint**, BoundaryTypes*, void>)&_getBoundaries;
-            callbacks[103] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_METHOD_STRUCT_*, uint, OffsetMapping*, void>)&_setBoundaries;
-            callbacks[104] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_METHOD_STRUCT_*, uint*, ILVarInfo**, bool*, void>)&_getVars;
-            callbacks[105] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_METHOD_STRUCT_*, uint, NativeVarInfo*, void>)&_setVars;
-            callbacks[106] = (delegate* unmanaged<IntPtr, IntPtr*, InlineTreeNode*, uint, RichOffsetMapping*, uint, void>)&_reportRichMappings;
-            callbacks[107] = (delegate* unmanaged<IntPtr, IntPtr*, byte*, void*, UIntPtr, void>)&_reportMetadata;
-            callbacks[108] = (delegate* unmanaged<IntPtr, IntPtr*, UIntPtr, void*>)&_allocateArray;
-            callbacks[109] = (delegate* unmanaged<IntPtr, IntPtr*, void*, void>)&_freeArray;
-            callbacks[110] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_ARG_LIST_STRUCT_*, CORINFO_ARG_LIST_STRUCT_*>)&_getArgNext;
-            callbacks[111] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_SIG_INFO*, CORINFO_ARG_LIST_STRUCT_*, CORINFO_CLASS_STRUCT_**, CorInfoTypeWithMod>)&_getArgType;
-            callbacks[112] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_CLASS_STRUCT_*, int, CORINFO_CLASS_STRUCT_**, int>)&_getExactClasses;
-            callbacks[113] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_SIG_INFO*, CORINFO_ARG_LIST_STRUCT_*, CORINFO_CLASS_STRUCT_*>)&_getArgClass;
-            callbacks[114] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_CLASS_STRUCT_*, CorInfoHFAElemType>)&_getHFAType;
-            callbacks[115] = (delegate* unmanaged<IntPtr, IntPtr*, void*, void*, byte>)&_runWithErrorTrap;
-            callbacks[116] = (delegate* unmanaged<IntPtr, IntPtr*, void*, void*, byte>)&_runWithSPMIErrorTrap;
-            callbacks[117] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_EE_INFO*, void>)&_getEEInfo;
-            callbacks[118] = (delegate* unmanaged<IntPtr, IntPtr*, char*>)&_getJitTimeLogFilename;
-            callbacks[119] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_METHOD_STRUCT_*, mdToken>)&_getMethodDefFromMethod;
-            callbacks[120] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_METHOD_STRUCT_*, byte*, UIntPtr, UIntPtr*, UIntPtr>)&_printMethodName;
-            callbacks[121] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_METHOD_STRUCT_*, byte**, byte**, byte**, UIntPtr, byte*>)&_getMethodNameFromMetadata;
-            callbacks[122] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_METHOD_STRUCT_*, uint>)&_getMethodHash;
-            callbacks[123] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_CLASS_STRUCT_*, SYSTEMV_AMD64_CORINFO_STRUCT_REG_PASSING_DESCRIPTOR*, byte>)&_getSystemVAmd64PassStructInRegisterDescriptor;
-            callbacks[124] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_CLASS_STRUCT_*, CORINFO_SWIFT_LOWERING*, void>)&_getSwiftLowering;
-            callbacks[125] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_CLASS_STRUCT_*, CORINFO_FPSTRUCT_LOWERING*, void>)&_getFpStructLowering;
-            callbacks[126] = (delegate* unmanaged<IntPtr, IntPtr*, void**, uint>)&_getThreadTLSIndex;
-            callbacks[127] = (delegate* unmanaged<IntPtr, IntPtr*, void**, int*>)&_getAddrOfCaptureThreadGlobal;
-            callbacks[128] = (delegate* unmanaged<IntPtr, IntPtr*, CorInfoHelpFunc, void**, void*>)&_getHelperFtn;
-            callbacks[129] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_METHOD_STRUCT_*, CORINFO_CONST_LOOKUP*, CORINFO_ACCESS_FLAGS, void>)&_getFunctionEntryPoint;
-            callbacks[130] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_METHOD_STRUCT_*, byte, CORINFO_CONST_LOOKUP*, void>)&_getFunctionFixedEntryPoint;
-            callbacks[131] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_METHOD_STRUCT_*, void**, void*>)&_getMethodSync;
-            callbacks[132] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_MODULE_STRUCT_*, CorInfoHelpFunc>)&_getLazyStringLiteralHelper;
-            callbacks[133] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_MODULE_STRUCT_*, void**, CORINFO_MODULE_STRUCT_*>)&_embedModuleHandle;
-            callbacks[134] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_CLASS_STRUCT_*, void**, CORINFO_CLASS_STRUCT_*>)&_embedClassHandle;
-            callbacks[135] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_METHOD_STRUCT_*, void**, CORINFO_METHOD_STRUCT_*>)&_embedMethodHandle;
-            callbacks[136] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_FIELD_STRUCT_*, void**, CORINFO_FIELD_STRUCT_*>)&_embedFieldHandle;
-            callbacks[137] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_RESOLVED_TOKEN*, byte, CORINFO_METHOD_STRUCT_*, CORINFO_GENERICHANDLE_RESULT*, void>)&_embedGenericHandle;
-            callbacks[138] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_METHOD_STRUCT_*, CORINFO_LOOKUP_KIND*, void>)&_getLocationOfThisType;
-            callbacks[139] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_METHOD_STRUCT_*, CORINFO_CONST_LOOKUP*, void>)&_getAddressOfPInvokeTarget;
-            callbacks[140] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_SIG_INFO*, void**, void*>)&_GetCookieForPInvokeCalliSig;
-            callbacks[141] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_SIG_INFO*, byte>)&_canGetCookieForPInvokeCalliSig;
-            callbacks[142] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_METHOD_STRUCT_*, CORINFO_JUST_MY_CODE_HANDLE_**, CORINFO_JUST_MY_CODE_HANDLE_*>)&_getJustMyCodeHandle;
-            callbacks[143] = (delegate* unmanaged<IntPtr, IntPtr*, bool*, void**, bool*, void>)&_GetProfilingHandle;
-            callbacks[144] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_RESOLVED_TOKEN*, CORINFO_RESOLVED_TOKEN*, CORINFO_METHOD_STRUCT_*, CORINFO_CALLINFO_FLAGS, CORINFO_CALL_INFO*, void>)&_getCallInfo;
-            callbacks[145] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_FIELD_STRUCT_*, byte*, int, int, byte, byte>)&_getStaticFieldContent;
-            callbacks[146] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_OBJECT_STRUCT_*, byte*, int, int, byte>)&_getObjectContent;
-            callbacks[147] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_FIELD_STRUCT_*, byte*, CORINFO_CLASS_STRUCT_*>)&_getStaticFieldCurrentClass;
-            callbacks[148] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_SIG_INFO*, void**, IntPtr>)&_getVarArgsHandle;
-            callbacks[149] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_SIG_INFO*, byte>)&_canGetVarArgsHandle;
-            callbacks[150] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_MODULE_STRUCT_*, mdToken, void**, InfoAccessType>)&_constructStringLiteral;
-            callbacks[151] = (delegate* unmanaged<IntPtr, IntPtr*, void**, InfoAccessType>)&_emptyStringLiteral;
-            callbacks[152] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_FIELD_STRUCT_*, void**, uint>)&_getFieldThreadLocalStoreID;
-            callbacks[153] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_METHOD_STRUCT_*, CORINFO_CLASS_STRUCT_*, CORINFO_METHOD_STRUCT_*, DelegateCtorArgs*, CORINFO_METHOD_STRUCT_*>)&_GetDelegateCtor;
-            callbacks[154] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_METHOD_STRUCT_*, void>)&_MethodCompileComplete;
-            callbacks[155] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_RESOLVED_TOKEN*, CORINFO_SIG_INFO*, CORINFO_GET_TAILCALL_HELPERS_FLAGS, CORINFO_TAILCALL_HELPERS*, byte>)&_getTailCallHelpers;
-            callbacks[156] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_RESOLVED_TOKEN*, byte, byte>)&_convertPInvokeCalliToCall;
-            callbacks[157] = (delegate* unmanaged<IntPtr, IntPtr*, InstructionSet, byte, byte>)&_notifyInstructionSetUsage;
-            callbacks[158] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_CONST_LOOKUP*, void>)&_updateEntryPointForTailCall;
-            callbacks[159] = (delegate* unmanaged<IntPtr, IntPtr*, AllocMemArgs*, void>)&_allocMem;
-            callbacks[160] = (delegate* unmanaged<IntPtr, IntPtr*, byte, byte, uint, void>)&_reserveUnwindInfo;
-            callbacks[161] = (delegate* unmanaged<IntPtr, IntPtr*, byte*, byte*, uint, uint, uint, byte*, CorJitFuncKind, void>)&_allocUnwindInfo;
-            callbacks[162] = (delegate* unmanaged<IntPtr, IntPtr*, UIntPtr, void*>)&_allocGCInfo;
-            callbacks[163] = (delegate* unmanaged<IntPtr, IntPtr*, uint, void>)&_setEHcount;
-            callbacks[164] = (delegate* unmanaged<IntPtr, IntPtr*, uint, CORINFO_EH_CLAUSE*, void>)&_setEHinfo;
-            callbacks[165] = (delegate* unmanaged<IntPtr, IntPtr*, uint, byte*, IntPtr, byte>)&_logMsg;
-            callbacks[166] = (delegate* unmanaged<IntPtr, IntPtr*, byte*, int, byte*, int>)&_doAssert;
-            callbacks[167] = (delegate* unmanaged<IntPtr, IntPtr*, CorJitResult, void>)&_reportFatalError;
-            callbacks[168] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_METHOD_STRUCT_*, PgoInstrumentationSchema**, uint*, byte**, PgoSource*, bool*, HRESULT>)&_getPgoInstrumentationResults;
-            callbacks[169] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_METHOD_STRUCT_*, PgoInstrumentationSchema*, uint, byte**, HRESULT>)&_allocPgoInstrumentationBySchema;
-            callbacks[170] = (delegate* unmanaged<IntPtr, IntPtr*, uint, CORINFO_SIG_INFO*, CORINFO_METHOD_STRUCT_*, void>)&_recordCallSite;
-            callbacks[171] = (delegate* unmanaged<IntPtr, IntPtr*, void*, void*, void*, ushort, int, void>)&_recordRelocation;
-            callbacks[172] = (delegate* unmanaged<IntPtr, IntPtr*, void*, ushort>)&_getRelocTypeHint;
-            callbacks[173] = (delegate* unmanaged<IntPtr, IntPtr*, uint>)&_getExpectedTargetArchitecture;
-            callbacks[174] = (delegate* unmanaged<IntPtr, IntPtr*, CORJIT_FLAGS*, uint, uint>)&_getJitFlags;
-            callbacks[175] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_CLASS_STRUCT_*, CORINFO_METHOD_STRUCT_*>)&_getSpecialCopyHelper;
-=======
             callbacks[18] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_METHOD_STRUCT_*, CORINFO_METHOD_STRUCT_**, CORINFO_CLASS_STRUCT_**, CORINFO_METHOD_STRUCT_*>)&_getInstantiatedEntry;
             callbacks[19] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_CLASS_STRUCT_*, CORINFO_CLASS_STRUCT_*>)&_getDefaultComparerClass;
             callbacks[20] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_CLASS_STRUCT_*, CORINFO_CLASS_STRUCT_*>)&_getDefaultEqualityComparerClass;
@@ -3101,64 +2774,65 @@ private static IntPtr GetUnmanagedCallbacks()
             callbacks[117] = (delegate* unmanaged<IntPtr, IntPtr*, void*, void*, byte>)&_runWithErrorTrap;
             callbacks[118] = (delegate* unmanaged<IntPtr, IntPtr*, void*, void*, byte>)&_runWithSPMIErrorTrap;
             callbacks[119] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_EE_INFO*, void>)&_getEEInfo;
-            callbacks[120] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_METHOD_STRUCT_*, mdToken>)&_getMethodDefFromMethod;
-            callbacks[121] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_METHOD_STRUCT_*, byte*, UIntPtr, UIntPtr*, UIntPtr>)&_printMethodName;
-            callbacks[122] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_METHOD_STRUCT_*, byte**, byte**, byte**, UIntPtr, byte*>)&_getMethodNameFromMetadata;
-            callbacks[123] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_METHOD_STRUCT_*, uint>)&_getMethodHash;
-            callbacks[124] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_CLASS_STRUCT_*, SYSTEMV_AMD64_CORINFO_STRUCT_REG_PASSING_DESCRIPTOR*, byte>)&_getSystemVAmd64PassStructInRegisterDescriptor;
-            callbacks[125] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_CLASS_STRUCT_*, CORINFO_SWIFT_LOWERING*, void>)&_getSwiftLowering;
-            callbacks[126] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_CLASS_STRUCT_*, CORINFO_FPSTRUCT_LOWERING*, void>)&_getFpStructLowering;
-            callbacks[127] = (delegate* unmanaged<IntPtr, IntPtr*, void**, uint>)&_getThreadTLSIndex;
-            callbacks[128] = (delegate* unmanaged<IntPtr, IntPtr*, void**, int*>)&_getAddrOfCaptureThreadGlobal;
-            callbacks[129] = (delegate* unmanaged<IntPtr, IntPtr*, CorInfoHelpFunc, void**, void*>)&_getHelperFtn;
-            callbacks[130] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_METHOD_STRUCT_*, CORINFO_CONST_LOOKUP*, CORINFO_ACCESS_FLAGS, void>)&_getFunctionEntryPoint;
-            callbacks[131] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_METHOD_STRUCT_*, byte, CORINFO_CONST_LOOKUP*, void>)&_getFunctionFixedEntryPoint;
-            callbacks[132] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_METHOD_STRUCT_*, void**, void*>)&_getMethodSync;
-            callbacks[133] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_MODULE_STRUCT_*, CorInfoHelpFunc>)&_getLazyStringLiteralHelper;
-            callbacks[134] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_MODULE_STRUCT_*, void**, CORINFO_MODULE_STRUCT_*>)&_embedModuleHandle;
-            callbacks[135] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_CLASS_STRUCT_*, void**, CORINFO_CLASS_STRUCT_*>)&_embedClassHandle;
-            callbacks[136] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_METHOD_STRUCT_*, void**, CORINFO_METHOD_STRUCT_*>)&_embedMethodHandle;
-            callbacks[137] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_FIELD_STRUCT_*, void**, CORINFO_FIELD_STRUCT_*>)&_embedFieldHandle;
-            callbacks[138] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_RESOLVED_TOKEN*, byte, CORINFO_METHOD_STRUCT_*, CORINFO_GENERICHANDLE_RESULT*, void>)&_embedGenericHandle;
-            callbacks[139] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_METHOD_STRUCT_*, CORINFO_LOOKUP_KIND*, void>)&_getLocationOfThisType;
-            callbacks[140] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_METHOD_STRUCT_*, CORINFO_CONST_LOOKUP*, void>)&_getAddressOfPInvokeTarget;
-            callbacks[141] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_SIG_INFO*, void**, void*>)&_GetCookieForPInvokeCalliSig;
-            callbacks[142] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_SIG_INFO*, byte>)&_canGetCookieForPInvokeCalliSig;
-            callbacks[143] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_METHOD_STRUCT_*, CORINFO_JUST_MY_CODE_HANDLE_**, CORINFO_JUST_MY_CODE_HANDLE_*>)&_getJustMyCodeHandle;
-            callbacks[144] = (delegate* unmanaged<IntPtr, IntPtr*, bool*, void**, bool*, void>)&_GetProfilingHandle;
-            callbacks[145] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_RESOLVED_TOKEN*, CORINFO_RESOLVED_TOKEN*, CORINFO_METHOD_STRUCT_*, CORINFO_CALLINFO_FLAGS, CORINFO_CALL_INFO*, void>)&_getCallInfo;
-            callbacks[146] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_FIELD_STRUCT_*, byte*, int, int, byte, byte>)&_getStaticFieldContent;
-            callbacks[147] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_OBJECT_STRUCT_*, byte*, int, int, byte>)&_getObjectContent;
-            callbacks[148] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_FIELD_STRUCT_*, byte*, CORINFO_CLASS_STRUCT_*>)&_getStaticFieldCurrentClass;
-            callbacks[149] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_SIG_INFO*, void**, IntPtr>)&_getVarArgsHandle;
-            callbacks[150] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_SIG_INFO*, byte>)&_canGetVarArgsHandle;
-            callbacks[151] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_MODULE_STRUCT_*, mdToken, void**, InfoAccessType>)&_constructStringLiteral;
-            callbacks[152] = (delegate* unmanaged<IntPtr, IntPtr*, void**, InfoAccessType>)&_emptyStringLiteral;
-            callbacks[153] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_FIELD_STRUCT_*, void**, uint>)&_getFieldThreadLocalStoreID;
-            callbacks[154] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_METHOD_STRUCT_*, CORINFO_CLASS_STRUCT_*, CORINFO_METHOD_STRUCT_*, DelegateCtorArgs*, CORINFO_METHOD_STRUCT_*>)&_GetDelegateCtor;
-            callbacks[155] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_METHOD_STRUCT_*, void>)&_MethodCompileComplete;
-            callbacks[156] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_RESOLVED_TOKEN*, CORINFO_SIG_INFO*, CORINFO_GET_TAILCALL_HELPERS_FLAGS, CORINFO_TAILCALL_HELPERS*, byte>)&_getTailCallHelpers;
-            callbacks[157] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_RESOLVED_TOKEN*, byte, byte>)&_convertPInvokeCalliToCall;
-            callbacks[158] = (delegate* unmanaged<IntPtr, IntPtr*, InstructionSet, byte, byte>)&_notifyInstructionSetUsage;
-            callbacks[159] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_CONST_LOOKUP*, void>)&_updateEntryPointForTailCall;
-            callbacks[160] = (delegate* unmanaged<IntPtr, IntPtr*, AllocMemArgs*, void>)&_allocMem;
-            callbacks[161] = (delegate* unmanaged<IntPtr, IntPtr*, byte, byte, uint, void>)&_reserveUnwindInfo;
-            callbacks[162] = (delegate* unmanaged<IntPtr, IntPtr*, byte*, byte*, uint, uint, uint, byte*, CorJitFuncKind, void>)&_allocUnwindInfo;
-            callbacks[163] = (delegate* unmanaged<IntPtr, IntPtr*, UIntPtr, void*>)&_allocGCInfo;
-            callbacks[164] = (delegate* unmanaged<IntPtr, IntPtr*, uint, void>)&_setEHcount;
-            callbacks[165] = (delegate* unmanaged<IntPtr, IntPtr*, uint, CORINFO_EH_CLAUSE*, void>)&_setEHinfo;
-            callbacks[166] = (delegate* unmanaged<IntPtr, IntPtr*, uint, byte*, IntPtr, byte>)&_logMsg;
-            callbacks[167] = (delegate* unmanaged<IntPtr, IntPtr*, byte*, int, byte*, int>)&_doAssert;
-            callbacks[168] = (delegate* unmanaged<IntPtr, IntPtr*, CorJitResult, void>)&_reportFatalError;
-            callbacks[169] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_METHOD_STRUCT_*, PgoInstrumentationSchema**, uint*, byte**, PgoSource*, bool*, HRESULT>)&_getPgoInstrumentationResults;
-            callbacks[170] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_METHOD_STRUCT_*, PgoInstrumentationSchema*, uint, byte**, HRESULT>)&_allocPgoInstrumentationBySchema;
-            callbacks[171] = (delegate* unmanaged<IntPtr, IntPtr*, uint, CORINFO_SIG_INFO*, CORINFO_METHOD_STRUCT_*, void>)&_recordCallSite;
-            callbacks[172] = (delegate* unmanaged<IntPtr, IntPtr*, void*, void*, void*, ushort, int, void>)&_recordRelocation;
-            callbacks[173] = (delegate* unmanaged<IntPtr, IntPtr*, void*, ushort>)&_getRelocTypeHint;
-            callbacks[174] = (delegate* unmanaged<IntPtr, IntPtr*, uint>)&_getExpectedTargetArchitecture;
-            callbacks[175] = (delegate* unmanaged<IntPtr, IntPtr*, CORJIT_FLAGS*, uint, uint>)&_getJitFlags;
-            callbacks[176] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_CLASS_STRUCT_*, CORINFO_METHOD_STRUCT_*>)&_getSpecialCopyHelper;
->>>>>>> 040cbe276907174316e2cc07b35814b3069874a6
+            callbacks[120] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_ASYNC2_INFO*, void>)&_getAsync2Info;
+            callbacks[121] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_METHOD_STRUCT_*, mdToken>)&_getMethodDefFromMethod;
+            callbacks[122] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_METHOD_STRUCT_*, byte*, UIntPtr, UIntPtr*, UIntPtr>)&_printMethodName;
+            callbacks[123] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_METHOD_STRUCT_*, byte**, byte**, byte**, UIntPtr, byte*>)&_getMethodNameFromMetadata;
+            callbacks[124] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_METHOD_STRUCT_*, uint>)&_getMethodHash;
+            callbacks[125] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_CLASS_STRUCT_*, SYSTEMV_AMD64_CORINFO_STRUCT_REG_PASSING_DESCRIPTOR*, byte>)&_getSystemVAmd64PassStructInRegisterDescriptor;
+            callbacks[126] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_CLASS_STRUCT_*, CORINFO_SWIFT_LOWERING*, void>)&_getSwiftLowering;
+            callbacks[127] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_CLASS_STRUCT_*, CORINFO_FPSTRUCT_LOWERING*, void>)&_getFpStructLowering;
+            callbacks[128] = (delegate* unmanaged<IntPtr, IntPtr*, void**, uint>)&_getThreadTLSIndex;
+            callbacks[129] = (delegate* unmanaged<IntPtr, IntPtr*, void**, int*>)&_getAddrOfCaptureThreadGlobal;
+            callbacks[130] = (delegate* unmanaged<IntPtr, IntPtr*, CorInfoHelpFunc, void**, void*>)&_getHelperFtn;
+            callbacks[131] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_METHOD_STRUCT_*, CORINFO_CONST_LOOKUP*, CORINFO_ACCESS_FLAGS, void>)&_getFunctionEntryPoint;
+            callbacks[132] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_METHOD_STRUCT_*, byte, CORINFO_CONST_LOOKUP*, void>)&_getFunctionFixedEntryPoint;
+            callbacks[133] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_METHOD_STRUCT_*, void**, void*>)&_getMethodSync;
+            callbacks[134] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_MODULE_STRUCT_*, CorInfoHelpFunc>)&_getLazyStringLiteralHelper;
+            callbacks[135] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_MODULE_STRUCT_*, void**, CORINFO_MODULE_STRUCT_*>)&_embedModuleHandle;
+            callbacks[136] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_CLASS_STRUCT_*, void**, CORINFO_CLASS_STRUCT_*>)&_embedClassHandle;
+            callbacks[137] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_METHOD_STRUCT_*, void**, CORINFO_METHOD_STRUCT_*>)&_embedMethodHandle;
+            callbacks[138] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_FIELD_STRUCT_*, void**, CORINFO_FIELD_STRUCT_*>)&_embedFieldHandle;
+            callbacks[139] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_RESOLVED_TOKEN*, byte, CORINFO_METHOD_STRUCT_*, CORINFO_GENERICHANDLE_RESULT*, void>)&_embedGenericHandle;
+            callbacks[140] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_METHOD_STRUCT_*, CORINFO_LOOKUP_KIND*, void>)&_getLocationOfThisType;
+            callbacks[141] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_METHOD_STRUCT_*, CORINFO_CONST_LOOKUP*, void>)&_getAddressOfPInvokeTarget;
+            callbacks[142] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_SIG_INFO*, void**, void*>)&_GetCookieForPInvokeCalliSig;
+            callbacks[143] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_SIG_INFO*, byte>)&_canGetCookieForPInvokeCalliSig;
+            callbacks[144] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_METHOD_STRUCT_*, CORINFO_JUST_MY_CODE_HANDLE_**, CORINFO_JUST_MY_CODE_HANDLE_*>)&_getJustMyCodeHandle;
+            callbacks[145] = (delegate* unmanaged<IntPtr, IntPtr*, bool*, void**, bool*, void>)&_GetProfilingHandle;
+            callbacks[146] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_RESOLVED_TOKEN*, CORINFO_RESOLVED_TOKEN*, CORINFO_METHOD_STRUCT_*, CORINFO_CALLINFO_FLAGS, CORINFO_CALL_INFO*, void>)&_getCallInfo;
+            callbacks[147] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_FIELD_STRUCT_*, byte*, int, int, byte, byte>)&_getStaticFieldContent;
+            callbacks[148] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_OBJECT_STRUCT_*, byte*, int, int, byte>)&_getObjectContent;
+            callbacks[149] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_FIELD_STRUCT_*, byte*, CORINFO_CLASS_STRUCT_*>)&_getStaticFieldCurrentClass;
+            callbacks[150] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_SIG_INFO*, void**, IntPtr>)&_getVarArgsHandle;
+            callbacks[151] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_SIG_INFO*, byte>)&_canGetVarArgsHandle;
+            callbacks[152] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_MODULE_STRUCT_*, mdToken, void**, InfoAccessType>)&_constructStringLiteral;
+            callbacks[153] = (delegate* unmanaged<IntPtr, IntPtr*, void**, InfoAccessType>)&_emptyStringLiteral;
+            callbacks[154] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_FIELD_STRUCT_*, void**, uint>)&_getFieldThreadLocalStoreID;
+            callbacks[155] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_METHOD_STRUCT_*, CORINFO_CLASS_STRUCT_*, CORINFO_METHOD_STRUCT_*, DelegateCtorArgs*, CORINFO_METHOD_STRUCT_*>)&_GetDelegateCtor;
+            callbacks[156] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_METHOD_STRUCT_*, void>)&_MethodCompileComplete;
+            callbacks[157] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_RESOLVED_TOKEN*, CORINFO_SIG_INFO*, CORINFO_GET_TAILCALL_HELPERS_FLAGS, CORINFO_TAILCALL_HELPERS*, byte>)&_getTailCallHelpers;
+            callbacks[158] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_METHOD_STRUCT_*>)&_getAsyncResumptionStub;
+            callbacks[159] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_RESOLVED_TOKEN*, byte, byte>)&_convertPInvokeCalliToCall;
+            callbacks[160] = (delegate* unmanaged<IntPtr, IntPtr*, InstructionSet, byte, byte>)&_notifyInstructionSetUsage;
+            callbacks[161] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_CONST_LOOKUP*, void>)&_updateEntryPointForTailCall;
+            callbacks[162] = (delegate* unmanaged<IntPtr, IntPtr*, AllocMemArgs*, void>)&_allocMem;
+            callbacks[163] = (delegate* unmanaged<IntPtr, IntPtr*, byte, byte, uint, void>)&_reserveUnwindInfo;
+            callbacks[164] = (delegate* unmanaged<IntPtr, IntPtr*, byte*, byte*, uint, uint, uint, byte*, CorJitFuncKind, void>)&_allocUnwindInfo;
+            callbacks[165] = (delegate* unmanaged<IntPtr, IntPtr*, UIntPtr, void*>)&_allocGCInfo;
+            callbacks[166] = (delegate* unmanaged<IntPtr, IntPtr*, uint, void>)&_setEHcount;
+            callbacks[167] = (delegate* unmanaged<IntPtr, IntPtr*, uint, CORINFO_EH_CLAUSE*, void>)&_setEHinfo;
+            callbacks[168] = (delegate* unmanaged<IntPtr, IntPtr*, uint, byte*, IntPtr, byte>)&_logMsg;
+            callbacks[169] = (delegate* unmanaged<IntPtr, IntPtr*, byte*, int, byte*, int>)&_doAssert;
+            callbacks[170] = (delegate* unmanaged<IntPtr, IntPtr*, CorJitResult, void>)&_reportFatalError;
+            callbacks[171] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_METHOD_STRUCT_*, PgoInstrumentationSchema**, uint*, byte**, PgoSource*, bool*, HRESULT>)&_getPgoInstrumentationResults;
+            callbacks[172] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_METHOD_STRUCT_*, PgoInstrumentationSchema*, uint, byte**, HRESULT>)&_allocPgoInstrumentationBySchema;
+            callbacks[173] = (delegate* unmanaged<IntPtr, IntPtr*, uint, CORINFO_SIG_INFO*, CORINFO_METHOD_STRUCT_*, void>)&_recordCallSite;
+            callbacks[174] = (delegate* unmanaged<IntPtr, IntPtr*, void*, void*, void*, ushort, int, void>)&_recordRelocation;
+            callbacks[175] = (delegate* unmanaged<IntPtr, IntPtr*, void*, ushort>)&_getRelocTypeHint;
+            callbacks[176] = (delegate* unmanaged<IntPtr, IntPtr*, uint>)&_getExpectedTargetArchitecture;
+            callbacks[177] = (delegate* unmanaged<IntPtr, IntPtr*, CORJIT_FLAGS*, uint, uint>)&_getJitFlags;
+            callbacks[178] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_CLASS_STRUCT_*, CORINFO_METHOD_STRUCT_*>)&_getSpecialCopyHelper;
 
             return (IntPtr)callbacks;
         }
diff --git a/src/coreclr/tools/aot/jitinterface/jitinterface_generated.h b/src/coreclr/tools/aot/jitinterface/jitinterface_generated.h
index 095126934804f0..9117a99d6a2b3f 100644
--- a/src/coreclr/tools/aot/jitinterface/jitinterface_generated.h
+++ b/src/coreclr/tools/aot/jitinterface/jitinterface_generated.h
@@ -1362,7 +1362,6 @@ class JitInterfaceWrapper : public ICorJitInfo
     if (pException != nullptr) throw pException;
 }
 
-<<<<<<< HEAD
     virtual void getAsync2Info(
           CORINFO_ASYNC2_INFO* pAsync2InfoOut)
 {
@@ -1371,25 +1370,6 @@ class JitInterfaceWrapper : public ICorJitInfo
     if (pException != nullptr) throw pException;
 }
 
-    virtual const char16_t* getJitTimeLogFilename()
-{
-    CorInfoExceptionClass* pException = nullptr;
-    const char16_t* temp = _callbacks->getJitTimeLogFilename(_thisHandle, &pException);
-    if (pException != nullptr) throw pException;
-    return temp;
-}
-
-||||||| 741390a6850
-    virtual const char16_t* getJitTimeLogFilename()
-{
-    CorInfoExceptionClass* pException = nullptr;
-    const char16_t* temp = _callbacks->getJitTimeLogFilename(_thisHandle, &pException);
-    if (pException != nullptr) throw pException;
-    return temp;
-}
-
-=======
->>>>>>> 040cbe276907174316e2cc07b35814b3069874a6
     virtual mdMethodDef getMethodDefFromMethod(
           CORINFO_METHOD_HANDLE hMethod)
 {
diff --git a/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.h b/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.h
index ffa0744bfe503c..02178e01f875d6 100644
--- a/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.h
+++ b/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.h
@@ -1212,6 +1212,7 @@ enum mcPackets
     Packet_GetMethodInstantiationArgument = 227,
     Packet_GetInstantiatedEntry = 228,
     Packet_GetAsync2Info = 229,
+    Packet_GetAsyncResumptionStub = 230,
 };
 
 void SetDebugDumpVariables();
diff --git a/src/coreclr/vm/comdelegate.cpp b/src/coreclr/vm/comdelegate.cpp
index 18fb1f1e103284..2b83faee3e7e43 100644
--- a/src/coreclr/vm/comdelegate.cpp
+++ b/src/coreclr/vm/comdelegate.cpp
@@ -1973,7 +1973,7 @@ Stub* COMDelegate::GetInvokeMethodStub(EEImplMethodDesc* pMD)
 
         MetaSig sig(pMD);
 
-        BYTE callConv = sig.GetCallingConventionInfo();
+        unsigned callConv = sig.GetCallingConventionInfo();
         if (callConv != (IMAGE_CEE_CS_CALLCONV_HASTHIS | IMAGE_CEE_CS_CALLCONV_DEFAULT))
             COMPlusThrow(kInvalidProgramException);
 
diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.cs
index 60ae6d753c5c76..f5dfe8afc578d7 100644
--- a/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.cs
+++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.cs
@@ -7,11 +7,6 @@
 using System.Threading;
 using System.Threading.Tasks;
 
-namespace System.Runtime
-{
-    internal sealed class BypassReadyToRunAttribute : Attribute {}
-}
-
 namespace System.Runtime.CompilerServices
 {
     public static partial class RuntimeHelpers

From bd7b3f53d6836b5d3e8c25382f00606b834e28dd Mon Sep 17 00:00:00 2001
From: Jakob Botsch Nielsen <Jakob.botsch.nielsen@gmail.com>
Date: Wed, 18 Dec 2024 00:34:56 +0100
Subject: [PATCH 134/203] Delete CODEOWNERS to avoid notifications (#2894)

---
 .github/CODEOWNERS | 114 ---------------------------------------------
 1 file changed, 114 deletions(-)
 delete mode 100644 .github/CODEOWNERS

diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS
deleted file mode 100644
index 47a365e54a5168..00000000000000
--- a/.github/CODEOWNERS
+++ /dev/null
@@ -1,114 +0,0 @@
-# Users referenced in this file will automatically be requested as reviewers for PRs that modify the given paths.
-# See https://help.github.com/articles/about-code-owners/
-
-/src/libraries/Common/src/Interop/                                  @dotnet/platform-deps-team
-/src/libraries/Common/src/System/Net/Http/aspnetcore/               @dotnet/http
-/src/libraries/Common/tests/Tests/System/Net/aspnetcore/            @dotnet/http
-
-# CoreCLR Code Owners
-
-/src/coreclr/inc/corinfo.h @dotnet/jit-contrib
-/src/coreclr/inc/corjit.h @dotnet/jit-contrib
-/src/coreclr/jit/ @dotnet/jit-contrib
-/src/coreclr/nativeaot @MichalStrehovsky
-/src/coreclr/tools/Common @dotnet/crossgen-contrib @MichalStrehovsky
-/src/coreclr/tools/aot @dotnet/crossgen-contrib
-/src/coreclr/tools/aot/ILCompiler.Compiler @MichalStrehovsky
-/src/coreclr/tools/aot/ILCompiler.RyuJit @MichalStrehovsky
-/src/coreclr/tools/aot/ILCompiler.MetadataTransform @MichalStrehovsky
-
-# Mono Code Owners
-
-/src/mono @marek-safar
-
-/src/mono/llvm @lambdageek @steveisok
-
-/src/mono/mono/arch @lambdageek @steveisok
-/src/mono/mono/eglib @lambdageek @steveisok
-
-/src/mono/mono/metadata @lambdageek @thaystg
-/src/mono/mono/metadata/*-win* @lateralusX @lambdageek
-/src/mono/mono/metadata/handle* @lambdageek @steveisok
-/src/mono/mono/metadata/monitor* @brzvlad @steveisok
-/src/mono/mono/metadata/sgen* @brzvlad @lambdageek
-/src/mono/mono/metadata/thread* @lateralusX @lambdageek
-/src/mono/mono/metadata/w32* @lateralusX @lambdageek
-
-/src/mono/mono/eventpipe @lateralusX @lambdageek
-
-/src/mono/mono/mini @lambdageek @steveisok
-/src/mono/mono/mini/*cfgdump* @lambdageek
-/src/mono/mono/mini/*exceptions* @BrzVlad
-/src/mono/mono/mini/*llvm* @fanyang-mono @steveisok
-/src/mono/mono/mini/*ppc* @lambdageek
-/src/mono/mono/mini/*profiler* @BrzVlad @lambdageek
-/src/mono/mono/mini/*riscv* @lambdageek @steveisok
-/src/mono/mono/mini/*type-check* @lambdageek @steveisok
-/src/mono/mono/mini/debugger-agent.c @thaystg @lambdageek
-/src/mono/mono/mini/interp/* @BrzVlad @kotlarmilos
-/src/mono/mono/mini/interp/*jiterp* @kg
-/src/mono/mono/mini/*simd* @fanyang-mono
-
-/src/mono/mono/profiler @BrzVlad @lambdageek
-/src/mono/mono/sgen @BrzVlad @lambdageek
-
-/src/mono/mono/utils @lambdageek @steveisok
-/src/mono/mono/utils/*-win* @lateralusX @lambdageek
-/src/mono/mono/utils/atomic* @lambdageek @steveisok
-/src/mono/mono/utils/mono-hwcap* @lambdageek
-/src/mono/mono/utils/mono-mem* @lambdageek @steveisok
-/src/mono/mono/utils/mono-threads* @lambdageek
-
-/src/mono/dlls @thaystg @lambdageek
-
-/src/native/public/mono @marek-safar @lambdageek
-/src/native/external/libunwind @janvorli @AaronRobinsonMSFT @dotnet/dotnet-diag
-/src/native/external/libunwind_extras @janvorli @AaronRobinsonMSFT @dotnet/dotnet-diag
-
-/src/libraries/sendtohelix-browser.targets @akoeplinger
-/src/libraries/sendtohelix-wasm.targets @akoeplinger
-/src/libraries/sendtohelix-wasi.targets @akoeplinger
-/src/mono/browser @lewing @pavelsavara
-/src/mono/wasi @lewing @pavelsavara
-/src/mono/wasm @lewing @pavelsavara
-/src/mono/browser/debugger @thaystg @ilonatommy
-/src/mono/wasm/build @maraf @akoeplinger
-/src/mono/wasi/build @maraf @akoeplinger
-/src/mono/browser/build @maraf @akoeplinger
-/src/mono/sample/wasm @lewing @pavelsavara
-/src/mono/sample/wasi @lewing @pavelsavara
-/src/libraries/System.Runtime.InteropServices.JavaScript @lewing @pavelsavara
-
-/src/mono/nuget/*WebAssembly*/ @lewing @akoeplinger
-/src/mono/nuget/*MonoTargets*/ @lewing @akoeplinger
-/src/mono/nuget/*BrowserDebugHost*/ @lewing @akoeplinger
-/src/mono/nuget/*Workload.Mono.Toolchain*/ @lewing @akoeplinger
-/src/mono/nuget/*MonoAOTCompiler*/ @lewing @akoeplinger
-
-/src/mono/wasm/Wasm* @maraf @ilonatommy
-/src/mono/wasm/testassets @maraf @ilonatommy
-/src/mono/wasi/testassets @maraf @ilonatommy
-/src/tasks/WasmAppBuilder/ @maraf @akoeplinger
-/src/tasks/WorkloadBuildTasks/ @akoeplinger
-/src/tasks/AotCompilerTask/ @akoeplinger
-/src/tasks/WasmBuildTasks/ @maraf @akoeplinger
-
-/eng/pipelines/**/*wasm* @akoeplinger
-
-# ILLink codeowners
-/src/tools/illink/ @marek-safar
-/src/tools/illink/src/analyzer/ @radekdoulik
-/src/tools/illink/src/ILLink.Tasks/ @sbomer
-/src/tools/illink/src/ILLink.RoslynAnalyzer/ @sbomer
-/src/tools/illink/src/linker/ @marek-safar @mrvoorhe
-/src/tools/illink/test/ @marek-safar @mrvoorhe
-
-# Obsoletions / Custom Diagnostics
-
-/docs/project/list-of-diagnostics.md                @jeffhandley
-/src/libraries/Common/src/System/Obsoletions.cs     @jeffhandley
-
-# Area ownership and repo automation
-/docs/area-owners.*                                 @jeffhandley
-/docs/issue*.md                                     @jeffhandley
-/.github/policies/                                  @jeffhandley @mkArtakMSFT

From b077e29955403ddeed723579bb7d4885d94ae3f9 Mon Sep 17 00:00:00 2001
From: Jakob Botsch Nielsen <Jakob.botsch.nielsen@gmail.com>
Date: Thu, 9 Jan 2025 12:10:56 +0100
Subject: [PATCH 135/203] Format async2 JIT code (#2915)

---
 src/coreclr/jit/async.cpp         | 75 ++++++++++++++++---------------
 src/coreclr/jit/async.h           | 26 ++++++-----
 src/coreclr/jit/codegenarm.cpp    |  6 +--
 src/coreclr/jit/codegenarm64.cpp  |  8 ++--
 src/coreclr/jit/codegenxarch.cpp  |  3 +-
 src/coreclr/jit/compiler.cpp      |  4 +-
 src/coreclr/jit/compiler.h        | 14 +++---
 src/coreclr/jit/emit.cpp          |  8 ++--
 src/coreclr/jit/emitloongarch64.h |  4 +-
 src/coreclr/jit/emitriscv64.h     |  4 +-
 src/coreclr/jit/emitxarch.h       |  4 +-
 src/coreclr/jit/fgbasic.cpp       |  3 +-
 src/coreclr/jit/fginline.cpp      | 18 +++++---
 src/coreclr/jit/flowgraph.cpp     |  6 +--
 src/coreclr/jit/gentree.cpp       | 10 ++---
 src/coreclr/jit/importer.cpp      |  7 +--
 src/coreclr/jit/importercalls.cpp |  4 +-
 src/coreclr/jit/inline.h          |  5 ++-
 src/coreclr/jit/lclvars.cpp       |  5 ++-
 src/coreclr/jit/morph.cpp         |  4 +-
 src/coreclr/jit/valuenum.cpp      |  4 +-
 21 files changed, 116 insertions(+), 106 deletions(-)

diff --git a/src/coreclr/jit/async.cpp b/src/coreclr/jit/async.cpp
index e77b25c732fbf0..51ef5f451397e3 100644
--- a/src/coreclr/jit/async.cpp
+++ b/src/coreclr/jit/async.cpp
@@ -19,7 +19,10 @@ class AsyncLiveness
 
 public:
     AsyncLiveness(Compiler* comp, bool hasLiveness)
-        : m_comp(comp), m_hasLiveness(hasLiveness), m_updater(comp), m_numVars(comp->lvaCount)
+        : m_comp(comp)
+        , m_hasLiveness(hasLiveness)
+        , m_updater(comp)
+        , m_numVars(comp->lvaCount)
     {
     }
 
@@ -210,7 +213,7 @@ PhaseStatus Async2Transformation::Run()
     m_sharedReturnBB->clearHndIndex();
 
     GenTree* continuation = m_comp->gtNewLclvNode(m_newContinuationVar, TYP_REF);
-    GenTree* ret = m_comp->gtNewOperNode(GT_RETURN_SUSPEND, TYP_VOID, continuation);
+    GenTree* ret          = m_comp->gtNewOperNode(GT_RETURN_SUSPEND, TYP_VOID, continuation);
     LIR::AsRange(m_sharedReturnBB).InsertAtEnd(continuation, ret);
 
     JITDUMP("Created shared return BB " FMT_BB "\n", m_sharedReturnBB->bbNum);
@@ -466,8 +469,8 @@ void Async2Transformation::Transform(
     if (returnSize > 0)
     {
         JITDUMP("  Will store return of type %s, size %u in",
-            call->gtReturnType == TYP_STRUCT ? returnStructLayout->GetClassName() : varTypeName(call->gtReturnType),
-            returnSize);
+                call->gtReturnType == TYP_STRUCT ? returnStructLayout->GetClassName() : varTypeName(call->gtReturnType),
+                returnSize);
 
         if (returnInGCData)
         {
@@ -796,7 +799,7 @@ void Async2Transformation::Transform(
     if (suspendBB->KindIs(BBJ_RETURN))
     {
         newContinuation = m_comp->gtNewLclvNode(m_newContinuationVar, TYP_REF);
-        GenTree* ret = m_comp->gtNewOperNode(GT_RETURN_SUSPEND, TYP_VOID, newContinuation);
+        GenTree* ret    = m_comp->gtNewOperNode(GT_RETURN_SUSPEND, TYP_VOID, newContinuation);
         LIR::AsRange(suspendBB).InsertAtEnd(newContinuation, ret);
     }
 
@@ -805,8 +808,8 @@ void Async2Transformation::Transform(
         m_lastResumptionBB = m_comp->fgLastBBInMainFunction();
     }
 
-    BasicBlock* resumeBB = m_comp->fgNewBBafter(BBJ_ALWAYS, m_lastResumptionBB, true);
-    FlowEdge* remainderEdge = m_comp->fgAddRefPred(remainder, resumeBB);
+    BasicBlock* resumeBB      = m_comp->fgNewBBafter(BBJ_ALWAYS, m_lastResumptionBB, true);
+    FlowEdge*   remainderEdge = m_comp->fgAddRefPred(remainder, resumeBB);
 
     resumeBB->SetTargetEdge(remainderEdge);
     resumeBB->bbSetRunRarely();
@@ -872,8 +875,8 @@ void Async2Transformation::Transform(
         }
     }
 
-    unsigned resumeObjectArrLclNum = BAD_VAR_NUM;
-    BasicBlock* storeResultBB = resumeBB;
+    unsigned    resumeObjectArrLclNum = BAD_VAR_NUM;
+    BasicBlock* storeResultBB         = resumeBB;
 
     if (gcRefsCount > 0)
     {
@@ -947,13 +950,14 @@ void Async2Transformation::Transform(
         {
             JITDUMP("  We need to rethrow an exception\n");
 
-            BasicBlock* rethrowExceptionBB = m_comp->fgNewBBinRegion(BBJ_THROW, block, /* runRarely */ true, /* insertAtEnd */ true);
+            BasicBlock* rethrowExceptionBB =
+                m_comp->fgNewBBinRegion(BBJ_THROW, block, /* runRarely */ true, /* insertAtEnd */ true);
             JITDUMP("  Created " FMT_BB " to rethrow exception on resumption\n", rethrowExceptionBB->bbNum);
 
             storeResultBB = m_comp->fgNewBBafter(BBJ_ALWAYS, resumeBB, true);
             JITDUMP("  Created " FMT_BB " to store result when resuming with no exception\n", storeResultBB->bbNum);
 
-            FlowEdge* rethrowEdge = m_comp->fgAddRefPred(rethrowExceptionBB, resumeBB);
+            FlowEdge* rethrowEdge     = m_comp->fgAddRefPred(rethrowExceptionBB, resumeBB);
             FlowEdge* storeResultEdge = m_comp->fgAddRefPred(storeResultBB, resumeBB);
 
             assert(resumeBB->KindIs(BBJ_ALWAYS));
@@ -1108,19 +1112,24 @@ void Async2Transformation::Transform(
     m_resumptionBBs.push_back(resumeBB);
 }
 
-GenTreeCall* Async2Transformation::CreateAllocContinuationCall(AsyncLiveness& life, GenTree* prevContinuation, unsigned gcRefsCount, unsigned dataSize)
+GenTreeCall* Async2Transformation::CreateAllocContinuationCall(AsyncLiveness& life,
+                                                               GenTree*       prevContinuation,
+                                                               unsigned       gcRefsCount,
+                                                               unsigned       dataSize)
 {
     GenTree* gcRefsCountNode = m_comp->gtNewIconNode((ssize_t)gcRefsCount, TYP_I_IMPL);
-    GenTree* dataSizeNode = m_comp->gtNewIconNode((ssize_t)dataSize, TYP_I_IMPL);
+    GenTree* dataSizeNode    = m_comp->gtNewIconNode((ssize_t)dataSize, TYP_I_IMPL);
     // If VM requests that we report the method handle, or if we have a shared generic context method handle
     // that is live here, then we need to call a different helper.
     GenTree* methodHandleArg = nullptr;
-    GenTree* classHandleArg = nullptr;
-    if (((m_comp->info.compMethodInfo->options & CORINFO_GENERICS_CTXT_FROM_METHODDESC) != 0) && life.IsLive(m_comp->info.compTypeCtxtArg))
+    GenTree* classHandleArg  = nullptr;
+    if (((m_comp->info.compMethodInfo->options & CORINFO_GENERICS_CTXT_FROM_METHODDESC) != 0) &&
+        life.IsLive(m_comp->info.compTypeCtxtArg))
     {
         methodHandleArg = m_comp->gtNewLclvNode(m_comp->info.compTypeCtxtArg, TYP_I_IMPL);
     }
-    else if (((m_comp->info.compMethodInfo->options & CORINFO_GENERICS_CTXT_FROM_METHODTABLE) != 0) && life.IsLive(m_comp->info.compTypeCtxtArg))
+    else if (((m_comp->info.compMethodInfo->options & CORINFO_GENERICS_CTXT_FROM_METHODTABLE) != 0) &&
+             life.IsLive(m_comp->info.compTypeCtxtArg))
     {
         classHandleArg = m_comp->gtNewLclvNode(m_comp->info.compTypeCtxtArg, TYP_I_IMPL);
     }
@@ -1131,27 +1140,20 @@ GenTreeCall* Async2Transformation::CreateAllocContinuationCall(AsyncLiveness& li
 
     if (methodHandleArg != nullptr)
     {
-        return m_comp->gtNewHelperCallNode(
-            CORINFO_HELP_ALLOC_CONTINUATION_METHOD,
-            TYP_REF,
-            prevContinuation, gcRefsCountNode, dataSizeNode, methodHandleArg);
+        return m_comp->gtNewHelperCallNode(CORINFO_HELP_ALLOC_CONTINUATION_METHOD, TYP_REF, prevContinuation,
+                                           gcRefsCountNode, dataSizeNode, methodHandleArg);
     }
 
     if (classHandleArg != nullptr)
     {
-        return m_comp->gtNewHelperCallNode(
-            CORINFO_HELP_ALLOC_CONTINUATION_CLASS,
-            TYP_REF,
-            prevContinuation, gcRefsCountNode, dataSizeNode, classHandleArg);
+        return m_comp->gtNewHelperCallNode(CORINFO_HELP_ALLOC_CONTINUATION_CLASS, TYP_REF, prevContinuation,
+                                           gcRefsCountNode, dataSizeNode, classHandleArg);
     }
 
-    return m_comp->gtNewHelperCallNode(
-        CORINFO_HELP_ALLOC_CONTINUATION,
-        TYP_REF,
-        prevContinuation, gcRefsCountNode, dataSizeNode);
+    return m_comp->gtNewHelperCallNode(CORINFO_HELP_ALLOC_CONTINUATION, TYP_REF, prevContinuation, gcRefsCountNode,
+                                       dataSizeNode);
 }
 
-
 GenTreeIndir* Async2Transformation::LoadFromOffset(GenTree*     base,
                                                    unsigned     offset,
                                                    var_types    type,
@@ -1388,7 +1390,7 @@ void Async2Transformation::CreateResumptionSwitch()
         BBswtDesc* swtDesc     = new (m_comp, CMK_BasicBlock) BBswtDesc;
         swtDesc->bbsCount      = (unsigned)m_resumptionBBs.size();
         swtDesc->bbsHasDefault = true;
-        swtDesc->bbsDstTab = new (m_comp, CMK_Async2) FlowEdge*[m_resumptionBBs.size()];
+        swtDesc->bbsDstTab     = new (m_comp, CMK_Async2) FlowEdge*[m_resumptionBBs.size()];
 
         weight_t stateLikelihood = 1.0 / m_resumptionBBs.size();
         for (size_t i = 0; i < m_resumptionBBs.size(); i++)
@@ -1416,7 +1418,7 @@ void Async2Transformation::CreateResumptionSwitch()
         JITDUMP("    Created " FMT_BB " for transitions back into OSR method\n", callHelperBB->bbNum);
 
         BasicBlock* onContinuationBB = newEntryBB->GetTrueTarget();
-        BasicBlock* checkILOffsetBB = m_comp->fgNewBBbefore(BBJ_COND, onContinuationBB, true);
+        BasicBlock* checkILOffsetBB  = m_comp->fgNewBBbefore(BBJ_COND, onContinuationBB, true);
 
         JITDUMP("    Created " FMT_BB " to check whether we should transition immediately to OSR\n",
                 checkILOffsetBB->bbNum);
@@ -1429,7 +1431,7 @@ void Async2Transformation::CreateResumptionSwitch()
         toCheckILOffsetBB->setLikelihood(RESUME_SUSPEND_LIKELIHOOD);
 
         FlowEdge* toOnContinuationBB = m_comp->fgAddRefPred(onContinuationBB, checkILOffsetBB);
-        FlowEdge* toCallHelperBB = m_comp->fgAddRefPred(callHelperBB, checkILOffsetBB);
+        FlowEdge* toCallHelperBB     = m_comp->fgAddRefPred(callHelperBB, checkILOffsetBB);
         checkILOffsetBB->SetCond(toCallHelperBB, toOnContinuationBB);
         toCallHelperBB->setLikelihood(RESUME_IN_OSR_LIKELIHOOD);
         toOnContinuationBB->setLikelihood(1 - RESUME_IN_OSR_LIKELIHOOD);
@@ -1467,10 +1469,9 @@ void Async2Transformation::CreateResumptionSwitch()
         // version by normal means then we will see a non-zero continuation
         // here that belongs to the tier0 method. In that case we should just
         // ignore it, so create a BB that jumps back.
-        BasicBlock* onContinuationBB = newEntryBB->GetTrueTarget();
+        BasicBlock* onContinuationBB   = newEntryBB->GetTrueTarget();
         BasicBlock* onNoContinuationBB = newEntryBB->GetFalseTarget();
-        BasicBlock* checkILOffsetBB =
-            m_comp->fgNewBBbefore(BBJ_COND, onContinuationBB, true);
+        BasicBlock* checkILOffsetBB    = m_comp->fgNewBBbefore(BBJ_COND, onContinuationBB, true);
 
         // Switch newEntryBB -> onContinuationBB into newEntryBB -> checkILOffsetBB
         m_comp->fgRemoveRefPred(newEntryBB->GetTrueEdge());
@@ -1478,10 +1479,10 @@ void Async2Transformation::CreateResumptionSwitch()
         newEntryBB->SetTrueEdge(toCheckILOffset);
         toCheckILOffset->setLikelihood(RESUME_SUSPEND_LIKELIHOOD);
 
-        // Make checkILOffsetBB ->(true)  onNoContinuationBB 
+        // Make checkILOffsetBB ->(true)  onNoContinuationBB
         //                      ->(false) onContinuationBB
 
-        FlowEdge* toOnContinuationBB = m_comp->fgAddRefPred(onContinuationBB, checkILOffsetBB);
+        FlowEdge* toOnContinuationBB   = m_comp->fgAddRefPred(onContinuationBB, checkILOffsetBB);
         FlowEdge* toOnNoContinuationBB = m_comp->fgAddRefPred(onNoContinuationBB, checkILOffsetBB);
         checkILOffsetBB->SetCond(toOnNoContinuationBB, toOnContinuationBB);
         toOnContinuationBB->setLikelihood(RESUME_SUSPEND_LIKELIHOOD);
diff --git a/src/coreclr/jit/async.h b/src/coreclr/jit/async.h
index 313d72cd0952ac..6f572c0dbf37ab 100644
--- a/src/coreclr/jit/async.h
+++ b/src/coreclr/jit/async.h
@@ -11,7 +11,8 @@ class Async2Transformation
         unsigned GCDataIndex;
         unsigned GCDataCount;
 
-        explicit LiveLocalInfo(unsigned lclNum) : LclNum(lclNum)
+        explicit LiveLocalInfo(unsigned lclNum)
+            : LclNum(lclNum)
         {
         }
     };
@@ -32,7 +33,7 @@ class Async2Transformation
     unsigned                      m_exceptionVar            = BAD_VAR_NUM;
     BasicBlock*                   m_lastSuspensionBB        = nullptr;
     BasicBlock*                   m_lastResumptionBB        = nullptr;
-    BasicBlock* m_sharedReturnBB                            = nullptr;
+    BasicBlock*                   m_sharedReturnBB          = nullptr;
 
     void LiftLIREdges(BasicBlock*                    block,
                       GenTree*                       beyond,
@@ -45,16 +46,15 @@ class Async2Transformation
                    class AsyncLiveness&      life,
                    BasicBlock**              remainder);
 
-    GenTreeCall* CreateAllocContinuationCall(
-        AsyncLiveness& life,
-        GenTree* prevContinuation,
-        unsigned gcRefsCount,
-        unsigned int dataSize);
+    GenTreeCall* CreateAllocContinuationCall(AsyncLiveness& life,
+                                             GenTree*       prevContinuation,
+                                             unsigned       gcRefsCount,
+                                             unsigned int   dataSize);
 
-    GenTreeIndir* LoadFromOffset(GenTree*     base,
-                                 unsigned     offset,
-                                 var_types    type,
-                                 GenTreeFlags indirFlags = GTF_IND_NONFAULTING);
+    GenTreeIndir*    LoadFromOffset(GenTree*     base,
+                                    unsigned     offset,
+                                    var_types    type,
+                                    GenTreeFlags indirFlags = GTF_IND_NONFAULTING);
     GenTreeStoreInd* StoreAtOffset(GenTree* base, unsigned offset, GenTree* value);
 
     unsigned GetDataArrayVar();
@@ -69,7 +69,9 @@ class Async2Transformation
 
 public:
     Async2Transformation(Compiler* comp)
-        : m_comp(comp), m_liveLocals(comp->getAllocator(CMK_Async2)), m_resumptionBBs(comp->getAllocator(CMK_Async2))
+        : m_comp(comp)
+        , m_liveLocals(comp->getAllocator(CMK_Async2))
+        , m_resumptionBBs(comp->getAllocator(CMK_Async2))
     {
     }
 
diff --git a/src/coreclr/jit/codegenarm.cpp b/src/coreclr/jit/codegenarm.cpp
index a1d45801c54f4c..4d2b989ba2534f 100644
--- a/src/coreclr/jit/codegenarm.cpp
+++ b/src/coreclr/jit/codegenarm.cpp
@@ -1655,9 +1655,9 @@ void CodeGen::genEmitHelperCall(unsigned helper, int argSize, emitAttr retSize,
         GetEmitter()->emitIns_Call(emitter::EC_FUNC_TOKEN, compiler->eeFindHelper(helper),
                                    INDEBUG_LDISASM_COMMA(nullptr) addr, argSize, retSize,
                                    false, // hasAsyncRet
-                                   gcInfo.gcVarPtrSetCur, gcInfo.gcRegGCrefSetCur, gcInfo.gcRegByrefSetCur, DebugInfo(), REG_NA, REG_NA, 0,
-                                   0,    /* ilOffset, ireg, xreg, xmul, disp */
-                                   false /* isJump */
+                                   gcInfo.gcVarPtrSetCur, gcInfo.gcRegGCrefSetCur, gcInfo.gcRegByrefSetCur, DebugInfo(),
+                                   REG_NA, REG_NA, 0, 0, /* ilOffset, ireg, xreg, xmul, disp */
+                                   false                 /* isJump */
         );
     }
 
diff --git a/src/coreclr/jit/codegenarm64.cpp b/src/coreclr/jit/codegenarm64.cpp
index 852b537f5aa8f6..a8be3c64ddd075 100644
--- a/src/coreclr/jit/codegenarm64.cpp
+++ b/src/coreclr/jit/codegenarm64.cpp
@@ -5197,10 +5197,10 @@ void CodeGen::genEmitHelperCall(unsigned helper, int argSize, emitAttr retSize,
     GetEmitter()->emitIns_Call(callType, compiler->eeFindHelper(helper), INDEBUG_LDISASM_COMMA(nullptr) addr, argSize,
                                retSize, EA_UNKNOWN,
                                false, // hasAsyncRet
-                               gcInfo.gcVarPtrSetCur, gcInfo.gcRegGCrefSetCur,
-                               gcInfo.gcRegByrefSetCur, DebugInfo(), callTarget, /* ireg */
-                               REG_NA, 0, 0,                                     /* xreg, xmul, disp */
-                               false                                             /* isJump */
+                               gcInfo.gcVarPtrSetCur, gcInfo.gcRegGCrefSetCur, gcInfo.gcRegByrefSetCur, DebugInfo(),
+                               callTarget,   /* ireg */
+                               REG_NA, 0, 0, /* xreg, xmul, disp */
+                               false         /* isJump */
     );
 
     regMaskTP killMask = compiler->compHelperCallKillSet((CorInfoHelpFunc)helper);
diff --git a/src/coreclr/jit/codegenxarch.cpp b/src/coreclr/jit/codegenxarch.cpp
index 531309d4abf64f..63b06996fb2218 100644
--- a/src/coreclr/jit/codegenxarch.cpp
+++ b/src/coreclr/jit/codegenxarch.cpp
@@ -10309,7 +10309,8 @@ void CodeGen::genFnEpilog(BasicBlock* block)
                 // do nothing before popping the callee-saved registers
             }
 #ifdef TARGET_X86
-            else if ((compiler->compLclFrameSize == REGSIZE_BYTES) && !compiler->compJmpOpUsed && !compiler->compIsAsync2())
+            else if ((compiler->compLclFrameSize == REGSIZE_BYTES) && !compiler->compJmpOpUsed &&
+                     !compiler->compIsAsync2())
             {
                 // "pop ecx" will make ESP point to the callee-saved registers
                 inst_RV(INS_pop, REG_ECX, TYP_I_IMPL);
diff --git a/src/coreclr/jit/compiler.cpp b/src/coreclr/jit/compiler.cpp
index 039c99ac3a3ab5..6d2b49132065f1 100644
--- a/src/coreclr/jit/compiler.cpp
+++ b/src/coreclr/jit/compiler.cpp
@@ -488,8 +488,8 @@ Compiler::Compiler(ArenaAllocator*       arena,
     info.compILCodeSize   = methodInfo->ILCodeSize;
     info.compILImportSize = 0;
 
-    info.compHasNextCallRetAddr = false;
-    info.compIsVarArgs          = false;
+    info.compHasNextCallRetAddr    = false;
+    info.compIsVarArgs             = false;
     info.compUsesAsyncContinuation = false;
 }
 
diff --git a/src/coreclr/jit/compiler.h b/src/coreclr/jit/compiler.h
index 34bfd299e41ee3..418aa591ec5d56 100644
--- a/src/coreclr/jit/compiler.h
+++ b/src/coreclr/jit/compiler.h
@@ -8313,7 +8313,7 @@ class Compiler
     bool rpMustCreateEBPFrame(INDEBUG(const char** wbReason));
 
 private:
-    Lowering*            m_pLowering;   // Lowering; needed to Lower IR that's added or modified after Lowering.
+    Lowering*            m_pLowering; // Lowering; needed to Lower IR that's added or modified after Lowering.
     LinearScanInterface* m_pLinearScan = nullptr; // Linear Scan allocator
 
     /*
@@ -10782,12 +10782,12 @@ class Compiler
         // (2) the code is hot/cold split, and we issued less code than we expected
         // in the cold section (the hot section will always be padded out to compTotalHotCodeSize).
 
-        bool compIsStatic           : 1; // Is the method static (no 'this' pointer)?
-        bool compIsVarArgs          : 1; // Does the method have varargs parameters?
-        bool compInitMem            : 1; // Is the CORINFO_OPT_INIT_LOCALS bit set in the method info options?
-        bool compProfilerCallback   : 1; // JIT inserted a profiler Enter callback
-        bool compPublishStubParam   : 1; // EAX captured in prolog will be available through an intrinsic
-        bool compHasNextCallRetAddr : 1; // The NextCallReturnAddress intrinsic is used.
+        bool compIsStatic              : 1; // Is the method static (no 'this' pointer)?
+        bool compIsVarArgs             : 1; // Does the method have varargs parameters?
+        bool compInitMem               : 1; // Is the CORINFO_OPT_INIT_LOCALS bit set in the method info options?
+        bool compProfilerCallback      : 1; // JIT inserted a profiler Enter callback
+        bool compPublishStubParam      : 1; // EAX captured in prolog will be available through an intrinsic
+        bool compHasNextCallRetAddr    : 1; // The NextCallReturnAddress intrinsic is used.
         bool compUsesAsyncContinuation : 1; // The Async2CallContinuation intrinsic is used.
 
         var_types compRetType;       // Return type of the method as declared in IL (including SIMD normalization)
diff --git a/src/coreclr/jit/emit.cpp b/src/coreclr/jit/emit.cpp
index c5c3df296cf553..31bb0322a5e7a7 100644
--- a/src/coreclr/jit/emit.cpp
+++ b/src/coreclr/jit/emit.cpp
@@ -3561,8 +3561,8 @@ emitter::instrDesc* emitter::emitNewInstrCallInd(int              argCnt,
                                                  VARSET_VALARG_TP GCvars,
                                                  regMaskTP        gcrefRegs,
                                                  regMaskTP        byrefRegs,
-                                                 emitAttr         retSizeIn
-                                                     MULTIREG_HAS_SECOND_GC_RET_ONLY_ARG(emitAttr secondRetSize),
+                                                 emitAttr retSizeIn
+                                                      MULTIREG_HAS_SECOND_GC_RET_ONLY_ARG(emitAttr secondRetSize),
                                                  bool hasAsyncRet)
 {
     emitAttr retSize = (retSizeIn != EA_UNKNOWN) ? retSizeIn : EA_PTRSIZE;
@@ -3648,8 +3648,8 @@ emitter::instrDesc* emitter::emitNewInstrCallDir(int              argCnt,
                                                  VARSET_VALARG_TP GCvars,
                                                  regMaskTP        gcrefRegs,
                                                  regMaskTP        byrefRegs,
-                                                 emitAttr         retSizeIn
-                                                     MULTIREG_HAS_SECOND_GC_RET_ONLY_ARG(emitAttr secondRetSize),
+                                                 emitAttr retSizeIn
+                                                      MULTIREG_HAS_SECOND_GC_RET_ONLY_ARG(emitAttr secondRetSize),
                                                  bool hasAsyncRet)
 {
     emitAttr retSize = (retSizeIn != EA_UNKNOWN) ? retSizeIn : EA_PTRSIZE;
diff --git a/src/coreclr/jit/emitloongarch64.h b/src/coreclr/jit/emitloongarch64.h
index 897bfefe76b559..e8ccf930aff03e 100644
--- a/src/coreclr/jit/emitloongarch64.h
+++ b/src/coreclr/jit/emitloongarch64.h
@@ -118,7 +118,7 @@ instrDesc* emitNewInstrCallDir(int              argCnt,
                                regMaskTP        gcrefRegs,
                                regMaskTP        byrefRegs,
                                emitAttr retSize MULTIREG_HAS_SECOND_GC_RET_ONLY_ARG(emitAttr secondRetSize),
-                               bool hasAsyncRet);
+                               bool             hasAsyncRet);
 
 instrDesc* emitNewInstrCallInd(int              argCnt,
                                ssize_t          disp,
@@ -126,7 +126,7 @@ instrDesc* emitNewInstrCallInd(int              argCnt,
                                regMaskTP        gcrefRegs,
                                regMaskTP        byrefRegs,
                                emitAttr retSize MULTIREG_HAS_SECOND_GC_RET_ONLY_ARG(emitAttr secondRetSize),
-                               bool hasAsyncRet);
+                               bool             hasAsyncRet);
 
 /************************************************************************/
 /*               Private helpers for instruction output                 */
diff --git a/src/coreclr/jit/emitriscv64.h b/src/coreclr/jit/emitriscv64.h
index b2a91e57b3514f..95794758abb387 100644
--- a/src/coreclr/jit/emitriscv64.h
+++ b/src/coreclr/jit/emitriscv64.h
@@ -43,7 +43,7 @@ instrDesc* emitNewInstrCallDir(int              argCnt,
                                regMaskTP        gcrefRegs,
                                regMaskTP        byrefRegs,
                                emitAttr retSize MULTIREG_HAS_SECOND_GC_RET_ONLY_ARG(emitAttr secondRetSize),
-                               bool hasAsyncRet);
+                               bool             hasAsyncRet);
 
 instrDesc* emitNewInstrCallInd(int              argCnt,
                                ssize_t          disp,
@@ -51,7 +51,7 @@ instrDesc* emitNewInstrCallInd(int              argCnt,
                                regMaskTP        gcrefRegs,
                                regMaskTP        byrefRegs,
                                emitAttr retSize MULTIREG_HAS_SECOND_GC_RET_ONLY_ARG(emitAttr secondRetSize),
-                               bool hasAsyncRet);
+                               bool             hasAsyncRet);
 
 /************************************************************************/
 /*               Private helpers for instruction output                 */
diff --git a/src/coreclr/jit/emitxarch.h b/src/coreclr/jit/emitxarch.h
index c97b725b6b2789..cb7245766275b9 100644
--- a/src/coreclr/jit/emitxarch.h
+++ b/src/coreclr/jit/emitxarch.h
@@ -668,7 +668,7 @@ instrDesc* emitNewInstrCallDir(int              argCnt,
                                regMaskTP        gcrefRegs,
                                regMaskTP        byrefRegs,
                                emitAttr retSize MULTIREG_HAS_SECOND_GC_RET_ONLY_ARG(emitAttr secondRetSize),
-                               bool hasAsyncRet);
+                               bool             hasAsyncRet);
 
 instrDesc* emitNewInstrCallInd(int              argCnt,
                                ssize_t          disp,
@@ -676,7 +676,7 @@ instrDesc* emitNewInstrCallInd(int              argCnt,
                                regMaskTP        gcrefRegs,
                                regMaskTP        byrefRegs,
                                emitAttr retSize MULTIREG_HAS_SECOND_GC_RET_ONLY_ARG(emitAttr secondRetSize),
-                               bool hasAsyncRet);
+                               bool             hasAsyncRet);
 
 void    emitGetInsCns(const instrDesc* id, CnsVal* cv) const;
 ssize_t emitGetInsAmdCns(const instrDesc* id, CnsVal* cv) const;
diff --git a/src/coreclr/jit/fgbasic.cpp b/src/coreclr/jit/fgbasic.cpp
index de0cb2a8db4983..96c5d2496e42b7 100644
--- a/src/coreclr/jit/fgbasic.cpp
+++ b/src/coreclr/jit/fgbasic.cpp
@@ -2553,7 +2553,6 @@ void Compiler::fgAdjustForAddressExposedOrWrittenThis()
     }
 }
 
-
 //------------------------------------------------------------------------
 // fgInitializeThisCopyVar:
 //   Initialize the local used to copy the "this" instance to for struct
@@ -2565,7 +2564,7 @@ void Compiler::fgInitializeThisCopyVar()
     lvaThisCopyVar = lvaGrabTemp(false DEBUGARG("Copy of 'this'"));
     lvaSetStruct(lvaThisCopyVar, info.compClassHnd, false);
 
-    LclVarDsc* lclDsc = lvaGetDesc(lvaThisCopyVar);
+    LclVarDsc* lclDsc     = lvaGetDesc(lvaThisCopyVar);
     lclDsc->lvHasLdAddrOp = 1;
 }
 
diff --git a/src/coreclr/jit/fginline.cpp b/src/coreclr/jit/fginline.cpp
index 237f8004f9cc04..0ee4c4312036d9 100644
--- a/src/coreclr/jit/fginline.cpp
+++ b/src/coreclr/jit/fginline.cpp
@@ -1760,9 +1760,12 @@ void Compiler::fgInsertInlineeBlocks(InlineInfo* pInlineInfo)
 //    newStmt   - updated with the new statement
 //    callDI    - debug info for the call
 //
-void Compiler::fgInsertInlineeArgument(
-    InlineInfo* inlineInfo,
-    const InlArgInfo& argInfo, BasicBlock* block, Statement** afterStmt, Statement** newStmt, const DebugInfo& callDI)
+void Compiler::fgInsertInlineeArgument(InlineInfo*       inlineInfo,
+                                       const InlArgInfo& argInfo,
+                                       BasicBlock*       block,
+                                       Statement**       afterStmt,
+                                       Statement**       newStmt,
+                                       const DebugInfo&  callDI)
 {
     const bool argIsSingleDef = !argInfo.argHasLdargaOp && !argInfo.argHasStargOp;
     CallArg*   arg            = argInfo.arg;
@@ -1798,16 +1801,17 @@ void Compiler::fgInsertInlineeArgument(
         }
         else if (argInfo.argIsByRefToCopy)
         {
-            ClassLayout* layout = typGetObjLayout(inlineInfo->inlineCandidateInfo->clsHandle);
-            unsigned copyOfThisLcl = lvaGrabTemp(false DEBUGARG("Copy of inlinee struct instance"));
+            ClassLayout* layout        = typGetObjLayout(inlineInfo->inlineCandidateInfo->clsHandle);
+            unsigned     copyOfThisLcl = lvaGrabTemp(false DEBUGARG("Copy of inlinee struct instance"));
             lvaSetStruct(copyOfThisLcl, layout, false);
             GenTree* copyBlock = gtNewStoreLclVarNode(copyOfThisLcl, gtNewBlkIndir(layout, argNode));
-            *newStmt = gtNewStmt(copyBlock, callDI);
+            *newStmt           = gtNewStmt(copyBlock, callDI);
             fgInsertStmtAfter(block, *afterStmt, *newStmt);
             DISPSTMT(*newStmt);
             *afterStmt = *newStmt;
 
-            GenTree* storeTmp = gtNewTempStore(argInfo.argTmpNum, gtNewLclVarAddrNode(copyOfThisLcl, argNode->TypeGet()));
+            GenTree* storeTmp =
+                gtNewTempStore(argInfo.argTmpNum, gtNewLclVarAddrNode(copyOfThisLcl, argNode->TypeGet()));
             *newStmt = gtNewStmt(storeTmp, callDI);
             fgInsertStmtAfter(block, *afterStmt, *newStmt);
             DISPSTMT(*newStmt);
diff --git a/src/coreclr/jit/flowgraph.cpp b/src/coreclr/jit/flowgraph.cpp
index 55221f05204618..d99c939cef8161 100644
--- a/src/coreclr/jit/flowgraph.cpp
+++ b/src/coreclr/jit/flowgraph.cpp
@@ -2354,9 +2354,9 @@ PhaseStatus Compiler::fgAddInternal()
         if (lvaThisCopyVar != BAD_VAR_NUM)
         {
             ClassLayout* layout = lvaGetDesc(lvaThisCopyVar)->GetLayout();
-            GenTree* addr = gtNewLclVarNode(info.compThisArg);
-            GenTree* store = gtNewStoreLclVarNode(lvaThisCopyVar, gtNewBlkIndir(layout, addr));
-            Statement* stmt = fgNewStmtAtBeg(fgFirstBB, store);
+            GenTree*     addr   = gtNewLclVarNode(info.compThisArg);
+            GenTree*     store  = gtNewStoreLclVarNode(lvaThisCopyVar, gtNewBlkIndir(layout, addr));
+            Statement*   stmt   = fgNewStmtAtBeg(fgFirstBB, store);
 
             JITDUMP("\nCopy \"this\" to V%02u for struct instance method operating on copy\n", lvaThisCopyVar);
             DISPSTMT(stmt);
diff --git a/src/coreclr/jit/gentree.cpp b/src/coreclr/jit/gentree.cpp
index 917b7f1673cae9..9af8e95e526ef6 100644
--- a/src/coreclr/jit/gentree.cpp
+++ b/src/coreclr/jit/gentree.cpp
@@ -8386,11 +8386,11 @@ GenTreeCall* Compiler::gtNewCallNode(gtCallTypes           callType,
     node->gtCallType    = callType;
     node->gtCallMethHnd = callHnd;
     INDEBUG(node->callSig = nullptr;)
-    node->tailCallInfo      = nullptr;
-    node->gtRetClsHnd       = nullptr;
-    node->gtControlExpr     = nullptr;
-    node->gtCallMoreFlags   = GTF_CALL_M_EMPTY;
-    node->gtIsAsyncCall     = false;
+    node->tailCallInfo    = nullptr;
+    node->gtRetClsHnd     = nullptr;
+    node->gtControlExpr   = nullptr;
+    node->gtCallMoreFlags = GTF_CALL_M_EMPTY;
+    node->gtIsAsyncCall   = false;
     INDEBUG(node->gtCallDebugFlags = GTF_CALL_MD_EMPTY);
     node->gtInlineInfoCount = 0;
 
diff --git a/src/coreclr/jit/importer.cpp b/src/coreclr/jit/importer.cpp
index 1015423e9c5586..4ef90962f41584 100644
--- a/src/coreclr/jit/importer.cpp
+++ b/src/coreclr/jit/importer.cpp
@@ -13190,14 +13190,15 @@ void Compiler::impInlineInitVars(InlineInfo* pInlineInfo)
             return;
         }
 
-        if ((arg.GetWellKnownArg() == WellKnownArg::ThisPointer) && ((methInfo->options & CORINFO_OPT_COPY_STRUCT_INSTANCE) != 0))
+        if ((arg.GetWellKnownArg() == WellKnownArg::ThisPointer) &&
+            ((methInfo->options & CORINFO_OPT_COPY_STRUCT_INSTANCE) != 0))
         {
             // Method call to a struct instance method that operates on a copy.
             // We will load the instance as part of copying, so set up flags to
             // indicate that there is a side effect.
             inlArgInfo[ilArgCnt].argIsByRefToCopy = true;
-            inlArgInfo[ilArgCnt].argHasGlobRef = true;
-            inlArgInfo[ilArgCnt].argHasSideEff = true;
+            inlArgInfo[ilArgCnt].argHasGlobRef    = true;
+            inlArgInfo[ilArgCnt].argHasSideEff    = true;
         }
 
         ilArgCnt++;
diff --git a/src/coreclr/jit/importercalls.cpp b/src/coreclr/jit/importercalls.cpp
index 356b06c637f897..1bc7423a44bd05 100644
--- a/src/coreclr/jit/importercalls.cpp
+++ b/src/coreclr/jit/importercalls.cpp
@@ -919,8 +919,8 @@ var_types Compiler::impImportCall(OPCODE                  opcode,
         {
             if (varArgsCookie != nullptr)
             {
-                call->AsCall()->gtArgs.PushFront(this,
-                                                 NewCallArg::Primitive(varArgsCookie).WellKnown(WellKnownArg::VarArgsCookie));
+                call->AsCall()->gtArgs.PushFront(this, NewCallArg::Primitive(varArgsCookie)
+                                                           .WellKnown(WellKnownArg::VarArgsCookie));
             }
 
             if (sig->isAsyncCall())
diff --git a/src/coreclr/jit/inline.h b/src/coreclr/jit/inline.h
index 4599c0388c0687..72bb7998e52210 100644
--- a/src/coreclr/jit/inline.h
+++ b/src/coreclr/jit/inline.h
@@ -658,8 +658,9 @@ struct InlArgInfo
     unsigned argHasStargOp           : 1; // Is there STARG(s) operation on this argument?
     unsigned argIsByRefToStructLocal : 1; // Is this arg an address of a struct local or a normed struct local or a
                                           // field in them?
-    unsigned argIsExact : 1;              // Is this arg of an exact class?
-    unsigned argIsByRefToCopy        : 1; // Arg is a reference to a local that will be copied (used when inlining async2 struct instance methods)
+    unsigned argIsExact       : 1;        // Is this arg of an exact class?
+    unsigned argIsByRefToCopy : 1; // Arg is a reference to a local that will be copied (used when inlining async2
+                                   // struct instance methods)
 };
 
 // InlLclVarInfo describes inline candidate argument and local variable properties.
diff --git a/src/coreclr/jit/lclvars.cpp b/src/coreclr/jit/lclvars.cpp
index a6db8d51fb6192..dc20b5582aa153 100644
--- a/src/coreclr/jit/lclvars.cpp
+++ b/src/coreclr/jit/lclvars.cpp
@@ -1505,7 +1505,7 @@ void Compiler::lvaInitAsyncContinuation(InitVarDscInfo* varDscInfo)
     varDsc->lvType          = TYP_REF;
     varDsc->lvIsParam       = true;
 
-     // The final home for this incoming register might be our local stack frame
+    // The final home for this incoming register might be our local stack frame
     varDsc->lvOnFrame = true;
 
 #ifdef DEBUG
@@ -1517,7 +1517,8 @@ void Compiler::lvaInitAsyncContinuation(InitVarDscInfo* varDscInfo)
         // Passed in register
 
         varDsc->lvIsRegArg = 1;
-        varDsc->SetArgReg(genMapRegArgNumToRegNum(varDscInfo->regArgNum(TYP_INT), varDsc->TypeGet(), info.compCallConv));
+        varDsc->SetArgReg(
+            genMapRegArgNumToRegNum(varDscInfo->regArgNum(TYP_INT), varDsc->TypeGet(), info.compCallConv));
 #if FEATURE_MULTIREG_ARGS
         varDsc->SetOtherArgReg(REG_NA);
 #endif
diff --git a/src/coreclr/jit/morph.cpp b/src/coreclr/jit/morph.cpp
index d8a68ecf487dd7..6d98a3aaf66019 100644
--- a/src/coreclr/jit/morph.cpp
+++ b/src/coreclr/jit/morph.cpp
@@ -188,8 +188,8 @@ GenTree* Compiler::fgMorphIntoHelperCall(GenTree* tree, int helper, bool morphAr
     call->gtRetClsHnd     = nullptr;
     call->gtCallMoreFlags = GTF_CALL_M_EMPTY;
     INDEBUG(call->gtCallDebugFlags = GTF_CALL_MD_EMPTY);
-    call->gtControlExpr   = nullptr;
-    call->gtIsAsyncCall   = false;
+    call->gtControlExpr = nullptr;
+    call->gtIsAsyncCall = false;
     call->ClearInlineInfo();
 #ifdef UNIX_X86_ABI
     call->gtFlags |= GTF_CALL_POP_ARGS;
diff --git a/src/coreclr/jit/valuenum.cpp b/src/coreclr/jit/valuenum.cpp
index 019a23b99d55f8..52db63cba6c9dc 100644
--- a/src/coreclr/jit/valuenum.cpp
+++ b/src/coreclr/jit/valuenum.cpp
@@ -10457,8 +10457,8 @@ static genTreeOps genTreeOpsIllegalAsVNFunc[] = {GT_IND, // When we do heap memo
                                                  GT_NOP,
 
                                                  // These control-flow operations need no values.
-                                                 GT_JTRUE, GT_RETURN, GT_RETURN_SUSPEND, GT_SWITCH, GT_RETFILT, GT_CKFINITE,
-                                                 GT_SWIFT_ERROR_RET};
+                                                 GT_JTRUE, GT_RETURN, GT_RETURN_SUSPEND, GT_SWITCH, GT_RETFILT,
+                                                 GT_CKFINITE, GT_SWIFT_ERROR_RET};
 
 void ValueNumStore::ValidateValueNumStoreStatics()
 {

From de4719c7838441289e3ae055443ef596a2cf2265 Mon Sep 17 00:00:00 2001
From: Vladimir Sadov <vsadov@microsoft.com>
Date: Mon, 24 Feb 2025 12:27:15 -0800
Subject: [PATCH 136/203] Implement a change in IL API to use
 RuntimeHelpers.Await<T>(Task<T>) and similar helpers. (#2951)

* T RuntimeHelpers.Await<T>(Task<T>)

* state machine version of  Await and friends

* bump roslyn ref

* more Await helpers

* remove no longer needed test

* undo no longer needed metasig

* comment

* formatting

* comment

* implements JIT optimization for Await intrinsics

* make the JitOptimizeAwait switch RELEASE_CONFIG_INTEGER  (for testing/benchmarking purposes)

* isIntrinsic

* CORINFO_TOKENKIND_Await

* revert CORINFO_CALLINFO_RUNTIMEASYNC_VARIANT

* undo unnecessary diff

* Apply suggestions from code review

Co-authored-by: Jakob Botsch Nielsen <Jakob.botsch.nielsen@gmail.com>

* one more unnecessary `return`

* uncomment ReturnsStructGC scenario.

* uncomment struct testcase

* awaiting things that are not async

---------

Co-authored-by: Jakob Botsch Nielsen <Jakob.botsch.nielsen@gmail.com>
---
 buildroslynnugets.cmd                         |  4 +-
 eng/Versions.props                            |  6 +-
 src/coreclr/inc/corinfo.h                     |  3 +
 src/coreclr/jit/importer.cpp                  | 58 ++++++++++++++-
 src/coreclr/jit/importercalls.cpp             | 17 +++--
 src/coreclr/jit/jitconfigvalues.h             |  2 +
 src/coreclr/jit/namedintrinsiclist.h          |  1 +
 .../src/CompatibilitySuppressions.xml         | 24 ++++++
 src/coreclr/vm/jitinterface.cpp               | 10 +++
 src/coreclr/vm/method.hpp                     | 49 +++++++++----
 src/coreclr/vm/methodtablebuilder.cpp         | 20 +++--
 src/coreclr/vm/threadsuspend.cpp              | 12 +--
 .../CompilerServices/RuntimeHelpers.cs        | 73 +++++++++++++++++--
 .../System.Runtime/ref/System.Runtime.cs      |  4 +
 src/tests/async/awaitingnotasync.cs           | 52 +++++++++++++
 src/tests/async/awaitingnotasync.csproj       |  8 ++
 16 files changed, 298 insertions(+), 45 deletions(-)
 create mode 100644 src/tests/async/awaitingnotasync.cs
 create mode 100644 src/tests/async/awaitingnotasync.csproj

diff --git a/buildroslynnugets.cmd b/buildroslynnugets.cmd
index 281ddee98868f3..bc51f7ce2d7db7 100644
--- a/buildroslynnugets.cmd
+++ b/buildroslynnugets.cmd
@@ -1,7 +1,7 @@
 setlocal ENABLEEXTENSIONS
 pushd %~dp0
-set ASYNC_ROSLYN_COMMIT=10a5611cb10cd64876a2638664f0740255197e1b
-set ASYNC_SUFFIX=async-11
+set ASYNC_ROSLYN_COMMIT=c1d47ed4bdbd28137fce8fbb350771677aa4cf15
+set ASYNC_SUFFIX=async-12
 set ASYNC_ROSLYN_BRANCH=demos/async2-experiment1
 
 cd ..
diff --git a/eng/Versions.props b/eng/Versions.props
index 21c3ead510a7d6..3fb08863235ae7 100644
--- a/eng/Versions.props
+++ b/eng/Versions.props
@@ -44,9 +44,9 @@
       Any tools that contribute to the design-time experience should use the MicrosoftCodeAnalysisVersion_LatestVS property above to ensure
       they do not break the local dev experience.
     -->
-    <MicrosoftCodeAnalysisCSharpVersion>4.13.0-async-11</MicrosoftCodeAnalysisCSharpVersion>
-    <MicrosoftCodeAnalysisVersion>4.13.0-async-11</MicrosoftCodeAnalysisVersion>
-    <MicrosoftNetCompilersToolsetVersion>4.13.0-async-11</MicrosoftNetCompilersToolsetVersion>
+    <MicrosoftCodeAnalysisCSharpVersion>4.13.0-async-12</MicrosoftCodeAnalysisCSharpVersion>
+    <MicrosoftCodeAnalysisVersion>4.13.0-async-12</MicrosoftCodeAnalysisVersion>
+    <MicrosoftNetCompilersToolsetVersion>4.13.0-async-12</MicrosoftNetCompilersToolsetVersion>
   </PropertyGroup>
   <!--
     For source generator support we need to target multiple versions of Roslyn in order to be able to run on older versions of Roslyn.
diff --git a/src/coreclr/inc/corinfo.h b/src/coreclr/inc/corinfo.h
index b4cfe8a9540c9b..ae8c4871d98f3e 100644
--- a/src/coreclr/inc/corinfo.h
+++ b/src/coreclr/inc/corinfo.h
@@ -1408,6 +1408,9 @@ enum CorInfoTokenKind
 
     // token comes from devirtualizing a method
     CORINFO_TOKENKIND_DevirtualizedMethod = 0x800 | CORINFO_TOKENKIND_Method,
+
+    // token comes from runtime async awaiting pattern
+    CORINFO_TOKENKIND_Await = 0x2000 | CORINFO_TOKENKIND_Method,
 };
 
 struct CORINFO_RESOLVED_TOKEN
diff --git a/src/coreclr/jit/importer.cpp b/src/coreclr/jit/importer.cpp
index 4ef90962f41584..f3a58285830e26 100644
--- a/src/coreclr/jit/importer.cpp
+++ b/src/coreclr/jit/importer.cpp
@@ -8947,7 +8947,63 @@ void Compiler::impImportBlockCode(BasicBlock* block)
                 // many other places.  We unfortunately embed that knowledge here.
                 if (opcode != CEE_CALLI)
                 {
-                    _impResolveToken(CORINFO_TOKENKIND_Method);
+                    bool isAwait = false;
+                    if (JitConfig.JitOptimizeAwait())
+                    {
+                        // If we see the following code pattern in runtime async methods:
+                        //
+                        //    call[virt] <Method>
+                        //    call       <Await>
+                        //
+                        //  we emit an eqivalent of
+                        //
+                        //    call[virt] <RtMethod>
+                        //
+                        //  where "RtMethod" is the runtime-async counterpart of a Task-returning method.
+                        //
+                        //  NOTE: we could potentially check if Method is not a thunk and, in cases when we can tell,
+                        //        bypass this optimization. Otherwise in a non-thunk case we would be
+                        //        replacing the pattern with a call to a thunk, which contains roughly the same code.
+
+                        const BYTE* nextOpcode = codeAddr + sizeof(mdToken);
+                        if (compIsAsync2() && (nextOpcode + sizeof(mdToken) < codeEndp) &&
+                            (getU1LittleEndian(nextOpcode) == CEE_CALL))
+                        {
+                            // resolve the next token
+                            CORINFO_RESOLVED_TOKEN nextCallTok;
+                            impResolveToken(nextOpcode + 1, &nextCallTok, CORINFO_TOKENKIND_Method);
+
+                            // check if it is an Await intrinsic
+                            if (eeIsIntrinsic(nextCallTok.hMethod) &&
+                                lookupNamedIntrinsic(nextCallTok.hMethod) == NI_System_Runtime_CompilerServices_RuntimeHelpers_Await)
+                            {
+                                // yes, this is an Await
+                                isAwait = true;
+                            }
+                        }
+                    }
+
+                    if (isAwait)
+                    {
+                        _impResolveToken(CORINFO_TOKENKIND_Await);
+                        if (resolvedToken.hMethod != NULL)
+                        {
+                            // There is a runtime async variant that is implicitly awaitable, just call that.
+                            // Skip the call to `Await`
+                            codeAddr += 1 + sizeof(mdToken);
+                        }
+                        else
+                        {
+                            // This can happen in rare cases when the Task-returning method is not a runtime Async function.
+                            // For example "T M1<T>(T arg) => arg" when called with a Task argument.
+                            // Treat that as a regualr call that is Awaited
+                            _impResolveToken(CORINFO_TOKENKIND_Method);
+                        }
+                    }
+                    else
+                    {
+                        _impResolveToken(CORINFO_TOKENKIND_Method);
+                    }
 
                     eeGetCallInfo(&resolvedToken,
                                   (prefixFlags & PREFIX_CONSTRAINED) ? &constrainedResolvedToken : nullptr,
diff --git a/src/coreclr/jit/importercalls.cpp b/src/coreclr/jit/importercalls.cpp
index 1bc7423a44bd05..7725b32dac51d5 100644
--- a/src/coreclr/jit/importercalls.cpp
+++ b/src/coreclr/jit/importercalls.cpp
@@ -528,7 +528,8 @@ var_types Compiler::impImportCall(OPCODE                  opcode,
         // calls in JIT generated state machines only.
         if (compIsAsync2() &&
             ((ni == NI_System_Runtime_CompilerServices_RuntimeHelpers_AwaitAwaiterFromRuntimeAsync) ||
-             (ni == NI_System_Runtime_CompilerServices_RuntimeHelpers_UnsafeAwaitAwaiterFromRuntimeAsync)))
+             (ni == NI_System_Runtime_CompilerServices_RuntimeHelpers_UnsafeAwaitAwaiterFromRuntimeAsync) ||
+             (ni == NI_System_Runtime_CompilerServices_RuntimeHelpers_Await)))
         {
             assert((call != nullptr) && call->OperIs(GT_CALL));
             call->AsCall()->gtIsAsyncCall = true;
@@ -913,7 +914,7 @@ var_types Compiler::impImportCall(OPCODE                  opcode,
     impPopCallArgs(sig, call->AsCall());
 
     // Extra args
-    if ((instParam != nullptr) || sig->isAsyncCall() || (varArgsCookie != nullptr))
+    if ((instParam != nullptr) || call->AsCall()->IsAsync2() || (varArgsCookie != nullptr))
     {
         if (Target::g_tgtArgOrder == Target::ARG_ORDER_R2L)
         {
@@ -923,7 +924,7 @@ var_types Compiler::impImportCall(OPCODE                  opcode,
                                                            .WellKnown(WellKnownArg::VarArgsCookie));
             }
 
-            if (sig->isAsyncCall())
+            if (call->AsCall()->IsAsync2())
             {
                 call->AsCall()->gtArgs.PushFront(this, NewCallArg::Primitive(gtNewNull(), TYP_REF)
                                                            .WellKnown(WellKnownArg::AsyncContinuation));
@@ -943,7 +944,7 @@ var_types Compiler::impImportCall(OPCODE                  opcode,
                                                 NewCallArg::Primitive(instParam).WellKnown(WellKnownArg::InstParam));
             }
 
-            if (sig->isAsyncCall())
+            if (call->AsCall()->IsAsync2())
             {
                 call->AsCall()->gtArgs.PushBack(this, NewCallArg::Primitive(gtNewNull(), TYP_REF)
                                                           .WellKnown(WellKnownArg::AsyncContinuation));
@@ -3376,7 +3377,8 @@ GenTree* Compiler::impIntrinsic(CORINFO_CLASS_HANDLE    clsHnd,
     }
 
     if ((ni == NI_System_Runtime_CompilerServices_RuntimeHelpers_AwaitAwaiterFromRuntimeAsync) ||
-        (ni == NI_System_Runtime_CompilerServices_RuntimeHelpers_UnsafeAwaitAwaiterFromRuntimeAsync))
+        (ni == NI_System_Runtime_CompilerServices_RuntimeHelpers_UnsafeAwaitAwaiterFromRuntimeAsync) ||
+        (ni == NI_System_Runtime_CompilerServices_RuntimeHelpers_Await))
     {
         // These are marked intrinsics simply to mark the call node as async,
         // which the caller will do. Make sure we keep pIntrinsicName assigned
@@ -10840,6 +10842,11 @@ NamedIntrinsic Compiler::lookupNamedIntrinsic(CORINFO_METHOD_HANDLE method)
                                 result =
                                     NI_System_Runtime_CompilerServices_RuntimeHelpers_UnsafeAwaitAwaiterFromRuntimeAsync;
                             }
+                            else if (strcmp(methodName, "Await") == 0)
+                            {
+                                result =
+                                    NI_System_Runtime_CompilerServices_RuntimeHelpers_Await;
+                            }
                             else if (strcmp(methodName, "SuspendAsync2") == 0)
                             {
                                 result = NI_System_Runtime_CompilerServices_RuntimeHelpers_SuspendAsync2;
diff --git a/src/coreclr/jit/jitconfigvalues.h b/src/coreclr/jit/jitconfigvalues.h
index 4c5fc2e8d5328e..f331a323da1ea8 100644
--- a/src/coreclr/jit/jitconfigvalues.h
+++ b/src/coreclr/jit/jitconfigvalues.h
@@ -586,6 +586,8 @@ OPT_CONFIG_INTEGER(JitDoIfConversion, "JitDoIfConversion", 1)
 OPT_CONFIG_INTEGER(JitDoOptimizeMaskConversions, "JitDoOptimizeMaskConversions", 1) // Perform optimization of mask
                                                                                     // conversions
 
+RELEASE_CONFIG_INTEGER(JitOptimizeAwait, "JitOptimizeAwait", 1) // Perform optimization of Await intrinsics
+
 RELEASE_CONFIG_INTEGER(JitEnableOptRepeat, "JitEnableOptRepeat", 1) // If zero, do not allow JitOptRepeat
 RELEASE_CONFIG_METHODSET(JitOptRepeat, "JitOptRepeat")            // Runs optimizer multiple times on specified methods
 RELEASE_CONFIG_INTEGER(JitOptRepeatCount, "JitOptRepeatCount", 2) // Number of times to repeat opts when repeating
diff --git a/src/coreclr/jit/namedintrinsiclist.h b/src/coreclr/jit/namedintrinsiclist.h
index 3b64215ea79a02..54b8225d6f4a48 100644
--- a/src/coreclr/jit/namedintrinsiclist.h
+++ b/src/coreclr/jit/namedintrinsiclist.h
@@ -120,6 +120,7 @@ enum NamedIntrinsic : unsigned short
     NI_System_Runtime_CompilerServices_RuntimeHelpers_GetMethodTable,
     NI_System_Runtime_CompilerServices_RuntimeHelpers_AwaitAwaiterFromRuntimeAsync,
     NI_System_Runtime_CompilerServices_RuntimeHelpers_UnsafeAwaitAwaiterFromRuntimeAsync,
+    NI_System_Runtime_CompilerServices_RuntimeHelpers_Await,
     NI_System_Runtime_CompilerServices_RuntimeHelpers_SuspendAsync2,
     NI_System_Runtime_CompilerServices_RuntimeHelpers_get_RuntimeAsyncViaJitGeneratedStateMachines,
 
diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/CompatibilitySuppressions.xml b/src/coreclr/nativeaot/System.Private.CoreLib/src/CompatibilitySuppressions.xml
index 4897d4b5ffe12c..69db65a1850946 100644
--- a/src/coreclr/nativeaot/System.Private.CoreLib/src/CompatibilitySuppressions.xml
+++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/CompatibilitySuppressions.xml
@@ -857,6 +857,30 @@
     <DiagnosticId>CP0002</DiagnosticId>
     <Target>M:System.Threading.Lock.#ctor(System.Boolean)</Target>
   </Suppression>
+  <Suppression>
+    <DiagnosticId>CP0002</DiagnosticId>
+    <Target>M:System.Runtime.CompilerServices.RuntimeHelpers.Await(System.Threading.Tasks.Task)</Target>
+    <Left>ref/net10.0/System.Private.CoreLib.dll</Left>
+    <Right>lib/net10.0/System.Private.CoreLib.dll</Right>
+  </Suppression>
+  <Suppression>
+    <DiagnosticId>CP0002</DiagnosticId>
+    <Target>M:System.Runtime.CompilerServices.RuntimeHelpers.Await(System.Threading.Tasks.ValueTask)</Target>
+    <Left>ref/net10.0/System.Private.CoreLib.dll</Left>
+    <Right>lib/net10.0/System.Private.CoreLib.dll</Right>
+  </Suppression>
+  <Suppression>
+    <DiagnosticId>CP0002</DiagnosticId>
+    <Target>M:System.Runtime.CompilerServices.RuntimeHelpers.Await``1(System.Threading.Tasks.Task{``0})</Target>
+    <Left>ref/net10.0/System.Private.CoreLib.dll</Left>
+    <Right>lib/net10.0/System.Private.CoreLib.dll</Right>
+  </Suppression>
+  <Suppression>
+    <DiagnosticId>CP0002</DiagnosticId>
+    <Target>M:System.Runtime.CompilerServices.RuntimeHelpers.Await``1(System.Threading.Tasks.ValueTask{``0})</Target>
+    <Left>ref/net10.0/System.Private.CoreLib.dll</Left>
+    <Right>lib/net10.0/System.Private.CoreLib.dll</Right>
+  </Suppression>
   <Suppression>
     <DiagnosticId>CP0002</DiagnosticId>
     <Target>M:System.Runtime.CompilerServices.RuntimeHelpers.AwaitAwaiterFromRuntimeAsync``1(``0)</Target>
diff --git a/src/coreclr/vm/jitinterface.cpp b/src/coreclr/vm/jitinterface.cpp
index fd0886e01641f3..71af8474315b4d 100644
--- a/src/coreclr/vm/jitinterface.cpp
+++ b/src/coreclr/vm/jitinterface.cpp
@@ -1100,6 +1100,16 @@ void CEEInfo::resolveToken(/* IN, OUT */ CORINFO_RESOLVED_TOKEN * pResolvedToken
             th = ClassLoader::LoadArrayTypeThrowing(th);
             break;
 
+        case CORINFO_TOKENKIND_Await:
+            // in rare cases a method that returns Task is not actually TaskReturning (i.e. returns T).
+            // we cannot resolve to an async2 variant in such case.
+            // return NULL, so that caller would re-resolve as a regular method call
+            pMD = pMD->IsTaskReturningMethod() ?
+                pMD->GetAsyncOtherVariant(/*allowInstParam*/FALSE):
+                NULL;
+
+            break;
+
         default:
             // Disallow ELEMENT_TYPE_BYREF and ELEMENT_TYPE_VOID
             if (et == ELEMENT_TYPE_BYREF || et == ELEMENT_TYPE_VOID)
diff --git a/src/coreclr/vm/method.hpp b/src/coreclr/vm/method.hpp
index 49168a9b734a25..3766cfdf050729 100644
--- a/src/coreclr/vm/method.hpp
+++ b/src/coreclr/vm/method.hpp
@@ -57,20 +57,30 @@ EXTERN_C VOID STDCALL NDirectImportThunk();
 
 enum class AsyncMethodKind
 {
-    // Regular methods, including Task-returning ones.
-    // Those that do not return Task/ValueTask do not get a thunk sibling
+    // Regular methods not returning tasks
+    // These do not get a thunk sibling.
+    // N.B. Generic T-returning methods are NotAsync
     NotAsync,
 
+    // Regular methods that return Task/ValueTask
+    // These methods have a synthetic sibling that is an async2 thunk
+    TaskReturning,
+
     // Task-returning methods marked as async in IL.
     // The method body forwards to Async2 implementation thunk (method with kind AsyncImplHelper)
-    Async,
+    RuntimeAsync,
 
     // Synthetic Async2 method that contains the
     // actual implementation transformed into a resumable state machine
     AsyncImplHelper,
 
-    // Synthetic Async2 method that forwards to the NotAsync Task-returning method
-    AsyncThunkHelper
+    // Synthetic Async2 method that forwards to a TaskReturning method
+    AsyncThunkHelper,
+
+    // Actual IL method that is explicitly declared as Async2 and thus compiled into a state machine.
+    // Such methods do not get Async thunks and can only be called from another Async2 method using Async2 call convention.
+    // This is used in a few infrastructure methods like `Await`
+    AsyncImplExplicit,
 };
 
 struct AsyncMethodData
@@ -1614,9 +1624,9 @@ class MethodDesc
                                                                      TypeHandle instType,
                                                                      Instantiation methodInst);
 
-    MethodDesc* GetAsyncOtherVariant()
+    MethodDesc* GetAsyncOtherVariant(BOOL allowInstParam = TRUE)
     {
-        return FindOrCreateAssociatedMethodDesc(this, GetMethodTable(), FALSE, GetMethodInstantiation(), TRUE, FALSE, TRUE, AsyncVariantLookup::AsyncOtherVariant);
+        return FindOrCreateAssociatedMethodDesc(this, GetMethodTable(), FALSE, GetMethodInstantiation(), allowInstParam, FALSE, TRUE, AsyncVariantLookup::AsyncOtherVariant);
     }
 
     // True if a MD is an funny BoxedEntryPointStub (not from the method table) or
@@ -1824,15 +1834,28 @@ class MethodDesc
             asyncKind == AsyncMethodKind::AsyncImplHelper;
     }
 
+    inline bool IsTaskReturningMethod() const
+    {
+        LIMITED_METHOD_DAC_CONTRACT;
+        if (!HasAsyncMethodData())
+            return false;
+        auto asyncKind = GetAddrOfAsyncMethodData()->kind;
+        return asyncKind == AsyncMethodKind::RuntimeAsync ||
+            asyncKind == AsyncMethodKind::TaskReturning;
+    }
+
     // We use "async2" for runtime async methods that return "Unwrapped" values (i.e. T instead of Task<T>)
     // The type of promise is typically captured in a modreq.
     // CONSIDER: We probably need a better name for the concept, but it is hard to beat shortness of "async2"
     inline bool IsAsync2Method() const
     {
-        // Right now the only Async2 methods that exist are synthetic helpers.
-        // It is possible to declare an Async2 method directly in IL/Metadata,
-        // but we do not have a scenario for that.
-        return IsAsyncHelperMethod();
+        LIMITED_METHOD_DAC_CONTRACT;
+        if (!HasAsyncMethodData())
+            return false;
+        auto asyncKind = GetAddrOfAsyncMethodData()->kind;
+        return asyncKind == AsyncMethodKind::AsyncThunkHelper ||
+            asyncKind == AsyncMethodKind::AsyncImplHelper ||
+            asyncKind == AsyncMethodKind::AsyncImplExplicit;
     }
 
     inline bool IsStructMethodOperatingOnCopy()
@@ -1848,7 +1871,7 @@ class MethodDesc
         return GetAddrOfAsyncMethodData()->kind == AsyncMethodKind::AsyncImplHelper;
     }
 
-    // The method is a Task/async2 adapter to an async2/Task implementation
+    // The method is a synthetic Task/async2 adapter to an async2/Task implementation
     inline bool IsAsyncThunkMethod() const
     {
         LIMITED_METHOD_DAC_CONTRACT;
@@ -1857,7 +1880,7 @@ class MethodDesc
 
         auto asyncType = GetAddrOfAsyncMethodData()->kind;
         return asyncType == AsyncMethodKind::AsyncThunkHelper ||
-            asyncType == AsyncMethodKind::Async;
+            asyncType == AsyncMethodKind::RuntimeAsync;
     }
 
     inline bool HasAsyncMethodData() const
diff --git a/src/coreclr/vm/methodtablebuilder.cpp b/src/coreclr/vm/methodtablebuilder.cpp
index f864e625aa8aa5..9cb5ab2d931b74 100644
--- a/src/coreclr/vm/methodtablebuilder.cpp
+++ b/src/coreclr/vm/methodtablebuilder.cpp
@@ -3595,17 +3595,27 @@ MethodTableBuilder::EnumerateClassMethods()
                 if (IsAsyncTaskMethodTaskReturningMethod(asyncMethodType))
                 {
                     // ordinary Task-returning method:
-                    //    declare an ordinary method and add a helper thunk with Async2 signature
+                    //    declare a TaskReturning method and add a helper thunk with Async2 signature
                     // 
                     // IsMiAsync Task-returning method:
-                    //    declare an Async method and add a helper method with the actual implementation
-                    //    the Async method becomes a thunk to the implementation helper.
-                    pNewMethod->SetAsyncMethodKind(IsMiAsync(dwImplFlags) ? AsyncMethodKind::Async : AsyncMethodKind::NotAsync);
+                    //    declare a RuntimeAsync method and add a helper method with the actual implementation
+                    //    the RuntimeAsync method becomes a thunk to the implementation helper.
+                    pNewMethod->SetAsyncMethodKind(IsMiAsync(dwImplFlags) ? AsyncMethodKind::RuntimeAsync : AsyncMethodKind::TaskReturning);
                 }
                 else
                 {
                     _ASSERTE(IsAsyncTaskMethodNormal(asyncMethodType));
-                    pNewMethod->SetAsyncMethodKind(AsyncMethodKind::NotAsync);
+
+                    if (IsMiAsync(dwImplFlags))
+                    {
+                        // TODO: explicitly-async methods have special semantics that is useful in the implementation of runtime async itself.
+                        //       It should not be valid to declare this outside of runtime infrastructure methods. (exact criteria TBD)
+                        pNewMethod->SetAsyncMethodKind(AsyncMethodKind::AsyncImplExplicit);
+                    }
+                    else
+                    {
+                        pNewMethod->SetAsyncMethodKind(AsyncMethodKind::NotAsync);
+                    }
                 }
 
                 pDeclaredMethod = pNewMethod;
diff --git a/src/coreclr/vm/threadsuspend.cpp b/src/coreclr/vm/threadsuspend.cpp
index 6aec5a3ef491b1..bcdba22af83b4e 100644
--- a/src/coreclr/vm/threadsuspend.cpp
+++ b/src/coreclr/vm/threadsuspend.cpp
@@ -4909,15 +4909,6 @@ void STDCALL OnHijackWorker(HijackArgs * pArgs)
 #endif // HIJACK_NONINTERRUPTIBLE_THREADS
 }
 
-bool IsSpecialCaseAsyncRet(MethodDesc* pMD)
-{
-    // TODO: What's the right way to do this through CoreLibBinder without
-    // causing loading to happen? Also, can we just mark them as async2 in SPC,
-    // or force them to be fully interruptible?
-    LPCUTF8 name = pMD->GetName();
-    return strcmp(name, "UnsafeAwaitAwaiterFromRuntimeAsync") == 0 || strcmp(name, "AwaitAwaiterFromRuntimeAsync") == 0;
-}
-
 static bool GetReturnAddressHijackInfo(EECodeInfo *pCodeInfo, ReturnKind *pReturnKind, bool* hasAsyncRet)
 {
     GCInfoToken gcInfoToken = pCodeInfo->GetGCInfoToken();
@@ -4928,8 +4919,7 @@ static bool GetReturnAddressHijackInfo(EECodeInfo *pCodeInfo, ReturnKind *pRetur
     *hasAsyncRet = false;
 
     MethodDesc* pMD = pCodeInfo->GetMethodDesc();
-    *hasAsyncRet = pMD->IsAsync2Method() ||
-        (pMD->IsIntrinsic() && IsSpecialCaseAsyncRet(pMD));
+    *hasAsyncRet = pMD->IsAsync2Method();
 
     return true;
 }
diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.cs
index f5dfe8afc578d7..b2abfafab488c7 100644
--- a/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.cs
+++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.cs
@@ -178,8 +178,8 @@ public static unsafe ReadOnlySpan<T> CreateSpan<T>(RuntimeFieldHandle fldHandle)
 
 #if !NATIVEAOT
         [Intrinsic]
-        [MethodImpl(MethodImplOptions.NoInlining)]
         [BypassReadyToRun]
+        [MethodImpl(MethodImplOptions.NoInlining | (MethodImplOptions)0x0400)]  // NoInlining | Async
         public static void AwaitAwaiterFromRuntimeAsync<TAwaiter>(TAwaiter awaiter) where TAwaiter : INotifyCompletion
         {
             ref RuntimeAsyncAwaitState state = ref t_runtimeAsyncAwaitState;
@@ -189,14 +189,13 @@ public static void AwaitAwaiterFromRuntimeAsync<TAwaiter>(TAwaiter awaiter) wher
 
             state.Notifier = awaiter;
             SuspendAsync2(sentinelContinuation);
-            return;
         }
 
         // Marked intrinsic since for JIT state machines this needs to be
-        // recognizes as an async2 call.
+        // recognized as an async2 call.
         [Intrinsic]
         [BypassReadyToRun]
-        [MethodImpl(MethodImplOptions.NoInlining)]
+        [MethodImpl(MethodImplOptions.NoInlining | (MethodImplOptions)0x0400)]  // NoInlining | Async
         public static void UnsafeAwaitAwaiterFromRuntimeAsync<TAwaiter>(TAwaiter awaiter) where TAwaiter : ICriticalNotifyCompletion
         {
             ref RuntimeAsyncAwaitState state = ref t_runtimeAsyncAwaitState;
@@ -206,8 +205,72 @@ public static void UnsafeAwaitAwaiterFromRuntimeAsync<TAwaiter>(TAwaiter awaiter
 
             state.Notifier = awaiter;
             SuspendAsync2(sentinelContinuation);
-            return;
         }
+
+        // Marked intrinsic since this needs to be
+        // recognized as an async2 call.
+        [Intrinsic]
+        [BypassReadyToRun]
+        [MethodImpl(MethodImplOptions.NoInlining | (MethodImplOptions)0x0400)]  // NoInlining | Async
+        public static T Await<T>(Task<T> task)
+        {
+            TaskAwaiter<T> awaiter = task.GetAwaiter();
+            if (!awaiter.IsCompleted)
+            {
+                UnsafeAwaitAwaiterFromRuntimeAsync(awaiter);
+            }
+
+            return awaiter.GetResult();
+        }
+
+        // Marked intrinsic since this needs to be
+        // recognized as an async2 call.
+        [Intrinsic]
+        [BypassReadyToRun]
+        [MethodImpl(MethodImplOptions.NoInlining | (MethodImplOptions)0x0400)]  // NoInlining | Async
+        public static void Await(Task task)
+        {
+            TaskAwaiter awaiter = task.GetAwaiter();
+            if (!awaiter.IsCompleted)
+            {
+                UnsafeAwaitAwaiterFromRuntimeAsync(awaiter);
+            }
+
+            awaiter.GetResult();
+        }
+
+        // Marked intrinsic since this needs to be
+        // recognized as an async2 call.
+        [Intrinsic]
+        [BypassReadyToRun]
+        [MethodImpl(MethodImplOptions.NoInlining | (MethodImplOptions)0x0400)]  // NoInlining | Async
+        public static T Await<T>(ValueTask<T> task)
+        {
+            ValueTaskAwaiter<T> awaiter = task.GetAwaiter();
+            if (!awaiter.IsCompleted)
+            {
+                UnsafeAwaitAwaiterFromRuntimeAsync(awaiter);
+            }
+
+            return awaiter.GetResult();
+        }
+
+        // Marked intrinsic since this needs to be
+        // recognized as an async2 call.
+        [Intrinsic]
+        [BypassReadyToRun]
+        [MethodImpl(MethodImplOptions.NoInlining | (MethodImplOptions)0x0400)]  // NoInlining | Async
+        public static void Await(ValueTask task)
+        {
+            ValueTaskAwaiter awaiter = task.GetAwaiter();
+            if (!awaiter.IsCompleted)
+            {
+                UnsafeAwaitAwaiterFromRuntimeAsync(awaiter);
+            }
+
+            awaiter.GetResult();
+        }
+
 #endif
     }
 }
diff --git a/src/libraries/System.Runtime/ref/System.Runtime.cs b/src/libraries/System.Runtime/ref/System.Runtime.cs
index 0cd7d5b7fce525..9de39dcc7411b1 100644
--- a/src/libraries/System.Runtime/ref/System.Runtime.cs
+++ b/src/libraries/System.Runtime/ref/System.Runtime.cs
@@ -13708,6 +13708,10 @@ public static void RunModuleConstructor(System.ModuleHandle module) { }
         public delegate void TryCode(object? userData);
         public static void UnsafeAwaitAwaiterFromRuntimeAsync<TAwaiter>(TAwaiter awaiter) where TAwaiter : ICriticalNotifyCompletion { }
         public static void AwaitAwaiterFromRuntimeAsync<TAwaiter>(TAwaiter awaiter) where TAwaiter : INotifyCompletion { }
+        public static void Await(System.Threading.Tasks.Task task) { throw null; }
+        public static T Await<T>(System.Threading.Tasks.Task<T> task) { throw null; }
+        public static void Await(System.Threading.Tasks.ValueTask task) { throw null; }
+        public static T Await<T>(System.Threading.Tasks.ValueTask<T> task) { throw null; }
     }
     public sealed partial class RuntimeWrappedException : System.Exception
     {
diff --git a/src/tests/async/awaitingnotasync.cs b/src/tests/async/awaitingnotasync.cs
new file mode 100644
index 00000000000000..9f4b1732313ca1
--- /dev/null
+++ b/src/tests/async/awaitingnotasync.cs
@@ -0,0 +1,52 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Threading.Tasks;
+using Xunit;
+
+public class AwaitNotAsync
+{
+    [Fact]
+    public static void TestEntryPoint()
+    {
+        AsyncEntryPoint().Wait();
+    }
+
+    private static async Task<T> GetTask<T>(T arg)
+    {
+        await Task.Yield();
+        return arg;
+    }
+
+    // TODO: switch every other scenario to use ValueTask
+    private static async ValueTask<T> GetValueTask<T>(T arg)
+    {
+        await Task.Yield();
+        return arg;
+    }
+
+    private static Task<int> sField;
+
+    private static Task<int> sProp => GetTask(6);
+
+    private static T sIdentity<T>(T arg) => arg;
+
+    private static async2 Task AsyncEntryPoint()
+    {
+        // static field
+        sField = GetTask(5);
+        Assert.Equal(5, await sField);
+
+        // property 
+        Assert.Equal(6, await sProp);
+
+        // generic identity
+        Assert.Equal(6, await sIdentity(sProp));
+
+        // await(await ...))
+        Assert.Equal(7, await await await GetTask(GetTask(GetTask(7))));
+
+    }
+}
diff --git a/src/tests/async/awaitingnotasync.csproj b/src/tests/async/awaitingnotasync.csproj
new file mode 100644
index 00000000000000..9367a79b2edbb1
--- /dev/null
+++ b/src/tests/async/awaitingnotasync.csproj
@@ -0,0 +1,8 @@
+<Project Sdk="Microsoft.NET.Sdk.IL">
+  <PropertyGroup>
+    <Optimize>True</Optimize>
+  </PropertyGroup>
+  <ItemGroup>
+    <Compile Include="$(MSBuildProjectName).cs" />
+  </ItemGroup>
+</Project>

From fdd554bdcaa3438058b5e5f61b4aea03bb7320b5 Mon Sep 17 00:00:00 2001
From: Jakob Botsch Nielsen <jakob.botsch.nielsen@gmail.com>
Date: Thu, 6 Mar 2025 22:36:09 +0100
Subject: [PATCH 137/203] Fixes after merge

---
 src/coreclr/inc/corinfo.h                     |   2 +-
 src/coreclr/inc/jiteeversionguid.h            |  12 +-
 src/coreclr/inc/jithelpers.h                  |   4 +-
 src/coreclr/jit/compiler.h                    |   9 +
 src/coreclr/jit/fginline.cpp                  |   2 +-
 src/coreclr/jit/importer.cpp                  |  29 +--
 src/coreclr/jit/lclvars.cpp                   |  49 +----
 .../Common/JitInterface/CorInfoHelpFunc.cs    |   3 +
 src/coreclr/vm/amd64/AsmHelpers.asm           |  12 ++
 src/coreclr/vm/amd64/unixasmhelpers.S         |   9 +
 src/coreclr/vm/arm64/asmhelpers.S             |   9 +
 src/coreclr/vm/arm64/asmhelpers.asm           |  11 ++
 src/coreclr/vm/callhelpers.cpp                |   1 -
 src/coreclr/vm/jithelpers.cpp                 | 172 ++++++++++++++++++
 src/coreclr/vm/jitinterface.h                 |   1 +
 src/coreclr/vm/loongarch64/asmhelpers.S       |   9 +
 src/coreclr/vm/riscv64/asmhelpers.S           |   9 +
 src/coreclr/vm/threadsuspend.cpp              |   4 +-
 18 files changed, 266 insertions(+), 81 deletions(-)

diff --git a/src/coreclr/inc/corinfo.h b/src/coreclr/inc/corinfo.h
index bd82fd90695a0f..57e78ba06a8558 100644
--- a/src/coreclr/inc/corinfo.h
+++ b/src/coreclr/inc/corinfo.h
@@ -580,6 +580,7 @@ enum CorInfoHelpFunc
 
     CORINFO_HELP_PATCHPOINT,                // Notify runtime that code has reached a patchpoint
     CORINFO_HELP_PARTIAL_COMPILATION_PATCHPOINT,  // Notify runtime that code has reached a part of the method that wasn't originally jitted.
+    CORINFO_HELP_RESUME_OSR,                // Resume in an OSR version of the code for the specified IL offset
 
     CORINFO_HELP_CLASSPROFILE32,            // Update 32-bit class profile for a call site
     CORINFO_HELP_CLASSPROFILE64,            // Update 64-bit class profile for a call site
@@ -598,7 +599,6 @@ enum CorInfoHelpFunc
     CORINFO_HELP_ALLOC_CONTINUATION,
     CORINFO_HELP_ALLOC_CONTINUATION_METHOD,
     CORINFO_HELP_ALLOC_CONTINUATION_CLASS,
-    CORINFO_HELP_RESUME_OSR,
 
     CORINFO_HELP_COUNT,
 };
diff --git a/src/coreclr/inc/jiteeversionguid.h b/src/coreclr/inc/jiteeversionguid.h
index cafcef9168218c..017728558bbdbb 100644
--- a/src/coreclr/inc/jiteeversionguid.h
+++ b/src/coreclr/inc/jiteeversionguid.h
@@ -35,11 +35,13 @@
 #ifndef JIT_EE_VERSIONING_GUID_H
 #define JIT_EE_VERSIONING_GUID_H
 
-constexpr GUID JITEEVersionIdentifier = { /* 2511e8ef-050b-4a05-bbe6-4bec02b96577 */
-    0x2511e8ef,
-    0x050b,
-    0x4a05,
-    {0xbb, 0xe6, 0x4b, 0xec, 0x02, 0xb9, 0x65, 0x77}
+#include <minipal/guid.h>
+
+constexpr GUID JITEEVersionIdentifier = { /* 814e1025-d560-45d1-8aeb-8dcb009ba6a7 */
+    0x814e1025,
+    0xd560,
+    0x45d1,
+    {0x8a, 0xeb, 0x8d, 0xcb, 0x00, 0x9b, 0xa6, 0xa7}
   };
 
 #endif // JIT_EE_VERSIONING_GUID_H
diff --git a/src/coreclr/inc/jithelpers.h b/src/coreclr/inc/jithelpers.h
index 437688c931f60e..aeaba680114ff0 100644
--- a/src/coreclr/inc/jithelpers.h
+++ b/src/coreclr/inc/jithelpers.h
@@ -125,7 +125,7 @@
     DYNAMICJITHELPER(CORINFO_HELP_THROW,        IL_Throw,           METHOD__NIL)
     DYNAMICJITHELPER(CORINFO_HELP_RETHROW,      IL_Rethrow,         METHOD__NIL)
     DYNAMICJITHELPER(CORINFO_HELP_THROWEXACT,   IL_ThrowExact,      METHOD__NIL)
-    JITHELPER(CORINFO_HELP_USER_BREAKPOINT,     JIT_UserBreakpoint, METHOD__NIL)
+    DYNAMICJITHELPER(CORINFO_HELP_USER_BREAKPOINT,         NULL,    METHOD__DEBUGGER__USERBREAKPOINT)
     DYNAMICJITHELPER_NOINDIRECT(CORINFO_HELP_RNGCHKFAIL,   NULL,    METHOD__THROWHELPERS__THROWINDEXOUTOFRANGEEXCEPTION)
     DYNAMICJITHELPER_NOINDIRECT(CORINFO_HELP_OVERFLOW,     NULL,    METHOD__THROWHELPERS__THROWOVERFLOWEXCEPTION)
     DYNAMICJITHELPER_NOINDIRECT(CORINFO_HELP_THROWDIVZERO, NULL,    METHOD__THROWHELPERS__THROWDIVIDEBYZEROEXCEPTION)
@@ -319,6 +319,7 @@
 
     JITHELPER(CORINFO_HELP_PATCHPOINT, JIT_Patchpoint, METHOD__NIL)
     JITHELPER(CORINFO_HELP_PARTIAL_COMPILATION_PATCHPOINT, JIT_PartialCompilationPatchpoint, METHOD__NIL)
+    JITHELPER(CORINFO_HELP_RESUME_OSR, JIT_ResumeOSR, METHOD__NIL)
 
     JITHELPER(CORINFO_HELP_CLASSPROFILE32, JIT_ClassProfile32, METHOD__NIL)
     JITHELPER(CORINFO_HELP_CLASSPROFILE64, JIT_ClassProfile64, METHOD__NIL)
@@ -346,7 +347,6 @@
     DYNAMICJITHELPER(CORINFO_HELP_ALLOC_CONTINUATION, NULL, METHOD__RUNTIME_HELPERS__ALLOC_CONTINUATION)
     DYNAMICJITHELPER(CORINFO_HELP_ALLOC_CONTINUATION_METHOD, NULL, METHOD__RUNTIME_HELPERS__ALLOC_CONTINUATION_METHOD)
     DYNAMICJITHELPER(CORINFO_HELP_ALLOC_CONTINUATION_CLASS, NULL, METHOD__RUNTIME_HELPERS__ALLOC_CONTINUATION_CLASS)
-    JITHELPER(CORINFO_HELP_RESUME_OSR, JIT_ResumeOSR, CORINFO_HELP_SIG_REG_ONLY)
 
 #undef JITHELPER
 #undef DYNAMICJITHELPER
diff --git a/src/coreclr/jit/compiler.h b/src/coreclr/jit/compiler.h
index f43ab48f167dbd..e7e511f5adf7c3 100644
--- a/src/coreclr/jit/compiler.h
+++ b/src/coreclr/jit/compiler.h
@@ -695,6 +695,15 @@ class LclVarDsc
     unsigned char lvAllDefsAreNoGc       : 1; // For pinned locals: true if all defs of this local are no-gc
     unsigned char lvStackAllocatedObject : 1; // Local is a stack allocated object (class, box, array, ...)
 
+    bool IsImplicitByRef()
+    {
+#if FEATURE_IMPLICIT_BYREFS
+        return lvIsImplicitByRef;
+#else
+        return false;
+#endif
+    }
+
     // lvIsMultiRegArgOrRet()
     //     returns true if this is a multireg LclVar struct used in an argument context
     //               or if this is a multireg LclVar struct assigned from a multireg call
diff --git a/src/coreclr/jit/fginline.cpp b/src/coreclr/jit/fginline.cpp
index ca6487e294ea47..fa7d0b3fa618b8 100644
--- a/src/coreclr/jit/fginline.cpp
+++ b/src/coreclr/jit/fginline.cpp
@@ -2090,7 +2090,7 @@ Statement* Compiler::fgInlinePrependStatements(InlineInfo* inlineInfo)
         }
 
         assert(argInfo != nullptr);
-        fgInsertInlineeArgument(*argInfo, block, &afterStmt, &newStmt, callDI);
+        fgInsertInlineeArgument(inlineInfo, *argInfo, block, &afterStmt, &newStmt, callDI);
     }
 
     // Add the CCTOR check if asked for.
diff --git a/src/coreclr/jit/importer.cpp b/src/coreclr/jit/importer.cpp
index c9c078ce22e991..0c37c600e376a6 100644
--- a/src/coreclr/jit/importer.cpp
+++ b/src/coreclr/jit/importer.cpp
@@ -946,21 +946,8 @@ GenTree* Compiler::impStoreStruct(GenTree*         store,
             // now returns void, not a struct
             src->gtType = TYP_VOID;
 
-            if (retbufSpillLcl != BAD_VAR_NUM)
-            {
-                assert(pAfterStmt == nullptr);
-                impAppendTree(src, curLevel, usedDI);
-
-                store->Data() = gtNewLclVarNode(retbufSpillLcl, genActualType(store));
-                impAppendTree(store, curLevel, usedDI);
-
-                return gtNewNothingNode();
-            }
-            else
-            {
-                // return the morphed call node
-                return src;
-            }
+            // return the morphed call node
+            return src;
         }
 
 #ifdef UNIX_AMD64_ABI
@@ -1010,17 +997,6 @@ GenTree* Compiler::impStoreStruct(GenTree*         store,
             src->gtType  = TYP_VOID;
             call->gtType = TYP_VOID;
 
-            if (retbufSpillLcl != BAD_VAR_NUM)
-            {
-                assert(pAfterStmt == nullptr);
-                impAppendTree(src, curLevel, usedDI);
-
-                store->Data() = gtNewLclVarNode(retbufSpillLcl, genActualType(store));
-                impAppendTree(store, curLevel, usedDI);
-
-                return gtNewNothingNode();
-            }
-
             // We already have appended the write to 'dest' GT_CALL's args
             // So now we just return an empty node (pruning the GT_RET_EXPR)
             return src;
@@ -13374,7 +13350,6 @@ void Compiler::impInlineInitVars(InlineInfo* pInlineInfo)
             argInfo->argHasGlobRef    = true;
             argInfo->argHasSideEff    = true;
         }
->>>>>>> upstream-main/main
     }
 
 #ifdef FEATURE_SIMD
diff --git a/src/coreclr/jit/lclvars.cpp b/src/coreclr/jit/lclvars.cpp
index d81c55567f8329..99d9a0fd69c9bc 100644
--- a/src/coreclr/jit/lclvars.cpp
+++ b/src/coreclr/jit/lclvars.cpp
@@ -376,7 +376,7 @@ void Compiler::lvaInitArgs(bool hasRetBuffArg)
     // and shared generic struct instance methods
     lvaInitGenericsCtxt(&varNum);
 
-    lvaInitAsyncContinuation(varDscInfo);
+    lvaInitAsyncContinuation(&varNum);
 
     /* If the method is varargs, process the varargs cookie */
     lvaInitVarArgsHandle(&varNum);
@@ -391,7 +391,7 @@ void Compiler::lvaInitArgs(bool hasRetBuffArg)
     // and shared generic struct instance methods
     lvaInitGenericsCtxt(&varNum);
 
-    lvaInitAsyncContinuation(varDscInfo);
+    lvaInitAsyncContinuation(&varNum);
 
     /* If the method is varargs, process the varargs cookie */
     lvaInitVarArgsHandle(&varNum);
@@ -685,59 +685,24 @@ void Compiler::lvaInitGenericsCtxt(unsigned* curVarNum)
     (*curVarNum)++;
 }
 
-void Compiler::lvaInitAsyncContinuation(InitVarDscInfo* varDscInfo)
+void Compiler::lvaInitAsyncContinuation(unsigned* curVarNum)
 {
     if (!compIsAsync2())
     {
         return;
     }
 
-    lvaAsyncContinuationArg = varDscInfo->varNum;
-    LclVarDsc* varDsc       = varDscInfo->varDsc;
+    lvaAsyncContinuationArg = *curVarNum;
+    LclVarDsc* varDsc       = lvaGetDesc(*curVarNum);
     varDsc->lvType          = TYP_REF;
     varDsc->lvIsParam       = true;
 
     // The final home for this incoming register might be our local stack frame
     varDsc->lvOnFrame = true;
 
-#ifdef DEBUG
-    varDsc->lvReason = "Async continuation arg";
-#endif
-
-    if (varDscInfo->canEnreg(TYP_REF))
-    {
-        // Passed in register
-
-        varDsc->lvIsRegArg = 1;
-        varDsc->SetArgReg(
-            genMapRegArgNumToRegNum(varDscInfo->regArgNum(TYP_INT), varDsc->TypeGet(), info.compCallConv));
-#if FEATURE_MULTIREG_ARGS
-        varDsc->SetOtherArgReg(REG_NA);
-#endif
-
-        varDscInfo->intRegArgNum++;
-
-        JITDUMP("'AsyncContinuation' passed in register %s\n", getRegName(varDsc->GetArgReg()));
-    }
-    else
-    {
-// We need to mark these as being on the stack, as this is not done elsewhere in the case that canEnreg
-// returns false.
-#if FEATURE_FASTTAILCALL
-        varDsc->SetStackOffset(varDscInfo->stackArgSize);
-        varDscInfo->stackArgSize += TARGET_POINTER_SIZE;
-#endif // FEATURE_FASTTAILCALL
-    }
+    INDEBUG(varDsc->lvReason = "Async continuation arg");
 
-    compArgSize += TARGET_POINTER_SIZE;
-
-#if defined(TARGET_X86)
-    if (info.compIsVarArgs)
-        varDsc->SetStackOffset(compArgSize);
-#endif // TARGET_X86
-
-    varDscInfo->varNum++;
-    varDscInfo->varDsc++;
+    (*curVarNum)++;
 }
 
 /*****************************************************************************/
diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoHelpFunc.cs b/src/coreclr/tools/Common/JitInterface/CorInfoHelpFunc.cs
index 56f86c1d432049..050bbb17082715 100644
--- a/src/coreclr/tools/Common/JitInterface/CorInfoHelpFunc.cs
+++ b/src/coreclr/tools/Common/JitInterface/CorInfoHelpFunc.cs
@@ -270,6 +270,7 @@ which is the right helper to use to allocate an object of a given type. */
 
         CORINFO_HELP_PATCHPOINT,                // Notify runtime that code has reached a patchpoint
         CORINFO_HELP_PARTIAL_COMPILATION_PATCHPOINT,  // Notify runtime that code has reached a part of the method that wasn't originally jitted.
+        CORINFO_HELP_RESUME_OSR,                // Resume in an OSR version of the code for the specified IL offset
 
         CORINFO_HELP_CLASSPROFILE32,            // Update 32-bit class profile for a call site
         CORINFO_HELP_CLASSPROFILE64,            // Update 64-bit class profile for a call site
@@ -286,6 +287,8 @@ which is the right helper to use to allocate an object of a given type. */
         CORINFO_HELP_DISPATCH_INDIRECT_CALL,    // CFG: Validate and dispatch to pointer
 
         CORINFO_HELP_ALLOC_CONTINUATION,
+        CORINFO_HELP_ALLOC_CONTINUATION_METHOD,
+        CORINFO_HELP_ALLOC_CONTINUATION_CLASS,
 
         CORINFO_HELP_COUNT,
     }
diff --git a/src/coreclr/vm/amd64/AsmHelpers.asm b/src/coreclr/vm/amd64/AsmHelpers.asm
index c14cfe9185247f..37a416a880739f 100644
--- a/src/coreclr/vm/amd64/AsmHelpers.asm
+++ b/src/coreclr/vm/amd64/AsmHelpers.asm
@@ -471,6 +471,18 @@ LEAF_ENTRY JIT_PartialCompilationPatchpoint, _TEXT
         jmp JIT_Patchpoint
 LEAF_END JIT_PartialCompilationPatchpoint, _TEXT
 
+extern JIT_ResumeOSRWorker:proc
+
+NESTED_ENTRY JIT_ResumeOSR, _TEXT
+        PROLOG_WITH_TRANSITION_BLOCK
+
+        lea     rcx, [rsp + __PWTB_TransitionBlock] ; TransitionBlock *
+        call    JIT_ResumeOSRWorker
+
+        EPILOG_WITH_TRANSITION_BLOCK_RETURN
+        TAILJMP_RAX
+NESTED_END JIT_ResumeOSR, _TEXT
+
 endif ; FEATURE_TIERED_COMPILATION
 
 LEAF_ENTRY JIT_PollGC, _TEXT
diff --git a/src/coreclr/vm/amd64/unixasmhelpers.S b/src/coreclr/vm/amd64/unixasmhelpers.S
index 8b9e8a49e106f4..78e107233f4594 100644
--- a/src/coreclr/vm/amd64/unixasmhelpers.S
+++ b/src/coreclr/vm/amd64/unixasmhelpers.S
@@ -216,4 +216,13 @@ LEAF_ENTRY JIT_PartialCompilationPatchpoint, _TEXT
         jmp C_FUNC(JIT_Patchpoint)
 LEAF_END JIT_PartialCompilationPatchpoint, _TEXT
 
+NESTED_ENTRY JIT_ResumeOSR, _TEXT, NoHandler
+        PROLOG_WITH_TRANSITION_BLOCK
+
+        lea     rdi, [rsp + __PWTB_TransitionBlock] // TransitionBlock *
+        call    C_FUNC(JIT_ResumeOSRWorker)
+
+        EPILOG_WITH_TRANSITION_BLOCK_RETURN
+NESTED_END JIT_ResumeOSR, _TEXT
+
 #endif // FEATURE_TIERED_COMPILATION
diff --git a/src/coreclr/vm/arm64/asmhelpers.S b/src/coreclr/vm/arm64/asmhelpers.S
index 93d669d5b131c6..b8ad1ff29aea06 100644
--- a/src/coreclr/vm/arm64/asmhelpers.S
+++ b/src/coreclr/vm/arm64/asmhelpers.S
@@ -770,6 +770,15 @@ LEAF_ENTRY JIT_PartialCompilationPatchpoint, _TEXT
     b C_FUNC(JIT_Patchpoint)
 LEAF_END JIT_PartialCompilationPatchpoint, _TEXT
 
+NESTED_ENTRY JIT_ResumeOSR, _TEXT, NoHandler
+    PROLOG_WITH_TRANSITION_BLOCK
+
+    add     x0, sp, #__PWTB_TransitionBlock // TransitionBlock *
+    bl      C_FUNC(JIT_ResumeOSRWorker)
+
+    EPILOG_WITH_TRANSITION_BLOCK_RETURN
+NESTED_END JIT_ResumeOSR, _TEXT
+
 #endif // FEATURE_TIERED_COMPILATION
 
 LEAF_ENTRY JIT_ValidateIndirectCall, _TEXT
diff --git a/src/coreclr/vm/arm64/asmhelpers.asm b/src/coreclr/vm/arm64/asmhelpers.asm
index 3cffe4d8f5f39b..f4ca9f66bb0cd2 100644
--- a/src/coreclr/vm/arm64/asmhelpers.asm
+++ b/src/coreclr/vm/arm64/asmhelpers.asm
@@ -1172,6 +1172,17 @@ __HelperNakedFuncName SETS "$helper":CC:"Naked"
         b JIT_Patchpoint
     LEAF_END
 
+    IMPORT JIT_ResumeOSRWorker
+
+    NESTED_ENTRY JIT_ResumeOSR
+        PROLOG_WITH_TRANSITION_BLOCK
+
+        add     x0, sp, #__PWTB_TransitionBlock ; TransitionBlock *
+        bl      JIT_ResumeOSRWorker
+
+        EPILOG_WITH_TRANSITION_BLOCK_RETURN
+    NESTED_END
+
 #endif ; FEATURE_TIERED_COMPILATION
 
     LEAF_ENTRY  JIT_ValidateIndirectCall
diff --git a/src/coreclr/vm/callhelpers.cpp b/src/coreclr/vm/callhelpers.cpp
index 5ba0c11374b0d9..f575a21899185c 100644
--- a/src/coreclr/vm/callhelpers.cpp
+++ b/src/coreclr/vm/callhelpers.cpp
@@ -328,7 +328,6 @@ void MethodDescCallSite::CallTargetWorker(const ARG_SLOT *pArguments, ARG_SLOT *
 
         _ASSERTE(isCallConv(m_methodSig.GetCallingConvention(), IMAGE_CEE_CS_CALLCONV_DEFAULT));
         _ASSERTE(!m_methodSig.HasGenericContextArg());
-#endif
 
 #ifdef DEBUGGING_SUPPORTED
         if (CORDebuggerTraceCall())
diff --git a/src/coreclr/vm/jithelpers.cpp b/src/coreclr/vm/jithelpers.cpp
index 512495b5887a7f..a9f2a5392a9e8d 100644
--- a/src/coreclr/vm/jithelpers.cpp
+++ b/src/coreclr/vm/jithelpers.cpp
@@ -2867,6 +2867,178 @@ extern "C" void JIT_PatchpointWorkerWorkerWithPolicy(TransitionBlock * pTransiti
     ::SetLastError(dwLastError);
 }
 
+extern "C" void JIT_ResumeOSRWorker(TransitionBlock * pTransitionBlock)
+{
+    // BEGIN_PRESERVE_LAST_ERROR;
+    DWORD dwLastError = ::GetLastError();
+
+    // This method may not return normally
+    STATIC_CONTRACT_NOTHROW;
+    STATIC_CONTRACT_GC_TRIGGERS;
+    STATIC_CONTRACT_MODE_COOPERATIVE;
+
+    PTR_PCODE pReturnAddress = (PTR_PCODE)(((BYTE*)pTransitionBlock) + TransitionBlock::GetOffsetOfReturnAddress());
+    PCODE ip = *pReturnAddress;
+    int ilOffset = *(int*)GetFirstArgumentRegisterValuePtr(pTransitionBlock);
+
+    // Fetch or setup patchpoint info for this patchpoint.
+    EECodeInfo codeInfo(ip);
+    MethodDesc* pMD = codeInfo.GetMethodDesc();
+    LoaderAllocator* allocator = pMD->GetLoaderAllocator();
+    OnStackReplacementManager* manager = allocator->GetOnStackReplacementManager();
+
+    PCODE tier0EntryAddr = (PCODE)codeInfo.GetStartAddress();
+    CodeVersionManager* codeVersionManager = pMD->GetCodeVersionManager();
+    CodeVersionManager::LockHolder lock;
+    NativeCodeVersionCollection nativeCodeVersions = codeVersionManager->GetNativeCodeVersions(pMD);
+    PCODE osrMethodCode = NULL;
+    for (NativeCodeVersionIterator itr = nativeCodeVersions.Begin(), end = nativeCodeVersions.End(); itr != end; itr++)
+    {
+        if (itr->GetOptimizationTier() == NativeCodeVersion::OptimizationTier1OSR)
+        {
+            unsigned versionILOffset;
+            PatchpointInfo* ppi = itr->GetOSRInfo(&versionILOffset);
+            if (ppi->GetTier0EntryPoint() == tier0EntryAddr && versionILOffset == (unsigned)ilOffset)
+            {
+                osrMethodCode = itr->GetNativeCode();
+                break;
+            }
+        }
+    }
+
+    if (osrMethodCode == (PCODE)NULL)
+    {
+        STRESS_LOG2(LF_TIEREDCOMPILATION, LL_FATALERROR, "JIT_ResumeOSRWorker: patchpoint (0x%p:%d) TRANSITION", ip, ilOffset);
+        _ASSERTE(!"Did not find OSR method code");
+        EEPOLICY_HANDLE_FATAL_ERROR(COR_E_EXECUTIONENGINE);
+    }
+
+    // If we get here, we have code to transition to...
+
+    {
+        Thread *pThread = GetThread();
+
+#ifdef FEATURE_HIJACK
+        // We can't crawl the stack of a thread that currently has a hijack pending
+        // (since the hijack routine won't be recognized by any code manager). So we
+        // Undo any hijack, the EE will re-attempt it later.
+        pThread->UnhijackThread();
+#endif
+
+        // Find context for the original method
+        CONTEXT *pFrameContext = NULL;
+#if defined(TARGET_WINDOWS) && defined(TARGET_AMD64)
+        DWORD contextSize = 0;
+        ULONG64 xStateCompactionMask = 0;
+        DWORD contextFlags = CONTEXT_FULL;
+        if (Thread::AreShadowStacksEnabled())
+        {
+            xStateCompactionMask = XSTATE_MASK_CET_U;
+            contextFlags |= CONTEXT_XSTATE;
+        }
+
+        // The initialize call should fail but return contextSize
+        BOOL success = g_pfnInitializeContext2 ?
+            g_pfnInitializeContext2(NULL, contextFlags, NULL, &contextSize, xStateCompactionMask) :
+            InitializeContext(NULL, contextFlags, NULL, &contextSize);
+
+        _ASSERTE(!success && (GetLastError() == ERROR_INSUFFICIENT_BUFFER));
+
+        PVOID pBuffer = _alloca(contextSize);
+        success = g_pfnInitializeContext2 ?
+            g_pfnInitializeContext2(pBuffer, contextFlags, &pFrameContext, &contextSize, xStateCompactionMask) :
+            InitializeContext(pBuffer, contextFlags, &pFrameContext, &contextSize);
+        _ASSERTE(success);
+#else // TARGET_WINDOWS && TARGET_AMD64
+        CONTEXT frameContext;
+        frameContext.ContextFlags = CONTEXT_FULL;
+        pFrameContext = &frameContext;
+#endif // TARGET_WINDOWS && TARGET_AMD64
+
+        // Find context for the original method
+        RtlCaptureContext(pFrameContext);
+
+#if defined(TARGET_WINDOWS) && defined(TARGET_AMD64)
+        if (Thread::AreShadowStacksEnabled())
+        {
+            pFrameContext->ContextFlags |= CONTEXT_XSTATE;
+            SetXStateFeaturesMask(pFrameContext, xStateCompactionMask);
+            SetSSP(pFrameContext, _rdsspq());
+        }
+#endif // TARGET_WINDOWS && TARGET_AMD64
+
+        // Walk back to the original method frame
+        pThread->VirtualUnwindToFirstManagedCallFrame(pFrameContext);
+
+        // Remember original method FP and SP because new method will inherit them.
+        UINT_PTR currentSP = GetSP(pFrameContext);
+        UINT_PTR currentFP = GetFP(pFrameContext);
+
+        // We expect to be back at the right IP
+        if ((UINT_PTR)ip != GetIP(pFrameContext))
+        {
+            // Should be fatal
+            STRESS_LOG2(LF_TIEREDCOMPILATION, LL_FATALERROR, "Jit_Patchpoint: patchpoint (0x%p) TRANSITION"
+                " unexpected context IP 0x%p\n", ip, GetIP(pFrameContext));
+            EEPOLICY_HANDLE_FATAL_ERROR(COR_E_EXECUTIONENGINE);
+        }
+
+        // Now unwind back to the original method caller frame.
+        EECodeInfo callerCodeInfo(GetIP(pFrameContext));
+        ULONG_PTR establisherFrame = 0;
+        PVOID handlerData = NULL;
+        RtlVirtualUnwind(UNW_FLAG_NHANDLER, callerCodeInfo.GetModuleBase(), GetIP(pFrameContext), callerCodeInfo.GetFunctionEntry(),
+            pFrameContext, &handlerData, &establisherFrame, NULL);
+
+        // Now, set FP and SP back to the values they had just before this helper was called,
+        // since the new method must have access to the original method frame.
+        //
+        // TODO: if we access the patchpointInfo here, we can read out the FP-SP delta from there and
+        // use that to adjust the stack, likely saving some stack space.
+
+#if defined(TARGET_AMD64)
+        // If calls push the return address, we need to simulate that here, so the OSR
+        // method sees the "expected" SP misalgnment on entry.
+        _ASSERTE(currentSP % 16 == 0);
+        currentSP -= 8;
+
+#if defined(TARGET_WINDOWS)
+        DWORD64 ssp = GetSSP(pFrameContext);
+        if (ssp != 0)
+        {
+            SetSSP(pFrameContext, ssp - 8);
+        }
+#endif // TARGET_WINDOWS
+
+        pFrameContext->Rbp = currentFP;
+#endif // TARGET_AMD64
+
+        SetSP(pFrameContext, currentSP);
+
+        // Note we can get here w/o triggering, if there is an existing OSR method and
+        // we hit the patchpoint.
+        LOG((LF_TIEREDCOMPILATION, LL_INFO1000, "Jit_ResumeOSRWorker: patchpoint (0x%p:%d) TRANSITION to ip 0x%p\n", ip, ilOffset, osrMethodCode));
+
+        // Install new entry point as IP
+        SetIP(pFrameContext, osrMethodCode);
+
+#ifdef _DEBUG
+        // Keep this context around to aid in debugging OSR transition problems
+        static CONTEXT s_lastOSRTransitionContext;
+        s_lastOSRTransitionContext = *pFrameContext;
+#endif
+
+        // Restore last error (since call below does not return)
+        // END_PRESERVE_LAST_ERROR;
+        ::SetLastError(dwLastError);
+
+        // Transition!
+        ClrRestoreNonvolatileContext(pFrameContext);
+    }
+
+    // END_PRESERVE_LAST_ERROR;
+    ::SetLastError(dwLastError);
+}
 
 #else
 
diff --git a/src/coreclr/vm/jitinterface.h b/src/coreclr/vm/jitinterface.h
index 957ca5906454db..09fd2d5f9391b8 100644
--- a/src/coreclr/vm/jitinterface.h
+++ b/src/coreclr/vm/jitinterface.h
@@ -106,6 +106,7 @@ BOOL LoadDynamicInfoEntry(Module *currentModule,
 // These must be implemented in assembly and generate a TransitionBlock then calling JIT_PatchpointWorkerWithPolicy in order to actually be used.
 EXTERN_C FCDECL2(void, JIT_Patchpoint, int* counter, int ilOffset);
 EXTERN_C FCDECL1(void, JIT_PartialCompilationPatchpoint, int ilOffset);
+EXTERN_C FCDECL1(void, JIT_ResumeOSR, int ilOffset);
 
 //
 // JIT HELPER ALIASING FOR PORTABILITY.
diff --git a/src/coreclr/vm/loongarch64/asmhelpers.S b/src/coreclr/vm/loongarch64/asmhelpers.S
index b2abecbafb5f31..1e9257f26777d7 100644
--- a/src/coreclr/vm/loongarch64/asmhelpers.S
+++ b/src/coreclr/vm/loongarch64/asmhelpers.S
@@ -1101,6 +1101,15 @@ LEAF_ENTRY JIT_PartialCompilationPatchpoint, _TEXT
     b C_FUNC(JIT_Patchpoint)
 LEAF_END JIT_PartialCompilationPatchpoint, _TEXT
 
+NESTED_ENTRY JIT_ResumeOSR, _TEXT, NoHandler
+    PROLOG_WITH_TRANSITION_BLOCK
+
+    addi.d  $a0, $sp, __PWTB_TransitionBlock // TransitionBlock *
+    bl      C_FUNC(JIT_ResumeOSR)
+
+    EPILOG_WITH_TRANSITION_BLOCK_RETURN
+NESTED_END JIT_ResumeOSR, _TEXT
+
 #endif // FEATURE_TIERED_COMPILATION
 
 // ------------------------------------------------------------------
diff --git a/src/coreclr/vm/riscv64/asmhelpers.S b/src/coreclr/vm/riscv64/asmhelpers.S
index d28639f6ff862f..e09cf212be7142 100644
--- a/src/coreclr/vm/riscv64/asmhelpers.S
+++ b/src/coreclr/vm/riscv64/asmhelpers.S
@@ -974,6 +974,15 @@ LEAF_ENTRY JIT_PartialCompilationPatchpoint, _TEXT
     j C_FUNC(JIT_Patchpoint)
 LEAF_END JIT_PartialCompilationPatchpoint, _TEXT
 
+NESTED_ENTRY JIT_ResumeOSR, _TEXT, NoHandler
+    PROLOG_WITH_TRANSITION_BLOCK
+
+    addi  a0, sp, __PWTB_TransitionBlock // TransitionBlock *
+    call  C_FUNC(JIT_ResumeOSRWorker)
+
+    EPILOG_WITH_TRANSITION_BLOCK_RETURN
+NESTED_END JIT_ResumeOSR, _TEXT
+
 #endif // FEATURE_TIERED_COMPILATION
 
 // ------------------------------------------------------------------
diff --git a/src/coreclr/vm/threadsuspend.cpp b/src/coreclr/vm/threadsuspend.cpp
index 90f94d708d9961..60c365d7d35469 100644
--- a/src/coreclr/vm/threadsuspend.cpp
+++ b/src/coreclr/vm/threadsuspend.cpp
@@ -4910,13 +4910,13 @@ void STDCALL OnHijackWorker(HijackArgs * pArgs)
 
 static bool GetReturnAddressHijackInfo(EECodeInfo *pCodeInfo X86_ARG(ReturnKind * returnKind) X86_ARG(bool* hasAsyncRet))
 {
-    *hasAsyncRet = false;
+    X86_ONLY(*hasAsyncRet = false);
     GCInfoToken gcInfoToken = pCodeInfo->GetGCInfoToken();
     if (!pCodeInfo->GetCodeManager()->GetReturnAddressHijackInfo(gcInfoToken X86_ARG(pReturnKind)))
         return false;
 
     MethodDesc* pMD = pCodeInfo->GetMethodDesc();
-    *hasAsyncRet = pMD->IsAsync2Method();
+    X86_ONLY(*hasAsyncRet = pMD->IsAsync2Method());
 
     return true;
 }

From b4257bf0290dee413b6482b31660cd5a966b1edc Mon Sep 17 00:00:00 2001
From: Jakob Botsch Nielsen <jakob.botsch.nielsen@gmail.com>
Date: Thu, 6 Mar 2025 22:36:38 +0100
Subject: [PATCH 138/203] Run jit-format

---
 src/coreclr/jit/gentree.cpp       |  4 ++--
 src/coreclr/jit/gentree.h         |  2 +-
 src/coreclr/jit/importer.cpp      | 15 +++++++++------
 src/coreclr/jit/importercalls.cpp |  3 +--
 4 files changed, 13 insertions(+), 11 deletions(-)

diff --git a/src/coreclr/jit/gentree.cpp b/src/coreclr/jit/gentree.cpp
index 5da9ce9c818d0c..39244d332ba4ea 100644
--- a/src/coreclr/jit/gentree.cpp
+++ b/src/coreclr/jit/gentree.cpp
@@ -9933,8 +9933,8 @@ GenTreeCall* Compiler::gtCloneExprCallHelper(GenTreeCall* tree)
     copy->gtLateDevirtualizationInfo = tree->gtLateDevirtualizationInfo;
 
     copy->gtIsAsyncCall = tree->gtIsAsyncCall;
-    copy->gtCallType   = tree->gtCallType;
-    copy->gtReturnType = tree->gtReturnType;
+    copy->gtCallType    = tree->gtCallType;
+    copy->gtReturnType  = tree->gtReturnType;
 
 #if FEATURE_MULTIREG_RET
     copy->gtReturnTypeDesc = tree->gtReturnTypeDesc;
diff --git a/src/coreclr/jit/gentree.h b/src/coreclr/jit/gentree.h
index 1674391396c62e..29d28df69cb16c 100644
--- a/src/coreclr/jit/gentree.h
+++ b/src/coreclr/jit/gentree.h
@@ -4728,7 +4728,7 @@ class CallArgs
 #endif
     bool m_hasThisPointer           : 1;
     bool m_hasRetBuffer             : 1;
-    bool m_hasAsyncContinuation        : 1;
+    bool m_hasAsyncContinuation     : 1;
     bool m_isVarArgs                : 1;
     bool m_abiInformationDetermined : 1;
     bool m_hasAddedFinalArgs        : 1;
diff --git a/src/coreclr/jit/importer.cpp b/src/coreclr/jit/importer.cpp
index 0c37c600e376a6..ce161e13f7d47d 100644
--- a/src/coreclr/jit/importer.cpp
+++ b/src/coreclr/jit/importer.cpp
@@ -846,7 +846,8 @@ GenTree* Compiler::impStoreStruct(GenTree*         store,
 
             // Make sure we don't pass something other than a local address to the return buffer arg.
             // It is allowed to pass current's method return buffer as it is a local too.
-            if ((fgAddrCouldBeHeap(destAddr) && !eeIsByrefLike(srcCall->gtRetClsHnd)) || (compIsAsync2() && !destAddr->OperIs(GT_LCL_ADDR)))
+            if ((fgAddrCouldBeHeap(destAddr) && !eeIsByrefLike(srcCall->gtRetClsHnd)) ||
+                (compIsAsync2() && !destAddr->OperIs(GT_LCL_ADDR)))
             {
                 unsigned tmp = lvaGrabTemp(false DEBUGARG("stack copy for value returned via return buffer"));
                 lvaSetStruct(tmp, srcCall->gtRetClsHnd, false);
@@ -972,7 +973,8 @@ GenTree* Compiler::impStoreStruct(GenTree*         store,
 
             // Make sure we don't pass something other than a local address to the return buffer arg.
             // It is allowed to pass current's method return buffer as it is a local too.
-            if (fgAddrCouldBeHeap(destAddr) && !eeIsByrefLike(call->gtRetClsHnd) || (compIsAsync2() && !destAddr->OperIs(GT_LCL_ADDR)))
+            if (fgAddrCouldBeHeap(destAddr) && !eeIsByrefLike(call->gtRetClsHnd) ||
+                (compIsAsync2() && !destAddr->OperIs(GT_LCL_ADDR)))
             {
                 unsigned tmp = lvaGrabTemp(false DEBUGARG("stack copy for value returned via return buffer"));
                 lvaSetStruct(tmp, call->gtRetClsHnd, false);
@@ -9038,7 +9040,8 @@ void Compiler::impImportBlockCode(BasicBlock* block)
 
                             // check if it is an Await intrinsic
                             if (eeIsIntrinsic(nextCallTok.hMethod) &&
-                                lookupNamedIntrinsic(nextCallTok.hMethod) == NI_System_Runtime_CompilerServices_RuntimeHelpers_Await)
+                                lookupNamedIntrinsic(nextCallTok.hMethod) ==
+                                    NI_System_Runtime_CompilerServices_RuntimeHelpers_Await)
                             {
                                 // yes, this is an Await
                                 isAwait = true;
@@ -9057,9 +9060,9 @@ void Compiler::impImportBlockCode(BasicBlock* block)
                         }
                         else
                         {
-                            // This can happen in rare cases when the Task-returning method is not a runtime Async function.
-                            // For example "T M1<T>(T arg) => arg" when called with a Task argument.
-                            // Treat that as a regualr call that is Awaited
+                            // This can happen in rare cases when the Task-returning method is not a runtime Async
+                            // function. For example "T M1<T>(T arg) => arg" when called with a Task argument. Treat
+                            // that as a regualr call that is Awaited
                             _impResolveToken(CORINFO_TOKENKIND_Method);
                         }
                     }
diff --git a/src/coreclr/jit/importercalls.cpp b/src/coreclr/jit/importercalls.cpp
index e4b6c980998f1e..b3f78ff2f4f501 100644
--- a/src/coreclr/jit/importercalls.cpp
+++ b/src/coreclr/jit/importercalls.cpp
@@ -10906,8 +10906,7 @@ NamedIntrinsic Compiler::lookupNamedIntrinsic(CORINFO_METHOD_HANDLE method)
                             }
                             else if (strcmp(methodName, "Await") == 0)
                             {
-                                result =
-                                    NI_System_Runtime_CompilerServices_RuntimeHelpers_Await;
+                                result = NI_System_Runtime_CompilerServices_RuntimeHelpers_Await;
                             }
                             else if (strcmp(methodName, "SuspendAsync2") == 0)
                             {

From 320a5650a5b639c89c0e25111fa47a0387b72771 Mon Sep 17 00:00:00 2001
From: vsadov <8218165+VSadov@users.noreply.github.com>
Date: Thu, 6 Mar 2025 17:34:14 -0800
Subject: [PATCH 139/203] bump roslyn ref to a newer drop

---
 buildroslynnugets.cmd | 14 +++++++-------
 eng/Versions.props    |  6 +++---
 2 files changed, 10 insertions(+), 10 deletions(-)

diff --git a/buildroslynnugets.cmd b/buildroslynnugets.cmd
index bc51f7ce2d7db7..983551b3c29755 100644
--- a/buildroslynnugets.cmd
+++ b/buildroslynnugets.cmd
@@ -1,7 +1,7 @@
 setlocal ENABLEEXTENSIONS
 pushd %~dp0
-set ASYNC_ROSLYN_COMMIT=c1d47ed4bdbd28137fce8fbb350771677aa4cf15
-set ASYNC_SUFFIX=async-12
+set ASYNC_ROSLYN_COMMIT=22232ae7939c97984d1158f4f371b0d89ea3db08
+set ASYNC_SUFFIX=async-13
 set ASYNC_ROSLYN_BRANCH=demos/async2-experiment1
 
 cd ..
@@ -26,9 +26,9 @@ pushd %~dp0
 
 md roslynpackages
 
-copy ..\async-roslyn-repo\artifacts\packages\Release\Shipping\Microsoft.Net.Compilers.Toolset.4.13.0-%ASYNC_SUFFIX%.nupkg roslynpackages
-copy ..\async-roslyn-repo\artifacts\packages\Release\Shipping\Microsoft.CodeAnalysis.Workspaces.Common.4.13.0-%ASYNC_SUFFIX%.nupkg roslynpackages
-copy ..\async-roslyn-repo\artifacts\packages\Release\Shipping\Microsoft.CodeAnalysis.CSharp.Workspaces.4.13.0-%ASYNC_SUFFIX%.nupkg roslynpackages
-copy ..\async-roslyn-repo\artifacts\packages\Release\Shipping\Microsoft.CodeAnalysis.CSharp.4.13.0-%ASYNC_SUFFIX%.nupkg roslynpackages
-copy ..\async-roslyn-repo\artifacts\packages\Release\Shipping\Microsoft.CodeAnalysis.Common.4.13.0-%ASYNC_SUFFIX%.nupkg roslynpackages
+copy ..\async-roslyn-repo\artifacts\packages\Release\Shipping\Microsoft.Net.Compilers.Toolset.4.14.0-%ASYNC_SUFFIX%.nupkg roslynpackages
+copy ..\async-roslyn-repo\artifacts\packages\Release\Shipping\Microsoft.CodeAnalysis.Workspaces.Common.4.14.0-%ASYNC_SUFFIX%.nupkg roslynpackages
+copy ..\async-roslyn-repo\artifacts\packages\Release\Shipping\Microsoft.CodeAnalysis.CSharp.Workspaces.4.14.0-%ASYNC_SUFFIX%.nupkg roslynpackages
+copy ..\async-roslyn-repo\artifacts\packages\Release\Shipping\Microsoft.CodeAnalysis.CSharp.4.14.0-%ASYNC_SUFFIX%.nupkg roslynpackages
+copy ..\async-roslyn-repo\artifacts\packages\Release\Shipping\Microsoft.CodeAnalysis.Common.4.14.0-%ASYNC_SUFFIX%.nupkg roslynpackages
 
diff --git a/eng/Versions.props b/eng/Versions.props
index 88945384fb7af7..4f21ee086e5c40 100644
--- a/eng/Versions.props
+++ b/eng/Versions.props
@@ -44,9 +44,9 @@
       Any tools that contribute to the design-time experience should use the MicrosoftCodeAnalysisVersion_LatestVS property above to ensure
       they do not break the local dev experience.
     -->
-    <MicrosoftCodeAnalysisCSharpVersion>4.13.0-async-12</MicrosoftCodeAnalysisCSharpVersion>
-    <MicrosoftCodeAnalysisVersion>4.13.0-async-12</MicrosoftCodeAnalysisVersion>
-    <MicrosoftNetCompilersToolsetVersion>4.13.0-async-12</MicrosoftNetCompilersToolsetVersion>
+    <MicrosoftCodeAnalysisCSharpVersion>4.14.0-async-13</MicrosoftCodeAnalysisCSharpVersion>
+    <MicrosoftCodeAnalysisVersion>4.14.0-async-13</MicrosoftCodeAnalysisVersion>
+    <MicrosoftNetCompilersToolsetVersion>4.14.0-async-13</MicrosoftNetCompilersToolsetVersion>
   </PropertyGroup>
   <!--
     For source generator support we need to target multiple versions of Roslyn in order to be able to run on older versions of Roslyn.

From 2922a4e23e06e1823fb99b2000809c4860f3a7b8 Mon Sep 17 00:00:00 2001
From: Jakob Botsch Nielsen <jakob.botsch.nielsen@gmail.com>
Date: Fri, 7 Mar 2025 11:27:27 +0100
Subject: [PATCH 140/203] Fix one assert

---
 src/coreclr/jit/codegenlinear.cpp | 7 ++++++-
 1 file changed, 6 insertions(+), 1 deletion(-)

diff --git a/src/coreclr/jit/codegenlinear.cpp b/src/coreclr/jit/codegenlinear.cpp
index a4685d992205f6..8a3cf8c1ad85e3 100644
--- a/src/coreclr/jit/codegenlinear.cpp
+++ b/src/coreclr/jit/codegenlinear.cpp
@@ -505,8 +505,13 @@ void CodeGen::genCodeForBBlist()
             }
         }
 
+        if (compiler->compIsAsync2())
+        {
+            nonVarPtrRegs &= ~RBM_ASYNC_CONTINUATION_RET;
+        }
+
         // For a tailcall arbitrary argument registers may be live into the
-        // prolog. Skip validating those.
+        // epilog. Skip validating those.
         if (block->HasFlag(BBF_HAS_JMP))
         {
             nonVarPtrRegs &= ~fullIntArgRegMask(CorInfoCallConvExtension::Managed);

From ce5fd93e22413ea22bbfe0bbd939a0b461c9ae68 Mon Sep 17 00:00:00 2001
From: Jakob Botsch Nielsen <jakob.botsch.nielsen@gmail.com>
Date: Fri, 7 Mar 2025 11:27:38 +0100
Subject: [PATCH 141/203] Update to handle custom layouts properly

---
 src/coreclr/jit/async.cpp | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/coreclr/jit/async.cpp b/src/coreclr/jit/async.cpp
index 51ef5f451397e3..800dc8de7e8c3d 100644
--- a/src/coreclr/jit/async.cpp
+++ b/src/coreclr/jit/async.cpp
@@ -372,11 +372,11 @@ void Async2Transformation::Transform(
             ClassLayout* layout = dsc->GetLayout();
             assert(!layout->HasGCByRef());
 
-            if (layout->IsBlockLayout())
+            if (layout->IsCustomLayout())
             {
                 inf.Alignment   = 1;
                 inf.DataSize    = layout->GetSize();
-                inf.GCDataCount = 0;
+                inf.GCDataCount = layout->GetGCPtrCount();
             }
             else
             {

From 8fb0f4cd78423b6d8dc272bbceacbadff27f9896 Mon Sep 17 00:00:00 2001
From: Jakob Botsch Nielsen <jakob.botsch.nielsen@gmail.com>
Date: Fri, 7 Mar 2025 12:19:57 +0100
Subject: [PATCH 142/203] FIx more JIT asserts

---
 src/coreclr/jit/async.cpp         | 65 +++++++++++++------------------
 src/coreclr/jit/codegen.h         |  1 +
 src/coreclr/jit/codegencommon.cpp | 47 ++++++++++++----------
 3 files changed, 56 insertions(+), 57 deletions(-)

diff --git a/src/coreclr/jit/async.cpp b/src/coreclr/jit/async.cpp
index 800dc8de7e8c3d..b5b8555ddffdde 100644
--- a/src/coreclr/jit/async.cpp
+++ b/src/coreclr/jit/async.cpp
@@ -292,10 +292,6 @@ PhaseStatus Async2Transformation::Run()
     return PhaseStatus::MODIFIED_EVERYTHING;
 }
 
-const weight_t RESUME_SUSPEND_LIKELIHOOD = 0.01;
-const weight_t RETHROW_LIKELIHOOD        = 0.01;
-const weight_t RESUME_IN_OSR_LIKELIHOOD  = 0.001;
-
 void Async2Transformation::Transform(
     BasicBlock* block, GenTreeCall* call, jitstd::vector<GenTree*>& defs, AsyncLiveness& life, BasicBlock** pRemainder)
 {
@@ -582,9 +578,9 @@ void Async2Transformation::Transform(
     }
 
     BasicBlock* suspendBB = m_comp->fgNewBBafter(BBJ_RETURN, m_lastSuspensionBB, false);
-    suspendBB->bbSetRunRarely();
     suspendBB->clearTryIndex();
     suspendBB->clearHndIndex();
+    suspendBB->inheritWeightPercentage(block, 0);
     m_lastSuspensionBB = suspendBB;
 
     if (m_sharedReturnBB != nullptr)
@@ -597,8 +593,8 @@ void Async2Transformation::Transform(
     FlowEdge* retBBEdge = m_comp->fgAddRefPred(suspendBB, block);
     block->SetCond(retBBEdge, block->GetTargetEdge());
 
-    block->GetTrueEdge()->setLikelihood(RESUME_SUSPEND_LIKELIHOOD);
-    block->GetFalseEdge()->setLikelihood(1 - RESUME_SUSPEND_LIKELIHOOD);
+    block->GetTrueEdge()->setLikelihood(0);
+    block->GetFalseEdge()->setLikelihood(1);
 
     // Allocate continuation
     returnedContinuation = m_comp->gtNewLclvNode(m_returnedContinuationVar, TYP_REF);
@@ -811,8 +807,11 @@ void Async2Transformation::Transform(
     BasicBlock* resumeBB      = m_comp->fgNewBBafter(BBJ_ALWAYS, m_lastResumptionBB, true);
     FlowEdge*   remainderEdge = m_comp->fgAddRefPred(remainder, resumeBB);
 
+    // It does not really make sense to inherit from the target, but given this
+    // is always 0% this just propagates the profile weight flag + sets
+    // BBF_RUN_RARELY.
+    resumeBB->inheritWeightPercentage(remainder, 0);
     resumeBB->SetTargetEdge(remainderEdge);
-    resumeBB->bbSetRunRarely();
     resumeBB->clearTryIndex();
     resumeBB->clearHndIndex();
     resumeBB->SetFlags(BBF_ASYNC_RESUMPTION);
@@ -963,8 +962,10 @@ void Async2Transformation::Transform(
             assert(resumeBB->KindIs(BBJ_ALWAYS));
 
             resumeBB->SetCond(rethrowEdge, storeResultEdge);
-            rethrowEdge->setLikelihood(RETHROW_LIKELIHOOD);
-            storeResultEdge->setLikelihood(1 - RETHROW_LIKELIHOOD);
+            rethrowEdge->setLikelihood(0);
+            storeResultEdge->setLikelihood(1);
+            rethrowExceptionBB->inheritWeightPercentage(resumeBB, 0);
+            storeResultBB->inheritWeightPercentage(resumeBB, 100);
             JITDUMP("  Resumption " FMT_BB " becomes BBJ_COND to check for non-null exception\n", resumeBB->bbNum);
 
             m_comp->fgRemoveRefPred(remainderEdge);
@@ -1306,21 +1307,8 @@ void Async2Transformation::LiftLIREdges(BasicBlock*                    block,
 
 void Async2Transformation::CreateResumptionSwitch()
 {
-    BasicBlock* newEntryBB = BasicBlock::New(m_comp, BBJ_ALWAYS);
-
-    if (m_comp->fgFirstBB->hasProfileWeight())
-    {
-        newEntryBB->inheritWeight(m_comp->fgFirstBB);
-    }
-    m_comp->fgFirstBB->bbRefs--;
-
-    FlowEdge* toPrevEntryEdge = m_comp->fgAddRefPred(m_comp->fgFirstBB, newEntryBB);
-    toPrevEntryEdge->setLikelihood(1);
-
-    JITDUMP("  Inserting new entry " FMT_BB " before old entry " FMT_BB "\n", newEntryBB->bbNum,
-            m_comp->fgFirstBB->bbNum);
-
-    m_comp->fgInsertBBbefore(m_comp->fgFirstBB, newEntryBB);
+    m_comp->fgCreateNewInitBB();
+    BasicBlock* newEntryBB = m_comp->fgFirstBB;
 
     GenTree* continuationArg = m_comp->gtNewLclvNode(m_comp->lvaAsyncContinuationArg, TYP_REF);
     GenTree* null            = m_comp->gtNewNull();
@@ -1339,7 +1327,7 @@ void Async2Transformation::CreateResumptionSwitch()
     else if (m_resumptionBBs.size() == 2)
     {
         BasicBlock* condBB = m_comp->fgNewBBbefore(BBJ_COND, m_resumptionBBs[0], true);
-        condBB->bbSetRunRarely();
+        condBB->inheritWeightPercentage(newEntryBB, 0);
 
         FlowEdge* to0 = m_comp->fgAddRefPred(m_resumptionBBs[0], condBB);
         FlowEdge* to1 = m_comp->fgAddRefPred(m_resumptionBBs[1], condBB);
@@ -1367,7 +1355,7 @@ void Async2Transformation::CreateResumptionSwitch()
     else
     {
         BasicBlock* switchBB = m_comp->fgNewBBbefore(BBJ_SWITCH, m_resumptionBBs[0], true);
-        switchBB->bbSetRunRarely();
+        switchBB->inheritWeightPercentage(newEntryBB, 0);
 
         resumingEdge = m_comp->fgAddRefPred(switchBB, newEntryBB);
 
@@ -1385,7 +1373,7 @@ void Async2Transformation::CreateResumptionSwitch()
 
         m_comp->fgHasSwitch = true;
 
-        //// Default case. TODO-CQ: Support bbsHasDefault = false before lowering.
+        // Default case. TODO-CQ: Support bbsHasDefault = false before lowering.
         m_resumptionBBs.push_back(m_resumptionBBs[0]);
         BBswtDesc* swtDesc     = new (m_comp, CMK_BasicBlock) BBswtDesc;
         swtDesc->bbsCount      = (unsigned)m_resumptionBBs.size();
@@ -1402,9 +1390,9 @@ void Async2Transformation::CreateResumptionSwitch()
         switchBB->SetSwitch(swtDesc);
     }
 
-    newEntryBB->SetCond(resumingEdge, toPrevEntryEdge);
-    resumingEdge->setLikelihood(RESUME_SUSPEND_LIKELIHOOD);
-    toPrevEntryEdge->setLikelihood(1 - RESUME_SUSPEND_LIKELIHOOD);
+    newEntryBB->SetCond(resumingEdge, newEntryBB->GetTargetEdge());
+    resumingEdge->setLikelihood(0);
+    newEntryBB->GetFalseEdge()->setLikelihood(1);
 
     if (m_comp->doesMethodHavePatchpoints())
     {
@@ -1428,13 +1416,15 @@ void Async2Transformation::CreateResumptionSwitch()
 
         FlowEdge* toCheckILOffsetBB = m_comp->fgAddRefPred(checkILOffsetBB, newEntryBB);
         newEntryBB->SetTrueEdge(toCheckILOffsetBB);
-        toCheckILOffsetBB->setLikelihood(RESUME_SUSPEND_LIKELIHOOD);
+        toCheckILOffsetBB->setLikelihood(0);
+        checkILOffsetBB->inheritWeightPercentage(newEntryBB, 0);
 
         FlowEdge* toOnContinuationBB = m_comp->fgAddRefPred(onContinuationBB, checkILOffsetBB);
         FlowEdge* toCallHelperBB     = m_comp->fgAddRefPred(callHelperBB, checkILOffsetBB);
         checkILOffsetBB->SetCond(toCallHelperBB, toOnContinuationBB);
-        toCallHelperBB->setLikelihood(RESUME_IN_OSR_LIKELIHOOD);
-        toOnContinuationBB->setLikelihood(1 - RESUME_IN_OSR_LIKELIHOOD);
+        toCallHelperBB->setLikelihood(0);
+        toOnContinuationBB->setLikelihood(1);
+        callHelperBB->inheritWeightPercentage(checkILOffsetBB, 0);
 
         // We need to dispatch to the OSR version if the IL offset is non-negative.
         continuationArg           = m_comp->gtNewLclvNode(m_comp->lvaAsyncContinuationArg, TYP_REF);
@@ -1477,7 +1467,8 @@ void Async2Transformation::CreateResumptionSwitch()
         m_comp->fgRemoveRefPred(newEntryBB->GetTrueEdge());
         FlowEdge* toCheckILOffset = m_comp->fgAddRefPred(checkILOffsetBB, newEntryBB);
         newEntryBB->SetTrueEdge(toCheckILOffset);
-        toCheckILOffset->setLikelihood(RESUME_SUSPEND_LIKELIHOOD);
+        toCheckILOffset->setLikelihood(0);
+        checkILOffsetBB->inheritWeightPercentage(newEntryBB, 0);
 
         // Make checkILOffsetBB ->(true)  onNoContinuationBB
         //                      ->(false) onContinuationBB
@@ -1485,8 +1476,8 @@ void Async2Transformation::CreateResumptionSwitch()
         FlowEdge* toOnContinuationBB   = m_comp->fgAddRefPred(onContinuationBB, checkILOffsetBB);
         FlowEdge* toOnNoContinuationBB = m_comp->fgAddRefPred(onNoContinuationBB, checkILOffsetBB);
         checkILOffsetBB->SetCond(toOnNoContinuationBB, toOnContinuationBB);
-        toOnContinuationBB->setLikelihood(RESUME_SUSPEND_LIKELIHOOD);
-        toOnNoContinuationBB->setLikelihood(1 - RESUME_SUSPEND_LIKELIHOOD);
+        toOnContinuationBB->setLikelihood(0);
+        toOnNoContinuationBB->setLikelihood(1);
 
         JITDUMP("    Created " FMT_BB " to check for Tier-0 continuations\n", checkILOffsetBB->bbNum);
 
diff --git a/src/coreclr/jit/codegen.h b/src/coreclr/jit/codegen.h
index 4327f1e243c57a..01dc60fe75c03f 100644
--- a/src/coreclr/jit/codegen.h
+++ b/src/coreclr/jit/codegen.h
@@ -1376,6 +1376,7 @@ class CodeGen final : public CodeGenInterface
 
     void genReturn(GenTree* treeNode);
     void genReturnSuspend(GenTreeUnOp* treeNode);
+    void genMarkReturnGCInfo();
 
 #ifdef SWIFT_SUPPORT
     void genSwiftErrorReturn(GenTree* treeNode);
diff --git a/src/coreclr/jit/codegencommon.cpp b/src/coreclr/jit/codegencommon.cpp
index 96df6807ae8632..4bb98bd74feb94 100644
--- a/src/coreclr/jit/codegencommon.cpp
+++ b/src/coreclr/jit/codegencommon.cpp
@@ -7230,26 +7230,7 @@ void CodeGen::genReturn(GenTree* treeNode)
 
     if (treeNode->OperIs(GT_RETURN, GT_SWIFT_ERROR_RET))
     {
-        const ReturnTypeDesc& retTypeDesc = compiler->compRetTypeDesc;
-
-        if (compiler->compMethodReturnsRetBufAddr())
-        {
-            gcInfo.gcMarkRegPtrVal(REG_INTRET, TYP_BYREF);
-        }
-        else
-        {
-            unsigned retRegCount = retTypeDesc.GetReturnRegCount();
-            for (unsigned i = 0; i < retRegCount; ++i)
-            {
-                gcInfo.gcMarkRegPtrVal(retTypeDesc.GetABIReturnReg(i, compiler->info.compCallConv),
-                                       retTypeDesc.GetReturnRegType(i));
-            }
-        }
-
-        if (compiler->compIsAsync2())
-        {
-            gcInfo.gcMarkRegPtrVal(REG_ASYNC_CONTINUATION_RET, TYP_REF);
-        }
+        genMarkReturnGCInfo();
     }
 
 #ifdef PROFILING_SUPPORTED
@@ -7334,6 +7315,32 @@ void CodeGen::genReturnSuspend(GenTreeUnOp* treeNode)
             instGen_Set_Reg_To_Zero(EA_PTRSIZE, returnReg);
         }
     }
+
+    genMarkReturnGCInfo();
+}
+
+void CodeGen::genMarkReturnGCInfo()
+{
+    const ReturnTypeDesc& retTypeDesc = compiler->compRetTypeDesc;
+
+    if (compiler->compMethodReturnsRetBufAddr())
+    {
+        gcInfo.gcMarkRegPtrVal(REG_INTRET, TYP_BYREF);
+    }
+    else
+    {
+        unsigned retRegCount = retTypeDesc.GetReturnRegCount();
+        for (unsigned i = 0; i < retRegCount; ++i)
+        {
+            gcInfo.gcMarkRegPtrVal(retTypeDesc.GetABIReturnReg(i, compiler->info.compCallConv),
+                                   retTypeDesc.GetReturnRegType(i));
+        }
+    }
+
+    if (compiler->compIsAsync2())
+    {
+        gcInfo.gcMarkRegPtrVal(REG_ASYNC_CONTINUATION_RET, TYP_REF);
+    }
 }
 
 void CodeGen::genCodeForAsyncContinuation(GenTree* tree)

From d32d2e8ded3450a84c118ba90734300bc2353848 Mon Sep 17 00:00:00 2001
From: Jakob Botsch Nielsen <jakob.botsch.nielsen@gmail.com>
Date: Fri, 7 Mar 2025 12:40:58 +0100
Subject: [PATCH 143/203] Fix inlining with AsyncContinuation

---
 src/coreclr/jit/fginline.cpp | 1 +
 1 file changed, 1 insertion(+)

diff --git a/src/coreclr/jit/fginline.cpp b/src/coreclr/jit/fginline.cpp
index fa7d0b3fa618b8..129574b13cf508 100644
--- a/src/coreclr/jit/fginline.cpp
+++ b/src/coreclr/jit/fginline.cpp
@@ -2079,6 +2079,7 @@ Statement* Compiler::fgInlinePrependStatements(InlineInfo* inlineInfo)
         switch (arg.GetWellKnownArg())
         {
             case WellKnownArg::RetBuffer:
+            case WellKnownArg::AsyncContinuation:
                 continue;
             case WellKnownArg::InstParam:
                 argInfo = inlineInfo->inlInstParamArgInfo;

From 2cea79abadc8b4a13fed3147b87e8379a78579ef Mon Sep 17 00:00:00 2001
From: Jakob Botsch Nielsen <jakob.botsch.nielsen@gmail.com>
Date: Fri, 7 Mar 2025 12:41:24 +0100
Subject: [PATCH 144/203] Fix `JIT_ResumeOSRWorker` holding lock for too long

---
 src/coreclr/vm/jithelpers.cpp | 33 +++++++++++++++++----------------
 1 file changed, 17 insertions(+), 16 deletions(-)

diff --git a/src/coreclr/vm/jithelpers.cpp b/src/coreclr/vm/jithelpers.cpp
index a9f2a5392a9e8d..78cc164aeda0a1 100644
--- a/src/coreclr/vm/jithelpers.cpp
+++ b/src/coreclr/vm/jithelpers.cpp
@@ -2881,27 +2881,28 @@ extern "C" void JIT_ResumeOSRWorker(TransitionBlock * pTransitionBlock)
     PCODE ip = *pReturnAddress;
     int ilOffset = *(int*)GetFirstArgumentRegisterValuePtr(pTransitionBlock);
 
-    // Fetch or setup patchpoint info for this patchpoint.
-    EECodeInfo codeInfo(ip);
-    MethodDesc* pMD = codeInfo.GetMethodDesc();
-    LoaderAllocator* allocator = pMD->GetLoaderAllocator();
-    OnStackReplacementManager* manager = allocator->GetOnStackReplacementManager();
+    // Find OSR method code for this IL offset.
 
-    PCODE tier0EntryAddr = (PCODE)codeInfo.GetStartAddress();
-    CodeVersionManager* codeVersionManager = pMD->GetCodeVersionManager();
-    CodeVersionManager::LockHolder lock;
-    NativeCodeVersionCollection nativeCodeVersions = codeVersionManager->GetNativeCodeVersions(pMD);
     PCODE osrMethodCode = NULL;
-    for (NativeCodeVersionIterator itr = nativeCodeVersions.Begin(), end = nativeCodeVersions.End(); itr != end; itr++)
+
     {
-        if (itr->GetOptimizationTier() == NativeCodeVersion::OptimizationTier1OSR)
+        EECodeInfo codeInfo(ip);
+        MethodDesc* pMD = codeInfo.GetMethodDesc();
+        PCODE tier0EntryAddr = (PCODE)codeInfo.GetStartAddress();
+        CodeVersionManager* codeVersionManager = pMD->GetCodeVersionManager();
+        CodeVersionManager::LockHolder lock;
+        NativeCodeVersionCollection nativeCodeVersions = codeVersionManager->GetNativeCodeVersions(pMD);
+        for (NativeCodeVersionIterator itr = nativeCodeVersions.Begin(), end = nativeCodeVersions.End(); itr != end; itr++)
         {
-            unsigned versionILOffset;
-            PatchpointInfo* ppi = itr->GetOSRInfo(&versionILOffset);
-            if (ppi->GetTier0EntryPoint() == tier0EntryAddr && versionILOffset == (unsigned)ilOffset)
+            if (itr->GetOptimizationTier() == NativeCodeVersion::OptimizationTier1OSR)
             {
-                osrMethodCode = itr->GetNativeCode();
-                break;
+                unsigned versionILOffset;
+                PatchpointInfo* ppi = itr->GetOSRInfo(&versionILOffset);
+                if (ppi->GetTier0EntryPoint() == tier0EntryAddr && versionILOffset == (unsigned)ilOffset)
+                {
+                    osrMethodCode = itr->GetNativeCode();
+                    break;
+                }
             }
         }
     }

From f342fe7be01193932e984fe2a5bb45a17b33b917 Mon Sep 17 00:00:00 2001
From: Jakob Botsch Nielsen <jakob.botsch.nielsen@gmail.com>
Date: Fri, 7 Mar 2025 14:01:06 +0100
Subject: [PATCH 145/203] Stop capturing ret buff arg

This changed from byref to unmanaged pointer and started getting
captured.
---
 src/coreclr/jit/async.cpp | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/src/coreclr/jit/async.cpp b/src/coreclr/jit/async.cpp
index b5b8555ddffdde..8d69a2986e505f 100644
--- a/src/coreclr/jit/async.cpp
+++ b/src/coreclr/jit/async.cpp
@@ -51,6 +51,11 @@ class AsyncLiveness
         }
 #endif
 
+        if (lclNum == m_comp->info.compRetBuffArg)
+        {
+            return true;
+        }
+
         if (lclNum == m_comp->lvaGSSecurityCookie)
         {
             // Initialized in prolog

From 22c4d06d6b90cf230354a79fadc1b07442ddba69 Mon Sep 17 00:00:00 2001
From: Jakob Botsch Nielsen <jakob.botsch.nielsen@gmail.com>
Date: Fri, 7 Mar 2025 14:01:42 +0100
Subject: [PATCH 146/203] Disable object stack allocation in async2 methods

---
 src/coreclr/jit/compiler.h | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/src/coreclr/jit/compiler.h b/src/coreclr/jit/compiler.h
index e7e511f5adf7c3..04ab89fe38e8bb 100644
--- a/src/coreclr/jit/compiler.h
+++ b/src/coreclr/jit/compiler.h
@@ -10933,6 +10933,13 @@ class Compiler
 
     bool compObjectStackAllocation()
     {
+        if (compIsAsync2())
+        {
+            // Object stack allocation takes the address of locals around
+            // suspension points. Disable entirely for now.
+            return false;
+        }
+
         return (JitConfig.JitObjectStackAllocation() != 0);
     }
 

From 917982fbb72274be93bb937ea3f149b39b726dc7 Mon Sep 17 00:00:00 2001
From: Vladimir Sadov <vsadov@microsoft.com>
Date: Tue, 11 Mar 2025 16:19:58 -0700
Subject: [PATCH 147/203] [RuntimeAsync] Some VM cleanups, TODOs, renames and
 better comments. (#3032)

* comments and renames

* Defined MethodImplOptions.Async and removed NoInlining on Async helpers.
---
 src/coreclr/debug/ee/functioninfo.cpp         |   2 +-
 src/coreclr/inc/corcompile.h                  |   2 +-
 src/coreclr/vm/clrtocomcall.cpp               |   4 +-
 src/coreclr/vm/clsload.cpp                    |   2 +-
 src/coreclr/vm/commodule.cpp                  |   2 +-
 src/coreclr/vm/commtmemberinfomap.cpp         |   4 +-
 src/coreclr/vm/comtoclrcall.cpp               |   2 +-
 src/coreclr/vm/dispatchinfo.cpp               |   6 +-
 src/coreclr/vm/dllimport.cpp                  |  14 +-
 src/coreclr/vm/encee.cpp                      |   2 +-
 src/coreclr/vm/genericdict.cpp                |  14 +-
 src/coreclr/vm/genmeth.cpp                    |  30 ++--
 src/coreclr/vm/instmethhash.cpp               |   6 +-
 src/coreclr/vm/instmethhash.h                 |   2 +-
 src/coreclr/vm/interoputil.cpp                |   4 +-
 src/coreclr/vm/jitinterface.cpp               |  10 +-
 src/coreclr/vm/method.cpp                     |   6 +-
 src/coreclr/vm/method.hpp                     | 129 +++++++++++-------
 src/coreclr/vm/methoditer.cpp                 |  12 +-
 src/coreclr/vm/methoditer.h                   |   6 +-
 src/coreclr/vm/methodtable.cpp                |  10 +-
 src/coreclr/vm/methodtablebuilder.cpp         |  73 +++++-----
 src/coreclr/vm/methodtablebuilder.h           |   6 +-
 src/coreclr/vm/multicorejit.cpp               |   4 +-
 src/coreclr/vm/prestub.cpp                    |   4 +-
 src/coreclr/vm/readytoruninfo.cpp             |   6 +-
 src/coreclr/vm/zapsig.cpp                     |   8 +-
 .../CompilerServices/MethodImplOptions.cs     |   1 +
 .../CompilerServices/RuntimeHelpers.cs        |  12 +-
 .../System.Runtime/ref/System.Runtime.cs      |   1 +
 30 files changed, 213 insertions(+), 171 deletions(-)

diff --git a/src/coreclr/debug/ee/functioninfo.cpp b/src/coreclr/debug/ee/functioninfo.cpp
index 2dc7400b94ebdd..b28620ede25439 100644
--- a/src/coreclr/debug/ee/functioninfo.cpp
+++ b/src/coreclr/debug/ee/functioninfo.cpp
@@ -2022,7 +2022,7 @@ void DebuggerMethodInfo::CreateDJIsForNativeBlobs(AppDomain * pAppDomain, Method
         // have DJIs for every verision of a method that was EnCed.
         // This also handles the possibility of getting the same methoddesc back from the iterator.
         // It also lets EnC + generics play nice together (including if an generic method was EnC-ed)
-        LoadedMethodDescIterator it(pAppDomain, m_module, m_token, /* fIsAsyncThunk */false); // TODO! Debugger doesn't handle async thunks now
+        LoadedMethodDescIterator it(pAppDomain, m_module, m_token, /* fIsAsync2Variant */false); // TODO! Debugger doesn't handle async2 variants now
         CollectibleAssemblyHolder<Assembly *> pAssembly;
         while (it.Next(pAssembly.This()))
         {
diff --git a/src/coreclr/inc/corcompile.h b/src/coreclr/inc/corcompile.h
index bef0f9812fc6a7..6458f919bb406c 100644
--- a/src/coreclr/inc/corcompile.h
+++ b/src/coreclr/inc/corcompile.h
@@ -180,7 +180,7 @@ enum EncodeMethodSigFlags
     ENCODE_METHOD_SIG_Constrained               = 0x20,
     ENCODE_METHOD_SIG_OwnerType                 = 0x40,
     ENCODE_METHOD_SIG_UpdateContext             = 0x80,
-    ENCODE_METHOD_SIG_AsyncHelper               = 0x100,
+    ENCODE_METHOD_SIG_Async2Variant             = 0x100,
 };
 
 enum EncodeFieldSigFlags
diff --git a/src/coreclr/vm/clrtocomcall.cpp b/src/coreclr/vm/clrtocomcall.cpp
index 35b9fd6abefedc..23c10df106b239 100644
--- a/src/coreclr/vm/clrtocomcall.cpp
+++ b/src/coreclr/vm/clrtocomcall.cpp
@@ -230,7 +230,7 @@ I4ARRAYREF SetUpWrapperInfo(MethodDesc *pMD)
 
         GCX_PREEMP();
 
-        if (pMD->IsAsyncHelperMethod())
+        if (pMD->IsAsync2Method())
         {
             ThrowHR(COR_E_NOTSUPPORTED);
         }
@@ -509,7 +509,7 @@ UINT32 CLRToCOMLateBoundWorker(
     LPCUTF8 strMemberName;
     ULONG uSemantic;
 
-    if (pItfMD->IsAsyncHelperMethod())
+    if (pItfMD->IsAsync2Method())
     {
         ThrowHR(COR_E_NOTSUPPORTED);
     }
diff --git a/src/coreclr/vm/clsload.cpp b/src/coreclr/vm/clsload.cpp
index a571ac0a19e3c1..fa882edbacecce 100644
--- a/src/coreclr/vm/clsload.cpp
+++ b/src/coreclr/vm/clsload.cpp
@@ -2801,7 +2801,7 @@ TypeHandle ClassLoader::PublishType(const TypeKey *pTypeKey, TypeHandle typeHnd)
         {
             MethodDesc * pMD = it.GetMethodDesc();
             CONSISTENCY_CHECK(pMD != NULL && pMD->GetMethodTable() == pMT);
-            if (!pMD->IsUnboxingStub() && !pMD->IsAsyncHelperMethod())
+            if (!pMD->IsUnboxingStub() && !pMD->IsAsync2VariantMethod())
             {
                 pModule->EnsuredStoreMethodDef(pMD->GetMemberDef(), pMD);
             }
diff --git a/src/coreclr/vm/commodule.cpp b/src/coreclr/vm/commodule.cpp
index 1500482caa1c81..1b79649fdfb046 100644
--- a/src/coreclr/vm/commodule.cpp
+++ b/src/coreclr/vm/commodule.cpp
@@ -302,7 +302,7 @@ extern "C" INT32 QCALLTYPE ModuleBuilder_GetMemberRefOfMethodInfo(QCall::ModuleH
         COMPlusThrow(kNotSupportedException);
     }
 
-    if ((pMeth->GetMethodTable()->GetModule() == pModule) && !pMeth->IsAsyncHelperMethod())
+    if ((pMeth->GetMethodTable()->GetModule() == pModule) && !pMeth->IsAsync2VariantMethod())
     {
         // If the passed in method is defined in the same module, just return the MethodDef token
         memberRefE = pMeth->GetMemberDef();
diff --git a/src/coreclr/vm/commtmemberinfomap.cpp b/src/coreclr/vm/commtmemberinfomap.cpp
index d64e57b61fb4b1..8126f9b673176b 100644
--- a/src/coreclr/vm/commtmemberinfomap.cpp
+++ b/src/coreclr/vm/commtmemberinfomap.cpp
@@ -688,7 +688,7 @@ void ComMTMemberInfoMap::GetMethodPropsForMeth(
     // Generally don't munge function into a getter.
     rProps[ix].bFunction2Getter = FALSE;
 
-    if (pMeth->IsAsyncHelperMethod())
+    if (pMeth->IsAsync2Method())
         ThrowHR(COR_E_NOTSUPPORTED);
 
     // See if there is property information for this member.
@@ -1607,7 +1607,7 @@ void ComMTMemberInfoMap::PopulateMemberHashtable()
 
             // We are dealing with a method.
             MethodDesc *pMD = pProps->pMeth;
-            if (pMD->IsAsyncHelperMethod())
+            if (pMD->IsAsync2Method())
             {
                 ThrowHR(COR_E_NOTSUPPORTED); // Probably this isn't right, and instead should be a skip, but a throw makes it easier to find if this is wrong
             }
diff --git a/src/coreclr/vm/comtoclrcall.cpp b/src/coreclr/vm/comtoclrcall.cpp
index 80ead01c3e69ee..1beae2cd959656 100644
--- a/src/coreclr/vm/comtoclrcall.cpp
+++ b/src/coreclr/vm/comtoclrcall.cpp
@@ -986,7 +986,7 @@ void ComCallMethodDesc::InitNativeInfo()
 
             MethodTable * pMT = pMD->GetMethodTable();
             IMDInternalImport * pInternalImport = pMT->GetMDImport();
-            if (pMD->IsAsyncHelperMethod())
+            if (pMD->IsAsync2Method())
             {
                 ThrowHR(COR_E_NOTSUPPORTED);
             }
diff --git a/src/coreclr/vm/dispatchinfo.cpp b/src/coreclr/vm/dispatchinfo.cpp
index 8caf1897cb4a86..00549cdc341786 100644
--- a/src/coreclr/vm/dispatchinfo.cpp
+++ b/src/coreclr/vm/dispatchinfo.cpp
@@ -449,7 +449,7 @@ ComMTMethodProps * DispatchMemberInfo::GetMemberProps(OBJECTREF MemberInfoObj, C
             MethodDesc* pMeth = (MethodDesc*) getMethodHandle.Call_RetLPVOID(&GetMethodHandleArg);
             if (pMeth)
             {
-                if (pMeth->IsAsyncHelperMethod())
+                if (pMeth->IsAsync2Method())
                 {
                     ThrowHR(COR_E_NOTSUPPORTED);
                 }
@@ -850,7 +850,7 @@ void DispatchMemberInfo::SetUpMethodMarshalerInfo(MethodDesc *pMD, BOOL bReturnV
     //
     // Initialize the parameter definition enum.
     //
-    if (pMD->IsAsyncHelperMethod())
+    if (pMD->IsAsync2Method())
     {
         ThrowHR(COR_E_NOTSUPPORTED);
     }
@@ -2588,7 +2588,7 @@ bool DispatchInfo::IsPropertyAccessorVisible(bool fIsSetter, OBJECTREF* pMemberI
 
         // Check to see if the new method is a property accessor.
         mdToken tkMember = mdTokenNil;
-        if (pMDForProperty->IsAsyncHelperMethod())
+        if (pMDForProperty->IsAsync2VariantMethod())
         {
             return false;
         }
diff --git a/src/coreclr/vm/dllimport.cpp b/src/coreclr/vm/dllimport.cpp
index 1ebf7d16c5deb7..2b6afa1ad7b34f 100644
--- a/src/coreclr/vm/dllimport.cpp
+++ b/src/coreclr/vm/dllimport.cpp
@@ -105,7 +105,7 @@ StubSigDesc::StubSigDesc(MethodDesc *pMD)
     m_sig           = pMD->GetSignature();
     m_pModule       = pMD->GetModule();         // Used for token resolution.
 
-    _ASSERTE(!pMD->IsAsyncHelperMethod());
+    _ASSERTE(!pMD->IsAsync2Method());
 
     m_tkMethodDef = pMD->GetMemberDef();
     SigTypeContext::InitTypeContext(pMD, &m_typeContext);
@@ -134,7 +134,7 @@ StubSigDesc::StubSigDesc(MethodDesc* pMD, const Signature& sig, Module* pModule,
 
     if (pMD != NULL)
     {
-        _ASSERTE(!pMD->IsAsyncHelperMethod());
+        _ASSERTE(!pMD->IsAsync2Method());
         m_tkMethodDef = pMD->GetMemberDef();
         SigTypeContext::InitTypeContext(pMD, &m_typeContext);
         m_pMetadataModule = pMD->GetModule();
@@ -1090,7 +1090,7 @@ class ILStubState : public StubState
         DWORD dwToken = 0;
         if (pTargetMD)
         {
-            _ASSERTE(!pTargetMD->IsAsyncHelperMethod());
+            _ASSERTE(!pTargetMD->IsAsync2VariantMethod());
             dwToken = pTargetMD->GetMemberDef();
         }
 
@@ -2716,7 +2716,7 @@ void PInvokeStaticSigInfo::DllImportInit(
     IMDInternalImport  *pInternalImport = pMD->GetMDImport();
     CorPinvokeMap mappingFlags = pmMaxValue;
     mdModuleRef modref = mdModuleRefNil;
-    if (pMD->IsAsyncHelperMethod())
+    if (pMD->IsAsync2Method())
     {
         ThrowHR(COR_E_NOTSUPPORTED);
     }
@@ -2997,7 +2997,7 @@ namespace
         CorInfoCallConvExtension callConvLocal;
         IMDInternalImport* pInternalImport = pMD->GetMDImport();
         CorPinvokeMap mappingFlags = pmMaxValue;
-        if (pMD->IsAsyncHelperMethod())
+        if (pMD->IsAsync2Method())
         {
             ThrowHR(COR_E_NOTSUPPORTED);
         }
@@ -3261,7 +3261,7 @@ BOOL NDirect::MarshalingRequired(
     mdMethodDef methodToken = mdMethodDefNil;
     if (pMD != NULL)
     {
-        if (pMD->IsAsyncHelperMethod())
+        if (pMD->IsAsync2Method())
         {
             ThrowHR(COR_E_NOTSUPPORTED);
         }
@@ -6059,7 +6059,7 @@ PCODE GetILStubForCalli(VASigCookie *pVASigCookie, MethodDesc *pMD)
     {
         PInvokeStaticSigInfo sigInfo(pMD);
 
-        if (pMD->IsAsyncHelperMethod())
+        if (pMD->IsAsync2Method())
         {
             ThrowHR(COR_E_NOTSUPPORTED);
         }
diff --git a/src/coreclr/vm/encee.cpp b/src/coreclr/vm/encee.cpp
index ce3bf13cf9ea6d..9b2d7c6f6d9818 100644
--- a/src/coreclr/vm/encee.cpp
+++ b/src/coreclr/vm/encee.cpp
@@ -342,7 +342,7 @@ HRESULT EditAndContinueModule::UpdateMethod(MethodDesc *pMethod)
             AppDomain::GetCurrentDomain(),
             module,
             tkMethod,
-            pMethod->IsAsyncHelperMethod(),
+            pMethod->IsAsync2VariantMethod(),
             AssemblyIterationFlags(kIncludeLoaded | kIncludeExecution));
         CollectibleAssemblyHolder<Assembly *> pAssembly;
         while (it.Next(pAssembly.This()))
diff --git a/src/coreclr/vm/genericdict.cpp b/src/coreclr/vm/genericdict.cpp
index 48a8e423b18d9b..90d52a4f20016a 100644
--- a/src/coreclr/vm/genericdict.cpp
+++ b/src/coreclr/vm/genericdict.cpp
@@ -889,7 +889,7 @@ Dictionary::PopulateEntry(
 
             uint32_t methodSlot = -1;
             BOOL fRequiresDispatchStub = 0;
-            BOOL isAsyncHelper = 0;
+            BOOL isAsync2Variant = 0;
 
             if (isReadyToRunModule)
             {
@@ -901,7 +901,7 @@ Dictionary::PopulateEntry(
                 isInstantiatingStub = ((methodFlags & ENCODE_METHOD_SIG_InstantiatingStub) != 0) || (kind == MethodEntrySlot);
                 isUnboxingStub = ((methodFlags & ENCODE_METHOD_SIG_UnboxingStub) != 0);
                 fMethodNeedsInstantiation = ((methodFlags & ENCODE_METHOD_SIG_MethodInstantiation) != 0);
-                isAsyncHelper = ((methodFlags & ENCODE_METHOD_SIG_AsyncHelper) != 0);
+                isAsync2Variant = ((methodFlags & ENCODE_METHOD_SIG_Async2Variant) != 0);
 
                 if (methodFlags & ENCODE_METHOD_SIG_OwnerType)
                 {
@@ -952,7 +952,7 @@ Dictionary::PopulateEntry(
                         _ASSERTE(pZapSigContext->pInfoModule->IsFullModule());
                         pMethod = MemberLoader::GetMethodDescFromMethodDef(static_cast<Module*>(pZapSigContext->pInfoModule), TokenFromRid(rid, mdtMethodDef), FALSE);
                     }
-                    if (isAsyncHelper)
+                    if (isAsync2Variant)
                     {
                         pMethod = pMethod->GetAsyncOtherVariant();
                     }
@@ -999,7 +999,7 @@ Dictionary::PopulateEntry(
                 isInstantiatingStub = ((methodFlags & ENCODE_METHOD_SIG_InstantiatingStub) != 0);
                 isUnboxingStub = ((methodFlags & ENCODE_METHOD_SIG_UnboxingStub) != 0);
                 fMethodNeedsInstantiation = ((methodFlags & ENCODE_METHOD_SIG_MethodInstantiation) != 0);
-                isAsyncHelper = ((methodFlags & ENCODE_METHOD_SIG_AsyncHelper) != 0);
+                isAsync2Variant = ((methodFlags & ENCODE_METHOD_SIG_Async2Variant) != 0);
 
                 if ((methodFlags & ENCODE_METHOD_SIG_SlotInsteadOfToken) != 0)
                 {
@@ -1042,7 +1042,7 @@ Dictionary::PopulateEntry(
                     // The RID map should have been filled out if we fully loaded the class
                     pMethod = pMethodDefMT->GetModule()->LookupMethodDef(token);
 
-                    if (isAsyncHelper)
+                    if (isAsync2Variant)
                     {
                         pMethod = pMethod->GetAsyncOtherVariant();
                     }
@@ -1077,7 +1077,7 @@ Dictionary::PopulateEntry(
                 break;
             }
 
-            _ASSERTE((!!isAsyncHelper) == pMethod->IsAsyncHelperMethod());
+            _ASSERTE((!!isAsync2Variant) == pMethod->IsAsync2VariantMethod());
 
             Instantiation inst;
 
@@ -1123,7 +1123,7 @@ Dictionary::PopulateEntry(
                 inst,
                 (!isInstantiatingStub && !isUnboxingStub));
 
-            _ASSERTE((!!isAsyncHelper) == pMethod->IsAsyncHelperMethod());
+            _ASSERTE((!!isAsync2Variant) == pMethod->IsAsync2VariantMethod());
 
             if (kind == ConstrainedMethodEntrySlot)
             {
diff --git a/src/coreclr/vm/genmeth.cpp b/src/coreclr/vm/genmeth.cpp
index 518b7e4e2eaf50..55bf8be02942b2 100644
--- a/src/coreclr/vm/genmeth.cpp
+++ b/src/coreclr/vm/genmeth.cpp
@@ -246,7 +246,7 @@ static MethodDesc * FindTightlyBoundWrappedMethodDesc_DEBUG(MethodDesc * pMD)
 
     mdMethodDef methodDef = pMD->GetMemberDef();
     Module *pModule = pMD->GetModule();
-    bool isAsyncHelperMethod = pMD->IsAsyncHelperMethod();
+    bool isAsync2VariantMethod = pMD->IsAsync2VariantMethod();
 
     MethodTable::MethodIterator it(pMD->GetCanonicalMethodTable());
     it.MoveToEnd();
@@ -258,7 +258,7 @@ static MethodDesc * FindTightlyBoundWrappedMethodDesc_DEBUG(MethodDesc * pMD)
             if (pCurMethod && !pCurMethod->IsUnboxingStub()) {
                 if ((pCurMethod->GetMemberDef() == methodDef)  &&
                     (pCurMethod->GetModule() == pModule) &&
-                    (pCurMethod->IsAsyncHelperMethod() == isAsyncHelperMethod))
+                    (pCurMethod->IsAsync2VariantMethod() == isAsync2VariantMethod))
                 {
                     return pCurMethod;
                 }
@@ -286,7 +286,7 @@ static MethodDesc * FindTightlyBoundUnboxingStub_DEBUG(MethodDesc * pMD)
 
     mdMethodDef methodDef = pMD->GetMemberDef();
     Module *pModule = pMD->GetModule();
-    bool isAsyncHelperMethod = pMD->IsAsyncHelperMethod();
+    bool isAsync2VariantMethod = pMD->IsAsync2VariantMethod();
 
     MethodTable::MethodIterator it(pMD->GetCanonicalMethodTable());
     it.MoveToEnd();
@@ -296,7 +296,7 @@ static MethodDesc * FindTightlyBoundUnboxingStub_DEBUG(MethodDesc * pMD)
             if (pCurMethod && pCurMethod->IsUnboxingStub()) {
                 if ((pCurMethod->GetMemberDef() == methodDef) &&
                     (pCurMethod->GetModule() == pModule) &&
-                    (pCurMethod->IsAsyncHelperMethod() == isAsyncHelperMethod)) {
+                    (pCurMethod->IsAsync2VariantMethod() == isAsync2VariantMethod)) {
                     return pCurMethod;
                 }
             }
@@ -363,7 +363,7 @@ InstantiatedMethodDesc::NewInstantiatedMethodDesc(MethodTable *pExactMT,
                                                   pGenericMDescInRepMT->GetMemberDef(),
                                                   methodInst,
                                                   getWrappedCode,
-                                                  pGenericMDescInRepMT->IsAsyncHelperMethod());
+                                                  pGenericMDescInRepMT->IsAsync2VariantMethod());
 
         // Crst goes out of scope here
         // We don't need to hold the crst while we build the MethodDesc, but we reacquire it later
@@ -484,7 +484,7 @@ InstantiatedMethodDesc::NewInstantiatedMethodDesc(MethodTable *pExactMT,
                                                       pGenericMDescInRepMT->GetMemberDef(),
                                                       methodInst,
                                                       getWrappedCode,
-                                                      pGenericMDescInRepMT->IsAsyncHelperMethod());
+                                                      pGenericMDescInRepMT->IsAsync2VariantMethod());
 
             if (pOldMD == NULL)
             {
@@ -559,7 +559,7 @@ InstantiatedMethodDesc::FindOrCreateExactClassMethod(MethodTable *pExactMT,
                                                                        pCanonicalMD->GetMemberDef(),
                                                                        Instantiation(),
                                                                        FALSE,
-                                                                       pCanonicalMD->IsAsyncHelperMethod());
+                                                                       pCanonicalMD->IsAsync2VariantMethod());
 
     if (pInstMD == NULL)
     {
@@ -895,7 +895,7 @@ MethodDesc::FindOrCreateAssociatedMethodDesc(MethodDesc* pDefMD,
                                                TRUE /* forceBoxedEntryPoint */,
                                                Instantiation(),
                                                FALSE /* no inst param */,
-                                               pMDescInCanonMT->IsAsyncHelperMethod());
+                                               pMDescInCanonMT->IsAsync2VariantMethod());
 
             // If we didn't find it then create it...
             if (!pResultMD)
@@ -914,7 +914,7 @@ MethodDesc::FindOrCreateAssociatedMethodDesc(MethodDesc* pDefMD,
                                                    TRUE,
                                                    Instantiation(),
                                                    FALSE,
-                                                   pMDescInCanonMT->IsAsyncHelperMethod());
+                                                   pMDescInCanonMT->IsAsync2VariantMethod());
                 if (pResultMD == NULL)
                 {
                     AllocMemTracker amt;
@@ -961,7 +961,7 @@ MethodDesc::FindOrCreateAssociatedMethodDesc(MethodDesc* pDefMD,
                                                TRUE, /* forceBoxedEntryPoint */
                                                methodInst,
                                                FALSE /* no inst param */,
-                                               pMDescInCanonMT->IsAsyncHelperMethod());
+                                               pMDescInCanonMT->IsAsync2VariantMethod());
 
             if (!pResultMD)
             {
@@ -983,7 +983,7 @@ MethodDesc::FindOrCreateAssociatedMethodDesc(MethodDesc* pDefMD,
                 _ASSERTE(pNonUnboxingStub->GetClassification() == mcInstantiated);
                 _ASSERTE(!pNonUnboxingStub->RequiresInstArg());
                 _ASSERTE(!pNonUnboxingStub->IsUnboxingStub());
-                _ASSERTE(pNonUnboxingStub->IsAsyncHelperMethod() == pMDescInCanonMT->IsAsyncHelperMethod());
+                _ASSERTE(pNonUnboxingStub->IsAsync2VariantMethod() == pMDescInCanonMT->IsAsync2VariantMethod());
 
                 // Enter the critical section *after* we've found or created the non-unboxing instantiating stub (else we'd have a race,
                 // and its possible that the non-unboxing instantiating stub may be in a different loader module than pLoaderModule
@@ -996,7 +996,7 @@ MethodDesc::FindOrCreateAssociatedMethodDesc(MethodDesc* pDefMD,
                                                    TRUE, /* forceBoxedEntryPoint */
                                                    methodInst,
                                                    FALSE /* no inst param */,
-                                                   pNonUnboxingStub->IsAsyncHelperMethod());
+                                                   pNonUnboxingStub->IsAsync2VariantMethod());
 
                 if (pResultMD == NULL)
                 {
@@ -1138,7 +1138,7 @@ MethodDesc::FindOrCreateAssociatedMethodDesc(MethodDesc* pDefMD,
                                                                          methodDef,
                                                                          Instantiation(repInst, methodInst.GetNumArgs()),
                                                                          TRUE,
-                                                                         pMDescInCanonMT->IsAsyncHelperMethod());
+                                                                         pMDescInCanonMT->IsAsync2VariantMethod());
 
             // No - so create one.
             if (pInstMD == NULL)
@@ -1163,7 +1163,7 @@ MethodDesc::FindOrCreateAssociatedMethodDesc(MethodDesc* pDefMD,
                                                                          methodDef,
                                                                          methodInst,
                                                                          FALSE,
-                                                                         pMDescInCanonMT->IsAsyncHelperMethod());
+                                                                         pMDescInCanonMT->IsAsync2VariantMethod());
 
             // No - so create one.  Go fetch the shared one first
             if (pInstMD == NULL)
@@ -1204,7 +1204,7 @@ MethodDesc::FindOrCreateAssociatedMethodDesc(MethodDesc* pDefMD,
                                                                          methodDef,
                                                                          methodInst,
                                                                          FALSE,
-                                                                         pMDescInCanonMT->IsAsyncHelperMethod());
+                                                                         pMDescInCanonMT->IsAsync2VariantMethod());
 
             // No - so create one.
             if (pInstMD == NULL)
diff --git a/src/coreclr/vm/instmethhash.cpp b/src/coreclr/vm/instmethhash.cpp
index 6087a5100b4df6..4076925daff089 100644
--- a/src/coreclr/vm/instmethhash.cpp
+++ b/src/coreclr/vm/instmethhash.cpp
@@ -119,7 +119,7 @@ MethodDesc* InstMethodHashTable::FindMethodDesc(TypeHandle declaringType,
                                                 BOOL unboxingStub,
                                                 Instantiation inst,
                                                 BOOL getSharedNotStub,
-                                                bool isAsyncHelper)
+                                                bool isAsync2Variant)
 {
     CONTRACTL
     {
@@ -161,7 +161,7 @@ MethodDesc* InstMethodHashTable::FindMethodDesc(TypeHandle declaringType,
             continue;  // Next iteration of the for loop
         }
 
-        if (pMD->IsAsyncHelperMethod() != isAsyncHelper)
+        if (pMD->IsAsync2VariantMethod() != isAsync2Variant)
         {
             continue;
         }
@@ -208,7 +208,7 @@ BOOL InstMethodHashTable::ContainsMethodDesc(MethodDesc* pMD)
 
     return FindMethodDesc(
         pMD->GetMethodTable(), pMD->GetMemberDef(), pMD->IsUnboxingStub(),
-        pMD->GetMethodInstantiation(), pMD->RequiresInstArg(), pMD->IsAsyncHelperMethod()) != NULL;
+        pMD->GetMethodInstantiation(), pMD->RequiresInstArg(), pMD->IsAsync2VariantMethod()) != NULL;
 }
 
 #endif // #ifndef DACCESS_COMPILE
diff --git a/src/coreclr/vm/instmethhash.h b/src/coreclr/vm/instmethhash.h
index 66d6a1d8da3b91..d678a4232a150d 100644
--- a/src/coreclr/vm/instmethhash.h
+++ b/src/coreclr/vm/instmethhash.h
@@ -110,7 +110,7 @@ class InstMethodHashTable : public DacEnumerableHashTable<InstMethodHashTable, I
                                BOOL unboxingStub,
                                Instantiation inst,
                                BOOL getSharedNotStub,
-                               bool isAsyncHelper);
+                               bool isAsync2Variant);
 
     BOOL ContainsMethodDesc(MethodDesc* pMD);
 
diff --git a/src/coreclr/vm/interoputil.cpp b/src/coreclr/vm/interoputil.cpp
index 9824e979656808..dca57de4a6ee71 100644
--- a/src/coreclr/vm/interoputil.cpp
+++ b/src/coreclr/vm/interoputil.cpp
@@ -2280,7 +2280,7 @@ ULONG GetStringizedClassItfDef(TypeHandle InterfaceType, CQuickArray<BYTE> &rDef
                 {
                     pDeclaringMT = pProps->pMeth->GetMethodTable();
                     tkMb = pProps->pMeth->GetMemberDef();
-                    if (pProps->pMeth->IsAsyncHelperMethod())
+                    if (pProps->pMeth->IsAsync2Method())
                     {
                         ThrowHR(COR_E_NOTSUPPORTED);
                     }
@@ -2494,7 +2494,7 @@ BOOL IsMethodVisibleFromCom(MethodDesc *pMD)
     mdProperty  pd;
     LPCUTF8     pPropName;
     ULONG       uSemantic;
-    if (pMD->IsAsyncHelperMethod())
+    if (pMD->IsAsync2Method())
         return false;
         
     mdMethodDef md = pMD->GetMemberDef();
diff --git a/src/coreclr/vm/jitinterface.cpp b/src/coreclr/vm/jitinterface.cpp
index 014c74464217e0..e023efcf15a2e2 100644
--- a/src/coreclr/vm/jitinterface.cpp
+++ b/src/coreclr/vm/jitinterface.cpp
@@ -432,7 +432,7 @@ enum ConvToJitSigFlags : int
 // localSig     - Is it a local variables declaration, or a method signature (with return type, etc).
 // contextType  - The type with any instantiaton information
 //
-AsyncTaskMethod ClassifyAsyncMethodCore(SigPointer sig, Module* pModule, PCCOR_SIGNATURE initialSig, ULONG* offsetOfAsyncDetails, bool *isValueTask);
+AsyncMethodSignatureKind ClassifyAsyncMethodSignatureCore(SigPointer sig, Module* pModule, PCCOR_SIGNATURE initialSig, ULONG* offsetOfAsyncDetails, bool *isValueTask);
 
 static void ConvToJitSig(
     SigPointer            sig,
@@ -513,8 +513,8 @@ static void ConvToJitSig(
         sigRet->retType = CEEInfo::asCorInfoType(type, typeHnd, &sigRet->retTypeClass);
         sigRet->retTypeSigClass = CORINFO_CLASS_HANDLE(typeHnd.AsPtr());
 
-        auto asyncMethodClassification = ClassifyAsyncMethodCore(sig, module, NULL, NULL, NULL);
-        if (IsAsyncTaskMethodAsync2Method(asyncMethodClassification))
+        auto asyncMethodClassification = ClassifyAsyncMethodSignatureCore(sig, module, NULL, NULL, NULL);
+        if (IsAsyncSigAsync2(asyncMethodClassification))
         {
             sigRet->callConv = (CorInfoCallConv)(sigRet->callConv | CORINFO_CALLCONV_ASYNCCALL);
         }
@@ -3233,9 +3233,9 @@ void CEEInfo::ComputeRuntimeLookupForSharedGenericToken(DictionaryEntryKind entr
 
                 methodFlags |= ENCODE_METHOD_SIG_SlotInsteadOfToken;
             }
-            if (pTemplateMD->IsAsyncHelperMethod())
+            if (pTemplateMD->IsAsync2VariantMethod())
             {
-                methodFlags |= ENCODE_METHOD_SIG_AsyncHelper;
+                methodFlags |= ENCODE_METHOD_SIG_Async2Variant;
             }
 
             sigBuilder.AppendData(methodFlags);
diff --git a/src/coreclr/vm/method.cpp b/src/coreclr/vm/method.cpp
index 5fc687293bb5ef..7b099d65058e21 100644
--- a/src/coreclr/vm/method.cpp
+++ b/src/coreclr/vm/method.cpp
@@ -433,7 +433,7 @@ void MethodDesc::GetSig(PCCOR_SIGNATURE *ppSig, DWORD *pcSig)
             return;
         }
     }
-    if (IsAsyncHelperMethod())
+    if (IsAsync2VariantMethod())
     {
         Signature sig = GetAddrOfAsyncMethodData()->sig;
         *ppSig = sig.GetRawSig();
@@ -468,7 +468,7 @@ void MethodDesc::GetSigFromMetadata(IMDInternalImport * importer,
     }
     CONTRACTL_END
 
-    _ASSERTE(!IsAsyncHelperMethod());
+    _ASSERTE(!IsAsync2VariantMethod());
     if (FAILED(importer->GetSigOfMethodDef(GetMemberDef(), pcSig, ppSig)))
     {   // Class loader already asked for signature, so this should always succeed (unless there's a
         // bug or a new code path)
@@ -741,7 +741,7 @@ BOOL MethodDesc::HasSameMethodDefAs(MethodDesc * pMD)
     if (this == pMD)
         return TRUE;
 
-    return (GetMemberDef() == pMD->GetMemberDef()) && (GetModule() == pMD->GetModule() && pMD->IsAsyncHelperMethod() == IsAsyncHelperMethod());
+    return (GetMemberDef() == pMD->GetMemberDef()) && (GetModule() == pMD->GetModule() && pMD->IsAsync2VariantMethod() == IsAsync2VariantMethod());
 }
 
 //*******************************************************************************
diff --git a/src/coreclr/vm/method.hpp b/src/coreclr/vm/method.hpp
index 0cb0009fe2d529..507d2c9e194afc 100644
--- a/src/coreclr/vm/method.hpp
+++ b/src/coreclr/vm/method.hpp
@@ -58,29 +58,54 @@ EXTERN_C VOID STDCALL NDirectImportThunk();
 enum class AsyncMethodKind
 {
     // Regular methods not returning tasks
-    // These do not get a thunk sibling.
-    // N.B. Generic T-returning methods are NotAsync
+    // These are "normal" methods that do not get other variants.
+    // Note: Generic T-returning methods are NotAsync, even if T could be a Task.
     NotAsync,
 
     // Regular methods that return Task/ValueTask
-    // These methods have a synthetic sibling that is an async2 thunk
+    // These methods have a synthetic variant that is an Async2-callable helper.
     TaskReturning,
 
-    // Task-returning methods marked as async in IL.
-    // The method body forwards to Async2 implementation thunk (method with kind AsyncImplHelper)
+    // Task-returning methods marked as MethodImpl::Async in metadata.
+    // These methods have bodies that are actually thunks to Async2 implementation variants (Async2VariantImpl)
     RuntimeAsync,
 
-    // Synthetic Async2 method that contains the
-    // actual implementation transformed into a resumable state machine
-    AsyncImplHelper,
-
-    // Synthetic Async2 method that forwards to a TaskReturning method
-    AsyncThunkHelper,
-
-    // Actual IL method that is explicitly declared as Async2 and thus compiled into a state machine.
-    // Such methods do not get Async thunks and can only be called from another Async2 method using Async2 call convention.
-    // This is used in a few infrastructure methods like `Await`
-    AsyncImplExplicit,
+    //=============================================================
+    // On {TaskReturning, Async2VariantThunk} and {RuntimeAsync, Async2VariantImpl} pairs:
+    //
+    // When we see a Task-returning method we create 2 method varaints that logically match the same method definition.
+    // One variant has the same signature/callconv as the defining method and another is a matching Async2 variant.
+    // Depending on whether the definition was a runtime async method or an ordinary Task-returning method,
+    // one variant is the actual implementation and another is a thunk.
+    //
+    // The signature of the Async2 variant is formed from the original signature by replacing Task return type with
+    // modreq'd element type:
+    //   Example: "Task<int> Foo();"  ===> "modreq(Task`) int Foo();"
+    //   Example: "ValueTask Bar();"  ===> "modreq(ValueTask) void Bar();"
+    //
+    // It is possible to get from one variant to another unambiguously via GetAsyncOtherVariant.
+    //
+    // Async2 methods are called with CORINFO_CALLCONV_ASYNCCALL call convention.
+    // 
+    // NOTE: not all Async2 methods are "variants" from a pair, see Async2ExplicitImpl below.
+    //=============================================================
+
+    // The following methods use special calling convention (CORINFO_CALLCONV_ASYNCCALL)
+    // These methods are emitted by the JIT as resumable state machines and also take an extra
+    // parameter and extra return - the continuation object.
+
+    // Async2 methods with actual IL implementation of a MethodImpl::Async method.
+    Async2VariantImpl,
+
+    // Async2 methods with synthetic bodies that forward to a TaskReturning method.
+    Async2VariantThunk,
+
+    // Methods that are explicitly declared as Async2 in metadata while not Task returning.
+    // This is a special case used in a few infrastructure methods like `Await`.
+    // Such methods do not get non-Async2 variants/thunks and can only be called from another Async2 method.
+    // NOTE: These methods have the original signature and it is not possible to tell if the method is Async2
+    //       from the signature alone, thus all these methods are also JIT intrinsics.
+    Async2ExplicitImpl,
 };
 
 struct AsyncMethodData
@@ -208,7 +233,7 @@ enum class AsyncVariantLookup
     AsyncOtherVariant
 };
 
-enum class AsyncTaskMethod
+enum class AsyncMethodSignatureKind
 {
     TaskReturningMethod,
     TaskNonGenericReturningMethod,
@@ -217,19 +242,19 @@ enum class AsyncTaskMethod
     NormalMethod
 };
 
-inline bool IsAsyncTaskMethodNormal(AsyncTaskMethod input)
+inline bool IsAsyncSigNormal(AsyncMethodSignatureKind input)
 {
-    return input == AsyncTaskMethod::NormalMethod;
+    return input == AsyncMethodSignatureKind::NormalMethod;
 }
 
-inline bool IsAsyncTaskMethodAsync2Method(AsyncTaskMethod input)
+inline bool IsAsyncSigAsync2(AsyncMethodSignatureKind input)
 {
-    return (input == AsyncTaskMethod::Async2Method) || (input == AsyncTaskMethod::Async2MethodNonGeneric);
+    return (input == AsyncMethodSignatureKind::Async2Method) || (input == AsyncMethodSignatureKind::Async2MethodNonGeneric);
 }
 
-inline bool IsAsyncTaskMethodTaskReturningMethod(AsyncTaskMethod input)
+inline bool IsAsyncSigTaskReturning(AsyncMethodSignatureKind input)
 {
-    return (input == AsyncTaskMethod::TaskReturningMethod) || (input == AsyncTaskMethod::TaskNonGenericReturningMethod);
+    return (input == AsyncMethodSignatureKind::TaskReturningMethod) || (input == AsyncMethodSignatureKind::TaskNonGenericReturningMethod);
 }
 
 // The size of this structure needs to be a multiple of MethodDesc::ALIGNMENT
@@ -1824,38 +1849,54 @@ class MethodDesc
         m_wFlags |= mdfHasNativeCodeSlot;
     }
 
-    inline bool IsAsyncHelperMethod() const
+    // Historically we use "Async2" to mean methods that can be called via CORINFO_CALLCONV_ASYNCCALL
+    // CONSIDER: We could have a better name for the concept, but it is hard to beat shortness of "Async2"
+    inline bool IsAsync2Method() const
     {
         LIMITED_METHOD_DAC_CONTRACT;
         if (!HasAsyncMethodData())
             return false;
         auto asyncKind = GetAddrOfAsyncMethodData()->kind;
-        return asyncKind == AsyncMethodKind::AsyncThunkHelper ||
-            asyncKind == AsyncMethodKind::AsyncImplHelper;
+        return asyncKind == AsyncMethodKind::Async2VariantThunk ||
+            asyncKind == AsyncMethodKind::Async2VariantImpl ||
+            asyncKind == AsyncMethodKind::Async2ExplicitImpl;
     }
 
-    inline bool IsTaskReturningMethod() const
+    // Is this an Async2-callable variant method?
+    // If yes, the method has another non-Async2 variant.
+    inline bool IsAsync2VariantMethod() const
     {
         LIMITED_METHOD_DAC_CONTRACT;
         if (!HasAsyncMethodData())
             return false;
         auto asyncKind = GetAddrOfAsyncMethodData()->kind;
-        return asyncKind == AsyncMethodKind::RuntimeAsync ||
-            asyncKind == AsyncMethodKind::TaskReturning;
+        return asyncKind == AsyncMethodKind::Async2VariantThunk ||
+            asyncKind == AsyncMethodKind::Async2VariantImpl;
     }
 
-    // We use "async2" for runtime async methods that return "Unwrapped" values (i.e. T instead of Task<T>)
-    // The type of promise is typically captured in a modreq.
-    // CONSIDER: We probably need a better name for the concept, but it is hard to beat shortness of "async2"
-    inline bool IsAsync2Method() const
+    // Is this a small(ish) synthetic Task/async2 adapter to an async2/Task implementation?
+    // If yes, the method has another variant, which has the actual user-defined method body.
+    // Depending on whether user defined method was runtime async or not, the corresponding thunk
+    // will be an ordinary or async2 variant.
+    inline bool IsAsyncThunkMethod() const
+    {
+        LIMITED_METHOD_DAC_CONTRACT;
+        if (!HasAsyncMethodData())
+            return false;
+
+        auto asyncType = GetAddrOfAsyncMethodData()->kind;
+        return asyncType == AsyncMethodKind::Async2VariantThunk ||
+            asyncType == AsyncMethodKind::RuntimeAsync;
+    }
+
+    inline bool IsTaskReturningMethod() const
     {
         LIMITED_METHOD_DAC_CONTRACT;
         if (!HasAsyncMethodData())
             return false;
         auto asyncKind = GetAddrOfAsyncMethodData()->kind;
-        return asyncKind == AsyncMethodKind::AsyncThunkHelper ||
-            asyncKind == AsyncMethodKind::AsyncImplHelper ||
-            asyncKind == AsyncMethodKind::AsyncImplExplicit;
+        return asyncKind == AsyncMethodKind::RuntimeAsync ||
+            asyncKind == AsyncMethodKind::TaskReturning;
     }
 
     inline bool IsStructMethodOperatingOnCopy()
@@ -1868,19 +1909,7 @@ class MethodDesc
 
         // Only async2 methods backed by actual user code operate on copies.
         // Thunks with runtime-supplied implementation do not.
-        return GetAddrOfAsyncMethodData()->kind == AsyncMethodKind::AsyncImplHelper;
-    }
-
-    // The method is a synthetic Task/async2 adapter to an async2/Task implementation
-    inline bool IsAsyncThunkMethod() const
-    {
-        LIMITED_METHOD_DAC_CONTRACT;
-        if (!HasAsyncMethodData())
-            return false;
-
-        auto asyncType = GetAddrOfAsyncMethodData()->kind;
-        return asyncType == AsyncMethodKind::AsyncThunkHelper ||
-            asyncType == AsyncMethodKind::RuntimeAsync;
+        return GetAddrOfAsyncMethodData()->kind == AsyncMethodKind::Async2VariantImpl;
     }
 
     inline bool HasAsyncMethodData() const
@@ -3822,7 +3851,7 @@ ReadyToRunStandaloneMethodMetadata* GetReadyToRunStandaloneMethodMetadata(Method
 void InitReadyToRunStandaloneMethodMetadata();
 #endif // FEATURE_READYTORUN
 
-AsyncTaskMethod ClassifyAsyncMethod(SigPointer sig, Module* pModule, ULONG* offsetOfAsyncDetails, bool *pIsValueType);
+AsyncMethodSignatureKind ClassifyAsyncMethodSignature(SigPointer sig, Module* pModule, ULONG* offsetOfAsyncDetails, bool *pIsValueType);
 
 #include "method.inl"
 
diff --git a/src/coreclr/vm/methoditer.cpp b/src/coreclr/vm/methoditer.cpp
index 1241941da4f1d8..681cdb9e73af15 100644
--- a/src/coreclr/vm/methoditer.cpp
+++ b/src/coreclr/vm/methoditer.cpp
@@ -57,7 +57,7 @@ BOOL LoadedMethodDescIterator::Next(
         return FALSE;
     }
 
-    if (m_fIsAsyncThunk)
+    if (m_fIsAsync2Variant)
     {
         m_mainMD = m_mainMD->GetMethodTable()->GetParallelMethodDesc(m_mainMD, AsyncVariantLookup::AsyncOtherVariant);
 
@@ -147,7 +147,7 @@ BOOL LoadedMethodDescIterator::Next(
             goto ADVANCE_METHOD;
         if (m_methodIteratorEntry->GetMethod()->GetMemberDef() != m_md)
             goto ADVANCE_METHOD;
-        if (m_methodIteratorEntry->GetMethod()->IsAsyncHelperMethod() != m_fIsAsyncThunk)
+        if (m_methodIteratorEntry->GetMethod()->IsAsync2VariantMethod() != m_fIsAsync2Variant)
             goto ADVANCE_METHOD;
     }
     else if (m_startedNonGenericMethod)
@@ -216,7 +216,7 @@ LoadedMethodDescIterator::Start(
     AppDomain * pAppDomain,
     Module *pModule,
     mdMethodDef md,
-    bool fIsAsyncThunk,
+    bool fIsAsync2Variant,
     AssemblyIterationFlags assemblyIterationFlags)
 {
     CONTRACTL
@@ -228,7 +228,7 @@ LoadedMethodDescIterator::Start(
     }
     CONTRACTL_END;
 
-    m_fIsAsyncThunk = fIsAsyncThunk;
+    m_fIsAsync2Variant = fIsAsync2Variant;
 
     m_assemIterationFlags = assemblyIterationFlags;
     m_mainMD = NULL;
@@ -250,7 +250,7 @@ LoadedMethodDescIterator::Start(
     mdMethodDef     md,
     MethodDesc      *pMethodDesc)
 {
-    Start(pAppDomain, pModule, md, pMethodDesc->IsAsyncHelperMethod());
+    Start(pAppDomain, pModule, md, pMethodDesc->IsAsync2VariantMethod());
     m_mainMD = pMethodDesc;
 }
 
@@ -261,5 +261,5 @@ LoadedMethodDescIterator::LoadedMethodDescIterator(void)
     m_module = NULL;
     m_md = mdTokenNil;
     m_pAppDomain = NULL;
-    m_fIsAsyncThunk = false;
+    m_fIsAsync2Variant = false;
 }
diff --git a/src/coreclr/vm/methoditer.h b/src/coreclr/vm/methoditer.h
index 6d77a0dcf7932a..2a0ddf8f83f13f 100644
--- a/src/coreclr/vm/methoditer.h
+++ b/src/coreclr/vm/methoditer.h
@@ -34,7 +34,7 @@ class LoadedMethodDescIterator
     mdMethodDef  m_md;
     MethodDesc * m_mainMD;
     AppDomain *  m_pAppDomain;
-    bool         m_fIsAsyncThunk;
+    bool         m_fIsAsync2Variant;
 
 
     // The following hold the state of the iteration....
@@ -70,7 +70,7 @@ class LoadedMethodDescIterator
     void Start(AppDomain * pAppDomain,
                Module *pModule,
                mdMethodDef md,
-               bool fIsAsyncThunk,
+               bool fIsAsync2Variant,
                AssemblyIterationFlags assemIterationFlags = (AssemblyIterationFlags)(kIncludeLoaded | kIncludeExecution));
     void Start(AppDomain * pAppDomain, Module *pModule, mdMethodDef md, MethodDesc *pDesc);
 
@@ -78,7 +78,7 @@ class LoadedMethodDescIterator
         AppDomain * pAppDomain,
         Module *pModule,
         mdMethodDef md,
-        bool fIsAsyncThunk,
+        bool fIsAsync2Variant,
         AssemblyIterationFlags assemblyIterationFlags = (AssemblyIterationFlags)(kIncludeLoaded | kIncludeExecution))
     {
         LIMITED_METHOD_CONTRACT;
diff --git a/src/coreclr/vm/methodtable.cpp b/src/coreclr/vm/methodtable.cpp
index b5646443d038c8..dd1ccdc074d547 100644
--- a/src/coreclr/vm/methodtable.cpp
+++ b/src/coreclr/vm/methodtable.cpp
@@ -7779,7 +7779,7 @@ namespace
             MethodDesc* pMD = it.GetMethodDesc();
             if (pMD->GetMemberDef() == tkMethod
                 && pMD->GetModule() == mod
-                && pMD->IsAsyncHelperMethod() == pDefMD->IsAsyncHelperMethod())
+                && pMD->IsAsync2VariantMethod() == pDefMD->IsAsync2VariantMethod())
             {
                 return pMD;
             }
@@ -7810,11 +7810,11 @@ MethodDesc* MethodTable::GetParallelMethodDesc(MethodDesc* pDefMD, AsyncVariantL
     }
     else
     {
-        // Slow path for finding the asyncThunk (or not the AsyncThunk)
+        // Slow path for finding the Async2 variant (or not-Async2 variant, if we start from Async2 one)
         // This could be optimized with some trickery around slot numbers, but doing so is ... confusing, so I'm not implementing this yet
         mdMethodDef tkMethod = pDefMD->GetMemberDef();
         Module* mod = pDefMD->GetModule();
-        bool isAsyncHelperMethod = pDefMD->IsAsyncHelperMethod();
+        bool isAsync2VariantMethod = pDefMD->IsAsync2VariantMethod();
 
         MethodTable::IntroducedMethodIterator it(this);
         for (; it.IsValid(); it.Next())
@@ -7822,7 +7822,7 @@ MethodDesc* MethodTable::GetParallelMethodDesc(MethodDesc* pDefMD, AsyncVariantL
             MethodDesc* pMD = it.GetMethodDesc();
             if (pMD->GetMemberDef() == tkMethod
                 && pMD->GetModule() == mod
-                && pMD->IsAsyncHelperMethod() != isAsyncHelperMethod)
+                && pMD->IsAsync2VariantMethod() != isAsync2VariantMethod)
             {
                 return pMD;
             }
@@ -8211,7 +8211,7 @@ MethodTable::TryResolveVirtualStaticMethodOnThisType(MethodTable* pInterfaceType
         {
             if (pMethodDecl->GetMemberDef() == pInterfaceMD->GetMemberDef() && 
                 pMethodDecl->GetModule() == pInterfaceMD->GetModule() &&
-                pMethodDecl->IsAsyncHelperMethod() != pInterfaceMD->IsAsyncHelperMethod())
+                pMethodDecl->IsAsync2VariantMethod() != pInterfaceMD->IsAsync2VariantMethod())
             {
                 differsByAsyncVariant = true;
                 pMethodDecl = pMethodDecl->GetAsyncOtherVariant();
diff --git a/src/coreclr/vm/methodtablebuilder.cpp b/src/coreclr/vm/methodtablebuilder.cpp
index b16ed3732cbcca..364faa59c93a1a 100644
--- a/src/coreclr/vm/methodtablebuilder.cpp
+++ b/src/coreclr/vm/methodtablebuilder.cpp
@@ -992,7 +992,7 @@ MethodTableBuilder::bmtRTMethod::bmtRTMethod(
     MethodDesc *    pMD)
     : m_pOwningType(pOwningType),
       m_pMD(pMD),
-      m_methodSig(pMD->IsAsyncHelperMethod()
+      m_methodSig(pMD->IsAsync2VariantMethod()
        ? MethodSignature(pMD->GetModule(),
                          pMD->GetMemberDef(),
                          pMD->GetSignature(),
@@ -2749,9 +2749,9 @@ bool IsTypeDefOrRefAByRefStruct(Module* pModule, mdToken tk)
     return false;
 }
 
-AsyncTaskMethod ClassifyAsyncMethodCore(SigPointer sig, Module* pModule, PCCOR_SIGNATURE initialSig, ULONG* offsetOfAsyncDetails, bool *isValueTask);
+AsyncMethodSignatureKind ClassifyAsyncMethodSignatureCore(SigPointer sig, Module* pModule, PCCOR_SIGNATURE initialSig, ULONG* offsetOfAsyncDetails, bool *isValueTask);
 
-AsyncTaskMethod ClassifyAsyncMethod(SigPointer sig, Module* pModule, ULONG* offsetOfAsyncDetails, bool *isValueTask)
+AsyncMethodSignatureKind ClassifyAsyncMethodSignature(SigPointer sig, Module* pModule, ULONG* offsetOfAsyncDetails, bool *isValueTask)
 {
     PCCOR_SIGNATURE initialSig = sig.GetPtr();
     uint32_t data;
@@ -2764,10 +2764,10 @@ AsyncTaskMethod ClassifyAsyncMethod(SigPointer sig, Module* pModule, ULONG* offs
 
     // Return argument count
     IfFailThrow(sig.GetData(&data));
-    return ClassifyAsyncMethodCore(sig, pModule, initialSig, offsetOfAsyncDetails, isValueTask);
+    return ClassifyAsyncMethodSignatureCore(sig, pModule, initialSig, offsetOfAsyncDetails, isValueTask);
 }
 
-AsyncTaskMethod ClassifyAsyncMethodCore(SigPointer sig, Module* pModule, PCCOR_SIGNATURE initialSig, ULONG* offsetOfAsyncDetails, bool *pIsValueTask)
+AsyncMethodSignatureKind ClassifyAsyncMethodSignatureCore(SigPointer sig, Module* pModule, PCCOR_SIGNATURE initialSig, ULONG* offsetOfAsyncDetails, bool *pIsValueTask)
 {
     uint32_t data;
     bool dummy = false;
@@ -2829,14 +2829,14 @@ AsyncTaskMethod ClassifyAsyncMethodCore(SigPointer sig, Module* pModule, PCCOR_S
                     case ELEMENT_TYPE_SZARRAY:
                     case ELEMENT_TYPE_VAR:
                     case ELEMENT_TYPE_MVAR:
-                        return AsyncTaskMethod::Async2Method;
+                        return AsyncMethodSignatureKind::Async2Method;
                     case ELEMENT_TYPE_TYPEDBYREF:
                         ThrowHR(COR_E_BADIMAGEFORMAT);
                     case ELEMENT_TYPE_GENERICINST:
                         IfFailThrow(sig.GetElemType(&elemType));
                         if (elemType == ELEMENT_TYPE_CLASS)
                         {
-                            return AsyncTaskMethod::Async2Method;
+                            return AsyncMethodSignatureKind::Async2Method;
                         }
                         else
                         {
@@ -2847,7 +2847,7 @@ AsyncTaskMethod ClassifyAsyncMethodCore(SigPointer sig, Module* pModule, PCCOR_S
                             }
                             else
                             {
-                                return AsyncTaskMethod::Async2Method;
+                                return AsyncMethodSignatureKind::Async2Method;
                             }
                         }
                     default:
@@ -2859,7 +2859,7 @@ AsyncTaskMethod ClassifyAsyncMethodCore(SigPointer sig, Module* pModule, PCCOR_S
                 *pIsValueTask = name[0] == 'V';
                 if (elemType == ELEMENT_TYPE_VOID)
                 {
-                    return AsyncTaskMethod::Async2MethodNonGeneric;
+                    return AsyncMethodSignatureKind::Async2MethodNonGeneric;
                 }
                 else
                 {
@@ -2885,7 +2885,7 @@ AsyncTaskMethod ClassifyAsyncMethodCore(SigPointer sig, Module* pModule, PCCOR_S
             if ((strcmp(name, *pIsValueTask ? "ValueTask`1" : "Task`1") == 0) && strcmp(_namespace, "System.Threading.Tasks") == 0)
             {
                 if (IsTypeDefOrRefImplementedInSystemModule(pModule, tk))
-                    return AsyncTaskMethod::TaskReturningMethod;
+                    return AsyncMethodSignatureKind::TaskReturningMethod;
             }
         }
     }
@@ -2898,11 +2898,11 @@ AsyncTaskMethod ClassifyAsyncMethodCore(SigPointer sig, Module* pModule, PCCOR_S
         if ((strcmp(name, *pIsValueTask ? "ValueTask" : "Task") == 0) && strcmp(_namespace, "System.Threading.Tasks") == 0)
         {
             if (IsTypeDefOrRefImplementedInSystemModule(pModule, tk))
-                return AsyncTaskMethod::TaskNonGenericReturningMethod;
+                return AsyncMethodSignatureKind::TaskNonGenericReturningMethod;
         }
     }
 
-    return AsyncTaskMethod::NormalMethod;
+    return AsyncMethodSignatureKind::NormalMethod;
 }
 
 //---------------------------------------------------------------------------------------
@@ -3038,9 +3038,15 @@ MethodTableBuilder::EnumerateClassMethods()
         SigParser sig(pMemberSignature, cMemberSignature);
         ULONG offsetOfAsyncDetails = 0;
         bool isAsyncValueType = false;
-        AsyncTaskMethod asyncMethodType;
+        AsyncMethodSignatureKind asyncMethodType;
         
-        asyncMethodType = IsDelegate() ? AsyncTaskMethod::NormalMethod : ClassifyAsyncMethod(sig, GetModule(), &offsetOfAsyncDetails, &isAsyncValueType);
+        asyncMethodType = IsDelegate() ? AsyncMethodSignatureKind::NormalMethod : ClassifyAsyncMethodSignature(sig, GetModule(), &offsetOfAsyncDetails, &isAsyncValueType);
+
+        if (IsAsyncSigAsync2(asyncMethodType))
+        {
+            // not expected in actual metadata methods
+            BuildMethodTableThrowException(IDS_CLASSLOAD_BADFORMAT);
+        }
 
         bool hasGenericMethodArgsComputed = false;
         bool hasGenericMethodArgs = this->GetModule()->m_pMethodIsGenericMap->IsGeneric(tok, &hasGenericMethodArgsComputed);
@@ -3592,7 +3598,7 @@ MethodTableBuilder::EnumerateClassMethods()
                     type,
                     implType);
 
-                if (IsAsyncTaskMethodTaskReturningMethod(asyncMethodType))
+                if (IsAsyncSigTaskReturning(asyncMethodType))
                 {
                     // ordinary Task-returning method:
                     //    declare a TaskReturning method and add a helper thunk with Async2 signature
@@ -3604,13 +3610,18 @@ MethodTableBuilder::EnumerateClassMethods()
                 }
                 else
                 {
-                    _ASSERTE(IsAsyncTaskMethodNormal(asyncMethodType));
+                    _ASSERTE(IsAsyncSigNormal(asyncMethodType));
 
                     if (IsMiAsync(dwImplFlags))
                     {
-                        // TODO: explicitly-async methods have special semantics that is useful in the implementation of runtime async itself.
-                        //       It should not be valid to declare this outside of runtime infrastructure methods. (exact criteria TBD)
-                        pNewMethod->SetAsyncMethodKind(AsyncMethodKind::AsyncImplExplicit);
+                        // Explicitly-async methods have special semantics that is useful in the implementation of runtime async itself.
+                        // It should not be valid to declare such methods outside of runtime infrastructure methods.
+                        if (!IsTypeDefOrRefImplementedInSystemModule(GetModule(), this->GetCl()))
+                        {
+                            BuildMethodTableThrowException(IDS_CLASSLOAD_BADFORMAT);
+                        }
+
+                        pNewMethod->SetAsyncMethodKind(AsyncMethodKind::Async2ExplicitImpl);
                     }
                     else
                     {
@@ -3631,23 +3642,23 @@ MethodTableBuilder::EnumerateClassMethods()
                 ULONG newSuffixSize;
                 ULONG newPrefixSize;
 
-                if (asyncMethodType == AsyncTaskMethod::TaskNonGenericReturningMethod)
+                if (asyncMethodType == AsyncMethodSignatureKind::TaskNonGenericReturningMethod)
                 {
                     cAsyncThunkMemberSignature += 1;
                     originalTokenOffsetFromAsyncDetailsOffset = 1;
                     newTokenOffsetFromAsyncDetailsOffset = 1;
-                    asyncKind = IsMiAsync(dwImplFlags) ? AsyncMethodKind::AsyncImplHelper : AsyncMethodKind::AsyncThunkHelper;
+                    asyncKind = IsMiAsync(dwImplFlags) ? AsyncMethodKind::Async2VariantImpl : AsyncMethodKind::Async2VariantThunk;
                     originalPrefixSize = 1;
                     newPrefixSize = 1;
                     originalSuffixSize = 0;
                     newSuffixSize = 1;
                 }
-                else if (asyncMethodType == AsyncTaskMethod::TaskReturningMethod)
+                else if (asyncMethodType == AsyncMethodSignatureKind::TaskReturningMethod)
                 {
                     cAsyncThunkMemberSignature -= 2;
                     originalTokenOffsetFromAsyncDetailsOffset = 2;
                     newTokenOffsetFromAsyncDetailsOffset = 1;
-                    asyncKind = IsMiAsync(dwImplFlags)? AsyncMethodKind::AsyncImplHelper : AsyncMethodKind::AsyncThunkHelper;
+                    asyncKind = IsMiAsync(dwImplFlags)? AsyncMethodKind::Async2VariantImpl : AsyncMethodKind::Async2VariantThunk;
                     originalPrefixSize = 2;
                     newPrefixSize = 1;
                     originalSuffixSize = 1;
@@ -3674,7 +3685,7 @@ MethodTableBuilder::EnumerateClassMethods()
 
                 BYTE elemTypeClassOrValuetype = isAsyncValueType ? (BYTE)ELEMENT_TYPE_VALUETYPE : (BYTE)ELEMENT_TYPE_CLASS;
 
-                if (asyncMethodType == AsyncTaskMethod::TaskNonGenericReturningMethod)
+                if (asyncMethodType == AsyncMethodSignatureKind::TaskNonGenericReturningMethod)
                 {
                     // Incoming sig will look like ... E_T_CLASS/E_T_VALUETYPE <TokenOfTask>
                     // and needs to be translated to ELEMENT_TYPE_CMOD_REQD <TokenOfTask> E_T_VOID
@@ -3685,7 +3696,7 @@ MethodTableBuilder::EnumerateClassMethods()
                 }
                 else
                 {
-                    _ASSERTE(asyncMethodType == AsyncTaskMethod::TaskReturningMethod);
+                    _ASSERTE(asyncMethodType == AsyncMethodSignatureKind::TaskReturningMethod);
                     // Incoming sig will look something like ... E_T_GENERICINST E_T_CLASS/E_T_VALUETYPE <TokenOfTask> 1 E_T_I4 ....
                     // And needs to be translated to ELEMENT_TYPE_CMOD_REQD <TokenOfTask> E_T_I4
 
@@ -3724,7 +3735,7 @@ MethodTableBuilder::EnumerateClassMethods()
             }
 
             // Normal methods only insert a single method
-            if (IsAsyncTaskMethodNormal(asyncMethodType))
+            if (IsAsyncSigNormal(asyncMethodType))
             {
                 break;
             }
@@ -5934,7 +5945,7 @@ MethodTableBuilder::FindDeclMethodOnInterfaceEntry(bmtInterfaceEntry *pItfEntry,
             if ((slotDeclMethod->GetOwningType() == declRTMethod->GetOwningType()) &&
                 (slotDeclMethod->GetMethodDesc()->GetMethodTable() == declRTMethod->GetMethodDesc()->GetMethodTable()) &&
                 (slotDeclMethod->GetMethodDesc()->GetMemberDef() == declRTMethod->GetMethodDesc()->GetMemberDef()) &&
-                (slotDeclMethod->GetMethodDesc()->IsAsyncHelperMethod() != declRTMethod->GetMethodDesc()->IsAsyncHelperMethod()))
+                (slotDeclMethod->GetMethodDesc()->IsAsync2VariantMethod() != declRTMethod->GetMethodDesc()->IsAsync2VariantMethod()))
             {
                 declMethod = slotIt->Decl();
                 foundOtherVariant = true;
@@ -6669,11 +6680,11 @@ MethodTableBuilder::InitMethodDesc(
 
     if (asyncKind != AsyncMethodKind::NotAsync)
     {
-        AsyncMethodData* pThunkData = pNewMD->GetAddrOfAsyncMethodData();
-        pThunkData->kind = asyncKind;
-        if (asyncKind == AsyncMethodKind::AsyncThunkHelper || asyncKind == AsyncMethodKind::AsyncImplHelper)
+        AsyncMethodData* pAsyncMethodData = pNewMD->GetAddrOfAsyncMethodData();
+        pAsyncMethodData->kind = asyncKind;
+        if (asyncKind == AsyncMethodKind::Async2VariantThunk || asyncKind == AsyncMethodKind::Async2VariantImpl)
         {
-            pThunkData->sig = sig;
+            pAsyncMethodData->sig = sig;
         }
         else
         {
diff --git a/src/coreclr/vm/methodtablebuilder.h b/src/coreclr/vm/methodtablebuilder.h
index 1ab653c6921692..01899ef8152d25 100644
--- a/src/coreclr/vm/methodtablebuilder.h
+++ b/src/coreclr/vm/methodtablebuilder.h
@@ -673,7 +673,7 @@ class MethodTableBuilder
 
         //-----------------------------------------------------------------------------------------
         // This constructor can be used with hard-coded signatures that are used for
-        // representing async helper methods
+        // representing async2 variant methods
         MethodSignature(
             Module *             pModule,
             mdToken              tok,
@@ -1062,8 +1062,8 @@ class MethodTableBuilder
 
         bool IsAsyncThunk() const
         {
-            return GetAsyncMethodKind() == AsyncMethodKind::AsyncThunkHelper ||
-                GetAsyncMethodKind() == AsyncMethodKind::AsyncImplHelper;
+            return GetAsyncMethodKind() == AsyncMethodKind::Async2VariantThunk ||
+                GetAsyncMethodKind() == AsyncMethodKind::Async2VariantImpl;
         }
 
         bool IsAsync2() const
diff --git a/src/coreclr/vm/multicorejit.cpp b/src/coreclr/vm/multicorejit.cpp
index a56aa14a3319b3..15393e28f16b89 100644
--- a/src/coreclr/vm/multicorejit.cpp
+++ b/src/coreclr/vm/multicorejit.cpp
@@ -398,9 +398,9 @@ HRESULT MulticoreJitRecorder::WriteOutput(IStream * pStream)
         }
 
         MethodDesc * pMethod = m_JitInfoArray[i].GetMethodDescAndClean();
-        if (pMethod->IsAsyncHelperMethod())
+        if (pMethod->IsAsync2VariantMethod())
         {
-            // TODO consider adding support for async helpers in the future
+            // TODO consider adding support for async variants in the future
             skipped++;
             continue;
         }
diff --git a/src/coreclr/vm/prestub.cpp b/src/coreclr/vm/prestub.cpp
index cb6697dbd80688..f07ba9a987d466 100644
--- a/src/coreclr/vm/prestub.cpp
+++ b/src/coreclr/vm/prestub.cpp
@@ -735,7 +735,7 @@ namespace
         // For async method the methoddef represents a thunk with runtime-provided implementation,
         // while the default IL logically belongs to the implementation method desc.
         // If config returned no IL for an implementation method desc, then ask the method desc itself.
-        if (ilHeader == NULL && pMD->IsAsyncHelperMethod() && !pMD->IsAsyncThunkMethod())
+        if (ilHeader == NULL && pMD->IsAsync2VariantMethod() && !pMD->IsAsyncThunkMethod())
         {
             ilHeader = pMD->GetILHeader();
         }
@@ -2807,7 +2807,7 @@ PrepareCodeConfigBuffer::PrepareCodeConfigBuffer(NativeCodeVersion codeVersion)
 
 // CreateDerivedTargetSigWithExtraParams:
 // This method is used to create the signature of the target of the ILStub for
-// instantiating, unboxing, and async helper stubs, when/where we need to
+// instantiating, unboxing, and async variant stubs, when/where we need to
 // introduce a generic context/async continuation.
 // And since the generic context/async continuations are hidden parameters,
 // we're creating a signature that looks like non-generic but with additional
diff --git a/src/coreclr/vm/readytoruninfo.cpp b/src/coreclr/vm/readytoruninfo.cpp
index 072e7ada2f34c6..bcfb47d3b8d57e 100644
--- a/src/coreclr/vm/readytoruninfo.cpp
+++ b/src/coreclr/vm/readytoruninfo.cpp
@@ -962,7 +962,7 @@ static bool SigMatchesMethodDesc(MethodDesc* pMD, SigPointer &sig, ModuleBase *
 {
     STANDARD_VM_CONTRACT;
 
-    _ASSERTE(!pMD->IsAsyncHelperMethod());
+    _ASSERTE(!pMD->IsAsync2VariantMethod());
 
     ModuleBase *pOrigModule = pModule;
     ZapSig::Context    zapSigContext(pModule, (void *)pModule, ZapSig::NormalTokens);
@@ -1072,7 +1072,7 @@ bool ReadyToRunInfo::GetPgoInstrumentationData(MethodDesc * pMD, BYTE** pAllocat
     if (ReadyToRunCodeDisabled())
         return false;
 
-    if (pMD->IsAsyncHelperMethod())
+    if (pMD->IsAsync2VariantMethod())
         return false;
 
     if (m_pgoInstrumentationDataHashtable.IsNull())
@@ -1147,7 +1147,7 @@ PCODE ReadyToRunInfo::GetEntryPoint(MethodDesc * pMD, PrepareCodeConfig* pConfig
     if (ReadyToRunCodeDisabled())
         goto done;
 
-    if (pMD->IsAsyncHelperMethod())
+    if (pMD->IsAsync2VariantMethod())
         goto done;
 
     ETW::MethodLog::GetR2RGetEntryPointStart(pMD);
diff --git a/src/coreclr/vm/zapsig.cpp b/src/coreclr/vm/zapsig.cpp
index 8aef73bdf9aabc..748b9cfda2370f 100644
--- a/src/coreclr/vm/zapsig.cpp
+++ b/src/coreclr/vm/zapsig.cpp
@@ -921,7 +921,7 @@ MethodDesc *ZapSig::DecodeMethod(ModuleBase *pInfoModule,
     // in non-generic structs.
     BOOL isInstantiatingStub = (methodFlags & ENCODE_METHOD_SIG_InstantiatingStub);
     BOOL isUnboxingStub = (methodFlags & ENCODE_METHOD_SIG_UnboxingStub);
-    bool isAsyncHelper = (methodFlags & ENCODE_METHOD_SIG_AsyncHelper) != 0;
+    bool isAsync2Variant = (methodFlags & ENCODE_METHOD_SIG_Async2Variant) != 0;
 
     pMethod = MethodDesc::FindOrCreateAssociatedMethodDesc(pMethod, thOwner.GetMethodTable(),
                                                             isUnboxingStub,
@@ -929,7 +929,7 @@ MethodDesc *ZapSig::DecodeMethod(ModuleBase *pInfoModule,
                                                             !(isInstantiatingStub || isUnboxingStub) && !actualOwnerRequired,
                                                             actualOwnerRequired,
                                                             TRUE,
-                                                            isAsyncHelper == pMethod->IsAsyncHelperMethod() ? AsyncVariantLookup::MatchingAsyncVariant : AsyncVariantLookup::AsyncOtherVariant);
+                                                            isAsync2Variant == pMethod->IsAsync2VariantMethod() ? AsyncVariantLookup::MatchingAsyncVariant : AsyncVariantLookup::AsyncOtherVariant);
 
     if (methodFlags & ENCODE_METHOD_SIG_Constrained)
     {
@@ -1219,8 +1219,8 @@ BOOL ZapSig::EncodeMethod(
         methodFlags |= ENCODE_METHOD_SIG_InstantiatingStub;
     if (fMethodNeedsInstantiation)
         methodFlags |= ENCODE_METHOD_SIG_MethodInstantiation;
-    if (pMethod->IsAsyncHelperMethod())
-        methodFlags |= ENCODE_METHOD_SIG_AsyncHelper;
+    if (pMethod->IsAsync2VariantMethod())
+        methodFlags |= ENCODE_METHOD_SIG_Async2Variant;
 
     // Assume that the owner type is going to be needed
     methodFlags |= ENCODE_METHOD_SIG_OwnerType;
diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/MethodImplOptions.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/MethodImplOptions.cs
index e4ae987873b6a3..d9a25764e35774 100644
--- a/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/MethodImplOptions.cs
+++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/MethodImplOptions.cs
@@ -16,6 +16,7 @@ public enum MethodImplOptions
         PreserveSig = 0x0080,
         AggressiveInlining = 0x0100,
         AggressiveOptimization = 0x0200,
+        Async = 0x0400,
         InternalCall = 0x1000
     }
 }
diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.cs
index 481c61504b878d..5d8b8ccca9929d 100644
--- a/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.cs
+++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.cs
@@ -179,7 +179,7 @@ public static ReadOnlySpan<T> CreateSpan<T>(RuntimeFieldHandle fldHandle)
 #if !NATIVEAOT
         [Intrinsic]
         [BypassReadyToRun]
-        [MethodImpl(MethodImplOptions.NoInlining | (MethodImplOptions)0x0400)]  // NoInlining | Async
+        [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.Async]
         public static void AwaitAwaiterFromRuntimeAsync<TAwaiter>(TAwaiter awaiter) where TAwaiter : INotifyCompletion
         {
             ref RuntimeAsyncAwaitState state = ref t_runtimeAsyncAwaitState;
@@ -195,7 +195,7 @@ public static void AwaitAwaiterFromRuntimeAsync<TAwaiter>(TAwaiter awaiter) wher
         // recognized as an async2 call.
         [Intrinsic]
         [BypassReadyToRun]
-        [MethodImpl(MethodImplOptions.NoInlining | (MethodImplOptions)0x0400)]  // NoInlining | Async
+        [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.Async]
         public static void UnsafeAwaitAwaiterFromRuntimeAsync<TAwaiter>(TAwaiter awaiter) where TAwaiter : ICriticalNotifyCompletion
         {
             ref RuntimeAsyncAwaitState state = ref t_runtimeAsyncAwaitState;
@@ -211,7 +211,7 @@ public static void UnsafeAwaitAwaiterFromRuntimeAsync<TAwaiter>(TAwaiter awaiter
         // recognized as an async2 call.
         [Intrinsic]
         [BypassReadyToRun]
-        [MethodImpl(MethodImplOptions.NoInlining | (MethodImplOptions)0x0400)]  // NoInlining | Async
+        [MethodImpl(MethodImplOptions.Async]
         public static T Await<T>(Task<T> task)
         {
             TaskAwaiter<T> awaiter = task.GetAwaiter();
@@ -227,7 +227,7 @@ public static T Await<T>(Task<T> task)
         // recognized as an async2 call.
         [Intrinsic]
         [BypassReadyToRun]
-        [MethodImpl(MethodImplOptions.NoInlining | (MethodImplOptions)0x0400)]  // NoInlining | Async
+        [MethodImpl(MethodImplOptions.Async]
         public static void Await(Task task)
         {
             TaskAwaiter awaiter = task.GetAwaiter();
@@ -243,7 +243,7 @@ public static void Await(Task task)
         // recognized as an async2 call.
         [Intrinsic]
         [BypassReadyToRun]
-        [MethodImpl(MethodImplOptions.NoInlining | (MethodImplOptions)0x0400)]  // NoInlining | Async
+        [MethodImpl(MethodImplOptions.Async]
         public static T Await<T>(ValueTask<T> task)
         {
             ValueTaskAwaiter<T> awaiter = task.GetAwaiter();
@@ -259,7 +259,7 @@ public static T Await<T>(ValueTask<T> task)
         // recognized as an async2 call.
         [Intrinsic]
         [BypassReadyToRun]
-        [MethodImpl(MethodImplOptions.NoInlining | (MethodImplOptions)0x0400)]  // NoInlining | Async
+        [MethodImpl(MethodImplOptions.Async]
         public static void Await(ValueTask task)
         {
             ValueTaskAwaiter awaiter = task.GetAwaiter();
diff --git a/src/libraries/System.Runtime/ref/System.Runtime.cs b/src/libraries/System.Runtime/ref/System.Runtime.cs
index c8576764e170fe..c5879025b87244 100644
--- a/src/libraries/System.Runtime/ref/System.Runtime.cs
+++ b/src/libraries/System.Runtime/ref/System.Runtime.cs
@@ -13594,6 +13594,7 @@ public enum MethodImplOptions
         PreserveSig = 128,
         AggressiveInlining = 256,
         AggressiveOptimization = 512,
+        Async = 1024,
         InternalCall = 4096,
     }
     [System.AttributeUsageAttribute(System.AttributeTargets.Method, Inherited=false)]

From a0d704d23a47cf8210580e3eb02c89971820479f Mon Sep 17 00:00:00 2001
From: Vladimir Sadov <vsadov@microsoft.com>
Date: Sat, 15 Mar 2025 14:53:22 -0700
Subject: [PATCH 148/203] fix build (#3041)

---
 .../Runtime/CompilerServices/RuntimeHelpers.cs       | 12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)

diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.cs
index 5d8b8ccca9929d..93f9e01de1fa45 100644
--- a/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.cs
+++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.cs
@@ -179,7 +179,7 @@ public static ReadOnlySpan<T> CreateSpan<T>(RuntimeFieldHandle fldHandle)
 #if !NATIVEAOT
         [Intrinsic]
         [BypassReadyToRun]
-        [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.Async]
+        [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.Async)]
         public static void AwaitAwaiterFromRuntimeAsync<TAwaiter>(TAwaiter awaiter) where TAwaiter : INotifyCompletion
         {
             ref RuntimeAsyncAwaitState state = ref t_runtimeAsyncAwaitState;
@@ -195,7 +195,7 @@ public static void AwaitAwaiterFromRuntimeAsync<TAwaiter>(TAwaiter awaiter) wher
         // recognized as an async2 call.
         [Intrinsic]
         [BypassReadyToRun]
-        [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.Async]
+        [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.Async)]
         public static void UnsafeAwaitAwaiterFromRuntimeAsync<TAwaiter>(TAwaiter awaiter) where TAwaiter : ICriticalNotifyCompletion
         {
             ref RuntimeAsyncAwaitState state = ref t_runtimeAsyncAwaitState;
@@ -211,7 +211,7 @@ public static void UnsafeAwaitAwaiterFromRuntimeAsync<TAwaiter>(TAwaiter awaiter
         // recognized as an async2 call.
         [Intrinsic]
         [BypassReadyToRun]
-        [MethodImpl(MethodImplOptions.Async]
+        [MethodImpl(MethodImplOptions.Async)]
         public static T Await<T>(Task<T> task)
         {
             TaskAwaiter<T> awaiter = task.GetAwaiter();
@@ -227,7 +227,7 @@ public static T Await<T>(Task<T> task)
         // recognized as an async2 call.
         [Intrinsic]
         [BypassReadyToRun]
-        [MethodImpl(MethodImplOptions.Async]
+        [MethodImpl(MethodImplOptions.Async)]
         public static void Await(Task task)
         {
             TaskAwaiter awaiter = task.GetAwaiter();
@@ -243,7 +243,7 @@ public static void Await(Task task)
         // recognized as an async2 call.
         [Intrinsic]
         [BypassReadyToRun]
-        [MethodImpl(MethodImplOptions.Async]
+        [MethodImpl(MethodImplOptions.Async)]
         public static T Await<T>(ValueTask<T> task)
         {
             ValueTaskAwaiter<T> awaiter = task.GetAwaiter();
@@ -259,7 +259,7 @@ public static T Await<T>(ValueTask<T> task)
         // recognized as an async2 call.
         [Intrinsic]
         [BypassReadyToRun]
-        [MethodImpl(MethodImplOptions.Async]
+        [MethodImpl(MethodImplOptions.Async)]
         public static void Await(ValueTask task)
         {
             ValueTaskAwaiter awaiter = task.GetAwaiter();

From a6e563bfad199a11ad1586b859d15c44b69634cd Mon Sep 17 00:00:00 2001
From: Vladimir Sadov <vsadov@microsoft.com>
Date: Mon, 17 Mar 2025 19:25:05 -0700
Subject: [PATCH 149/203] [RuntimeAsync] Normalize the box type in resume stubs
 (#3042)

* normalize the box type in resume stubs

* Use manually allocated box objects to avoid extra instantiations

* Do not normalize and avoid more instantiations.

* commented failing scenarios and added a few more.

* comments/typos undo unnecessary change

* comment on manual boxing
---
 .../RuntimeHelpers.CoreCLR.cs                 | 23 +++++++++++-
 .../src/System/RuntimeHandles.cs              |  7 ++++
 src/coreclr/vm/corelib.h                      |  1 +
 src/coreclr/vm/jitinterface.cpp               | 31 ++++++++++++++--
 src/tests/async/shared-generic.cs             | 37 +++++++++++++++++++
 5 files changed, 93 insertions(+), 6 deletions(-)

diff --git a/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs
index 40ac3c5b5e3c2c..f35eb16591572c 100644
--- a/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs
+++ b/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs
@@ -623,6 +623,15 @@ private static unsafe Continuation AllocContinuationClass(Continuation prevConti
             return newContinuation;
         }
 
+        // We are allocating a box directly instead of relying on regular boxing because we want
+        // to store structs without changing layout, including nullables.
+        private static unsafe object AllocContinuationResultBox(void* ptr)
+        {
+            MethodTable* pMT = (MethodTable*)ptr;
+            Debug.Assert(pMT->IsValueType);
+            // We need no type/cctor checks since we will be storing an instance that already exists.
+            return RuntimeTypeHandle.InternalAllocNoChecks((MethodTable*)pMT);
+        }
         private struct RuntimeAsyncAwaitState
         {
             public Continuation? SentinelContinuation;
@@ -710,7 +719,12 @@ private static Continuation UnlinkHeadContinuation(out AwaitableProxy awaitableP
                     Debug.Assert(finalResult == finalContinuation);
                     if (IsReferenceOrContainsReferences<T>())
                     {
-                        return (T?)finalResult.GCData![0];
+                        if (typeof(T).IsValueType)
+                        {
+                            return Unsafe.As<byte, T>(ref finalResult.GCData![0]!.GetRawData());
+                        }
+
+                        return Unsafe.As<object, T>(ref finalResult.GCData![0]!);
                     }
                     else
                     {
@@ -771,7 +785,12 @@ private static async Task FinalizeTaskReturningThunk(Continuation continuation)
                     Debug.Assert(finalResult == finalContinuation);
                     if (IsReferenceOrContainsReferences<T>())
                     {
-                        return (T?)finalResult.GCData![0];
+                        if (typeof(T).IsValueType)
+                        {
+                            return Unsafe.As<byte, T>(ref finalResult.GCData![0]!.GetRawData());
+                        }
+
+                        return Unsafe.As<object, T>(ref finalResult.GCData![0]!);
                     }
                     else
                     {
diff --git a/src/coreclr/System.Private.CoreLib/src/System/RuntimeHandles.cs b/src/coreclr/System.Private.CoreLib/src/System/RuntimeHandles.cs
index 1d70bed0c21445..8e03b99165d557 100644
--- a/src/coreclr/System.Private.CoreLib/src/System/RuntimeHandles.cs
+++ b/src/coreclr/System.Private.CoreLib/src/System/RuntimeHandles.cs
@@ -299,6 +299,13 @@ internal static object InternalAlloc(RuntimeType type)
         [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "RuntimeTypeHandle_InternalAlloc")]
         private static unsafe partial void InternalAlloc(MethodTable* pMT, ObjectHandleOnStack result);
 
+        internal static object InternalAllocNoChecks(MethodTable* pMT)
+        {
+            object? result = null;
+            InternalAllocNoChecks(pMT, ObjectHandleOnStack.Create(ref result));
+            return result!;
+        }
+
         internal static object InternalAllocNoChecks(RuntimeType type)
         {
             Debug.Assert(!type.GetNativeTypeHandle().IsTypeDesc);
diff --git a/src/coreclr/vm/corelib.h b/src/coreclr/vm/corelib.h
index b386d97579c7c0..bde4e8c0c00bbe 100644
--- a/src/coreclr/vm/corelib.h
+++ b/src/coreclr/vm/corelib.h
@@ -686,6 +686,7 @@ DEFINE_METHOD(RUNTIME_HELPERS,      COPY_CONSTRUCT,         CopyConstruct, NoSig
 DEFINE_METHOD(RUNTIME_HELPERS,      ALLOC_CONTINUATION,        AllocContinuation, NoSig)
 DEFINE_METHOD(RUNTIME_HELPERS,      ALLOC_CONTINUATION_METHOD, AllocContinuationMethod, NoSig)
 DEFINE_METHOD(RUNTIME_HELPERS,      ALLOC_CONTINUATION_CLASS,  AllocContinuationClass, NoSig)
+DEFINE_METHOD(RUNTIME_HELPERS,      ALLOC_CONTINUATION_RESULT_BOX, AllocContinuationResultBox, SM_VoidPtr_RetObj)
 DEFINE_METHOD(RUNTIME_HELPERS,      FINALIZE_TASK_RETURNING_THUNK, FinalizeTaskReturningThunk, SM_Continuation_RetTask)
 DEFINE_METHOD(RUNTIME_HELPERS,      FINALIZE_TASK_RETURNING_THUNK_1, FinalizeTaskReturningThunk, GM_Continuation_RetTaskOfT)
 DEFINE_METHOD(RUNTIME_HELPERS,      FINALIZE_VALUETASK_RETURNING_THUNK, FinalizeValueTaskReturningThunk, SM_Continuation_RetValueTask)
diff --git a/src/coreclr/vm/jitinterface.cpp b/src/coreclr/vm/jitinterface.cpp
index e023efcf15a2e2..f35da35659dd63 100644
--- a/src/coreclr/vm/jitinterface.cpp
+++ b/src/coreclr/vm/jitinterface.cpp
@@ -14526,11 +14526,34 @@ CORINFO_METHOD_HANDLE CEEJitInfo::getAsyncResumptionStub()
             // Now we have the GC array. At the first index is the result.
             pCode->EmitLDC(0);
 
-            // Box the result.
-            pCode->EmitLDLOC(resultLoc);
-            pCode->EmitBOX(pCode->GetToken(resultTypeHnd));
+            // NOTE: that we are not using regular boxing (in EmitBOX sense) and allocate our own box instances via a helper.
+            // There are two reasons:
+            // - resultTypeHnd may be a nullable type and have different layout in boxed/unboxed forms.
+            //   We do not want to deal with that.
+            // - resultTypeHnd may contain __Canon fields. Regular boxing would not allow that, but this box is used for a very
+            //   specific internal purpose where we only require that the GC layout of the box matches the data
+            //   that we store in it, thus we want to allow __Canon.
+            if (resultTypeHnd.IsValueType())
+            {
+                // make a box and dup the ref
+                MethodDesc* md = CoreLibBinder::GetMethod(METHOD__RUNTIME_HELPERS__ALLOC_CONTINUATION_RESULT_BOX);
+                pCode->EmitLDC((DWORD_PTR)resultTypeHnd.AsMethodTable());
+                pCode->EmitCALL(pCode->GetToken(md), 1, 1);
+                pCode->EmitDUP();
+                // dst is the offset of the first field in the box
+                pCode->EmitLDFLDA(FIELD__RAW_DATA__DATA);
+                // load the result
+                pCode->EmitLDLOC(resultLoc);
+                // store into the box
+                pCode->EmitSTOBJ(pCode->GetToken(resultTypeHnd));
+            }
+            else
+            {
+                // load the result
+                pCode->EmitLDLOC(resultLoc);
+            }
 
-            // Finally store it.
+            // Store the result.
             pCode->EmitSTELEM_REF();
         }
         else
diff --git a/src/tests/async/shared-generic.cs b/src/tests/async/shared-generic.cs
index cafb18d1eab974..bacaafcb320c86 100644
--- a/src/tests/async/shared-generic.cs
+++ b/src/tests/async/shared-generic.cs
@@ -6,16 +6,51 @@
 using Xunit;
 public class Async2SharedGeneric
 {
+    public struct S0
+    {
+        public object _o;
+        public S0(object o) => _o = o;
+    }
+
+    public struct S1<T>
+    {
+        public T t;
+    }
+
     [Fact]
     public static void TestEntryPoint()
     {
+        // simple cases
         Async1EntryPoint<int>(typeof(int), 42).Wait();
         Async1EntryPoint<string>(typeof(string), "abc").Wait();
         Async1EntryPoint<object>(typeof(object), "def").Wait();
 
+        // struct with an obj and its nullable
+        Async1EntryPoint<S0>(typeof(S0), new S0(42)).Wait();
+        // TODO: uncomment to repro https://github.com/dotnet/runtimelab/issues/3043
+        // Async1EntryPoint<S0?>(typeof(S0?), new S0(42)).Wait();
+        Async1EntryPoint<S0?>(typeof(S0?), null).Wait();
+
+        // generic struct with an obj and its nullable
+        Async1EntryPoint<S1<string>>(typeof(S1<string>), new S1<string> { t = "ghj" }).Wait();
+        // TODO: uncomment to repro https://github.com/dotnet/runtimelab/issues/3043
+        // Async1EntryPoint<S1<string>?>(typeof(S1<string>?), new S1<string> { t = "qwe" }).Wait();
+        Async1EntryPoint<S1<string>?>(typeof(S1<string>?), null).Wait();
+
+        // simple cases
         Async2EntryPoint<int>(typeof(int), 142).Wait();
         Async2EntryPoint<string>(typeof(string), "ghi").Wait();
         Async2EntryPoint<object>(typeof(object), "jkl").Wait();
+
+        // struct with an obj and its nullable
+        Async2EntryPoint<S0>(typeof(S0), new S0(4242)).Wait();
+        Async2EntryPoint<S0?>(typeof(S0?), new S0(424242)).Wait();
+        Async2EntryPoint<S0?>(typeof(S0?), null).Wait();
+
+        // generic struct with an obj and its nullable
+        Async2EntryPoint<S1<string>>(typeof(S1<string>), new S1<string> { t = "kl" }).Wait();
+        Async2EntryPoint<S1<string>?>(typeof(S1<string>?), new S1<string> { t = "zx" }).Wait();
+        Async2EntryPoint<S1<string>?>(typeof(S1<string>?), null).Wait();
     }
 
     private static async Task Async1EntryPoint<T>(Type t, T value)
@@ -25,6 +60,7 @@ private static async Task Async1EntryPoint<T>(Type t, T value)
         await GenericClass<T>.StaticMethod<T>(t, t);
         await GenericClass<T>.StaticMethodAsync1(t);
         await GenericClass<T>.StaticMethodAsync1<T>(t, t);
+        Assert.Equal(value, value); // make sure we can compare value
         Assert.Equal(value, await GenericClass<T>.StaticReturnClassType(value));
         Assert.Equal(value, await GenericClass<T>.StaticReturnMethodType<T>(value));
         Assert.Equal(value, await GenericClass<T>.StaticReturnClassTypeAsync1(value));
@@ -38,6 +74,7 @@ private static async2 Task Async2EntryPoint<T>(Type t, T value)
         await GenericClass<T>.StaticMethod<T>(t, t);
         await GenericClass<T>.StaticMethodAsync1(t);
         await GenericClass<T>.StaticMethodAsync1<T>(t, t);
+        Assert.Equal(value, value); // make sure we can compare value
         Assert.Equal(value, await GenericClass<T>.StaticReturnClassType(value));
         Assert.Equal(value, await GenericClass<T>.StaticReturnMethodType<T>(value));
         Assert.Equal(value, await GenericClass<T>.StaticReturnClassTypeAsync1(value));

From 76f1d171ff886f6711587a30e8b9ce0c35f66327 Mon Sep 17 00:00:00 2001
From: Vladimir Sadov <vsadov@microsoft.com>
Date: Fri, 21 Mar 2025 06:58:57 -0700
Subject: [PATCH 150/203] Made Fibonacci tests to actually run Fibonacci
 algorithm (#3050)

---
 .../async/fibonacci-with-yields-toplevel.cs   | 63 ------------------
 .../fibonacci-with-yields-toplevel.csproj     | 10 ---
 src/tests/async/fibonacci-with-yields.cs      | 60 +++++++----------
 .../fibonacci-with-yields_struct_return.cs    | 63 ++++++++++++++++++
 ...ibonacci-with-yields_struct_return.csproj} |  2 -
 src/tests/async/fibonacci-without-yields.cs   | 58 +++++++----------
 src/tests/async/syncfibonacci-with-yields.cs  | 60 -----------------
 .../async/syncfibonacci-without-yields.cs     | 52 ++++++++-------
 .../taskbased-asyncfibonacci-with-yields.cs   | 63 +++++++++---------
 ...taskbased-asyncfibonacci-without-yields.cs | 57 ++++++++--------
 ...luetaskbased-asyncfibonacci-with-yields.cs | 65 ++++++++++---------
 ...taskbased-asyncfibonacci-without-yields.cs | 59 +++++++++--------
 12 files changed, 267 insertions(+), 345 deletions(-)
 delete mode 100644 src/tests/async/fibonacci-with-yields-toplevel.cs
 delete mode 100644 src/tests/async/fibonacci-with-yields-toplevel.csproj
 create mode 100644 src/tests/async/fibonacci-with-yields_struct_return.cs
 rename src/tests/async/{syncfibonacci-with-yields.csproj => fibonacci-with-yields_struct_return.csproj} (69%)
 delete mode 100644 src/tests/async/syncfibonacci-with-yields.cs

diff --git a/src/tests/async/fibonacci-with-yields-toplevel.cs b/src/tests/async/fibonacci-with-yields-toplevel.cs
deleted file mode 100644
index ad1762fbdee49b..00000000000000
--- a/src/tests/async/fibonacci-with-yields-toplevel.cs
+++ /dev/null
@@ -1,63 +0,0 @@
-// 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 System.Threading.Tasks;
-using System.Diagnostics;
-using Xunit;
-
-const uint Threshold = 1_000;
-bool done = false;
-const int iterations = 1;
-
-async Task AsyncEntry()
-{
-    for (int i = 0; i < iterations && !done; i++)
-    {
-        var sw = new Stopwatch();
-        sw.Start();
-        uint result = await A(100_000_000);
-        Console.WriteLine($"{sw.ElapsedMilliseconds} ms result={result}");
-    }
-}
-
-async2 Task<uint> A(uint n)
-{
-    uint result = n;
-    for (uint i = 0; i < n && !done; i++)
-        result = await B(result);
-    return result;
-}
-
-async2 Task<uint> B(uint n)
-{
-    uint result = n;
-
-    result = result * 1_999_999_981;
-    if (result < Threshold)
-    {
-        await Task.Yield();
-    }
-
-    result = result * 1_999_999_981;
-    if (result < Threshold)
-    {
-        await Task.Yield();
-    }
-
-    result = result * 1_999_999_981;
-    if (result < Threshold)
-    {
-        await Task.Yield();
-    }
-
-    return result;
-}
-
-int Test()
-{
-    AsyncEntry().Wait();
-    return 100;
-}
-
-return Test();
diff --git a/src/tests/async/fibonacci-with-yields-toplevel.csproj b/src/tests/async/fibonacci-with-yields-toplevel.csproj
deleted file mode 100644
index 14c1fed92f82b6..00000000000000
--- a/src/tests/async/fibonacci-with-yields-toplevel.csproj
+++ /dev/null
@@ -1,10 +0,0 @@
-<Project Sdk="Microsoft.NET.Sdk">
-  <PropertyGroup>
-    <!-- Needed for top level statements -->
-    <RequiresProcessIsolation>True</RequiresProcessIsolation>
-    <Optimize>True</Optimize>
-  </PropertyGroup>
-  <ItemGroup>
-    <Compile Include="$(MSBuildProjectName).cs" />
-  </ItemGroup>
-</Project>
diff --git a/src/tests/async/fibonacci-with-yields.cs b/src/tests/async/fibonacci-with-yields.cs
index 9a8e9b505a55c1..a51a834fbdd773 100644
--- a/src/tests/async/fibonacci-with-yields.cs
+++ b/src/tests/async/fibonacci-with-yields.cs
@@ -6,59 +6,49 @@
 using System.Diagnostics;
 using Xunit;
 
-public class Async2FibonacceWithYields
+public class Async2FibonacciWithYields
 {
-    const uint Threshold = 1_000;
-    static bool done;
-    const int iterations = 1;
+    const int iterations = 3;
+    const bool doYields = true;
 
     [Fact]
     public static void Test()
     {
-        AsyncEntry().Wait();
+        long allocated = GC.GetTotalAllocatedBytes(precise: true);
+
+        AsyncEntry().GetAwaiter().GetResult();
+
+        allocated = GC.GetTotalAllocatedBytes(precise: true) - allocated;
+        System.Console.WriteLine("allocated: " + allocated);
     }
 
-    public static async Task AsyncEntry()
+    public static async2 Task AsyncEntry()
     {
-        for (int i = 0; i < iterations && !done; i++)
+        for (int i = 0; i < iterations; i++)
         {
-            var sw = new Stopwatch();
-            sw.Start();
-            uint result = await A(100_000_000);
+            var sw = Stopwatch.StartNew();
+            int result = await Fib(25);
+            sw.Stop();
+
             Console.WriteLine($"{sw.ElapsedMilliseconds} ms result={result}");
         }
     }
 
-    static async2 Task<uint> A(uint n)
-    {
-        uint result = n;
-        for (uint i = 0; i < n && !done; i++)
-            result = await B(result);
-        return result;
-    }
-
-    static async2 Task<uint> B(uint n)
+    static async2 Task<int> Fib(int i)
     {
-        uint result = n;
-
-        result = result * 1_999_999_981;
-        if (result < Threshold)
+        if (i <= 1)
         {
-            await Task.Yield();
-        }
+            if (doYields)
+            {
+                await Task.Yield();
+            }
 
-        result = result * 1_999_999_981;
-        if (result < Threshold)
-        {
-            await Task.Yield();
+            return 1;
         }
 
-        result = result * 1_999_999_981;
-        if (result < Threshold)
-        {
-            await Task.Yield();
-        }
+        int i1 = await Fib(i - 1);
+        int i2 = await Fib(i - 2);
 
-        return result;
+        return i1 + i2;
     }
 }
diff --git a/src/tests/async/fibonacci-with-yields_struct_return.cs b/src/tests/async/fibonacci-with-yields_struct_return.cs
new file mode 100644
index 00000000000000..eee23a2c16245b
--- /dev/null
+++ b/src/tests/async/fibonacci-with-yields_struct_return.cs
@@ -0,0 +1,63 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+#define WITH_OBJECT
+
+using System;
+using System.Threading.Tasks;
+using System.Diagnostics;
+using Xunit;
+
+public class Async2FibonacceWithYields
+{
+    const int iterations = 3;
+
+    [Fact]
+    public static void Test()
+    {
+        long allocated = GC.GetTotalAllocatedBytes(precise: true);
+
+        AsyncEntry().GetAwaiter().GetResult();
+
+        allocated = GC.GetTotalAllocatedBytes(precise: true) - allocated;
+        System.Console.WriteLine("allocated: " + allocated);
+    }
+
+    public struct MyInt
+    {
+        public int i;
+#if WITH_OBJECT
+        public object dummy;
+#else
+        IntPtr dummy;
+#endif
+        public MyInt(int i) => this.i = i;
+    }
+
+    public static async2 Task AsyncEntry()
+    {
+        for (int i = 0; i < iterations; i++)
+        {
+            var sw = Stopwatch.StartNew();
+            MyInt result = await Fib(new MyInt(25));
+            sw.Stop();
+
+            Console.WriteLine($"{sw.ElapsedMilliseconds} ms result={result.i}");
+        }
+    }
+
+    static async2 Task<MyInt> Fib(MyInt n)
+    {
+        int i = n.i;
+        if (i <= 1)
+        {
+            await Task.Yield();
+            return new MyInt(1);
+        }
+
+        int i1 = (await Fib(new MyInt(i - 1))).i;
+        int i2 = (await Fib(new MyInt(i - 2))).i;
+
+        return new MyInt(i1 + i2);
+    }
+}
diff --git a/src/tests/async/syncfibonacci-with-yields.csproj b/src/tests/async/fibonacci-with-yields_struct_return.csproj
similarity index 69%
rename from src/tests/async/syncfibonacci-with-yields.csproj
rename to src/tests/async/fibonacci-with-yields_struct_return.csproj
index a7c0f53acc0cb4..de6d5e08882e86 100644
--- a/src/tests/async/syncfibonacci-with-yields.csproj
+++ b/src/tests/async/fibonacci-with-yields_struct_return.csproj
@@ -1,8 +1,6 @@
 <Project Sdk="Microsoft.NET.Sdk">
   <PropertyGroup>
     <Optimize>True</Optimize>
-    <!-- This is a manual test/benchmark -->
-    <CLRTestKind>BuildOnly</CLRTestKind>
   </PropertyGroup>
   <ItemGroup>
     <Compile Include="$(MSBuildProjectName).cs" />
diff --git a/src/tests/async/fibonacci-without-yields.cs b/src/tests/async/fibonacci-without-yields.cs
index fc1d1fd7f1f087..77ff71e8d1ee36 100644
--- a/src/tests/async/fibonacci-without-yields.cs
+++ b/src/tests/async/fibonacci-without-yields.cs
@@ -6,59 +6,49 @@
 using System.Diagnostics;
 using Xunit;
 
-public class Async2FibonacceWithoutYields
+public class Async2FibonacciWithoutYields
 {
-    const uint Threshold = 1_000;
-    static bool doYields = false;
-    const int iterations = 1;
+    const int iterations = 3;
+    const bool doYields = false;
 
     [Fact]
     public static void Test()
     {
-        AsyncEntry().Wait();
+        long allocated = GC.GetTotalAllocatedBytes(precise: true);
+
+        AsyncEntry().GetAwaiter().GetResult();
+
+        allocated = GC.GetTotalAllocatedBytes(precise: true) - allocated;
+        System.Console.WriteLine("allocated: " + allocated);
     }
 
-    public static async Task AsyncEntry()
+    public static async2 Task AsyncEntry()
     {
         for (int i = 0; i < iterations; i++)
         {
-            var sw = new Stopwatch();
-            sw.Start();
-            uint result = await A(100_000_000);
+            var sw = Stopwatch.StartNew();
+            int result = await Fib(25);
+            sw.Stop();
+
             Console.WriteLine($"{sw.ElapsedMilliseconds} ms result={result}");
         }
     }
 
-    static async2 Task<uint> A(uint n)
-    {
-        uint result = n;
-        for (uint i = 0; i < n; i++)
-            result = await B(result);
-        return result;
-    }
-
-    static async2 Task<uint> B(uint n)
+    static async2 Task<int> Fib(int i)
     {
-        uint result = n;
-
-        result = result * 1_999_999_981;
-        if ((result < Threshold) && doYields)
+        if (i <= 1)
         {
-            await Task.Yield();
-        }
+            if (doYields)
+            {
+                await Task.Yield();
+            }
 
-        result = result * 1_999_999_981;
-        if ((result < Threshold) && doYields)
-        {
-            await Task.Yield();
+            return 1;
         }
 
-        result = result * 1_999_999_981;
-        if ((result < Threshold) && doYields)
-        {
-            await Task.Yield();
-        }
+        int i1 = await Fib(i - 1);
+        int i2 = await Fib(i - 2);
 
-        return result;
+        return i1 + i2;
     }
 }
diff --git a/src/tests/async/syncfibonacci-with-yields.cs b/src/tests/async/syncfibonacci-with-yields.cs
deleted file mode 100644
index fa05d09572ef0e..00000000000000
--- a/src/tests/async/syncfibonacci-with-yields.cs
+++ /dev/null
@@ -1,60 +0,0 @@
-// 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 System.Diagnostics;
-using System.Threading;
-using System.Threading.Tasks;
-using System.Runtime.CompilerServices;
-using Xunit;
-
-public class SyncFibonacciWithYields
-{
-    const uint Threshold = 1_000;
-
-    public static int Main()
-    {
-        for (int i = 0; i < 10; i++)
-        {
-            var sw = new Stopwatch();
-            sw.Start();
-            uint result = A(100_000_000);
-            Console.WriteLine($"{sw.ElapsedMilliseconds} ms result={result}");
-        }
-
-        return 100;
-    }
-
-    static uint A(uint n)
-    {
-        uint result = n;
-        for (uint i = 0; i < n; i++)
-            result = B(result);
-        return result;
-    }
-
-    static uint B(uint n)
-    {
-        uint result = n;
-
-        result = result * 1_999_999_981;
-        if (result < Threshold)
-            MyYield();
-
-        result = result * 1_999_999_981;
-        if (result < Threshold)
-            MyYield();
-
-        result = result * 1_999_999_981;
-        if (result < Threshold)
-            MyYield();
-
-        return result;
-    }
-
-    // Workaround for inlining of Thread.Yield inflating the caller's frame.
-    [MethodImpl(MethodImplOptions.NoInlining)]
-    static void MyYield() => Thread.Yield();
-
-}
-
diff --git a/src/tests/async/syncfibonacci-without-yields.cs b/src/tests/async/syncfibonacci-without-yields.cs
index 1b40117c02419b..c6eeb9725d70f7 100644
--- a/src/tests/async/syncfibonacci-without-yields.cs
+++ b/src/tests/async/syncfibonacci-without-yields.cs
@@ -2,47 +2,49 @@
 // The .NET Foundation licenses this file to you under the MIT license.
 
 using System;
-﻿using System.Diagnostics;
-using System.Threading;
 using System.Threading.Tasks;
-using System.Runtime.CompilerServices;
+using System.Diagnostics;
 using Xunit;
 
-public class SyncFibonacciWithoutYields
+public class SyncFibonacci
 {
-    const uint Threshold = 1_000;
+    const int iterations = 3;
+    const bool doYields = false;
 
     public static int Main()
     {
-        for (int i = 0; i < 10; i++)
-        {
-            var sw = new Stopwatch();
-            sw.Start();
-            uint result = A(100_000_000);
-            Console.WriteLine($"{sw.ElapsedMilliseconds} ms result={result}");
-        }
+        long allocated = GC.GetTotalAllocatedBytes(precise: true);
+
+        Entry();
+
+        allocated = GC.GetTotalAllocatedBytes(precise: true) - allocated;
+        System.Console.WriteLine("allocated: " + allocated);
 
         return 100;
     }
 
-    static uint A(uint n)
+    public static void Entry()
     {
-        uint result = n;
-        for (uint i = 0; i < n; i++)
-            result = B(result);
-        return result;
+        for (int i = 0; i < iterations; i++)
+        {
+            var sw = Stopwatch.StartNew();
+            int result = Fib(25);
+            sw.Stop();
+
+            Console.WriteLine($"{sw.ElapsedMilliseconds} ms result={result}");
+        }
     }
 
-    static uint B(uint n)
+    static int Fib(int i)
     {
-        uint result = n;
-
-        result = result * 1_999_999_981;
-
-        result = result * 1_999_999_981;
+        if (i <= 1)
+        {
+            return 1;
+        }
 
-        result = result * 1_999_999_981;
+        int i1 = Fib(i - 1);
+        int i2 = Fib(i - 2);
 
-        return result;
+        return i1 + i2;
     }
 }
diff --git a/src/tests/async/taskbased-asyncfibonacci-with-yields.cs b/src/tests/async/taskbased-asyncfibonacci-with-yields.cs
index 11b131f959c92d..4829e029636da6 100644
--- a/src/tests/async/taskbased-asyncfibonacci-with-yields.cs
+++ b/src/tests/async/taskbased-asyncfibonacci-with-yields.cs
@@ -3,52 +3,53 @@
 
 using System;
 using System.Threading.Tasks;
-﻿using System.Diagnostics;
-using System.Runtime.CompilerServices;
+using System.Diagnostics;
 using Xunit;
 
 public class TaskBasedAsyncFibonacciWithYields
 {
-    const uint Threshold = 1_000;
+    const int iterations = 3;
+    const bool doYields = true;
 
-    public static int Main() { return AsyncMain().Result; }
-    
-    static async Task<int> AsyncMain()
+    public static int Main()
     {
-        for (int i = 0; i < 10; i++)
-        {
-            var sw = new Stopwatch();
-            sw.Start();
-            uint result = await A(100_000_000);
-            Console.WriteLine($"{sw.ElapsedMilliseconds} ms result={result}");
-        }
+        long allocated = GC.GetTotalAllocatedBytes(precise: true);
+
+        AsyncEntry().GetAwaiter().GetResult();
+
+        allocated = GC.GetTotalAllocatedBytes(precise: true) - allocated;
+        System.Console.WriteLine("allocated: " + allocated);
 
         return 100;
     }
 
-    static async Task<uint> A(uint n)
+    public static async Task AsyncEntry()
     {
-        uint result = n;
-        for (uint i = 0; i < n; i++)
-            result = await B(result);
-        return result;
-    }
+        for (int i = 0; i < iterations; i++)
+        {
+            var sw = Stopwatch.StartNew();
+            int result = await Fib(25);
+            sw.Stop();
 
-    static async Task<uint> B(uint n){
-        uint result = n;
+            Console.WriteLine($"{sw.ElapsedMilliseconds} ms result={result}");
+        }
+    }
 
-        result = result * 1_999_999_981;
-        if (result < Threshold)
-            await Task.Yield();
+    static async Task<int> Fib(int i)
+    {
+        if (i <= 1)
+        {
+            if (doYields)
+            {
+                await Task.Yield();
+            }
 
-        result = result * 1_999_999_981;
-        if (result < Threshold)
-            await Task.Yield();
+            return 1;
+        }
 
-        result = result * 1_999_999_981;
-        if (result < Threshold)
-            await Task.Yield();
+        int i1 = await Fib(i - 1);
+        int i2 = await Fib(i - 2);
 
-        return result;
+        return i1 + i2;
     }
 }
diff --git a/src/tests/async/taskbased-asyncfibonacci-without-yields.cs b/src/tests/async/taskbased-asyncfibonacci-without-yields.cs
index 6be313d1986dec..e3e9c7ff21f8f1 100644
--- a/src/tests/async/taskbased-asyncfibonacci-without-yields.cs
+++ b/src/tests/async/taskbased-asyncfibonacci-without-yields.cs
@@ -2,49 +2,54 @@
 // The .NET Foundation licenses this file to you under the MIT license.
 
 using System;
-﻿using System.Diagnostics;
-using System.Runtime.CompilerServices;
 using System.Threading.Tasks;
+using System.Diagnostics;
 using Xunit;
 
 public class TaskBasedAsyncFibonacciWithoutYields
 {
-    const uint Threshold = 1_000;
+    const int iterations = 3;
+    const bool doYields = false;
 
-    public static int Main() { return AsyncMain().Result; }
-    
-    static async Task<int> AsyncMain()
+    public static int Main()
     {
-        for (int i = 0; i < 10; i++)
-        {
-            var sw = new Stopwatch();
-            sw.Start();
-            uint result = await A(100_000_000);
-            Console.WriteLine($"{sw.ElapsedMilliseconds} ms result={result}");
-        }
+        long allocated = GC.GetTotalAllocatedBytes(precise: true);
+
+        AsyncEntry().GetAwaiter().GetResult();
+
+        allocated = GC.GetTotalAllocatedBytes(precise: true) - allocated;
+        System.Console.WriteLine("allocated: " + allocated);
 
         return 100;
     }
 
-    static async Task<uint> A(uint n)
+    public static async Task AsyncEntry()
     {
-        uint result = n;
-        for (uint i = 0; i < n; i++)
-            result = await B(result);
-        return result;
+        for (int i = 0; i < iterations; i++)
+        {
+            var sw = Stopwatch.StartNew();
+            int result = await Fib(25);
+            sw.Stop();
+
+            Console.WriteLine($"{sw.ElapsedMilliseconds} ms result={result}");
+        }
     }
 
-    #pragma warning disable CS1998
-    static async Task<uint> B(uint n)
+    static async Task<int> Fib(int i)
     {
-        uint result = n;
-
-        result = result * 1_999_999_981;
+        if (i <= 1)
+        {
+            if (doYields)
+            {
+                await Task.Yield();
+            }
 
-        result = result * 1_999_999_981;
+            return 1;
+        }
 
-        result = result * 1_999_999_981;
+        int i1 = await Fib(i - 1);
+        int i2 = await Fib(i - 2);
 
-        return result;
+        return i1 + i2;
     }
 }
diff --git a/src/tests/async/valuetaskbased-asyncfibonacci-with-yields.cs b/src/tests/async/valuetaskbased-asyncfibonacci-with-yields.cs
index a288db583b4fbe..ac48372814efb6 100644
--- a/src/tests/async/valuetaskbased-asyncfibonacci-with-yields.cs
+++ b/src/tests/async/valuetaskbased-asyncfibonacci-with-yields.cs
@@ -3,52 +3,53 @@
 
 using System;
 using System.Threading.Tasks;
-﻿using System.Diagnostics;
-using System.Runtime.CompilerServices;
+using System.Diagnostics;
 using Xunit;
 
-public class ValueTaskBasedAsyncFibonacciWithYields
+public class TaskBasedAsyncFibonacciWithYields
 {
-    const uint Threshold = 1_000;
+    const int iterations = 3;
+    const bool doYields = true;
 
-    public static int Main() { return AsyncMain().Result; }
-    
-    static async Task<int> AsyncMain()
+    public static int Main()
     {
-        for (int i = 0; i < 10; i++)
-        {
-            var sw = new Stopwatch();
-            sw.Start();
-            uint result = await A(100_000_000);
-            Console.WriteLine($"{sw.ElapsedMilliseconds} ms result={result}");
-        }
+        long allocated = GC.GetTotalAllocatedBytes(precise: true);
+
+        AsyncEntry().GetAwaiter().GetResult();
+
+        allocated = GC.GetTotalAllocatedBytes(precise: true) - allocated;
+        System.Console.WriteLine("allocated: " + allocated);
 
         return 100;
     }
 
-    static async ValueTask<uint> A(uint n)
+    public static async ValueTask AsyncEntry()
     {
-        uint result = n;
-        for (uint i = 0; i < n; i++)
-            result = await B(result);
-        return result;
-    }
+        for (int i = 0; i < iterations; i++)
+        {
+            var sw = Stopwatch.StartNew();
+            int result = await Fib(25);
+            sw.Stop();
 
-    static async ValueTask<uint> B(uint n){
-        uint result = n;
+            Console.WriteLine($"{sw.ElapsedMilliseconds} ms result={result}");
+        }
+    }
 
-        result = result * 1_999_999_981;
-        if (result < Threshold)
-            await Task.Yield();
+    static async ValueTask<int> Fib(int i)
+    {
+        if (i <= 1)
+        {
+            if (doYields)
+            {
+                await Task.Yield();
+            }
 
-        result = result * 1_999_999_981;
-        if (result < Threshold)
-            await Task.Yield();
+            return 1;
+        }
 
-        result = result * 1_999_999_981;
-        if (result < Threshold)
-            await Task.Yield();
+        int i1 = await Fib(i - 1);
+        int i2 = await Fib(i - 2);
 
-        return result;
+        return i1 + i2;
     }
 }
diff --git a/src/tests/async/valuetaskbased-asyncfibonacci-without-yields.cs b/src/tests/async/valuetaskbased-asyncfibonacci-without-yields.cs
index bdbb7b0b3647a2..1e3aab57ec7da5 100644
--- a/src/tests/async/valuetaskbased-asyncfibonacci-without-yields.cs
+++ b/src/tests/async/valuetaskbased-asyncfibonacci-without-yields.cs
@@ -2,49 +2,54 @@
 // The .NET Foundation licenses this file to you under the MIT license.
 
 using System;
-﻿using System.Diagnostics;
-using System.Runtime.CompilerServices;
 using System.Threading.Tasks;
+using System.Diagnostics;
 using Xunit;
 
-public class ValueTaskBasedAsyncFibonacciWithoutYields
+public class TaskBasedAsyncFibonacciWithoutYields
 {
-    const uint Threshold = 1_000;
+    const int iterations = 3;
+    const bool doYields = false;
 
-    public static int Main() { return AsyncMain().Result; }
-    
-    static async ValueTask<int> AsyncMain()
+    public static int Main()
     {
-        for (int i = 0; i < 10; i++)
-        {
-            var sw = new Stopwatch();
-            sw.Start();
-            uint result = await A(100_000_000);
-            Console.WriteLine($"{sw.ElapsedMilliseconds} ms result={result}");
-        }
+        long allocated = GC.GetTotalAllocatedBytes(precise: true);
+
+        AsyncEntry().GetAwaiter().GetResult();
+
+        allocated = GC.GetTotalAllocatedBytes(precise: true) - allocated;
+        System.Console.WriteLine("allocated: " + allocated);
 
         return 100;
     }
 
-    static async ValueTask<uint> A(uint n)
+    public static async ValueTask AsyncEntry()
     {
-        uint result = n;
-        for (uint i = 0; i < n; i++)
-            result = await B(result);
-        return result;
+        for (int i = 0; i < iterations; i++)
+        {
+            var sw = Stopwatch.StartNew();
+            int result = await Fib(25);
+            sw.Stop();
+
+            Console.WriteLine($"{sw.ElapsedMilliseconds} ms result={result}");
+        }
     }
 
-    #pragma warning disable CS1998
-    static async ValueTask<uint> B(uint n)
+    static async ValueTask<int> Fib(int i)
     {
-        uint result = n;
-
-        result = result * 1_999_999_981;
+        if (i <= 1)
+        {
+            if (doYields)
+            {
+                await Task.Yield();
+            }
 
-        result = result * 1_999_999_981;
+            return 1;
+        }
 
-        result = result * 1_999_999_981;
+        int i1 = await Fib(i - 1);
+        int i2 = await Fib(i - 2);
 
-        return result;
+        return i1 + i2;
     }
 }

From d3be3290d645e2f40ac03bd75dd5878eeb582c79 Mon Sep 17 00:00:00 2001
From: Vladimir Sadov <vsadov@microsoft.com>
Date: Wed, 26 Mar 2025 08:54:26 -0700
Subject: [PATCH 151/203] [RuntimeAsync] Recognizing new overloads of Await
 related to the ConfigureAwait pattern. (#3051)

* Add new helpers

* added test for ConfigureAwait(false)

* detecting and optimizing the ConfigAwait(0|1) pattern

* set test to 3 iterations by default

* Factor out Await pattern match as a separate method.
---
 src/coreclr/jit/compiler.h                    |   2 +
 src/coreclr/jit/importer.cpp                  | 122 +++++++++++++-----
 src/coreclr/jit/importercalls.cpp             |  11 ++
 src/coreclr/jit/namedintrinsiclist.h          |   2 +
 .../src/CompatibilitySuppressions.xml         |  24 ++++
 .../CompilerServices/RuntimeHelpers.cs        |  63 +++++++++
 .../src/System/Threading/Tasks/Future.cs      |   2 +
 .../src/System/Threading/Tasks/Task.cs        |   2 +
 .../src/System/Threading/Tasks/ValueTask.cs   |   2 +
 .../System.Runtime/ref/System.Runtime.cs      |   4 +
 .../fibonacci-without-yields-config-await.cs  |  57 ++++++++
 ...bonacci-without-yields-config-await.csproj |   8 ++
 12 files changed, 266 insertions(+), 33 deletions(-)
 create mode 100644 src/tests/async/fibonacci-without-yields-config-await.cs
 create mode 100644 src/tests/async/fibonacci-without-yields-config-await.csproj

diff --git a/src/coreclr/jit/compiler.h b/src/coreclr/jit/compiler.h
index 04ab89fe38e8bb..211ecc16a05b8f 100644
--- a/src/coreclr/jit/compiler.h
+++ b/src/coreclr/jit/compiler.h
@@ -4847,6 +4847,8 @@ class Compiler
 
     bool impMatchIsInstBooleanConversion(const BYTE* codeAddr, const BYTE* codeEndp, int* consumed);
 
+    bool impMatchAwaitPattern(const BYTE * codeAddr, const BYTE * codeEndp, int* configVal);
+
     GenTree* impCastClassOrIsInstToTree(
         GenTree* op1, GenTree* op2, CORINFO_RESOLVED_TOKEN* pResolvedToken, bool isCastClass, bool* booleanCheck, IL_OFFSET ilOffset);
 
diff --git a/src/coreclr/jit/importer.cpp b/src/coreclr/jit/importer.cpp
index ce161e13f7d47d..f8975f2815bccf 100644
--- a/src/coreclr/jit/importer.cpp
+++ b/src/coreclr/jit/importer.cpp
@@ -5976,6 +5976,85 @@ bool Compiler::impBlockIsInALoop(BasicBlock* block)
            block->HasFlag(BBF_BACKWARD_JUMP);
 }
 
+//------------------------------------------------------------------------
+// impMatchAwaitPattern: check if a method call starts an Await pattern
+//                       that can be optimized for runtime async
+//
+// Arguments:
+//   codeAddr - IL after call[virt]
+//   codeEndp - End of IL code stream
+//   configVal - [out] set to 0 or 1, accordingly, if we saw ConfigureAwait(0|1)
+//
+// Returns:
+//    true if this is an Await that we can optimize
+//
+bool Compiler::impMatchAwaitPattern(const BYTE* codeAddr, const BYTE* codeEndp, int* configVal)
+{
+    // If we see the following code pattern in runtime async methods:
+    //
+    //    call[virt] <Method>
+    //    [ OPTIONAL ]
+    //       ldc.i4.0 / ldc.i4.1
+    //       call[virt] <ConfigureAwait>
+    //    call       <Await>
+    //
+    // We emit an eqivalent of:
+    //
+    //    call[virt] <RtMethod>
+    //
+    //    where "RtMethod" is the runtime-async counterpart of a Task-returning method.
+    //
+    //  NOTE: we could potentially check if Method is not a thunk and, in cases when we can tell,
+    //        bypass this optimization. Otherwise in a non-thunk case we would be
+    //        replacing the pattern with a call to a thunk, which contains roughly the same code.
+
+    const BYTE* nextOpcode = codeAddr + sizeof(mdToken);
+    // There must be enough space after ldc for {call + tk + call + tk}
+    if (nextOpcode + 2 * (1 + sizeof(mdToken)) < codeEndp)
+    {
+        uint8_t nextOp     = getU1LittleEndian(nextOpcode);
+        uint8_t nextNextOp = getU1LittleEndian(nextOpcode + 1);
+        if ((nextOp != CEE_LDC_I4_0 && nextOp != CEE_LDC_I4_1) ||
+            (nextNextOp != CEE_CALL && nextNextOp != CEE_CALLVIRT))
+        {
+            goto checkForAwait;
+        }
+
+        // check if the token after {ldc, call[virt]} is ConfigAwait
+        CORINFO_RESOLVED_TOKEN nextCallTok;
+        impResolveToken(nextOpcode + 2, &nextCallTok, CORINFO_TOKENKIND_Method);
+
+        if (!eeIsIntrinsic(nextCallTok.hMethod) ||
+            lookupNamedIntrinsic(nextCallTok.hMethod) != NI_System_Threading_Tasks_Task_ConfigureAwait)
+        {
+            goto checkForAwait;
+        }
+
+        *configVal = nextOp == CEE_LDC_I4_0 ? 0 : 1;
+        // skip {ldc; call; <ConfigureAwait>}
+        nextOpcode += 1 + 1 + sizeof(mdToken);
+    }
+
+checkForAwait:
+
+    if ((nextOpcode + sizeof(mdToken) < codeEndp) && (getU1LittleEndian(nextOpcode) == CEE_CALL))
+    {
+        // resolve the next token
+        CORINFO_RESOLVED_TOKEN nextCallTok;
+        impResolveToken(nextOpcode + 1, &nextCallTok, CORINFO_TOKENKIND_Method);
+
+        // check if it is an Await intrinsic
+        if (eeIsIntrinsic(nextCallTok.hMethod) &&
+            lookupNamedIntrinsic(nextCallTok.hMethod) == NI_System_Runtime_CompilerServices_RuntimeHelpers_Await)
+        {
+            // yes, this is an Await
+            return true;
+        }
+    }
+
+    return false;
+}
+
 #ifdef _PREFAST_
 #pragma warning(push)
 #pragma warning(disable : 21000) // Suppress PREFast warning about overly large function
@@ -9013,40 +9092,13 @@ void Compiler::impImportBlockCode(BasicBlock* block)
                 if (opcode != CEE_CALLI)
                 {
                     bool isAwait = false;
-                    if (JitConfig.JitOptimizeAwait())
+                    // TODO: The configVal should be wired to the actual implementation
+                    //       that control the flow of sync context.
+                    //       We do not have that yet.
+                    int configVal= -1;  // -1 not congigured, 0/1 configured to false/true
+                    if (compIsAsync2() && JitConfig.JitOptimizeAwait())
                     {
-                        // If we see the following code pattern in runtime async methods:
-                        //
-                        //    call[virt] <Method>
-                        //    call       <Await>
-                        //
-                        //  we emit an eqivalent of
-                        //
-                        //    call[virt] <RtMethod>
-                        //
-                        //  where "RtMethod" is the runtime-async counterpart of a Task-returning method.
-                        //
-                        //  NOTE: we could potentially check if Method is not a thunk and, in cases when we can tell,
-                        //        bypass this optimization. Otherwise in a non-thunk case we would be
-                        //        replacing the pattern with a call to a thunk, which contains roughly the same code.
-
-                        const BYTE* nextOpcode = codeAddr + sizeof(mdToken);
-                        if (compIsAsync2() && (nextOpcode + sizeof(mdToken) < codeEndp) &&
-                            (getU1LittleEndian(nextOpcode) == CEE_CALL))
-                        {
-                            // resolve the next token
-                            CORINFO_RESOLVED_TOKEN nextCallTok;
-                            impResolveToken(nextOpcode + 1, &nextCallTok, CORINFO_TOKENKIND_Method);
-
-                            // check if it is an Await intrinsic
-                            if (eeIsIntrinsic(nextCallTok.hMethod) &&
-                                lookupNamedIntrinsic(nextCallTok.hMethod) ==
-                                    NI_System_Runtime_CompilerServices_RuntimeHelpers_Await)
-                            {
-                                // yes, this is an Await
-                                isAwait = true;
-                            }
-                        }
+                        isAwait = impMatchAwaitPattern(codeAddr, codeEndp, &configVal);
                     }
 
                     if (isAwait)
@@ -9055,6 +9107,10 @@ void Compiler::impImportBlockCode(BasicBlock* block)
                         if (resolvedToken.hMethod != NULL)
                         {
                             // There is a runtime async variant that is implicitly awaitable, just call that.
+                            // if configured, skip {ldc call ConfigureAwait}
+                            if (configVal >= 0)
+                                codeAddr += 2 + sizeof(mdToken);
+
                             // Skip the call to `Await`
                             codeAddr += 1 + sizeof(mdToken);
                         }
diff --git a/src/coreclr/jit/importercalls.cpp b/src/coreclr/jit/importercalls.cpp
index b3f78ff2f4f501..8165f3b775fe80 100644
--- a/src/coreclr/jit/importercalls.cpp
+++ b/src/coreclr/jit/importercalls.cpp
@@ -11243,6 +11243,17 @@ NamedIntrinsic Compiler::lookupNamedIntrinsic(CORINFO_METHOD_HANDLE method)
                         }
                     }
                 }
+                else if (strcmp(namespaceName, "Threading.Tasks") == 0)
+                {
+                    if (strcmp(methodName, "ConfigureAwait") == 0)
+                    {
+                        if (strcmp(className, "Task`1") == 0 || strcmp(className, "Task") == 0 ||
+                            strcmp(className, "ValuTask`1") == 0 || strcmp(className, "ValueTask") == 0)
+                        {
+                            result = NI_System_Threading_Tasks_Task_ConfigureAwait;
+                        }
+                    }
+                }
         }
     }
     else if (strcmp(namespaceName, "Internal.Runtime") == 0)
diff --git a/src/coreclr/jit/namedintrinsiclist.h b/src/coreclr/jit/namedintrinsiclist.h
index 96ce387af66a4b..20d60f4b22c530 100644
--- a/src/coreclr/jit/namedintrinsiclist.h
+++ b/src/coreclr/jit/namedintrinsiclist.h
@@ -155,6 +155,8 @@ enum NamedIntrinsic : unsigned short
     NI_System_Threading_Interlocked_ExchangeAdd,
     NI_System_Threading_Interlocked_MemoryBarrier,
 
+    NI_System_Threading_Tasks_Task_ConfigureAwait,
+
     // These two are special marker IDs so that we still get the inlining profitability boost
     NI_System_Numerics_Intrinsic,
     NI_System_Runtime_Intrinsics_Intrinsic,
diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/CompatibilitySuppressions.xml b/src/coreclr/nativeaot/System.Private.CoreLib/src/CompatibilitySuppressions.xml
index 69db65a1850946..8c95bf386d1e6a 100644
--- a/src/coreclr/nativeaot/System.Private.CoreLib/src/CompatibilitySuppressions.xml
+++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/CompatibilitySuppressions.xml
@@ -857,6 +857,18 @@
     <DiagnosticId>CP0002</DiagnosticId>
     <Target>M:System.Threading.Lock.#ctor(System.Boolean)</Target>
   </Suppression>
+  <Suppression>
+    <DiagnosticId>CP0002</DiagnosticId>
+    <Target>M:System.Runtime.CompilerServices.RuntimeHelpers.Await(System.Runtime.CompilerServices.ConfiguredTaskAwaitable)</Target>
+    <Left>ref/net10.0/System.Private.CoreLib.dll</Left>
+    <Right>lib/net10.0/System.Private.CoreLib.dll</Right>
+  </Suppression>
+  <Suppression>
+    <DiagnosticId>CP0002</DiagnosticId>
+    <Target>M:System.Runtime.CompilerServices.RuntimeHelpers.Await(System.Runtime.CompilerServices.ConfiguredValueTaskAwaitable)</Target>
+    <Left>ref/net10.0/System.Private.CoreLib.dll</Left>
+    <Right>lib/net10.0/System.Private.CoreLib.dll</Right>
+  </Suppression>
   <Suppression>
     <DiagnosticId>CP0002</DiagnosticId>
     <Target>M:System.Runtime.CompilerServices.RuntimeHelpers.Await(System.Threading.Tasks.Task)</Target>
@@ -869,6 +881,18 @@
     <Left>ref/net10.0/System.Private.CoreLib.dll</Left>
     <Right>lib/net10.0/System.Private.CoreLib.dll</Right>
   </Suppression>
+  <Suppression>
+    <DiagnosticId>CP0002</DiagnosticId>
+    <Target>M:System.Runtime.CompilerServices.RuntimeHelpers.Await``1(System.Runtime.CompilerServices.ConfiguredTaskAwaitable{``0})</Target>
+    <Left>ref/net10.0/System.Private.CoreLib.dll</Left>
+    <Right>lib/net10.0/System.Private.CoreLib.dll</Right>
+  </Suppression>
+  <Suppression>
+    <DiagnosticId>CP0002</DiagnosticId>
+    <Target>M:System.Runtime.CompilerServices.RuntimeHelpers.Await``1(System.Runtime.CompilerServices.ConfiguredValueTaskAwaitable{``0})</Target>
+    <Left>ref/net10.0/System.Private.CoreLib.dll</Left>
+    <Right>lib/net10.0/System.Private.CoreLib.dll</Right>
+  </Suppression>
   <Suppression>
     <DiagnosticId>CP0002</DiagnosticId>
     <Target>M:System.Runtime.CompilerServices.RuntimeHelpers.Await``1(System.Threading.Tasks.Task{``0})</Target>
diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.cs
index 93f9e01de1fa45..45399a2a7d3c05 100644
--- a/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.cs
+++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.cs
@@ -271,6 +271,69 @@ public static void Await(ValueTask task)
             awaiter.GetResult();
         }
 
+        // Marked intrinsic since this needs to be
+        // recognized as an async2 call.
+        [Intrinsic]
+        [BypassReadyToRun]
+        [MethodImpl(MethodImplOptions.Async)]
+        public static void Await(ConfiguredTaskAwaitable configuredAwaitable)
+        {
+            ConfiguredTaskAwaitable.ConfiguredTaskAwaiter awaiter = configuredAwaitable.GetAwaiter();
+            if (!awaiter.IsCompleted)
+            {
+                UnsafeAwaitAwaiterFromRuntimeAsync(awaiter);
+            }
+
+            awaiter.GetResult();
+        }
+
+        // Marked intrinsic since this needs to be
+        // recognized as an async2 call.
+        [Intrinsic]
+        [BypassReadyToRun]
+        [MethodImpl(MethodImplOptions.Async)]
+        public static void Await(ConfiguredValueTaskAwaitable configuredAwaitable)
+        {
+            ConfiguredValueTaskAwaitable.ConfiguredValueTaskAwaiter awaiter = configuredAwaitable.GetAwaiter();
+            if (!awaiter.IsCompleted)
+            {
+                UnsafeAwaitAwaiterFromRuntimeAsync(awaiter);
+            }
+
+            awaiter.GetResult();
+        }
+
+        // Marked intrinsic since this needs to be
+        // recognized as an async2 call.
+        [Intrinsic]
+        [BypassReadyToRun]
+        [MethodImpl(MethodImplOptions.Async)]
+        public static T Await<T>(ConfiguredTaskAwaitable<T> configuredAwaitable)
+        {
+            ConfiguredTaskAwaitable<T>.ConfiguredTaskAwaiter awaiter = configuredAwaitable.GetAwaiter();
+            if (!awaiter.IsCompleted)
+            {
+                UnsafeAwaitAwaiterFromRuntimeAsync(awaiter);
+            }
+
+            return awaiter.GetResult();
+        }
+
+        // Marked intrinsic since this needs to be
+        // recognized as an async2 call.
+        [Intrinsic]
+        [BypassReadyToRun]
+        [MethodImpl(MethodImplOptions.Async)]
+        public static T Await<T>(ConfiguredValueTaskAwaitable<T> configuredAwaitable)
+        {
+            ConfiguredValueTaskAwaitable<T>.ConfiguredValueTaskAwaiter awaiter = configuredAwaitable.GetAwaiter();
+            if (!awaiter.IsCompleted)
+            {
+                UnsafeAwaitAwaiterFromRuntimeAsync(awaiter);
+            }
+
+            return awaiter.GetResult();
+        }
 #endif
     }
 }
diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/Future.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/Future.cs
index ce4f18b6099baf..1e96a5905b29e2 100644
--- a/src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/Future.cs
+++ b/src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/Future.cs
@@ -513,6 +513,7 @@ internal override void InnerInvoke()
         /// true to attempt to marshal the continuation back to the original context captured; otherwise, false.
         /// </param>
         /// <returns>An object used to await this task.</returns>
+        [Intrinsic]
         public new ConfiguredTaskAwaitable<TResult> ConfigureAwait(bool continueOnCapturedContext)
         {
             return new ConfiguredTaskAwaitable<TResult>(this, continueOnCapturedContext ? ConfigureAwaitOptions.ContinueOnCapturedContext : ConfigureAwaitOptions.None);
@@ -522,6 +523,7 @@ internal override void InnerInvoke()
         /// <param name="options">Options used to configure how awaits on this task are performed.</param>
         /// <returns>An object used to await this task.</returns>
         /// <exception cref="ArgumentOutOfRangeException">The <paramref name="options"/> argument specifies an invalid value.</exception>
+        [Intrinsic]
         public new ConfiguredTaskAwaitable<TResult> ConfigureAwait(ConfigureAwaitOptions options)
         {
             if ((options & ~(ConfigureAwaitOptions.ContinueOnCapturedContext |
diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/Task.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/Task.cs
index d007bc756e8bdb..0f64ba8ea52f94 100644
--- a/src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/Task.cs
+++ b/src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/Task.cs
@@ -2448,6 +2448,7 @@ public TaskAwaiter GetAwaiter()
         /// true to attempt to marshal the continuation back to the original context captured; otherwise, false.
         /// </param>
         /// <returns>An object used to await this task.</returns>
+        [Intrinsic]
         public ConfiguredTaskAwaitable ConfigureAwait(bool continueOnCapturedContext)
         {
             return new ConfiguredTaskAwaitable(this, continueOnCapturedContext ? ConfigureAwaitOptions.ContinueOnCapturedContext : ConfigureAwaitOptions.None);
@@ -2457,6 +2458,7 @@ public ConfiguredTaskAwaitable ConfigureAwait(bool continueOnCapturedContext)
         /// <param name="options">Options used to configure how awaits on this task are performed.</param>
         /// <returns>An object used to await this task.</returns>
         /// <exception cref="ArgumentOutOfRangeException">The <paramref name="options"/> argument specifies an invalid value.</exception>
+        [Intrinsic]
         public ConfiguredTaskAwaitable ConfigureAwait(ConfigureAwaitOptions options)
         {
             if ((options & ~(ConfigureAwaitOptions.ContinueOnCapturedContext |
diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/ValueTask.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/ValueTask.cs
index c8d0aec3960bbe..90f86c68159c28 100644
--- a/src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/ValueTask.cs
+++ b/src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/ValueTask.cs
@@ -425,6 +425,7 @@ internal static ValueTask DangerousCreateFromTypedValueTask<TResult>(ValueTask<T
         /// <param name="continueOnCapturedContext">
         /// true to attempt to marshal the continuation back to the captured context; otherwise, false.
         /// </param>
+        [Intrinsic]
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
         public ConfiguredValueTaskAwaitable ConfigureAwait(bool continueOnCapturedContext) =>
             new ConfiguredValueTaskAwaitable(new ValueTask(_obj, _token, continueOnCapturedContext));
@@ -825,6 +826,7 @@ public TResult Result
         /// <param name="continueOnCapturedContext">
         /// true to attempt to marshal the continuation back to the captured context; otherwise, false.
         /// </param>
+        [Intrinsic]
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
         public ConfiguredValueTaskAwaitable<TResult> ConfigureAwait(bool continueOnCapturedContext) =>
             new ConfiguredValueTaskAwaitable<TResult>(new ValueTask<TResult>(_obj, _result, _token, continueOnCapturedContext));
diff --git a/src/libraries/System.Runtime/ref/System.Runtime.cs b/src/libraries/System.Runtime/ref/System.Runtime.cs
index c5879025b87244..100b88dcaecedb 100644
--- a/src/libraries/System.Runtime/ref/System.Runtime.cs
+++ b/src/libraries/System.Runtime/ref/System.Runtime.cs
@@ -13757,6 +13757,10 @@ public static void AwaitAwaiterFromRuntimeAsync<TAwaiter>(TAwaiter awaiter) wher
         public static T Await<T>(System.Threading.Tasks.Task<T> task) { throw null; }
         public static void Await(System.Threading.Tasks.ValueTask task) { throw null; }
         public static T Await<T>(System.Threading.Tasks.ValueTask<T> task) { throw null; }
+        public static void Await(System.Runtime.CompilerServices.ConfiguredTaskAwaitable configuredAwaitable) { throw null; }
+        public static void Await(System.Runtime.CompilerServices.ConfiguredValueTaskAwaitable configuredAwaitable) { throw null; }
+        public static T Await<T>(System.Runtime.CompilerServices.ConfiguredTaskAwaitable<T> configuredAwaitable) { throw null; }
+        public static T Await<T>(System.Runtime.CompilerServices.ConfiguredValueTaskAwaitable<T> configuredAwaitable) { throw null; }
     }
     public sealed partial class RuntimeWrappedException : System.Exception
     {
diff --git a/src/tests/async/fibonacci-without-yields-config-await.cs b/src/tests/async/fibonacci-without-yields-config-await.cs
new file mode 100644
index 00000000000000..047411ec312838
--- /dev/null
+++ b/src/tests/async/fibonacci-without-yields-config-await.cs
@@ -0,0 +1,57 @@
+// 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 System.Threading.Tasks;
+using System.Diagnostics;
+using System.Runtime.CompilerServices;
+using Xunit;
+
+#pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously
+
+public class Async2FibonacciWithYields
+{
+    const int iterations = 3;
+    const bool doYields = false;
+
+    [Fact]
+    public static void Test()
+    {
+        long allocated = GC.GetTotalAllocatedBytes(precise: true);
+
+        AsyncEntry().GetAwaiter().GetResult();
+
+        allocated = GC.GetTotalAllocatedBytes(precise: true) - allocated;
+        System.Console.WriteLine("allocated: " + allocated);
+    }
+
+    public static async2 Task AsyncEntry()
+    {
+        for (int i = 0; i < iterations; i++)
+        {
+            var sw = Stopwatch.StartNew();
+            int result = RuntimeHelpers.Await(Fib(30).ConfigureAwait(false));
+            sw.Stop();
+
+            Console.WriteLine($"{sw.ElapsedMilliseconds} ms result={result}");
+        }
+    }
+
+    static async2 Task<int> Fib(int i)
+    {
+        if (i <= 1)
+        {
+            if (doYields)
+            {
+                await Task.Yield();
+            }
+
+            return 1;
+        }
+
+        int i1 = RuntimeHelpers.Await(Fib(i - 1).ConfigureAwait(true));
+        int i2 = RuntimeHelpers.Await(Fib(i - 2).ConfigureAwait(false));
+
+        return i1 + i2;
+    }
+}
diff --git a/src/tests/async/fibonacci-without-yields-config-await.csproj b/src/tests/async/fibonacci-without-yields-config-await.csproj
new file mode 100644
index 00000000000000..de6d5e08882e86
--- /dev/null
+++ b/src/tests/async/fibonacci-without-yields-config-await.csproj
@@ -0,0 +1,8 @@
+<Project Sdk="Microsoft.NET.Sdk">
+  <PropertyGroup>
+    <Optimize>True</Optimize>
+  </PropertyGroup>
+  <ItemGroup>
+    <Compile Include="$(MSBuildProjectName).cs" />
+  </ItemGroup>
+</Project>

From 94f8e5ab15f97aed6a1fd1e1ff1a8010ee858b64 Mon Sep 17 00:00:00 2001
From: Vladimir Sadov <vsadov@microsoft.com>
Date: Wed, 26 Mar 2025 08:59:30 -0700
Subject: [PATCH 152/203] turn off the assert and enable test scenario (#3054)

---
 src/coreclr/vm/prestub.cpp |  2 --
 src/tests/async/struct.cs  | 10 +++++++++-
 2 files changed, 9 insertions(+), 3 deletions(-)

diff --git a/src/coreclr/vm/prestub.cpp b/src/coreclr/vm/prestub.cpp
index f07ba9a987d466..8de48f53ab59d7 100644
--- a/src/coreclr/vm/prestub.cpp
+++ b/src/coreclr/vm/prestub.cpp
@@ -1843,8 +1843,6 @@ void MethodDesc::EmitJitStateMachineBasedRuntimeAsyncThunk(MethodDesc* pAsyncOth
             DWORD localArg = 0;
             if (thunkMsig.HasThis())
             {
-                // Struct async thunks not yet implemented
-                _ASSERTE(!this->GetMethodTable()->IsValueType());
                 pCode->EmitLDARG(localArg++);
             }
 
diff --git a/src/tests/async/struct.cs b/src/tests/async/struct.cs
index fd73631e9b9d66..cc2923b89ece5e 100644
--- a/src/tests/async/struct.cs
+++ b/src/tests/async/struct.cs
@@ -14,9 +14,17 @@ public class Async2Struct
     public static void TestEntryPoint()
     {
         Async().Wait();
+        Async2().Wait();
     }
 
-    private static async2 Task Async()
+    private static async Task Async()
+    {
+        S s = new S(100);
+        await s.Test();
+        AssertEqual(100, s.Value);
+    }
+
+    private static async2 Task Async2()
     {
         S s = new S(100);
         await s.Test();

From eae52eda2655c1d4132b68855c308300ea8b6265 Mon Sep 17 00:00:00 2001
From: vsadov <8218165+VSadov@users.noreply.github.com>
Date: Thu, 27 Mar 2025 13:14:48 -0700
Subject: [PATCH 153/203] reconcile root and eng files with runtime repo

---
 .gitignore                                    |   3 -
 Directory.Build.props                         | 139 +----
 NuGet.config                                  |   1 -
 README.md                                     |  35 --
 buildroslynnugets.cmd                         |  34 --
 eng/Brewfile                                  |   5 -
 eng/DotNetBuild.props                         |  17 +
 eng/OSArch.props                              |  35 ++
 eng/Publishing.props                          | 221 +++-----
 eng/RuntimeIdentifier.props                   | 100 ++++
 eng/Signing.props                             | 164 ++++--
 eng/Subsets.props                             |  13 +-
 eng/Version.Details.xml                       | 284 +++++-----
 eng/Versions.props                            | 155 +++---
 eng/build-analysis-configuration.json         |   4 +
 eng/build.ps1                                 |   2 +-
 eng/build.sh                                  |   6 +-
 eng/common/CIBuild.cmd                        |   2 +-
 eng/common/build.ps1                          |   7 +-
 eng/common/build.sh                           |  13 +-
 eng/common/cibuild.sh                         |   2 +-
 eng/common/core-templates/job/job.yml         |   1 -
 .../job/publish-build-assets.yml              |   4 -
 .../core-templates/jobs/codeql-build.yml      |   1 -
 eng/common/core-templates/jobs/jobs.yml       |   4 -
 .../core-templates/steps/generate-sbom.yml    |   2 +-
 .../steps/install-microbuild.yml              |  30 +-
 eng/common/cross/armel/armel.jessie.patch     |  43 --
 eng/common/cross/build-rootfs.sh              |  53 +-
 eng/common/generate-sbom-prep.ps1             |  20 +-
 eng/common/generate-sbom-prep.sh              |  17 +-
 eng/common/native/install-dependencies.sh     |   7 +-
 eng/common/templates-official/job/job.yml     |   5 +-
 .../steps/publish-build-artifacts.yml         |   7 +-
 eng/common/templates/job/job.yml              |   4 +-
 .../steps/publish-build-artifacts.yml         |   8 +-
 eng/common/tools.ps1                          |   2 +-
 eng/common/tools.sh                           |   2 +-
 eng/docker/libraries-sdk.linux.Dockerfile     |   2 +-
 eng/docker/libraries-sdk.windows.Dockerfile   |   2 +-
 eng/extract-for-crossdac.ps1                  |   2 +-
 eng/native/configurecompiler.cmake            |   2 +-
 eng/native/configureplatform.cmake            |   2 +-
 eng/native/functions.cmake                    |  15 +-
 eng/pipelines/common/global-build-job.yml     |  37 +-
 .../common/templates/global-build-step.yml    |   2 +-
 .../build-runtime-tests-and-send-to-helix.yml |   1 -
 .../runtimes/build-runtime-tests.yml          |   1 -
 .../templates/runtimes/run-test-job.yml       |   5 +-
 .../templates/runtimes/send-to-helix-step.yml |   2 +
 .../common/templates/runtimes/xplat-job.yml   |   6 -
 .../common/templates/wasm-library-tests.yml   |   2 +-
 eng/pipelines/coreclr/libraries-pgo.yml       |   1 -
 .../coreclr/superpmi-collect-test.yml         | 368 +------------
 eng/pipelines/coreclr/superpmi-collect.yml    | 364 +------------
 eng/pipelines/coreclr/superpmi-replay-apx.yml |  19 +
 eng/pipelines/coreclr/superpmi-replay.yml     |  74 +--
 .../coreclr/templates/helix-queues-setup.yml  |  12 +-
 .../coreclr/templates/jit-replay-pipeline.yml |  58 +++
 .../templates/run-superpmi-replay-job.yml     |   9 +-
 .../templates/superpmi-collect-pipeline.yml   | 359 +++++++++++++
 .../coreclr/templates/superpmi-replay-job.yml |  15 +-
 .../diagnostics/runtime-diag-job.yml          | 261 ++++++++++
 .../runtime-extra-platforms-ioslike.yml       |   4 +-
 ...ntime-extra-platforms-ioslikesimulator.yml |   2 +-
 .../runtime-extra-platforms-maccatalyst.yml   |   4 +-
 .../runtime-extra-platforms-other.yml         |  12 +-
 .../libraries/helix-queues-setup.yml          |  12 +-
 eng/pipelines/libraries/outerloop.yml         |   8 +-
 eng/pipelines/libraries/stress/http.yml       |   4 +-
 eng/pipelines/libraries/stress/ssl.yml        |   4 +-
 eng/pipelines/official/pipeline.yml           |  30 ++
 .../{jobs => }/prepare-signed-artifacts.yml   |  23 +-
 eng/pipelines/official/stages/publish.yml     |  57 --
 eng/pipelines/performance/perf-build.yml      | 236 +++++++++
 eng/pipelines/performance/perf-slow.yml       |   4 +-
 eng/pipelines/performance/perf.yml            |   1 -
 .../templates/perf-bdn-build-jobs.yml         |   2 +-
 .../performance/templates/perf-build-jobs.yml |   2 +-
 .../templates/perf-coreclr-build-jobs.yml     |   6 +-
 .../perf-ios-scenarios-build-jobs.yml         |   4 +-
 eng/pipelines/runtime-diagnostics.yml         |  68 ++-
 eng/pipelines/runtime-official.yml            | 492 ++++++++----------
 eng/pipelines/runtime.yml                     |  16 +-
 eng/testing/BrowserVersions.props             |  12 +-
 eng/testing/tests.wasm.targets                |   4 +-
 global.json                                   |  10 +-
 87 files changed, 2109 insertions(+), 1981 deletions(-)
 delete mode 100644 buildroslynnugets.cmd
 delete mode 100644 eng/Brewfile
 create mode 100644 eng/OSArch.props
 create mode 100644 eng/RuntimeIdentifier.props
 delete mode 100644 eng/common/cross/armel/armel.jessie.patch
 create mode 100644 eng/pipelines/coreclr/superpmi-replay-apx.yml
 create mode 100644 eng/pipelines/coreclr/templates/jit-replay-pipeline.yml
 create mode 100644 eng/pipelines/coreclr/templates/superpmi-collect-pipeline.yml
 create mode 100644 eng/pipelines/diagnostics/runtime-diag-job.yml
 create mode 100644 eng/pipelines/official/pipeline.yml
 rename eng/pipelines/official/{jobs => }/prepare-signed-artifacts.yml (73%)
 delete mode 100644 eng/pipelines/official/stages/publish.yml
 create mode 100644 eng/pipelines/performance/perf-build.yml

diff --git a/.gitignore b/.gitignore
index 0c35f3bd69184f..88e4f6ab632aee 100644
--- a/.gitignore
+++ b/.gitignore
@@ -11,9 +11,6 @@ syntax: glob
 .packages
 .tools
 
-# nuget packages for custom roslyn
-roslynpackages
-
 # User-specific files
 *.suo
 *.user
diff --git a/Directory.Build.props b/Directory.Build.props
index b3265a67189527..d13e0749062eaf 100644
--- a/Directory.Build.props
+++ b/Directory.Build.props
@@ -17,39 +17,8 @@
     <ShouldUnsetParentConfigurationAndPlatform>false</ShouldUnsetParentConfigurationAndPlatform>
   </PropertyGroup>
 
-  <PropertyGroup Label="CalculateTargetOS">
-    <_hostOS>linux</_hostOS>
-    <_hostOS Condition="$([MSBuild]::IsOSPlatform('OSX'))">osx</_hostOS>
-    <_hostOS Condition="$([MSBuild]::IsOSPlatform('FREEBSD'))">freebsd</_hostOS>
-    <_hostOS Condition="$([MSBuild]::IsOSPlatform('NETBSD'))">netbsd</_hostOS>
-    <_hostOS Condition="$([MSBuild]::IsOSPlatform('ILLUMOS'))">illumos</_hostOS>
-    <_hostOS Condition="$([MSBuild]::IsOSPlatform('SOLARIS'))">solaris</_hostOS>
-    <_hostOS Condition="$([MSBuild]::IsOSPlatform('HAIKU'))">haiku</_hostOS>
-    <_hostOS Condition="$([MSBuild]::IsOSPlatform('WINDOWS'))">windows</_hostOS>
-    <HostOS>$(_hostOS)</HostOS>
-    <TargetOS Condition="'$(TargetOS)' == '' and '$(RuntimeIdentifier)' == 'browser-wasm'">browser</TargetOS>
-    <TargetOS Condition="'$(TargetOS)' == ''">$(_hostOS)</TargetOS>
-    <TargetsMobile Condition="'$(TargetOS)' == 'ios' or '$(TargetOS)' == 'iossimulator' or '$(TargetOS)' == 'maccatalyst' or '$(TargetOS)' == 'tvos' or '$(TargetOS)' == 'tvossimulator' or '$(TargetOS)' == 'android' or '$(TargetOS)' == 'browser' or '$(TargetOS)' == 'wasi'">true</TargetsMobile>
-    <TargetsAppleMobile Condition="'$(TargetOS)' == 'ios' or '$(TargetOS)' == 'iossimulator' or '$(TargetOS)' == 'maccatalyst' or '$(TargetOS)' == 'tvos' or '$(TargetOS)' == 'tvossimulator'">true</TargetsAppleMobile>
-  </PropertyGroup>
-
-  <!-- Platform property is required by RepoLayout.props in Arcade SDK. -->
-  <PropertyGroup Label="CalculateArch">
-    <_hostArch>$([System.Runtime.InteropServices.RuntimeInformation]::ProcessArchitecture.ToString().ToLowerInvariant)</_hostArch>
-    <BuildArchitecture Condition="'$(BuildArchitecture)' == ''">$(_hostArch)</BuildArchitecture>
-    <TargetArchitecture Condition="'$(TargetArchitecture)' == '' and ('$(TargetOS)' == 'browser' or '$(RuntimeIdentifier)' == 'browser-wasm')">wasm</TargetArchitecture>
-    <TargetArchitecture Condition="'$(TargetArchitecture)' == '' and ('$(TargetOS)' == 'wasi' or '$(RuntimeIdentifier)' == 'wasi-wasm')">wasm</TargetArchitecture>
-    <TargetArchitecture Condition="'$(TargetArchitecture)' == '' and '$(_hostArch)' == 'arm'">arm</TargetArchitecture>
-    <TargetArchitecture Condition="'$(TargetArchitecture)' == '' and '$(_hostArch)' == 'armv6'">armv6</TargetArchitecture>
-    <TargetArchitecture Condition="'$(TargetArchitecture)' == '' and '$(_hostArch)' == 'armel'">armel</TargetArchitecture>
-    <TargetArchitecture Condition="'$(TargetArchitecture)' == '' and '$(_hostArch)' == 'arm64'">arm64</TargetArchitecture>
-    <TargetArchitecture Condition="'$(TargetArchitecture)' == '' and '$(_hostArch)' == 'loongarch64'">loongarch64</TargetArchitecture>
-    <TargetArchitecture Condition="'$(TargetArchitecture)' == '' and '$(_hostArch)' == 's390x'">s390x</TargetArchitecture>
-    <TargetArchitecture Condition="'$(TargetArchitecture)' == '' and '$(_hostArch)' == 'ppc64le'">ppc64le</TargetArchitecture>
-    <TargetArchitecture Condition="'$(TargetArchitecture)' == '' and '$(TargetsMobile)' == 'true'">x64</TargetArchitecture>
-    <TargetArchitecture Condition="'$(TargetArchitecture)' == ''">x64</TargetArchitecture>
-    <Platform Condition="'$(Platform)' == '' and '$(InferPlatformFromTargetArchitecture)' == 'true'">$(TargetArchitecture)</Platform>
-  </PropertyGroup>
+  <!-- We don't have RepoRoot or RepositoryEngineeringDir available at this point -->
+  <Import Project="$(MSBuildThisFileDirectory)/eng/OSArch.props" />
 
   <PropertyGroup Label="SetOSTargetMinVersions">
     <!--
@@ -113,9 +82,6 @@
     <AspNetCoreAppCurrentVersion>10.0</AspNetCoreAppCurrentVersion>
     <AspNetCoreAppCurrent>net$(AspNetCoreAppCurrentVersion)</AspNetCoreAppCurrent>
 
-    <NetFrameworkMinimum>net462</NetFrameworkMinimum>
-    <NetFrameworkCurrent>net48</NetFrameworkCurrent>
-    <NetFrameworkToolCurrent>net472</NetFrameworkToolCurrent>
     <!-- Don't build for NETFramework during source-build. -->
     <NetFrameworkMinimum Condition="'$(DotNetBuildSourceOnly)' == 'true'" />
     <NetFrameworkToolCurrent Condition="'$(DotNetBuildSourceOnly)' == 'true'" />
@@ -197,104 +163,7 @@
     <MonoCrossAOTTargetOS Condition="'$(TargetOS)' == 'osx'">$(MonoCrossAOTTargetOS)+tvos+ios+maccatalyst</MonoCrossAOTTargetOS>
   </PropertyGroup>
 
-  <PropertyGroup Label="CalculatePortableBuild">
-    <PortableBuild Condition="'$(PortableBuild)' == '' and '$(DotNetBuildSourceOnly)' == 'true'">false</PortableBuild>
-    <PortableBuild Condition="'$(PortableBuild)' == ''">true</PortableBuild>
-  </PropertyGroup>
-
-  <!-- _portableOS is the portable rid-OS corresponding to the target platform. -->
-  <PropertyGroup Label="CalculatePortableOS">
-    <!-- To determine _portableOS we use TargetOS.
-         TargetOS is not a rid-OS. For example: for Windows it is 'windows' instead of 'win'.
-         And, for flavors of Linux, like 'linux-musl' and 'linux-bionic', TargetOS is 'linux'. -->
-
-    <_portableOS>$(TargetOS.ToLowerInvariant())</_portableOS>
-    <_portableOS Condition="'$(_portableOS)' == 'windows'">win</_portableOS>
-
-    <!-- TargetOS=AnyOS is a sentinel value used by tests, ignore it.  -->
-    <_portableOS Condition="'$(_portableOS)' == 'anyos'">$(__PortableTargetOS)</_portableOS>
-
-    <!-- Detect linux flavors using __PortableTargetOS from the native script. -->
-    <_portableOS Condition="'$(_portableOS)' == 'linux' and '$(__PortableTargetOS)' == 'linux-musl'">linux-musl</_portableOS>
-    <_portableOS Condition="'$(_portableOS)' == 'linux' and '$(__PortableTargetOS)' == 'linux-bionic'">linux-bionic</_portableOS>
-
-    <!-- On Windows, we can build for Windows and Mobile.
-         For other TargetOSes, create a "win" build, built from TargetOS sources and "win" pre-built packages. -->
-    <_portableOS Condition="'$(HostOS)' == 'win' and '$(TargetsMobile)' != 'true'">win</_portableOS>
-  </PropertyGroup>
-
-  <!-- PackageRID is used for packages needed for the target. -->
-  <PropertyGroup Label="CalculatePackageRID">
-    <_packageOS>$(_portableOS)</_packageOS>
-
-    <_packageOS Condition="'$(CrossBuild)' == 'true' and '$(_portableOS)' != 'linux-musl' and '$(_portableOS)' != 'linux-bionic' and '$(_portableOS)' != 'android'">$(_hostOS)</_packageOS>
-
-    <!-- source-build sets PackageOS to build with non-portable rid packages that were source-built previously. -->
-    <PackageRID Condition="'$(PackageOS)' != ''">$(PackageOS)-$(TargetArchitecture)</PackageRID>
-    <PackageRID Condition="'$(PackageRID)' == ''">$(_packageOS)-$(TargetArchitecture)</PackageRID>
-  </PropertyGroup>
-
-  <!-- ToolsRID is used for packages needed on the build host. -->
-  <PropertyGroup Label="CalculateToolsRID">
-    <!-- _portableHostOS is the portable rid-OS corresponding to the build host platform.
-
-         To determine _portableHostOS we use _hostOS, similar to how _portableOS is calculated from TargetOS.
-
-         When we're not cross-building we can detect linux flavors by looking at _portableOS
-         because the target platform and the build host platform are the same.
-         For cross-builds, we're currently unable to detect the flavors. -->
-    <_portableHostOS>$(_hostOS)</_portableHostOS>
-    <_portableHostOS Condition="'$(_portableHostOS)' == 'windows'">win</_portableHostOS>
-    <_portableHostOS Condition="'$(CrossBuild)' != 'true' and '$(_portableOS)' == 'linux-musl'">linux-musl</_portableHostOS>
-
-    <!-- source-build sets ToolsOS to build with non-portable rid packages that were source-built previously. -->
-    <ToolsRID Condition="'$(ToolsOS)' != ''">$(ToolsOS)-$(_hostArch)</ToolsRID>
-    <ToolsRID Condition="'$(ToolsRID)' == ''">$(_portableHostOS)-$(_hostArch)</ToolsRID>
-
-    <!-- Microsoft.NET.Sdk.IL SDK defaults to the portable host rid. Match it to ToolsRID (for source-build). -->
-    <MicrosoftNetCoreIlasmPackageRuntimeId>$(ToolsRID)</MicrosoftNetCoreIlasmPackageRuntimeId>
-  </PropertyGroup>
-
-  <!-- OutputRID is used to name the target platform.
-       For portable builds, OutputRID matches _portableOS.
-       For non-portable builds, it uses __DistroRid (from the native build script), or falls back to RuntimeInformation.RuntimeIdentifier.
-       Source-build sets OutputRID directly. -->
-  <PropertyGroup Label="CalculateOutputRID">
-    <_hostRid Condition="'$(MSBuildRuntimeType)' == 'core'">$([System.Runtime.InteropServices.RuntimeInformation]::RuntimeIdentifier)</_hostRid>
-    <_hostRid Condition="'$(MSBuildRuntimeType)' != 'core'">win-$([System.Runtime.InteropServices.RuntimeInformation]::OSArchitecture.ToString().ToLowerInvariant)</_hostRid>
-
-    <_parseDistroRid>$(__DistroRid)</_parseDistroRid>
-    <_parseDistroRid Condition="'$(_parseDistroRid)' == ''">$(_hostRid)</_parseDistroRid>
-    <_distroRidIndex>$(_parseDistroRid.LastIndexOf('-'))</_distroRidIndex>
-
-    <_outputOS>$(_parseDistroRid.SubString(0, $(_distroRidIndex)))</_outputOS>
-    <_outputOS Condition="'$(PortableBuild)' == 'true'">$(_portableOS)</_outputOS>
-
-    <OutputRID Condition="'$(OutputRID)' == ''">$(_outputOS)-$(TargetArchitecture)</OutputRID>
-  </PropertyGroup>
-
-  <PropertyGroup Label="CalculateTargetOSName">
-    <TargetsFreeBSD Condition="'$(TargetOS)' == 'freebsd'">true</TargetsFreeBSD>
-    <Targetsillumos Condition="'$(TargetOS)' == 'illumos'">true</Targetsillumos>
-    <TargetsSolaris Condition="'$(TargetOS)' == 'solaris'">true</TargetsSolaris>
-    <TargetsHaiku Condition="'$(TargetOS)' == 'haiku'">true</TargetsHaiku>
-    <TargetsLinux Condition="'$(TargetOS)' == 'linux' or '$(TargetOS)' == 'android'">true</TargetsLinux>
-    <TargetsLinuxBionic Condition="'$(_portableOS)' == 'linux-bionic'">true</TargetsLinuxBionic>
-    <TargetsLinuxMusl Condition="'$(_portableOS)' == 'linux-musl'">true</TargetsLinuxMusl>
-    <TargetsLinuxGlibc Condition="'$(TargetsLinux)' == 'true' and '$(TargetsLinuxMusl)' != 'true' and '$(TargetsLinuxBionic)' != 'true'">true</TargetsLinuxGlibc>
-    <TargetsNetBSD Condition="'$(TargetOS)' == 'netbsd'">true</TargetsNetBSD>
-    <TargetsOSX Condition="'$(TargetOS)' == 'osx'">true</TargetsOSX>
-    <TargetsMacCatalyst Condition="'$(TargetOS)' == 'maccatalyst'">true</TargetsMacCatalyst>
-    <TargetsiOS Condition="'$(TargetOS)' == 'ios' or '$(TargetOS)' == 'iossimulator'">true</TargetsiOS>
-    <TargetstvOS Condition="'$(TargetOS)' == 'tvos' or '$(TargetOS)' == 'tvossimulator'">true</TargetstvOS>
-    <TargetsiOSSimulator Condition="'$(TargetOS)' == 'iossimulator'">true</TargetsiOSSimulator>
-    <TargetstvOSSimulator Condition="'$(TargetOS)' == 'tvossimulator'">true</TargetstvOSSimulator>
-    <TargetsAndroid Condition="'$(TargetOS)' == 'android'">true</TargetsAndroid>
-    <TargetsBrowser Condition="'$(TargetOS)' == 'browser'">true</TargetsBrowser>
-    <TargetsWasi Condition="'$(TargetOS)' == 'wasi'">true</TargetsWasi>
-    <TargetsWindows Condition="'$(TargetOS)' == 'windows'">true</TargetsWindows>
-    <TargetsUnix Condition="'$(TargetsFreeBSD)' == 'true' or '$(Targetsillumos)' == 'true' or '$(TargetsSolaris)' == 'true' or '$(TargetsHaiku)' == 'true' or '$(TargetsLinux)' == 'true' or '$(TargetsNetBSD)' == 'true' or '$(TargetsOSX)' == 'true' or '$(TargetsMacCatalyst)' == 'true' or '$(TargetstvOS)' == 'true' or '$(TargetsiOS)' == 'true' or '$(TargetsAndroid)' == 'true'">true</TargetsUnix>
-  </PropertyGroup>
+  <Import Project="$(RepositoryEngineeringDir)RuntimeIdentifier.props" />
 
   <PropertyGroup>
     <MicrosoftNetCoreAppRefPackDir>$([MSBuild]::NormalizeDirectory('$(ArtifactsBinDir)', 'microsoft.netcore.app.ref'))</MicrosoftNetCoreAppRefPackDir>
@@ -357,7 +226,7 @@
 
   <!-- Packaging -->
   <PropertyGroup>
-    <GitHubRepositoryName>runtimelab</GitHubRepositoryName>
+    <GitHubRepositoryName>runtime</GitHubRepositoryName>
     <RepositoryUrl>https://github.com/dotnet/$(GitHubRepositoryName)</RepositoryUrl>
     <PackageProjectUrl>https://dot.net</PackageProjectUrl>
     <Owners>microsoft,dotnetframework</Owners>
diff --git a/NuGet.config b/NuGet.config
index f05e91436c2ec5..b8539443065853 100644
--- a/NuGet.config
+++ b/NuGet.config
@@ -19,7 +19,6 @@
     <add key="dotnet-libraries-transport" value="https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-libraries-transport/nuget/v3/index.json" />
     <add key="dotnet10" value="https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet10/nuget/v3/index.json" />
     <add key="dotnet10-transport" value="https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet10-transport/nuget/v3/index.json" />
-    <add key="async-compiler" value="roslynpackages" />
   </packageSources>
   <auditSources>
     <clear />
diff --git a/README.md b/README.md
index a7d5533b6df305..cd95f118df2ffb 100644
--- a/README.md
+++ b/README.md
@@ -1,40 +1,5 @@
-# .NET Runtime - Async2 Experiment
-
-This branch contains experimental fork of CoreCLR [.NET runtime](http://github.com/dotnet/runtime) where we explore implementing async directly in the runtime instead of generating state machines in the IL compiler (Roslyn).
-
-## Current status
-
-We support two prototypes: a JIT-based codegen prototype, and an unwinder-only based prototype.
-The prototypes are described in detail in the design document, see below.
-The former prototype is the default; the latter can be switched to by setting `DOTNET_RuntimeAsyncViaJitGeneratedStateMachines=0`. 
-
-Current support in the prototypes looks like the following:
-
-| **Feature/characteristic**               |  **JIT based state machines**  |  **Unwinder based state machines**  |
-|------------------------------------------|:------------------------------:|:-----------------------------------:|
-| **Generics**                             |               ✅               |                  ❌                 |
-| **Byrefs live across suspension points** |               ❌               |                  ✅                 |
-| **Exception handling**                   |               ✅               |                  ❌                 |
-| **Returns via return buffers**           |               ✅               |                  ❌                 |
-
-
-## Samples
-
-See the tests in [src/tests/Loader/async](src/tests/Loader/async).
-
-## Documentation
-
-- Before building, run the buildroslynugets.cmd script, it will build a variant of the Roslyn compiler that can be used to test this codebase. Otherwise follow the standard developer workflow.
-- [Async Experiment Issue](https://github.com/dotnet/runtime/issues/94620)
-- [Design and details](docs/design/features/runtime-handled-tasks.md)
-
----
-
 # .NET Runtime
 
-In order to build in this repo, you must have set up a Roslyn repo parallel to this repo with the name dotnet-roslyn, and it must have a remote called AzDo which has the updated compiler in it. Then you must run buildroslynnugets.cmd to create a local copy of the compiler for use in the repo. The equivalent work has not yet been done for running on Unix-like platforms.
-
-
 [![Build Status](https://dev.azure.com/dnceng-public/public/_apis/build/status/dotnet/runtime/runtime?branchName=main)](https://dev.azure.com/dnceng-public/public/_build/latest?definitionId=129&branchName=main)
 [![Help Wanted](https://img.shields.io/github/issues/dotnet/runtime/help%20wanted?style=flat-square&color=%232EA043&label=help%20wanted)](https://github.com/dotnet/runtime/labels/help%20wanted)
 [![Good First Issue](https://img.shields.io/github/issues/dotnet/runtime/good%20first%20issue?style=flat-square&color=%232EA043&label=good%20first%20issue)](https://github.com/dotnet/runtime/labels/good%20first%20issue)
diff --git a/buildroslynnugets.cmd b/buildroslynnugets.cmd
deleted file mode 100644
index 983551b3c29755..00000000000000
--- a/buildroslynnugets.cmd
+++ /dev/null
@@ -1,34 +0,0 @@
-setlocal ENABLEEXTENSIONS
-pushd %~dp0
-set ASYNC_ROSLYN_COMMIT=22232ae7939c97984d1158f4f371b0d89ea3db08
-set ASYNC_SUFFIX=async-13
-set ASYNC_ROSLYN_BRANCH=demos/async2-experiment1
-
-cd ..
-if not exist async-roslyn-repo git clone -b %ASYNC_ROSLYN_BRANCH% -o async_roslyn_remote https://github.com/dotnet/roslyn.git async-roslyn-repo
-
-pushd async-roslyn-repo
-
-git fetch async_roslyn_remote %ASYNC_ROSLYN_BRANCH%
-rem when updating this, make sure to update the ASYNC_SUFFIX above and the versions.props file
-git checkout %ASYNC_ROSLYN_COMMIT%
-
-call restore.cmd
-call build.cmd -c release
-
-call dotnet pack src\NuGet\Microsoft.Net.Compilers.Toolset\AnyCpu\Microsoft.Net.Compilers.Toolset.Package.csproj --version-suffix %ASYNC_SUFFIX%
-call dotnet pack src\Workspaces\CSharp\Portable\Microsoft.CodeAnalysis.CSharp.Workspaces.csproj --version-suffix %ASYNC_SUFFIX%
-call dotnet pack src\Compilers\CSharp\Portable\Microsoft.CodeAnalysis.CSharp.csproj --version-suffix %ASYNC_SUFFIX%
-call dotnet pack src\Compilers\Core\Portable\Microsoft.CodeAnalysis.csproj --version-suffix %ASYNC_SUFFIX%
-call dotnet pack src\Workspaces\Core\Portable\Microsoft.CodeAnalysis.Workspaces.csproj --version-suffix %ASYNC_SUFFIX%
-
-pushd %~dp0
-
-md roslynpackages
-
-copy ..\async-roslyn-repo\artifacts\packages\Release\Shipping\Microsoft.Net.Compilers.Toolset.4.14.0-%ASYNC_SUFFIX%.nupkg roslynpackages
-copy ..\async-roslyn-repo\artifacts\packages\Release\Shipping\Microsoft.CodeAnalysis.Workspaces.Common.4.14.0-%ASYNC_SUFFIX%.nupkg roslynpackages
-copy ..\async-roslyn-repo\artifacts\packages\Release\Shipping\Microsoft.CodeAnalysis.CSharp.Workspaces.4.14.0-%ASYNC_SUFFIX%.nupkg roslynpackages
-copy ..\async-roslyn-repo\artifacts\packages\Release\Shipping\Microsoft.CodeAnalysis.CSharp.4.14.0-%ASYNC_SUFFIX%.nupkg roslynpackages
-copy ..\async-roslyn-repo\artifacts\packages\Release\Shipping\Microsoft.CodeAnalysis.Common.4.14.0-%ASYNC_SUFFIX%.nupkg roslynpackages
-
diff --git a/eng/Brewfile b/eng/Brewfile
deleted file mode 100644
index 7a145df5dc637c..00000000000000
--- a/eng/Brewfile
+++ /dev/null
@@ -1,5 +0,0 @@
-brew "cmake"
-brew "icu4c"
-brew "openssl@3"
-brew "pkg-config"
-brew "python3"
diff --git a/eng/DotNetBuild.props b/eng/DotNetBuild.props
index 87a013e43b127f..81d4d6792a1098 100644
--- a/eng/DotNetBuild.props
+++ b/eng/DotNetBuild.props
@@ -38,6 +38,19 @@
     <ShortStack Condition="'$(DotNetBuildMonoEnableLLVM)' == 'true' or '$(DotNetBuildMonoAOTEnableLLVM)' == 'true'">true</ShortStack>
   </PropertyGroup>
 
+  <!--
+    Allow the VMR orchestrator to control whether or not to build rid-specific artifacts,
+    but provide defaults until the VMR orchestrator provides controls in all scenarios.
+  -->
+  <PropertyGroup Condition="'$(EnableDefaultRidSpecificArtifacts)' == ''">
+    <!-- Source-build always needs all artifacts to be published. -->
+    <EnableDefaultRidSpecificArtifacts Condition="'$(DotNetBuildSourceOnly)' != ''">false</EnableDefaultRidSpecificArtifacts>
+    <!-- Short-stack builds should always only publish RID-specific artifacts. -->
+    <EnableDefaultRidSpecificArtifacts Condition="'$(ShortStack)' == 'true'">true</EnableDefaultRidSpecificArtifacts>
+    <!-- If no override is specified, don't use RID-specific publishing. Instead, publish everything. -->
+    <EnableDefaultRidSpecificArtifacts Condition="'$(EnableDefaultRidSpecificArtifacts)' == ''">false</EnableDefaultRidSpecificArtifacts>
+  </PropertyGroup>
+
   <Target Name="GetRuntimeSourceBuildCommandConfiguration"
           BeforeTargets="GetSourceBuildCommandConfiguration">
     <PropertyGroup>
@@ -86,6 +99,8 @@
       <InnerBuildArgs Condition="'$(DefaultArtifactVisibility)' != ''">$(InnerBuildArgs) /p:DefaultArtifactVisibility=$(DefaultArtifactVisibility)</InnerBuildArgs>
       <InnerBuildArgs Condition="'$(DotNetEsrpToolPath)' != ''">$(InnerBuildArgs) /p:DotNetEsrpToolPath=$(DotNetEsrpToolPath)</InnerBuildArgs>
       <InnerBuildArgs Condition="'$(DotNetBuildTests)' != ''">$(InnerBuildArgs) /p:DotNetBuildTests=$(DotNetBuildTests)</InnerBuildArgs>
+      <InnerBuildArgs Condition="'$(PublishingVersion)' != ''">$(InnerBuildArgs) /p:PublishingVersion=$(PublishingVersion)</InnerBuildArgs>
+      <InnerBuildArgs Condition="'$(RepositoryUrl)' != ''">$(InnerBuildArgs) /p:RepositoryUrl=$(RepositoryUrl)</InnerBuildArgs>
 
       <!-- Pass locations for assets, packages and symbols -->
       <InnerBuildArgs Condition="'$(SourceBuiltAssetsDir)' != ''">$(InnerBuildArgs) /p:SourceBuiltAssetsDir=$(SourceBuiltAssetsDir)</InnerBuildArgs>
@@ -107,6 +122,8 @@
 
       <!-- Needed until https://github.com/dotnet/runtime/issues/109329 is fixed. -->
       <InnerBuildArgs Condition="'$(NetCoreAppToolCurrentVersion)' != ''">$(InnerBuildArgs) /p:NetCoreAppToolCurrentVersion=$(NetCoreAppToolCurrentVersion)</InnerBuildArgs>
+
+      <InnerBuildArgs Condition="'$(EnableDefaultRidSpecificArtifacts)' != ''">$(InnerBuildArgs) /p:EnableDefaultRidSpecificArtifacts=$(EnableDefaultRidSpecificArtifacts)</InnerBuildArgs>
     </PropertyGroup>
   </Target>
 
diff --git a/eng/OSArch.props b/eng/OSArch.props
new file mode 100644
index 00000000000000..d1be0745175825
--- /dev/null
+++ b/eng/OSArch.props
@@ -0,0 +1,35 @@
+<Project>
+  <PropertyGroup Label="CalculateTargetOS">
+    <_hostOS>linux</_hostOS>
+    <_hostOS Condition="$([MSBuild]::IsOSPlatform('OSX'))">osx</_hostOS>
+    <_hostOS Condition="$([MSBuild]::IsOSPlatform('FREEBSD'))">freebsd</_hostOS>
+    <_hostOS Condition="$([MSBuild]::IsOSPlatform('NETBSD'))">netbsd</_hostOS>
+    <_hostOS Condition="$([MSBuild]::IsOSPlatform('ILLUMOS'))">illumos</_hostOS>
+    <_hostOS Condition="$([MSBuild]::IsOSPlatform('SOLARIS'))">solaris</_hostOS>
+    <_hostOS Condition="$([MSBuild]::IsOSPlatform('HAIKU'))">haiku</_hostOS>
+    <_hostOS Condition="$([MSBuild]::IsOSPlatform('WINDOWS'))">windows</_hostOS>
+    <HostOS>$(_hostOS)</HostOS>
+    <TargetOS Condition="'$(TargetOS)' == '' and '$(RuntimeIdentifier)' == 'browser-wasm'">browser</TargetOS>
+    <TargetOS Condition="'$(TargetOS)' == ''">$(_hostOS)</TargetOS>
+    <TargetsMobile Condition="'$(TargetOS)' == 'ios' or '$(TargetOS)' == 'iossimulator' or '$(TargetOS)' == 'maccatalyst' or '$(TargetOS)' == 'tvos' or '$(TargetOS)' == 'tvossimulator' or '$(TargetOS)' == 'android' or '$(TargetOS)' == 'browser' or '$(TargetOS)' == 'wasi'">true</TargetsMobile>
+    <TargetsAppleMobile Condition="'$(TargetOS)' == 'ios' or '$(TargetOS)' == 'iossimulator' or '$(TargetOS)' == 'maccatalyst' or '$(TargetOS)' == 'tvos' or '$(TargetOS)' == 'tvossimulator'">true</TargetsAppleMobile>
+  </PropertyGroup>
+
+  <!-- Platform property is required by RepoLayout.props in Arcade SDK. -->
+  <PropertyGroup Label="CalculateArch">
+    <_hostArch>$([System.Runtime.InteropServices.RuntimeInformation]::ProcessArchitecture.ToString().ToLowerInvariant)</_hostArch>
+    <BuildArchitecture Condition="'$(BuildArchitecture)' == ''">$(_hostArch)</BuildArchitecture>
+    <TargetArchitecture Condition="'$(TargetArchitecture)' == '' and ('$(TargetOS)' == 'browser' or '$(RuntimeIdentifier)' == 'browser-wasm')">wasm</TargetArchitecture>
+    <TargetArchitecture Condition="'$(TargetArchitecture)' == '' and ('$(TargetOS)' == 'wasi' or '$(RuntimeIdentifier)' == 'wasi-wasm')">wasm</TargetArchitecture>
+    <TargetArchitecture Condition="'$(TargetArchitecture)' == '' and '$(_hostArch)' == 'arm'">arm</TargetArchitecture>
+    <TargetArchitecture Condition="'$(TargetArchitecture)' == '' and '$(_hostArch)' == 'armv6'">armv6</TargetArchitecture>
+    <TargetArchitecture Condition="'$(TargetArchitecture)' == '' and '$(_hostArch)' == 'armel'">armel</TargetArchitecture>
+    <TargetArchitecture Condition="'$(TargetArchitecture)' == '' and '$(_hostArch)' == 'arm64'">arm64</TargetArchitecture>
+    <TargetArchitecture Condition="'$(TargetArchitecture)' == '' and '$(_hostArch)' == 'loongarch64'">loongarch64</TargetArchitecture>
+    <TargetArchitecture Condition="'$(TargetArchitecture)' == '' and '$(_hostArch)' == 's390x'">s390x</TargetArchitecture>
+    <TargetArchitecture Condition="'$(TargetArchitecture)' == '' and '$(_hostArch)' == 'ppc64le'">ppc64le</TargetArchitecture>
+    <TargetArchitecture Condition="'$(TargetArchitecture)' == '' and '$(TargetsMobile)' == 'true'">x64</TargetArchitecture>
+    <TargetArchitecture Condition="'$(TargetArchitecture)' == ''">x64</TargetArchitecture>
+    <Platform Condition="'$(Platform)' == '' and '$(InferPlatformFromTargetArchitecture)' == 'true'">$(TargetArchitecture)</Platform>
+  </PropertyGroup>
+</Project>
\ No newline at end of file
diff --git a/eng/Publishing.props b/eng/Publishing.props
index da3d606ed683c4..dbd80cac884d17 100644
--- a/eng/Publishing.props
+++ b/eng/Publishing.props
@@ -1,153 +1,62 @@
-<Project InitialTargets="ValidateDownloadedAssets">
-
+<Project>
   <PropertyGroup>
     <ProducesDotNetReleaseShippingAssets>true</ProducesDotNetReleaseShippingAssets>
     <!-- This avoids creating VS.*.symbols.nupkg packages that are identical to the original package. -->
     <AutoGenerateSymbolPackages>false</AutoGenerateSymbolPackages>
-    <!-- Set PlatformName to TargetArchitecture to create unique build manifest files. -->
-    <PlatformName Condition="'$(TargetArchitecture)' != ''">$(TargetArchitecture)</PlatformName>
   </PropertyGroup>
 
-  <!--
-    Mark assets as Vertical visibility when building in the VMR
-  -->
-  <ItemGroup Condition="'$(DotNetBuildOrchestrator)' == 'true'">
-    <Artifact Condition="'$(OutputRID)' != '$(NETCoreSdkRuntimeIdentifier)'"
-              Update="$(ArtifactsPackagesDir)**\runtime.$(NETCoreSdkRuntimeIdentifier).Microsoft.NETCore.ILAsm.*.nupkg"
-              Visibility="Vertical"
-              IsShipping="false" />
-    <Artifact Condition="'$(OutputRID)' != '$(NETCoreSdkRuntimeIdentifier)'"
-              Update="$(ArtifactsPackagesDir)**\runtime.$(NETCoreSdkRuntimeIdentifier).Microsoft.NETCore.ILDAsm.*.nupkg"
-              Visibility="Vertical"
-              IsShipping="false" />
-    <Artifact Condition="'$(RuntimeFlavor)' != 'Mono' and '$(OutputRID)' != '$(NETCoreSdkRuntimeIdentifier)'"
-              Update="$(ArtifactsPackagesDir)**\Microsoft.NETCore.App.Crossgen2.$(NETCoreSdkRuntimeIdentifier).*.nupkg"
-              Visibility="Vertical"
-              IsShipping="false" />
-    <Artifact Condition="'$(RuntimeFlavor)' != 'Mono' and '$(OutputRID)' != '$(NETCoreSdkRuntimeIdentifier)'"
-              Update="$(ArtifactsPackagesDir)**\runtime.$(NETCoreSDKRuntimeIdentifier).Microsoft.DotNet.ILCompiler.*.nupkg"
-              Visibility="Vertical"
-              IsShipping="false" />
+  <PropertyGroup Condition="'$(DotNetFinalPublish)' != 'true' and '$(DotNetBuild)' != 'true'">
+    <PushToLocalStorage>true</PushToLocalStorage>
+    <ArtifactsStagingDir>$(ArtifactsDir)staging/</ArtifactsStagingDir>
+    <SourceBuiltPdbArtifactsDir>$(ArtifactsStagingDir)SymStore</SourceBuiltPdbArtifactsDir>
+    <!-- Put blobs where we put packages so our regular globbing in our publish join job finds them. -->
+    <SourceBuiltAssetsDir>$(ArtifactsStagingDir)</SourceBuiltAssetsDir>
+    <SourceBuiltShippingPackagesDir>$(ArtifactsStagingDir)packages/$(Configuration)/Shipping</SourceBuiltShippingPackagesDir>
+    <SourceBuiltNonShippingPackagesDir>$(ArtifactsStagingDir)packages/$(Configuration)/NonShipping</SourceBuiltNonShippingPackagesDir>
+    <PublishingUseHardlinksIfPossible>true</PublishingUseHardlinksIfPossible>
+    <!-- We don't use the asset manifest from the vertical dotnet/runtime legs outside of the VMR. Just dump it in a temp dir. -->
+    <SourceBuiltAssetManifestsDir>$(ArtifactsTmpDir)/manifests/$(Configuration)</SourceBuiltAssetManifestsDir>
+    <AssetManifestName>VerticalManifest.xml</AssetManifestName>
+  </PropertyGroup>
+
+  <!-- In the runtime official build, we only sign a subset of files. Add all of our blob artifacts here again for publishing -->
+  <ItemGroup Condition="'$(DotNetFinalPublish)' == 'true'">
+    <Artifact Include="$(ArtifactsPackagesDir)**\*.tar.gz;
+                       $(ArtifactsPackagesDir)**\*.zip;
+                       $(ArtifactsPackagesDir)**\*.deb;
+                       $(ArtifactsPackagesDir)**\*.rpm;
+                       $(ArtifactsPackagesDir)**\*.pkg;
+                       $(ArtifactsPackagesDir)**\*.exe;
+                       $(ArtifactsPackagesDir)**\*.msi;"
+              Exclude="$(ArtifactsPackagesDir)**\Symbols.runtime.tar.gz"
+              Kind="Blob"
+              IsShipping="$([System.String]::Copy('%(RecursiveDir)').StartsWith('Shipping'))">
+      <!-- Exclude wixpack.zip files from checksum generation -->
+      <ChecksumPath Condition="$([System.String]::Copy('%(Filename)%(Extension)').EndsWith('.wixpack.zip')) != 'true'">%(FullPath).sha512</ChecksumPath>
+    </Artifact>
   </ItemGroup>
 
   <!--
-    Look through the downloaded artifacts to figure out how to ship them. Creates item groups for
-    other types of publishing to use.
-
-    DownloadDirectory is expected to have some directory inside, which then contains a dir for each
-    build job name. For example, this nupkg would be found:
-
-      $(DownloadDirectory)IntermediateArtifacts\windows_x64\Shipping\a.nupkg
+    Mark host-RID-targeting assets as Vertical visibility when building in the VMR.
+    We can't use NETCoreSdkRuntimeIdentifier here as the Arcade SDK projects don't import the .NET SDK.
+    Instead, just make sure we include the assets targeting "not the output rid", which will catch the host assets.
   -->
-  <ItemGroup Condition="'$(DotNetBuildRepo)' != 'true'">
-    <DownloadedArtifactFile Include="$(DownloadDirectory)**" />
-    <DownloadedSymbolNupkgFile Include="$(DownloadDirectory)**\*.symbols.nupkg" />
-    <DownloadedWixPdbFile Include="$(DownloadDirectory)**\*.wixpdb" />
-    <DownloadedWixpackFile Include="$(DownloadDirectory)**\*.wixpack.zip" Condition="'$(PostBuildSign)' != 'true'" />
-    <DownloadedWorkloadsVSInsertionFile Include="$(DownloadDirectory)*\workloads-vs\**\*" />
-    <DownloadedNupkgFile Include="$(DownloadDirectory)**\*.nupkg" Exclude="@(DownloadedSymbolNupkgFile)" />
-
-    <!-- Exclude symbol packages from have a NuGet signature. These are never pushed to NuGet.org or
-          other feeds (in fact, that have identical identity to their non-symbol variant) -->
-    <DownloadedSymbolPackagesWithoutPaths Include="@(DownloadedSymbolNupkgFile->'%(Filename)%(Extension)')" />
-    <FileSignInfo Include="@(DownloadedSymbolPackagesWithoutPaths->Distinct())" CertificateName="None" />
-
-    <!-- Add files that are not affected by filtering and create checksum for them. -->
-    <UploadToBlobStorageFile
-      Include="@(DownloadedArtifactFile)"
-      Exclude="@(DownloadedSymbolNupkgFile);
-                @(DownloadedNupkgFile);
-                @(DownloadedWixPdbFile);
-                @(DownloadedWorkloadsVSInsertionFile);
-                @(DownloadedWixpackFile)" />
-
-    <!--
-      Filter out the RID-specific (Runtime) nupkgs and RID-agnostic nupkgs. RID-specific packages
-      are published from every job. RID-agnostic nupkgs are built with the same ID/version by
-      every job, so one specific job's outputs must be picked to sign and publish.
-    -->
+  <ItemGroup Condition="'$(EnableDefaultRidSpecificArtifacts)' != 'true' and '$(DotNetBuildOrchestrator)' == 'true'">
+    <_HostArtifact Include="$(ArtifactsPackagesDir)**\runtime.*.Microsoft.NETCore.ILAsm.*.nupkg"
+                    Exclude="$(ArtifactsPackagesDir)**\runtime.$(OutputRID).Microsoft.NETCore.ILAsm.*.nupkg" />
 
-    <!-- RID-specific framework packs. -->
-    <RuntimeNupkgFile
-      Include="
-        $(DownloadDirectory)**\Microsoft.*.Runtime.*.nupkg;
-        $(DownloadDirectory)**\Microsoft.*.App.Host.*.nupkg;
-        $(DownloadDirectory)**\Microsoft.*.App.Crossgen2.*.nupkg"
-      Exclude="@(DownloadedSymbolNupkgFile)" />
+    <_HostArtifact Include="$(ArtifactsPackagesDir)**\runtime.*.Microsoft.NETCore.ILDAsm.*.nupkg"
+                    Exclude="$(ArtifactsPackagesDir)**\runtime.$(OutputRID).Microsoft.NETCore.ILDAsm.*.nupkg" />
 
-    <!-- VS insertion packages, carrying RID-specific installers. -->
-    <RuntimeNupkgFile
-      Include="$(DownloadDirectory)**\VS.Redist.Common.*.nupkg"
-      Exclude="@(DownloadedSymbolNupkgFile)" />
+    <_HostArtifact Include="$(ArtifactsPackagesDir)**\runtime.*.Microsoft.DotNet.ILCompiler.*.nupkg"
+                    Exclude="$(ArtifactsPackagesDir)**\runtime.$(OutputRID).Microsoft.DotNet.ILCompiler.*.nupkg" />
 
-    <!--
-      Workloads VS insertion artifacts produced by src/workloads/workloads.csproj. Only grab
-      the zip artifacts as they're grouped by SDK feature band which correlates with specific VS versions.
-    -->
-    <WorkloadsVSInsertionFile Include="$(DownloadDirectory)*\workloads-vs\**\*.zip" />
+    <_HostArtifact Include="$(ArtifactsPackagesDir)**\Microsoft.NETCore.App.Crossgen2.*.nupkg"
+                    Exclude="$(ArtifactsPackagesDir)**\Microsoft.NETCore.App.Crossgen2.$(OutputRID).*.nupkg" />
 
-    <!--
-      Runtime packages associated with some identity packages. Need to exclude "runtime.native.*"
-      because Libraries produces some "runtime.native.Foo" packages with
-      "runtime.<rid>.runtime.native.Foo" identity packages.
-    -->
-    <RuntimeNupkgFile
-      Include="$(DownloadDirectory)**\runtime.*.nupkg"
-      Exclude="
-        $(DownloadDirectory)**\runtime.native.*.nupkg;
-        @(DownloadedSymbolNupkgFile)" />
-
-    <!--
-      Packages that aren't matched above as RID-specific are considered RID-agnostic. Also include
-      the packages from the Libraries build.
-    -->
-    <RidAgnosticNupkgToPublishFile
-      Include="
-        $(DownloadDirectory)**\Microsoft.NET.Workload.Mono.Toolchain.*Manifest-*.nupkg;
-        $(DownloadDirectory)**\Microsoft.NET.Sdk.WebAssembly.Pack.*.nupkg;
-        $(DownloadDirectory)*\$(PublishRidAgnosticPackagesFromPlatform)\**\*.nupkg;
-        $(DownloadDirectory)*\Libraries_WithPackages\**\*.nupkg"
-      Exclude="@(RuntimeNupkgFile);@(DownloadedSymbolNupkgFile)" />
-
-    <TransportPackagesToPublishFile
-      Include="$(DownloadDirectory)**\*Transport*.nupkg"
-      Exclude="@(RuntimeNupkgFile);@(RidAgnosticNupkgToPublishFile);@(DownloadedSymbolNupkgFile)" />
-
-    <NupkgToPublishFile Include="@(RuntimeNupkgFile);@(RidAgnosticNupkgToPublishFile);@(TransportPackagesToPublishFile)" />
-
-    <!--
-      Assuming all symbol packages ship and can be found by turning .nupkg => .symbols.nupkg, find
-      them. Don't check for missing symbol packages here: some nupkgs don't have them for valid
-      reasons, such as the VS insertion packages that transport MSIs. Symbol package validation
-      will check for symbol completeness with file-by-file granularity rather than looking for
-      missing symbols.nupkg files: https://github.com/dotnet/arcade/issues/2499.
-
-      Handles several conventions:
-      * NonShipping packages have symbol nupkgs that are Shipping.
-      * Shipping packages have symbol packages in a "symbols" subdirectory.
-    -->
-    <PotentialSymbolNupkgToPublishFile
-      Include="
-        @(NupkgToPublishFile->Replace('\NonShipping\', '\Shipping\')->Replace('.nupkg', '.symbols.nupkg'));
-        @(NupkgToPublishFile->Replace('\NonShipping\', '\NonShipping\symbols\')->Replace('.nupkg', '.symbols.nupkg'));
-        @(NupkgToPublishFile->Replace('\Shipping\', '\Shipping\symbols\')->Replace('.nupkg', '.symbols.nupkg'))" />
-
-    <SymbolNupkgToPublishFile Include="@(PotentialSymbolNupkgToPublishFile->Distinct()->Exists())" />
-
-    <!-- Packages -->
-    <Artifact Include="@(NupkgToPublishFile)"
-              IsShipping="$([System.String]::new('%(Identity)').Contains('\Shipping\'))"
-              PublishFlatContainer="false" />
-    <Artifact Include="@(SymbolNupkgToPublishFile)" PublishFlatContainer="false" />
-
-    <!-- Blob storage -->
-    <Artifact Include="@(UploadToBlobStorageFile)"
-              Exclude="@(NupkgToPublishFile);@(SymbolNupkgToPublishFile)"
-              IsShipping="$([System.String]::new('%(Identity)').Contains('\Shipping\'))"
-              ChecksumPath="%(FullPath).sha512" />
-    <Artifact Include="@(WorkloadsVSInsertionFile)"
-              SubBlobFolder="workloads/"
-              IsShipping="$([System.String]::new('%(Identity)').Contains('\Shipping\'))" />
+    <Artifact Update="@(_HostArtifact)"
+              Visibility="Vertical"
+              IsShipping="false" />
   </ItemGroup>
 
   <Target Name="GetNonStableProductVersion">
@@ -168,15 +77,15 @@
     This ensures that we don't produce these files in the "Repo source build" builds,
     but we do produce them in both the VMR and the runtime official build.
   -->
-  <PropertyGroup>
-    <_ShouldGenerateProductVersionFiles Condition="'$(DotNetBuildRepo)' == 'true' and '$(DotNetBuildOrchestrator)' == 'true'">true</_ShouldGenerateProductVersionFiles>
-    <_ShouldGenerateProductVersionFiles Condition="'$(DotNetBuildRepo)' != 'true' and '$(DotNetBuildOrchestrator)' != 'true'">true</_ShouldGenerateProductVersionFiles>
+  <PropertyGroup Condition="'$(DotNetBuildOrchestrator)' == 'true'">
+    <ShouldGenerateProductVersionFiles Condition="'$(PackageRID)' == 'win-x64' and ('$(DotNetBuildPass)' == '' or '$(DotNetBuildPass)' == '1')">true</ShouldGenerateProductVersionFiles>
+    <ShouldGenerateProductVersionFiles Condition="'$(DotNetBuildSourceOnly)' == 'true'">true</ShouldGenerateProductVersionFiles>
   </PropertyGroup>
 
   <Target Name="GenerateProductVersionFiles"
           DependsOnTargets="GetNonStableProductVersion"
           BeforeTargets="PublishToAzureDevOpsArtifacts"
-          Condition="'$(_ShouldGenerateProductVersionFiles)' == 'true'">
+          Condition="'$(ShouldGenerateProductVersionFiles)' == 'true'">
     <!-- Retrieve the runtime pack product version. -->
     <MSBuild Projects="$(RepoRoot)src/installer/pkg/sfx/Microsoft.NETCore.App/Microsoft.NETCore.App.Runtime.CoreCLR.sfxproj"
              Targets="ReturnProductVersion"
@@ -206,23 +115,23 @@
           DependsOnTargets="GetNonStableProductVersion"
           BeforeTargets="PublishToAzureDevOpsArtifacts"
           AfterTargets="GenerateChecksumsFromArtifacts">
-    <ItemGroup>
-      <Artifact Condition="'%(Artifact.PublishFlatContainer)' == 'true' and '%(Artifact.RelativeBlobPath)' == ''"
+    <ItemGroup Condition="'$(DotNetFinalPublish)' == 'true' or '$(DotNetBuildOrchestrator)' == 'true'">
+      <Artifact Condition="'%(Artifact.Kind)' != 'Package' and '%(Artifact.RelativeBlobPath)' == ''"
                 RelativeBlobPath="Runtime/$(NonStableProductVersion)/%(Artifact.SubBlobFolder)%(Filename)%(Extension)" />
     </ItemGroup>
+    <!-- In vertical dotnet/runtime legs outside of the VMR, set the "relative blob path" to shipping vs non-shipping to make our download/upload work. -->
+    <ItemGroup Condition="'$(DotNetFinalPublish)' != 'true' and '$(DotNetBuildOrchestrator)' != 'true'">
+      <Artifact Condition="'%(Artifact.Kind)' == 'Blob' and '%(Artifact.IsShipping)' == 'true'"
+                RelativeBlobPath="packages/$(Configuration)/Shipping/%(Filename)%(Extension)" />
+      <Artifact Condition="'%(Artifact.Kind)' == 'Blob' and '%(Artifact.IsShipping)' != 'true'"
+                RelativeBlobPath="packages/$(Configuration)/NonShipping/%(Filename)%(Extension)" />
+      <ItemsToPushToBlobFeed Condition="'%(ItemsToPushToBlobFeed.Kind)' == 'Blob' and '%(ItemsToPushToBlobFeed.IsShipping)' == 'true'"
+                             RelativeBlobPath="packages/$(Configuration)/Shipping/%(Filename)%(Extension)" />
+      <ItemsToPushToBlobFeed Condition="'%(ItemsToPushToBlobFeed.Kind)' == 'Blob' and '%(ItemsToPushToBlobFeed.IsShipping)' != 'true'"
+                             RelativeBlobPath="packages/$(Configuration)/NonShipping/%(Filename)%(Extension)" />
+
+      <Artifact Condition="'%(Artifact.IsVSSetup)' == 'true'"
+                RelativeBlobPath="VSSetup/%(Filename)%(Extension)" />
+    </ItemGroup>
   </Target>
-
-  <Target Name="ValidateDownloadedAssets" Condition="'$(DotNetBuildRepo)' != 'true'">
-    <Error Text="DownloadDirectory is not set." Condition="'$(DownloadDirectory)' == ''" />
-    <Error Condition="'@(SymbolNupkgToPublishFile)' == ''" Text="No symbol packages found." />
-
-    <!--
-      Duplicate RuntimeNupkgFile items mean artifact upload will fail, but only after another hour
-      of signing. Detect this early. It's possible to automatically "fix" this with Distinct(),
-      however the patterns should be fairly specific: this is likely a build infra mistake that
-      should be corrected.
-    -->
-    <Error Text="Duplicate RuntimeNupkgFile entries for: %(RuntimeNupkgFile.Identity)" Condition="@(RuntimeNupkgFile->Count()) &gt; 1" />
-  </Target>
-
 </Project>
diff --git a/eng/RuntimeIdentifier.props b/eng/RuntimeIdentifier.props
new file mode 100644
index 00000000000000..9ebd5e65194874
--- /dev/null
+++ b/eng/RuntimeIdentifier.props
@@ -0,0 +1,100 @@
+<Project>
+  <PropertyGroup Label="CalculatePortableBuild">
+    <PortableBuild Condition="'$(PortableBuild)' == '' and '$(DotNetBuildSourceOnly)' == 'true'">false</PortableBuild>
+    <PortableBuild Condition="'$(PortableBuild)' == ''">true</PortableBuild>
+  </PropertyGroup>
+
+  <!-- _portableOS is the portable rid-OS corresponding to the target platform. -->
+  <PropertyGroup Label="CalculatePortableOS">
+    <!-- To determine _portableOS we use TargetOS.
+         TargetOS is not a rid-OS. For example: for Windows it is 'windows' instead of 'win'.
+         And, for flavors of Linux, like 'linux-musl' and 'linux-bionic', TargetOS is 'linux'. -->
+
+    <_portableOS>$(TargetOS.ToLowerInvariant())</_portableOS>
+    <_portableOS Condition="'$(_portableOS)' == 'windows'">win</_portableOS>
+
+    <!-- TargetOS=AnyOS is a sentinel value used by tests, ignore it.  -->
+    <_portableOS Condition="'$(_portableOS)' == 'anyos'">$(__PortableTargetOS)</_portableOS>
+
+    <!-- Detect linux flavors using __PortableTargetOS from the native script. -->
+    <_portableOS Condition="'$(_portableOS)' == 'linux' and '$(__PortableTargetOS)' == 'linux-musl'">linux-musl</_portableOS>
+    <_portableOS Condition="'$(_portableOS)' == 'linux' and '$(__PortableTargetOS)' == 'linux-bionic'">linux-bionic</_portableOS>
+
+    <!-- On Windows, we can build for Windows and Mobile.
+         For other TargetOSes, create a "win" build, built from TargetOS sources and "win" pre-built packages. -->
+    <_portableOS Condition="'$(HostOS)' == 'win' and '$(TargetsMobile)' != 'true'">win</_portableOS>
+  </PropertyGroup>
+
+  <!-- PackageRID is used for packages needed for the target. -->
+  <PropertyGroup Label="CalculatePackageRID">
+    <_packageOS>$(_portableOS)</_packageOS>
+
+    <_packageOS Condition="'$(CrossBuild)' == 'true' and '$(_portableOS)' != 'linux-musl' and '$(_portableOS)' != 'linux-bionic' and '$(_portableOS)' != 'android'">$(_hostOS)</_packageOS>
+
+    <!-- source-build sets PackageOS to build with non-portable rid packages that were source-built previously. -->
+    <PackageRID Condition="'$(PackageOS)' != ''">$(PackageOS)-$(TargetArchitecture)</PackageRID>
+    <PackageRID Condition="'$(PackageRID)' == ''">$(_packageOS)-$(TargetArchitecture)</PackageRID>
+  </PropertyGroup>
+
+  <!-- ToolsRID is used for packages needed on the build host. -->
+  <PropertyGroup Label="CalculateToolsRID">
+    <!-- _portableHostOS is the portable rid-OS corresponding to the build host platform.
+
+         To determine _portableHostOS we use _hostOS, similar to how _portableOS is calculated from TargetOS.
+
+         When we're not cross-building we can detect linux flavors by looking at _portableOS
+         because the target platform and the build host platform are the same.
+         For cross-builds, we're currently unable to detect the flavors. -->
+    <_portableHostOS>$(_hostOS)</_portableHostOS>
+    <_portableHostOS Condition="'$(_portableHostOS)' == 'windows'">win</_portableHostOS>
+    <_portableHostOS Condition="'$(CrossBuild)' != 'true' and '$(_portableOS)' == 'linux-musl'">linux-musl</_portableHostOS>
+
+    <!-- source-build sets ToolsOS to build with non-portable rid packages that were source-built previously. -->
+    <ToolsRID Condition="'$(ToolsOS)' != ''">$(ToolsOS)-$(_hostArch)</ToolsRID>
+    <ToolsRID Condition="'$(ToolsRID)' == ''">$(_portableHostOS)-$(_hostArch)</ToolsRID>
+
+    <!-- Microsoft.NET.Sdk.IL SDK defaults to the portable host rid. Match it to ToolsRID (for source-build). -->
+    <MicrosoftNetCoreIlasmPackageRuntimeId>$(ToolsRID)</MicrosoftNetCoreIlasmPackageRuntimeId>
+  </PropertyGroup>
+
+  <!-- OutputRID is used to name the target platform.
+       For portable builds, OutputRID matches _portableOS.
+       For non-portable builds, it uses __DistroRid (from the native build script), or falls back to RuntimeInformation.RuntimeIdentifier.
+       Source-build sets OutputRID directly. -->
+  <PropertyGroup Label="CalculateOutputRID">
+    <_hostRid Condition="'$(MSBuildRuntimeType)' == 'core'">$([System.Runtime.InteropServices.RuntimeInformation]::RuntimeIdentifier)</_hostRid>
+    <_hostRid Condition="'$(MSBuildRuntimeType)' != 'core'">win-$([System.Runtime.InteropServices.RuntimeInformation]::OSArchitecture.ToString().ToLowerInvariant)</_hostRid>
+
+    <_parseDistroRid>$(__DistroRid)</_parseDistroRid>
+    <_parseDistroRid Condition="'$(_parseDistroRid)' == ''">$(_hostRid)</_parseDistroRid>
+    <_distroRidIndex>$(_parseDistroRid.LastIndexOf('-'))</_distroRidIndex>
+
+    <_outputOS>$(_parseDistroRid.SubString(0, $(_distroRidIndex)))</_outputOS>
+    <_outputOS Condition="'$(PortableBuild)' == 'true'">$(_portableOS)</_outputOS>
+
+    <OutputRID Condition="'$(OutputRID)' == ''">$(_outputOS)-$(TargetArchitecture)</OutputRID>
+  </PropertyGroup>
+
+  <PropertyGroup Label="CalculateTargetOSName">
+    <TargetsFreeBSD Condition="'$(TargetOS)' == 'freebsd'">true</TargetsFreeBSD>
+    <Targetsillumos Condition="'$(TargetOS)' == 'illumos'">true</Targetsillumos>
+    <TargetsSolaris Condition="'$(TargetOS)' == 'solaris'">true</TargetsSolaris>
+    <TargetsHaiku Condition="'$(TargetOS)' == 'haiku'">true</TargetsHaiku>
+    <TargetsLinux Condition="'$(TargetOS)' == 'linux' or '$(TargetOS)' == 'android'">true</TargetsLinux>
+    <TargetsLinuxBionic Condition="'$(_portableOS)' == 'linux-bionic'">true</TargetsLinuxBionic>
+    <TargetsLinuxMusl Condition="'$(_portableOS)' == 'linux-musl'">true</TargetsLinuxMusl>
+    <TargetsLinuxGlibc Condition="'$(TargetsLinux)' == 'true' and '$(TargetsLinuxMusl)' != 'true' and '$(TargetsLinuxBionic)' != 'true'">true</TargetsLinuxGlibc>
+    <TargetsNetBSD Condition="'$(TargetOS)' == 'netbsd'">true</TargetsNetBSD>
+    <TargetsOSX Condition="'$(TargetOS)' == 'osx'">true</TargetsOSX>
+    <TargetsMacCatalyst Condition="'$(TargetOS)' == 'maccatalyst'">true</TargetsMacCatalyst>
+    <TargetsiOS Condition="'$(TargetOS)' == 'ios' or '$(TargetOS)' == 'iossimulator'">true</TargetsiOS>
+    <TargetstvOS Condition="'$(TargetOS)' == 'tvos' or '$(TargetOS)' == 'tvossimulator'">true</TargetstvOS>
+    <TargetsiOSSimulator Condition="'$(TargetOS)' == 'iossimulator'">true</TargetsiOSSimulator>
+    <TargetstvOSSimulator Condition="'$(TargetOS)' == 'tvossimulator'">true</TargetstvOSSimulator>
+    <TargetsAndroid Condition="'$(TargetOS)' == 'android'">true</TargetsAndroid>
+    <TargetsBrowser Condition="'$(TargetOS)' == 'browser'">true</TargetsBrowser>
+    <TargetsWasi Condition="'$(TargetOS)' == 'wasi'">true</TargetsWasi>
+    <TargetsWindows Condition="'$(TargetOS)' == 'windows'">true</TargetsWindows>
+    <TargetsUnix Condition="'$(TargetsFreeBSD)' == 'true' or '$(Targetsillumos)' == 'true' or '$(TargetsSolaris)' == 'true' or '$(TargetsHaiku)' == 'true' or '$(TargetsLinux)' == 'true' or '$(TargetsNetBSD)' == 'true' or '$(TargetsOSX)' == 'true' or '$(TargetsMacCatalyst)' == 'true' or '$(TargetstvOS)' == 'true' or '$(TargetsiOS)' == 'true' or '$(TargetsAndroid)' == 'true'">true</TargetsUnix>
+  </PropertyGroup>
+</Project>
\ No newline at end of file
diff --git a/eng/Signing.props b/eng/Signing.props
index 12fc319128c6a6..d06028e5d5e24b 100644
--- a/eng/Signing.props
+++ b/eng/Signing.props
@@ -1,6 +1,35 @@
 <Project>
+  <Import Project="$(MSBuildThisFileDirectory)OSArch.props" />
+  <Import Project="$(MSBuildThisFileDirectory)RuntimeIdentifier.props" />
+
   <PropertyGroup>
-    <EnableDefaultArtifacts Condition="'$(DotNetBuild)' != 'true'">false</EnableDefaultArtifacts>
+    <!--
+      By default, we will use RID-specific package publishing.
+      Every job will publish their RID-specific packages.
+      Some RIDs also have OS or arch-specific packages that are published by that RID.
+
+      For jobs that don't meet the above rules, we have various controls:
+
+      - A job can specify EnableDefaultRidSpecificArtifacts=false as a global property to publish all packages it produces.
+        We have specific jobs that produce RID-agnostic packages or packages for multiple RIDs set this property.
+      - Mono AOT LLVM builds only publish LLVM-specific packages always.
+
+      VMR jobs can control whether or not a vertical has EnableDefaultRidSpecificArtifacts set to true or false in DotNetBuild.props.
+
+      Packages that do not meet the above rules are added with Vertical visibility in the VMR and excluded in non-VMR builds.
+    -->
+    <EnableDefaultArtifacts Condition="'$(MonoAOTEnableLLVM)' == 'true'">false</EnableDefaultArtifacts>
+    <EnableBlobArtifacts>true</EnableBlobArtifacts>
+    <EnableBlobArtifacts Condition="'$(MonoAOTEnableLLVM)' == 'true'">false</EnableBlobArtifacts>
+
+    <!-- Set the TargetRid property to our PackageRID, which is the right RID to use for selecting packages from our repo. -->
+    <TargetRid>$(PackageRID)</TargetRid>
+
+    <!-- The final stage of the runtime official build should publish everything. -->
+    <EnableDefaultRidSpecificArtifacts Condition="'$(DotNetFinalPublish)' != ''">false</EnableDefaultRidSpecificArtifacts>
+    <EnableDefaultRidSpecificArtifacts Condition="'$(EnableDefaultRidSpecificArtifacts)' == ''">true</EnableDefaultRidSpecificArtifacts>
+
+    <UseDotNetCertificate>true</UseDotNetCertificate>
   </PropertyGroup>
 
   <ItemGroup>
@@ -14,6 +43,9 @@
     <FileSignInfo Include="mscordaccore.dll" CertificateName="None" />
     <FileSignInfo Include="mscordbi.dll" CertificateName="None" />
 
+    <!-- On MacOS, we need to sign a number of our executables with the Mac developer cert with hardening enabled. -->
+    <FileSignInfo Condition="'$(TargetsOSX)' == 'true'" Include="dotnet;apphost;corerun;createdump" CertificateName="MacDeveloperHarden" />
+
     <!-- We don't need to code sign .js files because they are not used in Windows Script Host. -->
     <!-- WARNING: Needs to happed outside of any target -->
     <FileExtensionSignInfo Update=".js" CertificateName="None" />
@@ -24,18 +56,6 @@
     <FileSignInfo Include="Mono.Cecil.Mdb.dll" CertificateName="3PartySHA2" />
     <FileSignInfo Include="Mono.Cecil.Pdb.dll" CertificateName="3PartySHA2" />
     <FileSignInfo Include="Mono.Cecil.Rocks.dll" CertificateName="3PartySHA2" />
-
-    <FileExtensionSignInfo Include=".msi" CertificateName="MicrosoftDotNet500" />
-
-    <!--
-      Removal is temporarily needed as we integrate support for these extensions into SignTool.
-      Should be cleaned up after https://github.com/dotnet/arcade/issues/14432,
-      https://github.com/dotnet/arcade/issues/14433, and
-      https://github.com/dotnet/arcade/issues/14435 are completed.
-     -->
-    <FileExtensionSignInfo Remove=".deb;.rpm;.pkg" />
-    <FileExtensionSignInfo Include=".pkg" CertificateName="8003" />
-    <FileExtensionSignInfo Include=".deb;.rpm" CertificateName="LinuxSign" />
   </ItemGroup>
 
   <!--
@@ -43,41 +63,101 @@
     This package name has the version in it, so we need to use a wildcard to discover the right name.
   -->
   <ItemGroup>
-    <AzureLinuxPrereqsRPM Include="$(ArtifactsPackagesDir)**/dotnet-runtime-deps-*-azl.3-*.rpm" />
-    <FileSignInfo Include="@(AzureLinuxPrereqsRPM->'%(Filename)%(Extension)')" CertificateName="LinuxSignMariner" />
-  </ItemGroup>
-
-  <!-- Update existing defaults from arcade that were using Microsoft400 to use the .NET-specific cert -->
-  <ItemGroup>
-    <FileExtensionSignInfo Update="@(FileExtensionSignInfo->WithMetadataValue('CertificateName','Microsoft400'))" CertificateName="MicrosoftDotNet500" />
-    <StrongNameSignInfo Update="@(StrongNameSignInfo->WithMetadataValue('CertificateName','Microsoft400'))" CertificateName="MicrosoftDotNet500" />
-    <FileSignInfo Update="@(FileSignInfo->WithMetadataValue('CertificateName','Microsoft400'))" CertificateName="MicrosoftDotNet500" />
+    <AzureLinuxRPM Include="$(ArtifactsPackagesDir)**/*-azl.*-*.rpm" />
+    <AzureLinuxRPM Include="$(ArtifactsPackagesDir)**/*-azl-*.rpm" />
+    <FileSignInfo Include="@(AzureLinuxRPM->'%(Filename)%(Extension)')" CertificateName="LinuxSignMariner" />
   </ItemGroup>
 
-  <!-- In build signing and publishing without a join point -->
-  <ItemGroup Condition="'$(DotNetBuild)' == 'true'">
-    <Artifact Include="$(ArtifactsPackagesDir)**\*.tar.gz;
-                       $(ArtifactsPackagesDir)**\*.zip;
-                       $(ArtifactsPackagesDir)**\*.deb;
-                       $(ArtifactsPackagesDir)**\*.rpm;
-                       $(ArtifactsPackagesDir)**\*.pkg;
-                       $(ArtifactsPackagesDir)**\*.exe;
-                       $(ArtifactsPackagesDir)**\*.msi"
-              Exclude="$(ArtifactsPackagesDir)**\Symbols.runtime.tar.gz"
-              IsShipping="$([System.String]::Copy('%(RecursiveDir)').StartsWith('Shipping'))">
+  <ItemGroup Condition="'$(DotNetFinalPublish)' != 'true'">
+    <BlobArtifact Include="$(ArtifactsPackagesDir)**\*.tar.gz;
+                           $(ArtifactsPackagesDir)**\*.zip;
+                           $(ArtifactsPackagesDir)**\*.deb;
+                           $(ArtifactsPackagesDir)**\*.rpm;
+                           $(ArtifactsPackagesDir)**\*.pkg;
+                           $(ArtifactsPackagesDir)**\*.exe;
+                           $(ArtifactsPackagesDir)**\*.msi;"
+                  Exclude="$(ArtifactsPackagesDir)**\Symbols.runtime.tar.gz"
+                  Kind="Blob"
+                  IsShipping="$([System.String]::Copy('%(RecursiveDir)').StartsWith('Shipping'))">
       <!-- Exclude wixpack.zip files from checksum generation -->
       <ChecksumPath Condition="$([System.String]::Copy('%(Filename)%(Extension)').EndsWith('.wixpack.zip')) != 'true'">%(FullPath).sha512</ChecksumPath>
-    </Artifact>
+    </BlobArtifact>
+  </ItemGroup>
+
+  <!-- In the runtime official build, we only sign a subset of files. -->
+  <ItemGroup Condition="'$(DotNetFinalPublish)' == 'true'">
+    <BlobArtifact Include="$(ArtifactsPackagesDir)**\*.msi;
+                    $(ArtifactsPackagesDir)**\*.exe;
+                    $(ArtifactsPackagesDir)**\*.zip"
+                  SkipPublish="true" />
   </ItemGroup>
 
-  <!-- Only the following artifacts should be signed.
-       Set SkipPublish=true as those artifacts are added again in Publishing.props. -->
-  <ItemGroup Condition="'$(DotNetBuild)' != 'true'">
-    <Artifact Include="$(DownloadDirectory)**\*.msi;
-                       $(DownloadDirectory)**\*.exe;
-                       $(DownloadDirectory)**\*.nupkg;
-                       $(DownloadDirectory)**\*.zip"
-              SkipPublish="true" />
+  <ItemGroup Condition="'$(EnableBlobArtifacts)' == 'true'">
+    <Artifact Include="@(BlobArtifact)" />
+
+    <Artifact Include="$(ArtifactsDir)VSSetup\**\*.zip"
+              Condition="Exists('$(ArtifactsDir)VSSetup')"
+              Kind="Blob"
+              ChecksumPath="%(FullPath).sha512"
+              IsVSSetup="true"
+              IsShipping="true"
+              SubBlobFolder="workloads/" />
   </ItemGroup>
 
+  <!--
+    Filter out the RID-specific (Runtime) nupkgs for this RID.
+    Every job will publish their RID-specific packages.
+    For non-RID-specific packages, we have various rules:
+
+    - A job can specify EnableDefaultArtifacts=true as a global property to publish all packages it produces.
+      We have specific jobs that produce RID-agnostic packages or packages for multiple RIDs set this property.
+    - For some target RIDs, we also include specific RID-agnostic packages.
+    - For LLVM builds, we only publish LLVM-specific packages.
+  -->
+  <Choose>
+    <When Condition="'$(MonoAOTEnableLLVM)' == 'true'">
+      <ItemGroup>
+        <PackageArtifacts Include="$(ArtifactsPackagesDir)**\*.LLVM.AOT.$(PackageRID).*.nupkg" />
+        <Artifact Include="@(PackageArtifacts)"
+                  IsShipping="$([System.String]::Copy('%(RecursiveDir)').StartsWith('Shipping'))"
+                  Kind="Package" />
+      </ItemGroup>
+    </When>
+    <When Condition="'$(EnableDefaultRidSpecificArtifacts)' == 'true'">
+      <ItemGroup Condition="'$(PackageRID)' == 'ios-arm64'">
+        <PackageArtifacts
+          Include="$(ArtifactsPackagesDir)**\Microsoft.NET.Runtime.iOS.Sample.Mono.*.nupkg;
+                  $(ArtifactsPackagesDir)**\Microsoft.NET.Runtime.LibraryBuilder.Sdk.*.nupkg;
+                  $(ArtifactsPackagesDir)**\Microsoft.NET.Runtime.MonoAOTCompiler.Task.*.nupkg;
+                  $(ArtifactsPackagesDir)**\Microsoft.NET.Runtime.MonoTargets.Sdk.*.nupkg" />
+      </ItemGroup>
+      <ItemGroup Condition="'$(PackageRID)' == 'android-arm64'">
+        <PackageArtifacts
+          Include="$(ArtifactsPackagesDir)**\Microsoft.NET.Runtime.Android.Sample.Mono.*.nupkg" />
+      </ItemGroup>
+      <ItemGroup Condition="'$(PackageRID)' == 'wasi-wasm'">
+        <PackageArtifacts
+          Include="$(ArtifactsPackagesDir)**\Microsoft.NET.Runtime.WebAssembly.Wasi.Sdk.*.nupkg" />
+      </ItemGroup>
+      <ItemGroup Condition="'$(PackageRID)' == 'browser-wasm' and '$(WasmEnableThreads)' != 'true'">
+        <PackageArtifacts
+          Include="$(ArtifactsPackagesDir)**\Microsoft.NET.Runtime.wasm.Sample.Mono.*.nupkg;
+                  $(ArtifactsPackagesDir)**\Microsoft.NET.Runtime.WorkloadTesting.Internal.*.nupkg;
+                  $(ArtifactsPackagesDir)**\Microsoft.NETCore.BrowserDebugHost.Transport.*.nupkg;
+                  $(ArtifactsPackagesDir)**\Microsoft.NET.Runtime.WebAssembly.Sdk.*.nupkg;
+                  $(ArtifactsPackagesDir)**\Microsoft.NET.Runtime.WebAssembly.Templates.net10.*.nupkg;
+                  $(ArtifactsPackagesDir)**\Microsoft.NET.Sdk.WebAssembly.Pack.*.nupkg;
+                  $(ArtifactsPackagesDir)**\Microsoft.NET.Workload.Mono.ToolChain.*.nupkg" />
+      </ItemGroup>
+
+      <ItemGroup>
+        <!--
+          Include the non-RID-specific packages for this RID.
+        -->
+        <Artifact Include="@(PackageArtifacts)"
+                  IsShipping="$([System.String]::Copy('%(RecursiveDir)').StartsWith('Shipping'))"
+                  Kind="Package" />
+      </ItemGroup>
+    </When>
+  </Choose>
 </Project>
diff --git a/eng/Subsets.props b/eng/Subsets.props
index aa4da501a51acc..fe161b00b0ac18 100644
--- a/eng/Subsets.props
+++ b/eng/Subsets.props
@@ -307,6 +307,10 @@
     <ClrRuntimeBuildSubsets>$(ClrRuntimeBuildSubsets);ClrILToolsSubset=true</ClrRuntimeBuildSubsets>
   </PropertyGroup>
 
+  <PropertyGroup Condition="$(_subset.Contains('+tools.cdacreader+'))">
+    <ClrRuntimeBuildSubsets>$(ClrRuntimeBuildSubsets);ClrCdacSubset=true</ClrRuntimeBuildSubsets>
+  </PropertyGroup>
+
   <PropertyGroup Condition="$(_subset.Contains('+clr.nativeaotruntime+')) and '$(NativeAotSupported)' == 'true'">
     <ClrRuntimeBuildSubsets>$(ClrRuntimeBuildSubsets);ClrNativeAotSubset=true</ClrRuntimeBuildSubsets>
   </PropertyGroup>
@@ -440,9 +444,12 @@
     <ProjectToBuild Include="$(CoreClrProjectRoot)tools\aot\ILCompiler\ILCompiler.csproj" Condition="'$(NativeAotSupported)' == 'true' and '$(StageOneBuild)' != 'true'" Category="clr" />
     <ProjectToBuild Include="$(CoreClrProjectRoot)tools\aot\crossgen2\crossgen2.csproj" Condition="'$(NativeAotSupported)' == 'true' and '$(StageOneBuild)' != 'true'" Category="clr" />
 
-    <!-- Always build the in-build variants of these tools. They run on the host machine, which we always have LKG packs for. -->
-    <ProjectToBuild Include="$(CoreClrProjectRoot)tools\aot\ILCompiler\ILCompiler_inbuild.csproj" Category="clr" />
-    <ProjectToBuild Include="$(CoreClrProjectRoot)tools\aot\crossgen2\crossgen2_inbuild.csproj" Category="clr" />
+    <!--
+      Always build the in-build variants of these tools. They run on the host machine, which we always have LKG packs for.
+      For two-stage builds, we omit this step during stage2 since stage1 covers it.
+    -->
+    <ProjectToBuild Include="$(CoreClrProjectRoot)tools\aot\ILCompiler\ILCompiler_inbuild.csproj" Category="clr" Condition="'$(StageTwoBuild)' != 'true'" />
+    <ProjectToBuild Include="$(CoreClrProjectRoot)tools\aot\crossgen2\crossgen2_inbuild.csproj" Category="clr" Condition="'$(StageTwoBuild)' != 'true'" />
 
     <ProjectToBuild Condition="'$(TargetOS)' == 'windows' or ('$(TargetOS)' == 'linux' and ('$(TargetArchitecture)' == 'x64' or '$(TargetArchitecture)' == 'arm64')) or '$(TargetOS)' == 'osx'" Include="$(CoreClrProjectRoot)tools\SuperFileCheck\SuperFileCheck.csproj" Category="clr" />
 
diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml
index af7bfc5fd40368..a935c46af19ab0 100644
--- a/eng/Version.Details.xml
+++ b/eng/Version.Details.xml
@@ -8,37 +8,37 @@
       <Uri>https://github.com/dotnet/wcf</Uri>
       <Sha>7f504aabb1988e9a093c1e74d8040bd52feb2f01</Sha>
     </Dependency>
-    <Dependency Name="runtime.linux-arm64.Microsoft.NETCore.Runtime.JIT.Tools" Version="19.1.0-alpha.1.25103.1" CoherentParentDependency="Microsoft.NET.Workload.Emscripten.Current.Manifest-10.0.100.Transport">
+    <Dependency Name="runtime.linux-arm64.Microsoft.NETCore.Runtime.JIT.Tools" Version="19.1.0-alpha.1.25167.1" CoherentParentDependency="Microsoft.NET.Workload.Emscripten.Current.Manifest-10.0.100.Transport">
       <Uri>https://github.com/dotnet/llvm-project</Uri>
-      <Sha>047727dd52040402cc85b90533e6e020fc0e84bb</Sha>
+      <Sha>da5dd054a531e6fea65643b7e754285b73eab433</Sha>
     </Dependency>
-    <Dependency Name="runtime.linux-x64.Microsoft.NETCore.Runtime.JIT.Tools" Version="19.1.0-alpha.1.25103.1" CoherentParentDependency="Microsoft.NET.Workload.Emscripten.Current.Manifest-10.0.100.Transport">
+    <Dependency Name="runtime.linux-x64.Microsoft.NETCore.Runtime.JIT.Tools" Version="19.1.0-alpha.1.25167.1" CoherentParentDependency="Microsoft.NET.Workload.Emscripten.Current.Manifest-10.0.100.Transport">
       <Uri>https://github.com/dotnet/llvm-project</Uri>
-      <Sha>047727dd52040402cc85b90533e6e020fc0e84bb</Sha>
+      <Sha>da5dd054a531e6fea65643b7e754285b73eab433</Sha>
     </Dependency>
-    <Dependency Name="runtime.linux-musl-arm64.Microsoft.NETCore.Runtime.JIT.Tools" Version="19.1.0-alpha.1.25103.1" CoherentParentDependency="Microsoft.NET.Workload.Emscripten.Current.Manifest-10.0.100.Transport">
+    <Dependency Name="runtime.linux-musl-arm64.Microsoft.NETCore.Runtime.JIT.Tools" Version="19.1.0-alpha.1.25167.1" CoherentParentDependency="Microsoft.NET.Workload.Emscripten.Current.Manifest-10.0.100.Transport">
       <Uri>https://github.com/dotnet/llvm-project</Uri>
-      <Sha>047727dd52040402cc85b90533e6e020fc0e84bb</Sha>
+      <Sha>da5dd054a531e6fea65643b7e754285b73eab433</Sha>
     </Dependency>
-    <Dependency Name="runtime.linux-musl-x64.Microsoft.NETCore.Runtime.JIT.Tools" Version="19.1.0-alpha.1.25103.1" CoherentParentDependency="Microsoft.NET.Workload.Emscripten.Current.Manifest-10.0.100.Transport">
+    <Dependency Name="runtime.linux-musl-x64.Microsoft.NETCore.Runtime.JIT.Tools" Version="19.1.0-alpha.1.25167.1" CoherentParentDependency="Microsoft.NET.Workload.Emscripten.Current.Manifest-10.0.100.Transport">
       <Uri>https://github.com/dotnet/llvm-project</Uri>
-      <Sha>047727dd52040402cc85b90533e6e020fc0e84bb</Sha>
+      <Sha>da5dd054a531e6fea65643b7e754285b73eab433</Sha>
     </Dependency>
-    <Dependency Name="runtime.win-arm64.Microsoft.NETCore.Runtime.JIT.Tools" Version="19.1.0-alpha.1.25103.1" CoherentParentDependency="Microsoft.NET.Workload.Emscripten.Current.Manifest-10.0.100.Transport">
+    <Dependency Name="runtime.win-arm64.Microsoft.NETCore.Runtime.JIT.Tools" Version="19.1.0-alpha.1.25167.1" CoherentParentDependency="Microsoft.NET.Workload.Emscripten.Current.Manifest-10.0.100.Transport">
       <Uri>https://github.com/dotnet/llvm-project</Uri>
-      <Sha>047727dd52040402cc85b90533e6e020fc0e84bb</Sha>
+      <Sha>da5dd054a531e6fea65643b7e754285b73eab433</Sha>
     </Dependency>
-    <Dependency Name="runtime.win-x64.Microsoft.NETCore.Runtime.JIT.Tools" Version="19.1.0-alpha.1.25103.1" CoherentParentDependency="Microsoft.NET.Workload.Emscripten.Current.Manifest-10.0.100.Transport">
+    <Dependency Name="runtime.win-x64.Microsoft.NETCore.Runtime.JIT.Tools" Version="19.1.0-alpha.1.25167.1" CoherentParentDependency="Microsoft.NET.Workload.Emscripten.Current.Manifest-10.0.100.Transport">
       <Uri>https://github.com/dotnet/llvm-project</Uri>
-      <Sha>047727dd52040402cc85b90533e6e020fc0e84bb</Sha>
+      <Sha>da5dd054a531e6fea65643b7e754285b73eab433</Sha>
     </Dependency>
-    <Dependency Name="runtime.osx-arm64.Microsoft.NETCore.Runtime.JIT.Tools" Version="19.1.0-alpha.1.25103.1" CoherentParentDependency="Microsoft.NET.Workload.Emscripten.Current.Manifest-10.0.100.Transport">
+    <Dependency Name="runtime.osx-arm64.Microsoft.NETCore.Runtime.JIT.Tools" Version="19.1.0-alpha.1.25167.1" CoherentParentDependency="Microsoft.NET.Workload.Emscripten.Current.Manifest-10.0.100.Transport">
       <Uri>https://github.com/dotnet/llvm-project</Uri>
-      <Sha>047727dd52040402cc85b90533e6e020fc0e84bb</Sha>
+      <Sha>da5dd054a531e6fea65643b7e754285b73eab433</Sha>
     </Dependency>
-    <Dependency Name="runtime.osx-x64.Microsoft.NETCore.Runtime.JIT.Tools" Version="19.1.0-alpha.1.25103.1" CoherentParentDependency="Microsoft.NET.Workload.Emscripten.Current.Manifest-10.0.100.Transport">
+    <Dependency Name="runtime.osx-x64.Microsoft.NETCore.Runtime.JIT.Tools" Version="19.1.0-alpha.1.25167.1" CoherentParentDependency="Microsoft.NET.Workload.Emscripten.Current.Manifest-10.0.100.Transport">
       <Uri>https://github.com/dotnet/llvm-project</Uri>
-      <Sha>047727dd52040402cc85b90533e6e020fc0e84bb</Sha>
+      <Sha>da5dd054a531e6fea65643b7e754285b73eab433</Sha>
     </Dependency>
     <Dependency Name="System.CommandLine" Version="2.0.0-beta4.24528.1">
       <Uri>https://github.com/dotnet/command-line-api</Uri>
@@ -60,20 +60,20 @@
       <Sha>f7a78a77069262841fe3c3c6b89ff243fc43cefc</Sha>
       <SourceBuild RepoName="cecil" ManagedOnly="true" />
     </Dependency>
-    <Dependency Name="Microsoft.NET.Workload.Emscripten.Current.Manifest-10.0.100.Transport" Version="10.0.0-preview.3.25155.1">
+    <Dependency Name="Microsoft.NET.Workload.Emscripten.Current.Manifest-10.0.100.Transport" Version="10.0.0-preview.4.25176.1">
       <Uri>https://github.com/dotnet/emsdk</Uri>
-      <Sha>7619d65bf2534a42257d1d8488552e59790475f6</Sha>
+      <Sha>e7ebd8aa0424ba4dc6ac03419954c3287d891faa</Sha>
     </Dependency>
     <!-- Intermediate is necessary for source build. -->
-    <Dependency Name="Microsoft.SourceBuild.Intermediate.emsdk" Version="10.0.0-preview.3.25155.1">
+    <Dependency Name="Microsoft.SourceBuild.Intermediate.emsdk" Version="10.0.0-preview.4.25176.1">
       <Uri>https://github.com/dotnet/emsdk</Uri>
-      <Sha>7619d65bf2534a42257d1d8488552e59790475f6</Sha>
+      <Sha>e7ebd8aa0424ba4dc6ac03419954c3287d891faa</Sha>
       <SourceBuild RepoName="emsdk" ManagedOnly="true" />
     </Dependency>
     <!-- Intermediate is necessary for source build. -->
-    <Dependency Name="Microsoft.SourceBuild.Intermediate.source-build-reference-packages" Version="10.0.612804">
+    <Dependency Name="Microsoft.SourceBuild.Intermediate.source-build-reference-packages" Version="10.0.617002">
       <Uri>https://github.com/dotnet/source-build-reference-packages</Uri>
-      <Sha>9c8e3885e783e984a48ed3585ff51d86b020d7ce</Sha>
+      <Sha>cf30e8fd726730aa4b142275fb2fc0503528b7dd</Sha>
       <SourceBuild RepoName="source-build-reference-packages" ManagedOnly="true" />
     </Dependency>
     <!-- Intermediate is necessary for source build. -->
@@ -84,87 +84,87 @@
     </Dependency>
   </ProductDependencies>
   <ToolsetDependencies>
-    <Dependency Name="Microsoft.DotNet.Arcade.Sdk" Version="10.0.0-beta.25125.2">
+    <Dependency Name="Microsoft.DotNet.Arcade.Sdk" Version="10.0.0-beta.25175.2">
       <Uri>https://github.com/dotnet/arcade</Uri>
-      <Sha>5b1e2d133be215e729cf90016ae12b520950ce7a</Sha>
+      <Sha>584374998d21b1d550452753c7b5f41725c07a11</Sha>
     </Dependency>
     <!-- Intermediate is necessary for source build. -->
-    <Dependency Name="Microsoft.SourceBuild.Intermediate.arcade" Version="10.0.0-beta.25125.2">
+    <Dependency Name="Microsoft.SourceBuild.Intermediate.arcade" Version="10.0.0-beta.25175.2">
       <Uri>https://github.com/dotnet/arcade</Uri>
-      <Sha>5b1e2d133be215e729cf90016ae12b520950ce7a</Sha>
+      <Sha>584374998d21b1d550452753c7b5f41725c07a11</Sha>
       <SourceBuild RepoName="arcade" ManagedOnly="true" />
     </Dependency>
-    <Dependency Name="Microsoft.DotNet.XliffTasks" Version="10.0.0-beta.25125.2">
+    <Dependency Name="Microsoft.DotNet.XliffTasks" Version="10.0.0-beta.25175.2">
       <Uri>https://github.com/dotnet/arcade</Uri>
-      <Sha>5b1e2d133be215e729cf90016ae12b520950ce7a</Sha>
+      <Sha>584374998d21b1d550452753c7b5f41725c07a11</Sha>
     </Dependency>
-    <Dependency Name="Microsoft.DotNet.Helix.Sdk" Version="10.0.0-beta.25125.2">
+    <Dependency Name="Microsoft.DotNet.Helix.Sdk" Version="10.0.0-beta.25175.2">
       <Uri>https://github.com/dotnet/arcade</Uri>
-      <Sha>5b1e2d133be215e729cf90016ae12b520950ce7a</Sha>
+      <Sha>584374998d21b1d550452753c7b5f41725c07a11</Sha>
     </Dependency>
-    <Dependency Name="Microsoft.DotNet.GenAPI" Version="10.0.0-beta.25125.2">
+    <Dependency Name="Microsoft.DotNet.GenAPI" Version="10.0.0-beta.25175.2">
       <Uri>https://github.com/dotnet/arcade</Uri>
-      <Sha>5b1e2d133be215e729cf90016ae12b520950ce7a</Sha>
+      <Sha>584374998d21b1d550452753c7b5f41725c07a11</Sha>
     </Dependency>
-    <Dependency Name="Microsoft.DotNet.GenFacades" Version="10.0.0-beta.25125.2">
+    <Dependency Name="Microsoft.DotNet.GenFacades" Version="10.0.0-beta.25175.2">
       <Uri>https://github.com/dotnet/arcade</Uri>
-      <Sha>5b1e2d133be215e729cf90016ae12b520950ce7a</Sha>
+      <Sha>584374998d21b1d550452753c7b5f41725c07a11</Sha>
     </Dependency>
-    <Dependency Name="Microsoft.DotNet.XUnitAssert" Version="2.9.2-beta.25125.2">
+    <Dependency Name="Microsoft.DotNet.XUnitAssert" Version="2.9.2-beta.25175.2">
       <Uri>https://github.com/dotnet/arcade</Uri>
-      <Sha>5b1e2d133be215e729cf90016ae12b520950ce7a</Sha>
+      <Sha>584374998d21b1d550452753c7b5f41725c07a11</Sha>
     </Dependency>
-    <Dependency Name="Microsoft.DotNet.XUnitExtensions" Version="10.0.0-beta.25125.2">
+    <Dependency Name="Microsoft.DotNet.XUnitExtensions" Version="10.0.0-beta.25175.2">
       <Uri>https://github.com/dotnet/arcade</Uri>
-      <Sha>5b1e2d133be215e729cf90016ae12b520950ce7a</Sha>
+      <Sha>584374998d21b1d550452753c7b5f41725c07a11</Sha>
     </Dependency>
-    <Dependency Name="Microsoft.DotNet.XUnitConsoleRunner" Version="2.9.2-beta.25125.2">
+    <Dependency Name="Microsoft.DotNet.XUnitConsoleRunner" Version="2.9.2-beta.25175.2">
       <Uri>https://github.com/dotnet/arcade</Uri>
-      <Sha>5b1e2d133be215e729cf90016ae12b520950ce7a</Sha>
+      <Sha>584374998d21b1d550452753c7b5f41725c07a11</Sha>
     </Dependency>
-    <Dependency Name="Microsoft.DotNet.Build.Tasks.Archives" Version="10.0.0-beta.25125.2">
+    <Dependency Name="Microsoft.DotNet.Build.Tasks.Archives" Version="10.0.0-beta.25175.2">
       <Uri>https://github.com/dotnet/arcade</Uri>
-      <Sha>5b1e2d133be215e729cf90016ae12b520950ce7a</Sha>
+      <Sha>584374998d21b1d550452753c7b5f41725c07a11</Sha>
     </Dependency>
-    <Dependency Name="Microsoft.DotNet.Build.Tasks.Packaging" Version="10.0.0-beta.25125.2">
+    <Dependency Name="Microsoft.DotNet.Build.Tasks.Packaging" Version="10.0.0-beta.25175.2">
       <Uri>https://github.com/dotnet/arcade</Uri>
-      <Sha>5b1e2d133be215e729cf90016ae12b520950ce7a</Sha>
+      <Sha>584374998d21b1d550452753c7b5f41725c07a11</Sha>
     </Dependency>
-    <Dependency Name="Microsoft.DotNet.Build.Tasks.Installers" Version="10.0.0-beta.25125.2">
+    <Dependency Name="Microsoft.DotNet.Build.Tasks.Installers" Version="10.0.0-beta.25175.2">
       <Uri>https://github.com/dotnet/arcade</Uri>
-      <Sha>5b1e2d133be215e729cf90016ae12b520950ce7a</Sha>
+      <Sha>584374998d21b1d550452753c7b5f41725c07a11</Sha>
     </Dependency>
-    <Dependency Name="Microsoft.DotNet.Build.Tasks.Templating" Version="10.0.0-beta.25125.2">
+    <Dependency Name="Microsoft.DotNet.Build.Tasks.Templating" Version="10.0.0-beta.25175.2">
       <Uri>https://github.com/dotnet/arcade</Uri>
-      <Sha>5b1e2d133be215e729cf90016ae12b520950ce7a</Sha>
+      <Sha>584374998d21b1d550452753c7b5f41725c07a11</Sha>
     </Dependency>
-    <Dependency Name="Microsoft.DotNet.Build.Tasks.Workloads" Version="10.0.0-beta.25125.2">
+    <Dependency Name="Microsoft.DotNet.Build.Tasks.Workloads" Version="10.0.0-beta.25175.2">
       <Uri>https://github.com/dotnet/arcade</Uri>
-      <Sha>5b1e2d133be215e729cf90016ae12b520950ce7a</Sha>
+      <Sha>584374998d21b1d550452753c7b5f41725c07a11</Sha>
     </Dependency>
-    <Dependency Name="Microsoft.DotNet.CodeAnalysis" Version="10.0.0-beta.25125.2">
+    <Dependency Name="Microsoft.DotNet.CodeAnalysis" Version="10.0.0-beta.25175.2">
       <Uri>https://github.com/dotnet/arcade</Uri>
-      <Sha>5b1e2d133be215e729cf90016ae12b520950ce7a</Sha>
+      <Sha>584374998d21b1d550452753c7b5f41725c07a11</Sha>
     </Dependency>
-    <Dependency Name="Microsoft.DotNet.Build.Tasks.TargetFramework" Version="10.0.0-beta.25125.2">
+    <Dependency Name="Microsoft.DotNet.Build.Tasks.TargetFramework" Version="10.0.0-beta.25175.2">
       <Uri>https://github.com/dotnet/arcade</Uri>
-      <Sha>5b1e2d133be215e729cf90016ae12b520950ce7a</Sha>
+      <Sha>584374998d21b1d550452753c7b5f41725c07a11</Sha>
     </Dependency>
-    <Dependency Name="Microsoft.DotNet.RemoteExecutor" Version="10.0.0-beta.25125.2">
+    <Dependency Name="Microsoft.DotNet.RemoteExecutor" Version="10.0.0-beta.25175.2">
       <Uri>https://github.com/dotnet/arcade</Uri>
-      <Sha>5b1e2d133be215e729cf90016ae12b520950ce7a</Sha>
+      <Sha>584374998d21b1d550452753c7b5f41725c07a11</Sha>
     </Dependency>
-    <Dependency Name="Microsoft.DotNet.Build.Tasks.Feed" Version="10.0.0-beta.25125.2">
+    <Dependency Name="Microsoft.DotNet.Build.Tasks.Feed" Version="10.0.0-beta.25175.2">
       <Uri>https://github.com/dotnet/arcade</Uri>
-      <Sha>5b1e2d133be215e729cf90016ae12b520950ce7a</Sha>
+      <Sha>584374998d21b1d550452753c7b5f41725c07a11</Sha>
     </Dependency>
-    <Dependency Name="Microsoft.DotNet.VersionTools.Tasks" Version="10.0.0-beta.25125.2">
+    <Dependency Name="Microsoft.DotNet.VersionTools.Tasks" Version="10.0.0-beta.25175.2">
       <Uri>https://github.com/dotnet/arcade</Uri>
-      <Sha>5b1e2d133be215e729cf90016ae12b520950ce7a</Sha>
+      <Sha>584374998d21b1d550452753c7b5f41725c07a11</Sha>
     </Dependency>
-    <Dependency Name="Microsoft.DotNet.SharedFramework.Sdk" Version="10.0.0-beta.25125.2">
+    <Dependency Name="Microsoft.DotNet.SharedFramework.Sdk" Version="10.0.0-beta.25175.2">
       <Uri>https://github.com/dotnet/arcade</Uri>
-      <Sha>5b1e2d133be215e729cf90016ae12b520950ce7a</Sha>
+      <Sha>584374998d21b1d550452753c7b5f41725c07a11</Sha>
     </Dependency>
     <Dependency Name="System.ComponentModel.TypeConverter.TestData" Version="10.0.0-beta.25126.1">
       <Uri>https://github.com/dotnet/runtime-assets</Uri>
@@ -218,89 +218,89 @@
       <Uri>https://github.com/dotnet/runtime-assets</Uri>
       <Sha>207b0aac7c32b425a684734a70ba78bfdddb9e48</Sha>
     </Dependency>
-    <Dependency Name="runtime.linux-arm64.Microsoft.NETCore.Runtime.Mono.LLVM.Libclang" Version="19.1.0-alpha.1.25103.1" CoherentParentDependency="Microsoft.NET.Workload.Emscripten.Current.Manifest-10.0.100.Transport">
+    <Dependency Name="runtime.linux-arm64.Microsoft.NETCore.Runtime.Mono.LLVM.Libclang" Version="19.1.0-alpha.1.25167.1" CoherentParentDependency="Microsoft.NET.Workload.Emscripten.Current.Manifest-10.0.100.Transport">
       <Uri>https://github.com/dotnet/llvm-project</Uri>
-      <Sha>047727dd52040402cc85b90533e6e020fc0e84bb</Sha>
+      <Sha>da5dd054a531e6fea65643b7e754285b73eab433</Sha>
     </Dependency>
-    <Dependency Name="runtime.linux-arm64.Microsoft.NETCore.Runtime.Mono.LLVM.Sdk" Version="19.1.0-alpha.1.25103.1" CoherentParentDependency="Microsoft.NET.Workload.Emscripten.Current.Manifest-10.0.100.Transport">
+    <Dependency Name="runtime.linux-arm64.Microsoft.NETCore.Runtime.Mono.LLVM.Sdk" Version="19.1.0-alpha.1.25167.1" CoherentParentDependency="Microsoft.NET.Workload.Emscripten.Current.Manifest-10.0.100.Transport">
       <Uri>https://github.com/dotnet/llvm-project</Uri>
-      <Sha>047727dd52040402cc85b90533e6e020fc0e84bb</Sha>
+      <Sha>da5dd054a531e6fea65643b7e754285b73eab433</Sha>
     </Dependency>
-    <Dependency Name="runtime.linux-arm64.Microsoft.NETCore.Runtime.Mono.LLVM.Tools" Version="19.1.0-alpha.1.25103.1" CoherentParentDependency="Microsoft.NET.Workload.Emscripten.Current.Manifest-10.0.100.Transport">
+    <Dependency Name="runtime.linux-arm64.Microsoft.NETCore.Runtime.Mono.LLVM.Tools" Version="19.1.0-alpha.1.25167.1" CoherentParentDependency="Microsoft.NET.Workload.Emscripten.Current.Manifest-10.0.100.Transport">
       <Uri>https://github.com/dotnet/llvm-project</Uri>
-      <Sha>047727dd52040402cc85b90533e6e020fc0e84bb</Sha>
+      <Sha>da5dd054a531e6fea65643b7e754285b73eab433</Sha>
     </Dependency>
-    <Dependency Name="runtime.linux-musl-arm64.Microsoft.NETCore.Runtime.Mono.LLVM.Libclang" Version="19.1.0-alpha.1.25103.1" CoherentParentDependency="Microsoft.NET.Workload.Emscripten.Current.Manifest-10.0.100.Transport">
+    <Dependency Name="runtime.linux-musl-arm64.Microsoft.NETCore.Runtime.Mono.LLVM.Libclang" Version="19.1.0-alpha.1.25167.1" CoherentParentDependency="Microsoft.NET.Workload.Emscripten.Current.Manifest-10.0.100.Transport">
       <Uri>https://github.com/dotnet/llvm-project</Uri>
-      <Sha>047727dd52040402cc85b90533e6e020fc0e84bb</Sha>
+      <Sha>da5dd054a531e6fea65643b7e754285b73eab433</Sha>
     </Dependency>
-    <Dependency Name="runtime.linux-musl-arm64.Microsoft.NETCore.Runtime.Mono.LLVM.Sdk" Version="19.1.0-alpha.1.25103.1" CoherentParentDependency="Microsoft.NET.Workload.Emscripten.Current.Manifest-10.0.100.Transport">
+    <Dependency Name="runtime.linux-musl-arm64.Microsoft.NETCore.Runtime.Mono.LLVM.Sdk" Version="19.1.0-alpha.1.25167.1" CoherentParentDependency="Microsoft.NET.Workload.Emscripten.Current.Manifest-10.0.100.Transport">
       <Uri>https://github.com/dotnet/llvm-project</Uri>
-      <Sha>047727dd52040402cc85b90533e6e020fc0e84bb</Sha>
+      <Sha>da5dd054a531e6fea65643b7e754285b73eab433</Sha>
     </Dependency>
-    <Dependency Name="runtime.linux-musl-arm64.Microsoft.NETCore.Runtime.Mono.LLVM.Tools" Version="19.1.0-alpha.1.25103.1" CoherentParentDependency="Microsoft.NET.Workload.Emscripten.Current.Manifest-10.0.100.Transport">
+    <Dependency Name="runtime.linux-musl-arm64.Microsoft.NETCore.Runtime.Mono.LLVM.Tools" Version="19.1.0-alpha.1.25167.1" CoherentParentDependency="Microsoft.NET.Workload.Emscripten.Current.Manifest-10.0.100.Transport">
       <Uri>https://github.com/dotnet/llvm-project</Uri>
-      <Sha>047727dd52040402cc85b90533e6e020fc0e84bb</Sha>
+      <Sha>da5dd054a531e6fea65643b7e754285b73eab433</Sha>
     </Dependency>
-    <Dependency Name="runtime.linux-x64.Microsoft.NETCore.Runtime.Mono.LLVM.Libclang" Version="19.1.0-alpha.1.25103.1" CoherentParentDependency="Microsoft.NET.Workload.Emscripten.Current.Manifest-10.0.100.Transport">
+    <Dependency Name="runtime.linux-x64.Microsoft.NETCore.Runtime.Mono.LLVM.Libclang" Version="19.1.0-alpha.1.25167.1" CoherentParentDependency="Microsoft.NET.Workload.Emscripten.Current.Manifest-10.0.100.Transport">
       <Uri>https://github.com/dotnet/llvm-project</Uri>
-      <Sha>047727dd52040402cc85b90533e6e020fc0e84bb</Sha>
+      <Sha>da5dd054a531e6fea65643b7e754285b73eab433</Sha>
     </Dependency>
-    <Dependency Name="runtime.linux-x64.Microsoft.NETCore.Runtime.Mono.LLVM.Sdk" Version="19.1.0-alpha.1.25103.1" CoherentParentDependency="Microsoft.NET.Workload.Emscripten.Current.Manifest-10.0.100.Transport">
+    <Dependency Name="runtime.linux-x64.Microsoft.NETCore.Runtime.Mono.LLVM.Sdk" Version="19.1.0-alpha.1.25167.1" CoherentParentDependency="Microsoft.NET.Workload.Emscripten.Current.Manifest-10.0.100.Transport">
       <Uri>https://github.com/dotnet/llvm-project</Uri>
-      <Sha>047727dd52040402cc85b90533e6e020fc0e84bb</Sha>
+      <Sha>da5dd054a531e6fea65643b7e754285b73eab433</Sha>
     </Dependency>
-    <Dependency Name="runtime.linux-x64.Microsoft.NETCore.Runtime.Mono.LLVM.Tools" Version="19.1.0-alpha.1.25103.1" CoherentParentDependency="Microsoft.NET.Workload.Emscripten.Current.Manifest-10.0.100.Transport">
+    <Dependency Name="runtime.linux-x64.Microsoft.NETCore.Runtime.Mono.LLVM.Tools" Version="19.1.0-alpha.1.25167.1" CoherentParentDependency="Microsoft.NET.Workload.Emscripten.Current.Manifest-10.0.100.Transport">
       <Uri>https://github.com/dotnet/llvm-project</Uri>
-      <Sha>047727dd52040402cc85b90533e6e020fc0e84bb</Sha>
+      <Sha>da5dd054a531e6fea65643b7e754285b73eab433</Sha>
     </Dependency>
-    <Dependency Name="runtime.linux-musl-x64.Microsoft.NETCore.Runtime.Mono.LLVM.Libclang" Version="19.1.0-alpha.1.25103.1" CoherentParentDependency="Microsoft.NET.Workload.Emscripten.Current.Manifest-10.0.100.Transport">
+    <Dependency Name="runtime.linux-musl-x64.Microsoft.NETCore.Runtime.Mono.LLVM.Libclang" Version="19.1.0-alpha.1.25167.1" CoherentParentDependency="Microsoft.NET.Workload.Emscripten.Current.Manifest-10.0.100.Transport">
       <Uri>https://github.com/dotnet/llvm-project</Uri>
-      <Sha>047727dd52040402cc85b90533e6e020fc0e84bb</Sha>
+      <Sha>da5dd054a531e6fea65643b7e754285b73eab433</Sha>
     </Dependency>
-    <Dependency Name="runtime.linux-musl-x64.Microsoft.NETCore.Runtime.Mono.LLVM.Sdk" Version="19.1.0-alpha.1.25103.1" CoherentParentDependency="Microsoft.NET.Workload.Emscripten.Current.Manifest-10.0.100.Transport">
+    <Dependency Name="runtime.linux-musl-x64.Microsoft.NETCore.Runtime.Mono.LLVM.Sdk" Version="19.1.0-alpha.1.25167.1" CoherentParentDependency="Microsoft.NET.Workload.Emscripten.Current.Manifest-10.0.100.Transport">
       <Uri>https://github.com/dotnet/llvm-project</Uri>
-      <Sha>047727dd52040402cc85b90533e6e020fc0e84bb</Sha>
+      <Sha>da5dd054a531e6fea65643b7e754285b73eab433</Sha>
     </Dependency>
-    <Dependency Name="runtime.linux-musl-x64.Microsoft.NETCore.Runtime.Mono.LLVM.Tools" Version="19.1.0-alpha.1.25103.1" CoherentParentDependency="Microsoft.NET.Workload.Emscripten.Current.Manifest-10.0.100.Transport">
+    <Dependency Name="runtime.linux-musl-x64.Microsoft.NETCore.Runtime.Mono.LLVM.Tools" Version="19.1.0-alpha.1.25167.1" CoherentParentDependency="Microsoft.NET.Workload.Emscripten.Current.Manifest-10.0.100.Transport">
       <Uri>https://github.com/dotnet/llvm-project</Uri>
-      <Sha>047727dd52040402cc85b90533e6e020fc0e84bb</Sha>
+      <Sha>da5dd054a531e6fea65643b7e754285b73eab433</Sha>
     </Dependency>
-    <Dependency Name="runtime.win-x64.Microsoft.NETCore.Runtime.Mono.LLVM.Libclang" Version="19.1.0-alpha.1.25103.1" CoherentParentDependency="Microsoft.NET.Workload.Emscripten.Current.Manifest-10.0.100.Transport">
+    <Dependency Name="runtime.win-x64.Microsoft.NETCore.Runtime.Mono.LLVM.Libclang" Version="19.1.0-alpha.1.25167.1" CoherentParentDependency="Microsoft.NET.Workload.Emscripten.Current.Manifest-10.0.100.Transport">
       <Uri>https://github.com/dotnet/llvm-project</Uri>
-      <Sha>047727dd52040402cc85b90533e6e020fc0e84bb</Sha>
+      <Sha>da5dd054a531e6fea65643b7e754285b73eab433</Sha>
     </Dependency>
-    <Dependency Name="runtime.win-x64.Microsoft.NETCore.Runtime.Mono.LLVM.Sdk" Version="19.1.0-alpha.1.25103.1" CoherentParentDependency="Microsoft.NET.Workload.Emscripten.Current.Manifest-10.0.100.Transport">
+    <Dependency Name="runtime.win-x64.Microsoft.NETCore.Runtime.Mono.LLVM.Sdk" Version="19.1.0-alpha.1.25167.1" CoherentParentDependency="Microsoft.NET.Workload.Emscripten.Current.Manifest-10.0.100.Transport">
       <Uri>https://github.com/dotnet/llvm-project</Uri>
-      <Sha>047727dd52040402cc85b90533e6e020fc0e84bb</Sha>
+      <Sha>da5dd054a531e6fea65643b7e754285b73eab433</Sha>
     </Dependency>
-    <Dependency Name="runtime.win-x64.Microsoft.NETCore.Runtime.Mono.LLVM.Tools" Version="19.1.0-alpha.1.25103.1" CoherentParentDependency="Microsoft.NET.Workload.Emscripten.Current.Manifest-10.0.100.Transport">
+    <Dependency Name="runtime.win-x64.Microsoft.NETCore.Runtime.Mono.LLVM.Tools" Version="19.1.0-alpha.1.25167.1" CoherentParentDependency="Microsoft.NET.Workload.Emscripten.Current.Manifest-10.0.100.Transport">
       <Uri>https://github.com/dotnet/llvm-project</Uri>
-      <Sha>047727dd52040402cc85b90533e6e020fc0e84bb</Sha>
+      <Sha>da5dd054a531e6fea65643b7e754285b73eab433</Sha>
     </Dependency>
-    <Dependency Name="runtime.osx-arm64.Microsoft.NETCore.Runtime.Mono.LLVM.Libclang" Version="19.1.0-alpha.1.25103.1" CoherentParentDependency="Microsoft.NET.Workload.Emscripten.Current.Manifest-10.0.100.Transport">
+    <Dependency Name="runtime.osx-arm64.Microsoft.NETCore.Runtime.Mono.LLVM.Libclang" Version="19.1.0-alpha.1.25167.1" CoherentParentDependency="Microsoft.NET.Workload.Emscripten.Current.Manifest-10.0.100.Transport">
       <Uri>https://github.com/dotnet/llvm-project</Uri>
-      <Sha>047727dd52040402cc85b90533e6e020fc0e84bb</Sha>
+      <Sha>da5dd054a531e6fea65643b7e754285b73eab433</Sha>
     </Dependency>
-    <Dependency Name="runtime.osx-arm64.Microsoft.NETCore.Runtime.Mono.LLVM.Sdk" Version="19.1.0-alpha.1.25103.1" CoherentParentDependency="Microsoft.NET.Workload.Emscripten.Current.Manifest-10.0.100.Transport">
+    <Dependency Name="runtime.osx-arm64.Microsoft.NETCore.Runtime.Mono.LLVM.Sdk" Version="19.1.0-alpha.1.25167.1" CoherentParentDependency="Microsoft.NET.Workload.Emscripten.Current.Manifest-10.0.100.Transport">
       <Uri>https://github.com/dotnet/llvm-project</Uri>
-      <Sha>047727dd52040402cc85b90533e6e020fc0e84bb</Sha>
+      <Sha>da5dd054a531e6fea65643b7e754285b73eab433</Sha>
     </Dependency>
-    <Dependency Name="runtime.osx-arm64.Microsoft.NETCore.Runtime.Mono.LLVM.Tools" Version="19.1.0-alpha.1.25103.1" CoherentParentDependency="Microsoft.NET.Workload.Emscripten.Current.Manifest-10.0.100.Transport">
+    <Dependency Name="runtime.osx-arm64.Microsoft.NETCore.Runtime.Mono.LLVM.Tools" Version="19.1.0-alpha.1.25167.1" CoherentParentDependency="Microsoft.NET.Workload.Emscripten.Current.Manifest-10.0.100.Transport">
       <Uri>https://github.com/dotnet/llvm-project</Uri>
-      <Sha>047727dd52040402cc85b90533e6e020fc0e84bb</Sha>
+      <Sha>da5dd054a531e6fea65643b7e754285b73eab433</Sha>
     </Dependency>
-    <Dependency Name="runtime.osx-x64.Microsoft.NETCore.Runtime.Mono.LLVM.Libclang" Version="19.1.0-alpha.1.25103.1" CoherentParentDependency="Microsoft.NET.Workload.Emscripten.Current.Manifest-10.0.100.Transport">
+    <Dependency Name="runtime.osx-x64.Microsoft.NETCore.Runtime.Mono.LLVM.Libclang" Version="19.1.0-alpha.1.25167.1" CoherentParentDependency="Microsoft.NET.Workload.Emscripten.Current.Manifest-10.0.100.Transport">
       <Uri>https://github.com/dotnet/llvm-project</Uri>
-      <Sha>047727dd52040402cc85b90533e6e020fc0e84bb</Sha>
+      <Sha>da5dd054a531e6fea65643b7e754285b73eab433</Sha>
     </Dependency>
-    <Dependency Name="runtime.osx-x64.Microsoft.NETCore.Runtime.Mono.LLVM.Sdk" Version="19.1.0-alpha.1.25103.1" CoherentParentDependency="Microsoft.NET.Workload.Emscripten.Current.Manifest-10.0.100.Transport">
+    <Dependency Name="runtime.osx-x64.Microsoft.NETCore.Runtime.Mono.LLVM.Sdk" Version="19.1.0-alpha.1.25167.1" CoherentParentDependency="Microsoft.NET.Workload.Emscripten.Current.Manifest-10.0.100.Transport">
       <Uri>https://github.com/dotnet/llvm-project</Uri>
-      <Sha>047727dd52040402cc85b90533e6e020fc0e84bb</Sha>
+      <Sha>da5dd054a531e6fea65643b7e754285b73eab433</Sha>
     </Dependency>
-    <Dependency Name="runtime.osx-x64.Microsoft.NETCore.Runtime.Mono.LLVM.Tools" Version="19.1.0-alpha.1.25103.1" CoherentParentDependency="Microsoft.NET.Workload.Emscripten.Current.Manifest-10.0.100.Transport">
+    <Dependency Name="runtime.osx-x64.Microsoft.NETCore.Runtime.Mono.LLVM.Tools" Version="19.1.0-alpha.1.25167.1" CoherentParentDependency="Microsoft.NET.Workload.Emscripten.Current.Manifest-10.0.100.Transport">
       <Uri>https://github.com/dotnet/llvm-project</Uri>
-      <Sha>047727dd52040402cc85b90533e6e020fc0e84bb</Sha>
+      <Sha>da5dd054a531e6fea65643b7e754285b73eab433</Sha>
     </Dependency>
     <Dependency Name="Microsoft.NETCore.App.Runtime.win-x64" Version="10.0.0-preview.3.25152.4">
       <Uri>https://github.com/dotnet/runtime</Uri>
@@ -336,37 +336,37 @@
       <Uri>https://github.com/dotnet/runtime</Uri>
       <Sha>088d199063dd1bf7fa00a445d23f93f915f84b31</Sha>
     </Dependency>
-    <Dependency Name="Microsoft.DotNet.XHarness.TestRunners.Common" Version="10.0.0-prerelease.25103.1">
+    <Dependency Name="Microsoft.DotNet.XHarness.TestRunners.Common" Version="10.0.0-prerelease.25167.4">
       <Uri>https://github.com/dotnet/xharness</Uri>
-      <Sha>cf1b2925785f504d4d52773bcab470044e35ea15</Sha>
+      <Sha>132fdfbd9c8f09f8c51179c1e7742f048f94734c</Sha>
     </Dependency>
-    <Dependency Name="Microsoft.DotNet.XHarness.TestRunners.Xunit" Version="10.0.0-prerelease.25103.1">
+    <Dependency Name="Microsoft.DotNet.XHarness.TestRunners.Xunit" Version="10.0.0-prerelease.25167.4">
       <Uri>https://github.com/dotnet/xharness</Uri>
-      <Sha>cf1b2925785f504d4d52773bcab470044e35ea15</Sha>
+      <Sha>132fdfbd9c8f09f8c51179c1e7742f048f94734c</Sha>
     </Dependency>
-    <Dependency Name="Microsoft.DotNet.XHarness.CLI" Version="10.0.0-prerelease.25103.1">
+    <Dependency Name="Microsoft.DotNet.XHarness.CLI" Version="10.0.0-prerelease.25167.4">
       <Uri>https://github.com/dotnet/xharness</Uri>
-      <Sha>cf1b2925785f504d4d52773bcab470044e35ea15</Sha>
+      <Sha>132fdfbd9c8f09f8c51179c1e7742f048f94734c</Sha>
     </Dependency>
-    <Dependency Name="Microsoft.DotNet.PackageTesting" Version="10.0.0-beta.25125.2">
+    <Dependency Name="Microsoft.DotNet.PackageTesting" Version="10.0.0-beta.25175.2">
       <Uri>https://github.com/dotnet/arcade</Uri>
-      <Sha>5b1e2d133be215e729cf90016ae12b520950ce7a</Sha>
+      <Sha>584374998d21b1d550452753c7b5f41725c07a11</Sha>
     </Dependency>
-    <Dependency Name="optimization.windows_nt-x64.MIBC.Runtime" Version="1.0.0-prerelease.25104.10">
+    <Dependency Name="optimization.windows_nt-x64.MIBC.Runtime" Version="1.0.0-prerelease.25171.2">
       <Uri>https://dev.azure.com/dnceng/internal/_git/dotnet-optimization</Uri>
-      <Sha>79859263121f7fb30c6f8a5c463616afc5a1fc5d</Sha>
+      <Sha>f0670b0742edbc53c8a2803fa86e5e4417a0e681</Sha>
     </Dependency>
-    <Dependency Name="optimization.windows_nt-x86.MIBC.Runtime" Version="1.0.0-prerelease.25104.10">
+    <Dependency Name="optimization.windows_nt-x86.MIBC.Runtime" Version="1.0.0-prerelease.25171.2">
       <Uri>https://dev.azure.com/dnceng/internal/_git/dotnet-optimization</Uri>
-      <Sha>79859263121f7fb30c6f8a5c463616afc5a1fc5d</Sha>
+      <Sha>f0670b0742edbc53c8a2803fa86e5e4417a0e681</Sha>
     </Dependency>
-    <Dependency Name="optimization.linux-x64.MIBC.Runtime" Version="1.0.0-prerelease.25104.10">
+    <Dependency Name="optimization.linux-x64.MIBC.Runtime" Version="1.0.0-prerelease.25171.2">
       <Uri>https://dev.azure.com/dnceng/internal/_git/dotnet-optimization</Uri>
-      <Sha>79859263121f7fb30c6f8a5c463616afc5a1fc5d</Sha>
+      <Sha>f0670b0742edbc53c8a2803fa86e5e4417a0e681</Sha>
     </Dependency>
-    <Dependency Name="optimization.PGO.CoreCLR" Version="1.0.0-prerelease.25104.10">
+    <Dependency Name="optimization.PGO.CoreCLR" Version="1.0.0-prerelease.25171.2">
       <Uri>https://dev.azure.com/dnceng/internal/_git/dotnet-optimization</Uri>
-      <Sha>79859263121f7fb30c6f8a5c463616afc5a1fc5d</Sha>
+      <Sha>f0670b0742edbc53c8a2803fa86e5e4417a0e681</Sha>
     </Dependency>
     <Dependency Name="Microsoft.DotNet.HotReload.Utils.Generator.BuildTool" Version="10.0.0-alpha.0.25126.1">
       <Uri>https://github.com/dotnet/hotreload-utils</Uri>
@@ -402,17 +402,17 @@
       <Sha>bfe9d9f9059008d919d867fe5bdfabfe8b6ed69d</Sha>
       <SourceBuild RepoName="roslyn" ManagedOnly="true" />
     </Dependency>
-    <Dependency Name="Microsoft.DotNet.ApiCompat.Task" Version="10.0.100-preview.3.25126.5">
+    <Dependency Name="Microsoft.DotNet.ApiCompat.Task" Version="10.0.100-preview.3.25173.9">
       <Uri>https://github.com/dotnet/sdk</Uri>
-      <Sha>c921fd0a32c3e6001b10791dd0a30f8ef80f915f</Sha>
+      <Sha>5ebd5267376b61d01b4afc9d4cf9c832488312b2</Sha>
     </Dependency>
-    <Dependency Name="optimization.windows_nt-arm64.MIBC.Runtime" Version="1.0.0-prerelease.25104.10">
+    <Dependency Name="optimization.windows_nt-arm64.MIBC.Runtime" Version="1.0.0-prerelease.25171.2">
       <Uri>https://dev.azure.com/dnceng/internal/_git/dotnet-optimization</Uri>
-      <Sha>79859263121f7fb30c6f8a5c463616afc5a1fc5d</Sha>
+      <Sha>f0670b0742edbc53c8a2803fa86e5e4417a0e681</Sha>
     </Dependency>
-    <Dependency Name="optimization.linux-arm64.MIBC.Runtime" Version="1.0.0-prerelease.25104.10">
+    <Dependency Name="optimization.linux-arm64.MIBC.Runtime" Version="1.0.0-prerelease.25171.2">
       <Uri>https://dev.azure.com/dnceng/internal/_git/dotnet-optimization</Uri>
-      <Sha>79859263121f7fb30c6f8a5c463616afc5a1fc5d</Sha>
+      <Sha>f0670b0742edbc53c8a2803fa86e5e4417a0e681</Sha>
     </Dependency>
     <!-- Necessary for source-build. This allows the package to be retrieved from previously-source-built artifacts
          and flow in as dependencies of the packages produced by runtime. -->
@@ -432,37 +432,37 @@
       <Uri>https://github.com/NuGet/NuGet.Client</Uri>
       <Sha>8fef55f5a55a3b4f2c96cd1a9b5ddc51d4b927f8</Sha>
     </Dependency>
-    <Dependency Name="runtime.linux-arm64.Microsoft.NETCore.Runtime.Wasm.Node.Transport" Version="10.0.0-alpha.1.25125.1" CoherentParentDependency="Microsoft.NET.Workload.Emscripten.Current.Manifest-10.0.100.Transport">
+    <Dependency Name="runtime.linux-arm64.Microsoft.NETCore.Runtime.Wasm.Node.Transport" Version="10.0.0-alpha.1.25169.1" CoherentParentDependency="Microsoft.NET.Workload.Emscripten.Current.Manifest-10.0.100.Transport">
       <Uri>https://github.com/dotnet/node</Uri>
-      <Sha>de1caf9e631e1e15d7fa7f016569d8ea16a3e470</Sha>
+      <Sha>7f33d14aae0d91f2d5befda939160177e13b3f47</Sha>
     </Dependency>
-    <Dependency Name="runtime.linux-musl-arm64.Microsoft.NETCore.Runtime.Wasm.Node.Transport" Version="10.0.0-alpha.1.25125.1" CoherentParentDependency="Microsoft.NET.Workload.Emscripten.Current.Manifest-10.0.100.Transport">
+    <Dependency Name="runtime.linux-musl-arm64.Microsoft.NETCore.Runtime.Wasm.Node.Transport" Version="10.0.0-alpha.1.25169.1" CoherentParentDependency="Microsoft.NET.Workload.Emscripten.Current.Manifest-10.0.100.Transport">
       <Uri>https://github.com/dotnet/node</Uri>
-      <Sha>de1caf9e631e1e15d7fa7f016569d8ea16a3e470</Sha>
+      <Sha>7f33d14aae0d91f2d5befda939160177e13b3f47</Sha>
     </Dependency>
-    <Dependency Name="runtime.linux-x64.Microsoft.NETCore.Runtime.Wasm.Node.Transport" Version="10.0.0-alpha.1.25125.1" CoherentParentDependency="Microsoft.NET.Workload.Emscripten.Current.Manifest-10.0.100.Transport">
+    <Dependency Name="runtime.linux-x64.Microsoft.NETCore.Runtime.Wasm.Node.Transport" Version="10.0.0-alpha.1.25169.1" CoherentParentDependency="Microsoft.NET.Workload.Emscripten.Current.Manifest-10.0.100.Transport">
       <Uri>https://github.com/dotnet/node</Uri>
-      <Sha>de1caf9e631e1e15d7fa7f016569d8ea16a3e470</Sha>
+      <Sha>7f33d14aae0d91f2d5befda939160177e13b3f47</Sha>
     </Dependency>
-    <Dependency Name="runtime.linux-musl-x64.Microsoft.NETCore.Runtime.Wasm.Node.Transport" Version="10.0.0-alpha.1.25125.1" CoherentParentDependency="Microsoft.NET.Workload.Emscripten.Current.Manifest-10.0.100.Transport">
+    <Dependency Name="runtime.linux-musl-x64.Microsoft.NETCore.Runtime.Wasm.Node.Transport" Version="10.0.0-alpha.1.25169.1" CoherentParentDependency="Microsoft.NET.Workload.Emscripten.Current.Manifest-10.0.100.Transport">
       <Uri>https://github.com/dotnet/node</Uri>
-      <Sha>de1caf9e631e1e15d7fa7f016569d8ea16a3e470</Sha>
+      <Sha>7f33d14aae0d91f2d5befda939160177e13b3f47</Sha>
     </Dependency>
-    <Dependency Name="runtime.osx-arm64.Microsoft.NETCore.Runtime.Wasm.Node.Transport" Version="10.0.0-alpha.1.25125.1" CoherentParentDependency="Microsoft.NET.Workload.Emscripten.Current.Manifest-10.0.100.Transport">
+    <Dependency Name="runtime.osx-arm64.Microsoft.NETCore.Runtime.Wasm.Node.Transport" Version="10.0.0-alpha.1.25169.1" CoherentParentDependency="Microsoft.NET.Workload.Emscripten.Current.Manifest-10.0.100.Transport">
       <Uri>https://github.com/dotnet/node</Uri>
-      <Sha>de1caf9e631e1e15d7fa7f016569d8ea16a3e470</Sha>
+      <Sha>7f33d14aae0d91f2d5befda939160177e13b3f47</Sha>
     </Dependency>
-    <Dependency Name="runtime.osx-x64.Microsoft.NETCore.Runtime.Wasm.Node.Transport" Version="10.0.0-alpha.1.25125.1" CoherentParentDependency="Microsoft.NET.Workload.Emscripten.Current.Manifest-10.0.100.Transport">
+    <Dependency Name="runtime.osx-x64.Microsoft.NETCore.Runtime.Wasm.Node.Transport" Version="10.0.0-alpha.1.25169.1" CoherentParentDependency="Microsoft.NET.Workload.Emscripten.Current.Manifest-10.0.100.Transport">
       <Uri>https://github.com/dotnet/node</Uri>
-      <Sha>de1caf9e631e1e15d7fa7f016569d8ea16a3e470</Sha>
+      <Sha>7f33d14aae0d91f2d5befda939160177e13b3f47</Sha>
     </Dependency>
-    <Dependency Name="runtime.win-arm64.Microsoft.NETCore.Runtime.Wasm.Node.Transport" Version="10.0.0-alpha.1.25125.1" CoherentParentDependency="Microsoft.NET.Workload.Emscripten.Current.Manifest-10.0.100.Transport">
+    <Dependency Name="runtime.win-arm64.Microsoft.NETCore.Runtime.Wasm.Node.Transport" Version="10.0.0-alpha.1.25169.1" CoherentParentDependency="Microsoft.NET.Workload.Emscripten.Current.Manifest-10.0.100.Transport">
       <Uri>https://github.com/dotnet/node</Uri>
-      <Sha>de1caf9e631e1e15d7fa7f016569d8ea16a3e470</Sha>
+      <Sha>7f33d14aae0d91f2d5befda939160177e13b3f47</Sha>
     </Dependency>
-    <Dependency Name="runtime.win-x64.Microsoft.NETCore.Runtime.Wasm.Node.Transport" Version="10.0.0-alpha.1.25125.1" CoherentParentDependency="Microsoft.NET.Workload.Emscripten.Current.Manifest-10.0.100.Transport">
+    <Dependency Name="runtime.win-x64.Microsoft.NETCore.Runtime.Wasm.Node.Transport" Version="10.0.0-alpha.1.25169.1" CoherentParentDependency="Microsoft.NET.Workload.Emscripten.Current.Manifest-10.0.100.Transport">
       <Uri>https://github.com/dotnet/node</Uri>
-      <Sha>de1caf9e631e1e15d7fa7f016569d8ea16a3e470</Sha>
+      <Sha>7f33d14aae0d91f2d5befda939160177e13b3f47</Sha>
     </Dependency>
     <Dependency Name="Microsoft.NET.HostModel.TestData" Version="10.0.0-beta.25126.1">
       <Uri>https://github.com/dotnet/runtime-assets</Uri>
diff --git a/eng/Versions.props b/eng/Versions.props
index 4f21ee086e5c40..74d48a52fedec6 100644
--- a/eng/Versions.props
+++ b/eng/Versions.props
@@ -7,12 +7,12 @@
     <MinorVersion>0</MinorVersion>
     <PatchVersion>0</PatchVersion>
     <SdkBandVersion>$(MajorVersion).0.100</SdkBandVersion>
-    <PackageVersionNet9>9.0.2</PackageVersionNet9>
+    <PackageVersionNet9>9.0.3</PackageVersionNet9>
     <PackageVersionNet8>8.0.$([MSBuild]::Add($([System.Version]::Parse('$(PackageVersionNet9)').Build),11))</PackageVersionNet8>
     <PackageVersionNet7>7.0.20</PackageVersionNet7>
-    <PackageVersionNet6>6.0.$([MSBuild]::Add($([System.Version]::Parse('$(PackageVersionNet8)').Build),25))</PackageVersionNet6>
-    <PreReleaseVersionLabel>async</PreReleaseVersionLabel>
-    <PreReleaseVersionIteration>1</PreReleaseVersionIteration>
+    <PackageVersionNet6>6.0.36</PackageVersionNet6>
+    <PreReleaseVersionLabel>preview</PreReleaseVersionLabel>
+    <PreReleaseVersionIteration>4</PreReleaseVersionIteration>
     <!-- Enable to remove prerelease label. -->
     <StabilizePackageVersion Condition="'$(StabilizePackageVersion)' == ''">false</StabilizePackageVersion>
     <DotNetFinalVersionKind Condition="'$(StabilizePackageVersion)' == 'true'">release</DotNetFinalVersionKind>
@@ -44,9 +44,9 @@
       Any tools that contribute to the design-time experience should use the MicrosoftCodeAnalysisVersion_LatestVS property above to ensure
       they do not break the local dev experience.
     -->
-    <MicrosoftCodeAnalysisCSharpVersion>4.14.0-async-13</MicrosoftCodeAnalysisCSharpVersion>
-    <MicrosoftCodeAnalysisVersion>4.14.0-async-13</MicrosoftCodeAnalysisVersion>
-    <MicrosoftNetCompilersToolsetVersion>4.14.0-async-13</MicrosoftNetCompilersToolsetVersion>
+    <MicrosoftCodeAnalysisCSharpVersion>4.14.0-2.25121.3</MicrosoftCodeAnalysisCSharpVersion>
+    <MicrosoftCodeAnalysisVersion>4.14.0-2.25121.3</MicrosoftCodeAnalysisVersion>
+    <MicrosoftNetCompilersToolsetVersion>4.14.0-2.25121.3</MicrosoftNetCompilersToolsetVersion>
   </PropertyGroup>
   <!--
     For source generator support we need to target multiple versions of Roslyn in order to be able to run on older versions of Roslyn.
@@ -83,24 +83,24 @@
   <PropertyGroup>
     <StaticCsVersion>0.2.0</StaticCsVersion>
     <!-- SDK dependencies (also used in wasm build tests -->
-    <MicrosoftDotNetApiCompatTaskVersion>10.0.100-preview.3.25126.5</MicrosoftDotNetApiCompatTaskVersion>
+    <MicrosoftDotNetApiCompatTaskVersion>10.0.100-preview.3.25173.9</MicrosoftDotNetApiCompatTaskVersion>
     <!-- Arcade dependencies -->
-    <MicrosoftDotNetBuildTasksFeedVersion>10.0.0-beta.25125.2</MicrosoftDotNetBuildTasksFeedVersion>
-    <MicrosoftDotNetCodeAnalysisVersion>10.0.0-beta.25125.2</MicrosoftDotNetCodeAnalysisVersion>
-    <MicrosoftDotNetGenAPIVersion>10.0.0-beta.25125.2</MicrosoftDotNetGenAPIVersion>
-    <MicrosoftDotNetGenFacadesVersion>10.0.0-beta.25125.2</MicrosoftDotNetGenFacadesVersion>
-    <MicrosoftDotNetXUnitAssertVersion>2.9.2-beta.25125.2</MicrosoftDotNetXUnitAssertVersion>
-    <MicrosoftDotNetXUnitExtensionsVersion>10.0.0-beta.25125.2</MicrosoftDotNetXUnitExtensionsVersion>
-    <MicrosoftDotNetXUnitConsoleRunnerVersion>2.9.2-beta.25125.2</MicrosoftDotNetXUnitConsoleRunnerVersion>
-    <MicrosoftDotNetBuildTasksArchivesVersion>10.0.0-beta.25125.2</MicrosoftDotNetBuildTasksArchivesVersion>
-    <MicrosoftDotNetBuildTasksInstallersVersion>10.0.0-beta.25125.2</MicrosoftDotNetBuildTasksInstallersVersion>
-    <MicrosoftDotNetBuildTasksPackagingVersion>10.0.0-beta.25125.2</MicrosoftDotNetBuildTasksPackagingVersion>
-    <MicrosoftDotNetBuildTasksTargetFrameworkVersion>10.0.0-beta.25125.2</MicrosoftDotNetBuildTasksTargetFrameworkVersion>
-    <MicrosoftDotNetBuildTasksTemplatingVersion>10.0.0-beta.25125.2</MicrosoftDotNetBuildTasksTemplatingVersion>
-    <MicrosoftDotNetBuildTasksWorkloadsPackageVersion>10.0.0-beta.25125.2</MicrosoftDotNetBuildTasksWorkloadsPackageVersion>
-    <MicrosoftDotNetRemoteExecutorVersion>10.0.0-beta.25125.2</MicrosoftDotNetRemoteExecutorVersion>
-    <MicrosoftDotNetVersionToolsTasksVersion>10.0.0-beta.25125.2</MicrosoftDotNetVersionToolsTasksVersion>
-    <MicrosoftDotNetPackageTestingVersion>10.0.0-beta.25125.2</MicrosoftDotNetPackageTestingVersion>
+    <MicrosoftDotNetBuildTasksFeedVersion>10.0.0-beta.25175.2</MicrosoftDotNetBuildTasksFeedVersion>
+    <MicrosoftDotNetCodeAnalysisVersion>10.0.0-beta.25175.2</MicrosoftDotNetCodeAnalysisVersion>
+    <MicrosoftDotNetGenAPIVersion>10.0.0-beta.25175.2</MicrosoftDotNetGenAPIVersion>
+    <MicrosoftDotNetGenFacadesVersion>10.0.0-beta.25175.2</MicrosoftDotNetGenFacadesVersion>
+    <MicrosoftDotNetXUnitAssertVersion>2.9.2-beta.25175.2</MicrosoftDotNetXUnitAssertVersion>
+    <MicrosoftDotNetXUnitExtensionsVersion>10.0.0-beta.25175.2</MicrosoftDotNetXUnitExtensionsVersion>
+    <MicrosoftDotNetXUnitConsoleRunnerVersion>2.9.2-beta.25175.2</MicrosoftDotNetXUnitConsoleRunnerVersion>
+    <MicrosoftDotNetBuildTasksArchivesVersion>10.0.0-beta.25175.2</MicrosoftDotNetBuildTasksArchivesVersion>
+    <MicrosoftDotNetBuildTasksInstallersVersion>10.0.0-beta.25175.2</MicrosoftDotNetBuildTasksInstallersVersion>
+    <MicrosoftDotNetBuildTasksPackagingVersion>10.0.0-beta.25175.2</MicrosoftDotNetBuildTasksPackagingVersion>
+    <MicrosoftDotNetBuildTasksTargetFrameworkVersion>10.0.0-beta.25175.2</MicrosoftDotNetBuildTasksTargetFrameworkVersion>
+    <MicrosoftDotNetBuildTasksTemplatingVersion>10.0.0-beta.25175.2</MicrosoftDotNetBuildTasksTemplatingVersion>
+    <MicrosoftDotNetBuildTasksWorkloadsPackageVersion>10.0.0-beta.25175.2</MicrosoftDotNetBuildTasksWorkloadsPackageVersion>
+    <MicrosoftDotNetRemoteExecutorVersion>10.0.0-beta.25175.2</MicrosoftDotNetRemoteExecutorVersion>
+    <MicrosoftDotNetVersionToolsTasksVersion>10.0.0-beta.25175.2</MicrosoftDotNetVersionToolsTasksVersion>
+    <MicrosoftDotNetPackageTestingVersion>10.0.0-beta.25175.2</MicrosoftDotNetPackageTestingVersion>
     <!-- TODO: Remove pinned xunit.analyzers version: https://github.com/dotnet/runtime/issues/97088 -->
     <XUnitAnalyzersVersion>1.4.0</XUnitAnalyzersVersion>
     <!-- NuGet dependencies -->
@@ -110,19 +110,23 @@
     <MicrosoftExtensionsDependencyModelVersion>6.0.0</MicrosoftExtensionsDependencyModelVersion>
     <!-- ILAsm dependencies -->
     <MicrosoftNETCoreILAsmVersion>10.0.0-preview.3.25152.4</MicrosoftNETCoreILAsmVersion>
+    <!-- Maintenance-Packages dependencies -->
+    <MicrosoftBclHashCodeVersion>6.0.0</MicrosoftBclHashCodeVersion>
+    <SystemBuffersVersion>4.6.1</SystemBuffersVersion>
+    <SystemDataSqlClientVersion>4.9.0</SystemDataSqlClientVersion>
+    <SystemMemoryVersion>4.6.2</SystemMemoryVersion>
+    <SystemNumericsVectorsVersion>4.6.1</SystemNumericsVectorsVersion>
+    <SystemRuntimeCompilerServicesUnsafeVersion>6.1.1</SystemRuntimeCompilerServicesUnsafeVersion>
+    <SystemThreadingTasksExtensionsVersion>4.6.2</SystemThreadingTasksExtensionsVersion>
+    <SystemValueTupleVersion>4.6.1</SystemValueTupleVersion>
     <!-- Libraries dependencies -->
     <MicrosoftBclAsyncInterfacesVersion>6.0.0</MicrosoftBclAsyncInterfacesVersion>
-    <MicrosoftBclHashCodeVersion>6.0.0</MicrosoftBclHashCodeVersion>
     <MicrosoftWin32RegistryVersion>5.0.0</MicrosoftWin32RegistryVersion>
     <StyleCopAnalyzersVersion>1.2.0-beta.556</StyleCopAnalyzersVersion>
-    <SystemBuffersVersion>4.6.0</SystemBuffersVersion>
     <SystemComponentModelAnnotationsVersion>5.0.0</SystemComponentModelAnnotationsVersion>
-    <SystemDataSqlClientVersion>4.9.0</SystemDataSqlClientVersion>
     <SystemDrawingCommonVersion>8.0.0</SystemDrawingCommonVersion>
     <SystemFormatsAsn1Version>8.0.1</SystemFormatsAsn1Version>
     <SystemIOFileSystemAccessControlVersion>5.0.0</SystemIOFileSystemAccessControlVersion>
-    <SystemMemoryVersion>4.6.0</SystemMemoryVersion>
-    <SystemNumericsVectorsVersion>4.6.0</SystemNumericsVectorsVersion>
     <SystemReflectionMetadataVersion>10.0.0-preview.3.25152.4</SystemReflectionMetadataVersion>
     <SystemReflectionMetadataLoadContextVersion>10.0.0-preview.3.25152.4</SystemReflectionMetadataLoadContextVersion>
     <SystemSecurityAccessControlVersion>6.0.0</SystemSecurityAccessControlVersion>
@@ -131,16 +135,19 @@
     <SystemSecurityPrincipalWindowsVersion>5.0.0</SystemSecurityPrincipalWindowsVersion>
     <SystemSecurityPermissionsVersion>7.0.0</SystemSecurityPermissionsVersion>
     <SystemTextJsonVersion>10.0.0-preview.3.25152.4</SystemTextJsonVersion>
-    <SystemRuntimeCompilerServicesUnsafeVersion>6.1.0</SystemRuntimeCompilerServicesUnsafeVersion>
     <SystemThreadingAccessControlVersion>7.0.0</SystemThreadingAccessControlVersion>
-    <SystemThreadingTasksExtensionsVersion>4.6.0</SystemThreadingTasksExtensionsVersion>
-    <SystemValueTupleVersion>4.5.0</SystemValueTupleVersion>
     <runtimenativeSystemIOPortsVersion>10.0.0-preview.3.25152.4</runtimenativeSystemIOPortsVersion>
     <!-- Keep toolset versions in sync with dotnet/msbuild and dotnet/sdk -->
+    <MicrosoftBclAsyncInterfacesToolsetVersion>8.0.0</MicrosoftBclAsyncInterfacesToolsetVersion>
+    <SystemBuffersToolsetVersion>4.5.1</SystemBuffersToolsetVersion>
     <SystemCollectionsImmutableToolsetVersion>8.0.0</SystemCollectionsImmutableToolsetVersion>
-    <SystemTextJsonToolsetVersion>8.0.4</SystemTextJsonToolsetVersion>
+    <SystemMemoryToolsetVersion>4.5.5</SystemMemoryToolsetVersion>
+    <SystemTextJsonToolsetVersion>8.0.5</SystemTextJsonToolsetVersion>
     <SystemReflectionMetadataToolsetVersion>8.0.0</SystemReflectionMetadataToolsetVersion>
     <SystemReflectionMetadataLoadContextToolsetVersion>8.0.0</SystemReflectionMetadataLoadContextToolsetVersion>
+    <SystemReflectionMetadataToolsetVersion>8.0.0</SystemReflectionMetadataToolsetVersion>
+    <SystemTextEncodingsWebToolsetVersion>8.0.0</SystemTextEncodingsWebToolsetVersion>
+    <SystemThreadingTasksExtensionsToolsetVersion>4.5.4</SystemThreadingTasksExtensionsToolsetVersion>
     <!-- Runtime-Assets dependencies -->
     <SystemRuntimeNumericsTestDataVersion>10.0.0-beta.25126.1</SystemRuntimeNumericsTestDataVersion>
     <SystemComponentModelTypeConverterTestDataVersion>10.0.0-beta.25126.1</SystemComponentModelTypeConverterTestDataVersion>
@@ -158,18 +165,18 @@
     <MicrosoftDotNetCilStripSourcesVersion>10.0.0-beta.25126.1</MicrosoftDotNetCilStripSourcesVersion>
     <MicrosoftNETHostModelTestDataVersion>10.0.0-beta.25126.1</MicrosoftNETHostModelTestDataVersion>
     <!-- xharness dependencies -->
-    <MicrosoftDotNetXHarnessTestRunnersCommonVersion>10.0.0-prerelease.25103.1</MicrosoftDotNetXHarnessTestRunnersCommonVersion>
-    <MicrosoftDotNetXHarnessTestRunnersXunitVersion>10.0.0-prerelease.25103.1</MicrosoftDotNetXHarnessTestRunnersXunitVersion>
-    <MicrosoftDotNetXHarnessCLIVersion>10.0.0-prerelease.25103.1</MicrosoftDotNetXHarnessCLIVersion>
+    <MicrosoftDotNetXHarnessTestRunnersCommonVersion>10.0.0-prerelease.25167.4</MicrosoftDotNetXHarnessTestRunnersCommonVersion>
+    <MicrosoftDotNetXHarnessTestRunnersXunitVersion>10.0.0-prerelease.25167.4</MicrosoftDotNetXHarnessTestRunnersXunitVersion>
+    <MicrosoftDotNetXHarnessCLIVersion>10.0.0-prerelease.25167.4</MicrosoftDotNetXHarnessCLIVersion>
     <!-- hotreload-utils dependencies -->
     <MicrosoftDotNetHotReloadUtilsGeneratorBuildToolVersion>10.0.0-alpha.0.25126.1</MicrosoftDotNetHotReloadUtilsGeneratorBuildToolVersion>
     <!-- dotnet-optimization dependencies -->
-    <optimizationwindows_ntx64MIBCRuntimeVersion>1.0.0-prerelease.25104.10</optimizationwindows_ntx64MIBCRuntimeVersion>
-    <optimizationwindows_ntx86MIBCRuntimeVersion>1.0.0-prerelease.25104.10</optimizationwindows_ntx86MIBCRuntimeVersion>
-    <optimizationwindows_ntarm64MIBCRuntimeVersion>1.0.0-prerelease.25104.10</optimizationwindows_ntarm64MIBCRuntimeVersion>
-    <optimizationlinuxx64MIBCRuntimeVersion>1.0.0-prerelease.25104.10</optimizationlinuxx64MIBCRuntimeVersion>
-    <optimizationlinuxarm64MIBCRuntimeVersion>1.0.0-prerelease.25104.10</optimizationlinuxarm64MIBCRuntimeVersion>
-    <optimizationPGOCoreCLRVersion>1.0.0-prerelease.25104.10</optimizationPGOCoreCLRVersion>
+    <optimizationwindows_ntx64MIBCRuntimeVersion>1.0.0-prerelease.25171.2</optimizationwindows_ntx64MIBCRuntimeVersion>
+    <optimizationwindows_ntx86MIBCRuntimeVersion>1.0.0-prerelease.25171.2</optimizationwindows_ntx86MIBCRuntimeVersion>
+    <optimizationwindows_ntarm64MIBCRuntimeVersion>1.0.0-prerelease.25171.2</optimizationwindows_ntarm64MIBCRuntimeVersion>
+    <optimizationlinuxx64MIBCRuntimeVersion>1.0.0-prerelease.25171.2</optimizationlinuxx64MIBCRuntimeVersion>
+    <optimizationlinuxarm64MIBCRuntimeVersion>1.0.0-prerelease.25171.2</optimizationlinuxarm64MIBCRuntimeVersion>
+    <optimizationPGOCoreCLRVersion>1.0.0-prerelease.25171.2</optimizationPGOCoreCLRVersion>
     <!-- Not auto-updated. -->
     <MicrosoftDiaSymReaderVersion>2.0.0</MicrosoftDiaSymReaderVersion>
     <MicrosoftDiaSymReaderNativeVersion>17.10.0-beta1.24272.1</MicrosoftDiaSymReaderNativeVersion>
@@ -201,7 +208,7 @@
     <NewtonsoftJsonBsonVersion>1.0.2</NewtonsoftJsonBsonVersion>
     <SQLitePCLRawbundle_greenVersion>2.0.4</SQLitePCLRawbundle_greenVersion>
     <MoqVersion>4.18.4</MoqVersion>
-    <FluentAssertionsVersion>6.7.0</FluentAssertionsVersion>
+    <AwesomeAssertionsVersion>8.0.2</AwesomeAssertionsVersion>
     <FsCheckVersion>2.14.3</FsCheckVersion>
     <CommandLineParserVersion>2.9.1</CommandLineParserVersion>
     <!-- Android gRPC client tests -->
@@ -226,51 +233,51 @@
     <MicrosoftNativeQuicMsQuicSchannelVersion>2.4.8</MicrosoftNativeQuicMsQuicSchannelVersion>
     <SystemNetMsQuicTransportVersion>9.0.0-alpha.1.24167.3</SystemNetMsQuicTransportVersion>
     <!-- Mono LLVM -->
-    <runtimelinuxarm64MicrosoftNETCoreRuntimeMonoLLVMLibclangVersion>19.1.0-alpha.1.25103.1</runtimelinuxarm64MicrosoftNETCoreRuntimeMonoLLVMLibclangVersion>
-    <runtimelinuxarm64MicrosoftNETCoreRuntimeMonoLLVMSdkVersion>19.1.0-alpha.1.25103.1</runtimelinuxarm64MicrosoftNETCoreRuntimeMonoLLVMSdkVersion>
-    <runtimelinuxarm64MicrosoftNETCoreRuntimeMonoLLVMToolsVersion>19.1.0-alpha.1.25103.1</runtimelinuxarm64MicrosoftNETCoreRuntimeMonoLLVMToolsVersion>
-    <runtimelinuxmuslarm64MicrosoftNETCoreRuntimeMonoLLVMLibclangVersion>19.1.0-alpha.1.25103.1</runtimelinuxmuslarm64MicrosoftNETCoreRuntimeMonoLLVMLibclangVersion>
-    <runtimelinuxmuslarm64MicrosoftNETCoreRuntimeMonoLLVMSdkVersion>19.1.0-alpha.1.25103.1</runtimelinuxmuslarm64MicrosoftNETCoreRuntimeMonoLLVMSdkVersion>
-    <runtimelinuxmuslarm64MicrosoftNETCoreRuntimeMonoLLVMToolsVersion>19.1.0-alpha.1.25103.1</runtimelinuxmuslarm64MicrosoftNETCoreRuntimeMonoLLVMToolsVersion>
-    <runtimelinuxx64MicrosoftNETCoreRuntimeMonoLLVMLibclangVersion>19.1.0-alpha.1.25103.1</runtimelinuxx64MicrosoftNETCoreRuntimeMonoLLVMLibclangVersion>
-    <runtimelinuxx64MicrosoftNETCoreRuntimeMonoLLVMSdkVersion>19.1.0-alpha.1.25103.1</runtimelinuxx64MicrosoftNETCoreRuntimeMonoLLVMSdkVersion>
-    <runtimelinuxx64MicrosoftNETCoreRuntimeMonoLLVMToolsVersion>19.1.0-alpha.1.25103.1</runtimelinuxx64MicrosoftNETCoreRuntimeMonoLLVMToolsVersion>
-    <runtimelinuxmuslx64MicrosoftNETCoreRuntimeMonoLLVMLibclangVersion>19.1.0-alpha.1.25103.1</runtimelinuxmuslx64MicrosoftNETCoreRuntimeMonoLLVMLibclangVersion>
-    <runtimelinuxmuslx64MicrosoftNETCoreRuntimeMonoLLVMSdkVersion>19.1.0-alpha.1.25103.1</runtimelinuxmuslx64MicrosoftNETCoreRuntimeMonoLLVMSdkVersion>
-    <runtimelinuxmuslx64MicrosoftNETCoreRuntimeMonoLLVMToolsVersion>19.1.0-alpha.1.25103.1</runtimelinuxmuslx64MicrosoftNETCoreRuntimeMonoLLVMToolsVersion>
-    <runtimewinx64MicrosoftNETCoreRuntimeMonoLLVMLibclangVersion>19.1.0-alpha.1.25103.1</runtimewinx64MicrosoftNETCoreRuntimeMonoLLVMLibclangVersion>
-    <runtimewinx64MicrosoftNETCoreRuntimeMonoLLVMSdkVersion>19.1.0-alpha.1.25103.1</runtimewinx64MicrosoftNETCoreRuntimeMonoLLVMSdkVersion>
-    <runtimewinx64MicrosoftNETCoreRuntimeMonoLLVMToolsVersion>19.1.0-alpha.1.25103.1</runtimewinx64MicrosoftNETCoreRuntimeMonoLLVMToolsVersion>
-    <runtimeosxarm64MicrosoftNETCoreRuntimeMonoLLVMLibclangVersion>19.1.0-alpha.1.25103.1</runtimeosxarm64MicrosoftNETCoreRuntimeMonoLLVMLibclangVersion>
-    <runtimeosxarm64MicrosoftNETCoreRuntimeMonoLLVMSdkVersion>19.1.0-alpha.1.25103.1</runtimeosxarm64MicrosoftNETCoreRuntimeMonoLLVMSdkVersion>
-    <runtimeosxarm64MicrosoftNETCoreRuntimeMonoLLVMToolsVersion>19.1.0-alpha.1.25103.1</runtimeosxarm64MicrosoftNETCoreRuntimeMonoLLVMToolsVersion>
-    <runtimeosxx64MicrosoftNETCoreRuntimeMonoLLVMLibclangVersion>19.1.0-alpha.1.25103.1</runtimeosxx64MicrosoftNETCoreRuntimeMonoLLVMLibclangVersion>
-    <runtimeosxx64MicrosoftNETCoreRuntimeMonoLLVMSdkVersion>19.1.0-alpha.1.25103.1</runtimeosxx64MicrosoftNETCoreRuntimeMonoLLVMSdkVersion>
-    <runtimeosxx64MicrosoftNETCoreRuntimeMonoLLVMToolsVersion>19.1.0-alpha.1.25103.1</runtimeosxx64MicrosoftNETCoreRuntimeMonoLLVMToolsVersion>
+    <runtimelinuxarm64MicrosoftNETCoreRuntimeMonoLLVMLibclangVersion>19.1.0-alpha.1.25167.1</runtimelinuxarm64MicrosoftNETCoreRuntimeMonoLLVMLibclangVersion>
+    <runtimelinuxarm64MicrosoftNETCoreRuntimeMonoLLVMSdkVersion>19.1.0-alpha.1.25167.1</runtimelinuxarm64MicrosoftNETCoreRuntimeMonoLLVMSdkVersion>
+    <runtimelinuxarm64MicrosoftNETCoreRuntimeMonoLLVMToolsVersion>19.1.0-alpha.1.25167.1</runtimelinuxarm64MicrosoftNETCoreRuntimeMonoLLVMToolsVersion>
+    <runtimelinuxmuslarm64MicrosoftNETCoreRuntimeMonoLLVMLibclangVersion>19.1.0-alpha.1.25167.1</runtimelinuxmuslarm64MicrosoftNETCoreRuntimeMonoLLVMLibclangVersion>
+    <runtimelinuxmuslarm64MicrosoftNETCoreRuntimeMonoLLVMSdkVersion>19.1.0-alpha.1.25167.1</runtimelinuxmuslarm64MicrosoftNETCoreRuntimeMonoLLVMSdkVersion>
+    <runtimelinuxmuslarm64MicrosoftNETCoreRuntimeMonoLLVMToolsVersion>19.1.0-alpha.1.25167.1</runtimelinuxmuslarm64MicrosoftNETCoreRuntimeMonoLLVMToolsVersion>
+    <runtimelinuxx64MicrosoftNETCoreRuntimeMonoLLVMLibclangVersion>19.1.0-alpha.1.25167.1</runtimelinuxx64MicrosoftNETCoreRuntimeMonoLLVMLibclangVersion>
+    <runtimelinuxx64MicrosoftNETCoreRuntimeMonoLLVMSdkVersion>19.1.0-alpha.1.25167.1</runtimelinuxx64MicrosoftNETCoreRuntimeMonoLLVMSdkVersion>
+    <runtimelinuxx64MicrosoftNETCoreRuntimeMonoLLVMToolsVersion>19.1.0-alpha.1.25167.1</runtimelinuxx64MicrosoftNETCoreRuntimeMonoLLVMToolsVersion>
+    <runtimelinuxmuslx64MicrosoftNETCoreRuntimeMonoLLVMLibclangVersion>19.1.0-alpha.1.25167.1</runtimelinuxmuslx64MicrosoftNETCoreRuntimeMonoLLVMLibclangVersion>
+    <runtimelinuxmuslx64MicrosoftNETCoreRuntimeMonoLLVMSdkVersion>19.1.0-alpha.1.25167.1</runtimelinuxmuslx64MicrosoftNETCoreRuntimeMonoLLVMSdkVersion>
+    <runtimelinuxmuslx64MicrosoftNETCoreRuntimeMonoLLVMToolsVersion>19.1.0-alpha.1.25167.1</runtimelinuxmuslx64MicrosoftNETCoreRuntimeMonoLLVMToolsVersion>
+    <runtimewinx64MicrosoftNETCoreRuntimeMonoLLVMLibclangVersion>19.1.0-alpha.1.25167.1</runtimewinx64MicrosoftNETCoreRuntimeMonoLLVMLibclangVersion>
+    <runtimewinx64MicrosoftNETCoreRuntimeMonoLLVMSdkVersion>19.1.0-alpha.1.25167.1</runtimewinx64MicrosoftNETCoreRuntimeMonoLLVMSdkVersion>
+    <runtimewinx64MicrosoftNETCoreRuntimeMonoLLVMToolsVersion>19.1.0-alpha.1.25167.1</runtimewinx64MicrosoftNETCoreRuntimeMonoLLVMToolsVersion>
+    <runtimeosxarm64MicrosoftNETCoreRuntimeMonoLLVMLibclangVersion>19.1.0-alpha.1.25167.1</runtimeosxarm64MicrosoftNETCoreRuntimeMonoLLVMLibclangVersion>
+    <runtimeosxarm64MicrosoftNETCoreRuntimeMonoLLVMSdkVersion>19.1.0-alpha.1.25167.1</runtimeosxarm64MicrosoftNETCoreRuntimeMonoLLVMSdkVersion>
+    <runtimeosxarm64MicrosoftNETCoreRuntimeMonoLLVMToolsVersion>19.1.0-alpha.1.25167.1</runtimeosxarm64MicrosoftNETCoreRuntimeMonoLLVMToolsVersion>
+    <runtimeosxx64MicrosoftNETCoreRuntimeMonoLLVMLibclangVersion>19.1.0-alpha.1.25167.1</runtimeosxx64MicrosoftNETCoreRuntimeMonoLLVMLibclangVersion>
+    <runtimeosxx64MicrosoftNETCoreRuntimeMonoLLVMSdkVersion>19.1.0-alpha.1.25167.1</runtimeosxx64MicrosoftNETCoreRuntimeMonoLLVMSdkVersion>
+    <runtimeosxx64MicrosoftNETCoreRuntimeMonoLLVMToolsVersion>19.1.0-alpha.1.25167.1</runtimeosxx64MicrosoftNETCoreRuntimeMonoLLVMToolsVersion>
     <!-- emscripten / Node
          Note: when the name is updated, make sure to update dependency name in eng/pipelines/common/xplat-setup.yml
                like - DarcDependenciesChanged.Microsoft_NET_Workload_Emscripten_Current_Manifest-10_0_100_Transport
     -->
-    <MicrosoftNETWorkloadEmscriptenCurrentManifest100100TransportVersion>10.0.0-preview.3.25155.1</MicrosoftNETWorkloadEmscriptenCurrentManifest100100TransportVersion>
+    <MicrosoftNETWorkloadEmscriptenCurrentManifest100100TransportVersion>10.0.0-preview.4.25176.1</MicrosoftNETWorkloadEmscriptenCurrentManifest100100TransportVersion>
     <MicrosoftNETRuntimeEmscriptenVersion>$(MicrosoftNETWorkloadEmscriptenCurrentManifest100100TransportVersion)</MicrosoftNETRuntimeEmscriptenVersion>
     <!-- workloads -->
     <SwixPackageVersion>1.1.87-gba258badda</SwixPackageVersion>
     <WixPackageVersion>1.0.0-v3.14.0.5722</WixPackageVersion>
     <!-- JIT Tools -->
-    <runtimelinuxarm64MicrosoftNETCoreRuntimeJITToolsVersion>19.1.0-alpha.1.25103.1</runtimelinuxarm64MicrosoftNETCoreRuntimeJITToolsVersion>
-    <runtimelinuxx64MicrosoftNETCoreRuntimeJITToolsVersion>19.1.0-alpha.1.25103.1</runtimelinuxx64MicrosoftNETCoreRuntimeJITToolsVersion>
-    <runtimelinuxmuslarm64MicrosoftNETCoreRuntimeJITToolsVersion>19.1.0-alpha.1.25103.1</runtimelinuxmuslarm64MicrosoftNETCoreRuntimeJITToolsVersion>
-    <runtimelinuxmuslx64MicrosoftNETCoreRuntimeJITToolsVersion>19.1.0-alpha.1.25103.1</runtimelinuxmuslx64MicrosoftNETCoreRuntimeJITToolsVersion>
-    <runtimewinarm64MicrosoftNETCoreRuntimeJITToolsVersion>19.1.0-alpha.1.25103.1</runtimewinarm64MicrosoftNETCoreRuntimeJITToolsVersion>
-    <runtimewinx64MicrosoftNETCoreRuntimeJITToolsVersion>19.1.0-alpha.1.25103.1</runtimewinx64MicrosoftNETCoreRuntimeJITToolsVersion>
-    <runtimeosxarm64MicrosoftNETCoreRuntimeJITToolsVersion>19.1.0-alpha.1.25103.1</runtimeosxarm64MicrosoftNETCoreRuntimeJITToolsVersion>
-    <runtimeosxx64MicrosoftNETCoreRuntimeJITToolsVersion>19.1.0-alpha.1.25103.1</runtimeosxx64MicrosoftNETCoreRuntimeJITToolsVersion>
+    <runtimelinuxarm64MicrosoftNETCoreRuntimeJITToolsVersion>19.1.0-alpha.1.25167.1</runtimelinuxarm64MicrosoftNETCoreRuntimeJITToolsVersion>
+    <runtimelinuxx64MicrosoftNETCoreRuntimeJITToolsVersion>19.1.0-alpha.1.25167.1</runtimelinuxx64MicrosoftNETCoreRuntimeJITToolsVersion>
+    <runtimelinuxmuslarm64MicrosoftNETCoreRuntimeJITToolsVersion>19.1.0-alpha.1.25167.1</runtimelinuxmuslarm64MicrosoftNETCoreRuntimeJITToolsVersion>
+    <runtimelinuxmuslx64MicrosoftNETCoreRuntimeJITToolsVersion>19.1.0-alpha.1.25167.1</runtimelinuxmuslx64MicrosoftNETCoreRuntimeJITToolsVersion>
+    <runtimewinarm64MicrosoftNETCoreRuntimeJITToolsVersion>19.1.0-alpha.1.25167.1</runtimewinarm64MicrosoftNETCoreRuntimeJITToolsVersion>
+    <runtimewinx64MicrosoftNETCoreRuntimeJITToolsVersion>19.1.0-alpha.1.25167.1</runtimewinx64MicrosoftNETCoreRuntimeJITToolsVersion>
+    <runtimeosxarm64MicrosoftNETCoreRuntimeJITToolsVersion>19.1.0-alpha.1.25167.1</runtimeosxarm64MicrosoftNETCoreRuntimeJITToolsVersion>
+    <runtimeosxx64MicrosoftNETCoreRuntimeJITToolsVersion>19.1.0-alpha.1.25167.1</runtimeosxx64MicrosoftNETCoreRuntimeJITToolsVersion>
     <!-- BrowserDebugProxy libs -->
     <MicrosoftExtensionsLoggingVersion>3.1.7</MicrosoftExtensionsLoggingVersion>
     <MicrosoftSymbolStoreVersion>1.0.406601</MicrosoftSymbolStoreVersion>
     <!-- sdk version, for testing workloads -->
     <SdkVersionForWorkloadTesting>$(MicrosoftDotNetApiCompatTaskVersion)</SdkVersionForWorkloadTesting>
-    <runtimewinx64MicrosoftNETCoreRuntimeWasmNodeTransportPackageVersion>10.0.0-alpha.1.25125.1</runtimewinx64MicrosoftNETCoreRuntimeWasmNodeTransportPackageVersion>
+    <runtimewinx64MicrosoftNETCoreRuntimeWasmNodeTransportPackageVersion>10.0.0-alpha.1.25169.1</runtimewinx64MicrosoftNETCoreRuntimeWasmNodeTransportPackageVersion>
     <EmsdkPackageVersion>$(MicrosoftNETRuntimeEmscriptenVersion)</EmsdkPackageVersion>
     <NodePackageVersion>$(runtimewinx64MicrosoftNETCoreRuntimeWasmNodeTransportPackageVersion)</NodePackageVersion>
     <!-- The package path for python in src/mono/mono.proj needs to be updated if this changes-->
diff --git a/eng/build-analysis-configuration.json b/eng/build-analysis-configuration.json
index 06bf441873e2dc..208e753c836201 100644
--- a/eng/build-analysis-configuration.json
+++ b/eng/build-analysis-configuration.json
@@ -15,6 +15,10 @@
        {
          "PipelineId": 157,
          "PipelineName": "runtime-llvm"
+       },
+       {
+         "PipelineId": 265,
+         "PipelineName": "runtime-nativeaot-outerloop"
        }
     ]
 }
diff --git a/eng/build.ps1 b/eng/build.ps1
index e38be81d660ecd..dd41e2cf85fbe9 100644
--- a/eng/build.ps1
+++ b/eng/build.ps1
@@ -78,7 +78,7 @@ function Get-Help() {
 
   Write-Host "Libraries settings:"
   Write-Host "  -coverage               Collect code coverage when testing."
-  Write-Host "  -framework (-f)         Build framework: net10.0 or net48."
+  Write-Host "  -framework (-f)         Build framework: net10.0 or net481."
   Write-Host "                          [Default: net10.0]"
   Write-Host "  -testnobuild            Skip building tests when invoking -test."
   Write-Host "  -testscope              Scope tests, allowed values: innerloop, outerloop, all."
diff --git a/eng/build.sh b/eng/build.sh
index 1cab7739726fa3..a4e36af19ee956 100755
--- a/eng/build.sh
+++ b/eng/build.sh
@@ -66,7 +66,7 @@ usage()
 
   echo "Libraries settings:"
   echo "  --coverage                 Collect code coverage when testing."
-  echo "  --framework (-f)           Build framework: net10.0 or net48."
+  echo "  --framework (-f)           Build framework: net10.0 or net481."
   echo "                             [Default: net10.0]"
   echo "  --testnobuild              Skip building tests when invoking -test."
   echo "  --testscope                Test scope, allowed values: innerloop, outerloop, all."
@@ -542,6 +542,10 @@ fi
 if [[ "$os" == "browser" ]]; then
     # override default arch for Browser, we only support wasm
     arch=wasm
+    # because on docker instance without swap file, MSBuild nodes need to make some room for LLVM
+    # https://github.com/dotnet/runtime/issues/113724
+    # this is hexa percentage: 46-> 70%
+    export DOTNET_GCHeapHardLimitPercent="46"
 fi
 if [[ "$os" == "wasi" ]]; then
     # override default arch for wasi, we only support wasm
diff --git a/eng/common/CIBuild.cmd b/eng/common/CIBuild.cmd
index 56c2f25ac22ffa..ac1f72bf94e055 100644
--- a/eng/common/CIBuild.cmd
+++ b/eng/common/CIBuild.cmd
@@ -1,2 +1,2 @@
 @echo off
-powershell -ExecutionPolicy ByPass -NoProfile -command "& """%~dp0Build.ps1""" -restore -build -test -sign -pack -publish -ci %*"
\ No newline at end of file
+powershell -ExecutionPolicy ByPass -NoProfile -command "& """%~dp0Build.ps1""" -restore -build -test -sign -pack -publish -ci %*"
diff --git a/eng/common/build.ps1 b/eng/common/build.ps1
index 438f9920c43e4e..d419c3a2eff55c 100644
--- a/eng/common/build.ps1
+++ b/eng/common/build.ps1
@@ -7,6 +7,7 @@ Param(
   [string] $msbuildEngine = $null,
   [bool] $warnAsError = $true,
   [bool] $nodeReuse = $true,
+  [switch] $buildCheck = $false,
   [switch][Alias('r')]$restore,
   [switch] $deployDeps,
   [switch][Alias('b')]$build,
@@ -71,6 +72,8 @@ function Print-Usage() {
   Write-Host "  -msbuildEngine <value>  Msbuild engine to use to run build ('dotnet', 'vs', or unspecified)."
   Write-Host "  -excludePrereleaseVS    Set to exclude build engines in prerelease versions of Visual Studio"
   Write-Host "  -nativeToolsOnMachine   Sets the native tools on machine environment variable (indicating that the script should use native tools on machine)"
+  Write-Host "  -nodeReuse <value>      Sets nodereuse msbuild parameter ('true' or 'false')"
+  Write-Host "  -buildCheck             Sets /check msbuild parameter"
   Write-Host ""
 
   Write-Host "Command line arguments not listed above are passed thru to msbuild."
@@ -97,6 +100,7 @@ function Build {
 
   $bl = if ($binaryLog) { '/bl:' + (Join-Path $LogDir 'Build.binlog') } else { '' }
   $platformArg = if ($platform) { "/p:Platform=$platform" } else { '' }
+  $check = if ($buildCheck) { '/check' } else { '' }
 
   if ($projects) {
     # Re-assign properties to a new variable because PowerShell doesn't let us append properties directly for unclear reasons.
@@ -113,6 +117,7 @@ function Build {
   MSBuild $toolsetBuildProj `
     $bl `
     $platformArg `
+    $check `
     /p:Configuration=$configuration `
     /p:RepoRoot=$RepoRoot `
     /p:Restore=$restore `
@@ -166,4 +171,4 @@ catch {
   ExitWithExitCode 1
 }
 
-ExitWithExitCode 0
+ExitWithExitCode 0
\ No newline at end of file
diff --git a/eng/common/build.sh b/eng/common/build.sh
index 483647daf182c6..e24bb68f484001 100755
--- a/eng/common/build.sh
+++ b/eng/common/build.sh
@@ -42,6 +42,7 @@ usage()
   echo "  --prepareMachine         Prepare machine for CI run, clean up processes after build"
   echo "  --nodeReuse <value>      Sets nodereuse msbuild parameter ('true' or 'false')"
   echo "  --warnAsError <value>    Sets warnaserror msbuild parameter ('true' or 'false')"
+  echo "  --buildCheck <value>     Sets /check msbuild parameter"
   echo ""
   echo "Command line arguments not listed above are passed thru to msbuild."
   echo "Arguments can also be passed in with a single hyphen."
@@ -76,6 +77,7 @@ clean=false
 
 warn_as_error=true
 node_reuse=true
+build_check=false
 binary_log=false
 exclude_ci_binary_log=false
 pipelines_log=false
@@ -173,6 +175,9 @@ while [[ $# > 0 ]]; do
       node_reuse=$2
       shift
       ;;
+    -buildcheck)
+      build_check=true
+      ;;
     -runtimesourcefeed)
       runtime_source_feed=$2
       shift
@@ -224,8 +229,14 @@ function Build {
     bl="/bl:\"$log_dir/Build.binlog\""
   fi
 
+  local check=""
+  if [[ "$build_check" == true ]]; then
+    check="/check"
+  fi
+
   MSBuild $_InitializeToolset \
     $bl \
+    $check \
     /p:Configuration=$configuration \
     /p:RepoRoot="$repo_root" \
     /p:Restore=$restore \
@@ -256,4 +267,4 @@ if [[ "$restore" == true ]]; then
   InitializeNativeTools
 fi
 
-Build
+Build
\ No newline at end of file
diff --git a/eng/common/cibuild.sh b/eng/common/cibuild.sh
index 1a02c0dec8fd7b..66e3b0ac61c365 100755
--- a/eng/common/cibuild.sh
+++ b/eng/common/cibuild.sh
@@ -13,4 +13,4 @@ while [[ -h $source ]]; do
 done
 scriptroot="$( cd -P "$( dirname "$source" )" && pwd )"
 
-. "$scriptroot/build.sh" --restore --build --test --pack --publish --ci $@
\ No newline at end of file
+. "$scriptroot/build.sh" --restore --build --test --pack --publish --ci $@
diff --git a/eng/common/core-templates/job/job.yml b/eng/common/core-templates/job/job.yml
index 295c9a2317c44b..4dfb3d86836c1d 100644
--- a/eng/common/core-templates/job/job.yml
+++ b/eng/common/core-templates/job/job.yml
@@ -23,7 +23,6 @@ parameters:
   enablePublishBuildArtifacts: false
   enablePublishBuildAssets: false
   enablePublishTestResults: false
-  enablePublishUsingPipelines: false
   enableBuildRetry: false
   mergeTestResults: false
   testRunTitle: ''
diff --git a/eng/common/core-templates/job/publish-build-assets.yml b/eng/common/core-templates/job/publish-build-assets.yml
index c7d59dcbfe1566..5da3c2a2a20967 100644
--- a/eng/common/core-templates/job/publish-build-assets.yml
+++ b/eng/common/core-templates/job/publish-build-assets.yml
@@ -20,9 +20,6 @@ parameters:
   #           if 'true', the build won't run any of the internal only steps, even if it is running in non-public projects.
   runAsPublic: false
 
-  # Optional: whether the build's artifacts will be published using release pipelines or direct feed publishing
-  publishUsingPipelines: false
-
   # Optional: whether the build's artifacts will be published using release pipelines or direct feed publishing
   publishAssetsImmediately: false
 
@@ -96,7 +93,6 @@ jobs:
         arguments: -task PublishBuildAssets -restore -msbuildEngine dotnet
           /p:ManifestsPath='$(Build.StagingDirectory)/AssetManifests'
           /p:MaestroApiEndpoint=https://maestro.dot.net
-          /p:PublishUsingPipelines=${{ parameters.publishUsingPipelines }}
           /p:OfficialBuildId=$(Build.BuildNumber)
       condition: ${{ parameters.condition }}
       continueOnError: ${{ parameters.continueOnError }}
diff --git a/eng/common/core-templates/jobs/codeql-build.yml b/eng/common/core-templates/jobs/codeql-build.yml
index f2144252cc65c8..693b00b370447b 100644
--- a/eng/common/core-templates/jobs/codeql-build.yml
+++ b/eng/common/core-templates/jobs/codeql-build.yml
@@ -15,7 +15,6 @@ jobs:
     enablePublishBuildArtifacts: false
     enablePublishTestResults: false
     enablePublishBuildAssets: false
-    enablePublishUsingPipelines: false
     enableTelemetry: true
 
     variables:
diff --git a/eng/common/core-templates/jobs/jobs.yml b/eng/common/core-templates/jobs/jobs.yml
index ea69be4341c62f..3d2deaa4a06d35 100644
--- a/eng/common/core-templates/jobs/jobs.yml
+++ b/eng/common/core-templates/jobs/jobs.yml
@@ -5,9 +5,6 @@ parameters:
   # Optional: Include PublishBuildArtifacts task
   enablePublishBuildArtifacts: false
 
-  # Optional: Enable publishing using release pipelines
-  enablePublishUsingPipelines: false
-
   # Optional: Enable running the source-build jobs to build repo from source
   enableSourceBuild: false
 
@@ -112,7 +109,6 @@ jobs:
           - Source_Build_Complete
 
         runAsPublic: ${{ parameters.runAsPublic }}
-        publishUsingPipelines: ${{ parameters.enablePublishUsingPipelines }}
         publishAssetsImmediately: ${{ parameters.publishAssetsImmediately }}
         enablePublishBuildArtifacts: ${{ parameters.enablePublishBuildArtifacts }}
         artifactsPublishingAdditionalParameters: ${{ parameters.artifactsPublishingAdditionalParameters }}
diff --git a/eng/common/core-templates/steps/generate-sbom.yml b/eng/common/core-templates/steps/generate-sbom.yml
index b8e6dea83c2886..44a9636cdff90a 100644
--- a/eng/common/core-templates/steps/generate-sbom.yml
+++ b/eng/common/core-templates/steps/generate-sbom.yml
@@ -38,7 +38,7 @@ steps:
       PackageName: ${{ parameters.packageName }}
       BuildDropPath: ${{ parameters.buildDropPath }}
       PackageVersion: ${{ parameters.packageVersion }}
-      ManifestDirPath: ${{ parameters.manifestDirPath }}
+      ManifestDirPath: ${{ parameters.manifestDirPath }}/$(ARTIFACT_NAME)
       ${{ if ne(parameters.IgnoreDirectories, '') }}:
         AdditionalComponentDetectorArgs: '--IgnoreDirectories ${{ parameters.IgnoreDirectories }}'
 
diff --git a/eng/common/core-templates/steps/install-microbuild.yml b/eng/common/core-templates/steps/install-microbuild.yml
index 2a6a529482b522..dba506e74c3cab 100644
--- a/eng/common/core-templates/steps/install-microbuild.yml
+++ b/eng/common/core-templates/steps/install-microbuild.yml
@@ -5,7 +5,7 @@ parameters:
   # Will be ignored if 'enableMicrobuild' is false or 'Agent.Os' is 'Windows_NT'
   enableMicrobuildForMacAndLinux: false
   # Location of the MicroBuild output folder
-  microBuildOutputFolder: '$(Agent.TempDirectory)'
+  microBuildOutputFolder: '$(Build.SourcesDirectory)'
   continueOnError: false
 
 steps:
@@ -41,7 +41,7 @@ steps:
         inputs:
           packageType: sdk
           version: 8.0.x
-          installationPath: ${{ parameters.microBuildOutputFolder }}/dotnet
+          installationPath: ${{ parameters.microBuildOutputFolder }}/.dotnet
           workingDirectory: ${{ parameters.microBuildOutputFolder }}
         condition: and(succeeded(), ne(variables['Agent.Os'], 'Windows_NT'))
 
@@ -53,6 +53,7 @@ steps:
         feedSource: https://dnceng.pkgs.visualstudio.com/_packaging/MicroBuildToolset/nuget/v3/index.json
         ${{ if and(eq(parameters.enableMicrobuildForMacAndLinux, 'true'), ne(variables['Agent.Os'], 'Windows_NT')) }}:
           azureSubscription: 'MicroBuild Signing Task (DevDiv)'
+          useEsrpCli: true
       env:
         TeamName: $(_TeamName)
         MicroBuildOutputFolderOverride: ${{ parameters.microBuildOutputFolder }}
@@ -71,3 +72,28 @@ steps:
             eq(variables['_SignType'], 'real')
           )
         ))
+
+    # Workaround for ESRP CLI on Linux - https://github.com/dotnet/source-build/issues/4964
+    - ${{ if eq(parameters.enableMicrobuildForMacAndLinux, 'true') }}:
+      - task: UseDotNet@2
+        displayName: Install .NET 9.0 SDK for ESRP CLI Workaround
+        inputs:
+          packageType: sdk
+          version: 9.0.x
+          installationPath: ${{ parameters.microBuildOutputFolder }}/.dotnet
+          workingDirectory: ${{ parameters.microBuildOutputFolder }}
+        condition: and(succeeded(), eq(variables['Agent.OS'], 'Linux'))
+
+      - task: PowerShell@2
+        displayName: Workaround for ESRP CLI on Linux
+        inputs:
+          targetType: 'inline'
+          script: |
+            Write-Host "Copying Linux Path"
+            $MBSIGN_APPFOLDER = '$(MBSIGN_APPFOLDER)'
+            $MBSIGN_APPFOLDER = $MBSIGN_APPFOLDER -replace '/build', ''
+            $MBSIGN_APPFOLDER = $MBSIGN_APPFOLDER + '/1.1.1032' + '/build'
+            $MBSIGN_APPFOLDER | Write-Host
+            $SignConfigPath = $MBSIGN_APPFOLDER + '/signconfig.xml'
+            Copy-Item -Path "$(MBSIGN_APPFOLDER)/signconfig.xml" -Destination $SignConfigPath -Force
+        condition: and(succeeded(), eq(variables['Agent.OS'], 'Linux'))
diff --git a/eng/common/cross/armel/armel.jessie.patch b/eng/common/cross/armel/armel.jessie.patch
deleted file mode 100644
index 2d2615619351fc..00000000000000
--- a/eng/common/cross/armel/armel.jessie.patch
+++ /dev/null
@@ -1,43 +0,0 @@
-diff -u -r a/usr/include/urcu/uatomic/generic.h b/usr/include/urcu/uatomic/generic.h
---- a/usr/include/urcu/uatomic/generic.h	2014-10-22 15:00:58.000000000 -0700
-+++ b/usr/include/urcu/uatomic/generic.h	2020-10-30 21:38:28.550000000 -0700
-@@ -69,10 +69,10 @@
- #endif
- #ifdef UATOMIC_HAS_ATOMIC_SHORT
- 	case 2:
--		return __sync_val_compare_and_swap_2(addr, old, _new);
-+		return __sync_val_compare_and_swap_2((uint16_t*) addr, old, _new);
- #endif
- 	case 4:
--		return __sync_val_compare_and_swap_4(addr, old, _new);
-+		return __sync_val_compare_and_swap_4((uint32_t*) addr, old, _new);
- #if (CAA_BITS_PER_LONG == 64)
- 	case 8:
- 		return __sync_val_compare_and_swap_8(addr, old, _new);
-@@ -109,7 +109,7 @@
- 		return;
- #endif
- 	case 4:
--		__sync_and_and_fetch_4(addr, val);
-+		__sync_and_and_fetch_4((uint32_t*) addr, val);
- 		return;
- #if (CAA_BITS_PER_LONG == 64)
- 	case 8:
-@@ -148,7 +148,7 @@
- 		return;
- #endif
- 	case 4:
--		__sync_or_and_fetch_4(addr, val);
-+		__sync_or_and_fetch_4((uint32_t*) addr, val);
- 		return;
- #if (CAA_BITS_PER_LONG == 64)
- 	case 8:
-@@ -187,7 +187,7 @@
- 		return __sync_add_and_fetch_2(addr, val);
- #endif
- 	case 4:
--		return __sync_add_and_fetch_4(addr, val);
-+		return __sync_add_and_fetch_4((uint32_t*) addr, val);
- #if (CAA_BITS_PER_LONG == 64)
- 	case 8:
- 		return __sync_add_and_fetch_8(addr, val);
diff --git a/eng/common/cross/build-rootfs.sh b/eng/common/cross/build-rootfs.sh
index 74f399716ba861..d6f005b5dabea6 100755
--- a/eng/common/cross/build-rootfs.sh
+++ b/eng/common/cross/build-rootfs.sh
@@ -164,9 +164,13 @@ while :; do
         armel)
             __BuildArch=armel
             __UbuntuArch=armel
-            __UbuntuRepo="http://ftp.debian.org/debian/"
-            __CodeName=jessie
+            __UbuntuRepo="http://archive.debian.org/debian/"
+            __CodeName=buster
             __KeyringFile="/usr/share/keyrings/debian-archive-keyring.gpg"
+            __LLDB_Package="liblldb-6.0-dev"
+            __UbuntuPackages="${__UbuntuPackages// libomp-dev/}"
+            __UbuntuPackages="${__UbuntuPackages// libomp5/}"
+            __UbuntuSuites=
             ;;
         armv6)
             __BuildArch=armv6
@@ -278,46 +282,23 @@ while :; do
 
             ;;
         xenial) # Ubuntu 16.04
-            if [[ "$__CodeName" != "jessie" ]]; then
-                __CodeName=xenial
-            fi
-            ;;
-        zesty) # Ubuntu 17.04
-            if [[ "$__CodeName" != "jessie" ]]; then
-                __CodeName=zesty
-            fi
+            __CodeName=xenial
             ;;
         bionic) # Ubuntu 18.04
-            if [[ "$__CodeName" != "jessie" ]]; then
-                __CodeName=bionic
-            fi
+            __CodeName=bionic
             ;;
         focal) # Ubuntu 20.04
-            if [[ "$__CodeName" != "jessie" ]]; then
-                __CodeName=focal
-            fi
+            __CodeName=focal
             ;;
         jammy) # Ubuntu 22.04
-            if [[ "$__CodeName" != "jessie" ]]; then
-                __CodeName=jammy
-            fi
+            __CodeName=jammy
             ;;
         noble) # Ubuntu 24.04
-            if [[ "$__CodeName" != "jessie" ]]; then
-                __CodeName=noble
-            fi
+            __CodeName=noble
             if [[ -n "$__LLDB_Package" ]]; then
                 __LLDB_Package="liblldb-18-dev"
             fi
             ;;
-        jessie) # Debian 8
-            __CodeName=jessie
-            __KeyringFile="/usr/share/keyrings/debian-archive-keyring.gpg"
-
-            if [[ -z "$__UbuntuRepo" ]]; then
-                __UbuntuRepo="http://ftp.debian.org/debian/"
-            fi
-            ;;
         stretch) # Debian 9
             __CodeName=stretch
             __LLDB_Package="liblldb-6.0-dev"
@@ -333,7 +314,7 @@ while :; do
             __KeyringFile="/usr/share/keyrings/debian-archive-keyring.gpg"
 
             if [[ -z "$__UbuntuRepo" ]]; then
-                __UbuntuRepo="http://ftp.debian.org/debian/"
+                __UbuntuRepo="http://archive.debian.org/debian/"
             fi
             ;;
         bullseye) # Debian 11
@@ -473,10 +454,6 @@ if [[ "$__AlpineVersion" =~ 3\.1[345] ]]; then
     __AlpinePackages="${__AlpinePackages/compiler-rt/compiler-rt-static}"
 fi
 
-if [[ "$__BuildArch" == "armel" ]]; then
-    __LLDB_Package="lldb-3.5-dev"
-fi
-
 __UbuntuPackages+=" ${__LLDB_Package:-}"
 
 if [[ -z "$__UbuntuRepo" ]]; then
@@ -850,12 +827,6 @@ EOF
     if [[ "$__SkipUnmount" == "0" ]]; then
         umount "$__RootfsDir"/* || true
     fi
-
-    if [[ "$__BuildArch" == "armel" && "$__CodeName" == "jessie" ]]; then
-        pushd "$__RootfsDir"
-        patch -p1 < "$__CrossDir/$__BuildArch/armel.jessie.patch"
-        popd
-    fi
 elif [[ "$__Tizen" == "tizen" ]]; then
     ROOTFS_DIR="$__RootfsDir" "$__CrossDir/tizen-build-rootfs.sh" "$__BuildArch"
 else
diff --git a/eng/common/generate-sbom-prep.ps1 b/eng/common/generate-sbom-prep.ps1
index 3e5c1c74a1c50d..a0c7d792a76fbe 100644
--- a/eng/common/generate-sbom-prep.ps1
+++ b/eng/common/generate-sbom-prep.ps1
@@ -4,18 +4,26 @@ Param(
 
 . $PSScriptRoot\pipeline-logging-functions.ps1
 
+# Normally - we'd listen to the manifest path given, but 1ES templates will overwrite if this level gets uploaded directly
+# with their own overwriting ours. So we create it as a sub directory of the requested manifest path.
+$ArtifactName = "${env:SYSTEM_STAGENAME}_${env:AGENT_JOBNAME}_SBOM"
+$SafeArtifactName = $ArtifactName -replace '["/:<>\\|?@*"() ]', '_'
+$SbomGenerationDir = Join-Path $ManifestDirPath $SafeArtifactName
+
+Write-Host "Artifact name before : $ArtifactName"
+Write-Host "Artifact name after : $SafeArtifactName"
+
 Write-Host "Creating dir $ManifestDirPath"
+
 # create directory for sbom manifest to be placed
-if (!(Test-Path -path $ManifestDirPath))
+if (!(Test-Path -path $SbomGenerationDir))
 {
-  New-Item -ItemType Directory -path $ManifestDirPath
-  Write-Host "Successfully created directory $ManifestDirPath"
+  New-Item -ItemType Directory -path $SbomGenerationDir
+  Write-Host "Successfully created directory $SbomGenerationDir"
 }
 else{
   Write-PipelineTelemetryError -category 'Build'  "Unable to create sbom folder."
 }
 
 Write-Host "Updating artifact name"
-$artifact_name = "${env:SYSTEM_STAGENAME}_${env:AGENT_JOBNAME}_SBOM" -replace '["/:<>\\|?@*"() ]', '_'
-Write-Host "Artifact name $artifact_name"
-Write-Host "##vso[task.setvariable variable=ARTIFACT_NAME]$artifact_name"
+Write-Host "##vso[task.setvariable variable=ARTIFACT_NAME]$SafeArtifactName"
diff --git a/eng/common/generate-sbom-prep.sh b/eng/common/generate-sbom-prep.sh
index d5c76dc827b496..b8ecca72bbf506 100644
--- a/eng/common/generate-sbom-prep.sh
+++ b/eng/common/generate-sbom-prep.sh
@@ -14,19 +14,24 @@ done
 scriptroot="$( cd -P "$( dirname "$source" )" && pwd )"
 . $scriptroot/pipeline-logging-functions.sh
 
+
+# replace all special characters with _, some builds use special characters like : in Agent.Jobname, that is not a permissible name while uploading artifacts.
+artifact_name=$SYSTEM_STAGENAME"_"$AGENT_JOBNAME"_SBOM"
+safe_artifact_name="${artifact_name//["/:<>\\|?@*$" ]/_}"
 manifest_dir=$1
 
-if [ ! -d "$manifest_dir" ] ; then
-  mkdir -p "$manifest_dir"
-  echo "Sbom directory created." $manifest_dir
+# Normally - we'd listen to the manifest path given, but 1ES templates will overwrite if this level gets uploaded directly
+# with their own overwriting ours. So we create it as a sub directory of the requested manifest path.
+sbom_generation_dir="$manifest_dir/$safe_artifact_name"
+
+if [ ! -d "$sbom_generation_dir" ] ; then
+  mkdir -p "$sbom_generation_dir"
+  echo "Sbom directory created." $sbom_generation_dir
 else
   Write-PipelineTelemetryError -category 'Build'  "Unable to create sbom folder."
 fi
 
-artifact_name=$SYSTEM_STAGENAME"_"$AGENT_JOBNAME"_SBOM"
 echo "Artifact name before : "$artifact_name
-# replace all special characters with _, some builds use special characters like : in Agent.Jobname, that is not a permissible name while uploading artifacts.
-safe_artifact_name="${artifact_name//["/:<>\\|?@*$" ]/_}"
 echo "Artifact name after : "$safe_artifact_name
 export ARTIFACT_NAME=$safe_artifact_name
 echo "##vso[task.setvariable variable=ARTIFACT_NAME]$safe_artifact_name"
diff --git a/eng/common/native/install-dependencies.sh b/eng/common/native/install-dependencies.sh
index ce661e9e5cd8de..477a44f335be35 100755
--- a/eng/common/native/install-dependencies.sh
+++ b/eng/common/native/install-dependencies.sh
@@ -27,8 +27,9 @@ case "$os" in
                 libssl-dev libkrb5-dev pigz cpio
 
             localedef -i en_US -c -f UTF-8 -A /usr/share/locale/locale.alias en_US.UTF-8
-        elif [ "$ID" = "fedora" ] || [ "$ID" = "rhel" ]; then
-            dnf install -y cmake llvm lld lldb clang python curl libicu-devel openssl-devel krb5-devel lttng-ust-devel pigz cpio
+        elif [ "$ID" = "fedora" ] || [ "$ID" = "rhel" ] || [ "$ID" = "azurelinux" ]; then
+            pkg_mgr="$(command -v tdnf 2>/dev/null || command -v dnf)"
+            $pkg_mgr install -y cmake llvm lld lldb clang python curl libicu-devel openssl-devel krb5-devel lttng-ust-devel pigz cpio
         elif [ "$ID" = "alpine" ]; then
             apk add build-base cmake bash curl clang llvm-dev lld lldb krb5-dev lttng-ust-dev icu-dev openssl-dev pigz cpio
         else
@@ -44,7 +45,7 @@ case "$os" in
         export HOMEBREW_NO_INSTALLED_DEPENDENTS_CHECK=1
         # Skip brew update for now, see https://github.com/actions/setup-python/issues/577
         # brew update --preinstall
-        brew bundle --no-upgrade --no-lock --file=- <<EOF
+        brew bundle --no-upgrade --file=- <<EOF
 brew "cmake"
 brew "icu4c"
 brew "openssl@3"
diff --git a/eng/common/templates-official/job/job.yml b/eng/common/templates-official/job/job.yml
index 605692d2fb770c..a8a94328745823 100644
--- a/eng/common/templates-official/job/job.yml
+++ b/eng/common/templates-official/job/job.yml
@@ -16,6 +16,7 @@ jobs:
         parameters:
           PackageVersion: ${{ parameters.packageVersion }}
           BuildDropPath: ${{ parameters.buildDropPath }}
+          ManifestDirPath: $(Build.ArtifactStagingDirectory)/sbom
           publishArtifacts: false
 
     # publish artifacts
@@ -30,6 +31,7 @@ jobs:
             PathtoPublish: '$(Build.ArtifactStagingDirectory)/artifacts'
             ArtifactName: ${{ coalesce(parameters.artifacts.publish.artifacts.name , 'Artifacts_$(Agent.Os)_$(_BuildConfig)') }}
             condition: always()
+            retryCountOnTaskFailure: 10 # for any logs being locked
             continueOnError: true
         - ${{ if and(ne(parameters.artifacts.publish.logs, 'false'), ne(parameters.artifacts.publish.logs, '')) }}:
           - output: pipelineArtifact
@@ -38,6 +40,7 @@ jobs:
             displayName: 'Publish logs'
             continueOnError: true
             condition: always()
+            retryCountOnTaskFailure: 10 # for any logs being locked
             sbomEnabled: false  # we don't need SBOM for logs
 
       - ${{ if eq(parameters.enablePublishBuildArtifacts, true) }}:
@@ -45,7 +48,7 @@ jobs:
           displayName: Publish Logs
           PathtoPublish: '$(Build.ArtifactStagingDirectory)/artifacts/log/$(_BuildConfig)'
           publishLocation: Container
-          ArtifactName: ${{ coalesce(parameters.enablePublishBuildArtifacts.artifactName, '$(Agent.Os)_$(Agent.JobName)' ) }}
+          ArtifactName: ${{ coalesce(parameters.enablePublishBuildArtifacts.artifactName, '$(Agent.Os)_$(Agent.JobName)_Attempt$(System.JobAttempt)' ) }}
           continueOnError: true
           condition: always()
           sbomEnabled: false  # we don't need SBOM for logs
diff --git a/eng/common/templates-official/steps/publish-build-artifacts.yml b/eng/common/templates-official/steps/publish-build-artifacts.yml
index 100a3fc98493cd..fcf6637b2ebccc 100644
--- a/eng/common/templates-official/steps/publish-build-artifacts.yml
+++ b/eng/common/templates-official/steps/publish-build-artifacts.yml
@@ -24,6 +24,10 @@ parameters:
 - name: is1ESPipeline
   type: boolean
   default: true
+
+- name: retryCountOnTaskFailure
+  type: string
+  default: 10
   
 steps:
 - ${{ if ne(parameters.is1ESPipeline, true) }}:
@@ -38,4 +42,5 @@ steps:
     PathtoPublish: ${{ parameters.pathToPublish }}
     ${{ if parameters.artifactName }}:
       ArtifactName: ${{ parameters.artifactName }}
-      
+    ${{ if parameters.retryCountOnTaskFailure }}:
+      retryCountOnTaskFailure: ${{ parameters.retryCountOnTaskFailure }}
diff --git a/eng/common/templates/job/job.yml b/eng/common/templates/job/job.yml
index d1aeb92fcea519..7cbf668c22bc04 100644
--- a/eng/common/templates/job/job.yml
+++ b/eng/common/templates/job/job.yml
@@ -46,6 +46,7 @@ jobs:
               artifactName: ${{ coalesce(parameters.artifacts.publish.artifacts.name , 'Artifacts_$(Agent.Os)_$(_BuildConfig)') }}
               continueOnError: true
               condition: always()
+              retryCountOnTaskFailure: 10 # for any logs being locked
       - ${{ if and(ne(parameters.artifacts.publish.logs, 'false'), ne(parameters.artifacts.publish.logs, '')) }}:
         - template: /eng/common/core-templates/steps/publish-pipeline-artifacts.yml
           parameters:
@@ -56,6 +57,7 @@ jobs:
               displayName: 'Publish logs'
               continueOnError: true
               condition: always()
+              retryCountOnTaskFailure: 10 # for any logs being locked
               sbomEnabled: false  # we don't need SBOM for logs
 
     - ${{ if ne(parameters.enablePublishBuildArtifacts, 'false') }}:
@@ -66,7 +68,7 @@ jobs:
             displayName: Publish Logs
             pathToPublish: '$(Build.ArtifactStagingDirectory)/artifacts/log/$(_BuildConfig)'
             publishLocation: Container
-            artifactName: ${{ coalesce(parameters.enablePublishBuildArtifacts.artifactName, '$(Agent.Os)_$(Agent.JobName)' ) }}
+            artifactName: ${{ coalesce(parameters.enablePublishBuildArtifacts.artifactName, '$(Agent.Os)_$(Agent.JobName)_Attempt$(System.JobAttempt)' ) }}
             continueOnError: true
             condition: always()
 
diff --git a/eng/common/templates/steps/publish-build-artifacts.yml b/eng/common/templates/steps/publish-build-artifacts.yml
index 6428a98dfef68e..605e602e94d136 100644
--- a/eng/common/templates/steps/publish-build-artifacts.yml
+++ b/eng/common/templates/steps/publish-build-artifacts.yml
@@ -25,6 +25,10 @@ parameters:
   type: string
   default: 'Container'
 
+- name: retryCountOnTaskFailure
+  type: string
+  default: 10
+
 steps:
 - ${{ if eq(parameters.is1ESPipeline, true) }}:
   - 'eng/common/templates cannot be referenced from a 1ES managed template': error
@@ -37,4 +41,6 @@ steps:
     PublishLocation: ${{ parameters.publishLocation }}  
     PathtoPublish: ${{ parameters.pathToPublish }}
     ${{ if parameters.artifactName }}:
-      ArtifactName: ${{ parameters.artifactName }}
\ No newline at end of file
+      ArtifactName: ${{ parameters.artifactName }}
+    ${{ if parameters.retryCountOnTaskFailure }}:
+      retryCountOnTaskFailure: ${{ parameters.retryCountOnTaskFailure }}
diff --git a/eng/common/tools.ps1 b/eng/common/tools.ps1
index 95ccdf82e4bbca..7373e5305465d7 100644
--- a/eng/common/tools.ps1
+++ b/eng/common/tools.ps1
@@ -262,7 +262,7 @@ function GetDotNetInstallScript([string] $dotnetRoot) {
   if (!(Test-Path $installScript)) {
     Create-Directory $dotnetRoot
     $ProgressPreference = 'SilentlyContinue' # Don't display the console progress UI - it's a huge perf hit
-    $uri = "https://builds.dotnet.microsoft.com/dotnet/scripts/v1/dotnet-install.ps1"
+    $uri = "https://builds.dotnet.microsoft.com/dotnet/scripts/$dotnetInstallScriptVersion/dotnet-install.ps1"
 
     Retry({
       Write-Host "GET $uri"
diff --git a/eng/common/tools.sh b/eng/common/tools.sh
index 4a5fa99478d146..d51f300c772007 100755
--- a/eng/common/tools.sh
+++ b/eng/common/tools.sh
@@ -295,7 +295,7 @@ function with_retries {
 function GetDotNetInstallScript {
   local root=$1
   local install_script="$root/dotnet-install.sh"
-  local install_script_url="https://builds.dotnet.microsoft.com/dotnet/scripts/v1/dotnet-install.sh"
+  local install_script_url="https://builds.dotnet.microsoft.com/dotnet/scripts/$dotnetInstallScriptVersion/dotnet-install.sh"
 
   if [[ ! -a "$install_script" ]]; then
     mkdir -p "$root"
diff --git a/eng/docker/libraries-sdk.linux.Dockerfile b/eng/docker/libraries-sdk.linux.Dockerfile
index 7e06eeb5e8dcb8..74752318dc66b1 100644
--- a/eng/docker/libraries-sdk.linux.Dockerfile
+++ b/eng/docker/libraries-sdk.linux.Dockerfile
@@ -20,7 +20,7 @@ ENV _DOTNET_INSTALL_CHANNEL=$VERSION
 RUN rm -rf /usr/share/dotnet
 
 # Install latest daily SDK:
-RUN wget https://dot.net/v1/dotnet-install.sh
+RUN wget https://builds.dotnet.microsoft.com/dotnet/scripts/v1/dotnet-install.sh
 RUN bash ./dotnet-install.sh --channel $_DOTNET_INSTALL_CHANNEL --quality daily --install-dir /usr/share/dotnet
 
 # Collect the following artifacts under /live-runtime-artifacts,
diff --git a/eng/docker/libraries-sdk.windows.Dockerfile b/eng/docker/libraries-sdk.windows.Dockerfile
index 5345413303b719..437a70543ef689 100644
--- a/eng/docker/libraries-sdk.windows.Dockerfile
+++ b/eng/docker/libraries-sdk.windows.Dockerfile
@@ -14,7 +14,7 @@ USER ContainerAdministrator
 # remove the existing ASP.NET SDK, we want to keep only the latest one we download later
 RUN Remove-Item -Force -Recurse 'C:/Program Files/dotnet/shared/Microsoft.AspNetCore.App/*'
 
-RUN Invoke-WebRequest -Uri https://dot.net/v1/dotnet-install.ps1 -OutFile .\dotnet-install.ps1
+RUN Invoke-WebRequest -Uri https://builds.dotnet.microsoft.com/dotnet/scripts/v1/dotnet-install.ps1 -OutFile .\dotnet-install.ps1
 RUN & .\dotnet-install.ps1 -Channel $env:_DOTNET_INSTALL_CHANNEL -Quality daily -InstallDir 'C:/Program Files/dotnet'
 
 USER ContainerUser
diff --git a/eng/extract-for-crossdac.ps1 b/eng/extract-for-crossdac.ps1
index 24ce91d1043fd3..2ae2e61e5d3240 100644
--- a/eng/extract-for-crossdac.ps1
+++ b/eng/extract-for-crossdac.ps1
@@ -20,6 +20,6 @@ foreach ($file in Get-ChildItem $DownloadDirectory -Recurse -Filter '*.nupkg') {
         Write-Host "Extracting Package: $id $ver to $ExtractDirectory/$($id.ToLowerInvariant())/$ver"
         [System.IO.Compression.ZipFile]::ExtractToDirectory($file.FullName, "$ExtractDirectory/$($id.ToLowerInvariant())/$ver")
     } else {
-        throw "Unexpected file name: $($file.Name)"
+        Write-Host "Skipping non-runtime pack: $($file.Name)"
     }
 }
diff --git a/eng/native/configurecompiler.cmake b/eng/native/configurecompiler.cmake
index 1bc6dfdb1b6266..e8ad8615f6d4bd 100644
--- a/eng/native/configurecompiler.cmake
+++ b/eng/native/configurecompiler.cmake
@@ -919,7 +919,7 @@ if (MSVC)
 
     add_compile_options($<$<AND:$<COMPILE_LANGUAGE:C,CXX,ASM_MASM>,$<BOOL:$<TARGET_PROPERTY:CLR_EH_CONTINUATION>>>:/guard:ehcont>)
     add_link_options($<$<BOOL:$<TARGET_PROPERTY:CLR_EH_CONTINUATION>>:/guard:ehcont>)
-    #set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} /CETCOMPAT")
+    set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} /CETCOMPAT")
   endif (CLR_CMAKE_HOST_ARCH_AMD64 AND NOT CLR_CMAKE_RUNTIME_MONO)
 
   # Statically linked CRT (libcmt[d].lib, libvcruntime[d].lib and libucrt[d].lib) by default. This is done to avoid
diff --git a/eng/native/configureplatform.cmake b/eng/native/configureplatform.cmake
index 274ab363bf7905..b04bfc9ca1397c 100644
--- a/eng/native/configureplatform.cmake
+++ b/eng/native/configureplatform.cmake
@@ -522,7 +522,7 @@ if (CLR_CMAKE_TARGET_ANDROID OR CLR_CMAKE_TARGET_MACCATALYST OR CLR_CMAKE_TARGET
     set(CLR_CMAKE_USE_SYSTEM_ZLIB 1)
 endif()
 
-if (NOT CLR_CMAKE_TARGET_ANDROID)
+if (NOT CLR_CMAKE_TARGET_ANDROID AND NOT CLR_CMAKE_TARGET_BROWSER)
     # opt into building tools like ildasm/ilasm
     set(CLR_CMAKE_BUILD_TOOLS 1)
 endif()
diff --git a/eng/native/functions.cmake b/eng/native/functions.cmake
index ba5689e01727e4..581dbd28fb2bad 100644
--- a/eng/native/functions.cmake
+++ b/eng/native/functions.cmake
@@ -554,11 +554,11 @@ function(install_static_library targetName destination component)
   endif()
 endfunction()
 
-# install_clr(TARGETS targetName [targetName2 ...] [DESTINATIONS destination [destination2 ...]] [COMPONENT componentName] [INSTALL_ALL_ARTIFACTS])
+# install_clr(TARGETS targetName [targetName2 ...] [DESTINATIONS destination [destination2 ...]] [COMPONENT componentName])
 function(install_clr)
   set(multiValueArgs TARGETS DESTINATIONS)
   set(singleValueArgs COMPONENT)
-  set(options INSTALL_ALL_ARTIFACTS)
+  set(options "")
   cmake_parse_arguments(INSTALL_CLR "${options}" "${singleValueArgs}" "${multiValueArgs}" ${ARGV})
 
   if ("${INSTALL_CLR_TARGETS}" STREQUAL "")
@@ -594,14 +594,9 @@ function(install_clr)
     endif()
 
     foreach(destination ${destinations})
-        # Install the export libraries for static libraries.
-      if (${INSTALL_CLR_INSTALL_ALL_ARTIFACTS})
-        install(TARGETS ${targetName} DESTINATION ${destination} COMPONENT ${INSTALL_CLR_COMPONENT})
-      else()
-        # We don't need to install the export libraries for our DLLs
-        # since they won't be directly linked against.
-        install(PROGRAMS $<TARGET_FILE:${targetName}> DESTINATION ${destination} COMPONENT ${INSTALL_CLR_COMPONENT})
-      endif()
+      # We don't need to install the export libraries for our DLLs
+      # since they won't be directly linked against.
+      install(PROGRAMS $<TARGET_FILE:${targetName}> DESTINATION ${destination} COMPONENT ${INSTALL_CLR_COMPONENT})
       if (NOT "${symbolFile}" STREQUAL "")
         install_symbol_file(${symbolFile} ${destination} COMPONENT ${INSTALL_CLR_COMPONENT})
       endif()
diff --git a/eng/pipelines/common/global-build-job.yml b/eng/pipelines/common/global-build-job.yml
index 6bdd96038acf75..6e3c5c0cf3208d 100644
--- a/eng/pipelines/common/global-build-job.yml
+++ b/eng/pipelines/common/global-build-job.yml
@@ -52,9 +52,26 @@ jobs:
     timeoutInMinutes: ${{ parameters.timeoutInMinutes }}
     enablePublishTestResults: ${{ parameters.enablePublishTestResults }}
     testResultsFormat: ${{ parameters.testResultsFormat }}
+    enableMicrobuild: ${{ parameters.isOfficialBuild }}
+    enableMicrobuildForMacAndLinux: ${{ parameters.isOfficialBuild }}
 
-    ${{ if ne(parameters.templateContext, '') }}:
-      templateContext: ${{ parameters.templateContext }}
+    templateContext:
+      ${{ if ne(parameters.templateContext, '') }}:
+        ${{ each pair in parameters.templateContext }}:
+          ${{ if notIn(pair.key, 'outputs') }}:
+            ${{ pair.key }}: ${{ pair.value }}
+      outputs:
+        - ${{ if ne(parameters.templateContext.outputs, '') }}:
+          - ${{ each output in parameters.templateContext.outputs }}:
+            ${{ output.key }}: ${{ output.value }}
+        - ${{ if eq(parameters.isOfficialBuild, true) }}:
+          - output: pipelineArtifact
+            displayName: 'Publish Pipeline Artifacts'
+            ${{ if eq(parameters.hostedOs, '') }}:
+              artifactName: ${{ format('build_{0}{1}_{2}_{3}_{4}_Artifacts', parameters.osGroup, parameters.osSubgroup, parameters.archType, parameters.buildConfig, parameters.nameSuffix) }}
+            ${{ if ne(parameters.hostedOs, '') }}:
+              artifactName: ${{ format('build_{0}{1}_{2}_{3}_{4}_{5}_Artifacts', parameters.osGroup, parameters.osSubgroup, parameters.archType, parameters.hostedOs, parameters.buildConfig, parameters.nameSuffix) }}
+            targetPath: $(Build.SourcesDirectory)/artifacts/staging
 
     artifacts:
       publish:
@@ -90,6 +107,12 @@ jobs:
       - name: _archParameter
         value: -arch ${{ parameters.archType }}
 
+      - name: _AssetManifestName
+        value: ${{ parameters.osGroup }}${{ parameters.osSubgroup }}_${{ parameters.archType }}_${{ parameters.nameSuffix }}
+
+      - name: _SignType
+        value: $[ coalesce(variables.OfficialSignType, 'real') ]
+
       - ${{ if and(eq(parameters.osGroup, 'linux'), eq(parameters.osSubGroup, '_bionic')) }}:
         - name: _osParameter
           value: -os linux-bionic
@@ -117,22 +140,16 @@ jobs:
       - ${{ if ne(parameters.cxxAbiLibrary, '') }}:
         - name: CxxAbiLibraryArg
           value: /p:TargetCxxAbiLibrary=${{ parameters.cxxAbiLibrary }}
-      
+
       - name: TargetCxxLibraryConfigurationArgs
         value: $(CxxStandardLibraryArg) $(CxxStandardLibraryStaticArg) $(CxxAbiLibraryArg)
 
       - name: _officialBuildParameter
         ${{ if eq(parameters.isOfficialBuild, true) }}:
-          value: /p:OfficialBuildId=$(Build.BuildNumber)
+          value: /p:OfficialBuildId=$(Build.BuildNumber) /p:DotNetPublishUsingPipelines=true /p:SignType=$(_SignType) /p:DotNetSignType=$(_SignType)
         ${{ if ne(parameters.isOfficialBuild, true) }}:
           value: ''
 
-      - name: _buildDarwinFrameworksParameter
-        ${{ if in(parameters.osGroup, 'ios', 'tvos', 'maccatalyst')}}:
-          value: /p:BuildDarwinFrameworks=true
-        ${{ if notin(parameters.osGroup, 'ios', 'tvos', 'maccatalyst')}}:
-          value: ''
-
       # Set no native sanitizers by default
       - name: _nativeSanitizersArg
         value: ''
diff --git a/eng/pipelines/common/templates/global-build-step.yml b/eng/pipelines/common/templates/global-build-step.yml
index 2a2262d9a4ead4..983e27c7422c8e 100644
--- a/eng/pipelines/common/templates/global-build-step.yml
+++ b/eng/pipelines/common/templates/global-build-step.yml
@@ -10,7 +10,7 @@ parameters:
   condition: succeeded()
 
 steps:
-  - script: $(Build.SourcesDirectory)$(dir)build$(scriptExt) -ci ${{ parameters.archParameter }} $(_osParameter) ${{ parameters.crossArg }} ${{ parameters.buildArgs }} ${{ parameters.targetCxxLibraryConfigurationArgs }} $(_officialBuildParameter) $(_buildDarwinFrameworksParameter) $(_overrideTestScriptWindowsCmdParameter)
+  - script: $(Build.SourcesDirectory)$(dir)build$(scriptExt) -ci ${{ parameters.archParameter }} $(_osParameter) ${{ parameters.crossArg }} ${{ parameters.buildArgs }} ${{ parameters.targetCxxLibraryConfigurationArgs }} $(_officialBuildParameter) $(_overrideTestScriptWindowsCmdParameter)
     displayName: ${{ parameters.displayName }}
     ${{ if eq(parameters.useContinueOnErrorDuringBuild, true) }}:
       continueOnError: ${{ parameters.shouldContinueOnError }}
diff --git a/eng/pipelines/common/templates/runtimes/build-runtime-tests-and-send-to-helix.yml b/eng/pipelines/common/templates/runtimes/build-runtime-tests-and-send-to-helix.yml
index 26b86483cd8e28..f88692190cc221 100644
--- a/eng/pipelines/common/templates/runtimes/build-runtime-tests-and-send-to-helix.yml
+++ b/eng/pipelines/common/templates/runtimes/build-runtime-tests-and-send-to-helix.yml
@@ -27,7 +27,6 @@ parameters:
   displayName: ''
   timeoutInMinutes: ''
   enableMicrobuild: ''
-  gatherAssetManifests: false
   shouldContinueOnError: false
 
 steps:
diff --git a/eng/pipelines/common/templates/runtimes/build-runtime-tests.yml b/eng/pipelines/common/templates/runtimes/build-runtime-tests.yml
index a822ccf28fd288..1b1660693d6e03 100644
--- a/eng/pipelines/common/templates/runtimes/build-runtime-tests.yml
+++ b/eng/pipelines/common/templates/runtimes/build-runtime-tests.yml
@@ -11,7 +11,6 @@ parameters:
   displayName: ''
   timeoutInMinutes: ''
   enableMicrobuild: ''
-  gatherAssetManifests: false
   shouldContinueOnError: false
 
 
diff --git a/eng/pipelines/common/templates/runtimes/run-test-job.yml b/eng/pipelines/common/templates/runtimes/run-test-job.yml
index 551a502ae9ed60..4171b5bcc33e57 100644
--- a/eng/pipelines/common/templates/runtimes/run-test-job.yml
+++ b/eng/pipelines/common/templates/runtimes/run-test-job.yml
@@ -386,6 +386,9 @@ jobs:
           - jitstress_isas_x86_nosse41
           - jitstress_isas_x86_nosse42
           - jitstress_isas_x86_nossse3
+          - jitstress_isas_x86_vectort128
+          - jitstress_isas_x86_vectort512
+          - jitstress_isas_x86_noavx512_vectort128
           - jitstress_isas_1_x86_noaes
           - jitstress_isas_1_x86_noavx
           - jitstress_isas_1_x86_noavx2
@@ -540,7 +543,6 @@ jobs:
             - jitpartialcompilation_pgo
             - jitpartialcompilation_pgo_stress_random
             - jitoptrepeat
-            - jitoldlayout
           ${{ else }}:
             scenarios:
             - jitosr_stress
@@ -554,7 +556,6 @@ jobs:
             - jitphysicalpromotion_full
             - jitrlcse
             - jitoptrepeat
-            - jitoldlayout
         ${{ if in(parameters.testGroup, 'jit-cfg') }}:
           scenarios:
           - jitcfg
diff --git a/eng/pipelines/common/templates/runtimes/send-to-helix-step.yml b/eng/pipelines/common/templates/runtimes/send-to-helix-step.yml
index 989ccdc9319a92..918ba5b814aaa3 100644
--- a/eng/pipelines/common/templates/runtimes/send-to-helix-step.yml
+++ b/eng/pipelines/common/templates/runtimes/send-to-helix-step.yml
@@ -30,6 +30,7 @@ parameters:
   runtimeVariant: ''
   shouldContinueOnError: false
   SuperPmiCollect: ''
+  SuperPmiReplayType: ''
   SuperPmiDiffType: ''
   SuperPmiBaseJitOptions: ''
   SuperPmiDiffJitOptions: ''
@@ -65,6 +66,7 @@ steps:
       RuntimeFlavor: ${{ parameters.runtimeFlavor }}
       _RuntimeVariant: ${{ parameters.runtimeVariant }}
       _SuperPmiCollect: ${{ parameters.SuperPmiCollect }}
+      _SuperPmiReplayType: ${{ parameters.SuperPmiReplayType }}
       _SuperPmiDiffType: ${{ parameters.SuperPmiDiffType }}
       _SuperPmiBaseJitOptions: ${{ parameters.SuperPmiBaseJitOptions }}
       _SuperPmiDiffJitOptions: ${{ parameters.SuperPmiDiffJitOptions }}
diff --git a/eng/pipelines/common/templates/runtimes/xplat-job.yml b/eng/pipelines/common/templates/runtimes/xplat-job.yml
index e22f8f968c4790..3b7dfa334ed259 100644
--- a/eng/pipelines/common/templates/runtimes/xplat-job.yml
+++ b/eng/pipelines/common/templates/runtimes/xplat-job.yml
@@ -18,7 +18,6 @@ parameters:
   displayName: ''
   timeoutInMinutes: ''
   enableMicrobuild: ''
-  gatherAssetManifests: false
   disableComponentGovernance: ''
   templatePath: 'templates'
 
@@ -69,11 +68,6 @@ jobs:
     ${{ else }}:
       disableComponentGovernance: ${{ parameters.disableComponentGovernance }}
 
-    # Setting this results in the arcade job template including a step
-    # that gathers asset manifests and publishes them to pipeline
-    # storage. Only relevant for build jobs.
-    enablePublishBuildAssets: ${{ parameters.gatherAssetManifests }}
-
     artifacts:
       publish:
         ${{ if ne(parameters.logsName, '') }}:
diff --git a/eng/pipelines/common/templates/wasm-library-tests.yml b/eng/pipelines/common/templates/wasm-library-tests.yml
index 51cf40074ff5bd..149215ea501937 100644
--- a/eng/pipelines/common/templates/wasm-library-tests.yml
+++ b/eng/pipelines/common/templates/wasm-library-tests.yml
@@ -80,7 +80,7 @@ jobs:
       isExtraPlatforms: ${{ parameters.isExtraPlatformsBuild }}
       testGroup: innerloop
       nameSuffix: LibraryTests${{ parameters.nameSuffix }}
-      buildArgs: -s mono+libs+host+packs+libs.tests -c $(_BuildConfig) /p:ArchiveTests=true /p:MonoEnableAssertMessages=true /p:BrowserHost=$(_hostedOs) $(_wasmRunSmokeTestsOnlyArg) $(chromeInstallArg) $(firefoxInstallArg) $(v8InstallArg) ${{ parameters.extraBuildArgs }}
+      buildArgs: -s mono+libs+host+packs+libs.tests -c $(_BuildConfig) /p:ArchiveTests=true /p:MonoEnableAssertMessages=true /p:BrowserHost=$(_hostedOs) $(_wasmRunSmokeTestsOnlyArg) $(chromeInstallArg) $(firefoxInstallArg) $(v8InstallArg) /maxcpucount:1 ${{ parameters.extraBuildArgs }}
       timeoutInMinutes: 240
       # if !alwaysRun, then:
       #   if this is runtime-wasm (isWasmOnlyBuild):
diff --git a/eng/pipelines/coreclr/libraries-pgo.yml b/eng/pipelines/coreclr/libraries-pgo.yml
index 00a050da0e6210..a8f0e16b01f35c 100644
--- a/eng/pipelines/coreclr/libraries-pgo.yml
+++ b/eng/pipelines/coreclr/libraries-pgo.yml
@@ -71,4 +71,3 @@ extends:
                     - syntheticpgo
                     - syntheticpgo_blend
                     - jitrlcse
-                    - jitoldlayout
diff --git a/eng/pipelines/coreclr/superpmi-collect-test.yml b/eng/pipelines/coreclr/superpmi-collect-test.yml
index 50728c776475e4..abc3fa9cdf089a 100644
--- a/eng/pipelines/coreclr/superpmi-collect-test.yml
+++ b/eng/pipelines/coreclr/superpmi-collect-test.yml
@@ -1,4 +1,4 @@
-# This job definition automates the SuperPMI collection process.
+# This job definition automates the SuperPMI collection process for the "test" pipeline.
 
 trigger: none
 
@@ -6,368 +6,4 @@ variables:
   - template: /eng/pipelines/common/variables.yml
 
 extends:
-  template:  /eng/pipelines/common/templates/pipeline-with-resources.yml
-  parameters:
-    stages:
-    - stage: Build
-      jobs:
-
-      - template: /eng/pipelines/common/platform-matrix.yml
-        parameters:
-          jobTemplate: /eng/pipelines/common/global-build-job.yml
-          buildConfig: checked
-          platforms:
-          - windows_x64
-          - linux_x64
-          jobParameters:
-            testGroup: outerloop
-            buildArgs: -s clr+libs+libs.tests -rc $(_BuildConfig) -c Release /p:ArchiveTests=true
-            timeoutInMinutes: 120
-            postBuildSteps:
-              - template: /eng/pipelines/coreclr/templates/build-native-test-assets-step.yml
-              - template: /eng/pipelines/common/upload-artifact-step.yml
-                parameters:
-                  rootFolder: $(Build.SourcesDirectory)/artifacts/bin
-                  includeRootFolder: false
-                  archiveType: $(archiveType)
-                  archiveExtension: $(archiveExtension)
-                  tarCompression: $(tarCompression)
-                  artifactName: BuildArtifacts_$(osGroup)$(osSubgroup)_$(archType)_$(_BuildConfig)
-              - template: /eng/pipelines/common/upload-artifact-step.yml
-                parameters:
-                  rootFolder: $(Build.SourcesDirectory)/artifacts/helix
-                  includeRootFolder: false
-                  archiveType: $(archiveType)
-                  archiveExtension: $(archiveExtension)
-                  tarCompression: $(tarCompression)
-                  artifactName: LibrariesTestArtifacts_$(osGroup)$(osSubgroup)_$(archType)_$(_BuildConfig)
-            extraVariablesTemplates:
-              - template: /eng/pipelines/common/templates/runtimes/native-test-assets-variables.yml
-                parameters:
-                  testGroup: outerloop
-            disableComponentGovernance: true # No shipping artifacts produced by this pipeline
-
-      - template: /eng/pipelines/common/platform-matrix.yml
-        parameters:
-          jobTemplate: /eng/pipelines/common/global-build-job.yml
-          buildConfig: checked
-          platforms:
-          - windows_x86
-          - windows_arm64
-          - osx_arm64
-          jobParameters:
-            testGroup: outerloop
-            buildArgs: -s clr+libs+libs.tests -rc $(_BuildConfig) -c Release /p:ArchiveTests=true
-            timeoutInMinutes: 120
-            postBuildSteps:
-              # Build CLR assets for x64 as well as the target as we need an x64 mcs
-              - template: /eng/pipelines/common/templates/global-build-step.yml
-                parameters:
-                  buildArgs: -s clr.spmi -c $(_BuildConfig)
-                  archParameter: -arch x64
-                  displayName: Build SuperPMI
-              - template: /eng/pipelines/coreclr/templates/build-native-test-assets-step.yml
-              - template: /eng/pipelines/common/upload-artifact-step.yml
-                parameters:
-                  rootFolder: $(Build.SourcesDirectory)/artifacts/bin
-                  includeRootFolder: false
-                  archiveType: $(archiveType)
-                  archiveExtension: $(archiveExtension)
-                  tarCompression: $(tarCompression)
-                  artifactName: BuildArtifacts_$(osGroup)$(osSubgroup)_$(archType)_$(_BuildConfig)
-              - template: /eng/pipelines/common/upload-artifact-step.yml
-                parameters:
-                  rootFolder: $(Build.SourcesDirectory)/artifacts/helix
-                  includeRootFolder: false
-                  archiveType: $(archiveType)
-                  archiveExtension: $(archiveExtension)
-                  tarCompression: $(tarCompression)
-                  artifactName: LibrariesTestArtifacts_$(osGroup)$(osSubgroup)_$(archType)_$(_BuildConfig)
-            extraVariablesTemplates:
-              - template: /eng/pipelines/common/templates/runtimes/native-test-assets-variables.yml
-                parameters:
-                  testGroup: outerloop
-            disableComponentGovernance: true # No shipping artifacts produced by this pipeline
-
-      - template: /eng/pipelines/common/platform-matrix.yml
-        parameters:
-          jobTemplate: /eng/pipelines/common/global-build-job.yml
-          buildConfig: checked
-          platforms:
-          - linux_arm
-          - linux_arm64
-          jobParameters:
-            testGroup: outerloop
-            buildArgs: -s clr+libs+libs.tests -rc $(_BuildConfig) -c Release /p:ArchiveTests=true
-            timeoutInMinutes: 120
-            postBuildSteps:
-              # Build CLR assets for x64 as well as the target as we need an x64 mcs
-              - template: /eng/pipelines/common/templates/global-build-step.yml
-                parameters:
-                  buildArgs: -s clr.spmi -c $(_BuildConfig)
-                  archParameter: -arch x64
-                  container: linux_x64
-                  displayName: Build SuperPMI
-              - template: /eng/pipelines/coreclr/templates/build-native-test-assets-step.yml
-              - template: /eng/pipelines/common/upload-artifact-step.yml
-                parameters:
-                  rootFolder: $(Build.SourcesDirectory)/artifacts/bin
-                  includeRootFolder: false
-                  archiveType: $(archiveType)
-                  archiveExtension: $(archiveExtension)
-                  tarCompression: $(tarCompression)
-                  artifactName: BuildArtifacts_$(osGroup)$(osSubgroup)_$(archType)_$(_BuildConfig)
-              - template: /eng/pipelines/common/upload-artifact-step.yml
-                parameters:
-                  rootFolder: $(Build.SourcesDirectory)/artifacts/helix
-                  includeRootFolder: false
-                  archiveType: $(archiveType)
-                  archiveExtension: $(archiveExtension)
-                  tarCompression: $(tarCompression)
-                  artifactName: LibrariesTestArtifacts_$(osGroup)$(osSubgroup)_$(archType)_$(_BuildConfig)
-            extraVariablesTemplates:
-              - template: /eng/pipelines/common/templates/runtimes/native-test-assets-variables.yml
-                parameters:
-                  testGroup: outerloop
-            disableComponentGovernance: true # No shipping artifacts produced by this pipeline
-
-      - template: /eng/pipelines/common/platform-matrix.yml
-        parameters:
-          jobTemplate: /eng/pipelines/common/templates/runtimes/build-test-job.yml
-          buildConfig: checked
-          platforms:
-          - CoreClrTestBuildHost # Either osx_x64 or linux_x64
-          jobParameters:
-            testGroup: outerloop
-
-      - template: /eng/pipelines/common/platform-matrix.yml
-        parameters:
-          jobTemplate: /eng/pipelines/coreclr/templates/superpmi-collect-job.yml
-          buildConfig: checked
-          platforms:
-          - osx_arm64
-          - linux_arm
-          - linux_arm64
-          - linux_x64
-          - windows_x64
-          - windows_x86
-          - windows_arm64
-          helixQueueGroup: ci
-          helixQueuesTemplate: /eng/pipelines/coreclr/templates/helix-queues-setup.yml
-          jobParameters:
-            testGroup: outerloop
-            liveLibrariesBuildConfig: Release
-            collectionType: pmi
-            collectionName: libraries
-            collectionUpload: false
-
-      - template: /eng/pipelines/common/platform-matrix.yml
-        parameters:
-          jobTemplate: /eng/pipelines/coreclr/templates/superpmi-collect-job.yml
-          buildConfig: checked
-          platforms:
-          - osx_arm64
-          - linux_arm
-          - linux_arm64
-          - linux_x64
-          - windows_x64
-          - windows_x86
-          - windows_arm64
-          helixQueueGroup: ci
-          helixQueuesTemplate: /eng/pipelines/coreclr/templates/helix-queues-setup.yml
-          jobParameters:
-            testGroup: outerloop
-            liveLibrariesBuildConfig: Release
-            collectionType: crossgen2
-            collectionName: libraries
-            collectionUpload: false
-
-      - template: /eng/pipelines/common/platform-matrix.yml
-        parameters:
-          jobTemplate: /eng/pipelines/coreclr/templates/superpmi-collect-job.yml
-          buildConfig: checked
-          platforms:
-          - osx_arm64
-          - linux_arm
-          - linux_arm64
-          - linux_x64
-          - windows_x64
-          - windows_x86
-          - windows_arm64
-          helixQueueGroup: ci
-          helixQueuesTemplate: /eng/pipelines/coreclr/templates/helix-queues-setup.yml
-          jobParameters:
-            testGroup: outerloop
-            liveLibrariesBuildConfig: Release
-            collectionType: run
-            collectionName: realworld
-            collectionUpload: false
-
-      - template: /eng/pipelines/common/platform-matrix.yml
-        parameters:
-          jobTemplate: /eng/pipelines/coreclr/templates/superpmi-collect-job.yml
-          buildConfig: checked
-          platforms:
-          - osx_arm64
-          - linux_arm
-          - linux_arm64
-          - linux_x64
-          - windows_x64
-          - windows_x86
-          - windows_arm64
-          helixQueueGroup: ci
-          helixQueuesTemplate: /eng/pipelines/coreclr/templates/helix-queues-setup.yml
-          jobParameters:
-            testGroup: outerloop
-            liveLibrariesBuildConfig: Release
-            collectionType: run
-            collectionName: benchmarks
-            collectionUpload: false
-
-      - template: /eng/pipelines/common/platform-matrix.yml
-        parameters:
-          jobTemplate: /eng/pipelines/coreclr/templates/superpmi-collect-job.yml
-          buildConfig: checked
-          platforms:
-          - osx_arm64
-          - linux_arm
-          - linux_arm64
-          - linux_x64
-          - windows_x64
-          - windows_x86
-          - windows_arm64
-          helixQueueGroup: ci
-          helixQueuesTemplate: /eng/pipelines/coreclr/templates/helix-queues-setup.yml
-          jobParameters:
-            testGroup: outerloop
-            liveLibrariesBuildConfig: Release
-            collectionType: run_pgo
-            collectionName: benchmarks
-            collectionUpload: false
-
-      - template: /eng/pipelines/common/platform-matrix.yml
-        parameters:
-          jobTemplate: /eng/pipelines/coreclr/templates/superpmi-collect-job.yml
-          buildConfig: checked
-          platforms:
-          - osx_arm64
-          - linux_arm
-          - linux_arm64
-          - linux_x64
-          - windows_x64
-          - windows_x86
-          - windows_arm64
-          helixQueueGroup: ci
-          helixQueuesTemplate: /eng/pipelines/coreclr/templates/helix-queues-setup.yml
-          jobParameters:
-            testGroup: outerloop
-            liveLibrariesBuildConfig: Release
-            collectionType: run_pgo_optrepeat
-            collectionName: benchmarks
-            collectionUpload: false
-
-      #
-      # Collection of coreclr test run
-      #
-      - template: /eng/pipelines/common/platform-matrix.yml
-        parameters:
-          jobTemplate: /eng/pipelines/common/templates/runtimes/run-test-job.yml
-          buildConfig: checked
-          platforms:
-          - osx_arm64
-          - linux_arm
-          - linux_arm64
-          - linux_x64
-          - windows_x64
-          - windows_x86
-          - windows_arm64
-          helixQueueGroup: superpmi
-          helixQueuesTemplate: /eng/pipelines/coreclr/templates/helix-queues-setup.yml
-          jobParameters:
-            testGroup: outerloop
-            liveLibrariesBuildConfig: Release
-            SuperPmiCollect: true
-            SuperPmiCollectionUpload: false
-            unifiedArtifactsName: BuildArtifacts_$(osGroup)$(osSubgroup)_$(archType)_$(_BuildConfig)
-
-      - template: /eng/pipelines/common/platform-matrix.yml
-        parameters:
-          jobTemplate: /eng/pipelines/coreclr/templates/superpmi-collect-job.yml
-          buildConfig: checked
-          platforms:
-          - linux_arm64
-          - linux_x64
-          - windows_x64
-          - windows_arm64
-          helixQueueGroup: ci
-          helixQueuesTemplate: /eng/pipelines/coreclr/templates/helix-queues-setup.yml
-          jobParameters:
-            testGroup: outerloop
-            liveLibrariesBuildConfig: Release
-            collectionType: nativeaot
-            collectionName: smoke_tests
-            collectionUpload: false
-
-      #
-      # Collection of libraries test run: normal
-      # Libraries Test Run using Release libraries, and Checked CoreCLR
-      #
-      - template: /eng/pipelines/common/platform-matrix.yml
-        parameters:
-          jobTemplate: /eng/pipelines/libraries/run-test-job.yml
-          buildConfig: Release
-          platforms:
-          - osx_arm64
-          - linux_arm
-          - linux_arm64
-          - linux_x64
-          - windows_x64
-          - windows_x86
-          - windows_arm64
-          helixQueueGroup: superpmi
-          helixQueuesTemplate: /eng/pipelines/coreclr/templates/helix-queues-setup.yml
-          jobParameters:
-            testScope: innerloop
-            liveRuntimeBuildConfig: Checked
-            dependsOnTestBuildConfiguration: Release
-            dependsOnTestArchitecture: x64
-            scenarios:
-            - normal
-            SuperPmiCollect: true
-            SuperPmiCollectionName: libraries_tests
-            SuperPmiCollectionUpload: false
-            unifiedArtifactsName: BuildArtifacts_$(osGroup)$(osSubgroup)_$(archType)_Checked
-            helixArtifactsName: LibrariesTestArtifacts_$(osGroup)$(osSubgroup)_$(archType)_Checked
-            unifiedBuildConfigOverride: checked
-
-      #
-      # Collection of libraries test run: no_tiered_compilation
-      # Libraries Test Run using Release libraries, and Checked CoreCLR
-      #
-      - template: /eng/pipelines/common/platform-matrix.yml
-        parameters:
-          jobTemplate: /eng/pipelines/libraries/run-test-job.yml
-          buildConfig: Release
-          platforms:
-          - osx_arm64
-          - linux_arm
-          - linux_arm64
-          - linux_x64
-          - windows_x64
-          - windows_x86
-          - windows_arm64
-          helixQueueGroup: superpmi
-          helixQueuesTemplate: /eng/pipelines/coreclr/templates/helix-queues-setup.yml
-          jobParameters:
-            testScope: innerloop
-            liveRuntimeBuildConfig: Checked
-            dependsOnTestBuildConfiguration: Release
-            dependsOnTestArchitecture: x64
-            scenarios:
-            - no_tiered_compilation
-            SuperPmiCollect: true
-            SuperPmiCollectionName: libraries_tests_no_tiered_compilation
-            SuperPmiCollectionUpload: false
-            unifiedArtifactsName: BuildArtifacts_$(osGroup)$(osSubgroup)_$(archType)_Checked
-            helixArtifactsName: LibrariesTestArtifacts_$(osGroup)$(osSubgroup)_$(archType)_Checked
-            unifiedBuildConfigOverride: checked
+  template: /eng/pipelines/coreclr/templates/superpmi-collect-pipeline.yml
\ No newline at end of file
diff --git a/eng/pipelines/coreclr/superpmi-collect.yml b/eng/pipelines/coreclr/superpmi-collect.yml
index 70910782ae1250..5fa3c7692fac32 100644
--- a/eng/pipelines/coreclr/superpmi-collect.yml
+++ b/eng/pipelines/coreclr/superpmi-collect.yml
@@ -1,4 +1,4 @@
-# This job definition automates the SuperPMI collection process.
+# This job definition automates the SuperPMI collection process for the "production" pipeline.
 
 # Trigger this job if the JIT-EE GUID changes, which invalidates previous SuperPMI
 # collections.
@@ -15,9 +15,6 @@ trigger:
 # and should not be triggerable from a PR.
 pr: none
 
-variables:
-  - template: /eng/pipelines/common/variables.yml
-
 schedules:
 - cron: "0 17 * * 0"
   displayName: Sun at 9:00 AM (UTC-8:00)
@@ -26,359 +23,8 @@ schedules:
     - main
   always: true
 
-extends:
-  template:  /eng/pipelines/common/templates/pipeline-with-resources.yml
-  parameters:
-    stages:
-    - stage: Build
-      jobs:
-
-      - template: /eng/pipelines/common/platform-matrix.yml
-        parameters:
-          jobTemplate: /eng/pipelines/common/global-build-job.yml
-          buildConfig: checked
-          platforms:
-          - windows_x64
-          - linux_x64
-          jobParameters:
-            testGroup: outerloop
-            buildArgs: -s clr+libs+libs.tests -rc $(_BuildConfig) -c Release /p:ArchiveTests=true
-            timeoutInMinutes: 120
-            postBuildSteps:
-              - template: /eng/pipelines/coreclr/templates/build-native-test-assets-step.yml
-              - template: /eng/pipelines/common/upload-artifact-step.yml
-                parameters:
-                  rootFolder: $(Build.SourcesDirectory)/artifacts/bin
-                  includeRootFolder: false
-                  archiveType: $(archiveType)
-                  archiveExtension: $(archiveExtension)
-                  tarCompression: $(tarCompression)
-                  artifactName: BuildArtifacts_$(osGroup)$(osSubgroup)_$(archType)_$(_BuildConfig)
-              - template: /eng/pipelines/common/upload-artifact-step.yml
-                parameters:
-                  rootFolder: $(Build.SourcesDirectory)/artifacts/helix
-                  includeRootFolder: false
-                  archiveType: $(archiveType)
-                  archiveExtension: $(archiveExtension)
-                  tarCompression: $(tarCompression)
-                  artifactName: LibrariesTestArtifacts_$(osGroup)$(osSubgroup)_$(archType)_$(_BuildConfig)
-            extraVariablesTemplates:
-              - template: /eng/pipelines/common/templates/runtimes/native-test-assets-variables.yml
-                parameters:
-                  testGroup: outerloop
-            disableComponentGovernance: true # No shipping artifacts produced by this pipeline
-
-      - template: /eng/pipelines/common/platform-matrix.yml
-        parameters:
-          jobTemplate: /eng/pipelines/common/global-build-job.yml
-          buildConfig: checked
-          platforms:
-          - windows_x86
-          - windows_arm64
-          - osx_arm64
-          jobParameters:
-            testGroup: outerloop
-            buildArgs: -s clr+libs+libs.tests -rc $(_BuildConfig) -c Release /p:ArchiveTests=true
-            timeoutInMinutes: 120
-            postBuildSteps:
-              # Build CLR assets for x64 as well as the target as we need an x64 mcs
-              - template: /eng/pipelines/common/templates/global-build-step.yml
-                parameters:
-                  buildArgs: -s clr.spmi -c $(_BuildConfig)
-                  archParameter: -arch x64
-                  displayName: Build SuperPMI
-              - template: /eng/pipelines/coreclr/templates/build-native-test-assets-step.yml
-              - template: /eng/pipelines/common/upload-artifact-step.yml
-                parameters:
-                  rootFolder: $(Build.SourcesDirectory)/artifacts/bin
-                  includeRootFolder: false
-                  archiveType: $(archiveType)
-                  archiveExtension: $(archiveExtension)
-                  tarCompression: $(tarCompression)
-                  artifactName: BuildArtifacts_$(osGroup)$(osSubgroup)_$(archType)_$(_BuildConfig)
-              - template: /eng/pipelines/common/upload-artifact-step.yml
-                parameters:
-                  rootFolder: $(Build.SourcesDirectory)/artifacts/helix
-                  includeRootFolder: false
-                  archiveType: $(archiveType)
-                  archiveExtension: $(archiveExtension)
-                  tarCompression: $(tarCompression)
-                  artifactName: LibrariesTestArtifacts_$(osGroup)$(osSubgroup)_$(archType)_$(_BuildConfig)
-            extraVariablesTemplates:
-              - template: /eng/pipelines/common/templates/runtimes/native-test-assets-variables.yml
-                parameters:
-                  testGroup: outerloop
-            disableComponentGovernance: true # No shipping artifacts produced by this pipeline
-
-      - template: /eng/pipelines/common/platform-matrix.yml
-        parameters:
-          jobTemplate: /eng/pipelines/common/global-build-job.yml
-          buildConfig: checked
-          platforms:
-          - linux_arm
-          - linux_arm64
-          jobParameters:
-            testGroup: outerloop
-            buildArgs: -s clr+libs+libs.tests -rc $(_BuildConfig) -c Release /p:ArchiveTests=true
-            timeoutInMinutes: 120
-            postBuildSteps:
-              # Build CLR assets for x64 as well as the target as we need an x64 mcs
-              - template: /eng/pipelines/common/templates/global-build-step.yml
-                parameters:
-                  buildArgs: -s clr.spmi -c $(_BuildConfig)
-                  archParameter: -arch x64
-                  container: linux_x64
-                  displayName: Build SuperPMI
-              - template: /eng/pipelines/coreclr/templates/build-native-test-assets-step.yml
-              - template: /eng/pipelines/common/upload-artifact-step.yml
-                parameters:
-                  rootFolder: $(Build.SourcesDirectory)/artifacts/bin
-                  includeRootFolder: false
-                  archiveType: $(archiveType)
-                  archiveExtension: $(archiveExtension)
-                  tarCompression: $(tarCompression)
-                  artifactName: BuildArtifacts_$(osGroup)$(osSubgroup)_$(archType)_$(_BuildConfig)
-              - template: /eng/pipelines/common/upload-artifact-step.yml
-                parameters:
-                  rootFolder: $(Build.SourcesDirectory)/artifacts/helix
-                  includeRootFolder: false
-                  archiveType: $(archiveType)
-                  archiveExtension: $(archiveExtension)
-                  tarCompression: $(tarCompression)
-                  artifactName: LibrariesTestArtifacts_$(osGroup)$(osSubgroup)_$(archType)_$(_BuildConfig)
-            extraVariablesTemplates:
-              - template: /eng/pipelines/common/templates/runtimes/native-test-assets-variables.yml
-                parameters:
-                  testGroup: outerloop
-            disableComponentGovernance: true # No shipping artifacts produced by this pipeline
-
-      - template: /eng/pipelines/common/platform-matrix.yml
-        parameters:
-          jobTemplate: /eng/pipelines/common/templates/runtimes/build-test-job.yml
-          buildConfig: checked
-          platforms:
-          - CoreClrTestBuildHost # Either osx_x64 or linux_x64
-          jobParameters:
-            testGroup: outerloop
-
-      - template: /eng/pipelines/common/platform-matrix.yml
-        parameters:
-          jobTemplate: /eng/pipelines/coreclr/templates/superpmi-collect-job.yml
-          buildConfig: checked
-          platforms:
-          - osx_arm64
-          - linux_arm
-          - linux_arm64
-          - linux_x64
-          - windows_x64
-          - windows_x86
-          - windows_arm64
-          helixQueueGroup: ci
-          helixQueuesTemplate: /eng/pipelines/coreclr/templates/helix-queues-setup.yml
-          jobParameters:
-            testGroup: outerloop
-            liveLibrariesBuildConfig: Release
-            collectionType: pmi
-            collectionName: libraries
-
-      - template: /eng/pipelines/common/platform-matrix.yml
-        parameters:
-          jobTemplate: /eng/pipelines/coreclr/templates/superpmi-collect-job.yml
-          buildConfig: checked
-          platforms:
-          - osx_arm64
-          - linux_arm
-          - linux_arm64
-          - linux_x64
-          - windows_x64
-          - windows_x86
-          - windows_arm64
-          helixQueueGroup: ci
-          helixQueuesTemplate: /eng/pipelines/coreclr/templates/helix-queues-setup.yml
-          jobParameters:
-            testGroup: outerloop
-            liveLibrariesBuildConfig: Release
-            collectionType: crossgen2
-            collectionName: libraries
-
-      - template: /eng/pipelines/common/platform-matrix.yml
-        parameters:
-          jobTemplate: /eng/pipelines/coreclr/templates/superpmi-collect-job.yml
-          buildConfig: checked
-          platforms:
-          - osx_arm64
-          - linux_arm
-          - linux_arm64
-          - linux_x64
-          - windows_x64
-          - windows_x86
-          - windows_arm64
-          helixQueueGroup: ci
-          helixQueuesTemplate: /eng/pipelines/coreclr/templates/helix-queues-setup.yml
-          jobParameters:
-            testGroup: outerloop
-            liveLibrariesBuildConfig: Release
-            collectionType: run
-            collectionName: realworld
-
-      - template: /eng/pipelines/common/platform-matrix.yml
-        parameters:
-          jobTemplate: /eng/pipelines/coreclr/templates/superpmi-collect-job.yml
-          buildConfig: checked
-          platforms:
-          - osx_arm64
-          - linux_arm
-          - linux_arm64
-          - linux_x64
-          - windows_x64
-          - windows_x86
-          - windows_arm64
-          helixQueueGroup: ci
-          helixQueuesTemplate: /eng/pipelines/coreclr/templates/helix-queues-setup.yml
-          jobParameters:
-            testGroup: outerloop
-            liveLibrariesBuildConfig: Release
-            collectionType: run
-            collectionName: benchmarks
-
-      - template: /eng/pipelines/common/platform-matrix.yml
-        parameters:
-          jobTemplate: /eng/pipelines/coreclr/templates/superpmi-collect-job.yml
-          buildConfig: checked
-          platforms:
-          - osx_arm64
-          - linux_arm
-          - linux_arm64
-          - linux_x64
-          - windows_x64
-          - windows_x86
-          - windows_arm64
-          helixQueueGroup: ci
-          helixQueuesTemplate: /eng/pipelines/coreclr/templates/helix-queues-setup.yml
-          jobParameters:
-            testGroup: outerloop
-            liveLibrariesBuildConfig: Release
-            collectionType: run_pgo
-            collectionName: benchmarks
-
-      - template: /eng/pipelines/common/platform-matrix.yml
-        parameters:
-          jobTemplate: /eng/pipelines/coreclr/templates/superpmi-collect-job.yml
-          buildConfig: checked
-          platforms:
-          - osx_arm64
-          - linux_arm
-          - linux_arm64
-          - linux_x64
-          - windows_x64
-          - windows_x86
-          - windows_arm64
-          helixQueueGroup: ci
-          helixQueuesTemplate: /eng/pipelines/coreclr/templates/helix-queues-setup.yml
-          jobParameters:
-            testGroup: outerloop
-            liveLibrariesBuildConfig: Release
-            collectionType: run_pgo_optrepeat
-            collectionName: benchmarks
-
-      #
-      # Collection of coreclr test run
-      #
-      - template: /eng/pipelines/common/platform-matrix.yml
-        parameters:
-          jobTemplate: /eng/pipelines/common/templates/runtimes/run-test-job.yml
-          buildConfig: checked
-          platforms:
-          - osx_arm64
-          - linux_arm
-          - linux_arm64
-          - linux_x64
-          - windows_x64
-          - windows_x86
-          - windows_arm64
-          helixQueueGroup: superpmi
-          helixQueuesTemplate: /eng/pipelines/coreclr/templates/helix-queues-setup.yml
-          jobParameters:
-            testGroup: outerloop
-            liveLibrariesBuildConfig: Release
-            SuperPmiCollect: true
-            unifiedArtifactsName: BuildArtifacts_$(osGroup)$(osSubgroup)_$(archType)_$(_BuildConfig)
-
-      - template: /eng/pipelines/common/platform-matrix.yml
-        parameters:
-          jobTemplate: /eng/pipelines/coreclr/templates/superpmi-collect-job.yml
-          buildConfig: checked
-          platforms:
-          - linux_arm64
-          - linux_x64
-          - windows_x64
-          - windows_arm64
-          helixQueueGroup: ci
-          helixQueuesTemplate: /eng/pipelines/coreclr/templates/helix-queues-setup.yml
-          jobParameters:
-            testGroup: outerloop
-            liveLibrariesBuildConfig: Release
-            collectionType: nativeaot
-            collectionName: smoke_tests
-
-      #
-      # Collection of libraries test run: normal
-      # Libraries Test Run using Release libraries, and Checked CoreCLR
-      #
-      - template: /eng/pipelines/common/platform-matrix.yml
-        parameters:
-          jobTemplate: /eng/pipelines/libraries/run-test-job.yml
-          buildConfig: Release
-          platforms:
-          - osx_arm64
-          - linux_arm
-          - linux_arm64
-          - linux_x64
-          - windows_x64
-          - windows_x86
-          - windows_arm64
-          helixQueueGroup: superpmi
-          helixQueuesTemplate: /eng/pipelines/coreclr/templates/helix-queues-setup.yml
-          jobParameters:
-            testScope: innerloop
-            liveRuntimeBuildConfig: Checked
-            dependsOnTestBuildConfiguration: Release
-            dependsOnTestArchitecture: x64
-            scenarios:
-            - normal
-            SuperPmiCollect: true
-            SuperPmiCollectionName: libraries_tests
-            unifiedArtifactsName: BuildArtifacts_$(osGroup)$(osSubgroup)_$(archType)_Checked
-            helixArtifactsName: LibrariesTestArtifacts_$(osGroup)$(osSubgroup)_$(archType)_Checked
-            unifiedBuildConfigOverride: checked
+variables:
+  - template: /eng/pipelines/common/variables.yml
 
-      #
-      # Collection of libraries test run: no_tiered_compilation
-      # Libraries Test Run using Release libraries, and Checked CoreCLR
-      #
-      - template: /eng/pipelines/common/platform-matrix.yml
-        parameters:
-          jobTemplate: /eng/pipelines/libraries/run-test-job.yml
-          buildConfig: Release
-          platforms:
-          - osx_arm64
-          - linux_arm
-          - linux_arm64
-          - linux_x64
-          - windows_x64
-          - windows_x86
-          - windows_arm64
-          helixQueueGroup: superpmi
-          helixQueuesTemplate: /eng/pipelines/coreclr/templates/helix-queues-setup.yml
-          jobParameters:
-            testScope: innerloop
-            liveRuntimeBuildConfig: Checked
-            dependsOnTestBuildConfiguration: Release
-            dependsOnTestArchitecture: x64
-            scenarios:
-            - no_tiered_compilation
-            SuperPmiCollect: true
-            SuperPmiCollectionName: libraries_tests_no_tiered_compilation
-            unifiedArtifactsName: BuildArtifacts_$(osGroup)$(osSubgroup)_$(archType)_Checked
-            helixArtifactsName: LibrariesTestArtifacts_$(osGroup)$(osSubgroup)_$(archType)_Checked
-            unifiedBuildConfigOverride: checked
+extends:
+  template: /eng/pipelines/coreclr/templates/superpmi-collect-pipeline.yml
diff --git a/eng/pipelines/coreclr/superpmi-replay-apx.yml b/eng/pipelines/coreclr/superpmi-replay-apx.yml
new file mode 100644
index 00000000000000..f5e3f0d1adba4e
--- /dev/null
+++ b/eng/pipelines/coreclr/superpmi-replay-apx.yml
@@ -0,0 +1,19 @@
+trigger: none
+
+schedules:
+- cron: "0 7 * * *"
+  displayName: Daily at 11:00 PM (UTC-8:00)
+  branches:
+    include:
+    - main
+  always: true
+
+variables:
+  - template: /eng/pipelines/common/variables.yml
+
+extends:
+  template:  /eng/pipelines/coreclr/templates/jit-replay-pipeline.yml
+  parameters:
+    platforms:
+    - windows_x64
+    replayType: apx
\ No newline at end of file
diff --git a/eng/pipelines/coreclr/superpmi-replay.yml b/eng/pipelines/coreclr/superpmi-replay.yml
index 7ec31af8732eeb..6c9c15e7f19791 100644
--- a/eng/pipelines/coreclr/superpmi-replay.yml
+++ b/eng/pipelines/coreclr/superpmi-replay.yml
@@ -1,74 +1,28 @@
-# This pipeline only runs on GitHub PRs, not on merges.
 trigger: none
 
-# Only run on changes to the JIT directory. Don't run if the JIT-EE GUID has changed,
-# since there won't be any SuperPMI collections with the new GUID until the collection
-# pipeline completes after this PR is merged.
 pr:
   branches:
     include:
     - main
   paths:
     include:
-    - src/coreclr/jit/*
-    - src/coreclr/gcinfo/*
-    - src/coreclr/tools/superpmi/*
-    exclude:
-    - src/coreclr/inc/jiteeversionguid.h
+    - src/coreclr/jit/lsra*.*
+
+schedules:
+- cron: "0 7 * * *"
+  displayName: Daily at 11:00 PM (UTC-8:00)
+  branches:
+    include:
+    - main
+  always: true
 
 variables:
   - template: /eng/pipelines/common/variables.yml
 
 extends:
-  template:  /eng/pipelines/common/templates/pipeline-with-resources.yml
+  template:  /eng/pipelines/coreclr/templates/jit-replay-pipeline.yml
   parameters:
-    stages:
-    # Don't run if the JIT-EE GUID has changed,
-    # since there won't be any SuperPMI collections with the new GUID until the collection
-    # pipeline completes after this PR is merged.
-    - ${{ if eq(variables['Build.Reason'], 'PullRequest') }}:
-      - stage: EvaluatePaths
-        displayName: Evaluate Paths
-        jobs:
-        - template: /eng/pipelines/common/evaluate-paths-job.yml
-          parameters:
-            paths:
-            - subset: jiteeversionguid
-              include:
-              - src/coreclr/inc/jiteeversionguid.h
-
-    - stage: Build
-      jobs:
-
-      - template: /eng/pipelines/common/platform-matrix.yml
-        parameters:
-          jobTemplate: /eng/pipelines/common/global-build-job.yml
-          buildConfig: checked
-          platforms:
-          - windows_x64
-          - windows_x86
-          jobParameters:
-            buildArgs: -s clr.alljits+clr.spmi -c $(_BuildConfig)
-            postBuildSteps:
-              - template: /eng/pipelines/common/upload-artifact-step.yml
-                parameters:
-                  rootFolder: $(Build.SourcesDirectory)/artifacts/bin/coreclr
-                  includeRootFolder: false
-                  archiveType: $(archiveType)
-                  tarCompression: $(tarCompression)
-                  archiveExtension: $(archiveExtension)
-                  artifactName: CheckedJIT_$(osGroup)$(osSubgroup)_$(archType)
-                  displayName: JIT and SuperPMI Assets
-            condition: not(eq(stageDependencies.EvaluatePaths.evaluate_paths.outputs['SetPathVars_jiteeversionguid.containsChange'], true))
-
-      - template: /eng/pipelines/common/platform-matrix.yml
-        parameters:
-          jobTemplate: /eng/pipelines/coreclr/templates/superpmi-replay-job.yml
-          buildConfig: checked
-          platforms:
-          - windows_x64
-          - windows_x86
-          helixQueueGroup: ci
-          helixQueuesTemplate: /eng/pipelines/coreclr/templates/helix-queues-setup.yml
-          jobParameters:
-            condition: not(eq(stageDependencies.EvaluatePaths.evaluate_paths.outputs['SetPathVars_jiteeversionguid.containsChange'], true))
+    platforms:
+    - windows_x64
+    - windows_x86
+    replayType: standard
diff --git a/eng/pipelines/coreclr/templates/helix-queues-setup.yml b/eng/pipelines/coreclr/templates/helix-queues-setup.yml
index 27f26132344789..3e27b328b67fd1 100644
--- a/eng/pipelines/coreclr/templates/helix-queues-setup.yml
+++ b/eng/pipelines/coreclr/templates/helix-queues-setup.yml
@@ -70,16 +70,16 @@ jobs:
     # Linux arm64
     - ${{ if eq(parameters.platform, 'linux_arm64') }}:
       - ${{ if eq(variables['System.TeamProject'], 'public') }}:
-        - (Ubuntu.2204.Arm64.Open)Ubuntu.2204.Armarch.Open@mcr.microsoft.com/dotnet-buildtools/prereqs:ubuntu-22.04-helix-arm64v8
+        - (Ubuntu.2404.Arm64.Open)Ubuntu.2204.Armarch.Open@mcr.microsoft.com/dotnet-buildtools/prereqs:ubuntu-24.04-helix-arm64v8
       - ${{ if eq(variables['System.TeamProject'], 'internal') }}:
-        - (Ubuntu.2204.Arm64)Ubuntu.2204.ArmArch@mcr.microsoft.com/dotnet-buildtools/prereqs:ubuntu-22.04-helix-arm64v8
+        - (Ubuntu.2404.Arm64)Ubuntu.2204.ArmArch@mcr.microsoft.com/dotnet-buildtools/prereqs:ubuntu-24.04-helix-arm64v8
 
     # Linux musl x64
     - ${{ if eq(parameters.platform, 'linux_musl_x64') }}:
       - ${{ if eq(variables['System.TeamProject'], 'public') }}:
-        - (Alpine.321.Amd64.Open)Ubuntu.2204.Amd64.Open@mcr.microsoft.com/dotnet-buildtools/prereqs:alpine-3.21-helix-amd64
+        - (Alpine.321.Amd64.Open)azurelinux.3.amd64.open@mcr.microsoft.com/dotnet-buildtools/prereqs:alpine-3.21-helix-amd64
       - ${{ if eq(variables['System.TeamProject'], 'internal') }}:
-        - (Alpine.321.Amd64)Ubuntu.2204.Amd64@mcr.microsoft.com/dotnet-buildtools/prereqs:alpine-3.21-helix-amd64
+        - (Alpine.321.Amd64)azurelinux.3.amd64@mcr.microsoft.com/dotnet-buildtools/prereqs:alpine-3.21-helix-amd64
 
     # Linux musl arm32
     - ${{ if eq(parameters.platform, 'linux_musl_arm') }}:
@@ -98,9 +98,9 @@ jobs:
     # Linux x64
     - ${{ if eq(parameters.platform, 'linux_x64') }}:
       - ${{ if eq(variables['System.TeamProject'], 'public') }}:
-        - Ubuntu.2204.Amd64.Open
+        - azurelinux.3.amd64.open
       - ${{ if eq(variables['System.TeamProject'], 'internal') }}:
-        - Ubuntu.2204.Amd64
+        - azurelinux.3.amd64
 
     # OSX arm64
     - ${{ if eq(parameters.platform, 'osx_arm64') }}:
diff --git a/eng/pipelines/coreclr/templates/jit-replay-pipeline.yml b/eng/pipelines/coreclr/templates/jit-replay-pipeline.yml
new file mode 100644
index 00000000000000..74f2caf2ff3852
--- /dev/null
+++ b/eng/pipelines/coreclr/templates/jit-replay-pipeline.yml
@@ -0,0 +1,58 @@
+parameters:
+  - name: platforms
+    type: object
+  - name: replayType
+    type: string
+    default: standard
+
+extends:
+  template:  /eng/pipelines/common/templates/pipeline-with-resources.yml
+  parameters:
+    stages:
+    # Don't run if the JIT-EE GUID has changed,
+    # since there won't be any SuperPMI collections with the new GUID until the collection
+    # pipeline completes after this PR is merged.
+    - ${{ if eq(variables['Build.Reason'], 'PullRequest') }}:
+      - stage: EvaluatePaths
+        displayName: Evaluate Paths
+        jobs:
+        - template: /eng/pipelines/common/evaluate-paths-job.yml
+          parameters:
+            paths:
+            - subset: jiteeversionguid
+              include:
+              - src/coreclr/inc/jiteeversionguid.h
+
+    - stage: Build
+      jobs:
+
+      - template: /eng/pipelines/common/platform-matrix.yml
+        parameters:
+          jobTemplate: /eng/pipelines/common/global-build-job.yml
+          buildConfig: checked
+          platforms: ${{ parameters.platforms }}
+          jobParameters:
+            buildArgs: -s clr.alljits+clr.spmi -c $(_BuildConfig)
+            postBuildSteps:
+              - template: /eng/pipelines/common/upload-artifact-step.yml
+                parameters:
+                  rootFolder: $(Build.SourcesDirectory)/artifacts/bin/coreclr
+                  includeRootFolder: false
+                  archiveType: $(archiveType)
+                  tarCompression: $(tarCompression)
+                  archiveExtension: $(archiveExtension)
+                  artifactName: BuildArtifacts_$(osGroup)$(osSubgroup)_$(archType)_$(_BuildConfig)
+                  displayName: Build Assets
+            condition: not(eq(stageDependencies.EvaluatePaths.evaluate_paths.outputs['SetPathVars_jiteeversionguid.containsChange'], true))
+
+      - template: /eng/pipelines/common/platform-matrix.yml
+        parameters:
+          jobTemplate: /eng/pipelines/coreclr/templates/superpmi-replay-job.yml
+          buildConfig: checked
+          platforms: ${{ parameters.platforms }}
+          helixQueueGroup: ci
+          helixQueuesTemplate: /eng/pipelines/coreclr/templates/helix-queues-setup.yml
+          jobParameters:
+            condition: not(eq(stageDependencies.EvaluatePaths.evaluate_paths.outputs['SetPathVars_jiteeversionguid.containsChange'], true))
+            replayType: ${{ parameters.replayType }}
+            unifiedArtifactsName: BuildArtifacts_$(osGroup)$(osSubgroup)_$(archType)_$(_BuildConfig)
diff --git a/eng/pipelines/coreclr/templates/run-superpmi-replay-job.yml b/eng/pipelines/coreclr/templates/run-superpmi-replay-job.yml
index 4047a8cb2f7cf6..27cc61c653de47 100644
--- a/eng/pipelines/coreclr/templates/run-superpmi-replay-job.yml
+++ b/eng/pipelines/coreclr/templates/run-superpmi-replay-job.yml
@@ -16,6 +16,7 @@ parameters:
   enableTelemetry: false          # optional -- enable for telemetry
   liveLibrariesBuildConfig: ''    # optional -- live-live libraries configuration to use for the run
   helixQueues: ''                 # required -- Helix queues
+  replayType: 'standard'          # required -- 'standard', 'apx'
 
 jobs:
 - template: /eng/pipelines/common/templates/runtimes/xplat-job.yml
@@ -47,6 +48,9 @@ jobs:
     - ${{ each variable in parameters.variables }}:
       - ${{insert}}: ${{ variable }}
 
+    - name: replayType
+      value: ${{ parameters.replayType }}
+
     - template: /eng/pipelines/coreclr/templates/jit-python-variables.yml
       parameters:
         osGroup: ${{ parameters.osGroup }}
@@ -74,8 +78,8 @@ jobs:
         mkdir $(SpmiLogsLocation)
       displayName: Create directories
 
-    - script: $(PythonScript) $(Build.SourcesDirectory)/src/coreclr/scripts/superpmi_replay_setup.py -source_directory $(Build.SourcesDirectory) -product_directory $(buildProductRootFolderPath) -arch $(archType)
-      displayName: ${{ format('SuperPMI replay setup ({0})', parameters.archType) }}
+    - script: $(PythonScript) $(Build.SourcesDirectory)/src/coreclr/scripts/superpmi_replay_setup.py -source_directory $(Build.SourcesDirectory) -product_directory $(buildProductRootFolderPath) -type $(replayType) -arch $(archType)
+      displayName: ${{ format('SuperPMI replay setup ({0} {1})', parameters.replayType, parameters.archType) }}
 
       # Run superpmi replay in helix
     - template: /eng/pipelines/common/templates/runtimes/send-to-helix-step.yml
@@ -93,6 +97,7 @@ jobs:
         BuildConfig: ${{ parameters.buildConfig }}
         osGroup: ${{ parameters.osGroup }}
         archType: ${{ parameters.archType }}
+        SuperPmiReplayType: ${{ parameters.replayType }}
 
       # Always upload the available logs for diagnostics
     - task: CopyFiles@2
diff --git a/eng/pipelines/coreclr/templates/superpmi-collect-pipeline.yml b/eng/pipelines/coreclr/templates/superpmi-collect-pipeline.yml
new file mode 100644
index 00000000000000..95d99f3357d385
--- /dev/null
+++ b/eng/pipelines/coreclr/templates/superpmi-collect-pipeline.yml
@@ -0,0 +1,359 @@
+# This template definition automates the SuperPMI collection process,
+# and is used by the SuperPMI pipeline definitions.
+
+extends:
+  template:  /eng/pipelines/common/templates/pipeline-with-resources.yml
+  parameters:
+    stages:
+    - stage: Build
+      jobs:
+
+      - template: /eng/pipelines/common/platform-matrix.yml
+        parameters:
+          jobTemplate: /eng/pipelines/common/global-build-job.yml
+          buildConfig: checked
+          platforms:
+          - windows_x64
+          - linux_x64
+          jobParameters:
+            testGroup: outerloop
+            buildArgs: -s clr+libs+libs.tests -rc $(_BuildConfig) -c Release /p:ArchiveTests=true
+            timeoutInMinutes: 120
+            postBuildSteps:
+              - template: /eng/pipelines/coreclr/templates/build-native-test-assets-step.yml
+              - template: /eng/pipelines/common/upload-artifact-step.yml
+                parameters:
+                  rootFolder: $(Build.SourcesDirectory)/artifacts/bin
+                  includeRootFolder: false
+                  archiveType: $(archiveType)
+                  archiveExtension: $(archiveExtension)
+                  tarCompression: $(tarCompression)
+                  artifactName: BuildArtifacts_$(osGroup)$(osSubgroup)_$(archType)_$(_BuildConfig)
+              - template: /eng/pipelines/common/upload-artifact-step.yml
+                parameters:
+                  rootFolder: $(Build.SourcesDirectory)/artifacts/helix
+                  includeRootFolder: false
+                  archiveType: $(archiveType)
+                  archiveExtension: $(archiveExtension)
+                  tarCompression: $(tarCompression)
+                  artifactName: LibrariesTestArtifacts_$(osGroup)$(osSubgroup)_$(archType)_$(_BuildConfig)
+            extraVariablesTemplates:
+              - template: /eng/pipelines/common/templates/runtimes/native-test-assets-variables.yml
+                parameters:
+                  testGroup: outerloop
+            disableComponentGovernance: true # No shipping artifacts produced by this pipeline
+
+      - template: /eng/pipelines/common/platform-matrix.yml
+        parameters:
+          jobTemplate: /eng/pipelines/common/global-build-job.yml
+          buildConfig: checked
+          platforms:
+          - windows_x86
+          - windows_arm64
+          - osx_arm64
+          jobParameters:
+            testGroup: outerloop
+            buildArgs: -s clr+libs+libs.tests -rc $(_BuildConfig) -c Release /p:ArchiveTests=true
+            timeoutInMinutes: 120
+            postBuildSteps:
+              # Build CLR assets for x64 as well as the target as we need an x64 mcs
+              - template: /eng/pipelines/common/templates/global-build-step.yml
+                parameters:
+                  buildArgs: -s clr.spmi -c $(_BuildConfig)
+                  archParameter: -arch x64
+                  displayName: Build SuperPMI
+              - template: /eng/pipelines/coreclr/templates/build-native-test-assets-step.yml
+              - template: /eng/pipelines/common/upload-artifact-step.yml
+                parameters:
+                  rootFolder: $(Build.SourcesDirectory)/artifacts/bin
+                  includeRootFolder: false
+                  archiveType: $(archiveType)
+                  archiveExtension: $(archiveExtension)
+                  tarCompression: $(tarCompression)
+                  artifactName: BuildArtifacts_$(osGroup)$(osSubgroup)_$(archType)_$(_BuildConfig)
+              - template: /eng/pipelines/common/upload-artifact-step.yml
+                parameters:
+                  rootFolder: $(Build.SourcesDirectory)/artifacts/helix
+                  includeRootFolder: false
+                  archiveType: $(archiveType)
+                  archiveExtension: $(archiveExtension)
+                  tarCompression: $(tarCompression)
+                  artifactName: LibrariesTestArtifacts_$(osGroup)$(osSubgroup)_$(archType)_$(_BuildConfig)
+            extraVariablesTemplates:
+              - template: /eng/pipelines/common/templates/runtimes/native-test-assets-variables.yml
+                parameters:
+                  testGroup: outerloop
+            disableComponentGovernance: true # No shipping artifacts produced by this pipeline
+
+      - template: /eng/pipelines/common/platform-matrix.yml
+        parameters:
+          jobTemplate: /eng/pipelines/common/global-build-job.yml
+          buildConfig: checked
+          platforms:
+          - linux_arm
+          - linux_arm64
+          jobParameters:
+            testGroup: outerloop
+            buildArgs: -s clr+libs+libs.tests -rc $(_BuildConfig) -c Release /p:ArchiveTests=true
+            timeoutInMinutes: 120
+            postBuildSteps:
+              # Build CLR assets for x64 as well as the target as we need an x64 mcs
+              - template: /eng/pipelines/common/templates/global-build-step.yml
+                parameters:
+                  buildArgs: -s clr.spmi -c $(_BuildConfig)
+                  archParameter: -arch x64
+                  container: linux_x64
+                  displayName: Build SuperPMI
+              - template: /eng/pipelines/coreclr/templates/build-native-test-assets-step.yml
+              - template: /eng/pipelines/common/upload-artifact-step.yml
+                parameters:
+                  rootFolder: $(Build.SourcesDirectory)/artifacts/bin
+                  includeRootFolder: false
+                  archiveType: $(archiveType)
+                  archiveExtension: $(archiveExtension)
+                  tarCompression: $(tarCompression)
+                  artifactName: BuildArtifacts_$(osGroup)$(osSubgroup)_$(archType)_$(_BuildConfig)
+              - template: /eng/pipelines/common/upload-artifact-step.yml
+                parameters:
+                  rootFolder: $(Build.SourcesDirectory)/artifacts/helix
+                  includeRootFolder: false
+                  archiveType: $(archiveType)
+                  archiveExtension: $(archiveExtension)
+                  tarCompression: $(tarCompression)
+                  artifactName: LibrariesTestArtifacts_$(osGroup)$(osSubgroup)_$(archType)_$(_BuildConfig)
+            extraVariablesTemplates:
+              - template: /eng/pipelines/common/templates/runtimes/native-test-assets-variables.yml
+                parameters:
+                  testGroup: outerloop
+            disableComponentGovernance: true # No shipping artifacts produced by this pipeline
+
+      - template: /eng/pipelines/common/platform-matrix.yml
+        parameters:
+          jobTemplate: /eng/pipelines/common/templates/runtimes/build-test-job.yml
+          buildConfig: checked
+          platforms:
+          - CoreClrTestBuildHost # Either osx_x64 or linux_x64
+          jobParameters:
+            testGroup: outerloop
+
+      - template: /eng/pipelines/common/platform-matrix.yml
+        parameters:
+          jobTemplate: /eng/pipelines/coreclr/templates/superpmi-collect-job.yml
+          buildConfig: checked
+          platforms:
+          - osx_arm64
+          - linux_arm
+          - linux_arm64
+          - linux_x64
+          - windows_x64
+          - windows_x86
+          - windows_arm64
+          helixQueueGroup: ci
+          helixQueuesTemplate: /eng/pipelines/coreclr/templates/helix-queues-setup.yml
+          jobParameters:
+            testGroup: outerloop
+            liveLibrariesBuildConfig: Release
+            collectionType: pmi
+            collectionName: libraries
+
+      - template: /eng/pipelines/common/platform-matrix.yml
+        parameters:
+          jobTemplate: /eng/pipelines/coreclr/templates/superpmi-collect-job.yml
+          buildConfig: checked
+          platforms:
+          - osx_arm64
+          - linux_arm
+          - linux_arm64
+          - linux_x64
+          - windows_x64
+          - windows_x86
+          - windows_arm64
+          helixQueueGroup: ci
+          helixQueuesTemplate: /eng/pipelines/coreclr/templates/helix-queues-setup.yml
+          jobParameters:
+            testGroup: outerloop
+            liveLibrariesBuildConfig: Release
+            collectionType: crossgen2
+            collectionName: libraries
+
+      - template: /eng/pipelines/common/platform-matrix.yml
+        parameters:
+          jobTemplate: /eng/pipelines/coreclr/templates/superpmi-collect-job.yml
+          buildConfig: checked
+          platforms:
+          - osx_arm64
+          - linux_arm
+          - linux_arm64
+          - linux_x64
+          - windows_x64
+          - windows_x86
+          - windows_arm64
+          helixQueueGroup: ci
+          helixQueuesTemplate: /eng/pipelines/coreclr/templates/helix-queues-setup.yml
+          jobParameters:
+            testGroup: outerloop
+            liveLibrariesBuildConfig: Release
+            collectionType: run
+            collectionName: realworld
+
+      - template: /eng/pipelines/common/platform-matrix.yml
+        parameters:
+          jobTemplate: /eng/pipelines/coreclr/templates/superpmi-collect-job.yml
+          buildConfig: checked
+          platforms:
+          - osx_arm64
+          - linux_arm
+          - linux_arm64
+          - linux_x64
+          - windows_x64
+          - windows_x86
+          - windows_arm64
+          helixQueueGroup: ci
+          helixQueuesTemplate: /eng/pipelines/coreclr/templates/helix-queues-setup.yml
+          jobParameters:
+            testGroup: outerloop
+            liveLibrariesBuildConfig: Release
+            collectionType: run
+            collectionName: benchmarks
+
+      - template: /eng/pipelines/common/platform-matrix.yml
+        parameters:
+          jobTemplate: /eng/pipelines/coreclr/templates/superpmi-collect-job.yml
+          buildConfig: checked
+          platforms:
+          - osx_arm64
+          - linux_arm
+          - linux_arm64
+          - linux_x64
+          - windows_x64
+          - windows_x86
+          - windows_arm64
+          helixQueueGroup: ci
+          helixQueuesTemplate: /eng/pipelines/coreclr/templates/helix-queues-setup.yml
+          jobParameters:
+            testGroup: outerloop
+            liveLibrariesBuildConfig: Release
+            collectionType: run_pgo
+            collectionName: benchmarks
+
+      - template: /eng/pipelines/common/platform-matrix.yml
+        parameters:
+          jobTemplate: /eng/pipelines/coreclr/templates/superpmi-collect-job.yml
+          buildConfig: checked
+          platforms:
+          - osx_arm64
+          - linux_arm
+          - linux_arm64
+          - linux_x64
+          - windows_x64
+          - windows_x86
+          - windows_arm64
+          helixQueueGroup: ci
+          helixQueuesTemplate: /eng/pipelines/coreclr/templates/helix-queues-setup.yml
+          jobParameters:
+            testGroup: outerloop
+            liveLibrariesBuildConfig: Release
+            collectionType: run_pgo_optrepeat
+            collectionName: benchmarks
+
+      #
+      # Collection of coreclr test run
+      #
+      - template: /eng/pipelines/common/platform-matrix.yml
+        parameters:
+          jobTemplate: /eng/pipelines/common/templates/runtimes/run-test-job.yml
+          buildConfig: checked
+          platforms:
+          - osx_arm64
+          - linux_arm
+          - linux_arm64
+          - linux_x64
+          - windows_x64
+          - windows_x86
+          - windows_arm64
+          helixQueueGroup: superpmi
+          helixQueuesTemplate: /eng/pipelines/coreclr/templates/helix-queues-setup.yml
+          jobParameters:
+            testGroup: outerloop
+            liveLibrariesBuildConfig: Release
+            SuperPmiCollect: true
+            unifiedArtifactsName: BuildArtifacts_$(osGroup)$(osSubgroup)_$(archType)_$(_BuildConfig)
+
+      - template: /eng/pipelines/common/platform-matrix.yml
+        parameters:
+          jobTemplate: /eng/pipelines/coreclr/templates/superpmi-collect-job.yml
+          buildConfig: checked
+          platforms:
+          - linux_arm64
+          - linux_x64
+          - windows_x64
+          - windows_arm64
+          helixQueueGroup: ci
+          helixQueuesTemplate: /eng/pipelines/coreclr/templates/helix-queues-setup.yml
+          jobParameters:
+            testGroup: outerloop
+            liveLibrariesBuildConfig: Release
+            collectionType: nativeaot
+            collectionName: smoke_tests
+
+      #
+      # Collection of libraries test run: normal
+      # Libraries Test Run using Release libraries, and Checked CoreCLR
+      #
+      - template: /eng/pipelines/common/platform-matrix.yml
+        parameters:
+          jobTemplate: /eng/pipelines/libraries/run-test-job.yml
+          buildConfig: Release
+          platforms:
+          - osx_arm64
+          - linux_arm
+          - linux_arm64
+          - linux_x64
+          - windows_x64
+          - windows_x86
+          - windows_arm64
+          helixQueueGroup: superpmi
+          helixQueuesTemplate: /eng/pipelines/coreclr/templates/helix-queues-setup.yml
+          jobParameters:
+            testScope: innerloop
+            liveRuntimeBuildConfig: Checked
+            dependsOnTestBuildConfiguration: Release
+            dependsOnTestArchitecture: x64
+            scenarios:
+            - normal
+            SuperPmiCollect: true
+            SuperPmiCollectionName: libraries_tests
+            unifiedArtifactsName: BuildArtifacts_$(osGroup)$(osSubgroup)_$(archType)_Checked
+            helixArtifactsName: LibrariesTestArtifacts_$(osGroup)$(osSubgroup)_$(archType)_Checked
+            unifiedBuildConfigOverride: checked
+
+      #
+      # Collection of libraries test run: no_tiered_compilation
+      # Libraries Test Run using Release libraries, and Checked CoreCLR
+      #
+      - template: /eng/pipelines/common/platform-matrix.yml
+        parameters:
+          jobTemplate: /eng/pipelines/libraries/run-test-job.yml
+          buildConfig: Release
+          platforms:
+          - osx_arm64
+          - linux_arm
+          - linux_arm64
+          - linux_x64
+          - windows_x64
+          - windows_x86
+          - windows_arm64
+          helixQueueGroup: superpmi
+          helixQueuesTemplate: /eng/pipelines/coreclr/templates/helix-queues-setup.yml
+          jobParameters:
+            testScope: innerloop
+            liveRuntimeBuildConfig: Checked
+            dependsOnTestBuildConfiguration: Release
+            dependsOnTestArchitecture: x64
+            scenarios:
+            - no_tiered_compilation
+            SuperPmiCollect: true
+            SuperPmiCollectionName: libraries_tests_no_tiered_compilation
+            unifiedArtifactsName: BuildArtifacts_$(osGroup)$(osSubgroup)_$(archType)_Checked
+            helixArtifactsName: LibrariesTestArtifacts_$(osGroup)$(osSubgroup)_$(archType)_Checked
+            unifiedBuildConfigOverride: checked
diff --git a/eng/pipelines/coreclr/templates/superpmi-replay-job.yml b/eng/pipelines/coreclr/templates/superpmi-replay-job.yml
index ea7854339a2147..ec364c8d506321 100644
--- a/eng/pipelines/coreclr/templates/superpmi-replay-job.yml
+++ b/eng/pipelines/coreclr/templates/superpmi-replay-job.yml
@@ -9,12 +9,14 @@ parameters:
   variables: {}
   helixQueues: ''
   runJobTemplate: '/eng/pipelines/coreclr/templates/run-superpmi-replay-job.yml'
+  replayType: 'standard'
+  unifiedArtifactsName: ''
 
 jobs:
 - template: ${{ parameters.runJobTemplate }}
   parameters:
-    jobName: ${{ format('superpmi_replay_{0}{1}_{2}_{3}', parameters.osGroup, parameters.osSubgroup, parameters.archType, parameters.buildConfig) }}
-    displayName: ${{ format('SuperPMI replay {0}{1} {2} {3}', parameters.osGroup, parameters.osSubgroup, parameters.archType, parameters.buildConfig) }}
+    jobName: ${{ format('superpmi_replay_{0}_{1}{2}_{3}_{4}', parameters.replayType, parameters.osGroup, parameters.osSubgroup, parameters.archType, parameters.buildConfig) }}
+    displayName: ${{ format('SuperPMI replay {0} {1}{2} {3} {4}', parameters.replayType, parameters.osGroup, parameters.osSubgroup, parameters.archType, parameters.buildConfig) }}
     pool: ${{ parameters.pool }}
     buildConfig: ${{ parameters.buildConfig }}
     archType: ${{ parameters.archType }}
@@ -23,6 +25,7 @@ jobs:
     condition: ${{ parameters.condition }}
     timeoutInMinutes: ${{ parameters.timeoutInMinutes }}
     helixQueues: ${{ parameters.helixQueues }}
+    replayType: ${{ parameters.replayType }}
     dependsOn:
       - 'build_${{ parameters.osGroup }}${{ parameters.osSubgroup }}_${{ parameters.archType }}_${{ parameters.buildConfig }}_'
 
@@ -30,11 +33,11 @@ jobs:
 
     steps:
 
-    # Download jit builds
+    # Download builds
     - template: /eng/pipelines/common/download-artifact-step.yml
       parameters:
         unpackFolder: $(Build.SourcesDirectory)/artifacts/bin/coreclr
-        artifactFileName: 'CheckedJIT_$(osGroup)$(osSubgroup)_$(archType)$(archiveExtension)'
-        artifactName: 'CheckedJIT_$(osGroup)$(osSubgroup)_$(archType)'
-        displayName: 'JIT checked build'
+        artifactFileName: '${{ parameters.unifiedArtifactsName }}$(archiveExtension)'
+        artifactName: '${{ parameters.unifiedArtifactsName }}'
+        displayName: 'unified artifacts'
         cleanUnpackFolder: false
diff --git a/eng/pipelines/diagnostics/runtime-diag-job.yml b/eng/pipelines/diagnostics/runtime-diag-job.yml
new file mode 100644
index 00000000000000..0c8009aaf8a8e2
--- /dev/null
+++ b/eng/pipelines/diagnostics/runtime-diag-job.yml
@@ -0,0 +1,261 @@
+parameters:
+  buildConfig: ''
+  name: ''
+  buildArgs: ''
+  archType: ''
+  hostedOs: ''
+  osGroup: ''
+  osSubgroup: ''
+  container: ''
+  crossBuild: false
+  variables: []
+  targetRid: ''
+  timeoutInMinutes: ''
+  dependsOn: []
+  # The following parameter is used to specify dependencies on other global build for the same platform.
+  # We provide this mechanism to allow for global builds to depend on other global builds and use the multiplexing
+  # that platform-matrix.yml enables.
+  # Each item can have the following properties:
+  # - name: The suffix of the job name to depend on.
+  # - buildConfig: The configuration of the job to depend on.
+  dependsOnGlobalBuilds: []
+  pool: ''
+  platform: ''
+  condition: true
+  useContinueOnErrorDuringBuild: false
+  shouldContinueOnError: false
+  isOfficialBuild: false
+  runtimeFlavor: 'coreclr'
+  runtimeVariant: ''
+  helixQueues: ''
+  enablePublishTestResults: false
+  testResultsFormat: ''
+  postBuildSteps: []
+  extraVariablesTemplates: []
+  preBuildSteps: []
+  templatePath: 'templates'
+  templateContext: ''
+  disableComponentGovernance: ''
+  liveRuntimeDir: ''
+
+jobs:
+- template: /eng/common/${{ parameters.templatePath }}/job/job.yml
+  parameters:
+    name: ${{ coalesce(parameters.name, parameters.osGroup) }}_${{ parameters.archType }}_${{ parameters.buildConfig }}
+    pool: ${{ parameters.pool }}
+    container: ${{ parameters.container }}
+    condition: and(succeeded(), ${{ parameters.condition }})
+    timeoutInMinutes: ${{ parameters.timeoutInMinutes }}
+    enablePublishTestResults: ${{ parameters.enablePublishTestResults }}
+    testResultsFormat: ${{ parameters.testResultsFormat }}
+
+    ${{ if ne(parameters.templateContext, '') }}:
+      templateContext: ${{ parameters.templateContext }}
+
+    artifacts:
+      publish:
+        logs:
+          ${{ if notin(parameters.osGroup, 'browser', 'wasi') }}:
+            name: Logs_Build_Attempt$(System.JobAttempt)_${{ parameters.osGroup }}_${{ parameters.osSubGroup }}_${{ parameters.archType }}_${{ parameters.buildConfig }}_${{ parameters.name }}
+          ${{ if in(parameters.osGroup, 'browser', 'wasi') }}:
+            name: Logs_Build_Attempt$(System.JobAttempt)_${{ parameters.osGroup }}_${{ parameters.archType }}_${{ parameters.hostedOs }}_${{ parameters.buildConfig }}_${{ parameters.name }}
+
+    # Component governance does not work on musl machines
+    ${{ if eq(parameters.osSubGroup, '_musl') }}:
+      disableComponentGovernance: true
+    ${{ else }}:
+      disableComponentGovernance: ${{ parameters.disableComponentGovernance }}
+
+    workspace:
+      clean: all
+
+    ${{ if or(ne(parameters.dependsOn,''), ne(parameters.dependsOnGlobalBuilds,'')) }}:
+      dependsOn:
+      - ${{ each build in parameters.dependsOn }}:
+        - ${{ build }}
+      - ${{ each globalBuild in parameters.dependsOnGlobalBuilds }}:
+        - ${{ format('build_{0}{1}_{2}_{3}_{4}', parameters.osGroup, parameters.osSubgroup, parameters.archType, coalesce(globalBuild.buildConfig, parameters.buildConfig), globalBuild.name) }}
+
+    variables:
+      - ${{ if eq(variables['System.TeamProject'], 'internal') }}:
+          - group: DotNet-HelixApi-Access
+          - group: AzureDevOps-Artifact-Feeds-Pats
+      
+      - _PhaseName: ${{ coalesce(parameters.name, parameters.osGroup) }}_${{ parameters.archType }}_${{ parameters.buildConfig }}
+      - _Pipeline_StreamDumpDir: $(Build.SourcesDirectory)/artifacts/tmp/${{ parameters.buildConfig }}/streams
+
+      - _TestArgs: '-test'
+      - _Cross: ''
+
+      - _buildScript: $(Build.SourcesDirectory)$(dir)build$(scriptExt)
+
+      - ${{ if and(eq(parameters.testOnly, 'true'), eq(parameters.buildOnly, 'true')) }}:
+        'error, testOnly and buildOnly cannot be true at the same time': error
+
+      - ${{ if eq(parameters.testOnly, 'true') }}:
+        - _TestArgs: '-test -skipnative'
+
+      - ${{ if or(eq(parameters.buildOnly, 'true'), eq(parameters.isCodeQLRun, 'true')) }}:
+        - _TestArgs: ''
+
+      # For testing msrc's and service releases. The RuntimeSourceVersion is either "default" or the service release version to test
+      - _InternalInstallArgs: ''
+      - ${{ if and(ne(variables['System.TeamProject'], 'public'), notin(variables['Build.Reason'], 'PullRequest'), eq(parameters.isCodeQLRun, 'false')) }}:
+        - _InternalInstallArgs:
+            -dotnetruntimeversion '$(DotnetRuntimeVersion)'
+            -dotnetruntimedownloadversion '$(DotnetRuntimeDownloadVersion)'
+            -runtimesourcefeed '$(RuntimeFeedUrl)'
+            -runtimesourcefeedkey '$(RuntimeFeedBase64SasToken)'
+
+      - ${{ each variableTemplate in parameters.extraVariablesTemplates }}:
+        - template: ${{ variableTemplate.template }}
+          parameters:
+            osGroup: ${{ parameters.osGroup }}
+            osSubgroup: ${{ parameters.osSubgroup }}
+            archType: ${{ parameters.archType }}
+            buildConfig: ${{ parameters.buildConfig }}
+            runtimeFlavor: ${{ parameters.runtimeFlavor }}
+            runtimeVariant: ${{ parameters.runtimeVariant }}
+            helixQueues: ${{ parameters.helixQueues }}
+            targetRid: ${{ parameters.targetRid }}
+            name: ${{ parameters.name }}
+            platform: ${{ parameters.platform }}
+            shouldContinueOnError: ${{ parameters.shouldContinueOnError }}
+            ${{ if ne(variableTemplate.forwardedParameters, '') }}:
+              ${{ each parameter in variableTemplate.forwardedParameters }}:
+                ${{ parameter }}: ${{ parameters[parameter] }}
+            ${{ if ne(variableTemplate.parameters, '') }}:
+              ${{ insert }}: ${{ variableTemplate.parameters }}
+
+      - ${{ each variable in parameters.variables }}:
+        - ${{ variable }}
+
+    steps:
+    - ${{ if eq(parameters.osGroup, 'windows') }}:
+      - template: /eng/pipelines/common/templates/disable-vsupdate-or-failfast.yml
+
+    - checkout: diagnostics
+      clean: true
+      fetchDepth: $(checkoutFetchDepth)
+
+    - ${{ if and(eq(parameters.isOfficialBuild, true), notin(parameters.osGroup, 'osx', 'maccatalyst', 'ios', 'iossimulator', 'tvos', 'tvossimulator')) }}:
+      - template: /eng/pipelines/common/restore-internal-tools.yml
+
+    - ${{ if ne(variables['System.TeamProject'], 'public') }}:
+      - ${{ if and(ne(parameters.osGroup, 'windows'), ne(parameters.hostedOs, 'windows')) }}:
+        - task: Bash@3
+          displayName: Setup Private Feeds Credentials
+          inputs:
+            filePath: $(Build.SourcesDirectory)/eng/common/SetupNugetSources.sh
+            arguments: $(Build.SourcesDirectory)/NuGet.config $Token
+          env:
+            Token: $(dn-bot-dnceng-artifact-feeds-rw)
+      - ${{ else }}:
+        - task: PowerShell@2
+          displayName: Setup Private Feeds Credentials
+          inputs:
+            filePath: $(Build.SourcesDirectory)/eng/common/SetupNugetSources.ps1
+            arguments: -ConfigFile $(Build.SourcesDirectory)/NuGet.config -Password $Env:Token
+          env:
+            Token: $(dn-bot-dnceng-artifact-feeds-rw)
+        # Run the NuGetAuthenticate task after the internal feeds are added to the nuget.config
+        # This ensures that creds are set appropriately for all feeds in the config, and that the
+        # credential provider is installed.
+        - task: NuGetAuthenticate@1
+
+    - ${{ if in(parameters.osGroup, 'osx', 'maccatalyst', 'ios', 'iossimulator', 'tvos', 'tvossimulator') }}:
+      - script: $(Build.SourcesDirectory)/eng/common/native/install-dependencies.sh ${{ parameters.osGroup }}
+        displayName: Install Build Dependencies
+
+      - script: |
+          du -sh $(Build.SourcesDirectory)/*
+          df -h
+        displayName: Disk Usage before Build
+
+    - ${{ if in(parameters.platform, 'browser_wasm_win', 'wasi_wasm_win') }}:
+      # Update machine certs
+      - task: PowerShell@2
+        displayName: Update machine certs
+        inputs:
+          filePath: $(Build.SourcesDirectory)/eng/pipelines/mono/update-machine-certs.ps1
+
+    - ${{ if ne(parameters.preBuildSteps,'') }}:
+      - ${{ each preBuildStep in parameters.preBuildSteps }}:
+        - ${{ if ne(preBuildStep.template, '') }}:
+          - template: ${{ preBuildStep.template }}
+            parameters:
+              osGroup: ${{ parameters.osGroup }}
+              osSubgroup: ${{ parameters.osSubgroup }}
+              archType: ${{ parameters.archType }}
+              buildConfig: ${{ parameters.buildConfig }}
+              runtimeFlavor: ${{ parameters.runtimeFlavor }}
+              runtimeVariant: ${{ parameters.runtimeVariant }}
+              helixQueues: ${{ parameters.helixQueues }}
+              targetRid: ${{ parameters.targetRid }}
+              name: ${{ parameters.name }}
+              platform: ${{ parameters.platform }}
+              shouldContinueOnError: ${{ parameters.shouldContinueOnError }}
+              ${{ if ne(preBuildStep.forwardedParameters, '') }}:
+                ${{ each parameter in preBuildStep.forwardedParameters }}:
+                  ${{ parameter }}: ${{ parameters[parameter] }}
+              ${{ if ne(preBuildStep.parameters, '') }}:
+                ${{ insert }}: ${{ preBuildStep.parameters }}
+        - ${{ else }}:
+          - ${{ preBuildStep }}
+
+    # Build
+    - script: $(_buildScript)
+          -ci
+          -configuration ${{ parameters.buildConfig }}
+          -architecture ${{ parameters.archType }}
+          -privatebuild
+          -useCdac
+          -liveRuntimeDir ${{ parameters.liveRuntimeDir }}
+          $(_TestArgs)
+          $(_Cross)
+          $(_InternalInstallArgs)
+          /p:OfficialBuildId=$(BUILD.BUILDNUMBER)
+      ${{ if eq(parameters.testOnly, 'true') }}:
+        displayName: Test
+      ${{ elseif eq(parameters.buildOnly, 'true') }}:
+        displayName: Build
+      ${{ else }}:
+        displayName: Build / Test
+      condition: succeeded()
+
+    - ${{ if in(parameters.osGroup, 'osx', 'ios', 'tvos', 'android') }}:
+      - script: |
+          du -sh $(Build.SourcesDirectory)/*
+          df -h
+        displayName: Disk Usage after Build
+        condition: always()
+
+      # If intended to send extra steps after regular build add them here.
+    - ${{ if ne(parameters.postBuildSteps,'') }}:
+      - ${{ each postBuildStep in parameters.postBuildSteps }}:
+        - ${{ if ne(postBuildStep.template, '') }}:
+          - template: ${{ postBuildStep.template }}
+            parameters:
+              osGroup: ${{ parameters.osGroup }}
+              osSubgroup: ${{ parameters.osSubgroup }}
+              archType: ${{ parameters.archType }}
+              buildConfig: ${{ parameters.buildConfig }}
+              runtimeFlavor: ${{ parameters.runtimeFlavor }}
+              runtimeVariant: ${{ parameters.runtimeVariant }}
+              helixQueues: ${{ parameters.helixQueues }}
+              targetRid: ${{ parameters.targetRid }}
+              name: ${{ parameters.name }}
+              platform: ${{ parameters.platform }}
+              shouldContinueOnError: ${{ parameters.shouldContinueOnError }}
+              ${{ if ne(postBuildStep.forwardedParameters, '') }}:
+                ${{ each parameter in postBuildStep.forwardedParameters }}:
+                  ${{ parameter }}: ${{ parameters[parameter] }}
+              ${{ if ne(postBuildStep.parameters, '') }}:
+                ${{ insert }}: ${{ postBuildStep.parameters }}
+        - ${{ else }}:
+          - ${{ postBuildStep }}
+
+      - ${{ if and(eq(parameters.isOfficialBuild, true), eq(parameters.osGroup, 'windows')) }}:
+        - powershell: ./eng/collect_vsinfo.ps1 -ArchiveRunName postbuild_log
+          displayName: Collect vslogs on exit
+          condition: always()
diff --git a/eng/pipelines/extra-platforms/runtime-extra-platforms-ioslike.yml b/eng/pipelines/extra-platforms/runtime-extra-platforms-ioslike.yml
index 47a38df343f80d..7f10d317464cf8 100644
--- a/eng/pipelines/extra-platforms/runtime-extra-platforms-ioslike.yml
+++ b/eng/pipelines/extra-platforms/runtime-extra-platforms-ioslike.yml
@@ -39,9 +39,9 @@ jobs:
       isExtraPlatforms: ${{ parameters.isExtraPlatformsBuild }}
       # Don't trim tests on rolling builds
       ${{ if eq(variables['isRollingBuild'], true) }}:
-        buildArgs: -s mono+libs+libs.tests -c $(_BuildConfig) /p:ArchiveTests=true /p:DevTeamProvisioning=- /p:RunAOTCompilation=true $(_runSmokeTestsOnlyArg) /p:BuildTestsOnHelix=true /p:EnableAdditionalTimezoneChecks=true /p:UsePortableRuntimePack=false /p:BuildDarwinFrameworks=true /p:IsManualOrRollingBuild=true /p:EnableAggressiveTrimming=false
+        buildArgs: -s mono+libs+libs.tests -c $(_BuildConfig) /p:ArchiveTests=true /p:DevTeamProvisioning=- /p:RunAOTCompilation=true $(_runSmokeTestsOnlyArg) /p:BuildTestsOnHelix=true /p:EnableAdditionalTimezoneChecks=true /p:UsePortableRuntimePack=false /p:IsManualOrRollingBuild=true /p:EnableAggressiveTrimming=false
       ${{ else }}:
-        buildArgs: -s mono+libs+libs.tests -c $(_BuildConfig) /p:ArchiveTests=true /p:DevTeamProvisioning=- /p:RunAOTCompilation=true $(_runSmokeTestsOnlyArg) /p:BuildTestsOnHelix=true /p:EnableAdditionalTimezoneChecks=true /p:UsePortableRuntimePack=false /p:BuildDarwinFrameworks=true /p:IsManualOrRollingBuild=true /p:EnableAggressiveTrimming=true
+        buildArgs: -s mono+libs+libs.tests -c $(_BuildConfig) /p:ArchiveTests=true /p:DevTeamProvisioning=- /p:RunAOTCompilation=true $(_runSmokeTestsOnlyArg) /p:BuildTestsOnHelix=true /p:EnableAdditionalTimezoneChecks=true /p:UsePortableRuntimePack=false /p:IsManualOrRollingBuild=true /p:EnableAggressiveTrimming=true
       timeoutInMinutes: 480
       # extra steps, run tests
       postBuildSteps:
diff --git a/eng/pipelines/extra-platforms/runtime-extra-platforms-ioslikesimulator.yml b/eng/pipelines/extra-platforms/runtime-extra-platforms-ioslikesimulator.yml
index 2cfb553d5dbd6e..3527856913c7ce 100644
--- a/eng/pipelines/extra-platforms/runtime-extra-platforms-ioslikesimulator.yml
+++ b/eng/pipelines/extra-platforms/runtime-extra-platforms-ioslikesimulator.yml
@@ -37,7 +37,7 @@ jobs:
     jobParameters:
       testGroup: innerloop
       nameSuffix: AllSubsets_Mono
-      buildArgs: -s mono+libs+host+packs+libs.tests -c $(_BuildConfig) /p:ArchiveTests=true $(_runSmokeTestsOnlyArg) /p:RunAOTCompilation=true /p:MonoForceInterpreter=true /p:BuildDarwinFrameworks=true
+      buildArgs: -s mono+libs+host+packs+libs.tests -c $(_BuildConfig) /p:ArchiveTests=true $(_runSmokeTestsOnlyArg) /p:RunAOTCompilation=true /p:MonoForceInterpreter=true
       timeoutInMinutes: 180
       # extra steps, run tests
       postBuildSteps:
diff --git a/eng/pipelines/extra-platforms/runtime-extra-platforms-maccatalyst.yml b/eng/pipelines/extra-platforms/runtime-extra-platforms-maccatalyst.yml
index 3eac90bcf95661..4ffece996d9cdc 100644
--- a/eng/pipelines/extra-platforms/runtime-extra-platforms-maccatalyst.yml
+++ b/eng/pipelines/extra-platforms/runtime-extra-platforms-maccatalyst.yml
@@ -34,7 +34,7 @@ jobs:
     jobParameters:
       testGroup: innerloop
       nameSuffix: AllSubsets_Mono
-      buildArgs: -s mono+libs+host+packs+libs.tests -c $(_BuildConfig) /p:ArchiveTests=true /p:DevTeamProvisioning=adhoc /p:RunAOTCompilation=true /p:MonoForceInterpreter=true /p:BuildDarwinFrameworks=true
+      buildArgs: -s mono+libs+host+packs+libs.tests -c $(_BuildConfig) /p:ArchiveTests=true /p:DevTeamProvisioning=adhoc /p:RunAOTCompilation=true /p:MonoForceInterpreter=true
       timeoutInMinutes: 180
       # extra steps, run tests
       postBuildSteps:
@@ -68,7 +68,7 @@ jobs:
     jobParameters:
       testGroup: innerloop
       nameSuffix: AllSubsets_Mono_AppSandbox
-      buildArgs: -s mono+libs+host+packs+libs.tests -c $(_BuildConfig) /p:ArchiveTests=true $(_runSmokeTestsOnlyArg) /p:DevTeamProvisioning=adhoc /p:RunAOTCompilation=true /p:MonoForceInterpreter=true /p:BuildDarwinFrameworks=true /p:EnableAppSandbox=true
+      buildArgs: -s mono+libs+host+packs+libs.tests -c $(_BuildConfig) /p:ArchiveTests=true $(_runSmokeTestsOnlyArg) /p:DevTeamProvisioning=adhoc /p:RunAOTCompilation=true /p:MonoForceInterpreter=true /p:EnableAppSandbox=true
       timeoutInMinutes: 180
       # extra steps, run tests
       postBuildSteps:
diff --git a/eng/pipelines/extra-platforms/runtime-extra-platforms-other.yml b/eng/pipelines/extra-platforms/runtime-extra-platforms-other.yml
index e47cb4996cc704..d4c755945c0372 100644
--- a/eng/pipelines/extra-platforms/runtime-extra-platforms-other.yml
+++ b/eng/pipelines/extra-platforms/runtime-extra-platforms-other.yml
@@ -40,7 +40,7 @@ jobs:
           eq(stageDependencies.EvaluatePaths.evaluate_paths.outputs['SetPathVars_libraries.containsChange'], true),
           eq(variables['isRollingBuild'], true))
 
-# Run net48 tests on win-x64
+# Run net481 tests on win-x64
 - template: /eng/pipelines/common/platform-matrix.yml
   parameters:
     jobTemplate: /eng/pipelines/common/global-build-job.yml
@@ -49,16 +49,16 @@ jobs:
     - windows_x64
     helixQueuesTemplate: /eng/pipelines/libraries/helix-queues-setup.yml
     jobParameters:
-      framework: net48
-      buildArgs: -s tools+libs+libs.tests -framework net48 -c $(_BuildConfig) -testscope innerloop /p:ArchiveTests=true
-      nameSuffix: Libraries_NET48
+      framework: net481
+      buildArgs: -s tools+libs+libs.tests -framework net481 -c $(_BuildConfig) -testscope innerloop /p:ArchiveTests=true
+      nameSuffix: Libraries_NET481
       timeoutInMinutes: 150
       postBuildSteps:
         - template: /eng/pipelines/libraries/helix.yml
           parameters:
             creator: dotnet-bot
-            testRunNamePrefixSuffix: NET48_$(_BuildConfig)
-            extraHelixArguments: /p:BuildTargetFramework=net48
+            testRunNamePrefixSuffix: NET481_$(_BuildConfig)
+            extraHelixArguments: /p:BuildTargetFramework=net481
       isExtraPlatformsBuild: ${{ parameters.isExtraPlatformsBuild }}
       condition: >-
         or(
diff --git a/eng/pipelines/libraries/helix-queues-setup.yml b/eng/pipelines/libraries/helix-queues-setup.yml
index 67afc0dd7b3c65..817c66b36286c5 100644
--- a/eng/pipelines/libraries/helix-queues-setup.yml
+++ b/eng/pipelines/libraries/helix-queues-setup.yml
@@ -34,7 +34,7 @@ jobs:
 
     # Linux arm64
     - ${{ if eq(parameters.platform, 'linux_arm64') }}:
-      - (Ubuntu.2204.Arm64.Open)Ubuntu.2204.ArmArch.Open@mcr.microsoft.com/dotnet-buildtools/prereqs:ubuntu-22.04-helix-arm64v8
+      - (Ubuntu.2504.Arm64.Open)Ubuntu.2204.ArmArch.Open@mcr.microsoft.com/dotnet-buildtools/prereqs:ubuntu-25.04-helix-arm64v8
       - ${{ if or(ne(parameters.jobParameters.isExtraPlatformsBuild, true), eq(parameters.jobParameters.includeAllPlatforms, true)) }}:
         - (Debian.13.Arm64.Open)Ubuntu.2204.Armarch.Open@mcr.microsoft.com/dotnet-buildtools/prereqs:debian-13-helix-arm64v8
 
@@ -56,7 +56,7 @@ jobs:
           - SLES.15.Amd64.Open
           - (Centos.10.Amd64.Open)Ubuntu.2204.Amd64.Open@mcr.microsoft.com/dotnet-buildtools/prereqs:centos-stream-10-helix-amd64
           - (Fedora.41.Amd64.Open)Ubuntu.2204.Amd64.Open@mcr.microsoft.com/dotnet-buildtools/prereqs:fedora-41-helix
-          - (Ubuntu.2204.Amd64.Open)Ubuntu.2204.Amd64.Open@mcr.microsoft.com/dotnet-buildtools/prereqs:ubuntu-22.04-helix-amd64
+          - (Ubuntu.2504.Amd64.Open)Ubuntu.2204.Amd64.Open@mcr.microsoft.com/dotnet-buildtools/prereqs:ubuntu-25.04-helix-amd64
           - (Debian.13.Amd64.Open)Ubuntu.2204.Amd64.Open@mcr.microsoft.com/dotnet-buildtools/prereqs:debian-13-helix-amd64
         - ${{ if or(ne(parameters.jobParameters.testScope, 'outerloop'), ne(parameters.jobParameters.runtimeFlavor, 'mono')) }}:
           - ${{ if or(eq(parameters.jobParameters.isExtraPlatformsBuild, true), eq(parameters.jobParameters.includeAllPlatforms, true)) }}:
@@ -115,7 +115,7 @@ jobs:
     # windows x64
     - ${{ if eq(parameters.platform, 'windows_x64') }}:
       # netcoreapp
-      - ${{ if notIn(parameters.jobParameters.framework, 'net48') }}:
+      - ${{ if notIn(parameters.jobParameters.framework, 'net481') }}:
         # libraries on mono outerloop
         - ${{ if and(eq(parameters.jobParameters.testScope, 'outerloop'), eq(parameters.jobParameters.runtimeFlavor, 'mono')) }}:
           - Windows.Amd64.Server2022.Open
@@ -137,14 +137,14 @@ jobs:
               - (Windows.Nano.1809.Amd64.Open)windows.10.amd64.serverrs5.open@mcr.microsoft.com/dotnet-buildtools/prereqs:nanoserver-1809-helix-amd64
 
       # .NETFramework
-      - ${{ if eq(parameters.jobParameters.framework, 'net48') }}:
+      - ${{ if eq(parameters.jobParameters.framework, 'net481') }}:
         - Windows.11.Amd64.Client.Open
 
 
     # windows x86
     - ${{ if eq(parameters.platform, 'windows_x86') }}:
       # netcoreapp
-      - ${{ if notIn(parameters.jobParameters.framework, 'net48') }}:
+      - ${{ if notIn(parameters.jobParameters.framework, 'net481') }}:
         # mono outerloop
         - ${{ if and(eq(parameters.jobParameters.testScope, 'outerloop'), eq(parameters.jobParameters.runtimeFlavor, 'mono')) }}:
           - Windows.11.Amd64.Client.Open
@@ -155,7 +155,7 @@ jobs:
             - Windows.11.Amd64.Client.Open
 
       # .NETFramework
-      - ${{ if eq(parameters.jobParameters.framework, 'net48') }}:
+      - ${{ if eq(parameters.jobParameters.framework, 'net481') }}:
         - Windows.10.Amd64.Client.Open
 
     # windows arm64
diff --git a/eng/pipelines/libraries/outerloop.yml b/eng/pipelines/libraries/outerloop.yml
index 597f298c37a3e0..bbb1adc12cde55 100644
--- a/eng/pipelines/libraries/outerloop.yml
+++ b/eng/pipelines/libraries/outerloop.yml
@@ -92,10 +92,10 @@ extends:
               - ${{ if eq(variables['isRollingBuild'], true) }}:
                 - windows_x64
               jobParameters:
-                framework: net48
+                framework: net481
                 testScope: outerloop
-                nameSuffix: NET48
-                buildArgs: -s libs+libs.tests -c $(_BuildConfig) -testscope outerloop /p:ArchiveTests=true -f net48
+                nameSuffix: NET481
+                buildArgs: -s libs+libs.tests -c $(_BuildConfig) -testscope outerloop /p:ArchiveTests=true -f net481
                 timeoutInMinutes: 180
                 includeAllPlatforms: ${{ variables['isRollingBuild'] }}
                 # extra steps, run tests
@@ -104,4 +104,4 @@ extends:
                     parameters:
                       testScope: outerloop
                       creator: dotnet-bot
-                      extraHelixArguments: /p:BuildTargetFramework=net48
+                      extraHelixArguments: /p:BuildTargetFramework=net481
diff --git a/eng/pipelines/libraries/stress/http.yml b/eng/pipelines/libraries/stress/http.yml
index 257334fce3e99c..e083bbac2a6e43 100644
--- a/eng/pipelines/libraries/stress/http.yml
+++ b/eng/pipelines/libraries/stress/http.yml
@@ -8,11 +8,11 @@ pr:
 schedules:
 - cron: "0 13 * * *" # 1PM UTC => 5 AM PST
   displayName: HttpStress nightly run
+  always: true
   branches:
     include:
     - main
-    - release/8.0
-    - release/9.0
+    - release/*-staging
 
 variables:
   - template: ../variables.yml
diff --git a/eng/pipelines/libraries/stress/ssl.yml b/eng/pipelines/libraries/stress/ssl.yml
index 360e67a86c98d4..eb2088242dcd2d 100644
--- a/eng/pipelines/libraries/stress/ssl.yml
+++ b/eng/pipelines/libraries/stress/ssl.yml
@@ -8,11 +8,11 @@ pr:
 schedules:
 - cron: "0 13 * * *" # 1PM UTC => 5 AM PST
   displayName: SslStress nightly run
+  always: true
   branches:
     include:
     - main
-    - release/8.0
-    - release/9.0
+    - release/*-staging
 
 variables:
   - template: ../variables.yml
diff --git a/eng/pipelines/official/pipeline.yml b/eng/pipelines/official/pipeline.yml
new file mode 100644
index 00000000000000..e343edd0e52b3f
--- /dev/null
+++ b/eng/pipelines/official/pipeline.yml
@@ -0,0 +1,30 @@
+parameters:
+- name: buildStage
+  type: stage
+- name: otherStages
+  type: stageList
+
+extends:
+  template:  /eng/pipelines/common/templates/pipeline-with-resources.yml
+  parameters:
+    isOfficialBuild: true
+    stages:
+    - ${{ each stage in parameters.otherStages }}:
+      - ${{ insert }}: ${{ stage }}
+    - ${{ insert }}: ${{ parameters.buildStage }}
+
+    - stage: Publish
+      jobs:
+      - template: /eng/pipelines/official/prepare-signed-artifacts.yml
+        parameters:
+          buildStage: ${{ parameters.buildStage }}
+          logArtifactName: 'Logs-PrepareSignedArtifacts_Attempt$(System.JobAttempt)'
+      - template: /eng/common/templates-official/job/publish-build-assets.yml
+        parameters:
+          publishUsingPipelines: true
+          publishAssetsImmediately: true
+          pool:
+            name: $(DncEngInternalBuildPool)
+            demands: ImageOverride -equals 1es-windows-2022
+          symbolPublishingAdditionalParameters: '/p:PublishSpecialClrFiles=true'
+          dependsOn: PrepareSignedArtifacts
diff --git a/eng/pipelines/official/jobs/prepare-signed-artifacts.yml b/eng/pipelines/official/prepare-signed-artifacts.yml
similarity index 73%
rename from eng/pipelines/official/jobs/prepare-signed-artifacts.yml
rename to eng/pipelines/official/prepare-signed-artifacts.yml
index 1440a7a1dfdc35..ab28ff29899acb 100644
--- a/eng/pipelines/official/jobs/prepare-signed-artifacts.yml
+++ b/eng/pipelines/official/prepare-signed-artifacts.yml
@@ -1,7 +1,9 @@
 parameters:
-  PublishRidAgnosticPackagesFromPlatform: ''
-  isOfficialBuild: false
-  logArtifactName: 'Logs-PrepareSignedArtifacts_Attempt$(System.JobAttempt)'
+  - name: logArtifactName
+    type: string
+    default: 'Logs-PrepareSignedArtifacts_Attempt$(System.JobAttempt)'
+  - name: buildStage
+    type: stage
 
 jobs:
 - template: /eng/common/templates-official/job/job.yml
@@ -31,9 +33,15 @@ jobs:
         repository: self
         clean: true
         fetchDepth: 20
-      - input: pipelineArtifact
-        artifactName: IntermediateArtifacts
-        targetPath: $(Build.SourcesDirectory)\artifacts\PackageDownload\IntermediateArtifacts
+      - ${{ each job in parameters.buildStage.jobs }}:
+        # Source Build publishes itself, so we don't need to publish it
+        - ${{ if notIn(job.job, 'Source_Build_Linux_x64')}}:
+          - input: pipelineArtifact
+            artifactName: ${{ job.job }}_Artifacts
+            targetPath: $(Build.SourcesDirectory)\artifacts
+            itemPattern: |
+              **/*
+              !_manifest/**/*
       outputs:
       - output: pipelineArtifact
         displayName: 'Publish BuildLogs'
@@ -45,12 +53,11 @@ jobs:
     - script: >-
         build.cmd -restore -sign -publish -ci -configuration Release
         /p:RestoreToolsetOnly=true
-        /p:PublishRidAgnosticPackagesFromPlatform=${{ parameters.PublishRidAgnosticPackagesFromPlatform }}
-        /p:DownloadDirectory=$(Build.SourcesDirectory)\artifacts\PackageDownload\
         /p:OfficialBuildId=$(Build.BuildNumber)
         /p:SignType=$(_SignType)
         /p:DotNetSignType=$(_SignType)
         /p:DotNetPublishUsingPipelines=true
+        /p:DotNetFinalPublish=true
         /bl:$(Build.SourcesDirectory)\prepare-artifacts.binlog
       displayName: Prepare artifacts and upload to build
     
diff --git a/eng/pipelines/official/stages/publish.yml b/eng/pipelines/official/stages/publish.yml
deleted file mode 100644
index 83b059ffc32a65..00000000000000
--- a/eng/pipelines/official/stages/publish.yml
+++ /dev/null
@@ -1,57 +0,0 @@
-parameters:
-  PublishRidAgnosticPackagesFromPlatform: windows_x64
-
-stages:
-
-- stage: PrepareForPublish
-  displayName: Prepare for Publish
-  variables:
-  - template: /eng/common/templates-official/variables/pool-providers.yml
-  jobs:
-  # Prep artifacts: sign them and upload pipeline artifacts expected by stages-based publishing.
-  - template: /eng/pipelines/official/jobs/prepare-signed-artifacts.yml
-    parameters:
-      PublishRidAgnosticPackagesFromPlatform: ${{ parameters.PublishRidAgnosticPackagesFromPlatform }}
-
-  # Publish to Build Asset Registry in order to generate the ReleaseConfigs artifact.
-  - template: /eng/common/templates-official/job/publish-build-assets.yml
-    parameters:
-      publishUsingPipelines: true
-      publishAssetsImmediately: true
-      dependsOn: PrepareSignedArtifacts
-      pool:
-        name: $(DncEngInternalBuildPool)
-        demands: ImageOverride -equals 1es-windows-2022
-      symbolPublishingAdditionalParameters: '/p:PublishSpecialClrFiles=true'
-
-# Stages-based publishing entry point
-- template: /eng/common/templates-official/post-build/post-build.yml
-  parameters:
-    validateDependsOn:
-    - PrepareForPublish
-    # The following checks are run after the build in the validation and release pipelines
-    # And thus are not enabled here. They can be enabled for dev builds for spot testing if desired
-    enableSymbolValidation: false
-    enableSigningValidation: false
-    enableNugetValidation: false
-    enableSourceLinkValidation: false
-    publishAssetsImmediately: true
-    SDLValidationParameters:
-      enable: false
-      artifactNames:
-      - PackageArtifacts
-      - BlobArtifacts
-      params: >-
-        -SourceToolsList @("policheck","credscan")
-        -TsaInstanceURL "$(TsaInstanceURL)"
-        -TsaProjectName "$(TsaProjectName)"
-        -TsaNotificationEmail "$(TsaNotificationEmail)"
-        -TsaCodebaseAdmin "$(TsaCodebaseAdmin)"
-        -TsaBugAreaPath "$(TsaBugAreaPath)"
-        -TsaIterationPath "$(TsaIterationPath)"
-        -TsaRepositoryName "$(TsaRepositoryName)"
-        -TsaCodebaseName "$(TsaCodebaseName)"
-        -TsaPublish $True
-    symbolPublishingAdditionalParameters: '/p:PublishSpecialClrFiles=true'
-    # Publish to blob storage.
-    publishInstallersAndChecksums: true
diff --git a/eng/pipelines/performance/perf-build.yml b/eng/pipelines/performance/perf-build.yml
new file mode 100644
index 00000000000000..de9a7674c15937
--- /dev/null
+++ b/eng/pipelines/performance/perf-build.yml
@@ -0,0 +1,236 @@
+parameters:
+- name: runPrivateJobs
+  displayName: Upload artifacts to blob storage
+  type: boolean
+  default: false
+- name: mauiFramework
+  type: string
+  default: 'net9.0'
+- name: coreclr_arm64_linux
+  displayName: Build Coreclr Arm64 Linux
+  type: boolean
+  default: true
+- name: coreclr_arm64_windows
+  displayName: Build Coreclr Arm64 Windows
+  type: boolean
+  default: true
+- name: coreclr_muslx64_linux
+  displayName: Build Coreclr Musl x64 Linux
+  type: boolean
+  default: true
+- name: coreclr_x64_linux
+  displayName: Build Coreclr x64 Linux
+  type: boolean
+  default: true
+- name: coreclr_x64_windows
+  displayName: Build Coreclr x64 Windows
+  type: boolean
+  default: true
+- name: coreclr_x86_windows
+  displayName: Build Coreclr x86 Windows
+  type: boolean
+  default: true
+- name: coreclr_arm64_android
+  displayName: Build Coreclr Arm64 Android
+  type: boolean
+  default: true
+- name: wasm
+  displayName: Build WebAssembly (wasm)
+  type: boolean
+  default: true
+- name: monoAot_arm64_linux
+  displayName: Build Mono AOT Arm64 Linux
+  type: boolean
+  default: true
+- name: monoAot_x64_linux
+  displayName: Build Mono AOT x64 Linux
+  type: boolean
+  default: true
+- name: mono_x64_linux
+  displayName: Build Mono x64 Linux
+  type: boolean
+  default: true
+- name: mono_arm64_linux
+  displayName: Build Mono Arm64 Linux
+  type: boolean
+  default: true
+- name: mono_arm64_android
+  displayName: Build Mono Arm64 Android
+  type: boolean
+  default: true
+- name: mono_arm64_ios
+  displayName: Build Mono Arm64 iOS
+  type: boolean
+  default: true
+- name: monoBDN_arm64_android
+  displayName: Build Mono Arm64 Android BDN (Not working)
+  type: boolean
+  default: false # currently not working
+- name: nativeAot_arm64_ios
+  displayName: Build native AOT Arm64 iOS
+  type: boolean
+  default: true
+
+trigger:
+  batch: false # we want to build every single commit
+  branches:
+    include:
+    - main
+    - release/9.0
+    - release/8.0
+  paths:
+    include:
+    - '*'
+    exclude:
+    - '**.md'
+    - .devcontainer/*
+    - .github/*
+    - docs/*
+    - LICENSE.TXT
+    - PATENTS.TXT
+    - THIRD-PARTY-NOTICES.TXT
+
+resources:
+  repositories:
+    - repository: performance
+      type: git
+      name: internal/dotnet-performance
+
+variables:
+  - template: /eng/pipelines/common/variables.yml
+
+extends:
+  template: /eng/pipelines/common/templates/pipeline-with-resources.yml
+  parameters:
+    stages:
+    - ${{ if and(ne(variables['System.TeamProject'], 'public'), or(eq(variables['Build.Reason'], 'IndividualCI'), parameters.runPrivateJobs)) }}:
+      - stage: RegisterBuild
+        displayName: 'Register Build'
+        jobs:
+        - template: /eng/pipelines/register-build-jobs.yml@performance
+          parameters:
+            runtimeRepoAlias: self
+            performanceRepoAlias: performance
+            buildType:
+              - ${{ if eq(parameters.coreclr_arm64_linux, true) }}:
+                - coreclr_arm64_linux
+              - ${{ if eq(parameters.coreclr_arm64_windows, true) }}:
+                - coreclr_arm64_windows
+              - ${{ if eq(parameters.coreclr_muslx64_linux, true) }}:
+                - coreclr_muslx64_linux
+              - ${{ if eq(parameters.coreclr_x64_linux, true) }}:
+                - coreclr_x64_linux
+              - ${{ if eq(parameters.coreclr_x64_windows, true) }}:
+                - coreclr_x64_windows
+              - ${{ if eq(parameters.coreclr_x86_windows, true) }}:
+                - coreclr_x86_windows
+              - ${{ if eq(parameters.coreclr_arm64_android, true) }}:
+                - coreclr_arm64_android
+              - ${{ if eq(parameters.wasm, true) }}:
+                - wasm
+              - ${{ if eq(parameters.monoAot_arm64_linux, true) }}:
+                - monoAot_arm64_linux
+              - ${{ if eq(parameters.monoAot_x64_linux, true) }}:
+                - monoAot_x64_linux
+              - ${{ if eq(parameters.mono_x64_linux, true) }}:
+                - mono_x64_linux
+              - ${{ if eq(parameters.mono_arm64_linux, true) }}:
+                - mono_arm64_linux
+              - ${{ if eq(parameters.mono_arm64_android, true) }}:
+                - mono_arm64_android
+              - ${{ if eq(parameters.mono_arm64_ios, true) }}:
+                - mono_arm64_ios
+              - ${{ if eq(parameters.monoBDN_arm64_android, true) }}:
+                - monoBDN_arm64_android
+              - ${{ if eq(parameters.nativeAot_arm64_ios, true) }}:
+                - nativeAot_arm64_ios
+
+    - stage: Build
+      displayName: 'Build'
+      dependsOn: [] # so it runs in parallel with the RegisterBuild stage
+      jobs:
+      - template: /eng/pipelines/runtime-perf-build-jobs.yml@performance
+        parameters:
+          runtimeRepoAlias: self
+          performanceRepoAlias: performance
+          buildType:
+            - ${{ if eq(parameters.coreclr_arm64_linux, true) }}:
+              - coreclr_arm64_linux
+            - ${{ if eq(parameters.coreclr_arm64_windows, true) }}:
+              - coreclr_arm64_windows
+            - ${{ if eq(parameters.coreclr_muslx64_linux, true) }}:
+              - coreclr_muslx64_linux
+            - ${{ if eq(parameters.coreclr_x64_linux, true) }}:
+              - coreclr_x64_linux
+            - ${{ if eq(parameters.coreclr_x64_windows, true) }}:
+              - coreclr_x64_windows
+            - ${{ if eq(parameters.coreclr_x86_windows, true) }}:
+              - coreclr_x86_windows
+            - ${{ if eq(parameters.coreclr_arm64_android, true) }}:
+              - coreclr_arm64_android
+            - ${{ if eq(parameters.wasm, true) }}:
+              - wasm
+            - ${{ if eq(parameters.monoAot_arm64_linux, true) }}:
+              - monoAot_arm64_linux
+            - ${{ if eq(parameters.monoAot_x64_linux, true) }}:
+              - monoAot_x64_linux
+            - ${{ if eq(parameters.mono_x64_linux, true) }}:
+              - mono_x64_linux
+            - ${{ if eq(parameters.mono_arm64_linux, true) }}:
+              - mono_arm64_linux
+            - ${{ if eq(parameters.mono_arm64_android, true) }}:
+              - mono_arm64_android
+            - ${{ if eq(parameters.mono_arm64_ios, true) }}:
+              - mono_arm64_ios
+            - ${{ if eq(parameters.monoBDN_arm64_android, true) }}:
+              - monoBDN_arm64_android
+            - ${{ if eq(parameters.nativeAot_arm64_ios, true) }}:
+              - nativeAot_arm64_ios
+          ${{ if parameters.mauiFramework }}:
+            mauiFramework: ${{ parameters.mauiFramework }}
+            
+    - ${{ if and(ne(variables['System.TeamProject'], 'public'), or(eq(variables['Build.Reason'], 'IndividualCI'), parameters.runPrivateJobs)) }}:
+      - stage: UploadArtifacts
+        displayName: 'Upload Artifacts'
+        condition: always()
+        dependsOn:
+          - Build
+          - RegisterBuild
+        jobs:
+        - template: /eng/pipelines/upload-build-artifacts-jobs.yml@performance
+          parameters:
+            runtimeRepoAlias: self
+            performanceRepoAlias: performance
+            buildType:
+              - ${{ if eq(parameters.coreclr_arm64_linux, true) }}:
+                - coreclr_arm64_linux
+              - ${{ if eq(parameters.coreclr_arm64_windows, true) }}:
+                - coreclr_arm64_windows
+              - ${{ if eq(parameters.coreclr_muslx64_linux, true) }}:
+                - coreclr_muslx64_linux
+              - ${{ if eq(parameters.coreclr_x64_linux, true) }}:
+                - coreclr_x64_linux
+              - ${{ if eq(parameters.coreclr_x64_windows, true) }}:
+                - coreclr_x64_windows
+              - ${{ if eq(parameters.coreclr_x86_windows, true) }}:
+                - coreclr_x86_windows
+              - ${{ if eq(parameters.coreclr_arm64_android, true) }}:
+                - coreclr_arm64_android
+              - ${{ if eq(parameters.wasm, true) }}:
+                - wasm
+              - ${{ if eq(parameters.monoAot_arm64_linux, true) }}:
+                - monoAot_arm64_linux
+              - ${{ if eq(parameters.monoAot_x64_linux, true) }}:
+                - monoAot_x64_linux
+              - ${{ if eq(parameters.mono_x64_linux, true) }}:
+                - mono_x64_linux
+              - ${{ if eq(parameters.mono_arm64_linux, true) }}:
+                - mono_arm64_linux
+              - ${{ if eq(parameters.mono_arm64_android, true) }}:
+                - mono_arm64_android
+              - ${{ if eq(parameters.mono_arm64_ios, true) }}:
+                - mono_arm64_ios
+              - ${{ if eq(parameters.monoBDN_arm64_android, true) }}:
+                - monoBDN_arm64_android
+              - ${{ if eq(parameters.nativeAot_arm64_ios, true) }}:
+                - nativeAot_arm64_ios
diff --git a/eng/pipelines/performance/perf-slow.yml b/eng/pipelines/performance/perf-slow.yml
index d30ecea46d79a5..84ca1836ff2321 100644
--- a/eng/pipelines/performance/perf-slow.yml
+++ b/eng/pipelines/performance/perf-slow.yml
@@ -15,10 +15,10 @@ trigger:
     include:
     - main
     - release/9.0
+    - release/8.0
   paths:
     include:
     - '*'
-    - src/libraries/System.Private.CoreLib/*
     exclude:
     - '**.md'
     - .devcontainer/*
@@ -62,4 +62,4 @@ extends:
             performanceRepoAlias: performance
             jobParameters:
               ${{ if parameters.onlySanityCheck }}:
-                onlySanityCheck: true
\ No newline at end of file
+                onlySanityCheck: true
diff --git a/eng/pipelines/performance/perf.yml b/eng/pipelines/performance/perf.yml
index e717fbe4915927..01b80b580db2bd 100644
--- a/eng/pipelines/performance/perf.yml
+++ b/eng/pipelines/performance/perf.yml
@@ -13,7 +13,6 @@ trigger:
   paths:
     include:
     - '*'
-    - src/libraries/System.Private.CoreLib/*
     exclude:
     - '**.md'
     - .devcontainer/*
diff --git a/eng/pipelines/performance/templates/perf-bdn-build-jobs.yml b/eng/pipelines/performance/templates/perf-bdn-build-jobs.yml
index 3355ceeedb9d2b..c6ffc983f8c175 100644
--- a/eng/pipelines/performance/templates/perf-bdn-build-jobs.yml
+++ b/eng/pipelines/performance/templates/perf-bdn-build-jobs.yml
@@ -18,7 +18,7 @@ jobs:
       - ios_arm64
       jobParameters:
         dependsOn:
-        - Build_android_arm64_release_Mono_Packs
+        - build_android_arm64_release_Mono_Packs
         buildArgs: -s mono -c $(_BuildConfig)
         nameSuffix: PerfBDNApp
         isOfficialBuild: false
diff --git a/eng/pipelines/performance/templates/perf-build-jobs.yml b/eng/pipelines/performance/templates/perf-build-jobs.yml
index 4a77a9796eaa75..4defede2528db2 100644
--- a/eng/pipelines/performance/templates/perf-build-jobs.yml
+++ b/eng/pipelines/performance/templates/perf-build-jobs.yml
@@ -9,7 +9,7 @@ jobs:
       windows_x64: true
       windows_x86: true
       linux_musl_x64: true
-      coreclrAndroid: true
+      android_arm64: true
 
   # build mono for AOT
   - template: /eng/pipelines/performance/templates/perf-mono-build-jobs.yml
diff --git a/eng/pipelines/performance/templates/perf-coreclr-build-jobs.yml b/eng/pipelines/performance/templates/perf-coreclr-build-jobs.yml
index 96792aab986a07..ddf103aaf4c0d9 100644
--- a/eng/pipelines/performance/templates/perf-coreclr-build-jobs.yml
+++ b/eng/pipelines/performance/templates/perf-coreclr-build-jobs.yml
@@ -1,11 +1,11 @@
 parameters:
     linux_x64: false
     linux_musl_x64: false
+    linux_arm64: false
     windows_x64: false
     windows_x86: false
-    linux_arm64: false
     windows_arm64: false
-    coreclrAndroid: false
+    android_arm64: false
 
 jobs:
   - ${{ if or(eq(parameters.linux_x64, true), eq(parameters.windows_x64, true), eq(parameters.windows_x86, true), eq(parameters.linux_musl_x64, true), eq(parameters.linux_arm64, true), eq(parameters.windows_arm64, true)) }}:
@@ -42,7 +42,7 @@ jobs:
               artifactName: BuildArtifacts_$(osGroup)$(osSubgroup)_$(archType)_$(_BuildConfig)_coreclr
               displayName: Build Assets
 
-  - ${{ if eq(parameters.coreclrAndroid, true) }}:
+  - ${{ if eq(parameters.android_arm64, true) }}:
     # build CoreCLR Android scenarios
     - template: /eng/pipelines/common/platform-matrix.yml
       parameters:
diff --git a/eng/pipelines/performance/templates/perf-ios-scenarios-build-jobs.yml b/eng/pipelines/performance/templates/perf-ios-scenarios-build-jobs.yml
index 10b2d79a8d03bc..85cff56fdc4c62 100644
--- a/eng/pipelines/performance/templates/perf-ios-scenarios-build-jobs.yml
+++ b/eng/pipelines/performance/templates/perf-ios-scenarios-build-jobs.yml
@@ -1,7 +1,7 @@
 parameters:
   hybridGlobalization: true
-  mono: true
-  nativeAot: true
+  mono: false
+  nativeAot: false
 
 jobs:
   - ${{ if eq(parameters.mono, true) }}:
diff --git a/eng/pipelines/runtime-diagnostics.yml b/eng/pipelines/runtime-diagnostics.yml
index 3f37ef73ef9e7d..2a4982442f198d 100644
--- a/eng/pipelines/runtime-diagnostics.yml
+++ b/eng/pipelines/runtime-diagnostics.yml
@@ -1,13 +1,5 @@
 trigger: none
 
-schedules:
-  - cron: "0 10 * * *" # run at 10:00 (UTC) which is 2:00 (PST).
-    displayName: Runtime Diagnostics scheduled run
-    branches:
-      include:
-      - main
-    always: true
-
 resources:
   repositories:
     - repository: diagnostics
@@ -31,4 +23,62 @@ extends:
   parameters:
     stages:
     - stage: Build
-      jobs:
\ No newline at end of file
+      jobs:
+      - template: /eng/pipelines/common/platform-matrix.yml
+        parameters:
+          jobTemplate: /eng/pipelines/common/global-build-job.yml
+          buildConfig: release
+          platforms:
+          - windows_x64
+          jobParameters:
+            buildArgs: -s clr+libs+tools.cdacreader+host+packs -c $(_BuildConfig)
+            nameSuffix: AllSubsets_CoreCLR
+            isOfficialBuild: ${{ variables.isOfficialBuild }}
+            timeoutInMinutes: 360
+            postBuildSteps:
+            - powershell: |
+                $versionDir = Get-ChildItem -Directory -Path "$(Build.SourcesDirectory)\artifacts\bin\testhost\net*\shared\Microsoft.NETCore.App" | Select-Object -ExpandProperty FullName
+                Write-Host "##vso[task.setvariable variable=versionDir]$versionDir"
+              displayName: 'Set Path to Shared Framework Artifacts'
+            - template: /eng/pipelines/common/upload-artifact-step.yml
+              parameters:
+                rootFolder: $(versionDir)
+                includeRootFolder: false
+                archiveType: $(archiveType)
+                archiveExtension: $(archiveExtension)
+                tarCompression: $(tarCompression)
+                artifactName: BuildArtifacts_$(osGroup)$(osSubgroup)_$(archType)_$(_BuildConfig)_coreclr
+                displayName: Build Assets            
+      - template: /eng/pipelines/common/platform-matrix.yml
+        parameters:
+          jobTemplate: /eng/pipelines/diagnostics/runtime-diag-job.yml
+          buildConfig: release
+          platforms:
+          - windows_x64
+          jobParameters:
+            name: Windows
+            isOfficialBuild: ${{ variables.isOfficialBuild }}
+            liveRuntimeDir: $(Build.SourcesDirectory)/artifacts/runtime
+            timeoutInMinutes: 360
+            dependsOn: 
+            - build_windows_x64_release_AllSubsets_CoreCLR
+            preBuildSteps:
+              - template: /eng/pipelines/common/download-artifact-step.yml
+                parameters:
+                  artifactName: BuildArtifacts_$(osGroup)$(osSubgroup)_$(archType)_$(_BuildConfig)_coreclr
+                  artifactFileName: BuildArtifacts_$(osGroup)$(osSubgroup)_$(archType)_$(_BuildConfig)_coreclr$(archiveExtension)
+                  unpackFolder: $(Build.SourcesDirectory)/artifacts/runtime
+                  displayName: 'Runtime Build Artifacts'
+            postBuildSteps:
+            - task: PublishTestResults@2
+              inputs:
+                testResultsFormat: xUnit
+                testResultsFiles: '**/*.xml'
+                searchFolder: '$(Build.SourcesDirectory)/artifacts/TestResults'
+                testRunTitle: 'Tests $(_PhaseName)'
+                failTaskOnFailedTests: true
+                publishRunAttachments: true
+                mergeTestResults: true
+                buildConfiguration: $(_BuildConfig)
+              continueOnError: true
+              condition: always()
\ No newline at end of file
diff --git a/eng/pipelines/runtime-official.yml b/eng/pipelines/runtime-official.yml
index 7b3afe0f222315..488464ae2d38e5 100644
--- a/eng/pipelines/runtime-official.yml
+++ b/eng/pipelines/runtime-official.yml
@@ -30,32 +30,36 @@ variables:
     teamName: dotnet-core-acquisition
 
 extends:
-  template:  /eng/pipelines/common/templates/pipeline-with-resources.yml
+  template:  /eng/pipelines/official/pipeline.yml
   parameters:
-    isOfficialBuild: true
-    stages:
-    - stage: Build
-      jobs:
-
-      #
-      # Localization build
-      #
-      - ${{ if eq(variables['Build.SourceBranch'], 'refs/heads/main') }}:
+    otherStages:
+    - ${{ if eq(variables['Build.SourceBranch'], 'refs/heads/main') }}:
+      - stage: Localization
+        dependsOn: []
+        jobs:
+        #
+        # Localization build
+        #
         - template: /eng/common/templates-official/job/onelocbuild.yml
           parameters:
             MirrorRepo: runtime
             MirrorBranch: main
             LclSource: lclFilesfromPackage
             LclPackageId: 'LCL-JUNO-PROD-RUNTIME'
-
-      #
-      # Source Index Build
-      #
-      - ${{ if eq(variables['Build.SourceBranch'], 'refs/heads/main') }}:
-        - template: /eng/common/templates-official/job/source-index-stage1.yml
-          parameters:
-            sourceIndexBuildCommand: build.cmd -subset libs.sfx+libs.oob -binarylog -os linux -ci /p:SkipLibrariesNativeRuntimePackages=true
-
+      - stage: Source_Index
+        dependsOn: []
+        displayName: Source Index
+        jobs:
+        #
+        # Source Index Build
+        #
+          - template: /eng/common/templates-official/job/source-index-stage1.yml
+            parameters:
+              sourceIndexBuildCommand: build.cmd -subset libs.sfx+libs.oob -binarylog -os linux -ci /p:SkipLibrariesNativeRuntimePackages=true
+    buildStage:
+      stage: Build
+      dependsOn: []
+      jobs:
       #
       # Build CoreCLR runtime packs
       # Windows x64/arm64
@@ -72,31 +76,28 @@ extends:
           variables:
             - name: _SignDiagnosticFilesArgs
               value: ''
+            - name: _EnableDefaultArtifactsArg
+              value: $[iif(and(eq(variables.osGroup, 'windows'), eq(variables.archType, 'x64')),'/p:EnableDefaultRidSpecificArtifacts=false','')]
           jobParameters:
             templatePath: 'templates-official'
             preBuildSteps:
             - template: /eng/pipelines/coreclr/templates/install-diagnostic-certs.yml
               parameters:
-                isOfficialBuild: ${{ variables.isOfficialBuild }}
+                isOfficialBuild: true
                 certNames:
                 - 'dotnetesrp-diagnostics-aad-ssl-cert'
                 - 'dotnet-diagnostics-esrp-pki-onecert'
                 vaultName: 'clrdiag-esrp-id'
                 azureSubscription: 'diagnostics-esrp-kvcertuser'
 
-            buildArgs: -c $(_BuildConfig) /p:DotNetBuildAllRuntimePacks=true $(_SignDiagnosticFilesArgs)
+            buildArgs: -c $(_BuildConfig) -restore -build -publish /p:DotNetBuildAllRuntimePacks=true $(_SignDiagnosticFilesArgs) $(_EnableDefaultArtifactsArg)
             nameSuffix: AllRuntimes
-            isOfficialBuild: ${{ variables.isOfficialBuild }}
+            isOfficialBuild: true
             timeoutInMinutes: 120
             postBuildSteps:
             - template: /eng/pipelines/coreclr/templates/remove-diagnostic-certs.yml
               parameters:
-                isOfficialBuild: ${{ variables.isOfficialBuild }}
-
-            # Upload the results.
-            - template: /eng/pipelines/common/upload-intermediate-artifacts-step.yml
-              parameters:
-                name: $(osGroup)$(osSubgroup)_$(archType)
+                isOfficialBuild: true
 
       #
       # Build all runtime packs
@@ -112,11 +113,16 @@ extends:
           - osx_x64
           jobParameters:
             templatePath: 'templates-official'
-            buildArgs: -s clr.runtime+clr.alljits+clr.nativeaotruntime+host.native -c $(_BuildConfig) /bl:$(Build.SourcesDirectory)/artifacts/logs/$(_BuildConfig)/CoreClrNativeBuild.binlog
+            buildArgs: -s clr.corelib+clr.nativecorelib+clr.nativeaotlibs+clr.tools+clr.packages+mono+libs+host.tools+host.pkg+packs -restore -build -publish -c $(_BuildConfig) /p:DotNetBuildAllRuntimePacks=true
             nameSuffix: AllRuntimes
             isOfficialBuild: ${{ variables.isOfficialBuild }}
             timeoutInMinutes: 120
-            postBuildSteps:
+            preBuildSteps:
+              # Build our native assets first so we can sign them.
+              - template: /eng/pipelines/common/templates/global-build-step.yml
+                parameters:
+                  buildArgs: -s clr.runtime+clr.alljits+clr.nativeaotruntime+host.native -c $(_BuildConfig) /bl:$(Build.SourcesDirectory)/artifacts/logs/$(_BuildConfig)/CoreClrNativeBuild.binlog
+                  displayName: Build native CoreCLR and host components
               - ${{ if and(ne(variables['System.TeamProject'], 'public'), notin(variables['Build.Reason'], 'PullRequest')) }}:
                 - template: /eng/pipelines/common/macos-sign-with-entitlements.yml
                   parameters:
@@ -130,27 +136,16 @@ extends:
                     - name: apphost
                       path: $(Build.SourcesDirectory)/artifacts/bin/$(osGroup)-$(archType).$(_BuildConfig)/corehost
 
-              - task: CopyFiles@2
-                displayName: 'Copy signed createdump to sharedFramework'
-                inputs:
-                  contents: createdump
-                  sourceFolder: $(Build.SourcesDirectory)/artifacts/bin/coreclr/$(osGroup).$(archType).$(_BuildConfig)
-                  targetFolder: $(Build.SourcesDirectory)/artifacts/bin/coreclr/$(osGroup).$(archType).$(_BuildConfig)/sharedFramework
-                  overWrite: true
-
-              # Now that we've entitled and signed createdump, we can build the rest.
-              - template: /eng/pipelines/common/templates/global-build-step.yml
-                parameters:
-                  buildArgs: -s clr.corelib+clr.nativecorelib+clr.nativeaotlibs+clr.tools+clr.packages+mono+libs+host.tools+host.pkg+packs -c $(_BuildConfig) /p:DotNetBuildAllRuntimePacks=true
-                  displayName: Build managed CoreCLR and host components, Mono, all libraries, and packs
-
-              # Upload the results.
-              - template: /eng/pipelines/common/upload-intermediate-artifacts-step.yml
-                parameters:
-                  name: $(osGroup)$(osSubgroup)_$(archType)
+                - task: CopyFiles@2
+                  displayName: 'Copy signed createdump to sharedFramework'
+                  inputs:
+                    contents: createdump
+                    sourceFolder: $(Build.SourcesDirectory)/artifacts/bin/coreclr/$(osGroup).$(archType).$(_BuildConfig)
+                    targetFolder: $(Build.SourcesDirectory)/artifacts/bin/coreclr/$(osGroup).$(archType).$(_BuildConfig)/sharedFramework
+                    overWrite: true
 
       #
-      # Build all runtime packs for Linux and Linux musl
+      # Build all runtime packs for Linux, Linux musl, and mobile
       #
       - template: /eng/pipelines/common/platform-matrix.yml
         parameters:
@@ -163,17 +158,29 @@ extends:
           - linux_musl_x64
           - linux_musl_arm
           - linux_musl_arm64
+          - android_x64
+          - android_x86
+          - android_arm
+          - android_arm64
+          - maccatalyst_x64
+          - maccatalyst_arm64
+          - tvossimulator_x64
+          - tvossimulator_arm64
+          - tvos_arm64
+          - iossimulator_x64
+          - iossimulator_arm64
+          - ios_arm64
+          - linux_bionic_x64
+          - linux_bionic_arm
+          - linux_bionic_arm64
+          - browser_wasm
+          - wasi_wasm
           jobParameters:
             templatePath: 'templates-official'
-            buildArgs: -c $(_BuildConfig) /p:DotNetBuildAllRuntimePacks=true
+            buildArgs: -c $(_BuildConfig) -restore -build -publish /p:DotNetBuildAllRuntimePacks=true
             nameSuffix: AllRuntimes
-            isOfficialBuild: ${{ variables.isOfficialBuild }}
+            isOfficialBuild: true
             timeoutInMinutes: 120
-            postBuildSteps:
-              # Upload the results.
-              - template: /eng/pipelines/common/upload-intermediate-artifacts-step.yml
-                parameters:
-                  name: $(osGroup)$(osSubgroup)_$(archType)
 
       #
       # Build and Pack CrossDac
@@ -186,24 +193,54 @@ extends:
           - windows_x64
           jobParameters:
             templatePath: 'templates-official'
-            buildArgs: -s crossdacpack -c $(_BuildConfig) /p:CrossRuntimeExtractionRoot=$(CrossRuntimeExtractionRoot) $(_SignDiagnosticFilesArgs)
+            buildArgs: -s crossdacpack -restore -build -publish -c $(_BuildConfig) /p:EnableDefaultRidSpecificArtifacts=false /p:CrossRuntimeExtractionRoot=$(CrossRuntimeExtractionRoot) $(_SignDiagnosticFilesArgs)
             nameSuffix: CrossDac
-            isOfficialBuild: ${{ variables.isOfficialBuild }}
+            isOfficialBuild: true
             timeoutInMinutes: 120
-            preBuildSteps:
-            - task: DownloadPipelineArtifact@2
-              displayName: Download runtime packs for CrossDac
+            templateContext:
               inputs:
-                artifact: 'IntermediateArtifacts'
-                path: $(Build.SourcesDirectory)/artifacts/RuntimeDownload
-                patterns: |
-                  IntermediateArtifacts/linux_*/Shipping/Microsoft.NETCore.App.Runtime.linux-*.nupkg
-                  !IntermediateArtifacts/linux_*/Shipping/Microsoft.NETCore.App.Runtime.linux-*.symbols.nupkg
-            - powershell: $(Build.SourcesDirectory)/eng/extract-for-crossdac.ps1 -DownloadDirectory $(Build.SourcesDirectory)/artifacts/RuntimeDownload -ExtractDirectory $(CrossRuntimeExtractionRoot)
+              - input: pipelineArtifact
+                artifactName: Build_linux_x64_release_AllRuntimes_Artifacts
+                targetPath: $(Build.ArtifactStagingDirectory)/artifacts/RuntimeDownload
+                itemPattern: |
+                  packages/Release/Shipping/Microsoft.NETCore.App.Runtime.linux-x64.*.nupkg
+                  !packages/Release/Shipping/Microsoft.NETCore.App.Runtime.linux-x64.*.symbols.nupkg
+              - input: pipelineArtifact
+                artifactName: Build_linux_arm64_release_AllRuntimes_Artifacts
+                targetPath: $(Build.ArtifactStagingDirectory)/artifacts/RuntimeDownload
+                itemPattern: |
+                  packages/Release/Shipping/Microsoft.NETCore.App.Runtime.linux-arm64.*.nupkg
+                  !packages/Release/Shipping/Microsoft.NETCore.App.Runtime.linux-arm64.*.symbols.nupkg
+              - input: pipelineArtifact
+                artifactName: Build_linux_musl_x64_release_AllRuntimes_Artifacts
+                targetPath: $(Build.ArtifactStagingDirectory)/artifacts/RuntimeDownload
+                itemPattern: |
+                  packages/Release/Shipping/Microsoft.NETCore.App.Runtime.linux-musl-x64.*.nupkg
+                  !packages/Release/Shipping/Microsoft.NETCore.App.Runtime.linux-musl-x64.*.symbols.nupkg
+              - input: pipelineArtifact
+                artifactName: Build_linux_musl_arm64_release_AllRuntimes_Artifacts
+                targetPath: $(Build.ArtifactStagingDirectory)/artifacts/RuntimeDownload
+                itemPattern: |
+                  packages/Release/Shipping/Microsoft.NETCore.App.Runtime.linux-musl-arm64.*.nupkg
+                  !packages/Release/Shipping/Microsoft.NETCore.App.Runtime.linux-musl-arm64.*.symbols.nupkg
+              - input: pipelineArtifact
+                artifactName: Build_linux_arm_release_AllRuntimes_Artifacts
+                targetPath: $(Build.ArtifactStagingDirectory)/artifacts/RuntimeDownload
+                itemPattern: |
+                  packages/Release/Shipping/Microsoft.NETCore.App.Runtime.linux-arm.*.nupkg
+                  !packages/Release/Shipping/Microsoft.NETCore.App.Runtime.linux-arm.*.symbols.nupkg
+              - input: pipelineArtifact
+                artifactName: Build_linux_musl_arm_release_AllRuntimes_Artifacts
+                targetPath: $(Build.ArtifactStagingDirectory)/artifacts/RuntimeDownload
+                itemPattern: |
+                  packages/Release/Shipping/Microsoft.NETCore.App.Runtime.linux-musl-arm.*.nupkg
+                  !packages/Release/Shipping/Microsoft.NETCore.App.Runtime.linux-musl-arm.*.symbols.nupkg
+            preBuildSteps:
+            - powershell: $(Build.SourcesDirectory)/eng/extract-for-crossdac.ps1 -DownloadDirectory $(Build.ArtifactStagingDirectory)/artifacts/RuntimeDownload -ExtractDirectory $(CrossRuntimeExtractionRoot)
               displayName: Extract runtime packs
             - template: /eng/pipelines/coreclr/templates/install-diagnostic-certs.yml
               parameters:
-                isOfficialBuild: ${{ variables.isOfficialBuild }}
+                isOfficialBuild: true
                 certNames:
                 - 'dotnetesrp-diagnostics-aad-ssl-cert'
                 - 'dotnet-diagnostics-esrp-pki-onecert'
@@ -212,12 +249,7 @@ extends:
             postBuildSteps:
             - template: /eng/pipelines/coreclr/templates/remove-diagnostic-certs.yml
               parameters:
-                isOfficialBuild: ${{ variables.isOfficialBuild }}
-            # Save packages using the prepare-signed-artifacts format.
-            # CrossDac packages are expected to be in the windows_x64 folder.
-            - template: /eng/pipelines/common/upload-intermediate-artifacts-step.yml
-              parameters:
-                name: windows_x64
+                isOfficialBuild: true
             dependsOn:
             - build_linux_x64_release_AllRuntimes
             - build_linux_arm_release_AllRuntimes
@@ -231,73 +263,6 @@ extends:
           - name: _SignDiagnosticFilesArgs
             value: ''
 
-      #
-      # Build All runtime packs for mobile platforms
-      #
-      - template: /eng/pipelines/common/platform-matrix.yml
-        parameters:
-          jobTemplate: /eng/pipelines/common/global-build-job.yml
-          buildConfig: release
-          platforms:
-          - android_x64
-          - android_x86
-          - android_arm
-          - android_arm64
-          - maccatalyst_x64
-          - maccatalyst_arm64
-          - tvossimulator_x64
-          - tvossimulator_arm64
-          - tvos_arm64
-          - iossimulator_x64
-          - iossimulator_arm64
-          - ios_arm64
-          - linux_bionic_x64
-          - linux_bionic_arm
-          - linux_bionic_arm64
-          jobParameters:
-            templatePath: 'templates-official'
-            buildArgs: -c $(_BuildConfig) /p:BuildMonoAOTCrossCompiler=false /p:DotNetBuildAllRuntimePacks=true
-            nameSuffix: AllRuntimes
-            isOfficialBuild: ${{ variables.isOfficialBuild }}
-            postBuildSteps:
-              # delete duplicate RIDless packages to prevent upload conflict
-              - task: DeleteFiles@1
-                displayName: 'Delete Microsoft.NETCore.App.Ref and Microsoft.NETCore.App.HostModel package'
-                inputs:
-                  SourceFolder: $(Build.SourcesDirectory)/artifacts/packages/$(_BuildConfig)/Shipping
-                  Contents: |
-                    'Microsoft.NETCore.App.Ref.*.nupkg'
-                    'Microsoft.NET.HostModel.*.nupkg'
-              - template: /eng/pipelines/common/upload-intermediate-artifacts-step.yml
-                parameters:
-                  name: MobileRuntimePacks
-
-      - template: /eng/pipelines/common/platform-matrix.yml
-        parameters:
-          jobTemplate: /eng/pipelines/common/global-build-job.yml
-          buildConfig: release
-          runtimeFlavor: mono
-          platforms:
-          - browser_wasm
-          - wasi_wasm
-          jobParameters:
-            templatePath: 'templates-official'
-            buildArgs: -s mono+libs+host+packs -c $(_BuildConfig) /p:AotHostArchitecture=x64 /p:AotHostOS=$(_hostedOS)
-            nameSuffix: Mono
-            isOfficialBuild: ${{ variables.isOfficialBuild }}
-            postBuildSteps:
-              # delete duplicate RIDless packages to prevent upload conflict
-              - task: DeleteFiles@1
-                displayName: 'Delete Microsoft.NETCore.App.Ref and Microsoft.NETCore.App.HostModel package'
-                inputs:
-                  SourceFolder: $(Build.SourcesDirectory)/artifacts/packages/$(_BuildConfig)/Shipping
-                  Contents: |
-                    'Microsoft.NETCore.App.Ref.*.nupkg'
-                    'Microsoft.NET.HostModel.*.nupkg'
-              - template: /eng/pipelines/common/upload-intermediate-artifacts-step.yml
-                parameters:
-                  name: MobileRuntimePacks
-
       - template: /eng/pipelines/common/platform-matrix.yml
         parameters:
           jobTemplate: /eng/pipelines/common/global-build-job.yml
@@ -307,22 +272,10 @@ extends:
           - browser_wasm
           jobParameters:
             templatePath: 'templates-official'
-            buildArgs: -s mono+libs+host+packs -c $(_BuildConfig) /p:WasmEnableThreads=true /p:AotHostArchitecture=x64 /p:AotHostOS=$(_hostedOS)
+            buildArgs: -c $(_BuildConfig) -restore -build -publish /p:DotNetBuildAllRuntimePacks=true /p:WasmEnableThreads=true
             nameSuffix: Mono_multithread
-            isOfficialBuild: ${{ variables.isOfficialBuild }}
+            isOfficialBuild: true
             runtimeVariant: multithread
-            postBuildSteps:
-              # delete duplicate RIDless packages to prevent upload conflict
-              - task: DeleteFiles@1
-                displayName: 'Delete Microsoft.NETCore.App.Ref and Microsoft.NETCore.App.HostModel package'
-                inputs:
-                  SourceFolder: $(Build.SourcesDirectory)/artifacts/packages/$(_BuildConfig)/Shipping
-                  Contents: |
-                    'Microsoft.NETCore.App.Ref.*.nupkg'
-                    'Microsoft.NET.HostModel.*.nupkg'
-              - template: /eng/pipelines/common/upload-intermediate-artifacts-step.yml
-                parameters:
-                  name: MobileRuntimePacks
 
       #
       # Build Mono LLVM runtime packs
@@ -338,23 +291,11 @@ extends:
           runtimeFlavor: mono
           jobParameters:
             templatePath: 'templates-official'
-            buildArgs: -s mono+libs+host+packs -c $(_BuildConfig)
+            buildArgs: -s mono+libs+host+packs -c $(_BuildConfig) -restore -build -publish
                         /p:MonoEnableLLVM=true /p:MonoAOTEnableLLVM=true /p:MonoBundleLLVMOptimizer=true
             nameSuffix: Mono_LLVMAOT
             runtimeVariant: LLVMAOT
-            isOfficialBuild: ${{ variables.isOfficialBuild }}
-            postBuildSteps:
-              # delete duplicate RIDless packages to prevent upload conflict
-              - task: DeleteFiles@1
-                displayName: 'Delete Microsoft.NETCore.App.Ref and Microsoft.NETCore.App.HostModel package'
-                inputs:
-                  SourceFolder: $(Build.SourcesDirectory)/artifacts/packages/$(_BuildConfig)/Shipping
-                  Contents: |
-                    'Microsoft.NETCore.App.Ref.*.nupkg'
-                    'Microsoft.NET.HostModel.*.nupkg'
-              - template: /eng/pipelines/common/upload-intermediate-artifacts-step.yml
-                parameters:
-                  name: $(osGroup)$(osSubgroup)_$(archType)
+            isOfficialBuild: true
 
       #
       # Build libraries (all TFMs) and packages
@@ -367,13 +308,9 @@ extends:
           - windows_x64
           jobParameters:
             templatePath: 'templates-official'
-            buildArgs: -s tools.illink+libs -pack -c $(_BuildConfig) /p:TestAssemblies=false /p:TestPackages=true
+            buildArgs: -s tools.illink+libs -restore -build -pack -publish -c $(_BuildConfig) /p:TestAssemblies=false /p:TestPackages=true /p:EnableDefaultRidSpecificArtifacts=false
             nameSuffix: Libraries_WithPackages
-            isOfficialBuild: ${{ variables.isOfficialBuild }}
-            postBuildSteps:
-              - template: /eng/pipelines/common/upload-intermediate-artifacts-step.yml
-                parameters:
-                  name: Libraries_WithPackages
+            isOfficialBuild: true
             timeoutInMinutes: 95
       #
       # Build SourceBuild packages
@@ -401,13 +338,9 @@ extends:
           - linux_arm64
           jobParameters:
             templatePath: 'templates-official'
-            buildArgs: -s clr.native+clr.corelib+clr.tools+clr.nativecorelib+libs+host+packs -c $(_BuildConfig) -pgoinstrument /p:SkipLibrariesNativeRuntimePackages=true
-            isOfficialBuild: ${{ variables.isOfficialBuild }}
+            buildArgs: -s clr.native+clr.corelib+clr.tools+clr.nativecorelib+libs+host+packs -c $(_BuildConfig) -restore -build -publish -pgoinstrument /p:SkipLibrariesNativeRuntimePackages=true
+            isOfficialBuild: true
             nameSuffix: PGO
-            postBuildSteps:
-              - template: /eng/pipelines/common/upload-intermediate-artifacts-step.yml
-                parameters:
-                  name: PGO
             timeoutInMinutes: 95
 
       #
@@ -422,96 +355,137 @@ extends:
           jobParameters:
             templatePath: 'templates-official'
             nameSuffix: Workloads
-            preBuildSteps:
-            - task: DownloadPipelineArtifact@2
+            templateContext:
               inputs:
-                artifact: 'IntermediateArtifacts'
-                path: $(Build.SourcesDirectory)/artifacts/workloadPackages
-                patterns: |
-                  IntermediateArtifacts/windows_x64/Shipping/Microsoft.NETCore.App.Runtime.AOT.win-x64.Cross.android-*.nupkg
-                  IntermediateArtifacts/windows_arm64/Shipping/Microsoft.NETCore.App.Runtime.AOT.win-arm64.Cross.android-*.nupkg
-                  IntermediateArtifacts/windows_x64/Shipping/Microsoft.NETCore.App.Runtime.AOT.win-x64.Cross.browser-wasm*.nupkg
-                  IntermediateArtifacts/windows_arm64/Shipping/Microsoft.NETCore.App.Runtime.AOT.win-arm64.Cross.browser-wasm*.nupkg
-                  IntermediateArtifacts/windows_x64/Shipping/Microsoft.NETCore.App.Runtime.AOT.win-x64.Cross.wasi-wasm*.nupkg
-                  IntermediateArtifacts/windows_arm64/Shipping/Microsoft.NETCore.App.Runtime.AOT.win-arm64.Cross.wasi-wasm*.nupkg
-                  IntermediateArtifacts/MobileRuntimePacks/Shipping/Microsoft.NETCore.App.Runtime.Mono.android-*.nupkg
-                  IntermediateArtifacts/MobileRuntimePacks/Shipping/Microsoft.NETCore.App.Runtime.Mono.browser-wasm*.nupkg
-                  IntermediateArtifacts/MobileRuntimePacks/Shipping/Microsoft.NETCore.App.Runtime.Mono.multithread.browser-wasm*.nupkg
-                  IntermediateArtifacts/MobileRuntimePacks/Shipping/Microsoft.NETCore.App.Runtime.Mono.ios-*.nupkg
-                  IntermediateArtifacts/MobileRuntimePacks/Shipping/Microsoft.NETCore.App.Runtime.Mono.iossimulator-*.nupkg
-                  IntermediateArtifacts/MobileRuntimePacks/Shipping/Microsoft.NETCore.App.Runtime.Mono.maccatalyst-*.nupkg
-                  IntermediateArtifacts/MobileRuntimePacks/Shipping/Microsoft.NETCore.App.Runtime.Mono.multithread.browser-wasm*.nupkg
-                  IntermediateArtifacts/MobileRuntimePacks/Shipping/Microsoft.NETCore.App.Runtime.Mono.tvos-*.nupkg
-                  IntermediateArtifacts/MobileRuntimePacks/Shipping/Microsoft.NETCore.App.Runtime.Mono.tvossimulator-*.nupkg
-                  IntermediateArtifacts/MobileRuntimePacks/Shipping/Microsoft.NETCore.App.Runtime.Mono.wasi-wasm*.nupkg
-                  IntermediateArtifacts/MobileRuntimePacks/Shipping/Microsoft.NET.Workload.Mono.ToolChain.Current.Manifest*.nupkg
-                  IntermediateArtifacts/MobileRuntimePacks/Shipping/Microsoft.NET.Workload.Mono.ToolChain.net6.Manifest*.nupkg
-                  IntermediateArtifacts/MobileRuntimePacks/Shipping/Microsoft.NET.Workload.Mono.ToolChain.net7.Manifest*.nupkg
-                  IntermediateArtifacts/MobileRuntimePacks/Shipping/Microsoft.NET.Workload.Mono.ToolChain.net8.Manifest*.nupkg
-                  IntermediateArtifacts/MobileRuntimePacks/Shipping/Microsoft.NET.Workload.Mono.ToolChain.net9.Manifest*.nupkg
-                  IntermediateArtifacts/MobileRuntimePacks/Shipping/Microsoft.NET.Runtime.MonoTargets.Sdk*.nupkg
-                  IntermediateArtifacts/MobileRuntimePacks/Shipping/Microsoft.NET.Runtime.MonoAOTCompiler.Task*.nupkg
-                  IntermediateArtifacts/MobileRuntimePacks/Shipping/Microsoft.NET.Runtime.WebAssembly.Sdk*.nupkg
-                  IntermediateArtifacts/MobileRuntimePacks/Shipping/Microsoft.NET.Runtime.WebAssembly.Wasi*.nupkg
-                  IntermediateArtifacts/MobileRuntimePacks/Shipping/Microsoft.NET.Runtime.WebAssembly.Templates*.nupkg
-                  IntermediateArtifacts/windows_arm64/Shipping/Microsoft.NETCore.App.Runtime.win-arm64*.nupkg
-                  IntermediateArtifacts/windows_x64/Shipping/Microsoft.NETCore.App.Runtime.win-x64*.nupkg
-                  IntermediateArtifacts/windows_x86/Shipping/Microsoft.NETCore.App.Runtime.win-x86*.nupkg
-                  IntermediateArtifacts/MobileRuntimePacks/Shipping/Microsoft.NET.Sdk.WebAssembly.Pack*.nupkg
+                - input: pipelineArtifact
+                  artifactName: Build_windows_x64_release_AllRuntimes_Artifacts
+                  targetPath: $(Build.ArtifactStagingDirectory)/artifacts/workloadPackages
+                  itemPattern: |
+                    **/Microsoft.NETCore.App.Runtime.AOT.win-x64.Cross.android-*.nupkg
+                    **/Microsoft.NETCore.App.Runtime.AOT.win-x64.Cross.browser-wasm*.nupkg
+                    **/Microsoft.NETCore.App.Runtime.AOT.win-x64.Cross.wasi-wasm*.nupkg
+                    **/Microsoft.NETCore.App.Runtime.win-x64*.nupkg
+                - input: pipelineArtifact
+                  artifactName: Build_windows_arm64_release_AllRuntimes_Artifacts
+                  targetPath: $(Build.ArtifactStagingDirectory)/artifacts/workloadPackages
+                  itemPattern: |
+                    **/Microsoft.NETCore.App.Runtime.AOT.win-arm64.Cross.android-*.nupkg
+                    **/Microsoft.NETCore.App.Runtime.AOT.win-arm64.Cross.browser-wasm*.nupkg
+                    **/Microsoft.NETCore.App.Runtime.AOT.win-arm64.Cross.wasi-wasm*.nupkg
+                    **/Microsoft.NETCore.App.Runtime.win-arm64*.nupkg
+                - input: pipelineArtifact
+                  artifactName: Build_windows_x86_release_AllRuntimes_Artifacts
+                  targetPath: $(Build.ArtifactStagingDirectory)/artifacts/workloadPackages
+                  itemPattern: |
+                    **/Microsoft.NETCore.App.Runtime.win-x86*.nupkg
+                - input: pipelineArtifact
+                  artifactName: Build_android_x64_release_AllRuntimes_Artifacts
+                  targetPath: $(Build.ArtifactStagingDirectory)/artifacts/workloadPackages
+                  itemPattern: |
+                    **/Microsoft.NETCore.App.Runtime.Mono.android-*.nupkg
+                - input: pipelineArtifact
+                  artifactName: Build_android_x86_release_AllRuntimes_Artifacts
+                  targetPath: $(Build.ArtifactStagingDirectory)/artifacts/workloadPackages
+                  itemPattern: |
+                    **/Microsoft.NETCore.App.Runtime.Mono.android-*.nupkg
+                - input: pipelineArtifact
+                  artifactName: Build_android_arm_release_AllRuntimes_Artifacts
+                  targetPath: $(Build.ArtifactStagingDirectory)/artifacts/workloadPackages
+                  itemPattern: |
+                    **/Microsoft.NETCore.App.Runtime.Mono.android-*.nupkg
+                - input: pipelineArtifact
+                  artifactName: Build_android_arm64_release_AllRuntimes_Artifacts
+                  targetPath: $(Build.ArtifactStagingDirectory)/artifacts/workloadPackages
+                  itemPattern: |
+                    **/Microsoft.NETCore.App.Runtime.Mono.android-*.nupkg
+                - input: pipelineArtifact
+                  artifactName: Build_browser_wasm_Linux_release_AllRuntimes_Artifacts
+                  targetPath: $(Build.ArtifactStagingDirectory)/artifacts/workloadPackages
+                  itemPattern: |
+                    **/Microsoft.NETCore.App.Runtime.Mono.browser-wasm*.nupkg
+                    **/Microsoft.NET.Workload.Mono.ToolChain.Current.Manifest*.nupkg
+                    **/Microsoft.NET.Workload.Mono.ToolChain.net6.Manifest*.nupkg
+                    **/Microsoft.NET.Workload.Mono.ToolChain.net7.Manifest*.nupkg
+                    **/Microsoft.NET.Workload.Mono.ToolChain.net8.Manifest*.nupkg
+                    **/Microsoft.NET.Workload.Mono.ToolChain.net9.Manifest*.nupkg
+                    **/Microsoft.NET.Runtime.WebAssembly.Sdk*.nupkg
+                    **/Microsoft.NET.Runtime.WebAssembly.Templates*.nupkg
+                    **/Microsoft.NET.Sdk.WebAssembly.Pack*.nupkg
+                - input: pipelineArtifact
+                  artifactName: build_browser_wasm_linux_release_Mono_multithread_Artifacts
+                  targetPath: $(Build.ArtifactStagingDirectory)/artifacts/workloadPackages
+                  itemPattern: |
+                    **/Microsoft.NETCore.App.Runtime.Mono.multithread.browser-wasm*.nupkg
+                - input: pipelineArtifact
+                  artifactName: Build_wasi_wasm_Linux_release_AllRuntimes_Artifacts
+                  targetPath: $(Build.ArtifactStagingDirectory)/artifacts/workloadPackages
+                  itemPattern: |
+                    **/Microsoft.NET.Runtime.WebAssembly.Wasi*.nupkg
+                    **/Microsoft.NETCore.App.Runtime.Mono.wasi-wasm*.nupkg
+                - input: pipelineArtifact
+                  artifactName: Build_ios_arm64_release_AllRuntimes_Artifacts
+                  targetPath: $(Build.ArtifactStagingDirectory)/artifacts/workloadPackages
+                  itemPattern: |
+                    **/Microsoft.NET.Runtime.MonoTargets.Sdk*.nupkg
+                    **/Microsoft.NET.Runtime.MonoAOTCompiler.Task*.nupkg
+                    **/Microsoft.NETCore.App.Runtime.Mono.ios-*.nupkg
+                - input: pipelineArtifact
+                  artifactName: Build_iossimulator_x64_release_AllRuntimes_Artifacts
+                  targetPath: $(Build.ArtifactStagingDirectory)/artifacts/workloadPackages
+                  itemPattern: |
+                    **/Microsoft.NETCore.App.Runtime.Mono.iossimulator-*.nupkg
+                - input: pipelineArtifact
+                  artifactName: Build_iossimulator_arm64_release_AllRuntimes_Artifacts
+                  targetPath: $(Build.ArtifactStagingDirectory)/artifacts/workloadPackages
+                  itemPattern: |
+                    **/Microsoft.NETCore.App.Runtime.Mono.iossimulator-*.nupkg
+                - input: pipelineArtifact
+                  artifactName: Build_maccatalyst_x64_release_AllRuntimes_Artifacts
+                  targetPath: $(Build.ArtifactStagingDirectory)/artifacts/workloadPackages
+                  itemPattern: |
+                    **/Microsoft.NETCore.App.Runtime.Mono.maccatalyst-*.nupkg
+                - input: pipelineArtifact
+                  artifactName: Build_maccatalyst_arm64_release_AllRuntimes_Artifacts
+                  targetPath: $(Build.ArtifactStagingDirectory)/artifacts/workloadPackages
+                  itemPattern: |
+                    **/Microsoft.NETCore.App.Runtime.Mono.maccatalyst-*.nupkg
+                - input: pipelineArtifact
+                  artifactName: Build_tvos_arm64_release_AllRuntimes_Artifacts
+                  targetPath: $(Build.ArtifactStagingDirectory)/artifacts/workloadPackages
+                  itemPattern: |
+                    **/Microsoft.NETCore.App.Runtime.Mono.tvos-*.nupkg
+                - input: pipelineArtifact
+                  artifactName: Build_tvossimulator_x64_release_AllRuntimes_Artifacts
+                  targetPath: $(Build.ArtifactStagingDirectory)/artifacts/workloadPackages
+                  itemPattern: |
+                    **/Microsoft.NETCore.App.Runtime.Mono.tvossimulator-*.nupkg
+                - input: pipelineArtifact
+                  artifactName: Build_tvossimulator_arm64_release_AllRuntimes_Artifacts
+                  targetPath: $(Build.ArtifactStagingDirectory)/artifacts/workloadPackages
+                  itemPattern: |
+                    **/Microsoft.NETCore.App.Runtime.Mono.tvossimulator-*.nupkg
 
+            preBuildSteps:
             - task: CopyFiles@2
               displayName: Flatten packages
               inputs:
-                sourceFolder: $(Build.SourcesDirectory)/artifacts/workloadPackages
-                contents: '*/Shipping/*.nupkg'
-                cleanTargetFolder: false
+                sourceFolder: $(Build.ArtifactStagingDirectory)/artifacts/workloadPackages
+                contents: 'packages/Release/Shipping/*.nupkg'
+                cleanTargetFolder: true
                 targetFolder: $(Build.SourcesDirectory)/artifacts/workloadPackages
                 flattenFolders: true
 
-            buildArgs: -s mono.workloads -c $(_BuildConfig) /p:PackageSource=$(Build.SourcesDirectory)/artifacts/workloadPackages /p:WorkloadOutputPath=$(Build.SourcesDirectory)/artifacts/workloads
-
-            postBuildSteps:
-            # Prepare packages wrapping msis
-            - task: CopyFiles@2
-              displayName: Prepare package artifacts
-              inputs:
-                SourceFolder: '$(Build.SourcesDirectory)/artifacts/packages/$(_BuildConfig)'
-                Contents: |
-                  Shipping/**/*
-                  NonShipping/**/*
-                TargetFolder: '$(Build.ArtifactStagingDirectory)/IntermediateArtifacts1/workloads'
-                CleanTargetFolder: true
-
-            # Prepare artifacts to be used for generating VS components
-            - task: CopyFiles@2
-              displayName: Prepare VS Insertion artifacts
-              inputs:
-                SourceFolder: '$(Build.SourcesDirectory)/artifacts/VSSetup/$(_BuildConfig)'
-                Contents: |
-                  Insertion/**/*
-                TargetFolder: '$(Build.ArtifactStagingDirectory)/IntermediateArtifacts2/workloads-vs'
-                CleanTargetFolder: true
-
-            templateContext:
-              outputs:
-              - output: buildArtifacts
-                PathtoPublish: '$(Build.ArtifactStagingDirectory)/IntermediateArtifacts1'
-                ArtifactName: IntermediateArtifacts
-                displayName: 'Publish workload packages'
-              - output: buildArtifacts
-                PathtoPublish: '$(Build.ArtifactStagingDirectory)/IntermediateArtifacts2'
-                ArtifactName: IntermediateArtifacts
-                displayName: 'Publish workload VS Insertion artifacts'
+            buildArgs: -s mono.workloads -c $(_BuildConfig) -restore -build -publish /p:PackageSource=$(Build.SourcesDirectory)/artifacts/workloadPackages /p:WorkloadOutputPath=$(Build.SourcesDirectory)/artifacts/workloads /p:ShouldGenerateProductVersionFiles=true /p:EnableDefaultRidSpecificArtifacts=false
 
-            isOfficialBuild: ${{ variables.isOfficialBuild }}
+            isOfficialBuild: true
             timeoutInMinutes: 120
             dependsOn:
             - Build_android_arm_release_AllRuntimes
             - Build_android_arm64_release_AllRuntimes
             - Build_android_x86_release_AllRuntimes
             - Build_android_x64_release_AllRuntimes
-            - Build_browser_wasm_Linux_release_Mono
-            - Build_wasi_wasm_linux_release_Mono
+            - Build_browser_wasm_Linux_release_AllRuntimes
+            - Build_wasi_wasm_linux_release_AllRuntimes
             - Build_ios_arm64_release_AllRuntimes
             - Build_iossimulator_x64_release_AllRuntimes
             - Build_iossimulator_arm64_release_AllRuntimes
@@ -523,8 +497,4 @@ extends:
             - Build_windows_x64_release_AllRuntimes
             - Build_windows_x86_release_AllRuntimes
             - Build_windows_arm64_release_AllRuntimes
-
-    - ${{ if eq(variables.isOfficialBuild, true) }}:
-      - template: /eng/pipelines/official/stages/publish.yml
-        parameters:
-          isOfficialBuild: ${{ variables.isOfficialBuild }}
+            - build_browser_wasm_linux_release_Mono_multithread
diff --git a/eng/pipelines/runtime.yml b/eng/pipelines/runtime.yml
index fe9c980fbb9354..48d65de310e92f 100644
--- a/eng/pipelines/runtime.yml
+++ b/eng/pipelines/runtime.yml
@@ -125,7 +125,7 @@ extends:
           - browser_wasm
           jobParameters:
             nameSuffix: AllSubsets_CoreCLR
-            buildArgs: -s mono.emsdk+clr.paltests -rc Release -c Release -lc $(_BuildConfig)
+            buildArgs: -s mono.emsdk+clr.runtime -rc Release -c Release -lc $(_BuildConfig)
             timeoutInMinutes: 120
             condition: >-
               or(
@@ -1018,7 +1018,7 @@ extends:
           jobParameters:
             testGroup: innerloop
             nameSuffix: AllSubsets_Mono
-            buildArgs: -s mono+libs+libs.tests+host+packs -c $(_BuildConfig) /p:ArchiveTests=true /p:DevTeamProvisioning=- /p:RunAOTCompilation=true /p:RunSmokeTestsOnly=true /p:BuildTestsOnHelix=true /p:EnableAdditionalTimezoneChecks=true /p:UsePortableRuntimePack=false /p:BuildDarwinFrameworks=true /p:EnableAggressiveTrimming=true
+            buildArgs: -s mono+libs+libs.tests+host+packs -c $(_BuildConfig) /p:ArchiveTests=true /p:DevTeamProvisioning=- /p:RunAOTCompilation=true /p:RunSmokeTestsOnly=true /p:BuildTestsOnHelix=true /p:EnableAdditionalTimezoneChecks=true /p:UsePortableRuntimePack=false /p:EnableAggressiveTrimming=true
             timeoutInMinutes: 480
             condition: >-
               or(
@@ -1107,7 +1107,7 @@ extends:
           jobParameters:
             testGroup: innerloop
             nameSuffix: AllSubsets_Mono
-            buildArgs: -s mono+libs+host+packs+libs.tests -c $(_BuildConfig) /p:ArchiveTests=true /p:RunSmokeTestsOnly=true /p:DevTeamProvisioning=adhoc /p:RunAOTCompilation=true /p:MonoForceInterpreter=true /p:BuildDarwinFrameworks=true
+            buildArgs: -s mono+libs+host+packs+libs.tests -c $(_BuildConfig) /p:ArchiveTests=true /p:RunSmokeTestsOnly=true /p:DevTeamProvisioning=adhoc /p:RunAOTCompilation=true /p:MonoForceInterpreter=true
             timeoutInMinutes: 180
             condition: >-
               or(
@@ -1234,16 +1234,16 @@ extends:
           - windows_x86
           helixQueuesTemplate: /eng/pipelines/libraries/helix-queues-setup.yml
           jobParameters:
-            framework: net48
-            buildArgs: -s tools+libs+libs.tests -framework net48 -c $(_BuildConfig) -testscope innerloop /p:ArchiveTests=true
-            nameSuffix: Libraries_NET48
+            framework: net481
+            buildArgs: -s tools+libs+libs.tests -framework net481 -c $(_BuildConfig) -testscope innerloop /p:ArchiveTests=true
+            nameSuffix: Libraries_NET481
             timeoutInMinutes: 150
             postBuildSteps:
               - template: /eng/pipelines/libraries/helix.yml
                 parameters:
                   creator: dotnet-bot
-                  testRunNamePrefixSuffix: NET48_$(_BuildConfig)
-                  extraHelixArguments: /p:BuildTargetFramework=net48
+                  testRunNamePrefixSuffix: NET481_$(_BuildConfig)
+                  extraHelixArguments: /p:BuildTargetFramework=net481
             condition: >-
               or(
                 eq(stageDependencies.EvaluatePaths.evaluate_paths.outputs['SetPathVars_libraries.containsChange'], true),
diff --git a/eng/testing/BrowserVersions.props b/eng/testing/BrowserVersions.props
index 458a34198846c4..e3eb9e2da48cf9 100644
--- a/eng/testing/BrowserVersions.props
+++ b/eng/testing/BrowserVersions.props
@@ -1,13 +1,13 @@
 <Project>
   <PropertyGroup>
-    <linux_ChromeVersion>133.0.6943.53</linux_ChromeVersion>
-    <linux_ChromeRevision>1402768</linux_ChromeRevision>
-    <linux_ChromeBaseSnapshotUrl>https://storage.googleapis.com/chromium-browser-snapshots/Linux_x64/1402768</linux_ChromeBaseSnapshotUrl>
-    <linux_V8Version>13.3.415</linux_V8Version>
-    <win_ChromeVersion>134.0.6998.35</win_ChromeVersion>
+    <linux_ChromeVersion>134.0.6998.165</linux_ChromeVersion>
+    <linux_ChromeRevision>1415337</linux_ChromeRevision>
+    <linux_ChromeBaseSnapshotUrl>https://storage.googleapis.com/chromium-browser-snapshots/Linux_x64/1415339</linux_ChromeBaseSnapshotUrl>
+    <linux_V8Version>13.4.115</linux_V8Version>
+    <win_ChromeVersion>134.0.6998.166</win_ChromeVersion>
     <win_ChromeRevision>1415337</win_ChromeRevision>
     <win_ChromeBaseSnapshotUrl>https://storage.googleapis.com/chromium-browser-snapshots/Win_x64/1415350</win_ChromeBaseSnapshotUrl>
-    <win_V8Version>13.4.114</win_V8Version>
+    <win_V8Version>13.4.115</win_V8Version>
     <linux_FirefoxRevision>125.0.1</linux_FirefoxRevision>
     <linux_GeckoDriverRevision>0.34.0</linux_GeckoDriverRevision>
     <win_FirefoxRevision>125.0.1</win_FirefoxRevision>
diff --git a/eng/testing/tests.wasm.targets b/eng/testing/tests.wasm.targets
index 65ab8ed35de7f8..0fc4926a782a41 100644
--- a/eng/testing/tests.wasm.targets
+++ b/eng/testing/tests.wasm.targets
@@ -87,7 +87,9 @@
     <WasmExtraFilesToDeploy Condition="'$(_UseWasmSymbolicator)' == 'true'" Include="$(ArtifactsBinDir)WasmSymbolicator\$(Configuration)\$(NetCoreAppToolCurrent)\WasmSymbolicator.dll" />
   </ItemGroup>
 
-  <Target Name="BundleTestWasmApp" DependsOnTargets="$(BundleTestWasmAppDependsOn)" />
+  <Target Name="BundleTestWasmApp" DependsOnTargets="$(BundleTestWasmAppDependsOn)" >
+    <ForceMSBuildGC />
+  </Target>
 
   <UsingTask Condition="'$(BuildAOTTestsOnHelix)' == 'true'"
              TaskName="Microsoft.WebAssembly.Build.Tasks.GenerateAOTProps"
diff --git a/global.json b/global.json
index 671b557316cd0e..cbeeffb6b07484 100644
--- a/global.json
+++ b/global.json
@@ -1,16 +1,16 @@
 {
   "sdk": {
-    "version": "10.0.100-preview.3.25125.5",
+    "version": "10.0.100-preview.3.25167.3",
     "allowPrerelease": true,
     "rollForward": "major"
   },
   "tools": {
-    "dotnet": "10.0.100-preview.3.25125.5"
+    "dotnet": "10.0.100-preview.3.25167.3"
   },
   "msbuild-sdks": {
-    "Microsoft.DotNet.Arcade.Sdk": "10.0.0-beta.25125.2",
-    "Microsoft.DotNet.Helix.Sdk": "10.0.0-beta.25125.2",
-    "Microsoft.DotNet.SharedFramework.Sdk": "10.0.0-beta.25125.2",
+    "Microsoft.DotNet.Arcade.Sdk": "10.0.0-beta.25175.2",
+    "Microsoft.DotNet.Helix.Sdk": "10.0.0-beta.25175.2",
+    "Microsoft.DotNet.SharedFramework.Sdk": "10.0.0-beta.25175.2",
     "Microsoft.Build.NoTargets": "3.7.0",
     "Microsoft.Build.Traversal": "3.4.0",
     "Microsoft.NET.Sdk.IL": "10.0.0-preview.3.25152.4"

From f788e1eb8a097edf1a983f23381efc829724da8f Mon Sep 17 00:00:00 2001
From: vsadov <8218165+VSadov@users.noreply.github.com>
Date: Thu, 27 Mar 2025 16:24:30 -0700
Subject: [PATCH 154/203] fix build

---
 src/coreclr/jit/lsra.cpp      | 10 +++++-----
 src/coreclr/vm/jitinterface.h |  2 +-
 2 files changed, 6 insertions(+), 6 deletions(-)

diff --git a/src/coreclr/jit/lsra.cpp b/src/coreclr/jit/lsra.cpp
index 9c4ca8d750fb93..ba1a9553708b22 100644
--- a/src/coreclr/jit/lsra.cpp
+++ b/src/coreclr/jit/lsra.cpp
@@ -3866,6 +3866,11 @@ void LinearScan::processKills(RefPosition* killRefPosition)
     regsBusyUntilKill &= ~killRefPosition->getKilledRegisters();
     INDEBUG(dumpLsraAllocationEvent(LSRA_EVENT_KILL_REGS, nullptr, REG_NA, nullptr, NONE,
                                     killRefPosition->getKilledRegisters()));
+
+    if (killRefPosition->busyUntilNextKill)
+    {
+        regsBusyUntilKill |= killRefPosition->getKilledRegisters();
+    }
 }
 
 //------------------------------------------------------------------------
@@ -3901,11 +3906,6 @@ void LinearScan::freeKilledRegs(RefPosition*     killRefPosition,
                                          : regRecord->recentRefPosition->nextRefPosition;
         updateNextFixedRef(regRecord, regNextRefPos, nextKill);
     }
-
-    if (killRefPosition->busyUntilNextKill)
-    {
-        regsBusyUntilKill |= killRefPosition->getKilledRegisters();
-    }
 }
 
 //------------------------------------------------------------------------
diff --git a/src/coreclr/vm/jitinterface.h b/src/coreclr/vm/jitinterface.h
index 0bd9fe62cf0a9f..3b1a8d92553cc7 100644
--- a/src/coreclr/vm/jitinterface.h
+++ b/src/coreclr/vm/jitinterface.h
@@ -887,7 +887,7 @@ class CEEJitInfo final : public CEECodeGenInfo
 #ifdef FEATURE_ON_STACK_REPLACEMENT
         , m_pPatchpointInfoFromJit(NULL),
           m_pPatchpointInfoFromRuntime(NULL),
-          m_ilOffset(0)
+          m_ilOffset(0),
 #endif
           m_finalCodeAddressSlot(NULL)
     {

From 1efcc6672f4737360e081325010516ef3cbc25eb Mon Sep 17 00:00:00 2001
From: vsadov <8218165+VSadov@users.noreply.github.com>
Date: Thu, 27 Mar 2025 17:09:03 -0700
Subject: [PATCH 155/203] disable building runtime async tests for now

---
 src/tests/async/Directory.Build.targets | 8 ++++++++
 1 file changed, 8 insertions(+)
 create mode 100644 src/tests/async/Directory.Build.targets

diff --git a/src/tests/async/Directory.Build.targets b/src/tests/async/Directory.Build.targets
new file mode 100644
index 00000000000000..f58f1644f5a61f
--- /dev/null
+++ b/src/tests/async/Directory.Build.targets
@@ -0,0 +1,8 @@
+<Project>
+  <PropertyGroup>
+    <!-- runtime async testing in main repo NYI -->
+    <DisableProjectBuild>true</DisableProjectBuild>
+  </PropertyGroup>
+
+  <Import Project="$([MSBuild]::GetPathOfFileAbove(Directory.Build.targets, $(MSBuildThisFileDirectory)..))" />
+</Project>

From 5de877a8c212f4db26ed5799415b94ae584ddcc7 Mon Sep 17 00:00:00 2001
From: vsadov <8218165+VSadov@users.noreply.github.com>
Date: Thu, 27 Mar 2025 21:05:58 -0700
Subject: [PATCH 156/203] fix for unix

---
 src/coreclr/jit/importer.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/coreclr/jit/importer.cpp b/src/coreclr/jit/importer.cpp
index 83088f16e5a85a..7f42784e514766 100644
--- a/src/coreclr/jit/importer.cpp
+++ b/src/coreclr/jit/importer.cpp
@@ -973,7 +973,7 @@ GenTree* Compiler::impStoreStruct(GenTree*         store,
 
             // Make sure we don't pass something other than a local address to the return buffer arg.
             // It is allowed to pass current's method return buffer as it is a local too.
-            if (fgAddrCouldBeHeap(destAddr) && !eeIsByrefLike(call->gtRetClsHnd) ||
+            if ((fgAddrCouldBeHeap(destAddr) && !eeIsByrefLike(call->gtRetClsHnd)) ||
                 (compIsAsync2() && !destAddr->OperIs(GT_LCL_ADDR)))
             {
                 unsigned tmp = lvaGrabTemp(false DEBUGARG("stack copy for value returned via return buffer"));

From d74066dd615e54832795ac41ecd17678f1c39242 Mon Sep 17 00:00:00 2001
From: vsadov <8218165+VSadov@users.noreply.github.com>
Date: Thu, 27 Mar 2025 21:43:09 -0700
Subject: [PATCH 157/203] another fix for unix

---
 src/coreclr/inc/patchpointinfo.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/coreclr/inc/patchpointinfo.h b/src/coreclr/inc/patchpointinfo.h
index 859044ecd144ef..bdff46def7ef99 100644
--- a/src/coreclr/inc/patchpointinfo.h
+++ b/src/coreclr/inc/patchpointinfo.h
@@ -38,7 +38,7 @@ struct PatchpointInfo
     void Initialize(unsigned localCount, int totalFrameSize)
     {
         m_calleeSaveRegisters     = 0;
-        m_tier0Version            = NULL;
+        m_tier0Version            = 0;
         m_totalFrameSize          = totalFrameSize;
         m_numberOfLocals          = localCount;
         m_genericContextArgOffset = -1;

From c9284cfcdd876001f7cf2dc2941fd9b4bfa3ec88 Mon Sep 17 00:00:00 2001
From: vsadov <8218165+VSadov@users.noreply.github.com>
Date: Thu, 27 Mar 2025 22:05:00 -0700
Subject: [PATCH 158/203] another unix fix

---
 src/coreclr/vm/jithelpers.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/coreclr/vm/jithelpers.cpp b/src/coreclr/vm/jithelpers.cpp
index d2f91893309487..765f2f380c9cf0 100644
--- a/src/coreclr/vm/jithelpers.cpp
+++ b/src/coreclr/vm/jithelpers.cpp
@@ -2576,7 +2576,7 @@ extern "C" void JIT_ResumeOSRWorker(TransitionBlock * pTransitionBlock)
 
     // Find OSR method code for this IL offset.
 
-    PCODE osrMethodCode = NULL;
+    PCODE osrMethodCode = 0;
 
     {
         EECodeInfo codeInfo(ip);

From 1ff64f1490e42058e9a81a1cd4c309c4eff93c3e Mon Sep 17 00:00:00 2001
From: vsadov <8218165+VSadov@users.noreply.github.com>
Date: Fri, 28 Mar 2025 09:56:35 -0700
Subject: [PATCH 159/203] build fixes for x86

---
 src/coreclr/vm/jithelpers.cpp    | 5 +++--
 src/coreclr/vm/jitinterface.h    | 4 ++--
 src/coreclr/vm/threadsuspend.cpp | 2 +-
 3 files changed, 6 insertions(+), 5 deletions(-)

diff --git a/src/coreclr/vm/jithelpers.cpp b/src/coreclr/vm/jithelpers.cpp
index 765f2f380c9cf0..560708f90d4c3b 100644
--- a/src/coreclr/vm/jithelpers.cpp
+++ b/src/coreclr/vm/jithelpers.cpp
@@ -1677,7 +1677,7 @@ HCIMPL1(void, IL_ThrowExact, Object* obj)
     OBJECTREF oref = ObjectToOBJECTREF(obj);
 
 #if defined(_DEBUG) && defined(TARGET_X86)
-    __helperframe.InsureInit(NULL);
+    __helperframe.EnsureInit(NULL);
     g_ExceptionEIP = (LPVOID)__helperframe.GetReturnAddress();
 #endif // defined(_DEBUG) && defined(TARGET_X86)
 
@@ -2756,7 +2756,7 @@ HCIMPL1(VOID, JIT_PartialCompilationPatchpoint, int ilOffset)
 }
 HCIMPLEND
 
-void JIT_ResumeOSR(unsigned ilOffset)
+HCIMPL1(VOID, JIT_ResumeOSR, int ilOffset)
 {
     // Stub version if OSR feature is disabled
     //
@@ -2764,6 +2764,7 @@ void JIT_ResumeOSR(unsigned ilOffset)
 
     UNREACHABLE();
 }
+HCIMPLEND
 
 #endif // FEATURE_ON_STACK_REPLACEMENT
 
diff --git a/src/coreclr/vm/jitinterface.h b/src/coreclr/vm/jitinterface.h
index 3b1a8d92553cc7..7eb7b24a1562b5 100644
--- a/src/coreclr/vm/jitinterface.h
+++ b/src/coreclr/vm/jitinterface.h
@@ -887,9 +887,9 @@ class CEEJitInfo final : public CEECodeGenInfo
 #ifdef FEATURE_ON_STACK_REPLACEMENT
         , m_pPatchpointInfoFromJit(NULL),
           m_pPatchpointInfoFromRuntime(NULL),
-          m_ilOffset(0),
+          m_ilOffset(0)
 #endif
-          m_finalCodeAddressSlot(NULL)
+        , m_finalCodeAddressSlot(NULL)
     {
         CONTRACTL
         {
diff --git a/src/coreclr/vm/threadsuspend.cpp b/src/coreclr/vm/threadsuspend.cpp
index ed495870066a89..d95340e7e8feeb 100644
--- a/src/coreclr/vm/threadsuspend.cpp
+++ b/src/coreclr/vm/threadsuspend.cpp
@@ -4911,7 +4911,7 @@ static bool GetReturnAddressHijackInfo(EECodeInfo *pCodeInfo X86_ARG(ReturnKind
 {
     X86_ONLY(*hasAsyncRet = false);
     GCInfoToken gcInfoToken = pCodeInfo->GetGCInfoToken();
-    if (!pCodeInfo->GetCodeManager()->GetReturnAddressHijackInfo(gcInfoToken X86_ARG(pReturnKind)))
+    if (!pCodeInfo->GetCodeManager()->GetReturnAddressHijackInfo(gcInfoToken X86_ARG(returnKind)))
         return false;
 
     MethodDesc* pMD = pCodeInfo->GetMethodDesc();

From e93475a1a4057e49d4b8a67ea5b703cbe348d6d5 Mon Sep 17 00:00:00 2001
From: vsadov <8218165+VSadov@users.noreply.github.com>
Date: Fri, 28 Mar 2025 10:38:52 -0700
Subject: [PATCH 160/203] build fix for mono

---
 .../src/CompatibilitySuppressions.xml         | 64 +-----------------
 .../CompilerServices/RuntimeHelpers.cs        | 65 ++++++++++++++++++-
 2 files changed, 66 insertions(+), 63 deletions(-)

diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/CompatibilitySuppressions.xml b/src/coreclr/nativeaot/System.Private.CoreLib/src/CompatibilitySuppressions.xml
index 6e2b94377078b0..24c02cd1d575d6 100644
--- a/src/coreclr/nativeaot/System.Private.CoreLib/src/CompatibilitySuppressions.xml
+++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/CompatibilitySuppressions.xml
@@ -743,11 +743,11 @@
   </Suppression>
   <Suppression>
     <DiagnosticId>CP0001</DiagnosticId>
-    <Target>T:System.MDArray</Target>
+    <Target>T:System.FieldHandleInfo</Target>
   </Suppression>
   <Suppression>
     <DiagnosticId>CP0001</DiagnosticId>
-    <Target>T:System.FieldHandleInfo</Target>
+    <Target>T:System.MDArray</Target>
   </Suppression>
   <Suppression>
     <DiagnosticId>CP0001</DiagnosticId>
@@ -849,64 +849,4 @@
     <DiagnosticId>CP0002</DiagnosticId>
     <Target>M:System.Threading.Lock.#ctor(System.Boolean)</Target>
   </Suppression>
-  <Suppression>
-    <DiagnosticId>CP0002</DiagnosticId>
-    <Target>M:System.Runtime.CompilerServices.RuntimeHelpers.Await(System.Runtime.CompilerServices.ConfiguredTaskAwaitable)</Target>
-    <Left>ref/net10.0/System.Private.CoreLib.dll</Left>
-    <Right>lib/net10.0/System.Private.CoreLib.dll</Right>
-  </Suppression>
-  <Suppression>
-    <DiagnosticId>CP0002</DiagnosticId>
-    <Target>M:System.Runtime.CompilerServices.RuntimeHelpers.Await(System.Runtime.CompilerServices.ConfiguredValueTaskAwaitable)</Target>
-    <Left>ref/net10.0/System.Private.CoreLib.dll</Left>
-    <Right>lib/net10.0/System.Private.CoreLib.dll</Right>
-  </Suppression>
-  <Suppression>
-    <DiagnosticId>CP0002</DiagnosticId>
-    <Target>M:System.Runtime.CompilerServices.RuntimeHelpers.Await(System.Threading.Tasks.Task)</Target>
-    <Left>ref/net10.0/System.Private.CoreLib.dll</Left>
-    <Right>lib/net10.0/System.Private.CoreLib.dll</Right>
-  </Suppression>
-  <Suppression>
-    <DiagnosticId>CP0002</DiagnosticId>
-    <Target>M:System.Runtime.CompilerServices.RuntimeHelpers.Await(System.Threading.Tasks.ValueTask)</Target>
-    <Left>ref/net10.0/System.Private.CoreLib.dll</Left>
-    <Right>lib/net10.0/System.Private.CoreLib.dll</Right>
-  </Suppression>
-  <Suppression>
-    <DiagnosticId>CP0002</DiagnosticId>
-    <Target>M:System.Runtime.CompilerServices.RuntimeHelpers.Await``1(System.Runtime.CompilerServices.ConfiguredTaskAwaitable{``0})</Target>
-    <Left>ref/net10.0/System.Private.CoreLib.dll</Left>
-    <Right>lib/net10.0/System.Private.CoreLib.dll</Right>
-  </Suppression>
-  <Suppression>
-    <DiagnosticId>CP0002</DiagnosticId>
-    <Target>M:System.Runtime.CompilerServices.RuntimeHelpers.Await``1(System.Runtime.CompilerServices.ConfiguredValueTaskAwaitable{``0})</Target>
-    <Left>ref/net10.0/System.Private.CoreLib.dll</Left>
-    <Right>lib/net10.0/System.Private.CoreLib.dll</Right>
-  </Suppression>
-  <Suppression>
-    <DiagnosticId>CP0002</DiagnosticId>
-    <Target>M:System.Runtime.CompilerServices.RuntimeHelpers.Await``1(System.Threading.Tasks.Task{``0})</Target>
-    <Left>ref/net10.0/System.Private.CoreLib.dll</Left>
-    <Right>lib/net10.0/System.Private.CoreLib.dll</Right>
-  </Suppression>
-  <Suppression>
-    <DiagnosticId>CP0002</DiagnosticId>
-    <Target>M:System.Runtime.CompilerServices.RuntimeHelpers.Await``1(System.Threading.Tasks.ValueTask{``0})</Target>
-    <Left>ref/net10.0/System.Private.CoreLib.dll</Left>
-    <Right>lib/net10.0/System.Private.CoreLib.dll</Right>
-  </Suppression>
-  <Suppression>
-    <DiagnosticId>CP0002</DiagnosticId>
-    <Target>M:System.Runtime.CompilerServices.RuntimeHelpers.AwaitAwaiterFromRuntimeAsync``1(``0)</Target>
-    <Left>ref/net10.0/System.Private.CoreLib.dll</Left>
-    <Right>lib/net10.0/System.Private.CoreLib.dll</Right>
-  </Suppression>
-  <Suppression>
-    <DiagnosticId>CP0002</DiagnosticId>
-    <Target>M:System.Runtime.CompilerServices.RuntimeHelpers.UnsafeAwaitAwaiterFromRuntimeAsync``1(``0)</Target>
-    <Left>ref/net10.0/System.Private.CoreLib.dll</Left>
-    <Right>lib/net10.0/System.Private.CoreLib.dll</Right>
-  </Suppression>
 </Suppressions>
\ No newline at end of file
diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.cs
index 45399a2a7d3c05..8f8f36315c6ed7 100644
--- a/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.cs
+++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.cs
@@ -176,7 +176,7 @@ public static ReadOnlySpan<T> CreateSpan<T>(RuntimeFieldHandle fldHandle)
         [Intrinsic]
         public static bool IsReferenceOrContainsReferences<T>() where T: allows ref struct => IsReferenceOrContainsReferences<T>();
 
-#if !NATIVEAOT
+#if !NATIVEAOT && !MONO
         [Intrinsic]
         [BypassReadyToRun]
         [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.Async)]
@@ -334,6 +334,69 @@ public static T Await<T>(ConfiguredValueTaskAwaitable<T> configuredAwaitable)
 
             return awaiter.GetResult();
         }
+#else
+        // TODO: PlatformSuppressions.xml does not seem to work on MONO.
+        //       Thus we have these as a workaround.
+
+        [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.Async)]
+        public static void AwaitAwaiterFromRuntimeAsync<TAwaiter>(TAwaiter awaiter) where TAwaiter : INotifyCompletion
+        {
+            throw new PlatformNotSupportedException();
+        }
+
+        [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.Async)]
+        public static void UnsafeAwaitAwaiterFromRuntimeAsync<TAwaiter>(TAwaiter awaiter) where TAwaiter : ICriticalNotifyCompletion
+        {
+            throw new PlatformNotSupportedException();
+        }
+
+        [MethodImpl(MethodImplOptions.Async)]
+        public static T Await<T>(Task<T> task)
+        {
+            throw new PlatformNotSupportedException();
+        }
+
+        [MethodImpl(MethodImplOptions.Async)]
+        public static void Await(Task task)
+        {
+            throw new PlatformNotSupportedException();
+        }
+
+        [MethodImpl(MethodImplOptions.Async)]
+        public static T Await<T>(ValueTask<T> task)
+        {
+            throw new PlatformNotSupportedException();
+        }
+
+        [MethodImpl(MethodImplOptions.Async)]
+        public static void Await(ValueTask task)
+        {
+            throw new PlatformNotSupportedException();
+        }
+
+        [MethodImpl(MethodImplOptions.Async)]
+        public static void Await(ConfiguredTaskAwaitable configuredAwaitable)
+        {
+            throw new PlatformNotSupportedException();
+        }
+
+        [MethodImpl(MethodImplOptions.Async)]
+        public static void Await(ConfiguredValueTaskAwaitable configuredAwaitable)
+        {
+            throw new PlatformNotSupportedException();
+        }
+
+        [MethodImpl(MethodImplOptions.Async)]
+        public static T Await<T>(ConfiguredTaskAwaitable<T> configuredAwaitable)
+        {
+            throw new PlatformNotSupportedException();
+        }
+
+        [MethodImpl(MethodImplOptions.Async)]
+        public static T Await<T>(ConfiguredValueTaskAwaitable<T> configuredAwaitable)
+        {
+            throw new PlatformNotSupportedException();
+        }
 #endif
     }
 }

From 5f48abb28566de4f6c2c1eb078a1e98c5e28d416 Mon Sep 17 00:00:00 2001
From: vsadov <8218165+VSadov@users.noreply.github.com>
Date: Fri, 28 Mar 2025 10:46:33 -0700
Subject: [PATCH 161/203] jit formatting

---
 src/coreclr/jit/importer.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/coreclr/jit/importer.cpp b/src/coreclr/jit/importer.cpp
index 7f42784e514766..d388b53d2324eb 100644
--- a/src/coreclr/jit/importer.cpp
+++ b/src/coreclr/jit/importer.cpp
@@ -9098,7 +9098,7 @@ void Compiler::impImportBlockCode(BasicBlock* block)
                     // TODO: The configVal should be wired to the actual implementation
                     //       that control the flow of sync context.
                     //       We do not have that yet.
-                    int configVal= -1;  // -1 not congigured, 0/1 configured to false/true
+                    int configVal = -1; // -1 not congigured, 0/1 configured to false/true
                     if (compIsAsync2() && JitConfig.JitOptimizeAwait())
                     {
                         isAwait = impMatchAwaitPattern(codeAddr, codeEndp, &configVal);

From f42e2607579f704232d6c596a8ba210dfa8cdcba Mon Sep 17 00:00:00 2001
From: vsadov <8218165+VSadov@users.noreply.github.com>
Date: Fri, 28 Mar 2025 10:56:14 -0700
Subject: [PATCH 162/203] trailing whitespaces

---
 docs/design/features/runtime-handled-tasks.md | 64 +++++++++----------
 1 file changed, 31 insertions(+), 33 deletions(-)

diff --git a/docs/design/features/runtime-handled-tasks.md b/docs/design/features/runtime-handled-tasks.md
index c869d7a2ce4722..eba5d22027889b 100644
--- a/docs/design/features/runtime-handled-tasks.md
+++ b/docs/design/features/runtime-handled-tasks.md
@@ -30,7 +30,7 @@ Unlike traditional C# compiler generated async, async2 methods shall not save/re
 
 #### Integration with `SynchronizationContext` and resumption
 
-A new attribute `System.Runtime.CompilerServices.ConfigureAwaitAttribute` will be defined. It can be applied at the assembly/module/type/method level. It shall be defined as 
+A new attribute `System.Runtime.CompilerServices.ConfigureAwaitAttribute` will be defined. It can be applied at the assembly/module/type/method level. It shall be defined as
 ```cs
 namespace System.Runtime.CompilerServices
 {
@@ -91,7 +91,7 @@ async2 Task<ReturnType> ThunkAsync(ParameterType param1, ParameterType2 param2,
 }
 ```
 
-If any of the parameter types to the target method are ref parameters, or ref structures, then the Thunk generated will be 
+If any of the parameter types to the target method are ref parameters, or ref structures, then the Thunk generated will be
 ```cs
 async2 Task<ReturnType> ThunkAsync(ParameterType param1, ParameterType2 param2, ...)
 {
@@ -145,7 +145,7 @@ Non-async2 code is never permitted to directly invoke async2 code. Instead any a
 
 ### Interaction with Reflection
 
-Async2 methods will not be visible in reflection via the `Type.GetMethod`, `TypeInfo.DeclaredMethods`, `TypeInfo.DeclaredMembers` , or `Type.GetInterfaceMap()` apis. 
+Async2 methods will not be visible in reflection via the `Type.GetMethod`, `TypeInfo.DeclaredMethods`, `TypeInfo.DeclaredMembers` , or `Type.GetInterfaceMap()` apis.
 
 `Type.GetMembers` and `Type.GetMethods` will be able to find the async2 methods if and only if the `BindingFlags.Async2Visible` flag is set.
 
@@ -456,7 +456,7 @@ async2 Task<ReturnType> Thunk(ParameterType param1, ParameterType2 param2, ...)
 ## C# Language changes
 
 For the purpose of the experiment we have extended the syntax and semantics of C# language to support async2 methods. It is not the goal for this syntax to become the syntax in the actual implementation, however we will record our experiences with the experimental syntax in case it is useful when designing the actual syntax.
-  
+
 #### === Syntax v2 (current)
 
 The syntax that is currently in use in the experiment looks like:
@@ -485,7 +485,7 @@ int32 modopt([System.Runtime]System.Threading.Tasks.ValueTask`1) M1(int arg)
 ```
 and combinations of the above.
 
-Internal Roslyn compiler representation of async2 methods is roughly the same as for regular `Task/ValueTask` returning methods, except that the method symbol reports that it is an async2 method thus scenarios that are different for different flavors of async/async2 (i.e. lowering of `await` operator), can work differently. 
+Internal Roslyn compiler representation of async2 methods is roughly the same as for regular `Task/ValueTask` returning methods, except that the method symbol reports that it is an async2 method thus scenarios that are different for different flavors of async/async2 (i.e. lowering of `await` operator), can work differently.
 
 In a particular case of lowering await operator, if we notice that both containing and awaited members are async2, we can lower the `await M1()` into a direct call to the `int modopt(..)M1()`
 Conversely, if we see an invocation of `M1()` without await or in a regular, non-async2 member, we lower that into a call to `Task<int> M1()` thunk.
@@ -517,18 +517,18 @@ public int32 modopt([System.Runtime]System.Threading.Tasks.Task`1) M1()
 
 Operations that are not affected by `async2`, such as generic substitution or Overriding/Hiding/Implementing end up naturally working with async2 methods. Note that these areas are some of the most complex parts of the compiler. It was very beneficial to not having to specialcase async2 in these areas.
 
-One thing to observe is that unlike regular `async`, which is a source-only concept, the async2 survives serialization/deserialization via metadata. 
+One thing to observe is that unlike regular `async`, which is a source-only concept, the async2 survives serialization/deserialization via metadata.
 Effectively we treat the `int32 modopt([System.Runtime]System.Threading.Tasks.Task'1)` as just a special encoding of `Task<int>` return type with additional property of making the method `async2`.
 
 #### Unresolved concerns.
 None of the following appears to be unresolvable or blocking, we just did not get to these due to time constraints and the scoping of the experiment.
 
 1. There is an existing concept for `async void` methods. Does that model need to exist in the new system?
-1. It looks like there might be a need for async2 flavors of delegates. 
+1. It looks like there might be a need for async2 flavors of delegates.
 To the runtime these would look like delegates with async2 `Invoke` method and may "just work". They would need some kind of syntax in C#.
-1. It looks like there might be a need for async2 flavors of method pointers - to represent async2 methods. 
+1. It looks like there might be a need for async2 flavors of method pointers - to represent async2 methods.
 In the runtime this is representable via the modopt on the return type and, again, might "just work", so this is mostly a matter of picking C# syntax/semantics.
-1. Is there any impact on async enumerable/iterator?  
+1. Is there any impact on async enumerable/iterator?
 While async2 is interoperable with regular async and these interfaces could be used or even implemented by the means of async2 methods, there could be opportunities to employ async2 implementations in the lowering. It is not clear if that could be beneficial without defining/deriving async2 versions of `IAsyncEnumerable`/`IAsyncEnumerator`/`IAsyncDisposable`.
 
 #### ==== Syntax v1 (abandoned)
@@ -547,12 +547,12 @@ int32 modopt([System.Runtime]System.Threading.Tasks.Task`1) M1(int arg)
 While initially favored for being close to the IL representation, it was eventually abandoned due to major problems and inconsistencies.
 - When the method above is called from regular code without awaiting, user gets `Task<int>` result. That is not what it looks in the signature.
 - For the purposes of overriding/hiding/inheriting the method above is equivalent to `Task<int> M1(int arg)`, which is easily observable.
-For example it is illegal to add such method in the same class as invoking `Task<int> M1(int arg)` would become ambiguous. On the other hand overloading a method like `int M1(int arg)` is completely non-conflicting.  
-This was very confusing, both as a programming model and as internal implementation.  
-- Internally, due to duality with `Task<int> M1(int arg)` signature it was often necessary to "lift" the signature into the `Task<int>` form, perform operation there (such as generic substitution or overriding resolution) and then "unlift" the result back into `async2 int` form. It was frequintly a source of subtle bugs.  
+For example it is illegal to add such method in the same class as invoking `Task<int> M1(int arg)` would become ambiguous. On the other hand overloading a method like `int M1(int arg)` is completely non-conflicting.
+This was very confusing, both as a programming model and as internal implementation.
+- Internally, due to duality with `Task<int> M1(int arg)` signature it was often necessary to "lift" the signature into the `Task<int>` form, perform operation there (such as generic substitution or overriding resolution) and then "unlift" the result back into `async2 int` form. It was frequintly a source of subtle bugs.
 - When we got to interfacing with `ValueTask` there was no good place in the syntax to express a variant of the same method, but with `ValueTask`-based thunk.
-For some time we used an attribute and that was verbose and not symmetrical.  
-Besides, it is valid for the same class to contain two async2 methods that differ only on `Task`/`ValueTask` flavor of the thunk. It would mean that in the source one could have two methods in the same class, differing only by an attribute.  
+For some time we used an attribute and that was verbose and not symmetrical.
+Besides, it is valid for the same class to contain two async2 methods that differ only on `Task`/`ValueTask` flavor of the thunk. It would mean that in the source one could have two methods in the same class, differing only by an attribute.
 
 ## Microbenchmarks
 
@@ -566,7 +566,7 @@ The data was gathered using the `src/tests/Loader/async/async-mincallcost-microb
 
 This benchmark works by an async method calling into another mostly empty async method in a loop.
 The benchmark operates for a fixed period of time, measuring the number of iterations.
-The caller method is always a `Task<long>` returning method, and the mostly empty method, reads a global variable, and then awaits the result of a call to `Task.Yield()` if that global variable is `0`. (The variable never has the value '0'.) 
+The caller method is always a `Task<long>` returning method, and the mostly empty method, reads a global variable, and then awaits the result of a call to `Task.Yield()` if that global variable is `0`. (The variable never has the value '0'.)
 
 ![MinCallCostGraph](async-mincallcost.svg)
 
@@ -576,7 +576,7 @@ In addition, the cost to call an async2 function from another async2 function is
 Here we can see the cost of inlining vs not inlining, as there is a meaningful reduction in iteration count.
 In addition, the async2 method with Context Save is also unable to inline at the current time, and also performs work simulating the additional work that the async2 logic would need to implement to fully replicate async1 behavior around async locals, and synchronization context handling.
 
-NOTES: 
+NOTES:
 - Cost of calls from async1 method to async2 method are shown for the JIT state machine model only.
 The performance of the calls from async1 to async2 for the unwinder model were substantially slower, although if we choose that as the implementation style it is expected they could be made at least as fast as the state machine model.
 - Measurement of calls from async1 to async 2 non-inlineable and with context save were not measured.
@@ -614,7 +614,7 @@ With `ValueTask`, the function signature is `async ValueTask<long> RunTask(int d
 With `Async2`, the function signature is `async2 long RunTask(int depth)`
 With `Async2Capture`, the function signature is `async2 long RunTask(int depth)`, and the body of the function contains a try/finally which saves/restores the Sync and ExecutionContext. This is intended to (imperfectly) measure the cost of adjusting the runtime async model to behave exactly like the existing async model.
 
-When a graph does not specify one of the config options, the graph is a relative performance gathered by dividing the iters/250ms that the benchmark produces between the two options, and then multiplying by 100 to produce a percentage. 
+When a graph does not specify one of the config options, the graph is a relative performance gathered by dividing the iters/250ms that the benchmark produces between the two options, and then multiplying by 100 to produce a percentage.
 
 ## Relative performance of throwing vs returning cleanly from a function at varying stack depths
 
@@ -708,15 +708,15 @@ It is not a secret that inability to use span and byref with async code causes s
 
 https://stackoverflow.com/questions/57229123/how-to-use-spanbyte-in-async-method
 
-https://stackoverflow.com/questions/63284335/spant-and-async-methods 
+https://stackoverflow.com/questions/63284335/spant-and-async-methods
 
-[Span<T> in async methods not supported · Issue #27147 · dotnet/roslyn (github.com)] (https://github.com/dotnet/roslyn/issues/27147) 
+[Span<T> in async methods not supported · Issue #27147 · dotnet/roslyn (github.com)] (https://github.com/dotnet/roslyn/issues/27147)
 
-https://stackoverflow.com/questions/20868103/ref-and-out-arguments-in-async-method 
+https://stackoverflow.com/questions/20868103/ref-and-out-arguments-in-async-method
 
-[Consider relaxing restrictions of async methods in blocks that do not contain `await`. · Issue 1331 · dotnet/csharplang (github.com)](https://github.com/dotnet/csharplang/issues/1331) Lots of discussions here, including insightful comments by Vance. 
+[Consider relaxing restrictions of async methods in blocks that do not contain `await`. · Issue 1331 · dotnet/csharplang (github.com)](https://github.com/dotnet/csharplang/issues/1331) Lots of discussions here, including insightful comments by Vance.
 
-The current situation is that C# does not allow byrefs and byref-likes in async methods in any form – be it parameters, locals or spans. The reason for this restriction is inability to capture byrefs as fields of display types. For consistency all use is forbidden, even if it does not require capture.  
+The current situation is that C# does not allow byrefs and byref-likes in async methods in any form – be it parameters, locals or spans. The reason for this restriction is inability to capture byrefs as fields of display types. For consistency all use is forbidden, even if it does not require capture.
 There are few cases were C# supports transient byrefs by the means of decomposing a byref-producing expression into constituent parts and capturing the parts and re-playing at use sites. Example `staticArray[i].Field += await Somehting();`    That allows to handle a subset of cases, but codegen is far from great.
 
 In runtime-assisted async supporting span and byrefs is not a strict “no”, but there are implications. There are roughly two kinds of byrefs that async implementation would need to deal with. They come with distinct challenges.
@@ -725,14 +725,14 @@ In runtime-assisted async supporting span and byrefs is not a strict “no”, b
 
 **Stack-referencing byrefs** do not have to be reported to GC, but must be adjusted when containing frame is reactivated as the referent is either in the current frame and thus now lives in a different stack location, or in one of still suspended caller frames and thus not on the stack at all. In the presence of “ref Span<T> param” and similar, updating upon suspending is also necessary to keep chains of byrefs safely walkable.
 
-In general it would not be a tractable programming model from the user perspective, if we allow only one kind of byrefs and not another as distinction may often only be known at run time.  
-Note that the stack/heap-referencing nature of a byref cannot change while the frame is not active. However that is not completely true if “ref Span<T> param” is allowed. In such case a calee may repoint a byref in a caller frame and switch from stack-pointing to heap-pointing.   
+In general it would not be a tractable programming model from the user perspective, if we allow only one kind of byrefs and not another as distinction may often only be known at run time.
+Note that the stack/heap-referencing nature of a byref cannot change while the frame is not active. However that is not completely true if “ref Span<T> param” is allowed. In such case a calee may repoint a byref in a caller frame and switch from stack-pointing to heap-pointing.
 
 **Stack-referencing byrefs, that lead into sync callers** these are unsafe as the sync caller may exit any or all its stack frames before the Task is resumed.
 
 In the current experiment we have looked into two implementing strategies:
 
-**Stack unwinding with 1:1 stack capture for suspended frames.** In this model byrefs/spans that point to heap or other async callers can be supported as there is a mechanism to report byrefs to the GC. There are some performance challenges, but not without solutions. 
+**Stack unwinding with 1:1 stack capture for suspended frames.** In this model byrefs/spans that point to heap or other async callers can be supported as there is a mechanism to report byrefs to the GC. There are some performance challenges, but not without solutions.
 
 **State machine with managed storage for captured variables.** The current implementation does not support capturing byrefs and byref-likes. On the other hand it can utilize regular GC reporting mechanisms, which has many advantages, notably not having O(n) components that need to run in GC pauses. There are some ideas on how byrefs may be captured and converted into {object, offset} at the next GC, so that not to incur continuous O(n) costs.
 
@@ -752,13 +752,13 @@ Our observations (on x64, AMD 5950X):
 
 There is some noise, but a typical Gen0 pause in the above scenario was: 120 microseconds.
 The managed heap size (as reported by `GetGCMemoryInfo.HeapSizeBytes`) is stable at 130 Mb.
-The peak working set (as reported by `Task Manager`) is 148 Mb. 
+The peak working set (as reported by `Task Manager`) is 148 Mb.
 
 **=====Async2 (unwinding/stack-capturing based)**:
 
 When GC pauses were measured as-is, the results were very disappointing - **48690 microsecond**. (**~400x worse** than async1 and generally unacceptable for a Gen0 pause)
 
-The reasons for such timings is that the implementation would go through every one of the 1000000 tasklets and report referenced objects as roots. That is time consuming and is also very redundant when GC generational model is considered. Since suspended stacks can't see assignments of new objects, once GC performs en-masse promotion, all the roots owned by these tasks become too old to be of interest for collections that target younger generations until the containing task is resumed.  
+The reasons for such timings is that the implementation would go through every one of the 1000000 tasklets and report referenced objects as roots. That is time consuming and is also very redundant when GC generational model is considered. Since suspended stacks can't see assignments of new objects, once GC performs en-masse promotion, all the roots owned by these tasks become too old to be of interest for collections that target younger generations until the containing task is resumed.
 This is a unique challenge of stack-capturing implementation. In async1 suspended tasks are regular heap objects and do not require any special reporting in GC STW phases.
 
 As a solution to this issue, we implemented tasklet aging mechanism, similar to approach used by GC handles. We make note of GC promotions, and increase "min age" of suspended tasks, so that, for example, tasks that only refer to tenured objects do not need to report roots in ephemeral collections.
@@ -768,20 +768,20 @@ It was still **4x worse** than async1.
 
 Once tasks know their age, root reporting is no longer the dominant factor, but we still need to check age of 10000 tasks, even if the answer is "too old". A further improvement is possible by grouping the suspended tasks in groups, so that age of entire group could be examined.
 
-For the memory consumption, the managed heap is reported to be very small - **28Mb**. Unsurprisingly, since this approach uses native/malloc memory to store captured data.  
+For the memory consumption, the managed heap is reported to be very small - **28Mb**. Unsurprisingly, since this approach uses native/malloc memory to store captured data.
 However, the peak working set was seen at **499Mb** that is **3.3x worse** than async1.
 
 The reason for higher memory consumption is that this approach captures the entire stack frame and its registers, which is less eficient than async1, which, based on data-flow analysis, may capture only variables that are live across awaits.
 
 **=====Async2 (JIT state-machine based)**:
 
-The pause times were observed at **100 microseconds**, which is **better than in async1** implementation. 
+The pause times were observed at **100 microseconds**, which is **better than in async1** implementation.
 Since the root set would be roughly the same as in async1, the difference could be just second-order effects of different heap graph or different distribution of object sizes.
 
-The peak working set was seen at 328Mb. 
+The peak working set was seen at 328Mb.
 That is higher than async1 and most likely just additional transient allocations in the JIT. As the benchmark has just a few methods, it cannot be method bodies, but may need some follow up to confirm.
 
-Managed allocation impact in this model depends on JIT optimizations as the capture is based on liveness information, which is not available in tier-0 compiled methods. That is expected and could be acceptable as hot methods would not stay in tier-0. Besides it is possible to enable liveness analysis in tier-0 async2 methods, if needed, at about 5% of the estimated JIT cost. 
+Managed allocation impact in this model depends on JIT optimizations as the capture is based on liveness information, which is not available in tier-0 compiled methods. That is expected and could be acceptable as hot methods would not stay in tier-0. Besides it is possible to enable liveness analysis in tier-0 async2 methods, if needed, at about 5% of the estimated JIT cost.
 It is also possible to improve liveness analysis/information in async2 methods in general - to further improve capturing efficiency.
 
 With `set DOTNET_TieredCompilation=0`
@@ -789,5 +789,3 @@ Managed heap size: **157Mb**   - slightly more than async1
 
 With `set DOTNET_TieredCompilation=1`
 Managed heap size: **reaches 300Mb** in the first couple iterations, but then drops and **stays at 105Mb**, which is better than async1.
-
- 

From 9558af64e38fc09d9f87c7b2b373430a33d90d8c Mon Sep 17 00:00:00 2001
From: vsadov <8218165+VSadov@users.noreply.github.com>
Date: Fri, 28 Mar 2025 11:25:52 -0700
Subject: [PATCH 163/203] build fix for riscv

---
 src/coreclr/jit/targetloongarch64.h | 3 +++
 src/coreclr/jit/targetriscv64.h     | 3 +++
 2 files changed, 6 insertions(+)

diff --git a/src/coreclr/jit/targetloongarch64.h b/src/coreclr/jit/targetloongarch64.h
index 452778c31963a0..d691f4c8fd1ec2 100644
--- a/src/coreclr/jit/targetloongarch64.h
+++ b/src/coreclr/jit/targetloongarch64.h
@@ -246,6 +246,9 @@
   #define REG_VALIDATE_INDIRECT_CALL_ADDR REG_T3
   #define REG_DISPATCH_INDIRECT_CALL_ADDR REG_T0
 
+  #define REG_ASYNC_CONTINUATION_RET REG_A2
+  #define RBM_ASYNC_CONTINUATION_RET RBM_A2
+
   #define REG_FPBASE               REG_FP
   #define RBM_FPBASE               RBM_FP
   #define STR_FPBASE               "fp"
diff --git a/src/coreclr/jit/targetriscv64.h b/src/coreclr/jit/targetriscv64.h
index e5dcded3d878f5..ee6c6d22260c7c 100644
--- a/src/coreclr/jit/targetriscv64.h
+++ b/src/coreclr/jit/targetriscv64.h
@@ -222,6 +222,9 @@
   #define REG_VALIDATE_INDIRECT_CALL_ADDR REG_T3
   #define REG_DISPATCH_INDIRECT_CALL_ADDR REG_T0
 
+  #define REG_ASYNC_CONTINUATION_RET REG_A2
+  #define RBM_ASYNC_CONTINUATION_RET RBM_A2
+
   #define REG_FPBASE               REG_FP
   #define RBM_FPBASE               RBM_FP
   #define STR_FPBASE               "fp"

From 01e96b5168846329fa3a5ca760737fd3a60af6be Mon Sep 17 00:00:00 2001
From: vsadov <8218165+VSadov@users.noreply.github.com>
Date: Fri, 28 Mar 2025 21:19:21 -0700
Subject: [PATCH 164/203] do not report async2 variants to reflection

---
 src/coreclr/vm/methodtable.cpp | 46 +++++++++++++++++++++-------------
 1 file changed, 29 insertions(+), 17 deletions(-)

diff --git a/src/coreclr/vm/methodtable.cpp b/src/coreclr/vm/methodtable.cpp
index dd1ccdc074d547..dca10e1f1418f6 100644
--- a/src/coreclr/vm/methodtable.cpp
+++ b/src/coreclr/vm/methodtable.cpp
@@ -7293,7 +7293,15 @@ MethodDesc * MethodTable::IntroducedMethodIterator::GetFirst(MethodTable *pMT)
 {
     LIMITED_METHOD_CONTRACT;
     MethodDescChunk * pChunk = pMT->GetClass()->GetChunks();
-    return (pChunk != NULL) ? pChunk->GetFirstMethodDesc() : NULL;
+    if (pChunk == NULL)
+        return NULL;
+
+    MethodDesc* md = pChunk->GetFirstMethodDesc();
+    if (!md->IsAsync2VariantMethod())
+        return md;
+
+    // skip async2 variants, the other variant will be reported instead
+    return GetNext(md);
 }
 
 //==========================================================================================
@@ -7302,26 +7310,30 @@ MethodDesc * MethodTable::IntroducedMethodIterator::GetNext(MethodDesc * pMD)
     WRAPPER_NO_CONTRACT;
 
     MethodDescChunk * pChunk = pMD->GetMethodDescChunk();
+    do
+    {
+        // Check whether the next MethodDesc is still within the bounds of the current chunk
+        TADDR pNext = dac_cast<TADDR>(pMD) + pMD->SizeOf();
+        TADDR pEnd = dac_cast<TADDR>(pChunk) + pChunk->SizeOf();
 
-    // Check whether the next MethodDesc is still within the bounds of the current chunk
-    TADDR pNext = dac_cast<TADDR>(pMD) + pMD->SizeOf();
-    TADDR pEnd = dac_cast<TADDR>(pChunk) + pChunk->SizeOf();
+        if (pNext < pEnd)
+        {
+            // Just skip to the next method in the same chunk
+            pMD = PTR_MethodDesc(pNext);
+        }
+        else
+        {
+            _ASSERTE(pNext == pEnd);
 
-    if (pNext < pEnd)
-    {
-        // Just skip to the next method in the same chunk
-        pMD = PTR_MethodDesc(pNext);
-    }
-    else
-    {
-        _ASSERTE(pNext == pEnd);
+            // We have walked all the methods in the current chunk. Move on
+            // to the next chunk.
+            pChunk = pChunk->GetNextChunk();
 
-        // We have walked all the methods in the current chunk. Move on
-        // to the next chunk.
-        pChunk = pChunk->GetNextChunk();
+            pMD = (pChunk != NULL) ? pChunk->GetFirstMethodDesc() : NULL;
+        }
 
-        pMD = (pChunk != NULL) ? pChunk->GetFirstMethodDesc() : NULL;
-    }
+    // skip async2 variants, the other variant will be reported instead
+    } while (pMD != NULL && pMD->IsAsync2VariantMethod());
 
     return pMD;
 }

From dba51dbfed6adb7c20759461f2860430ceca5af1 Mon Sep 17 00:00:00 2001
From: vsadov <8218165+VSadov@users.noreply.github.com>
Date: Sat, 29 Mar 2025 14:11:19 -0700
Subject: [PATCH 165/203] alow ELEMENT_TYPE_INTERNAL when classifying
 signatures.

---
 src/coreclr/vm/methodtablebuilder.cpp | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/src/coreclr/vm/methodtablebuilder.cpp b/src/coreclr/vm/methodtablebuilder.cpp
index 531e5562e515fe..32abe006a016ae 100644
--- a/src/coreclr/vm/methodtablebuilder.cpp
+++ b/src/coreclr/vm/methodtablebuilder.cpp
@@ -2875,6 +2875,9 @@ AsyncMethodSignatureKind ClassifyAsyncMethodSignatureCore(SigPointer sig, Module
     if (elemType == ELEMENT_TYPE_GENERICINST)
     {
         IfFailThrow(sig.GetElemType(&elemType));
+        if (elemType == ELEMENT_TYPE_INTERNAL)
+            return AsyncMethodSignatureKind::NormalMethod;
+
         *pIsValueTask = (elemType == ELEMENT_TYPE_VALUETYPE);
         IfFailThrow(sig.GetToken(&tk));
         IfFailThrow(sig.GetData(&data));

From 45a610b0b50c1d3a6aa1cad8dd27e75b2997fae1 Mon Sep 17 00:00:00 2001
From: vsadov <8218165+VSadov@users.noreply.github.com>
Date: Sat, 29 Mar 2025 14:49:34 -0700
Subject: [PATCH 166/203] handle Task-returning methods implementing/overriding
 non-Task-returning methods.

---
 src/coreclr/vm/methodtablebuilder.cpp | 38 ++++++++++++++++++++-------
 src/coreclr/vm/methodtablebuilder.h   |  7 +----
 2 files changed, 29 insertions(+), 16 deletions(-)

diff --git a/src/coreclr/vm/methodtablebuilder.cpp b/src/coreclr/vm/methodtablebuilder.cpp
index 32abe006a016ae..59ad52dce4987d 100644
--- a/src/coreclr/vm/methodtablebuilder.cpp
+++ b/src/coreclr/vm/methodtablebuilder.cpp
@@ -5603,7 +5603,7 @@ MethodTableBuilder::InitNewMethodDesc(
 #endif // _DEBUG
 
     Signature sig;
-    if (pMethod->IsAsyncThunk())
+    if (pMethod->IsAsync2Variant())
     {
         sig = pMethod->GetMethodSignature().GetSignatureClass();
     }
@@ -5937,10 +5937,10 @@ MethodTableBuilder::FindDeclMethodOnInterfaceEntry(bmtInterfaceEntry *pItfEntry,
         }
     }
 
-    if (variantLookup == AsyncVariantLookup::AsyncOtherVariant)
+    if (variantLookup == AsyncVariantLookup::AsyncOtherVariant && !declMethod.IsNull())
     {
         bmtRTMethod* declRTMethod = declMethod.AsRTMethod();
-        bool foundOtherVariant = false;
+        declMethod = {};
         for (; !slotIt.AtEnd(); slotIt.Next())
         {
             bmtRTMethod* slotDeclMethod = slotIt->Decl().AsRTMethod();
@@ -5951,12 +5951,9 @@ MethodTableBuilder::FindDeclMethodOnInterfaceEntry(bmtInterfaceEntry *pItfEntry,
                 (slotDeclMethod->GetMethodDesc()->IsAsync2VariantMethod() != declRTMethod->GetMethodDesc()->IsAsync2VariantMethod()))
             {
                 declMethod = slotIt->Decl();
-                foundOtherVariant = true;
                 break;
             }
         }
-
-        _ASSERTE(foundOtherVariant);
     }
 
     return declMethod;
@@ -6027,7 +6024,9 @@ MethodTableBuilder::ProcessInexactMethodImpls()
             continue;
         }
 
-        AsyncVariantLookup asyncVariantOfDeclToFind = !it->IsAsyncThunk() ? AsyncVariantLookup::MatchingAsyncVariant : AsyncVariantLookup::AsyncOtherVariant;
+        AsyncVariantLookup asyncVariantOfDeclToFind = !it->IsAsync2Variant() ?
+            AsyncVariantLookup::MatchingAsyncVariant :
+            AsyncVariantLookup::AsyncOtherVariant;
 
         // If this method serves as the BODY of a MethodImpl specification, then
         // we should iterate all the MethodImpl's for this class and see just how many
@@ -6170,7 +6169,9 @@ MethodTableBuilder::ProcessMethodImpls()
             continue;
         }
 
-        AsyncVariantLookup asyncVariantOfDeclToFind = !it->IsAsyncThunk() ? AsyncVariantLookup::MatchingAsyncVariant : AsyncVariantLookup::AsyncOtherVariant;
+        AsyncVariantLookup asyncVariantOfDeclToFind = !it->IsAsync2Variant() ?
+            AsyncVariantLookup::MatchingAsyncVariant :
+            AsyncVariantLookup::AsyncOtherVariant;
 
         // If this method serves as the BODY of a MethodImpl specification, then
         // we should iterate all the MethodImpl's for this class and see just how many
@@ -6355,6 +6356,14 @@ MethodTableBuilder::ProcessMethodImpls()
                                 declMethod = FindDeclMethodOnClassInHierarchy(it, pDeclMT, declSig, asyncVariantOfDeclToFind);
                             }
 
+                            if (declMethod.IsNull() && asyncVariantOfDeclToFind == AsyncVariantLookup::AsyncOtherVariant)
+                            {
+                                // when implementing/overriding, we may see a Task-returning method
+                                // which matches a T-returning method in the interface/base, which would not have variants.
+                                // in such case the async2 variant of the Task-returning method does not implement/override anything.
+                                continue;
+                            }
+
                             if (declMethod.IsNull())
                             {   // Would prefer to let this fall out to the BuildMethodTableThrowException
                                 // below, but due to v2.0 and earlier behaviour throwing a MissingMethodException,
@@ -6490,10 +6499,19 @@ MethodTableBuilder::bmtMethodHandle MethodTableBuilder::FindDeclMethodOnClassInH
                         iPass == 0 ? &newVisited : NULL))
                     {
                         if (variantLookup == AsyncVariantLookup::AsyncOtherVariant)
-                            pCurMD = pCurMD->GetAsyncOtherVariant();
+                        {
+                            if (pCurMD->IsTaskReturningMethod() || pCurMD->IsAsync2VariantMethod())
+                            {
+                                pCurMD = pCurMD->GetAsyncOtherVariant();
+                            }
+                            else
+                            {
+                                declMethod = {};
+                                break;
+                            }
+                        }
 
                         declMethod = (*bmtParent->pSlotTable)[pCurMD->GetSlot()].Decl();
-
                         break;
                     }
                 }
diff --git a/src/coreclr/vm/methodtablebuilder.h b/src/coreclr/vm/methodtablebuilder.h
index 01899ef8152d25..26e08a5d01bcf9 100644
--- a/src/coreclr/vm/methodtablebuilder.h
+++ b/src/coreclr/vm/methodtablebuilder.h
@@ -1060,17 +1060,12 @@ class MethodTableBuilder
         GetRVA() const
             { LIMITED_METHOD_CONTRACT; return m_dwRVA; }
 
-        bool IsAsyncThunk() const
+        bool IsAsync2Variant() const
         {
             return GetAsyncMethodKind() == AsyncMethodKind::Async2VariantThunk ||
                 GetAsyncMethodKind() == AsyncMethodKind::Async2VariantImpl;
         }
 
-        bool IsAsync2() const
-        {
-            return IsAsyncThunk();
-        }
-
         void SetAsyncMethodKind(AsyncMethodKind kind)
         {
             m_asyncMethodKind = kind;

From cdf37d0db7b86de82d1ce23122d6f6a3962b26f6 Mon Sep 17 00:00:00 2001
From: vsadov <8218165+VSadov@users.noreply.github.com>
Date: Sat, 29 Mar 2025 19:44:09 -0700
Subject: [PATCH 167/203] infinite recursion in
 TryResolveVirtualStaticMethodOnThisType

---
 src/coreclr/vm/methodtable.cpp | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/src/coreclr/vm/methodtable.cpp b/src/coreclr/vm/methodtable.cpp
index dca10e1f1418f6..5d303f07db97f8 100644
--- a/src/coreclr/vm/methodtable.cpp
+++ b/src/coreclr/vm/methodtable.cpp
@@ -8227,6 +8227,8 @@ MethodTable::TryResolveVirtualStaticMethodOnThisType(MethodTable* pInterfaceType
             {
                 differsByAsyncVariant = true;
                 pMethodDecl = pMethodDecl->GetAsyncOtherVariant();
+                if (verifyImplemented)
+                    return pMethodDecl;
             }
             else
             {

From aae997e4d368df250a066deb60ef5b4650cf53b6 Mon Sep 17 00:00:00 2001
From: vsadov <8218165+VSadov@users.noreply.github.com>
Date: Sun, 30 Mar 2025 14:05:18 -0700
Subject: [PATCH 168/203] fix for EnC

---
 src/coreclr/debug/ee/functioninfo.cpp | 2 +-
 src/coreclr/vm/encee.cpp              | 2 +-
 src/coreclr/vm/methoditer.cpp         | 4 ++--
 3 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/src/coreclr/debug/ee/functioninfo.cpp b/src/coreclr/debug/ee/functioninfo.cpp
index b28620ede25439..c7613dfd5d55c2 100644
--- a/src/coreclr/debug/ee/functioninfo.cpp
+++ b/src/coreclr/debug/ee/functioninfo.cpp
@@ -2022,7 +2022,7 @@ void DebuggerMethodInfo::CreateDJIsForNativeBlobs(AppDomain * pAppDomain, Method
         // have DJIs for every verision of a method that was EnCed.
         // This also handles the possibility of getting the same methoddesc back from the iterator.
         // It also lets EnC + generics play nice together (including if an generic method was EnC-ed)
-        LoadedMethodDescIterator it(pAppDomain, m_module, m_token, /* fIsAsync2Variant */false); // TODO! Debugger doesn't handle async2 variants now
+        LoadedMethodDescIterator it(pAppDomain, m_module, m_token, /* fIsAsync2Variant */false); // TODO: EnC doesn't handle async2 variants now
         CollectibleAssemblyHolder<Assembly *> pAssembly;
         while (it.Next(pAssembly.This()))
         {
diff --git a/src/coreclr/vm/encee.cpp b/src/coreclr/vm/encee.cpp
index 9b2d7c6f6d9818..5b1d2ee0fa601b 100644
--- a/src/coreclr/vm/encee.cpp
+++ b/src/coreclr/vm/encee.cpp
@@ -342,7 +342,7 @@ HRESULT EditAndContinueModule::UpdateMethod(MethodDesc *pMethod)
             AppDomain::GetCurrentDomain(),
             module,
             tkMethod,
-            pMethod->IsAsync2VariantMethod(),
+            /* fIsAsync2Variant */false, // TODO: EnC doesn't handle async2 variants now
             AssemblyIterationFlags(kIncludeLoaded | kIncludeExecution));
         CollectibleAssemblyHolder<Assembly *> pAssembly;
         while (it.Next(pAssembly.This()))
diff --git a/src/coreclr/vm/methoditer.cpp b/src/coreclr/vm/methoditer.cpp
index 681cdb9e73af15..79af7b137359e1 100644
--- a/src/coreclr/vm/methoditer.cpp
+++ b/src/coreclr/vm/methoditer.cpp
@@ -57,7 +57,7 @@ BOOL LoadedMethodDescIterator::Next(
         return FALSE;
     }
 
-    if (m_fIsAsync2Variant)
+    if (m_fIsAsync2Variant && m_mainMD->IsTaskReturningMethod())
     {
         m_mainMD = m_mainMD->GetMethodTable()->GetParallelMethodDesc(m_mainMD, AsyncVariantLookup::AsyncOtherVariant);
 
@@ -250,7 +250,7 @@ LoadedMethodDescIterator::Start(
     mdMethodDef     md,
     MethodDesc      *pMethodDesc)
 {
-    Start(pAppDomain, pModule, md, pMethodDesc->IsAsync2VariantMethod());
+    Start(pAppDomain, pModule, md, /*m_fIsAsync2Variant*/ false); // TODO: EnC doesn't handle async2 variants now
     m_mainMD = pMethodDesc;
 }
 

From 43cdf7d6624ac22ed05ee6dff23748742703cd1c Mon Sep 17 00:00:00 2001
From: vsadov <8218165+VSadov@users.noreply.github.com>
Date: Sun, 30 Mar 2025 16:22:09 -0700
Subject: [PATCH 169/203] Another workaround for reflection (need a better
 solution though)

---
 .../src/System/RuntimeType.CoreCLR.cs         | 25 +++++++++++++------
 src/coreclr/vm/methodtablebuilder.cpp         |  2 ++
 2 files changed, 19 insertions(+), 8 deletions(-)

diff --git a/src/coreclr/System.Private.CoreLib/src/System/RuntimeType.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/RuntimeType.CoreCLR.cs
index 5e872581a02f60..5aad763334d045 100644
--- a/src/coreclr/System.Private.CoreLib/src/System/RuntimeType.CoreCLR.cs
+++ b/src/coreclr/System.Private.CoreLib/src/System/RuntimeType.CoreCLR.cs
@@ -552,7 +552,12 @@ private void MergeWithGlobalList(T[] list)
                                     // Grow the list by exactly one element in this case to avoid null entries at the end.
                                     //
 
-                                    Debug.Assert(false);
+                                    // TODO: runtime-async we need to rationalize how async2 thunks eork in reflection.
+                                    //       possibly they should not be exposed as they are runtime-provided implementation
+                                    //       details, that in theory may change.
+                                    //
+                                    //       For now I will disable this assert as we may get here with extra methods.
+                                    // Debug.Assert(false);
 
                                     newSize = cachedMembers.Length + 1;
                                 }
@@ -604,9 +609,9 @@ private unsafe RuntimeMethodInfo[] PopulateMethods(Filter filter)
                             MethodAttributes methodAttributes = RuntimeMethodHandle.GetAttributes(methodHandle);
 
                             #region Continue if this is a constructor
-                            Debug.Assert(
-                                (RuntimeMethodHandle.GetAttributes(methodHandle) & MethodAttributes.RTSpecialName) == 0 ||
-                                RuntimeMethodHandle.GetName(methodHandle).Equals(".cctor"));
+                            //Debug.Assert(
+                            //    (RuntimeMethodHandle.GetAttributes(methodHandle) & MethodAttributes.RTSpecialName) == 0 ||
+                            //    RuntimeMethodHandle.GetName(methodHandle).Equals(".cctor"));
 
                             if ((methodAttributes & MethodAttributes.RTSpecialName) != 0)
                                 continue;
@@ -663,10 +668,10 @@ private unsafe RuntimeMethodInfo[] PopulateMethods(Filter filter)
                                 MethodAttributes methodAccess = methodAttributes & MethodAttributes.MemberAccessMask;
 
                                 #region Continue if this is a constructor
-                                Debug.Assert(
-                                    (RuntimeMethodHandle.GetAttributes(methodHandle) & MethodAttributes.RTSpecialName) == 0 ||
-                                    RuntimeMethodHandle.GetName(methodHandle).Equals(".ctor") ||
-                                    RuntimeMethodHandle.GetName(methodHandle).Equals(".cctor"));
+                                //Debug.Assert(
+                                //    (RuntimeMethodHandle.GetAttributes(methodHandle) & MethodAttributes.RTSpecialName) == 0 ||
+                                //    RuntimeMethodHandle.GetName(methodHandle).Equals(".ctor") ||
+                                //    RuntimeMethodHandle.GetName(methodHandle).Equals(".cctor"));
 
                                 if ((methodAttributes & MethodAttributes.RTSpecialName) != 0)
                                     continue;
@@ -766,6 +771,10 @@ private RuntimeConstructorInfo[] PopulateConstructors(Filter filter)
                         if ((methodAttributes & MethodAttributes.RTSpecialName) == 0)
                             continue;
 
+                        if (!RuntimeMethodHandle.GetName(methodHandle).Equals(".ctor") &&
+                            !RuntimeMethodHandle.GetName(methodHandle).Equals(".cctor"))
+                            continue;
+
                         // Constructors should not be virtual or abstract
                         Debug.Assert(
                             (methodAttributes & MethodAttributes.Abstract) == 0 &&
diff --git a/src/coreclr/vm/methodtablebuilder.cpp b/src/coreclr/vm/methodtablebuilder.cpp
index 59ad52dce4987d..9635ca0ec1ef78 100644
--- a/src/coreclr/vm/methodtablebuilder.cpp
+++ b/src/coreclr/vm/methodtablebuilder.cpp
@@ -3719,6 +3719,8 @@ MethodTableBuilder::EnumerateClassMethods()
                     type,
                     implType);
 
+                _ASSERTE(pNewMethod->IsAsync2Variant());
+
                 pNewMethod->SetAsyncOtherVariant(pDeclaredMethod);
                 pDeclaredMethod->SetAsyncOtherVariant(pNewMethod);
             }

From f9facc364339a67b4ac6649c051e6d2e8207b5d5 Mon Sep 17 00:00:00 2001
From: vsadov <8218165+VSadov@users.noreply.github.com>
Date: Sun, 30 Mar 2025 23:41:48 -0700
Subject: [PATCH 170/203] more build fixes for RISCV

---
 src/coreclr/jit/codegenloongarch64.cpp | 40 +++++++++++++++---------
 src/coreclr/jit/codegenriscv64.cpp     | 42 ++++++++++++++++----------
 2 files changed, 51 insertions(+), 31 deletions(-)

diff --git a/src/coreclr/jit/codegenloongarch64.cpp b/src/coreclr/jit/codegenloongarch64.cpp
index d660a4ae63e634..e696a3e340d03e 100644
--- a/src/coreclr/jit/codegenloongarch64.cpp
+++ b/src/coreclr/jit/codegenloongarch64.cpp
@@ -827,6 +827,7 @@ void CodeGen::genFnEpilog(BasicBlock* block)
                                        0,          // argSize
                                        EA_UNKNOWN // retSize
                                        MULTIREG_HAS_SECOND_GC_RET_ONLY_ARG(EA_UNKNOWN), // secondRetSize
+                                       false, // hasAsyncRet
                                        gcInfo.gcVarPtrSetCur,
                                        gcInfo.gcRegGCrefSetCur,
                                        gcInfo.gcRegByrefSetCur,
@@ -2891,11 +2892,13 @@ void CodeGen::genCodeForReturnTrap(GenTreeOp* tree)
 
     // TODO-LOONGARCH64: can optimize further !!!
     GetEmitter()->emitIns_Call(callType, compiler->eeFindHelper(CORINFO_HELP_STOP_FOR_GC),
-                               INDEBUG_LDISASM_COMMA(nullptr) addr, 0, EA_UNKNOWN, EA_UNKNOWN, gcInfo.gcVarPtrSetCur,
-                               gcInfo.gcRegGCrefSetCur, gcInfo.gcRegByrefSetCur, DebugInfo(), /* IL offset */
-                               callTarget,                                                    /* ireg */
-                               REG_NA, 0, 0,                                                  /* xreg, xmul, disp */
-                               false                                                          /* isJump */
+                               INDEBUG_LDISASM_COMMA(nullptr) addr, 0, EA_UNKNOWN, EA_UNKNOWN,
+                               false, // hasAsyncRet
+                               gcInfo.gcVarPtrSetCur, gcInfo.gcRegGCrefSetCur, gcInfo.gcRegByrefSetCur,
+                               DebugInfo(),  /* IL offset */
+                               callTarget,   /* ireg */
+                               REG_NA, 0, 0, /* xreg, xmul, disp */
+                               false         /* isJump */
     );
 
     regMaskTP killMask = compiler->compHelperCallKillSet(CORINFO_HELP_STOP_FOR_GC);
@@ -3982,11 +3985,13 @@ void CodeGen::genEmitHelperCall(unsigned helper, int argSize, emitAttr retSize,
     }
 
     GetEmitter()->emitIns_Call(callType, compiler->eeFindHelper(helper), INDEBUG_LDISASM_COMMA(nullptr) addr, argSize,
-                               retSize, EA_UNKNOWN, gcInfo.gcVarPtrSetCur, gcInfo.gcRegGCrefSetCur,
-                               gcInfo.gcRegByrefSetCur, DebugInfo(), /* IL offset */
-                               callTarget,                           /* ireg */
-                               REG_NA, 0, 0,                         /* xreg, xmul, disp */
-                               false                                 /* isJump */
+                               retSize, EA_UNKNOWN,
+                               false, // hasAsyncRet
+                               gcInfo.gcVarPtrSetCur, gcInfo.gcRegGCrefSetCur, gcInfo.gcRegByrefSetCur,
+                               DebugInfo(),  /* IL offset */
+                               callTarget,   /* ireg */
+                               REG_NA, 0, 0, /* xreg, xmul, disp */
+                               false         /* isJump */
     );
 
     regMaskTP killMask = compiler->compHelperCallKillSet((CorInfoHelpFunc)helper);
@@ -6117,6 +6122,7 @@ void CodeGen::genCallInstruction(GenTreeCall* call)
                     nullptr, // addr
                     retSize
                     MULTIREG_HAS_SECOND_GC_RET_ONLY_ARG(secondRetSize),
+                    false,    // hasAsyncRet
                     di,
                     target->GetRegNum(),
                     call->IsFastTailCall());
@@ -6165,6 +6171,7 @@ void CodeGen::genCallInstruction(GenTreeCall* call)
                         nullptr, // addr
                         retSize
                         MULTIREG_HAS_SECOND_GC_RET_ONLY_ARG(secondRetSize),
+                        false,    // hasAsyncRet
                         di,
                         targetAddrReg,
                         call->IsFastTailCall());
@@ -6208,6 +6215,7 @@ void CodeGen::genCallInstruction(GenTreeCall* call)
                         addr,
                         retSize
                         MULTIREG_HAS_SECOND_GC_RET_ONLY_ARG(secondRetSize),
+                        false,    // hasAsyncRet
                         di,
                         REG_NA,
                         call->IsFastTailCall());
@@ -6972,11 +6980,13 @@ inline void CodeGen::genJumpToThrowHlpBlk_la(
         BasicBlock* skipLabel = genCreateTempLabel();
 
         emit->emitIns_Call(callType, compiler->eeFindHelper(compiler->acdHelper(codeKind)),
-                           INDEBUG_LDISASM_COMMA(nullptr) addr, 0, EA_UNKNOWN, EA_UNKNOWN, gcInfo.gcVarPtrSetCur,
-                           gcInfo.gcRegGCrefSetCur, gcInfo.gcRegByrefSetCur, DebugInfo(), /* IL offset */
-                           callTarget,                                                    /* ireg */
-                           REG_NA, 0, 0,                                                  /* xreg, xmul, disp */
-                           false                                                          /* isJump */
+                           INDEBUG_LDISASM_COMMA(nullptr) addr, 0, EA_UNKNOWN, EA_UNKNOWN,
+                           false, // hasAsyncRet
+                           gcInfo.gcVarPtrSetCur, gcInfo.gcRegGCrefSetCur, gcInfo.gcRegByrefSetCur,
+                           DebugInfo(),  /* IL offset */
+                           callTarget,   /* ireg */
+                           REG_NA, 0, 0, /* xreg, xmul, disp */
+                           false         /* isJump */
         );
 
         regMaskTP killMask = compiler->compHelperCallKillSet((CorInfoHelpFunc)(compiler->acdHelper(codeKind)));
diff --git a/src/coreclr/jit/codegenriscv64.cpp b/src/coreclr/jit/codegenriscv64.cpp
index db7abe9cf5e08a..9cf6746c6c0336 100644
--- a/src/coreclr/jit/codegenriscv64.cpp
+++ b/src/coreclr/jit/codegenriscv64.cpp
@@ -779,6 +779,7 @@ void CodeGen::genFnEpilog(BasicBlock* block)
                                        0,          // argSize
                                        EA_UNKNOWN // retSize
                                        MULTIREG_HAS_SECOND_GC_RET_ONLY_ARG(EA_UNKNOWN), // secondRetSize
+                                       false, // hasAsyncRet
                                        gcInfo.gcVarPtrSetCur,
                                        gcInfo.gcRegGCrefSetCur,
                                        gcInfo.gcRegByrefSetCur,
@@ -2823,11 +2824,13 @@ void CodeGen::genCodeForReturnTrap(GenTreeOp* tree)
 
     // TODO-RISCV64: can optimize further !!!
     GetEmitter()->emitIns_Call(callType, compiler->eeFindHelper(CORINFO_HELP_STOP_FOR_GC),
-                               INDEBUG_LDISASM_COMMA(nullptr) addr, 0, EA_UNKNOWN, EA_UNKNOWN, gcInfo.gcVarPtrSetCur,
-                               gcInfo.gcRegGCrefSetCur, gcInfo.gcRegByrefSetCur, DebugInfo(), /* IL offset */
-                               callTarget,                                                    /* ireg */
-                               REG_NA, 0, 0,                                                  /* xreg, xmul, disp */
-                               false                                                          /* isJump */
+                               INDEBUG_LDISASM_COMMA(nullptr) addr, 0, EA_UNKNOWN, EA_UNKNOWN,
+                               false, // hasAsyncRet
+                               gcInfo.gcVarPtrSetCur, gcInfo.gcRegGCrefSetCur, gcInfo.gcRegByrefSetCur,
+                               DebugInfo(),  /* IL offset */
+                               callTarget,   /* ireg */
+                               REG_NA, 0, 0, /* xreg, xmul, disp */
+                               false         /* isJump */
     );
 
     regMaskTP killMask = compiler->compHelperCallKillSet(CORINFO_HELP_STOP_FOR_GC);
@@ -3844,11 +3847,13 @@ void CodeGen::genEmitHelperCall(unsigned helper, int argSize, emitAttr retSize,
     }
 
     GetEmitter()->emitIns_Call(callType, compiler->eeFindHelper(helper), INDEBUG_LDISASM_COMMA(nullptr) addr, argSize,
-                               retSize, EA_UNKNOWN, gcInfo.gcVarPtrSetCur, gcInfo.gcRegGCrefSetCur,
-                               gcInfo.gcRegByrefSetCur, DebugInfo(), /* IL offset */
-                               callTarget,                           /* ireg */
-                               REG_NA, 0, 0,                         /* xreg, xmul, disp */
-                               false                                 /* isJump */
+                               retSize, EA_UNKNOWN,
+                               false, // hasAsyncRet
+                               gcInfo.gcVarPtrSetCur, gcInfo.gcRegGCrefSetCur, gcInfo.gcRegByrefSetCur,
+                               DebugInfo(),  /* IL offset */
+                               callTarget,   /* ireg */
+                               REG_NA, 0, 0, /* xreg, xmul, disp */
+                               false         /* isJump */
     );
 
     regMaskTP killMask = compiler->compHelperCallKillSet((CorInfoHelpFunc)helper);
@@ -6095,6 +6100,7 @@ void CodeGen::genCallInstruction(GenTreeCall* call)
                     nullptr, // addr
                     retSize
                     MULTIREG_HAS_SECOND_GC_RET_ONLY_ARG(secondRetSize),
+                    false,   // hasAsyncRet
                     di,
                     target->GetRegNum(),
                     call->IsFastTailCall());
@@ -6143,8 +6149,9 @@ void CodeGen::genCallInstruction(GenTreeCall* call)
                         nullptr, // addr
                         retSize
                         MULTIREG_HAS_SECOND_GC_RET_ONLY_ARG(secondRetSize),
+                        false,   // hasAsyncRet
                         di,
-                        targetAddrReg,
+\                       targetAddrReg,
                         call->IsFastTailCall());
             // clang-format on
         }
@@ -6186,6 +6193,7 @@ void CodeGen::genCallInstruction(GenTreeCall* call)
                         addr,
                         retSize
                         MULTIREG_HAS_SECOND_GC_RET_ONLY_ARG(secondRetSize),
+                        false,    // hasAsyncRet
                         di,
                         REG_NA,
                         call->IsFastTailCall());
@@ -6772,11 +6780,13 @@ void CodeGen::genJumpToThrowHlpBlk_la(
         BasicBlock* skipLabel = genCreateTempLabel();
 
         emit->emitIns_Call(callType, compiler->eeFindHelper(compiler->acdHelper(codeKind)),
-                           INDEBUG_LDISASM_COMMA(nullptr) addr, 0, EA_UNKNOWN, EA_UNKNOWN, gcInfo.gcVarPtrSetCur,
-                           gcInfo.gcRegGCrefSetCur, gcInfo.gcRegByrefSetCur, DebugInfo(), /* IL offset */
-                           callTarget,                                                    /* ireg */
-                           REG_NA, 0, 0,                                                  /* xreg, xmul, disp */
-                           false                                                          /* isJump */
+                           INDEBUG_LDISASM_COMMA(nullptr) addr, 0, EA_UNKNOWN, EA_UNKNOWN,
+                           false, // hasAsyncRet
+                           gcInfo.gcVarPtrSetCur, gcInfo.gcRegGCrefSetCur, gcInfo.gcRegByrefSetCur,
+                           DebugInfo(),  /* IL offset */
+                           callTarget,   /* ireg */
+                           REG_NA, 0, 0, /* xreg, xmul, disp */
+                           false         /* isJump */
         );
 
         regMaskTP killMask = compiler->compHelperCallKillSet((CorInfoHelpFunc)(compiler->acdHelper(codeKind)));

From d105671711ac28d49f3d99cc7c88ac6d519c6123 Mon Sep 17 00:00:00 2001
From: Jakob Botsch Nielsen <jakob.botsch.nielsen@gmail.com>
Date: Mon, 31 Mar 2025 15:04:57 +0200
Subject: [PATCH 171/203] Restore CODEOWNERS

---
 .github/CODEOWNERS | 115 +++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 115 insertions(+)
 create mode 100644 .github/CODEOWNERS

diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS
new file mode 100644
index 00000000000000..34e18599e5beaf
--- /dev/null
+++ b/.github/CODEOWNERS
@@ -0,0 +1,115 @@
+# Users referenced in this file will automatically be requested as reviewers for PRs that modify the given paths.
+# See https://help.github.com/articles/about-code-owners/
+
+/src/libraries/Common/src/Interop/                                  @dotnet/platform-deps-team
+/src/libraries/Common/src/System/Net/Http/aspnetcore/               @dotnet/http
+/src/libraries/Common/tests/Tests/System/Net/aspnetcore/            @dotnet/http
+
+# CoreCLR Code Owners
+
+/src/coreclr/inc/corinfo.h @dotnet/jit-contrib
+/src/coreclr/inc/corjit.h @dotnet/jit-contrib
+/src/coreclr/jit/ @dotnet/jit-contrib
+/src/coreclr/nativeaot @MichalStrehovsky
+/src/coreclr/tools/Common @dotnet/crossgen-contrib @MichalStrehovsky
+/src/coreclr/tools/aot @dotnet/crossgen-contrib
+/src/coreclr/tools/aot/ILCompiler.Compiler @MichalStrehovsky
+/src/coreclr/tools/aot/ILCompiler.RyuJit @MichalStrehovsky
+/src/coreclr/tools/aot/ILCompiler.MetadataTransform @MichalStrehovsky
+
+# Mono Code Owners
+
+/src/mono @steveisok @vitek-karas
+
+/src/mono/llvm @steveisok @vitek-karas
+
+/src/mono/mono/arch @steveisok @vitek-karas
+/src/mono/mono/eglib @steveisok @vitek-karas
+
+/src/mono/mono/metadata @thaystg @steveisok @vitek-karas
+/src/mono/mono/metadata/*-win* @lateralusX @steveisok
+/src/mono/mono/metadata/handle* @steveisok @vitek-karas
+/src/mono/mono/metadata/monitor* @brzvlad @steveisok @vitek-karas
+/src/mono/mono/metadata/sgen* @brzvlad @steveisok @vitek-karas
+/src/mono/mono/metadata/thread* @lateralusX @steveisok @vitek-karas
+/src/mono/mono/metadata/w32* @lateralusX @steveisok @vitek-karas
+
+/src/mono/mono/eventpipe @lateralusX @steveisok @vitek-karas
+
+/src/mono/mono/mini @steveisok @vitek-karas
+/src/mono/mono/mini/*cfgdump* @steveisok @vitek-karas
+/src/mono/mono/mini/*exceptions* @BrzVlad @steveisok @vitek-karas
+/src/mono/mono/mini/*llvm* @steveisok @vitek-karas
+/src/mono/mono/mini/*ppc* @steveisok @vitek-karas
+/src/mono/mono/mini/*profiler* @BrzVlad @steveisok @vitek-karas
+/src/mono/mono/mini/*riscv* @steveisok @vitek-karas
+/src/mono/mono/mini/*type-check* @steveisok @vitek-karas
+/src/mono/mono/mini/debugger-agent.c @thaystg @steveisok @vitek-karas
+/src/mono/mono/mini/interp/* @BrzVlad @kotlarmilos @steveisok @vitek-karas
+/src/mono/mono/mini/interp/*jiterp* @kg @steveisok @vitek-karas
+/src/mono/mono/mini/*simd* @steveisok @vitek-karas
+
+/src/mono/mono/profiler @BrzVlad @steveisok @vitek-karas
+/src/mono/mono/sgen @BrzVlad @steveisok @vitek-karas
+
+/src/mono/mono/utils @steveisok @vitek-karas
+/src/mono/mono/utils/*-win* @lateralusX @steveisok @vitek-karas
+/src/mono/mono/utils/atomic* @steveisok @vitek-karas
+/src/mono/mono/utils/mono-hwcap* @steveisok @vitek-karas
+/src/mono/mono/utils/mono-mem* @steveisok @vitek-karas
+/src/mono/mono/utils/mono-threads* @steveisok @vitek-karas
+
+/src/mono/dlls @thaystg @steveisok @vitek-karas
+
+/src/native/public/mono @steveisok @vitek-karas
+/src/native/external/libunwind @janvorli @AaronRobinsonMSFT @dotnet/dotnet-diag
+/src/native/external/libunwind_extras @janvorli @AaronRobinsonMSFT @dotnet/dotnet-diag
+
+/src/libraries/sendtohelix-browser.targets @akoeplinger
+/src/libraries/sendtohelix-wasm.targets @akoeplinger
+/src/libraries/sendtohelix-wasi.targets @akoeplinger
+/src/mono/browser @lewing @pavelsavara
+/src/mono/wasi @lewing @pavelsavara
+/src/mono/wasm @lewing @pavelsavara
+/src/mono/browser/debugger @thaystg @ilonatommy
+/src/mono/wasm/build @maraf @akoeplinger
+/src/mono/wasi/build @maraf @akoeplinger
+/src/mono/browser/build @maraf @akoeplinger
+/src/mono/sample/wasm @lewing @pavelsavara
+/src/mono/sample/wasi @lewing @pavelsavara
+/src/libraries/System.Runtime.InteropServices.JavaScript @lewing @pavelsavara
+
+/src/mono/nuget/*WebAssembly*/ @lewing @akoeplinger
+/src/mono/nuget/*MonoTargets*/ @lewing @akoeplinger
+/src/mono/nuget/*BrowserDebugHost*/ @lewing @akoeplinger
+/src/mono/nuget/*Workload.Mono.Toolchain*/ @lewing @akoeplinger
+/src/mono/nuget/*MonoAOTCompiler*/ @lewing @akoeplinger
+
+/src/mono/wasm/Wasm* @maraf @ilonatommy
+/src/mono/wasm/testassets @maraf @ilonatommy
+/src/mono/wasi/testassets @maraf @ilonatommy
+/src/tasks/WasmAppBuilder/ @maraf @akoeplinger
+/src/tasks/WorkloadBuildTasks/ @akoeplinger
+/src/tasks/AotCompilerTask/ @akoeplinger
+/src/tasks/WasmBuildTasks/ @maraf @akoeplinger
+
+/eng/pipelines/**/*wasm* @akoeplinger
+
+# ILLink codeowners
+/src/tools/illink/ @marek-safar
+/src/tools/illink/src/analyzer/ @radekdoulik
+/src/tools/illink/src/ILLink.Tasks/ @sbomer
+/src/tools/illink/src/ILLink.RoslynAnalyzer/ @sbomer
+/src/tools/illink/src/linker/ @marek-safar @mrvoorhe
+/src/tools/illink/test/ @marek-safar @mrvoorhe
+
+# Obsoletions / Custom Diagnostics
+
+/docs/project/list-of-diagnostics.md                @jeffhandley
+/src/libraries/Common/src/System/Obsoletions.cs     @jeffhandley
+
+# Area ownership and repo automation
+/docs/area-owners.*                                 @jeffhandley
+/docs/issue*.md                                     @jeffhandley
+/.github/policies/                                  @jeffhandley @mkArtakMSFT
+/.github/workflows/                                 @jeffhandley @dotnet/runtime-infrastructure

From b8450d93cd545f8c96eeaebcda7e7a9639605097 Mon Sep 17 00:00:00 2001
From: Jakob Botsch Nielsen <jakob.botsch.nielsen@gmail.com>
Date: Mon, 31 Mar 2025 15:07:15 +0200
Subject: [PATCH 172/203] Add license headers for new files

---
 src/coreclr/jit/async.cpp | 3 +++
 src/coreclr/jit/async.h   | 3 +++
 2 files changed, 6 insertions(+)

diff --git a/src/coreclr/jit/async.cpp b/src/coreclr/jit/async.cpp
index 8d69a2986e505f..798f59a0ea1855 100644
--- a/src/coreclr/jit/async.cpp
+++ b/src/coreclr/jit/async.cpp
@@ -1,3 +1,6 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
 #include "jitpch.h"
 #include "jitstd/algorithm.h"
 #include "async.h"
diff --git a/src/coreclr/jit/async.h b/src/coreclr/jit/async.h
index 6f572c0dbf37ab..4ea0b6b805f8bf 100644
--- a/src/coreclr/jit/async.h
+++ b/src/coreclr/jit/async.h
@@ -1,3 +1,6 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
 class Async2Transformation
 {
     friend class AsyncLiveness;

From d796485f17b80154d1936ae5d9ddfe5fd6fe5ff3 Mon Sep 17 00:00:00 2001
From: Jakob Botsch Nielsen <jakob.botsch.nielsen@gmail.com>
Date: Mon, 31 Mar 2025 15:07:34 +0200
Subject: [PATCH 173/203] Delete unused file

---
 .../vm/restoreregs_for_runtimesuspension.h    | 22 -------------------
 1 file changed, 22 deletions(-)
 delete mode 100644 src/coreclr/vm/restoreregs_for_runtimesuspension.h

diff --git a/src/coreclr/vm/restoreregs_for_runtimesuspension.h b/src/coreclr/vm/restoreregs_for_runtimesuspension.h
deleted file mode 100644
index 689be7bf3fbf0e..00000000000000
--- a/src/coreclr/vm/restoreregs_for_runtimesuspension.h
+++ /dev/null
@@ -1,22 +0,0 @@
-#if defined(TARGET_AMD64)
-    DISCOVER_RESTORED_REG(Rbx)
-    DISCOVER_RESTORED_REG(Rbp)
-    DISCOVER_RESTORED_REG(Rdi)
-    DISCOVER_RESTORED_REG(Rsi)
-    DISCOVER_RESTORED_REG(R12)
-    DISCOVER_RESTORED_REG(R13)
-    DISCOVER_RESTORED_REG(R14)
-    DISCOVER_RESTORED_REG(R15)
-    DISCOVER_RESTORED_REG(Xmm6)
-    DISCOVER_RESTORED_REG(Xmm7)
-    DISCOVER_RESTORED_REG(Xmm8)
-    DISCOVER_RESTORED_REG(Xmm9)
-    DISCOVER_RESTORED_REG(Xmm10)
-    DISCOVER_RESTORED_REG(Xmm11)
-    DISCOVER_RESTORED_REG(Xmm12)
-    DISCOVER_RESTORED_REG(Xmm13)
-    DISCOVER_RESTORED_REG(Xmm14)
-    DISCOVER_RESTORED_REG(Xmm15)
-#else
-    PORTABILITY_ASSERT();
-#endif

From 66c8239a861e1afe4f12d431ecccf0dfe578e83b Mon Sep 17 00:00:00 2001
From: Jakob Botsch Nielsen <jakob.botsch.nielsen@gmail.com>
Date: Mon, 31 Mar 2025 15:10:09 +0200
Subject: [PATCH 174/203] Undo whitespace change

---
 src/coreclr/vm/codeman.cpp | 1 -
 1 file changed, 1 deletion(-)

diff --git a/src/coreclr/vm/codeman.cpp b/src/coreclr/vm/codeman.cpp
index 21cdf2bf1764e7..0be5ceff63d423 100644
--- a/src/coreclr/vm/codeman.cpp
+++ b/src/coreclr/vm/codeman.cpp
@@ -4286,7 +4286,6 @@ BOOL EECodeGenManager::JitCodeToMethodInfoWorker(
 
     if (ppMethodDesc)
     {
-
         *ppMethodDesc = pCHdr->GetMethodDesc();
     }
     return TRUE;

From 3190a605c252a61d64d2bbf085a62fc006ee0de0 Mon Sep 17 00:00:00 2001
From: Jakob Botsch Nielsen <jakob.botsch.nielsen@gmail.com>
Date: Mon, 31 Mar 2025 15:14:18 +0200
Subject: [PATCH 175/203] Remove new unused code slot methods

---
 src/coreclr/vm/codeversion.cpp | 19 -------------------
 src/coreclr/vm/codeversion.h   |  2 --
 src/coreclr/vm/method.hpp      |  1 -
 src/coreclr/vm/prestub.cpp     |  6 ------
 4 files changed, 28 deletions(-)

diff --git a/src/coreclr/vm/codeversion.cpp b/src/coreclr/vm/codeversion.cpp
index a2ff35497459cd..f67f3a4b72066d 100644
--- a/src/coreclr/vm/codeversion.cpp
+++ b/src/coreclr/vm/codeversion.cpp
@@ -85,12 +85,6 @@ PCODE NativeCodeVersionNode::GetNativeCode() const
     return m_pNativeCode;
 }
 
-PTR_PCODE NativeCodeVersionNode::GetNativeCodeSlot() const
-{
-    LIMITED_METHOD_DAC_CONTRACT;
-    return dac_cast<PTR_PCODE>(&m_pNativeCode);
-}
-
 ReJITID NativeCodeVersionNode::GetILVersionId() const
 {
     LIMITED_METHOD_DAC_CONTRACT;
@@ -227,19 +221,6 @@ PCODE NativeCodeVersion::GetNativeCode() const
     }
 }
 
-PTR_PCODE NativeCodeVersion::GetNativeCodeSlot() const
-{
-    LIMITED_METHOD_DAC_CONTRACT;
-    if (m_storageKind == StorageKind::Explicit)
-    {
-        return AsNode()->GetNativeCodeSlot();
-    }
-    else
-    {
-        return GetMethodDesc()->GetAddrOfSlot();
-    }
-}
-
 ReJITID NativeCodeVersion::GetILCodeVersionId() const
 {
     LIMITED_METHOD_DAC_CONTRACT;
diff --git a/src/coreclr/vm/codeversion.h b/src/coreclr/vm/codeversion.h
index 1c95aa0d2464f7..d4e973bdfbdcac 100644
--- a/src/coreclr/vm/codeversion.h
+++ b/src/coreclr/vm/codeversion.h
@@ -61,7 +61,6 @@ class NativeCodeVersion
     NativeCodeVersionId GetVersionId() const;
     BOOL IsDefaultVersion() const;
     PCODE GetNativeCode() const;
-    PTR_PCODE GetNativeCodeSlot() const;
 
 #ifdef FEATURE_CODE_VERSIONING
     ILCodeVersion GetILCodeVersion() const;
@@ -269,7 +268,6 @@ class NativeCodeVersionNode
     PTR_MethodDesc GetMethodDesc() const; // Can be called without any locks
     NativeCodeVersionId GetVersionId() const; // Can be called without any locks
     PCODE GetNativeCode() const; // Can be called without any locks, but result may be stale if it wasn't already set
-    PTR_PCODE GetNativeCodeSlot() const;
     ReJITID GetILVersionId() const; // Can be called without any locks
     ILCodeVersion GetILCodeVersion() const;// Can be called without any locks
     BOOL IsActiveChildVersion() const;
diff --git a/src/coreclr/vm/method.hpp b/src/coreclr/vm/method.hpp
index 507d2c9e194afc..9151b35df01d5d 100644
--- a/src/coreclr/vm/method.hpp
+++ b/src/coreclr/vm/method.hpp
@@ -2121,7 +2121,6 @@ class PrepareCodeConfig
     BOOL MayUsePrecompiledCode();
     virtual PCODE IsJitCancellationRequested();
     virtual BOOL SetNativeCode(PCODE pCode, PCODE * ppAlternateCodeToUse);
-    virtual PTR_PCODE GetNativeCodeSlot();
     virtual COR_ILMETHOD* GetILHeader();
     virtual CORJIT_FLAGS GetJitCompilationFlags();
 #ifdef FEATURE_ON_STACK_REPLACEMENT
diff --git a/src/coreclr/vm/prestub.cpp b/src/coreclr/vm/prestub.cpp
index 19d02e1481ef57..dd61a75bf08ffb 100644
--- a/src/coreclr/vm/prestub.cpp
+++ b/src/coreclr/vm/prestub.cpp
@@ -1757,12 +1757,6 @@ BOOL PrepareCodeConfig::SetNativeCode(PCODE pCode, PCODE * ppAlternateCodeToUse)
     return FALSE;
 }
 
-PTR_PCODE PrepareCodeConfig::GetNativeCodeSlot()
-{
-    LIMITED_METHOD_CONTRACT;
-    return m_nativeCodeVersion.GetNativeCodeSlot();
-}
-
 COR_ILMETHOD* PrepareCodeConfig::GetILHeader()
 {
     STANDARD_VM_CONTRACT;

From e6078fcb4b0799b880d10c0de7aa71474562971c Mon Sep 17 00:00:00 2001
From: Jakob Botsch Nielsen <jakob.botsch.nielsen@gmail.com>
Date: Mon, 31 Mar 2025 15:21:09 +0200
Subject: [PATCH 176/203] Remove new unused EmitUNBOX method

---
 src/coreclr/vm/stubgen.cpp | 8 +-------
 src/coreclr/vm/stubgen.h   | 1 -
 2 files changed, 1 insertion(+), 8 deletions(-)

diff --git a/src/coreclr/vm/stubgen.cpp b/src/coreclr/vm/stubgen.cpp
index 6a29c1962e92ba..401b5b9baa2097 100644
--- a/src/coreclr/vm/stubgen.cpp
+++ b/src/coreclr/vm/stubgen.cpp
@@ -174,7 +174,7 @@ void ILCodeStream::Emit(ILInstrEnum instr, INT16 iStackDelta, UINT_PTR uArg)
     pInstrBuffer[idxCurInstr].iStackDelta = iStackDelta;
     pInstrBuffer[idxCurInstr].uArg = uArg;
 
-    if(m_buildingEHClauses.GetCount() > 0)
+    if (m_buildingEHClauses.GetCount() > 0)
     {
         ILStubEHClauseBuilder& clause = m_buildingEHClauses[m_buildingEHClauses.GetCount() - 1];
 
@@ -1832,12 +1832,6 @@ void ILCodeStream::EmitUNALIGNED(BYTE alignment)
     Emit(CEE_UNALIGNED, 0, alignment);
 }
 
-void ILCodeStream::EmitUNBOX(int token)
-{
-    WRAPPER_NO_CONTRACT;
-    Emit(CEE_UNBOX, 0, token);
-}
-
 void ILCodeStream::EmitUNBOX_ANY(int token)
 {
     WRAPPER_NO_CONTRACT;
diff --git a/src/coreclr/vm/stubgen.h b/src/coreclr/vm/stubgen.h
index c848b0665008fa..b3590071386cb3 100644
--- a/src/coreclr/vm/stubgen.h
+++ b/src/coreclr/vm/stubgen.h
@@ -943,7 +943,6 @@ class ILCodeStream
     void EmitSUB        ();
     void EmitTHROW      ();
     void EmitUNALIGNED  (BYTE alignment);
-    void EmitUNBOX      (int token);
     void EmitUNBOX_ANY  (int token);
 
     // Overloads to simplify common usage patterns

From 479d4f404d999afffefb844056815fcfdab32689 Mon Sep 17 00:00:00 2001
From: Jakob Botsch Nielsen <jakob.botsch.nielsen@gmail.com>
Date: Mon, 31 Mar 2025 15:21:32 +0200
Subject: [PATCH 177/203] Revert a logging change

---
 src/coreclr/vm/stubgen.cpp | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/coreclr/vm/stubgen.cpp b/src/coreclr/vm/stubgen.cpp
index 401b5b9baa2097..18eed6274dd2db 100644
--- a/src/coreclr/vm/stubgen.cpp
+++ b/src/coreclr/vm/stubgen.cpp
@@ -834,10 +834,10 @@ size_t ILStubLinker::Link(UINT* puMaxStack)
     }
 
 #ifdef _DEBUG
-    if (fStackUnderflow || this->GetNumEHClauses() != 0)
+    if (fStackUnderflow)
     {
         LogILStub(CORJIT_FLAGS());
-//        CONSISTENCY_CHECK_MSG(false, "IL stack underflow! -- see logging output");
+        CONSISTENCY_CHECK_MSG(false, "IL stack underflow! -- see logging output");
     }
 #endif // _DEBUG
 

From 33d026d175d8f8402bebaa08148913c096032a95 Mon Sep 17 00:00:00 2001
From: Vladimir Sadov <vsadov@microsoft.com>
Date: Mon, 31 Mar 2025 09:29:38 -0700
Subject: [PATCH 178/203] Update src/coreclr/jit/codegenriscv64.cpp

Co-authored-by: Adeel Mujahid <3840695+am11@users.noreply.github.com>
---
 src/coreclr/jit/codegenriscv64.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/coreclr/jit/codegenriscv64.cpp b/src/coreclr/jit/codegenriscv64.cpp
index 9cf6746c6c0336..da6c9e06ecd1f7 100644
--- a/src/coreclr/jit/codegenriscv64.cpp
+++ b/src/coreclr/jit/codegenriscv64.cpp
@@ -6151,7 +6151,7 @@ void CodeGen::genCallInstruction(GenTreeCall* call)
                         MULTIREG_HAS_SECOND_GC_RET_ONLY_ARG(secondRetSize),
                         false,   // hasAsyncRet
                         di,
-\                       targetAddrReg,
+                        targetAddrReg,
                         call->IsFastTailCall());
             // clang-format on
         }

From 21981a989a7db37524b224d942f182b65b30f337 Mon Sep 17 00:00:00 2001
From: Jakob Botsch Nielsen <jakob.botsch.nielsen@gmail.com>
Date: Mon, 31 Mar 2025 15:36:38 +0200
Subject: [PATCH 179/203] Remove unused IsInCurrentFrame

---
 src/coreclr/inc/regdisp.h | 7 -------
 1 file changed, 7 deletions(-)

diff --git a/src/coreclr/inc/regdisp.h b/src/coreclr/inc/regdisp.h
index a8ffd5909f78a6..8f1f2a736be256 100644
--- a/src/coreclr/inc/regdisp.h
+++ b/src/coreclr/inc/regdisp.h
@@ -271,13 +271,6 @@ inline BOOL IsInCalleesFrames(REGDISPLAY *display, LPVOID stackPointer)
     return stackPointer < ((LPVOID)(display->SP));
 }
 
-inline BOOL IsInCurrentFrame(REGDISPLAY *display, LPVOID stackPointer)
-{
-    LIMITED_METHOD_CONTRACT;
-    _ASSERTE(display->IsCallerContextValid);
-    return stackPointer < ((LPVOID)(::GetSP(display->pCallerContext)));
-}
-
 inline TADDR GetRegdisplayStackMark(REGDISPLAY *display)
 {
 #if defined(TARGET_AMD64)

From 5299ec70144e038929333cbf5fbe53155a0b98f6 Mon Sep 17 00:00:00 2001
From: Jakob Botsch Nielsen <jakob.botsch.nielsen@gmail.com>
Date: Mon, 31 Mar 2025 15:38:57 +0200
Subject: [PATCH 180/203] Remove unused new CLASSID_SYSTEM_BYTE

---
 src/coreclr/inc/corinfo.h                             | 1 -
 src/coreclr/jit/async.cpp                             | 2 --
 src/coreclr/jit/async.h                               | 2 --
 src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs  | 3 ---
 src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs | 1 -
 src/coreclr/vm/jitinterface.cpp                       | 3 ---
 6 files changed, 12 deletions(-)

diff --git a/src/coreclr/inc/corinfo.h b/src/coreclr/inc/corinfo.h
index 57e78ba06a8558..fa8fea85f360bd 100644
--- a/src/coreclr/inc/corinfo.h
+++ b/src/coreclr/inc/corinfo.h
@@ -863,7 +863,6 @@ enum CorInfoGCType
 enum CorInfoClassId
 {
     CLASSID_SYSTEM_OBJECT,
-    CLASSID_SYSTEM_BYTE,
     CLASSID_TYPED_BYREF,
     CLASSID_TYPE_HANDLE,
     CLASSID_FIELD_HANDLE,
diff --git a/src/coreclr/jit/async.cpp b/src/coreclr/jit/async.cpp
index 798f59a0ea1855..b5366bf176064b 100644
--- a/src/coreclr/jit/async.cpp
+++ b/src/coreclr/jit/async.cpp
@@ -210,8 +210,6 @@ PhaseStatus Async2Transformation::Run()
     m_comp->lvaGetDesc(m_newContinuationVar)->lvType      = TYP_REF;
 
     m_comp->info.compCompHnd->getAsync2Info(&m_async2Info);
-    m_objectClsHnd = m_comp->info.compCompHnd->getBuiltinClass(CLASSID_SYSTEM_OBJECT);
-    m_byteClsHnd   = m_comp->info.compCompHnd->getBuiltinClass(CLASSID_SYSTEM_BYTE);
 
 #ifdef JIT32_GCENCODER
     // Due to a hard cap on epilogs we need a shared return here.
diff --git a/src/coreclr/jit/async.h b/src/coreclr/jit/async.h
index 4ea0b6b805f8bf..046f9976556363 100644
--- a/src/coreclr/jit/async.h
+++ b/src/coreclr/jit/async.h
@@ -23,8 +23,6 @@ class Async2Transformation
     Compiler*                     m_comp;
     jitstd::vector<LiveLocalInfo> m_liveLocals;
     CORINFO_ASYNC2_INFO           m_async2Info;
-    CORINFO_CLASS_HANDLE          m_objectClsHnd;
-    CORINFO_CLASS_HANDLE          m_byteClsHnd;
     jitstd::vector<BasicBlock*>   m_resumptionBBs;
     CORINFO_METHOD_HANDLE         m_resumeStub = NO_METHOD_HANDLE;
     CORINFO_CONST_LOOKUP          m_resumeStubLookup;
diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs
index 4fc03f3e8346c2..495f9b85512261 100644
--- a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs
+++ b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs
@@ -2786,9 +2786,6 @@ private CorInfoInitClassResult initClass(CORINFO_FIELD_STRUCT_* field, CORINFO_M
                 case CorInfoClassId.CLASSID_SYSTEM_OBJECT:
                     return ObjectToHandle(_compilation.TypeSystemContext.GetWellKnownType(WellKnownType.Object));
 
-                case CorInfoClassId.CLASSID_SYSTEM_BYTE:
-                    return ObjectToHandle(_compilation.TypeSystemContext.GetWellKnownType(WellKnownType.Byte));
-
                 case CorInfoClassId.CLASSID_TYPED_BYREF:
                     return ObjectToHandle(_compilation.TypeSystemContext.GetWellKnownType(WellKnownType.TypedReference));
 
diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs b/src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs
index 785c90785b6165..05e6c372766c88 100644
--- a/src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs
+++ b/src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs
@@ -469,7 +469,6 @@ public enum CorInfoGCType
     public enum CorInfoClassId
     {
         CLASSID_SYSTEM_OBJECT,
-        CLASSID_SYSTEM_BYTE,
         CLASSID_TYPED_BYREF,
         CLASSID_TYPE_HANDLE,
         CLASSID_FIELD_HANDLE,
diff --git a/src/coreclr/vm/jitinterface.cpp b/src/coreclr/vm/jitinterface.cpp
index 696695d1a62e7a..b452018ad59d93 100644
--- a/src/coreclr/vm/jitinterface.cpp
+++ b/src/coreclr/vm/jitinterface.cpp
@@ -3973,9 +3973,6 @@ CORINFO_CLASS_HANDLE CEEInfo::getBuiltinClass(CorInfoClassId classId)
     case CLASSID_SYSTEM_OBJECT:
         result = CORINFO_CLASS_HANDLE(g_pObjectClass);
         break;
-    case CLASSID_SYSTEM_BYTE:
-        result = CORINFO_CLASS_HANDLE(CoreLibBinder::GetClass(CLASS__BYTE));
-        break;
     case CLASSID_TYPED_BYREF:
         result = CORINFO_CLASS_HANDLE(g_TypedReferenceMT);
         break;

From 688466b3c4abeda2a6429dee4e71799444c78c33 Mon Sep 17 00:00:00 2001
From: Jakob Botsch Nielsen <jakob.botsch.nielsen@gmail.com>
Date: Mon, 31 Mar 2025 17:33:20 +0200
Subject: [PATCH 181/203] Refactor and split up async2 transformation

---
 src/coreclr/jit/async.cpp | 1367 +++++++++++++++++++++----------------
 src/coreclr/jit/async.h   |   79 ++-
 2 files changed, 861 insertions(+), 585 deletions(-)

diff --git a/src/coreclr/jit/async.cpp b/src/coreclr/jit/async.cpp
index b5366bf176064b..34731e1c42a672 100644
--- a/src/coreclr/jit/async.cpp
+++ b/src/coreclr/jit/async.cpp
@@ -5,14 +5,6 @@
 #include "jitstd/algorithm.h"
 #include "async.h"
 
-PhaseStatus Compiler::TransformAsync2()
-{
-    assert(compIsAsync2());
-
-    Async2Transformation transformation(this);
-    return transformation.Run();
-}
-
 class AsyncLiveness
 {
     Compiler*              m_comp;
@@ -29,158 +21,235 @@ class AsyncLiveness
     {
     }
 
-    void StartBlock(BasicBlock* block)
-    {
-        if (!m_hasLiveness)
-            return;
+    void StartBlock(BasicBlock* block);
+    void Update(GenTree* node);
+    bool IsLive(unsigned lclNum);
+    void GetLiveLocals(jitstd::vector<Async2Transformation::LiveLocalInfo>& liveLocals, unsigned fullyDefinedRetBufLcl);
 
-        VarSetOps::Assign(m_comp, m_comp->compCurLife, block->bbLiveIn);
-    }
+private:
+    bool IsLocalCaptureUnnecessary(unsigned lclNum);
+};
 
-    void Update(GenTree* node)
-    {
-        if (!m_hasLiveness)
-            return;
+//------------------------------------------------------------------------
+// AsyncLiveness::StartBlock:
+//   Indicate that we are now starting a new block, and do relevant liveness
+//   updates for it.
+//
+// Parameters:
+//   block - The block that we are starting.
+//
+void AsyncLiveness::StartBlock(BasicBlock* block)
+{
+    if (!m_hasLiveness)
+        return;
 
-        m_updater.UpdateLife(node);
-    }
+    VarSetOps::Assign(m_comp, m_comp->compCurLife, block->bbLiveIn);
+}
 
-    bool IsLocalCaptureUnnecessary(unsigned lclNum)
-    {
+//------------------------------------------------------------------------
+// AsyncLiveness::Update:
+//   Update liveness to be consistent with the specified node having been
+//   executed.
+//
+// Parameters:
+//   node - The node.
+//
+void AsyncLiveness::Update(GenTree* node)
+{
+    if (!m_hasLiveness)
+        return;
+
+    m_updater.UpdateLife(node);
+}
+
+//------------------------------------------------------------------------
+// AsyncLiveness::IsLocalCaptureUnnecessary:
+//   Check if capturing a specified local can be skipped.
+//
+// Parameters:
+//   lclNum - The local
+//
+// Returns:
+//   True if the local should not be captured. Even without liveness
+//
+bool AsyncLiveness::IsLocalCaptureUnnecessary(unsigned lclNum)
+{
 #if FEATURE_FIXED_OUT_ARGS
-        if (lclNum == m_comp->lvaOutgoingArgSpaceVar)
-        {
-            return true;
-        }
+    if (lclNum == m_comp->lvaOutgoingArgSpaceVar)
+    {
+        return true;
+    }
 #endif
 
-        if (lclNum == m_comp->info.compRetBuffArg)
-        {
-            return true;
-        }
+    if (lclNum == m_comp->info.compRetBuffArg)
+    {
+        return true;
+    }
 
-        if (lclNum == m_comp->lvaGSSecurityCookie)
-        {
-            // Initialized in prolog
-            return true;
-        }
+    if (lclNum == m_comp->lvaGSSecurityCookie)
+    {
+        // Initialized in prolog
+        return true;
+    }
 
-        if (lclNum == m_comp->lvaPSPSym)
-        {
-            // Initialized in prolog
-            return true;
-        }
+    if (lclNum == m_comp->lvaPSPSym)
+    {
+        // Initialized in prolog
+        return true;
+    }
 
-        if (lclNum == m_comp->info.compLvFrameListRoot)
-        {
-            return true;
-        }
+    if (lclNum == m_comp->info.compLvFrameListRoot)
+    {
+        return true;
+    }
 
-        if (lclNum == m_comp->lvaInlinedPInvokeFrameVar)
-        {
-            return true;
-        }
+    if (lclNum == m_comp->lvaInlinedPInvokeFrameVar)
+    {
+        return true;
+    }
 
 #ifdef FEATURE_EH_WINDOWS_X86
-        if (lclNum == m_comp->lvaShadowSPslotsVar)
-        {
-            // Only expected to be live in handlers
-            return true;
-        }
+    if (lclNum == m_comp->lvaShadowSPslotsVar)
+    {
+        // Only expected to be live in handlers
+        return true;
+    }
 #endif
 
-        if (lclNum == m_comp->lvaRetAddrVar)
-        {
-            return true;
-        }
+    if (lclNum == m_comp->lvaRetAddrVar)
+    {
+        return true;
+    }
 
-        if (lclNum == m_comp->lvaAsyncContinuationArg)
-        {
-            return true;
-        }
+    if (lclNum == m_comp->lvaAsyncContinuationArg)
+    {
+        return true;
+    }
 
+    return false;
+}
+
+//------------------------------------------------------------------------
+// AsyncLiveness::IsLive:
+//   Check if the specified local is live at this point and should be captured.
+//
+// Parameters:
+//   lclNum - The local
+//
+// Returns:
+//   True if the local is live and capturing it is necessary.
+//
+bool AsyncLiveness::IsLive(unsigned lclNum)
+{
+    if (IsLocalCaptureUnnecessary(lclNum))
+    {
         return false;
     }
 
-    bool IsLive(unsigned lclNum)
-    {
-        if (IsLocalCaptureUnnecessary(lclNum))
-        {
-            return false;
-        }
+    LclVarDsc* dsc = m_comp->lvaGetDesc(lclNum);
 
-        LclVarDsc* dsc = m_comp->lvaGetDesc(lclNum);
+    if ((dsc->TypeGet() == TYP_BYREF) || ((dsc->TypeGet() == TYP_STRUCT) && dsc->GetLayout()->HasGCByRef()))
+    {
+        // Even if these are address exposed we expect them to be dead at
+        // suspension points. TODO: It would be good to somehow verify these
+        // aren't obviously live, if the JIT creates live ranges that span a
+        // suspension point then this makes it quite hard to diagnose that.
+        return false;
+    }
 
-        if ((dsc->TypeGet() == TYP_BYREF) || ((dsc->TypeGet() == TYP_STRUCT) && dsc->GetLayout()->HasGCByRef()))
-        {
-            // Even if these are address exposed we expect them to be dead at
-            // suspension points. TODO: It would be good to somehow verify these
-            // aren't obviously live, if the JIT creates live ranges that span a
-            // suspension point then this makes it quite hard to diagnose that.
-            return false;
-        }
+    if (!m_hasLiveness)
+    {
+        return true;
+    }
 
-        if (!m_hasLiveness)
-        {
-            return true;
-        }
+    if (dsc->lvRefCnt(RCS_NORMAL) == 0)
+    {
+        return false;
+    }
 
-        if (dsc->lvRefCnt(RCS_NORMAL) == 0)
-        {
-            return false;
-        }
+    Compiler::lvaPromotionType promoType = m_comp->lvaGetPromotionType(dsc);
+    if (promoType == Compiler::PROMOTION_TYPE_INDEPENDENT)
+    {
+        // Independently promoted structs are handled only through their
+        // fields.
+        return false;
+    }
 
-        Compiler::lvaPromotionType promoType = m_comp->lvaGetPromotionType(dsc);
-        if (promoType == Compiler::PROMOTION_TYPE_INDEPENDENT)
-        {
-            // Independently promoted structs are handled only through their
-            // fields.
-            return false;
-        }
+    if (promoType == Compiler::PROMOTION_TYPE_DEPENDENT)
+    {
+        // Dependently promoted structs are handled only through the base
+        // struct local.
+        //
+        // A dependently promoted struct is live if any of its fields are live.
 
-        if (promoType == Compiler::PROMOTION_TYPE_DEPENDENT)
+        for (unsigned i = 0; i < dsc->lvFieldCnt; i++)
         {
-            // Dependently promoted structs are handled only through the base
-            // struct local.
-            //
-            // A dependently promoted struct is live if any of its fields are live.
-
-            for (unsigned i = 0; i < dsc->lvFieldCnt; i++)
+            LclVarDsc* fieldDsc = m_comp->lvaGetDesc(dsc->lvFieldLclStart + i);
+            if (!fieldDsc->lvTracked || VarSetOps::IsMember(m_comp, m_comp->compCurLife, fieldDsc->lvVarIndex))
             {
-                LclVarDsc* fieldDsc = m_comp->lvaGetDesc(dsc->lvFieldLclStart + i);
-                if (!fieldDsc->lvTracked || VarSetOps::IsMember(m_comp, m_comp->compCurLife, fieldDsc->lvVarIndex))
-                {
-                    return true;
-                }
+                return true;
             }
-
-            return false;
         }
 
-        if (dsc->lvIsStructField && (m_comp->lvaGetParentPromotionType(dsc) == Compiler::PROMOTION_TYPE_DEPENDENT))
-        {
-            return false;
-        }
+        return false;
+    }
 
-        return !dsc->lvTracked || VarSetOps::IsMember(m_comp, m_comp->compCurLife, dsc->lvVarIndex);
+    if (dsc->lvIsStructField && (m_comp->lvaGetParentPromotionType(dsc) == Compiler::PROMOTION_TYPE_DEPENDENT))
+    {
+        return false;
     }
 
-    void GetLiveLocals(jitstd::vector<Async2Transformation::LiveLocalInfo>& liveLocals, unsigned fullyDefinedRetBufLcl)
+    return !dsc->lvTracked || VarSetOps::IsMember(m_comp, m_comp->compCurLife, dsc->lvVarIndex);
+}
+
+//------------------------------------------------------------------------
+// AsyncLiveness::GetLiveLocals:
+//   Get live locals that should be captured at this point.
+//
+// Parameters:
+//   liveLocals            - Vector to add live local information into
+//   fullyDefinedRetBufLcl - Local to skip even if live
+//
+void AsyncLiveness::GetLiveLocals(jitstd::vector<Async2Transformation::LiveLocalInfo>& liveLocals,
+                                  unsigned                                             fullyDefinedRetBufLcl)
+{
+    for (unsigned lclNum = 0; lclNum < m_numVars; lclNum++)
     {
-        for (unsigned lclNum = 0; lclNum < m_numVars; lclNum++)
+        if ((lclNum != fullyDefinedRetBufLcl) && IsLive(lclNum))
         {
-            if ((lclNum != fullyDefinedRetBufLcl) && IsLive(lclNum))
-            {
-                liveLocals.push_back(Async2Transformation::LiveLocalInfo(lclNum));
-            }
+            liveLocals.push_back(Async2Transformation::LiveLocalInfo(lclNum));
         }
     }
-};
+}
+
+//------------------------------------------------------------------------
+// TransformAsync2: Run async2 transformation.
+//
+// Returns:
+//   Suitable phase status.
+//
+PhaseStatus Compiler::TransformAsync2()
+{
+    assert(compIsAsync2());
 
+    Async2Transformation transformation(this);
+    return transformation.Run();
+}
+
+//------------------------------------------------------------------------
+// Async2Transformation::Run:
+//   Run the transformation over all the IR.
+//
+// Returns:
+//   Suitable phase status.
+//
 PhaseStatus Async2Transformation::Run()
 {
     ArrayStack<BasicBlock*> worklist(m_comp->getAllocator(CMK_Async2));
 
+    // First find all basic blocks with awaits in them. We'll have to track
+    // liveness in these basic blocks, so it does not help to record the calls
+    // ahead of time.
     for (BasicBlock* block : m_comp->Blocks())
     {
         for (GenTree* tree : LIR::AsRange(block))
@@ -201,6 +270,9 @@ PhaseStatus Async2Transformation::Run()
         return PhaseStatus::MODIFIED_NOTHING;
     }
 
+    // Ask the VM to create a resumption stub for this specific version of the
+    // code. It is stored in the continuation as a function pointer, so we need
+    // the fixed entry point here.
     m_resumeStub = m_comp->info.compCompHnd->getAsyncResumptionStub();
     m_comp->info.compCompHnd->getFunctionFixedEntryPoint(m_resumeStub, false, &m_resumeStubLookup);
 
@@ -227,6 +299,8 @@ PhaseStatus Async2Transformation::Run()
     DISPRANGE(LIR::AsRange(m_sharedReturnBB));
 #endif
 
+    // Compute liveness to be used for determining what must be captured on
+    // suspension. In unoptimized codegen we capture everything.
     if (m_comp->opts.OptimizationEnabled())
     {
         if (m_comp->m_dfsTree == nullptr)
@@ -241,6 +315,9 @@ PhaseStatus Async2Transformation::Run()
 
     AsyncLiveness liveness(m_comp, m_comp->opts.OptimizationEnabled());
 
+    // Now walk the IR for all the blocks that contain async2 calls. Keep track
+    // of liveness and outstanding LIR edges as we go; the LIR edges that cross
+    // async2 calls are additional live variables that must be spilled.
     jitstd::vector<GenTree*> defs(m_comp->getAllocator(CMK_Async2));
 
     for (int i = 0; i < worklist.Height(); i++)
@@ -256,6 +333,8 @@ PhaseStatus Async2Transformation::Run()
             any = false;
             for (GenTree* tree : LIR::AsRange(block))
             {
+                // Remove all consumed defs; those are no longer 'live' LIR
+                // edges.
                 tree->VisitOperands([&defs](GenTree* op) {
                     if (op->IsValue())
                     {
@@ -273,16 +352,20 @@ PhaseStatus Async2Transformation::Run()
                     return GenTree::VisitResult::Continue;
                 });
 
+                // Update liveness to reflect state after this node.
                 liveness.Update(tree);
 
                 if (tree->IsCall() && tree->AsCall()->IsAsync2() && !tree->AsCall()->IsTailCall())
                 {
+                    // Transform call; continue with the remainder block
                     Transform(block, tree->AsCall(), defs, liveness, &block);
                     defs.clear();
                     any = true;
                     break;
                 }
 
+                // Push a new definition if necessary; this defined value is
+                // now a live LIR edge.
                 if (tree->IsValue() && !tree->IsUnusedValue())
                 {
                     defs.push_back(tree);
@@ -291,6 +374,8 @@ PhaseStatus Async2Transformation::Run()
         } while (any);
     }
 
+    // After transforming all async calls we have created resumption blocks;
+    // create the resumption switch.
     CreateResumptionSwitch();
 
     m_comp->fgInvalidateDfsTree();
@@ -298,8 +383,19 @@ PhaseStatus Async2Transformation::Run()
     return PhaseStatus::MODIFIED_EVERYTHING;
 }
 
+//------------------------------------------------------------------------
+// Async2Transformation::Transform:
+//   Transform a single async2 call in the specified block.
+//
+// Parameters:
+//   block     - The block containig the async2 call
+//   call      - The async2 call
+//   defs      - Current live LIR edges
+//   life      - Liveness information about live locals
+//   remainder - [out] Remainder block after the transformation
+//
 void Async2Transformation::Transform(
-    BasicBlock* block, GenTreeCall* call, jitstd::vector<GenTree*>& defs, AsyncLiveness& life, BasicBlock** pRemainder)
+    BasicBlock* block, GenTreeCall* call, jitstd::vector<GenTree*>& defs, AsyncLiveness& life, BasicBlock** remainder)
 {
 #ifdef DEBUG
     if (m_comp->verbose)
@@ -321,8 +417,45 @@ void Async2Transformation::Transform(
     }
 #endif
 
-    m_liveLocals.clear();
+    m_liveLocalsScratch.clear();
+    jitstd::vector<LiveLocalInfo>& liveLocals = m_liveLocalsScratch;
+
+    CreateLiveSetForSuspension(block, call, defs, life, liveLocals);
+
+    ContinuationLayout layout = LayOutContinuation(block, call, liveLocals);
+
+    CallDefinitionInfo callDefInfo = CanonicalizeCallDefinition(block, call, life);
+
+    unsigned stateNum = (unsigned)m_resumptionBBs.size();
+    JITDUMP("  Assigned state %u\n", stateNum);
+
+    BasicBlock* suspendBB = CreateSuspension(block, stateNum, layout, life, liveLocals);
 
+    CreateCheckAndSuspendAfterCall(block, callDefInfo, life, suspendBB, remainder);
+
+    BasicBlock* resumeBB = CreateResumption(block, *remainder, call, callDefInfo, stateNum, layout, liveLocals);
+
+    m_resumptionBBs.push_back(resumeBB);
+}
+
+//------------------------------------------------------------------------
+// Async2Transformation::CreateLiveSetForSuspension:
+//   Create the set of live state to be captured for suspension, for the
+//   specified call.
+//
+// Parameters:
+//   block        - The block containig the async2 call
+//   call         - The async2 call
+//   defs         - Current live LIR edges
+//   life         - Liveness information about live locals
+//   livenessInfo - Information about live state
+//
+void Async2Transformation::CreateLiveSetForSuspension(BasicBlock*                     block,
+                                                      GenTreeCall*                    call,
+                                                      const jitstd::vector<GenTree*>& defs,
+                                                      AsyncLiveness&                  life,
+                                                      jitstd::vector<LiveLocalInfo>&  liveLocals)
+{
     unsigned fullyDefinedRetBufLcl = BAD_VAR_NUM;
     CallArg* retbufArg             = call->gtArgs.GetRetBufferArg();
     if (retbufArg != nullptr)
@@ -343,18 +476,18 @@ void Async2Transformation::Transform(
         }
     }
 
-    life.GetLiveLocals(m_liveLocals, fullyDefinedRetBufLcl);
-    LiftLIREdges(block, call, defs, m_liveLocals);
+    life.GetLiveLocals(liveLocals, fullyDefinedRetBufLcl);
+    LiftLIREdges(block, call, defs, liveLocals);
 
 #ifdef DEBUG
     if (m_comp->verbose)
     {
-        printf("  %zu live locals\n", m_liveLocals.size());
+        printf("  %zu live locals\n", liveLocals.size());
 
-        if (m_liveLocals.size() > 0)
+        if (liveLocals.size() > 0)
         {
             const char* sep = "    ";
-            for (LiveLocalInfo& inf : m_liveLocals)
+            for (LiveLocalInfo& inf : liveLocals)
             {
                 printf("%sV%02u (%s)", sep, inf.LclNum, varTypeName(m_comp->lvaGetDesc(inf.LclNum)->TypeGet()));
                 sep = ", ";
@@ -364,8 +497,15 @@ void Async2Transformation::Transform(
         }
     }
 #endif
+}
 
-    for (LiveLocalInfo& inf : m_liveLocals)
+ContinuationLayout Async2Transformation::LayOutContinuation(BasicBlock*                    block,
+                                                            GenTreeCall*                   call,
+                                                            jitstd::vector<LiveLocalInfo>& liveLocals)
+{
+    ContinuationLayout layout;
+
+    for (LiveLocalInfo& inf : liveLocals)
     {
         LclVarDsc* dsc = m_comp->lvaGetDesc(inf.LclNum);
 
@@ -411,7 +551,7 @@ void Async2Transformation::Transform(
         }
     }
 
-    jitstd::sort(m_liveLocals.begin(), m_liveLocals.end(), [](const LiveLocalInfo& lhs, const LiveLocalInfo& rhs) {
+    jitstd::sort(liveLocals.begin(), liveLocals.end(), [](const LiveLocalInfo& lhs, const LiveLocalInfo& rhs) {
         if (lhs.Alignment == rhs.Alignment)
         {
             // Prefer lowest local num first for same alignment.
@@ -422,93 +562,86 @@ void Async2Transformation::Transform(
         return lhs.Alignment > rhs.Alignment;
     });
 
-    unsigned dataSize    = 0;
-    unsigned gcRefsCount = 0;
-
     // For OSR, we store the transition IL offset at the beginning of the data
     // (-1 in the tier0 version):
     if (m_comp->doesMethodHavePatchpoints() || m_comp->opts.IsOSR())
     {
         JITDUMP("  Method %s; keeping an IL offset at the beginning of non-GC data\n",
                 m_comp->doesMethodHavePatchpoints() ? "has patchpoints" : "is an OSR method");
-        dataSize += sizeof(int);
+        layout.DataSize += sizeof(int);
     }
 
-    ClassLayout* returnStructLayout = nullptr;
-    unsigned     returnSize         = 0;
-    bool         returnInGCData     = false;
     if (call->gtReturnType == TYP_STRUCT)
     {
-        returnStructLayout = m_comp->typGetObjLayout(call->gtRetClsHnd);
-        returnSize         = returnStructLayout->GetSize();
-        returnInGCData     = returnStructLayout->HasGCPtr();
+        layout.ReturnStructLayout = m_comp->typGetObjLayout(call->gtRetClsHnd);
+        layout.ReturnSize         = layout.ReturnStructLayout->GetSize();
+        layout.ReturnInGCData     = layout.ReturnStructLayout->HasGCPtr();
     }
     else
     {
-        returnSize     = genTypeSize(call->gtReturnType);
-        returnInGCData = varTypeIsGC(call->gtReturnType);
+        layout.ReturnSize     = genTypeSize(call->gtReturnType);
+        layout.ReturnInGCData = varTypeIsGC(call->gtReturnType);
     }
 
-    assert((returnSize > 0) == (call->gtReturnType != TYP_VOID));
+    assert((layout.ReturnSize > 0) == (call->gtReturnType != TYP_VOID));
 
     // The return value is always stored:
     // 1. At index 0 in GCData if it is a TYP_REF or a struct with GC references
     // 2. At index 0 in Data, for non OSR methods without GC ref returns
     // 3. At index 4 in Data for OSR methods without GC ref returns. The
     // continuation flags indicates this scenario with a flag.
-    unsigned returnValDataOffset = UINT_MAX;
-    if (returnInGCData)
+    if (layout.ReturnInGCData)
     {
-        gcRefsCount++;
+        layout.GCRefsCount++;
     }
-    else if (returnSize > 0)
+    else if (layout.ReturnSize > 0)
     {
-        returnValDataOffset = dataSize;
-        dataSize += returnSize;
+        layout.ReturnValDataOffset = layout.DataSize;
+        layout.DataSize += layout.ReturnSize;
     }
 
 #ifdef DEBUG
-    if (returnSize > 0)
+    if (layout.ReturnSize > 0)
     {
         JITDUMP("  Will store return of type %s, size %u in",
-                call->gtReturnType == TYP_STRUCT ? returnStructLayout->GetClassName() : varTypeName(call->gtReturnType),
-                returnSize);
+                call->gtReturnType == TYP_STRUCT ? layout.ReturnStructLayout->GetClassName()
+                                                 : varTypeName(call->gtReturnType),
+                layout.ReturnSize);
 
-        if (returnInGCData)
+        if (layout.ReturnInGCData)
         {
             JITDUMP(" GC data\n");
         }
         else
         {
-            JITDUMP(" non-GC data at offset %u\n", returnValDataOffset);
+            JITDUMP(" non-GC data at offset %u\n", layout.ReturnValDataOffset);
         }
     }
 #endif
 
-    unsigned exceptionGCDataIndex = UINT_MAX;
     if (block->hasTryIndex())
     {
-        exceptionGCDataIndex = gcRefsCount++;
+        layout.ExceptionGCDataIndex = layout.GCRefsCount++;
         JITDUMP("  " FMT_BB " is in try region %u; exception will be at GC@+%02u in GC data\n", block->bbNum,
-                block->getTryIndex(), exceptionGCDataIndex);
+                block->getTryIndex(), layout.ExceptionGCDataIndex);
     }
 
-    for (LiveLocalInfo& inf : m_liveLocals)
+    for (LiveLocalInfo& inf : liveLocals)
     {
-        dataSize = roundUp(dataSize, inf.Alignment);
+        layout.DataSize = roundUp(layout.DataSize, inf.Alignment);
 
-        inf.DataOffset  = dataSize;
-        inf.GCDataIndex = gcRefsCount;
+        inf.DataOffset  = layout.DataSize;
+        inf.GCDataIndex = layout.GCRefsCount;
 
-        dataSize += inf.DataSize;
-        gcRefsCount += inf.GCDataCount;
+        layout.DataSize += inf.DataSize;
+        layout.GCRefsCount += inf.GCDataCount;
     }
 
 #ifdef DEBUG
     if (m_comp->verbose)
     {
-        printf("  Continuation layout (%u bytes, %u GC pointers):\n", dataSize, gcRefsCount);
-        for (LiveLocalInfo& inf : m_liveLocals)
+        printf("  Continuation layout (%u bytes, %u GC pointers):\n", layout.DataSize, layout.GCRefsCount);
+        for (LiveLocalInfo& inf : liveLocals)
         {
             printf("    +%03u (GC@+%02u) V%02u: %u bytes, %u GC pointers\n", inf.DataOffset, inf.GCDataIndex,
                    inf.LclNum, inf.DataSize, inf.GCDataCount);
@@ -516,11 +649,18 @@ void Async2Transformation::Transform(
     }
 #endif
 
-    unsigned stateNum = (unsigned)m_resumptionBBs.size();
-    JITDUMP("  Assigned state %u\n", stateNum);
+    return layout;
+}
+
+CallDefinitionInfo Async2Transformation::CanonicalizeCallDefinition(BasicBlock*    block,
+                                                                    GenTreeCall*   call,
+                                                                    AsyncLiveness& life)
+{
+    CallDefinitionInfo callDefInfo;
 
-    GenTreeLclVarCommon* storeResultNode = nullptr;
-    GenTree*             insertAfter     = call;
+    callDefInfo.InsertAfter = call;
+
+    CallArg* retbufArg = call->gtArgs.GetRetBufferArg();
 
     if (!call->TypeIs(TYP_VOID) && !call->IsUnusedValue())
     {
@@ -541,8 +681,8 @@ void Async2Transformation::Transform(
         }
 
         assert(call->gtNext->OperIsLocalStore() && (call->gtNext->Data() == call));
-        storeResultNode = call->gtNext->AsLclVarCommon();
-        insertAfter     = call->gtNext;
+        callDefInfo.DefinitionNode = call->gtNext->AsLclVarCommon();
+        callDefInfo.InsertAfter    = call->gtNext;
     }
 
     if (retbufArg != nullptr)
@@ -556,28 +696,18 @@ void Async2Transformation::Transform(
         // introducing copies in the importer on the synchronous path.
         noway_assert(retbufArg->GetNode()->OperIs(GT_LCL_ADDR));
 
-        storeResultNode = retbufArg->GetNode()->AsLclVarCommon();
+        callDefInfo.DefinitionNode = retbufArg->GetNode()->AsLclVarCommon();
     }
 
-    GenTree* continuationArg = new (m_comp, GT_ASYNC_CONTINUATION) GenTree(GT_ASYNC_CONTINUATION, TYP_REF);
-    continuationArg->SetHasOrderingSideEffect();
-
-    GenTree* storeContinuation = m_comp->gtNewStoreLclVarNode(m_returnedContinuationVar, continuationArg);
-    LIR::AsRange(block).InsertAfter(insertAfter, continuationArg, storeContinuation);
-
-    GenTree* null                 = m_comp->gtNewNull();
-    GenTree* returnedContinuation = m_comp->gtNewLclvNode(m_returnedContinuationVar, TYP_REF);
-    GenTree* neNull               = m_comp->gtNewOperNode(GT_NE, TYP_INT, returnedContinuation, null);
-    GenTree* jtrue                = m_comp->gtNewOperNode(GT_JTRUE, TYP_VOID, neNull);
-
-    LIR::AsRange(block).InsertAfter(storeContinuation, null, returnedContinuation, neNull, jtrue);
-    BasicBlock* remainder = m_comp->fgSplitBlockAfterNode(block, jtrue);
-    *pRemainder           = remainder;
-
-    JITDUMP("  Remainder is " FMT_BB "\n", remainder->bbNum);
-
-    assert(block->KindIs(BBJ_ALWAYS) && block->TargetIs(remainder));
+    return callDefInfo;
+}
 
+BasicBlock* Async2Transformation::CreateSuspension(BasicBlock*                    block,
+                                                   unsigned                       stateNum,
+                                                   const ContinuationLayout&      layout,
+                                                   AsyncLiveness&                 life,
+                                                   jitstd::vector<LiveLocalInfo>& liveLocals)
+{
     if (m_lastSuspensionBB == nullptr)
     {
         m_lastSuspensionBB = m_comp->fgLastBBInMainFunction();
@@ -594,18 +724,13 @@ void Async2Transformation::Transform(
         suspendBB->SetKindAndTargetEdge(BBJ_ALWAYS, m_comp->fgAddRefPred(m_sharedReturnBB, suspendBB));
     }
 
-    JITDUMP("  Created suspension " FMT_BB " for state %u\n", suspendBB->bbNum, stateNum);
-
-    FlowEdge* retBBEdge = m_comp->fgAddRefPred(suspendBB, block);
-    block->SetCond(retBBEdge, block->GetTargetEdge());
-
-    block->GetTrueEdge()->setLikelihood(0);
-    block->GetFalseEdge()->setLikelihood(1);
+    JITDUMP("  Creating suspension " FMT_BB " for state %u\n", suspendBB->bbNum, stateNum);
 
     // Allocate continuation
-    returnedContinuation = m_comp->gtNewLclvNode(m_returnedContinuationVar, TYP_REF);
+    GenTree* returnedContinuation = m_comp->gtNewLclvNode(m_returnedContinuationVar, TYP_REF);
 
-    GenTreeCall* allocContinuation = CreateAllocContinuationCall(life, returnedContinuation, gcRefsCount, dataSize);
+    GenTreeCall* allocContinuation =
+        CreateAllocContinuationCall(life, returnedContinuation, layout.GCRefsCount, layout.DataSize);
 
     m_comp->compCurBB = suspendBB;
     m_comp->fgMorphTree(allocContinuation);
@@ -631,7 +756,7 @@ void Async2Transformation::Transform(
 
     // Fill in 'flags'
     unsigned continuationFlags = 0;
-    if (returnInGCData)
+    if (layout.ReturnInGCData)
         continuationFlags |= CORINFO_CONTINUATION_RESULT_IN_GCDATA;
     if (block->hasTryIndex())
         continuationFlags |= CORINFO_CONTINUATION_NEEDS_EXCEPTION;
@@ -644,167 +769,257 @@ void Async2Transformation::Transform(
     GenTree* storeFlags  = StoreAtOffset(newContinuation, flagsOffset, flagsNode);
     LIR::AsRange(suspendBB).InsertAtEnd(LIR::SeqTree(m_comp, storeFlags));
 
-    // Fill in GC pointers
-    if (gcRefsCount > 0)
+    if (layout.GCRefsCount > 0)
     {
-        unsigned objectArrLclNum = GetGCDataArrayVar();
+        FillInGCPointersOnSuspension(liveLocals, suspendBB);
+    }
 
-        newContinuation       = m_comp->gtNewLclvNode(m_newContinuationVar, TYP_REF);
-        unsigned gcDataOffset = m_comp->info.compCompHnd->getFieldOffset(m_async2Info.continuationGCDataFldHnd);
-        GenTree* gcDataInd    = LoadFromOffset(newContinuation, gcDataOffset, TYP_REF);
-        GenTree* storeAllocedObjectArr = m_comp->gtNewStoreLclVarNode(objectArrLclNum, gcDataInd);
-        LIR::AsRange(suspendBB).InsertAtEnd(LIR::SeqTree(m_comp, storeAllocedObjectArr));
+    if (layout.DataSize > 0)
+    {
+        FillInDataOnSuspension(liveLocals, suspendBB);
+    }
+
+    if (suspendBB->KindIs(BBJ_RETURN))
+    {
+        newContinuation = m_comp->gtNewLclvNode(m_newContinuationVar, TYP_REF);
+        GenTree* ret    = m_comp->gtNewOperNode(GT_RETURN_SUSPEND, TYP_VOID, newContinuation);
+        LIR::AsRange(suspendBB).InsertAtEnd(newContinuation, ret);
+    }
+
+    return suspendBB;
+}
 
-        for (LiveLocalInfo& inf : m_liveLocals)
+GenTreeCall* Async2Transformation::CreateAllocContinuationCall(AsyncLiveness& life,
+                                                               GenTree*       prevContinuation,
+                                                               unsigned       gcRefsCount,
+                                                               unsigned       dataSize)
+{
+    GenTree* gcRefsCountNode = m_comp->gtNewIconNode((ssize_t)gcRefsCount, TYP_I_IMPL);
+    GenTree* dataSizeNode    = m_comp->gtNewIconNode((ssize_t)dataSize, TYP_I_IMPL);
+    // If VM requests that we report the method handle, or if we have a shared generic context method handle
+    // that is live here, then we need to call a different helper.
+    GenTree* methodHandleArg = nullptr;
+    GenTree* classHandleArg  = nullptr;
+    if (((m_comp->info.compMethodInfo->options & CORINFO_GENERICS_CTXT_FROM_METHODDESC) != 0) &&
+        life.IsLive(m_comp->info.compTypeCtxtArg))
+    {
+        methodHandleArg = m_comp->gtNewLclvNode(m_comp->info.compTypeCtxtArg, TYP_I_IMPL);
+    }
+    else if (((m_comp->info.compMethodInfo->options & CORINFO_GENERICS_CTXT_FROM_METHODTABLE) != 0) &&
+             life.IsLive(m_comp->info.compTypeCtxtArg))
+    {
+        classHandleArg = m_comp->gtNewLclvNode(m_comp->info.compTypeCtxtArg, TYP_I_IMPL);
+    }
+    else if (m_async2Info.continuationsNeedMethodHandle)
+    {
+        methodHandleArg = m_comp->gtNewIconEmbMethHndNode(m_comp->info.compMethodHnd);
+    }
+
+    if (methodHandleArg != nullptr)
+    {
+        return m_comp->gtNewHelperCallNode(CORINFO_HELP_ALLOC_CONTINUATION_METHOD, TYP_REF, prevContinuation,
+                                           gcRefsCountNode, dataSizeNode, methodHandleArg);
+    }
+
+    if (classHandleArg != nullptr)
+    {
+        return m_comp->gtNewHelperCallNode(CORINFO_HELP_ALLOC_CONTINUATION_CLASS, TYP_REF, prevContinuation,
+                                           gcRefsCountNode, dataSizeNode, classHandleArg);
+    }
+
+    return m_comp->gtNewHelperCallNode(CORINFO_HELP_ALLOC_CONTINUATION, TYP_REF, prevContinuation, gcRefsCountNode,
+                                       dataSizeNode);
+}
+
+void Async2Transformation::FillInGCPointersOnSuspension(jitstd::vector<LiveLocalInfo>& liveLocals,
+                                                        BasicBlock*                    suspendBB)
+{
+    unsigned objectArrLclNum = GetGCDataArrayVar();
+
+    GenTree* newContinuation       = m_comp->gtNewLclvNode(m_newContinuationVar, TYP_REF);
+    unsigned gcDataOffset          = m_comp->info.compCompHnd->getFieldOffset(m_async2Info.continuationGCDataFldHnd);
+    GenTree* gcDataInd             = LoadFromOffset(newContinuation, gcDataOffset, TYP_REF);
+    GenTree* storeAllocedObjectArr = m_comp->gtNewStoreLclVarNode(objectArrLclNum, gcDataInd);
+    LIR::AsRange(suspendBB).InsertAtEnd(LIR::SeqTree(m_comp, storeAllocedObjectArr));
+
+    for (LiveLocalInfo& inf : liveLocals)
+    {
+        if (inf.GCDataCount <= 0)
         {
-            if (inf.GCDataCount <= 0)
-            {
-                continue;
-            }
+            continue;
+        }
 
-            LclVarDsc* dsc = m_comp->lvaGetDesc(inf.LclNum);
-            if (dsc->TypeGet() == TYP_REF)
+        LclVarDsc* dsc = m_comp->lvaGetDesc(inf.LclNum);
+        if (dsc->TypeGet() == TYP_REF)
+        {
+            GenTree* value     = m_comp->gtNewLclvNode(inf.LclNum, TYP_REF);
+            GenTree* objectArr = m_comp->gtNewLclvNode(objectArrLclNum, TYP_REF);
+            GenTree* store =
+                StoreAtOffset(objectArr, OFFSETOF__CORINFO_Array__data + (inf.GCDataIndex * TARGET_POINTER_SIZE),
+                              value);
+            LIR::AsRange(suspendBB).InsertAtEnd(LIR::SeqTree(m_comp, store));
+        }
+        else
+        {
+            assert((dsc->TypeGet() == TYP_STRUCT) || dsc->IsImplicitByRef());
+            ClassLayout* layout     = dsc->GetLayout();
+            unsigned     numSlots   = layout->GetSlotCount();
+            unsigned     gcRefIndex = 0;
+            for (unsigned i = 0; i < numSlots; i++)
             {
-                GenTree* value     = m_comp->gtNewLclvNode(inf.LclNum, TYP_REF);
+                var_types gcPtrType = layout->GetGCPtrType(i);
+                assert((gcPtrType == TYP_I_IMPL) || (gcPtrType == TYP_REF));
+                if (gcPtrType != TYP_REF)
+                {
+                    continue;
+                }
+
+                GenTree* value;
+                if (dsc->IsImplicitByRef())
+                {
+                    GenTree* baseAddr = m_comp->gtNewLclvNode(inf.LclNum, dsc->TypeGet());
+                    value             = LoadFromOffset(baseAddr, i * TARGET_POINTER_SIZE, TYP_REF);
+                }
+                else
+                {
+                    value = m_comp->gtNewLclFldNode(inf.LclNum, TYP_REF, i * TARGET_POINTER_SIZE);
+                }
+
                 GenTree* objectArr = m_comp->gtNewLclvNode(objectArrLclNum, TYP_REF);
-                GenTree* store =
-                    StoreAtOffset(objectArr, OFFSETOF__CORINFO_Array__data + (inf.GCDataIndex * TARGET_POINTER_SIZE),
-                                  value);
+                unsigned offset =
+                    OFFSETOF__CORINFO_Array__data + ((inf.GCDataIndex + gcRefIndex) * TARGET_POINTER_SIZE);
+                GenTree* store = StoreAtOffset(objectArr, offset, value);
                 LIR::AsRange(suspendBB).InsertAtEnd(LIR::SeqTree(m_comp, store));
-            }
-            else
-            {
-                assert((dsc->TypeGet() == TYP_STRUCT) || dsc->IsImplicitByRef());
-                ClassLayout* layout     = dsc->GetLayout();
-                unsigned     numSlots   = layout->GetSlotCount();
-                unsigned     gcRefIndex = 0;
-                for (unsigned i = 0; i < numSlots; i++)
+
+                gcRefIndex++;
+
+                if (inf.DataSize > 0)
                 {
-                    var_types gcPtrType = layout->GetGCPtrType(i);
-                    assert((gcPtrType == TYP_I_IMPL) || (gcPtrType == TYP_REF));
-                    if (gcPtrType != TYP_REF)
-                    {
-                        continue;
-                    }
+                    // Null out the GC field in preparation of storing the rest.
+                    GenTree* null = m_comp->gtNewNull();
 
-                    GenTree* value;
                     if (dsc->IsImplicitByRef())
                     {
                         GenTree* baseAddr = m_comp->gtNewLclvNode(inf.LclNum, dsc->TypeGet());
-                        value             = LoadFromOffset(baseAddr, i * TARGET_POINTER_SIZE, TYP_REF);
+                        store             = StoreAtOffset(baseAddr, i * TARGET_POINTER_SIZE, null);
                     }
                     else
                     {
-                        value = m_comp->gtNewLclFldNode(inf.LclNum, TYP_REF, i * TARGET_POINTER_SIZE);
+                        store = m_comp->gtNewStoreLclFldNode(inf.LclNum, TYP_REF, i * TARGET_POINTER_SIZE, null);
                     }
 
-                    GenTree* objectArr = m_comp->gtNewLclvNode(objectArrLclNum, TYP_REF);
-                    unsigned offset =
-                        OFFSETOF__CORINFO_Array__data + ((inf.GCDataIndex + gcRefIndex) * TARGET_POINTER_SIZE);
-                    GenTree* store = StoreAtOffset(objectArr, offset, value);
                     LIR::AsRange(suspendBB).InsertAtEnd(LIR::SeqTree(m_comp, store));
+                }
+            }
 
-                    gcRefIndex++;
+            m_comp->lvaSetVarDoNotEnregister(inf.LclNum DEBUGARG(DoNotEnregisterReason::LocalField));
+        }
+    }
+}
 
-                    if (inf.DataSize > 0)
-                    {
-                        // Null out the GC field in preparation of storing the rest.
-                        GenTree* null = m_comp->gtNewNull();
+void Async2Transformation::FillInDataOnSuspension(jitstd::vector<LiveLocalInfo>& liveLocals, BasicBlock* suspendBB)
+{
+    unsigned byteArrLclNum = GetDataArrayVar();
 
-                        if (dsc->IsImplicitByRef())
-                        {
-                            GenTree* baseAddr = m_comp->gtNewLclvNode(inf.LclNum, dsc->TypeGet());
-                            store             = StoreAtOffset(baseAddr, i * TARGET_POINTER_SIZE, null);
-                        }
-                        else
-                        {
-                            store = m_comp->gtNewStoreLclFldNode(inf.LclNum, TYP_REF, i * TARGET_POINTER_SIZE, null);
-                        }
+    GenTree* newContinuation     = m_comp->gtNewLclvNode(m_newContinuationVar, TYP_REF);
+    unsigned dataOffset          = m_comp->info.compCompHnd->getFieldOffset(m_async2Info.continuationDataFldHnd);
+    GenTree* dataInd             = LoadFromOffset(newContinuation, dataOffset, TYP_REF);
+    GenTree* storeAllocedByteArr = m_comp->gtNewStoreLclVarNode(byteArrLclNum, dataInd);
+    LIR::AsRange(suspendBB).InsertAtEnd(LIR::SeqTree(m_comp, storeAllocedByteArr));
 
-                        LIR::AsRange(suspendBB).InsertAtEnd(LIR::SeqTree(m_comp, store));
-                    }
-                }
+    if (m_comp->doesMethodHavePatchpoints() || m_comp->opts.IsOSR())
+    {
+        GenTree* ilOffsetToStore;
+        if (m_comp->doesMethodHavePatchpoints())
+            ilOffsetToStore = m_comp->gtNewIconNode(-1);
+        else
+            ilOffsetToStore = m_comp->gtNewIconNode((int)m_comp->info.compILEntry);
 
-                m_comp->lvaSetVarDoNotEnregister(inf.LclNum DEBUGARG(DoNotEnregisterReason::LocalField));
-            }
-        }
+        GenTree* byteArr               = m_comp->gtNewLclvNode(byteArrLclNum, TYP_REF);
+        unsigned offset                = OFFSETOF__CORINFO_Array__data;
+        GenTree* storePatchpointOffset = StoreAtOffset(byteArr, offset, ilOffsetToStore);
+        LIR::AsRange(suspendBB).InsertAtEnd(LIR::SeqTree(m_comp, storePatchpointOffset));
     }
 
-    // Store data in byte[]
-    if (dataSize > 0)
+    // Fill in data
+    for (LiveLocalInfo& inf : liveLocals)
     {
-        unsigned byteArrLclNum = GetDataArrayVar();
+        if (inf.DataSize <= 0)
+        {
+            continue;
+        }
 
-        GenTree* newContinuation     = m_comp->gtNewLclvNode(m_newContinuationVar, TYP_REF);
-        unsigned dataOffset          = m_comp->info.compCompHnd->getFieldOffset(m_async2Info.continuationDataFldHnd);
-        GenTree* dataInd             = LoadFromOffset(newContinuation, dataOffset, TYP_REF);
-        GenTree* storeAllocedByteArr = m_comp->gtNewStoreLclVarNode(byteArrLclNum, dataInd);
-        LIR::AsRange(suspendBB).InsertAtEnd(LIR::SeqTree(m_comp, storeAllocedByteArr));
+        LclVarDsc* dsc = m_comp->lvaGetDesc(inf.LclNum);
 
-        if (m_comp->doesMethodHavePatchpoints() || m_comp->opts.IsOSR())
-        {
-            GenTree* ilOffsetToStore;
-            if (m_comp->doesMethodHavePatchpoints())
-                ilOffsetToStore = m_comp->gtNewIconNode(-1);
-            else
-                ilOffsetToStore = m_comp->gtNewIconNode((int)m_comp->info.compILEntry);
+        GenTree* byteArr = m_comp->gtNewLclvNode(byteArrLclNum, TYP_REF);
+        unsigned offset  = OFFSETOF__CORINFO_Array__data + inf.DataOffset;
 
-            GenTree* byteArr               = m_comp->gtNewLclvNode(byteArrLclNum, TYP_REF);
-            unsigned offset                = OFFSETOF__CORINFO_Array__data;
-            GenTree* storePatchpointOffset = StoreAtOffset(byteArr, offset, ilOffsetToStore);
-            LIR::AsRange(suspendBB).InsertAtEnd(LIR::SeqTree(m_comp, storePatchpointOffset));
+        GenTree* value;
+        if (dsc->IsImplicitByRef())
+        {
+            GenTree* baseAddr = m_comp->gtNewLclvNode(inf.LclNum, dsc->TypeGet());
+            value             = m_comp->gtNewBlkIndir(dsc->GetLayout(), baseAddr, GTF_IND_NONFAULTING);
+        }
+        else
+        {
+            value = m_comp->gtNewLclvNode(inf.LclNum, genActualType(dsc->TypeGet()));
         }
 
-        // Fill in data
-        for (LiveLocalInfo& inf : m_liveLocals)
+        GenTree* store;
+        if ((dsc->TypeGet() == TYP_STRUCT) || dsc->IsImplicitByRef())
         {
-            if (inf.DataSize <= 0)
-            {
-                continue;
-            }
+            GenTree* cns  = m_comp->gtNewIconNode((ssize_t)offset, TYP_I_IMPL);
+            GenTree* addr = m_comp->gtNewOperNode(GT_ADD, TYP_BYREF, byteArr, cns);
+            // This is to heap, but all GC refs are nulled out already, so we can skip the write barrier.
+            // TODO-CQ: Backend does not care about GTF_IND_TGT_NOT_HEAP for STORE_BLK.
+            store =
+                m_comp->gtNewStoreBlkNode(dsc->GetLayout(), addr, value, GTF_IND_NONFAULTING | GTF_IND_TGT_NOT_HEAP);
+        }
+        else
+        {
+            store = StoreAtOffset(byteArr, offset, value);
+        }
 
-            LclVarDsc* dsc = m_comp->lvaGetDesc(inf.LclNum);
+        LIR::AsRange(suspendBB).InsertAtEnd(LIR::SeqTree(m_comp, store));
+    }
+}
 
-            GenTree* byteArr = m_comp->gtNewLclvNode(byteArrLclNum, TYP_REF);
-            unsigned offset  = OFFSETOF__CORINFO_Array__data + inf.DataOffset;
+void Async2Transformation::CreateCheckAndSuspendAfterCall(BasicBlock*               block,
+                                                          const CallDefinitionInfo& callDefInfo,
+                                                          AsyncLiveness&            life,
+                                                          BasicBlock*               suspendBB,
+                                                          BasicBlock**              remainder)
+{
+    GenTree* continuationArg = new (m_comp, GT_ASYNC_CONTINUATION) GenTree(GT_ASYNC_CONTINUATION, TYP_REF);
+    continuationArg->SetHasOrderingSideEffect();
 
-            GenTree* value;
-            if (dsc->IsImplicitByRef())
-            {
-                GenTree* baseAddr = m_comp->gtNewLclvNode(inf.LclNum, dsc->TypeGet());
-                value             = m_comp->gtNewBlkIndir(dsc->GetLayout(), baseAddr, GTF_IND_NONFAULTING);
-            }
-            else
-            {
-                value = m_comp->gtNewLclvNode(inf.LclNum, genActualType(dsc->TypeGet()));
-            }
+    GenTree* storeContinuation = m_comp->gtNewStoreLclVarNode(m_returnedContinuationVar, continuationArg);
+    LIR::AsRange(block).InsertAfter(callDefInfo.InsertAfter, continuationArg, storeContinuation);
 
-            GenTree* store;
-            if ((dsc->TypeGet() == TYP_STRUCT) || dsc->IsImplicitByRef())
-            {
-                GenTree* cns  = m_comp->gtNewIconNode((ssize_t)offset, TYP_I_IMPL);
-                GenTree* addr = m_comp->gtNewOperNode(GT_ADD, TYP_BYREF, byteArr, cns);
-                // This is to heap, but all GC refs are nulled out already, so we can skip the write barrier.
-                // TODO-CQ: Backend does not care about GTF_IND_TGT_NOT_HEAP for STORE_BLK.
-                store = m_comp->gtNewStoreBlkNode(dsc->GetLayout(), addr, value,
-                                                  GTF_IND_NONFAULTING | GTF_IND_TGT_NOT_HEAP);
-            }
-            else
-            {
-                store = StoreAtOffset(byteArr, offset, value);
-            }
+    GenTree* null                 = m_comp->gtNewNull();
+    GenTree* returnedContinuation = m_comp->gtNewLclvNode(m_returnedContinuationVar, TYP_REF);
+    GenTree* neNull               = m_comp->gtNewOperNode(GT_NE, TYP_INT, returnedContinuation, null);
+    GenTree* jtrue                = m_comp->gtNewOperNode(GT_JTRUE, TYP_VOID, neNull);
 
-            LIR::AsRange(suspendBB).InsertAtEnd(LIR::SeqTree(m_comp, store));
-        }
-    }
+    LIR::AsRange(block).InsertAfter(storeContinuation, null, returnedContinuation, neNull, jtrue);
+    *remainder = m_comp->fgSplitBlockAfterNode(block, jtrue);
+    JITDUMP("  Remainder is " FMT_BB "\n", (*remainder)->bbNum);
 
-    if (suspendBB->KindIs(BBJ_RETURN))
-    {
-        newContinuation = m_comp->gtNewLclvNode(m_newContinuationVar, TYP_REF);
-        GenTree* ret    = m_comp->gtNewOperNode(GT_RETURN_SUSPEND, TYP_VOID, newContinuation);
-        LIR::AsRange(suspendBB).InsertAtEnd(newContinuation, ret);
-    }
+    FlowEdge* retBBEdge = m_comp->fgAddRefPred(suspendBB, block);
+    block->SetCond(retBBEdge, block->GetTargetEdge());
 
+    block->GetTrueEdge()->setLikelihood(0);
+    block->GetFalseEdge()->setLikelihood(1);
+}
+
+BasicBlock* Async2Transformation::CreateResumption(BasicBlock*                    block,
+                                                   BasicBlock*                    remainder,
+                                                   GenTreeCall*                   call,
+                                                   const CallDefinitionInfo&      callDefInfo,
+                                                   unsigned                       stateNum,
+                                                   const ContinuationLayout&      layout,
+                                                   jitstd::vector<LiveLocalInfo>& liveLocals)
+{
     if (m_lastResumptionBB == nullptr)
     {
         m_lastResumptionBB = m_comp->fgLastBBInMainFunction();
@@ -823,10 +1038,10 @@ void Async2Transformation::Transform(
     resumeBB->SetFlags(BBF_ASYNC_RESUMPTION);
     m_lastResumptionBB = resumeBB;
 
-    JITDUMP("  Created resumption " FMT_BB " for state %u\n", resumeBB->bbNum, stateNum);
+    JITDUMP("  Creating resumption " FMT_BB " for state %u\n", resumeBB->bbNum, stateNum);
 
     unsigned resumeByteArrLclNum = BAD_VAR_NUM;
-    if (dataSize > 0)
+    if (layout.DataSize > 0)
     {
         resumeByteArrLclNum = GetDataArrayVar();
 
@@ -837,328 +1052,322 @@ void Async2Transformation::Transform(
 
         LIR::AsRange(resumeBB).InsertAtEnd(LIR::SeqTree(m_comp, storeAllocedByteArr));
 
-        // Copy data
-        for (LiveLocalInfo& inf : m_liveLocals)
-        {
-            if (inf.DataSize <= 0)
-            {
-                continue;
-            }
+        RestoreFromDataOnResumption(resumeByteArrLclNum, liveLocals, resumeBB);
+    }
 
-            LclVarDsc* dsc = m_comp->lvaGetDesc(inf.LclNum);
+    unsigned    resumeObjectArrLclNum = BAD_VAR_NUM;
+    BasicBlock* storeResultBB         = resumeBB;
 
-            GenTree* byteArr = m_comp->gtNewLclvNode(resumeByteArrLclNum, TYP_REF);
-            unsigned offset  = OFFSETOF__CORINFO_Array__data + inf.DataOffset;
-            GenTree* cns     = m_comp->gtNewIconNode((ssize_t)offset, TYP_I_IMPL);
-            GenTree* addr    = m_comp->gtNewOperNode(GT_ADD, TYP_BYREF, byteArr, cns);
+    if (layout.GCRefsCount > 0)
+    {
+        resumeObjectArrLclNum = GetGCDataArrayVar();
 
-            GenTree* value;
-            if ((dsc->TypeGet() == TYP_STRUCT) || dsc->IsImplicitByRef())
-            {
-                value = m_comp->gtNewBlkIndir(dsc->GetLayout(), addr, GTF_IND_NONFAULTING);
-            }
-            else
-            {
-                value = m_comp->gtNewIndir(dsc->TypeGet(), addr, GTF_IND_NONFAULTING);
-            }
+        GenTree* newContinuation = m_comp->gtNewLclvNode(m_comp->lvaAsyncContinuationArg, TYP_REF);
+        unsigned gcDataOffset    = m_comp->info.compCompHnd->getFieldOffset(m_async2Info.continuationGCDataFldHnd);
+        GenTree* gcDataInd       = LoadFromOffset(newContinuation, gcDataOffset, TYP_REF);
+        GenTree* storeAllocedObjectArr = m_comp->gtNewStoreLclVarNode(resumeObjectArrLclNum, gcDataInd);
+        LIR::AsRange(resumeBB).InsertAtEnd(LIR::SeqTree(m_comp, storeAllocedObjectArr));
 
-            GenTree* store;
-            if (dsc->IsImplicitByRef())
-            {
-                GenTree* baseAddr = m_comp->gtNewLclvNode(inf.LclNum, dsc->TypeGet());
-                // TODO-CQ: Incoming data has no non-null GC refs, so this does not need write barriers.
-                // Backend does not handle GTF_IND_TGT_NOT_HEAP for STORE_BLK.
-                store = m_comp->gtNewStoreBlkNode(dsc->GetLayout(), baseAddr, value,
-                                                  GTF_IND_NONFAULTING | GTF_IND_TGT_NOT_HEAP);
-            }
-            else
-            {
-                store = m_comp->gtNewStoreLclVarNode(inf.LclNum, value);
-            }
+        RestoreFromGCPointersOnResumption(resumeObjectArrLclNum, liveLocals, resumeBB);
 
-            LIR::AsRange(resumeBB).InsertAtEnd(LIR::SeqTree(m_comp, store));
+        if (layout.ExceptionGCDataIndex != UINT_MAX)
+        {
+            storeResultBB = RethrowExceptionOnResumption(block, remainder, resumeObjectArrLclNum, layout, resumeBB);
         }
     }
 
-    unsigned    resumeObjectArrLclNum = BAD_VAR_NUM;
-    BasicBlock* storeResultBB         = resumeBB;
+    // Copy call return value.
+    if (layout.ReturnSize > 0)
+    {
+        CopyReturnValueOnResumption(call, callDefInfo, resumeByteArrLclNum, resumeObjectArrLclNum, layout,
+                                    storeResultBB);
+    }
+
+    return resumeBB;
+}
 
-    if (gcRefsCount > 0)
+void Async2Transformation::RestoreFromDataOnResumption(unsigned                       resumeByteArrLclNum,
+                                                       jitstd::vector<LiveLocalInfo>& liveLocals,
+                                                       BasicBlock*                    resumeBB)
+{
+    // Copy data
+    for (LiveLocalInfo& inf : liveLocals)
     {
-        resumeObjectArrLclNum = GetGCDataArrayVar();
+        if (inf.DataSize <= 0)
+        {
+            continue;
+        }
 
-        newContinuation       = m_comp->gtNewLclvNode(m_comp->lvaAsyncContinuationArg, TYP_REF);
-        unsigned gcDataOffset = m_comp->info.compCompHnd->getFieldOffset(m_async2Info.continuationGCDataFldHnd);
-        GenTree* gcDataInd    = LoadFromOffset(newContinuation, gcDataOffset, TYP_REF);
-        GenTree* storeAllocedObjectArr = m_comp->gtNewStoreLclVarNode(resumeObjectArrLclNum, gcDataInd);
-        LIR::AsRange(resumeBB).InsertAtEnd(LIR::SeqTree(m_comp, storeAllocedObjectArr));
+        LclVarDsc* dsc = m_comp->lvaGetDesc(inf.LclNum);
 
-        // Copy GC pointers
-        for (LiveLocalInfo& inf : m_liveLocals)
+        GenTree* byteArr = m_comp->gtNewLclvNode(resumeByteArrLclNum, TYP_REF);
+        unsigned offset  = OFFSETOF__CORINFO_Array__data + inf.DataOffset;
+        GenTree* cns     = m_comp->gtNewIconNode((ssize_t)offset, TYP_I_IMPL);
+        GenTree* addr    = m_comp->gtNewOperNode(GT_ADD, TYP_BYREF, byteArr, cns);
+
+        GenTree* value;
+        if ((dsc->TypeGet() == TYP_STRUCT) || dsc->IsImplicitByRef())
         {
-            if (inf.GCDataCount <= 0)
-            {
-                continue;
-            }
+            value = m_comp->gtNewBlkIndir(dsc->GetLayout(), addr, GTF_IND_NONFAULTING);
+        }
+        else
+        {
+            value = m_comp->gtNewIndir(dsc->TypeGet(), addr, GTF_IND_NONFAULTING);
+        }
 
-            LclVarDsc* dsc = m_comp->lvaGetDesc(inf.LclNum);
-            if (dsc->TypeGet() == TYP_REF)
-            {
-                GenTree* objectArr = m_comp->gtNewLclvNode(resumeObjectArrLclNum, TYP_REF);
-                unsigned offset    = OFFSETOF__CORINFO_Array__data + (inf.GCDataIndex * TARGET_POINTER_SIZE);
-                GenTree* value     = LoadFromOffset(objectArr, offset, TYP_REF);
-                GenTree* store     = m_comp->gtNewStoreLclVarNode(inf.LclNum, value);
+        GenTree* store;
+        if (dsc->IsImplicitByRef())
+        {
+            GenTree* baseAddr = m_comp->gtNewLclvNode(inf.LclNum, dsc->TypeGet());
+            // TODO-CQ: Incoming data has no non-null GC refs, so this does not need write barriers.
+            // Backend does not handle GTF_IND_TGT_NOT_HEAP for STORE_BLK.
+            store = m_comp->gtNewStoreBlkNode(dsc->GetLayout(), baseAddr, value,
+                                              GTF_IND_NONFAULTING | GTF_IND_TGT_NOT_HEAP);
+        }
+        else
+        {
+            store = m_comp->gtNewStoreLclVarNode(inf.LclNum, value);
+        }
 
-                LIR::AsRange(resumeBB).InsertAtEnd(LIR::SeqTree(m_comp, store));
-            }
-            else
+        LIR::AsRange(resumeBB).InsertAtEnd(LIR::SeqTree(m_comp, store));
+    }
+}
+
+void Async2Transformation::RestoreFromGCPointersOnResumption(unsigned                       resumeObjectArrLclNum,
+                                                             jitstd::vector<LiveLocalInfo>& liveLocals,
+                                                             BasicBlock*                    resumeBB)
+{
+    for (LiveLocalInfo& inf : liveLocals)
+    {
+        if (inf.GCDataCount <= 0)
+        {
+            continue;
+        }
+
+        LclVarDsc* dsc = m_comp->lvaGetDesc(inf.LclNum);
+        if (dsc->TypeGet() == TYP_REF)
+        {
+            GenTree* objectArr = m_comp->gtNewLclvNode(resumeObjectArrLclNum, TYP_REF);
+            unsigned offset    = OFFSETOF__CORINFO_Array__data + (inf.GCDataIndex * TARGET_POINTER_SIZE);
+            GenTree* value     = LoadFromOffset(objectArr, offset, TYP_REF);
+            GenTree* store     = m_comp->gtNewStoreLclVarNode(inf.LclNum, value);
+
+            LIR::AsRange(resumeBB).InsertAtEnd(LIR::SeqTree(m_comp, store));
+        }
+        else
+        {
+            assert((dsc->TypeGet() == TYP_STRUCT) || dsc->IsImplicitByRef());
+            ClassLayout* layout     = dsc->GetLayout();
+            unsigned     numSlots   = layout->GetSlotCount();
+            unsigned     gcRefIndex = 0;
+            for (unsigned i = 0; i < numSlots; i++)
             {
-                assert((dsc->TypeGet() == TYP_STRUCT) || dsc->IsImplicitByRef());
-                ClassLayout* layout     = dsc->GetLayout();
-                unsigned     numSlots   = layout->GetSlotCount();
-                unsigned     gcRefIndex = 0;
-                for (unsigned i = 0; i < numSlots; i++)
+                var_types gcPtrType = layout->GetGCPtrType(i);
+                assert((gcPtrType == TYP_I_IMPL) || (gcPtrType == TYP_REF));
+                if (gcPtrType != TYP_REF)
                 {
-                    var_types gcPtrType = layout->GetGCPtrType(i);
-                    assert((gcPtrType == TYP_I_IMPL) || (gcPtrType == TYP_REF));
-                    if (gcPtrType != TYP_REF)
-                    {
-                        continue;
-                    }
+                    continue;
+                }
 
-                    GenTree* objectArr = m_comp->gtNewLclvNode(resumeObjectArrLclNum, TYP_REF);
-                    unsigned offset =
-                        OFFSETOF__CORINFO_Array__data + ((inf.GCDataIndex + gcRefIndex) * TARGET_POINTER_SIZE);
-                    GenTree* value = LoadFromOffset(objectArr, offset, TYP_REF);
-                    GenTree* store;
-                    if (dsc->IsImplicitByRef())
-                    {
-                        GenTree* baseAddr = m_comp->gtNewLclvNode(inf.LclNum, dsc->TypeGet());
-                        store             = StoreAtOffset(baseAddr, i * TARGET_POINTER_SIZE, value);
-                        // Implicit byref args are never on heap today, skip write barriers.
-                        // TODO-CQ: Remove this once all implicit byrefs are TYP_I_IMPL typed.
-                        store->gtFlags |= GTF_IND_TGT_NOT_HEAP;
-                    }
-                    else
-                    {
-                        store = m_comp->gtNewStoreLclFldNode(inf.LclNum, TYP_REF, i * TARGET_POINTER_SIZE, value);
-                    }
+                GenTree* objectArr = m_comp->gtNewLclvNode(resumeObjectArrLclNum, TYP_REF);
+                unsigned offset =
+                    OFFSETOF__CORINFO_Array__data + ((inf.GCDataIndex + gcRefIndex) * TARGET_POINTER_SIZE);
+                GenTree* value = LoadFromOffset(objectArr, offset, TYP_REF);
+                GenTree* store;
+                if (dsc->IsImplicitByRef())
+                {
+                    GenTree* baseAddr = m_comp->gtNewLclvNode(inf.LclNum, dsc->TypeGet());
+                    store             = StoreAtOffset(baseAddr, i * TARGET_POINTER_SIZE, value);
+                    // Implicit byref args are never on heap today, skip write barriers.
+                    // TODO-CQ: Remove this once all implicit byrefs are TYP_I_IMPL typed.
+                    store->gtFlags |= GTF_IND_TGT_NOT_HEAP;
+                }
+                else
+                {
+                    store = m_comp->gtNewStoreLclFldNode(inf.LclNum, TYP_REF, i * TARGET_POINTER_SIZE, value);
+                }
 
-                    LIR::AsRange(resumeBB).InsertAtEnd(LIR::SeqTree(m_comp, store));
+                LIR::AsRange(resumeBB).InsertAtEnd(LIR::SeqTree(m_comp, store));
 
-                    gcRefIndex++;
-                }
+                gcRefIndex++;
             }
         }
+    }
+}
 
-        if (exceptionGCDataIndex != UINT_MAX)
-        {
-            JITDUMP("  We need to rethrow an exception\n");
+BasicBlock* Async2Transformation::RethrowExceptionOnResumption(BasicBlock*               block,
+                                                               BasicBlock*               remainder,
+                                                               unsigned                  resumeObjectArrLclNum,
+                                                               const ContinuationLayout& layout,
+                                                               BasicBlock*               resumeBB)
+{
+    JITDUMP("  We need to rethrow an exception\n");
 
-            BasicBlock* rethrowExceptionBB =
-                m_comp->fgNewBBinRegion(BBJ_THROW, block, /* runRarely */ true, /* insertAtEnd */ true);
-            JITDUMP("  Created " FMT_BB " to rethrow exception on resumption\n", rethrowExceptionBB->bbNum);
+    BasicBlock* rethrowExceptionBB =
+        m_comp->fgNewBBinRegion(BBJ_THROW, block, /* runRarely */ true, /* insertAtEnd */ true);
+    JITDUMP("  Created " FMT_BB " to rethrow exception on resumption\n", rethrowExceptionBB->bbNum);
 
-            storeResultBB = m_comp->fgNewBBafter(BBJ_ALWAYS, resumeBB, true);
-            JITDUMP("  Created " FMT_BB " to store result when resuming with no exception\n", storeResultBB->bbNum);
+    BasicBlock* storeResultBB = m_comp->fgNewBBafter(BBJ_ALWAYS, resumeBB, true);
+    JITDUMP("  Created " FMT_BB " to store result when resuming with no exception\n", storeResultBB->bbNum);
 
-            FlowEdge* rethrowEdge     = m_comp->fgAddRefPred(rethrowExceptionBB, resumeBB);
-            FlowEdge* storeResultEdge = m_comp->fgAddRefPred(storeResultBB, resumeBB);
+    FlowEdge* rethrowEdge     = m_comp->fgAddRefPred(rethrowExceptionBB, resumeBB);
+    FlowEdge* storeResultEdge = m_comp->fgAddRefPred(storeResultBB, resumeBB);
 
-            assert(resumeBB->KindIs(BBJ_ALWAYS));
+    assert(resumeBB->KindIs(BBJ_ALWAYS));
+    m_comp->fgRemoveRefPred(resumeBB->GetTargetEdge());
 
-            resumeBB->SetCond(rethrowEdge, storeResultEdge);
-            rethrowEdge->setLikelihood(0);
-            storeResultEdge->setLikelihood(1);
-            rethrowExceptionBB->inheritWeightPercentage(resumeBB, 0);
-            storeResultBB->inheritWeightPercentage(resumeBB, 100);
-            JITDUMP("  Resumption " FMT_BB " becomes BBJ_COND to check for non-null exception\n", resumeBB->bbNum);
+    resumeBB->SetCond(rethrowEdge, storeResultEdge);
+    rethrowEdge->setLikelihood(0);
+    storeResultEdge->setLikelihood(1);
+    rethrowExceptionBB->inheritWeightPercentage(resumeBB, 0);
+    storeResultBB->inheritWeightPercentage(resumeBB, 100);
+    JITDUMP("  Resumption " FMT_BB " becomes BBJ_COND to check for non-null exception\n", resumeBB->bbNum);
 
-            m_comp->fgRemoveRefPred(remainderEdge);
-            remainderEdge = m_comp->fgAddRefPred(remainder, storeResultBB);
+    FlowEdge* remainderEdge = m_comp->fgAddRefPred(remainder, storeResultBB);
+    storeResultBB->SetTargetEdge(remainderEdge);
 
-            storeResultBB->SetTargetEdge(remainderEdge);
+    m_lastResumptionBB = storeResultBB;
 
-            m_lastResumptionBB = storeResultBB;
+    // Check if we have an exception.
+    unsigned exceptionLclNum = GetExceptionVar();
+    GenTree* objectArr       = m_comp->gtNewLclvNode(resumeObjectArrLclNum, TYP_REF);
+    unsigned exceptionOffset = OFFSETOF__CORINFO_Array__data + layout.ExceptionGCDataIndex * TARGET_POINTER_SIZE;
+    GenTree* exceptionInd    = LoadFromOffset(objectArr, exceptionOffset, TYP_REF);
+    GenTree* storeException  = m_comp->gtNewStoreLclVarNode(exceptionLclNum, exceptionInd);
+    LIR::AsRange(resumeBB).InsertAtEnd(LIR::SeqTree(m_comp, storeException));
 
-            // Check if we have an exception.
-            unsigned exceptionLclNum = GetExceptionVar();
-            GenTree* objectArr       = m_comp->gtNewLclvNode(resumeObjectArrLclNum, TYP_REF);
-            unsigned exceptionOffset = OFFSETOF__CORINFO_Array__data + exceptionGCDataIndex * TARGET_POINTER_SIZE;
-            GenTree* exceptionInd    = LoadFromOffset(objectArr, exceptionOffset, TYP_REF);
-            GenTree* storeException  = m_comp->gtNewStoreLclVarNode(exceptionLclNum, exceptionInd);
-            LIR::AsRange(resumeBB).InsertAtEnd(LIR::SeqTree(m_comp, storeException));
+    GenTree* exception = m_comp->gtNewLclVarNode(exceptionLclNum, TYP_REF);
+    GenTree* null      = m_comp->gtNewNull();
+    GenTree* neNull    = m_comp->gtNewOperNode(GT_NE, TYP_INT, exception, null);
+    GenTree* jtrue     = m_comp->gtNewOperNode(GT_JTRUE, TYP_VOID, neNull);
+    LIR::AsRange(resumeBB).InsertAtEnd(exception, null, neNull, jtrue);
 
-            GenTree* exception = m_comp->gtNewLclVarNode(exceptionLclNum, TYP_REF);
-            GenTree* null      = m_comp->gtNewNull();
-            GenTree* neNull    = m_comp->gtNewOperNode(GT_NE, TYP_INT, exception, null);
-            GenTree* jtrue     = m_comp->gtNewOperNode(GT_JTRUE, TYP_VOID, neNull);
-            LIR::AsRange(resumeBB).InsertAtEnd(exception, null, neNull, jtrue);
+    exception                     = m_comp->gtNewLclVarNode(exceptionLclNum, TYP_REF);
+    GenTreeCall* rethrowException = m_comp->gtNewHelperCallNode(CORINFO_HELP_THROWEXACT, TYP_VOID, exception);
 
-            exception                     = m_comp->gtNewLclVarNode(exceptionLclNum, TYP_REF);
-            GenTreeCall* rethrowException = m_comp->gtNewHelperCallNode(CORINFO_HELP_THROWEXACT, TYP_VOID, exception);
+    m_comp->compCurBB = rethrowExceptionBB;
+    m_comp->fgMorphTree(rethrowException);
 
-            m_comp->compCurBB = rethrowExceptionBB;
-            m_comp->fgMorphTree(rethrowException);
+    LIR::AsRange(rethrowExceptionBB).InsertAtEnd(LIR::SeqTree(m_comp, rethrowException));
 
-            LIR::AsRange(rethrowExceptionBB).InsertAtEnd(LIR::SeqTree(m_comp, rethrowException));
+    storeResultBB->SetFlags(BBF_ASYNC_RESUMPTION);
+    JITDUMP("  Added " FMT_BB " to rethrow exception at suspension point\n", rethrowExceptionBB->bbNum);
 
-            storeResultBB->SetFlags(BBF_ASYNC_RESUMPTION);
-            JITDUMP("  Added " FMT_BB " to rethrow exception at suspension point\n", rethrowExceptionBB->bbNum);
-        }
-    }
+    return storeResultBB;
+}
 
-    // Copy call return value.
-    if (storeResultNode != nullptr)
+void Async2Transformation::CopyReturnValueOnResumption(GenTreeCall*              call,
+                                                       const CallDefinitionInfo& callDefInfo,
+                                                       unsigned                  resumeByteArrLclNum,
+                                                       unsigned                  resumeObjectArrLclNum,
+                                                       const ContinuationLayout& layout,
+                                                       BasicBlock*               storeResultBB)
+{
+    GenTree*     resultBase;
+    unsigned     resultOffset;
+    GenTreeFlags resultIndirFlags = GTF_IND_NONFAULTING;
+    if (layout.ReturnInGCData)
     {
-        GenTree*     resultBase;
-        unsigned     resultOffset;
-        GenTreeFlags resultIndirFlags = GTF_IND_NONFAULTING;
-        if (returnInGCData)
-        {
-            assert(resumeObjectArrLclNum != BAD_VAR_NUM);
-            resultBase = m_comp->gtNewLclvNode(resumeObjectArrLclNum, TYP_REF);
+        assert(resumeObjectArrLclNum != BAD_VAR_NUM);
+        resultBase = m_comp->gtNewLclvNode(resumeObjectArrLclNum, TYP_REF);
 
-            if (call->gtReturnType == TYP_STRUCT)
-            {
-                // Boxed struct.
-                resultBase   = LoadFromOffset(resultBase, OFFSETOF__CORINFO_Array__data, TYP_REF);
-                resultOffset = TARGET_POINTER_SIZE; // Offset of data inside box
-            }
-            else
-            {
-                assert(call->gtReturnType == TYP_REF);
-                resultOffset = OFFSETOF__CORINFO_Array__data;
-            }
+        if (call->gtReturnType == TYP_STRUCT)
+        {
+            // Boxed struct.
+            resultBase   = LoadFromOffset(resultBase, OFFSETOF__CORINFO_Array__data, TYP_REF);
+            resultOffset = TARGET_POINTER_SIZE; // Offset of data inside box
         }
         else
         {
-            assert(resumeByteArrLclNum != BAD_VAR_NUM);
-            resultBase   = m_comp->gtNewLclvNode(resumeByteArrLclNum, TYP_REF);
-            resultOffset = OFFSETOF__CORINFO_Array__data + returnValDataOffset;
-            if (returnValDataOffset != 0)
-                resultIndirFlags = GTF_IND_UNALIGNED;
+            assert(call->gtReturnType == TYP_REF);
+            resultOffset = OFFSETOF__CORINFO_Array__data;
         }
+    }
+    else
+    {
+        assert(resumeByteArrLclNum != BAD_VAR_NUM);
+        resultBase   = m_comp->gtNewLclvNode(resumeByteArrLclNum, TYP_REF);
+        resultOffset = OFFSETOF__CORINFO_Array__data + layout.ReturnValDataOffset;
+        if (layout.ReturnValDataOffset != 0)
+            resultIndirFlags = GTF_IND_UNALIGNED;
+    }
 
-        LclVarDsc* resultLcl = m_comp->lvaGetDesc(storeResultNode);
-        assert((resultLcl->TypeGet() == TYP_STRUCT) == (call->gtReturnType == TYP_STRUCT));
+    assert(callDefInfo.DefinitionNode != nullptr);
+    LclVarDsc* resultLcl = m_comp->lvaGetDesc(callDefInfo.DefinitionNode);
+    assert((resultLcl->TypeGet() == TYP_STRUCT) == (call->gtReturnType == TYP_STRUCT));
 
-        // TODO-TP: We can use liveness to avoid generating a lot of this IR.
-        if (call->gtReturnType == TYP_STRUCT)
+    // TODO-TP: We can use liveness to avoid generating a lot of this IR.
+    if (call->gtReturnType == TYP_STRUCT)
+    {
+        if (m_comp->lvaGetPromotionType(resultLcl) != Compiler::PROMOTION_TYPE_INDEPENDENT)
         {
-            if (m_comp->lvaGetPromotionType(resultLcl) != Compiler::PROMOTION_TYPE_INDEPENDENT)
+            GenTree* resultOffsetNode = m_comp->gtNewIconNode((ssize_t)resultOffset, TYP_I_IMPL);
+            GenTree* resultAddr       = m_comp->gtNewOperNode(GT_ADD, TYP_BYREF, resultBase, resultOffsetNode);
+            GenTree* resultData       = m_comp->gtNewBlkIndir(layout.ReturnStructLayout, resultAddr, resultIndirFlags);
+            GenTree* storeResult;
+            if ((callDefInfo.DefinitionNode->GetLclOffs() == 0) &&
+                ClassLayout::AreCompatible(resultLcl->GetLayout(), layout.ReturnStructLayout))
             {
-                GenTree* resultOffsetNode = m_comp->gtNewIconNode((ssize_t)resultOffset, TYP_I_IMPL);
-                GenTree* resultAddr       = m_comp->gtNewOperNode(GT_ADD, TYP_BYREF, resultBase, resultOffsetNode);
-                GenTree* resultData       = m_comp->gtNewBlkIndir(returnStructLayout, resultAddr, resultIndirFlags);
-                GenTree* storeResult;
-                if ((storeResultNode->GetLclOffs() == 0) &&
-                    ClassLayout::AreCompatible(resultLcl->GetLayout(), returnStructLayout))
-                {
-                    storeResult = m_comp->gtNewStoreLclVarNode(storeResultNode->GetLclNum(), resultData);
-                }
-                else
-                {
-                    storeResult =
-                        m_comp->gtNewStoreLclFldNode(storeResultNode->GetLclNum(), TYP_STRUCT, returnStructLayout,
-                                                     storeResultNode->GetLclOffs(), resultData);
-                }
-
-                LIR::AsRange(storeResultBB).InsertAtEnd(LIR::SeqTree(m_comp, storeResult));
+                storeResult = m_comp->gtNewStoreLclVarNode(callDefInfo.DefinitionNode->GetLclNum(), resultData);
             }
             else
             {
-                assert(retbufArg == nullptr); // Locals defined through retbufs are never independently promoted.
-
-                if ((resultLcl->lvFieldCnt > 1) && !resultBase->OperIsLocal())
-                {
-                    unsigned resultBaseVar   = GetResultBaseVar();
-                    GenTree* storeResultBase = m_comp->gtNewStoreLclVarNode(resultBaseVar, resultBase);
-                    LIR::AsRange(storeResultBB).InsertAtEnd(LIR::SeqTree(m_comp, storeResultBase));
-
-                    resultBase = m_comp->gtNewLclVarNode(resultBaseVar, TYP_REF);
-                }
-
-                assert(storeResultNode->OperIs(GT_STORE_LCL_VAR));
-                for (unsigned i = 0; i < resultLcl->lvFieldCnt; i++)
-                {
-                    unsigned   fieldLclNum = resultLcl->lvFieldLclStart + i;
-                    LclVarDsc* fieldDsc    = m_comp->lvaGetDesc(fieldLclNum);
-
-                    unsigned fldOffset = resultOffset + fieldDsc->lvFldOffset;
-                    GenTree* value     = LoadFromOffset(resultBase, fldOffset, fieldDsc->TypeGet(), resultIndirFlags);
-                    GenTree* store     = m_comp->gtNewStoreLclVarNode(fieldLclNum, value);
-                    LIR::AsRange(storeResultBB).InsertAtEnd(LIR::SeqTree(m_comp, store));
-
-                    if (i + 1 != resultLcl->lvFieldCnt)
-                    {
-                        resultBase = m_comp->gtCloneExpr(resultBase);
-                    }
-                }
+                storeResult = m_comp->gtNewStoreLclFldNode(callDefInfo.DefinitionNode->GetLclNum(), TYP_STRUCT,
+                                                           layout.ReturnStructLayout,
+                                                           callDefInfo.DefinitionNode->GetLclOffs(), resultData);
             }
+
+            LIR::AsRange(storeResultBB).InsertAtEnd(LIR::SeqTree(m_comp, storeResult));
         }
         else
         {
-            GenTree* value = LoadFromOffset(resultBase, resultOffset, call->gtReturnType, resultIndirFlags);
+            assert(!call->gtArgs.HasRetBuffer()); // Locals defined through retbufs are never independently promoted.
 
-            GenTree* storeResult;
-            if (storeResultNode->OperIs(GT_STORE_LCL_VAR))
+            if ((resultLcl->lvFieldCnt > 1) && !resultBase->OperIsLocal())
             {
-                storeResult = m_comp->gtNewStoreLclVarNode(storeResultNode->GetLclNum(), value);
+                unsigned resultBaseVar   = GetResultBaseVar();
+                GenTree* storeResultBase = m_comp->gtNewStoreLclVarNode(resultBaseVar, resultBase);
+                LIR::AsRange(storeResultBB).InsertAtEnd(LIR::SeqTree(m_comp, storeResultBase));
+
+                resultBase = m_comp->gtNewLclVarNode(resultBaseVar, TYP_REF);
             }
-            else
+
+            assert(callDefInfo.DefinitionNode->OperIs(GT_STORE_LCL_VAR));
+            for (unsigned i = 0; i < resultLcl->lvFieldCnt; i++)
             {
-                storeResult = m_comp->gtNewStoreLclFldNode(storeResultNode->GetLclNum(), storeResultNode->TypeGet(),
-                                                           storeResultNode->GetLclOffs(), value);
-            }
+                unsigned   fieldLclNum = resultLcl->lvFieldLclStart + i;
+                LclVarDsc* fieldDsc    = m_comp->lvaGetDesc(fieldLclNum);
 
-            LIR::AsRange(storeResultBB).InsertAtEnd(LIR::SeqTree(m_comp, storeResult));
-        }
-    }
+                unsigned fldOffset = resultOffset + fieldDsc->lvFldOffset;
+                GenTree* value     = LoadFromOffset(resultBase, fldOffset, fieldDsc->TypeGet(), resultIndirFlags);
+                GenTree* store     = m_comp->gtNewStoreLclVarNode(fieldLclNum, value);
+                LIR::AsRange(storeResultBB).InsertAtEnd(LIR::SeqTree(m_comp, store));
 
-    m_resumptionBBs.push_back(resumeBB);
-}
-
-GenTreeCall* Async2Transformation::CreateAllocContinuationCall(AsyncLiveness& life,
-                                                               GenTree*       prevContinuation,
-                                                               unsigned       gcRefsCount,
-                                                               unsigned       dataSize)
-{
-    GenTree* gcRefsCountNode = m_comp->gtNewIconNode((ssize_t)gcRefsCount, TYP_I_IMPL);
-    GenTree* dataSizeNode    = m_comp->gtNewIconNode((ssize_t)dataSize, TYP_I_IMPL);
-    // If VM requests that we report the method handle, or if we have a shared generic context method handle
-    // that is live here, then we need to call a different helper.
-    GenTree* methodHandleArg = nullptr;
-    GenTree* classHandleArg  = nullptr;
-    if (((m_comp->info.compMethodInfo->options & CORINFO_GENERICS_CTXT_FROM_METHODDESC) != 0) &&
-        life.IsLive(m_comp->info.compTypeCtxtArg))
-    {
-        methodHandleArg = m_comp->gtNewLclvNode(m_comp->info.compTypeCtxtArg, TYP_I_IMPL);
-    }
-    else if (((m_comp->info.compMethodInfo->options & CORINFO_GENERICS_CTXT_FROM_METHODTABLE) != 0) &&
-             life.IsLive(m_comp->info.compTypeCtxtArg))
-    {
-        classHandleArg = m_comp->gtNewLclvNode(m_comp->info.compTypeCtxtArg, TYP_I_IMPL);
+                if (i + 1 != resultLcl->lvFieldCnt)
+                {
+                    resultBase = m_comp->gtCloneExpr(resultBase);
+                }
+            }
+        }
     }
-    else if (m_async2Info.continuationsNeedMethodHandle)
+    else
     {
-        methodHandleArg = m_comp->gtNewIconEmbMethHndNode(m_comp->info.compMethodHnd);
-    }
+        GenTree* value = LoadFromOffset(resultBase, resultOffset, call->gtReturnType, resultIndirFlags);
 
-    if (methodHandleArg != nullptr)
-    {
-        return m_comp->gtNewHelperCallNode(CORINFO_HELP_ALLOC_CONTINUATION_METHOD, TYP_REF, prevContinuation,
-                                           gcRefsCountNode, dataSizeNode, methodHandleArg);
-    }
+        GenTree* storeResult;
+        if (callDefInfo.DefinitionNode->OperIs(GT_STORE_LCL_VAR))
+        {
+            storeResult = m_comp->gtNewStoreLclVarNode(callDefInfo.DefinitionNode->GetLclNum(), value);
+        }
+        else
+        {
+            storeResult = m_comp->gtNewStoreLclFldNode(callDefInfo.DefinitionNode->GetLclNum(),
+                                                       callDefInfo.DefinitionNode->TypeGet(),
+                                                       callDefInfo.DefinitionNode->GetLclOffs(), value);
+        }
 
-    if (classHandleArg != nullptr)
-    {
-        return m_comp->gtNewHelperCallNode(CORINFO_HELP_ALLOC_CONTINUATION_CLASS, TYP_REF, prevContinuation,
-                                           gcRefsCountNode, dataSizeNode, classHandleArg);
+        LIR::AsRange(storeResultBB).InsertAtEnd(LIR::SeqTree(m_comp, storeResult));
     }
-
-    return m_comp->gtNewHelperCallNode(CORINFO_HELP_ALLOC_CONTINUATION, TYP_REF, prevContinuation, gcRefsCountNode,
-                                       dataSizeNode);
 }
 
 GenTreeIndir* Async2Transformation::LoadFromOffset(GenTree*     base,
@@ -1274,10 +1483,10 @@ GenTree* Async2Transformation::CreateFunctionTargetAddr(CORINFO_METHOD_HANDLE
     return con;
 }
 
-void Async2Transformation::LiftLIREdges(BasicBlock*                    block,
-                                        GenTree*                       beyond,
-                                        jitstd::vector<GenTree*>&      defs,
-                                        jitstd::vector<LiveLocalInfo>& liveLocals)
+void Async2Transformation::LiftLIREdges(BasicBlock*                     block,
+                                        GenTree*                        beyond,
+                                        const jitstd::vector<GenTree*>& defs,
+                                        jitstd::vector<LiveLocalInfo>&  liveLocals)
 {
     if (defs.size() <= 0)
     {
diff --git a/src/coreclr/jit/async.h b/src/coreclr/jit/async.h
index 046f9976556363..f99d5817f3bc08 100644
--- a/src/coreclr/jit/async.h
+++ b/src/coreclr/jit/async.h
@@ -1,6 +1,25 @@
 // Licensed to the .NET Foundation under one or more agreements.
 // The .NET Foundation licenses this file to you under the MIT license.
 
+struct ContinuationLayout
+{
+    unsigned     DataSize             = 0;
+    unsigned     GCRefsCount          = 0;
+    ClassLayout* ReturnStructLayout   = nullptr;
+    unsigned     ReturnSize           = 0;
+    bool         ReturnInGCData       = false;
+    unsigned     ReturnValDataOffset  = UINT_MAX;
+    unsigned     ExceptionGCDataIndex = UINT_MAX;
+};
+
+struct CallDefinitionInfo
+{
+    GenTreeLclVarCommon* DefinitionNode = nullptr;
+
+    // Where to insert new IR for suspension checks.
+    GenTree* InsertAfter = nullptr;
+};
+
 class Async2Transformation
 {
     friend class AsyncLiveness;
@@ -21,7 +40,7 @@ class Async2Transformation
     };
 
     Compiler*                     m_comp;
-    jitstd::vector<LiveLocalInfo> m_liveLocals;
+    jitstd::vector<LiveLocalInfo> m_liveLocalsScratch;
     CORINFO_ASYNC2_INFO           m_async2Info;
     jitstd::vector<BasicBlock*>   m_resumptionBBs;
     CORINFO_METHOD_HANDLE         m_resumeStub = NO_METHOD_HANDLE;
@@ -36,10 +55,10 @@ class Async2Transformation
     BasicBlock*                   m_lastResumptionBB        = nullptr;
     BasicBlock*                   m_sharedReturnBB          = nullptr;
 
-    void LiftLIREdges(BasicBlock*                    block,
-                      GenTree*                       beyond,
-                      jitstd::vector<GenTree*>&      defs,
-                      jitstd::vector<LiveLocalInfo>& liveLocals);
+    void LiftLIREdges(BasicBlock*                     block,
+                      GenTree*                        beyond,
+                      const jitstd::vector<GenTree*>& defs,
+                      jitstd::vector<LiveLocalInfo>&  liveLocals);
     bool IsLive(unsigned lclNum);
     void Transform(BasicBlock*               block,
                    GenTreeCall*              call,
@@ -47,10 +66,58 @@ class Async2Transformation
                    class AsyncLiveness&      life,
                    BasicBlock**              remainder);
 
+    void               CreateLiveSetForSuspension(BasicBlock*                     block,
+                                                  GenTreeCall*                    call,
+                                                  const jitstd::vector<GenTree*>& defs,
+                                                  AsyncLiveness&                  life,
+                                                  jitstd::vector<LiveLocalInfo>&  liveLocals);
+    ContinuationLayout LayOutContinuation(BasicBlock*                    block,
+                                          GenTreeCall*                   call,
+                                          jitstd::vector<LiveLocalInfo>& liveLocals);
+
+    CallDefinitionInfo CanonicalizeCallDefinition(BasicBlock* block, GenTreeCall* call, AsyncLiveness& life);
+
+    BasicBlock*  CreateSuspension(BasicBlock*                    block,
+                                  unsigned                       stateNum,
+                                  const ContinuationLayout&      layout,
+                                  AsyncLiveness&                 life,
+                                  jitstd::vector<LiveLocalInfo>& liveLocals);
     GenTreeCall* CreateAllocContinuationCall(AsyncLiveness& life,
                                              GenTree*       prevContinuation,
                                              unsigned       gcRefsCount,
                                              unsigned int   dataSize);
+    void         FillInGCPointersOnSuspension(jitstd::vector<LiveLocalInfo>& liveLocals, BasicBlock* suspendBB);
+    void         FillInDataOnSuspension(jitstd::vector<LiveLocalInfo>& liveLocals, BasicBlock* suspendBB);
+    void         CreateCheckAndSuspendAfterCall(BasicBlock*               block,
+                                                const CallDefinitionInfo& callDefInfo,
+                                                AsyncLiveness&            life,
+                                                BasicBlock*               suspendBB,
+                                                BasicBlock**              remainder);
+
+    BasicBlock* CreateResumption(BasicBlock*                    block,
+                                 BasicBlock*                    remainder,
+                                 GenTreeCall*                   call,
+                                 const CallDefinitionInfo&      callDefInfo,
+                                 unsigned                       stateNum,
+                                 const ContinuationLayout&      layout,
+                                 jitstd::vector<LiveLocalInfo>& liveLocals);
+    void        RestoreFromDataOnResumption(unsigned                       resumeByteArrLclNum,
+                                            jitstd::vector<LiveLocalInfo>& liveLocals,
+                                            BasicBlock*                    resumeBB);
+    void        RestoreFromGCPointersOnResumption(unsigned                       resumeObjectArrLclNum,
+                                                  jitstd::vector<LiveLocalInfo>& liveLocals,
+                                                  BasicBlock*                    resumeBB);
+    BasicBlock* RethrowExceptionOnResumption(BasicBlock*               block,
+                                             BasicBlock*               remainder,
+                                             unsigned                  resumeObjectArrLclNum,
+                                             const ContinuationLayout& layout,
+                                             BasicBlock*               resumeBB);
+    void        CopyReturnValueOnResumption(GenTreeCall*              call,
+                                            const CallDefinitionInfo& callDefInfo,
+                                            unsigned                  resumeByteArrLclNum,
+                                            unsigned                  resumeObjectArrLclNum,
+                                            const ContinuationLayout& layout,
+                                            BasicBlock*               storeResultBB);
 
     GenTreeIndir*    LoadFromOffset(GenTree*     base,
                                     unsigned     offset,
@@ -71,7 +138,7 @@ class Async2Transformation
 public:
     Async2Transformation(Compiler* comp)
         : m_comp(comp)
-        , m_liveLocals(comp->getAllocator(CMK_Async2))
+        , m_liveLocalsScratch(comp->getAllocator(CMK_Async2))
         , m_resumptionBBs(comp->getAllocator(CMK_Async2))
     {
     }

From 3a76d37334f8a6f0673c80191b40d3408969a0f0 Mon Sep 17 00:00:00 2001
From: Jakob Botsch Nielsen <jakob.botsch.nielsen@gmail.com>
Date: Tue, 1 Apr 2025 11:47:13 +0200
Subject: [PATCH 182/203] Write function headers for new JIT functions

---
 src/coreclr/jit/async.cpp | 394 ++++++++++++++++++++++++++++++--------
 src/coreclr/jit/async.h   | 107 ++++++-----
 2 files changed, 370 insertions(+), 131 deletions(-)

diff --git a/src/coreclr/jit/async.cpp b/src/coreclr/jit/async.cpp
index 34731e1c42a672..5361493e1ec909 100644
--- a/src/coreclr/jit/async.cpp
+++ b/src/coreclr/jit/async.cpp
@@ -24,7 +24,7 @@ class AsyncLiveness
     void StartBlock(BasicBlock* block);
     void Update(GenTree* node);
     bool IsLive(unsigned lclNum);
-    void GetLiveLocals(jitstd::vector<Async2Transformation::LiveLocalInfo>& liveLocals, unsigned fullyDefinedRetBufLcl);
+    void GetLiveLocals(jitstd::vector<LiveLocalInfo>& liveLocals, unsigned fullyDefinedRetBufLcl);
 
 private:
     bool IsLocalCaptureUnnecessary(unsigned lclNum);
@@ -210,14 +210,13 @@ bool AsyncLiveness::IsLive(unsigned lclNum)
 //   liveLocals            - Vector to add live local information into
 //   fullyDefinedRetBufLcl - Local to skip even if live
 //
-void AsyncLiveness::GetLiveLocals(jitstd::vector<Async2Transformation::LiveLocalInfo>& liveLocals,
-                                  unsigned                                             fullyDefinedRetBufLcl)
+void AsyncLiveness::GetLiveLocals(jitstd::vector<LiveLocalInfo>& liveLocals, unsigned fullyDefinedRetBufLcl)
 {
     for (unsigned lclNum = 0; lclNum < m_numVars; lclNum++)
     {
         if ((lclNum != fullyDefinedRetBufLcl) && IsLive(lclNum))
         {
-            liveLocals.push_back(Async2Transformation::LiveLocalInfo(lclNum));
+            liveLocals.push_back(LiveLocalInfo(lclNum));
         }
     }
 }
@@ -388,7 +387,7 @@ PhaseStatus Async2Transformation::Run()
 //   Transform a single async2 call in the specified block.
 //
 // Parameters:
-//   block     - The block containig the async2 call
+//   block     - The block containing the async2 call
 //   call      - The async2 call
 //   defs      - Current live LIR edges
 //   life      - Liveness information about live locals
@@ -429,11 +428,11 @@ void Async2Transformation::Transform(
     unsigned stateNum = (unsigned)m_resumptionBBs.size();
     JITDUMP("  Assigned state %u\n", stateNum);
 
-    BasicBlock* suspendBB = CreateSuspension(block, stateNum, layout, life, liveLocals);
+    BasicBlock* suspendBB = CreateSuspension(block, stateNum, life, liveLocals);
 
     CreateCheckAndSuspendAfterCall(block, callDefInfo, life, suspendBB, remainder);
 
-    BasicBlock* resumeBB = CreateResumption(block, *remainder, call, callDefInfo, stateNum, layout, liveLocals);
+    BasicBlock* resumeBB = CreateResumption(block, *remainder, call, callDefInfo, stateNum, layout);
 
     m_resumptionBBs.push_back(resumeBB);
 }
@@ -444,11 +443,11 @@ void Async2Transformation::Transform(
 //   specified call.
 //
 // Parameters:
-//   block        - The block containig the async2 call
+//   block        - The block containing the async2 call
 //   call         - The async2 call
 //   defs         - Current live LIR edges
 //   life         - Liveness information about live locals
-//   livenessInfo - Information about live state
+//   liveLocals   - Information about each live local.
 //
 void Async2Transformation::CreateLiveSetForSuspension(BasicBlock*                     block,
                                                       GenTreeCall*                    call,
@@ -477,7 +476,7 @@ void Async2Transformation::CreateLiveSetForSuspension(BasicBlock*
     }
 
     life.GetLiveLocals(liveLocals, fullyDefinedRetBufLcl);
-    LiftLIREdges(block, call, defs, liveLocals);
+    LiftLIREdges(block, defs, liveLocals);
 
 #ifdef DEBUG
     if (m_comp->verbose)
@@ -499,11 +498,72 @@ void Async2Transformation::CreateLiveSetForSuspension(BasicBlock*
 #endif
 }
 
+//------------------------------------------------------------------------
+// Async2Transformation::LiftLIREdges:
+//   Create locals capturing outstanding LIR edges and add information
+//   indicating that these locals are live.
+//
+// Parameters:
+//   block      - The block containing the definitions of the LIR edges
+//   defs       - Current outstanding LIR edges
+//   liveLocals - [out] Vector to add new live local information into
+//
+void Async2Transformation::LiftLIREdges(BasicBlock*                     block,
+                                        const jitstd::vector<GenTree*>& defs,
+                                        jitstd::vector<LiveLocalInfo>&  liveLocals)
+{
+    if (defs.size() <= 0)
+    {
+        return;
+    }
+
+    for (GenTree* tree : defs)
+    {
+        // TODO-CQ: Enable this. It currently breaks our recognition of how the
+        // call is stored.
+        // if (tree->OperIs(GT_LCL_VAR))
+        //{
+        //    LclVarDsc* dsc = m_comp->lvaGetDesc(tree->AsLclVarCommon());
+        //    if (!dsc->IsAddressExposed())
+        //    {
+        //        // No interference by IR invariants.
+        //        LIR::AsRange(block).Remove(tree);
+        //        LIR::AsRange(block).InsertAfter(beyond, tree);
+        //        continue;
+        //    }
+        //}
+
+        LIR::Use use;
+        bool     gotUse = LIR::AsRange(block).TryGetUse(tree, &use);
+        assert(gotUse); // Defs list should not contain unused values.
+
+        unsigned newLclNum = use.ReplaceWithLclVar(m_comp);
+        liveLocals.push_back(LiveLocalInfo(newLclNum));
+        GenTree* newUse = use.Def();
+        LIR::AsRange(block).Remove(newUse);
+        LIR::AsRange(block).InsertBefore(use.User(), newUse);
+    }
+}
+
+//------------------------------------------------------------------------
+// Async2Transformation::LayOutContinuation:
+//   Create the layout of the GC pointer and data arrays in the continuation
+//   object.
+//
+// Parameters:
+//   block      - The block containing the async2 call
+//   call       - The async2 call
+//   liveLocals - [in, out] Information about each live local. Size/alignment
+//                information is read and offset/index information is written.
+//
+// Returns:
+//   Layout information.
+//
 ContinuationLayout Async2Transformation::LayOutContinuation(BasicBlock*                    block,
                                                             GenTreeCall*                   call,
                                                             jitstd::vector<LiveLocalInfo>& liveLocals)
 {
-    ContinuationLayout layout;
+    ContinuationLayout layout(liveLocals);
 
     for (LiveLocalInfo& inf : liveLocals)
     {
@@ -652,6 +712,20 @@ ContinuationLayout Async2Transformation::LayOutContinuation(BasicBlock*
     return layout;
 }
 
+//------------------------------------------------------------------------
+// Async2Transformation::CanonicalizeCallDefinition:
+//   Put the call definition in a canonical form. This ensures that either the
+//   value is defined by a LCL_ADDR retbuffer or by a
+//   STORE_LCL_VAR/STORE_LCL_FLD that follows the call node.
+//
+// Parameters:
+//   block        - The block containing the async2 call
+//   call         - The async2 call
+//   life         - Liveness information about live locals
+//
+// Returns:
+//   Information about the definition after canonicalization.
+//
 CallDefinitionInfo Async2Transformation::CanonicalizeCallDefinition(BasicBlock*    block,
                                                                     GenTreeCall*   call,
                                                                     AsyncLiveness& life)
@@ -702,11 +776,24 @@ CallDefinitionInfo Async2Transformation::CanonicalizeCallDefinition(BasicBlock*
     return callDefInfo;
 }
 
-BasicBlock* Async2Transformation::CreateSuspension(BasicBlock*                    block,
-                                                   unsigned                       stateNum,
-                                                   const ContinuationLayout&      layout,
-                                                   AsyncLiveness&                 life,
-                                                   jitstd::vector<LiveLocalInfo>& liveLocals)
+//------------------------------------------------------------------------
+// Async2Transformation::CreateSuspension:
+//   Create the basic block that when branched to suspends execution after the
+//   specified async2 call.
+//
+// Parameters:
+//   block    - The block containing the async2 call
+//   stateNum - State number assigned to this suspension point
+//   life     - Liveness information about live locals
+//   layout   - Layout information for the continuation object
+//
+// Returns:
+//   The new basic block that was created.
+//
+BasicBlock* Async2Transformation::CreateSuspension(BasicBlock*               block,
+                                                   unsigned                  stateNum,
+                                                   AsyncLiveness&            life,
+                                                   const ContinuationLayout& layout)
 {
     if (m_lastSuspensionBB == nullptr)
     {
@@ -771,12 +858,12 @@ BasicBlock* Async2Transformation::CreateSuspension(BasicBlock*
 
     if (layout.GCRefsCount > 0)
     {
-        FillInGCPointersOnSuspension(liveLocals, suspendBB);
+        FillInGCPointersOnSuspension(layout.Locals, suspendBB);
     }
 
     if (layout.DataSize > 0)
     {
-        FillInDataOnSuspension(liveLocals, suspendBB);
+        FillInDataOnSuspension(layout.Locals, suspendBB);
     }
 
     if (suspendBB->KindIs(BBJ_RETURN))
@@ -789,6 +876,19 @@ BasicBlock* Async2Transformation::CreateSuspension(BasicBlock*
     return suspendBB;
 }
 
+//------------------------------------------------------------------------
+// Async2Transformation::CreateAllocContinuationCall:
+//   Create a call to the JIT helper that allocates a continuation.
+//
+// Parameters:
+//   life             - Liveness information about live locals
+//   prevContinuation - IR node that has the value of the previous continuation object
+//   gcRefsCount      - Number of GC refs to allocate in the continuation object
+//   dataSize         - Number of bytes to allocate in the continuation object
+//
+// Returns:
+//   IR node representing the allocation.
+//
 GenTreeCall* Async2Transformation::CreateAllocContinuationCall(AsyncLiveness& life,
                                                                GenTree*       prevContinuation,
                                                                unsigned       gcRefsCount,
@@ -797,7 +897,7 @@ GenTreeCall* Async2Transformation::CreateAllocContinuationCall(AsyncLiveness& li
     GenTree* gcRefsCountNode = m_comp->gtNewIconNode((ssize_t)gcRefsCount, TYP_I_IMPL);
     GenTree* dataSizeNode    = m_comp->gtNewIconNode((ssize_t)dataSize, TYP_I_IMPL);
     // If VM requests that we report the method handle, or if we have a shared generic context method handle
-    // that is live here, then we need to call a different helper.
+    // that is live here, then we need to call a different helper to keep the loader alive.
     GenTree* methodHandleArg = nullptr;
     GenTree* classHandleArg  = nullptr;
     if (((m_comp->info.compMethodInfo->options & CORINFO_GENERICS_CTXT_FROM_METHODDESC) != 0) &&
@@ -831,8 +931,16 @@ GenTreeCall* Async2Transformation::CreateAllocContinuationCall(AsyncLiveness& li
                                        dataSizeNode);
 }
 
-void Async2Transformation::FillInGCPointersOnSuspension(jitstd::vector<LiveLocalInfo>& liveLocals,
-                                                        BasicBlock*                    suspendBB)
+//------------------------------------------------------------------------
+// Async2Transformation::FillInGCPointersOnSuspension:
+//   Create IR that fills the GC pointers of the continuation object.
+//
+// Parameters:
+//   liveLocals - Information about each live local.
+//   suspendBB  - Basic block to add IR to.
+//
+void Async2Transformation::FillInGCPointersOnSuspension(const jitstd::vector<LiveLocalInfo>& liveLocals,
+                                                        BasicBlock*                          suspendBB)
 {
     unsigned objectArrLclNum = GetGCDataArrayVar();
 
@@ -842,7 +950,7 @@ void Async2Transformation::FillInGCPointersOnSuspension(jitstd::vector<LiveLocal
     GenTree* storeAllocedObjectArr = m_comp->gtNewStoreLclVarNode(objectArrLclNum, gcDataInd);
     LIR::AsRange(suspendBB).InsertAtEnd(LIR::SeqTree(m_comp, storeAllocedObjectArr));
 
-    for (LiveLocalInfo& inf : liveLocals)
+    for (const LiveLocalInfo& inf : liveLocals)
     {
         if (inf.GCDataCount <= 0)
         {
@@ -917,7 +1025,16 @@ void Async2Transformation::FillInGCPointersOnSuspension(jitstd::vector<LiveLocal
     }
 }
 
-void Async2Transformation::FillInDataOnSuspension(jitstd::vector<LiveLocalInfo>& liveLocals, BasicBlock* suspendBB)
+//------------------------------------------------------------------------
+// Async2Transformation::FillInDataOnSuspension:
+//   Create IR that fills the data array of the continuation object.
+//
+// Parameters:
+//   liveLocals - Information about each live local.
+//   suspendBB  - Basic block to add IR to.
+//
+void Async2Transformation::FillInDataOnSuspension(const jitstd::vector<LiveLocalInfo>& liveLocals,
+                                                  BasicBlock*                          suspendBB)
 {
     unsigned byteArrLclNum = GetDataArrayVar();
 
@@ -942,7 +1059,7 @@ void Async2Transformation::FillInDataOnSuspension(jitstd::vector<LiveLocalInfo>&
     }
 
     // Fill in data
-    for (LiveLocalInfo& inf : liveLocals)
+    for (const LiveLocalInfo& inf : liveLocals)
     {
         if (inf.DataSize <= 0)
         {
@@ -984,6 +1101,18 @@ void Async2Transformation::FillInDataOnSuspension(jitstd::vector<LiveLocalInfo>&
     }
 }
 
+//------------------------------------------------------------------------
+// Async2Transformation::CreateCheckAndSuspendAfterCall:
+//   Split the block containing the specified async2 call, and create the IR
+//   that checks whether suspension should be done after an async call.
+//
+// Parameters:
+//   block       - The block containing the async2 call
+//   callDefInfo - Information about the async2 call's definition
+//   life        - Liveness information about live locals
+//   suspendBB   - Basic block to add IR to
+//   remainder   - [out] The remainder block containing the IR that was after the async2 call.
+//
 void Async2Transformation::CreateCheckAndSuspendAfterCall(BasicBlock*               block,
                                                           const CallDefinitionInfo& callDefInfo,
                                                           AsyncLiveness&            life,
@@ -1012,13 +1141,28 @@ void Async2Transformation::CreateCheckAndSuspendAfterCall(BasicBlock*
     block->GetFalseEdge()->setLikelihood(1);
 }
 
-BasicBlock* Async2Transformation::CreateResumption(BasicBlock*                    block,
-                                                   BasicBlock*                    remainder,
-                                                   GenTreeCall*                   call,
-                                                   const CallDefinitionInfo&      callDefInfo,
-                                                   unsigned                       stateNum,
-                                                   const ContinuationLayout&      layout,
-                                                   jitstd::vector<LiveLocalInfo>& liveLocals)
+//------------------------------------------------------------------------
+// Async2Transformation::CreateResumption:
+//   Create the basic block that when branched to resumes execution on entry to
+//   the function.
+//
+// Parameters:
+//   block       - The block containing the async2 call
+//   remainder   - The block that contains the IR after the (split) async2 call
+//   call        - The async2 call
+//   callDefInfo - Information about the async2 call's definition
+//   stateNum    - State number assigned to this suspension point
+//   layout      - Layout information for the continuation object
+//
+// Returns:
+//   The new basic block that was created.
+//
+BasicBlock* Async2Transformation::CreateResumption(BasicBlock*               block,
+                                                   BasicBlock*               remainder,
+                                                   GenTreeCall*              call,
+                                                   const CallDefinitionInfo& callDefInfo,
+                                                   unsigned                  stateNum,
+                                                   const ContinuationLayout& layout)
 {
     if (m_lastResumptionBB == nullptr)
     {
@@ -1052,7 +1196,7 @@ BasicBlock* Async2Transformation::CreateResumption(BasicBlock*
 
         LIR::AsRange(resumeBB).InsertAtEnd(LIR::SeqTree(m_comp, storeAllocedByteArr));
 
-        RestoreFromDataOnResumption(resumeByteArrLclNum, liveLocals, resumeBB);
+        RestoreFromDataOnResumption(resumeByteArrLclNum, layout.Locals, resumeBB);
     }
 
     unsigned    resumeObjectArrLclNum = BAD_VAR_NUM;
@@ -1068,7 +1212,7 @@ BasicBlock* Async2Transformation::CreateResumption(BasicBlock*
         GenTree* storeAllocedObjectArr = m_comp->gtNewStoreLclVarNode(resumeObjectArrLclNum, gcDataInd);
         LIR::AsRange(resumeBB).InsertAtEnd(LIR::SeqTree(m_comp, storeAllocedObjectArr));
 
-        RestoreFromGCPointersOnResumption(resumeObjectArrLclNum, liveLocals, resumeBB);
+        RestoreFromGCPointersOnResumption(resumeObjectArrLclNum, layout.Locals, resumeBB);
 
         if (layout.ExceptionGCDataIndex != UINT_MAX)
         {
@@ -1086,12 +1230,22 @@ BasicBlock* Async2Transformation::CreateResumption(BasicBlock*
     return resumeBB;
 }
 
-void Async2Transformation::RestoreFromDataOnResumption(unsigned                       resumeByteArrLclNum,
-                                                       jitstd::vector<LiveLocalInfo>& liveLocals,
-                                                       BasicBlock*                    resumeBB)
+//------------------------------------------------------------------------
+// Async2Transformation::RestoreFromDataOnResumption:
+//   Create IR that restores locals from the data array of the continuation
+//   object.
+//
+// Parameters:
+//   resumeByteArrLclNum - Local that has the continuation object's data array
+//   liveLocals          - Information about each live local.
+//   resumeBB            - Basic block to append IR to
+//
+void Async2Transformation::RestoreFromDataOnResumption(unsigned                             resumeByteArrLclNum,
+                                                       const jitstd::vector<LiveLocalInfo>& liveLocals,
+                                                       BasicBlock*                          resumeBB)
 {
     // Copy data
-    for (LiveLocalInfo& inf : liveLocals)
+    for (const LiveLocalInfo& inf : liveLocals)
     {
         if (inf.DataSize <= 0)
         {
@@ -1133,11 +1287,21 @@ void Async2Transformation::RestoreFromDataOnResumption(unsigned
     }
 }
 
-void Async2Transformation::RestoreFromGCPointersOnResumption(unsigned                       resumeObjectArrLclNum,
-                                                             jitstd::vector<LiveLocalInfo>& liveLocals,
-                                                             BasicBlock*                    resumeBB)
+//------------------------------------------------------------------------
+// Async2Transformation::RestoreFromGCPointersOnResumption:
+//   Create IR that restores locals from the GC pointers array of the
+//   continuation object.
+//
+// Parameters:
+//   resumeObjectArrLclNum - Local that has the continuation object's GC pointers array
+//   liveLocals            - Information about each live local.
+//   resumeBB              - Basic block to append IR to
+//
+void Async2Transformation::RestoreFromGCPointersOnResumption(unsigned                             resumeObjectArrLclNum,
+                                                             const jitstd::vector<LiveLocalInfo>& liveLocals,
+                                                             BasicBlock*                          resumeBB)
 {
-    for (LiveLocalInfo& inf : liveLocals)
+    for (const LiveLocalInfo& inf : liveLocals)
     {
         if (inf.GCDataCount <= 0)
         {
@@ -1178,8 +1342,7 @@ void Async2Transformation::RestoreFromGCPointersOnResumption(unsigned
                 {
                     GenTree* baseAddr = m_comp->gtNewLclvNode(inf.LclNum, dsc->TypeGet());
                     store             = StoreAtOffset(baseAddr, i * TARGET_POINTER_SIZE, value);
-                    // Implicit byref args are never on heap today, skip write barriers.
-                    // TODO-CQ: Remove this once all implicit byrefs are TYP_I_IMPL typed.
+                    // Implicit byref args are never on heap
                     store->gtFlags |= GTF_IND_TGT_NOT_HEAP;
                 }
                 else
@@ -1195,6 +1358,23 @@ void Async2Transformation::RestoreFromGCPointersOnResumption(unsigned
     }
 }
 
+//------------------------------------------------------------------------
+// Async2Transformation::RethrowExceptionOnResumption:
+//   Create IR that checks for an exception and rethrows it at the original
+//   suspension point if necessary.
+//
+// Parameters:
+//   block                 - The block containing the async2 call
+//   remainder             - The block that contains the IR after the (split) async2 call
+//   resumeObjectArrLclNum - Local that has the continuation object's GC pointers array
+//   layout                - Layout information for the continuation object
+//   resumeBB              - Basic block to append IR to
+//
+// Returns:
+//   The new non-exception successor basic block for resumption. This is the
+//   basic block where execution will continue if there was no exception to
+//   rethrow.
+//
 BasicBlock* Async2Transformation::RethrowExceptionOnResumption(BasicBlock*               block,
                                                                BasicBlock*               remainder,
                                                                unsigned                  resumeObjectArrLclNum,
@@ -1256,6 +1436,20 @@ BasicBlock* Async2Transformation::RethrowExceptionOnResumption(BasicBlock*
     return storeResultBB;
 }
 
+//------------------------------------------------------------------------
+// Async2Transformation::CopyReturnValueOnResumption:
+//   Create IR that copies the return value from the continuation object to the
+//   right local.
+//
+// Parameters:
+//   call                  - The async2 call
+//   callDefInfo           - Information about the async2 call's definition
+//   block                 - The block containing the async2 call
+//   resumeByteArrLclNum   - Local that has the continuation object's data array
+//   resumeObjectArrLclNum - Local that has the continuation object's GC pointers array
+//   layout                - Layout information for the continuation object
+//   storeResultBB         - Basic block to append IR to
+//
 void Async2Transformation::CopyReturnValueOnResumption(GenTreeCall*              call,
                                                        const CallDefinitionInfo& callDefInfo,
                                                        unsigned                  resumeByteArrLclNum,
@@ -1370,6 +1564,19 @@ void Async2Transformation::CopyReturnValueOnResumption(GenTreeCall*
     }
 }
 
+//------------------------------------------------------------------------
+// Async2Transformation::LoadFromOffset:
+//   Create a load.
+//
+// Parameters:
+//   base       - Base address of the load
+//   offset     - Offset to add on top of the base address
+//   type       - Type of the load to create
+//   indirFlags - Flags to add to the load
+//
+// Returns:
+//   IR node of the load.
+//
 GenTreeIndir* Async2Transformation::LoadFromOffset(GenTree*     base,
                                                    unsigned     offset,
                                                    var_types    type,
@@ -1383,6 +1590,18 @@ GenTreeIndir* Async2Transformation::LoadFromOffset(GenTree*     base,
     return load;
 }
 
+//------------------------------------------------------------------------
+// Async2Transformation::LoadFromOffset:
+//   Create a store.
+//
+// Parameters:
+//   base       - Base address of the store
+//   offset     - Offset to add on top of the base address
+//   value      - Value to store
+//
+// Returns:
+//   IR node of the store.
+//
 GenTreeStoreInd* Async2Transformation::StoreAtOffset(GenTree* base, unsigned offset, GenTree* value)
 {
     assert(base->TypeIs(TYP_REF, TYP_BYREF, TYP_I_IMPL));
@@ -1393,6 +1612,15 @@ GenTreeStoreInd* Async2Transformation::StoreAtOffset(GenTree* base, unsigned off
     return store;
 }
 
+//------------------------------------------------------------------------
+// Async2Transformation::GetDataArrayVar:
+//   Create a new local to hold the data array of the continuation object. This
+//   local can be validly used for the entire suspension point; the returned
+//   local may be used by multiple suspension points.
+//
+// Returns:
+//   Local number.
+//
 unsigned Async2Transformation::GetDataArrayVar()
 {
     // Create separate locals unless we have many locals in the method for live
@@ -1407,6 +1635,15 @@ unsigned Async2Transformation::GetDataArrayVar()
     return m_dataArrayVar;
 }
 
+//------------------------------------------------------------------------
+// Async2Transformation::GetGCDataArrayVar:
+//   Create a new local to hold the GC pointers array of the continuation
+//   object. This local can be validly used for the entire suspension point;
+//   the returned local may be used by multiple suspension points.
+//
+// Returns:
+//   Local number.
+//
 unsigned Async2Transformation::GetGCDataArrayVar()
 {
     if ((m_gcDataArrayVar == BAD_VAR_NUM) || !m_comp->lvaHaveManyLocals())
@@ -1418,6 +1655,15 @@ unsigned Async2Transformation::GetGCDataArrayVar()
     return m_gcDataArrayVar;
 }
 
+//------------------------------------------------------------------------
+// Async2Transformation::GetResultBaseVar:
+//   Create a new local to hold the base address of the incoming result from
+//   the continuation. This local can be validly used for the entire suspension
+//   point; the returned local may be used by multiple suspension points.
+//
+// Returns:
+//   Local number.
+//
 unsigned Async2Transformation::GetResultBaseVar()
 {
     if ((m_resultBaseVar == BAD_VAR_NUM) || !m_comp->lvaHaveManyLocals())
@@ -1429,6 +1675,15 @@ unsigned Async2Transformation::GetResultBaseVar()
     return m_resultBaseVar;
 }
 
+//------------------------------------------------------------------------
+// Async2Transformation::GetExceptionVar:
+//   Create a new local to hold the exception in the continuation. This
+//   local can be validly used for the entire suspension point; the returned
+//   local may be used by multiple suspension points.
+//
+// Returns:
+//   Local number.
+//
 unsigned Async2Transformation::GetExceptionVar()
 {
     if ((m_exceptionVar == BAD_VAR_NUM) || !m_comp->lvaHaveManyLocals())
@@ -1440,6 +1695,14 @@ unsigned Async2Transformation::GetExceptionVar()
     return m_exceptionVar;
 }
 
+//------------------------------------------------------------------------
+// Async2Transformation::CreateResumptionStubAddrTree:
+//   Create a tree that represents the address of the resumption stub entry
+//   point.
+//
+// Returns:
+//   IR node.
+//
 GenTree* Async2Transformation::CreateResumptionStubAddrTree()
 {
     switch (m_resumeStubLookup.accessType)
@@ -1475,6 +1738,14 @@ GenTree* Async2Transformation::CreateResumptionStubAddrTree()
     }
 }
 
+//------------------------------------------------------------------------
+// Async2Transformation::CreateFunctionTargetAddr:
+//   Create a tree that represents the address of the resumption stub entry
+//   point.
+//
+// Returns:
+//   IR node.
+//
 GenTree* Async2Transformation::CreateFunctionTargetAddr(CORINFO_METHOD_HANDLE       methHnd,
                                                         const CORINFO_CONST_LOOKUP& lookup)
 {
@@ -1483,43 +1754,6 @@ GenTree* Async2Transformation::CreateFunctionTargetAddr(CORINFO_METHOD_HANDLE
     return con;
 }
 
-void Async2Transformation::LiftLIREdges(BasicBlock*                     block,
-                                        GenTree*                        beyond,
-                                        const jitstd::vector<GenTree*>& defs,
-                                        jitstd::vector<LiveLocalInfo>&  liveLocals)
-{
-    if (defs.size() <= 0)
-    {
-        return;
-    }
-
-    for (GenTree* tree : defs)
-    {
-        // TODO-CQ: Breaks our recognition of how the call is stored.
-        // if (tree->OperIs(GT_LCL_VAR))
-        //{
-        //    LclVarDsc* dsc = m_comp->lvaGetDesc(tree->AsLclVarCommon());
-        //    if (!dsc->IsAddressExposed())
-        //    {
-        //        // No interference by IR invariants.
-        //        LIR::AsRange(block).Remove(tree);
-        //        LIR::AsRange(block).InsertAfter(beyond, tree);
-        //        continue;
-        //    }
-        //}
-
-        LIR::Use use;
-        bool     gotUse = LIR::AsRange(block).TryGetUse(tree, &use);
-        assert(gotUse); // Defs list should not contain unused values.
-
-        unsigned newLclNum = use.ReplaceWithLclVar(m_comp);
-        liveLocals.push_back(LiveLocalInfo(newLclNum));
-        GenTree* newUse = use.Def();
-        LIR::AsRange(block).Remove(newUse);
-        LIR::AsRange(block).InsertBefore(use.User(), newUse);
-    }
-}
-
 void Async2Transformation::CreateResumptionSwitch()
 {
     m_comp->fgCreateNewInitBB();
diff --git a/src/coreclr/jit/async.h b/src/coreclr/jit/async.h
index f99d5817f3bc08..a07283b2b5fbab 100644
--- a/src/coreclr/jit/async.h
+++ b/src/coreclr/jit/async.h
@@ -1,15 +1,36 @@
 // Licensed to the .NET Foundation under one or more agreements.
 // The .NET Foundation licenses this file to you under the MIT license.
 
+struct LiveLocalInfo
+{
+    unsigned LclNum;
+    unsigned Alignment;
+    unsigned DataOffset;
+    unsigned DataSize;
+    unsigned GCDataIndex;
+    unsigned GCDataCount;
+
+    explicit LiveLocalInfo(unsigned lclNum)
+        : LclNum(lclNum)
+    {
+    }
+};
+
 struct ContinuationLayout
 {
-    unsigned     DataSize             = 0;
-    unsigned     GCRefsCount          = 0;
-    ClassLayout* ReturnStructLayout   = nullptr;
-    unsigned     ReturnSize           = 0;
-    bool         ReturnInGCData       = false;
-    unsigned     ReturnValDataOffset  = UINT_MAX;
-    unsigned     ExceptionGCDataIndex = UINT_MAX;
+    unsigned                             DataSize             = 0;
+    unsigned                             GCRefsCount          = 0;
+    ClassLayout*                         ReturnStructLayout   = nullptr;
+    unsigned                             ReturnSize           = 0;
+    bool                                 ReturnInGCData       = false;
+    unsigned                             ReturnValDataOffset  = UINT_MAX;
+    unsigned                             ExceptionGCDataIndex = UINT_MAX;
+    const jitstd::vector<LiveLocalInfo>& Locals;
+
+    ContinuationLayout(jitstd::vector<LiveLocalInfo>& locals)
+        : Locals(locals)
+    {
+    }
 };
 
 struct CallDefinitionInfo
@@ -24,21 +45,6 @@ class Async2Transformation
 {
     friend class AsyncLiveness;
 
-    struct LiveLocalInfo
-    {
-        unsigned LclNum;
-        unsigned Alignment;
-        unsigned DataOffset;
-        unsigned DataSize;
-        unsigned GCDataIndex;
-        unsigned GCDataCount;
-
-        explicit LiveLocalInfo(unsigned lclNum)
-            : LclNum(lclNum)
-        {
-        }
-    };
-
     Compiler*                     m_comp;
     jitstd::vector<LiveLocalInfo> m_liveLocalsScratch;
     CORINFO_ASYNC2_INFO           m_async2Info;
@@ -55,10 +61,6 @@ class Async2Transformation
     BasicBlock*                   m_lastResumptionBB        = nullptr;
     BasicBlock*                   m_sharedReturnBB          = nullptr;
 
-    void LiftLIREdges(BasicBlock*                     block,
-                      GenTree*                        beyond,
-                      const jitstd::vector<GenTree*>& defs,
-                      jitstd::vector<LiveLocalInfo>&  liveLocals);
     bool IsLive(unsigned lclNum);
     void Transform(BasicBlock*               block,
                    GenTreeCall*              call,
@@ -66,47 +68,50 @@ class Async2Transformation
                    class AsyncLiveness&      life,
                    BasicBlock**              remainder);
 
-    void               CreateLiveSetForSuspension(BasicBlock*                     block,
-                                                  GenTreeCall*                    call,
-                                                  const jitstd::vector<GenTree*>& defs,
-                                                  AsyncLiveness&                  life,
-                                                  jitstd::vector<LiveLocalInfo>&  liveLocals);
+    void CreateLiveSetForSuspension(BasicBlock*                     block,
+                                    GenTreeCall*                    call,
+                                    const jitstd::vector<GenTree*>& defs,
+                                    AsyncLiveness&                  life,
+                                    jitstd::vector<LiveLocalInfo>&  liveLocals);
+
+    void LiftLIREdges(BasicBlock*                     block,
+                      const jitstd::vector<GenTree*>& defs,
+                      jitstd::vector<LiveLocalInfo>&  liveLocals);
+
     ContinuationLayout LayOutContinuation(BasicBlock*                    block,
                                           GenTreeCall*                   call,
                                           jitstd::vector<LiveLocalInfo>& liveLocals);
 
     CallDefinitionInfo CanonicalizeCallDefinition(BasicBlock* block, GenTreeCall* call, AsyncLiveness& life);
 
-    BasicBlock*  CreateSuspension(BasicBlock*                    block,
-                                  unsigned                       stateNum,
-                                  const ContinuationLayout&      layout,
-                                  AsyncLiveness&                 life,
-                                  jitstd::vector<LiveLocalInfo>& liveLocals);
+    BasicBlock*  CreateSuspension(BasicBlock*               block,
+                                  unsigned                  stateNum,
+                                  AsyncLiveness&            life,
+                                  const ContinuationLayout& layout);
     GenTreeCall* CreateAllocContinuationCall(AsyncLiveness& life,
                                              GenTree*       prevContinuation,
                                              unsigned       gcRefsCount,
                                              unsigned int   dataSize);
-    void         FillInGCPointersOnSuspension(jitstd::vector<LiveLocalInfo>& liveLocals, BasicBlock* suspendBB);
-    void         FillInDataOnSuspension(jitstd::vector<LiveLocalInfo>& liveLocals, BasicBlock* suspendBB);
+    void         FillInGCPointersOnSuspension(const jitstd::vector<LiveLocalInfo>& liveLocals, BasicBlock* suspendBB);
+    void         FillInDataOnSuspension(const jitstd::vector<LiveLocalInfo>& liveLocals, BasicBlock* suspendBB);
     void         CreateCheckAndSuspendAfterCall(BasicBlock*               block,
                                                 const CallDefinitionInfo& callDefInfo,
                                                 AsyncLiveness&            life,
                                                 BasicBlock*               suspendBB,
                                                 BasicBlock**              remainder);
 
-    BasicBlock* CreateResumption(BasicBlock*                    block,
-                                 BasicBlock*                    remainder,
-                                 GenTreeCall*                   call,
-                                 const CallDefinitionInfo&      callDefInfo,
-                                 unsigned                       stateNum,
-                                 const ContinuationLayout&      layout,
-                                 jitstd::vector<LiveLocalInfo>& liveLocals);
-    void        RestoreFromDataOnResumption(unsigned                       resumeByteArrLclNum,
-                                            jitstd::vector<LiveLocalInfo>& liveLocals,
-                                            BasicBlock*                    resumeBB);
-    void        RestoreFromGCPointersOnResumption(unsigned                       resumeObjectArrLclNum,
-                                                  jitstd::vector<LiveLocalInfo>& liveLocals,
-                                                  BasicBlock*                    resumeBB);
+    BasicBlock* CreateResumption(BasicBlock*               block,
+                                 BasicBlock*               remainder,
+                                 GenTreeCall*              call,
+                                 const CallDefinitionInfo& callDefInfo,
+                                 unsigned                  stateNum,
+                                 const ContinuationLayout& layout);
+    void        RestoreFromDataOnResumption(unsigned                             resumeByteArrLclNum,
+                                            const jitstd::vector<LiveLocalInfo>& liveLocals,
+                                            BasicBlock*                          resumeBB);
+    void        RestoreFromGCPointersOnResumption(unsigned                             resumeObjectArrLclNum,
+                                                  const jitstd::vector<LiveLocalInfo>& liveLocals,
+                                                  BasicBlock*                          resumeBB);
     BasicBlock* RethrowExceptionOnResumption(BasicBlock*               block,
                                              BasicBlock*               remainder,
                                              unsigned                  resumeObjectArrLclNum,

From 828e7b2636b6313c30953935fad39ad35de5119b Mon Sep 17 00:00:00 2001
From: Jakob Botsch Nielsen <jakob.botsch.nielsen@gmail.com>
Date: Tue, 1 Apr 2025 11:52:39 +0200
Subject: [PATCH 183/203] Remove unused functions

---
 src/coreclr/jit/block.h     | 10 ----------
 src/coreclr/jit/fgbasic.cpp |  2 --
 2 files changed, 12 deletions(-)

diff --git a/src/coreclr/jit/block.h b/src/coreclr/jit/block.h
index d38d7d726a7b8b..2becb39f992089 100644
--- a/src/coreclr/jit/block.h
+++ b/src/coreclr/jit/block.h
@@ -1412,16 +1412,6 @@ struct BasicBlock : private LIR::Range
         m_firstNode = tree;
     }
 
-    GenTree* GetLastLIRNode() const
-    {
-        return m_lastNode;
-    }
-
-    void SetLastLIRNode(GenTree* tree)
-    {
-        m_lastNode = tree;
-    }
-
     EntryState* bbEntryState; // verifier tracked state of all entries in stack.
 
 #define NO_BASE_TMP UINT_MAX // base# to use when we have none
diff --git a/src/coreclr/jit/fgbasic.cpp b/src/coreclr/jit/fgbasic.cpp
index 515128ce7243b6..b570c5a8173854 100644
--- a/src/coreclr/jit/fgbasic.cpp
+++ b/src/coreclr/jit/fgbasic.cpp
@@ -4862,9 +4862,7 @@ BasicBlock* Compiler::fgSplitBlockAtBeginning(BasicBlock* curr)
     if (curr->IsLIR())
     {
         newBlock->SetFirstLIRNode(curr->GetFirstLIRNode());
-        newBlock->SetLastLIRNode(curr->GetLastLIRNode());
         curr->SetFirstLIRNode(nullptr);
-        curr->SetLastLIRNode(nullptr);
     }
     else
     {

From 52ce4f79b07a52529a4b727d2344ac21ad67973c Mon Sep 17 00:00:00 2001
From: Jakob Botsch Nielsen <jakob.botsch.nielsen@gmail.com>
Date: Tue, 1 Apr 2025 12:24:55 +0200
Subject: [PATCH 184/203] More function headers and clean up

---
 src/coreclr/jit/codegencommon.cpp |  18 +++
 src/coreclr/jit/layout.cpp        |   6 +
 src/coreclr/jit/lclvars.cpp       |   7 ++
 src/coreclr/jit/lower.cpp         |  13 ++
 src/coreclr/jit/lsrabuild.cpp     |   8 ++
 src/coreclr/jit/optcse.cpp        | 202 ++++++++++++++++--------------
 6 files changed, 159 insertions(+), 95 deletions(-)

diff --git a/src/coreclr/jit/codegencommon.cpp b/src/coreclr/jit/codegencommon.cpp
index be56cc82991cc8..79db11e1e102e4 100644
--- a/src/coreclr/jit/codegencommon.cpp
+++ b/src/coreclr/jit/codegencommon.cpp
@@ -7317,6 +7317,13 @@ void CodeGen::genSwiftErrorReturn(GenTree* treeNode)
 }
 #endif // SWIFT_SUPPORT
 
+//------------------------------------------------------------------------
+// genReturnSuspend:
+//   Generate code for a GT_RETURN_SUSPEND node
+//
+// Arguments:
+//   treeNode - The node
+//
 void CodeGen::genReturnSuspend(GenTreeUnOp* treeNode)
 {
     GenTree* op = treeNode->gtGetOp1();
@@ -7339,6 +7346,10 @@ void CodeGen::genReturnSuspend(GenTreeUnOp* treeNode)
     genMarkReturnGCInfo();
 }
 
+//------------------------------------------------------------------------
+// genMarkReturnGCInfo:
+//   Mark GC and non-GC pointers of return registers going into the epilog..
+//
 void CodeGen::genMarkReturnGCInfo()
 {
     const ReturnTypeDesc& retTypeDesc = compiler->compRetTypeDesc;
@@ -7363,6 +7374,13 @@ void CodeGen::genMarkReturnGCInfo()
     }
 }
 
+//------------------------------------------------------------------------
+// genCodeForAsyncContinuation:
+//   Generate code for a GC_ASYNC_CONTINUATION node.
+//
+// Arguments:
+//   tree - The node
+//
 void CodeGen::genCodeForAsyncContinuation(GenTree* tree)
 {
     assert(tree->OperIs(GT_ASYNC_CONTINUATION));
diff --git a/src/coreclr/jit/layout.cpp b/src/coreclr/jit/layout.cpp
index 2e36fea45eb2c2..2b9a0197d07a75 100644
--- a/src/coreclr/jit/layout.cpp
+++ b/src/coreclr/jit/layout.cpp
@@ -536,6 +536,12 @@ ClassLayout* ClassLayout::Create(Compiler* compiler, const ClassLayoutBuilder& b
     return newLayout;
 }
 
+//------------------------------------------------------------------------
+// HasGCByRef: //   Check if this classlayout has a TYP_BYREF GC pointer in it.
+//
+// Return value:
+//   True if so.
+//
 bool ClassLayout::HasGCByRef() const
 {
     if (!HasGCPtr())
diff --git a/src/coreclr/jit/lclvars.cpp b/src/coreclr/jit/lclvars.cpp
index 46e1a5685a3168..5213828696bf53 100644
--- a/src/coreclr/jit/lclvars.cpp
+++ b/src/coreclr/jit/lclvars.cpp
@@ -685,6 +685,13 @@ void Compiler::lvaInitGenericsCtxt(unsigned* curVarNum)
     (*curVarNum)++;
 }
 
+//-----------------------------------------------------------------------------
+// lvaInitAsyncContinuation:
+//  Initialize the async continuation parameter.
+//
+// Type parameters:
+//   curVarNum - [in, out] The current local variable number for parameters
+//
 void Compiler::lvaInitAsyncContinuation(unsigned* curVarNum)
 {
     if (!compIsAsync2())
diff --git a/src/coreclr/jit/lower.cpp b/src/coreclr/jit/lower.cpp
index 9214ffd740a094..0d072a878adb74 100644
--- a/src/coreclr/jit/lower.cpp
+++ b/src/coreclr/jit/lower.cpp
@@ -5391,6 +5391,12 @@ void Lowering::LowerRetSingleRegStructLclVar(GenTreeUnOp* ret)
     }
 }
 
+//----------------------------------------------------------------------------------------------
+// LowerAsyncContinuation: Lower a GT_ASYNC_CONTINUATION node
+//
+// Arguments:
+//   asyncCont - Async continuation node
+//
 void Lowering::LowerAsyncContinuation(GenTree* asyncCont)
 {
     assert(asyncCont->OperIs(GT_ASYNC_CONTINUATION));
@@ -5419,6 +5425,13 @@ void Lowering::LowerAsyncContinuation(GenTree* asyncCont)
     }
 }
 
+//----------------------------------------------------------------------------------------------
+// LowerReturnSuspend:
+//   Lower a GT_RETURN_SUSPEND by making it a terminator node.
+//
+// Arguments:
+//   node - The node
+//
 void Lowering::LowerReturnSuspend(GenTree* node)
 {
     assert(node->OperIs(GT_RETURN_SUSPEND));
diff --git a/src/coreclr/jit/lsrabuild.cpp b/src/coreclr/jit/lsrabuild.cpp
index 902eed9e230720..4b9d310a9a8434 100644
--- a/src/coreclr/jit/lsrabuild.cpp
+++ b/src/coreclr/jit/lsrabuild.cpp
@@ -4742,6 +4742,14 @@ void LinearScan::MarkSwiftErrorBusyForCall(GenTreeCall* call)
 }
 #endif
 
+//------------------------------------------------------------------------
+// MarkAsyncContinuationBusyForCall:
+//   Add a ref position that marks the async continuation register as busy
+//   until it is killed.
+//
+// Arguments:
+//    call - The call node
+//
 void LinearScan::MarkAsyncContinuationBusyForCall(GenTreeCall* call)
 {
     // Async2 calls return an async continuation argument in a separate
diff --git a/src/coreclr/jit/optcse.cpp b/src/coreclr/jit/optcse.cpp
index cb5bb7a9f250cf..d3df3d3bdec1e4 100644
--- a/src/coreclr/jit/optcse.cpp
+++ b/src/coreclr/jit/optcse.cpp
@@ -1001,101 +1001,7 @@ void Compiler::optValnumCSE_InitDataFlow()
 
     if (compIsAsync2())
     {
-        bool anyAsyncKills = false;
-        cseAsyncKillsMask  = BitVecOps::MakeFull(cseLivenessTraits);
-        for (unsigned inx = 1; inx <= optCSECandidateCount; inx++)
-        {
-            CSEdsc* dsc = optCSEtab[inx - 1];
-            assert(dsc->csdIndex == inx);
-            bool isByRef = false;
-            if (dsc->csdTree->TypeIs(TYP_BYREF))
-            {
-                isByRef = true;
-            }
-            else if (dsc->csdTree->TypeIs(TYP_STRUCT))
-            {
-                ClassLayout* layout = dsc->csdTree->GetLayout(this);
-                isByRef             = layout->HasGCByRef();
-            }
-
-            if (isByRef)
-            {
-                // We generate a bit pattern like: 1111111100111100 where there
-                // are 0s only for the byref CSEs.
-                BitVecOps::RemoveElemD(cseLivenessTraits, cseAsyncKillsMask, getCSEAvailBit(inx));
-                BitVecOps::RemoveElemD(cseLivenessTraits, cseAsyncKillsMask, getCSEAvailCrossCallBit(inx));
-                anyAsyncKills = true;
-            }
-        }
-
-        if (anyAsyncKills)
-        {
-            for (BasicBlock* block : Blocks())
-            {
-                Statement* asyncCallStmt = nullptr;
-                GenTree*   asyncCall     = nullptr;
-                // Find last async call in block
-                Statement* stmt = block->lastStmt();
-                if (stmt == nullptr)
-                {
-                    continue;
-                }
-
-                while (asyncCall == nullptr)
-                {
-                    if ((stmt->GetRootNode()->gtFlags & GTF_CALL) != 0)
-                    {
-                        for (GenTree* tree = stmt->GetRootNode(); tree != nullptr; tree = tree->gtPrev)
-                        {
-                            if (tree->IsCall() && tree->AsCall()->IsAsync2())
-                            {
-                                asyncCallStmt = stmt;
-                                asyncCall     = tree;
-                                break;
-                            }
-                        }
-                    }
-
-                    if (stmt == block->firstStmt())
-                        break;
-
-                    stmt = stmt->GetPrevStmt();
-                }
-
-                if (asyncCall == nullptr)
-                {
-                    continue;
-                }
-
-                // This block has a suspension point. Make all BYREF CSEs unavailable.
-                BitVecOps::IntersectionD(cseLivenessTraits, block->bbCseGen, cseAsyncKillsMask);
-                BitVecOps::IntersectionD(cseLivenessTraits, block->bbCseOut, cseAsyncKillsMask);
-
-                // Now make all byref CSEs after the suspension point available.
-                Statement* curStmt = asyncCallStmt;
-                GenTree*   curTree = asyncCall;
-                while (true)
-                {
-                    do
-                    {
-                        if (IS_CSE_INDEX(curTree->gtCSEnum))
-                        {
-                            unsigned CSEnum = GET_CSE_INDEX(curTree->gtCSEnum);
-                            BitVecOps::AddElemD(cseLivenessTraits, block->bbCseGen, getCSEAvailBit(CSEnum));
-                            BitVecOps::AddElemD(cseLivenessTraits, block->bbCseOut, getCSEAvailBit(CSEnum));
-                        }
-
-                        curTree = curTree->gtNext;
-                    } while (curTree != nullptr);
-
-                    curStmt = curStmt->GetNextStmt();
-                    if (curStmt == nullptr)
-                        break;
-
-                    curTree = curStmt->GetTreeList();
-                }
-            }
-        }
+        optValnumCSE_SetUpAsync2ByrefKills();
     }
 
     for (BasicBlock* const block : Blocks())
@@ -1181,6 +1087,112 @@ void Compiler::optValnumCSE_InitDataFlow()
 #endif // DEBUG
 }
 
+//---------------------------------------------------------------------------
+// optValnumCSE_SetUpAsync2ByrefKills:
+//   Compute kills because of async2 calls requiring byrefs not to be live
+//   across them.
+//
+void Compiler::optValnumCSE_SetUpAsync2ByrefKills()
+{
+    bool anyAsyncKills = false;
+    cseAsyncKillsMask  = BitVecOps::MakeFull(cseLivenessTraits);
+    for (unsigned inx = 1; inx <= optCSECandidateCount; inx++)
+    {
+        CSEdsc* dsc = optCSEtab[inx - 1];
+        assert(dsc->csdIndex == inx);
+        bool isByRef = false;
+        if (dsc->csdTree->TypeIs(TYP_BYREF))
+        {
+            isByRef = true;
+        }
+        else if (dsc->csdTree->TypeIs(TYP_STRUCT))
+        {
+            ClassLayout* layout = dsc->csdTree->GetLayout(this);
+            isByRef             = layout->HasGCByRef();
+        }
+
+        if (isByRef)
+        {
+            // We generate a bit pattern like: 1111111100111100 where there
+            // are 0s only for the byref CSEs.
+            BitVecOps::RemoveElemD(cseLivenessTraits, cseAsyncKillsMask, getCSEAvailBit(inx));
+            BitVecOps::RemoveElemD(cseLivenessTraits, cseAsyncKillsMask, getCSEAvailCrossCallBit(inx));
+            anyAsyncKills = true;
+        }
+    }
+
+    if (!anyAsyncKills)
+    {
+        return;
+    }
+
+    for (BasicBlock* block : Blocks())
+    {
+        Statement* asyncCallStmt = nullptr;
+        GenTree*   asyncCall     = nullptr;
+        // Find last async call in block
+        Statement* stmt = block->lastStmt();
+        if (stmt == nullptr)
+        {
+            continue;
+        }
+
+        while (asyncCall == nullptr)
+        {
+            if ((stmt->GetRootNode()->gtFlags & GTF_CALL) != 0)
+            {
+                for (GenTree* tree = stmt->GetRootNode(); tree != nullptr; tree = tree->gtPrev)
+                {
+                    if (tree->IsCall() && tree->AsCall()->IsAsync2())
+                    {
+                        asyncCallStmt = stmt;
+                        asyncCall     = tree;
+                        break;
+                    }
+                }
+            }
+
+            if (stmt == block->firstStmt())
+                break;
+
+            stmt = stmt->GetPrevStmt();
+        }
+
+        if (asyncCall == nullptr)
+        {
+            continue;
+        }
+
+        // This block has a suspension point. Make all BYREF CSEs unavailable.
+        BitVecOps::IntersectionD(cseLivenessTraits, block->bbCseGen, cseAsyncKillsMask);
+        BitVecOps::IntersectionD(cseLivenessTraits, block->bbCseOut, cseAsyncKillsMask);
+
+        // Now make all byref CSEs after the suspension point available.
+        Statement* curStmt = asyncCallStmt;
+        GenTree*   curTree = asyncCall;
+        while (true)
+        {
+            do
+            {
+                if (IS_CSE_INDEX(curTree->gtCSEnum))
+                {
+                    unsigned CSEnum = GET_CSE_INDEX(curTree->gtCSEnum);
+                    BitVecOps::AddElemD(cseLivenessTraits, block->bbCseGen, getCSEAvailBit(CSEnum));
+                    BitVecOps::AddElemD(cseLivenessTraits, block->bbCseOut, getCSEAvailBit(CSEnum));
+                }
+
+                curTree = curTree->gtNext;
+            } while (curTree != nullptr);
+
+            curStmt = curStmt->GetNextStmt();
+            if (curStmt == nullptr)
+                break;
+
+            curTree = curStmt->GetTreeList();
+        }
+    }
+}
+
 /*****************************************************************************
  *
  * CSE Dataflow, so that all helper methods for dataflow are in a single place

From d9487b2c073f42a3ee3b7a8a2aa1ef93a10a2fb5 Mon Sep 17 00:00:00 2001
From: Jakob Botsch Nielsen <jakob.botsch.nielsen@gmail.com>
Date: Tue, 1 Apr 2025 15:47:05 +0200
Subject: [PATCH 185/203] Rename async2 -> async

---
 src/coreclr/jit/async.cpp          | 203 ++++++++++++++---------------
 src/coreclr/jit/async.h            |   8 +-
 src/coreclr/jit/codegenarm.cpp     |   2 +-
 src/coreclr/jit/codegenarmarch.cpp |   2 +-
 src/coreclr/jit/codegencommon.cpp  |   6 +-
 src/coreclr/jit/codegenlinear.cpp  |   2 +-
 src/coreclr/jit/codegenxarch.cpp   |   6 +-
 src/coreclr/jit/compiler.cpp       |   6 +-
 src/coreclr/jit/compiler.h         |   7 +-
 src/coreclr/jit/compmemkind.h      |   2 +-
 src/coreclr/jit/compphases.h       |   2 +-
 src/coreclr/jit/fginline.cpp       |   2 +-
 src/coreclr/jit/flowgraph.cpp      |   2 +-
 src/coreclr/jit/gentree.cpp        |  10 +-
 src/coreclr/jit/gentree.h          |   9 +-
 src/coreclr/jit/importer.cpp       |   6 +-
 src/coreclr/jit/importercalls.cpp  |  12 +-
 src/coreclr/jit/lclvars.cpp        |   4 +-
 src/coreclr/jit/lower.cpp          |   4 +-
 src/coreclr/jit/lsraarmarch.cpp    |   2 +-
 src/coreclr/jit/lsraxarch.cpp      |   2 +-
 src/coreclr/jit/morph.cpp          |   3 +-
 src/coreclr/jit/optcse.cpp         |  14 +-
 23 files changed, 159 insertions(+), 157 deletions(-)

diff --git a/src/coreclr/jit/async.cpp b/src/coreclr/jit/async.cpp
index 5361493e1ec909..fcbe6d4adf2403 100644
--- a/src/coreclr/jit/async.cpp
+++ b/src/coreclr/jit/async.cpp
@@ -222,29 +222,29 @@ void AsyncLiveness::GetLiveLocals(jitstd::vector<LiveLocalInfo>& liveLocals, uns
 }
 
 //------------------------------------------------------------------------
-// TransformAsync2: Run async2 transformation.
+// TransformAsync: Run async transformation.
 //
 // Returns:
 //   Suitable phase status.
 //
-PhaseStatus Compiler::TransformAsync2()
+PhaseStatus Compiler::TransformAsync()
 {
-    assert(compIsAsync2());
+    assert(compIsAsync());
 
-    Async2Transformation transformation(this);
+    AsyncTransformation transformation(this);
     return transformation.Run();
 }
 
 //------------------------------------------------------------------------
-// Async2Transformation::Run:
+// AsyncTransformation::Run:
 //   Run the transformation over all the IR.
 //
 // Returns:
 //   Suitable phase status.
 //
-PhaseStatus Async2Transformation::Run()
+PhaseStatus AsyncTransformation::Run()
 {
-    ArrayStack<BasicBlock*> worklist(m_comp->getAllocator(CMK_Async2));
+    ArrayStack<BasicBlock*> worklist(m_comp->getAllocator(CMK_Async));
 
     // First find all basic blocks with awaits in them. We'll have to track
     // liveness in these basic blocks, so it does not help to record the calls
@@ -253,7 +253,7 @@ PhaseStatus Async2Transformation::Run()
     {
         for (GenTree* tree : LIR::AsRange(block))
         {
-            if (tree->IsCall() && tree->AsCall()->IsAsync2() && !tree->AsCall()->IsTailCall())
+            if (tree->IsCall() && tree->AsCall()->IsAsync() && !tree->AsCall()->IsTailCall())
             {
                 JITDUMP(FMT_BB " contains await(s)\n", block->bbNum);
                 worklist.Push(block);
@@ -317,7 +317,7 @@ PhaseStatus Async2Transformation::Run()
     // Now walk the IR for all the blocks that contain async2 calls. Keep track
     // of liveness and outstanding LIR edges as we go; the LIR edges that cross
     // async2 calls are additional live variables that must be spilled.
-    jitstd::vector<GenTree*> defs(m_comp->getAllocator(CMK_Async2));
+    jitstd::vector<GenTree*> defs(m_comp->getAllocator(CMK_Async));
 
     for (int i = 0; i < worklist.Height(); i++)
     {
@@ -354,7 +354,7 @@ PhaseStatus Async2Transformation::Run()
                 // Update liveness to reflect state after this node.
                 liveness.Update(tree);
 
-                if (tree->IsCall() && tree->AsCall()->IsAsync2() && !tree->AsCall()->IsTailCall())
+                if (tree->IsCall() && tree->AsCall()->IsAsync() && !tree->AsCall()->IsTailCall())
                 {
                     // Transform call; continue with the remainder block
                     Transform(block, tree->AsCall(), defs, liveness, &block);
@@ -383,7 +383,7 @@ PhaseStatus Async2Transformation::Run()
 }
 
 //------------------------------------------------------------------------
-// Async2Transformation::Transform:
+// AsyncTransformation::Transform:
 //   Transform a single async2 call in the specified block.
 //
 // Parameters:
@@ -393,7 +393,7 @@ PhaseStatus Async2Transformation::Run()
 //   life      - Liveness information about live locals
 //   remainder - [out] Remainder block after the transformation
 //
-void Async2Transformation::Transform(
+void AsyncTransformation::Transform(
     BasicBlock* block, GenTreeCall* call, jitstd::vector<GenTree*>& defs, AsyncLiveness& life, BasicBlock** remainder)
 {
 #ifdef DEBUG
@@ -438,7 +438,7 @@ void Async2Transformation::Transform(
 }
 
 //------------------------------------------------------------------------
-// Async2Transformation::CreateLiveSetForSuspension:
+// AsyncTransformation::CreateLiveSetForSuspension:
 //   Create the set of live state to be captured for suspension, for the
 //   specified call.
 //
@@ -449,11 +449,11 @@ void Async2Transformation::Transform(
 //   life         - Liveness information about live locals
 //   liveLocals   - Information about each live local.
 //
-void Async2Transformation::CreateLiveSetForSuspension(BasicBlock*                     block,
-                                                      GenTreeCall*                    call,
-                                                      const jitstd::vector<GenTree*>& defs,
-                                                      AsyncLiveness&                  life,
-                                                      jitstd::vector<LiveLocalInfo>&  liveLocals)
+void AsyncTransformation::CreateLiveSetForSuspension(BasicBlock*                     block,
+                                                     GenTreeCall*                    call,
+                                                     const jitstd::vector<GenTree*>& defs,
+                                                     AsyncLiveness&                  life,
+                                                     jitstd::vector<LiveLocalInfo>&  liveLocals)
 {
     unsigned fullyDefinedRetBufLcl = BAD_VAR_NUM;
     CallArg* retbufArg             = call->gtArgs.GetRetBufferArg();
@@ -499,7 +499,7 @@ void Async2Transformation::CreateLiveSetForSuspension(BasicBlock*
 }
 
 //------------------------------------------------------------------------
-// Async2Transformation::LiftLIREdges:
+// AsyncTransformation::LiftLIREdges:
 //   Create locals capturing outstanding LIR edges and add information
 //   indicating that these locals are live.
 //
@@ -508,9 +508,9 @@ void Async2Transformation::CreateLiveSetForSuspension(BasicBlock*
 //   defs       - Current outstanding LIR edges
 //   liveLocals - [out] Vector to add new live local information into
 //
-void Async2Transformation::LiftLIREdges(BasicBlock*                     block,
-                                        const jitstd::vector<GenTree*>& defs,
-                                        jitstd::vector<LiveLocalInfo>&  liveLocals)
+void AsyncTransformation::LiftLIREdges(BasicBlock*                     block,
+                                       const jitstd::vector<GenTree*>& defs,
+                                       jitstd::vector<LiveLocalInfo>&  liveLocals)
 {
     if (defs.size() <= 0)
     {
@@ -546,7 +546,7 @@ void Async2Transformation::LiftLIREdges(BasicBlock*                     block,
 }
 
 //------------------------------------------------------------------------
-// Async2Transformation::LayOutContinuation:
+// AsyncTransformation::LayOutContinuation:
 //   Create the layout of the GC pointer and data arrays in the continuation
 //   object.
 //
@@ -559,9 +559,9 @@ void Async2Transformation::LiftLIREdges(BasicBlock*                     block,
 // Returns:
 //   Layout information.
 //
-ContinuationLayout Async2Transformation::LayOutContinuation(BasicBlock*                    block,
-                                                            GenTreeCall*                   call,
-                                                            jitstd::vector<LiveLocalInfo>& liveLocals)
+ContinuationLayout AsyncTransformation::LayOutContinuation(BasicBlock*                    block,
+                                                           GenTreeCall*                   call,
+                                                           jitstd::vector<LiveLocalInfo>& liveLocals)
 {
     ContinuationLayout layout(liveLocals);
 
@@ -713,7 +713,7 @@ ContinuationLayout Async2Transformation::LayOutContinuation(BasicBlock*
 }
 
 //------------------------------------------------------------------------
-// Async2Transformation::CanonicalizeCallDefinition:
+// AsyncTransformation::CanonicalizeCallDefinition:
 //   Put the call definition in a canonical form. This ensures that either the
 //   value is defined by a LCL_ADDR retbuffer or by a
 //   STORE_LCL_VAR/STORE_LCL_FLD that follows the call node.
@@ -726,9 +726,9 @@ ContinuationLayout Async2Transformation::LayOutContinuation(BasicBlock*
 // Returns:
 //   Information about the definition after canonicalization.
 //
-CallDefinitionInfo Async2Transformation::CanonicalizeCallDefinition(BasicBlock*    block,
-                                                                    GenTreeCall*   call,
-                                                                    AsyncLiveness& life)
+CallDefinitionInfo AsyncTransformation::CanonicalizeCallDefinition(BasicBlock*    block,
+                                                                   GenTreeCall*   call,
+                                                                   AsyncLiveness& life)
 {
     CallDefinitionInfo callDefInfo;
 
@@ -777,7 +777,7 @@ CallDefinitionInfo Async2Transformation::CanonicalizeCallDefinition(BasicBlock*
 }
 
 //------------------------------------------------------------------------
-// Async2Transformation::CreateSuspension:
+// AsyncTransformation::CreateSuspension:
 //   Create the basic block that when branched to suspends execution after the
 //   specified async2 call.
 //
@@ -790,10 +790,10 @@ CallDefinitionInfo Async2Transformation::CanonicalizeCallDefinition(BasicBlock*
 // Returns:
 //   The new basic block that was created.
 //
-BasicBlock* Async2Transformation::CreateSuspension(BasicBlock*               block,
-                                                   unsigned                  stateNum,
-                                                   AsyncLiveness&            life,
-                                                   const ContinuationLayout& layout)
+BasicBlock* AsyncTransformation::CreateSuspension(BasicBlock*               block,
+                                                  unsigned                  stateNum,
+                                                  AsyncLiveness&            life,
+                                                  const ContinuationLayout& layout)
 {
     if (m_lastSuspensionBB == nullptr)
     {
@@ -877,7 +877,7 @@ BasicBlock* Async2Transformation::CreateSuspension(BasicBlock*               blo
 }
 
 //------------------------------------------------------------------------
-// Async2Transformation::CreateAllocContinuationCall:
+// AsyncTransformation::CreateAllocContinuationCall:
 //   Create a call to the JIT helper that allocates a continuation.
 //
 // Parameters:
@@ -889,10 +889,10 @@ BasicBlock* Async2Transformation::CreateSuspension(BasicBlock*               blo
 // Returns:
 //   IR node representing the allocation.
 //
-GenTreeCall* Async2Transformation::CreateAllocContinuationCall(AsyncLiveness& life,
-                                                               GenTree*       prevContinuation,
-                                                               unsigned       gcRefsCount,
-                                                               unsigned       dataSize)
+GenTreeCall* AsyncTransformation::CreateAllocContinuationCall(AsyncLiveness& life,
+                                                              GenTree*       prevContinuation,
+                                                              unsigned       gcRefsCount,
+                                                              unsigned       dataSize)
 {
     GenTree* gcRefsCountNode = m_comp->gtNewIconNode((ssize_t)gcRefsCount, TYP_I_IMPL);
     GenTree* dataSizeNode    = m_comp->gtNewIconNode((ssize_t)dataSize, TYP_I_IMPL);
@@ -932,15 +932,15 @@ GenTreeCall* Async2Transformation::CreateAllocContinuationCall(AsyncLiveness& li
 }
 
 //------------------------------------------------------------------------
-// Async2Transformation::FillInGCPointersOnSuspension:
+// AsyncTransformation::FillInGCPointersOnSuspension:
 //   Create IR that fills the GC pointers of the continuation object.
 //
 // Parameters:
 //   liveLocals - Information about each live local.
 //   suspendBB  - Basic block to add IR to.
 //
-void Async2Transformation::FillInGCPointersOnSuspension(const jitstd::vector<LiveLocalInfo>& liveLocals,
-                                                        BasicBlock*                          suspendBB)
+void AsyncTransformation::FillInGCPointersOnSuspension(const jitstd::vector<LiveLocalInfo>& liveLocals,
+                                                       BasicBlock*                          suspendBB)
 {
     unsigned objectArrLclNum = GetGCDataArrayVar();
 
@@ -1026,15 +1026,14 @@ void Async2Transformation::FillInGCPointersOnSuspension(const jitstd::vector<Liv
 }
 
 //------------------------------------------------------------------------
-// Async2Transformation::FillInDataOnSuspension:
+// AsyncTransformation::FillInDataOnSuspension:
 //   Create IR that fills the data array of the continuation object.
 //
 // Parameters:
 //   liveLocals - Information about each live local.
 //   suspendBB  - Basic block to add IR to.
 //
-void Async2Transformation::FillInDataOnSuspension(const jitstd::vector<LiveLocalInfo>& liveLocals,
-                                                  BasicBlock*                          suspendBB)
+void AsyncTransformation::FillInDataOnSuspension(const jitstd::vector<LiveLocalInfo>& liveLocals, BasicBlock* suspendBB)
 {
     unsigned byteArrLclNum = GetDataArrayVar();
 
@@ -1102,7 +1101,7 @@ void Async2Transformation::FillInDataOnSuspension(const jitstd::vector<LiveLocal
 }
 
 //------------------------------------------------------------------------
-// Async2Transformation::CreateCheckAndSuspendAfterCall:
+// AsyncTransformation::CreateCheckAndSuspendAfterCall:
 //   Split the block containing the specified async2 call, and create the IR
 //   that checks whether suspension should be done after an async call.
 //
@@ -1113,11 +1112,11 @@ void Async2Transformation::FillInDataOnSuspension(const jitstd::vector<LiveLocal
 //   suspendBB   - Basic block to add IR to
 //   remainder   - [out] The remainder block containing the IR that was after the async2 call.
 //
-void Async2Transformation::CreateCheckAndSuspendAfterCall(BasicBlock*               block,
-                                                          const CallDefinitionInfo& callDefInfo,
-                                                          AsyncLiveness&            life,
-                                                          BasicBlock*               suspendBB,
-                                                          BasicBlock**              remainder)
+void AsyncTransformation::CreateCheckAndSuspendAfterCall(BasicBlock*               block,
+                                                         const CallDefinitionInfo& callDefInfo,
+                                                         AsyncLiveness&            life,
+                                                         BasicBlock*               suspendBB,
+                                                         BasicBlock**              remainder)
 {
     GenTree* continuationArg = new (m_comp, GT_ASYNC_CONTINUATION) GenTree(GT_ASYNC_CONTINUATION, TYP_REF);
     continuationArg->SetHasOrderingSideEffect();
@@ -1142,7 +1141,7 @@ void Async2Transformation::CreateCheckAndSuspendAfterCall(BasicBlock*
 }
 
 //------------------------------------------------------------------------
-// Async2Transformation::CreateResumption:
+// AsyncTransformation::CreateResumption:
 //   Create the basic block that when branched to resumes execution on entry to
 //   the function.
 //
@@ -1157,12 +1156,12 @@ void Async2Transformation::CreateCheckAndSuspendAfterCall(BasicBlock*
 // Returns:
 //   The new basic block that was created.
 //
-BasicBlock* Async2Transformation::CreateResumption(BasicBlock*               block,
-                                                   BasicBlock*               remainder,
-                                                   GenTreeCall*              call,
-                                                   const CallDefinitionInfo& callDefInfo,
-                                                   unsigned                  stateNum,
-                                                   const ContinuationLayout& layout)
+BasicBlock* AsyncTransformation::CreateResumption(BasicBlock*               block,
+                                                  BasicBlock*               remainder,
+                                                  GenTreeCall*              call,
+                                                  const CallDefinitionInfo& callDefInfo,
+                                                  unsigned                  stateNum,
+                                                  const ContinuationLayout& layout)
 {
     if (m_lastResumptionBB == nullptr)
     {
@@ -1231,7 +1230,7 @@ BasicBlock* Async2Transformation::CreateResumption(BasicBlock*               blo
 }
 
 //------------------------------------------------------------------------
-// Async2Transformation::RestoreFromDataOnResumption:
+// AsyncTransformation::RestoreFromDataOnResumption:
 //   Create IR that restores locals from the data array of the continuation
 //   object.
 //
@@ -1240,9 +1239,9 @@ BasicBlock* Async2Transformation::CreateResumption(BasicBlock*               blo
 //   liveLocals          - Information about each live local.
 //   resumeBB            - Basic block to append IR to
 //
-void Async2Transformation::RestoreFromDataOnResumption(unsigned                             resumeByteArrLclNum,
-                                                       const jitstd::vector<LiveLocalInfo>& liveLocals,
-                                                       BasicBlock*                          resumeBB)
+void AsyncTransformation::RestoreFromDataOnResumption(unsigned                             resumeByteArrLclNum,
+                                                      const jitstd::vector<LiveLocalInfo>& liveLocals,
+                                                      BasicBlock*                          resumeBB)
 {
     // Copy data
     for (const LiveLocalInfo& inf : liveLocals)
@@ -1288,7 +1287,7 @@ void Async2Transformation::RestoreFromDataOnResumption(unsigned
 }
 
 //------------------------------------------------------------------------
-// Async2Transformation::RestoreFromGCPointersOnResumption:
+// AsyncTransformation::RestoreFromGCPointersOnResumption:
 //   Create IR that restores locals from the GC pointers array of the
 //   continuation object.
 //
@@ -1297,9 +1296,9 @@ void Async2Transformation::RestoreFromDataOnResumption(unsigned
 //   liveLocals            - Information about each live local.
 //   resumeBB              - Basic block to append IR to
 //
-void Async2Transformation::RestoreFromGCPointersOnResumption(unsigned                             resumeObjectArrLclNum,
-                                                             const jitstd::vector<LiveLocalInfo>& liveLocals,
-                                                             BasicBlock*                          resumeBB)
+void AsyncTransformation::RestoreFromGCPointersOnResumption(unsigned                             resumeObjectArrLclNum,
+                                                            const jitstd::vector<LiveLocalInfo>& liveLocals,
+                                                            BasicBlock*                          resumeBB)
 {
     for (const LiveLocalInfo& inf : liveLocals)
     {
@@ -1359,7 +1358,7 @@ void Async2Transformation::RestoreFromGCPointersOnResumption(unsigned
 }
 
 //------------------------------------------------------------------------
-// Async2Transformation::RethrowExceptionOnResumption:
+// AsyncTransformation::RethrowExceptionOnResumption:
 //   Create IR that checks for an exception and rethrows it at the original
 //   suspension point if necessary.
 //
@@ -1375,11 +1374,11 @@ void Async2Transformation::RestoreFromGCPointersOnResumption(unsigned
 //   basic block where execution will continue if there was no exception to
 //   rethrow.
 //
-BasicBlock* Async2Transformation::RethrowExceptionOnResumption(BasicBlock*               block,
-                                                               BasicBlock*               remainder,
-                                                               unsigned                  resumeObjectArrLclNum,
-                                                               const ContinuationLayout& layout,
-                                                               BasicBlock*               resumeBB)
+BasicBlock* AsyncTransformation::RethrowExceptionOnResumption(BasicBlock*               block,
+                                                              BasicBlock*               remainder,
+                                                              unsigned                  resumeObjectArrLclNum,
+                                                              const ContinuationLayout& layout,
+                                                              BasicBlock*               resumeBB)
 {
     JITDUMP("  We need to rethrow an exception\n");
 
@@ -1437,7 +1436,7 @@ BasicBlock* Async2Transformation::RethrowExceptionOnResumption(BasicBlock*
 }
 
 //------------------------------------------------------------------------
-// Async2Transformation::CopyReturnValueOnResumption:
+// AsyncTransformation::CopyReturnValueOnResumption:
 //   Create IR that copies the return value from the continuation object to the
 //   right local.
 //
@@ -1450,12 +1449,12 @@ BasicBlock* Async2Transformation::RethrowExceptionOnResumption(BasicBlock*
 //   layout                - Layout information for the continuation object
 //   storeResultBB         - Basic block to append IR to
 //
-void Async2Transformation::CopyReturnValueOnResumption(GenTreeCall*              call,
-                                                       const CallDefinitionInfo& callDefInfo,
-                                                       unsigned                  resumeByteArrLclNum,
-                                                       unsigned                  resumeObjectArrLclNum,
-                                                       const ContinuationLayout& layout,
-                                                       BasicBlock*               storeResultBB)
+void AsyncTransformation::CopyReturnValueOnResumption(GenTreeCall*              call,
+                                                      const CallDefinitionInfo& callDefInfo,
+                                                      unsigned                  resumeByteArrLclNum,
+                                                      unsigned                  resumeObjectArrLclNum,
+                                                      const ContinuationLayout& layout,
+                                                      BasicBlock*               storeResultBB)
 {
     GenTree*     resultBase;
     unsigned     resultOffset;
@@ -1565,7 +1564,7 @@ void Async2Transformation::CopyReturnValueOnResumption(GenTreeCall*
 }
 
 //------------------------------------------------------------------------
-// Async2Transformation::LoadFromOffset:
+// AsyncTransformation::LoadFromOffset:
 //   Create a load.
 //
 // Parameters:
@@ -1577,10 +1576,10 @@ void Async2Transformation::CopyReturnValueOnResumption(GenTreeCall*
 // Returns:
 //   IR node of the load.
 //
-GenTreeIndir* Async2Transformation::LoadFromOffset(GenTree*     base,
-                                                   unsigned     offset,
-                                                   var_types    type,
-                                                   GenTreeFlags indirFlags)
+GenTreeIndir* AsyncTransformation::LoadFromOffset(GenTree*     base,
+                                                  unsigned     offset,
+                                                  var_types    type,
+                                                  GenTreeFlags indirFlags)
 {
     assert(base->TypeIs(TYP_REF, TYP_BYREF, TYP_I_IMPL));
     GenTree*      cns      = m_comp->gtNewIconNode((ssize_t)offset, TYP_I_IMPL);
@@ -1591,7 +1590,7 @@ GenTreeIndir* Async2Transformation::LoadFromOffset(GenTree*     base,
 }
 
 //------------------------------------------------------------------------
-// Async2Transformation::LoadFromOffset:
+// AsyncTransformation::LoadFromOffset:
 //   Create a store.
 //
 // Parameters:
@@ -1602,7 +1601,7 @@ GenTreeIndir* Async2Transformation::LoadFromOffset(GenTree*     base,
 // Returns:
 //   IR node of the store.
 //
-GenTreeStoreInd* Async2Transformation::StoreAtOffset(GenTree* base, unsigned offset, GenTree* value)
+GenTreeStoreInd* AsyncTransformation::StoreAtOffset(GenTree* base, unsigned offset, GenTree* value)
 {
     assert(base->TypeIs(TYP_REF, TYP_BYREF, TYP_I_IMPL));
     GenTree*         cns      = m_comp->gtNewIconNode((ssize_t)offset, TYP_I_IMPL);
@@ -1613,7 +1612,7 @@ GenTreeStoreInd* Async2Transformation::StoreAtOffset(GenTree* base, unsigned off
 }
 
 //------------------------------------------------------------------------
-// Async2Transformation::GetDataArrayVar:
+// AsyncTransformation::GetDataArrayVar:
 //   Create a new local to hold the data array of the continuation object. This
 //   local can be validly used for the entire suspension point; the returned
 //   local may be used by multiple suspension points.
@@ -1621,7 +1620,7 @@ GenTreeStoreInd* Async2Transformation::StoreAtOffset(GenTree* base, unsigned off
 // Returns:
 //   Local number.
 //
-unsigned Async2Transformation::GetDataArrayVar()
+unsigned AsyncTransformation::GetDataArrayVar()
 {
     // Create separate locals unless we have many locals in the method for live
     // range splitting purposes. This helps LSRA to avoid create additional
@@ -1636,7 +1635,7 @@ unsigned Async2Transformation::GetDataArrayVar()
 }
 
 //------------------------------------------------------------------------
-// Async2Transformation::GetGCDataArrayVar:
+// AsyncTransformation::GetGCDataArrayVar:
 //   Create a new local to hold the GC pointers array of the continuation
 //   object. This local can be validly used for the entire suspension point;
 //   the returned local may be used by multiple suspension points.
@@ -1644,7 +1643,7 @@ unsigned Async2Transformation::GetDataArrayVar()
 // Returns:
 //   Local number.
 //
-unsigned Async2Transformation::GetGCDataArrayVar()
+unsigned AsyncTransformation::GetGCDataArrayVar()
 {
     if ((m_gcDataArrayVar == BAD_VAR_NUM) || !m_comp->lvaHaveManyLocals())
     {
@@ -1656,7 +1655,7 @@ unsigned Async2Transformation::GetGCDataArrayVar()
 }
 
 //------------------------------------------------------------------------
-// Async2Transformation::GetResultBaseVar:
+// AsyncTransformation::GetResultBaseVar:
 //   Create a new local to hold the base address of the incoming result from
 //   the continuation. This local can be validly used for the entire suspension
 //   point; the returned local may be used by multiple suspension points.
@@ -1664,7 +1663,7 @@ unsigned Async2Transformation::GetGCDataArrayVar()
 // Returns:
 //   Local number.
 //
-unsigned Async2Transformation::GetResultBaseVar()
+unsigned AsyncTransformation::GetResultBaseVar()
 {
     if ((m_resultBaseVar == BAD_VAR_NUM) || !m_comp->lvaHaveManyLocals())
     {
@@ -1676,7 +1675,7 @@ unsigned Async2Transformation::GetResultBaseVar()
 }
 
 //------------------------------------------------------------------------
-// Async2Transformation::GetExceptionVar:
+// AsyncTransformation::GetExceptionVar:
 //   Create a new local to hold the exception in the continuation. This
 //   local can be validly used for the entire suspension point; the returned
 //   local may be used by multiple suspension points.
@@ -1684,7 +1683,7 @@ unsigned Async2Transformation::GetResultBaseVar()
 // Returns:
 //   Local number.
 //
-unsigned Async2Transformation::GetExceptionVar()
+unsigned AsyncTransformation::GetExceptionVar()
 {
     if ((m_exceptionVar == BAD_VAR_NUM) || !m_comp->lvaHaveManyLocals())
     {
@@ -1696,14 +1695,14 @@ unsigned Async2Transformation::GetExceptionVar()
 }
 
 //------------------------------------------------------------------------
-// Async2Transformation::CreateResumptionStubAddrTree:
+// AsyncTransformation::CreateResumptionStubAddrTree:
 //   Create a tree that represents the address of the resumption stub entry
 //   point.
 //
 // Returns:
 //   IR node.
 //
-GenTree* Async2Transformation::CreateResumptionStubAddrTree()
+GenTree* AsyncTransformation::CreateResumptionStubAddrTree()
 {
     switch (m_resumeStubLookup.accessType)
     {
@@ -1739,22 +1738,22 @@ GenTree* Async2Transformation::CreateResumptionStubAddrTree()
 }
 
 //------------------------------------------------------------------------
-// Async2Transformation::CreateFunctionTargetAddr:
+// AsyncTransformation::CreateFunctionTargetAddr:
 //   Create a tree that represents the address of the resumption stub entry
 //   point.
 //
 // Returns:
 //   IR node.
 //
-GenTree* Async2Transformation::CreateFunctionTargetAddr(CORINFO_METHOD_HANDLE       methHnd,
-                                                        const CORINFO_CONST_LOOKUP& lookup)
+GenTree* AsyncTransformation::CreateFunctionTargetAddr(CORINFO_METHOD_HANDLE       methHnd,
+                                                       const CORINFO_CONST_LOOKUP& lookup)
 {
     GenTree* con = m_comp->gtNewIconHandleNode((size_t)lookup.addr, GTF_ICON_FTN_ADDR);
     INDEBUG(con->AsIntCon()->gtTargetHandle = (size_t)methHnd);
     return con;
 }
 
-void Async2Transformation::CreateResumptionSwitch()
+void AsyncTransformation::CreateResumptionSwitch()
 {
     m_comp->fgCreateNewInitBB();
     BasicBlock* newEntryBB = m_comp->fgFirstBB;
@@ -1827,7 +1826,7 @@ void Async2Transformation::CreateResumptionSwitch()
         BBswtDesc* swtDesc     = new (m_comp, CMK_BasicBlock) BBswtDesc;
         swtDesc->bbsCount      = (unsigned)m_resumptionBBs.size();
         swtDesc->bbsHasDefault = true;
-        swtDesc->bbsDstTab     = new (m_comp, CMK_Async2) FlowEdge*[m_resumptionBBs.size()];
+        swtDesc->bbsDstTab     = new (m_comp, CMK_Async) FlowEdge*[m_resumptionBBs.size()];
 
         weight_t stateLikelihood = 1.0 / m_resumptionBBs.size();
         for (size_t i = 0; i < m_resumptionBBs.size(); i++)
diff --git a/src/coreclr/jit/async.h b/src/coreclr/jit/async.h
index a07283b2b5fbab..e7d3933be65a9d 100644
--- a/src/coreclr/jit/async.h
+++ b/src/coreclr/jit/async.h
@@ -41,7 +41,7 @@ struct CallDefinitionInfo
     GenTree* InsertAfter = nullptr;
 };
 
-class Async2Transformation
+class AsyncTransformation
 {
     friend class AsyncLiveness;
 
@@ -141,10 +141,10 @@ class Async2Transformation
     void CreateResumptionSwitch();
 
 public:
-    Async2Transformation(Compiler* comp)
+    AsyncTransformation(Compiler* comp)
         : m_comp(comp)
-        , m_liveLocalsScratch(comp->getAllocator(CMK_Async2))
-        , m_resumptionBBs(comp->getAllocator(CMK_Async2))
+        , m_liveLocalsScratch(comp->getAllocator(CMK_Async))
+        , m_resumptionBBs(comp->getAllocator(CMK_Async))
     {
     }
 
diff --git a/src/coreclr/jit/codegenarm.cpp b/src/coreclr/jit/codegenarm.cpp
index 2089a7f409977f..2eec1d69d04613 100644
--- a/src/coreclr/jit/codegenarm.cpp
+++ b/src/coreclr/jit/codegenarm.cpp
@@ -2092,7 +2092,7 @@ regMaskTP CodeGen::genStackAllocRegisterMask(unsigned frameSize, regMaskTP maskC
 
     // We similarly skip it for async2 due to the extra async continuation
     // return that may be overridden by the pop.
-    if (compiler->compIsAsync2())
+    if (compiler->compIsAsync())
     {
         return RBM_NONE;
     }
diff --git a/src/coreclr/jit/codegenarmarch.cpp b/src/coreclr/jit/codegenarmarch.cpp
index fa634af8880998..97b84c1bb9b7c2 100644
--- a/src/coreclr/jit/codegenarmarch.cpp
+++ b/src/coreclr/jit/codegenarmarch.cpp
@@ -3545,7 +3545,7 @@ void CodeGen::genCallInstruction(GenTreeCall* call)
     }
 #endif // DEBUG
 
-    bool                  hasAsyncRet = call->IsAsync2();
+    bool                  hasAsyncRet = call->IsAsync();
     CORINFO_METHOD_HANDLE methHnd;
     GenTree*              target = getCallTarget(call, &methHnd);
 
diff --git a/src/coreclr/jit/codegencommon.cpp b/src/coreclr/jit/codegencommon.cpp
index 79db11e1e102e4..164db290b34bb0 100644
--- a/src/coreclr/jit/codegencommon.cpp
+++ b/src/coreclr/jit/codegencommon.cpp
@@ -1938,7 +1938,7 @@ void CodeGen::genGenerateMachineCode()
             printf("; OSR variant for entry point 0x%x\n", compiler->info.compILEntry);
         }
 
-        if (compiler->compIsAsync2())
+        if (compiler->compIsAsync())
         {
             printf("; async2\n");
         }
@@ -7243,7 +7243,7 @@ void CodeGen::genReturn(GenTree* treeNode)
         }
     }
 
-    if (treeNode->OperIs(GT_RETURN) && compiler->compIsAsync2())
+    if (treeNode->OperIs(GT_RETURN) && compiler->compIsAsync())
     {
         instGen_Set_Reg_To_Zero(EA_PTRSIZE, REG_ASYNC_CONTINUATION_RET);
     }
@@ -7368,7 +7368,7 @@ void CodeGen::genMarkReturnGCInfo()
         }
     }
 
-    if (compiler->compIsAsync2())
+    if (compiler->compIsAsync())
     {
         gcInfo.gcMarkRegPtrVal(REG_ASYNC_CONTINUATION_RET, TYP_REF);
     }
diff --git a/src/coreclr/jit/codegenlinear.cpp b/src/coreclr/jit/codegenlinear.cpp
index 42c13a966f78e8..f5ae5a5e9ba593 100644
--- a/src/coreclr/jit/codegenlinear.cpp
+++ b/src/coreclr/jit/codegenlinear.cpp
@@ -503,7 +503,7 @@ void CodeGen::genCodeForBBlist()
             }
         }
 
-        if (compiler->compIsAsync2())
+        if (compiler->compIsAsync())
         {
             nonVarPtrRegs &= ~RBM_ASYNC_CONTINUATION_RET;
         }
diff --git a/src/coreclr/jit/codegenxarch.cpp b/src/coreclr/jit/codegenxarch.cpp
index e74bff6589fc74..a3644fbc57c736 100644
--- a/src/coreclr/jit/codegenxarch.cpp
+++ b/src/coreclr/jit/codegenxarch.cpp
@@ -6321,7 +6321,7 @@ void CodeGen::genCallInstruction(GenTreeCall* call X86_ARG(target_ssize_t stackA
     }
 #endif // DEBUG
 
-    bool                  hasAsyncRet = call->IsAsync2();
+    bool                  hasAsyncRet = call->IsAsync();
     CORINFO_METHOD_HANDLE methHnd;
     GenTree*              target = getCallTarget(call, &methHnd);
     if (target != nullptr)
@@ -10540,7 +10540,7 @@ void CodeGen::genFnEpilog(BasicBlock* block)
             // Add 'compiler->compLclFrameSize' to ESP. Use "pop ECX" for that, except in cases
             // where ECX may contain some state.
 
-            if ((frameSize == TARGET_POINTER_SIZE) && !compiler->compJmpOpUsed && !compiler->compIsAsync2())
+            if ((frameSize == TARGET_POINTER_SIZE) && !compiler->compJmpOpUsed && !compiler->compIsAsync())
             {
                 inst_RV(INS_pop, REG_ECX, TYP_I_IMPL);
                 regSet.verifyRegUsed(REG_ECX);
@@ -10628,7 +10628,7 @@ void CodeGen::genFnEpilog(BasicBlock* block)
             }
 #ifdef TARGET_X86
             else if ((compiler->compLclFrameSize == REGSIZE_BYTES) && !compiler->compJmpOpUsed &&
-                     !compiler->compIsAsync2())
+                     !compiler->compIsAsync())
             {
                 // "pop ecx" will make ESP point to the callee-saved registers
                 inst_RV(INS_pop, REG_ECX, TYP_I_IMPL);
diff --git a/src/coreclr/jit/compiler.cpp b/src/coreclr/jit/compiler.cpp
index 5fe5ada65b8bd5..bcbd88cb996df8 100644
--- a/src/coreclr/jit/compiler.cpp
+++ b/src/coreclr/jit/compiler.cpp
@@ -3161,7 +3161,7 @@ void Compiler::compInitOptions(JitFlags* jitFlags)
             printf("OPTIONS: Jit invoked for AOT\n");
         }
 
-        if (compIsAsync2())
+        if (compIsAsync())
         {
             printf("OPTIONS: compilation is an async2 state machine\n");
         }
@@ -5008,9 +5008,9 @@ void Compiler::compCompile(void** methodCodePtr, uint32_t* methodCodeSize, JitFl
     }
 #endif // TARGET_ARM
 
-    if (compIsAsync2())
+    if (compIsAsync())
     {
-        DoPhase(this, PHASE_ASYNC2, &Compiler::TransformAsync2);
+        DoPhase(this, PHASE_ASYNC, &Compiler::TransformAsync);
     }
 
     // Assign registers to variables, etc.
diff --git a/src/coreclr/jit/compiler.h b/src/coreclr/jit/compiler.h
index 9ecc0eb7d10551..37c601dc160d35 100644
--- a/src/coreclr/jit/compiler.h
+++ b/src/coreclr/jit/compiler.h
@@ -5533,7 +5533,7 @@ class Compiler
     PhaseStatus placeLoopAlignInstructions();
 #endif
 
-    PhaseStatus TransformAsync2();
+    PhaseStatus TransformAsync();
 
     // This field keep the R2R helper call that would be inserted to trigger the constructor
     // of the static class. It is set as nongc or gc static base if they are imported, so
@@ -7285,6 +7285,7 @@ class Compiler
     unsigned optValnumCSE_Index(GenTree* tree, Statement* stmt);
     bool optValnumCSE_Locate(CSE_HeuristicCommon* heuristic);
     void optValnumCSE_InitDataFlow();
+    void optValnumCSE_SetUpAsyncByrefKills();
     void optValnumCSE_DataFlow();
     void optValnumCSE_Availability();
     void optValnumCSE_Heuristic(CSE_HeuristicCommon* heuristic);
@@ -10881,7 +10882,7 @@ class Compiler
 #endif // TARGET_AMD64
     }
 
-    bool compIsAsync2() const
+    bool compIsAsync() const
     {
         return opts.jitFlags->IsSet(JitFlags::JIT_FLAG_RUNTIMEASYNCFUNCTION);
     }
@@ -10915,7 +10916,7 @@ class Compiler
 
     bool compObjectStackAllocation()
     {
-        if (compIsAsync2())
+        if (compIsAsync())
         {
             // Object stack allocation takes the address of locals around
             // suspension points. Disable entirely for now.
diff --git a/src/coreclr/jit/compmemkind.h b/src/coreclr/jit/compmemkind.h
index 3aacd830407e44..eb2c0dffc0ee6e 100644
--- a/src/coreclr/jit/compmemkind.h
+++ b/src/coreclr/jit/compmemkind.h
@@ -66,7 +66,7 @@ CompMemKindMacro(ZeroInit)
 CompMemKindMacro(Pgo)
 CompMemKindMacro(MaskConversionOpt)
 CompMemKindMacro(TryRegionClone)
-CompMemKindMacro(Async2)
+CompMemKindMacro(Async)
 CompMemKindMacro(RangeCheckCloning)
 //clang-format on
 
diff --git a/src/coreclr/jit/compphases.h b/src/coreclr/jit/compphases.h
index 1a23c804c26c42..1d8c0b4e3c4a71 100644
--- a/src/coreclr/jit/compphases.h
+++ b/src/coreclr/jit/compphases.h
@@ -118,7 +118,7 @@ CompPhaseNameMacro(PHASE_DETERMINE_FIRST_COLD_BLOCK, "Determine first cold block
 CompPhaseNameMacro(PHASE_RATIONALIZE,                "Rationalize IR",                 false, -1, false)
 CompPhaseNameMacro(PHASE_REPAIR_PROFILE,             "Repair profile",                 false, -1, false)
 
-CompPhaseNameMacro(PHASE_ASYNC2,                     "Transform async2",               false, -1, true)
+CompPhaseNameMacro(PHASE_ASYNC,                      "Transform async",                false, -1, true)
 CompPhaseNameMacro(PHASE_LCLVARLIVENESS,             "Local var liveness",             true,  -1, false)
 CompPhaseNameMacro(PHASE_LCLVARLIVENESS_INIT,        "Local var liveness init",        false, PHASE_LCLVARLIVENESS, false)
 CompPhaseNameMacro(PHASE_LCLVARLIVENESS_PERBLOCK,    "Per block local var liveness",   false, PHASE_LCLVARLIVENESS, false)
diff --git a/src/coreclr/jit/fginline.cpp b/src/coreclr/jit/fginline.cpp
index 125f6f2ff293e4..de1ad80ba38c06 100644
--- a/src/coreclr/jit/fginline.cpp
+++ b/src/coreclr/jit/fginline.cpp
@@ -1072,7 +1072,7 @@ void Compiler::fgMorphCallInlineHelper(GenTreeCall* call, InlineResult* result,
         return;
     }
 
-    if (call->gtIsAsyncCall && info.compUsesAsyncContinuation)
+    if (call->IsAsync() && info.compUsesAsyncContinuation)
     {
         // Currently not supported. Could provide a nice perf benefit for
         // async1 -> async2 thunks if we supported it.
diff --git a/src/coreclr/jit/flowgraph.cpp b/src/coreclr/jit/flowgraph.cpp
index ab120d1cb3939f..a8ea2f5c471ab6 100644
--- a/src/coreclr/jit/flowgraph.cpp
+++ b/src/coreclr/jit/flowgraph.cpp
@@ -2468,7 +2468,7 @@ PhaseStatus Compiler::fgAddInternal()
             // async2 functions we will be introducing another return during
             // the async2 transformation, so make sure there's a free epilog
             // for it.
-            if (compIsAsync2())
+            if (compIsAsync())
             {
                 limit--;
             }
diff --git a/src/coreclr/jit/gentree.cpp b/src/coreclr/jit/gentree.cpp
index cd140ceb2d9d5a..5658494e9510f5 100644
--- a/src/coreclr/jit/gentree.cpp
+++ b/src/coreclr/jit/gentree.cpp
@@ -2281,9 +2281,9 @@ bool GenTreeCall::HasSideEffects(Compiler* compiler, bool ignoreExceptions, bool
 //   behavior and GC reporting around the returned async continuation. This is
 //   currently done in lowering; see LowerAsyncContinuation().
 //
-bool GenTreeCall::IsAsync2() const
+bool GenTreeCall::IsAsync() const
 {
-    return gtIsAsyncCall;
+    return (gtCallMoreFlags & GTF_CALL_M_ASYNC) != 0;
 }
 
 //-------------------------------------------------------------------------
@@ -8324,7 +8324,6 @@ GenTreeCall* Compiler::gtNewCallNode(gtCallTypes           callType,
     node->gtRetClsHnd     = nullptr;
     node->gtControlExpr   = nullptr;
     node->gtCallMoreFlags = GTF_CALL_M_EMPTY;
-    node->gtIsAsyncCall   = false;
     INDEBUG(node->gtCallDebugFlags = GTF_CALL_MD_EMPTY);
     node->gtInlineInfoCount = 0;
 
@@ -9938,9 +9937,8 @@ GenTreeCall* Compiler::gtCloneExprCallHelper(GenTreeCall* tree)
 
     copy->gtLateDevirtualizationInfo = tree->gtLateDevirtualizationInfo;
 
-    copy->gtIsAsyncCall = tree->gtIsAsyncCall;
-    copy->gtCallType    = tree->gtCallType;
-    copy->gtReturnType  = tree->gtReturnType;
+    copy->gtCallType   = tree->gtCallType;
+    copy->gtReturnType = tree->gtReturnType;
 
 #if FEATURE_MULTIREG_RET
     copy->gtReturnTypeDesc = tree->gtReturnTypeDesc;
diff --git a/src/coreclr/jit/gentree.h b/src/coreclr/jit/gentree.h
index 567a9ab315c431..8f8a1675c11abd 100644
--- a/src/coreclr/jit/gentree.h
+++ b/src/coreclr/jit/gentree.h
@@ -4238,6 +4238,7 @@ enum GenTreeCallFlags : unsigned int
     GTF_CALL_M_GUARDED_DEVIRT_CHAIN    = 0x00080000, // this call is a candidate for chained guarded devirtualization
     GTF_CALL_M_ALLOC_SIDE_EFFECTS      = 0x00100000, // this is a call to an allocator with side effects
     GTF_CALL_M_SUPPRESS_GC_TRANSITION  = 0x00200000, // suppress the GC transition (i.e. during a pinvoke) but a separate GC safe point is required.
+    GTF_CALL_M_ASYNC                   = 0x00400000, // this call is a runtime async method call
     GTF_CALL_M_EXPANDED_EARLY          = 0x00800000, // the Virtual Call target address is expanded and placed in gtControlExpr in Morph rather than in Lower
     GTF_CALL_M_LDVIRTFTN_INTERFACE     = 0x01000000, // ldvirtftn on an interface type
     GTF_CALL_M_CAST_CAN_BE_EXPANDED    = 0x02000000, // this cast (helper call) can be expanded if it's profitable. To be removed.
@@ -5023,7 +5024,12 @@ struct GenTreeCall final : public GenTree
 #endif
     }
 
-    bool IsAsync2() const;
+    void SetIsAsync()
+    {
+        gtCallMoreFlags |= GTF_CALL_M_ASYNC;
+    }
+
+    bool IsAsync() const;
 
     //---------------------------------------------------------------------------
     // GetRegNumByIdx: get i'th return register allocated to this call node.
@@ -5594,7 +5600,6 @@ struct GenTreeCall final : public GenTree
     var_types        gtReturnType : 5; // exact return type
 
     uint8_t gtInlineInfoCount; // number of inline candidates for the given call
-    bool    gtIsAsyncCall;
 
     CORINFO_CLASS_HANDLE gtRetClsHnd; // The return type handle of the call if it is a struct; always available
     union
diff --git a/src/coreclr/jit/importer.cpp b/src/coreclr/jit/importer.cpp
index 2f073ee7ba71e1..4623b61f85b6b7 100644
--- a/src/coreclr/jit/importer.cpp
+++ b/src/coreclr/jit/importer.cpp
@@ -847,7 +847,7 @@ GenTree* Compiler::impStoreStruct(GenTree*         store,
             // Make sure we don't pass something other than a local address to the return buffer arg.
             // It is allowed to pass current's method return buffer as it is a local too.
             if ((fgAddrCouldBeHeap(destAddr) && !eeIsByrefLike(srcCall->gtRetClsHnd)) ||
-                (compIsAsync2() && !destAddr->OperIs(GT_LCL_ADDR)))
+                (compIsAsync() && !destAddr->OperIs(GT_LCL_ADDR)))
             {
                 unsigned tmp = lvaGrabTemp(false DEBUGARG("stack copy for value returned via return buffer"));
                 lvaSetStruct(tmp, srcCall->gtRetClsHnd, false);
@@ -974,7 +974,7 @@ GenTree* Compiler::impStoreStruct(GenTree*         store,
             // Make sure we don't pass something other than a local address to the return buffer arg.
             // It is allowed to pass current's method return buffer as it is a local too.
             if ((fgAddrCouldBeHeap(destAddr) && !eeIsByrefLike(call->gtRetClsHnd)) ||
-                (compIsAsync2() && !destAddr->OperIs(GT_LCL_ADDR)))
+                (compIsAsync() && !destAddr->OperIs(GT_LCL_ADDR)))
             {
                 unsigned tmp = lvaGrabTemp(false DEBUGARG("stack copy for value returned via return buffer"));
                 lvaSetStruct(tmp, call->gtRetClsHnd, false);
@@ -9099,7 +9099,7 @@ void Compiler::impImportBlockCode(BasicBlock* block)
                     //       that control the flow of sync context.
                     //       We do not have that yet.
                     int configVal = -1; // -1 not congigured, 0/1 configured to false/true
-                    if (compIsAsync2() && JitConfig.JitOptimizeAwait())
+                    if (compIsAsync() && JitConfig.JitOptimizeAwait())
                     {
                         isAwait = impMatchAwaitPattern(codeAddr, codeEndp, &configVal);
                     }
diff --git a/src/coreclr/jit/importercalls.cpp b/src/coreclr/jit/importercalls.cpp
index 91ef811dfa2f15..e7e4b197f376af 100644
--- a/src/coreclr/jit/importercalls.cpp
+++ b/src/coreclr/jit/importercalls.cpp
@@ -524,13 +524,13 @@ var_types Compiler::impImportCall(OPCODE                  opcode,
 
         // Temporary hack since these functions have to be recognized as async2
         // calls in JIT generated state machines only.
-        if (compIsAsync2() &&
+        if (compIsAsync() &&
             ((ni == NI_System_Runtime_CompilerServices_RuntimeHelpers_AwaitAwaiterFromRuntimeAsync) ||
              (ni == NI_System_Runtime_CompilerServices_RuntimeHelpers_UnsafeAwaitAwaiterFromRuntimeAsync) ||
              (ni == NI_System_Runtime_CompilerServices_RuntimeHelpers_Await)))
         {
             assert((call != nullptr) && call->OperIs(GT_CALL));
-            call->AsCall()->gtIsAsyncCall = true;
+            call->AsCall()->SetIsAsync();
             JITDUMP("Marking [%06u] as a special-case async call\n", dspTreeID(call));
         }
     }
@@ -724,7 +724,7 @@ var_types Compiler::impImportCall(OPCODE                  opcode,
 
     if (sig->isAsyncCall())
     {
-        call->AsCall()->gtIsAsyncCall = true;
+        call->AsCall()->SetIsAsync();
     }
 
     // Now create the argument list.
@@ -900,7 +900,7 @@ var_types Compiler::impImportCall(OPCODE                  opcode,
     impPopCallArgs(sig, call->AsCall());
 
     // Extra args
-    if ((instParam != nullptr) || call->AsCall()->IsAsync2() || (varArgsCookie != nullptr))
+    if ((instParam != nullptr) || call->AsCall()->IsAsync() || (varArgsCookie != nullptr))
     {
         if (Target::g_tgtArgOrder == Target::ARG_ORDER_R2L)
         {
@@ -910,7 +910,7 @@ var_types Compiler::impImportCall(OPCODE                  opcode,
                                                            .WellKnown(WellKnownArg::VarArgsCookie));
             }
 
-            if (call->AsCall()->IsAsync2())
+            if (call->AsCall()->IsAsync())
             {
                 call->AsCall()->gtArgs.PushFront(this, NewCallArg::Primitive(gtNewNull(), TYP_REF)
                                                            .WellKnown(WellKnownArg::AsyncContinuation));
@@ -930,7 +930,7 @@ var_types Compiler::impImportCall(OPCODE                  opcode,
                                                 NewCallArg::Primitive(instParam).WellKnown(WellKnownArg::InstParam));
             }
 
-            if (call->AsCall()->IsAsync2())
+            if (call->AsCall()->IsAsync())
             {
                 call->AsCall()->gtArgs.PushBack(this, NewCallArg::Primitive(gtNewNull(), TYP_REF)
                                                           .WellKnown(WellKnownArg::AsyncContinuation));
diff --git a/src/coreclr/jit/lclvars.cpp b/src/coreclr/jit/lclvars.cpp
index 5213828696bf53..bbd3fce509f652 100644
--- a/src/coreclr/jit/lclvars.cpp
+++ b/src/coreclr/jit/lclvars.cpp
@@ -161,7 +161,7 @@ void Compiler::lvaInitTypeRef()
         info.compTypeCtxtArg = BAD_VAR_NUM;
     }
 
-    if (compIsAsync2())
+    if (compIsAsync())
     {
         info.compArgsCount++;
     }
@@ -694,7 +694,7 @@ void Compiler::lvaInitGenericsCtxt(unsigned* curVarNum)
 //
 void Compiler::lvaInitAsyncContinuation(unsigned* curVarNum)
 {
-    if (!compIsAsync2())
+    if (!compIsAsync())
     {
         return;
     }
diff --git a/src/coreclr/jit/lower.cpp b/src/coreclr/jit/lower.cpp
index 0d072a878adb74..d1db172cb5177d 100644
--- a/src/coreclr/jit/lower.cpp
+++ b/src/coreclr/jit/lower.cpp
@@ -5413,11 +5413,11 @@ void Lowering::LowerAsyncContinuation(GenTree* asyncCont)
 
         if (node->IsCall())
         {
-            if (!node->AsCall()->IsAsync2())
+            if (!node->AsCall()->IsAsync())
             {
                 JITDUMP("Marking the call [%06u] before async continuation [%06u] as an async call\n",
                         Compiler::dspTreeID(node), Compiler::dspTreeID(asyncCont));
-                node->AsCall()->gtIsAsyncCall = true;
+                node->AsCall()->SetIsAsync();
             }
 
             break;
diff --git a/src/coreclr/jit/lsraarmarch.cpp b/src/coreclr/jit/lsraarmarch.cpp
index 289d55f155902e..2610501b4b74d4 100644
--- a/src/coreclr/jit/lsraarmarch.cpp
+++ b/src/coreclr/jit/lsraarmarch.cpp
@@ -302,7 +302,7 @@ int LinearScan::BuildCall(GenTreeCall* call)
     }
 #endif // SWIFT_SUPPORT
 
-    if (call->IsAsync2() && compiler->compIsAsync2())
+    if (call->IsAsync() && compiler->compIsAsync())
     {
         MarkAsyncContinuationBusyForCall(call);
     }
diff --git a/src/coreclr/jit/lsraxarch.cpp b/src/coreclr/jit/lsraxarch.cpp
index 83a3fa2f160126..76160df2857a1b 100644
--- a/src/coreclr/jit/lsraxarch.cpp
+++ b/src/coreclr/jit/lsraxarch.cpp
@@ -1394,7 +1394,7 @@ int LinearScan::BuildCall(GenTreeCall* call)
     }
 #endif // SWIFT_SUPPORT
 
-    if (call->IsAsync2() && compiler->compIsAsync2())
+    if (call->IsAsync() && compiler->compIsAsync())
     {
         MarkAsyncContinuationBusyForCall(call);
     }
diff --git a/src/coreclr/jit/morph.cpp b/src/coreclr/jit/morph.cpp
index 2a8873e0d8ccde..f2eed98cdc65dd 100644
--- a/src/coreclr/jit/morph.cpp
+++ b/src/coreclr/jit/morph.cpp
@@ -189,7 +189,6 @@ GenTree* Compiler::fgMorphIntoHelperCall(GenTree* tree, int helper, bool morphAr
     call->gtCallMoreFlags = GTF_CALL_M_EMPTY;
     INDEBUG(call->gtCallDebugFlags = GTF_CALL_MD_EMPTY);
     call->gtControlExpr = nullptr;
-    call->gtIsAsyncCall = false;
     call->ClearInlineInfo();
 #ifdef UNIX_X86_ABI
     call->gtFlags |= GTF_CALL_POP_ARGS;
@@ -4493,7 +4492,7 @@ GenTree* Compiler::fgMorphPotentialTailCall(GenTreeCall* call)
     }
 #endif
 
-    if (compIsAsync2() != call->IsAsync2())
+    if (compIsAsync() != call->IsAsync())
     {
         failTailCall("Caller and callee do not agree on async2-ness");
         return nullptr;
diff --git a/src/coreclr/jit/optcse.cpp b/src/coreclr/jit/optcse.cpp
index d3df3d3bdec1e4..711e1d91e9a109 100644
--- a/src/coreclr/jit/optcse.cpp
+++ b/src/coreclr/jit/optcse.cpp
@@ -999,9 +999,9 @@ void Compiler::optValnumCSE_InitDataFlow()
         }
     }
 
-    if (compIsAsync2())
+    if (compIsAsync())
     {
-        optValnumCSE_SetUpAsync2ByrefKills();
+        optValnumCSE_SetUpAsyncByrefKills();
     }
 
     for (BasicBlock* const block : Blocks())
@@ -1088,11 +1088,11 @@ void Compiler::optValnumCSE_InitDataFlow()
 }
 
 //---------------------------------------------------------------------------
-// optValnumCSE_SetUpAsync2ByrefKills:
-//   Compute kills because of async2 calls requiring byrefs not to be live
+// optValnumCSE_SetUpAsyncByrefKills:
+//   Compute kills because of async calls requiring byrefs not to be live
 //   across them.
 //
-void Compiler::optValnumCSE_SetUpAsync2ByrefKills()
+void Compiler::optValnumCSE_SetUpAsyncByrefKills()
 {
     bool anyAsyncKills = false;
     cseAsyncKillsMask  = BitVecOps::MakeFull(cseLivenessTraits);
@@ -1143,7 +1143,7 @@ void Compiler::optValnumCSE_SetUpAsync2ByrefKills()
             {
                 for (GenTree* tree = stmt->GetRootNode(); tree != nullptr; tree = tree->gtPrev)
                 {
-                    if (tree->IsCall() && tree->AsCall()->IsAsync2())
+                    if (tree->IsCall() && tree->AsCall()->IsAsync())
                     {
                         asyncCallStmt = stmt;
                         asyncCall     = tree;
@@ -1707,7 +1707,7 @@ void Compiler::optValnumCSE_Availability()
                             BitVecOps::IntersectionD(cseLivenessTraits, available_cses, cseCallKillsMask);
 
                             // In async state machines, make all byref CSEs unavailable after suspension points.
-                            if (tree->AsCall()->IsAsync2() && compIsAsync2())
+                            if (tree->AsCall()->IsAsync() && compIsAsync())
                             {
                                 BitVecOps::IntersectionD(cseLivenessTraits, available_cses, cseAsyncKillsMask);
                             }

From 7364c0cb2a640a47a8d622d595f27a301904bbe5 Mon Sep 17 00:00:00 2001
From: Jakob Botsch Nielsen <jakob.botsch.nielsen@gmail.com>
Date: Tue, 1 Apr 2025 16:13:59 +0200
Subject: [PATCH 186/203] Rename in RISC-V and LA64 as well

---
 src/coreclr/jit/lsraloongarch64.cpp | 2 +-
 src/coreclr/jit/lsrariscv64.cpp     | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/coreclr/jit/lsraloongarch64.cpp b/src/coreclr/jit/lsraloongarch64.cpp
index 18d978e6db2ac7..3dd551bd2757bc 100644
--- a/src/coreclr/jit/lsraloongarch64.cpp
+++ b/src/coreclr/jit/lsraloongarch64.cpp
@@ -814,7 +814,7 @@ int LinearScan::BuildCall(GenTreeCall* call)
         BuildKills(call, killMask);
     }
 
-    if (call->IsAsync2() && compiler->compIsAsync2())
+    if (call->IsAsync() && compiler->compIsAsync())
     {
         MarkAsyncContinuationBusyForCall(call);
     }
diff --git a/src/coreclr/jit/lsrariscv64.cpp b/src/coreclr/jit/lsrariscv64.cpp
index 1621146ddb07a7..6fa1561e47696a 100644
--- a/src/coreclr/jit/lsrariscv64.cpp
+++ b/src/coreclr/jit/lsrariscv64.cpp
@@ -994,7 +994,7 @@ int LinearScan::BuildCall(GenTreeCall* call)
         BuildKills(call, killMask);
     }
 
-    if (call->IsAsync2() && compiler->compIsAsync2())
+    if (call->IsAsync() && compiler->compIsAsync())
     {
         MarkAsyncContinuationBusyForCall(call);
     }

From e6599877f139ab930fa88f6a8a61a5598e144535 Mon Sep 17 00:00:00 2001
From: vsadov <8218165+VSadov@users.noreply.github.com>
Date: Thu, 3 Apr 2025 15:08:13 -0700
Subject: [PATCH 187/203] remove code added to eetwain and gcinfodecoder

---
 src/coreclr/inc/eetwain.h        |  2 --
 src/coreclr/vm/eetwain.cpp       | 14 ++------------
 src/coreclr/vm/gcinfodecoder.cpp |  2 +-
 3 files changed, 3 insertions(+), 15 deletions(-)

diff --git a/src/coreclr/inc/eetwain.h b/src/coreclr/inc/eetwain.h
index 3d69f4180faf89..96860e694f3da3 100644
--- a/src/coreclr/inc/eetwain.h
+++ b/src/coreclr/inc/eetwain.h
@@ -106,8 +106,6 @@ enum ICodeManagerFlags
     ReportFPBasedSlotsOnly
                     =   0x0200, // EnumGCRefs/EnumerateLiveSlots should only include
                                 // slots that are based on the frame pointer
-    NoGcDecoderValidation
-                    =   0x0400, // Turn off GCDecoder validation
 };
 
 //*****************************************************************************
diff --git a/src/coreclr/vm/eetwain.cpp b/src/coreclr/vm/eetwain.cpp
index 0c6dc08f8b1c65..9377c42aeed003 100644
--- a/src/coreclr/vm/eetwain.cpp
+++ b/src/coreclr/vm/eetwain.cpp
@@ -1393,19 +1393,9 @@ bool EECodeManager::EnumGcRefs( PREGDISPLAY     pRD,
     reportScratchSlots = (flags & ActiveStackFrame) != 0;
 
 
-    GcInfoDecoderFlags decoderFlags;
-    if (flags & NoGcDecoderValidation)
-    {
-        decoderFlags = GcInfoDecoderFlags (DECODE_GC_LIFETIMES | DECODE_SECURITY_OBJECT | DECODE_VARARG | DECODE_NO_VALIDATION);
-    }
-    else
-    {
-        decoderFlags = GcInfoDecoderFlags (DECODE_GC_LIFETIMES | DECODE_SECURITY_OBJECT | DECODE_VARARG);
-    }
-
     GcInfoDecoder gcInfoDecoder(
                         gcInfoToken,
-                        decoderFlags,
+                        GcInfoDecoderFlags (DECODE_GC_LIFETIMES | DECODE_SECURITY_OBJECT | DECODE_VARARG),
                         curOffs
                         );
 
@@ -2341,4 +2331,4 @@ size_t InterpreterCodeManager::GetFunctionSize(GCInfoToken gcInfoToken)
     return 0;
 }
 
-#endif // FEATURE_INTERPRETER
\ No newline at end of file
+#endif // FEATURE_INTERPRETER
diff --git a/src/coreclr/vm/gcinfodecoder.cpp b/src/coreclr/vm/gcinfodecoder.cpp
index a94335bb991a9a..bccb5313b6a0d9 100644
--- a/src/coreclr/vm/gcinfodecoder.cpp
+++ b/src/coreclr/vm/gcinfodecoder.cpp
@@ -57,7 +57,7 @@
 
 #ifndef LOG_PIPTR
 #define LOG_PIPTR(pObjRef, gcFlags, hCallBack)                                                                                                  \
-    if (!(m_Flags & DECODE_NO_VALIDATION)) {                                                                                                                                           \
+    {                                                                                                                                           \
         GCCONTEXT* pGCCtx = (GCCONTEXT*)(hCallBack);                                                                                            \
         if (pGCCtx->sc->promotion)                                                                                                              \
         {                                                                                                                                       \

From 0aff212500f5ff65159290c63e8e8c77633b33c4 Mon Sep 17 00:00:00 2001
From: vsadov <8218165+VSadov@users.noreply.github.com>
Date: Thu, 3 Apr 2025 15:31:27 -0700
Subject: [PATCH 188/203] fix after merge

---
 src/coreclr/vm/jithelpers.cpp | 7 ++-----
 1 file changed, 2 insertions(+), 5 deletions(-)

diff --git a/src/coreclr/vm/jithelpers.cpp b/src/coreclr/vm/jithelpers.cpp
index 676f1070f09f53..b50bd8099b3504 100644
--- a/src/coreclr/vm/jithelpers.cpp
+++ b/src/coreclr/vm/jithelpers.cpp
@@ -1697,11 +1697,8 @@ HCIMPL1(void, IL_ThrowExact, Object* obj)
     GetThread()->GetExceptionState()->SetRaisingForeignException();
 
 #ifdef FEATURE_EH_FUNCLETS
-    if (g_isNewExceptionHandlingEnabled)
-    {
-        DispatchManagedException(oref);
-        UNREACHABLE();
-    }
+    DispatchManagedException(oref);
+    UNREACHABLE();
 #endif
 
     RaiseTheExceptionInternalOnly(oref, FALSE);

From 211a28141a7bd4389034ec7b9674e53ff9c5f067 Mon Sep 17 00:00:00 2001
From: vsadov <8218165+VSadov@users.noreply.github.com>
Date: Thu, 3 Apr 2025 17:14:50 -0700
Subject: [PATCH 189/203] undo unnecessary change in QCallHandlers.cs

---
 .../src/System/Runtime/CompilerServices/QCallHandles.cs         | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/QCallHandles.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/QCallHandles.cs
index 2e9517bdef790f..10b955478ecd39 100644
--- a/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/QCallHandles.cs
+++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/QCallHandles.cs
@@ -34,7 +34,7 @@ internal static ObjectHandleOnStack Create<T>(ref T o) where T : class?
         }
     }
 
-    internal unsafe ref struct ByteRef
+    internal ref struct ByteRef
     {
         private ref byte _ref;
 

From 6bb549287658ac5cf5cd372024f310b22776d954 Mon Sep 17 00:00:00 2001
From: vsadov <8218165+VSadov@users.noreply.github.com>
Date: Thu, 3 Apr 2025 18:18:42 -0700
Subject: [PATCH 190/203] reference continuation return from
 volatileCurrContextPointers on ARM[64]

---
 src/coreclr/vm/arm/stubs.cpp   | 3 +++
 src/coreclr/vm/arm64/stubs.cpp | 1 +
 2 files changed, 4 insertions(+)

diff --git a/src/coreclr/vm/arm/stubs.cpp b/src/coreclr/vm/arm/stubs.cpp
index cb665654d0cac4..56293fb430df5e 100644
--- a/src/coreclr/vm/arm/stubs.cpp
+++ b/src/coreclr/vm/arm/stubs.cpp
@@ -1595,7 +1595,10 @@ void HijackFrame::UpdateRegDisplay_Impl(const PREGDISPLAY pRD, bool updateFloats
      pRD->pCurrentContext->Sp = PTR_TO_TADDR(m_Args) + sizeof(struct HijackArgs);
 
      pRD->pCurrentContext->R0 = m_Args->R0;
+     pRD->pCurrentContext->R2 = m_Args->R2;
+
      pRD->volatileCurrContextPointers.R0 = &m_Args->R0;
+     pRD->volatileCurrContextPointers.R2 = &m_Args->R2;
 
      pRD->pCurrentContext->R4 = m_Args->R4;
      pRD->pCurrentContext->R5 = m_Args->R5;
diff --git a/src/coreclr/vm/arm64/stubs.cpp b/src/coreclr/vm/arm64/stubs.cpp
index 5588b65b3bc2d0..751595b9f528b3 100644
--- a/src/coreclr/vm/arm64/stubs.cpp
+++ b/src/coreclr/vm/arm64/stubs.cpp
@@ -772,6 +772,7 @@ void HijackFrame::UpdateRegDisplay_Impl(const PREGDISPLAY pRD, bool updateFloats
 
      pRD->volatileCurrContextPointers.X0 = &m_Args->X0;
      pRD->volatileCurrContextPointers.X1 = &m_Args->X1;
+     pRD->volatileCurrContextPointers.X1 = &m_Args->X2;
 
      pRD->pCurrentContext->X19 = m_Args->X19;
      pRD->pCurrentContext->X20 = m_Args->X20;

From df5b767871fadd92c36ed548110c65840bd36a5e Mon Sep 17 00:00:00 2001
From: Vladimir Sadov <vsadov@microsoft.com>
Date: Fri, 4 Apr 2025 16:52:43 -0700
Subject: [PATCH 191/203] PR feedback

---
 .../System.Private.CoreLib/src/System/RuntimeType.CoreCLR.cs | 5 ++---
 1 file changed, 2 insertions(+), 3 deletions(-)

diff --git a/src/coreclr/System.Private.CoreLib/src/System/RuntimeType.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/RuntimeType.CoreCLR.cs
index 5aad763334d045..066573811cd5eb 100644
--- a/src/coreclr/System.Private.CoreLib/src/System/RuntimeType.CoreCLR.cs
+++ b/src/coreclr/System.Private.CoreLib/src/System/RuntimeType.CoreCLR.cs
@@ -552,7 +552,7 @@ private void MergeWithGlobalList(T[] list)
                                     // Grow the list by exactly one element in this case to avoid null entries at the end.
                                     //
 
-                                    // TODO: runtime-async we need to rationalize how async2 thunks eork in reflection.
+                                    // TODO: runtime-async we need to rationalize how async2 thunks work in reflection.
                                     //       possibly they should not be exposed as they are runtime-provided implementation
                                     //       details, that in theory may change.
                                     //
@@ -771,8 +771,7 @@ private RuntimeConstructorInfo[] PopulateConstructors(Filter filter)
                         if ((methodAttributes & MethodAttributes.RTSpecialName) == 0)
                             continue;
 
-                        if (!RuntimeMethodHandle.GetName(methodHandle).Equals(".ctor") &&
-                            !RuntimeMethodHandle.GetName(methodHandle).Equals(".cctor"))
+                        if (RuntimeMethodHandle.GetName(methodHandle) is not ".ctor" and not ".cctor")
                             continue;
 
                         // Constructors should not be virtual or abstract

From af8488749fb8afc395a6bba3bbb7985a0b193a49 Mon Sep 17 00:00:00 2001
From: vsadov <8218165+VSadov@users.noreply.github.com>
Date: Fri, 4 Apr 2025 17:19:49 -0700
Subject: [PATCH 192/203] remove unnecessary change

---
 src/coreclr/jit/block.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/coreclr/jit/block.h b/src/coreclr/jit/block.h
index 2becb39f992089..0ecf8bdf42557f 100644
--- a/src/coreclr/jit/block.h
+++ b/src/coreclr/jit/block.h
@@ -447,7 +447,7 @@ enum BasicBlockFlags : uint64_t
     BBF_COLD                           = MAKE_BBFLAG(23), // BB is cold
     BBF_PROF_WEIGHT                    = MAKE_BBFLAG(24), // BB weight is computed from profile data
     BBF_KEEP_BBJ_ALWAYS                = MAKE_BBFLAG(25), // A special BBJ_ALWAYS block, used by EH code generation. Keep the jump kind
-                                                          // as BBJ_ALWAYS. Used on x86 for the final step block out of a finally
+                                                          // as BBJ_ALWAYS. Used on x86 for the final step block out of a finally.
     BBF_HAS_CALL                       = MAKE_BBFLAG(26), // BB contains a call
     BBF_DOMINATED_BY_EXCEPTIONAL_ENTRY = MAKE_BBFLAG(27), // Block is dominated by exceptional entry.
     BBF_BACKWARD_JUMP                  = MAKE_BBFLAG(28), // BB is surrounded by a backward jump/switch arc

From 7ba09e8a6110c2d4cdb15b955b77ced76d05e3f9 Mon Sep 17 00:00:00 2001
From: vsadov <8218165+VSadov@users.noreply.github.com>
Date: Fri, 4 Apr 2025 18:00:34 -0700
Subject: [PATCH 193/203] fix for x86

---
 src/coreclr/jit/importercalls.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/coreclr/jit/importercalls.cpp b/src/coreclr/jit/importercalls.cpp
index c8718e346e34e4..9bec6d242e5748 100644
--- a/src/coreclr/jit/importercalls.cpp
+++ b/src/coreclr/jit/importercalls.cpp
@@ -940,7 +940,7 @@ var_types Compiler::impImportCall(OPCODE                  opcode,
 
             if (varArgsCookie != nullptr)
             {
-                call->AsCall()->gtArgs.PushBack(this, NewCallArg::Primitive(varArgsCookie, TYP_REF)
+                call->AsCall()->gtArgs.PushBack(this, NewCallArg::Primitive(varArgsCookie)
                                                           .WellKnown(WellKnownArg::VarArgsCookie));
             }
         }

From 50e1e8500755c71946dfc2703d42f4f82d6cfef6 Mon Sep 17 00:00:00 2001
From: vsadov <8218165+VSadov@users.noreply.github.com>
Date: Fri, 4 Apr 2025 19:09:20 -0700
Subject: [PATCH 194/203] fix for arm32

---
 src/coreclr/vm/arm/stubs.cpp | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/src/coreclr/vm/arm/stubs.cpp b/src/coreclr/vm/arm/stubs.cpp
index 56293fb430df5e..e28af8bcd67aaa 100644
--- a/src/coreclr/vm/arm/stubs.cpp
+++ b/src/coreclr/vm/arm/stubs.cpp
@@ -1592,7 +1592,11 @@ void HijackFrame::UpdateRegDisplay_Impl(const PREGDISPLAY pRD, bool updateFloats
      pRD->IsCallerSPValid      = FALSE;
 
      pRD->pCurrentContext->Pc = m_ReturnAddress;
-     pRD->pCurrentContext->Sp = PTR_TO_TADDR(m_Args) + sizeof(struct HijackArgs);
+     size_t s = sizeof(struct HijackArgs);
+     _ASSERTE(s%4 == 0); // HijackArgs contains register values and hence will be a multiple of 4
+     // stack must be multiple of 8. So if s is not multiple of 8 then there must be padding of 4 bytes
+     s = s + s%8;
+     pRD->pCurrentContext->Sp = PTR_TO_TADDR(m_Args) + s ;
 
      pRD->pCurrentContext->R0 = m_Args->R0;
      pRD->pCurrentContext->R2 = m_Args->R2;

From b1fe9e272f3c1e2aea40f63fbc4c16413789fdf7 Mon Sep 17 00:00:00 2001
From: vsadov <8218165+VSadov@users.noreply.github.com>
Date: Sat, 5 Apr 2025 13:21:11 -0700
Subject: [PATCH 195/203] more fixing for arm32

---
 src/coreclr/vm/arm/asmhelpers.S | 9 ++++-----
 src/coreclr/vm/arm/cgencpu.h    | 4 ++++
 2 files changed, 8 insertions(+), 5 deletions(-)

diff --git a/src/coreclr/vm/arm/asmhelpers.S b/src/coreclr/vm/arm/asmhelpers.S
index be2e22f0ccf058..d8e0e62992bd64 100644
--- a/src/coreclr/vm/arm/asmhelpers.S
+++ b/src/coreclr/vm/arm/asmhelpers.S
@@ -884,20 +884,19 @@ DelayLoad_Helper\suffix:
 // ------------------------------------------------------------------
 // Hijack function for functions which return a value type
         NESTED_ENTRY OnHijackTripThread, _TEXT, NoHandler
-        PROLOG_PUSH "{r0,r2,r4-r11,lr}"
+        // saving r1 as well, as it can have partial return value when return is > 32 bits
+        PROLOG_PUSH "{r0,r1,r2,r4-r11,lr}"
 
         PROLOG_VPUSH "{d0-d3}"    // saving as d0-d3 can have the floating point return value
-        PROLOG_PUSH "{r1}"        // saving as r1 can have partial return value when return is > 32 bits
 
         CHECK_STACK_ALIGNMENT
 
-        add r0, sp, #40
+        add r0, sp, #50
         bl C_FUNC(OnHijackWorker)
 
-        EPILOG_POP "{r1}"
         EPILOG_VPOP "{d0-d3}"
 
-        EPILOG_POP "{r0,r2,r4-r11,pc}"
+        EPILOG_POP "{r0,r1,r2,r4-r11,pc}"
         NESTED_END OnHijackTripThread, _TEXT
 #endif
 
diff --git a/src/coreclr/vm/arm/cgencpu.h b/src/coreclr/vm/arm/cgencpu.h
index d2fba390a34ffd..6ae9d7e3b580d5 100644
--- a/src/coreclr/vm/arm/cgencpu.h
+++ b/src/coreclr/vm/arm/cgencpu.h
@@ -919,6 +919,10 @@ struct HijackArgs
                                // this is only used by functions OnHijackWorker()
     };
 
+    // saving r1 as well, as it can have partial return value when return is > 32 bits
+    // also keeps the struct size 8-byte aligned.
+    DWORD R1;
+
     union
     {
         DWORD R2;

From 030c9887c24d1c0da839adbc9b101de41ac641a4 Mon Sep 17 00:00:00 2001
From: vsadov <8218165+VSadov@users.noreply.github.com>
Date: Sat, 5 Apr 2025 16:33:00 -0700
Subject: [PATCH 196/203] typo

---
 src/coreclr/vm/arm/asmhelpers.S | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/coreclr/vm/arm/asmhelpers.S b/src/coreclr/vm/arm/asmhelpers.S
index d8e0e62992bd64..cf1c8f76d447ab 100644
--- a/src/coreclr/vm/arm/asmhelpers.S
+++ b/src/coreclr/vm/arm/asmhelpers.S
@@ -891,7 +891,7 @@ DelayLoad_Helper\suffix:
 
         CHECK_STACK_ALIGNMENT
 
-        add r0, sp, #50
+        add r0, sp, #40
         bl C_FUNC(OnHijackWorker)
 
         EPILOG_VPOP "{d0-d3}"

From 3f3fe9e6867eca04a36e76dfc450fbe7189f2958 Mon Sep 17 00:00:00 2001
From: vsadov <8218165+VSadov@users.noreply.github.com>
Date: Sat, 5 Apr 2025 20:23:44 -0700
Subject: [PATCH 197/203] actual fix?

---
 src/coreclr/vm/arm/asmhelpers.S | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/coreclr/vm/arm/asmhelpers.S b/src/coreclr/vm/arm/asmhelpers.S
index cf1c8f76d447ab..973de5db340ea7 100644
--- a/src/coreclr/vm/arm/asmhelpers.S
+++ b/src/coreclr/vm/arm/asmhelpers.S
@@ -891,7 +891,7 @@ DelayLoad_Helper\suffix:
 
         CHECK_STACK_ALIGNMENT
 
-        add r0, sp, #40
+        add r0, sp, #32
         bl C_FUNC(OnHijackWorker)
 
         EPILOG_VPOP "{d0-d3}"

From d5e4faf14509409e23f2c85dc58f8bc4a54b2ed9 Mon Sep 17 00:00:00 2001
From: vsadov <8218165+VSadov@users.noreply.github.com>
Date: Sun, 6 Apr 2025 16:22:12 -0700
Subject: [PATCH 198/203] hide async2 variants from reflection

---
 src/coreclr/vm/runtimehandles.cpp | 10 ++++++++++
 1 file changed, 10 insertions(+)

diff --git a/src/coreclr/vm/runtimehandles.cpp b/src/coreclr/vm/runtimehandles.cpp
index 7ac7928f6e2266..434849f278d079 100644
--- a/src/coreclr/vm/runtimehandles.cpp
+++ b/src/coreclr/vm/runtimehandles.cpp
@@ -1826,6 +1826,10 @@ FCIMPL2(MethodDesc*, RuntimeMethodHandle::GetStubIfNeededInternal,
 
     TypeHandle instType = refType->GetType();
 
+    // do not report async2 variants to reflection.
+    if (pMethod->IsAsync2VariantMethod())
+        return NULL;
+
     // Perf optimization: this logic is actually duplicated in FindOrCreateAssociatedMethodDescForReflection, but since it
     // is the more common case it's worth the duplicate check here to avoid the helper method frame
     if (pMethod->HasMethodInstantiation()
@@ -1850,6 +1854,12 @@ extern "C" MethodDesc* QCALLTYPE RuntimeMethodHandle_GetStubIfNeededSlow(MethodD
 
     GCX_COOP();
 
+    if (pMethod->IsAsync2VariantMethod())
+    {
+        // do not report async2 variants to reflection.
+        pMethod = pMethod->GetAsyncOtherVariant(/*allowInstParam*/ false);
+    }
+
     TypeHandle instType = declaringTypeHandle.AsTypeHandle();
 
     TypeHandle* inst = NULL;

From 6e43cadc60a7dcc49ddb25d98c84e1f89713aefd Mon Sep 17 00:00:00 2001
From: vsadov <8218165+VSadov@users.noreply.github.com>
Date: Sun, 6 Apr 2025 16:35:01 -0700
Subject: [PATCH 199/203] undo reflection workaround

---
 .../src/System/RuntimeType.CoreCLR.cs         | 70 ++++++++-----------
 1 file changed, 31 insertions(+), 39 deletions(-)

diff --git a/src/coreclr/System.Private.CoreLib/src/System/RuntimeType.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/RuntimeType.CoreCLR.cs
index 066573811cd5eb..cbee1aff762651 100644
--- a/src/coreclr/System.Private.CoreLib/src/System/RuntimeType.CoreCLR.cs
+++ b/src/coreclr/System.Private.CoreLib/src/System/RuntimeType.CoreCLR.cs
@@ -407,34 +407,34 @@ internal void Insert(ref T[] list, string? name, MemberListType listType)
                         switch (listType)
                         {
                             case MemberListType.CaseSensitive:
+                            {
+                                // Ensure we always return a list that has
+                                // been merged with the global list.
+                                T[]? cachedList = m_csMemberInfos[name!];
+                                if (cachedList == null)
                                 {
-                                    // Ensure we always return a list that has
-                                    // been merged with the global list.
-                                    T[]? cachedList = m_csMemberInfos[name!];
-                                    if (cachedList == null)
-                                    {
-                                        MergeWithGlobalList(list);
-                                        m_csMemberInfos[name!] = list;
-                                    }
-                                    else
-                                        list = cachedList;
+                                    MergeWithGlobalList(list);
+                                    m_csMemberInfos[name!] = list;
                                 }
-                                break;
+                                else
+                                    list = cachedList;
+                            }
+                            break;
 
                             case MemberListType.CaseInsensitive:
+                            {
+                                // Ensure we always return a list that has
+                                // been merged with the global list.
+                                T[]? cachedList = m_cisMemberInfos[name!];
+                                if (cachedList == null)
                                 {
-                                    // Ensure we always return a list that has
-                                    // been merged with the global list.
-                                    T[]? cachedList = m_cisMemberInfos[name!];
-                                    if (cachedList == null)
-                                    {
-                                        MergeWithGlobalList(list);
-                                        m_cisMemberInfos[name!] = list;
-                                    }
-                                    else
-                                        list = cachedList;
+                                    MergeWithGlobalList(list);
+                                    m_cisMemberInfos[name!] = list;
                                 }
-                                break;
+                                else
+                                    list = cachedList;
+                            }
+                            break;
 
                             case MemberListType.All:
                                 if (!m_cacheComplete)
@@ -552,12 +552,7 @@ private void MergeWithGlobalList(T[] list)
                                     // Grow the list by exactly one element in this case to avoid null entries at the end.
                                     //
 
-                                    // TODO: runtime-async we need to rationalize how async2 thunks work in reflection.
-                                    //       possibly they should not be exposed as they are runtime-provided implementation
-                                    //       details, that in theory may change.
-                                    //
-                                    //       For now I will disable this assert as we may get here with extra methods.
-                                    // Debug.Assert(false);
+                                    Debug.Assert(false);
 
                                     newSize = cachedMembers.Length + 1;
                                 }
@@ -609,9 +604,9 @@ private unsafe RuntimeMethodInfo[] PopulateMethods(Filter filter)
                             MethodAttributes methodAttributes = RuntimeMethodHandle.GetAttributes(methodHandle);
 
                             #region Continue if this is a constructor
-                            //Debug.Assert(
-                            //    (RuntimeMethodHandle.GetAttributes(methodHandle) & MethodAttributes.RTSpecialName) == 0 ||
-                            //    RuntimeMethodHandle.GetName(methodHandle).Equals(".cctor"));
+                            Debug.Assert(
+                                (RuntimeMethodHandle.GetAttributes(methodHandle) & MethodAttributes.RTSpecialName) == 0 ||
+                                RuntimeMethodHandle.GetName(methodHandle).Equals(".cctor"));
 
                             if ((methodAttributes & MethodAttributes.RTSpecialName) != 0)
                                 continue;
@@ -668,10 +663,10 @@ private unsafe RuntimeMethodInfo[] PopulateMethods(Filter filter)
                                 MethodAttributes methodAccess = methodAttributes & MethodAttributes.MemberAccessMask;
 
                                 #region Continue if this is a constructor
-                                //Debug.Assert(
-                                //    (RuntimeMethodHandle.GetAttributes(methodHandle) & MethodAttributes.RTSpecialName) == 0 ||
-                                //    RuntimeMethodHandle.GetName(methodHandle).Equals(".ctor") ||
-                                //    RuntimeMethodHandle.GetName(methodHandle).Equals(".cctor"));
+                                Debug.Assert(
+                                    (RuntimeMethodHandle.GetAttributes(methodHandle) & MethodAttributes.RTSpecialName) == 0 ||
+                                    RuntimeMethodHandle.GetName(methodHandle).Equals(".ctor") ||
+                                    RuntimeMethodHandle.GetName(methodHandle).Equals(".cctor"));
 
                                 if ((methodAttributes & MethodAttributes.RTSpecialName) != 0)
                                     continue;
@@ -771,9 +766,6 @@ private RuntimeConstructorInfo[] PopulateConstructors(Filter filter)
                         if ((methodAttributes & MethodAttributes.RTSpecialName) == 0)
                             continue;
 
-                        if (RuntimeMethodHandle.GetName(methodHandle) is not ".ctor" and not ".cctor")
-                            continue;
-
                         // Constructors should not be virtual or abstract
                         Debug.Assert(
                             (methodAttributes & MethodAttributes.Abstract) == 0 &&
@@ -2379,7 +2371,7 @@ private static bool FilterApplyMethodBase(
 
         #endregion
 
-#endregion
+        #endregion
 
         #region Private Data Members
 

From f015aec2753681ce9c20b34e3e492f0e9a68bd96 Mon Sep 17 00:00:00 2001
From: vsadov <8218165+VSadov@users.noreply.github.com>
Date: Sun, 6 Apr 2025 17:12:11 -0700
Subject: [PATCH 200/203] undo unnecessary EnC related changes

---
 src/coreclr/debug/ee/functioninfo.cpp |  2 +-
 src/coreclr/vm/encee.cpp              |  1 -
 src/coreclr/vm/methoditer.cpp         | 19 +------------------
 src/coreclr/vm/methoditer.h           |  4 ----
 4 files changed, 2 insertions(+), 24 deletions(-)

diff --git a/src/coreclr/debug/ee/functioninfo.cpp b/src/coreclr/debug/ee/functioninfo.cpp
index c7613dfd5d55c2..cbe39f0d4ac902 100644
--- a/src/coreclr/debug/ee/functioninfo.cpp
+++ b/src/coreclr/debug/ee/functioninfo.cpp
@@ -2022,7 +2022,7 @@ void DebuggerMethodInfo::CreateDJIsForNativeBlobs(AppDomain * pAppDomain, Method
         // have DJIs for every verision of a method that was EnCed.
         // This also handles the possibility of getting the same methoddesc back from the iterator.
         // It also lets EnC + generics play nice together (including if an generic method was EnC-ed)
-        LoadedMethodDescIterator it(pAppDomain, m_module, m_token, /* fIsAsync2Variant */false); // TODO: EnC doesn't handle async2 variants now
+        LoadedMethodDescIterator it(pAppDomain, m_module, m_token);
         CollectibleAssemblyHolder<Assembly *> pAssembly;
         while (it.Next(pAssembly.This()))
         {
diff --git a/src/coreclr/vm/encee.cpp b/src/coreclr/vm/encee.cpp
index 5b1d2ee0fa601b..b61b8acdc8bfe2 100644
--- a/src/coreclr/vm/encee.cpp
+++ b/src/coreclr/vm/encee.cpp
@@ -342,7 +342,6 @@ HRESULT EditAndContinueModule::UpdateMethod(MethodDesc *pMethod)
             AppDomain::GetCurrentDomain(),
             module,
             tkMethod,
-            /* fIsAsync2Variant */false, // TODO: EnC doesn't handle async2 variants now
             AssemblyIterationFlags(kIncludeLoaded | kIncludeExecution));
         CollectibleAssemblyHolder<Assembly *> pAssembly;
         while (it.Next(pAssembly.This()))
diff --git a/src/coreclr/vm/methoditer.cpp b/src/coreclr/vm/methoditer.cpp
index 79af7b137359e1..7f84806123d5a5 100644
--- a/src/coreclr/vm/methoditer.cpp
+++ b/src/coreclr/vm/methoditer.cpp
@@ -57,17 +57,6 @@ BOOL LoadedMethodDescIterator::Next(
         return FALSE;
     }
 
-    if (m_fIsAsync2Variant && m_mainMD->IsTaskReturningMethod())
-    {
-        m_mainMD = m_mainMD->GetMethodTable()->GetParallelMethodDesc(m_mainMD, AsyncVariantLookup::AsyncOtherVariant);
-
-        if (m_mainMD == NULL)
-        {
-            *pAssemblyHolder = NULL;
-            return FALSE;
-        }
-    }
-
     // Needs to work w/ non-generic methods too.
     if (!m_mainMD->HasClassOrMethodInstantiation())
     {
@@ -147,8 +136,6 @@ BOOL LoadedMethodDescIterator::Next(
             goto ADVANCE_METHOD;
         if (m_methodIteratorEntry->GetMethod()->GetMemberDef() != m_md)
             goto ADVANCE_METHOD;
-        if (m_methodIteratorEntry->GetMethod()->IsAsync2VariantMethod() != m_fIsAsync2Variant)
-            goto ADVANCE_METHOD;
     }
     else if (m_startedNonGenericMethod)
     {
@@ -216,7 +203,6 @@ LoadedMethodDescIterator::Start(
     AppDomain * pAppDomain,
     Module *pModule,
     mdMethodDef md,
-    bool fIsAsync2Variant,
     AssemblyIterationFlags assemblyIterationFlags)
 {
     CONTRACTL
@@ -228,8 +214,6 @@ LoadedMethodDescIterator::Start(
     }
     CONTRACTL_END;
 
-    m_fIsAsync2Variant = fIsAsync2Variant;
-
     m_assemIterationFlags = assemblyIterationFlags;
     m_mainMD = NULL;
     m_module = pModule;
@@ -250,7 +234,7 @@ LoadedMethodDescIterator::Start(
     mdMethodDef     md,
     MethodDesc      *pMethodDesc)
 {
-    Start(pAppDomain, pModule, md, /*m_fIsAsync2Variant*/ false); // TODO: EnC doesn't handle async2 variants now
+    Start(pAppDomain, pModule, md);
     m_mainMD = pMethodDesc;
 }
 
@@ -261,5 +245,4 @@ LoadedMethodDescIterator::LoadedMethodDescIterator(void)
     m_module = NULL;
     m_md = mdTokenNil;
     m_pAppDomain = NULL;
-    m_fIsAsync2Variant = false;
 }
diff --git a/src/coreclr/vm/methoditer.h b/src/coreclr/vm/methoditer.h
index 2a0ddf8f83f13f..90ccc67e96f6c5 100644
--- a/src/coreclr/vm/methoditer.h
+++ b/src/coreclr/vm/methoditer.h
@@ -34,8 +34,6 @@ class LoadedMethodDescIterator
     mdMethodDef  m_md;
     MethodDesc * m_mainMD;
     AppDomain *  m_pAppDomain;
-    bool         m_fIsAsync2Variant;
-
 
     // The following hold the state of the iteration....
     // Yes we iterate everything for the moment - we need
@@ -70,7 +68,6 @@ class LoadedMethodDescIterator
     void Start(AppDomain * pAppDomain,
                Module *pModule,
                mdMethodDef md,
-               bool fIsAsync2Variant,
                AssemblyIterationFlags assemIterationFlags = (AssemblyIterationFlags)(kIncludeLoaded | kIncludeExecution));
     void Start(AppDomain * pAppDomain, Module *pModule, mdMethodDef md, MethodDesc *pDesc);
 
@@ -78,7 +75,6 @@ class LoadedMethodDescIterator
         AppDomain * pAppDomain,
         Module *pModule,
         mdMethodDef md,
-        bool fIsAsync2Variant,
         AssemblyIterationFlags assemblyIterationFlags = (AssemblyIterationFlags)(kIncludeLoaded | kIncludeExecution))
     {
         LIMITED_METHOD_CONTRACT;

From 32d27f81e0a969f69904f4aa1e594976bed2a162 Mon Sep 17 00:00:00 2001
From: Vladimir Sadov <vsadov@microsoft.com>
Date: Sun, 6 Apr 2025 17:34:48 -0700
Subject: [PATCH 201/203] Undo whitespace only changes

---
 src/coreclr/gc/sample/gcenv.ee.cpp | 2 +-
 src/coreclr/vm/eetwain.cpp         | 2 +-
 src/coreclr/vm/gcenv.ee.cpp        | 2 +-
 3 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/src/coreclr/gc/sample/gcenv.ee.cpp b/src/coreclr/gc/sample/gcenv.ee.cpp
index 19ceb56fc16f07..4aba23c9a077dc 100644
--- a/src/coreclr/gc/sample/gcenv.ee.cpp
+++ b/src/coreclr/gc/sample/gcenv.ee.cpp
@@ -366,4 +366,4 @@ void GCToEEInterface::LogErrorToHost(const char *message)
 uint64_t GCToEEInterface::GetThreadOSThreadId(Thread* thread)
 {
     return 0;
-}
+}
\ No newline at end of file
diff --git a/src/coreclr/vm/eetwain.cpp b/src/coreclr/vm/eetwain.cpp
index 51cb1daf1ac271..7f6b27c14be328 100644
--- a/src/coreclr/vm/eetwain.cpp
+++ b/src/coreclr/vm/eetwain.cpp
@@ -2401,4 +2401,4 @@ size_t InterpreterCodeManager::GetFunctionSize(GCInfoToken gcInfoToken)
     return 0;
 }
 
-#endif // FEATURE_INTERPRETER
+#endif // FEATURE_INTERPRETER
\ No newline at end of file
diff --git a/src/coreclr/vm/gcenv.ee.cpp b/src/coreclr/vm/gcenv.ee.cpp
index 749174e575b7b0..4738b0d7aeb8fa 100644
--- a/src/coreclr/vm/gcenv.ee.cpp
+++ b/src/coreclr/vm/gcenv.ee.cpp
@@ -1832,4 +1832,4 @@ void GCToEEInterface::LogErrorToHost(const char *message)
 uint64_t GCToEEInterface::GetThreadOSThreadId(Thread* thread)
 {
     return thread->GetOSThreadId64();
-}
+}
\ No newline at end of file

From 86075c1614fba5b2c5ef758f19a28568fb1f0a01 Mon Sep 17 00:00:00 2001
From: vsadov <8218165+VSadov@users.noreply.github.com>
Date: Sun, 6 Apr 2025 19:17:04 -0700
Subject: [PATCH 202/203] some trivial cleanups

---
 src/coreclr/vm/commodule.cpp    | 10 ++++++++--
 src/coreclr/vm/jitinterface.cpp |  4 ++--
 src/coreclr/vm/method.hpp       |  2 +-
 3 files changed, 11 insertions(+), 5 deletions(-)

diff --git a/src/coreclr/vm/commodule.cpp b/src/coreclr/vm/commodule.cpp
index 1b79649fdfb046..e89b5cf3362a25 100644
--- a/src/coreclr/vm/commodule.cpp
+++ b/src/coreclr/vm/commodule.cpp
@@ -302,7 +302,13 @@ extern "C" INT32 QCALLTYPE ModuleBuilder_GetMemberRefOfMethodInfo(QCall::ModuleH
         COMPlusThrow(kNotSupportedException);
     }
 
-    if ((pMeth->GetMethodTable()->GetModule() == pModule) && !pMeth->IsAsync2VariantMethod())
+    if (pMeth->IsAsync2VariantMethod())
+    {
+        _ASSERTE(!"Should not have come here!");
+        COMPlusThrow(kNotSupportedException);
+    }
+
+    if ((pMeth->GetMethodTable()->GetModule() == pModule))
     {
         // If the passed in method is defined in the same module, just return the MethodDef token
         memberRefE = pMeth->GetMemberDef();
@@ -317,7 +323,7 @@ extern "C" INT32 QCALLTYPE ModuleBuilder_GetMemberRefOfMethodInfo(QCall::ModuleH
 
         ULONG           cbComSig;
         PCCOR_SIGNATURE pvComSig;
-        pMeth->GetSig(&pvComSig, &cbComSig);
+        IfFailThrow(pMeth->GetMDImport()->GetSigOfMethodDef(pMeth->GetMemberDef(), &cbComSig, &pvComSig));
 
         // Translate the method sig into this scope
         Assembly * pRefedAssembly = pMeth->GetModule()->GetAssembly();
diff --git a/src/coreclr/vm/jitinterface.cpp b/src/coreclr/vm/jitinterface.cpp
index 4d794b4dfb0aab..5e98fbbeccb43d 100644
--- a/src/coreclr/vm/jitinterface.cpp
+++ b/src/coreclr/vm/jitinterface.cpp
@@ -418,6 +418,8 @@ enum ConvToJitSigFlags : int
     CONV_TO_JITSIG_FLAGS_LOCALSIG   = 0x1,
 };
 
+AsyncMethodSignatureKind ClassifyAsyncMethodSignatureCore(SigPointer sig, Module* pModule, PCCOR_SIGNATURE initialSig, ULONG* offsetOfAsyncDetails, bool *isValueTask);
+
 //---------------------------------------------------------------------------------------
 //
 //@GENERICS:
@@ -432,8 +434,6 @@ enum ConvToJitSigFlags : int
 // localSig     - Is it a local variables declaration, or a method signature (with return type, etc).
 // contextType  - The type with any instantiaton information
 //
-AsyncMethodSignatureKind ClassifyAsyncMethodSignatureCore(SigPointer sig, Module* pModule, PCCOR_SIGNATURE initialSig, ULONG* offsetOfAsyncDetails, bool *isValueTask);
-
 static void ConvToJitSig(
     SigPointer            sig,
     CORINFO_MODULE_HANDLE scopeHnd,
diff --git a/src/coreclr/vm/method.hpp b/src/coreclr/vm/method.hpp
index 9151b35df01d5d..d02dda8b256ae4 100644
--- a/src/coreclr/vm/method.hpp
+++ b/src/coreclr/vm/method.hpp
@@ -59,7 +59,7 @@ enum class AsyncMethodKind
 {
     // Regular methods not returning tasks
     // These are "normal" methods that do not get other variants.
-    // Note: Generic T-returning methods are NotAsync, even if T could be a Task.
+    // NOTE: Generic T-returning methods are NotAsync, even if T could be a Task.
     NotAsync,
 
     // Regular methods that return Task/ValueTask

From 21fcafa7a32689fe096d66ca858d10a42ad6ec16 Mon Sep 17 00:00:00 2001
From: vsadov <8218165+VSadov@users.noreply.github.com>
Date: Sun, 6 Apr 2025 22:33:15 -0700
Subject: [PATCH 203/203] IntroducedMethodIterator should return all methods,
 filter async variants in API used in reflection,

---
 src/coreclr/vm/methodtable.cpp    | 46 ++++++++++++-------------------
 src/coreclr/vm/runtimehandles.cpp |  5 ++++
 2 files changed, 22 insertions(+), 29 deletions(-)

diff --git a/src/coreclr/vm/methodtable.cpp b/src/coreclr/vm/methodtable.cpp
index e0d7c320497332..efb2ad647f2993 100644
--- a/src/coreclr/vm/methodtable.cpp
+++ b/src/coreclr/vm/methodtable.cpp
@@ -7293,15 +7293,7 @@ MethodDesc * MethodTable::IntroducedMethodIterator::GetFirst(MethodTable *pMT)
 {
     LIMITED_METHOD_CONTRACT;
     MethodDescChunk * pChunk = pMT->GetClass()->GetChunks();
-    if (pChunk == NULL)
-        return NULL;
-
-    MethodDesc* md = pChunk->GetFirstMethodDesc();
-    if (!md->IsAsync2VariantMethod())
-        return md;
-
-    // skip async2 variants, the other variant will be reported instead
-    return GetNext(md);
+    return (pChunk != NULL) ? pChunk->GetFirstMethodDesc() : NULL;
 }
 
 //==========================================================================================
@@ -7310,30 +7302,26 @@ MethodDesc * MethodTable::IntroducedMethodIterator::GetNext(MethodDesc * pMD)
     WRAPPER_NO_CONTRACT;
 
     MethodDescChunk * pChunk = pMD->GetMethodDescChunk();
-    do
-    {
-        // Check whether the next MethodDesc is still within the bounds of the current chunk
-        TADDR pNext = dac_cast<TADDR>(pMD) + pMD->SizeOf();
-        TADDR pEnd = dac_cast<TADDR>(pChunk) + pChunk->SizeOf();
 
-        if (pNext < pEnd)
-        {
-            // Just skip to the next method in the same chunk
-            pMD = PTR_MethodDesc(pNext);
-        }
-        else
-        {
-            _ASSERTE(pNext == pEnd);
+    // Check whether the next MethodDesc is still within the bounds of the current chunk
+    TADDR pNext = dac_cast<TADDR>(pMD) + pMD->SizeOf();
+    TADDR pEnd = dac_cast<TADDR>(pChunk) + pChunk->SizeOf();
 
-            // We have walked all the methods in the current chunk. Move on
-            // to the next chunk.
-            pChunk = pChunk->GetNextChunk();
+    if (pNext < pEnd)
+    {
+        // Just skip to the next method in the same chunk
+        pMD = PTR_MethodDesc(pNext);
+    }
+    else
+    {
+        _ASSERTE(pNext == pEnd);
 
-            pMD = (pChunk != NULL) ? pChunk->GetFirstMethodDesc() : NULL;
-        }
+        // We have walked all the methods in the current chunk. Move on
+        // to the next chunk.
+        pChunk = pChunk->GetNextChunk();
 
-    // skip async2 variants, the other variant will be reported instead
-    } while (pMD != NULL && pMD->IsAsync2VariantMethod());
+        pMD = (pChunk != NULL) ? pChunk->GetFirstMethodDesc() : NULL;
+    }
 
     return pMD;
 }
diff --git a/src/coreclr/vm/runtimehandles.cpp b/src/coreclr/vm/runtimehandles.cpp
index 434849f278d079..a5015847d6e697 100644
--- a/src/coreclr/vm/runtimehandles.cpp
+++ b/src/coreclr/vm/runtimehandles.cpp
@@ -191,6 +191,9 @@ FCIMPL1(MethodDesc *, RuntimeTypeHandle::GetFirstIntroducedMethod, ReflectClassB
 
     MethodTable* pMT = typeHandle.AsMethodTable();
     MethodDesc* pMethod = MethodTable::IntroducedMethodIterator::GetFirst(pMT);
+    while (pMethod && pMethod->IsAsync2VariantMethod())
+        pMethod = MethodTable::IntroducedMethodIterator::GetNext(pMethod);
+
     return pMethod;
 }
 FCIMPLEND
@@ -205,6 +208,8 @@ FCIMPL1(void, RuntimeTypeHandle::GetNextIntroducedMethod, MethodDesc ** ppMethod
     CONTRACTL_END;
 
     MethodDesc *pMethod = MethodTable::IntroducedMethodIterator::GetNext(*ppMethod);
+    while (pMethod && pMethod->IsAsync2VariantMethod())
+        pMethod = MethodTable::IntroducedMethodIterator::GetNext(pMethod);
 
     *ppMethod = pMethod;
 }
