From 99f405d410d10926e28fe441ab649d378597db5f Mon Sep 17 00:00:00 2001 From: David Wrighton Date: Wed, 3 Jun 2026 15:18:56 -0700 Subject: [PATCH 01/19] Make incorrect PerfMap usage trigger contract failures on Windows (where Contracts are supported) --- src/coreclr/vm/amd64/cgenamd64.cpp | 8 ---- src/coreclr/vm/arm/stubs.cpp | 6 --- src/coreclr/vm/arm64/stubs.cpp | 6 --- src/coreclr/vm/ceemain.cpp | 2 - src/coreclr/vm/codeman.cpp | 4 -- src/coreclr/vm/codeman.h | 6 +++ src/coreclr/vm/dllimportcallback.cpp | 4 -- .../vm/eventing/eventpipe/ds-rt-coreclr.h | 2 - src/coreclr/vm/i386/cgenx86.cpp | 6 --- src/coreclr/vm/jitinterface.cpp | 2 - src/coreclr/vm/loongarch64/stubs.cpp | 6 --- src/coreclr/vm/perfmap.h | 47 +++++++++++++++++++ src/coreclr/vm/precode.cpp | 10 ---- src/coreclr/vm/prestub.cpp | 6 --- src/coreclr/vm/readytoruninfo.cpp | 4 -- src/coreclr/vm/riscv64/stubs.cpp | 6 --- src/coreclr/vm/stublink.cpp | 4 -- src/coreclr/vm/threads.cpp | 4 -- src/coreclr/vm/virtualcallstub.cpp | 12 ----- 19 files changed, 53 insertions(+), 92 deletions(-) diff --git a/src/coreclr/vm/amd64/cgenamd64.cpp b/src/coreclr/vm/amd64/cgenamd64.cpp index 70ebf04fa7fc1c..5d2bf853b51755 100644 --- a/src/coreclr/vm/amd64/cgenamd64.cpp +++ b/src/coreclr/vm/amd64/cgenamd64.cpp @@ -24,9 +24,7 @@ #include "clrtocomcall.h" #endif // FEATURE_COMINTEROP -#ifdef FEATURE_PERFMAP #include "perfmap.h" -#endif void UpdateRegDisplayFromCalleeSavedRegisters(REGDISPLAY * pRD, CalleeSavedRegisters * pRegs) { @@ -607,17 +605,11 @@ DWORD GetOffsetAtEndOfFunction(ULONGLONG uImageBase, size_t rxOffset = pStartRX - pStart; \ BYTE * p = pStart; -#ifdef FEATURE_PERFMAP #define BEGIN_DYNAMIC_HELPER_EMIT(size) \ BEGIN_DYNAMIC_HELPER_EMIT_WORKER(size) \ PerfMap::LogStubs(__FUNCTION__, "DynamicHelper", (PCODE)p, size, PerfMapStubType::Individual); -#else -#define BEGIN_DYNAMIC_HELPER_EMIT(size) BEGIN_DYNAMIC_HELPER_EMIT_WORKER(size) -#endif #define END_DYNAMIC_HELPER_EMIT() \ - _ASSERTE(pStart + cb == p); \ - while (p < pStart + cbAligned) *p++ = X86_INSTR_INT3; \ ClrFlushInstructionCache(pStartRX, cbAligned); \ return (PCODE)pStartRX diff --git a/src/coreclr/vm/arm/stubs.cpp b/src/coreclr/vm/arm/stubs.cpp index 815ab68867a005..5d1f5d2ad60633 100644 --- a/src/coreclr/vm/arm/stubs.cpp +++ b/src/coreclr/vm/arm/stubs.cpp @@ -25,9 +25,7 @@ #include "ecall.h" #include "threadsuspend.h" -#ifdef FEATURE_PERFMAP #include "perfmap.h" -#endif // target write barriers EXTERN_C void JIT_WriteBarrier(Object **dst, Object *ref); @@ -1505,13 +1503,9 @@ void MovRegImm(BYTE* p, int reg, TADDR imm) size_t rxOffset = pStartRX - pStart; \ BYTE * p = pStart; -#ifdef FEATURE_PERFMAP #define BEGIN_DYNAMIC_HELPER_EMIT(size) \ BEGIN_DYNAMIC_HELPER_EMIT_WORKER(size) \ PerfMap::LogStubs(__FUNCTION__, "DynamicHelper", (PCODE)p, size, PerfMapStubType::Individual); -#else -#define BEGIN_DYNAMIC_HELPER_EMIT(size) BEGIN_DYNAMIC_HELPER_EMIT_WORKER(size) -#endif #define END_DYNAMIC_HELPER_EMIT() \ diff --git a/src/coreclr/vm/arm64/stubs.cpp b/src/coreclr/vm/arm64/stubs.cpp index ba9a29ff4bcde5..0e123c42e7ed9f 100644 --- a/src/coreclr/vm/arm64/stubs.cpp +++ b/src/coreclr/vm/arm64/stubs.cpp @@ -13,9 +13,7 @@ #include "ecall.h" #include "writebarriermanager.h" -#ifdef FEATURE_PERFMAP #include "perfmap.h" -#endif #ifndef DACCESS_COMPILE //----------------------------------------------------------------------- @@ -951,13 +949,9 @@ void StubLinkerCPU::EmitCallManagedMethod(MethodDesc *pMD, BOOL fTailCall) size_t rxOffset = pStartRX - pStart; \ BYTE * p = pStart; -#ifdef FEATURE_PERFMAP #define BEGIN_DYNAMIC_HELPER_EMIT(size) \ BEGIN_DYNAMIC_HELPER_EMIT_WORKER(size) \ PerfMap::LogStubs(__FUNCTION__, "DynamicHelper", (PCODE)p, size, PerfMapStubType::Individual); -#else -#define BEGIN_DYNAMIC_HELPER_EMIT(size) BEGIN_DYNAMIC_HELPER_EMIT_WORKER(size) -#endif #define END_DYNAMIC_HELPER_EMIT() \ diff --git a/src/coreclr/vm/ceemain.cpp b/src/coreclr/vm/ceemain.cpp index 70867dd457e08c..a5239863d6da44 100644 --- a/src/coreclr/vm/ceemain.cpp +++ b/src/coreclr/vm/ceemain.cpp @@ -194,9 +194,7 @@ #include "profilinghelper.h" #endif // PROFILING_SUPPORTED -#ifdef FEATURE_PERFMAP #include "perfmap.h" -#endif #include "diagnosticserveradapter.h" #include "eventpipeadapter.h" diff --git a/src/coreclr/vm/codeman.cpp b/src/coreclr/vm/codeman.cpp index 0ae31afb30ebef..24abe374d3fffe 100644 --- a/src/coreclr/vm/codeman.cpp +++ b/src/coreclr/vm/codeman.cpp @@ -41,9 +41,7 @@ #include "../debug/daccess/fntableaccess.h" #endif // HOST_64BIT -#ifdef FEATURE_PERFMAP #include "perfmap.h" -#endif // Default number of jump stubs in a jump stub block #define DEFAULT_JUMPSTUBS_PER_BLOCK 32 @@ -6237,9 +6235,7 @@ PCODE ExecutionManager::getNextJumpStub(MethodDesc* pMD, PCODE target, emitBackToBackJump(jumpStub, jumpStubRW, (void*) target); -#ifdef FEATURE_PERFMAP PerfMap::LogStubs(__FUNCTION__, "emitBackToBackJump", (PCODE)jumpStub, BACK_TO_BACK_JUMP_ALLOCATE_SIZE, PerfMapStubType::IndividualWithinBlock); -#endif // We always add the new jumpstub to the jumpStubCache // diff --git a/src/coreclr/vm/codeman.h b/src/coreclr/vm/codeman.h index dca673f271c172..8ec4e541a5c6e7 100644 --- a/src/coreclr/vm/codeman.h +++ b/src/coreclr/vm/codeman.h @@ -152,6 +152,12 @@ void ReportStubBlock(void* start, size_t size, StubCodeBlockKind kind); #ifndef FEATURE_PERFMAP inline void ReportStubBlock(void* start, size_t size, StubCodeBlockKind kind) { + CONTRACTL + { + GC_NOTRIGGER; + MODE_PREEMPTIVE; + } + CONTRACTL_END; } #endif diff --git a/src/coreclr/vm/dllimportcallback.cpp b/src/coreclr/vm/dllimportcallback.cpp index e4cb9206c9160f..7113a6779469d5 100644 --- a/src/coreclr/vm/dllimportcallback.cpp +++ b/src/coreclr/vm/dllimportcallback.cpp @@ -19,9 +19,7 @@ #include "stubgen.h" #include "appdomain.inl" -#ifdef FEATURE_PERFMAP #include "perfmap.h" -#endif class UMEntryThunkFreeList { @@ -293,9 +291,7 @@ UMEntryThunkData* UMEntryThunkData::CreateUMEntryThunk() #else // !FEATURE_PORTABLE_ENTRYPOINTS pThunk = (UMEntryThunk*)pamTracker->Track(pLoaderAllocator->GetNewStubPrecodeHeap()->AllocStub()); #endif // FEATURE_PORTABLE_ENTRYPOINTS -#ifdef FEATURE_PERFMAP PerfMap::LogStubs(__FUNCTION__, "UMEntryThunk", (PCODE)pThunk, sizeof(UMEntryThunk), PerfMapStubType::IndividualWithinBlock); -#endif pData->m_pUMEntryThunk = pThunk; pThunk->Init(pThunk, dac_cast(pData), NULL, dac_cast(PRECODE_UMENTRY_THUNK)); pamTracker->SuppressRelease(); diff --git a/src/coreclr/vm/eventing/eventpipe/ds-rt-coreclr.h b/src/coreclr/vm/eventing/eventpipe/ds-rt-coreclr.h index c2d4ab066c76a9..666abc188af493 100644 --- a/src/coreclr/vm/eventing/eventpipe/ds-rt-coreclr.h +++ b/src/coreclr/vm/eventing/eventpipe/ds-rt-coreclr.h @@ -15,9 +15,7 @@ #include #include #include -#ifdef FEATURE_PERFMAP #include "perfmap.h" -#endif #undef DS_LOG_ALWAYS_0 #define DS_LOG_ALWAYS_0(msg) STRESS_LOG0(LF_DIAGNOSTICS_PORT, LL_ALWAYS, msg "\n") diff --git a/src/coreclr/vm/i386/cgenx86.cpp b/src/coreclr/vm/i386/cgenx86.cpp index 8068a7ed4f1a70..fa234e073cc976 100644 --- a/src/coreclr/vm/i386/cgenx86.cpp +++ b/src/coreclr/vm/i386/cgenx86.cpp @@ -37,9 +37,7 @@ #include "stublink.inl" -#ifdef FEATURE_PERFMAP #include "perfmap.h" -#endif void UpdateRegDisplayFromCalleeSavedRegisters(REGDISPLAY * pRD, CalleeSavedRegisters * regs) { @@ -565,13 +563,9 @@ void ResumeAtJit(PCONTEXT pContext, LPVOID oldESP) size_t rxOffset = pStartRX - pStart; \ BYTE * p = pStart; -#ifdef FEATURE_PERFMAP #define BEGIN_DYNAMIC_HELPER_EMIT(size) \ BEGIN_DYNAMIC_HELPER_EMIT_WORKER(size) \ PerfMap::LogStubs(__FUNCTION__, "DynamicHelper", (PCODE)p, size, PerfMapStubType::Individual); -#else -#define BEGIN_DYNAMIC_HELPER_EMIT(size) BEGIN_DYNAMIC_HELPER_EMIT_WORKER(size) -#endif #define END_DYNAMIC_HELPER_EMIT() \ _ASSERTE(pStart + cb == p); \ diff --git a/src/coreclr/vm/jitinterface.cpp b/src/coreclr/vm/jitinterface.cpp index c0ec22f3e75a8f..0f0e10f2572ac5 100644 --- a/src/coreclr/vm/jitinterface.cpp +++ b/src/coreclr/vm/jitinterface.cpp @@ -52,9 +52,7 @@ #endif // HAVE_GCCOVER #include "debugdebugger.h" -#ifdef FEATURE_PERFMAP #include "perfmap.h" -#endif #ifdef FEATURE_PGO #include "pgo.h" diff --git a/src/coreclr/vm/loongarch64/stubs.cpp b/src/coreclr/vm/loongarch64/stubs.cpp index fc5a820e0aca07..096d160695d435 100644 --- a/src/coreclr/vm/loongarch64/stubs.cpp +++ b/src/coreclr/vm/loongarch64/stubs.cpp @@ -14,9 +14,7 @@ #include "jitinterface.h" #include "ecall.h" -#ifdef FEATURE_PERFMAP #include "perfmap.h" -#endif #ifndef DACCESS_COMPILE //----------------------------------------------------------------------- @@ -961,13 +959,9 @@ void StubLinkerCPU::EmitCallManagedMethod(MethodDesc *pMD, BOOL fTailCall) size_t rxOffset = pStartRX - pStart; \ BYTE * p = pStart; -#ifdef FEATURE_PERFMAP #define BEGIN_DYNAMIC_HELPER_EMIT(size) \ BEGIN_DYNAMIC_HELPER_EMIT_WORKER(size) \ PerfMap::LogStubs(__FUNCTION__, "DynamicHelper", (PCODE)p, size, PerfMapStubType::Individual); -#else -#define BEGIN_DYNAMIC_HELPER_EMIT(size) BEGIN_DYNAMIC_HELPER_EMIT_WORKER(size) -#endif #define END_DYNAMIC_HELPER_EMIT() \ _ASSERTE(pStart + cb == p); \ diff --git a/src/coreclr/vm/perfmap.h b/src/coreclr/vm/perfmap.h index 4dc23f65e107db..7e2012b07d7caa 100644 --- a/src/coreclr/vm/perfmap.h +++ b/src/coreclr/vm/perfmap.h @@ -19,6 +19,52 @@ enum class PerfMapStubType Individual }; +#ifndef FEATURE_PERFMAP + +class PerfMap +{ +public: + static bool IsEnabled() + { +#ifdef DEBUG + return true; +#else + return false; +#endif + } + static void LogJITCompiledMethod(MethodDesc * pMethod, PCODE pCode, size_t codeSize, PrepareCodeConfig *pConfig) + { + CONTRACTL + { + THROWS; + MODE_PREEMPTIVE; + } + CONTRACTL_END; + } + + static void LogPreCompiledMethod(MethodDesc * pMethod, PCODE pCode) + { + CONTRACTL + { + THROWS; + MODE_PREEMPTIVE; + } + CONTRACTL_END; + } + + static void LogStubs(const char* stubType, const char* stubOwner, PCODE pCode, size_t codeSize, PerfMapStubType stubAllocationType) + { + CONTRACTL + { + GC_NOTRIGGER; + MODE_PREEMPTIVE; + } + CONTRACTL_END; + } +}; + +#else // FEATURE_PERFMAP + class PerfMap { private: @@ -112,4 +158,5 @@ class PerfMap static bool LowGranularityStubs() { return !s_IndividualAllocationStubReporting; } }; +#endif // FEATURE_PERFMAP #endif // PERFPID_H diff --git a/src/coreclr/vm/precode.cpp b/src/coreclr/vm/precode.cpp index 6b72dee83957bc..e62556c72e9bda 100644 --- a/src/coreclr/vm/precode.cpp +++ b/src/coreclr/vm/precode.cpp @@ -15,9 +15,7 @@ #include #endif // FEATURE_INTERPRETER -#ifdef FEATURE_PERFMAP #include "perfmap.h" -#endif InterleavedLoaderHeapConfig s_stubPrecodeHeapConfig; #ifdef HAS_FIXUP_PRECODE @@ -253,9 +251,7 @@ InterpreterPrecode* Precode::AllocateInterpreterPrecode(PCODE byteCode, FlushCacheForDynamicMappedStub(pPrecode, sizeof(InterpreterPrecode)); -#ifdef FEATURE_PERFMAP PerfMap::LogStubs(__FUNCTION__, "UMEntryThunk", (PCODE)pPrecode, sizeof(InterpreterPrecode), PerfMapStubType::IndividualWithinBlock); -#endif return pPrecode; } #endif // FEATURE_INTERPRETER @@ -288,9 +284,7 @@ Precode* Precode::Allocate(PrecodeType t, MethodDesc* pMD, // to see the actual final Target (which doesn't require any further synchronization), or we'll hit the memory // barrier in the second portion of the FixupPrecodeThunk and find that the MethodDesc/PrecodeFixupThunk are // properly set. See FixupPrecode::GenerateDataPage for the code to fill in the target. -#ifdef FEATURE_PERFMAP PerfMap::LogStubs(__FUNCTION__, "FixupPrecode", (PCODE)pPrecode, sizeof(FixupPrecode), PerfMapStubType::IndividualWithinBlock); -#endif } #ifdef HAS_THISPTR_RETBUF_PRECODE else if (t == PRECODE_THISPTR_RETBUF) @@ -302,9 +296,7 @@ Precode* Precode::Allocate(PrecodeType t, MethodDesc* pMD, FlushCacheForDynamicMappedStub(pPrecode, sizeof(ThisPtrRetBufPrecode)); -#ifdef FEATURE_PERFMAP PerfMap::LogStubs(__FUNCTION__, "ThisPtrRetBuf", (PCODE)pPrecode, sizeof(ThisPtrRetBufPrecodeData), PerfMapStubType::IndividualWithinBlock); -#endif } #endif // HAS_THISPTR_RETBUF_PRECODE else @@ -315,9 +307,7 @@ Precode* Precode::Allocate(PrecodeType t, MethodDesc* pMD, FlushCacheForDynamicMappedStub(pPrecode, sizeof(StubPrecode)); -#ifdef FEATURE_PERFMAP PerfMap::LogStubs(__FUNCTION__, t == PRECODE_STUB ? "StubPrecode" : "PInvokeImportPrecode", (PCODE)pPrecode, sizeof(StubPrecode), PerfMapStubType::IndividualWithinBlock); -#endif } return pPrecode; diff --git a/src/coreclr/vm/prestub.cpp b/src/coreclr/vm/prestub.cpp index d4220ae0185414..ac9655c489ee0e 100644 --- a/src/coreclr/vm/prestub.cpp +++ b/src/coreclr/vm/prestub.cpp @@ -33,9 +33,7 @@ #include "clrtocomcall.h" #endif -#ifdef FEATURE_PERFMAP #include "perfmap.h" -#endif #include "methoddescbackpatchinfo.h" @@ -371,10 +369,8 @@ PCODE MethodDesc::PrepareCode(PrepareCodeConfig* pConfig) pCode = GetPrecompiledCode(pConfig, shouldTier); } -#ifdef FEATURE_PERFMAP if (pCode != (PCODE)NULL) PerfMap::LogPreCompiledMethod(this, pCode); -#endif } if (pConfig->IsForMulticoreJit() && pCode == (PCODE)NULL && pConfig->ReadyToRunRejectedPrecompiledCode()) @@ -876,10 +872,8 @@ PCODE MethodDesc::JitCompileCodeLockedEventWrapper(PrepareCodeConfig* pConfig, J } #endif // PROFILING_SUPPORTED -#ifdef FEATURE_PERFMAP // Save the JIT'd method information so that perf can resolve JIT'd call frames. PerfMap::LogJITCompiledMethod(this, pCode, sizeOfCode, pConfig); -#endif // The notification will only occur if someone has registered for this method. DACNotifyCompilationFinished(this, pCode); diff --git a/src/coreclr/vm/readytoruninfo.cpp b/src/coreclr/vm/readytoruninfo.cpp index ee9ca4fd3dec65..444e6afea845d2 100644 --- a/src/coreclr/vm/readytoruninfo.cpp +++ b/src/coreclr/vm/readytoruninfo.cpp @@ -20,9 +20,7 @@ #include "ilstubcache.h" #include "sigbuilder.h" -#ifdef FEATURE_PERFMAP #include "perfmap.h" -#endif #ifndef DACCESS_COMPILE extern "C" PCODE g_pMethodWithSlotAndModule; @@ -2533,9 +2531,7 @@ PCODE CreateDynamicHelperPrecode(LoaderAllocator *pAllocator, AllocMemTracker *p FlushCacheForDynamicMappedStub(pPrecode, sizeof(StubPrecode)); -#ifdef FEATURE_PERFMAP PerfMap::LogStubs(__FUNCTION__, "DynamicHelper", (PCODE)pPrecode, size, PerfMapStubType::IndividualWithinBlock); -#endif return ((Precode*)pPrecode)->GetEntryPoint(); } diff --git a/src/coreclr/vm/riscv64/stubs.cpp b/src/coreclr/vm/riscv64/stubs.cpp index 9064d3f844f5fd..10571d08e5ca19 100644 --- a/src/coreclr/vm/riscv64/stubs.cpp +++ b/src/coreclr/vm/riscv64/stubs.cpp @@ -14,9 +14,7 @@ #include "jitinterface.h" #include "ecall.h" -#ifdef FEATURE_PERFMAP #include "perfmap.h" -#endif #ifndef DACCESS_COMPILE //----------------------------------------------------------------------- @@ -1027,13 +1025,9 @@ void StubLinkerCPU::EmitCallManagedMethod(MethodDesc *pMD, BOOL fTailCall) size_t rxOffset = pStartRX - pStart; \ BYTE * p = pStart; -#ifdef FEATURE_PERFMAP #define BEGIN_DYNAMIC_HELPER_EMIT(size) \ BEGIN_DYNAMIC_HELPER_EMIT_WORKER(size) \ PerfMap::LogStubs(__FUNCTION__, "DynamicHelper", (PCODE)p, size, PerfMapStubType::Individual); -#else -#define BEGIN_DYNAMIC_HELPER_EMIT(size) BEGIN_DYNAMIC_HELPER_EMIT_WORKER(size) -#endif #define END_DYNAMIC_HELPER_EMIT() \ _ASSERTE(pStart + cb == p); \ diff --git a/src/coreclr/vm/stublink.cpp b/src/coreclr/vm/stublink.cpp index 972d5d487007ca..86c24647bb6aaa 100644 --- a/src/coreclr/vm/stublink.cpp +++ b/src/coreclr/vm/stublink.cpp @@ -16,9 +16,7 @@ #include "rtlfunctions.h" -#ifdef FEATURE_PERFMAP #include "perfmap.h" -#endif #define S_BYTEPTR(x) S_SIZE_T((SIZE_T)(x)) @@ -579,9 +577,7 @@ Stub *StubLinker::Link(LoaderHeap *pHeap, DWORD flags, const char *stubType) EmitStub(pStub, globalsize, size, pHeap); -#ifdef FEATURE_PERFMAP PerfMap::LogStubs(__FUNCTION__, stubType, pStub->GetEntryPoint(), pStub->GetNumCodeBytes(), PerfMapStubType::Individual); -#endif return pStub.Detach(); } diff --git a/src/coreclr/vm/threads.cpp b/src/coreclr/vm/threads.cpp index e820c8678586e0..b10b13237473ad 100644 --- a/src/coreclr/vm/threads.cpp +++ b/src/coreclr/vm/threads.cpp @@ -51,9 +51,7 @@ #include #endif -#ifdef FEATURE_PERFMAP #include "perfmap.h" -#endif #include "exinfo.h" @@ -1028,13 +1026,11 @@ void InitThreadManagerPerfMapData() GC_TRIGGERS; } CONTRACTL_END; -#ifdef FEATURE_PERFMAP if (IsWriteBarrierCopyEnabled()) { size_t writeBarrierSize = (BYTE*)JIT_PatchedCodeLast - (BYTE*)JIT_PatchedCodeStart; PerfMap::LogStubs(__FUNCTION__, "JIT_CopiedWriteBarriers", (PCODE)s_barrierCopy, writeBarrierSize, PerfMapStubType::Individual); } -#endif } //--------------------------------------------------------------------------- diff --git a/src/coreclr/vm/virtualcallstub.cpp b/src/coreclr/vm/virtualcallstub.cpp index e4e616315e9e3f..cc538a7b990c5a 100644 --- a/src/coreclr/vm/virtualcallstub.cpp +++ b/src/coreclr/vm/virtualcallstub.cpp @@ -10,9 +10,7 @@ #include "comdelegate.h" #include -#ifdef FEATURE_PERFMAP #include "perfmap.h" -#endif #ifndef DACCESS_COMPILE @@ -1115,9 +1113,7 @@ VTableCallHolder* VirtualCallStubManager::GenerateVTableCallStub(DWORD slot) LOG((LF_STUBS, LL_INFO10000, "GenerateVTableCallStub for slot " FMT_ADDR "at" FMT_ADDR "\n", DBG_ADDR(slot), DBG_ADDR(pHolder->stub()))); -#ifdef FEATURE_PERFMAP PerfMap::LogStubs(__FUNCTION__, "GenerateVTableCallStub", (PCODE)pHolder->stub(), pHolder->stub()->size(), PerfMapStubType::IndividualWithinBlock); -#endif RETURN(pHolder); } @@ -2862,9 +2858,7 @@ DispatchHolder *VirtualCallStubManager::GenerateDispatchStub(PCODE ad LOG((LF_STUBS, LL_INFO10000, "GenerateDispatchStub for token" FMT_ADDR "and pMT" FMT_ADDR "at" FMT_ADDR "\n", DBG_ADDR(dispatchToken), DBG_ADDR(pMTExpected), DBG_ADDR(holder->stub()))); -#ifdef FEATURE_PERFMAP PerfMap::LogStubs(__FUNCTION__, "GenerateDispatchStub", (PCODE)holder->stub(), holder->stub()->size(), PerfMapStubType::IndividualWithinBlock); -#endif RETURN (holder); } @@ -2923,9 +2917,7 @@ DispatchHolder *VirtualCallStubManager::GenerateDispatchStubLong(PCODE LOG((LF_STUBS, LL_INFO10000, "GenerateDispatchStub for token" FMT_ADDR "and pMT" FMT_ADDR "at" FMT_ADDR "\n", DBG_ADDR(dispatchToken), DBG_ADDR(pMTExpected), DBG_ADDR(holder->stub()))); -#ifdef FEATURE_PERFMAP PerfMap::LogStubs(__FUNCTION__, "GenerateDispatchStub", (PCODE)holder->stub(), holder->stub()->size(), PerfMapStubType::IndividualWithinBlock); -#endif RETURN (holder); } @@ -3021,9 +3013,7 @@ ResolveHolder *VirtualCallStubManager::GenerateResolveStub(PCODE addr LOG((LF_STUBS, LL_INFO10000, "GenerateResolveStub for token" FMT_ADDR "at" FMT_ADDR "\n", DBG_ADDR(dispatchToken), DBG_ADDR(holder->stub()))); -#ifdef FEATURE_PERFMAP PerfMap::LogStubs(__FUNCTION__, "GenerateResolveStub", (PCODE)holder->stub(), holder->stub()->size(), PerfMapStubType::IndividualWithinBlock); -#endif RETURN (holder); } @@ -3054,9 +3044,7 @@ LookupHolder *VirtualCallStubManager::GenerateLookupStub(PCODE addrOfResolver, s LOG((LF_STUBS, LL_INFO10000, "GenerateLookupStub for token" FMT_ADDR "at" FMT_ADDR "\n", DBG_ADDR(dispatchToken), DBG_ADDR(holder->stub()))); -#ifdef FEATURE_PERFMAP PerfMap::LogStubs(__FUNCTION__, "GenerateLookupStub", (PCODE)holder->stub(), holder->stub()->size(), PerfMapStubType::IndividualWithinBlock); -#endif RETURN (holder); } From 1759f5def238a7f6fd69194ac3f3b64ebf6f85b1 Mon Sep 17 00:00:00 2001 From: David Wrighton Date: Thu, 4 Jun 2026 15:08:18 -0700 Subject: [PATCH 02/19] Adjust preemptive/cooperative state throughout the runtime so that the various techniques for getting an entrypoint, and FindAssociatedMethodDesc are called in preemptive code. This has the effect that the PerfMap::LogStubs logic is always called in preemptive mode, and thus taking the critical section in it won't trigger a GC transition. --- .../src/System/Delegate.CoreCLR.cs | 6 +- src/coreclr/debug/ee/funceval.cpp | 11 +- src/coreclr/vm/assembly.cpp | 6 +- src/coreclr/vm/callhelpers.cpp | 10 +- src/coreclr/vm/callhelpers.h | 169 ------------------ src/coreclr/vm/class.cpp | 2 + src/coreclr/vm/comcallablewrapper.cpp | 2 +- src/coreclr/vm/comconnectionpoints.cpp | 16 +- src/coreclr/vm/comdelegate.cpp | 95 +++++----- src/coreclr/vm/comdelegate.h | 2 +- src/coreclr/vm/comutilnative.cpp | 20 ++- src/coreclr/vm/corhost.cpp | 6 +- src/coreclr/vm/customattribute.cpp | 10 +- src/coreclr/vm/dispatchinfo.cpp | 11 +- src/coreclr/vm/dllimportcallback.cpp | 4 +- .../vm/eventing/eventpipe/ds-rt-coreclr.h | 4 +- src/coreclr/vm/fptrstubs.cpp | 3 +- src/coreclr/vm/genmeth.cpp | 45 +---- src/coreclr/vm/interpexec.cpp | 2 + src/coreclr/vm/jithelpers.cpp | 1 + src/coreclr/vm/method.cpp | 54 ++++-- src/coreclr/vm/method.hpp | 3 - src/coreclr/vm/methodtable.cpp | 43 +++-- src/coreclr/vm/methodtable.inl | 2 +- src/coreclr/vm/prestub.cpp | 2 + src/coreclr/vm/qcall.h | 6 + src/coreclr/vm/reflectioninvocation.cpp | 15 +- src/coreclr/vm/runtimehandles.cpp | 18 +- src/coreclr/vm/virtualcallstub.cpp | 73 ++++++-- 29 files changed, 299 insertions(+), 342 deletions(-) diff --git a/src/coreclr/System.Private.CoreLib/src/System/Delegate.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Delegate.CoreCLR.cs index 358dab7f43674d..fc80b8b229f19c 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Delegate.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Delegate.CoreCLR.cs @@ -535,11 +535,13 @@ private static bool InternalEqualMethodHandles(Delegate left, Delegate right) internal static IntPtr AdjustTarget(object target, IntPtr methodPtr) { - return AdjustTarget(ObjectHandleOnStack.Create(ref target), methodPtr); + IntPtr result = AdjustTarget(RuntimeHelpers.GetMethodTable(target), methodPtr); + GC.KeepAlive(target); + return result; } [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "Delegate_AdjustTarget")] - private static partial IntPtr AdjustTarget(ObjectHandleOnStack target, IntPtr methodPtr); + private static partial IntPtr AdjustTarget(MethodTable* targetMT, IntPtr methodPtr); internal void InitializeVirtualCallStub(IntPtr methodPtr) { diff --git a/src/coreclr/debug/ee/funceval.cpp b/src/coreclr/debug/ee/funceval.cpp index 263beaa911bbfc..45723c4a9b8ae2 100644 --- a/src/coreclr/debug/ee/funceval.cpp +++ b/src/coreclr/debug/ee/funceval.cpp @@ -2099,6 +2099,7 @@ void GatherFuncEvalMethodInfo(DebuggerEval *pDE, // if ((pDE->m_evalType != DB_IPCE_FET_NEW_OBJECT) && !pDE->m_md->IsStatic() && pDE->m_md->IsUnboxingStub()) { + GCX_PREEMP(); *ppUnboxedMD = pDE->m_md->GetMethodTable()->GetUnboxedEntryPointMD(pDE->m_md); } @@ -2213,13 +2214,19 @@ void GatherFuncEvalMethodInfo(DebuggerEval *pDE, // Now, find the proper MethodDesc for this interface method based on the object we're invoking the // method on. // - pDE->m_targetCodeAddr = pDE->m_md->GetCallTarget(&objRef, pDE->m_ownerTypeHandle); + { + GCX_PREEMP(); + pDE->m_targetCodeAddr = pDE->m_md->GetCallTarget(&objRef, pDE->m_ownerTypeHandle); + } GCPROTECT_END(); } else { - pDE->m_targetCodeAddr = pDE->m_md->GetCallTarget(NULL, pDE->m_ownerTypeHandle); + { + GCX_PREEMP(); + pDE->m_targetCodeAddr = pDE->m_md->GetCallTarget(NULL, pDE->m_ownerTypeHandle); + } } // diff --git a/src/coreclr/vm/assembly.cpp b/src/coreclr/vm/assembly.cpp index 5f2526172d941d..7bd7c0ae0217a6 100644 --- a/src/coreclr/vm/assembly.cpp +++ b/src/coreclr/vm/assembly.cpp @@ -1156,7 +1156,11 @@ static void RunMainInternal(Param* pParam) StrArgArray = *pParam->stringArgs; pParam->pFD->EnsureActive(); - PCODE entryPoint = pParam->pFD->GetSingleCallableAddrOfCode(); + PCODE entryPoint; + { + GCX_PREEMP(); + entryPoint = pParam->pFD->GetSingleCallableAddrOfCode(); + } BOOL hasReturnValue = !pParam->pFD->IsVoid(); PTRARRAYREF* pArgument = (pParam->EntryType == EntryManagedMain) ? &StrArgArray : NULL; diff --git a/src/coreclr/vm/callhelpers.cpp b/src/coreclr/vm/callhelpers.cpp index 2568a1483d5714..b2160b1ac6bfd3 100644 --- a/src/coreclr/vm/callhelpers.cpp +++ b/src/coreclr/vm/callhelpers.cpp @@ -559,11 +559,17 @@ void CallDefaultConstructor(OBJECTREF ref) GCPROTECT_BEGIN (ref); - MethodDesc *pMD = pMT->GetDefaultConstructor(); + + PCODE methodEntry; + { + GCX_PREEMP(); + MethodDesc *pMD = pMT->GetDefaultConstructor(); + methodEntry = pMD->GetSingleCallableAddrOfCode(); + } UnmanagedCallersOnlyCaller defaultCtorInvoker{METHOD__RUNTIME_HELPERS__CALL_DEFAULT_CONSTRUCTOR}; - defaultCtorInvoker.InvokeThrowing(&ref, pMD->GetSingleCallableAddrOfCode()); + defaultCtorInvoker.InvokeThrowing(&ref, methodEntry); GCPROTECT_END (); } diff --git a/src/coreclr/vm/callhelpers.h b/src/coreclr/vm/callhelpers.h index a91f65078c271c..5630d73b12a67e 100644 --- a/src/coreclr/vm/callhelpers.h +++ b/src/coreclr/vm/callhelpers.h @@ -104,178 +104,9 @@ class MethodDescCallSite } #endif // _DEBUG - void DefaultInit(OBJECTREF* porProtectedThis) - { - CONTRACTL - { - MODE_ANY; - GC_TRIGGERS; - THROWS; - } - CONTRACTL_END; - -#ifdef _DEBUG - // - // Make sure we are passing in a 'this' if and only if it is required - // - if (m_pMD->IsVtableMethod()) - { - CONSISTENCY_CHECK_MSG(NULL != porProtectedThis, "You did not pass in the 'this' object for a vtable method"); - } - else - { - if (NULL != porProtectedThis) - { - if (CLRConfig::GetConfigValue(CLRConfig::INTERNAL_AssertOnUnneededThis)) - { - CONSISTENCY_CHECK_MSG(NULL == porProtectedThis, "You passed in a 'this' object to a non-vtable method."); - } - else - { - LogWeakAssert(); - } - - } - } -#endif // _DEBUG - - m_pCallTarget = m_pMD->GetCallTarget(porProtectedThis); - - m_argIt.ForceSigWalk(); - } - - void DefaultInit(TypeHandle th) - { - CONTRACTL - { - MODE_ANY; - GC_TRIGGERS; - THROWS; - } - CONTRACTL_END; - - m_pCallTarget = m_pMD->GetCallTarget(NULL, th); - - m_argIt.ForceSigWalk(); -} - void CallTargetWorker(const ARG_SLOT *pArguments, ARG_SLOT *pReturnValue, int cbReturnValue); public: - // Used to avoid touching metadata for CoreLib methods. - // instance methods must pass in the 'this' object - // static methods must pass null - MethodDescCallSite(BinderMethodID id, OBJECTREF* porProtectedThis = NULL) : - m_pMD( - CoreLibBinder::GetMethod(id) - ), - m_methodSig(id), - m_argIt(&m_methodSig) - { - CONTRACTL - { - THROWS; - GC_TRIGGERS; - MODE_COOPERATIVE; - } - CONTRACTL_END; - DefaultInit(porProtectedThis); - } - - // Used to avoid touching metadata for CoreLib methods. - // instance methods must pass in the 'this' object - // static methods must pass null - MethodDescCallSite(BinderMethodID id, OBJECTHANDLE hThis) : - m_pMD( - CoreLibBinder::GetMethod(id) - ), - m_methodSig(id), - m_argIt(&m_methodSig) - { - WRAPPER_NO_CONTRACT; - - DefaultInit((OBJECTREF*)hThis); - } - - // instance methods must pass in the 'this' object - // static methods must pass null - MethodDescCallSite(MethodDesc* pMD, OBJECTREF* porProtectedThis = NULL) : - m_pMD(pMD), - m_methodSig(pMD), - m_argIt(&m_methodSig) - { - CONTRACTL - { - THROWS; - GC_TRIGGERS; - MODE_COOPERATIVE; - } - CONTRACTL_END; - - if (porProtectedThis == NULL) - { - // We don't have a "this" pointer - ensure that we have activated the containing module - m_pMD->EnsureActive(); - } - - DefaultInit(porProtectedThis); - } - - // instance methods must pass in the 'this' object - // static methods must pass null - MethodDescCallSite(MethodDesc* pMD, OBJECTHANDLE hThis) : - m_pMD(pMD), - m_methodSig(pMD), - m_argIt(&m_methodSig) - { - WRAPPER_NO_CONTRACT; - - if (hThis == NULL) - { - // We don't have a "this" pointer - ensure that we have activated the containing module - m_pMD->EnsureActive(); - } - - DefaultInit((OBJECTREF*)hThis); - } - - // instance methods must pass in the 'this' object - // static methods must pass null - MethodDescCallSite(MethodDesc* pMD, LPHARDCODEDMETASIG pwzSignature, OBJECTREF* porProtectedThis = NULL) : - m_pMD(pMD), - m_methodSig(pwzSignature), - m_argIt(&m_methodSig) - { - WRAPPER_NO_CONTRACT; - - if (porProtectedThis == NULL) - { - // We don't have a "this" pointer - ensure that we have activated the containing module - m_pMD->EnsureActive(); - } - - DefaultInit(porProtectedThis); - } - - MethodDescCallSite(MethodDesc* pMD, TypeHandle th) : - m_pMD(pMD), - m_methodSig(pMD, th), - m_argIt(&m_methodSig) - { - CONTRACTL - { - THROWS; - GC_TRIGGERS; - MODE_COOPERATIVE; - } - CONTRACTL_END; - - // We don't have a "this" pointer - ensure that we have activated the containing module - m_pMD->EnsureActive(); - - DefaultInit(th); - } - // // Only use this constructor if you're certain you know where // you're going and it cannot be affected by generics/virtual diff --git a/src/coreclr/vm/class.cpp b/src/coreclr/vm/class.cpp index 551c456691954b..4e228cbd4c4fef 100644 --- a/src/coreclr/vm/class.cpp +++ b/src/coreclr/vm/class.cpp @@ -1709,6 +1709,7 @@ void TypeHandle::NotifyDebuggerUnload() const MethodDesc* MethodTable::GetBoxedEntryPointMD(MethodDesc *pMD) { CONTRACT (MethodDesc *) { + MODE_PREEMPTIVE; THROWS; GC_TRIGGERS; INJECT_FAULT(COMPlusThrowOM();); @@ -1732,6 +1733,7 @@ MethodDesc* MethodTable::GetBoxedEntryPointMD(MethodDesc *pMD) MethodDesc* MethodTable::GetUnboxedEntryPointMD(MethodDesc *pMD) { CONTRACT (MethodDesc *) { + MODE_PREEMPTIVE; THROWS; GC_TRIGGERS; INJECT_FAULT(COMPlusThrowOM();); diff --git a/src/coreclr/vm/comcallablewrapper.cpp b/src/coreclr/vm/comcallablewrapper.cpp index 3bd5be1351ff10..070e92139a95d3 100644 --- a/src/coreclr/vm/comcallablewrapper.cpp +++ b/src/coreclr/vm/comcallablewrapper.cpp @@ -2836,7 +2836,7 @@ namespace { THROWS; GC_NOTRIGGER; - MODE_ANY; + MODE_PREEMPTIVE; INJECT_FAULT(COMPlusThrowOM()); } CONTRACTL_END; diff --git a/src/coreclr/vm/comconnectionpoints.cpp b/src/coreclr/vm/comconnectionpoints.cpp index f4c60e20ea451e..09e2cef87c7196 100644 --- a/src/coreclr/vm/comconnectionpoints.cpp +++ b/src/coreclr/vm/comconnectionpoints.cpp @@ -556,14 +556,24 @@ void ConnectionPoint::InvokeProviderMethod( OBJECTREF pProvider, OBJECTREF pSubs { UnmanagedCallersOnlyCaller invokeConnectionPointProviderMethod(METHOD__STUBHELPERS__INVOKE_CONNECTION_POINT_PROVIDER_METHOD); + PCODE pProvCode; + PCODE pDlgCtorCode; + PCODE pEventMethodCode; + { + GCX_PREEMP(); + pProvCode = pProvMethodDesc->GetSingleCallableAddrOfCode(); + pDlgCtorCode = pDlgCtorMD->GetSingleCallableAddrOfCode(); + pEventMethodCode = pEventMethodDesc->GetMultiCallableAddrOfCode(); + } + // Using GetMultiCallableAddrOfCode() for the event target since it is stored for future invokes. invokeConnectionPointProviderMethod.InvokeThrowing( &pProvider, - pProvMethodDesc->GetSingleCallableAddrOfCode(), + pProvCode, &pDelegate, - pDlgCtorMD->GetSingleCallableAddrOfCode(), + pDlgCtorCode, &pSubscriber, - pEventMethodDesc->GetMultiCallableAddrOfCode()); + pEventMethodCode); } GCPROTECT_END(); } diff --git a/src/coreclr/vm/comdelegate.cpp b/src/coreclr/vm/comdelegate.cpp index ffa068c905d405..80676ebaae5535 100644 --- a/src/coreclr/vm/comdelegate.cpp +++ b/src/coreclr/vm/comdelegate.cpp @@ -1145,13 +1145,16 @@ extern "C" BOOL QCALLTYPE Delegate_BindToMethodInfo(QCall::ObjectHandleOnStack d _ASSERTE(pInvokeMeth); // See the comment in BindToMethodName - method = - MethodDesc::FindOrCreateAssociatedMethodDesc(method, - pMethMT, - (!method->IsStatic() && pMethMT->IsValueType()), - method->GetMethodInstantiation(), - false /* do not allow code with a shared-code calling convention to be returned */, - true /* Ensure that methods on generic interfaces are returned as instantiated method descs */); + { + GCX_PREEMP(); + method = + MethodDesc::FindOrCreateAssociatedMethodDesc(method, + pMethMT, + (!method->IsStatic() && pMethMT->IsValueType()), + method->GetMethodInstantiation(), + false /* do not allow code with a shared-code calling convention to be returned */, + true /* Ensure that methods on generic interfaces are returned as instantiated method descs */); + } bool fIsOpenDelegate; if (COMDelegate::IsMethodDescCompatible((gc.refFirstArg == NULL) ? TypeHandle() : gc.refFirstArg->GetTypeHandle(), @@ -1246,27 +1249,31 @@ void COMDelegate::BindToMethod(DELEGATEREF *pRefThis, } else { - // Reflection or the code in BindToMethodName will pass us the unboxing stub for non-static methods on value types. But - // for open invocation on value type methods the actual reference will be passed so we need the unboxed method desc - // instead. - if (pTargetMethod->IsUnboxingStub()) + PCODE pTargetCode; { - // We want a MethodDesc which is not an unboxing stub, but is an instantiating stub if needed. - pTargetMethod = MethodDesc::FindOrCreateAssociatedMethodDesc( - pTargetMethod, - pExactMethodType, - FALSE /* don't want unboxing entry point */, - pTargetMethod->GetMethodInstantiation(), - FALSE /* don't want MD that requires inst. arguments */, - true /* Ensure that methods on generic interfaces are returned as instantiated method descs */); - } + GCX_PREEMP(); + // Reflection or the code in BindToMethodName will pass us the unboxing stub for non-static methods on value types. But + // for open invocation on value type methods the actual reference will be passed so we need the unboxed method desc + // instead. + if (pTargetMethod->IsUnboxingStub()) + { + // We want a MethodDesc which is not an unboxing stub, but is an instantiating stub if needed. + pTargetMethod = MethodDesc::FindOrCreateAssociatedMethodDesc( + pTargetMethod, + pExactMethodType, + FALSE /* don't want unboxing entry point */, + pTargetMethod->GetMethodInstantiation(), + FALSE /* don't want MD that requires inst. arguments */, + true /* Ensure that methods on generic interfaces are returned as instantiated method descs */); + } - // The method must not require any extra hidden instantiation arguments. - _ASSERTE(!pTargetMethod->RequiresInstArg()); + // The method must not require any extra hidden instantiation arguments. + _ASSERTE(!pTargetMethod->RequiresInstArg()); - // Note that it is important to cache pTargetCode in local variable to avoid GC hole. - // GetMultiCallableAddrOfCode() can trigger GC. - PCODE pTargetCode = pTargetMethod->GetMultiCallableAddrOfCode(); + // Note that it is important to cache pTargetCode in local variable to avoid GC hole. + // GetMultiCallableAddrOfCode() can trigger GC. + pTargetCode = pTargetMethod->GetMultiCallableAddrOfCode(); + } refRealDelegate->SetMethodPtrAux(pTargetCode); } } @@ -1284,16 +1291,19 @@ void COMDelegate::BindToMethod(DELEGATEREF *pRefThis, && *pRefFirstArg != NULL && pTargetMethod->GetMethodTable() != (*pRefFirstArg)->GetMethodTable()) { + GCX_PREEMP(); pTargetCode = pTargetMethod->GetMultiCallableAddrOfVirtualizedCode(pRefFirstArg, pTargetMethod->GetMethodTable()); } #ifdef HAS_THISPTR_RETBUF_PRECODE else if (pTargetMethod->IsStatic() && pTargetMethod->HasRetBuffArg() && IsRetBuffPassedAsFirstArg()) { + GCX_PREEMP(); pTargetCode = pTargetMethod->GetLoaderAllocator()->GetFuncPtrStubs()->GetFuncPtrStub(pTargetMethod, PRECODE_THISPTR_RETBUF); } #endif // HAS_THISPTR_RETBUF_PRECODE else { + GCX_PREEMP(); pTargetCode = pTargetMethod->GetMultiCallableAddrOfCode(); } _ASSERTE(pTargetCode); @@ -1354,6 +1364,7 @@ LPVOID COMDelegate::ConvertToCallback(OBJECTREF pDelegateObj) InteropSyncBlockInfo* pInteropInfo = pSyncBlock->GetInteropInfo(); + GCX_PREEMP(); pUMEntryThunk = pInteropInfo->GetUMEntryThunk(); if (!pUMEntryThunk) @@ -1363,8 +1374,6 @@ LPVOID COMDelegate::ConvertToCallback(OBJECTREF pDelegateObj) if (!pUMThunkMarshInfo) { - GCX_PREEMP(); - pUMThunkMarshInfo = (UMThunkMarshInfo*)(void*)pMT->GetLoaderAllocator()->GetLowFrequencyHeap()->AllocMem(S_SIZE_T(sizeof(DelegateUMThunkMarshInfo))); new (pUMThunkMarshInfo) DelegateUMThunkMarshInfo(pInvokeMeth); @@ -1381,15 +1390,22 @@ LPVOID COMDelegate::ConvertToCallback(OBJECTREF pDelegateObj) _ASSERTE(pUMThunkMarshInfo == pClass->m_pUMThunkMarshInfo); pUMEntryThunk = UMEntryThunkData::CreateUMEntryThunk(); + Holder umHolder; umHolder.Assign(pUMEntryThunk); - // multicast. go thru Invoke - OBJECTHANDLE objhnd = GetAppDomain()->CreateLongWeakHandle(pDelegate); - _ASSERTE(objhnd != NULL); + OBJECTHANDLE objhnd; + PCODE pManagedTargetForDiagnostics; + { + GCX_COOP(); + + // multicast. go thru Invoke + objhnd = GetAppDomain()->CreateLongWeakHandle(pDelegate); + _ASSERTE(objhnd != NULL); - // This target should not ever be used. We are storing it in the thunk for better diagnostics of "call on collected delegate" crashes. - PCODE pManagedTargetForDiagnostics = (pDelegate->GetMethodPtrAux() != (PCODE)NULL) ? pDelegate->GetMethodPtrAux() : pDelegate->GetMethodPtr(); + // This target should not ever be used. We are storing it in the thunk for better diagnostics of "call on collected delegate" crashes. + pManagedTargetForDiagnostics = (pDelegate->GetMethodPtrAux() != (PCODE)NULL) ? pDelegate->GetMethodPtrAux() : pDelegate->GetMethodPtr(); + } // MethodDesc is passed in for profiling to know the method desc of target pUMEntryThunk->LoadTimeInit( @@ -1603,18 +1619,12 @@ extern "C" void QCALLTYPE Delegate_InitializeVirtualCallStub(QCall::ObjectHandle END_QCALL; } -extern "C" PCODE QCALLTYPE Delegate_AdjustTarget(QCall::ObjectHandleOnStack target, PCODE method) +extern "C" PCODE QCALLTYPE Delegate_AdjustTarget(MethodTable* pMTTarg, PCODE method) { QCALL_CONTRACT; BEGIN_QCALL; - GCX_COOP(); - - _ASSERTE(method); - - MethodTable* pMTTarg = target.Get()->GetMethodTable(); - MethodDesc *pMeth = NonVirtualEntry2MethodDesc(method); _ASSERTE(pMeth); _ASSERTE(!pMeth->IsStatic()); @@ -1780,6 +1790,7 @@ extern "C" void QCALLTYPE Delegate_Construct(QCall::ObjectHandleOnStack _this, Q && (pMTMeth != g_pObjectClass)) { pMeth->CheckRestore(); + GCX_PREEMP(); pMeth = pMTTarg->GetBoxedEntryPointMD(pMeth); _ASSERTE(pMeth != NULL); } @@ -1789,6 +1800,7 @@ extern "C" void QCALLTYPE Delegate_Construct(QCall::ObjectHandleOnStack _this, Q // so we don't have to do all this mucking about. if (pMeth != pMethOrig) { + GCX_PREEMP(); method = pMeth->GetMultiCallableAddrOfCode(); } } @@ -2133,7 +2145,10 @@ extern "C" void QCALLTYPE Delegate_FindMethodHandle(QCall::ObjectHandleOnStack d GCX_COOP(); MethodDesc* pMD = COMDelegate::GetMethodDesc(d.Get()); - pMD = MethodDesc::FindOrCreateAssociatedMethodDescForReflection(pMD, TypeHandle(pMD->GetMethodTable()), pMD->GetMethodInstantiation()); + { + GCX_PREEMP(); + pMD = MethodDesc::FindOrCreateAssociatedMethodDescForReflection(pMD, TypeHandle(pMD->GetMethodTable()), pMD->GetMethodInstantiation()); + } retMethodInfo.Set(pMD->AllocateStubMethodInfo()); END_QCALL; diff --git a/src/coreclr/vm/comdelegate.h b/src/coreclr/vm/comdelegate.h index 1c95811d017131..a742d281ae5dcb 100644 --- a/src/coreclr/vm/comdelegate.h +++ b/src/coreclr/vm/comdelegate.h @@ -99,7 +99,7 @@ extern "C" void QCALLTYPE Delegate_Construct(QCall::ObjectHandleOnStack _this, Q extern "C" PCODE QCALLTYPE Delegate_GetMulticastInvokeSlow(MethodTable* pDelegateMT); -extern "C" PCODE QCALLTYPE Delegate_AdjustTarget(QCall::ObjectHandleOnStack target, PCODE method); +extern "C" PCODE QCALLTYPE Delegate_AdjustTarget(MethodTable* pMTTarg, PCODE method); extern "C" void QCALLTYPE Delegate_InitializeVirtualCallStub(QCall::ObjectHandleOnStack d, PCODE method); diff --git a/src/coreclr/vm/comutilnative.cpp b/src/coreclr/vm/comutilnative.cpp index 8db3217f1cb855..64723383a2ad6e 100644 --- a/src/coreclr/vm/comutilnative.cpp +++ b/src/coreclr/vm/comutilnative.cpp @@ -775,19 +775,23 @@ extern "C" void* QCALLTYPE GCInterface_GetNextFinalizableObject(QCall::ObjectHan BEGIN_QCALL; - GCX_COOP(); - - OBJECTREF target = FinalizerThread::GetNextFinalizableObject(); - - if (target != NULL) + MethodTable *pTargetMT = NULL; { - pObj.Set(target); + GCX_COOP(); - MethodTable* pMT = target->GetMethodTable(); + OBJECTREF target = FinalizerThread::GetNextFinalizableObject(); - funcPtr = pMT->GetRestoredSlot(g_pObjectFinalizerMD->GetSlot()); + if (target != NULL) + { + pObj.Set(target); + + pTargetMT = target->GetMethodTable(); + } } + if (pTargetMT != NULL) + funcPtr = pTargetMT->GetRestoredSlot(g_pObjectFinalizerMD->GetSlot()); + END_QCALL; return (void*)funcPtr; diff --git a/src/coreclr/vm/corhost.cpp b/src/coreclr/vm/corhost.cpp index 7a67425b885f75..3315af1bd20774 100644 --- a/src/coreclr/vm/corhost.cpp +++ b/src/coreclr/vm/corhost.cpp @@ -430,7 +430,11 @@ HRESULT CorHost2::ExecuteInDefaultAppDomain(LPCWSTR pwzAssemblyPath, UnmanagedCallersOnlyCaller executeInDefaultAppDomain(METHOD__ENVIRONMENT__EXECUTE_IN_DEFAULT_APP_DOMAIN); pMethodMD->EnsureActive(); - PCODE entryPoint = pMethodMD->GetSingleCallableAddrOfCode(); + PCODE entryPoint; + { + GCX_PREEMP(); + entryPoint = pMethodMD->GetSingleCallableAddrOfCode(); + } INT32 retval = executeInDefaultAppDomain.InvokeThrowing_Ret( static_cast(entryPoint), diff --git a/src/coreclr/vm/customattribute.cpp b/src/coreclr/vm/customattribute.cpp index 59c3d87f73b3d2..c505bec2b167a9 100644 --- a/src/coreclr/vm/customattribute.cpp +++ b/src/coreclr/vm/customattribute.cpp @@ -915,7 +915,15 @@ extern "C" void QCALLTYPE CustomAttribute_CreateCustomAttributeInstance( MethodDesc* pCtorMD = ((REFLECTMETHODREF)pMethod.Get())->GetMethod(); TypeHandle th = ((REFLECTCLASSBASEREF)pCaType.Get())->GetType(); - MethodDescCallSite ctorCallSite(pCtorMD, th); + PCODE pCallTarget; + + { + GCX_PREEMP(); + pCtorMD->EnsureActive(); + pCallTarget = pCtorMD->GetCallTarget(NULL, th); + } + + MethodDescCallSite ctorCallSite(pCtorMD, pCallTarget); MetaSig* pSig = ctorCallSite.GetMetaSig(); BYTE* pBlob = *ppBlob; diff --git a/src/coreclr/vm/dispatchinfo.cpp b/src/coreclr/vm/dispatchinfo.cpp index 388c4366d2e74d..fe840a99ea99c5 100644 --- a/src/coreclr/vm/dispatchinfo.cpp +++ b/src/coreclr/vm/dispatchinfo.cpp @@ -2514,13 +2514,12 @@ BOOL DispatchInfo::SynchWithManagedView() // Determine if this is the first time we synch. BOOL bFirstSynch = (m_pFirstMemberInfo == NULL); + GCX_PREEMP(); + // This method needs to be synchronized to make sure two threads don't try and // add members at the same time. CrstHolder ch(&m_lock); { - // Make sure we switch to cooperative mode before we start. - GCX_COOP(); - // Go through the list of member info's and find the end. DispatchMemberInfo **ppNextMember = &m_pFirstMemberInfo; while (*ppNextMember) @@ -2529,6 +2528,9 @@ BOOL DispatchInfo::SynchWithManagedView() // Retrieve the member info map. pMemberMap = GetMemberInfoMap(); + // Make sure we switch to cooperative mode before we start. + GCX_COOP(); + for (int cPhase = 0; cPhase < 3; cPhase++) { PTRARRAYREF MemberArrayObj = NULL; @@ -2837,13 +2839,12 @@ ComMTMemberInfoMap *DispatchInfo::GetMemberInfoMap() { THROWS; GC_TRIGGERS; - MODE_COOPERATIVE; + MODE_PREEMPTIVE; INJECT_FAULT(COMPlusThrowOM()); POSTCONDITION(CheckPointer(RETVAL)); } CONTRACT_END; - // Create the member info map. NewHolder pMemberInfoMap (new ComMTMemberInfoMap(m_pMT)); diff --git a/src/coreclr/vm/dllimportcallback.cpp b/src/coreclr/vm/dllimportcallback.cpp index 7113a6779469d5..5c08f190499e49 100644 --- a/src/coreclr/vm/dllimportcallback.cpp +++ b/src/coreclr/vm/dllimportcallback.cpp @@ -120,7 +120,7 @@ UMEntryThunkData *UMEntryThunkCache::GetUMEntryThunk(MethodDesc *pMD) { THROWS; GC_TRIGGERS; - MODE_ANY; + MODE_PREEMPTIVE; PRECONDITION(CheckPointer(pMD)); POSTCONDITION(CheckPointer(RETVAL)); } @@ -268,7 +268,7 @@ UMEntryThunkData* UMEntryThunkData::CreateUMEntryThunk() { THROWS; GC_NOTRIGGER; - MODE_ANY; + MODE_PREEMPTIVE; INJECT_FAULT(COMPlusThrowOM()); POSTCONDITION(CheckPointer(RETVAL)); } diff --git a/src/coreclr/vm/eventing/eventpipe/ds-rt-coreclr.h b/src/coreclr/vm/eventing/eventpipe/ds-rt-coreclr.h index 666abc188af493..49ce12e5b0dd3f 100644 --- a/src/coreclr/vm/eventing/eventpipe/ds-rt-coreclr.h +++ b/src/coreclr/vm/eventing/eventpipe/ds-rt-coreclr.h @@ -231,7 +231,9 @@ static uint32_t ds_rt_enable_perfmap (uint32_t type) { - LIMITED_METHOD_CONTRACT; + CONTRACTL{ + MODE_PREEMPTIVE; + } CONTRACTL_END; #ifdef FEATURE_PERFMAP PerfMap::PerfMapType perfMapType = (PerfMap::PerfMapType)type; diff --git a/src/coreclr/vm/fptrstubs.cpp b/src/coreclr/vm/fptrstubs.cpp index cf3f4db379f92b..f0e3a466df6bf0 100644 --- a/src/coreclr/vm/fptrstubs.cpp +++ b/src/coreclr/vm/fptrstubs.cpp @@ -78,6 +78,7 @@ PCODE FuncPtrStubs::GetFuncPtrStub(MethodDesc * pMD, PrecodeType type) { return pPrecode->GetEntryPoint(); } + GCX_PREEMP(); PCODE target = (PCODE)NULL; bool setTargetAfterAddingToHashTable = false; @@ -151,8 +152,6 @@ PCODE FuncPtrStubs::GetFuncPtrStub(MethodDesc * pMD, PrecodeType type) if (setTargetAfterAddingToHashTable) { - GCX_PREEMP(); - _ASSERTE(pMD->IsVersionableWithVtableSlotBackpatch()); PCODE temporaryEntryPoint = pMD->GetTemporaryEntryPoint(); diff --git a/src/coreclr/vm/genmeth.cpp b/src/coreclr/vm/genmeth.cpp index 62f413ada24e67..460325e24d2a2b 100644 --- a/src/coreclr/vm/genmeth.cpp +++ b/src/coreclr/vm/genmeth.cpp @@ -170,6 +170,7 @@ static MethodDesc * FindTightlyBoundWrappedMethodDesc(MethodDesc * pMD) { CONTRACTL { + MODE_ANY; NOTHROW; GC_NOTRIGGER; PRECONDITION(CheckPointer(pMD)); @@ -326,6 +327,7 @@ InstantiatedMethodDesc::NewInstantiatedMethodDesc(MethodTable *pExactMT, { CONTRACT(InstantiatedMethodDesc*) { + MODE_PREEMPTIVE; THROWS; GC_TRIGGERS; INJECT_FAULT(COMPlusThrowOM();); @@ -545,45 +547,6 @@ InstantiatedMethodDesc::NewInstantiatedMethodDesc(MethodTable *pExactMT, RETURN pNewMD; } - -// Calling this method is equivalent to -// FindOrCreateAssociatedMethodDesc(pCanonicalMD, pExactMT, FALSE, Instantiation(), FALSE, TRUE) -// except that it also creates InstantiatedMethodDescs based on shared class methods. This is -// convenient for interop where, unlike ordinary managed methods, marshaling stubs for say Foo -// and Foo look very different and need separate representation. -InstantiatedMethodDesc* -InstantiatedMethodDesc::FindOrCreateExactClassMethod(MethodTable *pExactMT, - MethodDesc *pCanonicalMD) -{ - CONTRACTL - { - THROWS; - GC_TRIGGERS; - MODE_ANY; - PRECONDITION(!pExactMT->IsSharedByGenericInstantiations()); - PRECONDITION(pCanonicalMD->IsSharedByGenericInstantiations()); - } - CONTRACTL_END; - - InstantiatedMethodDesc *pInstMD = FindLoadedInstantiatedMethodDesc(pExactMT, - pCanonicalMD->GetMemberDef(), - Instantiation(), - FALSE, - pCanonicalMD->IsAsyncVariantMethod()); - - if (pInstMD == NULL) - { - // create a new MD if not found - pInstMD = NewInstantiatedMethodDesc(pExactMT, - pCanonicalMD, - pCanonicalMD, - Instantiation(), - FALSE); - } - - return pInstMD; -} - #endif // !DACCESS_COMPILE // N.B. it is not guarantee that the returned InstantiatedMethodDesc is restored. @@ -755,8 +718,9 @@ MethodDesc::FindOrCreateAssociatedMethodDesc(MethodDesc* pDefMD, { CONTRACT(MethodDesc*) { + MODE_PREEMPTIVE; THROWS; - if (allowCreate) { GC_TRIGGERS; } else { GC_NOTRIGGER; } + if (allowCreate) { MODE_PREEMPTIVE; } else { MODE_ANY; } if (!allowCreate) { SUPPORTS_DAC; } INJECT_FAULT(COMPlusThrowOM();); @@ -1271,6 +1235,7 @@ MethodDesc::FindOrCreateAssociatedMethodDesc(MethodDesc* pDefMD, Instantiation methodInst) { CONTRACTL { + MODE_PREEMPTIVE; THROWS; GC_TRIGGERS; // Because allowCreate is TRUE PRECONDITION(CheckPointer(pMethod)); diff --git a/src/coreclr/vm/interpexec.cpp b/src/coreclr/vm/interpexec.cpp index 4ef4ad40c9617f..2654d118d0de9f 100644 --- a/src/coreclr/vm/interpexec.cpp +++ b/src/coreclr/vm/interpexec.cpp @@ -3141,6 +3141,7 @@ void InterpExecMethod(InterpreterFrame *pInterpreterFrame, InterpMethodContextFr // miss, resolve the virtual method and cache it targetMethod = CallWithSEHWrapper( [&pMD, &pThisArg]() { + GCX_PREEMP(); return pMD->GetMethodDescOfVirtualizedCode(pThisArg, pMD->GetMethodTable()); }); g_InterpDispatchCache.Insert(dispatchToken, pObjMT, targetMethod, (uint16_t)dispatchTokenHash); @@ -3308,6 +3309,7 @@ void InterpExecMethod(InterpreterFrame *pInterpreterFrame, InterpMethodContextFr NULL_CHECK(*pThisArg); targetMethod = CallWithSEHWrapper( [&targetMethod, &pThisArg]() { + GCX_PREEMP(); return targetMethod->GetMethodDescOfVirtualizedCode(pThisArg, targetMethod->GetMethodTable()); }); } diff --git a/src/coreclr/vm/jithelpers.cpp b/src/coreclr/vm/jithelpers.cpp index a71a92fc5519e4..f600727c4c238b 100644 --- a/src/coreclr/vm/jithelpers.cpp +++ b/src/coreclr/vm/jithelpers.cpp @@ -724,6 +724,7 @@ extern "C" PCODE QCALLTYPE ResolveVirtualFunctionPointer(QCall::ObjectHandleOnSt } else { + GCX_PREEMP(); // This is the new way of resolving a virtual call, including generic virtual methods. // The code is now also used by reflection, remoting etc. addr = pStaticMD->GetMultiCallableAddrOfVirtualizedCode(&objRef, staticTH); diff --git a/src/coreclr/vm/method.cpp b/src/coreclr/vm/method.cpp index a71ea6efd1a0c2..ac0d91b15957e8 100644 --- a/src/coreclr/vm/method.cpp +++ b/src/coreclr/vm/method.cpp @@ -619,7 +619,7 @@ PCODE MethodDesc::GetMethodEntryPoint() { THROWS; GC_NOTRIGGER; - MODE_ANY; + MODE_PREEMPTIVE; SUPPORTS_DAC; } CONTRACTL_END; @@ -1602,7 +1602,6 @@ MethodDesc *MethodDesc::GetExistingWrappedMethodDesc() { THROWS; GC_NOTRIGGER; - MODE_ANY; } CONTRACTL_END; @@ -1937,6 +1936,7 @@ MethodDesc* MethodDesc::ResolveGenericVirtualMethod(OBJECTREF *orThis) { CONTRACT(MethodDesc *) { + MODE_PREEMPTIVE; THROWS; GC_TRIGGERS; @@ -1949,7 +1949,12 @@ MethodDesc* MethodDesc::ResolveGenericVirtualMethod(OBJECTREF *orThis) CONTRACT_END; // Method table of target (might be instantiated) - MethodTable *pObjMT = (*orThis)->GetMethodTable(); + MethodTable *pObjMT; + + { + GCX_COOP(); + pObjMT = (*orThis)->GetMethodTable(); + } // This is the static method descriptor describing the call. // It is not the destination of the call, which we must compute. @@ -2007,7 +2012,7 @@ PCODE MethodDesc::GetSingleCallableAddrOfCodeForUnmanagedCallersOnly() { THROWS; GC_NOTRIGGER; - MODE_ANY; + MODE_PREEMPTIVE; PRECONDITION(HasUnmanagedCallersOnlyAttribute()); } CONTRACTL_END; @@ -2028,16 +2033,22 @@ PCODE MethodDesc::GetSingleCallableAddrOfCodeForUnmanagedCallersOnly() //******************************************************************************* PCODE MethodDesc::GetSingleCallableAddrOfVirtualizedCode(OBJECTREF *orThis, TypeHandle staticTH) { - WRAPPER_NO_CONTRACT; - PRECONDITION(IsVtableMethod()); + CONTRACTL + { + THROWS; // Resolving a generic virtual method can throw + GC_TRIGGERS; + MODE_PREEMPTIVE; + } + CONTRACTL_END; - MethodTable *pObjMT = (*orThis)->GetMethodTable(); + PRECONDITION(IsVtableMethod()); + if (HasMethodInstantiation()) { CheckRestore(); MethodDesc *pResultMD = ResolveGenericVirtualMethod(orThis); - + // If we're remoting this call we can't call directly on the returned // method desc, we need to go through a stub that guarantees we end up // in the remoting handler. The stub we use below is normally just for @@ -2045,16 +2056,21 @@ PCODE MethodDesc::GetSingleCallableAddrOfVirtualizedCode(OBJECTREF *orThis, Type // where we could end up bypassing the remoting system), but it serves // our purpose here (basically pushes our correctly instantiated, // resolved method desc on the stack and calls the remoting code). - + return pResultMD->GetSingleCallableAddrOfCode(); } - + if (IsInterface()) { MethodDesc * pTargetMD = MethodTable::GetMethodDescForInterfaceMethodAndServer(staticTH,this,orThis); return pTargetMD->GetSingleCallableAddrOfCode(); } - + + MethodTable *pObjMT; + { + GCX_COOP(); + pObjMT = (*orThis)->GetMethodTable(); + } return pObjMT->GetRestoredSlot(GetSlot()); } @@ -2062,6 +2078,7 @@ MethodDesc* MethodDesc::GetMethodDescOfVirtualizedCode(OBJECTREF *orThis, TypeHa { CONTRACT(MethodDesc*) { + MODE_PREEMPTIVE; THROWS; GC_TRIGGERS; @@ -2070,8 +2087,6 @@ MethodDesc* MethodDesc::GetMethodDescOfVirtualizedCode(OBJECTREF *orThis, TypeHa POSTCONDITION(RETVAL != NULL); } CONTRACT_END; - // Method table of target (might be instantiated) - MethodTable *pObjMT = (*orThis)->GetMethodTable(); // This is the static method descriptor describing the call. // It is not the destination of the call, which we must compute. @@ -2088,6 +2103,12 @@ MethodDesc* MethodDesc::GetMethodDescOfVirtualizedCode(OBJECTREF *orThis, TypeHa RETURN(MethodTable::GetMethodDescForInterfaceMethodAndServer(staticTH, pStaticMD, orThis)); } + // Method table of target (might be instantiated) + MethodTable *pObjMT; + { + GCX_COOP(); + pObjMT = (*orThis)->GetMethodTable(); + } RETURN(pObjMT->GetMethodDescForSlot(pStaticMD->GetSlot())); } @@ -2099,6 +2120,7 @@ PCODE MethodDesc::GetMultiCallableAddrOfVirtualizedCode(OBJECTREF *orThis, TypeH { CONTRACT(PCODE) { + MODE_PREEMPTIVE; THROWS; GC_TRIGGERS; POSTCONDITION(RETVAL != NULL); @@ -2294,7 +2316,7 @@ PCODE MethodDesc::GetCallTarget(OBJECTREF* pThisObj, TypeHandle ownerType) { THROWS; // Resolving a generic virtual method can throw GC_TRIGGERS; - MODE_COOPERATIVE; + MODE_PREEMPTIVE; } CONTRACTL_END @@ -2789,7 +2811,7 @@ PCODE MethodDesc::GetTemporaryEntryPoint() { THROWS; GC_NOTRIGGER; - MODE_ANY; + MODE_PREEMPTIVE; } CONTRACTL_END; @@ -2864,7 +2886,7 @@ void MethodDesc::EnsureTemporaryEntryPointCore(AllocMemTracker *pamTracker) { THROWS; GC_NOTRIGGER; - MODE_ANY; + MODE_PREEMPTIVE; } CONTRACTL_END; diff --git a/src/coreclr/vm/method.hpp b/src/coreclr/vm/method.hpp index dc0c6b3cef2b08..efd3707e0a89d9 100644 --- a/src/coreclr/vm/method.hpp +++ b/src/coreclr/vm/method.hpp @@ -3891,9 +3891,6 @@ class InstantiatedMethodDesc final : public MethodDesc WORD m_wNumGenericArgs; public: - static InstantiatedMethodDesc *FindOrCreateExactClassMethod(MethodTable *pExactMT, - MethodDesc *pCanonicalMD); - static InstantiatedMethodDesc* FindLoadedInstantiatedMethodDesc(MethodTable *pMT, mdMethodDef methodDef, Instantiation methodInst, diff --git a/src/coreclr/vm/methodtable.cpp b/src/coreclr/vm/methodtable.cpp index 50e628b85b0cec..3ac2b398542e9c 100644 --- a/src/coreclr/vm/methodtable.cpp +++ b/src/coreclr/vm/methodtable.cpp @@ -475,7 +475,7 @@ PTR_MethodTable InterfaceInfo_t::GetApproxMethodTable(Module * pContainingModule { THROWS; GC_TRIGGERS; - MODE_COOPERATIVE; + MODE_PREEMPTIVE; PRECONDITION(CheckPointer(pItfMD)); PRECONDITION(pItfMD->IsInterface()); PRECONDITION(!ownerType.IsNull()); @@ -483,14 +483,23 @@ PTR_MethodTable InterfaceInfo_t::GetApproxMethodTable(Module * pContainingModule POSTCONDITION(CheckPointer(RETVAL)); } CONTRACT_END; - VALIDATEOBJECTREF(*pServer); +#ifdef DEBUG + { + GCX_COOP(); + VALIDATEOBJECTREF(*pServer); + } +#endif #ifdef _DEBUG MethodTable * pItfMT = ownerType.GetMethodTable(); _ASSERTE(pItfMT != NULL); #endif // _DEBUG - MethodTable *pServerMT = (*pServer)->GetMethodTable(); + MethodTable *pServerMT; + { + GCX_COOP(); + pServerMT = (*pServer)->GetMethodTable(); + } _ASSERTE(pServerMT != NULL); #ifdef FEATURE_COMINTEROP @@ -516,15 +525,19 @@ PTR_MethodTable InterfaceInfo_t::GetApproxMethodTable(Module * pContainingModule && !TypeHandle(pServerMT).CanCastTo(ownerType)) // we need to make sure object doesn't implement this interface in a natural way { TypeHandle implTypeHandle; - OBJECTREF obj = *pServer; + { + GCX_COOP(); - GCPROTECT_BEGIN(obj); - OBJECTREF implTypeRef = DynamicInterfaceCastable::GetInterfaceImplementation(&obj, ownerType); - _ASSERTE(implTypeRef != NULL); + OBJECTREF obj = *pServer; - ReflectClassBaseObject *implTypeObj = ((ReflectClassBaseObject *)OBJECTREFToObject(implTypeRef)); - implTypeHandle = implTypeObj->GetType(); - GCPROTECT_END(); + GCPROTECT_BEGIN(obj); + OBJECTREF implTypeRef = DynamicInterfaceCastable::GetInterfaceImplementation(&obj, ownerType); + _ASSERTE(implTypeRef != NULL); + + ReflectClassBaseObject *implTypeObj = ((ReflectClassBaseObject *)OBJECTREFToObject(implTypeRef)); + implTypeHandle = implTypeObj->GetType(); + GCPROTECT_END(); + } RETURN(implTypeHandle.GetMethodTable()->GetMethodDescForInterfaceMethod(ownerType, pItfMD, TRUE /* throwOnConflict */)); } @@ -542,7 +555,7 @@ MethodDesc *MethodTable::GetMethodDescForComInterfaceMethod(MethodDesc *pItfMD) { THROWS; GC_TRIGGERS; - MODE_COOPERATIVE; + MODE_PREEMPTIVE; PRECONDITION(CheckPointer(pItfMD)); PRECONDITION(pItfMD->IsInterface()); PRECONDITION(IsComObjectType()); @@ -3537,7 +3550,11 @@ BOOL MethodTable::RunClassInitEx(OBJECTREF *pThrowable) MethodTable * pCanonMT = GetCanonicalMethodTable(); // Call the code method without touching MethodDesc if possible - PCODE pCctorCode = pCanonMT->GetRestoredSlot(pCanonMT->GetClassConstructorSlot()); + PCODE pCctorCode; + { + GCX_PREEMP(); + pCctorCode = pCanonMT->GetRestoredSlot(pCanonMT->GetClassConstructorSlot()); + } MethodTable* instantiatingArg = pCanonMT->IsSharedByGenericInstantiations() ? this : nullptr; UnmanagedCallersOnlyCaller caller(METHOD__INITHELPERS__CALLCLASSCONSTRUCTOR); caller.InvokeThrowing(pCctorCode, instantiatingArg); @@ -7782,7 +7799,7 @@ PCODE MethodTable::GetRestoredSlot(DWORD slotNumber) CONTRACTL { THROWS; GC_NOTRIGGER; - MODE_ANY; + MODE_PREEMPTIVE; SUPPORTS_DAC; } CONTRACTL_END; diff --git a/src/coreclr/vm/methodtable.inl b/src/coreclr/vm/methodtable.inl index 5bd6ddf711b680..e6b343e86a2e63 100644 --- a/src/coreclr/vm/methodtable.inl +++ b/src/coreclr/vm/methodtable.inl @@ -407,7 +407,7 @@ inline MethodDesc* MethodTable::GetMethodDescForSlot(DWORD slot) { THROWS; GC_NOTRIGGER; - MODE_ANY; + MODE_PREEMPTIVE; } CONTRACTL_END; diff --git a/src/coreclr/vm/prestub.cpp b/src/coreclr/vm/prestub.cpp index ac9655c489ee0e..89da6e3d74e56a 100644 --- a/src/coreclr/vm/prestub.cpp +++ b/src/coreclr/vm/prestub.cpp @@ -3758,6 +3758,8 @@ extern "C" SIZE_T STDCALL DynamicHelperWorker(TransitionBlock * pTransitionBlock if (objRef == NULL) COMPlusThrow(kNullReferenceException); + GCX_PREEMP(); + // Duplicated logic from JIT_VirtualFunctionPointer_Framed if (!pMD->IsVtableMethod()) { diff --git a/src/coreclr/vm/qcall.h b/src/coreclr/vm/qcall.h index 079f93780d40df..6cf2a33975c226 100644 --- a/src/coreclr/vm/qcall.h +++ b/src/coreclr/vm/qcall.h @@ -194,6 +194,12 @@ class QCall { Object** m_ppObject; + bool IsNull() const + { + LIMITED_METHOD_CONTRACT; + return *m_ppObject == NULL; + } + OBJECTREF Get() { LIMITED_METHOD_CONTRACT; diff --git a/src/coreclr/vm/reflectioninvocation.cpp b/src/coreclr/vm/reflectioninvocation.cpp index 3b0b32dd32ec20..2b623fb397ae22 100644 --- a/src/coreclr/vm/reflectioninvocation.cpp +++ b/src/coreclr/vm/reflectioninvocation.cpp @@ -443,13 +443,16 @@ extern "C" void QCALLTYPE RuntimeMethodHandle_InvokeMethod( // This is duplicated logic from MethodDesc::GetCallTarget PCODE pTarget; - if (pMeth->IsVtableMethod()) { - pTarget = pMeth->GetSingleCallableAddrOfVirtualizedCode(&gc.target, ownerType); - } - else - { - pTarget = pMeth->GetSingleCallableAddrOfCode(); + GCX_PREEMP(); + if (pMeth->IsVtableMethod()) + { + pTarget = pMeth->GetSingleCallableAddrOfVirtualizedCode(&gc.target, ownerType); + } + else + { + pTarget = pMeth->GetSingleCallableAddrOfCode(); + } } callDescrData.pTarget = pTarget; diff --git a/src/coreclr/vm/runtimehandles.cpp b/src/coreclr/vm/runtimehandles.cpp index 1e1fb0c0222cae..0857ebee305668 100644 --- a/src/coreclr/vm/runtimehandles.cpp +++ b/src/coreclr/vm/runtimehandles.cpp @@ -1947,22 +1947,23 @@ extern "C" MethodDesc* QCALLTYPE RuntimeMethodHandle_GetStubIfNeededSlow(MethodD BEGIN_QCALL; - GCX_COOP(); + TypeHandle* inst = NULL; + DWORD ntypars = 0; + if (pMethod->IsAsyncVariantMethod()) { // do not report async variants to reflection. pMethod = pMethod->GetOrdinaryVariant(/*allowInstParam*/ false); } - + TypeHandle instType = declaringTypeHandle.AsTypeHandle(); - - TypeHandle* inst = NULL; - DWORD ntypars = 0; - + // Construct TypeHandle array for instantiation. - if (methodInstantiation.Get() != NULL) + if (!methodInstantiation.IsNull()) { + GCX_COOP(); + ntypars = ((PTRARRAYREF)methodInstantiation.Get())->GetNumComponents(); size_t size = ntypars * sizeof(TypeHandle); @@ -2037,7 +2038,10 @@ extern "C" void QCALLTYPE RuntimeMethodHandle_GetMethodBody(MethodDesc* pMethod, { MethodDesc* pMethodIL = pMethod; if (pMethod->IsWrapperStub()) + { + GCX_PREEMP(); pMethodIL = pMethod->GetWrappedMethodDesc(); + } pILHeader = pMethodIL->GetILHeader(); } diff --git a/src/coreclr/vm/virtualcallstub.cpp b/src/coreclr/vm/virtualcallstub.cpp index cc538a7b990c5a..dd256a50499126 100644 --- a/src/coreclr/vm/virtualcallstub.cpp +++ b/src/coreclr/vm/virtualcallstub.cpp @@ -1049,7 +1049,19 @@ PCODE VirtualCallStubManager::GetCallStub(DispatchToken token) { if ((stub = (PCODE)(lookups->Find(&probeL))) == CALL_STUB_EMPTY_ENTRY) { - LookupHolder *pLookupHolder = GenerateLookupStub(addrOfResolver, token.To_SIZE_T()); + LookupHolder *pLookupHolder; + bool reenteredCooperativeGCMode = PerfMap::IsEnabled(); + { + GCX_MAYBE_PREEMP(reenteredCooperativeGCMode); + pLookupHolder = GenerateLookupStub(addrOfResolver, token.To_SIZE_T()); + } + + if (reenteredCooperativeGCMode) + { + // The prober may have been invalidated by reentering cooperative GC mode, reset it + BOOL success = lookups->SetUpProber(token.To_SIZE_T(), 0, &probeL); + _ASSERTE(success); + } stub = (PCODE) (lookups->Add((size_t)(pLookupHolder->stub()->entryPoint()), &probeL)); } } @@ -1080,7 +1092,20 @@ PCODE VirtualCallStubManager::GetVTableCallStub(DWORD slot) { if ((stub = (PCODE)(vtableCallers->Find(&probe))) == CALL_STUB_EMPTY_ENTRY) { - VTableCallHolder *pHolder = GenerateVTableCallStub(slot); + VTableCallHolder *pHolder; + + bool reenteredCooperativeGCMode = PerfMap::IsEnabled(); + { + GCX_MAYBE_PREEMP(reenteredCooperativeGCMode); + pHolder = GenerateVTableCallStub(slot); + } + + if (reenteredCooperativeGCMode) + { + // The prober may have been invalidated by reentering cooperative GC mode, reset it + BOOL success = vtableCallers->SetUpProber(DispatchToken::CreateDispatchToken(slot).To_SIZE_T(), 0, &probe); + _ASSERTE(success); + } stub = (PCODE)(vtableCallers->Add((size_t)(pHolder->stub()->entryPoint()), &probe)); } } @@ -2050,14 +2075,24 @@ PCODE VirtualCallStubManager::ResolveWorker(StubCallSite* pCallSite, } #endif // TARGET_X86 && !UNIX_X86_ABI - pResolveHolder = GenerateResolveStub(pResolverFcn, - pBackPatchFcn, - token.To_SIZE_T() + bool reenteredCooperativeGCMode = PerfMap::IsEnabled(); + { + GCX_MAYBE_PREEMP(reenteredCooperativeGCMode); + pResolveHolder = GenerateResolveStub(pResolverFcn, + pBackPatchFcn, + token.To_SIZE_T() #if defined(TARGET_X86) && !defined(UNIX_X86_ABI) - , stackArgumentsSize + , stackArgumentsSize #endif ); + } + if (reenteredCooperativeGCMode) + { + // The prober may have been invalidated by reentering cooperative GC mode, reset it + BOOL success = resolvers->SetUpProber(token.To_SIZE_T(), 0, &probeR); + _ASSERTE(success); + } // Add the resolve entrypoint into the cache. //@TODO: Can we store a pointer to the holder rather than the entrypoint? resolvers->Add((size_t)(pResolveHolder->stub()->resolveEntryPoint()), &probeR); @@ -2091,9 +2126,12 @@ PCODE VirtualCallStubManager::ResolveWorker(StubCallSite* pCallSite, if (addrOfDispatch == CALL_STUB_EMPTY_ENTRY) { PCODE addrOfFail = pResolveHolder->stub()->failEntryPoint(); - bool reenteredCooperativeGCMode = false; - pDispatchHolder = GenerateDispatchStub( - target, addrOfFail, objectType, token.To_SIZE_T(), &reenteredCooperativeGCMode); + bool reenteredCooperativeGCMode = PerfMap::IsEnabled(); + { + GCX_MAYBE_PREEMP(reenteredCooperativeGCMode); + pDispatchHolder = GenerateDispatchStub( + target, addrOfFail, objectType, token.To_SIZE_T(), &reenteredCooperativeGCMode); + } if (reenteredCooperativeGCMode) { // The prober may have been invalidated by reentering cooperative GC mode, reset it @@ -2204,9 +2242,12 @@ PCODE VirtualCallStubManager::ResolveWorker(StubCallSite* pCallSite, // so we may have to create it now ResolveHolder* pResolveHolder = ResolveHolder::FromResolveEntry(pCallSite->GetSiteTarget()); PCODE addrOfFail = pResolveHolder->stub()->failEntryPoint(); - bool reenteredCooperativeGCMode = false; - pDispatchHolder = GenerateDispatchStub( - target, addrOfFail, objectType, token.To_SIZE_T(), &reenteredCooperativeGCMode); + bool reenteredCooperativeGCMode = PerfMap::IsEnabled(); + { + GCX_MAYBE_PREEMP(reenteredCooperativeGCMode); + pDispatchHolder = GenerateDispatchStub( + target, addrOfFail, objectType, token.To_SIZE_T(), &reenteredCooperativeGCMode); + } if (reenteredCooperativeGCMode) { // The prober may have been invalidated by reentering cooperative GC mode, reset it @@ -2795,7 +2836,6 @@ DispatchHolder *VirtualCallStubManager::GenerateDispatchStub(PCODE ad PRECONDITION(addrOfFail != NULL); PRECONDITION(CheckPointer(pMTExpected)); PRECONDITION(pMayHaveReenteredCooperativeGCMode != nullptr); - PRECONDITION(!*pMayHaveReenteredCooperativeGCMode); POSTCONDITION(CheckPointer(RETVAL)); } CONTRACT_END; @@ -2882,7 +2922,6 @@ DispatchHolder *VirtualCallStubManager::GenerateDispatchStubLong(PCODE PRECONDITION(addrOfFail != NULL); PRECONDITION(CheckPointer(pMTExpected)); PRECONDITION(pMayHaveReenteredCooperativeGCMode != nullptr); - PRECONDITION(!*pMayHaveReenteredCooperativeGCMode); POSTCONDITION(CheckPointer(RETVAL)); } CONTRACT_END; @@ -3072,8 +3111,12 @@ ResolveCacheElem *VirtualCallStubManager::GenerateResolveCacheElem(void *addrOfC CONSISTENCY_CHECK(CheckPointer(pMTExpected)); //allocate from the requisite heap and set the appropriate fields - ResolveCacheElem *e = (ResolveCacheElem*) (void*) + ResolveCacheElem *e; + { + GCX_NOTRIGGER(); + e = (ResolveCacheElem*) (void*) cache_entry_heap->AllocAlignedMem(sizeof(ResolveCacheElem), CODE_SIZE_ALIGN); + } e->pMT = pMTExpected; e->token = token; From e1f7ef189f4f468140aed20ffe0af40012a59675 Mon Sep 17 00:00:00 2001 From: David Wrighton Date: Thu, 4 Jun 2026 15:28:08 -0700 Subject: [PATCH 03/19] Update genmeth.cpp --- src/coreclr/vm/genmeth.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coreclr/vm/genmeth.cpp b/src/coreclr/vm/genmeth.cpp index 460325e24d2a2b..008e7cafc2dc57 100644 --- a/src/coreclr/vm/genmeth.cpp +++ b/src/coreclr/vm/genmeth.cpp @@ -718,9 +718,9 @@ MethodDesc::FindOrCreateAssociatedMethodDesc(MethodDesc* pDefMD, { CONTRACT(MethodDesc*) { - MODE_PREEMPTIVE; THROWS; if (allowCreate) { MODE_PREEMPTIVE; } else { MODE_ANY; } + if (allowCreate) { GC_TRIGGERS; } else { GC_NOTRIGGER; } if (!allowCreate) { SUPPORTS_DAC; } INJECT_FAULT(COMPlusThrowOM();); From 029f87db531fd9e981ea81029fc0ffde34d51b8a Mon Sep 17 00:00:00 2001 From: David Wrighton Date: Fri, 5 Jun 2026 15:00:12 -0700 Subject: [PATCH 04/19] Adjust based on test failures --- src/coreclr/vm/comdelegate.cpp | 18 +++++++++++++----- src/coreclr/vm/genericdict.cpp | 1 + src/coreclr/vm/prestub.cpp | 7 ++++--- src/coreclr/vm/virtualcallstub.cpp | 23 ++++++++++++++++++++--- 4 files changed, 38 insertions(+), 11 deletions(-) diff --git a/src/coreclr/vm/comdelegate.cpp b/src/coreclr/vm/comdelegate.cpp index 80676ebaae5535..4a5c7e8da58319 100644 --- a/src/coreclr/vm/comdelegate.cpp +++ b/src/coreclr/vm/comdelegate.cpp @@ -967,7 +967,7 @@ static PCODE GetVirtualCallStub(MethodDesc *method, TypeHandle scopeType) { THROWS; GC_TRIGGERS; - MODE_ANY; + MODE_PREEMPTIVE; INJECT_FAULT(COMPlusThrowOM()); // from MetaSig::SizeOfArgStack } CONTRACTL_END; @@ -1243,7 +1243,11 @@ void COMDelegate::BindToMethod(DELEGATEREF *pRefThis, // Since this is an open delegate over a virtual method we cannot virtualize the call target now. So the shuffle thunk // needs to jump to another stub (this time provided by the VirtualStubManager) that will virtualize the call at // runtime. - PCODE pTargetCall = GetVirtualCallStub(pTargetMethod, TypeHandle(pExactMethodType)); + PCODE pTargetCall; + { + GCX_PREEMP(); + pTargetCall = GetVirtualCallStub(pTargetMethod, TypeHandle(pExactMethodType)); + } refRealDelegate->SetMethodPtrAux(pTargetCall); refRealDelegate->SetInvocationCount((INT_PTR)(void *)pTargetMethod); } @@ -1605,13 +1609,13 @@ extern "C" void QCALLTYPE Delegate_InitializeVirtualCallStub(QCall::ObjectHandle BEGIN_QCALL; - GCX_COOP(); - MethodDesc *pMeth = NonVirtualEntry2MethodDesc((PCODE)method); _ASSERTE(pMeth); _ASSERTE(!pMeth->IsStatic() && pMeth->IsVirtual()); PCODE target = GetVirtualCallStub(pMeth, TypeHandle(pMeth->GetMethodTable())); + GCX_COOP(); + DELEGATEREF refThis = (DELEGATEREF)d.Get(); refThis->SetMethodPtrAux(target); refThis->SetInvocationCount((INT_PTR)(void*)pMeth); @@ -1755,7 +1759,11 @@ extern "C" void QCALLTYPE Delegate_Construct(QCall::ObjectHandleOnStack _this, Q // set the ptr aux according to what is needed, if virtual need to call make virtual stub dispatch if (!pMeth->IsStatic() && pMeth->IsVirtual() && !pMeth->GetMethodTable()->IsValueType()) { - PCODE pTargetCall = GetVirtualCallStub(pMeth, TypeHandle(pMeth->GetMethodTable())); + PCODE pTargetCall; + { + GCX_PREEMP(); + pTargetCall = GetVirtualCallStub(pMeth, TypeHandle(pMeth->GetMethodTable())); + } refThis->SetMethodPtrAux(pTargetCall); refThis->SetInvocationCount((INT_PTR)(void *)pMeth); } diff --git a/src/coreclr/vm/genericdict.cpp b/src/coreclr/vm/genericdict.cpp index e9d15c0fa6bb63..ce2cfd806363f8 100644 --- a/src/coreclr/vm/genericdict.cpp +++ b/src/coreclr/vm/genericdict.cpp @@ -674,6 +674,7 @@ Dictionary::PopulateEntry( Module * pModule /* = NULL */) { CONTRACTL { + MODE_PREEMPTIVE; THROWS; GC_TRIGGERS; } CONTRACTL_END; diff --git a/src/coreclr/vm/prestub.cpp b/src/coreclr/vm/prestub.cpp index 89da6e3d74e56a..e3b6e43edaab1e 100644 --- a/src/coreclr/vm/prestub.cpp +++ b/src/coreclr/vm/prestub.cpp @@ -2865,14 +2865,12 @@ EXTERN_C PCODE STDCALL ExternalMethodFixupWorker(TransitionBlock * pTransitionBl if (fVirtual) { - GCX_COOP_THREAD_EXISTS(CURRENT_THREAD); - // Get the stub manager for this module VirtualCallStubManager *pMgr = pModule->GetLoaderAllocator()->GetVirtualCallStubManager(); OBJECTREF *protectedObj = pEMFrame->GetThisPtr(); _ASSERTE(protectedObj != NULL); - if (*protectedObj == NULL) { + if (!*protectedObj) { COMPlusThrow(kNullReferenceException); } @@ -2911,6 +2909,8 @@ EXTERN_C PCODE STDCALL ExternalMethodFixupWorker(TransitionBlock * pTransitionBl #endif } + GCX_COOP_THREAD_EXISTS(CURRENT_THREAD); + // We lost the race or the R2R image was generated without cached interface dispatch support, simply do the resolution in pure C++ DispatchToken token; if (pMT->IsInterface()) @@ -2936,6 +2936,7 @@ EXTERN_C PCODE STDCALL ExternalMethodFixupWorker(TransitionBlock * pTransitionBl token = pMT->GetLoaderAllocator()->GetDispatchToken(pMT->GetTypeID(), slot); StubCallSite callSite(pIndirection, pEMFrame->GetReturnAddress()); + GCX_COOP_THREAD_EXISTS(CURRENT_THREAD); pCode = pMgr->ResolveWorker(&callSite, protectedObj, token, STUB_CODE_BLOCK_VSD_LOOKUP_STUB); } else diff --git a/src/coreclr/vm/virtualcallstub.cpp b/src/coreclr/vm/virtualcallstub.cpp index dd256a50499126..7404ca72acbd92 100644 --- a/src/coreclr/vm/virtualcallstub.cpp +++ b/src/coreclr/vm/virtualcallstub.cpp @@ -98,11 +98,23 @@ struct CachedIndirectionCellBlockListNode BYTE* GenerateDispatchStubCellEntryMethodDesc(LoaderAllocator *pLoaderAllocator, TypeHandle ownerType, MethodDesc *pMD, LCGMethodResolver *pResolver) { + CONTRACTL { + THROWS; + GC_TRIGGERS; + MODE_PREEMPTIVE; + } CONTRACTL_END; + return GenerateDispatchStubCellEntrySlot(pLoaderAllocator, ownerType, pMD->GetSlot(), pResolver); } BYTE* GenerateDispatchStubCellEntrySlot(LoaderAllocator *pLoaderAllocator, TypeHandle ownerType, int methodSlot, LCGMethodResolver *pResolver) { + CONTRACTL { + THROWS; + GC_TRIGGERS; + MODE_PREEMPTIVE; + } CONTRACTL_END; + VirtualCallStubManager * pMgr = pLoaderAllocator->GetVirtualCallStubManager(); DispatchToken token = VirtualCallStubManager::GetTokenFromOwnerAndSlot(ownerType, methodSlot); @@ -992,7 +1004,7 @@ DispatchToken VirtualCallStubManager::GetTokenFromOwnerAndSlot(TypeHandle ownerT { THROWS; GC_TRIGGERS; - MODE_ANY; + MODE_PREEMPTIVE; INJECT_FAULT(COMPlusThrowOM();); } CONTRACTL_END @@ -1015,7 +1027,7 @@ PCODE VirtualCallStubManager::GetCallStub(TypeHandle ownerType, MethodDesc *pMD) CONTRACTL { THROWS; GC_TRIGGERS; - MODE_ANY; + MODE_PREEMPTIVE; PRECONDITION(CheckPointer(pMD)); PRECONDITION(!pMD->IsInterface() || ownerType.GetMethodTable()->HasSameTypeDefAs(pMD->GetMethodTable())); INJECT_FAULT(COMPlusThrowOM();); @@ -1482,7 +1494,12 @@ extern "C" PCODE CID_VirtualOpenDelegateDispatchWorker(TransitionBlock * pTransi GCStress::MaybeTriggerAndProtect(pObj); - DispatchToken token = VirtualCallStubManager::GetTokenFromOwnerAndSlot(TypeHandle(pTargetMD->GetMethodTable()), pTargetMD->GetSlot()); + DispatchToken token; + + { + GCX_PREEMP(); + token = VirtualCallStubManager::GetTokenFromOwnerAndSlot(TypeHandle(pTargetMD->GetMethodTable()), pTargetMD->GetSlot()); + } target = CachedInterfaceDispatchResolveWorker(NULL, protectedObj, token); #if _DEBUG From d94d6a5f7bc05bdcdc75030016f25b461b6d6540 Mon Sep 17 00:00:00 2001 From: David Wrighton Date: Fri, 5 Jun 2026 15:50:57 -0700 Subject: [PATCH 05/19] Fix wasm build break --- src/coreclr/vm/threads.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/coreclr/vm/threads.cpp b/src/coreclr/vm/threads.cpp index b10b13237473ad..1fa70acc931304 100644 --- a/src/coreclr/vm/threads.cpp +++ b/src/coreclr/vm/threads.cpp @@ -1026,11 +1026,13 @@ void InitThreadManagerPerfMapData() GC_TRIGGERS; } CONTRACTL_END; +#ifndef FEATURE_PORTABLE_HELPERS if (IsWriteBarrierCopyEnabled()) { size_t writeBarrierSize = (BYTE*)JIT_PatchedCodeLast - (BYTE*)JIT_PatchedCodeStart; PerfMap::LogStubs(__FUNCTION__, "JIT_CopiedWriteBarriers", (PCODE)s_barrierCopy, writeBarrierSize, PerfMapStubType::Individual); } +#endif // !FEATURE_PORTABLE_HELPERS } //--------------------------------------------------------------------------- From 01bf23e624155f7b890c202f8cd7a7f733ab1a4d Mon Sep 17 00:00:00 2001 From: David Wrighton Date: Thu, 11 Jun 2026 15:11:08 -0700 Subject: [PATCH 06/19] Code review updates --- src/coreclr/vm/amd64/cgenamd64.cpp | 2 ++ src/coreclr/vm/prestub.cpp | 4 ++++ 2 files changed, 6 insertions(+) diff --git a/src/coreclr/vm/amd64/cgenamd64.cpp b/src/coreclr/vm/amd64/cgenamd64.cpp index 5d2bf853b51755..30311f13032b9c 100644 --- a/src/coreclr/vm/amd64/cgenamd64.cpp +++ b/src/coreclr/vm/amd64/cgenamd64.cpp @@ -610,6 +610,8 @@ DWORD GetOffsetAtEndOfFunction(ULONGLONG uImageBase, PerfMap::LogStubs(__FUNCTION__, "DynamicHelper", (PCODE)p, size, PerfMapStubType::Individual); #define END_DYNAMIC_HELPER_EMIT() \ + _ASSERTE(pStart + cb == p); \ + while (p < pStart + cbAligned) *p++ = X86_INSTR_INT3; \ ClrFlushInstructionCache(pStartRX, cbAligned); \ return (PCODE)pStartRX diff --git a/src/coreclr/vm/prestub.cpp b/src/coreclr/vm/prestub.cpp index e3b6e43edaab1e..136a9f4b0719e7 100644 --- a/src/coreclr/vm/prestub.cpp +++ b/src/coreclr/vm/prestub.cpp @@ -2871,6 +2871,10 @@ EXTERN_C PCODE STDCALL ExternalMethodFixupWorker(TransitionBlock * pTransitionBl OBJECTREF *protectedObj = pEMFrame->GetThisPtr(); _ASSERTE(protectedObj != NULL); if (!*protectedObj) { + // NOTE: This is in a preemptive block, but the ! operator + // is safe to use on OBJECTREF even in preemptive mode + // (as long as the OBJECTREF is not on a managed object which + // in this case it is not) COMPlusThrow(kNullReferenceException); } From be0225a661ec8058b1826d372d56d8e5af516eff Mon Sep 17 00:00:00 2001 From: David Wrighton Date: Thu, 11 Jun 2026 15:14:03 -0700 Subject: [PATCH 07/19] PerfMap::LogStubs is now MODE_PREEMPTIVE only --- src/coreclr/vm/perfmap.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coreclr/vm/perfmap.cpp b/src/coreclr/vm/perfmap.cpp index 248488d1383dc2..4efb8e36b935b5 100644 --- a/src/coreclr/vm/perfmap.cpp +++ b/src/coreclr/vm/perfmap.cpp @@ -422,7 +422,7 @@ void PerfMap::LogStubs(const char* stubType, const char* stubOwner, PCODE pCode, CONTRACTL { GC_NOTRIGGER; - MODE_ANY; + MODE_PREEMPTIVE; } CONTRACTL_END; From 9d6d2e9fa3792a9f274d1cd6a7d4a10f6f181e6d Mon Sep 17 00:00:00 2001 From: David Wrighton Date: Mon, 15 Jun 2026 15:07:04 -0700 Subject: [PATCH 08/19] Make apply metadata logic happen on in preemptive mode, and fix a few issues around reading EnC fields to use volatile load/store operations as there is no apparent synchronization. --- src/coreclr/debug/ee/debugger.cpp | 9 --------- src/coreclr/debug/ee/funceval.cpp | 5 ++++- src/coreclr/vm/assembly.cpp | 7 +++++-- src/coreclr/vm/assemblynative.cpp | 1 - src/coreclr/vm/ceeload.cpp | 12 ++++++------ src/coreclr/vm/class.cpp | 8 ++++---- src/coreclr/vm/classhash.cpp | 4 ++-- src/coreclr/vm/clsload.cpp | 24 ++++++++++++------------ src/coreclr/vm/encee.cpp | 27 ++++++++++++++------------- src/coreclr/vm/methodtablebuilder.cpp | 2 +- 10 files changed, 48 insertions(+), 51 deletions(-) diff --git a/src/coreclr/debug/ee/debugger.cpp b/src/coreclr/debug/ee/debugger.cpp index cdde8f49dc0821..fdf3e05a55d78f 100644 --- a/src/coreclr/debug/ee/debugger.cpp +++ b/src/coreclr/debug/ee/debugger.cpp @@ -12437,15 +12437,6 @@ HRESULT Debugger::ApplyChangesAndSendResult(DebuggerModule * pDebuggerModule, } else { - // Violation with the following call stack: - // CONTRACT in MethodTableBuilder::InitMethodDesc - // CONTRACT in EEClass::AddMethod - // CONTRACT in EditAndContinueModule::AddMethod - // CONTRACT in EditAndContinueModule::ApplyEditAndContinue - // CONTRACT in EEDbgInterfaceImpl::EnCApplyChanges - // VIOLATED--> CONTRACT in Debugger::ApplyChangesAndSendResult - CONTRACT_VIOLATION(GCViolation); - // Tell the VM to apply the edit hr = g_pEEInterface->EnCApplyChanges( (EditAndContinueModule*)pModule, cbMetadata, pMetadata, cbIL, pIL); diff --git a/src/coreclr/debug/ee/funceval.cpp b/src/coreclr/debug/ee/funceval.cpp index 45723c4a9b8ae2..e6009e0bc88263 100644 --- a/src/coreclr/debug/ee/funceval.cpp +++ b/src/coreclr/debug/ee/funceval.cpp @@ -3202,7 +3202,10 @@ static void DoNormalFuncEval( DebuggerEval *pDE, // Now that all the args are protected, we can go back and deal with generic args and resolving // all their information. // - ResolveFuncEvalGenericArgInfo(pDE); + { + GCX_PREEMP(); + ResolveFuncEvalGenericArgInfo(pDE); + } // // Grab the signature of the method we're working on and do some error checking. diff --git a/src/coreclr/vm/assembly.cpp b/src/coreclr/vm/assembly.cpp index 7bd7c0ae0217a6..b408cb8ed17154 100644 --- a/src/coreclr/vm/assembly.cpp +++ b/src/coreclr/vm/assembly.cpp @@ -1705,7 +1705,8 @@ void Assembly::AddType( CONTRACTL { THROWS; - GC_TRIGGERS; + GC_NOTRIGGER; + MODE_PREEMPTIVE; INJECT_FAULT(COMPlusThrowOM();); } CONTRACTL_END @@ -1731,7 +1732,8 @@ void Assembly::AddExportedType(mdExportedType cl) CONTRACTL { THROWS; - GC_TRIGGERS; + GC_NOTRIGGER; + MODE_PREEMPTIVE; INJECT_FAULT(COMPlusThrowOM();); } CONTRACTL_END @@ -1995,6 +1997,7 @@ void Assembly::SetError(Exception *ex) SetProfilerNotified(); #ifdef PROFILING_SUPPORTED + GCX_PREEMP(); // Only send errors for non-shared assemblies; other assemblies might be successfully completed // in another app domain later. m_pModule->NotifyProfilerLoadFinished(ex->GetHR()); diff --git a/src/coreclr/vm/assemblynative.cpp b/src/coreclr/vm/assemblynative.cpp index 17c2f079eaae04..c35442ce86030f 100644 --- a/src/coreclr/vm/assemblynative.cpp +++ b/src/coreclr/vm/assemblynative.cpp @@ -1408,7 +1408,6 @@ extern "C" void QCALLTYPE AssemblyNative_ApplyUpdate( _ASSERTE(ilDeltaLength > 0); #ifdef FEATURE_METADATA_UPDATER - GCX_COOP(); { if (CORDebuggerAttached()) { diff --git a/src/coreclr/vm/ceeload.cpp b/src/coreclr/vm/ceeload.cpp index ab5a28c591051e..d4c08fe7bc32e9 100644 --- a/src/coreclr/vm/ceeload.cpp +++ b/src/coreclr/vm/ceeload.cpp @@ -214,8 +214,9 @@ void Module::UpdateNewlyAddedTypes() { CONTRACTL { + MODE_PREEMPTIVE; THROWS; - GC_TRIGGERS; + GC_NOTRIGGER; INJECT_FAULT(COMPlusThrowOM();); } CONTRACTL_END @@ -278,7 +279,7 @@ void Module::NotifyProfilerLoadFinished(HRESULT hr) THROWS; GC_TRIGGERS; INJECT_FAULT(COMPlusThrowOM()); - MODE_ANY; + MODE_PREEMPTIVE; } CONTRACTL_END; @@ -296,7 +297,6 @@ void Module::NotifyProfilerLoadFinished(HRESULT hr) { BEGIN_PROFILER_CALLBACK(CORProfilerTrackModuleLoads()); { - GCX_PREEMP(); (&g_profControlBlock)->ModuleLoadFinished((ModuleID) this, hr); if (SUCCEEDED(hr)) @@ -640,8 +640,8 @@ void Module::ApplyMetaData() CONTRACTL { THROWS; - GC_TRIGGERS; - MODE_ANY; + GC_NOTRIGGER; + MODE_PREEMPTIVE; } CONTRACTL_END; @@ -957,7 +957,7 @@ void Module::SetDynamicRvaField(mdToken token, TADDR blobAddress) { THROWS; GC_NOTRIGGER; - MODE_COOPERATIVE; + MODE_PREEMPTIVE; } CONTRACTL_END; diff --git a/src/coreclr/vm/class.cpp b/src/coreclr/vm/class.cpp index 4e228cbd4c4fef..66956a029feb41 100644 --- a/src/coreclr/vm/class.cpp +++ b/src/coreclr/vm/class.cpp @@ -322,7 +322,7 @@ HRESULT EEClass::AddField(MethodTable* pMT, mdFieldDef fieldDef, FieldDesc** ppN { THROWS; GC_NOTRIGGER; - MODE_COOPERATIVE; + MODE_PREEMPTIVE; PRECONDITION(pMT != NULL); PRECONDITION(ppNewFD != NULL); } @@ -442,7 +442,7 @@ HRESULT EEClass::AddFieldDesc( { THROWS; GC_NOTRIGGER; - MODE_COOPERATIVE; + MODE_PREEMPTIVE; PRECONDITION(pMT != NULL); PRECONDITION(ppNewFD != NULL); } @@ -508,7 +508,7 @@ HRESULT EEClass::AddMethod(MethodTable* pMT, mdMethodDef methodDef, MethodDesc** { THROWS; GC_NOTRIGGER; - MODE_COOPERATIVE; + MODE_PREEMPTIVE; PRECONDITION(pMT != NULL); PRECONDITION(methodDef != mdTokenNil); } @@ -776,7 +776,7 @@ HRESULT EEClass::AddMethodDesc( { THROWS; GC_NOTRIGGER; - MODE_COOPERATIVE; + MODE_PREEMPTIVE; PRECONDITION(pMT != NULL); PRECONDITION(methodDef != mdTokenNil); PRECONDITION(ppNewMD != NULL); diff --git a/src/coreclr/vm/classhash.cpp b/src/coreclr/vm/classhash.cpp index 10c73eeff8570a..39fc890474c811 100644 --- a/src/coreclr/vm/classhash.cpp +++ b/src/coreclr/vm/classhash.cpp @@ -676,7 +676,7 @@ BOOL EEClassHashTable::IsNested(ModuleBase *pModule, mdToken token, mdToken *mdE CONTRACTL { if (FORBIDGC_LOADER_USE_ENABLED()) NOTHROW; else THROWS; - if (FORBIDGC_LOADER_USE_ENABLED()) GC_NOTRIGGER; else GC_TRIGGERS; + GC_NOTRIGGER; if (FORBIDGC_LOADER_USE_ENABLED()) FORBID_FAULT; else { INJECT_FAULT(COMPlusThrowOM()); } MODE_ANY; SUPPORTS_DAC; @@ -714,7 +714,7 @@ BOOL EEClassHashTable::IsNested(const NameHandle* pName, mdToken *mdEncloser) CONTRACTL { if (FORBIDGC_LOADER_USE_ENABLED()) NOTHROW; else THROWS; - if (FORBIDGC_LOADER_USE_ENABLED()) GC_NOTRIGGER; else GC_TRIGGERS; + GC_NOTRIGGER; if (FORBIDGC_LOADER_USE_ENABLED()) FORBID_FAULT; else { INJECT_FAULT(COMPlusThrowOM()); } MODE_ANY; SUPPORTS_DAC; diff --git a/src/coreclr/vm/clsload.cpp b/src/coreclr/vm/clsload.cpp index ba86d440ecb2fc..aab83563b46119 100644 --- a/src/coreclr/vm/clsload.cpp +++ b/src/coreclr/vm/clsload.cpp @@ -638,8 +638,8 @@ void ClassLoader::LazyPopulateCaseSensitiveHashTablesDontHaveLock() { INSTANCE_CHECK; THROWS; - GC_TRIGGERS; - MODE_ANY; + GC_NOTRIGGER; + MODE_PREEMPTIVE; INJECT_FAULT(COMPlusThrowOM()); } CONTRACTL_END; @@ -655,8 +655,8 @@ void ClassLoader::LazyPopulateCaseSensitiveHashTables() { INSTANCE_CHECK; THROWS; - GC_TRIGGERS; - MODE_ANY; + GC_NOTRIGGER; + MODE_PREEMPTIVE; INJECT_FAULT(COMPlusThrowOM()); } CONTRACTL_END; @@ -3455,8 +3455,8 @@ VOID ClassLoader::AddAvailableClassDontHaveLock(Module *pModule, { INSTANCE_CHECK; THROWS; - GC_TRIGGERS; - MODE_ANY; + GC_NOTRIGGER; + MODE_PREEMPTIVE; INJECT_FAULT(COMPlusThrowOM();); } CONTRACTL_END @@ -3490,8 +3490,8 @@ VOID ClassLoader::AddAvailableClassHaveLock( { INSTANCE_CHECK; THROWS; - GC_TRIGGERS; - MODE_ANY; + GC_NOTRIGGER; + MODE_PREEMPTIVE; INJECT_FAULT(COMPlusThrowOM();); } CONTRACTL_END @@ -3564,8 +3564,8 @@ VOID ClassLoader::AddExportedTypeDontHaveLock(Module *pManifestModule, { INSTANCE_CHECK; THROWS; - GC_TRIGGERS; - MODE_ANY; + GC_NOTRIGGER; + MODE_PREEMPTIVE; INJECT_FAULT(COMPlusThrowOM();); } CONTRACTL_END @@ -3589,8 +3589,8 @@ VOID ClassLoader::AddExportedTypeHaveLock(Module *pManifestModule, { INSTANCE_CHECK; THROWS; - GC_TRIGGERS; - MODE_ANY; + GC_NOTRIGGER; + MODE_PREEMPTIVE; INJECT_FAULT(COMPlusThrowOM();); } CONTRACTL_END diff --git a/src/coreclr/vm/encee.cpp b/src/coreclr/vm/encee.cpp index 025df8ac2aa77f..9a8bd30c3e13d3 100644 --- a/src/coreclr/vm/encee.cpp +++ b/src/coreclr/vm/encee.cpp @@ -109,7 +109,7 @@ HRESULT EditAndContinueModule::ApplyEditAndContinue( { THROWS; GC_NOTRIGGER; - MODE_COOPERATIVE; + MODE_PREEMPTIVE; } CONTRACTL_END; @@ -340,7 +340,7 @@ HRESULT EditAndContinueModule::UpdateMethod(MethodDesc *pMethod) { THROWS; GC_NOTRIGGER; - MODE_COOPERATIVE; + MODE_PREEMPTIVE; } CONTRACTL_END; @@ -417,7 +417,7 @@ HRESULT EditAndContinueModule::AddMethod(mdMethodDef token) { THROWS; GC_NOTRIGGER; - MODE_COOPERATIVE; + MODE_PREEMPTIVE; } CONTRACTL_END; @@ -493,7 +493,7 @@ HRESULT EditAndContinueModule::AddField(mdFieldDef token) { THROWS; GC_NOTRIGGER; - MODE_COOPERATIVE; + MODE_PREEMPTIVE; } CONTRACTL_END; @@ -1147,7 +1147,7 @@ void EnCFieldDesc::Init(mdFieldDef token, BOOL fIsStatic) { THROWS; GC_NOTRIGGER; - MODE_COOPERATIVE; + MODE_PREEMPTIVE; } CONTRACTL_END; @@ -1627,17 +1627,18 @@ void EnCEEClassData::AddField(EnCAddedFieldElement *pAddedField) // If the list is empty, just add this field as the only entry if (*pList == NULL) { - *pList = pAddedField; + VolatileStore(pList, pAddedField); return; } // Otherwise, add this field to the end of the field list - EnCAddedFieldElement *pCur = *pList; - while (pCur->m_next != NULL) + EnCAddedFieldElement *pCur = VolatileLoad(pList); + EnCAddedFieldElement *pNext; + while (pNext = VolatileLoad(&pCur->m_next), pNext != NULL) { - pCur = pCur->m_next; + pCur = pNext; } - pCur->m_next = pAddedField; + VolatileStore(&pCur->m_next, pAddedField); } #endif // #ifndef DACCESS_COMPILE @@ -1831,7 +1832,7 @@ PTR_EnCFieldDesc EncApproxFieldDescIterator::NextEnC() // We're at the start of the instance list. if ( doInst ) { - m_pCurrListElem = m_encClassData->m_pAddedInstanceFields; + m_pCurrListElem = VolatileLoad(&m_encClassData->m_pAddedInstanceFields); } } @@ -1844,7 +1845,7 @@ PTR_EnCFieldDesc EncApproxFieldDescIterator::NextEnC() // We're at the start of the statics list. if ( doStatic ) { - m_pCurrListElem = m_encClassData->m_pAddedStaticFields; + m_pCurrListElem = VolatileLoad(&m_encClassData->m_pAddedStaticFields); } } @@ -1859,7 +1860,7 @@ PTR_EnCFieldDesc EncApproxFieldDescIterator::NextEnC() // Advance the list pointer and return the element m_encFieldsReturned++; PTR_EnCFieldDesc fd = PTR_EnCFieldDesc(PTR_HOST_MEMBER_TADDR(EnCAddedFieldElement, m_pCurrListElem, m_fieldDesc)); - m_pCurrListElem = m_pCurrListElem->m_next; + m_pCurrListElem = VolatileLoad(&m_pCurrListElem->m_next); return fd; } diff --git a/src/coreclr/vm/methodtablebuilder.cpp b/src/coreclr/vm/methodtablebuilder.cpp index fb16290b29e561..28dafc7a35a5e0 100644 --- a/src/coreclr/vm/methodtablebuilder.cpp +++ b/src/coreclr/vm/methodtablebuilder.cpp @@ -6255,7 +6255,7 @@ MethodTableBuilder::InitMethodDesc( { THROWS; if (fEnC) { GC_NOTRIGGER; } else { GC_TRIGGERS; } - MODE_ANY; + MODE_PREEMPTIVE; } CONTRACTL_END; From 1863d42e32b72122ee54eceaf7e1cea372e2d7c5 Mon Sep 17 00:00:00 2001 From: David Wrighton Date: Mon, 15 Jun 2026 15:38:20 -0700 Subject: [PATCH 09/19] Adjust BuildMethodTable --- src/coreclr/vm/methodtablebuilder.cpp | 2 +- src/coreclr/vm/methodtablebuilder.h | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/coreclr/vm/methodtablebuilder.cpp b/src/coreclr/vm/methodtablebuilder.cpp index 28dafc7a35a5e0..586558485195ed 100644 --- a/src/coreclr/vm/methodtablebuilder.cpp +++ b/src/coreclr/vm/methodtablebuilder.cpp @@ -675,7 +675,7 @@ MethodTableBuilder::BuildMethodTableThrowException( CONTRACTL { THROWS; - GC_TRIGGERS; + GC_NOTRIGGER; INJECT_FAULT(COMPlusThrowOM();); } CONTRACTL_END diff --git a/src/coreclr/vm/methodtablebuilder.h b/src/coreclr/vm/methodtablebuilder.h index fece0cc7c6f83f..d7096697f61d4f 100644 --- a/src/coreclr/vm/methodtablebuilder.h +++ b/src/coreclr/vm/methodtablebuilder.h @@ -2394,8 +2394,8 @@ class MethodTableBuilder CONTRACTL { THROWS; - GC_TRIGGERS; - MODE_ANY; + GC_NOTRIGGER; + MODE_PREEMPTIVE; } CONTRACTL_END; bmtError->resIDWhy = idResWhy; @@ -2416,8 +2416,8 @@ class MethodTableBuilder CONTRACTL { THROWS; - GC_TRIGGERS; - MODE_ANY; + GC_NOTRIGGER; + MODE_PREEMPTIVE; } CONTRACTL_END; bmtError->resIDWhy = idResWhy; From 16a5f31732d7861f6a4bc79801ef5fae2b53a9d4 Mon Sep 17 00:00:00 2001 From: David Wrighton Date: Tue, 16 Jun 2026 13:55:51 -0700 Subject: [PATCH 10/19] Make error handling and EEClassHashTable construction GC_NOTRIGGER --- src/coreclr/vm/classhash.cpp | 4 +-- src/coreclr/vm/clsload.cpp | 4 +-- src/coreclr/vm/excep.cpp | 54 ++++++++++++------------------------ src/coreclr/vm/excep.h | 7 ----- src/coreclr/vm/threads.cpp | 2 +- 5 files changed, 22 insertions(+), 49 deletions(-) diff --git a/src/coreclr/vm/classhash.cpp b/src/coreclr/vm/classhash.cpp index 39fc890474c811..7c361f8d39c6b7 100644 --- a/src/coreclr/vm/classhash.cpp +++ b/src/coreclr/vm/classhash.cpp @@ -73,8 +73,8 @@ EEClassHashTable *EEClassHashTable::Create(Module *pModule, DWORD dwNumBuckets, CONTRACTL { THROWS; - GC_TRIGGERS; - MODE_ANY; + GC_NOTRIGGER; + MODE_PREEMPTIVE; INJECT_FAULT(COMPlusThrowOM();); PRECONDITION(!FORBIDGC_LOADER_USE_ENABLED()); diff --git a/src/coreclr/vm/clsload.cpp b/src/coreclr/vm/clsload.cpp index aab83563b46119..fa7c2442a9cb95 100644 --- a/src/coreclr/vm/clsload.cpp +++ b/src/coreclr/vm/clsload.cpp @@ -584,8 +584,8 @@ VOID ClassLoader::PopulateAvailableClassHashTable(Module* pModule, { INSTANCE_CHECK; THROWS; - GC_TRIGGERS; - MODE_ANY; + GC_NOTRIGGER; + MODE_PREEMPTIVE; INJECT_FAULT(COMPlusThrowOM();); } CONTRACTL_END; diff --git a/src/coreclr/vm/excep.cpp b/src/coreclr/vm/excep.cpp index 279cb3e85871a8..13b85908f38b2a 100644 --- a/src/coreclr/vm/excep.cpp +++ b/src/coreclr/vm/excep.cpp @@ -9551,7 +9551,7 @@ VOID DECLSPEC_NORETURN RealCOMPlusThrowWin32() CONTRACTL { THROWS; - DISABLED(GC_NOTRIGGER); // Must sanitize first pass handling to enable this + GC_NOTRIGGER; MODE_ANY; } CONTRACTL_END; @@ -9567,7 +9567,7 @@ VOID DECLSPEC_NORETURN RealCOMPlusThrowWin32(HRESULT hr) CONTRACTL { THROWS; - DISABLED(GC_NOTRIGGER); // Must sanitize first pass handling to enable this + GC_NOTRIGGER; MODE_ANY; } CONTRACTL_END; @@ -9588,7 +9588,7 @@ VOID DECLSPEC_NORETURN RealCOMPlusThrowOM() CONTRACTL { THROWS; - DISABLED(GC_NOTRIGGER); // Must sanitize first pass handling to enable this + GC_NOTRIGGER; CANNOT_TAKE_LOCK; MODE_ANY; SUPPORTS_DAC; @@ -9606,7 +9606,7 @@ VOID DECLSPEC_NORETURN RealCOMPlusThrow(RuntimeExceptionKind reKind) CONTRACTL { THROWS; - DISABLED(GC_NOTRIGGER); // Must sanitize first pass handling to enable this + GC_NOTRIGGER; MODE_ANY; } CONTRACTL_END; @@ -9626,7 +9626,7 @@ VOID DECLSPEC_NORETURN RealCOMPlusThrowNonLocalized(RuntimeExceptionKind reKind, CONTRACTL { THROWS; - DISABLED(GC_NOTRIGGER); // Must sanitize first pass handling to enable this + GC_NOTRIGGER; MODE_ANY; } CONTRACTL_END; @@ -9707,33 +9707,13 @@ VOID DECLSPEC_NORETURN RealCOMPlusThrowHR(HRESULT hr) EX_THROW(EEMessageException, (hr)); } - -VOID DECLSPEC_NORETURN RealCOMPlusThrowHR(HRESULT hr, tagGetErrorInfo) -{ - CONTRACTL - { - THROWS; - DISABLED(GC_NOTRIGGER); // Must sanitize first pass handling to enable this - MODE_ANY; - } - CONTRACTL_END; - - // Get an IErrorInfo if one is available. - IErrorInfo *pErrInfo = NULL; - - if (SafeGetErrorInfo(&pErrInfo) != S_OK) - pErrInfo = NULL; - - // Throw the exception. - RealCOMPlusThrowHR(hr, pErrInfo); -} #else // FEATURE_COMINTEROP VOID DECLSPEC_NORETURN RealCOMPlusThrowHR(HRESULT hr) { CONTRACTL { THROWS; - DISABLED(GC_NOTRIGGER); // Must sanitize first pass handling to enable this + GC_NOTRIGGER; MODE_ANY; } CONTRACTL_END; @@ -9781,7 +9761,7 @@ VOID DECLSPEC_NORETURN RealCOMPlusThrow(RuntimeExceptionKind reKind, LPCWSTR wsz CONTRACTL { THROWS; - DISABLED(GC_NOTRIGGER); // Must sanitize first pass handling to enable this + GC_NOTRIGGER; MODE_ANY; PRECONDITION(CheckPointer(wszResourceName)); } @@ -9821,7 +9801,7 @@ VOID DECLSPEC_NORETURN ThrowTypeLoadException(LPCWSTR pFullTypeName, CONTRACTL { THROWS; - DISABLED(GC_NOTRIGGER); // Must sanitize first pass handling to enable this + GC_NOTRIGGER; MODE_ANY; } CONTRACTL_END; @@ -9841,7 +9821,7 @@ VOID DECLSPEC_NORETURN ThrowFieldLayoutError(mdTypeDef cl, // cl CONTRACTL { THROWS; - DISABLED(GC_NOTRIGGER); // Must sanitize first pass handling to enable this + GC_NOTRIGGER; MODE_ANY; } CONTRACTL_END; @@ -9872,7 +9852,7 @@ VOID DECLSPEC_NORETURN RealCOMPlusThrowArithmetic() CONTRACTL { THROWS; - DISABLED(GC_NOTRIGGER); // Must sanitize first pass handling to enable this + GC_NOTRIGGER; MODE_ANY; } CONTRACTL_END; @@ -9888,7 +9868,7 @@ VOID DECLSPEC_NORETURN RealCOMPlusThrowArgumentNull(LPCWSTR argName, LPCWSTR wsz CONTRACTL { THROWS; - DISABLED(GC_NOTRIGGER); // Must sanitize first pass handling to enable this + GC_NOTRIGGER; MODE_ANY; PRECONDITION(CheckPointer(wszResourceName)); } @@ -9903,7 +9883,7 @@ VOID DECLSPEC_NORETURN RealCOMPlusThrowArgumentNull(LPCWSTR argName) CONTRACTL { THROWS; - DISABLED(GC_NOTRIGGER); // Must sanitize first pass handling to enable this + GC_NOTRIGGER; MODE_ANY; } CONTRACTL_END; @@ -9920,7 +9900,7 @@ VOID DECLSPEC_NORETURN RealCOMPlusThrowArgumentOutOfRange(LPCWSTR argName, LPCWS CONTRACTL { THROWS; - DISABLED(GC_NOTRIGGER); // Must sanitize first pass handling to enable this + GC_NOTRIGGER; MODE_ANY; } CONTRACTL_END; @@ -9936,7 +9916,7 @@ VOID DECLSPEC_NORETURN RealCOMPlusThrowArgumentException(LPCWSTR argName, LPCWST CONTRACTL { THROWS; - DISABLED(GC_NOTRIGGER); // Must sanitize first pass handling to enable this + GC_NOTRIGGER; MODE_ANY; } CONTRACTL_END; @@ -9961,7 +9941,7 @@ VOID DECLSPEC_NORETURN ThrowTypeLoadException(LPCUTF8 pszNameSpace, CONTRACTL { THROWS; - DISABLED(GC_NOTRIGGER); // Must sanitize first pass handling to enable this + GC_NOTRIGGER; MODE_ANY; } CONTRACTL_END; @@ -9979,7 +9959,7 @@ VOID DECLSPEC_NORETURN RealCOMPlusThrow(RuntimeExceptionKind reKind, UINT resID CONTRACTL { THROWS; - DISABLED(GC_NOTRIGGER); // Must sanitize first pass handling to enable this + GC_NOTRIGGER; MODE_ANY; } CONTRACTL_END; @@ -10019,7 +9999,7 @@ VOID DECLSPEC_NORETURN RealCOMPlusThrowHR(EXCEPINFO *pExcepInfo) CONTRACTL { THROWS; - DISABLED(GC_NOTRIGGER); // Must sanitize first pass handling to enable this + GC_NOTRIGGER; MODE_ANY; } CONTRACTL_END; diff --git a/src/coreclr/vm/excep.h b/src/coreclr/vm/excep.h index 472782de483d33..04c737390dbe5a 100644 --- a/src/coreclr/vm/excep.h +++ b/src/coreclr/vm/excep.h @@ -239,13 +239,6 @@ VOID DECLSPEC_NORETURN RealCOMPlusThrowHR(HRESULT hr, UINT resID, LPCWSTR wszArg #ifdef FEATURE_COMINTEROP -enum tagGetErrorInfo -{ - kGetErrorInfo -}; - -VOID DECLSPEC_NORETURN RealCOMPlusThrowHR(HRESULT hr, tagGetErrorInfo); - //========================================================================== // Throw a runtime exception based on an HResult, check for error info //========================================================================== diff --git a/src/coreclr/vm/threads.cpp b/src/coreclr/vm/threads.cpp index 1fa70acc931304..98fbde49e45e1c 100644 --- a/src/coreclr/vm/threads.cpp +++ b/src/coreclr/vm/threads.cpp @@ -6094,7 +6094,7 @@ Frame * Thread::NotifyFrameChainOfExceptionUnwind(Frame* pStartFrame, LPVOID pvL CONTRACTL { NOTHROW; - DISABLED(GC_TRIGGERS); // due to UnwindFrameChain from NOTRIGGER areas + GC_NOTRIGGER; MODE_COOPERATIVE; PRECONDITION(CheckPointer(pStartFrame)); PRECONDITION(CheckPointer(pvLimitSP)); From 6d21f26233590cd3766f756e1e123083badaff31 Mon Sep 17 00:00:00 2001 From: David Wrighton Date: Wed, 17 Jun 2026 17:19:28 -0700 Subject: [PATCH 11/19] Make new MethodDescCallSite for the custom attribute call scenario --- src/coreclr/vm/callhelpers.h | 24 ++++++++++ src/coreclr/vm/comdelegate.cpp | 73 ++++++++++++++++++------------ src/coreclr/vm/customattribute.cpp | 3 +- 3 files changed, 68 insertions(+), 32 deletions(-) diff --git a/src/coreclr/vm/callhelpers.h b/src/coreclr/vm/callhelpers.h index 5630d73b12a67e..b5b19b89d61a33 100644 --- a/src/coreclr/vm/callhelpers.h +++ b/src/coreclr/vm/callhelpers.h @@ -131,6 +131,30 @@ class MethodDescCallSite m_argIt.ForceSigWalk(); } + // + // Only use this constructor if you're certain you know where + // you're going and it cannot be affected by generics/virtual + // dispatch/etc.. + // + MethodDescCallSite(MethodDesc* pMD, PCODE pCallTarget, TypeHandle th) : + m_pMD(pMD), + m_pCallTarget(pCallTarget), + m_methodSig(pMD, th), + m_argIt(&m_methodSig) + { + CONTRACTL + { + THROWS; + GC_TRIGGERS; + MODE_ANY; + } + CONTRACTL_END; + + m_pMD->EnsureActive(); + + m_argIt.ForceSigWalk(); + } + MetaSig* GetMetaSig() { return &m_methodSig; diff --git a/src/coreclr/vm/comdelegate.cpp b/src/coreclr/vm/comdelegate.cpp index 4a5c7e8da58319..23e7bb9ce76492 100644 --- a/src/coreclr/vm/comdelegate.cpp +++ b/src/coreclr/vm/comdelegate.cpp @@ -999,24 +999,18 @@ extern "C" BOOL QCALLTYPE Delegate_BindToMethodName(QCall::ObjectHandleOnStack d BEGIN_QCALL; - GCX_COOP(); - - struct - { - DELEGATEREF refThis; - OBJECTREF target; - } gc; - - gc.refThis = (DELEGATEREF) d.Get(); - gc.target = target.Get(); - - GCPROTECT_BEGIN(gc); TypeHandle methodType = pMethodType.AsTypeHandle(); - TypeHandle targetType((gc.target != NULL) ? gc.target->GetMethodTable() : NULL); + TypeHandle targetType; + MethodTable * pDelegateType; + { + GCX_COOP(); + targetType = (target.Get() != NULL) ? TypeHandle(target.Get()->GetMethodTable()) : TypeHandle(); + pDelegateType = d.Get()->GetMethodTable(); + } + // get the invoke of the delegate - MethodTable * pDelegateType = gc.refThis->GetMethodTable(); MethodDesc* pInvokeMeth = COMDelegate::FindDelegateInvokeMethod(pDelegateType); _ASSERTE(pInvokeMeth); @@ -1073,10 +1067,10 @@ extern "C" BOOL QCALLTYPE Delegate_BindToMethodName(QCall::ObjectHandleOnStack d false /* do not allow code with a shared-code calling convention to be returned */, true /* Ensure that methods on generic interfaces are returned as instantiated method descs */); bool fIsOpenDelegate; - if (!COMDelegate::IsMethodDescCompatible((gc.target == NULL) ? TypeHandle() : gc.target->GetTypeHandle(), + if (!COMDelegate::IsMethodDescCompatible(targetType, methodType, pCurMethod, - gc.refThis->GetTypeHandle(), + TypeHandle(pDelegateType), pInvokeMeth, flags, &fIsOpenDelegate)) @@ -1085,13 +1079,29 @@ extern "C" BOOL QCALLTYPE Delegate_BindToMethodName(QCall::ObjectHandleOnStack d continue; } - // Found the target that matches the signature and satisfies security transparency rules - // Initialize the delegate to point to the target method. - COMDelegate::BindToMethod(&gc.refThis, - &gc.target, - pCurMethod, - methodType.GetMethodTable(), - fIsOpenDelegate); + { + GCX_COOP(); + + struct + { + DELEGATEREF refThis; + OBJECTREF target; + } gc; + + gc.refThis = (DELEGATEREF) d.Get(); + gc.target = target.Get(); + + GCPROTECT_BEGIN(gc); + + // Found the target that matches the signature and satisfies security transparency rules + // Initialize the delegate to point to the target method. + COMDelegate::BindToMethod(&gc.refThis, + &gc.target, + pCurMethod, + methodType.GetMethodTable(), + fIsOpenDelegate); + GCPROTECT_END(); + } pMatchingMethod = pCurMethod; goto done; @@ -1101,7 +1111,6 @@ extern "C" BOOL QCALLTYPE Delegate_BindToMethodName(QCall::ObjectHandleOnStack d done: ; - GCPROTECT_END(); END_QCALL; @@ -1145,6 +1154,9 @@ extern "C" BOOL QCALLTYPE Delegate_BindToMethodInfo(QCall::ObjectHandleOnStack d _ASSERTE(pInvokeMeth); // See the comment in BindToMethodName + bool fIsOpenDelegate; + bool fIsMethodDescCompatible; + TypeHandle targetType = (gc.refFirstArg == NULL) ? TypeHandle() : gc.refFirstArg->GetTypeHandle(); { GCX_PREEMP(); method = @@ -1154,16 +1166,17 @@ extern "C" BOOL QCALLTYPE Delegate_BindToMethodInfo(QCall::ObjectHandleOnStack d method->GetMethodInstantiation(), false /* do not allow code with a shared-code calling convention to be returned */, true /* Ensure that methods on generic interfaces are returned as instantiated method descs */); - } - - bool fIsOpenDelegate; - if (COMDelegate::IsMethodDescCompatible((gc.refFirstArg == NULL) ? TypeHandle() : gc.refFirstArg->GetTypeHandle(), + + fIsMethodDescCompatible = COMDelegate::IsMethodDescCompatible(targetType, TypeHandle(pMethMT), method, - gc.refThis->GetTypeHandle(), + TypeHandle(pDelegateType), pInvokeMeth, flags, - &fIsOpenDelegate)) + &fIsOpenDelegate); + } + + if (fIsMethodDescCompatible) { // Initialize the delegate to point to the target method. COMDelegate::BindToMethod(&gc.refThis, diff --git a/src/coreclr/vm/customattribute.cpp b/src/coreclr/vm/customattribute.cpp index c505bec2b167a9..7f82b876f718d1 100644 --- a/src/coreclr/vm/customattribute.cpp +++ b/src/coreclr/vm/customattribute.cpp @@ -919,11 +919,10 @@ extern "C" void QCALLTYPE CustomAttribute_CreateCustomAttributeInstance( { GCX_PREEMP(); - pCtorMD->EnsureActive(); pCallTarget = pCtorMD->GetCallTarget(NULL, th); } - MethodDescCallSite ctorCallSite(pCtorMD, pCallTarget); + MethodDescCallSite ctorCallSite(pCtorMD, pCallTarget, th); MetaSig* pSig = ctorCallSite.GetMetaSig(); BYTE* pBlob = *ppBlob; From df2fb2a103fe231f007966c48cc631ccdf5245af Mon Sep 17 00:00:00 2001 From: David Wrighton Date: Fri, 19 Jun 2026 12:01:50 -0700 Subject: [PATCH 12/19] Tweaks on delegate construction --- .../src/System/Delegate.CoreCLR.cs | 60 +++++- .../src/System/MulticastDelegate.CoreCLR.cs | 2 +- src/coreclr/vm/comdelegate.cpp | 182 ++++++------------ src/coreclr/vm/comdelegate.h | 19 +- 4 files changed, 128 insertions(+), 135 deletions(-) diff --git a/src/coreclr/System.Private.CoreLib/src/System/Delegate.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Delegate.CoreCLR.cs index 2726c61f3b72f3..7b58320b215565 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Delegate.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Delegate.CoreCLR.cs @@ -411,26 +411,70 @@ internal static Delegate CreateDelegateNoSecurityCheck(Type type, object? target private bool BindToMethodName(object? target, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.AllMethods)] RuntimeType methodType, string method, DelegateBindingFlags flags) { Delegate d = this; - return BindToMethodName(ObjectHandleOnStack.Create(ref d), ObjectHandleOnStack.Create(ref target), - new QCallTypeHandle(ref methodType), method, flags); + bool ret = BindToMethodName(RuntimeHelpers.GetMethodTable(d), (target != null) ? RuntimeHelpers.GetMethodTable(target) : null, + new QCallTypeHandle(ref methodType), method, flags, out BindToMethodDetails bindToMethodDetails); + + if (ret) + { + // Apply the results of the QCall to the delegate instance. + _methodPtr = bindToMethodDetails.methodPtr; + _methodPtrAux = bindToMethodDetails.methodPtrAux; + Unsafe.As(this)._invocationCount = bindToMethodDetails.invocationCount; + if (bindToMethodDetails.loaderAllocatorGCHandle.IsAllocated) + { + _methodBase = bindToMethodDetails.loaderAllocatorGCHandle.Target; + GC.KeepAlive(method); + } + + if (bindToMethodDetails.selfReferentialTarget != 0) + _target = this; + else + _target = target; + } + return ret; + } + + struct BindToMethodDetails + { + public int selfReferentialTarget; // Whether the delegate's target object is the same as the first argument of the method to bind to. Only meaningful for open instance delegates. + public IntPtr methodPtr; + public IntPtr methodPtrAux; + public IntPtr invocationCount; + public GCHandle loaderAllocatorGCHandle; // The loader allocator needed if the delegate needs to keep it alive } [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "Delegate_BindToMethodName", StringMarshalling = StringMarshalling.Utf8)] [return: MarshalAs(UnmanagedType.Bool)] - private static partial bool BindToMethodName(ObjectHandleOnStack d, ObjectHandleOnStack target, QCallTypeHandle methodType, string method, DelegateBindingFlags flags); + private static partial bool BindToMethodName(MethodTable* pDelegateMT, MethodTable *pTargetMT, QCallTypeHandle methodType, string method, DelegateBindingFlags flags, out BindToMethodDetails bindToMethodDetails); private bool BindToMethodInfo(object? target, IRuntimeMethodInfo method, RuntimeType methodType, DelegateBindingFlags flags) { - Delegate d = this; - bool ret = BindToMethodInfo(ObjectHandleOnStack.Create(ref d), ObjectHandleOnStack.Create(ref target), - method.Value, new QCallTypeHandle(ref methodType), flags); - GC.KeepAlive(method); + bool ret = BindToMethodInfo(RuntimeHelpers.GetMethodTable(this), (target != null) ? RuntimeHelpers.GetMethodTable(target) : null, + method.Value, new QCallTypeHandle(ref methodType), flags, out BindToMethodDetails bindToMethodDetails); + + if (ret) + { + // Apply the results of the QCall to the delegate instance. + _methodPtr = bindToMethodDetails.methodPtr; + _methodPtrAux = bindToMethodDetails.methodPtrAux; + Unsafe.As(this)._invocationCount = bindToMethodDetails.invocationCount; + if (bindToMethodDetails.loaderAllocatorGCHandle.IsAllocated) + { + _methodBase = bindToMethodDetails.loaderAllocatorGCHandle.Target; + GC.KeepAlive(method); + } + + if (bindToMethodDetails.selfReferentialTarget != 0) + _target = this; + else + _target = target; + } return ret; } [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "Delegate_BindToMethodInfo")] [return: MarshalAs(UnmanagedType.Bool)] - private static partial bool BindToMethodInfo(ObjectHandleOnStack d, ObjectHandleOnStack target, RuntimeMethodHandleInternal method, QCallTypeHandle methodType, DelegateBindingFlags flags); + private static partial bool BindToMethodInfo(MethodTable* pDelegateMT, MethodTable *pTargetMT, RuntimeMethodHandleInternal method, QCallTypeHandle methodType, DelegateBindingFlags flags); private static MulticastDelegate InternalAlloc(RuntimeType type) { diff --git a/src/coreclr/System.Private.CoreLib/src/System/MulticastDelegate.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/MulticastDelegate.CoreCLR.cs index 38fd1f5189f6ec..2348719e9e1282 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/MulticastDelegate.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/MulticastDelegate.CoreCLR.cs @@ -22,7 +22,7 @@ public abstract partial class MulticastDelegate : Delegate // 2. Unmanaged function pointer // 3. Open virtual delegate private object? _invocationList; // Initialized by VM as needed - private nint _invocationCount; + internal nint _invocationCount; internal bool IsUnmanagedFunctionPtr() { diff --git a/src/coreclr/vm/comdelegate.cpp b/src/coreclr/vm/comdelegate.cpp index 5527c42eda7937..562ef290dc161a 100644 --- a/src/coreclr/vm/comdelegate.cpp +++ b/src/coreclr/vm/comdelegate.cpp @@ -990,8 +990,8 @@ static PCODE GetVirtualCallStub(MethodDesc *method, TypeHandle scopeType) ); } -extern "C" BOOL QCALLTYPE Delegate_BindToMethodName(QCall::ObjectHandleOnStack d, QCall::ObjectHandleOnStack target, - QCall::TypeHandle pMethodType, LPCUTF8 pszMethodName, DelegateBindingFlags flags) +extern "C" BOOL QCALLTYPE Delegate_BindToMethodName(MethodTable* pDelegateMT, MethodTable *pTargetMT, + QCall::TypeHandle pMethodType, LPCUTF8 pszMethodName, DelegateBindingFlags flags, BindToMethodDetails *pBindToMethodDetails) { QCALL_CONTRACT; @@ -1002,16 +1002,11 @@ extern "C" BOOL QCALLTYPE Delegate_BindToMethodName(QCall::ObjectHandleOnStack d TypeHandle methodType = pMethodType.AsTypeHandle(); - TypeHandle targetType; - MethodTable * pDelegateType; - { - GCX_COOP(); - targetType = (target.Get() != NULL) ? TypeHandle(target.Get()->GetMethodTable()) : TypeHandle(); - pDelegateType = d.Get()->GetMethodTable(); - } - + TypeHandle targetType = TypeHandle(pTargetMT); + MethodTable *pDelegateMT; + // get the invoke of the delegate - MethodDesc* pInvokeMeth = COMDelegate::FindDelegateInvokeMethod(pDelegateType); + MethodDesc* pInvokeMeth = COMDelegate::FindDelegateInvokeMethod(pDelegateMT); _ASSERTE(pInvokeMeth); // @@ -1079,29 +1074,13 @@ extern "C" BOOL QCALLTYPE Delegate_BindToMethodName(QCall::ObjectHandleOnStack d continue; } - { - GCX_COOP(); - - struct - { - DELEGATEREF refThis; - OBJECTREF target; - } gc; - - gc.refThis = (DELEGATEREF) d.Get(); - gc.target = target.Get(); - - GCPROTECT_BEGIN(gc); - - // Found the target that matches the signature and satisfies security transparency rules - // Initialize the delegate to point to the target method. - COMDelegate::BindToMethod(&gc.refThis, - &gc.target, - pCurMethod, - methodType.GetMethodTable(), - fIsOpenDelegate); - GCPROTECT_END(); - } + // Found the target that matches the signature and satisfies security transparency rules + // Initialize the delegate to point to the target method. + COMDelegate::BindToMethod(pDelegateMT, + pCurMethod, + methodType.GetMethodTable(), + fIsOpenDelegate, + pBindToMethodDetails); pMatchingMethod = pCurMethod; goto done; @@ -1117,8 +1096,8 @@ extern "C" BOOL QCALLTYPE Delegate_BindToMethodName(QCall::ObjectHandleOnStack d return (pMatchingMethod != NULL); } -extern "C" BOOL QCALLTYPE Delegate_BindToMethodInfo(QCall::ObjectHandleOnStack d, QCall::ObjectHandleOnStack target, - MethodDesc * method, QCall::TypeHandle pMethodType, DelegateBindingFlags flags) +extern "C" BOOL QCALLTYPE Delegate_BindToMethodInfo(MethodTable* pDelegateMT, MethodTable *pTargetMT, + MethodDesc * method, QCall::TypeHandle pMethodType, DelegateBindingFlags flags, BindToMethodDetails *pBindToMethodDetails) { QCALL_CONTRACT; @@ -1126,69 +1105,48 @@ extern "C" BOOL QCALLTYPE Delegate_BindToMethodInfo(QCall::ObjectHandleOnStack d BEGIN_QCALL; - GCX_COOP(); - - struct - { - DELEGATEREF refThis; - OBJECTREF refFirstArg; - } gc; - - gc.refThis = (DELEGATEREF) d.Get(); - gc.refFirstArg = target.Get(); - - GCPROTECT_BEGIN(gc); - MethodTable *pMethMT = pMethodType.AsTypeHandle().GetMethodTable(); - // Assert to track down VS#458689. - _ASSERTE(gc.refThis != gc.refFirstArg); - // A generic method had better be instantiated (we can't dispatch to an uninstantiated one). if (method->IsGenericMethodDefinition()) COMPlusThrow(kArgumentException, W("Arg_DlgtTargMeth")); // get the invoke of the delegate - MethodTable * pDelegateType = gc.refThis->GetMethodTable(); - MethodDesc* pInvokeMeth = COMDelegate::FindDelegateInvokeMethod(pDelegateType); + MethodDesc* pInvokeMeth = COMDelegate::FindDelegateInvokeMethod(pDelegateMT); _ASSERTE(pInvokeMeth); // See the comment in BindToMethodName bool fIsOpenDelegate; bool fIsMethodDescCompatible; - TypeHandle targetType = (gc.refFirstArg == NULL) ? TypeHandle() : gc.refFirstArg->GetTypeHandle(); - { - GCX_PREEMP(); - method = - MethodDesc::FindOrCreateAssociatedMethodDesc(method, + + method = MethodDesc::FindOrCreateAssociatedMethodDesc(method, pMethMT, (!method->IsStatic() && pMethMT->IsValueType()), method->GetMethodInstantiation(), false /* do not allow code with a shared-code calling convention to be returned */, true /* Ensure that methods on generic interfaces are returned as instantiated method descs */); - fIsMethodDescCompatible = COMDelegate::IsMethodDescCompatible(targetType, + if (COMDelegate::IsMethodDescCompatible(TypeHandle(pTargetMT), TypeHandle(pMethMT), method, - TypeHandle(pDelegateType), + TypeHandle(pDelegateMT), pInvokeMeth, flags, - &fIsOpenDelegate); - } - - if (fIsMethodDescCompatible) + &fIsOpenDelegate)) { // Initialize the delegate to point to the target method. - COMDelegate::BindToMethod(&gc.refThis, - &gc.refFirstArg, + COMDelegate::BindToMethod(pDelegateMT, method, pMethMT, - fIsOpenDelegate); + fIsOpenDelegate, + pBindToMethodDetails); + + result = TRUE; } else + { result = FALSE; - - GCPROTECT_END(); + } END_QCALL; @@ -1198,19 +1156,19 @@ extern "C" BOOL QCALLTYPE Delegate_BindToMethodInfo(QCall::ObjectHandleOnStack d // This method is called (in the late bound case only) once a target method has been decided on. All the consistency checks // (signature matching etc.) have been done at this point, this method will simply initialize the delegate, with any required // wrapping. The delegate returned will be ready for invocation immediately. -void COMDelegate::BindToMethod(DELEGATEREF *pRefThis, - OBJECTREF *pRefFirstArg, +void COMDelegate::BindToMethod(MethodTable* pDelegateMT, + MethodTable* pTargetMT, MethodDesc *pTargetMethod, MethodTable *pExactMethodType, - BOOL fIsOpenDelegate) + BOOL fIsOpenDelegate, + BindToMethodDetails *pBindToMethodDetails) { CONTRACTL { THROWS; GC_TRIGGERS; - MODE_COOPERATIVE; - PRECONDITION(CheckPointer(pRefThis)); - PRECONDITION(CheckPointer(pRefFirstArg, NULL_OK)); + MODE_PREEMPTIVE; + PRECONDITION(CheckPointer(pDelegateMT)); PRECONDITION(CheckPointer(pTargetMethod)); PRECONDITION(CheckPointer(pExactMethodType)); } @@ -1220,19 +1178,16 @@ void COMDelegate::BindToMethod(DELEGATEREF *pRefThis, if (fIsOpenDelegate) { - _ASSERTE(pRefFirstArg == NULL || *pRefFirstArg == NULL); - // Open delegates use themselves as the target (which handily allows their shuffle thunks to locate additional data at // invocation time). - (*pRefThis)->SetTarget(*pRefThis); + pBindToMethodDetails->selfReferentialTarget = TRUE; // We need to shuffle arguments for open delegates since the first argument on the calling side is not meaningful to the // callee. - MethodTable * pDelegateMT = (*pRefThis)->GetMethodTable(); PCODE pEntryPoint = SetupShuffleThunk(pDelegateMT, pTargetMethod); // Indicate that the delegate will jump to the shuffle thunk rather than directly to the target method. - (*pRefThis)->SetMethodPtr(pEntryPoint); + pBindToMethodDetails->methodPtr = pEntryPoint; // Use stub dispatch for all virtuals. // Investigate not using this for non-interface virtuals. @@ -1245,46 +1200,37 @@ void COMDelegate::BindToMethod(DELEGATEREF *pRefThis, // Since this is an open delegate over a virtual method we cannot virtualize the call target now. So the shuffle thunk // needs to jump to another stub (this time provided by the VirtualStubManager) that will virtualize the call at // runtime. - PCODE pTargetCall; - { - GCX_PREEMP(); - pTargetCall = GetVirtualCallStub(pTargetMethod, TypeHandle(pExactMethodType)); - } - (*pRefThis)->SetMethodPtrAux(pTargetCall); - (*pRefThis)->SetInvocationCount((INT_PTR)(void *)pTargetMethod); + pBindToMethodDetails->methodPtrAux = GetVirtualCallStub(pTargetMethod, TypeHandle(pExactMethodType)); + pBindToMethodDetails->invocationCount = (INT_PTR)(void *)pTargetMethod; } else { - PCODE pTargetCode; + // Reflection or the code in BindToMethodName will pass us the unboxing stub for non-static methods on value types. But + // for open invocation on value type methods the actual reference will be passed so we need the unboxed method desc + // instead. + if (pTargetMethod->IsUnboxingStub()) { - GCX_PREEMP(); - // Reflection or the code in BindToMethodName will pass us the unboxing stub for non-static methods on value types. But - // for open invocation on value type methods the actual reference will be passed so we need the unboxed method desc - // instead. - if (pTargetMethod->IsUnboxingStub()) - { - // We want a MethodDesc which is not an unboxing stub, but is an instantiating stub if needed. - pTargetMethod = MethodDesc::FindOrCreateAssociatedMethodDesc( - pTargetMethod, - pExactMethodType, - FALSE /* don't want unboxing entry point */, - pTargetMethod->GetMethodInstantiation(), - FALSE /* don't want MD that requires inst. arguments */, - true /* Ensure that methods on generic interfaces are returned as instantiated method descs */); - } + // We want a MethodDesc which is not an unboxing stub, but is an instantiating stub if needed. + pTargetMethod = MethodDesc::FindOrCreateAssociatedMethodDesc( + pTargetMethod, + pExactMethodType, + FALSE /* don't want unboxing entry point */, + pTargetMethod->GetMethodInstantiation(), + FALSE /* don't want MD that requires inst. arguments */, + true /* Ensure that methods on generic interfaces are returned as instantiated method descs */); + } - // The method must not require any extra hidden instantiation arguments. - _ASSERTE(!pTargetMethod->RequiresInstArg()); + // The method must not require any extra hidden instantiation arguments. + _ASSERTE(!pTargetMethod->RequiresInstArg()); - // Note that it is important to cache pTargetCode in local variable to avoid GC hole. - // GetMultiCallableAddrOfCode() can trigger GC. - pTargetCode = pTargetMethod->GetMultiCallableAddrOfCode(); - } - (*pRefThis)->SetMethodPtrAux(pTargetCode); + // Note that it is important to cache pTargetCode in local variable to avoid GC hole. + // GetMultiCallableAddrOfCode() can trigger GC. + pBindToMethodDetails->methodPtrAux = pTargetMethod->GetMultiCallableAddrOfCode(); } } else { + pBindToMethodDetails->selfReferentialTarget = FALSE; PCODE pTargetCode = (PCODE)NULL; // For virtual methods we can (and should) virtualize the call now (so we don't have to insert a thunk to do so at runtime). @@ -1294,34 +1240,28 @@ void COMDelegate::BindToMethod(DELEGATEREF *pRefThis, // virtualize since we're just going to throw NullRefException at invocation time). // if (pTargetMethod->IsVirtual() - && *pRefFirstArg != NULL - && pTargetMethod->GetMethodTable() != (*pRefFirstArg)->GetMethodTable()) + && pTargetMT != NULL + && pTargetMethod->GetMethodTable() != pTargetMT) { - GCX_PREEMP(); pTargetCode = pTargetMethod->GetMultiCallableAddrOfVirtualizedCode(pRefFirstArg, pTargetMethod->GetMethodTable()); } #ifdef HAS_THISPTR_RETBUF_PRECODE else if (pTargetMethod->IsStatic() && pTargetMethod->HasRetBuffArg() && IsRetBuffPassedAsFirstArg()) { - GCX_PREEMP(); pTargetCode = pTargetMethod->GetLoaderAllocator()->GetFuncPtrStubs()->GetFuncPtrStub(pTargetMethod, PRECODE_THISPTR_RETBUF); } #endif // HAS_THISPTR_RETBUF_PRECODE else { - GCX_PREEMP(); pTargetCode = pTargetMethod->GetMultiCallableAddrOfCode(); } _ASSERTE(pTargetCode); - - (*pRefThis)->SetTarget(*pRefFirstArg); - (*pRefThis)->SetMethodPtr(pTargetCode); + pBindToMethodDetails->methodPtr = pTargetCode; } LoaderAllocator *pLoaderAllocator = pTargetMethod->GetLoaderAllocator(); - if (pLoaderAllocator->IsCollectible()) - (*pRefThis)->SetMethodBase(pLoaderAllocator->GetExposedObject()); + pBindToMethodDetails->loaderAllocatorGCHandle = pLoaderAllocator->GetLoaderAllocatorObjectHandle(); } // Marshals a delegate to a unmanaged callback. diff --git a/src/coreclr/vm/comdelegate.h b/src/coreclr/vm/comdelegate.h index 1bea501884bb98..27466bc5584519 100644 --- a/src/coreclr/vm/comdelegate.h +++ b/src/coreclr/vm/comdelegate.h @@ -26,6 +26,15 @@ enum class ShuffleComputationType }; BOOL GenerateShuffleArrayPortable(MethodDesc* pMethodSrc, MethodDesc *pMethodDst, SArray * pShuffleEntryArray, ShuffleComputationType shuffleType); +struct BindToMethodDetails +{ + BOOL selfReferentialTarget; // Whether the delegate's target object is the same as the first argument of the method to bind to. Only meaningful for open instance delegates. + PCODE methodPtr; + PCODE methodPtrAux; + INT_PTR invocationCount; + OBJECTHANDLE loaderAllocatorGCHandle; // The loader allocator needed if the delegate needs to keep it alive +}; + // This class represents the native methods for the Delegate class class COMDelegate { @@ -80,11 +89,11 @@ class COMDelegate bool *pfIsOpenDelegate); static MethodDesc* GetDelegateCtor(TypeHandle delegateType, MethodDesc *pTargetMethod, DelegateCtorArgs *pCtorData); - static void BindToMethod(DELEGATEREF *pRefThis, - OBJECTREF *pRefFirstArg, + static void BindToMethod(MethodTable* pDelegateMT, MethodDesc *pTargetMethod, MethodTable *pExactMethodType, - BOOL fIsOpenDelegate); + BOOL fIsOpenDelegate, + BindToMethodDetails *pBindToMethodDetails); }; extern "C" void QCALLTYPE Delegate_Construct(QCall::ObjectHandleOnStack _this, QCall::ObjectHandleOnStack target, PCODE method); @@ -111,8 +120,8 @@ enum DelegateBindingFlags extern "C" BOOL QCALLTYPE Delegate_BindToMethodName(QCall::ObjectHandleOnStack d, QCall::ObjectHandleOnStack target, QCall::TypeHandle pMethodType, LPCUTF8 pszMethodName, DelegateBindingFlags flags); -extern "C" BOOL QCALLTYPE Delegate_BindToMethodInfo(QCall::ObjectHandleOnStack d, QCall::ObjectHandleOnStack target, - MethodDesc * method, QCall::TypeHandle pMethodType, DelegateBindingFlags flags); +extern "C" BOOL QCALLTYPE Delegate_BindToMethodInfo(MethodTable* pDelegateMT, MethodTable *pTargetMT, + MethodDesc * method, QCall::TypeHandle pMethodType, DelegateBindingFlags flags, BindToMethodDetails *pBindToMethodDetails); extern "C" void QCALLTYPE Delegate_FindMethodHandle(QCall::ObjectHandleOnStack d, QCall::ObjectHandleOnStack retMethodInfo); From b46706543a7dca60ab871461d324d6b6888ffc37 Mon Sep 17 00:00:00 2001 From: David Wrighton Date: Fri, 19 Jun 2026 13:47:27 -0700 Subject: [PATCH 13/19] Handle BindToMethodDetails correctly --- .../src/System/Delegate.CoreCLR.cs | 13 ++++++------ src/coreclr/vm/comdelegate.cpp | 21 ++++++++++--------- src/coreclr/vm/comdelegate.h | 8 ++++--- src/coreclr/vm/qcall.h | 6 ++++++ 4 files changed, 28 insertions(+), 20 deletions(-) diff --git a/src/coreclr/System.Private.CoreLib/src/System/Delegate.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Delegate.CoreCLR.cs index 7b58320b215565..179a27a2829dbd 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Delegate.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Delegate.CoreCLR.cs @@ -410,9 +410,8 @@ internal static Delegate CreateDelegateNoSecurityCheck(Type type, object? target Justification = "The parameter 'methodType' is passed by ref to QCallTypeHandle")] private bool BindToMethodName(object? target, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.AllMethods)] RuntimeType methodType, string method, DelegateBindingFlags flags) { - Delegate d = this; - bool ret = BindToMethodName(RuntimeHelpers.GetMethodTable(d), (target != null) ? RuntimeHelpers.GetMethodTable(target) : null, - new QCallTypeHandle(ref methodType), method, flags, out BindToMethodDetails bindToMethodDetails); + bool ret = BindToMethodName(RuntimeHelpers.GetMethodTable(this), (target != null) ? RuntimeHelpers.GetMethodTable(target) : null, + new QCallTypeHandle(ref methodType), method, flags, ObjectHandleOnStack.Create(ref target), out BindToMethodDetails bindToMethodDetails); if (ret) { @@ -434,7 +433,7 @@ private bool BindToMethodName(object? target, [DynamicallyAccessedMembers(Dynami return ret; } - struct BindToMethodDetails + private struct BindToMethodDetails { public int selfReferentialTarget; // Whether the delegate's target object is the same as the first argument of the method to bind to. Only meaningful for open instance delegates. public IntPtr methodPtr; @@ -445,12 +444,12 @@ struct BindToMethodDetails [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "Delegate_BindToMethodName", StringMarshalling = StringMarshalling.Utf8)] [return: MarshalAs(UnmanagedType.Bool)] - private static partial bool BindToMethodName(MethodTable* pDelegateMT, MethodTable *pTargetMT, QCallTypeHandle methodType, string method, DelegateBindingFlags flags, out BindToMethodDetails bindToMethodDetails); + private static partial bool BindToMethodName(MethodTable* pDelegateMT, MethodTable *pTargetMT, QCallTypeHandle methodType, string method, DelegateBindingFlags flags, ObjectHandleOnStack targetParameter, out BindToMethodDetails bindToMethodDetails); private bool BindToMethodInfo(object? target, IRuntimeMethodInfo method, RuntimeType methodType, DelegateBindingFlags flags) { bool ret = BindToMethodInfo(RuntimeHelpers.GetMethodTable(this), (target != null) ? RuntimeHelpers.GetMethodTable(target) : null, - method.Value, new QCallTypeHandle(ref methodType), flags, out BindToMethodDetails bindToMethodDetails); + method.Value, new QCallTypeHandle(ref methodType), flags, ObjectHandleOnStack.Create(ref target), out BindToMethodDetails bindToMethodDetails); if (ret) { @@ -474,7 +473,7 @@ private bool BindToMethodInfo(object? target, IRuntimeMethodInfo method, Runtime [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "Delegate_BindToMethodInfo")] [return: MarshalAs(UnmanagedType.Bool)] - private static partial bool BindToMethodInfo(MethodTable* pDelegateMT, MethodTable *pTargetMT, RuntimeMethodHandleInternal method, QCallTypeHandle methodType, DelegateBindingFlags flags); + private static partial bool BindToMethodInfo(MethodTable* pDelegateMT, MethodTable *pTargetMT, RuntimeMethodHandleInternal method, QCallTypeHandle methodType, DelegateBindingFlags flags, ObjectHandleOnStack targetParameter, out BindToMethodDetails bindToMethodDetails); private static MulticastDelegate InternalAlloc(RuntimeType type) { diff --git a/src/coreclr/vm/comdelegate.cpp b/src/coreclr/vm/comdelegate.cpp index 562ef290dc161a..453f881ac6b498 100644 --- a/src/coreclr/vm/comdelegate.cpp +++ b/src/coreclr/vm/comdelegate.cpp @@ -991,7 +991,7 @@ static PCODE GetVirtualCallStub(MethodDesc *method, TypeHandle scopeType) } extern "C" BOOL QCALLTYPE Delegate_BindToMethodName(MethodTable* pDelegateMT, MethodTable *pTargetMT, - QCall::TypeHandle pMethodType, LPCUTF8 pszMethodName, DelegateBindingFlags flags, BindToMethodDetails *pBindToMethodDetails) + QCall::TypeHandle pMethodType, LPCUTF8 pszMethodName, DelegateBindingFlags flags, QCall::ObjectHandleOnStack targetParameter, BindToMethodDetails *pBindToMethodDetails) { QCALL_CONTRACT; @@ -1002,9 +1002,6 @@ extern "C" BOOL QCALLTYPE Delegate_BindToMethodName(MethodTable* pDelegateMT, Me TypeHandle methodType = pMethodType.AsTypeHandle(); - TypeHandle targetType = TypeHandle(pTargetMT); - MethodTable *pDelegateMT; - // get the invoke of the delegate MethodDesc* pInvokeMeth = COMDelegate::FindDelegateInvokeMethod(pDelegateMT); _ASSERTE(pInvokeMeth); @@ -1062,10 +1059,10 @@ extern "C" BOOL QCALLTYPE Delegate_BindToMethodName(MethodTable* pDelegateMT, Me false /* do not allow code with a shared-code calling convention to be returned */, true /* Ensure that methods on generic interfaces are returned as instantiated method descs */); bool fIsOpenDelegate; - if (!COMDelegate::IsMethodDescCompatible(targetType, + if (!COMDelegate::IsMethodDescCompatible(TypeHandle(pTargetMT), methodType, pCurMethod, - TypeHandle(pDelegateType), + TypeHandle(pDelegateMT), pInvokeMeth, flags, &fIsOpenDelegate)) @@ -1077,9 +1074,11 @@ extern "C" BOOL QCALLTYPE Delegate_BindToMethodName(MethodTable* pDelegateMT, Me // Found the target that matches the signature and satisfies security transparency rules // Initialize the delegate to point to the target method. COMDelegate::BindToMethod(pDelegateMT, + pTargetMT, pCurMethod, methodType.GetMethodTable(), fIsOpenDelegate, + targetParameter, pBindToMethodDetails); pMatchingMethod = pCurMethod; @@ -1097,7 +1096,7 @@ extern "C" BOOL QCALLTYPE Delegate_BindToMethodName(MethodTable* pDelegateMT, Me } extern "C" BOOL QCALLTYPE Delegate_BindToMethodInfo(MethodTable* pDelegateMT, MethodTable *pTargetMT, - MethodDesc * method, QCall::TypeHandle pMethodType, DelegateBindingFlags flags, BindToMethodDetails *pBindToMethodDetails) + MethodDesc * method, QCall::TypeHandle pMethodType, DelegateBindingFlags flags, QCall::ObjectHandleOnStack targetParameter, BindToMethodDetails *pBindToMethodDetails) { QCALL_CONTRACT; @@ -1117,7 +1116,6 @@ extern "C" BOOL QCALLTYPE Delegate_BindToMethodInfo(MethodTable* pDelegateMT, Me // See the comment in BindToMethodName bool fIsOpenDelegate; - bool fIsMethodDescCompatible; method = MethodDesc::FindOrCreateAssociatedMethodDesc(method, pMethMT, @@ -1136,9 +1134,11 @@ extern "C" BOOL QCALLTYPE Delegate_BindToMethodInfo(MethodTable* pDelegateMT, Me { // Initialize the delegate to point to the target method. COMDelegate::BindToMethod(pDelegateMT, + pTargetMT, method, pMethMT, fIsOpenDelegate, + targetParameter, pBindToMethodDetails); result = TRUE; @@ -1157,10 +1157,11 @@ extern "C" BOOL QCALLTYPE Delegate_BindToMethodInfo(MethodTable* pDelegateMT, Me // (signature matching etc.) have been done at this point, this method will simply initialize the delegate, with any required // wrapping. The delegate returned will be ready for invocation immediately. void COMDelegate::BindToMethod(MethodTable* pDelegateMT, - MethodTable* pTargetMT, + MethodTable *pTargetMT, MethodDesc *pTargetMethod, MethodTable *pExactMethodType, BOOL fIsOpenDelegate, + QCall::ObjectHandleOnStack targetParameter, BindToMethodDetails *pBindToMethodDetails) { CONTRACTL @@ -1243,7 +1244,7 @@ void COMDelegate::BindToMethod(MethodTable* pDelegateMT, && pTargetMT != NULL && pTargetMethod->GetMethodTable() != pTargetMT) { - pTargetCode = pTargetMethod->GetMultiCallableAddrOfVirtualizedCode(pRefFirstArg, pTargetMethod->GetMethodTable()); + pTargetCode = pTargetMethod->GetMultiCallableAddrOfVirtualizedCode((OBJECTREF*)targetParameter.GetObjectPointer(), pTargetMethod->GetMethodTable()); } #ifdef HAS_THISPTR_RETBUF_PRECODE else if (pTargetMethod->IsStatic() && pTargetMethod->HasRetBuffArg() && IsRetBuffPassedAsFirstArg()) diff --git a/src/coreclr/vm/comdelegate.h b/src/coreclr/vm/comdelegate.h index 27466bc5584519..e28a08fa301c4d 100644 --- a/src/coreclr/vm/comdelegate.h +++ b/src/coreclr/vm/comdelegate.h @@ -90,9 +90,11 @@ class COMDelegate static MethodDesc* GetDelegateCtor(TypeHandle delegateType, MethodDesc *pTargetMethod, DelegateCtorArgs *pCtorData); static void BindToMethod(MethodTable* pDelegateMT, + MethodTable *pTargetMT, MethodDesc *pTargetMethod, MethodTable *pExactMethodType, BOOL fIsOpenDelegate, + QCall::ObjectHandleOnStack targetParameter, BindToMethodDetails *pBindToMethodDetails); }; @@ -117,11 +119,11 @@ enum DelegateBindingFlags DBF_RelaxedSignature = 0x00000040, // Allow relaxed signature matching (co/contra variance) }; -extern "C" BOOL QCALLTYPE Delegate_BindToMethodName(QCall::ObjectHandleOnStack d, QCall::ObjectHandleOnStack target, - QCall::TypeHandle pMethodType, LPCUTF8 pszMethodName, DelegateBindingFlags flags); +extern "C" BOOL QCALLTYPE Delegate_BindToMethodName(MethodTable* pDelegateMT, MethodTable *pTargetMT, + QCall::TypeHandle pMethodType, LPCUTF8 pszMethodName, DelegateBindingFlags flags, QCall::ObjectHandleOnStack targetParameter, BindToMethodDetails *pBindToMethodDetails); extern "C" BOOL QCALLTYPE Delegate_BindToMethodInfo(MethodTable* pDelegateMT, MethodTable *pTargetMT, - MethodDesc * method, QCall::TypeHandle pMethodType, DelegateBindingFlags flags, BindToMethodDetails *pBindToMethodDetails); + MethodDesc * method, QCall::TypeHandle pMethodType, DelegateBindingFlags flags, QCall::ObjectHandleOnStack targetParameter, BindToMethodDetails *pBindToMethodDetails); extern "C" void QCALLTYPE Delegate_FindMethodHandle(QCall::ObjectHandleOnStack d, QCall::ObjectHandleOnStack retMethodInfo); diff --git a/src/coreclr/vm/qcall.h b/src/coreclr/vm/qcall.h index a260d1c494e3d7..96d59421ae50dc 100644 --- a/src/coreclr/vm/qcall.h +++ b/src/coreclr/vm/qcall.h @@ -208,6 +208,12 @@ class QCall return ObjectToOBJECTREF(*m_ppObject); } + Object** GetObjectPointer() const + { + LIMITED_METHOD_CONTRACT; + return m_ppObject; + } + #ifndef DACCESS_COMPILE // // Helpers for returning common managed types from QCall From 92f86c4c5802e778194aad5ea9da683a14bb2864 Mon Sep 17 00:00:00 2001 From: David Wrighton Date: Fri, 19 Jun 2026 14:10:45 -0700 Subject: [PATCH 14/19] Make the standard delegate slow construction path avoid GC transitions --- .../src/System/Delegate.CoreCLR.cs | 20 +++++++-- src/coreclr/vm/comdelegate.cpp | 42 +++++++------------ src/coreclr/vm/comdelegate.h | 2 +- 3 files changed, 33 insertions(+), 31 deletions(-) diff --git a/src/coreclr/System.Private.CoreLib/src/System/Delegate.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Delegate.CoreCLR.cs index 179a27a2829dbd..ae2d8852744d8c 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Delegate.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Delegate.CoreCLR.cs @@ -518,12 +518,26 @@ private void DelegateConstruct(object target, IntPtr method) throw new ArgumentNullException(nameof(method)); } - Delegate _this = this; - Construct(ObjectHandleOnStack.Create(ref _this), ObjectHandleOnStack.Create(ref target), method); + Construct(RuntimeHelpers.GetMethodTable(this), (target != null) ? RuntimeHelpers.GetMethodTable(target) : null, + method, out BindToMethodDetails bindToMethodDetails); + + // Apply the results of the QCall to the delegate instance. + _methodPtr = bindToMethodDetails.methodPtr; + _methodPtrAux = bindToMethodDetails.methodPtrAux; + Unsafe.As(this)._invocationCount = bindToMethodDetails.invocationCount; + if (bindToMethodDetails.loaderAllocatorGCHandle.IsAllocated) + { + _methodBase = bindToMethodDetails.loaderAllocatorGCHandle.Target; + } + + if (bindToMethodDetails.selfReferentialTarget != 0) + _target = this; + else + _target = target; } [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "Delegate_Construct")] - private static partial void Construct(ObjectHandleOnStack _this, ObjectHandleOnStack target, IntPtr method); + private static partial void Construct(MethodTable* pDelegateMT, MethodTable* pTargetMT, IntPtr method, out BindToMethodDetails bindToMethodDetails); [MethodImpl(MethodImplOptions.InternalCall)] private static extern unsafe void* GetMulticastInvoke(MethodTable* pMT); diff --git a/src/coreclr/vm/comdelegate.cpp b/src/coreclr/vm/comdelegate.cpp index 453f881ac6b498..d2d44dd4108860 100644 --- a/src/coreclr/vm/comdelegate.cpp +++ b/src/coreclr/vm/comdelegate.cpp @@ -1262,6 +1262,7 @@ void COMDelegate::BindToMethod(MethodTable* pDelegateMT, LoaderAllocator *pLoaderAllocator = pTargetMethod->GetLoaderAllocator(); + // If the loader allocator is not collectible, this will be NULL pBindToMethodDetails->loaderAllocatorGCHandle = pLoaderAllocator->GetLoaderAllocatorObjectHandle(); } @@ -1622,7 +1623,7 @@ uint32_t MethodDescToNumFixedArgs(MethodDesc *pMD) // This is the single constructor for all Delegates. The compiler // doesn't provide an implementation of the Delegate constructor. We // provide that implementation through a QCall call to this method. -extern "C" void QCALLTYPE Delegate_Construct(QCall::ObjectHandleOnStack _this, QCall::ObjectHandleOnStack target, PCODE method) +extern "C" void QCALLTYPE Delegate_Construct(MethodTable* pDelegateMT, MethodTable* pTargetMT, PCODE method, BindToMethodDetails *pBindToMethodDetails) { QCALL_CONTRACT; @@ -1631,23 +1632,15 @@ extern "C" void QCALLTYPE Delegate_Construct(QCall::ObjectHandleOnStack _this, Q _ASSERTE(method != (PCODE)NULL); BEGIN_QCALL; - GCX_COOP(); - - DELEGATEREF refThis = (DELEGATEREF) ObjectToOBJECTREF(_this.Get()); - _ASSERTE(refThis != NULL); - - GCPROTECT_BEGIN(refThis); + _ASSERTE(pDelegateMT != NULL); // Programmers could feed garbage data to DelegateConstruct(). // It's difficult to validate a method code pointer, but at least we'll // try to catch the easy garbage. _ASSERTE(isMemoryReadable(method, 1)); - MethodTable* pMTTarg = NULL; - if (target.Get() != NULL) - pMTTarg = target.Get()->GetMethodTable(); - - MethodTable* pDelMT = refThis->GetMethodTable(); + MethodTable* pMTTarg = pTargetMT; + MethodTable* pDelMT = pDelegateMT; MethodDesc* pMethOrig = NonVirtualEntry2MethodDesc(method); MethodDesc* pMeth = pMethOrig; _ASSERTE(pMeth != NULL); @@ -1676,42 +1669,41 @@ extern "C" void QCALLTYPE Delegate_Construct(QCall::ObjectHandleOnStack _this, Q if (!isStatic) methodArgCount++; // count 'this' - if (pMeth->GetLoaderAllocator()->IsCollectible()) - refThis->SetMethodBase(pMeth->GetLoaderAllocator()->GetExposedObject()); + // If the loader allocator is not collectible, this will be NULL + pBindToMethodDetails->loaderAllocatorGCHandle = pMeth->GetLoaderAllocator()->GetLoaderAllocatorObjectHandle(); // Open delegates. if (invokeArgCount == methodArgCount) { - // set the target - refThis->SetTarget(refThis); + pBindToMethodDetails->selfReferentialTarget = TRUE; // set the shuffle thunk PCODE pEntryPoint = SetupShuffleThunk(pDelMT, pMeth); - refThis->SetMethodPtr(pEntryPoint); + pBindToMethodDetails->methodPtr = pEntryPoint; // set the ptr aux according to what is needed, if virtual need to call make virtual stub dispatch if (!pMeth->IsStatic() && pMeth->IsVirtual() && !pMeth->GetMethodTable()->IsValueType()) { PCODE pTargetCall; { - GCX_PREEMP(); pTargetCall = GetVirtualCallStub(pMeth, TypeHandle(pMeth->GetMethodTable())); } - refThis->SetMethodPtrAux(pTargetCall); - refThis->SetInvocationCount((INT_PTR)(void *)pMeth); + pBindToMethodDetails->methodPtrAux = pTargetCall; + pBindToMethodDetails->invocationCount = (INT_PTR)(void *)pMeth; } else { - refThis->SetMethodPtrAux(method); + pBindToMethodDetails->methodPtrAux = method; } } else { + pBindToMethodDetails->selfReferentialTarget = FALSE; MethodTable* pMTMeth = pMeth->GetMethodTable(); if (!pMeth->IsStatic()) { - if (target.Get() == NULL) + if (pTargetMT == NULL) COMPlusThrow(kArgumentException, W("Arg_DlgtNullInst")); if (pMTTarg != NULL) @@ -1731,7 +1723,6 @@ extern "C" void QCALLTYPE Delegate_Construct(QCall::ObjectHandleOnStack _this, Q && (pMTMeth != g_pObjectClass)) { pMeth->CheckRestore(); - GCX_PREEMP(); pMeth = pMTTarg->GetBoxedEntryPointMD(pMeth); _ASSERTE(pMeth != NULL); } @@ -1741,7 +1732,6 @@ extern "C" void QCALLTYPE Delegate_Construct(QCall::ObjectHandleOnStack _this, Q // so we don't have to do all this mucking about. if (pMeth != pMethOrig) { - GCX_PREEMP(); method = pMeth->GetMultiCallableAddrOfCode(); } } @@ -1753,11 +1743,9 @@ extern "C" void QCALLTYPE Delegate_Construct(QCall::ObjectHandleOnStack _this, Q } #endif // HAS_THISPTR_RETBUF_PRECODE - refThis->SetTarget(target.Get()); - refThis->SetMethodPtr((PCODE)(void *)method); + pBindToMethodDetails->methodPtr = (PCODE)(void *)method; } - GCPROTECT_END(); END_QCALL; } diff --git a/src/coreclr/vm/comdelegate.h b/src/coreclr/vm/comdelegate.h index e28a08fa301c4d..63d3c473703572 100644 --- a/src/coreclr/vm/comdelegate.h +++ b/src/coreclr/vm/comdelegate.h @@ -98,7 +98,7 @@ class COMDelegate BindToMethodDetails *pBindToMethodDetails); }; -extern "C" void QCALLTYPE Delegate_Construct(QCall::ObjectHandleOnStack _this, QCall::ObjectHandleOnStack target, PCODE method); +extern "C" void QCALLTYPE Delegate_Construct(MethodTable* pDelegateMT, MethodTable* pTargetMT, PCODE method, BindToMethodDetails *pBindToMethodDetails); extern "C" PCODE QCALLTYPE Delegate_GetMulticastInvokeSlow(MethodTable* pDelegateMT); From efa3adbb4c3636a57ac0b86014e520b897fc1a93 Mon Sep 17 00:00:00 2001 From: David Wrighton Date: Fri, 19 Jun 2026 14:13:09 -0700 Subject: [PATCH 15/19] Safety comment about cast --- src/coreclr/vm/comdelegate.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/coreclr/vm/comdelegate.cpp b/src/coreclr/vm/comdelegate.cpp index d2d44dd4108860..564a986318813a 100644 --- a/src/coreclr/vm/comdelegate.cpp +++ b/src/coreclr/vm/comdelegate.cpp @@ -1244,6 +1244,7 @@ void COMDelegate::BindToMethod(MethodTable* pDelegateMT, && pTargetMT != NULL && pTargetMethod->GetMethodTable() != pTargetMT) { + // Casting Object** to OBJECTREF* is safe as long as no code attempts to mutate the OBJECTREF through the OBJECTREF* (and we don't, we only use it to read the this pointer/target object) pTargetCode = pTargetMethod->GetMultiCallableAddrOfVirtualizedCode((OBJECTREF*)targetParameter.GetObjectPointer(), pTargetMethod->GetMethodTable()); } #ifdef HAS_THISPTR_RETBUF_PRECODE From 827e26d8359af9de0def7926fc5fd73e7ffcc40b Mon Sep 17 00:00:00 2001 From: David Wrighton Date: Fri, 19 Jun 2026 14:36:50 -0700 Subject: [PATCH 16/19] Fix global.json oops --- global.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/global.json b/global.json index 8d47900ffba1b7..7551131ef396f5 100644 --- a/global.json +++ b/global.json @@ -13,11 +13,11 @@ "dotnet": "11.0.100-preview.5.26227.104" }, "msbuild-sdks": { - "Microsoft.DotNet.Arcade.Sdk": "11.0.0-beta.26302.118", - "Microsoft.DotNet.Helix.Sdk": "11.0.0-beta.26302.118", - "Microsoft.DotNet.SharedFramework.Sdk": "11.0.0-beta.26302.118", + "Microsoft.DotNet.Arcade.Sdk": "11.0.0-beta.26317.104", + "Microsoft.DotNet.Helix.Sdk": "11.0.0-beta.26317.104", + "Microsoft.DotNet.SharedFramework.Sdk": "11.0.0-beta.26317.104", "Microsoft.Build.NoTargets": "3.7.0", "Microsoft.Build.Traversal": "3.4.0", - "Microsoft.NET.Sdk.IL": "11.0.0-preview.6.26302.118" + "Microsoft.NET.Sdk.IL": "11.0.0-preview.6.26317.104" } } From 5a9fe47837131aaee1e913003748d514a99a4895 Mon Sep 17 00:00:00 2001 From: David Wrighton Date: Mon, 22 Jun 2026 16:23:21 -0700 Subject: [PATCH 17/19] Optimize access to the default FuncPtrStub --- src/coreclr/vm/fptrstubs.cpp | 23 +++++++++++++++++++++++ src/coreclr/vm/method.cpp | 21 +++++++++++++++++++-- src/coreclr/vm/method.hpp | 3 +++ 3 files changed, 45 insertions(+), 2 deletions(-) diff --git a/src/coreclr/vm/fptrstubs.cpp b/src/coreclr/vm/fptrstubs.cpp index f0e3a466df6bf0..e0520e5c14853b 100644 --- a/src/coreclr/vm/fptrstubs.cpp +++ b/src/coreclr/vm/fptrstubs.cpp @@ -69,10 +69,16 @@ PCODE FuncPtrStubs::GetFuncPtrStub(MethodDesc * pMD, PrecodeType type) CONTRACTL_END Precode* pPrecode = NULL; + + if (type != GetDefaultType(pMD)) { CrstHolder ch(&m_hashTableCrst); pPrecode = m_hashTable.Lookup(PrecodeKey(pMD, type)); } + else + { + pPrecode = pMD->GetDefaultFuncPtrStub(); + } if (pPrecode != NULL) { @@ -129,6 +135,23 @@ PCODE FuncPtrStubs::GetFuncPtrStub(MethodDesc * pMD, PrecodeType type) pNewPrecode->SetTargetInterlocked(target); } + if (type == GetDefaultType(pMD)) + { + // Set the default funcptr stub in the MethodDesc if it is not already set. If another thread beat us to it, then + // we will use the one that was set by the other thread. + if (pMD->SetDefaultFuncPtrStub(pNewPrecode)) + { + pPrecode = pNewPrecode; + amt.SuppressRelease(); + } + else + { + pPrecode = pMD->GetDefaultFuncPtrStub(); + _ASSERTE(pPrecode != NULL); + setTargetAfterAddingToHashTable = false; + } + } + else { CrstHolder ch(&m_hashTableCrst); diff --git a/src/coreclr/vm/method.cpp b/src/coreclr/vm/method.cpp index ac0d91b15957e8..8ee8cab723d35a 100644 --- a/src/coreclr/vm/method.cpp +++ b/src/coreclr/vm/method.cpp @@ -275,6 +275,16 @@ void MethodDesc::SetMethodDescOptimizationTier(NativeCodeVersion::OptimizationTi VolatileStoreWithoutBarrier(&m_codeData->OptimizationTier, tier); } +bool MethodDesc::SetDefaultFuncPtrStub(Precode* stub) +{ + STANDARD_VM_CONTRACT; + + IfFailThrow(EnsureCodeDataExists(NULL)); + + _ASSERTE(m_codeData != NULL); + return InterlockedCompareExchangeT((void**)&m_codeData->DefaultFuncPtrStub, (void*)stub, (void*)NULL) == NULL; +} + #if defined(_DEBUG) && defined(ALLOW_SXS_JIT) HRESULT MethodDesc::SetMethodDescAltJitPatchpointInfo(PatchpointInfo* pInfo) { @@ -343,6 +353,15 @@ NativeCodeVersion::OptimizationTier MethodDesc::GetMethodDescOptimizationTier() return NativeCodeVersion::OptimizationTierUnknown; return VolatileLoadWithoutBarrier(&codeData->OptimizationTier); } + +Precode* MethodDesc::GetDefaultFuncPtrStub() +{ + WRAPPER_NO_CONTRACT; + PTR_MethodDescCodeData codeData = VolatileLoadWithoutBarrier(&m_codeData); + if (codeData == NULL) + return NULL; + return VolatileLoadWithoutBarrier(&codeData->DefaultFuncPtrStub); +} #endif // FEATURE_CODE_VERSIONING //******************************************************************************* @@ -2149,8 +2168,6 @@ PCODE MethodDesc::GetMultiCallableAddrOfCode(CORINFO_ACCESS_FLAGS accessFlags /* #else if (ret == (PCODE)NULL) { - GCX_COOP(); - // We have to allocate funcptr stub ret = GetLoaderAllocator()->GetFuncPtrStubs()->GetFuncPtrStub(this); } diff --git a/src/coreclr/vm/method.hpp b/src/coreclr/vm/method.hpp index 628c3f4de7bb47..765baa62a00e37 100644 --- a/src/coreclr/vm/method.hpp +++ b/src/coreclr/vm/method.hpp @@ -262,6 +262,7 @@ struct MethodDescCodeData final #ifdef FEATURE_CODE_VERSIONING PTR_MethodDescVersioningState VersioningState; NativeCodeVersion::OptimizationTier OptimizationTier; + Precode* DefaultFuncPtrStub; #endif // FEATURE_CODE_VERSIONING PCODE TemporaryEntryPoint; #ifdef FEATURE_INTERPRETER @@ -2010,9 +2011,11 @@ class MethodDesc HRESULT SetMethodDescAltJitPatchpointInfo(PatchpointInfo* pInfo); PatchpointInfo* GetMethodDescAltJitPatchpointInfo(); #endif + bool SetDefaultFuncPtrStub(Precode* stub); #endif // !DACCESS_COMPILE PTR_MethodDescVersioningState GetMethodDescVersionState(); NativeCodeVersion::OptimizationTier GetMethodDescOptimizationTier(); + Precode* GetDefaultFuncPtrStub(); #endif // FEATURE_CODE_VERSIONING public: From 5aff2d6041ad49e8bf17f32b64616156b743d436 Mon Sep 17 00:00:00 2001 From: David Wrighton Date: Mon, 22 Jun 2026 17:15:44 -0700 Subject: [PATCH 18/19] Fix build break --- .../System.Private.CoreLib/src/System/Delegate.CoreCLR.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/coreclr/System.Private.CoreLib/src/System/Delegate.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Delegate.CoreCLR.cs index bf5e9c529a0804..7d15b3607ad5e1 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Delegate.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Delegate.CoreCLR.cs @@ -421,7 +421,7 @@ private bool BindToMethodName(object? target, [DynamicallyAccessedMembers(Dynami Unsafe.As(this)._invocationCount = bindToMethodDetails.invocationCount; if (bindToMethodDetails.loaderAllocatorGCHandle.IsAllocated) { - _methodBase = bindToMethodDetails.loaderAllocatorGCHandle.Target; + _helperObject = bindToMethodDetails.loaderAllocatorGCHandle.Target; GC.KeepAlive(method); } @@ -459,7 +459,7 @@ private bool BindToMethodInfo(object? target, IRuntimeMethodInfo method, Runtime Unsafe.As(this)._invocationCount = bindToMethodDetails.invocationCount; if (bindToMethodDetails.loaderAllocatorGCHandle.IsAllocated) { - _methodBase = bindToMethodDetails.loaderAllocatorGCHandle.Target; + _helperObject = bindToMethodDetails.loaderAllocatorGCHandle.Target; GC.KeepAlive(method); } @@ -527,7 +527,7 @@ private void DelegateConstruct(object target, IntPtr method) Unsafe.As(this)._invocationCount = bindToMethodDetails.invocationCount; if (bindToMethodDetails.loaderAllocatorGCHandle.IsAllocated) { - _methodBase = bindToMethodDetails.loaderAllocatorGCHandle.Target; + _helperObject = bindToMethodDetails.loaderAllocatorGCHandle.Target; } if (bindToMethodDetails.selfReferentialTarget != 0) From b75b3db2315191d8b6fbda161430751be13c3584 Mon Sep 17 00:00:00 2001 From: David Wrighton Date: Fri, 26 Jun 2026 17:28:27 -0700 Subject: [PATCH 19/19] Remove default FuncPtrStub caching optimization The MethodDescCodeData.DefaultFuncPtrStub caching optimization is being extracted into its own PR, so back it out here. The unrelated GCX_COOP removal in GetMultiCallableAddrOfCode (part of the preemptive-mode work) is retained. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- src/coreclr/vm/fptrstubs.cpp | 23 ----------------------- src/coreclr/vm/method.cpp | 19 ------------------- src/coreclr/vm/method.hpp | 3 --- 3 files changed, 45 deletions(-) diff --git a/src/coreclr/vm/fptrstubs.cpp b/src/coreclr/vm/fptrstubs.cpp index e0520e5c14853b..f0e3a466df6bf0 100644 --- a/src/coreclr/vm/fptrstubs.cpp +++ b/src/coreclr/vm/fptrstubs.cpp @@ -69,16 +69,10 @@ PCODE FuncPtrStubs::GetFuncPtrStub(MethodDesc * pMD, PrecodeType type) CONTRACTL_END Precode* pPrecode = NULL; - - if (type != GetDefaultType(pMD)) { CrstHolder ch(&m_hashTableCrst); pPrecode = m_hashTable.Lookup(PrecodeKey(pMD, type)); } - else - { - pPrecode = pMD->GetDefaultFuncPtrStub(); - } if (pPrecode != NULL) { @@ -135,23 +129,6 @@ PCODE FuncPtrStubs::GetFuncPtrStub(MethodDesc * pMD, PrecodeType type) pNewPrecode->SetTargetInterlocked(target); } - if (type == GetDefaultType(pMD)) - { - // Set the default funcptr stub in the MethodDesc if it is not already set. If another thread beat us to it, then - // we will use the one that was set by the other thread. - if (pMD->SetDefaultFuncPtrStub(pNewPrecode)) - { - pPrecode = pNewPrecode; - amt.SuppressRelease(); - } - else - { - pPrecode = pMD->GetDefaultFuncPtrStub(); - _ASSERTE(pPrecode != NULL); - setTargetAfterAddingToHashTable = false; - } - } - else { CrstHolder ch(&m_hashTableCrst); diff --git a/src/coreclr/vm/method.cpp b/src/coreclr/vm/method.cpp index 8ee8cab723d35a..18cc268893c220 100644 --- a/src/coreclr/vm/method.cpp +++ b/src/coreclr/vm/method.cpp @@ -275,16 +275,6 @@ void MethodDesc::SetMethodDescOptimizationTier(NativeCodeVersion::OptimizationTi VolatileStoreWithoutBarrier(&m_codeData->OptimizationTier, tier); } -bool MethodDesc::SetDefaultFuncPtrStub(Precode* stub) -{ - STANDARD_VM_CONTRACT; - - IfFailThrow(EnsureCodeDataExists(NULL)); - - _ASSERTE(m_codeData != NULL); - return InterlockedCompareExchangeT((void**)&m_codeData->DefaultFuncPtrStub, (void*)stub, (void*)NULL) == NULL; -} - #if defined(_DEBUG) && defined(ALLOW_SXS_JIT) HRESULT MethodDesc::SetMethodDescAltJitPatchpointInfo(PatchpointInfo* pInfo) { @@ -353,15 +343,6 @@ NativeCodeVersion::OptimizationTier MethodDesc::GetMethodDescOptimizationTier() return NativeCodeVersion::OptimizationTierUnknown; return VolatileLoadWithoutBarrier(&codeData->OptimizationTier); } - -Precode* MethodDesc::GetDefaultFuncPtrStub() -{ - WRAPPER_NO_CONTRACT; - PTR_MethodDescCodeData codeData = VolatileLoadWithoutBarrier(&m_codeData); - if (codeData == NULL) - return NULL; - return VolatileLoadWithoutBarrier(&codeData->DefaultFuncPtrStub); -} #endif // FEATURE_CODE_VERSIONING //******************************************************************************* diff --git a/src/coreclr/vm/method.hpp b/src/coreclr/vm/method.hpp index 974ad948d3d27f..53210db12e804a 100644 --- a/src/coreclr/vm/method.hpp +++ b/src/coreclr/vm/method.hpp @@ -262,7 +262,6 @@ struct MethodDescCodeData final #ifdef FEATURE_CODE_VERSIONING PTR_MethodDescVersioningState VersioningState; NativeCodeVersion::OptimizationTier OptimizationTier; - Precode* DefaultFuncPtrStub; #endif // FEATURE_CODE_VERSIONING PCODE TemporaryEntryPoint; #ifdef FEATURE_INTERPRETER @@ -2011,11 +2010,9 @@ class MethodDesc HRESULT SetMethodDescAltJitPatchpointInfo(PatchpointInfo* pInfo); PatchpointInfo* GetMethodDescAltJitPatchpointInfo(); #endif - bool SetDefaultFuncPtrStub(Precode* stub); #endif // !DACCESS_COMPILE PTR_MethodDescVersioningState GetMethodDescVersionState(); NativeCodeVersion::OptimizationTier GetMethodDescOptimizationTier(); - Precode* GetDefaultFuncPtrStub(); #endif // FEATURE_CODE_VERSIONING public: