From fd44d5c66771764d265ba6b607d2db85dbba28b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20Strehovsk=C3=BD?= Date: Fri, 19 Jan 2024 00:19:00 +0900 Subject: [PATCH] Fold GeneratingMetadataManager into MetadataManager (#97140) Long time ago, `MetadataManager` had two descendants: the `GeneratingMetadataManager` and another one that I forgot the name of. It was used for .NET Native and didn't generate metadata, because ILTransforms did. Fold the classes together. --- .../Compiler/AnalysisBasedMetadataManager.cs | 2 +- .../Compiler/GeneratingMetadataManager.cs | 224 ------------------ .../Compiler/MetadataManager.cs | 197 ++++++++++++++- .../Compiler/UsageBasedMetadataManager.cs | 2 +- .../ILCompiler.Compiler.csproj | 1 - 5 files changed, 195 insertions(+), 231 deletions(-) delete mode 100644 src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/GeneratingMetadataManager.cs diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/AnalysisBasedMetadataManager.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/AnalysisBasedMetadataManager.cs index b748cc2c46c02..8c24c7addbb08 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/AnalysisBasedMetadataManager.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/AnalysisBasedMetadataManager.cs @@ -19,7 +19,7 @@ namespace ILCompiler /// /// A metadata manager that knows the full set of metadata ahead of time. /// - public sealed class AnalysisBasedMetadataManager : GeneratingMetadataManager, ICompilationRootProvider + public sealed class AnalysisBasedMetadataManager : MetadataManager, ICompilationRootProvider { private readonly List _modulesWithMetadata; private readonly List _typesWithRootedCctorContext; diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/GeneratingMetadataManager.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/GeneratingMetadataManager.cs deleted file mode 100644 index 1e7442fec7486..0000000000000 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/GeneratingMetadataManager.cs +++ /dev/null @@ -1,224 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System; -using System.IO; -using System.Collections.Generic; -using System.Text; -using Internal.TypeSystem; -using Internal.Metadata.NativeFormat.Writer; - -using ILCompiler.Metadata; -using ILCompiler.DependencyAnalysis; - -using Debug = System.Diagnostics.Debug; - -namespace ILCompiler -{ - /// - /// Base class for metadata managers that generate metadata blobs. - /// - public abstract class GeneratingMetadataManager : MetadataManager - { - protected readonly string _metadataLogFile; - protected readonly StackTraceEmissionPolicy _stackTraceEmissionPolicy; - private readonly ModuleDesc _generatedAssembly; - - public GeneratingMetadataManager(CompilerTypeSystemContext typeSystemContext, MetadataBlockingPolicy blockingPolicy, - ManifestResourceBlockingPolicy resourceBlockingPolicy, string logFile, StackTraceEmissionPolicy stackTracePolicy, - DynamicInvokeThunkGenerationPolicy invokeThunkGenerationPolicy, MetadataManagerOptions options) - : base(typeSystemContext, blockingPolicy, resourceBlockingPolicy, invokeThunkGenerationPolicy, options) - { - _metadataLogFile = logFile; - _stackTraceEmissionPolicy = stackTracePolicy; - _generatedAssembly = typeSystemContext.GeneratedAssembly; - } - - public sealed override bool WillUseMetadataTokenToReferenceMethod(MethodDesc method) - { - return (GetMetadataCategory(method) & MetadataCategory.Description) != 0; - } - - public sealed override bool WillUseMetadataTokenToReferenceField(FieldDesc field) - { - return (GetMetadataCategory(field) & MetadataCategory.Description) != 0; - } - - protected void ComputeMetadata( - TPolicy policy, - NodeFactory factory, - out byte[] metadataBlob, - out List> typeMappings, - out List> methodMappings, - out List> fieldMappings, - out List stackTraceMapping) where TPolicy : struct, IMetadataPolicy - { - var transformed = MetadataTransform.Run(policy, GetCompilationModulesWithMetadata()); - MetadataTransform transform = transformed.Transform; - - // Generate metadata blob - var writer = new MetadataWriter(); - writer.ScopeDefinitions.AddRange(transformed.Scopes); - - // Generate entries in the blob for methods that will be necessary for stack trace purposes. - var stackTraceRecords = new List(); - foreach (var methodBody in GetCompiledMethodBodies()) - { - MethodDesc method = methodBody.Method; - - MethodDesc typicalMethod = method.GetTypicalMethodDefinition(); - - // 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) && - (GetMetadataCategory(method) & MetadataCategory.RuntimeMapping) != 0) - continue; - - MethodStackTraceVisibilityFlags stackVisibility = _stackTraceEmissionPolicy.GetMethodVisibility(method); - bool isHidden = (stackVisibility & MethodStackTraceVisibilityFlags.IsHidden) != 0; - - if ((stackVisibility & MethodStackTraceVisibilityFlags.HasMetadata) != 0) - { - StackTraceRecordData record = CreateStackTraceRecord(transform, method, isHidden); - - stackTraceRecords.Add(record); - - writer.AdditionalRootRecords.Add(record.OwningType); - writer.AdditionalRootRecords.Add(record.MethodName); - writer.AdditionalRootRecords.Add(record.MethodSignature); - writer.AdditionalRootRecords.Add(record.MethodInstantiationArgumentCollection); - } - else if (isHidden) - { - stackTraceRecords.Add(new StackTraceRecordData(method, null, null, null, null, isHidden)); - } - } - - var ms = new MemoryStream(); - - // .NET metadata is UTF-16 and UTF-16 contains code points that don't translate to UTF-8. - var noThrowUtf8Encoding = new UTF8Encoding(false, false); - - using (var logWriter = _metadataLogFile != null ? new StreamWriter(File.Open(_metadataLogFile, FileMode.Create, FileAccess.Write, FileShare.Read), noThrowUtf8Encoding) : null) - { - writer.LogWriter = logWriter; - writer.Write(ms); - } - - metadataBlob = ms.ToArray(); - - const int MaxAllowedMetadataOffset = 0xFFFFFF; - if (metadataBlob.Length > MaxAllowedMetadataOffset) - { - // Offset portion of metadata handles is limited to 16 MB. - throw new InvalidOperationException($"Metadata blob exceeded the addressing range (allowed: {MaxAllowedMetadataOffset}, actual: {metadataBlob.Length})"); - } - - typeMappings = new List>(); - methodMappings = new List>(); - fieldMappings = new List>(); - stackTraceMapping = new List(); - - // Generate type definition mappings - foreach (var type in factory.MetadataManager.GetTypesWithEETypes()) - { - MetadataType definition = type.IsTypeDefinition ? type as MetadataType : null; - if (definition == null) - continue; - - MetadataRecord record = transformed.GetTransformedTypeDefinition(definition); - - // Reflection requires that we maintain type identity. Even if we only generated a TypeReference record, - // if there is an MethodTable for it, we also need a mapping table entry for it. - record ??= transformed.GetTransformedTypeReference(definition); - - if (record != null) - typeMappings.Add(new MetadataMapping(definition, writer.GetRecordHandle(record))); - } - - foreach (var method in GetReflectableMethods()) - { - if (method.IsGenericMethodDefinition || method.OwningType.IsGenericDefinition) - { - // Generic definitions don't have runtime artifacts we would need to map to. - continue; - } - - if (method.GetCanonMethodTarget(CanonicalFormKind.Specific) != method) - { - // Methods that are not in their canonical form are not interesting - continue; - } - - 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) - methodMappings.Add(new MetadataMapping(method, writer.GetRecordHandle(record))); - } - - HashSet canonicalFields = new HashSet(); - foreach (var field in GetFieldsWithRuntimeMapping()) - { - FieldDesc fieldToAdd = field; - TypeDesc canonOwningType = field.OwningType.ConvertToCanonForm(CanonicalFormKind.Specific); - if (canonOwningType.IsCanonicalSubtype(CanonicalFormKind.Any)) - { - FieldDesc canonField = _typeSystemContext.GetFieldForInstantiatedType(field.GetTypicalFieldDefinition(), (InstantiatedType)canonOwningType); - - // If we already added a canonically equivalent field, skip this one. - if (!canonicalFields.Add(canonField)) - continue; - - fieldToAdd = canonField; - } - - Field record = transformed.GetTransformedFieldDefinition(fieldToAdd.GetTypicalFieldDefinition()); - if (record != null) - fieldMappings.Add(new MetadataMapping(fieldToAdd, writer.GetRecordHandle(record))); - } - - // Generate stack trace metadata mapping - foreach (var stackTraceRecord in stackTraceRecords) - { - if (stackTraceRecord.OwningType != null) - { - StackTraceMapping mapping = new StackTraceMapping( - stackTraceRecord.Method, - writer.GetRecordHandle(stackTraceRecord.OwningType), - writer.GetRecordHandle(stackTraceRecord.MethodSignature), - writer.GetRecordHandle(stackTraceRecord.MethodName), - stackTraceRecord.MethodInstantiationArgumentCollection != null ? writer.GetRecordHandle(stackTraceRecord.MethodInstantiationArgumentCollection) : 0, - stackTraceRecord.IsHidden); - stackTraceMapping.Add(mapping); - } - else - { - Debug.Assert(stackTraceRecord.IsHidden); - stackTraceMapping.Add(new StackTraceMapping(stackTraceRecord.Method, 0, 0, 0, 0, stackTraceRecord.IsHidden)); - } - } - } - - /// - /// Gets a list of fields that got "compiled" and are eligible for a runtime mapping. - /// - /// - protected abstract IEnumerable GetFieldsWithRuntimeMapping(); - - /// - /// Gets a stub that can be used to reflection-invoke a method with a given signature. - /// - public sealed override MethodDesc GetReflectionInvokeStub(MethodDesc method) - { - return _typeSystemContext.GetDynamicInvokeThunk(method.Signature, - !method.Signature.IsStatic && method.OwningType.IsValueType); - } - } -} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/MetadataManager.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/MetadataManager.cs index 5058524c8cecb..2f92ccdb9048e 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/MetadataManager.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/MetadataManager.cs @@ -3,11 +3,14 @@ using System; using System.Collections.Generic; +using System.IO; +using System.Text; using Internal.TypeSystem; using ILCompiler.DependencyAnalysis; using ILCompiler.DependencyAnalysisFramework; +using ILCompiler.Metadata; using Debug = System.Diagnostics.Debug; using ReadyToRunSectionType = Internal.Runtime.ReadyToRunSectionType; @@ -17,9 +20,12 @@ using CombinedDependencyListEntry = ILCompiler.DependencyAnalysisFramework.DependencyNodeCore.CombinedDependencyListEntry; using MethodIL = Internal.IL.MethodIL; using CustomAttributeValue = System.Reflection.Metadata.CustomAttributeValue; +using MethodSignature = Internal.TypeSystem.MethodSignature; using MetadataRecord = Internal.Metadata.NativeFormat.Writer.MetadataRecord; +using MetadataWriter = Internal.Metadata.NativeFormat.Writer.MetadataWriter; using TypeReference = Internal.Metadata.NativeFormat.Writer.TypeReference; +using Field = Internal.Metadata.NativeFormat.Writer.Field; using TypeSpecification = Internal.Metadata.NativeFormat.Writer.TypeSpecification; using ConstantStringValue = Internal.Metadata.NativeFormat.Writer.ConstantStringValue; using TypeInstantiationSignature = Internal.Metadata.NativeFormat.Writer.TypeInstantiationSignature; @@ -43,6 +49,8 @@ public abstract class MetadataManager : ICompilationRootProvider private List> _fieldMappings; private List> _methodMappings; private List _stackTraceMappings; + protected readonly string _metadataLogFile; + protected readonly StackTraceEmissionPolicy _stackTraceEmissionPolicy; protected readonly CompilerTypeSystemContext _typeSystemContext; protected readonly MetadataBlockingPolicy _blockingPolicy; @@ -75,7 +83,8 @@ private readonly SortedSet _typeGVMEntries internal NativeLayoutInfoNode NativeLayoutInfo { get; private set; } public MetadataManager(CompilerTypeSystemContext typeSystemContext, MetadataBlockingPolicy blockingPolicy, - ManifestResourceBlockingPolicy resourceBlockingPolicy, DynamicInvokeThunkGenerationPolicy dynamicInvokeThunkGenerationPolicy, + ManifestResourceBlockingPolicy resourceBlockingPolicy, string logFile, StackTraceEmissionPolicy stackTracePolicy, + DynamicInvokeThunkGenerationPolicy dynamicInvokeThunkGenerationPolicy, MetadataManagerOptions options) { _typeSystemContext = typeSystemContext; @@ -83,6 +92,8 @@ public MetadataManager(CompilerTypeSystemContext typeSystemContext, MetadataBloc _resourceBlockingPolicy = resourceBlockingPolicy; _dynamicInvokeThunkGenerationPolicy = dynamicInvokeThunkGenerationPolicy; _options = options; + _metadataLogFile = logFile; + _stackTraceEmissionPolicy = stackTracePolicy; } public bool IsDataDehydrated => (_options & MetadataManagerOptions.DehydrateData) != 0; @@ -561,6 +572,12 @@ public virtual void GetDependenciesForOverridingMethod(ref CombinedDependencyLis { } + /// + /// Gets a list of fields that got "compiled" and are eligible for a runtime mapping. + /// + /// + protected abstract IEnumerable GetFieldsWithRuntimeMapping(); + /// /// This method is an extension point that can provide additional metadata-based dependencies to generated method bodies. /// @@ -601,18 +618,28 @@ public bool HasReflectionInvokeStubForInvokableMethod(MethodDesc method) /// Given that a method is invokable, if it is inserted into the reflection invoke table /// will it use a method token to be referenced, or not? /// - public abstract bool WillUseMetadataTokenToReferenceMethod(MethodDesc method); + public bool WillUseMetadataTokenToReferenceMethod(MethodDesc method) + { + return (GetMetadataCategory(method) & MetadataCategory.Description) != 0; + } /// /// Given that a method is invokable, if it is inserted into the reflection invoke table /// will it use a field token to be referenced, or not? /// - public abstract bool WillUseMetadataTokenToReferenceField(FieldDesc field); + public bool WillUseMetadataTokenToReferenceField(FieldDesc field) + { + return (GetMetadataCategory(field) & MetadataCategory.Description) != 0; + } /// /// Gets a stub that can be used to reflection-invoke a method with a given signature. /// - public abstract MethodDesc GetReflectionInvokeStub(MethodDesc method); + public MethodDesc GetReflectionInvokeStub(MethodDesc method) + { + return _typeSystemContext.GetDynamicInvokeThunk(method.Signature, + !method.Signature.IsStatic && method.OwningType.IsValueType); + } protected void EnsureMetadataGenerated(NodeFactory factory) { @@ -635,6 +662,168 @@ protected abstract void ComputeMetadata(NodeFactory factory, out List> fieldMappings, out List stackTraceMapping); + protected void ComputeMetadata( + TPolicy policy, + NodeFactory factory, + out byte[] metadataBlob, + out List> typeMappings, + out List> methodMappings, + out List> fieldMappings, + out List stackTraceMapping) where TPolicy : struct, IMetadataPolicy + { + var transformed = MetadataTransform.Run(policy, GetCompilationModulesWithMetadata()); + MetadataTransform transform = transformed.Transform; + + // Generate metadata blob + var writer = new MetadataWriter(); + writer.ScopeDefinitions.AddRange(transformed.Scopes); + + // Generate entries in the blob for methods that will be necessary for stack trace purposes. + var stackTraceRecords = new List(); + foreach (var methodBody in GetCompiledMethodBodies()) + { + MethodDesc method = methodBody.Method; + + MethodDesc typicalMethod = method.GetTypicalMethodDefinition(); + + // 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) && + (GetMetadataCategory(method) & MetadataCategory.RuntimeMapping) != 0) + continue; + + MethodStackTraceVisibilityFlags stackVisibility = _stackTraceEmissionPolicy.GetMethodVisibility(method); + bool isHidden = (stackVisibility & MethodStackTraceVisibilityFlags.IsHidden) != 0; + + if ((stackVisibility & MethodStackTraceVisibilityFlags.HasMetadata) != 0) + { + StackTraceRecordData record = CreateStackTraceRecord(transform, method, isHidden); + + stackTraceRecords.Add(record); + + writer.AdditionalRootRecords.Add(record.OwningType); + writer.AdditionalRootRecords.Add(record.MethodName); + writer.AdditionalRootRecords.Add(record.MethodSignature); + writer.AdditionalRootRecords.Add(record.MethodInstantiationArgumentCollection); + } + else if (isHidden) + { + stackTraceRecords.Add(new StackTraceRecordData(method, null, null, null, null, isHidden)); + } + } + + var ms = new MemoryStream(); + + // .NET metadata is UTF-16 and UTF-16 contains code points that don't translate to UTF-8. + var noThrowUtf8Encoding = new UTF8Encoding(false, false); + + using (var logWriter = _metadataLogFile != null ? new StreamWriter(File.Open(_metadataLogFile, FileMode.Create, FileAccess.Write, FileShare.Read), noThrowUtf8Encoding) : null) + { + writer.LogWriter = logWriter; + writer.Write(ms); + } + + metadataBlob = ms.ToArray(); + + const int MaxAllowedMetadataOffset = 0xFFFFFF; + if (metadataBlob.Length > MaxAllowedMetadataOffset) + { + // Offset portion of metadata handles is limited to 16 MB. + throw new InvalidOperationException($"Metadata blob exceeded the addressing range (allowed: {MaxAllowedMetadataOffset}, actual: {metadataBlob.Length})"); + } + + typeMappings = new List>(); + methodMappings = new List>(); + fieldMappings = new List>(); + stackTraceMapping = new List(); + + // Generate type definition mappings + foreach (var type in factory.MetadataManager.GetTypesWithEETypes()) + { + MetadataType definition = type.IsTypeDefinition ? type as MetadataType : null; + if (definition == null) + continue; + + MetadataRecord record = transformed.GetTransformedTypeDefinition(definition); + + // Reflection requires that we maintain type identity. Even if we only generated a TypeReference record, + // if there is an MethodTable for it, we also need a mapping table entry for it. + record ??= transformed.GetTransformedTypeReference(definition); + + if (record != null) + typeMappings.Add(new MetadataMapping(definition, writer.GetRecordHandle(record))); + } + + foreach (var method in GetReflectableMethods()) + { + if (method.IsGenericMethodDefinition || method.OwningType.IsGenericDefinition) + { + // Generic definitions don't have runtime artifacts we would need to map to. + continue; + } + + if (method.GetCanonMethodTarget(CanonicalFormKind.Specific) != method) + { + // Methods that are not in their canonical form are not interesting + continue; + } + + 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) + methodMappings.Add(new MetadataMapping(method, writer.GetRecordHandle(record))); + } + + HashSet canonicalFields = new HashSet(); + foreach (var field in GetFieldsWithRuntimeMapping()) + { + FieldDesc fieldToAdd = field; + TypeDesc canonOwningType = field.OwningType.ConvertToCanonForm(CanonicalFormKind.Specific); + if (canonOwningType.IsCanonicalSubtype(CanonicalFormKind.Any)) + { + FieldDesc canonField = _typeSystemContext.GetFieldForInstantiatedType(field.GetTypicalFieldDefinition(), (InstantiatedType)canonOwningType); + + // If we already added a canonically equivalent field, skip this one. + if (!canonicalFields.Add(canonField)) + continue; + + fieldToAdd = canonField; + } + + Field record = transformed.GetTransformedFieldDefinition(fieldToAdd.GetTypicalFieldDefinition()); + if (record != null) + fieldMappings.Add(new MetadataMapping(fieldToAdd, writer.GetRecordHandle(record))); + } + + // Generate stack trace metadata mapping + foreach (var stackTraceRecord in stackTraceRecords) + { + if (stackTraceRecord.OwningType != null) + { + StackTraceMapping mapping = new StackTraceMapping( + stackTraceRecord.Method, + writer.GetRecordHandle(stackTraceRecord.OwningType), + writer.GetRecordHandle(stackTraceRecord.MethodSignature), + writer.GetRecordHandle(stackTraceRecord.MethodName), + stackTraceRecord.MethodInstantiationArgumentCollection != null ? writer.GetRecordHandle(stackTraceRecord.MethodInstantiationArgumentCollection) : 0, + stackTraceRecord.IsHidden); + stackTraceMapping.Add(mapping); + } + else + { + Debug.Assert(stackTraceRecord.IsHidden); + stackTraceMapping.Add(new StackTraceMapping(stackTraceRecord.Method, 0, 0, 0, 0, stackTraceRecord.IsHidden)); + } + } + } + protected StackTraceRecordData CreateStackTraceRecord(Metadata.MetadataTransform transform, MethodDesc method, bool isHidden) { // In the metadata, we only represent the generic definition diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/UsageBasedMetadataManager.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/UsageBasedMetadataManager.cs index 1f798698ba151..2b823f4246510 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/UsageBasedMetadataManager.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/UsageBasedMetadataManager.cs @@ -34,7 +34,7 @@ namespace ILCompiler /// 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. /// - public sealed class UsageBasedMetadataManager : GeneratingMetadataManager + public sealed class UsageBasedMetadataManager : MetadataManager { private readonly CompilationModuleGroup _compilationModuleGroup; diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/ILCompiler.Compiler.csproj b/src/coreclr/tools/aot/ILCompiler.Compiler/ILCompiler.Compiler.csproj index 1137962a79322..6a35c1cc5c906 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/ILCompiler.Compiler.csproj +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/ILCompiler.Compiler.csproj @@ -455,7 +455,6 @@ -