From 564532e43e074aafbbc821fc248fa0cee829ec33 Mon Sep 17 00:00:00 2001 From: Vlad Brezae Date: Fri, 27 Mar 2026 13:40:55 +0200 Subject: [PATCH] Intrinsify default Comparer and EqualityComparer creation in R2R During cctor, backing field initialization was calling into `ComparerHelpers.CreateDefaultEqualityComparer` which used the reflection based api: `CreateInstanceForAnotherGenericParameter` to create a new type that R2R had now knowledge of. In order to fix this, we follow the same approach that NativeAOT takes, reusing existing helpers. This means that we will end up with specialized helpers in the R2R image that create these comparers in the cctor and R2R will now know to root the specialized comparer types. --- .../System/Collections/Generic/Comparer.CoreCLR.cs | 6 +++++- .../Generic/EqualityComparer.CoreCLR.cs | 6 +++++- .../IL/ReadyToRunILProvider.cs | 14 ++++++++++++++ 3 files changed, 24 insertions(+), 2 deletions(-) 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..f214dc095a84d5 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 @@ -7,9 +7,13 @@ namespace System.Collections.Generic { public abstract partial class Comparer : IComparer, IComparer { + // This method is special cased in R2R, with its implementation replaced by IL helper + [Intrinsic] + private static Comparer Create() => (Comparer)ComparerHelpers.CreateDefaultComparer(typeof(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 Default { [Intrinsic] get; } = (Comparer)ComparerHelpers.CreateDefaultComparer(typeof(T)); + public static Comparer Default { [Intrinsic] get; } = Create(); } internal sealed partial class EnumComparer : Comparer where T : struct, Enum 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 3fa3a14abffa30..d4e32bad8f97ae 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 @@ -8,9 +8,13 @@ namespace System.Collections.Generic { public abstract partial class EqualityComparer : IEqualityComparer, IEqualityComparer { + // This method is special cased in R2R, with its implementation replaced by IL helper + [Intrinsic] + private static EqualityComparer Create() => (EqualityComparer)ComparerHelpers.CreateDefaultEqualityComparer(typeof(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 EqualityComparer Default { [Intrinsic] get; } = (EqualityComparer)ComparerHelpers.CreateDefaultEqualityComparer(typeof(T)); + public static EqualityComparer Default { [Intrinsic] get; } = Create(); } public sealed partial class GenericEqualityComparer : EqualityComparer where T : IEquatable? diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/IL/ReadyToRunILProvider.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/IL/ReadyToRunILProvider.cs index 5460cad265c0d6..7e1d4b59038743 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/IL/ReadyToRunILProvider.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/IL/ReadyToRunILProvider.cs @@ -120,6 +120,20 @@ private MethodIL TryGetPerInstantiationIntrinsicMethodIL(MethodDesc method) return InterlockedIntrinsics.EmitIL(_compilationModuleGroup, method); } + if (mdType.Namespace.SequenceEqual("System.Collections.Generic"u8)) + { + if (mdType.Name.SequenceEqual("Comparer`1"u8)) + { + if (method.Name.SequenceEqual("Create"u8)) + return ComparerIntrinsics.EmitComparerCreate(method); + } + else if (mdType.Name.SequenceEqual("EqualityComparer`1"u8)) + { + if (method.Name.SequenceEqual("Create"u8)) + return ComparerIntrinsics.EmitEqualityComparerCreate(method); + } + } + return null; }