未验证 提交 a08fec66 编写于 作者: E Egor Bogatov 提交者: GitHub

[RyuJIT] Devirtualize Comparer<T>.Default (#48160)

上级 3844c76b
......@@ -10,7 +10,7 @@ public abstract partial class Comparer<T> : IComparer, IComparer<T>
{
// To minimize generic instantiation overhead of creating the comparer per type, we keep the generic portion of the code as small
// as possible and define most of the creation logic in a non-generic class.
public static Comparer<T> Default { get; } = (Comparer<T>)ComparerHelpers.CreateDefaultComparer(typeof(T));
public static Comparer<T> Default { [Intrinsic] get; } = (Comparer<T>)ComparerHelpers.CreateDefaultComparer(typeof(T));
}
internal sealed partial class EnumComparer<T> : Comparer<T> where T : struct, Enum
......
......@@ -74,6 +74,7 @@ LWM(GetClassSize, DWORDLONG, DWORD)
LWM(GetHeapClassSize, DWORDLONG, DWORD)
LWM(CanAllocateOnStack, DWORDLONG, DWORD)
LWM(GetCookieForPInvokeCalliSig, GetCookieForPInvokeCalliSigValue, DLDL)
LWM(GetDefaultComparerClass, DWORDLONG, DWORDLONG)
LWM(GetDefaultEqualityComparerClass, DWORDLONG, DWORDLONG)
LWM(GetDelegateCtor, Agnostic_GetDelegateCtorIn, Agnostic_GetDelegateCtorOut)
LWM(GetEEInfo, DWORD, Agnostic_CORINFO_EE_INFO)
......
......@@ -3101,6 +3101,23 @@ CORINFO_METHOD_HANDLE MethodContext::repGetUnboxedEntry(CORINFO_METHOD_HANDLE ft
return (CORINFO_METHOD_HANDLE)(result.A);
}
void MethodContext::recGetDefaultComparerClass(CORINFO_CLASS_HANDLE cls, CORINFO_CLASS_HANDLE result)
{
if (GetDefaultComparerClass == nullptr)
GetDefaultComparerClass = new LightWeightMap<DWORDLONG, DWORDLONG>();
GetDefaultComparerClass->Add(CastHandle(cls), CastHandle(result));
}
void MethodContext::dmpGetDefaultComparerClass(DWORDLONG key, DWORDLONG value)
{
printf("GetDefaultComparerClass key cls-%016llX, value cls-%016llX", key, value);
}
CORINFO_CLASS_HANDLE MethodContext::repGetDefaultComparerClass(CORINFO_CLASS_HANDLE cls)
{
CORINFO_CLASS_HANDLE result = (CORINFO_CLASS_HANDLE)GetDefaultComparerClass->Get(CastHandle(cls));
return result;
}
void MethodContext::recGetDefaultEqualityComparerClass(CORINFO_CLASS_HANDLE cls, CORINFO_CLASS_HANDLE result)
{
if (GetDefaultEqualityComparerClass == nullptr)
......
......@@ -401,6 +401,10 @@ public:
void dmpGetUnboxedEntry(DWORDLONG key, DLD value);
CORINFO_METHOD_HANDLE repGetUnboxedEntry(CORINFO_METHOD_HANDLE ftn, bool* requiresInstMethodTableArg);
void recGetDefaultComparerClass(CORINFO_CLASS_HANDLE cls, CORINFO_CLASS_HANDLE result);
void dmpGetDefaultComparerClass(DWORDLONG key, DWORDLONG value);
CORINFO_CLASS_HANDLE repGetDefaultComparerClass(CORINFO_CLASS_HANDLE cls);
void recGetDefaultEqualityComparerClass(CORINFO_CLASS_HANDLE cls, CORINFO_CLASS_HANDLE result);
void dmpGetDefaultEqualityComparerClass(DWORDLONG key, DWORDLONG value);
CORINFO_CLASS_HANDLE repGetDefaultEqualityComparerClass(CORINFO_CLASS_HANDLE cls);
......@@ -867,7 +871,7 @@ private:
};
// ********************* Please keep this up-to-date to ease adding more ***************
// Highest packet number: 187
// Highest packet number: 188
// *************************************************************************************
enum mcPackets
{
......@@ -943,6 +947,7 @@ enum mcPackets
Packet_GetIntConfigValue = 151, // Added 2/12/2015
Packet_GetStringConfigValue = 152, // Added 2/12/2015
Packet_GetCookieForPInvokeCalliSig = 48,
Packet_GetDefaultComparerClass = 188, // Added 2/10/2021
Packet_GetDefaultEqualityComparerClass = 162, // Added 9/24/2017
Packet_GetDelegateCtor = 49,
Packet_GetEEInfo = 50,
......
......@@ -252,6 +252,16 @@ CORINFO_METHOD_HANDLE interceptor_ICJI::getUnboxedEntry(CORINFO_METHOD_HANDLE ft
return result;
}
// Given T, return the type of the default Comparer<T>.
// Returns null if the type can't be determined exactly.
CORINFO_CLASS_HANDLE interceptor_ICJI::getDefaultComparerClass(CORINFO_CLASS_HANDLE cls)
{
mc->cr->AddCall("getDefaultComparerClass");
CORINFO_CLASS_HANDLE result = original_ICorJitInfo->getDefaultComparerClass(cls);
mc->recGetDefaultComparerClass(cls, result);
return result;
}
// Given T, return the type of the default EqualityComparer<T>.
// Returns null if the type can't be determined exactly.
CORINFO_CLASS_HANDLE interceptor_ICJI::getDefaultEqualityComparerClass(CORINFO_CLASS_HANDLE cls)
......
......@@ -132,6 +132,13 @@ CORINFO_METHOD_HANDLE interceptor_ICJI::getUnboxedEntry(
return original_ICorJitInfo->getUnboxedEntry(ftn, requiresInstMethodTableArg);
}
CORINFO_CLASS_HANDLE interceptor_ICJI::getDefaultComparerClass(
CORINFO_CLASS_HANDLE elemType)
{
mcs->AddCall("getDefaultComparerClass");
return original_ICorJitInfo->getDefaultComparerClass(elemType);
}
CORINFO_CLASS_HANDLE interceptor_ICJI::getDefaultEqualityComparerClass(
CORINFO_CLASS_HANDLE elemType)
{
......
......@@ -118,6 +118,12 @@ CORINFO_METHOD_HANDLE interceptor_ICJI::getUnboxedEntry(
return original_ICorJitInfo->getUnboxedEntry(ftn, requiresInstMethodTableArg);
}
CORINFO_CLASS_HANDLE interceptor_ICJI::getDefaultComparerClass(
CORINFO_CLASS_HANDLE elemType)
{
return original_ICorJitInfo->getDefaultComparerClass(elemType);
}
CORINFO_CLASS_HANDLE interceptor_ICJI::getDefaultEqualityComparerClass(
CORINFO_CLASS_HANDLE elemType)
{
......
......@@ -186,6 +186,15 @@ CORINFO_METHOD_HANDLE MyICJI::getUnboxedEntry(CORINFO_METHOD_HANDLE ftn, bool* r
return result;
}
// Given T, return the type of the default Comparer<T>.
// Returns null if the type can't be determined exactly.
CORINFO_CLASS_HANDLE MyICJI::getDefaultComparerClass(CORINFO_CLASS_HANDLE cls)
{
jitInstance->mc->cr->AddCall("getDefaultComparerClass");
CORINFO_CLASS_HANDLE result = jitInstance->mc->repGetDefaultComparerClass(cls);
return result;
}
// Given T, return the type of the default EqualityComparer<T>.
// Returns null if the type can't be determined exactly.
CORINFO_CLASS_HANDLE MyICJI::getDefaultEqualityComparerClass(CORINFO_CLASS_HANDLE cls)
......
......@@ -2057,6 +2057,12 @@ public:
bool* requiresInstMethodTableArg
) = 0;
// Given T, return the type of the default Comparer<T>.
// Returns null if the type can't be determined exactly.
virtual CORINFO_CLASS_HANDLE getDefaultComparerClass(
CORINFO_CLASS_HANDLE elemType
) = 0;
// Given T, return the type of the default EqualityComparer<T>.
// Returns null if the type can't be determined exactly.
virtual CORINFO_CLASS_HANDLE getDefaultEqualityComparerClass(
......
......@@ -85,6 +85,9 @@ CORINFO_METHOD_HANDLE getUnboxedEntry(
CORINFO_METHOD_HANDLE ftn,
bool* requiresInstMethodTableArg) override;
CORINFO_CLASS_HANDLE getDefaultComparerClass(
CORINFO_CLASS_HANDLE elemType) override;
CORINFO_CLASS_HANDLE getDefaultEqualityComparerClass(
CORINFO_CLASS_HANDLE elemType) override;
......
......@@ -32,11 +32,11 @@
//////////////////////////////////////////////////////////////////////////////////////////////////////////
//
constexpr GUID JITEEVersionIdentifier = { /* 20017875-6552-4375-80A8-F7E2CFC6AAB7 */
0x20017875,
0x6552,
0x4375,
{ 0x80, 0xa8, 0xf7, 0xe2, 0xcf, 0xc6, 0xaa, 0xb7 }
constexpr GUID JITEEVersionIdentifier = { /* 6ca59d19-13a4-44f7-a184-e9cd1e8f57f8 */
0x6ca59d19,
0x13a4,
0x44f7,
{ 0xa1, 0x84, 0xe9, 0xcd, 0x1e, 0x8f, 0x57, 0xf8 }
};
//////////////////////////////////////////////////////////////////////////////////////////////////////////
......
......@@ -18,6 +18,7 @@ DEF_CLR_API(getMethodModule)
DEF_CLR_API(getMethodVTableOffset)
DEF_CLR_API(resolveVirtualMethod)
DEF_CLR_API(getUnboxedEntry)
DEF_CLR_API(getDefaultComparerClass)
DEF_CLR_API(getDefaultEqualityComparerClass)
DEF_CLR_API(expandRawHandleIntrinsic)
DEF_CLR_API(getIntrinsicID)
......
......@@ -154,6 +154,15 @@ CORINFO_METHOD_HANDLE WrapICorJitInfo::getUnboxedEntry(
return temp;
}
CORINFO_CLASS_HANDLE WrapICorJitInfo::getDefaultComparerClass(
CORINFO_CLASS_HANDLE elemType)
{
API_ENTER(getDefaultComparerClass);
CORINFO_CLASS_HANDLE temp = wrapHnd->getDefaultComparerClass(elemType);
API_LEAVE(getDefaultComparerClass);
return temp;
}
CORINFO_CLASS_HANDLE WrapICorJitInfo::getDefaultEqualityComparerClass(
CORINFO_CLASS_HANDLE elemType)
{
......
......@@ -926,13 +926,14 @@ GenTreeCall* Compiler::fgGetStaticsCCtorHelper(CORINFO_CLASS_HANDLE cls, CorInfo
GenTreeCall* result = gtNewHelperCallNode(helper, type, argList);
result->gtFlags |= callFlags;
// If we're importing the special EqualityComparer<T>.Default
// intrinsic, flag the helper call. Later during inlining, we can
// If we're importing the special EqualityComparer<T>.Default or Comparer<T>.Default
// intrinsics, flag the helper call. Later during inlining, we can
// remove the helper call if the associated field lookup is unused.
if ((info.compFlags & CORINFO_FLG_JIT_INTRINSIC) != 0)
{
NamedIntrinsic ni = lookupNamedIntrinsic(info.compMethodHnd);
if (ni == NI_System_Collections_Generic_EqualityComparer_get_Default)
if ((ni == NI_System_Collections_Generic_EqualityComparer_get_Default) ||
(ni == NI_System_Collections_Generic_Comparer_get_Default))
{
JITDUMP("\nmarking helper call [%06u] as special dce...\n", result->gtTreeID);
result->gtCallMoreFlags |= GTF_CALL_M_HELPER_SPECIAL_DCE;
......
......@@ -4458,6 +4458,7 @@ GenTree* Compiler::impIntrinsic(GenTree* newobjThis,
break;
}
case NI_System_Collections_Generic_Comparer_get_Default:
case NI_System_Collections_Generic_EqualityComparer_get_Default:
{
// Flag for later handling during devirtualization.
......@@ -4943,6 +4944,10 @@ NamedIntrinsic Compiler::lookupNamedIntrinsic(CORINFO_METHOD_HANDLE method)
{
result = NI_System_Collections_Generic_EqualityComparer_get_Default;
}
else if ((strcmp(className, "Comparer`1") == 0) && (strcmp(methodName, "get_Default") == 0))
{
result = NI_System_Collections_Generic_Comparer_get_Default;
}
}
else if ((strcmp(namespaceName, "System.Numerics") == 0) && (strcmp(className, "BitOperations") == 0))
{
......@@ -21475,6 +21480,7 @@ CORINFO_CLASS_HANDLE Compiler::impGetSpecialIntrinsicExactReturnType(CORINFO_MET
const NamedIntrinsic ni = lookupNamedIntrinsic(methodHnd);
switch (ni)
{
case NI_System_Collections_Generic_Comparer_get_Default:
case NI_System_Collections_Generic_EqualityComparer_get_Default:
{
// Expect one class generic parameter; figure out which it is.
......@@ -21496,7 +21502,15 @@ CORINFO_CLASS_HANDLE Compiler::impGetSpecialIntrinsicExactReturnType(CORINFO_MET
if (isFinalType)
{
result = info.compCompHnd->getDefaultEqualityComparerClass(typeHnd);
if (ni == NI_System_Collections_Generic_EqualityComparer_get_Default)
{
result = info.compCompHnd->getDefaultEqualityComparerClass(typeHnd);
}
else
{
assert(ni == NI_System_Collections_Generic_Comparer_get_Default);
result = info.compCompHnd->getDefaultComparerClass(typeHnd);
}
JITDUMP("Special intrinsic for type %s: return type is %s\n", eeGetClassName(typeHnd),
result != nullptr ? eeGetClassName(result) : "unknown");
}
......
......@@ -42,6 +42,7 @@ enum NamedIntrinsic : unsigned short
NI_System_Math_Tanh,
NI_SYSTEM_MATH_END,
NI_System_Collections_Generic_Comparer_get_Default,
NI_System_Collections_Generic_EqualityComparer_get_Default,
NI_System_Buffers_Binary_BinaryPrimitives_ReverseEndianness,
NI_System_Numerics_BitOperations_PopCount,
......
......@@ -1056,6 +1056,13 @@ private bool resolveVirtualMethod(CORINFO_DEVIRTUALIZATION_INFO* info)
return result != null ? ObjectToHandle(result) : null;
}
private CORINFO_CLASS_STRUCT_* getDefaultComparerClass(CORINFO_CLASS_STRUCT_* elemType)
{
TypeDesc comparand = HandleToObject(elemType);
TypeDesc comparer = IL.Stubs.ComparerIntrinsics.GetComparerForType(comparand);
return comparer != null ? ObjectToHandle(comparer) : null;
}
private CORINFO_CLASS_STRUCT_* getDefaultEqualityComparerClass(CORINFO_CLASS_STRUCT_* elemType)
{
TypeDesc comparand = HandleToObject(elemType);
......
......@@ -175,6 +175,7 @@ FUNCTIONS
void getMethodVTableOffset( CORINFO_METHOD_HANDLE method, unsigned* offsetOfIndirection, unsigned* offsetAfterIndirection, bool* isRelative);
bool resolveVirtualMethod(CORINFO_DEVIRTUALIZATION_INFO* info);
CORINFO_METHOD_HANDLE getUnboxedEntry(CORINFO_METHOD_HANDLE ftn, bool* requiresInstMethodTableArg);
CORINFO_CLASS_HANDLE getDefaultComparerClass(CORINFO_CLASS_HANDLE elemType);
CORINFO_CLASS_HANDLE getDefaultEqualityComparerClass(CORINFO_CLASS_HANDLE elemType);
void expandRawHandleIntrinsic(CORINFO_RESOLVED_TOKEN * pResolvedToken, CORINFO_GENERICHANDLE_RESULT * pResult);
CorInfoIntrinsics getIntrinsicID( CORINFO_METHOD_HANDLE method , BoolStar pMustExpand);
......
......@@ -31,6 +31,14 @@ public static MethodIL EmitEqualityComparerCreate(MethodDesc target)
return EmitComparerAndEqualityComparerCreateCommon(target, "EqualityComparer", "IEquatable`1");
}
/// <summary>
/// Gets the concrete type Comparer`1.Create returns or null if it's not known at compile time.
/// </summary>
public static TypeDesc GetComparerForType(TypeDesc comparand)
{
return GetComparerForType(comparand, "Comparer", "IComparable`1");
}
/// <summary>
/// Gets the concrete type EqualityComparer`1.Create returns or null if it's not known at compile time.
/// </summary>
......
......@@ -24,6 +24,7 @@ struct JitInterfaceCallbacks
void (* getMethodVTableOffset)(void * thisHandle, CorInfoExceptionClass** ppException, void* method, unsigned* offsetOfIndirection, unsigned* offsetAfterIndirection, bool* isRelative);
bool (* resolveVirtualMethod)(void * thisHandle, CorInfoExceptionClass** ppException, void* info);
void* (* getUnboxedEntry)(void * thisHandle, CorInfoExceptionClass** ppException, void* ftn, bool* requiresInstMethodTableArg);
void* (* getDefaultComparerClass)(void * thisHandle, CorInfoExceptionClass** ppException, void* elemType);
void* (* getDefaultEqualityComparerClass)(void * thisHandle, CorInfoExceptionClass** ppException, void* elemType);
void (* expandRawHandleIntrinsic)(void * thisHandle, CorInfoExceptionClass** ppException, void* pResolvedToken, void* pResult);
int (* getIntrinsicID)(void * thisHandle, CorInfoExceptionClass** ppException, void* method, bool* pMustExpand);
......@@ -338,6 +339,15 @@ public:
return temp;
}
virtual void* getDefaultComparerClass(
void* elemType)
{
CorInfoExceptionClass* pException = nullptr;
void* temp = _callbacks->getDefaultComparerClass(_thisHandle, &pException, elemType);
if (pException != nullptr) throw pException;
return temp;
}
virtual void* getDefaultEqualityComparerClass(
void* elemType)
{
......
......@@ -1189,6 +1189,13 @@ DEFINE_CLASS(NULLABLE_EQUALITYCOMPARER, CollectionsGeneric, NullableEqualityComp
DEFINE_CLASS(GENERIC_EQUALITYCOMPARER, CollectionsGeneric, GenericEqualityComparer`1)
DEFINE_CLASS(OBJECT_EQUALITYCOMPARER, CollectionsGeneric, ObjectEqualityComparer`1)
// Classes referenced in Comparer<T>.Default optimization
DEFINE_CLASS(GENERIC_COMPARER, CollectionsGeneric, GenericComparer`1)
DEFINE_CLASS(OBJECT_COMPARER, CollectionsGeneric, ObjectComparer`1)
DEFINE_CLASS(ENUM_COMPARER, CollectionsGeneric, EnumComparer`1)
DEFINE_CLASS(NULLABLE_COMPARER, CollectionsGeneric, NullableComparer`1)
DEFINE_CLASS(INATTRIBUTE, Interop, InAttribute)
DEFINE_CLASS_U(CompilerServices, GCHeapHash, GCHeapHashObject)
......
......@@ -9082,6 +9082,99 @@ void CEEInfo::expandRawHandleIntrinsic(
UNREACHABLE(); // only called with CoreRT.
}
/*********************************************************************/
CORINFO_CLASS_HANDLE CEEInfo::getDefaultComparerClass(CORINFO_CLASS_HANDLE elemType)
{
CONTRACTL {
THROWS;
GC_TRIGGERS;
MODE_PREEMPTIVE;
} CONTRACTL_END;
CORINFO_CLASS_HANDLE result = NULL;
JIT_TO_EE_TRANSITION();
result = getDefaultComparerClassHelper(elemType);
EE_TO_JIT_TRANSITION();
return result;
}
CORINFO_CLASS_HANDLE CEEInfo::getDefaultComparerClassHelper(CORINFO_CLASS_HANDLE elemType)
{
CONTRACTL {
THROWS;
GC_TRIGGERS;
MODE_PREEMPTIVE;
} CONTRACTL_END;
TypeHandle elemTypeHnd(elemType);
// Mirrors the logic in BCL's CompareHelpers.CreateDefaultComparer
// And in compile.cpp's SpecializeComparer
//
// We need to find the appropriate instantiation
Instantiation inst(&elemTypeHnd, 1);
// If T implements IComparable<T>
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<T>
if (Nullable::IsNullableType(elemTypeHnd))
{
Instantiation nullableInst = elemTypeHnd.AsMethodTable()->GetInstantiation();
TypeHandle iequatable = TypeHandle(CoreLibBinder::GetClass(CLASS__IEQUATABLEGENERIC)).Instantiate(nullableInst);
if (nullableInst[0].CanCastTo(iequatable))
{
TypeHandle resultTh = ((TypeHandle)CoreLibBinder::GetClass(CLASS__NULLABLE_COMPARER)).Instantiate(nullableInst);
return CORINFO_CLASS_HANDLE(resultTh.GetMethodTable());
}
}
// We need to special case the Enum comparers based on their underlying type to avoid boxing
if (elemTypeHnd.IsEnum())
{
MethodTable* targetClass = NULL;
CorElementType normType = elemTypeHnd.GetVerifierCorElementType();
switch(normType)
{
case ELEMENT_TYPE_I1:
case ELEMENT_TYPE_I2:
case ELEMENT_TYPE_U1:
case ELEMENT_TYPE_U2:
case ELEMENT_TYPE_I4:
case ELEMENT_TYPE_U4:
case ELEMENT_TYPE_I8:
case ELEMENT_TYPE_U8:
{
targetClass = CoreLibBinder::GetClass(CLASS__ENUM_COMPARER);
break;
}
default:
break;
}
if (targetClass != NULL)
{
TypeHandle resultTh = ((TypeHandle)targetClass->GetCanonicalMethodTable()).Instantiate(inst);
return CORINFO_CLASS_HANDLE(resultTh.GetMethodTable());
}
}
// Default case
TypeHandle resultTh = ((TypeHandle)CoreLibBinder::GetClass(CLASS__OBJECT_COMPARER)).Instantiate(inst);
return CORINFO_CLASS_HANDLE(resultTh.GetMethodTable());
}
/*********************************************************************/
CORINFO_CLASS_HANDLE CEEInfo::getDefaultEqualityComparerClass(CORINFO_CLASS_HANDLE elemType)
{
......@@ -9120,7 +9213,10 @@ CORINFO_CLASS_HANDLE CEEInfo::getDefaultEqualityComparerClassHelper(CORINFO_CLAS
return CORINFO_CLASS_HANDLE(CoreLibBinder::GetClass(CLASS__BYTE_EQUALITYCOMPARER));
}
// Else we'll need to find the appropriate instantation
// Mirrors the logic in BCL's CompareHelpers.CreateDefaultComparer
// And in compile.cpp's SpecializeComparer
//
// We need to find the appropriate instantiation
Instantiation inst(&elemTypeHnd, 1);
// If T implements IEquatable<T>
......
......@@ -452,6 +452,10 @@ public:
bool resolveVirtualMethodHelper(CORINFO_DEVIRTUALIZATION_INFO * info);
CORINFO_CLASS_HANDLE getDefaultComparerClassHelper(
CORINFO_CLASS_HANDLE elemType
);
CORINFO_CLASS_HANDLE getDefaultEqualityComparerClassHelper(
CORINFO_CLASS_HANDLE elemType
);
......
......@@ -4069,6 +4069,12 @@ CORINFO_METHOD_HANDLE ZapInfo::getUnboxedEntry(
return m_pEEJitInfo->getUnboxedEntry(ftn, requiresInstMethodTableArg);
}
CORINFO_CLASS_HANDLE ZapInfo::getDefaultComparerClass(
CORINFO_CLASS_HANDLE elemType)
{
return m_pEEJitInfo->getDefaultComparerClass(elemType);
}
CORINFO_CLASS_HANDLE ZapInfo::getDefaultEqualityComparerClass(
CORINFO_CLASS_HANDLE elemType)
{
......
......@@ -55,6 +55,7 @@ public ComparisonComparer(Comparison<T> comparison)
// Needs to be public to support binary serialization compatibility
public sealed partial class GenericComparer<T> : Comparer<T> where T : IComparable<T>
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override int Compare(T? x, T? y)
{
if (x != null)
......
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;
class Program
{
private static int s_ReturnCode = 100;
private static void AssertEquals<T>(T expected, T actual, [CallerLineNumber] int line = 0)
{
if (!expected.Equals(actual))
{
Console.WriteLine($"{expected} != {actual}, L{line}");
s_ReturnCode++;
}
}
private static void Compare_Boolean(Boolean a, Boolean b) =>
AssertEquals(a.CompareTo(b), Comparer<Boolean>.Default.Compare(a, b));
private static void Compare_Byte(Byte a, Byte b) =>
AssertEquals(a.CompareTo(b), Comparer<Byte>.Default.Compare(a, b));
private static void Compare_SByte(SByte a, SByte b) =>
AssertEquals(a.CompareTo(b), Comparer<SByte>.Default.Compare(a, b));
private static void Compare_Char(Char a, Char b) =>
AssertEquals(a.CompareTo(b), Comparer<Char>.Default.Compare(a, b));
private static void Compare_UInt16(UInt16 a, UInt16 b) =>
AssertEquals(a.CompareTo(b), Comparer<UInt16>.Default.Compare(a, b));
private static void Compare_Int16(Int16 a, Int16 b) =>
AssertEquals(a.CompareTo(b), Comparer<Int16>.Default.Compare(a, b));
private static void Compare_UInt32(UInt32 a, UInt32 b) =>
AssertEquals(a.CompareTo(b), Comparer<UInt32>.Default.Compare(a, b));
private static void Compare_Int32(Int32 a, Int32 b) =>
AssertEquals(a.CompareTo(b), Comparer<Int32>.Default.Compare(a, b));
private static void Compare_Int64(Int64 a, Int64 b) =>
AssertEquals(a.CompareTo(b), Comparer<Int64>.Default.Compare(a, b));
private static void Compare_UInt64(UInt64 a, UInt64 b) =>
AssertEquals(a.CompareTo(b), Comparer<UInt64>.Default.Compare(a, b));
private static void Compare_IntPtr(IntPtr a, IntPtr b) =>
AssertEquals(a.CompareTo(b), Comparer<IntPtr>.Default.Compare(a, b));
private static void Compare_UIntPtr(UIntPtr a, UIntPtr b) =>
AssertEquals(a.CompareTo(b), Comparer<UIntPtr>.Default.Compare(a, b));
private static void Compare_nint(nint a, nint b) =>
AssertEquals(a.CompareTo(b), Comparer<nint>.Default.Compare(a, b));
private static void Compare_nuint(nuint a, nuint b) =>
AssertEquals(a.CompareTo(b), Comparer<nuint>.Default.Compare(a, b));
private static void Compare_Enum_Int32(MethodImplOptions a, MethodImplOptions b) =>
AssertEquals(a.CompareTo(b), Comparer<MethodImplOptions>.Default.Compare(a, b));
private static void Compare_Enum_Byte(Enum_byte a, Enum_byte b) =>
AssertEquals(a.CompareTo(b), Comparer<Enum_byte>.Default.Compare(a, b));
private static void Compare_String(String a, String b) =>
AssertEquals(a.CompareTo(b), Comparer<String>.Default.Compare(a, b));
private static void Compare_DateTime(DateTime a, DateTime b) =>
AssertEquals(a.CompareTo(b), Comparer<DateTime>.Default.Compare(a, b));
private static void Compare_Struct1(Struct1 a, Struct1 b) =>
AssertEquals(a.CompareTo(b), Comparer<Struct1>.Default.Compare(a, b));
private static void Compare_Int32_Nullable(long? a, long? b)
{
int actual = Comparer<long?>.Default.Compare(a, b);
int expected = 0;
if (a.HasValue)
expected = b.HasValue ? a.Value.CompareTo(b.Value) : 1;
else
expected = b.HasValue ? -1 : 0;
AssertEquals(expected, actual);
}
public static int Main(string[] args)
{
long[] values = Enumerable.Range(1000, 2000)
.Select(i => (long)i)
.Concat(new[]
{
short.MinValue, short.MinValue + 1, short.MaxValue - 1, short.MaxValue, short.MaxValue + 1,
int.MinValue, int.MinValue + 1, int.MaxValue, int.MaxValue - 1,
long.MinValue, long.MinValue + 1, long.MaxValue, long.MaxValue - 1,
})
.ToArray();
for (var i = 0; i < values.Length; i++)
{
for (int j = 0; j < values.Length; j++)
{
long a = values[i];
long b = values[j];
var boolA = Unsafe.As<long, bool>(ref a);
var boolB = Unsafe.As<long, bool>(ref b);
Compare_Boolean(boolA, boolB);
var byteA = Unsafe.As<long, byte>(ref a);
var byteB = Unsafe.As<long, byte>(ref b);
Compare_Byte(byteA, byteB);
var sbyteA = Unsafe.As<long, sbyte>(ref a);
var sbyteB = Unsafe.As<long, sbyte>(ref b);
Compare_SByte(sbyteA, sbyteB);
var shortA = Unsafe.As<long, short>(ref a);
var shortB = Unsafe.As<long, short>(ref b);
Compare_Int16(shortA, shortB);
var ushortA = Unsafe.As<long, ushort>(ref a);
var ushortB = Unsafe.As<long, ushort>(ref b);
Compare_UInt16(ushortA, ushortB);
var charA = Unsafe.As<long, char>(ref a);
var charB = Unsafe.As<long, char>(ref b);
Compare_Char(charA, charB);
var intA = Unsafe.As<long, int>(ref a);
var intB = Unsafe.As<long, int>(ref b);
Compare_Int32(intA, intB);
var uintA = Unsafe.As<long, uint>(ref a);
var uintB = Unsafe.As<long, uint>(ref b);
Compare_UInt32(uintA, uintB);
var longA = Unsafe.As<long, long>(ref a);
var longB = Unsafe.As<long, long>(ref b);
Compare_Int64(longA, longB);
var ulongA = Unsafe.As<long, ulong>(ref a);
var ulongB = Unsafe.As<long, ulong>(ref b);
Compare_UInt64(ulongA, ulongB);
var intPtrA = Unsafe.As<long, IntPtr>(ref a);
var intPtrB = Unsafe.As<long, IntPtr>(ref b);
Compare_IntPtr(intPtrA, intPtrB);
var uintPtrA = Unsafe.As<long, UIntPtr>(ref a);
var uintPtrB = Unsafe.As<long, UIntPtr>(ref b);
Compare_UIntPtr(uintPtrA, uintPtrB);
var nintA = Unsafe.As<long, nint>(ref a);
var nintB = Unsafe.As<long, nint>(ref b);
Compare_nint(nintA, nintB);
var nuintA = Unsafe.As<long, nuint>(ref a);
var nuintB = Unsafe.As<long, nuint>(ref b);
Compare_nuint(nuintA, nuintB);
var enumIntA = Unsafe.As<long, MethodImplOptions>(ref a);
var enumIntB = Unsafe.As<long, MethodImplOptions>(ref b);
Compare_Enum_Int32(enumIntA, enumIntB);
var enumByteA = Unsafe.As<long, Enum_byte>(ref a);
var enumByteB = Unsafe.As<long, Enum_byte>(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);
}
}
string[] strings = { "", "0", "00", "1", "11", "111", "привет", "Hello" };
foreach (var str1 in strings)
{
foreach (var str2 in strings)
{
Compare_String(str1, str2);
Compare_String(str2, str1);
}
}
Compare_Int32_Nullable(null, 0);
Compare_Int32_Nullable(0, null);
Compare_Int32_Nullable(null, null);
Compare_Int32_Nullable(null, 1);
Compare_Int32_Nullable(1, null);
Compare_Int32_Nullable(null, -1);
Compare_Int32_Nullable(-1, null);
GetTypeTests();
GetHashCodeTests();
return s_ReturnCode;
}
private static void GetTypeTests()
{
AssertEquals("System.Collections.Generic.GenericComparer`1[System.Int32]", Comparer<int>.Default.GetType().ToString());
AssertEquals("System.Collections.Generic.GenericComparer`1[System.String]", Comparer<string>.Default.GetType().ToString());
AssertEquals("System.Collections.Generic.GenericComparer`1[System.Guid]", Comparer<Guid>.Default.GetType().ToString());
AssertEquals("System.Collections.Generic.EnumComparer`1[System.Runtime.CompilerServices.MethodImplOptions]", Comparer<MethodImplOptions>.Default.GetType().ToString());
AssertEquals("System.Collections.Generic.NullableComparer`1[System.Byte]", Comparer<byte?>.Default.GetType().ToString());
AssertEquals("System.Collections.Generic.ObjectComparer`1[Struct1]", Comparer<Struct1>.Default.GetType().ToString());
}
private static int GetHashCodeTests()
{
// Just to make sure it doesn't crash
return Comparer<int>.Default.GetHashCode() +
Comparer<string>.Default.GetHashCode() +
Comparer<MethodImplOptions>.Default.GetHashCode() +
Comparer<byte?>.Default.GetHashCode() +
Comparer<Guid>.Default.GetHashCode() +
Comparer<Struct1>.Default.GetHashCode();
}
}
public enum Enum_byte : byte
{
A,B,C,D,E
}
public struct Struct1 : IComparable
{
public long a;
public long b;
public int CompareTo(object? obj)
{
return b.CompareTo(((Struct1) obj).b);
}
}
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<Optimize>True</Optimize>
</PropertyGroup>
<ItemGroup>
<Compile Include="Comparer_get_Default.cs" />
</ItemGroup>
</Project>
......@@ -963,6 +963,9 @@
<!-- Known failures for mono runtime on *all* architectures/operating systems -->
<ItemGroup Condition="'$(RuntimeFlavor)' == 'mono'" >
<ExcludeList Include="$(XunitTestBinBase)/JIT/opt/Devirtualization/Comparer_get_Default/*">
<Issue>https://github.com/dotnet/runtime/issues/48190</Issue>
</ExcludeList>
<ExcludeList Include="$(XunitTestBinBase)/JIT/Directed/DynamicPgo/**">
<Issue>Mono doesn't have a dynamic pgo or tiered compilation infrastructure</Issue>
</ExcludeList>
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册