Skip to content

Commit d00e383

Browse files
Trim custom attributes (#118640)
1. To be able to read custom attributes at runtime, we need to use the internal metadata APIs 2. When the internal metadata API is not used, we know nobody could be reading the attributes 3. Make custom attribute emission conditional on the presence of the reading API in the graph 4. Use separate conditions for different kinds of attributes - fields, types, methods, etc. In practice, this doesn't help much outside of Hello World scenarios because custom attribute reading at runtime happens through a virtual method on `MemberInfo`. So if e.g. `PropertyInfo` is allocated and somebody calls `Type.GetCustomAttributes` (which is `MemberInfo.GetCustomAttributes` virtual), we include code to read attributes on properties too. This is a solvable problem if we learn to do devirtualization during scanning phase; then we can reap more benefit from this.
1 parent 13ff5d0 commit d00e383

File tree

10 files changed

+83
-37
lines changed

10 files changed

+83
-37
lines changed

src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/CustomAttributeBasedDependencyAlgorithm.cs

Lines changed: 37 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@
1010
using Internal.TypeSystem.Ecma;
1111

1212
using DependencyList = ILCompiler.DependencyAnalysisFramework.DependencyNodeCore<ILCompiler.DependencyAnalysis.NodeFactory>.DependencyList;
13+
using DependencyListEntry = ILCompiler.DependencyAnalysisFramework.DependencyNodeCore<ILCompiler.DependencyAnalysis.NodeFactory>.DependencyListEntry;
14+
using CombinedDependencyList = System.Collections.Generic.List<ILCompiler.DependencyAnalysisFramework.DependencyNodeCore<ILCompiler.DependencyAnalysis.NodeFactory>.CombinedDependencyListEntry>;
15+
using CombinedDependencyListEntry = ILCompiler.DependencyAnalysisFramework.DependencyNodeCore<ILCompiler.DependencyAnalysis.NodeFactory>.CombinedDependencyListEntry;
1316
using MethodAttributes = System.Reflection.MethodAttributes;
1417

1518
namespace ILCompiler.DependencyAnalysis
@@ -20,27 +23,35 @@ namespace ILCompiler.DependencyAnalysis
2023
/// </summary>
2124
internal static class CustomAttributeBasedDependencyAlgorithm
2225
{
23-
public static void AddDependenciesDueToCustomAttributes(ref DependencyList dependencies, NodeFactory factory, EcmaMethod method)
26+
private static IMethodNode GetMetadataApiDependency(NodeFactory factory, string entityName, string propertyName)
27+
=> factory.MethodEntrypoint(factory.TypeSystemContext.SystemModule.GetType("Internal.Metadata.NativeFormat", entityName).GetMethod(propertyName, null));
28+
29+
private static IMethodNode GetMetadataApiDependency(NodeFactory factory, string entityName)
30+
=> GetMetadataApiDependency(factory, entityName, "get_CustomAttributes");
31+
32+
public static void AddDependenciesDueToCustomAttributes(ref CombinedDependencyList dependencies, NodeFactory factory, EcmaMethod method)
2433
{
2534
MetadataReader reader = method.MetadataReader;
2635
MethodDefinitionHandle methodHandle = method.Handle;
2736
MethodDefinition methodDef = reader.GetMethodDefinition(methodHandle);
2837

2938
// Handle custom attributes on the method
30-
AddDependenciesDueToCustomAttributes(ref dependencies, factory, method.Module, methodDef.GetCustomAttributes(), method);
39+
AddDependenciesDueToCustomAttributes(ref dependencies, GetMetadataApiDependency(factory, "Method"), factory, method.Module, methodDef.GetCustomAttributes(), method);
3140

3241
// Handle custom attributes on method parameters
42+
object parameterCondition = GetMetadataApiDependency(factory, "Parameter");
3343
foreach (ParameterHandle parameterHandle in methodDef.GetParameters())
3444
{
3545
Parameter parameter = reader.GetParameter(parameterHandle);
36-
AddDependenciesDueToCustomAttributes(ref dependencies, factory, method.Module, parameter.GetCustomAttributes(), method);
46+
AddDependenciesDueToCustomAttributes(ref dependencies, parameterCondition, factory, method.Module, parameter.GetCustomAttributes(), method);
3747
}
3848

3949
// Handle custom attributes on generic method parameters
50+
object genericParameterCondition = GetMetadataApiDependency(factory, "GenericParameter");
4051
foreach (GenericParameterHandle genericParameterHandle in methodDef.GetGenericParameters())
4152
{
4253
GenericParameter parameter = reader.GetGenericParameter(genericParameterHandle);
43-
AddDependenciesDueToCustomAttributes(ref dependencies, factory, method.Module, parameter.GetCustomAttributes(), method);
54+
AddDependenciesDueToCustomAttributes(ref dependencies, genericParameterCondition, factory, method.Module, parameter.GetCustomAttributes(), method);
4455
}
4556

4657
// We don't model properties and events as separate entities within the compiler, so ensuring
@@ -50,6 +61,7 @@ public static void AddDependenciesDueToCustomAttributes(ref DependencyList depen
5061
// As a performance optimization, we look for associated events and properties only
5162
// if the method is SpecialName. This is required for CLS compliance and compilers we
5263
// care about emit accessors like this.
64+
object propertyCondition = GetMetadataApiDependency(factory, "Property");
5365
if ((methodDef.Attributes & MethodAttributes.SpecialName) != 0)
5466
{
5567
TypeDefinition declaringType = reader.GetTypeDefinition(methodDef.GetDeclaringType());
@@ -60,50 +72,52 @@ public static void AddDependenciesDueToCustomAttributes(ref DependencyList depen
6072
PropertyAccessors accessors = property.GetAccessors();
6173

6274
if (accessors.Getter == methodHandle || accessors.Setter == methodHandle)
63-
AddDependenciesDueToCustomAttributes(ref dependencies, factory, method.Module, property.GetCustomAttributes(), new PropertyPseudoDesc((EcmaType)method.OwningType, propertyHandle));
75+
AddDependenciesDueToCustomAttributes(ref dependencies, propertyCondition, factory, method.Module, property.GetCustomAttributes(), new PropertyPseudoDesc((EcmaType)method.OwningType, propertyHandle));
6476
}
6577

78+
object eventCondition = GetMetadataApiDependency(factory, "Event");
6679
foreach (EventDefinitionHandle eventHandle in declaringType.GetEvents())
6780
{
6881
EventDefinition @event = reader.GetEventDefinition(eventHandle);
6982
EventAccessors accessors = @event.GetAccessors();
7083

7184
if (accessors.Adder == methodHandle || accessors.Remover == methodHandle || accessors.Raiser == methodHandle)
72-
AddDependenciesDueToCustomAttributes(ref dependencies, factory, method.Module, @event.GetCustomAttributes(), new EventPseudoDesc((EcmaType)method.OwningType, eventHandle));
85+
AddDependenciesDueToCustomAttributes(ref dependencies, eventCondition, factory, method.Module, @event.GetCustomAttributes(), new EventPseudoDesc((EcmaType)method.OwningType, eventHandle));
7386
}
7487
}
7588
}
7689

77-
public static void AddDependenciesDueToCustomAttributes(ref DependencyList dependencies, NodeFactory factory, EcmaType type)
90+
public static void AddDependenciesDueToCustomAttributes(ref CombinedDependencyList dependencies, NodeFactory factory, EcmaType type)
7891
{
7992
MetadataReader reader = type.MetadataReader;
8093
TypeDefinition typeDef = reader.GetTypeDefinition(type.Handle);
81-
AddDependenciesDueToCustomAttributes(ref dependencies, factory, type.EcmaModule, typeDef.GetCustomAttributes(), type);
94+
AddDependenciesDueToCustomAttributes(ref dependencies, GetMetadataApiDependency(factory, "TypeDefinition"), factory, type.EcmaModule, typeDef.GetCustomAttributes(), type);
8295

8396
// Handle custom attributes on generic type parameters
97+
object genericParameterCondition = GetMetadataApiDependency(factory, "GenericParameter");
8498
foreach (GenericParameterHandle genericParameterHandle in typeDef.GetGenericParameters())
8599
{
86100
GenericParameter parameter = reader.GetGenericParameter(genericParameterHandle);
87-
AddDependenciesDueToCustomAttributes(ref dependencies, factory, type.EcmaModule, parameter.GetCustomAttributes(), type);
101+
AddDependenciesDueToCustomAttributes(ref dependencies, genericParameterCondition, factory, type.EcmaModule, parameter.GetCustomAttributes(), type);
88102
}
89103
}
90104

91-
public static void AddDependenciesDueToCustomAttributes(ref DependencyList dependencies, NodeFactory factory, EcmaField field)
105+
public static void AddDependenciesDueToCustomAttributes(ref CombinedDependencyList dependencies, NodeFactory factory, EcmaField field)
92106
{
93107
FieldDefinition fieldDef = field.MetadataReader.GetFieldDefinition(field.Handle);
94-
AddDependenciesDueToCustomAttributes(ref dependencies, factory, field.Module, fieldDef.GetCustomAttributes(), field);
108+
AddDependenciesDueToCustomAttributes(ref dependencies, GetMetadataApiDependency(factory, "Field"), factory, field.Module, fieldDef.GetCustomAttributes(), field);
95109
}
96110

97-
public static void AddDependenciesDueToCustomAttributes(ref DependencyList dependencies, NodeFactory factory, EcmaAssembly assembly)
111+
public static void AddDependenciesDueToCustomAttributes(ref CombinedDependencyList dependencies, NodeFactory factory, EcmaAssembly assembly)
98112
{
99113
AssemblyDefinition asmDef = assembly.MetadataReader.GetAssemblyDefinition();
100-
AddDependenciesDueToCustomAttributes(ref dependencies, factory, assembly, asmDef.GetCustomAttributes(), assembly);
114+
AddDependenciesDueToCustomAttributes(ref dependencies, GetMetadataApiDependency(factory, "ScopeDefinition"), factory, assembly, asmDef.GetCustomAttributes(), assembly);
101115

102116
ModuleDefinition moduleDef = assembly.MetadataReader.GetModuleDefinition();
103-
AddDependenciesDueToCustomAttributes(ref dependencies, factory, assembly, moduleDef.GetCustomAttributes(), assembly);
117+
AddDependenciesDueToCustomAttributes(ref dependencies, GetMetadataApiDependency(factory, "ScopeDefinition", "get_ModuleCustomAttributes"), factory, assembly, moduleDef.GetCustomAttributes(), assembly);
104118
}
105119

106-
private static void AddDependenciesDueToCustomAttributes(ref DependencyList dependencies, NodeFactory factory, EcmaModule module, CustomAttributeHandleCollection attributeHandles, TypeSystemEntity parent)
120+
private static void AddDependenciesDueToCustomAttributes(ref CombinedDependencyList dependencies, object condition, NodeFactory factory, EcmaModule module, CustomAttributeHandleCollection attributeHandles, TypeSystemEntity parent)
107121
{
108122
MetadataReader reader = module.MetadataReader;
109123
var mdManager = (UsageBasedMetadataManager)factory.MetadataManager;
@@ -134,9 +148,14 @@ private static void AddDependenciesDueToCustomAttributes(ref DependencyList depe
134148

135149
if (AddDependenciesFromCustomAttributeBlob(caDependencies, factory, constructor.OwningType, decodedValue))
136150
{
137-
dependencies ??= new DependencyList();
138-
dependencies.AddRange(caDependencies);
139-
dependencies.Add(factory.CustomAttributeMetadata(new ReflectableCustomAttribute(module, caHandle)), "Attribute metadata");
151+
dependencies ??= new CombinedDependencyList();
152+
153+
foreach (DependencyListEntry caDependency in caDependencies)
154+
{
155+
dependencies.Add(new CombinedDependencyListEntry(caDependency.Node, condition, caDependency.Reason));
156+
}
157+
158+
dependencies.Add(new CombinedDependencyListEntry(factory.CustomAttributeMetadata(new ReflectableCustomAttribute(module, caHandle)), condition, "Attribute metadata"));
140159
}
141160
}
142161
catch (TypeSystemException)

src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/FieldMetadataNode.cs

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -40,8 +40,6 @@ public override IEnumerable<DependencyListEntry> GetStaticDependencies(NodeFacto
4040
DependencyList dependencies = new DependencyList();
4141
dependencies.Add(factory.TypeMetadata((MetadataType)_field.OwningType), "Owning type metadata");
4242

43-
CustomAttributeBasedDependencyAlgorithm.AddDependenciesDueToCustomAttributes(ref dependencies, factory, ((EcmaField)_field));
44-
4543
if (_field is EcmaField ecmaField)
4644
{
4745
DynamicDependencyAttributesOnEntityNode.AddDependenciesDueToDynamicDependencyAttribute(ref dependencies, factory, ecmaField);
@@ -62,6 +60,14 @@ public override IEnumerable<DependencyListEntry> GetStaticDependencies(NodeFacto
6260

6361
return dependencies;
6462
}
63+
64+
public override IEnumerable<CombinedDependencyListEntry> GetConditionalStaticDependencies(NodeFactory factory)
65+
{
66+
var dependencies = new List<CombinedDependencyListEntry>();
67+
CustomAttributeBasedDependencyAlgorithm.AddDependenciesDueToCustomAttributes(ref dependencies, factory, (EcmaField)_field);
68+
return dependencies;
69+
}
70+
6571
protected override string GetName(NodeFactory factory)
6672
{
6773
return "Field metadata: " + _field.ToString();
@@ -75,9 +81,8 @@ protected override void OnMarked(NodeFactory factory)
7581

7682
public override bool InterestingForDynamicDependencyAnalysis => false;
7783
public override bool HasDynamicDependencies => false;
78-
public override bool HasConditionalStaticDependencies => false;
84+
public override bool HasConditionalStaticDependencies => true;
7985
public override bool StaticDependenciesAreComputed => true;
80-
public override IEnumerable<CombinedDependencyListEntry> GetConditionalStaticDependencies(NodeFactory factory) => null;
8186
public override IEnumerable<CombinedDependencyListEntry> SearchDynamicDependencies(List<DependencyNodeCore<NodeFactory>> markedNodes, int firstNode, NodeFactory factory) => null;
8287
}
8388
}

src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/MethodMetadataNode.cs

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,6 @@ public override IEnumerable<DependencyListEntry> GetStaticDependencies(NodeFacto
4444

4545
if (!_isMinimal)
4646
{
47-
CustomAttributeBasedDependencyAlgorithm.AddDependenciesDueToCustomAttributes(ref dependencies, factory, _method);
48-
4947
foreach (var parameterHandle in _method.MetadataReader.GetMethodDefinition(_method.Handle).GetParameters())
5048
{
5149
dependencies.Add(factory.MethodParameterMetadata(new ReflectableParameter(_method.Module, parameterHandle)), "Parameter is visible");
@@ -83,6 +81,14 @@ public override IEnumerable<DependencyListEntry> GetStaticDependencies(NodeFacto
8381

8482
return dependencies;
8583
}
84+
85+
public override IEnumerable<CombinedDependencyListEntry> GetConditionalStaticDependencies(NodeFactory factory)
86+
{
87+
var dependencies = new List<CombinedDependencyListEntry>();
88+
CustomAttributeBasedDependencyAlgorithm.AddDependenciesDueToCustomAttributes(ref dependencies, factory, _method);
89+
return dependencies;
90+
}
91+
8692
protected override string GetName(NodeFactory factory)
8793
{
8894
return "Method metadata: " + _method.ToString();
@@ -96,9 +102,8 @@ protected override void OnMarked(NodeFactory factory)
96102

97103
public override bool InterestingForDynamicDependencyAnalysis => false;
98104
public override bool HasDynamicDependencies => false;
99-
public override bool HasConditionalStaticDependencies => false;
105+
public override bool HasConditionalStaticDependencies => !_isMinimal;
100106
public override bool StaticDependenciesAreComputed => true;
101-
public override IEnumerable<CombinedDependencyListEntry> GetConditionalStaticDependencies(NodeFactory factory) => null;
102107
public override IEnumerable<CombinedDependencyListEntry> SearchDynamicDependencies(List<DependencyNodeCore<NodeFactory>> markedNodes, int firstNode, NodeFactory factory) => null;
103108
}
104109
}

src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ModuleMetadataNode.cs

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,6 @@ public override IEnumerable<DependencyListEntry> GetStaticDependencies(NodeFacto
4646

4747
EcmaAssembly ecmaAssembly = (EcmaAssembly)_module;
4848

49-
CustomAttributeBasedDependencyAlgorithm.AddDependenciesDueToCustomAttributes(ref dependencies, factory, ecmaAssembly);
50-
5149
foreach (EcmaModule satelliteModule in ((UsageBasedMetadataManager)factory.MetadataManager).GetSatelliteAssemblies(ecmaAssembly))
5250
{
5351
dependencies.Add(factory.ModuleMetadata(satelliteModule), "Satellite assembly");
@@ -56,16 +54,22 @@ public override IEnumerable<DependencyListEntry> GetStaticDependencies(NodeFacto
5654
return dependencies;
5755
}
5856

57+
public override IEnumerable<CombinedDependencyListEntry> GetConditionalStaticDependencies(NodeFactory factory)
58+
{
59+
var dependencies = new List<CombinedDependencyListEntry>();
60+
CustomAttributeBasedDependencyAlgorithm.AddDependenciesDueToCustomAttributes(ref dependencies, factory, (EcmaAssembly)_module);
61+
return dependencies;
62+
}
63+
5964
protected override string GetName(NodeFactory factory)
6065
{
6166
return "Reflectable module: " + ((IAssemblyDesc)_module).GetName().FullName;
6267
}
6368

6469
public override bool InterestingForDynamicDependencyAnalysis => false;
6570
public override bool HasDynamicDependencies => false;
66-
public override bool HasConditionalStaticDependencies => false;
71+
public override bool HasConditionalStaticDependencies => true;
6772
public override bool StaticDependenciesAreComputed => true;
68-
public override IEnumerable<CombinedDependencyListEntry> GetConditionalStaticDependencies(NodeFactory factory) => null;
6973
public override IEnumerable<CombinedDependencyListEntry> SearchDynamicDependencies(List<DependencyNodeCore<NodeFactory>> markedNodes, int firstNode, NodeFactory factory) => null;
7074
}
7175
}

src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/TypeMetadataNode.cs

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,6 @@ public override IEnumerable<DependencyListEntry> GetStaticDependencies(NodeFacto
3737
{
3838
DependencyList dependencies = new DependencyList();
3939

40-
CustomAttributeBasedDependencyAlgorithm.AddDependenciesDueToCustomAttributes(ref dependencies, factory, ((EcmaType)_type));
41-
4240
DefType containingType = _type.ContainingType;
4341
if (containingType != null)
4442
dependencies.Add(factory.TypeMetadata((MetadataType)containingType), "Containing type of a reflectable type");
@@ -102,6 +100,13 @@ public override IEnumerable<DependencyListEntry> GetStaticDependencies(NodeFacto
102100
return dependencies;
103101
}
104102

103+
public override IEnumerable<CombinedDependencyListEntry> GetConditionalStaticDependencies(NodeFactory factory)
104+
{
105+
var dependencies = new List<CombinedDependencyListEntry>();
106+
CustomAttributeBasedDependencyAlgorithm.AddDependenciesDueToCustomAttributes(ref dependencies, factory, ((EcmaType)_type));
107+
return dependencies;
108+
}
109+
105110
/// <summary>
106111
/// Decomposes a constructed type into individual <see cref="TypeMetadataNode"/> units that will be needed to
107112
/// express the constructed type in metadata.
@@ -183,9 +188,8 @@ protected override void OnMarked(NodeFactory factory)
183188

184189
public override bool InterestingForDynamicDependencyAnalysis => false;
185190
public override bool HasDynamicDependencies => false;
186-
public override bool HasConditionalStaticDependencies => false;
191+
public override bool HasConditionalStaticDependencies => true;
187192
public override bool StaticDependenciesAreComputed => true;
188-
public override IEnumerable<CombinedDependencyListEntry> GetConditionalStaticDependencies(NodeFactory factory) => null;
189193
public override IEnumerable<CombinedDependencyListEntry> SearchDynamicDependencies(List<DependencyNodeCore<NodeFactory>> markedNodes, int firstNode, NodeFactory factory) => null;
190194
}
191195
}

src/tools/illink/test/Mono.Linker.Tests.Cases/DataFlow/AttributeConstructorDataflow.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
using System.Reflection;
77
using Mono.Linker.Tests.Cases.DataFlow;
88
using Mono.Linker.Tests.Cases.Expectations.Assertions;
9+
using Mono.Linker.Tests.Cases.Expectations.Metadata;
910

1011
[assembly: KeptAttributeAttribute(typeof(AttributeConstructorDataflow.KeepsPublicPropertiesAttribute))]
1112
[assembly: ExpectedWarning("IL2026", "--ClassWithKeptPublicProperties--")]
@@ -15,6 +16,7 @@ namespace Mono.Linker.Tests.Cases.DataFlow
1516
{
1617
[Kept]
1718
[ExpectedNoWarnings]
19+
[SetupIlcWholeProgramAnalysis]
1820
class AttributeConstructorDataflow
1921
{
2022
[KeptAttributeAttribute(typeof(KeepsPublicConstructorAttribute))]
@@ -30,6 +32,7 @@ public static void Main()
3032
{
3133
typeof(AttributeConstructorDataflow).GetMethod("Main").GetCustomAttribute(typeof(KeepsPublicConstructorAttribute));
3234
typeof(AttributeConstructorDataflow).GetMethod("Main").GetCustomAttribute(typeof(KeepsPublicMethodsAttribute));
35+
Assembly.GetEntryAssembly().GetCustomAttributes();
3336
AllOnSelf.Test();
3437
AnnotationOnTypeArray.Test();
3538
}

0 commit comments

Comments
 (0)