From fccac9050268e520cb9ced91b727f6beef426e9e Mon Sep 17 00:00:00 2001 From: Aaron R Robinson Date: Wed, 19 Mar 2025 14:53:03 -0700 Subject: [PATCH 01/15] Remove explicit GC Poll from GC.SuppressFinalize() --- .../src/System/GC.CoreCLR.cs | 12 ++++++++---- src/coreclr/vm/comutilnative.cpp | 15 ++++----------- src/coreclr/vm/comutilnative.h | 3 ++- src/coreclr/vm/ecalllist.h | 2 -- src/coreclr/vm/qcallentrypoints.cpp | 1 + 5 files changed, 15 insertions(+), 18 deletions(-) diff --git a/src/coreclr/System.Private.CoreLib/src/System/GC.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/GC.CoreCLR.cs index babcf894736ddf..c76c3bff82e85b 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/GC.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/GC.CoreCLR.cs @@ -346,14 +346,18 @@ public static void WaitForPendingFinalizers() // Indicates that the system should not call the Finalize() method on // an object that would normally require this call. - [MethodImpl(MethodImplOptions.InternalCall)] - private static extern void _SuppressFinalize(object o); + [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "GCInterface_SuppressFinalize")] + [SuppressGCTransition] + private static partial void SuppressFinalize(ObjectHandleOnStack obj); - public static void SuppressFinalize(object obj) + public static unsafe void SuppressFinalize(object obj) { ArgumentNullException.ThrowIfNull(obj); - _SuppressFinalize(obj); + if (RuntimeHelpers.GetMethodTable(obj)->HasFinalizer) + { + SuppressFinalize(ObjectHandleOnStack.Create(ref obj)); + } } // Indicates that the system should call the Finalize() method on an object diff --git a/src/coreclr/vm/comutilnative.cpp b/src/coreclr/vm/comutilnative.cpp index 91a1c8c68f2166..c8a12d3d0db1fa 100644 --- a/src/coreclr/vm/comutilnative.cpp +++ b/src/coreclr/vm/comutilnative.cpp @@ -1106,21 +1106,14 @@ extern "C" void QCALLTYPE GCInterface_UnregisterFrozenSegment(void* segment) **Arguments: Object of interest **Exceptions: None ==============================================================================*/ -FCIMPL1(void, GCInterface::SuppressFinalize, Object *obj) +extern "C" void QCALLTYPE GCInterface_SuppressFinalize(QCall::ObjectHandleOnStack pObj) { - FCALL_CONTRACT; + QCALL_CONTRACT_NO_GC_TRANSITION; // Checked by the caller - _ASSERTE(obj != NULL); - - if (!obj->GetMethodTable ()->HasFinalizer()) - return; - - GCHeapUtilities::GetGCHeap()->SetFinalizationRun(obj); - FC_GC_POLL(); + _ASSERTE(pObj.Get()->GetMethodTable()->HasFinalizer()); + GCHeapUtilities::GetGCHeap()->SetFinalizationRun(OBJECTREFToObject(pObj.Get())); } -FCIMPLEND - /*============================ReRegisterForFinalize============================== **Action: Indicate that an object's finalizer should be run by the system. diff --git a/src/coreclr/vm/comutilnative.h b/src/coreclr/vm/comutilnative.h index 2d9116e135c590..5dda57c301d665 100644 --- a/src/coreclr/vm/comutilnative.h +++ b/src/coreclr/vm/comutilnative.h @@ -181,7 +181,6 @@ class GCInterface { static FCDECL0(int, GetMaxGeneration); static FCDECL1(void, KeepAlive, Object *obj); - static FCDECL1(void, SuppressFinalize, Object *obj); static FCDECL2(int, CollectionCount, INT32 generation, INT32 getSpecialGCCount); static FCDECL0(INT64, GetAllocatedBytesForCurrentThread); @@ -212,6 +211,8 @@ extern "C" INT64 QCALLTYPE GCInterface_GetTotalMemory(); extern "C" void QCALLTYPE GCInterface_Collect(INT32 generation, INT32 mode); +extern "C" void QCALLTYPE GCInterface_SuppressFinalize(QCall::ObjectHandleOnStack pObj); + extern "C" void* QCALLTYPE GCInterface_GetNextFinalizableObject(QCall::ObjectHandleOnStack pObj); extern "C" void QCALLTYPE GCInterface_WaitForPendingFinalizers(); diff --git a/src/coreclr/vm/ecalllist.h b/src/coreclr/vm/ecalllist.h index 6758a6f007d132..744215738d188a 100644 --- a/src/coreclr/vm/ecalllist.h +++ b/src/coreclr/vm/ecalllist.h @@ -270,8 +270,6 @@ FCFuncStart(gGCInterfaceFuncs) FCFuncElement("GetGenerationSize", GCInterface::GetGenerationSize) FCFuncElement("GetGenerationInternal", GCInterface::GetGenerationInternal) FCFuncElement("GetMaxGeneration", GCInterface::GetMaxGeneration) - FCFuncElement("_SuppressFinalize", GCInterface::SuppressFinalize) - FCFuncElement("GetAllocatedBytesForCurrentThread", GCInterface::GetAllocatedBytesForCurrentThread) FCFuncElement("GetTotalAllocatedBytesApproximate", GCInterface::GetTotalAllocatedBytesApproximate) FCFuncEnd() diff --git a/src/coreclr/vm/qcallentrypoints.cpp b/src/coreclr/vm/qcallentrypoints.cpp index 5ac5a3889f9386..ede8bc415112d0 100644 --- a/src/coreclr/vm/qcallentrypoints.cpp +++ b/src/coreclr/vm/qcallentrypoints.cpp @@ -318,6 +318,7 @@ static const Entry s_QCall[] = DllImportEntry(GCInterface_AllocateNewArray) DllImportEntry(GCInterface_GetTotalMemory) DllImportEntry(GCInterface_Collect) + DllImportEntry(GCInterface_SuppressFinalize) DllImportEntry(GCInterface_ReRegisterForFinalize) DllImportEntry(GCInterface_GetNextFinalizableObject) DllImportEntry(GCInterface_WaitForPendingFinalizers) From 1f0d713bf67dc8f8bde4e9f8c5694e5b9dce2d49 Mon Sep 17 00:00:00 2001 From: Aaron R Robinson Date: Wed, 19 Mar 2025 16:11:18 -0700 Subject: [PATCH 02/15] Remove GC Poll from FCall and move it to managed. --- .../System.Private.CoreLib/src/System/Buffer.CoreCLR.cs | 9 ++++++++- .../src/System/Threading/Thread.CoreCLR.cs | 9 ++++++--- src/coreclr/vm/comutilnative.cpp | 2 -- src/coreclr/vm/ecalllist.h | 2 +- src/coreclr/vm/fcall.cpp | 1 - src/coreclr/vm/fcall.h | 1 - 6 files changed, 15 insertions(+), 9 deletions(-) diff --git a/src/coreclr/System.Private.CoreLib/src/System/Buffer.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Buffer.CoreCLR.cs index 2a375e74035f0d..15c4c31d86bf65 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Buffer.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Buffer.CoreCLR.cs @@ -4,6 +4,7 @@ using System.Diagnostics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; +using System.Threading; namespace System { @@ -16,7 +17,13 @@ public partial class Buffer private static unsafe partial void __Memmove(byte* dest, byte* src, nuint len); [MethodImpl(MethodImplOptions.InternalCall)] - private static extern void __BulkMoveWithWriteBarrier(ref byte destination, ref byte source, nuint byteCount); + private static extern void BulkMoveWithWriteBarrierInternal(ref byte destination, ref byte source, nuint byteCount); + + private static void __BulkMoveWithWriteBarrier(ref byte destination, ref byte source, nuint byteCount) + { + Thread.PollGC(); + BulkMoveWithWriteBarrierInternal(ref destination, ref source, byteCount); + } // Used by ilmarshalers.cpp internal static unsafe void Memcpy(byte* dest, byte* src, int len) diff --git a/src/coreclr/System.Private.CoreLib/src/System/Threading/Thread.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Threading/Thread.CoreCLR.cs index c646bada45e01c..b52d483053048b 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Threading/Thread.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Threading/Thread.CoreCLR.cs @@ -492,18 +492,21 @@ private void ResetFinalizerThreadSlow() } [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "ThreadNative_PollGC")] - private static partial void ThreadNative_PollGC(); + private static partial void PollGCInternal(); // GC Suspension is done by simply dropping into native code via p/invoke, and we reuse the p/invoke // mechanism for suspension. On all architectures we should have the actual stub used for the check be implemented // as a small assembly stub which checks the global g_TrapReturningThreads flag and tail-call to this helper - private static unsafe void PollGC() + internal static unsafe void PollGC() { NativeThreadState catchAtSafePoint = ((NativeThreadClass*)Thread.DirectOnThreadLocalData.pNativeThread)->m_State & NativeThreadState.TS_CatchAtSafePoint; if (catchAtSafePoint != NativeThreadState.None) { - ThreadNative_PollGC(); + PollGCWorker(); } + + [MethodImpl(MethodImplOptions.NoInlining)] + static void PollGCWorker() => PollGCInternal(); } [StructLayout(LayoutKind.Sequential)] diff --git a/src/coreclr/vm/comutilnative.cpp b/src/coreclr/vm/comutilnative.cpp index c8a12d3d0db1fa..7f1ebf7e702e7c 100644 --- a/src/coreclr/vm/comutilnative.cpp +++ b/src/coreclr/vm/comutilnative.cpp @@ -553,8 +553,6 @@ FCIMPL3(VOID, Buffer::BulkMoveWithWriteBarrier, void *dst, void *src, size_t byt if (dst != src && byteCount != 0) InlinedMemmoveGCRefsHelper(dst, src, byteCount); - - FC_GC_POLL(); } FCIMPLEND diff --git a/src/coreclr/vm/ecalllist.h b/src/coreclr/vm/ecalllist.h index 744215738d188a..41acb57f1a2c3c 100644 --- a/src/coreclr/vm/ecalllist.h +++ b/src/coreclr/vm/ecalllist.h @@ -250,7 +250,7 @@ FCFuncStart(gArrayFuncs) FCFuncEnd() FCFuncStart(gBufferFuncs) - FCFuncElement("__BulkMoveWithWriteBarrier", Buffer::BulkMoveWithWriteBarrier) + FCFuncElement("BulkMoveWithWriteBarrierInternal", Buffer::BulkMoveWithWriteBarrier) FCFuncEnd() FCFuncStart(gGCFrameRegistration) diff --git a/src/coreclr/vm/fcall.cpp b/src/coreclr/vm/fcall.cpp index fc2e16f98a49b3..2696df624374a9 100644 --- a/src/coreclr/vm/fcall.cpp +++ b/src/coreclr/vm/fcall.cpp @@ -181,7 +181,6 @@ DEBUG_NOINLINE FCallCheck::~FCallCheck() // If you don't have a helper frame you can used // // FC_GC_POLL_AND_RETURN_OBJREF or - // FC_GC_POLL or // FC_GC_POLL_RET // // Note that these must be at GC safe points. In particular diff --git a/src/coreclr/vm/fcall.h b/src/coreclr/vm/fcall.h index 0b362c0cc71696..4ed9e56fac7832 100644 --- a/src/coreclr/vm/fcall.h +++ b/src/coreclr/vm/fcall.h @@ -815,7 +815,6 @@ Object* FC_GCPoll(void* me, Object* objToProtect = NULL); } \ } -#define FC_GC_POLL() FC_GC_POLL_EX(;) #define FC_GC_POLL_RET() FC_GC_POLL_EX(0) #define FC_GC_POLL_AND_RETURN_OBJREF(obj) \ From b8863768c34c54eeb052b040145e43d49ffb7f41 Mon Sep 17 00:00:00 2001 From: Aaron R Robinson Date: Wed, 19 Mar 2025 17:50:52 -0700 Subject: [PATCH 03/15] Convert GCInterface methods to QCalls and suppress GC. This removes the explicit GC Poll operation. --- .../src/System/GC.CoreCLR.cs | 19 +++++----- src/coreclr/vm/comutilnative.cpp | 35 +++++++------------ src/coreclr/vm/comutilnative.h | 9 +++-- src/coreclr/vm/ecalllist.h | 3 -- src/coreclr/vm/qcallentrypoints.cpp | 3 ++ 5 files changed, 33 insertions(+), 36 deletions(-) diff --git a/src/coreclr/System.Private.CoreLib/src/System/GC.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/GC.CoreCLR.cs index c76c3bff82e85b..6403a6dd007d16 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/GC.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/GC.CoreCLR.cs @@ -116,11 +116,13 @@ internal enum GC_ALLOC_FLAGS [MethodImpl(MethodImplOptions.InternalCall)] private static extern int GetMaxGeneration(); - [MethodImpl(MethodImplOptions.InternalCall)] - private static extern int _CollectionCount(int generation, int getSpecialGCCount); + [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "GCInterface_GetSegmentSize")] + [SuppressGCTransition] + internal static partial ulong GetSegmentSize(); - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern ulong GetSegmentSize(); + [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "GCInterface_CollectionCount")] + [SuppressGCTransition] + private static partial int CollectionCountInternal(int generation, int getSpecialGCCount); [MethodImpl(MethodImplOptions.InternalCall)] internal static extern int GetLastGCPercentTimeInGC(); @@ -161,11 +163,12 @@ public static void RemoveMemoryPressure(long bytesAllocated) public static int GetGeneration(object obj) { ArgumentNullException.ThrowIfNull(obj); - return GetGenerationInternal(obj); + return GetGenerationInternal(ObjectHandleOnStack.Create(ref obj)); } - [MethodImpl(MethodImplOptions.InternalCall)] - private static extern int GetGenerationInternal(object obj); + [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "GCInterface_GetGeneration")] + [SuppressGCTransition] + private static partial int GetGenerationInternal(ObjectHandleOnStack obj); // Forces a collection of all generations from 0 through Generation. // @@ -244,7 +247,7 @@ public static void Collect(int generation, GCCollectionMode mode, bool blocking, public static int CollectionCount(int generation) { ArgumentOutOfRangeException.ThrowIfNegative(generation); - return _CollectionCount(generation, 0); + return CollectionCountInternal(generation, 0); } // This method DOES NOT DO ANYTHING in and of itself. It's used to diff --git a/src/coreclr/vm/comutilnative.cpp b/src/coreclr/vm/comutilnative.cpp index 7f1ebf7e702e7c..0a45aede4e58bb 100644 --- a/src/coreclr/vm/comutilnative.cpp +++ b/src/coreclr/vm/comutilnative.cpp @@ -725,28 +725,25 @@ extern "C" int QCALLTYPE GCInterface_WaitForFullGCComplete(int millisecondsTimeo return result; } -/*================================GetGenerationInternal================================= -**Action: Returns the generation in which args->obj is found. -**Returns: The generation in which args->obj is found. -**Arguments: args->obj -- The object to locate. +/*================================GetGeneration================================= +**Action: Returns the generation in which object is found. +**Returns: The generation in which object is found. ==============================================================================*/ -FCIMPL1(int, GCInterface::GetGenerationInternal, Object* objUNSAFE) +extern "C" int QCALLTYPE GCInterface_GetGeneration(QCall::ObjectHandleOnStack pObjRef) { - FCALL_CONTRACT; - _ASSERTE(objUNSAFE != NULL); - int result = (INT32)GCHeapUtilities::GetGCHeap()->WhichGeneration(objUNSAFE); - FC_GC_POLL_RET(); - return result; + QCALL_CONTRACT_NO_GC_TRANSITION; + + _ASSERTE(pObjRef.Get() != NULL); + return (INT32)GCHeapUtilities::GetGCHeap()->WhichGeneration(OBJECTREFToObject(pObjRef.Get())); } -FCIMPLEND /*================================GetSegmentSize========-======================= **Action: Returns the maximum GC heap segment size **Returns: The maximum segment size of either the normal heap or the large object heap, whichever is bigger ==============================================================================*/ -FCIMPL0(UINT64, GCInterface::GetSegmentSize) +extern "C" UINT64 QCALLTYPE GCInterface_GetSegmentSize() { - FCALL_CONTRACT; + QCALL_CONTRACT_NO_GC_TRANSITION; IGCHeap * pGC = GCHeapUtilities::GetGCHeap(); size_t segment_size = pGC->GetValidSegmentSize(false); @@ -755,30 +752,24 @@ FCIMPL0(UINT64, GCInterface::GetSegmentSize) if (segment_size < large_segment_size) segment_size = large_segment_size; - FC_GC_POLL_RET(); - return (UINT64) segment_size; + return (UINT64)segment_size; } -FCIMPLEND /*================================CollectionCount================================= **Action: Returns the number of collections for this generation since the beginning of the life of the process **Returns: The collection count. -**Arguments: args->generation -- The generation -**Exceptions: Argument exception if args->generation is < 0 or > GetMaxGeneration(); ==============================================================================*/ -FCIMPL2(int, GCInterface::CollectionCount, INT32 generation, INT32 getSpecialGCCount) +extern "C" int QCALLTYPE GCInterface_CollectionCount(INT32 generation, INT32 getSpecialGCCount) { - FCALL_CONTRACT; + QCALL_CONTRACT_NO_GC_TRANSITION; //We've already checked this in GC.cs, so we'll just assert it here. _ASSERTE(generation >= 0); //We don't need to check the top end because the GC will take care of that. int result = (INT32)GCHeapUtilities::GetGCHeap()->CollectionCount(generation, getSpecialGCCount); - FC_GC_POLL_RET(); return result; } -FCIMPLEND extern "C" int QCALLTYPE GCInterface_StartNoGCRegion(INT64 totalSize, BOOL lohSizeKnown, INT64 lohSize, BOOL disallowFullBlockingGC) { diff --git a/src/coreclr/vm/comutilnative.h b/src/coreclr/vm/comutilnative.h index 5dda57c301d665..9a87f5e2c1ce57 100644 --- a/src/coreclr/vm/comutilnative.h +++ b/src/coreclr/vm/comutilnative.h @@ -174,14 +174,11 @@ class GCInterface { static FCDECL1(void, SetLOHCompactionMode, int newLOHCompactionyMode); static FCDECL2(FC_BOOL_RET, RegisterForFullGCNotification, UINT32 gen2Percentage, UINT32 lohPercentage); static FCDECL0(FC_BOOL_RET, CancelFullGCNotification); - static FCDECL1(int, GetGenerationInternal, Object* objUNSAFE); - static FCDECL0(UINT64, GetSegmentSize); static FCDECL0(int, GetLastGCPercentTimeInGC); static FCDECL1(UINT64, GetGenerationSize, int gen); static FCDECL0(int, GetMaxGeneration); static FCDECL1(void, KeepAlive, Object *obj); - static FCDECL2(int, CollectionCount, INT32 generation, INT32 getSpecialGCCount); static FCDECL0(INT64, GetAllocatedBytesForCurrentThread); static FCDECL0(INT64, GetTotalAllocatedBytesApproximate); @@ -226,6 +223,12 @@ extern "C" int QCALLTYPE GCInterface_WaitForFullGCApproach(int millisecondsTimeo extern "C" int QCALLTYPE GCInterface_WaitForFullGCComplete(int millisecondsTimeout); +extern "C" int QCALLTYPE GCInterface_GetGeneration(QCall::ObjectHandleOnStack pObjRef); + +extern "C" UINT64 QCALLTYPE GCInterface_GetSegmentSize(); + +extern "C" int QCALLTYPE GCInterface_CollectionCount(INT32 generation, INT32 getSpecialGCCount); + extern "C" int QCALLTYPE GCInterface_StartNoGCRegion(INT64 totalSize, BOOL lohSizeKnown, INT64 lohSize, BOOL disallowFullBlockingGC); extern "C" int QCALLTYPE GCInterface_EndNoGCRegion(); diff --git a/src/coreclr/vm/ecalllist.h b/src/coreclr/vm/ecalllist.h index 41acb57f1a2c3c..2471086c366479 100644 --- a/src/coreclr/vm/ecalllist.h +++ b/src/coreclr/vm/ecalllist.h @@ -261,14 +261,11 @@ FCFuncEnd() FCFuncStart(gGCInterfaceFuncs) FCFuncElement("_RegisterForFullGCNotification", GCInterface::RegisterForFullGCNotification) FCFuncElement("_CancelFullGCNotification", GCInterface::CancelFullGCNotification) - FCFuncElement("_CollectionCount", GCInterface::CollectionCount) FCFuncElement("GetMemoryInfo", GCInterface::GetMemoryInfo) FCFuncElement("_GetTotalPauseDuration", GCInterface::GetTotalPauseDuration) FCFuncElement("GetMemoryLoad", GCInterface::GetMemoryLoad) - FCFuncElement("GetSegmentSize", GCInterface::GetSegmentSize) FCFuncElement("GetLastGCPercentTimeInGC", GCInterface::GetLastGCPercentTimeInGC) FCFuncElement("GetGenerationSize", GCInterface::GetGenerationSize) - FCFuncElement("GetGenerationInternal", GCInterface::GetGenerationInternal) FCFuncElement("GetMaxGeneration", GCInterface::GetMaxGeneration) FCFuncElement("GetAllocatedBytesForCurrentThread", GCInterface::GetAllocatedBytesForCurrentThread) FCFuncElement("GetTotalAllocatedBytesApproximate", GCInterface::GetTotalAllocatedBytesApproximate) diff --git a/src/coreclr/vm/qcallentrypoints.cpp b/src/coreclr/vm/qcallentrypoints.cpp index ede8bc415112d0..9b3a82545575f7 100644 --- a/src/coreclr/vm/qcallentrypoints.cpp +++ b/src/coreclr/vm/qcallentrypoints.cpp @@ -313,6 +313,9 @@ static const Entry s_QCall[] = DllImportEntry(GCInterface_GetTotalAllocatedBytesPrecise) DllImportEntry(GCInterface_WaitForFullGCApproach) DllImportEntry(GCInterface_WaitForFullGCComplete) + DllImportEntry(GCInterface_GetGeneration) + DllImportEntry(GCInterface_GetSegmentSize) + DllImportEntry(GCInterface_CollectionCount) DllImportEntry(GCInterface_StartNoGCRegion) DllImportEntry(GCInterface_EndNoGCRegion) DllImportEntry(GCInterface_AllocateNewArray) From 3c8ae93abe4442b9a4d8fc73b6b6b9b4b0b657ab Mon Sep 17 00:00:00 2001 From: Aaron R Robinson Date: Wed, 19 Mar 2025 17:51:17 -0700 Subject: [PATCH 04/15] Convert ContentEquals FCall to managed. --- .../RuntimeHelpers.CoreCLR.cs | 8 +++---- .../classlibnative/bcltype/objectnative.cpp | 23 ------------------- .../classlibnative/bcltype/objectnative.h | 1 - src/coreclr/vm/ecalllist.h | 1 - 4 files changed, 4 insertions(+), 29 deletions(-) diff --git a/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs index d60b48ec0e552e..fa4e2ec4846858 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs @@ -282,12 +282,12 @@ static int GetHashCodeWorker(object? o) return false; // Compare the contents - return ContentEquals(o1, o2); + return SpanHelpers.SequenceEqual( + ref RuntimeHelpers.GetRawData(o1), + ref RuntimeHelpers.GetRawData(o2), + pMT->GetNumInstanceFieldBytes()); } - [MethodImpl(MethodImplOptions.InternalCall)] - private static extern bool ContentEquals(object o1, object o2); - [Obsolete("OffsetToStringData has been deprecated. Use string.GetPinnableReference() instead.")] public static int OffsetToStringData { diff --git a/src/coreclr/classlibnative/bcltype/objectnative.cpp b/src/coreclr/classlibnative/bcltype/objectnative.cpp index 56ed9d3ee84515..b0b6a92abdb222 100644 --- a/src/coreclr/classlibnative/bcltype/objectnative.cpp +++ b/src/coreclr/classlibnative/bcltype/objectnative.cpp @@ -66,29 +66,6 @@ FCIMPL1(INT32, ObjectNative::TryGetHashCode, Object* obj) } FCIMPLEND -FCIMPL2(FC_BOOL_RET, ObjectNative::ContentEquals, Object *pThisRef, Object *pCompareRef) -{ - FCALL_CONTRACT; - - // Should be ensured by caller - _ASSERTE(pThisRef != NULL); - _ASSERTE(pCompareRef != NULL); - _ASSERTE(pThisRef->GetMethodTable() == pCompareRef->GetMethodTable()); - - MethodTable *pThisMT = pThisRef->GetMethodTable(); - - // Compare the contents - BOOL ret = memcmp( - pThisRef->GetData(), - pCompareRef->GetData(), - pThisMT->GetNumInstanceFieldBytes()) == 0; - - FC_GC_POLL_RET(); - - FC_RETURN_BOOL(ret); -} -FCIMPLEND - extern "C" void QCALLTYPE ObjectNative_AllocateUninitializedClone(QCall::ObjectHandleOnStack objHandle) { QCALL_CONTRACT; diff --git a/src/coreclr/classlibnative/bcltype/objectnative.h b/src/coreclr/classlibnative/bcltype/objectnative.h index 8178ce79a55270..e9c8dff4a606f8 100644 --- a/src/coreclr/classlibnative/bcltype/objectnative.h +++ b/src/coreclr/classlibnative/bcltype/objectnative.h @@ -25,7 +25,6 @@ class ObjectNative { public: static FCDECL1(INT32, TryGetHashCode, Object* vThisRef); - static FCDECL2(FC_BOOL_RET, ContentEquals, Object *pThisRef, Object *pCompareRef); static FCDECL1(FC_BOOL_RET, IsLockHeld, Object* pThisUNSAFE); }; diff --git a/src/coreclr/vm/ecalllist.h b/src/coreclr/vm/ecalllist.h index 2471086c366479..dd6194865c85c3 100644 --- a/src/coreclr/vm/ecalllist.h +++ b/src/coreclr/vm/ecalllist.h @@ -317,7 +317,6 @@ FCFuncEnd() FCFuncStart(gRuntimeHelpers) FCFuncElement("TryGetHashCode", ObjectNative::TryGetHashCode) - FCFuncElement("ContentEquals", ObjectNative::ContentEquals) FCFuncElement("TryEnsureSufficientExecutionStack", ReflectionInvocation::TryEnsureSufficientExecutionStack) FCFuncElement("AllocTailCallArgBufferWorker", TailCallHelp::AllocTailCallArgBufferWorker) FCFuncElement("GetTailCallInfo", TailCallHelp::GetTailCallInfo) From 76850dcc7b8ad8e1e36f49402e6479112b7f47f4 Mon Sep 17 00:00:00 2001 From: Aaron R Robinson Date: Thu, 20 Mar 2025 08:46:47 -0700 Subject: [PATCH 05/15] Revert "Convert GCInterface methods to QCalls and suppress GC." This reverts commit ecd38a7f398b543efe26e14ca63410ea5c90baa3. --- .../src/System/GC.CoreCLR.cs | 19 +++++----- src/coreclr/vm/comutilnative.cpp | 35 ++++++++++++------- src/coreclr/vm/comutilnative.h | 9 ++--- src/coreclr/vm/ecalllist.h | 3 ++ src/coreclr/vm/qcallentrypoints.cpp | 3 -- 5 files changed, 36 insertions(+), 33 deletions(-) diff --git a/src/coreclr/System.Private.CoreLib/src/System/GC.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/GC.CoreCLR.cs index 6403a6dd007d16..c76c3bff82e85b 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/GC.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/GC.CoreCLR.cs @@ -116,13 +116,11 @@ internal enum GC_ALLOC_FLAGS [MethodImpl(MethodImplOptions.InternalCall)] private static extern int GetMaxGeneration(); - [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "GCInterface_GetSegmentSize")] - [SuppressGCTransition] - internal static partial ulong GetSegmentSize(); + [MethodImpl(MethodImplOptions.InternalCall)] + private static extern int _CollectionCount(int generation, int getSpecialGCCount); - [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "GCInterface_CollectionCount")] - [SuppressGCTransition] - private static partial int CollectionCountInternal(int generation, int getSpecialGCCount); + [MethodImpl(MethodImplOptions.InternalCall)] + internal static extern ulong GetSegmentSize(); [MethodImpl(MethodImplOptions.InternalCall)] internal static extern int GetLastGCPercentTimeInGC(); @@ -163,12 +161,11 @@ public static void RemoveMemoryPressure(long bytesAllocated) public static int GetGeneration(object obj) { ArgumentNullException.ThrowIfNull(obj); - return GetGenerationInternal(ObjectHandleOnStack.Create(ref obj)); + return GetGenerationInternal(obj); } - [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "GCInterface_GetGeneration")] - [SuppressGCTransition] - private static partial int GetGenerationInternal(ObjectHandleOnStack obj); + [MethodImpl(MethodImplOptions.InternalCall)] + private static extern int GetGenerationInternal(object obj); // Forces a collection of all generations from 0 through Generation. // @@ -247,7 +244,7 @@ public static void Collect(int generation, GCCollectionMode mode, bool blocking, public static int CollectionCount(int generation) { ArgumentOutOfRangeException.ThrowIfNegative(generation); - return CollectionCountInternal(generation, 0); + return _CollectionCount(generation, 0); } // This method DOES NOT DO ANYTHING in and of itself. It's used to diff --git a/src/coreclr/vm/comutilnative.cpp b/src/coreclr/vm/comutilnative.cpp index 0a45aede4e58bb..7f1ebf7e702e7c 100644 --- a/src/coreclr/vm/comutilnative.cpp +++ b/src/coreclr/vm/comutilnative.cpp @@ -725,25 +725,28 @@ extern "C" int QCALLTYPE GCInterface_WaitForFullGCComplete(int millisecondsTimeo return result; } -/*================================GetGeneration================================= -**Action: Returns the generation in which object is found. -**Returns: The generation in which object is found. +/*================================GetGenerationInternal================================= +**Action: Returns the generation in which args->obj is found. +**Returns: The generation in which args->obj is found. +**Arguments: args->obj -- The object to locate. ==============================================================================*/ -extern "C" int QCALLTYPE GCInterface_GetGeneration(QCall::ObjectHandleOnStack pObjRef) +FCIMPL1(int, GCInterface::GetGenerationInternal, Object* objUNSAFE) { - QCALL_CONTRACT_NO_GC_TRANSITION; - - _ASSERTE(pObjRef.Get() != NULL); - return (INT32)GCHeapUtilities::GetGCHeap()->WhichGeneration(OBJECTREFToObject(pObjRef.Get())); + FCALL_CONTRACT; + _ASSERTE(objUNSAFE != NULL); + int result = (INT32)GCHeapUtilities::GetGCHeap()->WhichGeneration(objUNSAFE); + FC_GC_POLL_RET(); + return result; } +FCIMPLEND /*================================GetSegmentSize========-======================= **Action: Returns the maximum GC heap segment size **Returns: The maximum segment size of either the normal heap or the large object heap, whichever is bigger ==============================================================================*/ -extern "C" UINT64 QCALLTYPE GCInterface_GetSegmentSize() +FCIMPL0(UINT64, GCInterface::GetSegmentSize) { - QCALL_CONTRACT_NO_GC_TRANSITION; + FCALL_CONTRACT; IGCHeap * pGC = GCHeapUtilities::GetGCHeap(); size_t segment_size = pGC->GetValidSegmentSize(false); @@ -752,24 +755,30 @@ extern "C" UINT64 QCALLTYPE GCInterface_GetSegmentSize() if (segment_size < large_segment_size) segment_size = large_segment_size; - return (UINT64)segment_size; + FC_GC_POLL_RET(); + return (UINT64) segment_size; } +FCIMPLEND /*================================CollectionCount================================= **Action: Returns the number of collections for this generation since the beginning of the life of the process **Returns: The collection count. +**Arguments: args->generation -- The generation +**Exceptions: Argument exception if args->generation is < 0 or > GetMaxGeneration(); ==============================================================================*/ -extern "C" int QCALLTYPE GCInterface_CollectionCount(INT32 generation, INT32 getSpecialGCCount) +FCIMPL2(int, GCInterface::CollectionCount, INT32 generation, INT32 getSpecialGCCount) { - QCALL_CONTRACT_NO_GC_TRANSITION; + FCALL_CONTRACT; //We've already checked this in GC.cs, so we'll just assert it here. _ASSERTE(generation >= 0); //We don't need to check the top end because the GC will take care of that. int result = (INT32)GCHeapUtilities::GetGCHeap()->CollectionCount(generation, getSpecialGCCount); + FC_GC_POLL_RET(); return result; } +FCIMPLEND extern "C" int QCALLTYPE GCInterface_StartNoGCRegion(INT64 totalSize, BOOL lohSizeKnown, INT64 lohSize, BOOL disallowFullBlockingGC) { diff --git a/src/coreclr/vm/comutilnative.h b/src/coreclr/vm/comutilnative.h index 9a87f5e2c1ce57..5dda57c301d665 100644 --- a/src/coreclr/vm/comutilnative.h +++ b/src/coreclr/vm/comutilnative.h @@ -174,11 +174,14 @@ class GCInterface { static FCDECL1(void, SetLOHCompactionMode, int newLOHCompactionyMode); static FCDECL2(FC_BOOL_RET, RegisterForFullGCNotification, UINT32 gen2Percentage, UINT32 lohPercentage); static FCDECL0(FC_BOOL_RET, CancelFullGCNotification); + static FCDECL1(int, GetGenerationInternal, Object* objUNSAFE); + static FCDECL0(UINT64, GetSegmentSize); static FCDECL0(int, GetLastGCPercentTimeInGC); static FCDECL1(UINT64, GetGenerationSize, int gen); static FCDECL0(int, GetMaxGeneration); static FCDECL1(void, KeepAlive, Object *obj); + static FCDECL2(int, CollectionCount, INT32 generation, INT32 getSpecialGCCount); static FCDECL0(INT64, GetAllocatedBytesForCurrentThread); static FCDECL0(INT64, GetTotalAllocatedBytesApproximate); @@ -223,12 +226,6 @@ extern "C" int QCALLTYPE GCInterface_WaitForFullGCApproach(int millisecondsTimeo extern "C" int QCALLTYPE GCInterface_WaitForFullGCComplete(int millisecondsTimeout); -extern "C" int QCALLTYPE GCInterface_GetGeneration(QCall::ObjectHandleOnStack pObjRef); - -extern "C" UINT64 QCALLTYPE GCInterface_GetSegmentSize(); - -extern "C" int QCALLTYPE GCInterface_CollectionCount(INT32 generation, INT32 getSpecialGCCount); - extern "C" int QCALLTYPE GCInterface_StartNoGCRegion(INT64 totalSize, BOOL lohSizeKnown, INT64 lohSize, BOOL disallowFullBlockingGC); extern "C" int QCALLTYPE GCInterface_EndNoGCRegion(); diff --git a/src/coreclr/vm/ecalllist.h b/src/coreclr/vm/ecalllist.h index dd6194865c85c3..3ad2378c74fc52 100644 --- a/src/coreclr/vm/ecalllist.h +++ b/src/coreclr/vm/ecalllist.h @@ -261,11 +261,14 @@ FCFuncEnd() FCFuncStart(gGCInterfaceFuncs) FCFuncElement("_RegisterForFullGCNotification", GCInterface::RegisterForFullGCNotification) FCFuncElement("_CancelFullGCNotification", GCInterface::CancelFullGCNotification) + FCFuncElement("_CollectionCount", GCInterface::CollectionCount) FCFuncElement("GetMemoryInfo", GCInterface::GetMemoryInfo) FCFuncElement("_GetTotalPauseDuration", GCInterface::GetTotalPauseDuration) FCFuncElement("GetMemoryLoad", GCInterface::GetMemoryLoad) + FCFuncElement("GetSegmentSize", GCInterface::GetSegmentSize) FCFuncElement("GetLastGCPercentTimeInGC", GCInterface::GetLastGCPercentTimeInGC) FCFuncElement("GetGenerationSize", GCInterface::GetGenerationSize) + FCFuncElement("GetGenerationInternal", GCInterface::GetGenerationInternal) FCFuncElement("GetMaxGeneration", GCInterface::GetMaxGeneration) FCFuncElement("GetAllocatedBytesForCurrentThread", GCInterface::GetAllocatedBytesForCurrentThread) FCFuncElement("GetTotalAllocatedBytesApproximate", GCInterface::GetTotalAllocatedBytesApproximate) diff --git a/src/coreclr/vm/qcallentrypoints.cpp b/src/coreclr/vm/qcallentrypoints.cpp index 9b3a82545575f7..ede8bc415112d0 100644 --- a/src/coreclr/vm/qcallentrypoints.cpp +++ b/src/coreclr/vm/qcallentrypoints.cpp @@ -313,9 +313,6 @@ static const Entry s_QCall[] = DllImportEntry(GCInterface_GetTotalAllocatedBytesPrecise) DllImportEntry(GCInterface_WaitForFullGCApproach) DllImportEntry(GCInterface_WaitForFullGCComplete) - DllImportEntry(GCInterface_GetGeneration) - DllImportEntry(GCInterface_GetSegmentSize) - DllImportEntry(GCInterface_CollectionCount) DllImportEntry(GCInterface_StartNoGCRegion) DllImportEntry(GCInterface_EndNoGCRegion) DllImportEntry(GCInterface_AllocateNewArray) From 7f70dca11752f537515620f9427a98418d703f49 Mon Sep 17 00:00:00 2001 From: Aaron R Robinson Date: Thu, 20 Mar 2025 08:48:42 -0700 Subject: [PATCH 06/15] Remove FC_GC_POLL_RET() from comutilnative.cpp --- src/coreclr/vm/comutilnative.cpp | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/src/coreclr/vm/comutilnative.cpp b/src/coreclr/vm/comutilnative.cpp index 7f1ebf7e702e7c..9f30bab2df9305 100644 --- a/src/coreclr/vm/comutilnative.cpp +++ b/src/coreclr/vm/comutilnative.cpp @@ -734,9 +734,7 @@ FCIMPL1(int, GCInterface::GetGenerationInternal, Object* objUNSAFE) { FCALL_CONTRACT; _ASSERTE(objUNSAFE != NULL); - int result = (INT32)GCHeapUtilities::GetGCHeap()->WhichGeneration(objUNSAFE); - FC_GC_POLL_RET(); - return result; + return (INT32)GCHeapUtilities::GetGCHeap()->WhichGeneration(objUNSAFE); } FCIMPLEND @@ -754,8 +752,6 @@ FCIMPL0(UINT64, GCInterface::GetSegmentSize) _ASSERTE(segment_size < SIZE_T_MAX && large_segment_size < SIZE_T_MAX); if (segment_size < large_segment_size) segment_size = large_segment_size; - - FC_GC_POLL_RET(); return (UINT64) segment_size; } FCIMPLEND @@ -774,9 +770,7 @@ FCIMPL2(int, GCInterface::CollectionCount, INT32 generation, INT32 getSpecialGCC _ASSERTE(generation >= 0); //We don't need to check the top end because the GC will take care of that. - int result = (INT32)GCHeapUtilities::GetGCHeap()->CollectionCount(generation, getSpecialGCCount); - FC_GC_POLL_RET(); - return result; + return (INT32)GCHeapUtilities::GetGCHeap()->CollectionCount(generation, getSpecialGCCount); } FCIMPLEND From dfa20265026be5669aad8afd1ba01ca9a0870a15 Mon Sep 17 00:00:00 2001 From: Aaron R Robinson Date: Thu, 20 Mar 2025 08:50:19 -0700 Subject: [PATCH 07/15] Revert "Remove explicit GC Poll from GC.SuppressFinalize()" This reverts commit 77caee7dd2332b2fed88edd2a83745492d02171b. --- .../src/System/GC.CoreCLR.cs | 12 ++++-------- src/coreclr/vm/comutilnative.cpp | 15 +++++++++++---- src/coreclr/vm/comutilnative.h | 3 +-- src/coreclr/vm/ecalllist.h | 2 ++ src/coreclr/vm/qcallentrypoints.cpp | 1 - 5 files changed, 18 insertions(+), 15 deletions(-) diff --git a/src/coreclr/System.Private.CoreLib/src/System/GC.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/GC.CoreCLR.cs index c76c3bff82e85b..babcf894736ddf 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/GC.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/GC.CoreCLR.cs @@ -346,18 +346,14 @@ public static void WaitForPendingFinalizers() // Indicates that the system should not call the Finalize() method on // an object that would normally require this call. - [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "GCInterface_SuppressFinalize")] - [SuppressGCTransition] - private static partial void SuppressFinalize(ObjectHandleOnStack obj); + [MethodImpl(MethodImplOptions.InternalCall)] + private static extern void _SuppressFinalize(object o); - public static unsafe void SuppressFinalize(object obj) + public static void SuppressFinalize(object obj) { ArgumentNullException.ThrowIfNull(obj); - if (RuntimeHelpers.GetMethodTable(obj)->HasFinalizer) - { - SuppressFinalize(ObjectHandleOnStack.Create(ref obj)); - } + _SuppressFinalize(obj); } // Indicates that the system should call the Finalize() method on an object diff --git a/src/coreclr/vm/comutilnative.cpp b/src/coreclr/vm/comutilnative.cpp index 9f30bab2df9305..d67982b40d5af9 100644 --- a/src/coreclr/vm/comutilnative.cpp +++ b/src/coreclr/vm/comutilnative.cpp @@ -1098,14 +1098,21 @@ extern "C" void QCALLTYPE GCInterface_UnregisterFrozenSegment(void* segment) **Arguments: Object of interest **Exceptions: None ==============================================================================*/ -extern "C" void QCALLTYPE GCInterface_SuppressFinalize(QCall::ObjectHandleOnStack pObj) +FCIMPL1(void, GCInterface::SuppressFinalize, Object *obj) { - QCALL_CONTRACT_NO_GC_TRANSITION; + FCALL_CONTRACT; // Checked by the caller - _ASSERTE(pObj.Get()->GetMethodTable()->HasFinalizer()); - GCHeapUtilities::GetGCHeap()->SetFinalizationRun(OBJECTREFToObject(pObj.Get())); + _ASSERTE(obj != NULL); + + if (!obj->GetMethodTable ()->HasFinalizer()) + return; + + GCHeapUtilities::GetGCHeap()->SetFinalizationRun(obj); + FC_GC_POLL(); } +FCIMPLEND + /*============================ReRegisterForFinalize============================== **Action: Indicate that an object's finalizer should be run by the system. diff --git a/src/coreclr/vm/comutilnative.h b/src/coreclr/vm/comutilnative.h index 5dda57c301d665..2d9116e135c590 100644 --- a/src/coreclr/vm/comutilnative.h +++ b/src/coreclr/vm/comutilnative.h @@ -181,6 +181,7 @@ class GCInterface { static FCDECL0(int, GetMaxGeneration); static FCDECL1(void, KeepAlive, Object *obj); + static FCDECL1(void, SuppressFinalize, Object *obj); static FCDECL2(int, CollectionCount, INT32 generation, INT32 getSpecialGCCount); static FCDECL0(INT64, GetAllocatedBytesForCurrentThread); @@ -211,8 +212,6 @@ extern "C" INT64 QCALLTYPE GCInterface_GetTotalMemory(); extern "C" void QCALLTYPE GCInterface_Collect(INT32 generation, INT32 mode); -extern "C" void QCALLTYPE GCInterface_SuppressFinalize(QCall::ObjectHandleOnStack pObj); - extern "C" void* QCALLTYPE GCInterface_GetNextFinalizableObject(QCall::ObjectHandleOnStack pObj); extern "C" void QCALLTYPE GCInterface_WaitForPendingFinalizers(); diff --git a/src/coreclr/vm/ecalllist.h b/src/coreclr/vm/ecalllist.h index 3ad2378c74fc52..093c5a881872b0 100644 --- a/src/coreclr/vm/ecalllist.h +++ b/src/coreclr/vm/ecalllist.h @@ -270,6 +270,8 @@ FCFuncStart(gGCInterfaceFuncs) FCFuncElement("GetGenerationSize", GCInterface::GetGenerationSize) FCFuncElement("GetGenerationInternal", GCInterface::GetGenerationInternal) FCFuncElement("GetMaxGeneration", GCInterface::GetMaxGeneration) + FCFuncElement("_SuppressFinalize", GCInterface::SuppressFinalize) + FCFuncElement("GetAllocatedBytesForCurrentThread", GCInterface::GetAllocatedBytesForCurrentThread) FCFuncElement("GetTotalAllocatedBytesApproximate", GCInterface::GetTotalAllocatedBytesApproximate) FCFuncEnd() diff --git a/src/coreclr/vm/qcallentrypoints.cpp b/src/coreclr/vm/qcallentrypoints.cpp index ede8bc415112d0..5ac5a3889f9386 100644 --- a/src/coreclr/vm/qcallentrypoints.cpp +++ b/src/coreclr/vm/qcallentrypoints.cpp @@ -318,7 +318,6 @@ static const Entry s_QCall[] = DllImportEntry(GCInterface_AllocateNewArray) DllImportEntry(GCInterface_GetTotalMemory) DllImportEntry(GCInterface_Collect) - DllImportEntry(GCInterface_SuppressFinalize) DllImportEntry(GCInterface_ReRegisterForFinalize) DllImportEntry(GCInterface_GetNextFinalizableObject) DllImportEntry(GCInterface_WaitForPendingFinalizers) From c65a9085e51524bea90c51c8830a051d520f348a Mon Sep 17 00:00:00 2001 From: Aaron R Robinson Date: Thu, 20 Mar 2025 09:14:03 -0700 Subject: [PATCH 08/15] Apply feedback to remove explicit GC Poll from GC.SupressFinalize. --- .../System.Private.CoreLib/src/System/GC.CoreCLR.cs | 9 +++++++-- src/coreclr/vm/comutilnative.cpp | 9 +-------- 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/src/coreclr/System.Private.CoreLib/src/System/GC.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/GC.CoreCLR.cs index babcf894736ddf..225cc7208ee218 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/GC.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/GC.CoreCLR.cs @@ -349,11 +349,16 @@ public static void WaitForPendingFinalizers() [MethodImpl(MethodImplOptions.InternalCall)] private static extern void _SuppressFinalize(object o); - public static void SuppressFinalize(object obj) + public static unsafe void SuppressFinalize(object obj) { ArgumentNullException.ThrowIfNull(obj); - _SuppressFinalize(obj); + if (RuntimeHelpers.GetMethodTable(obj)->HasFinalizer) + { + // SuppressFinalize is a no-op if the object doesn't have a finalizer. + // We don't need to call _SuppressFinalize in that case. + _SuppressFinalize(obj); + } } // Indicates that the system should call the Finalize() method on an object diff --git a/src/coreclr/vm/comutilnative.cpp b/src/coreclr/vm/comutilnative.cpp index d67982b40d5af9..c63319a24e5f39 100644 --- a/src/coreclr/vm/comutilnative.cpp +++ b/src/coreclr/vm/comutilnative.cpp @@ -1101,15 +1101,8 @@ extern "C" void QCALLTYPE GCInterface_UnregisterFrozenSegment(void* segment) FCIMPL1(void, GCInterface::SuppressFinalize, Object *obj) { FCALL_CONTRACT; - - // Checked by the caller - _ASSERTE(obj != NULL); - - if (!obj->GetMethodTable ()->HasFinalizer()) - return; - + _ASSERTE(obj->GetMethodTable ()->HasFinalizer()); GCHeapUtilities::GetGCHeap()->SetFinalizationRun(obj); - FC_GC_POLL(); } FCIMPLEND From dd985b1a459c00da6e7feb342690c2750397cde7 Mon Sep 17 00:00:00 2001 From: Aaron R Robinson Date: Thu, 20 Mar 2025 09:19:55 -0700 Subject: [PATCH 09/15] Remove unmanaged FC_GCPoll implementation. --- src/coreclr/vm/fcall.cpp | 55 ---------------------------------------- src/coreclr/vm/fcall.h | 31 ---------------------- 2 files changed, 86 deletions(-) diff --git a/src/coreclr/vm/fcall.cpp b/src/coreclr/vm/fcall.cpp index 2696df624374a9..8dc242e934db0e 100644 --- a/src/coreclr/vm/fcall.cpp +++ b/src/coreclr/vm/fcall.cpp @@ -65,48 +65,6 @@ NOINLINE LPVOID __FCThrow(LPVOID __me, RuntimeExceptionKind reKind, UINT resID, return NULL; } -/**************************************************************************************/ -/* erect a frame in the FCALL and then poll the GC, objToProtect will be protected - during the poll and the updated object returned. */ - -NOINLINE Object* FC_GCPoll(void* __me, Object* objToProtect) -{ - CONTRACTL { - THROWS; - // This isn't strictly true... But the guarantee that we make here is - // that we won't trigger without having setup a frame. - UNCHECKED(GC_NOTRIGGER); - } CONTRACTL_END; - - FC_CAN_TRIGGER_GC(); - INCONTRACT(FCallCheck __fCallCheck(__FILE__, __LINE__)); - - Thread *thread = GetThread(); - if (thread->CatchAtSafePoint()) // Does someone want this thread stopped? - { - HELPER_METHOD_FRAME_BEGIN_RET_ATTRIB_1(Frame::FRAME_ATTR_CAPTURE_DEPTH_2, objToProtect); - -#ifdef _DEBUG - BOOL GCOnTransition = FALSE; - if (g_pConfig->FastGCStressLevel()) { - GCOnTransition = GC_ON_TRANSITIONS (FALSE); - } -#endif - CommonTripThread(); -#ifdef _DEBUG - if (g_pConfig->FastGCStressLevel()) { - GC_ON_TRANSITIONS (GCOnTransition); - } -#endif - - HELPER_METHOD_FRAME_END(); - } - - FC_CAN_TRIGGER_GC_END(); - - return objToProtect; -} - #ifdef ENABLE_CONTRACTS /**************************************************************************************/ @@ -177,19 +135,6 @@ DEBUG_NOINLINE FCallCheck::~FCallCheck() // // Call HELPER_METHOD_POLL() // or use HELPER_METHOD_FRAME_END_POLL - // - // If you don't have a helper frame you can used - // - // FC_GC_POLL_AND_RETURN_OBJREF or - // FC_GC_POLL_RET - // - // Note that these must be at GC safe points. In particular - // all object references that are NOT protected will be trashed. - - - // There is a special poll called FC_GC_POLL_NOT_NEEDED - // which says the code path is short enough that a GC poll is not need - // you should not use this in most cases. _ASSERTE(unbreakableLockCount == m_pThread->GetUnbreakableLockCount() || (!m_pThread->HasUnbreakableLock() && !m_pThread->HasThreadStateNC(Thread::TSNC_OwnsSpinLock))); diff --git a/src/coreclr/vm/fcall.h b/src/coreclr/vm/fcall.h index 4ed9e56fac7832..58d6641304657d 100644 --- a/src/coreclr/vm/fcall.h +++ b/src/coreclr/vm/fcall.h @@ -59,9 +59,6 @@ // to do the poll at the end. If somewhere in the middle is the best // place you can do that too with HELPER_METHOD_POLL() -// You don't need to erect a helper method frame to do a poll. FC_GC_POLL -// can do this (remember all your GC refs will be trashed). - // Finally if your method is VERY small, you can get away without a poll, // you have to use FC_GC_POLL_NOT_NEEDED to mark this. // Use sparingly! @@ -801,34 +798,6 @@ LPVOID __FCThrow(LPVOID me, enum RuntimeExceptionKind reKind, UINT resID, LPCWST // don't need to poll the GC. USE VERY SPARINGLY!!! #define FC_GC_POLL_NOT_NEEDED() INCONTRACT(__fCallCheck.SetNotNeeded()) -Object* FC_GCPoll(void* me, Object* objToProtect = NULL); - -#define FC_GC_POLL_EX(ret) \ - { \ - INCONTRACT(Thread::TriggersGC(GetThread());) \ - INCONTRACT(__fCallCheck.SetDidPoll();) \ - if (g_TrapReturningThreads) \ - { \ - if (FC_GCPoll(__me)) \ - return ret; \ - while (0 == FC_NO_TAILCALL) { }; /* side effect the compile can't remove */ \ - } \ - } - -#define FC_GC_POLL_RET() FC_GC_POLL_EX(0) - -#define FC_GC_POLL_AND_RETURN_OBJREF(obj) \ - { \ - INCONTRACT(__fCallCheck.SetDidPoll();) \ - Object* __temp = OBJECTREFToObject(obj); \ - if (g_TrapReturningThreads) \ - { \ - __temp = FC_GCPoll(__me, __temp); \ - while (0 == FC_NO_TAILCALL) { }; /* side effect the compile can't remove */ \ - } \ - return __temp; \ - } - #if defined(ENABLE_CONTRACTS) #define FC_CAN_TRIGGER_GC() FCallGCCanTrigger::Enter() #define FC_CAN_TRIGGER_GC_END() FCallGCCanTrigger::Leave(__FUNCTION__, __FILE__, __LINE__) From fc311c47f5a1018964502550c87502b944a864ca Mon Sep 17 00:00:00 2001 From: Aaron R Robinson Date: Thu, 20 Mar 2025 09:55:50 -0700 Subject: [PATCH 10/15] Revert "Convert ContentEquals FCall to managed." This reverts commit 58f2bae4ea7c39f419b8add1bc10f175bee76864. --- .../RuntimeHelpers.CoreCLR.cs | 8 +++---- .../classlibnative/bcltype/objectnative.cpp | 21 +++++++++++++++++++ .../classlibnative/bcltype/objectnative.h | 1 + src/coreclr/vm/ecalllist.h | 1 + 4 files changed, 27 insertions(+), 4 deletions(-) diff --git a/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs index fa4e2ec4846858..d60b48ec0e552e 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs @@ -282,12 +282,12 @@ static int GetHashCodeWorker(object? o) return false; // Compare the contents - return SpanHelpers.SequenceEqual( - ref RuntimeHelpers.GetRawData(o1), - ref RuntimeHelpers.GetRawData(o2), - pMT->GetNumInstanceFieldBytes()); + return ContentEquals(o1, o2); } + [MethodImpl(MethodImplOptions.InternalCall)] + private static extern bool ContentEquals(object o1, object o2); + [Obsolete("OffsetToStringData has been deprecated. Use string.GetPinnableReference() instead.")] public static int OffsetToStringData { diff --git a/src/coreclr/classlibnative/bcltype/objectnative.cpp b/src/coreclr/classlibnative/bcltype/objectnative.cpp index b0b6a92abdb222..1ee90e337c2cdf 100644 --- a/src/coreclr/classlibnative/bcltype/objectnative.cpp +++ b/src/coreclr/classlibnative/bcltype/objectnative.cpp @@ -66,6 +66,27 @@ FCIMPL1(INT32, ObjectNative::TryGetHashCode, Object* obj) } FCIMPLEND +FCIMPL2(FC_BOOL_RET, ObjectNative::ContentEquals, Object *pThisRef, Object *pCompareRef) +{ + FCALL_CONTRACT; + + // Should be ensured by caller + _ASSERTE(pThisRef != NULL); + _ASSERTE(pCompareRef != NULL); + _ASSERTE(pThisRef->GetMethodTable() == pCompareRef->GetMethodTable()); + + MethodTable *pThisMT = pThisRef->GetMethodTable(); + + // Compare the contents + BOOL ret = memcmp( + pThisRef->GetData(), + pCompareRef->GetData(), + pThisMT->GetNumInstanceFieldBytes()) == 0; + + FC_RETURN_BOOL(ret); +} +FCIMPLEND + extern "C" void QCALLTYPE ObjectNative_AllocateUninitializedClone(QCall::ObjectHandleOnStack objHandle) { QCALL_CONTRACT; diff --git a/src/coreclr/classlibnative/bcltype/objectnative.h b/src/coreclr/classlibnative/bcltype/objectnative.h index e9c8dff4a606f8..8178ce79a55270 100644 --- a/src/coreclr/classlibnative/bcltype/objectnative.h +++ b/src/coreclr/classlibnative/bcltype/objectnative.h @@ -25,6 +25,7 @@ class ObjectNative { public: static FCDECL1(INT32, TryGetHashCode, Object* vThisRef); + static FCDECL2(FC_BOOL_RET, ContentEquals, Object *pThisRef, Object *pCompareRef); static FCDECL1(FC_BOOL_RET, IsLockHeld, Object* pThisUNSAFE); }; diff --git a/src/coreclr/vm/ecalllist.h b/src/coreclr/vm/ecalllist.h index 093c5a881872b0..074694d51243ca 100644 --- a/src/coreclr/vm/ecalllist.h +++ b/src/coreclr/vm/ecalllist.h @@ -322,6 +322,7 @@ FCFuncEnd() FCFuncStart(gRuntimeHelpers) FCFuncElement("TryGetHashCode", ObjectNative::TryGetHashCode) + FCFuncElement("ContentEquals", ObjectNative::ContentEquals) FCFuncElement("TryEnsureSufficientExecutionStack", ReflectionInvocation::TryEnsureSufficientExecutionStack) FCFuncElement("AllocTailCallArgBufferWorker", TailCallHelp::AllocTailCallArgBufferWorker) FCFuncElement("GetTailCallInfo", TailCallHelp::GetTailCallInfo) From 9071a0dc627b4f0df3bc0e1abf679bf2fde85a2b Mon Sep 17 00:00:00 2001 From: Aaron R Robinson Date: Thu, 20 Mar 2025 11:53:30 -0700 Subject: [PATCH 11/15] Convert __BulkMoveWithWriteBarrier to QCall and apply SuppressGCTransition. --- .../src/System/Buffer.CoreCLR.cs | 11 +++-------- .../src/System/Threading/Thread.CoreCLR.cs | 2 +- src/coreclr/vm/comutilnative.cpp | 15 +++++++-------- src/coreclr/vm/comutilnative.h | 9 ++------- src/coreclr/vm/ecalllist.h | 5 ----- src/coreclr/vm/qcallentrypoints.cpp | 1 + 6 files changed, 14 insertions(+), 29 deletions(-) diff --git a/src/coreclr/System.Private.CoreLib/src/System/Buffer.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Buffer.CoreCLR.cs index 15c4c31d86bf65..a43005ad667f2c 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Buffer.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Buffer.CoreCLR.cs @@ -16,14 +16,9 @@ public partial class Buffer [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "Buffer_MemMove")] private static unsafe partial void __Memmove(byte* dest, byte* src, nuint len); - [MethodImpl(MethodImplOptions.InternalCall)] - private static extern void BulkMoveWithWriteBarrierInternal(ref byte destination, ref byte source, nuint byteCount); - - private static void __BulkMoveWithWriteBarrier(ref byte destination, ref byte source, nuint byteCount) - { - Thread.PollGC(); - BulkMoveWithWriteBarrierInternal(ref destination, ref source, byteCount); - } + [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "Buffer_BulkMoveWithWriteBarrier")] + [SuppressGCTransition] + private static partial void __BulkMoveWithWriteBarrier(ref byte destination, ref byte source, nuint byteCount); // Used by ilmarshalers.cpp internal static unsafe void Memcpy(byte* dest, byte* src, int len) diff --git a/src/coreclr/System.Private.CoreLib/src/System/Threading/Thread.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Threading/Thread.CoreCLR.cs index b52d483053048b..ac849954207553 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Threading/Thread.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Threading/Thread.CoreCLR.cs @@ -497,7 +497,7 @@ private void ResetFinalizerThreadSlow() // GC Suspension is done by simply dropping into native code via p/invoke, and we reuse the p/invoke // mechanism for suspension. On all architectures we should have the actual stub used for the check be implemented // as a small assembly stub which checks the global g_TrapReturningThreads flag and tail-call to this helper - internal static unsafe void PollGC() + private static unsafe void PollGC() { NativeThreadState catchAtSafePoint = ((NativeThreadClass*)Thread.DirectOnThreadLocalData.pNativeThread)->m_State & NativeThreadState.TS_CatchAtSafePoint; if (catchAtSafePoint != NativeThreadState.None) diff --git a/src/coreclr/vm/comutilnative.cpp b/src/coreclr/vm/comutilnative.cpp index c63319a24e5f39..21bfdae843c073 100644 --- a/src/coreclr/vm/comutilnative.cpp +++ b/src/coreclr/vm/comutilnative.cpp @@ -547,20 +547,19 @@ extern "C" void QCALLTYPE Buffer_Clear(void *dst, size_t length) memset(dst, 0, length); } -FCIMPL3(VOID, Buffer::BulkMoveWithWriteBarrier, void *dst, void *src, size_t byteCount) +extern "C" void QCALLTYPE Buffer_MemMove(void *dst, void *src, size_t length) { - FCALL_CONTRACT; + QCALL_CONTRACT; - if (dst != src && byteCount != 0) - InlinedMemmoveGCRefsHelper(dst, src, byteCount); + memmove(dst, src, length); } -FCIMPLEND -extern "C" void QCALLTYPE Buffer_MemMove(void *dst, void *src, size_t length) +extern "C" void QCALLTYPE Buffer_BulkMoveWithWriteBarrier(void *dst, void *src, size_t length) { - QCALL_CONTRACT; + QCALL_CONTRACT_NO_GC_TRANSITION; - memmove(dst, src, length); + if (dst != src && length != 0) + InlinedMemmoveGCRefsHelper(dst, src, length); } // diff --git a/src/coreclr/vm/comutilnative.h b/src/coreclr/vm/comutilnative.h index 2d9116e135c590..b356872294bc93 100644 --- a/src/coreclr/vm/comutilnative.h +++ b/src/coreclr/vm/comutilnative.h @@ -81,14 +81,9 @@ extern "C" void QCALLTYPE ExceptionNative_ThrowClassAccessException(MethodDesc* // // Buffer // -class Buffer -{ -public: - static FCDECL3(VOID, BulkMoveWithWriteBarrier, void *dst, void *src, size_t byteCount); -}; - -extern "C" void QCALLTYPE Buffer_MemMove(void *dst, void *src, size_t length); extern "C" void QCALLTYPE Buffer_Clear(void *dst, size_t length); +extern "C" void QCALLTYPE Buffer_MemMove(void *dst, void *src, size_t length); +extern "C" void QCALLTYPE Buffer_BulkMoveWithWriteBarrier(void *dst, void *src, size_t length); const UINT MEM_PRESSURE_COUNT = 4; diff --git a/src/coreclr/vm/ecalllist.h b/src/coreclr/vm/ecalllist.h index 074694d51243ca..fc404a3d542ddb 100644 --- a/src/coreclr/vm/ecalllist.h +++ b/src/coreclr/vm/ecalllist.h @@ -249,10 +249,6 @@ FCFuncStart(gArrayFuncs) FCFuncElement("GetCorElementTypeOfElementType", ArrayNative::GetCorElementTypeOfElementType) FCFuncEnd() -FCFuncStart(gBufferFuncs) - FCFuncElement("BulkMoveWithWriteBarrierInternal", Buffer::BulkMoveWithWriteBarrier) -FCFuncEnd() - FCFuncStart(gGCFrameRegistration) FCFuncElement("RegisterForGCReporting", GCReporting::Register) FCFuncElement("UnregisterForGCReporting", GCReporting::Unregister) @@ -373,7 +369,6 @@ FCFuncEnd() FCClassElement("Array", "System", gArrayFuncs) FCClassElement("AssemblyLoadContext", "System.Runtime.Loader", gAssemblyLoadContextFuncs) -FCClassElement("Buffer", "System", gBufferFuncs) FCClassElement("CastHelpers", "System.Runtime.CompilerServices", gCastHelpers) FCClassElement("ComAwareWeakReference", "System", gComAwareWeakReferenceFuncs) FCClassElement("Delegate", "System", gDelegateFuncs) diff --git a/src/coreclr/vm/qcallentrypoints.cpp b/src/coreclr/vm/qcallentrypoints.cpp index 5ac5a3889f9386..1cb8bd85c8784b 100644 --- a/src/coreclr/vm/qcallentrypoints.cpp +++ b/src/coreclr/vm/qcallentrypoints.cpp @@ -308,6 +308,7 @@ static const Entry s_QCall[] = DllImportEntry(ClrConfig_GetConfigBoolValue) DllImportEntry(Buffer_Clear) DllImportEntry(Buffer_MemMove) + DllImportEntry(Buffer_BulkMoveWithWriteBarrier) DllImportEntry(DependentHandle_InternalAllocWithGCTransition) DllImportEntry(DependentHandle_InternalFreeWithGCTransition) DllImportEntry(GCInterface_GetTotalAllocatedBytesPrecise) From d187034970d9e8d5cf0aa36597e6ae19d34f3793 Mon Sep 17 00:00:00 2001 From: Aaron R Robinson Date: Thu, 20 Mar 2025 17:37:35 -0700 Subject: [PATCH 12/15] Make a FastGCPoll intrinsic in the JIT. Update native AOT and CoreCLR to use the new managed FastGCPoll. --- .../src/System/Buffer.CoreCLR.cs | 6 ++---- src/coreclr/jit/compiler.h | 1 + src/coreclr/jit/compiler.hpp | 1 + src/coreclr/jit/flowgraph.cpp | 4 ++++ src/coreclr/jit/gentree.cpp | 8 ++++++++ src/coreclr/jit/gtlist.h | 1 + src/coreclr/jit/importercalls.cpp | 20 +++++++++++++++++++ src/coreclr/jit/liveness.cpp | 1 + src/coreclr/jit/namedintrinsiclist.h | 1 + src/coreclr/jit/optcse.cpp | 1 + src/coreclr/jit/rationalize.cpp | 7 +++++++ src/coreclr/jit/valuenum.cpp | 1 + src/coreclr/vm/comutilnative.cpp | 9 +++++---- src/coreclr/vm/comutilnative.h | 7 ++++++- src/coreclr/vm/ecalllist.h | 5 +++++ src/coreclr/vm/qcallentrypoints.cpp | 1 - .../src/System/Buffer.cs | 9 +++++++++ .../src/System/Threading/Thread.cs | 5 +++++ 18 files changed, 78 insertions(+), 10 deletions(-) diff --git a/src/coreclr/System.Private.CoreLib/src/System/Buffer.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Buffer.CoreCLR.cs index a43005ad667f2c..2a375e74035f0d 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Buffer.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Buffer.CoreCLR.cs @@ -4,7 +4,6 @@ using System.Diagnostics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; -using System.Threading; namespace System { @@ -16,9 +15,8 @@ public partial class Buffer [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "Buffer_MemMove")] private static unsafe partial void __Memmove(byte* dest, byte* src, nuint len); - [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "Buffer_BulkMoveWithWriteBarrier")] - [SuppressGCTransition] - private static partial void __BulkMoveWithWriteBarrier(ref byte destination, ref byte source, nuint byteCount); + [MethodImpl(MethodImplOptions.InternalCall)] + private static extern void __BulkMoveWithWriteBarrier(ref byte destination, ref byte source, nuint byteCount); // Used by ilmarshalers.cpp internal static unsafe void Memcpy(byte* dest, byte* src, int len) diff --git a/src/coreclr/jit/compiler.h b/src/coreclr/jit/compiler.h index 1be19a765767c7..a650d3cc2a596f 100644 --- a/src/coreclr/jit/compiler.h +++ b/src/coreclr/jit/compiler.h @@ -11878,6 +11878,7 @@ class GenTreeVisitor case GT_IL_OFFSET: case GT_NOP: case GT_SWIFT_ERROR: + case GT_GCPOLL: break; // Lclvar unary operators diff --git a/src/coreclr/jit/compiler.hpp b/src/coreclr/jit/compiler.hpp index ec44e86e45ef04..ce51a90ca02a99 100644 --- a/src/coreclr/jit/compiler.hpp +++ b/src/coreclr/jit/compiler.hpp @@ -4408,6 +4408,7 @@ void GenTree::VisitOperands(TVisitor visitor) case GT_IL_OFFSET: case GT_NOP: case GT_SWIFT_ERROR: + case GT_GCPOLL: return; // Unary operators with an optional operand diff --git a/src/coreclr/jit/flowgraph.cpp b/src/coreclr/jit/flowgraph.cpp index 08ac83188ccf85..1f295a08d47158 100644 --- a/src/coreclr/jit/flowgraph.cpp +++ b/src/coreclr/jit/flowgraph.cpp @@ -48,6 +48,10 @@ static bool blockNeedsGCPoll(BasicBlock* block) blockMayNeedGCPoll = true; } } + else if (tree->OperGet() == GT_GCPOLL) + { + blockMayNeedGCPoll = true; + } } } } diff --git a/src/coreclr/jit/gentree.cpp b/src/coreclr/jit/gentree.cpp index 022c750bdc89ea..3d938a381013fd 100644 --- a/src/coreclr/jit/gentree.cpp +++ b/src/coreclr/jit/gentree.cpp @@ -2670,6 +2670,7 @@ bool GenTree::Compare(GenTree* op1, GenTree* op2, bool swapOK) case GT_NOP: case GT_LABEL: case GT_SWIFT_ERROR: + case GT_GCPOLL: return true; default: @@ -5239,6 +5240,7 @@ unsigned Compiler::gtSetEvalOrder(GenTree* tree) break; case GT_NOP: + case GT_GCPOLL: level = 0; costEx = 0; costSz = 0; @@ -6584,6 +6586,7 @@ bool GenTree::TryGetUse(GenTree* operand, GenTree*** pUse) case GT_IL_OFFSET: case GT_NOP: case GT_SWIFT_ERROR: + case GT_GCPOLL: return false; // Standard unary operators @@ -6912,6 +6915,7 @@ bool GenTree::OperRequiresCallFlag(Compiler* comp) const case GT_CALL: return true; + case GT_GCPOLL: case GT_KEEPALIVE: return true; @@ -7243,6 +7247,7 @@ bool GenTree::OperRequiresGlobRefFlag(Compiler* comp) const case GT_MEMORYBARRIER: case GT_KEEPALIVE: case GT_SWIFT_ERROR: + case GT_GCPOLL: return true; case GT_CALL: @@ -9430,6 +9435,7 @@ GenTree* Compiler::gtCloneExpr(GenTree* tree) case GT_NOP: case GT_LABEL: case GT_SWIFT_ERROR: + case GT_GCPOLL: copy = new (this, oper) GenTree(oper, tree->gtType); goto DONE; @@ -10201,6 +10207,7 @@ GenTreeUseEdgeIterator::GenTreeUseEdgeIterator(GenTree* node) case GT_IL_OFFSET: case GT_NOP: case GT_SWIFT_ERROR: + case GT_GCPOLL: m_state = -1; return; @@ -12349,6 +12356,7 @@ void Compiler::gtDispLeaf(GenTree* tree, IndentStack* indentStack) case GT_PINVOKE_PROLOG: case GT_JMPTABLE: case GT_SWIFT_ERROR: + case GT_GCPOLL: break; case GT_RET_EXPR: diff --git a/src/coreclr/jit/gtlist.h b/src/coreclr/jit/gtlist.h index 75726fc2ef713f..ff7ed26f311e61 100644 --- a/src/coreclr/jit/gtlist.h +++ b/src/coreclr/jit/gtlist.h @@ -37,6 +37,7 @@ GTNODE(LABEL , GenTree ,0,0,GTK_LEAF) // Jump- GTNODE(JMP , GenTreeVal ,0,0,GTK_LEAF|GTK_NOVALUE) // Jump to another function GTNODE(FTN_ADDR , GenTreeFptrVal ,0,0,GTK_LEAF) // Address of a function GTNODE(RET_EXPR , GenTreeRetExpr ,0,0,GTK_LEAF|DBK_NOTLIR) // Place holder for the return expression from an inline candidate +GTNODE(GCPOLL , GenTree ,0,0,GTK_LEAF|GTK_NOVALUE|DBK_NOTLIR) //----------------------------------------------------------------------------- // Constant nodes: diff --git a/src/coreclr/jit/importercalls.cpp b/src/coreclr/jit/importercalls.cpp index 4db4d09c751871..da79c603b67b11 100644 --- a/src/coreclr/jit/importercalls.cpp +++ b/src/coreclr/jit/importercalls.cpp @@ -4090,6 +4090,22 @@ GenTree* Compiler::impIntrinsic(CORINFO_CLASS_HANDLE clsHnd, break; } + case NI_System_Threading_Thread_FastPollGC: + { + optMethodFlags |= OMF_NEEDS_GCPOLLS; + compCurBB->SetFlags(BBF_NEEDS_GCPOLL); + + GenTree* gcpoll = new (this, GT_GCPOLL) GenTree(GT_GCPOLL, TYP_VOID); + // Prevent both reordering and removal. Invalid optimizations of Thread.FastPollGC are + // very subtle and hard to observe. Thus we are conservatively marking it with both + // GTF_CALL and GTF_GLOB_REF side-effects even though it may be more than strictly + // necessary. The conservative side-effects are unlikely to have negative impact + // on code quality in this case. + gcpoll->gtFlags |= (GTF_CALL | GTF_GLOB_REF); + retNode = gcpoll; + break; + } + #if defined(TARGET_ARM64) || defined(TARGET_RISCV64) || defined(TARGET_XARCH) case NI_System_Threading_Interlocked_Or: case NI_System_Threading_Interlocked_And: @@ -11145,6 +11161,10 @@ NamedIntrinsic Compiler::lookupNamedIntrinsic(CORINFO_METHOD_HANDLE method) { result = NI_System_Threading_Thread_get_ManagedThreadId; } + else if (strcmp(methodName, "FastPollGC") == 0) + { + result = NI_System_Threading_Thread_FastPollGC; + } } else if (strcmp(className, "Volatile") == 0) { diff --git a/src/coreclr/jit/liveness.cpp b/src/coreclr/jit/liveness.cpp index 726b4a5e3b2763..b199d9b6612509 100644 --- a/src/coreclr/jit/liveness.cpp +++ b/src/coreclr/jit/liveness.cpp @@ -1470,6 +1470,7 @@ void Compiler::fgComputeLifeLIR(VARSET_TP& life, BasicBlock* block, VARSET_VALAR case GT_IL_OFFSET: case GT_KEEPALIVE: case GT_SWIFT_ERROR_RET: + case GT_GCPOLL: // Never remove these nodes, as they are always side-effecting. // // NOTE: the only side-effect of some of these nodes (GT_CMP, GT_SUB_HI) is a write to the flags diff --git a/src/coreclr/jit/namedintrinsiclist.h b/src/coreclr/jit/namedintrinsiclist.h index baf060aaec2e77..ddee451a200bea 100644 --- a/src/coreclr/jit/namedintrinsiclist.h +++ b/src/coreclr/jit/namedintrinsiclist.h @@ -75,6 +75,7 @@ enum NamedIntrinsic : unsigned short NI_System_Threading_Thread_get_CurrentThread, NI_System_Threading_Thread_get_ManagedThreadId, + NI_System_Threading_Thread_FastPollGC, NI_System_Threading_Volatile_Read, NI_System_Threading_Volatile_Write, NI_System_Threading_Volatile_ReadBarrier, diff --git a/src/coreclr/jit/optcse.cpp b/src/coreclr/jit/optcse.cpp index 5e298345fc4505..9afb109802e85f 100644 --- a/src/coreclr/jit/optcse.cpp +++ b/src/coreclr/jit/optcse.cpp @@ -1908,6 +1908,7 @@ bool CSE_HeuristicCommon::CanConsiderTree(GenTree* tree, bool isReturn) case GT_COLON: case GT_QMARK: case GT_NOP: + case GT_GCPOLL: case GT_RETURN: return false; // Currently the only special nodes that we hit // that we know that we don't want to CSE diff --git a/src/coreclr/jit/rationalize.cpp b/src/coreclr/jit/rationalize.cpp index 9c7a8c6a112c28..be2e6f619447b0 100644 --- a/src/coreclr/jit/rationalize.cpp +++ b/src/coreclr/jit/rationalize.cpp @@ -738,6 +738,13 @@ Compiler::fgWalkResult Rationalizer::RewriteNode(GenTree** useEdge, Compiler::Ge } break; + case GT_GCPOLL: + { + // GCPOLL is essentially a no-op, we used it as a hint for fgCreateGCPoll + node->gtBashToNOP(); + return Compiler::WALK_CONTINUE; + } + case GT_COMMA: { GenTree* op1 = node->gtGetOp1(); diff --git a/src/coreclr/jit/valuenum.cpp b/src/coreclr/jit/valuenum.cpp index 9e223650129306..8f34f02d9b97c9 100644 --- a/src/coreclr/jit/valuenum.cpp +++ b/src/coreclr/jit/valuenum.cpp @@ -12276,6 +12276,7 @@ void Compiler::fgValueNumberTree(GenTree* tree) // These do not represent values. case GT_NO_OP: case GT_NOP: + case GT_GCPOLL: case GT_JMP: // Control flow case GT_LABEL: // Control flow #if defined(FEATURE_EH_WINDOWS_X86) diff --git a/src/coreclr/vm/comutilnative.cpp b/src/coreclr/vm/comutilnative.cpp index 21bfdae843c073..a0f93aa88bd2ac 100644 --- a/src/coreclr/vm/comutilnative.cpp +++ b/src/coreclr/vm/comutilnative.cpp @@ -554,13 +554,14 @@ extern "C" void QCALLTYPE Buffer_MemMove(void *dst, void *src, size_t length) memmove(dst, src, length); } -extern "C" void QCALLTYPE Buffer_BulkMoveWithWriteBarrier(void *dst, void *src, size_t length) +FCIMPL3(VOID, Buffer::BulkMoveWithWriteBarrier, void *dst, void *src, size_t byteCount) { - QCALL_CONTRACT_NO_GC_TRANSITION; + FCALL_CONTRACT; - if (dst != src && length != 0) - InlinedMemmoveGCRefsHelper(dst, src, length); + if (dst != src && byteCount != 0) + InlinedMemmoveGCRefsHelper(dst, src, byteCount); } +FCIMPLEND // // GCInterface diff --git a/src/coreclr/vm/comutilnative.h b/src/coreclr/vm/comutilnative.h index b356872294bc93..1f5fda9ed02ccc 100644 --- a/src/coreclr/vm/comutilnative.h +++ b/src/coreclr/vm/comutilnative.h @@ -81,9 +81,14 @@ extern "C" void QCALLTYPE ExceptionNative_ThrowClassAccessException(MethodDesc* // // Buffer // +class Buffer +{ +public: + static FCDECL3(VOID, BulkMoveWithWriteBarrier, void *dst, void *src, size_t byteCount); +}; + extern "C" void QCALLTYPE Buffer_Clear(void *dst, size_t length); extern "C" void QCALLTYPE Buffer_MemMove(void *dst, void *src, size_t length); -extern "C" void QCALLTYPE Buffer_BulkMoveWithWriteBarrier(void *dst, void *src, size_t length); const UINT MEM_PRESSURE_COUNT = 4; diff --git a/src/coreclr/vm/ecalllist.h b/src/coreclr/vm/ecalllist.h index fc404a3d542ddb..6758a6f007d132 100644 --- a/src/coreclr/vm/ecalllist.h +++ b/src/coreclr/vm/ecalllist.h @@ -249,6 +249,10 @@ FCFuncStart(gArrayFuncs) FCFuncElement("GetCorElementTypeOfElementType", ArrayNative::GetCorElementTypeOfElementType) FCFuncEnd() +FCFuncStart(gBufferFuncs) + FCFuncElement("__BulkMoveWithWriteBarrier", Buffer::BulkMoveWithWriteBarrier) +FCFuncEnd() + FCFuncStart(gGCFrameRegistration) FCFuncElement("RegisterForGCReporting", GCReporting::Register) FCFuncElement("UnregisterForGCReporting", GCReporting::Unregister) @@ -369,6 +373,7 @@ FCFuncEnd() FCClassElement("Array", "System", gArrayFuncs) FCClassElement("AssemblyLoadContext", "System.Runtime.Loader", gAssemblyLoadContextFuncs) +FCClassElement("Buffer", "System", gBufferFuncs) FCClassElement("CastHelpers", "System.Runtime.CompilerServices", gCastHelpers) FCClassElement("ComAwareWeakReference", "System", gComAwareWeakReferenceFuncs) FCClassElement("Delegate", "System", gDelegateFuncs) diff --git a/src/coreclr/vm/qcallentrypoints.cpp b/src/coreclr/vm/qcallentrypoints.cpp index 1cb8bd85c8784b..5ac5a3889f9386 100644 --- a/src/coreclr/vm/qcallentrypoints.cpp +++ b/src/coreclr/vm/qcallentrypoints.cpp @@ -308,7 +308,6 @@ static const Entry s_QCall[] = DllImportEntry(ClrConfig_GetConfigBoolValue) DllImportEntry(Buffer_Clear) DllImportEntry(Buffer_MemMove) - DllImportEntry(Buffer_BulkMoveWithWriteBarrier) DllImportEntry(DependentHandle_InternalAllocWithGCTransition) DllImportEntry(DependentHandle_InternalFreeWithGCTransition) DllImportEntry(GCInterface_GetTotalAllocatedBytesPrecise) diff --git a/src/libraries/System.Private.CoreLib/src/System/Buffer.cs b/src/libraries/System.Private.CoreLib/src/System/Buffer.cs index d3fcd04dcb46ff..5528f5d2ccd06e 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Buffer.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Buffer.cs @@ -4,6 +4,7 @@ using System.Diagnostics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; +using System.Threading; namespace System { @@ -178,9 +179,14 @@ ref Unsafe.As(ref source), internal static void BulkMoveWithWriteBarrier(ref byte destination, ref byte source, nuint byteCount) { if (byteCount <= BulkMoveWithWriteBarrierChunk) + { __BulkMoveWithWriteBarrier(ref destination, ref source, byteCount); + Thread.FastPollGC(); + } else + { _BulkMoveWithWriteBarrier(ref destination, ref source, byteCount); + } } // Non-inlinable wrapper around the loop for copying large blocks in chunks @@ -200,6 +206,7 @@ private static void _BulkMoveWithWriteBarrier(ref byte destination, ref byte sou { byteCount -= BulkMoveWithWriteBarrierChunk; __BulkMoveWithWriteBarrier(ref destination, ref source, BulkMoveWithWriteBarrierChunk); + Thread.FastPollGC(); destination = ref Unsafe.AddByteOffset(ref destination, BulkMoveWithWriteBarrierChunk); source = ref Unsafe.AddByteOffset(ref source, BulkMoveWithWriteBarrierChunk); } @@ -212,10 +219,12 @@ private static void _BulkMoveWithWriteBarrier(ref byte destination, ref byte sou { byteCount -= BulkMoveWithWriteBarrierChunk; __BulkMoveWithWriteBarrier(ref Unsafe.AddByteOffset(ref destination, byteCount), ref Unsafe.AddByteOffset(ref source, byteCount), BulkMoveWithWriteBarrierChunk); + Thread.FastPollGC(); } while (byteCount > BulkMoveWithWriteBarrierChunk); } __BulkMoveWithWriteBarrier(ref destination, ref source, byteCount); + Thread.FastPollGC(); } #endif // !MONO diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/Thread.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/Thread.cs index 7157468c7528fc..4dc0a5002e7e78 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/Thread.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/Thread.cs @@ -374,6 +374,11 @@ public static void Sleep(int millisecondsTimeout) internal static ulong CurrentOSThreadId => GetCurrentOSThreadId(); #endif +#if !MONO + [Intrinsic] + internal static void FastPollGC() => FastPollGC(); +#endif + public ExecutionContext? ExecutionContext => ExecutionContext.Capture(); public string? Name From 55060a9e8ce02484552003225a5bc156641f8ce0 Mon Sep 17 00:00:00 2001 From: Aaron R Robinson Date: Fri, 21 Mar 2025 10:50:48 -0700 Subject: [PATCH 13/15] Update helper names across runtimes. --- .../src/System/Buffer.CoreCLR.cs | 6 ++--- .../src/System/GC.CoreCLR.cs | 9 ++++---- .../src/System/Buffer.NativeAot.cs | 6 ++--- src/coreclr/vm/comutilnative.cpp | 1 + src/coreclr/vm/ecalllist.h | 4 ++-- .../src/System/Buffer.cs | 22 +++++++++---------- .../src/System/SpanHelpers.ByteMemOps.cs | 4 ++-- .../src/System/Buffer.Mono.cs | 4 ++-- src/mono/mono/metadata/icall-def.h | 4 ++-- 9 files changed, 30 insertions(+), 30 deletions(-) diff --git a/src/coreclr/System.Private.CoreLib/src/System/Buffer.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Buffer.CoreCLR.cs index 2a375e74035f0d..69c7ee7b4abb91 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Buffer.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Buffer.CoreCLR.cs @@ -10,13 +10,13 @@ namespace System public partial class Buffer { [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "Buffer_Clear")] - private static unsafe partial void __ZeroMemory(void* b, nuint byteLength); + private static unsafe partial void ZeroMemoryInternal(void* b, nuint byteLength); [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "Buffer_MemMove")] - private static unsafe partial void __Memmove(byte* dest, byte* src, nuint len); + private static unsafe partial void MemmoveInternal(byte* dest, byte* src, nuint len); [MethodImpl(MethodImplOptions.InternalCall)] - private static extern void __BulkMoveWithWriteBarrier(ref byte destination, ref byte source, nuint byteCount); + private static extern void BulkMoveWithWriteBarrierInternal(ref byte destination, ref byte source, nuint byteCount); // Used by ilmarshalers.cpp internal static unsafe void Memcpy(byte* dest, byte* src, int len) diff --git a/src/coreclr/System.Private.CoreLib/src/System/GC.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/GC.CoreCLR.cs index 225cc7208ee218..1d588b0a4dbbe8 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/GC.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/GC.CoreCLR.cs @@ -347,17 +347,16 @@ public static void WaitForPendingFinalizers() // Indicates that the system should not call the Finalize() method on // an object that would normally require this call. [MethodImpl(MethodImplOptions.InternalCall)] - private static extern void _SuppressFinalize(object o); + private static extern void SuppressFinalizeInternal(object o); public static unsafe void SuppressFinalize(object obj) { ArgumentNullException.ThrowIfNull(obj); - if (RuntimeHelpers.GetMethodTable(obj)->HasFinalizer) + MethodTable* pMT = RuntimeHelpers.GetMethodTable(obj); + if (pMT->HasFinalizer) { - // SuppressFinalize is a no-op if the object doesn't have a finalizer. - // We don't need to call _SuppressFinalize in that case. - _SuppressFinalize(obj); + SuppressFinalizeInternal(obj); } } diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Buffer.NativeAot.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Buffer.NativeAot.cs index caf6d9b7089a3d..4ceecdf70f664b 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Buffer.NativeAot.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Buffer.NativeAot.cs @@ -12,15 +12,15 @@ namespace System public static partial class Buffer { [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static unsafe void __ZeroMemory(void* b, nuint byteLength) => + private static unsafe void ZeroMemoryInternal(void* b, nuint byteLength) => RuntimeImports.memset((byte*)b, 0, byteLength); [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static unsafe void __Memmove(byte* dest, byte* src, nuint len) => + private static unsafe void MemmoveInternal(byte* dest, byte* src, nuint len) => RuntimeImports.memmove(dest, src, len); [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static void __BulkMoveWithWriteBarrier(ref byte destination, ref byte source, nuint byteCount) => + private static void BulkMoveWithWriteBarrierInternal(ref byte destination, ref byte source, nuint byteCount) => RuntimeImports.RhBulkMoveWithWriteBarrier(ref destination, ref source, byteCount); } } diff --git a/src/coreclr/vm/comutilnative.cpp b/src/coreclr/vm/comutilnative.cpp index a0f93aa88bd2ac..d039226c328cad 100644 --- a/src/coreclr/vm/comutilnative.cpp +++ b/src/coreclr/vm/comutilnative.cpp @@ -1101,6 +1101,7 @@ extern "C" void QCALLTYPE GCInterface_UnregisterFrozenSegment(void* segment) FCIMPL1(void, GCInterface::SuppressFinalize, Object *obj) { FCALL_CONTRACT; + _ASSERTE(obj->GetMethodTable ()->HasFinalizer()); GCHeapUtilities::GetGCHeap()->SetFinalizationRun(obj); } diff --git a/src/coreclr/vm/ecalllist.h b/src/coreclr/vm/ecalllist.h index 6758a6f007d132..8138eef495db94 100644 --- a/src/coreclr/vm/ecalllist.h +++ b/src/coreclr/vm/ecalllist.h @@ -250,7 +250,7 @@ FCFuncStart(gArrayFuncs) FCFuncEnd() FCFuncStart(gBufferFuncs) - FCFuncElement("__BulkMoveWithWriteBarrier", Buffer::BulkMoveWithWriteBarrier) + FCFuncElement("BulkMoveWithWriteBarrierInternal", Buffer::BulkMoveWithWriteBarrier) FCFuncEnd() FCFuncStart(gGCFrameRegistration) @@ -270,7 +270,7 @@ FCFuncStart(gGCInterfaceFuncs) FCFuncElement("GetGenerationSize", GCInterface::GetGenerationSize) FCFuncElement("GetGenerationInternal", GCInterface::GetGenerationInternal) FCFuncElement("GetMaxGeneration", GCInterface::GetMaxGeneration) - FCFuncElement("_SuppressFinalize", GCInterface::SuppressFinalize) + FCFuncElement("SuppressFinalizeInternal", GCInterface::SuppressFinalize) FCFuncElement("GetAllocatedBytesForCurrentThread", GCInterface::GetAllocatedBytesForCurrentThread) FCFuncElement("GetTotalAllocatedBytesApproximate", GCInterface::GetTotalAllocatedBytesApproximate) diff --git a/src/libraries/System.Private.CoreLib/src/System/Buffer.cs b/src/libraries/System.Private.CoreLib/src/System/Buffer.cs index 5528f5d2ccd06e..7de71b86ea6d04 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Buffer.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Buffer.cs @@ -127,21 +127,21 @@ public static unsafe void MemoryCopy(void* source, void* destination, ulong dest // Non-inlinable wrapper around the QCall that avoids polluting the fast path // with P/Invoke prolog/epilog. [MethodImpl(MethodImplOptions.NoInlining)] - internal static unsafe void _Memmove(ref byte dest, ref byte src, nuint len) + internal static unsafe void Memmove(ref byte dest, ref byte src, nuint len) { fixed (byte* pDest = &dest) fixed (byte* pSrc = &src) - __Memmove(pDest, pSrc, len); + MemmoveInternal(pDest, pSrc, len); } // Non-inlinable wrapper around the QCall that avoids polluting the fast path // with P/Invoke prolog/epilog. [MethodImpl(MethodImplOptions.NoInlining)] - internal static unsafe void _ZeroMemory(ref byte b, nuint byteLength) + internal static unsafe void ZeroMemory(ref byte b, nuint byteLength) { fixed (byte* bytePointer = &b) { - __ZeroMemory(bytePointer, byteLength); + ZeroMemoryInternal(bytePointer, byteLength); } } @@ -169,7 +169,7 @@ ref Unsafe.As(ref source), } } - // The maximum block size to for __BulkMoveWithWriteBarrier FCall. This is required to avoid GC starvation. + // The maximum block size to for BulkMoveWithWriteBarrierInternal FCall. This is required to avoid GC starvation. #if DEBUG // Stress the mechanism in debug builds private const uint BulkMoveWithWriteBarrierChunk = 0x400; #else @@ -180,18 +180,18 @@ internal static void BulkMoveWithWriteBarrier(ref byte destination, ref byte sou { if (byteCount <= BulkMoveWithWriteBarrierChunk) { - __BulkMoveWithWriteBarrier(ref destination, ref source, byteCount); + BulkMoveWithWriteBarrierInternal(ref destination, ref source, byteCount); Thread.FastPollGC(); } else { - _BulkMoveWithWriteBarrier(ref destination, ref source, byteCount); + BulkMoveWithWriteBarrierBatch(ref destination, ref source, byteCount); } } // Non-inlinable wrapper around the loop for copying large blocks in chunks [MethodImpl(MethodImplOptions.NoInlining)] - private static void _BulkMoveWithWriteBarrier(ref byte destination, ref byte source, nuint byteCount) + private static void BulkMoveWithWriteBarrierBatch(ref byte destination, ref byte source, nuint byteCount) { Debug.Assert(byteCount > BulkMoveWithWriteBarrierChunk); @@ -205,7 +205,7 @@ private static void _BulkMoveWithWriteBarrier(ref byte destination, ref byte sou do { byteCount -= BulkMoveWithWriteBarrierChunk; - __BulkMoveWithWriteBarrier(ref destination, ref source, BulkMoveWithWriteBarrierChunk); + BulkMoveWithWriteBarrierInternal(ref destination, ref source, BulkMoveWithWriteBarrierChunk); Thread.FastPollGC(); destination = ref Unsafe.AddByteOffset(ref destination, BulkMoveWithWriteBarrierChunk); source = ref Unsafe.AddByteOffset(ref source, BulkMoveWithWriteBarrierChunk); @@ -218,12 +218,12 @@ private static void _BulkMoveWithWriteBarrier(ref byte destination, ref byte sou do { byteCount -= BulkMoveWithWriteBarrierChunk; - __BulkMoveWithWriteBarrier(ref Unsafe.AddByteOffset(ref destination, byteCount), ref Unsafe.AddByteOffset(ref source, byteCount), BulkMoveWithWriteBarrierChunk); + BulkMoveWithWriteBarrierInternal(ref Unsafe.AddByteOffset(ref destination, byteCount), ref Unsafe.AddByteOffset(ref source, byteCount), BulkMoveWithWriteBarrierChunk); Thread.FastPollGC(); } while (byteCount > BulkMoveWithWriteBarrierChunk); } - __BulkMoveWithWriteBarrier(ref destination, ref source, byteCount); + BulkMoveWithWriteBarrierInternal(ref destination, ref source, byteCount); Thread.FastPollGC(); } diff --git a/src/libraries/System.Private.CoreLib/src/System/SpanHelpers.ByteMemOps.cs b/src/libraries/System.Private.CoreLib/src/System/SpanHelpers.ByteMemOps.cs index de4b6e97e427dd..319c23e0bf2152 100644 --- a/src/libraries/System.Private.CoreLib/src/System/SpanHelpers.ByteMemOps.cs +++ b/src/libraries/System.Private.CoreLib/src/System/SpanHelpers.ByteMemOps.cs @@ -239,7 +239,7 @@ internal static void Memmove(ref byte dest, ref byte src, nuint len) Debug.Assert(len > 0); _ = Unsafe.ReadUnaligned(ref dest); _ = Unsafe.ReadUnaligned(ref src); - Buffer._Memmove(ref dest, ref src, len); + Buffer.Memmove(ref dest, ref src, len); } [Intrinsic] // Unrolled for small sizes @@ -425,7 +425,7 @@ public static void ClearWithoutReferences(ref byte dest, nuint len) PInvoke: // Implicit nullchecks _ = Unsafe.ReadUnaligned(ref dest); - Buffer._ZeroMemory(ref dest, len); + Buffer.ZeroMemory(ref dest, len); } internal static void Fill(ref byte dest, byte value, nuint len) diff --git a/src/mono/System.Private.CoreLib/src/System/Buffer.Mono.cs b/src/mono/System.Private.CoreLib/src/System/Buffer.Mono.cs index a74166daba159c..4139326cc1a370 100644 --- a/src/mono/System.Private.CoreLib/src/System/Buffer.Mono.cs +++ b/src/mono/System.Private.CoreLib/src/System/Buffer.Mono.cs @@ -8,10 +8,10 @@ namespace System public partial class Buffer { [MethodImpl(MethodImplOptions.InternalCall)] - private static extern unsafe void __ZeroMemory(void* b, nuint byteLength); + private static extern unsafe void ZeroMemoryInternal(void* b, nuint byteLength); [MethodImpl(MethodImplOptions.InternalCall)] - private static extern unsafe void __Memmove(byte* dest, byte* src, nuint len); + private static extern unsafe void MemmoveInternal(byte* dest, byte* src, nuint len); [MethodImpl(MethodImplOptions.InternalCall)] private static extern void BulkMoveWithWriteBarrier(ref byte dmem, ref byte smem, nuint len, IntPtr type_handle); diff --git a/src/mono/mono/metadata/icall-def.h b/src/mono/mono/metadata/icall-def.h index 41a0481a7f047e..2661ebd4bc0989 100644 --- a/src/mono/mono/metadata/icall-def.h +++ b/src/mono/mono/metadata/icall-def.h @@ -140,8 +140,8 @@ HANDLES(ARRAY_14, "SetValueRelaxedImpl", ves_icall_System_Array_SetValueRelaxed ICALL_TYPE(BUFFER, "System.Buffer", BUFFER_0) NOHANDLES(ICALL(BUFFER_0, "BulkMoveWithWriteBarrier", ves_icall_System_Buffer_BulkMoveWithWriteBarrier)) -NOHANDLES(ICALL(BUFFER_2, "__Memmove", ves_icall_System_Runtime_RuntimeImports_Memmove)) -NOHANDLES(ICALL(BUFFER_3, "__ZeroMemory", ves_icall_System_Runtime_RuntimeImports_ZeroMemory)) +NOHANDLES(ICALL(BUFFER_2, "MemmoveInternal", ves_icall_System_Runtime_RuntimeImports_Memmove)) +NOHANDLES(ICALL(BUFFER_3, "ZeroMemoryInternal", ves_icall_System_Runtime_RuntimeImports_ZeroMemory)) ICALL_TYPE(DELEGATE, "System.Delegate", DELEGATE_1) HANDLES(DELEGATE_1, "AllocDelegateLike_internal", ves_icall_System_Delegate_AllocDelegateLike_internal, MonoMulticastDelegate, 1, (MonoDelegate)) From c8c238f990cd3cfa84ca45838e62116a9538350e Mon Sep 17 00:00:00 2001 From: Aaron R Robinson Date: Sun, 23 Mar 2025 13:29:45 -0700 Subject: [PATCH 14/15] Tweak GC.CoreCLR.cs file for whitespace issues. --- .../src/System/GC.CoreCLR.cs | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/src/coreclr/System.Private.CoreLib/src/System/GC.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/GC.CoreCLR.cs index 1d588b0a4dbbe8..184ce80f44b899 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/GC.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/GC.CoreCLR.cs @@ -2,14 +2,9 @@ // The .NET Foundation licenses this file to you under the MIT license. /*============================================================ -** -** -** ** Purpose: Exposes features of the Garbage Collector through ** the class libraries. This is a class which cannot be ** instantiated. -** -** ===========================================================*/ using System.Collections.Generic; @@ -604,12 +599,14 @@ private static bool InvokeMemoryLoadChangeNotifications() return true; } - // We need to take a snapshot of s_notifications.Count, so that in the case that s_notifications[i].Notification() registers new notifications, - // we neither get rid of them nor iterate over them + // We need to take a snapshot of s_notifications.Count, so that in the case that + // s_notifications[i].Notification() registers new notifications, we neither get rid + // of them nor iterate over them. int count = s_notifications.Count; - // If there is no existing notifications, we won't be iterating over any and we won't be adding any new one. Also, there wasn't any added since - // we last invoked this method so it's safe to assume we can reset s_previousMemoryLoad. + // If there is no existing notifications, we won't be iterating over any and we won't + // be adding any new one. Also, there wasn't any added since we last invoked this + // method so it's safe to assume we can reset s_previousMemoryLoad. if (count == 0) { s_previousMemoryLoad = float.MaxValue; From 80e825071b127a3103bd3d01f37bdb2d8f3097d1 Mon Sep 17 00:00:00 2001 From: Aaron R Robinson Date: Sun, 23 Mar 2025 13:30:33 -0700 Subject: [PATCH 15/15] Try to fix GCPoll regression. --- .../src/System/Threading/Thread.CoreCLR.cs | 6 ++++-- src/coreclr/vm/comsynchronizable.cpp | 8 ++++++++ src/coreclr/vm/comsynchronizable.h | 5 +++-- src/coreclr/vm/ecalllist.h | 1 + 4 files changed, 16 insertions(+), 4 deletions(-) diff --git a/src/coreclr/System.Private.CoreLib/src/System/Threading/Thread.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Threading/Thread.CoreCLR.cs index ac849954207553..9b78cde00d6141 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Threading/Thread.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Threading/Thread.CoreCLR.cs @@ -491,6 +491,9 @@ private void ResetFinalizerThreadSlow() } } + [MethodImpl(MethodImplOptions.InternalCall)] + private static extern bool CatchAtSafePoint(); + [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "ThreadNative_PollGC")] private static partial void PollGCInternal(); @@ -499,8 +502,7 @@ private void ResetFinalizerThreadSlow() // as a small assembly stub which checks the global g_TrapReturningThreads flag and tail-call to this helper private static unsafe void PollGC() { - NativeThreadState catchAtSafePoint = ((NativeThreadClass*)Thread.DirectOnThreadLocalData.pNativeThread)->m_State & NativeThreadState.TS_CatchAtSafePoint; - if (catchAtSafePoint != NativeThreadState.None) + if (CatchAtSafePoint()) { PollGCWorker(); } diff --git a/src/coreclr/vm/comsynchronizable.cpp b/src/coreclr/vm/comsynchronizable.cpp index 2e4170a81ef838..c759df307a71be 100644 --- a/src/coreclr/vm/comsynchronizable.cpp +++ b/src/coreclr/vm/comsynchronizable.cpp @@ -714,6 +714,14 @@ FCIMPL1(void, ThreadNative::Finalize, ThreadBaseObject* pThisUNSAFE) } FCIMPLEND +FCIMPL0(FC_BOOL_RET, ThreadNative::CatchAtSafePoint) +{ + FCALL_CONTRACT; + + FC_RETURN_BOOL(GetThread()->CatchAtSafePoint()); +} +FCIMPLEND + // Get whether or not this is a background thread. extern "C" BOOL QCALLTYPE ThreadNative_GetIsBackground(QCall::ThreadHandle thread) { diff --git a/src/coreclr/vm/comsynchronizable.h b/src/coreclr/vm/comsynchronizable.h index 9b17981e16e7a3..004da5691c7a65 100644 --- a/src/coreclr/vm/comsynchronizable.h +++ b/src/coreclr/vm/comsynchronizable.h @@ -39,8 +39,9 @@ class ThreadNative ThreadAbortRequested = 128, }; - static FCDECL0(INT32, GetOptimalMaxSpinWaitsPerSpinIteration); - static FCDECL1(void, Finalize, ThreadBaseObject* pThis); + static FCDECL0(INT32, GetOptimalMaxSpinWaitsPerSpinIteration); + static FCDECL1(void, Finalize, ThreadBaseObject* pThis); + static FCDECL0(FC_BOOL_RET, CatchAtSafePoint); }; extern "C" void QCALLTYPE ThreadNative_Start(QCall::ThreadHandle thread, int threadStackSize, int priority, BOOL isThreadPool, PCWSTR pThreadName); diff --git a/src/coreclr/vm/ecalllist.h b/src/coreclr/vm/ecalllist.h index 8138eef495db94..0ec9409cb9676d 100644 --- a/src/coreclr/vm/ecalllist.h +++ b/src/coreclr/vm/ecalllist.h @@ -238,6 +238,7 @@ FCFuncEnd() FCFuncStart(gThreadFuncs) FCFuncElement("InternalFinalize", ThreadNative::Finalize) + FCFuncElement("CatchAtSafePoint", ThreadNative::CatchAtSafePoint) FCFuncElement("get_OptimalMaxSpinWaitsPerSpinIteration", ThreadNative::GetOptimalMaxSpinWaitsPerSpinIteration) FCFuncEnd()