diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/AnalysisBasedMetadataManager.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/AnalysisBasedMetadataManager.cs index 92458d4d7abf..40e210d12256 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/AnalysisBasedMetadataManager.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/AnalysisBasedMetadataManager.cs @@ -23,6 +23,7 @@ public sealed class AnalysisBasedMetadataManager : GeneratingMetadataManager, IC { private readonly List _modulesWithMetadata; private readonly List _typesWithRootedCctorContext; + private readonly List _forcedTypes; private readonly Dictionary _reflectableTypes = new Dictionary(); private readonly Dictionary _reflectableMethods = new Dictionary(); @@ -32,7 +33,7 @@ public sealed class AnalysisBasedMetadataManager : GeneratingMetadataManager, IC public AnalysisBasedMetadataManager(CompilerTypeSystemContext typeSystemContext) : this(typeSystemContext, new FullyBlockedMetadataBlockingPolicy(), new FullyBlockedManifestResourceBlockingPolicy(), null, new NoStackTraceEmissionPolicy(), - new NoDynamicInvokeThunkGenerationPolicy(), Array.Empty(), + new NoDynamicInvokeThunkGenerationPolicy(), Array.Empty(), Array.Empty(), Array.Empty>(), Array.Empty>(), Array.Empty>(), Array.Empty(), Array.Empty(), default) @@ -47,6 +48,7 @@ public AnalysisBasedMetadataManager(CompilerTypeSystemContext typeSystemContext) StackTraceEmissionPolicy stackTracePolicy, DynamicInvokeThunkGenerationPolicy invokeThunkGenerationPolicy, IEnumerable modulesWithMetadata, + IEnumerable forcedTypes, IEnumerable> reflectableTypes, IEnumerable> reflectableMethods, IEnumerable> reflectableFields, @@ -57,6 +59,7 @@ public AnalysisBasedMetadataManager(CompilerTypeSystemContext typeSystemContext) { _modulesWithMetadata = new List(modulesWithMetadata); _typesWithRootedCctorContext = new List(rootedCctorContexts); + _forcedTypes = new List(forcedTypes); foreach (var refType in reflectableTypes) { @@ -180,10 +183,9 @@ void ICompilationRootProvider.AddCompilationRoots(IRootingServiceProvider rootPr const string reason = "Reflection"; - foreach (var pair in _reflectableTypes) + foreach (var type in _forcedTypes) { - if ((pair.Value & MetadataCategory.RuntimeMapping) != 0) - rootProvider.AddCompilationRoot(pair.Key, reason); + rootProvider.AddReflectionRoot(type, reason); } foreach (var pair in _reflectableMethods) diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/ReflectionMethodBodyScanner.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/ReflectionMethodBodyScanner.cs index 28b346ccbbb3..22692dc23873 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/ReflectionMethodBodyScanner.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/ReflectionMethodBodyScanner.cs @@ -341,7 +341,7 @@ public override bool HandleCall(MethodIL callingMethodBody, MethodDesc calledMet { if (systemTypeValue.RepresentedType.Type.IsEnum) { - reflectionMarker.Dependencies.Add(reflectionMarker.Factory.ConstructedTypeSymbol(systemTypeValue.RepresentedType.Type.MakeArrayType()), "Enum.GetValues"); + reflectionMarker.Dependencies.Add(reflectionMarker.Factory.ReflectedType(systemTypeValue.RepresentedType.Type.MakeArrayType()), "Enum.GetValues"); } } else @@ -381,7 +381,7 @@ public override bool HandleCall(MethodIL callingMethodBody, MethodDesc calledMet && systemTypeValue.RepresentedType.Type.GetParameterlessConstructor() is MethodDesc ctorMethod && !reflectionMarker.Factory.MetadataManager.IsReflectionBlocked(ctorMethod)) { - reflectionMarker.Dependencies.Add(reflectionMarker.Factory.ReflectableMethod(ctorMethod), "Marshal API"); + reflectionMarker.Dependencies.Add(reflectionMarker.Factory.ReflectedMethod(ctorMethod), "Marshal API"); } } } diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/CustomAttributeBasedDependencyAlgorithm.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/CustomAttributeBasedDependencyAlgorithm.cs index 3265e501a8ce..77b41a505e5d 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/CustomAttributeBasedDependencyAlgorithm.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/CustomAttributeBasedDependencyAlgorithm.cs @@ -128,8 +128,8 @@ private static void AddDependenciesDueToCustomAttributes(ref DependencyList depe // Make a new list in case we need to abort. var caDependencies = factory.MetadataManager.GetDependenciesForCustomAttribute(factory, constructor, decodedValue, parent) ?? new DependencyList(); - caDependencies.Add(factory.ReflectableMethod(constructor), "Attribute constructor"); - caDependencies.Add(factory.ConstructedTypeSymbol(constructor.OwningType), "Attribute type"); + caDependencies.Add(factory.ReflectedMethod(constructor), "Attribute constructor"); + caDependencies.Add(factory.ReflectedType(constructor.OwningType), "Attribute type"); if (AddDependenciesFromCustomAttributeBlob(caDependencies, factory, constructor.OwningType, decodedValue)) { @@ -193,7 +193,7 @@ private static bool AddDependenciesFromField(DependencyList dependencies, NodeFa if (factory.MetadataManager.IsReflectionBlocked(field)) return false; - dependencies.Add(factory.ReflectableField(field), "Custom attribute blob"); + dependencies.Add(factory.ReflectedField(field), "Custom attribute blob"); return true; } @@ -234,7 +234,7 @@ private static bool AddDependenciesFromPropertySetter(DependencyList dependencie setterMethod = factory.TypeSystemContext.GetMethodForInstantiatedType(setterMethod, (InstantiatedType)attributeType); } - dependencies.Add(factory.ReflectableMethod(setterMethod), "Custom attribute blob"); + dependencies.Add(factory.ReflectedMethod(setterMethod), "Custom attribute blob"); } return true; @@ -259,7 +259,7 @@ private static bool AddDependenciesFromCustomAttributeArgument(DependencyList de // Reflection will need to be able to allocate this type at runtime // (e.g. this could be an array that needs to be allocated, or an enum that needs to be boxed). - dependencies.Add(factory.ConstructedTypeSymbol(type), "Custom attribute blob"); + dependencies.Add(factory.ReflectedType(type), "Custom attribute blob"); if (type.UnderlyingType.IsPrimitive || type.IsString || value == null) return true; diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/FieldMetadataNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/FieldMetadataNode.cs index d00ef8946197..c9b6fdf74023 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/FieldMetadataNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/FieldMetadataNode.cs @@ -14,6 +14,9 @@ namespace ILCompiler.DependencyAnalysis { /// /// Represents a field that has metadata generated in the current compilation. + /// This corresponds to a ECMA-335 FieldDef record. It is however not a 1:1 + /// mapping because a field could be used in the AOT compiled program without generating + /// the reflection metadata for it (which would not be possible in IL terms). /// /// /// Only expected to be used during ILScanning when scanning for reflection. diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/MethodMetadataNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/MethodMetadataNode.cs index 6f5b542f4cfb..9912ab27621c 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/MethodMetadataNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/MethodMetadataNode.cs @@ -14,6 +14,9 @@ namespace ILCompiler.DependencyAnalysis { /// /// Represents a method that has metadata generated in the current compilation. + /// This corresponds to a ECMA-335 MethodDef record. It is however not a 1:1 + /// mapping because a method could be used in the AOT compiled program without generating + /// the reflection metadata for it (which would not be possible in IL terms). /// /// /// Only expected to be used during ILScanning when scanning for reflection. diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ModuleMetadataNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ModuleMetadataNode.cs index 354cf77ed8f9..cc703cde0575 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ModuleMetadataNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ModuleMetadataNode.cs @@ -41,7 +41,7 @@ public override IEnumerable GetStaticDependencies(NodeFacto && ecmaModule.EntryPoint is MethodDesc entrypoint && !factory.MetadataManager.IsReflectionBlocked(entrypoint)) { - dependencies.Add(factory.ReflectableMethod(entrypoint), "Reflectable entrypoint"); + dependencies.Add(factory.ReflectedMethod(entrypoint), "Reflectable entrypoint"); } CustomAttributeBasedDependencyAlgorithm.AddDependenciesDueToCustomAttributes(ref dependencies, factory, (EcmaAssembly)_module); 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 be70251c991d..93bb120e7909 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/NodeFactory.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/NodeFactory.cs @@ -277,14 +277,19 @@ private void CreateNodeCaches() return new TypeGVMEntriesNode(type); }); - _reflectableMethods = new NodeCache(method => + _reflectedMethods = new NodeCache(method => { - return new ReflectableMethodNode(method); + return new ReflectedMethodNode(method); }); - _reflectableFields = new NodeCache(field => + _reflectedFields = new NodeCache(field => { - return new ReflectableFieldNode(field); + return new ReflectedFieldNode(field); + }); + + _reflectedTypes = new NodeCache(type => + { + return new ReflectedTypeNode(type); }); _genericStaticBaseInfos = new NodeCache(type => @@ -881,16 +886,22 @@ internal TypeGVMEntriesNode TypeGVMEntries(TypeDesc type) return _gvmTableEntries.GetOrAdd(type); } - private NodeCache _reflectableMethods; - public ReflectableMethodNode ReflectableMethod(MethodDesc method) + private NodeCache _reflectedMethods; + public ReflectedMethodNode ReflectedMethod(MethodDesc method) + { + return _reflectedMethods.GetOrAdd(method); + } + + private NodeCache _reflectedFields; + public ReflectedFieldNode ReflectedField(FieldDesc field) { - return _reflectableMethods.GetOrAdd(method); + return _reflectedFields.GetOrAdd(field); } - private NodeCache _reflectableFields; - public ReflectableFieldNode ReflectableField(FieldDesc field) + private NodeCache _reflectedTypes; + public ReflectedTypeNode ReflectedType(TypeDesc type) { - return _reflectableFields.GetOrAdd(field); + return _reflectedTypes.GetOrAdd(type); } private NodeCache _genericStaticBaseInfos; diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ReflectableFieldNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ReflectedFieldNode.cs similarity index 91% rename from src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ReflectableFieldNode.cs rename to src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ReflectedFieldNode.cs index d7d7e03dc114..f4cd0c8022ef 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ReflectableFieldNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ReflectedFieldNode.cs @@ -13,12 +13,15 @@ namespace ILCompiler.DependencyAnalysis { /// /// Represents a field that is gettable/settable from reflection. + /// The field can be on a non-generic type, generic type definition, or an instantiatied type. + /// To match IL semantics, we maintain that a field on a generic type will be consistently + /// reflection-accessible. Either the field is accessible on all instantiations or on none of them. /// - public class ReflectableFieldNode : DependencyNodeCore + public class ReflectedFieldNode : DependencyNodeCore { private readonly FieldDesc _field; - public ReflectableFieldNode(FieldDesc field) + public ReflectedFieldNode(FieldDesc field) { Debug.Assert(!field.OwningType.IsCanonicalSubtype(CanonicalFormKind.Any) || field.OwningType.ConvertToCanonForm(CanonicalFormKind.Specific) == field.OwningType); @@ -46,7 +49,7 @@ public override IEnumerable GetStaticDependencies(NodeFacto // Ensure we consistently apply reflectability to all fields sharing the same definition. // Bases for different instantiations of the field have a conditional dependency on the definition node that // brings a ReflectableField of the instantiated field if it's necessary for it to be reflectable. - dependencies.Add(factory.ReflectableField(typicalField), "Definition of the reflectable field"); + dependencies.Add(factory.ReflectedField(typicalField), "Definition of the reflectable field"); } // Runtime reflection stack needs to see the type handle of the owning type diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ReflectableMethodNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ReflectedMethodNode.cs similarity index 85% rename from src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ReflectableMethodNode.cs rename to src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ReflectedMethodNode.cs index c15760007775..0a4a1ef59ed8 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ReflectableMethodNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ReflectedMethodNode.cs @@ -13,12 +13,16 @@ namespace ILCompiler.DependencyAnalysis { /// /// Represents a method that is visible to reflection. + /// The method can be on a non-generic type, generic type definition, or an instantiatied type. + /// To match IL semantics, we maintain that a method on a generic type will be consistently + /// reflection-accessible. Either the method is accessible on all instantiations or on none of them. + /// Similar invariants hold for generic methods. /// - public class ReflectableMethodNode : DependencyNodeCore + public class ReflectedMethodNode : DependencyNodeCore { private readonly MethodDesc _method; - public ReflectableMethodNode(MethodDesc method) + public ReflectedMethodNode(MethodDesc method) { Debug.Assert(!method.IsCanonicalMethod(CanonicalFormKind.Any) || method.GetCanonMethodTarget(CanonicalFormKind.Specific) == method); @@ -46,13 +50,13 @@ public override IEnumerable GetStaticDependencies(NodeFacto MethodDesc typicalMethod = _method.GetTypicalMethodDefinition(); if (typicalMethod != _method) { - dependencies.Add(factory.ReflectableMethod(typicalMethod), "Definition of the reflectable method"); + dependencies.Add(factory.ReflectedMethod(typicalMethod), "Definition of the reflectable method"); } MethodDesc canonMethod = _method.GetCanonMethodTarget(CanonicalFormKind.Specific); if (canonMethod != _method) { - dependencies.Add(factory.ReflectableMethod(canonMethod), "Canonical version of the reflectable method"); + dependencies.Add(factory.ReflectedMethod(canonMethod), "Canonical version of the reflectable method"); } // Make sure we generate the method body and other artifacts. diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ReflectedTypeNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ReflectedTypeNode.cs new file mode 100644 index 000000000000..fa37372f4c6d --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ReflectedTypeNode.cs @@ -0,0 +1,54 @@ +// 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 ILCompiler.DependencyAnalysisFramework; + +using Internal.TypeSystem; + +using Debug = System.Diagnostics.Debug; + +namespace ILCompiler.DependencyAnalysis +{ + /// + /// Represents a type that is forced to be visible from reflection. + /// The system needs to implicitly assume that any allocated type could be visible from + /// reflection due to , so presence of this node is not + /// a necessary condition for a type to be reflection visible. However, the presence of this + /// node indicates that a new reflectable type was forced into existence by e.g. dataflow + /// analysis, and is not just a byproduct of allocating an instance of this type. + /// + public class ReflectedTypeNode : DependencyNodeCore + { + private readonly TypeDesc _type; + + public ReflectedTypeNode(TypeDesc type) + { + Debug.Assert(!type.IsCanonicalSubtype(CanonicalFormKind.Any) + || type.ConvertToCanonForm(CanonicalFormKind.Specific) == type); + _type = type; + } + + public TypeDesc Type => _type; + + public override IEnumerable GetStaticDependencies(NodeFactory factory) + { + return new DependencyListEntry[] + { + new DependencyListEntry(factory.MaximallyConstructableType(_type), "Reflection target"), + }; + } + protected override string GetName(NodeFactory factory) + { + return "Reflectable type: " + _type.ToString(); + } + + public override bool InterestingForDynamicDependencyAnalysis => false; + public override bool HasDynamicDependencies => false; + public override bool HasConditionalStaticDependencies => false; + public override bool StaticDependenciesAreComputed => true; + public override IEnumerable GetConditionalStaticDependencies(NodeFactory factory) => null; + public override IEnumerable SearchDynamicDependencies(List> markedNodes, int firstNode, NodeFactory factory) => null; + } +} 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 db8c11315b4b..de162987d508 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/TypeMetadataNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/TypeMetadataNode.cs @@ -14,6 +14,9 @@ namespace ILCompiler.DependencyAnalysis { /// /// Represents a type that has metadata generated in the current compilation. + /// This node corresponds to an ECMA-335 TypeDef record. It is however not a 1:1 + /// mapping because IL could be compiled into machine code without generating a record + /// in the reflection metadata (which would not be possible in IL terms). /// /// /// Only expected to be used during ILScanning when scanning for reflection. @@ -48,7 +51,7 @@ public override IEnumerable GetStaticDependencies(NodeFacto { // A lot of the enum reflection actually happens on top of the respective MethodTable (e.g. getting the underlying type), // so for enums also include their MethodTable. - dependencies.Add(factory.MaximallyConstructableType(_type), "Reflectable enum"); + dependencies.Add(factory.ReflectedType(_type), "Reflectable enum"); // Enums are not useful without their literal fields. The literal fields are not referenced // from anywhere (source code reference to enums compiles to the underlying numerical constants in IL). diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DescriptorMarker.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DescriptorMarker.cs index 1552bbf956e9..4423e1bd23bc 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DescriptorMarker.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DescriptorMarker.cs @@ -213,7 +213,7 @@ protected override void ProcessField(TypeDesc type, FieldDesc field, XPathNaviga // LogWarning(nav, DiagnosticId.XmlDuplicatePreserveMember, field.FullName); }*/ - _dependencies.Add(_factory.ReflectableField(field), "field kept due to descriptor"); + _dependencies.Add(_factory.ReflectedField(field), "field kept due to descriptor"); } protected override void ProcessMethod(TypeDesc type, MethodDesc method, XPathNavigator nav, object? customData) @@ -228,11 +228,11 @@ protected override void ProcessMethod(TypeDesc type, MethodDesc method, XPathNav if (customData is bool required && !required) { //TODO: Add a conditional dependency if the type is used also mark the method - _dependencies.Add(_factory.ReflectableMethod(method), "method kept due to descriptor"); + _dependencies.Add(_factory.ReflectedMethod(method), "method kept due to descriptor"); } else { - _dependencies.Add(_factory.ReflectableMethod(method), "method kept due to descriptor"); + _dependencies.Add(_factory.ReflectedMethod(method), "method kept due to descriptor"); } } diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ILScanner.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ILScanner.cs index b644ba6a6d1b..c15d56635cda 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ILScanner.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ILScanner.cs @@ -401,6 +401,8 @@ public ScannedDevirtualizationManager(ImmutableArray obj) return; } - var reflectableMethodNode = obj as ReflectableMethodNode; - if (reflectableMethodNode != null) + var reflectedMethodNode = obj as ReflectedMethodNode; + if (reflectedMethodNode != null) { - _reflectableMethods.Add(reflectableMethodNode.Method); + _reflectableMethods.Add(reflectedMethodNode.Method); } var nonGcStaticSectionNode = obj as NonGCStaticsNode; diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/RootingHelpers.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/RootingHelpers.cs index cfe769b60e97..61b43a2195b7 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/RootingHelpers.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/RootingHelpers.cs @@ -26,7 +26,7 @@ public static bool TryRootType(IRootingServiceProvider rootProvider, TypeDesc ty public static void RootType(IRootingServiceProvider rootProvider, TypeDesc type, string reason) { - rootProvider.AddCompilationRoot(type, reason); + rootProvider.AddReflectionRoot(type, reason); InstantiatedType fallbackNonCanonicalOwningType = null; @@ -43,7 +43,7 @@ public static void RootType(IRootingServiceProvider rootProvider, TypeDesc type, type = ((MetadataType)type).MakeInstantiatedType(canonInst); - rootProvider.AddCompilationRoot(type, reason); + rootProvider.AddReflectionRoot(type, reason); } // Also root base types. This is so that we make methods on the base types callable. @@ -162,7 +162,7 @@ public static bool TryGetDependenciesForReflectedMethod(ref DependencyList depen if (typicalMethod.IsGenericMethodDefinition || typicalMethod.OwningType.IsGenericDefinition) { dependencies ??= new DependencyList(); - dependencies.Add(factory.ReflectableMethod(typicalMethod), reason); + dependencies.Add(factory.ReflectedMethod(typicalMethod), reason); } // If there's any genericness involved, try to create a fitting instantiation that would be usable at runtime. @@ -207,7 +207,7 @@ public static bool TryGetDependenciesForReflectedMethod(ref DependencyList depen } dependencies ??= new DependencyList(); - dependencies.Add(factory.ReflectableMethod(method), reason); + dependencies.Add(factory.ReflectedMethod(method), reason); return true; } @@ -227,7 +227,7 @@ public static bool TryGetDependenciesForReflectedField(ref DependencyList depend // for it below. if (typicalField.OwningType.HasInstantiation) { - dependencies.Add(factory.ReflectableField(typicalField), reason); + dependencies.Add(factory.ReflectedField(typicalField), reason); } // If there's any genericness involved, try to create a fitting instantiation that would be usable at runtime. @@ -248,7 +248,7 @@ public static bool TryGetDependenciesForReflectedField(ref DependencyList depend ((MetadataType)owningType).MakeInstantiatedType(inst)); } - dependencies.Add(factory.ReflectableField(field), reason); + dependencies.Add(factory.ReflectedField(field), reason); return true; } @@ -270,7 +270,7 @@ public static bool TryGetDependenciesForReflectedType(ref DependencyList depende dependencies ??= new DependencyList(); - dependencies.Add(factory.MaximallyConstructableType(type), reason); + dependencies.Add(factory.ReflectedType(type), reason); // If there's any unknown genericness involved, try to create a fitting instantiation that would be usable at runtime. // This is not a complete solution to the problem. @@ -281,7 +281,7 @@ public static bool TryGetDependenciesForReflectedType(ref DependencyList depende Instantiation inst = TypeExtensions.GetInstantiationThatMeetsConstraints(type.Instantiation, allowCanon: true); if (!inst.IsNull) { - dependencies.Add(factory.MaximallyConstructableType(((MetadataType)type).MakeInstantiatedType(inst)), reason); + dependencies.Add(factory.ReflectedType(((MetadataType)type).MakeInstantiatedType(inst)), reason); } } } diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/RootingServiceProvider.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/RootingServiceProvider.cs index 4beb7bf6204b..443c463d8949 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/RootingServiceProvider.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/RootingServiceProvider.cs @@ -41,16 +41,21 @@ public void AddCompilationRoot(TypeDesc type, string reason) _rootAdder(_factory.MaximallyConstructableType(type), reason); } + public void AddReflectionRoot(TypeDesc type, string reason) + { + _rootAdder(_factory.ReflectedType(type), reason); + } + public void AddReflectionRoot(MethodDesc method, string reason) { if (!_factory.MetadataManager.IsReflectionBlocked(method)) - _rootAdder(_factory.ReflectableMethod(method), reason); + _rootAdder(_factory.ReflectedMethod(method), reason); } public void AddReflectionRoot(FieldDesc field, string reason) { if (!_factory.MetadataManager.IsReflectionBlocked(field)) - _rootAdder(_factory.ReflectableField(field), reason); + _rootAdder(_factory.ReflectedField(field), reason); } public void AddCompilationRoot(object o, string reason) diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/UsageBasedMetadataManager.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/UsageBasedMetadataManager.cs index fad631ad5f7b..5b245606213f 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/UsageBasedMetadataManager.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/UsageBasedMetadataManager.cs @@ -48,6 +48,7 @@ public sealed class UsageBasedMetadataManager : GeneratingMetadataManager (DiagnosticUtilities.RequiresAssemblyFilesAttribute, DiagnosticId.RequiresAssemblyFilesAttributeMismatch) }; + private readonly List _typesWithForcedEEType = new List(); private readonly List _modulesWithMetadata = new List(); private readonly List _fieldsWithMetadata = new List(); private readonly List _methodsWithMetadata = new List(); @@ -132,10 +133,10 @@ protected override void Graph_NewMarkedNode(DependencyNodeCore obj) _customAttributesWithMetadata.Add(customAttributeMetadataNode.CustomAttribute); } - var reflectableFieldNode = obj as ReflectableFieldNode; - if (reflectableFieldNode != null) + var reflectedFieldNode = obj as ReflectedFieldNode; + if (reflectedFieldNode != null) { - FieldDesc field = reflectableFieldNode.Field; + FieldDesc field = reflectedFieldNode.Field; TypeDesc fieldOwningType = field.OwningType; // Filter out to those that make sense to have in the mapping tables @@ -147,6 +148,11 @@ protected override void Graph_NewMarkedNode(DependencyNodeCore obj) _fieldsWithRuntimeMapping.Add(field); } } + + if (obj is ReflectedTypeNode reflectableType) + { + _typesWithForcedEEType.Add(reflectableType.Type); + } } protected override MetadataCategory GetMetadataCategory(FieldDesc field) @@ -266,7 +272,7 @@ protected override void GetMetadataDependenciesDueToReflectability(ref Dependenc if (!IsReflectionBlocked(invokeMethod)) { dependencies ??= new DependencyList(); - dependencies.Add(factory.ReflectableMethod(invokeMethod), "Delegate invoke method is always reflectable"); + dependencies.Add(factory.ReflectedMethod(invokeMethod), "Delegate invoke method is always reflectable"); } } @@ -480,8 +486,8 @@ public override void GetConditionalDependenciesDueToEETypePresence(ref CombinedD dependencies ??= new CombinedDependencyList(); dependencies.Add(new DependencyNodeCore.CombinedDependencyListEntry( - factory.ReflectableField(field), - factory.ReflectableField(field.GetTypicalFieldDefinition()), + factory.ReflectedField(field), + factory.ReflectedField(field.GetTypicalFieldDefinition()), "Fields have same reflectability")); } @@ -497,8 +503,8 @@ public override void GetConditionalDependenciesDueToEETypePresence(ref CombinedD dependencies ??= new CombinedDependencyList(); dependencies.Add(new DependencyNodeCore.CombinedDependencyListEntry( - factory.ReflectableMethod(method), - factory.ReflectableMethod(method.GetTypicalMethodDefinition()), + factory.ReflectedMethod(method), + factory.ReflectedMethod(method.GetTypicalMethodDefinition()), "Methods have same reflectability")); } } @@ -509,7 +515,7 @@ public override void GetDependenciesDueToLdToken(ref DependencyList dependencies if (!IsReflectionBlocked(field)) { dependencies ??= new DependencyList(); - dependencies.Add(factory.ReflectableField(field), "LDTOKEN field"); + dependencies.Add(factory.ReflectedField(field), "LDTOKEN field"); } } @@ -518,7 +524,7 @@ public override void GetDependenciesDueToLdToken(ref DependencyList dependencies dependencies ??= new DependencyList(); if (!IsReflectionBlocked(method)) - dependencies.Add(factory.ReflectableMethod(method), "LDTOKEN method"); + dependencies.Add(factory.ReflectedMethod(method), "LDTOKEN method"); } public override void GetDependenciesDueToDelegateCreation(ref DependencyList dependencies, NodeFactory factory, MethodDesc target) @@ -526,7 +532,7 @@ public override void GetDependenciesDueToDelegateCreation(ref DependencyList dep if (!IsReflectionBlocked(target)) { dependencies ??= new DependencyList(); - dependencies.Add(factory.ReflectableMethod(target), "Target of a delegate"); + dependencies.Add(factory.ReflectedMethod(target), "Target of a delegate"); } } @@ -549,14 +555,14 @@ public override void GetDependenciesForOverridingMethod(ref CombinedDependencyLi // typeof(Derived2).GetMethods(...) // // In the above case, we don't really need Derived1.Boo to become reflection visible - // but the below code will do that because ReflectableMethodNode tracks all reflectable methods, + // but the below code will do that because ReflectedMethodNode tracks all reflectable methods, // without keeping information about subtleities like "reflectable delegate". if (!IsReflectionBlocked(decl) && !IsReflectionBlocked(impl)) { dependencies ??= new CombinedDependencyList(); dependencies.Add(new DependencyNodeCore.CombinedDependencyListEntry( - factory.ReflectableMethod(impl.GetCanonMethodTarget(CanonicalFormKind.Specific)), - factory.ReflectableMethod(decl.GetCanonMethodTarget(CanonicalFormKind.Specific)), + factory.ReflectedMethod(impl.GetCanonMethodTarget(CanonicalFormKind.Specific)), + factory.ReflectedMethod(decl.GetCanonMethodTarget(CanonicalFormKind.Specific)), "Virtual method declaration is reflectable")); } } @@ -620,7 +626,7 @@ public override void GetConditionalDependenciesDueToMethodGenericDictionary(ref dependencies ??= new CombinedDependencyList(); dependencies.Add(new DependencyNodeCore.CombinedDependencyListEntry( - factory.ReflectableMethod(method), factory.ReflectableMethod(typicalMethod), "Reflectability of methods is same across genericness")); + factory.ReflectedMethod(method), factory.ReflectedMethod(typicalMethod), "Reflectability of methods is same across genericness")); } } @@ -634,7 +640,7 @@ public override void GetConditionalDependenciesDueToMethodCodePresence(ref Combi { dependencies ??= new CombinedDependencyList(); dependencies.Add(new DependencyNodeCore.CombinedDependencyListEntry( - factory.ReflectableMethod(method), factory.ReflectableMethod(typicalMethod), "Reflectability of methods is same across genericness")); + factory.ReflectedMethod(method), factory.ReflectedMethod(typicalMethod), "Reflectability of methods is same across genericness")); } } @@ -648,7 +654,7 @@ public override void GetDependenciesDueToVirtualMethodReflectability(ref Depende if (method.IsAbstract && GetMetadataCategory(method) != 0) { dependencies ??= new DependencyList(); - dependencies.Add(factory.ReflectableMethod(method), "Abstract reflectable method"); + dependencies.Add(factory.ReflectedMethod(method), "Abstract reflectable method"); } } } @@ -741,7 +747,7 @@ public override void GetDependenciesDueToAccess(ref DependencyList dependencies, } dependencies ??= new DependencyList(); - dependencies.Add(factory.ReflectableField(fieldToReport), reason); + dependencies.Add(factory.ReflectedField(fieldToReport), reason); } if (writtenField.GetTypicalFieldDefinition() is EcmaField ecmaField) @@ -971,7 +977,7 @@ public MetadataManager ToAnalysisBasedMetadataManager() return new AnalysisBasedMetadataManager( _typeSystemContext, _blockingPolicy, _resourceBlockingPolicy, _metadataLogFile, _stackTraceEmissionPolicy, _dynamicInvokeThunkGenerationPolicy, - _modulesWithMetadata, reflectableTypes.ToEnumerable(), reflectableMethods.ToEnumerable(), + _modulesWithMetadata, _typesWithForcedEEType, reflectableTypes.ToEnumerable(), reflectableMethods.ToEnumerable(), reflectableFields.ToEnumerable(), _customAttributesWithMetadata, rootedCctorContexts, _options); } diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/ILCompiler.Compiler.csproj b/src/coreclr/tools/aot/ILCompiler.Compiler/ILCompiler.Compiler.csproj index 5dd83b8178a8..8a2a8083e426 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/ILCompiler.Compiler.csproj +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/ILCompiler.Compiler.csproj @@ -397,7 +397,8 @@ - + + @@ -441,7 +442,7 @@ - + diff --git a/src/coreclr/tools/aot/ILCompiler/Program.cs b/src/coreclr/tools/aot/ILCompiler/Program.cs index 61bdaf1fb240..140d76b951ec 100644 --- a/src/coreclr/tools/aot/ILCompiler/Program.cs +++ b/src/coreclr/tools/aot/ILCompiler/Program.cs @@ -254,7 +254,7 @@ public int Run() // same as conditionally rooted ones and here we're fulfilling the condition ("something is used"). compilationRoots.Add( new GenericRootProvider(module, - (ModuleDesc module, IRootingServiceProvider rooter) => rooter.AddCompilationRoot(module.GetGlobalModuleType(), "Command line root"))); + (ModuleDesc module, IRootingServiceProvider rooter) => rooter.AddReflectionRoot(module.GetGlobalModuleType(), "Command line root"))); } // diff --git a/src/tests/nativeaot/SmokeTests/TrimmingBehaviors/DeadCodeElimination.cs b/src/tests/nativeaot/SmokeTests/TrimmingBehaviors/DeadCodeElimination.cs index bc44bf552ad8..9abbe3a93ac5 100644 --- a/src/tests/nativeaot/SmokeTests/TrimmingBehaviors/DeadCodeElimination.cs +++ b/src/tests/nativeaot/SmokeTests/TrimmingBehaviors/DeadCodeElimination.cs @@ -20,6 +20,7 @@ public static int Run() TestArrayElementTypeOperations.Run(); TestStaticVirtualMethodOptimizations.Run(); TestTypeEquals.Run(); + TestBranchesInGenericCodeRemoval.Run(); return 100; } @@ -34,7 +35,7 @@ public static void Run() { typeof(PresentType).ToString(); - if (!IsTypePresent(typeof(SanityTest), nameof(PresentType))) + if (GetTypeSecretly(typeof(SanityTest), nameof(PresentType)) == null) throw new Exception(); ThrowIfPresent(typeof(SanityTest), nameof(NotPresentType)); @@ -330,21 +331,80 @@ public static void Run() } } + class TestBranchesInGenericCodeRemoval + { + struct Unused { public byte Val; } + struct Used { public byte Val; } + + [MethodImpl(MethodImplOptions.NoInlining)] + static T Cast(byte o) + { + if (typeof(T) == typeof(Unused)) + { + Unused result = new Unused { Val = o }; + return (T)(object)result; + } + else if (typeof(T) == typeof(Used)) + { + Used result = new Used { Val = o }; + return (T)(object)result; + } + return default; + } + + public static void Run() + { + Console.WriteLine("Testing dead branches guarded by typeof in generic code removal"); + + Cast(12); + + // We only expect to be able to get rid of it when optimizing +#if !DEBUG + ThrowIfPresentWithTypeHandle(typeof(TestBranchesInGenericCodeRemoval), nameof(Unused)); +#endif + ThrowIfNotPresent(typeof(TestBranchesInGenericCodeRemoval), nameof(Used)); + } + } + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2070:UnrecognizedReflectionPattern", Justification = "That's the point")] - private static bool IsTypePresent(Type testType, string typeName) => testType.GetNestedType(typeName, BindingFlags.NonPublic | BindingFlags.Public) != null; + private static Type GetTypeSecretly(Type testType, string typeName) => testType.GetNestedType(typeName, BindingFlags.NonPublic | BindingFlags.Public); private static void ThrowIfPresent(Type testType, string typeName) { - if (IsTypePresent(testType, typeName)) + if (GetTypeSecretly(testType, typeName) != null) { throw new Exception(typeName); } } + private static void ThrowIfPresentWithTypeHandle(Type testType, string typeName) + { + Type t = GetTypeSecretly(testType, typeName); + if (t == null) + { + throw new Exception("Not found " + typeName); + } + + bool thrown = false; + try + { + _ = t.TypeHandle; + } + catch (NotSupportedException) + { + thrown = true; + } + + if (!thrown) + { + throw new Exception(typeName + " has type handle"); + } + } + private static void ThrowIfNotPresent(Type testType, string typeName) { - if (!IsTypePresent(testType, typeName)) + if (GetTypeSecretly(testType, typeName) == null) { throw new Exception(typeName); }