From a73abd2966d75393a2baf52dfcbcfda4d8ba0f8d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20Strehovsk=C3=BD?= Date: Fri, 21 Feb 2025 12:34:06 +0100 Subject: [PATCH 1/5] Differentiate `MethodTable`s that are/aren't visible to reflection Fixes #112029 --- .../Compiler/DevirtualizationManager.cs | 2 + .../ConstructedEETypeNode.cs | 4 +- .../Compiler/DependencyAnalysis/EETypeNode.cs | 6 +- .../GenericDefinitionEETypeNode.cs | 69 +++++++++++++++---- .../DependencyAnalysis/NodeFactory.cs | 8 ++- .../ILCompiler.Compiler/Compiler/ILScanner.cs | 12 ++++ .../Compiler/RyuJitCompilation.cs | 6 ++ 7 files changed, 87 insertions(+), 20 deletions(-) diff --git a/src/coreclr/tools/Common/Compiler/DevirtualizationManager.cs b/src/coreclr/tools/Common/Compiler/DevirtualizationManager.cs index 5574a9f8aa100f..2728cb113d3a89 100644 --- a/src/coreclr/tools/Common/Compiler/DevirtualizationManager.cs +++ b/src/coreclr/tools/Common/Compiler/DevirtualizationManager.cs @@ -202,6 +202,8 @@ protected virtual MethodDesc ResolveVirtualMethod(MethodDesc declMethod, DefType } #if !READYTORUN + public virtual bool IsGenericDefinitionMethodTableReflectionVisible(TypeDesc type) => true; + /// /// 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). diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ConstructedEETypeNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ConstructedEETypeNode.cs index 1905d09e64ef0a..d564640f4d2091 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ConstructedEETypeNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ConstructedEETypeNode.cs @@ -121,8 +121,8 @@ public static bool CreationAllowed(TypeDesc type) default: // Generic definition EETypes can't be allocated - if (type.IsGenericDefinition) - return false; + //if (type.IsGenericDefinition) + // return false; // Full MethodTable of System.Canon should never be used. if (type.IsCanonicalDefinitionType(CanonicalFormKind.Any)) diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/EETypeNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/EETypeNode.cs index 70d9bd026c68b7..345d4edb6e1b70 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/EETypeNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/EETypeNode.cs @@ -1135,7 +1135,8 @@ protected void OutputGenericInstantiationDetails(NodeFactory factory, ref Object { if (!_type.IsTypeDefinition) { - IEETypeNode typeDefNode = factory.NecessaryTypeSymbol(_type.GetTypeDefinition()); + IEETypeNode typeDefNode = factory.MaximallyConstructableType(_type) == this ? + factory.ConstructedTypeSymbol(_type.GetTypeDefinition()) : factory.NecessaryTypeSymbol(_type.GetTypeDefinition()); if (factory.Target.SupportsRelativePointers) objData.EmitReloc(typeDefNode, RelocType.IMAGE_REL_BASED_RELPTR32); else @@ -1312,7 +1313,8 @@ public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) // If the whole program view contains a reference to a preallocated RuntimeType // instance for this type, generate a reference to it. // Otherwise, generate as zero to save size. - if (!_type.Type.IsCanonicalSubtype(CanonicalFormKind.Any) + if (!relocsOnly + && !_type.Type.IsCanonicalSubtype(CanonicalFormKind.Any) && _type.GetFrozenRuntimeTypeNode(factory) is { Marked: true } runtimeTypeObject) { builder.EmitPointerReloc(runtimeTypeObject); diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/GenericDefinitionEETypeNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/GenericDefinitionEETypeNode.cs index 22cd3c46497340..8521dbf9fab206 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/GenericDefinitionEETypeNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/GenericDefinitionEETypeNode.cs @@ -2,32 +2,23 @@ // The .NET Foundation licenses this file to you under the MIT license. using Internal.Runtime; +using Internal.Text; using Internal.TypeSystem; using Debug = System.Diagnostics.Debug; namespace ILCompiler.DependencyAnalysis { - internal sealed class GenericDefinitionEETypeNode : EETypeNode + internal abstract class GenericDefinitionEETypeNode : EETypeNode { public GenericDefinitionEETypeNode(NodeFactory factory, TypeDesc type) : base(factory, type) { Debug.Assert(type.IsGenericDefinition); } - public override bool ShouldSkipEmittingObjectNode(NodeFactory factory) + public override ISymbolNode NodeForLinkage(NodeFactory factory) { - return false; - } - - protected override DependencyList ComputeNonRelocationBasedDependencies(NodeFactory factory) - { - DependencyList dependencyList = null; - - // Ask the metadata manager if we have any dependencies due to the presence of the EEType. - factory.MetadataManager.GetDependenciesDueToEETypePresence(ref dependencyList, factory, _type); - - return dependencyList; + return factory.NecessaryTypeSymbol(_type); } protected override ObjectData GetDehydratableData(NodeFactory factory, bool relocsOnly = false) @@ -63,7 +54,57 @@ protected override ObjectData GetDehydratableData(NodeFactory factory, bool relo return dataBuilder.ToObjectData(); } + } + + internal sealed class ReflectionInvisibleGenericDefinitionEETypeNode : GenericDefinitionEETypeNode + { + public ReflectionInvisibleGenericDefinitionEETypeNode(NodeFactory factory, TypeDesc type) : base(factory, type) + { + } + + public override bool ShouldSkipEmittingObjectNode(NodeFactory factory) + { + return factory.ConstructedTypeSymbol(_type).Marked; + } + + protected override DependencyList ComputeNonRelocationBasedDependencies(NodeFactory factory) + { + return new DependencyList(); + } + + public override int ClassCode => -287423988; + } + + internal sealed class ReflectionVisibleGenericDefinitionEETypeNode : GenericDefinitionEETypeNode + { + public ReflectionVisibleGenericDefinitionEETypeNode(NodeFactory factory, TypeDesc type) : base(factory, type) + { + } + + public override bool ShouldSkipEmittingObjectNode(NodeFactory factory) + { + return false; + } + + protected override FrozenRuntimeTypeNode GetFrozenRuntimeTypeNode(NodeFactory factory) + { + return factory.SerializedConstructedRuntimeTypeObject(_type); + } + + protected override string GetName(NodeFactory factory) => this.GetMangledName(factory.NameMangler) + " reflection visible"; + + protected override DependencyList ComputeNonRelocationBasedDependencies(NodeFactory factory) + { + var dependencyList = new DependencyList(); + + dependencyList.Add(factory.NecessaryTypeSymbol(_type), "Reflection invisible type for a visible type"); + + // Ask the metadata manager if we have any dependencies due to the presence of the EEType. + factory.MetadataManager.GetDependenciesDueToEETypePresence(ref dependencyList, factory, _type); + + return dependencyList; + } - public override int ClassCode => -160325006; + public override int ClassCode => 983279111; } } diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/NodeFactory.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/NodeFactory.cs index d66f9fcc413a65..90f21bc2a992a5 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/NodeFactory.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/NodeFactory.cs @@ -591,7 +591,7 @@ private IEETypeNode CreateNecessaryTypeNode(TypeDesc type) { if (type.IsGenericDefinition) { - return new GenericDefinitionEETypeNode(this, type); + return new ReflectionInvisibleGenericDefinitionEETypeNode(this, type); } else if (type.IsCanonicalDefinitionType(CanonicalFormKind.Any)) { @@ -620,7 +620,11 @@ private IEETypeNode CreateConstructedTypeNode(TypeDesc type) if (_compilationModuleGroup.ContainsType(type)) { - if (type.IsCanonicalSubtype(CanonicalFormKind.Any)) + if (type.IsGenericDefinition) + { + return new ReflectionVisibleGenericDefinitionEETypeNode(this, type); + } + else if (type.IsCanonicalSubtype(CanonicalFormKind.Any)) { return new CanonicalEETypeNode(this, type); } diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ILScanner.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ILScanner.cs index 4d9328f1a34eef..d0773ba4806468 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ILScanner.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ILScanner.cs @@ -436,6 +436,7 @@ public override DictionaryLayoutNode GetLayout(TypeSystemEntity methodOrType) private sealed class ScannedDevirtualizationManager : DevirtualizationManager { private HashSet _constructedMethodTables = new HashSet(); + private HashSet _reflectionVisibleGenericDefinitionMethodTables = new HashSet(); private HashSet _canonConstructedMethodTables = new HashSet(); private HashSet _canonConstructedTypes = new HashSet(); private HashSet _unsealedTypes = new HashSet(); @@ -456,6 +457,11 @@ public ScannedDevirtualizationManager(NodeFactory factory, ImmutableArray eetypeNode.Type, @@ -736,6 +742,12 @@ public override bool CanReferenceConstructedTypeOrCanonicalFormOfType(TypeDesc t return _constructedMethodTables.Contains(type) || _canonConstructedMethodTables.Contains(type); } + public override bool IsGenericDefinitionMethodTableReflectionVisible(TypeDesc type) + { + Debug.Assert(type.IsGenericDefinition); + return _reflectionVisibleGenericDefinitionMethodTables.Contains(type); + } + public override TypeDesc[] GetImplementingClasses(TypeDesc type) { if (_disqualifiedTypes.Contains(type)) diff --git a/src/coreclr/tools/aot/ILCompiler.RyuJit/Compiler/RyuJitCompilation.cs b/src/coreclr/tools/aot/ILCompiler.RyuJit/Compiler/RyuJitCompilation.cs index 99e4a10809d196..2f1305289f1d05 100644 --- a/src/coreclr/tools/aot/ILCompiler.RyuJit/Compiler/RyuJitCompilation.cs +++ b/src/coreclr/tools/aot/ILCompiler.RyuJit/Compiler/RyuJitCompilation.cs @@ -77,6 +77,9 @@ public override IEETypeNode NecessaryTypeSymbolIfPossible(TypeDesc type) if (canPotentiallyConstruct) return _nodeFactory.MaximallyConstructableType(type); + if (type.IsGenericDefinition && NodeFactory.DevirtualizationManager.IsGenericDefinitionMethodTableReflectionVisible(type)) + return _nodeFactory.ConstructedTypeSymbol(type); + return _nodeFactory.NecessaryTypeSymbol(type); } @@ -87,6 +90,9 @@ public FrozenRuntimeTypeNode NecessaryRuntimeTypeIfPossible(TypeDesc type) if (canPotentiallyConstruct) return _nodeFactory.SerializedMaximallyConstructableRuntimeTypeObject(type); + if (type.IsGenericDefinition && NodeFactory.DevirtualizationManager.IsGenericDefinitionMethodTableReflectionVisible(type)) + return _nodeFactory.SerializedConstructedRuntimeTypeObject(type); + return _nodeFactory.SerializedNecessaryRuntimeTypeObject(type); } From 846eb57d91b50aa88029c0560c7aabed7071a0e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20Strehovsk=C3=BD?= Date: Tue, 25 Feb 2025 12:48:11 +0100 Subject: [PATCH 2/5] Tweaks --- .../Compiler/Compilation.cs | 12 +++++++--- .../ConstructedEETypeNode.cs | 4 ++-- .../Compiler/DependencyAnalysis/EETypeNode.cs | 6 ++--- .../GenericDefinitionEETypeNode.cs | 5 ++++ .../NativeLayoutVertexNode.cs | 3 ++- .../DependencyAnalysis/NodeFactory.cs | 4 ++-- .../Compiler/RyuJitCompilation.cs | 24 +++++-------------- 7 files changed, 29 insertions(+), 29 deletions(-) diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Compilation.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Compilation.cs index 6a542d8d00a748..da836366290959 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Compilation.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Compilation.cs @@ -266,9 +266,15 @@ public bool NeedsRuntimeLookup(ReadyToRunHelperId lookupKind, object targetOfLoo public ReadyToRunHelperId GetLdTokenHelperForType(TypeDesc type) { - return (ConstructedEETypeNode.CreationAllowed(type) && NodeFactory.DevirtualizationManager.CanReferenceConstructedMethodTable(type.NormalizeInstantiation())) - ? ReadyToRunHelperId.TypeHandle - : ReadyToRunHelperId.NecessaryTypeHandle; + bool canPotentiallyConstruct = ConstructedEETypeNode.CreationAllowed(type) + && NodeFactory.DevirtualizationManager.CanReferenceConstructedMethodTable(type.NormalizeInstantiation()); + if (canPotentiallyConstruct) + return ReadyToRunHelperId.TypeHandle; + + if (type.IsGenericDefinition && NodeFactory.DevirtualizationManager.IsGenericDefinitionMethodTableReflectionVisible(type)) + return ReadyToRunHelperId.TypeHandle; + + return ReadyToRunHelperId.NecessaryTypeHandle; } public static MethodDesc GetConstructorForCreateInstanceIntrinsic(TypeDesc type) diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ConstructedEETypeNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ConstructedEETypeNode.cs index d564640f4d2091..1905d09e64ef0a 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ConstructedEETypeNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ConstructedEETypeNode.cs @@ -121,8 +121,8 @@ public static bool CreationAllowed(TypeDesc type) default: // Generic definition EETypes can't be allocated - //if (type.IsGenericDefinition) - // return false; + if (type.IsGenericDefinition) + return false; // Full MethodTable of System.Canon should never be used. if (type.IsCanonicalDefinitionType(CanonicalFormKind.Any)) diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/EETypeNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/EETypeNode.cs index 345d4edb6e1b70..a37fb8d8d85c86 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/EETypeNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/EETypeNode.cs @@ -272,7 +272,7 @@ private bool CanonFormTypeMayExist } } - public sealed override bool HasConditionalStaticDependencies + public override bool HasConditionalStaticDependencies { get { @@ -323,7 +323,7 @@ public sealed override bool HasConditionalStaticDependencies } } - public sealed override IEnumerable GetConditionalStaticDependencies(NodeFactory factory) + public override IEnumerable GetConditionalStaticDependencies(NodeFactory factory) { List result = new List(); @@ -353,7 +353,7 @@ public sealed override IEnumerable GetConditionalSt "Information about static bases for type with template")); } - if (!_type.IsGenericDefinition && !_type.IsCanonicalSubtype(CanonicalFormKind.Any)) + if (!_type.IsCanonicalSubtype(CanonicalFormKind.Any)) { foreach (DefType iface in _type.RuntimeInterfaces) { diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/GenericDefinitionEETypeNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/GenericDefinitionEETypeNode.cs index 8521dbf9fab206..a87f87bdcb425f 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/GenericDefinitionEETypeNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/GenericDefinitionEETypeNode.cs @@ -1,6 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Collections.Generic; using Internal.Runtime; using Internal.Text; using Internal.TypeSystem; @@ -16,6 +17,10 @@ public GenericDefinitionEETypeNode(NodeFactory factory, TypeDesc type) : base(fa Debug.Assert(type.IsGenericDefinition); } + public override bool HasConditionalStaticDependencies => false; + + public override IEnumerable GetConditionalStaticDependencies(NodeFactory factory) => null; + public override ISymbolNode NodeForLinkage(NodeFactory factory) { return factory.NecessaryTypeSymbol(_type); diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/NativeLayoutVertexNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/NativeLayoutVertexNode.cs index ce1c0aa01e3c6e..f807b6b8e6c037 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/NativeLayoutVertexNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/NativeLayoutVertexNode.cs @@ -569,7 +569,8 @@ public override IEnumerable GetStaticDependencies(NodeFacto // A necessary EEType might be enough for some cases. // But we definitely need constructed if this is e.g. layout for a typehandle. // Measurements show this doesn't amount to much (0.004% - 0.3% size cost vs Necessary). - new DependencyListEntry(context.MaximallyConstructableType(_type), "NativeLayoutEETypeVertexNode containing type signature") + new DependencyListEntry(_type.IsGenericDefinition ? context.NecessaryTypeSymbol(_type) : context.MaximallyConstructableType(_type), + "NativeLayoutEETypeVertexNode containing type signature") }; } public override Vertex WriteVertex(NodeFactory factory) diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/NodeFactory.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/NodeFactory.cs index 90f21bc2a992a5..f7ef925ea3f6f9 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/NodeFactory.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/NodeFactory.cs @@ -707,7 +707,7 @@ public IEETypeNode ConstructedTypeSymbol(TypeDesc type) public IEETypeNode MaximallyConstructableType(TypeDesc type) { - if (ConstructedEETypeNode.CreationAllowed(type)) + if (ConstructedEETypeNode.CreationAllowed(type) || type.IsGenericDefinition) return ConstructedTypeSymbol(type); else return NecessaryTypeSymbol(type); @@ -1383,7 +1383,7 @@ public SerializedFrozenObjectNode SerializedFrozenObject(MetadataType owningType public FrozenRuntimeTypeNode SerializedMaximallyConstructableRuntimeTypeObject(TypeDesc type) { - if (ConstructedEETypeNode.CreationAllowed(type)) + if (ConstructedEETypeNode.CreationAllowed(type) || type.IsGenericDefinition) return SerializedConstructedRuntimeTypeObject(type); return SerializedNecessaryRuntimeTypeObject(type); } diff --git a/src/coreclr/tools/aot/ILCompiler.RyuJit/Compiler/RyuJitCompilation.cs b/src/coreclr/tools/aot/ILCompiler.RyuJit/Compiler/RyuJitCompilation.cs index 2f1305289f1d05..de1d6490de306e 100644 --- a/src/coreclr/tools/aot/ILCompiler.RyuJit/Compiler/RyuJitCompilation.cs +++ b/src/coreclr/tools/aot/ILCompiler.RyuJit/Compiler/RyuJitCompilation.cs @@ -72,28 +72,16 @@ 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 = ConstructedEETypeNode.CreationAllowed(type) - && NodeFactory.DevirtualizationManager.CanReferenceConstructedMethodTable(type); - if (canPotentiallyConstruct) - return _nodeFactory.MaximallyConstructableType(type); - - if (type.IsGenericDefinition && NodeFactory.DevirtualizationManager.IsGenericDefinitionMethodTableReflectionVisible(type)) - return _nodeFactory.ConstructedTypeSymbol(type); - - return _nodeFactory.NecessaryTypeSymbol(type); + return GetLdTokenHelperForType(type) == ReadyToRunHelperId.TypeHandle + ? _nodeFactory.ConstructedTypeSymbol(type) + : _nodeFactory.NecessaryTypeSymbol(type); } public FrozenRuntimeTypeNode NecessaryRuntimeTypeIfPossible(TypeDesc type) { - bool canPotentiallyConstruct = ConstructedEETypeNode.CreationAllowed(type) - && NodeFactory.DevirtualizationManager.CanReferenceConstructedMethodTable(type); - if (canPotentiallyConstruct) - return _nodeFactory.SerializedMaximallyConstructableRuntimeTypeObject(type); - - if (type.IsGenericDefinition && NodeFactory.DevirtualizationManager.IsGenericDefinitionMethodTableReflectionVisible(type)) - return _nodeFactory.SerializedConstructedRuntimeTypeObject(type); - - return _nodeFactory.SerializedNecessaryRuntimeTypeObject(type); + return GetLdTokenHelperForType(type) == ReadyToRunHelperId.TypeHandle + ? _nodeFactory.SerializedConstructedRuntimeTypeObject(type) + : _nodeFactory.SerializedNecessaryRuntimeTypeObject(type); } protected override void CompileInternal(string outputFile, ObjectDumper dumper) From 296de2115ac017ccf176e0d7cd1304fce9ad0fa6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20Strehovsk=C3=BD?= Date: Tue, 25 Feb 2025 14:26:45 +0100 Subject: [PATCH 3/5] Test --- .../Preinitialization/Preinitialization.cs | 17 ++++++++++ .../TrimmingBehaviors/DeadCodeElimination.cs | 34 +++++++++++++++++++ 2 files changed, 51 insertions(+) diff --git a/src/tests/nativeaot/SmokeTests/Preinitialization/Preinitialization.cs b/src/tests/nativeaot/SmokeTests/Preinitialization/Preinitialization.cs index b645995549b22d..96470ff34fcbbb 100644 --- a/src/tests/nativeaot/SmokeTests/Preinitialization/Preinitialization.cs +++ b/src/tests/nativeaot/SmokeTests/Preinitialization/Preinitialization.cs @@ -56,6 +56,7 @@ private static int Main() TestStaticInterfaceMethod.Run(); TestConstrainedCall.Run(); TestTypeHandles.Run(); + TestPreinitDefinition.Run(); TestIsValueType.Run(); TestIndirectLoads.Run(); TestInitBlock.Run(); @@ -1335,6 +1336,22 @@ public static void Run() } } +class TestPreinitDefinition +{ + class Gen; + + class PreinitHolder + { + public readonly static Type TheType = typeof(Gen<>); + } + + public static void Run() + { + Assert.IsPreinitialized(typeof(PreinitHolder)); + Assert.AreEqual("Gen`1", PreinitHolder.TheType.Name); + } +} + class TestIsValueType { class IsValueTypeTests diff --git a/src/tests/nativeaot/SmokeTests/TrimmingBehaviors/DeadCodeElimination.cs b/src/tests/nativeaot/SmokeTests/TrimmingBehaviors/DeadCodeElimination.cs index 032c3a07d7dea6..fe0a2b9e760e25 100644 --- a/src/tests/nativeaot/SmokeTests/TrimmingBehaviors/DeadCodeElimination.cs +++ b/src/tests/nativeaot/SmokeTests/TrimmingBehaviors/DeadCodeElimination.cs @@ -32,6 +32,7 @@ public static int Run() TestUnmodifiableInstanceFieldOptimization.Run(); TestGetMethodOptimization.Run(); TestTypeOfCodegenBranchElimination.Run(); + TestInvisibleGenericsTrimming.Run(); return 100; } @@ -1047,6 +1048,39 @@ static void Consume(object o) { } } } + class TestInvisibleGenericsTrimming + { + class NotPresentType1; + class NotPresentType2; + + class PresentType; + + [MethodImpl(MethodImplOptions.NoInlining)] + static bool IsNotPresentType1(object o) => o is NotPresentType1; + + [MethodImpl(MethodImplOptions.NoInlining)] + static bool IsNotPresentType2(object o) => o.GetType() == typeof(NotPresentType2); + + [MethodImpl(MethodImplOptions.NoInlining)] + static bool IsPresentType(object o) => o.GetType() == typeof(PresentType); + + public static void Run() + { + IsNotPresentType1(new object()); +#if !DEBUG + ThrowIfPresent(typeof(TestInvisibleGenericsTrimming), "NotPresentType1`1"); +#endif + + IsNotPresentType2(new object()); +#if !DEBUG + ThrowIfPresent(typeof(TestInvisibleGenericsTrimming), "NotPresentType2`1"); +#endif + + IsPresentType(new PresentType()); + ThrowIfNotPresent(typeof(TestInvisibleGenericsTrimming), "PresentType`1"); + } + } + [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 eda604af5c976ebe6a936eae59d0997b2478bca3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20Strehovsk=C3=BD?= Date: Tue, 25 Feb 2025 14:26:51 +0100 Subject: [PATCH 4/5] Relax assert --- .../Compiler/AnalysisBasedMetadataManager.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/AnalysisBasedMetadataManager.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/AnalysisBasedMetadataManager.cs index 68b56bd378ee76..854f958b69f916 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/AnalysisBasedMetadataManager.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/AnalysisBasedMetadataManager.cs @@ -101,7 +101,8 @@ public AnalysisBasedMetadataManager( Debug.Assert((GetMetadataCategory(refType.Entity.GetTypeDefinition()) & MetadataCategory.Description) == (GetMetadataCategory(refType.Entity) & MetadataCategory.Description)); - Debug.Assert(!(refType.Entity is MetadataType) || moduleHash.Contains(((MetadataType)refType.Entity).Module)); + Debug.Assert((refType.Category & MetadataCategory.Description) == 0 || + !(refType.Entity is MetadataType) || moduleHash.Contains(((MetadataType)refType.Entity).Module)); } foreach (var refMethod in reflectableMethods) From de183091f2e8f46fe32c6db3a34a9a8e6174b2ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20Strehovsk=C3=BD?= Date: Wed, 26 Feb 2025 07:56:42 +0100 Subject: [PATCH 5/5] Fix multifile --- .../Compiler/MultiFileCompilationModuleGroup.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/MultiFileCompilationModuleGroup.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/MultiFileCompilationModuleGroup.cs index 031508b7cc5f26..4e0cc67c824a42 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/MultiFileCompilationModuleGroup.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/MultiFileCompilationModuleGroup.cs @@ -97,7 +97,7 @@ public override bool ShouldProduceFullVTable(TypeDesc type) public override bool ShouldPromoteToFullType(TypeDesc type) { - return ShouldProduceFullVTable(type); + return ShouldProduceFullVTable(type) || type.IsGenericDefinition; } public override bool PresenceOfEETypeImpliesAllMethodsOnType(TypeDesc type)