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 5c2e18b3382b65..5819acf6c4a182 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 @@ -11,12 +11,4 @@ public abstract partial class Comparer : IComparer, IComparer // as possible and define most of the creation logic in a non-generic class. public static Comparer Default { [Intrinsic] get; } = (Comparer)ComparerHelpers.CreateDefaultComparer(typeof(T)); } - - internal sealed partial class EnumComparer : Comparer where T : struct, Enum - { - public override int Compare(T x, T y) - { - return RuntimeHelpers.EnumCompareTo(x, y); - } - } } diff --git a/src/coreclr/System.Private.CoreLib/src/System/Collections/Generic/EqualityComparer.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Collections/Generic/EqualityComparer.CoreCLR.cs index bd4532bad23851..e9f4eb76af0b05 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Collections/Generic/EqualityComparer.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Collections/Generic/EqualityComparer.CoreCLR.cs @@ -161,12 +161,6 @@ internal override int LastIndexOf(byte[] array, byte value, int startIndex, int public sealed partial class EnumEqualityComparer : EqualityComparer where T : struct, Enum { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public override bool Equals(T x, T y) - { - return RuntimeHelpers.EnumEquals(x, y); - } - internal override int IndexOf(T[] array, T value, int startIndex, int count) { int endIndex = startIndex + count; 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 831582694aa534..def73acc8e0fb4 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 @@ -188,43 +188,6 @@ public static object GetUninitializedObject( [MethodImpl(MethodImplOptions.InternalCall)] internal static extern object AllocateUninitializedClone(object obj); - /// true if given type is reference type or value type that contains references - [Intrinsic] - public static bool IsReferenceOrContainsReferences() - { - // The body of this function will be replaced by the EE with unsafe code!!! - // See getILIntrinsicImplementationForRuntimeHelpers for how this happens. - throw new InvalidOperationException(); - } - - /// true if given type is bitwise equatable (memcmp can be used for equality checking) - /// - /// Only use the result of this for Equals() comparison, not for CompareTo() comparison. - /// - [Intrinsic] - internal static bool IsBitwiseEquatable() - { - // The body of this function will be replaced by the EE with unsafe code!!! - // See getILIntrinsicImplementationForRuntimeHelpers for how this happens. - throw new InvalidOperationException(); - } - - [Intrinsic] - internal static bool EnumEquals(T x, T y) where T : struct, Enum - { - // The body of this function will be replaced by the EE with unsafe code - // See getILIntrinsicImplementation for how this happens. - return x.Equals(y); - } - - [Intrinsic] - internal static int EnumCompareTo(T x, T y) where T : struct, Enum - { - // The body of this function will be replaced by the EE with unsafe code - // See getILIntrinsicImplementation for how this happens. - return x.CompareTo(y); - } - internal static ref byte GetRawData(this object obj) => ref Unsafe.As(obj).Data; @@ -297,18 +260,12 @@ internal static unsafe bool ObjectHasComponentSize(object obj) // ... work with pMT ... // // GC.KeepAlive(o); - // - [MethodImpl(MethodImplOptions.AggressiveInlining)] [Intrinsic] internal static unsafe MethodTable* GetMethodTable(object obj) { - // The body of this function will be replaced by the EE with unsafe code - // See getILIntrinsicImplementationForRuntimeHelpers for how this happens. - - return (MethodTable *)Unsafe.Add(ref Unsafe.As(ref obj.GetRawData()), -1); + return GetMethodTable(obj); } - [LibraryImport(QCall, EntryPoint = "MethodTable_AreTypesEquivalent")] [return: MarshalAs(UnmanagedType.Bool)] internal static unsafe partial bool AreTypesEquivalent(MethodTable* pMTa, MethodTable* pMTb); diff --git a/src/coreclr/inc/corinfo.h b/src/coreclr/inc/corinfo.h index ea1e0c10ec9548..4c410dbe5f9b97 100644 --- a/src/coreclr/inc/corinfo.h +++ b/src/coreclr/inc/corinfo.h @@ -2354,6 +2354,9 @@ class ICorStaticInfo // Quick check whether the type is a value class. Returns the same value as getClassAttribs(cls) & CORINFO_FLG_VALUECLASS, except faster. virtual bool isValueClass(CORINFO_CLASS_HANDLE cls) = 0; + // Checks if type can be compared for equality as bytes. + virtual bool isBitwiseEquatable(CORINFO_CLASS_HANDLE cls) = 0; + // Decides how the JIT should do the optimization to inline the check for // GetTypeFromHandle(handle) == obj.GetType() (for CORINFO_INLINE_TYPECHECK_SOURCE_VTABLE) // GetTypeFromHandle(X) == GetTypeFromHandle(Y) (for CORINFO_INLINE_TYPECHECK_SOURCE_TOKEN) diff --git a/src/coreclr/inc/icorjitinfoimpl_generated.h b/src/coreclr/inc/icorjitinfoimpl_generated.h index 20ee55464b3c46..e9a42d4cc97848 100644 --- a/src/coreclr/inc/icorjitinfoimpl_generated.h +++ b/src/coreclr/inc/icorjitinfoimpl_generated.h @@ -182,6 +182,9 @@ size_t printClassName( bool isValueClass( CORINFO_CLASS_HANDLE cls) override; +bool isBitwiseEquatable( + CORINFO_CLASS_HANDLE cls) override; + CorInfoInlineTypeCheck canInlineTypeCheck( CORINFO_CLASS_HANDLE cls, CorInfoInlineTypeCheckSource source) override; diff --git a/src/coreclr/inc/jiteeversionguid.h b/src/coreclr/inc/jiteeversionguid.h index 6c6f7e8283c01d..b9c51ab1b4e988 100644 --- a/src/coreclr/inc/jiteeversionguid.h +++ b/src/coreclr/inc/jiteeversionguid.h @@ -43,11 +43,11 @@ typedef const GUID *LPCGUID; #define GUID_DEFINED #endif // !GUID_DEFINED -constexpr GUID JITEEVersionIdentifier = { /* 02e334af-4e6e-4a68-9feb-308d3d2661bc */ - 0x2e334af, - 0x4e6e, - 0x4a68, - {0x9f, 0xeb, 0x30, 0x8d, 0x3d, 0x26, 0x61, 0xbc} +constexpr GUID JITEEVersionIdentifier = { /* f81c24a8-c7c1-420c-859c-36183e237c02 */ + 0xf81c24a8, + 0xc7c1, + 0x420c, + {0x85, 0x9c, 0x36, 0x18, 0x3e, 0x23, 0x7c, 0x02} }; ////////////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/src/coreclr/jit/ICorJitInfo_names_generated.h b/src/coreclr/jit/ICorJitInfo_names_generated.h index 79ca7d43da06e8..8c54b7b7def4a4 100644 --- a/src/coreclr/jit/ICorJitInfo_names_generated.h +++ b/src/coreclr/jit/ICorJitInfo_names_generated.h @@ -42,6 +42,7 @@ DEF_CLR_API(getClassNameFromMetadata) DEF_CLR_API(getTypeInstantiationArgument) DEF_CLR_API(printClassName) DEF_CLR_API(isValueClass) +DEF_CLR_API(isBitwiseEquatable) DEF_CLR_API(canInlineTypeCheck) DEF_CLR_API(getClassAttribs) DEF_CLR_API(getClassModule) diff --git a/src/coreclr/jit/ICorJitInfo_wrapper_generated.hpp b/src/coreclr/jit/ICorJitInfo_wrapper_generated.hpp index ae0b25b0a63cc5..cdbd0bbc9b94e5 100644 --- a/src/coreclr/jit/ICorJitInfo_wrapper_generated.hpp +++ b/src/coreclr/jit/ICorJitInfo_wrapper_generated.hpp @@ -387,6 +387,15 @@ bool WrapICorJitInfo::isValueClass( return temp; } +bool WrapICorJitInfo::isBitwiseEquatable( + CORINFO_CLASS_HANDLE cls) +{ + API_ENTER(isBitwiseEquatable); + bool temp = wrapHnd->isBitwiseEquatable(cls); + API_LEAVE(isBitwiseEquatable); + return temp; +} + CorInfoInlineTypeCheck WrapICorJitInfo::canInlineTypeCheck( CORINFO_CLASS_HANDLE cls, CorInfoInlineTypeCheckSource source) diff --git a/src/coreclr/jit/assertionprop.cpp b/src/coreclr/jit/assertionprop.cpp index a3b6c3c8bc7e87..39109af6490b00 100644 --- a/src/coreclr/jit/assertionprop.cpp +++ b/src/coreclr/jit/assertionprop.cpp @@ -4699,6 +4699,17 @@ GenTree* Compiler::optAssertionProp_Update(GenTree* newTree, GenTree* tree, Stat if (parent != nullptr) { parent->ReplaceOperand(useEdge, newTree); + if ((parent->gtOper == GT_IND) && newTree->IsIconHandle()) + { + parent->gtFlags |= GTF_IND_NONFAULTING; + GenTreeFlags handleKind = newTree->GetIconHandleFlag(); + + if ((handleKind != GTF_ICON_STATIC_HDL) && (handleKind != GTF_ICON_BBC_PTR) && + (handleKind != GTF_ICON_GLOBAL_PTR)) + { + parent->gtFlags |= GTF_IND_INVARIANT; + } + } } else { diff --git a/src/coreclr/jit/compiler.h b/src/coreclr/jit/compiler.h index 1d42520215dede..8cbb0fd96fe34d 100644 --- a/src/coreclr/jit/compiler.h +++ b/src/coreclr/jit/compiler.h @@ -3904,6 +3904,7 @@ class Compiler void impImportLeave(BasicBlock* block); void impResetLeaveBlock(BasicBlock* block, unsigned jmpAddr); GenTree* impTypeIsAssignable(GenTree* typeTo, GenTree* typeFrom); + GenTree* impImportCompare(GenTree* op1, GenTree* op2, genTreeOps oper, bool isUnsignedOrUnordered); // Mirrors StringComparison.cs enum StringComparison diff --git a/src/coreclr/jit/gentree.cpp b/src/coreclr/jit/gentree.cpp index ee9be975c32d52..2262fbcb1d6428 100644 --- a/src/coreclr/jit/gentree.cpp +++ b/src/coreclr/jit/gentree.cpp @@ -8214,6 +8214,19 @@ GenTreeBlk* Compiler::gtNewBlkIndir(ClassLayout* layout, GenTree* addr, GenTreeF // GenTreeIndir* Compiler::gtNewIndir(var_types typ, GenTree* addr, GenTreeFlags indirFlags) { + if (addr->IsIconHandle()) + { + GenTreeFlags handleKind = addr->GetIconHandleFlag(); + + if ((handleKind != GTF_ICON_STATIC_HDL) && (handleKind != GTF_ICON_BBC_PTR) && + (handleKind != GTF_ICON_GLOBAL_PTR)) + { + indirFlags |= GTF_IND_INVARIANT; + } + + indirFlags |= GTF_IND_NONFAULTING; + } + GenTreeIndir* indir = new (this, GT_IND) GenTreeIndir(GT_IND, typ, addr, nullptr); gtInitializeIndirNode(indir, indirFlags); diff --git a/src/coreclr/jit/importer.cpp b/src/coreclr/jit/importer.cpp index e745bce1bd0379..40e519f4542e06 100644 --- a/src/coreclr/jit/importer.cpp +++ b/src/coreclr/jit/importer.cpp @@ -2424,6 +2424,49 @@ GenTree* Compiler::impTypeIsAssignable(GenTree* typeTo, GenTree* typeFrom) return nullptr; } +GenTree* Compiler::impImportCompare(GenTree* op1, GenTree* op2, genTreeOps oper, bool isUnsignedOrUnordered) +{ + // Recognize the IL idiom of CGT_UN(op1, 0) and normalize + // it so that downstream optimizations don't have to. + if ((oper == GT_GT) && isUnsignedOrUnordered && op2->IsIntegralConst(0)) + { + oper = GT_NE; + isUnsignedOrUnordered = false; + } + +#ifdef TARGET_64BIT + if (varTypeIsI(op1) && genActualTypeIsInt(op2)) + { + op2 = impImplicitIorI4Cast(op2, TYP_I_IMPL); + } + else if (varTypeIsI(op2) && genActualTypeIsInt(op1)) + { + op1 = impImplicitIorI4Cast(op1, TYP_I_IMPL); + } +#endif // TARGET_64BIT + + assert(genActualType(op1) == genActualType(op2) || (varTypeIsI(op1) && varTypeIsI(op2)) || + (varTypeIsFloating(op1) && varTypeIsFloating(op2))); + + if ((op1->TypeGet() != op2->TypeGet()) && varTypeIsFloating(op1)) + { + op1 = impImplicitR4orR8Cast(op1, TYP_DOUBLE); + op2 = impImplicitR4orR8Cast(op2, TYP_DOUBLE); + } + + // Create the comparison node. + op1 = gtNewOperNode(oper, TYP_INT, op1, op2); + + // TODO: setting both flags when only one is appropriate. + if (isUnsignedOrUnordered) + { + op1->gtFlags |= GTF_RELOP_NAN_UN | GTF_UNSIGNED; + } + + // Fold result, if possible. + return gtFoldExpr(op1); +} + /***************************************************************************** * 'logMsg' is true if a log message needs to be logged. false if the caller has * already logged it (presumably in a more detailed fashion than done here) @@ -7396,47 +7439,7 @@ void Compiler::impImportBlockCode(BasicBlock* block) op2 = impPopStack().val; op1 = impPopStack().val; - // Recognize the IL idiom of CGT_UN(op1, 0) and normalize - // it so that downstream optimizations don't have to. - if ((opcode == CEE_CGT_UN) && op2->IsIntegralConst(0)) - { - oper = GT_NE; - uns = false; - } - -#ifdef TARGET_64BIT - if (varTypeIsI(op1) && genActualTypeIsInt(op2)) - { - op2 = impImplicitIorI4Cast(op2, TYP_I_IMPL); - } - else if (varTypeIsI(op2) && genActualTypeIsInt(op1)) - { - op1 = impImplicitIorI4Cast(op1, TYP_I_IMPL); - } -#endif // TARGET_64BIT - - assertImp(genActualType(op1) == genActualType(op2) || (varTypeIsI(op1) && varTypeIsI(op2)) || - (varTypeIsFloating(op1) && varTypeIsFloating(op2))); - - if ((op1->TypeGet() != op2->TypeGet()) && varTypeIsFloating(op1)) - { - op1 = impImplicitR4orR8Cast(op1, TYP_DOUBLE); - op2 = impImplicitR4orR8Cast(op2, TYP_DOUBLE); - } - - // Create the comparison node. - op1 = gtNewOperNode(oper, TYP_INT, op1, op2); - - // TODO: setting both flags when only one is appropriate. - if (uns) - { - op1->gtFlags |= GTF_RELOP_NAN_UN | GTF_UNSIGNED; - } - - // Fold result, if possible. - op1 = gtFoldExpr(op1); - - impPushOnStack(op1, tiRetVal); + impPushOnStack(impImportCompare(op1, op2, oper, uns), tiRetVal); break; case CEE_BEQ_S: diff --git a/src/coreclr/jit/importercalls.cpp b/src/coreclr/jit/importercalls.cpp index 7a304b11469eda..0f60244fe82d0c 100644 --- a/src/coreclr/jit/importercalls.cpp +++ b/src/coreclr/jit/importercalls.cpp @@ -2551,6 +2551,9 @@ GenTree* Compiler::impIntrinsic(GenTree* newobjThis, // This one is just `return true/false` case NI_System_Runtime_CompilerServices_RuntimeHelpers_IsKnownConstant: + case NI_System_Runtime_CompilerServices_RuntimeHelpers_EnumEquals: + case NI_System_Runtime_CompilerServices_RuntimeHelpers_EnumCompareTo: + // We need these to be able to fold "typeof(...) == typeof(...)" case NI_System_Type_GetTypeFromHandle: case NI_System_Type_op_Equality: @@ -2759,6 +2762,89 @@ GenTree* Compiler::impIntrinsic(GenTree* newobjThis, break; } + case NI_System_Runtime_CompilerServices_RuntimeHelpers_IsReference: + { + assert(sig->sigInst.methInstCount == 1); + CORINFO_CLASS_HANDLE hClass = sig->sigInst.methInst[0]; + retNode = gtNewIconNode(eeIsValueClass(hClass) ? 0 : 1); + break; + } + + case NI_System_Runtime_CompilerServices_RuntimeHelpers_IsReferenceOrContainsReferences: + { + assert(sig->sigInst.methInstCount == 1); + CORINFO_CLASS_HANDLE hClass = sig->sigInst.methInst[0]; + retNode = gtNewIconNode(!eeIsValueClass(hClass) || ((info.compCompHnd->getClassAttribs(hClass) & + CORINFO_FLG_CONTAINS_GC_PTR) != 0) + ? 1 + : 0); + break; + } + + case NI_System_Runtime_CompilerServices_RuntimeHelpers_IsBitwiseEquatable: + { + assert(sig->sigInst.methInstCount == 1); + CORINFO_CLASS_HANDLE hClass = sig->sigInst.methInst[0]; + retNode = gtNewIconNode(info.compCompHnd->isBitwiseEquatable(hClass) ? 1 : 0); + break; + } + + case NI_System_Runtime_CompilerServices_RuntimeHelpers_GetMethodTable: + { + retNode = gtNewMethodTableLookup(impPopStack().val); + break; + } + + case NI_System_Runtime_CompilerServices_RuntimeHelpers_EnumEquals: + { + assert(sig->sigInst.methInstCount == 1); + CORINFO_CLASS_HANDLE hClass = sig->sigInst.methInst[0]; + CorInfoType jitType = info.compCompHnd->asCorInfoType(hClass); + + if (varTypeIsFloating(JitType2PreciseVarType(jitType))) + { + return nullptr; + } + + GenTree* op2 = impPopStack().val; + GenTree* op1 = impPopStack().val; + retNode = impImportCompare(op1, op2, GT_EQ, false); + break; + } + + case NI_System_Runtime_CompilerServices_RuntimeHelpers_EnumCompareTo: + { + assert(sig->sigInst.methInstCount == 1); + + CORINFO_CLASS_HANDLE hClass = sig->sigInst.methInst[0]; + CorInfoType jitType = info.compCompHnd->asCorInfoType(hClass); + var_types type = JitType2PreciseVarType(jitType); + + if (varTypeIsFloating(type)) + { + return nullptr; + } + + GenTree* op2 = impPopStack().val; + GenTree* op1 = impPopStack().val; + + if (varTypeIsSmall(type)) + { + return gtFoldExpr(gtNewOperNode(GT_SUB, TYP_INT, op1, op2)); + } + + bool uns = varTypeIsUnsigned(type); + + GenTree* op1Clone; + GenTree* op2Clone; + op1 = impCloneExpr(op1, &op1Clone, CHECK_SPILL_ALL, nullptr DEBUGARG("EnumCompareTo arg1")); + op2 = impCloneExpr(op2, &op2Clone, CHECK_SPILL_ALL, nullptr DEBUGARG("EnumCompareTo arg2")); + + retNode = gtFoldExpr(gtNewOperNode(GT_SUB, TYP_INT, impImportCompare(op1, op2, GT_GT, uns), + impImportCompare(op1Clone, op2Clone, GT_LT, uns))); + break; + } + case NI_System_Runtime_InteropService_MemoryMarshal_GetArrayDataReference: { assert(sig->numArgs == 1); @@ -8948,6 +9034,30 @@ NamedIntrinsic Compiler::lookupNamedIntrinsic(CORINFO_METHOD_HANDLE method) { result = NI_System_Runtime_CompilerServices_RuntimeHelpers_IsKnownConstant; } + else if (strcmp(methodName, "IsReference") == 0) + { + result = NI_System_Runtime_CompilerServices_RuntimeHelpers_IsReference; + } + else if (strcmp(methodName, "IsReferenceOrContainsReferences") == 0) + { + result = NI_System_Runtime_CompilerServices_RuntimeHelpers_IsReferenceOrContainsReferences; + } + else if (strcmp(methodName, "IsBitwiseEquatable") == 0) + { + result = NI_System_Runtime_CompilerServices_RuntimeHelpers_IsBitwiseEquatable; + } + else if (strcmp(methodName, "EnumEquals") == 0) + { + result = NI_System_Runtime_CompilerServices_RuntimeHelpers_EnumEquals; + } + else if (strcmp(methodName, "EnumCompareTo") == 0) + { + result = NI_System_Runtime_CompilerServices_RuntimeHelpers_EnumCompareTo; + } + else if (strcmp(methodName, "GetMethodTable") == 0) + { + result = NI_System_Runtime_CompilerServices_RuntimeHelpers_GetMethodTable; + } } else if (strcmp(className, "Unsafe") == 0) { diff --git a/src/coreclr/jit/namedintrinsiclist.h b/src/coreclr/jit/namedintrinsiclist.h index 86c7d445dabba1..c8d1a3df2ab754 100644 --- a/src/coreclr/jit/namedintrinsiclist.h +++ b/src/coreclr/jit/namedintrinsiclist.h @@ -104,6 +104,12 @@ enum NamedIntrinsic : unsigned short NI_System_Runtime_CompilerServices_RuntimeHelpers_CreateSpan, NI_System_Runtime_CompilerServices_RuntimeHelpers_InitializeArray, NI_System_Runtime_CompilerServices_RuntimeHelpers_IsKnownConstant, + NI_System_Runtime_CompilerServices_RuntimeHelpers_IsReference, + NI_System_Runtime_CompilerServices_RuntimeHelpers_IsReferenceOrContainsReferences, + NI_System_Runtime_CompilerServices_RuntimeHelpers_IsBitwiseEquatable, + NI_System_Runtime_CompilerServices_RuntimeHelpers_EnumEquals, + NI_System_Runtime_CompilerServices_RuntimeHelpers_EnumCompareTo, + NI_System_Runtime_CompilerServices_RuntimeHelpers_GetMethodTable, NI_System_Runtime_InteropService_MemoryMarshal_GetArrayDataReference, diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/IntrinsicSupport/ComparerHelpers.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/IntrinsicSupport/ComparerHelpers.cs index db284d80cce0e3..a168c42ffb842d 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/IntrinsicSupport/ComparerHelpers.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/IntrinsicSupport/ComparerHelpers.cs @@ -90,12 +90,5 @@ internal static object GetComparer(RuntimeTypeHandle t) return RuntimeAugments.RawNewObject(comparerType); } - - // This one is an intrinsic that is used to make enum comparisons more efficient. - [Intrinsic] - internal static int EnumOnlyCompare(T x, T y) where T : struct, Enum - { - return x.CompareTo(y); - } } } diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/IntrinsicSupport/EqualityComparerHelpers.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/IntrinsicSupport/EqualityComparerHelpers.cs index dc63cad6caeb69..534af0c7283cbd 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/IntrinsicSupport/EqualityComparerHelpers.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/IntrinsicSupport/EqualityComparerHelpers.cs @@ -98,13 +98,6 @@ internal static object GetComparer(RuntimeTypeHandle t) // Redirection target functions for redirecting behavior of Array.IndexOf //----------------------------------------------------------------------- - // This one is an intrinsic that is used to make enum comparisons more efficient. - [Intrinsic] - internal static bool EnumOnlyEquals(T x, T y) where T : struct - { - return x.Equals(y); - } - private static bool StructOnlyEqualsIEquatable(T x, T y) where T : IEquatable { return x.Equals(y); diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Collections/Generic/Comparer.NativeAot.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Collections/Generic/Comparer.NativeAot.cs index c14dc49f0e61aa..5f97c3dc2548ae 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Collections/Generic/Comparer.NativeAot.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Collections/Generic/Comparer.NativeAot.cs @@ -32,12 +32,4 @@ private static Comparer Create() public static Comparer Default { [Intrinsic] get; } = Create(); } - - internal sealed partial class EnumComparer : Comparer where T : struct, Enum - { - public override int Compare(T x, T y) - { - return ComparerHelpers.EnumOnlyCompare(x, y); - } - } } diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Collections/Generic/EqualityComparer.NativeAot.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Collections/Generic/EqualityComparer.NativeAot.cs index 0b5c3d2914aa9e..3667e34f35486e 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Collections/Generic/EqualityComparer.NativeAot.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Collections/Generic/EqualityComparer.NativeAot.cs @@ -32,12 +32,4 @@ private static EqualityComparer Create() public static EqualityComparer Default { [Intrinsic] get; } = Create(); } - - public sealed partial class EnumEqualityComparer : EqualityComparer where T : struct, Enum - { - public sealed override bool Equals(T x, T y) - { - return EqualityComparerHelpers.EnumOnlyEquals(x, y); - } - } } diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.NativeAot.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.NativeAot.cs index fde97404f3f185..715107da481c36 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.NativeAot.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.NativeAot.cs @@ -186,27 +186,10 @@ public static unsafe bool TryEnsureSufficientExecutionStack() return (t_sufficientStackLimit = limit); } - [Intrinsic] - public static bool IsReferenceOrContainsReferences() - { - var pEEType = EETypePtr.EETypePtrOf(); - return !pEEType.IsValueType || pEEType.ContainsGCPointers; - } - [Intrinsic] internal static bool IsReference() { - var pEEType = EETypePtr.EETypePtrOf(); - return !pEEType.IsValueType; - } - - [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal static bool IsBitwiseEquatable() - { - // Only reachable for universal shared code - the compiler replaces this otherwise. - // Returning false is conservative. - return false; + return IsReference(); } internal static ref byte GetRawData(this object obj) => @@ -231,8 +214,9 @@ internal static unsafe ushort GetElementSize(this Array array) return array.GetMethodTable()->ComponentSize; } + [Intrinsic] internal static unsafe MethodTable* GetMethodTable(this object obj) - => obj.m_pEEType; + => GetMethodTable(obj); internal static unsafe ref MethodTable* GetMethodTableRef(this object obj) => ref obj.m_pEEType; diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs index 453e80c381e5c0..12a393ca7508c4 100644 --- a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs +++ b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs @@ -17,6 +17,7 @@ #endif using Internal.IL; +using Internal.IL.Stubs; using Internal.TypeSystem; using Internal.TypeSystem.Ecma; using Internal.TypeSystem.Interop; @@ -1968,6 +1969,52 @@ private bool isValueClass(CORINFO_CLASS_STRUCT_* cls) return HandleToObject(cls).IsValueType; } + private bool isBitwiseEquatable(CORINFO_CLASS_STRUCT_* cls) + { + TypeDesc type = HandleToObject(cls); + + // Ideally we could detect automatically whether a type is trivially equatable + // (i.e., its operator == could be implemented via memcmp). But for now we'll + // do the simple thing and hardcode the list of types we know fulfill this contract. + // n.b. This doesn't imply that the type's CompareTo method can be memcmp-implemented, + // as a method like CompareTo may need to take a type's signedness into account. + switch (type.UnderlyingType.Category) + { + case TypeFlags.Boolean: + case TypeFlags.Byte: + case TypeFlags.SByte: + case TypeFlags.Char: + case TypeFlags.UInt16: + case TypeFlags.Int16: + case TypeFlags.UInt32: + case TypeFlags.Int32: + case TypeFlags.UInt64: + case TypeFlags.Int64: + case TypeFlags.IntPtr: + case TypeFlags.UIntPtr: + return true; + + default: + if (type is not MetadataType mdType) + break; + + if (mdType.Module == mdType.Context.SystemModule && + mdType.Namespace == "System.Text" && mdType.Name == "Rune") + { + return true; + } + + if (!mdType.IsValueType || ComparerIntrinsics.ImplementsIEquatable(mdType.GetTypeDefinition())) + break; + + // Value type that can use memcmp and that doesn't override object.Equals or implement IEquatable.Equals. + MethodDesc objectEquals = mdType.Context.GetWellKnownType(WellKnownType.Object).GetMethod("Equals", null); + return mdType.FindVirtualFunctionTargetMethodOnObjectType(objectEquals).OwningType != mdType && + ComparerIntrinsics.CanCompareValueTypeBits(mdType, objectEquals); + } + return false; + } + #pragma warning disable CA1822 // Mark members as static private CorInfoInlineTypeCheck canInlineTypeCheck(CORINFO_CLASS_STRUCT_* cls, CorInfoInlineTypeCheckSource source) #pragma warning restore CA1822 // Mark members as static diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoImpl_generated.cs b/src/coreclr/tools/Common/JitInterface/CorInfoImpl_generated.cs index 51d3f1ba7fb658..d8b3344b6fbea6 100644 --- a/src/coreclr/tools/Common/JitInterface/CorInfoImpl_generated.cs +++ b/src/coreclr/tools/Common/JitInterface/CorInfoImpl_generated.cs @@ -568,6 +568,21 @@ private static byte _isValueClass(IntPtr thisHandle, IntPtr* ppException, CORINF } } + [UnmanagedCallersOnly] + private static byte _isBitwiseEquatable(IntPtr thisHandle, IntPtr* ppException, CORINFO_CLASS_STRUCT_* cls) + { + var _this = GetThis(thisHandle); + try + { + return _this.isBitwiseEquatable(cls) ? (byte)1 : (byte)0; + } + catch (Exception ex) + { + *ppException = _this.AllocException(ex); + return default; + } + } + [UnmanagedCallersOnly] private static CorInfoInlineTypeCheck _canInlineTypeCheck(IntPtr thisHandle, IntPtr* ppException, CORINFO_CLASS_STRUCT_* cls, CorInfoInlineTypeCheckSource source) { @@ -2507,7 +2522,7 @@ private static uint _getJitFlags(IntPtr thisHandle, IntPtr* ppException, CORJIT_ private static IntPtr GetUnmanagedCallbacks() { - void** callbacks = (void**)Marshal.AllocCoTaskMem(sizeof(IntPtr) * 169); + void** callbacks = (void**)Marshal.AllocCoTaskMem(sizeof(IntPtr) * 170); callbacks[0] = (delegate* unmanaged)&_isIntrinsic; callbacks[1] = (delegate* unmanaged)&_getMethodAttribs; @@ -2547,137 +2562,138 @@ private static IntPtr GetUnmanagedCallbacks() callbacks[35] = (delegate* unmanaged)&_getTypeInstantiationArgument; callbacks[36] = (delegate* unmanaged)&_printClassName; callbacks[37] = (delegate* unmanaged)&_isValueClass; - callbacks[38] = (delegate* unmanaged)&_canInlineTypeCheck; - callbacks[39] = (delegate* unmanaged)&_getClassAttribs; - callbacks[40] = (delegate* unmanaged)&_getClassModule; - callbacks[41] = (delegate* unmanaged)&_getModuleAssembly; - callbacks[42] = (delegate* unmanaged)&_getAssemblyName; - callbacks[43] = (delegate* unmanaged)&_LongLifetimeMalloc; - callbacks[44] = (delegate* unmanaged)&_LongLifetimeFree; - callbacks[45] = (delegate* unmanaged)&_getClassModuleIdForStatics; - callbacks[46] = (delegate* unmanaged)&_getIsClassInitedFlagAddress; - callbacks[47] = (delegate* unmanaged)&_getStaticBaseAddress; - callbacks[48] = (delegate* unmanaged)&_getClassSize; - callbacks[49] = (delegate* unmanaged)&_getHeapClassSize; - callbacks[50] = (delegate* unmanaged)&_canAllocateOnStack; - callbacks[51] = (delegate* unmanaged)&_getClassAlignmentRequirement; - callbacks[52] = (delegate* unmanaged)&_getClassGClayout; - callbacks[53] = (delegate* unmanaged)&_getClassNumInstanceFields; - callbacks[54] = (delegate* unmanaged)&_getFieldInClass; - callbacks[55] = (delegate* unmanaged)&_getTypeLayout; - callbacks[56] = (delegate* unmanaged)&_checkMethodModifier; - callbacks[57] = (delegate* unmanaged)&_getNewHelper; - callbacks[58] = (delegate* unmanaged)&_getNewArrHelper; - callbacks[59] = (delegate* unmanaged)&_getCastingHelper; - callbacks[60] = (delegate* unmanaged)&_getSharedCCtorHelper; - callbacks[61] = (delegate* unmanaged)&_getTypeForBox; - callbacks[62] = (delegate* unmanaged)&_getBoxHelper; - callbacks[63] = (delegate* unmanaged)&_getUnBoxHelper; - callbacks[64] = (delegate* unmanaged)&_getRuntimeTypePointer; - callbacks[65] = (delegate* unmanaged)&_isObjectImmutable; - callbacks[66] = (delegate* unmanaged)&_getStringChar; - callbacks[67] = (delegate* unmanaged)&_getObjectType; - callbacks[68] = (delegate* unmanaged)&_getReadyToRunHelper; - callbacks[69] = (delegate* unmanaged)&_getReadyToRunDelegateCtorHelper; - callbacks[70] = (delegate* unmanaged)&_initClass; - callbacks[71] = (delegate* unmanaged)&_classMustBeLoadedBeforeCodeIsRun; - callbacks[72] = (delegate* unmanaged)&_getBuiltinClass; - callbacks[73] = (delegate* unmanaged)&_getTypeForPrimitiveValueClass; - callbacks[74] = (delegate* unmanaged)&_getTypeForPrimitiveNumericClass; - callbacks[75] = (delegate* unmanaged)&_canCast; - callbacks[76] = (delegate* unmanaged)&_compareTypesForCast; - callbacks[77] = (delegate* unmanaged)&_compareTypesForEquality; - callbacks[78] = (delegate* unmanaged)&_isMoreSpecificType; - callbacks[79] = (delegate* unmanaged)&_isEnum; - callbacks[80] = (delegate* unmanaged)&_getParentType; - callbacks[81] = (delegate* unmanaged)&_getChildType; - callbacks[82] = (delegate* unmanaged)&_isSDArray; - callbacks[83] = (delegate* unmanaged)&_getArrayRank; - callbacks[84] = (delegate* unmanaged)&_getArrayIntrinsicID; - callbacks[85] = (delegate* unmanaged)&_getArrayInitializationData; - callbacks[86] = (delegate* unmanaged)&_canAccessClass; - callbacks[87] = (delegate* unmanaged)&_printFieldName; - callbacks[88] = (delegate* unmanaged)&_getFieldClass; - callbacks[89] = (delegate* unmanaged)&_getFieldType; - callbacks[90] = (delegate* unmanaged)&_getFieldOffset; - callbacks[91] = (delegate* unmanaged)&_getFieldInfo; - callbacks[92] = (delegate* unmanaged)&_getThreadLocalFieldInfo; - callbacks[93] = (delegate* unmanaged)&_getThreadLocalStaticBlocksInfo; - callbacks[94] = (delegate* unmanaged)&_isFieldStatic; - callbacks[95] = (delegate* unmanaged)&_getArrayOrStringLength; - callbacks[96] = (delegate* unmanaged)&_getBoundaries; - callbacks[97] = (delegate* unmanaged)&_setBoundaries; - callbacks[98] = (delegate* unmanaged)&_getVars; - callbacks[99] = (delegate* unmanaged)&_setVars; - callbacks[100] = (delegate* unmanaged)&_reportRichMappings; - callbacks[101] = (delegate* unmanaged)&_allocateArray; - callbacks[102] = (delegate* unmanaged)&_freeArray; - callbacks[103] = (delegate* unmanaged)&_getArgNext; - callbacks[104] = (delegate* unmanaged)&_getArgType; - callbacks[105] = (delegate* unmanaged)&_getExactClasses; - callbacks[106] = (delegate* unmanaged)&_getArgClass; - callbacks[107] = (delegate* unmanaged)&_getHFAType; - callbacks[108] = (delegate* unmanaged)&_runWithErrorTrap; - callbacks[109] = (delegate* unmanaged)&_runWithSPMIErrorTrap; - callbacks[110] = (delegate* unmanaged)&_getEEInfo; - callbacks[111] = (delegate* unmanaged)&_getJitTimeLogFilename; - callbacks[112] = (delegate* unmanaged)&_getMethodDefFromMethod; - callbacks[113] = (delegate* unmanaged)&_printMethodName; - callbacks[114] = (delegate* unmanaged)&_getMethodNameFromMetadata; - callbacks[115] = (delegate* unmanaged)&_getMethodHash; - callbacks[116] = (delegate* unmanaged)&_getSystemVAmd64PassStructInRegisterDescriptor; - callbacks[117] = (delegate* unmanaged)&_getLoongArch64PassStructInRegisterFlags; - callbacks[118] = (delegate* unmanaged)&_getRISCV64PassStructInRegisterFlags; - callbacks[119] = (delegate* unmanaged)&_getThreadTLSIndex; - 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)&_getClassDomainID; - callbacks[139] = (delegate* unmanaged)&_getStaticFieldContent; - callbacks[140] = (delegate* unmanaged)&_getObjectContent; - 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)&_GetDelegateCtor; - callbacks[148] = (delegate* unmanaged)&_MethodCompileComplete; - callbacks[149] = (delegate* unmanaged)&_getTailCallHelpers; - callbacks[150] = (delegate* unmanaged)&_convertPInvokeCalliToCall; - callbacks[151] = (delegate* unmanaged)&_notifyInstructionSetUsage; - callbacks[152] = (delegate* unmanaged)&_updateEntryPointForTailCall; - callbacks[153] = (delegate* unmanaged)&_allocMem; - callbacks[154] = (delegate* unmanaged)&_reserveUnwindInfo; - callbacks[155] = (delegate* unmanaged)&_allocUnwindInfo; - callbacks[156] = (delegate* unmanaged)&_allocGCInfo; - callbacks[157] = (delegate* unmanaged)&_setEHcount; - callbacks[158] = (delegate* unmanaged)&_setEHinfo; - callbacks[159] = (delegate* unmanaged)&_logMsg; - callbacks[160] = (delegate* unmanaged)&_doAssert; - callbacks[161] = (delegate* unmanaged)&_reportFatalError; - callbacks[162] = (delegate* unmanaged)&_getPgoInstrumentationResults; - callbacks[163] = (delegate* unmanaged)&_allocPgoInstrumentationBySchema; - callbacks[164] = (delegate* unmanaged)&_recordCallSite; - callbacks[165] = (delegate* unmanaged)&_recordRelocation; - callbacks[166] = (delegate* unmanaged)&_getRelocTypeHint; - callbacks[167] = (delegate* unmanaged)&_getExpectedTargetArchitecture; - callbacks[168] = (delegate* unmanaged)&_getJitFlags; + callbacks[38] = (delegate* unmanaged)&_isBitwiseEquatable; + callbacks[39] = (delegate* unmanaged)&_canInlineTypeCheck; + callbacks[40] = (delegate* unmanaged)&_getClassAttribs; + callbacks[41] = (delegate* unmanaged)&_getClassModule; + callbacks[42] = (delegate* unmanaged)&_getModuleAssembly; + callbacks[43] = (delegate* unmanaged)&_getAssemblyName; + callbacks[44] = (delegate* unmanaged)&_LongLifetimeMalloc; + callbacks[45] = (delegate* unmanaged)&_LongLifetimeFree; + callbacks[46] = (delegate* unmanaged)&_getClassModuleIdForStatics; + callbacks[47] = (delegate* unmanaged)&_getIsClassInitedFlagAddress; + callbacks[48] = (delegate* unmanaged)&_getStaticBaseAddress; + callbacks[49] = (delegate* unmanaged)&_getClassSize; + callbacks[50] = (delegate* unmanaged)&_getHeapClassSize; + callbacks[51] = (delegate* unmanaged)&_canAllocateOnStack; + callbacks[52] = (delegate* unmanaged)&_getClassAlignmentRequirement; + callbacks[53] = (delegate* unmanaged)&_getClassGClayout; + callbacks[54] = (delegate* unmanaged)&_getClassNumInstanceFields; + callbacks[55] = (delegate* unmanaged)&_getFieldInClass; + callbacks[56] = (delegate* unmanaged)&_getTypeLayout; + 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)&_getRuntimeTypePointer; + callbacks[66] = (delegate* unmanaged)&_isObjectImmutable; + callbacks[67] = (delegate* unmanaged)&_getStringChar; + callbacks[68] = (delegate* unmanaged)&_getObjectType; + callbacks[69] = (delegate* unmanaged)&_getReadyToRunHelper; + callbacks[70] = (delegate* unmanaged)&_getReadyToRunDelegateCtorHelper; + callbacks[71] = (delegate* unmanaged)&_initClass; + callbacks[72] = (delegate* unmanaged)&_classMustBeLoadedBeforeCodeIsRun; + callbacks[73] = (delegate* unmanaged)&_getBuiltinClass; + callbacks[74] = (delegate* unmanaged)&_getTypeForPrimitiveValueClass; + callbacks[75] = (delegate* unmanaged)&_getTypeForPrimitiveNumericClass; + callbacks[76] = (delegate* unmanaged)&_canCast; + callbacks[77] = (delegate* unmanaged)&_compareTypesForCast; + callbacks[78] = (delegate* unmanaged)&_compareTypesForEquality; + callbacks[79] = (delegate* unmanaged)&_isMoreSpecificType; + callbacks[80] = (delegate* unmanaged)&_isEnum; + callbacks[81] = (delegate* unmanaged)&_getParentType; + callbacks[82] = (delegate* unmanaged)&_getChildType; + callbacks[83] = (delegate* unmanaged)&_isSDArray; + callbacks[84] = (delegate* unmanaged)&_getArrayRank; + callbacks[85] = (delegate* unmanaged)&_getArrayIntrinsicID; + callbacks[86] = (delegate* unmanaged)&_getArrayInitializationData; + callbacks[87] = (delegate* unmanaged)&_canAccessClass; + callbacks[88] = (delegate* unmanaged)&_printFieldName; + callbacks[89] = (delegate* unmanaged)&_getFieldClass; + callbacks[90] = (delegate* unmanaged)&_getFieldType; + callbacks[91] = (delegate* unmanaged)&_getFieldOffset; + callbacks[92] = (delegate* unmanaged)&_getFieldInfo; + callbacks[93] = (delegate* unmanaged)&_getThreadLocalFieldInfo; + callbacks[94] = (delegate* unmanaged)&_getThreadLocalStaticBlocksInfo; + callbacks[95] = (delegate* unmanaged)&_isFieldStatic; + callbacks[96] = (delegate* unmanaged)&_getArrayOrStringLength; + callbacks[97] = (delegate* unmanaged)&_getBoundaries; + callbacks[98] = (delegate* unmanaged)&_setBoundaries; + callbacks[99] = (delegate* unmanaged)&_getVars; + callbacks[100] = (delegate* unmanaged)&_setVars; + callbacks[101] = (delegate* unmanaged)&_reportRichMappings; + callbacks[102] = (delegate* unmanaged)&_allocateArray; + callbacks[103] = (delegate* unmanaged)&_freeArray; + callbacks[104] = (delegate* unmanaged)&_getArgNext; + callbacks[105] = (delegate* unmanaged)&_getArgType; + callbacks[106] = (delegate* unmanaged)&_getExactClasses; + callbacks[107] = (delegate* unmanaged)&_getArgClass; + callbacks[108] = (delegate* unmanaged)&_getHFAType; + callbacks[109] = (delegate* unmanaged)&_runWithErrorTrap; + callbacks[110] = (delegate* unmanaged)&_runWithSPMIErrorTrap; + callbacks[111] = (delegate* unmanaged)&_getEEInfo; + callbacks[112] = (delegate* unmanaged)&_getJitTimeLogFilename; + callbacks[113] = (delegate* unmanaged)&_getMethodDefFromMethod; + callbacks[114] = (delegate* unmanaged)&_printMethodName; + callbacks[115] = (delegate* unmanaged)&_getMethodNameFromMetadata; + callbacks[116] = (delegate* unmanaged)&_getMethodHash; + callbacks[117] = (delegate* unmanaged)&_getSystemVAmd64PassStructInRegisterDescriptor; + callbacks[118] = (delegate* unmanaged)&_getLoongArch64PassStructInRegisterFlags; + callbacks[119] = (delegate* unmanaged)&_getRISCV64PassStructInRegisterFlags; + callbacks[120] = (delegate* unmanaged)&_getThreadTLSIndex; + callbacks[121] = (delegate* unmanaged)&_getAddrOfCaptureThreadGlobal; + callbacks[122] = (delegate* unmanaged)&_getHelperFtn; + callbacks[123] = (delegate* unmanaged)&_getFunctionEntryPoint; + callbacks[124] = (delegate* unmanaged)&_getFunctionFixedEntryPoint; + callbacks[125] = (delegate* unmanaged)&_getMethodSync; + callbacks[126] = (delegate* unmanaged)&_getLazyStringLiteralHelper; + callbacks[127] = (delegate* unmanaged)&_embedModuleHandle; + callbacks[128] = (delegate* unmanaged)&_embedClassHandle; + callbacks[129] = (delegate* unmanaged)&_embedMethodHandle; + callbacks[130] = (delegate* unmanaged)&_embedFieldHandle; + callbacks[131] = (delegate* unmanaged)&_embedGenericHandle; + callbacks[132] = (delegate* unmanaged)&_getLocationOfThisType; + callbacks[133] = (delegate* unmanaged)&_getAddressOfPInvokeTarget; + callbacks[134] = (delegate* unmanaged)&_GetCookieForPInvokeCalliSig; + callbacks[135] = (delegate* unmanaged)&_canGetCookieForPInvokeCalliSig; + callbacks[136] = (delegate* unmanaged)&_getJustMyCodeHandle; + callbacks[137] = (delegate* unmanaged)&_GetProfilingHandle; + callbacks[138] = (delegate* unmanaged)&_getCallInfo; + callbacks[139] = (delegate* unmanaged)&_getClassDomainID; + callbacks[140] = (delegate* unmanaged)&_getStaticFieldContent; + callbacks[141] = (delegate* unmanaged)&_getObjectContent; + 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)&_GetDelegateCtor; + callbacks[149] = (delegate* unmanaged)&_MethodCompileComplete; + callbacks[150] = (delegate* unmanaged)&_getTailCallHelpers; + callbacks[151] = (delegate* unmanaged)&_convertPInvokeCalliToCall; + callbacks[152] = (delegate* unmanaged)&_notifyInstructionSetUsage; + callbacks[153] = (delegate* unmanaged)&_updateEntryPointForTailCall; + 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)&_recordCallSite; + callbacks[166] = (delegate* unmanaged)&_recordRelocation; + callbacks[167] = (delegate* unmanaged)&_getRelocTypeHint; + callbacks[168] = (delegate* unmanaged)&_getExpectedTargetArchitecture; + callbacks[169] = (delegate* unmanaged)&_getJitFlags; return (IntPtr)callbacks; } diff --git a/src/coreclr/tools/Common/JitInterface/ThunkGenerator/ThunkInput.txt b/src/coreclr/tools/Common/JitInterface/ThunkGenerator/ThunkInput.txt index e6892816cf410b..452d0917f4d5a6 100644 --- a/src/coreclr/tools/Common/JitInterface/ThunkGenerator/ThunkInput.txt +++ b/src/coreclr/tools/Common/JitInterface/ThunkGenerator/ThunkInput.txt @@ -199,6 +199,7 @@ FUNCTIONS CORINFO_CLASS_HANDLE getTypeInstantiationArgument(CORINFO_CLASS_HANDLE cls, unsigned index) size_t printClassName(CORINFO_CLASS_HANDLE cls, char* buffer, size_t bufferSize, size_t* pRequiredBufferSize) bool isValueClass(CORINFO_CLASS_HANDLE cls) + bool isBitwiseEquatable(CORINFO_CLASS_HANDLE cls) CorInfoInlineTypeCheck canInlineTypeCheck(CORINFO_CLASS_HANDLE cls, CorInfoInlineTypeCheckSource source) uint32_t getClassAttribs(CORINFO_CLASS_HANDLE cls) CORINFO_MODULE_HANDLE getClassModule(CORINFO_CLASS_HANDLE cls) diff --git a/src/coreclr/tools/Common/TypeSystem/IL/NativeAotILProvider.cs b/src/coreclr/tools/Common/TypeSystem/IL/NativeAotILProvider.cs index 83bc1647934bb4..d0b8a978763bbf 100644 --- a/src/coreclr/tools/Common/TypeSystem/IL/NativeAotILProvider.cs +++ b/src/coreclr/tools/Common/TypeSystem/IL/NativeAotILProvider.cs @@ -130,12 +130,6 @@ private static MethodIL TryGetPerInstantiationIntrinsicMethodIL(MethodDesc metho } } break; - case "RuntimeHelpers": - { - if (owningType.Namespace == "System.Runtime.CompilerServices") - return RuntimeHelpersIntrinsics.EmitIL(method); - } - break; case "Comparer`1": { if (methodName == "Create" && owningType.Namespace == "System.Collections.Generic") @@ -148,81 +142,12 @@ private static MethodIL TryGetPerInstantiationIntrinsicMethodIL(MethodDesc metho return ComparerIntrinsics.EmitEqualityComparerCreate(method); } break; - case "ComparerHelpers": - { - if (owningType.Namespace != "Internal.IntrinsicSupport") - return null; - - if (methodName == "EnumOnlyCompare") - { - //calls CompareTo for underlyingType to avoid boxing - - TypeDesc elementType = method.Instantiation[0]; - if (!elementType.IsEnum) - return null; - - TypeDesc underlyingType = elementType.UnderlyingType; - TypeDesc returnType = method.Context.GetWellKnownType(WellKnownType.Int32); - MethodDesc underlyingCompareToMethod = underlyingType.GetKnownMethod("CompareTo", - new MethodSignature( - MethodSignatureFlags.None, - genericParameterCount: 0, - returnType: returnType, - parameters: new TypeDesc[] {underlyingType})); - - ILEmitter emitter = new ILEmitter(); - var codeStream = emitter.NewCodeStream(); - - codeStream.EmitLdArga(0); - codeStream.EmitLdArg(1); - codeStream.Emit(ILOpcode.call, emitter.NewToken(underlyingCompareToMethod)); - codeStream.Emit(ILOpcode.ret); - - return emitter.Link(method); - } - } - break; case "EqualityComparerHelpers": { if (owningType.Namespace != "Internal.IntrinsicSupport") return null; - if (methodName == "EnumOnlyEquals") - { - // EnumOnlyEquals would basically like to do this: - // static bool EnumOnlyEquals(T x, T y) where T: struct => x == y; - // This is not legal though. - // We don't want to do this: - // static bool EnumOnlyEquals(T x, T y) where T: struct => x.Equals(y); - // Because it would box y. - // So we resort to some per-instantiation magic. - - TypeDesc elementType = method.Instantiation[0]; - if (!elementType.IsEnum) - return null; - - ILOpcode convInstruction; - if (((DefType)elementType).InstanceFieldSize.AsInt <= 4) - { - convInstruction = ILOpcode.conv_i4; - } - else - { - Debug.Assert(((DefType)elementType).InstanceFieldSize.AsInt == 8); - convInstruction = ILOpcode.conv_i8; - } - - return new ILStubMethodIL(method, new byte[] { - (byte)ILOpcode.ldarg_0, - (byte)convInstruction, - (byte)ILOpcode.ldarg_1, - (byte)convInstruction, - (byte)ILOpcode.prefix1, unchecked((byte)ILOpcode.ceq), - (byte)ILOpcode.ret, - }, - Array.Empty(), null); - } - else if (methodName == "GetComparerForReferenceTypesOnly") + if (methodName == "GetComparerForReferenceTypesOnly") { TypeDesc elementType = method.Instantiation[0]; if (!elementType.IsRuntimeDeterminedSubtype @@ -251,7 +176,8 @@ private static MethodIL TryGetPerInstantiationIntrinsicMethodIL(MethodDesc metho MethodDesc methodToCall; if (elementType.IsEnum) { - methodToCall = helperType.GetKnownMethod("EnumOnlyEquals", null).MakeInstantiatedMethod(elementType); + helperType = context.SystemModule.GetKnownType("System.Runtime.CompilerServices", "RuntimeHelpers"); + methodToCall = helperType.GetKnownMethod("EnumEquals", null).MakeInstantiatedMethod(elementType); } else if (elementType.IsNullable && ComparerIntrinsics.ImplementsIEquatable(elementType.Instantiation[0])) { diff --git a/src/coreclr/tools/Common/TypeSystem/IL/Stubs/RuntimeHelpersIntrinsics.cs b/src/coreclr/tools/Common/TypeSystem/IL/Stubs/RuntimeHelpersIntrinsics.cs deleted file mode 100644 index 3f44400161342d..00000000000000 --- a/src/coreclr/tools/Common/TypeSystem/IL/Stubs/RuntimeHelpersIntrinsics.cs +++ /dev/null @@ -1,109 +0,0 @@ -// 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 Internal.TypeSystem; - -using Debug = System.Diagnostics.Debug; - -namespace Internal.IL.Stubs -{ - /// - /// Provides method bodies for System.Runtime.CompilerServices.RuntimeHelpers intrinsics. - /// - public static class RuntimeHelpersIntrinsics - { - public static MethodIL EmitIL(MethodDesc method) - { - Debug.Assert(((MetadataType)method.OwningType).Name == "RuntimeHelpers"); - string methodName = method.Name; - - if (methodName == "GetMethodTable") - { - ILEmitter emit = new ILEmitter(); - ILCodeStream codeStream = emit.NewCodeStream(); - codeStream.EmitLdArg(0); - codeStream.Emit(ILOpcode.ldflda, emit.NewToken(method.Context.SystemModule.GetKnownType("System.Runtime.CompilerServices", "RawData").GetField("Data"))); - codeStream.EmitLdc(-method.Context.Target.PointerSize); - codeStream.Emit(ILOpcode.add); - codeStream.Emit(ILOpcode.ldind_i); - codeStream.Emit(ILOpcode.ret); - return emit.Link(method); - } - - // All the methods handled below are per-instantiation generic methods - if (method.Instantiation.Length != 1 || method.IsTypicalMethodDefinition) - return null; - - TypeDesc elementType = method.Instantiation[0]; - - // Fallback to non-intrinsic implementation for universal generics - if (elementType.IsCanonicalSubtype(CanonicalFormKind.Universal)) - return null; - - bool result; - if (methodName == "IsReferenceOrContainsReferences") - { - result = elementType.IsGCPointer || (elementType is DefType defType && defType.ContainsGCPointers); - } - else if (methodName == "IsReference") - { - result = elementType.IsGCPointer; - } - else if (methodName == "IsBitwiseEquatable") - { - // Ideally we could detect automatically whether a type is trivially equatable - // (i.e., its operator == could be implemented via memcmp). But for now we'll - // do the simple thing and hardcode the list of types we know fulfill this contract. - // n.b. This doesn't imply that the type's CompareTo method can be memcmp-implemented, - // as a method like CompareTo may need to take a type's signedness into account. - switch (elementType.UnderlyingType.Category) - { - case TypeFlags.Boolean: - case TypeFlags.Byte: - case TypeFlags.SByte: - case TypeFlags.Char: - case TypeFlags.UInt16: - case TypeFlags.Int16: - case TypeFlags.UInt32: - case TypeFlags.Int32: - case TypeFlags.UInt64: - case TypeFlags.Int64: - case TypeFlags.IntPtr: - case TypeFlags.UIntPtr: - result = true; - break; - default: - result = false; - if (elementType is MetadataType mdType) - { - if (mdType.Module == mdType.Context.SystemModule && - mdType.Namespace == "System.Text" && - mdType.Name == "Rune") - { - result = true; - } - else if (mdType.IsValueType && !ComparerIntrinsics.ImplementsIEquatable(mdType.GetTypeDefinition())) - { - // Value type that can use memcmp and that doesn't override object.Equals or implement IEquatable.Equals. - MethodDesc objectEquals = mdType.Context.GetWellKnownType(WellKnownType.Object).GetMethod("Equals", null); - result = - mdType.FindVirtualFunctionTargetMethodOnObjectType(objectEquals).OwningType != mdType && - ComparerIntrinsics.CanCompareValueTypeBits(mdType, objectEquals); - } - } - break; - } - } - else - { - return null; - } - - ILOpcode opcode = result ? ILOpcode.ldc_i4_1 : ILOpcode.ldc_i4_0; - - return new ILStubMethodIL(method, new byte[] { (byte)opcode, (byte)ILOpcode.ret }, Array.Empty(), Array.Empty()); - } - } -} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/ILCompiler.Compiler.csproj b/src/coreclr/tools/aot/ILCompiler.Compiler/ILCompiler.Compiler.csproj index f04589d2cf7eeb..686ecb9d8a5ec2 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/ILCompiler.Compiler.csproj +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/ILCompiler.Compiler.csproj @@ -250,7 +250,6 @@ IL\Stubs\MethodBaseGetCurrentMethodThunk.Sorting.cs - IL\Stubs\TypeGetTypeMethodThunk.cs diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/IL/ReadyToRunILProvider.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/IL/ReadyToRunILProvider.cs index c5e67286dc2001..14c1e9b4c3e954 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/IL/ReadyToRunILProvider.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/IL/ReadyToRunILProvider.cs @@ -75,11 +75,6 @@ private MethodIL TryGetIntrinsicMethodIL(MethodDesc method) if (mdType == null) return null; - if (mdType.Name == "RuntimeHelpers" && mdType.Namespace == "System.Runtime.CompilerServices") - { - return RuntimeHelpersIntrinsics.EmitIL(method); - } - if (mdType.Name == "Unsafe" && mdType.Namespace == "System.Runtime.CompilerServices") { return UnsafeIntrinsics.EmitIL(method); @@ -104,11 +99,6 @@ private MethodIL TryGetPerInstantiationIntrinsicMethodIL(MethodDesc method) if (mdType == null) return null; - if (mdType.Name == "RuntimeHelpers" && mdType.Namespace == "System.Runtime.CompilerServices") - { - return RuntimeHelpersIntrinsics.EmitIL(method); - } - if (mdType.Name == "Activator" && mdType.Namespace == "System") { return TryGetIntrinsicMethodILForActivator(method); diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ILCompiler.ReadyToRun.csproj b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ILCompiler.ReadyToRun.csproj index 21edfddd496058..59e88c48bac379 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ILCompiler.ReadyToRun.csproj +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ILCompiler.ReadyToRun.csproj @@ -47,7 +47,6 @@ - diff --git a/src/coreclr/tools/aot/jitinterface/jitinterface_generated.h b/src/coreclr/tools/aot/jitinterface/jitinterface_generated.h index 1fb8df9f2e0eb7..4a0d00bc034aa3 100644 --- a/src/coreclr/tools/aot/jitinterface/jitinterface_generated.h +++ b/src/coreclr/tools/aot/jitinterface/jitinterface_generated.h @@ -49,6 +49,7 @@ struct JitInterfaceCallbacks CORINFO_CLASS_HANDLE (* getTypeInstantiationArgument)(void * thisHandle, CorInfoExceptionClass** ppException, CORINFO_CLASS_HANDLE cls, unsigned index); size_t (* printClassName)(void * thisHandle, CorInfoExceptionClass** ppException, CORINFO_CLASS_HANDLE cls, char* buffer, size_t bufferSize, size_t* pRequiredBufferSize); bool (* isValueClass)(void * thisHandle, CorInfoExceptionClass** ppException, CORINFO_CLASS_HANDLE cls); + bool (* isBitwiseEquatable)(void * thisHandle, CorInfoExceptionClass** ppException, CORINFO_CLASS_HANDLE cls); CorInfoInlineTypeCheck (* canInlineTypeCheck)(void * thisHandle, CorInfoExceptionClass** ppException, CORINFO_CLASS_HANDLE cls, CorInfoInlineTypeCheckSource source); uint32_t (* getClassAttribs)(void * thisHandle, CorInfoExceptionClass** ppException, CORINFO_CLASS_HANDLE cls); CORINFO_MODULE_HANDLE (* getClassModule)(void * thisHandle, CorInfoExceptionClass** ppException, CORINFO_CLASS_HANDLE cls); @@ -570,6 +571,15 @@ class JitInterfaceWrapper : public ICorJitInfo return temp; } + virtual bool isBitwiseEquatable( + CORINFO_CLASS_HANDLE cls) +{ + CorInfoExceptionClass* pException = nullptr; + bool temp = _callbacks->isBitwiseEquatable(_thisHandle, &pException, cls); + if (pException != nullptr) throw pException; + return temp; +} + virtual CorInfoInlineTypeCheck canInlineTypeCheck( CORINFO_CLASS_HANDLE cls, CorInfoInlineTypeCheckSource source) diff --git a/src/coreclr/tools/superpmi/superpmi-shared/lwmlist.h b/src/coreclr/tools/superpmi/superpmi-shared/lwmlist.h index 6c6bbed9bd9650..57b82045115eb4 100644 --- a/src/coreclr/tools/superpmi/superpmi-shared/lwmlist.h +++ b/src/coreclr/tools/superpmi/superpmi-shared/lwmlist.h @@ -153,6 +153,7 @@ LWM(PrintClassName, DWORDLONG, Agnostic_PrintResult) LWM(PrintFieldName, DWORDLONG, Agnostic_PrintResult) LWM(PrintMethodName, DWORDLONG, Agnostic_PrintResult) LWM(IsValueClass, DWORDLONG, DWORD) +LWM(IsBitwiseEquatable, DWORDLONG, DWORD) LWM(IsMoreSpecificType, DLDL, DWORD) LWM(IsEnum, DWORDLONG, DLD) LWM(PInvokeMarshalingRequired, MethodOrSigInfoValue, DWORD) diff --git a/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.cpp b/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.cpp index ad0f2c7dd6dae3..9bee2503c75598 100644 --- a/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.cpp +++ b/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.cpp @@ -1759,6 +1759,28 @@ bool MethodContext::repIsValueClass(CORINFO_CLASS_HANDLE cls) return value != 0; } +void MethodContext::recIsBitwiseEquatable(CORINFO_CLASS_HANDLE cls, bool result) +{ + if (IsBitwiseEquatable == nullptr) + IsBitwiseEquatable = new LightWeightMap(); + + DWORDLONG key = CastHandle(cls); + DWORD value = result ? 1 : 0; + IsBitwiseEquatable->Add(key, value); + DEBUG_REC(dmpIsBitwiseEquatable(key, value)); +} +void MethodContext::dmpIsBitwiseEquatable(DWORDLONG key, DWORD value) +{ + printf("IsBitwiseEquatable key cls-%016" PRIX64 ", value res-%u", key, value); +} +bool MethodContext::repIsBitwiseEquatable(CORINFO_CLASS_HANDLE cls) +{ + DWORDLONG key = CastHandle(cls); + DWORD value = LookupByKeyOrMiss(IsBitwiseEquatable, key, ": key %016" PRIX64 "", key); + DEBUG_REP(dmpIsBitwiseEquatable(key, value)); + return value != 0; +} + void MethodContext::recGetClassSize(CORINFO_CLASS_HANDLE cls, unsigned result) { if (GetClassSize == nullptr) @@ -3596,10 +3618,10 @@ void MethodContext::recGetThreadLocalStaticBlocksInfo(CORINFO_THREAD_STATIC_BLOC void MethodContext::dmpGetThreadLocalStaticBlocksInfo(DWORD key, const Agnostic_GetThreadLocalStaticBlocksInfo& value) { printf("GetThreadLocalStaticBlocksInfo key %u, tlsIndex-%s, " - ", tlsGetAddrFtnPtr-%016" PRIX64 ", tlsIndexObject - %016" PRIX64 + ", tlsGetAddrFtnPtr-%016" PRIX64 ", tlsIndexObject - %016" PRIX64 ", threadVarsSection - %016" PRIX64 ", offsetOfThreadLocalStoragePointer-%u, offsetOfMaxThreadStaticBlocks-%u" - ", offsetOfThreadStaticBlocks-%u, offsetOfGCDataPointer-%u", + ", offsetOfThreadStaticBlocks-%u, offsetOfGCDataPointer-%u", key, SpmiDumpHelper::DumpAgnostic_CORINFO_CONST_LOOKUP(value.tlsIndex).c_str(), value.tlsGetAddrFtnPtr, value.tlsIndexObject, value.threadVarsSection, value.offsetOfThreadLocalStoragePointer, value.offsetOfMaxThreadStaticBlocks, value.offsetOfThreadStaticBlocks, value.offsetOfGCDataPointer); @@ -3617,7 +3639,7 @@ void MethodContext::repGetThreadLocalStaticBlocksInfo(CORINFO_THREAD_STATIC_BLOC pInfo->tlsIndexObject = (void*)value.tlsIndexObject; pInfo->threadVarsSection = (void*)value.threadVarsSection; pInfo->offsetOfThreadLocalStoragePointer = value.offsetOfThreadLocalStoragePointer; - pInfo->offsetOfMaxThreadStaticBlocks = value.offsetOfMaxThreadStaticBlocks; + pInfo->offsetOfMaxThreadStaticBlocks = value.offsetOfMaxThreadStaticBlocks; pInfo->offsetOfThreadStaticBlocks = value.offsetOfThreadStaticBlocks; pInfo->offsetOfGCDataPointer = value.offsetOfGCDataPointer; } diff --git a/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.h b/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.h index 63c2f0191c3d99..e1e808130423fb 100644 --- a/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.h +++ b/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.h @@ -216,6 +216,10 @@ class MethodContext void dmpIsValueClass(DWORDLONG key, DWORD value); bool repIsValueClass(CORINFO_CLASS_HANDLE cls); + void recIsBitwiseEquatable(CORINFO_CLASS_HANDLE cls, bool result); + void dmpIsBitwiseEquatable(DWORDLONG key, DWORD value); + bool repIsBitwiseEquatable(CORINFO_CLASS_HANDLE cls); + void recGetClassSize(CORINFO_CLASS_HANDLE cls, unsigned result); void dmpGetClassSize(DWORDLONG key, DWORD val); unsigned repGetClassSize(CORINFO_CLASS_HANDLE cls); @@ -561,7 +565,7 @@ class MethodContext void recGetIsClassInitedFlagAddress(CORINFO_CLASS_HANDLE cls, CORINFO_CONST_LOOKUP* addr, int* offset, bool result); void dmpGetIsClassInitedFlagAddress(DWORDLONG key, const Agnostic_GetIsClassInitedFlagAddress& value); bool repGetIsClassInitedFlagAddress(CORINFO_CLASS_HANDLE cls, CORINFO_CONST_LOOKUP* addr, int* offset); - + void recGetStaticBaseAddress(CORINFO_CLASS_HANDLE cls, bool isGc, CORINFO_CONST_LOOKUP* addr, bool result); void dmpGetStaticBaseAddress(DLD key, const Agnostic_GetStaticBaseAddress& value); bool repGetStaticBaseAddress(CORINFO_CLASS_HANDLE cls, bool isGc, CORINFO_CONST_LOOKUP* addr); @@ -1133,6 +1137,7 @@ enum mcPackets Packet_GetRISCV64PassStructInRegisterFlags = 209, Packet_GetObjectContent = 210, Packet_GetTypeLayout = 211, + Packet_IsBitwiseEquatable = 212, }; void SetDebugDumpVariables(); diff --git a/src/coreclr/tools/superpmi/superpmi-shim-collector/icorjitinfo.cpp b/src/coreclr/tools/superpmi/superpmi-shim-collector/icorjitinfo.cpp index 48790166a53ebc..3d930e119a0108 100644 --- a/src/coreclr/tools/superpmi/superpmi-shim-collector/icorjitinfo.cpp +++ b/src/coreclr/tools/superpmi/superpmi-shim-collector/icorjitinfo.cpp @@ -460,6 +460,15 @@ bool interceptor_ICJI::isValueClass(CORINFO_CLASS_HANDLE cls) return temp; } +// Checks if type can be compared for equality as bytes. +bool interceptor_ICJI::isBitwiseEquatable(CORINFO_CLASS_HANDLE cls) +{ + mc->cr->AddCall("isBitwiseEquatable"); + bool temp = original_ICorJitInfo->isBitwiseEquatable(cls); + mc->recIsBitwiseEquatable(cls, temp); + return temp; +} + // Decides how the JIT should do the optimization to inline the check for // GetTypeFromHandle(handle) == obj.GetType() (for CORINFO_INLINE_TYPECHECK_SOURCE_VTABLE) // GetTypeFromHandle(X) == GetTypeFromHandle(Y) (for CORINFO_INLINE_TYPECHECK_SOURCE_TOKEN) diff --git a/src/coreclr/tools/superpmi/superpmi-shim-counter/icorjitinfo_generated.cpp b/src/coreclr/tools/superpmi/superpmi-shim-counter/icorjitinfo_generated.cpp index bc95548b696a5b..fbfea7fd1f5b3d 100644 --- a/src/coreclr/tools/superpmi/superpmi-shim-counter/icorjitinfo_generated.cpp +++ b/src/coreclr/tools/superpmi/superpmi-shim-counter/icorjitinfo_generated.cpp @@ -325,6 +325,13 @@ bool interceptor_ICJI::isValueClass( return original_ICorJitInfo->isValueClass(cls); } +bool interceptor_ICJI::isBitwiseEquatable( + CORINFO_CLASS_HANDLE cls) +{ + mcs->AddCall("isBitwiseEquatable"); + return original_ICorJitInfo->isBitwiseEquatable(cls); +} + CorInfoInlineTypeCheck interceptor_ICJI::canInlineTypeCheck( CORINFO_CLASS_HANDLE cls, CorInfoInlineTypeCheckSource source) diff --git a/src/coreclr/tools/superpmi/superpmi-shim-simple/icorjitinfo_generated.cpp b/src/coreclr/tools/superpmi/superpmi-shim-simple/icorjitinfo_generated.cpp index 1ec24e309490b3..e59b1072c34c81 100644 --- a/src/coreclr/tools/superpmi/superpmi-shim-simple/icorjitinfo_generated.cpp +++ b/src/coreclr/tools/superpmi/superpmi-shim-simple/icorjitinfo_generated.cpp @@ -287,6 +287,12 @@ bool interceptor_ICJI::isValueClass( return original_ICorJitInfo->isValueClass(cls); } +bool interceptor_ICJI::isBitwiseEquatable( + CORINFO_CLASS_HANDLE cls) +{ + return original_ICorJitInfo->isBitwiseEquatable(cls); +} + CorInfoInlineTypeCheck interceptor_ICJI::canInlineTypeCheck( CORINFO_CLASS_HANDLE cls, CorInfoInlineTypeCheckSource source) diff --git a/src/coreclr/tools/superpmi/superpmi/icorjitinfo.cpp b/src/coreclr/tools/superpmi/superpmi/icorjitinfo.cpp index f8f84b24cd6cd0..f9637f5c9722b2 100644 --- a/src/coreclr/tools/superpmi/superpmi/icorjitinfo.cpp +++ b/src/coreclr/tools/superpmi/superpmi/icorjitinfo.cpp @@ -394,6 +394,13 @@ bool MyICJI::isValueClass(CORINFO_CLASS_HANDLE cls) return jitInstance->mc->repIsValueClass(cls); } +// Checks if type can be compared for equality as bytes. +bool MyICJI::isBitwiseEquatable(CORINFO_CLASS_HANDLE cls) +{ + jitInstance->mc->cr->AddCall("isBitwiseEquatable"); + return jitInstance->mc->repIsBitwiseEquatable(cls); +} + // Decides how the JIT should do the optimization to inline the check for // GetTypeFromHandle(handle) == obj.GetType() (for CORINFO_INLINE_TYPECHECK_SOURCE_VTABLE) // GetTypeFromHandle(X) == GetTypeFromHandle(Y) (for CORINFO_INLINE_TYPECHECK_SOURCE_TOKEN) diff --git a/src/coreclr/vm/corelib.h b/src/coreclr/vm/corelib.h index 6e3dba6638e0f4..092aaf805b2338 100644 --- a/src/coreclr/vm/corelib.h +++ b/src/coreclr/vm/corelib.h @@ -657,13 +657,8 @@ DEFINE_CLASS(RTFIELD, Reflection, RtFieldInfo) DEFINE_METHOD(RTFIELD, GET_FIELDHANDLE, GetFieldHandle, IM_RetIntPtr) DEFINE_CLASS(RUNTIME_HELPERS, CompilerServices, RuntimeHelpers) -DEFINE_METHOD(RUNTIME_HELPERS, IS_REFERENCE_OR_CONTAINS_REFERENCES, IsReferenceOrContainsReferences, NoSig) -DEFINE_METHOD(RUNTIME_HELPERS, IS_BITWISE_EQUATABLE, IsBitwiseEquatable, NoSig) -DEFINE_METHOD(RUNTIME_HELPERS, GET_METHOD_TABLE, GetMethodTable, NoSig) DEFINE_METHOD(RUNTIME_HELPERS, GET_RAW_DATA, GetRawData, NoSig) DEFINE_METHOD(RUNTIME_HELPERS, GET_UNINITIALIZED_OBJECT, GetUninitializedObject, SM_Type_RetObj) -DEFINE_METHOD(RUNTIME_HELPERS, ENUM_EQUALS, EnumEquals, NoSig) -DEFINE_METHOD(RUNTIME_HELPERS, ENUM_COMPARE_TO, EnumCompareTo, NoSig) DEFINE_METHOD(RUNTIME_HELPERS, ALLOC_TAILCALL_ARG_BUFFER, AllocTailCallArgBuffer, SM_Int_IntPtr_RetIntPtr) DEFINE_METHOD(RUNTIME_HELPERS, GET_TAILCALL_INFO, GetTailCallInfo, NoSig) DEFINE_METHOD(RUNTIME_HELPERS, DISPATCH_TAILCALLS, DispatchTailCalls, NoSig) diff --git a/src/coreclr/vm/jitinterface.cpp b/src/coreclr/vm/jitinterface.cpp index 2dd8eaf5dac840..5e8abc7b72a5f3 100644 --- a/src/coreclr/vm/jitinterface.cpp +++ b/src/coreclr/vm/jitinterface.cpp @@ -3839,6 +3839,57 @@ bool CEEInfo::isValueClass(CORINFO_CLASS_HANDLE clsHnd) return ret; } +bool CEEInfo::isBitwiseEquatable(CORINFO_CLASS_HANDLE cls) +{ + CONTRACTL { + THROWS; + GC_TRIGGERS; + MODE_PREEMPTIVE; + } CONTRACTL_END; + + bool ret = false; + + JIT_TO_EE_TRANSITION(); + + TypeHandle typeHandle(cls); + MethodTable* methodTable = typeHandle.GetMethodTable(); + + if (methodTable->IsEnum()) + { + CorElementType elemType = typeHandle.AsMethodTable()->GetInternalCorElementType(); + typeHandle = TypeHandle(CoreLibBinder::GetElementType(elemType)); + methodTable = typeHandle.GetMethodTable(); + } + + if (methodTable == CoreLibBinder::GetClass(CLASS__BOOLEAN) + || methodTable == CoreLibBinder::GetClass(CLASS__BYTE) + || methodTable == CoreLibBinder::GetClass(CLASS__SBYTE) + || methodTable == CoreLibBinder::GetClass(CLASS__CHAR) + || methodTable == CoreLibBinder::GetClass(CLASS__INT16) + || methodTable == CoreLibBinder::GetClass(CLASS__UINT16) + || methodTable == CoreLibBinder::GetClass(CLASS__INT32) + || methodTable == CoreLibBinder::GetClass(CLASS__UINT32) + || methodTable == CoreLibBinder::GetClass(CLASS__INT64) + || methodTable == CoreLibBinder::GetClass(CLASS__UINT64) + || methodTable == CoreLibBinder::GetClass(CLASS__INTPTR) + || methodTable == CoreLibBinder::GetClass(CLASS__UINTPTR) + || methodTable == CoreLibBinder::GetClass(CLASS__RUNE)) + { + ret = true; + } + else if (methodTable->IsValueType() && CanCompareBitsOrUseFastGetHashCode(methodTable)) + { + // CanCompareBitsOrUseFastGetHashCode checks for an object.Equals override. + // We also need to check for an IEquatable implementation. + Instantiation inst(&typeHandle, 1); + ret = !typeHandle.CanCastTo(TypeHandle(CoreLibBinder::GetClass(CLASS__IEQUATABLEGENERIC)).Instantiate(inst)); + } + + EE_TO_JIT_TRANSITION(); + + return ret; +} + /*********************************************************************/ // Decides how the JIT should do the optimization to inline the check for // GetTypeFromHandle(handle) == obj.GetType() @@ -7246,244 +7297,6 @@ bool getILIntrinsicImplementationForInterlocked(MethodDesc * ftn, return true; } -bool IsBitwiseEquatable(TypeHandle typeHandle, MethodTable * methodTable) -{ - if (!methodTable->IsValueType() || - !CanCompareBitsOrUseFastGetHashCode(methodTable)) - { - return false; - } - - // CanCompareBitsOrUseFastGetHashCode checks for an object.Equals override. - // We also need to check for an IEquatable implementation. - Instantiation inst(&typeHandle, 1); - if (typeHandle.CanCastTo(TypeHandle(CoreLibBinder::GetClass(CLASS__IEQUATABLEGENERIC)).Instantiate(inst))) - { - return false; - } - - return true; -} - -bool getILIntrinsicImplementationForRuntimeHelpers(MethodDesc * ftn, - CORINFO_METHOD_INFO * methInfo) -{ - STANDARD_VM_CONTRACT; - - _ASSERTE(CoreLibBinder::IsClass(ftn->GetMethodTable(), CLASS__RUNTIME_HELPERS)); - - mdMethodDef tk = ftn->GetMemberDef(); - - if (tk == CoreLibBinder::GetMethod(METHOD__RUNTIME_HELPERS__IS_REFERENCE_OR_CONTAINS_REFERENCES)->GetMemberDef()) - { - _ASSERTE(ftn->HasMethodInstantiation()); - Instantiation inst = ftn->GetMethodInstantiation(); - - _ASSERTE(ftn->GetNumGenericMethodArgs() == 1); - TypeHandle typeHandle = inst[0]; - MethodTable * methodTable = typeHandle.GetMethodTable(); - - static const BYTE returnTrue[] = { CEE_LDC_I4_1, CEE_RET }; - static const BYTE returnFalse[] = { CEE_LDC_I4_0, CEE_RET }; - - if (!methodTable->IsValueType() || methodTable->ContainsPointers()) - { - methInfo->ILCode = const_cast(returnTrue); - } - else - { - methInfo->ILCode = const_cast(returnFalse); - } - - methInfo->ILCodeSize = sizeof(returnTrue); - methInfo->maxStack = 1; - methInfo->EHcount = 0; - methInfo->options = (CorInfoOptions)0; - return true; - } - - if (tk == CoreLibBinder::GetMethod(METHOD__RUNTIME_HELPERS__IS_BITWISE_EQUATABLE)->GetMemberDef()) - { - _ASSERTE(ftn->HasMethodInstantiation()); - Instantiation inst = ftn->GetMethodInstantiation(); - - _ASSERTE(ftn->GetNumGenericMethodArgs() == 1); - TypeHandle typeHandle = inst[0]; - MethodTable * methodTable = typeHandle.GetMethodTable(); - - static const BYTE returnTrue[] = { CEE_LDC_I4_1, CEE_RET }; - static const BYTE returnFalse[] = { CEE_LDC_I4_0, CEE_RET }; - - // Ideally we could detect automatically whether a type is trivially equatable - // (i.e., its operator == could be implemented via memcmp). The best we can do - // for now is hardcode a list of known supported types and then also include anything - // that doesn't provide its own object.Equals override / IEquatable implementation. - // n.b. This doesn't imply that the type's CompareTo method can be memcmp-implemented, - // as a method like CompareTo may need to take a type's signedness into account. - - if (methodTable == CoreLibBinder::GetClass(CLASS__BOOLEAN) - || methodTable == CoreLibBinder::GetClass(CLASS__BYTE) - || methodTable == CoreLibBinder::GetClass(CLASS__SBYTE) - || methodTable == CoreLibBinder::GetClass(CLASS__CHAR) - || methodTable == CoreLibBinder::GetClass(CLASS__INT16) - || methodTable == CoreLibBinder::GetClass(CLASS__UINT16) - || methodTable == CoreLibBinder::GetClass(CLASS__INT32) - || methodTable == CoreLibBinder::GetClass(CLASS__UINT32) - || methodTable == CoreLibBinder::GetClass(CLASS__INT64) - || methodTable == CoreLibBinder::GetClass(CLASS__UINT64) - || methodTable == CoreLibBinder::GetClass(CLASS__INTPTR) - || methodTable == CoreLibBinder::GetClass(CLASS__UINTPTR) - || methodTable == CoreLibBinder::GetClass(CLASS__RUNE) - || methodTable->IsEnum() - || IsBitwiseEquatable(typeHandle, methodTable)) - { - methInfo->ILCode = const_cast(returnTrue); - } - else - { - methInfo->ILCode = const_cast(returnFalse); - } - - methInfo->ILCodeSize = sizeof(returnTrue); - methInfo->maxStack = 1; - methInfo->EHcount = 0; - methInfo->options = (CorInfoOptions)0; - return true; - } - - if (tk == CoreLibBinder::GetMethod(METHOD__RUNTIME_HELPERS__GET_METHOD_TABLE)->GetMemberDef()) - { - mdToken tokRawData = CoreLibBinder::GetField(FIELD__RAW_DATA__DATA)->GetMemberDef(); - - // In the CLR, an object is laid out as follows. - // [ object_header || MethodTable* (64-bit pointer) || instance_data ] - // ^ ^-- ref .firstField points here - // `-- reference (type O) points here - // - // So essentially what we want to do is to turn an object reference (type O) into a - // native int&, then dereference it to get the MethodTable*. (Essentially, an object - // reference is a MethodTable**.) Per ECMA-335, Sec. III.1.5, we can add - // (but not subtract) a & and an int32 to produce a &. So we'll get a reference to - // .firstField (type &), then back up one pointer length to get a value of - // essentially type (MethodTable*)&. Both of these are legal GC-trackable references - // to , regardless of 's actual length. - - static BYTE ilcode[] = { CEE_LDARG_0, // stack contains [ O ] = - CEE_LDFLDA,0,0,0,0, // stack contains [ & ] = ref .firstField - CEE_LDC_I4_S,(BYTE)(-TARGET_POINTER_SIZE), // stack contains [ &, int32 ] = -IntPtr.Size - CEE_ADD, // stack contains [ & ] = ref .methodTablePtr - CEE_LDIND_I, // stack contains [ native int ] = .methodTablePtr - CEE_RET }; - - ilcode[2] = (BYTE)(tokRawData); - ilcode[3] = (BYTE)(tokRawData >> 8); - ilcode[4] = (BYTE)(tokRawData >> 16); - ilcode[5] = (BYTE)(tokRawData >> 24); - - methInfo->ILCode = const_cast(ilcode); - methInfo->ILCodeSize = sizeof(ilcode); - methInfo->maxStack = 2; - methInfo->EHcount = 0; - methInfo->options = (CorInfoOptions)0; - return true; - } - - if (tk == CoreLibBinder::GetMethod(METHOD__RUNTIME_HELPERS__ENUM_EQUALS)->GetMemberDef()) - { - // Normally we would follow the above pattern and unconditionally replace the IL, - // relying on generic type constraints to guarantee that it will only ever be instantiated - // on the type/size of argument we expect. - // - // However C#/CLR does not support restricting a generic type to be an Enum, so the best - // we can do is constrain it to be a value type. This is fine for run time, since we only - // ever create instantiations on 4 byte or less Enums. But during NGen we may compile instantiations - // on other value types (to be specific, every value type instatiation of EqualityComparer - // because of its TypeDependencyAttribute; here again we would like to restrict this to - // 4 byte or less Enums but cannot). - // - // This IL is invalid for those instantiations, and replacing it would lead to all sorts of - // errors at NGen time. So we only replace it for instantiations where it would be valid, - // leaving the others, which we should never execute, with the C# implementation of throwing. - - _ASSERTE(ftn->HasMethodInstantiation()); - Instantiation inst = ftn->GetMethodInstantiation(); - - _ASSERTE(inst.GetNumArgs() == 1); - CorElementType et = inst[0].GetVerifierCorElementType(); - if (et == ELEMENT_TYPE_I4 || - et == ELEMENT_TYPE_U4 || - et == ELEMENT_TYPE_I2 || - et == ELEMENT_TYPE_U2 || - et == ELEMENT_TYPE_I1 || - et == ELEMENT_TYPE_U1 || - et == ELEMENT_TYPE_I8 || - et == ELEMENT_TYPE_U8) - { - static const BYTE ilcode[] = { CEE_LDARG_0, CEE_LDARG_1, CEE_PREFIX1, (CEE_CEQ & 0xFF), CEE_RET }; - methInfo->ILCode = const_cast(ilcode); - methInfo->ILCodeSize = sizeof(ilcode); - methInfo->maxStack = 2; - methInfo->EHcount = 0; - methInfo->options = (CorInfoOptions)0; - return true; - } - } - else if (tk == CoreLibBinder::GetMethod(METHOD__RUNTIME_HELPERS__ENUM_COMPARE_TO)->GetMemberDef()) - { - // The comment above on why this is not an unconditional replacement. This case handles - // Enums backed by 8 byte values. - - _ASSERTE(ftn->HasMethodInstantiation()); - Instantiation inst = ftn->GetMethodInstantiation(); - - _ASSERTE(inst.GetNumArgs() == 1); - CorElementType et = inst[0].GetVerifierCorElementType(); - if (et == ELEMENT_TYPE_I4 || - et == ELEMENT_TYPE_U4 || - et == ELEMENT_TYPE_I2 || - et == ELEMENT_TYPE_U2 || - et == ELEMENT_TYPE_I1 || - et == ELEMENT_TYPE_U1 || - et == ELEMENT_TYPE_I8 || - et == ELEMENT_TYPE_U8) - { - static BYTE ilcode[8][9]; - - TypeHandle thUnderlyingType = CoreLibBinder::GetElementType(et); - - TypeHandle thIComparable = TypeHandle(CoreLibBinder::GetClass(CLASS__ICOMPARABLEGENERIC)).Instantiate(Instantiation(&thUnderlyingType, 1)); - - MethodDesc* pCompareToMD = thUnderlyingType.AsMethodTable()->GetMethodDescForInterfaceMethod( - thIComparable, CoreLibBinder::GetMethod(METHOD__ICOMPARABLEGENERIC__COMPARE_TO), TRUE /* throwOnConflict */); - - // Call CompareTo method on the primitive type - int tokCompareTo = pCompareToMD->GetMemberDef(); - - unsigned int index = (et - ELEMENT_TYPE_I1); - _ASSERTE(index < ARRAY_SIZE(ilcode)); - - ilcode[index][0] = CEE_LDARGA_S; - ilcode[index][1] = 0; - ilcode[index][2] = CEE_LDARG_1; - ilcode[index][3] = CEE_CALL; - ilcode[index][4] = (BYTE)(tokCompareTo); - ilcode[index][5] = (BYTE)(tokCompareTo >> 8); - ilcode[index][6] = (BYTE)(tokCompareTo >> 16); - ilcode[index][7] = (BYTE)(tokCompareTo >> 24); - ilcode[index][8] = CEE_RET; - - methInfo->ILCode = const_cast(ilcode[index]); - methInfo->ILCodeSize = sizeof(ilcode[index]); - methInfo->maxStack = 2; - methInfo->EHcount = 0; - methInfo->options = (CorInfoOptions)0; - return true; - } - } - - return false; -} - bool getILIntrinsicImplementationForActivator(MethodDesc* ftn, CORINFO_METHOD_INFO* methInfo, SigPointer* pSig) @@ -7627,10 +7440,6 @@ static void getMethodInfoHelper( { fILIntrinsic = getILIntrinsicImplementationForInterlocked(ftn, methInfo); } - else if (CoreLibBinder::IsClass(pMT, CLASS__RUNTIME_HELPERS)) - { - fILIntrinsic = getILIntrinsicImplementationForRuntimeHelpers(ftn, methInfo); - } else if (CoreLibBinder::IsClass(pMT, CLASS__ACTIVATOR)) { SigPointer localSig; 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 8c89e7c6b0edcc..f97a4b28a12709 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 @@ -135,7 +135,9 @@ public EnumComparer() { } // Used by the serialization engine. private EnumComparer(SerializationInfo info, StreamingContext context) { } - // public override int Compare(T x, T y) is runtime-specific + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public override int Compare(T x, T y) => + RuntimeHelpers.EnumCompareTo(x, y); // Equals method for the comparer itself. public override bool Equals([NotNullWhen(true)] object? obj) => diff --git a/src/libraries/System.Private.CoreLib/src/System/Collections/Generic/EqualityComparer.cs b/src/libraries/System.Private.CoreLib/src/System/Collections/Generic/EqualityComparer.cs index aedbfa953bf886..0ac33cee6b83d2 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Collections/Generic/EqualityComparer.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Collections/Generic/EqualityComparer.cs @@ -249,7 +249,9 @@ public void GetObjectData(SerializationInfo info, StreamingContext context) } } - // public override bool Equals(T x, T y) is runtime-specific + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public override bool Equals(T x, T y) => + RuntimeHelpers.EnumEquals(x, y); [MethodImpl(MethodImplOptions.AggressiveInlining)] public override int GetHashCode(T obj) => diff --git a/src/libraries/System.Private.CoreLib/src/System/Reflection/InvokerEmitUtil.cs b/src/libraries/System.Private.CoreLib/src/System/Reflection/InvokerEmitUtil.cs index 0dac01f77a0e42..c5ffc1f6adf37f 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Reflection/InvokerEmitUtil.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Reflection/InvokerEmitUtil.cs @@ -193,7 +193,7 @@ public static MethodInfo Type_GetTypeFromHandle() => #if MONO private static MethodInfo? s_DisableInline; public static MethodInfo DisableInline() => - s_DisableInline ??= typeof(System.Runtime.CompilerServices.JitHelpers).GetMethod(nameof(System.Runtime.CompilerServices.JitHelpers.DisableInline), BindingFlags.NonPublic | BindingFlags.Static)!; + s_DisableInline ??= typeof(System.Runtime.CompilerServices.RuntimeHelpers).GetMethod(nameof(System.Runtime.CompilerServices.RuntimeHelpers.DisableInline), BindingFlags.NonPublic | BindingFlags.Static)!; #else private static MethodInfo? s_NextCallReturnAddress; public static MethodInfo NextCallReturnAddress() => diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.cs index 47014ec5d718f5..04166e0dd1301f 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.cs @@ -99,6 +99,22 @@ internal static bool IsPrimitiveType(this CorElementType et) [Intrinsic] public static unsafe ReadOnlySpan CreateSpan(RuntimeFieldHandle fldHandle) => new ReadOnlySpan(GetSpanDataFrom(fldHandle, typeof(T).TypeHandle, out int length), length); + /// true if given type is reference type or value type that contains references + [Intrinsic] + public static bool IsReferenceOrContainsReferences() => IsReferenceOrContainsReferences(); + + /// true if given type is bitwise equatable (memcmp can be used for equality checking) + /// + /// Only use the result of this for Equals() comparison, not for CompareTo() comparison. + /// + [Intrinsic] + internal static bool IsBitwiseEquatable() => IsBitwiseEquatable(); + + [Intrinsic] + internal static bool EnumEquals(T x, T y) where T : struct, Enum => x.Equals(y); + + [Intrinsic] + internal static int EnumCompareTo(T x, T y) where T : struct, Enum => x.CompareTo(y); // The following intrinsics return true if input is a compile-time constant // Feel free to add more overloads on demand diff --git a/src/mono/System.Private.CoreLib/System.Private.CoreLib.csproj b/src/mono/System.Private.CoreLib/System.Private.CoreLib.csproj index 2dc26dbe71fdce..13c4436d905a03 100644 --- a/src/mono/System.Private.CoreLib/System.Private.CoreLib.csproj +++ b/src/mono/System.Private.CoreLib/System.Private.CoreLib.csproj @@ -250,7 +250,6 @@ - diff --git a/src/mono/System.Private.CoreLib/src/System/Collections/Generic/Comparer.Mono.cs b/src/mono/System.Private.CoreLib/src/System/Collections/Generic/Comparer.Mono.cs index 06044427a30e61..7547debc982c94 100644 --- a/src/mono/System.Private.CoreLib/src/System/Collections/Generic/Comparer.Mono.cs +++ b/src/mono/System.Private.CoreLib/src/System/Collections/Generic/Comparer.Mono.cs @@ -46,10 +46,4 @@ private static Comparer CreateComparer() return new ObjectComparer(); } } - - internal partial class EnumComparer - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public override int Compare(T x, T y) => JitHelpers.EnumCompareTo(x, y); - } } diff --git a/src/mono/System.Private.CoreLib/src/System/Collections/Generic/EqualityComparer.Mono.cs b/src/mono/System.Private.CoreLib/src/System/Collections/Generic/EqualityComparer.Mono.cs index 6d6bb681e1a9e7..3024e0ac8f47be 100644 --- a/src/mono/System.Private.CoreLib/src/System/Collections/Generic/EqualityComparer.Mono.cs +++ b/src/mono/System.Private.CoreLib/src/System/Collections/Generic/EqualityComparer.Mono.cs @@ -60,10 +60,4 @@ private static EqualityComparer CreateComparer() return new ObjectEqualityComparer(); } } - - public partial class EnumEqualityComparer - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public override bool Equals(T x, T y) => JitHelpers.EnumEquals(x, y); - } } diff --git a/src/mono/System.Private.CoreLib/src/System/Runtime/CompilerServices/JitHelpers.cs b/src/mono/System.Private.CoreLib/src/System/Runtime/CompilerServices/JitHelpers.cs deleted file mode 100644 index c60d5f6f733b34..00000000000000 --- a/src/mono/System.Private.CoreLib/src/System/Runtime/CompilerServices/JitHelpers.cs +++ /dev/null @@ -1,17 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -namespace System.Runtime.CompilerServices -{ - internal static class JitHelpers - { - [Intrinsic] - public static bool EnumEquals(T x, T y) where T : struct, Enum => x.Equals(y); - - [Intrinsic] - public static int EnumCompareTo(T x, T y) where T : struct, Enum => x.CompareTo(y); - - [Intrinsic] - internal static void DisableInline () => throw new NotImplementedException(); - } -} diff --git a/src/mono/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.Mono.cs b/src/mono/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.Mono.cs index 732ebb26d8922a..1174078dd77c5a 100644 --- a/src/mono/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.Mono.cs +++ b/src/mono/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.Mono.cs @@ -152,12 +152,6 @@ public static IntPtr AllocateTypeAssociatedMemory(Type type, int size) [Intrinsic] internal static ref byte GetRawData(this object obj) => ref obj.GetRawData(); - [Intrinsic] - public static bool IsReferenceOrContainsReferences() => IsReferenceOrContainsReferences(); - - [Intrinsic] - internal static bool IsBitwiseEquatable() => IsBitwiseEquatable(); - [Intrinsic] internal static bool ObjectHasComponentSize(object obj) => ObjectHasComponentSize(obj); @@ -227,5 +221,8 @@ private static extern unsafe IntPtr GetSpanDataFrom( [MethodImplAttribute(MethodImplOptions.InternalCall)] private static extern bool SufficientExecutionStack(); + + [Intrinsic] + internal static void DisableInline () => throw new NotImplementedException(); } } diff --git a/src/mono/mono/mini/interp/transform.c b/src/mono/mono/mini/interp/transform.c index 361baf3b141bed..82d4f52f4505b5 100644 --- a/src/mono/mono/mini/interp/transform.c +++ b/src/mono/mono/mini/interp/transform.c @@ -2406,6 +2406,80 @@ interp_handle_intrinsics (TransformData *td, MonoMethod *target_method, MonoClas td->ip += 5; return TRUE; + } else if (!strcmp (tm, "EnumEquals") || !strcmp (tm, "EnumCompareTo")) { + MonoGenericContext *ctx = mono_method_get_context (target_method); + g_assert (ctx); + g_assert (ctx->method_inst); + g_assert (ctx->method_inst->type_argc == 1); + g_assert (csignature->param_count == 2); + + MonoType *t = ctx->method_inst->type_argv [0]; + t = mini_get_underlying_type (t); + + if (t->type == MONO_TYPE_R4 || t->type == MONO_TYPE_R8) + return FALSE; + + gboolean is_i8 = (t->type == MONO_TYPE_I8 || t->type == MONO_TYPE_U8 || (TARGET_SIZEOF_VOID_P == 8 && (t->type == MONO_TYPE_I || t->type == MONO_TYPE_U))); + gboolean is_unsigned = (t->type == MONO_TYPE_U1 || t->type == MONO_TYPE_U2 || t->type == MONO_TYPE_U4 || t->type == MONO_TYPE_U8 || t->type == MONO_TYPE_U); + + gboolean is_compareto = strcmp (tm, "EnumCompareTo") == 0; + if (is_compareto) { + int locala, localb; + locala = create_interp_local (td, t); + localb = create_interp_local (td, t); + + // Save arguments + store_local (td, localb); + store_local (td, locala); + load_local (td, locala); + load_local (td, localb); + + if (t->type >= MONO_TYPE_BOOLEAN && t->type <= MONO_TYPE_U2) + { + interp_add_ins (td, MINT_SUB_I4); + td->sp -= 2; + interp_ins_set_sregs2 (td->last_ins, td->sp [0].local, td->sp [1].local); + push_simple_type (td, STACK_TYPE_I4); + interp_ins_set_dreg (td->last_ins, td->sp [-1].local); + } + else + { + // (a > b) + if (is_unsigned) + interp_add_ins (td, is_i8 ? MINT_CGT_UN_I8 : MINT_CGT_UN_I4); + else + interp_add_ins (td, is_i8 ? MINT_CGT_I8 : MINT_CGT_I4); + td->sp -= 2; + interp_ins_set_sregs2 (td->last_ins, td->sp [0].local, td->sp [1].local); + push_simple_type (td, STACK_TYPE_I4); + interp_ins_set_dreg (td->last_ins, td->sp [-1].local); + // (a < b) + load_local (td, locala); + load_local (td, localb); + if (is_unsigned) + interp_add_ins (td, is_i8 ? MINT_CLT_UN_I8 : MINT_CLT_UN_I4); + else + interp_add_ins (td, is_i8 ? MINT_CLT_I8 : MINT_CLT_I4); + td->sp -= 2; + interp_ins_set_sregs2 (td->last_ins, td->sp [0].local, td->sp [1].local); + push_simple_type (td, STACK_TYPE_I4); + interp_ins_set_dreg (td->last_ins, td->sp [-1].local); + // (a > b) - (a < b) + interp_add_ins (td, MINT_SUB_I4); + td->sp -= 2; + interp_ins_set_sregs2 (td->last_ins, td->sp [0].local, td->sp [1].local); + push_simple_type (td, STACK_TYPE_I4); + interp_ins_set_dreg (td->last_ins, td->sp [-1].local); + } + td->ip += 5; + return TRUE; + } else { + if (is_i8) { + *op = MINT_CEQ_I8; + } else { + *op = MINT_CEQ_I4; + } + } } } else if (in_corlib && !strcmp (klass_name_space, "System") && !strcmp (klass_name, "RuntimeMethodHandle") && !strcmp (tm, "GetFunctionPointer") && csignature->param_count == 1) { // We must intrinsify this method on interp so we don't return a pointer to native code entering interpreter @@ -2528,84 +2602,6 @@ interp_handle_intrinsics (TransformData *td, MonoMethod *target_method, MonoClas if (!strcmp (tm, "MemoryBarrier") && csignature->param_count == 0) *op = MINT_MONO_MEMORY_BARRIER; } else if (in_corlib && - !strcmp (klass_name_space, "System.Runtime.CompilerServices") && - !strcmp (klass_name, "JitHelpers") && - (!strcmp (tm, "EnumEquals") || !strcmp (tm, "EnumCompareTo"))) { - MonoGenericContext *ctx = mono_method_get_context (target_method); - g_assert (ctx); - g_assert (ctx->method_inst); - g_assert (ctx->method_inst->type_argc == 1); - g_assert (csignature->param_count == 2); - - MonoType *t = ctx->method_inst->type_argv [0]; - t = mini_get_underlying_type (t); - - if (t->type == MONO_TYPE_R4 || t->type == MONO_TYPE_R8) - return FALSE; - - gboolean is_i8 = (t->type == MONO_TYPE_I8 || t->type == MONO_TYPE_U8 || (TARGET_SIZEOF_VOID_P == 8 && (t->type == MONO_TYPE_I || t->type == MONO_TYPE_U))); - gboolean is_unsigned = (t->type == MONO_TYPE_U1 || t->type == MONO_TYPE_U2 || t->type == MONO_TYPE_U4 || t->type == MONO_TYPE_U8 || t->type == MONO_TYPE_U); - - gboolean is_compareto = strcmp (tm, "EnumCompareTo") == 0; - if (is_compareto) { - int locala, localb; - locala = create_interp_local (td, t); - localb = create_interp_local (td, t); - - // Save arguments - store_local (td, localb); - store_local (td, locala); - load_local (td, locala); - load_local (td, localb); - - if (t->type >= MONO_TYPE_BOOLEAN && t->type <= MONO_TYPE_U2) - { - interp_add_ins (td, MINT_SUB_I4); - td->sp -= 2; - interp_ins_set_sregs2 (td->last_ins, td->sp [0].local, td->sp [1].local); - push_simple_type (td, STACK_TYPE_I4); - interp_ins_set_dreg (td->last_ins, td->sp [-1].local); - } - else - { - // (a > b) - if (is_unsigned) - interp_add_ins (td, is_i8 ? MINT_CGT_UN_I8 : MINT_CGT_UN_I4); - else - interp_add_ins (td, is_i8 ? MINT_CGT_I8 : MINT_CGT_I4); - td->sp -= 2; - interp_ins_set_sregs2 (td->last_ins, td->sp [0].local, td->sp [1].local); - push_simple_type (td, STACK_TYPE_I4); - interp_ins_set_dreg (td->last_ins, td->sp [-1].local); - // (a < b) - load_local (td, locala); - load_local (td, localb); - if (is_unsigned) - interp_add_ins (td, is_i8 ? MINT_CLT_UN_I8 : MINT_CLT_UN_I4); - else - interp_add_ins (td, is_i8 ? MINT_CLT_I8 : MINT_CLT_I4); - td->sp -= 2; - interp_ins_set_sregs2 (td->last_ins, td->sp [0].local, td->sp [1].local); - push_simple_type (td, STACK_TYPE_I4); - interp_ins_set_dreg (td->last_ins, td->sp [-1].local); - // (a > b) - (a < b) - interp_add_ins (td, MINT_SUB_I4); - td->sp -= 2; - interp_ins_set_sregs2 (td->last_ins, td->sp [0].local, td->sp [1].local); - push_simple_type (td, STACK_TYPE_I4); - interp_ins_set_dreg (td->last_ins, td->sp [-1].local); - } - td->ip += 5; - return TRUE; - } else { - if (is_i8) { - *op = MINT_CEQ_I8; - } else { - *op = MINT_CEQ_I4; - } - } - } - else if (in_corlib && !strcmp ("System.Runtime.CompilerServices", klass_name_space) && !strcmp ("RuntimeFeature", klass_name)) { // NOTE: on the interpreter, use the C# code in System.Private.CoreLib for IsDynamicCodeSupported diff --git a/src/mono/mono/mini/intrinsics.c b/src/mono/mono/mini/intrinsics.c index 68dc78bd22bc4d..2688d30ab7d2aa 100644 --- a/src/mono/mono/mini/intrinsics.c +++ b/src/mono/mono/mini/intrinsics.c @@ -657,7 +657,7 @@ MONO_RESTORE_WARNING } static MonoInst* -emit_jit_helpers_intrinsics (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSignature *fsig, MonoInst **args) +emit_runtime_helpers_intrinsics (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSignature *fsig, MonoInst **args) { MonoInst *ins; int dreg; @@ -1058,7 +1058,7 @@ mini_emit_inst_for_method (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSign EMIT_NEW_TEMPLOAD (cfg, ins, span->inst_c0); return ins; } else - return NULL; + return emit_runtime_helpers_intrinsics (cfg, cmethod, fsig, args); } else if (cmethod->klass == mono_class_try_get_memory_marshal_class ()) { if (!strcmp (cmethod->name, "GetArrayDataReference")) { // Logic below works for both SZARRAY and MDARRAY @@ -2057,10 +2057,6 @@ mini_emit_inst_for_method (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSign !strcmp (cmethod_klass_name_space, "System.Runtime.CompilerServices") && !strcmp (cmethod_klass_name, "Unsafe")) { return emit_unsafe_intrinsics (cfg, cmethod, fsig, args); - } else if (in_corlib && - !strcmp (cmethod_klass_name_space, "System.Runtime.CompilerServices") && - !strcmp (cmethod_klass_name, "JitHelpers")) { - return emit_jit_helpers_intrinsics (cfg, cmethod, fsig, args); } else if (in_corlib && (strcmp (cmethod_klass_name_space, "System") == 0) && (strcmp (cmethod_klass_name, "Activator") == 0)) {