Skip to content

Commit 0d97ceb

Browse files
committed
Improve performance of IsCollectible reflection properties
These properties are often used for non-collectible fast paths. It is important that they are cheap. Converted these proproperties to FCalls to reduce overhead. Fixes #119499
1 parent 2bcadad commit 0d97ceb

File tree

14 files changed

+77
-63
lines changed

14 files changed

+77
-63
lines changed

src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimeAssembly.cs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -252,15 +252,16 @@ public override IEnumerable<TypeInfo> DefinedTypes
252252
get => GetManifestModule().GetDefinedTypes();
253253
}
254254

255-
[LibraryImport(RuntimeHelpers.QCall, EntryPoint = "AssemblyNative_GetIsCollectible")]
256-
internal static partial Interop.BOOL GetIsCollectible(QCallAssembly assembly);
255+
[MethodImpl(MethodImplOptions.InternalCall)]
256+
private static extern bool GetIsCollectible(IntPtr assembly);
257257

258258
public override bool IsCollectible
259259
{
260260
get
261261
{
262-
RuntimeAssembly runtimeAssembly = this;
263-
return GetIsCollectible(new QCallAssembly(ref runtimeAssembly)) != Interop.BOOL.FALSE;
262+
bool isCollectible = GetIsCollectible(GetUnderlyingNativeHandle());
263+
GC.KeepAlive(this); // We directly pass the native handle above - make sure this object stays alive for the call
264+
return isCollectible;
264265
}
265266
}
266267

src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimeMethodInfo.CoreCLR.cs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -319,7 +319,15 @@ internal void InvokePropertySetter(object? obj, BindingFlags invokeAttr, Binder?
319319

320320
public override ParameterInfo ReturnParameter => FetchReturnParameter();
321321

322-
public override bool IsCollectible => RuntimeMethodHandle.GetIsCollectible(new RuntimeMethodHandleInternal(m_handle)) != Interop.BOOL.FALSE;
322+
public override bool IsCollectible
323+
{
324+
get
325+
{
326+
bool isCollectible = RuntimeMethodHandle.IsCollectible(new RuntimeMethodHandleInternal(m_handle));
327+
GC.KeepAlive(this); // We directly pass the native handle above - make sure this object stays alive for the call
328+
return isCollectible;
329+
}
330+
}
323331

324332
public override MethodInfo GetBaseDefinition()
325333
{

src/coreclr/System.Private.CoreLib/src/System/RuntimeHandles.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -824,8 +824,8 @@ internal RuntimeType MakePointer()
824824
return type!;
825825
}
826826

827-
[LibraryImport(RuntimeHelpers.QCall, EntryPoint = "RuntimeTypeHandle_IsCollectible")]
828-
internal static partial Interop.BOOL IsCollectible(QCallTypeHandle handle);
827+
[MethodImpl(MethodImplOptions.InternalCall)]
828+
internal static extern bool IsCollectible(RuntimeType type);
829829

830830
[LibraryImport(RuntimeHelpers.QCall, EntryPoint = "RuntimeTypeHandle_GetGenericTypeDefinition")]
831831
internal static partial void GetGenericTypeDefinition(QCallTypeHandle type, ObjectHandleOnStack retType);
@@ -1053,8 +1053,8 @@ public IntPtr GetFunctionPointer()
10531053
return ptr;
10541054
}
10551055

1056-
[LibraryImport(RuntimeHelpers.QCall, EntryPoint = "RuntimeMethodHandle_GetIsCollectible")]
1057-
internal static partial Interop.BOOL GetIsCollectible(RuntimeMethodHandleInternal handle);
1056+
[MethodImpl(MethodImplOptions.InternalCall)]
1057+
internal static extern bool IsCollectible(RuntimeMethodHandleInternal method);
10581058

