diff options
author | Michal Strehovský <MichalStrehovsky@users.noreply.github.com> | 2017-12-19 15:36:45 +0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2017-12-19 15:36:45 +0300 |
commit | 5bf7b5324555956614d222367d97f3c591a37cf0 (patch) | |
tree | eb859b4b644f4333501796be2ea68741a1c9bcce | |
parent | d5ecb0cb9e85eeba829aece223556ee611f7ddf8 (diff) | |
parent | ea415ea2ce856e50c5ca61c52e8153af48c2e928 (diff) |
Merge pull request #5129 from MichalStrehovsky/metadataSplit
-rw-r--r-- | src/ILCompiler.Compiler/src/Compiler/AnalysisBasedMetadataManager.cs | 254 | ||||
-rw-r--r-- | src/ILCompiler.Compiler/src/Compiler/CompilationBuilder.cs | 2 | ||||
-rw-r--r-- | src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/UtcNodeFactory.cs | 2 | ||||
-rw-r--r-- | src/ILCompiler.Compiler/src/Compiler/EmptyMetadataManager.cs | 6 | ||||
-rw-r--r-- | src/ILCompiler.Compiler/src/Compiler/GeneratingMetadataManager.cs (renamed from src/ILCompiler.Compiler/src/Compiler/CompilerGeneratedMetadataManager.cs) | 156 | ||||
-rw-r--r-- | src/ILCompiler.Compiler/src/Compiler/ILScannerBuilder.cs | 12 | ||||
-rw-r--r-- | src/ILCompiler.Compiler/src/Compiler/MetadataManager.cs | 31 | ||||
-rw-r--r-- | src/ILCompiler.Compiler/src/Compiler/PrecomputedMetadataManager.cs | 4 | ||||
-rw-r--r-- | src/ILCompiler.Compiler/src/Compiler/UsageBasedMetadataManager.cs | 325 | ||||
-rw-r--r-- | src/ILCompiler.Compiler/src/ILCompiler.Compiler.csproj | 4 | ||||
-rw-r--r-- | src/ILCompiler/src/Program.cs | 27 |
11 files changed, 651 insertions, 172 deletions
diff --git a/src/ILCompiler.Compiler/src/Compiler/AnalysisBasedMetadataManager.cs b/src/ILCompiler.Compiler/src/Compiler/AnalysisBasedMetadataManager.cs new file mode 100644 index 000000000..a9cd21190 --- /dev/null +++ b/src/ILCompiler.Compiler/src/Compiler/AnalysisBasedMetadataManager.cs @@ -0,0 +1,254 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections.Generic; + +using Internal.TypeSystem; + +using ILCompiler.Metadata; +using ILCompiler.DependencyAnalysis; + +using Debug = System.Diagnostics.Debug; + +namespace ILCompiler +{ + /// <summary> + /// A metadata manager that knows the full set of metadata ahead of time. + /// </summary> + public sealed class AnalysisBasedMetadataManager : GeneratingMetadataManager, ICompilationRootProvider + { + private readonly List<ModuleDesc> _modulesWithMetadata; + + private readonly Dictionary<TypeDesc, MetadataCategory> _reflectableTypes = new Dictionary<TypeDesc, MetadataCategory>(); + private readonly Dictionary<MethodDesc, MetadataCategory> _reflectableMethods = new Dictionary<MethodDesc, MetadataCategory>(); + private readonly Dictionary<FieldDesc, MetadataCategory> _reflectableFields = new Dictionary<FieldDesc, MetadataCategory>(); + + public AnalysisBasedMetadataManager( + ModuleDesc generatedAssembly, + CompilerTypeSystemContext typeSystemContext, + MetadataBlockingPolicy blockingPolicy, + string logFile, + StackTraceEmissionPolicy stackTracePolicy, + IEnumerable<ModuleDesc> modulesWithMetadata, + IEnumerable<ReflectableEntity<TypeDesc>> reflectableTypes, + IEnumerable<ReflectableEntity<MethodDesc>> reflectableMethods, + IEnumerable<ReflectableEntity<FieldDesc>> reflectableFields) + : base(generatedAssembly, typeSystemContext, blockingPolicy, logFile, stackTracePolicy) + { + _modulesWithMetadata = new List<ModuleDesc>(modulesWithMetadata); + + foreach (var refType in reflectableTypes) + { + _reflectableTypes.Add(refType.Entity, refType.Category); + } + + foreach (var refMethod in reflectableMethods) + { + // Asking for description or runtime mapping for a member without asking + // for the owning type would mean we can't actually satisfy the request. + Debug.Assert((refMethod.Category & MetadataCategory.Description) == 0 + || (_reflectableTypes[refMethod.Entity.OwningType] & MetadataCategory.Description) != 0); + Debug.Assert((refMethod.Category & MetadataCategory.RuntimeMapping) == 0 + || (_reflectableTypes[refMethod.Entity.OwningType] & MetadataCategory.RuntimeMapping) != 0); + _reflectableMethods.Add(refMethod.Entity, refMethod.Category); + + MethodDesc canonMethod = refMethod.Entity.GetCanonMethodTarget(CanonicalFormKind.Specific); + if (refMethod.Entity != canonMethod) + { + if (!_reflectableMethods.TryGetValue(canonMethod, out MetadataCategory category)) + category = 0; + _reflectableMethods[canonMethod] = category | refMethod.Category; + } + } + + foreach (var refField in reflectableFields) + { + // Asking for description or runtime mapping for a member without asking + // for the owning type would mean we can't actually satisfy the request. + Debug.Assert((refField.Category & MetadataCategory.Description) == 0 + || (_reflectableTypes[refField.Entity.OwningType] & MetadataCategory.Description) != 0); + Debug.Assert((refField.Category & MetadataCategory.RuntimeMapping) == 0 + || (_reflectableTypes[refField.Entity.OwningType] & MetadataCategory.RuntimeMapping) != 0); + _reflectableFields.Add(refField.Entity, refField.Category); + } + +#if DEBUG + HashSet<ModuleDesc> moduleHash = new HashSet<ModuleDesc>(_modulesWithMetadata); + foreach (var refType in reflectableTypes) + { + // The instantiated types need to agree on the Description bit with the definition. + // GetMetadataCategory relies on that. + 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)); + } + + foreach (var refMethod in reflectableMethods) + { + // The instantiated methods need to agree on the Description bit with the definition. + // GetMetadataCategory relies on that. + Debug.Assert((GetMetadataCategory(refMethod.Entity.GetTypicalMethodDefinition()) & MetadataCategory.Description) + == (GetMetadataCategory(refMethod.Entity) & MetadataCategory.Description)); + } + + foreach (var refField in reflectableFields) + { + // The instantiated fields need to agree on the Description bit with the definition. + // GetMetadataCategory relies on that. + Debug.Assert((GetMetadataCategory(refField.Entity.GetTypicalFieldDefinition()) & MetadataCategory.Description) + == (GetMetadataCategory(refField.Entity) & MetadataCategory.Description)); + } +#endif + } + + public override IEnumerable<ModuleDesc> GetCompilationModulesWithMetadata() + { + return _modulesWithMetadata; + } + + protected override void ComputeMetadata(NodeFactory factory, + out byte[] metadataBlob, + out List<MetadataMapping<MetadataType>> typeMappings, + out List<MetadataMapping<MethodDesc>> methodMappings, + out List<MetadataMapping<FieldDesc>> fieldMappings, + out List<MetadataMapping<MethodDesc>> stackTraceMapping) + { + ComputeMetadata(new Policy(_blockingPolicy, this), factory, + out metadataBlob, + out typeMappings, + out methodMappings, + out fieldMappings, + out stackTraceMapping); + } + + protected sealed override MetadataCategory GetMetadataCategory(MethodDesc method) + { + if (_reflectableMethods.TryGetValue(method, out MetadataCategory value)) + return value; + return 0; + } + + protected sealed override MetadataCategory GetMetadataCategory(TypeDesc type) + { + if (_reflectableTypes.TryGetValue(type, out MetadataCategory value)) + return value; + return 0; + } + + protected sealed override MetadataCategory GetMetadataCategory(FieldDesc field) + { + if (_reflectableFields.TryGetValue(field, out MetadataCategory value)) + return value; + return 0; + } + + void ICompilationRootProvider.AddCompilationRoots(IRootingServiceProvider rootProvider) + { + // We go over all the types and members that need a runtime artiface present in the + // compiled executable and root it. + + const string reason = "Reflection"; + + foreach (var pair in _reflectableTypes) + { + if ((pair.Value & MetadataCategory.RuntimeMapping) != 0) + rootProvider.AddCompilationRoot(pair.Key, reason); + } + + foreach (var pair in _reflectableMethods) + { + if ((pair.Value & MetadataCategory.RuntimeMapping) != 0) + { + MethodDesc method = pair.Key; + + // We need to root virtual methods as if they were called virtually. + // This will possibly trigger the generation of other overrides too. + if (method.IsVirtual) + rootProvider.RootVirtualMethodForReflection(method, reason); + + if (!method.IsAbstract) + rootProvider.AddCompilationRoot(pair.Key, reason); + } + } + + foreach (var pair in _reflectableFields) + { + if ((pair.Value & MetadataCategory.RuntimeMapping) != 0) + { + FieldDesc field = pair.Key; + + // We only care about static fields at this point. Instance fields don't need + // runtime artifacts generated in the image. + if (field.IsStatic && !field.IsLiteral) + { + if (field.IsThreadStatic) + rootProvider.RootThreadStaticBaseForType(field.OwningType, reason); + else if (field.HasGCStaticBase) + rootProvider.RootGCStaticBaseForType(field.OwningType, reason); + else + rootProvider.RootNonGCStaticBaseForType(field.OwningType, reason); + } + } + } + } + + private struct Policy : IMetadataPolicy + { + private readonly MetadataBlockingPolicy _blockingPolicy; + private readonly ExplicitScopeAssemblyPolicyMixin _explicitScopeMixin; + private readonly AnalysisBasedMetadataManager _parent; + + public Policy(MetadataBlockingPolicy blockingPolicy, + AnalysisBasedMetadataManager parent) + { + _blockingPolicy = blockingPolicy; + _parent = parent; + _explicitScopeMixin = new ExplicitScopeAssemblyPolicyMixin(); + } + + public bool GeneratesMetadata(FieldDesc fieldDef) + { + return (_parent.GetMetadataCategory(fieldDef) & MetadataCategory.Description) != 0; + } + + public bool GeneratesMetadata(MethodDesc methodDef) + { + return (_parent.GetMetadataCategory(methodDef) & MetadataCategory.Description) != 0; + } + + public bool GeneratesMetadata(MetadataType typeDef) + { + return (_parent.GetMetadataCategory(typeDef) & MetadataCategory.Description) != 0; + } + + public bool IsBlocked(MetadataType typeDef) + { + return _blockingPolicy.IsBlocked(typeDef); + } + + public bool IsBlocked(MethodDesc methodDef) + { + return _blockingPolicy.IsBlocked(methodDef); + } + + public ModuleDesc GetModuleOfType(MetadataType typeDef) + { + return _explicitScopeMixin.GetModuleOfType(typeDef); + } + } + } + + public struct ReflectableEntity<TEntity> + { + public readonly TEntity Entity; + public readonly MetadataCategory Category; + + public ReflectableEntity(TEntity entity, MetadataCategory category) + { + Entity = entity; + Category = category; + } + } +} diff --git a/src/ILCompiler.Compiler/src/Compiler/CompilationBuilder.cs b/src/ILCompiler.Compiler/src/Compiler/CompilationBuilder.cs index 7bdaf6abe..fc20b3432 100644 --- a/src/ILCompiler.Compiler/src/Compiler/CompilationBuilder.cs +++ b/src/ILCompiler.Compiler/src/Compiler/CompilationBuilder.cs @@ -33,7 +33,7 @@ namespace ILCompiler _context = context; _compilationGroup = compilationGroup; _nameMangler = nameMangler; - _metadataManager = new EmptyMetadataManager(compilationGroup, context); + _metadataManager = new EmptyMetadataManager(context); } public CompilationBuilder UseLogger(Logger logger) diff --git a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/UtcNodeFactory.cs b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/UtcNodeFactory.cs index a5be25757..fbeb03680 100644 --- a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/UtcNodeFactory.cs +++ b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/UtcNodeFactory.cs @@ -63,7 +63,7 @@ namespace ILCompiler { if (metadataFile == null) { - return new EmptyMetadataManager(compilationModuleGroup, context); + return new EmptyMetadataManager(context); } else { diff --git a/src/ILCompiler.Compiler/src/Compiler/EmptyMetadataManager.cs b/src/ILCompiler.Compiler/src/Compiler/EmptyMetadataManager.cs index f65e50c67..97eed867a 100644 --- a/src/ILCompiler.Compiler/src/Compiler/EmptyMetadataManager.cs +++ b/src/ILCompiler.Compiler/src/Compiler/EmptyMetadataManager.cs @@ -12,12 +12,12 @@ using Debug = System.Diagnostics.Debug; namespace ILCompiler { - class EmptyMetadataManager : MetadataManager + public class EmptyMetadataManager : MetadataManager { public override bool SupportsReflection => false; - public EmptyMetadataManager(CompilationModuleGroup group, CompilerTypeSystemContext typeSystemContext) - : base(group, typeSystemContext, new FullyBlockedMetadataPolicy()) + public EmptyMetadataManager(CompilerTypeSystemContext typeSystemContext) + : base(typeSystemContext, new FullyBlockedMetadataPolicy()) { } diff --git a/src/ILCompiler.Compiler/src/Compiler/CompilerGeneratedMetadataManager.cs b/src/ILCompiler.Compiler/src/Compiler/GeneratingMetadataManager.cs index 893f39163..e9270da0e 100644 --- a/src/ILCompiler.Compiler/src/Compiler/CompilerGeneratedMetadataManager.cs +++ b/src/ILCompiler.Compiler/src/Compiler/GeneratingMetadataManager.cs @@ -1,4 +1,4 @@ -// Licensed to the .NET Foundation under one or more agreements. +// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. @@ -16,25 +16,25 @@ using ILCompiler.Metadata; using ILCompiler.DependencyAnalysis; using Debug = System.Diagnostics.Debug; -using DependencyList = ILCompiler.DependencyAnalysisFramework.DependencyNodeCore<ILCompiler.DependencyAnalysis.NodeFactory>.DependencyList; namespace ILCompiler { /// <summary> - /// This class is responsible for managing native metadata to be emitted into the compiled - /// module. It applies a policy that every type/method emitted shall be reflectable. + /// Base class for metadata managers that generate metadata blobs. /// </summary> - public sealed class CompilerGeneratedMetadataManager : MetadataManager + public abstract class GeneratingMetadataManager : MetadataManager { - private readonly string _metadataLogFile; - private readonly StackTraceEmissionPolicy _stackTraceEmissionPolicy; - private Dictionary<DynamicInvokeMethodSignature, MethodDesc> _dynamicInvokeThunks; + protected readonly string _metadataLogFile; + protected readonly StackTraceEmissionPolicy _stackTraceEmissionPolicy; + private readonly Dictionary<DynamicInvokeMethodSignature, MethodDesc> _dynamicInvokeThunks; + private readonly ModuleDesc _generatedAssembly; - public CompilerGeneratedMetadataManager(CompilationModuleGroup group, CompilerTypeSystemContext typeSystemContext, string logFile, StackTraceEmissionPolicy stackTracePolicy) - : base(group, typeSystemContext, new BlockedInternalsBlockingPolicy()) + public GeneratingMetadataManager(ModuleDesc generatedAssembly, CompilerTypeSystemContext typeSystemContext, MetadataBlockingPolicy blockingPolicy, string logFile, StackTraceEmissionPolicy stackTracePolicy) + : base(typeSystemContext, blockingPolicy) { _metadataLogFile = logFile; _stackTraceEmissionPolicy = stackTracePolicy; + _generatedAssembly = generatedAssembly; if (DynamicInvokeMethodThunk.SupportsThunks(typeSystemContext)) { @@ -42,69 +42,26 @@ namespace ILCompiler } } - public override bool WillUseMetadataTokenToReferenceMethod(MethodDesc method) + public sealed override bool WillUseMetadataTokenToReferenceMethod(MethodDesc method) { return (GetMetadataCategory(method) & MetadataCategory.Description) != 0; } - public override bool WillUseMetadataTokenToReferenceField(FieldDesc field) + public sealed override bool WillUseMetadataTokenToReferenceField(FieldDesc field) { return (GetMetadataCategory(field) & MetadataCategory.Description) != 0; } - protected override MetadataCategory GetMetadataCategory(FieldDesc field) + protected void ComputeMetadata<TPolicy>( + TPolicy policy, + NodeFactory factory, + out byte[] metadataBlob, + out List<MetadataMapping<MetadataType>> typeMappings, + out List<MetadataMapping<MethodDesc>> methodMappings, + out List<MetadataMapping<FieldDesc>> fieldMappings, + out List<MetadataMapping<MethodDesc>> stackTraceMapping) where TPolicy : struct, IMetadataPolicy { - MetadataCategory category = 0; - - if (!IsReflectionBlocked(field)) - { - category = MetadataCategory.RuntimeMapping; - - if (_compilationModuleGroup.ContainsType(field.GetTypicalFieldDefinition().OwningType)) - category |= MetadataCategory.Description; - } - - return category; - } - - protected override MetadataCategory GetMetadataCategory(MethodDesc method) - { - MetadataCategory category = 0; - - if (!IsReflectionBlocked(method)) - { - category = MetadataCategory.RuntimeMapping; - - if (_compilationModuleGroup.ContainsType(method.GetTypicalMethodDefinition().OwningType)) - category |= MetadataCategory.Description; - } - - return category; - } - - protected override MetadataCategory GetMetadataCategory(TypeDesc type) - { - MetadataCategory category = 0; - - if (!IsReflectionBlocked(type)) - { - category = MetadataCategory.RuntimeMapping; - - if (_compilationModuleGroup.ContainsType(type.GetTypeDefinition())) - category |= MetadataCategory.Description; - } - - return category; - } - - protected override void ComputeMetadata(NodeFactory factory, - out byte[] metadataBlob, - out List<MetadataMapping<MetadataType>> typeMappings, - out List<MetadataMapping<MethodDesc>> methodMappings, - out List<MetadataMapping<FieldDesc>> fieldMappings, - out List<MetadataMapping<MethodDesc>> stackTraceMapping) - { - var transformed = MetadataTransform.Run(new GeneratedTypesAndCodeMetadataPolicy(_blockingPolicy, factory), GetCompilationModulesWithMetadata()); + var transformed = MetadataTransform.Run(policy, GetCompilationModulesWithMetadata()); MetadataTransform transform = transformed.Transform; // TODO: DeveloperExperienceMode: Use transformed.Transform.HandleType() to generate @@ -126,14 +83,15 @@ namespace ILCompiler // Methods that will end up in the reflection invoke table should not have an entry in stack trace table // We'll try looking them up in reflection data at runtime. if (transformed.GetTransformedMethodDefinition(typicalMethod) != null && - ShouldMethodBeInInvokeMap(method)) + ShouldMethodBeInInvokeMap(method) && + (GetMetadataCategory(method) & MetadataCategory.RuntimeMapping) != 0) continue; if (!_stackTraceEmissionPolicy.ShouldIncludeMethod(method)) continue; MetadataRecord record = transform.HandleQualifiedMethod(typicalMethod); - + // As a twist, instantiated generic methods appear as if instantiated over their formals. if (typicalMethod.HasInstantiation) { @@ -159,7 +117,7 @@ namespace ILCompiler writer.AdditionalRootRecords.Add(record); } - + var ms = new MemoryStream(); // .NET metadata is UTF-16 and UTF-16 contains code points that don't translate to UTF-8. @@ -207,6 +165,9 @@ namespace ILCompiler if (IsReflectionBlocked(method.Instantiation) || IsReflectionBlocked(method.OwningType.Instantiation)) continue; + if ((GetMetadataCategory(method) & MetadataCategory.RuntimeMapping) == 0) + continue; + MetadataRecord record = transformed.GetTransformedMethodDefinition(method.GetTypicalMethodDefinition()); if (record != null) @@ -248,7 +209,7 @@ namespace ILCompiler /// <summary> /// Is there a reflection invoke stub for a method that is invokable? /// </summary> - public override bool HasReflectionInvokeStubForInvokableMethod(MethodDesc method) + public sealed override bool HasReflectionInvokeStubForInvokableMethod(MethodDesc method) { Debug.Assert(IsReflectionInvokable(method)); @@ -267,7 +228,7 @@ namespace ILCompiler /// <summary> /// Gets a stub that can be used to reflection-invoke a method with a given signature. /// </summary> - public override MethodDesc GetCanonicalReflectionInvokeStub(MethodDesc method) + public sealed override MethodDesc GetCanonicalReflectionInvokeStub(MethodDesc method) { TypeSystemContext context = method.Context; var sig = method.Signature; @@ -277,66 +238,11 @@ namespace ILCompiler var lookupSig = new DynamicInvokeMethodSignature(sig); if (!_dynamicInvokeThunks.TryGetValue(lookupSig, out thunk)) { - thunk = new DynamicInvokeMethodThunk(_compilationModuleGroup.GeneratedAssembly.GetGlobalModuleType(), lookupSig); + thunk = new DynamicInvokeMethodThunk(_generatedAssembly.GetGlobalModuleType(), lookupSig); _dynamicInvokeThunks.Add(lookupSig, thunk); } return InstantiateCanonicalDynamicInvokeMethodForMethod(thunk, method); } - - protected override void GetMetadataDependenciesDueToReflectability(ref DependencyList dependencies, NodeFactory factory, MethodDesc method) - { - dependencies = dependencies ?? new DependencyList(); - dependencies.Add(factory.MethodMetadata(method.GetTypicalMethodDefinition()), "Reflectable method"); - } - - protected override void GetMetadataDependenciesDueToReflectability(ref DependencyList dependencies, NodeFactory factory, TypeDesc type) - { - TypeMetadataNode.GetMetadataDependencies(ref dependencies, factory, type, "Reflectable type"); - } - - private struct GeneratedTypesAndCodeMetadataPolicy : IMetadataPolicy - { - private readonly MetadataBlockingPolicy _blockingPolicy; - private readonly NodeFactory _factory; - private readonly ExplicitScopeAssemblyPolicyMixin _explicitScopeMixin; - - public GeneratedTypesAndCodeMetadataPolicy(MetadataBlockingPolicy blockingPolicy, NodeFactory factory) - { - _blockingPolicy = blockingPolicy; - _factory = factory; - _explicitScopeMixin = new ExplicitScopeAssemblyPolicyMixin(); - } - - public bool GeneratesMetadata(FieldDesc fieldDef) - { - return _factory.FieldMetadata(fieldDef).Marked; - } - - public bool GeneratesMetadata(MethodDesc methodDef) - { - return _factory.MethodMetadata(methodDef).Marked; - } - - public bool GeneratesMetadata(MetadataType typeDef) - { - return _factory.TypeMetadata(typeDef).Marked; - } - - public bool IsBlocked(MetadataType typeDef) - { - return _blockingPolicy.IsBlocked(typeDef); - } - - public bool IsBlocked(MethodDesc methodDef) - { - return _blockingPolicy.IsBlocked(methodDef); - } - - public ModuleDesc GetModuleOfType(MetadataType typeDef) - { - return _explicitScopeMixin.GetModuleOfType(typeDef); - } - } } } diff --git a/src/ILCompiler.Compiler/src/Compiler/ILScannerBuilder.cs b/src/ILCompiler.Compiler/src/Compiler/ILScannerBuilder.cs index 81b8458c9..340c5603a 100644 --- a/src/ILCompiler.Compiler/src/Compiler/ILScannerBuilder.cs +++ b/src/ILCompiler.Compiler/src/Compiler/ILScannerBuilder.cs @@ -23,12 +23,14 @@ namespace ILCompiler private Logger _logger = Logger.Null; private DependencyTrackingLevel _dependencyTrackingLevel = DependencyTrackingLevel.None; private IEnumerable<ICompilationRootProvider> _compilationRoots = Array.Empty<ICompilationRootProvider>(); + private MetadataManager _metadataManager; internal ILScannerBuilder(CompilerTypeSystemContext context, CompilationModuleGroup compilationGroup, NameMangler mangler) { _context = context; _compilationGroup = compilationGroup; _nameMangler = mangler; + _metadataManager = new EmptyMetadataManager(context); } public ILScannerBuilder UseDependencyTracking(DependencyTrackingLevel trackingLevel) @@ -43,12 +45,16 @@ namespace ILCompiler return this; } + public ILScannerBuilder UseMetadataManager(MetadataManager metadataManager) + { + _metadataManager = metadataManager; + return this; + } + public IILScanner ToILScanner() { - // TODO: we will want different metadata managers depending on whether we're doing reflection analysis - var metadataManager = new CompilerGeneratedMetadataManager(_compilationGroup, _context, null, new NoStackTraceEmissionPolicy()); var interopStubManager = new CompilerGeneratedInteropStubManager(_compilationGroup, _context, new InteropStateManager(_compilationGroup.GeneratedAssembly)); - var nodeFactory = new ILScanNodeFactory(_context, _compilationGroup, metadataManager, interopStubManager, _nameMangler); + var nodeFactory = new ILScanNodeFactory(_context, _compilationGroup, _metadataManager, interopStubManager, _nameMangler); DependencyAnalyzerBase<NodeFactory> graph = _dependencyTrackingLevel.CreateDependencyGraph(nodeFactory); return new ILScanner(graph, nodeFactory, _compilationRoots, new NullDebugInformationProvider(), _logger); diff --git a/src/ILCompiler.Compiler/src/Compiler/MetadataManager.cs b/src/ILCompiler.Compiler/src/Compiler/MetadataManager.cs index 41799b762..237e850ab 100644 --- a/src/ILCompiler.Compiler/src/Compiler/MetadataManager.cs +++ b/src/ILCompiler.Compiler/src/Compiler/MetadataManager.cs @@ -3,12 +3,10 @@ // See the LICENSE file in the project root for more information. using System; -using System.IO; using System.Collections.Generic; using Internal.TypeSystem; -using ILCompiler.Metadata; using ILCompiler.DependencyAnalysis; using ILCompiler.DependencyAnalysisFramework; @@ -34,7 +32,6 @@ namespace ILCompiler private List<MetadataMapping<MethodDesc>> _methodMappings; private List<MetadataMapping<MethodDesc>> _stackTraceMappings; - protected readonly CompilationModuleGroup _compilationModuleGroup; protected readonly CompilerTypeSystemContext _typeSystemContext; protected readonly MetadataBlockingPolicy _blockingPolicy; @@ -44,7 +41,6 @@ namespace ILCompiler private HashSet<MethodDesc> _methodsGenerated = new HashSet<MethodDesc>(); private HashSet<GenericDictionaryNode> _genericDictionariesGenerated = new HashSet<GenericDictionaryNode>(); private HashSet<IMethodBodyNode> _methodBodiesGenerated = new HashSet<IMethodBodyNode>(); - private List<ModuleDesc> _modulesWithMetadata = new List<ModuleDesc>(); private List<TypeGVMEntriesNode> _typeGVMEntries = new List<TypeGVMEntriesNode>(); private HashSet<DefaultConstructorFromLazyNode> _defaultConstructorsNeeded = new HashSet<DefaultConstructorFromLazyNode>(); @@ -52,9 +48,8 @@ namespace ILCompiler internal DynamicInvokeTemplateDataNode DynamicInvokeTemplateData { get; private set; } public virtual bool SupportsReflection => true; - public MetadataManager(CompilationModuleGroup compilationModuleGroup, CompilerTypeSystemContext typeSystemContext, MetadataBlockingPolicy blockingPolicy) + public MetadataManager(CompilerTypeSystemContext typeSystemContext, MetadataBlockingPolicy blockingPolicy) { - _compilationModuleGroup = compilationModuleGroup; _typeSystemContext = typeSystemContext; _blockingPolicy = blockingPolicy; } @@ -154,7 +149,7 @@ namespace ILCompiler header.Add(BlobIdToReadyToRunSection(ReflectionMapBlob.NativeStatics), nativeStaticsTableNode, nativeStaticsTableNode, nativeStaticsTableNode.EndSymbol); } - private void Graph_NewMarkedNode(DependencyNodeCore<NodeFactory> obj) + protected virtual void Graph_NewMarkedNode(DependencyNodeCore<NodeFactory> obj) { var eetypeNode = obj as EETypeNode; if (eetypeNode != null) @@ -175,13 +170,10 @@ namespace ILCompiler _methodBodiesGenerated.Add(methodBodyNode); } - IMethodNode methodNode = obj as MethodCodeNode; + IMethodNode methodNode = methodBodyNode; if (methodNode == null) methodNode = obj as ShadowConcreteMethodNode; - if (methodNode == null) - methodNode = obj as NonExternMethodSymbolNode; - if (methodNode != null) { _methodsGenerated.Add(methodNode.Method); @@ -217,13 +209,6 @@ namespace ILCompiler { _defaultConstructorsNeeded.Add(ctorFromLazyGenericsNode); } - - // TODO: temporary until we have an IL scanning Metadata Manager. We shouldn't have to keep track of these. - var moduleMetadataNode = obj as ModuleMetadataNode; - if (moduleMetadataNode != null) - { - _modulesWithMetadata.Add(moduleMetadataNode.Module); - } } /// <summary> @@ -547,15 +532,7 @@ namespace ILCompiler /// <summary> /// Returns a set of modules that will get some metadata emitted into the output module /// </summary> - public virtual IEnumerable<ModuleDesc> GetCompilationModulesWithMetadata() - { - // TODO: this is temporary until we have a metadata manager for IL scanner. This method should be abstract. - - // TODO: We should also return the list of satellite assemblies here (this API is used by the ResourceDataNode to - // generate the BlobIdResourceIndex blob, for the ResourceManager to work at runtime). - - return _modulesWithMetadata; - } + public abstract IEnumerable<ModuleDesc> GetCompilationModulesWithMetadata(); public byte[] GetMetadataBlob(NodeFactory factory) { diff --git a/src/ILCompiler.Compiler/src/Compiler/PrecomputedMetadataManager.cs b/src/ILCompiler.Compiler/src/Compiler/PrecomputedMetadataManager.cs index 6413fcff2..5a539cf81 100644 --- a/src/ILCompiler.Compiler/src/Compiler/PrecomputedMetadataManager.cs +++ b/src/ILCompiler.Compiler/src/Compiler/PrecomputedMetadataManager.cs @@ -52,6 +52,7 @@ namespace ILCompiler private readonly byte[] _metadataBlob; private readonly StackTraceEmissionPolicy _stackTraceEmissionPolicy; private byte[] _stackTraceBlob; + private readonly CompilationModuleGroup _compilationModuleGroup; public PrecomputedMetadataManager( CompilationModuleGroup group, @@ -61,8 +62,9 @@ namespace ILCompiler IEnumerable<ModuleDesc> inputMetadataOnlyAssemblies, byte[] metadataBlob, StackTraceEmissionPolicy stackTraceEmissionPolicy) - : base(group, typeSystemContext, new AttributeSpecifiedBlockingPolicy()) + : base(typeSystemContext, new AttributeSpecifiedBlockingPolicy()) { + _compilationModuleGroup = group; _metadataDescribingModule = metadataDescribingModule; _compilationModules = new HashSet<ModuleDesc>(compilationModules); _metadataOnlyAssemblies = new HashSet<ModuleDesc>(inputMetadataOnlyAssemblies); diff --git a/src/ILCompiler.Compiler/src/Compiler/UsageBasedMetadataManager.cs b/src/ILCompiler.Compiler/src/Compiler/UsageBasedMetadataManager.cs new file mode 100644 index 000000000..454432b9b --- /dev/null +++ b/src/ILCompiler.Compiler/src/Compiler/UsageBasedMetadataManager.cs @@ -0,0 +1,325 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections.Generic; + +using Internal.TypeSystem; + +using ILCompiler.Metadata; +using ILCompiler.DependencyAnalysis; +using ILCompiler.DependencyAnalysisFramework; + +using DependencyList = ILCompiler.DependencyAnalysisFramework.DependencyNodeCore<ILCompiler.DependencyAnalysis.NodeFactory>.DependencyList; + +namespace ILCompiler +{ + /// <summary> + /// This class is responsible for managing native metadata to be emitted into the compiled + /// module. It applies a policy that every type/method that is statically used shall be reflectable. + /// </summary> + public sealed class UsageBasedMetadataManager : GeneratingMetadataManager + { + private readonly CompilationModuleGroup _compilationModuleGroup; + + private readonly List<ModuleDesc> _modulesWithMetadata = new List<ModuleDesc>(); + private readonly List<FieldDesc> _fieldsWithMetadata = new List<FieldDesc>(); + private readonly List<MethodDesc> _methodsWithMetadata = new List<MethodDesc>(); + private readonly List<MetadataType> _typesWithMetadata = new List<MetadataType>(); + + public UsageBasedMetadataManager( + CompilationModuleGroup group, + CompilerTypeSystemContext typeSystemContext, + MetadataBlockingPolicy blockingPolicy, + string logFile, + StackTraceEmissionPolicy stackTracePolicy) + : base(group.GeneratedAssembly, typeSystemContext, blockingPolicy, logFile, stackTracePolicy) + { + _compilationModuleGroup = group; + } + + protected override void Graph_NewMarkedNode(DependencyNodeCore<NodeFactory> obj) + { + base.Graph_NewMarkedNode(obj); + + var moduleMetadataNode = obj as ModuleMetadataNode; + if (moduleMetadataNode != null) + { + _modulesWithMetadata.Add(moduleMetadataNode.Module); + } + + var fieldMetadataNode = obj as FieldMetadataNode; + if (fieldMetadataNode != null) + { + _fieldsWithMetadata.Add(fieldMetadataNode.Field); + } + + var methodMetadataNode = obj as MethodMetadataNode; + if (methodMetadataNode != null) + { + _methodsWithMetadata.Add(methodMetadataNode.Method); + } + + var typeMetadataNode = obj as TypeMetadataNode; + if (typeMetadataNode != null) + { + _typesWithMetadata.Add(typeMetadataNode.Type); + } + } + + protected override MetadataCategory GetMetadataCategory(FieldDesc field) + { + MetadataCategory category = 0; + + if (!IsReflectionBlocked(field)) + { + category = MetadataCategory.RuntimeMapping; + + if (_compilationModuleGroup.ContainsType(field.GetTypicalFieldDefinition().OwningType)) + category |= MetadataCategory.Description; + } + + return category; + } + + protected override MetadataCategory GetMetadataCategory(MethodDesc method) + { + MetadataCategory category = 0; + + if (!IsReflectionBlocked(method)) + { + category = MetadataCategory.RuntimeMapping; + + if (_compilationModuleGroup.ContainsType(method.GetTypicalMethodDefinition().OwningType)) + category |= MetadataCategory.Description; + } + + return category; + } + + protected override MetadataCategory GetMetadataCategory(TypeDesc type) + { + MetadataCategory category = 0; + + if (!IsReflectionBlocked(type)) + { + category = MetadataCategory.RuntimeMapping; + + if (_compilationModuleGroup.ContainsType(type.GetTypeDefinition())) + category |= MetadataCategory.Description; + } + + return category; + } + + protected override void ComputeMetadata(NodeFactory factory, + out byte[] metadataBlob, + out List<MetadataMapping<MetadataType>> typeMappings, + out List<MetadataMapping<MethodDesc>> methodMappings, + out List<MetadataMapping<FieldDesc>> fieldMappings, + out List<MetadataMapping<MethodDesc>> stackTraceMapping) + { + ComputeMetadata(new GeneratedTypesAndCodeMetadataPolicy(_blockingPolicy, factory), + factory, out metadataBlob, out typeMappings, out methodMappings, out fieldMappings, out stackTraceMapping); + } + + protected override void GetMetadataDependenciesDueToReflectability(ref DependencyList dependencies, NodeFactory factory, MethodDesc method) + { + if (method.GetTypicalMethodDefinition().OwningType == factory.ArrayOfTClass) + return; + + dependencies = dependencies ?? new DependencyList(); + dependencies.Add(factory.MethodMetadata(method.GetTypicalMethodDefinition()), "Reflectable method"); + } + + protected override void GetMetadataDependenciesDueToReflectability(ref DependencyList dependencies, NodeFactory factory, TypeDesc type) + { + if (type.GetTypeDefinition() == factory.ArrayOfTClass) + return; + + TypeMetadataNode.GetMetadataDependencies(ref dependencies, factory, type, "Reflectable type"); + } + + public override IEnumerable<ModuleDesc> GetCompilationModulesWithMetadata() + { + return _modulesWithMetadata; + } + + public MetadataManager ToAnalysisBasedMetadataManager() + { + var reflectableTypes = ReflectableEntityBuilder<TypeDesc>.Create(); + + // Collect the list of types that are generating reflection metadata + foreach (var typeWithMetadata in _typesWithMetadata) + { + reflectableTypes[typeWithMetadata] = MetadataCategory.Description; + } + + // All the constructed types we generated that are not blocked are required to have runtime artifacts + foreach (var constructedType in GetTypesWithConstructedEETypes()) + { + if (!IsReflectionBlocked(constructedType)) + { + reflectableTypes[constructedType] |= MetadataCategory.RuntimeMapping; + + // Also set the description bit if the definition is getting metadata. + TypeDesc constructedTypeDefinition = constructedType.GetTypeDefinition(); + if (constructedType != constructedTypeDefinition && + (reflectableTypes[constructedTypeDefinition] & MetadataCategory.Description) != 0) + { + reflectableTypes[constructedType] |= MetadataCategory.Description; + } + } + } + + // All the necessary types for which this is the higest load level are required to have runtime artifacts + foreach (var necessaryType in GetTypesWithEETypes()) + { + if (!ConstructedEETypeNode.CreationAllowed(necessaryType) && + !IsReflectionBlocked(necessaryType)) + { + reflectableTypes[necessaryType] |= MetadataCategory.RuntimeMapping; + + // Also set the description bit if the definition is getting metadata. + TypeDesc necessaryTypeDefinition = necessaryType.GetTypeDefinition(); + if (necessaryType != necessaryTypeDefinition && + (reflectableTypes[necessaryTypeDefinition] & MetadataCategory.Description) != 0) + { + reflectableTypes[necessaryType] |= MetadataCategory.Description; + } + } + } + + var reflectableMethods = ReflectableEntityBuilder<MethodDesc>.Create(); + foreach (var methodWithMetadata in _methodsWithMetadata) + { + reflectableMethods[methodWithMetadata] = MetadataCategory.Description; + } + + foreach (var method in GetCompiledMethods()) + { + if (!method.IsCanonicalMethod(CanonicalFormKind.Specific) && + !IsReflectionBlocked(method)) + { + if ((reflectableTypes[method.OwningType] & MetadataCategory.RuntimeMapping) != 0) + reflectableMethods[method] |= MetadataCategory.RuntimeMapping; + + // Also set the description bit if the definition is getting metadata. + MethodDesc typicalMethod = method.GetTypicalMethodDefinition(); + if (method != typicalMethod && + (reflectableMethods[typicalMethod] & MetadataCategory.Description) != 0) + { + reflectableMethods[method] |= MetadataCategory.Description; + reflectableTypes[method.OwningType] |= MetadataCategory.Description; + } + } + } + + var reflectableFields = ReflectableEntityBuilder<FieldDesc>.Create(); + foreach (var fieldWithMetadata in _fieldsWithMetadata) + { + reflectableFields[fieldWithMetadata] = MetadataCategory.Description; + } + + // TODO: this should be more precise + foreach (var reflectableType in reflectableTypes.ToEnumerable()) + { + if (reflectableType.Entity.IsGenericDefinition) + continue; + + if (reflectableType.Entity.IsCanonicalSubtype(CanonicalFormKind.Specific)) + continue; + + foreach (var field in reflectableType.Entity.GetFields()) + { + if (CanGenerateMetadata(field.GetTypicalFieldDefinition())) + reflectableFields[field] |= reflectableType.Category; + } + } + + return new AnalysisBasedMetadataManager(_compilationModuleGroup.GeneratedAssembly, + _typeSystemContext, _blockingPolicy, _metadataLogFile, _stackTraceEmissionPolicy, + _modulesWithMetadata, reflectableTypes.ToEnumerable(), reflectableMethods.ToEnumerable(), + reflectableFields.ToEnumerable()); + } + + private struct ReflectableEntityBuilder<T> + { + private Dictionary<T, MetadataCategory> _dictionary; + + public static ReflectableEntityBuilder<T> Create() + { + return new ReflectableEntityBuilder<T> + { + _dictionary = new Dictionary<T, MetadataCategory>(), + }; + } + + public MetadataCategory this[T key] + { + get + { + if (_dictionary.TryGetValue(key, out MetadataCategory category)) + return category; + return 0; + } + set + { + _dictionary[key] = value; + } + } + + public IEnumerable<ReflectableEntity<T>> ToEnumerable() + { + foreach (var entry in _dictionary) + { + yield return new ReflectableEntity<T>(entry.Key, entry.Value); + } + } + } + + private struct GeneratedTypesAndCodeMetadataPolicy : IMetadataPolicy + { + private readonly MetadataBlockingPolicy _blockingPolicy; + private readonly NodeFactory _factory; + private readonly ExplicitScopeAssemblyPolicyMixin _explicitScopeMixin; + + public GeneratedTypesAndCodeMetadataPolicy(MetadataBlockingPolicy blockingPolicy, NodeFactory factory) + { + _blockingPolicy = blockingPolicy; + _factory = factory; + _explicitScopeMixin = new ExplicitScopeAssemblyPolicyMixin(); + } + + public bool GeneratesMetadata(FieldDesc fieldDef) + { + return _factory.FieldMetadata(fieldDef).Marked; + } + + public bool GeneratesMetadata(MethodDesc methodDef) + { + return _factory.MethodMetadata(methodDef).Marked; + } + + public bool GeneratesMetadata(MetadataType typeDef) + { + return _factory.TypeMetadata(typeDef).Marked; + } + + public bool IsBlocked(MetadataType typeDef) + { + return _blockingPolicy.IsBlocked(typeDef); + } + + public bool IsBlocked(MethodDesc methodDef) + { + return _blockingPolicy.IsBlocked(methodDef); + } + + public ModuleDesc GetModuleOfType(MetadataType typeDef) + { + return _explicitScopeMixin.GetModuleOfType(typeDef); + } + } + } +} diff --git a/src/ILCompiler.Compiler/src/ILCompiler.Compiler.csproj b/src/ILCompiler.Compiler/src/ILCompiler.Compiler.csproj index 43cc9b802..7df0a8e4d 100644 --- a/src/ILCompiler.Compiler/src/ILCompiler.Compiler.csproj +++ b/src/ILCompiler.Compiler/src/ILCompiler.Compiler.csproj @@ -97,6 +97,7 @@ <Compile Include="..\..\JitInterface\src\TypeString.cs"> <Link>JitInterface\TypeString.cs</Link> </Compile> + <Compile Include="Compiler\AnalysisBasedMetadataManager.cs" /> <Compile Include="Compiler\BlockedInternalsBlockingPolicy.cs" /> <Compile Include="Compiler\CodeGenerationFailedException.cs" /> <Compile Include="Compiler\CompilerGeneratedType.cs" /> @@ -137,6 +138,7 @@ <Compile Include="Compiler\EmptyInteropStubManager.cs" /> <Compile Include="Compiler\DependencyAnalysis\SortableDependencyNode.cs" /> <Compile Include="Compiler\DependencyAnalysis\ImportedNodeProvider.cs" /> + <Compile Include="Compiler\GeneratingMetadataManager.cs" /> <Compile Include="Compiler\InternalCompilerErrorException.cs" /> <Compile Include="Compiler\PreInitFieldInfo.cs" /> <Compile Include="Compiler\DependencyAnalysis\FrozenArrayNode.cs" /> @@ -321,7 +323,6 @@ <Compile Include="Compiler\MainMethodRootProvider.cs" /> <Compile Include="Compiler\ManagedBinaryEmitter.cs" /> <Compile Include="Compiler\MemoryHelper.cs" /> - <Compile Include="Compiler\CompilerGeneratedMetadataManager.cs" /> <Compile Include="Compiler\MergedAssemblyRecords.cs" /> <Compile Include="Compiler\MetadataManager.cs" /> <Compile Include="Compiler\InteropStubManager.cs" /> @@ -343,6 +344,7 @@ <Compile Include="Compiler\TypeExtensions.cs" /> <Compile Include="Compiler\CompilerTypeSystemContext.TypeInit.cs" /> <Compile Include="Compiler\CoreRTNameMangler.cs" /> + <Compile Include="Compiler\UsageBasedMetadataManager.cs" /> <Compile Include="Compiler\UserDefinedTypeDescriptor.cs" /> <Compile Include="Compiler\UniversalGenericsRootProvider.cs" /> <Compile Include="Compiler\UnixNodeMangler.cs" /> diff --git a/src/ILCompiler/src/Program.cs b/src/ILCompiler/src/Program.cs index e3efa2321..6070ec35d 100644 --- a/src/ILCompiler/src/Program.cs +++ b/src/ILCompiler/src/Program.cs @@ -382,6 +382,16 @@ namespace ILCompiler else builder = new RyuJitCompilationBuilder(typeSystemContext, compilationGroup); + var stackTracePolicy = _emitStackTraceData ? + (StackTraceEmissionPolicy)new EcmaMethodStackTraceEmissionPolicy() : new NoStackTraceEmissionPolicy(); + + UsageBasedMetadataManager metadataManager = new UsageBasedMetadataManager( + compilationGroup, + typeSystemContext, + new BlockedInternalsBlockingPolicy(), + _metadataLogFileName, + stackTracePolicy); + // Unless explicitly opted in at the command line, we enable scanner for retail builds by default. // We don't do this for CppCodegen and Wasm, because those codegens are behind. // We also don't do this for multifile because scanner doesn't simulate inlining (this would be @@ -392,11 +402,13 @@ namespace ILCompiler useScanner &= !_noScanner; + MetadataManager compilationMetadataManager = _isWasmCodegen ? (MetadataManager)new EmptyMetadataManager(typeSystemContext) : metadataManager; ILScanResults scanResults = null; if (useScanner) { ILScannerBuilder scannerBuilder = builder.GetILScannerBuilder() - .UseCompilationRoots(compilationRoots); + .UseCompilationRoots(compilationRoots) + .UseMetadataManager(metadataManager); if (_scanDgmlLogFileName != null) scannerBuilder.UseDependencyTracking(_generateFullScanDgmlLog ? DependencyTrackingLevel.All : DependencyTrackingLevel.First); @@ -404,6 +416,8 @@ namespace ILCompiler IILScanner scanner = scannerBuilder.ToILScanner(); scanResults = scanner.Scan(); + + compilationMetadataManager = metadataManager.ToAnalysisBasedMetadataManager(); } var logger = new Logger(Console.Out, _isVerbose); @@ -415,18 +429,11 @@ namespace ILCompiler DependencyTrackingLevel trackingLevel = _dgmlLogFileName == null ? DependencyTrackingLevel.None : (_generateFullDgmlLog ? DependencyTrackingLevel.All : DependencyTrackingLevel.First); - var stackTracePolicy = _emitStackTraceData ? - (StackTraceEmissionPolicy)new EcmaMethodStackTraceEmissionPolicy() : new NoStackTraceEmissionPolicy(); - - CompilerGeneratedMetadataManager metadataManager = new CompilerGeneratedMetadataManager( - compilationGroup, - typeSystemContext, - _metadataLogFileName, - stackTracePolicy); + compilationRoots.Add(compilationMetadataManager); builder .UseBackendOptions(_codegenOptions) - .UseMetadataManager(metadataManager) + .UseMetadataManager(compilationMetadataManager) .UseLogger(logger) .UseDependencyTracking(trackingLevel) .UseCompilationRoots(compilationRoots) |