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 61520b4bfadaf..cd1ef49a22147 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/EETypeNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/EETypeNode.cs @@ -625,7 +625,8 @@ protected override DependencyList ComputeNonRelocationBasedDependencies(NodeFact // Ask the metadata manager // if we have any dependencies due to presence of the EEType. - factory.MetadataManager.GetDependenciesDueToEETypePresence(ref dependencies, factory, _type); + bool isFullType = factory.MaximallyConstructableType(_type) == this; + factory.MetadataManager.GetDependenciesDueToEETypePresence(ref dependencies, factory, _type, isFullType); if (_type is MetadataType mdType) ModuleUseBasedDependencyAlgorithm.AddDependenciesDueToModuleUse(ref dependencies, factory, mdType.Module); 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 b42d93273e468..2bf7672884de0 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/GenericDefinitionEETypeNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/GenericDefinitionEETypeNode.cs @@ -25,7 +25,7 @@ protected override DependencyList ComputeNonRelocationBasedDependencies(NodeFact 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); + factory.MetadataManager.GetDependenciesDueToEETypePresence(ref dependencyList, factory, _type, isFullType: true); return dependencyList; } 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 fbb6c08e04006..5bc64c02d2886 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/NodeFactory.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/NodeFactory.cs @@ -458,7 +458,12 @@ private void CreateNodeCaches() _typesWithMetadata = new NodeCache(type => { - return new TypeMetadataNode(type); + return new TypeMetadataNode(type, includeCustomAttributes: true); + }); + + _typesWithMetadataWithoutCustomAttributes = new NodeCache(type => + { + return new TypeMetadataNode(type, includeCustomAttributes: false); }); _methodsWithMetadata = new NodeCache(method => @@ -1156,6 +1161,16 @@ internal TypeMetadataNode TypeMetadata(MetadataType type) return _typesWithMetadata.GetOrAdd(type); } + private NodeCache _typesWithMetadataWithoutCustomAttributes; + + internal TypeMetadataNode TypeMetadataWithoutCustomAttributes(MetadataType type) + { + // These are only meaningful for UsageBasedMetadataManager. We should not have them + // in the dependency graph otherwise. + Debug.Assert(MetadataManager is UsageBasedMetadataManager); + return _typesWithMetadataWithoutCustomAttributes.GetOrAdd(type); + } + private NodeCache _methodsWithMetadata; internal MethodMetadataNode MethodMetadata(MethodDesc method) diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/TypeMetadataNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/TypeMetadataNode.cs index de162987d508b..8979feb1061ca 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/TypeMetadataNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/TypeMetadataNode.cs @@ -24,11 +24,13 @@ namespace ILCompiler.DependencyAnalysis internal sealed class TypeMetadataNode : DependencyNodeCore { private readonly MetadataType _type; + private readonly bool _includeCustomAttributes; - public TypeMetadataNode(MetadataType type) + public TypeMetadataNode(MetadataType type, bool includeCustomAttributes) { Debug.Assert(type.IsTypeDefinition); _type = type; + _includeCustomAttributes = includeCustomAttributes; } public MetadataType Type => _type; @@ -37,13 +39,21 @@ public override IEnumerable GetStaticDependencies(NodeFacto { DependencyList dependencies = new DependencyList(); - CustomAttributeBasedDependencyAlgorithm.AddDependenciesDueToCustomAttributes(ref dependencies, factory, ((EcmaType)_type)); + if (_includeCustomAttributes) + CustomAttributeBasedDependencyAlgorithm.AddDependenciesDueToCustomAttributes(ref dependencies, factory, ((EcmaType)_type)); DefType containingType = _type.ContainingType; if (containingType != null) - dependencies.Add(factory.TypeMetadata((MetadataType)containingType), "Containing type of a reflectable type"); + { + TypeMetadataNode metadataNode = _includeCustomAttributes + ? factory.TypeMetadata((MetadataType)containingType) + : factory.TypeMetadataWithoutCustomAttributes((MetadataType)containingType); + dependencies.Add(metadataNode, "Containing type of a reflectable type"); + } else + { dependencies.Add(factory.ModuleMetadata(_type.Module), "Containing module of a reflectable type"); + } var mdManager = (UsageBasedMetadataManager)factory.MetadataManager; @@ -100,7 +110,7 @@ public override IEnumerable GetStaticDependencies(NodeFacto /// Decomposes a constructed type into individual units that will be needed to /// express the constructed type in metadata. /// - public static void GetMetadataDependencies(ref DependencyList dependencies, NodeFactory nodeFactory, TypeDesc type, string reason) + public static void GetMetadataDependencies(ref DependencyList dependencies, NodeFactory nodeFactory, TypeDesc type, string reason, bool isFullType = true) { MetadataManager mdManager = nodeFactory.MetadataManager; @@ -110,13 +120,13 @@ public static void GetMetadataDependencies(ref DependencyList dependencies, Node case TypeFlags.SzArray: case TypeFlags.ByRef: case TypeFlags.Pointer: - GetMetadataDependencies(ref dependencies, nodeFactory, ((ParameterizedType)type).ParameterType, reason); + GetMetadataDependencies(ref dependencies, nodeFactory, ((ParameterizedType)type).ParameterType, reason, isFullType); break; case TypeFlags.FunctionPointer: var pointerType = (FunctionPointerType)type; - GetMetadataDependencies(ref dependencies, nodeFactory, pointerType.Signature.ReturnType, reason); + GetMetadataDependencies(ref dependencies, nodeFactory, pointerType.Signature.ReturnType, reason, isFullType); foreach (TypeDesc paramType in pointerType.Signature) - GetMetadataDependencies(ref dependencies, nodeFactory, paramType, reason); + GetMetadataDependencies(ref dependencies, nodeFactory, paramType, reason, isFullType); break; case TypeFlags.SignatureMethodVariable: @@ -126,27 +136,22 @@ public static void GetMetadataDependencies(ref DependencyList dependencies, Node default: Debug.Assert(type.IsDefType); - TypeDesc typeDefinition = type.GetTypeDefinition(); + var typeDefinition = (MetadataType)type.GetTypeDefinition(); if (typeDefinition != type) { - if (mdManager.CanGenerateMetadata((MetadataType)typeDefinition)) - { - dependencies ??= new DependencyList(); - dependencies.Add(nodeFactory.TypeMetadata((MetadataType)typeDefinition), reason); - } - foreach (TypeDesc typeArg in type.Instantiation) { - GetMetadataDependencies(ref dependencies, nodeFactory, typeArg, reason); + GetMetadataDependencies(ref dependencies, nodeFactory, typeArg, reason, isFullType); } } - else + + if (mdManager.CanGenerateMetadata(typeDefinition)) { - if (mdManager.CanGenerateMetadata((MetadataType)type)) - { - dependencies ??= new DependencyList(); - dependencies.Add(nodeFactory.TypeMetadata((MetadataType)type), reason); - } + dependencies ??= new DependencyList(); + TypeMetadataNode node = isFullType + ? nodeFactory.TypeMetadata(typeDefinition) + : nodeFactory.TypeMetadataWithoutCustomAttributes(typeDefinition); + dependencies.Add(node, reason); } break; } @@ -154,7 +159,7 @@ public static void GetMetadataDependencies(ref DependencyList dependencies, Node protected override string GetName(NodeFactory factory) { - return "Reflectable type: " + _type.ToString(); + return $"Reflectable type: {_type}{(!_includeCustomAttributes ? " (No custom attributes)" : "")}"; } protected override void OnMarked(NodeFactory factory) diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/MetadataManager.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/MetadataManager.cs index 8658068c1ebe4..b62304f362c16 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/MetadataManager.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/MetadataManager.cs @@ -478,13 +478,13 @@ protected virtual void GetMetadataDependenciesDueToReflectability(ref Dependency /// /// This method is an extension point that can provide additional metadata-based dependencies to generated EETypes. /// - public virtual void GetDependenciesDueToEETypePresence(ref DependencyList dependencies, NodeFactory factory, TypeDesc type) + public virtual void GetDependenciesDueToEETypePresence(ref DependencyList dependencies, NodeFactory factory, TypeDesc type, bool isFullType) { MetadataCategory category = GetMetadataCategory(type); if ((category & MetadataCategory.Description) != 0) { - GetMetadataDependenciesDueToReflectability(ref dependencies, factory, type); + GetMetadataDependenciesDueToReflectability(ref dependencies, factory, type, isFullType); } } @@ -493,7 +493,7 @@ internal virtual void GetDependenciesDueToModuleUse(ref DependencyList dependenc // MetadataManagers can override this to provide additional dependencies caused by using a module } - protected virtual void GetMetadataDependenciesDueToReflectability(ref DependencyList dependencies, NodeFactory factory, TypeDesc type) + protected virtual void GetMetadataDependenciesDueToReflectability(ref DependencyList dependencies, NodeFactory factory, TypeDesc type, bool isFullType) { // MetadataManagers can override this to provide additional dependencies caused by the emission of metadata // (E.g. dependencies caused by the type having custom attributes applied to it: making sure we compile the attribute constructor diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/UsageBasedMetadataManager.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/UsageBasedMetadataManager.cs index e680bf80f2dfa..fc0249f76847e 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/UsageBasedMetadataManager.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/UsageBasedMetadataManager.cs @@ -271,9 +271,9 @@ internal override void GetDependenciesDueToModuleUse(ref DependencyList dependen } } - protected override void GetMetadataDependenciesDueToReflectability(ref DependencyList dependencies, NodeFactory factory, TypeDesc type) + protected override void GetMetadataDependenciesDueToReflectability(ref DependencyList dependencies, NodeFactory factory, TypeDesc type, bool isFullType) { - TypeMetadataNode.GetMetadataDependencies(ref dependencies, factory, type, "Reflectable type"); + TypeMetadataNode.GetMetadataDependencies(ref dependencies, factory, type, "Reflectable type", isFullType); if (type.IsDelegate) { @@ -385,9 +385,9 @@ private static bool IsTrimmableAssembly(ModuleDesc assembly) return false; } - public override void GetDependenciesDueToEETypePresence(ref DependencyList dependencies, NodeFactory factory, TypeDesc type) + public override void GetDependenciesDueToEETypePresence(ref DependencyList dependencies, NodeFactory factory, TypeDesc type, bool isFullType) { - base.GetDependenciesDueToEETypePresence(ref dependencies, factory, type); + base.GetDependenciesDueToEETypePresence(ref dependencies, factory, type, isFullType); DataflowAnalyzedTypeDefinitionNode.GetDependencies(ref dependencies, factory, FlowAnnotations, type); } @@ -970,7 +970,7 @@ public bool GeneratesMetadata(MethodDesc methodDef) public bool GeneratesMetadata(MetadataType typeDef) { - return _factory.TypeMetadata(typeDef).Marked; + return _factory.TypeMetadata(typeDef).Marked || _factory.TypeMetadataWithoutCustomAttributes(typeDef).Marked; } public bool GeneratesMetadata(EcmaModule module, CustomAttributeHandle caHandle) diff --git a/src/tests/nativeaot/SmokeTests/TrimmingBehaviors/DeadCodeElimination.cs b/src/tests/nativeaot/SmokeTests/TrimmingBehaviors/DeadCodeElimination.cs index 152c851455666..e030b3a1de99c 100644 --- a/src/tests/nativeaot/SmokeTests/TrimmingBehaviors/DeadCodeElimination.cs +++ b/src/tests/nativeaot/SmokeTests/TrimmingBehaviors/DeadCodeElimination.cs @@ -21,6 +21,7 @@ public static int Run() TestStaticVirtualMethodOptimizations.Run(); TestTypeEquals.Run(); TestBranchesInGenericCodeRemoval.Run(); + TestLimitedMetadataBlobs.Run(); return 100; } @@ -378,6 +379,51 @@ public static void Run() } } + class TestLimitedMetadataBlobs + { + class MyAttribute : Attribute + { + public MyAttribute([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] Type t) { } + } + + class ShouldNotBeNeeded + { + } + + [My(typeof(ShouldNotBeNeeded))] + interface INeverImplemented + { + void Never(); + } + + static INeverImplemented s_instance; +#if !DEBUG + static Type s_type; +#endif + + internal static void Run() + { + Console.WriteLine("Testing generation of limited metadata blobs"); + + // Force a reference to the interface from a dispatch cell + if (s_instance != null) + s_instance.Never(); + + // Following is for release only since it relies on optimizing the typeof into an unconstructed + // MethodTable. +#if !DEBUG + // Force another reference from an LDTOKEN + if (s_type == typeof(INeverImplemented)) + s_type = typeof(object); +#endif + + ThrowIfPresent(typeof(TestLimitedMetadataBlobs), nameof(ShouldNotBeNeeded)); + ThrowIfPresent(typeof(TestLimitedMetadataBlobs), nameof(MyAttribute)); + ThrowIfNotPresent(typeof(TestLimitedMetadataBlobs), nameof(INeverImplemented)); + } + } + + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2070:UnrecognizedReflectionPattern", Justification = "That's the point")] private static Type GetTypeSecretly(Type testType, string typeName) => testType.GetNestedType(typeName, BindingFlags.NonPublic | BindingFlags.Public);