From 7c297814f8a24ea487102c6137b6aa448559b765 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20Strehovsk=C3=BD?= Date: Thu, 14 Mar 2024 13:13:24 +0100 Subject: [PATCH 01/11] Fold always false type checks If we have a program like: ```csharp class Never { } class Program { static void Main() { Test(null); } [MethodImpl(MethodImplOptions.NoInlining)] static void Test(object o) { if (o is Never) Console.WriteLine("Hello!"); if (o.GetType() == typeof(Never)) Console.WriteLine("Hello!"); } } ``` We know these checks are never going to be true thanks to the whole program view. Fold these to `false`. --- src/coreclr/inc/corinfo.h | 3 + src/coreclr/jit/gentree.cpp | 31 +++-- src/coreclr/jit/importer.cpp | 27 ++++- .../Compiler/DevirtualizationManager.cs | 16 ++- .../tools/Common/JitInterface/CorInfoImpl.cs | 14 ++- .../tools/Common/JitInterface/CorInfoTypes.cs | 1 + .../Compiler/Compilation.cs | 11 +- .../ILCompiler.Compiler/Compiler/ILScanner.cs | 12 +- .../Compiler/RyuJitCompilation.cs | 4 +- .../JitInterface/CorInfoImpl.RyuJit.cs | 26 +++++ .../TrimmingBehaviors/DeadCodeElimination.cs | 110 ++++++++++++++++++ 11 files changed, 231 insertions(+), 24 deletions(-) diff --git a/src/coreclr/inc/corinfo.h b/src/coreclr/inc/corinfo.h index 963793057bfc17..0ace173e272b17 100644 --- a/src/coreclr/inc/corinfo.h +++ b/src/coreclr/inc/corinfo.h @@ -1247,6 +1247,7 @@ struct CORINFO_LOOKUP_KIND #define CORINFO_USEHELPER ((uint16_t) 0xffff) #define CORINFO_USENULL ((uint16_t) 0xfffe) #define CORINFO_NO_SIZE_CHECK ((uint16_t) 0xffff) +#define CORINFO_NEVER_INSTANTIATED (-1) struct CORINFO_RUNTIME_LOOKUP { @@ -2983,6 +2984,8 @@ class ICorStaticInfo // Obtains a list of exact classes for a given base type. Returns 0 if the number of // the exact classes is greater than maxExactClasses or if more types might be loaded // in future. + // When called with maxExactClasses == 0, returns CORINFO_NEVER_INSTANTIATED if an + // exact class of this type cannot exist. Returns 0 otherwise. virtual int getExactClasses( CORINFO_CLASS_HANDLE baseType, /* IN */ int maxExactClasses, /* IN */ diff --git a/src/coreclr/jit/gentree.cpp b/src/coreclr/jit/gentree.cpp index 458117f8ebfb69..aecbbde518a9b6 100644 --- a/src/coreclr/jit/gentree.cpp +++ b/src/coreclr/jit/gentree.cpp @@ -14050,13 +14050,8 @@ GenTree* Compiler::gtFoldTypeCompare(GenTree* tree) return compare; } - // If one operand creates a type from a handle and the other operand is fetching the type from an object, - // we can sometimes optimize the type compare into a simpler - // method table comparison. - // - // TODO: if other operand is null... - if (!(((op1Kind == TPK_GetType) && (op2Kind == TPK_Handle)) || - ((op1Kind == TPK_Handle) && (op2Kind == TPK_GetType)))) + // In the following code, we need one of these to be a handle. + if (op1Kind != TPK_Handle && op2Kind != TPK_Handle) { return tree; } @@ -14074,6 +14069,28 @@ GenTree* Compiler::gtFoldTypeCompare(GenTree* tree) return tree; } + // Check if an object of this type can even exist + if (info.compCompHnd->getExactClasses(clsHnd, 0, nullptr) == CORINFO_NEVER_INSTANTIATED) + { + JITDUMP("Runtime reported %p (%s) is never allocated\n", dspPtr(clsHnd), eeGetClassName(clsHnd)); + + const bool operatorIsEQ = (oper == GT_EQ); + const int compareResult = operatorIsEQ ? 0 : 1; + JITDUMP("Runtime reports comparison is known at jit time: %u\n", compareResult); + GenTree* result = gtNewIconNode(compareResult); + return result; + } + + // If one operand creates a type from a handle and the other operand is fetching the type from an object, + // we can sometimes optimize the type compare into a simpler + // method table comparison. + // + // TODO: if other operand is null... + if (op1Kind != TPK_GetType && op2Kind != TPK_GetType) + { + return tree; + } + // We're good to go. JITDUMP("Optimizing compare of obj.GetType()" " and type-from-handle to compare method table pointer\n"); diff --git a/src/coreclr/jit/importer.cpp b/src/coreclr/jit/importer.cpp index 49bdd4e5e77576..9db39a0b9cd0ac 100644 --- a/src/coreclr/jit/importer.cpp +++ b/src/coreclr/jit/importer.cpp @@ -5349,6 +5349,32 @@ GenTree* Compiler::impOptimizeCastClassOrIsInst(GenTree* op1, CORINFO_RESOLVED_T return nullptr; } + CORINFO_CLASS_HANDLE toClass = pResolvedToken->hClass; + if (info.compCompHnd->getExactClasses(toClass, 0, nullptr) == CORINFO_NEVER_INSTANTIATED) + { + JITDUMP("\nClass %s%p (%s) can never be allocated\n", dspPtr(toClass), eeGetClassName(toClass)); + + if (!isCastClass) + { + JITDUMP("Cast will fail, optimizing to return null\n"); + + // If the cast was fed by a box, we can remove that too. + if (op1->IsBoxedValue()) + { + JITDUMP("Also removing upstream box\n"); + gtTryRemoveBoxUpstreamEffects(op1); + } + + if (gtTreeHasSideEffects(op1, GTF_SIDE_EFFECT)) + { + impAppendTree(op1, CHECK_SPILL_ALL, impCurStmtDI); + } + return gtNewNull(); + } + + JITDUMP("Cast will always throw, but not optimizing yet\n"); + } + // See what we know about the type of the object being cast. bool isExact = false; bool isNonNull = false; @@ -5356,7 +5382,6 @@ GenTree* Compiler::impOptimizeCastClassOrIsInst(GenTree* op1, CORINFO_RESOLVED_T if (fromClass != nullptr) { - CORINFO_CLASS_HANDLE toClass = pResolvedToken->hClass; JITDUMP("\nConsidering optimization of %s from %s%p (%s) to %p (%s)\n", isCastClass ? "castclass" : "isinst", isExact ? "exact " : "", dspPtr(fromClass), eeGetClassName(fromClass), dspPtr(toClass), eeGetClassName(toClass)); diff --git a/src/coreclr/tools/Common/Compiler/DevirtualizationManager.cs b/src/coreclr/tools/Common/Compiler/DevirtualizationManager.cs index 6222ec6d649cca..2145460177a4e2 100644 --- a/src/coreclr/tools/Common/Compiler/DevirtualizationManager.cs +++ b/src/coreclr/tools/Common/Compiler/DevirtualizationManager.cs @@ -203,13 +203,17 @@ protected virtual MethodDesc ResolveVirtualMethod(MethodDesc declMethod, DefType #if !READYTORUN /// - /// Gets a value indicating whether it might be possible to obtain a constructed type data structure for the given type. + /// Gets a value indicating whether it might be possible to obtain a constructed type data structure for the given type + /// in this compilation (i.e. is it possible to reference a constructed MethodTable symbol for this). /// - /// - /// This is a bit of a hack, but devirtualization manager has a global view of all allocated types - /// so it can answer this question. - /// - public virtual bool CanConstructType(TypeDesc type) => true; + public virtual bool CanReferenceConstructedMethodTable(TypeDesc type) => true; + + /// + /// Gets a value indicating whether a (potentially canonically-equlivalent) constructed MethodTable could + /// exist. This is similar to , but will return true + /// for List<__Canon> if a constructed MethodTable for List<object> exists. + /// + public virtual bool CanTypeOrCanonicalFormOfTypeBeAllocated(TypeDesc type) => true; public virtual TypeDesc[] GetImplementingClasses(TypeDesc type) => null; #endif diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs index 0f87233e90b8b2..14882fd56d8a65 100644 --- a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs +++ b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs @@ -2874,12 +2874,22 @@ private TypeCompareState compareTypesForEquality(CORINFO_CLASS_STRUCT_* cls1, CO TypeDesc type1 = HandleToObject(cls1); TypeDesc type2 = HandleToObject(cls2); - return TypeExtensions.CompareTypesForEquality(type1, type2) switch + TypeCompareState result = TypeExtensions.CompareTypesForEquality(type1, type2) switch { true => TypeCompareState.Must, false => TypeCompareState.MustNot, _ => TypeCompareState.May, }; + +#if !READYTORUN + if (result == TypeCompareState.May + && (canNeverHaveInstanceOfSubclassOf(type1) || canNeverHaveInstanceOfSubclassOf(type2))) + { + return TypeCompareState.MustNot; + } +#endif + + return result; } private bool isMoreSpecificType(CORINFO_CLASS_STRUCT_* cls1, CORINFO_CLASS_STRUCT_* cls2) @@ -4284,7 +4294,7 @@ private HRESULT getPgoInstrumentationResults(CORINFO_METHOD_STRUCT_* ftnHnd, ref #pragma warning disable SA1001, SA1113, SA1115 // Commas should be spaced correctly ComputeJitPgoInstrumentationSchema(ObjectToHandle, pgoResultsSchemas, out var nativeSchemas, _cachedMemoryStream #if !READYTORUN - , _compilation.CanConstructType + , _compilation.CanReferenceConstructedMethodTable #endif ); #pragma warning restore SA1001, SA1113, SA1115 // Commas should be spaced correctly diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs b/src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs index 2276c5cdc9a7cc..a23f2cd43eba86 100644 --- a/src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs +++ b/src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs @@ -26,6 +26,7 @@ public static class CORINFO public const ushort USEHELPER = 0xffff; public const ushort USENULL = 0xfffe; public const ushort CORINFO_NO_SIZE_CHECK = 0xffff; + public const int CORINFO_NEVER_INSTANTIATED = -1; } public struct CORINFO_METHOD_STRUCT_ diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Compilation.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Compilation.cs index a4d4a8968af920..b6a84b82978870 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Compilation.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Compilation.cs @@ -103,9 +103,14 @@ public bool CanInline(MethodDesc caller, MethodDesc callee) return _inliningPolicy.CanInline(caller, callee); } - public bool CanConstructType(TypeDesc type) + public bool CanReferenceConstructedMethodTable(TypeDesc type) { - return NodeFactory.DevirtualizationManager.CanConstructType(type); + return NodeFactory.DevirtualizationManager.CanReferenceConstructedMethodTable(type); + } + + public bool CanTypeOrCanonicalFormOfTypeBeAllocated(TypeDesc type) + { + return NodeFactory.DevirtualizationManager.CanTypeOrCanonicalFormOfTypeBeAllocated(type); } public DelegateCreationInfo GetDelegateCtor(TypeDesc delegateType, MethodDesc target, TypeDesc constrainedType, bool followVirtualDispatch) @@ -261,7 +266,7 @@ public bool NeedsRuntimeLookup(ReadyToRunHelperId lookupKind, object targetOfLoo public ReadyToRunHelperId GetLdTokenHelperForType(TypeDesc type) { - bool canConstructPerWholeProgramAnalysis = NodeFactory.DevirtualizationManager.CanConstructType(type); + bool canConstructPerWholeProgramAnalysis = NodeFactory.DevirtualizationManager.CanReferenceConstructedMethodTable(type); bool creationAllowed = ConstructedEETypeNode.CreationAllowed(type); return (canConstructPerWholeProgramAnalysis && creationAllowed) ? ReadyToRunHelperId.TypeHandle diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ILScanner.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ILScanner.cs index 630de4a7470645..af3ae5021dece9 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ILScanner.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ILScanner.cs @@ -413,7 +413,8 @@ public override DictionaryLayoutNode GetLayout(TypeSystemEntity methodOrType) private sealed class ScannedDevirtualizationManager : DevirtualizationManager { - private HashSet _constructedTypes = new HashSet(); + private HashSet _constructedMethodTables = new HashSet(); + private HashSet _canonConstructedMethodTables = new HashSet(); private HashSet _canonConstructedTypes = new HashSet(); private HashSet _unsealedTypes = new HashSet(); private Dictionary> _implementators = new(); @@ -442,7 +443,8 @@ public ScannedDevirtualizationManager(NodeFactory factory, ImmutableArray _constructedTypes.Contains(type); + public override bool CanReferenceConstructedMethodTable(TypeDesc type) + => _constructedMethodTables.Contains(type); + + public override bool CanTypeOrCanonicalFormOfTypeBeAllocated(TypeDesc type) + => _constructedMethodTables.Contains(type) || _canonConstructedMethodTables.Contains(type); public override TypeDesc[] GetImplementingClasses(TypeDesc type) { diff --git a/src/coreclr/tools/aot/ILCompiler.RyuJit/Compiler/RyuJitCompilation.cs b/src/coreclr/tools/aot/ILCompiler.RyuJit/Compiler/RyuJitCompilation.cs index a0dd7850ae8806..80aef8f4c2118a 100644 --- a/src/coreclr/tools/aot/ILCompiler.RyuJit/Compiler/RyuJitCompilation.cs +++ b/src/coreclr/tools/aot/ILCompiler.RyuJit/Compiler/RyuJitCompilation.cs @@ -72,7 +72,7 @@ public override IEETypeNode NecessaryTypeSymbolIfPossible(TypeDesc type) // information proving that it isn't, give RyuJIT the constructed symbol even // though we just need the unconstructed one. // https://github.com/dotnet/runtimelab/issues/1128 - bool canPotentiallyConstruct = NodeFactory.DevirtualizationManager.CanConstructType(type); + bool canPotentiallyConstruct = NodeFactory.DevirtualizationManager.CanReferenceConstructedMethodTable(type); if (canPotentiallyConstruct) return _nodeFactory.MaximallyConstructableType(type); @@ -81,7 +81,7 @@ public override IEETypeNode NecessaryTypeSymbolIfPossible(TypeDesc type) public FrozenRuntimeTypeNode NecessaryRuntimeTypeIfPossible(TypeDesc type) { - bool canPotentiallyConstruct = NodeFactory.DevirtualizationManager.CanConstructType(type); + bool canPotentiallyConstruct = NodeFactory.DevirtualizationManager.CanReferenceConstructedMethodTable(type); if (canPotentiallyConstruct) return _nodeFactory.SerializedMaximallyConstructableRuntimeTypeObject(type); diff --git a/src/coreclr/tools/aot/ILCompiler.RyuJit/JitInterface/CorInfoImpl.RyuJit.cs b/src/coreclr/tools/aot/ILCompiler.RyuJit/JitInterface/CorInfoImpl.RyuJit.cs index b482a153539af0..ea3e3e88720d52 100644 --- a/src/coreclr/tools/aot/ILCompiler.RyuJit/JitInterface/CorInfoImpl.RyuJit.cs +++ b/src/coreclr/tools/aot/ILCompiler.RyuJit/JitInterface/CorInfoImpl.RyuJit.cs @@ -2261,6 +2261,24 @@ private void getFieldInfo(ref CORINFO_RESOLVED_TOKEN pResolvedToken, CORINFO_MET // and STS::AccessCheck::CanAccess. } + private bool canNeverHaveInstanceOfSubclassOf(TypeDesc type) + { + // Don't try to optimize nullable + if (type.IsNullable) + return false; + + // We don't track unconstructable types very well and they are rare anyway + if (!ConstructedEETypeNode.CreationAllowed(type)) + return false; + + TypeDesc canonType = type.ConvertToCanonForm(CanonicalFormKind.Specific); + + // If we don't have a constructed MethodTable for the exact type or for its template, + // this type or any of its subclasses can never be instantiated. + return !_compilation.CanTypeOrCanonicalFormOfTypeBeAllocated(type) + && (type == canonType || !_compilation.CanReferenceConstructedMethodTable(canonType)); + } + private int getExactClasses(CORINFO_CLASS_STRUCT_* baseType, int maxExactClasses, CORINFO_CLASS_STRUCT_** exactClsRet) { MetadataType type = HandleToObject(baseType) as MetadataType; @@ -2269,6 +2287,14 @@ private int getExactClasses(CORINFO_CLASS_STRUCT_* baseType, int maxExactClasses return 0; } + if (maxExactClasses == 0) + { + if (canNeverHaveInstanceOfSubclassOf(type)) + return CORINFO.CORINFO_NEVER_INSTANTIATED; + + return 0; + } + // type is already sealed, return it if (_compilation.IsEffectivelySealed(type)) { diff --git a/src/tests/nativeaot/SmokeTests/TrimmingBehaviors/DeadCodeElimination.cs b/src/tests/nativeaot/SmokeTests/TrimmingBehaviors/DeadCodeElimination.cs index bf4b8639cefa0c..acaa40feb861ac 100644 --- a/src/tests/nativeaot/SmokeTests/TrimmingBehaviors/DeadCodeElimination.cs +++ b/src/tests/nativeaot/SmokeTests/TrimmingBehaviors/DeadCodeElimination.cs @@ -26,6 +26,7 @@ public static int Run() TestUnmodifiableStaticFieldOptimization.Run(); TestUnmodifiableInstanceFieldOptimization.Run(); TestGetMethodOptimization.Run(); + TestTypeOfCodegenBranchElimination.Run(); return 100; } @@ -644,6 +645,115 @@ public static void Run() } } + class TestTypeOfCodegenBranchElimination + { + class Never1 { } + class Never2 { } + class Never3 { } + class Never4 { } + class Never5 { } + class Never6 { } + + class Canary1 { } + class Canary2 { } + class Canary3 { } + class Canary4 { } + class Canary5 { } + class Canary6 { } + + class Maybe1 { } + + class Marker1 { } + + class Atom1 { } + + [UnconditionalSuppressMessage("AotAnalysis", "IL3050:UnrecognizedAotPattern", + Justification = "That's the point")] + public static void Run() + { + if (GetUnknownType().GetType() == typeof(Never1)) + { + Consume(new Canary1()); + } +#if !DEBUG + ThrowIfPresentWithUsableMethodTable(typeof(TestTypeOfCodegenBranchElimination), nameof(Canary1)); +#endif + + if (GetUnknownType() is Never2) + { + Consume(new Canary2()); + } +#if !DEBUG + ThrowIfPresentWithUsableMethodTable(typeof(TestTypeOfCodegenBranchElimination), nameof(Canary2)); +#endif + + IsNever3(new object()); + [MethodImpl(MethodImplOptions.NoInlining)] + static void IsNever3(object o) + { + if (typeof(T) == typeof(Never3)) + { + Consume(new Canary3()); + } + } +#if !DEBUG + ThrowIfPresentWithUsableMethodTable(typeof(TestTypeOfCodegenBranchElimination), nameof(Canary3)); +#endif + + // ********* + + if (GetUnknownType().GetType() == typeof(Never4)) + { + Consume(new Canary4()); + } +#if !DEBUG + ThrowIfPresentWithUsableMethodTable(typeof(TestTypeOfCodegenBranchElimination), nameof(Canary4)); +#endif + + if (GetUnknownType() is Never5) + { + Consume(new Canary5()); + } +#if !DEBUG + ThrowIfPresentWithUsableMethodTable(typeof(TestTypeOfCodegenBranchElimination), nameof(Canary5)); +#endif + + IsNever6(new object()); + [MethodImpl(MethodImplOptions.NoInlining)] + static void IsNever6(object o) + { + if (typeof(T) == typeof(Never6)) + { + Consume(new Canary6()); + } + } +#if !DEBUG + ThrowIfPresentWithUsableMethodTable(typeof(TestTypeOfCodegenBranchElimination), nameof(Canary6)); +#endif + + // ************ + + Activator.CreateInstance(typeof(Maybe1<>).MakeGenericType(GetAtom1())); + + if (GetUnknownType().GetType() == typeof(Maybe1)) + { + // This should not be optimized away because Maybe1 is possible + // with the type loader template for MakeGeneric above. + Consume(new Marker1()); + } + ThrowIfNotPresent(typeof(TestTypeOfCodegenBranchElimination), nameof(Marker1)); + + [MethodImpl(MethodImplOptions.NoInlining)] + static void Consume(object o) { } + + [MethodImpl(MethodImplOptions.NoInlining)] + static object GetUnknownType() => new object(); + + [MethodImpl(MethodImplOptions.NoInlining)] + static Type GetAtom1() => typeof(Atom1); + } + } + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2070:UnrecognizedReflectionPattern", Justification = "That's the point")] private static Type GetTypeSecretly(Type testType, string typeName) => testType.GetNestedType(typeName, BindingFlags.NonPublic | BindingFlags.Public); From a51509a3d1fe212149f8939dbf0774edb6907b8a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20Strehovsk=C3=BD?= Date: Thu, 14 Mar 2024 22:37:45 +0100 Subject: [PATCH 02/11] FB --- src/coreclr/inc/corinfo.h | 5 +---- src/coreclr/jit/gentree.cpp | 2 +- src/coreclr/jit/importer.cpp | 4 ++-- src/coreclr/jit/importercalls.cpp | 2 +- .../tools/Common/JitInterface/CorInfoTypes.cs | 1 - .../JitInterface/CorInfoImpl.RyuJit.cs | 12 +++++++----- .../TrimmingBehaviors/DeadCodeElimination.cs | 17 +++++++++++++++++ 7 files changed, 29 insertions(+), 14 deletions(-) diff --git a/src/coreclr/inc/corinfo.h b/src/coreclr/inc/corinfo.h index 0ace173e272b17..773fcdeb97d6da 100644 --- a/src/coreclr/inc/corinfo.h +++ b/src/coreclr/inc/corinfo.h @@ -1247,7 +1247,6 @@ struct CORINFO_LOOKUP_KIND #define CORINFO_USEHELPER ((uint16_t) 0xffff) #define CORINFO_USENULL ((uint16_t) 0xfffe) #define CORINFO_NO_SIZE_CHECK ((uint16_t) 0xffff) -#define CORINFO_NEVER_INSTANTIATED (-1) struct CORINFO_RUNTIME_LOOKUP { @@ -2981,11 +2980,9 @@ class ICorStaticInfo CORINFO_CLASS_HANDLE* vcTypeRet /* OUT */ ) = 0; - // Obtains a list of exact classes for a given base type. Returns 0 if the number of + // Obtains a list of exact classes for a given base type. Returns -1 if the number of // the exact classes is greater than maxExactClasses or if more types might be loaded // in future. - // When called with maxExactClasses == 0, returns CORINFO_NEVER_INSTANTIATED if an - // exact class of this type cannot exist. Returns 0 otherwise. virtual int getExactClasses( CORINFO_CLASS_HANDLE baseType, /* IN */ int maxExactClasses, /* IN */ diff --git a/src/coreclr/jit/gentree.cpp b/src/coreclr/jit/gentree.cpp index aecbbde518a9b6..1b06e4effa7916 100644 --- a/src/coreclr/jit/gentree.cpp +++ b/src/coreclr/jit/gentree.cpp @@ -14070,7 +14070,7 @@ GenTree* Compiler::gtFoldTypeCompare(GenTree* tree) } // Check if an object of this type can even exist - if (info.compCompHnd->getExactClasses(clsHnd, 0, nullptr) == CORINFO_NEVER_INSTANTIATED) + if (info.compCompHnd->getExactClasses(clsHnd, 0, nullptr) == 0) { JITDUMP("Runtime reported %p (%s) is never allocated\n", dspPtr(clsHnd), eeGetClassName(clsHnd)); diff --git a/src/coreclr/jit/importer.cpp b/src/coreclr/jit/importer.cpp index 9db39a0b9cd0ac..e65a5030326597 100644 --- a/src/coreclr/jit/importer.cpp +++ b/src/coreclr/jit/importer.cpp @@ -5350,9 +5350,9 @@ GenTree* Compiler::impOptimizeCastClassOrIsInst(GenTree* op1, CORINFO_RESOLVED_T } CORINFO_CLASS_HANDLE toClass = pResolvedToken->hClass; - if (info.compCompHnd->getExactClasses(toClass, 0, nullptr) == CORINFO_NEVER_INSTANTIATED) + if (info.compCompHnd->getExactClasses(toClass, 0, nullptr) == 0) { - JITDUMP("\nClass %s%p (%s) can never be allocated\n", dspPtr(toClass), eeGetClassName(toClass)); + JITDUMP("\nClass %p (%s) can never be allocated\n", dspPtr(toClass), eeGetClassName(toClass)); if (!isCastClass) { diff --git a/src/coreclr/jit/importercalls.cpp b/src/coreclr/jit/importercalls.cpp index 7796f2f9700f1e..19b15f93e5cd59 100644 --- a/src/coreclr/jit/importercalls.cpp +++ b/src/coreclr/jit/importercalls.cpp @@ -6552,7 +6552,7 @@ void Compiler::considerGuardedDevirtualization(GenTreeCall* call, { JITDUMP("No exact classes implementing %s\n", eeGetClassName(baseClass)) } - else if (numExactClasses > maxTypeChecks) + else if (numExactClasses < 0 || numExactClasses > maxTypeChecks) { JITDUMP("Too many exact classes implementing %s (%d > %d)\n", eeGetClassName(baseClass), numExactClasses, maxTypeChecks) diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs b/src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs index a23f2cd43eba86..2276c5cdc9a7cc 100644 --- a/src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs +++ b/src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs @@ -26,7 +26,6 @@ public static class CORINFO public const ushort USEHELPER = 0xffff; public const ushort USENULL = 0xfffe; public const ushort CORINFO_NO_SIZE_CHECK = 0xffff; - public const int CORINFO_NEVER_INSTANTIATED = -1; } public struct CORINFO_METHOD_STRUCT_ diff --git a/src/coreclr/tools/aot/ILCompiler.RyuJit/JitInterface/CorInfoImpl.RyuJit.cs b/src/coreclr/tools/aot/ILCompiler.RyuJit/JitInterface/CorInfoImpl.RyuJit.cs index ea3e3e88720d52..f003afa5691f61 100644 --- a/src/coreclr/tools/aot/ILCompiler.RyuJit/JitInterface/CorInfoImpl.RyuJit.cs +++ b/src/coreclr/tools/aot/ILCompiler.RyuJit/JitInterface/CorInfoImpl.RyuJit.cs @@ -2283,16 +2283,18 @@ private int getExactClasses(CORINFO_CLASS_STRUCT_* baseType, int maxExactClasses { MetadataType type = HandleToObject(baseType) as MetadataType; if (type == null) + { + return -1; + } + + if (canNeverHaveInstanceOfSubclassOf(type)) { return 0; } if (maxExactClasses == 0) { - if (canNeverHaveInstanceOfSubclassOf(type)) - return CORINFO.CORINFO_NEVER_INSTANTIATED; - - return 0; + return -1; } // type is already sealed, return it @@ -2305,7 +2307,7 @@ private int getExactClasses(CORINFO_CLASS_STRUCT_* baseType, int maxExactClasses TypeDesc[] implClasses = _compilation.GetImplementingClasses(type); if (implClasses == null || implClasses.Length > maxExactClasses) { - return 0; + return -1; } int index = 0; diff --git a/src/tests/nativeaot/SmokeTests/TrimmingBehaviors/DeadCodeElimination.cs b/src/tests/nativeaot/SmokeTests/TrimmingBehaviors/DeadCodeElimination.cs index acaa40feb861ac..e9247c1d595d8f 100644 --- a/src/tests/nativeaot/SmokeTests/TrimmingBehaviors/DeadCodeElimination.cs +++ b/src/tests/nativeaot/SmokeTests/TrimmingBehaviors/DeadCodeElimination.cs @@ -667,6 +667,15 @@ class Marker1 { } class Atom1 { } + interface IDynamicCastableImplemented { void A(); } + [DynamicInterfaceCastableImplementation] + interface IDynamicCastableImplementedImpl : IDynamicCastableImplemented { void IDynamicCastableImplemented.A() { } } + class DynamicInterfaceCastable : IDynamicInterfaceCastable + { + RuntimeTypeHandle IDynamicInterfaceCastable.GetInterfaceImplementation(RuntimeTypeHandle interfaceType) => typeof(IDynamicCastableImplementedImpl).TypeHandle; + bool IDynamicInterfaceCastable.IsInterfaceImplemented(RuntimeTypeHandle interfaceType, bool throwIfNotImplemented) => true; + } + [UnconditionalSuppressMessage("AotAnalysis", "IL3050:UnrecognizedAotPattern", Justification = "That's the point")] public static void Run() @@ -743,6 +752,14 @@ static void IsNever6(object o) } ThrowIfNotPresent(typeof(TestTypeOfCodegenBranchElimination), nameof(Marker1)); + // ************ + + if (GetDynamicInterfaceCastableType() is not IDynamicCastableImplemented) + throw new Exception(); + + [MethodImpl(MethodImplOptions.NoInlining)] + static object GetDynamicInterfaceCastableType() => new DynamicInterfaceCastable(); + [MethodImpl(MethodImplOptions.NoInlining)] static void Consume(object o) { } From 8e363977544fa241f3f7f94908170d1d3697f98a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20Strehovsk=C3=BD?= Date: Fri, 15 Mar 2024 06:55:35 +0100 Subject: [PATCH 03/11] FB --- src/coreclr/inc/jiteeversionguid.h | 10 +++++----- .../JitInterface/CorInfoImpl.ReadyToRun.cs | 2 +- src/coreclr/vm/jitinterface.cpp | 4 +--- 3 files changed, 7 insertions(+), 9 deletions(-) diff --git a/src/coreclr/inc/jiteeversionguid.h b/src/coreclr/inc/jiteeversionguid.h index 41e58ca24537b8..e7d744d80bf517 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 = { /* 6fd660c7-96be-4832-a84c-4200141f7d08 */ - 0x6fd660c7, - 0x96be, - 0x4832, - {0xa8, 0x4c, 0x42, 0x00, 0x14, 0x1f, 0x7d, 0x08} +constexpr GUID JITEEVersionIdentifier = { /* bdf34b26-0725-4ad6-9935-40bfd2a4c4fc */ + 0xbdf34b26, + 0x0725, + 0x4ad6, + {0x99, 0x35, 0x40, 0xbf, 0xd2, 0xa4, 0xc4, 0xfc} }; ////////////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs index aba1e6bbd36080..e1b1b359c0878a 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs @@ -3280,7 +3280,7 @@ private void updateEntryPointForTailCall(ref CORINFO_CONST_LOOKUP entryPoint) private int getExactClasses(CORINFO_CLASS_STRUCT_* baseType, int maxExactClasses, CORINFO_CLASS_STRUCT_** exactClsRet) { // Not implemented for R2R yet - return 0; + return -1; } private bool getStaticFieldContent(CORINFO_FIELD_STRUCT_* fieldHandle, byte* buffer, int bufferSize, int valueOffset, bool ignoreMovableObjects) diff --git a/src/coreclr/vm/jitinterface.cpp b/src/coreclr/vm/jitinterface.cpp index 05d9365e967964..6a38dcc4e3a08f 100644 --- a/src/coreclr/vm/jitinterface.cpp +++ b/src/coreclr/vm/jitinterface.cpp @@ -9663,8 +9663,6 @@ int CEEInfo::getExactClasses ( MODE_ANY; } CONTRACTL_END; - int exactClassesCount = 0; - JIT_TO_EE_TRANSITION(); // This function is currently implemented only on NativeAOT @@ -9672,7 +9670,7 @@ int CEEInfo::getExactClasses ( EE_TO_JIT_TRANSITION(); - return exactClassesCount; + return -1; } /*********************************************************************/ From d1e26b35f2b5e6becfeb027a84b782e6947d2e02 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20Strehovsk=C3=BD?= Date: Fri, 15 Mar 2024 07:38:13 +0100 Subject: [PATCH 04/11] Update Compilation.csproj --- src/tests/managed/Compilation/Compilation.csproj | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/tests/managed/Compilation/Compilation.csproj b/src/tests/managed/Compilation/Compilation.csproj index f6521d8ff2df4b..304697739aad0d 100644 --- a/src/tests/managed/Compilation/Compilation.csproj +++ b/src/tests/managed/Compilation/Compilation.csproj @@ -4,6 +4,9 @@ true true + + + true From 5f01872a6a6a422eb75fa8135e4256cf7180adfd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20Strehovsk=C3=BD?= Date: Fri, 15 Mar 2024 11:41:12 +0100 Subject: [PATCH 05/11] Fix SPMI --- .../tools/superpmi/superpmi-shared/methodcontext.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.cpp b/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.cpp index 70c5c627bd404c..6f7952477d0645 100644 --- a/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.cpp +++ b/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.cpp @@ -2787,15 +2787,15 @@ void MethodContext::recGetExactClasses(CORINFO_CLASS_HANDLE baseType, int maxExa key.A = CastHandle(baseType); key.B = maxExactClasses; - Assert(result >= 0); + int numResults = result < 0 ? 0 : result; - DWORDLONG* exactClassesAgnostic = new DWORDLONG[result]; - for (int i = 0; i < result; i++) + DWORDLONG* exactClassesAgnostic = new DWORDLONG[numResults]; + for (int i = 0; i < numResults; i++) exactClassesAgnostic[i] = CastHandle(exactClsRet[i]); Agnostic_GetExactClassesResult value; value.numClasses = result; - value.classes = GetExactClasses->AddBuffer((unsigned char*)exactClassesAgnostic, (unsigned int)(result * sizeof(DWORDLONG))); + value.classes = GetExactClasses->AddBuffer((unsigned char*)exactClassesAgnostic, (unsigned int)(numResults * sizeof(DWORDLONG))); delete[] exactClassesAgnostic; From 83d4e47052dc8603a2296ebee9f9ee253451b223 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20Strehovsk=C3=BD?= Date: Sat, 16 Mar 2024 11:55:25 +0100 Subject: [PATCH 06/11] FB --- src/coreclr/jit/gentree.cpp | 31 +++++-------------- .../TrimmingBehaviors/DeadCodeElimination.cs | 10 +++--- 2 files changed, 12 insertions(+), 29 deletions(-) diff --git a/src/coreclr/jit/gentree.cpp b/src/coreclr/jit/gentree.cpp index 843a385f8f9ab5..0db384e5c69a0c 100644 --- a/src/coreclr/jit/gentree.cpp +++ b/src/coreclr/jit/gentree.cpp @@ -14093,8 +14093,13 @@ GenTree* Compiler::gtFoldTypeCompare(GenTree* tree) return compare; } - // In the following code, we need one of these to be a handle. - if (op1Kind != TPK_Handle && op2Kind != TPK_Handle) + // If one operand creates a type from a handle and the other operand is fetching the type from an object, + // we can sometimes optimize the type compare into a simpler + // method table comparison. + // + // TODO: if other operand is null... + if (!(((op1Kind == TPK_GetType) && (op2Kind == TPK_Handle)) || + ((op1Kind == TPK_Handle) && (op2Kind == TPK_GetType)))) { return tree; } @@ -14112,28 +14117,6 @@ GenTree* Compiler::gtFoldTypeCompare(GenTree* tree) return tree; } - // Check if an object of this type can even exist - if (info.compCompHnd->getExactClasses(clsHnd, 0, nullptr) == 0) - { - JITDUMP("Runtime reported %p (%s) is never allocated\n", dspPtr(clsHnd), eeGetClassName(clsHnd)); - - const bool operatorIsEQ = (oper == GT_EQ); - const int compareResult = operatorIsEQ ? 0 : 1; - JITDUMP("Runtime reports comparison is known at jit time: %u\n", compareResult); - GenTree* result = gtNewIconNode(compareResult); - return result; - } - - // If one operand creates a type from a handle and the other operand is fetching the type from an object, - // we can sometimes optimize the type compare into a simpler - // method table comparison. - // - // TODO: if other operand is null... - if (op1Kind != TPK_GetType && op2Kind != TPK_GetType) - { - return tree; - } - // We're good to go. JITDUMP("Optimizing compare of obj.GetType()" " and type-from-handle to compare method table pointer\n"); diff --git a/src/tests/nativeaot/SmokeTests/TrimmingBehaviors/DeadCodeElimination.cs b/src/tests/nativeaot/SmokeTests/TrimmingBehaviors/DeadCodeElimination.cs index e9247c1d595d8f..b1076b3e0b7140 100644 --- a/src/tests/nativeaot/SmokeTests/TrimmingBehaviors/DeadCodeElimination.cs +++ b/src/tests/nativeaot/SmokeTests/TrimmingBehaviors/DeadCodeElimination.cs @@ -684,7 +684,7 @@ public static void Run() { Consume(new Canary1()); } -#if !DEBUG +#if false // Not yet implemented ThrowIfPresentWithUsableMethodTable(typeof(TestTypeOfCodegenBranchElimination), nameof(Canary1)); #endif @@ -705,7 +705,7 @@ static void IsNever3(object o) Consume(new Canary3()); } } -#if !DEBUG +#if false // Not yet implemented ThrowIfPresentWithUsableMethodTable(typeof(TestTypeOfCodegenBranchElimination), nameof(Canary3)); #endif @@ -715,7 +715,7 @@ static void IsNever3(object o) { Consume(new Canary4()); } -#if !DEBUG +#if false // Not yet implemented ThrowIfPresentWithUsableMethodTable(typeof(TestTypeOfCodegenBranchElimination), nameof(Canary4)); #endif @@ -736,7 +736,7 @@ static void IsNever6(object o) Consume(new Canary6()); } } -#if !DEBUG +#if false // Not yet implemented ThrowIfPresentWithUsableMethodTable(typeof(TestTypeOfCodegenBranchElimination), nameof(Canary6)); #endif @@ -744,7 +744,7 @@ static void IsNever6(object o) Activator.CreateInstance(typeof(Maybe1<>).MakeGenericType(GetAtom1())); - if (GetUnknownType().GetType() == typeof(Maybe1)) + if (GetUnknownType() is Maybe1) { // This should not be optimized away because Maybe1 is possible // with the type loader template for MakeGeneric above. From 40d7553795c604039db9a5ab32949679b00af0e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20Strehovsk=C3=BD?= Date: Sat, 16 Mar 2024 11:56:02 +0100 Subject: [PATCH 07/11] Update src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ILScanner.cs Co-authored-by: Jan Kotas --- .../tools/aot/ILCompiler.Compiler/Compiler/ILScanner.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ILScanner.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ILScanner.cs index af3ae5021dece9..30738a6bfdc51e 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ILScanner.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ILScanner.cs @@ -444,7 +444,11 @@ public ScannedDevirtualizationManager(NodeFactory factory, ImmutableArray Date: Sat, 16 Mar 2024 13:43:46 +0100 Subject: [PATCH 08/11] Update ILScanner.cs --- src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ILScanner.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ILScanner.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ILScanner.cs index 30738a6bfdc51e..6500a5be49718b 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ILScanner.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ILScanner.cs @@ -444,7 +444,7 @@ public ScannedDevirtualizationManager(NodeFactory factory, ImmutableArray Date: Sun, 17 Mar 2024 06:04:33 +0100 Subject: [PATCH 09/11] FB --- .../tools/Common/Compiler/DevirtualizationManager.cs | 2 +- src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs | 2 +- .../tools/aot/ILCompiler.Compiler/Compiler/Compilation.cs | 4 ++-- .../tools/aot/ILCompiler.Compiler/Compiler/ILScanner.cs | 2 +- .../ILCompiler.RyuJit/JitInterface/CorInfoImpl.RyuJit.cs | 6 +++--- src/tests/managed/Compilation/Compilation.csproj | 3 --- 6 files changed, 8 insertions(+), 11 deletions(-) diff --git a/src/coreclr/tools/Common/Compiler/DevirtualizationManager.cs b/src/coreclr/tools/Common/Compiler/DevirtualizationManager.cs index 2145460177a4e2..5574a9f8aa100f 100644 --- a/src/coreclr/tools/Common/Compiler/DevirtualizationManager.cs +++ b/src/coreclr/tools/Common/Compiler/DevirtualizationManager.cs @@ -213,7 +213,7 @@ protected virtual MethodDesc ResolveVirtualMethod(MethodDesc declMethod, DefType /// exist. This is similar to , but will return true /// for List<__Canon> if a constructed MethodTable for List<object> exists. /// - public virtual bool CanTypeOrCanonicalFormOfTypeBeAllocated(TypeDesc type) => true; + public virtual bool CanReferenceConstructedTypeOrCanonicalFormOfType(TypeDesc type) => true; public virtual TypeDesc[] GetImplementingClasses(TypeDesc type) => null; #endif diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs index 14882fd56d8a65..ea4d4b3cde3c9b 100644 --- a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs +++ b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs @@ -2883,7 +2883,7 @@ private TypeCompareState compareTypesForEquality(CORINFO_CLASS_STRUCT_* cls1, CO #if !READYTORUN if (result == TypeCompareState.May - && (canNeverHaveInstanceOfSubclassOf(type1) || canNeverHaveInstanceOfSubclassOf(type2))) + && (CanNeverHaveInstanceOfSubclassOf(type1) || CanNeverHaveInstanceOfSubclassOf(type2))) { return TypeCompareState.MustNot; } diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Compilation.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Compilation.cs index b6a84b82978870..0f5a9d3c15293b 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Compilation.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Compilation.cs @@ -108,9 +108,9 @@ public bool CanReferenceConstructedMethodTable(TypeDesc type) return NodeFactory.DevirtualizationManager.CanReferenceConstructedMethodTable(type); } - public bool CanTypeOrCanonicalFormOfTypeBeAllocated(TypeDesc type) + public bool CanReferenceConstructedTypeOrCanonicalFormOfType(TypeDesc type) { - return NodeFactory.DevirtualizationManager.CanTypeOrCanonicalFormOfTypeBeAllocated(type); + return NodeFactory.DevirtualizationManager.CanReferenceConstructedTypeOrCanonicalFormOfType(type); } public DelegateCreationInfo GetDelegateCtor(TypeDesc delegateType, MethodDesc target, TypeDesc constrainedType, bool followVirtualDispatch) diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ILScanner.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ILScanner.cs index 6500a5be49718b..46e3df57efd1ff 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ILScanner.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ILScanner.cs @@ -696,7 +696,7 @@ protected override MethodDesc ResolveVirtualMethod(MethodDesc declMethod, DefTyp public override bool CanReferenceConstructedMethodTable(TypeDesc type) => _constructedMethodTables.Contains(type); - public override bool CanTypeOrCanonicalFormOfTypeBeAllocated(TypeDesc type) + public override bool CanReferenceConstructedTypeOrCanonicalFormOfType(TypeDesc type) => _constructedMethodTables.Contains(type) || _canonConstructedMethodTables.Contains(type); public override TypeDesc[] GetImplementingClasses(TypeDesc type) diff --git a/src/coreclr/tools/aot/ILCompiler.RyuJit/JitInterface/CorInfoImpl.RyuJit.cs b/src/coreclr/tools/aot/ILCompiler.RyuJit/JitInterface/CorInfoImpl.RyuJit.cs index 4c03d20fa73735..f98d7b0fc06505 100644 --- a/src/coreclr/tools/aot/ILCompiler.RyuJit/JitInterface/CorInfoImpl.RyuJit.cs +++ b/src/coreclr/tools/aot/ILCompiler.RyuJit/JitInterface/CorInfoImpl.RyuJit.cs @@ -2248,7 +2248,7 @@ private void getFieldInfo(ref CORINFO_RESOLVED_TOKEN pResolvedToken, CORINFO_MET // and STS::AccessCheck::CanAccess. } - private bool canNeverHaveInstanceOfSubclassOf(TypeDesc type) + private bool CanNeverHaveInstanceOfSubclassOf(TypeDesc type) { // Don't try to optimize nullable if (type.IsNullable) @@ -2262,7 +2262,7 @@ private bool canNeverHaveInstanceOfSubclassOf(TypeDesc type) // If we don't have a constructed MethodTable for the exact type or for its template, // this type or any of its subclasses can never be instantiated. - return !_compilation.CanTypeOrCanonicalFormOfTypeBeAllocated(type) + return !_compilation.CanReferenceConstructedTypeOrCanonicalFormOfType(type) && (type == canonType || !_compilation.CanReferenceConstructedMethodTable(canonType)); } @@ -2274,7 +2274,7 @@ private int getExactClasses(CORINFO_CLASS_STRUCT_* baseType, int maxExactClasses return -1; } - if (canNeverHaveInstanceOfSubclassOf(type)) + if (CanNeverHaveInstanceOfSubclassOf(type)) { return 0; } diff --git a/src/tests/managed/Compilation/Compilation.csproj b/src/tests/managed/Compilation/Compilation.csproj index 304697739aad0d..f6521d8ff2df4b 100644 --- a/src/tests/managed/Compilation/Compilation.csproj +++ b/src/tests/managed/Compilation/Compilation.csproj @@ -4,9 +4,6 @@ true true - - - true From b8b7a0229a5d61e329f49a78616dc1cffef7aa3e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20Strehovsk=C3=BD?= Date: Mon, 18 Mar 2024 07:20:23 +0100 Subject: [PATCH 10/11] I deleted the wrong part... --- src/coreclr/jit/gentree.cpp | 12 ++++++++++++ src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs | 12 +----------- .../TrimmingBehaviors/DeadCodeElimination.cs | 10 +++++----- 3 files changed, 18 insertions(+), 16 deletions(-) diff --git a/src/coreclr/jit/gentree.cpp b/src/coreclr/jit/gentree.cpp index 0db384e5c69a0c..90677f8f4975c1 100644 --- a/src/coreclr/jit/gentree.cpp +++ b/src/coreclr/jit/gentree.cpp @@ -14117,6 +14117,18 @@ GenTree* Compiler::gtFoldTypeCompare(GenTree* tree) return tree; } + // Check if an object of this type can even exist + if (info.compCompHnd->getExactClasses(clsHnd, 0, nullptr) == 0) + { + JITDUMP("Runtime reported %p (%s) is never allocated\n", dspPtr(clsHnd), eeGetClassName(clsHnd)); + + const bool operatorIsEQ = (oper == GT_EQ); + const int compareResult = operatorIsEQ ? 0 : 1; + JITDUMP("Runtime reports comparison is known at jit time: %u\n", compareResult); + GenTree* result = gtNewIconNode(compareResult); + return result; + } + // We're good to go. JITDUMP("Optimizing compare of obj.GetType()" " and type-from-handle to compare method table pointer\n"); diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs index ea4d4b3cde3c9b..9366e1aa65b6d3 100644 --- a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs +++ b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs @@ -2874,22 +2874,12 @@ private TypeCompareState compareTypesForEquality(CORINFO_CLASS_STRUCT_* cls1, CO TypeDesc type1 = HandleToObject(cls1); TypeDesc type2 = HandleToObject(cls2); - TypeCompareState result = TypeExtensions.CompareTypesForEquality(type1, type2) switch + return TypeExtensions.CompareTypesForEquality(type1, type2) switch { true => TypeCompareState.Must, false => TypeCompareState.MustNot, _ => TypeCompareState.May, }; - -#if !READYTORUN - if (result == TypeCompareState.May - && (CanNeverHaveInstanceOfSubclassOf(type1) || CanNeverHaveInstanceOfSubclassOf(type2))) - { - return TypeCompareState.MustNot; - } -#endif - - return result; } private bool isMoreSpecificType(CORINFO_CLASS_STRUCT_* cls1, CORINFO_CLASS_STRUCT_* cls2) diff --git a/src/tests/nativeaot/SmokeTests/TrimmingBehaviors/DeadCodeElimination.cs b/src/tests/nativeaot/SmokeTests/TrimmingBehaviors/DeadCodeElimination.cs index b1076b3e0b7140..e9247c1d595d8f 100644 --- a/src/tests/nativeaot/SmokeTests/TrimmingBehaviors/DeadCodeElimination.cs +++ b/src/tests/nativeaot/SmokeTests/TrimmingBehaviors/DeadCodeElimination.cs @@ -684,7 +684,7 @@ public static void Run() { Consume(new Canary1()); } -#if false // Not yet implemented +#if !DEBUG ThrowIfPresentWithUsableMethodTable(typeof(TestTypeOfCodegenBranchElimination), nameof(Canary1)); #endif @@ -705,7 +705,7 @@ static void IsNever3(object o) Consume(new Canary3()); } } -#if false // Not yet implemented +#if !DEBUG ThrowIfPresentWithUsableMethodTable(typeof(TestTypeOfCodegenBranchElimination), nameof(Canary3)); #endif @@ -715,7 +715,7 @@ static void IsNever3(object o) { Consume(new Canary4()); } -#if false // Not yet implemented +#if !DEBUG ThrowIfPresentWithUsableMethodTable(typeof(TestTypeOfCodegenBranchElimination), nameof(Canary4)); #endif @@ -736,7 +736,7 @@ static void IsNever6(object o) Consume(new Canary6()); } } -#if false // Not yet implemented +#if !DEBUG ThrowIfPresentWithUsableMethodTable(typeof(TestTypeOfCodegenBranchElimination), nameof(Canary6)); #endif @@ -744,7 +744,7 @@ static void IsNever6(object o) Activator.CreateInstance(typeof(Maybe1<>).MakeGenericType(GetAtom1())); - if (GetUnknownType() is Maybe1) + if (GetUnknownType().GetType() == typeof(Maybe1)) { // This should not be optimized away because Maybe1 is possible // with the type loader template for MakeGeneric above. From a2891f4fe6087434327955e31055524999ba6332 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20Strehovsk=C3=BD?= Date: Mon, 18 Mar 2024 08:47:02 +0100 Subject: [PATCH 11/11] Disable the tests that relied on compareTypesForEquality logic --- .../SmokeTests/TrimmingBehaviors/DeadCodeElimination.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/tests/nativeaot/SmokeTests/TrimmingBehaviors/DeadCodeElimination.cs b/src/tests/nativeaot/SmokeTests/TrimmingBehaviors/DeadCodeElimination.cs index e9247c1d595d8f..076ce813828c37 100644 --- a/src/tests/nativeaot/SmokeTests/TrimmingBehaviors/DeadCodeElimination.cs +++ b/src/tests/nativeaot/SmokeTests/TrimmingBehaviors/DeadCodeElimination.cs @@ -705,7 +705,7 @@ static void IsNever3(object o) Consume(new Canary3()); } } -#if !DEBUG +#if false // This optimization is disabled for now, don't check. ThrowIfPresentWithUsableMethodTable(typeof(TestTypeOfCodegenBranchElimination), nameof(Canary3)); #endif @@ -736,7 +736,7 @@ static void IsNever6(object o) Consume(new Canary6()); } } -#if !DEBUG +#if false // This optimization is disabled for now, don't check. ThrowIfPresentWithUsableMethodTable(typeof(TestTypeOfCodegenBranchElimination), nameof(Canary6)); #endif