Skip to content

Commit

Permalink
Improve EqualityComparer for NativeAOT (#83054)
Browse files Browse the repository at this point in the history
Remove s_default field from EqualityComparer.NativeAot.cs and Comparer.NativeAot.cs
  • Loading branch information
EgorBo committed Mar 7, 2023
1 parent 795a287 commit 1247d8f
Show file tree
Hide file tree
Showing 3 changed files with 10 additions and 50 deletions.
Expand Up @@ -12,8 +12,6 @@ namespace System.Collections.Generic
{
public abstract partial class Comparer<T> : IComparer, IComparer<T>
{
private static Comparer<T> s_default;

// The AOT compiler can flip this to false under certain circumstances.
private static bool SupportsGenericIComparableInterfaces => true;

Expand All @@ -25,23 +23,14 @@ private static Comparer<T> Create()
// This body serves as a fallback when instantiation-specific implementation is unavailable.
// If that happens, the compiler ensures we generate data structures to make the fallback work
// when this method is compiled.
Interlocked.CompareExchange(ref s_default,
SupportsGenericIComparableInterfaces
? Unsafe.As<Comparer<T>>(ComparerHelpers.GetComparer(typeof(T).TypeHandle))
: new ObjectComparer<T>(),
null);
return s_default;
}

public static Comparer<T> Default
{
[Intrinsic]
get
if (SupportsGenericIComparableInterfaces)
{
// Lazy initialization produces smaller code for AOT compilation than initialization in constructor
return s_default ?? Create();
return Unsafe.As<Comparer<T>>(ComparerHelpers.GetComparer(typeof(T).TypeHandle));
}
return new ObjectComparer<T>();
}

public static Comparer<T> Default { [Intrinsic] get; } = Create();
}

internal sealed partial class EnumComparer<T> : Comparer<T> where T : struct, Enum
Expand Down
Expand Up @@ -12,8 +12,6 @@ namespace System.Collections.Generic
{
public abstract partial class EqualityComparer<T> : IEqualityComparer, IEqualityComparer<T>
{
private static EqualityComparer<T> s_default;

// The AOT compiler can flip this to false under certain circumstances.
private static bool SupportsGenericIEquatableInterfaces => true;

Expand All @@ -25,23 +23,14 @@ private static EqualityComparer<T> Create()
// This body serves as a fallback when instantiation-specific implementation is unavailable.
// If that happens, the compiler ensures we generate data structures to make the fallback work
// when this method is compiled.
Interlocked.CompareExchange(ref s_default,
SupportsGenericIEquatableInterfaces
? Unsafe.As<EqualityComparer<T>>(EqualityComparerHelpers.GetComparer(typeof(T).TypeHandle))
: new ObjectEqualityComparer<T>(),
null);
return s_default;
}

public static EqualityComparer<T> Default
{
[Intrinsic]
get
if (SupportsGenericIEquatableInterfaces)
{
// Lazy initialization produces smaller code for AOT compilation than initialization in constructor
return s_default ?? Create();
return Unsafe.As<EqualityComparer<T>>(EqualityComparerHelpers.GetComparer(typeof(T).TypeHandle));
}
return new ObjectEqualityComparer<T>();
}

public static EqualityComparer<T> Default { [Intrinsic] get; } = Create();
}

public sealed partial class EnumEqualityComparer<T> : EqualityComparer<T> where T : struct, Enum
Expand Down
18 changes: 0 additions & 18 deletions src/coreclr/tools/Common/TypeSystem/IL/Stubs/ComparerIntrinsics.cs
Expand Up @@ -65,25 +65,7 @@ private static MethodIL EmitComparerAndEqualityComparerCreateCommon(MethodDesc m
ILEmitter emitter = new ILEmitter();
var codeStream = emitter.NewCodeStream();

FieldDesc defaultField = owningType.GetKnownField("s_default");

TypeSystemContext context = comparerType.Context;
TypeDesc objectType = context.GetWellKnownType(WellKnownType.Object);
MethodDesc compareExchangeObject = context.SystemModule.
GetKnownType("System.Threading", "Interlocked").
GetKnownMethod("CompareExchange",
new MethodSignature(
MethodSignatureFlags.Static,
genericParameterCount: 0,
returnType: objectType,
parameters: new TypeDesc[] { objectType.MakeByRefType(), objectType, objectType }));

codeStream.Emit(ILOpcode.ldsflda, emitter.NewToken(defaultField));
codeStream.Emit(ILOpcode.newobj, emitter.NewToken(comparerType.GetParameterlessConstructor()));
codeStream.Emit(ILOpcode.ldnull);
codeStream.Emit(ILOpcode.call, emitter.NewToken(compareExchangeObject));
codeStream.Emit(ILOpcode.pop);
codeStream.Emit(ILOpcode.ldsfld, emitter.NewToken(defaultField));
codeStream.Emit(ILOpcode.ret);

return emitter.Link(methodBeingGenerated);
Expand Down

0 comments on commit 1247d8f

Please sign in to comment.