From b7bb968e7c63d87c23fae0b00376dcfc316d7867 Mon Sep 17 00:00:00 2001 From: Jakob Botsch Nielsen Date: Wed, 1 Oct 2025 14:54:31 +0200 Subject: [PATCH 01/72] Set target method desc for resumption stubs --- src/coreclr/vm/jitinterface.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/coreclr/vm/jitinterface.cpp b/src/coreclr/vm/jitinterface.cpp index aea6055a76dbff..f31b650dc9f349 100644 --- a/src/coreclr/vm/jitinterface.cpp +++ b/src/coreclr/vm/jitinterface.cpp @@ -14810,6 +14810,9 @@ CORINFO_METHOD_HANDLE CEEJitInfo::getAsyncResumptionStub() amTracker.SuppressRelease(); + ILStubResolver *pResolver = result->AsDynamicMethodDesc()->GetILStubResolver(); + pResolver->SetStubTargetMethodDesc(m_pMethodBeingCompiled); + const char* optimizationTierName = "UnknownTier"; #ifdef FEATURE_TIERED_COMPILATION switch (ncv.GetOptimizationTier()) From d5d08640c9463d573971cd1f3037df2824d011ec Mon Sep 17 00:00:00 2001 From: Jakob Botsch Nielsen Date: Wed, 1 Oct 2025 16:21:57 +0200 Subject: [PATCH 02/72] Add JIT-EE boilerplate --- src/coreclr/inc/cordebuginfo.h | 35 ++++ src/coreclr/inc/corinfo.h | 10 ++ src/coreclr/inc/icorjitinfoimpl_generated.h | 6 + src/coreclr/inc/jiteeversionguid.h | 10 +- src/coreclr/jit/ICorJitInfo_names_generated.h | 1 + .../jit/ICorJitInfo_wrapper_generated.hpp | 11 ++ .../tools/Common/JitInterface/CorInfoImpl.cs | 9 ++ .../JitInterface/CorInfoImpl_generated.cs | 153 ++++++++++-------- .../tools/Common/JitInterface/CorInfoTypes.cs | 35 ++++ .../ThunkGenerator/ThunkInput.txt | 4 + .../aot/jitinterface/jitinterface_generated.h | 12 ++ .../superpmi-shim-collector/icorjitinfo.cpp | 10 ++ .../icorjitinfo_generated.cpp | 10 ++ .../icorjitinfo_generated.cpp | 9 ++ .../tools/superpmi/superpmi/icorjitinfo.cpp | 13 ++ src/coreclr/vm/jitinterface.cpp | 45 ++++++ src/coreclr/vm/jitinterface.h | 21 +++ 17 files changed, 320 insertions(+), 74 deletions(-) diff --git a/src/coreclr/inc/cordebuginfo.h b/src/coreclr/inc/cordebuginfo.h index b0e813dffd3ab9..03fe0d9d463cd6 100644 --- a/src/coreclr/inc/cordebuginfo.h +++ b/src/coreclr/inc/cordebuginfo.h @@ -431,4 +431,39 @@ class ICorDebugInfo // Source information about the IL instruction in the inlinee SourceTypes Source; }; + + struct AsyncContinuationVarInfo + { + // IL number of variable (or one of the special IL numbers, like UNKNOWN_ILNUM) + uint32_t VarNumber; + // Offset in continuation's data where this variable is stored + uint32_t Offset; + }; + + struct AsyncSuspensionPoint + { + // State number assigned to this suspension point. UINT_MAX if no state + // number is assigned (when AsyncInfo::NumSuspensionPoints == 1). + uint32_t StateNumber; + // IL offset in the root method that resulted in the creation of this suspension point. + uint32_t RootILOffset; + // Index of inline tree node containing the IL offset (0 for root) + uint32_t Inlinee; + // IL offset that resulted in the creation of the suspension point. + uint32_t ILOffset; + uint32_t NumVars; + // Count of AsyncContinuationVarInfo + uint32_t NumContinuationVars; + // Index into array of AsyncContinuationVarInfo of first variable + uint32_t ContinuationVarsIndex; + }; + + struct AsyncInfo + { + // Offset in contiuation's data where state number is stored. UINT_MAX + // if no state number is stored (when NumSuspensionPoints == 1). + uint32_t StateOffset; + // Number of suspension points in the method. + uint32_t NumSuspensionPoints; + }; }; diff --git a/src/coreclr/inc/corinfo.h b/src/coreclr/inc/corinfo.h index b0d0108a31dffe..528b12f801fdb8 100644 --- a/src/coreclr/inc/corinfo.h +++ b/src/coreclr/inc/corinfo.h @@ -2912,6 +2912,16 @@ class ICorStaticInfo uint32_t numMappings // [IN] Number of rich mappings ) = 0; + // Report async debug information to EE. + // The arrays are expected to be allocated with allocateArray + // and ownership is transferred to the EE with this call. + virtual void reportAsyncDebugInfo( + ICorDebugInfo::AsyncInfo* asyncInfo, // [IN] Async method information + ICorDebugInfo::AsyncSuspensionPoint* suspensionPoints, // [IN] Async suspension points + ICorDebugInfo::AsyncContinuationVarInfo* vars, // [IN] Async continuation variable info + uint32_t numVars // [IN] Number of continuation variables + ) = 0; + // Report back some metadata about the compilation to the EE -- for // example, metrics about the compilation. virtual void reportMetadata( diff --git a/src/coreclr/inc/icorjitinfoimpl_generated.h b/src/coreclr/inc/icorjitinfoimpl_generated.h index 6f4908d9c05235..d20a1a39698a65 100644 --- a/src/coreclr/inc/icorjitinfoimpl_generated.h +++ b/src/coreclr/inc/icorjitinfoimpl_generated.h @@ -453,6 +453,12 @@ void reportRichMappings( ICorDebugInfo::RichOffsetMapping* mappings, uint32_t numMappings) override; +void reportAsyncDebugInfo( + ICorDebugInfo::AsyncInfo* asyncInfo, + ICorDebugInfo::AsyncSuspensionPoint* suspensionPoints, + ICorDebugInfo::AsyncContinuationVarInfo* vars, + uint32_t numVars) override; + void reportMetadata( const char* key, const void* value, diff --git a/src/coreclr/inc/jiteeversionguid.h b/src/coreclr/inc/jiteeversionguid.h index e1fda2eeb3cbb0..64cb2e4f1d8c61 100644 --- a/src/coreclr/inc/jiteeversionguid.h +++ b/src/coreclr/inc/jiteeversionguid.h @@ -37,11 +37,11 @@ #include -constexpr GUID JITEEVersionIdentifier = { /* 3d2bdd20-eced-4a07-b9fb-227ce7f55fcd */ - 0x3d2bdd20, - 0xeced, - 0x4a07, - {0xb9, 0xfb, 0x22, 0x7c, 0xe7, 0xf5, 0x5f, 0xcd} +constexpr GUID JITEEVersionIdentifier = { /* e1c4ef4f-62a1-4055-813e-23837fdfdffa */ + 0xe1c4ef4f, + 0x62a1, + 0x4055, + {0x81, 0x3e, 0x23, 0x83, 0x7f, 0xdf, 0xdf, 0xfa} }; #endif // JIT_EE_VERSIONING_GUID_H diff --git a/src/coreclr/jit/ICorJitInfo_names_generated.h b/src/coreclr/jit/ICorJitInfo_names_generated.h index 1e455eb98bc74e..7a0e5b39c718cb 100644 --- a/src/coreclr/jit/ICorJitInfo_names_generated.h +++ b/src/coreclr/jit/ICorJitInfo_names_generated.h @@ -112,6 +112,7 @@ DEF_CLR_API(setBoundaries) DEF_CLR_API(getVars) DEF_CLR_API(setVars) DEF_CLR_API(reportRichMappings) +DEF_CLR_API(reportAsyncDebugInfo) DEF_CLR_API(reportMetadata) DEF_CLR_API(allocateArray) DEF_CLR_API(freeArray) diff --git a/src/coreclr/jit/ICorJitInfo_wrapper_generated.hpp b/src/coreclr/jit/ICorJitInfo_wrapper_generated.hpp index 71dc8eaf7119af..e600aa12f90ebe 100644 --- a/src/coreclr/jit/ICorJitInfo_wrapper_generated.hpp +++ b/src/coreclr/jit/ICorJitInfo_wrapper_generated.hpp @@ -1067,6 +1067,17 @@ void WrapICorJitInfo::reportRichMappings( API_LEAVE(reportRichMappings); } +void WrapICorJitInfo::reportAsyncDebugInfo( + ICorDebugInfo::AsyncInfo* asyncInfo, + ICorDebugInfo::AsyncSuspensionPoint* suspensionPoints, + ICorDebugInfo::AsyncContinuationVarInfo* vars, + uint32_t numVars) +{ + API_ENTER(reportAsyncDebugInfo); + wrapHnd->reportAsyncDebugInfo(asyncInfo, suspensionPoints, vars, numVars); + API_LEAVE(reportAsyncDebugInfo); +} + void WrapICorJitInfo::reportMetadata( const char* key, const void* value, diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs index c0bd2516d730bf..2a8ce050d7329d 100644 --- a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs +++ b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs @@ -3215,6 +3215,15 @@ private void reportRichMappings(InlineTreeNode* inlineTree, uint numInlineTree, Marshal.FreeHGlobal((IntPtr)mappings); } +#pragma warning disable CA1822 // Mark members as static + private void reportAsyncDebugInfo(AsyncInfo* asyncInfo, AsyncSuspensionPoint* suspensionPoints, AsyncContinuationVarInfo* vars, uint numVars) +#pragma warning restore CA1822 // Mark members as static + { + Marshal.FreeHGlobal((IntPtr)asyncInfo); + Marshal.FreeHGlobal((IntPtr)suspensionPoints); + Marshal.FreeHGlobal((IntPtr)vars); + } + #pragma warning disable CA1822 // Mark members as static private void reportMetadata(byte* key, void* value, nuint length) #pragma warning restore CA1822 // Mark members as static diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoImpl_generated.cs b/src/coreclr/tools/Common/JitInterface/CorInfoImpl_generated.cs index 8d0c83046277dd..9ff76dddc4462b 100644 --- a/src/coreclr/tools/Common/JitInterface/CorInfoImpl_generated.cs +++ b/src/coreclr/tools/Common/JitInterface/CorInfoImpl_generated.cs @@ -1607,6 +1607,20 @@ private static void _reportRichMappings(IntPtr thisHandle, IntPtr* ppException, } } + [UnmanagedCallersOnly] + private static void _reportAsyncDebugInfo(IntPtr thisHandle, IntPtr* ppException, AsyncInfo* asyncInfo, AsyncSuspensionPoint* suspensionPoints, AsyncContinuationVarInfo* vars, uint numVars) + { + var _this = GetThis(thisHandle); + try + { + _this.reportAsyncDebugInfo(asyncInfo, suspensionPoints, vars, numVars); + } + catch (Exception ex) + { + *ppException = _this.AllocException(ex); + } + } + [UnmanagedCallersOnly] private static void _reportMetadata(IntPtr thisHandle, IntPtr* ppException, byte* key, void* value, UIntPtr length) { @@ -2606,7 +2620,7 @@ private static uint _getJitFlags(IntPtr thisHandle, IntPtr* ppException, CORJIT_ private static IntPtr GetUnmanagedCallbacks() { - void** callbacks = (void**)Marshal.AllocCoTaskMem(sizeof(IntPtr) * 176); + void** callbacks = (void**)Marshal.AllocCoTaskMem(sizeof(IntPtr) * 177); callbacks[0] = (delegate* unmanaged)&_isIntrinsic; callbacks[1] = (delegate* unmanaged)&_notifyMethodInfoUsage; @@ -2716,74 +2730,75 @@ private static IntPtr GetUnmanagedCallbacks() callbacks[105] = (delegate* unmanaged)&_getVars; callbacks[106] = (delegate* unmanaged)&_setVars; callbacks[107] = (delegate* unmanaged)&_reportRichMappings; - callbacks[108] = (delegate* unmanaged)&_reportMetadata; - callbacks[109] = (delegate* unmanaged)&_allocateArray; - callbacks[110] = (delegate* unmanaged)&_freeArray; - callbacks[111] = (delegate* unmanaged)&_getArgNext; - callbacks[112] = (delegate* unmanaged)&_getArgType; - callbacks[113] = (delegate* unmanaged)&_getExactClasses; - callbacks[114] = (delegate* unmanaged)&_getArgClass; - callbacks[115] = (delegate* unmanaged)&_getHFAType; - callbacks[116] = (delegate* unmanaged)&_runWithErrorTrap; - callbacks[117] = (delegate* unmanaged)&_runWithSPMIErrorTrap; - callbacks[118] = (delegate* unmanaged)&_getEEInfo; - callbacks[119] = (delegate* unmanaged)&_getAsyncInfo; - callbacks[120] = (delegate* unmanaged)&_getMethodDefFromMethod; - callbacks[121] = (delegate* unmanaged)&_printMethodName; - callbacks[122] = (delegate* unmanaged)&_getMethodNameFromMetadata; - callbacks[123] = (delegate* unmanaged)&_getMethodHash; - callbacks[124] = (delegate* unmanaged)&_getSystemVAmd64PassStructInRegisterDescriptor; - callbacks[125] = (delegate* unmanaged)&_getSwiftLowering; - callbacks[126] = (delegate* unmanaged)&_getFpStructLowering; - callbacks[127] = (delegate* unmanaged)&_getThreadTLSIndex; - callbacks[128] = (delegate* unmanaged)&_getAddrOfCaptureThreadGlobal; - callbacks[129] = (delegate* unmanaged)&_getHelperFtn; - callbacks[130] = (delegate* unmanaged)&_getFunctionEntryPoint; - callbacks[131] = (delegate* unmanaged)&_getFunctionFixedEntryPoint; - callbacks[132] = (delegate* unmanaged)&_getLazyStringLiteralHelper; - callbacks[133] = (delegate* unmanaged)&_embedModuleHandle; - callbacks[134] = (delegate* unmanaged)&_embedClassHandle; - callbacks[135] = (delegate* unmanaged)&_embedMethodHandle; - callbacks[136] = (delegate* unmanaged)&_embedFieldHandle; - callbacks[137] = (delegate* unmanaged)&_embedGenericHandle; - callbacks[138] = (delegate* unmanaged)&_getLocationOfThisType; - callbacks[139] = (delegate* unmanaged)&_getAddressOfPInvokeTarget; - callbacks[140] = (delegate* unmanaged)&_GetCookieForPInvokeCalliSig; - callbacks[141] = (delegate* unmanaged)&_GetCookieForInterpreterCalliSig; - callbacks[142] = (delegate* unmanaged)&_getJustMyCodeHandle; - callbacks[143] = (delegate* unmanaged)&_GetProfilingHandle; - callbacks[144] = (delegate* unmanaged)&_getCallInfo; - callbacks[145] = (delegate* unmanaged)&_getStaticFieldContent; - callbacks[146] = (delegate* unmanaged)&_getObjectContent; - callbacks[147] = (delegate* unmanaged)&_getStaticFieldCurrentClass; - callbacks[148] = (delegate* unmanaged)&_getVarArgsHandle; - callbacks[149] = (delegate* unmanaged)&_constructStringLiteral; - callbacks[150] = (delegate* unmanaged)&_emptyStringLiteral; - callbacks[151] = (delegate* unmanaged)&_getFieldThreadLocalStoreID; - callbacks[152] = (delegate* unmanaged)&_GetDelegateCtor; - callbacks[153] = (delegate* unmanaged)&_MethodCompileComplete; - callbacks[154] = (delegate* unmanaged)&_getTailCallHelpers; - callbacks[155] = (delegate* unmanaged)&_getAsyncResumptionStub; - callbacks[156] = (delegate* unmanaged)&_convertPInvokeCalliToCall; - callbacks[157] = (delegate* unmanaged)&_notifyInstructionSetUsage; - callbacks[158] = (delegate* unmanaged)&_updateEntryPointForTailCall; - callbacks[159] = (delegate* unmanaged)&_allocMem; - callbacks[160] = (delegate* unmanaged)&_reserveUnwindInfo; - callbacks[161] = (delegate* unmanaged)&_allocUnwindInfo; - callbacks[162] = (delegate* unmanaged)&_allocGCInfo; - callbacks[163] = (delegate* unmanaged)&_setEHcount; - callbacks[164] = (delegate* unmanaged)&_setEHinfo; - callbacks[165] = (delegate* unmanaged)&_logMsg; - callbacks[166] = (delegate* unmanaged)&_doAssert; - callbacks[167] = (delegate* unmanaged)&_reportFatalError; - callbacks[168] = (delegate* unmanaged)&_getPgoInstrumentationResults; - callbacks[169] = (delegate* unmanaged)&_allocPgoInstrumentationBySchema; - callbacks[170] = (delegate* unmanaged)&_recordCallSite; - callbacks[171] = (delegate* unmanaged)&_recordRelocation; - callbacks[172] = (delegate* unmanaged)&_getRelocTypeHint; - callbacks[173] = (delegate* unmanaged)&_getExpectedTargetArchitecture; - callbacks[174] = (delegate* unmanaged)&_getJitFlags; - callbacks[175] = (delegate* unmanaged)&_getSpecialCopyHelper; + callbacks[108] = (delegate* unmanaged)&_reportAsyncDebugInfo; + callbacks[109] = (delegate* unmanaged)&_reportMetadata; + callbacks[110] = (delegate* unmanaged)&_allocateArray; + callbacks[111] = (delegate* unmanaged)&_freeArray; + callbacks[112] = (delegate* unmanaged)&_getArgNext; + callbacks[113] = (delegate* unmanaged)&_getArgType; + callbacks[114] = (delegate* unmanaged)&_getExactClasses; + callbacks[115] = (delegate* unmanaged)&_getArgClass; + callbacks[116] = (delegate* unmanaged)&_getHFAType; + callbacks[117] = (delegate* unmanaged)&_runWithErrorTrap; + callbacks[118] = (delegate* unmanaged)&_runWithSPMIErrorTrap; + callbacks[119] = (delegate* unmanaged)&_getEEInfo; + callbacks[120] = (delegate* unmanaged)&_getAsyncInfo; + callbacks[121] = (delegate* unmanaged)&_getMethodDefFromMethod; + callbacks[122] = (delegate* unmanaged)&_printMethodName; + callbacks[123] = (delegate* unmanaged)&_getMethodNameFromMetadata; + callbacks[124] = (delegate* unmanaged)&_getMethodHash; + callbacks[125] = (delegate* unmanaged)&_getSystemVAmd64PassStructInRegisterDescriptor; + callbacks[126] = (delegate* unmanaged)&_getSwiftLowering; + callbacks[127] = (delegate* unmanaged)&_getFpStructLowering; + callbacks[128] = (delegate* unmanaged)&_getThreadTLSIndex; + callbacks[129] = (delegate* unmanaged)&_getAddrOfCaptureThreadGlobal; + callbacks[130] = (delegate* unmanaged)&_getHelperFtn; + callbacks[131] = (delegate* unmanaged)&_getFunctionEntryPoint; + callbacks[132] = (delegate* unmanaged)&_getFunctionFixedEntryPoint; + callbacks[133] = (delegate* unmanaged)&_getLazyStringLiteralHelper; + callbacks[134] = (delegate* unmanaged)&_embedModuleHandle; + callbacks[135] = (delegate* unmanaged)&_embedClassHandle; + callbacks[136] = (delegate* unmanaged)&_embedMethodHandle; + callbacks[137] = (delegate* unmanaged)&_embedFieldHandle; + callbacks[138] = (delegate* unmanaged)&_embedGenericHandle; + callbacks[139] = (delegate* unmanaged)&_getLocationOfThisType; + callbacks[140] = (delegate* unmanaged)&_getAddressOfPInvokeTarget; + callbacks[141] = (delegate* unmanaged)&_GetCookieForPInvokeCalliSig; + callbacks[142] = (delegate* unmanaged)&_GetCookieForInterpreterCalliSig; + callbacks[143] = (delegate* unmanaged)&_getJustMyCodeHandle; + callbacks[144] = (delegate* unmanaged)&_GetProfilingHandle; + callbacks[145] = (delegate* unmanaged)&_getCallInfo; + callbacks[146] = (delegate* unmanaged)&_getStaticFieldContent; + callbacks[147] = (delegate* unmanaged)&_getObjectContent; + callbacks[148] = (delegate* unmanaged)&_getStaticFieldCurrentClass; + callbacks[149] = (delegate* unmanaged)&_getVarArgsHandle; + callbacks[150] = (delegate* unmanaged)&_constructStringLiteral; + callbacks[151] = (delegate* unmanaged)&_emptyStringLiteral; + callbacks[152] = (delegate* unmanaged)&_getFieldThreadLocalStoreID; + callbacks[153] = (delegate* unmanaged)&_GetDelegateCtor; + callbacks[154] = (delegate* unmanaged)&_MethodCompileComplete; + callbacks[155] = (delegate* unmanaged)&_getTailCallHelpers; + callbacks[156] = (delegate* unmanaged)&_getAsyncResumptionStub; + callbacks[157] = (delegate* unmanaged)&_convertPInvokeCalliToCall; + callbacks[158] = (delegate* unmanaged)&_notifyInstructionSetUsage; + callbacks[159] = (delegate* unmanaged)&_updateEntryPointForTailCall; + callbacks[160] = (delegate* unmanaged)&_allocMem; + callbacks[161] = (delegate* unmanaged)&_reserveUnwindInfo; + callbacks[162] = (delegate* unmanaged)&_allocUnwindInfo; + callbacks[163] = (delegate* unmanaged)&_allocGCInfo; + callbacks[164] = (delegate* unmanaged)&_setEHcount; + callbacks[165] = (delegate* unmanaged)&_setEHinfo; + callbacks[166] = (delegate* unmanaged)&_logMsg; + callbacks[167] = (delegate* unmanaged)&_doAssert; + callbacks[168] = (delegate* unmanaged)&_reportFatalError; + callbacks[169] = (delegate* unmanaged)&_getPgoInstrumentationResults; + callbacks[170] = (delegate* unmanaged)&_allocPgoInstrumentationBySchema; + callbacks[171] = (delegate* unmanaged)&_recordCallSite; + callbacks[172] = (delegate* unmanaged)&_recordRelocation; + callbacks[173] = (delegate* unmanaged)&_getRelocTypeHint; + callbacks[174] = (delegate* unmanaged)&_getExpectedTargetArchitecture; + callbacks[175] = (delegate* unmanaged)&_getJitFlags; + callbacks[176] = (delegate* unmanaged)&_getSpecialCopyHelper; return (IntPtr)callbacks; } diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs b/src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs index 2ae78da1fde8f0..e7e11c79095109 100644 --- a/src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs +++ b/src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs @@ -1319,6 +1319,41 @@ public struct RichOffsetMapping public SourceTypes Source; } + public struct AsyncContinuationVarInfo + { + // IL number of variable (or one of the special IL numbers, like UNKNOWN_ILNUM) + public uint VarNumber; + // Offset in continuation's data where this variable is stored + public uint Offset; + } + + public struct AsyncSuspensionPoint + { + // State number assigned to this suspension point. UINT_MAX if no state + // number is assigned (when AsyncInfo::NumSuspensionPoints == 1). + public uint StateNumber; + // IL offset in the root method that resulted in the creation of this suspension point. + public uint RootILOffset; + // Index of inline tree node containing the IL offset (0 for root) + public uint Inlinee; + // IL offset that resulted in the creation of the suspension point. + public uint ILOffset; + public uint NumVars; + // Count of AsyncContinuationVarInfo + public uint NumContinuationVars; + // Index into array of AsyncContinuationVarInfo of first variable + public uint ContinuationVarsIndex; + } + + public struct AsyncInfo + { + // Offset in contiuation's data where state number is stored. UINT_MAX + // if no state number is stored (when NumSuspensionPoints == 1). + public uint StateOffset; + // Number of suspension points in the method. + public uint NumSuspensionPoints; + } + // This enum is used for JIT to tell EE where this token comes from. // E.g. Depending on different opcodes, we might allow/disallow certain types of tokens or // return different types of handles (e.g. boxed vs. regular entrypoints) diff --git a/src/coreclr/tools/Common/JitInterface/ThunkGenerator/ThunkInput.txt b/src/coreclr/tools/Common/JitInterface/ThunkGenerator/ThunkInput.txt index 4c4945475399c1..7bb7bce49ffc04 100644 --- a/src/coreclr/tools/Common/JitInterface/ThunkGenerator/ThunkInput.txt +++ b/src/coreclr/tools/Common/JitInterface/ThunkGenerator/ThunkInput.txt @@ -161,6 +161,9 @@ ICorDebugInfo::NativeVarInfo*,NativeVarInfo* ICorDebugInfo::BoundaryTypes*,BoundaryTypes* ICorDebugInfo::InlineTreeNode*,InlineTreeNode* ICorDebugInfo::RichOffsetMapping*,RichOffsetMapping* +ICorDebugInfo::AsyncInfo*,AsyncInfo* +ICorDebugInfo::AsyncSuspensionPoint*,AsyncSuspensionPoint* +ICorDebugInfo::AsyncContinuationVarInfo*,AsyncContinuationVarInfo* struct _EXCEPTION_POINTERS*,_EXCEPTION_POINTERS* ICorJitInfo::errorTrapFunction,void* @@ -274,6 +277,7 @@ FUNCTIONS void getVars(CORINFO_METHOD_HANDLE ftn, uint32_t* cVars, ICorDebugInfo::ILVarInfo** vars, bool* extendOthers) void setVars(CORINFO_METHOD_HANDLE ftn, uint32_t cVars, ICorDebugInfo::NativeVarInfo* vars) void reportRichMappings(ICorDebugInfo::InlineTreeNode* inlineTreeNodes, uint32_t numInlineTreeNodes, ICorDebugInfo::RichOffsetMapping* mappings, uint32_t numMappings) + void reportAsyncDebugInfo(ICorDebugInfo::AsyncInfo* asyncInfo, ICorDebugInfo::AsyncSuspensionPoint* suspensionPoints, ICorDebugInfo::AsyncContinuationVarInfo* vars, uint32_t numVars) void reportMetadata(const char* key, const void* value, size_t length) void*allocateArray(size_t cBytes); void freeArray(void*array); diff --git a/src/coreclr/tools/aot/jitinterface/jitinterface_generated.h b/src/coreclr/tools/aot/jitinterface/jitinterface_generated.h index 4069f29308dadd..57ffa7277e9ebf 100644 --- a/src/coreclr/tools/aot/jitinterface/jitinterface_generated.h +++ b/src/coreclr/tools/aot/jitinterface/jitinterface_generated.h @@ -119,6 +119,7 @@ struct JitInterfaceCallbacks void (* getVars)(void * thisHandle, CorInfoExceptionClass** ppException, CORINFO_METHOD_HANDLE ftn, uint32_t* cVars, ICorDebugInfo::ILVarInfo** vars, bool* extendOthers); void (* setVars)(void * thisHandle, CorInfoExceptionClass** ppException, CORINFO_METHOD_HANDLE ftn, uint32_t cVars, ICorDebugInfo::NativeVarInfo* vars); void (* reportRichMappings)(void * thisHandle, CorInfoExceptionClass** ppException, ICorDebugInfo::InlineTreeNode* inlineTreeNodes, uint32_t numInlineTreeNodes, ICorDebugInfo::RichOffsetMapping* mappings, uint32_t numMappings); + void (* reportAsyncDebugInfo)(void * thisHandle, CorInfoExceptionClass** ppException, ICorDebugInfo::AsyncInfo* asyncInfo, ICorDebugInfo::AsyncSuspensionPoint* suspensionPoints, ICorDebugInfo::AsyncContinuationVarInfo* vars, uint32_t numVars); void (* reportMetadata)(void * thisHandle, CorInfoExceptionClass** ppException, const char* key, const void* value, size_t length); void* (* allocateArray)(void * thisHandle, CorInfoExceptionClass** ppException, size_t cBytes); void (* freeArray)(void * thisHandle, CorInfoExceptionClass** ppException, void* array); @@ -1257,6 +1258,17 @@ class JitInterfaceWrapper : public ICorJitInfo if (pException != nullptr) throw pException; } + virtual void reportAsyncDebugInfo( + ICorDebugInfo::AsyncInfo* asyncInfo, + ICorDebugInfo::AsyncSuspensionPoint* suspensionPoints, + ICorDebugInfo::AsyncContinuationVarInfo* vars, + uint32_t numVars) +{ + CorInfoExceptionClass* pException = nullptr; + _callbacks->reportAsyncDebugInfo(_thisHandle, &pException, asyncInfo, suspensionPoints, vars, numVars); + if (pException != nullptr) throw pException; +} + virtual void reportMetadata( const char* key, const void* value, diff --git a/src/coreclr/tools/superpmi/superpmi-shim-collector/icorjitinfo.cpp b/src/coreclr/tools/superpmi/superpmi-shim-collector/icorjitinfo.cpp index 0a6118d73ab862..7fc8560a236310 100644 --- a/src/coreclr/tools/superpmi/superpmi-shim-collector/icorjitinfo.cpp +++ b/src/coreclr/tools/superpmi/superpmi-shim-collector/icorjitinfo.cpp @@ -1233,6 +1233,16 @@ void interceptor_ICJI::reportRichMappings(ICorDebugInfo::InlineTreeNode* inli original_ICorJitInfo->reportRichMappings(inlineTreeNodes, numInlineTreeNodes, mappings, numMappings); } +void interceptor_ICJI::reportAsyncDebugInfo(ICorDebugInfo::AsyncInfo* asyncInfo, + ICorDebugInfo::AsyncSuspensionPoint* suspensionPoints, + ICorDebugInfo::AsyncContinuationVarInfo* vars, + uint32_t numVars) +{ + mc->cr->AddCall("reportAsyncDebugInfo"); + // TODO: record async debug info + original_ICorJitInfo->reportAsyncDebugInfo(asyncInfo, suspensionPoints, vars, numVars); +} + void interceptor_ICJI::reportMetadata(const char* key, const void* value, size_t length) { mc->cr->AddCall("reportMetadata"); 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 ac148df7c4e295..c06ed0b9e45e9f 100644 --- a/src/coreclr/tools/superpmi/superpmi-shim-counter/icorjitinfo_generated.cpp +++ b/src/coreclr/tools/superpmi/superpmi-shim-counter/icorjitinfo_generated.cpp @@ -876,6 +876,16 @@ void interceptor_ICJI::reportRichMappings( original_ICorJitInfo->reportRichMappings(inlineTreeNodes, numInlineTreeNodes, mappings, numMappings); } +void interceptor_ICJI::reportAsyncDebugInfo( + ICorDebugInfo::AsyncInfo* asyncInfo, + ICorDebugInfo::AsyncSuspensionPoint* suspensionPoints, + ICorDebugInfo::AsyncContinuationVarInfo* vars, + uint32_t numVars) +{ + mcs->AddCall("reportAsyncDebugInfo"); + original_ICorJitInfo->reportAsyncDebugInfo(asyncInfo, suspensionPoints, vars, numVars); +} + void interceptor_ICJI::reportMetadata( const char* key, const void* value, 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 1f47ea740c0c48..18516e58feef20 100644 --- a/src/coreclr/tools/superpmi/superpmi-shim-simple/icorjitinfo_generated.cpp +++ b/src/coreclr/tools/superpmi/superpmi-shim-simple/icorjitinfo_generated.cpp @@ -768,6 +768,15 @@ void interceptor_ICJI::reportRichMappings( original_ICorJitInfo->reportRichMappings(inlineTreeNodes, numInlineTreeNodes, mappings, numMappings); } +void interceptor_ICJI::reportAsyncDebugInfo( + ICorDebugInfo::AsyncInfo* asyncInfo, + ICorDebugInfo::AsyncSuspensionPoint* suspensionPoints, + ICorDebugInfo::AsyncContinuationVarInfo* vars, + uint32_t numVars) +{ + original_ICorJitInfo->reportAsyncDebugInfo(asyncInfo, suspensionPoints, vars, numVars); +} + void interceptor_ICJI::reportMetadata( const char* key, const void* value, diff --git a/src/coreclr/tools/superpmi/superpmi/icorjitinfo.cpp b/src/coreclr/tools/superpmi/superpmi/icorjitinfo.cpp index a1b207c3de797e..af4107d01b1079 100644 --- a/src/coreclr/tools/superpmi/superpmi/icorjitinfo.cpp +++ b/src/coreclr/tools/superpmi/superpmi/icorjitinfo.cpp @@ -1057,6 +1057,19 @@ void MyICJI::reportRichMappings( freeArray(mappings); } +void MyICJI::reportAsyncDebugInfo( + ICorDebugInfo::AsyncInfo* asyncInfo, + ICorDebugInfo::AsyncSuspensionPoint* suspensionPoints, + ICorDebugInfo::AsyncContinuationVarInfo* vars, + uint32_t numVars) +{ + jitInstance->mc->cr->AddCall("reportAsyncDebugInfo"); + // TODO: record async debug info + freeArray(asyncInfo); + freeArray(suspensionPoints); + freeArray(vars); +} + void MyICJI::reportMetadata(const char* key, const void* value, size_t length) { jitInstance->mc->cr->AddCall("reportMetadata"); diff --git a/src/coreclr/vm/jitinterface.cpp b/src/coreclr/vm/jitinterface.cpp index f31b650dc9f349..a28bb895fd481e 100644 --- a/src/coreclr/vm/jitinterface.cpp +++ b/src/coreclr/vm/jitinterface.cpp @@ -10726,6 +10726,10 @@ CEECodeGenInfo::CEECodeGenInfo(PrepareCodeConfig* config, MethodDesc* fd, COR_IL , m_numInlineTreeNodes(0) , m_richOffsetMappings(NULL) , m_numRichOffsetMappings(0) + , m_asyncInfo(NULL) + , m_asyncSuspensionPoints(NULL) + , m_asyncContinuationVars(NULL) + , m_numAsyncContinuationVars(0) , m_gphCache() { STANDARD_VM_CONTRACT; @@ -11169,6 +11173,37 @@ void CEECodeGenInfo::reportRichMappings( EE_TO_JIT_TRANSITION(); } +void CEECodeGenInfo::reportAsyncDebugInfo( + ICorDebugInfo::AsyncInfo* asyncInfo, + ICorDebugInfo::AsyncSuspensionPoint* suspensionPoints, + ICorDebugInfo::AsyncContinuationVarInfo* vars, + uint32_t numVars) +{ + CONTRACTL { + NOTHROW; + GC_NOTRIGGER; + MODE_PREEMPTIVE; + } CONTRACTL_END; + + JIT_TO_EE_TRANSITION(); + + if (((EECodeGenManager*)m_jitManager)->IsStoringRichDebugInfo()) + { + m_asyncInfo = asyncInfo; + m_asyncSuspensionPoints = suspensionPoints; + m_asyncContinuationVars = vars; + m_numAsyncContinuationVars = numVars; + } + else + { + freeArrayInternal(asyncInfo); + freeArrayInternal(suspensionPoints); + freeArrayInternal(vars); + } + + EE_TO_JIT_TRANSITION(); +} + void CEECodeGenInfo::reportMetadata( const char* key, const void* value, @@ -15014,6 +15049,16 @@ void CEEInfo::reportRichMappings( UNREACHABLE(); // only called on derived class. } +void CEEInfo::reportAsyncDebugInfo( + ICorDebugInfo::AsyncInfo* asyncInfo, + ICorDebugInfo::AsyncSuspensionPoint* suspensionPoints, + ICorDebugInfo::AsyncContinuationVarInfo* vars, + uint32_t numVars) +{ + LIMITED_METHOD_CONTRACT; + UNREACHABLE(); // only called on derived class. +} + void CEEInfo::reportMetadata(const char* key, const void* value, size_t length) { LIMITED_METHOD_CONTRACT; diff --git a/src/coreclr/vm/jitinterface.h b/src/coreclr/vm/jitinterface.h index 460c7e7f3fdd5a..11551c0bd20f71 100644 --- a/src/coreclr/vm/jitinterface.h +++ b/src/coreclr/vm/jitinterface.h @@ -531,11 +531,21 @@ class CEECodeGenInfo : public CEEInfo freeArrayInternal(m_inlineTreeNodes); if (m_richOffsetMappings != NULL) freeArrayInternal(m_richOffsetMappings); + if (m_asyncInfo != NULL) + freeArrayInternal(m_asyncInfo); + if (m_asyncSuspensionPoints != NULL) + freeArrayInternal(m_asyncSuspensionPoints); + if (m_asyncContinuationVars != NULL) + freeArrayInternal(m_asyncContinuationVars); m_inlineTreeNodes = NULL; m_numInlineTreeNodes = 0; m_richOffsetMappings = NULL; m_numRichOffsetMappings = 0; + m_asyncInfo = NULL; + m_asyncSuspensionPoints = NULL; + m_asyncContinuationVars = NULL; + m_numAsyncContinuationVars = 0; } // ICorDebugInfo stuff. @@ -562,6 +572,12 @@ class CEECodeGenInfo : public CEEInfo ICorDebugInfo::RichOffsetMapping* mappings, uint32_t numMappings) override final; + void reportAsyncDebugInfo( + ICorDebugInfo::AsyncInfo* asyncInfo, + ICorDebugInfo::AsyncSuspensionPoint* suspensionPoints, + ICorDebugInfo::AsyncContinuationVarInfo* vars, + uint32_t numVars) override final; + void reportMetadata(const char* key, const void* value, size_t length) override final; virtual void WriteCode(EECodeGenManager * jitMgr) = 0; @@ -625,6 +641,11 @@ class CEECodeGenInfo : public CEEInfo ICorDebugInfo::RichOffsetMapping *m_richOffsetMappings; ULONG32 m_numRichOffsetMappings; + ICorDebugInfo::AsyncInfo *m_asyncInfo; + ICorDebugInfo::AsyncSuspensionPoint *m_asyncSuspensionPoints; + ICorDebugInfo::AsyncContinuationVarInfo *m_asyncContinuationVars; + ULONG32 m_numAsyncContinuationVars; + // 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 // in lieu of calling into the base CEEInfo::GetProfilingHandle() again. This protects the From d7277fb8f5304f2fecc696b56b9e59436b454845 Mon Sep 17 00:00:00 2001 From: Jakob Botsch Nielsen Date: Wed, 1 Oct 2025 21:31:00 +0200 Subject: [PATCH 03/72] Add debug information for runtime async information - Add new JIT-EE API to report back debug information about the generated state machine and continuations - Refactor debug info storage on VM side to be more easily extensible. The new format has either a thin or fat header. The fat header is used when we have either uninstrumented bounds, patchpoint info, rich debug info or async debug info, and stores the blob sizes of all of those components in addition to the bounds and vars. - Add new async debug information to the storage on the VM side - Set get target method desc for async resumption stubs, to be used for mapping from continuations back to the async IL function that it will resume. --- src/coreclr/inc/cordebuginfo.h | 11 +- src/coreclr/inc/corinfo.h | 4 +- src/coreclr/jit/async.cpp | 74 +++ src/coreclr/jit/async.h | 9 + src/coreclr/jit/compiler.h | 2 +- src/coreclr/jit/gentree.h | 3 + src/coreclr/jit/importercalls.cpp | 11 +- .../tools/Common/JitInterface/CorInfoTypes.cs | 3 - .../tools/superpmi/superpmi/icorjitinfo.cpp | 4 +- src/coreclr/vm/codeman.cpp | 30 +- src/coreclr/vm/debuginfostore.cpp | 422 +++++++++--------- src/coreclr/vm/debuginfostore.h | 59 ++- src/coreclr/vm/jitinterface.cpp | 37 +- src/coreclr/vm/jitinterface.h | 21 +- 14 files changed, 385 insertions(+), 305 deletions(-) diff --git a/src/coreclr/inc/cordebuginfo.h b/src/coreclr/inc/cordebuginfo.h index 03fe0d9d463cd6..a90a0289df50f1 100644 --- a/src/coreclr/inc/cordebuginfo.h +++ b/src/coreclr/inc/cordebuginfo.h @@ -442,27 +442,18 @@ class ICorDebugInfo struct AsyncSuspensionPoint { - // State number assigned to this suspension point. UINT_MAX if no state - // number is assigned (when AsyncInfo::NumSuspensionPoints == 1). - uint32_t StateNumber; // IL offset in the root method that resulted in the creation of this suspension point. uint32_t RootILOffset; // Index of inline tree node containing the IL offset (0 for root) uint32_t Inlinee; // IL offset that resulted in the creation of the suspension point. uint32_t ILOffset; - uint32_t NumVars; - // Count of AsyncContinuationVarInfo + // Count of AsyncContinuationVarInfo in array of locals uint32_t NumContinuationVars; - // Index into array of AsyncContinuationVarInfo of first variable - uint32_t ContinuationVarsIndex; }; struct AsyncInfo { - // Offset in contiuation's data where state number is stored. UINT_MAX - // if no state number is stored (when NumSuspensionPoints == 1). - uint32_t StateOffset; // Number of suspension points in the method. uint32_t NumSuspensionPoints; }; diff --git a/src/coreclr/inc/corinfo.h b/src/coreclr/inc/corinfo.h index 528b12f801fdb8..36b4ab4925f0f9 100644 --- a/src/coreclr/inc/corinfo.h +++ b/src/coreclr/inc/corinfo.h @@ -2917,8 +2917,8 @@ class ICorStaticInfo // and ownership is transferred to the EE with this call. virtual void reportAsyncDebugInfo( ICorDebugInfo::AsyncInfo* asyncInfo, // [IN] Async method information - ICorDebugInfo::AsyncSuspensionPoint* suspensionPoints, // [IN] Async suspension points - ICorDebugInfo::AsyncContinuationVarInfo* vars, // [IN] Async continuation variable info + ICorDebugInfo::AsyncSuspensionPoint* suspensionPoints, // [IN] Array of async suspension points, indexed by state number + ICorDebugInfo::AsyncContinuationVarInfo* vars, // [IN] Array of async continuation variable info uint32_t numVars // [IN] Number of continuation variables ) = 0; diff --git a/src/coreclr/jit/async.cpp b/src/coreclr/jit/async.cpp index ec92f9b23ca6a7..76288114c9866b 100644 --- a/src/coreclr/jit/async.cpp +++ b/src/coreclr/jit/async.cpp @@ -760,6 +760,8 @@ PhaseStatus AsyncTransformation::Run() m_comp->fgInvalidateDfsTree(); + ReportDebugInfo(); + return PhaseStatus::MODIFIED_EVERYTHING; } @@ -818,6 +820,8 @@ void AsyncTransformation::Transform( BasicBlock* resumeBB = CreateResumption(block, *remainder, call, callDefInfo, stateNum, layout); m_resumptionBBs.push_back(resumeBB); + + CreateDebugInfoForSuspensionPoint(call, layout); } //------------------------------------------------------------------------ @@ -2202,6 +2206,76 @@ GenTreeStoreInd* AsyncTransformation::StoreAtOffset(GenTree* base, unsigned offs return store; } +//------------------------------------------------------------------------ +// AsyncTransformation::CreateDebugInfoForSuspensionPoint: +// Create debug info for the specific suspension point we just created. +// +// Parameters: +// asyncCall - Call node resulting in the suspension point +// stateNum - State number that was assigned to the suspension point +// layout - Layout of continuation +// +void AsyncTransformation::CreateDebugInfoForSuspensionPoint(GenTreeCall* asyncCall, const ContinuationLayout& layout) +{ + if (!m_comp->opts.compDbgInfo) + { + return; + } + + uint32_t numLocals = 0; + for (const LiveLocalInfo& local : layout.Locals) + { + unsigned ilVarNum = m_comp->compMap2ILvarNum(local.LclNum); + if (ilVarNum == ICorDebugInfo::UNKNOWN_ILNUM) + { + continue; + } + + ICorDebugInfo::AsyncContinuationVarInfo varInf; + varInf.VarNumber = ilVarNum; + varInf.Offset = local.DataOffset; + m_dbgContinuationVars.push_back(varInf); + numLocals++; + } + + ICorDebugInfo::AsyncSuspensionPoint suspensionPoint; + const DebugInfo& di = asyncCall->GetAsyncInfo().DebugInfo; + suspensionPoint.RootILOffset = di.GetRoot().GetLocation().GetOffset(); + suspensionPoint.Inlinee = di.GetInlineContext()->GetOrdinal(); + suspensionPoint.ILOffset = di.GetLocation().GetOffset(); + suspensionPoint.NumContinuationVars = numLocals; + + m_dbgSuspensionPoints.push_back(suspensionPoint); +} + +//------------------------------------------------------------------------ +// AsyncTransformation::ReportDebugInfo: +// Report debug info back to EE. +// +void AsyncTransformation::ReportDebugInfo() +{ + if (!m_comp->opts.compDbgInfo) + { + return; + } + + ICorDebugInfo::AsyncInfo asyncInfo; + asyncInfo.NumSuspensionPoints = static_cast(m_dbgSuspensionPoints.size()); + + ICorDebugInfo::AsyncSuspensionPoint* hostSuspensionPoints = + static_cast(m_comp->info.compCompHnd->allocateArray( + m_dbgSuspensionPoints.size() * sizeof(ICorDebugInfo::AsyncSuspensionPoint))); + std::copy(m_dbgSuspensionPoints.begin(), m_dbgSuspensionPoints.end(), hostSuspensionPoints); + + ICorDebugInfo::AsyncContinuationVarInfo* hostVars = + static_cast(m_comp->info.compCompHnd->allocateArray( + m_dbgContinuationVars.size() * sizeof(ICorDebugInfo::AsyncContinuationVarInfo))); + std::copy(m_dbgContinuationVars.begin(), m_dbgContinuationVars.end(), hostVars); + + m_comp->info.compCompHnd->reportAsyncDebugInfo(&asyncInfo, hostSuspensionPoints, hostVars, + static_cast(m_dbgContinuationVars.size())); +} + //------------------------------------------------------------------------ // AsyncTransformation::GetDataArrayVar: // Create a new local to hold the data array of the continuation object. This diff --git a/src/coreclr/jit/async.h b/src/coreclr/jit/async.h index e30aaf760e6395..b8c0459ef9055e 100644 --- a/src/coreclr/jit/async.h +++ b/src/coreclr/jit/async.h @@ -63,6 +63,10 @@ class AsyncTransformation BasicBlock* m_lastResumptionBB = nullptr; BasicBlock* m_sharedReturnBB = nullptr; + // Debug info + jitstd::vector m_dbgContinuationVars; + jitstd::vector m_dbgSuspensionPoints; + bool IsLive(unsigned lclNum); void Transform(BasicBlock* block, GenTreeCall* call, @@ -132,6 +136,9 @@ class AsyncTransformation GenTreeFlags indirFlags = GTF_IND_NONFAULTING); GenTreeStoreInd* StoreAtOffset(GenTree* base, unsigned offset, GenTree* value, var_types storeType); + void CreateDebugInfoForSuspensionPoint(GenTreeCall* asyncCall, const ContinuationLayout& layout); + void ReportDebugInfo(); + unsigned GetDataArrayVar(); unsigned GetGCDataArrayVar(); unsigned GetResultBaseVar(); @@ -147,6 +154,8 @@ class AsyncTransformation : m_comp(comp) , m_liveLocalsScratch(comp->getAllocator(CMK_Async)) , m_resumptionBBs(comp->getAllocator(CMK_Async)) + , m_dbgContinuationVars(comp->getAllocator(CMK_Async)) + , m_dbgSuspensionPoints(comp->getAllocator(CMK_Async)) { } diff --git a/src/coreclr/jit/compiler.h b/src/coreclr/jit/compiler.h index 134d236f78a108..263111e04ca82f 100644 --- a/src/coreclr/jit/compiler.h +++ b/src/coreclr/jit/compiler.h @@ -4601,7 +4601,7 @@ class Compiler CORINFO_CALL_INFO* callInfo, IL_OFFSET rawILOffset); - void impSetupAndSpillForAsyncCall(GenTreeCall* call, OPCODE opcode, unsigned prefixFlags); + void impSetupAndSpillForAsyncCall(GenTreeCall* call, OPCODE opcode, unsigned prefixFlags, const DebugInfo& callInstDI); void impInsertAsyncContinuationForLdvirtftnCall(GenTreeCall* call); diff --git a/src/coreclr/jit/gentree.h b/src/coreclr/jit/gentree.h index b76a27879a4a7e..d348b04f95f099 100644 --- a/src/coreclr/jit/gentree.h +++ b/src/coreclr/jit/gentree.h @@ -4400,6 +4400,9 @@ struct AsyncCallInfo bool SaveAndRestoreSynchronizationContextField = false; bool HasSuspensionIndicatorDef = false; unsigned SynchronizationContextLclNum = BAD_VAR_NUM; + + // Exact debug info of call IL instruction + DebugInfo DebugInfo; }; // Return type descriptor of a GT_CALL node. diff --git a/src/coreclr/jit/importercalls.cpp b/src/coreclr/jit/importercalls.cpp index b5f22d85d10dd4..1ad1bad152a0d7 100644 --- a/src/coreclr/jit/importercalls.cpp +++ b/src/coreclr/jit/importercalls.cpp @@ -386,7 +386,7 @@ var_types Compiler::impImportCall(OPCODE opcode, if (sig->isAsyncCall()) { - impSetupAndSpillForAsyncCall(call->AsCall(), opcode, prefixFlags); + impSetupAndSpillForAsyncCall(call->AsCall(), opcode, prefixFlags, di); } impPopCallArgs(sig, call->AsCall()); @@ -691,7 +691,7 @@ var_types Compiler::impImportCall(OPCODE opcode, if (sig->isAsyncCall()) { - impSetupAndSpillForAsyncCall(call->AsCall(), opcode, prefixFlags); + impSetupAndSpillForAsyncCall(call->AsCall(), opcode, prefixFlags, di); } // Now create the argument list. @@ -6800,10 +6800,15 @@ void Compiler::impCheckForPInvokeCall( // call - The call // opcode - The IL opcode for the call // prefixFlags - Flags containing context handling information from IL +// callInstDI - Debug info for the exact call instruction // -void Compiler::impSetupAndSpillForAsyncCall(GenTreeCall* call, OPCODE opcode, unsigned prefixFlags) +void Compiler::impSetupAndSpillForAsyncCall(GenTreeCall* call, + OPCODE opcode, + unsigned prefixFlags, + const DebugInfo& callInstDI) { AsyncCallInfo asyncInfo; + asyncInfo.DebugInfo = callInstDI; if ((prefixFlags & PREFIX_IS_TASK_AWAIT) != 0) { diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs b/src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs index e7e11c79095109..1b9ed8c69cf8de 100644 --- a/src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs +++ b/src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs @@ -1347,9 +1347,6 @@ public struct AsyncSuspensionPoint public struct AsyncInfo { - // Offset in contiuation's data where state number is stored. UINT_MAX - // if no state number is stored (when NumSuspensionPoints == 1). - public uint StateOffset; // Number of suspension points in the method. public uint NumSuspensionPoints; } diff --git a/src/coreclr/tools/superpmi/superpmi/icorjitinfo.cpp b/src/coreclr/tools/superpmi/superpmi/icorjitinfo.cpp index af4107d01b1079..996e417a50e43d 100644 --- a/src/coreclr/tools/superpmi/superpmi/icorjitinfo.cpp +++ b/src/coreclr/tools/superpmi/superpmi/icorjitinfo.cpp @@ -1052,7 +1052,7 @@ void MyICJI::reportRichMappings( uint32_t numMappings) { jitInstance->mc->cr->AddCall("reportRichMappings"); - // TODO: record these mappings + // Compile output that we do not currently save freeArray(inlineTreeNodes); freeArray(mappings); } @@ -1064,7 +1064,7 @@ void MyICJI::reportAsyncDebugInfo( uint32_t numVars) { jitInstance->mc->cr->AddCall("reportAsyncDebugInfo"); - // TODO: record async debug info + // Compile output that we do not currently save freeArray(asyncInfo); freeArray(suspensionPoints); freeArray(vars); diff --git a/src/coreclr/vm/codeman.cpp b/src/coreclr/vm/codeman.cpp index fae188a904282d..18a2559db515f9 100644 --- a/src/coreclr/vm/codeman.cpp +++ b/src/coreclr/vm/codeman.cpp @@ -3963,17 +3963,6 @@ BOOL EECodeGenManager::GetBoundariesAndVarsWorker( if (pDebugInfo == NULL) return FALSE; -#ifdef FEATURE_ON_STACK_REPLACEMENT - BOOL hasFlagByte = TRUE; -#else - BOOL hasFlagByte = FALSE; -#endif - - if (m_storeRichDebugInfo) - { - hasFlagByte = TRUE; - } - // Uncompress. This allocates memory and may throw. CompressDebugInfo::RestoreBoundariesAndVars( fpNew, @@ -3981,8 +3970,7 @@ BOOL EECodeGenManager::GetBoundariesAndVarsWorker( boundsType, pDebugInfo, // input pcMap, ppMap, // output - pcVars, ppVars, // output - hasFlagByte + pcVars, ppVars // output ); return TRUE; @@ -4047,22 +4035,10 @@ size_t EECodeGenManager::WalkILOffsetsWorker(PTR_BYTE pDebugInfo, if (pDebugInfo == NULL) return 0; -#ifdef FEATURE_ON_STACK_REPLACEMENT - BOOL hasFlagByte = TRUE; -#else - BOOL hasFlagByte = FALSE; -#endif - - if (m_storeRichDebugInfo) - { - hasFlagByte = TRUE; - } - // Uncompress. This allocates memory and may throw. return CompressDebugInfo::WalkILOffsets( pDebugInfo, // input boundsType, - hasFlagByte, pContext, pfnWalkILOffsets ); @@ -6442,8 +6418,7 @@ BOOL ReadyToRunJitManager::GetBoundariesAndVars( boundsType, pDebugInfo, // input pcMap, ppMap, // output - pcVars, ppVars, // output - FALSE); // no patchpoint info + pcVars, ppVars); // output return TRUE; } @@ -6475,7 +6450,6 @@ size_t ReadyToRunJitManager::WalkILOffsets( return CompressDebugInfo::WalkILOffsets( pDebugInfo, // input boundsType, - FALSE, // no patchpoint info pContext, pfnWalkILOffsets); } diff --git a/src/coreclr/vm/debuginfostore.cpp b/src/coreclr/vm/debuginfostore.cpp index 2687329aea9862..f97483662f9cd8 100644 --- a/src/coreclr/vm/debuginfostore.cpp +++ b/src/coreclr/vm/debuginfostore.cpp @@ -312,6 +312,9 @@ static int g_CDI_bVarsTotalCompress = 0; static int g_CDI_bRichDebugInfoTotalUncompress = 0; static int g_CDI_bRichDebugInfoTotalCompress = 0; + +static int g_CDI_bAsyncDebugInfoTotalUncompress = 0; +static int g_CDI_bAsyncDebugInfoTotalCompress = 0; #endif // Helper to write a compressed Native Var Info @@ -457,12 +460,60 @@ static void DoRichOffsetMappings( } } +template +static void DoAsyncSuspensionPoints( + T trans, + ULONG32 cSuspensionPoints, + ICorDebugInfo::AsyncSuspensionPoint* suspensionPoints) +{ + // Loop through and transfer each Entry in the Mapping. + uint32_t lastRootILOffset = 0; + uint32_t lastInlinee = 0; + uint32_t lastILOffset = 0; + for (uint32_t i = 0; i < cSuspensionPoints; i++) + { + ICorDebugInfo::AsyncSuspensionPoint* sp = &suspensionPoints[i]; + + trans.DoEncodedDeltaU32NonMonotonic(sp->RootILOffset, lastRootILOffset); + lastRootILOffset = sp->RootILOffset; + + trans.DoEncodedDeltaU32NonMonotonic(sp->Inlinee, lastInlinee); + lastInlinee = sp->Inlinee; + + trans.DoEncodedDeltaU32NonMonotonic(sp->ILOffset, lastILOffset); + lastILOffset = sp->ILOffset; + + trans.DoEncodedU32(sp->NumContinuationVars); + } +} + +template +static void DoAsyncVars( + T trans, + ULONG32 cVars, + ICorDebugInfo::AsyncContinuationVarInfo* vars) +{ + uint32_t lastOffset = 0; + for (uint32_t i = 0; i < cVars; i++) + { + ICorDebugInfo::AsyncContinuationVarInfo* var = &vars[i]; + + trans.DoEncodedAdjustedU32(var->VarNumber, (DWORD) ICorDebugInfo::MAX_ILNUM); + + trans.DoEncodedDeltaU32(var->Offset, lastOffset); + lastOffset = var->Offset; + } +} + enum EXTRA_DEBUG_INFO_FLAGS { // Debug info contains patchpoint information EXTRA_DEBUG_INFO_PATCHPOINT = 1, // Debug info contains rich information EXTRA_DEBUG_INFO_RICH = 2, + // Debug info contains async information + EXTRA_DEBUG_INFO_ASYNC = 4, + EXTRA_DEBUG_INFO_UNINSTRUMENTED_BOUNDS = 8, }; #ifndef DACCESS_COMPILE @@ -694,7 +745,42 @@ void CompressDebugInfo::CompressRichDebugInfo( PVOID pBlob = pWriter->GetBlob(&cbBlob); g_CDI_bRichDebugInfoTotalUncompress += 8 + cInlineTree * sizeof(ICorDebugInfo::InlineTreeNode) + cRichOffsetMappings * sizeof(ICorDebugInfo::RichOffsetMapping); - g_CDI_bRichDebugInfoTotalCompress += 4 + cbBlob; + g_CDI_bRichDebugInfoTotalCompress += cbBlob; +#endif +} + +void CompressDebugInfo::CompressAsyncDebugInfo( + IN ICorDebugInfo::AsyncInfo* asyncInfo, + IN ICorDebugInfo::AsyncSuspensionPoint* pSuspensionPoints, + IN ICorDebugInfo::AsyncContinuationVarInfo* pAsyncVars, + IN ULONG iAsyncVars, + IN OUT NibbleWriter* pWriter) +{ + CONTRACTL + { + THROWS; + GC_NOTRIGGER; + MODE_ANY; + } + CONTRACTL_END; + + _ASSERTE(pWriter != NULL); + _ASSERTE((asyncInfo->NumSuspensionPoints > 0) && (pSuspensionPoints != NULL)); + pWriter->WriteEncodedU32(asyncInfo->NumSuspensionPoints); + pWriter->WriteEncodedU32(iAsyncVars); + + TransferWriter t(*pWriter); + DoAsyncSuspensionPoints(t, asyncInfo->NumSuspensionPoints, pSuspensionPoints); + DoAsyncVars(t, iAsyncVars, pAsyncVars); + + pWriter->Flush(); + +#ifdef _DEBUG + DWORD cbBlob; + PVOID pBlob = pWriter->GetBlob(&cbBlob); + + g_CDI_bAsyncDebugInfoTotalUncompress += 8 + asyncInfo->NumSuspensionPoints * sizeof(ICorDebugInfo::AsyncSuspensionPoint) + iAsyncVars * sizeof(ICorDebugInfo::AsyncContinuationVarInfo); + g_CDI_bAsyncDebugInfoTotalCompress += cbBlob; #endif } @@ -810,19 +896,22 @@ static void ComposeMapping(const InstrumentedILOffsetMapping * pProfilerILMap, I } } -PTR_BYTE CompressDebugInfo::CompressBoundariesAndVars( - IN ICorDebugInfo::OffsetMapping* pOffsetMapping, - IN ULONG iOffsetMapping, - const InstrumentedILOffsetMapping * pInstrumentedILBounds, - IN ICorDebugInfo::NativeVarInfo* pNativeVarInfo, - IN ULONG iNativeVarInfo, - IN PatchpointInfo* patchpointInfo, - IN ICorDebugInfo::InlineTreeNode* pInlineTree, - IN ULONG iInlineTree, - IN ICorDebugInfo::RichOffsetMapping* pRichOffsetMappings, - IN ULONG iRichOffsetMappings, - IN BOOL writeFlagByte, - IN LoaderHeap* pLoaderHeap +PTR_BYTE CompressDebugInfo::Compress( + IN ICorDebugInfo::OffsetMapping* pOffsetMapping, + IN ULONG iOffsetMapping, + const InstrumentedILOffsetMapping * pInstrumentedILBounds, + IN ICorDebugInfo::NativeVarInfo* pNativeVarInfo, + IN ULONG iNativeVarInfo, + IN PatchpointInfo* patchpointInfo, + IN ICorDebugInfo::InlineTreeNode* pInlineTree, + IN ULONG iInlineTree, + IN ICorDebugInfo::RichOffsetMapping* pRichOffsetMappings, + IN ULONG iRichOffsetMappings, + IN ICorDebugInfo::AsyncInfo* asyncInfo, + IN ICorDebugInfo::AsyncSuspensionPoint* pSuspensionPoints, + IN ICorDebugInfo::AsyncContinuationVarInfo* pAsyncVars, + IN ULONG iAsyncVars, + IN LoaderHeap* pLoaderHeap ) { CONTRACTL { @@ -831,7 +920,8 @@ PTR_BYTE CompressDebugInfo::CompressBoundariesAndVars( PRECONDITION((iNativeVarInfo == 0) == (pNativeVarInfo == NULL)); PRECONDITION((iInlineTree == 0) || (pInlineTree != NULL)); PRECONDITION((iRichOffsetMappings == 0) || (pRichOffsetMappings != NULL)); - PRECONDITION(writeFlagByte || ((patchpointInfo == NULL) && (iInlineTree == 0) && (iRichOffsetMappings == 0))); + PRECONDITION((asyncInfo->NumSuspensionPoints == 0) || (pSuspensionPoints != NULL)); + PRECONDITION((iAsyncVars == 0) || (pAsyncVars != NULL)); PRECONDITION(pLoaderHeap != NULL); } CONTRACTL_END; @@ -892,31 +982,53 @@ PTR_BYTE CompressDebugInfo::CompressBoundariesAndVars( pRichDebugInfo = richDebugInfoBuffer.GetBlob(&cbRichDebugInfo); } + NibbleWriter asyncInfoBuffer; + DWORD cbAsyncInfo = 0; + PVOID pAsyncInfoBlob = NULL; + if (asyncInfo->NumSuspensionPoints > 0) + { + CompressDebugInfo::CompressAsyncDebugInfo(asyncInfo, pSuspensionPoints, pAsyncVars, iAsyncVars, &asyncInfoBuffer); + pAsyncInfoBlob = asyncInfoBuffer.GetBlob(&cbAsyncInfo); + } + // Now write it all out to the buffer in a compact fashion. NibbleWriter w; - if (cbUninstrumentedBounds != 0) + + bool isFat = + (cbPatchpointInfo > 0) || + (cbRichDebugInfo > 0) || + (cbAsyncInfo > 0) || + (cbUninstrumentedBounds > 0); + + if (isFat) { - w.WriteEncodedU32(DebugInfoBoundsHasInstrumentedBounds); // 0xFFFFFFFF is used to indicate that the instrumented bounds are present. + w.WriteEncodedU32(DebugInfoFat); w.WriteEncodedU32(cbBounds); + w.WriteEncodedU32(cbVars); w.WriteEncodedU32(cbUninstrumentedBounds); + w.WriteEncodedU32(cbPatchpointInfo); + w.WriteEncodedU32(cbRichDebugInfo); + w.WriteEncodedU32(cbAsyncInfo); } else { w.WriteEncodedU32(cbBounds); + w.WriteEncodedU32(cbVars); } - w.WriteEncodedU32(cbVars); + w.Flush(); DWORD cbHeader; PVOID pHeader = w.GetBlob(&cbHeader); S_UINT32 cbFinalSize(0); - if (writeFlagByte) - cbFinalSize += 1; - + cbFinalSize += cbHeader; + cbFinalSize += cbBounds; + cbFinalSize += cbVars; + cbFinalSize += cbUninstrumentedBounds; cbFinalSize += cbPatchpointInfo; - cbFinalSize += S_UINT32(4) + S_UINT32(cbRichDebugInfo); - cbFinalSize += S_UINT32(cbHeader) + S_UINT32(cbBounds) + S_UINT32(cbUninstrumentedBounds) + S_UINT32(cbVars); + cbFinalSize += cbRichDebugInfo; + cbFinalSize += cbAsyncInfo; if (cbFinalSize.IsOverflow()) ThrowHR(COR_E_OVERFLOW); @@ -924,29 +1036,6 @@ PTR_BYTE CompressDebugInfo::CompressBoundariesAndVars( BYTE *ptrStart = (BYTE *)(void *)pLoaderHeap->AllocMem(S_SIZE_T(cbFinalSize.Value())); BYTE *ptr = ptrStart; - if (writeFlagByte) - { - BYTE flagByte = 0; - if (cbPatchpointInfo > 0) - flagByte |= EXTRA_DEBUG_INFO_PATCHPOINT; - if (cbRichDebugInfo > 0) - flagByte |= EXTRA_DEBUG_INFO_RICH; - - *ptr++ = flagByte; - } - - if (cbPatchpointInfo > 0) - memcpy(ptr, (BYTE*) patchpointInfo, cbPatchpointInfo); - ptr += cbPatchpointInfo; - - if (cbRichDebugInfo > 0) - { - memcpy(ptr, &cbRichDebugInfo, 4); - ptr += 4; - memcpy(ptr, pRichDebugInfo, cbRichDebugInfo); - ptr += cbRichDebugInfo; - } - memcpy(ptr, pHeader, cbHeader); ptr += cbHeader; @@ -954,13 +1043,25 @@ PTR_BYTE CompressDebugInfo::CompressBoundariesAndVars( memcpy(ptr, pBounds, cbBounds); ptr += cbBounds; + if (cbVars > 0) + memcpy(ptr, pVars, cbVars); + ptr += cbVars; + if (cbUninstrumentedBounds > 0) memcpy(ptr, pUninstrumentedBounds, cbUninstrumentedBounds); ptr += cbUninstrumentedBounds; - if (cbVars > 0) - memcpy(ptr, pVars, cbVars); - ptr += cbVars; + if (cbPatchpointInfo > 0) + memcpy(ptr, (BYTE*) patchpointInfo, cbPatchpointInfo); + ptr += cbPatchpointInfo; + + if (cbRichDebugInfo > 0) + memcpy(ptr, pRichDebugInfo, cbRichDebugInfo); + ptr += cbRichDebugInfo; + + if (cbAsyncInfo > 0) + memcpy(ptr, pAsyncInfoBlob, cbAsyncInfo); + ptr += cbAsyncInfo; #ifdef _DEBUG ULONG32 cNewBounds = 0; @@ -968,7 +1069,7 @@ PTR_BYTE CompressDebugInfo::CompressBoundariesAndVars( ICorDebugInfo::OffsetMapping *pNewMap = NULL; ICorDebugInfo::NativeVarInfo *pNewVars = NULL; RestoreBoundariesAndVars( - DecompressNew, NULL, BoundsType::Instrumented, ptrStart, &cNewBounds, &pNewMap, &cNewVars, &pNewVars, writeFlagByte); + DecompressNew, NULL, BoundsType::Instrumented, ptrStart, &cNewBounds, &pNewMap, &cNewVars, &pNewVars); _ASSERTE(cNewBounds == iOffsetMapping); _ASSERTE(cNewBounds == 0 || pNewMap != NULL); @@ -1060,6 +1161,53 @@ static void DoBounds(PTR_BYTE addrBounds, uint32_t cbBounds, TNumBounds countHan // Uncompression (restore) routines //----------------------------------------------------------------------------- +DebugInfoChunks CompressDebugInfo::Restore(IN PTR_BYTE pDebugInfo) +{ + CONTRACTL + { + THROWS; // reading from nibble stream may throw on invalid data. + GC_NOTRIGGER; + MODE_ANY; + SUPPORTS_DAC; + } + CONTRACTL_END; + + NibbleReader r(pDebugInfo, 42 /* maximum size of compressed 7 UINT32s */); + + ULONG cbBoundsOrFatMarker = r.ReadEncodedU32(); + + DebugInfoChunks chunks; + + if (cbBoundsOrFatMarker == DebugInfoFat) + { + // Fat header + chunks.cbBounds = r.ReadEncodedU32(); + chunks.cbVars = r.ReadEncodedU32(); + chunks.cbUninstrumentedBounds = r.ReadEncodedU32(); + chunks.cbPatchpointInfo = r.ReadEncodedU32(); + chunks.cbRichDebugInfo = r.ReadEncodedU32(); + chunks.cbAsyncInfo = r.ReadEncodedU32(); + } + else + { + chunks.cbBounds = cbBoundsOrFatMarker; + chunks.cbVars = r.ReadEncodedU32(); + chunks.cbUninstrumentedBounds = 0; + chunks.cbPatchpointInfo = 0; + chunks.cbRichDebugInfo = 0; + chunks.cbAsyncInfo = 0; + } + + chunks.pBounds = pDebugInfo + r.GetNextByteIndex(); + chunks.pVars = chunks.pBounds + chunks.cbBounds; + chunks.pUninstrumentedBounds = chunks.pVars + chunks.cbVars; + chunks.pPatchpointInfo = chunks.pUninstrumentedBounds + chunks.cbUninstrumentedBounds; + chunks.pRichDebugInfo = chunks.pPatchpointInfo + chunks.cbPatchpointInfo; + chunks.pAsyncInfo = chunks.pRichDebugInfo + chunks.cbRichDebugInfo; + chunks.pEnd = chunks.pAsyncInfo + chunks.cbAsyncInfo; + return chunks; +} + // Uncompress data supplied by Compress functions. void CompressDebugInfo::RestoreBoundariesAndVars( IN FP_IDS_NEW fpNew, @@ -1069,8 +1217,7 @@ void CompressDebugInfo::RestoreBoundariesAndVars( OUT ULONG32 * pcMap, // number of entries in ppMap OUT ICorDebugInfo::OffsetMapping **ppMap, // pointer to newly allocated array OUT ULONG32 *pcVars, - OUT ICorDebugInfo::NativeVarInfo **ppVars, - BOOL hasFlagByte + OUT ICorDebugInfo::NativeVarInfo **ppVars ) { CONTRACTL @@ -1087,50 +1234,16 @@ void CompressDebugInfo::RestoreBoundariesAndVars( if (pcVars != NULL) *pcVars = 0; if (ppVars != NULL) *ppVars = NULL; - if (hasFlagByte) - { - // Check flag byte and skip over any patchpoint info - BYTE flagByte = *pDebugInfo; - pDebugInfo++; - - if ((flagByte & EXTRA_DEBUG_INFO_PATCHPOINT) != 0) - { - PTR_PatchpointInfo patchpointInfo = dac_cast(pDebugInfo); - pDebugInfo += patchpointInfo->PatchpointInfoSize(); - flagByte &= ~EXTRA_DEBUG_INFO_PATCHPOINT; - } - - if ((flagByte & EXTRA_DEBUG_INFO_RICH) != 0) - { - UINT32 cbRichDebugInfo = *PTR_UINT32(pDebugInfo); - pDebugInfo += 4; - pDebugInfo += cbRichDebugInfo; - flagByte &= ~EXTRA_DEBUG_INFO_RICH; - } - - _ASSERTE(flagByte == 0); - } - - NibbleReader r(pDebugInfo, 24 /* maximum size of compressed 4 UINT32s */); - - ULONG cbBounds = r.ReadEncodedU32(); - ULONG cbUninstrumentedBounds = 0; - if (cbBounds == DebugInfoBoundsHasInstrumentedBounds) - { - // This means we have instrumented bounds. - cbBounds = r.ReadEncodedU32(); - cbUninstrumentedBounds = r.ReadEncodedU32(); - } - ULONG cbVars = r.ReadEncodedU32(); + DebugInfoChunks chunks = Restore(pDebugInfo); - PTR_BYTE addrBounds = pDebugInfo + r.GetNextByteIndex(); - PTR_BYTE addrVars = addrBounds + cbBounds + cbUninstrumentedBounds; + PTR_BYTE addrBounds = chunks.pBounds; + unsigned cbBounds = chunks.cbBounds; - if ((boundsType == BoundsType::Uninstrumented) && cbUninstrumentedBounds != 0) + if ((boundsType == BoundsType::Uninstrumented) && chunks.cbUninstrumentedBounds != 0) { // If we have uninstrumented bounds, we will use them instead of the regular bounds. - addrBounds = addrBounds + cbBounds; - cbBounds = cbUninstrumentedBounds; + addrBounds = chunks.pUninstrumentedBounds; + cbBounds = chunks.cbUninstrumentedBounds; } if ((pcMap != NULL || ppMap != NULL) && (cbBounds != 0)) @@ -1162,9 +1275,9 @@ void CompressDebugInfo::RestoreBoundariesAndVars( }); } - if ((pcVars != NULL || ppVars != NULL) && (cbVars != 0)) + if ((pcVars != NULL || ppVars != NULL) && (chunks.cbVars != 0)) { - NibbleReader r(addrVars, cbVars); + NibbleReader r(chunks.pVars, chunks.cbVars); TransferReader t(r); UINT32 cNumEntries = r.ReadEncodedU32(); @@ -1194,7 +1307,6 @@ void CompressDebugInfo::RestoreBoundariesAndVars( size_t CompressDebugInfo::WalkILOffsets( IN PTR_BYTE pDebugInfo, BoundsType boundsType, - BOOL hasFlagByte, void* pContext, size_t (* pfnWalkILOffsets)(ICorDebugInfo::OffsetMapping *pOffsetMapping, void *pContext) ) @@ -1208,50 +1320,16 @@ size_t CompressDebugInfo::WalkILOffsets( } CONTRACTL_END; - if (hasFlagByte) - { - // Check flag byte and skip over any patchpoint info - BYTE flagByte = *pDebugInfo; - pDebugInfo++; - - if ((flagByte & EXTRA_DEBUG_INFO_PATCHPOINT) != 0) - { - PTR_PatchpointInfo patchpointInfo = dac_cast(pDebugInfo); - pDebugInfo += patchpointInfo->PatchpointInfoSize(); - flagByte &= ~EXTRA_DEBUG_INFO_PATCHPOINT; - } - - if ((flagByte & EXTRA_DEBUG_INFO_RICH) != 0) - { - UINT32 cbRichDebugInfo = *PTR_UINT32(pDebugInfo); - pDebugInfo += 4; - pDebugInfo += cbRichDebugInfo; - flagByte &= ~EXTRA_DEBUG_INFO_RICH; - } - - _ASSERTE(flagByte == 0); - } + DebugInfoChunks chunks = Restore(pDebugInfo); - NibbleReader r(pDebugInfo, 24 /* maximum size of compressed 4 UINT32s */); + PTR_BYTE addrBounds = chunks.pBounds; + unsigned cbBounds = chunks.cbBounds; - ULONG cbBounds = r.ReadEncodedU32_NoThrow(); - ULONG cbUninstrumentedBounds = 0; - if (cbBounds == DebugInfoBoundsHasInstrumentedBounds) - { - // This means we have instrumented bounds. - cbBounds = r.ReadEncodedU32(); - cbUninstrumentedBounds = r.ReadEncodedU32(); - } - ULONG cbVars = r.ReadEncodedU32_NoThrow(); - - PTR_BYTE addrBounds = pDebugInfo + r.GetNextByteIndex(); - PTR_BYTE addrVars = addrBounds + cbBounds + cbUninstrumentedBounds; - - if ((boundsType == BoundsType::Uninstrumented) && cbUninstrumentedBounds != 0) + if ((boundsType == BoundsType::Uninstrumented) && chunks.cbUninstrumentedBounds != 0) { // If we have uninstrumented bounds, we will use them instead of the regular bounds. - addrBounds = addrBounds + cbBounds; - cbBounds = cbUninstrumentedBounds; + addrBounds = chunks.pUninstrumentedBounds; + cbBounds = chunks.cbUninstrumentedBounds; } if (cbBounds != 0) @@ -1298,14 +1376,12 @@ PatchpointInfo * CompressDebugInfo::RestorePatchpointInfo(IN PTR_BYTE pDebugInfo } CONTRACTL_END; - // Check flag byte. - BYTE flagByte = *pDebugInfo; - pDebugInfo++; + DebugInfoChunks chunks = Restore(pDebugInfo); - if ((flagByte & EXTRA_DEBUG_INFO_PATCHPOINT) == 0) + if (chunks.cbPatchpointInfo == 0) return NULL; - return static_cast(PTR_READ(dac_cast(pDebugInfo), dac_cast(pDebugInfo)->PatchpointInfoSize())); + return static_cast(PTR_READ(dac_cast(chunks.pPatchpointInfo), chunks.cbPatchpointInfo)); } #endif @@ -1327,29 +1403,9 @@ void CompressDebugInfo::RestoreRichDebugInfo( } CONTRACTL_END; - BYTE flagByte = *pDebugInfo; - if ((flagByte & EXTRA_DEBUG_INFO_RICH) == 0) - { - *ppInlineTree = NULL; - *pNumInlineTree = 0; - *ppRichMappings = NULL; - *pNumRichMappings = 0; - return; - } + DebugInfoChunks chunks = Restore(pDebugInfo); - pDebugInfo++; - -#ifdef FEATURE_ON_STACK_REPLACEMENT - if ((flagByte & EXTRA_DEBUG_INFO_PATCHPOINT) != 0) - { - PTR_PatchpointInfo patchpointInfo = dac_cast(pDebugInfo); - pDebugInfo += patchpointInfo->PatchpointInfoSize(); - } -#endif - - UINT32 cbRichDebugInfo = *PTR_UINT32(pDebugInfo); - pDebugInfo += 4; - NibbleReader r(pDebugInfo, cbRichDebugInfo); + NibbleReader r(chunks.pRichDebugInfo, chunks.cbRichDebugInfo); *pNumInlineTree = r.ReadEncodedU32(); *pNumRichMappings = r.ReadEncodedU32(); @@ -1382,49 +1438,13 @@ void CompressDebugInfo::EnumMemoryRegions(CLRDataEnumMemoryFlags flags, PTR_BYTE PTR_BYTE pStart = pDebugInfo; - if (hasFlagByte) - { - // Check flag byte and skip over any patchpoint info - BYTE flagByte = *pDebugInfo; - pDebugInfo++; - - if ((flagByte & EXTRA_DEBUG_INFO_PATCHPOINT) != 0) - { - PTR_PatchpointInfo patchpointInfo = dac_cast(pDebugInfo); - pDebugInfo += patchpointInfo->PatchpointInfoSize(); - flagByte &= ~EXTRA_DEBUG_INFO_PATCHPOINT; - } - - if ((flagByte & EXTRA_DEBUG_INFO_RICH) != 0) - { - UINT32 cbRichDebugInfo = *PTR_UINT32(pDebugInfo); - pDebugInfo += 4; - pDebugInfo += cbRichDebugInfo; - flagByte &= ~EXTRA_DEBUG_INFO_RICH; - } - - _ASSERTE(flagByte == 0); - } - - NibbleReader r(pDebugInfo, 24 /* maximum size of compressed 4 UINT32s */); - - ULONG cbBounds = r.ReadEncodedU32(); - ULONG cbUninstrumentedBounds = 0; - if (cbBounds == DebugInfoBoundsHasInstrumentedBounds) - { - // This means we have instrumented bounds. - cbBounds = r.ReadEncodedU32(); - cbUninstrumentedBounds = r.ReadEncodedU32(); - } - ULONG cbVars = r.ReadEncodedU32(); - - pDebugInfo += r.GetNextByteIndex() + cbBounds + cbUninstrumentedBounds + cbVars; + DebugInfoChunks chunks = Restore(pDebugInfo); // NibbleReader reads in units of sizeof(NibbleChunkType) // So we need to account for any partial chunk at the end. - pDebugInfo = AlignUp(dac_cast(pDebugInfo), sizeof(NibbleReader::NibbleChunkType)); + PTR_BYTE pEnd = AlignUp(dac_cast(chunks.pEnd), sizeof(NibbleReader::NibbleChunkType)); - DacEnumMemoryRegion(dac_cast(pStart), pDebugInfo - pStart); + DacEnumMemoryRegion(dac_cast(pStart), pEnd - pStart); } #endif // DACCESS_COMPILE diff --git a/src/coreclr/vm/debuginfostore.h b/src/coreclr/vm/debuginfostore.h index b8ca0b27ad2a9c..916203d8dd46d1 100644 --- a/src/coreclr/vm/debuginfostore.h +++ b/src/coreclr/vm/debuginfostore.h @@ -60,6 +60,23 @@ enum class BoundsType Uninstrumented, // Get the uninstrumented bounds }; +struct DebugInfoChunks +{ + PTR_BYTE pBounds; + ULONG32 cbBounds; + PTR_BYTE pVars; + ULONG32 cbVars; + PTR_BYTE pUninstrumentedBounds; + ULONG32 cbUninstrumentedBounds; + PTR_BYTE pPatchpointInfo; + ULONG32 cbPatchpointInfo; + PTR_BYTE pRichDebugInfo; + ULONG32 cbRichDebugInfo; + PTR_BYTE pAsyncInfo; + ULONG32 cbAsyncInfo; + PTR_BYTE pEnd; +}; + //----------------------------------------------------------------------------- // Utility routines used for compression // Note that the compression is just an implementation detail of the stores, @@ -87,20 +104,32 @@ class CompressDebugInfo IN ICorDebugInfo::RichOffsetMapping* pRichOffsetMappings, IN OUT NibbleWriter* pWriter); + static void CompressAsyncDebugInfo( + IN ICorDebugInfo::AsyncInfo* asyncInfo, + IN ICorDebugInfo::AsyncSuspensionPoint* pSuspensionPoints, + IN ICorDebugInfo::AsyncContinuationVarInfo* pAsyncVars, + IN ULONG iAsyncVars, + IN OUT NibbleWriter* pWriter); + + static DebugInfoChunks Restore(IN PTR_BYTE pDebugInfo); + public: // Stores the result in LoaderHeap - static PTR_BYTE CompressBoundariesAndVars( - IN ICorDebugInfo::OffsetMapping * pOffsetMapping, - IN ULONG iOffsetMapping, - const InstrumentedILOffsetMapping * pInstrumentedILBounds, - IN ICorDebugInfo::NativeVarInfo * pNativeVarInfo, - IN ULONG iNativeVarInfo, - IN PatchpointInfo * patchpointInfo, - IN ICorDebugInfo::InlineTreeNode * pInlineTree, - IN ULONG iInlineTree, - IN ICorDebugInfo::RichOffsetMapping * pRichOffsetMappings, - IN ULONG iRichOffsetMappings, - IN BOOL writeFlagByte, + static PTR_BYTE Compress( + IN ICorDebugInfo::OffsetMapping* pOffsetMapping, + IN ULONG iOffsetMapping, + const InstrumentedILOffsetMapping* pInstrumentedILBounds, + IN ICorDebugInfo::NativeVarInfo* pNativeVarInfo, + IN ULONG iNativeVarInfo, + IN PatchpointInfo* patchpointInfo, + IN ICorDebugInfo::InlineTreeNode* pInlineTree, + IN ULONG iInlineTree, + IN ICorDebugInfo::RichOffsetMapping* pRichOffsetMappings, + IN ULONG iRichOffsetMappings, + IN ICorDebugInfo::AsyncInfo* asyncInfo, + IN ICorDebugInfo::AsyncSuspensionPoint* pSuspensionPoints, + IN ICorDebugInfo::AsyncContinuationVarInfo* pAsyncVars, + IN ULONG iAsyncVars, IN LoaderHeap * pLoaderHeap ); @@ -113,15 +142,13 @@ class CompressDebugInfo OUT ULONG32 * pcMap, // number of entries in ppMap OUT ICorDebugInfo::OffsetMapping **ppMap, // pointer to newly allocated array OUT ULONG32 *pcVars, - OUT ICorDebugInfo::NativeVarInfo **ppVars, - BOOL hasFlagByte + OUT ICorDebugInfo::NativeVarInfo **ppVars ); // Walk the ILOffsets without needing to allocate a buffer static size_t WalkILOffsets( IN PTR_BYTE pDebugInfo, BoundsType boundsType, - BOOL hasFlagByte, void* pContext, size_t (* pfnWalkILOffsets)(ICorDebugInfo::OffsetMapping *pOffsetMapping, void *pContext) ); @@ -177,6 +204,6 @@ class DebugInfoManager #endif }; -#define DebugInfoBoundsHasInstrumentedBounds 0xFFFFFFFF +#define DebugInfoFat 0xFFFFFFFF #endif // __DebugInfoStore_H_ diff --git a/src/coreclr/vm/jitinterface.cpp b/src/coreclr/vm/jitinterface.cpp index a28bb895fd481e..28ce25c3a2dbaf 100644 --- a/src/coreclr/vm/jitinterface.cpp +++ b/src/coreclr/vm/jitinterface.cpp @@ -10726,9 +10726,8 @@ CEECodeGenInfo::CEECodeGenInfo(PrepareCodeConfig* config, MethodDesc* fd, COR_IL , m_numInlineTreeNodes(0) , m_richOffsetMappings(NULL) , m_numRichOffsetMappings(0) - , m_asyncInfo(NULL) - , m_asyncSuspensionPoints(NULL) - , m_asyncContinuationVars(NULL) + , m_dbgAsyncSuspensionPoints(NULL) + , m_dbgAsyncContinuationVars(NULL) , m_numAsyncContinuationVars(0) , m_gphCache() { @@ -10742,6 +10741,7 @@ CEECodeGenInfo::CEECodeGenInfo(PrepareCodeConfig* config, MethodDesc* fd, COR_IL m_ILHeader = ilHeader; m_jitFlags = GetCompileFlags(config, m_pMethodBeingCompiled, &m_MethodInfo); + m_dbgAsyncInfo.NumSuspensionPoints = 0; } void CEECodeGenInfo::getHelperFtn(CorInfoHelpFunc ftnNum, /* IN */ @@ -11187,19 +11187,10 @@ void CEECodeGenInfo::reportAsyncDebugInfo( JIT_TO_EE_TRANSITION(); - if (((EECodeGenManager*)m_jitManager)->IsStoringRichDebugInfo()) - { - m_asyncInfo = asyncInfo; - m_asyncSuspensionPoints = suspensionPoints; - m_asyncContinuationVars = vars; - m_numAsyncContinuationVars = numVars; - } - else - { - freeArrayInternal(asyncInfo); - freeArrayInternal(suspensionPoints); - freeArrayInternal(vars); - } + m_dbgAsyncInfo = *asyncInfo; + m_dbgAsyncSuspensionPoints = suspensionPoints; + m_dbgAsyncContinuationVars = vars; + m_numAsyncContinuationVars = numVars; EE_TO_JIT_TRANSITION(); } @@ -11466,7 +11457,7 @@ void CEECodeGenInfo::CompressDebugInfo(PCODE nativeEntry, NativeCodeVersion nati return; } - if ((m_iOffsetMapping == 0) && (m_iNativeVarInfo == 0) && (patchpointInfo == NULL) && (m_numInlineTreeNodes == 0) && (m_numRichOffsetMappings == 0)) + if ((m_iOffsetMapping == 0) && (m_iNativeVarInfo == 0) && (patchpointInfo == NULL) && (m_numInlineTreeNodes == 0) && (m_numRichOffsetMappings == 0) && (m_dbgAsyncInfo.NumSuspensionPoints == 0)) return; if (patchpointInfo != NULL) @@ -11476,14 +11467,6 @@ void CEECodeGenInfo::CompressDebugInfo(PCODE nativeEntry, NativeCodeVersion nati EX_TRY { - BOOL writeFlagByte = FALSE; -#ifdef FEATURE_ON_STACK_REPLACEMENT - writeFlagByte = TRUE; -#endif - if (m_jitManager->IsStoringRichDebugInfo()) - writeFlagByte = TRUE; - - const InstrumentedILOffsetMapping *pILOffsetMapping = NULL; InstrumentedILOffsetMapping loadTimeMapping; #ifdef FEATURE_REJIT @@ -11511,13 +11494,13 @@ void CEECodeGenInfo::CompressDebugInfo(PCODE nativeEntry, NativeCodeVersion nati } #endif - PTR_BYTE pDebugInfo = CompressDebugInfo::CompressBoundariesAndVars( + PTR_BYTE pDebugInfo = CompressDebugInfo::Compress( m_pOffsetMapping, m_iOffsetMapping, pILOffsetMapping, m_pNativeVarInfo, m_iNativeVarInfo, patchpointInfo, m_inlineTreeNodes, m_numInlineTreeNodes, m_richOffsetMappings, m_numRichOffsetMappings, - writeFlagByte, + &m_dbgAsyncInfo, m_dbgAsyncSuspensionPoints, m_dbgAsyncContinuationVars, m_numAsyncContinuationVars, m_pMethodBeingCompiled->GetLoaderAllocator()->GetLowFrequencyHeap()); SetDebugInfo(pDebugInfo); diff --git a/src/coreclr/vm/jitinterface.h b/src/coreclr/vm/jitinterface.h index 11551c0bd20f71..18ade9201b1600 100644 --- a/src/coreclr/vm/jitinterface.h +++ b/src/coreclr/vm/jitinterface.h @@ -531,20 +531,17 @@ class CEECodeGenInfo : public CEEInfo freeArrayInternal(m_inlineTreeNodes); if (m_richOffsetMappings != NULL) freeArrayInternal(m_richOffsetMappings); - if (m_asyncInfo != NULL) - freeArrayInternal(m_asyncInfo); - if (m_asyncSuspensionPoints != NULL) - freeArrayInternal(m_asyncSuspensionPoints); - if (m_asyncContinuationVars != NULL) - freeArrayInternal(m_asyncContinuationVars); + if (m_dbgAsyncSuspensionPoints != NULL) + freeArrayInternal(m_dbgAsyncSuspensionPoints); + if (m_dbgAsyncContinuationVars != NULL) + freeArrayInternal(m_dbgAsyncContinuationVars); m_inlineTreeNodes = NULL; m_numInlineTreeNodes = 0; m_richOffsetMappings = NULL; m_numRichOffsetMappings = 0; - m_asyncInfo = NULL; - m_asyncSuspensionPoints = NULL; - m_asyncContinuationVars = NULL; + m_dbgAsyncSuspensionPoints = NULL; + m_dbgAsyncContinuationVars = NULL; m_numAsyncContinuationVars = 0; } @@ -641,9 +638,9 @@ class CEECodeGenInfo : public CEEInfo ICorDebugInfo::RichOffsetMapping *m_richOffsetMappings; ULONG32 m_numRichOffsetMappings; - ICorDebugInfo::AsyncInfo *m_asyncInfo; - ICorDebugInfo::AsyncSuspensionPoint *m_asyncSuspensionPoints; - ICorDebugInfo::AsyncContinuationVarInfo *m_asyncContinuationVars; + ICorDebugInfo::AsyncInfo m_dbgAsyncInfo; + ICorDebugInfo::AsyncSuspensionPoint *m_dbgAsyncSuspensionPoints; + ICorDebugInfo::AsyncContinuationVarInfo *m_dbgAsyncContinuationVars; ULONG32 m_numAsyncContinuationVars; // The first time a call is made to CEEJitInfo::GetProfilingHandle() from this thread From 6addd0e1da4c22d70af66e392ed6e10881da0c23 Mon Sep 17 00:00:00 2001 From: Jakob Botsch Nielsen Date: Wed, 1 Oct 2025 21:41:57 +0200 Subject: [PATCH 04/72] Update managed view --- src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs b/src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs index 1b9ed8c69cf8de..ed2a0b0b2d662f 100644 --- a/src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs +++ b/src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs @@ -1329,9 +1329,6 @@ public struct AsyncContinuationVarInfo public struct AsyncSuspensionPoint { - // State number assigned to this suspension point. UINT_MAX if no state - // number is assigned (when AsyncInfo::NumSuspensionPoints == 1). - public uint StateNumber; // IL offset in the root method that resulted in the creation of this suspension point. public uint RootILOffset; // Index of inline tree node containing the IL offset (0 for root) @@ -1341,8 +1338,6 @@ public struct AsyncSuspensionPoint public uint NumVars; // Count of AsyncContinuationVarInfo public uint NumContinuationVars; - // Index into array of AsyncContinuationVarInfo of first variable - public uint ContinuationVarsIndex; } public struct AsyncInfo From 6a5bf19d720b6e3b6404b2f1c9e4e3d66ff3b9ed Mon Sep 17 00:00:00 2001 From: Jakob Botsch Nielsen Date: Wed, 1 Oct 2025 21:44:02 +0200 Subject: [PATCH 05/72] Remove TODOs --- .../tools/superpmi/superpmi-shim-collector/icorjitinfo.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/coreclr/tools/superpmi/superpmi-shim-collector/icorjitinfo.cpp b/src/coreclr/tools/superpmi/superpmi-shim-collector/icorjitinfo.cpp index 7fc8560a236310..ba04575f0117c9 100644 --- a/src/coreclr/tools/superpmi/superpmi-shim-collector/icorjitinfo.cpp +++ b/src/coreclr/tools/superpmi/superpmi-shim-collector/icorjitinfo.cpp @@ -1229,7 +1229,7 @@ void interceptor_ICJI::reportRichMappings(ICorDebugInfo::InlineTreeNode* inli uint32_t numMappings) { mc->cr->AddCall("reportRichMappings"); - // TODO: record these mappings + // Compile output that we do not currently save original_ICorJitInfo->reportRichMappings(inlineTreeNodes, numInlineTreeNodes, mappings, numMappings); } @@ -1239,7 +1239,7 @@ void interceptor_ICJI::reportAsyncDebugInfo(ICorDebugInfo::AsyncInfo* uint32_t numVars) { mc->cr->AddCall("reportAsyncDebugInfo"); - // TODO: record async debug info + // Compile output that we do not currently save original_ICorJitInfo->reportAsyncDebugInfo(asyncInfo, suspensionPoints, vars, numVars); } From 262260a9f30e76e4dfc404bbcce4aefdf3b346f1 Mon Sep 17 00:00:00 2001 From: Jakob Botsch Nielsen Date: Wed, 1 Oct 2025 21:46:58 +0200 Subject: [PATCH 06/72] Leaf transition --- src/coreclr/vm/jitinterface.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/coreclr/vm/jitinterface.cpp b/src/coreclr/vm/jitinterface.cpp index 28ce25c3a2dbaf..f86721844a9823 100644 --- a/src/coreclr/vm/jitinterface.cpp +++ b/src/coreclr/vm/jitinterface.cpp @@ -11185,14 +11185,14 @@ void CEECodeGenInfo::reportAsyncDebugInfo( MODE_PREEMPTIVE; } CONTRACTL_END; - JIT_TO_EE_TRANSITION(); + JIT_TO_EE_TRANSITION_LEAF(); m_dbgAsyncInfo = *asyncInfo; m_dbgAsyncSuspensionPoints = suspensionPoints; m_dbgAsyncContinuationVars = vars; m_numAsyncContinuationVars = numVars; - EE_TO_JIT_TRANSITION(); + EE_TO_JIT_TRANSITION_LEAF(); } void CEECodeGenInfo::reportMetadata( From 4e2a829bca809a723002507e7a893429c2850cfb Mon Sep 17 00:00:00 2001 From: Jakob Botsch Nielsen Date: Wed, 1 Oct 2025 21:50:42 +0200 Subject: [PATCH 07/72] Comment --- src/coreclr/inc/cordebuginfo.h | 3 ++- src/coreclr/inc/corinfo.h | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/coreclr/inc/cordebuginfo.h b/src/coreclr/inc/cordebuginfo.h index a90a0289df50f1..1611dde47878f1 100644 --- a/src/coreclr/inc/cordebuginfo.h +++ b/src/coreclr/inc/cordebuginfo.h @@ -448,7 +448,8 @@ class ICorDebugInfo uint32_t Inlinee; // IL offset that resulted in the creation of the suspension point. uint32_t ILOffset; - // Count of AsyncContinuationVarInfo in array of locals + // Count of AsyncContinuationVarInfo in array of locals starting where + // the previous suspension point's locals end. uint32_t NumContinuationVars; }; diff --git a/src/coreclr/inc/corinfo.h b/src/coreclr/inc/corinfo.h index 36b4ab4925f0f9..5722f5a616f025 100644 --- a/src/coreclr/inc/corinfo.h +++ b/src/coreclr/inc/corinfo.h @@ -2918,8 +2918,8 @@ class ICorStaticInfo virtual void reportAsyncDebugInfo( ICorDebugInfo::AsyncInfo* asyncInfo, // [IN] Async method information ICorDebugInfo::AsyncSuspensionPoint* suspensionPoints, // [IN] Array of async suspension points, indexed by state number - ICorDebugInfo::AsyncContinuationVarInfo* vars, // [IN] Array of async continuation variable info - uint32_t numVars // [IN] Number of continuation variables + ICorDebugInfo::AsyncContinuationVarInfo* vars, // [IN] Array of async continuation variable info + uint32_t numVars // [IN] Number of entries in the async vars array ) = 0; // Report back some metadata about the compilation to the EE -- for From 820afa975b6d7ad58cc68376239c5f35dfd75abb Mon Sep 17 00:00:00 2001 From: Jakob Botsch Nielsen Date: Wed, 1 Oct 2025 21:52:32 +0200 Subject: [PATCH 08/72] Delete unused enum --- src/coreclr/vm/debuginfostore.cpp | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/src/coreclr/vm/debuginfostore.cpp b/src/coreclr/vm/debuginfostore.cpp index f97483662f9cd8..2cdfba389bc784 100644 --- a/src/coreclr/vm/debuginfostore.cpp +++ b/src/coreclr/vm/debuginfostore.cpp @@ -505,17 +505,6 @@ static void DoAsyncVars( } } -enum EXTRA_DEBUG_INFO_FLAGS -{ - // Debug info contains patchpoint information - EXTRA_DEBUG_INFO_PATCHPOINT = 1, - // Debug info contains rich information - EXTRA_DEBUG_INFO_RICH = 2, - // Debug info contains async information - EXTRA_DEBUG_INFO_ASYNC = 4, - EXTRA_DEBUG_INFO_UNINSTRUMENTED_BOUNDS = 8, -}; - #ifndef DACCESS_COMPILE BYTE* DecompressNew(void*, size_t cBytes) From 3621eae193ab6ee7773746375a94c57e84a590c7 Mon Sep 17 00:00:00 2001 From: Jakob Botsch Nielsen Date: Wed, 1 Oct 2025 21:57:03 +0200 Subject: [PATCH 09/72] Restore comment --- src/coreclr/vm/debuginfostore.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coreclr/vm/debuginfostore.cpp b/src/coreclr/vm/debuginfostore.cpp index 2cdfba389bc784..ce5f482dc6b75f 100644 --- a/src/coreclr/vm/debuginfostore.cpp +++ b/src/coreclr/vm/debuginfostore.cpp @@ -991,7 +991,7 @@ PTR_BYTE CompressDebugInfo::Compress( if (isFat) { - w.WriteEncodedU32(DebugInfoFat); + w.WriteEncodedU32(DebugInfoFat);// 0xFFFFFFFF is used to indicate that this is a fat header w.WriteEncodedU32(cbBounds); w.WriteEncodedU32(cbVars); w.WriteEncodedU32(cbUninstrumentedBounds); From f597424a23a6edafc2d67212a0ae68964021b8b4 Mon Sep 17 00:00:00 2001 From: Jakob Botsch Nielsen Date: Wed, 1 Oct 2025 22:10:42 +0200 Subject: [PATCH 10/72] Fix osx build --- src/coreclr/jit/async.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/coreclr/jit/async.cpp b/src/coreclr/jit/async.cpp index 76288114c9866b..7956f6f60fa275 100644 --- a/src/coreclr/jit/async.cpp +++ b/src/coreclr/jit/async.cpp @@ -2265,12 +2265,14 @@ void AsyncTransformation::ReportDebugInfo() ICorDebugInfo::AsyncSuspensionPoint* hostSuspensionPoints = static_cast(m_comp->info.compCompHnd->allocateArray( m_dbgSuspensionPoints.size() * sizeof(ICorDebugInfo::AsyncSuspensionPoint))); - std::copy(m_dbgSuspensionPoints.begin(), m_dbgSuspensionPoints.end(), hostSuspensionPoints); + for (size_t i = 0; i < m_dbgSuspensionPoints.size(); i++) + hostSuspensionPoints[i] = m_dbgSuspensionPoints[i]; ICorDebugInfo::AsyncContinuationVarInfo* hostVars = static_cast(m_comp->info.compCompHnd->allocateArray( m_dbgContinuationVars.size() * sizeof(ICorDebugInfo::AsyncContinuationVarInfo))); - std::copy(m_dbgContinuationVars.begin(), m_dbgContinuationVars.end(), hostVars); + for (size_t i = 0; i < m_dbgContinuationVars.size(); i++) + hostVars[i] = m_dbgContinuationVars[i]; m_comp->info.compCompHnd->reportAsyncDebugInfo(&asyncInfo, hostSuspensionPoints, hostVars, static_cast(m_dbgContinuationVars.size())); From 6bb6cee13e4d7e6c281268445a9e207ec828e458 Mon Sep 17 00:00:00 2001 From: Jakob Botsch Nielsen Date: Wed, 1 Oct 2025 22:45:30 +0200 Subject: [PATCH 11/72] Fix GCC build --- 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 7956f6f60fa275..1e560842352827 100644 --- a/src/coreclr/jit/async.cpp +++ b/src/coreclr/jit/async.cpp @@ -2226,7 +2226,7 @@ void AsyncTransformation::CreateDebugInfoForSuspensionPoint(GenTreeCall* asyncCa for (const LiveLocalInfo& local : layout.Locals) { unsigned ilVarNum = m_comp->compMap2ILvarNum(local.LclNum); - if (ilVarNum == ICorDebugInfo::UNKNOWN_ILNUM) + if (ilVarNum == (unsigned)ICorDebugInfo::UNKNOWN_ILNUM) { continue; } From f580e3f7175c933128c8ca950d72b6539e376826 Mon Sep 17 00:00:00 2001 From: Jakob Botsch Nielsen Date: Thu, 2 Oct 2025 12:17:56 +0200 Subject: [PATCH 12/72] Change sentinel value, fix contract --- src/coreclr/vm/debuginfostore.cpp | 17 +++++++++-------- src/coreclr/vm/debuginfostore.h | 4 ++-- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/src/coreclr/vm/debuginfostore.cpp b/src/coreclr/vm/debuginfostore.cpp index ce5f482dc6b75f..d0479081d72f4e 100644 --- a/src/coreclr/vm/debuginfostore.cpp +++ b/src/coreclr/vm/debuginfostore.cpp @@ -984,6 +984,7 @@ PTR_BYTE CompressDebugInfo::Compress( NibbleWriter w; bool isFat = + (cbBounds == DebugInfoFat) || (cbPatchpointInfo > 0) || (cbRichDebugInfo > 0) || (cbAsyncInfo > 0) || @@ -991,7 +992,7 @@ PTR_BYTE CompressDebugInfo::Compress( if (isFat) { - w.WriteEncodedU32(DebugInfoFat);// 0xFFFFFFFF is used to indicate that this is a fat header + w.WriteEncodedU32(DebugInfoFat); // Indicator that this is a fat header w.WriteEncodedU32(cbBounds); w.WriteEncodedU32(cbVars); w.WriteEncodedU32(cbUninstrumentedBounds); @@ -1150,7 +1151,7 @@ static void DoBounds(PTR_BYTE addrBounds, uint32_t cbBounds, TNumBounds countHan // Uncompression (restore) routines //----------------------------------------------------------------------------- -DebugInfoChunks CompressDebugInfo::Restore(IN PTR_BYTE pDebugInfo) +DebugInfoChunks CompressDebugInfo::DecodeChunks(IN PTR_BYTE pDebugInfo) { CONTRACTL { @@ -1223,7 +1224,7 @@ void CompressDebugInfo::RestoreBoundariesAndVars( if (pcVars != NULL) *pcVars = 0; if (ppVars != NULL) *ppVars = NULL; - DebugInfoChunks chunks = Restore(pDebugInfo); + DebugInfoChunks chunks = DecodeChunks(pDebugInfo); PTR_BYTE addrBounds = chunks.pBounds; unsigned cbBounds = chunks.cbBounds; @@ -1309,7 +1310,7 @@ size_t CompressDebugInfo::WalkILOffsets( } CONTRACTL_END; - DebugInfoChunks chunks = Restore(pDebugInfo); + DebugInfoChunks chunks = DecodeChunks(pDebugInfo); PTR_BYTE addrBounds = chunks.pBounds; unsigned cbBounds = chunks.cbBounds; @@ -1358,14 +1359,14 @@ PatchpointInfo * CompressDebugInfo::RestorePatchpointInfo(IN PTR_BYTE pDebugInfo { CONTRACTL { - NOTHROW; + THROWS; GC_NOTRIGGER; MODE_ANY; SUPPORTS_DAC; } CONTRACTL_END; - DebugInfoChunks chunks = Restore(pDebugInfo); + DebugInfoChunks chunks = DecodeChunks(pDebugInfo); if (chunks.cbPatchpointInfo == 0) return NULL; @@ -1392,7 +1393,7 @@ void CompressDebugInfo::RestoreRichDebugInfo( } CONTRACTL_END; - DebugInfoChunks chunks = Restore(pDebugInfo); + DebugInfoChunks chunks = DecodeChunks(pDebugInfo); NibbleReader r(chunks.pRichDebugInfo, chunks.cbRichDebugInfo); @@ -1427,7 +1428,7 @@ void CompressDebugInfo::EnumMemoryRegions(CLRDataEnumMemoryFlags flags, PTR_BYTE PTR_BYTE pStart = pDebugInfo; - DebugInfoChunks chunks = Restore(pDebugInfo); + DebugInfoChunks chunks = DecodeChunks(pDebugInfo); // NibbleReader reads in units of sizeof(NibbleChunkType) // So we need to account for any partial chunk at the end. diff --git a/src/coreclr/vm/debuginfostore.h b/src/coreclr/vm/debuginfostore.h index 916203d8dd46d1..8a56b5b0a3300b 100644 --- a/src/coreclr/vm/debuginfostore.h +++ b/src/coreclr/vm/debuginfostore.h @@ -111,7 +111,7 @@ class CompressDebugInfo IN ULONG iAsyncVars, IN OUT NibbleWriter* pWriter); - static DebugInfoChunks Restore(IN PTR_BYTE pDebugInfo); + static DebugInfoChunks DecodeChunks(IN PTR_BYTE pDebugInfo); public: // Stores the result in LoaderHeap @@ -204,6 +204,6 @@ class DebugInfoManager #endif }; -#define DebugInfoFat 0xFFFFFFFF +#define DebugInfoFat 0 #endif // __DebugInfoStore_H_ From 55b952239fecae6f36812711f5627901dac8aa8f Mon Sep 17 00:00:00 2001 From: Jakob Botsch Nielsen Date: Thu, 2 Oct 2025 13:41:48 +0200 Subject: [PATCH 13/72] Bump R2R --- src/coreclr/inc/readytorun.h | 5 ++-- .../Common/Internal/Runtime/ModuleHeaders.cs | 2 +- .../ReadyToRun/DebugInfoTableNode.cs | 22 +++++++++++++++-- .../JitInterface/CorInfoImpl.ReadyToRun.cs | 12 ++-------- .../DebugInfo.cs | 24 +++++++++++++++++-- 5 files changed, 48 insertions(+), 17 deletions(-) diff --git a/src/coreclr/inc/readytorun.h b/src/coreclr/inc/readytorun.h index 134b685ff9fbe6..93826f5b8ca771 100644 --- a/src/coreclr/inc/readytorun.h +++ b/src/coreclr/inc/readytorun.h @@ -19,10 +19,10 @@ // src/coreclr/nativeaot/Runtime/inc/ModuleHeaders.h // If you update this, ensure you run `git grep MINIMUM_READYTORUN_MAJOR_VERSION` // and handle pending work. -#define READYTORUN_MAJOR_VERSION 16 +#define READYTORUN_MAJOR_VERSION 17 #define READYTORUN_MINOR_VERSION 0x0000 -#define MINIMUM_READYTORUN_MAJOR_VERSION 16 +#define MINIMUM_READYTORUN_MAJOR_VERSION 17 // R2R Version 2.1 adds the InliningInfo section // R2R Version 2.2 adds the ProfileDataInfo section @@ -47,6 +47,7 @@ // R2R Version 14 changed x86 code generation to use funclets // R2R Version 15 removes double to int/uint helper calls // R2R Version 16 replaces the compression format for debug boundaries with a new format that is smaller and more efficient to parse +// R2R Version 17 adds support for producing "fat" debug information (that e.g. can include async debug info) struct READYTORUN_CORE_HEADER { diff --git a/src/coreclr/tools/Common/Internal/Runtime/ModuleHeaders.cs b/src/coreclr/tools/Common/Internal/Runtime/ModuleHeaders.cs index 1ea99252052d90..090d62bad8cc17 100644 --- a/src/coreclr/tools/Common/Internal/Runtime/ModuleHeaders.cs +++ b/src/coreclr/tools/Common/Internal/Runtime/ModuleHeaders.cs @@ -15,7 +15,7 @@ internal struct ReadyToRunHeaderConstants { public const uint Signature = 0x00525452; // 'RTR' - public const ushort CurrentMajorVersion = 16; + public const ushort CurrentMajorVersion = 17; public const ushort CurrentMinorVersion = 0; } #if READYTORUN diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/DebugInfoTableNode.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/DebugInfoTableNode.cs index b809ef074b9af0..d1318d881bd74e 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/DebugInfoTableNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/DebugInfoTableNode.cs @@ -90,8 +90,24 @@ public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) byte[] vars = method.DebugVarInfos; NibbleWriter nibbleWriter = new NibbleWriter(); - nibbleWriter.WriteUInt((uint)(bounds?.Length ?? 0)); - nibbleWriter.WriteUInt((uint)(vars?.Length ?? 0)); + uint boundsLength = (uint)(bounds?.Length ?? 0); + bool isFatHeader = boundsLength == DebugInfoFat; + + if (isFatHeader) + { + nibbleWriter.WriteUInt(DebugInfoFat); + nibbleWriter.WriteUInt(boundsLength); + nibbleWriter.WriteUInt((uint)(vars?.Length ?? 0)); + nibbleWriter.WriteUInt(0); // cbUninstrumentedBounds + nibbleWriter.WriteUInt(0); // cbPatchpointInfo + nibbleWriter.WriteUInt(0); // cbRichDebugInfo + nibbleWriter.WriteUInt(0); // cbAsyncInfo + } + else + { + nibbleWriter.WriteUInt(boundsLength); + nibbleWriter.WriteUInt((uint)(vars?.Length ?? 0)); + } byte[] header = nibbleWriter.ToArray(); methodDebugBlob.Write(header, 0, header.Length); @@ -307,5 +323,7 @@ static void WriteEncodedStackOffset(NibbleWriter _writer, int offset, bool assum return writer.ToArray(); } + + private const int DebugInfoFat = 0; } } diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs index 92302c2181d398..461fbb8fb68a6b 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs @@ -1506,11 +1506,7 @@ private void setVars(CORINFO_METHOD_STRUCT_* ftn, uint cVars, NativeVarInfo* var if (cVars == 0) return; - _debugVarInfos = new NativeVarInfo[cVars]; - for (int i = 0; i < cVars; i++) - { - _debugVarInfos[i] = vars[i]; - } + _debugVarInfos = new Span(vars, (int)cVars).ToArray(); // JIT gave the ownership of this to us, so need to free this. freeArray(vars); @@ -1523,11 +1519,7 @@ private void setBoundaries(CORINFO_METHOD_STRUCT_* ftn, uint cMap, OffsetMapping { Debug.Assert(_debugLocInfos == null); - _debugLocInfos = new OffsetMapping[cMap]; - for (int i = 0; i < cMap; i++) - { - _debugLocInfos[i] = pMap[i]; - } + _debugLocInfos = new Span(pMap, (int)cMap).ToArray(); // JIT gave the ownership of this to us, so need to free this. freeArray(pMap); diff --git a/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/DebugInfo.cs b/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/DebugInfo.cs index 019b74c714b3a9..e17638bcb36b23 100644 --- a/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/DebugInfo.cs +++ b/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/DebugInfo.cs @@ -116,8 +116,28 @@ private void EnsureInitialized() } NibbleReader reader = new NibbleReader(imageReader, (int)debugInfoOffset); - uint boundsByteCount = reader.ReadUInt(); - uint variablesByteCount = reader.ReadUInt(); + + uint boundsByteCountOrIndicator = reader.ReadUInt(); + + uint boundsByteCount = 0; + uint variablesByteCount = 0; + + const int DebugInfoFat = 0; + if (_runtimeFunction.ReadyToRunReader.ReadyToRunHeader.MajorVersion >= 17 && boundsByteCountOrIndicator == DebugInfoFat) + { + boundsByteCount = reader.ReadUInt(); + variablesByteCount = reader.ReadUInt(); + reader.ReadUInt(); // uninstrumented bounds + reader.ReadUInt(); // patchpoint info + reader.ReadUInt(); // rich debug info + reader.ReadUInt(); // async info + } + else + { + boundsByteCount = boundsByteCountOrIndicator; + variablesByteCount = reader.ReadUInt(); + } + int boundsOffset = reader.GetNextByteOffset(); int variablesOffset = (int)(boundsOffset + boundsByteCount); From c9c64be687f923c47a9ea8e9754560df81890d4b Mon Sep 17 00:00:00 2001 From: Jakob Botsch Nielsen Date: Thu, 2 Oct 2025 14:11:43 +0200 Subject: [PATCH 14/72] Clean up --- src/coreclr/vm/codeman.cpp | 12 +++--------- src/coreclr/vm/debuginfostore.cpp | 2 +- src/coreclr/vm/debuginfostore.h | 2 +- 3 files changed, 5 insertions(+), 11 deletions(-) diff --git a/src/coreclr/vm/codeman.cpp b/src/coreclr/vm/codeman.cpp index 18a2559db515f9..7fdd758de949b5 100644 --- a/src/coreclr/vm/codeman.cpp +++ b/src/coreclr/vm/codeman.cpp @@ -4207,15 +4207,9 @@ void CodeHeader::EnumMemoryRegions(CLRDataEnumMemoryFlags flags, IJitManager* pJ } #endif // FEATURE_EH_FUNCLETS -#ifdef FEATURE_ON_STACK_REPLACEMENT - BOOL hasFlagByte = TRUE; -#else - BOOL hasFlagByte = FALSE; -#endif - if (this->GetDebugInfo() != NULL) { - CompressDebugInfo::EnumMemoryRegions(flags, this->GetDebugInfo(), hasFlagByte); + CompressDebugInfo::EnumMemoryRegions(flags, this->GetDebugInfo()); } } @@ -4271,7 +4265,7 @@ void InterpreterCodeHeader::EnumMemoryRegions(CLRDataEnumMemoryFlags flags, IJit if (this->GetDebugInfo() != NULL) { - CompressDebugInfo::EnumMemoryRegions(flags, this->GetDebugInfo(), FALSE /* hasFlagByte */); + CompressDebugInfo::EnumMemoryRegions(flags, this->GetDebugInfo()); } } @@ -6482,7 +6476,7 @@ void ReadyToRunJitManager::EnumMemoryRegionsForMethodDebugInfo(CLRDataEnumMemory if (pDebugInfo == NULL) return; - CompressDebugInfo::EnumMemoryRegions(flags, pDebugInfo, FALSE); + CompressDebugInfo::EnumMemoryRegions(flags, pDebugInfo); } #endif diff --git a/src/coreclr/vm/debuginfostore.cpp b/src/coreclr/vm/debuginfostore.cpp index d0479081d72f4e..8cd10c29315c11 100644 --- a/src/coreclr/vm/debuginfostore.cpp +++ b/src/coreclr/vm/debuginfostore.cpp @@ -1416,7 +1416,7 @@ void CompressDebugInfo::RestoreRichDebugInfo( } #ifdef DACCESS_COMPILE -void CompressDebugInfo::EnumMemoryRegions(CLRDataEnumMemoryFlags flags, PTR_BYTE pDebugInfo, BOOL hasFlagByte) +void CompressDebugInfo::EnumMemoryRegions(CLRDataEnumMemoryFlags flags, PTR_BYTE pDebugInfo) { CONTRACTL { diff --git a/src/coreclr/vm/debuginfostore.h b/src/coreclr/vm/debuginfostore.h index 8a56b5b0a3300b..e926293ea6e1f2 100644 --- a/src/coreclr/vm/debuginfostore.h +++ b/src/coreclr/vm/debuginfostore.h @@ -169,7 +169,7 @@ class CompressDebugInfo OUT ULONG32* pNumRichMappings); #ifdef DACCESS_COMPILE - static void EnumMemoryRegions(CLRDataEnumMemoryFlags flags, PTR_BYTE pDebugInfo, BOOL hasFlagByte); + static void EnumMemoryRegions(CLRDataEnumMemoryFlags flags, PTR_BYTE pDebugInfo); #endif }; From db77446d9894c04dc04f7deeef93f336873f7e24 Mon Sep 17 00:00:00 2001 From: Jakob Botsch Nielsen Date: Thu, 2 Oct 2025 14:54:58 +0200 Subject: [PATCH 15/72] Expose async debug info accessor APIs --- src/coreclr/vm/codeman.cpp | 133 +++++++++++++++++++++++++++++- src/coreclr/vm/codeman.h | 40 +++++++++ src/coreclr/vm/debuginfostore.cpp | 81 ++++++++++++++++++ src/coreclr/vm/debuginfostore.h | 17 ++++ 4 files changed, 270 insertions(+), 1 deletion(-) diff --git a/src/coreclr/vm/codeman.cpp b/src/coreclr/vm/codeman.cpp index 7fdd758de949b5..3753492d82b352 100644 --- a/src/coreclr/vm/codeman.cpp +++ b/src/coreclr/vm/codeman.cpp @@ -4099,6 +4099,56 @@ BOOL EEJitManager::GetRichDebugInfo( return GetRichDebugInfoWorker(pDebugInfo, fpNew, pNewData, ppInlineTree, pNumInlineTree, ppRichMappings, pNumRichMappings); } +BOOL EECodeGenManager::GetAsyncDebugInfoWorker( + PTR_BYTE pDebugInfo, + IN FP_IDS_NEW fpNew, IN void* pNewData, + OUT ICorDebugInfo::AsyncInfo* pAsyncInfo, + OUT ICorDebugInfo::AsyncSuspensionPoint** ppSuspensionPoints, + OUT ICorDebugInfo::AsyncContinuationVarInfo** ppAsyncVars, + OUT ULONG32* pcAsyncVars) +{ + CONTRACTL { + THROWS; // on OOM. + GC_NOTRIGGER; // getting debug info shouldn't trigger + SUPPORTS_DAC; + } CONTRACTL_END; + + // No header created, which means no jit information is available. + if (pDebugInfo == NULL) + return FALSE; + + CompressDebugInfo::RestoreAsyncDebugInfo( + fpNew, pNewData, + pDebugInfo, + pAsyncInfo, + ppSuspensionPoints, + ppAsyncVars, pcAsyncVars); + + return TRUE; +} + +BOOL EEJitManager::GetAsyncDebugInfo( + const DebugInfoRequest & request, + IN FP_IDS_NEW fpNew, IN void * pNewData, + OUT ICorDebugInfo::AsyncInfo* pAsyncInfo, + OUT ICorDebugInfo::AsyncSuspensionPoint** ppSuspensionPoints, + OUT ICorDebugInfo::AsyncContinuationVarInfo** ppAsyncVars, + OUT ULONG32* pcAsyncVars) +{ + CONTRACTL { + THROWS; // on OOM. + GC_NOTRIGGER; // getting debug info shouldn't trigger + SUPPORTS_DAC; + } CONTRACTL_END; + + CodeHeader * pHdr = GetCodeHeaderFromDebugInfoRequest(request); + _ASSERTE(pHdr != NULL); + + PTR_BYTE pDebugInfo = pHdr->GetDebugInfo(); + + return GetAsyncDebugInfoWorker(pDebugInfo, fpNew, pNewData, pAsyncInfo, ppSuspensionPoints, ppAsyncVars, pcAsyncVars); +} + #ifdef FEATURE_INTERPRETER BOOL InterpreterJitManager::GetBoundariesAndVars( const DebugInfoRequest & request, @@ -4182,6 +4232,29 @@ BOOL InterpreterJitManager::GetRichDebugInfo( return GetRichDebugInfoWorker(pDebugInfo, fpNew, pNewData, ppInlineTree, pNumInlineTree, ppRichMappings, pNumRichMappings); } + +BOOL InterpreterJitManager::GetAsyncDebugInfo( + const DebugInfoRequest & request, + IN FP_IDS_NEW fpNew, IN void * pNewData, + OUT ICorDebugInfo::AsyncInfo* pAsyncInfo, + OUT ICorDebugInfo::AsyncSuspensionPoint** ppSuspensionPoints, + OUT ICorDebugInfo::AsyncContinuationVarInfo** ppAsyncVars, + OUT ULONG32* pcAsyncVars) +{ + CONTRACTL { + THROWS; // on OOM. + GC_NOTRIGGER; // getting debug info shouldn't trigger + SUPPORTS_DAC; + } CONTRACTL_END; + + InterpreterCodeHeader * pHdr = GetCodeHeaderFromDebugInfoRequest(request); + _ASSERTE(pHdr != NULL); + + PTR_BYTE pDebugInfo = pHdr->GetDebugInfo(); + + return GetAsyncDebugInfoWorker(pDebugInfo, fpNew, pNewData, pAsyncInfo, ppSuspensionPoints, ppAsyncVars, pcAsyncVars); +} + #endif // FEATURE_INTERPRETER #ifdef DACCESS_COMPILE @@ -6455,7 +6528,65 @@ BOOL ReadyToRunJitManager::GetRichDebugInfo( OUT ICorDebugInfo::RichOffsetMapping** ppRichMappings, OUT ULONG32* pNumRichMappings) { - return FALSE; + CONTRACTL { + THROWS; // on OOM. + GC_NOTRIGGER; // getting vars shouldn't trigger + SUPPORTS_DAC; + } CONTRACTL_END; + + EECodeInfo codeInfo(request.GetStartAddress()); + if (!codeInfo.IsValid()) + return FALSE; + + ReadyToRunInfo * pReadyToRunInfo = JitTokenToReadyToRunInfo(codeInfo.GetMethodToken()); + PTR_RUNTIME_FUNCTION pRuntimeFunction = JitTokenToRuntimeFunction(codeInfo.GetMethodToken()); + + PTR_BYTE pDebugInfo = pReadyToRunInfo->GetDebugInfo(pRuntimeFunction); + if (pDebugInfo == NULL) + return FALSE; + + CompressDebugInfo::RestoreRichDebugInfo( + fpNew, pNewData, + pDebugInfo, + ppInlineTree, pNumInlineTree, + ppRichMappings, pNumRichMappings); + + return TRUE; +} + +BOOL ReadyToRunJitManager::GetAsyncDebugInfo( + const DebugInfoRequest & request, + IN FP_IDS_NEW fpNew, IN void * pNewData, + OUT ICorDebugInfo::AsyncInfo* pAsyncInfo, + OUT ICorDebugInfo::AsyncSuspensionPoint** ppSuspensionPoints, + OUT ICorDebugInfo::AsyncContinuationVarInfo** ppAsyncVars, + OUT ULONG32* pcAsyncVars) +{ + CONTRACTL { + THROWS; // on OOM. + GC_NOTRIGGER; // getting vars shouldn't trigger + SUPPORTS_DAC; + } CONTRACTL_END; + + EECodeInfo codeInfo(request.GetStartAddress()); + if (!codeInfo.IsValid()) + return FALSE; + + ReadyToRunInfo * pReadyToRunInfo = JitTokenToReadyToRunInfo(codeInfo.GetMethodToken()); + PTR_RUNTIME_FUNCTION pRuntimeFunction = JitTokenToRuntimeFunction(codeInfo.GetMethodToken()); + + PTR_BYTE pDebugInfo = pReadyToRunInfo->GetDebugInfo(pRuntimeFunction); + if (pDebugInfo == NULL) + return FALSE; + + CompressDebugInfo::RestoreAsyncDebugInfo( + fpNew, pNewData, + pDebugInfo, + pAsyncInfo, + ppSuspensionPoints, + ppAsyncVars, pcAsyncVars); + + return TRUE; } #ifdef DACCESS_COMPILE diff --git a/src/coreclr/vm/codeman.h b/src/coreclr/vm/codeman.h index 8d4c3fb425f4fb..1070db939ff923 100644 --- a/src/coreclr/vm/codeman.h +++ b/src/coreclr/vm/codeman.h @@ -1716,6 +1716,14 @@ class IJitManager OUT ICorDebugInfo::RichOffsetMapping** ppRichMappings, OUT ULONG32* pNumRichMappings) = 0; + virtual BOOL GetAsyncDebugInfo( + const DebugInfoRequest & request, + IN FP_IDS_NEW fpNew, IN void * pNewData, + OUT ICorDebugInfo::AsyncInfo* pAsyncInfo, + OUT ICorDebugInfo::AsyncSuspensionPoint** ppSuspensionPoints, + OUT ICorDebugInfo::AsyncContinuationVarInfo** ppAsyncVars, + OUT ULONG32* pcAsyncVars) = 0; + virtual PCODE GetCodeAddressForRelOffset(const METHODTOKEN& MethodToken, DWORD relOffset) = 0; virtual BOOL JitCodeToMethodInfo( @@ -1901,6 +1909,14 @@ class EECodeGenManager : public IJitManager OUT ICorDebugInfo::RichOffsetMapping** ppRichMappings, OUT ULONG32* pNumRichMappings); + BOOL GetAsyncDebugInfoWorker( + PTR_BYTE pDebugInfo, + IN FP_IDS_NEW fpNew, IN void* pNewData, + OUT ICorDebugInfo::AsyncInfo* pAsyncInfo, + OUT ICorDebugInfo::AsyncSuspensionPoint** ppSuspensionPoints, + OUT ICorDebugInfo::AsyncContinuationVarInfo** ppAsyncVars, + OUT ULONG32* pcAsyncVars); + template BOOL JitCodeToMethodInfoWorker( RangeSection * pRangeSection, @@ -2116,6 +2132,14 @@ class EEJitManager final : public EECodeGenManager OUT ICorDebugInfo::RichOffsetMapping** ppRichMappings, OUT ULONG32* pNumRichMappings); + virtual BOOL GetAsyncDebugInfo( + const DebugInfoRequest & request, + IN FP_IDS_NEW fpNew, IN void * pNewData, + OUT ICorDebugInfo::AsyncInfo* pAsyncInfo, + OUT ICorDebugInfo::AsyncSuspensionPoint** ppSuspensionPoints, + OUT ICorDebugInfo::AsyncContinuationVarInfo** ppAsyncVars, + OUT ULONG32* pcAsyncVars); + virtual PCODE GetCodeAddressForRelOffset(const METHODTOKEN& MethodToken, DWORD relOffset); virtual BOOL JitCodeToMethodInfo(RangeSection * pRangeSection, @@ -2668,6 +2692,14 @@ class ReadyToRunJitManager final : public IJitManager OUT ICorDebugInfo::RichOffsetMapping** ppRichMappings, OUT ULONG32* pNumRichMappings); + virtual BOOL GetAsyncDebugInfo( + const DebugInfoRequest & request, + IN FP_IDS_NEW fpNew, IN void * pNewData, + OUT ICorDebugInfo::AsyncInfo* pAsyncInfo, + OUT ICorDebugInfo::AsyncSuspensionPoint** ppSuspensionPoints, + OUT ICorDebugInfo::AsyncContinuationVarInfo** ppAsyncVars, + OUT ULONG32* pcAsyncVars); + virtual BOOL JitCodeToMethodInfo(RangeSection * pRangeSection, PCODE currentPC, MethodDesc** ppMethodDesc, @@ -2795,6 +2827,14 @@ class InterpreterJitManager final : public EECodeGenManager OUT ICorDebugInfo::RichOffsetMapping** ppRichMappings, OUT ULONG32* pNumRichMappings); + virtual BOOL GetAsyncDebugInfo( + const DebugInfoRequest & request, + IN FP_IDS_NEW fpNew, IN void * pNewData, + OUT ICorDebugInfo::AsyncInfo* pAsyncInfo, + OUT ICorDebugInfo::AsyncSuspensionPoint** ppSuspensionPoints, + OUT ICorDebugInfo::AsyncContinuationVarInfo** ppAsyncVars, + OUT ULONG32* pcAsyncVars); + #ifndef DACCESS_COMPILE virtual TypeHandle ResolveEHClause(EE_ILEXCEPTION_CLAUSE* pEHClause, CrawlFrame *pCf); diff --git a/src/coreclr/vm/debuginfostore.cpp b/src/coreclr/vm/debuginfostore.cpp index 8cd10c29315c11..f743a60eb7cd83 100644 --- a/src/coreclr/vm/debuginfostore.cpp +++ b/src/coreclr/vm/debuginfostore.cpp @@ -1395,6 +1395,15 @@ void CompressDebugInfo::RestoreRichDebugInfo( DebugInfoChunks chunks = DecodeChunks(pDebugInfo); + if (chunks.cbRichDebugInfo == 0) + { + *ppInlineTree = NULL; + *pNumInlineTree = 0; + *ppRichMappings = NULL; + *pNumRichMappings = 0; + return; + } + NibbleReader r(chunks.pRichDebugInfo, chunks.cbRichDebugInfo); *pNumInlineTree = r.ReadEncodedU32(); @@ -1415,6 +1424,53 @@ void CompressDebugInfo::RestoreRichDebugInfo( DoRichOffsetMappings(t, *pNumRichMappings, *ppRichMappings); } +void CompressDebugInfo::RestoreAsyncDebugInfo( + IN FP_IDS_NEW fpNew, + IN void* pNewData, + IN PTR_BYTE pDebugInfo, + OUT ICorDebugInfo::AsyncInfo* pAsyncInfo, + OUT ICorDebugInfo::AsyncSuspensionPoint** ppSuspensionPoints, + OUT ICorDebugInfo::AsyncContinuationVarInfo** ppAsyncVars, + OUT ULONG32* pNumAsyncVars) +{ + CONTRACTL + { + THROWS; + GC_NOTRIGGER; + MODE_ANY; + } + CONTRACTL_END; + + DebugInfoChunks chunks = DecodeChunks(pDebugInfo); + + if (chunks.cbAsyncInfo == 0) + { + *pAsyncInfo = {}; + *ppSuspensionPoints = NULL; + *ppAsyncVars = NULL; + *pNumAsyncVars = 0; + return; + } + + NibbleReader r(chunks.pAsyncInfo, chunks.cbAsyncInfo); + pAsyncInfo->NumSuspensionPoints = r.ReadEncodedU32(); + *pNumAsyncVars = r.ReadEncodedU32(); + + UINT32 cbSuspPoints = pAsyncInfo->NumSuspensionPoints * sizeof(ICorDebugInfo::AsyncSuspensionPoint); + *ppSuspensionPoints = reinterpret_cast(fpNew(pNewData, cbSuspPoints)); + if (*ppSuspensionPoints == NULL) + ThrowOutOfMemory(); + + UINT32 cbAsyncVars = *pNumAsyncVars * sizeof(ICorDebugInfo::AsyncContinuationVarInfo); + *ppAsyncVars = reinterpret_cast(fpNew(pNewData, cbAsyncVars)); + if (*ppAsyncVars == NULL) + ThrowOutOfMemory(); + + TransferReader t(r); + DoAsyncSuspensionPoints(t, pAsyncInfo->NumSuspensionPoints, *ppSuspensionPoints); + DoAsyncVars(t, *pNumAsyncVars, *ppAsyncVars); +} + #ifdef DACCESS_COMPILE void CompressDebugInfo::EnumMemoryRegions(CLRDataEnumMemoryFlags flags, PTR_BYTE pDebugInfo) { @@ -1513,6 +1569,31 @@ BOOL DebugInfoManager::GetRichDebugInfo( return pJitMan->GetRichDebugInfo(request, fpNew, pNewData, ppInlineTree, pNumInlineTree, ppRichMappings, pNumRichMappings); } +BOOL DebugInfoManager::GetAsyncDebugInfo( + const DebugInfoRequest & request, + IN FP_IDS_NEW fpNew, IN void * pNewData, + OUT ICorDebugInfo::AsyncInfo* pAsyncInfo, + OUT ICorDebugInfo::AsyncSuspensionPoint** ppSuspensionPoints, + OUT ICorDebugInfo::AsyncContinuationVarInfo** ppAsyncVars, + OUT ULONG32* pcAsyncVars) +{ + CONTRACTL + { + THROWS; + WRAPPER(GC_TRIGGERS); // depends on fpNew + SUPPORTS_DAC; + } + CONTRACTL_END; + + IJitManager* pJitMan = ExecutionManager::FindJitMan(request.GetStartAddress()); + if (pJitMan == NULL) + { + return FALSE; // no info available. + } + + return pJitMan->GetAsyncDebugInfo(request, fpNew, pNewData, pAsyncInfo, ppSuspensionPoints, ppAsyncVars, pcAsyncVars); +} + #ifdef DACCESS_COMPILE void DebugInfoManager::EnumMemoryRegionsForMethodDebugInfo(CLRDataEnumMemoryFlags flags, EECodeInfo * pCodeInfo) { diff --git a/src/coreclr/vm/debuginfostore.h b/src/coreclr/vm/debuginfostore.h index e926293ea6e1f2..abeac114249964 100644 --- a/src/coreclr/vm/debuginfostore.h +++ b/src/coreclr/vm/debuginfostore.h @@ -168,6 +168,15 @@ class CompressDebugInfo OUT ICorDebugInfo::RichOffsetMapping** ppRichMappings, OUT ULONG32* pNumRichMappings); + static void RestoreAsyncDebugInfo( + IN FP_IDS_NEW fpNew, + IN void* pNewData, + IN PTR_BYTE pDebugInfo, + OUT ICorDebugInfo::AsyncInfo* pAsyncInfo, + OUT ICorDebugInfo::AsyncSuspensionPoint** ppSuspensionPoints, + OUT ICorDebugInfo::AsyncContinuationVarInfo** ppAsyncVars, + OUT ULONG32* pNumAsyncVars); + #ifdef DACCESS_COMPILE static void EnumMemoryRegions(CLRDataEnumMemoryFlags flags, PTR_BYTE pDebugInfo); #endif @@ -199,6 +208,14 @@ class DebugInfoManager OUT ICorDebugInfo::RichOffsetMapping** ppRichMappings, OUT ULONG32* pNumRichMappings); + static BOOL GetAsyncDebugInfo( + const DebugInfoRequest & request, + IN FP_IDS_NEW fpNew, IN void * pNewData, + OUT ICorDebugInfo::AsyncInfo* pAsyncInfo, + OUT ICorDebugInfo::AsyncSuspensionPoint** ppSuspensionPoints, + OUT ICorDebugInfo::AsyncContinuationVarInfo** ppAsyncVars, + OUT ULONG32* pcAsyncVars); + #ifdef DACCESS_COMPILE static void EnumMemoryRegionsForMethodDebugInfo(CLRDataEnumMemoryFlags flags, EECodeInfo * pCodeInfo); #endif From c808390dd2ce5f174dddc388b7f85a834f3ef352 Mon Sep 17 00:00:00 2001 From: Jakob Botsch Nielsen Date: Thu, 2 Oct 2025 16:18:46 +0200 Subject: [PATCH 16/72] Missed bumping R2R version for naot --- src/coreclr/nativeaot/Runtime/inc/ModuleHeaders.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coreclr/nativeaot/Runtime/inc/ModuleHeaders.h b/src/coreclr/nativeaot/Runtime/inc/ModuleHeaders.h index 59ea64bf324bb0..f634629cfbada3 100644 --- a/src/coreclr/nativeaot/Runtime/inc/ModuleHeaders.h +++ b/src/coreclr/nativeaot/Runtime/inc/ModuleHeaders.h @@ -11,7 +11,7 @@ struct ReadyToRunHeaderConstants { static const uint32_t Signature = 0x00525452; // 'RTR' - static const uint32_t CurrentMajorVersion = 16; + static const uint32_t CurrentMajorVersion = 17; static const uint32_t CurrentMinorVersion = 0; }; From 3ec516024c74129db7f55a76d79cdc38625f2201 Mon Sep 17 00:00:00 2001 From: Jakob Botsch Nielsen Date: Thu, 2 Oct 2025 16:46:53 +0200 Subject: [PATCH 17/72] Fix reverse mapping to IL local nums --- src/coreclr/jit/lclvars.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/coreclr/jit/lclvars.cpp b/src/coreclr/jit/lclvars.cpp index 4e6b559c989baf..7f5152fc41a9e7 100644 --- a/src/coreclr/jit/lclvars.cpp +++ b/src/coreclr/jit/lclvars.cpp @@ -1139,6 +1139,11 @@ unsigned Compiler::compMap2ILvarNum(unsigned varNum) const varNum--; } + if (lvaAsyncContinuationArg != BAD_VAR_NUM && varNum > lvaAsyncContinuationArg) + { + varNum--; + } + /* Is there a hidden argument for the return buffer. Note that this code works because if the RetBuffArg is not present, compRetBuffArg will be BAD_VAR_NUM */ From bb12c776044f311895dbc6c3a94a7c02a471e498 Mon Sep 17 00:00:00 2001 From: Jakob Botsch Nielsen Date: Thu, 2 Oct 2025 16:47:15 +0200 Subject: [PATCH 18/72] Fix monotonicity for async vars --- src/coreclr/vm/debuginfostore.cpp | 27 +++++++++++++++++---------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/src/coreclr/vm/debuginfostore.cpp b/src/coreclr/vm/debuginfostore.cpp index f743a60eb7cd83..5014d2e200ef28 100644 --- a/src/coreclr/vm/debuginfostore.cpp +++ b/src/coreclr/vm/debuginfostore.cpp @@ -490,19 +490,26 @@ static void DoAsyncSuspensionPoints( template static void DoAsyncVars( T trans, + ULONG32 cSuspensionPoints, + ICorDebugInfo::AsyncSuspensionPoint* suspensionPoints, ULONG32 cVars, ICorDebugInfo::AsyncContinuationVarInfo* vars) { - uint32_t lastOffset = 0; - for (uint32_t i = 0; i < cVars; i++) + uint32_t varIndex = 0; + for (uint32_t i = 0; i < cSuspensionPoints; i++) { - ICorDebugInfo::AsyncContinuationVarInfo* var = &vars[i]; - - trans.DoEncodedAdjustedU32(var->VarNumber, (DWORD) ICorDebugInfo::MAX_ILNUM); - - trans.DoEncodedDeltaU32(var->Offset, lastOffset); - lastOffset = var->Offset; + uint32_t lastOffset = 0; + uint32_t numVars = suspensionPoints[i].NumContinuationVars; + for (uint32_t j = 0; j < numVars; j++) + { + ICorDebugInfo::AsyncContinuationVarInfo* var = &vars[varIndex++]; + trans.DoEncodedAdjustedU32(var->VarNumber, (DWORD) ICorDebugInfo::MAX_ILNUM); + trans.DoEncodedDeltaU32(var->Offset, lastOffset); + lastOffset = var->Offset; + } } + + _ASSERTE(varIndex == cVars); } #ifndef DACCESS_COMPILE @@ -760,7 +767,7 @@ void CompressDebugInfo::CompressAsyncDebugInfo( TransferWriter t(*pWriter); DoAsyncSuspensionPoints(t, asyncInfo->NumSuspensionPoints, pSuspensionPoints); - DoAsyncVars(t, iAsyncVars, pAsyncVars); + DoAsyncVars(t, asyncInfo->NumSuspensionPoints, pSuspensionPoints, iAsyncVars, pAsyncVars); pWriter->Flush(); @@ -1468,7 +1475,7 @@ void CompressDebugInfo::RestoreAsyncDebugInfo( TransferReader t(r); DoAsyncSuspensionPoints(t, pAsyncInfo->NumSuspensionPoints, *ppSuspensionPoints); - DoAsyncVars(t, *pNumAsyncVars, *ppAsyncVars); + DoAsyncVars(t, pAsyncInfo->NumSuspensionPoints, *ppSuspensionPoints, *pNumAsyncVars, *ppAsyncVars); } #ifdef DACCESS_COMPILE From bf3364bd8a0e4e09ec3e7d51fe72dbb589efee17 Mon Sep 17 00:00:00 2001 From: Jakob Botsch Nielsen Date: Thu, 2 Oct 2025 16:50:04 +0200 Subject: [PATCH 19/72] Code style --- src/coreclr/jit/lclvars.cpp | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/coreclr/jit/lclvars.cpp b/src/coreclr/jit/lclvars.cpp index 7f5152fc41a9e7..e05714a2e0a6d1 100644 --- a/src/coreclr/jit/lclvars.cpp +++ b/src/coreclr/jit/lclvars.cpp @@ -1109,14 +1109,14 @@ unsigned Compiler::compMap2ILvarNum(unsigned varNum) const } // Is this a varargs function? - if (info.compIsVarArgs && varNum == lvaVarargsHandleArg) + if (info.compIsVarArgs && (varNum == lvaVarargsHandleArg)) { return (unsigned)ICorDebugInfo::VARARGS_HND_ILNUM; } // We create an extra argument for the type context parameter // needed for shared generic code. - if ((info.compMethodInfo->args.callConv & CORINFO_CALLCONV_PARAMTYPE) && varNum == info.compTypeCtxtArg) + if (((info.compMethodInfo->args.callConv & CORINFO_CALLCONV_PARAMTYPE) != 0) && (varNum == info.compTypeCtxtArg)) { return (unsigned)ICorDebugInfo::TYPECTXT_ILNUM; } @@ -1129,25 +1129,25 @@ unsigned Compiler::compMap2ILvarNum(unsigned varNum) const #endif // FEATURE_FIXED_OUT_ARGS // Now mutate varNum to remove extra parameters from the count. - if ((info.compMethodInfo->args.callConv & CORINFO_CALLCONV_PARAMTYPE) && varNum > info.compTypeCtxtArg) + if (((info.compMethodInfo->args.callConv & CORINFO_CALLCONV_PARAMTYPE) != 0) && (varNum > info.compTypeCtxtArg)) { varNum--; } - if (info.compIsVarArgs && varNum > lvaVarargsHandleArg) + if (info.compIsVarArgs && (varNum > lvaVarargsHandleArg)) { varNum--; } - if (lvaAsyncContinuationArg != BAD_VAR_NUM && varNum > lvaAsyncContinuationArg) + if ((lvaAsyncContinuationArg != BAD_VAR_NUM) && (varNum > lvaAsyncContinuationArg)) { varNum--; } - /* Is there a hidden argument for the return buffer. - Note that this code works because if the RetBuffArg is not present, - compRetBuffArg will be BAD_VAR_NUM */ - if (info.compRetBuffArg != BAD_VAR_NUM && varNum > info.compRetBuffArg) + // Is there a hidden argument for the return buffer. Note that this code + // works because if the RetBuffArg is not present, compRetBuffArg will be + // BAD_VAR_NUM + if ((info.compRetBuffArg != BAD_VAR_NUM) && (varNum > info.compRetBuffArg)) { varNum--; } From b49f38f00dda1d60e3dc92ff06379ea79502c104 Mon Sep 17 00:00:00 2001 From: Jakob Botsch Nielsen Date: Thu, 2 Oct 2025 16:57:42 +0200 Subject: [PATCH 20/72] Fix JIT-EE prompt tools from Egor's instructions, allow use of experimental APIs in async tests --- .github/prompts/add-new-jit-ee-api.prompt.md | 16 ++++++++-------- src/tests/async/Directory.Build.props | 2 +- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/.github/prompts/add-new-jit-ee-api.prompt.md b/.github/prompts/add-new-jit-ee-api.prompt.md index ce3f56b937f6f5..81c5de02328e65 100644 --- a/.github/prompts/add-new-jit-ee-api.prompt.md +++ b/.github/prompts/add-new-jit-ee-api.prompt.md @@ -1,23 +1,23 @@ --- mode: 'agent' -tools: ['githubRepo', 'codebase', 'terminalLastCommand'] +tools: ['fetch', 'codebase', 'runCommands', 'usages', 'search', 'think'] description: 'Add a new API to the JIT-VM (aka JIT-EE) interface in the codebase.' --- #### 1 — Goal -Implement **one** new JIT-VM (also known as JIT-EE) API and all supporting glue. +Implement **one** new JIT-VM (also known as JIT-EE) API and all supporting glue. The JIT-VM interface defines the APIs through which the JIT compiler communicates with the runtime (VM). #### 2 — Prerequisites for the model * You have full repo access -* You may run scripts (e.g., `.sh` or `.bat`) +* You may run scripts (e.g., `.sh` or `.bat`) * Ask **clarifying questions** before the first code change if anything (signature, types, platform constraints) is unclear. #### 3 — Required user inputs -Ask the user for a C-like signature of the new API if it's not provided. +Ask the user for a C-like signature of the new API if it's not provided. Suggest `/src/coreclr/tools/Common/JitInterface/ThunkGenerator/ThunkInput.txt` file as a reference. Example: ``` @@ -83,8 +83,8 @@ Use the correct directory for the script to run. +} ``` -6. Now implement the most complex part - SuperPMI. SuperPMI acts as a (de)serializer for JIT-VM queries in order -to then replay them without the actual VM to speed up jit-diffs and other scenarios. All parameters and return +6. Now implement the most complex part - SuperPMI. SuperPMI acts as a (de)serializer for JIT-VM queries in order +to then replay them without the actual VM to speed up jit-diffs and other scenarios. All parameters and return values recorded/restored using special primitve types and helpers. We need to update the following files: * `/src/coreclr/tools/superpmi/superpmi-shared/agnostic.h`: @@ -96,7 +96,7 @@ Go through each of them one by one. * `/src/coreclr/tools/superpmi/superpmi-shared/agnostic.h`: Define two `Agnostic_*` types for input arguments and another one for output parameters (return value, output arguments). - Do not create them if one of the generics ones can be re-used such as `DLD`, `DD`, `DLDL`, etc. Use `DWORD*` + Do not create them if one of the generics ones can be re-used such as `DLD`, `DD`, `DLDL`, etc. Use `DWORD*` like types for integers. Inspect the whole file to see how other APIs are defined. * `/src/coreclr/tools/superpmi/superpmi-shared/lwmlist.h`: @@ -126,7 +126,7 @@ Now add a new element to `enum mcPackets` enum in the same file. Example: ``` * `/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.cpp`: -Add the implementation of the 3 methods to `methodcontext.cpp` at the end of it. +Add the implementation of the 3 methods to `methodcontext.cpp` at the end of it. Consider other similar methods in the file for reference. Do not change implementations of other methods in the file. Example: ```diff diff --git a/src/tests/async/Directory.Build.props b/src/tests/async/Directory.Build.props index a1f7d48cf60a7d..f505092384b1b1 100644 --- a/src/tests/async/Directory.Build.props +++ b/src/tests/async/Directory.Build.props @@ -5,7 +5,7 @@ true - $(NoWarn);xUnit1013;CS1998 + $(NoWarn);xUnit1013;CS1998;SYSLIB5007 false $(Features);runtime-async=on From d1bf49f767ccd120f4aa86675cbacdf2a685bf33 Mon Sep 17 00:00:00 2001 From: Jakob Botsch Nielsen Date: Fri, 3 Oct 2025 11:24:16 +0200 Subject: [PATCH 21/72] Address feedback, make comment less misleading --- src/coreclr/inc/cordebuginfo.h | 2 +- src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs | 1 - src/coreclr/tools/superpmi/superpmi/icorjitinfo.cpp | 1 - 3 files changed, 1 insertion(+), 3 deletions(-) diff --git a/src/coreclr/inc/cordebuginfo.h b/src/coreclr/inc/cordebuginfo.h index 1611dde47878f1..75f80f0c86af3e 100644 --- a/src/coreclr/inc/cordebuginfo.h +++ b/src/coreclr/inc/cordebuginfo.h @@ -434,7 +434,7 @@ class ICorDebugInfo struct AsyncContinuationVarInfo { - // IL number of variable (or one of the special IL numbers, like UNKNOWN_ILNUM) + // IL number of variable (or one of the special IL numbers, like TYPECTXT_ILNUM) uint32_t VarNumber; // Offset in continuation's data where this variable is stored uint32_t Offset; diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs index 2a8ce050d7329d..4b21f348999cce 100644 --- a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs +++ b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs @@ -3219,7 +3219,6 @@ private void reportRichMappings(InlineTreeNode* inlineTree, uint numInlineTree, private void reportAsyncDebugInfo(AsyncInfo* asyncInfo, AsyncSuspensionPoint* suspensionPoints, AsyncContinuationVarInfo* vars, uint numVars) #pragma warning restore CA1822 // Mark members as static { - Marshal.FreeHGlobal((IntPtr)asyncInfo); Marshal.FreeHGlobal((IntPtr)suspensionPoints); Marshal.FreeHGlobal((IntPtr)vars); } diff --git a/src/coreclr/tools/superpmi/superpmi/icorjitinfo.cpp b/src/coreclr/tools/superpmi/superpmi/icorjitinfo.cpp index 996e417a50e43d..410430119e847e 100644 --- a/src/coreclr/tools/superpmi/superpmi/icorjitinfo.cpp +++ b/src/coreclr/tools/superpmi/superpmi/icorjitinfo.cpp @@ -1065,7 +1065,6 @@ void MyICJI::reportAsyncDebugInfo( { jitInstance->mc->cr->AddCall("reportAsyncDebugInfo"); // Compile output that we do not currently save - freeArray(asyncInfo); freeArray(suspensionPoints); freeArray(vars); } From 579714e7db48ee32618fde2a7450645a2e57fcd7 Mon Sep 17 00:00:00 2001 From: Jakob Botsch Nielsen Date: Tue, 7 Oct 2025 00:47:44 +0200 Subject: [PATCH 22/72] Add AsyncContinuationVarInfo.GCIndex --- src/coreclr/inc/cordebuginfo.h | 6 +++++- src/coreclr/jit/async.cpp | 3 ++- .../tools/Common/JitInterface/CorInfoTypes.cs | 1 + src/coreclr/vm/debuginfostore.cpp | 18 +++++------------- 4 files changed, 13 insertions(+), 15 deletions(-) diff --git a/src/coreclr/inc/cordebuginfo.h b/src/coreclr/inc/cordebuginfo.h index 75f80f0c86af3e..73b7f0134a7dd4 100644 --- a/src/coreclr/inc/cordebuginfo.h +++ b/src/coreclr/inc/cordebuginfo.h @@ -436,8 +436,12 @@ class ICorDebugInfo { // IL number of variable (or one of the special IL numbers, like TYPECTXT_ILNUM) uint32_t VarNumber; - // Offset in continuation's data where this variable is stored + // Index in continuation's byte[] data where this variable is stored, or 0xFFFFFFFF if the + // variable does not have any byte[] data uint32_t Offset; + // Index in continuation's object[] data where this variable's GC pointers are stored, or 0xFFFFFFFF + // if the variable does not have any GC pointers + uint32_t GCIndex; }; struct AsyncSuspensionPoint diff --git a/src/coreclr/jit/async.cpp b/src/coreclr/jit/async.cpp index 1e560842352827..62a53d60f42223 100644 --- a/src/coreclr/jit/async.cpp +++ b/src/coreclr/jit/async.cpp @@ -2233,7 +2233,8 @@ void AsyncTransformation::CreateDebugInfoForSuspensionPoint(GenTreeCall* asyncCa ICorDebugInfo::AsyncContinuationVarInfo varInf; varInf.VarNumber = ilVarNum; - varInf.Offset = local.DataOffset; + varInf.Offset = local.DataSize > 0 ? local.DataOffset : UINT_MAX; + varInf.GCIndex = local.GCDataCount > 0 ? local.GCDataIndex : UINT_MAX; m_dbgContinuationVars.push_back(varInf); numLocals++; } diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs b/src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs index ed2a0b0b2d662f..1d7d3d6f6e80c3 100644 --- a/src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs +++ b/src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs @@ -1325,6 +1325,7 @@ public struct AsyncContinuationVarInfo public uint VarNumber; // Offset in continuation's data where this variable is stored public uint Offset; + public uint GCIndex; } public struct AsyncSuspensionPoint diff --git a/src/coreclr/vm/debuginfostore.cpp b/src/coreclr/vm/debuginfostore.cpp index 5014d2e200ef28..b6c7f08e9709c3 100644 --- a/src/coreclr/vm/debuginfostore.cpp +++ b/src/coreclr/vm/debuginfostore.cpp @@ -495,21 +495,13 @@ static void DoAsyncVars( ULONG32 cVars, ICorDebugInfo::AsyncContinuationVarInfo* vars) { - uint32_t varIndex = 0; - for (uint32_t i = 0; i < cSuspensionPoints; i++) + for (uint32_t i = 0; i < cVars; i++) { - uint32_t lastOffset = 0; - uint32_t numVars = suspensionPoints[i].NumContinuationVars; - for (uint32_t j = 0; j < numVars; j++) - { - ICorDebugInfo::AsyncContinuationVarInfo* var = &vars[varIndex++]; - trans.DoEncodedAdjustedU32(var->VarNumber, (DWORD) ICorDebugInfo::MAX_ILNUM); - trans.DoEncodedDeltaU32(var->Offset, lastOffset); - lastOffset = var->Offset; - } + ICorDebugInfo::AsyncContinuationVarInfo* var = &vars[i]; + trans.DoEncodedAdjustedU32(var->VarNumber, (DWORD) ICorDebugInfo::MAX_ILNUM); + trans.DoEncodedAdjustedU32(var->Offset, UINT_MAX); + trans.DoEncodedAdjustedU32(var->GCIndex, UINT_MAX); } - - _ASSERTE(varIndex == cVars); } #ifndef DACCESS_COMPILE From c056793a4d261e2792553b51492244968df557db Mon Sep 17 00:00:00 2001 From: Jakob Botsch Nielsen Date: Wed, 8 Oct 2025 18:03:08 +0200 Subject: [PATCH 23/72] Publish NextContinuation in TLS --- .../CompilerServices/AsyncHelpers.CoreCLR.cs | 49 ++++++++++++++----- 1 file changed, 37 insertions(+), 12 deletions(-) diff --git a/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/AsyncHelpers.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/AsyncHelpers.CoreCLR.cs index ba703738d3c7c3..064d1e209b62e8 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/AsyncHelpers.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/AsyncHelpers.CoreCLR.cs @@ -341,33 +341,53 @@ public static void PostToSyncContext(ThunkTask task, SynchronizationContext sync private static class ThunkTaskCore { + private unsafe struct NextContinuationData + { + public NextContinuationData* Next; + public Continuation* NextContinuation; + } + + // To be used for async stack walking + [ThreadStatic] + private static unsafe NextContinuationData* t_nextContinuation; + public static unsafe void MoveNext(T task) where T : Task where TOps : IThunkTaskOps { ExecutionAndSyncBlockStore contexts = default; contexts.Push(); Continuation continuation = TOps.GetContinuationState(task); + ref NextContinuationData* nextContRef = ref t_nextContinuation; + NextContinuationData nextContinuationData; + nextContinuationData.Next = nextContRef; + nextContinuationData.NextContinuation = &continuation; + + nextContRef = &nextContinuationData; + while (true) { try { - Continuation? newContinuation = continuation.Resume(continuation); + Continuation? curContinuation = continuation; + Debug.Assert(curContinuation != null); + Debug.Assert(curContinuation.Next != null); + continuation = curContinuation.Next; + + Continuation? newContinuation = curContinuation.Resume(curContinuation); if (newContinuation != null) { - newContinuation.Next = continuation.Next; + newContinuation.Next = continuation; HandleSuspended(task); contexts.Pop(); + t_nextContinuation = nextContinuationData.Next; return; } - - Debug.Assert(continuation.Next != null); - continuation = continuation.Next; } catch (Exception ex) { - Continuation nextContinuation = UnwindToPossibleHandler(continuation); - if (nextContinuation.Resume == null) + Continuation handlerContinuation = UnwindToPossibleHandler(continuation); + if (handlerContinuation.Resume == null) { // Tail of AsyncTaskMethodBuilderT.SetException bool successfullySet = ex is OperationCanceledException oce ? @@ -376,6 +396,8 @@ public static unsafe void MoveNext(T task) where T : Task where TOps : contexts.Pop(); + t_nextContinuation = nextContinuationData.Next; + if (!successfullySet) { ThrowHelper.ThrowInvalidOperationException(ExceptionResource.TaskT_TransitionToFinal_AlreadyCompleted); @@ -384,9 +406,8 @@ public static unsafe void MoveNext(T task) where T : Task where TOps : return; } - nextContinuation.SetException(ex); - - continuation = nextContinuation; + handlerContinuation.SetException(ex); + continuation = handlerContinuation; } if (continuation.Resume == null) @@ -395,6 +416,8 @@ public static unsafe void MoveNext(T task) where T : Task where TOps : contexts.Pop(); + t_nextContinuation = nextContinuationData.Next; + if (!successfullySet) { ThrowHelper.ThrowInvalidOperationException(ExceptionResource.TaskT_TransitionToFinal_AlreadyCompleted); @@ -406,6 +429,7 @@ public static unsafe void MoveNext(T task) where T : Task where TOps : if (QueueContinuationFollowUpActionIfNecessary(task, continuation)) { contexts.Pop(); + t_nextContinuation = nextContinuationData.Next; return; } } @@ -415,10 +439,11 @@ 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; + + Debug.Assert(continuation.Next != null); + continuation = continuation.Next; } } From 9679f91bd160e676fb3c7e6e4c960da9a911f015 Mon Sep 17 00:00:00 2001 From: Jakob Botsch Nielsen Date: Wed, 8 Oct 2025 18:05:07 +0200 Subject: [PATCH 24/72] Rename ThunkTask -> RuntimeAsyncTask --- .../CompilerServices/AsyncHelpers.CoreCLR.cs | 70 ++++++++++--------- 1 file changed, 36 insertions(+), 34 deletions(-) diff --git a/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/AsyncHelpers.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/AsyncHelpers.CoreCLR.cs index 064d1e209b62e8..cde4c7ebcf11cb 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/AsyncHelpers.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/AsyncHelpers.CoreCLR.cs @@ -203,7 +203,7 @@ private static unsafe object AllocContinuationResultBox(void* ptr) return RuntimeTypeHandle.InternalAllocNoChecks((MethodTable*)pMT); } - private interface IThunkTaskOps + private interface IRuntimeAsyncTaskOps { static abstract Action GetContinuationAction(T task); static abstract Continuation GetContinuationState(T task); @@ -212,9 +212,11 @@ private interface IThunkTaskOps static abstract void PostToSyncContext(T task, SynchronizationContext syncCtx); } - private sealed class ThunkTask : Task + // Represents execution of a chain of suspended and resuming runtime + // async functions. + private sealed class RuntimeAsyncTask : Task { - public ThunkTask() + public RuntimeAsyncTask() { // We use the base Task's state object field to store the Continuation while posting the task around. // Ensure that state object isn't published out for others to see. @@ -231,31 +233,31 @@ internal override void ExecuteFromThreadPool(Thread threadPoolThread) private void MoveNext() { - ThunkTaskCore.MoveNext, Ops>(this); + RuntimeAsyncTaskCore.DispatchContinuations, Ops>(this); } public void HandleSuspended() { - ThunkTaskCore.HandleSuspended, Ops>(this); + RuntimeAsyncTaskCore.HandleSuspended, Ops>(this); } private static readonly SendOrPostCallback s_postCallback = static state => { - Debug.Assert(state is ThunkTask); - ((ThunkTask)state).MoveNext(); + Debug.Assert(state is RuntimeAsyncTask); + ((RuntimeAsyncTask)state).MoveNext(); }; - private struct Ops : IThunkTaskOps> + private struct Ops : IRuntimeAsyncTaskOps> { - public static Action GetContinuationAction(ThunkTask task) => (Action)task.m_action!; - public static void MoveNext(ThunkTask task) => task.MoveNext(); - public static Continuation GetContinuationState(ThunkTask task) => (Continuation)task.m_stateObject!; - public static void SetContinuationState(ThunkTask task, Continuation value) + public static Action GetContinuationAction(RuntimeAsyncTask task) => (Action)task.m_action!; + public static void MoveNext(RuntimeAsyncTask task) => task.MoveNext(); + public static Continuation GetContinuationState(RuntimeAsyncTask task) => (Continuation)task.m_stateObject!; + public static void SetContinuationState(RuntimeAsyncTask task, Continuation value) { task.m_stateObject = value; } - public static bool SetCompleted(ThunkTask task, Continuation continuation) + public static bool SetCompleted(RuntimeAsyncTask task, Continuation continuation) { T result; if (RuntimeHelpers.IsReferenceOrContainsReferences()) @@ -277,16 +279,16 @@ public static bool SetCompleted(ThunkTask task, Continuation continuation) return task.TrySetResult(result); } - public static void PostToSyncContext(ThunkTask task, SynchronizationContext syncContext) + public static void PostToSyncContext(RuntimeAsyncTask task, SynchronizationContext syncContext) { syncContext.Post(s_postCallback, task); } } } - private sealed class ThunkTask : Task + private sealed class RuntimeAsyncTask : Task { - public ThunkTask() + public RuntimeAsyncTask() { // We use the base Task's state object field to store the Continuation while posting the task around. // Ensure that state object isn't published out for others to see. @@ -303,43 +305,43 @@ internal override void ExecuteFromThreadPool(Thread threadPoolThread) private void MoveNext() { - ThunkTaskCore.MoveNext(this); + RuntimeAsyncTaskCore.DispatchContinuations(this); } public void HandleSuspended() { - ThunkTaskCore.HandleSuspended(this); + RuntimeAsyncTaskCore.HandleSuspended(this); } private static readonly SendOrPostCallback s_postCallback = static state => { - Debug.Assert(state is ThunkTask); - ((ThunkTask)state).MoveNext(); + Debug.Assert(state is RuntimeAsyncTask); + ((RuntimeAsyncTask)state).MoveNext(); }; - private struct Ops : IThunkTaskOps + private struct Ops : IRuntimeAsyncTaskOps { - public static Action GetContinuationAction(ThunkTask task) => (Action)task.m_action!; - public static void MoveNext(ThunkTask task) => task.MoveNext(); - public static Continuation GetContinuationState(ThunkTask task) => (Continuation)task.m_stateObject!; - public static void SetContinuationState(ThunkTask task, Continuation value) + public static Action GetContinuationAction(RuntimeAsyncTask task) => (Action)task.m_action!; + public static void MoveNext(RuntimeAsyncTask task) => task.MoveNext(); + public static Continuation GetContinuationState(RuntimeAsyncTask task) => (Continuation)task.m_stateObject!; + public static void SetContinuationState(RuntimeAsyncTask task, Continuation value) { task.m_stateObject = value; } - public static bool SetCompleted(ThunkTask task, Continuation continuation) + public static bool SetCompleted(RuntimeAsyncTask task, Continuation continuation) { return task.TrySetResult(); } - public static void PostToSyncContext(ThunkTask task, SynchronizationContext syncContext) + public static void PostToSyncContext(RuntimeAsyncTask task, SynchronizationContext syncContext) { syncContext.Post(s_postCallback, task); } } } - private static class ThunkTaskCore + private static class RuntimeAsyncTaskCore { private unsafe struct NextContinuationData { @@ -351,7 +353,7 @@ private unsafe struct NextContinuationData [ThreadStatic] private static unsafe NextContinuationData* t_nextContinuation; - public static unsafe void MoveNext(T task) where T : Task where TOps : IThunkTaskOps + public static unsafe void DispatchContinuations(T task) where T : Task where TOps : IRuntimeAsyncTaskOps { ExecutionAndSyncBlockStore contexts = default; contexts.Push(); @@ -447,7 +449,7 @@ private static Continuation UnwindToPossibleHandler(Continuation continuation) } } - public static void HandleSuspended(T task) where T : Task where TOps : IThunkTaskOps + public static void HandleSuspended(T task) where T : Task where TOps : IRuntimeAsyncTaskOps { Continuation headContinuation = UnlinkHeadContinuation(out INotifyCompletion? notifier); @@ -491,7 +493,7 @@ private static Continuation UnlinkHeadContinuation(out INotifyCompletion? notifi return head; } - private static bool QueueContinuationFollowUpActionIfNecessary(T task, Continuation continuation) where T : Task where TOps : IThunkTaskOps + private static bool QueueContinuationFollowUpActionIfNecessary(T task, Continuation continuation) where T : Task where TOps : IRuntimeAsyncTaskOps { if ((continuation.Flags & CorInfoContinuationFlags.CORINFO_CONTINUATION_CONTINUE_ON_THREAD_POOL) != 0) { @@ -555,7 +557,7 @@ private static bool QueueContinuationFollowUpActionIfNecessary(T task, } } - // Change return type to ThunkTask -- no benefit since this is used for Task returning thunks only + // Change return type to RuntimeAsyncTask -- no benefit since this is used for Task returning thunks only #pragma warning disable CA1859 // When a Task-returning thunk gets a continuation result // it calls here to make a Task that awaits on the current async state. @@ -579,7 +581,7 @@ private static bool QueueContinuationFollowUpActionIfNecessary(T task, continuation.Next = finalContinuation; - ThunkTask result = new(); + RuntimeAsyncTask result = new(); result.HandleSuspended(); return result; } @@ -592,7 +594,7 @@ private static Task FinalizeTaskReturningThunk(Continuation continuation) }; continuation.Next = finalContinuation; - ThunkTask result = new(); + RuntimeAsyncTask result = new(); result.HandleSuspended(); return result; } From e83940957498e0582eda6b1d0f5b58b7c24580cf Mon Sep 17 00:00:00 2001 From: Jakob Botsch Nielsen Date: Thu, 9 Oct 2025 14:03:58 +0200 Subject: [PATCH 25/72] Report native offsets instead --- src/coreclr/inc/cordebuginfo.h | 8 +--- src/coreclr/jit/async.cpp | 55 ++++++-------------------- src/coreclr/jit/async.h | 9 +---- src/coreclr/jit/codegen.h | 3 +- src/coreclr/jit/codegencommon.cpp | 46 +++++++++++++++++++++ src/coreclr/jit/codegenlinear.cpp | 8 ++++ src/coreclr/jit/codegenloongarch64.cpp | 1 + src/coreclr/jit/codegenriscv64.cpp | 1 + src/coreclr/jit/codegenxarch.cpp | 2 + src/coreclr/jit/compiler.h | 12 +++++- src/coreclr/jit/compiler.hpp | 1 + src/coreclr/jit/gentree.cpp | 6 +++ src/coreclr/jit/gentree.h | 23 +++++++++-- src/coreclr/jit/gtlist.h | 1 + src/coreclr/jit/gtstructs.h | 1 + src/coreclr/jit/importercalls.cpp | 9 ++--- src/coreclr/jit/liveness.cpp | 1 + src/coreclr/jit/lsraarm.cpp | 1 + src/coreclr/vm/debuginfostore.cpp | 14 ++----- 19 files changed, 123 insertions(+), 79 deletions(-) diff --git a/src/coreclr/inc/cordebuginfo.h b/src/coreclr/inc/cordebuginfo.h index 73b7f0134a7dd4..3a326b8091aaed 100644 --- a/src/coreclr/inc/cordebuginfo.h +++ b/src/coreclr/inc/cordebuginfo.h @@ -446,12 +446,8 @@ class ICorDebugInfo struct AsyncSuspensionPoint { - // IL offset in the root method that resulted in the creation of this suspension point. - uint32_t RootILOffset; - // Index of inline tree node containing the IL offset (0 for root) - uint32_t Inlinee; - // IL offset that resulted in the creation of the suspension point. - uint32_t ILOffset; + // Logical return address of the async call (join point of synchronous and resuming paths) + uint32_t NativeOffset; // Count of AsyncContinuationVarInfo in array of locals starting where // the previous suspension point's locals end. uint32_t NumContinuationVars; diff --git a/src/coreclr/jit/async.cpp b/src/coreclr/jit/async.cpp index 62a53d60f42223..1925d04819a986 100644 --- a/src/coreclr/jit/async.cpp +++ b/src/coreclr/jit/async.cpp @@ -642,6 +642,9 @@ PhaseStatus AsyncTransformation::Run() return PhaseStatus::MODIFIED_NOTHING; } + m_comp->compSuspensionPoints = new (m_comp, CMK_Async) jitstd::vector(m_comp->getAllocator(CMK_Async)); + m_comp->compAsyncVars = new (m_comp, CMK_Async) jitstd::vector(m_comp->getAllocator(CMK_Async)); + // 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. @@ -760,8 +763,6 @@ PhaseStatus AsyncTransformation::Run() m_comp->fgInvalidateDfsTree(); - ReportDebugInfo(); - return PhaseStatus::MODIFIED_EVERYTHING; } @@ -821,7 +822,7 @@ void AsyncTransformation::Transform( m_resumptionBBs.push_back(resumeBB); - CreateDebugInfoForSuspensionPoint(call, layout); + CreateDebugInfoForSuspensionPoint(call, layout, *remainder); } //------------------------------------------------------------------------ @@ -2212,10 +2213,10 @@ GenTreeStoreInd* AsyncTransformation::StoreAtOffset(GenTree* base, unsigned offs // // Parameters: // asyncCall - Call node resulting in the suspension point -// stateNum - State number that was assigned to the suspension point // layout - Layout of continuation +// joinBB - BB where the synchronous and resumption paths join // -void AsyncTransformation::CreateDebugInfoForSuspensionPoint(GenTreeCall* asyncCall, const ContinuationLayout& layout) +void AsyncTransformation::CreateDebugInfoForSuspensionPoint(GenTreeCall* asyncCall, const ContinuationLayout& layout, BasicBlock* joinBB) { if (!m_comp->opts.compDbgInfo) { @@ -2235,48 +2236,16 @@ void AsyncTransformation::CreateDebugInfoForSuspensionPoint(GenTreeCall* asyncCa varInf.VarNumber = ilVarNum; varInf.Offset = local.DataSize > 0 ? local.DataOffset : UINT_MAX; varInf.GCIndex = local.GCDataCount > 0 ? local.GCDataIndex : UINT_MAX; - m_dbgContinuationVars.push_back(varInf); + m_comp->compAsyncVars->push_back(varInf); numLocals++; } - ICorDebugInfo::AsyncSuspensionPoint suspensionPoint; - const DebugInfo& di = asyncCall->GetAsyncInfo().DebugInfo; - suspensionPoint.RootILOffset = di.GetRoot().GetLocation().GetOffset(); - suspensionPoint.Inlinee = di.GetInlineContext()->GetOrdinal(); - suspensionPoint.ILOffset = di.GetLocation().GetOffset(); - suspensionPoint.NumContinuationVars = numLocals; - - m_dbgSuspensionPoints.push_back(suspensionPoint); -} - -//------------------------------------------------------------------------ -// AsyncTransformation::ReportDebugInfo: -// Report debug info back to EE. -// -void AsyncTransformation::ReportDebugInfo() -{ - if (!m_comp->opts.compDbgInfo) - { - return; - } - - ICorDebugInfo::AsyncInfo asyncInfo; - asyncInfo.NumSuspensionPoints = static_cast(m_dbgSuspensionPoints.size()); - - ICorDebugInfo::AsyncSuspensionPoint* hostSuspensionPoints = - static_cast(m_comp->info.compCompHnd->allocateArray( - m_dbgSuspensionPoints.size() * sizeof(ICorDebugInfo::AsyncSuspensionPoint))); - for (size_t i = 0; i < m_dbgSuspensionPoints.size(); i++) - hostSuspensionPoints[i] = m_dbgSuspensionPoints[i]; - - ICorDebugInfo::AsyncContinuationVarInfo* hostVars = - static_cast(m_comp->info.compCompHnd->allocateArray( - m_dbgContinuationVars.size() * sizeof(ICorDebugInfo::AsyncContinuationVarInfo))); - for (size_t i = 0; i < m_dbgContinuationVars.size(); i++) - hostVars[i] = m_dbgContinuationVars[i]; + AsyncSuspensionPoint suspensionPoint; + suspensionPoint.numContinuationVars = numLocals; + m_comp->compSuspensionPoints->push_back(suspensionPoint); - m_comp->info.compCompHnd->reportAsyncDebugInfo(&asyncInfo, hostSuspensionPoints, hostVars, - static_cast(m_dbgContinuationVars.size())); + GenTree* recordOffset = new (m_comp, GT_RECORD_ASYNC_JOIN) GenTreeRecordAsyncJoin((int)(m_comp->compSuspensionPoints->size() - 1)); + LIR::AsRange(joinBB).InsertAtBeginning(recordOffset); } //------------------------------------------------------------------------ diff --git a/src/coreclr/jit/async.h b/src/coreclr/jit/async.h index b8c0459ef9055e..53ac2bc9274b68 100644 --- a/src/coreclr/jit/async.h +++ b/src/coreclr/jit/async.h @@ -63,10 +63,6 @@ class AsyncTransformation BasicBlock* m_lastResumptionBB = nullptr; BasicBlock* m_sharedReturnBB = nullptr; - // Debug info - jitstd::vector m_dbgContinuationVars; - jitstd::vector m_dbgSuspensionPoints; - bool IsLive(unsigned lclNum); void Transform(BasicBlock* block, GenTreeCall* call, @@ -136,8 +132,7 @@ class AsyncTransformation GenTreeFlags indirFlags = GTF_IND_NONFAULTING); GenTreeStoreInd* StoreAtOffset(GenTree* base, unsigned offset, GenTree* value, var_types storeType); - void CreateDebugInfoForSuspensionPoint(GenTreeCall* asyncCall, const ContinuationLayout& layout); - void ReportDebugInfo(); + void CreateDebugInfoForSuspensionPoint(GenTreeCall* asyncCall, const ContinuationLayout& layout, BasicBlock* joinBB); unsigned GetDataArrayVar(); unsigned GetGCDataArrayVar(); @@ -154,8 +149,6 @@ class AsyncTransformation : m_comp(comp) , m_liveLocalsScratch(comp->getAllocator(CMK_Async)) , m_resumptionBBs(comp->getAllocator(CMK_Async)) - , m_dbgContinuationVars(comp->getAllocator(CMK_Async)) - , m_dbgSuspensionPoints(comp->getAllocator(CMK_Async)) { } diff --git a/src/coreclr/jit/codegen.h b/src/coreclr/jit/codegen.h index e47fe8ab57bbe6..478a05d55678e1 100644 --- a/src/coreclr/jit/codegen.h +++ b/src/coreclr/jit/codegen.h @@ -646,7 +646,6 @@ class CodeGen final : public CodeGenInterface void genAddRichIPMappingHere(const DebugInfo& di); void genReportRichDebugInfo(); - void genRecordRichDebugInfoInlineTree(InlineContext* context, ICorDebugInfo::InlineTreeNode* tree); #ifdef DEBUG @@ -654,6 +653,8 @@ class CodeGen final : public CodeGenInterface void genReportRichDebugInfoInlineTreeToFile(FILE* file, InlineContext* context, bool* first); #endif + void genReportAsyncDebugInfo(); + void genEnsureCodeEmitted(const DebugInfo& di); //------------------------------------------------------------------------- diff --git a/src/coreclr/jit/codegencommon.cpp b/src/coreclr/jit/codegencommon.cpp index 0ffa6e8887f7cd..3edb9fef750789 100644 --- a/src/coreclr/jit/codegencommon.cpp +++ b/src/coreclr/jit/codegencommon.cpp @@ -2201,6 +2201,8 @@ void CodeGen::genEmitUnwindDebugGCandEH() genReportRichDebugInfo(); + genReportAsyncDebugInfo(); + /* Finalize the Local Var info in terms of generated code */ genSetScopeInfo(); @@ -6654,6 +6656,50 @@ void CodeGen::genAddRichIPMappingHere(const DebugInfo& di) compiler->genRichIPmappings.push_back(mapping); } +//------------------------------------------------------------------------ +// genReportAsyncDebugInfo: +// Report async debug info back to EE. +// +void CodeGen::genReportAsyncDebugInfo() +{ + jitstd::vector* suspPoints = compiler->compSuspensionPoints; + if (suspPoints == nullptr) + { + return; + } + + ICorDebugInfo::AsyncInfo asyncInfo; + asyncInfo.NumSuspensionPoints = static_cast(suspPoints->size()); + + ICorDebugInfo::AsyncSuspensionPoint* hostSuspensionPoints = + static_cast(compiler->info.compCompHnd->allocateArray( + suspPoints->size() * sizeof(ICorDebugInfo::AsyncSuspensionPoint))); + for (size_t i = 0; i < suspPoints->size(); i++) + { + AsyncSuspensionPoint& suspPoint = (*suspPoints)[i]; + if (suspPoint.nativeLoc.Valid()) + { + hostSuspensionPoints[i].NativeOffset = suspPoint.nativeLoc.CodeOffset(GetEmitter()); + hostSuspensionPoints[i].NumContinuationVars = suspPoint.numContinuationVars; + } + else + { + hostSuspensionPoints[i].NativeOffset = 0; + hostSuspensionPoints[i].NumContinuationVars = 0; + } + } + + jitstd::vector* asyncVars = compiler->compAsyncVars; + ICorDebugInfo::AsyncContinuationVarInfo* hostVars = + static_cast(compiler->info.compCompHnd->allocateArray( + asyncVars->size() * sizeof(ICorDebugInfo::AsyncContinuationVarInfo))); + for (size_t i = 0; i < asyncVars->size(); i++) + hostVars[i] = (*asyncVars)[i]; + + compiler->info.compCompHnd->reportAsyncDebugInfo(&asyncInfo, hostSuspensionPoints, hostVars, + static_cast(asyncVars->size())); +} + /*============================================================================ * * These are empty stubs to help the late dis-assembler to compile diff --git a/src/coreclr/jit/codegenlinear.cpp b/src/coreclr/jit/codegenlinear.cpp index 339ac1eab1610d..2223e003b92e24 100644 --- a/src/coreclr/jit/codegenlinear.cpp +++ b/src/coreclr/jit/codegenlinear.cpp @@ -462,6 +462,14 @@ void CodeGen::genCodeForBBlist() #endif // DEBUG } + else if (node->OperIs(GT_RECORD_ASYNC_JOIN)) + { + int index = node->AsRecordAsyncJoin()->gtSuspensionPointIndex; + assert(compiler->compSuspensionPoints != nullptr); + assert(index >= 0 && (size_t)index < compiler->compSuspensionPoints->size()); + + (*compiler->compSuspensionPoints)[index].nativeLoc.CaptureLocation(GetEmitter()); + } genCodeForTreeNode(node); if (node->gtHasReg(compiler) && node->IsUnusedValue()) diff --git a/src/coreclr/jit/codegenloongarch64.cpp b/src/coreclr/jit/codegenloongarch64.cpp index c0ecf124ee1da6..6c239f098506d5 100644 --- a/src/coreclr/jit/codegenloongarch64.cpp +++ b/src/coreclr/jit/codegenloongarch64.cpp @@ -4380,6 +4380,7 @@ void CodeGen::genCodeForTreeNode(GenTree* treeNode) break; case GT_IL_OFFSET: + case GT_RECORD_ASYNC_JOIN: // Do nothing; these nodes are simply markers for debug info. break; diff --git a/src/coreclr/jit/codegenriscv64.cpp b/src/coreclr/jit/codegenriscv64.cpp index e581bc44d7029b..c3db7a89653c30 100644 --- a/src/coreclr/jit/codegenriscv64.cpp +++ b/src/coreclr/jit/codegenriscv64.cpp @@ -4381,6 +4381,7 @@ void CodeGen::genCodeForTreeNode(GenTree* treeNode) break; case GT_IL_OFFSET: + case GT_RECORD_ASYNC_JOIN: // Do nothing; these nodes are simply markers for debug info. break; diff --git a/src/coreclr/jit/codegenxarch.cpp b/src/coreclr/jit/codegenxarch.cpp index bde2493ab0d137..58b980b44816ea 100644 --- a/src/coreclr/jit/codegenxarch.cpp +++ b/src/coreclr/jit/codegenxarch.cpp @@ -2343,9 +2343,11 @@ void CodeGen::genCodeForTreeNode(GenTree* treeNode) #endif case GT_IL_OFFSET: + case GT_RECORD_ASYNC_JOIN: // Do nothing; these nodes are simply markers for debug info. break; + #if defined(TARGET_AMD64) case GT_CCMP: genCodeForCCMP(treeNode->AsCCMP()); diff --git a/src/coreclr/jit/compiler.h b/src/coreclr/jit/compiler.h index 263111e04ca82f..08b8e8aa656f5c 100644 --- a/src/coreclr/jit/compiler.h +++ b/src/coreclr/jit/compiler.h @@ -2397,6 +2397,12 @@ struct RichIPMapping DebugInfo debugInfo; }; +struct AsyncSuspensionPoint +{ + emitLocation nativeLoc; + unsigned numContinuationVars = 0; +}; + // Current kind of node threading stored in GenTree::gtPrev and GenTree::gtNext. // See fgNodeThreading for more information. enum class NodeThreading @@ -4601,7 +4607,7 @@ class Compiler CORINFO_CALL_INFO* callInfo, IL_OFFSET rawILOffset); - void impSetupAndSpillForAsyncCall(GenTreeCall* call, OPCODE opcode, unsigned prefixFlags, const DebugInfo& callInstDI); + void impSetupAndSpillForAsyncCall(GenTreeCall* call, OPCODE opcode, unsigned prefixFlags); void impInsertAsyncContinuationForLdvirtftnCall(GenTreeCall* call); @@ -8631,6 +8637,9 @@ class Compiler jitstd::list genIPmappings; jitstd::list genRichIPmappings; + jitstd::vector* compSuspensionPoints = nullptr; + jitstd::vector* compAsyncVars = nullptr; + // Managed RetVal - A side hash table meant to record the mapping from a // GT_CALL node to its debug info. This info is used to emit sequence points // that can be used by debugger to determine the native offset at which the @@ -11727,6 +11736,7 @@ class GenTreeVisitor case GT_PINVOKE_PROLOG: case GT_PINVOKE_EPILOG: case GT_IL_OFFSET: + case GT_RECORD_ASYNC_JOIN: case GT_NOP: case GT_SWIFT_ERROR: case GT_GCPOLL: diff --git a/src/coreclr/jit/compiler.hpp b/src/coreclr/jit/compiler.hpp index 12280cde617228..a1f64ec2cfa87b 100644 --- a/src/coreclr/jit/compiler.hpp +++ b/src/coreclr/jit/compiler.hpp @@ -4522,6 +4522,7 @@ GenTree::VisitResult GenTree::VisitOperands(TVisitor visitor) case GT_PINVOKE_PROLOG: case GT_PINVOKE_EPILOG: case GT_IL_OFFSET: + case GT_RECORD_ASYNC_JOIN: case GT_NOP: case GT_SWIFT_ERROR: case GT_GCPOLL: diff --git a/src/coreclr/jit/gentree.cpp b/src/coreclr/jit/gentree.cpp index dbdb7c7485108d..475e3b27e1906b 100644 --- a/src/coreclr/jit/gentree.cpp +++ b/src/coreclr/jit/gentree.cpp @@ -6724,6 +6724,7 @@ bool GenTree::TryGetUse(GenTree* operand, GenTree*** pUse) case GT_PINVOKE_PROLOG: case GT_PINVOKE_EPILOG: case GT_IL_OFFSET: + case GT_RECORD_ASYNC_JOIN: case GT_NOP: case GT_SWIFT_ERROR: case GT_GCPOLL: @@ -10357,6 +10358,7 @@ GenTreeUseEdgeIterator::GenTreeUseEdgeIterator(GenTree* node) case GT_PINVOKE_PROLOG: case GT_PINVOKE_EPILOG: case GT_IL_OFFSET: + case GT_RECORD_ASYNC_JOIN: case GT_NOP: case GT_SWIFT_ERROR: case GT_GCPOLL: @@ -12443,6 +12445,10 @@ void Compiler::gtDispLeaf(GenTree* tree, IndentStack* indentStack) tree->AsILOffset()->gtStmtDI.Dump(true); break; + case GT_RECORD_ASYNC_JOIN: + printf(" %d", tree->AsRecordAsyncJoin()->gtSuspensionPointIndex); + break; + case GT_JCC: case GT_SETCC: printf(" cond=%s", tree->AsCC()->gtCondition.Name()); diff --git a/src/coreclr/jit/gentree.h b/src/coreclr/jit/gentree.h index d348b04f95f099..51654b1bc572aa 100644 --- a/src/coreclr/jit/gentree.h +++ b/src/coreclr/jit/gentree.h @@ -4400,9 +4400,6 @@ struct AsyncCallInfo bool SaveAndRestoreSynchronizationContextField = false; bool HasSuspensionIndicatorDef = false; unsigned SynchronizationContextLclNum = BAD_VAR_NUM; - - // Exact debug info of call IL instruction - DebugInfo DebugInfo; }; // Return type descriptor of a GT_CALL node. @@ -8287,6 +8284,26 @@ struct GenTreeILOffset : public GenTree #endif }; +// No-op node that records the native offset into async debug info during +// codegen/emit. +struct GenTreeRecordAsyncJoin : public GenTree +{ + int gtSuspensionPointIndex; + + GenTreeRecordAsyncJoin(int suspensionPointIndex) + : GenTree(GT_RECORD_ASYNC_JOIN, TYP_VOID) + , gtSuspensionPointIndex(suspensionPointIndex) + { + } + +#if DEBUGGABLE_GENTREE + GenTreeRecordAsyncJoin() + : GenTree(GT_RECORD_ASYNC_JOIN, TYP_VOID) + { + } +#endif +}; + // GenTreeList: adapter class for forward iteration of the execution order GenTree linked list // using range-based `for`, normally used via Statement::TreeList(), e.g.: // for (GenTree* const tree : stmt->TreeList()) ... diff --git a/src/coreclr/jit/gtlist.h b/src/coreclr/jit/gtlist.h index 93fb4dfd358b37..13bd019a98f470 100644 --- a/src/coreclr/jit/gtlist.h +++ b/src/coreclr/jit/gtlist.h @@ -358,6 +358,7 @@ GTNODE(SWAP , GenTreeOp ,0,0,GTK_BINOP|GTK_NOVALUE|DBK_NOTH GTNODE(COPY , GenTreeCopyOrReload,0,0,GTK_UNOP|DBK_NOTHIR) // Copies a variable from its current location to a register that satisfies GTNODE(RELOAD , GenTreeCopyOrReload,0,0,GTK_UNOP|DBK_NOTHIR) // code generation constraints. The operand is the actual lclVar node. GTNODE(IL_OFFSET , GenTreeILOffset ,0,0,GTK_LEAF|GTK_NOVALUE|DBK_NOTHIR) // marks an IL offset for debugging purposes +GTNODE(RECORD_ASYNC_JOIN, GenTreeRecordAsyncJoin,0,0,GTK_LEAF|GTK_NOVALUE|DBK_NOTHIR) // records native offset of async suspension point for async stackwalking purposes /*****************************************************************************/ #undef GTNODE diff --git a/src/coreclr/jit/gtstructs.h b/src/coreclr/jit/gtstructs.h index b785279801c778..d31917752584f8 100644 --- a/src/coreclr/jit/gtstructs.h +++ b/src/coreclr/jit/gtstructs.h @@ -88,6 +88,7 @@ GTSTRUCT_2(MDArr , GT_MDARR_LENGTH, GT_MDARR_LOWER_BOUND) GTSTRUCT_1(ArrElem , GT_ARR_ELEM) GTSTRUCT_1(RetExpr , GT_RET_EXPR) GTSTRUCT_1(ILOffset , GT_IL_OFFSET) +GTSTRUCT_1(RecordAsyncJoin, GT_RECORD_ASYNC_JOIN) GTSTRUCT_2(CopyOrReload, GT_COPY, GT_RELOAD) GTSTRUCT_1(AddrMode , GT_LEA) GTSTRUCT_1(Qmark , GT_QMARK) diff --git a/src/coreclr/jit/importercalls.cpp b/src/coreclr/jit/importercalls.cpp index f9243f7eade3ba..22d06f00e62be6 100644 --- a/src/coreclr/jit/importercalls.cpp +++ b/src/coreclr/jit/importercalls.cpp @@ -386,7 +386,7 @@ var_types Compiler::impImportCall(OPCODE opcode, if (sig->isAsyncCall()) { - impSetupAndSpillForAsyncCall(call->AsCall(), opcode, prefixFlags, di); + impSetupAndSpillForAsyncCall(call->AsCall(), opcode, prefixFlags); } impPopCallArgs(sig, call->AsCall()); @@ -691,7 +691,7 @@ var_types Compiler::impImportCall(OPCODE opcode, if (sig->isAsyncCall()) { - impSetupAndSpillForAsyncCall(call->AsCall(), opcode, prefixFlags, di); + impSetupAndSpillForAsyncCall(call->AsCall(), opcode, prefixFlags); } // Now create the argument list. @@ -6814,15 +6814,12 @@ void Compiler::impCheckForPInvokeCall( // call - The call // opcode - The IL opcode for the call // prefixFlags - Flags containing context handling information from IL -// callInstDI - Debug info for the exact call instruction // void Compiler::impSetupAndSpillForAsyncCall(GenTreeCall* call, OPCODE opcode, - unsigned prefixFlags, - const DebugInfo& callInstDI) + unsigned prefixFlags) { AsyncCallInfo asyncInfo; - asyncInfo.DebugInfo = callInstDI; if ((prefixFlags & PREFIX_IS_TASK_AWAIT) != 0) { diff --git a/src/coreclr/jit/liveness.cpp b/src/coreclr/jit/liveness.cpp index 05786eb1a9045e..0c4994faf393c2 100644 --- a/src/coreclr/jit/liveness.cpp +++ b/src/coreclr/jit/liveness.cpp @@ -1501,6 +1501,7 @@ void Compiler::fgComputeLifeLIR(VARSET_TP& life, BasicBlock* block, VARSET_VALAR case GT_RETURNTRAP: case GT_PUTARG_STK: case GT_IL_OFFSET: + case GT_RECORD_ASYNC_JOIN: case GT_KEEPALIVE: case GT_SWIFT_ERROR_RET: case GT_GCPOLL: diff --git a/src/coreclr/jit/lsraarm.cpp b/src/coreclr/jit/lsraarm.cpp index 6fe1c76635dee3..58c320805ee8f4 100644 --- a/src/coreclr/jit/lsraarm.cpp +++ b/src/coreclr/jit/lsraarm.cpp @@ -689,6 +689,7 @@ int LinearScan::BuildNode(GenTree* tree) case GT_LCL_ADDR: case GT_PHYSREG: case GT_IL_OFFSET: + case GT_RECORD_ASYNC_JOIN: case GT_LABEL: case GT_PINVOKE_PROLOG: case GT_JCC: diff --git a/src/coreclr/vm/debuginfostore.cpp b/src/coreclr/vm/debuginfostore.cpp index b6c7f08e9709c3..e3f79891622853 100644 --- a/src/coreclr/vm/debuginfostore.cpp +++ b/src/coreclr/vm/debuginfostore.cpp @@ -467,21 +467,13 @@ static void DoAsyncSuspensionPoints( ICorDebugInfo::AsyncSuspensionPoint* suspensionPoints) { // Loop through and transfer each Entry in the Mapping. - uint32_t lastRootILOffset = 0; - uint32_t lastInlinee = 0; - uint32_t lastILOffset = 0; + uint32_t lastNativeOffset = 0; for (uint32_t i = 0; i < cSuspensionPoints; i++) { ICorDebugInfo::AsyncSuspensionPoint* sp = &suspensionPoints[i]; - trans.DoEncodedDeltaU32NonMonotonic(sp->RootILOffset, lastRootILOffset); - lastRootILOffset = sp->RootILOffset; - - trans.DoEncodedDeltaU32NonMonotonic(sp->Inlinee, lastInlinee); - lastInlinee = sp->Inlinee; - - trans.DoEncodedDeltaU32NonMonotonic(sp->ILOffset, lastILOffset); - lastILOffset = sp->ILOffset; + trans.DoEncodedDeltaU32NonMonotonic(sp->NativeOffset, lastNativeOffset); + lastNativeOffset = sp->NativeOffset; trans.DoEncodedU32(sp->NumContinuationVars); } From d8ad0c1ce96eb4a050474241d07f21271a46de3e Mon Sep 17 00:00:00 2001 From: Jakob Botsch Nielsen Date: Thu, 9 Oct 2025 14:04:32 +0200 Subject: [PATCH 26/72] Run jit-format --- src/coreclr/jit/async.cpp | 13 +++++++++---- src/coreclr/jit/async.h | 4 +++- src/coreclr/jit/codegencommon.cpp | 14 ++++++-------- src/coreclr/jit/codegenxarch.cpp | 1 - src/coreclr/jit/compiler.h | 4 ++-- src/coreclr/jit/importercalls.cpp | 4 +--- 6 files changed, 21 insertions(+), 19 deletions(-) diff --git a/src/coreclr/jit/async.cpp b/src/coreclr/jit/async.cpp index 1925d04819a986..559910ee81aff1 100644 --- a/src/coreclr/jit/async.cpp +++ b/src/coreclr/jit/async.cpp @@ -642,8 +642,10 @@ PhaseStatus AsyncTransformation::Run() return PhaseStatus::MODIFIED_NOTHING; } - m_comp->compSuspensionPoints = new (m_comp, CMK_Async) jitstd::vector(m_comp->getAllocator(CMK_Async)); - m_comp->compAsyncVars = new (m_comp, CMK_Async) jitstd::vector(m_comp->getAllocator(CMK_Async)); + m_comp->compSuspensionPoints = + new (m_comp, CMK_Async) jitstd::vector(m_comp->getAllocator(CMK_Async)); + m_comp->compAsyncVars = new (m_comp, CMK_Async) + jitstd::vector(m_comp->getAllocator(CMK_Async)); // 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 @@ -2216,7 +2218,9 @@ GenTreeStoreInd* AsyncTransformation::StoreAtOffset(GenTree* base, unsigned offs // layout - Layout of continuation // joinBB - BB where the synchronous and resumption paths join // -void AsyncTransformation::CreateDebugInfoForSuspensionPoint(GenTreeCall* asyncCall, const ContinuationLayout& layout, BasicBlock* joinBB) +void AsyncTransformation::CreateDebugInfoForSuspensionPoint(GenTreeCall* asyncCall, + const ContinuationLayout& layout, + BasicBlock* joinBB) { if (!m_comp->opts.compDbgInfo) { @@ -2244,7 +2248,8 @@ void AsyncTransformation::CreateDebugInfoForSuspensionPoint(GenTreeCall* asyncCa suspensionPoint.numContinuationVars = numLocals; m_comp->compSuspensionPoints->push_back(suspensionPoint); - GenTree* recordOffset = new (m_comp, GT_RECORD_ASYNC_JOIN) GenTreeRecordAsyncJoin((int)(m_comp->compSuspensionPoints->size() - 1)); + GenTree* recordOffset = + new (m_comp, GT_RECORD_ASYNC_JOIN) GenTreeRecordAsyncJoin((int)(m_comp->compSuspensionPoints->size() - 1)); LIR::AsRange(joinBB).InsertAtBeginning(recordOffset); } diff --git a/src/coreclr/jit/async.h b/src/coreclr/jit/async.h index 53ac2bc9274b68..55c2d0cc8653a6 100644 --- a/src/coreclr/jit/async.h +++ b/src/coreclr/jit/async.h @@ -132,7 +132,9 @@ class AsyncTransformation GenTreeFlags indirFlags = GTF_IND_NONFAULTING); GenTreeStoreInd* StoreAtOffset(GenTree* base, unsigned offset, GenTree* value, var_types storeType); - void CreateDebugInfoForSuspensionPoint(GenTreeCall* asyncCall, const ContinuationLayout& layout, BasicBlock* joinBB); + void CreateDebugInfoForSuspensionPoint(GenTreeCall* asyncCall, + const ContinuationLayout& layout, + BasicBlock* joinBB); unsigned GetDataArrayVar(); unsigned GetGCDataArrayVar(); diff --git a/src/coreclr/jit/codegencommon.cpp b/src/coreclr/jit/codegencommon.cpp index 3edb9fef750789..c12e6a325ed2e4 100644 --- a/src/coreclr/jit/codegencommon.cpp +++ b/src/coreclr/jit/codegencommon.cpp @@ -6671,28 +6671,26 @@ void CodeGen::genReportAsyncDebugInfo() ICorDebugInfo::AsyncInfo asyncInfo; asyncInfo.NumSuspensionPoints = static_cast(suspPoints->size()); - ICorDebugInfo::AsyncSuspensionPoint* hostSuspensionPoints = - static_cast(compiler->info.compCompHnd->allocateArray( - suspPoints->size() * sizeof(ICorDebugInfo::AsyncSuspensionPoint))); + ICorDebugInfo::AsyncSuspensionPoint* hostSuspensionPoints = static_cast( + compiler->info.compCompHnd->allocateArray(suspPoints->size() * sizeof(ICorDebugInfo::AsyncSuspensionPoint))); for (size_t i = 0; i < suspPoints->size(); i++) { AsyncSuspensionPoint& suspPoint = (*suspPoints)[i]; if (suspPoint.nativeLoc.Valid()) { - hostSuspensionPoints[i].NativeOffset = suspPoint.nativeLoc.CodeOffset(GetEmitter()); + hostSuspensionPoints[i].NativeOffset = suspPoint.nativeLoc.CodeOffset(GetEmitter()); hostSuspensionPoints[i].NumContinuationVars = suspPoint.numContinuationVars; } else { - hostSuspensionPoints[i].NativeOffset = 0; + hostSuspensionPoints[i].NativeOffset = 0; hostSuspensionPoints[i].NumContinuationVars = 0; } } jitstd::vector* asyncVars = compiler->compAsyncVars; - ICorDebugInfo::AsyncContinuationVarInfo* hostVars = - static_cast(compiler->info.compCompHnd->allocateArray( - asyncVars->size() * sizeof(ICorDebugInfo::AsyncContinuationVarInfo))); + ICorDebugInfo::AsyncContinuationVarInfo* hostVars = static_cast( + compiler->info.compCompHnd->allocateArray(asyncVars->size() * sizeof(ICorDebugInfo::AsyncContinuationVarInfo))); for (size_t i = 0; i < asyncVars->size(); i++) hostVars[i] = (*asyncVars)[i]; diff --git a/src/coreclr/jit/codegenxarch.cpp b/src/coreclr/jit/codegenxarch.cpp index 58b980b44816ea..1ebb7ad5d587e9 100644 --- a/src/coreclr/jit/codegenxarch.cpp +++ b/src/coreclr/jit/codegenxarch.cpp @@ -2347,7 +2347,6 @@ void CodeGen::genCodeForTreeNode(GenTree* treeNode) // Do nothing; these nodes are simply markers for debug info. break; - #if defined(TARGET_AMD64) case GT_CCMP: genCodeForCCMP(treeNode->AsCCMP()); diff --git a/src/coreclr/jit/compiler.h b/src/coreclr/jit/compiler.h index 08b8e8aa656f5c..b9858aa0a019c8 100644 --- a/src/coreclr/jit/compiler.h +++ b/src/coreclr/jit/compiler.h @@ -8637,8 +8637,8 @@ class Compiler jitstd::list genIPmappings; jitstd::list genRichIPmappings; - jitstd::vector* compSuspensionPoints = nullptr; - jitstd::vector* compAsyncVars = nullptr; + jitstd::vector* compSuspensionPoints = nullptr; + jitstd::vector* compAsyncVars = nullptr; // Managed RetVal - A side hash table meant to record the mapping from a // GT_CALL node to its debug info. This info is used to emit sequence points diff --git a/src/coreclr/jit/importercalls.cpp b/src/coreclr/jit/importercalls.cpp index 22d06f00e62be6..2577343545f4a9 100644 --- a/src/coreclr/jit/importercalls.cpp +++ b/src/coreclr/jit/importercalls.cpp @@ -6815,9 +6815,7 @@ void Compiler::impCheckForPInvokeCall( // opcode - The IL opcode for the call // prefixFlags - Flags containing context handling information from IL // -void Compiler::impSetupAndSpillForAsyncCall(GenTreeCall* call, - OPCODE opcode, - unsigned prefixFlags) +void Compiler::impSetupAndSpillForAsyncCall(GenTreeCall* call, OPCODE opcode, unsigned prefixFlags) { AsyncCallInfo asyncInfo; From 9fdd8a70bab957ef4f0795292f82df9ab307af11 Mon Sep 17 00:00:00 2001 From: Jakob Botsch Nielsen Date: Thu, 9 Oct 2025 14:49:53 +0200 Subject: [PATCH 27/72] Print reported async debug info, always report it --- src/coreclr/jit/async.cpp | 5 ----- src/coreclr/jit/codegencommon.cpp | 19 +++++++++++++++++++ 2 files changed, 19 insertions(+), 5 deletions(-) diff --git a/src/coreclr/jit/async.cpp b/src/coreclr/jit/async.cpp index 559910ee81aff1..830e038ca814f1 100644 --- a/src/coreclr/jit/async.cpp +++ b/src/coreclr/jit/async.cpp @@ -2222,11 +2222,6 @@ void AsyncTransformation::CreateDebugInfoForSuspensionPoint(GenTreeCall* const ContinuationLayout& layout, BasicBlock* joinBB) { - if (!m_comp->opts.compDbgInfo) - { - return; - } - uint32_t numLocals = 0; for (const LiveLocalInfo& local : layout.Locals) { diff --git a/src/coreclr/jit/codegencommon.cpp b/src/coreclr/jit/codegencommon.cpp index c12e6a325ed2e4..4153e2a3abe0a7 100644 --- a/src/coreclr/jit/codegencommon.cpp +++ b/src/coreclr/jit/codegencommon.cpp @@ -6696,6 +6696,25 @@ void CodeGen::genReportAsyncDebugInfo() compiler->info.compCompHnd->reportAsyncDebugInfo(&asyncInfo, hostSuspensionPoints, hostVars, static_cast(asyncVars->size())); + +#ifdef DEBUG + if (verbose) + { + printf("Reported async suspension points:\n"); + for (size_t i = 0; i < suspPoints->size(); i++) + { + printf(" [%zu] Offset = %x, NumAsyncVars = %u\n", i, hostSuspensionPoints[i].NativeOffset, + hostSuspensionPoints[i].NumContinuationVars); + } + + printf("Reported async vars:\n"); + for (size_t i = 0; i < asyncVars->size(); i++) + { + printf(" [%zu] VarNumber = %u, Offset = %x, GCIndex = %u\n", i, hostVars[i].VarNumber, hostVars[i].Offset, + hostVars[i].GCIndex); + } + } +#endif } /*============================================================================ From 69e02db8961eb7e2aee3db12ceed5e265b9b3736 Mon Sep 17 00:00:00 2001 From: Jakob Botsch Nielsen Date: Tue, 14 Oct 2025 16:08:47 +0200 Subject: [PATCH 28/72] Store target IPs in AsyncResumeILStubResolver --- src/coreclr/vm/ilstubcache.cpp | 24 +++++++---- src/coreclr/vm/ilstubcache.h | 6 ++- src/coreclr/vm/ilstubresolver.cpp | 22 ++++++++++ src/coreclr/vm/ilstubresolver.h | 18 ++++++++ src/coreclr/vm/jitinterface.cpp | 71 ++++++++++++++++--------------- src/coreclr/vm/jitinterface.h | 4 +- 6 files changed, 99 insertions(+), 46 deletions(-) diff --git a/src/coreclr/vm/ilstubcache.cpp b/src/coreclr/vm/ilstubcache.cpp index 13f6891133f56a..e5422bd3ea3ee9 100644 --- a/src/coreclr/vm/ilstubcache.cpp +++ b/src/coreclr/vm/ilstubcache.cpp @@ -70,7 +70,7 @@ void CreateModuleIndependentSignature(LoaderHeap* pCreationHeap, // static MethodDesc* ILStubCache::CreateAndLinkNewILStubMethodDesc(LoaderAllocator* pAllocator, MethodTable* pMT, DWORD dwStubFlags, Module* pSigModule, PCCOR_SIGNATURE pSig, DWORD cbSig, SigTypeContext *pTypeContext, - ILStubLinker* pStubLinker, BOOL isAsync /* = FALSE */) + ILStubLinker* pStubLinker, BOOL isAsync /* = FALSE */, ILStubResolver* pResolver /* = NULL */) { CONTRACT (MethodDesc*) { @@ -88,13 +88,14 @@ MethodDesc* ILStubCache::CreateAndLinkNewILStubMethodDesc(LoaderAllocator* pAllo pSig, cbSig, isAsync, pTypeContext, - &amTracker); + &amTracker, + pResolver); amTracker.SuppressRelease(); pStubLinker->SetStubMethodDesc(pStubMD); - ILStubResolver *pResolver = pStubMD->AsDynamicMethodDesc()->GetILStubResolver(); + pResolver = pStubMD->AsDynamicMethodDesc()->GetILStubResolver(); pResolver->SetStubMethodDesc(pStubMD); @@ -148,7 +149,7 @@ namespace // static MethodDesc* ILStubCache::CreateNewMethodDesc(LoaderHeap* pCreationHeap, MethodTable* pMT, DWORD dwStubFlags, Module* pSigModule, PCCOR_SIGNATURE pSig, DWORD cbSig, BOOL isAsync, SigTypeContext *pTypeContext, - AllocMemTracker* pamTracker) + AllocMemTracker* pamTracker, ILStubResolver* pResolver) { CONTRACT (MethodDesc*) { @@ -216,12 +217,19 @@ MethodDesc* ILStubCache::CreateNewMethodDesc(LoaderHeap* pCreationHeap, MethodTa pMD->SetStatic(); } - pMD->m_pResolver = (ILStubResolver*)pamTracker->Track(pCreationHeap->AllocMem(S_SIZE_T(sizeof(ILStubResolver)))); + if (pResolver != NULL) + { + pMD->m_pResolver = pResolver; + } + else + { + pMD->m_pResolver = (ILStubResolver*)pamTracker->Track(pCreationHeap->AllocMem(S_SIZE_T(sizeof(ILStubResolver)))); #ifdef _DEBUG - // Poison the ILStubResolver storage - memset((void*)pMD->m_pResolver, 0xCC, sizeof(ILStubResolver)); + // Poison the ILStubResolver storage + memset((void*)pMD->m_pResolver, 0xCC, sizeof(ILStubResolver)); #endif // _DEBUG - pMD->m_pResolver = new (pMD->m_pResolver) ILStubResolver(); + pMD->m_pResolver = new (pMD->m_pResolver) ILStubResolver(); + } if (SF_IsArrayOpStub(dwStubFlags)) { diff --git a/src/coreclr/vm/ilstubcache.h b/src/coreclr/vm/ilstubcache.h index 52be99d9fb8ef1..873769d61b6aa7 100644 --- a/src/coreclr/vm/ilstubcache.h +++ b/src/coreclr/vm/ilstubcache.h @@ -74,7 +74,8 @@ class ILStubCache final DWORD cbSig, SigTypeContext *pTypeContext, ILStubLinker* pStubLinker, - BOOL isAsync = FALSE); + BOOL isAsync = FALSE, + class ILStubResolver* pResolver = NULL); MethodTable * GetStubMethodTable() { @@ -100,7 +101,8 @@ class ILStubCache final DWORD cbSig, BOOL isAsync, SigTypeContext *pTypeContext, - AllocMemTracker* pamTracker); + AllocMemTracker* pamTracker, + class ILStubResolver* pResolver = NULL); private: // Inner classes struct ILStubCacheEntry diff --git a/src/coreclr/vm/ilstubresolver.cpp b/src/coreclr/vm/ilstubresolver.cpp index 0ab3b1476bccbb..09d395f6c5af7d 100644 --- a/src/coreclr/vm/ilstubresolver.cpp +++ b/src/coreclr/vm/ilstubresolver.cpp @@ -559,3 +559,25 @@ void ILStubResolver::StubGenFailed(ILStubResolver* pResolver) pResolver->ClearCompileTimeState(ILNotYetGenerated); } + +PCODE AsyncResumeILStubResolver::GetFinalResumeMethodStartAddress() +{ + return m_resumeIP; +} + +#ifndef DACCESS_COMPILE +PCODE* AsyncResumeILStubResolver::GetAddrOfResumeMethodStartAddress() +{ + return &m_resumeIP; +} + +void AsyncResumeILStubResolver::SetResumeMethodStartAddress(PCODE ip) +{ + m_resumeIP = ip; +} + +void AsyncResumeILStubResolver::SetFinalResumeMethodStartAddress(PCODE ip) +{ + m_finalResumeIP = ip; +} +#endif diff --git a/src/coreclr/vm/ilstubresolver.h b/src/coreclr/vm/ilstubresolver.h index 4e970423a69da0..6d888cfba79665 100644 --- a/src/coreclr/vm/ilstubresolver.h +++ b/src/coreclr/vm/ilstubresolver.h @@ -108,6 +108,24 @@ class ILStubResolver : public DynamicResolver PTR_LoaderHeap m_loaderHeap; }; +class AsyncResumeILStubResolver : public ILStubResolver +{ +public: + PCODE GetFinalResumeMethodStartAddress(); + +#ifndef DACCESS_COMPILE + PCODE* GetAddrOfResumeMethodStartAddress(); + void SetResumeMethodStartAddress(PCODE ip); + void SetFinalResumeMethodStartAddress(PCODE ip); +#endif + +protected: + // IP to resume in. Can be tier0 code if we suspended in an OSR method. + PCODE m_resumeIP = NULL; + // Final IP resumed in. Will be OSR method IP if suspension happened there. + PCODE m_finalResumeIP = NULL; +}; + typedef Holder, ILStubResolver::StubGenFailed, 0> ILStubGenHolder; #endif // __ILSTUBRESOLVER_H__ diff --git a/src/coreclr/vm/jitinterface.cpp b/src/coreclr/vm/jitinterface.cpp index 654dfff271e5e3..ce1adaa7c2c24f 100644 --- a/src/coreclr/vm/jitinterface.cpp +++ b/src/coreclr/vm/jitinterface.cpp @@ -61,6 +61,7 @@ #include "tailcallhelp.h" #include "patchpointinfo.h" +#include "ilstubresolver.h" // The Stack Overflow probe takes place in the COOPERATIVE_TRANSITION_BEGIN() macro // @@ -11061,10 +11062,34 @@ void CEEJitInfo::PublishFinalCodeAddress(PCODE addr) { LIMITED_METHOD_CONTRACT; - if (m_finalCodeAddressSlot != NULL) + if (m_resumptionStubResolver == NULL) { - *m_finalCodeAddressSlot = addr; + return; } + + m_resumptionStubResolver->SetFinalResumeMethodStartAddress(addr); + m_resumptionStubResolver->SetResumeMethodStartAddress(addr); + +#ifdef FEATURE_TIERED_COMPILATION + // 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) + { +#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); + m_resumptionStubResolver->SetResumeMethodStartAddress((DWORD_PTR)m_pPatchpointInfoFromRuntime->GetTier0EntryPoint()); +#else // !FEATURE_ON_STACK_REPLACEMENT + _ASSERTE(!"Unexpected optimization tier with OSR disabled"); +#endif // FEATURE_ON_STACK_REPLACEMENT + } +#endif } template @@ -14667,35 +14692,10 @@ CORINFO_METHOD_HANDLE CEEJitInfo::getAsyncResumptionStub() numArgs++; #endif -#ifdef FEATURE_TIERED_COMPILATION - // 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) - { -#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 // !FEATURE_ON_STACK_REPLACEMENT - _ASSERTE(!"Unexpected optimization tier with OSR disabled"); -#endif // FEATURE_ON_STACK_REPLACEMENT - } - else -#endif // FEATURE_TIERED_COMPILATION - { - { - m_finalCodeAddressSlot = (PCODE*)amTracker.Track(m_pMethodBeingCompiled->GetLoaderAllocator()->GetHighFrequencyHeap()->AllocMem(S_SIZE_T(sizeof(PCODE)))); - } - - pCode->EmitLDC((DWORD_PTR)m_finalCodeAddressSlot); - pCode->EmitLDIND_I(); - } + void* resolverMem = amTracker.Track(loaderAlloc->GetHighFrequencyHeap()->AllocMem(S_SIZE_T(sizeof(AsyncResumeILStubResolver)))); + m_resumptionStubResolver = new (resolverMem) AsyncResumeILStubResolver(); + pCode->EmitLDC((DWORD_PTR)m_resumptionStubResolver->GetAddrOfResumeMethodStartAddress()); + pCode->EmitLDIND_I(); pCode->EmitCALLI(pCode->GetSigToken(calliSig.GetRawSig(), calliSig.GetRawSigLen()), numArgs, msig.IsReturnTypeVoid() ? 0 : 1); @@ -14821,15 +14821,18 @@ CORINFO_METHOD_HANDLE CEEJitInfo::getAsyncResumptionStub() md->GetModule(), stubSig.GetRawSig(), stubSig.GetRawSigLen(), &emptyCtx, - &sl); + &sl, + FALSE, /* isAsync */ + m_resumptionStubResolver); amTracker.SuppressRelease(); - ILStubResolver *pResolver = result->AsDynamicMethodDesc()->GetILStubResolver(); - pResolver->SetStubTargetMethodDesc(m_pMethodBeingCompiled); + m_resumptionStubResolver->SetStubTargetMethodDesc(m_pMethodBeingCompiled); const char* optimizationTierName = "UnknownTier"; #ifdef FEATURE_TIERED_COMPILATION + PrepareCodeConfig* config = GetThread()->GetCurrentPrepareCodeConfig(); + NativeCodeVersion ncv = config->GetCodeVersion(); switch (ncv.GetOptimizationTier()) { case NativeCodeVersion::OptimizationTier0: optimizationTierName = "Tier0"; break; diff --git a/src/coreclr/vm/jitinterface.h b/src/coreclr/vm/jitinterface.h index 18ade9201b1600..0649f4dbd91f45 100644 --- a/src/coreclr/vm/jitinterface.h +++ b/src/coreclr/vm/jitinterface.h @@ -852,7 +852,7 @@ class CEEJitInfo final : public CEECodeGenInfo m_pPatchpointInfoFromRuntime(NULL), m_ilOffset(0) #endif - , m_finalCodeAddressSlot(NULL) + , m_resumptionStubResolver(NULL) { CONTRACTL { @@ -949,7 +949,7 @@ protected : PatchpointInfo * m_pPatchpointInfoFromRuntime; unsigned m_ilOffset; #endif - PCODE* m_finalCodeAddressSlot; + class AsyncResumeILStubResolver* m_resumptionStubResolver; }; From 68c245b874e1e5e5d82687e729143c9df58ea18b Mon Sep 17 00:00:00 2001 From: Jakob Botsch Nielsen Date: Tue, 14 Oct 2025 16:12:41 +0200 Subject: [PATCH 29/72] Fix bug --- src/coreclr/vm/ilstubresolver.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coreclr/vm/ilstubresolver.cpp b/src/coreclr/vm/ilstubresolver.cpp index 09d395f6c5af7d..5c2fa0fc966421 100644 --- a/src/coreclr/vm/ilstubresolver.cpp +++ b/src/coreclr/vm/ilstubresolver.cpp @@ -562,7 +562,7 @@ void ILStubResolver::StubGenFailed(ILStubResolver* pResolver) PCODE AsyncResumeILStubResolver::GetFinalResumeMethodStartAddress() { - return m_resumeIP; + return m_finalResumeIP; } #ifndef DACCESS_COMPILE From 2031f08b4f9bce28d7a2ab91a00785761631259e Mon Sep 17 00:00:00 2001 From: Jakob Botsch Nielsen Date: Wed, 15 Oct 2025 18:27:12 +0200 Subject: [PATCH 30/72] Remove BBF_INTERNAL from rethrow BB to avoid broken mappings --- src/coreclr/jit/async.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/coreclr/jit/async.cpp b/src/coreclr/jit/async.cpp index 830e038ca814f1..e60f111a4ff86b 100644 --- a/src/coreclr/jit/async.cpp +++ b/src/coreclr/jit/async.cpp @@ -1979,6 +1979,14 @@ BasicBlock* AsyncTransformation::RethrowExceptionOnResumption(BasicBlock* m_comp->fgNewBBinRegion(BBJ_THROW, block, /* runRarely */ true, /* insertAtEnd */ true); JITDUMP(" Created " FMT_BB " to rethrow exception on resumption\n", rethrowExceptionBB->bbNum); + // If this ends up placed after 'block' then ensure it does not break + // debugging. We split 'block' at the call, so a BBF_INTERNAL block after + // it would result in broken debug info. + if ((rethrowExceptionBB->Prev() == block) && !block->HasFlag(BBF_INTERNAL)) + { + rethrowExceptionBB->RemoveFlags(BBF_INTERNAL); + } + BasicBlock* storeResultBB = m_comp->fgNewBBafter(BBJ_ALWAYS, resumeBB, true); JITDUMP(" Created " FMT_BB " to store result when resuming with no exception\n", storeResultBB->bbNum); From b7576b20a13227600950b08b8e1f78a82ebd28d8 Mon Sep 17 00:00:00 2001 From: Jakob Botsch Nielsen Date: Fri, 17 Oct 2025 23:07:44 +0200 Subject: [PATCH 31/72] Trampolines for resumption --- src/coreclr/inc/cordebuginfo.h | 4 +- src/coreclr/jit/async.cpp | 74 ++---------------------- src/coreclr/jit/async.h | 5 -- src/coreclr/jit/block.cpp | 2 +- src/coreclr/jit/codegen.h | 6 ++ src/coreclr/jit/codegenarmarch.cpp | 78 ++++++++++++++++++++++++++ src/coreclr/jit/codegencommon.cpp | 40 ++++++++++--- src/coreclr/jit/codegenlinear.cpp | 53 +++++++++++++++-- src/coreclr/jit/codegenloongarch64.cpp | 53 +++++++++++++++++ src/coreclr/jit/codegenriscv64.cpp | 53 +++++++++++++++++ src/coreclr/jit/codegenxarch.cpp | 70 +++++++++++++++++++++++ src/coreclr/jit/compiler.h | 4 +- src/coreclr/jit/compiler.hpp | 1 + src/coreclr/jit/gentree.cpp | 7 ++- src/coreclr/jit/gentree.h | 20 ------- src/coreclr/jit/gtlist.h | 3 +- src/coreclr/jit/gtstructs.h | 5 +- src/coreclr/vm/debuginfostore.cpp | 11 ++-- 18 files changed, 369 insertions(+), 120 deletions(-) diff --git a/src/coreclr/inc/cordebuginfo.h b/src/coreclr/inc/cordebuginfo.h index 3a326b8091aaed..e80d57323836ff 100644 --- a/src/coreclr/inc/cordebuginfo.h +++ b/src/coreclr/inc/cordebuginfo.h @@ -446,8 +446,10 @@ class ICorDebugInfo struct AsyncSuspensionPoint { + // Offset of this suspension point's resumption point. + uint32_t NativeResumeOffset; // Logical return address of the async call (join point of synchronous and resuming paths) - uint32_t NativeOffset; + uint32_t NativeJoinOffset; // Count of AsyncContinuationVarInfo in array of locals starting where // the previous suspension point's locals end. uint32_t NumContinuationVars; diff --git a/src/coreclr/jit/async.cpp b/src/coreclr/jit/async.cpp index e60f111a4ff86b..d812ab3cafd601 100644 --- a/src/coreclr/jit/async.cpp +++ b/src/coreclr/jit/async.cpp @@ -647,12 +647,6 @@ PhaseStatus AsyncTransformation::Run() m_comp->compAsyncVars = new (m_comp, CMK_Async) jitstd::vector(m_comp->getAllocator(CMK_Async)); - // 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); - 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")); @@ -1308,8 +1302,9 @@ BasicBlock* AsyncTransformation::CreateSuspension( // Fill in 'Resume' GenTree* newContinuation = m_comp->gtNewLclvNode(m_newContinuationVar, TYP_REF); unsigned resumeOffset = m_comp->info.compCompHnd->getFieldOffset(m_asyncInfo->continuationResumeFldHnd); - GenTree* resumeStubAddr = CreateResumptionStubAddrTree(); - GenTree* storeResume = StoreAtOffset(newContinuation, resumeOffset, resumeStubAddr, TYP_I_IMPL); + GenTree* resumeAddr = + new (m_comp, GT_ASYNC_RESUME_TRAMPOLINE) GenTreeVal(GT_ASYNC_RESUME_TRAMPOLINE, TYP_I_IMPL, (ssize_t)stateNum); + GenTree* storeResume = StoreAtOffset(newContinuation, resumeOffset, resumeAddr, TYP_I_IMPL); LIR::AsRange(suspendBB).InsertAtEnd(LIR::SeqTree(m_comp, storeResume)); // Fill in 'state' @@ -2251,8 +2246,8 @@ void AsyncTransformation::CreateDebugInfoForSuspensionPoint(GenTreeCall* suspensionPoint.numContinuationVars = numLocals; m_comp->compSuspensionPoints->push_back(suspensionPoint); - GenTree* recordOffset = - new (m_comp, GT_RECORD_ASYNC_JOIN) GenTreeRecordAsyncJoin((int)(m_comp->compSuspensionPoints->size() - 1)); + GenTree* recordOffset = new (m_comp, GT_RECORD_ASYNC_JOIN) + GenTreeVal(GT_RECORD_ASYNC_JOIN, TYP_VOID, (int)(m_comp->compSuspensionPoints->size() - 1)); LIR::AsRange(joinBB).InsertAtBeginning(recordOffset); } @@ -2339,65 +2334,6 @@ unsigned AsyncTransformation::GetExceptionVar() return m_exceptionVar; } -//------------------------------------------------------------------------ -// AsyncTransformation::CreateResumptionStubAddrTree: -// Create a tree that represents the address of the resumption stub entry -// point. -// -// Returns: -// IR node. -// -GenTree* AsyncTransformation::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; - } - } -} - -//------------------------------------------------------------------------ -// AsyncTransformation::CreateFunctionTargetAddr: -// Create a tree that represents the address of the resumption stub entry -// point. -// -// Returns: -// IR node. -// -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; -} - //------------------------------------------------------------------------ // AsyncTransformation::CreateResumptionSwitch: // Create the IR for the entry of the function that checks the continuation diff --git a/src/coreclr/jit/async.h b/src/coreclr/jit/async.h index 55c2d0cc8653a6..0b26c9f8b3e3c7 100644 --- a/src/coreclr/jit/async.h +++ b/src/coreclr/jit/async.h @@ -51,8 +51,6 @@ class AsyncTransformation jitstd::vector m_liveLocalsScratch; CORINFO_ASYNC_INFO* m_asyncInfo; jitstd::vector 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; @@ -141,9 +139,6 @@ class AsyncTransformation unsigned GetResultBaseVar(); unsigned GetExceptionVar(); - GenTree* CreateResumptionStubAddrTree(); - GenTree* CreateFunctionTargetAddr(CORINFO_METHOD_HANDLE methHnd, const CORINFO_CONST_LOOKUP& lookup); - void CreateResumptionSwitch(); public: diff --git a/src/coreclr/jit/block.cpp b/src/coreclr/jit/block.cpp index f36e55e216a5aa..bcbe4c6046eb85 100644 --- a/src/coreclr/jit/block.cpp +++ b/src/coreclr/jit/block.cpp @@ -1206,7 +1206,7 @@ bool BasicBlock::endsWithJmpMethod(Compiler* comp) const } // Returns true if the basic block ends with either -// i) GT_JMP or +// i) GT_JMP/GT_NONLOCAL_JMP or // ii) tail call (implicit or explicit) // // Params: diff --git a/src/coreclr/jit/codegen.h b/src/coreclr/jit/codegen.h index 478a05d55678e1..968803ed3f7497 100644 --- a/src/coreclr/jit/codegen.h +++ b/src/coreclr/jit/codegen.h @@ -186,6 +186,9 @@ class CodeGen final : public CodeGenInterface // the current (pending) label ref, a label which has been referenced but not yet seen BasicBlock* genPendingCallLabel; + // label for trampoline to resumption stub + jitstd::vector* genAsyncResumptionTrampolineLabels = nullptr; + void** codePtr; void* codePtrRW; uint32_t* nativeSizeOfCode; @@ -208,6 +211,8 @@ class CodeGen final : public CodeGenInterface void genInitializeRegisterState(); void genCodeForBBlist(); + void genCodeForAsyncResumeTrampolines(); + void genCodeForAsyncResumeTrampolineJump(CORINFO_METHOD_HANDLE resumeMethod, const CORINFO_CONST_LOOKUP& lookup); public: void genSpillVar(GenTree* tree); @@ -220,6 +225,7 @@ class CodeGen final : public CodeGenInterface void genGCWriteBarrier(GenTreeStoreInd* store, GCInfo::WriteBarrierForm wbf); BasicBlock* genCreateTempLabel(); + BasicBlock* genCreateAsyncResumptionTrampolineLabel(GenTreeVal* node); private: void genLogLabel(BasicBlock* bb); diff --git a/src/coreclr/jit/codegenarmarch.cpp b/src/coreclr/jit/codegenarmarch.cpp index 96f01019ac0ecd..d152a3a7fa0e0b 100644 --- a/src/coreclr/jit/codegenarmarch.cpp +++ b/src/coreclr/jit/codegenarmarch.cpp @@ -514,6 +514,15 @@ void CodeGen::genCodeForTreeNode(GenTree* treeNode) genCodeForAsyncContinuation(treeNode); break; + case GT_ASYNC_RESUME_TRAMPOLINE: +#if defined(TARGET_ARM) + genMov32RelocatableDisplacement(genCreateAsyncResumptionTrampolineLabel(treeNode->AsVal()), targetReg); +#else + emit->emitIns_R_L(INS_adr, EA_PTRSIZE, genCreateAsyncResumptionTrampolineLabel(treeNode->AsVal()), + targetReg); +#endif + break; + case GT_PINVOKE_PROLOG: noway_assert(((gcInfo.gcRegGCrefSetCur | gcInfo.gcRegByrefSetCur) & ~fullIntArgRegMask(compiler->info.compCallConv)) == 0); @@ -553,6 +562,7 @@ void CodeGen::genCodeForTreeNode(GenTree* treeNode) #endif // TARGET_ARM case GT_IL_OFFSET: + case GT_RECORD_ASYNC_JOIN: // Do nothing; these nodes are simply markers for debug info. break; @@ -5122,4 +5132,72 @@ void CodeGen::genFnEpilog(BasicBlock* block) compiler->unwindEndEpilog(); } + +//----------------------------------------------------------------------------------- +// genCodeForAsyncresumeTrampolineJump: +// Generate code to jump to an async resumption stub. +// +// Arguments: +// methHnd - Handle of resumption stub +// lookup - Lookup for the address of the resumption stub +// +// Remarks: +// The codegen for these jumps must handle a non-standard environment. No +// prolog has been run when these are invoked, so they cannot use locals, +// they cannot use non-volatile registers, and they cannot trash the argument +// registers that resumption stubs use (see BuildResumptionStubSignature in +// the VM). +// +void CodeGen::genCodeForAsyncResumeTrampolineJump(CORINFO_METHOD_HANDLE methHnd, const CORINFO_CONST_LOOKUP& lookup) +{ + switch (lookup.accessType) + { + case IAT_VALUE: + case IAT_PVALUE: + { + EmitCallParams call; + call.ptrVars = VarSetOps::MakeEmpty(compiler); + call.gcrefRegs = RBM_NONE; + call.byrefRegs = RBM_NONE; + call.isJump = true; + call.methHnd = methHnd; + + if ((lookup.accessType == IAT_VALUE) && validImmForBL((ssize_t)lookup.addr)) + { + call.callType = EC_FUNC_TOKEN; + call.addr = lookup.addr; + } + else + { +#ifdef TARGET_ARM64 + regNumber tempReg = REG_IP0; +#else + regNumber tempReg = REG_R12; +#endif + + instGen_Set_Reg_To_Imm(EA_SET_FLG(EA_PTRSIZE, EA_CNS_RELOC_FLG), tempReg, (ssize_t)lookup.addr, + INS_FLAGS_DONT_CARE DEBUGARG((size_t)methHnd) DEBUGARG(GTF_ICON_FTN_ADDR)); + + if (lookup.accessType == IAT_PVALUE) + { + GetEmitter()->emitIns_R_R(ins_Load(TYP_I_IMPL), EA_PTRSIZE, tempReg, tempReg); + } + call.callType = EC_INDIR_R; + call.ireg = tempReg; + } + GetEmitter()->emitIns_Call(call); + break; + } + case IAT_PPVALUE: + noway_assert(!"Cannot handle PPVALUE access type"); + break; + case IAT_RELPVALUE: + noway_assert(!"Cannot handle RELPVALUE access type"); + break; + default: + noway_assert(!"Cannot handle access type"); + break; + } +} + #endif // TARGET_ARMARCH diff --git a/src/coreclr/jit/codegencommon.cpp b/src/coreclr/jit/codegencommon.cpp index 4153e2a3abe0a7..29382029f17cc8 100644 --- a/src/coreclr/jit/codegencommon.cpp +++ b/src/coreclr/jit/codegencommon.cpp @@ -988,6 +988,28 @@ BasicBlock* CodeGen::genCreateTempLabel() return block; } +BasicBlock* CodeGen::genCreateAsyncResumptionTrampolineLabel(GenTreeVal* node) +{ + assert(node->OperIs(GT_ASYNC_RESUME_TRAMPOLINE)); + + if (genAsyncResumptionTrampolineLabels == nullptr) + { + genAsyncResumptionTrampolineLabels = + new (compiler, CMK_Codegen) jitstd::vector(compiler->compSuspensionPoints->size(), nullptr, + compiler->getAllocator(CMK_Codegen)); + } + + assert(genAsyncResumptionTrampolineLabels->size() > node->gtVal1); + + BasicBlock*& label = (*genAsyncResumptionTrampolineLabels)[node->gtVal1]; + if (label == nullptr) + { + label = genCreateTempLabel(); + } + + return label; +} + void CodeGen::genLogLabel(BasicBlock* bb) { #ifdef DEBUG @@ -6676,16 +6698,17 @@ void CodeGen::genReportAsyncDebugInfo() for (size_t i = 0; i < suspPoints->size(); i++) { AsyncSuspensionPoint& suspPoint = (*suspPoints)[i]; - if (suspPoint.nativeLoc.Valid()) + if (suspPoint.nativeResumeLoc.Valid()) { - hostSuspensionPoints[i].NativeOffset = suspPoint.nativeLoc.CodeOffset(GetEmitter()); - hostSuspensionPoints[i].NumContinuationVars = suspPoint.numContinuationVars; + hostSuspensionPoints[i].NativeResumeOffset = suspPoint.nativeResumeLoc.CodeOffset(GetEmitter()); } - else + + if (suspPoint.nativeJoinLoc.Valid()) { - hostSuspensionPoints[i].NativeOffset = 0; - hostSuspensionPoints[i].NumContinuationVars = 0; + hostSuspensionPoints[i].NativeJoinOffset = suspPoint.nativeJoinLoc.CodeOffset(GetEmitter()); } + + hostSuspensionPoints[i].NumContinuationVars = suspPoint.numContinuationVars; } jitstd::vector* asyncVars = compiler->compAsyncVars; @@ -6703,7 +6726,8 @@ void CodeGen::genReportAsyncDebugInfo() printf("Reported async suspension points:\n"); for (size_t i = 0; i < suspPoints->size(); i++) { - printf(" [%zu] Offset = %x, NumAsyncVars = %u\n", i, hostSuspensionPoints[i].NativeOffset, + printf(" [%zu] ResumeOffset = %x, JoinOffset = %x, NumAsyncVars = %u\n", i, + hostSuspensionPoints[i].NativeResumeOffset, hostSuspensionPoints[i].NativeJoinOffset, hostSuspensionPoints[i].NumContinuationVars); } @@ -6925,7 +6949,7 @@ void CodeGen::genReturn(GenTree* treeNode) // Reason for not materializing Leave callback as a GT_PROF_HOOK node after GT_RETURN: // In flowgraph and other places assert that the last node of a block marked as - // BBJ_RETURN is either a GT_RETURN or GT_JMP or a tail call. It would be nice to + // BBJ_RETURN is either a GT_RETURN, GT_JMP, GT_NONLOCAL_JMP or a tail call. It would be nice to // maintain such an invariant irrespective of whether profiler hook needed or not. // Also, there is not much to be gained by materializing it as an explicit node. // diff --git a/src/coreclr/jit/codegenlinear.cpp b/src/coreclr/jit/codegenlinear.cpp index 2223e003b92e24..7f64dbdb9555f1 100644 --- a/src/coreclr/jit/codegenlinear.cpp +++ b/src/coreclr/jit/codegenlinear.cpp @@ -464,11 +464,11 @@ void CodeGen::genCodeForBBlist() } else if (node->OperIs(GT_RECORD_ASYNC_JOIN)) { - int index = node->AsRecordAsyncJoin()->gtSuspensionPointIndex; + size_t index = node->AsVal()->gtVal1; assert(compiler->compSuspensionPoints != nullptr); - assert(index >= 0 && (size_t)index < compiler->compSuspensionPoints->size()); + assert(index < compiler->compSuspensionPoints->size()); - (*compiler->compSuspensionPoints)[index].nativeLoc.CaptureLocation(GetEmitter()); + (*compiler->compSuspensionPoints)[index].nativeJoinLoc.CaptureLocation(GetEmitter()); } genCodeForTreeNode(node); @@ -864,7 +864,15 @@ void CodeGen::genCodeForBBlist() } compiler->compCurBB = nullptr; #endif // DEBUG - } //------------------ END-FOR each block of the method ------------------- + + // We may need to generate a jump trampoline for async resumption outside the main method. + // This ensures we have an IP inside our main method that Coroutine.Resume points at, for + // diagnostic purposes. + if ((genAsyncResumptionTrampolineLabels != nullptr) && (block == compiler->fgLastBBInMainFunction())) + { + genCodeForAsyncResumeTrampolines(); + } + } //------------------ END-FOR each block of the method ------------------- #if defined(FEATURE_EH_WINDOWS_X86) // If this is a synchronized method on x86, and we generated all the code without @@ -890,11 +898,11 @@ void CodeGen::genCodeForBBlist() // This call is for cleaning the GC refs genUpdateLife(VarSetOps::MakeEmpty(compiler)); - /* Finalize the spill tracking logic */ + // Finalize the spill tracking logic regSet.rsSpillEnd(); - /* Finalize the temp tracking logic */ + // Finalize the temp tracking logic regSet.tmpEnd(); @@ -909,6 +917,39 @@ void CodeGen::genCodeForBBlist() #endif } +//------------------------------------------------------------------------ +// genCodeForAsyncResumeTrampolines: +// Generate jumps to the async resumption stub and bind them to each label +// that was referenced from suspension code. +// +// Arguments: +// None +// +void CodeGen::genCodeForAsyncResumeTrampolines() +{ + if (genAsyncResumptionTrampolineLabels->size() == 0) + { + return; + } + + CORINFO_METHOD_HANDLE resumeStub = compiler->info.compCompHnd->getAsyncResumptionStub(); + CORINFO_CONST_LOOKUP resumeStubLookup; + compiler->info.compCompHnd->getFunctionEntryPoint(resumeStub, &resumeStubLookup); + + for (size_t i = 0; i < genAsyncResumptionTrampolineLabels->size(); i++) + { + BasicBlock* label = (*genAsyncResumptionTrampolineLabels)[i]; + if (label == nullptr) + continue; + + genLogLabel(label); + label->bbEmitCookie = GetEmitter()->emitAddLabel(VarSetOps::MakeEmpty(compiler), RBM_NONE, RBM_NONE); + + (*compiler->compSuspensionPoints)[i].nativeResumeLoc.CaptureLocation(GetEmitter()); + genCodeForAsyncResumeTrampolineJump(resumeStub, resumeStubLookup); + } +} + /* XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX diff --git a/src/coreclr/jit/codegenloongarch64.cpp b/src/coreclr/jit/codegenloongarch64.cpp index 6c239f098506d5..2dac0dc1bffacc 100644 --- a/src/coreclr/jit/codegenloongarch64.cpp +++ b/src/coreclr/jit/codegenloongarch64.cpp @@ -6959,4 +6959,57 @@ void CodeGen::genProfilingLeaveCallback(unsigned helper /*= CORINFO_HELP_PROF_FC } #endif // PROFILING_SUPPORTED +//----------------------------------------------------------------------------------- +// genCodeForAsyncresumeTrampolineJump: +// Generate code to jump to an async resumption stub. +// +// Arguments: +// methHnd - Handle of resumption stub +// lookup - Lookup for the address of the resumption stub +// +// Remarks: +// The codegen for these jumps must handle a non-standard environment. No +// prolog has been run when these are invoked, so they cannot use locals, +// they cannot use non-volatile registers, and they cannot trash the argument +// registers that resumption stubs use (see BuildResumptionStubSignature in +// the VM). +// +void CodeGen::genCodeForAsyncResumeTrampolineJump(CORINFO_METHOD_HANDLE methHnd, const CORINFO_CONST_LOOKUP& lookup) +{ + switch (lookup.accessType) + { + case IAT_VALUE: + case IAT_PVALUE: + { + EmitCallParams call; + call.ptrVars = VarSetOps::MakeEmpty(compiler); + call.gcrefRegs = RBM_NONE; + call.byrefRegs = RBM_NONE; + call.isJump = true; + call.methHnd = methHnd; + + instGen_Set_Reg_To_Imm(EA_SET_FLG(EA_PTRSIZE, EA_CNS_RELOC_FLG), REG_T0, (ssize_t)lookup.addr, + INS_FLAGS_DONT_CARE DEBUGARG((size_t)methHnd) DEBUGARG(GTF_ICON_FTN_ADDR)); + + if (lookup.accessType == IAT_PVALUE) + { + GetEmitter()->emitIns_R_R(ins_Load(TYP_I_IMPL), EA_PTRSIZE, REG_T0, REG_T0); + } + call.callType = EC_INDIR_R; + call.ireg = REG_T0; + GetEmitter()->emitIns_Call(call); + break; + } + case IAT_PPVALUE: + noway_assert(!"Cannot handle PPVALUE access type"); + break; + case IAT_RELPVALUE: + noway_assert(!"Cannot handle RELPVALUE access type"); + break; + default: + noway_assert(!"Cannot handle access type"); + break; + } +} + #endif // TARGET_LOONGARCH64 diff --git a/src/coreclr/jit/codegenriscv64.cpp b/src/coreclr/jit/codegenriscv64.cpp index c3db7a89653c30..198f9da10c9402 100644 --- a/src/coreclr/jit/codegenriscv64.cpp +++ b/src/coreclr/jit/codegenriscv64.cpp @@ -6986,4 +6986,57 @@ void CodeGen::genProfilingLeaveCallback(unsigned helper /*= CORINFO_HELP_PROF_FC } #endif // PROFILING_SUPPORTED +//----------------------------------------------------------------------------------- +// genCodeForAsyncresumeTrampolineJump: +// Generate code to jump to an async resumption stub. +// +// Arguments: +// methHnd - Handle of resumption stub +// lookup - Lookup for the address of the resumption stub +// +// Remarks: +// The codegen for these jumps must handle a non-standard environment. No +// prolog has been run when these are invoked, so they cannot use locals, +// they cannot use non-volatile registers, and they cannot trash the argument +// registers that resumption stubs use (see BuildResumptionStubSignature in +// the VM). +// +void CodeGen::genCodeForAsyncResumeTrampolineJump(CORINFO_METHOD_HANDLE methHnd, const CORINFO_CONST_LOOKUP& lookup) +{ + switch (lookup.accessType) + { + case IAT_VALUE: + case IAT_PVALUE: + { + EmitCallParams call; + call.ptrVars = VarSetOps::MakeEmpty(compiler); + call.gcrefRegs = RBM_NONE; + call.byrefRegs = RBM_NONE; + call.isJump = true; + call.methHnd = methHnd; + + instGen_Set_Reg_To_Imm(EA_SET_FLG(EA_PTRSIZE, EA_CNS_RELOC_FLG), REG_T0, (ssize_t)lookup.addr, + INS_FLAGS_DONT_CARE DEBUGARG((size_t)methHnd) DEBUGARG(GTF_ICON_FTN_ADDR)); + + if (lookup.accessType == IAT_PVALUE) + { + GetEmitter()->emitIns_R_R(ins_Load(TYP_I_IMPL), EA_PTRSIZE, REG_T0, REG_T0); + } + call.callType = EC_INDIR_R; + call.ireg = REG_T0; + GetEmitter()->emitIns_Call(call); + break; + } + case IAT_PPVALUE: + noway_assert(!"Cannot handle PPVALUE access type"); + break; + case IAT_RELPVALUE: + noway_assert(!"Cannot handle RELPVALUE access type"); + break; + default: + noway_assert(!"Cannot handle access type"); + break; + } +} + #endif // TARGET_RISCV64 diff --git a/src/coreclr/jit/codegenxarch.cpp b/src/coreclr/jit/codegenxarch.cpp index 1ebb7ad5d587e9..707e106cd2055e 100644 --- a/src/coreclr/jit/codegenxarch.cpp +++ b/src/coreclr/jit/codegenxarch.cpp @@ -2323,6 +2323,11 @@ void CodeGen::genCodeForTreeNode(GenTree* treeNode) emit->emitIns_R_L(INS_lea, EA_PTR_DSP_RELOC, genPendingCallLabel, treeNode->GetRegNum()); break; + case GT_ASYNC_RESUME_TRAMPOLINE: + emit->emitIns_R_L(INS_lea, EA_PTR_DSP_RELOC, genCreateAsyncResumptionTrampolineLabel(treeNode->AsVal()), + treeNode->GetRegNum()); + break; + case GT_STORE_BLK: genCodeForStoreBlk(treeNode->AsBlk()); break; @@ -11768,6 +11773,71 @@ void CodeGen::instGen_MemoryBarrier(BarrierKind barrierKind) } } +//----------------------------------------------------------------------------------- +// genCodeForAsyncresumeTrampolineJump: +// Generate code to jump to an async resumption stub. +// +// Arguments: +// methHnd - Handle of resumption stub +// lookup - Lookup for the address of the resumption stub +// +// Remarks: +// The codegen for these jumps must handle a non-standard environment. No +// prolog has been run when these are invoked, so they cannot use locals, +// they cannot use non-volatile registers, and they cannot trash the argument +// registers that resumption stubs use (see BuildResumptionStubSignature in +// the VM). +// +void CodeGen::genCodeForAsyncResumeTrampolineJump(CORINFO_METHOD_HANDLE methHnd, const CORINFO_CONST_LOOKUP& lookup) +{ + switch (lookup.accessType) + { + case IAT_VALUE: + case IAT_PVALUE: + { + EmitCallParams call; + call.ptrVars = VarSetOps::MakeEmpty(compiler); + call.gcrefRegs = RBM_NONE; + call.byrefRegs = RBM_NONE; + call.isJump = true; + call.methHnd = methHnd; + + bool containedImmInd = true; + +#ifdef DEBUG + containedImmInd &= compiler->opts.compEnablePCRelAddr; +#endif + containedImmInd &= (lookup.accessType == IAT_PVALUE) && + (compiler->eeGetRelocTypeHint(lookup.addr) == IMAGE_REL_BASED_REL32); + + if ((lookup.accessType == IAT_PVALUE) && !containedImmInd) + { + instGen_Set_Reg_To_Imm(EA_SET_FLG(EA_PTRSIZE, EA_CNS_RELOC_FLG), REG_EAX, (ssize_t)lookup.addr, + INS_FLAGS_DONT_CARE DEBUGARG((size_t)methHnd) DEBUGARG(GTF_ICON_METHOD_HDL)); + call.callType = EC_INDIR_ARD; + call.ireg = REG_EAX; + } + else + { + call.callType = lookup.accessType == IAT_VALUE ? EC_FUNC_TOKEN : EC_FUNC_TOKEN_INDIR; + call.addr = (void*)lookup.addr; + } + + GetEmitter()->emitIns_Call(call); + break; + } + case IAT_PPVALUE: + noway_assert(!"Cannot handle PPVALUE access type"); + break; + case IAT_RELPVALUE: + noway_assert(!"Cannot handle RELPVALUE access type"); + break; + default: + noway_assert(!"Cannot handle access type"); + break; + } +} + #ifdef TARGET_AMD64 // Returns relocation type hint for an addr. // Note that there are no reloc hints on x86. diff --git a/src/coreclr/jit/compiler.h b/src/coreclr/jit/compiler.h index b9858aa0a019c8..7b1396bf31a344 100644 --- a/src/coreclr/jit/compiler.h +++ b/src/coreclr/jit/compiler.h @@ -2399,7 +2399,8 @@ struct RichIPMapping struct AsyncSuspensionPoint { - emitLocation nativeLoc; + emitLocation nativeJoinLoc; + emitLocation nativeResumeLoc; unsigned numContinuationVars = 0; }; @@ -11705,6 +11706,7 @@ class GenTreeVisitor // Leaf nodes case GT_CATCH_ARG: case GT_ASYNC_CONTINUATION: + case GT_ASYNC_RESUME_TRAMPOLINE: 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 a1f64ec2cfa87b..bd2fc62e307e64 100644 --- a/src/coreclr/jit/compiler.hpp +++ b/src/coreclr/jit/compiler.hpp @@ -4491,6 +4491,7 @@ GenTree::VisitResult GenTree::VisitOperands(TVisitor visitor) case GT_LCL_ADDR: case GT_CATCH_ARG: case GT_ASYNC_CONTINUATION: + case GT_ASYNC_RESUME_TRAMPOLINE: case GT_LABEL: case GT_FTN_ADDR: case GT_RET_EXPR: diff --git a/src/coreclr/jit/gentree.cpp b/src/coreclr/jit/gentree.cpp index 475e3b27e1906b..afad1d29d0422b 100644 --- a/src/coreclr/jit/gentree.cpp +++ b/src/coreclr/jit/gentree.cpp @@ -2734,6 +2734,7 @@ bool GenTree::Compare(GenTree* op1, GenTree* op2, bool swapOK) case GT_NOP: case GT_LABEL: + case GT_ASYNC_RESUME_TRAMPOLINE: case GT_SWIFT_ERROR: case GT_GCPOLL: return true; @@ -6693,6 +6694,7 @@ bool GenTree::TryGetUse(GenTree* operand, GenTree*** pUse) case GT_LCL_ADDR: case GT_CATCH_ARG: case GT_ASYNC_CONTINUATION: + case GT_ASYNC_RESUME_TRAMPOLINE: case GT_LABEL: case GT_FTN_ADDR: case GT_RET_EXPR: @@ -9569,6 +9571,7 @@ GenTree* Compiler::gtCloneExpr(GenTree* tree) case GT_CATCH_ARG: case GT_ASYNC_CONTINUATION: + case GT_ASYNC_RESUME_TRAMPOLINE: case GT_NO_OP: case GT_NOP: case GT_LABEL: @@ -10327,6 +10330,7 @@ GenTreeUseEdgeIterator::GenTreeUseEdgeIterator(GenTree* node) case GT_LCL_ADDR: case GT_CATCH_ARG: case GT_ASYNC_CONTINUATION: + case GT_ASYNC_RESUME_TRAMPOLINE: case GT_LABEL: case GT_FTN_ADDR: case GT_RET_EXPR: @@ -12446,7 +12450,8 @@ void Compiler::gtDispLeaf(GenTree* tree, IndentStack* indentStack) break; case GT_RECORD_ASYNC_JOIN: - printf(" %d", tree->AsRecordAsyncJoin()->gtSuspensionPointIndex); + case GT_ASYNC_RESUME_TRAMPOLINE: + printf(" state=%zu", tree->AsVal()->gtVal1); break; case GT_JCC: diff --git a/src/coreclr/jit/gentree.h b/src/coreclr/jit/gentree.h index 51654b1bc572aa..b76a27879a4a7e 100644 --- a/src/coreclr/jit/gentree.h +++ b/src/coreclr/jit/gentree.h @@ -8284,26 +8284,6 @@ struct GenTreeILOffset : public GenTree #endif }; -// No-op node that records the native offset into async debug info during -// codegen/emit. -struct GenTreeRecordAsyncJoin : public GenTree -{ - int gtSuspensionPointIndex; - - GenTreeRecordAsyncJoin(int suspensionPointIndex) - : GenTree(GT_RECORD_ASYNC_JOIN, TYP_VOID) - , gtSuspensionPointIndex(suspensionPointIndex) - { - } - -#if DEBUGGABLE_GENTREE - GenTreeRecordAsyncJoin() - : GenTree(GT_RECORD_ASYNC_JOIN, TYP_VOID) - { - } -#endif -}; - // GenTreeList: adapter class for forward iteration of the execution order GenTree linked list // using range-based `for`, normally used via Statement::TreeList(), e.g.: // for (GenTree* const tree : stmt->TreeList()) ... diff --git a/src/coreclr/jit/gtlist.h b/src/coreclr/jit/gtlist.h index 13bd019a98f470..fedc51e8591564 100644 --- a/src/coreclr/jit/gtlist.h +++ b/src/coreclr/jit/gtlist.h @@ -39,6 +39,7 @@ GTNODE(JMP , GenTreeVal ,0,0,GTK_LEAF|GTK_NOVALUE) // Jump GTNODE(FTN_ADDR , GenTreeFptrVal ,0,0,GTK_LEAF) // Address of a function GTNODE(RET_EXPR , GenTreeRetExpr ,0,0,GTK_LEAF|DBK_NOTLIR) // Place holder for the return expression from an inline candidate GTNODE(GCPOLL , GenTree ,0,0,GTK_LEAF|GTK_NOVALUE|DBK_NOTLIR) +GTNODE(ASYNC_RESUME_TRAMPOLINE, GenTreeVal ,0,0,GTK_LEAF) // Address of async resume trampoline //----------------------------------------------------------------------------- // Constant nodes: @@ -358,7 +359,7 @@ GTNODE(SWAP , GenTreeOp ,0,0,GTK_BINOP|GTK_NOVALUE|DBK_NOTH GTNODE(COPY , GenTreeCopyOrReload,0,0,GTK_UNOP|DBK_NOTHIR) // Copies a variable from its current location to a register that satisfies GTNODE(RELOAD , GenTreeCopyOrReload,0,0,GTK_UNOP|DBK_NOTHIR) // code generation constraints. The operand is the actual lclVar node. GTNODE(IL_OFFSET , GenTreeILOffset ,0,0,GTK_LEAF|GTK_NOVALUE|DBK_NOTHIR) // marks an IL offset for debugging purposes -GTNODE(RECORD_ASYNC_JOIN, GenTreeRecordAsyncJoin,0,0,GTK_LEAF|GTK_NOVALUE|DBK_NOTHIR) // records native offset of async suspension point for async stackwalking purposes +GTNODE(RECORD_ASYNC_JOIN, GenTreeVal ,0,0,GTK_LEAF|GTK_NOVALUE|DBK_NOTHIR) // records native offset of async suspension point for async stackwalking purposes /*****************************************************************************/ #undef GTNODE diff --git a/src/coreclr/jit/gtstructs.h b/src/coreclr/jit/gtstructs.h index d31917752584f8..c23a79720bd5c9 100644 --- a/src/coreclr/jit/gtstructs.h +++ b/src/coreclr/jit/gtstructs.h @@ -51,9 +51,9 @@ GTSTRUCT_0(UnOp , GT_OP) GTSTRUCT_0(Op , GT_OP) #if defined(FEATURE_EH_WINDOWS_X86) -GTSTRUCT_2(Val , GT_END_LFIN, GT_JMP) +GTSTRUCT_N(Val , GT_END_LFIN, GT_JMP, GT_RECORD_ASYNC_JOIN, GT_ASYNC_RESUME_TRAMPOLINE) #else -GTSTRUCT_1(Val , GT_JMP) +GTSTRUCT_N(Val , GT_JMP, GT_RECORD_ASYNC_JOIN, GT_ASYNC_RESUME_TRAMPOLINE) #endif GTSTRUCT_2_SPECIAL(IntConCommon, GT_CNS_INT, GT_CNS_LNG) GTSTRUCT_1(IntCon , GT_CNS_INT) @@ -88,7 +88,6 @@ GTSTRUCT_2(MDArr , GT_MDARR_LENGTH, GT_MDARR_LOWER_BOUND) GTSTRUCT_1(ArrElem , GT_ARR_ELEM) GTSTRUCT_1(RetExpr , GT_RET_EXPR) GTSTRUCT_1(ILOffset , GT_IL_OFFSET) -GTSTRUCT_1(RecordAsyncJoin, GT_RECORD_ASYNC_JOIN) GTSTRUCT_2(CopyOrReload, GT_COPY, GT_RELOAD) GTSTRUCT_1(AddrMode , GT_LEA) GTSTRUCT_1(Qmark , GT_QMARK) diff --git a/src/coreclr/vm/debuginfostore.cpp b/src/coreclr/vm/debuginfostore.cpp index e3f79891622853..d7d9bf7d3cd59c 100644 --- a/src/coreclr/vm/debuginfostore.cpp +++ b/src/coreclr/vm/debuginfostore.cpp @@ -466,14 +466,17 @@ static void DoAsyncSuspensionPoints( ULONG32 cSuspensionPoints, ICorDebugInfo::AsyncSuspensionPoint* suspensionPoints) { - // Loop through and transfer each Entry in the Mapping. - uint32_t lastNativeOffset = 0; + uint32_t lastNativeResumeOffset = 0; + uint32_t lastNativeJoinOffset = 0; for (uint32_t i = 0; i < cSuspensionPoints; i++) { ICorDebugInfo::AsyncSuspensionPoint* sp = &suspensionPoints[i]; - trans.DoEncodedDeltaU32NonMonotonic(sp->NativeOffset, lastNativeOffset); - lastNativeOffset = sp->NativeOffset; + trans.DoEncodedDeltaU32(sp->NativeResumeOffset, lastNativeResumeOffset); + lastNativeResumeOffset = sp->NativeResumeOffset; + + trans.DoEncodedDeltaU32NonMonotonic(sp->NativeJoinOffset, lastNativeJoinOffset); + lastNativeJoinOffset = sp->NativeJoinOffset; trans.DoEncodedU32(sp->NumContinuationVars); } From 06fea819392e012576c1169d6e4d9c8f7ce17071 Mon Sep 17 00:00:00 2001 From: Jakob Botsch Nielsen Date: Fri, 17 Oct 2025 23:08:54 +0200 Subject: [PATCH 32/72] Fix after merge --- src/coreclr/inc/jiteeversionguid.h | 10 +++++----- .../Common/JitInterface/CorInfoImpl_generated.cs | 14 +++++++------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/coreclr/inc/jiteeversionguid.h b/src/coreclr/inc/jiteeversionguid.h index 64cb2e4f1d8c61..3f6cd5878c7d8d 100644 --- a/src/coreclr/inc/jiteeversionguid.h +++ b/src/coreclr/inc/jiteeversionguid.h @@ -37,11 +37,11 @@ #include -constexpr GUID JITEEVersionIdentifier = { /* e1c4ef4f-62a1-4055-813e-23837fdfdffa */ - 0xe1c4ef4f, - 0x62a1, - 0x4055, - {0x81, 0x3e, 0x23, 0x83, 0x7f, 0xdf, 0xdf, 0xfa} +constexpr GUID JITEEVersionIdentifier = { /* 421f544d-55cb-4e2a-a43d-c308a8c071d7 */ + 0x421f544d, + 0x55cb, + 0x4e2a, + {0xa4, 0x3d, 0xc3, 0x08, 0xa8, 0xc0, 0x71, 0xd7} }; #endif // JIT_EE_VERSIONING_GUID_H diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoImpl_generated.cs b/src/coreclr/tools/Common/JitInterface/CorInfoImpl_generated.cs index 1bfbaa00fcfce3..2443c41605862b 100644 --- a/src/coreclr/tools/Common/JitInterface/CorInfoImpl_generated.cs +++ b/src/coreclr/tools/Common/JitInterface/CorInfoImpl_generated.cs @@ -1622,7 +1622,7 @@ private static void _reportAsyncDebugInfo(IntPtr thisHandle, IntPtr* ppException } [UnmanagedCallersOnly] - private static void _reportMetadata(IntPtr thisHandle, IntPtr* ppException, byte* key, void* value, UIntPtr length) + private static void _reportMetadata(IntPtr thisHandle, IntPtr* ppException, byte* key, void* value, nuint length) { var _this = GetThis(thisHandle); try @@ -2620,7 +2620,7 @@ private static uint _getJitFlags(IntPtr thisHandle, IntPtr* ppException, CORJIT_ private static IntPtr GetUnmanagedCallbacks() { - void** callbacks = (void**)Marshal.AllocCoTaskMem(sizeof(IntPtr) * 177); + void** callbacks = (void**)NativeMemory.Alloc((nuint)(sizeof(void*) * 177)); callbacks[0] = (delegate* unmanaged)&_isIntrinsic; callbacks[1] = (delegate* unmanaged)&_notifyMethodInfoUsage; @@ -2731,8 +2731,8 @@ private static IntPtr GetUnmanagedCallbacks() callbacks[106] = (delegate* unmanaged)&_setVars; callbacks[107] = (delegate* unmanaged)&_reportRichMappings; callbacks[108] = (delegate* unmanaged)&_reportAsyncDebugInfo; - callbacks[109] = (delegate* unmanaged)&_reportMetadata; - callbacks[110] = (delegate* unmanaged)&_allocateArray; + callbacks[109] = (delegate* unmanaged)&_reportMetadata; + callbacks[110] = (delegate* unmanaged)&_allocateArray; callbacks[111] = (delegate* unmanaged)&_freeArray; callbacks[112] = (delegate* unmanaged)&_getArgNext; callbacks[113] = (delegate* unmanaged)&_getArgType; @@ -2744,8 +2744,8 @@ private static IntPtr GetUnmanagedCallbacks() callbacks[119] = (delegate* unmanaged)&_getEEInfo; callbacks[120] = (delegate* unmanaged)&_getAsyncInfo; callbacks[121] = (delegate* unmanaged)&_getMethodDefFromMethod; - callbacks[122] = (delegate* unmanaged)&_printMethodName; - callbacks[123] = (delegate* unmanaged)&_getMethodNameFromMetadata; + callbacks[122] = (delegate* unmanaged)&_printMethodName; + callbacks[123] = (delegate* unmanaged)&_getMethodNameFromMetadata; callbacks[124] = (delegate* unmanaged)&_getMethodHash; callbacks[125] = (delegate* unmanaged)&_getSystemVAmd64PassStructInRegisterDescriptor; callbacks[126] = (delegate* unmanaged)&_getSwiftLowering; @@ -2785,7 +2785,7 @@ private static IntPtr GetUnmanagedCallbacks() callbacks[160] = (delegate* unmanaged)&_allocMem; callbacks[161] = (delegate* unmanaged)&_reserveUnwindInfo; callbacks[162] = (delegate* unmanaged)&_allocUnwindInfo; - callbacks[163] = (delegate* unmanaged)&_allocGCInfo; + callbacks[163] = (delegate* unmanaged)&_allocGCInfo; callbacks[164] = (delegate* unmanaged)&_setEHcount; callbacks[165] = (delegate* unmanaged)&_setEHinfo; callbacks[166] = (delegate* unmanaged)&_logMsg; From 29d8998d27a0ee61144e8e6ec8e69eb53681b31c Mon Sep 17 00:00:00 2001 From: Jakob Botsch Nielsen Date: Fri, 17 Oct 2025 23:09:16 +0200 Subject: [PATCH 33/72] Enable runtime async testing --- src/coreclr/inc/clrconfigvalues.h | 2 +- src/tests/async/Directory.Build.targets | 13 ------------- 2 files changed, 1 insertion(+), 14 deletions(-) delete mode 100644 src/tests/async/Directory.Build.targets diff --git a/src/coreclr/inc/clrconfigvalues.h b/src/coreclr/inc/clrconfigvalues.h index e5dc2fe9d8fd06..d95d6c2b7a2857 100644 --- a/src/coreclr/inc/clrconfigvalues.h +++ b/src/coreclr/inc/clrconfigvalues.h @@ -715,7 +715,7 @@ RETAIL_CONFIG_DWORD_INFO(EXTERNAL_EnableRiscV64Zbb, W("EnableRiscV64 #endif // Runtime-async -RETAIL_CONFIG_DWORD_INFO(UNSUPPORTED_RuntimeAsync, W("RuntimeAsync"), 0, "Enables runtime async method support") +RETAIL_CONFIG_DWORD_INFO(UNSUPPORTED_RuntimeAsync, W("RuntimeAsync"), 1, "Enables runtime async method support") /// /// Uncategorized diff --git a/src/tests/async/Directory.Build.targets b/src/tests/async/Directory.Build.targets deleted file mode 100644 index 5a4d413a9e1f62..00000000000000 --- a/src/tests/async/Directory.Build.targets +++ /dev/null @@ -1,13 +0,0 @@ - - - - true - - - - - true - - - - From cb7a943cbb24555eb32a49d2536f84c7726cff96 Mon Sep 17 00:00:00 2001 From: Jakob Botsch Nielsen Date: Fri, 17 Oct 2025 23:13:23 +0200 Subject: [PATCH 34/72] Undo changes --- src/coreclr/jit/block.cpp | 2 +- src/coreclr/jit/codegen.h | 2 +- src/coreclr/jit/codegencommon.cpp | 2 +- src/coreclr/jit/codegenlinear.cpp | 5 +++-- 4 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/coreclr/jit/block.cpp b/src/coreclr/jit/block.cpp index bcbe4c6046eb85..f36e55e216a5aa 100644 --- a/src/coreclr/jit/block.cpp +++ b/src/coreclr/jit/block.cpp @@ -1206,7 +1206,7 @@ bool BasicBlock::endsWithJmpMethod(Compiler* comp) const } // Returns true if the basic block ends with either -// i) GT_JMP/GT_NONLOCAL_JMP or +// i) GT_JMP or // ii) tail call (implicit or explicit) // // Params: diff --git a/src/coreclr/jit/codegen.h b/src/coreclr/jit/codegen.h index 4da5e126d08c2b..67f454ca8d609c 100644 --- a/src/coreclr/jit/codegen.h +++ b/src/coreclr/jit/codegen.h @@ -186,7 +186,7 @@ class CodeGen final : public CodeGenInterface // the current (pending) label ref, a label which has been referenced but not yet seen BasicBlock* genPendingCallLabel; - // label for trampoline to resumption stub + // labels for trampolines to resumption stub jitstd::vector* genAsyncResumptionTrampolineLabels = nullptr; void** codePtr; diff --git a/src/coreclr/jit/codegencommon.cpp b/src/coreclr/jit/codegencommon.cpp index 75064a4e1a65b4..a35cf47c538653 100644 --- a/src/coreclr/jit/codegencommon.cpp +++ b/src/coreclr/jit/codegencommon.cpp @@ -6987,7 +6987,7 @@ void CodeGen::genReturn(GenTree* treeNode) // Reason for not materializing Leave callback as a GT_PROF_HOOK node after GT_RETURN: // In flowgraph and other places assert that the last node of a block marked as - // BBJ_RETURN is either a GT_RETURN, GT_JMP, GT_NONLOCAL_JMP or a tail call. It would be nice to + // BBJ_RETURN is either a GT_RETURN, GT_JMP or a tail call. It would be nice to // maintain such an invariant irrespective of whether profiler hook needed or not. // Also, there is not much to be gained by materializing it as an explicit node. // diff --git a/src/coreclr/jit/codegenlinear.cpp b/src/coreclr/jit/codegenlinear.cpp index 7f64dbdb9555f1..ac09ccde06de7b 100644 --- a/src/coreclr/jit/codegenlinear.cpp +++ b/src/coreclr/jit/codegenlinear.cpp @@ -865,8 +865,9 @@ void CodeGen::genCodeForBBlist() compiler->compCurBB = nullptr; #endif // DEBUG - // We may need to generate a jump trampoline for async resumption outside the main method. - // This ensures we have an IP inside our main method that Coroutine.Resume points at, for + // We may need to generate jump trampolines for async resumption + // outside the main method. This ensures we have unique IPs inside our + // main method that Coroutine.Resume points at for each state, for // diagnostic purposes. if ((genAsyncResumptionTrampolineLabels != nullptr) && (block == compiler->fgLastBBInMainFunction())) { From 879ec4f96e63422aa53b71d6b424eb63660cbed9 Mon Sep 17 00:00:00 2001 From: Jakob Botsch Nielsen Date: Fri, 17 Oct 2025 23:46:59 +0200 Subject: [PATCH 35/72] Hacky late Friday GC issue fix --- src/coreclr/jit/codegenlinear.cpp | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/coreclr/jit/codegenlinear.cpp b/src/coreclr/jit/codegenlinear.cpp index ac09ccde06de7b..8f88357e9e859e 100644 --- a/src/coreclr/jit/codegenlinear.cpp +++ b/src/coreclr/jit/codegenlinear.cpp @@ -933,6 +933,14 @@ void CodeGen::genCodeForAsyncResumeTrampolines() return; } + GetEmitter()->emitThisGCrefVars = VarSetOps::MakeEmpty(compiler); + GetEmitter()->emitThisGCrefRegs = RBM_NONE; + GetEmitter()->emitThisByrefRegs = RBM_NONE; + GetEmitter()->emitNxtIG(); + GetEmitter()->emitDisableGC(); + // emitDisableGC still allows GC at the current IP, so we need to insert a nop. + instGen(INS_nop); + CORINFO_METHOD_HANDLE resumeStub = compiler->info.compCompHnd->getAsyncResumptionStub(); CORINFO_CONST_LOOKUP resumeStubLookup; compiler->info.compCompHnd->getFunctionEntryPoint(resumeStub, &resumeStubLookup); @@ -944,11 +952,13 @@ void CodeGen::genCodeForAsyncResumeTrampolines() continue; genLogLabel(label); - label->bbEmitCookie = GetEmitter()->emitAddLabel(VarSetOps::MakeEmpty(compiler), RBM_NONE, RBM_NONE); + label->bbEmitCookie = GetEmitter()->emitAddInlineLabel(); (*compiler->compSuspensionPoints)[i].nativeResumeLoc.CaptureLocation(GetEmitter()); genCodeForAsyncResumeTrampolineJump(resumeStub, resumeStubLookup); } + + GetEmitter()->emitEnableGC(); } /* From 0749688f33a67e7bf59c576b465275a569bd2e71 Mon Sep 17 00:00:00 2001 From: Jakob Botsch Nielsen Date: Sat, 18 Oct 2025 00:13:18 +0200 Subject: [PATCH 36/72] Fix 32 bit build --- src/coreclr/vm/ilstubresolver.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/coreclr/vm/ilstubresolver.h b/src/coreclr/vm/ilstubresolver.h index 6d888cfba79665..eefef1bdddd066 100644 --- a/src/coreclr/vm/ilstubresolver.h +++ b/src/coreclr/vm/ilstubresolver.h @@ -121,9 +121,9 @@ class AsyncResumeILStubResolver : public ILStubResolver protected: // IP to resume in. Can be tier0 code if we suspended in an OSR method. - PCODE m_resumeIP = NULL; + PCODE m_resumeIP = 0; // Final IP resumed in. Will be OSR method IP if suspension happened there. - PCODE m_finalResumeIP = NULL; + PCODE m_finalResumeIP = 0; }; typedef Holder, ILStubResolver::StubGenFailed, 0> ILStubGenHolder; From 6bcc004e231f34f99315c6ad5aed2a7be35190b9 Mon Sep 17 00:00:00 2001 From: Jakob Botsch Nielsen Date: Sat, 18 Oct 2025 17:25:41 +0200 Subject: [PATCH 37/72] Last block no longer always results in the last IG --- src/coreclr/jit/codegencommon.cpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/coreclr/jit/codegencommon.cpp b/src/coreclr/jit/codegencommon.cpp index a35cf47c538653..ffae969ba50d3e 100644 --- a/src/coreclr/jit/codegencommon.cpp +++ b/src/coreclr/jit/codegencommon.cpp @@ -4463,8 +4463,15 @@ void CodeGen::genReserveEpilog(BasicBlock* block) JITDUMP("Reserving epilog IG for block " FMT_BB "\n", block->bbNum); + bool isLast = block->IsLast(); + if ((genAsyncResumptionTrampolineLabels != nullptr) && (genAsyncResumptionTrampolineLabels->size() != 0) && + (block == compiler->fgLastBBInMainFunction())) + { + isLast = false; + } + GetEmitter()->emitCreatePlaceholderIG(IGPT_EPILOG, block, VarSetOps::MakeEmpty(compiler), gcInfo.gcRegGCrefSetCur, - gcInfo.gcRegByrefSetCur, block->IsLast()); + gcInfo.gcRegByrefSetCur, isLast); } /***************************************************************************** From 8cf3a7215000ab51027ec775cf6d1563c12aeb26 Mon Sep 17 00:00:00 2001 From: Jakob Botsch Nielsen Date: Sat, 18 Oct 2025 18:14:39 +0200 Subject: [PATCH 38/72] Fix after merge --- .../CompilerServices/AsyncHelpers.CoreCLR.cs | 2 +- src/coreclr/inc/cordebuginfo.h | 6 +-- src/coreclr/inc/jiteeversionguid.h | 10 ++--- src/coreclr/jit/async.cpp | 3 +- src/coreclr/jit/codegencommon.cpp | 3 +- .../JitInterface/CorInfoImpl_generated.cs | 45 ++++++++++--------- src/coreclr/vm/debuginfostore.cpp | 3 +- 7 files changed, 33 insertions(+), 39 deletions(-) diff --git a/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/AsyncHelpers.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/AsyncHelpers.CoreCLR.cs index e7415a82bb01c6..1878bce3139e40 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/AsyncHelpers.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/AsyncHelpers.CoreCLR.cs @@ -374,7 +374,7 @@ public static unsafe void DispatchContinuations(T task) where T : Task, } catch (Exception ex) { - Continuation handlerContinuation = UnwindToPossibleHandler(continuation); + Continuation? handlerContinuation = UnwindToPossibleHandler(continuation); if (handlerContinuation == null) { // Tail of AsyncTaskMethodBuilderT.SetException diff --git a/src/coreclr/inc/cordebuginfo.h b/src/coreclr/inc/cordebuginfo.h index e80d57323836ff..a210a2b57bc29d 100644 --- a/src/coreclr/inc/cordebuginfo.h +++ b/src/coreclr/inc/cordebuginfo.h @@ -436,12 +436,8 @@ class ICorDebugInfo { // IL number of variable (or one of the special IL numbers, like TYPECTXT_ILNUM) uint32_t VarNumber; - // Index in continuation's byte[] data where this variable is stored, or 0xFFFFFFFF if the - // variable does not have any byte[] data + // Offset in continuation object where this variable is stored uint32_t Offset; - // Index in continuation's object[] data where this variable's GC pointers are stored, or 0xFFFFFFFF - // if the variable does not have any GC pointers - uint32_t GCIndex; }; struct AsyncSuspensionPoint diff --git a/src/coreclr/inc/jiteeversionguid.h b/src/coreclr/inc/jiteeversionguid.h index 3f6cd5878c7d8d..fb48ca25856949 100644 --- a/src/coreclr/inc/jiteeversionguid.h +++ b/src/coreclr/inc/jiteeversionguid.h @@ -37,11 +37,11 @@ #include -constexpr GUID JITEEVersionIdentifier = { /* 421f544d-55cb-4e2a-a43d-c308a8c071d7 */ - 0x421f544d, - 0x55cb, - 0x4e2a, - {0xa4, 0x3d, 0xc3, 0x08, 0xa8, 0xc0, 0x71, 0xd7} +constexpr GUID JITEEVersionIdentifier = { /* 590a21bb-2a2e-4b80-82dc-0bf1fd92a301 */ + 0x590a21bb, + 0x2a2e, + 0x4b80, + {0x82, 0xdc, 0x0b, 0xf1, 0xfd, 0x92, 0xa3, 0x01} }; #endif // JIT_EE_VERSIONING_GUID_H diff --git a/src/coreclr/jit/async.cpp b/src/coreclr/jit/async.cpp index 8b85eca1a6614c..ca5186bd2fb3b1 100644 --- a/src/coreclr/jit/async.cpp +++ b/src/coreclr/jit/async.cpp @@ -2107,8 +2107,7 @@ void AsyncTransformation::CreateDebugInfoForSuspensionPoint(GenTreeCall* ICorDebugInfo::AsyncContinuationVarInfo varInf; varInf.VarNumber = ilVarNum; - varInf.Offset = local.DataSize > 0 ? local.DataOffset : UINT_MAX; - varInf.GCIndex = local.GCDataCount > 0 ? local.GCDataIndex : UINT_MAX; + varInf.Offset = OFFSETOF__CORINFO_Continuation__data + local.Offset; m_comp->compAsyncVars->push_back(varInf); numLocals++; } diff --git a/src/coreclr/jit/codegencommon.cpp b/src/coreclr/jit/codegencommon.cpp index ffae969ba50d3e..29214943e47ced 100644 --- a/src/coreclr/jit/codegencommon.cpp +++ b/src/coreclr/jit/codegencommon.cpp @@ -6779,8 +6779,7 @@ void CodeGen::genReportAsyncDebugInfo() printf("Reported async vars:\n"); for (size_t i = 0; i < asyncVars->size(); i++) { - printf(" [%zu] VarNumber = %u, Offset = %x, GCIndex = %u\n", i, hostVars[i].VarNumber, hostVars[i].Offset, - hostVars[i].GCIndex); + printf(" [%zu] VarNumber = %u, Offset = %x\n", i, hostVars[i].VarNumber, hostVars[i].Offset); } } #endif diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoImpl_generated.cs b/src/coreclr/tools/Common/JitInterface/CorInfoImpl_generated.cs index 47883bacde0a85..6911ece7bd1042 100644 --- a/src/coreclr/tools/Common/JitInterface/CorInfoImpl_generated.cs +++ b/src/coreclr/tools/Common/JitInterface/CorInfoImpl_generated.cs @@ -2635,7 +2635,7 @@ private static uint _getJitFlags(IntPtr thisHandle, IntPtr* ppException, CORJIT_ private static IntPtr GetUnmanagedCallbacks() { - void** callbacks = (void**)NativeMemory.Alloc((nuint)(sizeof(void*) * 177)); + void** callbacks = (void**)NativeMemory.Alloc((nuint)(sizeof(void*) * 178)); callbacks[0] = (delegate* unmanaged)&_isIntrinsic; callbacks[1] = (delegate* unmanaged)&_notifyMethodInfoUsage; @@ -2793,27 +2793,28 @@ private static IntPtr GetUnmanagedCallbacks() callbacks[153] = (delegate* unmanaged)&_GetDelegateCtor; callbacks[154] = (delegate* unmanaged)&_MethodCompileComplete; callbacks[155] = (delegate* unmanaged)&_getTailCallHelpers; - callbacks[156] = (delegate* unmanaged)&_getAsyncResumptionStub; - callbacks[157] = (delegate* unmanaged)&_convertPInvokeCalliToCall; - callbacks[158] = (delegate* unmanaged)&_notifyInstructionSetUsage; - callbacks[159] = (delegate* unmanaged)&_updateEntryPointForTailCall; - callbacks[160] = (delegate* unmanaged)&_allocMem; - callbacks[161] = (delegate* unmanaged)&_reserveUnwindInfo; - callbacks[162] = (delegate* unmanaged)&_allocUnwindInfo; - callbacks[163] = (delegate* unmanaged)&_allocGCInfo; - callbacks[164] = (delegate* unmanaged)&_setEHcount; - callbacks[165] = (delegate* unmanaged)&_setEHinfo; - callbacks[166] = (delegate* unmanaged)&_logMsg; - callbacks[167] = (delegate* unmanaged)&_doAssert; - callbacks[168] = (delegate* unmanaged)&_reportFatalError; - callbacks[169] = (delegate* unmanaged)&_getPgoInstrumentationResults; - callbacks[170] = (delegate* unmanaged)&_allocPgoInstrumentationBySchema; - callbacks[171] = (delegate* unmanaged)&_recordCallSite; - callbacks[172] = (delegate* unmanaged)&_recordRelocation; - callbacks[173] = (delegate* unmanaged)&_getRelocTypeHint; - callbacks[174] = (delegate* unmanaged)&_getExpectedTargetArchitecture; - callbacks[175] = (delegate* unmanaged)&_getJitFlags; - callbacks[176] = (delegate* unmanaged)&_getSpecialCopyHelper; + callbacks[156] = (delegate* unmanaged)&_getContinuationType; + callbacks[157] = (delegate* unmanaged)&_getAsyncResumptionStub; + callbacks[158] = (delegate* unmanaged)&_convertPInvokeCalliToCall; + callbacks[159] = (delegate* unmanaged)&_notifyInstructionSetUsage; + callbacks[160] = (delegate* unmanaged)&_updateEntryPointForTailCall; + callbacks[161] = (delegate* unmanaged)&_allocMem; + callbacks[162] = (delegate* unmanaged)&_reserveUnwindInfo; + callbacks[163] = (delegate* unmanaged)&_allocUnwindInfo; + callbacks[164] = (delegate* unmanaged)&_allocGCInfo; + callbacks[165] = (delegate* unmanaged)&_setEHcount; + callbacks[166] = (delegate* unmanaged)&_setEHinfo; + callbacks[167] = (delegate* unmanaged)&_logMsg; + callbacks[168] = (delegate* unmanaged)&_doAssert; + callbacks[169] = (delegate* unmanaged)&_reportFatalError; + callbacks[170] = (delegate* unmanaged)&_getPgoInstrumentationResults; + callbacks[171] = (delegate* unmanaged)&_allocPgoInstrumentationBySchema; + callbacks[172] = (delegate* unmanaged)&_recordCallSite; + callbacks[173] = (delegate* unmanaged)&_recordRelocation; + callbacks[174] = (delegate* unmanaged)&_getRelocTypeHint; + callbacks[175] = (delegate* unmanaged)&_getExpectedTargetArchitecture; + callbacks[176] = (delegate* unmanaged)&_getJitFlags; + callbacks[177] = (delegate* unmanaged)&_getSpecialCopyHelper; return (IntPtr)callbacks; } diff --git a/src/coreclr/vm/debuginfostore.cpp b/src/coreclr/vm/debuginfostore.cpp index d7d9bf7d3cd59c..3f6500160150b1 100644 --- a/src/coreclr/vm/debuginfostore.cpp +++ b/src/coreclr/vm/debuginfostore.cpp @@ -494,8 +494,7 @@ static void DoAsyncVars( { ICorDebugInfo::AsyncContinuationVarInfo* var = &vars[i]; trans.DoEncodedAdjustedU32(var->VarNumber, (DWORD) ICorDebugInfo::MAX_ILNUM); - trans.DoEncodedAdjustedU32(var->Offset, UINT_MAX); - trans.DoEncodedAdjustedU32(var->GCIndex, UINT_MAX); + trans.DoEncodedU32(var->Offset); } } From 3096aa87f4c675eab151e46050e4568e2368bfca Mon Sep 17 00:00:00 2001 From: Jakob Botsch Nielsen Date: Sat, 18 Oct 2025 18:15:20 +0200 Subject: [PATCH 39/72] Run jit-format --- src/coreclr/jit/async.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/coreclr/jit/async.h b/src/coreclr/jit/async.h index de1507f59ebe62..d857ed529da44e 100644 --- a/src/coreclr/jit/async.h +++ b/src/coreclr/jit/async.h @@ -135,9 +135,9 @@ class AsyncTransformation var_types storeType, GenTreeFlags indirFlags = GTF_IND_NONFAULTING); - void CreateDebugInfoForSuspensionPoint(GenTreeCall* asyncCall, - const ContinuationLayout& layout, - BasicBlock* joinBB); + void CreateDebugInfoForSuspensionPoint(GenTreeCall* asyncCall, + const ContinuationLayout& layout, + BasicBlock* joinBB); unsigned GetResultBaseVar(); unsigned GetExceptionVar(); From 9f913f32ee220956287d46518df5874cf35f265d Mon Sep 17 00:00:00 2001 From: Jakob Botsch Nielsen Date: Sun, 19 Oct 2025 00:39:48 +0200 Subject: [PATCH 40/72] Clean up --- .../System/Runtime/CompilerServices/AsyncHelpers.CoreCLR.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/AsyncHelpers.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/AsyncHelpers.CoreCLR.cs index 1878bce3139e40..79f4f2ec1644a2 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/AsyncHelpers.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/AsyncHelpers.CoreCLR.cs @@ -355,8 +355,7 @@ public static unsafe void DispatchContinuations(T task) where T : Task, Debug.Assert(continuation != null); try { - Continuation? curContinuation = continuation; - Debug.Assert(curContinuation != null); + Continuation curContinuation = continuation; Continuation? nextContinuation = curContinuation.Next; continuation = nextContinuation; From 85bd57ac157bd45cb8cd3174520fdb1939c175b6 Mon Sep 17 00:00:00 2001 From: Jakob Botsch Nielsen Date: Sun, 19 Oct 2025 00:39:52 +0200 Subject: [PATCH 41/72] Fix arm32 --- src/coreclr/jit/lsraarm.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/coreclr/jit/lsraarm.cpp b/src/coreclr/jit/lsraarm.cpp index 58c320805ee8f4..f3840ef897d5f0 100644 --- a/src/coreclr/jit/lsraarm.cpp +++ b/src/coreclr/jit/lsraarm.cpp @@ -690,6 +690,7 @@ int LinearScan::BuildNode(GenTree* tree) case GT_PHYSREG: case GT_IL_OFFSET: case GT_RECORD_ASYNC_JOIN: + case GT_ASYNC_RESUME_TRAMPOLINE: case GT_LABEL: case GT_PINVOKE_PROLOG: case GT_JCC: From 43c5710ec0838a1c13a407c670c834ad7b69b474 Mon Sep 17 00:00:00 2001 From: Jakob Botsch Nielsen Date: Mon, 20 Oct 2025 19:08:10 +0200 Subject: [PATCH 42/72] Revert "Store target IPs in AsyncResumeILStubResolver" This reverts commit 69e02db8961eb7e2aee3db12ceed5e265b9b3736. --- src/coreclr/vm/ilstubcache.cpp | 24 ++++------- src/coreclr/vm/ilstubcache.h | 6 +-- src/coreclr/vm/ilstubresolver.cpp | 22 ---------- src/coreclr/vm/ilstubresolver.h | 18 -------- src/coreclr/vm/jitinterface.cpp | 71 +++++++++++++++---------------- src/coreclr/vm/jitinterface.h | 4 +- 6 files changed, 46 insertions(+), 99 deletions(-) diff --git a/src/coreclr/vm/ilstubcache.cpp b/src/coreclr/vm/ilstubcache.cpp index e5422bd3ea3ee9..13f6891133f56a 100644 --- a/src/coreclr/vm/ilstubcache.cpp +++ b/src/coreclr/vm/ilstubcache.cpp @@ -70,7 +70,7 @@ void CreateModuleIndependentSignature(LoaderHeap* pCreationHeap, // static MethodDesc* ILStubCache::CreateAndLinkNewILStubMethodDesc(LoaderAllocator* pAllocator, MethodTable* pMT, DWORD dwStubFlags, Module* pSigModule, PCCOR_SIGNATURE pSig, DWORD cbSig, SigTypeContext *pTypeContext, - ILStubLinker* pStubLinker, BOOL isAsync /* = FALSE */, ILStubResolver* pResolver /* = NULL */) + ILStubLinker* pStubLinker, BOOL isAsync /* = FALSE */) { CONTRACT (MethodDesc*) { @@ -88,14 +88,13 @@ MethodDesc* ILStubCache::CreateAndLinkNewILStubMethodDesc(LoaderAllocator* pAllo pSig, cbSig, isAsync, pTypeContext, - &amTracker, - pResolver); + &amTracker); amTracker.SuppressRelease(); pStubLinker->SetStubMethodDesc(pStubMD); - pResolver = pStubMD->AsDynamicMethodDesc()->GetILStubResolver(); + ILStubResolver *pResolver = pStubMD->AsDynamicMethodDesc()->GetILStubResolver(); pResolver->SetStubMethodDesc(pStubMD); @@ -149,7 +148,7 @@ namespace // static MethodDesc* ILStubCache::CreateNewMethodDesc(LoaderHeap* pCreationHeap, MethodTable* pMT, DWORD dwStubFlags, Module* pSigModule, PCCOR_SIGNATURE pSig, DWORD cbSig, BOOL isAsync, SigTypeContext *pTypeContext, - AllocMemTracker* pamTracker, ILStubResolver* pResolver) + AllocMemTracker* pamTracker) { CONTRACT (MethodDesc*) { @@ -217,19 +216,12 @@ MethodDesc* ILStubCache::CreateNewMethodDesc(LoaderHeap* pCreationHeap, MethodTa pMD->SetStatic(); } - if (pResolver != NULL) - { - pMD->m_pResolver = pResolver; - } - else - { - pMD->m_pResolver = (ILStubResolver*)pamTracker->Track(pCreationHeap->AllocMem(S_SIZE_T(sizeof(ILStubResolver)))); + pMD->m_pResolver = (ILStubResolver*)pamTracker->Track(pCreationHeap->AllocMem(S_SIZE_T(sizeof(ILStubResolver)))); #ifdef _DEBUG - // Poison the ILStubResolver storage - memset((void*)pMD->m_pResolver, 0xCC, sizeof(ILStubResolver)); + // Poison the ILStubResolver storage + memset((void*)pMD->m_pResolver, 0xCC, sizeof(ILStubResolver)); #endif // _DEBUG - pMD->m_pResolver = new (pMD->m_pResolver) ILStubResolver(); - } + pMD->m_pResolver = new (pMD->m_pResolver) ILStubResolver(); if (SF_IsArrayOpStub(dwStubFlags)) { diff --git a/src/coreclr/vm/ilstubcache.h b/src/coreclr/vm/ilstubcache.h index 873769d61b6aa7..52be99d9fb8ef1 100644 --- a/src/coreclr/vm/ilstubcache.h +++ b/src/coreclr/vm/ilstubcache.h @@ -74,8 +74,7 @@ class ILStubCache final DWORD cbSig, SigTypeContext *pTypeContext, ILStubLinker* pStubLinker, - BOOL isAsync = FALSE, - class ILStubResolver* pResolver = NULL); + BOOL isAsync = FALSE); MethodTable * GetStubMethodTable() { @@ -101,8 +100,7 @@ class ILStubCache final DWORD cbSig, BOOL isAsync, SigTypeContext *pTypeContext, - AllocMemTracker* pamTracker, - class ILStubResolver* pResolver = NULL); + AllocMemTracker* pamTracker); private: // Inner classes struct ILStubCacheEntry diff --git a/src/coreclr/vm/ilstubresolver.cpp b/src/coreclr/vm/ilstubresolver.cpp index 5c2fa0fc966421..0ab3b1476bccbb 100644 --- a/src/coreclr/vm/ilstubresolver.cpp +++ b/src/coreclr/vm/ilstubresolver.cpp @@ -559,25 +559,3 @@ void ILStubResolver::StubGenFailed(ILStubResolver* pResolver) pResolver->ClearCompileTimeState(ILNotYetGenerated); } - -PCODE AsyncResumeILStubResolver::GetFinalResumeMethodStartAddress() -{ - return m_finalResumeIP; -} - -#ifndef DACCESS_COMPILE -PCODE* AsyncResumeILStubResolver::GetAddrOfResumeMethodStartAddress() -{ - return &m_resumeIP; -} - -void AsyncResumeILStubResolver::SetResumeMethodStartAddress(PCODE ip) -{ - m_resumeIP = ip; -} - -void AsyncResumeILStubResolver::SetFinalResumeMethodStartAddress(PCODE ip) -{ - m_finalResumeIP = ip; -} -#endif diff --git a/src/coreclr/vm/ilstubresolver.h b/src/coreclr/vm/ilstubresolver.h index eefef1bdddd066..4e970423a69da0 100644 --- a/src/coreclr/vm/ilstubresolver.h +++ b/src/coreclr/vm/ilstubresolver.h @@ -108,24 +108,6 @@ class ILStubResolver : public DynamicResolver PTR_LoaderHeap m_loaderHeap; }; -class AsyncResumeILStubResolver : public ILStubResolver -{ -public: - PCODE GetFinalResumeMethodStartAddress(); - -#ifndef DACCESS_COMPILE - PCODE* GetAddrOfResumeMethodStartAddress(); - void SetResumeMethodStartAddress(PCODE ip); - void SetFinalResumeMethodStartAddress(PCODE ip); -#endif - -protected: - // IP to resume in. Can be tier0 code if we suspended in an OSR method. - PCODE m_resumeIP = 0; - // Final IP resumed in. Will be OSR method IP if suspension happened there. - PCODE m_finalResumeIP = 0; -}; - typedef Holder, ILStubResolver::StubGenFailed, 0> ILStubGenHolder; #endif // __ILSTUBRESOLVER_H__ diff --git a/src/coreclr/vm/jitinterface.cpp b/src/coreclr/vm/jitinterface.cpp index 648e0ed5c61e38..9be5481c7d4c6a 100644 --- a/src/coreclr/vm/jitinterface.cpp +++ b/src/coreclr/vm/jitinterface.cpp @@ -61,7 +61,6 @@ #include "tailcallhelp.h" #include "patchpointinfo.h" -#include "ilstubresolver.h" // The Stack Overflow probe takes place in the COOPERATIVE_TRANSITION_BEGIN() macro // @@ -11111,34 +11110,10 @@ void CEEJitInfo::PublishFinalCodeAddress(PCODE addr) { LIMITED_METHOD_CONTRACT; - if (m_resumptionStubResolver == NULL) + if (m_finalCodeAddressSlot != NULL) { - return; + *m_finalCodeAddressSlot = addr; } - - m_resumptionStubResolver->SetFinalResumeMethodStartAddress(addr); - m_resumptionStubResolver->SetResumeMethodStartAddress(addr); - -#ifdef FEATURE_TIERED_COMPILATION - // 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) - { -#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); - m_resumptionStubResolver->SetResumeMethodStartAddress((DWORD_PTR)m_pPatchpointInfoFromRuntime->GetTier0EntryPoint()); -#else // !FEATURE_ON_STACK_REPLACEMENT - _ASSERTE(!"Unexpected optimization tier with OSR disabled"); -#endif // FEATURE_ON_STACK_REPLACEMENT - } -#endif } template @@ -14742,10 +14717,35 @@ CORINFO_METHOD_HANDLE CEEJitInfo::getAsyncResumptionStub() numArgs++; #endif - void* resolverMem = amTracker.Track(loaderAlloc->GetHighFrequencyHeap()->AllocMem(S_SIZE_T(sizeof(AsyncResumeILStubResolver)))); - m_resumptionStubResolver = new (resolverMem) AsyncResumeILStubResolver(); - pCode->EmitLDC((DWORD_PTR)m_resumptionStubResolver->GetAddrOfResumeMethodStartAddress()); - pCode->EmitLDIND_I(); +#ifdef FEATURE_TIERED_COMPILATION + // 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) + { +#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 // !FEATURE_ON_STACK_REPLACEMENT + _ASSERTE(!"Unexpected optimization tier with OSR disabled"); +#endif // FEATURE_ON_STACK_REPLACEMENT + } + else +#endif // FEATURE_TIERED_COMPILATION + { + { + m_finalCodeAddressSlot = (PCODE*)amTracker.Track(m_pMethodBeingCompiled->GetLoaderAllocator()->GetHighFrequencyHeap()->AllocMem(S_SIZE_T(sizeof(PCODE)))); + } + + pCode->EmitLDC((DWORD_PTR)m_finalCodeAddressSlot); + pCode->EmitLDIND_I(); + } pCode->EmitCALLI(pCode->GetSigToken(calliSig.GetRawSig(), calliSig.GetRawSigLen()), numArgs, msig.IsReturnTypeVoid() ? 0 : 1); @@ -14787,18 +14787,15 @@ CORINFO_METHOD_HANDLE CEEJitInfo::getAsyncResumptionStub() md->GetModule(), stubSig.GetRawSig(), stubSig.GetRawSigLen(), &emptyCtx, - &sl, - FALSE, /* isAsync */ - m_resumptionStubResolver); + &sl); amTracker.SuppressRelease(); - m_resumptionStubResolver->SetStubTargetMethodDesc(m_pMethodBeingCompiled); + ILStubResolver *pResolver = result->AsDynamicMethodDesc()->GetILStubResolver(); + pResolver->SetStubTargetMethodDesc(m_pMethodBeingCompiled); const char* optimizationTierName = "UnknownTier"; #ifdef FEATURE_TIERED_COMPILATION - PrepareCodeConfig* config = GetThread()->GetCurrentPrepareCodeConfig(); - NativeCodeVersion ncv = config->GetCodeVersion(); switch (ncv.GetOptimizationTier()) { case NativeCodeVersion::OptimizationTier0: optimizationTierName = "Tier0"; break; diff --git a/src/coreclr/vm/jitinterface.h b/src/coreclr/vm/jitinterface.h index 9c501cd56da53b..1ba4ad3b33a879 100644 --- a/src/coreclr/vm/jitinterface.h +++ b/src/coreclr/vm/jitinterface.h @@ -854,7 +854,7 @@ class CEEJitInfo final : public CEECodeGenInfo m_pPatchpointInfoFromRuntime(NULL), m_ilOffset(0) #endif - , m_resumptionStubResolver(NULL) + , m_finalCodeAddressSlot(NULL) { CONTRACTL { @@ -951,7 +951,7 @@ protected : PatchpointInfo * m_pPatchpointInfoFromRuntime; unsigned m_ilOffset; #endif - class AsyncResumeILStubResolver* m_resumptionStubResolver; + PCODE* m_finalCodeAddressSlot; }; From dc9c7d8a2ade3044ee83b0121e44a89d209dde12 Mon Sep 17 00:00:00 2001 From: Jakob Botsch Nielsen Date: Thu, 23 Oct 2025 11:40:28 +0200 Subject: [PATCH 43/72] Allocate constant resume info data and point continuations at it --- .../CompilerServices/AsyncHelpers.CoreCLR.cs | 12 ++- src/coreclr/inc/cordebuginfo.h | 7 +- src/coreclr/inc/corinfo.h | 6 +- src/coreclr/inc/icorjitinfoimpl_generated.h | 3 +- src/coreclr/inc/jiteeversionguid.h | 10 +- .../jit/ICorJitInfo_wrapper_generated.hpp | 5 +- src/coreclr/jit/async.cpp | 16 +-- src/coreclr/jit/codegen.h | 26 ++--- src/coreclr/jit/codegenarm.cpp | 12 +++ src/coreclr/jit/codegenarm64.cpp | 7 ++ src/coreclr/jit/codegenarmarch.cpp | 78 +------------- src/coreclr/jit/codegencommon.cpp | 88 +++++++-------- src/coreclr/jit/codegenlinear.cpp | 64 ++--------- src/coreclr/jit/codegenxarch.cpp | 84 +++------------ src/coreclr/jit/compiler.h | 6 +- src/coreclr/jit/compiler.hpp | 4 +- src/coreclr/jit/debuginfo.cpp | 31 +----- src/coreclr/jit/debuginfo.h | 29 ++--- src/coreclr/jit/ee_il_dll.cpp | 2 +- src/coreclr/jit/emit.cpp | 100 +++++++++++++++++- src/coreclr/jit/emit.h | 16 ++- src/coreclr/jit/gentree.cpp | 17 +-- src/coreclr/jit/gtlist.h | 4 +- src/coreclr/jit/gtstructs.h | 4 +- src/coreclr/jit/importer.cpp | 18 ++-- src/coreclr/jit/liveness.cpp | 2 +- src/coreclr/jit/lsraarm.cpp | 4 +- .../tools/Common/JitInterface/CorInfoImpl.cs | 2 +- .../JitInterface/CorInfoImpl_generated.cs | 6 +- .../ThunkGenerator/ThunkInput.txt | 2 +- .../aot/jitinterface/jitinterface_generated.h | 7 +- .../tools/superpmi/superpmi-shared/agnostic.h | 2 +- .../tools/superpmi/superpmi-shared/lwmlist.h | 2 +- .../superpmi-shared/methodcontext.cpp | 30 +++--- .../superpmi/superpmi-shared/methodcontext.h | 6 +- .../superpmi-shim-collector/icorjitinfo.cpp | 6 +- .../icorjitinfo_generated.cpp | 5 +- .../icorjitinfo_generated.cpp | 5 +- .../tools/superpmi/superpmi/icorjitinfo.cpp | 4 +- src/coreclr/vm/asynccontinuations.h | 6 +- src/coreclr/vm/corelib.h | 2 +- src/coreclr/vm/debuginfostore.cpp | 7 -- src/coreclr/vm/jitinterface.cpp | 14 ++- src/coreclr/vm/jitinterface.h | 2 +- 44 files changed, 347 insertions(+), 416 deletions(-) diff --git a/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/AsyncHelpers.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/AsyncHelpers.CoreCLR.cs index 79f4f2ec1644a2..efb472bbe19264 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/AsyncHelpers.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/AsyncHelpers.CoreCLR.cs @@ -78,11 +78,19 @@ internal enum ContinuationFlags ContinueOnCapturedTaskScheduler = 64, } + // Keep in sync with dataAsyncResumeInfo in the JIT + internal unsafe struct ResumeInfo + { + public delegate* Resume; + // IP in final code + public void* FinalResumeIP; + } + #pragma warning disable CA1852 // "Type can be sealed" -- no it cannot because the runtime constructs subtypes dynamically internal unsafe class Continuation { public Continuation? Next; - public delegate* Resume; + public ResumeInfo* ResumeInfo; public ContinuationFlags Flags; public int State; @@ -360,7 +368,7 @@ public static unsafe void DispatchContinuations(T task) where T : Task, continuation = nextContinuation; ref byte resultLoc = ref nextContinuation != null ? ref nextContinuation.GetResultStorageOrNull() : ref TOps.GetResultStorage(task); - Continuation? newContinuation = curContinuation.Resume(curContinuation, ref resultLoc); + Continuation? newContinuation = curContinuation.ResumeInfo->Resume(curContinuation, ref resultLoc); if (newContinuation != null) { diff --git a/src/coreclr/inc/cordebuginfo.h b/src/coreclr/inc/cordebuginfo.h index a210a2b57bc29d..c4f9532ff8e699 100644 --- a/src/coreclr/inc/cordebuginfo.h +++ b/src/coreclr/inc/cordebuginfo.h @@ -45,7 +45,8 @@ class ICorDebugInfo STACK_EMPTY = 0x02, // The stack is empty here CALL_SITE = 0x04, // This is a call site. NATIVE_END_OFFSET_UNKNOWN = 0x08, // Indicates a epilog endpoint - CALL_INSTRUCTION = 0x10 // The actual instruction of a call. + CALL_INSTRUCTION = 0x10, // The actual instruction of a call. + ASYNC = 0x20, // Indicates suspension/resumption for an async call }; @@ -442,10 +443,6 @@ class ICorDebugInfo struct AsyncSuspensionPoint { - // Offset of this suspension point's resumption point. - uint32_t NativeResumeOffset; - // Logical return address of the async call (join point of synchronous and resuming paths) - uint32_t NativeJoinOffset; // Count of AsyncContinuationVarInfo in array of locals starting where // the previous suspension point's locals end. uint32_t NumContinuationVars; diff --git a/src/coreclr/inc/corinfo.h b/src/coreclr/inc/corinfo.h index ce3640b3c50018..15523518a38657 100644 --- a/src/coreclr/inc/corinfo.h +++ b/src/coreclr/inc/corinfo.h @@ -1734,8 +1734,8 @@ struct CORINFO_ASYNC_INFO CORINFO_CLASS_HANDLE continuationClsHnd; // 'Next' field CORINFO_FIELD_HANDLE continuationNextFldHnd; - // 'Resume' field - CORINFO_FIELD_HANDLE continuationResumeFldHnd; + // 'ResumeInfo' field + CORINFO_FIELD_HANDLE continuationResumeInfoFldHnd; // 'State' field CORINFO_FIELD_HANDLE continuationStateFldHnd; // 'Flags' field @@ -3350,7 +3350,7 @@ class ICorDynamicInfo : public ICorStaticInfo CORINFO_TAILCALL_HELPERS* pResult ) = 0; - virtual CORINFO_METHOD_HANDLE getAsyncResumptionStub() = 0; + virtual CORINFO_METHOD_HANDLE getAsyncResumptionStub(void** entryPoint) = 0; virtual CORINFO_CLASS_HANDLE getContinuationType( size_t dataSize, diff --git a/src/coreclr/inc/icorjitinfoimpl_generated.h b/src/coreclr/inc/icorjitinfoimpl_generated.h index d8df1d49942d54..8ab46ba76c5c30 100644 --- a/src/coreclr/inc/icorjitinfoimpl_generated.h +++ b/src/coreclr/inc/icorjitinfoimpl_generated.h @@ -666,7 +666,8 @@ CORINFO_CLASS_HANDLE getContinuationType( bool* objRefs, size_t objRefsSize) override; -CORINFO_METHOD_HANDLE getAsyncResumptionStub() override; +CORINFO_METHOD_HANDLE getAsyncResumptionStub( + void** entryPoint) override; bool convertPInvokeCalliToCall( CORINFO_RESOLVED_TOKEN* pResolvedToken, diff --git a/src/coreclr/inc/jiteeversionguid.h b/src/coreclr/inc/jiteeversionguid.h index fb48ca25856949..ab8004ce707608 100644 --- a/src/coreclr/inc/jiteeversionguid.h +++ b/src/coreclr/inc/jiteeversionguid.h @@ -37,11 +37,11 @@ #include -constexpr GUID JITEEVersionIdentifier = { /* 590a21bb-2a2e-4b80-82dc-0bf1fd92a301 */ - 0x590a21bb, - 0x2a2e, - 0x4b80, - {0x82, 0xdc, 0x0b, 0xf1, 0xfd, 0x92, 0xa3, 0x01} +constexpr GUID JITEEVersionIdentifier = { /* a802fbbf-3e14-4b34-a348-5fba9fd756d4 */ + 0xa802fbbf, + 0x3e14, + 0x4b34, + {0xa3, 0x48, 0x5f, 0xba, 0x9f, 0xd7, 0x56, 0xd4} }; #endif // JIT_EE_VERSIONING_GUID_H diff --git a/src/coreclr/jit/ICorJitInfo_wrapper_generated.hpp b/src/coreclr/jit/ICorJitInfo_wrapper_generated.hpp index 1cb6a17b4d1090..6ad0a136a09b47 100644 --- a/src/coreclr/jit/ICorJitInfo_wrapper_generated.hpp +++ b/src/coreclr/jit/ICorJitInfo_wrapper_generated.hpp @@ -1558,10 +1558,11 @@ CORINFO_CLASS_HANDLE WrapICorJitInfo::getContinuationType( return temp; } -CORINFO_METHOD_HANDLE WrapICorJitInfo::getAsyncResumptionStub() +CORINFO_METHOD_HANDLE WrapICorJitInfo::getAsyncResumptionStub( + void** entryPoint) { API_ENTER(getAsyncResumptionStub); - CORINFO_METHOD_HANDLE temp = wrapHnd->getAsyncResumptionStub(); + CORINFO_METHOD_HANDLE temp = wrapHnd->getAsyncResumptionStub(entryPoint); API_LEAVE(getAsyncResumptionStub); return temp; } diff --git a/src/coreclr/jit/async.cpp b/src/coreclr/jit/async.cpp index ca5186bd2fb3b1..e2d5439149ea56 100644 --- a/src/coreclr/jit/async.cpp +++ b/src/coreclr/jit/async.cpp @@ -1424,12 +1424,12 @@ BasicBlock* AsyncTransformation::CreateSuspension( GenTree* storeNewContinuation = m_comp->gtNewStoreLclVarNode(m_newContinuationVar, allocContinuation); 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_asyncInfo->continuationResumeFldHnd); - GenTree* resumeAddr = - new (m_comp, GT_ASYNC_RESUME_TRAMPOLINE) GenTreeVal(GT_ASYNC_RESUME_TRAMPOLINE, TYP_I_IMPL, (ssize_t)stateNum); - GenTree* storeResume = StoreAtOffset(newContinuation, resumeOffset, resumeAddr, TYP_I_IMPL); + // Fill in 'ResumeInfo' + GenTree* newContinuation = m_comp->gtNewLclvNode(m_newContinuationVar, TYP_REF); + unsigned resumeInfoOffset = m_comp->info.compCompHnd->getFieldOffset(m_asyncInfo->continuationResumeInfoFldHnd); + GenTree* resumeInfoAddr = + new (m_comp, GT_ASYNC_RESUME_INFO) GenTreeVal(GT_ASYNC_RESUME_INFO, TYP_I_IMPL, (ssize_t)stateNum); + GenTree* storeResume = StoreAtOffset(newContinuation, resumeInfoOffset, resumeInfoAddr, TYP_I_IMPL); LIR::AsRange(suspendBB).InsertAtEnd(LIR::SeqTree(m_comp, storeResume)); // Fill in 'state' @@ -2116,8 +2116,8 @@ void AsyncTransformation::CreateDebugInfoForSuspensionPoint(GenTreeCall* suspensionPoint.numContinuationVars = numLocals; m_comp->compSuspensionPoints->push_back(suspensionPoint); - GenTree* recordOffset = new (m_comp, GT_RECORD_ASYNC_JOIN) - GenTreeVal(GT_RECORD_ASYNC_JOIN, TYP_VOID, (int)(m_comp->compSuspensionPoints->size() - 1)); + GenTree* recordOffset = new (m_comp, GT_RECORD_ASYNC_RESUME) + GenTreeVal(GT_RECORD_ASYNC_RESUME, TYP_VOID, (int)(m_comp->compSuspensionPoints->size() - 1)); LIR::AsRange(joinBB).InsertAtBeginning(recordOffset); } diff --git a/src/coreclr/jit/codegen.h b/src/coreclr/jit/codegen.h index 67f454ca8d609c..727b4688980020 100644 --- a/src/coreclr/jit/codegen.h +++ b/src/coreclr/jit/codegen.h @@ -186,8 +186,8 @@ class CodeGen final : public CodeGenInterface // the current (pending) label ref, a label which has been referenced but not yet seen BasicBlock* genPendingCallLabel; - // labels for trampolines to resumption stub - jitstd::vector* genAsyncResumptionTrampolineLabels = nullptr; + emitter::dataSection* genAsyncResumeInfoTable = nullptr; + UNATIVE_OFFSET genAsyncResumeInfoTableOffset = UINT_MAX; void** codePtr; void* codePtrRW; @@ -211,8 +211,6 @@ class CodeGen final : public CodeGenInterface void genInitializeRegisterState(); void genCodeForBBlist(); - void genCodeForAsyncResumeTrampolines(); - void genCodeForAsyncResumeTrampolineJump(CORINFO_METHOD_HANDLE resumeMethod, const CORINFO_CONST_LOOKUP& lookup); public: void genSpillVar(GenTree* tree); @@ -225,7 +223,8 @@ class CodeGen final : public CodeGenInterface void genGCWriteBarrier(GenTreeStoreInd* store, GCInfo::WriteBarrierForm wbf); BasicBlock* genCreateTempLabel(); - BasicBlock* genCreateAsyncResumptionTrampolineLabel(GenTreeVal* node); + + void genRecordAsyncResume(GenTreeVal* asyncResume); private: void genLogLabel(BasicBlock* bb); @@ -1205,13 +1204,16 @@ class CodeGen final : public CodeGenInterface void genStructPutArgPartialRepMovs(GenTreePutArgStk* putArgStkNode); #endif - void genCodeForStoreBlk(GenTreeBlk* storeBlkNode); - void genCodeForInitBlkLoop(GenTreeBlk* initBlkNode); - void genCodeForInitBlkRepStos(GenTreeBlk* initBlkNode); - void genCodeForInitBlkUnroll(GenTreeBlk* initBlkNode); - unsigned genEmitJumpTable(GenTree* treeNode, bool relativeAddr); - void genJumpTable(GenTree* tree); - void genTableBasedSwitch(GenTree* tree); + void genCodeForStoreBlk(GenTreeBlk* storeBlkNode); + void genCodeForInitBlkLoop(GenTreeBlk* initBlkNode); + void genCodeForInitBlkRepStos(GenTreeBlk* initBlkNode); + void genCodeForInitBlkUnroll(GenTreeBlk* initBlkNode); + unsigned genEmitJumpTable(GenTree* treeNode, bool relativeAddr); + void genJumpTable(GenTree* tree); + void genTableBasedSwitch(GenTree* tree); + void genAsyncResumeInfo(GenTreeVal* tree); + UNATIVE_OFFSET genEmitAsyncResumeInfoTable(emitter::dataSection** dataSec); + CORINFO_FIELD_HANDLE genEmitAsyncResumeInfo(unsigned stateNum); #if defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64) instruction genGetInsForOper(GenTree* treeNode); #else diff --git a/src/coreclr/jit/codegenarm.cpp b/src/coreclr/jit/codegenarm.cpp index 4af842a2d10c73..25fdf4fa9f209d 100644 --- a/src/coreclr/jit/codegenarm.cpp +++ b/src/coreclr/jit/codegenarm.cpp @@ -653,6 +653,18 @@ void CodeGen::genJumpTable(GenTree* treeNode) genProduceReg(treeNode); } +//------------------------------------------------------------------------ +// genAsyncResumeInfo: emits offset of async resume info +// +void CodeGen::genAsyncResumeInfo(GenTreeVal* treeNode) +{ + CORINFO_FIELD_HANDLE fieldOffs = genEmitAsyncResumeInfo((unsigned)treeNode->gtVal1); + assert(compiler->eeIsJitDataOffs(fieldOffs)); + genMov32RelocatableDataLabel(compiler->eeGetJitDataOffs(fieldOffs), treeNode->GetRegNum()); + + genProduceReg(treeNode); +} + //------------------------------------------------------------------------ // genGetInsForOper: Return instruction encoding of the operation tree. // diff --git a/src/coreclr/jit/codegenarm64.cpp b/src/coreclr/jit/codegenarm64.cpp index 4c3bf607017696..3394cf9ff340bb 100644 --- a/src/coreclr/jit/codegenarm64.cpp +++ b/src/coreclr/jit/codegenarm64.cpp @@ -3764,6 +3764,13 @@ void CodeGen::genJumpTable(GenTree* treeNode) genProduceReg(treeNode); } +void CodeGen::genAsyncResumeInfo(GenTreeVal* treeNode) +{ + GetEmitter()->emitIns_R_C(INS_adr, emitActualTypeSize(TYP_I_IMPL), treeNode->GetRegNum(), REG_NA, + genEmitAsyncResumeInfo((unsigned)treeNode->gtVal1), 0); + genProduceReg(treeNode); +} + //------------------------------------------------------------------------ // genLockedInstructions: Generate code for a GT_XADD, GT_XAND, GT_XORR or GT_XCHG node. // diff --git a/src/coreclr/jit/codegenarmarch.cpp b/src/coreclr/jit/codegenarmarch.cpp index 0e7549244678af..a083d3b9caa3b3 100644 --- a/src/coreclr/jit/codegenarmarch.cpp +++ b/src/coreclr/jit/codegenarmarch.cpp @@ -514,13 +514,8 @@ void CodeGen::genCodeForTreeNode(GenTree* treeNode) genCodeForAsyncContinuation(treeNode); break; - case GT_ASYNC_RESUME_TRAMPOLINE: -#if defined(TARGET_ARM) - genMov32RelocatableDisplacement(genCreateAsyncResumptionTrampolineLabel(treeNode->AsVal()), targetReg); -#else - emit->emitIns_R_L(INS_adr, EA_PTRSIZE, genCreateAsyncResumptionTrampolineLabel(treeNode->AsVal()), - targetReg); -#endif + case GT_ASYNC_RESUME_INFO: + genAsyncResumeInfo(treeNode->AsVal()); break; case GT_PINVOKE_PROLOG: @@ -562,7 +557,7 @@ void CodeGen::genCodeForTreeNode(GenTree* treeNode) #endif // TARGET_ARM case GT_IL_OFFSET: - case GT_RECORD_ASYNC_JOIN: + case GT_RECORD_ASYNC_RESUME: // Do nothing; these nodes are simply markers for debug info. break; @@ -5137,71 +5132,4 @@ void CodeGen::genFnEpilog(BasicBlock* block) compiler->unwindEndEpilog(); } -//----------------------------------------------------------------------------------- -// genCodeForAsyncresumeTrampolineJump: -// Generate code to jump to an async resumption stub. -// -// Arguments: -// methHnd - Handle of resumption stub -// lookup - Lookup for the address of the resumption stub -// -// Remarks: -// The codegen for these jumps must handle a non-standard environment. No -// prolog has been run when these are invoked, so they cannot use locals, -// they cannot use non-volatile registers, and they cannot trash the argument -// registers that resumption stubs use (see BuildResumptionStubSignature in -// the VM). -// -void CodeGen::genCodeForAsyncResumeTrampolineJump(CORINFO_METHOD_HANDLE methHnd, const CORINFO_CONST_LOOKUP& lookup) -{ - switch (lookup.accessType) - { - case IAT_VALUE: - case IAT_PVALUE: - { - EmitCallParams call; - call.ptrVars = VarSetOps::MakeEmpty(compiler); - call.gcrefRegs = RBM_NONE; - call.byrefRegs = RBM_NONE; - call.isJump = true; - call.methHnd = methHnd; - - if ((lookup.accessType == IAT_VALUE) && validImmForBL((ssize_t)lookup.addr)) - { - call.callType = EC_FUNC_TOKEN; - call.addr = lookup.addr; - } - else - { -#ifdef TARGET_ARM64 - regNumber tempReg = REG_IP0; -#else - regNumber tempReg = REG_R12; -#endif - - instGen_Set_Reg_To_Imm(EA_SET_FLG(EA_PTRSIZE, EA_CNS_RELOC_FLG), tempReg, (ssize_t)lookup.addr, - INS_FLAGS_DONT_CARE DEBUGARG((size_t)methHnd) DEBUGARG(GTF_ICON_FTN_ADDR)); - - if (lookup.accessType == IAT_PVALUE) - { - GetEmitter()->emitIns_R_R(ins_Load(TYP_I_IMPL), EA_PTRSIZE, tempReg, tempReg); - } - call.callType = EC_INDIR_R; - call.ireg = tempReg; - } - GetEmitter()->emitIns_Call(call); - break; - } - case IAT_PPVALUE: - noway_assert(!"Cannot handle PPVALUE access type"); - break; - case IAT_RELPVALUE: - noway_assert(!"Cannot handle RELPVALUE access type"); - break; - default: - noway_assert(!"Cannot handle access type"); - break; - } -} - #endif // TARGET_ARMARCH diff --git a/src/coreclr/jit/codegencommon.cpp b/src/coreclr/jit/codegencommon.cpp index 29214943e47ced..c96416ffb730f8 100644 --- a/src/coreclr/jit/codegencommon.cpp +++ b/src/coreclr/jit/codegencommon.cpp @@ -988,28 +988,6 @@ BasicBlock* CodeGen::genCreateTempLabel() return block; } -BasicBlock* CodeGen::genCreateAsyncResumptionTrampolineLabel(GenTreeVal* node) -{ - assert(node->OperIs(GT_ASYNC_RESUME_TRAMPOLINE)); - - if (genAsyncResumptionTrampolineLabels == nullptr) - { - genAsyncResumptionTrampolineLabels = - new (compiler, CMK_Codegen) jitstd::vector(compiler->compSuspensionPoints->size(), nullptr, - compiler->getAllocator(CMK_Codegen)); - } - - assert(genAsyncResumptionTrampolineLabels->size() > node->gtVal1); - - BasicBlock*& label = (*genAsyncResumptionTrampolineLabels)[node->gtVal1]; - if (label == nullptr) - { - label = genCreateTempLabel(); - } - - return label; -} - void CodeGen::genLogLabel(BasicBlock* bb) { #ifdef DEBUG @@ -4463,15 +4441,8 @@ void CodeGen::genReserveEpilog(BasicBlock* block) JITDUMP("Reserving epilog IG for block " FMT_BB "\n", block->bbNum); - bool isLast = block->IsLast(); - if ((genAsyncResumptionTrampolineLabels != nullptr) && (genAsyncResumptionTrampolineLabels->size() != 0) && - (block == compiler->fgLastBBInMainFunction())) - { - isLast = false; - } - GetEmitter()->emitCreatePlaceholderIG(IGPT_EPILOG, block, VarSetOps::MakeEmpty(compiler), gcInfo.gcRegGCrefSetCur, - gcInfo.gcRegByrefSetCur, isLast); + gcInfo.gcRegByrefSetCur, block->IsLast()); } /***************************************************************************** @@ -5673,6 +5644,30 @@ unsigned CodeGen::genEmitJumpTable(GenTree* treeNode, bool relativeAddr) return jmpTabBase; } +UNATIVE_OFFSET CodeGen::genEmitAsyncResumeInfoTable(emitter::dataSection** dataSection) +{ + assert(compiler->compSuspensionPoints != nullptr); + + if (genAsyncResumeInfoTable == nullptr) + { + GetEmitter()->emitAsyncResumeTable((unsigned)compiler->compSuspensionPoints->size(), + &genAsyncResumeInfoTableOffset, &genAsyncResumeInfoTable); + } + + *dataSection = genAsyncResumeInfoTable; + return genAsyncResumeInfoTableOffset; +} + +CORINFO_FIELD_HANDLE CodeGen::genEmitAsyncResumeInfo(unsigned stateNum) +{ + assert(compiler->compSuspensionPoints != nullptr); + assert(stateNum < compiler->compSuspensionPoints->size()); + + emitter::dataSection* dataSection; + UNATIVE_OFFSET baseOffs = genEmitAsyncResumeInfoTable(&dataSection); + return compiler->eeFindJitDataOffs(baseOffs + stateNum * sizeof(emitter::dataAsyncResumeInfo)); +} + //------------------------------------------------------------------------ // getCallTarget - Get the node that evaluates to the call target // @@ -6169,16 +6164,21 @@ void CodeGen::genIPmappingDisp(unsigned mappingNum, const IPmappingDsc* ipMappin case IPmappingDscKind::Normal: const ILLocation& loc = ipMapping->ipmdLoc; Compiler::eeDispILOffs(loc.GetOffset()); - if (loc.IsStackEmpty()) + if ((loc.GetSourceType() & ICorDebugInfo::STACK_EMPTY) != 0) { printf(" STACK_EMPTY"); } - if (loc.IsCall()) + if ((loc.GetSourceType() & ICorDebugInfo::CALL_INSTRUCTION) != 0) { printf(" CALL_INSTRUCTION"); } + if ((loc.GetSourceType() & ICorDebugInfo::ASYNC) != 0) + { + printf(" ASYNC"); + } + break; } @@ -6411,8 +6411,8 @@ void CodeGen::genIPmappingGen() // For managed return values we store all calls. Keep both in this case // too. - if (((prev->ipmdKind == IPmappingDscKind::Normal) && (prev->ipmdLoc.IsCall())) || - ((it->ipmdKind == IPmappingDscKind::Normal) && (it->ipmdLoc.IsCall()))) + if (((prev->ipmdKind == IPmappingDscKind::Normal) && prev->ipmdLoc.IsCallInstruction()) || + ((it->ipmdKind == IPmappingDscKind::Normal) && it->ipmdLoc.IsCallInstruction())) { ++it; continue; @@ -6512,7 +6512,7 @@ void CodeGen::genReportRichDebugInfoInlineTreeToFile(FILE* file, InlineContext* fprintf(file, "{\"Ordinal\":%u,", context->GetOrdinal()); fprintf(file, "\"MethodID\":%lld,", (int64_t)context->GetCallee()); fprintf(file, "\"ILOffset\":%u,", context->GetLocation().GetOffset()); - fprintf(file, "\"LocationFlags\":%u,", (uint32_t)context->GetLocation().EncodeSourceTypes()); + fprintf(file, "\"LocationFlags\":%u,", (uint32_t)context->GetLocation().GetSourceType()); fprintf(file, "\"ExactILOffset\":%u,", context->GetActualCallOffset()); auto append = [&]() { char buffer[256]; @@ -6677,7 +6677,7 @@ void CodeGen::genReportRichDebugInfo() mapping->NativeOffset = richMapping.nativeLoc.CodeOffset(GetEmitter()); mapping->Inlinee = richMapping.debugInfo.GetInlineContext()->GetOrdinal(); mapping->ILOffset = richMapping.debugInfo.GetLocation().GetOffset(); - mapping->Source = richMapping.debugInfo.GetLocation().EncodeSourceTypes(); + mapping->Source = richMapping.debugInfo.GetLocation().GetSourceType(); mappingIndex++; } @@ -6742,17 +6742,7 @@ void CodeGen::genReportAsyncDebugInfo() compiler->info.compCompHnd->allocateArray(suspPoints->size() * sizeof(ICorDebugInfo::AsyncSuspensionPoint))); for (size_t i = 0; i < suspPoints->size(); i++) { - AsyncSuspensionPoint& suspPoint = (*suspPoints)[i]; - if (suspPoint.nativeResumeLoc.Valid()) - { - hostSuspensionPoints[i].NativeResumeOffset = suspPoint.nativeResumeLoc.CodeOffset(GetEmitter()); - } - - if (suspPoint.nativeJoinLoc.Valid()) - { - hostSuspensionPoints[i].NativeJoinOffset = suspPoint.nativeJoinLoc.CodeOffset(GetEmitter()); - } - + AsyncSuspensionPoint& suspPoint = (*suspPoints)[i]; hostSuspensionPoints[i].NumContinuationVars = suspPoint.numContinuationVars; } @@ -6771,9 +6761,7 @@ void CodeGen::genReportAsyncDebugInfo() printf("Reported async suspension points:\n"); for (size_t i = 0; i < suspPoints->size(); i++) { - printf(" [%zu] ResumeOffset = %x, JoinOffset = %x, NumAsyncVars = %u\n", i, - hostSuspensionPoints[i].NativeResumeOffset, hostSuspensionPoints[i].NativeJoinOffset, - hostSuspensionPoints[i].NumContinuationVars); + printf(" [%zu] NumAsyncVars = %u\n", i, hostSuspensionPoints[i].NumContinuationVars); } printf("Reported async vars:\n"); diff --git a/src/coreclr/jit/codegenlinear.cpp b/src/coreclr/jit/codegenlinear.cpp index 8f88357e9e859e..9c4aa63a2a297e 100644 --- a/src/coreclr/jit/codegenlinear.cpp +++ b/src/coreclr/jit/codegenlinear.cpp @@ -462,13 +462,8 @@ void CodeGen::genCodeForBBlist() #endif // DEBUG } - else if (node->OperIs(GT_RECORD_ASYNC_JOIN)) + else if (node->OperIs(GT_RECORD_ASYNC_RESUME)) { - size_t index = node->AsVal()->gtVal1; - assert(compiler->compSuspensionPoints != nullptr); - assert(index < compiler->compSuspensionPoints->size()); - - (*compiler->compSuspensionPoints)[index].nativeJoinLoc.CaptureLocation(GetEmitter()); } genCodeForTreeNode(node); @@ -864,16 +859,7 @@ void CodeGen::genCodeForBBlist() } compiler->compCurBB = nullptr; #endif // DEBUG - - // We may need to generate jump trampolines for async resumption - // outside the main method. This ensures we have unique IPs inside our - // main method that Coroutine.Resume points at for each state, for - // diagnostic purposes. - if ((genAsyncResumptionTrampolineLabels != nullptr) && (block == compiler->fgLastBBInMainFunction())) - { - genCodeForAsyncResumeTrampolines(); - } - } //------------------ END-FOR each block of the method ------------------- + } //------------------ END-FOR each block of the method ------------------- #if defined(FEATURE_EH_WINDOWS_X86) // If this is a synchronized method on x86, and we generated all the code without @@ -918,47 +904,17 @@ void CodeGen::genCodeForBBlist() #endif } -//------------------------------------------------------------------------ -// genCodeForAsyncResumeTrampolines: -// Generate jumps to the async resumption stub and bind them to each label -// that was referenced from suspension code. -// -// Arguments: -// None -// -void CodeGen::genCodeForAsyncResumeTrampolines() +void CodeGen::genRecordAsyncResume(GenTreeVal* asyncResume) { - if (genAsyncResumptionTrampolineLabels->size() == 0) - { - return; - } - - GetEmitter()->emitThisGCrefVars = VarSetOps::MakeEmpty(compiler); - GetEmitter()->emitThisGCrefRegs = RBM_NONE; - GetEmitter()->emitThisByrefRegs = RBM_NONE; - GetEmitter()->emitNxtIG(); - GetEmitter()->emitDisableGC(); - // emitDisableGC still allows GC at the current IP, so we need to insert a nop. - instGen(INS_nop); - - CORINFO_METHOD_HANDLE resumeStub = compiler->info.compCompHnd->getAsyncResumptionStub(); - CORINFO_CONST_LOOKUP resumeStubLookup; - compiler->info.compCompHnd->getFunctionEntryPoint(resumeStub, &resumeStubLookup); + size_t index = asyncResume->gtVal1; + assert(compiler->compSuspensionPoints != nullptr); + assert(index < compiler->compSuspensionPoints->size()); - for (size_t i = 0; i < genAsyncResumptionTrampolineLabels->size(); i++) - { - BasicBlock* label = (*genAsyncResumptionTrampolineLabels)[i]; - if (label == nullptr) - continue; - - genLogLabel(label); - label->bbEmitCookie = GetEmitter()->emitAddInlineLabel(); - - (*compiler->compSuspensionPoints)[i].nativeResumeLoc.CaptureLocation(GetEmitter()); - genCodeForAsyncResumeTrampolineJump(resumeStub, resumeStubLookup); - } + emitter::dataSection* asyncResumeInfo; + genEmitAsyncResumeInfoTable(&asyncResumeInfo); - GetEmitter()->emitEnableGC(); + BYTE* addr = asyncResumeInfo->dsCont + index * sizeof(emitLocation); + new (addr, jitstd::placement_t()) emitLocation(GetEmitter()); } /* diff --git a/src/coreclr/jit/codegenxarch.cpp b/src/coreclr/jit/codegenxarch.cpp index 9b6bea1aea8759..92b8bffd0be64b 100644 --- a/src/coreclr/jit/codegenxarch.cpp +++ b/src/coreclr/jit/codegenxarch.cpp @@ -2265,9 +2265,8 @@ void CodeGen::genCodeForTreeNode(GenTree* treeNode) emit->emitIns_R_L(INS_lea, EA_PTR_DSP_RELOC, genPendingCallLabel, treeNode->GetRegNum()); break; - case GT_ASYNC_RESUME_TRAMPOLINE: - emit->emitIns_R_L(INS_lea, EA_PTR_DSP_RELOC, genCreateAsyncResumptionTrampolineLabel(treeNode->AsVal()), - treeNode->GetRegNum()); + case GT_ASYNC_RESUME_INFO: + genAsyncResumeInfo(treeNode->AsVal()); break; case GT_STORE_BLK: @@ -2290,8 +2289,11 @@ void CodeGen::genCodeForTreeNode(GenTree* treeNode) #endif case GT_IL_OFFSET: - case GT_RECORD_ASYNC_JOIN: - // Do nothing; these nodes are simply markers for debug info. + // Do nothing; this node is a marker for debug info. + break; + + case GT_RECORD_ASYNC_RESUME: + genRecordAsyncResume(treeNode->AsVal()); break; #if defined(TARGET_AMD64) @@ -4421,6 +4423,13 @@ void CodeGen::genJumpTable(GenTree* treeNode) genProduceReg(treeNode); } +void CodeGen::genAsyncResumeInfo(GenTreeVal* treeNode) +{ + GetEmitter()->emitIns_R_C(INS_lea, emitTypeSize(TYP_I_IMPL), treeNode->GetRegNum(), + genEmitAsyncResumeInfo((unsigned)treeNode->gtVal1), 0); + genProduceReg(treeNode); +} + //------------------------------------------------------------------------ // genCodeForLockAdd: Generate code for a GT_LOCKADD node // @@ -11697,71 +11706,6 @@ void CodeGen::instGen_MemoryBarrier(BarrierKind barrierKind) } } -//----------------------------------------------------------------------------------- -// genCodeForAsyncresumeTrampolineJump: -// Generate code to jump to an async resumption stub. -// -// Arguments: -// methHnd - Handle of resumption stub -// lookup - Lookup for the address of the resumption stub -// -// Remarks: -// The codegen for these jumps must handle a non-standard environment. No -// prolog has been run when these are invoked, so they cannot use locals, -// they cannot use non-volatile registers, and they cannot trash the argument -// registers that resumption stubs use (see BuildResumptionStubSignature in -// the VM). -// -void CodeGen::genCodeForAsyncResumeTrampolineJump(CORINFO_METHOD_HANDLE methHnd, const CORINFO_CONST_LOOKUP& lookup) -{ - switch (lookup.accessType) - { - case IAT_VALUE: - case IAT_PVALUE: - { - EmitCallParams call; - call.ptrVars = VarSetOps::MakeEmpty(compiler); - call.gcrefRegs = RBM_NONE; - call.byrefRegs = RBM_NONE; - call.isJump = true; - call.methHnd = methHnd; - - bool containedImmInd = true; - -#ifdef DEBUG - containedImmInd &= compiler->opts.compEnablePCRelAddr; -#endif - containedImmInd &= (lookup.accessType == IAT_PVALUE) && - (compiler->eeGetRelocTypeHint(lookup.addr) == IMAGE_REL_BASED_REL32); - - if ((lookup.accessType == IAT_PVALUE) && !containedImmInd) - { - instGen_Set_Reg_To_Imm(EA_SET_FLG(EA_PTRSIZE, EA_CNS_RELOC_FLG), REG_EAX, (ssize_t)lookup.addr, - INS_FLAGS_DONT_CARE DEBUGARG((size_t)methHnd) DEBUGARG(GTF_ICON_METHOD_HDL)); - call.callType = EC_INDIR_ARD; - call.ireg = REG_EAX; - } - else - { - call.callType = lookup.accessType == IAT_VALUE ? EC_FUNC_TOKEN : EC_FUNC_TOKEN_INDIR; - call.addr = (void*)lookup.addr; - } - - GetEmitter()->emitIns_Call(call); - break; - } - case IAT_PPVALUE: - noway_assert(!"Cannot handle PPVALUE access type"); - break; - case IAT_RELPVALUE: - noway_assert(!"Cannot handle RELPVALUE access type"); - break; - default: - noway_assert(!"Cannot handle access type"); - break; - } -} - #ifdef TARGET_AMD64 // Returns relocation type hint for an addr. // Note that there are no reloc hints on x86. diff --git a/src/coreclr/jit/compiler.h b/src/coreclr/jit/compiler.h index 77a5cf0d93a641..409b040f8bf108 100644 --- a/src/coreclr/jit/compiler.h +++ b/src/coreclr/jit/compiler.h @@ -2399,8 +2399,6 @@ struct RichIPMapping struct AsyncSuspensionPoint { - emitLocation nativeJoinLoc; - emitLocation nativeResumeLoc; unsigned numContinuationVars = 0; }; @@ -11706,7 +11704,7 @@ class GenTreeVisitor // Leaf nodes case GT_CATCH_ARG: case GT_ASYNC_CONTINUATION: - case GT_ASYNC_RESUME_TRAMPOLINE: + case GT_ASYNC_RESUME_INFO: case GT_LABEL: case GT_FTN_ADDR: case GT_RET_EXPR: @@ -11738,7 +11736,7 @@ class GenTreeVisitor case GT_PINVOKE_PROLOG: case GT_PINVOKE_EPILOG: case GT_IL_OFFSET: - case GT_RECORD_ASYNC_JOIN: + case GT_RECORD_ASYNC_RESUME: case GT_NOP: case GT_SWIFT_ERROR: case GT_GCPOLL: diff --git a/src/coreclr/jit/compiler.hpp b/src/coreclr/jit/compiler.hpp index bd2fc62e307e64..36406e9fa8ef15 100644 --- a/src/coreclr/jit/compiler.hpp +++ b/src/coreclr/jit/compiler.hpp @@ -4491,7 +4491,7 @@ GenTree::VisitResult GenTree::VisitOperands(TVisitor visitor) case GT_LCL_ADDR: case GT_CATCH_ARG: case GT_ASYNC_CONTINUATION: - case GT_ASYNC_RESUME_TRAMPOLINE: + case GT_ASYNC_RESUME_INFO: case GT_LABEL: case GT_FTN_ADDR: case GT_RET_EXPR: @@ -4523,7 +4523,7 @@ GenTree::VisitResult GenTree::VisitOperands(TVisitor visitor) case GT_PINVOKE_PROLOG: case GT_PINVOKE_EPILOG: case GT_IL_OFFSET: - case GT_RECORD_ASYNC_JOIN: + case GT_RECORD_ASYNC_RESUME: case GT_NOP: case GT_SWIFT_ERROR: case GT_GCPOLL: diff --git a/src/coreclr/jit/debuginfo.cpp b/src/coreclr/jit/debuginfo.cpp index 6b4e25635f2d38..29f4a3a0d50276 100644 --- a/src/coreclr/jit/debuginfo.cpp +++ b/src/coreclr/jit/debuginfo.cpp @@ -4,32 +4,6 @@ #include "jitpch.h" #include "debuginfo.h" -//------------------------------------------------------------------------ -// EncodeSourceTypes: -// Encode the JIT-EE source type for an ILLocation. -// -// Returns: -// The JIT-EE interface source type. -// -// Remarks: -// We currently encode only calls and stack empty location. -// -ICorDebugInfo::SourceTypes ILLocation::EncodeSourceTypes() const -{ - int source = 0; - if (IsStackEmpty()) - { - source |= ICorDebugInfo::STACK_EMPTY; - } - - if (IsCall()) - { - source |= ICorDebugInfo::CALL_INSTRUCTION; - } - - return static_cast(source); -} - #ifdef DEBUG //------------------------------------------------------------------------ // Dump: Print a textual representation of this ILLocation. @@ -47,8 +21,9 @@ void ILLocation::Dump() const else { printf("0x%03X[", GetOffset()); - printf("%c", IsStackEmpty() ? 'E' : '-'); - printf("%c", IsCall() ? 'C' : '-'); + printf("%c", ((m_sourceType & ICorDebugInfo::STACK_EMPTY) != 0) ? 'E' : '-'); + printf("%c", ((m_sourceType & ICorDebugInfo::CALL_INSTRUCTION) != 0) ? 'C' : '-'); + printf("%c", ((m_sourceType & ICorDebugInfo::ASYNC) != 0) ? 'A' : '-'); printf("]"); } } diff --git a/src/coreclr/jit/debuginfo.h b/src/coreclr/jit/debuginfo.h index 72119b905c948a..fb93e628c25285 100644 --- a/src/coreclr/jit/debuginfo.h +++ b/src/coreclr/jit/debuginfo.h @@ -13,16 +13,12 @@ class ILLocation { public: ILLocation() - : m_offset(BAD_IL_OFFSET) - , m_isStackEmpty(false) - , m_isCall(false) { } - ILLocation(IL_OFFSET offset, bool isStackEmpty, bool isCall) + ILLocation(IL_OFFSET offset, ICorDebugInfo::SourceTypes sourceType) : m_offset(offset) - , m_isStackEmpty(isStackEmpty) - , m_isCall(isCall) + , m_sourceType(sourceType) { } @@ -31,18 +27,14 @@ class ILLocation return m_offset; } - // Is this source location at a stack empty point? We need to be able to - // report this information back to the debugger since we only allow EnC - // transitions at stack empty points. - bool IsStackEmpty() const + ICorDebugInfo::SourceTypes GetSourceType() const { - return m_isStackEmpty; + return m_sourceType; } - // Is this a call instruction? Used for managed return values. - bool IsCall() const + bool IsCallInstruction() const { - return m_isCall; + return (m_sourceType & ICorDebugInfo::CALL_INSTRUCTION) != 0; } bool IsValid() const @@ -52,7 +44,7 @@ class ILLocation inline bool operator==(const ILLocation& other) const { - return (m_offset == other.m_offset) && (m_isStackEmpty == other.m_isStackEmpty) && (m_isCall == other.m_isCall); + return (m_offset == other.m_offset) && (m_sourceType == other.m_sourceType); } inline bool operator!=(const ILLocation& other) const @@ -60,17 +52,14 @@ class ILLocation return !(*this == other); } - ICorDebugInfo::SourceTypes EncodeSourceTypes() const; - #ifdef DEBUG // Dump textual representation of this ILLocation to jitstdout. void Dump() const; #endif private: - IL_OFFSET m_offset; - bool m_isStackEmpty : 1; - bool m_isCall : 1; + IL_OFFSET m_offset = BAD_IL_OFFSET; + ICorDebugInfo::SourceTypes m_sourceType = ICorDebugInfo::SOURCE_TYPE_INVALID; }; // Represents debug information about a statement. diff --git a/src/coreclr/jit/ee_il_dll.cpp b/src/coreclr/jit/ee_il_dll.cpp index b419d44cc3003f..5dcb4d42c37680 100644 --- a/src/coreclr/jit/ee_il_dll.cpp +++ b/src/coreclr/jit/ee_il_dll.cpp @@ -1003,7 +1003,7 @@ void Compiler::eeSetLIinfo(unsigned which, UNATIVE_OFFSET nativeOffset, IPmappin { case IPmappingDscKind::Normal: eeBoundaries[which].ilOffset = loc.GetOffset(); - eeBoundaries[which].source = loc.EncodeSourceTypes(); + eeBoundaries[which].source = loc.GetSourceType(); break; case IPmappingDscKind::Prolog: eeBoundaries[which].ilOffset = ICorDebugInfo::PROLOG; diff --git a/src/coreclr/jit/emit.cpp b/src/coreclr/jit/emit.cpp index 88cf1c67337c61..80f1701e73762b 100644 --- a/src/coreclr/jit/emit.cpp +++ b/src/coreclr/jit/emit.cpp @@ -7965,6 +7965,45 @@ UNATIVE_OFFSET emitter::emitBBTableDataGenBeg(unsigned numEntries, bool relative return secOffs; } +void emitter::emitAsyncResumeTable(unsigned numEntries, UNATIVE_OFFSET* dataSecOffs, emitter::dataSection** dataSec) +{ + UNATIVE_OFFSET secOffs = emitConsDsc.dsdOffs; + unsigned emittedSize = sizeof(emitter::dataAsyncResumeInfo) * numEntries; + emitConsDsc.dsdOffs += emittedSize; + + dataSection* secDesc = (dataSection*)emitGetMem(roundUp(sizeof(dataSection) + numEntries * sizeof(emitLocation))); + + for (unsigned i = 0; i < numEntries; i++) + new (secDesc->dsCont + i * sizeof(emitLocation), jitstd::placement_t()) emitLocation(); + + secDesc->dsSize = emittedSize; + secDesc->dsType = dataSection::asyncResumeInfo; + secDesc->dsDataType = TYP_UNKNOWN; + secDesc->dsNext = nullptr; + + if (emitConsDsc.dsdLast) + { + emitConsDsc.dsdLast->dsNext = secDesc; + } + else + { + emitConsDsc.dsdList = secDesc; + } + + emitConsDsc.dsdLast = secDesc; + emitConsDsc.alignment = std::max(emitConsDsc.alignment, (UNATIVE_OFFSET)TARGET_POINTER_SIZE); + + *dataSecOffs = secOffs; + *dataSec = secDesc; + + // We will need the resume stub. Get it from the EE now so we can display + // it before we emit the actual table later. + if (emitAsyncResumeStub == NO_METHOD_HANDLE) + { + emitAsyncResumeStub = emitCmpHandle->getAsyncResumptionStub(&emitAsyncResumeStubEntryPoint); + } +} + /***************************************************************************** * * Emit the given block of bits into the current data section. @@ -8435,7 +8474,8 @@ void emitter::emitOutputDataSec(dataSecDsc* sec, BYTE* dst) bDstRW[i] = (target_size_t)(size_t)target; if (emitComp->opts.compReloc) { - emitRecordRelocation(&(bDstRW[i]), target, IMAGE_REL_BASED_HIGHLOW); + uint16_t relocType = TARGET_POINTER_SIZE == 8 ? IMAGE_REL_BASED_DIR64 : IMAGE_REL_BASED_HIGHLOW; + emitRecordRelocation(&(bDstRW[i]), target, relocType); } JITDUMP(" " FMT_BB ": 0x%p\n", block->bbNum, bDstRW[i]); @@ -8463,6 +8503,30 @@ void emitter::emitOutputDataSec(dataSecDsc* sec, BYTE* dst) JITDUMP(" " FMT_BB ": 0x%x\n", block->bbNum, uDstRW[i]); } } + else if (dsc->dsType == dataSection::asyncResumeInfo) + { + JITDUMP(" section %u, size %u, async resume info\n", secNum++, dscSize); + + size_t numElems = dscSize / sizeof(emitter::dataAsyncResumeInfo); + + emitter::dataAsyncResumeInfo* aDstRW = (emitter::dataAsyncResumeInfo*)dstRW; + for (size_t i = 0; i < numElems; i++) + { + emitLocation* emitLoc = &((emitLocation*)dsc->dsCont)[i]; + + BYTE* target = emitOffsetToPtr(emitLoc->CodeOffset(this)); + aDstRW[i].Resume = emitAsyncResumeStubEntryPoint; + aDstRW[i].FinalResumeIP = target; + if (emitComp->opts.compReloc) + { + uint16_t relocType = TARGET_POINTER_SIZE == 8 ? IMAGE_REL_BASED_DIR64 : IMAGE_REL_BASED_HIGHLOW; + emitRecordRelocation(&aDstRW[i].Resume, emitAsyncResumeStubEntryPoint, relocType); + emitRecordRelocation(&aDstRW[i].FinalResumeIP, target, relocType); + } + + JITDUMP(" Resume=%p, FinalResumeIP=%p\n", emitAsyncResumeStubEntryPoint, target); + } + } else { // Simple binary data: copy the bytes to the target @@ -8600,6 +8664,40 @@ void emitter::emitDispDataSec(dataSecDsc* section, BYTE* dst) } } } + else if (data->dsType == dataSection::asyncResumeInfo) + { + assert(emitAsyncResumeStub != NO_METHOD_HANDLE); + assert(emitAsyncResumeStubEntryPoint != nullptr); + + char nameBuffer[256]; + const char* resumeStubName = + emitComp->eeGetMethodFullName(emitAsyncResumeStub, true, true, nameBuffer, sizeof(nameBuffer)); + + size_t infoCount = data->dsSize / sizeof(emitLocation); + for (size_t i = 0; i < infoCount; i++) + { + if (i > 0) + { + sprintf_s(label, ArrLen(label), "RWD%02zu", i * sizeof(dataAsyncResumeInfo)); + printf(labelFormat, label); + } + + emitLocation* emitLoc = &((emitLocation*)data->dsCont)[i]; + + printf("\tdq\t%s\n", resumeStubName); + + UNATIVE_OFFSET codeOffset = emitLoc->CodeOffset(this); + if (codeOffset != emitLoc->GetIG()->igOffs) + { + printf("\tdq\t%s + %zu\n", emitLabelString(emitLoc->GetIG()), + static_cast(codeOffset - emitLoc->GetIG()->igOffs)); + } + else + { + printf("\tdq\t%s\n", emitLabelString(emitLoc->GetIG())); + } + } + } else { assert(data->dsType == dataSection::data); diff --git a/src/coreclr/jit/emit.h b/src/coreclr/jit/emit.h index 398671531bfaa7..568f4345126419 100644 --- a/src/coreclr/jit/emit.h +++ b/src/coreclr/jit/emit.h @@ -3304,6 +3304,9 @@ class emitter int emitSyncThisObjOffs; // what is the offset of "this" for synchronized methods? + CORINFO_METHOD_HANDLE emitAsyncResumeStub = NO_METHOD_HANDLE; + void* emitAsyncResumeStubEntryPoint = nullptr; + public: void emitSetFrameRangeGCRs(int offsLo, int offsHi); void emitSetFrameRangeLcls(int offsLo, int offsHi); @@ -3465,6 +3468,15 @@ class emitter /* The following logic keeps track of initialized data sections */ /************************************************************************/ + // Note: Keep synchronized with AsyncHelpers.ResumeInfo + struct dataAsyncResumeInfo + { + // delegate* + void* Resume; + // Pointer in main code where resumption eventually ends up + void* FinalResumeIP; + }; + /* One of these is allocated for every blob of initialized data */ struct dataSection @@ -3479,7 +3491,8 @@ class emitter { data, blockAbsoluteAddr, - blockRelative32 + blockRelative32, + asyncResumeInfo, }; dataSection* dsNext; @@ -3516,6 +3529,7 @@ class emitter void emitOutputDataSec(dataSecDsc* sec, BYTE* dst); void emitDispDataSec(dataSecDsc* section, BYTE* dst); + void emitAsyncResumeTable(unsigned numEntries, UNATIVE_OFFSET* dataOffset, dataSection** dataSection); /************************************************************************/ /* Handles to the current class and method. */ diff --git a/src/coreclr/jit/gentree.cpp b/src/coreclr/jit/gentree.cpp index afad1d29d0422b..4aec12b85d1da1 100644 --- a/src/coreclr/jit/gentree.cpp +++ b/src/coreclr/jit/gentree.cpp @@ -2734,7 +2734,7 @@ bool GenTree::Compare(GenTree* op1, GenTree* op2, bool swapOK) case GT_NOP: case GT_LABEL: - case GT_ASYNC_RESUME_TRAMPOLINE: + case GT_ASYNC_RESUME_INFO: case GT_SWIFT_ERROR: case GT_GCPOLL: return true; @@ -6694,7 +6694,7 @@ bool GenTree::TryGetUse(GenTree* operand, GenTree*** pUse) case GT_LCL_ADDR: case GT_CATCH_ARG: case GT_ASYNC_CONTINUATION: - case GT_ASYNC_RESUME_TRAMPOLINE: + case GT_ASYNC_RESUME_INFO: case GT_LABEL: case GT_FTN_ADDR: case GT_RET_EXPR: @@ -6726,7 +6726,7 @@ bool GenTree::TryGetUse(GenTree* operand, GenTree*** pUse) case GT_PINVOKE_PROLOG: case GT_PINVOKE_EPILOG: case GT_IL_OFFSET: - case GT_RECORD_ASYNC_JOIN: + case GT_RECORD_ASYNC_RESUME: case GT_NOP: case GT_SWIFT_ERROR: case GT_GCPOLL: @@ -9571,7 +9571,6 @@ GenTree* Compiler::gtCloneExpr(GenTree* tree) case GT_CATCH_ARG: case GT_ASYNC_CONTINUATION: - case GT_ASYNC_RESUME_TRAMPOLINE: case GT_NO_OP: case GT_NOP: case GT_LABEL: @@ -9584,6 +9583,8 @@ GenTree* Compiler::gtCloneExpr(GenTree* tree) case GT_END_LFIN: #endif // FEATURE_EH_WINDOWS_X86 case GT_JMP: + case GT_RECORD_ASYNC_RESUME: + case GT_ASYNC_RESUME_INFO: copy = new (this, oper) GenTreeVal(oper, tree->gtType, tree->AsVal()->gtVal1); goto DONE; @@ -10330,7 +10331,7 @@ GenTreeUseEdgeIterator::GenTreeUseEdgeIterator(GenTree* node) case GT_LCL_ADDR: case GT_CATCH_ARG: case GT_ASYNC_CONTINUATION: - case GT_ASYNC_RESUME_TRAMPOLINE: + case GT_ASYNC_RESUME_INFO: case GT_LABEL: case GT_FTN_ADDR: case GT_RET_EXPR: @@ -10362,7 +10363,7 @@ GenTreeUseEdgeIterator::GenTreeUseEdgeIterator(GenTree* node) case GT_PINVOKE_PROLOG: case GT_PINVOKE_EPILOG: case GT_IL_OFFSET: - case GT_RECORD_ASYNC_JOIN: + case GT_RECORD_ASYNC_RESUME: case GT_NOP: case GT_SWIFT_ERROR: case GT_GCPOLL: @@ -12449,8 +12450,8 @@ void Compiler::gtDispLeaf(GenTree* tree, IndentStack* indentStack) tree->AsILOffset()->gtStmtDI.Dump(true); break; - case GT_RECORD_ASYNC_JOIN: - case GT_ASYNC_RESUME_TRAMPOLINE: + case GT_RECORD_ASYNC_RESUME: + case GT_ASYNC_RESUME_INFO: printf(" state=%zu", tree->AsVal()->gtVal1); break; diff --git a/src/coreclr/jit/gtlist.h b/src/coreclr/jit/gtlist.h index fedc51e8591564..4b5074efdbac25 100644 --- a/src/coreclr/jit/gtlist.h +++ b/src/coreclr/jit/gtlist.h @@ -39,7 +39,7 @@ GTNODE(JMP , GenTreeVal ,0,0,GTK_LEAF|GTK_NOVALUE) // Jump GTNODE(FTN_ADDR , GenTreeFptrVal ,0,0,GTK_LEAF) // Address of a function GTNODE(RET_EXPR , GenTreeRetExpr ,0,0,GTK_LEAF|DBK_NOTLIR) // Place holder for the return expression from an inline candidate GTNODE(GCPOLL , GenTree ,0,0,GTK_LEAF|GTK_NOVALUE|DBK_NOTLIR) -GTNODE(ASYNC_RESUME_TRAMPOLINE, GenTreeVal ,0,0,GTK_LEAF) // Address of async resume trampoline +GTNODE(ASYNC_RESUME_INFO, GenTreeVal ,0,0,GTK_LEAF) // Address of async resume info for a state //----------------------------------------------------------------------------- // Constant nodes: @@ -359,7 +359,7 @@ GTNODE(SWAP , GenTreeOp ,0,0,GTK_BINOP|GTK_NOVALUE|DBK_NOTH GTNODE(COPY , GenTreeCopyOrReload,0,0,GTK_UNOP|DBK_NOTHIR) // Copies a variable from its current location to a register that satisfies GTNODE(RELOAD , GenTreeCopyOrReload,0,0,GTK_UNOP|DBK_NOTHIR) // code generation constraints. The operand is the actual lclVar node. GTNODE(IL_OFFSET , GenTreeILOffset ,0,0,GTK_LEAF|GTK_NOVALUE|DBK_NOTHIR) // marks an IL offset for debugging purposes -GTNODE(RECORD_ASYNC_JOIN, GenTreeVal ,0,0,GTK_LEAF|GTK_NOVALUE|DBK_NOTHIR) // records native offset of async suspension point for async stackwalking purposes +GTNODE(RECORD_ASYNC_RESUME, GenTreeVal ,0,0,GTK_LEAF|GTK_NOVALUE|DBK_NOTHIR) // record native offset for an async resume point /*****************************************************************************/ #undef GTNODE diff --git a/src/coreclr/jit/gtstructs.h b/src/coreclr/jit/gtstructs.h index c23a79720bd5c9..b9cf9839f31f8d 100644 --- a/src/coreclr/jit/gtstructs.h +++ b/src/coreclr/jit/gtstructs.h @@ -51,9 +51,9 @@ GTSTRUCT_0(UnOp , GT_OP) GTSTRUCT_0(Op , GT_OP) #if defined(FEATURE_EH_WINDOWS_X86) -GTSTRUCT_N(Val , GT_END_LFIN, GT_JMP, GT_RECORD_ASYNC_JOIN, GT_ASYNC_RESUME_TRAMPOLINE) +GTSTRUCT_N(Val , GT_END_LFIN, GT_JMP, GT_RECORD_ASYNC_RESUME, GT_ASYNC_RESUME_INFO) #else -GTSTRUCT_N(Val , GT_JMP, GT_RECORD_ASYNC_JOIN, GT_ASYNC_RESUME_TRAMPOLINE) +GTSTRUCT_N(Val , GT_JMP, GT_RECORD_ASYNC_RESUME, GT_ASYNC_RESUME_INFO) #endif GTSTRUCT_2_SPECIAL(IntConCommon, GT_CNS_INT, GT_CNS_LNG) GTSTRUCT_1(IntCon , GT_CNS_INT) diff --git a/src/coreclr/jit/importer.cpp b/src/coreclr/jit/importer.cpp index 3bd72cb08e609a..dee14b3290ce6e 100644 --- a/src/coreclr/jit/importer.cpp +++ b/src/coreclr/jit/importer.cpp @@ -2100,11 +2100,10 @@ BasicBlock* Compiler::impPushCatchArgOnStack(BasicBlock* hndBlk, CORINFO_CLASS_H { // Report the debug info. impImportBlockCode won't treat the actual handler as exception block and thus // won't do it for us. - // TODO-DEBUGINFO: Previous code always set stack as non-empty - // here. Can we not just use impCurStmtOffsSet? Are we out of sync - // here with the stack? - impCurStmtDI = DebugInfo(compInlineContext, ILLocation(newBlk->bbCodeOffs, false, false)); - argStmt = gtNewStmt(argStore, impCurStmtDI); + // TODO-Bug: Should be reported with ICorDebugInfo::CALL_SITE? + impCurStmtDI = + DebugInfo(compInlineContext, ILLocation(newBlk->bbCodeOffs, ICorDebugInfo::SOURCE_TYPE_INVALID)); + argStmt = gtNewStmt(argStore, impCurStmtDI); } else { @@ -2175,8 +2174,13 @@ DebugInfo Compiler::impCreateDIWithCurrentStackInfo(IL_OFFSET offs, bool isCall) { assert(offs != BAD_IL_OFFSET); - bool isStackEmpty = stackState.esStackDepth <= 0; - return DebugInfo(compInlineContext, ILLocation(offs, isStackEmpty, isCall)); + unsigned sourceTypes = 0; + if (isCall) + sourceTypes |= ICorDebugInfo::CALL_INSTRUCTION; + if (stackState.esStackDepth <= 0) + sourceTypes |= ICorDebugInfo::STACK_EMPTY; + + return DebugInfo(compInlineContext, ILLocation(offs, (ICorDebugInfo::SourceTypes)sourceTypes)); } //------------------------------------------------------------------------ diff --git a/src/coreclr/jit/liveness.cpp b/src/coreclr/jit/liveness.cpp index 0c4994faf393c2..6d91e69178a57f 100644 --- a/src/coreclr/jit/liveness.cpp +++ b/src/coreclr/jit/liveness.cpp @@ -1501,7 +1501,7 @@ void Compiler::fgComputeLifeLIR(VARSET_TP& life, BasicBlock* block, VARSET_VALAR case GT_RETURNTRAP: case GT_PUTARG_STK: case GT_IL_OFFSET: - case GT_RECORD_ASYNC_JOIN: + case GT_RECORD_ASYNC_RESUME: case GT_KEEPALIVE: case GT_SWIFT_ERROR_RET: case GT_GCPOLL: diff --git a/src/coreclr/jit/lsraarm.cpp b/src/coreclr/jit/lsraarm.cpp index f3840ef897d5f0..1699ed07f2d397 100644 --- a/src/coreclr/jit/lsraarm.cpp +++ b/src/coreclr/jit/lsraarm.cpp @@ -689,8 +689,8 @@ int LinearScan::BuildNode(GenTree* tree) case GT_LCL_ADDR: case GT_PHYSREG: case GT_IL_OFFSET: - case GT_RECORD_ASYNC_JOIN: - case GT_ASYNC_RESUME_TRAMPOLINE: + case GT_RECORD_ASYNC_RESUME: + case GT_ASYNC_RESUME_INFO: case GT_LABEL: case GT_PINVOKE_PROLOG: case GT_JCC: diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs index 21cf65fcb6a2ae..b1877a7b77ad8a 100644 --- a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs +++ b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs @@ -3724,7 +3724,7 @@ private bool getTailCallHelpers(ref CORINFO_RESOLVED_TOKEN callToken, CORINFO_SI } #pragma warning disable CA1822 // Mark members as static - private CORINFO_METHOD_STRUCT_* getAsyncResumptionStub() + private CORINFO_METHOD_STRUCT_* getAsyncResumptionStub(ref void* entryPoint) #pragma warning restore CA1822 // Mark members as static { throw new NotImplementedException("Crossgen2 does not support runtime-async yet"); diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoImpl_generated.cs b/src/coreclr/tools/Common/JitInterface/CorInfoImpl_generated.cs index 6911ece7bd1042..73a82ead1edd1d 100644 --- a/src/coreclr/tools/Common/JitInterface/CorInfoImpl_generated.cs +++ b/src/coreclr/tools/Common/JitInterface/CorInfoImpl_generated.cs @@ -2327,12 +2327,12 @@ private static byte _getTailCallHelpers(IntPtr thisHandle, IntPtr* ppException, } [UnmanagedCallersOnly] - private static CORINFO_METHOD_STRUCT_* _getAsyncResumptionStub(IntPtr thisHandle, IntPtr* ppException) + private static CORINFO_METHOD_STRUCT_* _getAsyncResumptionStub(IntPtr thisHandle, IntPtr* ppException, void** entryPoint) { var _this = GetThis(thisHandle); try { - return _this.getAsyncResumptionStub(); + return _this.getAsyncResumptionStub(ref *entryPoint); } catch (Exception ex) { @@ -2794,7 +2794,7 @@ private static IntPtr GetUnmanagedCallbacks() callbacks[154] = (delegate* unmanaged)&_MethodCompileComplete; callbacks[155] = (delegate* unmanaged)&_getTailCallHelpers; callbacks[156] = (delegate* unmanaged)&_getContinuationType; - callbacks[157] = (delegate* unmanaged)&_getAsyncResumptionStub; + callbacks[157] = (delegate* unmanaged)&_getAsyncResumptionStub; callbacks[158] = (delegate* unmanaged)&_convertPInvokeCalliToCall; callbacks[159] = (delegate* unmanaged)&_notifyInstructionSetUsage; callbacks[160] = (delegate* unmanaged)&_updateEntryPointForTailCall; diff --git a/src/coreclr/tools/Common/JitInterface/ThunkGenerator/ThunkInput.txt b/src/coreclr/tools/Common/JitInterface/ThunkGenerator/ThunkInput.txt index d9b917e9b591a4..fff938a53009d5 100644 --- a/src/coreclr/tools/Common/JitInterface/ThunkGenerator/ThunkInput.txt +++ b/src/coreclr/tools/Common/JitInterface/ThunkGenerator/ThunkInput.txt @@ -326,7 +326,7 @@ FUNCTIONS 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_CLASS_HANDLE getContinuationType(size_t dataSize, bool* objRefs, size_t objRefsSize); - CORINFO_METHOD_HANDLE getAsyncResumptionStub(); + CORINFO_METHOD_HANDLE getAsyncResumptionStub(void **entryPoint); bool convertPInvokeCalliToCall(CORINFO_RESOLVED_TOKEN * pResolvedToken, bool mustConvert); bool notifyInstructionSetUsage(CORINFO_InstructionSet instructionSet,bool supportEnabled); void updateEntryPointForTailCall(REF_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 5f812007938416..ac78ed4c2a1144 100644 --- a/src/coreclr/tools/aot/jitinterface/jitinterface_generated.h +++ b/src/coreclr/tools/aot/jitinterface/jitinterface_generated.h @@ -168,7 +168,7 @@ struct JitInterfaceCallbacks 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_CLASS_HANDLE (* getContinuationType)(void * thisHandle, CorInfoExceptionClass** ppException, size_t dataSize, bool* objRefs, size_t objRefsSize); - CORINFO_METHOD_HANDLE (* getAsyncResumptionStub)(void * thisHandle, CorInfoExceptionClass** ppException); + CORINFO_METHOD_HANDLE (* getAsyncResumptionStub)(void * thisHandle, CorInfoExceptionClass** ppException, void** entryPoint); 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); @@ -1738,10 +1738,11 @@ class JitInterfaceWrapper : public ICorJitInfo return temp; } - virtual CORINFO_METHOD_HANDLE getAsyncResumptionStub() + virtual CORINFO_METHOD_HANDLE getAsyncResumptionStub( + void** entryPoint) { CorInfoExceptionClass* pException = nullptr; - CORINFO_METHOD_HANDLE temp = _callbacks->getAsyncResumptionStub(_thisHandle, &pException); + CORINFO_METHOD_HANDLE temp = _callbacks->getAsyncResumptionStub(_thisHandle, &pException, entryPoint); if (pException != nullptr) throw pException; return temp; } diff --git a/src/coreclr/tools/superpmi/superpmi-shared/agnostic.h b/src/coreclr/tools/superpmi/superpmi-shared/agnostic.h index d45676ab5a8784..a4250fdadce9c0 100644 --- a/src/coreclr/tools/superpmi/superpmi-shared/agnostic.h +++ b/src/coreclr/tools/superpmi/superpmi-shared/agnostic.h @@ -197,7 +197,7 @@ struct Agnostic_CORINFO_ASYNC_INFO { DWORDLONG continuationClsHnd; DWORDLONG continuationNextFldHnd; - DWORDLONG continuationResumeFldHnd; + DWORDLONG continuationResumeInfoFldHnd; DWORDLONG continuationStateFldHnd; DWORDLONG continuationFlagsFldHnd; DWORDLONG captureExecutionContextMethHnd; diff --git a/src/coreclr/tools/superpmi/superpmi-shared/lwmlist.h b/src/coreclr/tools/superpmi/superpmi-shared/lwmlist.h index a52578a288bc15..12272fc9e0d63b 100644 --- a/src/coreclr/tools/superpmi/superpmi-shared/lwmlist.h +++ b/src/coreclr/tools/superpmi/superpmi-shared/lwmlist.h @@ -129,7 +129,7 @@ LWM(GetSystemVAmd64PassStructInRegisterDescriptor, DWORDLONG, Agnostic_GetSystem LWM(GetSwiftLowering, DWORDLONG, Agnostic_GetSwiftLowering) LWM(GetFpStructLowering, DWORDLONG, Agnostic_GetFpStructLowering) LWM(GetTailCallHelpers, Agnostic_GetTailCallHelpers, Agnostic_CORINFO_TAILCALL_HELPERS) -LWM(GetAsyncResumptionStub, DWORD, DWORDLONG) +LWM(GetAsyncResumptionStub, DWORD, DLDL) LWM(GetContinuationType, Agnostic_GetContinuationTypeIn, DWORDLONG) LWM(UpdateEntryPointForTailCall, Agnostic_CORINFO_CONST_LOOKUP, Agnostic_CORINFO_CONST_LOOKUP) LWM(GetSpecialCopyHelper, DWORDLONG, DWORDLONG) diff --git a/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.cpp b/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.cpp index 7dad7bac1fc57b..f436ad7e13f83b 100644 --- a/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.cpp +++ b/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.cpp @@ -4454,7 +4454,7 @@ void MethodContext::recGetAsyncInfo(const CORINFO_ASYNC_INFO* pAsyncInfo) value.continuationClsHnd = CastHandle(pAsyncInfo->continuationClsHnd); value.continuationNextFldHnd = CastHandle(pAsyncInfo->continuationNextFldHnd); - value.continuationResumeFldHnd = CastHandle(pAsyncInfo->continuationResumeFldHnd); + value.continuationResumeInfoFldHnd = CastHandle(pAsyncInfo->continuationResumeInfoFldHnd); value.continuationStateFldHnd = CastHandle(pAsyncInfo->continuationStateFldHnd); value.continuationFlagsFldHnd = CastHandle(pAsyncInfo->continuationFlagsFldHnd); value.captureExecutionContextMethHnd = CastHandle(pAsyncInfo->captureExecutionContextMethHnd); @@ -4468,9 +4468,9 @@ void MethodContext::recGetAsyncInfo(const CORINFO_ASYNC_INFO* pAsyncInfo) } void MethodContext::dmpGetAsyncInfo(DWORD key, const Agnostic_CORINFO_ASYNC_INFO& value) { - printf("GetAsyncInfo key %u value contClsHnd-%016" PRIX64 " contNextFldHnd-%016" PRIX64 " contResumeFldHnd-%016" PRIX64 + printf("GetAsyncInfo key %u value contClsHnd-%016" PRIX64 " contNextFldHnd-%016" PRIX64 " contResumeInfoFldHnd-%016" PRIX64 " contStateFldHnd-%016" PRIX64 " contFlagsFldHnd-%016" PRIX64, - key, value.continuationClsHnd, value.continuationNextFldHnd, value.continuationResumeFldHnd, + key, value.continuationClsHnd, value.continuationNextFldHnd, value.continuationResumeInfoFldHnd, value.continuationStateFldHnd, value.continuationFlagsFldHnd); } void MethodContext::repGetAsyncInfo(CORINFO_ASYNC_INFO* pAsyncInfoOut) @@ -4478,7 +4478,7 @@ void MethodContext::repGetAsyncInfo(CORINFO_ASYNC_INFO* pAsyncInfoOut) Agnostic_CORINFO_ASYNC_INFO value = LookupByKeyOrMissNoMessage(GetAsyncInfo, 0); pAsyncInfoOut->continuationClsHnd = (CORINFO_CLASS_HANDLE)value.continuationClsHnd; pAsyncInfoOut->continuationNextFldHnd = (CORINFO_FIELD_HANDLE)value.continuationNextFldHnd; - pAsyncInfoOut->continuationResumeFldHnd = (CORINFO_FIELD_HANDLE)value.continuationResumeFldHnd; + pAsyncInfoOut->continuationResumeInfoFldHnd = (CORINFO_FIELD_HANDLE)value.continuationResumeInfoFldHnd; pAsyncInfoOut->continuationStateFldHnd = (CORINFO_FIELD_HANDLE)value.continuationStateFldHnd; pAsyncInfoOut->continuationFlagsFldHnd = (CORINFO_FIELD_HANDLE)value.continuationFlagsFldHnd; pAsyncInfoOut->captureExecutionContextMethHnd = (CORINFO_METHOD_HANDLE)value.captureExecutionContextMethHnd; @@ -6921,22 +6921,26 @@ bool MethodContext::repGetTailCallHelpers( } -void MethodContext::recGetAsyncResumptionStub(CORINFO_METHOD_HANDLE hnd) +void MethodContext::recGetAsyncResumptionStub(CORINFO_METHOD_HANDLE hnd, void* entryPoint) { if (GetAsyncResumptionStub == nullptr) - GetAsyncResumptionStub = new LightWeightMap(); + GetAsyncResumptionStub = new LightWeightMap(); - GetAsyncResumptionStub->Add(0, CastHandle(hnd)); - DEBUG_REC(dmpGetAsyncResumptionStub(CastHandle(hnd))); + DLDL result; + result.A = CastHandle(hnd); + result.B = CastPointer(entryPoint); + GetAsyncResumptionStub->Add(0, result); + DEBUG_REC(dmpGetAsyncResumptionStub(CastHandle(hnd), CastPointer(entryPoint))); } -void MethodContext::dmpGetAsyncResumptionStub(DWORD key, DWORDLONG hnd) +void MethodContext::dmpGetAsyncResumptionStub(DWORD key, const DLDL& value) { - printf("GetAsyncResumptionStub key-%u, value-%016" PRIX64, key, hnd); + printf("GetAsyncResumptionStub key-%u, hnd-%016" PRIX64 ", entrypoint-%016" PRIX64, key, value.A, value.B); } -CORINFO_METHOD_HANDLE MethodContext::repGetAsyncResumptionStub() +CORINFO_METHOD_HANDLE MethodContext::repGetAsyncResumptionStub(void** entryPoint) { - DWORDLONG hnd = LookupByKeyOrMissNoMessage(GetAsyncResumptionStub, 0); - return (CORINFO_METHOD_HANDLE)hnd; + DLDL value = LookupByKeyOrMissNoMessage(GetAsyncResumptionStub, 0); + *entryPoint = (void*)value.B; + return (CORINFO_METHOD_HANDLE)value.A; } void MethodContext::recGetContinuationType(size_t dataSize, diff --git a/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.h b/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.h index c5d7f82923841e..bf853790fb9d8d 100644 --- a/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.h +++ b/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.h @@ -863,9 +863,9 @@ 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 recGetAsyncResumptionStub(CORINFO_METHOD_HANDLE hnd, void* entryPoint); + void dmpGetAsyncResumptionStub(DWORD key, const DLDL& value); + CORINFO_METHOD_HANDLE repGetAsyncResumptionStub(void** entryPoint); void recGetContinuationType(size_t dataSize, bool* objRefs, size_t objRefsSize, CORINFO_CLASS_HANDLE result); void dmpGetContinuationType(const Agnostic_GetContinuationTypeIn& key, DWORDLONG value); diff --git a/src/coreclr/tools/superpmi/superpmi-shim-collector/icorjitinfo.cpp b/src/coreclr/tools/superpmi/superpmi-shim-collector/icorjitinfo.cpp index f4649729143254..09544216cb913f 100644 --- a/src/coreclr/tools/superpmi/superpmi-shim-collector/icorjitinfo.cpp +++ b/src/coreclr/tools/superpmi/superpmi-shim-collector/icorjitinfo.cpp @@ -1784,11 +1784,11 @@ bool interceptor_ICJI::getTailCallHelpers( return result; } -CORINFO_METHOD_HANDLE interceptor_ICJI::getAsyncResumptionStub() +CORINFO_METHOD_HANDLE interceptor_ICJI::getAsyncResumptionStub(void** entryPoint) { mc->cr->AddCall("getAsyncResumptionStub"); - CORINFO_METHOD_HANDLE stub = original_ICorJitInfo->getAsyncResumptionStub(); - mc->recGetAsyncResumptionStub(stub); + CORINFO_METHOD_HANDLE stub = original_ICorJitInfo->getAsyncResumptionStub(entryPoint); + mc->recGetAsyncResumptionStub(stub, *entryPoint); return stub; } 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 ab9f65eedccdc0..6e39de50c5d89e 100644 --- a/src/coreclr/tools/superpmi/superpmi-shim-counter/icorjitinfo_generated.cpp +++ b/src/coreclr/tools/superpmi/superpmi-shim-counter/icorjitinfo_generated.cpp @@ -1285,10 +1285,11 @@ CORINFO_CLASS_HANDLE interceptor_ICJI::getContinuationType( return original_ICorJitInfo->getContinuationType(dataSize, objRefs, objRefsSize); } -CORINFO_METHOD_HANDLE interceptor_ICJI::getAsyncResumptionStub() +CORINFO_METHOD_HANDLE interceptor_ICJI::getAsyncResumptionStub( + void** entryPoint) { mcs->AddCall("getAsyncResumptionStub"); - return original_ICorJitInfo->getAsyncResumptionStub(); + return original_ICorJitInfo->getAsyncResumptionStub(entryPoint); } bool interceptor_ICJI::convertPInvokeCalliToCall( 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 1d94a4133b3e23..fcb77f747663d9 100644 --- a/src/coreclr/tools/superpmi/superpmi-shim-simple/icorjitinfo_generated.cpp +++ b/src/coreclr/tools/superpmi/superpmi-shim-simple/icorjitinfo_generated.cpp @@ -1128,9 +1128,10 @@ CORINFO_CLASS_HANDLE interceptor_ICJI::getContinuationType( return original_ICorJitInfo->getContinuationType(dataSize, objRefs, objRefsSize); } -CORINFO_METHOD_HANDLE interceptor_ICJI::getAsyncResumptionStub() +CORINFO_METHOD_HANDLE interceptor_ICJI::getAsyncResumptionStub( + void** entryPoint) { - return original_ICorJitInfo->getAsyncResumptionStub(); + return original_ICorJitInfo->getAsyncResumptionStub(entryPoint); } bool interceptor_ICJI::convertPInvokeCalliToCall( diff --git a/src/coreclr/tools/superpmi/superpmi/icorjitinfo.cpp b/src/coreclr/tools/superpmi/superpmi/icorjitinfo.cpp index c6b59d36841a58..38aaca767c9d36 100644 --- a/src/coreclr/tools/superpmi/superpmi/icorjitinfo.cpp +++ b/src/coreclr/tools/superpmi/superpmi/icorjitinfo.cpp @@ -1514,10 +1514,10 @@ bool MyICJI::getTailCallHelpers( return jitInstance->mc->repGetTailCallHelpers(callToken, sig, flags, pResult); } -CORINFO_METHOD_HANDLE MyICJI::getAsyncResumptionStub() +CORINFO_METHOD_HANDLE MyICJI::getAsyncResumptionStub(void** entryPoint) { jitInstance->mc->cr->AddCall("getAsyncResumptionStub"); - return jitInstance->mc->repGetAsyncResumptionStub();; + return jitInstance->mc->repGetAsyncResumptionStub(entryPoint); } CORINFO_CLASS_HANDLE MyICJI::getContinuationType(size_t dataSize, bool* objRefs, size_t objRefsSize) diff --git a/src/coreclr/vm/asynccontinuations.h b/src/coreclr/vm/asynccontinuations.h index ad577fb4fa00b3..7c4e5ccd9c03ae 100644 --- a/src/coreclr/vm/asynccontinuations.h +++ b/src/coreclr/vm/asynccontinuations.h @@ -68,9 +68,9 @@ void AsyncContinuationsManager::PrintContinuationName(MethodTable* pMT, AppendSt { append("Continuation_", W("Continuation_")); appendNum(pMT->GetBaseSize() - (OBJHEADER_SIZE + OFFSETOF__CORINFO_Continuation__data)); - CGCDesc* desc = CGCDesc::GetCGCDescFromMT(pMT); - CGCDescSeries* lowestSeries = desc->GetLowestSeries(); - for (CGCDescSeries* curSeries = desc->GetHighestSeries(); curSeries >= lowestSeries; curSeries--) + PTR_CGCDesc desc = CGCDesc::GetCGCDescFromMT(pMT); + PTR_CGCDescSeries lowestSeries = desc->GetLowestSeries(); + for (PTR_CGCDescSeries curSeries = desc->GetHighestSeries(); curSeries >= lowestSeries; curSeries--) { if (curSeries->GetSeriesOffset() < OFFSETOF__CORINFO_Continuation__data) { diff --git a/src/coreclr/vm/corelib.h b/src/coreclr/vm/corelib.h index 7227608a0dba17..544a5f90732086 100644 --- a/src/coreclr/vm/corelib.h +++ b/src/coreclr/vm/corelib.h @@ -856,7 +856,7 @@ 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, RESUME_INFO, ResumeInfo) DEFINE_FIELD(CONTINUATION, STATE, State) DEFINE_FIELD(CONTINUATION, FLAGS, Flags) diff --git a/src/coreclr/vm/debuginfostore.cpp b/src/coreclr/vm/debuginfostore.cpp index 3f6500160150b1..06e9712f4fef41 100644 --- a/src/coreclr/vm/debuginfostore.cpp +++ b/src/coreclr/vm/debuginfostore.cpp @@ -471,13 +471,6 @@ static void DoAsyncSuspensionPoints( for (uint32_t i = 0; i < cSuspensionPoints; i++) { ICorDebugInfo::AsyncSuspensionPoint* sp = &suspensionPoints[i]; - - trans.DoEncodedDeltaU32(sp->NativeResumeOffset, lastNativeResumeOffset); - lastNativeResumeOffset = sp->NativeResumeOffset; - - trans.DoEncodedDeltaU32NonMonotonic(sp->NativeJoinOffset, lastNativeJoinOffset); - lastNativeJoinOffset = sp->NativeJoinOffset; - trans.DoEncodedU32(sp->NumContinuationVars); } } diff --git a/src/coreclr/vm/jitinterface.cpp b/src/coreclr/vm/jitinterface.cpp index 9be5481c7d4c6a..c82aa35fe8894a 100644 --- a/src/coreclr/vm/jitinterface.cpp +++ b/src/coreclr/vm/jitinterface.cpp @@ -10261,7 +10261,7 @@ void CEEInfo::getAsyncInfo(CORINFO_ASYNC_INFO* pAsyncInfoOut) pAsyncInfoOut->continuationClsHnd = CORINFO_CLASS_HANDLE(CoreLibBinder::GetClass(CLASS__CONTINUATION)); pAsyncInfoOut->continuationNextFldHnd = CORINFO_FIELD_HANDLE(CoreLibBinder::GetField(FIELD__CONTINUATION__NEXT)); - pAsyncInfoOut->continuationResumeFldHnd = CORINFO_FIELD_HANDLE(CoreLibBinder::GetField(FIELD__CONTINUATION__RESUME)); + pAsyncInfoOut->continuationResumeInfoFldHnd = CORINFO_FIELD_HANDLE(CoreLibBinder::GetField(FIELD__CONTINUATION__RESUME_INFO)); pAsyncInfoOut->continuationStateFldHnd = CORINFO_FIELD_HANDLE(CoreLibBinder::GetField(FIELD__CONTINUATION__STATE)); pAsyncInfoOut->continuationFlagsFldHnd = CORINFO_FIELD_HANDLE(CoreLibBinder::GetField(FIELD__CONTINUATION__FLAGS)); pAsyncInfoOut->captureExecutionContextMethHnd = CORINFO_METHOD_HANDLE(CoreLibBinder::GetMethod(METHOD__ASYNC_HELPERS__CAPTURE_EXECUTION_CONTEXT)); @@ -11825,10 +11825,17 @@ void CEEJitInfo::recordRelocation(void * location, switch (fRelocType) { +#ifdef TARGET_64BIT case IMAGE_REL_BASED_DIR64: // Write 64-bits into location *((UINT64 *) locationRW) = (UINT64) target; break; +#else + case IMAGE_REL_BASED_HIGHLOW: + // Write 32-bits into location + *((UINT32 *) locationRW) = (UINT32) target; + break; +#endif #ifdef TARGET_AMD64 case IMAGE_REL_BASED_REL32: @@ -14640,7 +14647,7 @@ static Signature BuildResumptionStubCalliSignature(MetaSig& msig, MethodTable* m return AllocateSignature(alloc, sigBuilder, pamTracker); } -CORINFO_METHOD_HANDLE CEEJitInfo::getAsyncResumptionStub() +CORINFO_METHOD_HANDLE CEEJitInfo::getAsyncResumptionStub(void** entryPoint) { CONTRACTL{ THROWS; @@ -14825,6 +14832,7 @@ CORINFO_METHOD_HANDLE CEEJitInfo::getAsyncResumptionStub() sl.LogILStub(CORJIT_FLAGS()); #endif + *entryPoint = (void*)result->GetMultiCallableAddrOfCode(); return CORINFO_METHOD_HANDLE(result); } @@ -15024,7 +15032,7 @@ PatchpointInfo* CEEInfo::getOSRInfo(unsigned* ilOffset) UNREACHABLE(); // only called on derived class. } -CORINFO_METHOD_HANDLE CEEInfo::getAsyncResumptionStub() +CORINFO_METHOD_HANDLE CEEInfo::getAsyncResumptionStub(void** entryPoint) { LIMITED_METHOD_CONTRACT; UNREACHABLE(); // only called on derived class. diff --git a/src/coreclr/vm/jitinterface.h b/src/coreclr/vm/jitinterface.h index 1ba4ad3b33a879..a158c2307a877f 100644 --- a/src/coreclr/vm/jitinterface.h +++ b/src/coreclr/vm/jitinterface.h @@ -905,7 +905,7 @@ class CEEJitInfo final : public CEECodeGenInfo void setPatchpointInfo(PatchpointInfo* patchpointInfo) override; PatchpointInfo* getOSRInfo(unsigned* ilOffset) override; - virtual CORINFO_METHOD_HANDLE getAsyncResumptionStub() override final; + virtual CORINFO_METHOD_HANDLE getAsyncResumptionStub(void** entryPoint) override final; protected : From eadab401ede45266215e7da0fcd7aaeaf6c38212 Mon Sep 17 00:00:00 2001 From: Jakob Botsch Nielsen Date: Thu, 23 Oct 2025 11:47:43 +0200 Subject: [PATCH 44/72] Remove unnecessary intermediate type --- src/coreclr/jit/async.cpp | 6 +++--- src/coreclr/jit/codegencommon.cpp | 7 ++----- src/coreclr/jit/compiler.h | 7 +------ 3 files changed, 6 insertions(+), 14 deletions(-) diff --git a/src/coreclr/jit/async.cpp b/src/coreclr/jit/async.cpp index e2d5439149ea56..ef5b7ae5e18c6b 100644 --- a/src/coreclr/jit/async.cpp +++ b/src/coreclr/jit/async.cpp @@ -643,7 +643,7 @@ PhaseStatus AsyncTransformation::Run() } m_comp->compSuspensionPoints = - new (m_comp, CMK_Async) jitstd::vector(m_comp->getAllocator(CMK_Async)); + new (m_comp, CMK_Async) jitstd::vector(m_comp->getAllocator(CMK_Async)); m_comp->compAsyncVars = new (m_comp, CMK_Async) jitstd::vector(m_comp->getAllocator(CMK_Async)); @@ -2112,8 +2112,8 @@ void AsyncTransformation::CreateDebugInfoForSuspensionPoint(GenTreeCall* numLocals++; } - AsyncSuspensionPoint suspensionPoint; - suspensionPoint.numContinuationVars = numLocals; + ICorDebugInfo::AsyncSuspensionPoint suspensionPoint; + suspensionPoint.NumContinuationVars = numLocals; m_comp->compSuspensionPoints->push_back(suspensionPoint); GenTree* recordOffset = new (m_comp, GT_RECORD_ASYNC_RESUME) diff --git a/src/coreclr/jit/codegencommon.cpp b/src/coreclr/jit/codegencommon.cpp index c96416ffb730f8..b6d9edcb6f1f90 100644 --- a/src/coreclr/jit/codegencommon.cpp +++ b/src/coreclr/jit/codegencommon.cpp @@ -6729,7 +6729,7 @@ void CodeGen::genAddRichIPMappingHere(const DebugInfo& di) // void CodeGen::genReportAsyncDebugInfo() { - jitstd::vector* suspPoints = compiler->compSuspensionPoints; + jitstd::vector* suspPoints = compiler->compSuspensionPoints; if (suspPoints == nullptr) { return; @@ -6741,10 +6741,7 @@ void CodeGen::genReportAsyncDebugInfo() ICorDebugInfo::AsyncSuspensionPoint* hostSuspensionPoints = static_cast( compiler->info.compCompHnd->allocateArray(suspPoints->size() * sizeof(ICorDebugInfo::AsyncSuspensionPoint))); for (size_t i = 0; i < suspPoints->size(); i++) - { - AsyncSuspensionPoint& suspPoint = (*suspPoints)[i]; - hostSuspensionPoints[i].NumContinuationVars = suspPoint.numContinuationVars; - } + hostSuspensionPoints[i] = (*suspPoints)[i]; jitstd::vector* asyncVars = compiler->compAsyncVars; ICorDebugInfo::AsyncContinuationVarInfo* hostVars = static_cast( diff --git a/src/coreclr/jit/compiler.h b/src/coreclr/jit/compiler.h index 409b040f8bf108..bbf887a6a08556 100644 --- a/src/coreclr/jit/compiler.h +++ b/src/coreclr/jit/compiler.h @@ -2397,11 +2397,6 @@ struct RichIPMapping DebugInfo debugInfo; }; -struct AsyncSuspensionPoint -{ - unsigned numContinuationVars = 0; -}; - // Current kind of node threading stored in GenTree::gtPrev and GenTree::gtNext. // See fgNodeThreading for more information. enum class NodeThreading @@ -8636,7 +8631,7 @@ class Compiler jitstd::list genIPmappings; jitstd::list genRichIPmappings; - jitstd::vector* compSuspensionPoints = nullptr; + jitstd::vector* compSuspensionPoints = nullptr; jitstd::vector* compAsyncVars = nullptr; // Managed RetVal - A side hash table meant to record the mapping from a From d57fc4dbf452862e29e0e3848cbc7144c3d9dffe Mon Sep 17 00:00:00 2001 From: Jakob Botsch Nielsen Date: Thu, 23 Oct 2025 11:48:11 +0200 Subject: [PATCH 45/72] Go back to not reporting async debug info unless requested --- src/coreclr/jit/codegencommon.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/coreclr/jit/codegencommon.cpp b/src/coreclr/jit/codegencommon.cpp index b6d9edcb6f1f90..a98acbdb2e2085 100644 --- a/src/coreclr/jit/codegencommon.cpp +++ b/src/coreclr/jit/codegencommon.cpp @@ -6729,6 +6729,11 @@ void CodeGen::genAddRichIPMappingHere(const DebugInfo& di) // void CodeGen::genReportAsyncDebugInfo() { + if (!compiler->opts.compDbgInfo) + { + return; + } + jitstd::vector* suspPoints = compiler->compSuspensionPoints; if (suspPoints == nullptr) { From 1308e96f2bacb9390a1e583829d9e704d7db76e8 Mon Sep 17 00:00:00 2001 From: Jakob Botsch Nielsen Date: Thu, 23 Oct 2025 12:52:55 +0200 Subject: [PATCH 46/72] Insert ASYNC mappings in resume and suspension code --- src/coreclr/jit/async.cpp | 10 +++ src/coreclr/jit/codegencommon.cpp | 10 +-- src/coreclr/jit/compiler.h | 4 +- src/coreclr/jit/debuginfo.cpp | 6 +- src/coreclr/jit/debuginfo.h | 16 ++--- src/coreclr/jit/ee_il_dll.cpp | 9 ++- src/coreclr/jit/gentree.cpp | 5 ++ src/coreclr/jit/gentree.h | 3 + src/coreclr/jit/importercalls.cpp | 15 ++++- src/coreclr/jit/rationalize.cpp | 3 +- .../tools/Common/JitInterface/CorInfoTypes.cs | 3 +- .../ReadyToRun/DebugInfoTableNode.cs | 35 +++++----- src/coreclr/vm/debuginfostore.cpp | 66 ++++++++----------- 13 files changed, 101 insertions(+), 84 deletions(-) diff --git a/src/coreclr/jit/async.cpp b/src/coreclr/jit/async.cpp index ef5b7ae5e18c6b..6837d34ffcbd9e 100644 --- a/src/coreclr/jit/async.cpp +++ b/src/coreclr/jit/async.cpp @@ -1411,6 +1411,11 @@ BasicBlock* AsyncTransformation::CreateSuspension( JITDUMP(" Creating suspension " FMT_BB " for state %u\n", suspendBB->bbNum, stateNum); + GenTreeILOffset* ilOffsetNode = + m_comp->gtNewILOffsetNode(call->GetAsyncInfo().CallAsyncDebugInfo DEBUGARG(BAD_IL_OFFSET)); + + LIR::AsRange(suspendBB).InsertAtEnd(LIR::SeqTree(m_comp, ilOffsetNode)); + // Allocate continuation GenTree* returnedContinuation = m_comp->gtNewLclvNode(m_returnedContinuationVar, TYP_REF); @@ -1748,6 +1753,11 @@ BasicBlock* AsyncTransformation::CreateResumption(BasicBlock* bloc JITDUMP(" Creating resumption " FMT_BB " for state %u\n", resumeBB->bbNum, stateNum); + GenTreeILOffset* ilOffsetNode = + m_comp->gtNewILOffsetNode(call->GetAsyncInfo().CallAsyncDebugInfo DEBUGARG(BAD_IL_OFFSET)); + + LIR::AsRange(resumeBB).InsertAtEnd(LIR::SeqTree(m_comp, ilOffsetNode)); + SetSuspendedIndicator(resumeBB, block, call); if (layout.Size > 0) diff --git a/src/coreclr/jit/codegencommon.cpp b/src/coreclr/jit/codegencommon.cpp index a98acbdb2e2085..0461ce6f68c3f8 100644 --- a/src/coreclr/jit/codegencommon.cpp +++ b/src/coreclr/jit/codegencommon.cpp @@ -6164,17 +6164,17 @@ void CodeGen::genIPmappingDisp(unsigned mappingNum, const IPmappingDsc* ipMappin case IPmappingDscKind::Normal: const ILLocation& loc = ipMapping->ipmdLoc; Compiler::eeDispILOffs(loc.GetOffset()); - if ((loc.GetSourceType() & ICorDebugInfo::STACK_EMPTY) != 0) + if ((loc.GetSourceTypes() & ICorDebugInfo::STACK_EMPTY) != 0) { printf(" STACK_EMPTY"); } - if ((loc.GetSourceType() & ICorDebugInfo::CALL_INSTRUCTION) != 0) + if ((loc.GetSourceTypes() & ICorDebugInfo::CALL_INSTRUCTION) != 0) { printf(" CALL_INSTRUCTION"); } - if ((loc.GetSourceType() & ICorDebugInfo::ASYNC) != 0) + if ((loc.GetSourceTypes() & ICorDebugInfo::ASYNC) != 0) { printf(" ASYNC"); } @@ -6512,7 +6512,7 @@ void CodeGen::genReportRichDebugInfoInlineTreeToFile(FILE* file, InlineContext* fprintf(file, "{\"Ordinal\":%u,", context->GetOrdinal()); fprintf(file, "\"MethodID\":%lld,", (int64_t)context->GetCallee()); fprintf(file, "\"ILOffset\":%u,", context->GetLocation().GetOffset()); - fprintf(file, "\"LocationFlags\":%u,", (uint32_t)context->GetLocation().GetSourceType()); + fprintf(file, "\"LocationFlags\":%u,", (uint32_t)context->GetLocation().GetSourceTypes()); fprintf(file, "\"ExactILOffset\":%u,", context->GetActualCallOffset()); auto append = [&]() { char buffer[256]; @@ -6677,7 +6677,7 @@ void CodeGen::genReportRichDebugInfo() mapping->NativeOffset = richMapping.nativeLoc.CodeOffset(GetEmitter()); mapping->Inlinee = richMapping.debugInfo.GetInlineContext()->GetOrdinal(); mapping->ILOffset = richMapping.debugInfo.GetLocation().GetOffset(); - mapping->Source = richMapping.debugInfo.GetLocation().GetSourceType(); + mapping->Source = richMapping.debugInfo.GetLocation().GetSourceTypes(); mappingIndex++; } diff --git a/src/coreclr/jit/compiler.h b/src/coreclr/jit/compiler.h index bbf887a6a08556..51f838784dfed0 100644 --- a/src/coreclr/jit/compiler.h +++ b/src/coreclr/jit/compiler.h @@ -2996,6 +2996,8 @@ class Compiler GenTreeIntCon* gtNewTrue(); GenTreeIntCon* gtNewFalse(); + GenTreeILOffset* gtNewILOffsetNode(const DebugInfo& di DEBUGARG(IL_OFFSET lastOffset)); + GenTree* gtNewPhysRegNode(regNumber reg, var_types type); GenTree* gtNewJmpTableNode(); @@ -4601,7 +4603,7 @@ class Compiler CORINFO_CALL_INFO* callInfo, IL_OFFSET rawILOffset); - void impSetupAndSpillForAsyncCall(GenTreeCall* call, OPCODE opcode, unsigned prefixFlags); + void impSetupAndSpillForAsyncCall(GenTreeCall* call, OPCODE opcode, unsigned prefixFlags, const DebugInfo& callDI); void impInsertAsyncContinuationForLdvirtftnCall(GenTreeCall* call); diff --git a/src/coreclr/jit/debuginfo.cpp b/src/coreclr/jit/debuginfo.cpp index 29f4a3a0d50276..15cb567bbdeb9b 100644 --- a/src/coreclr/jit/debuginfo.cpp +++ b/src/coreclr/jit/debuginfo.cpp @@ -21,9 +21,9 @@ void ILLocation::Dump() const else { printf("0x%03X[", GetOffset()); - printf("%c", ((m_sourceType & ICorDebugInfo::STACK_EMPTY) != 0) ? 'E' : '-'); - printf("%c", ((m_sourceType & ICorDebugInfo::CALL_INSTRUCTION) != 0) ? 'C' : '-'); - printf("%c", ((m_sourceType & ICorDebugInfo::ASYNC) != 0) ? 'A' : '-'); + printf("%c", ((m_sourceTypes & ICorDebugInfo::STACK_EMPTY) != 0) ? 'E' : '-'); + printf("%c", ((m_sourceTypes & ICorDebugInfo::CALL_INSTRUCTION) != 0) ? 'C' : '-'); + printf("%c", ((m_sourceTypes & ICorDebugInfo::ASYNC) != 0) ? 'A' : '-'); printf("]"); } } diff --git a/src/coreclr/jit/debuginfo.h b/src/coreclr/jit/debuginfo.h index fb93e628c25285..3db39fa73cee20 100644 --- a/src/coreclr/jit/debuginfo.h +++ b/src/coreclr/jit/debuginfo.h @@ -16,9 +16,9 @@ class ILLocation { } - ILLocation(IL_OFFSET offset, ICorDebugInfo::SourceTypes sourceType) + ILLocation(IL_OFFSET offset, ICorDebugInfo::SourceTypes sourceTypes) : m_offset(offset) - , m_sourceType(sourceType) + , m_sourceTypes(sourceTypes) { } @@ -27,14 +27,14 @@ class ILLocation return m_offset; } - ICorDebugInfo::SourceTypes GetSourceType() const + ICorDebugInfo::SourceTypes GetSourceTypes() const { - return m_sourceType; + return m_sourceTypes; } bool IsCallInstruction() const { - return (m_sourceType & ICorDebugInfo::CALL_INSTRUCTION) != 0; + return (m_sourceTypes & ICorDebugInfo::CALL_INSTRUCTION) != 0; } bool IsValid() const @@ -44,7 +44,7 @@ class ILLocation inline bool operator==(const ILLocation& other) const { - return (m_offset == other.m_offset) && (m_sourceType == other.m_sourceType); + return (m_offset == other.m_offset) && (m_sourceTypes == other.m_sourceTypes); } inline bool operator!=(const ILLocation& other) const @@ -58,8 +58,8 @@ class ILLocation #endif private: - IL_OFFSET m_offset = BAD_IL_OFFSET; - ICorDebugInfo::SourceTypes m_sourceType = ICorDebugInfo::SOURCE_TYPE_INVALID; + IL_OFFSET m_offset = BAD_IL_OFFSET; + ICorDebugInfo::SourceTypes m_sourceTypes = ICorDebugInfo::SOURCE_TYPE_INVALID; }; // Represents debug information about a statement. diff --git a/src/coreclr/jit/ee_il_dll.cpp b/src/coreclr/jit/ee_il_dll.cpp index 5dcb4d42c37680..b0959b9a9d95e5 100644 --- a/src/coreclr/jit/ee_il_dll.cpp +++ b/src/coreclr/jit/ee_il_dll.cpp @@ -1003,7 +1003,7 @@ void Compiler::eeSetLIinfo(unsigned which, UNATIVE_OFFSET nativeOffset, IPmappin { case IPmappingDscKind::Normal: eeBoundaries[which].ilOffset = loc.GetOffset(); - eeBoundaries[which].source = loc.GetSourceType(); + eeBoundaries[which].source = loc.GetSourceTypes(); break; case IPmappingDscKind::Prolog: eeBoundaries[which].ilOffset = ICorDebugInfo::PROLOG; @@ -1096,12 +1096,17 @@ void Compiler::eeDispLineInfo(const ICorDebugInfo::OffsetMapping* line) { printf("CALL_SITE "); } + if ((line->source & ICorDebugInfo::ASYNC) != 0) + { + printf("ASYNC "); + } printf(")"); } printf("\n"); // We don't expect to see any other bits. - assert((line->source & ~(ICorDebugInfo::STACK_EMPTY | ICorDebugInfo::CALL_INSTRUCTION)) == 0); + assert((line->source & ~(ICorDebugInfo::STACK_EMPTY | ICorDebugInfo::CALL_INSTRUCTION | ICorDebugInfo::ASYNC)) == + 0); } void Compiler::eeDispLineInfos() diff --git a/src/coreclr/jit/gentree.cpp b/src/coreclr/jit/gentree.cpp index 4aec12b85d1da1..01a42fe713a004 100644 --- a/src/coreclr/jit/gentree.cpp +++ b/src/coreclr/jit/gentree.cpp @@ -7749,6 +7749,11 @@ GenTreeIntCon* Compiler::gtNewFalse() return gtNewIconNode(0, TYP_INT); } +GenTreeILOffset* Compiler::gtNewILOffsetNode(const DebugInfo& di DEBUGARG(IL_OFFSET lastOffset)) +{ + return new (this, GT_IL_OFFSET) GenTreeILOffset(di DEBUGARG(lastOffset)); +} + // return a new node representing the value in a physical register GenTree* Compiler::gtNewPhysRegNode(regNumber reg, var_types type) { diff --git a/src/coreclr/jit/gentree.h b/src/coreclr/jit/gentree.h index b76a27879a4a7e..6aa4a5e637cf09 100644 --- a/src/coreclr/jit/gentree.h +++ b/src/coreclr/jit/gentree.h @@ -4364,6 +4364,9 @@ enum class ContinuationContextHandling // Additional async call info. struct AsyncCallInfo { + // DebugInfo with SOURCE_TYPE_ASYNC pointing at the call IL instruction + DebugInfo CallAsyncDebugInfo; + // The following information is used to implement the proper observable handling of `ExecutionContext`, // `SynchronizationContext` and `TaskScheduler` in async methods. // diff --git a/src/coreclr/jit/importercalls.cpp b/src/coreclr/jit/importercalls.cpp index beca2374c6713a..5bcd8f2f4ee68a 100644 --- a/src/coreclr/jit/importercalls.cpp +++ b/src/coreclr/jit/importercalls.cpp @@ -386,7 +386,7 @@ var_types Compiler::impImportCall(OPCODE opcode, if (sig->isAsyncCall()) { - impSetupAndSpillForAsyncCall(call->AsCall(), opcode, prefixFlags); + impSetupAndSpillForAsyncCall(call->AsCall(), opcode, prefixFlags, di); } impPopCallArgs(sig, call->AsCall()); @@ -691,7 +691,7 @@ var_types Compiler::impImportCall(OPCODE opcode, if (sig->isAsyncCall()) { - impSetupAndSpillForAsyncCall(call->AsCall(), opcode, prefixFlags); + impSetupAndSpillForAsyncCall(call->AsCall(), opcode, prefixFlags, di); } // Now create the argument list. @@ -6814,11 +6814,20 @@ void Compiler::impCheckForPInvokeCall( // call - The call // opcode - The IL opcode for the call // prefixFlags - Flags containing context handling information from IL +// callDI - Debug info for the async call // -void Compiler::impSetupAndSpillForAsyncCall(GenTreeCall* call, OPCODE opcode, unsigned prefixFlags) +void Compiler::impSetupAndSpillForAsyncCall(GenTreeCall* call, + OPCODE opcode, + unsigned prefixFlags, + const DebugInfo& callDI) { AsyncCallInfo asyncInfo; + unsigned newSourceTypes = ICorDebugInfo::ASYNC; + newSourceTypes |= (unsigned)callDI.GetLocation().GetSourceTypes() & ~ICorDebugInfo::CALL_INSTRUCTION; + ILLocation newILLocation(callDI.GetLocation().GetOffset(), (ICorDebugInfo::SourceTypes)newSourceTypes); + asyncInfo.CallAsyncDebugInfo = DebugInfo(callDI.GetInlineContext(), newILLocation); + if ((prefixFlags & PREFIX_IS_TASK_AWAIT) != 0) { JITDUMP("Call is an async task await\n"); diff --git a/src/coreclr/jit/rationalize.cpp b/src/coreclr/jit/rationalize.cpp index 2ae7bef6c5c42a..dcec6f6c608bf3 100644 --- a/src/coreclr/jit/rationalize.cpp +++ b/src/coreclr/jit/rationalize.cpp @@ -1951,8 +1951,7 @@ PhaseStatus Rationalizer::DoPhase() DebugInfo di = statement->GetDebugInfo(); if (di.IsValid() || di.GetRoot().IsValid()) { - GenTreeILOffset* ilOffset = - new (comp, GT_IL_OFFSET) GenTreeILOffset(di DEBUGARG(statement->GetLastILOffset())); + GenTreeILOffset* ilOffset = comp->gtNewILOffsetNode(di DEBUGARG(statement->GetLastILOffset())); BlockRange().InsertBefore(statement->GetTreeList(), ilOffset); } diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs b/src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs index 310cfd5d752da0..c22c3954907475 100644 --- a/src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs +++ b/src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs @@ -1262,7 +1262,8 @@ public enum SourceTypes STACK_EMPTY = 0x02, // The stack is empty here CALL_SITE = 0x04, // This is a call site. NATIVE_END_OFFSET_UNKNOWN = 0x08, // Indicates a epilog endpoint - CALL_INSTRUCTION = 0x10 // The actual instruction of a call. + CALL_INSTRUCTION = 0x10, // The actual instruction of a call. + ASYNC = 0x20, // async suspension/resumption code for a specific async call }; public struct OffsetMapping diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/DebugInfoTableNode.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/DebugInfoTableNode.cs index d1318d881bd74e..f2a1484bdeea91 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/DebugInfoTableNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/DebugInfoTableNode.cs @@ -167,9 +167,10 @@ public static byte[] CreateBoundsBlobForMethod(OffsetMapping[] offsetMapping) writer2.WriteUInt((uint)offsetMapping.Length); // We need the total count writer2.WriteUInt((uint)bitWidthReportForNativeDelta); // Number of bits needed for native deltas writer2.WriteUInt((uint)bitWidthReportForILOffset); // How many bits needed for IL offsets + const int BitsForSourceType = 3; int bitWidth = bitWidthForNativeDelta + bitWidthForILOffset + - 2; // for the source data + BitsForSourceType; int totalBits = bitWidth * offsetMapping.Length; int bytesNeededForArray = (totalBits + 7) / 8; @@ -190,28 +191,22 @@ public static byte[] CreateBoundsBlobForMethod(OffsetMapping[] offsetMapping) uint nativeOffsetDelta = bound.nativeOffset - prevNativeOffset; uint sourceBits = 0; - switch ((int)bound.source) - { - case (int)Internal.JitInterface.SourceTypes.SOURCE_TYPE_INVALID: - sourceBits = 0; - break; - case (int)Internal.JitInterface.SourceTypes.CALL_INSTRUCTION: - sourceBits = 1; - break; - case (int)Internal.JitInterface.SourceTypes.STACK_EMPTY: - sourceBits = 2; - break; - case (int)(Internal.JitInterface.SourceTypes.CALL_INSTRUCTION | Internal.JitInterface.SourceTypes.STACK_EMPTY): - sourceBits = 3; - break; - default: - throw new InternalCompilerErrorException("Unknown source type"); - } + if ((bound.source & SourceTypes.CALL_INSTRUCTION) != 0) + sourceBits |= 1; + if ((bound.source & SourceTypes.STACK_EMPTY) != 0) + sourceBits |= 2; + if ((bound.source & SourceTypes.ASYNC) != 0) + sourceBits |= 4; + + if ((bound.source & ~(SourceTypes.CALL_INSTRUCTION | SourceTypes.STACK_EMPTY | SourceTypes.ASYNC)) != 0) + throw new InternalCompilerErrorException("Unknown source type " + (uint)bound.source); + if ((sourceBits & ~((1u << BitsForSourceType) - 1)) != 0) + throw new InternalCompilerErrorException("Unencodable source type " + sourceBits + " (for " + (uint)bound.source + ")"); ulong mappingDataEncoded = (ulong)sourceBits | - ((ulong)nativeOffsetDelta << 2) | - ((ulong)((int)bound.ilOffset - (int)MappingTypes.EPILOG) << (2 + bitWidthForNativeDelta)); + ((ulong)nativeOffsetDelta << BitsForSourceType) | + ((ulong)((int)bound.ilOffset - (int)MappingTypes.EPILOG) << (BitsForSourceType + bitWidthForNativeDelta)); for (byte bitsToWrite = (byte)bitWidth; bitsToWrite > 0;) { diff --git a/src/coreclr/vm/debuginfostore.cpp b/src/coreclr/vm/debuginfostore.cpp index 06e9712f4fef41..eff248a3fb976a 100644 --- a/src/coreclr/vm/debuginfostore.cpp +++ b/src/coreclr/vm/debuginfostore.cpp @@ -491,6 +491,8 @@ static void DoAsyncVars( } } +static constexpr int BITS_FOR_SOURCE_TYPE = 3; + #ifndef DACCESS_COMPILE BYTE* DecompressNew(void*, size_t cBytes) @@ -562,7 +564,7 @@ void CompressDebugInfo::CompressBoundaries( pWriter->WriteEncodedU32(nativeOffsetBits - 1); pWriter->WriteEncodedU32(ilOffsetBits - 1); - uint32_t bitWidth = 2 + nativeOffsetBits + ilOffsetBits; + uint32_t bitWidth = BITS_FOR_SOURCE_TYPE + nativeOffsetBits + ilOffsetBits; uint8_t bitsInProgress = 0; uint8_t bitsInProgressCount = 0; @@ -578,31 +580,23 @@ void CompressDebugInfo::CompressBoundaries( ICorDebugInfo::OffsetMapping * pBound = &pMap[i]; + // We only expect to see some source types + _ASSERTE((pBound->source & ~(ICorDebugInfo::CALL_INSTRUCTION | ICorDebugInfo::STACK_EMPTY | ICorDebugInfo::ASYNC)) == 0); + uint32_t sourceBits = 0; - switch ((int)pBound->source) - { - case (int)ICorDebugInfo::SOURCE_TYPE_INVALID: - sourceBits = 0; - break; - case (int)ICorDebugInfo::CALL_INSTRUCTION: - sourceBits = 1; - break; - case (int)ICorDebugInfo::STACK_EMPTY: - sourceBits = 2; - break; - case (int)(ICorDebugInfo::CALL_INSTRUCTION | ICorDebugInfo::STACK_EMPTY): - sourceBits = 3; - break; - default: - _ASSERTE(!"Unknown source type in CompressDebugInfo::CompressBoundaries"); - sourceBits = 0; // default to invalid - break; - } + if ((pBound->source & ICorDebugInfo::CALL_INSTRUCTION) != 0) + sourceBits |= 1; + if ((pBound->source & ICorDebugInfo::STACK_EMPTY) != 0) + sourceBits |= 2; + if ((pBound->source & ICorDebugInfo::ASYNC) != 0) + sourceBits |= 4; + // Should be encodable in BITS_FOR_SOURCE_TYPE bits + _ASSERTE((sourceBits & ~((1u << BITS_FOR_SOURCE_TYPE) - 1)) == 0); uint64_t mappingDataEncoded = sourceBits | - ((uint64_t)nativeOffsetDelta << 2) | - ((uint64_t)((int32_t)pBound->ilOffset - (int32_t)ICorDebugInfo::MAX_MAPPING_VALUE) << (2 + nativeOffsetBits)); + ((uint64_t)nativeOffsetDelta << BITS_FOR_SOURCE_TYPE) | + ((uint64_t)((int32_t)pBound->ilOffset - (int32_t)ICorDebugInfo::MAX_MAPPING_VALUE) << (BITS_FOR_SOURCE_TYPE + nativeOffsetBits)); for (uint8_t bitsToWrite = (uint8_t)bitWidth; bitsToWrite > 0;) { @@ -1090,7 +1084,7 @@ static void DoBounds(PTR_BYTE addrBounds, uint32_t cbBounds, TNumBounds countHan uint32_t bitsForNativeDelta = r.ReadEncodedU32_NoThrow() + 1; // Number of bits needed for native deltas uint32_t bitsForILOffsets = r.ReadEncodedU32_NoThrow() + 1; // How many bits needed for IL offsets - uint32_t bitsPerEntry = bitsForNativeDelta + bitsForILOffsets + 2; // 2 bits for source type + uint32_t bitsPerEntry = bitsForNativeDelta + bitsForILOffsets + BITS_FOR_SOURCE_TYPE; TADDR addrBoundsArray = dac_cast(addrBounds) + r.GetNextByteIndex(); TADDR addrBoundsArrayForReads = AlignDown(addrBoundsArray, sizeof(uint64_t)); uint32_t bitOffsetForReads = (uint32_t)((addrBoundsArray - addrBoundsArrayForReads) * 8); // We want to read using aligned 64bit reads, but we want to start at the right bit offset. @@ -1104,23 +1098,17 @@ static void DoBounds(PTR_BYTE addrBounds, uint32_t cbBounds, TNumBounds countHan for (uint32_t iEntry = 0; iEntry < cNumEntries; iEntry++, bitOffsetForReads += bitsPerEntry) { uint64_t mappingDataEncoded = ReadFromBitOffsets(dac_cast(addrBoundsArrayForReads), bitOffsetForReads, bitsPerEntry); - switch (mappingDataEncoded & 0x3) // Last 2 bits are source type - { - case 0: - bound.source = ICorDebugInfo::SOURCE_TYPE_INVALID; - break; - case 1: - bound.source = ICorDebugInfo::CALL_INSTRUCTION; - break; - case 2: - bound.source = ICorDebugInfo::STACK_EMPTY; - break; - case 3: - bound.source = (ICorDebugInfo::SourceTypes)(ICorDebugInfo::STACK_EMPTY | ICorDebugInfo::CALL_INSTRUCTION); - break; - } + uint32_t sourceTypes = 0; + if ((mappingDataEncoded & 1) != 0) + sourceTypes |= ICorDebugInfo::CALL_INSTRUCTION; + if ((mappingDataEncoded & 2) != 0) + sourceTypes |= ICorDebugInfo::STACK_EMPTY; + if ((mappingDataEncoded & 4) != 0) + sourceTypes |= ICorDebugInfo::ASYNC; + + bound.source = (ICorDebugInfo::SourceTypes)sourceTypes; - mappingDataEncoded = mappingDataEncoded >> 2; // Remove source type bits + mappingDataEncoded = mappingDataEncoded >> BITS_FOR_SOURCE_TYPE; // Remove source type bits uint32_t nativeOffsetDelta = (uint32_t)(mappingDataEncoded & ((1ULL << bitsForNativeDelta) - 1)); currentNativeOffset += nativeOffsetDelta; bound.nativeOffset = currentNativeOffset; From bd99f2cf2c8a82d07f804a2082c76b36317da86d Mon Sep 17 00:00:00 2001 From: Jakob Botsch Nielsen Date: Thu, 23 Oct 2025 13:22:45 +0200 Subject: [PATCH 47/72] Fix arm arch codegen, fix LA64/RISCV64 builds --- src/coreclr/jit/codegenarmarch.cpp | 5 +- src/coreclr/jit/codegenloongarch64.cpp | 71 ++++++-------------------- src/coreclr/jit/codegenriscv64.cpp | 71 ++++++-------------------- 3 files changed, 36 insertions(+), 111 deletions(-) diff --git a/src/coreclr/jit/codegenarmarch.cpp b/src/coreclr/jit/codegenarmarch.cpp index a083d3b9caa3b3..1849c5496126f9 100644 --- a/src/coreclr/jit/codegenarmarch.cpp +++ b/src/coreclr/jit/codegenarmarch.cpp @@ -557,8 +557,11 @@ void CodeGen::genCodeForTreeNode(GenTree* treeNode) #endif // TARGET_ARM case GT_IL_OFFSET: + // Do nothing; this node is a marker for debug info. + break; + case GT_RECORD_ASYNC_RESUME: - // Do nothing; these nodes are simply markers for debug info. + genRecordAsyncResume(treeNode->AsVal()); break; default: diff --git a/src/coreclr/jit/codegenloongarch64.cpp b/src/coreclr/jit/codegenloongarch64.cpp index d172af0afb5882..c89c9919f364d6 100644 --- a/src/coreclr/jit/codegenloongarch64.cpp +++ b/src/coreclr/jit/codegenloongarch64.cpp @@ -2278,6 +2278,13 @@ void CodeGen::genJumpTable(GenTree* treeNode) genProduceReg(treeNode); } +void CodeGen::genAsyncResumeInfo(GenTreeVal* treeNode) +{ + GetEmitter()->emitIns_R_C(INS_bl, emitActualTypeSize(TYP_I_IMPL), treeNode->GetRegNum(), REG_NA, + genEmitAsyncResumeInfo((unsigned)treeNode->gtVal1), 0); + genProduceReg(treeNode); +} + //------------------------------------------------------------------------ // genLockedInstructions: Generate code for a GT_XADD or GT_XCHG node. // @@ -4367,6 +4374,10 @@ void CodeGen::genCodeForTreeNode(GenTree* treeNode) emit->emitIns_R_L(INS_ld_d, EA_PTRSIZE, genPendingCallLabel, targetReg); break; + case GT_ASYNC_RESUME_INFO: + genAsyncResumeInfo(treeNode->AsVal()); + break; + case GT_STORE_BLK: genCodeForStoreBlk(treeNode->AsBlk()); break; @@ -4380,8 +4391,11 @@ void CodeGen::genCodeForTreeNode(GenTree* treeNode) break; case GT_IL_OFFSET: - case GT_RECORD_ASYNC_JOIN: - // Do nothing; these nodes are simply markers for debug info. + // Do nothing; this node is a marker for debug info. + break; + + case GT_RECORD_ASYNC_RESUME: + genRecordAsyncResume(treeNode->AsVal()); break; default: @@ -6962,57 +6976,4 @@ void CodeGen::genProfilingLeaveCallback(unsigned helper /*= CORINFO_HELP_PROF_FC } #endif // PROFILING_SUPPORTED -//----------------------------------------------------------------------------------- -// genCodeForAsyncresumeTrampolineJump: -// Generate code to jump to an async resumption stub. -// -// Arguments: -// methHnd - Handle of resumption stub -// lookup - Lookup for the address of the resumption stub -// -// Remarks: -// The codegen for these jumps must handle a non-standard environment. No -// prolog has been run when these are invoked, so they cannot use locals, -// they cannot use non-volatile registers, and they cannot trash the argument -// registers that resumption stubs use (see BuildResumptionStubSignature in -// the VM). -// -void CodeGen::genCodeForAsyncResumeTrampolineJump(CORINFO_METHOD_HANDLE methHnd, const CORINFO_CONST_LOOKUP& lookup) -{ - switch (lookup.accessType) - { - case IAT_VALUE: - case IAT_PVALUE: - { - EmitCallParams call; - call.ptrVars = VarSetOps::MakeEmpty(compiler); - call.gcrefRegs = RBM_NONE; - call.byrefRegs = RBM_NONE; - call.isJump = true; - call.methHnd = methHnd; - - instGen_Set_Reg_To_Imm(EA_SET_FLG(EA_PTRSIZE, EA_CNS_RELOC_FLG), REG_T0, (ssize_t)lookup.addr, - INS_FLAGS_DONT_CARE DEBUGARG((size_t)methHnd) DEBUGARG(GTF_ICON_FTN_ADDR)); - - if (lookup.accessType == IAT_PVALUE) - { - GetEmitter()->emitIns_R_R(ins_Load(TYP_I_IMPL), EA_PTRSIZE, REG_T0, REG_T0); - } - call.callType = EC_INDIR_R; - call.ireg = REG_T0; - GetEmitter()->emitIns_Call(call); - break; - } - case IAT_PPVALUE: - noway_assert(!"Cannot handle PPVALUE access type"); - break; - case IAT_RELPVALUE: - noway_assert(!"Cannot handle RELPVALUE access type"); - break; - default: - noway_assert(!"Cannot handle access type"); - break; - } -} - #endif // TARGET_LOONGARCH64 diff --git a/src/coreclr/jit/codegenriscv64.cpp b/src/coreclr/jit/codegenriscv64.cpp index a149155339f2c9..31f770c37db291 100644 --- a/src/coreclr/jit/codegenriscv64.cpp +++ b/src/coreclr/jit/codegenriscv64.cpp @@ -2233,6 +2233,13 @@ void CodeGen::genJumpTable(GenTree* treeNode) genProduceReg(treeNode); } +void CodeGen::genAsyncResumeInfo(GenTreeVal* treeNode) +{ + GetEmitter()->emitIns_R_C(INS_addi, emitActualTypeSize(TYP_I_IMPL), treeNode->GetRegNum(), REG_NA, + genEmitAsyncResumeInfo((unsigned)treeNode->gtVal1)); + genProduceReg(treeNode); +} + //------------------------------------------------------------------------ // genLockedInstructions: Generate code for a GT_XADD, GT_XAND, GT_XORR or GT_XCHG node. // @@ -4368,6 +4375,10 @@ void CodeGen::genCodeForTreeNode(GenTree* treeNode) emit->emitIns_R_L(INS_ld, EA_PTRSIZE, genPendingCallLabel, targetReg); break; + case GT_ASYNC_RESUME_INFO: + genAsyncResumeInfo(treeNode->AsVal()); + break; + case GT_STORE_BLK: genCodeForStoreBlk(treeNode->AsBlk()); break; @@ -4381,8 +4392,11 @@ void CodeGen::genCodeForTreeNode(GenTree* treeNode) break; case GT_IL_OFFSET: - case GT_RECORD_ASYNC_JOIN: - // Do nothing; these nodes are simply markers for debug info. + // Do nothing; this node is a marker for debug info. + break; + + case GT_RECORD_ASYNC_RESUME: + genRecordAsyncResume(treeNode->AsVal()); break; case GT_SH1ADD: @@ -6990,57 +7004,4 @@ void CodeGen::genProfilingLeaveCallback(unsigned helper /*= CORINFO_HELP_PROF_FC } #endif // PROFILING_SUPPORTED -//----------------------------------------------------------------------------------- -// genCodeForAsyncresumeTrampolineJump: -// Generate code to jump to an async resumption stub. -// -// Arguments: -// methHnd - Handle of resumption stub -// lookup - Lookup for the address of the resumption stub -// -// Remarks: -// The codegen for these jumps must handle a non-standard environment. No -// prolog has been run when these are invoked, so they cannot use locals, -// they cannot use non-volatile registers, and they cannot trash the argument -// registers that resumption stubs use (see BuildResumptionStubSignature in -// the VM). -// -void CodeGen::genCodeForAsyncResumeTrampolineJump(CORINFO_METHOD_HANDLE methHnd, const CORINFO_CONST_LOOKUP& lookup) -{ - switch (lookup.accessType) - { - case IAT_VALUE: - case IAT_PVALUE: - { - EmitCallParams call; - call.ptrVars = VarSetOps::MakeEmpty(compiler); - call.gcrefRegs = RBM_NONE; - call.byrefRegs = RBM_NONE; - call.isJump = true; - call.methHnd = methHnd; - - instGen_Set_Reg_To_Imm(EA_SET_FLG(EA_PTRSIZE, EA_CNS_RELOC_FLG), REG_T0, (ssize_t)lookup.addr, - INS_FLAGS_DONT_CARE DEBUGARG((size_t)methHnd) DEBUGARG(GTF_ICON_FTN_ADDR)); - - if (lookup.accessType == IAT_PVALUE) - { - GetEmitter()->emitIns_R_R(ins_Load(TYP_I_IMPL), EA_PTRSIZE, REG_T0, REG_T0); - } - call.callType = EC_INDIR_R; - call.ireg = REG_T0; - GetEmitter()->emitIns_Call(call); - break; - } - case IAT_PPVALUE: - noway_assert(!"Cannot handle PPVALUE access type"); - break; - case IAT_RELPVALUE: - noway_assert(!"Cannot handle RELPVALUE access type"); - break; - default: - noway_assert(!"Cannot handle access type"); - break; - } -} - #endif // TARGET_RISCV64 From ffa76fa0558f0aac7a0226ad8076deb7545dde13 Mon Sep 17 00:00:00 2001 From: Jakob Botsch Nielsen Date: Thu, 23 Oct 2025 16:47:33 +0200 Subject: [PATCH 48/72] Clean up, revert unnecessary changes --- src/coreclr/jit/gtlist.h | 2 +- .../tools/Common/JitInterface/CorInfoTypes.cs | 15 ++++----------- .../JitInterface/CorInfoImpl.ReadyToRun.cs | 12 ++++++++++-- 3 files changed, 15 insertions(+), 14 deletions(-) diff --git a/src/coreclr/jit/gtlist.h b/src/coreclr/jit/gtlist.h index 4b5074efdbac25..162aaf42424585 100644 --- a/src/coreclr/jit/gtlist.h +++ b/src/coreclr/jit/gtlist.h @@ -359,7 +359,7 @@ GTNODE(SWAP , GenTreeOp ,0,0,GTK_BINOP|GTK_NOVALUE|DBK_NOTH GTNODE(COPY , GenTreeCopyOrReload,0,0,GTK_UNOP|DBK_NOTHIR) // Copies a variable from its current location to a register that satisfies GTNODE(RELOAD , GenTreeCopyOrReload,0,0,GTK_UNOP|DBK_NOTHIR) // code generation constraints. The operand is the actual lclVar node. GTNODE(IL_OFFSET , GenTreeILOffset ,0,0,GTK_LEAF|GTK_NOVALUE|DBK_NOTHIR) // marks an IL offset for debugging purposes -GTNODE(RECORD_ASYNC_RESUME, GenTreeVal ,0,0,GTK_LEAF|GTK_NOVALUE|DBK_NOTHIR) // record native offset for an async resume point +GTNODE(RECORD_ASYNC_RESUME, GenTreeVal ,0,0,GTK_LEAF|GTK_NOVALUE|DBK_NOTHIR) // record native offset for an async resume point /*****************************************************************************/ #undef GTNODE diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs b/src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs index c22c3954907475..6931f4975c996c 100644 --- a/src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs +++ b/src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs @@ -1322,23 +1322,16 @@ public struct RichOffsetMapping public struct AsyncContinuationVarInfo { - // IL number of variable (or one of the special IL numbers, like UNKNOWN_ILNUM) + // IL number of variable (or one of the special IL numbers, like TYPECTXT_ILNUM) public uint VarNumber; - // Offset in continuation's data where this variable is stored + // Offset in continuation object where this variable is stored public uint Offset; - public uint GCIndex; } public struct AsyncSuspensionPoint { - // IL offset in the root method that resulted in the creation of this suspension point. - public uint RootILOffset; - // Index of inline tree node containing the IL offset (0 for root) - public uint Inlinee; - // IL offset that resulted in the creation of the suspension point. - public uint ILOffset; - public uint NumVars; - // Count of AsyncContinuationVarInfo + // Count of AsyncContinuationVarInfo in array of locals starting where + // the previous suspension point's locals end. public uint NumContinuationVars; } diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs index a91178d904d89c..2bde41e031f496 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs @@ -1506,7 +1506,11 @@ private void setVars(CORINFO_METHOD_STRUCT_* ftn, uint cVars, NativeVarInfo* var if (cVars == 0) return; - _debugVarInfos = new Span(vars, (int)cVars).ToArray(); + _debugVarInfos = new NativeVarInfo[cVars]; + for (int i = 0; i < cVars; i++) + { + _debugVarInfos[i] = vars[i]; + } // JIT gave the ownership of this to us, so need to free this. freeArray(vars); @@ -1519,7 +1523,11 @@ private void setBoundaries(CORINFO_METHOD_STRUCT_* ftn, uint cMap, OffsetMapping { Debug.Assert(_debugLocInfos == null); - _debugLocInfos = new Span(pMap, (int)cMap).ToArray(); + _debugLocInfos = new OffsetMapping[cMap]; + for (int i = 0; i < cMap; i++) + { + _debugLocInfos[i] = pMap[i]; + } // JIT gave the ownership of this to us, so need to free this. freeArray(pMap); From 07bf13757239afc25a7a5959d1447125660ae105 Mon Sep 17 00:00:00 2001 From: Jakob Botsch Nielsen Date: Thu, 23 Oct 2025 16:48:33 +0200 Subject: [PATCH 49/72] Update after merge --- src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs | 2 +- src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs index 3b2ac8253c7a93..84aee76e93622e 100644 --- a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs +++ b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs @@ -3369,7 +3369,7 @@ private void getAsyncInfo(ref CORINFO_ASYNC_INFO pAsyncInfoOut) DefType continuation = _compilation.TypeSystemContext.SystemModule.GetKnownType("System.Runtime.CompilerServices"u8, "Continuation"u8); pAsyncInfoOut.continuationClsHnd = ObjectToHandle(continuation); pAsyncInfoOut.continuationNextFldHnd = ObjectToHandle(continuation.GetKnownField("Next"u8)); - pAsyncInfoOut.continuationResumeFldHnd = ObjectToHandle(continuation.GetKnownField("Resume"u8)); + pAsyncInfoOut.continuationResumeInfoFldHnd = ObjectToHandle(continuation.GetKnownField("ResumeInfo"u8)); pAsyncInfoOut.continuationStateFldHnd = ObjectToHandle(continuation.GetKnownField("State"u8)); pAsyncInfoOut.continuationFlagsFldHnd = ObjectToHandle(continuation.GetKnownField("Flags"u8)); DefType asyncHelpers = _compilation.TypeSystemContext.SystemModule.GetKnownType("System.Runtime.CompilerServices"u8, "AsyncHelpers"u8); diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs b/src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs index 9077c2f11f34cb..b4fc8cc0c6a160 100644 --- a/src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs +++ b/src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs @@ -878,8 +878,8 @@ public unsafe struct CORINFO_ASYNC_INFO public CORINFO_CLASS_STRUCT_* continuationClsHnd; // 'Next' field public CORINFO_FIELD_STRUCT_* continuationNextFldHnd; - // 'Resume' field - public CORINFO_FIELD_STRUCT_* continuationResumeFldHnd; + // 'ResumeInfo' field + public CORINFO_FIELD_STRUCT_* continuationResumeInfoFldHnd; // 'State' field public CORINFO_FIELD_STRUCT_* continuationStateFldHnd; // 'Flags' field From cd0e6016a3fb82065e11f777a7627f2fd17a36e1 Mon Sep 17 00:00:00 2001 From: Jakob Botsch Nielsen Date: Thu, 23 Oct 2025 16:52:02 +0200 Subject: [PATCH 50/72] Disable async testing --- src/coreclr/inc/clrconfigvalues.h | 2 +- src/tests/async/Directory.Build.targets | 13 +++++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) create mode 100644 src/tests/async/Directory.Build.targets diff --git a/src/coreclr/inc/clrconfigvalues.h b/src/coreclr/inc/clrconfigvalues.h index d95d6c2b7a2857..e5dc2fe9d8fd06 100644 --- a/src/coreclr/inc/clrconfigvalues.h +++ b/src/coreclr/inc/clrconfigvalues.h @@ -715,7 +715,7 @@ RETAIL_CONFIG_DWORD_INFO(EXTERNAL_EnableRiscV64Zbb, W("EnableRiscV64 #endif // Runtime-async -RETAIL_CONFIG_DWORD_INFO(UNSUPPORTED_RuntimeAsync, W("RuntimeAsync"), 1, "Enables runtime async method support") +RETAIL_CONFIG_DWORD_INFO(UNSUPPORTED_RuntimeAsync, W("RuntimeAsync"), 0, "Enables runtime async method support") /// /// Uncategorized diff --git a/src/tests/async/Directory.Build.targets b/src/tests/async/Directory.Build.targets new file mode 100644 index 00000000000000..5a4d413a9e1f62 --- /dev/null +++ b/src/tests/async/Directory.Build.targets @@ -0,0 +1,13 @@ + + + + true + + + + + true + + + + From e016f7707d02973adadba5aa3de8af5c9ca00401 Mon Sep 17 00:00:00 2001 From: Jakob Botsch Nielsen Date: Thu, 23 Oct 2025 16:59:50 +0200 Subject: [PATCH 51/72] Function headers --- src/coreclr/jit/codegenarm.cpp | 5 ++++- src/coreclr/jit/codegenarm64.cpp | 6 ++++++ src/coreclr/jit/codegencommon.cpp | 23 +++++++++++++++++++++++ src/coreclr/jit/codegenloongarch64.cpp | 6 ++++++ src/coreclr/jit/codegenriscv64.cpp | 6 ++++++ src/coreclr/jit/codegenxarch.cpp | 6 ++++++ src/coreclr/jit/emit.cpp | 9 +++++++++ src/coreclr/jit/gentree.cpp | 11 +++++++++++ 8 files changed, 71 insertions(+), 1 deletion(-) diff --git a/src/coreclr/jit/codegenarm.cpp b/src/coreclr/jit/codegenarm.cpp index 25fdf4fa9f209d..4b8a288408c501 100644 --- a/src/coreclr/jit/codegenarm.cpp +++ b/src/coreclr/jit/codegenarm.cpp @@ -654,7 +654,10 @@ void CodeGen::genJumpTable(GenTree* treeNode) } //------------------------------------------------------------------------ -// genAsyncResumeInfo: emits offset of async resume info +// genAsyncResumeInfo: emits address of async resume info for a specific state +// +// Parameters: +// treeNode - the GT_ASYNC_RESUME_INFO node // void CodeGen::genAsyncResumeInfo(GenTreeVal* treeNode) { diff --git a/src/coreclr/jit/codegenarm64.cpp b/src/coreclr/jit/codegenarm64.cpp index 3394cf9ff340bb..9e760801ca2412 100644 --- a/src/coreclr/jit/codegenarm64.cpp +++ b/src/coreclr/jit/codegenarm64.cpp @@ -3764,6 +3764,12 @@ void CodeGen::genJumpTable(GenTree* treeNode) genProduceReg(treeNode); } +//------------------------------------------------------------------------ +// genAsyncResumeInfo: emits address of async resume info for a specific state +// +// Parameters: +// treeNode - the GT_ASYNC_RESUME_INFO node +// void CodeGen::genAsyncResumeInfo(GenTreeVal* treeNode) { GetEmitter()->emitIns_R_C(INS_adr, emitActualTypeSize(TYP_I_IMPL), treeNode->GetRegNum(), REG_NA, diff --git a/src/coreclr/jit/codegencommon.cpp b/src/coreclr/jit/codegencommon.cpp index 74a4018fab2957..7a215a580704f2 100644 --- a/src/coreclr/jit/codegencommon.cpp +++ b/src/coreclr/jit/codegencommon.cpp @@ -5643,6 +5643,17 @@ unsigned CodeGen::genEmitJumpTable(GenTree* treeNode, bool relativeAddr) return jmpTabBase; } +//---------------------------------------------------------------------------------- +// genEmitAsyncResumeInfoTable: +// Register the singleton async resumption info table if not registered +// before. Return information about it. +// +// Arguments: +// dataSection - [out] The information about the registered data section +// +// Return Value: +// Base offset of the async resumption info table +// UNATIVE_OFFSET CodeGen::genEmitAsyncResumeInfoTable(emitter::dataSection** dataSection) { assert(compiler->compSuspensionPoints != nullptr); @@ -5657,6 +5668,18 @@ UNATIVE_OFFSET CodeGen::genEmitAsyncResumeInfoTable(emitter::dataSection** dataS return genAsyncResumeInfoTableOffset; } +//---------------------------------------------------------------------------------- +// genEmitAsyncResumeInfo: +// Obtain a pseudo-CORINFO_FIELD_HANDLE describing how to access the async +// resume information for a specific state number. +// +// Arguments: +// stateNum - The state +// +// Return Value: +// CORINFO_FIELD_HANDLE encoding access of read-only data at a specific +// offset. +// CORINFO_FIELD_HANDLE CodeGen::genEmitAsyncResumeInfo(unsigned stateNum) { assert(compiler->compSuspensionPoints != nullptr); diff --git a/src/coreclr/jit/codegenloongarch64.cpp b/src/coreclr/jit/codegenloongarch64.cpp index c89c9919f364d6..21efe701971365 100644 --- a/src/coreclr/jit/codegenloongarch64.cpp +++ b/src/coreclr/jit/codegenloongarch64.cpp @@ -2278,6 +2278,12 @@ void CodeGen::genJumpTable(GenTree* treeNode) genProduceReg(treeNode); } +//------------------------------------------------------------------------ +// genAsyncResumeInfo: emits address of async resume info for a specific state +// +// Parameters: +// treeNode - the GT_ASYNC_RESUME_INFO node +// void CodeGen::genAsyncResumeInfo(GenTreeVal* treeNode) { GetEmitter()->emitIns_R_C(INS_bl, emitActualTypeSize(TYP_I_IMPL), treeNode->GetRegNum(), REG_NA, diff --git a/src/coreclr/jit/codegenriscv64.cpp b/src/coreclr/jit/codegenriscv64.cpp index 1c7e1871eef9bd..dc2f177b97cdf7 100644 --- a/src/coreclr/jit/codegenriscv64.cpp +++ b/src/coreclr/jit/codegenriscv64.cpp @@ -2221,6 +2221,12 @@ void CodeGen::genJumpTable(GenTree* treeNode) genProduceReg(treeNode); } +//------------------------------------------------------------------------ +// genAsyncResumeInfo: emits address of async resume info for a specific state +// +// Parameters: +// treeNode - the GT_ASYNC_RESUME_INFO node +// void CodeGen::genAsyncResumeInfo(GenTreeVal* treeNode) { GetEmitter()->emitIns_R_C(INS_addi, emitActualTypeSize(TYP_I_IMPL), treeNode->GetRegNum(), REG_NA, diff --git a/src/coreclr/jit/codegenxarch.cpp b/src/coreclr/jit/codegenxarch.cpp index 92b8bffd0be64b..cfe344c76b0f0d 100644 --- a/src/coreclr/jit/codegenxarch.cpp +++ b/src/coreclr/jit/codegenxarch.cpp @@ -4423,6 +4423,12 @@ void CodeGen::genJumpTable(GenTree* treeNode) genProduceReg(treeNode); } +//------------------------------------------------------------------------ +// genAsyncResumeInfo: emits address of async resume info for a specific state +// +// Parameters: +// treeNode - the GT_ASYNC_RESUME_INFO node +// void CodeGen::genAsyncResumeInfo(GenTreeVal* treeNode) { GetEmitter()->emitIns_R_C(INS_lea, emitTypeSize(TYP_I_IMPL), treeNode->GetRegNum(), diff --git a/src/coreclr/jit/emit.cpp b/src/coreclr/jit/emit.cpp index 80f1701e73762b..f5fc4e293007c5 100644 --- a/src/coreclr/jit/emit.cpp +++ b/src/coreclr/jit/emit.cpp @@ -7965,6 +7965,15 @@ UNATIVE_OFFSET emitter::emitBBTableDataGenBeg(unsigned numEntries, bool relative return secOffs; } +//--------------------------------------------------------------------------- +// emitAsyncResumeTable: +// Allocate space for an async resumption info table in the data sections. +// +// Arguments: +// numEntries - Number of entries in the table +// dataSecOffset - [out] Offset of the data section that was allocated +// dataSec - [out] Information about the data section that was allocated +// void emitter::emitAsyncResumeTable(unsigned numEntries, UNATIVE_OFFSET* dataSecOffs, emitter::dataSection** dataSec) { UNATIVE_OFFSET secOffs = emitConsDsc.dsdOffs; diff --git a/src/coreclr/jit/gentree.cpp b/src/coreclr/jit/gentree.cpp index 17637064885e38..c2790f129df370 100644 --- a/src/coreclr/jit/gentree.cpp +++ b/src/coreclr/jit/gentree.cpp @@ -7749,6 +7749,17 @@ GenTreeIntCon* Compiler::gtNewFalse() return gtNewIconNode(0, TYP_INT); } +//----------------------------------------------------------------------------------------- +// gtNewILOffsetNode: +// Create a GT_IL_OFFSET node with the specified debug information. +// +// Arguments: +// di - The debug information +// lastOffset - Offset corresponding to the IL instruction after this one +// +// Return Value: +// New node. +// GenTreeILOffset* Compiler::gtNewILOffsetNode(const DebugInfo& di DEBUGARG(IL_OFFSET lastOffset)) { return new (this, GT_IL_OFFSET) GenTreeILOffset(di DEBUGARG(lastOffset)); From a9d5243c2748158a0fdc3f3d23cb4dbfa96e56f1 Mon Sep 17 00:00:00 2001 From: Jakob Botsch Nielsen Date: Fri, 24 Oct 2025 10:58:35 +0200 Subject: [PATCH 52/72] Address feedback --- .../CompilerServices/AsyncHelpers.CoreCLR.cs | 52 +++++++++++-------- src/coreclr/jit/emit.cpp | 6 +-- src/coreclr/jit/emit.h | 4 +- 3 files changed, 34 insertions(+), 28 deletions(-) diff --git a/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/AsyncHelpers.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/AsyncHelpers.CoreCLR.cs index efb472bbe19264..353a4981fb6cd3 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/AsyncHelpers.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/AsyncHelpers.CoreCLR.cs @@ -335,37 +335,43 @@ public static void PostToSyncContext(RuntimeAsyncTask task, SynchronizationConte private static class RuntimeAsyncTaskCore { - private unsafe struct NextContinuationData + private unsafe ref struct DispatcherInfo { - public NextContinuationData* Next; - public Continuation* NextContinuation; + // Dispatcher info for next dispatcher present on stack, or + // null if none. + public DispatcherInfo* Next; + // Next continuation the dispatcher will process. + public Continuation? NextContinuation; } - // To be used for async stack walking + // Information about current task dispatching, to be used for async + // stackwalking. [ThreadStatic] - private static unsafe NextContinuationData* t_nextContinuation; + private static unsafe DispatcherInfo* t_dispatcherInfo; public static unsafe void DispatchContinuations(T task) where T : Task, ITaskCompletionAction where TOps : IRuntimeAsyncTaskOps { ExecutionAndSyncBlockStore contexts = default; contexts.Push(); - Continuation? continuation = TOps.GetContinuationState(task); - ref NextContinuationData* nextContRef = ref t_nextContinuation; - NextContinuationData nextContinuationData; - nextContinuationData.Next = nextContRef; - nextContinuationData.NextContinuation = &continuation; - - nextContRef = &nextContinuationData; + ref DispatcherInfo* dispatcherInfoRef = ref t_dispatcherInfo; + DispatcherInfo dispatcherInfo; + dispatcherInfo.Next = dispatcherInfoRef; + dispatcherInfo.NextContinuation = TOps.GetContinuationState(task); + // Ensure JIT does not reorder the above stores with publishing + // the info, which could put the frame information in a bad + // state. + Volatile.WriteBarrier(); + dispatcherInfoRef = &dispatcherInfo; while (true) { - Debug.Assert(continuation != null); + Debug.Assert(dispatcherInfo.NextContinuation != null); try { - Continuation curContinuation = continuation; + Continuation curContinuation = dispatcherInfo.NextContinuation; Continuation? nextContinuation = curContinuation.Next; - continuation = nextContinuation; + dispatcherInfo.NextContinuation = nextContinuation; ref byte resultLoc = ref nextContinuation != null ? ref nextContinuation.GetResultStorageOrNull() : ref TOps.GetResultStorage(task); Continuation? newContinuation = curContinuation.ResumeInfo->Resume(curContinuation, ref resultLoc); @@ -375,13 +381,13 @@ public static unsafe void DispatchContinuations(T task) where T : Task, newContinuation.Next = nextContinuation; HandleSuspended(task); contexts.Pop(); - t_nextContinuation = nextContinuationData.Next; + t_dispatcherInfo = dispatcherInfo.Next; return; } } catch (Exception ex) { - Continuation? handlerContinuation = UnwindToPossibleHandler(continuation); + Continuation? handlerContinuation = UnwindToPossibleHandler(dispatcherInfo.NextContinuation); if (handlerContinuation == null) { // Tail of AsyncTaskMethodBuilderT.SetException @@ -391,7 +397,7 @@ public static unsafe void DispatchContinuations(T task) where T : Task, contexts.Pop(); - t_nextContinuation = nextContinuationData.Next; + t_dispatcherInfo = dispatcherInfo.Next; if (!successfullySet) { @@ -402,16 +408,16 @@ public static unsafe void DispatchContinuations(T task) where T : Task, } handlerContinuation.SetException(ex); - continuation = handlerContinuation; + dispatcherInfo.NextContinuation = handlerContinuation; } - if (continuation == null) + if (dispatcherInfo.NextContinuation == null) { bool successfullySet = TOps.SetCompleted(task); contexts.Pop(); - t_nextContinuation = nextContinuationData.Next; + t_dispatcherInfo = dispatcherInfo.Next; if (!successfullySet) { @@ -421,10 +427,10 @@ public static unsafe void DispatchContinuations(T task) where T : Task, return; } - if (QueueContinuationFollowUpActionIfNecessary(task, continuation)) + if (QueueContinuationFollowUpActionIfNecessary(task, dispatcherInfo.NextContinuation)) { contexts.Pop(); - t_nextContinuation = nextContinuationData.Next; + t_dispatcherInfo = dispatcherInfo.Next; return; } } diff --git a/src/coreclr/jit/emit.cpp b/src/coreclr/jit/emit.cpp index f5fc4e293007c5..5d6df745d098f5 100644 --- a/src/coreclr/jit/emit.cpp +++ b/src/coreclr/jit/emit.cpp @@ -8524,8 +8524,8 @@ void emitter::emitOutputDataSec(dataSecDsc* sec, BYTE* dst) emitLocation* emitLoc = &((emitLocation*)dsc->dsCont)[i]; BYTE* target = emitOffsetToPtr(emitLoc->CodeOffset(this)); - aDstRW[i].Resume = emitAsyncResumeStubEntryPoint; - aDstRW[i].FinalResumeIP = target; + aDstRW[i].Resume = (target_size_t)(uintptr_t)emitAsyncResumeStubEntryPoint; + aDstRW[i].FinalResumeIP = (target_size_t)(uintptr_t)target; if (emitComp->opts.compReloc) { uint16_t relocType = TARGET_POINTER_SIZE == 8 ? IMAGE_REL_BASED_DIR64 : IMAGE_REL_BASED_HIGHLOW; @@ -8533,7 +8533,7 @@ void emitter::emitOutputDataSec(dataSecDsc* sec, BYTE* dst) emitRecordRelocation(&aDstRW[i].FinalResumeIP, target, relocType); } - JITDUMP(" Resume=%p, FinalResumeIP=%p\n", emitAsyncResumeStubEntryPoint, target); + JITDUMP(" Resume=%p, FinalResumeIP=%p\n", emitAsyncResumeStubEntryPoint, (void*)target); } } else diff --git a/src/coreclr/jit/emit.h b/src/coreclr/jit/emit.h index 568f4345126419..c7ac39d628d815 100644 --- a/src/coreclr/jit/emit.h +++ b/src/coreclr/jit/emit.h @@ -3472,9 +3472,9 @@ class emitter struct dataAsyncResumeInfo { // delegate* - void* Resume; + target_size_t Resume; // Pointer in main code where resumption eventually ends up - void* FinalResumeIP; + target_size_t FinalResumeIP; }; /* One of these is allocated for every blob of initialized data */ From d4c2b9e1616336c676903a5b13b9e690507bd650 Mon Sep 17 00:00:00 2001 From: Jakob Botsch Nielsen Date: Fri, 24 Oct 2025 11:06:37 +0200 Subject: [PATCH 53/72] Fix crossgen2 reportAsyncDebugInfo --- src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs index 84aee76e93622e..9ee93e31af0e27 100644 --- a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs +++ b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs @@ -3219,8 +3219,8 @@ private void reportRichMappings(InlineTreeNode* inlineTree, uint numInlineTree, private void reportAsyncDebugInfo(AsyncInfo* asyncInfo, AsyncSuspensionPoint* suspensionPoints, AsyncContinuationVarInfo* vars, uint numVars) #pragma warning restore CA1822 // Mark members as static { - Marshal.FreeHGlobal((IntPtr)suspensionPoints); - Marshal.FreeHGlobal((IntPtr)vars); + NativeMemory.Free(suspensionPoints); + NativeMemory.Free(vars); } #pragma warning disable CA1822 // Mark members as static From fabea2d1666c1f3b3c783974d706f9b4a85a2ccd Mon Sep 17 00:00:00 2001 From: Jakob Botsch Nielsen Date: Fri, 24 Oct 2025 20:46:50 +0200 Subject: [PATCH 54/72] Clean up, remove paranoia code --- .../Runtime/CompilerServices/AsyncHelpers.CoreCLR.cs | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/AsyncHelpers.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/AsyncHelpers.CoreCLR.cs index 353a4981fb6cd3..dd11a001b0adc5 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/AsyncHelpers.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/AsyncHelpers.CoreCLR.cs @@ -354,15 +354,10 @@ public static unsafe void DispatchContinuations(T task) where T : Task, ExecutionAndSyncBlockStore contexts = default; contexts.Push(); - ref DispatcherInfo* dispatcherInfoRef = ref t_dispatcherInfo; DispatcherInfo dispatcherInfo; - dispatcherInfo.Next = dispatcherInfoRef; + dispatcherInfo.Next = t_dispatcherInfo; dispatcherInfo.NextContinuation = TOps.GetContinuationState(task); - // Ensure JIT does not reorder the above stores with publishing - // the info, which could put the frame information in a bad - // state. - Volatile.WriteBarrier(); - dispatcherInfoRef = &dispatcherInfo; + t_dispatcherInfo = &dispatcherInfo; while (true) { From 1762f196d26ef89592f673957683e6788ec48f8a Mon Sep 17 00:00:00 2001 From: Jakob Botsch Nielsen Date: Mon, 27 Oct 2025 13:45:19 +0100 Subject: [PATCH 55/72] Point IL offsets at await call --- src/coreclr/jit/compiler.h | 2 +- src/coreclr/jit/gentree.h | 2 +- src/coreclr/jit/importer.cpp | 27 +++++++++++++++++---------- src/coreclr/jit/importercalls.cpp | 2 +- 4 files changed, 20 insertions(+), 13 deletions(-) diff --git a/src/coreclr/jit/compiler.h b/src/coreclr/jit/compiler.h index 51f838784dfed0..5f51bd68e138db 100644 --- a/src/coreclr/jit/compiler.h +++ b/src/coreclr/jit/compiler.h @@ -4871,7 +4871,7 @@ class Compiler bool impMatchIsInstBooleanConversion(const BYTE* codeAddr, const BYTE* codeEndp, int* consumed); - const BYTE* impMatchTaskAwaitPattern(const BYTE * codeAddr, const BYTE * codeEndp, int* configVal); + const BYTE* impMatchTaskAwaitPattern(const BYTE* codeAddr, const BYTE* codeEndp, int* configVal, IL_OFFSET* awaitOffset); GenTree* impCastClassOrIsInstToTree( GenTree* op1, GenTree* op2, CORINFO_RESOLVED_TOKEN* pResolvedToken, bool isCastClass, bool* booleanCheck, IL_OFFSET ilOffset); diff --git a/src/coreclr/jit/gentree.h b/src/coreclr/jit/gentree.h index 2013e0d4aa4bf1..794b6f66b09203 100644 --- a/src/coreclr/jit/gentree.h +++ b/src/coreclr/jit/gentree.h @@ -4366,7 +4366,7 @@ enum class ContinuationContextHandling // Additional async call info. struct AsyncCallInfo { - // DebugInfo with SOURCE_TYPE_ASYNC pointing at the call IL instruction + // DebugInfo with SOURCE_TYPE_ASYNC pointing at the await call IL instruction DebugInfo CallAsyncDebugInfo; // The following information is used to implement the proper observable handling of `ExecutionContext`, diff --git a/src/coreclr/jit/importer.cpp b/src/coreclr/jit/importer.cpp index dee14b3290ce6e..146672d08c4390 100644 --- a/src/coreclr/jit/importer.cpp +++ b/src/coreclr/jit/importer.cpp @@ -5984,15 +5984,19 @@ bool Compiler::impBlockIsInALoop(BasicBlock* block) // optimized for runtime async // // Arguments: -// codeAddr - IL after call[virt] NB: pointing at unconsumed token. -// codeEndp - End of IL code stream -// configVal - [out] set to 0 or 1, accordingly, if we saw ConfigureAwait(0|1) +// codeAddr - IL after call[virt] NB: pointing at unconsumed token. +// codeEndp - End of IL code stream +// configVal - [out] set to 0 or 1, accordingly, if we saw ConfigureAwait(0|1) +// awaitOffset - [out] IL offset of await call // // Returns: -// NULL if we did not recognise an Await pattern that we can optimize +// nullptr if we did not recognise an Await pattern that we can optimize // Otherwise returns position at the end of the Await pattern with one token left unconsumed. // -const BYTE* Compiler::impMatchTaskAwaitPattern(const BYTE* codeAddr, const BYTE* codeEndp, int* configVal) +const BYTE* Compiler::impMatchTaskAwaitPattern(const BYTE* codeAddr, + const BYTE* codeEndp, + int* configVal, + IL_OFFSET* awaitOffset) { // If we see the following code pattern in runtime async methods: // @@ -6138,6 +6142,7 @@ const BYTE* Compiler::impMatchTaskAwaitPattern(const BYTE* codeAddr, const BYTE* if (eeIsIntrinsic(nextCallTok.hMethod) && lookupNamedIntrinsic(nextCallTok.hMethod) == NI_System_Runtime_CompilerServices_AsyncHelpers_Await) { + *awaitOffset = (IL_OFFSET)(nextOpcode - info.compCode); // yes, this is an Await // Consume the call opcode, but not the token. // The call importer always consumes one token before moving to the next opcode. @@ -9182,15 +9187,16 @@ void Compiler::impImportBlockCode(BasicBlock* block) { bool isAwait = false; int configVal = -1; // -1 not configured, 0/1 configured to false/true - const BYTE* codeAddrAfterMatch = NULL; + const BYTE* codeAddrAfterMatch = nullptr; + IL_OFFSET awaitOffset = BAD_IL_OFFSET; #ifdef DEBUG if (compIsAsync() && JitConfig.JitOptimizeAwait()) #else if (compIsAsync()) #endif { - codeAddrAfterMatch = impMatchTaskAwaitPattern(codeAddr, codeEndp, &configVal); - if (codeAddrAfterMatch != NULL) + codeAddrAfterMatch = impMatchTaskAwaitPattern(codeAddr, codeEndp, &configVal, &awaitOffset); + if (codeAddrAfterMatch != nullptr) { isAwait = true; prefixFlags |= PREFIX_IS_TASK_AWAIT; @@ -9204,11 +9210,12 @@ void Compiler::impImportBlockCode(BasicBlock* block) if (isAwait) { _impResolveToken(CORINFO_TOKENKIND_Await); - if (resolvedToken.hMethod != NULL) + if (resolvedToken.hMethod != nullptr) { // There is a runtime async variant that is implicitly awaitable, just call that. // skip the await pattern to the last token. - codeAddr = codeAddrAfterMatch; + codeAddr = codeAddrAfterMatch; + opcodeOffs = awaitOffset; } else { diff --git a/src/coreclr/jit/importercalls.cpp b/src/coreclr/jit/importercalls.cpp index 5bcd8f2f4ee68a..e5fd8229af4d4f 100644 --- a/src/coreclr/jit/importercalls.cpp +++ b/src/coreclr/jit/importercalls.cpp @@ -13,7 +13,7 @@ // newObjThis - tree for this pointer or uninitialized newobj temp (or nullptr) // prefixFlags - IL prefix flags for the call // callInfo - EE supplied info for the call -// rawILOffset - IL offset of the opcode, used for guarded devirtualization. +// rawILOffset - IL offset of the opcode // // Returns: // Type of the call's return value. From ed3da479ae1834308a3029877eccebf5d51ff895 Mon Sep 17 00:00:00 2001 From: Jakob Botsch Nielsen Date: Mon, 27 Oct 2025 15:59:43 +0100 Subject: [PATCH 56/72] Clean ups --- src/coreclr/jit/codegenlinear.cpp | 10 ++++++++-- src/coreclr/jit/emit.h | 5 +++-- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/src/coreclr/jit/codegenlinear.cpp b/src/coreclr/jit/codegenlinear.cpp index 9c4aa63a2a297e..b873c17d7ed933 100644 --- a/src/coreclr/jit/codegenlinear.cpp +++ b/src/coreclr/jit/codegenlinear.cpp @@ -904,6 +904,13 @@ void CodeGen::genCodeForBBlist() #endif } +//------------------------------------------------------------------------ +// genRecordAsyncResume: +// Record information about an async resume point in the async resume info tabl.e +// +// Arguments: +// asyncResume - GT_RECORD_ASYNC_RESUME node +// void CodeGen::genRecordAsyncResume(GenTreeVal* asyncResume) { size_t index = asyncResume->gtVal1; @@ -913,8 +920,7 @@ void CodeGen::genRecordAsyncResume(GenTreeVal* asyncResume) emitter::dataSection* asyncResumeInfo; genEmitAsyncResumeInfoTable(&asyncResumeInfo); - BYTE* addr = asyncResumeInfo->dsCont + index * sizeof(emitLocation); - new (addr, jitstd::placement_t()) emitLocation(GetEmitter()); + ((emitLocation*)asyncResumeInfo->dsCont)[index] = emitLocation(GetEmitter()); } /* diff --git a/src/coreclr/jit/emit.h b/src/coreclr/jit/emit.h index c7ac39d628d815..1f707fffea3373 100644 --- a/src/coreclr/jit/emit.h +++ b/src/coreclr/jit/emit.h @@ -3500,8 +3500,9 @@ class emitter sectionType dsType; var_types dsDataType; - // variable-sized array used to store the constant data - // or BasicBlock* array in the block cases. + // variable-sized array used to store the constant data, BasicBlock* + // array in the block cases, or emitLocation for the asyncResumeInfo + // case. BYTE dsCont[0]; }; From 8c69bf9f1fbd62b11f155f8db788e981a9cc01c7 Mon Sep 17 00:00:00 2001 From: Jakob Botsch Nielsen Date: Mon, 27 Oct 2025 16:00:14 +0100 Subject: [PATCH 57/72] Encode async continuation layouts as part of `NativeVarInfo` --- src/coreclr/inc/cordebuginfo.h | 1 + src/coreclr/jit/codegen.h | 2 + src/coreclr/jit/scopeinfo.cpp | 75 ++++++++++++++++++++++++++++++- src/coreclr/vm/debuginfostore.cpp | 10 +++++ 4 files changed, 86 insertions(+), 2 deletions(-) diff --git a/src/coreclr/inc/cordebuginfo.h b/src/coreclr/inc/cordebuginfo.h index c4f9532ff8e699..bd5cbaccdfaa20 100644 --- a/src/coreclr/inc/cordebuginfo.h +++ b/src/coreclr/inc/cordebuginfo.h @@ -222,6 +222,7 @@ class ICorDebugInfo REGNUM_COUNT, REGNUM_AMBIENT_SP, // ambient SP support. Ambient SP is the original SP in the non-BP based frame. // Ambient SP should not change even if there are push/pop operations in the method. + REGNUM_CONTINUATION, // Represents a Continuation instance for async methods #ifdef TARGET_X86 REGNUM_FP = REGNUM_EBP, diff --git a/src/coreclr/jit/codegen.h b/src/coreclr/jit/codegen.h index 727b4688980020..4e0c8401489dc8 100644 --- a/src/coreclr/jit/codegen.h +++ b/src/coreclr/jit/codegen.h @@ -677,6 +677,8 @@ class CodeGen final : public CodeGenInterface // Send VariableLiveRanges as debug info to the debugger void genSetScopeInfoUsingVariableRanges(); + void genDescribeAsyncContinuationLayouts(ArrayStack& vars); + public: void siInit(); void checkICodeDebugInfo(); diff --git a/src/coreclr/jit/scopeinfo.cpp b/src/coreclr/jit/scopeinfo.cpp index df9b0083798d1a..ece3c5376a9315 100644 --- a/src/coreclr/jit/scopeinfo.cpp +++ b/src/coreclr/jit/scopeinfo.cpp @@ -56,6 +56,7 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX #include "emit.h" #include "codegen.h" +#include "jitstd/algorithm.h" //============================================================================ // siVarLoc functions @@ -1780,9 +1781,12 @@ void CodeGen::genSetScopeInfo() } #endif - unsigned varsLocationsCount = 0; + unsigned varsLocationsCount = (unsigned int)varLiveKeeper->getLiveRangesCount(); - varsLocationsCount = (unsigned int)varLiveKeeper->getLiveRangesCount(); + ArrayStack asyncVars(compiler->getAllocator(CMK_DebugInfo)); + genDescribeAsyncContinuationLayouts(asyncVars); + + varsLocationsCount += (unsigned)asyncVars.Height(); if (varsLocationsCount == 0) { @@ -1811,6 +1815,12 @@ void CodeGen::genSetScopeInfo() genSetScopeInfoUsingVariableRanges(); + for (int i = 0; i < asyncVars.Height(); i++) + { + assert(compiler->eeVarsCount < varsLocationsCount); + compiler->eeVars[compiler->eeVarsCount++] = asyncVars.BottomRef(i); + } + compiler->eeSetLVdone(); } @@ -1907,6 +1917,67 @@ void CodeGen::genSetScopeInfoUsingVariableRanges() compiler->eeVarsCount = liveRangeIndex; } +void CodeGen::genDescribeAsyncContinuationLayouts(ArrayStack& asyncVars) +{ + if (compiler->compSuspensionPoints == nullptr) + { + return; + } + + unsigned varIndex = 0; + for (size_t i = 0; i < compiler->compSuspensionPoints->size(); i++) + { + emitLocation& emitLoc = ((emitLocation*)genAsyncResumeInfoTable->dsCont)[i]; + UNATIVE_OFFSET resumeOffs = emitLoc.CodeOffset(GetEmitter()); + + unsigned numContVars = (*compiler->compSuspensionPoints)[i].NumContinuationVars; + for (unsigned j = 0; j < numContVars; j++) + { + ICorDebugInfo::AsyncContinuationVarInfo& asyncVar = (*compiler->compAsyncVars)[varIndex + j]; + Compiler::VarResultInfo varInfo; + varInfo.startOffset = resumeOffs; + varInfo.endOffset = resumeOffs + 1; + varInfo.varNumber = asyncVar.VarNumber; + varInfo.loc.vlType = VLT_STK; + varInfo.loc.vlStk.vlsBaseReg = (regNumber)ICorDebugInfo::REGNUM_CONTINUATION; + varInfo.loc.vlStk.vlsOffset = (int)asyncVar.Offset; + asyncVars.Push(varInfo); + } + varIndex += numContVars; + } + + auto lt = [](const Compiler::VarResultInfo& a, const Compiler::VarResultInfo& b) { + if (a.varNumber != b.varNumber) + { + return a.varNumber < b.varNumber; + } + return a.startOffset < b.startOffset; + }; + + jitstd::sort(asyncVars.Data(), asyncVars.Data() + asyncVars.Height(), lt); + + // Now coalesce subsequent entries if they have the same offset. + for (int i = 1; i < asyncVars.Height(); i++) + { + Compiler::VarResultInfo& prev = asyncVars.BottomRef(i - 1); + Compiler::VarResultInfo& cur = asyncVars.BottomRef(i); + + if ((prev.varNumber != cur.varNumber) || + (prev.loc.vlStk.vlsOffset != cur.loc.vlStk.vlsOffset)) + { + continue; + } + + // Make previous entry cover current one. + prev.endOffset = cur.endOffset; + // Remove current entry. + for (int j = i + 1; j < asyncVars.Height(); j++) + asyncVars.BottomRef(j - 1) = asyncVars.BottomRef(j); + asyncVars.Pop(); + i--; + } +} + //------------------------------------------------------------------------ // genSetScopeInfo: Record scope information for debug info // diff --git a/src/coreclr/vm/debuginfostore.cpp b/src/coreclr/vm/debuginfostore.cpp index eff248a3fb976a..3937454b5e527c 100644 --- a/src/coreclr/vm/debuginfostore.cpp +++ b/src/coreclr/vm/debuginfostore.cpp @@ -1069,6 +1069,16 @@ PTR_BYTE CompressDebugInfo::Compress( { DecompressDelete(pNewVars); } + + if (iAsyncVars > 0) + { + static DWORD s_asyncSize = 0; + static DWORD s_varsAsyncSize = 0; + s_asyncSize += cbAsyncInfo; + s_varsAsyncSize += cbVars; + + printf("Async size: %u, vars async size: %u\n", s_asyncSize, s_varsAsyncSize); + } #endif // _DEBUG return ptrStart; From 092bd67ad189f9789527dafa10c44ca2c5c3bc17 Mon Sep 17 00:00:00 2001 From: Jakob Botsch Nielsen Date: Mon, 27 Oct 2025 16:01:00 +0100 Subject: [PATCH 58/72] Revert "Encode async continuation layouts as part of `NativeVarInfo`" This reverts commit 8c69bf9f1fbd62b11f155f8db788e981a9cc01c7. --- src/coreclr/inc/cordebuginfo.h | 1 - src/coreclr/jit/codegen.h | 2 - src/coreclr/jit/scopeinfo.cpp | 75 +------------------------------ src/coreclr/vm/debuginfostore.cpp | 10 ----- 4 files changed, 2 insertions(+), 86 deletions(-) diff --git a/src/coreclr/inc/cordebuginfo.h b/src/coreclr/inc/cordebuginfo.h index bd5cbaccdfaa20..c4f9532ff8e699 100644 --- a/src/coreclr/inc/cordebuginfo.h +++ b/src/coreclr/inc/cordebuginfo.h @@ -222,7 +222,6 @@ class ICorDebugInfo REGNUM_COUNT, REGNUM_AMBIENT_SP, // ambient SP support. Ambient SP is the original SP in the non-BP based frame. // Ambient SP should not change even if there are push/pop operations in the method. - REGNUM_CONTINUATION, // Represents a Continuation instance for async methods #ifdef TARGET_X86 REGNUM_FP = REGNUM_EBP, diff --git a/src/coreclr/jit/codegen.h b/src/coreclr/jit/codegen.h index 4e0c8401489dc8..727b4688980020 100644 --- a/src/coreclr/jit/codegen.h +++ b/src/coreclr/jit/codegen.h @@ -677,8 +677,6 @@ class CodeGen final : public CodeGenInterface // Send VariableLiveRanges as debug info to the debugger void genSetScopeInfoUsingVariableRanges(); - void genDescribeAsyncContinuationLayouts(ArrayStack& vars); - public: void siInit(); void checkICodeDebugInfo(); diff --git a/src/coreclr/jit/scopeinfo.cpp b/src/coreclr/jit/scopeinfo.cpp index ece3c5376a9315..df9b0083798d1a 100644 --- a/src/coreclr/jit/scopeinfo.cpp +++ b/src/coreclr/jit/scopeinfo.cpp @@ -56,7 +56,6 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX #include "emit.h" #include "codegen.h" -#include "jitstd/algorithm.h" //============================================================================ // siVarLoc functions @@ -1781,12 +1780,9 @@ void CodeGen::genSetScopeInfo() } #endif - unsigned varsLocationsCount = (unsigned int)varLiveKeeper->getLiveRangesCount(); + unsigned varsLocationsCount = 0; - ArrayStack asyncVars(compiler->getAllocator(CMK_DebugInfo)); - genDescribeAsyncContinuationLayouts(asyncVars); - - varsLocationsCount += (unsigned)asyncVars.Height(); + varsLocationsCount = (unsigned int)varLiveKeeper->getLiveRangesCount(); if (varsLocationsCount == 0) { @@ -1815,12 +1811,6 @@ void CodeGen::genSetScopeInfo() genSetScopeInfoUsingVariableRanges(); - for (int i = 0; i < asyncVars.Height(); i++) - { - assert(compiler->eeVarsCount < varsLocationsCount); - compiler->eeVars[compiler->eeVarsCount++] = asyncVars.BottomRef(i); - } - compiler->eeSetLVdone(); } @@ -1917,67 +1907,6 @@ void CodeGen::genSetScopeInfoUsingVariableRanges() compiler->eeVarsCount = liveRangeIndex; } -void CodeGen::genDescribeAsyncContinuationLayouts(ArrayStack& asyncVars) -{ - if (compiler->compSuspensionPoints == nullptr) - { - return; - } - - unsigned varIndex = 0; - for (size_t i = 0; i < compiler->compSuspensionPoints->size(); i++) - { - emitLocation& emitLoc = ((emitLocation*)genAsyncResumeInfoTable->dsCont)[i]; - UNATIVE_OFFSET resumeOffs = emitLoc.CodeOffset(GetEmitter()); - - unsigned numContVars = (*compiler->compSuspensionPoints)[i].NumContinuationVars; - for (unsigned j = 0; j < numContVars; j++) - { - ICorDebugInfo::AsyncContinuationVarInfo& asyncVar = (*compiler->compAsyncVars)[varIndex + j]; - Compiler::VarResultInfo varInfo; - varInfo.startOffset = resumeOffs; - varInfo.endOffset = resumeOffs + 1; - varInfo.varNumber = asyncVar.VarNumber; - varInfo.loc.vlType = VLT_STK; - varInfo.loc.vlStk.vlsBaseReg = (regNumber)ICorDebugInfo::REGNUM_CONTINUATION; - varInfo.loc.vlStk.vlsOffset = (int)asyncVar.Offset; - asyncVars.Push(varInfo); - } - varIndex += numContVars; - } - - auto lt = [](const Compiler::VarResultInfo& a, const Compiler::VarResultInfo& b) { - if (a.varNumber != b.varNumber) - { - return a.varNumber < b.varNumber; - } - return a.startOffset < b.startOffset; - }; - - jitstd::sort(asyncVars.Data(), asyncVars.Data() + asyncVars.Height(), lt); - - // Now coalesce subsequent entries if they have the same offset. - for (int i = 1; i < asyncVars.Height(); i++) - { - Compiler::VarResultInfo& prev = asyncVars.BottomRef(i - 1); - Compiler::VarResultInfo& cur = asyncVars.BottomRef(i); - - if ((prev.varNumber != cur.varNumber) || - (prev.loc.vlStk.vlsOffset != cur.loc.vlStk.vlsOffset)) - { - continue; - } - - // Make previous entry cover current one. - prev.endOffset = cur.endOffset; - // Remove current entry. - for (int j = i + 1; j < asyncVars.Height(); j++) - asyncVars.BottomRef(j - 1) = asyncVars.BottomRef(j); - asyncVars.Pop(); - i--; - } -} - //------------------------------------------------------------------------ // genSetScopeInfo: Record scope information for debug info // diff --git a/src/coreclr/vm/debuginfostore.cpp b/src/coreclr/vm/debuginfostore.cpp index 3937454b5e527c..eff248a3fb976a 100644 --- a/src/coreclr/vm/debuginfostore.cpp +++ b/src/coreclr/vm/debuginfostore.cpp @@ -1069,16 +1069,6 @@ PTR_BYTE CompressDebugInfo::Compress( { DecompressDelete(pNewVars); } - - if (iAsyncVars > 0) - { - static DWORD s_asyncSize = 0; - static DWORD s_varsAsyncSize = 0; - s_asyncSize += cbAsyncInfo; - s_varsAsyncSize += cbVars; - - printf("Async size: %u, vars async size: %u\n", s_asyncSize, s_varsAsyncSize); - } #endif // _DEBUG return ptrStart; From 96b0f882bd03e9f7faf90ac8d9b2d41c1a1dcba4 Mon Sep 17 00:00:00 2001 From: Jakob Botsch Nielsen Date: Mon, 27 Oct 2025 17:38:57 +0100 Subject: [PATCH 59/72] Avoid reporting continuation parameter in debug info --- src/coreclr/jit/lclvars.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/coreclr/jit/lclvars.cpp b/src/coreclr/jit/lclvars.cpp index e05714a2e0a6d1..109afd011d8190 100644 --- a/src/coreclr/jit/lclvars.cpp +++ b/src/coreclr/jit/lclvars.cpp @@ -1128,6 +1128,11 @@ unsigned Compiler::compMap2ILvarNum(unsigned varNum) const } #endif // FEATURE_FIXED_OUT_ARGS + if (varNum == lvaAsyncContinuationArg) + { + return (unsigned)ICorDebugInfo::UNKNOWN_ILNUM; + } + // Now mutate varNum to remove extra parameters from the count. if (((info.compMethodInfo->args.callConv & CORINFO_CALLCONV_PARAMTYPE) != 0) && (varNum > info.compTypeCtxtArg)) { From fb0914440d0bb516505bf8859f1be3184f9c003e Mon Sep 17 00:00:00 2001 From: Jakob Botsch Nielsen Date: Tue, 28 Oct 2025 00:21:44 +0100 Subject: [PATCH 60/72] Add `AsyncSuspensionPoint::FinalResumeNativeOffset` To be used to key into `AsyncSuspensionPoint` given `Continuation` info --- src/coreclr/inc/cordebuginfo.h | 3 +++ src/coreclr/jit/async.cpp | 1 + src/coreclr/jit/codegencommon.cpp | 8 ++++++++ src/coreclr/vm/debuginfostore.cpp | 6 ++++-- 4 files changed, 16 insertions(+), 2 deletions(-) diff --git a/src/coreclr/inc/cordebuginfo.h b/src/coreclr/inc/cordebuginfo.h index c4f9532ff8e699..bb03f951ffab7c 100644 --- a/src/coreclr/inc/cordebuginfo.h +++ b/src/coreclr/inc/cordebuginfo.h @@ -443,6 +443,9 @@ class ICorDebugInfo struct AsyncSuspensionPoint { + // Offset of IP stored in Continuation's resume info when we would + // resume at this suspension point. + uint32_t FinalResumeNativeOffset; // Count of AsyncContinuationVarInfo in array of locals starting where // the previous suspension point's locals end. uint32_t NumContinuationVars; diff --git a/src/coreclr/jit/async.cpp b/src/coreclr/jit/async.cpp index 6cc7e243864179..0f3dad4235c968 100644 --- a/src/coreclr/jit/async.cpp +++ b/src/coreclr/jit/async.cpp @@ -2128,6 +2128,7 @@ void AsyncTransformation::CreateDebugInfoForSuspensionPoint(GenTreeCall* } ICorDebugInfo::AsyncSuspensionPoint suspensionPoint; + suspensionPoint.FinalResumeNativeOffset = 0; suspensionPoint.NumContinuationVars = numLocals; m_comp->compSuspensionPoints->push_back(suspensionPoint); diff --git a/src/coreclr/jit/codegencommon.cpp b/src/coreclr/jit/codegencommon.cpp index 7a215a580704f2..c82649368fa43d 100644 --- a/src/coreclr/jit/codegencommon.cpp +++ b/src/coreclr/jit/codegencommon.cpp @@ -6762,6 +6762,14 @@ void CodeGen::genReportAsyncDebugInfo() return; } + assert(genAsyncResumeInfoTable != nullptr); + for (size_t i = 0; i < suspPoints->size(); i++) + { + emitLocation& emitLoc = ((emitLocation*)genAsyncResumeInfoTable->dsCont)[i]; + UNATIVE_OFFSET finalResumeOffset = emitLoc.CodeOffset(GetEmitter()); + (*suspPoints)[i].FinalResumeNativeOffset = finalResumeOffset; + } + ICorDebugInfo::AsyncInfo asyncInfo; asyncInfo.NumSuspensionPoints = static_cast(suspPoints->size()); diff --git a/src/coreclr/vm/debuginfostore.cpp b/src/coreclr/vm/debuginfostore.cpp index eff248a3fb976a..0d2624666697c6 100644 --- a/src/coreclr/vm/debuginfostore.cpp +++ b/src/coreclr/vm/debuginfostore.cpp @@ -466,11 +466,13 @@ static void DoAsyncSuspensionPoints( ULONG32 cSuspensionPoints, ICorDebugInfo::AsyncSuspensionPoint* suspensionPoints) { - uint32_t lastNativeResumeOffset = 0; - uint32_t lastNativeJoinOffset = 0; + unsigned lastFinalResumeNativeOffset = 0; for (uint32_t i = 0; i < cSuspensionPoints; i++) { ICorDebugInfo::AsyncSuspensionPoint* sp = &suspensionPoints[i]; + trans.DoEncodedDeltaU32NonMonotonic(sp->FinalResumeNativeOffset, lastFinalResumeNativeOffset); + lastFinalResumeNativeOffset = sp->FinalResumeNativeOffset; + trans.DoEncodedU32(sp->NumContinuationVars); } } From 161e18042115d9b47396278ae6efef0e5bb5844d Mon Sep 17 00:00:00 2001 From: Jakob Botsch Nielsen Date: Wed, 29 Oct 2025 10:40:10 +0100 Subject: [PATCH 61/72] Further changes to ResumeInfo diagnostic IP * Use offset after call instead of the JoinIP * Rename to DiagnosticIP and clarify in contract that it is usable as a key for suspension points --- .../CompilerServices/AsyncHelpers.CoreCLR.cs | 5 +++-- src/coreclr/inc/cordebuginfo.h | 8 +++++--- src/coreclr/jit/async.cpp | 20 +++++++++---------- src/coreclr/jit/async.h | 9 +++++---- src/coreclr/jit/codegencommon.cpp | 5 ++--- src/coreclr/jit/codegenlinear.cpp | 3 --- src/coreclr/jit/emit.cpp | 8 ++++---- src/coreclr/jit/emit.h | 6 ++++-- src/coreclr/jit/gtlist.h | 2 +- src/coreclr/vm/debuginfostore.cpp | 6 +++--- 10 files changed, 37 insertions(+), 35 deletions(-) diff --git a/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/AsyncHelpers.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/AsyncHelpers.CoreCLR.cs index dd11a001b0adc5..a696bc3683481b 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/AsyncHelpers.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/AsyncHelpers.CoreCLR.cs @@ -82,8 +82,9 @@ internal enum ContinuationFlags internal unsafe struct ResumeInfo { public delegate* Resume; - // IP in final code - public void* FinalResumeIP; + // IP to use for diagnostics. Points into main code and maps to source + // in the same way as the call instruction does. + public void* DiagnosticIP; } #pragma warning disable CA1852 // "Type can be sealed" -- no it cannot because the runtime constructs subtypes dynamically diff --git a/src/coreclr/inc/cordebuginfo.h b/src/coreclr/inc/cordebuginfo.h index bb03f951ffab7c..39261d5ddc47a9 100644 --- a/src/coreclr/inc/cordebuginfo.h +++ b/src/coreclr/inc/cordebuginfo.h @@ -443,9 +443,11 @@ class ICorDebugInfo struct AsyncSuspensionPoint { - // Offset of IP stored in Continuation's resume info when we would - // resume at this suspension point. - uint32_t FinalResumeNativeOffset; + // Offset of IP stored in Continuation.DiagnosticIP. This IP maps to + // source in the same way as the call instruction does, and it can be + // used as a unique key for debug information about the suspension + // point. + uint32_t DiagnosticNativeOffset; // Count of AsyncContinuationVarInfo in array of locals starting where // the previous suspension point's locals end. uint32_t NumContinuationVars; diff --git a/src/coreclr/jit/async.cpp b/src/coreclr/jit/async.cpp index 0f3dad4235c968..7c35cd84536a1a 100644 --- a/src/coreclr/jit/async.cpp +++ b/src/coreclr/jit/async.cpp @@ -823,7 +823,7 @@ void AsyncTransformation::Transform( m_resumptionBBs.push_back(resumeBB); - CreateDebugInfoForSuspensionPoint(call, layout, *remainder); + CreateDebugInfoForSuspensionPoint(block, callDefInfo, layout); } //------------------------------------------------------------------------ @@ -2103,13 +2103,13 @@ GenTreeStoreInd* AsyncTransformation::StoreAtOffset( // Create debug info for the specific suspension point we just created. // // Parameters: -// asyncCall - Call node resulting in the suspension point -// layout - Layout of continuation -// joinBB - BB where the synchronous and resumption paths join +// asyncCallBlock - Block that has the async call +// callDefInfo - Information about the call def +// layout - Layout of continuation // -void AsyncTransformation::CreateDebugInfoForSuspensionPoint(GenTreeCall* asyncCall, - const ContinuationLayout& layout, - BasicBlock* joinBB) +void AsyncTransformation::CreateDebugInfoForSuspensionPoint(BasicBlock* asyncCallBlock, + const CallDefinitionInfo& callDefInfo, + const ContinuationLayout& layout) { uint32_t numLocals = 0; for (const LiveLocalInfo& local : layout.Locals) @@ -2128,13 +2128,13 @@ void AsyncTransformation::CreateDebugInfoForSuspensionPoint(GenTreeCall* } ICorDebugInfo::AsyncSuspensionPoint suspensionPoint; - suspensionPoint.FinalResumeNativeOffset = 0; - suspensionPoint.NumContinuationVars = numLocals; + suspensionPoint.DiagnosticNativeOffset = 0; + suspensionPoint.NumContinuationVars = numLocals; m_comp->compSuspensionPoints->push_back(suspensionPoint); GenTree* recordOffset = new (m_comp, GT_RECORD_ASYNC_RESUME) GenTreeVal(GT_RECORD_ASYNC_RESUME, TYP_VOID, (int)(m_comp->compSuspensionPoints->size() - 1)); - LIR::AsRange(joinBB).InsertAtBeginning(recordOffset); + LIR::AsRange(asyncCallBlock).InsertAfter(callDefInfo.InsertAfter, recordOffset); } // AsyncTransformation::GetResultBaseVar: diff --git a/src/coreclr/jit/async.h b/src/coreclr/jit/async.h index d857ed529da44e..6606ca3ae1ec1b 100644 --- a/src/coreclr/jit/async.h +++ b/src/coreclr/jit/async.h @@ -49,7 +49,8 @@ struct CallDefinitionInfo { GenTreeLclVarCommon* DefinitionNode = nullptr; - // Where to insert new IR for suspension checks. + // Where to insert new IR after the call in the original block, for + // suspension checks and for the async suspension for diagnostics purposes. GenTree* InsertAfter = nullptr; }; @@ -135,9 +136,9 @@ class AsyncTransformation var_types storeType, GenTreeFlags indirFlags = GTF_IND_NONFAULTING); - void CreateDebugInfoForSuspensionPoint(GenTreeCall* asyncCall, - const ContinuationLayout& layout, - BasicBlock* joinBB); + void CreateDebugInfoForSuspensionPoint(BasicBlock* asyncCallBlock, + const CallDefinitionInfo& callDefInfo, + const ContinuationLayout& layout); unsigned GetResultBaseVar(); unsigned GetExceptionVar(); diff --git a/src/coreclr/jit/codegencommon.cpp b/src/coreclr/jit/codegencommon.cpp index c82649368fa43d..c84e50103793c4 100644 --- a/src/coreclr/jit/codegencommon.cpp +++ b/src/coreclr/jit/codegencommon.cpp @@ -6765,9 +6765,8 @@ void CodeGen::genReportAsyncDebugInfo() assert(genAsyncResumeInfoTable != nullptr); for (size_t i = 0; i < suspPoints->size(); i++) { - emitLocation& emitLoc = ((emitLocation*)genAsyncResumeInfoTable->dsCont)[i]; - UNATIVE_OFFSET finalResumeOffset = emitLoc.CodeOffset(GetEmitter()); - (*suspPoints)[i].FinalResumeNativeOffset = finalResumeOffset; + emitLocation& emitLoc = ((emitLocation*)genAsyncResumeInfoTable->dsCont)[i]; + (*suspPoints)[i].DiagnosticNativeOffset = emitLoc.CodeOffset(GetEmitter()); } ICorDebugInfo::AsyncInfo asyncInfo; diff --git a/src/coreclr/jit/codegenlinear.cpp b/src/coreclr/jit/codegenlinear.cpp index b873c17d7ed933..58e55ebf13f1c4 100644 --- a/src/coreclr/jit/codegenlinear.cpp +++ b/src/coreclr/jit/codegenlinear.cpp @@ -462,9 +462,6 @@ void CodeGen::genCodeForBBlist() #endif // DEBUG } - else if (node->OperIs(GT_RECORD_ASYNC_RESUME)) - { - } genCodeForTreeNode(node); if (node->gtHasReg(compiler) && node->IsUnusedValue()) diff --git a/src/coreclr/jit/emit.cpp b/src/coreclr/jit/emit.cpp index 5d6df745d098f5..770e3b449460cc 100644 --- a/src/coreclr/jit/emit.cpp +++ b/src/coreclr/jit/emit.cpp @@ -8523,14 +8523,14 @@ void emitter::emitOutputDataSec(dataSecDsc* sec, BYTE* dst) { emitLocation* emitLoc = &((emitLocation*)dsc->dsCont)[i]; - BYTE* target = emitOffsetToPtr(emitLoc->CodeOffset(this)); - aDstRW[i].Resume = (target_size_t)(uintptr_t)emitAsyncResumeStubEntryPoint; - aDstRW[i].FinalResumeIP = (target_size_t)(uintptr_t)target; + BYTE* target = emitOffsetToPtr(emitLoc->CodeOffset(this)); + aDstRW[i].Resume = (target_size_t)(uintptr_t)emitAsyncResumeStubEntryPoint; + aDstRW[i].DiagnosticIP = (target_size_t)(uintptr_t)target; if (emitComp->opts.compReloc) { uint16_t relocType = TARGET_POINTER_SIZE == 8 ? IMAGE_REL_BASED_DIR64 : IMAGE_REL_BASED_HIGHLOW; emitRecordRelocation(&aDstRW[i].Resume, emitAsyncResumeStubEntryPoint, relocType); - emitRecordRelocation(&aDstRW[i].FinalResumeIP, target, relocType); + emitRecordRelocation(&aDstRW[i].DiagnosticIP, target, relocType); } JITDUMP(" Resume=%p, FinalResumeIP=%p\n", emitAsyncResumeStubEntryPoint, (void*)target); diff --git a/src/coreclr/jit/emit.h b/src/coreclr/jit/emit.h index 1f707fffea3373..876ae476425b4a 100644 --- a/src/coreclr/jit/emit.h +++ b/src/coreclr/jit/emit.h @@ -3473,8 +3473,10 @@ class emitter { // delegate* target_size_t Resume; - // Pointer in main code where resumption eventually ends up - target_size_t FinalResumeIP; + // Pointer in main code for diagnostics. See comments on + // ICorDebugInfo::AsyncSuspensionPoint::DiagnosticNativeOffset and + // ResumeInfo.DiagnosticIP in SPC. + target_size_t DiagnosticIP; }; /* One of these is allocated for every blob of initialized data */ diff --git a/src/coreclr/jit/gtlist.h b/src/coreclr/jit/gtlist.h index 162aaf42424585..489239d879fb0d 100644 --- a/src/coreclr/jit/gtlist.h +++ b/src/coreclr/jit/gtlist.h @@ -359,7 +359,7 @@ GTNODE(SWAP , GenTreeOp ,0,0,GTK_BINOP|GTK_NOVALUE|DBK_NOTH GTNODE(COPY , GenTreeCopyOrReload,0,0,GTK_UNOP|DBK_NOTHIR) // Copies a variable from its current location to a register that satisfies GTNODE(RELOAD , GenTreeCopyOrReload,0,0,GTK_UNOP|DBK_NOTHIR) // code generation constraints. The operand is the actual lclVar node. GTNODE(IL_OFFSET , GenTreeILOffset ,0,0,GTK_LEAF|GTK_NOVALUE|DBK_NOTHIR) // marks an IL offset for debugging purposes -GTNODE(RECORD_ASYNC_RESUME, GenTreeVal ,0,0,GTK_LEAF|GTK_NOVALUE|DBK_NOTHIR) // record native offset for an async resume point +GTNODE(RECORD_ASYNC_RESUME, GenTreeVal ,0,0,GTK_LEAF|GTK_NOVALUE|DBK_NOTHIR) // record native offset for async resumption info /*****************************************************************************/ #undef GTNODE diff --git a/src/coreclr/vm/debuginfostore.cpp b/src/coreclr/vm/debuginfostore.cpp index 0d2624666697c6..585bdabf8de52d 100644 --- a/src/coreclr/vm/debuginfostore.cpp +++ b/src/coreclr/vm/debuginfostore.cpp @@ -466,12 +466,12 @@ static void DoAsyncSuspensionPoints( ULONG32 cSuspensionPoints, ICorDebugInfo::AsyncSuspensionPoint* suspensionPoints) { - unsigned lastFinalResumeNativeOffset = 0; + unsigned lastDiagnosticNativeOffset = 0; for (uint32_t i = 0; i < cSuspensionPoints; i++) { ICorDebugInfo::AsyncSuspensionPoint* sp = &suspensionPoints[i]; - trans.DoEncodedDeltaU32NonMonotonic(sp->FinalResumeNativeOffset, lastFinalResumeNativeOffset); - lastFinalResumeNativeOffset = sp->FinalResumeNativeOffset; + trans.DoEncodedDeltaU32NonMonotonic(sp->DiagnosticNativeOffset, lastDiagnosticNativeOffset); + lastDiagnosticNativeOffset = sp->DiagnosticNativeOffset; trans.DoEncodedU32(sp->NumContinuationVars); } From a5a2e1677ca15f2417f3a84aa773f49d3a177e94 Mon Sep 17 00:00:00 2001 From: Jakob Botsch Nielsen Date: Wed, 29 Oct 2025 11:22:13 +0100 Subject: [PATCH 62/72] Avoid marking async mappings as label mappings --- src/coreclr/jit/codegencommon.cpp | 2 +- src/coreclr/jit/codegenlinear.cpp | 16 +++++++++++----- src/coreclr/jit/debuginfo.h | 5 +++++ 3 files changed, 17 insertions(+), 6 deletions(-) diff --git a/src/coreclr/jit/codegencommon.cpp b/src/coreclr/jit/codegencommon.cpp index c84e50103793c4..c4c9b18f783c0c 100644 --- a/src/coreclr/jit/codegencommon.cpp +++ b/src/coreclr/jit/codegencommon.cpp @@ -6441,7 +6441,7 @@ void CodeGen::genIPmappingGen() } // Otherwise report the higher offset unless the previous mapping is a - // label. + // label coming from IL. if (prev->ipmdIsLabel) { it = compiler->genIPmappings.erase(it); diff --git a/src/coreclr/jit/codegenlinear.cpp b/src/coreclr/jit/codegenlinear.cpp index 58e55ebf13f1c4..aef05793b2d032 100644 --- a/src/coreclr/jit/codegenlinear.cpp +++ b/src/coreclr/jit/codegenlinear.cpp @@ -368,8 +368,6 @@ void CodeGen::genCodeForBBlist() genIPmappingAdd(IPmappingDscKind::NoMapping, DebugInfo(), true); } - bool firstMapping = true; - if (compiler->bbIsFuncletBeg(block)) { genUpdateCurrentFunclet(block); @@ -422,7 +420,8 @@ void CodeGen::genCodeForBBlist() } #endif // DEBUG - bool addRichMappings = JitConfig.RichDebugInfo() != 0; + bool producedLabelMapping = false; + bool addRichMappings = JitConfig.RichDebugInfo() != 0; INDEBUG(addRichMappings |= JitConfig.JitDisasmWithDebugInfo() != 0); INDEBUG(addRichMappings |= JitConfig.WriteRichDebugInfoFile() != nullptr); @@ -439,8 +438,15 @@ void CodeGen::genCodeForBBlist() { genEnsureCodeEmitted(currentDI); currentDI = rootDI; - genIPmappingAdd(IPmappingDscKind::Normal, currentDI, firstMapping); - firstMapping = false; + + // We need a tie breaker when we have multiple IL offsets that map to the same native offset. + // Normally we pick the latest, but for block joins we pick the earliest to ensure end up with + // a mapping to that IL offset. Async mappings should not participate in this -- they are + // internally produced and never fall on the join point in the IL. + // See + bool isLabel = !producedLabelMapping && !currentDI.GetLocation().IsAsync(); + genIPmappingAdd(IPmappingDscKind::Normal, currentDI, isLabel); + producedLabelMapping |= isLabel; } if (addRichMappings && ilOffset->gtStmtDI.IsValid()) diff --git a/src/coreclr/jit/debuginfo.h b/src/coreclr/jit/debuginfo.h index 3db39fa73cee20..39ab3e9a6bd07c 100644 --- a/src/coreclr/jit/debuginfo.h +++ b/src/coreclr/jit/debuginfo.h @@ -37,6 +37,11 @@ class ILLocation return (m_sourceTypes & ICorDebugInfo::CALL_INSTRUCTION) != 0; } + bool IsAsync() const + { + return (m_sourceTypes & ICorDebugInfo::ASYNC) != 0; + } + bool IsValid() const { return m_offset != BAD_IL_OFFSET; From f4f7fff04cec6d554e4db5416d495e043b9d3b90 Mon Sep 17 00:00:00 2001 From: Jakob Botsch Nielsen Date: Wed, 29 Oct 2025 11:23:22 +0100 Subject: [PATCH 63/72] Fix comment --- src/coreclr/jit/codegenlinear.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coreclr/jit/codegenlinear.cpp b/src/coreclr/jit/codegenlinear.cpp index aef05793b2d032..b5b978cfd99fab 100644 --- a/src/coreclr/jit/codegenlinear.cpp +++ b/src/coreclr/jit/codegenlinear.cpp @@ -443,7 +443,7 @@ void CodeGen::genCodeForBBlist() // Normally we pick the latest, but for block joins we pick the earliest to ensure end up with // a mapping to that IL offset. Async mappings should not participate in this -- they are // internally produced and never fall on the join point in the IL. - // See + // See genIPmappingGen for the tiebreaker. bool isLabel = !producedLabelMapping && !currentDI.GetLocation().IsAsync(); genIPmappingAdd(IPmappingDscKind::Normal, currentDI, isLabel); producedLabelMapping |= isLabel; From 7b8b9b0793237c8517e200f7a13ee5d19541113c Mon Sep 17 00:00:00 2001 From: Jakob Botsch Nielsen Date: Fri, 31 Oct 2025 11:52:53 +0100 Subject: [PATCH 64/72] Point DiagnosticIP at suspension code --- .../CompilerServices/AsyncHelpers.CoreCLR.cs | 4 ++-- src/coreclr/inc/cordebuginfo.h | 6 +++--- src/coreclr/jit/async.cpp | 15 +++++---------- src/coreclr/jit/async.h | 4 +--- 4 files changed, 11 insertions(+), 18 deletions(-) diff --git a/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/AsyncHelpers.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/AsyncHelpers.CoreCLR.cs index a696bc3683481b..dffef0096413a3 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/AsyncHelpers.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/AsyncHelpers.CoreCLR.cs @@ -82,8 +82,8 @@ internal enum ContinuationFlags internal unsafe struct ResumeInfo { public delegate* Resume; - // IP to use for diagnostics. Points into main code and maps to source - // in the same way as the call instruction does. + // IP to use for diagnostics. Points into main code and maps to the + // call that resulted in suspension. public void* DiagnosticIP; } diff --git a/src/coreclr/inc/cordebuginfo.h b/src/coreclr/inc/cordebuginfo.h index 39261d5ddc47a9..41f861e1cbe0f5 100644 --- a/src/coreclr/inc/cordebuginfo.h +++ b/src/coreclr/inc/cordebuginfo.h @@ -444,9 +444,9 @@ class ICorDebugInfo struct AsyncSuspensionPoint { // Offset of IP stored in Continuation.DiagnosticIP. This IP maps to - // source in the same way as the call instruction does, and it can be - // used as a unique key for debug information about the suspension - // point. + // the IL call that resulted in the suspension point through an ASYNC + // mapping. Also used as a unique key for debug information about the + // suspension point. uint32_t DiagnosticNativeOffset; // Count of AsyncContinuationVarInfo in array of locals starting where // the previous suspension point's locals end. diff --git a/src/coreclr/jit/async.cpp b/src/coreclr/jit/async.cpp index 7c35cd84536a1a..b2a40386254298 100644 --- a/src/coreclr/jit/async.cpp +++ b/src/coreclr/jit/async.cpp @@ -823,7 +823,7 @@ void AsyncTransformation::Transform( m_resumptionBBs.push_back(resumeBB); - CreateDebugInfoForSuspensionPoint(block, callDefInfo, layout); + CreateDebugInfoForSuspensionPoint(layout); } //------------------------------------------------------------------------ @@ -1421,6 +1421,9 @@ BasicBlock* AsyncTransformation::CreateSuspension( LIR::AsRange(suspendBB).InsertAtEnd(LIR::SeqTree(m_comp, ilOffsetNode)); + GenTree* recordOffset = new (m_comp, GT_RECORD_ASYNC_RESUME) GenTreeVal(GT_RECORD_ASYNC_RESUME, TYP_VOID, stateNum); + LIR::AsRange(suspendBB).InsertAtEnd(recordOffset); + // Allocate continuation GenTree* returnedContinuation = m_comp->gtNewLclvNode(m_returnedContinuationVar, TYP_REF); @@ -2103,13 +2106,9 @@ GenTreeStoreInd* AsyncTransformation::StoreAtOffset( // Create debug info for the specific suspension point we just created. // // Parameters: -// asyncCallBlock - Block that has the async call -// callDefInfo - Information about the call def // layout - Layout of continuation // -void AsyncTransformation::CreateDebugInfoForSuspensionPoint(BasicBlock* asyncCallBlock, - const CallDefinitionInfo& callDefInfo, - const ContinuationLayout& layout) +void AsyncTransformation::CreateDebugInfoForSuspensionPoint(const ContinuationLayout& layout) { uint32_t numLocals = 0; for (const LiveLocalInfo& local : layout.Locals) @@ -2131,10 +2130,6 @@ void AsyncTransformation::CreateDebugInfoForSuspensionPoint(BasicBlock* suspensionPoint.DiagnosticNativeOffset = 0; suspensionPoint.NumContinuationVars = numLocals; m_comp->compSuspensionPoints->push_back(suspensionPoint); - - GenTree* recordOffset = new (m_comp, GT_RECORD_ASYNC_RESUME) - GenTreeVal(GT_RECORD_ASYNC_RESUME, TYP_VOID, (int)(m_comp->compSuspensionPoints->size() - 1)); - LIR::AsRange(asyncCallBlock).InsertAfter(callDefInfo.InsertAfter, recordOffset); } // AsyncTransformation::GetResultBaseVar: diff --git a/src/coreclr/jit/async.h b/src/coreclr/jit/async.h index 6606ca3ae1ec1b..13621666a0163b 100644 --- a/src/coreclr/jit/async.h +++ b/src/coreclr/jit/async.h @@ -136,9 +136,7 @@ class AsyncTransformation var_types storeType, GenTreeFlags indirFlags = GTF_IND_NONFAULTING); - void CreateDebugInfoForSuspensionPoint(BasicBlock* asyncCallBlock, - const CallDefinitionInfo& callDefInfo, - const ContinuationLayout& layout); + void CreateDebugInfoForSuspensionPoint(const ContinuationLayout& layout); unsigned GetResultBaseVar(); unsigned GetExceptionVar(); From e9cef1cd50aae3d64a2be37a67959d1fd01466bb Mon Sep 17 00:00:00 2001 From: Jakob Botsch Nielsen Date: Mon, 3 Nov 2025 13:01:40 +0100 Subject: [PATCH 65/72] Update comment --- .../Runtime/CompilerServices/AsyncHelpers.CoreCLR.cs | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/AsyncHelpers.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/AsyncHelpers.CoreCLR.cs index dffef0096413a3..5dccd8cf1370a1 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/AsyncHelpers.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/AsyncHelpers.CoreCLR.cs @@ -82,8 +82,15 @@ internal enum ContinuationFlags internal unsafe struct ResumeInfo { public delegate* Resume; - // IP to use for diagnostics. Points into main code and maps to the - // call that resulted in suspension. + // IP to use for diagnostics. Points into the jitted suspension code. + // For debug codegen the IP resolves via an ASYNC native->IL mapping to + // the IL AsyncHelpers.Await (or other async function) call which + // caused the suspension. + // For optimized codegen the mapping into the root method may be more + // approximate (e.g. because of inlining). + // For all codegen (DiagnosticsIP - MethodStartIP) matches + // DiagnosticNativeOffset for the corresponding AsyncSuspensionPoint in + // the debug info. public void* DiagnosticIP; } From bc2f7807623c51ae2a50740dbde1561b28169ea4 Mon Sep 17 00:00:00 2001 From: Jakob Botsch Nielsen Date: Mon, 3 Nov 2025 16:13:33 +0100 Subject: [PATCH 66/72] Set proper IL offset ranges when splitting user code for async calls --- src/coreclr/jit/async.cpp | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/coreclr/jit/async.cpp b/src/coreclr/jit/async.cpp index b2a40386254298..f1eb605e1e951c 100644 --- a/src/coreclr/jit/async.cpp +++ b/src/coreclr/jit/async.cpp @@ -1713,6 +1713,20 @@ void AsyncTransformation::CreateCheckAndSuspendAfterCall(BasicBlock* *remainder = m_comp->fgSplitBlockAfterNode(block, jtrue); JITDUMP(" Remainder is " FMT_BB "\n", (*remainder)->bbNum); + // For non-inlined calls adjust offset for the split. We have the exact + // offset of the await call, so we can do better than + // fgSplitBlockAfterNode. The previous block contains the call so add 1 to + // include its start offset (the IL offsets are only used for range checks + // in the backend, so having the offset be inside an IL instruction is ok.) + DebugInfo di = call->GetAsyncInfo().CallAsyncDebugInfo.GetRoot(); + DebugInfo par; + if (!di.GetParent(&par)) + { + IL_OFFSET awaitOffset = di.GetLocation().GetOffset(); + block->bbCodeOffsEnd = awaitOffset + 1; + (*remainder)->bbCodeOffs = awaitOffset + 1; + } + FlowEdge* retBBEdge = m_comp->fgAddRefPred(suspendBB, block); block->SetCond(retBBEdge, block->GetTargetEdge()); From ea96acc6d865d7ae6f43fed457552e3b2654ec36 Mon Sep 17 00:00:00 2001 From: Jakob Botsch Nielsen Date: Mon, 3 Nov 2025 16:13:42 +0100 Subject: [PATCH 67/72] Address feedback --- .../System/Runtime/CompilerServices/AsyncHelpers.CoreCLR.cs | 2 +- src/coreclr/inc/cordebuginfo.h | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/AsyncHelpers.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/AsyncHelpers.CoreCLR.cs index 5dccd8cf1370a1..8a7db5c09bf42f 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/AsyncHelpers.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/AsyncHelpers.CoreCLR.cs @@ -88,7 +88,7 @@ internal unsafe struct ResumeInfo // caused the suspension. // For optimized codegen the mapping into the root method may be more // approximate (e.g. because of inlining). - // For all codegen (DiagnosticsIP - MethodStartIP) matches + // For all codegens the offset of DiagnosticsIP matches // DiagnosticNativeOffset for the corresponding AsyncSuspensionPoint in // the debug info. public void* DiagnosticIP; diff --git a/src/coreclr/inc/cordebuginfo.h b/src/coreclr/inc/cordebuginfo.h index 41f861e1cbe0f5..914cda4258e9e2 100644 --- a/src/coreclr/inc/cordebuginfo.h +++ b/src/coreclr/inc/cordebuginfo.h @@ -443,10 +443,10 @@ class ICorDebugInfo struct AsyncSuspensionPoint { - // Offset of IP stored in Continuation.DiagnosticIP. This IP maps to + // Offset of IP stored in ResumeInfo.DiagnosticIP. This offset maps to // the IL call that resulted in the suspension point through an ASYNC // mapping. Also used as a unique key for debug information about the - // suspension point. + // suspension point. See ResumeInfo.DiagnosticIP in SPC for more info. uint32_t DiagnosticNativeOffset; // Count of AsyncContinuationVarInfo in array of locals starting where // the previous suspension point's locals end. From 959be114703b4a14fffead46bce309d1ba77fa29 Mon Sep 17 00:00:00 2001 From: Jakob Botsch Nielsen Date: Tue, 4 Nov 2025 12:18:23 +0100 Subject: [PATCH 68/72] Make `DispatcherInfo` have explicit layout --- .../Runtime/CompilerServices/AsyncHelpers.CoreCLR.cs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/AsyncHelpers.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/AsyncHelpers.CoreCLR.cs index 8a7db5c09bf42f..600a6722f0645d 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/AsyncHelpers.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/AsyncHelpers.CoreCLR.cs @@ -343,12 +343,20 @@ public static void PostToSyncContext(RuntimeAsyncTask task, SynchronizationConte private static class RuntimeAsyncTaskCore { + [StructLayout(LayoutKind.Explicit)] private unsafe ref struct DispatcherInfo { // Dispatcher info for next dispatcher present on stack, or // null if none. + [FieldOffset(0)] public DispatcherInfo* Next; + // Next continuation the dispatcher will process. +#if TARGET_64BIT + [FieldOffset(8)] +#else + [FieldOffset(4)] +#endif public Continuation? NextContinuation; } From 4061420f724507d16406685f75c5b13ce39b9ba9 Mon Sep 17 00:00:00 2001 From: Jakob Botsch Nielsen Date: Tue, 4 Nov 2025 16:50:33 +0100 Subject: [PATCH 69/72] Address some feedback, add a missing field --- src/coreclr/jit/importer.cpp | 5 +++++ src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/src/coreclr/jit/importer.cpp b/src/coreclr/jit/importer.cpp index 146672d08c4390..c473b11a947508 100644 --- a/src/coreclr/jit/importer.cpp +++ b/src/coreclr/jit/importer.cpp @@ -2175,10 +2175,15 @@ DebugInfo Compiler::impCreateDIWithCurrentStackInfo(IL_OFFSET offs, bool isCall) assert(offs != BAD_IL_OFFSET); unsigned sourceTypes = 0; + if (isCall) + { sourceTypes |= ICorDebugInfo::CALL_INSTRUCTION; + } if (stackState.esStackDepth <= 0) + { sourceTypes |= ICorDebugInfo::STACK_EMPTY; + } return DebugInfo(compInlineContext, ILLocation(offs, (ICorDebugInfo::SourceTypes)sourceTypes)); } diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs b/src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs index b4fc8cc0c6a160..29f4dd4c82b75b 100644 --- a/src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs +++ b/src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs @@ -1339,6 +1339,11 @@ public struct AsyncContinuationVarInfo public struct AsyncSuspensionPoint { + // Offset of IP stored in ResumeInfo.DiagnosticIP. This offset maps to + // the IL call that resulted in the suspension point through an ASYNC + // mapping. Also used as a unique key for debug information about the + // suspension point. See ResumeInfo.DiagnosticIP in SPC for more info. + public uint DiagnosticNativeOffset; // Count of AsyncContinuationVarInfo in array of locals starting where // the previous suspension point's locals end. public uint NumContinuationVars; From 54bab6d1400db41e16c9c3cb4a3f6b1cbccf0c2b Mon Sep 17 00:00:00 2001 From: Jakob Botsch Nielsen Date: Tue, 4 Nov 2025 16:59:52 +0100 Subject: [PATCH 70/72] Fix a comment --- src/coreclr/vm/codeman.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coreclr/vm/codeman.cpp b/src/coreclr/vm/codeman.cpp index ebe3d0c717ba94..8223718ed31c55 100644 --- a/src/coreclr/vm/codeman.cpp +++ b/src/coreclr/vm/codeman.cpp @@ -6568,7 +6568,7 @@ BOOL ReadyToRunJitManager::GetAsyncDebugInfo( { CONTRACTL { THROWS; // on OOM. - GC_NOTRIGGER; // getting vars shouldn't trigger + GC_NOTRIGGER; // getting async debug info shouldn't trigger SUPPORTS_DAC; } CONTRACTL_END; From c84f027603cc9e09253d4221a38ea7aa07d186b9 Mon Sep 17 00:00:00 2001 From: Jakob Botsch Nielsen Date: Wed, 5 Nov 2025 14:24:09 +0100 Subject: [PATCH 71/72] Fix r2rdump reading of bounds --- .../DebugInfo.cs | 30 ++++++++----------- .../DebugInfoTypes.cs | 6 +++- src/coreclr/tools/aot/crossgen2.slnx | 3 ++ src/coreclr/tools/r2rdump/R2RDump.slnx | 3 ++ 4 files changed, 23 insertions(+), 19 deletions(-) diff --git a/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/DebugInfo.cs b/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/DebugInfo.cs index e17638bcb36b23..d2c0e7af16732f 100644 --- a/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/DebugInfo.cs +++ b/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/DebugInfo.cs @@ -163,7 +163,8 @@ private void ParseBounds(NativeReader imageReader, int offset) // - IL offsets aren't sorted // They may also include a sentinel value from MappingTypes. // - flags is 3 independent bits. - if (_runtimeFunction.ReadyToRunReader.ReadyToRunHeader.MajorVersion >= 16) + int version = _runtimeFunction.ReadyToRunReader.ReadyToRunHeader.MajorVersion; + if (version >= 16) { NibbleReader reader = new NibbleReader(imageReader, offset); uint boundsEntryCount = reader.ReadUInt(); @@ -171,7 +172,8 @@ private void ParseBounds(NativeReader imageReader, int offset) uint bitsForNativeDelta = reader.ReadUInt() + 1; // Number of bits needed for native deltas uint bitsForILOffsets = reader.ReadUInt() + 1; // Number of bits needed for IL offsets - uint bitsPerEntry = bitsForNativeDelta + bitsForILOffsets + 2; // 2 bits for source type + uint bitsForSourceType = version >= 17 ? 3u : 2u; + uint bitsPerEntry = bitsForNativeDelta + bitsForILOffsets + bitsForSourceType; ulong bitsMeaningfulMask = (1UL << ((int)bitsPerEntry)) - 1; int offsetOfActualBoundsData = reader.GetNextByteOffset(); @@ -192,22 +194,14 @@ private void ParseBounds(NativeReader imageReader, int offset) bitsCollected -= bitsPerEntry; var entry = new DebugInfoBoundsEntry(); - switch (mappingDataEncoded & 0x3) - { - case 0: - entry.SourceTypes = SourceTypes.SourceTypeInvalid; - break; - case 1: - entry.SourceTypes = SourceTypes.CallInstruction; - break; - case 2: - entry.SourceTypes = SourceTypes.StackEmpty; - break; - case 3: - entry.SourceTypes = SourceTypes.StackEmpty | SourceTypes.CallInstruction; - break; - } - mappingDataEncoded >>= 2; + if ((mappingDataEncoded & 0x1) != 0) + entry.SourceTypes |= SourceTypes.CallInstruction; + if ((mappingDataEncoded & 0x2) != 0) + entry.SourceTypes |= SourceTypes.StackEmpty; + if (version >= 17 && (mappingDataEncoded & 0x4) != 0) + entry.SourceTypes |= SourceTypes.Async; + + mappingDataEncoded >>= (int)bitsForSourceType; uint nativeOffsetDelta = (uint)(mappingDataEncoded & ((1UL << (int)bitsForNativeDelta) - 1)); previousNativeOffset += nativeOffsetDelta; entry.NativeOffset = previousNativeOffset; diff --git a/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/DebugInfoTypes.cs b/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/DebugInfoTypes.cs index eef0ab4673cbe1..9232c0000a69d0 100644 --- a/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/DebugInfoTypes.cs +++ b/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/DebugInfoTypes.cs @@ -62,7 +62,11 @@ public enum SourceTypes /// /// The actual instruction of a call /// - CallInstruction = 0x10 + CallInstruction = 0x10, + /// + /// Suspension or resumption code for a call + /// + Async = 0x20, } public enum DebugInfoBoundsType : uint diff --git a/src/coreclr/tools/aot/crossgen2.slnx b/src/coreclr/tools/aot/crossgen2.slnx index bc39e9cb4ffee7..f404d3a841f868 100644 --- a/src/coreclr/tools/aot/crossgen2.slnx +++ b/src/coreclr/tools/aot/crossgen2.slnx @@ -7,6 +7,9 @@ + + + diff --git a/src/coreclr/tools/r2rdump/R2RDump.slnx b/src/coreclr/tools/r2rdump/R2RDump.slnx index 87cdcb67bd45c1..53952f61cdd334 100644 --- a/src/coreclr/tools/r2rdump/R2RDump.slnx +++ b/src/coreclr/tools/r2rdump/R2RDump.slnx @@ -7,6 +7,9 @@ + + + From 9f497bdcad0c33f37df387982718e38eb7661284 Mon Sep 17 00:00:00 2001 From: Jakob Botsch Nielsen Date: Wed, 5 Nov 2025 14:25:20 +0100 Subject: [PATCH 72/72] Bump debug info contract version --- src/coreclr/vm/datadescriptor/datadescriptor.inc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coreclr/vm/datadescriptor/datadescriptor.inc b/src/coreclr/vm/datadescriptor/datadescriptor.inc index e84411a6fc338b..298fb468f0daf0 100644 --- a/src/coreclr/vm/datadescriptor/datadescriptor.inc +++ b/src/coreclr/vm/datadescriptor/datadescriptor.inc @@ -1072,7 +1072,7 @@ CDAC_GLOBAL_CONTRACT(CodeVersions, 1) CDAC_GLOBAL_CONTRACT(ComWrappers, 1) #endif // FEATURE_COMWRAPPERS CDAC_GLOBAL_CONTRACT(DacStreams, 1) -CDAC_GLOBAL_CONTRACT(DebugInfo, 1) +CDAC_GLOBAL_CONTRACT(DebugInfo, 2) CDAC_GLOBAL_CONTRACT(EcmaMetadata, 1) CDAC_GLOBAL_CONTRACT(Exception, 1) CDAC_GLOBAL_CONTRACT(ExecutionManager, 2)