10591059
[LibraryImport(RuntimeHelpers.QCall, EntryPoint = "RuntimeMethodHandle_IsCAVisibleFromDecoratedType")]
10601060
internal static partial Interop.BOOL IsCAVisibleFromDecoratedType(

src/coreclr/System.Private.CoreLib/src/System/RuntimeType.CoreCLR.cs

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3227,14 +3227,7 @@ public override MemberInfo GetMemberWithSameMetadataDefinitionAs(MemberInfo memb
32273227

32283228
#region Identity
32293229

3230-
public sealed override bool IsCollectible
3231-
{
3232-
get
3233-
{
3234-
RuntimeType thisType = this;
3235-
return RuntimeTypeHandle.IsCollectible(new QCallTypeHandle(ref thisType)) != Interop.BOOL.FALSE;
3236-
}
3237-
}
3230+
public sealed override bool IsCollectible => RuntimeTypeHandle.IsCollectible(this);
32383231

32393232
public override MethodBase? DeclaringMethod
32403233
{

src/coreclr/vm/assemblynative.cpp

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -742,20 +742,18 @@ extern "C" void QCALLTYPE AssemblyNative_GetModules(QCall::AssemblyHandle pAssem
742742
END_QCALL;
743743
}
744744

745-
extern "C" BOOL QCALLTYPE AssemblyNative_GetIsCollectible(QCall::AssemblyHandle pAssembly)
745+
FCIMPL1(FC_BOOL_RET, AssemblyNative::GetIsCollectible, Assembly* pAssembly)
746746
{
747-
QCALL_CONTRACT;
748-
749-
BOOL retVal = FALSE;
750-
751-
BEGIN_QCALL;
752-
753-
retVal = pAssembly->IsCollectible();
754-
755-
END_QCALL;
747+
CONTRACTL
748+
{
749+
FCALL_CHECK;
750+
PRECONDITION(CheckPointer(pAssembly));
751+
}
752+
CONTRACTL_END;
756753

757-
return retVal;
754+
FC_RETURN_BOOL(pAssembly->IsCollectible());
758755
}
756+
FCIMPLEND
759757

760758
extern volatile uint32_t g_cAssemblies;
761759

src/coreclr/vm/assemblynative.hpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,9 @@ class AssemblyNative
3232

3333
static
3434
FCDECL1(FC_BOOL_RET, GetIsDynamic, Assembly* pAssembly);
35+
36+
static
37+
FCDECL1(FC_BOOL_RET, GetIsCollectible, Assembly* pAssembly);
3538
};
3639

3740
extern "C" uint32_t QCALLTYPE AssemblyNative_GetAssemblyCount();
@@ -108,8 +111,6 @@ extern "C" void QCALLTYPE AssemblyNative_GetEntryPoint(QCall::AssemblyHandle pAs
108111
extern "C" void QCALLTYPE AssemblyNative_GetImageRuntimeVersion(QCall::AssemblyHandle pAssembly, QCall::StringHandleOnStack retString);
109112

110113

111-
extern "C" BOOL QCALLTYPE AssemblyNative_GetIsCollectible(QCall::AssemblyHandle pAssembly);
112-
113114
extern "C" INT_PTR QCALLTYPE AssemblyNative_InitializeAssemblyLoadContext(INT_PTR ptrAssemblyLoadContext, BOOL fRepresentsTPALoadContext, BOOL fIsCollectible);
114115

115116
extern "C" void QCALLTYPE AssemblyNative_PrepareForAssemblyLoadContextRelease(INT_PTR ptrNativeAssemblyBinder, INT_PTR ptrManagedStrongAssemblyLoadContext);

src/coreclr/vm/ecalllist.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@ FCFuncStart(gCOMTypeHandleFuncs)
9494
FCFuncElement("IsGenericVariable", RuntimeTypeHandle::IsGenericVariable)
9595
FCFuncElement("ContainsGenericVariables", RuntimeTypeHandle::ContainsGenericVariables)
9696
FCFuncElement("IsUnmanagedFunctionPointer", RuntimeTypeHandle::IsUnmanagedFunctionPointer)
97+
FCFuncElement("IsCollectible", RuntimeTypeHandle::IsCollectible)
9798
FCFuncElement("CompareCanonicalHandles", RuntimeTypeHandle::CompareCanonicalHandles)
9899
FCFuncElement("InternalAllocNoChecks_FastPath", RuntimeTypeHandle::InternalAllocNoChecks_FastPath)
99100
FCFuncEnd()
@@ -134,6 +135,7 @@ FCFuncEnd()
134135

135136
FCFuncStart(gRuntimeMethodHandle)
136137
FCFuncElement("GetImplAttributes", RuntimeMethodHandle::GetImplAttributes)
138+
FCFuncElement("IsCollectible", RuntimeMethodHandle::IsCollectible)
137139
FCFuncElement("GetAttributes", RuntimeMethodHandle::GetAttributes)
138140
FCFuncElement("GetMethodTable", RuntimeMethodHandle::GetMethodTable)
139141
FCFuncElement("GetSlot", RuntimeMethodHandle::GetSlot)
@@ -166,6 +168,7 @@ FCFuncEnd()
166168

167169
FCFuncStart(gRuntimeAssemblyFuncs)
168170
FCFuncElement("GetIsDynamic", AssemblyNative::GetIsDynamic)
171+
FCFuncElement("GetIsCollectible", AssemblyNative::GetIsCollectible)
169172
FCFuncElement("GetManifestModule", AssemblyHandle::GetManifestModule)
170173
FCFuncElement("GetTokenInternal", AssemblyHandle::GetTokenInternal)
171174
FCFuncEnd()

src/coreclr/vm/method.cpp

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3856,9 +3856,15 @@ MethodDesc *MethodDesc::GetInterfaceMD()
38563856
}
38573857
#endif // !DACCESS_COMPILE
38583858

3859+
bool MethodDesc::IsCollectible()
3860+
{
3861+
LIMITED_METHOD_DAC_CONTRACT;
3862+
return HasMethodInstantiation() ? GetLoaderAllocator()->IsCollectible() : GetMethodTable()->Collectible();
3863+
}
3864+
38593865
PTR_LoaderAllocator MethodDesc::GetLoaderAllocator()
38603866
{
3861-
WRAPPER_NO_CONTRACT;
3867+
LIMITED_METHOD_DAC_CONTRACT;
38623868
return GetLoaderModule()->GetLoaderAllocator();
38633869
}
38643870

src/coreclr/vm/method.hpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -595,6 +595,8 @@ class MethodDesc
595595

596596
MethodDescBackpatchInfoTracker* GetBackpatchInfoTracker();
597597

598+
bool IsCollectible();
599+
598600
PTR_LoaderAllocator GetLoaderAllocator();
599601

600602
Module* GetLoaderModule();

src/coreclr/vm/qcallentrypoints.cpp

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,6 @@ static const Entry s_QCall[] =
122122
DllImportEntry(RuntimeTypeHandle_MakeByRef)
123123
DllImportEntry(RuntimeTypeHandle_MakeSZArray)
124124
DllImportEntry(RuntimeTypeHandle_MakeArray)
125-
DllImportEntry(RuntimeTypeHandle_IsCollectible)
126125
DllImportEntry(RuntimeTypeHandle_GetConstraints)
127126
DllImportEntry(RuntimeTypeHandle_GetArgumentTypesFromFunctionPointer)
128127
DllImportEntry(RuntimeTypeHandle_GetAssemblySlow)
@@ -159,7 +158,6 @@ static const Entry s_QCall[] =
159158
DllImportEntry(RuntimeMethodHandle_InvokeMethod)
160159
DllImportEntry(RuntimeMethodHandle_ConstructInstantiation)
161160
DllImportEntry(RuntimeMethodHandle_GetFunctionPointer)
162-
DllImportEntry(RuntimeMethodHandle_GetIsCollectible)
163161
DllImportEntry(RuntimeMethodHandle_GetMethodInstantiation)
164162
DllImportEntry(RuntimeMethodHandle_GetTypicalMethodDefinition)
165163
DllImportEntry(RuntimeMethodHandle_StripMethodInstantiation)
@@ -249,7 +247,6 @@ static const Entry s_QCall[] =
249247
DllImportEntry(AssemblyNative_GetExportedTypes)
250248
DllImportEntry(AssemblyNative_GetEntryPoint)
251249
DllImportEntry(AssemblyNative_GetImageRuntimeVersion)
252-
DllImportEntry(AssemblyNative_GetIsCollectible)
253250
DllImportEntry(AssemblyNative_InternalTryGetRawMetadata)
254251
DllImportEntry(AssemblyNative_ApplyUpdate)
255252
DllImportEntry(AssemblyNative_IsApplyUpdateSupported)

0 commit comments

Comments
 (0)