diff --git a/src/coreclr/inc/readytorun.h b/src/coreclr/inc/readytorun.h index 7956fa7b77d0cb..14b3d5e6217776 100644 --- a/src/coreclr/inc/readytorun.h +++ b/src/coreclr/inc/readytorun.h @@ -52,6 +52,7 @@ // R2R 17 is not backward compatible with 16.x or earlier. // R2R Version 17.1 adds the READYTORUN_FLAG_PLATFORM_NATIVE_IMAGE flag to specify that the R2R image pointed to by OwnerCompositeExecutable is in the platform native format. // R2R Version 18 updates fields layout algorithm +// R2R Version 18.1 adds the ExternalTypeMaps, ProxyTypeMaps, TypeMapAssemblyTargets sections struct READYTORUN_CORE_HEADER { @@ -117,6 +118,9 @@ enum class ReadyToRunSectionType : uint32_t MethodIsGenericMap = 121, // Added in V9.0 EnclosingTypeMap = 122, // Added in V9.0 TypeGenericInfoMap = 123, // Added in V9.0 + ExternalTypeMaps = 124, // Added in V18.1 + ProxyTypeMaps = 125, // Added in V18.1 + TypeMapAssemblyTargets = 126, // Added in V18.1 // If you add a new section consider whether it is a breaking or non-breaking change. // Usually it is non-breaking, but if it is preferable to have older runtimes fail diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/InteropServices/TypeMapLazyDictionary.NativeAot.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/InteropServices/TypeMapLazyDictionary.NativeAot.cs index 9784f5d0a5b306..7f41a30cb81095 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/InteropServices/TypeMapLazyDictionary.NativeAot.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/InteropServices/TypeMapLazyDictionary.NativeAot.cs @@ -23,7 +23,7 @@ public static IReadOnlyDictionary CreateExternalTypeMap(RuntimeTyp RuntimeTypeHandle typeMapGroupHandle = typeMapGroup.TypeHandle; foreach (TypeManagerHandle module in RuntimeAugments.GetLoadedModules()) { - if (!TryGetNativeReaderForBlob(module, ReflectionMapBlob.ExternalTypeMap, out NativeReader externalTypeMapReader)) + if (!TryGetNativeReaderForBlob(module, ReadyToRunSectionType.ExternalTypeMaps, out NativeReader externalTypeMapReader)) { continue; } @@ -66,7 +66,7 @@ public static IReadOnlyDictionary CreateProxyTypeMap(RuntimeType typ RuntimeTypeHandle typeMapGroupHandle = typeMapGroup.TypeHandle; foreach (TypeManagerHandle module in RuntimeAugments.GetLoadedModules()) { - if (!TryGetNativeReaderForBlob(module, ReflectionMapBlob.ProxyTypeMap, out NativeReader externalTypeMapReader)) + if (!TryGetNativeReaderForBlob(module, ReadyToRunSectionType.ProxyTypeMaps, out NativeReader externalTypeMapReader)) { continue; } @@ -104,14 +104,13 @@ public static IReadOnlyDictionary CreateProxyTypeMap(RuntimeType typ throw ReflectionCoreExecution.ExecutionEnvironment.CreateMissingMetadataException(typeMapGroup); } - private static unsafe bool TryGetNativeReaderForBlob(TypeManagerHandle module, ReflectionMapBlob blob, out NativeReader reader) + private static unsafe bool TryGetNativeReaderForBlob(TypeManagerHandle module, ReadyToRunSectionType sectionType, out NativeReader reader) { - byte* pBlob; - uint cbBlob; + byte* pBlob = (byte*)RuntimeImports.RhGetModuleSection(module, sectionType, out int length); - if (RuntimeImports.RhFindBlob(module, (uint)blob, &pBlob, &cbBlob)) + if (pBlob != null) { - reader = new NativeReader(pBlob, cbBlob); + reader = new NativeReader(pBlob, (uint)length); return true; } diff --git a/src/coreclr/tools/Common/Compiler/DependencyAnalysis/SortableDependencyNode.cs b/src/coreclr/tools/Common/Compiler/DependencyAnalysis/SortableDependencyNode.cs index b515c093ba5240..525fdca00dc444 100644 --- a/src/coreclr/tools/Common/Compiler/DependencyAnalysis/SortableDependencyNode.cs +++ b/src/coreclr/tools/Common/Compiler/DependencyAnalysis/SortableDependencyNode.cs @@ -61,7 +61,7 @@ protected internal enum ObjectNodeOrder // Win32ResourcesNode, CorHeaderNode, - ReadyToRunHeaderNode, + GlobalHeaderNode, ReadyToRunAssemblyHeaderNode, ImportSectionsTableNode, ImportSectionNode, diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ExternalTypeMapObjectNode.cs b/src/coreclr/tools/Common/Compiler/ExternalTypeMapObjectNode.cs similarity index 86% rename from src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ExternalTypeMapObjectNode.cs rename to src/coreclr/tools/Common/Compiler/ExternalTypeMapObjectNode.cs index 44c6bf0a1977e0..96b421624262be 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ExternalTypeMapObjectNode.cs +++ b/src/coreclr/tools/Common/Compiler/ExternalTypeMapObjectNode.cs @@ -12,7 +12,7 @@ namespace ILCompiler.DependencyAnalysis { - internal sealed class ExternalTypeMapObjectNode(ExternalReferencesTableNode externalReferences) : ObjectNode, ISymbolDefinitionNode + public sealed class ExternalTypeMapObjectNode(TypeMapManager manager, INativeFormatTypeReferenceProvider externalReferences) : ObjectNode, ISymbolDefinitionNode { public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) { @@ -26,7 +26,7 @@ public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) Section hashTableSection = writer.NewSection(); hashTableSection.Place(typeMapGroupHashTable); - foreach (IExternalTypeMapNode externalTypeMap in factory.TypeMapManager.GetExternalTypeMaps()) + foreach (IExternalTypeMapNode externalTypeMap in manager.GetExternalTypeMaps()) { typeMapGroupHashTable.Append((uint)externalTypeMap.TypeMapGroup.GetHashCode(), externalTypeMap.CreateTypeMap(factory, writer, hashTableSection, externalReferences)); } @@ -42,7 +42,7 @@ public void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) public int Offset => 0; public override bool IsShareable => false; - public override ObjectNodeSection GetSection(NodeFactory factory) => externalReferences.GetSection(factory); + public override ObjectNodeSection GetSection(NodeFactory factory) => ObjectNodeSection.ReadOnlyDataSection; protected internal override int Phase => (int)ObjectNodePhase.Ordered; public override int ClassCode => (int)ObjectNodeOrder.ExternalTypeMapObjectNode; diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/IExternalTypeMapNode.cs b/src/coreclr/tools/Common/Compiler/IExternalTypeMapNode.cs similarity index 75% rename from src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/IExternalTypeMapNode.cs rename to src/coreclr/tools/Common/Compiler/IExternalTypeMapNode.cs index 32e059da731210..e4e571f4da091e 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/IExternalTypeMapNode.cs +++ b/src/coreclr/tools/Common/Compiler/IExternalTypeMapNode.cs @@ -10,12 +10,13 @@ namespace ILCompiler.DependencyAnalysis { - public interface IExternalTypeMapNode : IDependencyNode, ISortableNode + internal interface IExternalTypeMapNode : IDependencyNode, ISortableNode { TypeDesc TypeMapGroup { get; } - Vertex CreateTypeMap(NodeFactory factory, NativeWriter writer, Section section, ExternalReferencesTableNode externalReferences); - + Vertex CreateTypeMap(NodeFactory factory, NativeWriter writer, Section section, INativeFormatTypeReferenceProvider externalReferences); +#if !READYTORUN IExternalTypeMapNode ToAnalysisBasedNode(NodeFactory factory); +#endif } } diff --git a/src/coreclr/tools/Common/Compiler/INativeFormatTypeReferenceProvider.cs b/src/coreclr/tools/Common/Compiler/INativeFormatTypeReferenceProvider.cs new file mode 100644 index 00000000000000..99a95a4fec8770 --- /dev/null +++ b/src/coreclr/tools/Common/Compiler/INativeFormatTypeReferenceProvider.cs @@ -0,0 +1,18 @@ +// 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.Collections.Generic; +using System.Text; +using ILCompiler.DependencyAnalysisFramework; +using Internal.NativeFormat; +using Internal.TypeSystem; + +namespace ILCompiler.DependencyAnalysis +{ + public interface INativeFormatTypeReferenceProvider + { + internal Vertex EncodeReferenceToType(NativeWriter writer, TypeDesc type); + internal Vertex EncodeReferenceToMethod(NativeWriter writer, MethodDesc method); + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/IProxyTypeMapNode.cs b/src/coreclr/tools/Common/Compiler/IProxyTypeMapNode.cs similarity index 74% rename from src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/IProxyTypeMapNode.cs rename to src/coreclr/tools/Common/Compiler/IProxyTypeMapNode.cs index 331ec8357b6f71..13961085edd497 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/IProxyTypeMapNode.cs +++ b/src/coreclr/tools/Common/Compiler/IProxyTypeMapNode.cs @@ -10,12 +10,13 @@ namespace ILCompiler.DependencyAnalysis { - public interface IProxyTypeMapNode : IDependencyNode, ISortableNode + internal interface IProxyTypeMapNode : IDependencyNode, ISortableNode { TypeDesc TypeMapGroup { get; } - Vertex CreateTypeMap(NodeFactory factory, NativeWriter writer, Section section, ExternalReferencesTableNode externalReferences); - - IProxyTypeMapNode ToAnalysisBasedNode(NodeFactory factor); + Vertex CreateTypeMap(NodeFactory factory, NativeWriter writer, Section section, INativeFormatTypeReferenceProvider externalReferences); +#if !READYTORUN + IProxyTypeMapNode ToAnalysisBasedNode(NodeFactory factory); +#endif } } diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ProxyTypeMapObjectNode.cs b/src/coreclr/tools/Common/Compiler/ProxyTypeMapObjectNode.cs similarity index 87% rename from src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ProxyTypeMapObjectNode.cs rename to src/coreclr/tools/Common/Compiler/ProxyTypeMapObjectNode.cs index c5f55d565c1498..765c606ac13059 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ProxyTypeMapObjectNode.cs +++ b/src/coreclr/tools/Common/Compiler/ProxyTypeMapObjectNode.cs @@ -13,7 +13,7 @@ namespace ILCompiler.DependencyAnalysis { - internal sealed class ProxyTypeMapObjectNode(ExternalReferencesTableNode externalReferences) : ObjectNode, ISymbolDefinitionNode + internal sealed class ProxyTypeMapObjectNode(TypeMapManager manager, INativeFormatTypeReferenceProvider externalReferences) : ObjectNode, ISymbolDefinitionNode { public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) { @@ -27,7 +27,7 @@ public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) Section hashTableSection = writer.NewSection(); hashTableSection.Place(typeMapGroupHashTable); - foreach (IProxyTypeMapNode proxyTypeMap in factory.TypeMapManager.GetProxyTypeMaps()) + foreach (IProxyTypeMapNode proxyTypeMap in manager.GetProxyTypeMaps()) { TypeDesc typeMapGroup = proxyTypeMap.TypeMapGroup; typeMapGroupHashTable.Append((uint)typeMapGroup.GetHashCode(), proxyTypeMap.CreateTypeMap(factory, writer, hashTableSection, externalReferences)); @@ -44,7 +44,7 @@ public void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) public int Offset => 0; public override bool IsShareable => false; - public override ObjectNodeSection GetSection(NodeFactory factory) => externalReferences.GetSection(factory); + public override ObjectNodeSection GetSection(NodeFactory factory) => ObjectNodeSection.ReadOnlyDataSection; protected internal override int Phase => (int)ObjectNodePhase.Ordered; public override int ClassCode => (int)ObjectNodeOrder.ProxyTypeMapObjectNode; diff --git a/src/coreclr/tools/Common/Compiler/TypeMapManager.cs b/src/coreclr/tools/Common/Compiler/TypeMapManager.cs new file mode 100644 index 00000000000000..e6469dad59d471 --- /dev/null +++ b/src/coreclr/tools/Common/Compiler/TypeMapManager.cs @@ -0,0 +1,49 @@ +// 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.Collections; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Diagnostics; +using System.Reflection.Metadata; +using ILCompiler.DependencyAnalysis; +#if READYTORUN +using ILCompiler.DependencyAnalysis.ReadyToRun; +#endif +using ILCompiler.DependencyAnalysisFramework; +using Internal.Runtime; +using Internal.TypeSystem; + +namespace ILCompiler +{ + + /// + /// This class is responsible for managing emitted data for type maps. + /// + public abstract class TypeMapManager : ICompilationRootProvider + { + public virtual void AttachToDependencyGraph(DependencyAnalyzerBase graph) + { + } + + internal abstract IEnumerable GetExternalTypeMaps(); + + internal abstract IEnumerable GetProxyTypeMaps(); + + public abstract void AddCompilationRoots(IRootingServiceProvider rootProvider); + + protected abstract bool IsEmpty { get; } + + public virtual void AddToReadyToRunHeader(ReadyToRunHeaderNode header, NodeFactory nodeFactory, INativeFormatTypeReferenceProvider commonFixupsTableNode) + { + if (IsEmpty) + { + return; // No type maps to emit + } + + header.Add(ReadyToRunSectionType.ExternalTypeMaps, new ExternalTypeMapObjectNode(this, commonFixupsTableNode)); + header.Add(ReadyToRunSectionType.ProxyTypeMaps, new ProxyTypeMapObjectNode(this, commonFixupsTableNode)); + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/TypeMapMetadata.cs b/src/coreclr/tools/Common/Compiler/TypeMapMetadata.cs similarity index 91% rename from src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/TypeMapMetadata.cs rename to src/coreclr/tools/Common/Compiler/TypeMapMetadata.cs index 88290a47d718cc..c74903c38d6946 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/TypeMapMetadata.cs +++ b/src/coreclr/tools/Common/Compiler/TypeMapMetadata.cs @@ -10,14 +10,47 @@ using Internal.IL.Stubs; using Internal.TypeSystem; using Internal.TypeSystem.Ecma; -using static ILCompiler.TypeMapManager; -using static ILCompiler.UsageBasedTypeMapManager; namespace ILCompiler { public sealed class TypeMapMetadata { - internal sealed class Map + private enum TypeMapAttributeKind + { + None, + TypeMapAssemblyTarget, + TypeMap, + TypeMapAssociation + } + + private static TypeMapAttributeKind LookupTypeMapType(TypeDesc attrType) + { + var typeDef = attrType.GetTypeDefinition() as MetadataType; + if (typeDef != null && typeDef.Namespace.SequenceEqual("System.Runtime.InteropServices"u8)) + { + if (typeDef.Name.SequenceEqual("TypeMapAssemblyTargetAttribute`1"u8)) + return TypeMapAttributeKind.TypeMapAssemblyTarget; + else if (typeDef.Name.SequenceEqual("TypeMapAttribute`1"u8)) + return TypeMapAttributeKind.TypeMap; + else if (typeDef.Name.SequenceEqual("TypeMapAssociationAttribute`1"u8)) + return TypeMapAttributeKind.TypeMapAssociation; + } + return TypeMapAttributeKind.None; + } + + internal interface IExternalTypeMap + { + IReadOnlyDictionary TypeMap { get; } + MethodDesc ThrowingMethodStub { get; } + } + + internal interface IProxyTypeMap + { + IReadOnlyDictionary TypeMap { get; } + MethodDesc ThrowingMethodStub { get; } + } + + internal sealed class Map : IExternalTypeMap, IProxyTypeMap { private sealed class ThrowingMethodStub : ILStubMethod { @@ -36,7 +69,11 @@ public ThrowingMethodStub(TypeDesc owningType, TypeDesc typeMapGroup, bool exter public override ReadOnlySpan Name => _name; public override MethodIL EmitIL() { +#if READYTORUN + throw new UnreachableException(); +#else return TypeSystemThrowingILEmitter.EmitIL(this, Exception); +#endif } protected override int CompareToImpl(MethodDesc other, TypeSystemComparer comparer) @@ -156,24 +193,6 @@ public void MergePendingMap(Map pendingMap) _targetModules.AddRange(pendingMap._targetModules); } - public IExternalTypeMapNode GetExternalTypeMapNode() - { - if (_externalTypeMapExceptionStub is not null) - { - return new InvalidExternalTypeMapNode(TypeMapGroup, _externalTypeMapExceptionStub); - } - return new ExternalTypeMapNode(TypeMapGroup, _externalTypeMap); - } - - public IProxyTypeMapNode GetProxyTypeMapNode() - { - if (_associatedTypeMapExceptionStub is not null) - { - return new InvalidProxyTypeMapNode(TypeMapGroup, _associatedTypeMapExceptionStub); - } - return new ProxyTypeMapNode(TypeMapGroup, _associatedTypeMap); - } - public void AddTargetModule(ModuleDesc targetModule) { _targetModules.Add(targetModule); @@ -182,7 +201,15 @@ public void AddTargetModule(ModuleDesc targetModule) /// /// The modules targeted with TypeMapAssemblyTarget attributes for this type map group. This is only populated when TypeMapMetadata is created with TypeMapAssemblyTargetsMode.Record. When TypeMapMetadata is created with TypeMapAssemblyTargetsMode.Traverse, this will be empty as the target assemblies will be traversed to include their type maps instead of just being recorded as targets. /// - public IEnumerable TargetModules => _targetModules; + public IReadOnlyList TargetModules => _targetModules; + + IReadOnlyDictionary IExternalTypeMap.TypeMap => _externalTypeMap; + + MethodDesc IExternalTypeMap.ThrowingMethodStub => _externalTypeMapExceptionStub; + + IReadOnlyDictionary IProxyTypeMap.TypeMap => _associatedTypeMap; + + MethodDesc IProxyTypeMap.ThrowingMethodStub => _associatedTypeMapExceptionStub; } public static readonly TypeMapMetadata Empty = new TypeMapMetadata(new Dictionary(), "No type maps"); @@ -195,8 +222,6 @@ private TypeMapMetadata(IReadOnlyDictionary states, string diagno DiagnosticName = diagnosticName; } - internal Map this[TypeDesc typeMapGroup] => _states[typeMapGroup]; - public bool IsEmpty => _states.Count == 0; internal IEnumerable> Maps => _states; diff --git a/src/coreclr/tools/Common/Internal/Runtime/MetadataBlob.cs b/src/coreclr/tools/Common/Internal/Runtime/MetadataBlob.cs index 9a53c2e9dd3b8d..1803e980e3a411 100644 --- a/src/coreclr/tools/Common/Internal/Runtime/MetadataBlob.cs +++ b/src/coreclr/tools/Common/Internal/Runtime/MetadataBlob.cs @@ -44,9 +44,5 @@ internal enum ReflectionMapBlob StaticsInfoHashtable = 34, GenericMethodsHashtable = 35, ExactMethodInstantiationsHashtable = 36, - - // Type map blobs: - ExternalTypeMap = 40, - ProxyTypeMap = 41, } } diff --git a/src/coreclr/tools/Common/Internal/Runtime/ModuleHeaders.cs b/src/coreclr/tools/Common/Internal/Runtime/ModuleHeaders.cs index 230a9389ac4a2c..671f2e7cfe9609 100644 --- a/src/coreclr/tools/Common/Internal/Runtime/ModuleHeaders.cs +++ b/src/coreclr/tools/Common/Internal/Runtime/ModuleHeaders.cs @@ -80,6 +80,11 @@ enum ReadyToRunSectionType EnclosingTypeMap = 122, // Added in V9.0 TypeGenericInfoMap = 123, // Added in V9.0 + // Shared ReadyToRun sections + ExternalTypeMaps = 124, // Added to CoreCLR in V18.1 + ProxyTypeMaps = 125, // Added to CoreCLR in V18.1 + TypeMapAssemblyTargets = 126, // Added in V18.1 + // // NativeAOT ReadyToRun sections // diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/AnalyzedExternalTypeMapNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/AnalyzedExternalTypeMapNode.cs index a0c397a6180afe..0d8535488348f3 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/AnalyzedExternalTypeMapNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/AnalyzedExternalTypeMapNode.cs @@ -10,24 +10,24 @@ namespace ILCompiler.DependencyAnalysis { - internal sealed class AnalyzedExternalTypeMapNode(TypeDesc typeMapGroup, IReadOnlyDictionary entries) : DependencyNodeCore, IExternalTypeMapNode + internal sealed class AnalyzedExternalTypeMapNode(TypeDesc typeMapGroup, IReadOnlyDictionary entries) : SortableDependencyNode, IExternalTypeMapNode { public TypeDesc TypeMapGroup => typeMapGroup; - public Vertex CreateTypeMap(NodeFactory factory, NativeWriter writer, Section section, ExternalReferencesTableNode externalReferences) + public Vertex CreateTypeMap(NodeFactory factory, NativeWriter writer, Section section, INativeFormatTypeReferenceProvider externalReferences) { VertexHashtable typeMapHashTable = new(); foreach ((string key, TypeDesc type) in entries) { Vertex keyVertex = writer.GetStringConstant(key); - Vertex valueVertex = writer.GetUnsignedConstant(externalReferences.GetIndex(factory.MetadataTypeSymbol(type))); + Vertex valueVertex = externalReferences.EncodeReferenceToType(writer, type); Vertex entry = writer.GetTuple(keyVertex, valueVertex); typeMapHashTable.Append((uint)TypeHashingAlgorithms.ComputeNameHashCode(key), section.Place(entry)); } Vertex typeMapStateVertex = writer.GetUnsignedConstant(1); // Valid type map state - Vertex typeMapGroupVertex = writer.GetUnsignedConstant(externalReferences.GetIndex(factory.NecessaryTypeSymbol(TypeMapGroup))); + Vertex typeMapGroupVertex = externalReferences.EncodeReferenceToType(writer, TypeMapGroup); Vertex tuple = writer.GetTuple(typeMapGroupVertex, typeMapStateVertex, typeMapHashTable); return section.Place(tuple); } @@ -51,9 +51,9 @@ public override IEnumerable GetStaticDependencies(NodeFacto public override bool HasConditionalStaticDependencies => false; public override bool StaticDependenciesAreComputed => true; - public int ClassCode => -874354558; + public override int ClassCode => -874354558; - public int CompareToImpl(ISortableNode other, CompilerComparer comparer) + public override int CompareToImpl(ISortableNode other, CompilerComparer comparer) { AnalyzedExternalTypeMapNode otherEntry = (AnalyzedExternalTypeMapNode)other; return comparer.Compare(TypeMapGroup, otherEntry.TypeMapGroup); diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/AnalyzedProxyTypeMapNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/AnalyzedProxyTypeMapNode.cs index 1689bc15716e59..416497cbf46bd2 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/AnalyzedProxyTypeMapNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/AnalyzedProxyTypeMapNode.cs @@ -11,23 +11,23 @@ namespace ILCompiler.DependencyAnalysis { - internal sealed class AnalyzedProxyTypeMapNode(TypeDesc typeMapGroup, IReadOnlyDictionary entries) : DependencyNodeCore, IProxyTypeMapNode + internal sealed class AnalyzedProxyTypeMapNode(TypeDesc typeMapGroup, IReadOnlyDictionary entries) : SortableDependencyNode, IProxyTypeMapNode { public TypeDesc TypeMapGroup => typeMapGroup; - public Vertex CreateTypeMap(NodeFactory factory, NativeWriter writer, Section section, ExternalReferencesTableNode externalReferences) + public Vertex CreateTypeMap(NodeFactory factory, NativeWriter writer, Section section, INativeFormatTypeReferenceProvider externalReferences) { VertexHashtable typeMapHashTable = new(); foreach ((TypeDesc key, TypeDesc type) in entries) { - Vertex keyVertex = writer.GetUnsignedConstant(externalReferences.GetIndex(factory.MaximallyConstructableType(key))); - Vertex valueVertex = writer.GetUnsignedConstant(externalReferences.GetIndex(factory.MetadataTypeSymbol(type))); + Vertex keyVertex = externalReferences.EncodeReferenceToType(writer, key); + Vertex valueVertex = externalReferences.EncodeReferenceToType(writer, type); Vertex entry = writer.GetTuple(keyVertex, valueVertex); typeMapHashTable.Append((uint)key.GetHashCode(), section.Place(entry)); } Vertex typeMapStateVertex = writer.GetUnsignedConstant(1); // Valid type map state - Vertex typeMapGroupVertex = writer.GetUnsignedConstant(externalReferences.GetIndex(factory.NecessaryTypeSymbol(TypeMapGroup))); + Vertex typeMapGroupVertex = externalReferences.EncodeReferenceToType(writer, TypeMapGroup); Vertex tuple = writer.GetTuple(typeMapGroupVertex, typeMapStateVertex, typeMapHashTable); return section.Place(tuple); } @@ -52,9 +52,9 @@ public override IEnumerable GetStaticDependencies(NodeFacto public override bool StaticDependenciesAreComputed => true; - public int ClassCode => 171742984; + public override int ClassCode => 171742984; - public int CompareToImpl(ISortableNode other, CompilerComparer comparer) + public override int CompareToImpl(ISortableNode other, CompilerComparer comparer) { AnalyzedProxyTypeMapNode otherEntry = (AnalyzedProxyTypeMapNode)other; return comparer.Compare(TypeMapGroup, otherEntry.TypeMapGroup); diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ExternalTypeMapNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ExternalTypeMapNode.cs index 2d23d46991f93f..bc3baed8f8f440 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ExternalTypeMapNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ExternalTypeMapNode.cs @@ -11,7 +11,7 @@ namespace ILCompiler.DependencyAnalysis { - internal sealed class ExternalTypeMapNode : DependencyNodeCore, IExternalTypeMapNode + internal sealed class ExternalTypeMapNode : SortableDependencyNode, IExternalTypeMapNode { private readonly IEnumerable> _mapEntries; @@ -63,9 +63,9 @@ public override IEnumerable GetStaticDependencies(NodeFacto public override IEnumerable SearchDynamicDependencies(List> markedNodes, int firstNode, NodeFactory context) => Array.Empty(); protected override string GetName(NodeFactory context) => $"External type map: {TypeMapGroup}"; - public int ClassCode => -785190502; + public override int ClassCode => -785190502; - public int CompareToImpl(ISortableNode other, CompilerComparer comparer) + public override int CompareToImpl(ISortableNode other, CompilerComparer comparer) { ExternalTypeMapNode otherEntry = (ExternalTypeMapNode)other; return comparer.Compare(TypeMapGroup, otherEntry.TypeMapGroup); @@ -87,20 +87,20 @@ public int CompareToImpl(ISortableNode other, CompilerComparer comparer) } } - public Vertex CreateTypeMap(NodeFactory factory, NativeWriter writer, Section section, ExternalReferencesTableNode externalReferences) + public Vertex CreateTypeMap(NodeFactory factory, NativeWriter writer, Section section, INativeFormatTypeReferenceProvider externalReferences) { VertexHashtable typeMapHashTable = new(); foreach ((string key, IEETypeNode valueNode) in GetMarkedEntries(factory)) { Vertex keyVertex = writer.GetStringConstant(key); - Vertex valueVertex = writer.GetUnsignedConstant(externalReferences.GetIndex(valueNode)); + Vertex valueVertex = externalReferences.EncodeReferenceToType(writer, valueNode.Type); Vertex entry = writer.GetTuple(keyVertex, valueVertex); typeMapHashTable.Append((uint)TypeHashingAlgorithms.ComputeNameHashCode(key), section.Place(entry)); } Vertex typeMapStateVertex = writer.GetUnsignedConstant(1); // Valid type map state - Vertex typeMapGroupVertex = writer.GetUnsignedConstant(externalReferences.GetIndex(factory.NecessaryTypeSymbol(TypeMapGroup))); + Vertex typeMapGroupVertex = externalReferences.EncodeReferenceToType(writer, TypeMapGroup); Vertex tuple = writer.GetTuple(typeMapGroupVertex, typeMapStateVertex, typeMapHashTable); return section.Place(tuple); } diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/InvalidExternalTypeMapNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/InvalidExternalTypeMapNode.cs index 4674e713ec7a38..657382714ac04c 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/InvalidExternalTypeMapNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/InvalidExternalTypeMapNode.cs @@ -10,7 +10,7 @@ namespace ILCompiler.DependencyAnalysis { - internal sealed class InvalidExternalTypeMapNode(TypeDesc typeMapGroup, MethodDesc throwingMethodStub) : DependencyNodeCore, ISortableNode, IExternalTypeMapNode + internal sealed class InvalidExternalTypeMapNode(TypeDesc typeMapGroup, MethodDesc throwingMethodStub) : SortableDependencyNode, IExternalTypeMapNode { public override bool InterestingForDynamicDependencyAnalysis => false; @@ -34,15 +34,15 @@ public override IEnumerable GetStaticDependencies(NodeFacto public TypeDesc TypeMapGroup { get; } = typeMapGroup; public MethodDesc ThrowingMethodStub { get; } = throwingMethodStub; - public int ClassCode => 36910224; + public override int ClassCode => 36910224; - public int CompareToImpl(ISortableNode other, CompilerComparer comparer) => comparer.Compare(TypeMapGroup, ((InvalidExternalTypeMapNode)other).TypeMapGroup); + public override int CompareToImpl(ISortableNode other, CompilerComparer comparer) => comparer.Compare(TypeMapGroup, ((InvalidExternalTypeMapNode)other).TypeMapGroup); - public Vertex CreateTypeMap(NodeFactory factory, NativeWriter writer, Section section, ExternalReferencesTableNode externalReferences) + public Vertex CreateTypeMap(NodeFactory factory, NativeWriter writer, Section section, INativeFormatTypeReferenceProvider externalReferences) { Vertex typeMapStateVertex = writer.GetUnsignedConstant(0); // Invalid type map state - Vertex typeMapGroupVertex = writer.GetUnsignedConstant(externalReferences.GetIndex(factory.NecessaryTypeSymbol(TypeMapGroup))); - Vertex throwingMethodStubVertex = writer.GetUnsignedConstant(externalReferences.GetIndex(factory.MethodEntrypoint(ThrowingMethodStub))); + Vertex typeMapGroupVertex = externalReferences.EncodeReferenceToType(writer, TypeMapGroup); + Vertex throwingMethodStubVertex = externalReferences.EncodeReferenceToMethod(writer, ThrowingMethodStub); Vertex tuple = writer.GetTuple(typeMapGroupVertex, typeMapStateVertex, throwingMethodStubVertex); return section.Place(tuple); } diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/InvalidProxyTypeMapNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/InvalidProxyTypeMapNode.cs index a929fbb205ac8e..db0fb70e2a8cbe 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/InvalidProxyTypeMapNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/InvalidProxyTypeMapNode.cs @@ -10,7 +10,7 @@ namespace ILCompiler.DependencyAnalysis { - internal sealed class InvalidProxyTypeMapNode(TypeDesc typeMapGroup, MethodDesc throwingMethodStub) : DependencyNodeCore, IProxyTypeMapNode + internal sealed class InvalidProxyTypeMapNode(TypeDesc typeMapGroup, MethodDesc throwingMethodStub) : SortableDependencyNode, IProxyTypeMapNode { public TypeDesc TypeMapGroup { get; } = typeMapGroup; public MethodDesc ThrowingMethodStub { get; } = throwingMethodStub; @@ -34,14 +34,14 @@ public override IEnumerable GetStaticDependencies(NodeFacto public override IEnumerable SearchDynamicDependencies(List> markedNodes, int firstNode, NodeFactory context) => Array.Empty(); protected override string GetName(NodeFactory context) => $"Invalid proxy type map: {TypeMapGroup}"; - public int ClassCode => 36910224; + public override int ClassCode => 36910224; - public int CompareToImpl(ISortableNode other, CompilerComparer comparer) => comparer.Compare(TypeMapGroup, ((InvalidProxyTypeMapNode)other).TypeMapGroup); - public Vertex CreateTypeMap(NodeFactory factory, NativeWriter writer, Section section, ExternalReferencesTableNode externalReferences) + public override int CompareToImpl(ISortableNode other, CompilerComparer comparer) => comparer.Compare(TypeMapGroup, ((InvalidProxyTypeMapNode)other).TypeMapGroup); + public Vertex CreateTypeMap(NodeFactory factory, NativeWriter writer, Section section, INativeFormatTypeReferenceProvider externalReferences) { Vertex typeMapStateVertex = writer.GetUnsignedConstant(0); // Invalid type map state - Vertex typeMapGroupVertex = writer.GetUnsignedConstant(externalReferences.GetIndex(factory.NecessaryTypeSymbol(TypeMapGroup))); - Vertex throwingMethodStubVertex = writer.GetUnsignedConstant(externalReferences.GetIndex(factory.MethodEntrypoint(ThrowingMethodStub))); + Vertex typeMapGroupVertex = externalReferences.EncodeReferenceToType(writer, TypeMapGroup); + Vertex throwingMethodStubVertex = externalReferences.EncodeReferenceToMethod(writer, ThrowingMethodStub); Vertex tuple = writer.GetTuple(typeMapGroupVertex, typeMapStateVertex, throwingMethodStubVertex); return section.Place(tuple); } 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 820cf40f5813be..e228370554ef06 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/NodeFactory.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/NodeFactory.cs @@ -10,6 +10,7 @@ using ILCompiler.DependencyAnalysisFramework; using Internal.IL; +using Internal.NativeFormat; using Internal.Runtime; using Internal.Text; using Internal.TypeSystem; @@ -1641,13 +1642,21 @@ public virtual void AttachToDependencyGraph(DependencyAnalyzerBase var commonFixupsTableNode = new ExternalReferencesTableNode("CommonFixupsTable", this); InteropStubManager.AddToReadyToRunHeader(ReadyToRunHeader, this, commonFixupsTableNode); - TypeMapManager.AddToReadyToRunHeader(ReadyToRunHeader, this, commonFixupsTableNode); + TypeMapManager.AddToReadyToRunHeader(ReadyToRunHeader, this, new ExternalReferencesTableIndex(commonFixupsTableNode, this)); MetadataManager.AddToReadyToRunHeader(ReadyToRunHeader, this, commonFixupsTableNode); MetadataManager.AttachToDependencyGraph(graph); TypeMapManager.AttachToDependencyGraph(graph); ReadyToRunHeader.Add(MetadataManager.BlobIdToReadyToRunSection(ReflectionMapBlob.CommonFixupsTable), commonFixupsTableNode); } + private sealed class ExternalReferencesTableIndex(ExternalReferencesTableNode table, NodeFactory factory) : INativeFormatTypeReferenceProvider + { + public Vertex EncodeReferenceToMethod(NativeWriter writer, MethodDesc method) + => writer.GetUnsignedConstant(table.GetIndex(factory.MethodEntrypoint(method))); + public Vertex EncodeReferenceToType(NativeWriter writer, TypeDesc type) + => writer.GetUnsignedConstant(table.GetIndex(factory.NecessaryTypeSymbol(type))); + } + protected struct MethodKey : IEquatable { public readonly MethodDesc Method; diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ProxyTypeMapNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ProxyTypeMapNode.cs index 0053d0b88a3664..ebcadfb3ee9b34 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ProxyTypeMapNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ProxyTypeMapNode.cs @@ -13,7 +13,7 @@ namespace ILCompiler.DependencyAnalysis { - internal sealed class ProxyTypeMapNode : DependencyNodeCore, IProxyTypeMapNode + internal sealed class ProxyTypeMapNode : SortableDependencyNode, IProxyTypeMapNode { private readonly IEnumerable> _mapEntries; @@ -34,9 +34,9 @@ public ProxyTypeMapNode(TypeDesc typeMapGroup, IEnumerable true; - public int ClassCode => 779513676; + public override int ClassCode => 779513676; - public int CompareToImpl(ISortableNode other, CompilerComparer comparer) => comparer.Compare(TypeMapGroup, ((ProxyTypeMapNode)other).TypeMapGroup); + public override int CompareToImpl(ISortableNode other, CompilerComparer comparer) => comparer.Compare(TypeMapGroup, ((ProxyTypeMapNode)other).TypeMapGroup); public override IEnumerable GetConditionalStaticDependencies(NodeFactory context) { @@ -78,20 +78,20 @@ public override IEnumerable GetConditionalStaticDep } } - public Vertex CreateTypeMap(NodeFactory factory, NativeWriter writer, Section section, ExternalReferencesTableNode externalReferences) + public Vertex CreateTypeMap(NodeFactory factory, NativeWriter writer, Section section, INativeFormatTypeReferenceProvider externalReferences) { VertexHashtable typeMapHashTable = new VertexHashtable(); foreach ((IEETypeNode keyNode, IEETypeNode valueNode) in GetMarkedEntries(factory)) { - Vertex keyVertex = writer.GetUnsignedConstant(externalReferences.GetIndex(keyNode)); - Vertex valueVertex = writer.GetUnsignedConstant(externalReferences.GetIndex(valueNode)); + Vertex keyVertex = externalReferences.EncodeReferenceToType(writer, keyNode.Type); + Vertex valueVertex = externalReferences.EncodeReferenceToType(writer, valueNode.Type); Vertex entry = writer.GetTuple(keyVertex, valueVertex); typeMapHashTable.Append((uint)keyNode.Type.GetHashCode(), section.Place(entry)); } Vertex typeMapStateVertex = writer.GetUnsignedConstant(1); // Valid type map state - Vertex typeMapGroupVertex = writer.GetUnsignedConstant(externalReferences.GetIndex(factory.NecessaryTypeSymbol(TypeMapGroup))); + Vertex typeMapGroupVertex = externalReferences.EncodeReferenceToType(writer, TypeMapGroup); Vertex tuple = writer.GetTuple(typeMapGroupVertex, typeMapStateVertex, typeMapHashTable); return section.Place(tuple); } diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/TypeMapManager.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/TypeMapManager.cs deleted file mode 100644 index c59b9e854b3e94..00000000000000 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/TypeMapManager.cs +++ /dev/null @@ -1,71 +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.Collections; -using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.Diagnostics; -using System.Reflection.Metadata; -using ILCompiler.DependencyAnalysis; -using ILCompiler.DependencyAnalysisFramework; -using Internal.IL; -using Internal.IL.Stubs; -using Internal.TypeSystem; -using ReflectionMapBlob = Internal.Runtime.ReflectionMapBlob; - -namespace ILCompiler -{ - - /// - /// This class is responsible for managing emitted data for type maps. - /// - public abstract class TypeMapManager : ICompilationRootProvider - { - public enum TypeMapAttributeKind - { - None, - TypeMapAssemblyTarget, - TypeMap, - TypeMapAssociation - } - - public static TypeMapAttributeKind LookupTypeMapType(TypeDesc attrType) - { - var typeDef = attrType.GetTypeDefinition() as MetadataType; - if (typeDef != null && typeDef.Namespace.SequenceEqual("System.Runtime.InteropServices"u8)) - { - if (typeDef.Name.SequenceEqual("TypeMapAssemblyTargetAttribute`1"u8)) - return TypeMapAttributeKind.TypeMapAssemblyTarget; - else if (typeDef.Name.SequenceEqual("TypeMapAttribute`1"u8)) - return TypeMapAttributeKind.TypeMap; - else if (typeDef.Name.SequenceEqual("TypeMapAssociationAttribute`1"u8)) - return TypeMapAttributeKind.TypeMapAssociation; - } - return TypeMapAttributeKind.None; - } - - public virtual void AttachToDependencyGraph(DependencyAnalyzerBase graph) - { - } - - internal abstract IEnumerable GetExternalTypeMaps(); - - internal abstract IEnumerable GetProxyTypeMaps(); - - public abstract void AddCompilationRoots(IRootingServiceProvider rootProvider); - - protected abstract bool IsEmpty { get; } - - public void AddToReadyToRunHeader(ReadyToRunHeaderNode header, NodeFactory nodeFactory, ExternalReferencesTableNode commonFixupsTableNode) - { - if (IsEmpty) - { - return; // No type maps to emit - } - - header.Add(MetadataManager.BlobIdToReadyToRunSection(ReflectionMapBlob.ExternalTypeMap), new ExternalTypeMapObjectNode(commonFixupsTableNode)); - header.Add(MetadataManager.BlobIdToReadyToRunSection(ReflectionMapBlob.ProxyTypeMap), new ProxyTypeMapObjectNode(commonFixupsTableNode)); - } - } -} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/UsageBasedTypeMapManager.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/UsageBasedTypeMapManager.cs index 54be1c847d7e6f..cdc4dd3f8b8494 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/UsageBasedTypeMapManager.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/UsageBasedTypeMapManager.cs @@ -10,6 +10,7 @@ using Internal.IL.Stubs; using Internal.TypeSystem; using Internal.TypeSystem.Ecma; +using static ILCompiler.TypeMapMetadata; namespace ILCompiler { @@ -30,8 +31,8 @@ public override IEnumerable GetConditionalStaticDep List entries = []; foreach ((TypeDesc typeMapGroup, TypeMapMetadata.Map typeMap) in typeMapState.Maps) { - entries.Add(new CombinedDependencyListEntry(typeMap.GetExternalTypeMapNode(), context.ExternalTypeMapRequest(typeMapGroup), "ExternalTypeMap")); - entries.Add(new CombinedDependencyListEntry(typeMap.GetProxyTypeMapNode(), context.ProxyTypeMapRequest(typeMapGroup), "ProxyTypeMap")); + entries.Add(new CombinedDependencyListEntry(GetExternalTypeMapNode(typeMapGroup, typeMap), context.ExternalTypeMapRequest(typeMapGroup), "ExternalTypeMap")); + entries.Add(new CombinedDependencyListEntry(GetProxyTypeMapNode(typeMapGroup, typeMap), context.ProxyTypeMapRequest(typeMapGroup), "ProxyTypeMap")); } return entries; @@ -40,6 +41,20 @@ public override IEnumerable GetConditionalStaticDep public override IEnumerable GetStaticDependencies(NodeFactory context) => Array.Empty(); public override IEnumerable SearchDynamicDependencies(List> markedNodes, int firstNode, NodeFactory context) => Array.Empty(); protected override string GetName(NodeFactory context) => $"Type maps root node: {typeMapState.DiagnosticName}"; + + private static IExternalTypeMapNode GetExternalTypeMapNode(TypeDesc typeMapGroup, IExternalTypeMap map) + { + return map.ThrowingMethodStub is not null + ? new InvalidExternalTypeMapNode(typeMapGroup, map.ThrowingMethodStub) + : new ExternalTypeMapNode(typeMapGroup, map.TypeMap); + } + + private static IProxyTypeMapNode GetProxyTypeMapNode(TypeDesc typeMapGroup, IProxyTypeMap map) + { + return map.ThrowingMethodStub is not null + ? new InvalidProxyTypeMapNode(typeMapGroup, map.ThrowingMethodStub) + : new ProxyTypeMapNode(typeMapGroup, map.TypeMap); + } } private readonly HashSet _requestedExternalTypeMaps = []; diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/ILCompiler.Compiler.csproj b/src/coreclr/tools/aot/ILCompiler.Compiler/ILCompiler.Compiler.csproj index 8d138b97ce980e..b5c28d50c941cb 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/ILCompiler.Compiler.csproj +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/ILCompiler.Compiler.csproj @@ -83,6 +83,15 @@ TypeSystem\Common\VersionResilientHashCode.cs + + + + + + + + + Interop\IL\MarshalHelpers.cs @@ -442,12 +451,8 @@ - - - - @@ -663,7 +668,6 @@ - @@ -686,7 +690,6 @@ - diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ImportReferenceProvider.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ImportReferenceProvider.cs new file mode 100644 index 00000000000000..1fdb5029a286d5 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ImportReferenceProvider.cs @@ -0,0 +1,50 @@ +// 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 ILCompiler.DependencyAnalysis.ReadyToRun; +using Internal.TypeSystem; +using Internal.NativeFormat; +using Internal.ReadyToRunConstants; +using System.Diagnostics; +using Internal.TypeSystem.Ecma; + +namespace ILCompiler.DependencyAnalysis +{ + public sealed class ImportReferenceProvider : INativeFormatTypeReferenceProvider + { + private ReadyToRunSymbolNodeFactory _symbolNodeFactory; + + public Import GetImportToType(TypeDesc type) + { + return _symbolNodeFactory.CreateReadyToRunHelper(ReadyToRunHelperId.TypeHandle, type); + } + + public Import GetImportToModule(ModuleDesc module) + { + Debug.Assert(module is IEcmaModule); + return _symbolNodeFactory.ModuleLookup((IEcmaModule)module); + } + + public void Initialize(ReadyToRunSymbolNodeFactory symbolNodeFactory) + { + _symbolNodeFactory = symbolNodeFactory; + } + + internal Vertex EncodeReferenceToModule(NativeWriter writer, ModuleDesc module) + { + Import typeImport = GetImportToModule(module); + return writer.GetTuple(writer.GetUnsignedConstant((uint)typeImport.Table.IndexFromBeginningOfArray), writer.GetUnsignedConstant((uint)typeImport.IndexFromBeginningOfArray)); + } + + internal Vertex EncodeReferenceToType(NativeWriter writer, TypeDesc type) + { + Import typeImport = GetImportToType(type); + return writer.GetTuple(writer.GetUnsignedConstant((uint)typeImport.Table.IndexFromBeginningOfArray), writer.GetUnsignedConstant((uint)typeImport.IndexFromBeginningOfArray)); + } + + Vertex INativeFormatTypeReferenceProvider.EncodeReferenceToMethod(NativeWriter writer, MethodDesc method) => throw new NotImplementedException(); + Vertex INativeFormatTypeReferenceProvider.EncodeReferenceToType(NativeWriter writer, TypeDesc type) => EncodeReferenceToType(writer, type); + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/ManifestMetadataTableNode.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/ManifestMetadataTableNode.cs index 26ac359f10f6ec..3ecf636ee1bee3 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/ManifestMetadataTableNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/ManifestMetadataTableNode.cs @@ -253,7 +253,6 @@ private int ModuleToIndexInternal(IEcmaModule module) } else { - Debug.Assert(_nodeFactory.CompilationModuleGroup.CrossModuleInlineableModule(emodule)); _manifestAssemblyMvids.Add(default(Guid)); } } diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/HeaderNode.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/ReadyToRunHeaderNode.cs similarity index 94% rename from src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/HeaderNode.cs rename to src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/ReadyToRunHeaderNode.cs index 702e69f6192c42..3f556dda8aedc9 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/HeaderNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/ReadyToRunHeaderNode.cs @@ -79,7 +79,7 @@ public sealed override void AppendMangledName(NameMangler nameMangler, Utf8Strin } } - public abstract class HeaderNode : ObjectNode, ISymbolDefinitionNode + public abstract class ReadyToRunHeaderNode : ObjectNode, ISymbolDefinitionNode { struct HeaderItem { @@ -99,7 +99,7 @@ public HeaderItem(ReadyToRunSectionType id, DependencyNodeCore node private readonly ReadyToRunFlags _flags; private readonly Task<(bool canSkipValidation, string[] reasons)> _shouldAddSkipTypeValidationFlag; - public HeaderNode(ReadyToRunFlags flags, EcmaModule moduleToCheckForSkipTypeValidation) + public ReadyToRunHeaderNode(ReadyToRunFlags flags, EcmaModule moduleToCheckForSkipTypeValidation) { if (moduleToCheckForSkipTypeValidation != null) @@ -118,6 +118,12 @@ public void Add(ReadyToRunSectionType id, DependencyNodeCore node, _items.Add(new HeaderItem(id, node, startSymbol)); } + public void Add(ReadyToRunSectionType id, T node) + where T : DependencyNodeCore, ISymbolNode + { + Add(id, node, node); + } + public int Offset => 0; public override bool IsShareable => false; @@ -200,7 +206,7 @@ public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) protected internal override int Phase => (int)ObjectNodePhase.Ordered; } - public class GlobalHeaderNode : HeaderNode + public class GlobalHeaderNode : ReadyToRunHeaderNode { public GlobalHeaderNode(ReadyToRunFlags flags, EcmaModule moduleToCheckForSkipValidation) : base(flags, moduleToCheckForSkipValidation) @@ -223,10 +229,10 @@ protected override void EmitHeaderPrefix(ref ObjectDataBuilder builder) builder.EmitShort((short)(ReadyToRunHeaderConstants.CurrentMinorVersion)); } - public override int ClassCode => (int)ObjectNodeOrder.ReadyToRunHeaderNode; + public override int ClassCode => (int)ObjectNodeOrder.GlobalHeaderNode; } - public class AssemblyHeaderNode : HeaderNode + public class AssemblyHeaderNode : ReadyToRunHeaderNode { private readonly int _index; diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/ReadyToRunModuleSignature.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/ReadyToRunModuleSignature.cs new file mode 100644 index 00000000000000..13a23c4f6daf31 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/ReadyToRunModuleSignature.cs @@ -0,0 +1,59 @@ +// 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.Diagnostics; + +using Internal.Text; +using Internal.ReadyToRunConstants; +using Internal.TypeSystem; +using Internal.TypeSystem.Ecma; + +namespace ILCompiler.DependencyAnalysis.ReadyToRun +{ + public class ReadyToRunModuleSignature(IEcmaModule module) : Signature + { + public override int ClassCode => 208107955; + + public IEcmaModule Module { get; } = module; + + public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) + { + ObjectDataSignatureBuilder builder = new(factory, relocsOnly); + builder.AddSymbol(this); + + if (relocsOnly) + { + return builder.ToObjectData(); + } + + // Don't use EmitFixup as that validates cross-module references. + // For just the module helper fixup, we aren't bound by that restriction + // as the module helper's result is not affected by changes in the target module. + if (Module == factory.SignatureContext) + { + builder.EmitByte((byte)ReadyToRunFixupKind.Helper); + } + else + { + builder.EmitByte((byte)(ReadyToRunFixupKind.Helper | ReadyToRunFixupKind.ModuleOverride)); + builder.EmitUInt((uint)factory.ManifestMetadataTable.ModuleToIndex(Module)); + } + builder.EmitUInt((uint)ReadyToRunHelper.Module); + + return builder.ToObjectData(); + } + + public override void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) + { + sb.Append(nameMangler.CompilationUnitPrefix); + sb.Append("ReadyToRunModule_"u8); + sb.Append(Module.Assembly.Name); + } + + public override int CompareToImpl(ISortableNode other, CompilerComparer comparer) + { + return Module.CompareTo(((ReadyToRunModuleSignature)other).Module); + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRunCodegenNodeFactory.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRunCodegenNodeFactory.cs index a2a846481e3091..c92ebe5da1de15 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRunCodegenNodeFactory.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRunCodegenNodeFactory.cs @@ -20,6 +20,7 @@ using Internal.CorConstants; using Internal.ReadyToRunConstants; using ILCompiler.ReadyToRun.TypeSystem; +using ILCompiler.ReadyToRun; namespace ILCompiler.DependencyAnalysis { @@ -99,7 +100,7 @@ public void GenerateHotColdMap(DependencyAnalyzerBase dependencyGra if (HotColdMap == null) { HotColdMap = new HotColdMapNode(); - Header.Add(Internal.Runtime.ReadyToRunSectionType.HotColdMap, HotColdMap, HotColdMap); + Header.Add(Internal.Runtime.ReadyToRunSectionType.HotColdMap, HotColdMap); dependencyGraph.AddRoot(HotColdMap, "HotColdMap is generated because there is cold code"); } } @@ -396,6 +397,7 @@ private void CreateNodeCaches() public InstrumentationDataTableNode InstrumentationDataTable; public InliningInfoNode CrossModuleInlningInfo; + public ImportReferenceProvider ImportReferenceProvider; public Import ModuleImport; @@ -701,37 +703,37 @@ public void AttachToDependencyGraph(DependencyAnalyzerBase graph, I graph.ComputingDependencyPhaseChange += Graph_ComputingDependencyPhaseChange; var compilerIdentifierNode = new CompilerIdentifierNode(Target); - Header.Add(Internal.Runtime.ReadyToRunSectionType.CompilerIdentifier, compilerIdentifierNode, compilerIdentifierNode); + Header.Add(Internal.Runtime.ReadyToRunSectionType.CompilerIdentifier, compilerIdentifierNode); RuntimeFunctionsTable = new RuntimeFunctionsTableNode(this); - Header.Add(Internal.Runtime.ReadyToRunSectionType.RuntimeFunctions, RuntimeFunctionsTable, RuntimeFunctionsTable); + Header.Add(Internal.Runtime.ReadyToRunSectionType.RuntimeFunctions, RuntimeFunctionsTable); RuntimeFunctionsGCInfo = new RuntimeFunctionsGCInfoNode(); graph.AddRoot(RuntimeFunctionsGCInfo, "GC info is always generated"); DelayLoadMethodCallThunks = new SymbolNodeRange("DelayLoadMethodCallThunkNodeRange"); graph.AddRoot(DelayLoadMethodCallThunks, "DelayLoadMethodCallThunks header entry is always generated"); - Header.Add(Internal.Runtime.ReadyToRunSectionType.DelayLoadMethodCallThunks, DelayLoadMethodCallThunks, DelayLoadMethodCallThunks); + Header.Add(Internal.Runtime.ReadyToRunSectionType.DelayLoadMethodCallThunks, DelayLoadMethodCallThunks); ExceptionInfoLookupTableNode exceptionInfoLookupTableNode = new ExceptionInfoLookupTableNode(this); - Header.Add(Internal.Runtime.ReadyToRunSectionType.ExceptionInfo, exceptionInfoLookupTableNode, exceptionInfoLookupTableNode); + Header.Add(Internal.Runtime.ReadyToRunSectionType.ExceptionInfo, exceptionInfoLookupTableNode); graph.AddRoot(exceptionInfoLookupTableNode, "ExceptionInfoLookupTable is always generated"); ManifestMetadataTable = new ManifestMetadataTableNode(this); - Header.Add(Internal.Runtime.ReadyToRunSectionType.ManifestMetadata, ManifestMetadataTable, ManifestMetadataTable); + Header.Add(Internal.Runtime.ReadyToRunSectionType.ManifestMetadata, ManifestMetadataTable); Resolver.SetModuleIndexLookup(ManifestMetadataTable.ModuleToIndex); ((ReadyToRunILProvider)ilProvider).InitManifestMutableModule(ManifestMetadataTable._mutableModule); Resolver.InitManifestMutableModule(ManifestMetadataTable._mutableModule); ManifestAssemblyMvidHeaderNode mvidTableNode = new ManifestAssemblyMvidHeaderNode(ManifestMetadataTable); - Header.Add(Internal.Runtime.ReadyToRunSectionType.ManifestAssemblyMvids, mvidTableNode, mvidTableNode); + Header.Add(Internal.Runtime.ReadyToRunSectionType.ManifestAssemblyMvids, mvidTableNode); AssemblyTableNode assemblyTable = null; if (CompilationModuleGroup.IsCompositeBuildMode) { assemblyTable = new AssemblyTableNode(); - Header.Add(Internal.Runtime.ReadyToRunSectionType.ComponentAssemblies, assemblyTable, assemblyTable); + Header.Add(Internal.Runtime.ReadyToRunSectionType.ComponentAssemblies, assemblyTable); } // Generate per assembly header tables @@ -739,7 +741,7 @@ public void AttachToDependencyGraph(DependencyAnalyzerBase graph, I foreach (EcmaModule inputModule in CompilationModuleGroup.CompilationModuleSet) { assemblyIndex++; - HeaderNode tableHeader = Header; + ReadyToRunHeaderNode tableHeader = Header; if (assemblyTable != null) { AssemblyHeaderNode perAssemblyHeader = new AssemblyHeaderNode(ReadyToRunFlags.READYTORUN_FLAG_Component, assemblyIndex); @@ -748,15 +750,15 @@ public void AttachToDependencyGraph(DependencyAnalyzerBase graph, I } MethodEntryPointTableNode methodEntryPointTable = new MethodEntryPointTableNode(inputModule); - tableHeader.Add(Internal.Runtime.ReadyToRunSectionType.MethodDefEntryPoints, methodEntryPointTable, methodEntryPointTable); + tableHeader.Add(Internal.Runtime.ReadyToRunSectionType.MethodDefEntryPoints, methodEntryPointTable); TypesTableNode typesTable = new TypesTableNode(inputModule); - tableHeader.Add(Internal.Runtime.ReadyToRunSectionType.AvailableTypes, typesTable, typesTable); + tableHeader.Add(Internal.Runtime.ReadyToRunSectionType.AvailableTypes, typesTable); if (CompilationModuleGroup.IsCompositeBuildMode) { InliningInfoNode inliningInfoTable = new InliningInfoNode(inputModule, InliningInfoNode.InfoType.InliningInfo2); - tableHeader.Add(Internal.Runtime.ReadyToRunSectionType.InliningInfo2, inliningInfoTable, inliningInfoTable); + tableHeader.Add(Internal.Runtime.ReadyToRunSectionType.InliningInfo2, inliningInfoTable); } // Core library attributes are checked FAR more often than other dlls @@ -766,41 +768,48 @@ public void AttachToDependencyGraph(DependencyAnalyzerBase graph, I if (inputModule == TypeSystemContext.SystemModule) { AttributePresenceFilterNode attributePresenceTable = new AttributePresenceFilterNode(inputModule); - tableHeader.Add(Internal.Runtime.ReadyToRunSectionType.AttributePresence, attributePresenceTable, attributePresenceTable); + tableHeader.Add(Internal.Runtime.ReadyToRunSectionType.AttributePresence, attributePresenceTable); } if (EnclosingTypeMapNode.IsSupported(inputModule.MetadataReader)) { var node = new EnclosingTypeMapNode(inputModule); - tableHeader.Add(Internal.Runtime.ReadyToRunSectionType.EnclosingTypeMap, node, node); + tableHeader.Add(Internal.Runtime.ReadyToRunSectionType.EnclosingTypeMap, node); } if (TypeGenericInfoMapNode.IsSupported(inputModule.MetadataReader)) { var node = new TypeGenericInfoMapNode(inputModule); - tableHeader.Add(Internal.Runtime.ReadyToRunSectionType.TypeGenericInfoMap, node, node); + tableHeader.Add(Internal.Runtime.ReadyToRunSectionType.TypeGenericInfoMap, node); } if (MethodIsGenericMapNode.IsSupported(inputModule.MetadataReader)) { var node = new MethodIsGenericMapNode(inputModule); - tableHeader.Add(Internal.Runtime.ReadyToRunSectionType.MethodIsGenericMap, node, node); + tableHeader.Add(Internal.Runtime.ReadyToRunSectionType.MethodIsGenericMap, node); } + ImportReferenceProvider ??= new ImportReferenceProvider(); + + TypeMapMetadata metadata = TypeMapMetadata.CreateFromAssembly((EcmaAssembly)inputModule.Assembly, TypeSystemContext.SystemModule, TypeMapAssemblyTargetsMode.Record); + + ReadyToRunTypeMapManager typeMapManager = new(inputModule, metadata); + typeMapManager.AddToReadyToRunHeader(tableHeader, this, ImportReferenceProvider); + typeMapManager.AttachToDependencyGraph(graph); } InliningInfoNode crossModuleInliningInfoTable = new InliningInfoNode(null, CompilationModuleGroup.IsCompositeBuildMode ? InliningInfoNode.InfoType.CrossModuleInliningForCrossModuleDataOnly : InliningInfoNode.InfoType.CrossModuleAllMethods); - Header.Add(Internal.Runtime.ReadyToRunSectionType.CrossModuleInlineInfo, crossModuleInliningInfoTable, crossModuleInliningInfoTable); + Header.Add(Internal.Runtime.ReadyToRunSectionType.CrossModuleInlineInfo, crossModuleInliningInfoTable); this.CrossModuleInlningInfo = crossModuleInliningInfoTable; InstanceEntryPointTable = new InstanceEntryPointTableNode(this); - Header.Add(Internal.Runtime.ReadyToRunSectionType.InstanceMethodEntryPoints, InstanceEntryPointTable, InstanceEntryPointTable); + Header.Add(Internal.Runtime.ReadyToRunSectionType.InstanceMethodEntryPoints, InstanceEntryPointTable); ImportSectionsTable = new ImportSectionsTableNode(this); - Header.Add(Internal.Runtime.ReadyToRunSectionType.ImportSections, ImportSectionsTable, ImportSectionsTable); + Header.Add(Internal.Runtime.ReadyToRunSectionType.ImportSections, ImportSectionsTable); DebugInfoTable = new DebugInfoTableNode(); - Header.Add(Internal.Runtime.ReadyToRunSectionType.DebugInfo, DebugInfoTable, DebugInfoTable); + Header.Add(Internal.Runtime.ReadyToRunSectionType.DebugInfo, DebugInfoTable); EagerImports = new ImportSectionNode( "EagerImports", @@ -854,7 +863,7 @@ bool HasAnyProfileDataForInput() if (ProfileDataManager.SynthesizeRandomPgoData || HasAnyProfileDataForInput()) { InstrumentationDataTable = new InstrumentationDataTableNode(this, ProfileDataManager); - Header.Add(Internal.Runtime.ReadyToRunSectionType.PgoInstrumentationData, InstrumentationDataTable, InstrumentationDataTable); + Header.Add(Internal.Runtime.ReadyToRunSectionType.PgoInstrumentationData, InstrumentationDataTable); } } diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRunSymbolNodeFactory.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRunSymbolNodeFactory.cs index 981ae21d19507e..d846e4c36d84b8 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRunSymbolNodeFactory.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRunSymbolNodeFactory.cs @@ -51,21 +51,21 @@ public ReadyToRunSymbolNodeFactory(NodeFactory codegenNodeFactory, bool verifyTy private void CreateNodeCaches() { - _importStrings = new NodeCache(key => + _importStrings = new NodeCache(key => { return new StringImport(_codegenNodeFactory.StringImports, key); }); - _r2rHelpers = new NodeCache(CreateReadyToRunHelper); + _r2rHelpers = new NodeCache(CreateReadyToRunHelper); - _instructionSetSupportFixups = new NodeCache(key => + _instructionSetSupportFixups = new NodeCache(key => { return new PrecodeHelperImport( _codegenNodeFactory, new ReadyToRunInstructionSetSupportSignature(key)); }); - _fieldAddressCache = new NodeCache(key => + _fieldAddressCache = new NodeCache(key => { return new DelayLoadHelperImport( _codegenNodeFactory, @@ -75,7 +75,7 @@ private void CreateNodeCaches() ); }); - _fieldOffsetCache = new NodeCache(key => + _fieldOffsetCache = new NodeCache(key => { return new PrecodeHelperImport( _codegenNodeFactory, @@ -83,7 +83,7 @@ private void CreateNodeCaches() ); }); - _fieldBaseOffsetCache = new NodeCache(key => + _fieldBaseOffsetCache = new NodeCache(key => { return new PrecodeHelperImport( _codegenNodeFactory, @@ -91,7 +91,7 @@ private void CreateNodeCaches() ); }); - _rvaFieldAddressCache = new NodeCache(key => + _rvaFieldAddressCache = new NodeCache(key => { return new PrecodeHelperImport( _codegenNodeFactory, @@ -99,7 +99,7 @@ private void CreateNodeCaches() ); }); - _checkFieldOffsetCache = new NodeCache(key => + _checkFieldOffsetCache = new NodeCache(key => { return new PrecodeHelperImport( _codegenNodeFactory, @@ -107,7 +107,7 @@ private void CreateNodeCaches() ); }); - _interfaceDispatchCells = new NodeCache(cellKey => + _interfaceDispatchCells = new NodeCache(cellKey => { return new DelayLoadHelperMethodImport( _codegenNodeFactory, @@ -122,7 +122,7 @@ private void CreateNodeCaches() cellKey.CallingMethod); }); - _delegateCtors = new NodeCache(ctorKey => + _delegateCtors = new NodeCache(ctorKey => { IMethodNode targetMethodNode = _codegenNodeFactory.MethodEntrypoint( ctorKey.Method, @@ -137,7 +137,7 @@ private void CreateNodeCaches() new DelegateCtorSignature(ctorKey.Type, targetMethodNode, ctorKey.Method)); }); - _checkTypeLayoutCache = new NodeCache(key => + _checkTypeLayoutCache = new NodeCache(key => { return new PrecodeHelperImport( _codegenNodeFactory, @@ -145,14 +145,14 @@ private void CreateNodeCaches() ); }); - _virtualFunctionOverrideCache = new NodeCache(key => + _virtualFunctionOverrideCache = new NodeCache(key => { return new PrecodeHelperImport(_codegenNodeFactory, key); }); _ilBodyFixupsCache = new NodeCache(key => new PrecodeHelperImport(_codegenNodeFactory.ILBodyPrecodeImports, key)); - _genericLookupHelpers = new NodeCache(key => + _genericLookupHelpers = new NodeCache(key => { return new DelayLoadHelperImport( _codegenNodeFactory, @@ -167,7 +167,7 @@ private void CreateNodeCaches() key.MethodContext)); }); - _pInvokeTargetNodes = new NodeCache(key => + _pInvokeTargetNodes = new NodeCache(key => { return new PrecodeHelperImport( _codegenNodeFactory, @@ -184,6 +184,13 @@ private void CreateNodeCaches() _codegenNodeFactory.TypeSignature(ReadyToRunFixupKind.ContinuationLayout, key) ); }); + + _ecmaModuleFixupCache = new NodeCache(key => + { + return new PrecodeHelperImport( + _codegenNodeFactory, + new ReadyToRunModuleSignature(key)); + }); } private NodeCache _continuationTypeFixups; @@ -193,9 +200,9 @@ public ISymbolNode ContinuationTypeSymbol(AsyncContinuationType key) return _continuationTypeFixups.GetOrAdd(key); } - private NodeCache _importStrings; + private NodeCache _importStrings; - public ISymbolNode StringLiteral(ModuleToken moduleToken) + public Import StringLiteral(ModuleToken moduleToken) { return _importStrings.GetOrAdd(moduleToken); } @@ -227,9 +234,9 @@ public override int GetHashCode() } } - private NodeCache _r2rHelpers; + private NodeCache _r2rHelpers; - private ISymbolNode CreateReadyToRunHelper(ReadyToRunHelperKey key) + private Import CreateReadyToRunHelper(ReadyToRunHelperKey key) { switch (key.Id) { @@ -280,20 +287,20 @@ private ISymbolNode CreateReadyToRunHelper(ReadyToRunHelperKey key) } } - public ISymbolNode CreateReadyToRunHelper(ReadyToRunHelperId id, object target) + public Import CreateReadyToRunHelper(ReadyToRunHelperId id, object target) { return _r2rHelpers.GetOrAdd(new ReadyToRunHelperKey(id, target)); } - private NodeCache _instructionSetSupportFixups; + private NodeCache _instructionSetSupportFixups; - public ISymbolNode PerMethodInstructionSetSupportFixup(InstructionSetSupport instructionSetSupport) + public Import PerMethodInstructionSetSupportFixup(InstructionSetSupport instructionSetSupport) { string key = ReadyToRunInstructionSetSupportSignature.ToInstructionSetSupportString(instructionSetSupport); return _instructionSetSupportFixups.GetOrAdd(key); } - private ISymbolNode CreateNewHelper(TypeDesc type) + private Import CreateNewHelper(TypeDesc type) { return new DelayLoadHelperImport( _codegenNodeFactory, @@ -302,7 +309,7 @@ private ISymbolNode CreateNewHelper(TypeDesc type) new NewObjectFixupSignature(type)); } - private ISymbolNode CreateNewArrayHelper(ArrayType type) + private Import CreateNewArrayHelper(ArrayType type) { return new DelayLoadHelperImport( _codegenNodeFactory, @@ -311,7 +318,7 @@ private ISymbolNode CreateNewArrayHelper(ArrayType type) new NewArrayFixupSignature(type)); } - private ISymbolNode CreateGCStaticBaseHelper(TypeDesc type) + private Import CreateGCStaticBaseHelper(TypeDesc type) { return new DelayLoadHelperImport( _codegenNodeFactory, @@ -320,7 +327,7 @@ private ISymbolNode CreateGCStaticBaseHelper(TypeDesc type) _codegenNodeFactory.TypeSignature(ReadyToRunFixupKind.StaticBaseGC, type)); } - private ISymbolNode CreateNonGCStaticBaseHelper(TypeDesc type) + private Import CreateNonGCStaticBaseHelper(TypeDesc type) { return new DelayLoadHelperImport( _codegenNodeFactory, @@ -329,7 +336,7 @@ private ISymbolNode CreateNonGCStaticBaseHelper(TypeDesc type) _codegenNodeFactory.TypeSignature(ReadyToRunFixupKind.StaticBaseNonGC, type)); } - private ISymbolNode CreateThreadGcStaticBaseHelper(TypeDesc type) + private Import CreateThreadGcStaticBaseHelper(TypeDesc type) { return new DelayLoadHelperImport( _codegenNodeFactory, @@ -338,7 +345,7 @@ private ISymbolNode CreateThreadGcStaticBaseHelper(TypeDesc type) _codegenNodeFactory.TypeSignature(ReadyToRunFixupKind.ThreadStaticBaseGC, type)); } - private ISymbolNode CreateThreadNonGcStaticBaseHelper(TypeDesc type) + private Import CreateThreadNonGcStaticBaseHelper(TypeDesc type) { return new DelayLoadHelperImport( _codegenNodeFactory, @@ -347,7 +354,7 @@ private ISymbolNode CreateThreadNonGcStaticBaseHelper(TypeDesc type) _codegenNodeFactory.TypeSignature(ReadyToRunFixupKind.ThreadStaticBaseNonGC, type)); } - private ISymbolNode CreateIsInstanceOfHelper(TypeDesc type) + private Import CreateIsInstanceOfHelper(TypeDesc type) { return new DelayLoadHelperImport( _codegenNodeFactory, @@ -356,7 +363,7 @@ private ISymbolNode CreateIsInstanceOfHelper(TypeDesc type) _codegenNodeFactory.TypeSignature(ReadyToRunFixupKind.IsInstanceOf, type)); } - private ISymbolNode CreateCastClassHelper(TypeDesc type) + private Import CreateCastClassHelper(TypeDesc type) { return new DelayLoadHelperImport( _codegenNodeFactory, @@ -365,14 +372,14 @@ private ISymbolNode CreateCastClassHelper(TypeDesc type) _codegenNodeFactory.TypeSignature(ReadyToRunFixupKind.ChkCast, type)); } - private ISymbolNode CreateTypeHandleHelper(TypeDesc type) + private Import CreateTypeHandleHelper(TypeDesc type) { return new PrecodeHelperImport( _codegenNodeFactory, _codegenNodeFactory.TypeSignature(ReadyToRunFixupKind.TypeHandle, type)); } - private ISymbolNode CreateMethodHandleHelper(MethodWithToken method) + private Import CreateMethodHandleHelper(MethodWithToken method) { bool useInstantiatingStub = method.Method.GetCanonMethodTarget(CanonicalFormKind.Specific) != method.Method; @@ -384,14 +391,14 @@ private ISymbolNode CreateMethodHandleHelper(MethodWithToken method) isInstantiatingStub: useInstantiatingStub)); } - private ISymbolNode CreateFieldHandleHelper(FieldWithToken field) + private Import CreateFieldHandleHelper(FieldWithToken field) { return new PrecodeHelperImport( _codegenNodeFactory, new FieldFixupSignature(ReadyToRunFixupKind.FieldHandle, field, _codegenNodeFactory)); } - private ISymbolNode CreateCctorTrigger(TypeDesc type) + private Import CreateCctorTrigger(TypeDesc type) { return new DelayLoadHelperImport( _codegenNodeFactory, @@ -400,7 +407,7 @@ private ISymbolNode CreateCctorTrigger(TypeDesc type) _codegenNodeFactory.TypeSignature(ReadyToRunFixupKind.CctorTrigger, type)); } - private ISymbolNode CreateTypeDictionary(TypeDesc type) + private Import CreateTypeDictionary(TypeDesc type) { return new PrecodeHelperImport( _codegenNodeFactory, @@ -408,62 +415,62 @@ private ISymbolNode CreateTypeDictionary(TypeDesc type) ); } - private ISymbolNode CreateMethodDictionary(MethodWithToken method) + private Import CreateMethodDictionary(MethodWithToken method) { return new PrecodeHelperImport( _codegenNodeFactory, _codegenNodeFactory.MethodSignature( - ReadyToRunFixupKind.MethodDictionary, + ReadyToRunFixupKind.MethodDictionary, method, isInstantiatingStub: true)); } - private NodeCache _fieldAddressCache; + private NodeCache _fieldAddressCache; - public ISymbolNode FieldAddress(FieldWithToken fieldWithToken) + public Import FieldAddress(FieldWithToken fieldWithToken) { return _fieldAddressCache.GetOrAdd(fieldWithToken); } - private NodeCache _fieldOffsetCache; + private NodeCache _fieldOffsetCache; - public ISymbolNode FieldOffset(FieldWithToken fieldWithToken) + public Import FieldOffset(FieldWithToken fieldWithToken) { return _fieldOffsetCache.GetOrAdd(fieldWithToken); } - private NodeCache _checkFieldOffsetCache; + private NodeCache _checkFieldOffsetCache; - public ISymbolNode CheckFieldOffset(FieldWithToken fieldWithToken) + public Import CheckFieldOffset(FieldWithToken fieldWithToken) { return _checkFieldOffsetCache.GetOrAdd(fieldWithToken); } - private NodeCache _fieldBaseOffsetCache; + private NodeCache _fieldBaseOffsetCache; - public ISymbolNode FieldBaseOffset(TypeDesc typeDesc) + public Import FieldBaseOffset(TypeDesc typeDesc) { return _fieldBaseOffsetCache.GetOrAdd(typeDesc); } - private NodeCache _rvaFieldAddressCache; + private NodeCache _rvaFieldAddressCache; - public ISymbolNode RvaFieldAddress(FieldWithToken fieldWithToken) + public Import RvaFieldAddress(FieldWithToken fieldWithToken) { return _rvaFieldAddressCache.GetOrAdd(fieldWithToken); } - private NodeCache _interfaceDispatchCells = new NodeCache(); + private NodeCache _interfaceDispatchCells = new NodeCache(); - public ISymbolNode InterfaceDispatchCell(MethodWithToken method, MethodDesc callingMethod) + public Import InterfaceDispatchCell(MethodWithToken method, MethodDesc callingMethod) { MethodAndCallSite cellKey = new MethodAndCallSite(method, null); return _interfaceDispatchCells.GetOrAdd(cellKey); } - private NodeCache _delegateCtors = new NodeCache(); + private NodeCache _delegateCtors = new NodeCache(); - public ISymbolNode DelegateCtor(TypeDesc delegateType, MethodWithToken method) + public Import DelegateCtor(TypeDesc delegateType, MethodWithToken method) { TypeAndMethod ctorKey = new TypeAndMethod( delegateType, @@ -474,16 +481,16 @@ public ISymbolNode DelegateCtor(TypeDesc delegateType, MethodWithToken method) return _delegateCtors.GetOrAdd(ctorKey); } - private NodeCache _checkTypeLayoutCache; + private NodeCache _checkTypeLayoutCache; - public ISymbolNode CheckTypeLayout(TypeDesc type) + public Import CheckTypeLayout(TypeDesc type) { return _checkTypeLayoutCache.GetOrAdd(type); } - private NodeCache _virtualFunctionOverrideCache; + private NodeCache _virtualFunctionOverrideCache; - public ISymbolNode CheckVirtualFunctionOverride(MethodWithToken declMethod, TypeDesc implType, MethodWithToken implMethod) + public Import CheckVirtualFunctionOverride(MethodWithToken declMethod, TypeDesc implType, MethodWithToken implMethod) { return _virtualFunctionOverrideCache.GetOrAdd(_codegenNodeFactory.VirtualResolutionFixupSignature( _verifyTypeAndFieldLayout ? ReadyToRunFixupKind.Verify_VirtualFunctionOverride : ReadyToRunFixupKind.Check_VirtualFunctionOverride, @@ -498,6 +505,12 @@ public Import CheckILBodyFixupSignature(EcmaMethod method) method)); } + private NodeCache _ecmaModuleFixupCache; + public Import ModuleLookup(IEcmaModule module) + { + return _ecmaModuleFixupCache.GetOrAdd(module); + } + struct MethodAndCallSite : IEquatable { public readonly MethodWithToken Method; @@ -577,9 +590,9 @@ public override int GetHashCode() } } - private NodeCache _genericLookupHelpers; + private NodeCache _genericLookupHelpers; - public ISymbolNode GenericLookupHelper( + public Import GenericLookupHelper( CORINFO_RUNTIME_LOOKUP_KIND runtimeLookupKind, ReadyToRunHelperId helperId, object helperArgument, @@ -641,7 +654,7 @@ public ISymbolNode GenericLookupHelper( } } - private ISymbolNode GenericLookupTypeHelper( + private Import GenericLookupTypeHelper( CORINFO_RUNTIME_LOOKUP_KIND runtimeLookupKind, ReadyToRunFixupKind fixupKind, object helperArgument, @@ -665,7 +678,7 @@ private ISymbolNode GenericLookupTypeHelper( return _genericLookupHelpers.GetOrAdd(key); } - private ISymbolNode GenericLookupFieldHelper( + private Import GenericLookupFieldHelper( CORINFO_RUNTIME_LOOKUP_KIND runtimeLookupKind, ReadyToRunFixupKind fixupKind, FieldWithToken fieldArgument, @@ -675,7 +688,7 @@ private ISymbolNode GenericLookupFieldHelper( return _genericLookupHelpers.GetOrAdd(key); } - private ISymbolNode GenericLookupMethodHelper( + private Import GenericLookupMethodHelper( CORINFO_RUNTIME_LOOKUP_KIND runtimeLookupKind, ReadyToRunFixupKind fixupKind, MethodWithToken methodArgument, @@ -712,14 +725,14 @@ public override int GetHashCode() } } - private NodeCache _pInvokeTargetNodes = new NodeCache(); + private NodeCache _pInvokeTargetNodes = new NodeCache(); - public ISymbolNode GetIndirectPInvokeTargetNode(MethodWithToken methodWithToken) + public Import GetIndirectPInvokeTargetNode(MethodWithToken methodWithToken) { return _pInvokeTargetNodes.GetOrAdd(new PInvokeTargetKey(methodWithToken, isIndirect: true)); } - public ISymbolNode GetPInvokeTargetNode(MethodWithToken methodWithToken) + public Import GetPInvokeTargetNode(MethodWithToken methodWithToken) { return _pInvokeTargetNodes.GetOrAdd(new PInvokeTargetKey(methodWithToken, isIndirect: false)); } diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunCodegenCompilation.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunCodegenCompilation.cs index 82c54c7b8d9a95..53376c1ca7aed3 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunCodegenCompilation.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunCodegenCompilation.cs @@ -377,6 +377,8 @@ internal ReadyToRunCodegenCompilation( nodeFactory.InstrumentationDataTable.Initialize(SymbolNodeFactory); if (nodeFactory.CrossModuleInlningInfo != null) nodeFactory.CrossModuleInlningInfo.Initialize(SymbolNodeFactory); + if (nodeFactory.ImportReferenceProvider != null) + nodeFactory.ImportReferenceProvider.Initialize(SymbolNodeFactory); _inputFiles = inputFiles; _compositeRootPath = compositeRootPath; _printReproInstructions = printReproInstructions; @@ -531,7 +533,7 @@ private void RewriteComponentFile(string inputFile, string outputFile, string ow componentGraph.AddRoot(componentFactory.Win32ResourcesNode, "Win32 resources"); } componentGraph.ComputeMarkedNodes(); - componentFactory.Header.Add(Internal.Runtime.ReadyToRunSectionType.OwnerCompositeExecutable, ownerExecutableNode, ownerExecutableNode); + componentFactory.Header.Add(Internal.Runtime.ReadyToRunSectionType.OwnerCompositeExecutable, ownerExecutableNode); ReadyToRunObjectWriter.EmitObject( outputFile, componentModule: inputModule, diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunExternalTypeMapNode.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunExternalTypeMapNode.cs new file mode 100644 index 00000000000000..de8ddd46052c23 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunExternalTypeMapNode.cs @@ -0,0 +1,84 @@ +// 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.DependencyAnalysis; +using ILCompiler.DependencyAnalysisFramework; +using Internal.NativeFormat; +using Internal.TypeSystem; +using Internal.TypeSystem.Ecma; + +namespace ILCompiler.ReadyToRun +{ + internal class ReadyToRunExternalTypeMapNode(ModuleDesc triggeringModule, TypeDesc group, TypeMapMetadata.IExternalTypeMap map, ImportReferenceProvider importProvider) : SortableDependencyNode, IExternalTypeMapNode + { + public TypeDesc TypeMapGroup => group; + + public override int ClassCode => 565222977; + + public override bool InterestingForDynamicDependencyAnalysis => false; + + public override bool HasDynamicDependencies => false; + + public override bool HasConditionalStaticDependencies => false; + + public override bool StaticDependenciesAreComputed => true; + + private ModuleDesc TriggeringModule { get; } = triggeringModule; + + public override int CompareToImpl(ISortableNode other, CompilerComparer comparer) + { + ReadyToRunExternalTypeMapNode otherNode = (ReadyToRunExternalTypeMapNode)other; + int result = comparer.Compare(TypeMapGroup, otherNode.TypeMapGroup); + if (result != 0) + return result; + + return comparer.Compare(TriggeringModule, otherNode.TriggeringModule); + } + + public Vertex CreateTypeMap(NodeFactory factory, NativeWriter writer, Section section, INativeFormatTypeReferenceProvider externalReferences) + { + if (map.ThrowingMethodStub is not null) + { + // We don't write out the throwing method stub for R2R + // as emitting loose methods is not supported/very expensive. + // Instead, we defer to the runtime to generate the type map + // and throw on error cases. + return section.Place(writer.GetUnsignedConstant(0)); // Invalid type map state + } + + VertexHashtable typeMapHashTable = new(); + + Section typeMapEntriesSection = writer.NewSection(); + + foreach ((string key, (TypeDesc type, _)) in map.TypeMap) + { + Vertex keyVertex = writer.GetStringConstant(key); + Vertex valueVertex = externalReferences.EncodeReferenceToType(writer, type); + Vertex entry = writer.GetTuple(keyVertex, valueVertex); + typeMapHashTable.Append((uint)TypeHashingAlgorithms.ComputeNameHashCode(key), typeMapEntriesSection.Place(entry)); + } + + Vertex typeMapStateVertex = writer.GetUnsignedConstant(1); // Valid type map state + Vertex typeMapGroupVertex = externalReferences.EncodeReferenceToType(writer, TypeMapGroup); + Vertex tuple = writer.GetTuple(typeMapGroupVertex, typeMapStateVertex, typeMapHashTable); + return section.Place(tuple); + } + + public override IEnumerable GetConditionalStaticDependencies(NodeFactory context) => []; + public override IEnumerable GetStaticDependencies(NodeFactory context) + { + if (map.ThrowingMethodStub is not null) + { + yield break; + } + + foreach (var entry in map.TypeMap) + { + yield return new DependencyListEntry(importProvider.GetImportToType(entry.Value.type), $"External type map entry target for key '{entry.Key}'"); + } + } + public override IEnumerable SearchDynamicDependencies(List> markedNodes, int firstNode, NodeFactory context) => []; + protected override string GetName(NodeFactory context) => $"ExternalTypeMap {TypeMapGroup} entries in assembly {TriggeringModule.GetDisplayName()}"; + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunProxyTypeMapNode.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunProxyTypeMapNode.cs new file mode 100644 index 00000000000000..ee1951902deef5 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunProxyTypeMapNode.cs @@ -0,0 +1,87 @@ +// 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.DependencyAnalysis; +using ILCompiler.DependencyAnalysisFramework; +using Internal.NativeFormat; +using Internal.TypeSystem; +using Internal.TypeSystem.Ecma; + +namespace ILCompiler.ReadyToRun +{ + internal class ReadyToRunProxyTypeMapNode(ModuleDesc triggeringModule, TypeDesc group, TypeMapMetadata.IProxyTypeMap map, ImportReferenceProvider importProvider) : SortableDependencyNode, IProxyTypeMapNode + { + public TypeDesc TypeMapGroup => group; + + public override int ClassCode => 210131165; + + public override bool InterestingForDynamicDependencyAnalysis => false; + + public override bool HasDynamicDependencies => false; + + public override bool HasConditionalStaticDependencies => false; + + public override bool StaticDependenciesAreComputed => true; + + private ModuleDesc TriggeringModule { get; } = triggeringModule; + + public override int CompareToImpl(ISortableNode other, CompilerComparer comparer) + { + ReadyToRunProxyTypeMapNode otherNode = (ReadyToRunProxyTypeMapNode)other; + int result = comparer.Compare(TypeMapGroup, otherNode.TypeMapGroup); + if (result != 0) + return result; + + return comparer.Compare(TriggeringModule, otherNode.TriggeringModule); + } + + public Vertex CreateTypeMap(NodeFactory factory, NativeWriter writer, Section section, INativeFormatTypeReferenceProvider ProxyReferences) + { + if (map.ThrowingMethodStub is not null) + { + // We don't write out the throwing method stub for R2R + // as emitting loose methods is not supported/very expensive. + // Also, matching CoreCLR's exact set of exceptions is difficult + // in the managed type system. + // Instead, we defer to the runtime to generate the type map + // and throw on error cases. + return section.Place(writer.GetUnsignedConstant(0)); // Invalid type map state + } + + VertexHashtable typeMapHashTable = new(); + + Section typeMapEntriesSection = writer.NewSection(); + + foreach ((TypeDesc type, TypeDesc targetType) in map.TypeMap) + { + Vertex keyVertex = ProxyReferences.EncodeReferenceToType(writer, type); + Vertex valueVertex = ProxyReferences.EncodeReferenceToType(writer, targetType); + Vertex entry = writer.GetTuple(keyVertex, valueVertex); + typeMapHashTable.Append((uint)type.GetHashCode(), typeMapEntriesSection.Place(entry)); + } + + Vertex typeMapStateVertex = writer.GetUnsignedConstant(1); // Valid type map state + Vertex typeMapGroupVertex = ProxyReferences.EncodeReferenceToType(writer, TypeMapGroup); + Vertex tuple = writer.GetTuple(typeMapGroupVertex, typeMapStateVertex, typeMapHashTable); + return section.Place(tuple); + } + + public override IEnumerable GetConditionalStaticDependencies(NodeFactory context) => []; + public override IEnumerable GetStaticDependencies(NodeFactory context) + { + if (map.ThrowingMethodStub is not null) + { + yield break; + } + + foreach (var entry in map.TypeMap) + { + yield return new DependencyListEntry(importProvider.GetImportToType(entry.Key), $"Key type of Proxy type map entry"); + yield return new DependencyListEntry(importProvider.GetImportToType(entry.Value), $"Proxy type map entry target for key '{entry.Key}'"); + } + } + public override IEnumerable SearchDynamicDependencies(List> markedNodes, int firstNode, NodeFactory context) => []; + protected override string GetName(NodeFactory context) => $"ProxyTypeMap {TypeMapGroup} entries in assembly {TriggeringModule.GetDisplayName()}"; + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunTypeMapManager.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunTypeMapManager.cs new file mode 100644 index 00000000000000..61ed5314d0880a --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunTypeMapManager.cs @@ -0,0 +1,66 @@ +// 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.Collections.Generic; +using System.Text; +using ILCompiler.DependencyAnalysis; +using ILCompiler.DependencyAnalysis.ReadyToRun; +using ILCompiler.DependencyAnalysisFramework; +using Internal.Runtime; +using Internal.TypeSystem; + +namespace ILCompiler.ReadyToRun +{ + public sealed class ReadyToRunTypeMapManager(ModuleDesc triggeringModule, TypeMapMetadata assemblyTypeMaps) : TypeMapManager + { + private ImportReferenceProvider _importReferenceProvider; + + public override void AttachToDependencyGraph(DependencyAnalyzerBase graph) + { + base.AttachToDependencyGraph(graph); + foreach (var map in GetExternalTypeMaps()) + { + graph.AddRoot(map, "External type map"); + } + foreach (var map in GetProxyTypeMaps()) + { + graph.AddRoot(map, "Proxy type map"); + } + } + + protected override bool IsEmpty => assemblyTypeMaps.IsEmpty; + + public override void AddCompilationRoots(IRootingServiceProvider rootProvider) + { + } + + internal override IEnumerable GetExternalTypeMaps() + { + foreach (var map in assemblyTypeMaps.Maps) + { + yield return new ReadyToRunExternalTypeMapNode(triggeringModule, map.Key, map.Value, _importReferenceProvider); + } + } + + internal override IEnumerable GetProxyTypeMaps() + { + foreach (var map in assemblyTypeMaps.Maps) + { + yield return new ReadyToRunProxyTypeMapNode(triggeringModule, map.Key, map.Value, _importReferenceProvider); + } + } + + public void AddToReadyToRunHeader(ReadyToRunHeaderNode header, NodeFactory nodeFactory, ImportReferenceProvider importReferenceProvider) + { + base.AddToReadyToRunHeader(header, nodeFactory, importReferenceProvider); + + _importReferenceProvider = importReferenceProvider; + + if (IsEmpty) + return; + + header.Add(ReadyToRunSectionType.TypeMapAssemblyTargets, new TypeMapAssemblyTargetsNode(assemblyTypeMaps, importReferenceProvider)); + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/TypeMapAssemblyTargetsNode.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/TypeMapAssemblyTargetsNode.cs new file mode 100644 index 00000000000000..38a50c71fbc5c2 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/TypeMapAssemblyTargetsNode.cs @@ -0,0 +1,82 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using ILCompiler.DependencyAnalysis; +using ILCompiler.DependencyAnalysisFramework; +using Internal.NativeFormat; +using Internal.Text; +using Internal.TypeSystem.Ecma; + +namespace ILCompiler.ReadyToRun +{ + internal class TypeMapAssemblyTargetsNode : ObjectNode, ISymbolDefinitionNode + { + private TypeMapMetadata _assemblyTypeMaps; + private ImportReferenceProvider _importReferenceProvider; + + public TypeMapAssemblyTargetsNode(TypeMapMetadata assemblyTypeMaps, ImportReferenceProvider importReferenceProvider) + { + _assemblyTypeMaps = assemblyTypeMaps; + _importReferenceProvider = importReferenceProvider; + } + + public override bool IsShareable => false; + + public override int ClassCode => 1564556383; + + public override bool StaticDependenciesAreComputed => true; + + public int Offset => 0; + + protected override DependencyList ComputeNonRelocationBasedDependencies(NodeFactory factory) + { + DependencyList dependencies = []; + foreach (var map in _assemblyTypeMaps.Maps) + { + var groupType = map.Key; + dependencies.Add(new DependencyListEntry(_importReferenceProvider.GetImportToType(groupType), "Type Map Assembly Target")); + foreach (var targetModule in map.Value.TargetModules) + { + dependencies.Add(new DependencyListEntry(_importReferenceProvider.GetImportToModule(targetModule), "Type Map Assembly Target")); + } + } + return dependencies; + } + + public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) + { + // This node does not trigger generation of other nodes. + if (relocsOnly) + return new ObjectData([], [], 1, [this]); + + ObjectDataBuilder builder = new(factory, relocsOnly); + builder.AddSymbol(this); + + NativeWriter writer = new(); + Section section = writer.NewSection(); + + VertexHashtable table = new(); + section.Place(table); + + foreach (var map in _assemblyTypeMaps.Maps) + { + var groupType = map.Key; + Vertex groupTypeVertex = _importReferenceProvider.EncodeReferenceToType(writer, groupType); + VertexSequence modules = new(); + foreach (var targetModule in map.Value.TargetModules) + { + Vertex targetModuleVertex = _importReferenceProvider.EncodeReferenceToModule(writer, targetModule); + modules.Append(targetModuleVertex); + } + Vertex entry = writer.GetTuple(groupTypeVertex, modules); + table.Append((uint)groupType.GetHashCode(), section.Place(entry)); + } + + builder.EmitBytes(writer.Save()); + return builder.ToObjectData(); + } + public override ObjectNodeSection GetSection(NodeFactory factory) => ObjectNodeSection.ReadOnlyDataSection; + protected override string GetName(NodeFactory context) => "Type Map Assembly Targets Tables"; + public void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) => sb.Append(nameMangler.CompilationUnitPrefix).Append("__TypeMapAssemblyTargets"u8); + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ILCompiler.ReadyToRun.csproj b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ILCompiler.ReadyToRun.csproj index 1cff95dd437131..2c93ca5f506919 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ILCompiler.ReadyToRun.csproj +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ILCompiler.ReadyToRun.csproj @@ -41,6 +41,7 @@ + @@ -123,20 +124,27 @@ + + + + + + + @@ -187,8 +195,10 @@ + + @@ -197,9 +207,13 @@ + + + + @@ -235,7 +249,7 @@ - + diff --git a/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/ReadyToRunReader.cs b/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/ReadyToRunReader.cs index a960fcfdee3a2a..f1f55173e7d817 100644 --- a/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/ReadyToRunReader.cs +++ b/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/ReadyToRunReader.cs @@ -1281,9 +1281,15 @@ public Guid GetAssemblyMvid(int assemblyIndex) } return new Guid(mvidBytes); } + else if (assemblyIndex != 0) + { + // It's possible to have an index for an assembly in a non-composite image in one case: + // If the assembly index is only used for a module fixup, then we won't have an MVID for it + // as we haven't taken a dependency on any image details, just existence of the assembly. + return default(Guid); + } else { - Debug.Assert(assemblyIndex == 0); MetadataReader mdReader = GetGlobalMetadata().MetadataReader; return mdReader.GetGuid(mdReader.GetModuleDefinition().Mvid); } diff --git a/src/coreclr/vm/assemblynative.cpp b/src/coreclr/vm/assemblynative.cpp index 923517a3aa4837..707af6cce5b3eb 100644 --- a/src/coreclr/vm/assemblynative.cpp +++ b/src/coreclr/vm/assemblynative.cpp @@ -1535,6 +1535,35 @@ namespace } } +#ifdef FEATURE_READYTORUN + template + bool ProcessPrecachedTypeMapInfo( + TReadyToRunInfoFilter filter, + TReadyToRunInfoCallback callback, + Assembly* pAssembly, + bool* pHasPrecachedInfo + ) + { + STANDARD_VM_CONTRACT; + + PTR_Module pModule = pAssembly->GetModule(); + if (!pModule->IsReadyToRun()) + { + *pHasPrecachedInfo = false; + return true; + } + + PTR_ReadyToRunInfo pR2RInfo = pModule->GetReadyToRunInfo(); + + if (filter(pR2RInfo)) + { + *pHasPrecachedInfo = true; + return callback(pR2RInfo); + } + return true; + } +#endif + class AssemblyPtrCollectionTraits : public DefaultSHashTraits { public: @@ -1585,6 +1614,15 @@ namespace return TRUE; } + void Add(Assembly* assembly) + { + if (_toProcess.Lookup(assembly) == NULL + && _processed.Lookup(assembly) == NULL) + { + _toProcess.Add(assembly); + } + } + bool IsEmpty() const { return _toProcess.GetCount() == 0; @@ -1652,6 +1690,8 @@ extern "C" void QCALLTYPE TypeMapLazyDictionary_ProcessAttributes( QCall::TypeHandle pGroupType, BOOL (*newExternalTypeEntry)(CallbackContext* context, ProcessAttributesCallbackArg* arg), BOOL (*newProxyTypeEntry)(CallbackContext* context, ProcessAttributesCallbackArg* arg), + BOOL (*newPrecachedExternalTypeMap)(CallbackContext* context), + BOOL (*newPrecachedProxyTypeMap)(CallbackContext* context), CallbackContext* context) { QCALL_CONTRACT; @@ -1676,33 +1716,155 @@ extern "C" void QCALLTYPE TypeMapLazyDictionary_ProcessAttributes( GCX_COOP(); context->_currAssembly = currAssembly->GetExposedObject(); } + bool hasPrecachedInfo = false; + +#ifdef FEATURE_READYTORUN + // Only process the external type map if requested. + if (newExternalTypeEntry != nullptr + && !ProcessPrecachedTypeMapInfo( + [=](PTR_ReadyToRunInfo pR2RInfo) { return pR2RInfo->HasPrecachedExternalTypeMap(groupTypeMT); }, + [=](PTR_ReadyToRunInfo pR2RInfo) { return newPrecachedExternalTypeMap(context); }, + currAssembly, + &hasPrecachedInfo)) + { + // Return now so we can throw the exception encountered during creation. + return; + } - ProcessTypeMapAttribute( - TypeMapAssemblyTargetAttributeName, - assemblies, - groupTypeMT, - currAssembly); - - if (newExternalTypeEntry != NULL) + // Only process the proxy type map if requested. + if (newProxyTypeEntry != nullptr + && !ProcessPrecachedTypeMapInfo( + [=](PTR_ReadyToRunInfo pR2RInfo) { return pR2RInfo->HasPrecachedProxyTypeMap(groupTypeMT); }, + [=](PTR_ReadyToRunInfo pR2RInfo) { return newPrecachedProxyTypeMap(context); }, + currAssembly, + &hasPrecachedInfo)) { - MappingsProcessor onExternalType{ newExternalTypeEntry, context }; - ProcessTypeMapAttribute( - TypeMapAttributeName, - onExternalType, - groupTypeMT, - currAssembly); + // Return now so we can throw the exception encountered during creation. + return; } - if (newProxyTypeEntry != NULL) + // Don't count having external assembly targets as having precached info. + // We don't want valid external assembly targets to make us mistakenly think that the pre-cached maps are valid when they are not. + bool hasExternalTypeMapAssemblyTargets; + COUNT_T assemblyTargetCount = 0; + ProcessPrecachedTypeMapInfo( + [=, &assemblyTargetCount](PTR_ReadyToRunInfo pR2RInfo) { return pR2RInfo->HasTypeMapAssemblyTargets(groupTypeMT, &assemblyTargetCount); }, + [&](PTR_ReadyToRunInfo pR2RInfo) + { + CQuickArray targetModules; + targetModules.ReSizeThrows(assemblyTargetCount); + COUNT_T numReturnedTargets = pR2RInfo->GetTypeMapAssemblyTargets(groupTypeMT, targetModules.Ptr(), assemblyTargetCount); + _ASSERTE(numReturnedTargets == assemblyTargetCount); + for (COUNT_T i = 0; i < assemblyTargetCount; i++) + { + Assembly* targetAssembly = targetModules[i]->GetAssembly(); + assemblies.Add(targetAssembly); + } + return true; + }, + currAssembly, + &hasExternalTypeMapAssemblyTargets + ); +#endif // FEATURE_READYTORUN + + if (!hasPrecachedInfo) { - MappingsProcessor onProxyType{ newProxyTypeEntry, context }; ProcessTypeMapAttribute( - TypeMapAssociationAttributeName, - onProxyType, + TypeMapAssemblyTargetAttributeName, + assemblies, groupTypeMT, currAssembly); + + + // We will only process the specific type maps if we have a callback to process + // the entry and the precached map was not calculated for this module. + if (newExternalTypeEntry != NULL) + { + MappingsProcessor onExternalType{ newExternalTypeEntry, context }; + ProcessTypeMapAttribute( + TypeMapAttributeName, + onExternalType, + groupTypeMT, + currAssembly); + } + + if (newProxyTypeEntry != NULL) + { + MappingsProcessor onProxyType{ newProxyTypeEntry, context }; + ProcessTypeMapAttribute( + TypeMapAssociationAttributeName, + onProxyType, + groupTypeMT, + currAssembly); + } } } END_QCALL; } + +extern "C" TADDR QCALLTYPE TypeMapLazyDictionary_FindPrecachedExternalTypeMapEntry( + QCall::ModuleHandle pModule, + QCall::TypeHandle pGroupType, + LPCUTF8 key) +{ + QCALL_CONTRACT; + _ASSERTE(pModule != NULL); + _ASSERTE(!pGroupType.AsTypeHandle().IsNull()); + + TypeHandle resultTypeHnd{}; + + BEGIN_QCALL; + +#ifdef FEATURE_READYTORUN + if (pModule->IsReadyToRun()) + { + PTR_ReadyToRunInfo pR2RInfo = pModule->GetReadyToRunInfo(); + + TypeHandle groupTypeTH = pGroupType.AsTypeHandle(); + _ASSERTE(!groupTypeTH.IsTypeDesc()); + MethodTable* groupTypeMT = groupTypeTH.AsMethodTable(); + + resultTypeHnd = pR2RInfo->FindPrecachedExternalTypeMapEntry( + groupTypeMT, + key); + } +#endif // FEATURE_READYTORUN + + END_QCALL; + + return resultTypeHnd.AsTAddr(); +} + +extern "C" TADDR QCALLTYPE TypeMapLazyDictionary_FindPrecachedProxyTypeMapEntry( + QCall::ModuleHandle pModule, + QCall::TypeHandle pGroupType, + QCall::TypeHandle pType) +{ + QCALL_CONTRACT; + _ASSERTE(pModule != NULL); + _ASSERTE(!pGroupType.AsTypeHandle().IsNull()); + + TypeHandle resultTypeHnd{}; + + BEGIN_QCALL; + +#ifdef FEATURE_READYTORUN + if (pModule->IsReadyToRun()) + { + PTR_ReadyToRunInfo pR2RInfo = pModule->GetReadyToRunInfo(); + + TypeHandle groupTypeTH = pGroupType.AsTypeHandle(); + _ASSERTE(!groupTypeTH.IsTypeDesc()); + MethodTable* groupTypeMT = groupTypeTH.AsMethodTable(); + + resultTypeHnd = pR2RInfo->FindPrecachedProxyTypeMapEntry( + groupTypeMT, + pType.AsTypeHandle()); + } +#endif // FEATURE_READYTORUN + + END_QCALL; + + return resultTypeHnd.AsTAddr(); +} diff --git a/src/coreclr/vm/assemblynative.hpp b/src/coreclr/vm/assemblynative.hpp index cba6b322391d2a..c0aa373d8d1867 100644 --- a/src/coreclr/vm/assemblynative.hpp +++ b/src/coreclr/vm/assemblynative.hpp @@ -149,6 +149,7 @@ extern "C" void QCALLTYPE AssemblyName_InitializeAssemblySpec(NativeAssemblyName struct CallbackContext final { OBJECTREF _currAssembly; + OBJECTREF _groupType; OBJECTREF _externalTypeMap; OBJECTREF _proxyTypeMap; OBJECTREF _creationException; @@ -168,6 +169,20 @@ extern "C" void QCALLTYPE TypeMapLazyDictionary_ProcessAttributes( QCall::TypeHandle pTypeGroup, BOOL (*newExternalTypeEntry)(CallbackContext* context, ProcessAttributesCallbackArg* arg), BOOL (*newProxyTypeEntry)(CallbackContext* context, ProcessAttributesCallbackArg* arg), + BOOL (*newPrecachedExternalTypeMap)(CallbackContext* context), + BOOL (*newPrecachedProxyTypeMap)(CallbackContext* context), CallbackContext* context); +extern "C" TADDR TypeMapLazyDictionary_FindPrecachedExternalTypeMapEntry( + QCall::ModuleHandle pModule, + QCall::TypeHandle pGroupType, + LPCUTF8 key +); + +extern "C" TADDR TypeMapLazyDictionary_FindPrecachedProxyTypeMapEntry( + QCall::ModuleHandle pModule, + QCall::TypeHandle pGroupType, + QCall::TypeHandle pType +); + #endif diff --git a/src/coreclr/vm/binder.cpp b/src/coreclr/vm/binder.cpp index eb919caf5ed03d..0a2326d1143def 100644 --- a/src/coreclr/vm/binder.cpp +++ b/src/coreclr/vm/binder.cpp @@ -23,6 +23,7 @@ #include "configuration.h" #include "conditionalweaktable.h" #include "interoplibinterface_comwrappers.h" +#include "assemblynative.hpp" // // Retrieve structures from ID. diff --git a/src/coreclr/vm/corelib.h b/src/coreclr/vm/corelib.h index 24dec1fd462a1e..ee8376e41abdc8 100644 --- a/src/coreclr/vm/corelib.h +++ b/src/coreclr/vm/corelib.h @@ -452,6 +452,9 @@ DEFINE_CLASS(OBJCMARSHAL, ObjectiveC, ObjectiveCMarshal) DEFINE_METHOD(OBJCMARSHAL, INVOKEUNHANDLEDEXCEPTIONPROPAGATION, InvokeUnhandledExceptionPropagation, SM_PtrException_IntPtr_PtrIntPtr_PtrException_RetVoidPtr) #endif // FEATURE_OBJCMARSHAL +DEFINE_CLASS_U(Interop, TypeMapLazyDictionary+CallbackContext, CallbackContext) +DEFINE_FIELD_U(_currAssembly, CallbackContext, _currAssembly) + DEFINE_CLASS(IENUMERATOR, Collections, IEnumerator) DEFINE_CLASS(IENUMERABLE, Collections, IEnumerable) diff --git a/src/coreclr/vm/nativeformatreader.h b/src/coreclr/vm/nativeformatreader.h index d20f324610b8fb..b49e7f53a8dc3f 100644 --- a/src/coreclr/vm/nativeformatreader.h +++ b/src/coreclr/vm/nativeformatreader.h @@ -101,6 +101,13 @@ namespace NativeFormat return *dac_cast(_base + offset); // Assumes little endian and unaligned access } + UInt64 ReadUInt64(uint offset) + { + if ((int)offset < 0 || offset + 7 >= _size) + ThrowBadImageFormatException(); + return *dac_cast(_base + offset); // Assumes little endian and unaligned access + } + uint DecodeUnsigned(uint offset, uint * pValue) { if (offset >= _size) @@ -247,6 +254,69 @@ namespace NativeFormat } } + uint DecodeString(uint offset, PTR_CBYTE * ppValue, uint * pLength) + { + uint numBytes; + offset = DecodeUnsigned(offset, &numBytes); + + if (numBytes == 0) + { + *ppValue = NULL; + *pLength = 0; + return offset; + } + + uint endOffset = offset + numBytes; + if (endOffset < numBytes || endOffset > _size) + ThrowBadImageFormatException(); + + *ppValue = _base + offset; + *pLength = numBytes; + + return endOffset; + } + + uint SkipString(uint offset) + { + uint numBytes; + offset = DecodeUnsigned(offset, &numBytes); + + if (numBytes == 0) + { + return offset; + } + + uint endOffset = offset + numBytes; + if (endOffset < numBytes || endOffset > _size) + ThrowBadImageFormatException(); + + return endOffset; + } + + bool StringEquals(uint offset, LPCUTF8 value, uint valueLength) + { + uint originalOffset = offset; + + uint numBytes; + offset = DecodeUnsigned(offset, &numBytes); + + uint endOffset = offset + numBytes; + if (endOffset < numBytes || endOffset > _size) + ThrowBadImageFormatException(); + + if (numBytes != valueLength) + return false; + + PTR_CBYTE pData = _base + offset; + for (uint i = 0; i < numBytes; i++) + { + if (pData[i] != (byte)value[i]) + return false; + } + + return true; + } + #ifndef DACCESS_COMPILE const BYTE* GetBlob(uint offset) { @@ -331,6 +401,21 @@ namespace NativeFormat _offset = _pReader->SkipInteger(_offset); } + void GetString(PTR_CBYTE * ppValue, uint * pLength) + { + _offset = _pReader->DecodeString(_offset, ppValue, pLength); + } + + void SkipString() + { + _offset = _pReader->SkipString(_offset); + } + + bool StringEquals(LPCUTF8 value, uint valueLength) + { + return _pReader->StringEquals(_offset, value, valueLength); + } + NativeParser GetParserFromRelativeOffset() { return NativeParser(_pReader, GetRelativeOffset()); diff --git a/src/coreclr/vm/qcallentrypoints.cpp b/src/coreclr/vm/qcallentrypoints.cpp index 0a03fbce6b82a2..cc76fbe08ac8b5 100644 --- a/src/coreclr/vm/qcallentrypoints.cpp +++ b/src/coreclr/vm/qcallentrypoints.cpp @@ -278,6 +278,8 @@ static const Entry s_QCall[] = DllImportEntry(AssemblyNative_GetAssemblyCount) DllImportEntry(AssemblyNative_GetEntryAssembly) DllImportEntry(AssemblyNative_GetExecutingAssembly) + DllImportEntry(TypeMapLazyDictionary_FindPrecachedExternalTypeMapEntry) + DllImportEntry(TypeMapLazyDictionary_FindPrecachedProxyTypeMapEntry) DllImportEntry(TypeMapLazyDictionary_ProcessAttributes) #if defined(FEATURE_MULTICOREJIT) DllImportEntry(MultiCoreJIT_InternalSetProfileRoot) diff --git a/src/coreclr/vm/readytoruninfo.cpp b/src/coreclr/vm/readytoruninfo.cpp index 3cd42e18b8ff5a..9dc3348099659f 100644 --- a/src/coreclr/vm/readytoruninfo.cpp +++ b/src/coreclr/vm/readytoruninfo.cpp @@ -930,6 +930,30 @@ ReadyToRunInfo::ReadyToRunInfo(Module * pModule, LoaderAllocator* pLoaderAllocat } #endif + if (IsImageVersionAtLeast(18, 1)) + { + IMAGE_DATA_DIRECTORY* pExternalTypeMapsDir = m_component.FindSection(ReadyToRunSectionType::ExternalTypeMaps); + if (pExternalTypeMapsDir != NULL) + { + NativeParser parser = NativeParser(&m_nativeReader, pExternalTypeMapsDir->VirtualAddress); + m_externalTypeMaps = NativeHashtable(parser); + } + + IMAGE_DATA_DIRECTORY* pProxyTypeMapsDir = m_component.FindSection(ReadyToRunSectionType::ProxyTypeMaps); + if (pProxyTypeMapsDir != NULL) + { + NativeParser parser = NativeParser(&m_nativeReader, pProxyTypeMapsDir->VirtualAddress); + m_proxyTypeMaps = NativeHashtable(parser); + } + + IMAGE_DATA_DIRECTORY* pTypeMapAssemblyTargetsDir = m_component.FindSection(ReadyToRunSectionType::TypeMapAssemblyTargets); + if (pTypeMapAssemblyTargetsDir != NULL) + { + NativeParser parser = NativeParser(&m_nativeReader, pTypeMapAssemblyTargetsDir->VirtualAddress); + m_typeMapAssemblyTargets = NativeHashtable(parser); + } + } + if (!m_isComponentAssembly) { // For component assemblies we don't initialize the reverse lookup map mapping entry points to MethodDescs; @@ -1553,6 +1577,285 @@ void ReadyToRunInfo::DisableCustomAttributeFilter() m_attributesPresence.DisableFilter(); } +namespace +{ + TypeHandle GetTypeHandleForNativeFormatFixupReference(PTR_ReadyToRunInfo pR2RInfo, PTR_Module pModule, uint32_t importSection, uint32_t fixupIndex) + { + STANDARD_VM_CONTRACT; + + COUNT_T countImportSections; + PTR_READYTORUN_IMPORT_SECTION pImportSections = pR2RInfo->GetImportSections(&countImportSections); + + if (importSection >= countImportSections) + { + _ASSERTE(!"Malformed PGO type or method handle data"); + return TypeHandle(); + } + + PTR_READYTORUN_IMPORT_SECTION pImportSection = &pImportSections[importSection]; + COUNT_T cbData; + TADDR pData = pR2RInfo->GetImage()->GetDirectoryData(&pImportSection->Section, &cbData); + PTR_SIZE_T fixupAddress = dac_cast(pData + fixupIndex * sizeof(TADDR)); + if (!pModule->FixupNativeEntry(pImportSections + importSection, fixupIndex, fixupAddress)) + { + return TypeHandle(); + } + + return *(TypeHandle*)fixupAddress; + } + + Module* GetModuleForNativeFormatFixupReference(PTR_ReadyToRunInfo pR2RInfo, PTR_Module pModule, uint32_t importSection, uint32_t fixupIndex) + { + STANDARD_VM_CONTRACT; + + COUNT_T countImportSections; + PTR_READYTORUN_IMPORT_SECTION pImportSections = pR2RInfo->GetImportSections(&countImportSections); + + if (importSection >= countImportSections) + { + _ASSERTE(!"Malformed PGO type or method handle data"); + return nullptr; + } + + PTR_READYTORUN_IMPORT_SECTION pImportSection = &pImportSections[importSection]; + COUNT_T cbData; + TADDR pData = pR2RInfo->GetImage()->GetDirectoryData(&pImportSection->Section, &cbData); + PTR_SIZE_T fixupAddress = dac_cast(pData + fixupIndex * sizeof(TADDR)); + if (!pModule->FixupNativeEntry(pImportSections + importSection, fixupIndex, fixupAddress)) + { + return nullptr; + } + + return *(Module**)fixupAddress; + } +} + +bool ReadyToRunInfo::HasPrecachedExternalTypeMap(MethodTable* pGroupTypeMT) +{ + STANDARD_VM_CONTRACT; + + _ASSERTE(pGroupTypeMT != nullptr); + + if (m_externalTypeMaps.IsNull()) + { + return false; + } + + UINT32 hash = GetVersionResilientTypeHashCode(pGroupTypeMT); + NativeHashtable::Enumerator lookup = m_externalTypeMaps.Lookup(hash); + NativeParser entryParser; + while (lookup.GetNext(entryParser)) + { + uint32_t importSection = entryParser.GetUnsigned(); + uint32_t fixupIndex = entryParser.GetUnsigned(); + TypeHandle typeHandle = GetTypeHandleForNativeFormatFixupReference(this, m_pModule, importSection, fixupIndex); + if (typeHandle == TypeHandle(pGroupTypeMT)) + { + // A non-zero value next in the entry indicates that the table is valid. + return entryParser.GetUnsigned() != 0; + } + } + return false; +} + +TypeHandle ReadyToRunInfo::FindPrecachedExternalTypeMapEntry(MethodTable* pGroupType, LPCUTF8 pKey) +{ + STANDARD_VM_CONTRACT; + + _ASSERTE(pGroupType != nullptr); + if (m_externalTypeMaps.IsNull()) + { + return TypeHandle(); + } + + UINT32 hash = GetVersionResilientTypeHashCode(pGroupType); + NativeHashtable::Enumerator lookup = m_externalTypeMaps.Lookup(hash); + NativeParser entryParser; + while (lookup.GetNext(entryParser)) + { + uint32_t groupTypeImportSection = entryParser.GetUnsigned(); + uint32_t groupTypeFixupIndex = entryParser.GetUnsigned(); + TypeHandle groupTypeHandle = GetTypeHandleForNativeFormatFixupReference(this, m_pModule, groupTypeImportSection, groupTypeFixupIndex); + if (groupTypeHandle != TypeHandle(pGroupType)) + { + continue; + } + + if (entryParser.GetUnsigned() == 0) + { + // Table is not valid + return TypeHandle(); + } + + NativeHashtable typeMapTable = NativeHashtable(entryParser); + + UINT32 typeArgHash = ComputeNameHashCode(pKey); + NativeHashtable::Enumerator typeMapLookup = typeMapTable.Lookup(typeArgHash); + NativeParser typeMapEntryParser; + while (typeMapLookup.GetNext(typeMapEntryParser)) + { + if (typeMapEntryParser.StringEquals(pKey, (uint32_t)strlen(pKey))) + { + typeMapEntryParser.SkipString(); + uint32_t resultImportSection = typeMapEntryParser.GetUnsigned(); + uint32_t resultFixupIndex = typeMapEntryParser.GetUnsigned(); + return GetTypeHandleForNativeFormatFixupReference(this, m_pModule, resultImportSection, resultFixupIndex); + } + } + + // No matching entry found in the table. + return TypeHandle(); + } + + // No table found for the group type. + return TypeHandle(); +} + +bool ReadyToRunInfo::HasPrecachedProxyTypeMap(MethodTable* pGroupType) +{ + STANDARD_VM_CONTRACT; + _ASSERTE(pGroupType != nullptr); + if (m_proxyTypeMaps.IsNull()) + { + return false; + } + UINT32 hash = GetVersionResilientTypeHashCode(pGroupType); + NativeHashtable::Enumerator lookup = m_proxyTypeMaps.Lookup(hash); + NativeParser entryParser; + while (lookup.GetNext(entryParser)) + { + uint32_t importSection = entryParser.GetUnsigned(); + uint32_t fixupIndex = entryParser.GetUnsigned(); + TypeHandle typeHandle = GetTypeHandleForNativeFormatFixupReference(this, m_pModule, importSection, fixupIndex); + if (typeHandle == TypeHandle(pGroupType)) + { + // A non-zero value next in the entry indicates that the table is valid. + return entryParser.GetUnsigned() != 0; + } + } + return false; +} + +TypeHandle ReadyToRunInfo::FindPrecachedProxyTypeMapEntry(MethodTable* pGroupType, TypeHandle key) +{ + STANDARD_VM_CONTRACT; + + _ASSERTE(pGroupType != nullptr); + if (m_proxyTypeMaps.IsNull()) + { + return TypeHandle(); + } + + UINT32 hash = GetVersionResilientTypeHashCode(pGroupType); + NativeHashtable::Enumerator lookup = m_proxyTypeMaps.Lookup(hash); + NativeParser entryParser; + while (lookup.GetNext(entryParser)) + { + uint32_t groupTypeImportSection = entryParser.GetUnsigned(); + uint32_t groupTypeFixupIndex = entryParser.GetUnsigned(); + TypeHandle groupTypeHandle = GetTypeHandleForNativeFormatFixupReference(this, m_pModule, groupTypeImportSection, groupTypeFixupIndex); + if (groupTypeHandle != TypeHandle(pGroupType)) + { + continue; + } + + if (entryParser.GetUnsigned() == 0) + { + // Table is not valid + return TypeHandle(); + } + + NativeHashtable typeMapTable = NativeHashtable(entryParser); + + UINT32 typeArgHash = GetVersionResilientTypeHashCode(key); + NativeHashtable::Enumerator typeMapLookup = typeMapTable.Lookup(typeArgHash); + NativeParser typeMapEntryParser; + while (typeMapLookup.GetNext(typeMapEntryParser)) + { + uint32_t keyImportSection = typeMapEntryParser.GetUnsigned(); + uint32_t keyFixupIndex = typeMapEntryParser.GetUnsigned(); + TypeHandle keyTypeHandle = GetTypeHandleForNativeFormatFixupReference(this, m_pModule, keyImportSection, keyFixupIndex); + if (keyTypeHandle != key) + { + continue; + } + + uint32_t resultImportSection = typeMapEntryParser.GetUnsigned(); + uint32_t resultFixupIndex = typeMapEntryParser.GetUnsigned(); + return GetTypeHandleForNativeFormatFixupReference(this, m_pModule, resultImportSection, resultFixupIndex); + } + + // No matching entry found in the table. + return TypeHandle(); + } + + // No table found for the group type. + return TypeHandle(); +} + +bool ReadyToRunInfo::HasTypeMapAssemblyTargets(MethodTable* pGroupType, COUNT_T* pCount) +{ + STANDARD_VM_CONTRACT; + _ASSERTE(pGroupType != nullptr); + if (m_typeMapAssemblyTargets.IsNull()) + { + return false; + } + UINT32 hash = GetVersionResilientTypeHashCode(pGroupType); + NativeHashtable::Enumerator lookup = m_typeMapAssemblyTargets.Lookup(hash); + NativeParser entryParser; + while (lookup.GetNext(entryParser)) + { + uint32_t importSection = entryParser.GetUnsigned(); + uint32_t fixupIndex = entryParser.GetUnsigned(); + TypeHandle typeHandle = GetTypeHandleForNativeFormatFixupReference(this, m_pModule, importSection, fixupIndex); + if (typeHandle == TypeHandle(pGroupType)) + { + *pCount = entryParser.GetUnsigned(); + return true; + } + } + + return false; +} + +COUNT_T ReadyToRunInfo::GetTypeMapAssemblyTargets(MethodTable* pGroupType, Module** pTargetModules, COUNT_T count) +{ + STANDARD_VM_CONTRACT; + _ASSERTE(pGroupType != nullptr); + if (m_typeMapAssemblyTargets.IsNull()) + { + return 0; + } + + UINT32 hash = GetVersionResilientTypeHashCode(pGroupType); + NativeHashtable::Enumerator lookup = m_typeMapAssemblyTargets.Lookup(hash); + NativeParser entryParser; + while (lookup.GetNext(entryParser)) + { + uint32_t importSection = entryParser.GetUnsigned(); + uint32_t fixupIndex = entryParser.GetUnsigned(); + TypeHandle typeHandle = GetTypeHandleForNativeFormatFixupReference(this, m_pModule, importSection, fixupIndex); + if (typeHandle != TypeHandle(pGroupType)) + { + continue; + } + + COUNT_T numTargets = entryParser.GetUnsigned(); + COUNT_T resultCount = min(numTargets, count); + for (COUNT_T i = 0; i < resultCount; i++) + { + uint32_t moduleImportSection = entryParser.GetUnsigned(); + uint32_t moduleFixupIndex = entryParser.GetUnsigned(); + pTargetModules[i] = GetModuleForNativeFormatFixupReference(this, m_pModule, moduleImportSection, moduleFixupIndex); + } + + return resultCount; + } + + return 0; +} + class NativeManifestModule : public ModuleBase { IMDInternalImport* m_pMDImport; diff --git a/src/coreclr/vm/readytoruninfo.h b/src/coreclr/vm/readytoruninfo.h index 9f58e81c736c93..eb78a2e23a94f2 100644 --- a/src/coreclr/vm/readytoruninfo.h +++ b/src/coreclr/vm/readytoruninfo.h @@ -143,6 +143,10 @@ class ReadyToRunInfo PTR_PersistentInlineTrackingMapR2R m_pPersistentInlineTrackingMap; PTR_PersistentInlineTrackingMapR2R m_pCrossModulePersistentInlineTrackingMap; + NativeFormat::NativeHashtable m_externalTypeMaps; + NativeFormat::NativeHashtable m_proxyTypeMaps; + NativeFormat::NativeHashtable m_typeMapAssemblyTargets; + PTR_ReadyToRunInfo m_pNextR2RForUnrelatedCode; public: @@ -330,6 +334,16 @@ class ReadyToRunInfo bool MayHaveCustomAttribute(WellKnownAttribute attribute, mdToken token); void DisableCustomAttributeFilter(); + bool HasPrecachedExternalTypeMap(MethodTable* pGroupType); + TypeHandle FindPrecachedExternalTypeMapEntry(MethodTable* pGroupType, LPCUTF8 pKey); + + bool HasPrecachedProxyTypeMap(MethodTable* pGroupType); + TypeHandle FindPrecachedProxyTypeMapEntry(MethodTable* pGroupType, TypeHandle key); + + bool HasTypeMapAssemblyTargets(MethodTable* pGroupType, COUNT_T* pCount); + + COUNT_T GetTypeMapAssemblyTargets(MethodTable* pGroupType, Module** pTargetModules, COUNT_T count); + BOOL IsImageVersionAtLeast(int majorVersion, int minorVersion); private: BOOL GetTypeNameFromToken(IMDInternalImport * pImport, mdToken mdType, LPCUTF8 * ppszName, LPCUTF8 * ppszNameSpace); diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/TypeMapLazyDictionary.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/TypeMapLazyDictionary.cs index 048b0a4679f83f..22e237239ab473 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/TypeMapLazyDictionary.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/TypeMapLazyDictionary.cs @@ -22,10 +22,16 @@ internal static partial class TypeMapLazyDictionary private ref struct CallbackContext { private RuntimeAssembly? _currAssembly; + private readonly RuntimeType _groupType; private LazyExternalTypeDictionary? _externalTypeMap; private LazyProxyTypeDictionary? _proxyTypeMap; private ExceptionDispatchInfo? _creationException; + public CallbackContext(RuntimeType groupType) + { + _groupType = groupType; + } + public RuntimeAssembly CurrentAssembly { get @@ -41,7 +47,7 @@ public LazyExternalTypeDictionary ExternalTypeMap [RequiresUnreferencedCode("Lazy TypeMap isn't supported for Trimmer scenarios")] get { - _externalTypeMap ??= new LazyExternalTypeDictionary(); + _externalTypeMap ??= new LazyExternalTypeDictionary(_groupType); return _externalTypeMap; } } @@ -51,7 +57,7 @@ public LazyProxyTypeDictionary ProxyTypeMap [RequiresUnreferencedCode("Lazy TypeMap isn't supported for Trimmer scenarios")] get { - _proxyTypeMap ??= new LazyProxyTypeDictionary(); + _proxyTypeMap ??= new LazyProxyTypeDictionary(_groupType); return _proxyTypeMap; } } @@ -78,8 +84,16 @@ private static unsafe partial void ProcessAttributes( QCallTypeHandle groupType, delegate* unmanaged newExternalTypeEntry, delegate* unmanaged newProxyTypeEntry, + delegate* unmanaged newPrecachedExternalTypeMap, + delegate* unmanaged newPrecachedProxyTypeMap, CallbackContext* context); + [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "TypeMapLazyDictionary_FindPrecachedExternalTypeMapEntry", StringMarshalling = StringMarshalling.Utf8)] + private static unsafe partial IntPtr FindPrecachedExternalTypeMapEntry(QCallModule module, QCallTypeHandle groupType, string key); + + [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "TypeMapLazyDictionary_FindPrecachedProxyTypeMapEntry")] + private static unsafe partial IntPtr FindPrecachedProxyTypeMapEntry(QCallModule module, QCallTypeHandle groupType, QCallTypeHandle type); + public ref struct Utf16SharedBuffer { private char[]? _backingArray; @@ -118,6 +132,42 @@ private static void ConvertUtf8ToUtf16(ReadOnlySpan utf8TypeName, out Utf1 utf16Buffer = new Utf16SharedBuffer(buffer, converted); } + [UnmanagedCallersOnly] + private static unsafe Interop.BOOL NewPrecachedExternalTypeMap(CallbackContext* context) + { + Debug.Assert(context != null); + + try + { + context->ExternalTypeMap.AddPreCachedModule((RuntimeModule)context->CurrentAssembly.ManifestModule); + } + catch (Exception ex) + { + context->CreationException = ExceptionDispatchInfo.Capture(ex); + return Interop.BOOL.FALSE; // Stop processing. + } + + return Interop.BOOL.TRUE; // Continue processing. + } + + [UnmanagedCallersOnly] + private static unsafe Interop.BOOL NewPrecachedProxyTypeMap(CallbackContext* context) + { + Debug.Assert(context != null); + + try + { + context->ProxyTypeMap.AddPreCachedModule((RuntimeModule)context->CurrentAssembly.ManifestModule); + } + catch (Exception ex) + { + context->CreationException = ExceptionDispatchInfo.Capture(ex); + return Interop.BOOL.FALSE; // Stop processing. + } + + return Interop.BOOL.TRUE; // Continue processing. + } + [UnmanagedCallersOnly] private static unsafe Interop.BOOL NewExternalTypeEntry(CallbackContext* context, ProcessAttributesCallbackArg* arg) { @@ -204,12 +254,14 @@ private static unsafe CallbackContext CreateMaps( throw new InvalidOperationException(SR.InvalidOperation_TypeMapMissingEntryAssembly); } - CallbackContext context; + CallbackContext context = new(groupType); ProcessAttributes( new QCallAssembly(ref startingAssembly), new QCallTypeHandle(ref groupType), newExternalTypeEntry, newProxyTypeEntry, + &NewPrecachedExternalTypeMap, + &NewPrecachedProxyTypeMap, &context); // If an exception was thrown during the processing of @@ -243,12 +295,25 @@ public static IReadOnlyDictionary CreateProxyTypeMap(RuntimeType gro private abstract class LazyTypeLoadDictionary : IReadOnlyDictionary where TKey : notnull { + protected RuntimeType _groupType; + private readonly List _preCachedModules = []; + protected abstract bool TryGetOrLoadType(TKey key, [NotNullWhen(true)] out Type? type); + protected abstract bool TryGetOrLoadTypeFromPreCachedDictionary(RuntimeModule module, TKey key, [NotNullWhen(true)] out Type? type); + public Type this[TKey key] { get { + foreach (RuntimeModule module in _preCachedModules) + { + if (TryGetOrLoadTypeFromPreCachedDictionary(module, key, out Type? precachedType)) + { + return precachedType; + } + } + if (!TryGetOrLoadType(key, out Type? type)) { ThrowHelper.ThrowKeyNotFoundException(key); @@ -258,7 +323,27 @@ public Type this[TKey key] } } - public bool TryGetValue(TKey key, [MaybeNullWhen(false)] out Type value) => TryGetOrLoadType(key, out value); + protected LazyTypeLoadDictionary(RuntimeType groupType) + { + _groupType = groupType; + } + + public void AddPreCachedModule(RuntimeModule module) + { + _preCachedModules.Add(module); + } + + public bool TryGetValue(TKey key, [MaybeNullWhen(false)] out Type value) + { + foreach (RuntimeModule module in _preCachedModules) + { + if (TryGetOrLoadTypeFromPreCachedDictionary(module, key, out value)) + { + return true; + } + } + return TryGetOrLoadType(key, out value); + } // Not supported to avoid exposing TypeMap entries in a manner that // would violate invariants the Trimmer is attempting to enforce. @@ -317,7 +402,14 @@ public unsafe Type GetOrLoadType() [RequiresUnreferencedCode("Lazy TypeMap isn't supported for Trimmer scenarios")] private sealed class LazyExternalTypeDictionary : LazyTypeLoadDictionary { - private readonly Dictionary _lazyData = new(); + private readonly Dictionary _lazyData = []; + + protected override bool TryGetOrLoadTypeFromPreCachedDictionary(RuntimeModule module, string key, [NotNullWhen(true)] out Type? type) + { + IntPtr handle = FindPrecachedExternalTypeMapEntry(new QCallModule(ref module), new QCallTypeHandle(ref _groupType), key); + type = RuntimeTypeHandle.GetRuntimeTypeFromHandleMaybeNull(handle); + return type != null; + } protected override bool TryGetOrLoadType(string key, [NotNullWhen(true)] out Type? type) { @@ -331,6 +423,10 @@ protected override bool TryGetOrLoadType(string key, [NotNullWhen(true)] out Typ return true; } + public LazyExternalTypeDictionary(RuntimeType groupType) : base(groupType) + { + } + public void Add(string key, TypeNameUtf8 targetType, RuntimeAssembly fallbackAssembly) { if (_lazyData.ContainsKey(key)) @@ -371,6 +467,14 @@ public void Add(SourceProxyPair newEntryMaybe) private readonly Dictionary _lazyData = new(); + protected override bool TryGetOrLoadTypeFromPreCachedDictionary(RuntimeModule module, Type key, [NotNullWhen(true)] out Type? type) + { + RuntimeType rtKey = (RuntimeType)key; + IntPtr handle = FindPrecachedProxyTypeMapEntry(new QCallModule(ref module), new QCallTypeHandle(ref _groupType), new QCallTypeHandle(ref rtKey)); + type = RuntimeTypeHandle.GetRuntimeTypeFromHandleMaybeNull(handle); + return type != null; + } + protected override bool TryGetOrLoadType(Type key, [NotNullWhen(true)] out Type? type) { if (key is not RuntimeType rtType) @@ -405,6 +509,10 @@ protected override bool TryGetOrLoadType(Type key, [NotNullWhen(true)] out Type? return false; } + public LazyProxyTypeDictionary(RuntimeType groupType) : base(groupType) + { + } + public void Add( TypeName parsedSourceTypeName, TypeNameUtf8 sourceTypeName,