From afa7a602b1f9e11216510c1d0d3d7b297e44d46f Mon Sep 17 00:00:00 2001 From: EgorBo Date: Thu, 11 Feb 2021 15:53:02 +0300 Subject: [PATCH 1/9] Devirtualize Comparer.Default --- .../Collections/Generic/Comparer.CoreCLR.cs | 2 +- .../superpmi/superpmi-shared/lwmlist.h | 1 + .../superpmi-shared/methodcontext.cpp | 17 + .../superpmi/superpmi-shared/methodcontext.h | 7 +- .../superpmi-shim-collector/icorjitinfo.cpp | 10 + .../superpmi-shim-counter/icorjitinfo.cpp | 7 + .../superpmi-shim-simple/icorjitinfo.cpp | 6 + .../ToolBox/superpmi/superpmi/icorjitinfo.cpp | 9 + src/coreclr/inc/corinfo.h | 6 + src/coreclr/inc/icorjitinfoimpl_generated.h | 3 + src/coreclr/inc/jiteeversionguid.h | 10 +- src/coreclr/jit/ICorJitInfo_API_names.h | 1 + src/coreclr/jit/ICorJitInfo_API_wrapper.hpp | 9 + src/coreclr/jit/flowgraph.cpp | 3 +- src/coreclr/jit/importer.cpp | 16 +- src/coreclr/jit/namedintrinsiclist.h | 1 + .../tools/Common/JitInterface/CorInfoBase.cs | 332 +++++++++--------- .../tools/Common/JitInterface/CorInfoImpl.cs | 7 + .../ThunkGenerator/ThunkInput.txt | 1 + .../TypeSystem/IL/Stubs/ComparerIntrinsics.cs | 8 + .../tools/aot/jitinterface/jitinterface.h | 10 + src/coreclr/vm/corelib.h | 6 + src/coreclr/vm/jitinterface.cpp | 80 +++++ src/coreclr/vm/jitinterface.h | 4 + src/coreclr/zap/zapinfo.cpp | 6 + .../System/Collections/Generic/Comparer.cs | 1 + .../Devirtualization/Comparer_get_Default.cs | 161 +++++++++ .../Comparer_get_Default.csproj | 9 + 28 files changed, 566 insertions(+), 167 deletions(-) create mode 100644 src/tests/JIT/opt/Devirtualization/Comparer_get_Default.cs create mode 100644 src/tests/JIT/opt/Devirtualization/Comparer_get_Default.csproj diff --git a/src/coreclr/System.Private.CoreLib/src/System/Collections/Generic/Comparer.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Collections/Generic/Comparer.CoreCLR.cs index 1af5fca470232d..a2790cbd121ded 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Collections/Generic/Comparer.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Collections/Generic/Comparer.CoreCLR.cs @@ -10,7 +10,7 @@ public abstract partial class Comparer : IComparer, IComparer { // To minimize generic instantiation overhead of creating the comparer per type, we keep the generic portion of the code as small // as possible and define most of the creation logic in a non-generic class. - public static Comparer Default { get; } = (Comparer)ComparerHelpers.CreateDefaultComparer(typeof(T)); + public static Comparer Default { [Intrinsic] get; } = (Comparer)ComparerHelpers.CreateDefaultComparer(typeof(T)); } internal sealed partial class EnumComparer : Comparer where T : struct, Enum diff --git a/src/coreclr/ToolBox/superpmi/superpmi-shared/lwmlist.h b/src/coreclr/ToolBox/superpmi/superpmi-shared/lwmlist.h index 18948eafc4ce41..961514d847dc8e 100644 --- a/src/coreclr/ToolBox/superpmi/superpmi-shared/lwmlist.h +++ b/src/coreclr/ToolBox/superpmi/superpmi-shared/lwmlist.h @@ -74,6 +74,7 @@ LWM(GetClassSize, DWORDLONG, DWORD) LWM(GetHeapClassSize, DWORDLONG, DWORD) LWM(CanAllocateOnStack, DWORDLONG, DWORD) LWM(GetCookieForPInvokeCalliSig, GetCookieForPInvokeCalliSigValue, DLDL) +LWM(GetDefaultComparerClass, DWORDLONG, DWORDLONG) LWM(GetDefaultEqualityComparerClass, DWORDLONG, DWORDLONG) LWM(GetDelegateCtor, Agnostic_GetDelegateCtorIn, Agnostic_GetDelegateCtorOut) LWM(GetEEInfo, DWORD, Agnostic_CORINFO_EE_INFO) diff --git a/src/coreclr/ToolBox/superpmi/superpmi-shared/methodcontext.cpp b/src/coreclr/ToolBox/superpmi/superpmi-shared/methodcontext.cpp index 3b57ce0aa9c5aa..38d954f7d3b7d7 100644 --- a/src/coreclr/ToolBox/superpmi/superpmi-shared/methodcontext.cpp +++ b/src/coreclr/ToolBox/superpmi/superpmi-shared/methodcontext.cpp @@ -3101,6 +3101,23 @@ CORINFO_METHOD_HANDLE MethodContext::repGetUnboxedEntry(CORINFO_METHOD_HANDLE ft return (CORINFO_METHOD_HANDLE)(result.A); } +void MethodContext::recGetDefaultComparerClass(CORINFO_CLASS_HANDLE cls, CORINFO_CLASS_HANDLE result) +{ + if (GetDefaultComparerClass == nullptr) + GetDefaultComparerClass = new LightWeightMap(); + + GetDefaultComparerClass->Add(CastHandle(cls), CastHandle(result)); +} +void MethodContext::dmpGetDefaultComparerClass(DWORDLONG key, DWORDLONG value) +{ + printf("GetDefaultComparerClass key cls-%016llX, value cls-%016llX", key, value); +} +CORINFO_CLASS_HANDLE MethodContext::repGetDefaultComparerClass(CORINFO_CLASS_HANDLE cls) +{ + CORINFO_CLASS_HANDLE result = (CORINFO_CLASS_HANDLE)GetDefaultComparerClass->Get(CastHandle(cls)); + return result; +} + void MethodContext::recGetDefaultEqualityComparerClass(CORINFO_CLASS_HANDLE cls, CORINFO_CLASS_HANDLE result) { if (GetDefaultEqualityComparerClass == nullptr) diff --git a/src/coreclr/ToolBox/superpmi/superpmi-shared/methodcontext.h b/src/coreclr/ToolBox/superpmi/superpmi-shared/methodcontext.h index 0bf0cfaa8ef28d..d45b4df3672aba 100644 --- a/src/coreclr/ToolBox/superpmi/superpmi-shared/methodcontext.h +++ b/src/coreclr/ToolBox/superpmi/superpmi-shared/methodcontext.h @@ -401,6 +401,10 @@ class MethodContext void dmpGetUnboxedEntry(DWORDLONG key, DLD value); CORINFO_METHOD_HANDLE repGetUnboxedEntry(CORINFO_METHOD_HANDLE ftn, bool* requiresInstMethodTableArg); + void recGetDefaultComparerClass(CORINFO_CLASS_HANDLE cls, CORINFO_CLASS_HANDLE result); + void dmpGetDefaultComparerClass(DWORDLONG key, DWORDLONG value); + CORINFO_CLASS_HANDLE repGetDefaultComparerClass(CORINFO_CLASS_HANDLE cls); + void recGetDefaultEqualityComparerClass(CORINFO_CLASS_HANDLE cls, CORINFO_CLASS_HANDLE result); void dmpGetDefaultEqualityComparerClass(DWORDLONG key, DWORDLONG value); CORINFO_CLASS_HANDLE repGetDefaultEqualityComparerClass(CORINFO_CLASS_HANDLE cls); @@ -867,7 +871,7 @@ class MethodContext }; // ********************* Please keep this up-to-date to ease adding more *************** -// Highest packet number: 187 +// Highest packet number: 188 // ************************************************************************************* enum mcPackets { @@ -943,6 +947,7 @@ enum mcPackets Packet_GetIntConfigValue = 151, // Added 2/12/2015 Packet_GetStringConfigValue = 152, // Added 2/12/2015 Packet_GetCookieForPInvokeCalliSig = 48, + Packet_GetDefaultComparerClass = 188, // Added 2/10/2021 Packet_GetDefaultEqualityComparerClass = 162, // Added 9/24/2017 Packet_GetDelegateCtor = 49, Packet_GetEEInfo = 50, diff --git a/src/coreclr/ToolBox/superpmi/superpmi-shim-collector/icorjitinfo.cpp b/src/coreclr/ToolBox/superpmi/superpmi-shim-collector/icorjitinfo.cpp index 6f07edb6a9dc48..045037565d469e 100644 --- a/src/coreclr/ToolBox/superpmi/superpmi-shim-collector/icorjitinfo.cpp +++ b/src/coreclr/ToolBox/superpmi/superpmi-shim-collector/icorjitinfo.cpp @@ -252,6 +252,16 @@ CORINFO_METHOD_HANDLE interceptor_ICJI::getUnboxedEntry(CORINFO_METHOD_HANDLE ft return result; } +// Given T, return the type of the default Comparer. +// Returns null if the type can't be determined exactly. +CORINFO_CLASS_HANDLE interceptor_ICJI::getDefaultComparerClass(CORINFO_CLASS_HANDLE cls) +{ + mc->cr->AddCall("getDefaultComparerClass"); + CORINFO_CLASS_HANDLE result = original_ICorJitInfo->getDefaultComparerClass(cls); + mc->recGetDefaultComparerClass(cls, result); + return result; +} + // Given T, return the type of the default EqualityComparer. // Returns null if the type can't be determined exactly. CORINFO_CLASS_HANDLE interceptor_ICJI::getDefaultEqualityComparerClass(CORINFO_CLASS_HANDLE cls) diff --git a/src/coreclr/ToolBox/superpmi/superpmi-shim-counter/icorjitinfo.cpp b/src/coreclr/ToolBox/superpmi/superpmi-shim-counter/icorjitinfo.cpp index 6945b6d74d38df..1f832798ce34f9 100644 --- a/src/coreclr/ToolBox/superpmi/superpmi-shim-counter/icorjitinfo.cpp +++ b/src/coreclr/ToolBox/superpmi/superpmi-shim-counter/icorjitinfo.cpp @@ -132,6 +132,13 @@ CORINFO_METHOD_HANDLE interceptor_ICJI::getUnboxedEntry( return original_ICorJitInfo->getUnboxedEntry(ftn, requiresInstMethodTableArg); } +CORINFO_CLASS_HANDLE interceptor_ICJI::getDefaultComparerClass( + CORINFO_CLASS_HANDLE elemType) +{ + mcs->AddCall("getDefaultComparerClass"); + return original_ICorJitInfo->getDefaultComparerClass(elemType); +} + CORINFO_CLASS_HANDLE interceptor_ICJI::getDefaultEqualityComparerClass( CORINFO_CLASS_HANDLE elemType) { diff --git a/src/coreclr/ToolBox/superpmi/superpmi-shim-simple/icorjitinfo.cpp b/src/coreclr/ToolBox/superpmi/superpmi-shim-simple/icorjitinfo.cpp index 1d73a9759c6004..373b21ffa5219d 100644 --- a/src/coreclr/ToolBox/superpmi/superpmi-shim-simple/icorjitinfo.cpp +++ b/src/coreclr/ToolBox/superpmi/superpmi-shim-simple/icorjitinfo.cpp @@ -118,6 +118,12 @@ CORINFO_METHOD_HANDLE interceptor_ICJI::getUnboxedEntry( return original_ICorJitInfo->getUnboxedEntry(ftn, requiresInstMethodTableArg); } +CORINFO_CLASS_HANDLE interceptor_ICJI::getDefaultComparerClass( + CORINFO_CLASS_HANDLE elemType) +{ + return original_ICorJitInfo->getDefaultComparerClass(elemType); +} + CORINFO_CLASS_HANDLE interceptor_ICJI::getDefaultEqualityComparerClass( CORINFO_CLASS_HANDLE elemType) { diff --git a/src/coreclr/ToolBox/superpmi/superpmi/icorjitinfo.cpp b/src/coreclr/ToolBox/superpmi/superpmi/icorjitinfo.cpp index 5e85ded0486d81..6e5086b0365777 100644 --- a/src/coreclr/ToolBox/superpmi/superpmi/icorjitinfo.cpp +++ b/src/coreclr/ToolBox/superpmi/superpmi/icorjitinfo.cpp @@ -186,6 +186,15 @@ CORINFO_METHOD_HANDLE MyICJI::getUnboxedEntry(CORINFO_METHOD_HANDLE ftn, bool* r return result; } +// Given T, return the type of the default Comparer. +// Returns null if the type can't be determined exactly. +CORINFO_CLASS_HANDLE MyICJI::getDefaultComparerClass(CORINFO_CLASS_HANDLE cls) +{ + jitInstance->mc->cr->AddCall("getDefaultComparerClass"); + CORINFO_CLASS_HANDLE result = jitInstance->mc->repGetDefaultComparerClass(cls); + return result; +} + // Given T, return the type of the default EqualityComparer. // Returns null if the type can't be determined exactly. CORINFO_CLASS_HANDLE MyICJI::getDefaultEqualityComparerClass(CORINFO_CLASS_HANDLE cls) diff --git a/src/coreclr/inc/corinfo.h b/src/coreclr/inc/corinfo.h index 97e044fc8951b2..6d2e881f628e80 100644 --- a/src/coreclr/inc/corinfo.h +++ b/src/coreclr/inc/corinfo.h @@ -2057,6 +2057,12 @@ class ICorStaticInfo bool* requiresInstMethodTableArg ) = 0; + // Given T, return the type of the default Comparer. + // Returns null if the type can't be determined exactly. + virtual CORINFO_CLASS_HANDLE getDefaultComparerClass( + CORINFO_CLASS_HANDLE elemType + ) = 0; + // Given T, return the type of the default EqualityComparer. // Returns null if the type can't be determined exactly. virtual CORINFO_CLASS_HANDLE getDefaultEqualityComparerClass( diff --git a/src/coreclr/inc/icorjitinfoimpl_generated.h b/src/coreclr/inc/icorjitinfoimpl_generated.h index 20219a094d659b..e02a50993b551b 100644 --- a/src/coreclr/inc/icorjitinfoimpl_generated.h +++ b/src/coreclr/inc/icorjitinfoimpl_generated.h @@ -85,6 +85,9 @@ CORINFO_METHOD_HANDLE getUnboxedEntry( CORINFO_METHOD_HANDLE ftn, bool* requiresInstMethodTableArg) override; +CORINFO_CLASS_HANDLE getDefaultComparerClass( + CORINFO_CLASS_HANDLE elemType) override; + CORINFO_CLASS_HANDLE getDefaultEqualityComparerClass( CORINFO_CLASS_HANDLE elemType) override; diff --git a/src/coreclr/inc/jiteeversionguid.h b/src/coreclr/inc/jiteeversionguid.h index 6c2aeea227b10b..8cc074bf578377 100644 --- a/src/coreclr/inc/jiteeversionguid.h +++ b/src/coreclr/inc/jiteeversionguid.h @@ -32,11 +32,11 @@ ////////////////////////////////////////////////////////////////////////////////////////////////////////// // -constexpr GUID JITEEVersionIdentifier = { /* 20017875-6552-4375-80A8-F7E2CFC6AAB7 */ - 0x20017875, - 0x6552, - 0x4375, - { 0x80, 0xa8, 0xf7, 0xe2, 0xcf, 0xc6, 0xaa, 0xb7 } +constexpr GUID JITEEVersionIdentifier = { /* 6ca59d19-13a4-44f7-a184-e9cd1e8f57f8 */ + 0x6ca59d19, + 0x13a4, + 0x44f7, + { 0xa1, 0x84, 0xe9, 0xcd, 0x1e, 0x8f, 0x57, 0xf8 } }; ////////////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/src/coreclr/jit/ICorJitInfo_API_names.h b/src/coreclr/jit/ICorJitInfo_API_names.h index 1c74f8b96aed79..2e1e67cc24a773 100644 --- a/src/coreclr/jit/ICorJitInfo_API_names.h +++ b/src/coreclr/jit/ICorJitInfo_API_names.h @@ -18,6 +18,7 @@ DEF_CLR_API(getMethodModule) DEF_CLR_API(getMethodVTableOffset) DEF_CLR_API(resolveVirtualMethod) DEF_CLR_API(getUnboxedEntry) +DEF_CLR_API(getDefaultComparerClass) DEF_CLR_API(getDefaultEqualityComparerClass) DEF_CLR_API(expandRawHandleIntrinsic) DEF_CLR_API(getIntrinsicID) diff --git a/src/coreclr/jit/ICorJitInfo_API_wrapper.hpp b/src/coreclr/jit/ICorJitInfo_API_wrapper.hpp index 42d2b55b7303d9..ccae8f7096dc88 100644 --- a/src/coreclr/jit/ICorJitInfo_API_wrapper.hpp +++ b/src/coreclr/jit/ICorJitInfo_API_wrapper.hpp @@ -154,6 +154,15 @@ CORINFO_METHOD_HANDLE WrapICorJitInfo::getUnboxedEntry( return temp; } +CORINFO_CLASS_HANDLE WrapICorJitInfo::getDefaultComparerClass( + CORINFO_CLASS_HANDLE elemType) +{ + API_ENTER(getDefaultComparerClass); + CORINFO_CLASS_HANDLE temp = wrapHnd->getDefaultComparerClass(elemType); + API_LEAVE(getDefaultComparerClass); + return temp; +} + CORINFO_CLASS_HANDLE WrapICorJitInfo::getDefaultEqualityComparerClass( CORINFO_CLASS_HANDLE elemType) { diff --git a/src/coreclr/jit/flowgraph.cpp b/src/coreclr/jit/flowgraph.cpp index 594a59af14ef4d..4a40055971f058 100644 --- a/src/coreclr/jit/flowgraph.cpp +++ b/src/coreclr/jit/flowgraph.cpp @@ -932,7 +932,8 @@ GenTreeCall* Compiler::fgGetStaticsCCtorHelper(CORINFO_CLASS_HANDLE cls, CorInfo if ((info.compFlags & CORINFO_FLG_JIT_INTRINSIC) != 0) { NamedIntrinsic ni = lookupNamedIntrinsic(info.compMethodHnd); - if (ni == NI_System_Collections_Generic_EqualityComparer_get_Default) + if ((ni == NI_System_Collections_Generic_EqualityComparer_get_Default) || + (ni == NI_System_Collections_Generic_Comparer_get_Default)) { JITDUMP("\nmarking helper call [%06u] as special dce...\n", result->gtTreeID); result->gtCallMoreFlags |= GTF_CALL_M_HELPER_SPECIAL_DCE; diff --git a/src/coreclr/jit/importer.cpp b/src/coreclr/jit/importer.cpp index b8e9cbbba00586..d1f12fc24fbc60 100644 --- a/src/coreclr/jit/importer.cpp +++ b/src/coreclr/jit/importer.cpp @@ -4458,6 +4458,7 @@ GenTree* Compiler::impIntrinsic(GenTree* newobjThis, break; } + case NI_System_Collections_Generic_Comparer_get_Default: case NI_System_Collections_Generic_EqualityComparer_get_Default: { // Flag for later handling during devirtualization. @@ -4943,6 +4944,10 @@ NamedIntrinsic Compiler::lookupNamedIntrinsic(CORINFO_METHOD_HANDLE method) { result = NI_System_Collections_Generic_EqualityComparer_get_Default; } + else if ((strcmp(className, "Comparer`1") == 0) && (strcmp(methodName, "get_Default") == 0)) + { + result = NI_System_Collections_Generic_Comparer_get_Default; + } } else if ((strcmp(namespaceName, "System.Numerics") == 0) && (strcmp(className, "BitOperations") == 0)) { @@ -21473,6 +21478,7 @@ CORINFO_CLASS_HANDLE Compiler::impGetSpecialIntrinsicExactReturnType(CORINFO_MET const NamedIntrinsic ni = lookupNamedIntrinsic(methodHnd); switch (ni) { + case NI_System_Collections_Generic_Comparer_get_Default: case NI_System_Collections_Generic_EqualityComparer_get_Default: { // Expect one class generic parameter; figure out which it is. @@ -21494,7 +21500,15 @@ CORINFO_CLASS_HANDLE Compiler::impGetSpecialIntrinsicExactReturnType(CORINFO_MET if (isFinalType) { - result = info.compCompHnd->getDefaultEqualityComparerClass(typeHnd); + if (ni == NI_System_Collections_Generic_EqualityComparer_get_Default) + { + result = info.compCompHnd->getDefaultEqualityComparerClass(typeHnd); + } + else + { + assert(ni == NI_System_Collections_Generic_Comparer_get_Default); + result = info.compCompHnd->getDefaultComparerClass(typeHnd); + } JITDUMP("Special intrinsic for type %s: return type is %s\n", eeGetClassName(typeHnd), result != nullptr ? eeGetClassName(result) : "unknown"); } diff --git a/src/coreclr/jit/namedintrinsiclist.h b/src/coreclr/jit/namedintrinsiclist.h index 79214e2d395959..06122ddb30a237 100644 --- a/src/coreclr/jit/namedintrinsiclist.h +++ b/src/coreclr/jit/namedintrinsiclist.h @@ -42,6 +42,7 @@ enum NamedIntrinsic : unsigned short NI_System_Math_Tanh, NI_SYSTEM_MATH_END, + NI_System_Collections_Generic_Comparer_get_Default, NI_System_Collections_Generic_EqualityComparer_get_Default, NI_System_Buffers_Binary_BinaryPrimitives_ReverseEndianness, NI_System_Numerics_BitOperations_PopCount, diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoBase.cs b/src/coreclr/tools/Common/JitInterface/CorInfoBase.cs index 3fc35919e9626c..80fc5e6ea34a65 100644 --- a/src/coreclr/tools/Common/JitInterface/CorInfoBase.cs +++ b/src/coreclr/tools/Common/JitInterface/CorInfoBase.cs @@ -216,6 +216,21 @@ static byte _resolveVirtualMethod(IntPtr thisHandle, IntPtr* ppException, CORINF } } + [UnmanagedCallersOnly] + static CORINFO_CLASS_STRUCT_* _getDefaultComparerClass(IntPtr thisHandle, IntPtr* ppException, CORINFO_CLASS_STRUCT_* elemType) + { + var _this = GetThis(thisHandle); + try + { + return _this.getDefaultComparerClass(elemType); + } + catch (Exception ex) + { + *ppException = _this.AllocException(ex); + return default; + } + } + [UnmanagedCallersOnly] static CORINFO_CLASS_STRUCT_* _getDefaultEqualityComparerClass(IntPtr thisHandle, IntPtr* ppException, CORINFO_CLASS_STRUCT_* elemType) { @@ -2536,7 +2551,7 @@ static uint _getJitFlags(IntPtr thisHandle, IntPtr* ppException, CORJIT_FLAGS* f static IntPtr GetUnmanagedCallbacks() { - void** callbacks = (void**)Marshal.AllocCoTaskMem(sizeof(IntPtr) * 171); + void** callbacks = (void**)Marshal.AllocCoTaskMem(sizeof(IntPtr) * 172); callbacks[0] = (delegate* unmanaged)&_getMethodAttribs; callbacks[1] = (delegate* unmanaged)&_setMethodAttribs; @@ -2552,163 +2567,164 @@ static IntPtr GetUnmanagedCallbacks() callbacks[11] = (delegate* unmanaged)&_getMethodVTableOffset; callbacks[12] = (delegate* unmanaged)&_resolveVirtualMethod; callbacks[13] = (delegate* unmanaged)&_getUnboxedEntry; - callbacks[14] = (delegate* unmanaged)&_getDefaultEqualityComparerClass; - callbacks[15] = (delegate* unmanaged)&_expandRawHandleIntrinsic; - callbacks[16] = (delegate* unmanaged)&_getIntrinsicID; - callbacks[17] = (delegate* unmanaged)&_isIntrinsicType; - callbacks[18] = (delegate* unmanaged)&_getUnmanagedCallConv; - callbacks[19] = (delegate* unmanaged)&_pInvokeMarshalingRequired; - callbacks[20] = (delegate* unmanaged)&_satisfiesMethodConstraints; - callbacks[21] = (delegate* unmanaged)&_isCompatibleDelegate; - callbacks[22] = (delegate* unmanaged)&_methodMustBeLoadedBeforeCodeIsRun; - callbacks[23] = (delegate* unmanaged)&_mapMethodDeclToMethodImpl; - callbacks[24] = (delegate* unmanaged)&_getGSCookie; - callbacks[25] = (delegate* unmanaged)&_setPatchpointInfo; - callbacks[26] = (delegate* unmanaged)&_getOSRInfo; - callbacks[27] = (delegate* unmanaged)&_resolveToken; - callbacks[28] = (delegate* unmanaged)&_tryResolveToken; - callbacks[29] = (delegate* unmanaged)&_findSig; - callbacks[30] = (delegate* unmanaged)&_findCallSiteSig; - callbacks[31] = (delegate* unmanaged)&_getTokenTypeAsHandle; - callbacks[32] = (delegate* unmanaged)&_isValidToken; - callbacks[33] = (delegate* unmanaged)&_isValidStringRef; - callbacks[34] = (delegate* unmanaged)&_getStringLiteral; - callbacks[35] = (delegate* unmanaged)&_asCorInfoType; - callbacks[36] = (delegate* unmanaged)&_getClassName; - callbacks[37] = (delegate* unmanaged)&_getClassNameFromMetadata; - callbacks[38] = (delegate* unmanaged)&_getTypeInstantiationArgument; - callbacks[39] = (delegate* unmanaged)&_appendClassName; - callbacks[40] = (delegate* unmanaged)&_isValueClass; - callbacks[41] = (delegate* unmanaged)&_canInlineTypeCheck; - callbacks[42] = (delegate* unmanaged)&_getClassAttribs; - callbacks[43] = (delegate* unmanaged)&_isStructRequiringStackAllocRetBuf; - callbacks[44] = (delegate* unmanaged)&_getClassModule; - callbacks[45] = (delegate* unmanaged)&_getModuleAssembly; - callbacks[46] = (delegate* unmanaged)&_getAssemblyName; - callbacks[47] = (delegate* unmanaged)&_LongLifetimeMalloc; - callbacks[48] = (delegate* unmanaged)&_LongLifetimeFree; - callbacks[49] = (delegate* unmanaged)&_getClassModuleIdForStatics; - callbacks[50] = (delegate* unmanaged)&_getClassSize; - callbacks[51] = (delegate* unmanaged)&_getHeapClassSize; - callbacks[52] = (delegate* unmanaged)&_canAllocateOnStack; - callbacks[53] = (delegate* unmanaged)&_getClassAlignmentRequirement; - callbacks[54] = (delegate* unmanaged)&_getClassGClayout; - callbacks[55] = (delegate* unmanaged)&_getClassNumInstanceFields; - callbacks[56] = (delegate* unmanaged)&_getFieldInClass; - callbacks[57] = (delegate* unmanaged)&_checkMethodModifier; - callbacks[58] = (delegate* unmanaged)&_getNewHelper; - callbacks[59] = (delegate* unmanaged)&_getNewArrHelper; - callbacks[60] = (delegate* unmanaged)&_getCastingHelper; - callbacks[61] = (delegate* unmanaged)&_getSharedCCtorHelper; - callbacks[62] = (delegate* unmanaged)&_getTypeForBox; - callbacks[63] = (delegate* unmanaged)&_getBoxHelper; - callbacks[64] = (delegate* unmanaged)&_getUnBoxHelper; - callbacks[65] = (delegate* unmanaged)&_getReadyToRunHelper; - callbacks[66] = (delegate* unmanaged)&_getReadyToRunDelegateCtorHelper; - callbacks[67] = (delegate* unmanaged)&_getHelperName; - callbacks[68] = (delegate* unmanaged)&_initClass; - callbacks[69] = (delegate* unmanaged)&_classMustBeLoadedBeforeCodeIsRun; - callbacks[70] = (delegate* unmanaged)&_getBuiltinClass; - callbacks[71] = (delegate* unmanaged)&_getTypeForPrimitiveValueClass; - callbacks[72] = (delegate* unmanaged)&_getTypeForPrimitiveNumericClass; - callbacks[73] = (delegate* unmanaged)&_canCast; - callbacks[74] = (delegate* unmanaged)&_areTypesEquivalent; - callbacks[75] = (delegate* unmanaged)&_compareTypesForCast; - callbacks[76] = (delegate* unmanaged)&_compareTypesForEquality; - callbacks[77] = (delegate* unmanaged)&_mergeClasses; - callbacks[78] = (delegate* unmanaged)&_isMoreSpecificType; - callbacks[79] = (delegate* unmanaged)&_getParentType; - callbacks[80] = (delegate* unmanaged)&_getChildType; - callbacks[81] = (delegate* unmanaged)&_satisfiesClassConstraints; - callbacks[82] = (delegate* unmanaged)&_isSDArray; - callbacks[83] = (delegate* unmanaged)&_getArrayRank; - callbacks[84] = (delegate* unmanaged)&_getArrayInitializationData; - callbacks[85] = (delegate* unmanaged)&_canAccessClass; - callbacks[86] = (delegate* unmanaged)&_getFieldName; - callbacks[87] = (delegate* unmanaged)&_getFieldClass; - callbacks[88] = (delegate* unmanaged)&_getFieldType; - callbacks[89] = (delegate* unmanaged)&_getFieldOffset; - callbacks[90] = (delegate* unmanaged)&_getFieldInfo; - callbacks[91] = (delegate* unmanaged)&_isFieldStatic; - callbacks[92] = (delegate* unmanaged)&_getBoundaries; - callbacks[93] = (delegate* unmanaged)&_setBoundaries; - callbacks[94] = (delegate* unmanaged)&_getVars; - callbacks[95] = (delegate* unmanaged)&_setVars; - callbacks[96] = (delegate* unmanaged)&_allocateArray; - callbacks[97] = (delegate* unmanaged)&_freeArray; - callbacks[98] = (delegate* unmanaged)&_getArgNext; - callbacks[99] = (delegate* unmanaged)&_getArgType; - callbacks[100] = (delegate* unmanaged)&_getArgClass; - callbacks[101] = (delegate* unmanaged)&_getHFAType; - callbacks[102] = (delegate* unmanaged)&_GetErrorHRESULT; - callbacks[103] = (delegate* unmanaged)&_GetErrorMessage; - callbacks[104] = (delegate* unmanaged)&_FilterException; - callbacks[105] = (delegate* unmanaged)&_HandleException; - callbacks[106] = (delegate* unmanaged)&_ThrowExceptionForJitResult; - callbacks[107] = (delegate* unmanaged)&_ThrowExceptionForHelper; - callbacks[108] = (delegate* unmanaged)&_runWithErrorTrap; - callbacks[109] = (delegate* unmanaged)&_getEEInfo; - callbacks[110] = (delegate* unmanaged)&_getJitTimeLogFilename; - callbacks[111] = (delegate* unmanaged)&_getMethodDefFromMethod; - callbacks[112] = (delegate* unmanaged)&_getMethodName; - callbacks[113] = (delegate* unmanaged)&_getMethodNameFromMetadata; - callbacks[114] = (delegate* unmanaged)&_getMethodHash; - callbacks[115] = (delegate* unmanaged)&_findNameOfToken; - callbacks[116] = (delegate* unmanaged)&_getSystemVAmd64PassStructInRegisterDescriptor; - callbacks[117] = (delegate* unmanaged)&_getThreadTLSIndex; - callbacks[118] = (delegate* unmanaged)&_getInlinedCallFrameVptr; - callbacks[119] = (delegate* unmanaged)&_getAddrOfCaptureThreadGlobal; - callbacks[120] = (delegate* unmanaged)&_getHelperFtn; - callbacks[121] = (delegate* unmanaged)&_getFunctionEntryPoint; - callbacks[122] = (delegate* unmanaged)&_getFunctionFixedEntryPoint; - callbacks[123] = (delegate* unmanaged)&_getMethodSync; - callbacks[124] = (delegate* unmanaged)&_getLazyStringLiteralHelper; - callbacks[125] = (delegate* unmanaged)&_embedModuleHandle; - callbacks[126] = (delegate* unmanaged)&_embedClassHandle; - callbacks[127] = (delegate* unmanaged)&_embedMethodHandle; - callbacks[128] = (delegate* unmanaged)&_embedFieldHandle; - callbacks[129] = (delegate* unmanaged)&_embedGenericHandle; - callbacks[130] = (delegate* unmanaged)&_getLocationOfThisType; - callbacks[131] = (delegate* unmanaged)&_getAddressOfPInvokeTarget; - callbacks[132] = (delegate* unmanaged)&_GetCookieForPInvokeCalliSig; - callbacks[133] = (delegate* unmanaged)&_canGetCookieForPInvokeCalliSig; - callbacks[134] = (delegate* unmanaged)&_getJustMyCodeHandle; - callbacks[135] = (delegate* unmanaged)&_GetProfilingHandle; - callbacks[136] = (delegate* unmanaged)&_getCallInfo; - callbacks[137] = (delegate* unmanaged)&_canAccessFamily; - callbacks[138] = (delegate* unmanaged)&_isRIDClassDomainID; - callbacks[139] = (delegate* unmanaged)&_getClassDomainID; - callbacks[140] = (delegate* unmanaged)&_getFieldAddress; - callbacks[141] = (delegate* unmanaged)&_getStaticFieldCurrentClass; - callbacks[142] = (delegate* unmanaged)&_getVarArgsHandle; - callbacks[143] = (delegate* unmanaged)&_canGetVarArgsHandle; - callbacks[144] = (delegate* unmanaged)&_constructStringLiteral; - callbacks[145] = (delegate* unmanaged)&_emptyStringLiteral; - callbacks[146] = (delegate* unmanaged)&_getFieldThreadLocalStoreID; - callbacks[147] = (delegate* unmanaged)&_setOverride; - callbacks[148] = (delegate* unmanaged)&_addActiveDependency; - callbacks[149] = (delegate* unmanaged)&_GetDelegateCtor; - callbacks[150] = (delegate* unmanaged)&_MethodCompileComplete; - callbacks[151] = (delegate* unmanaged)&_getTailCallHelpers; - callbacks[152] = (delegate* unmanaged)&_convertPInvokeCalliToCall; - callbacks[153] = (delegate* unmanaged)&_notifyInstructionSetUsage; - callbacks[154] = (delegate* unmanaged)&_allocMem; - callbacks[155] = (delegate* unmanaged)&_reserveUnwindInfo; - callbacks[156] = (delegate* unmanaged)&_allocUnwindInfo; - callbacks[157] = (delegate* unmanaged)&_allocGCInfo; - callbacks[158] = (delegate* unmanaged)&_setEHcount; - callbacks[159] = (delegate* unmanaged)&_setEHinfo; - callbacks[160] = (delegate* unmanaged)&_logMsg; - callbacks[161] = (delegate* unmanaged)&_doAssert; - callbacks[162] = (delegate* unmanaged)&_reportFatalError; - callbacks[163] = (delegate* unmanaged)&_getPgoInstrumentationResults; - callbacks[164] = (delegate* unmanaged)&_allocPgoInstrumentationBySchema; - callbacks[165] = (delegate* unmanaged)&_getLikelyClass; - callbacks[166] = (delegate* unmanaged)&_recordCallSite; - callbacks[167] = (delegate* unmanaged)&_recordRelocation; - callbacks[168] = (delegate* unmanaged)&_getRelocTypeHint; - callbacks[169] = (delegate* unmanaged)&_getExpectedTargetArchitecture; - callbacks[170] = (delegate* unmanaged)&_getJitFlags; + callbacks[14] = (delegate* unmanaged)&_getDefaultComparerClass; + callbacks[15] = (delegate* unmanaged)&_getDefaultEqualityComparerClass; + callbacks[16] = (delegate* unmanaged)&_expandRawHandleIntrinsic; + callbacks[17] = (delegate* unmanaged)&_getIntrinsicID; + callbacks[18] = (delegate* unmanaged)&_isIntrinsicType; + callbacks[19] = (delegate* unmanaged)&_getUnmanagedCallConv; + callbacks[20] = (delegate* unmanaged)&_pInvokeMarshalingRequired; + callbacks[21] = (delegate* unmanaged)&_satisfiesMethodConstraints; + callbacks[22] = (delegate* unmanaged)&_isCompatibleDelegate; + callbacks[23] = (delegate* unmanaged)&_methodMustBeLoadedBeforeCodeIsRun; + callbacks[24] = (delegate* unmanaged)&_mapMethodDeclToMethodImpl; + callbacks[25] = (delegate* unmanaged)&_getGSCookie; + callbacks[26] = (delegate* unmanaged)&_setPatchpointInfo; + callbacks[27] = (delegate* unmanaged)&_getOSRInfo; + callbacks[28] = (delegate* unmanaged)&_resolveToken; + callbacks[29] = (delegate* unmanaged)&_tryResolveToken; + callbacks[30] = (delegate* unmanaged)&_findSig; + callbacks[31] = (delegate* unmanaged)&_findCallSiteSig; + callbacks[32] = (delegate* unmanaged)&_getTokenTypeAsHandle; + callbacks[33] = (delegate* unmanaged)&_isValidToken; + callbacks[34] = (delegate* unmanaged)&_isValidStringRef; + callbacks[35] = (delegate* unmanaged)&_getStringLiteral; + callbacks[36] = (delegate* unmanaged)&_asCorInfoType; + callbacks[37] = (delegate* unmanaged)&_getClassName; + callbacks[38] = (delegate* unmanaged)&_getClassNameFromMetadata; + callbacks[39] = (delegate* unmanaged)&_getTypeInstantiationArgument; + callbacks[40] = (delegate* unmanaged)&_appendClassName; + callbacks[41] = (delegate* unmanaged)&_isValueClass; + callbacks[42] = (delegate* unmanaged)&_canInlineTypeCheck; + callbacks[43] = (delegate* unmanaged)&_getClassAttribs; + callbacks[44] = (delegate* unmanaged)&_isStructRequiringStackAllocRetBuf; + callbacks[45] = (delegate* unmanaged)&_getClassModule; + callbacks[46] = (delegate* unmanaged)&_getModuleAssembly; + callbacks[47] = (delegate* unmanaged)&_getAssemblyName; + callbacks[48] = (delegate* unmanaged)&_LongLifetimeMalloc; + callbacks[49] = (delegate* unmanaged)&_LongLifetimeFree; + callbacks[50] = (delegate* unmanaged)&_getClassModuleIdForStatics; + callbacks[51] = (delegate* unmanaged)&_getClassSize; + callbacks[52] = (delegate* unmanaged)&_getHeapClassSize; + callbacks[53] = (delegate* unmanaged)&_canAllocateOnStack; + callbacks[54] = (delegate* unmanaged)&_getClassAlignmentRequirement; + callbacks[55] = (delegate* unmanaged)&_getClassGClayout; + callbacks[56] = (delegate* unmanaged)&_getClassNumInstanceFields; + callbacks[57] = (delegate* unmanaged)&_getFieldInClass; + callbacks[58] = (delegate* unmanaged)&_checkMethodModifier; + callbacks[59] = (delegate* unmanaged)&_getNewHelper; + callbacks[60] = (delegate* unmanaged)&_getNewArrHelper; + callbacks[61] = (delegate* unmanaged)&_getCastingHelper; + callbacks[62] = (delegate* unmanaged)&_getSharedCCtorHelper; + callbacks[63] = (delegate* unmanaged)&_getTypeForBox; + callbacks[64] = (delegate* unmanaged)&_getBoxHelper; + callbacks[65] = (delegate* unmanaged)&_getUnBoxHelper; + callbacks[66] = (delegate* unmanaged)&_getReadyToRunHelper; + callbacks[67] = (delegate* unmanaged)&_getReadyToRunDelegateCtorHelper; + callbacks[68] = (delegate* unmanaged)&_getHelperName; + callbacks[69] = (delegate* unmanaged)&_initClass; + callbacks[70] = (delegate* unmanaged)&_classMustBeLoadedBeforeCodeIsRun; + callbacks[71] = (delegate* unmanaged)&_getBuiltinClass; + callbacks[72] = (delegate* unmanaged)&_getTypeForPrimitiveValueClass; + callbacks[73] = (delegate* unmanaged)&_getTypeForPrimitiveNumericClass; + callbacks[74] = (delegate* unmanaged)&_canCast; + callbacks[75] = (delegate* unmanaged)&_areTypesEquivalent; + callbacks[76] = (delegate* unmanaged)&_compareTypesForCast; + callbacks[77] = (delegate* unmanaged)&_compareTypesForEquality; + callbacks[78] = (delegate* unmanaged)&_mergeClasses; + callbacks[79] = (delegate* unmanaged)&_isMoreSpecificType; + callbacks[80] = (delegate* unmanaged)&_getParentType; + callbacks[81] = (delegate* unmanaged)&_getChildType; + callbacks[82] = (delegate* unmanaged)&_satisfiesClassConstraints; + callbacks[83] = (delegate* unmanaged)&_isSDArray; + callbacks[84] = (delegate* unmanaged)&_getArrayRank; + callbacks[85] = (delegate* unmanaged)&_getArrayInitializationData; + callbacks[86] = (delegate* unmanaged)&_canAccessClass; + callbacks[87] = (delegate* unmanaged)&_getFieldName; + callbacks[88] = (delegate* unmanaged)&_getFieldClass; + callbacks[89] = (delegate* unmanaged)&_getFieldType; + callbacks[90] = (delegate* unmanaged)&_getFieldOffset; + callbacks[91] = (delegate* unmanaged)&_getFieldInfo; + callbacks[92] = (delegate* unmanaged)&_isFieldStatic; + callbacks[93] = (delegate* unmanaged)&_getBoundaries; + callbacks[94] = (delegate* unmanaged)&_setBoundaries; + callbacks[95] = (delegate* unmanaged)&_getVars; + callbacks[96] = (delegate* unmanaged)&_setVars; + callbacks[97] = (delegate* unmanaged)&_allocateArray; + callbacks[98] = (delegate* unmanaged)&_freeArray; + callbacks[99] = (delegate* unmanaged)&_getArgNext; + callbacks[100] = (delegate* unmanaged)&_getArgType; + callbacks[101] = (delegate* unmanaged)&_getArgClass; + callbacks[102] = (delegate* unmanaged)&_getHFAType; + callbacks[103] = (delegate* unmanaged)&_GetErrorHRESULT; + callbacks[104] = (delegate* unmanaged)&_GetErrorMessage; + callbacks[105] = (delegate* unmanaged)&_FilterException; + callbacks[106] = (delegate* unmanaged)&_HandleException; + callbacks[107] = (delegate* unmanaged)&_ThrowExceptionForJitResult; + callbacks[108] = (delegate* unmanaged)&_ThrowExceptionForHelper; + callbacks[109] = (delegate* unmanaged)&_runWithErrorTrap; + callbacks[110] = (delegate* unmanaged)&_getEEInfo; + callbacks[111] = (delegate* unmanaged)&_getJitTimeLogFilename; + callbacks[112] = (delegate* unmanaged)&_getMethodDefFromMethod; + callbacks[113] = (delegate* unmanaged)&_getMethodName; + callbacks[114] = (delegate* unmanaged)&_getMethodNameFromMetadata; + callbacks[115] = (delegate* unmanaged)&_getMethodHash; + callbacks[116] = (delegate* unmanaged)&_findNameOfToken; + callbacks[117] = (delegate* unmanaged)&_getSystemVAmd64PassStructInRegisterDescriptor; + callbacks[118] = (delegate* unmanaged)&_getThreadTLSIndex; + callbacks[119] = (delegate* unmanaged)&_getInlinedCallFrameVptr; + callbacks[120] = (delegate* unmanaged)&_getAddrOfCaptureThreadGlobal; + callbacks[121] = (delegate* unmanaged)&_getHelperFtn; + callbacks[122] = (delegate* unmanaged)&_getFunctionEntryPoint; + callbacks[123] = (delegate* unmanaged)&_getFunctionFixedEntryPoint; + callbacks[124] = (delegate* unmanaged)&_getMethodSync; + callbacks[125] = (delegate* unmanaged)&_getLazyStringLiteralHelper; + callbacks[126] = (delegate* unmanaged)&_embedModuleHandle; + callbacks[127] = (delegate* unmanaged)&_embedClassHandle; + callbacks[128] = (delegate* unmanaged)&_embedMethodHandle; + callbacks[129] = (delegate* unmanaged)&_embedFieldHandle; + callbacks[130] = (delegate* unmanaged)&_embedGenericHandle; + callbacks[131] = (delegate* unmanaged)&_getLocationOfThisType; + callbacks[132] = (delegate* unmanaged)&_getAddressOfPInvokeTarget; + callbacks[133] = (delegate* unmanaged)&_GetCookieForPInvokeCalliSig; + callbacks[134] = (delegate* unmanaged)&_canGetCookieForPInvokeCalliSig; + callbacks[135] = (delegate* unmanaged)&_getJustMyCodeHandle; + callbacks[136] = (delegate* unmanaged)&_GetProfilingHandle; + callbacks[137] = (delegate* unmanaged)&_getCallInfo; + callbacks[138] = (delegate* unmanaged)&_canAccessFamily; + callbacks[139] = (delegate* unmanaged)&_isRIDClassDomainID; + callbacks[140] = (delegate* unmanaged)&_getClassDomainID; + callbacks[141] = (delegate* unmanaged)&_getFieldAddress; + callbacks[142] = (delegate* unmanaged)&_getStaticFieldCurrentClass; + callbacks[143] = (delegate* unmanaged)&_getVarArgsHandle; + callbacks[144] = (delegate* unmanaged)&_canGetVarArgsHandle; + callbacks[145] = (delegate* unmanaged)&_constructStringLiteral; + callbacks[146] = (delegate* unmanaged)&_emptyStringLiteral; + callbacks[147] = (delegate* unmanaged)&_getFieldThreadLocalStoreID; + callbacks[148] = (delegate* unmanaged)&_setOverride; + callbacks[149] = (delegate* unmanaged)&_addActiveDependency; + callbacks[150] = (delegate* unmanaged)&_GetDelegateCtor; + callbacks[151] = (delegate* unmanaged)&_MethodCompileComplete; + callbacks[152] = (delegate* unmanaged)&_getTailCallHelpers; + callbacks[153] = (delegate* unmanaged)&_convertPInvokeCalliToCall; + callbacks[154] = (delegate* unmanaged)&_notifyInstructionSetUsage; + callbacks[155] = (delegate* unmanaged)&_allocMem; + callbacks[156] = (delegate* unmanaged)&_reserveUnwindInfo; + callbacks[157] = (delegate* unmanaged)&_allocUnwindInfo; + callbacks[158] = (delegate* unmanaged)&_allocGCInfo; + callbacks[159] = (delegate* unmanaged)&_setEHcount; + callbacks[160] = (delegate* unmanaged)&_setEHinfo; + callbacks[161] = (delegate* unmanaged)&_logMsg; + callbacks[162] = (delegate* unmanaged)&_doAssert; + callbacks[163] = (delegate* unmanaged)&_reportFatalError; + callbacks[164] = (delegate* unmanaged)&_getPgoInstrumentationResults; + callbacks[165] = (delegate* unmanaged)&_allocPgoInstrumentationBySchema; + callbacks[166] = (delegate* unmanaged)&_getLikelyClass; + callbacks[167] = (delegate* unmanaged)&_recordCallSite; + callbacks[168] = (delegate* unmanaged)&_recordRelocation; + callbacks[169] = (delegate* unmanaged)&_getRelocTypeHint; + callbacks[170] = (delegate* unmanaged)&_getExpectedTargetArchitecture; + callbacks[171] = (delegate* unmanaged)&_getJitFlags; return (IntPtr)callbacks; } diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs index 01cddcf9c3753f..bc56f2cda1fdc7 100644 --- a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs +++ b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs @@ -1056,6 +1056,13 @@ private bool resolveVirtualMethod(CORINFO_DEVIRTUALIZATION_INFO* info) return result != null ? ObjectToHandle(result) : null; } + private CORINFO_CLASS_STRUCT_* getDefaultComparerClass(CORINFO_CLASS_STRUCT_* elemType) + { + TypeDesc comparand = HandleToObject(elemType); + TypeDesc comparer = IL.Stubs.ComparerIntrinsics.GetComparerForType(comparand); + return comparer != null ? ObjectToHandle(comparer) : null; + } + private CORINFO_CLASS_STRUCT_* getDefaultEqualityComparerClass(CORINFO_CLASS_STRUCT_* elemType) { TypeDesc comparand = HandleToObject(elemType); diff --git a/src/coreclr/tools/Common/JitInterface/ThunkGenerator/ThunkInput.txt b/src/coreclr/tools/Common/JitInterface/ThunkGenerator/ThunkInput.txt index 1b6b4e8f259ca8..7824fae24b41ae 100644 --- a/src/coreclr/tools/Common/JitInterface/ThunkGenerator/ThunkInput.txt +++ b/src/coreclr/tools/Common/JitInterface/ThunkGenerator/ThunkInput.txt @@ -175,6 +175,7 @@ FUNCTIONS void getMethodVTableOffset( CORINFO_METHOD_HANDLE method, unsigned* offsetOfIndirection, unsigned* offsetAfterIndirection, bool* isRelative); bool resolveVirtualMethod(CORINFO_DEVIRTUALIZATION_INFO* info); CORINFO_METHOD_HANDLE getUnboxedEntry(CORINFO_METHOD_HANDLE ftn, bool* requiresInstMethodTableArg); + CORINFO_CLASS_HANDLE getDefaultComparerClass(CORINFO_CLASS_HANDLE elemType); CORINFO_CLASS_HANDLE getDefaultEqualityComparerClass(CORINFO_CLASS_HANDLE elemType); void expandRawHandleIntrinsic(CORINFO_RESOLVED_TOKEN * pResolvedToken, CORINFO_GENERICHANDLE_RESULT * pResult); CorInfoIntrinsics getIntrinsicID( CORINFO_METHOD_HANDLE method , BoolStar pMustExpand); diff --git a/src/coreclr/tools/Common/TypeSystem/IL/Stubs/ComparerIntrinsics.cs b/src/coreclr/tools/Common/TypeSystem/IL/Stubs/ComparerIntrinsics.cs index 7297b94279f431..5c089b18756843 100644 --- a/src/coreclr/tools/Common/TypeSystem/IL/Stubs/ComparerIntrinsics.cs +++ b/src/coreclr/tools/Common/TypeSystem/IL/Stubs/ComparerIntrinsics.cs @@ -31,6 +31,14 @@ public static MethodIL EmitEqualityComparerCreate(MethodDesc target) return EmitComparerAndEqualityComparerCreateCommon(target, "EqualityComparer", "IEquatable`1"); } + /// + /// Gets the concrete type Comparer`1.Create returns or null if it's not known at compile time. + /// + public static TypeDesc GetComparerForType(TypeDesc comparand) + { + return GetComparerForType(comparand, "Comparer", "IComparable`1"); + } + /// /// Gets the concrete type EqualityComparer`1.Create returns or null if it's not known at compile time. /// diff --git a/src/coreclr/tools/aot/jitinterface/jitinterface.h b/src/coreclr/tools/aot/jitinterface/jitinterface.h index 8d6df6f35572b0..3c1fe2d09fb66f 100644 --- a/src/coreclr/tools/aot/jitinterface/jitinterface.h +++ b/src/coreclr/tools/aot/jitinterface/jitinterface.h @@ -24,6 +24,7 @@ struct JitInterfaceCallbacks void (* getMethodVTableOffset)(void * thisHandle, CorInfoExceptionClass** ppException, void* method, unsigned* offsetOfIndirection, unsigned* offsetAfterIndirection, bool* isRelative); bool (* resolveVirtualMethod)(void * thisHandle, CorInfoExceptionClass** ppException, void* info); void* (* getUnboxedEntry)(void * thisHandle, CorInfoExceptionClass** ppException, void* ftn, bool* requiresInstMethodTableArg); + void* (* getDefaultComparerClass)(void * thisHandle, CorInfoExceptionClass** ppException, void* elemType); void* (* getDefaultEqualityComparerClass)(void * thisHandle, CorInfoExceptionClass** ppException, void* elemType); void (* expandRawHandleIntrinsic)(void * thisHandle, CorInfoExceptionClass** ppException, void* pResolvedToken, void* pResult); int (* getIntrinsicID)(void * thisHandle, CorInfoExceptionClass** ppException, void* method, bool* pMustExpand); @@ -338,6 +339,15 @@ class JitInterfaceWrapper return temp; } + virtual void* getDefaultComparerClass( + void* elemType) +{ + CorInfoExceptionClass* pException = nullptr; + void* temp = _callbacks->getDefaultComparerClass(_thisHandle, &pException, elemType); + if (pException != nullptr) throw pException; + return temp; +} + virtual void* getDefaultEqualityComparerClass( void* elemType) { diff --git a/src/coreclr/vm/corelib.h b/src/coreclr/vm/corelib.h index b34222a0939053..145045f03854d3 100644 --- a/src/coreclr/vm/corelib.h +++ b/src/coreclr/vm/corelib.h @@ -1189,6 +1189,12 @@ DEFINE_CLASS(NULLABLE_EQUALITYCOMPARER, CollectionsGeneric, NullableEqualityComp DEFINE_CLASS(GENERIC_EQUALITYCOMPARER, CollectionsGeneric, GenericEqualityComparer`1) DEFINE_CLASS(OBJECT_EQUALITYCOMPARER, CollectionsGeneric, ObjectEqualityComparer`1) +// Classes referenced in Comparer.Default optimization + +DEFINE_CLASS(GENERIC_COMPARER, CollectionsGeneric, GenericComparer`1) +DEFINE_CLASS(OBJECT_COMPARER, CollectionsGeneric, ObjectComparer`1) +DEFINE_CLASS(ENUM_COMPARER, CollectionsGeneric, EnumComparer`1) + DEFINE_CLASS(INATTRIBUTE, Interop, InAttribute) DEFINE_CLASS_U(CompilerServices, GCHeapHash, GCHeapHashObject) diff --git a/src/coreclr/vm/jitinterface.cpp b/src/coreclr/vm/jitinterface.cpp index b48fbbe43a0e84..9a0673cb1721e2 100644 --- a/src/coreclr/vm/jitinterface.cpp +++ b/src/coreclr/vm/jitinterface.cpp @@ -9082,6 +9082,86 @@ void CEEInfo::expandRawHandleIntrinsic( UNREACHABLE(); // only called with CoreRT. } +/*********************************************************************/ +CORINFO_CLASS_HANDLE CEEInfo::getDefaultComparerClass(CORINFO_CLASS_HANDLE elemType) +{ + CONTRACTL { + THROWS; + GC_TRIGGERS; + MODE_PREEMPTIVE; + } CONTRACTL_END; + + CORINFO_CLASS_HANDLE result = NULL; + + JIT_TO_EE_TRANSITION(); + + result = getDefaultComparerClassHelper(elemType); + + EE_TO_JIT_TRANSITION(); + + return result; +} + +CORINFO_CLASS_HANDLE CEEInfo::getDefaultComparerClassHelper(CORINFO_CLASS_HANDLE elemType) +{ + CONTRACTL { + THROWS; + GC_TRIGGERS; + MODE_PREEMPTIVE; + } CONTRACTL_END; + + // Mirrors the logic in BCL's CompareHelpers.CreateDefaultComparer + // And in compile.cpp's SpecializeComparer + TypeHandle elemTypeHnd(elemType); + + // Else we'll need to find the appropriate instantation + Instantiation inst(&elemTypeHnd, 1); + + // If T implements IEquatable + if (elemTypeHnd.CanCastTo(TypeHandle(CoreLibBinder::GetClass(CLASS__ICOMPARABLEGENERIC)).Instantiate(inst))) + { + TypeHandle resultTh = ((TypeHandle)CoreLibBinder::GetClass(CLASS__GENERIC_COMPARER)).Instantiate(inst); + return CORINFO_CLASS_HANDLE(resultTh.GetMethodTable()); + } + + // We need to special case the Enum comparers based on their underlying type to avoid boxing + if (elemTypeHnd.IsEnum()) + { + MethodTable* targetClass = NULL; + CorElementType normType = elemTypeHnd.GetVerifierCorElementType(); + + switch(normType) + { + case ELEMENT_TYPE_I1: + case ELEMENT_TYPE_I2: + case ELEMENT_TYPE_U1: + case ELEMENT_TYPE_U2: + case ELEMENT_TYPE_I4: + case ELEMENT_TYPE_U4: + case ELEMENT_TYPE_I8: + case ELEMENT_TYPE_U8: + { + targetClass = CoreLibBinder::GetClass(CLASS__ENUM_COMPARER); + break; + } + + default: + break; + } + + if (targetClass != NULL) + { + TypeHandle resultTh = ((TypeHandle)targetClass->GetCanonicalMethodTable()).Instantiate(inst); + return CORINFO_CLASS_HANDLE(resultTh.GetMethodTable()); + } + } + + // Default case + TypeHandle resultTh = ((TypeHandle)CoreLibBinder::GetClass(CLASS__OBJECT_COMPARER)).Instantiate(inst); + + return CORINFO_CLASS_HANDLE(resultTh.GetMethodTable()); +} + /*********************************************************************/ CORINFO_CLASS_HANDLE CEEInfo::getDefaultEqualityComparerClass(CORINFO_CLASS_HANDLE elemType) { diff --git a/src/coreclr/vm/jitinterface.h b/src/coreclr/vm/jitinterface.h index c19e8934a8699b..85723c4a78518b 100644 --- a/src/coreclr/vm/jitinterface.h +++ b/src/coreclr/vm/jitinterface.h @@ -452,6 +452,10 @@ class CEEInfo : public ICorJitInfo bool resolveVirtualMethodHelper(CORINFO_DEVIRTUALIZATION_INFO * info); + CORINFO_CLASS_HANDLE getDefaultComparerClassHelper( + CORINFO_CLASS_HANDLE elemType + ); + CORINFO_CLASS_HANDLE getDefaultEqualityComparerClassHelper( CORINFO_CLASS_HANDLE elemType ); diff --git a/src/coreclr/zap/zapinfo.cpp b/src/coreclr/zap/zapinfo.cpp index d49fe508fe8675..afa68c6f3e8710 100644 --- a/src/coreclr/zap/zapinfo.cpp +++ b/src/coreclr/zap/zapinfo.cpp @@ -4069,6 +4069,12 @@ CORINFO_METHOD_HANDLE ZapInfo::getUnboxedEntry( return m_pEEJitInfo->getUnboxedEntry(ftn, requiresInstMethodTableArg); } +CORINFO_CLASS_HANDLE ZapInfo::getDefaultComparerClass( + CORINFO_CLASS_HANDLE elemType) +{ + return m_pEEJitInfo->getDefaultComparerClass(elemType); +} + CORINFO_CLASS_HANDLE ZapInfo::getDefaultEqualityComparerClass( CORINFO_CLASS_HANDLE elemType) { diff --git a/src/libraries/System.Private.CoreLib/src/System/Collections/Generic/Comparer.cs b/src/libraries/System.Private.CoreLib/src/System/Collections/Generic/Comparer.cs index f022ee27b1b09f..c21928a7381fdb 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Collections/Generic/Comparer.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Collections/Generic/Comparer.cs @@ -55,6 +55,7 @@ public ComparisonComparer(Comparison comparison) // Needs to be public to support binary serialization compatibility public sealed partial class GenericComparer : Comparer where T : IComparable { + [MethodImpl(MethodImplOptions.AggressiveInlining)] public override int Compare(T? x, T? y) { if (x != null) diff --git a/src/tests/JIT/opt/Devirtualization/Comparer_get_Default.cs b/src/tests/JIT/opt/Devirtualization/Comparer_get_Default.cs new file mode 100644 index 00000000000000..3491e34f73f073 --- /dev/null +++ b/src/tests/JIT/opt/Devirtualization/Comparer_get_Default.cs @@ -0,0 +1,161 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; +using System.Runtime.CompilerServices; + +class Program +{ + private static int s_ReturnCode = 100; + + private static void AssertEquals(int expected, int actual, [CallerLineNumber] int line = 0) + { + if (expected != actual) + { + Console.WriteLine($"{expected} != {actual}, L{line}"); + s_ReturnCode++; + } + } + + private static void Compare_Boolean(Boolean a, Boolean b) => + AssertEquals(a.CompareTo(b), Comparer.Default.Compare(a, b)); + + private static void Compare_Byte(Byte a, Byte b) => + AssertEquals(a.CompareTo(b), Comparer.Default.Compare(a, b)); + + private static void Compare_SByte(SByte a, SByte b) => + AssertEquals(a.CompareTo(b), Comparer.Default.Compare(a, b)); + + private static void Compare_Char(Char a, Char b) => + AssertEquals(a.CompareTo(b), Comparer.Default.Compare(a, b)); + + private static void Compare_UInt16(UInt16 a, UInt16 b) => + AssertEquals(a.CompareTo(b), Comparer.Default.Compare(a, b)); + + private static void Compare_Int16(Int16 a, Int16 b) => + AssertEquals(a.CompareTo(b), Comparer.Default.Compare(a, b)); + + private static void Compare_UInt32(UInt32 a, UInt32 b) => + AssertEquals(a.CompareTo(b), Comparer.Default.Compare(a, b)); + + private static void Compare_Int32(Int32 a, Int32 b) => + AssertEquals(a.CompareTo(b), Comparer.Default.Compare(a, b)); + + private static void Compare_Int64(Int64 a, Int64 b) => + AssertEquals(a.CompareTo(b), Comparer.Default.Compare(a, b)); + + private static void Compare_UInt64(UInt64 a, UInt64 b) => + AssertEquals(a.CompareTo(b), Comparer.Default.Compare(a, b)); + + private static void Compare_IntPtr(IntPtr a, IntPtr b) => + AssertEquals(a.CompareTo(b), Comparer.Default.Compare(a, b)); + + private static void Compare_UIntPtr(UIntPtr a, UIntPtr b) => + AssertEquals(a.CompareTo(b), Comparer.Default.Compare(a, b)); + + private static void Compare_nint(nint a, nint b) => + AssertEquals(a.CompareTo(b), Comparer.Default.Compare(a, b)); + + private static void Compare_nuint(nuint a, nuint b) => + AssertEquals(a.CompareTo(b), Comparer.Default.Compare(a, b)); + + private static void Compare_Enum(MethodImplOptions a, MethodImplOptions b) => + AssertEquals(a.CompareTo(b), Comparer.Default.Compare(a, b)); + + private static void Compare_String(String a, String b) => + AssertEquals(a.CompareTo(b), Comparer.Default.Compare(a, b)); + + private static void Compare_DateTime(DateTime a, DateTime b) => + AssertEquals(a.CompareTo(b), Comparer.Default.Compare(a, b)); + + public static int Main(string[] args) + { + long[] values = {1, 2, 3, long.MaxValue}; + + for (var i = 0; i < values.Length; i++) + { + for (int j = 0; j < values.Length; j++) + { + long a = values[i]; + long b = values[j]; + + var boolA = Unsafe.As(ref a); + var boolB = Unsafe.As(ref b); + Compare_Boolean(boolA, boolB); + + var byteA = Unsafe.As(ref a); + var byteB = Unsafe.As(ref b); + Compare_Byte(byteA, byteB); + + var sbyteA = Unsafe.As(ref a); + var sbyteB = Unsafe.As(ref b); + Compare_SByte(sbyteA, sbyteB); + + var shortA = Unsafe.As(ref a); + var shortB = Unsafe.As(ref b); + Compare_Int16(shortA, shortB); + + var ushortA = Unsafe.As(ref a); + var ushortB = Unsafe.As(ref b); + Compare_UInt16(ushortA, ushortB); + + var charA = Unsafe.As(ref a); + var charB = Unsafe.As(ref b); + Compare_Char(charA, charB); + + var intA = Unsafe.As(ref a); + var intB = Unsafe.As(ref b); + Compare_Int32(intA, intB); + + var uintA = Unsafe.As(ref a); + var uintB = Unsafe.As(ref b); + Compare_UInt32(uintA, uintB); + + var longA = Unsafe.As(ref a); + var longB = Unsafe.As(ref b); + Compare_Int64(longA, longB); + + var ulongA = Unsafe.As(ref a); + var ulongB = Unsafe.As(ref b); + Compare_UInt64(ulongA, ulongB); + + var intPtrA = Unsafe.As(ref a); + var intPtrB = Unsafe.As(ref b); + Compare_IntPtr(intPtrA, intPtrB); + + var uintPtrA = Unsafe.As(ref a); + var uintPtrB = Unsafe.As(ref b); + Compare_UIntPtr(uintPtrA, uintPtrB); + + var nintA = Unsafe.As(ref a); + var nintB = Unsafe.As(ref b); + Compare_nint(nintA, nintB); + + var nuintA = Unsafe.As(ref a); + var nuintB = Unsafe.As(ref b); + Compare_nuint(nuintA, nuintB); + + var enumA = Unsafe.As(ref a); + var enumB = Unsafe.As(ref b); + Compare_Enum(enumA, enumB); + + Compare_DateTime( + new DateTime(Math.Clamp(a, DateTime.MinValue.Ticks, DateTime.MaxValue.Ticks)), + new DateTime(Math.Clamp(b, DateTime.MinValue.Ticks, DateTime.MaxValue.Ticks))); + } + } + + string[] strings = {"", "0", "00", "1", "11", "111", "привет", "Hello"}; + foreach (var str1 in strings) + { + foreach (var str2 in strings) + { + Compare_String(str1, str2); + Compare_String(str2, str1); + } + } + + return s_ReturnCode; + } +} diff --git a/src/tests/JIT/opt/Devirtualization/Comparer_get_Default.csproj b/src/tests/JIT/opt/Devirtualization/Comparer_get_Default.csproj new file mode 100644 index 00000000000000..d3861623ca4039 --- /dev/null +++ b/src/tests/JIT/opt/Devirtualization/Comparer_get_Default.csproj @@ -0,0 +1,9 @@ + + + Exe + True + + + + + From 5d7711ff55174302590f516c796ba4aa04914b89 Mon Sep 17 00:00:00 2001 From: Egor Bogatov Date: Thu, 11 Feb 2021 16:06:29 +0300 Subject: [PATCH 2/9] Update jitinterface.cpp --- src/coreclr/vm/jitinterface.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/coreclr/vm/jitinterface.cpp b/src/coreclr/vm/jitinterface.cpp index 9a0673cb1721e2..3ad40e58d0a2bb 100644 --- a/src/coreclr/vm/jitinterface.cpp +++ b/src/coreclr/vm/jitinterface.cpp @@ -9110,14 +9110,12 @@ CORINFO_CLASS_HANDLE CEEInfo::getDefaultComparerClassHelper(CORINFO_CLASS_HANDLE MODE_PREEMPTIVE; } CONTRACTL_END; - // Mirrors the logic in BCL's CompareHelpers.CreateDefaultComparer - // And in compile.cpp's SpecializeComparer TypeHandle elemTypeHnd(elemType); // Else we'll need to find the appropriate instantation Instantiation inst(&elemTypeHnd, 1); - // If T implements IEquatable + // If T implements IComparable if (elemTypeHnd.CanCastTo(TypeHandle(CoreLibBinder::GetClass(CLASS__ICOMPARABLEGENERIC)).Instantiate(inst))) { TypeHandle resultTh = ((TypeHandle)CoreLibBinder::GetClass(CLASS__GENERIC_COMPARER)).Instantiate(inst); From 8b162180238259c6a47c45ea77c1152885198672 Mon Sep 17 00:00:00 2001 From: EgorBo Date: Thu, 11 Feb 2021 18:05:28 +0300 Subject: [PATCH 3/9] Add more test cases --- .../Devirtualization/Comparer_get_Default.cs | 74 +++++++++++++++++-- 1 file changed, 68 insertions(+), 6 deletions(-) diff --git a/src/tests/JIT/opt/Devirtualization/Comparer_get_Default.cs b/src/tests/JIT/opt/Devirtualization/Comparer_get_Default.cs index 3491e34f73f073..76f5466412a3a5 100644 --- a/src/tests/JIT/opt/Devirtualization/Comparer_get_Default.cs +++ b/src/tests/JIT/opt/Devirtualization/Comparer_get_Default.cs @@ -3,15 +3,16 @@ using System; using System.Collections.Generic; +using System.Linq; using System.Runtime.CompilerServices; class Program { private static int s_ReturnCode = 100; - private static void AssertEquals(int expected, int actual, [CallerLineNumber] int line = 0) + private static void AssertEquals(T expected, T actual, [CallerLineNumber] int line = 0) { - if (expected != actual) + if (!expected.Equals(actual)) { Console.WriteLine($"{expected} != {actual}, L{line}"); s_ReturnCode++; @@ -69,9 +70,29 @@ private static void Compare_String(String a, String b) => private static void Compare_DateTime(DateTime a, DateTime b) => AssertEquals(a.CompareTo(b), Comparer.Default.Compare(a, b)); + private static void Compare_Int32_Nullable(long? a, long? b) + { + int actual = Comparer.Default.Compare(a, b); + int expected = 0; + if (a.HasValue) + expected = b.HasValue ? a.Value.CompareTo(b.Value) : 1; + else + expected = b.HasValue ? -1 : 0; + AssertEquals(expected, actual); + } + public static int Main(string[] args) { - long[] values = {1, 2, 3, long.MaxValue}; + ; + long[] values = Enumerable.Range(1000, 2000) + .Select(i => (long)i) + .Concat(new [] + { + short.MinValue, short.MinValue + 1, short.MaxValue - 1, short.MaxValue, short.MaxValue + 1, + int.MinValue, int.MinValue + 1, int.MaxValue, int.MaxValue - 1, + long.MinValue, long.MinValue + 1, long.MaxValue, long.MaxValue - 1, + }) + .ToArray(); for (var i = 0; i < values.Length; i++) { @@ -139,14 +160,16 @@ public static int Main(string[] args) var enumA = Unsafe.As(ref a); var enumB = Unsafe.As(ref b); Compare_Enum(enumA, enumB); - + Compare_DateTime( - new DateTime(Math.Clamp(a, DateTime.MinValue.Ticks, DateTime.MaxValue.Ticks)), + new DateTime(Math.Clamp(a, DateTime.MinValue.Ticks, DateTime.MaxValue.Ticks)), new DateTime(Math.Clamp(b, DateTime.MinValue.Ticks, DateTime.MaxValue.Ticks))); + + Compare_Int32_Nullable(a, b); } } - string[] strings = {"", "0", "00", "1", "11", "111", "привет", "Hello"}; + string[] strings = { "", "0", "00", "1", "11", "111", "привет", "Hello" }; foreach (var str1 in strings) { foreach (var str2 in strings) @@ -156,6 +179,45 @@ public static int Main(string[] args) } } + Compare_Int32_Nullable(null, 0); + Compare_Int32_Nullable(0, null); + Compare_Int32_Nullable(null, null); + Compare_Int32_Nullable(null, 1); + Compare_Int32_Nullable(1, null); + Compare_Int32_Nullable(null, -1); + Compare_Int32_Nullable(-1, null); + + GetTypeTests(); + GetHashCodeTests(); + return s_ReturnCode; } + + private static void GetTypeTests() + { + AssertEquals("System.Collections.Generic.GenericComparer`1[System.Int32]", Comparer.Default.GetType().ToString()); + AssertEquals("System.Collections.Generic.GenericComparer`1[System.String]", Comparer.Default.GetType().ToString()); + AssertEquals("System.Collections.Generic.GenericComparer`1[System.Guid]", Comparer.Default.GetType().ToString()); + AssertEquals("System.Collections.Generic.EnumComparer`1[System.Runtime.CompilerServices.MethodImplOptions]", Comparer.Default.GetType().ToString()); + AssertEquals("System.Collections.Generic.NullableComparer`1[System.Byte]", Comparer.Default.GetType().ToString()); + AssertEquals("System.Collections.Generic.ObjectComparer`1[Struct1]", Comparer.Default.GetType().ToString()); + } + private static int GetHashCodeTests() + { + // Just to make sure it doesn't crash + return Comparer.Default.GetHashCode() + + Comparer.Default.GetHashCode() + + Comparer.Default.GetHashCode() + + Comparer.Default.GetHashCode() + + Comparer.Default.GetHashCode() + + Comparer.Default.GetHashCode(); + } +} + +public struct Struct1 +{ + public long a; + public long b; + public long c; + public long d; } From f49b89e1e5d0e87625ca1c36ddb1af9ddabf7213 Mon Sep 17 00:00:00 2001 From: EgorBo Date: Thu, 11 Feb 2021 18:07:59 +0300 Subject: [PATCH 4/9] Add Nullable<> support --- src/coreclr/vm/corelib.h | 1 + src/coreclr/vm/jitinterface.cpp | 12 ++++++++++++ 2 files changed, 13 insertions(+) diff --git a/src/coreclr/vm/corelib.h b/src/coreclr/vm/corelib.h index 145045f03854d3..4edc72533ad1f2 100644 --- a/src/coreclr/vm/corelib.h +++ b/src/coreclr/vm/corelib.h @@ -1194,6 +1194,7 @@ DEFINE_CLASS(OBJECT_EQUALITYCOMPARER, CollectionsGeneric, ObjectEqualityComparer DEFINE_CLASS(GENERIC_COMPARER, CollectionsGeneric, GenericComparer`1) DEFINE_CLASS(OBJECT_COMPARER, CollectionsGeneric, ObjectComparer`1) DEFINE_CLASS(ENUM_COMPARER, CollectionsGeneric, EnumComparer`1) +DEFINE_CLASS(NULLABLE_COMPARER, CollectionsGeneric, NullableComparer`1) DEFINE_CLASS(INATTRIBUTE, Interop, InAttribute) diff --git a/src/coreclr/vm/jitinterface.cpp b/src/coreclr/vm/jitinterface.cpp index 3ad40e58d0a2bb..64d94643900073 100644 --- a/src/coreclr/vm/jitinterface.cpp +++ b/src/coreclr/vm/jitinterface.cpp @@ -9122,6 +9122,18 @@ CORINFO_CLASS_HANDLE CEEInfo::getDefaultComparerClassHelper(CORINFO_CLASS_HANDLE return CORINFO_CLASS_HANDLE(resultTh.GetMethodTable()); } + // Nullable + if (Nullable::IsNullableType(elemTypeHnd)) + { + Instantiation nullableInst = elemTypeHnd.AsMethodTable()->GetInstantiation(); + TypeHandle iequatable = TypeHandle(CoreLibBinder::GetClass(CLASS__IEQUATABLEGENERIC)).Instantiate(nullableInst); + if (nullableInst[0].CanCastTo(iequatable)) + { + TypeHandle resultTh = ((TypeHandle)CoreLibBinder::GetClass(CLASS__NULLABLE_COMPARER)).Instantiate(nullableInst); + return CORINFO_CLASS_HANDLE(resultTh.GetMethodTable()); + } + } + // We need to special case the Enum comparers based on their underlying type to avoid boxing if (elemTypeHnd.IsEnum()) { From b2303e6462b004e51204aa8355b131a5da3c34b9 Mon Sep 17 00:00:00 2001 From: EgorBo Date: Thu, 11 Feb 2021 21:37:21 +0300 Subject: [PATCH 5/9] Address feedback --- src/coreclr/jit/flowgraph.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/coreclr/jit/flowgraph.cpp b/src/coreclr/jit/flowgraph.cpp index 4a40055971f058..d6a9ed7642b159 100644 --- a/src/coreclr/jit/flowgraph.cpp +++ b/src/coreclr/jit/flowgraph.cpp @@ -926,8 +926,8 @@ GenTreeCall* Compiler::fgGetStaticsCCtorHelper(CORINFO_CLASS_HANDLE cls, CorInfo GenTreeCall* result = gtNewHelperCallNode(helper, type, argList); result->gtFlags |= callFlags; - // If we're importing the special EqualityComparer.Default - // intrinsic, flag the helper call. Later during inlining, we can + // If we're importing the special EqualityComparer.Default or Comparer.Default + // intrinsics, flag the helper call. Later during inlining, we can // remove the helper call if the associated field lookup is unused. if ((info.compFlags & CORINFO_FLG_JIT_INTRINSIC) != 0) { From 5c0b3d55d04fcd8dbc3fdb1fd3de963d7ba41e35 Mon Sep 17 00:00:00 2001 From: EgorBo Date: Thu, 11 Feb 2021 21:47:15 +0300 Subject: [PATCH 6/9] Add more test cases --- .../Devirtualization/Comparer_get_Default.cs | 39 ++++++++++++++----- 1 file changed, 30 insertions(+), 9 deletions(-) diff --git a/src/tests/JIT/opt/Devirtualization/Comparer_get_Default.cs b/src/tests/JIT/opt/Devirtualization/Comparer_get_Default.cs index 76f5466412a3a5..36dfb4418faf77 100644 --- a/src/tests/JIT/opt/Devirtualization/Comparer_get_Default.cs +++ b/src/tests/JIT/opt/Devirtualization/Comparer_get_Default.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; +using System.Collections; using System.Collections.Generic; using System.Linq; using System.Runtime.CompilerServices; @@ -61,15 +62,21 @@ private static void Compare_nint(nint a, nint b) => private static void Compare_nuint(nuint a, nuint b) => AssertEquals(a.CompareTo(b), Comparer.Default.Compare(a, b)); - private static void Compare_Enum(MethodImplOptions a, MethodImplOptions b) => + private static void Compare_Enum_Int32(MethodImplOptions a, MethodImplOptions b) => AssertEquals(a.CompareTo(b), Comparer.Default.Compare(a, b)); + private static void Compare_Enum_Byte(Enum_byte a, Enum_byte b) => + AssertEquals(a.CompareTo(b), Comparer.Default.Compare(a, b)); + private static void Compare_String(String a, String b) => AssertEquals(a.CompareTo(b), Comparer.Default.Compare(a, b)); private static void Compare_DateTime(DateTime a, DateTime b) => AssertEquals(a.CompareTo(b), Comparer.Default.Compare(a, b)); + private static void Compare_Struct1(Struct1 a, Struct1 b) => + AssertEquals(a.CompareTo(b), Comparer.Default.Compare(a, b)); + private static void Compare_Int32_Nullable(long? a, long? b) { int actual = Comparer.Default.Compare(a, b); @@ -83,10 +90,9 @@ private static void Compare_Int32_Nullable(long? a, long? b) public static int Main(string[] args) { - ; long[] values = Enumerable.Range(1000, 2000) .Select(i => (long)i) - .Concat(new [] + .Concat(new[] { short.MinValue, short.MinValue + 1, short.MaxValue - 1, short.MaxValue, short.MaxValue + 1, int.MinValue, int.MinValue + 1, int.MaxValue, int.MaxValue - 1, @@ -157,9 +163,17 @@ public static int Main(string[] args) var nuintB = Unsafe.As(ref b); Compare_nuint(nuintA, nuintB); - var enumA = Unsafe.As(ref a); - var enumB = Unsafe.As(ref b); - Compare_Enum(enumA, enumB); + var enumIntA = Unsafe.As(ref a); + var enumIntB = Unsafe.As(ref b); + Compare_Enum_Int32(enumIntA, enumIntB); + + var enumByteA = Unsafe.As(ref a); + var enumByteB = Unsafe.As(ref b); + Compare_Enum_Byte(enumByteA, enumByteB); + + var structA = new Struct1 {a = a, b = b}; + var structB = new Struct1 {a = b, b = a}; + Compare_Struct1(structA, structB); Compare_DateTime( new DateTime(Math.Clamp(a, DateTime.MinValue.Ticks, DateTime.MaxValue.Ticks)), @@ -214,10 +228,17 @@ private static int GetHashCodeTests() } } -public struct Struct1 +public enum Enum_byte : byte +{ + A,B,C,D,E +} + +public struct Struct1 : IComparable { public long a; public long b; - public long c; - public long d; + public int CompareTo(object? obj) + { + return b.CompareTo(((Struct1) obj).b); + } } From 18821b499fc58e94e4e474d138ca7e5d55fdb7ec Mon Sep 17 00:00:00 2001 From: EgorBo Date: Thu, 11 Feb 2021 21:51:37 +0300 Subject: [PATCH 7/9] Address feedback --- src/coreclr/vm/jitinterface.cpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/coreclr/vm/jitinterface.cpp b/src/coreclr/vm/jitinterface.cpp index 64d94643900073..c464d3cdd14a53 100644 --- a/src/coreclr/vm/jitinterface.cpp +++ b/src/coreclr/vm/jitinterface.cpp @@ -9112,7 +9112,10 @@ CORINFO_CLASS_HANDLE CEEInfo::getDefaultComparerClassHelper(CORINFO_CLASS_HANDLE TypeHandle elemTypeHnd(elemType); - // Else we'll need to find the appropriate instantation + // Mirrors the logic in BCL's CompareHelpers.CreateDefaultComparer + // And in compile.cpp's SpecializeComparer + // + // We need to find the appropriate instantiation Instantiation inst(&elemTypeHnd, 1); // If T implements IComparable @@ -9210,7 +9213,10 @@ CORINFO_CLASS_HANDLE CEEInfo::getDefaultEqualityComparerClassHelper(CORINFO_CLAS return CORINFO_CLASS_HANDLE(CoreLibBinder::GetClass(CLASS__BYTE_EQUALITYCOMPARER)); } - // Else we'll need to find the appropriate instantation + // Mirrors the logic in BCL's CompareHelpers.CreateDefaultComparer + // And in compile.cpp's SpecializeComparer + // + // We need to find the appropriate instantiation Instantiation inst(&elemTypeHnd, 1); // If T implements IEquatable From 67b1509f636aaec1e74be1e0e18957a95192eda4 Mon Sep 17 00:00:00 2001 From: EgorBo Date: Fri, 12 Feb 2021 01:57:53 +0300 Subject: [PATCH 8/9] Disable the test on Mono --- src/tests/issues.targets | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/tests/issues.targets b/src/tests/issues.targets index 3f2f9847c7f7e9..f48703909ac54b 100644 --- a/src/tests/issues.targets +++ b/src/tests/issues.targets @@ -963,6 +963,9 @@ + + https://github.com/dotnet/runtime/issues/48190 + Mono doesn't have a dynamic pgo or tiered compilation infrastructure From 7a11cf7cfd6499c37c6b12f6dd22b01be5224c5f Mon Sep 17 00:00:00 2001 From: Egor Bogatov Date: Fri, 12 Feb 2021 13:35:56 +0300 Subject: [PATCH 9/9] Update issues.targets --- src/tests/issues.targets | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tests/issues.targets b/src/tests/issues.targets index f48703909ac54b..052e88329a1096 100644 --- a/src/tests/issues.targets +++ b/src/tests/issues.targets @@ -963,7 +963,7 @@ - + https://github.com/dotnet/runtime/issues/48190