From 4c1143bb973b2ecb351ecd5c9638d37d9fe2886d Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Tue, 27 May 2025 16:20:51 -0700 Subject: [PATCH 01/45] Track type cast targets --- .../DependencyAnalysis/NodeFactory.cs | 12 +++++ .../DependencyAnalysis/TypeCastTargetNode.cs | 52 +++++++++++++++++++ .../Compiler/UsageBasedMetadataManager.cs | 6 +++ .../IL/ILImporter.Scanner.cs | 1 + .../ILCompiler.Compiler.csproj | 1 + 5 files changed, 72 insertions(+) create mode 100644 src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/TypeCastTargetNode.cs 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 9b73ea95894a3a..5f99bd32520c56 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/NodeFactory.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/NodeFactory.cs @@ -581,6 +581,11 @@ private void CreateNodeCaches() return new StringAllocatorMethodNode(constructor); }); + _typeCastTargets = new NodeCache(type => + { + return new TypeCastTargetNode(type); + }); + NativeLayout = new NativeLayoutHelper(this); } @@ -1464,6 +1469,13 @@ public StructMarshallingDataNode StructMarshallingData(DefType type) return _structMarshalingDataNodes.GetOrAdd(type); } + private NodeCache _typeCastTargets; + + public TypeCastTargetNode TypeCastTarget(TypeDesc type) + { + return _typeCastTargets.GetOrAdd(type); + } + /// /// Returns alternative symbol name that object writer should produce for given symbols /// in addition to the regular one. diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/TypeCastTargetNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/TypeCastTargetNode.cs new file mode 100644 index 00000000000000..7aaaa6d0ce6a37 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/TypeCastTargetNode.cs @@ -0,0 +1,52 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections.Generic; + +using ILCompiler.DependencyAnalysisFramework; + +using Internal.TypeSystem; + +using Debug = System.Diagnostics.Debug; + +namespace ILCompiler.DependencyAnalysis +{ + /// + /// Represents a type that is the target of a type cast operation. + /// Types that are targets of type casts preserve entries in type maps. + /// + public class TypeCastTargetNode : DependencyNodeCore + { + private readonly TypeDesc _type; + + public TypeCastTargetNode(TypeDesc type) + { + Debug.Assert(!type.IsCanonicalSubtype(CanonicalFormKind.Any) + || type.ConvertToCanonForm(CanonicalFormKind.Specific) == type); + _type = type; + } + + public TypeDesc Type => _type; + + public override IEnumerable GetStaticDependencies(NodeFactory factory) + { + var result = new DependencyList + { + new DependencyListEntry(factory.NecessaryTypeSymbol(_type), "Type map cast target"), + }; + + return result; + } + protected override string GetName(NodeFactory factory) + { + return "Cast target type: " + _type.ToString(); + } + + public override bool InterestingForDynamicDependencyAnalysis => false; + public override bool HasDynamicDependencies => false; + public override bool HasConditionalStaticDependencies => false; + public override bool StaticDependenciesAreComputed => true; + public override IEnumerable GetConditionalStaticDependencies(NodeFactory factory) => null; + public override IEnumerable SearchDynamicDependencies(List> markedNodes, int firstNode, NodeFactory factory) => null; + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/UsageBasedMetadataManager.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/UsageBasedMetadataManager.cs index 799792ea28abf0..6cf4f7249e9112 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/UsageBasedMetadataManager.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/UsageBasedMetadataManager.cs @@ -57,6 +57,7 @@ private static (string AttributeName, DiagnosticId Id)[] _requiresAttributeMisma private readonly List _fieldsWithRuntimeMapping = new List(); private readonly List _customAttributesWithMetadata = new List(); private readonly List _parametersWithMetadata = new List(); + private readonly List _typeMapTrimTargets = new List(); internal IReadOnlyDictionary FeatureSwitches { get; } @@ -171,6 +172,11 @@ protected override void Graph_NewMarkedNode(DependencyNodeCore obj) { _typesWithForcedEEType.Add(reflectableType.Type); } + + if (obj is TypeCastTargetNode typeCastTarget) + { + _typeMapTrimTargets.Add(typeCastTarget.Type); + } } protected override MetadataCategory GetMetadataCategory(FieldDesc field) diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/IL/ILImporter.Scanner.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/IL/ILImporter.Scanner.cs index 00a0d56d48150f..ecf27532b91234 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/IL/ILImporter.Scanner.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/IL/ILImporter.Scanner.cs @@ -283,6 +283,7 @@ private void ImportCasting(ILOpcode opcode, int token) } else { + _dependencies.Add(_factory.TypeCastTarget(type), "IsInst/CastClass target type"); _dependencies.Add(_compilation.ComputeConstantLookup(ReadyToRunHelperId.TypeHandleForCasting, type), "IsInst/CastClass"); } } diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/ILCompiler.Compiler.csproj b/src/coreclr/tools/aot/ILCompiler.Compiler/ILCompiler.Compiler.csproj index 7de58a994435fb..1a6f9f721f8504 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/ILCompiler.Compiler.csproj +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/ILCompiler.Compiler.csproj @@ -567,6 +567,7 @@ + From ce10c21f99f662bfe44d1a85542b77408f0203cb Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Tue, 27 May 2025 16:47:55 -0700 Subject: [PATCH 02/45] Scan for casts as part of the MethodBodyScanner instead --- .../Compiler/Dataflow/MethodBodyScanner.cs | 25 +++++++++++++++---- .../Compiler/Dataflow/ReflectionMarker.cs | 8 ++++++ .../Dataflow/ReflectionMethodBodyScanner.cs | 5 ++++ .../IL/ILImporter.Scanner.cs | 1 - 4 files changed, 33 insertions(+), 6 deletions(-) diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/MethodBodyScanner.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/MethodBodyScanner.cs index eef3878c24461b..89dc67b1f49ff9 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/MethodBodyScanner.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/MethodBodyScanner.cs @@ -594,7 +594,6 @@ protected virtual void Scan(MethodIL methodBody, ref InterproceduralState interp case ILOpcode.ldobj: case ILOpcode.mkrefany: case ILOpcode.unbox: - case ILOpcode.unbox_any: case ILOpcode.box: case ILOpcode.neg: case ILOpcode.not: @@ -603,12 +602,24 @@ protected virtual void Scan(MethodIL methodBody, ref InterproceduralState interp reader.Skip(opcode); break; + case ILOpcode.unbox_any: + { + PopUnknown(currentStack, 1, methodBody, offset); + PushUnknown(currentStack); + + var type = (TypeDesc)methodBody.GetObject(reader.ReadILToken()); + if (!type.IsValueType) + { + goto case ILOpcode.castclass; + } + } + break; case ILOpcode.isinst: case ILOpcode.castclass: - // We can consider a NOP because the value doesn't change. - // It might change to NULL, but for the purposes of dataflow analysis - // it doesn't hurt much. - reader.Skip(opcode); + { + var type = (TypeDesc)methodBody.GetObject(reader.ReadILToken()); + ScanCast(type); + } break; case ILOpcode.ldfld: @@ -1417,5 +1428,9 @@ private void ScanLdelem( else PushUnknown(currentStack); } + + protected virtual void ScanCast(TypeDesc targetType) + { + } } } diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/ReflectionMarker.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/ReflectionMarker.cs index 056032d025bfe8..120e870dc93af2 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/ReflectionMarker.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/ReflectionMarker.cs @@ -254,6 +254,14 @@ internal void CheckAndWarnOnReflectionAccess(in MessageOrigin origin, TypeSystem } } + internal void MarkTypeForCastTarget(TypeDesc type) + { + if (!_enabled) + return; + + _dependencies.Add(Factory.TypeCastTarget(type), "ReflectionMarker.MarkTypeForCastTarget"); + } + private void ReportWarningsForReflectionAccess(in MessageOrigin origin, TypeSystemEntity entity, AccessKind accessKind) { Debug.Assert(entity is MethodDesc or FieldDesc); diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/ReflectionMethodBodyScanner.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/ReflectionMethodBodyScanner.cs index 4738e0755bf08b..f23994dfc55aac 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/ReflectionMethodBodyScanner.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/ReflectionMethodBodyScanner.cs @@ -492,5 +492,10 @@ internal static bool IsPInvokeDangerous(MethodDesc calledMethod, out bool comDan return aotUnsafeDelegate || comDangerousMethod; } + + protected override void ScanCast(TypeDesc targetType) + { + _reflectionMarker.MarkTypeForCastTarget(targetType); + } } } diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/IL/ILImporter.Scanner.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/IL/ILImporter.Scanner.cs index ecf27532b91234..00a0d56d48150f 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/IL/ILImporter.Scanner.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/IL/ILImporter.Scanner.cs @@ -283,7 +283,6 @@ private void ImportCasting(ILOpcode opcode, int token) } else { - _dependencies.Add(_factory.TypeCastTarget(type), "IsInst/CastClass target type"); _dependencies.Add(_compilation.ComputeConstantLookup(ReadyToRunHelperId.TypeHandleForCasting, type), "IsInst/CastClass"); } } From 5495324a750d11761b6f0709f2a40af26da2ab25 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Tue, 3 Jun 2025 16:00:44 -0700 Subject: [PATCH 03/45] After feedback, first draft implementing external type map (compiler side) --- .../Common/Internal/Runtime/MetadataBlob.cs | 4 + .../Compiler/CompilationBuilder.Aot.cs | 7 + .../Compiler/Dataflow/HandleCallAction.cs | 30 ++ .../Compiler/Dataflow/ReflectionMarker.cs | 8 - .../Dataflow/ReflectionMethodBodyScanner.cs | 5 - .../ExternalTypeMapEntryNode.cs | 55 +++ .../DependencyAnalysis/ExternalTypeMapNode.cs | 43 +++ .../ExternalTypeMapObjectNode.cs | 86 +++++ .../ExternalTypeMapRequestNode.cs | 29 ++ .../DependencyAnalysis/ILScanNodeFactory.cs | 4 +- .../InvalidExternalTypeMapNode.cs | 53 +++ .../DependencyAnalysis/NodeFactory.cs | 32 +- .../ProxyTypeMapRequestNode.cs | 29 ++ .../DependencyAnalysis/TypeCastTargetNode.cs | 2 +- .../Compiler/ILScannerBuilder.cs | 9 +- .../Compiler/MetadataManager.cs | 22 ++ .../Compiler/TypeMapManager.cs | 320 ++++++++++++++++++ .../ILCompiler.Compiler.csproj | 7 + .../DependencyAnalysis/RyuJitNodeFactory.cs | 4 +- .../Compiler/RyuJitCompilationBuilder.cs | 2 +- src/coreclr/tools/aot/ILCompiler/Program.cs | 15 +- .../illink/src/ILLink.Shared/DiagnosticId.cs | 1 + .../src/ILLink.Shared/SharedStrings.resx | 6 + .../ILLink.Shared/TrimAnalysis/IntrinsicId.cs | 8 + .../ILLink.Shared/TrimAnalysis/Intrinsics.cs | 12 + 25 files changed, 766 insertions(+), 27 deletions(-) create mode 100644 src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ExternalTypeMapEntryNode.cs create mode 100644 src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ExternalTypeMapNode.cs create mode 100644 src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ExternalTypeMapObjectNode.cs create mode 100644 src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ExternalTypeMapRequestNode.cs create mode 100644 src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/InvalidExternalTypeMapNode.cs create mode 100644 src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ProxyTypeMapRequestNode.cs create mode 100644 src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/TypeMapManager.cs diff --git a/src/coreclr/tools/Common/Internal/Runtime/MetadataBlob.cs b/src/coreclr/tools/Common/Internal/Runtime/MetadataBlob.cs index e1a4d90162c2d2..1040a2d3f50c8b 100644 --- a/src/coreclr/tools/Common/Internal/Runtime/MetadataBlob.cs +++ b/src/coreclr/tools/Common/Internal/Runtime/MetadataBlob.cs @@ -42,5 +42,9 @@ internal enum ReflectionMapBlob StaticsInfoHashtable = 34, GenericMethodsHashtable = 35, ExactMethodInstantiationsHashtable = 36, + + // Type map blobs: + ExternalTypeMap = 40, + AssociatedTypeMap = 41, } } diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/CompilationBuilder.Aot.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/CompilationBuilder.Aot.cs index dae5bc317ae47e..fc0b1c9f84a651 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/CompilationBuilder.Aot.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/CompilationBuilder.Aot.cs @@ -26,6 +26,7 @@ public partial class CompilationBuilder protected SecurityMitigationOptions _mitigationOptions; protected bool _dehydrate; protected bool _useDwarf5; + protected TypeMapManager _typeMapManager = new TypeMapManager(null); partial void InitializePartial() { @@ -129,6 +130,12 @@ public CompilationBuilder UseDwarf5(bool value) return this; } + public CompilationBuilder UseTypeMapManager(TypeMapManager typeMapManager) + { + _typeMapManager = typeMapManager; + return this; + } + protected PreinitializationManager GetPreinitializationManager() { if (_preinitializationManager == null) diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/HandleCallAction.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/HandleCallAction.cs index 4767523a95bf3b..870a969c7e2b90 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/HandleCallAction.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/HandleCallAction.cs @@ -488,6 +488,36 @@ private partial bool TryHandleIntrinsic ( _diagnosticContext.AddDiagnostic(DiagnosticId.AvoidAssemblyGetFilesInSingleFile, calledMethod.GetDisplayName()); break; + case IntrinsicId.TypeMapping_GetOrCreateExternalTypeMapping: + { + if (calledMethod.Method.Instantiation[0].ContainsSignatureVariables(treatGenericParameterLikeSignatureVariable: true)) + { + // We only support GetOrCreateExternalTypeMapping for a fully specified type. + _diagnosticContext.AddDiagnostic(DiagnosticId.TypeMapGroupTypeCannotBeStaticallyDetermined, + calledMethod.Method.Instantiation[0].GetDisplayName()); + } + else + { + TypeDesc typeMapGroup = calledMethod.Method.Instantiation[0]; + _reflectionMarker.Dependencies.Add(_reflectionMarker.Factory.ExternalTypeMapRequest(typeMapGroup), "TypeMapping.GetOrCreateExternalTypeMapping called on type"); + } + break; + } + case IntrinsicId.TypeMapping_GetOrCreateProxyTypeMapping: + { + if (calledMethod.Method.Instantiation[0].ContainsSignatureVariables(treatGenericParameterLikeSignatureVariable: true)) + { + // We only support GetOrCreateProxyTypeMapping for a fully specified type. + _diagnosticContext.AddDiagnostic(DiagnosticId.TypeMapGroupTypeCannotBeStaticallyDetermined, + calledMethod.Method.Instantiation[0].GetDisplayName()); + } + else + { + TypeDesc typeMapGroup = calledMethod.Method.Instantiation[0]; + _reflectionMarker.Dependencies.Add(_reflectionMarker.Factory.ProxyTypeMapRequest(typeMapGroup), "TypeMapping.GetOrCreateProxyTypeMapping called on type"); + } + break; + } default: return false; } diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/ReflectionMarker.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/ReflectionMarker.cs index 120e870dc93af2..056032d025bfe8 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/ReflectionMarker.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/ReflectionMarker.cs @@ -254,14 +254,6 @@ internal void CheckAndWarnOnReflectionAccess(in MessageOrigin origin, TypeSystem } } - internal void MarkTypeForCastTarget(TypeDesc type) - { - if (!_enabled) - return; - - _dependencies.Add(Factory.TypeCastTarget(type), "ReflectionMarker.MarkTypeForCastTarget"); - } - private void ReportWarningsForReflectionAccess(in MessageOrigin origin, TypeSystemEntity entity, AccessKind accessKind) { Debug.Assert(entity is MethodDesc or FieldDesc); diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/ReflectionMethodBodyScanner.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/ReflectionMethodBodyScanner.cs index f23994dfc55aac..4738e0755bf08b 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/ReflectionMethodBodyScanner.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/ReflectionMethodBodyScanner.cs @@ -492,10 +492,5 @@ internal static bool IsPInvokeDangerous(MethodDesc calledMethod, out bool comDan return aotUnsafeDelegate || comDangerousMethod; } - - protected override void ScanCast(TypeDesc targetType) - { - _reflectionMarker.MarkTypeForCastTarget(targetType); - } } } diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ExternalTypeMapEntryNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ExternalTypeMapEntryNode.cs new file mode 100644 index 00000000000000..d430ea496a2c47 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ExternalTypeMapEntryNode.cs @@ -0,0 +1,55 @@ +// 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 ILCompiler.DependencyAnalysis; +using ILCompiler.DependencyAnalysisFramework; +using Internal.TypeSystem; + +namespace ILCompiler.DependencyAnalysis +{ + internal sealed class ExternalTypeMapEntryNode(TypeDesc typeMapGroup, string key, TypeDesc targetType) : DependencyNodeCore, ISortableNode + { + public override bool InterestingForDynamicDependencyAnalysis => true; + + public override bool HasDynamicDependencies => false; + + public override bool HasConditionalStaticDependencies => false; + + public override bool StaticDependenciesAreComputed => true; + + public TypeDesc TypeMapGroup { get; } = typeMapGroup; + + public string Key { get; } = key; + + public TypeDesc TargetType { get; } = targetType; + + public int ClassCode => -785190502; + + public int CompareToImpl(ISortableNode other, CompilerComparer comparer) + { + ExternalTypeMapEntryNode otherEntry = (ExternalTypeMapEntryNode)other; + if (TypeMapGroup != otherEntry.TypeMapGroup) + { + return comparer.Compare(TypeMapGroup, otherEntry.TypeMapGroup); + } + else if (Key != otherEntry.Key) + { + return string.Compare(Key, otherEntry.Key, StringComparison.Ordinal); + } + else if (TargetType != otherEntry.TargetType) + { + return comparer.Compare(TargetType, otherEntry.TargetType); + } + + return 0; + } + + public override IEnumerable GetConditionalStaticDependencies(NodeFactory context) => Array.Empty(); + + 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) => $"ExternalTypeMapEntryNode({Key}, {TargetType}) in group {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 new file mode 100644 index 00000000000000..5ee82b8377a23d --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ExternalTypeMapNode.cs @@ -0,0 +1,43 @@ +// 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.DependencyAnalysisFramework; +using Internal.TypeSystem; + +namespace ILCompiler.DependencyAnalysis +{ + internal sealed class ExternalTypeMapNode(TypeDesc typeMapGroup, IEnumerable> mapEntries) : DependencyNodeCore + { + public override bool InterestingForDynamicDependencyAnalysis => false; + + public override bool HasDynamicDependencies => false; + + public override bool HasConditionalStaticDependencies => true; + + public override bool StaticDependenciesAreComputed => true; + + public override IEnumerable GetConditionalStaticDependencies(NodeFactory context) + { + List entries = []; + foreach (var entry in mapEntries) + { + var targetType = entry.Value.targetType; + var trimmingTargetType = entry.Value.trimmingTargetType; + entries.Add(new CombinedDependencyListEntry( + new ExternalTypeMapEntryNode(typeMapGroup, entry.Key, targetType), + context.NecessaryTypeSymbol(trimmingTargetType), + "Type in external type map is cast target")); + } + + return entries; + } + + 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) => "External type map"; + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ExternalTypeMapObjectNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ExternalTypeMapObjectNode.cs new file mode 100644 index 00000000000000..a4955c0302c33e --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ExternalTypeMapObjectNode.cs @@ -0,0 +1,86 @@ +// 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.Diagnostics; +using ILCompiler.DependencyAnalysis; +using ILCompiler.DependencyAnalysisFramework; +using Internal.NativeFormat; +using Internal.Text; +using Internal.TypeSystem; + +namespace ILCompiler.DependencyAnalysis +{ + internal sealed class ExternalTypeMapObjectNode(ExternalReferencesTableNode externalReferences) : ObjectNode, ISymbolDefinitionNode, INodeWithSize + { + public void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) + { + sb.Append(nameMangler.CompilationUnitPrefix).Append("__external_type_map__"u8); + } + + public int Size { get; private set; } + public int Offset => 0; + public override bool IsShareable => false; + public override ObjectNodeSection GetSection(NodeFactory factory) => externalReferences.GetSection(factory); + + public override bool HasDynamicDependencies => true; + + public override int ClassCode => 2090746844; + + public override bool StaticDependenciesAreComputed => true; + + protected override string GetName(NodeFactory context) => $"External Type Map"; + public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) + { + // This node does not trigger generation of other nodes. + if (relocsOnly) + return new ObjectData(Array.Empty(), Array.Empty(), 1, [this]); + + var writer = new NativeWriter(); + var typeMapGroupHashTable = new VertexHashtable(); + + Dictionary typeMapHashTables = new(); + + Section hashTableSection = writer.NewSection(); + hashTableSection.Place(typeMapGroupHashTable); + + foreach (ExternalTypeMapEntryNode entryNode in factory.MetadataManager.GetExternalTypeMapEntries()) + { + if (!typeMapHashTables.TryGetValue(entryNode.TypeMapGroup, out VertexHashtable typeMapHashTable)) + { + TypeDesc typeMapGroup = entryNode.TypeMapGroup; + typeMapHashTable = typeMapHashTables[typeMapGroup] = new VertexHashtable(); + Vertex typeMapStateVertex = writer.GetUnsignedConstant(1); // Hash table + Vertex typeMapGroupVertex = writer.GetUnsignedConstant(externalReferences.GetIndex(factory.NecessaryTypeSymbol(typeMapGroup))); + typeMapGroupHashTable.Append((uint)typeMapGroup.GetHashCode(), writer.GetTuple(typeMapGroupVertex, typeMapStateVertex, hashTableSection.Place(typeMapHashTable))); + } + + Vertex nameVertex = writer.GetStringConstant(entryNode.Key); + Vertex targetTypeVertex = writer.GetUnsignedConstant(externalReferences.GetIndex(factory.MaximallyConstructableType(entryNode.TargetType))); + Vertex entry = writer.GetTuple(nameVertex, targetTypeVertex); + + typeMapHashTable.Append((uint)TypeHashingAlgorithms.ComputeNameHashCode(entryNode.Key), hashTableSection.Place(entry)); + } + + foreach (InvalidExternalTypeMapNode invalidNode in factory.MetadataManager.GetInvalidExternalTypeMaps()) + { + if (!typeMapHashTables.TryGetValue(invalidNode.TypeMapGroup, out _)) + { + TypeDesc typeMapGroup = invalidNode.TypeMapGroup; + typeMapHashTables[typeMapGroup] = new VertexHashtable(); + Vertex typeMapStateVertex = writer.GetUnsignedConstant(0); + Vertex typeMapGroupVertex = writer.GetUnsignedConstant(externalReferences.GetIndex(factory.NecessaryTypeSymbol(typeMapGroup))); + Vertex throwingMethodStubVertex = writer.GetUnsignedConstant(externalReferences.GetIndex(factory.MethodEntrypoint(invalidNode.ThrowingMethodStub))); + typeMapGroupHashTable.Append((uint)typeMapGroup.GetHashCode(), writer.GetTuple(typeMapGroupVertex, typeMapStateVertex, throwingMethodStubVertex)); + } + } + + byte[] hashTableBytes = writer.Save(); + + Size = hashTableBytes.Length; + + return new ObjectData(hashTableBytes, Array.Empty(), 1, [this]); + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ExternalTypeMapRequestNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ExternalTypeMapRequestNode.cs new file mode 100644 index 00000000000000..71a8cfd1bbfbd0 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ExternalTypeMapRequestNode.cs @@ -0,0 +1,29 @@ +// 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 ILCompiler.DependencyAnalysisFramework; +using Internal.TypeSystem; +using Debug = System.Diagnostics.Debug; + +namespace ILCompiler.DependencyAnalysis +{ + public sealed class ExternalTypeMapRequestNode(TypeDesc typeMapGroup) : DependencyNodeCore + { + public override bool InterestingForDynamicDependencyAnalysis => false; + + public override bool HasDynamicDependencies => false; + + public override bool HasConditionalStaticDependencies => false; + + public override bool StaticDependenciesAreComputed => false; + + public TypeDesc TypeMapGroup { get; } = typeMapGroup; + + public override IEnumerable GetConditionalStaticDependencies(NodeFactory context) => []; + public override IEnumerable GetStaticDependencies(NodeFactory context) => []; + public override IEnumerable SearchDynamicDependencies(List> markedNodes, int firstNode, NodeFactory context) => []; + protected override string GetName(NodeFactory context) => $"ExternalTypeMapRequestNode({TypeMapGroup})"; + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ILScanNodeFactory.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ILScanNodeFactory.cs index 2d13f181a62242..d780d5e99891cf 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ILScanNodeFactory.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ILScanNodeFactory.cs @@ -12,8 +12,8 @@ namespace ILCompiler.DependencyAnalysis /// public sealed class ILScanNodeFactory : NodeFactory { - public ILScanNodeFactory(CompilerTypeSystemContext context, CompilationModuleGroup compilationModuleGroup, MetadataManager metadataManager, InteropStubManager interopStubManager, NameMangler nameMangler, PreinitializationManager preinitManager) - : base(context, compilationModuleGroup, metadataManager, interopStubManager, nameMangler, new LazyGenericsDisabledPolicy(), new LazyVTableSliceProvider(), new LazyDictionaryLayoutProvider(), new InlinedThreadStatics(), new ExternSymbolsImportedNodeProvider(), preinitManager, new DevirtualizationManager(), ObjectDataInterner.Null) + public ILScanNodeFactory(CompilerTypeSystemContext context, CompilationModuleGroup compilationModuleGroup, MetadataManager metadataManager, InteropStubManager interopStubManager, NameMangler nameMangler, PreinitializationManager preinitManager, TypeMapManager typeMapManager) + : base(context, compilationModuleGroup, metadataManager, interopStubManager, nameMangler, new LazyGenericsDisabledPolicy(), new LazyVTableSliceProvider(), new LazyDictionaryLayoutProvider(), new InlinedThreadStatics(), new ExternSymbolsImportedNodeProvider(), preinitManager, new DevirtualizationManager(), ObjectDataInterner.Null, typeMapManager) { } diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/InvalidExternalTypeMapNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/InvalidExternalTypeMapNode.cs new file mode 100644 index 00000000000000..6d7c5581e634da --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/InvalidExternalTypeMapNode.cs @@ -0,0 +1,53 @@ +// 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.TypeSystem; + +namespace ILCompiler.DependencyAnalysis +{ + internal sealed class InvalidExternalTypeMapNode(TypeDesc typeMapGroup, MethodDesc throwingMethodStub) : DependencyNodeCore, ISortableNode + { + public override bool InterestingForDynamicDependencyAnalysis => false; + + public override bool HasDynamicDependencies => false; + + public override bool HasConditionalStaticDependencies => false; + + public override bool StaticDependenciesAreComputed => true; + + public override IEnumerable GetConditionalStaticDependencies(NodeFactory context) => Array.Empty(); + public override IEnumerable GetStaticDependencies(NodeFactory context) + { + return [ + new DependencyListEntry(context.MethodEntrypoint(ThrowingMethodStub), "Throwing method stub for invalid type map"), + ]; + } + + public override IEnumerable SearchDynamicDependencies(List> markedNodes, int firstNode, NodeFactory context) => Array.Empty(); + protected override string GetName(NodeFactory context) => "InvalidExternalTypeMapNode"; + + public TypeDesc TypeMapGroup { get; } = typeMapGroup; + public MethodDesc ThrowingMethodStub { get; } = throwingMethodStub; + + public int ClassCode => 36910224; + + public int CompareToImpl(ISortableNode other, CompilerComparer comparer) + { + if (other is InvalidExternalTypeMapNode otherNode) + { + int result = comparer.Compare(TypeMapGroup, otherNode.TypeMapGroup); + if (result != 0) + return result; + return comparer.Compare(ThrowingMethodStub, otherNode.ThrowingMethodStub); + } + else + { + return -1; // This node is always less than any other node + } + } + } +} 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 5f99bd32520c56..510ce746d5a17c 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/NodeFactory.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/NodeFactory.cs @@ -41,7 +41,8 @@ public NodeFactory( ImportedNodeProvider importedNodeProvider, PreinitializationManager preinitializationManager, DevirtualizationManager devirtualizationManager, - ObjectDataInterner dataInterner) + ObjectDataInterner dataInterner, + TypeMapManager typeMapManager) { _target = context.Target; @@ -61,6 +62,7 @@ public NodeFactory( PreinitializationManager = preinitializationManager; DevirtualizationManager = devirtualizationManager; ObjectInterner = dataInterner; + TypeMapManager = typeMapManager; } public void SetMarkingComplete() @@ -130,6 +132,11 @@ internal ObjectDataInterner ObjectInterner get; } + public TypeMapManager TypeMapManager + { + get; + } + /// /// Return true if the type is not permitted by the rules of the runtime to have an MethodTable. /// The implementation here is not intended to be complete, but represents many conditions @@ -581,9 +588,14 @@ private void CreateNodeCaches() return new StringAllocatorMethodNode(constructor); }); - _typeCastTargets = new NodeCache(type => + _externalTypeMapRequests = new NodeCache(type => { - return new TypeCastTargetNode(type); + return new ExternalTypeMapRequestNode(type); + }); + + _proxyTypeMapRequests = new NodeCache(type => + { + return new ProxyTypeMapRequestNode(type); }); NativeLayout = new NativeLayoutHelper(this); @@ -1469,11 +1481,18 @@ public StructMarshallingDataNode StructMarshallingData(DefType type) return _structMarshalingDataNodes.GetOrAdd(type); } - private NodeCache _typeCastTargets; + private NodeCache _externalTypeMapRequests; + + public ExternalTypeMapRequestNode ExternalTypeMapRequest(TypeDesc type) + { + return _externalTypeMapRequests.GetOrAdd(type); + } + + private NodeCache _proxyTypeMapRequests; - public TypeCastTargetNode TypeCastTarget(TypeDesc type) + public ProxyTypeMapRequestNode ProxyTypeMapRequest(TypeDesc type) { - return _typeCastTargets.GetOrAdd(type); + return _proxyTypeMapRequests.GetOrAdd(type); } /// @@ -1548,6 +1567,7 @@ public virtual void AttachToDependencyGraph(DependencyAnalyzerBase var commonFixupsTableNode = new ExternalReferencesTableNode("CommonFixupsTable", this); InteropStubManager.AddToReadyToRunHeader(ReadyToRunHeader, this, commonFixupsTableNode); + TypeMapManager.AddToReadyToRunHeader(ReadyToRunHeader, this, commonFixupsTableNode); MetadataManager.AddToReadyToRunHeader(ReadyToRunHeader, this, commonFixupsTableNode); MetadataManager.AttachToDependencyGraph(graph); ReadyToRunHeader.Add(MetadataManager.BlobIdToReadyToRunSection(ReflectionMapBlob.CommonFixupsTable), commonFixupsTableNode); diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ProxyTypeMapRequestNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ProxyTypeMapRequestNode.cs new file mode 100644 index 00000000000000..3ec77118781e13 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ProxyTypeMapRequestNode.cs @@ -0,0 +1,29 @@ +// 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 ILCompiler.DependencyAnalysisFramework; +using Internal.TypeSystem; +using Debug = System.Diagnostics.Debug; + +namespace ILCompiler.DependencyAnalysis +{ + public sealed class ProxyTypeMapRequestNode(TypeDesc typeMapGroup) : DependencyNodeCore + { + public override bool InterestingForDynamicDependencyAnalysis => false; + + public override bool HasDynamicDependencies => false; + + public override bool HasConditionalStaticDependencies => false; + + public override bool StaticDependenciesAreComputed => false; + + public TypeDesc TypeMapGroup { get; } = typeMapGroup; + + public override IEnumerable GetConditionalStaticDependencies(NodeFactory context) => []; + public override IEnumerable GetStaticDependencies(NodeFactory context) => []; + public override IEnumerable SearchDynamicDependencies(List> markedNodes, int firstNode, NodeFactory context) => []; + protected override string GetName(NodeFactory context) => $"ExternalTypeMapRequestNode({TypeMapGroup})"; + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/TypeCastTargetNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/TypeCastTargetNode.cs index 7aaaa6d0ce6a37..18cee5904951a4 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/TypeCastTargetNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/TypeCastTargetNode.cs @@ -32,7 +32,7 @@ public override IEnumerable GetStaticDependencies(NodeFacto { var result = new DependencyList { - new DependencyListEntry(factory.NecessaryTypeSymbol(_type), "Type map cast target"), + new DependencyListEntry(factory.ConstructedTypeSymbol(_type), "Type map cast target"), }; return result; diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ILScannerBuilder.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ILScannerBuilder.cs index 887c90703daf7e..1d1438830a492a 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ILScannerBuilder.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ILScannerBuilder.cs @@ -25,6 +25,7 @@ public sealed class ILScannerBuilder private IEnumerable _compilationRoots = Array.Empty(); private MetadataManager _metadataManager; private InteropStubManager _interopStubManager = new EmptyInteropStubManager(); + private TypeMapManager _typeMapManager; private int _parallelism = -1; internal ILScannerBuilder(CompilerTypeSystemContext context, CompilationModuleGroup compilationGroup, NameMangler mangler, ILProvider ilProvider, PreinitializationManager preinitializationManager) @@ -61,6 +62,12 @@ public ILScannerBuilder UseInteropStubManager(InteropStubManager interopStubMana return this; } + public ILScannerBuilder UseTypeMapManager(TypeMapManager typeMapManager) + { + _typeMapManager = typeMapManager; + return this; + } + public ILScannerBuilder UseParallelism(int parallelism) { _parallelism = parallelism; @@ -75,7 +82,7 @@ public ILScannerBuilder UseLogger(Logger logger) public IILScanner ToILScanner() { - var nodeFactory = new ILScanNodeFactory(_context, _compilationGroup, _metadataManager, _interopStubManager, _nameMangler, _preinitializationManager); + var nodeFactory = new ILScanNodeFactory(_context, _compilationGroup, _metadataManager, _interopStubManager, _nameMangler, _preinitializationManager, _typeMapManager); DependencyAnalyzerBase graph = _dependencyTrackingLevel.CreateDependencyGraph(nodeFactory); return new ILScanner(graph, nodeFactory, _compilationRoots, _ilProvider, new NullDebugInformationProvider(), _logger, _parallelism); diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/MetadataManager.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/MetadataManager.cs index 1476d46cbd10e0..b6cab4cc0bb00e 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/MetadataManager.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/MetadataManager.cs @@ -82,6 +82,8 @@ private readonly SortedSet _typeGVMEntries private readonly SortedSet _genericMethodHashtableEntries = new SortedSet(TypeSystemComparer.Instance); private readonly SortedSet _exactMethodHashtableEntries = new SortedSet(TypeSystemComparer.Instance); private readonly HashSet _usedInterfaces = new HashSet(); + private readonly SortedSet _externalTypeMapEntries = new SortedSet(CompilerComparer.Instance); + private readonly SortedSet _invalidExternalTypeMaps = new SortedSet(CompilerComparer.Instance); private List<(DehydratableObjectNode Node, ObjectNode.ObjectData Data)> _dehydratableData = new List<(DehydratableObjectNode Node, ObjectNode.ObjectData data)>(); @@ -347,6 +349,16 @@ protected virtual void Graph_NewMarkedNode(DependencyNodeCore obj) { _usedInterfaces.Add(interfaceUse.Type); } + + if (obj is ExternalTypeMapEntryNode externalTypeMapEntryNode) + { + _externalTypeMapEntries.Add(externalTypeMapEntryNode); + } + + if (obj is InvalidExternalTypeMapNode invalidExternalTypeMapNode) + { + _invalidExternalTypeMaps.Add(invalidExternalTypeMapNode); + } } protected virtual bool AllMethodsCanBeReflectable => false; @@ -1110,6 +1122,16 @@ internal IEnumerable GetTemplateM return _templateMethodEntries; } + internal IEnumerable GetExternalTypeMapEntries() + { + return _externalTypeMapEntries; + } + + internal IEnumerable GetInvalidExternalTypeMaps() + { + return _invalidExternalTypeMaps; + } + public bool IsReflectionBlocked(TypeDesc type) { switch (type.Category) diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/TypeMapManager.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/TypeMapManager.cs new file mode 100644 index 00000000000000..933642159fcf93 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/TypeMapManager.cs @@ -0,0 +1,320 @@ +// 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.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 Internal.TypeSystem.Ecma; +using DependencyList = ILCompiler.DependencyAnalysisFramework.DependencyNodeCore.DependencyList; +using ReflectionMapBlob = Internal.Runtime.ReflectionMapBlob; + +namespace ILCompiler +{ + /// + /// This class is responsible for managing emitted data for type maps. + /// + public sealed class TypeMapManager : ICompilationRootProvider + { + private sealed class TypeMapState + { + private readonly Dictionary _associatedTypeMap = []; + private readonly Dictionary _externalTypeMap = []; + private ThrowingMethodStub _externalTypeMapExceptionStub; + private ThrowingMethodStub _associatedTypeMapExceptionStub; + + public void AddAssociatedTypeMapEntry(TypeDesc type, TypeDesc associatedType) + { + if (!_associatedTypeMap.TryAdd(type, associatedType)) + { + ThrowHelper.ThrowBadImageFormatException(); + } + } + public void AddExternalTypeMapEntry(string typeName, TypeDesc type, TypeDesc trimmingTarget) + { + if (!_externalTypeMap.TryAdd(typeName, (type, trimmingTarget))) + { + ThrowHelper.ThrowBadImageFormatException(); + } + } + + public void SetExternalTypeMapStub(ThrowingMethodStub stub) + { + if (_externalTypeMapExceptionStub?.Exception is TypeSystemException.FileNotFoundException) + { + // FileNotFound exception takes precedence. + return; + } + _externalTypeMapExceptionStub ??= stub; + } + + public void SetAssociatedTypeMapStub(ThrowingMethodStub stub) + { + if (_associatedTypeMapExceptionStub?.Exception is TypeSystemException.FileNotFoundException) + { + // FileNotFound exception takes precedence. + return; + } + _associatedTypeMapExceptionStub ??= stub; + } + + public object GetExternalTypeMapNode(TypeDesc group) + { + if (_externalTypeMapExceptionStub is not null) + { + return new InvalidExternalTypeMapNode(group, _externalTypeMapExceptionStub); + } + return new ExternalTypeMapNode(group, _externalTypeMap); + } + } + + private sealed class ThrowingMethodStub(TypeDesc owningType, TypeSystemException ex) : ILStubMethod + { + public TypeSystemException Exception { get; } = ex; + + public override string Name => "InvalidTypeMapStub"; + public override MethodIL EmitIL() + { + return TypeSystemThrowingILEmitter.EmitIL(this, Exception); + } + + protected override int CompareToImpl(MethodDesc other, TypeSystemComparer comparer) => other is ThrowingMethodStub otherStub ? Exception.Message.CompareTo(otherStub.Exception.Message, StringComparison.Ordinal) : -1; + + public override bool IsPInvoke => false; + + public override string DiagnosticName => "InvalidTypeMap"; + + protected override int ClassCode => 1744789196; + + public override TypeDesc OwningType => owningType; + + public override MethodSignature Signature => new MethodSignature(MethodSignatureFlags.Static, 0, Context.GetWellKnownType(WellKnownType.Void), []); + + public override TypeSystemContext Context => owningType.Context; + } + + private Dictionary _typeMapStates = new Dictionary(); + private Dictionary _exceptionStubs = new Dictionary(); + + private enum TypeMapAttributeKind + { + None, + TypeMapAssemblyTarget, + TypeMap, + TypeMapAssociation + } + + private ModuleDesc _entryModule; + + public TypeMapManager(ModuleDesc entryModule) + { + _entryModule = entryModule; + + if (entryModule is not { Assembly: EcmaAssembly assembly }) + { + // We can only process EcmaAssembly-based modules as we can only read custom attributes from them. + return; + } + + HashSet scannedAssemblies = []; + + Queue assembliesToScan = new Queue(); + assembliesToScan.Enqueue(assembly); + + TypeDesc typeMapAssemblyTargetType = assembly.Context.SystemModule.GetTypeByCustomAttributeTypeName("System.Runtime.InteropServices.TypeMapAssemblyTargetAttribute`1"); + + while (assembliesToScan.Count > 0) + { + EcmaAssembly currentAssembly = assembliesToScan.Dequeue(); + if (scannedAssemblies.Contains(currentAssembly)) + continue; + + scannedAssemblies.Add(currentAssembly); + + foreach (CustomAttributeHandle attrHandle in assembly.MetadataReader.GetCustomAttributes(EntityHandle.AssemblyDefinition)) + { + CustomAttribute attr = assembly.MetadataReader.GetCustomAttribute(attrHandle); + + if (!MetadataExtensions.GetAttributeTypeAndConstructor(assembly.MetadataReader, attrHandle, out EntityHandle attributeType, out _)) + { + continue; + } + + TypeDesc type = (TypeDesc)assembly.GetObject(attributeType); + + TypeMapAttributeKind attrKind = TypeMapAttributeKind.None; + + if (typeMapAssemblyTargetType == type.GetTypeDefinition()) + { + attrKind = TypeMapAttributeKind.TypeMapAssemblyTarget; + } + + if (attrKind == TypeMapAttributeKind.None) + { + continue; + } + + CustomAttributeValue attrValue = attr.DecodeValue(new CustomAttributeTypeProvider(currentAssembly)); + + TypeDesc typeMapGroup = type.Instantiation[0]; + + try + { + switch (attrKind) + { + case TypeMapAttributeKind.TypeMapAssemblyTarget: + ProcessTypeMapAssemblyTargetAttribute(attrValue, typeMapGroup); + break; + + case TypeMapAttributeKind.TypeMap: + ProcessTypeMapAttribute(attrValue, typeMapGroup); + break; + + case TypeMapAttributeKind.TypeMapAssociation: + ProcessTypeMapAssociationAttribute(attrValue, typeMapGroup); + break; + + default: + break; + } + } + catch (TypeSystemException ex) + { + if (!_exceptionStubs.TryGetValue(ex, out ThrowingMethodStub stub)) + { + _exceptionStubs[ex] = stub = new ThrowingMethodStub(typeMapGroup, ex); + } + _typeMapStates[typeMapGroup] ??= new TypeMapState(); + if (attrKind is TypeMapAttributeKind.TypeMapAssemblyTarget or TypeMapAttributeKind.TypeMap) + { + _typeMapStates[typeMapGroup].SetExternalTypeMapStub(stub); + } + + if (attrKind is TypeMapAttributeKind.TypeMapAssemblyTarget or TypeMapAttributeKind.TypeMapAssociation) + { + _typeMapStates[typeMapGroup].SetAssociatedTypeMapStub(stub); + } + } + } + + void ProcessTypeMapAssemblyTargetAttribute(CustomAttributeValue attrValue, TypeDesc typeMapGroup) + { + // If attribute is TypeMapAssemblyTargetAttribute, we need to extract the generic argument (type map group) + // and process it. + if (attrValue.FixedArguments is not [{ Value: string assemblyName }]) + { + ThrowHelper.ThrowBadImageFormatException(); + return; + } + + EcmaAssembly targetAssembly = (EcmaAssembly)assembly.Context.ResolveAssembly(AssemblyNameInfo.Parse(assemblyName), throwIfNotFound: true); + + assembliesToScan.Enqueue(targetAssembly); + } + + void ProcessTypeMapAttribute(CustomAttributeValue attrValue, TypeDesc typeMapGroup) + { + switch (attrValue.FixedArguments) + { + case [{ Value: string typeName }, { Value: TypeDesc targetType }]: + { + if (!_typeMapStates.TryGetValue(typeMapGroup, out TypeMapState typeMapState)) + { + _typeMapStates[typeMapGroup] = typeMapState = new TypeMapState(); + } + typeMapState.AddExternalTypeMapEntry(typeName, targetType, targetType); + break; + } + + case [{ Value: string typeName }, { Value: TypeDesc targetType }, { Value: TypeDesc trimTargetType }]: + { + if (!_typeMapStates.TryGetValue(typeMapGroup, out TypeMapState typeMapState)) + { + _typeMapStates[typeMapGroup] = typeMapState = new TypeMapState(); + } + typeMapState.AddExternalTypeMapEntry(typeName, targetType, targetType); + break; + } + + default: + ThrowHelper.ThrowBadImageFormatException(); + return; + } + } + + void ProcessTypeMapAssociationAttribute(CustomAttributeValue attrValue, TypeDesc typeMapGroup) + { + // If attribute is TypeMapAssociationAttribute, we need to extract the generic argument (type map group) + // and process it. + if (attrValue.FixedArguments is not [{ Value: TypeDesc type }, { Value: TypeDesc associatedType }]) + { + ThrowHelper.ThrowBadImageFormatException(); + return; + } + + if (!_typeMapStates.TryGetValue(typeMapGroup, out TypeMapState typeMapState)) + { + _typeMapStates[typeMapGroup] = typeMapState = new TypeMapState(); + } + + typeMapState.AddAssociatedTypeMapEntry(type, associatedType); + } + } + } + + private sealed class TypeMapsNode(IReadOnlyDictionary typeMapState) : DependencyNodeCore + { + public override bool InterestingForDynamicDependencyAnalysis => false; + + public override bool HasDynamicDependencies => false; + + public override bool HasConditionalStaticDependencies => true; + + public override bool StaticDependenciesAreComputed => true; + + public override IEnumerable GetConditionalStaticDependencies(NodeFactory context) + { + List entries = []; + foreach ((TypeDesc typeMapGroup, TypeMapState typeMapState) in typeMapState) + { + entries.Add(new CombinedDependencyListEntry(typeMapState.GetExternalTypeMapNode(typeMapGroup), context.ExternalTypeMapRequest(typeMapGroup), "ExternalTypeMap")); + } + + return entries; + } + + 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) => "TypeMapsNode"; + } + + public void AddCompilationRoots(IRootingServiceProvider rootProvider) + { + if (_typeMapStates.Count == 0) + { + return; // No type maps to process + } + + Debug.Assert(_entryModule is not null, "We should only find type map entries if we have an entry module."); + + rootProvider.AddCompilationRoot(new TypeMapsNode(_typeMapStates), "TypeMapManager"); + } + + public void AddToReadyToRunHeader(ReadyToRunHeaderNode header, NodeFactory nodeFactory, ExternalReferencesTableNode commonFixupsTableNode) + { + if (_typeMapStates.Count == 0) + { + return; // No type maps to emit + } + + ExternalTypeMapObjectNode externalTypeMap = new ExternalTypeMapObjectNode(commonFixupsTableNode); + header.Add(MetadataManager.BlobIdToReadyToRunSection(ReflectionMapBlob.ExternalTypeMap), externalTypeMap); + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/ILCompiler.Compiler.csproj b/src/coreclr/tools/aot/ILCompiler.Compiler/ILCompiler.Compiler.csproj index 1a6f9f721f8504..012d59c00ffe92 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/ILCompiler.Compiler.csproj +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/ILCompiler.Compiler.csproj @@ -407,6 +407,9 @@ + + + @@ -439,6 +442,7 @@ + @@ -509,6 +513,7 @@ + @@ -523,6 +528,7 @@ + @@ -646,6 +652,7 @@ + diff --git a/src/coreclr/tools/aot/ILCompiler.RyuJit/Compiler/DependencyAnalysis/RyuJitNodeFactory.cs b/src/coreclr/tools/aot/ILCompiler.RyuJit/Compiler/DependencyAnalysis/RyuJitNodeFactory.cs index fa328d268a9a5e..1e75766b8f5ed3 100644 --- a/src/coreclr/tools/aot/ILCompiler.RyuJit/Compiler/DependencyAnalysis/RyuJitNodeFactory.cs +++ b/src/coreclr/tools/aot/ILCompiler.RyuJit/Compiler/DependencyAnalysis/RyuJitNodeFactory.cs @@ -11,8 +11,8 @@ public sealed class RyuJitNodeFactory : NodeFactory { public RyuJitNodeFactory(CompilerTypeSystemContext context, CompilationModuleGroup compilationModuleGroup, MetadataManager metadataManager, InteropStubManager interopStubManager, NameMangler nameMangler, VTableSliceProvider vtableSliceProvider, DictionaryLayoutProvider dictionaryLayoutProvider, InlinedThreadStatics inlinedThreadStatics, PreinitializationManager preinitializationManager, - DevirtualizationManager devirtualizationManager, ObjectDataInterner dataInterner) - : base(context, compilationModuleGroup, metadataManager, interopStubManager, nameMangler, new LazyGenericsDisabledPolicy(), vtableSliceProvider, dictionaryLayoutProvider, inlinedThreadStatics, new ExternSymbolsImportedNodeProvider(), preinitializationManager, devirtualizationManager, dataInterner) + DevirtualizationManager devirtualizationManager, ObjectDataInterner dataInterner, TypeMapManager typeMapManager) + : base(context, compilationModuleGroup, metadataManager, interopStubManager, nameMangler, new LazyGenericsDisabledPolicy(), vtableSliceProvider, dictionaryLayoutProvider, inlinedThreadStatics, new ExternSymbolsImportedNodeProvider(), preinitializationManager, devirtualizationManager, dataInterner, typeMapManager) { } diff --git a/src/coreclr/tools/aot/ILCompiler.RyuJit/Compiler/RyuJitCompilationBuilder.cs b/src/coreclr/tools/aot/ILCompiler.RyuJit/Compiler/RyuJitCompilationBuilder.cs index 7952b51e26533b..f03494489a1dde 100644 --- a/src/coreclr/tools/aot/ILCompiler.RyuJit/Compiler/RyuJitCompilationBuilder.cs +++ b/src/coreclr/tools/aot/ILCompiler.RyuJit/Compiler/RyuJitCompilationBuilder.cs @@ -131,7 +131,7 @@ public override ICompilation ToCompilation() ObjectDataInterner interner = _methodBodyFolding ? new ObjectDataInterner() : ObjectDataInterner.Null; - var factory = new RyuJitNodeFactory(_context, _compilationGroup, _metadataManager, _interopStubManager, _nameMangler, _vtableSliceProvider, _dictionaryLayoutProvider, _inlinedThreadStatics, GetPreinitializationManager(), _devirtualizationManager, interner); + var factory = new RyuJitNodeFactory(_context, _compilationGroup, _metadataManager, _interopStubManager, _nameMangler, _vtableSliceProvider, _dictionaryLayoutProvider, _inlinedThreadStatics, GetPreinitializationManager(), _devirtualizationManager, interner, _typeMapManager); JitConfigProvider.Initialize(_context.Target, jitFlagBuilder.ToArray(), _ryujitOptions, _jitPath); DependencyAnalyzerBase graph = CreateDependencyGraph(factory, new ObjectNode.ObjectNodeComparer(CompilerComparer.Instance)); diff --git a/src/coreclr/tools/aot/ILCompiler/Program.cs b/src/coreclr/tools/aot/ILCompiler/Program.cs index c8ec8eda8e8c27..1c8acda2292da5 100644 --- a/src/coreclr/tools/aot/ILCompiler/Program.cs +++ b/src/coreclr/tools/aot/ILCompiler/Program.cs @@ -192,12 +192,17 @@ public int Run() CompilationModuleGroup compilationGroup; List compilationRoots = new List(); + TypeMapManager typeMapManager = new TypeMapManager(null); bool multiFile = Get(_command.MultiFile); if (singleMethod != null) { // Compiling just a single method compilationGroup = new SingleMethodCompilationModuleGroup(singleMethod); compilationRoots.Add(new SingleMethodRootProvider(singleMethod)); + if (singleMethod.OwningType is MetadataType { Module: ModuleDesc module }) + { + compilationRoots.Add(typeMapManager = new TypeMapManager(module)); + } } else { @@ -307,6 +312,12 @@ public int Run() throw new CommandLineException($"'{linkTrimFilePath}' doesn't exist"); compilationRoots.Add(new ILCompiler.DependencyAnalysis.TrimmingDescriptorNode(linkTrimFilePath)); } + + if (entrypointModule != null) + { + compilationRoots.Add(new TypeMapManager(entrypointModule)); + compilationRoots.Add(typeMapManager = new TypeMapManager(entrypointModule)); + } } // Root whatever assemblies were specified on the command line @@ -491,6 +502,7 @@ void RunScanner() .UseMetadataManager(metadataManager) .UseParallelism(parallelism) .UseInteropStubManager(interopStubManager) + .UseTypeMapManager(typeMapManager) .UseLogger(logger); string scanDgmlLogFileName = Get(_command.ScanDgmlLogFileName); @@ -600,7 +612,8 @@ void RunScanner() .UseSecurityMitigationOptions(securityMitigationOptions) .UseDebugInfoProvider(debugInfoProvider) .UseDwarf5(Get(_command.UseDwarf5)) - .UseResilience(resilient); + .UseResilience(resilient) + .UseTypeMapManager(typeMapManager); ICompilation compilation = builder.ToCompilation(); diff --git a/src/tools/illink/src/ILLink.Shared/DiagnosticId.cs b/src/tools/illink/src/ILLink.Shared/DiagnosticId.cs index 9e65a71e1a33c7..54cdf18670ac3c 100644 --- a/src/tools/illink/src/ILLink.Shared/DiagnosticId.cs +++ b/src/tools/illink/src/ILLink.Shared/DiagnosticId.cs @@ -188,6 +188,7 @@ public enum DiagnosticId RedundantSuppression = 2121, TypeNameIsNotAssemblyQualified = 2122, RequiresUnreferencedCodeOnEntryPoint = 2123, + TypeMapGroupTypeCannotBeStaticallyDetermined = 2124, _EndTrimAnalysisWarningsSentinel, // Single-file diagnostic ids. diff --git a/src/tools/illink/src/ILLink.Shared/SharedStrings.resx b/src/tools/illink/src/ILLink.Shared/SharedStrings.resx index 920b71e7126080..40c921ca706cde 100644 --- a/src/tools/illink/src/ILLink.Shared/SharedStrings.resx +++ b/src/tools/illink/src/ILLink.Shared/SharedStrings.resx @@ -1089,6 +1089,12 @@ 'DynamicallyAccessedMemberAttribute' on '{0}' or one of its base types references compiler-generated member '{1}'. + + The provided generic argument cannot be statically determined. + + + Type '{0}' must not contain signature variables to be used as a type map group. + Avoid accessing Assembly file path when publishing as a single file diff --git a/src/tools/illink/src/ILLink.Shared/TrimAnalysis/IntrinsicId.cs b/src/tools/illink/src/ILLink.Shared/TrimAnalysis/IntrinsicId.cs index 8056970932704e..2296bc312f56fd 100644 --- a/src/tools/illink/src/ILLink.Shared/TrimAnalysis/IntrinsicId.cs +++ b/src/tools/illink/src/ILLink.Shared/TrimAnalysis/IntrinsicId.cs @@ -352,5 +352,13 @@ internal enum IntrinsicId /// /// Delegate_get_Method, + /// + /// . + /// + TypeMapping_GetOrCreateExternalTypeMapping, + /// + /// . + /// + TypeMapping_GetOrCreateProxyTypeMapping, } } diff --git a/src/tools/illink/src/ILLink.Shared/TrimAnalysis/Intrinsics.cs b/src/tools/illink/src/ILLink.Shared/TrimAnalysis/Intrinsics.cs index 2d672707ac64fc..74fe628b668c09 100644 --- a/src/tools/illink/src/ILLink.Shared/TrimAnalysis/Intrinsics.cs +++ b/src/tools/illink/src/ILLink.Shared/TrimAnalysis/Intrinsics.cs @@ -418,6 +418,18 @@ public static IntrinsicId GetIntrinsicIdForMethod (MethodProxy calledMethod) && calledMethod.HasMetadataParametersCount (0) => IntrinsicId.Delegate_get_Method, + // static System.Runtime.InteropServices.TypeMapping.GetOrCreateExternalTypeMapping () + "GetOrCreateExternalTypeMapping" when calledMethod.IsDeclaredOnType ("System.Runtime.InteropServices.TypeMapping") + && calledMethod.IsStatic () + && calledMethod.HasGenericParametersCount (1) + => IntrinsicId.TypeMapping_GetOrCreateExternalTypeMapping, + + // static System.Runtime.InteropServices.TypeMapping.GetOrCreateProxyTypeMapping () + "GetOrCreateProxyTypeMapping" when calledMethod.IsDeclaredOnType ("System.Runtime.InteropServices.TypeMapping") + && calledMethod.IsStatic () + && calledMethod.HasGenericParametersCount (1) + => IntrinsicId.TypeMapping_GetOrCreateProxyTypeMapping, + _ => IntrinsicId.None, }; } From bb84f34002f34bb374981858bca8d2d354d83f73 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Tue, 3 Jun 2025 16:38:34 -0700 Subject: [PATCH 04/45] Add first pass of external type map implementation for NAOT runtime --- .../src/CompatibilitySuppressions.xml | 54 ++++++-- .../src/System.Private.CoreLib.csproj | 4 + .../TypeMapLazyDictionary.NativeAot.cs | 123 ++++++++++++++++++ .../src/System.Private.TypeLoader.csproj | 1 - .../Common/TypeHashingAlgorithms.cs | 2 + .../ExternalTypeMapObjectNode.cs | 4 +- .../Runtime/InteropServices/TypeMapping.cs | 4 +- 7 files changed, 174 insertions(+), 18 deletions(-) create mode 100644 src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/InteropServices/TypeMapLazyDictionary.NativeAot.cs diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/CompatibilitySuppressions.xml b/src/coreclr/nativeaot/System.Private.CoreLib/src/CompatibilitySuppressions.xml index 2de459555d114d..6d8d04d8a785f7 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/CompatibilitySuppressions.xml +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/CompatibilitySuppressions.xml @@ -1,6 +1,10 @@  + + CP0001 + T:Internal.Console + CP0001 T:Internal.Metadata.NativeFormat.ArraySignature @@ -53,14 +57,6 @@ CP0001 T:Internal.Metadata.NativeFormat.ConstantBooleanValueHandle - - CP0001 - T:Internal.Metadata.NativeFormat.ConstantEnumValue - - - CP0001 - T:Internal.Metadata.NativeFormat.ConstantEnumValueHandle - CP0001 T:Internal.Metadata.NativeFormat.ConstantByteArray @@ -117,6 +113,14 @@ CP0001 T:Internal.Metadata.NativeFormat.ConstantEnumArrayHandle + + CP0001 + T:Internal.Metadata.NativeFormat.ConstantEnumValue + + + CP0001 + T:Internal.Metadata.NativeFormat.ConstantEnumValueHandle + CP0001 T:Internal.Metadata.NativeFormat.ConstantHandleArray @@ -653,6 +657,10 @@ CP0001 T:Internal.Metadata.NativeFormat.UInt64Collection + + CP0001 + T:Internal.NativeFormat.TypeHashingAlgorithms + CP0001 T:Internal.Reflection.Core.AssemblyBinder @@ -725,6 +733,10 @@ CP0001 T:Internal.TypeSystem.LockFreeReaderHashtable`2 + + CP0001 + T:Internal.TypeSystem.LockFreeReaderHashtableOfPointers`2 + CP0001 T:System.Diagnostics.DebugAnnotations @@ -735,11 +747,11 @@ CP0001 - T:System.MDArray + T:System.FieldHandleInfo CP0001 - T:System.FieldHandleInfo + T:System.MDArray CP0001 @@ -810,13 +822,29 @@ T:System.Runtime.CompilerServices.StaticClassConstructionContext - CP0001 - T:Internal.TypeSystem.LockFreeReaderHashtableOfPointers`2 + CP0002 + F:System.Resources.ResourceManager.BaseNameField + + + CP0002 + F:System.Resources.ResourceSet.Reader CP0002 M:System.Reflection.MethodBase.GetParametersAsSpan + + CP0002 + M:System.String.Trim(System.ReadOnlySpan{System.Char}) + + + CP0002 + M:System.String.TrimEnd(System.ReadOnlySpan{System.Char}) + + + CP0002 + M:System.String.TrimStart(System.ReadOnlySpan{System.Char}) + CP0002 M:System.Threading.Lock.#ctor(System.Boolean) @@ -824,5 +852,7 @@ CP0002 M:System.Diagnostics.DiagnosticMethodInfo.#ctor(System.String,System.String,System.String) + ref/net10.0/System.Private.CoreLib.dll + lib/net10.0/System.Private.CoreLib.dll \ No newline at end of file diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System.Private.CoreLib.csproj b/src/coreclr/nativeaot/System.Private.CoreLib/src/System.Private.CoreLib.csproj index 251d8bfa97ecf6..6b44f0d8a79c92 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System.Private.CoreLib.csproj +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System.Private.CoreLib.csproj @@ -196,6 +196,7 @@ + @@ -322,6 +323,9 @@ Utilities\LockFreeReaderHashtableOfPointers.cs + + Utilities\TypeHashingAlgorithms.cs + System\Collections\Generic\LowLevelList.cs 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 new file mode 100644 index 00000000000000..a82892bb4d0278 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/InteropServices/TypeMapLazyDictionary.NativeAot.cs @@ -0,0 +1,123 @@ +// 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.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Text; + +using Internal.NativeFormat; +using Internal.Runtime; +using Internal.Runtime.Augments; +using Internal.Runtime.TypeLoader; + +namespace System.Runtime.InteropServices +{ + internal static class TypeMapLazyDictionary + { + public static IReadOnlyDictionary CreateExternalTypeMap(RuntimeType typeMapGroup) + { + RuntimeTypeHandle typeMapGroupHandle = typeMapGroup.TypeHandle; + foreach (TypeManagerHandle module in RuntimeAugments.GetLoadedModules()) + { + if (!TryGetNativeReaderForBlob(module, ReflectionMapBlob.ExternalTypeMap, out NativeReader externalTypeMapReader)) + { + continue; + } + NativeParser externalTypeMapParser = new NativeParser(externalTypeMapReader, 0); + NativeHashtable externalTypeMapTable = new NativeHashtable(externalTypeMapParser); + + ExternalReferencesTable externalReferences = default; + externalReferences.InitializeCommonFixupsTable(module); + + var lookup = externalTypeMapTable.Lookup(typeMapGroupHandle.GetHashCode()); + NativeParser entryParser; + while (!(entryParser = lookup.GetNext()).IsNull) + { + RuntimeTypeHandle foundTypeMapGroup = externalReferences.GetRuntimeTypeHandleFromIndex(entryParser.GetUnsigned()); + if (!foundTypeMapGroup.Equals(typeMapGroupHandle)) + { + continue; + } + bool isValid = entryParser.GetUnsigned() == 1; + if (!isValid) + { + unsafe + { + delegate* exceptionStub = (delegate*)externalReferences.GetFunctionPointerFromIndex(entryParser.GetUnsigned()); + exceptionStub(); + Debug.Fail("Expected exception stub to throw an exception."); + return null; // Should never reach here, as the exception stub should throw an exception. + } + } + + return new ExternalTypeMapDictionary(new NativeHashtable(entryParser), externalReferences); + } + } + + return new Dictionary(); // Return an empty dictionary if no valid type map is found. + } + + private static unsafe bool TryGetNativeReaderForBlob(TypeManagerHandle module, ReflectionMapBlob blob, out NativeReader reader) + { + byte* pBlob; + uint cbBlob; + + if (RuntimeImports.RhFindBlob(module, (uint)blob, &pBlob, &cbBlob)) + { + reader = new NativeReader(pBlob, cbBlob); + return true; + } + + reader = default(NativeReader); + return false; + } + + private class ExternalTypeMapDictionary(NativeHashtable table, ExternalReferencesTable externalReferences) : IReadOnlyDictionary + { + public Type this[string key] + { + get + { + if (!TryGetValue(key, out Type? value)) + { + ThrowHelper.ThrowKeyNotFoundException(key); + } + return value; + } + } + + public bool TryGetValue(string key, [MaybeNullWhen(false)] out Type value) + { + var lookup = table.Lookup(TypeHashingAlgorithms.ComputeNameHashCode(key)); + NativeParser entryParser; + while (!(entryParser = lookup.GetNext()).IsNull) + { + string foundName = entryParser.GetString(); + if (foundName.Equals(key)) + { + RuntimeTypeHandle typeHandle = externalReferences.GetRuntimeTypeHandleFromIndex(entryParser.GetUnsigned()); + value = Type.GetTypeFromHandle(typeHandle)!; + return true; + } + } + value = null; + return false; + } + + // Not supported to avoid exposing TypeMap entries in a manner that + // would violate invariants the Trimmer is attempting to enforce. + public IEnumerable Keys => throw new NotSupportedException(); + + public IEnumerable Values => throw new NotSupportedException(); + + public int Count => throw new NotSupportedException(); + + public bool ContainsKey(string key) => throw new NotSupportedException(); + public IEnumerator> GetEnumerator() => throw new NotSupportedException(); + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.TypeLoader/src/System.Private.TypeLoader.csproj b/src/coreclr/nativeaot/System.Private.TypeLoader/src/System.Private.TypeLoader.csproj index 4ed2c0fec251ec..367dd4750479e5 100644 --- a/src/coreclr/nativeaot/System.Private.TypeLoader/src/System.Private.TypeLoader.csproj +++ b/src/coreclr/nativeaot/System.Private.TypeLoader/src/System.Private.TypeLoader.csproj @@ -216,7 +216,6 @@ - diff --git a/src/coreclr/tools/Common/TypeSystem/Common/TypeHashingAlgorithms.cs b/src/coreclr/tools/Common/TypeSystem/Common/TypeHashingAlgorithms.cs index 1b9692037fe94e..c63ec5fdddfdd4 100644 --- a/src/coreclr/tools/Common/TypeSystem/Common/TypeHashingAlgorithms.cs +++ b/src/coreclr/tools/Common/TypeSystem/Common/TypeHashingAlgorithms.cs @@ -5,12 +5,14 @@ // Generic functions to compute the hashcode value of types // --------------------------------------------------------------------------- +using System; using System.Diagnostics; using System.Runtime.CompilerServices; using System.Text; namespace Internal.NativeFormat { + [CLSCompliant(false)] public static class TypeHashingAlgorithms { public struct HashCodeBuilder diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ExternalTypeMapObjectNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ExternalTypeMapObjectNode.cs index a4955c0302c33e..b6cae13a2c43f7 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ExternalTypeMapObjectNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ExternalTypeMapObjectNode.cs @@ -51,7 +51,7 @@ public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) { TypeDesc typeMapGroup = entryNode.TypeMapGroup; typeMapHashTable = typeMapHashTables[typeMapGroup] = new VertexHashtable(); - Vertex typeMapStateVertex = writer.GetUnsignedConstant(1); // Hash table + Vertex typeMapStateVertex = writer.GetUnsignedConstant(1); // Valid type map state Vertex typeMapGroupVertex = writer.GetUnsignedConstant(externalReferences.GetIndex(factory.NecessaryTypeSymbol(typeMapGroup))); typeMapGroupHashTable.Append((uint)typeMapGroup.GetHashCode(), writer.GetTuple(typeMapGroupVertex, typeMapStateVertex, hashTableSection.Place(typeMapHashTable))); } @@ -69,7 +69,7 @@ public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) { TypeDesc typeMapGroup = invalidNode.TypeMapGroup; typeMapHashTables[typeMapGroup] = new VertexHashtable(); - Vertex typeMapStateVertex = writer.GetUnsignedConstant(0); + 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(invalidNode.ThrowingMethodStub))); typeMapGroupHashTable.Append((uint)typeMapGroup.GetHashCode(), writer.GetTuple(typeMapGroupVertex, typeMapStateVertex, throwingMethodStubVertex)); diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/TypeMapping.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/TypeMapping.cs index ff6b5b542a2f99..6561b55323f753 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/TypeMapping.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/TypeMapping.cs @@ -23,9 +23,7 @@ public static class TypeMapping [RequiresUnreferencedCode("Interop types may be removed by trimming")] public static IReadOnlyDictionary GetOrCreateExternalTypeMapping() { -#if NATIVEAOT - throw new NotImplementedException(); -#elif MONO +#if MONO throw new NotSupportedException(); #else return TypeMapLazyDictionary.CreateExternalTypeMap((RuntimeType)typeof(TTypeMapGroup)); From 650c033e59ba8a28dbe72a6b4db2e70d2f12780f Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Tue, 3 Jun 2025 16:50:01 -0700 Subject: [PATCH 05/45] Force the target type in the external type map entry to be constructable. --- .../DependencyAnalysis/ExternalTypeMapEntryNode.cs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ExternalTypeMapEntryNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ExternalTypeMapEntryNode.cs index d430ea496a2c47..76fbba482757f5 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ExternalTypeMapEntryNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ExternalTypeMapEntryNode.cs @@ -48,7 +48,13 @@ public int CompareToImpl(ISortableNode other, CompilerComparer comparer) public override IEnumerable GetConditionalStaticDependencies(NodeFactory context) => Array.Empty(); - public override IEnumerable GetStaticDependencies(NodeFactory context) => Array.Empty(); + public override IEnumerable GetStaticDependencies(NodeFactory context) + { + return [ + new DependencyListEntry(context.MaximallyConstructableType(TargetType), "Target type in external type map") + ]; + } + public override IEnumerable SearchDynamicDependencies(List> markedNodes, int firstNode, NodeFactory context) => Array.Empty(); protected override string GetName(NodeFactory context) => $"ExternalTypeMapEntryNode({Key}, {TargetType}) in group {TypeMapGroup}"; } From 1e9b6082c6e224230771c924f278ef3660d3c401 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Tue, 3 Jun 2025 17:11:56 -0700 Subject: [PATCH 06/45] First pass implementing the proxy type map --- .../TypeMapLazyDictionary.NativeAot.cs | 99 ++++++++++++++++++- .../Common/TypeHashingAlgorithms.cs | 2 + .../AssociatedTypeMapEntryNode.cs | 61 ++++++++++++ .../AssociatedTypeMapNode.cs | 41 ++++++++ .../AssociatedTypeMapObjectNode.cs | 86 ++++++++++++++++ .../ExternalTypeMapObjectNode.cs | 2 +- .../InvalidAssociatedTypeMapEntry.cs | 53 ++++++++++ .../Compiler/MetadataManager.cs | 23 +++++ .../Compiler/TypeMapManager.cs | 14 ++- .../ILCompiler.Compiler.csproj | 4 + .../Runtime/InteropServices/TypeMapping.cs | 4 +- 11 files changed, 381 insertions(+), 8 deletions(-) create mode 100644 src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/AssociatedTypeMapEntryNode.cs create mode 100644 src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/AssociatedTypeMapNode.cs create mode 100644 src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/AssociatedTypeMapObjectNode.cs create mode 100644 src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/InvalidAssociatedTypeMapEntry.cs 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 a82892bb4d0278..8cbef91f554fb1 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 @@ -60,6 +60,49 @@ public static IReadOnlyDictionary CreateExternalTypeMap(RuntimeTyp return new Dictionary(); // Return an empty dictionary if no valid type map is found. } + public static IReadOnlyDictionary CreateProxyTypeMap(RuntimeType typeMapGroup) + { + RuntimeTypeHandle typeMapGroupHandle = typeMapGroup.TypeHandle; + foreach (TypeManagerHandle module in RuntimeAugments.GetLoadedModules()) + { + if (!TryGetNativeReaderForBlob(module, ReflectionMapBlob.AssociatedTypeMap, out NativeReader externalTypeMapReader)) + { + continue; + } + NativeParser externalTypeMapParser = new NativeParser(externalTypeMapReader, 0); + NativeHashtable externalTypeMapTable = new NativeHashtable(externalTypeMapParser); + + ExternalReferencesTable externalReferences = default; + externalReferences.InitializeCommonFixupsTable(module); + + var lookup = externalTypeMapTable.Lookup(typeMapGroupHandle.GetHashCode()); + NativeParser entryParser; + while (!(entryParser = lookup.GetNext()).IsNull) + { + RuntimeTypeHandle foundTypeMapGroup = externalReferences.GetRuntimeTypeHandleFromIndex(entryParser.GetUnsigned()); + if (!foundTypeMapGroup.Equals(typeMapGroupHandle)) + { + continue; + } + bool isValid = entryParser.GetUnsigned() == 1; + if (!isValid) + { + unsafe + { + delegate* exceptionStub = (delegate*)externalReferences.GetFunctionPointerFromIndex(entryParser.GetUnsigned()); + exceptionStub(); + Debug.Fail("Expected exception stub to throw an exception."); + return null; // Should never reach here, as the exception stub should throw an exception. + } + } + + return new AssociatedTypeMapDictionary(new NativeHashtable(entryParser), externalReferences); + } + } + + return new Dictionary(); // Return an empty dictionary if no valid type map is found. + } + private static unsafe bool TryGetNativeReaderForBlob(TypeManagerHandle module, ReflectionMapBlob blob, out NativeReader reader) { byte* pBlob; @@ -71,7 +114,7 @@ private static unsafe bool TryGetNativeReaderForBlob(TypeManagerHandle module, R return true; } - reader = default(NativeReader); + reader = default; return false; } @@ -96,7 +139,7 @@ public bool TryGetValue(string key, [MaybeNullWhen(false)] out Type value) while (!(entryParser = lookup.GetNext()).IsNull) { string foundName = entryParser.GetString(); - if (foundName.Equals(key)) + if (foundName == key) { RuntimeTypeHandle typeHandle = externalReferences.GetRuntimeTypeHandleFromIndex(entryParser.GetUnsigned()); value = Type.GetTypeFromHandle(typeHandle)!; @@ -119,5 +162,57 @@ public bool TryGetValue(string key, [MaybeNullWhen(false)] out Type value) public IEnumerator> GetEnumerator() => throw new NotSupportedException(); IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); } + + private class AssociatedTypeMapDictionary(NativeHashtable table, ExternalReferencesTable externalReferences) : IReadOnlyDictionary + { + public Type this[Type key] + { + get + { + if (!TryGetValue(key, out Type? value)) + { + ThrowHelper.ThrowKeyNotFoundException(key); + } + return value; + } + } + + public bool TryGetValue(Type key, [MaybeNullWhen(false)] out Type value) + { + RuntimeTypeHandle handle = key.TypeHandle; + if (handle.IsNull) + { + value = null; + return false; + } + + var lookup = table.Lookup(handle.GetHashCode()); + NativeParser entryParser; + while (!(entryParser = lookup.GetNext()).IsNull) + { + RuntimeTypeHandle foundHandle = externalReferences.GetRuntimeTypeHandleFromIndex(entryParser.GetUnsigned()); + if (foundHandle.Equals(handle)) + { + RuntimeTypeHandle targetHandle = externalReferences.GetRuntimeTypeHandleFromIndex(entryParser.GetUnsigned()); + value = Type.GetTypeFromHandle(targetHandle)!; + return true; + } + } + value = null; + return false; + } + + // Not supported to avoid exposing TypeMap entries in a manner that + // would violate invariants the Trimmer is attempting to enforce. + public IEnumerable Keys => throw new NotSupportedException(); + + public IEnumerable Values => throw new NotSupportedException(); + + public int Count => throw new NotSupportedException(); + + public bool ContainsKey(Type key) => throw new NotSupportedException(); + public IEnumerator> GetEnumerator() => throw new NotSupportedException(); + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + } } } diff --git a/src/coreclr/tools/Common/TypeSystem/Common/TypeHashingAlgorithms.cs b/src/coreclr/tools/Common/TypeSystem/Common/TypeHashingAlgorithms.cs index c63ec5fdddfdd4..04d2857a2b0014 100644 --- a/src/coreclr/tools/Common/TypeSystem/Common/TypeHashingAlgorithms.cs +++ b/src/coreclr/tools/Common/TypeSystem/Common/TypeHashingAlgorithms.cs @@ -12,7 +12,9 @@ namespace Internal.NativeFormat { +#if SYSTEM_PRIVATE_CORELIB [CLSCompliant(false)] +#endif public static class TypeHashingAlgorithms { public struct HashCodeBuilder diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/AssociatedTypeMapEntryNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/AssociatedTypeMapEntryNode.cs new file mode 100644 index 00000000000000..2d5e8f0108ffd5 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/AssociatedTypeMapEntryNode.cs @@ -0,0 +1,61 @@ +// 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 ILCompiler.DependencyAnalysis; +using ILCompiler.DependencyAnalysisFramework; +using Internal.TypeSystem; + +namespace ILCompiler.DependencyAnalysis +{ + internal sealed class AssociatedTypeMapEntryNode(TypeDesc typeMapGroup, TypeDesc key, TypeDesc targetType) : DependencyNodeCore, ISortableNode + { + public override bool InterestingForDynamicDependencyAnalysis => true; + + public override bool HasDynamicDependencies => false; + + public override bool HasConditionalStaticDependencies => false; + + public override bool StaticDependenciesAreComputed => true; + + public TypeDesc TypeMapGroup { get; } = typeMapGroup; + + public TypeDesc Key { get; } = key; + + public TypeDesc TargetType { get; } = targetType; + + public int ClassCode => 779513676; + + public int CompareToImpl(ISortableNode other, CompilerComparer comparer) + { + AssociatedTypeMapEntryNode otherEntry = (AssociatedTypeMapEntryNode)other; + if (TypeMapGroup != otherEntry.TypeMapGroup) + { + return comparer.Compare(TypeMapGroup, otherEntry.TypeMapGroup); + } + else if (Key != otherEntry.Key) + { + return comparer.Compare(Key, otherEntry.Key); + } + else if (TargetType != otherEntry.TargetType) + { + return comparer.Compare(TargetType, otherEntry.TargetType); + } + + return 0; + } + + public override IEnumerable GetConditionalStaticDependencies(NodeFactory context) => Array.Empty(); + + public override IEnumerable GetStaticDependencies(NodeFactory context) + { + return [ + new DependencyListEntry(context.MaximallyConstructableType(TargetType), "Target type in associated type map") + ]; + } + + public override IEnumerable SearchDynamicDependencies(List> markedNodes, int firstNode, NodeFactory context) => Array.Empty(); + protected override string GetName(NodeFactory context) => $"AssociatedTypeMapEntryNode({Key}, {TargetType}) in group {TypeMapGroup}"; + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/AssociatedTypeMapNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/AssociatedTypeMapNode.cs new file mode 100644 index 00000000000000..cc33512025b61d --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/AssociatedTypeMapNode.cs @@ -0,0 +1,41 @@ +// 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.DependencyAnalysisFramework; +using Internal.TypeSystem; + +namespace ILCompiler.DependencyAnalysis +{ + internal sealed class AssociatedTypeMapNode(TypeDesc typeMapGroup, IEnumerable> mapEntries) : DependencyNodeCore + { + public override bool InterestingForDynamicDependencyAnalysis => false; + + public override bool HasDynamicDependencies => false; + + public override bool HasConditionalStaticDependencies => true; + + public override bool StaticDependenciesAreComputed => true; + + public override IEnumerable GetConditionalStaticDependencies(NodeFactory context) + { + List entries = []; + foreach (var (key, value) in mapEntries) + { + entries.Add(new CombinedDependencyListEntry( + new AssociatedTypeMapEntryNode(typeMapGroup, key, value), + context.MaximallyConstructableType(key), + "Type in associated map may be constructed")); + } + + return entries; + } + + 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) => "External type map"; + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/AssociatedTypeMapObjectNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/AssociatedTypeMapObjectNode.cs new file mode 100644 index 00000000000000..83a0217769a67c --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/AssociatedTypeMapObjectNode.cs @@ -0,0 +1,86 @@ +// 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.Diagnostics; +using ILCompiler.DependencyAnalysis; +using ILCompiler.DependencyAnalysisFramework; +using Internal.NativeFormat; +using Internal.Text; +using Internal.TypeSystem; + +namespace ILCompiler.DependencyAnalysis +{ + internal sealed class AssociatedTypeMapObjectNode(ExternalReferencesTableNode externalReferences) : ObjectNode, ISymbolDefinitionNode, INodeWithSize + { + public void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) + { + sb.Append(nameMangler.CompilationUnitPrefix).Append("__associated_type_map__"u8); + } + + public int Size { get; private set; } + public int Offset => 0; + public override bool IsShareable => false; + public override ObjectNodeSection GetSection(NodeFactory factory) => externalReferences.GetSection(factory); + + public override bool HasDynamicDependencies => true; + + public override int ClassCode => 2090746844; + + public override bool StaticDependenciesAreComputed => true; + + protected override string GetName(NodeFactory context) => "Associated Type Map"; + public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) + { + // This node does not trigger generation of other nodes. + if (relocsOnly) + return new ObjectData(Array.Empty(), Array.Empty(), 1, [this]); + + var writer = new NativeWriter(); + var typeMapGroupHashTable = new VertexHashtable(); + + Dictionary typeMapHashTables = new(); + + Section hashTableSection = writer.NewSection(); + hashTableSection.Place(typeMapGroupHashTable); + + foreach (AssociatedTypeMapEntryNode entryNode in factory.MetadataManager.GetAssociatedTypeMapEntries()) + { + if (!typeMapHashTables.TryGetValue(entryNode.TypeMapGroup, out VertexHashtable typeMapHashTable)) + { + TypeDesc typeMapGroup = entryNode.TypeMapGroup; + typeMapHashTable = typeMapHashTables[typeMapGroup] = new VertexHashtable(); + Vertex typeMapStateVertex = writer.GetUnsignedConstant(1); // Valid type map state + Vertex typeMapGroupVertex = writer.GetUnsignedConstant(externalReferences.GetIndex(factory.NecessaryTypeSymbol(typeMapGroup))); + typeMapGroupHashTable.Append((uint)typeMapGroup.GetHashCode(), writer.GetTuple(typeMapGroupVertex, typeMapStateVertex, hashTableSection.Place(typeMapHashTable))); + } + + Vertex nameVertex = writer.GetUnsignedConstant(externalReferences.GetIndex(factory.MaximallyConstructableType(entryNode.Key))); + Vertex targetTypeVertex = writer.GetUnsignedConstant(externalReferences.GetIndex(factory.MaximallyConstructableType(entryNode.TargetType))); + Vertex entry = writer.GetTuple(nameVertex, targetTypeVertex); + + typeMapHashTable.Append((uint)entryNode.Key.GetHashCode(), hashTableSection.Place(entry)); + } + + foreach (InvalidAssociatedTypeMapNode invalidNode in factory.MetadataManager.GetInvalidAssociatedTypeMaps()) + { + if (!typeMapHashTables.TryGetValue(invalidNode.TypeMapGroup, out _)) + { + TypeDesc typeMapGroup = invalidNode.TypeMapGroup; + typeMapHashTables[typeMapGroup] = new VertexHashtable(); + 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(invalidNode.ThrowingMethodStub))); + typeMapGroupHashTable.Append((uint)typeMapGroup.GetHashCode(), writer.GetTuple(typeMapGroupVertex, typeMapStateVertex, throwingMethodStubVertex)); + } + } + + byte[] hashTableBytes = writer.Save(); + + Size = hashTableBytes.Length; + + return new ObjectData(hashTableBytes, Array.Empty(), 1, [this]); + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ExternalTypeMapObjectNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ExternalTypeMapObjectNode.cs index b6cae13a2c43f7..3e42f8d247d123 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ExternalTypeMapObjectNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ExternalTypeMapObjectNode.cs @@ -30,7 +30,7 @@ public void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) public override bool StaticDependenciesAreComputed => true; - protected override string GetName(NodeFactory context) => $"External Type Map"; + protected override string GetName(NodeFactory context) => "External Type Map"; public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) { // This node does not trigger generation of other nodes. diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/InvalidAssociatedTypeMapEntry.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/InvalidAssociatedTypeMapEntry.cs new file mode 100644 index 00000000000000..c74b8c6ed6b036 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/InvalidAssociatedTypeMapEntry.cs @@ -0,0 +1,53 @@ +// 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.TypeSystem; + +namespace ILCompiler.DependencyAnalysis +{ + internal sealed class InvalidAssociatedTypeMapNode(TypeDesc typeMapGroup, MethodDesc throwingMethodStub) : DependencyNodeCore, ISortableNode + { + public override bool InterestingForDynamicDependencyAnalysis => false; + + public override bool HasDynamicDependencies => false; + + public override bool HasConditionalStaticDependencies => false; + + public override bool StaticDependenciesAreComputed => true; + + public override IEnumerable GetConditionalStaticDependencies(NodeFactory context) => Array.Empty(); + public override IEnumerable GetStaticDependencies(NodeFactory context) + { + return [ + new DependencyListEntry(context.MethodEntrypoint(ThrowingMethodStub), "Throwing method stub for invalid type map"), + ]; + } + + public override IEnumerable SearchDynamicDependencies(List> markedNodes, int firstNode, NodeFactory context) => Array.Empty(); + protected override string GetName(NodeFactory context) => "InvalidAssociatedTypeMapNode"; + + public TypeDesc TypeMapGroup { get; } = typeMapGroup; + public MethodDesc ThrowingMethodStub { get; } = throwingMethodStub; + + public int ClassCode => 36910224; + + public int CompareToImpl(ISortableNode other, CompilerComparer comparer) + { + if (other is InvalidExternalTypeMapNode otherNode) + { + int result = comparer.Compare(TypeMapGroup, otherNode.TypeMapGroup); + if (result != 0) + return result; + return comparer.Compare(ThrowingMethodStub, otherNode.ThrowingMethodStub); + } + else + { + return -1; // This node is always less than any other node + } + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/MetadataManager.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/MetadataManager.cs index b6cab4cc0bb00e..86a43fcefef611 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/MetadataManager.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/MetadataManager.cs @@ -84,6 +84,8 @@ private readonly SortedSet _typeGVMEntries private readonly HashSet _usedInterfaces = new HashSet(); private readonly SortedSet _externalTypeMapEntries = new SortedSet(CompilerComparer.Instance); private readonly SortedSet _invalidExternalTypeMaps = new SortedSet(CompilerComparer.Instance); + private readonly SortedSet _associatedTypeMapEntries = new SortedSet(CompilerComparer.Instance); + private readonly SortedSet _invalidAssociatedTypeMaps = new SortedSet(CompilerComparer.Instance); private List<(DehydratableObjectNode Node, ObjectNode.ObjectData Data)> _dehydratableData = new List<(DehydratableObjectNode Node, ObjectNode.ObjectData data)>(); @@ -359,6 +361,17 @@ protected virtual void Graph_NewMarkedNode(DependencyNodeCore obj) { _invalidExternalTypeMaps.Add(invalidExternalTypeMapNode); } + + + if (obj is AssociatedTypeMapEntryNode associatedTypeMapEntryNode) + { + _associatedTypeMapEntries.Add(associatedTypeMapEntryNode); + } + + if (obj is InvalidAssociatedTypeMapNode invalidAssociatedTypeMapNode) + { + _invalidAssociatedTypeMaps.Add(invalidAssociatedTypeMapNode); + } } protected virtual bool AllMethodsCanBeReflectable => false; @@ -1132,6 +1145,16 @@ internal IEnumerable GetInvalidExternalTypeMaps() return _invalidExternalTypeMaps; } + internal IEnumerable GetAssociatedTypeMapEntries() + { + return _associatedTypeMapEntries; + } + + internal IEnumerable GetInvalidAssociatedTypeMaps() + { + return _invalidAssociatedTypeMaps; + } + public bool IsReflectionBlocked(TypeDesc type) { switch (type.Category) diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/TypeMapManager.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/TypeMapManager.cs index 933642159fcf93..4ba9679d693036 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/TypeMapManager.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/TypeMapManager.cs @@ -72,6 +72,15 @@ public object GetExternalTypeMapNode(TypeDesc group) } return new ExternalTypeMapNode(group, _externalTypeMap); } + + public object GetAssociatedTypeMapNode(TypeDesc group) + { + if (_associatedTypeMapExceptionStub is not null) + { + return new InvalidAssociatedTypeMapNode(group, _associatedTypeMapExceptionStub); + } + return new AssociatedTypeMapNode(group, _associatedTypeMap); + } } private sealed class ThrowingMethodStub(TypeDesc owningType, TypeSystemException ex) : ILStubMethod @@ -284,6 +293,7 @@ public override IEnumerable GetConditionalStaticDep foreach ((TypeDesc typeMapGroup, TypeMapState typeMapState) in typeMapState) { entries.Add(new CombinedDependencyListEntry(typeMapState.GetExternalTypeMapNode(typeMapGroup), context.ExternalTypeMapRequest(typeMapGroup), "ExternalTypeMap")); + entries.Add(new CombinedDependencyListEntry(typeMapState.GetAssociatedTypeMapNode(typeMapGroup), context.ProxyTypeMapRequest(typeMapGroup), "ProxyTypeMap")); } return entries; @@ -313,8 +323,8 @@ public void AddToReadyToRunHeader(ReadyToRunHeaderNode header, NodeFactory nodeF return; // No type maps to emit } - ExternalTypeMapObjectNode externalTypeMap = new ExternalTypeMapObjectNode(commonFixupsTableNode); - header.Add(MetadataManager.BlobIdToReadyToRunSection(ReflectionMapBlob.ExternalTypeMap), externalTypeMap); + header.Add(MetadataManager.BlobIdToReadyToRunSection(ReflectionMapBlob.ExternalTypeMap), new ExternalTypeMapObjectNode(commonFixupsTableNode)); + header.Add(MetadataManager.BlobIdToReadyToRunSection(ReflectionMapBlob.AssociatedTypeMap), new AssociatedTypeMapObjectNode(commonFixupsTableNode)); } } } diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/ILCompiler.Compiler.csproj b/src/coreclr/tools/aot/ILCompiler.Compiler/ILCompiler.Compiler.csproj index 012d59c00ffe92..d5f2c46e15edde 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/ILCompiler.Compiler.csproj +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/ILCompiler.Compiler.csproj @@ -407,8 +407,11 @@ + + + @@ -422,6 +425,7 @@ + diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/TypeMapping.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/TypeMapping.cs index 6561b55323f753..c652ef9887fcdc 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/TypeMapping.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/TypeMapping.cs @@ -41,9 +41,7 @@ public static IReadOnlyDictionary GetOrCreateExternalTypeMapping GetOrCreateProxyTypeMapping() { -#if NATIVEAOT - throw new NotImplementedException(); -#elif MONO +#if MONO throw new NotSupportedException(); #else return TypeMapLazyDictionary.CreateProxyTypeMap((RuntimeType)typeof(TTypeMapGroup)); From a619e5d79187bc7d1242bd38c550dbb3d8d0ac09 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Wed, 4 Jun 2025 08:22:41 -0700 Subject: [PATCH 07/45] Fix compiler asserts --- .../Compiler/Dataflow/MethodBodyScanner.cs | 25 ++++--------------- .../AssociatedTypeMapEntryNode.cs | 2 +- .../AssociatedTypeMapObjectNode.cs | 3 +-- .../ExternalTypeMapEntryNode.cs | 2 +- .../ExternalTypeMapObjectNode.cs | 2 -- .../ExternalTypeMapRequestNode.cs | 2 +- .../ProxyTypeMapRequestNode.cs | 4 +-- .../Compiler/TypeMapManager.cs | 23 ++++++++--------- src/coreclr/tools/aot/ILCompiler/Program.cs | 1 - 9 files changed, 22 insertions(+), 42 deletions(-) diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/MethodBodyScanner.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/MethodBodyScanner.cs index 89dc67b1f49ff9..eef3878c24461b 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/MethodBodyScanner.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/MethodBodyScanner.cs @@ -594,6 +594,7 @@ protected virtual void Scan(MethodIL methodBody, ref InterproceduralState interp case ILOpcode.ldobj: case ILOpcode.mkrefany: case ILOpcode.unbox: + case ILOpcode.unbox_any: case ILOpcode.box: case ILOpcode.neg: case ILOpcode.not: @@ -602,24 +603,12 @@ protected virtual void Scan(MethodIL methodBody, ref InterproceduralState interp reader.Skip(opcode); break; - case ILOpcode.unbox_any: - { - PopUnknown(currentStack, 1, methodBody, offset); - PushUnknown(currentStack); - - var type = (TypeDesc)methodBody.GetObject(reader.ReadILToken()); - if (!type.IsValueType) - { - goto case ILOpcode.castclass; - } - } - break; case ILOpcode.isinst: case ILOpcode.castclass: - { - var type = (TypeDesc)methodBody.GetObject(reader.ReadILToken()); - ScanCast(type); - } + // We can consider a NOP because the value doesn't change. + // It might change to NULL, but for the purposes of dataflow analysis + // it doesn't hurt much. + reader.Skip(opcode); break; case ILOpcode.ldfld: @@ -1428,9 +1417,5 @@ private void ScanLdelem( else PushUnknown(currentStack); } - - protected virtual void ScanCast(TypeDesc targetType) - { - } } } diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/AssociatedTypeMapEntryNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/AssociatedTypeMapEntryNode.cs index 2d5e8f0108ffd5..f3e8972eb59976 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/AssociatedTypeMapEntryNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/AssociatedTypeMapEntryNode.cs @@ -11,7 +11,7 @@ namespace ILCompiler.DependencyAnalysis { internal sealed class AssociatedTypeMapEntryNode(TypeDesc typeMapGroup, TypeDesc key, TypeDesc targetType) : DependencyNodeCore, ISortableNode { - public override bool InterestingForDynamicDependencyAnalysis => true; + public override bool InterestingForDynamicDependencyAnalysis => false; public override bool HasDynamicDependencies => false; diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/AssociatedTypeMapObjectNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/AssociatedTypeMapObjectNode.cs index 83a0217769a67c..bc75960dc136e2 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/AssociatedTypeMapObjectNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/AssociatedTypeMapObjectNode.cs @@ -24,9 +24,8 @@ public void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) public override bool IsShareable => false; public override ObjectNodeSection GetSection(NodeFactory factory) => externalReferences.GetSection(factory); - public override bool HasDynamicDependencies => true; - public override int ClassCode => 2090746844; + public override int ClassCode => 1146226395; public override bool StaticDependenciesAreComputed => true; diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ExternalTypeMapEntryNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ExternalTypeMapEntryNode.cs index 76fbba482757f5..14a5a75464e83a 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ExternalTypeMapEntryNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ExternalTypeMapEntryNode.cs @@ -11,7 +11,7 @@ namespace ILCompiler.DependencyAnalysis { internal sealed class ExternalTypeMapEntryNode(TypeDesc typeMapGroup, string key, TypeDesc targetType) : DependencyNodeCore, ISortableNode { - public override bool InterestingForDynamicDependencyAnalysis => true; + public override bool InterestingForDynamicDependencyAnalysis => false; public override bool HasDynamicDependencies => false; diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ExternalTypeMapObjectNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ExternalTypeMapObjectNode.cs index 3e42f8d247d123..147031f0c8c407 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ExternalTypeMapObjectNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ExternalTypeMapObjectNode.cs @@ -24,8 +24,6 @@ public void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) public override bool IsShareable => false; public override ObjectNodeSection GetSection(NodeFactory factory) => externalReferences.GetSection(factory); - public override bool HasDynamicDependencies => true; - public override int ClassCode => 2090746844; public override bool StaticDependenciesAreComputed => true; diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ExternalTypeMapRequestNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ExternalTypeMapRequestNode.cs index 71a8cfd1bbfbd0..53a0960e800c45 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ExternalTypeMapRequestNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ExternalTypeMapRequestNode.cs @@ -17,7 +17,7 @@ public sealed class ExternalTypeMapRequestNode(TypeDesc typeMapGroup) : Dependen public override bool HasConditionalStaticDependencies => false; - public override bool StaticDependenciesAreComputed => false; + public override bool StaticDependenciesAreComputed => true; public TypeDesc TypeMapGroup { get; } = typeMapGroup; diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ProxyTypeMapRequestNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ProxyTypeMapRequestNode.cs index 3ec77118781e13..625fe9a88a6583 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ProxyTypeMapRequestNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ProxyTypeMapRequestNode.cs @@ -17,13 +17,13 @@ public sealed class ProxyTypeMapRequestNode(TypeDesc typeMapGroup) : DependencyN public override bool HasConditionalStaticDependencies => false; - public override bool StaticDependenciesAreComputed => false; + public override bool StaticDependenciesAreComputed => true; public TypeDesc TypeMapGroup { get; } = typeMapGroup; public override IEnumerable GetConditionalStaticDependencies(NodeFactory context) => []; public override IEnumerable GetStaticDependencies(NodeFactory context) => []; public override IEnumerable SearchDynamicDependencies(List> markedNodes, int firstNode, NodeFactory context) => []; - protected override string GetName(NodeFactory context) => $"ExternalTypeMapRequestNode({TypeMapGroup})"; + protected override string GetName(NodeFactory context) => $"ProxyTypeMapRequestNode({TypeMapGroup})"; } } diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/TypeMapManager.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/TypeMapManager.cs index 4ba9679d693036..ac855900a08a58 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/TypeMapManager.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/TypeMapManager.cs @@ -83,21 +83,20 @@ public object GetAssociatedTypeMapNode(TypeDesc group) } } - private sealed class ThrowingMethodStub(TypeDesc owningType, TypeSystemException ex) : ILStubMethod + private sealed class ThrowingMethodStub(TypeDesc owningType, TypeDesc typeMapGroup, bool externalTypeMap, TypeSystemException ex) : ILStubMethod { - public TypeSystemException Exception { get; } = ex; - - public override string Name => "InvalidTypeMapStub"; + public TypeSystemException Exception => ex; + public override string Name => $"InvalidTypeMapStub_{typeMapGroup}_{(externalTypeMap ? "External" : "Proxy")}"; public override MethodIL EmitIL() { return TypeSystemThrowingILEmitter.EmitIL(this, Exception); } - protected override int CompareToImpl(MethodDesc other, TypeSystemComparer comparer) => other is ThrowingMethodStub otherStub ? Exception.Message.CompareTo(otherStub.Exception.Message, StringComparison.Ordinal) : -1; + protected override int CompareToImpl(MethodDesc other, TypeSystemComparer comparer) => other is ThrowingMethodStub otherStub ? Name.CompareTo(otherStub.Name) : -1; public override bool IsPInvoke => false; - public override string DiagnosticName => "InvalidTypeMap"; + public override string DiagnosticName => Name; protected override int ClassCode => 1744789196; @@ -109,7 +108,6 @@ public override MethodIL EmitIL() } private Dictionary _typeMapStates = new Dictionary(); - private Dictionary _exceptionStubs = new Dictionary(); private enum TypeMapAttributeKind { @@ -195,19 +193,20 @@ public TypeMapManager(ModuleDesc entryModule) } catch (TypeSystemException ex) { - if (!_exceptionStubs.TryGetValue(ex, out ThrowingMethodStub stub)) + if (!_typeMapStates.TryGetValue(typeMapGroup, out TypeMapState value)) { - _exceptionStubs[ex] = stub = new ThrowingMethodStub(typeMapGroup, ex); + value = new TypeMapState(); + _typeMapStates[typeMapGroup] = value; } - _typeMapStates[typeMapGroup] ??= new TypeMapState(); + if (attrKind is TypeMapAttributeKind.TypeMapAssemblyTarget or TypeMapAttributeKind.TypeMap) { - _typeMapStates[typeMapGroup].SetExternalTypeMapStub(stub); + value.SetExternalTypeMapStub(new ThrowingMethodStub(typeMapGroup, typeMapGroup, externalTypeMap: true, ex)); } if (attrKind is TypeMapAttributeKind.TypeMapAssemblyTarget or TypeMapAttributeKind.TypeMapAssociation) { - _typeMapStates[typeMapGroup].SetAssociatedTypeMapStub(stub); + value.SetAssociatedTypeMapStub(new ThrowingMethodStub(typeMapGroup, typeMapGroup, externalTypeMap: false, ex)); } } } diff --git a/src/coreclr/tools/aot/ILCompiler/Program.cs b/src/coreclr/tools/aot/ILCompiler/Program.cs index 1c8acda2292da5..0ccd3afacc47ba 100644 --- a/src/coreclr/tools/aot/ILCompiler/Program.cs +++ b/src/coreclr/tools/aot/ILCompiler/Program.cs @@ -315,7 +315,6 @@ public int Run() if (entrypointModule != null) { - compilationRoots.Add(new TypeMapManager(entrypointModule)); compilationRoots.Add(typeMapManager = new TypeMapManager(entrypointModule)); } } From 2a5531e3a82ba6bbbce338066e5ca4258f5bbb9b Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Wed, 4 Jun 2025 08:50:07 -0700 Subject: [PATCH 08/45] Don't root type map custom attributes --- .../Compiler/TypeMapManager.cs | 53 ++++++++++++------- .../Compiler/UsageBasedMetadataManager.cs | 4 ++ 2 files changed, 37 insertions(+), 20 deletions(-) diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/TypeMapManager.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/TypeMapManager.cs index ac855900a08a58..d9100cf24714ff 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/TypeMapManager.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/TypeMapManager.cs @@ -109,7 +109,7 @@ public override MethodIL EmitIL() private Dictionary _typeMapStates = new Dictionary(); - private enum TypeMapAttributeKind + public enum TypeMapAttributeKind { None, TypeMapAssemblyTarget, @@ -117,12 +117,32 @@ private enum TypeMapAttributeKind TypeMapAssociation } - private ModuleDesc _entryModule; + public static TypeMapAttributeKind LookupTypeMapType(TypeDesc attrType) + { + TypeDesc typeMapAssemblyTargetType = attrType.Context.SystemModule.GetTypeByCustomAttributeTypeName("System.Runtime.InteropServices.TypeMapAssemblyTargetAttribute`1"); + TypeDesc typeMapType = attrType.Context.SystemModule.GetTypeByCustomAttributeTypeName("System.Runtime.InteropServices.TypeMapAttribute`1"); + TypeDesc typeMapAssociationType = attrType.Context.SystemModule.GetTypeByCustomAttributeTypeName("System.Runtime.InteropServices.TypeMapAssociationAttribute`1"); + + if (typeMapAssemblyTargetType == attrType.GetTypeDefinition()) + { + return TypeMapAttributeKind.TypeMapAssemblyTarget; + } + + if (typeMapType == attrType.GetTypeDefinition()) + { + return TypeMapAttributeKind.TypeMap; + } + + if (typeMapAssociationType == attrType.GetTypeDefinition()) + { + return TypeMapAttributeKind.TypeMapAssociation; + } + + return TypeMapAttributeKind.None; + } public TypeMapManager(ModuleDesc entryModule) { - _entryModule = entryModule; - if (entryModule is not { Assembly: EcmaAssembly assembly }) { // We can only process EcmaAssembly-based modules as we can only read custom attributes from them. @@ -134,8 +154,6 @@ public TypeMapManager(ModuleDesc entryModule) Queue assembliesToScan = new Queue(); assembliesToScan.Enqueue(assembly); - TypeDesc typeMapAssemblyTargetType = assembly.Context.SystemModule.GetTypeByCustomAttributeTypeName("System.Runtime.InteropServices.TypeMapAssemblyTargetAttribute`1"); - while (assembliesToScan.Count > 0) { EcmaAssembly currentAssembly = assembliesToScan.Dequeue(); @@ -144,26 +162,22 @@ public TypeMapManager(ModuleDesc entryModule) scannedAssemblies.Add(currentAssembly); - foreach (CustomAttributeHandle attrHandle in assembly.MetadataReader.GetCustomAttributes(EntityHandle.AssemblyDefinition)) + foreach (CustomAttributeHandle attrHandle in currentAssembly.MetadataReader.GetCustomAttributes(EntityHandle.AssemblyDefinition)) { - CustomAttribute attr = assembly.MetadataReader.GetCustomAttribute(attrHandle); + CustomAttribute attr = currentAssembly.MetadataReader.GetCustomAttribute(attrHandle); - if (!MetadataExtensions.GetAttributeTypeAndConstructor(assembly.MetadataReader, attrHandle, out EntityHandle attributeType, out _)) + if (!MetadataExtensions.GetAttributeTypeAndConstructor(currentAssembly.MetadataReader, attrHandle, out EntityHandle attributeType, out _)) { continue; } - TypeDesc type = (TypeDesc)assembly.GetObject(attributeType); + TypeDesc type = (TypeDesc)currentAssembly.GetObject(attributeType); - TypeMapAttributeKind attrKind = TypeMapAttributeKind.None; - - if (typeMapAssemblyTargetType == type.GetTypeDefinition()) - { - attrKind = TypeMapAttributeKind.TypeMapAssemblyTarget; - } + TypeMapAttributeKind attrKind = LookupTypeMapType(type); if (attrKind == TypeMapAttributeKind.None) { + // Not a type map attribute, skip it continue; } @@ -188,6 +202,7 @@ public TypeMapManager(ModuleDesc entryModule) break; default: + Debug.Fail($"Unexpected TypeMapAttributeKind: {attrKind}"); break; } } @@ -201,12 +216,12 @@ public TypeMapManager(ModuleDesc entryModule) if (attrKind is TypeMapAttributeKind.TypeMapAssemblyTarget or TypeMapAttributeKind.TypeMap) { - value.SetExternalTypeMapStub(new ThrowingMethodStub(typeMapGroup, typeMapGroup, externalTypeMap: true, ex)); + value.SetExternalTypeMapStub(new ThrowingMethodStub(entryModule.GetGlobalModuleType(), typeMapGroup, externalTypeMap: true, ex)); } if (attrKind is TypeMapAttributeKind.TypeMapAssemblyTarget or TypeMapAttributeKind.TypeMapAssociation) { - value.SetAssociatedTypeMapStub(new ThrowingMethodStub(typeMapGroup, typeMapGroup, externalTypeMap: false, ex)); + value.SetAssociatedTypeMapStub(new ThrowingMethodStub(entryModule.GetGlobalModuleType(), typeMapGroup, externalTypeMap: false, ex)); } } } @@ -310,8 +325,6 @@ public void AddCompilationRoots(IRootingServiceProvider rootProvider) return; // No type maps to process } - Debug.Assert(_entryModule is not null, "We should only find type map entries if we have an entry module."); - rootProvider.AddCompilationRoot(new TypeMapsNode(_typeMapStates), "TypeMapManager"); } diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/UsageBasedMetadataManager.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/UsageBasedMetadataManager.cs index 6cf4f7249e9112..38cc6e7ed7217d 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/UsageBasedMetadataManager.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/UsageBasedMetadataManager.cs @@ -824,6 +824,10 @@ public override DependencyList GetDependenciesForCustomAttribute(NodeFactory fac public bool GeneratesAttributeMetadata(TypeDesc attributeType) { + if (TypeMapManager.LookupTypeMapType(attributeType) != TypeMapManager.TypeMapAttributeKind.None) + { + return false; + } var ecmaType = attributeType.GetTypeDefinition() as EcmaType; if (ecmaType != null) { From c7f36387e6db7a648ddf86df8c5d6909bb68d133 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Wed, 4 Jun 2025 08:50:28 -0700 Subject: [PATCH 09/45] Provide a mechanism to specify NativeAOT-only test apps --- eng/testing/linker/trimmingTests.targets | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/eng/testing/linker/trimmingTests.targets b/eng/testing/linker/trimmingTests.targets index a4845dd24f63ae..3e00d10299eeee 100644 --- a/eng/testing/linker/trimmingTests.targets +++ b/eng/testing/linker/trimmingTests.targets @@ -40,6 +40,7 @@ <_SkippedAppSourceFiles Include="@(TestConsoleAppSourceFiles)" Condition="$([System.String]::Copy('%(TestConsoleAppSourceFiles.SkipOnTestRuntimes)').Contains('$(OutputRID)'))" /> <_SkippedAppSourceFiles Include="@(TestConsoleAppSourceFiles)" Condition="'$(RunNativeAotTestApps)' == 'true' and '%(TestConsoleAppSourceFiles.NativeAotIncompatible)' == 'true'" /> + <_SkippedAppSourceFiles Include="@(TestConsoleAppSourceFiles)" Condition="'$(RunNativeAotTestApps)' != 'true' and '%(TestConsoleAppSourceFiles.NativeAotOnly)' == 'true'" /> <_AppSourceFiles Include="@(TestConsoleAppSourceFiles)" Exclude="@(_SkippedAppSourceFiles)" /> @@ -165,7 +166,7 @@ - + From 847760b6f8609f622d01895c20c0d7a4bc199741 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Wed, 4 Jun 2025 16:57:06 -0700 Subject: [PATCH 10/45] Fix format writing so it actually works --- .../SortableDependencyNode.cs | 2 + .../NativeFormat/NativeFormatWriter.cs | 26 +++- .../Compiler/AnalysisBasedMetadataManager.cs | 19 ++- .../AssociatedTypeMapObjectNode.cs | 32 ++--- .../ExternalTypeMapObjectNode.cs | 29 +++-- .../DependencyAnalysis/TypeCastTargetNode.cs | 52 -------- .../Compiler/IRootingServiceProvider.cs | 2 + .../Compiler/RootingServiceProvider.cs | 10 ++ .../Compiler/UsageBasedMetadataManager.cs | 14 ++- .../ILCompiler.Compiler.csproj | 1 - ...Runtime.InteropServices.TrimmingTests.proj | 3 + .../tests/TrimmingTests/TypeMap.cs | 112 ++++++++++++++++++ 12 files changed, 211 insertions(+), 91 deletions(-) delete mode 100644 src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/TypeCastTargetNode.cs create mode 100644 src/libraries/System.Runtime.InteropServices/tests/TrimmingTests/TypeMap.cs diff --git a/src/coreclr/tools/Common/Compiler/DependencyAnalysis/SortableDependencyNode.cs b/src/coreclr/tools/Common/Compiler/DependencyAnalysis/SortableDependencyNode.cs index ffb7f9e789fd3a..5238d0baca0593 100644 --- a/src/coreclr/tools/Common/Compiler/DependencyAnalysis/SortableDependencyNode.cs +++ b/src/coreclr/tools/Common/Compiler/DependencyAnalysis/SortableDependencyNode.cs @@ -95,6 +95,8 @@ protected enum ObjectNodeOrder StaticsInfoHashtableNode, ReflectionVirtualInvokeMapNode, ArrayOfEmbeddedPointersNode, + ExternalTypeMapObjectNode, + AssociatedTypeMapObjectNode, ExternalReferencesTableNode, StackTraceEmbeddedMetadataNode, StackTraceMethodMappingNode, diff --git a/src/coreclr/tools/Common/Internal/NativeFormat/NativeFormatWriter.cs b/src/coreclr/tools/Common/Internal/NativeFormat/NativeFormatWriter.cs index f2f0f33a8c6f25..86b7e3dfe3c10d 100644 --- a/src/coreclr/tools/Common/Internal/NativeFormat/NativeFormatWriter.cs +++ b/src/coreclr/tools/Common/Internal/NativeFormat/NativeFormatWriter.cs @@ -2,12 +2,13 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; -using System.IO; -using System.Diagnostics; using System.Collections.Generic; +using System.Diagnostics; +using System.IO; using System.Linq; -using System.Text; using System.Numerics; +using System.Text; +using System.Xml.Linq; // Managed mirror of NativeFormatWriter.h/.cpp namespace Internal.NativeFormat @@ -389,6 +390,13 @@ public void Save(Stream stream) #endif } + if (_offsetAdjustment > 0) + { + _paddingSize += _offsetAdjustment; + WritePad(_offsetAdjustment); + + } + if (_offsetAdjustment == 0) { _encoder.Save(stream); @@ -2044,12 +2052,20 @@ internal override void Save(NativeWriter writer) public override bool Equals(object obj) { - throw new NotImplementedException(); + // TODO: Support value equality instead of just referential equality + return ReferenceEquals(this, obj); } public override int GetHashCode() { - throw new NotImplementedException(); + int hashCode = 13; + foreach (var entry in _Entries) + { + int value = (int)entry.Hashcode * 0x5498341 + 0x832424; + hashCode = hashCode * 31 + value; + } + + return hashCode; } } } diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/AnalysisBasedMetadataManager.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/AnalysisBasedMetadataManager.cs index 0e5a953736a5ac..71367a5c96a43a 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/AnalysisBasedMetadataManager.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/AnalysisBasedMetadataManager.cs @@ -31,6 +31,8 @@ public sealed class AnalysisBasedMetadataManager : MetadataManager, ICompilation private readonly Dictionary _reflectableFields = new Dictionary(); private readonly HashSet _reflectableAttributes = new HashSet(); private readonly HashSet _reflectableParameters = new HashSet(); + private readonly List _externalTypeMapGroupRequests; + private readonly List _proxyTypeMapGroupRequests; public AnalysisBasedMetadataManager(CompilerTypeSystemContext typeSystemContext) : this(typeSystemContext, new FullyBlockedMetadataBlockingPolicy(), @@ -38,7 +40,7 @@ public AnalysisBasedMetadataManager(CompilerTypeSystemContext typeSystemContext) new NoDynamicInvokeThunkGenerationPolicy(), null, Array.Empty(), Array.Empty(), Array.Empty>(), Array.Empty>(), Array.Empty>(), Array.Empty(), - Array.Empty(), + Array.Empty(), Array.Empty(), Array.Empty(), default) { } @@ -58,6 +60,8 @@ public AnalysisBasedMetadataManager( IEnumerable> reflectableFields, IEnumerable reflectableAttributes, IEnumerable reflectableParameters, + IEnumerable externalTypeMapGroupRequests, + IEnumerable proxyTypeMapGroupRequests, MetadataManagerOptions options) : base(typeSystemContext, blockingPolicy, resourceBlockingPolicy, logFile, stackTracePolicy, invokeThunkGenerationPolicy, options, flowAnnotations) { @@ -132,6 +136,9 @@ public AnalysisBasedMetadataManager( Debug.Assert((GetMetadataCategory(refField.Entity.GetTypicalFieldDefinition()) & MetadataCategory.Description) == (GetMetadataCategory(refField.Entity) & MetadataCategory.Description)); } + + _externalTypeMapGroupRequests = [..externalTypeMapGroupRequests]; + _proxyTypeMapGroupRequests = [..proxyTypeMapGroupRequests]; #endif } @@ -218,6 +225,16 @@ void ICompilationRootProvider.AddCompilationRoots(IRootingServiceProvider rootPr rootProvider.AddReflectionRoot(field, reason); } } + + foreach (var typeMapGroup in _externalTypeMapGroupRequests) + { + rootProvider.RootExternalTypeMapRequest(typeMapGroup, "TypeMapRequest"); + } + + foreach (var typeMapGroup in _proxyTypeMapGroupRequests) + { + rootProvider.RootProxyTypeMapRequest(typeMapGroup, "TypeMapRequest"); + } } private struct Policy : IMetadataPolicy diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/AssociatedTypeMapObjectNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/AssociatedTypeMapObjectNode.cs index bc75960dc136e2..82420b4d9e9d61 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/AssociatedTypeMapObjectNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/AssociatedTypeMapObjectNode.cs @@ -23,9 +23,9 @@ 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); + protected internal override int Phase => (int)ObjectNodePhase.Ordered; - - public override int ClassCode => 1146226395; + public override int ClassCode => (int)ObjectNodeOrder.AssociatedTypeMapObjectNode; public override bool StaticDependenciesAreComputed => true; @@ -50,9 +50,6 @@ public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) { TypeDesc typeMapGroup = entryNode.TypeMapGroup; typeMapHashTable = typeMapHashTables[typeMapGroup] = new VertexHashtable(); - Vertex typeMapStateVertex = writer.GetUnsignedConstant(1); // Valid type map state - Vertex typeMapGroupVertex = writer.GetUnsignedConstant(externalReferences.GetIndex(factory.NecessaryTypeSymbol(typeMapGroup))); - typeMapGroupHashTable.Append((uint)typeMapGroup.GetHashCode(), writer.GetTuple(typeMapGroupVertex, typeMapStateVertex, hashTableSection.Place(typeMapHashTable))); } Vertex nameVertex = writer.GetUnsignedConstant(externalReferences.GetIndex(factory.MaximallyConstructableType(entryNode.Key))); @@ -62,17 +59,22 @@ public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) typeMapHashTable.Append((uint)entryNode.Key.GetHashCode(), hashTableSection.Place(entry)); } - foreach (InvalidAssociatedTypeMapNode invalidNode in factory.MetadataManager.GetInvalidAssociatedTypeMaps()) + foreach ((TypeDesc typeMapGroup, VertexHashtable typeMapHashTable) in typeMapHashTables) { - if (!typeMapHashTables.TryGetValue(invalidNode.TypeMapGroup, out _)) - { - TypeDesc typeMapGroup = invalidNode.TypeMapGroup; - typeMapHashTables[typeMapGroup] = new VertexHashtable(); - 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(invalidNode.ThrowingMethodStub))); - typeMapGroupHashTable.Append((uint)typeMapGroup.GetHashCode(), writer.GetTuple(typeMapGroupVertex, typeMapStateVertex, throwingMethodStubVertex)); - } + Vertex typeMapStateVertex = writer.GetUnsignedConstant(1); // Valid type map state + Vertex typeMapGroupVertex = writer.GetUnsignedConstant(externalReferences.GetIndex(factory.NecessaryTypeSymbol(typeMapGroup))); + Vertex tuple = writer.GetTuple(typeMapGroupVertex, typeMapStateVertex, typeMapHashTable); + typeMapGroupHashTable.Append((uint)typeMapGroup.GetHashCode(), hashTableSection.Place(tuple)); + } + + foreach (InvalidExternalTypeMapNode invalidNode in factory.MetadataManager.GetInvalidExternalTypeMaps()) + { + TypeDesc typeMapGroup = invalidNode.TypeMapGroup; + 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(invalidNode.ThrowingMethodStub))); + Vertex tuple = writer.GetTuple(typeMapGroupVertex, typeMapStateVertex, throwingMethodStubVertex); + typeMapGroupHashTable.Append((uint)typeMapGroup.GetHashCode(), hashTableSection.Place(tuple)); } byte[] hashTableBytes = writer.Save(); diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ExternalTypeMapObjectNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ExternalTypeMapObjectNode.cs index 147031f0c8c407..1dc5afd43784f3 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ExternalTypeMapObjectNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ExternalTypeMapObjectNode.cs @@ -23,8 +23,9 @@ 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); + protected internal override int Phase => (int)ObjectNodePhase.Ordered; - public override int ClassCode => 2090746844; + public override int ClassCode => (int)ObjectNodeOrder.ExternalTypeMapObjectNode; public override bool StaticDependenciesAreComputed => true; @@ -49,9 +50,6 @@ public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) { TypeDesc typeMapGroup = entryNode.TypeMapGroup; typeMapHashTable = typeMapHashTables[typeMapGroup] = new VertexHashtable(); - Vertex typeMapStateVertex = writer.GetUnsignedConstant(1); // Valid type map state - Vertex typeMapGroupVertex = writer.GetUnsignedConstant(externalReferences.GetIndex(factory.NecessaryTypeSymbol(typeMapGroup))); - typeMapGroupHashTable.Append((uint)typeMapGroup.GetHashCode(), writer.GetTuple(typeMapGroupVertex, typeMapStateVertex, hashTableSection.Place(typeMapHashTable))); } Vertex nameVertex = writer.GetStringConstant(entryNode.Key); @@ -61,17 +59,22 @@ public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) typeMapHashTable.Append((uint)TypeHashingAlgorithms.ComputeNameHashCode(entryNode.Key), hashTableSection.Place(entry)); } + foreach ((TypeDesc typeMapGroup, VertexHashtable typeMapHashTable) in typeMapHashTables) + { + Vertex typeMapStateVertex = writer.GetUnsignedConstant(1); // Valid type map state + Vertex typeMapGroupVertex = writer.GetUnsignedConstant(externalReferences.GetIndex(factory.NecessaryTypeSymbol(typeMapGroup))); + Vertex tuple = writer.GetTuple(typeMapGroupVertex, typeMapStateVertex, typeMapHashTable); + typeMapGroupHashTable.Append((uint)typeMapGroup.GetHashCode(), hashTableSection.Place(tuple)); + } + foreach (InvalidExternalTypeMapNode invalidNode in factory.MetadataManager.GetInvalidExternalTypeMaps()) { - if (!typeMapHashTables.TryGetValue(invalidNode.TypeMapGroup, out _)) - { - TypeDesc typeMapGroup = invalidNode.TypeMapGroup; - typeMapHashTables[typeMapGroup] = new VertexHashtable(); - 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(invalidNode.ThrowingMethodStub))); - typeMapGroupHashTable.Append((uint)typeMapGroup.GetHashCode(), writer.GetTuple(typeMapGroupVertex, typeMapStateVertex, throwingMethodStubVertex)); - } + TypeDesc typeMapGroup = invalidNode.TypeMapGroup; + 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(invalidNode.ThrowingMethodStub))); + Vertex tuple = writer.GetTuple(typeMapGroupVertex, typeMapStateVertex, throwingMethodStubVertex); + typeMapGroupHashTable.Append((uint)typeMapGroup.GetHashCode(), hashTableSection.Place(tuple)); } byte[] hashTableBytes = writer.Save(); diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/TypeCastTargetNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/TypeCastTargetNode.cs deleted file mode 100644 index 18cee5904951a4..00000000000000 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/TypeCastTargetNode.cs +++ /dev/null @@ -1,52 +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.Collections.Generic; - -using ILCompiler.DependencyAnalysisFramework; - -using Internal.TypeSystem; - -using Debug = System.Diagnostics.Debug; - -namespace ILCompiler.DependencyAnalysis -{ - /// - /// Represents a type that is the target of a type cast operation. - /// Types that are targets of type casts preserve entries in type maps. - /// - public class TypeCastTargetNode : DependencyNodeCore - { - private readonly TypeDesc _type; - - public TypeCastTargetNode(TypeDesc type) - { - Debug.Assert(!type.IsCanonicalSubtype(CanonicalFormKind.Any) - || type.ConvertToCanonForm(CanonicalFormKind.Specific) == type); - _type = type; - } - - public TypeDesc Type => _type; - - public override IEnumerable GetStaticDependencies(NodeFactory factory) - { - var result = new DependencyList - { - new DependencyListEntry(factory.ConstructedTypeSymbol(_type), "Type map cast target"), - }; - - return result; - } - protected override string GetName(NodeFactory factory) - { - return "Cast target type: " + _type.ToString(); - } - - public override bool InterestingForDynamicDependencyAnalysis => false; - public override bool HasDynamicDependencies => false; - public override bool HasConditionalStaticDependencies => false; - public override bool StaticDependenciesAreComputed => true; - public override IEnumerable GetConditionalStaticDependencies(NodeFactory factory) => null; - public override IEnumerable SearchDynamicDependencies(List> markedNodes, int firstNode, NodeFactory factory) => null; - } -} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/IRootingServiceProvider.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/IRootingServiceProvider.cs index 74bdcbdac01c8e..20959bd4df3638 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/IRootingServiceProvider.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/IRootingServiceProvider.cs @@ -22,6 +22,8 @@ public interface IRootingServiceProvider void RootReadOnlyDataBlob(byte[] data, int alignment, string reason, string exportName, bool exportHidden); void RootDelegateMarshallingData(DefType type, string reason); void RootStructMarshallingData(DefType type, string reason); + void RootExternalTypeMapRequest(TypeDesc typeMapGroup, string reason); + void RootProxyTypeMapRequest(TypeDesc typeMapGroup, string reason); void AddCompilationRoot(object o, string reason); } } diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/RootingServiceProvider.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/RootingServiceProvider.cs index f9fa7937f2cb06..77e34bd4dd968f 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/RootingServiceProvider.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/RootingServiceProvider.cs @@ -157,5 +157,15 @@ public void RootStructMarshallingData(DefType type, string reason) { _rootAdder(_factory.StructMarshallingData(type), reason); } + + public void RootExternalTypeMapRequest(TypeDesc typeMapGroup, string reason) + { + _rootAdder(_factory.ExternalTypeMapRequest(typeMapGroup), reason); + } + + public void RootProxyTypeMapRequest(TypeDesc typeMapGroup, string reason) + { + _rootAdder(_factory.ProxyTypeMapRequest(typeMapGroup), reason); + } } } diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/UsageBasedMetadataManager.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/UsageBasedMetadataManager.cs index 38cc6e7ed7217d..be1cf164a56441 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/UsageBasedMetadataManager.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/UsageBasedMetadataManager.cs @@ -57,7 +57,8 @@ private static (string AttributeName, DiagnosticId Id)[] _requiresAttributeMisma private readonly List _fieldsWithRuntimeMapping = new List(); private readonly List _customAttributesWithMetadata = new List(); private readonly List _parametersWithMetadata = new List(); - private readonly List _typeMapTrimTargets = new List(); + private readonly List _externalTypeMapGroup = new List(); + private readonly List _proxyTypeMapGroup = new List(); internal IReadOnlyDictionary FeatureSwitches { get; } @@ -173,9 +174,14 @@ protected override void Graph_NewMarkedNode(DependencyNodeCore obj) _typesWithForcedEEType.Add(reflectableType.Type); } - if (obj is TypeCastTargetNode typeCastTarget) + if (obj is ExternalTypeMapRequestNode externalTypeMapRequest) { - _typeMapTrimTargets.Add(typeCastTarget.Type); + _externalTypeMapGroup.Add(externalTypeMapRequest.TypeMapGroup); + } + + if (obj is ProxyTypeMapRequestNode proxyTypeMapRequestNode) + { + _proxyTypeMapGroup.Add(proxyTypeMapRequestNode.TypeMapGroup); } } @@ -952,7 +958,7 @@ public MetadataManager ToAnalysisBasedMetadataManager() return new AnalysisBasedMetadataManager( _typeSystemContext, _blockingPolicy, _resourceBlockingPolicy, _metadataLogFile, _stackTraceEmissionPolicy, _dynamicInvokeThunkGenerationPolicy, FlowAnnotations, _modulesWithMetadata, _typesWithForcedEEType, reflectableTypes.ToEnumerable(), reflectableMethods.ToEnumerable(), - reflectableFields.ToEnumerable(), _customAttributesWithMetadata, _parametersWithMetadata, _options); + reflectableFields.ToEnumerable(), _customAttributesWithMetadata, _parametersWithMetadata, _externalTypeMapGroup, _proxyTypeMapGroup, _options); } private void AddDataflowDependency(ref DependencyList dependencies, NodeFactory factory, MethodIL methodIL, string reason) diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/ILCompiler.Compiler.csproj b/src/coreclr/tools/aot/ILCompiler.Compiler/ILCompiler.Compiler.csproj index d5f2c46e15edde..1cb8fa19775fed 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/ILCompiler.Compiler.csproj +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/ILCompiler.Compiler.csproj @@ -577,7 +577,6 @@ - diff --git a/src/libraries/System.Runtime.InteropServices/tests/TrimmingTests/System.Runtime.InteropServices.TrimmingTests.proj b/src/libraries/System.Runtime.InteropServices/tests/TrimmingTests/System.Runtime.InteropServices.TrimmingTests.proj index a812b392537fad..f1600704c4c5c0 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/TrimmingTests/System.Runtime.InteropServices.TrimmingTests.proj +++ b/src/libraries/System.Runtime.InteropServices/tests/TrimmingTests/System.Runtime.InteropServices.TrimmingTests.proj @@ -17,6 +17,9 @@ win-x64;browser-wasm + + + diff --git a/src/libraries/System.Runtime.InteropServices/tests/TrimmingTests/TypeMap.cs b/src/libraries/System.Runtime.InteropServices/tests/TrimmingTests/TypeMap.cs new file mode 100644 index 00000000000000..5dec3ec1860ca2 --- /dev/null +++ b/src/libraries/System.Runtime.InteropServices/tests/TrimmingTests/TypeMap.cs @@ -0,0 +1,112 @@ +// 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.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +[assembly: TypeMap("TrimTargetIsTarget", typeof(TargetAndTrimTarget), typeof(TargetAndTrimTarget))] +[assembly: TypeMap("TrimTargetIsUnrelated", typeof(TargetType), typeof(TrimTarget))] +[assembly: TypeMap("TrimTargetIsUnreferenced", typeof(UnreferencedTargetType), typeof(UnreferencedTrimTarget))] +[assembly: TypeMapAssociation(typeof(SourceClass), typeof(ProxyType))] + +[assembly: TypeMap("UnusedName", typeof(UnusedTargetType), typeof(TrimTarget))] +[assembly: TypeMapAssociation(typeof(UnusedSourceClass), typeof(UnusedProxyType))] + +if (args.Length > 1 && args[0] == "instantiate") +{ + Console.WriteLine("This code path should never actually be called. It exists exclusively for the trimmer to see that types are used in a way that it can't fully analyze."); + // Execute some code here to ensure that our "trim target" types are seen as "possibly used". + object t = Activator.CreateInstance(Type.GetType(args[1])); + if (t is TargetAndTrimTarget) + { + Console.WriteLine("Type deriving from TargetAndTrimTarget instantiated."); + } + else if (t is TrimTarget) + { + Console.WriteLine("Type deriving from TrimTarget instantiated."); + } + + Console.WriteLine("Hash code of SourceClass instance: " + new SourceClass().GetHashCode()); + return -1; +} + +IReadOnlyDictionary usedTypeMap = TypeMapping.GetOrCreateExternalTypeMapping(); + +if (!usedTypeMap.TryGetValue("TrimTargetIsTarget", out Type targetAndTrimTargetType)) +{ + Console.WriteLine("TrimTargetIsTarget not found in used type map."); + return 1; +} + +if (targetAndTrimTargetType != GetTypeWithoutTrimAnalysis("TargetAndTrimTarget")) +{ + Console.WriteLine("TrimTargetIsTarget type does not match expected type."); + return 2; +} + +if (!usedTypeMap.TryGetValue("TrimTargetIsUnrelated", out Type targetType)) +{ + Console.WriteLine("TrimTargetIsUnrelated not found in used type map."); + return 3; +} + +if (targetType != GetTypeWithoutTrimAnalysis("TargetType")) +{ + Console.WriteLine("TrimTargetIsUnrelated type does not match expected type."); + return 4; +} + +if (usedTypeMap.TryGetValue("TrimTargetIsUnreferenced", out _)) +{ + Console.WriteLine("TrimTargetIsUnreferenced should not be found in used type map."); + return 5; +} + +IReadOnlyDictionary usedProxyTypeMap = TypeMapping.GetOrCreateProxyTypeMapping(); +if (!usedProxyTypeMap.TryGetValue(typeof(SourceClass), out Type proxyType)) +{ + Console.WriteLine("SourceClass not found in used proxy type map."); + return 6; +} + +if (proxyType != GetTypeWithoutTrimAnalysis("ProxyType")) +{ + Console.WriteLine("SourceClass proxy type does not match expected type."); + return 7; +} + +if (GetTypeWithoutTrimAnalysis("UnusedTargetType") is not null) +{ + Console.WriteLine("UnusedTargetType should not be preserved if the external type map is not used and it is not referenced otherwise even if the entry's trim target is kept."); + return 8; +} + +if (GetTypeWithoutTrimAnalysis("UnusedProxyType") is not null) +{ + Console.WriteLine("UnusedProxyType should not be preserved if the proxy type map is not used and it is not referenced otherwise even if the entry's source type is kept."); + return 9; +} + +return 100; + +[MethodImpl(MethodImplOptions.NoInlining)] +static Type GetTypeWithoutTrimAnalysis(string typeName) +{ + return Type.GetType(typeName, throwOnError: false); +} + +class UsedTypeMap; +class TargetAndTrimTarget; +class TargetType; +class TrimTarget; +class UnreferencedTargetType; +class UnreferencedTrimTarget; +class SourceClass; +class ProxyType; + +class UnusedTypeMap; +class UnusedTargetType; +class UnusedSourceClass; +class UnusedProxyType; From 6c825d89ba5e8b667679b5a47703f299a8114ef1 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Thu, 5 Jun 2025 09:52:06 -0700 Subject: [PATCH 11/45] Cache type map attribute type lookup --- .../DependencyGraphTests.cs | 2 +- .../Compiler/TypeMapManager.cs | 19 +++++----- .../Compiler/UsageBasedMetadataManager.cs | 10 +++--- .../TestCasesRunner/TrimmingDriver.cs | 35 ++++++++++--------- src/coreclr/tools/aot/ILCompiler/Program.cs | 1 + 5 files changed, 36 insertions(+), 31 deletions(-) diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler.Tests/DependencyGraphTests.cs b/src/coreclr/tools/aot/ILCompiler.Compiler.Tests/DependencyGraphTests.cs index c20912efd6a5fb..53ee6ee867eb72 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler.Tests/DependencyGraphTests.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler.Tests/DependencyGraphTests.cs @@ -73,7 +73,7 @@ public void TestDependencyGraphInvariants(EcmaMethod method) new FullyBlockedMetadataBlockingPolicy(), new FullyBlockedManifestResourceBlockingPolicy(), null, new NoStackTraceEmissionPolicy(), new NoDynamicInvokeThunkGenerationPolicy(), new ILLink.Shared.TrimAnalysis.FlowAnnotations(Logger.Null, ilProvider, compilerGeneratedState), UsageBasedMetadataGenerationOptions.None, - default, Logger.Null, new Dictionary(), Array.Empty(), Array.Empty(), Array.Empty(), Array.Empty()); + default, new TypeMapManager(method.Module), Logger.Null, new Dictionary(), Array.Empty(), Array.Empty(), Array.Empty(), Array.Empty()); CompilationBuilder builder = new RyuJitCompilationBuilder(context, compilationGroup) .UseILProvider(ilProvider); diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/TypeMapManager.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/TypeMapManager.cs index d9100cf24714ff..6a9c8e7039fad4 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/TypeMapManager.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/TypeMapManager.cs @@ -108,6 +108,9 @@ public override MethodIL EmitIL() } private Dictionary _typeMapStates = new Dictionary(); + private TypeDesc _typeMapAssemblyTargetType; + private TypeDesc _typeMapType; + private TypeDesc _typeMapAssociationType; public enum TypeMapAttributeKind { @@ -117,23 +120,19 @@ public enum TypeMapAttributeKind TypeMapAssociation } - public static TypeMapAttributeKind LookupTypeMapType(TypeDesc attrType) + public TypeMapAttributeKind LookupTypeMapType(TypeDesc attrType) { - TypeDesc typeMapAssemblyTargetType = attrType.Context.SystemModule.GetTypeByCustomAttributeTypeName("System.Runtime.InteropServices.TypeMapAssemblyTargetAttribute`1"); - TypeDesc typeMapType = attrType.Context.SystemModule.GetTypeByCustomAttributeTypeName("System.Runtime.InteropServices.TypeMapAttribute`1"); - TypeDesc typeMapAssociationType = attrType.Context.SystemModule.GetTypeByCustomAttributeTypeName("System.Runtime.InteropServices.TypeMapAssociationAttribute`1"); - - if (typeMapAssemblyTargetType == attrType.GetTypeDefinition()) + if (_typeMapAssemblyTargetType == attrType.GetTypeDefinition()) { return TypeMapAttributeKind.TypeMapAssemblyTarget; } - if (typeMapType == attrType.GetTypeDefinition()) + if (_typeMapType == attrType.GetTypeDefinition()) { return TypeMapAttributeKind.TypeMap; } - if (typeMapAssociationType == attrType.GetTypeDefinition()) + if (_typeMapAssociationType == attrType.GetTypeDefinition()) { return TypeMapAttributeKind.TypeMapAssociation; } @@ -143,6 +142,10 @@ public static TypeMapAttributeKind LookupTypeMapType(TypeDesc attrType) public TypeMapManager(ModuleDesc entryModule) { + _typeMapAssemblyTargetType = entryModule.Context.SystemModule.GetTypeByCustomAttributeTypeName("System.Runtime.InteropServices.TypeMapAssemblyTargetAttribute`1"); + _typeMapType = entryModule.Context.SystemModule.GetTypeByCustomAttributeTypeName("System.Runtime.InteropServices.TypeMapAttribute`1"); + _typeMapAssociationType = entryModule.Context.SystemModule.GetTypeByCustomAttributeTypeName("System.Runtime.InteropServices.TypeMapAssociationAttribute`1"); + if (entryModule is not { Assembly: EcmaAssembly assembly }) { // We can only process EcmaAssembly-based modules as we can only read custom attributes from them. diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/UsageBasedMetadataManager.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/UsageBasedMetadataManager.cs index be1cf164a56441..0ddb0956ba8344 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/UsageBasedMetadataManager.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/UsageBasedMetadataManager.cs @@ -39,7 +39,7 @@ public sealed class UsageBasedMetadataManager : MetadataManager private readonly CompilationModuleGroup _compilationModuleGroup; internal readonly UsageBasedMetadataGenerationOptions _generationOptions; - + private readonly TypeMapManager _typeMapManager; private readonly LinkAttributesHashTable _linkAttributesHashTable; private static (string AttributeName, DiagnosticId Id)[] _requiresAttributeMismatchNameAndId = new[] @@ -81,17 +81,17 @@ public UsageBasedMetadataManager( FlowAnnotations flowAnnotations, UsageBasedMetadataGenerationOptions generationOptions, MetadataManagerOptions options, + TypeMapManager typeMapManager, Logger logger, IReadOnlyDictionary featureSwitchValues, IEnumerable rootEntireAssembliesModules, IEnumerable additionalRootedAssemblies, - IEnumerable trimmedAssemblies, - IEnumerable satelliteAssemblyFilePaths) + IEnumerable trimmedAssemblies, IEnumerable satelliteAssemblyFilePaths) : base(typeSystemContext, blockingPolicy, resourceBlockingPolicy, logFile, stackTracePolicy, invokeThunkGenerationPolicy, options, flowAnnotations) { _compilationModuleGroup = group; _generationOptions = generationOptions; - + _typeMapManager = typeMapManager; Logger = logger; _linkAttributesHashTable = new LinkAttributesHashTable(Logger, featureSwitchValues); @@ -830,7 +830,7 @@ public override DependencyList GetDependenciesForCustomAttribute(NodeFactory fac public bool GeneratesAttributeMetadata(TypeDesc attributeType) { - if (TypeMapManager.LookupTypeMapType(attributeType) != TypeMapManager.TypeMapAttributeKind.None) + if (_typeMapManager.LookupTypeMapType(attributeType) != TypeMapManager.TypeMapAttributeKind.None) { return false; } diff --git a/src/coreclr/tools/aot/ILCompiler.Trimming.Tests/TestCasesRunner/TrimmingDriver.cs b/src/coreclr/tools/aot/ILCompiler.Trimming.Tests/TestCasesRunner/TrimmingDriver.cs index f87cbf739f11b6..5b7dd7cd8232f2 100644 --- a/src/coreclr/tools/aot/ILCompiler.Trimming.Tests/TestCasesRunner/TrimmingDriver.cs +++ b/src/coreclr/tools/aot/ILCompiler.Trimming.Tests/TestCasesRunner/TrimmingDriver.cs @@ -117,23 +117,24 @@ public ILScanResults Trim (ILCompilerOptions options, TrimmingCustomizations? cu CompilerGeneratedState compilerGeneratedState = new CompilerGeneratedState (ilProvider, logger); - UsageBasedMetadataManager metadataManager = new UsageBasedMetadataManager ( - compilationGroup, - typeSystemContext, - new NoMetadataBlockingPolicy (), - new ManifestResourceBlockingPolicy (logger, options.FeatureSwitches, new Dictionary>()), - logFile: null, - new NoStackTraceEmissionPolicy (), - new DefaultDynamicInvokeThunkGenerationPolicy (), - new FlowAnnotations (logger, ilProvider, compilerGeneratedState), - UsageBasedMetadataGenerationOptions.ReflectionILScanning, - options: default, - logger, - options.FeatureSwitches, - Array.Empty (), - options.AdditionalRootAssemblies.ToArray (), - options.TrimAssemblies.ToArray (), - Array.Empty ()); + UsageBasedMetadataManager metadataManager = new UsageBasedMetadataManager( + compilationGroup, + typeSystemContext, + new NoMetadataBlockingPolicy(), + new ManifestResourceBlockingPolicy(logger, options.FeatureSwitches, new Dictionary>()), + logFile: null, + stackTracePolicy: new NoStackTraceEmissionPolicy(), + invokeThunkGenerationPolicy: new DefaultDynamicInvokeThunkGenerationPolicy(), + flowAnnotations: new FlowAnnotations(logger, ilProvider, compilerGeneratedState), + generationOptions: UsageBasedMetadataGenerationOptions.ReflectionILScanning, + options: default, + typeMapManager: new TypeMapManager(entrypointModule), + logger: logger, + featureSwitchValues: options.FeatureSwitches, + rootEntireAssembliesModules: Array.Empty(), + additionalRootedAssemblies: options.AdditionalRootAssemblies.ToArray(), + trimmedAssemblies: options.TrimAssemblies.ToArray(), + satelliteAssemblyFilePaths: Array.Empty()); PInvokeILEmitterConfiguration pinvokePolicy = new ILCompilerTestPInvokePolicy (); InteropStateManager interopStateManager = new InteropStateManager (typeSystemContext.GeneratedAssembly); diff --git a/src/coreclr/tools/aot/ILCompiler/Program.cs b/src/coreclr/tools/aot/ILCompiler/Program.cs index 0ccd3afacc47ba..1e5754bf780d6f 100644 --- a/src/coreclr/tools/aot/ILCompiler/Program.cs +++ b/src/coreclr/tools/aot/ILCompiler/Program.cs @@ -457,6 +457,7 @@ public int Run() flowAnnotations, metadataGenerationOptions, metadataOptions, + typeMapManager, logger, featureSwitches, Get(_command.ConditionallyRootedAssemblies), From 64af9d3b516e68771866dd59c3390cb5108fba28 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Thu, 5 Jun 2025 12:13:56 -0700 Subject: [PATCH 12/45] Add additional node in the IL Scanner when we see a cast that may be optimized away to root any external type map entry. These entries should be rooted even if the type is never instantiated and the corresponding target type is unrelated to the trimming type. --- .../Compiler/AnalysisBasedMetadataManager.cs | 15 ++++++++-- .../DependencyAnalysis/ExternalTypeMapNode.cs | 10 +++++-- .../DependencyAnalysis/NodeFactory.cs | 12 ++++++++ .../ScannedCastTargetNode.cs | 29 +++++++++++++++++++ .../Compiler/IRootingServiceProvider.cs | 1 + .../Compiler/RootingServiceProvider.cs | 5 ++++ .../Compiler/TypeMapManager.cs | 10 +++---- .../Compiler/UsageBasedMetadataManager.cs | 9 +++++- .../IL/ILImporter.Scanner.cs | 3 ++ .../ILCompiler.Compiler.csproj | 1 + 10 files changed, 84 insertions(+), 11 deletions(-) create mode 100644 src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ScannedCastTargetNode.cs diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/AnalysisBasedMetadataManager.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/AnalysisBasedMetadataManager.cs index 71367a5c96a43a..60713473db7a5c 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/AnalysisBasedMetadataManager.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/AnalysisBasedMetadataManager.cs @@ -33,6 +33,7 @@ public sealed class AnalysisBasedMetadataManager : MetadataManager, ICompilation private readonly HashSet _reflectableParameters = new HashSet(); private readonly List _externalTypeMapGroupRequests; private readonly List _proxyTypeMapGroupRequests; + private readonly List _possiblyOptimizedOutCastTargets; public AnalysisBasedMetadataManager(CompilerTypeSystemContext typeSystemContext) : this(typeSystemContext, new FullyBlockedMetadataBlockingPolicy(), @@ -41,6 +42,7 @@ public AnalysisBasedMetadataManager(CompilerTypeSystemContext typeSystemContext) Array.Empty>(), Array.Empty>(), Array.Empty>(), Array.Empty(), Array.Empty(), Array.Empty(), Array.Empty(), + Array.Empty(), default) { } @@ -62,6 +64,7 @@ public AnalysisBasedMetadataManager( IEnumerable reflectableParameters, IEnumerable externalTypeMapGroupRequests, IEnumerable proxyTypeMapGroupRequests, + IEnumerable possiblyOptimizedOutCastTargets, MetadataManagerOptions options) : base(typeSystemContext, blockingPolicy, resourceBlockingPolicy, logFile, stackTracePolicy, invokeThunkGenerationPolicy, options, flowAnnotations) { @@ -105,6 +108,10 @@ public AnalysisBasedMetadataManager( _reflectableParameters.Add(refParameter); } + _externalTypeMapGroupRequests = [.. externalTypeMapGroupRequests]; + _proxyTypeMapGroupRequests = [.. proxyTypeMapGroupRequests]; + _possiblyOptimizedOutCastTargets = [.. possiblyOptimizedOutCastTargets]; + #if DEBUG HashSet moduleHash = new HashSet(_modulesWithMetadata); foreach (var refType in reflectableTypes) @@ -136,9 +143,6 @@ public AnalysisBasedMetadataManager( Debug.Assert((GetMetadataCategory(refField.Entity.GetTypicalFieldDefinition()) & MetadataCategory.Description) == (GetMetadataCategory(refField.Entity) & MetadataCategory.Description)); } - - _externalTypeMapGroupRequests = [..externalTypeMapGroupRequests]; - _proxyTypeMapGroupRequests = [..proxyTypeMapGroupRequests]; #endif } @@ -235,6 +239,11 @@ void ICompilationRootProvider.AddCompilationRoots(IRootingServiceProvider rootPr { rootProvider.RootProxyTypeMapRequest(typeMapGroup, "TypeMapRequest"); } + + foreach (var castTarget in _possiblyOptimizedOutCastTargets) + { + rootProvider.RootPossibleCastTarget(castTarget, "Possibly optimized out cast target"); + } } private struct Policy : IMetadataPolicy 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 5ee82b8377a23d..f98b26ca1ad9df 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ExternalTypeMapNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ExternalTypeMapNode.cs @@ -27,16 +27,22 @@ public override IEnumerable GetConditionalStaticDep { var targetType = entry.Value.targetType; var trimmingTargetType = entry.Value.trimmingTargetType; + ExternalTypeMapEntryNode node = new(typeMapGroup, entry.Key, targetType); entries.Add(new CombinedDependencyListEntry( - new ExternalTypeMapEntryNode(typeMapGroup, entry.Key, targetType), + node, context.NecessaryTypeSymbol(trimmingTargetType), "Type in external type map is cast target")); + entries.Add(new CombinedDependencyListEntry( + node, + context.ScannedCastTarget(trimmingTargetType), + "Type in external type map is cast target for cast that may have been optimized away")); } return entries; } - public override IEnumerable GetStaticDependencies(NodeFactory context) => Array.Empty(); + public override IEnumerable GetStaticDependencies(NodeFactory context) => []; + public override IEnumerable SearchDynamicDependencies(List> markedNodes, int firstNode, NodeFactory context) => Array.Empty(); protected override string GetName(NodeFactory context) => "External type map"; } 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 510ce746d5a17c..4bfecead2377e3 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/NodeFactory.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/NodeFactory.cs @@ -598,6 +598,11 @@ private void CreateNodeCaches() return new ProxyTypeMapRequestNode(type); }); + _scannedCastTarget = new NodeCache(type => + { + return new ScannedCastTargetNode(type); + }); + NativeLayout = new NativeLayoutHelper(this); } @@ -1495,6 +1500,13 @@ public ProxyTypeMapRequestNode ProxyTypeMapRequest(TypeDesc type) return _proxyTypeMapRequests.GetOrAdd(type); } + private NodeCache _scannedCastTarget; + + public ScannedCastTargetNode ScannedCastTarget(TypeDesc type) + { + return _scannedCastTarget.GetOrAdd(type); + } + /// /// Returns alternative symbol name that object writer should produce for given symbols /// in addition to the regular one. diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ScannedCastTargetNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ScannedCastTargetNode.cs new file mode 100644 index 00000000000000..d5d60604050cd4 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ScannedCastTargetNode.cs @@ -0,0 +1,29 @@ +// 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.TypeSystem; + +namespace ILCompiler.DependencyAnalysis +{ + public sealed class ScannedCastTargetNode(TypeDesc type) : DependencyNodeCore + { + public override bool InterestingForDynamicDependencyAnalysis => false; + + public override bool HasDynamicDependencies => false; + + public override bool HasConditionalStaticDependencies => true; + + public override bool StaticDependenciesAreComputed => true; + + public TypeDesc Type => type; + + public override IEnumerable GetConditionalStaticDependencies(NodeFactory context) => []; + public override IEnumerable GetStaticDependencies(NodeFactory context) => []; + public override IEnumerable SearchDynamicDependencies(List> markedNodes, int firstNode, NodeFactory context) => []; + protected override string GetName(NodeFactory context) => $"ExternalTrimTarget {type}"; + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/IRootingServiceProvider.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/IRootingServiceProvider.cs index 20959bd4df3638..633e8b254fdcc8 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/IRootingServiceProvider.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/IRootingServiceProvider.cs @@ -24,6 +24,7 @@ public interface IRootingServiceProvider void RootStructMarshallingData(DefType type, string reason); void RootExternalTypeMapRequest(TypeDesc typeMapGroup, string reason); void RootProxyTypeMapRequest(TypeDesc typeMapGroup, string reason); + void RootPossibleCastTarget(TypeDesc type, string reason); void AddCompilationRoot(object o, string reason); } } diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/RootingServiceProvider.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/RootingServiceProvider.cs index 77e34bd4dd968f..6694b375c76c60 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/RootingServiceProvider.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/RootingServiceProvider.cs @@ -167,5 +167,10 @@ public void RootProxyTypeMapRequest(TypeDesc typeMapGroup, string reason) { _rootAdder(_factory.ProxyTypeMapRequest(typeMapGroup), reason); } + + public void RootPossibleCastTarget(TypeDesc type, string reason) + { + _rootAdder(_factory.ScannedCastTarget(type), reason); + } } } diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/TypeMapManager.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/TypeMapManager.cs index 6a9c8e7039fad4..5690bdb8100598 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/TypeMapManager.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/TypeMapManager.cs @@ -142,16 +142,16 @@ public TypeMapAttributeKind LookupTypeMapType(TypeDesc attrType) public TypeMapManager(ModuleDesc entryModule) { - _typeMapAssemblyTargetType = entryModule.Context.SystemModule.GetTypeByCustomAttributeTypeName("System.Runtime.InteropServices.TypeMapAssemblyTargetAttribute`1"); - _typeMapType = entryModule.Context.SystemModule.GetTypeByCustomAttributeTypeName("System.Runtime.InteropServices.TypeMapAttribute`1"); - _typeMapAssociationType = entryModule.Context.SystemModule.GetTypeByCustomAttributeTypeName("System.Runtime.InteropServices.TypeMapAssociationAttribute`1"); - if (entryModule is not { Assembly: EcmaAssembly assembly }) { // We can only process EcmaAssembly-based modules as we can only read custom attributes from them. return; } + _typeMapAssemblyTargetType = entryModule.Context.SystemModule.GetTypeByCustomAttributeTypeName("System.Runtime.InteropServices.TypeMapAssemblyTargetAttribute`1"); + _typeMapType = entryModule.Context.SystemModule.GetTypeByCustomAttributeTypeName("System.Runtime.InteropServices.TypeMapAttribute`1"); + _typeMapAssociationType = entryModule.Context.SystemModule.GetTypeByCustomAttributeTypeName("System.Runtime.InteropServices.TypeMapAssociationAttribute`1"); + HashSet scannedAssemblies = []; Queue assembliesToScan = new Queue(); @@ -264,7 +264,7 @@ void ProcessTypeMapAttribute(CustomAttributeValue attrValue, TypeDesc { _typeMapStates[typeMapGroup] = typeMapState = new TypeMapState(); } - typeMapState.AddExternalTypeMapEntry(typeName, targetType, targetType); + typeMapState.AddExternalTypeMapEntry(typeName, targetType, trimTargetType); break; } diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/UsageBasedMetadataManager.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/UsageBasedMetadataManager.cs index 0ddb0956ba8344..ed84da529db4b5 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/UsageBasedMetadataManager.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/UsageBasedMetadataManager.cs @@ -59,6 +59,7 @@ private static (string AttributeName, DiagnosticId Id)[] _requiresAttributeMisma private readonly List _parametersWithMetadata = new List(); private readonly List _externalTypeMapGroup = new List(); private readonly List _proxyTypeMapGroup = new List(); + private readonly List _possiblyOptimizedOutCastTargets = new List(); internal IReadOnlyDictionary FeatureSwitches { get; } @@ -183,6 +184,11 @@ protected override void Graph_NewMarkedNode(DependencyNodeCore obj) { _proxyTypeMapGroup.Add(proxyTypeMapRequestNode.TypeMapGroup); } + + if (obj is ScannedCastTargetNode castTarget) + { + _possiblyOptimizedOutCastTargets.Add(castTarget.Type); + } } protected override MetadataCategory GetMetadataCategory(FieldDesc field) @@ -958,7 +964,8 @@ public MetadataManager ToAnalysisBasedMetadataManager() return new AnalysisBasedMetadataManager( _typeSystemContext, _blockingPolicy, _resourceBlockingPolicy, _metadataLogFile, _stackTraceEmissionPolicy, _dynamicInvokeThunkGenerationPolicy, FlowAnnotations, _modulesWithMetadata, _typesWithForcedEEType, reflectableTypes.ToEnumerable(), reflectableMethods.ToEnumerable(), - reflectableFields.ToEnumerable(), _customAttributesWithMetadata, _parametersWithMetadata, _externalTypeMapGroup, _proxyTypeMapGroup, _options); + reflectableFields.ToEnumerable(), _customAttributesWithMetadata, _parametersWithMetadata, + _externalTypeMapGroup, _proxyTypeMapGroup, _possiblyOptimizedOutCastTargets, _options); } private void AddDataflowDependency(ref DependencyList dependencies, NodeFactory factory, MethodIL methodIL, string reason) diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/IL/ILImporter.Scanner.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/IL/ILImporter.Scanner.cs index 00a0d56d48150f..2362f2837d9b43 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/IL/ILImporter.Scanner.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/IL/ILImporter.Scanner.cs @@ -839,6 +839,8 @@ private void ImportBranch(ILOpcode opcode, BasicBlock target, BasicBlock fallthr condition = _factory.TypeMetadata(typeEqualityCheckMetadataType); else condition = _factory.MaximallyConstructableType(typeEqualityCheckType); + + _dependencies.Add(_factory.ScannedCastTarget(typeEqualityCheckType), "Type equality check that may be optimized out."); } } @@ -849,6 +851,7 @@ private void ImportBranch(ILOpcode opcode, BasicBlock target, BasicBlock fallthr && !isinstCheckType.ConvertToCanonForm(CanonicalFormKind.Specific).IsCanonicalSubtype(CanonicalFormKind.Any)) { condition = _factory.MaximallyConstructableType(isinstCheckType); + _dependencies.Add(_factory.ScannedCastTarget(isinstCheckType), "isinst check that may be optimized out."); } } diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/ILCompiler.Compiler.csproj b/src/coreclr/tools/aot/ILCompiler.Compiler/ILCompiler.Compiler.csproj index 1cb8fa19775fed..7977997ba3f057 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/ILCompiler.Compiler.csproj +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/ILCompiler.Compiler.csproj @@ -413,6 +413,7 @@ + From c3bf1ed085fbb73757e5f3583241e68923831c4c Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Thu, 5 Jun 2025 12:28:19 -0700 Subject: [PATCH 13/45] Enable the TypeMapApp test for NativeAOT and adjust as necessary to make it trim safe (and for now update the exception types thrown) --- src/tests/Interop/TypeMap/TypeMapApp.cs | 56 ++++++++++---------- src/tests/Interop/TypeMap/TypeMapApp.csproj | 3 +- src/tests/Interop/TypeMap/TypeMapLib1.csproj | 2 - src/tests/Interop/TypeMap/TypeMapLib2.csproj | 2 - src/tests/Interop/TypeMap/TypeMapLib3.csproj | 2 - src/tests/Interop/TypeMap/TypeMapLib4.csproj | 2 - 6 files changed, 29 insertions(+), 38 deletions(-) diff --git a/src/tests/Interop/TypeMap/TypeMapApp.cs b/src/tests/Interop/TypeMap/TypeMapApp.cs index 63e1b64342316e..5a355855d89df5 100644 --- a/src/tests/Interop/TypeMap/TypeMapApp.cs +++ b/src/tests/Interop/TypeMap/TypeMapApp.cs @@ -101,40 +101,38 @@ public static void Validate_GroupType_Types() { Console.WriteLine(nameof(Validate_GroupType_Types)); - ValidateExternalTypeMap(); - ValidateExternalTypeMap(); - ValidateExternalTypeMap(); - ValidateExternalTypeMap(); - ValidateExternalTypeMap>(); - ValidateExternalTypeMap>(); - ValidateExternalTypeMap.I1>(); - ValidateExternalTypeMap.I2>(); - ValidateExternalTypeMap.I1>(); - ValidateExternalTypeMap.I2>(); - - ValidateProxyTypeMap(); - ValidateProxyTypeMap(); - ValidateProxyTypeMap(); - ValidateProxyTypeMap(); - ValidateProxyTypeMap>(); - ValidateProxyTypeMap>(); - ValidateProxyTypeMap.I1>(); - ValidateProxyTypeMap.I2>(); - ValidateProxyTypeMap.I1>(); - ValidateProxyTypeMap.I2>(); + ValidateExternalTypeMap(TypeMapping.GetOrCreateExternalTypeMapping()); + ValidateExternalTypeMap(TypeMapping.GetOrCreateExternalTypeMapping()); + ValidateExternalTypeMap(TypeMapping.GetOrCreateExternalTypeMapping()); + ValidateExternalTypeMap(TypeMapping.GetOrCreateExternalTypeMapping()); + ValidateExternalTypeMap(TypeMapping.GetOrCreateExternalTypeMapping>()); + ValidateExternalTypeMap(TypeMapping.GetOrCreateExternalTypeMapping>()); + ValidateExternalTypeMap(TypeMapping.GetOrCreateExternalTypeMapping.I1>()); + ValidateExternalTypeMap(TypeMapping.GetOrCreateExternalTypeMapping.I2>()); + ValidateExternalTypeMap(TypeMapping.GetOrCreateExternalTypeMapping.I1>()); + ValidateExternalTypeMap(TypeMapping.GetOrCreateExternalTypeMapping.I2>()); + + ValidateProxyTypeMap(TypeMapping.GetOrCreateProxyTypeMapping()); + ValidateProxyTypeMap(TypeMapping.GetOrCreateProxyTypeMapping()); + ValidateProxyTypeMap(TypeMapping.GetOrCreateProxyTypeMapping()); + ValidateProxyTypeMap(TypeMapping.GetOrCreateProxyTypeMapping()); + ValidateProxyTypeMap(TypeMapping.GetOrCreateProxyTypeMapping>()); + ValidateProxyTypeMap(TypeMapping.GetOrCreateProxyTypeMapping>()); + ValidateProxyTypeMap(TypeMapping.GetOrCreateProxyTypeMapping.I1>()); + ValidateProxyTypeMap(TypeMapping.GetOrCreateProxyTypeMapping.I2>()); + ValidateProxyTypeMap(TypeMapping.GetOrCreateProxyTypeMapping.I1>()); + ValidateProxyTypeMap(TypeMapping.GetOrCreateProxyTypeMapping.I2>()); [MethodImpl(MethodImplOptions.NoInlining)] - static void ValidateExternalTypeMap() + static void ValidateExternalTypeMap(IReadOnlyDictionary map) { - IReadOnlyDictionary map = TypeMapping.GetOrCreateExternalTypeMapping(); Assert.Equal(typeof(string), map["1"]); Assert.False(map.TryGetValue("2", out Type? _)); } [MethodImpl(MethodImplOptions.NoInlining)] - static void ValidateProxyTypeMap() + static void ValidateProxyTypeMap(IReadOnlyDictionary map) { - IReadOnlyDictionary map = TypeMapping.GetOrCreateProxyTypeMapping(); Assert.Equal(typeof(string), map[typeof(object)]); Assert.False(map.TryGetValue(typeof(string), out Type? _)); } @@ -184,7 +182,7 @@ public static void Validate_ExternalTypeMapping_DuplicateTypeKey() { Console.WriteLine(nameof(Validate_ExternalTypeMapping_DuplicateTypeKey)); - Assert.Throws(() => TypeMapping.GetOrCreateExternalTypeMapping()); + AssertExtensions.ThrowsAny(() => TypeMapping.GetOrCreateExternalTypeMapping()); } [Fact] @@ -264,7 +262,7 @@ public static void Validate_EmptyOrInvalidMappings() { Console.WriteLine(nameof(Validate_EmptyOrInvalidMappings)); - Assert.Throws(() => TypeMapping.GetOrCreateExternalTypeMapping()); - Assert.Throws(() => TypeMapping.GetOrCreateProxyTypeMapping()); + AssertExtensions.ThrowsAny(() => TypeMapping.GetOrCreateExternalTypeMapping()); + AssertExtensions.ThrowsAny(() => TypeMapping.GetOrCreateProxyTypeMapping()); } -} \ No newline at end of file +} diff --git a/src/tests/Interop/TypeMap/TypeMapApp.csproj b/src/tests/Interop/TypeMap/TypeMapApp.csproj index b9d153fc8e8942..345b96c4fb75dc 100644 --- a/src/tests/Interop/TypeMap/TypeMapApp.csproj +++ b/src/tests/Interop/TypeMap/TypeMapApp.csproj @@ -4,8 +4,9 @@ true True true - true true + true + true diff --git a/src/tests/Interop/TypeMap/TypeMapLib1.csproj b/src/tests/Interop/TypeMap/TypeMapLib1.csproj index 7d08b32f779ad2..2193b71207b019 100644 --- a/src/tests/Interop/TypeMap/TypeMapLib1.csproj +++ b/src/tests/Interop/TypeMap/TypeMapLib1.csproj @@ -2,11 +2,9 @@ library true - true true - diff --git a/src/tests/Interop/TypeMap/TypeMapLib2.csproj b/src/tests/Interop/TypeMap/TypeMapLib2.csproj index 8b16e48913a27d..50629c2438f912 100644 --- a/src/tests/Interop/TypeMap/TypeMapLib2.csproj +++ b/src/tests/Interop/TypeMap/TypeMapLib2.csproj @@ -2,11 +2,9 @@ library true - true true - diff --git a/src/tests/Interop/TypeMap/TypeMapLib3.csproj b/src/tests/Interop/TypeMap/TypeMapLib3.csproj index bce24f7a9ea85d..1df043651476d5 100644 --- a/src/tests/Interop/TypeMap/TypeMapLib3.csproj +++ b/src/tests/Interop/TypeMap/TypeMapLib3.csproj @@ -2,7 +2,6 @@ library true - true true @@ -12,4 +11,3 @@ - diff --git a/src/tests/Interop/TypeMap/TypeMapLib4.csproj b/src/tests/Interop/TypeMap/TypeMapLib4.csproj index 225c462d4d92e4..ba85519a4a3726 100644 --- a/src/tests/Interop/TypeMap/TypeMapLib4.csproj +++ b/src/tests/Interop/TypeMap/TypeMapLib4.csproj @@ -2,7 +2,6 @@ library true - true true @@ -12,4 +11,3 @@ - From 2ed2d97454efa252554898d494b4ebb543f1e6cb Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Thu, 5 Jun 2025 14:46:13 -0700 Subject: [PATCH 14/45] Split type map manager type so we don't need to pass null --- .../DependencyGraphTests.cs | 2 +- .../Compiler/CompilationBuilder.Aot.cs | 2 +- .../Compiler/EmptyTypeMapManager.cs | 18 + .../Compiler/ILScannerBuilder.cs | 2 +- .../Compiler/MetadataBasedTypeMapManager.cs | 331 +++++++++++++++++ .../Compiler/TypeMapManager.cs | 333 +----------------- .../ILCompiler.Compiler.csproj | 4 +- .../TestCasesRunner/TrimmingDriver.cs | 2 +- src/coreclr/tools/aot/ILCompiler/Program.cs | 10 +- 9 files changed, 366 insertions(+), 338 deletions(-) create mode 100644 src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/EmptyTypeMapManager.cs create mode 100644 src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/MetadataBasedTypeMapManager.cs diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler.Tests/DependencyGraphTests.cs b/src/coreclr/tools/aot/ILCompiler.Compiler.Tests/DependencyGraphTests.cs index 53ee6ee867eb72..ce8af83a298e3f 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler.Tests/DependencyGraphTests.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler.Tests/DependencyGraphTests.cs @@ -73,7 +73,7 @@ public void TestDependencyGraphInvariants(EcmaMethod method) new FullyBlockedMetadataBlockingPolicy(), new FullyBlockedManifestResourceBlockingPolicy(), null, new NoStackTraceEmissionPolicy(), new NoDynamicInvokeThunkGenerationPolicy(), new ILLink.Shared.TrimAnalysis.FlowAnnotations(Logger.Null, ilProvider, compilerGeneratedState), UsageBasedMetadataGenerationOptions.None, - default, new TypeMapManager(method.Module), Logger.Null, new Dictionary(), Array.Empty(), Array.Empty(), Array.Empty(), Array.Empty()); + default, new EmptyTypeMapManager(), Logger.Null, new Dictionary(), Array.Empty(), Array.Empty(), Array.Empty(), Array.Empty()); CompilationBuilder builder = new RyuJitCompilationBuilder(context, compilationGroup) .UseILProvider(ilProvider); diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/CompilationBuilder.Aot.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/CompilationBuilder.Aot.cs index fc0b1c9f84a651..85af60a86e6973 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/CompilationBuilder.Aot.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/CompilationBuilder.Aot.cs @@ -26,7 +26,7 @@ public partial class CompilationBuilder protected SecurityMitigationOptions _mitigationOptions; protected bool _dehydrate; protected bool _useDwarf5; - protected TypeMapManager _typeMapManager = new TypeMapManager(null); + protected TypeMapManager _typeMapManager = new EmptyTypeMapManager(); partial void InitializePartial() { diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/EmptyTypeMapManager.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/EmptyTypeMapManager.cs new file mode 100644 index 00000000000000..f18b11b3b147e1 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/EmptyTypeMapManager.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.DependencyAnalysis; +using Internal.TypeSystem; + +namespace ILCompiler +{ + public sealed class EmptyTypeMapManager : TypeMapManager + { + public override void AddCompilationRoots(IRootingServiceProvider rootProvider) { } + public override void AddToReadyToRunHeader(ReadyToRunHeaderNode header, NodeFactory nodeFactory, ExternalReferencesTableNode commonFixupsTableNode) { } + public override TypeMapAttributeKind LookupTypeMapType(TypeDesc attrType) => TypeMapAttributeKind.None; + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ILScannerBuilder.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ILScannerBuilder.cs index 1d1438830a492a..9f73a4d10a9141 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ILScannerBuilder.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ILScannerBuilder.cs @@ -25,7 +25,7 @@ public sealed class ILScannerBuilder private IEnumerable _compilationRoots = Array.Empty(); private MetadataManager _metadataManager; private InteropStubManager _interopStubManager = new EmptyInteropStubManager(); - private TypeMapManager _typeMapManager; + private TypeMapManager _typeMapManager = new EmptyTypeMapManager(); private int _parallelism = -1; internal ILScannerBuilder(CompilerTypeSystemContext context, CompilationModuleGroup compilationGroup, NameMangler mangler, ILProvider ilProvider, PreinitializationManager preinitializationManager) diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/MetadataBasedTypeMapManager.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/MetadataBasedTypeMapManager.cs new file mode 100644 index 00000000000000..dde358e4de0bf6 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/MetadataBasedTypeMapManager.cs @@ -0,0 +1,331 @@ +// 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.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 Internal.TypeSystem.Ecma; +using DependencyList = ILCompiler.DependencyAnalysisFramework.DependencyNodeCore.DependencyList; +using ReflectionMapBlob = Internal.Runtime.ReflectionMapBlob; + +namespace ILCompiler +{ + /// + /// This class is responsible for managing emitted data for type maps. + /// + public sealed class MetadataBasedTypeMapManager : TypeMapManager + { + private sealed class TypeMapState + { + private readonly Dictionary _associatedTypeMap = []; + private readonly Dictionary _externalTypeMap = []; + private ThrowingMethodStub _externalTypeMapExceptionStub; + private ThrowingMethodStub _associatedTypeMapExceptionStub; + + public void AddAssociatedTypeMapEntry(TypeDesc type, TypeDesc associatedType) + { + if (!_associatedTypeMap.TryAdd(type, associatedType)) + { + ThrowHelper.ThrowBadImageFormatException(); + } + } + public void AddExternalTypeMapEntry(string typeName, TypeDesc type, TypeDesc trimmingTarget) + { + if (!_externalTypeMap.TryAdd(typeName, (type, trimmingTarget))) + { + ThrowHelper.ThrowBadImageFormatException(); + } + } + + public void SetExternalTypeMapStub(ThrowingMethodStub stub) + { + if (_externalTypeMapExceptionStub?.Exception is TypeSystemException.FileNotFoundException) + { + // FileNotFound exception takes precedence. + return; + } + _externalTypeMapExceptionStub ??= stub; + } + + public void SetAssociatedTypeMapStub(ThrowingMethodStub stub) + { + if (_associatedTypeMapExceptionStub?.Exception is TypeSystemException.FileNotFoundException) + { + // FileNotFound exception takes precedence. + return; + } + _associatedTypeMapExceptionStub ??= stub; + } + + public object GetExternalTypeMapNode(TypeDesc group) + { + if (_externalTypeMapExceptionStub is not null) + { + return new InvalidExternalTypeMapNode(group, _externalTypeMapExceptionStub); + } + return new ExternalTypeMapNode(group, _externalTypeMap); + } + + public object GetAssociatedTypeMapNode(TypeDesc group) + { + if (_associatedTypeMapExceptionStub is not null) + { + return new InvalidAssociatedTypeMapNode(group, _associatedTypeMapExceptionStub); + } + return new AssociatedTypeMapNode(group, _associatedTypeMap); + } + } + + private sealed class ThrowingMethodStub(TypeDesc owningType, TypeDesc typeMapGroup, bool externalTypeMap, TypeSystemException ex) : ILStubMethod + { + public TypeSystemException Exception => ex; + public override string Name => $"InvalidTypeMapStub_{typeMapGroup}_{(externalTypeMap ? "External" : "Proxy")}"; + public override MethodIL EmitIL() + { + return TypeSystemThrowingILEmitter.EmitIL(this, Exception); + } + + protected override int CompareToImpl(MethodDesc other, TypeSystemComparer comparer) => other is ThrowingMethodStub otherStub ? Name.CompareTo(otherStub.Name) : -1; + + public override bool IsPInvoke => false; + + public override string DiagnosticName => Name; + + protected override int ClassCode => 1744789196; + + public override TypeDesc OwningType => owningType; + + public override MethodSignature Signature => new MethodSignature(MethodSignatureFlags.Static, 0, Context.GetWellKnownType(WellKnownType.Void), []); + + public override TypeSystemContext Context => owningType.Context; + } + + private Dictionary _typeMapStates = new Dictionary(); + private TypeDesc _typeMapAssemblyTargetType; + private TypeDesc _typeMapType; + private TypeDesc _typeMapAssociationType; + + public override TypeMapAttributeKind LookupTypeMapType(TypeDesc attrType) + { + if (_typeMapAssemblyTargetType == attrType.GetTypeDefinition()) + { + return TypeMapAttributeKind.TypeMapAssemblyTarget; + } + + if (_typeMapType == attrType.GetTypeDefinition()) + { + return TypeMapAttributeKind.TypeMap; + } + + if (_typeMapAssociationType == attrType.GetTypeDefinition()) + { + return TypeMapAttributeKind.TypeMapAssociation; + } + + return TypeMapAttributeKind.None; + } + + public MetadataBasedTypeMapManager(EcmaAssembly assembly) + { + _typeMapAssemblyTargetType = assembly.Context.SystemModule.GetTypeByCustomAttributeTypeName("System.Runtime.InteropServices.TypeMapAssemblyTargetAttribute`1"); + _typeMapType = assembly.Context.SystemModule.GetTypeByCustomAttributeTypeName("System.Runtime.InteropServices.TypeMapAttribute`1"); + _typeMapAssociationType = assembly.Context.SystemModule.GetTypeByCustomAttributeTypeName("System.Runtime.InteropServices.TypeMapAssociationAttribute`1"); + + HashSet scannedAssemblies = []; + + Queue assembliesToScan = new Queue(); + assembliesToScan.Enqueue(assembly); + + while (assembliesToScan.Count > 0) + { + EcmaAssembly currentAssembly = assembliesToScan.Dequeue(); + if (scannedAssemblies.Contains(currentAssembly)) + continue; + + scannedAssemblies.Add(currentAssembly); + + foreach (CustomAttributeHandle attrHandle in currentAssembly.MetadataReader.GetCustomAttributes(EntityHandle.AssemblyDefinition)) + { + CustomAttribute attr = currentAssembly.MetadataReader.GetCustomAttribute(attrHandle); + + if (!MetadataExtensions.GetAttributeTypeAndConstructor(currentAssembly.MetadataReader, attrHandle, out EntityHandle attributeType, out _)) + { + continue; + } + + TypeDesc type = (TypeDesc)currentAssembly.GetObject(attributeType); + + TypeMapAttributeKind attrKind = LookupTypeMapType(type); + + if (attrKind == TypeMapAttributeKind.None) + { + // Not a type map attribute, skip it + continue; + } + + CustomAttributeValue attrValue = attr.DecodeValue(new CustomAttributeTypeProvider(currentAssembly)); + + TypeDesc typeMapGroup = type.Instantiation[0]; + + try + { + switch (attrKind) + { + case TypeMapAttributeKind.TypeMapAssemblyTarget: + ProcessTypeMapAssemblyTargetAttribute(attrValue, typeMapGroup); + break; + + case TypeMapAttributeKind.TypeMap: + ProcessTypeMapAttribute(attrValue, typeMapGroup); + break; + + case TypeMapAttributeKind.TypeMapAssociation: + ProcessTypeMapAssociationAttribute(attrValue, typeMapGroup); + break; + + default: + Debug.Fail($"Unexpected TypeMapAttributeKind: {attrKind}"); + break; + } + } + catch (TypeSystemException ex) + { + if (!_typeMapStates.TryGetValue(typeMapGroup, out TypeMapState value)) + { + value = new TypeMapState(); + _typeMapStates[typeMapGroup] = value; + } + + if (attrKind is TypeMapAttributeKind.TypeMapAssemblyTarget or TypeMapAttributeKind.TypeMap) + { + value.SetExternalTypeMapStub(new ThrowingMethodStub(assembly.GetGlobalModuleType(), typeMapGroup, externalTypeMap: true, ex)); + } + + if (attrKind is TypeMapAttributeKind.TypeMapAssemblyTarget or TypeMapAttributeKind.TypeMapAssociation) + { + value.SetAssociatedTypeMapStub(new ThrowingMethodStub(assembly.GetGlobalModuleType(), typeMapGroup, externalTypeMap: false, ex)); + } + } + } + + void ProcessTypeMapAssemblyTargetAttribute(CustomAttributeValue attrValue, TypeDesc typeMapGroup) + { + // If attribute is TypeMapAssemblyTargetAttribute, we need to extract the generic argument (type map group) + // and process it. + if (attrValue.FixedArguments is not [{ Value: string assemblyName }]) + { + ThrowHelper.ThrowBadImageFormatException(); + return; + } + + EcmaAssembly targetAssembly = (EcmaAssembly)assembly.Context.ResolveAssembly(AssemblyNameInfo.Parse(assemblyName), throwIfNotFound: true); + + assembliesToScan.Enqueue(targetAssembly); + } + + void ProcessTypeMapAttribute(CustomAttributeValue attrValue, TypeDesc typeMapGroup) + { + switch (attrValue.FixedArguments) + { + case [{ Value: string typeName }, { Value: TypeDesc targetType }]: + { + if (!_typeMapStates.TryGetValue(typeMapGroup, out TypeMapState typeMapState)) + { + _typeMapStates[typeMapGroup] = typeMapState = new TypeMapState(); + } + typeMapState.AddExternalTypeMapEntry(typeName, targetType, targetType); + break; + } + + case [{ Value: string typeName }, { Value: TypeDesc targetType }, { Value: TypeDesc trimTargetType }]: + { + if (!_typeMapStates.TryGetValue(typeMapGroup, out TypeMapState typeMapState)) + { + _typeMapStates[typeMapGroup] = typeMapState = new TypeMapState(); + } + typeMapState.AddExternalTypeMapEntry(typeName, targetType, trimTargetType); + break; + } + + default: + ThrowHelper.ThrowBadImageFormatException(); + return; + } + } + + void ProcessTypeMapAssociationAttribute(CustomAttributeValue attrValue, TypeDesc typeMapGroup) + { + // If attribute is TypeMapAssociationAttribute, we need to extract the generic argument (type map group) + // and process it. + if (attrValue.FixedArguments is not [{ Value: TypeDesc type }, { Value: TypeDesc associatedType }]) + { + ThrowHelper.ThrowBadImageFormatException(); + return; + } + + if (!_typeMapStates.TryGetValue(typeMapGroup, out TypeMapState typeMapState)) + { + _typeMapStates[typeMapGroup] = typeMapState = new TypeMapState(); + } + + typeMapState.AddAssociatedTypeMapEntry(type, associatedType); + } + } + } + + private sealed class TypeMapsNode(IReadOnlyDictionary typeMapState) : DependencyNodeCore + { + public override bool InterestingForDynamicDependencyAnalysis => false; + + public override bool HasDynamicDependencies => false; + + public override bool HasConditionalStaticDependencies => true; + + public override bool StaticDependenciesAreComputed => true; + + public override IEnumerable GetConditionalStaticDependencies(NodeFactory context) + { + List entries = []; + foreach ((TypeDesc typeMapGroup, TypeMapState typeMapState) in typeMapState) + { + entries.Add(new CombinedDependencyListEntry(typeMapState.GetExternalTypeMapNode(typeMapGroup), context.ExternalTypeMapRequest(typeMapGroup), "ExternalTypeMap")); + entries.Add(new CombinedDependencyListEntry(typeMapState.GetAssociatedTypeMapNode(typeMapGroup), context.ProxyTypeMapRequest(typeMapGroup), "ProxyTypeMap")); + } + + return entries; + } + + 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) => "TypeMapsNode"; + } + + public override void AddCompilationRoots(IRootingServiceProvider rootProvider) + { + if (_typeMapStates.Count == 0) + { + return; // No type maps to process + } + + rootProvider.AddCompilationRoot(new TypeMapsNode(_typeMapStates), "TypeMapManager"); + } + + public override void AddToReadyToRunHeader(ReadyToRunHeaderNode header, NodeFactory nodeFactory, ExternalReferencesTableNode commonFixupsTableNode) + { + if (_typeMapStates.Count == 0) + { + return; // No type maps to emit + } + + header.Add(MetadataManager.BlobIdToReadyToRunSection(ReflectionMapBlob.ExternalTypeMap), new ExternalTypeMapObjectNode(commonFixupsTableNode)); + header.Add(MetadataManager.BlobIdToReadyToRunSection(ReflectionMapBlob.AssociatedTypeMap), new AssociatedTypeMapObjectNode(commonFixupsTableNode)); + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/TypeMapManager.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/TypeMapManager.cs index 5690bdb8100598..a3445b538d1143 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/TypeMapManager.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/TypeMapManager.cs @@ -1,117 +1,13 @@ -// Licensed to the .NET Foundation under one or more agreements. +// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System; -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 Internal.TypeSystem.Ecma; -using DependencyList = ILCompiler.DependencyAnalysisFramework.DependencyNodeCore.DependencyList; -using ReflectionMapBlob = Internal.Runtime.ReflectionMapBlob; namespace ILCompiler { - /// - /// This class is responsible for managing emitted data for type maps. - /// - public sealed class TypeMapManager : ICompilationRootProvider + public abstract class TypeMapManager : ICompilationRootProvider { - private sealed class TypeMapState - { - private readonly Dictionary _associatedTypeMap = []; - private readonly Dictionary _externalTypeMap = []; - private ThrowingMethodStub _externalTypeMapExceptionStub; - private ThrowingMethodStub _associatedTypeMapExceptionStub; - - public void AddAssociatedTypeMapEntry(TypeDesc type, TypeDesc associatedType) - { - if (!_associatedTypeMap.TryAdd(type, associatedType)) - { - ThrowHelper.ThrowBadImageFormatException(); - } - } - public void AddExternalTypeMapEntry(string typeName, TypeDesc type, TypeDesc trimmingTarget) - { - if (!_externalTypeMap.TryAdd(typeName, (type, trimmingTarget))) - { - ThrowHelper.ThrowBadImageFormatException(); - } - } - - public void SetExternalTypeMapStub(ThrowingMethodStub stub) - { - if (_externalTypeMapExceptionStub?.Exception is TypeSystemException.FileNotFoundException) - { - // FileNotFound exception takes precedence. - return; - } - _externalTypeMapExceptionStub ??= stub; - } - - public void SetAssociatedTypeMapStub(ThrowingMethodStub stub) - { - if (_associatedTypeMapExceptionStub?.Exception is TypeSystemException.FileNotFoundException) - { - // FileNotFound exception takes precedence. - return; - } - _associatedTypeMapExceptionStub ??= stub; - } - - public object GetExternalTypeMapNode(TypeDesc group) - { - if (_externalTypeMapExceptionStub is not null) - { - return new InvalidExternalTypeMapNode(group, _externalTypeMapExceptionStub); - } - return new ExternalTypeMapNode(group, _externalTypeMap); - } - - public object GetAssociatedTypeMapNode(TypeDesc group) - { - if (_associatedTypeMapExceptionStub is not null) - { - return new InvalidAssociatedTypeMapNode(group, _associatedTypeMapExceptionStub); - } - return new AssociatedTypeMapNode(group, _associatedTypeMap); - } - } - - private sealed class ThrowingMethodStub(TypeDesc owningType, TypeDesc typeMapGroup, bool externalTypeMap, TypeSystemException ex) : ILStubMethod - { - public TypeSystemException Exception => ex; - public override string Name => $"InvalidTypeMapStub_{typeMapGroup}_{(externalTypeMap ? "External" : "Proxy")}"; - public override MethodIL EmitIL() - { - return TypeSystemThrowingILEmitter.EmitIL(this, Exception); - } - - protected override int CompareToImpl(MethodDesc other, TypeSystemComparer comparer) => other is ThrowingMethodStub otherStub ? Name.CompareTo(otherStub.Name) : -1; - - public override bool IsPInvoke => false; - - public override string DiagnosticName => Name; - - protected override int ClassCode => 1744789196; - - public override TypeDesc OwningType => owningType; - - public override MethodSignature Signature => new MethodSignature(MethodSignatureFlags.Static, 0, Context.GetWellKnownType(WellKnownType.Void), []); - - public override TypeSystemContext Context => owningType.Context; - } - - private Dictionary _typeMapStates = new Dictionary(); - private TypeDesc _typeMapAssemblyTargetType; - private TypeDesc _typeMapType; - private TypeDesc _typeMapAssociationType; - public enum TypeMapAttributeKind { None, @@ -119,227 +15,8 @@ public enum TypeMapAttributeKind TypeMap, TypeMapAssociation } - - public TypeMapAttributeKind LookupTypeMapType(TypeDesc attrType) - { - if (_typeMapAssemblyTargetType == attrType.GetTypeDefinition()) - { - return TypeMapAttributeKind.TypeMapAssemblyTarget; - } - - if (_typeMapType == attrType.GetTypeDefinition()) - { - return TypeMapAttributeKind.TypeMap; - } - - if (_typeMapAssociationType == attrType.GetTypeDefinition()) - { - return TypeMapAttributeKind.TypeMapAssociation; - } - - return TypeMapAttributeKind.None; - } - - public TypeMapManager(ModuleDesc entryModule) - { - if (entryModule is not { Assembly: EcmaAssembly assembly }) - { - // We can only process EcmaAssembly-based modules as we can only read custom attributes from them. - return; - } - - _typeMapAssemblyTargetType = entryModule.Context.SystemModule.GetTypeByCustomAttributeTypeName("System.Runtime.InteropServices.TypeMapAssemblyTargetAttribute`1"); - _typeMapType = entryModule.Context.SystemModule.GetTypeByCustomAttributeTypeName("System.Runtime.InteropServices.TypeMapAttribute`1"); - _typeMapAssociationType = entryModule.Context.SystemModule.GetTypeByCustomAttributeTypeName("System.Runtime.InteropServices.TypeMapAssociationAttribute`1"); - - HashSet scannedAssemblies = []; - - Queue assembliesToScan = new Queue(); - assembliesToScan.Enqueue(assembly); - - while (assembliesToScan.Count > 0) - { - EcmaAssembly currentAssembly = assembliesToScan.Dequeue(); - if (scannedAssemblies.Contains(currentAssembly)) - continue; - - scannedAssemblies.Add(currentAssembly); - - foreach (CustomAttributeHandle attrHandle in currentAssembly.MetadataReader.GetCustomAttributes(EntityHandle.AssemblyDefinition)) - { - CustomAttribute attr = currentAssembly.MetadataReader.GetCustomAttribute(attrHandle); - - if (!MetadataExtensions.GetAttributeTypeAndConstructor(currentAssembly.MetadataReader, attrHandle, out EntityHandle attributeType, out _)) - { - continue; - } - - TypeDesc type = (TypeDesc)currentAssembly.GetObject(attributeType); - - TypeMapAttributeKind attrKind = LookupTypeMapType(type); - - if (attrKind == TypeMapAttributeKind.None) - { - // Not a type map attribute, skip it - continue; - } - - CustomAttributeValue attrValue = attr.DecodeValue(new CustomAttributeTypeProvider(currentAssembly)); - - TypeDesc typeMapGroup = type.Instantiation[0]; - - try - { - switch (attrKind) - { - case TypeMapAttributeKind.TypeMapAssemblyTarget: - ProcessTypeMapAssemblyTargetAttribute(attrValue, typeMapGroup); - break; - - case TypeMapAttributeKind.TypeMap: - ProcessTypeMapAttribute(attrValue, typeMapGroup); - break; - - case TypeMapAttributeKind.TypeMapAssociation: - ProcessTypeMapAssociationAttribute(attrValue, typeMapGroup); - break; - - default: - Debug.Fail($"Unexpected TypeMapAttributeKind: {attrKind}"); - break; - } - } - catch (TypeSystemException ex) - { - if (!_typeMapStates.TryGetValue(typeMapGroup, out TypeMapState value)) - { - value = new TypeMapState(); - _typeMapStates[typeMapGroup] = value; - } - - if (attrKind is TypeMapAttributeKind.TypeMapAssemblyTarget or TypeMapAttributeKind.TypeMap) - { - value.SetExternalTypeMapStub(new ThrowingMethodStub(entryModule.GetGlobalModuleType(), typeMapGroup, externalTypeMap: true, ex)); - } - - if (attrKind is TypeMapAttributeKind.TypeMapAssemblyTarget or TypeMapAttributeKind.TypeMapAssociation) - { - value.SetAssociatedTypeMapStub(new ThrowingMethodStub(entryModule.GetGlobalModuleType(), typeMapGroup, externalTypeMap: false, ex)); - } - } - } - - void ProcessTypeMapAssemblyTargetAttribute(CustomAttributeValue attrValue, TypeDesc typeMapGroup) - { - // If attribute is TypeMapAssemblyTargetAttribute, we need to extract the generic argument (type map group) - // and process it. - if (attrValue.FixedArguments is not [{ Value: string assemblyName }]) - { - ThrowHelper.ThrowBadImageFormatException(); - return; - } - - EcmaAssembly targetAssembly = (EcmaAssembly)assembly.Context.ResolveAssembly(AssemblyNameInfo.Parse(assemblyName), throwIfNotFound: true); - - assembliesToScan.Enqueue(targetAssembly); - } - - void ProcessTypeMapAttribute(CustomAttributeValue attrValue, TypeDesc typeMapGroup) - { - switch (attrValue.FixedArguments) - { - case [{ Value: string typeName }, { Value: TypeDesc targetType }]: - { - if (!_typeMapStates.TryGetValue(typeMapGroup, out TypeMapState typeMapState)) - { - _typeMapStates[typeMapGroup] = typeMapState = new TypeMapState(); - } - typeMapState.AddExternalTypeMapEntry(typeName, targetType, targetType); - break; - } - - case [{ Value: string typeName }, { Value: TypeDesc targetType }, { Value: TypeDesc trimTargetType }]: - { - if (!_typeMapStates.TryGetValue(typeMapGroup, out TypeMapState typeMapState)) - { - _typeMapStates[typeMapGroup] = typeMapState = new TypeMapState(); - } - typeMapState.AddExternalTypeMapEntry(typeName, targetType, trimTargetType); - break; - } - - default: - ThrowHelper.ThrowBadImageFormatException(); - return; - } - } - - void ProcessTypeMapAssociationAttribute(CustomAttributeValue attrValue, TypeDesc typeMapGroup) - { - // If attribute is TypeMapAssociationAttribute, we need to extract the generic argument (type map group) - // and process it. - if (attrValue.FixedArguments is not [{ Value: TypeDesc type }, { Value: TypeDesc associatedType }]) - { - ThrowHelper.ThrowBadImageFormatException(); - return; - } - - if (!_typeMapStates.TryGetValue(typeMapGroup, out TypeMapState typeMapState)) - { - _typeMapStates[typeMapGroup] = typeMapState = new TypeMapState(); - } - - typeMapState.AddAssociatedTypeMapEntry(type, associatedType); - } - } - } - - private sealed class TypeMapsNode(IReadOnlyDictionary typeMapState) : DependencyNodeCore - { - public override bool InterestingForDynamicDependencyAnalysis => false; - - public override bool HasDynamicDependencies => false; - - public override bool HasConditionalStaticDependencies => true; - - public override bool StaticDependenciesAreComputed => true; - - public override IEnumerable GetConditionalStaticDependencies(NodeFactory context) - { - List entries = []; - foreach ((TypeDesc typeMapGroup, TypeMapState typeMapState) in typeMapState) - { - entries.Add(new CombinedDependencyListEntry(typeMapState.GetExternalTypeMapNode(typeMapGroup), context.ExternalTypeMapRequest(typeMapGroup), "ExternalTypeMap")); - entries.Add(new CombinedDependencyListEntry(typeMapState.GetAssociatedTypeMapNode(typeMapGroup), context.ProxyTypeMapRequest(typeMapGroup), "ProxyTypeMap")); - } - - return entries; - } - - 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) => "TypeMapsNode"; - } - - public void AddCompilationRoots(IRootingServiceProvider rootProvider) - { - if (_typeMapStates.Count == 0) - { - return; // No type maps to process - } - - rootProvider.AddCompilationRoot(new TypeMapsNode(_typeMapStates), "TypeMapManager"); - } - - public void AddToReadyToRunHeader(ReadyToRunHeaderNode header, NodeFactory nodeFactory, ExternalReferencesTableNode commonFixupsTableNode) - { - if (_typeMapStates.Count == 0) - { - return; // No type maps to emit - } - - header.Add(MetadataManager.BlobIdToReadyToRunSection(ReflectionMapBlob.ExternalTypeMap), new ExternalTypeMapObjectNode(commonFixupsTableNode)); - header.Add(MetadataManager.BlobIdToReadyToRunSection(ReflectionMapBlob.AssociatedTypeMap), new AssociatedTypeMapObjectNode(commonFixupsTableNode)); - } + public abstract void AddCompilationRoots(IRootingServiceProvider rootProvider); + public abstract void AddToReadyToRunHeader(ReadyToRunHeaderNode header, NodeFactory nodeFactory, ExternalReferencesTableNode commonFixupsTableNode); + public abstract TypeMapAttributeKind LookupTypeMapType(TypeDesc attrType); } } diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/ILCompiler.Compiler.csproj b/src/coreclr/tools/aot/ILCompiler.Compiler/ILCompiler.Compiler.csproj index 7977997ba3f057..aa0ce520e2f064 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/ILCompiler.Compiler.csproj +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/ILCompiler.Compiler.csproj @@ -458,6 +458,7 @@ + @@ -634,6 +635,7 @@ + @@ -656,7 +658,7 @@ - + diff --git a/src/coreclr/tools/aot/ILCompiler.Trimming.Tests/TestCasesRunner/TrimmingDriver.cs b/src/coreclr/tools/aot/ILCompiler.Trimming.Tests/TestCasesRunner/TrimmingDriver.cs index 5b7dd7cd8232f2..0f16d7c450df21 100644 --- a/src/coreclr/tools/aot/ILCompiler.Trimming.Tests/TestCasesRunner/TrimmingDriver.cs +++ b/src/coreclr/tools/aot/ILCompiler.Trimming.Tests/TestCasesRunner/TrimmingDriver.cs @@ -128,7 +128,7 @@ public ILScanResults Trim (ILCompilerOptions options, TrimmingCustomizations? cu flowAnnotations: new FlowAnnotations(logger, ilProvider, compilerGeneratedState), generationOptions: UsageBasedMetadataGenerationOptions.ReflectionILScanning, options: default, - typeMapManager: new TypeMapManager(entrypointModule), + typeMapManager: new MetadataBasedTypeMapManager((EcmaAssembly)entrypointModule!.Assembly), logger: logger, featureSwitchValues: options.FeatureSwitches, rootEntireAssembliesModules: Array.Empty(), diff --git a/src/coreclr/tools/aot/ILCompiler/Program.cs b/src/coreclr/tools/aot/ILCompiler/Program.cs index 1e5754bf780d6f..ca84230fc7c733 100644 --- a/src/coreclr/tools/aot/ILCompiler/Program.cs +++ b/src/coreclr/tools/aot/ILCompiler/Program.cs @@ -192,16 +192,16 @@ public int Run() CompilationModuleGroup compilationGroup; List compilationRoots = new List(); - TypeMapManager typeMapManager = new TypeMapManager(null); + TypeMapManager typeMapManager = new EmptyTypeMapManager(); bool multiFile = Get(_command.MultiFile); if (singleMethod != null) { // Compiling just a single method compilationGroup = new SingleMethodCompilationModuleGroup(singleMethod); compilationRoots.Add(new SingleMethodRootProvider(singleMethod)); - if (singleMethod.OwningType is MetadataType { Module: ModuleDesc module }) + if (singleMethod.OwningType is MetadataType { Module.Assembly: EcmaAssembly assembly }) { - compilationRoots.Add(typeMapManager = new TypeMapManager(module)); + compilationRoots.Add(typeMapManager = new MetadataBasedTypeMapManager(assembly)); } } else @@ -313,9 +313,9 @@ public int Run() compilationRoots.Add(new ILCompiler.DependencyAnalysis.TrimmingDescriptorNode(linkTrimFilePath)); } - if (entrypointModule != null) + if (entrypointModule is { Assembly: EcmaAssembly entryAssembly }) { - compilationRoots.Add(typeMapManager = new TypeMapManager(entrypointModule)); + compilationRoots.Add(typeMapManager = new MetadataBasedTypeMapManager(entryAssembly)); } } From 4334a6356b979690c7a3a32e597e52b2acd1220d Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Thu, 5 Jun 2025 14:58:40 -0700 Subject: [PATCH 15/45] Match attributes based on name. Don't care about the assembly --- .../DependencyGraphTests.cs | 2 +- .../Compiler/EmptyTypeMapManager.cs | 1 - .../Compiler/MetadataBasedTypeMapManager.cs | 27 ------------------- .../Compiler/TypeMapManager.cs | 13 ++++++++- .../Compiler/UsageBasedMetadataManager.cs | 8 +++--- .../TestCasesRunner/TrimmingDriver.cs | 1 - src/coreclr/tools/aot/ILCompiler/Program.cs | 1 - 7 files changed, 16 insertions(+), 37 deletions(-) diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler.Tests/DependencyGraphTests.cs b/src/coreclr/tools/aot/ILCompiler.Compiler.Tests/DependencyGraphTests.cs index ce8af83a298e3f..c20912efd6a5fb 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler.Tests/DependencyGraphTests.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler.Tests/DependencyGraphTests.cs @@ -73,7 +73,7 @@ public void TestDependencyGraphInvariants(EcmaMethod method) new FullyBlockedMetadataBlockingPolicy(), new FullyBlockedManifestResourceBlockingPolicy(), null, new NoStackTraceEmissionPolicy(), new NoDynamicInvokeThunkGenerationPolicy(), new ILLink.Shared.TrimAnalysis.FlowAnnotations(Logger.Null, ilProvider, compilerGeneratedState), UsageBasedMetadataGenerationOptions.None, - default, new EmptyTypeMapManager(), Logger.Null, new Dictionary(), Array.Empty(), Array.Empty(), Array.Empty(), Array.Empty()); + default, Logger.Null, new Dictionary(), Array.Empty(), Array.Empty(), Array.Empty(), Array.Empty()); CompilationBuilder builder = new RyuJitCompilationBuilder(context, compilationGroup) .UseILProvider(ilProvider); diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/EmptyTypeMapManager.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/EmptyTypeMapManager.cs index f18b11b3b147e1..222623db7c61da 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/EmptyTypeMapManager.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/EmptyTypeMapManager.cs @@ -13,6 +13,5 @@ public sealed class EmptyTypeMapManager : TypeMapManager { public override void AddCompilationRoots(IRootingServiceProvider rootProvider) { } public override void AddToReadyToRunHeader(ReadyToRunHeaderNode header, NodeFactory nodeFactory, ExternalReferencesTableNode commonFixupsTableNode) { } - public override TypeMapAttributeKind LookupTypeMapType(TypeDesc attrType) => TypeMapAttributeKind.None; } } diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/MetadataBasedTypeMapManager.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/MetadataBasedTypeMapManager.cs index dde358e4de0bf6..c05c753067489d 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/MetadataBasedTypeMapManager.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/MetadataBasedTypeMapManager.cs @@ -108,36 +108,9 @@ public override MethodIL EmitIL() } private Dictionary _typeMapStates = new Dictionary(); - private TypeDesc _typeMapAssemblyTargetType; - private TypeDesc _typeMapType; - private TypeDesc _typeMapAssociationType; - - public override TypeMapAttributeKind LookupTypeMapType(TypeDesc attrType) - { - if (_typeMapAssemblyTargetType == attrType.GetTypeDefinition()) - { - return TypeMapAttributeKind.TypeMapAssemblyTarget; - } - - if (_typeMapType == attrType.GetTypeDefinition()) - { - return TypeMapAttributeKind.TypeMap; - } - - if (_typeMapAssociationType == attrType.GetTypeDefinition()) - { - return TypeMapAttributeKind.TypeMapAssociation; - } - - return TypeMapAttributeKind.None; - } public MetadataBasedTypeMapManager(EcmaAssembly assembly) { - _typeMapAssemblyTargetType = assembly.Context.SystemModule.GetTypeByCustomAttributeTypeName("System.Runtime.InteropServices.TypeMapAssemblyTargetAttribute`1"); - _typeMapType = assembly.Context.SystemModule.GetTypeByCustomAttributeTypeName("System.Runtime.InteropServices.TypeMapAttribute`1"); - _typeMapAssociationType = assembly.Context.SystemModule.GetTypeByCustomAttributeTypeName("System.Runtime.InteropServices.TypeMapAssociationAttribute`1"); - HashSet scannedAssemblies = []; Queue assembliesToScan = new Queue(); diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/TypeMapManager.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/TypeMapManager.cs index a3445b538d1143..c1dabea7db40f7 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/TypeMapManager.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/TypeMapManager.cs @@ -17,6 +17,17 @@ public enum TypeMapAttributeKind } public abstract void AddCompilationRoots(IRootingServiceProvider rootProvider); public abstract void AddToReadyToRunHeader(ReadyToRunHeaderNode header, NodeFactory nodeFactory, ExternalReferencesTableNode commonFixupsTableNode); - public abstract TypeMapAttributeKind LookupTypeMapType(TypeDesc attrType); + + public static TypeMapAttributeKind LookupTypeMapType(TypeDesc attrType) + { + TypeDesc typeDef = attrType.GetTypeDefinition(); + return typeDef switch + { + MetadataType { Namespace: "System.Runtime.InteropServices", Name: "TypeMapAssemblyTargetAttribute`1", Instantiation.Length: 1 } => TypeMapAttributeKind.TypeMapAssemblyTarget, + MetadataType { Namespace: "System.Runtime.InteropServices", Name: "TypeMapAttribute`1", Instantiation.Length: 1 } => TypeMapAttributeKind.TypeMap, + MetadataType { Namespace: "System.Runtime.InteropServices", Name: "TypeMapAssociationAttribute`1", Instantiation.Length: 1 } => TypeMapAttributeKind.TypeMapAssociation, + _ => TypeMapAttributeKind.None, + }; + } } } diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/UsageBasedMetadataManager.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/UsageBasedMetadataManager.cs index ed84da529db4b5..9d9bef05ff532b 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/UsageBasedMetadataManager.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/UsageBasedMetadataManager.cs @@ -39,7 +39,6 @@ public sealed class UsageBasedMetadataManager : MetadataManager private readonly CompilationModuleGroup _compilationModuleGroup; internal readonly UsageBasedMetadataGenerationOptions _generationOptions; - private readonly TypeMapManager _typeMapManager; private readonly LinkAttributesHashTable _linkAttributesHashTable; private static (string AttributeName, DiagnosticId Id)[] _requiresAttributeMismatchNameAndId = new[] @@ -82,17 +81,16 @@ public UsageBasedMetadataManager( FlowAnnotations flowAnnotations, UsageBasedMetadataGenerationOptions generationOptions, MetadataManagerOptions options, - TypeMapManager typeMapManager, Logger logger, IReadOnlyDictionary featureSwitchValues, IEnumerable rootEntireAssembliesModules, IEnumerable additionalRootedAssemblies, - IEnumerable trimmedAssemblies, IEnumerable satelliteAssemblyFilePaths) + IEnumerable trimmedAssemblies, + IEnumerable satelliteAssemblyFilePaths) : base(typeSystemContext, blockingPolicy, resourceBlockingPolicy, logFile, stackTracePolicy, invokeThunkGenerationPolicy, options, flowAnnotations) { _compilationModuleGroup = group; _generationOptions = generationOptions; - _typeMapManager = typeMapManager; Logger = logger; _linkAttributesHashTable = new LinkAttributesHashTable(Logger, featureSwitchValues); @@ -836,7 +834,7 @@ public override DependencyList GetDependenciesForCustomAttribute(NodeFactory fac public bool GeneratesAttributeMetadata(TypeDesc attributeType) { - if (_typeMapManager.LookupTypeMapType(attributeType) != TypeMapManager.TypeMapAttributeKind.None) + if (TypeMapManager.LookupTypeMapType(attributeType) != TypeMapManager.TypeMapAttributeKind.None) { return false; } diff --git a/src/coreclr/tools/aot/ILCompiler.Trimming.Tests/TestCasesRunner/TrimmingDriver.cs b/src/coreclr/tools/aot/ILCompiler.Trimming.Tests/TestCasesRunner/TrimmingDriver.cs index 0f16d7c450df21..fa05991d84084d 100644 --- a/src/coreclr/tools/aot/ILCompiler.Trimming.Tests/TestCasesRunner/TrimmingDriver.cs +++ b/src/coreclr/tools/aot/ILCompiler.Trimming.Tests/TestCasesRunner/TrimmingDriver.cs @@ -128,7 +128,6 @@ public ILScanResults Trim (ILCompilerOptions options, TrimmingCustomizations? cu flowAnnotations: new FlowAnnotations(logger, ilProvider, compilerGeneratedState), generationOptions: UsageBasedMetadataGenerationOptions.ReflectionILScanning, options: default, - typeMapManager: new MetadataBasedTypeMapManager((EcmaAssembly)entrypointModule!.Assembly), logger: logger, featureSwitchValues: options.FeatureSwitches, rootEntireAssembliesModules: Array.Empty(), diff --git a/src/coreclr/tools/aot/ILCompiler/Program.cs b/src/coreclr/tools/aot/ILCompiler/Program.cs index ca84230fc7c733..d231a842b7173b 100644 --- a/src/coreclr/tools/aot/ILCompiler/Program.cs +++ b/src/coreclr/tools/aot/ILCompiler/Program.cs @@ -457,7 +457,6 @@ public int Run() flowAnnotations, metadataGenerationOptions, metadataOptions, - typeMapManager, logger, featureSwitches, Get(_command.ConditionallyRootedAssemblies), From 654731b587231ca4617b8ebb031bbbe96804b4f9 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Thu, 5 Jun 2025 15:04:40 -0700 Subject: [PATCH 16/45] Hook up the type map manager to the trimming test driver --- .../TestCasesRunner/TrimmingDriver.cs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/coreclr/tools/aot/ILCompiler.Trimming.Tests/TestCasesRunner/TrimmingDriver.cs b/src/coreclr/tools/aot/ILCompiler.Trimming.Tests/TestCasesRunner/TrimmingDriver.cs index fa05991d84084d..6ecbbe21caa2db 100644 --- a/src/coreclr/tools/aot/ILCompiler.Trimming.Tests/TestCasesRunner/TrimmingDriver.cs +++ b/src/coreclr/tools/aot/ILCompiler.Trimming.Tests/TestCasesRunner/TrimmingDriver.cs @@ -139,7 +139,15 @@ public ILScanResults Trim (ILCompilerOptions options, TrimmingCustomizations? cu InteropStateManager interopStateManager = new InteropStateManager (typeSystemContext.GeneratedAssembly); InteropStubManager interopStubManager = new UsageBasedInteropStubManager (interopStateManager, pinvokePolicy, logger); - CompilationBuilder builder = new RyuJitCompilationBuilder (typeSystemContext, compilationGroup) + TypeMapManager typeMapManager = new EmptyTypeMapManager(); + if (entrypointModule is { Assembly: EcmaAssembly entryAssembly }) + { + typeMapManager = new MetadataBasedTypeMapManager (entryAssembly); + } + + compilationRoots.Add(typeMapManager); + + CompilationBuilder builder = new RyuJitCompilationBuilder (typeSystemContext, compilationGroup) .UseILProvider (ilProvider) .UseCompilationUnitPrefix(""); @@ -148,6 +156,7 @@ public ILScanResults Trim (ILCompilerOptions options, TrimmingCustomizations? cu .UseMetadataManager (metadataManager) .UseParallelism (System.Diagnostics.Debugger.IsAttached ? 1 : -1) .UseInteropStubManager (interopStubManager) + .UseTypeMapManager (typeMapManager) .ToILScanner (); return scanner.Scan (); From 89a776f69950f9fbdc49612b21043223ab6dfc61 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Thu, 5 Jun 2025 15:56:41 -0700 Subject: [PATCH 17/45] Add TrimmingTest and add one more test case in the NativeAOT test app --- ...Runtime.InteropServices.TrimmingTests.proj | 1 + .../tests/TrimmingTests/TypeMap.cs | 16 ++- .../Reflection/TypeMap.cs | 120 ++++++++++++++++++ 3 files changed, 132 insertions(+), 5 deletions(-) create mode 100644 src/tools/illink/test/Mono.Linker.Tests.Cases/Reflection/TypeMap.cs diff --git a/src/libraries/System.Runtime.InteropServices/tests/TrimmingTests/System.Runtime.InteropServices.TrimmingTests.proj b/src/libraries/System.Runtime.InteropServices/tests/TrimmingTests/System.Runtime.InteropServices.TrimmingTests.proj index f1600704c4c5c0..18c7b69e7fd119 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/TrimmingTests/System.Runtime.InteropServices.TrimmingTests.proj +++ b/src/libraries/System.Runtime.InteropServices/tests/TrimmingTests/System.Runtime.InteropServices.TrimmingTests.proj @@ -19,6 +19,7 @@ + IlcGenerateMstatFile;IlcGenerateDgmlFile diff --git a/src/libraries/System.Runtime.InteropServices/tests/TrimmingTests/TypeMap.cs b/src/libraries/System.Runtime.InteropServices/tests/TrimmingTests/TypeMap.cs index 5dec3ec1860ca2..a688e8a88a5832 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/TrimmingTests/TypeMap.cs +++ b/src/libraries/System.Runtime.InteropServices/tests/TrimmingTests/TypeMap.cs @@ -58,35 +58,41 @@ return 4; } +if (GetTypeWithoutTrimAnalysis("TrimTarget") is not null) +{ + Console.WriteLine("TrimTarget should not be preserved if the only place that would preserve it is a check that is optimized away."); + return 5; +} + if (usedTypeMap.TryGetValue("TrimTargetIsUnreferenced", out _)) { Console.WriteLine("TrimTargetIsUnreferenced should not be found in used type map."); - return 5; + return 6; } IReadOnlyDictionary usedProxyTypeMap = TypeMapping.GetOrCreateProxyTypeMapping(); if (!usedProxyTypeMap.TryGetValue(typeof(SourceClass), out Type proxyType)) { Console.WriteLine("SourceClass not found in used proxy type map."); - return 6; + return 7; } if (proxyType != GetTypeWithoutTrimAnalysis("ProxyType")) { Console.WriteLine("SourceClass proxy type does not match expected type."); - return 7; + return 8; } if (GetTypeWithoutTrimAnalysis("UnusedTargetType") is not null) { Console.WriteLine("UnusedTargetType should not be preserved if the external type map is not used and it is not referenced otherwise even if the entry's trim target is kept."); - return 8; + return 9; } if (GetTypeWithoutTrimAnalysis("UnusedProxyType") is not null) { Console.WriteLine("UnusedProxyType should not be preserved if the proxy type map is not used and it is not referenced otherwise even if the entry's source type is kept."); - return 9; + return 10; } return 100; diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/Reflection/TypeMap.cs b/src/tools/illink/test/Mono.Linker.Tests.Cases/Reflection/TypeMap.cs new file mode 100644 index 00000000000000..ebf2243ebef84d --- /dev/null +++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/Reflection/TypeMap.cs @@ -0,0 +1,120 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using Mono.Linker.Tests.Cases.Expectations.Assertions; +using Mono.Linker.Tests.Cases.Reflection; + +[assembly: TypeMap ("TrimTargetIsTarget", typeof (TargetAndTrimTarget), typeof (TargetAndTrimTarget))] +[assembly: TypeMap ("TrimTargetIsUnrelated", typeof (TargetType), typeof (TrimTarget))] +[assembly: TypeMap ("TrimTargetIsUnreferenced", typeof (UnreferencedTargetType), typeof (UnreferencedTrimTarget))] +[assembly: TypeMapAssociation (typeof (SourceClass), typeof (ProxyType))] + +[assembly: TypeMap ("UnusedName", typeof (UnusedTargetType), typeof (TrimTarget))] +[assembly: TypeMapAssociation (typeof (UnusedSourceClass), typeof (UnusedProxyType))] + +namespace Mono.Linker.Tests.Cases.Reflection +{ + [Kept] + [IgnoreTestCase("Trimmer support is currently not implemented", IgnoredBy = Tool.Trimmer)] + class TypeMap + { + [Kept] + public static void Main(string[] args) + { + object t = Activator.CreateInstance (Type.GetType (args[1])); + if (t is TargetAndTrimTarget) { + Console.WriteLine ("Type deriving from TargetAndTrimTarget instantiated."); + } else if (t is TrimTarget) { + Console.WriteLine ("Type deriving from TrimTarget instantiated."); + } + + Console.WriteLine ("Hash code of SourceClass instance: " + new SourceClass ().GetHashCode ()); + + Console.WriteLine (TypeMapping.GetOrCreateExternalTypeMapping ()); + Console.WriteLine (TypeMapping.GetOrCreateProxyTypeMapping ()); + } + } + + [Kept(By = Tool.Trimmer)] + class UsedTypeMap; + + [Kept] + class TargetAndTrimTarget; + + [Kept] + class TargetType; + + [Kept] + class TrimTarget; + + class UnreferencedTargetType; + + class UnreferencedTrimTarget; + + [Kept] + [KeptMember(".ctor()")] + class SourceClass; + + [Kept] + class ProxyType; + + class UnusedTypeMap; + class UnusedTargetType; + class UnusedSourceClass; + class UnusedProxyType; +} + +// Polyfill for the type map types until we use an LKG runtime that has it. +namespace System.Runtime.InteropServices +{ + [Kept (By = Tool.Trimmer)] + [KeptBaseType (typeof (Attribute), By = Tool.Trimmer)] + [KeptAttributeAttribute (typeof (AttributeUsageAttribute), By = Tool.Trimmer)] + [AttributeUsage (AttributeTargets.Assembly, AllowMultiple = true)] + public sealed class TypeMapAttribute : Attribute + { + [Kept (By = Tool.Trimmer)] + public TypeMapAttribute (string value, Type target) { } + + [Kept (By = Tool.Trimmer)] + [KeptAttributeAttribute (typeof (RequiresUnreferencedCodeAttribute), By = Tool.Trimmer)] + [RequiresUnreferencedCode ("Interop types may be removed by trimming")] + public TypeMapAttribute (string value, Type target, Type trimTarget) { } + } + + [Kept (By = Tool.Trimmer)] + [KeptBaseType (typeof (Attribute), By = Tool.Trimmer)] + [KeptAttributeAttribute (typeof (AttributeUsageAttribute), By = Tool.Trimmer)] + [AttributeUsage (AttributeTargets.Assembly, AllowMultiple = true)] + public sealed class TypeMapAssociationAttribute : Attribute + { + [Kept (By = Tool.Trimmer)] + public TypeMapAssociationAttribute (Type source, Type proxy) { } + } + + [Kept(By = Tool.Trimmer)] + public static class TypeMapping + { + [Kept(By = Tool.Trimmer)] + [KeptAttributeAttribute (typeof (RequiresUnreferencedCodeAttribute), By = Tool.Trimmer)] + [RequiresUnreferencedCode ("Interop types may be removed by trimming")] + public static IReadOnlyDictionary GetOrCreateExternalTypeMapping () + { + throw new NotImplementedException (); + } + + [Kept(By = Tool.Trimmer)] + [KeptAttributeAttribute (typeof (RequiresUnreferencedCodeAttribute), By = Tool.Trimmer)] + [RequiresUnreferencedCode ("Interop types may be removed by trimming")] + public static IReadOnlyDictionary GetOrCreateProxyTypeMapping () + { + throw new NotImplementedException (); + } + } +} + From 99cb5c9773d733900d9d951d1f4f5a10e592c44e Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Thu, 5 Jun 2025 16:10:55 -0700 Subject: [PATCH 18/45] Add warning validation --- .../Mono.Linker.Tests.Cases/Reflection/TypeMap.cs | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/Reflection/TypeMap.cs b/src/tools/illink/test/Mono.Linker.Tests.Cases/Reflection/TypeMap.cs index ebf2243ebef84d..742aac48d7c33e 100644 --- a/src/tools/illink/test/Mono.Linker.Tests.Cases/Reflection/TypeMap.cs +++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/Reflection/TypeMap.cs @@ -20,11 +20,12 @@ namespace Mono.Linker.Tests.Cases.Reflection { [Kept] - [IgnoreTestCase("Trimmer support is currently not implemented", IgnoredBy = Tool.Trimmer)] + [IgnoreTestCase("Trimmer support is currently not implemented", IgnoredBy = Tool.Trimmer | Tool.Analyzer)] class TypeMap { [Kept] - public static void Main(string[] args) + [ExpectedWarning ("IL2057", "Unrecognized value passed to the parameter 'typeName' of method 'System.Type.GetType(String)'")] + public static void Main (string[] args) { object t = Activator.CreateInstance (Type.GetType (args[1])); if (t is TargetAndTrimTarget) { @@ -37,6 +38,14 @@ public static void Main(string[] args) Console.WriteLine (TypeMapping.GetOrCreateExternalTypeMapping ()); Console.WriteLine (TypeMapping.GetOrCreateProxyTypeMapping ()); + Console.WriteLine (GetExternalTypeMap ()); + } + + [Kept] + [ExpectedWarning ("IL2124", "Type 'T' must not contain signature variables to be used as a type map group.")] + private static IReadOnlyDictionary GetExternalTypeMap () + { + return TypeMapping.GetOrCreateExternalTypeMapping (); } } From b73848d21ccad4506e2235a9918354df2ec62028 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Thu, 5 Jun 2025 16:13:49 -0700 Subject: [PATCH 19/45] Fix node name --- .../Compiler/DependencyAnalysis/AssociatedTypeMapNode.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/AssociatedTypeMapNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/AssociatedTypeMapNode.cs index cc33512025b61d..5b5a3d55c79446 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/AssociatedTypeMapNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/AssociatedTypeMapNode.cs @@ -36,6 +36,6 @@ 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) => "External type map"; + protected override string GetName(NodeFactory context) => "Associated type map"; } } From 0f083a54637570ed9a9cf95f8d968ad5fa75a82c Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Thu, 5 Jun 2025 16:23:00 -0700 Subject: [PATCH 20/45] Don't materialize a string for every entry as we search the hashtable --- .../InteropServices/TypeMapLazyDictionary.NativeAot.cs | 4 ++-- .../Internal/NativeFormat/NativeFormatReader.String.cs | 5 +++++ 2 files changed, 7 insertions(+), 2 deletions(-) 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 8cbef91f554fb1..a72dfda043db35 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 @@ -138,9 +138,9 @@ public bool TryGetValue(string key, [MaybeNullWhen(false)] out Type value) NativeParser entryParser; while (!(entryParser = lookup.GetNext()).IsNull) { - string foundName = entryParser.GetString(); - if (foundName == key) + if (entryParser.StringEquals(key)) { + entryParser.SkipString(); RuntimeTypeHandle typeHandle = externalReferences.GetRuntimeTypeHandleFromIndex(entryParser.GetUnsigned()); value = Type.GetTypeFromHandle(typeHandle)!; return true; diff --git a/src/coreclr/tools/Common/Internal/NativeFormat/NativeFormatReader.String.cs b/src/coreclr/tools/Common/Internal/NativeFormat/NativeFormatReader.String.cs index e3b90b71132bf8..f36f0732d23607 100644 --- a/src/coreclr/tools/Common/Internal/NativeFormat/NativeFormatReader.String.cs +++ b/src/coreclr/tools/Common/Internal/NativeFormat/NativeFormatReader.String.cs @@ -24,6 +24,11 @@ public void SkipString() { _offset = _reader.SkipString(_offset); } + + public bool StringEquals(string str) + { + return _reader.StringEquals(_offset, str); + } } internal partial class NativeReader From a48496abea42ee05bc1ef02eb4a48195f24ad968 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Fri, 6 Jun 2025 12:03:27 -0700 Subject: [PATCH 21/45] Remove the "type map entry" nodes and rename the "associated" type map to the "proxy" type map to match the API users call --- .../src/CompatibilitySuppressions.xml | 60 ++++++------------ .../TypeMapLazyDictionary.NativeAot.cs | 2 +- .../SortableDependencyNode.cs | 2 +- .../Common/Internal/Runtime/MetadataBlob.cs | 2 +- .../AssociatedTypeMapEntryNode.cs | 61 ------------------- .../ExternalTypeMapEntryNode.cs | 61 ------------------- .../DependencyAnalysis/ExternalTypeMapNode.cs | 52 +++++++++++++--- .../ExternalTypeMapObjectNode.cs | 20 +++--- .../ExternalTypeMapRequestNode.cs | 5 +- ...MapEntry.cs => InvalidProxyTypeMapNode.cs} | 25 +++----- ...atedTypeMapNode.cs => ProxyTypeMapNode.cs} | 33 ++++++++-- ...bjectNode.cs => ProxyTypeMapObjectNode.cs} | 27 ++++---- .../ProxyTypeMapRequestNode.cs | 5 +- .../Compiler/MetadataBasedTypeMapManager.cs | 6 +- .../Compiler/MetadataManager.cs | 30 ++++----- .../ILCompiler.Compiler.csproj | 8 +-- 16 files changed, 153 insertions(+), 246 deletions(-) delete mode 100644 src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/AssociatedTypeMapEntryNode.cs delete mode 100644 src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ExternalTypeMapEntryNode.cs rename src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/{InvalidAssociatedTypeMapEntry.cs => InvalidProxyTypeMapNode.cs} (73%) rename src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/{AssociatedTypeMapNode.cs => ProxyTypeMapNode.cs} (52%) rename src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/{AssociatedTypeMapObjectNode.cs => ProxyTypeMapObjectNode.cs} (73%) diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/CompatibilitySuppressions.xml b/src/coreclr/nativeaot/System.Private.CoreLib/src/CompatibilitySuppressions.xml index 6d8d04d8a785f7..a3fa5bc1c88242 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/CompatibilitySuppressions.xml +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/CompatibilitySuppressions.xml @@ -1,10 +1,6 @@  - - CP0001 - T:Internal.Console - CP0001 T:Internal.Metadata.NativeFormat.ArraySignature @@ -57,6 +53,14 @@ CP0001 T:Internal.Metadata.NativeFormat.ConstantBooleanValueHandle + + CP0001 + T:Internal.Metadata.NativeFormat.ConstantEnumValue + + + CP0001 + T:Internal.Metadata.NativeFormat.ConstantEnumValueHandle + CP0001 T:Internal.Metadata.NativeFormat.ConstantByteArray @@ -113,14 +117,6 @@ CP0001 T:Internal.Metadata.NativeFormat.ConstantEnumArrayHandle - - CP0001 - T:Internal.Metadata.NativeFormat.ConstantEnumValue - - - CP0001 - T:Internal.Metadata.NativeFormat.ConstantEnumValueHandle - CP0001 T:Internal.Metadata.NativeFormat.ConstantHandleArray @@ -657,10 +653,6 @@ CP0001 T:Internal.Metadata.NativeFormat.UInt64Collection - - CP0001 - T:Internal.NativeFormat.TypeHashingAlgorithms - CP0001 T:Internal.Reflection.Core.AssemblyBinder @@ -733,10 +725,6 @@ CP0001 T:Internal.TypeSystem.LockFreeReaderHashtable`2 - - CP0001 - T:Internal.TypeSystem.LockFreeReaderHashtableOfPointers`2 - CP0001 T:System.Diagnostics.DebugAnnotations @@ -747,11 +735,11 @@ CP0001 - T:System.FieldHandleInfo + T:System.MDArray CP0001 - T:System.MDArray + T:System.FieldHandleInfo CP0001 @@ -822,29 +810,13 @@ T:System.Runtime.CompilerServices.StaticClassConstructionContext - CP0002 - F:System.Resources.ResourceManager.BaseNameField - - - CP0002 - F:System.Resources.ResourceSet.Reader + CP0001 + T:Internal.TypeSystem.LockFreeReaderHashtableOfPointers`2 CP0002 M:System.Reflection.MethodBase.GetParametersAsSpan - - CP0002 - M:System.String.Trim(System.ReadOnlySpan{System.Char}) - - - CP0002 - M:System.String.TrimEnd(System.ReadOnlySpan{System.Char}) - - - CP0002 - M:System.String.TrimStart(System.ReadOnlySpan{System.Char}) - CP0002 M:System.Threading.Lock.#ctor(System.Boolean) @@ -852,7 +824,9 @@ CP0002 M:System.Diagnostics.DiagnosticMethodInfo.#ctor(System.String,System.String,System.String) - ref/net10.0/System.Private.CoreLib.dll - lib/net10.0/System.Private.CoreLib.dll - \ No newline at end of file + + CP0001 + T:Internal.NativeFormat.TypeHashingAlgorithms + + 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 a72dfda043db35..b420cd12ab3b1d 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 @@ -65,7 +65,7 @@ public static IReadOnlyDictionary CreateProxyTypeMap(RuntimeType typ RuntimeTypeHandle typeMapGroupHandle = typeMapGroup.TypeHandle; foreach (TypeManagerHandle module in RuntimeAugments.GetLoadedModules()) { - if (!TryGetNativeReaderForBlob(module, ReflectionMapBlob.AssociatedTypeMap, out NativeReader externalTypeMapReader)) + if (!TryGetNativeReaderForBlob(module, ReflectionMapBlob.ProxyTypeMap, out NativeReader externalTypeMapReader)) { continue; } diff --git a/src/coreclr/tools/Common/Compiler/DependencyAnalysis/SortableDependencyNode.cs b/src/coreclr/tools/Common/Compiler/DependencyAnalysis/SortableDependencyNode.cs index 5238d0baca0593..62297efb6973e0 100644 --- a/src/coreclr/tools/Common/Compiler/DependencyAnalysis/SortableDependencyNode.cs +++ b/src/coreclr/tools/Common/Compiler/DependencyAnalysis/SortableDependencyNode.cs @@ -96,7 +96,7 @@ protected enum ObjectNodeOrder ReflectionVirtualInvokeMapNode, ArrayOfEmbeddedPointersNode, ExternalTypeMapObjectNode, - AssociatedTypeMapObjectNode, + ProxyTypeMapObjectNode, ExternalReferencesTableNode, StackTraceEmbeddedMetadataNode, StackTraceMethodMappingNode, diff --git a/src/coreclr/tools/Common/Internal/Runtime/MetadataBlob.cs b/src/coreclr/tools/Common/Internal/Runtime/MetadataBlob.cs index 1040a2d3f50c8b..9190062cc0baf1 100644 --- a/src/coreclr/tools/Common/Internal/Runtime/MetadataBlob.cs +++ b/src/coreclr/tools/Common/Internal/Runtime/MetadataBlob.cs @@ -45,6 +45,6 @@ internal enum ReflectionMapBlob // Type map blobs: ExternalTypeMap = 40, - AssociatedTypeMap = 41, + ProxyTypeMap = 41, } } diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/AssociatedTypeMapEntryNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/AssociatedTypeMapEntryNode.cs deleted file mode 100644 index f3e8972eb59976..00000000000000 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/AssociatedTypeMapEntryNode.cs +++ /dev/null @@ -1,61 +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.Generic; -using ILCompiler.DependencyAnalysis; -using ILCompiler.DependencyAnalysisFramework; -using Internal.TypeSystem; - -namespace ILCompiler.DependencyAnalysis -{ - internal sealed class AssociatedTypeMapEntryNode(TypeDesc typeMapGroup, TypeDesc key, TypeDesc targetType) : DependencyNodeCore, ISortableNode - { - public override bool InterestingForDynamicDependencyAnalysis => false; - - public override bool HasDynamicDependencies => false; - - public override bool HasConditionalStaticDependencies => false; - - public override bool StaticDependenciesAreComputed => true; - - public TypeDesc TypeMapGroup { get; } = typeMapGroup; - - public TypeDesc Key { get; } = key; - - public TypeDesc TargetType { get; } = targetType; - - public int ClassCode => 779513676; - - public int CompareToImpl(ISortableNode other, CompilerComparer comparer) - { - AssociatedTypeMapEntryNode otherEntry = (AssociatedTypeMapEntryNode)other; - if (TypeMapGroup != otherEntry.TypeMapGroup) - { - return comparer.Compare(TypeMapGroup, otherEntry.TypeMapGroup); - } - else if (Key != otherEntry.Key) - { - return comparer.Compare(Key, otherEntry.Key); - } - else if (TargetType != otherEntry.TargetType) - { - return comparer.Compare(TargetType, otherEntry.TargetType); - } - - return 0; - } - - public override IEnumerable GetConditionalStaticDependencies(NodeFactory context) => Array.Empty(); - - public override IEnumerable GetStaticDependencies(NodeFactory context) - { - return [ - new DependencyListEntry(context.MaximallyConstructableType(TargetType), "Target type in associated type map") - ]; - } - - public override IEnumerable SearchDynamicDependencies(List> markedNodes, int firstNode, NodeFactory context) => Array.Empty(); - protected override string GetName(NodeFactory context) => $"AssociatedTypeMapEntryNode({Key}, {TargetType}) in group {TypeMapGroup}"; - } -} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ExternalTypeMapEntryNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ExternalTypeMapEntryNode.cs deleted file mode 100644 index 14a5a75464e83a..00000000000000 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ExternalTypeMapEntryNode.cs +++ /dev/null @@ -1,61 +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.Generic; -using ILCompiler.DependencyAnalysis; -using ILCompiler.DependencyAnalysisFramework; -using Internal.TypeSystem; - -namespace ILCompiler.DependencyAnalysis -{ - internal sealed class ExternalTypeMapEntryNode(TypeDesc typeMapGroup, string key, TypeDesc targetType) : DependencyNodeCore, ISortableNode - { - public override bool InterestingForDynamicDependencyAnalysis => false; - - public override bool HasDynamicDependencies => false; - - public override bool HasConditionalStaticDependencies => false; - - public override bool StaticDependenciesAreComputed => true; - - public TypeDesc TypeMapGroup { get; } = typeMapGroup; - - public string Key { get; } = key; - - public TypeDesc TargetType { get; } = targetType; - - public int ClassCode => -785190502; - - public int CompareToImpl(ISortableNode other, CompilerComparer comparer) - { - ExternalTypeMapEntryNode otherEntry = (ExternalTypeMapEntryNode)other; - if (TypeMapGroup != otherEntry.TypeMapGroup) - { - return comparer.Compare(TypeMapGroup, otherEntry.TypeMapGroup); - } - else if (Key != otherEntry.Key) - { - return string.Compare(Key, otherEntry.Key, StringComparison.Ordinal); - } - else if (TargetType != otherEntry.TargetType) - { - return comparer.Compare(TargetType, otherEntry.TargetType); - } - - return 0; - } - - public override IEnumerable GetConditionalStaticDependencies(NodeFactory context) => Array.Empty(); - - public override IEnumerable GetStaticDependencies(NodeFactory context) - { - return [ - new DependencyListEntry(context.MaximallyConstructableType(TargetType), "Target type in external type map") - ]; - } - - public override IEnumerable SearchDynamicDependencies(List> markedNodes, int firstNode, NodeFactory context) => Array.Empty(); - protected override string GetName(NodeFactory context) => $"ExternalTypeMapEntryNode({Key}, {TargetType}) in group {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 f98b26ca1ad9df..1c1afa88cb7948 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ExternalTypeMapNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ExternalTypeMapNode.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.Text; using ILCompiler.DependencyAnalysis; using ILCompiler.DependencyAnalysisFramework; @@ -10,8 +11,16 @@ namespace ILCompiler.DependencyAnalysis { - internal sealed class ExternalTypeMapNode(TypeDesc typeMapGroup, IEnumerable> mapEntries) : DependencyNodeCore + internal sealed class ExternalTypeMapNode : DependencyNodeCore, ISortableNode { + private readonly IEnumerable> _mapEntries; + + public ExternalTypeMapNode(TypeDesc typeMapGroup, IEnumerable> mapEntries) + { + _mapEntries = mapEntries; + TypeMapGroup = typeMapGroup; + } + public override bool InterestingForDynamicDependencyAnalysis => false; public override bool HasDynamicDependencies => false; @@ -20,20 +29,20 @@ internal sealed class ExternalTypeMapNode(TypeDesc typeMapGroup, IEnumerable true; + public TypeDesc TypeMapGroup { get; } + public override IEnumerable GetConditionalStaticDependencies(NodeFactory context) { List entries = []; - foreach (var entry in mapEntries) + foreach (var entry in _mapEntries) { - var targetType = entry.Value.targetType; - var trimmingTargetType = entry.Value.trimmingTargetType; - ExternalTypeMapEntryNode node = new(typeMapGroup, entry.Key, targetType); + var (targetType, trimmingTargetType) = entry.Value; entries.Add(new CombinedDependencyListEntry( - node, + context.MaximallyConstructableType(targetType), context.NecessaryTypeSymbol(trimmingTargetType), "Type in external type map is cast target")); entries.Add(new CombinedDependencyListEntry( - node, + context.MaximallyConstructableType(targetType), context.ScannedCastTarget(trimmingTargetType), "Type in external type map is cast target for cast that may have been optimized away")); } @@ -44,6 +53,33 @@ public override IEnumerable GetConditionalStaticDep public override IEnumerable GetStaticDependencies(NodeFactory context) => []; public override IEnumerable SearchDynamicDependencies(List> markedNodes, int firstNode, NodeFactory context) => Array.Empty(); - protected override string GetName(NodeFactory context) => "External type map"; + protected override string GetName(NodeFactory context) => $"External type map: {TypeMapGroup}"; + + public int ClassCode => -785190502; + + public int CompareToImpl(ISortableNode other, CompilerComparer comparer) + { + ExternalTypeMapNode otherEntry = (ExternalTypeMapNode)other; + return comparer.Compare(TypeMapGroup, otherEntry.TypeMapGroup); + } + + public IEnumerable<(string Name, IEETypeNode target)> GetMarkedEntries(NodeFactory factory) + { + List<(string Name, IEETypeNode target)> markedEntries = []; + foreach (var entry in _mapEntries) + { + var (targetType, trimmingTargetType) = entry.Value; + IEETypeNode trimmingTarget = factory.NecessaryTypeSymbol(trimmingTargetType); + ScannedCastTargetNode scannedCastTarget = factory.ScannedCastTarget(trimmingTargetType); + + if (trimmingTarget.Marked || scannedCastTarget.Marked) + { + IEETypeNode targetNode = factory.MaximallyConstructableType(targetType); + Debug.Assert(targetNode.Marked); + markedEntries.Add((entry.Key, targetNode)); + } + } + return markedEntries; + } } } diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ExternalTypeMapObjectNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ExternalTypeMapObjectNode.cs index 1dc5afd43784f3..70e5bb3f0c3cf5 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ExternalTypeMapObjectNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ExternalTypeMapObjectNode.cs @@ -29,7 +29,7 @@ public void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) public override bool StaticDependenciesAreComputed => true; - protected override string GetName(NodeFactory context) => "External Type Map"; + protected override string GetName(NodeFactory context) => "External Type Map Hash Table"; public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) { // This node does not trigger generation of other nodes. @@ -44,19 +44,21 @@ public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) Section hashTableSection = writer.NewSection(); hashTableSection.Place(typeMapGroupHashTable); - foreach (ExternalTypeMapEntryNode entryNode in factory.MetadataManager.GetExternalTypeMapEntries()) + foreach (ExternalTypeMapNode externalTypeMap in factory.MetadataManager.GetExternalTypeMaps()) { - if (!typeMapHashTables.TryGetValue(entryNode.TypeMapGroup, out VertexHashtable typeMapHashTable)) + if (!typeMapHashTables.TryGetValue(externalTypeMap.TypeMapGroup, out VertexHashtable typeMapHashTable)) { - TypeDesc typeMapGroup = entryNode.TypeMapGroup; + TypeDesc typeMapGroup = externalTypeMap.TypeMapGroup; typeMapHashTable = typeMapHashTables[typeMapGroup] = new VertexHashtable(); } - Vertex nameVertex = writer.GetStringConstant(entryNode.Key); - Vertex targetTypeVertex = writer.GetUnsignedConstant(externalReferences.GetIndex(factory.MaximallyConstructableType(entryNode.TargetType))); - Vertex entry = writer.GetTuple(nameVertex, targetTypeVertex); - - typeMapHashTable.Append((uint)TypeHashingAlgorithms.ComputeNameHashCode(entryNode.Key), hashTableSection.Place(entry)); + foreach ((string key, IEETypeNode valueNode) in externalTypeMap.GetMarkedEntries(factory)) + { + Vertex keyVertex = writer.GetStringConstant(key); + Vertex valueVertex = writer.GetUnsignedConstant(externalReferences.GetIndex(valueNode)); + Vertex entry = writer.GetTuple(keyVertex, valueVertex); + typeMapHashTable.Append((uint)TypeHashingAlgorithms.ComputeNameHashCode(key), hashTableSection.Place(entry)); + } } foreach ((TypeDesc typeMapGroup, VertexHashtable typeMapHashTable) in typeMapHashTables) diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ExternalTypeMapRequestNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ExternalTypeMapRequestNode.cs index 53a0960e800c45..46bec73bd78a52 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ExternalTypeMapRequestNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ExternalTypeMapRequestNode.cs @@ -11,6 +11,7 @@ namespace ILCompiler.DependencyAnalysis { public sealed class ExternalTypeMapRequestNode(TypeDesc typeMapGroup) : DependencyNodeCore { + public TypeDesc TypeMapGroup { get; } = typeMapGroup; public override bool InterestingForDynamicDependencyAnalysis => false; public override bool HasDynamicDependencies => false; @@ -19,11 +20,9 @@ public sealed class ExternalTypeMapRequestNode(TypeDesc typeMapGroup) : Dependen public override bool StaticDependenciesAreComputed => true; - public TypeDesc TypeMapGroup { get; } = typeMapGroup; - public override IEnumerable GetConditionalStaticDependencies(NodeFactory context) => []; public override IEnumerable GetStaticDependencies(NodeFactory context) => []; public override IEnumerable SearchDynamicDependencies(List> markedNodes, int firstNode, NodeFactory context) => []; - protected override string GetName(NodeFactory context) => $"ExternalTypeMapRequestNode({TypeMapGroup})"; + protected override string GetName(NodeFactory context) => $"External type map request: {TypeMapGroup}"; } } diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/InvalidAssociatedTypeMapEntry.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/InvalidProxyTypeMapNode.cs similarity index 73% rename from src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/InvalidAssociatedTypeMapEntry.cs rename to src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/InvalidProxyTypeMapNode.cs index c74b8c6ed6b036..5e19833e16f538 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/InvalidAssociatedTypeMapEntry.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/InvalidProxyTypeMapNode.cs @@ -9,8 +9,11 @@ namespace ILCompiler.DependencyAnalysis { - internal sealed class InvalidAssociatedTypeMapNode(TypeDesc typeMapGroup, MethodDesc throwingMethodStub) : DependencyNodeCore, ISortableNode + internal sealed class InvalidProxyTypeMapNode(TypeDesc typeMapGroup, MethodDesc throwingMethodStub) : DependencyNodeCore, ISortableNode { + public TypeDesc TypeMapGroup { get; } = typeMapGroup; + public MethodDesc ThrowingMethodStub { get; } = throwingMethodStub; + public override bool InterestingForDynamicDependencyAnalysis => false; public override bool HasDynamicDependencies => false; @@ -30,24 +33,14 @@ public override IEnumerable GetStaticDependencies(NodeFacto public override IEnumerable SearchDynamicDependencies(List> markedNodes, int firstNode, NodeFactory context) => Array.Empty(); protected override string GetName(NodeFactory context) => "InvalidAssociatedTypeMapNode"; - public TypeDesc TypeMapGroup { get; } = typeMapGroup; - public MethodDesc ThrowingMethodStub { get; } = throwingMethodStub; - public int ClassCode => 36910224; - public int CompareToImpl(ISortableNode other, CompilerComparer comparer) { - if (other is InvalidExternalTypeMapNode otherNode) - { - int result = comparer.Compare(TypeMapGroup, otherNode.TypeMapGroup); - if (result != 0) - return result; - return comparer.Compare(ThrowingMethodStub, otherNode.ThrowingMethodStub); - } - else - { - return -1; // This node is always less than any other node - } + var otherNode = (InvalidExternalTypeMapNode)other; + int result = comparer.Compare(TypeMapGroup, otherNode.TypeMapGroup); + if (result != 0) + return result; + return comparer.Compare(ThrowingMethodStub, otherNode.ThrowingMethodStub); } } } diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/AssociatedTypeMapNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ProxyTypeMapNode.cs similarity index 52% rename from src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/AssociatedTypeMapNode.cs rename to src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ProxyTypeMapNode.cs index 5b5a3d55c79446..de38fbd7479ee9 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/AssociatedTypeMapNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ProxyTypeMapNode.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.Text; using ILCompiler.DependencyAnalysis; using ILCompiler.DependencyAnalysisFramework; @@ -10,8 +11,11 @@ namespace ILCompiler.DependencyAnalysis { - internal sealed class AssociatedTypeMapNode(TypeDesc typeMapGroup, IEnumerable> mapEntries) : DependencyNodeCore + internal sealed class ProxyTypeMapNode(TypeDesc typeMapGroup, IEnumerable> mapEntries) : DependencyNodeCore, ISortableNode { + public TypeDesc TypeMapGroup { get; } = typeMapGroup; + + public IEnumerable> MapEntries => mapEntries; public override bool InterestingForDynamicDependencyAnalysis => false; public override bool HasDynamicDependencies => false; @@ -20,15 +24,19 @@ internal sealed class AssociatedTypeMapNode(TypeDesc typeMapGroup, IEnumerable true; + public int ClassCode => 779513676; + + public int CompareToImpl(ISortableNode other, CompilerComparer comparer) => comparer.Compare(TypeMapGroup, ((ProxyTypeMapNode)other).TypeMapGroup); + public override IEnumerable GetConditionalStaticDependencies(NodeFactory context) { List entries = []; foreach (var (key, value) in mapEntries) { entries.Add(new CombinedDependencyListEntry( - new AssociatedTypeMapEntryNode(typeMapGroup, key, value), + context.MaximallyConstructableType(value), context.MaximallyConstructableType(key), - "Type in associated map may be constructed")); + "Proxy type map entry")); } return entries; @@ -36,6 +44,23 @@ 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) => "Associated type map"; + protected override string GetName(NodeFactory context) => $"Proxy type map: {TypeMapGroup}"; + + public IEnumerable<(IEETypeNode key, IEETypeNode value)> GetMarkedEntries(NodeFactory factory) + { + List<(IEETypeNode key, IEETypeNode value)> markedEntries = []; + foreach (var (key, value) in MapEntries) + { + IEETypeNode keyNode = factory.MaximallyConstructableType(key); + if (keyNode.Marked) + { + IEETypeNode valueNode = factory.MaximallyConstructableType(value); + Debug.Assert(valueNode.Marked); + markedEntries.Add((keyNode, valueNode)); + } + } + + return markedEntries; + } } } diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/AssociatedTypeMapObjectNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ProxyTypeMapObjectNode.cs similarity index 73% rename from src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/AssociatedTypeMapObjectNode.cs rename to src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ProxyTypeMapObjectNode.cs index 82420b4d9e9d61..1ee1fd58f7e0d9 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/AssociatedTypeMapObjectNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ProxyTypeMapObjectNode.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.Diagnostics; +using System.Formats.Tar; using ILCompiler.DependencyAnalysis; using ILCompiler.DependencyAnalysisFramework; using Internal.NativeFormat; @@ -12,7 +13,7 @@ namespace ILCompiler.DependencyAnalysis { - internal sealed class AssociatedTypeMapObjectNode(ExternalReferencesTableNode externalReferences) : ObjectNode, ISymbolDefinitionNode, INodeWithSize + internal sealed class ProxyTypeMapObjectNode(ExternalReferencesTableNode externalReferences) : ObjectNode, ISymbolDefinitionNode, INodeWithSize { public void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) { @@ -25,11 +26,11 @@ public void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) public override ObjectNodeSection GetSection(NodeFactory factory) => externalReferences.GetSection(factory); protected internal override int Phase => (int)ObjectNodePhase.Ordered; - public override int ClassCode => (int)ObjectNodeOrder.AssociatedTypeMapObjectNode; + public override int ClassCode => (int)ObjectNodeOrder.ProxyTypeMapObjectNode; public override bool StaticDependenciesAreComputed => true; - protected override string GetName(NodeFactory context) => "Associated Type Map"; + protected override string GetName(NodeFactory context) => "Associated Type Map Hash Table"; public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) { // This node does not trigger generation of other nodes. @@ -44,19 +45,21 @@ public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) Section hashTableSection = writer.NewSection(); hashTableSection.Place(typeMapGroupHashTable); - foreach (AssociatedTypeMapEntryNode entryNode in factory.MetadataManager.GetAssociatedTypeMapEntries()) + foreach (ProxyTypeMapNode proxyTypeMap in factory.MetadataManager.GetProxyTypeMaps()) { - if (!typeMapHashTables.TryGetValue(entryNode.TypeMapGroup, out VertexHashtable typeMapHashTable)) + if (!typeMapHashTables.TryGetValue(proxyTypeMap.TypeMapGroup, out VertexHashtable typeMapHashTable)) { - TypeDesc typeMapGroup = entryNode.TypeMapGroup; + TypeDesc typeMapGroup = proxyTypeMap.TypeMapGroup; typeMapHashTable = typeMapHashTables[typeMapGroup] = new VertexHashtable(); } - Vertex nameVertex = writer.GetUnsignedConstant(externalReferences.GetIndex(factory.MaximallyConstructableType(entryNode.Key))); - Vertex targetTypeVertex = writer.GetUnsignedConstant(externalReferences.GetIndex(factory.MaximallyConstructableType(entryNode.TargetType))); - Vertex entry = writer.GetTuple(nameVertex, targetTypeVertex); - - typeMapHashTable.Append((uint)entryNode.Key.GetHashCode(), hashTableSection.Place(entry)); + foreach ((IEETypeNode keyNode, IEETypeNode valueNode) in proxyTypeMap.GetMarkedEntries(factory)) + { + Vertex keyVertex = writer.GetUnsignedConstant(externalReferences.GetIndex(keyNode)); + Vertex valueVertex = writer.GetUnsignedConstant(externalReferences.GetIndex(valueNode)); + Vertex entry = writer.GetTuple(keyVertex, valueVertex); + typeMapHashTable.Append((uint)keyNode.Type.GetHashCode(), hashTableSection.Place(entry)); + } } foreach ((TypeDesc typeMapGroup, VertexHashtable typeMapHashTable) in typeMapHashTables) @@ -67,7 +70,7 @@ public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) typeMapGroupHashTable.Append((uint)typeMapGroup.GetHashCode(), hashTableSection.Place(tuple)); } - foreach (InvalidExternalTypeMapNode invalidNode in factory.MetadataManager.GetInvalidExternalTypeMaps()) + foreach (InvalidProxyTypeMapNode invalidNode in factory.MetadataManager.GetInvalidProxyTypeMaps()) { TypeDesc typeMapGroup = invalidNode.TypeMapGroup; Vertex typeMapStateVertex = writer.GetUnsignedConstant(0); // Invalid type map state diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ProxyTypeMapRequestNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ProxyTypeMapRequestNode.cs index 625fe9a88a6583..ebfb9a412c7a24 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ProxyTypeMapRequestNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ProxyTypeMapRequestNode.cs @@ -11,6 +11,7 @@ namespace ILCompiler.DependencyAnalysis { public sealed class ProxyTypeMapRequestNode(TypeDesc typeMapGroup) : DependencyNodeCore { + public TypeDesc TypeMapGroup { get; } = typeMapGroup; public override bool InterestingForDynamicDependencyAnalysis => false; public override bool HasDynamicDependencies => false; @@ -19,11 +20,9 @@ public sealed class ProxyTypeMapRequestNode(TypeDesc typeMapGroup) : DependencyN public override bool StaticDependenciesAreComputed => true; - public TypeDesc TypeMapGroup { get; } = typeMapGroup; - public override IEnumerable GetConditionalStaticDependencies(NodeFactory context) => []; public override IEnumerable GetStaticDependencies(NodeFactory context) => []; public override IEnumerable SearchDynamicDependencies(List> markedNodes, int firstNode, NodeFactory context) => []; - protected override string GetName(NodeFactory context) => $"ProxyTypeMapRequestNode({TypeMapGroup})"; + protected override string GetName(NodeFactory context) => $"Proxy type map request: {TypeMapGroup}"; } } diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/MetadataBasedTypeMapManager.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/MetadataBasedTypeMapManager.cs index c05c753067489d..c34aa300edc524 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/MetadataBasedTypeMapManager.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/MetadataBasedTypeMapManager.cs @@ -77,9 +77,9 @@ public object GetAssociatedTypeMapNode(TypeDesc group) { if (_associatedTypeMapExceptionStub is not null) { - return new InvalidAssociatedTypeMapNode(group, _associatedTypeMapExceptionStub); + return new InvalidProxyTypeMapNode(group, _associatedTypeMapExceptionStub); } - return new AssociatedTypeMapNode(group, _associatedTypeMap); + return new ProxyTypeMapNode(group, _associatedTypeMap); } } @@ -298,7 +298,7 @@ public override void AddToReadyToRunHeader(ReadyToRunHeaderNode header, NodeFact } header.Add(MetadataManager.BlobIdToReadyToRunSection(ReflectionMapBlob.ExternalTypeMap), new ExternalTypeMapObjectNode(commonFixupsTableNode)); - header.Add(MetadataManager.BlobIdToReadyToRunSection(ReflectionMapBlob.AssociatedTypeMap), new AssociatedTypeMapObjectNode(commonFixupsTableNode)); + header.Add(MetadataManager.BlobIdToReadyToRunSection(ReflectionMapBlob.ProxyTypeMap), new ProxyTypeMapObjectNode(commonFixupsTableNode)); } } } diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/MetadataManager.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/MetadataManager.cs index 86a43fcefef611..47461eec807c19 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/MetadataManager.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/MetadataManager.cs @@ -82,10 +82,10 @@ private readonly SortedSet _typeGVMEntries private readonly SortedSet _genericMethodHashtableEntries = new SortedSet(TypeSystemComparer.Instance); private readonly SortedSet _exactMethodHashtableEntries = new SortedSet(TypeSystemComparer.Instance); private readonly HashSet _usedInterfaces = new HashSet(); - private readonly SortedSet _externalTypeMapEntries = new SortedSet(CompilerComparer.Instance); + private readonly SortedSet _externalTypeMaps = new SortedSet(CompilerComparer.Instance); private readonly SortedSet _invalidExternalTypeMaps = new SortedSet(CompilerComparer.Instance); - private readonly SortedSet _associatedTypeMapEntries = new SortedSet(CompilerComparer.Instance); - private readonly SortedSet _invalidAssociatedTypeMaps = new SortedSet(CompilerComparer.Instance); + private readonly SortedSet _proxyTypeMaps = new SortedSet(CompilerComparer.Instance); + private readonly SortedSet _invalidProxyTypeMaps = new SortedSet(CompilerComparer.Instance); private List<(DehydratableObjectNode Node, ObjectNode.ObjectData Data)> _dehydratableData = new List<(DehydratableObjectNode Node, ObjectNode.ObjectData data)>(); @@ -352,9 +352,9 @@ protected virtual void Graph_NewMarkedNode(DependencyNodeCore obj) _usedInterfaces.Add(interfaceUse.Type); } - if (obj is ExternalTypeMapEntryNode externalTypeMapEntryNode) + if (obj is ExternalTypeMapNode externalTypeMapNode) { - _externalTypeMapEntries.Add(externalTypeMapEntryNode); + _externalTypeMaps.Add(externalTypeMapNode); } if (obj is InvalidExternalTypeMapNode invalidExternalTypeMapNode) @@ -363,14 +363,14 @@ protected virtual void Graph_NewMarkedNode(DependencyNodeCore obj) } - if (obj is AssociatedTypeMapEntryNode associatedTypeMapEntryNode) + if (obj is ProxyTypeMapNode proxyTypeMapNode) { - _associatedTypeMapEntries.Add(associatedTypeMapEntryNode); + _proxyTypeMaps.Add(proxyTypeMapNode); } - if (obj is InvalidAssociatedTypeMapNode invalidAssociatedTypeMapNode) + if (obj is InvalidProxyTypeMapNode invalidProxyTypeMapNode) { - _invalidAssociatedTypeMaps.Add(invalidAssociatedTypeMapNode); + _invalidProxyTypeMaps.Add(invalidProxyTypeMapNode); } } @@ -1135,9 +1135,9 @@ internal IEnumerable GetTemplateM return _templateMethodEntries; } - internal IEnumerable GetExternalTypeMapEntries() + internal IEnumerable GetExternalTypeMaps() { - return _externalTypeMapEntries; + return _externalTypeMaps; } internal IEnumerable GetInvalidExternalTypeMaps() @@ -1145,14 +1145,14 @@ internal IEnumerable GetInvalidExternalTypeMaps() return _invalidExternalTypeMaps; } - internal IEnumerable GetAssociatedTypeMapEntries() + internal IEnumerable GetProxyTypeMaps() { - return _associatedTypeMapEntries; + return _proxyTypeMaps; } - internal IEnumerable GetInvalidAssociatedTypeMaps() + internal IEnumerable GetInvalidProxyTypeMaps() { - return _invalidAssociatedTypeMaps; + return _invalidProxyTypeMaps; } public bool IsReflectionBlocked(TypeDesc type) diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/ILCompiler.Compiler.csproj b/src/coreclr/tools/aot/ILCompiler.Compiler/ILCompiler.Compiler.csproj index aa0ce520e2f064..da46e8be7903f4 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/ILCompiler.Compiler.csproj +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/ILCompiler.Compiler.csproj @@ -407,11 +407,9 @@ - - - + - + @@ -426,7 +424,7 @@ - + From bcf98ef91caf77af9e0debf3df2e001fc6105796 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Fri, 6 Jun 2025 14:23:56 -0700 Subject: [PATCH 22/45] Collapse MetadataBasedTypeMapManager and TypeMapManager --- .../Compiler/AnalysisBasedMetadataManager.cs | 19 +- .../Compiler/AnalysisBasedTypeMapManager.cs | 36 ++ .../Compiler/CompilationBuilder.Aot.cs | 2 +- .../ExternalTypeMapObjectNode.cs | 4 +- .../ProxyTypeMapObjectNode.cs | 8 +- .../Compiler/EmptyTypeMapManager.cs | 17 - .../Compiler/ILScannerBuilder.cs | 2 +- .../Compiler/IRootingServiceProvider.cs | 2 - .../Compiler/MetadataBasedTypeMapManager.cs | 304 ---------------- .../Compiler/MetadataManager.cs | 45 --- .../Compiler/RootingServiceProvider.cs | 10 - .../Compiler/TypeMapManager.cs | 340 +++++++++++++++++- .../Compiler/UsageBasedMetadataManager.cs | 15 +- .../Compiler/UsageBasedTypeMapManager.cs | 78 ++++ .../ILCompiler.Compiler.csproj | 6 +- .../TestCasesRunner/TrimmingDriver.cs | 4 +- src/coreclr/tools/aot/ILCompiler/Program.cs | 9 +- 17 files changed, 471 insertions(+), 430 deletions(-) create mode 100644 src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/AnalysisBasedTypeMapManager.cs delete mode 100644 src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/EmptyTypeMapManager.cs delete mode 100644 src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/MetadataBasedTypeMapManager.cs create mode 100644 src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/UsageBasedTypeMapManager.cs diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/AnalysisBasedMetadataManager.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/AnalysisBasedMetadataManager.cs index 60713473db7a5c..d8994daa473483 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/AnalysisBasedMetadataManager.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/AnalysisBasedMetadataManager.cs @@ -31,8 +31,6 @@ public sealed class AnalysisBasedMetadataManager : MetadataManager, ICompilation private readonly Dictionary _reflectableFields = new Dictionary(); private readonly HashSet _reflectableAttributes = new HashSet(); private readonly HashSet _reflectableParameters = new HashSet(); - private readonly List _externalTypeMapGroupRequests; - private readonly List _proxyTypeMapGroupRequests; private readonly List _possiblyOptimizedOutCastTargets; public AnalysisBasedMetadataManager(CompilerTypeSystemContext typeSystemContext) @@ -41,8 +39,7 @@ public AnalysisBasedMetadataManager(CompilerTypeSystemContext typeSystemContext) new NoDynamicInvokeThunkGenerationPolicy(), null, Array.Empty(), Array.Empty(), Array.Empty>(), Array.Empty>(), Array.Empty>(), Array.Empty(), - Array.Empty(), Array.Empty(), Array.Empty(), - Array.Empty(), + Array.Empty(), Array.Empty(), default) { } @@ -62,8 +59,6 @@ public AnalysisBasedMetadataManager( IEnumerable> reflectableFields, IEnumerable reflectableAttributes, IEnumerable reflectableParameters, - IEnumerable externalTypeMapGroupRequests, - IEnumerable proxyTypeMapGroupRequests, IEnumerable possiblyOptimizedOutCastTargets, MetadataManagerOptions options) : base(typeSystemContext, blockingPolicy, resourceBlockingPolicy, logFile, stackTracePolicy, invokeThunkGenerationPolicy, options, flowAnnotations) @@ -108,8 +103,6 @@ public AnalysisBasedMetadataManager( _reflectableParameters.Add(refParameter); } - _externalTypeMapGroupRequests = [.. externalTypeMapGroupRequests]; - _proxyTypeMapGroupRequests = [.. proxyTypeMapGroupRequests]; _possiblyOptimizedOutCastTargets = [.. possiblyOptimizedOutCastTargets]; #if DEBUG @@ -230,16 +223,6 @@ void ICompilationRootProvider.AddCompilationRoots(IRootingServiceProvider rootPr } } - foreach (var typeMapGroup in _externalTypeMapGroupRequests) - { - rootProvider.RootExternalTypeMapRequest(typeMapGroup, "TypeMapRequest"); - } - - foreach (var typeMapGroup in _proxyTypeMapGroupRequests) - { - rootProvider.RootProxyTypeMapRequest(typeMapGroup, "TypeMapRequest"); - } - foreach (var castTarget in _possiblyOptimizedOutCastTargets) { rootProvider.RootPossibleCastTarget(castTarget, "Possibly optimized out cast target"); diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/AnalysisBasedTypeMapManager.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/AnalysisBasedTypeMapManager.cs new file mode 100644 index 00000000000000..3e69a3764efffa --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/AnalysisBasedTypeMapManager.cs @@ -0,0 +1,36 @@ +// 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 Internal.TypeSystem; + +namespace ILCompiler +{ + public sealed class AnalysisBasedTypeMapManager : TypeMapManager + { + private readonly IEnumerable _usedExternalTypeMaps; + private readonly IEnumerable _usedProxyTypeMaps; + + internal AnalysisBasedTypeMapManager(TypeMapStates typeMaps, IEnumerable usedExternalTypeMaps, IEnumerable usedProxyTypeMaps) : base(typeMaps) + { + _usedExternalTypeMaps = usedExternalTypeMaps; + _usedProxyTypeMaps = usedProxyTypeMaps; + } + + public override void AddCompilationRoots(IRootingServiceProvider rootProvider) + { + const string reason = "Used Type Map Group"; + foreach (TypeDesc typeMapGroup in _usedExternalTypeMaps) + { + rootProvider.AddCompilationRoot(_typeMaps[typeMapGroup].GetExternalTypeMapNode(typeMapGroup), reason); + } + + foreach (TypeDesc typeMapGroup in _usedProxyTypeMaps) + { + rootProvider.AddCompilationRoot(_typeMaps[typeMapGroup].GetProxyTypeMapNode(typeMapGroup), reason); + } + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/CompilationBuilder.Aot.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/CompilationBuilder.Aot.cs index 85af60a86e6973..abaab9cc12433f 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/CompilationBuilder.Aot.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/CompilationBuilder.Aot.cs @@ -26,7 +26,7 @@ public partial class CompilationBuilder protected SecurityMitigationOptions _mitigationOptions; protected bool _dehydrate; protected bool _useDwarf5; - protected TypeMapManager _typeMapManager = new EmptyTypeMapManager(); + protected TypeMapManager _typeMapManager = new UsageBasedTypeMapManager(TypeMapManager.TypeMapStates.Empty); partial void InitializePartial() { diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ExternalTypeMapObjectNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ExternalTypeMapObjectNode.cs index 70e5bb3f0c3cf5..3bd00cad6b34e3 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ExternalTypeMapObjectNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ExternalTypeMapObjectNode.cs @@ -44,7 +44,7 @@ public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) Section hashTableSection = writer.NewSection(); hashTableSection.Place(typeMapGroupHashTable); - foreach (ExternalTypeMapNode externalTypeMap in factory.MetadataManager.GetExternalTypeMaps()) + foreach (ExternalTypeMapNode externalTypeMap in factory.TypeMapManager.GetExternalTypeMaps()) { if (!typeMapHashTables.TryGetValue(externalTypeMap.TypeMapGroup, out VertexHashtable typeMapHashTable)) { @@ -69,7 +69,7 @@ public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) typeMapGroupHashTable.Append((uint)typeMapGroup.GetHashCode(), hashTableSection.Place(tuple)); } - foreach (InvalidExternalTypeMapNode invalidNode in factory.MetadataManager.GetInvalidExternalTypeMaps()) + foreach (InvalidExternalTypeMapNode invalidNode in factory.TypeMapManager.GetInvalidExternalTypeMaps()) { TypeDesc typeMapGroup = invalidNode.TypeMapGroup; Vertex typeMapStateVertex = writer.GetUnsignedConstant(0); // Invalid type map state diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ProxyTypeMapObjectNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ProxyTypeMapObjectNode.cs index 1ee1fd58f7e0d9..d0e54a72f495b7 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ProxyTypeMapObjectNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ProxyTypeMapObjectNode.cs @@ -17,7 +17,7 @@ internal sealed class ProxyTypeMapObjectNode(ExternalReferencesTableNode externa { public void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) { - sb.Append(nameMangler.CompilationUnitPrefix).Append("__associated_type_map__"u8); + sb.Append(nameMangler.CompilationUnitPrefix).Append("__proxy_type_map__"u8); } public int Size { get; private set; } @@ -30,7 +30,7 @@ public void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) public override bool StaticDependenciesAreComputed => true; - protected override string GetName(NodeFactory context) => "Associated Type Map Hash Table"; + protected override string GetName(NodeFactory context) => "Proxy Type Map Hash Table"; public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) { // This node does not trigger generation of other nodes. @@ -45,7 +45,7 @@ public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) Section hashTableSection = writer.NewSection(); hashTableSection.Place(typeMapGroupHashTable); - foreach (ProxyTypeMapNode proxyTypeMap in factory.MetadataManager.GetProxyTypeMaps()) + foreach (ProxyTypeMapNode proxyTypeMap in factory.TypeMapManager.GetProxyTypeMaps()) { if (!typeMapHashTables.TryGetValue(proxyTypeMap.TypeMapGroup, out VertexHashtable typeMapHashTable)) { @@ -70,7 +70,7 @@ public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) typeMapGroupHashTable.Append((uint)typeMapGroup.GetHashCode(), hashTableSection.Place(tuple)); } - foreach (InvalidProxyTypeMapNode invalidNode in factory.MetadataManager.GetInvalidProxyTypeMaps()) + foreach (InvalidProxyTypeMapNode invalidNode in factory.TypeMapManager.GetInvalidProxyTypeMaps()) { TypeDesc typeMapGroup = invalidNode.TypeMapGroup; Vertex typeMapStateVertex = writer.GetUnsignedConstant(0); // Invalid type map state diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/EmptyTypeMapManager.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/EmptyTypeMapManager.cs deleted file mode 100644 index 222623db7c61da..00000000000000 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/EmptyTypeMapManager.cs +++ /dev/null @@ -1,17 +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.Generic; -using System.Text; -using ILCompiler.DependencyAnalysis; -using Internal.TypeSystem; - -namespace ILCompiler -{ - public sealed class EmptyTypeMapManager : TypeMapManager - { - public override void AddCompilationRoots(IRootingServiceProvider rootProvider) { } - public override void AddToReadyToRunHeader(ReadyToRunHeaderNode header, NodeFactory nodeFactory, ExternalReferencesTableNode commonFixupsTableNode) { } - } -} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ILScannerBuilder.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ILScannerBuilder.cs index 9f73a4d10a9141..732e6c761b07dd 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ILScannerBuilder.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ILScannerBuilder.cs @@ -25,7 +25,7 @@ public sealed class ILScannerBuilder private IEnumerable _compilationRoots = Array.Empty(); private MetadataManager _metadataManager; private InteropStubManager _interopStubManager = new EmptyInteropStubManager(); - private TypeMapManager _typeMapManager = new EmptyTypeMapManager(); + private TypeMapManager _typeMapManager = new UsageBasedTypeMapManager(TypeMapManager.TypeMapStates.Empty); private int _parallelism = -1; internal ILScannerBuilder(CompilerTypeSystemContext context, CompilationModuleGroup compilationGroup, NameMangler mangler, ILProvider ilProvider, PreinitializationManager preinitializationManager) diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/IRootingServiceProvider.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/IRootingServiceProvider.cs index 633e8b254fdcc8..ae616277399617 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/IRootingServiceProvider.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/IRootingServiceProvider.cs @@ -22,8 +22,6 @@ public interface IRootingServiceProvider void RootReadOnlyDataBlob(byte[] data, int alignment, string reason, string exportName, bool exportHidden); void RootDelegateMarshallingData(DefType type, string reason); void RootStructMarshallingData(DefType type, string reason); - void RootExternalTypeMapRequest(TypeDesc typeMapGroup, string reason); - void RootProxyTypeMapRequest(TypeDesc typeMapGroup, string reason); void RootPossibleCastTarget(TypeDesc type, string reason); void AddCompilationRoot(object o, string reason); } diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/MetadataBasedTypeMapManager.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/MetadataBasedTypeMapManager.cs deleted file mode 100644 index c34aa300edc524..00000000000000 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/MetadataBasedTypeMapManager.cs +++ /dev/null @@ -1,304 +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.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 Internal.TypeSystem.Ecma; -using DependencyList = ILCompiler.DependencyAnalysisFramework.DependencyNodeCore.DependencyList; -using ReflectionMapBlob = Internal.Runtime.ReflectionMapBlob; - -namespace ILCompiler -{ - /// - /// This class is responsible for managing emitted data for type maps. - /// - public sealed class MetadataBasedTypeMapManager : TypeMapManager - { - private sealed class TypeMapState - { - private readonly Dictionary _associatedTypeMap = []; - private readonly Dictionary _externalTypeMap = []; - private ThrowingMethodStub _externalTypeMapExceptionStub; - private ThrowingMethodStub _associatedTypeMapExceptionStub; - - public void AddAssociatedTypeMapEntry(TypeDesc type, TypeDesc associatedType) - { - if (!_associatedTypeMap.TryAdd(type, associatedType)) - { - ThrowHelper.ThrowBadImageFormatException(); - } - } - public void AddExternalTypeMapEntry(string typeName, TypeDesc type, TypeDesc trimmingTarget) - { - if (!_externalTypeMap.TryAdd(typeName, (type, trimmingTarget))) - { - ThrowHelper.ThrowBadImageFormatException(); - } - } - - public void SetExternalTypeMapStub(ThrowingMethodStub stub) - { - if (_externalTypeMapExceptionStub?.Exception is TypeSystemException.FileNotFoundException) - { - // FileNotFound exception takes precedence. - return; - } - _externalTypeMapExceptionStub ??= stub; - } - - public void SetAssociatedTypeMapStub(ThrowingMethodStub stub) - { - if (_associatedTypeMapExceptionStub?.Exception is TypeSystemException.FileNotFoundException) - { - // FileNotFound exception takes precedence. - return; - } - _associatedTypeMapExceptionStub ??= stub; - } - - public object GetExternalTypeMapNode(TypeDesc group) - { - if (_externalTypeMapExceptionStub is not null) - { - return new InvalidExternalTypeMapNode(group, _externalTypeMapExceptionStub); - } - return new ExternalTypeMapNode(group, _externalTypeMap); - } - - public object GetAssociatedTypeMapNode(TypeDesc group) - { - if (_associatedTypeMapExceptionStub is not null) - { - return new InvalidProxyTypeMapNode(group, _associatedTypeMapExceptionStub); - } - return new ProxyTypeMapNode(group, _associatedTypeMap); - } - } - - private sealed class ThrowingMethodStub(TypeDesc owningType, TypeDesc typeMapGroup, bool externalTypeMap, TypeSystemException ex) : ILStubMethod - { - public TypeSystemException Exception => ex; - public override string Name => $"InvalidTypeMapStub_{typeMapGroup}_{(externalTypeMap ? "External" : "Proxy")}"; - public override MethodIL EmitIL() - { - return TypeSystemThrowingILEmitter.EmitIL(this, Exception); - } - - protected override int CompareToImpl(MethodDesc other, TypeSystemComparer comparer) => other is ThrowingMethodStub otherStub ? Name.CompareTo(otherStub.Name) : -1; - - public override bool IsPInvoke => false; - - public override string DiagnosticName => Name; - - protected override int ClassCode => 1744789196; - - public override TypeDesc OwningType => owningType; - - public override MethodSignature Signature => new MethodSignature(MethodSignatureFlags.Static, 0, Context.GetWellKnownType(WellKnownType.Void), []); - - public override TypeSystemContext Context => owningType.Context; - } - - private Dictionary _typeMapStates = new Dictionary(); - - public MetadataBasedTypeMapManager(EcmaAssembly assembly) - { - HashSet scannedAssemblies = []; - - Queue assembliesToScan = new Queue(); - assembliesToScan.Enqueue(assembly); - - while (assembliesToScan.Count > 0) - { - EcmaAssembly currentAssembly = assembliesToScan.Dequeue(); - if (scannedAssemblies.Contains(currentAssembly)) - continue; - - scannedAssemblies.Add(currentAssembly); - - foreach (CustomAttributeHandle attrHandle in currentAssembly.MetadataReader.GetCustomAttributes(EntityHandle.AssemblyDefinition)) - { - CustomAttribute attr = currentAssembly.MetadataReader.GetCustomAttribute(attrHandle); - - if (!MetadataExtensions.GetAttributeTypeAndConstructor(currentAssembly.MetadataReader, attrHandle, out EntityHandle attributeType, out _)) - { - continue; - } - - TypeDesc type = (TypeDesc)currentAssembly.GetObject(attributeType); - - TypeMapAttributeKind attrKind = LookupTypeMapType(type); - - if (attrKind == TypeMapAttributeKind.None) - { - // Not a type map attribute, skip it - continue; - } - - CustomAttributeValue attrValue = attr.DecodeValue(new CustomAttributeTypeProvider(currentAssembly)); - - TypeDesc typeMapGroup = type.Instantiation[0]; - - try - { - switch (attrKind) - { - case TypeMapAttributeKind.TypeMapAssemblyTarget: - ProcessTypeMapAssemblyTargetAttribute(attrValue, typeMapGroup); - break; - - case TypeMapAttributeKind.TypeMap: - ProcessTypeMapAttribute(attrValue, typeMapGroup); - break; - - case TypeMapAttributeKind.TypeMapAssociation: - ProcessTypeMapAssociationAttribute(attrValue, typeMapGroup); - break; - - default: - Debug.Fail($"Unexpected TypeMapAttributeKind: {attrKind}"); - break; - } - } - catch (TypeSystemException ex) - { - if (!_typeMapStates.TryGetValue(typeMapGroup, out TypeMapState value)) - { - value = new TypeMapState(); - _typeMapStates[typeMapGroup] = value; - } - - if (attrKind is TypeMapAttributeKind.TypeMapAssemblyTarget or TypeMapAttributeKind.TypeMap) - { - value.SetExternalTypeMapStub(new ThrowingMethodStub(assembly.GetGlobalModuleType(), typeMapGroup, externalTypeMap: true, ex)); - } - - if (attrKind is TypeMapAttributeKind.TypeMapAssemblyTarget or TypeMapAttributeKind.TypeMapAssociation) - { - value.SetAssociatedTypeMapStub(new ThrowingMethodStub(assembly.GetGlobalModuleType(), typeMapGroup, externalTypeMap: false, ex)); - } - } - } - - void ProcessTypeMapAssemblyTargetAttribute(CustomAttributeValue attrValue, TypeDesc typeMapGroup) - { - // If attribute is TypeMapAssemblyTargetAttribute, we need to extract the generic argument (type map group) - // and process it. - if (attrValue.FixedArguments is not [{ Value: string assemblyName }]) - { - ThrowHelper.ThrowBadImageFormatException(); - return; - } - - EcmaAssembly targetAssembly = (EcmaAssembly)assembly.Context.ResolveAssembly(AssemblyNameInfo.Parse(assemblyName), throwIfNotFound: true); - - assembliesToScan.Enqueue(targetAssembly); - } - - void ProcessTypeMapAttribute(CustomAttributeValue attrValue, TypeDesc typeMapGroup) - { - switch (attrValue.FixedArguments) - { - case [{ Value: string typeName }, { Value: TypeDesc targetType }]: - { - if (!_typeMapStates.TryGetValue(typeMapGroup, out TypeMapState typeMapState)) - { - _typeMapStates[typeMapGroup] = typeMapState = new TypeMapState(); - } - typeMapState.AddExternalTypeMapEntry(typeName, targetType, targetType); - break; - } - - case [{ Value: string typeName }, { Value: TypeDesc targetType }, { Value: TypeDesc trimTargetType }]: - { - if (!_typeMapStates.TryGetValue(typeMapGroup, out TypeMapState typeMapState)) - { - _typeMapStates[typeMapGroup] = typeMapState = new TypeMapState(); - } - typeMapState.AddExternalTypeMapEntry(typeName, targetType, trimTargetType); - break; - } - - default: - ThrowHelper.ThrowBadImageFormatException(); - return; - } - } - - void ProcessTypeMapAssociationAttribute(CustomAttributeValue attrValue, TypeDesc typeMapGroup) - { - // If attribute is TypeMapAssociationAttribute, we need to extract the generic argument (type map group) - // and process it. - if (attrValue.FixedArguments is not [{ Value: TypeDesc type }, { Value: TypeDesc associatedType }]) - { - ThrowHelper.ThrowBadImageFormatException(); - return; - } - - if (!_typeMapStates.TryGetValue(typeMapGroup, out TypeMapState typeMapState)) - { - _typeMapStates[typeMapGroup] = typeMapState = new TypeMapState(); - } - - typeMapState.AddAssociatedTypeMapEntry(type, associatedType); - } - } - } - - private sealed class TypeMapsNode(IReadOnlyDictionary typeMapState) : DependencyNodeCore - { - public override bool InterestingForDynamicDependencyAnalysis => false; - - public override bool HasDynamicDependencies => false; - - public override bool HasConditionalStaticDependencies => true; - - public override bool StaticDependenciesAreComputed => true; - - public override IEnumerable GetConditionalStaticDependencies(NodeFactory context) - { - List entries = []; - foreach ((TypeDesc typeMapGroup, TypeMapState typeMapState) in typeMapState) - { - entries.Add(new CombinedDependencyListEntry(typeMapState.GetExternalTypeMapNode(typeMapGroup), context.ExternalTypeMapRequest(typeMapGroup), "ExternalTypeMap")); - entries.Add(new CombinedDependencyListEntry(typeMapState.GetAssociatedTypeMapNode(typeMapGroup), context.ProxyTypeMapRequest(typeMapGroup), "ProxyTypeMap")); - } - - return entries; - } - - 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) => "TypeMapsNode"; - } - - public override void AddCompilationRoots(IRootingServiceProvider rootProvider) - { - if (_typeMapStates.Count == 0) - { - return; // No type maps to process - } - - rootProvider.AddCompilationRoot(new TypeMapsNode(_typeMapStates), "TypeMapManager"); - } - - public override void AddToReadyToRunHeader(ReadyToRunHeaderNode header, NodeFactory nodeFactory, ExternalReferencesTableNode commonFixupsTableNode) - { - if (_typeMapStates.Count == 0) - { - 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/MetadataManager.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/MetadataManager.cs index 47461eec807c19..1476d46cbd10e0 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/MetadataManager.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/MetadataManager.cs @@ -82,10 +82,6 @@ private readonly SortedSet _typeGVMEntries private readonly SortedSet _genericMethodHashtableEntries = new SortedSet(TypeSystemComparer.Instance); private readonly SortedSet _exactMethodHashtableEntries = new SortedSet(TypeSystemComparer.Instance); private readonly HashSet _usedInterfaces = new HashSet(); - private readonly SortedSet _externalTypeMaps = new SortedSet(CompilerComparer.Instance); - private readonly SortedSet _invalidExternalTypeMaps = new SortedSet(CompilerComparer.Instance); - private readonly SortedSet _proxyTypeMaps = new SortedSet(CompilerComparer.Instance); - private readonly SortedSet _invalidProxyTypeMaps = new SortedSet(CompilerComparer.Instance); private List<(DehydratableObjectNode Node, ObjectNode.ObjectData Data)> _dehydratableData = new List<(DehydratableObjectNode Node, ObjectNode.ObjectData data)>(); @@ -351,27 +347,6 @@ protected virtual void Graph_NewMarkedNode(DependencyNodeCore obj) { _usedInterfaces.Add(interfaceUse.Type); } - - if (obj is ExternalTypeMapNode externalTypeMapNode) - { - _externalTypeMaps.Add(externalTypeMapNode); - } - - if (obj is InvalidExternalTypeMapNode invalidExternalTypeMapNode) - { - _invalidExternalTypeMaps.Add(invalidExternalTypeMapNode); - } - - - if (obj is ProxyTypeMapNode proxyTypeMapNode) - { - _proxyTypeMaps.Add(proxyTypeMapNode); - } - - if (obj is InvalidProxyTypeMapNode invalidProxyTypeMapNode) - { - _invalidProxyTypeMaps.Add(invalidProxyTypeMapNode); - } } protected virtual bool AllMethodsCanBeReflectable => false; @@ -1135,26 +1110,6 @@ internal IEnumerable GetTemplateM return _templateMethodEntries; } - internal IEnumerable GetExternalTypeMaps() - { - return _externalTypeMaps; - } - - internal IEnumerable GetInvalidExternalTypeMaps() - { - return _invalidExternalTypeMaps; - } - - internal IEnumerable GetProxyTypeMaps() - { - return _proxyTypeMaps; - } - - internal IEnumerable GetInvalidProxyTypeMaps() - { - return _invalidProxyTypeMaps; - } - public bool IsReflectionBlocked(TypeDesc type) { switch (type.Category) diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/RootingServiceProvider.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/RootingServiceProvider.cs index 6694b375c76c60..89d5593c04e90b 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/RootingServiceProvider.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/RootingServiceProvider.cs @@ -158,16 +158,6 @@ public void RootStructMarshallingData(DefType type, string reason) _rootAdder(_factory.StructMarshallingData(type), reason); } - public void RootExternalTypeMapRequest(TypeDesc typeMapGroup, string reason) - { - _rootAdder(_factory.ExternalTypeMapRequest(typeMapGroup), reason); - } - - public void RootProxyTypeMapRequest(TypeDesc typeMapGroup, string reason) - { - _rootAdder(_factory.ProxyTypeMapRequest(typeMapGroup), reason); - } - public void RootPossibleCastTarget(TypeDesc type, string reason) { _rootAdder(_factory.ScannedCastTarget(type), reason); diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/TypeMapManager.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/TypeMapManager.cs index c1dabea7db40f7..7ff71b7c2f3ed1 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/TypeMapManager.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/TypeMapManager.cs @@ -1,13 +1,142 @@ -// Licensed to the .NET Foundation under one or more agreements. +// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +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 Internal.TypeSystem.Ecma; +using DependencyList = ILCompiler.DependencyAnalysisFramework.DependencyNodeCore.DependencyList; +using ReflectionMapBlob = Internal.Runtime.ReflectionMapBlob; namespace ILCompiler { + /// + /// This class is responsible for managing emitted data for type maps. + /// public abstract class TypeMapManager : ICompilationRootProvider { + protected internal sealed class TypeMapState + { + private readonly Dictionary _associatedTypeMap = []; + private readonly Dictionary _externalTypeMap = []; + private ThrowingMethodStub _externalTypeMapExceptionStub; + private ThrowingMethodStub _associatedTypeMapExceptionStub; + + public void AddAssociatedTypeMapEntry(TypeDesc type, TypeDesc associatedType) + { + if (!_associatedTypeMap.TryAdd(type, associatedType)) + { + ThrowHelper.ThrowBadImageFormatException(); + } + } + public void AddExternalTypeMapEntry(string typeName, TypeDesc type, TypeDesc trimmingTarget) + { + if (!_externalTypeMap.TryAdd(typeName, (type, trimmingTarget))) + { + ThrowHelper.ThrowBadImageFormatException(); + } + } + + public void SetExternalTypeMapStub(ThrowingMethodStub stub) + { + if (_externalTypeMapExceptionStub?.Exception is TypeSystemException.FileNotFoundException) + { + // FileNotFound exception takes precedence. + return; + } + _externalTypeMapExceptionStub ??= stub; + } + + public void SetAssociatedTypeMapStub(ThrowingMethodStub stub) + { + if (_associatedTypeMapExceptionStub?.Exception is TypeSystemException.FileNotFoundException) + { + // FileNotFound exception takes precedence. + return; + } + _associatedTypeMapExceptionStub ??= stub; + } + + public object GetExternalTypeMapNode(TypeDesc group) + { + if (_externalTypeMapExceptionStub is not null) + { + return new InvalidExternalTypeMapNode(group, _externalTypeMapExceptionStub); + } + return new ExternalTypeMapNode(group, _externalTypeMap); + } + + public object GetProxyTypeMapNode(TypeDesc group) + { + if (_associatedTypeMapExceptionStub is not null) + { + return new InvalidProxyTypeMapNode(group, _associatedTypeMapExceptionStub); + } + return new ProxyTypeMapNode(group, _associatedTypeMap); + } + } + + public sealed class TypeMapStates + { + public static readonly TypeMapStates Empty = new TypeMapStates(new Dictionary()); + + private readonly IReadOnlyDictionary _states; + + internal TypeMapStates(IReadOnlyDictionary states) + { + _states = states; + } + + internal TypeMapState this[TypeDesc typeMapGroup] => _states[typeMapGroup]; + + public bool IsEmpty => _states.Count == 0; + + internal IEnumerable> States => _states; + } + + protected internal sealed class ThrowingMethodStub(TypeDesc owningType, TypeDesc typeMapGroup, bool externalTypeMap, TypeSystemException ex) : ILStubMethod + { + public TypeSystemException Exception => ex; + public override string Name => $"InvalidTypeMapStub_{typeMapGroup}_{(externalTypeMap ? "External" : "Proxy")}"; + public override MethodIL EmitIL() + { + return TypeSystemThrowingILEmitter.EmitIL(this, Exception); + } + + protected override int CompareToImpl(MethodDesc other, TypeSystemComparer comparer) => other is ThrowingMethodStub otherStub ? Name.CompareTo(otherStub.Name) : -1; + + public override bool IsPInvoke => false; + + public override string DiagnosticName => Name; + + protected override int ClassCode => 1744789196; + + public override TypeDesc OwningType => owningType; + + public override MethodSignature Signature => new MethodSignature(MethodSignatureFlags.Static, 0, Context.GetWellKnownType(WellKnownType.Void), []); + + public override TypeSystemContext Context => owningType.Context; + } + + protected readonly TypeMapStates _typeMaps; + + private readonly SortedSet _externalTypeMaps = new SortedSet(CompilerComparer.Instance); + private readonly SortedSet _invalidExternalTypeMaps = new SortedSet(CompilerComparer.Instance); + private readonly SortedSet _proxyTypeMaps = new SortedSet(CompilerComparer.Instance); + private readonly SortedSet _invalidProxyTypeMaps = new SortedSet(CompilerComparer.Instance); + + protected TypeMapManager(TypeMapStates typeMapStates) + { + _typeMaps = typeMapStates; + } public enum TypeMapAttributeKind { None, @@ -15,9 +144,6 @@ public enum TypeMapAttributeKind TypeMap, TypeMapAssociation } - public abstract void AddCompilationRoots(IRootingServiceProvider rootProvider); - public abstract void AddToReadyToRunHeader(ReadyToRunHeaderNode header, NodeFactory nodeFactory, ExternalReferencesTableNode commonFixupsTableNode); - public static TypeMapAttributeKind LookupTypeMapType(TypeDesc attrType) { TypeDesc typeDef = attrType.GetTypeDefinition(); @@ -29,5 +155,211 @@ public static TypeMapAttributeKind LookupTypeMapType(TypeDesc attrType) _ => TypeMapAttributeKind.None, }; } + + public static TypeMapStates CreateTypeMapStateRootedAtAssembly(EcmaAssembly assembly) + { + Dictionary typeMapStates = []; + HashSet scannedAssemblies = []; + + Queue assembliesToScan = new Queue(); + assembliesToScan.Enqueue(assembly); + + while (assembliesToScan.Count > 0) + { + EcmaAssembly currentAssembly = assembliesToScan.Dequeue(); + if (scannedAssemblies.Contains(currentAssembly)) + continue; + + scannedAssemblies.Add(currentAssembly); + + foreach (CustomAttributeHandle attrHandle in currentAssembly.MetadataReader.GetCustomAttributes(EntityHandle.AssemblyDefinition)) + { + CustomAttribute attr = currentAssembly.MetadataReader.GetCustomAttribute(attrHandle); + + if (!MetadataExtensions.GetAttributeTypeAndConstructor(currentAssembly.MetadataReader, attrHandle, out EntityHandle attributeType, out _)) + { + continue; + } + + TypeDesc type = (TypeDesc)currentAssembly.GetObject(attributeType); + + TypeMapAttributeKind attrKind = LookupTypeMapType(type); + + if (attrKind == TypeMapAttributeKind.None) + { + // Not a type map attribute, skip it + continue; + } + + CustomAttributeValue attrValue = attr.DecodeValue(new CustomAttributeTypeProvider(currentAssembly)); + + TypeDesc typeMapGroup = type.Instantiation[0]; + + try + { + switch (attrKind) + { + case TypeMapAttributeKind.TypeMapAssemblyTarget: + ProcessTypeMapAssemblyTargetAttribute(attrValue); + break; + + case TypeMapAttributeKind.TypeMap: + ProcessTypeMapAttribute(attrValue, typeMapGroup); + break; + + case TypeMapAttributeKind.TypeMapAssociation: + ProcessTypeMapAssociationAttribute(attrValue, typeMapGroup); + break; + + default: + Debug.Fail($"Unexpected TypeMapAttributeKind: {attrKind}"); + break; + } + } + catch (TypeSystemException ex) + { + if (!typeMapStates.TryGetValue(typeMapGroup, out TypeMapState value)) + { + value = new TypeMapState(); + typeMapStates[typeMapGroup] = value; + } + + if (attrKind is TypeMapAttributeKind.TypeMapAssemblyTarget or TypeMapAttributeKind.TypeMap) + { + value.SetExternalTypeMapStub(new ThrowingMethodStub(assembly.GetGlobalModuleType(), typeMapGroup, externalTypeMap: true, ex)); + } + + if (attrKind is TypeMapAttributeKind.TypeMapAssemblyTarget or TypeMapAttributeKind.TypeMapAssociation) + { + value.SetAssociatedTypeMapStub(new ThrowingMethodStub(assembly.GetGlobalModuleType(), typeMapGroup, externalTypeMap: false, ex)); + } + } + } + + void ProcessTypeMapAssemblyTargetAttribute(CustomAttributeValue attrValue) + { + if (attrValue.FixedArguments is not [{ Value: string assemblyName }]) + { + ThrowHelper.ThrowBadImageFormatException(); + return; + } + + EcmaAssembly targetAssembly = (EcmaAssembly)assembly.Context.ResolveAssembly(AssemblyNameInfo.Parse(assemblyName), throwIfNotFound: true); + + assembliesToScan.Enqueue(targetAssembly); + } + + void ProcessTypeMapAttribute(CustomAttributeValue attrValue, TypeDesc typeMapGroup) + { + switch (attrValue.FixedArguments) + { + case [{ Value: string typeName }, { Value: TypeDesc targetType }]: + { + if (!typeMapStates.TryGetValue(typeMapGroup, out TypeMapState typeMapState)) + { + typeMapStates[typeMapGroup] = typeMapState = new TypeMapState(); + } + typeMapState.AddExternalTypeMapEntry(typeName, targetType, targetType); + break; + } + + case [{ Value: string typeName }, { Value: TypeDesc targetType }, { Value: TypeDesc trimTargetType }]: + { + if (!typeMapStates.TryGetValue(typeMapGroup, out TypeMapState typeMapState)) + { + typeMapStates[typeMapGroup] = typeMapState = new TypeMapState(); + } + typeMapState.AddExternalTypeMapEntry(typeName, targetType, trimTargetType); + break; + } + + default: + ThrowHelper.ThrowBadImageFormatException(); + return; + } + } + + void ProcessTypeMapAssociationAttribute(CustomAttributeValue attrValue, TypeDesc typeMapGroup) + { + // If attribute is TypeMapAssociationAttribute, we need to extract the generic argument (type map group) + // and process it. + if (attrValue.FixedArguments is not [{ Value: TypeDesc type }, { Value: TypeDesc associatedType }]) + { + ThrowHelper.ThrowBadImageFormatException(); + return; + } + + if (!typeMapStates.TryGetValue(typeMapGroup, out TypeMapState typeMapState)) + { + typeMapStates[typeMapGroup] = typeMapState = new TypeMapState(); + } + + typeMapState.AddAssociatedTypeMapEntry(type, associatedType); + } + } + + return new TypeMapStates(typeMapStates); + } + + public virtual void AttachToDependencyGraph(DependencyAnalyzerBase graph) + { + graph.NewMarkedNode += Graph_NewMarkedNode; + } + + protected virtual void Graph_NewMarkedNode(DependencyNodeCore obj) + { + if (obj is ExternalTypeMapNode externalTypeMapNode) + { + _externalTypeMaps.Add(externalTypeMapNode); + } + + if (obj is InvalidExternalTypeMapNode invalidExternalTypeMapNode) + { + _invalidExternalTypeMaps.Add(invalidExternalTypeMapNode); + } + + if (obj is ProxyTypeMapNode proxyTypeMapNode) + { + _proxyTypeMaps.Add(proxyTypeMapNode); + } + + if (obj is InvalidProxyTypeMapNode invalidProxyTypeMapNode) + { + _invalidProxyTypeMaps.Add(invalidProxyTypeMapNode); + } + } + + public void AddToReadyToRunHeader(ReadyToRunHeaderNode header, NodeFactory nodeFactory, ExternalReferencesTableNode commonFixupsTableNode) + { + if (_typeMaps.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)); + } + + internal IEnumerable GetExternalTypeMaps() + { + return _externalTypeMaps; + } + + internal IEnumerable GetInvalidExternalTypeMaps() + { + return _invalidExternalTypeMaps; + } + + internal IEnumerable GetProxyTypeMaps() + { + return _proxyTypeMaps; + } + + internal IEnumerable GetInvalidProxyTypeMaps() + { + return _invalidProxyTypeMaps; + } + + public abstract void AddCompilationRoots(IRootingServiceProvider rootProvider); } } diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/UsageBasedMetadataManager.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/UsageBasedMetadataManager.cs index 9d9bef05ff532b..d55fe1028e83f8 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/UsageBasedMetadataManager.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/UsageBasedMetadataManager.cs @@ -56,8 +56,6 @@ private static (string AttributeName, DiagnosticId Id)[] _requiresAttributeMisma private readonly List _fieldsWithRuntimeMapping = new List(); private readonly List _customAttributesWithMetadata = new List(); private readonly List _parametersWithMetadata = new List(); - private readonly List _externalTypeMapGroup = new List(); - private readonly List _proxyTypeMapGroup = new List(); private readonly List _possiblyOptimizedOutCastTargets = new List(); internal IReadOnlyDictionary FeatureSwitches { get; } @@ -173,16 +171,6 @@ protected override void Graph_NewMarkedNode(DependencyNodeCore obj) _typesWithForcedEEType.Add(reflectableType.Type); } - if (obj is ExternalTypeMapRequestNode externalTypeMapRequest) - { - _externalTypeMapGroup.Add(externalTypeMapRequest.TypeMapGroup); - } - - if (obj is ProxyTypeMapRequestNode proxyTypeMapRequestNode) - { - _proxyTypeMapGroup.Add(proxyTypeMapRequestNode.TypeMapGroup); - } - if (obj is ScannedCastTargetNode castTarget) { _possiblyOptimizedOutCastTargets.Add(castTarget.Type); @@ -962,8 +950,7 @@ public MetadataManager ToAnalysisBasedMetadataManager() return new AnalysisBasedMetadataManager( _typeSystemContext, _blockingPolicy, _resourceBlockingPolicy, _metadataLogFile, _stackTraceEmissionPolicy, _dynamicInvokeThunkGenerationPolicy, FlowAnnotations, _modulesWithMetadata, _typesWithForcedEEType, reflectableTypes.ToEnumerable(), reflectableMethods.ToEnumerable(), - reflectableFields.ToEnumerable(), _customAttributesWithMetadata, _parametersWithMetadata, - _externalTypeMapGroup, _proxyTypeMapGroup, _possiblyOptimizedOutCastTargets, _options); + reflectableFields.ToEnumerable(), _customAttributesWithMetadata, _parametersWithMetadata, _possiblyOptimizedOutCastTargets, _options); } private void AddDataflowDependency(ref DependencyList dependencies, NodeFactory factory, MethodIL methodIL, string reason) diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/UsageBasedTypeMapManager.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/UsageBasedTypeMapManager.cs new file mode 100644 index 00000000000000..40b6440d1b62bc --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/UsageBasedTypeMapManager.cs @@ -0,0 +1,78 @@ +// 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.DependencyAnalysisFramework; +using Internal.TypeSystem; +using Internal.TypeSystem.Ecma; + +namespace ILCompiler +{ + public sealed class UsageBasedTypeMapManager : TypeMapManager + { + private sealed class TypeMapsNode(TypeMapStates typeMapState) : DependencyNodeCore + { + public override bool InterestingForDynamicDependencyAnalysis => false; + + public override bool HasDynamicDependencies => false; + + public override bool HasConditionalStaticDependencies => true; + + public override bool StaticDependenciesAreComputed => true; + + public override IEnumerable GetConditionalStaticDependencies(NodeFactory context) + { + List entries = []; + foreach ((TypeDesc typeMapGroup, TypeMapState typeMapState) in typeMapState.States) + { + entries.Add(new CombinedDependencyListEntry(typeMapState.GetExternalTypeMapNode(typeMapGroup), context.ExternalTypeMapRequest(typeMapGroup), "ExternalTypeMap")); + entries.Add(new CombinedDependencyListEntry(typeMapState.GetProxyTypeMapNode(typeMapGroup), context.ProxyTypeMapRequest(typeMapGroup), "ProxyTypeMap")); + } + + return entries; + } + + 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) => "TypeMapsNode"; + } + + private readonly List _usedExternalTypeMap = new List(); + private readonly List _usedProxyTypeMap = new List(); + + public UsageBasedTypeMapManager(TypeMapStates state) : base(state) { } + + protected override void Graph_NewMarkedNode(DependencyNodeCore obj) + { + base.Graph_NewMarkedNode(obj); + + if (obj is ExternalTypeMapRequestNode externalTypeMapRequest) + { + _usedExternalTypeMap.Add(externalTypeMapRequest.TypeMapGroup); + } + + if (obj is ProxyTypeMapRequestNode proxyTypeMapRequestNode) + { + _usedProxyTypeMap.Add(proxyTypeMapRequestNode.TypeMapGroup); + } + } + + public override void AddCompilationRoots(IRootingServiceProvider rootProvider) + { + if (_typeMaps.IsEmpty) + { + return; // No type maps to process + } + + rootProvider.AddCompilationRoot(new TypeMapsNode(_typeMaps), "TypeMapManager"); + } + + public AnalysisBasedTypeMapManager ToAnalysisBasedTypeMapManager() + { + return new AnalysisBasedTypeMapManager(_typeMaps, _usedExternalTypeMap, _usedProxyTypeMap); + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/ILCompiler.Compiler.csproj b/src/coreclr/tools/aot/ILCompiler.Compiler/ILCompiler.Compiler.csproj index da46e8be7903f4..c90dd7577189ee 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/ILCompiler.Compiler.csproj +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/ILCompiler.Compiler.csproj @@ -343,6 +343,7 @@ + @@ -456,7 +457,6 @@ - @@ -633,7 +633,6 @@ - @@ -656,10 +655,11 @@ - + + diff --git a/src/coreclr/tools/aot/ILCompiler.Trimming.Tests/TestCasesRunner/TrimmingDriver.cs b/src/coreclr/tools/aot/ILCompiler.Trimming.Tests/TestCasesRunner/TrimmingDriver.cs index 6ecbbe21caa2db..b6d3891e104e52 100644 --- a/src/coreclr/tools/aot/ILCompiler.Trimming.Tests/TestCasesRunner/TrimmingDriver.cs +++ b/src/coreclr/tools/aot/ILCompiler.Trimming.Tests/TestCasesRunner/TrimmingDriver.cs @@ -139,10 +139,10 @@ public ILScanResults Trim (ILCompilerOptions options, TrimmingCustomizations? cu InteropStateManager interopStateManager = new InteropStateManager (typeSystemContext.GeneratedAssembly); InteropStubManager interopStubManager = new UsageBasedInteropStubManager (interopStateManager, pinvokePolicy, logger); - TypeMapManager typeMapManager = new EmptyTypeMapManager(); + TypeMapManager typeMapManager = new UsageBasedTypeMapManager (TypeMapManager.TypeMapStates.Empty); if (entrypointModule is { Assembly: EcmaAssembly entryAssembly }) { - typeMapManager = new MetadataBasedTypeMapManager (entryAssembly); + typeMapManager = new UsageBasedTypeMapManager (TypeMapManager.CreateTypeMapStateRootedAtAssembly (entryAssembly)); } compilationRoots.Add(typeMapManager); diff --git a/src/coreclr/tools/aot/ILCompiler/Program.cs b/src/coreclr/tools/aot/ILCompiler/Program.cs index d231a842b7173b..6675b995e0b611 100644 --- a/src/coreclr/tools/aot/ILCompiler/Program.cs +++ b/src/coreclr/tools/aot/ILCompiler/Program.cs @@ -192,7 +192,7 @@ public int Run() CompilationModuleGroup compilationGroup; List compilationRoots = new List(); - TypeMapManager typeMapManager = new EmptyTypeMapManager(); + TypeMapManager typeMapManager = new UsageBasedTypeMapManager(TypeMapManager.TypeMapStates.Empty); bool multiFile = Get(_command.MultiFile); if (singleMethod != null) { @@ -201,7 +201,7 @@ public int Run() compilationRoots.Add(new SingleMethodRootProvider(singleMethod)); if (singleMethod.OwningType is MetadataType { Module.Assembly: EcmaAssembly assembly }) { - compilationRoots.Add(typeMapManager = new MetadataBasedTypeMapManager(assembly)); + compilationRoots.Add(typeMapManager = new UsageBasedTypeMapManager(TypeMapManager.CreateTypeMapStateRootedAtAssembly(assembly))); } } else @@ -315,7 +315,7 @@ public int Run() if (entrypointModule is { Assembly: EcmaAssembly entryAssembly }) { - compilationRoots.Add(typeMapManager = new MetadataBasedTypeMapManager(entryAssembly)); + compilationRoots.Add(typeMapManager = new UsageBasedTypeMapManager(TypeMapManager.CreateTypeMapStateRootedAtAssembly(entryAssembly))); } } @@ -525,6 +525,8 @@ void RunScanner() metadataManager = ((UsageBasedMetadataManager)metadataManager).ToAnalysisBasedMetadataManager(); + typeMapManager = ((UsageBasedTypeMapManager)typeMapManager).ToAnalysisBasedTypeMapManager(); + interopStubManager = scanResults.GetInteropStubManager(interopStateManager, pinvokePolicy); ilProvider = new SubstitutedILProvider(unsubstitutedILProvider, substitutionProvider, devirtualizationManager, metadataManager); @@ -596,6 +598,7 @@ void RunScanner() compilationRoots.Add(metadataManager); compilationRoots.Add(interopStubManager); + compilationRoots.Add(typeMapManager); builder .UseInstructionSetSupport(instructionSetSupport) From 462334cf3e30c8b6b6a1c23446c7c480cac03370 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Fri, 6 Jun 2025 14:42:46 -0700 Subject: [PATCH 23/45] We know all the type map nodes up front in the AnalysisBasedTypeMapManager. Utilize that --- .../Compiler/AnalysisBasedTypeMapManager.cs | 77 +++++++++++++++++-- .../DependencyAnalysis/NodeFactory.cs | 1 + .../Compiler/TypeMapManager.cs | 47 +++++------ .../Compiler/UsageBasedTypeMapManager.cs | 46 ++++++++++- 4 files changed, 132 insertions(+), 39 deletions(-) diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/AnalysisBasedTypeMapManager.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/AnalysisBasedTypeMapManager.cs index 3e69a3764efffa..7c643a1fea654f 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/AnalysisBasedTypeMapManager.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/AnalysisBasedTypeMapManager.cs @@ -3,34 +3,95 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.Text; +using ILCompiler.DependencyAnalysis; using Internal.TypeSystem; namespace ILCompiler { public sealed class AnalysisBasedTypeMapManager : TypeMapManager { - private readonly IEnumerable _usedExternalTypeMaps; - private readonly IEnumerable _usedProxyTypeMaps; + private readonly IEnumerable _externalTypeMapNodes; + private readonly IEnumerable _proxyTypeMapNodes; + private readonly IEnumerable _invalidExternalTypeMapNodes; + private readonly IEnumerable _invalidProxyTypeMapNodes; internal AnalysisBasedTypeMapManager(TypeMapStates typeMaps, IEnumerable usedExternalTypeMaps, IEnumerable usedProxyTypeMaps) : base(typeMaps) { - _usedExternalTypeMaps = usedExternalTypeMaps; - _usedProxyTypeMaps = usedProxyTypeMaps; + List externalTypeMapNodes = []; + List invalidExternalTypeMapNodes = []; + List invalidProxyTypeMapNodes = []; + List proxyTypeMapNodes = []; + + foreach (TypeDesc typeMapGroup in usedExternalTypeMaps) + { + object externalTypeMapNode = _typeMaps[typeMapGroup].GetExternalTypeMapNode(typeMapGroup); + switch (externalTypeMapNode) + { + case ExternalTypeMapNode valid: + externalTypeMapNodes.Add(valid); + break; + case InvalidExternalTypeMapNode invalid: + invalidExternalTypeMapNodes.Add(invalid); + break; + default: + Debug.Fail("External type map node should be a known node type."); + break; + } + } + + foreach (TypeDesc typeMapGroup in usedProxyTypeMaps) + { + object proxyTypeMapNode = _typeMaps[typeMapGroup].GetProxyTypeMapNode(typeMapGroup); + switch (proxyTypeMapNode) + { + case ProxyTypeMapNode valid: + proxyTypeMapNodes.Add(valid); + break; + case InvalidProxyTypeMapNode invalid: + invalidProxyTypeMapNodes.Add(invalid); + break; + default: + Debug.Fail("External type map node should be a known node type."); + break; + } + } + + _externalTypeMapNodes = externalTypeMapNodes; + _proxyTypeMapNodes = proxyTypeMapNodes; + _invalidProxyTypeMapNodes = invalidProxyTypeMapNodes; + _invalidExternalTypeMapNodes = invalidExternalTypeMapNodes; } public override void AddCompilationRoots(IRootingServiceProvider rootProvider) { const string reason = "Used Type Map Group"; - foreach (TypeDesc typeMapGroup in _usedExternalTypeMaps) + + foreach (ExternalTypeMapNode externalTypeMap in _externalTypeMapNodes) { - rootProvider.AddCompilationRoot(_typeMaps[typeMapGroup].GetExternalTypeMapNode(typeMapGroup), reason); + rootProvider.AddCompilationRoot(externalTypeMap, reason); } - foreach (TypeDesc typeMapGroup in _usedProxyTypeMaps) + foreach (InvalidExternalTypeMapNode invalidExternalMap in _invalidExternalTypeMapNodes) { - rootProvider.AddCompilationRoot(_typeMaps[typeMapGroup].GetProxyTypeMapNode(typeMapGroup), reason); + rootProvider.AddCompilationRoot(invalidExternalMap, reason); + } + + foreach (ProxyTypeMapNode proxyTypeMap in _proxyTypeMapNodes) + { + rootProvider.AddCompilationRoot(proxyTypeMap, reason); + } + + foreach (InvalidProxyTypeMapNode invalidProxyMap in _invalidProxyTypeMapNodes) + { + rootProvider.AddCompilationRoot(invalidProxyMap, reason); } } + + internal override IEnumerable GetExternalTypeMaps() => _externalTypeMapNodes; + internal override IEnumerable GetProxyTypeMaps() => _proxyTypeMapNodes; + internal override IEnumerable GetInvalidExternalTypeMaps() => _invalidExternalTypeMapNodes; + internal override IEnumerable GetInvalidProxyTypeMaps() => _invalidProxyTypeMapNodes; } } 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 4bfecead2377e3..971e971d9679c4 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/NodeFactory.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/NodeFactory.cs @@ -1582,6 +1582,7 @@ public virtual void AttachToDependencyGraph(DependencyAnalyzerBase TypeMapManager.AddToReadyToRunHeader(ReadyToRunHeader, this, commonFixupsTableNode); MetadataManager.AddToReadyToRunHeader(ReadyToRunHeader, this, commonFixupsTableNode); MetadataManager.AttachToDependencyGraph(graph); + TypeMapManager.AttachToDependencyGraph(graph); ReadyToRunHeader.Add(MetadataManager.BlobIdToReadyToRunSection(ReflectionMapBlob.CommonFixupsTable), commonFixupsTableNode); } diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/TypeMapManager.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/TypeMapManager.cs index 7ff71b7c2f3ed1..84079fb32782b2 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/TypeMapManager.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/TypeMapManager.cs @@ -111,7 +111,7 @@ public override MethodIL EmitIL() return TypeSystemThrowingILEmitter.EmitIL(this, Exception); } - protected override int CompareToImpl(MethodDesc other, TypeSystemComparer comparer) => other is ThrowingMethodStub otherStub ? Name.CompareTo(otherStub.Name) : -1; + protected override int CompareToImpl(MethodDesc other, TypeSystemComparer comparer) => other is ThrowingMethodStub otherStub ? Name.CompareTo(otherStub.Name, StringComparison.Ordinal) : -1; public override bool IsPInvoke => false; @@ -128,15 +128,11 @@ public override MethodIL EmitIL() protected readonly TypeMapStates _typeMaps; - private readonly SortedSet _externalTypeMaps = new SortedSet(CompilerComparer.Instance); - private readonly SortedSet _invalidExternalTypeMaps = new SortedSet(CompilerComparer.Instance); - private readonly SortedSet _proxyTypeMaps = new SortedSet(CompilerComparer.Instance); - private readonly SortedSet _invalidProxyTypeMaps = new SortedSet(CompilerComparer.Instance); - protected TypeMapManager(TypeMapStates typeMapStates) { _typeMaps = typeMapStates; } + public enum TypeMapAttributeKind { None, @@ -301,11 +297,16 @@ void ProcessTypeMapAssociationAttribute(CustomAttributeValue attrValue return new TypeMapStates(typeMapStates); } - public virtual void AttachToDependencyGraph(DependencyAnalyzerBase graph) + public void AttachToDependencyGraph(DependencyAnalyzerBase graph) { graph.NewMarkedNode += Graph_NewMarkedNode; } + private readonly SortedSet _externalTypeMaps = new SortedSet(CompilerComparer.Instance); + private readonly SortedSet _invalidExternalTypeMaps = new SortedSet(CompilerComparer.Instance); + private readonly SortedSet _proxyTypeMaps = new SortedSet(CompilerComparer.Instance); + private readonly SortedSet _invalidProxyTypeMaps = new SortedSet(CompilerComparer.Instance); + protected virtual void Graph_NewMarkedNode(DependencyNodeCore obj) { if (obj is ExternalTypeMapNode externalTypeMapNode) @@ -329,6 +330,16 @@ protected virtual void Graph_NewMarkedNode(DependencyNodeCore obj) } } + internal abstract IEnumerable GetExternalTypeMaps(); + + internal abstract IEnumerable GetInvalidExternalTypeMaps(); + + internal abstract IEnumerable GetProxyTypeMaps(); + + internal abstract IEnumerable GetInvalidProxyTypeMaps(); + + public abstract void AddCompilationRoots(IRootingServiceProvider rootProvider); + public void AddToReadyToRunHeader(ReadyToRunHeaderNode header, NodeFactory nodeFactory, ExternalReferencesTableNode commonFixupsTableNode) { if (_typeMaps.IsEmpty) @@ -339,27 +350,5 @@ public void AddToReadyToRunHeader(ReadyToRunHeaderNode header, NodeFactory nodeF header.Add(MetadataManager.BlobIdToReadyToRunSection(ReflectionMapBlob.ExternalTypeMap), new ExternalTypeMapObjectNode(commonFixupsTableNode)); header.Add(MetadataManager.BlobIdToReadyToRunSection(ReflectionMapBlob.ProxyTypeMap), new ProxyTypeMapObjectNode(commonFixupsTableNode)); } - - internal IEnumerable GetExternalTypeMaps() - { - return _externalTypeMaps; - } - - internal IEnumerable GetInvalidExternalTypeMaps() - { - return _invalidExternalTypeMaps; - } - - internal IEnumerable GetProxyTypeMaps() - { - return _proxyTypeMaps; - } - - internal IEnumerable GetInvalidProxyTypeMaps() - { - return _invalidProxyTypeMaps; - } - - public abstract void AddCompilationRoots(IRootingServiceProvider rootProvider); } } diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/UsageBasedTypeMapManager.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/UsageBasedTypeMapManager.cs index 40b6440d1b62bc..1c3d4d0b0bf4c1 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/UsageBasedTypeMapManager.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/UsageBasedTypeMapManager.cs @@ -11,7 +11,7 @@ namespace ILCompiler { - public sealed class UsageBasedTypeMapManager : TypeMapManager + public sealed class UsageBasedTypeMapManager(TypeMapManager.TypeMapStates state) : TypeMapManager(state) { private sealed class TypeMapsNode(TypeMapStates typeMapState) : DependencyNodeCore { @@ -43,7 +43,10 @@ public override IEnumerable GetConditionalStaticDep private readonly List _usedExternalTypeMap = new List(); private readonly List _usedProxyTypeMap = new List(); - public UsageBasedTypeMapManager(TypeMapStates state) : base(state) { } + private readonly SortedSet _externalTypeMaps = new SortedSet(CompilerComparer.Instance); + private readonly SortedSet _invalidExternalTypeMaps = new SortedSet(CompilerComparer.Instance); + private readonly SortedSet _proxyTypeMaps = new SortedSet(CompilerComparer.Instance); + private readonly SortedSet _invalidProxyTypeMaps = new SortedSet(CompilerComparer.Instance); protected override void Graph_NewMarkedNode(DependencyNodeCore obj) { @@ -58,6 +61,45 @@ protected override void Graph_NewMarkedNode(DependencyNodeCore obj) { _usedProxyTypeMap.Add(proxyTypeMapRequestNode.TypeMapGroup); } + + if (obj is ExternalTypeMapNode externalTypeMapNode) + { + _externalTypeMaps.Add(externalTypeMapNode); + } + + if (obj is InvalidExternalTypeMapNode invalidExternalTypeMapNode) + { + _invalidExternalTypeMaps.Add(invalidExternalTypeMapNode); + } + + if (obj is ProxyTypeMapNode proxyTypeMapNode) + { + _proxyTypeMaps.Add(proxyTypeMapNode); + } + + if (obj is InvalidProxyTypeMapNode invalidProxyTypeMapNode) + { + _invalidProxyTypeMaps.Add(invalidProxyTypeMapNode); + } + } + internal override IEnumerable GetExternalTypeMaps() + { + return _externalTypeMaps; + } + + internal override IEnumerable GetInvalidExternalTypeMaps() + { + return _invalidExternalTypeMaps; + } + + internal override IEnumerable GetProxyTypeMaps() + { + return _proxyTypeMaps; + } + + internal override IEnumerable GetInvalidProxyTypeMaps() + { + return _invalidProxyTypeMaps; } public override void AddCompilationRoots(IRootingServiceProvider rootProvider) From e4ff1d92a9e9b04fb6c3f9c99cecae8859b23ec5 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Fri, 6 Jun 2025 15:11:23 -0700 Subject: [PATCH 24/45] Add an interface for each set of nodes to collapse the number of lists to maintain from 4 to 2 --- .../Compiler/AnalysisBasedTypeMapManager.cs | 62 +++---------------- .../DependencyAnalysis/ExternalTypeMapNode.cs | 2 +- .../ExternalTypeMapObjectNode.cs | 55 ++++++++-------- .../IExternalTypeMapNode.cs | 16 +++++ .../DependencyAnalysis/IProxyTypeMapNode.cs | 16 +++++ .../InvalidExternalTypeMapNode.cs | 4 +- .../InvalidProxyTypeMapNode.cs | 2 +- .../DependencyAnalysis/ProxyTypeMapNode.cs | 2 +- .../ProxyTypeMapObjectNode.cs | 55 ++++++++-------- .../Compiler/ILScannerBuilder.cs | 2 +- .../Compiler/TypeMapManager.cs | 43 ++----------- .../Compiler/UsageBasedTypeMapManager.cs | 44 +++++-------- .../Compiler/RyuJitCompilationBuilder.cs | 2 +- .../TestCasesRunner/TrimmingDriver.cs | 2 - src/coreclr/tools/aot/ILCompiler/Program.cs | 5 +- 15 files changed, 121 insertions(+), 191 deletions(-) create mode 100644 src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/IExternalTypeMapNode.cs create mode 100644 src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/IProxyTypeMapNode.cs diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/AnalysisBasedTypeMapManager.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/AnalysisBasedTypeMapManager.cs index 7c643a1fea654f..2a2c8a46695a8d 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/AnalysisBasedTypeMapManager.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/AnalysisBasedTypeMapManager.cs @@ -12,86 +12,44 @@ namespace ILCompiler { public sealed class AnalysisBasedTypeMapManager : TypeMapManager { - private readonly IEnumerable _externalTypeMapNodes; - private readonly IEnumerable _proxyTypeMapNodes; - private readonly IEnumerable _invalidExternalTypeMapNodes; - private readonly IEnumerable _invalidProxyTypeMapNodes; + private readonly IEnumerable _externalTypeMapNodes; + private readonly IEnumerable _proxyTypeMapNodes; internal AnalysisBasedTypeMapManager(TypeMapStates typeMaps, IEnumerable usedExternalTypeMaps, IEnumerable usedProxyTypeMaps) : base(typeMaps) { - List externalTypeMapNodes = []; - List invalidExternalTypeMapNodes = []; - List invalidProxyTypeMapNodes = []; - List proxyTypeMapNodes = []; + List externalTypeMapNodes = []; + List proxyTypeMapNodes = []; foreach (TypeDesc typeMapGroup in usedExternalTypeMaps) { - object externalTypeMapNode = _typeMaps[typeMapGroup].GetExternalTypeMapNode(typeMapGroup); - switch (externalTypeMapNode) - { - case ExternalTypeMapNode valid: - externalTypeMapNodes.Add(valid); - break; - case InvalidExternalTypeMapNode invalid: - invalidExternalTypeMapNodes.Add(invalid); - break; - default: - Debug.Fail("External type map node should be a known node type."); - break; - } + externalTypeMapNodes.Add(_typeMaps[typeMapGroup].GetExternalTypeMapNode(typeMapGroup)); } foreach (TypeDesc typeMapGroup in usedProxyTypeMaps) { - object proxyTypeMapNode = _typeMaps[typeMapGroup].GetProxyTypeMapNode(typeMapGroup); - switch (proxyTypeMapNode) - { - case ProxyTypeMapNode valid: - proxyTypeMapNodes.Add(valid); - break; - case InvalidProxyTypeMapNode invalid: - invalidProxyTypeMapNodes.Add(invalid); - break; - default: - Debug.Fail("External type map node should be a known node type."); - break; - } + proxyTypeMapNodes.Add(_typeMaps[typeMapGroup].GetProxyTypeMapNode(typeMapGroup)); } _externalTypeMapNodes = externalTypeMapNodes; _proxyTypeMapNodes = proxyTypeMapNodes; - _invalidProxyTypeMapNodes = invalidProxyTypeMapNodes; - _invalidExternalTypeMapNodes = invalidExternalTypeMapNodes; } public override void AddCompilationRoots(IRootingServiceProvider rootProvider) { const string reason = "Used Type Map Group"; - foreach (ExternalTypeMapNode externalTypeMap in _externalTypeMapNodes) + foreach (IExternalTypeMapNode externalTypeMap in _externalTypeMapNodes) { rootProvider.AddCompilationRoot(externalTypeMap, reason); } - foreach (InvalidExternalTypeMapNode invalidExternalMap in _invalidExternalTypeMapNodes) - { - rootProvider.AddCompilationRoot(invalidExternalMap, reason); - } - - foreach (ProxyTypeMapNode proxyTypeMap in _proxyTypeMapNodes) + foreach (IProxyTypeMapNode proxyTypeMap in _proxyTypeMapNodes) { rootProvider.AddCompilationRoot(proxyTypeMap, reason); } - - foreach (InvalidProxyTypeMapNode invalidProxyMap in _invalidProxyTypeMapNodes) - { - rootProvider.AddCompilationRoot(invalidProxyMap, reason); - } } - internal override IEnumerable GetExternalTypeMaps() => _externalTypeMapNodes; - internal override IEnumerable GetProxyTypeMaps() => _proxyTypeMapNodes; - internal override IEnumerable GetInvalidExternalTypeMaps() => _invalidExternalTypeMapNodes; - internal override IEnumerable GetInvalidProxyTypeMaps() => _invalidProxyTypeMapNodes; + internal override IEnumerable GetExternalTypeMaps() => _externalTypeMapNodes; + internal override IEnumerable GetProxyTypeMaps() => _proxyTypeMapNodes; } } 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 1c1afa88cb7948..18f5a4af79d4e8 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, ISortableNode + internal sealed class ExternalTypeMapNode : DependencyNodeCore, ISortableNode, IExternalTypeMapNode { private readonly IEnumerable> _mapEntries; diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ExternalTypeMapObjectNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ExternalTypeMapObjectNode.cs index 3bd00cad6b34e3..b96291d2ec6e7a 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ExternalTypeMapObjectNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ExternalTypeMapObjectNode.cs @@ -39,46 +39,41 @@ public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) var writer = new NativeWriter(); var typeMapGroupHashTable = new VertexHashtable(); - Dictionary typeMapHashTables = new(); - Section hashTableSection = writer.NewSection(); hashTableSection.Place(typeMapGroupHashTable); - foreach (ExternalTypeMapNode externalTypeMap in factory.TypeMapManager.GetExternalTypeMaps()) + foreach (IExternalTypeMapNode externalTypeMap in factory.TypeMapManager.GetExternalTypeMaps()) { - if (!typeMapHashTables.TryGetValue(externalTypeMap.TypeMapGroup, out VertexHashtable typeMapHashTable)) + TypeDesc typeMapGroup = externalTypeMap.TypeMapGroup; + + if (externalTypeMap is InvalidExternalTypeMapNode invalidNode) { - TypeDesc typeMapGroup = externalTypeMap.TypeMapGroup; - typeMapHashTable = typeMapHashTables[typeMapGroup] = new VertexHashtable(); + 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(invalidNode.ThrowingMethodStub))); + Vertex tuple = writer.GetTuple(typeMapGroupVertex, typeMapStateVertex, throwingMethodStubVertex); + typeMapGroupHashTable.Append((uint)typeMapGroup.GetHashCode(), hashTableSection.Place(tuple)); } - - foreach ((string key, IEETypeNode valueNode) in externalTypeMap.GetMarkedEntries(factory)) + else { - Vertex keyVertex = writer.GetStringConstant(key); - Vertex valueVertex = writer.GetUnsignedConstant(externalReferences.GetIndex(valueNode)); - Vertex entry = writer.GetTuple(keyVertex, valueVertex); - typeMapHashTable.Append((uint)TypeHashingAlgorithms.ComputeNameHashCode(key), hashTableSection.Place(entry)); + ExternalTypeMapNode validNode = (ExternalTypeMapNode)externalTypeMap; + VertexHashtable typeMapHashTable = new(); + + foreach ((string key, IEETypeNode valueNode) in validNode.GetMarkedEntries(factory)) + { + Vertex keyVertex = writer.GetStringConstant(key); + Vertex valueVertex = writer.GetUnsignedConstant(externalReferences.GetIndex(valueNode)); + Vertex entry = writer.GetTuple(keyVertex, valueVertex); + typeMapHashTable.Append((uint)TypeHashingAlgorithms.ComputeNameHashCode(key), hashTableSection.Place(entry)); + } + + Vertex typeMapStateVertex = writer.GetUnsignedConstant(1); // Valid type map state + Vertex typeMapGroupVertex = writer.GetUnsignedConstant(externalReferences.GetIndex(factory.NecessaryTypeSymbol(typeMapGroup))); + Vertex tuple = writer.GetTuple(typeMapGroupVertex, typeMapStateVertex, typeMapHashTable); + typeMapGroupHashTable.Append((uint)typeMapGroup.GetHashCode(), hashTableSection.Place(tuple)); } } - foreach ((TypeDesc typeMapGroup, VertexHashtable typeMapHashTable) in typeMapHashTables) - { - Vertex typeMapStateVertex = writer.GetUnsignedConstant(1); // Valid type map state - Vertex typeMapGroupVertex = writer.GetUnsignedConstant(externalReferences.GetIndex(factory.NecessaryTypeSymbol(typeMapGroup))); - Vertex tuple = writer.GetTuple(typeMapGroupVertex, typeMapStateVertex, typeMapHashTable); - typeMapGroupHashTable.Append((uint)typeMapGroup.GetHashCode(), hashTableSection.Place(tuple)); - } - - foreach (InvalidExternalTypeMapNode invalidNode in factory.TypeMapManager.GetInvalidExternalTypeMaps()) - { - TypeDesc typeMapGroup = invalidNode.TypeMapGroup; - 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(invalidNode.ThrowingMethodStub))); - Vertex tuple = writer.GetTuple(typeMapGroupVertex, typeMapStateVertex, throwingMethodStubVertex); - typeMapGroupHashTable.Append((uint)typeMapGroup.GetHashCode(), hashTableSection.Place(tuple)); - } - byte[] hashTableBytes = writer.Save(); Size = hashTableBytes.Length; diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/IExternalTypeMapNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/IExternalTypeMapNode.cs new file mode 100644 index 00000000000000..58a67cf27bf608 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/IExternalTypeMapNode.cs @@ -0,0 +1,16 @@ +// 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.TypeSystem; + +namespace ILCompiler.DependencyAnalysis +{ + public interface IExternalTypeMapNode : IDependencyNode, ISortableNode + { + TypeDesc TypeMapGroup { get; } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/IProxyTypeMapNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/IProxyTypeMapNode.cs new file mode 100644 index 00000000000000..5a487e370af720 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/IProxyTypeMapNode.cs @@ -0,0 +1,16 @@ +// 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.TypeSystem; + +namespace ILCompiler.DependencyAnalysis +{ + public interface IProxyTypeMapNode : IDependencyNode, ISortableNode + { + TypeDesc TypeMapGroup { get; } + } +} 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 6d7c5581e634da..662849480d9dd1 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/InvalidExternalTypeMapNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/InvalidExternalTypeMapNode.cs @@ -9,7 +9,7 @@ namespace ILCompiler.DependencyAnalysis { - internal sealed class InvalidExternalTypeMapNode(TypeDesc typeMapGroup, MethodDesc throwingMethodStub) : DependencyNodeCore, ISortableNode + internal sealed class InvalidExternalTypeMapNode(TypeDesc typeMapGroup, MethodDesc throwingMethodStub) : DependencyNodeCore, ISortableNode, IExternalTypeMapNode { public override bool InterestingForDynamicDependencyAnalysis => false; @@ -35,6 +35,8 @@ public override IEnumerable GetStaticDependencies(NodeFacto public int ClassCode => 36910224; + bool IExternalTypeMapNode.IsValid => false; + public int CompareToImpl(ISortableNode other, CompilerComparer comparer) { if (other is InvalidExternalTypeMapNode otherNode) 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 5e19833e16f538..2467b44fa2ddf7 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/InvalidProxyTypeMapNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/InvalidProxyTypeMapNode.cs @@ -9,7 +9,7 @@ namespace ILCompiler.DependencyAnalysis { - internal sealed class InvalidProxyTypeMapNode(TypeDesc typeMapGroup, MethodDesc throwingMethodStub) : DependencyNodeCore, ISortableNode + internal sealed class InvalidProxyTypeMapNode(TypeDesc typeMapGroup, MethodDesc throwingMethodStub) : DependencyNodeCore, IProxyTypeMapNode { public TypeDesc TypeMapGroup { get; } = typeMapGroup; public MethodDesc ThrowingMethodStub { get; } = throwingMethodStub; 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 de38fbd7479ee9..ec010afadd1a75 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ProxyTypeMapNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ProxyTypeMapNode.cs @@ -11,7 +11,7 @@ namespace ILCompiler.DependencyAnalysis { - internal sealed class ProxyTypeMapNode(TypeDesc typeMapGroup, IEnumerable> mapEntries) : DependencyNodeCore, ISortableNode + internal sealed class ProxyTypeMapNode(TypeDesc typeMapGroup, IEnumerable> mapEntries) : DependencyNodeCore, IProxyTypeMapNode { public TypeDesc TypeMapGroup { get; } = typeMapGroup; diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ProxyTypeMapObjectNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ProxyTypeMapObjectNode.cs index d0e54a72f495b7..ff31210ae71666 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ProxyTypeMapObjectNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ProxyTypeMapObjectNode.cs @@ -40,46 +40,41 @@ public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) var writer = new NativeWriter(); var typeMapGroupHashTable = new VertexHashtable(); - Dictionary typeMapHashTables = new(); - Section hashTableSection = writer.NewSection(); hashTableSection.Place(typeMapGroupHashTable); - foreach (ProxyTypeMapNode proxyTypeMap in factory.TypeMapManager.GetProxyTypeMaps()) + foreach (IProxyTypeMapNode proxyTypeMap in factory.TypeMapManager.GetProxyTypeMaps()) { - if (!typeMapHashTables.TryGetValue(proxyTypeMap.TypeMapGroup, out VertexHashtable typeMapHashTable)) + TypeDesc typeMapGroup = proxyTypeMap.TypeMapGroup; + + if (proxyTypeMap is InvalidProxyTypeMapNode invalidNode) { - TypeDesc typeMapGroup = proxyTypeMap.TypeMapGroup; - typeMapHashTable = typeMapHashTables[typeMapGroup] = new VertexHashtable(); + 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(invalidNode.ThrowingMethodStub))); + Vertex tuple = writer.GetTuple(typeMapGroupVertex, typeMapStateVertex, throwingMethodStubVertex); + typeMapGroupHashTable.Append((uint)typeMapGroup.GetHashCode(), hashTableSection.Place(tuple)); } - - foreach ((IEETypeNode keyNode, IEETypeNode valueNode) in proxyTypeMap.GetMarkedEntries(factory)) + else { - Vertex keyVertex = writer.GetUnsignedConstant(externalReferences.GetIndex(keyNode)); - Vertex valueVertex = writer.GetUnsignedConstant(externalReferences.GetIndex(valueNode)); - Vertex entry = writer.GetTuple(keyVertex, valueVertex); - typeMapHashTable.Append((uint)keyNode.Type.GetHashCode(), hashTableSection.Place(entry)); + ProxyTypeMapNode validNode = (ProxyTypeMapNode)proxyTypeMap; + VertexHashtable typeMapHashTable = new VertexHashtable(); + + foreach ((IEETypeNode keyNode, IEETypeNode valueNode) in validNode.GetMarkedEntries(factory)) + { + Vertex keyVertex = writer.GetUnsignedConstant(externalReferences.GetIndex(keyNode)); + Vertex valueVertex = writer.GetUnsignedConstant(externalReferences.GetIndex(valueNode)); + Vertex entry = writer.GetTuple(keyVertex, valueVertex); + typeMapHashTable.Append((uint)keyNode.Type.GetHashCode(), hashTableSection.Place(entry)); + } + + Vertex typeMapStateVertex = writer.GetUnsignedConstant(1); // Valid type map state + Vertex typeMapGroupVertex = writer.GetUnsignedConstant(externalReferences.GetIndex(factory.NecessaryTypeSymbol(typeMapGroup))); + Vertex tuple = writer.GetTuple(typeMapGroupVertex, typeMapStateVertex, typeMapHashTable); + typeMapGroupHashTable.Append((uint)typeMapGroup.GetHashCode(), hashTableSection.Place(tuple)); } } - foreach ((TypeDesc typeMapGroup, VertexHashtable typeMapHashTable) in typeMapHashTables) - { - Vertex typeMapStateVertex = writer.GetUnsignedConstant(1); // Valid type map state - Vertex typeMapGroupVertex = writer.GetUnsignedConstant(externalReferences.GetIndex(factory.NecessaryTypeSymbol(typeMapGroup))); - Vertex tuple = writer.GetTuple(typeMapGroupVertex, typeMapStateVertex, typeMapHashTable); - typeMapGroupHashTable.Append((uint)typeMapGroup.GetHashCode(), hashTableSection.Place(tuple)); - } - - foreach (InvalidProxyTypeMapNode invalidNode in factory.TypeMapManager.GetInvalidProxyTypeMaps()) - { - TypeDesc typeMapGroup = invalidNode.TypeMapGroup; - 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(invalidNode.ThrowingMethodStub))); - Vertex tuple = writer.GetTuple(typeMapGroupVertex, typeMapStateVertex, throwingMethodStubVertex); - typeMapGroupHashTable.Append((uint)typeMapGroup.GetHashCode(), hashTableSection.Place(tuple)); - } - byte[] hashTableBytes = writer.Save(); Size = hashTableBytes.Length; diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ILScannerBuilder.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ILScannerBuilder.cs index 732e6c761b07dd..d8462369d82f3b 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ILScannerBuilder.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ILScannerBuilder.cs @@ -85,7 +85,7 @@ public IILScanner ToILScanner() var nodeFactory = new ILScanNodeFactory(_context, _compilationGroup, _metadataManager, _interopStubManager, _nameMangler, _preinitializationManager, _typeMapManager); DependencyAnalyzerBase graph = _dependencyTrackingLevel.CreateDependencyGraph(nodeFactory); - return new ILScanner(graph, nodeFactory, _compilationRoots, _ilProvider, new NullDebugInformationProvider(), _logger, _parallelism); + return new ILScanner(graph, nodeFactory, [.._compilationRoots, _typeMapManager], _ilProvider, new NullDebugInformationProvider(), _logger, _parallelism); } } } diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/TypeMapManager.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/TypeMapManager.cs index 84079fb32782b2..0d93763172ce4e 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/TypeMapManager.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/TypeMapManager.cs @@ -65,7 +65,7 @@ public void SetAssociatedTypeMapStub(ThrowingMethodStub stub) _associatedTypeMapExceptionStub ??= stub; } - public object GetExternalTypeMapNode(TypeDesc group) + public IExternalTypeMapNode GetExternalTypeMapNode(TypeDesc group) { if (_externalTypeMapExceptionStub is not null) { @@ -74,7 +74,7 @@ public object GetExternalTypeMapNode(TypeDesc group) return new ExternalTypeMapNode(group, _externalTypeMap); } - public object GetProxyTypeMapNode(TypeDesc group) + public IProxyTypeMapNode GetProxyTypeMapNode(TypeDesc group) { if (_associatedTypeMapExceptionStub is not null) { @@ -297,46 +297,13 @@ void ProcessTypeMapAssociationAttribute(CustomAttributeValue attrValue return new TypeMapStates(typeMapStates); } - public void AttachToDependencyGraph(DependencyAnalyzerBase graph) + public virtual void AttachToDependencyGraph(DependencyAnalyzerBase graph) { - graph.NewMarkedNode += Graph_NewMarkedNode; } - private readonly SortedSet _externalTypeMaps = new SortedSet(CompilerComparer.Instance); - private readonly SortedSet _invalidExternalTypeMaps = new SortedSet(CompilerComparer.Instance); - private readonly SortedSet _proxyTypeMaps = new SortedSet(CompilerComparer.Instance); - private readonly SortedSet _invalidProxyTypeMaps = new SortedSet(CompilerComparer.Instance); + internal abstract IEnumerable GetExternalTypeMaps(); - protected virtual void Graph_NewMarkedNode(DependencyNodeCore obj) - { - if (obj is ExternalTypeMapNode externalTypeMapNode) - { - _externalTypeMaps.Add(externalTypeMapNode); - } - - if (obj is InvalidExternalTypeMapNode invalidExternalTypeMapNode) - { - _invalidExternalTypeMaps.Add(invalidExternalTypeMapNode); - } - - if (obj is ProxyTypeMapNode proxyTypeMapNode) - { - _proxyTypeMaps.Add(proxyTypeMapNode); - } - - if (obj is InvalidProxyTypeMapNode invalidProxyTypeMapNode) - { - _invalidProxyTypeMaps.Add(invalidProxyTypeMapNode); - } - } - - internal abstract IEnumerable GetExternalTypeMaps(); - - internal abstract IEnumerable GetInvalidExternalTypeMaps(); - - internal abstract IEnumerable GetProxyTypeMaps(); - - internal abstract IEnumerable GetInvalidProxyTypeMaps(); + internal abstract IEnumerable GetProxyTypeMaps(); public abstract void AddCompilationRoots(IRootingServiceProvider rootProvider); diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/UsageBasedTypeMapManager.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/UsageBasedTypeMapManager.cs index 1c3d4d0b0bf4c1..880e59320bbcdc 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/UsageBasedTypeMapManager.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/UsageBasedTypeMapManager.cs @@ -43,15 +43,18 @@ public override IEnumerable GetConditionalStaticDep private readonly List _usedExternalTypeMap = new List(); private readonly List _usedProxyTypeMap = new List(); - private readonly SortedSet _externalTypeMaps = new SortedSet(CompilerComparer.Instance); - private readonly SortedSet _invalidExternalTypeMaps = new SortedSet(CompilerComparer.Instance); - private readonly SortedSet _proxyTypeMaps = new SortedSet(CompilerComparer.Instance); - private readonly SortedSet _invalidProxyTypeMaps = new SortedSet(CompilerComparer.Instance); + private readonly SortedSet _externalTypeMaps = new SortedSet(CompilerComparer.Instance); + private readonly SortedSet _proxyTypeMaps = new SortedSet(CompilerComparer.Instance); - protected override void Graph_NewMarkedNode(DependencyNodeCore obj) + public override void AttachToDependencyGraph(DependencyAnalyzerBase graph) { - base.Graph_NewMarkedNode(obj); + base.AttachToDependencyGraph(graph); + graph.NewMarkedNode += Graph_NewMarkedNode; + } + + private void Graph_NewMarkedNode(DependencyNodeCore obj) + { if (obj is ExternalTypeMapRequestNode externalTypeMapRequest) { _usedExternalTypeMap.Add(externalTypeMapRequest.TypeMapGroup); @@ -62,46 +65,27 @@ protected override void Graph_NewMarkedNode(DependencyNodeCore obj) _usedProxyTypeMap.Add(proxyTypeMapRequestNode.TypeMapGroup); } - if (obj is ExternalTypeMapNode externalTypeMapNode) + if (obj is IExternalTypeMapNode externalTypeMapNode) { _externalTypeMaps.Add(externalTypeMapNode); } - if (obj is InvalidExternalTypeMapNode invalidExternalTypeMapNode) - { - _invalidExternalTypeMaps.Add(invalidExternalTypeMapNode); - } - - if (obj is ProxyTypeMapNode proxyTypeMapNode) + if (obj is IProxyTypeMapNode proxyTypeMapNode) { _proxyTypeMaps.Add(proxyTypeMapNode); } - - if (obj is InvalidProxyTypeMapNode invalidProxyTypeMapNode) - { - _invalidProxyTypeMaps.Add(invalidProxyTypeMapNode); - } - } - internal override IEnumerable GetExternalTypeMaps() - { - return _externalTypeMaps; } - internal override IEnumerable GetInvalidExternalTypeMaps() + internal override IEnumerable GetExternalTypeMaps() { - return _invalidExternalTypeMaps; + return _externalTypeMaps; } - internal override IEnumerable GetProxyTypeMaps() + internal override IEnumerable GetProxyTypeMaps() { return _proxyTypeMaps; } - internal override IEnumerable GetInvalidProxyTypeMaps() - { - return _invalidProxyTypeMaps; - } - public override void AddCompilationRoots(IRootingServiceProvider rootProvider) { if (_typeMaps.IsEmpty) diff --git a/src/coreclr/tools/aot/ILCompiler.RyuJit/Compiler/RyuJitCompilationBuilder.cs b/src/coreclr/tools/aot/ILCompiler.RyuJit/Compiler/RyuJitCompilationBuilder.cs index f03494489a1dde..b1291ac837dce8 100644 --- a/src/coreclr/tools/aot/ILCompiler.RyuJit/Compiler/RyuJitCompilationBuilder.cs +++ b/src/coreclr/tools/aot/ILCompiler.RyuJit/Compiler/RyuJitCompilationBuilder.cs @@ -137,7 +137,7 @@ public override ICompilation ToCompilation() DependencyAnalyzerBase graph = CreateDependencyGraph(factory, new ObjectNode.ObjectNodeComparer(CompilerComparer.Instance)); return new RyuJitCompilation(graph, factory, - _compilationRoots, + [.._compilationRoots, _typeMapManager], _ilProvider, _debugInformationProvider, _logger, diff --git a/src/coreclr/tools/aot/ILCompiler.Trimming.Tests/TestCasesRunner/TrimmingDriver.cs b/src/coreclr/tools/aot/ILCompiler.Trimming.Tests/TestCasesRunner/TrimmingDriver.cs index b6d3891e104e52..35a2638c243880 100644 --- a/src/coreclr/tools/aot/ILCompiler.Trimming.Tests/TestCasesRunner/TrimmingDriver.cs +++ b/src/coreclr/tools/aot/ILCompiler.Trimming.Tests/TestCasesRunner/TrimmingDriver.cs @@ -145,8 +145,6 @@ public ILScanResults Trim (ILCompilerOptions options, TrimmingCustomizations? cu typeMapManager = new UsageBasedTypeMapManager (TypeMapManager.CreateTypeMapStateRootedAtAssembly (entryAssembly)); } - compilationRoots.Add(typeMapManager); - CompilationBuilder builder = new RyuJitCompilationBuilder (typeSystemContext, compilationGroup) .UseILProvider (ilProvider) .UseCompilationUnitPrefix(""); diff --git a/src/coreclr/tools/aot/ILCompiler/Program.cs b/src/coreclr/tools/aot/ILCompiler/Program.cs index 6675b995e0b611..74ddbe68dffe67 100644 --- a/src/coreclr/tools/aot/ILCompiler/Program.cs +++ b/src/coreclr/tools/aot/ILCompiler/Program.cs @@ -201,7 +201,7 @@ public int Run() compilationRoots.Add(new SingleMethodRootProvider(singleMethod)); if (singleMethod.OwningType is MetadataType { Module.Assembly: EcmaAssembly assembly }) { - compilationRoots.Add(typeMapManager = new UsageBasedTypeMapManager(TypeMapManager.CreateTypeMapStateRootedAtAssembly(assembly))); + typeMapManager = new UsageBasedTypeMapManager(TypeMapManager.CreateTypeMapStateRootedAtAssembly(assembly)); } } else @@ -315,7 +315,7 @@ public int Run() if (entrypointModule is { Assembly: EcmaAssembly entryAssembly }) { - compilationRoots.Add(typeMapManager = new UsageBasedTypeMapManager(TypeMapManager.CreateTypeMapStateRootedAtAssembly(entryAssembly))); + typeMapManager = new UsageBasedTypeMapManager(TypeMapManager.CreateTypeMapStateRootedAtAssembly(entryAssembly)); } } @@ -598,7 +598,6 @@ void RunScanner() compilationRoots.Add(metadataManager); compilationRoots.Add(interopStubManager); - compilationRoots.Add(typeMapManager); builder .UseInstructionSetSupport(instructionSetSupport) From 18810423b835564dc8a9ca5397f233e0fd301866 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Fri, 6 Jun 2025 15:33:09 -0700 Subject: [PATCH 25/45] Refactor the concept of "optimized out observations we must not forget" into a separate compilation root provider --- .../Compiler/AnalysisBasedMetadataManager.cs | 11 +------ .../InvalidExternalTypeMapNode.cs | 2 -- .../ILCompiler.Compiler/Compiler/ILScanner.cs | 33 +++++++++++++++++++ .../Compiler/UsageBasedMetadataManager.cs | 8 +---- .../ILCompiler.Compiler.csproj | 2 ++ src/coreclr/tools/aot/ILCompiler/Program.cs | 3 ++ 6 files changed, 40 insertions(+), 19 deletions(-) diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/AnalysisBasedMetadataManager.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/AnalysisBasedMetadataManager.cs index d8994daa473483..0e5a953736a5ac 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/AnalysisBasedMetadataManager.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/AnalysisBasedMetadataManager.cs @@ -31,7 +31,6 @@ public sealed class AnalysisBasedMetadataManager : MetadataManager, ICompilation private readonly Dictionary _reflectableFields = new Dictionary(); private readonly HashSet _reflectableAttributes = new HashSet(); private readonly HashSet _reflectableParameters = new HashSet(); - private readonly List _possiblyOptimizedOutCastTargets; public AnalysisBasedMetadataManager(CompilerTypeSystemContext typeSystemContext) : this(typeSystemContext, new FullyBlockedMetadataBlockingPolicy(), @@ -39,7 +38,7 @@ public AnalysisBasedMetadataManager(CompilerTypeSystemContext typeSystemContext) new NoDynamicInvokeThunkGenerationPolicy(), null, Array.Empty(), Array.Empty(), Array.Empty>(), Array.Empty>(), Array.Empty>(), Array.Empty(), - Array.Empty(), Array.Empty(), + Array.Empty(), default) { } @@ -59,7 +58,6 @@ public AnalysisBasedMetadataManager( IEnumerable> reflectableFields, IEnumerable reflectableAttributes, IEnumerable reflectableParameters, - IEnumerable possiblyOptimizedOutCastTargets, MetadataManagerOptions options) : base(typeSystemContext, blockingPolicy, resourceBlockingPolicy, logFile, stackTracePolicy, invokeThunkGenerationPolicy, options, flowAnnotations) { @@ -103,8 +101,6 @@ public AnalysisBasedMetadataManager( _reflectableParameters.Add(refParameter); } - _possiblyOptimizedOutCastTargets = [.. possiblyOptimizedOutCastTargets]; - #if DEBUG HashSet moduleHash = new HashSet(_modulesWithMetadata); foreach (var refType in reflectableTypes) @@ -222,11 +218,6 @@ void ICompilationRootProvider.AddCompilationRoots(IRootingServiceProvider rootPr rootProvider.AddReflectionRoot(field, reason); } } - - foreach (var castTarget in _possiblyOptimizedOutCastTargets) - { - rootProvider.RootPossibleCastTarget(castTarget, "Possibly optimized out cast target"); - } } private struct Policy : IMetadataPolicy 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 662849480d9dd1..d87f71e2b242c4 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/InvalidExternalTypeMapNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/InvalidExternalTypeMapNode.cs @@ -35,8 +35,6 @@ public override IEnumerable GetStaticDependencies(NodeFacto public int ClassCode => 36910224; - bool IExternalTypeMapNode.IsValid => false; - public int CompareToImpl(ISortableNode other, CompilerComparer comparer) { if (other is InvalidExternalTypeMapNode otherNode) diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ILScanner.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ILScanner.cs index efad0516f5a354..9a4faae85e4f74 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ILScanner.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ILScanner.cs @@ -275,6 +275,11 @@ public ReadOnlyFieldPolicy GetReadOnlyFieldPolicy() return new ScannedReadOnlyPolicy(MarkedNodes); } + public ICompilationRootProvider GetOptimizedAwayObservationsProvider(DevirtualizationManager devirtualizationManager) + { + return new OptimizedAwayObservationsRootProvider(MarkedNodes, devirtualizationManager); + } + private sealed class ScannedVTableProvider : VTableSliceProvider { private readonly Dictionary _vtableSlices = new Dictionary(); @@ -983,5 +988,33 @@ public override bool IsReadOnly(FieldDesc field) return !_writtenFields.Contains(field); } } + + private sealed class OptimizedAwayObservationsRootProvider : ICompilationRootProvider + { + private HashSet _optimizedAwayCastTargets = new(); + public OptimizedAwayObservationsRootProvider(ImmutableArray> markedNodes, DevirtualizationManager devirtualizationManager) + { + foreach (var node in markedNodes) + { + if (node is ScannedCastTargetNode { Type: TypeDesc castTarget } + && !devirtualizationManager.CanReferenceConstructedTypeOrCanonicalFormOfType(castTarget)) + { + // Scanning observed a cast to castTarget, but castTarget can't be constructed. + // We may completely remove the cast. If this is the only reference to castTarget, + // this means that the codegen compilation would never observe castTarget. + // Record it here so we can root the fact that the cast was observed. + _optimizedAwayCastTargets.Add(castTarget); + } + } + } + + public void AddCompilationRoots(IRootingServiceProvider rootProvider) + { + foreach (var target in _optimizedAwayCastTargets) + { + rootProvider.RootPossibleCastTarget(target, "Observed cast target"); + } + } + } } } diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/UsageBasedMetadataManager.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/UsageBasedMetadataManager.cs index d55fe1028e83f8..dee058aff12411 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/UsageBasedMetadataManager.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/UsageBasedMetadataManager.cs @@ -56,7 +56,6 @@ private static (string AttributeName, DiagnosticId Id)[] _requiresAttributeMisma private readonly List _fieldsWithRuntimeMapping = new List(); private readonly List _customAttributesWithMetadata = new List(); private readonly List _parametersWithMetadata = new List(); - private readonly List _possiblyOptimizedOutCastTargets = new List(); internal IReadOnlyDictionary FeatureSwitches { get; } @@ -170,11 +169,6 @@ protected override void Graph_NewMarkedNode(DependencyNodeCore obj) { _typesWithForcedEEType.Add(reflectableType.Type); } - - if (obj is ScannedCastTargetNode castTarget) - { - _possiblyOptimizedOutCastTargets.Add(castTarget.Type); - } } protected override MetadataCategory GetMetadataCategory(FieldDesc field) @@ -950,7 +944,7 @@ public MetadataManager ToAnalysisBasedMetadataManager() return new AnalysisBasedMetadataManager( _typeSystemContext, _blockingPolicy, _resourceBlockingPolicy, _metadataLogFile, _stackTraceEmissionPolicy, _dynamicInvokeThunkGenerationPolicy, FlowAnnotations, _modulesWithMetadata, _typesWithForcedEEType, reflectableTypes.ToEnumerable(), reflectableMethods.ToEnumerable(), - reflectableFields.ToEnumerable(), _customAttributesWithMetadata, _parametersWithMetadata, _possiblyOptimizedOutCastTargets, _options); + reflectableFields.ToEnumerable(), _customAttributesWithMetadata, _parametersWithMetadata, _options); } private void AddDataflowDependency(ref DependencyList dependencies, NodeFactory factory, MethodIL methodIL, string reason) diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/ILCompiler.Compiler.csproj b/src/coreclr/tools/aot/ILCompiler.Compiler/ILCompiler.Compiler.csproj index c90dd7577189ee..07551d0cf089cc 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/ILCompiler.Compiler.csproj +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/ILCompiler.Compiler.csproj @@ -408,6 +408,8 @@ + + diff --git a/src/coreclr/tools/aot/ILCompiler/Program.cs b/src/coreclr/tools/aot/ILCompiler/Program.cs index 74ddbe68dffe67..69540bfe6c5f16 100644 --- a/src/coreclr/tools/aot/ILCompiler/Program.cs +++ b/src/coreclr/tools/aot/ILCompiler/Program.cs @@ -558,6 +558,9 @@ void RunScanner() // have answers for because we didn't scan the entire method. builder.UseMethodImportationErrorProvider(scanResults.GetMethodImportationErrorProvider()); + // Root any observations that we may optimize out from whole program analysis + compilationRoots.Add(scanResults.GetOptimizedAwayObservationsProvider(devirtualizationManager)); + // If we're doing preinitialization, use a new preinitialization manager that // has the whole program view. if (preinitStatics) From 3a9dbc91171966d9f8b7a6ad79b968f98e51b355 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Fri, 6 Jun 2025 15:36:11 -0700 Subject: [PATCH 26/45] Move attribute check outside of UsageBasedMetadataManager --- .../CustomAttributeBasedDependencyAlgorithm.cs | 3 +++ .../Compiler/UsageBasedMetadataManager.cs | 6 ++---- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/CustomAttributeBasedDependencyAlgorithm.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/CustomAttributeBasedDependencyAlgorithm.cs index a18251e401fc03..936f900bb0002a 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/CustomAttributeBasedDependencyAlgorithm.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/CustomAttributeBasedDependencyAlgorithm.cs @@ -118,6 +118,9 @@ private static void AddDependenciesDueToCustomAttributes(ref DependencyList depe { MethodDesc constructor = module.GetMethod(attribute.Constructor); + if (TypeMapManager.LookupTypeMapType(constructor.OwningType) != TypeMapManager.TypeMapAttributeKind.None) + continue; + if (!mdManager.GeneratesAttributeMetadata(constructor.OwningType)) continue; diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/UsageBasedMetadataManager.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/UsageBasedMetadataManager.cs index dee058aff12411..799792ea28abf0 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/UsageBasedMetadataManager.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/UsageBasedMetadataManager.cs @@ -39,6 +39,7 @@ public sealed class UsageBasedMetadataManager : MetadataManager private readonly CompilationModuleGroup _compilationModuleGroup; internal readonly UsageBasedMetadataGenerationOptions _generationOptions; + private readonly LinkAttributesHashTable _linkAttributesHashTable; private static (string AttributeName, DiagnosticId Id)[] _requiresAttributeMismatchNameAndId = new[] @@ -88,6 +89,7 @@ public UsageBasedMetadataManager( { _compilationModuleGroup = group; _generationOptions = generationOptions; + Logger = logger; _linkAttributesHashTable = new LinkAttributesHashTable(Logger, featureSwitchValues); @@ -816,10 +818,6 @@ public override DependencyList GetDependenciesForCustomAttribute(NodeFactory fac public bool GeneratesAttributeMetadata(TypeDesc attributeType) { - if (TypeMapManager.LookupTypeMapType(attributeType) != TypeMapManager.TypeMapAttributeKind.None) - { - return false; - } var ecmaType = attributeType.GetTypeDefinition() as EcmaType; if (ecmaType != null) { From 098f57f5f3ac82cff8f5146a062fca9818752e5e Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Fri, 6 Jun 2025 15:42:15 -0700 Subject: [PATCH 27/45] Put TypeMapStates into its own file as a top-level type --- .../Compiler/CompilationBuilder.Aot.cs | 2 +- .../Compiler/ILScannerBuilder.cs | 2 +- .../Compiler/TypeMapManager.cs | 167 +---------------- .../Compiler/TypeMapStates.cs | 175 ++++++++++++++++++ .../Compiler/UsageBasedTypeMapManager.cs | 2 +- .../ILCompiler.Compiler.csproj | 1 + .../TestCasesRunner/TrimmingDriver.cs | 4 +- src/coreclr/tools/aot/ILCompiler/Program.cs | 6 +- 8 files changed, 186 insertions(+), 173 deletions(-) create mode 100644 src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/TypeMapStates.cs diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/CompilationBuilder.Aot.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/CompilationBuilder.Aot.cs index abaab9cc12433f..7ab591ee348b5d 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/CompilationBuilder.Aot.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/CompilationBuilder.Aot.cs @@ -26,7 +26,7 @@ public partial class CompilationBuilder protected SecurityMitigationOptions _mitigationOptions; protected bool _dehydrate; protected bool _useDwarf5; - protected TypeMapManager _typeMapManager = new UsageBasedTypeMapManager(TypeMapManager.TypeMapStates.Empty); + protected TypeMapManager _typeMapManager = new UsageBasedTypeMapManager(TypeMapStates.Empty); partial void InitializePartial() { diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ILScannerBuilder.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ILScannerBuilder.cs index d8462369d82f3b..3df2518f11f80f 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ILScannerBuilder.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ILScannerBuilder.cs @@ -25,7 +25,7 @@ public sealed class ILScannerBuilder private IEnumerable _compilationRoots = Array.Empty(); private MetadataManager _metadataManager; private InteropStubManager _interopStubManager = new EmptyInteropStubManager(); - private TypeMapManager _typeMapManager = new UsageBasedTypeMapManager(TypeMapManager.TypeMapStates.Empty); + private TypeMapManager _typeMapManager = new UsageBasedTypeMapManager(TypeMapStates.Empty); private int _parallelism = -1; internal ILScannerBuilder(CompilerTypeSystemContext context, CompilationModuleGroup compilationGroup, NameMangler mangler, ILProvider ilProvider, PreinitializationManager preinitializationManager) diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/TypeMapManager.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/TypeMapManager.cs index 0d93763172ce4e..9e33bffe718422 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/TypeMapManager.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/TypeMapManager.cs @@ -12,12 +12,11 @@ using Internal.IL; using Internal.IL.Stubs; using Internal.TypeSystem; -using Internal.TypeSystem.Ecma; -using DependencyList = ILCompiler.DependencyAnalysisFramework.DependencyNodeCore.DependencyList; using ReflectionMapBlob = Internal.Runtime.ReflectionMapBlob; namespace ILCompiler { + /// /// This class is responsible for managing emitted data for type maps. /// @@ -84,24 +83,6 @@ public IProxyTypeMapNode GetProxyTypeMapNode(TypeDesc group) } } - public sealed class TypeMapStates - { - public static readonly TypeMapStates Empty = new TypeMapStates(new Dictionary()); - - private readonly IReadOnlyDictionary _states; - - internal TypeMapStates(IReadOnlyDictionary states) - { - _states = states; - } - - internal TypeMapState this[TypeDesc typeMapGroup] => _states[typeMapGroup]; - - public bool IsEmpty => _states.Count == 0; - - internal IEnumerable> States => _states; - } - protected internal sealed class ThrowingMethodStub(TypeDesc owningType, TypeDesc typeMapGroup, bool externalTypeMap, TypeSystemException ex) : ILStubMethod { public TypeSystemException Exception => ex; @@ -140,6 +121,7 @@ public enum TypeMapAttributeKind TypeMap, TypeMapAssociation } + public static TypeMapAttributeKind LookupTypeMapType(TypeDesc attrType) { TypeDesc typeDef = attrType.GetTypeDefinition(); @@ -152,151 +134,6 @@ public static TypeMapAttributeKind LookupTypeMapType(TypeDesc attrType) }; } - public static TypeMapStates CreateTypeMapStateRootedAtAssembly(EcmaAssembly assembly) - { - Dictionary typeMapStates = []; - HashSet scannedAssemblies = []; - - Queue assembliesToScan = new Queue(); - assembliesToScan.Enqueue(assembly); - - while (assembliesToScan.Count > 0) - { - EcmaAssembly currentAssembly = assembliesToScan.Dequeue(); - if (scannedAssemblies.Contains(currentAssembly)) - continue; - - scannedAssemblies.Add(currentAssembly); - - foreach (CustomAttributeHandle attrHandle in currentAssembly.MetadataReader.GetCustomAttributes(EntityHandle.AssemblyDefinition)) - { - CustomAttribute attr = currentAssembly.MetadataReader.GetCustomAttribute(attrHandle); - - if (!MetadataExtensions.GetAttributeTypeAndConstructor(currentAssembly.MetadataReader, attrHandle, out EntityHandle attributeType, out _)) - { - continue; - } - - TypeDesc type = (TypeDesc)currentAssembly.GetObject(attributeType); - - TypeMapAttributeKind attrKind = LookupTypeMapType(type); - - if (attrKind == TypeMapAttributeKind.None) - { - // Not a type map attribute, skip it - continue; - } - - CustomAttributeValue attrValue = attr.DecodeValue(new CustomAttributeTypeProvider(currentAssembly)); - - TypeDesc typeMapGroup = type.Instantiation[0]; - - try - { - switch (attrKind) - { - case TypeMapAttributeKind.TypeMapAssemblyTarget: - ProcessTypeMapAssemblyTargetAttribute(attrValue); - break; - - case TypeMapAttributeKind.TypeMap: - ProcessTypeMapAttribute(attrValue, typeMapGroup); - break; - - case TypeMapAttributeKind.TypeMapAssociation: - ProcessTypeMapAssociationAttribute(attrValue, typeMapGroup); - break; - - default: - Debug.Fail($"Unexpected TypeMapAttributeKind: {attrKind}"); - break; - } - } - catch (TypeSystemException ex) - { - if (!typeMapStates.TryGetValue(typeMapGroup, out TypeMapState value)) - { - value = new TypeMapState(); - typeMapStates[typeMapGroup] = value; - } - - if (attrKind is TypeMapAttributeKind.TypeMapAssemblyTarget or TypeMapAttributeKind.TypeMap) - { - value.SetExternalTypeMapStub(new ThrowingMethodStub(assembly.GetGlobalModuleType(), typeMapGroup, externalTypeMap: true, ex)); - } - - if (attrKind is TypeMapAttributeKind.TypeMapAssemblyTarget or TypeMapAttributeKind.TypeMapAssociation) - { - value.SetAssociatedTypeMapStub(new ThrowingMethodStub(assembly.GetGlobalModuleType(), typeMapGroup, externalTypeMap: false, ex)); - } - } - } - - void ProcessTypeMapAssemblyTargetAttribute(CustomAttributeValue attrValue) - { - if (attrValue.FixedArguments is not [{ Value: string assemblyName }]) - { - ThrowHelper.ThrowBadImageFormatException(); - return; - } - - EcmaAssembly targetAssembly = (EcmaAssembly)assembly.Context.ResolveAssembly(AssemblyNameInfo.Parse(assemblyName), throwIfNotFound: true); - - assembliesToScan.Enqueue(targetAssembly); - } - - void ProcessTypeMapAttribute(CustomAttributeValue attrValue, TypeDesc typeMapGroup) - { - switch (attrValue.FixedArguments) - { - case [{ Value: string typeName }, { Value: TypeDesc targetType }]: - { - if (!typeMapStates.TryGetValue(typeMapGroup, out TypeMapState typeMapState)) - { - typeMapStates[typeMapGroup] = typeMapState = new TypeMapState(); - } - typeMapState.AddExternalTypeMapEntry(typeName, targetType, targetType); - break; - } - - case [{ Value: string typeName }, { Value: TypeDesc targetType }, { Value: TypeDesc trimTargetType }]: - { - if (!typeMapStates.TryGetValue(typeMapGroup, out TypeMapState typeMapState)) - { - typeMapStates[typeMapGroup] = typeMapState = new TypeMapState(); - } - typeMapState.AddExternalTypeMapEntry(typeName, targetType, trimTargetType); - break; - } - - default: - ThrowHelper.ThrowBadImageFormatException(); - return; - } - } - - void ProcessTypeMapAssociationAttribute(CustomAttributeValue attrValue, TypeDesc typeMapGroup) - { - // If attribute is TypeMapAssociationAttribute, we need to extract the generic argument (type map group) - // and process it. - if (attrValue.FixedArguments is not [{ Value: TypeDesc type }, { Value: TypeDesc associatedType }]) - { - ThrowHelper.ThrowBadImageFormatException(); - return; - } - - if (!typeMapStates.TryGetValue(typeMapGroup, out TypeMapState typeMapState)) - { - typeMapStates[typeMapGroup] = typeMapState = new TypeMapState(); - } - - typeMapState.AddAssociatedTypeMapEntry(type, associatedType); - } - } - - return new TypeMapStates(typeMapStates); - } - public virtual void AttachToDependencyGraph(DependencyAnalyzerBase graph) { } diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/TypeMapStates.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/TypeMapStates.cs new file mode 100644 index 00000000000000..6c915736538ef2 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/TypeMapStates.cs @@ -0,0 +1,175 @@ +// 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 System.Diagnostics; +using System.Reflection.Metadata; +using Internal.TypeSystem; +using Internal.TypeSystem.Ecma; +using static ILCompiler.TypeMapManager; + +namespace ILCompiler +{ + public sealed class TypeMapStates + { + public static readonly TypeMapStates Empty = new TypeMapStates(new Dictionary()); + + private readonly IReadOnlyDictionary _states; + + internal TypeMapStates(IReadOnlyDictionary states) + { + _states = states; + } + + internal TypeMapState this[TypeDesc typeMapGroup] => _states[typeMapGroup]; + + public bool IsEmpty => _states.Count == 0; + + internal IEnumerable> States => _states; + + public static TypeMapStates CreateFromAssembly(EcmaAssembly assembly) + { + Dictionary typeMapStates = []; + HashSet scannedAssemblies = []; + + Queue assembliesToScan = new Queue(); + assembliesToScan.Enqueue(assembly); + + while (assembliesToScan.Count > 0) + { + EcmaAssembly currentAssembly = assembliesToScan.Dequeue(); + if (scannedAssemblies.Contains(currentAssembly)) + continue; + + scannedAssemblies.Add(currentAssembly); + + foreach (CustomAttributeHandle attrHandle in currentAssembly.MetadataReader.GetCustomAttributes(EntityHandle.AssemblyDefinition)) + { + CustomAttribute attr = currentAssembly.MetadataReader.GetCustomAttribute(attrHandle); + + if (!MetadataExtensions.GetAttributeTypeAndConstructor(currentAssembly.MetadataReader, attrHandle, out EntityHandle attributeType, out _)) + { + continue; + } + + TypeDesc type = (TypeDesc)currentAssembly.GetObject(attributeType); + + TypeMapAttributeKind attrKind = LookupTypeMapType(type); + + if (attrKind == TypeMapAttributeKind.None) + { + // Not a type map attribute, skip it + continue; + } + + CustomAttributeValue attrValue = attr.DecodeValue(new CustomAttributeTypeProvider(currentAssembly)); + + TypeDesc typeMapGroup = type.Instantiation[0]; + + try + { + switch (attrKind) + { + case TypeMapAttributeKind.TypeMapAssemblyTarget: + ProcessTypeMapAssemblyTargetAttribute(attrValue); + break; + + case TypeMapAttributeKind.TypeMap: + ProcessTypeMapAttribute(attrValue, typeMapGroup); + break; + + case TypeMapAttributeKind.TypeMapAssociation: + ProcessTypeMapAssociationAttribute(attrValue, typeMapGroup); + break; + + default: + Debug.Fail($"Unexpected TypeMapAttributeKind: {attrKind}"); + break; + } + } + catch (TypeSystemException ex) + { + if (!typeMapStates.TryGetValue(typeMapGroup, out TypeMapState value)) + { + value = new TypeMapState(); + typeMapStates[typeMapGroup] = value; + } + + if (attrKind is TypeMapAttributeKind.TypeMapAssemblyTarget or TypeMapAttributeKind.TypeMap) + { + value.SetExternalTypeMapStub(new ThrowingMethodStub(assembly.GetGlobalModuleType(), typeMapGroup, externalTypeMap: true, ex)); + } + + if (attrKind is TypeMapAttributeKind.TypeMapAssemblyTarget or TypeMapAttributeKind.TypeMapAssociation) + { + value.SetAssociatedTypeMapStub(new ThrowingMethodStub(assembly.GetGlobalModuleType(), typeMapGroup, externalTypeMap: false, ex)); + } + } + } + + void ProcessTypeMapAssemblyTargetAttribute(CustomAttributeValue attrValue) + { + if (attrValue.FixedArguments is not [{ Value: string assemblyName }]) + { + ThrowHelper.ThrowBadImageFormatException(); + return; + } + + EcmaAssembly targetAssembly = (EcmaAssembly)assembly.Context.ResolveAssembly(AssemblyNameInfo.Parse(assemblyName), throwIfNotFound: true); + + assembliesToScan.Enqueue(targetAssembly); + } + + void ProcessTypeMapAttribute(CustomAttributeValue attrValue, TypeDesc typeMapGroup) + { + switch (attrValue.FixedArguments) + { + case [{ Value: string typeName }, { Value: TypeDesc targetType }]: + { + if (!typeMapStates.TryGetValue(typeMapGroup, out TypeMapState typeMapState)) + { + typeMapStates[typeMapGroup] = typeMapState = new TypeMapState(); + } + typeMapState.AddExternalTypeMapEntry(typeName, targetType, targetType); + break; + } + + case [{ Value: string typeName }, { Value: TypeDesc targetType }, { Value: TypeDesc trimTargetType }]: + { + if (!typeMapStates.TryGetValue(typeMapGroup, out TypeMapState typeMapState)) + { + typeMapStates[typeMapGroup] = typeMapState = new TypeMapState(); + } + typeMapState.AddExternalTypeMapEntry(typeName, targetType, trimTargetType); + break; + } + + default: + ThrowHelper.ThrowBadImageFormatException(); + return; + } + } + + void ProcessTypeMapAssociationAttribute(CustomAttributeValue attrValue, TypeDesc typeMapGroup) + { + // If attribute is TypeMapAssociationAttribute, we need to extract the generic argument (type map group) + // and process it. + if (attrValue.FixedArguments is not [{ Value: TypeDesc type }, { Value: TypeDesc associatedType }]) + { + ThrowHelper.ThrowBadImageFormatException(); + return; + } + + if (!typeMapStates.TryGetValue(typeMapGroup, out TypeMapState typeMapState)) + { + typeMapStates[typeMapGroup] = typeMapState = new TypeMapState(); + } + + typeMapState.AddAssociatedTypeMapEntry(type, associatedType); + } + } + + return new TypeMapStates(typeMapStates); + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/UsageBasedTypeMapManager.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/UsageBasedTypeMapManager.cs index 880e59320bbcdc..8ba8e6ce633ff7 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/UsageBasedTypeMapManager.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/UsageBasedTypeMapManager.cs @@ -11,7 +11,7 @@ namespace ILCompiler { - public sealed class UsageBasedTypeMapManager(TypeMapManager.TypeMapStates state) : TypeMapManager(state) + public sealed class UsageBasedTypeMapManager(TypeMapStates state) : TypeMapManager(state) { private sealed class TypeMapsNode(TypeMapStates typeMapState) : DependencyNodeCore { diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/ILCompiler.Compiler.csproj b/src/coreclr/tools/aot/ILCompiler.Compiler/ILCompiler.Compiler.csproj index 07551d0cf089cc..64aa0aa6425157 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/ILCompiler.Compiler.csproj +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/ILCompiler.Compiler.csproj @@ -635,6 +635,7 @@ + diff --git a/src/coreclr/tools/aot/ILCompiler.Trimming.Tests/TestCasesRunner/TrimmingDriver.cs b/src/coreclr/tools/aot/ILCompiler.Trimming.Tests/TestCasesRunner/TrimmingDriver.cs index 35a2638c243880..496b79d9d4ea4f 100644 --- a/src/coreclr/tools/aot/ILCompiler.Trimming.Tests/TestCasesRunner/TrimmingDriver.cs +++ b/src/coreclr/tools/aot/ILCompiler.Trimming.Tests/TestCasesRunner/TrimmingDriver.cs @@ -139,10 +139,10 @@ public ILScanResults Trim (ILCompilerOptions options, TrimmingCustomizations? cu InteropStateManager interopStateManager = new InteropStateManager (typeSystemContext.GeneratedAssembly); InteropStubManager interopStubManager = new UsageBasedInteropStubManager (interopStateManager, pinvokePolicy, logger); - TypeMapManager typeMapManager = new UsageBasedTypeMapManager (TypeMapManager.TypeMapStates.Empty); + TypeMapManager typeMapManager = new UsageBasedTypeMapManager (TypeMapStates.Empty); if (entrypointModule is { Assembly: EcmaAssembly entryAssembly }) { - typeMapManager = new UsageBasedTypeMapManager (TypeMapManager.CreateTypeMapStateRootedAtAssembly (entryAssembly)); + typeMapManager = new UsageBasedTypeMapManager (TypeMapStates.CreateFromAssembly (entryAssembly)); } CompilationBuilder builder = new RyuJitCompilationBuilder (typeSystemContext, compilationGroup) diff --git a/src/coreclr/tools/aot/ILCompiler/Program.cs b/src/coreclr/tools/aot/ILCompiler/Program.cs index 69540bfe6c5f16..15ed927ec8a830 100644 --- a/src/coreclr/tools/aot/ILCompiler/Program.cs +++ b/src/coreclr/tools/aot/ILCompiler/Program.cs @@ -192,7 +192,7 @@ public int Run() CompilationModuleGroup compilationGroup; List compilationRoots = new List(); - TypeMapManager typeMapManager = new UsageBasedTypeMapManager(TypeMapManager.TypeMapStates.Empty); + TypeMapManager typeMapManager = new UsageBasedTypeMapManager(TypeMapStates.Empty); bool multiFile = Get(_command.MultiFile); if (singleMethod != null) { @@ -201,7 +201,7 @@ public int Run() compilationRoots.Add(new SingleMethodRootProvider(singleMethod)); if (singleMethod.OwningType is MetadataType { Module.Assembly: EcmaAssembly assembly }) { - typeMapManager = new UsageBasedTypeMapManager(TypeMapManager.CreateTypeMapStateRootedAtAssembly(assembly)); + typeMapManager = new UsageBasedTypeMapManager(TypeMapStates.CreateFromAssembly(assembly)); } } else @@ -315,7 +315,7 @@ public int Run() if (entrypointModule is { Assembly: EcmaAssembly entryAssembly }) { - typeMapManager = new UsageBasedTypeMapManager(TypeMapManager.CreateTypeMapStateRootedAtAssembly(entryAssembly)); + typeMapManager = new UsageBasedTypeMapManager(TypeMapStates.CreateFromAssembly(entryAssembly)); } } From b3026cb008927a89c6c341a127186590c423cdf2 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Fri, 6 Jun 2025 15:49:47 -0700 Subject: [PATCH 28/45] Fix comparison to match pattern --- .../InvalidExternalTypeMapNode.cs | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) 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 d87f71e2b242c4..86b5400e8007d8 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/InvalidExternalTypeMapNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/InvalidExternalTypeMapNode.cs @@ -37,17 +37,12 @@ public override IEnumerable GetStaticDependencies(NodeFacto public int CompareToImpl(ISortableNode other, CompilerComparer comparer) { - if (other is InvalidExternalTypeMapNode otherNode) - { - int result = comparer.Compare(TypeMapGroup, otherNode.TypeMapGroup); - if (result != 0) - return result; - return comparer.Compare(ThrowingMethodStub, otherNode.ThrowingMethodStub); - } - else - { - return -1; // This node is always less than any other node - } + var otherNode = (InvalidExternalTypeMapNode)other; + + int result = comparer.Compare(TypeMapGroup, otherNode.TypeMapGroup); + if (result != 0) + return result; + return comparer.Compare(ThrowingMethodStub, otherNode.ThrowingMethodStub); } } } From de0ed0b579790bf3b5fd23a1daf29f4c447d94ca Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Fri, 6 Jun 2025 17:30:59 -0700 Subject: [PATCH 29/45] Fix cast typo --- .../Compiler/DependencyAnalysis/InvalidProxyTypeMapNode.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 2467b44fa2ddf7..c0f261139aaa23 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/InvalidProxyTypeMapNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/InvalidProxyTypeMapNode.cs @@ -36,7 +36,7 @@ public override IEnumerable GetStaticDependencies(NodeFacto public int ClassCode => 36910224; public int CompareToImpl(ISortableNode other, CompilerComparer comparer) { - var otherNode = (InvalidExternalTypeMapNode)other; + var otherNode = (InvalidProxyTypeMapNode)other; int result = comparer.Compare(TypeMapGroup, otherNode.TypeMapGroup); if (result != 0) return result; From 2f40f9cd7b5bad858650130adaec98666f7b096e Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Fri, 6 Jun 2025 17:32:21 -0700 Subject: [PATCH 30/45] Make diagnostic names for invalid type map nodes unique --- .../Compiler/DependencyAnalysis/InvalidExternalTypeMapNode.cs | 2 +- .../Compiler/DependencyAnalysis/InvalidProxyTypeMapNode.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) 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 86b5400e8007d8..b10bfd51465c73 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/InvalidExternalTypeMapNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/InvalidExternalTypeMapNode.cs @@ -28,7 +28,7 @@ public override IEnumerable GetStaticDependencies(NodeFacto } public override IEnumerable SearchDynamicDependencies(List> markedNodes, int firstNode, NodeFactory context) => Array.Empty(); - protected override string GetName(NodeFactory context) => "InvalidExternalTypeMapNode"; + protected override string GetName(NodeFactory context) => $"Invalid external type map: {TypeMapGroup}"; public TypeDesc TypeMapGroup { get; } = typeMapGroup; public MethodDesc ThrowingMethodStub { get; } = throwingMethodStub; 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 c0f261139aaa23..9a4aa85d8a245a 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/InvalidProxyTypeMapNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/InvalidProxyTypeMapNode.cs @@ -31,7 +31,7 @@ public override IEnumerable GetStaticDependencies(NodeFacto } public override IEnumerable SearchDynamicDependencies(List> markedNodes, int firstNode, NodeFactory context) => Array.Empty(); - protected override string GetName(NodeFactory context) => "InvalidAssociatedTypeMapNode"; + protected override string GetName(NodeFactory context) => $"Invalid proxy type map: {TypeMapGroup}"; public int ClassCode => 36910224; public int CompareToImpl(ISortableNode other, CompilerComparer comparer) From 0445c4c0f003768c1865888d4854ab97232d2c91 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Mon, 9 Jun 2025 11:23:57 -0700 Subject: [PATCH 31/45] Just use reference equality and the default hash code for the hashtable. We don't care about unifying different hashtables together based on value equality. --- .../NativeFormat/NativeFormatWriter.cs | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/src/coreclr/tools/Common/Internal/NativeFormat/NativeFormatWriter.cs b/src/coreclr/tools/Common/Internal/NativeFormat/NativeFormatWriter.cs index 86b7e3dfe3c10d..e733aa41c03d6b 100644 --- a/src/coreclr/tools/Common/Internal/NativeFormat/NativeFormatWriter.cs +++ b/src/coreclr/tools/Common/Internal/NativeFormat/NativeFormatWriter.cs @@ -2049,23 +2049,5 @@ internal override void Save(NativeWriter writer) } } } - - public override bool Equals(object obj) - { - // TODO: Support value equality instead of just referential equality - return ReferenceEquals(this, obj); - } - - public override int GetHashCode() - { - int hashCode = 13; - foreach (var entry in _Entries) - { - int value = (int)entry.Hashcode * 0x5498341 + 0x832424; - hashCode = hashCode * 31 + value; - } - - return hashCode; - } } } From 3eae3d1915ee2e5f363ddb0d718bfa3358e0e2f4 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Mon, 9 Jun 2025 12:02:29 -0700 Subject: [PATCH 32/45] Add some more test cases around interfaces and use nameof instead of raw strings --- .../tests/TrimmingTests/TypeMap.cs | 12 +++---- .../Reflection/TypeMap.cs | 32 +++++++++++++++++++ 2 files changed, 38 insertions(+), 6 deletions(-) diff --git a/src/libraries/System.Runtime.InteropServices/tests/TrimmingTests/TypeMap.cs b/src/libraries/System.Runtime.InteropServices/tests/TrimmingTests/TypeMap.cs index a688e8a88a5832..5dcf5e35009ecd 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/TrimmingTests/TypeMap.cs +++ b/src/libraries/System.Runtime.InteropServices/tests/TrimmingTests/TypeMap.cs @@ -40,7 +40,7 @@ return 1; } -if (targetAndTrimTargetType != GetTypeWithoutTrimAnalysis("TargetAndTrimTarget")) +if (targetAndTrimTargetType != GetTypeWithoutTrimAnalysis(nameof(TargetAndTrimTarget))) { Console.WriteLine("TrimTargetIsTarget type does not match expected type."); return 2; @@ -52,13 +52,13 @@ return 3; } -if (targetType != GetTypeWithoutTrimAnalysis("TargetType")) +if (targetType != GetTypeWithoutTrimAnalysis(nameof(TargetType))) { Console.WriteLine("TrimTargetIsUnrelated type does not match expected type."); return 4; } -if (GetTypeWithoutTrimAnalysis("TrimTarget") is not null) +if (GetTypeWithoutTrimAnalysis(nameof(TrimTarget)) is not null) { Console.WriteLine("TrimTarget should not be preserved if the only place that would preserve it is a check that is optimized away."); return 5; @@ -77,19 +77,19 @@ return 7; } -if (proxyType != GetTypeWithoutTrimAnalysis("ProxyType")) +if (proxyType != GetTypeWithoutTrimAnalysis(nameof(ProxyType))) { Console.WriteLine("SourceClass proxy type does not match expected type."); return 8; } -if (GetTypeWithoutTrimAnalysis("UnusedTargetType") is not null) +if (GetTypeWithoutTrimAnalysis(nameof(UnusedTargetType)) is not null) { Console.WriteLine("UnusedTargetType should not be preserved if the external type map is not used and it is not referenced otherwise even if the entry's trim target is kept."); return 9; } -if (GetTypeWithoutTrimAnalysis("UnusedProxyType") is not null) +if (GetTypeWithoutTrimAnalysis(nameof(UnusedProxyType)) is not null) { Console.WriteLine("UnusedProxyType should not be preserved if the proxy type map is not used and it is not referenced otherwise even if the entry's source type is kept."); return 10; diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/Reflection/TypeMap.cs b/src/tools/illink/test/Mono.Linker.Tests.Cases/Reflection/TypeMap.cs index 742aac48d7c33e..c8490a251c2ef6 100644 --- a/src/tools/illink/test/Mono.Linker.Tests.Cases/Reflection/TypeMap.cs +++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/Reflection/TypeMap.cs @@ -13,6 +13,8 @@ [assembly: TypeMap ("TrimTargetIsUnrelated", typeof (TargetType), typeof (TrimTarget))] [assembly: TypeMap ("TrimTargetIsUnreferenced", typeof (UnreferencedTargetType), typeof (UnreferencedTrimTarget))] [assembly: TypeMapAssociation (typeof (SourceClass), typeof (ProxyType))] +[assembly: TypeMapAssociation (typeof (I), typeof (IImpl))] +[assembly: TypeMapAssociation (typeof (IInterfaceWithDynamicImpl), typeof (IDynamicImpl))] [assembly: TypeMap ("UnusedName", typeof (UnusedTargetType), typeof (TrimTarget))] [assembly: TypeMapAssociation (typeof (UnusedSourceClass), typeof (UnusedProxyType))] @@ -32,9 +34,12 @@ public static void Main (string[] args) Console.WriteLine ("Type deriving from TargetAndTrimTarget instantiated."); } else if (t is TrimTarget) { Console.WriteLine ("Type deriving from TrimTarget instantiated."); + } else if (t is IInterfaceWithDynamicImpl d) { + d.Method (); } Console.WriteLine ("Hash code of SourceClass instance: " + new SourceClass ().GetHashCode ()); + Console.WriteLine ("Hash code of UsedClass instance: " + new UsedClass ().GetHashCode ()); Console.WriteLine (TypeMapping.GetOrCreateExternalTypeMapping ()); Console.WriteLine (TypeMapping.GetOrCreateProxyTypeMapping ()); @@ -76,6 +81,33 @@ class UnusedTypeMap; class UnusedTargetType; class UnusedSourceClass; class UnusedProxyType; + + interface I; + + interface IImpl : I; + + [Kept] + [KeptMember (".ctor()")] + class UsedClass : IImpl; + + [Kept] + interface IInterfaceWithDynamicImpl + { + [Kept (By = Tool.Trimmer)] + void Method (); + } + + [Kept] + [KeptInterface (typeof (IInterfaceWithDynamicImpl))] + [KeptAttributeAttribute (typeof (DynamicInterfaceCastableImplementationAttribute), By = Tool.Trimmer)] + [DynamicInterfaceCastableImplementation] + interface IDynamicImpl : IInterfaceWithDynamicImpl + { + [Kept] + void IInterfaceWithDynamicImpl.Method () + { + } + } } // Polyfill for the type map types until we use an LKG runtime that has it. From 46cd22b6a5eec45c0b426d9b0cd2fdaece8a6483 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Mon, 9 Jun 2025 13:47:39 -0700 Subject: [PATCH 33/45] Add support in the illink analyzer test host for no-body classes and add support in the analyzer for the type map types (it's pretty small and would have been 90% of the work to gracefully not handle it). --- .../DynamicallyAccessedMembersAnalyzer.cs | 1 + .../TrimAnalysis/HandleCallAction.cs | 51 +++++++++++++++++++ .../TestChecker.cs | 7 ++- .../ReflectionTests.g.cs | 6 +++ .../Reflection/TypeMap.cs | 2 +- 5 files changed, 65 insertions(+), 2 deletions(-) diff --git a/src/tools/illink/src/ILLink.RoslynAnalyzer/DynamicallyAccessedMembersAnalyzer.cs b/src/tools/illink/src/ILLink.RoslynAnalyzer/DynamicallyAccessedMembersAnalyzer.cs index 1f31934d9b898b..9d8e2d8671c118 100644 --- a/src/tools/illink/src/ILLink.RoslynAnalyzer/DynamicallyAccessedMembersAnalyzer.cs +++ b/src/tools/illink/src/ILLink.RoslynAnalyzer/DynamicallyAccessedMembersAnalyzer.cs @@ -59,6 +59,7 @@ public static ImmutableArray GetSupportedDiagnostics () diagDescriptorsArrayBuilder.Add (DiagnosticDescriptors.GetDiagnosticDescriptor (DiagnosticId.ParametersOfAssemblyCreateInstanceCannotBeAnalyzed)); diagDescriptorsArrayBuilder.Add (DiagnosticDescriptors.GetDiagnosticDescriptor (DiagnosticId.ReturnValueDoesNotMatchFeatureGuards)); diagDescriptorsArrayBuilder.Add (DiagnosticDescriptors.GetDiagnosticDescriptor (DiagnosticId.InvalidFeatureGuard)); + diagDescriptorsArrayBuilder.Add (DiagnosticDescriptors.GetDiagnosticDescriptor (DiagnosticId.TypeMapGroupTypeCannotBeStaticallyDetermined)); foreach (var requiresAnalyzer in RequiresAnalyzers.Value) { foreach (var diagnosticDescriptor in requiresAnalyzer.SupportedDiagnostics) diff --git a/src/tools/illink/src/ILLink.RoslynAnalyzer/TrimAnalysis/HandleCallAction.cs b/src/tools/illink/src/ILLink.RoslynAnalyzer/TrimAnalysis/HandleCallAction.cs index 620e9fa582bb17..5390d309bec51d 100644 --- a/src/tools/illink/src/ILLink.RoslynAnalyzer/TrimAnalysis/HandleCallAction.cs +++ b/src/tools/illink/src/ILLink.RoslynAnalyzer/TrimAnalysis/HandleCallAction.cs @@ -44,6 +44,45 @@ public HandleCallAction ( _multiValueLattice = multiValueLattice; } + sealed class HasGenericTypeSymbolsVisitor : SymbolVisitor + { + internal static readonly HasGenericTypeSymbolsVisitor Instance = new (); + + public override bool VisitArrayType (IArrayTypeSymbol symbol) => Visit(symbol.ElementType); + + public override bool VisitPointerType (IPointerTypeSymbol symbol) => Visit(symbol.PointedAtType); + + public override bool VisitTypeParameter (ITypeParameterSymbol symbol) => true; + + public override bool VisitFunctionPointerType (IFunctionPointerTypeSymbol symbol) + { + IMethodSymbol signature = symbol.Signature; + + if (Visit (signature.ReturnType)) { + return true; + } + + foreach (var param in signature.Parameters) { + if (Visit(param.Type)) { + return true; + } + } + + return false; + } + + public override bool VisitNamedType (INamedTypeSymbol symbol) + { + foreach (var arg in symbol.TypeArguments) { + if (Visit (arg)) { + return true; + } + } + + return false; + } + } + private partial bool TryHandleIntrinsic ( MethodProxy calledMethod, MultiValue instanceValue, @@ -130,6 +169,17 @@ private partial bool TryHandleIntrinsic ( break; } + case IntrinsicId.TypeMapping_GetOrCreateProxyTypeMapping: + case IntrinsicId.TypeMapping_GetOrCreateExternalTypeMapping: { + ITypeSymbol typeMapGroup = calledMethod.Method.TypeArguments[0]; + if (HasGenericTypeSymbolsVisitor.Instance.Visit(typeMapGroup)) { + // We only support GetOrCreateExternalTypeMapping or GetOrCreateProxyTypeMapping for a fully specified type. + _diagnosticContext.AddDiagnostic (DiagnosticId.TypeMapGroupTypeCannotBeStaticallyDetermined, + typeMapGroup.GetDisplayName ()); + } + break; + } + // Some intrinsics are unimplemented by the analyzer. // These will fall back to the usual return-value handling. case IntrinsicId.Array_CreateInstance: @@ -148,6 +198,7 @@ private partial bool TryHandleIntrinsic ( case IntrinsicId.RuntimeReflectionExtensions_GetMethodInfo: break; + default: return false; } diff --git a/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/TestChecker.cs b/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/TestChecker.cs index c50d24f136051d..28d07e71406a08 100644 --- a/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/TestChecker.cs +++ b/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/TestChecker.cs @@ -168,9 +168,14 @@ private void ValidateDiagnostics (CSharpSyntaxNode memberSyntax, SyntaxList Date: Tue, 10 Jun 2025 12:08:38 -0700 Subject: [PATCH 34/45] Don't try to remember optimizations. Just root whatever we scanned during IL Scanning. Also enhance the type map node interfaces to actually act as interfaces. --- .../Compiler/AnalysisBasedTypeMapManager.cs | 55 -------- .../Compiler/CompilationBuilder.Aot.cs | 2 +- .../AnalyzedExternalTypeMapNode.cs | 62 ++++++++ .../AnalyzedProxyTypeMapNode.cs | 64 +++++++++ .../DependencyAnalysis/ExternalTypeMapNode.cs | 51 ++++--- .../ExternalTypeMapObjectNode.cs | 29 +--- .../IExternalTypeMapNode.cs | 5 + .../DependencyAnalysis/IProxyTypeMapNode.cs | 5 + .../InvalidExternalTypeMapNode.cs | 18 ++- .../InvalidProxyTypeMapNode.cs | 17 ++- .../DependencyAnalysis/NodeFactory.cs | 12 -- .../DependencyAnalysis/ProxyTypeMapNode.cs | 38 +++-- .../ProxyTypeMapObjectNode.cs | 28 +--- .../ScannedCastTargetNode.cs | 29 ---- .../ILCompiler.Compiler/Compiler/ILScanner.cs | 49 +++++-- .../Compiler/ILScannerBuilder.cs | 2 +- .../Compiler/IRootingServiceProvider.cs | 1 - .../Compiler/RootingServiceProvider.cs | 5 - .../Compiler/TypeMapManager.cs | 96 +------------ .../{TypeMapStates.cs => TypeMapMetadata.cs} | 133 +++++++++++++++--- .../Compiler/UsageBasedTypeMapManager.cs | 43 +++--- .../IL/ILImporter.Scanner.cs | 3 - .../ILCompiler.Compiler.csproj | 6 +- .../TestCasesRunner/TrimmingDriver.cs | 4 +- src/coreclr/tools/aot/ILCompiler/Program.cs | 17 +-- 25 files changed, 404 insertions(+), 370 deletions(-) delete mode 100644 src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/AnalysisBasedTypeMapManager.cs create mode 100644 src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/AnalyzedExternalTypeMapNode.cs create mode 100644 src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/AnalyzedProxyTypeMapNode.cs delete mode 100644 src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ScannedCastTargetNode.cs rename src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/{TypeMapStates.cs => TypeMapMetadata.cs} (53%) diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/AnalysisBasedTypeMapManager.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/AnalysisBasedTypeMapManager.cs deleted file mode 100644 index 2a2c8a46695a8d..00000000000000 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/AnalysisBasedTypeMapManager.cs +++ /dev/null @@ -1,55 +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.Generic; -using System.Diagnostics; -using System.Text; -using ILCompiler.DependencyAnalysis; -using Internal.TypeSystem; - -namespace ILCompiler -{ - public sealed class AnalysisBasedTypeMapManager : TypeMapManager - { - private readonly IEnumerable _externalTypeMapNodes; - private readonly IEnumerable _proxyTypeMapNodes; - - internal AnalysisBasedTypeMapManager(TypeMapStates typeMaps, IEnumerable usedExternalTypeMaps, IEnumerable usedProxyTypeMaps) : base(typeMaps) - { - List externalTypeMapNodes = []; - List proxyTypeMapNodes = []; - - foreach (TypeDesc typeMapGroup in usedExternalTypeMaps) - { - externalTypeMapNodes.Add(_typeMaps[typeMapGroup].GetExternalTypeMapNode(typeMapGroup)); - } - - foreach (TypeDesc typeMapGroup in usedProxyTypeMaps) - { - proxyTypeMapNodes.Add(_typeMaps[typeMapGroup].GetProxyTypeMapNode(typeMapGroup)); - } - - _externalTypeMapNodes = externalTypeMapNodes; - _proxyTypeMapNodes = proxyTypeMapNodes; - } - - public override void AddCompilationRoots(IRootingServiceProvider rootProvider) - { - const string reason = "Used Type Map Group"; - - foreach (IExternalTypeMapNode externalTypeMap in _externalTypeMapNodes) - { - rootProvider.AddCompilationRoot(externalTypeMap, reason); - } - - foreach (IProxyTypeMapNode proxyTypeMap in _proxyTypeMapNodes) - { - rootProvider.AddCompilationRoot(proxyTypeMap, reason); - } - } - - internal override IEnumerable GetExternalTypeMaps() => _externalTypeMapNodes; - internal override IEnumerable GetProxyTypeMaps() => _proxyTypeMapNodes; - } -} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/CompilationBuilder.Aot.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/CompilationBuilder.Aot.cs index 7ab591ee348b5d..5eb049c74e442b 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/CompilationBuilder.Aot.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/CompilationBuilder.Aot.cs @@ -26,7 +26,7 @@ public partial class CompilationBuilder protected SecurityMitigationOptions _mitigationOptions; protected bool _dehydrate; protected bool _useDwarf5; - protected TypeMapManager _typeMapManager = new UsageBasedTypeMapManager(TypeMapStates.Empty); + protected TypeMapManager _typeMapManager = new UsageBasedTypeMapManager(TypeMapMetadata.Empty); partial void InitializePartial() { diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/AnalyzedExternalTypeMapNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/AnalyzedExternalTypeMapNode.cs new file mode 100644 index 00000000000000..ea35be180b45d7 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/AnalyzedExternalTypeMapNode.cs @@ -0,0 +1,62 @@ +// 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 +{ + internal sealed class AnalyzedExternalTypeMapNode(TypeDesc typeMapGroup, IReadOnlyDictionary entries) : DependencyNodeCore, IExternalTypeMapNode + { + public override bool InterestingForDynamicDependencyAnalysis => false; + + public override bool HasDynamicDependencies => false; + + public override bool HasConditionalStaticDependencies => false; + + public override bool StaticDependenciesAreComputed => true; + + public TypeDesc TypeMapGroup => typeMapGroup; + + public int ClassCode => -874354558; + + public int CompareToImpl(ISortableNode other, CompilerComparer comparer) + { + AnalyzedExternalTypeMapNode otherEntry = (AnalyzedExternalTypeMapNode)other; + return comparer.Compare(TypeMapGroup, otherEntry.TypeMapGroup); + } + public Vertex CreateTypeMap(NodeFactory factory, NativeWriter writer, Section section, ExternalReferencesTableNode externalReferences) + { + VertexHashtable typeMapHashTable = new(); + + foreach ((string key, TypeDesc type) in entries) + { + Vertex keyVertex = writer.GetStringConstant(key); + Vertex valueVertex = writer.GetUnsignedConstant(externalReferences.GetIndex(factory.MaximallyConstructableType(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 tuple = writer.GetTuple(typeMapGroupVertex, typeMapStateVertex, typeMapHashTable); + return section.Place(tuple); + } + + public override IEnumerable GetConditionalStaticDependencies(NodeFactory context) => []; + public override IEnumerable GetStaticDependencies(NodeFactory context) + { + foreach (TypeDesc targetType in entries.Values) + { + yield return new DependencyListEntry(context.MaximallyConstructableType(targetType), "Analyzed external type map entry target type"); + } + } + public override IEnumerable SearchDynamicDependencies(List> markedNodes, int firstNode, NodeFactory context) => []; + public IExternalTypeMapNode ToAnalysisBasedNode(NodeFactory factory) => this; + protected override string GetName(NodeFactory context) => $"Analyzed External Type Map: {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 new file mode 100644 index 00000000000000..ddcb724977ede6 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/AnalyzedProxyTypeMapNode.cs @@ -0,0 +1,64 @@ +// 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.DependencyAnalysisFramework; +using Internal.NativeFormat; +using Internal.TypeSystem; + +namespace ILCompiler.DependencyAnalysis +{ + internal sealed class AnalyzedProxyTypeMapNode(TypeDesc typeMapGroup, IReadOnlyDictionary entries) : DependencyNodeCore, IProxyTypeMapNode + { + public override bool InterestingForDynamicDependencyAnalysis => false; + + public override bool HasDynamicDependencies => false; + + public override bool HasConditionalStaticDependencies => false; + + public override bool StaticDependenciesAreComputed => true; + + public TypeDesc TypeMapGroup => typeMapGroup; + + public int ClassCode => 171742984; + + public int CompareToImpl(ISortableNode other, CompilerComparer comparer) + { + AnalyzedProxyTypeMapNode otherEntry = (AnalyzedProxyTypeMapNode)other; + return comparer.Compare(TypeMapGroup, otherEntry.TypeMapGroup); + } + public Vertex CreateTypeMap(NodeFactory factory, NativeWriter writer, Section section, ExternalReferencesTableNode 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.MaximallyConstructableType(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 tuple = writer.GetTuple(typeMapGroupVertex, typeMapStateVertex, typeMapHashTable); + return section.Place(tuple); + } + + public override IEnumerable GetConditionalStaticDependencies(NodeFactory context) => []; + public override IEnumerable GetStaticDependencies(NodeFactory context) + { + foreach (var (sourceType, proxyType) in entries) + { + yield return new DependencyListEntry(context.MaximallyConstructableType(sourceType), "Analyzed proxy type map entry source type"); + yield return new DependencyListEntry(context.MaximallyConstructableType(proxyType), "Analyzed proxy type map entry proxy type"); + } + } + public override IEnumerable SearchDynamicDependencies(List> markedNodes, int firstNode, NodeFactory context) => []; + public IProxyTypeMapNode ToAnalysisBasedNode(NodeFactory factory) => this; + protected override string GetName(NodeFactory context) => $"Analyzed Proxy Type Map: {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 18f5a4af79d4e8..c17781ff8d3c47 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ExternalTypeMapNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ExternalTypeMapNode.cs @@ -3,15 +3,15 @@ using System; using System.Collections.Generic; +using System.Collections.Immutable; using System.Diagnostics; -using System.Text; -using ILCompiler.DependencyAnalysis; using ILCompiler.DependencyAnalysisFramework; +using Internal.NativeFormat; using Internal.TypeSystem; namespace ILCompiler.DependencyAnalysis { - internal sealed class ExternalTypeMapNode : DependencyNodeCore, ISortableNode, IExternalTypeMapNode + internal sealed class ExternalTypeMapNode : DependencyNodeCore, IExternalTypeMapNode { private readonly IEnumerable> _mapEntries; @@ -33,21 +33,14 @@ public ExternalTypeMapNode(TypeDesc typeMapGroup, IEnumerable GetConditionalStaticDependencies(NodeFactory context) { - List entries = []; foreach (var entry in _mapEntries) { var (targetType, trimmingTargetType) = entry.Value; - entries.Add(new CombinedDependencyListEntry( + yield return new CombinedDependencyListEntry( context.MaximallyConstructableType(targetType), context.NecessaryTypeSymbol(trimmingTargetType), - "Type in external type map is cast target")); - entries.Add(new CombinedDependencyListEntry( - context.MaximallyConstructableType(targetType), - context.ScannedCastTarget(trimmingTargetType), - "Type in external type map is cast target for cast that may have been optimized away")); + "Type in external type map is cast target"); } - - return entries; } public override IEnumerable GetStaticDependencies(NodeFactory context) => []; @@ -63,23 +56,43 @@ public int CompareToImpl(ISortableNode other, CompilerComparer comparer) return comparer.Compare(TypeMapGroup, otherEntry.TypeMapGroup); } - public IEnumerable<(string Name, IEETypeNode target)> GetMarkedEntries(NodeFactory factory) + private IEnumerable<(string Name, IEETypeNode target)> GetMarkedEntries(NodeFactory factory) { - List<(string Name, IEETypeNode target)> markedEntries = []; foreach (var entry in _mapEntries) { var (targetType, trimmingTargetType) = entry.Value; - IEETypeNode trimmingTarget = factory.NecessaryTypeSymbol(trimmingTargetType); - ScannedCastTargetNode scannedCastTarget = factory.ScannedCastTarget(trimmingTargetType); - if (trimmingTarget.Marked || scannedCastTarget.Marked) + if (factory.NecessaryTypeSymbol(trimmingTargetType).Marked) { IEETypeNode targetNode = factory.MaximallyConstructableType(targetType); Debug.Assert(targetNode.Marked); - markedEntries.Add((entry.Key, targetNode)); + yield return (entry.Key, targetNode); } } - return markedEntries; } + + public Vertex CreateTypeMap(NodeFactory factory, NativeWriter writer, Section section, ExternalReferencesTableNode 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 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 tuple = writer.GetTuple(typeMapGroupVertex, typeMapStateVertex, typeMapHashTable); + return section.Place(tuple); + } + + public IExternalTypeMapNode ToAnalysisBasedNode(NodeFactory factory) + => new AnalyzedExternalTypeMapNode( + TypeMapGroup, + GetMarkedEntries(factory) + .ToImmutableDictionary(p => p.Name, p => p.target.Type)); } } diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ExternalTypeMapObjectNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ExternalTypeMapObjectNode.cs index b96291d2ec6e7a..b25753d27b0dc3 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ExternalTypeMapObjectNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ExternalTypeMapObjectNode.cs @@ -44,34 +44,7 @@ public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) foreach (IExternalTypeMapNode externalTypeMap in factory.TypeMapManager.GetExternalTypeMaps()) { - TypeDesc typeMapGroup = externalTypeMap.TypeMapGroup; - - if (externalTypeMap is InvalidExternalTypeMapNode invalidNode) - { - 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(invalidNode.ThrowingMethodStub))); - Vertex tuple = writer.GetTuple(typeMapGroupVertex, typeMapStateVertex, throwingMethodStubVertex); - typeMapGroupHashTable.Append((uint)typeMapGroup.GetHashCode(), hashTableSection.Place(tuple)); - } - else - { - ExternalTypeMapNode validNode = (ExternalTypeMapNode)externalTypeMap; - VertexHashtable typeMapHashTable = new(); - - foreach ((string key, IEETypeNode valueNode) in validNode.GetMarkedEntries(factory)) - { - Vertex keyVertex = writer.GetStringConstant(key); - Vertex valueVertex = writer.GetUnsignedConstant(externalReferences.GetIndex(valueNode)); - Vertex entry = writer.GetTuple(keyVertex, valueVertex); - typeMapHashTable.Append((uint)TypeHashingAlgorithms.ComputeNameHashCode(key), hashTableSection.Place(entry)); - } - - Vertex typeMapStateVertex = writer.GetUnsignedConstant(1); // Valid type map state - Vertex typeMapGroupVertex = writer.GetUnsignedConstant(externalReferences.GetIndex(factory.NecessaryTypeSymbol(typeMapGroup))); - Vertex tuple = writer.GetTuple(typeMapGroupVertex, typeMapStateVertex, typeMapHashTable); - typeMapGroupHashTable.Append((uint)typeMapGroup.GetHashCode(), hashTableSection.Place(tuple)); - } + typeMapGroupHashTable.Append((uint)externalTypeMap.TypeMapGroup.GetHashCode(), externalTypeMap.CreateTypeMap(factory, writer, hashTableSection, externalReferences)); } byte[] hashTableBytes = writer.Save(); diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/IExternalTypeMapNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/IExternalTypeMapNode.cs index 58a67cf27bf608..32e059da731210 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/IExternalTypeMapNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/IExternalTypeMapNode.cs @@ -5,6 +5,7 @@ using System.Collections.Generic; using System.Text; using ILCompiler.DependencyAnalysisFramework; +using Internal.NativeFormat; using Internal.TypeSystem; namespace ILCompiler.DependencyAnalysis @@ -12,5 +13,9 @@ namespace ILCompiler.DependencyAnalysis public interface IExternalTypeMapNode : IDependencyNode, ISortableNode { TypeDesc TypeMapGroup { get; } + + Vertex CreateTypeMap(NodeFactory factory, NativeWriter writer, Section section, ExternalReferencesTableNode externalReferences); + + IExternalTypeMapNode ToAnalysisBasedNode(NodeFactory factory); } } diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/IProxyTypeMapNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/IProxyTypeMapNode.cs index 5a487e370af720..331ec8357b6f71 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/IProxyTypeMapNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/IProxyTypeMapNode.cs @@ -5,6 +5,7 @@ using System.Collections.Generic; using System.Text; using ILCompiler.DependencyAnalysisFramework; +using Internal.NativeFormat; using Internal.TypeSystem; namespace ILCompiler.DependencyAnalysis @@ -12,5 +13,9 @@ namespace ILCompiler.DependencyAnalysis public interface IProxyTypeMapNode : IDependencyNode, ISortableNode { TypeDesc TypeMapGroup { get; } + + Vertex CreateTypeMap(NodeFactory factory, NativeWriter writer, Section section, ExternalReferencesTableNode externalReferences); + + IProxyTypeMapNode ToAnalysisBasedNode(NodeFactory factor); } } 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 b10bfd51465c73..05041970615600 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/InvalidExternalTypeMapNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/InvalidExternalTypeMapNode.cs @@ -5,6 +5,7 @@ using System.Collections.Generic; using System.Text; using ILCompiler.DependencyAnalysisFramework; +using Internal.NativeFormat; using Internal.TypeSystem; namespace ILCompiler.DependencyAnalysis @@ -35,14 +36,17 @@ public override IEnumerable GetStaticDependencies(NodeFacto public int ClassCode => 36910224; - public int CompareToImpl(ISortableNode other, CompilerComparer comparer) - { - var otherNode = (InvalidExternalTypeMapNode)other; + public int CompareToImpl(ISortableNode other, CompilerComparer comparer) => comparer.Compare(TypeMapGroup, ((InvalidExternalTypeMapNode)other).TypeMapGroup); - int result = comparer.Compare(TypeMapGroup, otherNode.TypeMapGroup); - if (result != 0) - return result; - return comparer.Compare(ThrowingMethodStub, otherNode.ThrowingMethodStub); + public Vertex CreateTypeMap(NodeFactory factory, NativeWriter writer, Section section, ExternalReferencesTableNode 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 tuple = writer.GetTuple(typeMapGroupVertex, typeMapStateVertex, throwingMethodStubVertex); + return section.Place(tuple); } + + public IExternalTypeMapNode ToAnalysisBasedNode(NodeFactory factory) => this; } } 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 9a4aa85d8a245a..d2583547809da0 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/InvalidProxyTypeMapNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/InvalidProxyTypeMapNode.cs @@ -5,6 +5,7 @@ using System.Collections.Generic; using System.Text; using ILCompiler.DependencyAnalysisFramework; +using Internal.NativeFormat; using Internal.TypeSystem; namespace ILCompiler.DependencyAnalysis @@ -34,13 +35,17 @@ public override IEnumerable GetStaticDependencies(NodeFacto protected override string GetName(NodeFactory context) => $"Invalid proxy type map: {TypeMapGroup}"; public int ClassCode => 36910224; - public int CompareToImpl(ISortableNode other, CompilerComparer comparer) + + public int CompareToImpl(ISortableNode other, CompilerComparer comparer) => comparer.Compare(TypeMapGroup, ((InvalidProxyTypeMapNode)other).TypeMapGroup); + public Vertex CreateTypeMap(NodeFactory factory, NativeWriter writer, Section section, ExternalReferencesTableNode externalReferences) { - var otherNode = (InvalidProxyTypeMapNode)other; - int result = comparer.Compare(TypeMapGroup, otherNode.TypeMapGroup); - if (result != 0) - return result; - return comparer.Compare(ThrowingMethodStub, otherNode.ThrowingMethodStub); + 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 tuple = writer.GetTuple(typeMapGroupVertex, typeMapStateVertex, throwingMethodStubVertex); + return section.Place(tuple); } + + public IProxyTypeMapNode ToAnalysisBasedNode(NodeFactory factory) => this; } } 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 971e971d9679c4..a6add520521126 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/NodeFactory.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/NodeFactory.cs @@ -598,11 +598,6 @@ private void CreateNodeCaches() return new ProxyTypeMapRequestNode(type); }); - _scannedCastTarget = new NodeCache(type => - { - return new ScannedCastTargetNode(type); - }); - NativeLayout = new NativeLayoutHelper(this); } @@ -1500,13 +1495,6 @@ public ProxyTypeMapRequestNode ProxyTypeMapRequest(TypeDesc type) return _proxyTypeMapRequests.GetOrAdd(type); } - private NodeCache _scannedCastTarget; - - public ScannedCastTargetNode ScannedCastTarget(TypeDesc type) - { - return _scannedCastTarget.GetOrAdd(type); - } - /// /// Returns alternative symbol name that object writer should produce for given symbols /// in addition to the regular one. 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 ec010afadd1a75..fd966b679b17bc 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ProxyTypeMapNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ProxyTypeMapNode.cs @@ -3,10 +3,12 @@ using System; using System.Collections.Generic; +using System.Collections.Immutable; using System.Diagnostics; using System.Text; using ILCompiler.DependencyAnalysis; using ILCompiler.DependencyAnalysisFramework; +using Internal.NativeFormat; using Internal.TypeSystem; namespace ILCompiler.DependencyAnalysis @@ -30,25 +32,21 @@ internal sealed class ProxyTypeMapNode(TypeDesc typeMapGroup, IEnumerable GetConditionalStaticDependencies(NodeFactory context) { - List entries = []; foreach (var (key, value) in mapEntries) { - entries.Add(new CombinedDependencyListEntry( + yield return new CombinedDependencyListEntry( context.MaximallyConstructableType(value), context.MaximallyConstructableType(key), - "Proxy type map entry")); + "Proxy type map entry"); } - - return entries; } 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) => $"Proxy type map: {TypeMapGroup}"; - public IEnumerable<(IEETypeNode key, IEETypeNode value)> GetMarkedEntries(NodeFactory factory) + private IEnumerable<(IEETypeNode key, IEETypeNode value)> GetMarkedEntries(NodeFactory factory) { - List<(IEETypeNode key, IEETypeNode value)> markedEntries = []; foreach (var (key, value) in MapEntries) { IEETypeNode keyNode = factory.MaximallyConstructableType(key); @@ -56,11 +54,33 @@ public override IEnumerable GetConditionalStaticDep { IEETypeNode valueNode = factory.MaximallyConstructableType(value); Debug.Assert(valueNode.Marked); - markedEntries.Add((keyNode, valueNode)); + yield return (keyNode, valueNode); } } + } + + public Vertex CreateTypeMap(NodeFactory factory, NativeWriter writer, Section section, ExternalReferencesTableNode 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 entry = writer.GetTuple(keyVertex, valueVertex); + typeMapHashTable.Append((uint)keyNode.Type.GetHashCode(), section.Place(entry)); + } - return markedEntries; + Vertex typeMapStateVertex = writer.GetUnsignedConstant(1); // Valid type map state + Vertex typeMapGroupVertex = writer.GetUnsignedConstant(externalReferences.GetIndex(factory.NecessaryTypeSymbol(TypeMapGroup))); + Vertex tuple = writer.GetTuple(typeMapGroupVertex, typeMapStateVertex, typeMapHashTable); + return section.Place(tuple); } + + public IProxyTypeMapNode ToAnalysisBasedNode(NodeFactory factory) + => new AnalyzedProxyTypeMapNode( + TypeMapGroup, + GetMarkedEntries(factory) + .ToImmutableDictionary(p => p.key.Type, p => p.value.Type)); } } diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ProxyTypeMapObjectNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ProxyTypeMapObjectNode.cs index ff31210ae71666..ee8c7f05cfae3d 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ProxyTypeMapObjectNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ProxyTypeMapObjectNode.cs @@ -46,33 +46,7 @@ public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) foreach (IProxyTypeMapNode proxyTypeMap in factory.TypeMapManager.GetProxyTypeMaps()) { TypeDesc typeMapGroup = proxyTypeMap.TypeMapGroup; - - if (proxyTypeMap is InvalidProxyTypeMapNode invalidNode) - { - 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(invalidNode.ThrowingMethodStub))); - Vertex tuple = writer.GetTuple(typeMapGroupVertex, typeMapStateVertex, throwingMethodStubVertex); - typeMapGroupHashTable.Append((uint)typeMapGroup.GetHashCode(), hashTableSection.Place(tuple)); - } - else - { - ProxyTypeMapNode validNode = (ProxyTypeMapNode)proxyTypeMap; - VertexHashtable typeMapHashTable = new VertexHashtable(); - - foreach ((IEETypeNode keyNode, IEETypeNode valueNode) in validNode.GetMarkedEntries(factory)) - { - Vertex keyVertex = writer.GetUnsignedConstant(externalReferences.GetIndex(keyNode)); - Vertex valueVertex = writer.GetUnsignedConstant(externalReferences.GetIndex(valueNode)); - Vertex entry = writer.GetTuple(keyVertex, valueVertex); - typeMapHashTable.Append((uint)keyNode.Type.GetHashCode(), hashTableSection.Place(entry)); - } - - Vertex typeMapStateVertex = writer.GetUnsignedConstant(1); // Valid type map state - Vertex typeMapGroupVertex = writer.GetUnsignedConstant(externalReferences.GetIndex(factory.NecessaryTypeSymbol(typeMapGroup))); - Vertex tuple = writer.GetTuple(typeMapGroupVertex, typeMapStateVertex, typeMapHashTable); - typeMapGroupHashTable.Append((uint)typeMapGroup.GetHashCode(), hashTableSection.Place(tuple)); - } + typeMapGroupHashTable.Append((uint)typeMapGroup.GetHashCode(), proxyTypeMap.CreateTypeMap(factory, writer, hashTableSection, externalReferences)); } byte[] hashTableBytes = writer.Save(); diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ScannedCastTargetNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ScannedCastTargetNode.cs deleted file mode 100644 index d5d60604050cd4..00000000000000 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ScannedCastTargetNode.cs +++ /dev/null @@ -1,29 +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.Generic; -using System.Text; -using ILCompiler.DependencyAnalysisFramework; -using Internal.TypeSystem; - -namespace ILCompiler.DependencyAnalysis -{ - public sealed class ScannedCastTargetNode(TypeDesc type) : DependencyNodeCore - { - public override bool InterestingForDynamicDependencyAnalysis => false; - - public override bool HasDynamicDependencies => false; - - public override bool HasConditionalStaticDependencies => true; - - public override bool StaticDependenciesAreComputed => true; - - public TypeDesc Type => type; - - public override IEnumerable GetConditionalStaticDependencies(NodeFactory context) => []; - public override IEnumerable GetStaticDependencies(NodeFactory context) => []; - public override IEnumerable SearchDynamicDependencies(List> markedNodes, int firstNode, NodeFactory context) => []; - protected override string GetName(NodeFactory context) => $"ExternalTrimTarget {type}"; - } -} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ILScanner.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ILScanner.cs index 9a4faae85e4f74..549316953de55d 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ILScanner.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ILScanner.cs @@ -16,6 +16,7 @@ using Internal.ReadyToRunConstants; using Debug = System.Diagnostics.Debug; +using Internal.NativeFormat; namespace ILCompiler { @@ -275,9 +276,9 @@ public ReadOnlyFieldPolicy GetReadOnlyFieldPolicy() return new ScannedReadOnlyPolicy(MarkedNodes); } - public ICompilationRootProvider GetOptimizedAwayObservationsProvider(DevirtualizationManager devirtualizationManager) + public TypeMapManager GetTypeMapManager() { - return new OptimizedAwayObservationsRootProvider(MarkedNodes, devirtualizationManager); + return new ScannedTypeMapManager(_factory, MarkedNodes); } private sealed class ScannedVTableProvider : VTableSliceProvider @@ -989,32 +990,50 @@ public override bool IsReadOnly(FieldDesc field) } } - private sealed class OptimizedAwayObservationsRootProvider : ICompilationRootProvider + private sealed class ScannedTypeMapManager : TypeMapManager { - private HashSet _optimizedAwayCastTargets = new(); - public OptimizedAwayObservationsRootProvider(ImmutableArray> markedNodes, DevirtualizationManager devirtualizationManager) + private ImmutableArray _externalTypeMapNodes; + private ImmutableArray _proxyTypeMapNodes; + + public ScannedTypeMapManager(NodeFactory factory, ImmutableArray> markedNodes) { + ImmutableArray.Builder externalTypeMapNodes = ImmutableArray.CreateBuilder(); + ImmutableArray.Builder proxyTypeMapNodes = ImmutableArray.CreateBuilder(); foreach (var node in markedNodes) { - if (node is ScannedCastTargetNode { Type: TypeDesc castTarget } - && !devirtualizationManager.CanReferenceConstructedTypeOrCanonicalFormOfType(castTarget)) + if (node is IExternalTypeMapNode externalTypeMapNode) + { + externalTypeMapNodes.Add(externalTypeMapNode.ToAnalysisBasedNode(factory)); + } + else if (node is IProxyTypeMapNode proxyTypeMapNode) { - // Scanning observed a cast to castTarget, but castTarget can't be constructed. - // We may completely remove the cast. If this is the only reference to castTarget, - // this means that the codegen compilation would never observe castTarget. - // Record it here so we can root the fact that the cast was observed. - _optimizedAwayCastTargets.Add(castTarget); + proxyTypeMapNodes.Add(proxyTypeMapNode.ToAnalysisBasedNode(factory)); } } + + _externalTypeMapNodes = externalTypeMapNodes.ToImmutable(); + _proxyTypeMapNodes = proxyTypeMapNodes.ToImmutable(); } - public void AddCompilationRoots(IRootingServiceProvider rootProvider) + protected override bool IsEmpty => _externalTypeMapNodes.Length == 0 && _proxyTypeMapNodes.Length == 0; + + public override void AddCompilationRoots(IRootingServiceProvider rootProvider) { - foreach (var target in _optimizedAwayCastTargets) + const string reason = "Used Type Map Group"; + + foreach (IExternalTypeMapNode externalTypeMap in _externalTypeMapNodes) { - rootProvider.RootPossibleCastTarget(target, "Observed cast target"); + rootProvider.AddCompilationRoot(externalTypeMap, reason); + } + + foreach (IProxyTypeMapNode proxyTypeMap in _proxyTypeMapNodes) + { + rootProvider.AddCompilationRoot(proxyTypeMap, reason); } } + + internal override IEnumerable GetExternalTypeMaps() => _externalTypeMapNodes; + internal override IEnumerable GetProxyTypeMaps() => _proxyTypeMapNodes; } } } diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ILScannerBuilder.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ILScannerBuilder.cs index 3df2518f11f80f..d231500c89f13b 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ILScannerBuilder.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ILScannerBuilder.cs @@ -25,7 +25,7 @@ public sealed class ILScannerBuilder private IEnumerable _compilationRoots = Array.Empty(); private MetadataManager _metadataManager; private InteropStubManager _interopStubManager = new EmptyInteropStubManager(); - private TypeMapManager _typeMapManager = new UsageBasedTypeMapManager(TypeMapStates.Empty); + private TypeMapManager _typeMapManager = new UsageBasedTypeMapManager(TypeMapMetadata.Empty); private int _parallelism = -1; internal ILScannerBuilder(CompilerTypeSystemContext context, CompilationModuleGroup compilationGroup, NameMangler mangler, ILProvider ilProvider, PreinitializationManager preinitializationManager) diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/IRootingServiceProvider.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/IRootingServiceProvider.cs index ae616277399617..74bdcbdac01c8e 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/IRootingServiceProvider.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/IRootingServiceProvider.cs @@ -22,7 +22,6 @@ public interface IRootingServiceProvider void RootReadOnlyDataBlob(byte[] data, int alignment, string reason, string exportName, bool exportHidden); void RootDelegateMarshallingData(DefType type, string reason); void RootStructMarshallingData(DefType type, string reason); - void RootPossibleCastTarget(TypeDesc type, string reason); void AddCompilationRoot(object o, string reason); } } diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/RootingServiceProvider.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/RootingServiceProvider.cs index 89d5593c04e90b..f9fa7937f2cb06 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/RootingServiceProvider.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/RootingServiceProvider.cs @@ -157,10 +157,5 @@ public void RootStructMarshallingData(DefType type, string reason) { _rootAdder(_factory.StructMarshallingData(type), reason); } - - public void RootPossibleCastTarget(TypeDesc type, string reason) - { - _rootAdder(_factory.ScannedCastTarget(type), reason); - } } } diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/TypeMapManager.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/TypeMapManager.cs index 9e33bffe718422..b4392aead3d712 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/TypeMapManager.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/TypeMapManager.cs @@ -22,98 +22,6 @@ namespace ILCompiler /// public abstract class TypeMapManager : ICompilationRootProvider { - protected internal sealed class TypeMapState - { - private readonly Dictionary _associatedTypeMap = []; - private readonly Dictionary _externalTypeMap = []; - private ThrowingMethodStub _externalTypeMapExceptionStub; - private ThrowingMethodStub _associatedTypeMapExceptionStub; - - public void AddAssociatedTypeMapEntry(TypeDesc type, TypeDesc associatedType) - { - if (!_associatedTypeMap.TryAdd(type, associatedType)) - { - ThrowHelper.ThrowBadImageFormatException(); - } - } - public void AddExternalTypeMapEntry(string typeName, TypeDesc type, TypeDesc trimmingTarget) - { - if (!_externalTypeMap.TryAdd(typeName, (type, trimmingTarget))) - { - ThrowHelper.ThrowBadImageFormatException(); - } - } - - public void SetExternalTypeMapStub(ThrowingMethodStub stub) - { - if (_externalTypeMapExceptionStub?.Exception is TypeSystemException.FileNotFoundException) - { - // FileNotFound exception takes precedence. - return; - } - _externalTypeMapExceptionStub ??= stub; - } - - public void SetAssociatedTypeMapStub(ThrowingMethodStub stub) - { - if (_associatedTypeMapExceptionStub?.Exception is TypeSystemException.FileNotFoundException) - { - // FileNotFound exception takes precedence. - return; - } - _associatedTypeMapExceptionStub ??= stub; - } - - public IExternalTypeMapNode GetExternalTypeMapNode(TypeDesc group) - { - if (_externalTypeMapExceptionStub is not null) - { - return new InvalidExternalTypeMapNode(group, _externalTypeMapExceptionStub); - } - return new ExternalTypeMapNode(group, _externalTypeMap); - } - - public IProxyTypeMapNode GetProxyTypeMapNode(TypeDesc group) - { - if (_associatedTypeMapExceptionStub is not null) - { - return new InvalidProxyTypeMapNode(group, _associatedTypeMapExceptionStub); - } - return new ProxyTypeMapNode(group, _associatedTypeMap); - } - } - - protected internal sealed class ThrowingMethodStub(TypeDesc owningType, TypeDesc typeMapGroup, bool externalTypeMap, TypeSystemException ex) : ILStubMethod - { - public TypeSystemException Exception => ex; - public override string Name => $"InvalidTypeMapStub_{typeMapGroup}_{(externalTypeMap ? "External" : "Proxy")}"; - public override MethodIL EmitIL() - { - return TypeSystemThrowingILEmitter.EmitIL(this, Exception); - } - - protected override int CompareToImpl(MethodDesc other, TypeSystemComparer comparer) => other is ThrowingMethodStub otherStub ? Name.CompareTo(otherStub.Name, StringComparison.Ordinal) : -1; - - public override bool IsPInvoke => false; - - public override string DiagnosticName => Name; - - protected override int ClassCode => 1744789196; - - public override TypeDesc OwningType => owningType; - - public override MethodSignature Signature => new MethodSignature(MethodSignatureFlags.Static, 0, Context.GetWellKnownType(WellKnownType.Void), []); - - public override TypeSystemContext Context => owningType.Context; - } - - protected readonly TypeMapStates _typeMaps; - - protected TypeMapManager(TypeMapStates typeMapStates) - { - _typeMaps = typeMapStates; - } - public enum TypeMapAttributeKind { None, @@ -144,9 +52,11 @@ public virtual void AttachToDependencyGraph(DependencyAnalyzerBase public abstract void AddCompilationRoots(IRootingServiceProvider rootProvider); + protected abstract bool IsEmpty { get; } + public void AddToReadyToRunHeader(ReadyToRunHeaderNode header, NodeFactory nodeFactory, ExternalReferencesTableNode commonFixupsTableNode) { - if (_typeMaps.IsEmpty) + if (IsEmpty) { return; // No type maps to emit } diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/TypeMapStates.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/TypeMapMetadata.cs similarity index 53% rename from src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/TypeMapStates.cs rename to src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/TypeMapMetadata.cs index 6c915736538ef2..dadcbf7101bf8f 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/TypeMapStates.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/TypeMapMetadata.cs @@ -1,35 +1,130 @@ // 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.Diagnostics; using System.Reflection.Metadata; +using ILCompiler.DependencyAnalysis; +using Internal.IL; +using Internal.IL.Stubs; using Internal.TypeSystem; using Internal.TypeSystem.Ecma; using static ILCompiler.TypeMapManager; +using static ILCompiler.UsageBasedTypeMapManager; namespace ILCompiler { - public sealed class TypeMapStates + public sealed class TypeMapMetadata { - public static readonly TypeMapStates Empty = new TypeMapStates(new Dictionary()); + internal sealed class Map(TypeDesc typeMapGroup) + { + private sealed class ThrowingMethodStub(TypeDesc owningType, TypeDesc typeMapGroup, bool externalTypeMap, TypeSystemException ex) : ILStubMethod + { + public TypeSystemException Exception => ex; + public override string Name => $"InvalidTypeMapStub_{typeMapGroup}_{(externalTypeMap ? "External" : "Proxy")}"; + public override MethodIL EmitIL() + { + return TypeSystemThrowingILEmitter.EmitIL(this, Exception); + } + + protected override int CompareToImpl(MethodDesc other, TypeSystemComparer comparer) => other is ThrowingMethodStub otherStub ? Name.CompareTo(otherStub.Name, StringComparison.Ordinal) : -1; + + public override bool IsPInvoke => false; + + public override string DiagnosticName => Name; + + protected override int ClassCode => 1744789196; + + public override TypeDesc OwningType => owningType; + + public override MethodSignature Signature => new MethodSignature(MethodSignatureFlags.Static, 0, Context.GetWellKnownType(WellKnownType.Void), []); + + public override TypeSystemContext Context => owningType.Context; + } + + private readonly Dictionary _associatedTypeMap = []; + private readonly Dictionary _externalTypeMap = []; + private ThrowingMethodStub _externalTypeMapExceptionStub; + private ThrowingMethodStub _associatedTypeMapExceptionStub; + + public TypeDesc TypeMapGroup { get; } = typeMapGroup; + + public void AddAssociatedTypeMapEntry(TypeDesc type, TypeDesc associatedType) + { + if (!_associatedTypeMap.TryAdd(type, associatedType)) + { + ThrowHelper.ThrowBadImageFormatException(); + } + } + public void AddExternalTypeMapEntry(string typeName, TypeDesc type, TypeDesc trimmingTarget) + { + if (!_externalTypeMap.TryAdd(typeName, (type, trimmingTarget))) + { + ThrowHelper.ThrowBadImageFormatException(); + } + } - private readonly IReadOnlyDictionary _states; + public void SetExternalTypeMapStub(ModuleDesc stubModule, TypeSystemException exception) + { + if (_externalTypeMapExceptionStub?.Exception is TypeSystemException.FileNotFoundException) + { + // FileNotFound exception takes precedence. + return; + } + _externalTypeMapExceptionStub ??= new ThrowingMethodStub(stubModule.GetGlobalModuleType(), TypeMapGroup, externalTypeMap: true, exception); + } + + public void SetAssociatedTypeMapStub(ModuleDesc stubModule, TypeSystemException exception) + { + if (_associatedTypeMapExceptionStub?.Exception is TypeSystemException.FileNotFoundException) + { + // FileNotFound exception takes precedence. + return; + } + _associatedTypeMapExceptionStub ??= new ThrowingMethodStub(stubModule.GetGlobalModuleType(), TypeMapGroup, externalTypeMap: true, exception); + } - internal TypeMapStates(IReadOnlyDictionary states) + 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 static readonly TypeMapMetadata Empty = new TypeMapMetadata(new Dictionary(), "No type maps"); + + private readonly IReadOnlyDictionary _states; + + internal TypeMapMetadata(IReadOnlyDictionary states, string diagnosticName) { _states = states; + DiagnosticName = diagnosticName; } - internal TypeMapState this[TypeDesc typeMapGroup] => _states[typeMapGroup]; + internal Map this[TypeDesc typeMapGroup] => _states[typeMapGroup]; public bool IsEmpty => _states.Count == 0; - internal IEnumerable> States => _states; + internal IEnumerable> Maps => _states; + + public string DiagnosticName { get; } - public static TypeMapStates CreateFromAssembly(EcmaAssembly assembly) + public static TypeMapMetadata CreateFromAssembly(EcmaAssembly assembly) { - Dictionary typeMapStates = []; + Dictionary typeMapStates = []; HashSet scannedAssemblies = []; Queue assembliesToScan = new Queue(); @@ -89,20 +184,20 @@ public static TypeMapStates CreateFromAssembly(EcmaAssembly assembly) } catch (TypeSystemException ex) { - if (!typeMapStates.TryGetValue(typeMapGroup, out TypeMapState value)) + if (!typeMapStates.TryGetValue(typeMapGroup, out Map value)) { - value = new TypeMapState(); + value = new Map(typeMapGroup); typeMapStates[typeMapGroup] = value; } if (attrKind is TypeMapAttributeKind.TypeMapAssemblyTarget or TypeMapAttributeKind.TypeMap) { - value.SetExternalTypeMapStub(new ThrowingMethodStub(assembly.GetGlobalModuleType(), typeMapGroup, externalTypeMap: true, ex)); + value.SetExternalTypeMapStub(assembly, ex); } if (attrKind is TypeMapAttributeKind.TypeMapAssemblyTarget or TypeMapAttributeKind.TypeMapAssociation) { - value.SetAssociatedTypeMapStub(new ThrowingMethodStub(assembly.GetGlobalModuleType(), typeMapGroup, externalTypeMap: false, ex)); + value.SetAssociatedTypeMapStub(assembly, ex); } } } @@ -126,9 +221,9 @@ void ProcessTypeMapAttribute(CustomAttributeValue attrValue, TypeDesc { case [{ Value: string typeName }, { Value: TypeDesc targetType }]: { - if (!typeMapStates.TryGetValue(typeMapGroup, out TypeMapState typeMapState)) + if (!typeMapStates.TryGetValue(typeMapGroup, out Map typeMapState)) { - typeMapStates[typeMapGroup] = typeMapState = new TypeMapState(); + typeMapStates[typeMapGroup] = typeMapState = new Map(typeMapGroup); } typeMapState.AddExternalTypeMapEntry(typeName, targetType, targetType); break; @@ -136,9 +231,9 @@ void ProcessTypeMapAttribute(CustomAttributeValue attrValue, TypeDesc case [{ Value: string typeName }, { Value: TypeDesc targetType }, { Value: TypeDesc trimTargetType }]: { - if (!typeMapStates.TryGetValue(typeMapGroup, out TypeMapState typeMapState)) + if (!typeMapStates.TryGetValue(typeMapGroup, out Map typeMapState)) { - typeMapStates[typeMapGroup] = typeMapState = new TypeMapState(); + typeMapStates[typeMapGroup] = typeMapState = new Map(typeMapGroup); } typeMapState.AddExternalTypeMapEntry(typeName, targetType, trimTargetType); break; @@ -160,16 +255,16 @@ void ProcessTypeMapAssociationAttribute(CustomAttributeValue attrValue return; } - if (!typeMapStates.TryGetValue(typeMapGroup, out TypeMapState typeMapState)) + if (!typeMapStates.TryGetValue(typeMapGroup, out Map typeMapState)) { - typeMapStates[typeMapGroup] = typeMapState = new TypeMapState(); + typeMapStates[typeMapGroup] = typeMapState = new Map(typeMapGroup); } typeMapState.AddAssociatedTypeMapEntry(type, associatedType); } } - return new TypeMapStates(typeMapStates); + return new TypeMapMetadata(typeMapStates, $"Type maps rooted at {assembly}"); } } } diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/UsageBasedTypeMapManager.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/UsageBasedTypeMapManager.cs index 8ba8e6ce633ff7..9952f5ebc1bb78 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/UsageBasedTypeMapManager.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/UsageBasedTypeMapManager.cs @@ -6,14 +6,16 @@ using System.Text; using ILCompiler.DependencyAnalysis; using ILCompiler.DependencyAnalysisFramework; +using Internal.IL; +using Internal.IL.Stubs; using Internal.TypeSystem; using Internal.TypeSystem.Ecma; namespace ILCompiler { - public sealed class UsageBasedTypeMapManager(TypeMapStates state) : TypeMapManager(state) + public sealed class UsageBasedTypeMapManager : TypeMapManager { - private sealed class TypeMapsNode(TypeMapStates typeMapState) : DependencyNodeCore + private sealed class AllTypeMapsNode(TypeMapMetadata typeMapState) : DependencyNodeCore { public override bool InterestingForDynamicDependencyAnalysis => false; @@ -26,10 +28,10 @@ private sealed class TypeMapsNode(TypeMapStates typeMapState) : DependencyNodeCo public override IEnumerable GetConditionalStaticDependencies(NodeFactory context) { List entries = []; - foreach ((TypeDesc typeMapGroup, TypeMapState typeMapState) in typeMapState.States) + foreach ((TypeDesc typeMapGroup, TypeMapMetadata.Map typeMap) in typeMapState.Maps) { - entries.Add(new CombinedDependencyListEntry(typeMapState.GetExternalTypeMapNode(typeMapGroup), context.ExternalTypeMapRequest(typeMapGroup), "ExternalTypeMap")); - entries.Add(new CombinedDependencyListEntry(typeMapState.GetProxyTypeMapNode(typeMapGroup), context.ProxyTypeMapRequest(typeMapGroup), "ProxyTypeMap")); + entries.Add(new CombinedDependencyListEntry(typeMap.GetExternalTypeMapNode(), context.ExternalTypeMapRequest(typeMapGroup), "ExternalTypeMap")); + entries.Add(new CombinedDependencyListEntry(typeMap.GetProxyTypeMapNode(), context.ProxyTypeMapRequest(typeMapGroup), "ProxyTypeMap")); } return entries; @@ -37,15 +39,21 @@ 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) => "TypeMapsNode"; + protected override string GetName(NodeFactory context) => $"Type maps root node: {typeMapState.DiagnosticName}"; } - private readonly List _usedExternalTypeMap = new List(); - private readonly List _usedProxyTypeMap = new List(); - private readonly SortedSet _externalTypeMaps = new SortedSet(CompilerComparer.Instance); private readonly SortedSet _proxyTypeMaps = new SortedSet(CompilerComparer.Instance); + private readonly TypeMapMetadata _typeMaps; + + public UsageBasedTypeMapManager(TypeMapMetadata state) + { + _typeMaps = state; + } + + protected override bool IsEmpty => _typeMaps.IsEmpty; + public override void AttachToDependencyGraph(DependencyAnalyzerBase graph) { base.AttachToDependencyGraph(graph); @@ -55,16 +63,6 @@ public override void AttachToDependencyGraph(DependencyAnalyzerBase private void Graph_NewMarkedNode(DependencyNodeCore obj) { - if (obj is ExternalTypeMapRequestNode externalTypeMapRequest) - { - _usedExternalTypeMap.Add(externalTypeMapRequest.TypeMapGroup); - } - - if (obj is ProxyTypeMapRequestNode proxyTypeMapRequestNode) - { - _usedProxyTypeMap.Add(proxyTypeMapRequestNode.TypeMapGroup); - } - if (obj is IExternalTypeMapNode externalTypeMapNode) { _externalTypeMaps.Add(externalTypeMapNode); @@ -93,12 +91,7 @@ public override void AddCompilationRoots(IRootingServiceProvider rootProvider) return; // No type maps to process } - rootProvider.AddCompilationRoot(new TypeMapsNode(_typeMaps), "TypeMapManager"); - } - - public AnalysisBasedTypeMapManager ToAnalysisBasedTypeMapManager() - { - return new AnalysisBasedTypeMapManager(_typeMaps, _usedExternalTypeMap, _usedProxyTypeMap); + rootProvider.AddCompilationRoot(new AllTypeMapsNode(_typeMaps), "TypeMapManager"); } } } diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/IL/ILImporter.Scanner.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/IL/ILImporter.Scanner.cs index 2362f2837d9b43..00a0d56d48150f 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/IL/ILImporter.Scanner.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/IL/ILImporter.Scanner.cs @@ -839,8 +839,6 @@ private void ImportBranch(ILOpcode opcode, BasicBlock target, BasicBlock fallthr condition = _factory.TypeMetadata(typeEqualityCheckMetadataType); else condition = _factory.MaximallyConstructableType(typeEqualityCheckType); - - _dependencies.Add(_factory.ScannedCastTarget(typeEqualityCheckType), "Type equality check that may be optimized out."); } } @@ -851,7 +849,6 @@ private void ImportBranch(ILOpcode opcode, BasicBlock target, BasicBlock fallthr && !isinstCheckType.ConvertToCanonForm(CanonicalFormKind.Specific).IsCanonicalSubtype(CanonicalFormKind.Any)) { condition = _factory.MaximallyConstructableType(isinstCheckType); - _dependencies.Add(_factory.ScannedCastTarget(isinstCheckType), "isinst check that may be optimized out."); } } diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/ILCompiler.Compiler.csproj b/src/coreclr/tools/aot/ILCompiler.Compiler/ILCompiler.Compiler.csproj index 64aa0aa6425157..8f0bb038d21fe1 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/ILCompiler.Compiler.csproj +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/ILCompiler.Compiler.csproj @@ -343,10 +343,11 @@ - + + @@ -414,7 +415,6 @@ - @@ -635,7 +635,7 @@ - + diff --git a/src/coreclr/tools/aot/ILCompiler.Trimming.Tests/TestCasesRunner/TrimmingDriver.cs b/src/coreclr/tools/aot/ILCompiler.Trimming.Tests/TestCasesRunner/TrimmingDriver.cs index 496b79d9d4ea4f..73464777dd6ca8 100644 --- a/src/coreclr/tools/aot/ILCompiler.Trimming.Tests/TestCasesRunner/TrimmingDriver.cs +++ b/src/coreclr/tools/aot/ILCompiler.Trimming.Tests/TestCasesRunner/TrimmingDriver.cs @@ -139,10 +139,10 @@ public ILScanResults Trim (ILCompilerOptions options, TrimmingCustomizations? cu InteropStateManager interopStateManager = new InteropStateManager (typeSystemContext.GeneratedAssembly); InteropStubManager interopStubManager = new UsageBasedInteropStubManager (interopStateManager, pinvokePolicy, logger); - TypeMapManager typeMapManager = new UsageBasedTypeMapManager (TypeMapStates.Empty); + TypeMapManager typeMapManager = new UsageBasedTypeMapManager (TypeMapMetadata.Empty); if (entrypointModule is { Assembly: EcmaAssembly entryAssembly }) { - typeMapManager = new UsageBasedTypeMapManager (TypeMapStates.CreateFromAssembly (entryAssembly)); + typeMapManager = new UsageBasedTypeMapManager (TypeMapMetadata.CreateFromAssembly (entryAssembly)); } CompilationBuilder builder = new RyuJitCompilationBuilder (typeSystemContext, compilationGroup) diff --git a/src/coreclr/tools/aot/ILCompiler/Program.cs b/src/coreclr/tools/aot/ILCompiler/Program.cs index 15ed927ec8a830..0f78ee41fb9fe7 100644 --- a/src/coreclr/tools/aot/ILCompiler/Program.cs +++ b/src/coreclr/tools/aot/ILCompiler/Program.cs @@ -192,7 +192,7 @@ public int Run() CompilationModuleGroup compilationGroup; List compilationRoots = new List(); - TypeMapManager typeMapManager = new UsageBasedTypeMapManager(TypeMapStates.Empty); + TypeMapManager typeMapManager = new UsageBasedTypeMapManager(TypeMapMetadata.Empty); bool multiFile = Get(_command.MultiFile); if (singleMethod != null) { @@ -201,7 +201,7 @@ public int Run() compilationRoots.Add(new SingleMethodRootProvider(singleMethod)); if (singleMethod.OwningType is MetadataType { Module.Assembly: EcmaAssembly assembly }) { - typeMapManager = new UsageBasedTypeMapManager(TypeMapStates.CreateFromAssembly(assembly)); + typeMapManager = new UsageBasedTypeMapManager(TypeMapMetadata.CreateFromAssembly(assembly)); } } else @@ -315,7 +315,7 @@ public int Run() if (entrypointModule is { Assembly: EcmaAssembly entryAssembly }) { - typeMapManager = new UsageBasedTypeMapManager(TypeMapStates.CreateFromAssembly(entryAssembly)); + typeMapManager = new UsageBasedTypeMapManager(TypeMapMetadata.CreateFromAssembly(entryAssembly)); } } @@ -478,7 +478,8 @@ public int Run() var preinitManager = new PreinitializationManager(typeSystemContext, compilationGroup, ilProvider, preinitPolicy, new StaticReadOnlyFieldPolicy(), flowAnnotations); builder .UseILProvider(ilProvider) - .UsePreinitializationManager(preinitManager); + .UsePreinitializationManager(preinitManager) + .UseTypeMapManager(typeMapManager); #if DEBUG List scannerConstructedTypes = null; @@ -525,7 +526,7 @@ void RunScanner() metadataManager = ((UsageBasedMetadataManager)metadataManager).ToAnalysisBasedMetadataManager(); - typeMapManager = ((UsageBasedTypeMapManager)typeMapManager).ToAnalysisBasedTypeMapManager(); + builder.UseTypeMapManager(scanResults.GetTypeMapManager()); interopStubManager = scanResults.GetInteropStubManager(interopStateManager, pinvokePolicy); @@ -558,9 +559,6 @@ void RunScanner() // have answers for because we didn't scan the entire method. builder.UseMethodImportationErrorProvider(scanResults.GetMethodImportationErrorProvider()); - // Root any observations that we may optimize out from whole program analysis - compilationRoots.Add(scanResults.GetOptimizedAwayObservationsProvider(devirtualizationManager)); - // If we're doing preinitialization, use a new preinitialization manager that // has the whole program view. if (preinitStatics) @@ -616,8 +614,7 @@ void RunScanner() .UseSecurityMitigationOptions(securityMitigationOptions) .UseDebugInfoProvider(debugInfoProvider) .UseDwarf5(Get(_command.UseDwarf5)) - .UseResilience(resilient) - .UseTypeMapManager(typeMapManager); + .UseResilience(resilient); ICompilation compilation = builder.ToCompilation(); From 0b037e8ad10e6f3facb00cf6bb0a5536897b0673 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Tue, 10 Jun 2025 12:59:09 -0700 Subject: [PATCH 35/45] Add comment on TrimTarget --- .../illink/test/Mono.Linker.Tests.Cases/Reflection/TypeMap.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/Reflection/TypeMap.cs b/src/tools/illink/test/Mono.Linker.Tests.Cases/Reflection/TypeMap.cs index 91adb61673e162..3976fc42ffb345 100644 --- a/src/tools/illink/test/Mono.Linker.Tests.Cases/Reflection/TypeMap.cs +++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/Reflection/TypeMap.cs @@ -63,7 +63,7 @@ class TargetAndTrimTarget; [Kept] class TargetType; - [Kept] + [Kept] // This is kept by NativeAot by the scanner. It is not kept during codegen. class TrimTarget; class UnreferencedTargetType; From aa1ada7027aa5c23176bab5ea19c329edf530a17 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Wed, 11 Jun 2025 10:13:31 -0700 Subject: [PATCH 36/45] Fix failures in TypeMapApp test --- .../DependencyAnalysis/InvalidExternalTypeMapNode.cs | 2 +- .../DependencyAnalysis/InvalidProxyTypeMapNode.cs | 2 +- .../aot/ILCompiler.Compiler/Compiler/TypeMapMetadata.cs | 9 ++++++--- 3 files changed, 8 insertions(+), 5 deletions(-) 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 05041970615600..4674e713ec7a38 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/InvalidExternalTypeMapNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/InvalidExternalTypeMapNode.cs @@ -47,6 +47,6 @@ public Vertex CreateTypeMap(NodeFactory factory, NativeWriter writer, Section se return section.Place(tuple); } - public IExternalTypeMapNode ToAnalysisBasedNode(NodeFactory factory) => this; + public IExternalTypeMapNode ToAnalysisBasedNode(NodeFactory factory) => new InvalidExternalTypeMapNode(TypeMapGroup, ThrowingMethodStub); } } 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 d2583547809da0..a929fbb205ac8e 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/InvalidProxyTypeMapNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/InvalidProxyTypeMapNode.cs @@ -46,6 +46,6 @@ public Vertex CreateTypeMap(NodeFactory factory, NativeWriter writer, Section se return section.Place(tuple); } - public IProxyTypeMapNode ToAnalysisBasedNode(NodeFactory factory) => this; + public IProxyTypeMapNode ToAnalysisBasedNode(NodeFactory factory) => new InvalidProxyTypeMapNode(TypeMapGroup, ThrowingMethodStub); } } diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/TypeMapMetadata.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/TypeMapMetadata.cs index dadcbf7101bf8f..b1910457fc1725 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/TypeMapMetadata.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/TypeMapMetadata.cs @@ -28,7 +28,10 @@ public override MethodIL EmitIL() return TypeSystemThrowingILEmitter.EmitIL(this, Exception); } - protected override int CompareToImpl(MethodDesc other, TypeSystemComparer comparer) => other is ThrowingMethodStub otherStub ? Name.CompareTo(otherStub.Name, StringComparison.Ordinal) : -1; + protected override int CompareToImpl(MethodDesc other, TypeSystemComparer comparer) + { + return Name.CompareTo(other.Name, StringComparison.Ordinal); + } public override bool IsPInvoke => false; @@ -82,7 +85,7 @@ public void SetAssociatedTypeMapStub(ModuleDesc stubModule, TypeSystemException // FileNotFound exception takes precedence. return; } - _associatedTypeMapExceptionStub ??= new ThrowingMethodStub(stubModule.GetGlobalModuleType(), TypeMapGroup, externalTypeMap: true, exception); + _associatedTypeMapExceptionStub ??= new ThrowingMethodStub(stubModule.GetGlobalModuleType(), TypeMapGroup, externalTypeMap: false, exception); } public IExternalTypeMapNode GetExternalTypeMapNode() @@ -108,7 +111,7 @@ public IProxyTypeMapNode GetProxyTypeMapNode() private readonly IReadOnlyDictionary _states; - internal TypeMapMetadata(IReadOnlyDictionary states, string diagnosticName) + private TypeMapMetadata(IReadOnlyDictionary states, string diagnosticName) { _states = states; DiagnosticName = diagnosticName; From da59332e53e162313070cd95179914ec6eb05002 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Thu, 12 Jun 2025 11:10:19 -0700 Subject: [PATCH 37/45] Seal types --- .../InteropServices/TypeMapLazyDictionary.NativeAot.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 b420cd12ab3b1d..190a75a2d016bf 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 @@ -118,7 +118,7 @@ private static unsafe bool TryGetNativeReaderForBlob(TypeManagerHandle module, R return false; } - private class ExternalTypeMapDictionary(NativeHashtable table, ExternalReferencesTable externalReferences) : IReadOnlyDictionary + private sealed class ExternalTypeMapDictionary(NativeHashtable table, ExternalReferencesTable externalReferences) : IReadOnlyDictionary { public Type this[string key] { @@ -163,7 +163,7 @@ public bool TryGetValue(string key, [MaybeNullWhen(false)] out Type value) IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); } - private class AssociatedTypeMapDictionary(NativeHashtable table, ExternalReferencesTable externalReferences) : IReadOnlyDictionary + private sealed class AssociatedTypeMapDictionary(NativeHashtable table, ExternalReferencesTable externalReferences) : IReadOnlyDictionary { public Type this[Type key] { From c37d5855384ab9657f53c154c0a76f6d59ff4f05 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Thu, 12 Jun 2025 11:14:28 -0700 Subject: [PATCH 38/45] Put stubs on the generated asssembly --- .../aot/ILCompiler.Compiler/Compiler/TypeMapMetadata.cs | 6 +++--- .../TestCasesRunner/TrimmingDriver.cs | 2 +- src/coreclr/tools/aot/ILCompiler/Program.cs | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/TypeMapMetadata.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/TypeMapMetadata.cs index b1910457fc1725..2452be22e95cd3 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/TypeMapMetadata.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/TypeMapMetadata.cs @@ -125,7 +125,7 @@ private TypeMapMetadata(IReadOnlyDictionary states, string diagno public string DiagnosticName { get; } - public static TypeMapMetadata CreateFromAssembly(EcmaAssembly assembly) + public static TypeMapMetadata CreateFromAssembly(EcmaAssembly assembly, CompilerTypeSystemContext typeSystemContext) { Dictionary typeMapStates = []; HashSet scannedAssemblies = []; @@ -195,12 +195,12 @@ public static TypeMapMetadata CreateFromAssembly(EcmaAssembly assembly) if (attrKind is TypeMapAttributeKind.TypeMapAssemblyTarget or TypeMapAttributeKind.TypeMap) { - value.SetExternalTypeMapStub(assembly, ex); + value.SetExternalTypeMapStub(typeSystemContext.GeneratedAssembly, ex); } if (attrKind is TypeMapAttributeKind.TypeMapAssemblyTarget or TypeMapAttributeKind.TypeMapAssociation) { - value.SetAssociatedTypeMapStub(assembly, ex); + value.SetAssociatedTypeMapStub(typeSystemContext.GeneratedAssembly, ex); } } } diff --git a/src/coreclr/tools/aot/ILCompiler.Trimming.Tests/TestCasesRunner/TrimmingDriver.cs b/src/coreclr/tools/aot/ILCompiler.Trimming.Tests/TestCasesRunner/TrimmingDriver.cs index 73464777dd6ca8..0cce37ed12b222 100644 --- a/src/coreclr/tools/aot/ILCompiler.Trimming.Tests/TestCasesRunner/TrimmingDriver.cs +++ b/src/coreclr/tools/aot/ILCompiler.Trimming.Tests/TestCasesRunner/TrimmingDriver.cs @@ -142,7 +142,7 @@ public ILScanResults Trim (ILCompilerOptions options, TrimmingCustomizations? cu TypeMapManager typeMapManager = new UsageBasedTypeMapManager (TypeMapMetadata.Empty); if (entrypointModule is { Assembly: EcmaAssembly entryAssembly }) { - typeMapManager = new UsageBasedTypeMapManager (TypeMapMetadata.CreateFromAssembly (entryAssembly)); + typeMapManager = new UsageBasedTypeMapManager (TypeMapMetadata.CreateFromAssembly(entryAssembly, typeSystemContext)); } CompilationBuilder builder = new RyuJitCompilationBuilder (typeSystemContext, compilationGroup) diff --git a/src/coreclr/tools/aot/ILCompiler/Program.cs b/src/coreclr/tools/aot/ILCompiler/Program.cs index 0f78ee41fb9fe7..91efe2aa27086b 100644 --- a/src/coreclr/tools/aot/ILCompiler/Program.cs +++ b/src/coreclr/tools/aot/ILCompiler/Program.cs @@ -201,7 +201,7 @@ public int Run() compilationRoots.Add(new SingleMethodRootProvider(singleMethod)); if (singleMethod.OwningType is MetadataType { Module.Assembly: EcmaAssembly assembly }) { - typeMapManager = new UsageBasedTypeMapManager(TypeMapMetadata.CreateFromAssembly(assembly)); + typeMapManager = new UsageBasedTypeMapManager(TypeMapMetadata.CreateFromAssembly(assembly, typeSystemContext)); } } else @@ -315,7 +315,7 @@ public int Run() if (entrypointModule is { Assembly: EcmaAssembly entryAssembly }) { - typeMapManager = new UsageBasedTypeMapManager(TypeMapMetadata.CreateFromAssembly(entryAssembly)); + typeMapManager = new UsageBasedTypeMapManager(TypeMapMetadata.CreateFromAssembly(entryAssembly, typeSystemContext)); } } From 44c0635a471a36ee02b4456d0a9f1fe37435101d Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Thu, 12 Jun 2025 11:51:01 -0700 Subject: [PATCH 39/45] Don't instantiate Dictionary on the "no map" path. --- .../TypeMapLazyDictionary.NativeAot.cs | 71 ++++++++++++------- .../Compiler/TypeMapMetadata.cs | 8 +-- 2 files changed, 48 insertions(+), 31 deletions(-) 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 190a75a2d016bf..fdfbbed72317c9 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 @@ -57,7 +57,7 @@ public static IReadOnlyDictionary CreateExternalTypeMap(RuntimeTyp } } - return new Dictionary(); // Return an empty dictionary if no valid type map is found. + return EmptyExternalTypeMapDictionary.Instance; } public static IReadOnlyDictionary CreateProxyTypeMap(RuntimeType typeMapGroup) @@ -100,7 +100,7 @@ public static IReadOnlyDictionary CreateProxyTypeMap(RuntimeType typ } } - return new Dictionary(); // Return an empty dictionary if no valid type map is found. + return EmptyAssociatedTypeMapDictionary.Instance; } private static unsafe bool TryGetNativeReaderForBlob(TypeManagerHandle module, ReflectionMapBlob blob, out NativeReader reader) @@ -118,9 +118,23 @@ private static unsafe bool TryGetNativeReaderForBlob(TypeManagerHandle module, R return false; } - private sealed class ExternalTypeMapDictionary(NativeHashtable table, ExternalReferencesTable externalReferences) : IReadOnlyDictionary + private abstract class TypeMapDictionaryBase : IReadOnlyDictionary { - public Type this[string key] + public abstract Type this[TKey key] { get; } + public abstract bool TryGetValue(TKey key, [MaybeNullWhen(false)] out Type value); + // Not supported to avoid exposing TypeMap entries in a manner that + // would violate invariants the Trimmer is attempting to enforce. + public IEnumerable Keys => throw new NotSupportedException(); + public IEnumerable Values => throw new NotSupportedException(); + public int Count => throw new NotSupportedException(); + public bool ContainsKey(TKey key) => throw new NotSupportedException(); + public IEnumerator> GetEnumerator() => throw new NotSupportedException(); + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + } + + private sealed class ExternalTypeMapDictionary(NativeHashtable table, ExternalReferencesTable externalReferences) : TypeMapDictionaryBase + { + public override Type this[string key] { get { @@ -132,7 +146,7 @@ public Type this[string key] } } - public bool TryGetValue(string key, [MaybeNullWhen(false)] out Type value) + public override bool TryGetValue(string key, [MaybeNullWhen(false)] out Type value) { var lookup = table.Lookup(TypeHashingAlgorithms.ComputeNameHashCode(key)); NativeParser entryParser; @@ -149,23 +163,25 @@ public bool TryGetValue(string key, [MaybeNullWhen(false)] out Type value) value = null; return false; } + } - // Not supported to avoid exposing TypeMap entries in a manner that - // would violate invariants the Trimmer is attempting to enforce. - public IEnumerable Keys => throw new NotSupportedException(); - - public IEnumerable Values => throw new NotSupportedException(); + private sealed class EmptyExternalTypeMapDictionary : TypeMapDictionaryBase + { + internal static readonly EmptyExternalTypeMapDictionary Instance = new EmptyExternalTypeMapDictionary(); - public int Count => throw new NotSupportedException(); + private EmptyExternalTypeMapDictionary() { } - public bool ContainsKey(string key) => throw new NotSupportedException(); - public IEnumerator> GetEnumerator() => throw new NotSupportedException(); - IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + public override Type this[string key] => throw new KeyNotFoundException(key); + public override bool TryGetValue(string key, [MaybeNullWhen(false)] out Type value) + { + value = null; + return false; + } } - private sealed class AssociatedTypeMapDictionary(NativeHashtable table, ExternalReferencesTable externalReferences) : IReadOnlyDictionary + private sealed class AssociatedTypeMapDictionary(NativeHashtable table, ExternalReferencesTable externalReferences) : TypeMapDictionaryBase { - public Type this[Type key] + public override Type this[Type key] { get { @@ -177,7 +193,7 @@ public Type this[Type key] } } - public bool TryGetValue(Type key, [MaybeNullWhen(false)] out Type value) + public override bool TryGetValue(Type key, [MaybeNullWhen(false)] out Type value) { RuntimeTypeHandle handle = key.TypeHandle; if (handle.IsNull) @@ -201,18 +217,19 @@ public bool TryGetValue(Type key, [MaybeNullWhen(false)] out Type value) value = null; return false; } + } - // Not supported to avoid exposing TypeMap entries in a manner that - // would violate invariants the Trimmer is attempting to enforce. - public IEnumerable Keys => throw new NotSupportedException(); - - public IEnumerable Values => throw new NotSupportedException(); - - public int Count => throw new NotSupportedException(); + private sealed class EmptyAssociatedTypeMapDictionary : TypeMapDictionaryBase + { + internal static readonly EmptyAssociatedTypeMapDictionary Instance = new EmptyAssociatedTypeMapDictionary(); - public bool ContainsKey(Type key) => throw new NotSupportedException(); - public IEnumerator> GetEnumerator() => throw new NotSupportedException(); - IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + private EmptyAssociatedTypeMapDictionary() { } + public override Type this[Type key] => throw new KeyNotFoundException(key.ToString()!); + public override bool TryGetValue(Type key, [MaybeNullWhen(false)] out Type value) + { + value = null; + return false; + } } } } diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/TypeMapMetadata.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/TypeMapMetadata.cs index 2452be22e95cd3..4144a042732de8 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/TypeMapMetadata.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/TypeMapMetadata.cs @@ -68,7 +68,7 @@ public void AddExternalTypeMapEntry(string typeName, TypeDesc type, TypeDesc tri } } - public void SetExternalTypeMapStub(ModuleDesc stubModule, TypeSystemException exception) + public void SetExternalTypeMapException(ModuleDesc stubModule, TypeSystemException exception) { if (_externalTypeMapExceptionStub?.Exception is TypeSystemException.FileNotFoundException) { @@ -78,7 +78,7 @@ public void SetExternalTypeMapStub(ModuleDesc stubModule, TypeSystemException ex _externalTypeMapExceptionStub ??= new ThrowingMethodStub(stubModule.GetGlobalModuleType(), TypeMapGroup, externalTypeMap: true, exception); } - public void SetAssociatedTypeMapStub(ModuleDesc stubModule, TypeSystemException exception) + public void SetAssociatedTypeMapException(ModuleDesc stubModule, TypeSystemException exception) { if (_associatedTypeMapExceptionStub?.Exception is TypeSystemException.FileNotFoundException) { @@ -195,12 +195,12 @@ public static TypeMapMetadata CreateFromAssembly(EcmaAssembly assembly, Compiler if (attrKind is TypeMapAttributeKind.TypeMapAssemblyTarget or TypeMapAttributeKind.TypeMap) { - value.SetExternalTypeMapStub(typeSystemContext.GeneratedAssembly, ex); + value.SetExternalTypeMapException(typeSystemContext.GeneratedAssembly, ex); } if (attrKind is TypeMapAttributeKind.TypeMapAssemblyTarget or TypeMapAttributeKind.TypeMapAssociation) { - value.SetAssociatedTypeMapStub(typeSystemContext.GeneratedAssembly, ex); + value.SetAssociatedTypeMapException(typeSystemContext.GeneratedAssembly, ex); } } } From 680e8bcd4acb8923bd28017ac702a099f405564f Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Thu, 12 Jun 2025 12:23:20 -0700 Subject: [PATCH 40/45] Persist maps for all requests and throw an exception when no map is available. --- .../TypeMapLazyDictionary.NativeAot.cs | 32 ++------------ .../ILCompiler.Compiler/Compiler/ILScanner.cs | 20 ++++----- .../Compiler/UsageBasedTypeMapManager.cs | 44 ++++++++++++++++++- 3 files changed, 54 insertions(+), 42 deletions(-) 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 fdfbbed72317c9..9784f5d0a5b306 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 @@ -9,6 +9,7 @@ using System.Text; using Internal.NativeFormat; +using Internal.Reflection.Core.Execution; using Internal.Runtime; using Internal.Runtime.Augments; using Internal.Runtime.TypeLoader; @@ -57,7 +58,7 @@ public static IReadOnlyDictionary CreateExternalTypeMap(RuntimeTyp } } - return EmptyExternalTypeMapDictionary.Instance; + throw ReflectionCoreExecution.ExecutionEnvironment.CreateMissingMetadataException(typeMapGroup); } public static IReadOnlyDictionary CreateProxyTypeMap(RuntimeType typeMapGroup) @@ -100,7 +101,7 @@ public static IReadOnlyDictionary CreateProxyTypeMap(RuntimeType typ } } - return EmptyAssociatedTypeMapDictionary.Instance; + throw ReflectionCoreExecution.ExecutionEnvironment.CreateMissingMetadataException(typeMapGroup); } private static unsafe bool TryGetNativeReaderForBlob(TypeManagerHandle module, ReflectionMapBlob blob, out NativeReader reader) @@ -165,20 +166,6 @@ public override bool TryGetValue(string key, [MaybeNullWhen(false)] out Type va } } - private sealed class EmptyExternalTypeMapDictionary : TypeMapDictionaryBase - { - internal static readonly EmptyExternalTypeMapDictionary Instance = new EmptyExternalTypeMapDictionary(); - - private EmptyExternalTypeMapDictionary() { } - - public override Type this[string key] => throw new KeyNotFoundException(key); - public override bool TryGetValue(string key, [MaybeNullWhen(false)] out Type value) - { - value = null; - return false; - } - } - private sealed class AssociatedTypeMapDictionary(NativeHashtable table, ExternalReferencesTable externalReferences) : TypeMapDictionaryBase { public override Type this[Type key] @@ -218,18 +205,5 @@ public override bool TryGetValue(Type key, [MaybeNullWhen(false)] out Type value return false; } } - - private sealed class EmptyAssociatedTypeMapDictionary : TypeMapDictionaryBase - { - internal static readonly EmptyAssociatedTypeMapDictionary Instance = new EmptyAssociatedTypeMapDictionary(); - - private EmptyAssociatedTypeMapDictionary() { } - public override Type this[Type key] => throw new KeyNotFoundException(key.ToString()!); - public override bool TryGetValue(Type key, [MaybeNullWhen(false)] out Type value) - { - value = null; - return false; - } - } } } diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ILScanner.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ILScanner.cs index 549316953de55d..309772d6e5ef72 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ILScanner.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ILScanner.cs @@ -278,7 +278,7 @@ public ReadOnlyFieldPolicy GetReadOnlyFieldPolicy() public TypeMapManager GetTypeMapManager() { - return new ScannedTypeMapManager(_factory, MarkedNodes); + return new ScannedTypeMapManager(_factory); } private sealed class ScannedVTableProvider : VTableSliceProvider @@ -995,20 +995,18 @@ private sealed class ScannedTypeMapManager : TypeMapManager private ImmutableArray _externalTypeMapNodes; private ImmutableArray _proxyTypeMapNodes; - public ScannedTypeMapManager(NodeFactory factory, ImmutableArray> markedNodes) + public ScannedTypeMapManager(NodeFactory factory) { ImmutableArray.Builder externalTypeMapNodes = ImmutableArray.CreateBuilder(); ImmutableArray.Builder proxyTypeMapNodes = ImmutableArray.CreateBuilder(); - foreach (var node in markedNodes) + foreach (var externalTypeMapNode in factory.TypeMapManager.GetExternalTypeMaps()) { - if (node is IExternalTypeMapNode externalTypeMapNode) - { - externalTypeMapNodes.Add(externalTypeMapNode.ToAnalysisBasedNode(factory)); - } - else if (node is IProxyTypeMapNode proxyTypeMapNode) - { - proxyTypeMapNodes.Add(proxyTypeMapNode.ToAnalysisBasedNode(factory)); - } + externalTypeMapNodes.Add(externalTypeMapNode.ToAnalysisBasedNode(factory)); + } + + foreach (var proxyTypeMapNode in factory.TypeMapManager.GetProxyTypeMaps()) + { + proxyTypeMapNodes.Add(proxyTypeMapNode.ToAnalysisBasedNode(factory)); } _externalTypeMapNodes = externalTypeMapNodes.ToImmutable(); diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/UsageBasedTypeMapManager.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/UsageBasedTypeMapManager.cs index 9952f5ebc1bb78..54be1c847d7e6f 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/UsageBasedTypeMapManager.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/UsageBasedTypeMapManager.cs @@ -42,6 +42,8 @@ public override IEnumerable GetConditionalStaticDep protected override string GetName(NodeFactory context) => $"Type maps root node: {typeMapState.DiagnosticName}"; } + private readonly HashSet _requestedExternalTypeMaps = []; + private readonly HashSet _requestedProxyTypeMaps = []; private readonly SortedSet _externalTypeMaps = new SortedSet(CompilerComparer.Instance); private readonly SortedSet _proxyTypeMaps = new SortedSet(CompilerComparer.Instance); @@ -72,16 +74,54 @@ private void Graph_NewMarkedNode(DependencyNodeCore obj) { _proxyTypeMaps.Add(proxyTypeMapNode); } + + if (obj is ExternalTypeMapRequestNode externalTypeMapRequestNode) + { + _requestedExternalTypeMaps.Add(externalTypeMapRequestNode.TypeMapGroup); + } + + if (obj is ProxyTypeMapRequestNode proxyTypeMapRequestNode) + { + _requestedProxyTypeMaps.Add(proxyTypeMapRequestNode.TypeMapGroup); + } } internal override IEnumerable GetExternalTypeMaps() { - return _externalTypeMaps; + List typeMaps = [.._externalTypeMaps]; + SortedSet generatedMaps = new(TypeSystemComparer.Instance); + foreach (var generatedMap in typeMaps) + { + generatedMaps.Add(generatedMap.TypeMapGroup); + } + + SortedSet emptyMapsToGenerate = new SortedSet(_requestedExternalTypeMaps, TypeSystemComparer.Instance); + emptyMapsToGenerate.ExceptWith(generatedMaps); + + foreach (var emptyMap in emptyMapsToGenerate) + { + typeMaps.Add(new ExternalTypeMapNode(emptyMap, [])); + } + return typeMaps; } internal override IEnumerable GetProxyTypeMaps() { - return _proxyTypeMaps; + List typeMaps = [.. _proxyTypeMaps]; + SortedSet generatedMaps = new(TypeSystemComparer.Instance); + foreach (var generatedMap in typeMaps) + { + generatedMaps.Add(generatedMap.TypeMapGroup); + } + + SortedSet emptyMapsToGenerate = new SortedSet(_requestedProxyTypeMaps, TypeSystemComparer.Instance); + emptyMapsToGenerate.ExceptWith(generatedMaps); + + foreach (var emptyMap in emptyMapsToGenerate) + { + typeMaps.Add(new ProxyTypeMapNode(emptyMap, [])); + } + return typeMaps; } public override void AddCompilationRoots(IRootingServiceProvider rootProvider) From a31d9dc62d7958774fc6f112682ae2e5823e231a Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Fri, 13 Jun 2025 14:31:52 -0700 Subject: [PATCH 41/45] Fix indentation and revert change in NativeFormatWriter --- .../NativeFormat/NativeFormatWriter.cs | 14 ++---- .../TestCasesRunner/TrimmingDriver.cs | 46 +++++++++---------- 2 files changed, 26 insertions(+), 34 deletions(-) diff --git a/src/coreclr/tools/Common/Internal/NativeFormat/NativeFormatWriter.cs b/src/coreclr/tools/Common/Internal/NativeFormat/NativeFormatWriter.cs index e733aa41c03d6b..b7f1886da914c6 100644 --- a/src/coreclr/tools/Common/Internal/NativeFormat/NativeFormatWriter.cs +++ b/src/coreclr/tools/Common/Internal/NativeFormat/NativeFormatWriter.cs @@ -2,13 +2,12 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; -using System.Collections.Generic; -using System.Diagnostics; using System.IO; +using System.Diagnostics; +using System.Collections.Generic; using System.Linq; -using System.Numerics; using System.Text; -using System.Xml.Linq; +using System.Numerics; // Managed mirror of NativeFormatWriter.h/.cpp namespace Internal.NativeFormat @@ -390,13 +389,6 @@ public void Save(Stream stream) #endif } - if (_offsetAdjustment > 0) - { - _paddingSize += _offsetAdjustment; - WritePad(_offsetAdjustment); - - } - if (_offsetAdjustment == 0) { _encoder.Save(stream); diff --git a/src/coreclr/tools/aot/ILCompiler.Trimming.Tests/TestCasesRunner/TrimmingDriver.cs b/src/coreclr/tools/aot/ILCompiler.Trimming.Tests/TestCasesRunner/TrimmingDriver.cs index 0cce37ed12b222..a97f9638c5f8f0 100644 --- a/src/coreclr/tools/aot/ILCompiler.Trimming.Tests/TestCasesRunner/TrimmingDriver.cs +++ b/src/coreclr/tools/aot/ILCompiler.Trimming.Tests/TestCasesRunner/TrimmingDriver.cs @@ -118,34 +118,34 @@ public ILScanResults Trim (ILCompilerOptions options, TrimmingCustomizations? cu CompilerGeneratedState compilerGeneratedState = new CompilerGeneratedState (ilProvider, logger); UsageBasedMetadataManager metadataManager = new UsageBasedMetadataManager( - compilationGroup, - typeSystemContext, - new NoMetadataBlockingPolicy(), - new ManifestResourceBlockingPolicy(logger, options.FeatureSwitches, new Dictionary>()), - logFile: null, - stackTracePolicy: new NoStackTraceEmissionPolicy(), - invokeThunkGenerationPolicy: new DefaultDynamicInvokeThunkGenerationPolicy(), - flowAnnotations: new FlowAnnotations(logger, ilProvider, compilerGeneratedState), - generationOptions: UsageBasedMetadataGenerationOptions.ReflectionILScanning, - options: default, - logger: logger, - featureSwitchValues: options.FeatureSwitches, - rootEntireAssembliesModules: Array.Empty(), - additionalRootedAssemblies: options.AdditionalRootAssemblies.ToArray(), - trimmedAssemblies: options.TrimAssemblies.ToArray(), - satelliteAssemblyFilePaths: Array.Empty()); + compilationGroup, + typeSystemContext, + new NoMetadataBlockingPolicy(), + new ManifestResourceBlockingPolicy(logger, options.FeatureSwitches, new Dictionary>()), + logFile: null, + stackTracePolicy: new NoStackTraceEmissionPolicy(), + invokeThunkGenerationPolicy: new DefaultDynamicInvokeThunkGenerationPolicy(), + flowAnnotations: new FlowAnnotations(logger, ilProvider, compilerGeneratedState), + generationOptions: UsageBasedMetadataGenerationOptions.ReflectionILScanning, + options: default, + logger: logger, + featureSwitchValues: options.FeatureSwitches, + rootEntireAssembliesModules: Array.Empty(), + additionalRootedAssemblies: options.AdditionalRootAssemblies.ToArray(), + trimmedAssemblies: options.TrimAssemblies.ToArray(), + satelliteAssemblyFilePaths: Array.Empty()); PInvokeILEmitterConfiguration pinvokePolicy = new ILCompilerTestPInvokePolicy (); InteropStateManager interopStateManager = new InteropStateManager (typeSystemContext.GeneratedAssembly); InteropStubManager interopStubManager = new UsageBasedInteropStubManager (interopStateManager, pinvokePolicy, logger); - TypeMapManager typeMapManager = new UsageBasedTypeMapManager (TypeMapMetadata.Empty); - if (entrypointModule is { Assembly: EcmaAssembly entryAssembly }) - { - typeMapManager = new UsageBasedTypeMapManager (TypeMapMetadata.CreateFromAssembly(entryAssembly, typeSystemContext)); - } + TypeMapManager typeMapManager = new UsageBasedTypeMapManager (TypeMapMetadata.Empty); + if (entrypointModule is { Assembly: EcmaAssembly entryAssembly }) + { + typeMapManager = new UsageBasedTypeMapManager (TypeMapMetadata.CreateFromAssembly(entryAssembly, typeSystemContext)); + } - CompilationBuilder builder = new RyuJitCompilationBuilder (typeSystemContext, compilationGroup) + CompilationBuilder builder = new RyuJitCompilationBuilder (typeSystemContext, compilationGroup) .UseILProvider (ilProvider) .UseCompilationUnitPrefix(""); @@ -154,7 +154,7 @@ public ILScanResults Trim (ILCompilerOptions options, TrimmingCustomizations? cu .UseMetadataManager (metadataManager) .UseParallelism (System.Diagnostics.Debugger.IsAttached ? 1 : -1) .UseInteropStubManager (interopStubManager) - .UseTypeMapManager (typeMapManager) + .UseTypeMapManager (typeMapManager) .ToILScanner (); return scanner.Scan (); From 74c8a5acc1e1aed4f075f9b41941a8e82b26f881 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Mon, 16 Jun 2025 14:32:01 -0700 Subject: [PATCH 42/45] Add more interesting test cases. --- .../Reflection/TypeMap.cs | 57 +++++++++++++++++-- 1 file changed, 52 insertions(+), 5 deletions(-) diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/Reflection/TypeMap.cs b/src/tools/illink/test/Mono.Linker.Tests.Cases/Reflection/TypeMap.cs index 3976fc42ffb345..48bcc7f62dab0a 100644 --- a/src/tools/illink/test/Mono.Linker.Tests.Cases/Reflection/TypeMap.cs +++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/Reflection/TypeMap.cs @@ -11,8 +11,12 @@ [assembly: TypeMap ("TrimTargetIsTarget", typeof (TargetAndTrimTarget), typeof (TargetAndTrimTarget))] [assembly: TypeMap ("TrimTargetIsUnrelated", typeof (TargetType), typeof (TrimTarget))] +[assembly: TypeMap ("TrimTargetIsAllocatedNoTypeCheckClass", typeof (TargetType2), typeof (AllocatedNoTypeCheckClass))] +[assembly: TypeMap ("TrimTargetIsAllocatedNoTypeCheckStruct", typeof (TargetType3), typeof (AllocatedNoTypeCheckStruct))] [assembly: TypeMap ("TrimTargetIsUnreferenced", typeof (UnreferencedTargetType), typeof (UnreferencedTrimTarget))] [assembly: TypeMapAssociation (typeof (SourceClass), typeof (ProxyType))] +[assembly: TypeMapAssociation (typeof (TypeCheckOnlyClass), typeof (ProxyType2))] +[assembly: TypeMapAssociation (typeof (AllocatedNoBoxStructType), typeof (ProxyType3))] [assembly: TypeMapAssociation (typeof (I), typeof (IImpl))] [assembly: TypeMapAssociation (typeof (IInterfaceWithDynamicImpl), typeof (IDynamicImpl))] @@ -22,7 +26,7 @@ namespace Mono.Linker.Tests.Cases.Reflection { [Kept] - [IgnoreTestCase("Trimmer support is currently not implemented", IgnoredBy = Tool.Trimmer)] + [IgnoreTestCase ("Trimmer support is currently not implemented", IgnoredBy = Tool.Trimmer)] class TypeMap { [Kept] @@ -36,14 +40,23 @@ public static void Main (string[] args) Console.WriteLine ("Type deriving from TrimTarget instantiated."); } else if (t is IInterfaceWithDynamicImpl d) { d.Method (); + } else if (t is TypeCheckOnlyClass typeCheckOnlyClass) { + Console.WriteLine ("Type deriving from TypeCheckOnlyClass instantiated."); } Console.WriteLine ("Hash code of SourceClass instance: " + new SourceClass ().GetHashCode ()); Console.WriteLine ("Hash code of UsedClass instance: " + new UsedClass ().GetHashCode ()); + Console.WriteLine ("Hash code of AllocatedNoTypeCheckClass instance: " + new AllocatedNoTypeCheckClass ().GetHashCode ()); + Console.WriteLine ("Hash code of AllocatedNoTypeCheckStruct instance: " + new AllocatedNoTypeCheckStruct ().GetHashCode ()); Console.WriteLine (TypeMapping.GetOrCreateExternalTypeMapping ()); - Console.WriteLine (TypeMapping.GetOrCreateProxyTypeMapping ()); Console.WriteLine (GetExternalTypeMap ()); + + var proxyMap = TypeMapping.GetOrCreateProxyTypeMapping (); + + AllocatedNoBoxStructType allocatedNoBoxStructType = new AllocatedNoBoxStructType (Random.Shared.Next ()); + Console.WriteLine ("AllocatedNoBoxStructType value: " + allocatedNoBoxStructType.Value); + Console.WriteLine (proxyMap[typeof (AllocatedNoBoxStructType)]); } [Kept] @@ -54,7 +67,7 @@ private static IReadOnlyDictionary GetExternalTypeMap () } } - [Kept(By = Tool.Trimmer)] + [Kept (By = Tool.Trimmer)] class UsedTypeMap; [Kept] @@ -71,7 +84,7 @@ class UnreferencedTargetType; class UnreferencedTrimTarget; [Kept] - [KeptMember(".ctor()")] + [KeptMember (".ctor()")] class SourceClass; [Kept] @@ -96,7 +109,7 @@ interface IInterfaceWithDynamicImpl [Kept (By = Tool.Trimmer)] void Method (); } - + [Kept] [KeptInterface (typeof (IInterfaceWithDynamicImpl))] [KeptAttributeAttribute (typeof (DynamicInterfaceCastableImplementationAttribute), By = Tool.Trimmer)] @@ -108,6 +121,40 @@ void IInterfaceWithDynamicImpl.Method () { } } + + [Kept] + [KeptMember(".ctor()")] + class AllocatedNoTypeCheckClass; + + [Kept] + struct AllocatedNoTypeCheckStruct; + + [Kept] + class TargetType2; + + [Kept] + class TargetType3; + + [Kept] + class TypeCheckOnlyClass; + + class ProxyType2; + + [Kept] + struct AllocatedNoBoxStructType + { + [Kept] + public AllocatedNoBoxStructType (int value) + { + Value = value; + } + + [Kept] + public int Value { [Kept] get; } + } + + [Kept] + class ProxyType3; } // Polyfill for the type map types until we use an LKG runtime that has it. From 1571bf51b2f794f77f1057d24febafa9d946efa8 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Wed, 18 Jun 2025 18:06:48 +0000 Subject: [PATCH 43/45] Add a more in-depth spec at the IL level describing the type map feature --- docs/design/features/typemap.md | 223 ++++++++++++++++++++++++++++++++ 1 file changed, 223 insertions(+) create mode 100644 docs/design/features/typemap.md diff --git a/docs/design/features/typemap.md b/docs/design/features/typemap.md new file mode 100644 index 00000000000000..799761ad0c9e8e --- /dev/null +++ b/docs/design/features/typemap.md @@ -0,0 +1,223 @@ +# Interop Type Map + +## Background + +When interop between languages/platforms involves the projection of types, some kind of type mapping logic must often exist. This mapping mechanism is used to determine what .NET type should be used to project a type from language X and vice versa. + +The most common mechanism for this is the generation of a large look-up table at build time, which is then injected into the application or Assembly. If injected into the Assembly, there is typically some registration mechanism for the mapping data. Additional modifications and optimizations can be applied based on the user experience or scenarios constraints (that is, build time, execution environment limitations, etc). + +Prior to .NET 10 there were at least three (3) bespoke mechanisms for this in the .NET ecosystem: + +* C#/WinRT - [Built-in mappings](https://github.com/microsoft/CsWinRT/blob/master/src/WinRT.Runtime/Projections.CustomTypeMappings.tt), [Generation of vtables for AOT](https://github.com/microsoft/CsWinRT/blob/b1733e95c6d35b551fc8cf6fe04e2a0c287346dd/src/Authoring/WinRT.SourceGenerator/AotOptimizer.cs#L1597). + +* .NET For Android - [Assembly Store doc](https://github.com/dotnet/android/blob/main/Documentation/project-docs/AssemblyStores.md), [Assembly Store generator](https://github.com/dotnet/android/blob/main/src/Xamarin.Android.Build.Tasks/Utilities/TypeMappingReleaseNativeAssemblyGenerator.cs), [unmanaged Assembly Store types](https://github.com/dotnet/android/blob/main/src/native/xamarin-app-stub/xamarin-app.hh). + +* Objective-C - [Registrar](https://github.com/xamarin/xamarin-macios/blob/cee75657955e29981ded2fb0c6f0ee832db9a8d3/src/ObjCRuntime/Registrar.cs#L87), [Managed Static Registrar](https://github.com/xamarin/xamarin-macios/blob/main/docs/managed-static-registrar.md). + +## Priorties + +1) Trimmer friendly - AOT compatible. +2) Usable from both managed and unmanaged environments. +3) Low impact to application start-up and/or Assembly load. +4) Be composable - handle multiple type mappings. + +## APIs + +The below .NET APIs represents only part of the feature. The complete scenario would involve additional steps and tooling. + +**Provided by BCL (that is, NetCoreApp)** +```csharp +namespace System.Runtime.InteropServices; + +/// +/// Type mapping between a string and a type. +/// +/// Type universe +[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] +public sealed class TypeMapAttribute : Attribute +{ + /// + /// Create a mapping between a value and a . + /// + /// String representation of key + /// Type value + /// + /// This mapping is unconditionally inserted into the type map. + /// + public TypeMapAttribute(string value, Type target) + { } + + /// + /// Create a mapping between a value and a . + /// + /// String representation of key + /// Type value + /// Type used by Trimmer to determine type map inclusion. + /// + /// This mapping is only included in the type map if the Trimmer observes a type check + /// using the represented by . + /// + [RequiresUnreferencedCode("Interop types may be removed by trimming")] + public TypeMapAttribute(string value, Type target, Type trimTarget) + { } +} + +/// +/// Declare an assembly that should be inspected during type map building. +/// +/// Type universe +[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] +public sealed class TypeMapAssemblyTargetAttribute : Attribute +{ + /// + /// Provide the assembly to look for type mapping attributes. + /// + /// Assembly to reference + public TypeMapAssemblyTargetAttribute(string assemblyName) + { } +} + +/// +/// Create a type association between a type and its proxy. +/// +/// Type universe +[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = false)] +public sealed class TypeMapAssociationAttribute : Attribute +{ + /// + /// Create an association between two types in the type map. + /// + /// Target type. + /// Type to associated with . + /// + /// This mapping will only exist in the type map if the Trimmer observes + /// an allocation using the represented by . + /// + public TypeMapAssociationAttribute(Type source, Type proxy) + { } +} + +/// +/// Entry type for interop type mapping logic. +/// +public static class TypeMapping +{ + /// + /// Returns the External type type map generated for the current application. + /// + /// Type universe + /// Requested type map + /// True if the map is returned, otherwise false. + /// + /// Call sites are treated as an intrinsic by the Trimmer and implemented inline. + /// + [RequiresUnreferencedCode("Interop types may be removed by trimming")] + public static IReadOnlyDictionary GetOrCreateExternalTypeMapping(); + + /// + /// Returns the associated type type map generated for the current application. + /// + /// Type universe + /// Requested type map + /// True if the map is returned, otherwise false. + /// + /// Call sites are treated as an intrinsic by the Trimmer and implemented inline. + /// + [RequiresUnreferencedCode("Interop types may be removed by trimming")] + public static IReadOnlyDictionary GetOrCreateProxyTypeMapping(); +} +``` + +Given the above types the following would take place. + +1. Types involved in unmanaged-to-managed interop operations would be referenced in a +`TypeMapAttribute` assembly attribute that declared the external type system name, a target +type, and optionally a "trim-target" to determine if the target +type should be included in the map. If the `TypeMapAttribute` constructor that doesn't +take a trim-target is used, the "target type" will be treated as the "trim-target". + +2. Types used in a managed-to-unmanaged interop operation would use `TypeMapAssociationAttribute` +to define a conditional link between the source and proxy type. In other words, if the +source is kept, so is the proxy type. If Trimmer observes an explicit allocation of the source +type, the entry will be inserted into the map. + +3. During application build, source would be generated and injected into the application +that defines appropriate `TypeMapAssemblyTargetAttribute` instances. This attribute would help the +Trimmer know other assemblies to examine for `TypeMapAttribute` and `TypeMapAssociationAttribute` +instances. These linked assemblies could also be used in the non-Trimmed scenario whereby we +avoid creating the map at build-time and create a dynamic map at run-time instead. + +4. The Trimmer will build two maps based on the above attributes from the application reference +closure. + + **(a)** Using `TypeMapAttribute` a map from `string` to target `Type`. + + **(b)** Using `TypeMapAssociationAttribute` a map from `Type` to `Type` (source to proxy). + +> [!IMPORTANT] +> Conflicting key/value mappings are not allowed. + +> [!NOTE] +> The underlying format of the produced maps is implementation-defined. Different .NET form factors may use different formats. +> +> Additionally, it is not guaranteed that the `TypeMapAttribute`, `TypeMapAssociationAttribute`, and `TypeMapAssemblyTargetAttribute` attributes are present in the final image after a trimming tool has been run. + + +5. Trimming tools will consider calls to `TypeMapping.GetOrCreateExternalTypeMapping<>` and +`TypeMapping.GetOrCreateProxyTypeMapping<>` as intrinsics (for example, Java via `JavaTypeMapGroup`). As a result, it is not trim-compatible to call either of these methods with non-fully-instantiated generic (such as a type argument or a type that is instantiated over a type argument). + +## Type Map entry trimming rules + +This section provides the minimum rules for entries to be included in a given type map by a trimming tool (ie. ILLink or NativeAOT). Due to restrictions in some form factors, some trimming tools may include more entries than would be included based on the rules described below. + +The following rules only apply to code that is considered "reachable" from the entry-point method. Code that a trimming tool determines is unreachable does not contribute to determining if a type map entry is preserved. + +### Type Map Assembly Target probing + +The process of building type maps starts at the entry-point method of the app (the `Main` method). The initial entries for the type maps are collected from the assembly containing the entry-point for the app. From that assembly, any assembly names that are mentioned in a `TypeMapAssemblyTargetAttribute` are scanned. This process then repeats for those assemblies until all assemblies transitively referenced by `TypeMapAssemblyTargetAttribute`s have been scanned. + +An assembly name mentioned in the `TypeMapAssemblyTargetAttribute` does not need to map to an `AssemblyRef` row in the module's metadata. As long as a given name can be resolved by the runtime or by whatever trimming tool is run on the application, it can be used. + +### External Type Map + +An entry in an External Type Map is included when the "trim target" type is referenced in one of the following ways: + +- The argument to the `ldtoken` IL instruction. +- The argument to the `unbox` IL instruction. +- The argument to the `unbox.any` IL instruction. +- The argument to the `isinst` IL instruction. +- The argument to the `castclass` IL instruction. +- The argument to the `box` instruction. +- The argument to the `mkrefany` instruction. +- The argument to the `refanyval` instruction. +- The argument to the `newarr` instruction. +- The argument to the `ldobj` instruction. +- The argument to the `stobj` instruction. +- The argument to the `.constrained` instruction prefix. +- The type of a method argument to the `newobj` instruction. +- The owning type of the method argument to `call`, `callvirt`, `ldftn`, or `ldvirtftn`. + - If the owning type is an interface and the trimming tool can determine that there is only one implementation of the interface, it is free to interpret the method token argument as though it is the method on the only implementing type. +- The generic argument to the `Activator.CreateInstance` method. +- Calls to `Type.GetType` with a constant string representing the type name. + +Many of these instructions that can be passed a generic parameter. In that case, the trimming tool should consider type arguments of instantiations of that type as having met one of these rules and include any entries with those types as "trim target" types. + +### Proxy Type Map + +An entry in the Proxy Type Map is included when the "source type" is referenced in one of the following ways: + +- The argument to the `ldtoken` IL instruction when `DynamicallyAccessedMembersAttribute` is specified with one of the flags that preserves constructors for the storage location. +- Calls to `Type.GetType` with a constant string representing the type name when `DynamicallyAccessedMembersAttribute` is specified with one of the flags that preserves constructors for the storage location. +- The type of a method argument to the `newobj` instruction. +- The generic argument to the `Activator.CreateInstance` method. +- The argument to the `box` instruction. +- The argument to the `newarr` instruction. +- The argument to the `.constrained` instruction prefix. +- The argument to the `mkrefany` instruction. +- The argument to the `refanyval` instruction. + +If the type is an interface type and the user could possibly see a `RuntimeTypeHandle` for the type as part of a casting or virtual method resolution operation (such as with `System.Runtime.InteropServices.IDynamicInterfaceCastable`), then the following cases also apply: + +- The argument to the `isinst` IL instruction. +- The argument to the `castclass` IL instruction. +- The owning type of the method argument to `callvirt`, or `ldvirtftn`. From 41a894b09ebfc7dd856355a1dc09b86de0741216 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Wed, 18 Jun 2025 13:37:27 -0700 Subject: [PATCH 44/45] PR feedback --- docs/design/features/typemap.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/design/features/typemap.md b/docs/design/features/typemap.md index 799761ad0c9e8e..6501b3b0b33b8a 100644 --- a/docs/design/features/typemap.md +++ b/docs/design/features/typemap.md @@ -8,11 +8,11 @@ The most common mechanism for this is the generation of a large look-up table at Prior to .NET 10 there were at least three (3) bespoke mechanisms for this in the .NET ecosystem: -* C#/WinRT - [Built-in mappings](https://github.com/microsoft/CsWinRT/blob/master/src/WinRT.Runtime/Projections.CustomTypeMappings.tt), [Generation of vtables for AOT](https://github.com/microsoft/CsWinRT/blob/b1733e95c6d35b551fc8cf6fe04e2a0c287346dd/src/Authoring/WinRT.SourceGenerator/AotOptimizer.cs#L1597). +* C#/WinRT - [Built-in mappings](https://github.com/microsoft/CsWinRT/b1733e95c6d35b551fc8cf6fe04e2a0c287346dd/master/src/WinRT.Runtime/Projections.CustomTypeMappings.tt), [Generation of vtables for AOT](https://github.com/microsoft/CsWinRT/blob/b1733e95c6d35b551fc8cf6fe04e2a0c287346dd/src/Authoring/WinRT.SourceGenerator/AotOptimizer.cs#L1597). -* .NET For Android - [Assembly Store doc](https://github.com/dotnet/android/blob/main/Documentation/project-docs/AssemblyStores.md), [Assembly Store generator](https://github.com/dotnet/android/blob/main/src/Xamarin.Android.Build.Tasks/Utilities/TypeMappingReleaseNativeAssemblyGenerator.cs), [unmanaged Assembly Store types](https://github.com/dotnet/android/blob/main/src/native/xamarin-app-stub/xamarin-app.hh). +* .NET For Android - [Assembly Store doc](https://github.com/dotnet/android/blob/b8d0669e951d683443c19ecac06dc96363791820/Documentation/project-docs/AssemblyStores.md), [Assembly Store generator](https://github.com/dotnet/android/blob/b8d0669e951d683443c19ecac06dc96363791820/src/Xamarin.Android.Build.Tasks/Utilities/TypeMappingReleaseNativeAssemblyGenerator.cs), [unmanaged Assembly Store types](https://github.com/dotnet/android/blob/b8d0669e951d683443c19ecac06dc96363791820/src/native/xamarin-app-stub/xamarin-app.hh). -* Objective-C - [Registrar](https://github.com/xamarin/xamarin-macios/blob/cee75657955e29981ded2fb0c6f0ee832db9a8d3/src/ObjCRuntime/Registrar.cs#L87), [Managed Static Registrar](https://github.com/xamarin/xamarin-macios/blob/main/docs/managed-static-registrar.md). +* Objective-C - [Registrar](https://github.com/dotnet/macios/blob/cee75657955e29981ded2fb0c6f0ee832db9a8d3/src/ObjCRuntime/Registrar.cs#L87), [Managed Static Registrar](https://github.com/dotnet/macios/blob/cee75657955e29981ded2fb0c6f0ee832db9a8d3/docs/managed-static-registrar.md). ## Priorties @@ -138,7 +138,7 @@ take a trim-target is used, the "target type" will be treated as the "trim-targe 2. Types used in a managed-to-unmanaged interop operation would use `TypeMapAssociationAttribute` to define a conditional link between the source and proxy type. In other words, if the -source is kept, so is the proxy type. If Trimmer observes an explicit allocation of the source +source is kept, so is the proxy type. If the Trimmer observes an explicit allocation of the source type, the entry will be inserted into the map. 3. During application build, source would be generated and injected into the application @@ -200,7 +200,7 @@ An entry in an External Type Map is included when the "trim target" type is refe - The generic argument to the `Activator.CreateInstance` method. - Calls to `Type.GetType` with a constant string representing the type name. -Many of these instructions that can be passed a generic parameter. In that case, the trimming tool should consider type arguments of instantiations of that type as having met one of these rules and include any entries with those types as "trim target" types. +Many of these instructions can be passed a generic parameter. In that case, the trimming tool should consider type arguments of instantiations of that type as having met one of these rules and include any entries with those types as "trim target" types. ### Proxy Type Map From e45a0267629867869b30f94372ef0a8b9b06e639 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Thu, 19 Jun 2025 16:19:23 -0700 Subject: [PATCH 45/45] Cleanup based on primary constructor guidance --- .../AnalyzedExternalTypeMapNode.cs | 32 +++++++++--------- .../AnalyzedProxyTypeMapNode.cs | 33 +++++++++---------- .../ExternalTypeMapObjectNode.cs | 32 +++++++++--------- .../DependencyAnalysis/ProxyTypeMapNode.cs | 16 ++++++--- .../ProxyTypeMapObjectNode.cs | 32 +++++++++--------- .../Compiler/TypeMapMetadata.cs | 29 ++++++++++++---- 6 files changed, 98 insertions(+), 76 deletions(-) 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 ea35be180b45d7..b62b3da716dc28 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/AnalyzedExternalTypeMapNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/AnalyzedExternalTypeMapNode.cs @@ -12,23 +12,8 @@ namespace ILCompiler.DependencyAnalysis { internal sealed class AnalyzedExternalTypeMapNode(TypeDesc typeMapGroup, IReadOnlyDictionary entries) : DependencyNodeCore, IExternalTypeMapNode { - public override bool InterestingForDynamicDependencyAnalysis => false; - - public override bool HasDynamicDependencies => false; - - public override bool HasConditionalStaticDependencies => false; - - public override bool StaticDependenciesAreComputed => true; - public TypeDesc TypeMapGroup => typeMapGroup; - public int ClassCode => -874354558; - - public int CompareToImpl(ISortableNode other, CompilerComparer comparer) - { - AnalyzedExternalTypeMapNode otherEntry = (AnalyzedExternalTypeMapNode)other; - return comparer.Compare(TypeMapGroup, otherEntry.TypeMapGroup); - } public Vertex CreateTypeMap(NodeFactory factory, NativeWriter writer, Section section, ExternalReferencesTableNode externalReferences) { VertexHashtable typeMapHashTable = new(); @@ -56,7 +41,22 @@ public override IEnumerable GetStaticDependencies(NodeFacto } } public override IEnumerable SearchDynamicDependencies(List> markedNodes, int firstNode, NodeFactory context) => []; + protected override string GetName(NodeFactory context) => $"Analyzed External Type Map: {TypeMapGroup}"; public IExternalTypeMapNode ToAnalysisBasedNode(NodeFactory factory) => this; - protected override string GetName(NodeFactory context) => $"Analyzed External Type Map: {typeMapGroup}"; + + public override bool InterestingForDynamicDependencyAnalysis => false; + + public override bool HasDynamicDependencies => false; + + public override bool HasConditionalStaticDependencies => false; + + public override bool StaticDependenciesAreComputed => true; + public int ClassCode => -874354558; + + public 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 ddcb724977ede6..7d19848d23ebb0 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/AnalyzedProxyTypeMapNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/AnalyzedProxyTypeMapNode.cs @@ -13,23 +13,7 @@ namespace ILCompiler.DependencyAnalysis { internal sealed class AnalyzedProxyTypeMapNode(TypeDesc typeMapGroup, IReadOnlyDictionary entries) : DependencyNodeCore, IProxyTypeMapNode { - public override bool InterestingForDynamicDependencyAnalysis => false; - - public override bool HasDynamicDependencies => false; - - public override bool HasConditionalStaticDependencies => false; - - public override bool StaticDependenciesAreComputed => true; - public TypeDesc TypeMapGroup => typeMapGroup; - - public int ClassCode => 171742984; - - public int CompareToImpl(ISortableNode other, CompilerComparer comparer) - { - AnalyzedProxyTypeMapNode otherEntry = (AnalyzedProxyTypeMapNode)other; - return comparer.Compare(TypeMapGroup, otherEntry.TypeMapGroup); - } public Vertex CreateTypeMap(NodeFactory factory, NativeWriter writer, Section section, ExternalReferencesTableNode externalReferences) { VertexHashtable typeMapHashTable = new(); @@ -58,7 +42,22 @@ public override IEnumerable GetStaticDependencies(NodeFacto } } public override IEnumerable SearchDynamicDependencies(List> markedNodes, int firstNode, NodeFactory context) => []; - public IProxyTypeMapNode ToAnalysisBasedNode(NodeFactory factory) => this; protected override string GetName(NodeFactory context) => $"Analyzed Proxy Type Map: {typeMapGroup}"; + public IProxyTypeMapNode ToAnalysisBasedNode(NodeFactory factory) => this; + public override bool InterestingForDynamicDependencyAnalysis => false; + + public override bool HasDynamicDependencies => false; + + public override bool HasConditionalStaticDependencies => false; + + public override bool StaticDependenciesAreComputed => true; + + public int ClassCode => 171742984; + + public 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/ExternalTypeMapObjectNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ExternalTypeMapObjectNode.cs index b25753d27b0dc3..8f72dee452374f 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ExternalTypeMapObjectNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ExternalTypeMapObjectNode.cs @@ -14,22 +14,6 @@ namespace ILCompiler.DependencyAnalysis { internal sealed class ExternalTypeMapObjectNode(ExternalReferencesTableNode externalReferences) : ObjectNode, ISymbolDefinitionNode, INodeWithSize { - public void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) - { - sb.Append(nameMangler.CompilationUnitPrefix).Append("__external_type_map__"u8); - } - - public int Size { get; private set; } - public int Offset => 0; - public override bool IsShareable => false; - public override ObjectNodeSection GetSection(NodeFactory factory) => externalReferences.GetSection(factory); - protected internal override int Phase => (int)ObjectNodePhase.Ordered; - - public override int ClassCode => (int)ObjectNodeOrder.ExternalTypeMapObjectNode; - - public override bool StaticDependenciesAreComputed => true; - - protected override string GetName(NodeFactory context) => "External Type Map Hash Table"; public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) { // This node does not trigger generation of other nodes. @@ -53,5 +37,21 @@ public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) return new ObjectData(hashTableBytes, Array.Empty(), 1, [this]); } + public void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) + { + sb.Append(nameMangler.CompilationUnitPrefix).Append("__external_type_map__"u8); + } + + public int Size { get; private set; } + public int Offset => 0; + public override bool IsShareable => false; + public override ObjectNodeSection GetSection(NodeFactory factory) => externalReferences.GetSection(factory); + protected internal override int Phase => (int)ObjectNodePhase.Ordered; + + public override int ClassCode => (int)ObjectNodeOrder.ExternalTypeMapObjectNode; + + public override bool StaticDependenciesAreComputed => true; + + protected override string GetName(NodeFactory context) => "External Type Map Hash Table"; } } 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 fd966b679b17bc..a131677a776be6 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ProxyTypeMapNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ProxyTypeMapNode.cs @@ -13,11 +13,19 @@ namespace ILCompiler.DependencyAnalysis { - internal sealed class ProxyTypeMapNode(TypeDesc typeMapGroup, IEnumerable> mapEntries) : DependencyNodeCore, IProxyTypeMapNode + internal sealed class ProxyTypeMapNode : DependencyNodeCore, IProxyTypeMapNode { - public TypeDesc TypeMapGroup { get; } = typeMapGroup; + private readonly IEnumerable> _mapEntries; - public IEnumerable> MapEntries => mapEntries; + public ProxyTypeMapNode(TypeDesc typeMapGroup, IEnumerable> mapEntries) + { + _mapEntries = mapEntries; + TypeMapGroup = typeMapGroup; + } + + public TypeDesc TypeMapGroup { get; } + + public IEnumerable> MapEntries => _mapEntries; public override bool InterestingForDynamicDependencyAnalysis => false; public override bool HasDynamicDependencies => false; @@ -32,7 +40,7 @@ internal sealed class ProxyTypeMapNode(TypeDesc typeMapGroup, IEnumerable GetConditionalStaticDependencies(NodeFactory context) { - foreach (var (key, value) in mapEntries) + foreach (var (key, value) in _mapEntries) { yield return new CombinedDependencyListEntry( context.MaximallyConstructableType(value), diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ProxyTypeMapObjectNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ProxyTypeMapObjectNode.cs index ee8c7f05cfae3d..cdca8804ba5a80 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ProxyTypeMapObjectNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ProxyTypeMapObjectNode.cs @@ -15,22 +15,6 @@ namespace ILCompiler.DependencyAnalysis { internal sealed class ProxyTypeMapObjectNode(ExternalReferencesTableNode externalReferences) : ObjectNode, ISymbolDefinitionNode, INodeWithSize { - public void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) - { - sb.Append(nameMangler.CompilationUnitPrefix).Append("__proxy_type_map__"u8); - } - - public int Size { get; private set; } - public int Offset => 0; - public override bool IsShareable => false; - public override ObjectNodeSection GetSection(NodeFactory factory) => externalReferences.GetSection(factory); - protected internal override int Phase => (int)ObjectNodePhase.Ordered; - - public override int ClassCode => (int)ObjectNodeOrder.ProxyTypeMapObjectNode; - - public override bool StaticDependenciesAreComputed => true; - - protected override string GetName(NodeFactory context) => "Proxy Type Map Hash Table"; public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) { // This node does not trigger generation of other nodes. @@ -55,5 +39,21 @@ public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) return new ObjectData(hashTableBytes, Array.Empty(), 1, [this]); } + public void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) + { + sb.Append(nameMangler.CompilationUnitPrefix).Append("__proxy_type_map__"u8); + } + + public int Size { get; private set; } + public int Offset => 0; + public override bool IsShareable => false; + public override ObjectNodeSection GetSection(NodeFactory factory) => externalReferences.GetSection(factory); + protected internal override int Phase => (int)ObjectNodePhase.Ordered; + + public override int ClassCode => (int)ObjectNodeOrder.ProxyTypeMapObjectNode; + + public override bool StaticDependenciesAreComputed => true; + + protected override string GetName(NodeFactory context) => "Proxy Type Map Hash Table"; } } diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/TypeMapMetadata.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/TypeMapMetadata.cs index 4144a042732de8..4d2c597ee2447e 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/TypeMapMetadata.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/TypeMapMetadata.cs @@ -17,12 +17,22 @@ namespace ILCompiler { public sealed class TypeMapMetadata { - internal sealed class Map(TypeDesc typeMapGroup) + internal sealed class Map { - private sealed class ThrowingMethodStub(TypeDesc owningType, TypeDesc typeMapGroup, bool externalTypeMap, TypeSystemException ex) : ILStubMethod + private sealed class ThrowingMethodStub : ILStubMethod { - public TypeSystemException Exception => ex; - public override string Name => $"InvalidTypeMapStub_{typeMapGroup}_{(externalTypeMap ? "External" : "Proxy")}"; + private readonly TypeDesc _typeMapGroup; + + public ThrowingMethodStub(TypeDesc owningType, TypeDesc typeMapGroup, bool externalTypeMap, TypeSystemException ex) + { + OwningType = owningType; + _typeMapGroup = typeMapGroup; + Name = $"InvalidTypeMapStub_{_typeMapGroup}_{(externalTypeMap ? "External" : "Proxy")}"; + Exception = ex; + } + + public TypeSystemException Exception { get; } + public override string Name { get; } public override MethodIL EmitIL() { return TypeSystemThrowingILEmitter.EmitIL(this, Exception); @@ -39,11 +49,11 @@ protected override int CompareToImpl(MethodDesc other, TypeSystemComparer compar protected override int ClassCode => 1744789196; - public override TypeDesc OwningType => owningType; + public override TypeDesc OwningType { get; } public override MethodSignature Signature => new MethodSignature(MethodSignatureFlags.Static, 0, Context.GetWellKnownType(WellKnownType.Void), []); - public override TypeSystemContext Context => owningType.Context; + public override TypeSystemContext Context => OwningType.Context; } private readonly Dictionary _associatedTypeMap = []; @@ -51,7 +61,12 @@ protected override int CompareToImpl(MethodDesc other, TypeSystemComparer compar private ThrowingMethodStub _externalTypeMapExceptionStub; private ThrowingMethodStub _associatedTypeMapExceptionStub; - public TypeDesc TypeMapGroup { get; } = typeMapGroup; + public Map(TypeDesc typeMapGroup) + { + TypeMapGroup = typeMapGroup; + } + + public TypeDesc TypeMapGroup { get; } public void AddAssociatedTypeMapEntry(TypeDesc type, TypeDesc associatedType) {