diff --git a/src/coreclr/System.Private.CoreLib/src/System/Collections/Generic/ComparerHelpers.cs b/src/coreclr/System.Private.CoreLib/src/System/Collections/Generic/ComparerHelpers.cs index f07e70deaf8d88..87c07da9cdb38b 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Collections/Generic/ComparerHelpers.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Collections/Generic/ComparerHelpers.cs @@ -46,44 +46,12 @@ internal static object CreateDefaultComparer(Type type) // The comparer for enums is specialized to avoid boxing. else if (type.IsEnum) { - result = TryCreateEnumComparer(runtimeType); + result = CreateInstanceForAnotherGenericParameter((RuntimeType)typeof(EnumComparer<>), runtimeType); } return result ?? CreateInstanceForAnotherGenericParameter((RuntimeType)typeof(ObjectComparer), runtimeType); } - /// - /// Creates the default for an enum type. - /// - /// The enum type to create the default comparer for. - private static object? TryCreateEnumComparer(RuntimeType enumType) - { - Debug.Assert(enumType != null); - Debug.Assert(enumType.IsEnum); - - // Explicitly call Enum.GetUnderlyingType here. Although GetTypeCode - // ends up doing this anyway, we end up avoiding an unnecessary P/Invoke - // and virtual method call. - TypeCode underlyingTypeCode = Type.GetTypeCode(Enum.GetUnderlyingType(enumType)); - - // Depending on the enum type, we need to special case the comparers so that we avoid boxing. - // Specialize differently for signed/unsigned types so we avoid problems with large numbers. - switch (underlyingTypeCode) - { - case TypeCode.SByte: - case TypeCode.Int16: - case TypeCode.Int32: - case TypeCode.Byte: - case TypeCode.UInt16: - case TypeCode.UInt32: - case TypeCode.Int64: - case TypeCode.UInt64: - return CreateInstanceForAnotherGenericParameter((RuntimeType)typeof(EnumComparer<>), enumType); - } - - return null; - } - /// /// Creates the default . /// @@ -98,15 +66,9 @@ internal static object CreateDefaultEqualityComparer(Type type) object? result = null; var runtimeType = (RuntimeType)type; - if (type == typeof(byte)) - { - // Specialize for byte so Array.IndexOf is faster. - result = new ByteEqualityComparer(); - } - else if (type == typeof(string)) + if (type == typeof(string)) { - // Specialize for string, as EqualityComparer.Default is on the startup path - result = new GenericEqualityComparer(); + return new GenericEqualityComparer(); } else if (type.IsAssignableTo(typeof(IEquatable<>).MakeGenericType(type))) { @@ -122,41 +84,10 @@ internal static object CreateDefaultEqualityComparer(Type type) else if (type.IsEnum) { // The equality comparer for enums is specialized to avoid boxing. - result = TryCreateEnumEqualityComparer(runtimeType); + result = CreateInstanceForAnotherGenericParameter((RuntimeType)typeof(EnumEqualityComparer<>), runtimeType); } return result ?? CreateInstanceForAnotherGenericParameter((RuntimeType)typeof(ObjectEqualityComparer), runtimeType); } - - /// - /// Creates the default for an enum type. - /// - /// The enum type to create the default equality comparer for. - private static object? TryCreateEnumEqualityComparer(RuntimeType enumType) - { - Debug.Assert(enumType != null); - Debug.Assert(enumType.IsEnum); - - // See the METHOD__JIT_HELPERS__UNSAFE_ENUM_CAST and METHOD__JIT_HELPERS__UNSAFE_ENUM_CAST_LONG cases in getILIntrinsicImplementation - // for how we cast the enum types to integral values in the comparer without boxing. - - TypeCode underlyingTypeCode = Type.GetTypeCode(Enum.GetUnderlyingType(enumType)); - - // Depending on the enum type, we need to special case the comparers so that we avoid boxing. - switch (underlyingTypeCode) - { - case TypeCode.Int32: - case TypeCode.UInt32: - case TypeCode.SByte: - case TypeCode.Byte: - case TypeCode.Int16: - case TypeCode.Int64: - case TypeCode.UInt64: - case TypeCode.UInt16: - return CreateInstanceForAnotherGenericParameter((RuntimeType)typeof(EnumEqualityComparer<>), enumType); - } - - return null; - } } } diff --git a/src/coreclr/jit/importercalls.cpp b/src/coreclr/jit/importercalls.cpp index 4ba9d9b342d79e..b4ea24fae523f6 100644 --- a/src/coreclr/jit/importercalls.cpp +++ b/src/coreclr/jit/importercalls.cpp @@ -7553,57 +7553,41 @@ CORINFO_CLASS_HANDLE Compiler::impGetSpecialIntrinsicExactReturnType(GenTreeCall CORINFO_SIG_INFO sig; info.compCompHnd->getMethodSig(methodHnd, &sig); assert(sig.sigInst.classInstCount == 1); + CORINFO_CLASS_HANDLE typeHnd = sig.sigInst.classInst[0]; assert(typeHnd != nullptr); - // Lookup can incorrect when we have __Canon as it won't appear - // to implement any interface types. - // - // And if we do not have a final type, devirt & inlining is - // unlikely to result in much simplification. - // - // We can use CORINFO_FLG_FINAL to screen out both of these cases. - const DWORD typeAttribs = info.compCompHnd->getClassAttribs(typeHnd); - bool isFinalType = ((typeAttribs & CORINFO_FLG_FINAL) != 0); - - if (!isFinalType) + CallArg* instParam = call->gtArgs.FindWellKnownArg(WellKnownArg::InstParam); + if (instParam != nullptr) { - CallArg* instParam = call->gtArgs.FindWellKnownArg(WellKnownArg::InstParam); - if (instParam != nullptr) + assert(instParam->GetNext() == nullptr); + CORINFO_CLASS_HANDLE hClass = gtGetHelperArgClassHandle(instParam->GetNode()); + if (hClass != NO_CLASS_HANDLE) { - assert(instParam->GetNext() == nullptr); - CORINFO_CLASS_HANDLE hClass = gtGetHelperArgClassHandle(instParam->GetNode()); - if (hClass != NO_CLASS_HANDLE) - { - hClass = getTypeInstantiationArgument(hClass, 0); - if ((info.compCompHnd->getClassAttribs(hClass) & CORINFO_FLG_FINAL) != 0) - { - typeHnd = hClass; - isFinalType = true; - } - } + typeHnd = getTypeInstantiationArgument(hClass, 0); } } - if (isFinalType) + if (ni == NI_System_Collections_Generic_EqualityComparer_get_Default) + { + result = info.compCompHnd->getDefaultEqualityComparerClass(typeHnd); + } + else + { + assert(ni == NI_System_Collections_Generic_Comparer_get_Default); + result = info.compCompHnd->getDefaultComparerClass(typeHnd); + } + + if (result != NO_CLASS_HANDLE) { - if (ni == NI_System_Collections_Generic_EqualityComparer_get_Default) - { - result = info.compCompHnd->getDefaultEqualityComparerClass(typeHnd); - } - else - { - assert(ni == NI_System_Collections_Generic_Comparer_get_Default); - result = info.compCompHnd->getDefaultComparerClass(typeHnd); - } JITDUMP("Special intrinsic for type %s: return type is %s\n", eeGetClassName(typeHnd), result != nullptr ? eeGetClassName(result) : "unknown"); } else { - JITDUMP("Special intrinsic for type %s: type not final, so deferring opt\n", eeGetClassName(typeHnd)); + JITDUMP("Special intrinsic for type %s: type undetermined, so deferring opt\n", + eeGetClassName(typeHnd)); } - break; } diff --git a/src/coreclr/vm/corelib.h b/src/coreclr/vm/corelib.h index 0fa94138ed46c9..6e3dba6638e0f4 100644 --- a/src/coreclr/vm/corelib.h +++ b/src/coreclr/vm/corelib.h @@ -1155,7 +1155,6 @@ DEFINE_METHOD(UTF8BUFFERMARSHALER, CONVERT_TO_MANAGED, ConvertToManaged, NoSig) // Classes referenced in EqualityComparer.Default optimization -DEFINE_CLASS(BYTE_EQUALITYCOMPARER, CollectionsGeneric, ByteEqualityComparer) DEFINE_CLASS(ENUM_EQUALITYCOMPARER, CollectionsGeneric, EnumEqualityComparer`1) DEFINE_CLASS(NULLABLE_EQUALITYCOMPARER, CollectionsGeneric, NullableEqualityComparer`1) DEFINE_CLASS(GENERIC_EQUALITYCOMPARER, CollectionsGeneric, GenericEqualityComparer`1) diff --git a/src/coreclr/vm/jitinterface.cpp b/src/coreclr/vm/jitinterface.cpp index bd8640ac47c975..8c336989ce0627 100644 --- a/src/coreclr/vm/jitinterface.cpp +++ b/src/coreclr/vm/jitinterface.cpp @@ -8792,55 +8792,31 @@ CORINFO_CLASS_HANDLE CEEInfo::getDefaultComparerClassHelper(CORINFO_CLASS_HANDLE // We need to find the appropriate instantiation Instantiation inst(&elemTypeHnd, 1); - // If T implements IComparable - if (elemTypeHnd.CanCastTo(TypeHandle(CoreLibBinder::GetClass(CLASS__ICOMPARABLEGENERIC)).Instantiate(inst))) - { - TypeHandle resultTh = ((TypeHandle)CoreLibBinder::GetClass(CLASS__GENERIC_COMPARER)).Instantiate(inst); - return CORINFO_CLASS_HANDLE(resultTh.GetMethodTable()); - } - // Nullable if (Nullable::IsNullableType(elemTypeHnd)) { Instantiation nullableInst = elemTypeHnd.AsMethodTable()->GetInstantiation(); - TypeHandle iequatable = TypeHandle(CoreLibBinder::GetClass(CLASS__IEQUATABLEGENERIC)).Instantiate(nullableInst); - if (nullableInst[0].CanCastTo(iequatable)) - { - TypeHandle resultTh = ((TypeHandle)CoreLibBinder::GetClass(CLASS__NULLABLE_COMPARER)).Instantiate(nullableInst); - return CORINFO_CLASS_HANDLE(resultTh.GetMethodTable()); - } + TypeHandle resultTh = ((TypeHandle)CoreLibBinder::GetClass(CLASS__NULLABLE_COMPARER)).Instantiate(nullableInst); + return CORINFO_CLASS_HANDLE(resultTh.GetMethodTable()); } // We need to special case the Enum comparers based on their underlying type to avoid boxing if (elemTypeHnd.IsEnum()) { - MethodTable* targetClass = NULL; - CorElementType normType = elemTypeHnd.GetVerifierCorElementType(); - - switch(normType) - { - case ELEMENT_TYPE_I1: - case ELEMENT_TYPE_I2: - case ELEMENT_TYPE_U1: - case ELEMENT_TYPE_U2: - case ELEMENT_TYPE_I4: - case ELEMENT_TYPE_U4: - case ELEMENT_TYPE_I8: - case ELEMENT_TYPE_U8: - { - targetClass = CoreLibBinder::GetClass(CLASS__ENUM_COMPARER); - break; - } + TypeHandle resultTh = ((TypeHandle)CoreLibBinder::GetClass(CLASS__ENUM_COMPARER)).Instantiate(inst); + return CORINFO_CLASS_HANDLE(resultTh.GetMethodTable()); + } - default: - break; - } + if (elemTypeHnd.IsCanonicalSubtype()) + { + return NULL; + } - if (targetClass != NULL) - { - TypeHandle resultTh = ((TypeHandle)targetClass->GetCanonicalMethodTable()).Instantiate(inst); - return CORINFO_CLASS_HANDLE(resultTh.GetMethodTable()); - } + // If T implements IComparable + if (elemTypeHnd.CanCastTo(TypeHandle(CoreLibBinder::GetClass(CLASS__ICOMPARABLEGENERIC)).Instantiate(inst))) + { + TypeHandle resultTh = ((TypeHandle)CoreLibBinder::GetClass(CLASS__GENERIC_COMPARER)).Instantiate(inst); + return CORINFO_CLASS_HANDLE(resultTh.GetMethodTable()); } // Default case @@ -8881,25 +8857,12 @@ CORINFO_CLASS_HANDLE CEEInfo::getDefaultEqualityComparerClassHelper(CORINFO_CLAS // And in compile.cpp's SpecializeEqualityComparer TypeHandle elemTypeHnd(elemType); - // Special case for byte - if (elemTypeHnd.AsMethodTable()->HasSameTypeDefAs(CoreLibBinder::GetClass(CLASS__ELEMENT_TYPE_U1))) - { - return CORINFO_CLASS_HANDLE(CoreLibBinder::GetClass(CLASS__BYTE_EQUALITYCOMPARER)); - } - // Mirrors the logic in BCL's CompareHelpers.CreateDefaultComparer // And in compile.cpp's SpecializeComparer // // We need to find the appropriate instantiation Instantiation inst(&elemTypeHnd, 1); - // If T implements IEquatable - if (elemTypeHnd.CanCastTo(TypeHandle(CoreLibBinder::GetClass(CLASS__IEQUATABLEGENERIC)).Instantiate(inst))) - { - TypeHandle resultTh = ((TypeHandle)CoreLibBinder::GetClass(CLASS__GENERIC_EQUALITYCOMPARER)).Instantiate(inst); - return CORINFO_CLASS_HANDLE(resultTh.GetMethodTable()); - } - // Nullable if (Nullable::IsNullableType(elemTypeHnd)) { @@ -8914,33 +8877,20 @@ CORINFO_CLASS_HANDLE CEEInfo::getDefaultEqualityComparerClassHelper(CORINFO_CLAS // to avoid boxing and call the correct versions of GetHashCode. if (elemTypeHnd.IsEnum()) { - MethodTable* targetClass = NULL; - CorElementType normType = elemTypeHnd.GetVerifierCorElementType(); - - switch(normType) - { - case ELEMENT_TYPE_I1: - case ELEMENT_TYPE_I2: - case ELEMENT_TYPE_U1: - case ELEMENT_TYPE_U2: - case ELEMENT_TYPE_I4: - case ELEMENT_TYPE_U4: - case ELEMENT_TYPE_I8: - case ELEMENT_TYPE_U8: - { - targetClass = CoreLibBinder::GetClass(CLASS__ENUM_EQUALITYCOMPARER); - break; - } + TypeHandle resultTh = ((TypeHandle)CoreLibBinder::GetClass(CLASS__ENUM_EQUALITYCOMPARER)).Instantiate(inst); + return CORINFO_CLASS_HANDLE(resultTh.GetMethodTable()); + } - default: - break; - } + if (elemTypeHnd.IsCanonicalSubtype()) + { + return NULL; + } - if (targetClass != NULL) - { - TypeHandle resultTh = ((TypeHandle)targetClass->GetCanonicalMethodTable()).Instantiate(inst); - return CORINFO_CLASS_HANDLE(resultTh.GetMethodTable()); - } + // If T implements IEquatable + if (elemTypeHnd.CanCastTo(TypeHandle(CoreLibBinder::GetClass(CLASS__IEQUATABLEGENERIC)).Instantiate(inst))) + { + TypeHandle resultTh = ((TypeHandle)CoreLibBinder::GetClass(CLASS__GENERIC_EQUALITYCOMPARER)).Instantiate(inst); + return CORINFO_CLASS_HANDLE(resultTh.GetMethodTable()); } // Default case 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 603c1603290678..6d6bb681e1a9e7 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 @@ -35,11 +35,7 @@ private static EqualityComparer CreateComparer() // IN mini_handle_call_res_devirt ///////////////////////////////////////////////// - if (t == typeof(byte)) - { - return (EqualityComparer)(object)(new ByteEqualityComparer()); - } - else if (t == typeof(string)) + if (t == typeof(string)) { // Specialize for string, as EqualityComparer.Default is on the startup path return (EqualityComparer)(object)(new GenericEqualityComparer()); 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 index 40d30c6001599d..c60d5f6f733b34 100644 --- a/src/mono/System.Private.CoreLib/src/System/Runtime/CompilerServices/JitHelpers.cs +++ b/src/mono/System.Private.CoreLib/src/System/Runtime/CompilerServices/JitHelpers.cs @@ -5,13 +5,11 @@ namespace System.Runtime.CompilerServices { internal static class JitHelpers { -#pragma warning disable IDE0060 [Intrinsic] - public static bool EnumEquals(T x, T y) where T : struct, Enum => throw new NotImplementedException(); + 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 => throw new NotImplementedException(); -#pragma warning restore IDE0060 + 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/mono/mini/interp/transform.c b/src/mono/mono/mini/interp/transform.c index f416818ca73589..d36e400316a359 100644 --- a/src/mono/mono/mini/interp/transform.c +++ b/src/mono/mono/mini/interp/transform.c @@ -2536,7 +2536,10 @@ interp_handle_intrinsics (TransformData *td, MonoMethod *target_method, MonoClas MonoType *t = ctx->method_inst->type_argv [0]; t = mini_get_underlying_type (t); - gboolean is_i8 = (t->type == MONO_TYPE_I8 || t->type == MONO_TYPE_U8); + 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; diff --git a/src/mono/mono/mini/intrinsics.c b/src/mono/mono/mini/intrinsics.c index 76dbabbd4018d9..68dc78bd22bc4d 100644 --- a/src/mono/mono/mini/intrinsics.c +++ b/src/mono/mono/mini/intrinsics.c @@ -672,10 +672,10 @@ emit_jit_helpers_intrinsics (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSi t = ctx->method_inst->type_argv [0]; t = mini_get_underlying_type (t); - if (mini_is_gsharedvt_variable_type (t)) + if (mini_is_gsharedvt_variable_type (t) || t->type == MONO_TYPE_R4 || t->type == MONO_TYPE_R8) return NULL; - gboolean is_i8 = (t->type == MONO_TYPE_I8 || t->type == MONO_TYPE_U8); + 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); int cmp_op, ceq_op, cgt_op, clt_op; diff --git a/src/mono/mono/mini/mini.c b/src/mono/mono/mini/mini.c index 97eea8e486940a..76e7e5612f519a 100644 --- a/src/mono/mono/mini/mini.c +++ b/src/mono/mono/mini/mini.c @@ -4301,7 +4301,7 @@ mini_handle_call_res_devirt (MonoMethod *cmethod) // EqualityComparer.Default returns specific types depending on T // FIXME: Special case more types: byte, string, nullable, enum ? - if (mono_class_is_assignable_from_internal (inst, mono_class_from_mono_type_internal (param_type)) && param_type->type != MONO_TYPE_U1 && param_type->type != MONO_TYPE_STRING) { + if (mono_class_is_assignable_from_internal (inst, mono_class_from_mono_type_internal (param_type)) && param_type->type != MONO_TYPE_STRING) { MonoClass *gcomparer_inst; memset (&ctx, 0, sizeof (ctx)); diff --git a/src/tests/JIT/opt/Devirtualization/Comparer_get_Default.cs b/src/tests/JIT/opt/Devirtualization/Comparer_get_Default.cs index 6a8b09163d7765..1b30afb2b58178 100644 --- a/src/tests/JIT/opt/Devirtualization/Comparer_get_Default.cs +++ b/src/tests/JIT/opt/Devirtualization/Comparer_get_Default.cs @@ -12,71 +12,85 @@ public class Program { private static int s_ReturnCode = 100; - private static void AssertEquals(T expected, T actual, [CallerLineNumber] int line = 0) + private static void AssertEquals(T expected, T actual, string values = "", [CallerLineNumber] int line = 0) { if (!expected.Equals(actual)) { - Console.WriteLine($"{expected} != {actual}, L{line}"); + Console.WriteLine($"{values}{expected} != {actual}, L{line}"); + s_ReturnCode++; + } + } + + private static void AssertThrows(Action action, string values = "", [CallerLineNumber] int line = 0) where TException : Exception + { + try + { + action(); + Console.WriteLine($"{values}no {typeof(TException).FullName}, L{line}"); + s_ReturnCode++; + } + catch (Exception ex) + { + if (ex.GetType() == typeof(TException)) + return; + Console.WriteLine($"{values}{ex.GetType().FullName} != {typeof(TException).FullName}, L{line}"); s_ReturnCode++; } } private static void Compare_Boolean(Boolean a, Boolean b) => - AssertEquals(a.CompareTo(b), Comparer.Default.Compare(a, b)); + AssertEquals(a.CompareTo(b), Comparer.Default.Compare(a, b), $"({a}; {b}): "); private static void Compare_Byte(Byte a, Byte b) => - AssertEquals(a.CompareTo(b), Comparer.Default.Compare(a, b)); + AssertEquals(a.CompareTo(b), Comparer.Default.Compare(a, b), $"({a}; {b}): "); private static void Compare_SByte(SByte a, SByte b) => - AssertEquals(a.CompareTo(b), Comparer.Default.Compare(a, b)); + AssertEquals(a.CompareTo(b), Comparer.Default.Compare(a, b), $"({a}; {b}): "); private static void Compare_Char(Char a, Char b) => - AssertEquals(a.CompareTo(b), Comparer.Default.Compare(a, b)); + AssertEquals(a.CompareTo(b), Comparer.Default.Compare(a, b), $"({a}; {b}): "); private static void Compare_UInt16(UInt16 a, UInt16 b) => - AssertEquals(a.CompareTo(b), Comparer.Default.Compare(a, b)); + AssertEquals(a.CompareTo(b), Comparer.Default.Compare(a, b), $"({a}; {b}): "); private static void Compare_Int16(Int16 a, Int16 b) => - AssertEquals(a.CompareTo(b), Comparer.Default.Compare(a, b)); + AssertEquals(a.CompareTo(b), Comparer.Default.Compare(a, b), $"({a}; {b}): "); private static void Compare_UInt32(UInt32 a, UInt32 b) => - AssertEquals(a.CompareTo(b), Comparer.Default.Compare(a, b)); + AssertEquals(a.CompareTo(b), Comparer.Default.Compare(a, b), $"({a}; {b}): "); private static void Compare_Int32(Int32 a, Int32 b) => - AssertEquals(a.CompareTo(b), Comparer.Default.Compare(a, b)); + AssertEquals(a.CompareTo(b), Comparer.Default.Compare(a, b), $"({a}; {b}): "); private static void Compare_Int64(Int64 a, Int64 b) => - AssertEquals(a.CompareTo(b), Comparer.Default.Compare(a, b)); + AssertEquals(a.CompareTo(b), Comparer.Default.Compare(a, b), $"({a}; {b}): "); private static void Compare_UInt64(UInt64 a, UInt64 b) => - AssertEquals(a.CompareTo(b), Comparer.Default.Compare(a, b)); + AssertEquals(a.CompareTo(b), Comparer.Default.Compare(a, b), $"({a}; {b}): "); private static void Compare_IntPtr(IntPtr a, IntPtr b) => - AssertEquals(a.CompareTo(b), Comparer.Default.Compare(a, b)); + AssertEquals(a.CompareTo(b), Comparer.Default.Compare(a, b), $"({a}; {b}): "); private static void Compare_UIntPtr(UIntPtr a, UIntPtr b) => - AssertEquals(a.CompareTo(b), Comparer.Default.Compare(a, b)); + AssertEquals(a.CompareTo(b), Comparer.Default.Compare(a, b), $"({a}; {b}): "); private static void Compare_nint(nint a, nint b) => - AssertEquals(a.CompareTo(b), Comparer.Default.Compare(a, b)); + AssertEquals(a.CompareTo(b), Comparer.Default.Compare(a, b), $"({a}; {b}): "); private static void Compare_nuint(nuint a, nuint b) => - AssertEquals(a.CompareTo(b), Comparer.Default.Compare(a, b)); + AssertEquals(a.CompareTo(b), Comparer.Default.Compare(a, b), $"({a}; {b}): "); private static void Compare_Enum_Int32(MethodImplOptions a, MethodImplOptions b) => - AssertEquals(a.CompareTo(b), Comparer.Default.Compare(a, b)); + AssertEquals(a.CompareTo(b), Comparer.Default.Compare(a, b), $"({a}; {b}): "); private static void Compare_Enum_Byte(Enum_byte a, Enum_byte b) => - AssertEquals(a.CompareTo(b), Comparer.Default.Compare(a, b)); + AssertEquals(a.CompareTo(b), Comparer.Default.Compare(a, b), $"({a}; {b}): "); private static void Compare_String(String a, String b) => - AssertEquals(a.CompareTo(b), Comparer.Default.Compare(a, b)); + AssertEquals(a.CompareTo(b), Comparer.Default.Compare(a, b), $"({a}; {b}): "); private static void Compare_DateTime(DateTime a, DateTime b) => - AssertEquals(a.CompareTo(b), Comparer.Default.Compare(a, b)); - - private static void Compare_Struct1(Struct1 a, Struct1 b) => - AssertEquals(a.CompareTo(b), Comparer.Default.Compare(a, b)); + AssertEquals(a.CompareTo(b), Comparer.Default.Compare(a, b), $"({a}; {b}): "); private static void Compare_Int32_Nullable(long? a, long? b) { @@ -86,26 +100,71 @@ private static void Compare_Int32_Nullable(long? a, long? b) expected = b.HasValue ? a.Value.CompareTo(b.Value) : 1; else expected = b.HasValue ? -1 : 0; - AssertEquals(expected, actual); + AssertEquals(expected, actual, $"({a}; {b}): "); + } + + private static void Compare_Enum_Int32_Nullable(MethodImplOptions? a, MethodImplOptions? b) + { + int actual = Comparer.Default.Compare(a, b); + int expected = 0; + if (a.HasValue) + expected = b.HasValue ? a.Value.CompareTo(b.Value) : 1; + else + expected = b.HasValue ? -1 : 0; + AssertEquals(expected, actual, $"({a}; {b}): "); } + private static void Compare_Struct1(Struct1 a, Struct1 b) => + AssertEquals(a.CompareTo(b), Comparer.Default.Compare(a, b), $"({a}; {b}): "); + + private static void Compare_Struct2(Struct2 a, Struct2 b) => + AssertThrows(() => Comparer.Default.Compare(a, b), $"({a}; {b}): "); + + private static void Compare_Struct1_Nullable(Struct1? a, Struct1? b) => + AssertEquals(((IComparable)a)?.CompareTo(b) ?? (b.HasValue ? -1 : 0), Comparer.Default.Compare(a, b), $"({a}; {b}): "); + + private static void Compare_Struct2_Nullable(Struct2? a, Struct2? b) + { + if (!a.HasValue && !b.HasValue) + AssertEquals(0, Comparer.Default.Compare(a, b), $"({a}; {b}): "); + else if (!a.HasValue) + AssertEquals(-1, Comparer.Default.Compare(a, b), $"({a}; {b}): "); + else if (!b.HasValue) + AssertEquals(1, Comparer.Default.Compare(a, b), $"({a}; {b}): "); + else + AssertThrows(() => Comparer.Default.Compare(a, b)); + } + + private static string PrintBits(T value) + { + ulong l = 0; + Unsafe.As(ref l) = value; + return $"({typeof(T).FullName}){l}"; + } + + private static void Compare_Double_Enum(DoubleEnum a, DoubleEnum b) => + AssertEquals(a.CompareTo(b), Comparer.Default.Compare(a, b), $"({PrintBits(a)}; {PrintBits(b)}): "); + + private static void Compare_Generic_Enum(TEnum a, TEnum b) where TEnum : Enum => + AssertEquals(a.CompareTo(b), Comparer.Default.Compare(a, b), $"({PrintBits(a)}; {PrintBits(b)}): "); + [Fact] public static int TestEntryPoint() { - long[] values = + long[] values = { -2, -1, 0, 1, 2, - sbyte.MinValue - 1, sbyte.MinValue, sbyte.MinValue + 1, + sbyte.MinValue - 1, sbyte.MinValue, sbyte.MinValue + 1, sbyte.MaxValue - 1, sbyte.MaxValue, sbyte.MaxValue + 1, byte.MaxValue - 1, byte.MaxValue, byte.MaxValue + 1, - short.MinValue, short.MinValue + 1, + short.MinValue, short.MinValue + 1, short.MaxValue - 1, short.MaxValue, short.MaxValue + 1, ushort.MaxValue - 1, ushort.MaxValue, ushort.MaxValue + 1, - int.MinValue, int.MinValue + 1, + int.MinValue, int.MinValue + 1, int.MaxValue - 1, int.MaxValue, int.MaxValue + 1L, uint.MaxValue - 1, uint.MaxValue, uint.MaxValue + 1L, - long.MinValue, long.MinValue + 1, - long.MaxValue - 1, long.MaxValue + long.MinValue, long.MinValue + 1, + long.MaxValue - 1, long.MaxValue, BitConverter.DoubleToInt64Bits(double.NaN) }; for (var i = 0; i < values.Length; i++) @@ -179,15 +238,66 @@ public static int TestEntryPoint() var enumByteB = Unsafe.As(ref b); Compare_Enum_Byte(enumByteA, enumByteB); - var structA = new Struct1 {a = a, b = b}; - var structB = new Struct1 {a = b, b = a}; - Compare_Struct1(structA, structB); - Compare_DateTime( new DateTime(Math.Clamp(a, DateTime.MinValue.Ticks, DateTime.MaxValue.Ticks)), new DateTime(Math.Clamp(b, DateTime.MinValue.Ticks, DateTime.MaxValue.Ticks))); Compare_Int32_Nullable(a, b); + Compare_Enum_Int32_Nullable(enumIntA, enumIntB); + + Compare_Int32_Nullable(null, b); + Compare_Enum_Int32_Nullable(null, enumIntB); + + Compare_Int32_Nullable(a, null); + Compare_Enum_Int32_Nullable(enumIntA, null); + + var structA = new Struct1 {a = a, b = b}; + var structB = new Struct1 {a = b, b = a}; + Compare_Struct1(structA, structB); + + var struct2A = new Struct2 {a = a, b = b}; + var struct2B = new Struct2 {a = b, b = a}; + Compare_Struct2(struct2A, struct2B); + + Compare_Struct1_Nullable(structA, structB); + Compare_Struct2_Nullable(struct2A, struct2B); + + Compare_Struct1_Nullable(null, structB); + Compare_Struct2_Nullable(null, struct2B); + + Compare_Struct1_Nullable(structA, null); + Compare_Struct2_Nullable(struct2A, null); + + Compare_Struct1_Nullable(null, null); + Compare_Struct2_Nullable(null, null); + + // workaround for: https://github.com/dotnet/roslyn/issues/68770 + static T Bitcast(long l) => Unsafe.As(ref l); + + var enumCharA = Bitcast(a); + var enumCharB = Bitcast(b); + Compare_Generic_Enum(enumCharA, enumCharB); + + var enumBoolA = Bitcast(a); + var enumBoolB = Bitcast(b); + Compare_Generic_Enum(enumBoolA, enumBoolB); + + var enumFloatA = Bitcast(a); + var enumFloatB = Bitcast(b); + Compare_Generic_Enum(enumFloatA, enumFloatB); + + var enumDoubleA = Bitcast(a); + var enumDoubleB = Bitcast(b); + Compare_Generic_Enum(enumDoubleA, enumDoubleB); + Compare_Double_Enum(enumDoubleA, enumDoubleB); + + var enumIntPtrA = Bitcast(a); + var enumIntPtrB = Bitcast(b); + Compare_Generic_Enum(enumIntPtrA, enumIntPtrB); + + var enumUIntPtrA = Bitcast(a); + var enumUIntPtrB = Bitcast(b); + Compare_Generic_Enum(enumUIntPtrA, enumUIntPtrB); } } @@ -208,26 +318,111 @@ public static int TestEntryPoint() Compare_Int32_Nullable(1, null); Compare_Int32_Nullable(null, -1); Compare_Int32_Nullable(-1, null); + Compare_Enum_Int32_Nullable(null, null); + GenericsTest(); GetTypeTests(); GetHashCodeTests(); return s_ReturnCode; } + private static void GenericsTest() + { + AssertEquals(true, Test()); + + [MethodImpl(MethodImplOptions.NoInlining)] + static bool Test() + { + return EqualityComparer>.Default.Equals(default, default); + } + } + private static void GetTypeTests() { + AssertEquals("System.Collections.Generic.GenericComparer`1[System.Byte]", Comparer.Default.GetType().ToString()); AssertEquals("System.Collections.Generic.GenericComparer`1[System.Int32]", Comparer.Default.GetType().ToString()); AssertEquals("System.Collections.Generic.GenericComparer`1[System.String]", Comparer.Default.GetType().ToString()); AssertEquals("System.Collections.Generic.GenericComparer`1[System.Guid]", Comparer.Default.GetType().ToString()); AssertEquals("System.Collections.Generic.EnumComparer`1[System.Runtime.CompilerServices.MethodImplOptions]", Comparer.Default.GetType().ToString()); - AssertEquals("System.Collections.Generic.NullableComparer`1[System.Byte]", Comparer.Default.GetType().ToString()); + AssertEquals("System.Collections.Generic.EnumComparer`1[CharEnum]", Comparer.Default.GetType().ToString()); + AssertEquals("System.Collections.Generic.EnumComparer`1[BoolEnum]", Comparer.Default.GetType().ToString()); + AssertEquals("System.Collections.Generic.EnumComparer`1[FloatEnum]", Comparer.Default.GetType().ToString()); + AssertEquals("System.Collections.Generic.EnumComparer`1[DoubleEnum]", Comparer.Default.GetType().ToString()); + AssertEquals("System.Collections.Generic.EnumComparer`1[IntPtrEnum]", Comparer.Default.GetType().ToString()); + AssertEquals("System.Collections.Generic.EnumComparer`1[UIntPtrEnum]", Comparer.Default.GetType().ToString()); AssertEquals("System.Collections.Generic.ObjectComparer`1[Struct1]", Comparer.Default.GetType().ToString()); - + AssertEquals("System.Collections.Generic.ObjectComparer`1[Struct2]", Comparer.Default.GetType().ToString()); + AssertEquals("System.Collections.Generic.GenericComparer`1[StructGeneric`1[System.Int32]]", Comparer>.Default.GetType().ToString()); + AssertEquals("System.Collections.Generic.GenericComparer`1[StructGeneric`1[System.String]]", Comparer>.Default.GetType().ToString()); + AssertEquals("System.Collections.Generic.ObjectComparer`1[StructGenericString`1[System.String]]", Comparer>.Default.GetType().ToString()); + AssertEquals("System.Collections.Generic.ObjectComparer`1[StructGenericString`1[System.Object]]", Comparer>.Default.GetType().ToString()); + AssertEquals("System.Collections.Generic.GenericComparer`1[StructGeneric`1[StructGeneric`1[System.Int32]]]", Comparer>>.Default.GetType().ToString()); + AssertEquals("System.Collections.Generic.GenericComparer`1[StructGeneric`1[StructGeneric`1[System.String]]]", Comparer>>.Default.GetType().ToString()); + AssertEquals("System.Collections.Generic.ObjectComparer`1[StructGenericString`1[StructGeneric`1[System.String]]]", Comparer>>.Default.GetType().ToString()); + AssertEquals("System.Collections.Generic.ObjectComparer`1[StructGenericString`1[StructGeneric`1[System.Object]]]", Comparer>>.Default.GetType().ToString()); + + AssertEquals("System.Collections.Generic.NullableComparer`1[System.Byte]", Comparer.Default.GetType().ToString()); + AssertEquals("System.Collections.Generic.NullableComparer`1[System.Int32]", Comparer.Default.GetType().ToString()); AssertEquals("System.Collections.Generic.NullableComparer`1[System.Runtime.CompilerServices.MethodImplOptions]", Comparer.Default.GetType().ToString()); - AssertEquals("System.Collections.Generic.NullableEqualityComparer`1[System.Runtime.CompilerServices.MethodImplOptions]", EqualityComparer.Default.GetType().ToString()); + AssertEquals("System.Collections.Generic.NullableComparer`1[CharEnum]", Comparer.Default.GetType().ToString()); + AssertEquals("System.Collections.Generic.NullableComparer`1[BoolEnum]", Comparer.Default.GetType().ToString()); + AssertEquals("System.Collections.Generic.NullableComparer`1[FloatEnum]", Comparer.Default.GetType().ToString()); + AssertEquals("System.Collections.Generic.NullableComparer`1[DoubleEnum]", Comparer.Default.GetType().ToString()); + AssertEquals("System.Collections.Generic.NullableComparer`1[IntPtrEnum]", Comparer.Default.GetType().ToString()); + AssertEquals("System.Collections.Generic.NullableComparer`1[UIntPtrEnum]", Comparer.Default.GetType().ToString()); AssertEquals("System.Collections.Generic.NullableComparer`1[Struct1]", Comparer.Default.GetType().ToString()); + AssertEquals("System.Collections.Generic.NullableComparer`1[Struct2]", Comparer.Default.GetType().ToString()); + AssertEquals("System.Collections.Generic.NullableComparer`1[StructGeneric`1[System.Int32]]", Comparer?>.Default.GetType().ToString()); + AssertEquals("System.Collections.Generic.NullableComparer`1[StructGeneric`1[System.String]]", Comparer?>.Default.GetType().ToString()); + AssertEquals("System.Collections.Generic.NullableComparer`1[StructGenericString`1[System.String]]", Comparer?>.Default.GetType().ToString()); + AssertEquals("System.Collections.Generic.NullableComparer`1[StructGenericString`1[System.Object]]", Comparer?>.Default.GetType().ToString()); + AssertEquals("System.Collections.Generic.NullableComparer`1[StructGeneric`1[StructGeneric`1[System.Int32]]]", Comparer>?>.Default.GetType().ToString()); + AssertEquals("System.Collections.Generic.NullableComparer`1[StructGeneric`1[StructGeneric`1[System.String]]]", Comparer>?>.Default.GetType().ToString()); + AssertEquals("System.Collections.Generic.NullableComparer`1[StructGenericString`1[StructGeneric`1[System.String]]]", Comparer>?>.Default.GetType().ToString()); + AssertEquals("System.Collections.Generic.NullableComparer`1[StructGenericString`1[StructGeneric`1[System.Object]]]", Comparer>?>.Default.GetType().ToString()); + + AssertEquals("System.Collections.Generic.GenericEqualityComparer`1[System.Byte]", EqualityComparer.Default.GetType().ToString()); + AssertEquals("System.Collections.Generic.GenericEqualityComparer`1[System.Int32]", EqualityComparer.Default.GetType().ToString()); + AssertEquals("System.Collections.Generic.GenericEqualityComparer`1[System.String]", EqualityComparer.Default.GetType().ToString()); + AssertEquals("System.Collections.Generic.GenericEqualityComparer`1[System.Guid]", EqualityComparer.Default.GetType().ToString()); + AssertEquals("System.Collections.Generic.EnumEqualityComparer`1[System.Runtime.CompilerServices.MethodImplOptions]", EqualityComparer.Default.GetType().ToString()); + AssertEquals("System.Collections.Generic.EnumEqualityComparer`1[CharEnum]", EqualityComparer.Default.GetType().ToString()); + AssertEquals("System.Collections.Generic.EnumEqualityComparer`1[BoolEnum]", EqualityComparer.Default.GetType().ToString()); + AssertEquals("System.Collections.Generic.EnumEqualityComparer`1[FloatEnum]", EqualityComparer.Default.GetType().ToString()); + AssertEquals("System.Collections.Generic.EnumEqualityComparer`1[DoubleEnum]", EqualityComparer.Default.GetType().ToString()); + AssertEquals("System.Collections.Generic.EnumEqualityComparer`1[IntPtrEnum]", EqualityComparer.Default.GetType().ToString()); + AssertEquals("System.Collections.Generic.EnumEqualityComparer`1[UIntPtrEnum]", EqualityComparer.Default.GetType().ToString()); + AssertEquals("System.Collections.Generic.ObjectEqualityComparer`1[Struct1]", EqualityComparer.Default.GetType().ToString()); + AssertEquals("System.Collections.Generic.ObjectEqualityComparer`1[Struct2]", EqualityComparer.Default.GetType().ToString()); + AssertEquals("System.Collections.Generic.GenericEqualityComparer`1[StructGeneric`1[System.Int32]]", EqualityComparer>.Default.GetType().ToString()); + AssertEquals("System.Collections.Generic.GenericEqualityComparer`1[StructGeneric`1[System.String]]", EqualityComparer>.Default.GetType().ToString()); + AssertEquals("System.Collections.Generic.ObjectEqualityComparer`1[StructGenericString`1[System.String]]", EqualityComparer>.Default.GetType().ToString()); + AssertEquals("System.Collections.Generic.ObjectEqualityComparer`1[StructGenericString`1[System.Object]]", EqualityComparer>.Default.GetType().ToString()); + AssertEquals("System.Collections.Generic.GenericEqualityComparer`1[StructGeneric`1[StructGeneric`1[System.Int32]]]", EqualityComparer>>.Default.GetType().ToString()); + AssertEquals("System.Collections.Generic.GenericEqualityComparer`1[StructGeneric`1[StructGeneric`1[System.String]]]", EqualityComparer>>.Default.GetType().ToString()); + AssertEquals("System.Collections.Generic.ObjectEqualityComparer`1[StructGenericString`1[StructGeneric`1[System.String]]]", EqualityComparer>>.Default.GetType().ToString()); + AssertEquals("System.Collections.Generic.ObjectEqualityComparer`1[StructGenericString`1[StructGeneric`1[System.Object]]]", EqualityComparer>>.Default.GetType().ToString()); + + AssertEquals("System.Collections.Generic.NullableEqualityComparer`1[System.Byte]", EqualityComparer.Default.GetType().ToString()); + AssertEquals("System.Collections.Generic.NullableEqualityComparer`1[System.Int32]", EqualityComparer.Default.GetType().ToString()); + AssertEquals("System.Collections.Generic.NullableEqualityComparer`1[System.Runtime.CompilerServices.MethodImplOptions]", EqualityComparer.Default.GetType().ToString()); + AssertEquals("System.Collections.Generic.NullableEqualityComparer`1[CharEnum]", EqualityComparer.Default.GetType().ToString()); + AssertEquals("System.Collections.Generic.NullableEqualityComparer`1[BoolEnum]", EqualityComparer.Default.GetType().ToString()); + AssertEquals("System.Collections.Generic.NullableEqualityComparer`1[FloatEnum]", EqualityComparer.Default.GetType().ToString()); + AssertEquals("System.Collections.Generic.NullableEqualityComparer`1[DoubleEnum]", EqualityComparer.Default.GetType().ToString()); + AssertEquals("System.Collections.Generic.NullableEqualityComparer`1[IntPtrEnum]", EqualityComparer.Default.GetType().ToString()); + AssertEquals("System.Collections.Generic.NullableEqualityComparer`1[UIntPtrEnum]", EqualityComparer.Default.GetType().ToString()); AssertEquals("System.Collections.Generic.NullableEqualityComparer`1[Struct1]", EqualityComparer.Default.GetType().ToString()); + AssertEquals("System.Collections.Generic.NullableEqualityComparer`1[Struct2]", EqualityComparer.Default.GetType().ToString()); + AssertEquals("System.Collections.Generic.NullableEqualityComparer`1[StructGeneric`1[System.Int32]]", EqualityComparer?>.Default.GetType().ToString()); + AssertEquals("System.Collections.Generic.NullableEqualityComparer`1[StructGeneric`1[System.String]]", EqualityComparer?>.Default.GetType().ToString()); + AssertEquals("System.Collections.Generic.NullableEqualityComparer`1[StructGenericString`1[System.String]]", EqualityComparer?>.Default.GetType().ToString()); + AssertEquals("System.Collections.Generic.NullableEqualityComparer`1[StructGenericString`1[System.Object]]", EqualityComparer?>.Default.GetType().ToString()); + AssertEquals("System.Collections.Generic.NullableEqualityComparer`1[StructGeneric`1[StructGeneric`1[System.Int32]]]", EqualityComparer>?>.Default.GetType().ToString()); + AssertEquals("System.Collections.Generic.NullableEqualityComparer`1[StructGeneric`1[StructGeneric`1[System.String]]]", EqualityComparer>?>.Default.GetType().ToString()); + AssertEquals("System.Collections.Generic.NullableEqualityComparer`1[StructGenericString`1[StructGeneric`1[System.String]]]", EqualityComparer>?>.Default.GetType().ToString()); + AssertEquals("System.Collections.Generic.NullableEqualityComparer`1[StructGenericString`1[StructGeneric`1[System.Object]]]", EqualityComparer>?>.Default.GetType().ToString()); } private static int GetHashCodeTests() { @@ -235,9 +430,13 @@ private static int GetHashCodeTests() return Comparer.Default.GetHashCode() + Comparer.Default.GetHashCode() + Comparer.Default.GetHashCode() + + Comparer.Default.GetHashCode() + Comparer.Default.GetHashCode() + Comparer.Default.GetHashCode() + - Comparer.Default.GetHashCode(); + Comparer.Default.GetHashCode() + + Comparer.Default.GetHashCode() + + Comparer.Default.GetHashCode() + + Comparer.Default.GetHashCode(); } } @@ -252,6 +451,33 @@ public struct Struct1 : IComparable public long b; public int CompareTo(object obj) { - return b.CompareTo(((Struct1) obj).b); + return obj is Struct1 str ? b.CompareTo(str.b) : 1; } } + +public struct Struct2 +{ + public long a; + public long b; +} + +public struct StructGeneric : IEquatable>, IComparable> +{ + public T t; + + public bool Equals(StructGeneric s) => EqualityComparer.Default.Equals(t, s.t); + public int CompareTo(StructGeneric s) => Comparer.Default.Compare(t, s.t); +} + +public struct StructGenericString : IEquatable, IComparable +{ + public T t; + + public bool Equals(string s) => s == t?.ToString(); + public int CompareTo(string s) => Comparer.Default.Compare(t?.ToString(), s); +} + +struct G : IEquatable> +{ + public bool Equals(G x) => false; +} diff --git a/src/tests/JIT/opt/Devirtualization/Comparer_get_Default.csproj b/src/tests/JIT/opt/Devirtualization/Comparer_get_Default.csproj index 669603b68d88e8..687f3d8b5c532e 100644 --- a/src/tests/JIT/opt/Devirtualization/Comparer_get_Default.csproj +++ b/src/tests/JIT/opt/Devirtualization/Comparer_get_Default.csproj @@ -5,4 +5,7 @@ + + + diff --git a/src/tests/JIT/opt/Devirtualization/UncommonEnums.il b/src/tests/JIT/opt/Devirtualization/UncommonEnums.il new file mode 100644 index 00000000000000..a3bd3874ff1000 --- /dev/null +++ b/src/tests/JIT/opt/Devirtualization/UncommonEnums.il @@ -0,0 +1,42 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +.assembly extern System.Runtime { .publickeytoken = (B0 3F 5F 7F 11 D5 0A 3A ) } + +.assembly UncommonEnums { } + +.class public auto ansi sealed CharEnum + extends [System.Runtime]System.Enum +{ + .field public specialname rtspecialname char value__ +} // end of class CharEnum + +.class public auto ansi sealed BoolEnum + extends [System.Runtime]System.Enum +{ + .field public specialname rtspecialname bool value__ +} // end of class BoolEnum + +.class public auto ansi sealed FloatEnum + extends [System.Runtime]System.Enum +{ + .field public specialname rtspecialname float32 value__ +} // end of class FloatEnum + +.class public auto ansi sealed DoubleEnum + extends [System.Runtime]System.Enum +{ + .field public specialname rtspecialname float64 value__ +} // end of class DoubleEnum + +.class public auto ansi sealed IntPtrEnum + extends [System.Runtime]System.Enum +{ + .field public specialname rtspecialname native int value__ +} // end of class IntPtrEnum + +.class public auto ansi sealed UIntPtrEnum + extends [System.Runtime]System.Enum +{ + .field public specialname rtspecialname native uint value__ +} // end of class UIntPtrEnum diff --git a/src/tests/JIT/opt/Devirtualization/UncommonEnums.ilproj b/src/tests/JIT/opt/Devirtualization/UncommonEnums.ilproj new file mode 100644 index 00000000000000..05fbf28a4e307c --- /dev/null +++ b/src/tests/JIT/opt/Devirtualization/UncommonEnums.ilproj @@ -0,0 +1,11 @@ + + + Library + true + BuildOnly + false + + + + +