diff --git a/src/tools/illink/src/linker/Linker.Steps/DependencyNodes/EdgeVisitor.cs b/src/tools/illink/src/linker/Linker.Steps/DependencyNodes/EdgeVisitor.cs new file mode 100644 index 00000000000000..a85d5d4f033070 --- /dev/null +++ b/src/tools/illink/src/linker/Linker.Steps/DependencyNodes/EdgeVisitor.cs @@ -0,0 +1,60 @@ +// 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.Diagnostics; +using ILCompiler.DependencyAnalysisFramework; +using Mono.Linker.Steps; + +namespace Mono.Linker.Steps +{ + public partial class MarkStep + { + internal sealed class EdgeVisitor : IDependencyAnalyzerLogEdgeVisitor + { + MarkStep _markStep; + public EdgeVisitor (MarkStep markStep) => _markStep = markStep; + + private bool ShouldBeLogged (DependencyNodeCore o) + { + if (o is ProcessCallbackNode) + return false; + + if (!_markStep.Context.EnableReducedTracing) + return true; + + var context = _markStep.Context; + + if (o is TypeDefinitionNode t) + return DependencyRecorderHelper.WillAssemblyBeModified (context, t.Type.Module.Assembly); + + return true; + } + + public void VisitEdge (DependencyNodeCore nodeDepender, DependencyNodeCore nodeDependedOn, string reason) + { + if (!(ShouldBeLogged (nodeDependedOn) && ShouldBeLogged (nodeDepender))) + return; + var dependerName = DependencyNodeCore.GetNodeName (nodeDepender, null!); + var dependeeName = DependencyNodeCore.GetNodeName (nodeDependedOn, null!); + DependencyInfo depInfo = new (NodeFactory.StringToDependencyKindMap[reason], dependerName); + _markStep.Context.Tracer.AddDirectDependency (dependeeName!, depInfo, true); + } + + public void VisitEdge (string root, DependencyNodeCore dependedOn) + { + // Root nodes will be traced in MarkStep.Mark[Type|Method|Field] and not here + } + + public void VisitEdge (DependencyNodeCore nodeDepender, DependencyNodeCore nodeDependerOther, DependencyNodeCore nodeDependedOn, string reason) + { + if (!(ShouldBeLogged (nodeDependedOn) && ShouldBeLogged (nodeDepender))) + return; + + var dependerName = DependencyNodeCore.GetNodeName (nodeDepender, null!); + var dependeeName = DependencyNodeCore.GetNodeName (nodeDependedOn, null!); + DependencyInfo depInfo = new (NodeFactory.StringToDependencyKindMap[reason], dependerName); + _markStep.Context.Tracer.AddDirectDependency (dependeeName!, depInfo, true); + } + } + } +} diff --git a/src/tools/illink/src/linker/Linker.Steps/MarkStep.MethodDefinitionNode.cs b/src/tools/illink/src/linker/Linker.Steps/DependencyNodes/MethodDefinitionNode.cs similarity index 63% rename from src/tools/illink/src/linker/Linker.Steps/MarkStep.MethodDefinitionNode.cs rename to src/tools/illink/src/linker/Linker.Steps/DependencyNodes/MethodDefinitionNode.cs index 34dd6e8a8d03e4..e5c0af784011e5 100644 --- a/src/tools/illink/src/linker/Linker.Steps/MarkStep.MethodDefinitionNode.cs +++ b/src/tools/illink/src/linker/Linker.Steps/DependencyNodes/MethodDefinitionNode.cs @@ -31,13 +31,24 @@ public MethodDefinitionNode (MethodDefinition method, DependencyInfo reason) public override IEnumerable? GetStaticDependencies (NodeFactory context) { + var methodOrigin = new MessageOrigin (method); + if (method.HasMetadataParameters ()) { +#pragma warning disable RS0030 // MethodReference.Parameters is banned. It's easiest to leave the code as is for now + foreach (ParameterDefinition pd in method.Parameters) { + var type = context.MarkStep.MarkType (pd.ParameterType, new DependencyInfo (DependencyKind.ParameterType, method), methodOrigin, false); + if (type is not null) + yield return new (context.GetTypeNode (type), nameof (DependencyKind.ParameterType)); + context.MarkStep.MarkCustomAttributes (pd, new DependencyInfo (DependencyKind.ParameterAttribute, method), methodOrigin); + context.MarkStep.MarkMarshalSpec (pd, new DependencyInfo (DependencyKind.ParameterMarshalSpec, method), methodOrigin); + } +#pragma warning restore RS0030 + } context.MarkStep.ProcessMethod (method, reason); - return null; } public override IEnumerable? GetConditionalStaticDependencies (NodeFactory context) => null; public override IEnumerable? SearchDynamicDependencies (List> markedNodes, int firstNode, NodeFactory context) => null; - protected override string GetName (NodeFactory context) => method.GetDisplayName(); + protected override string GetName (NodeFactory context) => method.GetDisplayName (); } } } diff --git a/src/tools/illink/src/linker/Linker.Steps/MarkStep.NodeFactory.cs b/src/tools/illink/src/linker/Linker.Steps/DependencyNodes/NodeFactory.cs similarity index 83% rename from src/tools/illink/src/linker/Linker.Steps/MarkStep.NodeFactory.cs rename to src/tools/illink/src/linker/Linker.Steps/DependencyNodes/NodeFactory.cs index 61d1e806209f43..8e4e32a573f891 100644 --- a/src/tools/illink/src/linker/Linker.Steps/MarkStep.NodeFactory.cs +++ b/src/tools/illink/src/linker/Linker.Steps/DependencyNodes/NodeFactory.cs @@ -2,8 +2,8 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. using System; -using System.Collections.Concurrent; using System.Collections.Generic; +using System.Collections.Immutable; using Mono.Cecil; namespace Mono.Linker.Steps @@ -13,9 +13,16 @@ public partial class MarkStep internal sealed class NodeFactory (MarkStep markStep) { public MarkStep MarkStep { get; } = markStep; - readonly NodeCache _typeNodes = new (static t => new TypeDefinitionNode(t)); + + public static readonly ImmutableDictionary StringToDependencyKindMap = Enum.GetValues ().ToImmutableDictionary (v => v.ToString ()); + public static readonly ImmutableDictionary DependencyKindToStringMap = Enum.GetValues ().ToImmutableDictionary (v => v, v => v.ToString ()); + + readonly NodeCache _typeNodes = new (static t => new TypeDefinitionNode (t)); + readonly NodeCache _methodNodes = new (static _ => throw new InvalidOperationException ("Creation of node requires more than the key.")); + readonly NodeCache _propertyNodes = new (static p => new PropertyDefinitionNode(p)); + readonly NodeCache _typeIsRelevantToVariantCastingNodes = new (static (t) => new TypeIsRelevantToVariantCastingNode (t)); internal TypeDefinitionNode GetTypeNode (TypeDefinition definition) diff --git a/src/tools/illink/src/linker/Linker.Steps/MarkStep.ProcessCallbackNode.cs b/src/tools/illink/src/linker/Linker.Steps/DependencyNodes/ProcessCallbackNode.cs similarity index 100% rename from src/tools/illink/src/linker/Linker.Steps/MarkStep.ProcessCallbackNode.cs rename to src/tools/illink/src/linker/Linker.Steps/DependencyNodes/ProcessCallbackNode.cs diff --git a/src/tools/illink/src/linker/Linker.Steps/MarkStep.TypeDefinitionNode.cs b/src/tools/illink/src/linker/Linker.Steps/DependencyNodes/TypeDefinitionNode.cs similarity index 95% rename from src/tools/illink/src/linker/Linker.Steps/MarkStep.TypeDefinitionNode.cs rename to src/tools/illink/src/linker/Linker.Steps/DependencyNodes/TypeDefinitionNode.cs index d26467930afce0..6e38aa2a21af6b 100644 --- a/src/tools/illink/src/linker/Linker.Steps/MarkStep.TypeDefinitionNode.cs +++ b/src/tools/illink/src/linker/Linker.Steps/DependencyNodes/TypeDefinitionNode.cs @@ -12,6 +12,7 @@ public partial class MarkStep internal sealed class TypeDefinitionNode : DependencyNodeCore { readonly TypeDefinition type; + public TypeDefinition Type => type; public TypeDefinitionNode (TypeDefinition type) { @@ -34,7 +35,7 @@ public TypeDefinitionNode (TypeDefinition type) public override IEnumerable? GetConditionalStaticDependencies (NodeFactory context) => null; public override IEnumerable? SearchDynamicDependencies (List> markedNodes, int firstNode, NodeFactory context) => null; - protected override string GetName (NodeFactory context) => type.GetDisplayName(); + protected override string GetName (NodeFactory context) => type.GetDisplayName (); } } } diff --git a/src/tools/illink/src/linker/Linker.Steps/MarkStep.TypeIsRelevantToVariantCastingNode.cs b/src/tools/illink/src/linker/Linker.Steps/DependencyNodes/TypeIsRelevantToVariantCastingNode.cs similarity index 96% rename from src/tools/illink/src/linker/Linker.Steps/MarkStep.TypeIsRelevantToVariantCastingNode.cs rename to src/tools/illink/src/linker/Linker.Steps/DependencyNodes/TypeIsRelevantToVariantCastingNode.cs index 00f790b5d9d472..c07fcabeaa53cc 100644 --- a/src/tools/illink/src/linker/Linker.Steps/MarkStep.TypeIsRelevantToVariantCastingNode.cs +++ b/src/tools/illink/src/linker/Linker.Steps/DependencyNodes/TypeIsRelevantToVariantCastingNode.cs @@ -29,7 +29,7 @@ internal sealed class TypeIsRelevantToVariantCastingNode : DependencyNodeCore? GetConditionalStaticDependencies (NodeFactory context) => null; public override IEnumerable? SearchDynamicDependencies (List> markedNodes, int firstNode, NodeFactory context) => null; - protected override string GetName (NodeFactory context) => $"{type.GetDisplayName()} is relevant to variant casting"; + protected override string GetName (NodeFactory context) => $"{type.GetDisplayName ()} is relevant to variant casting"; protected override void OnMarked (NodeFactory context) { context.MarkStep.Annotations.MarkRelevantToVariantCasting (type); diff --git a/src/tools/illink/src/linker/Linker.Steps/MarkStep.cs b/src/tools/illink/src/linker/Linker.Steps/MarkStep.cs index d6cc0630a65d58..a3f1ccc6430c10 100644 --- a/src/tools/illink/src/linker/Linker.Steps/MarkStep.cs +++ b/src/tools/illink/src/linker/Linker.Steps/MarkStep.cs @@ -50,7 +50,6 @@ namespace Mono.Linker.Steps { - public partial class MarkStep : IStep { LinkContext? _context; @@ -76,7 +75,7 @@ protected LinkContext Context { // method body scanner. readonly Dictionary _compilerGeneratedMethodRequiresScanner; private readonly NodeFactory _nodeFactory; - private readonly DependencyAnalyzer, NodeFactory> _dependencyGraph; + private readonly DependencyAnalyzer, NodeFactory> _dependencyGraph; MarkStepContext? _markContext; MarkStepContext MarkContext { @@ -227,7 +226,7 @@ public MarkStep () _entireTypesMarked = new HashSet (); _compilerGeneratedMethodRequiresScanner = new Dictionary (); _nodeFactory = new NodeFactory (this); - _dependencyGraph = new DependencyAnalyzer, NodeFactory> (_nodeFactory, null); + _dependencyGraph = new DependencyAnalyzer, NodeFactory> (_nodeFactory, null); } public AnnotationStore Annotations => Context.Annotations; @@ -382,6 +381,7 @@ void Process () _dependencyGraph.ComputeMarkedNodes (); ProcessPendingTypeChecks (); + _dependencyGraph.VisitLogEdges (new EdgeVisitor (this)); bool ProcessAllPendingItems () => ProcessPrimaryQueue () || @@ -1930,14 +1930,14 @@ internal void MarkStaticConstructorVisibleToReflection (TypeDefinition type, in MarkStaticConstructor (type, reason, origin); } - /// /// Marks the specified as referenced. /// /// The type reference to mark. /// The reason why the marking is occuring + /// Whether to add the node to the dependency graph /// The resolved type definition if the reference can be resolved - protected internal virtual TypeDefinition? MarkType (TypeReference reference, DependencyInfo reason, MessageOrigin origin) + protected internal virtual TypeDefinition? MarkType (TypeReference reference, DependencyInfo reason, MessageOrigin origin, bool addToGraphAsRoot = true) { #if DEBUG if (!_typeReasons.Contains (reason.Kind)) @@ -1965,7 +1965,12 @@ internal void MarkStaticConstructorVisibleToReflection (TypeDefinition type, in Debug.Assert (Annotations.IsMarked (type)); break; default: - Annotations.Mark (type, reason, origin); + if (addToGraphAsRoot) + Annotations.Mark (type, reason, origin); + else +#pragma warning disable CS0618 // Mark with a reason: We add the reason in the dependency graph edge visitor + Annotations.Mark (type); +#pragma warning restore CS0618 break; } @@ -1990,7 +1995,10 @@ internal void MarkStaticConstructorVisibleToReflection (TypeDefinition type, in if (type.Scope is ModuleDefinition module) MarkModule (module, new DependencyInfo (DependencyKind.ScopeOfType, type), origin); - _dependencyGraph.AddRoot (_nodeFactory.GetTypeNode (type), Enum.GetName (reason.Kind)); + if(addToGraphAsRoot) { + var typeNode = _nodeFactory.GetTypeNode (type); + _dependencyGraph.AddRoot (typeNode, NodeFactory.DependencyKindToStringMap[reason.Kind]); + } return type; } @@ -3011,7 +3019,9 @@ void MarkMethodCollection (IList methods, in DependencyInfo re // We will only enqueue a method to be processed if it hasn't been processed yet. if (!CheckProcessed (method)) _completed = false; - _dependencyGraph.AddRoot (_nodeFactory.GetMethodDefinitionNode (method, reason), Enum.GetName (reason.Kind)); + + var methodNode = _nodeFactory.GetMethodDefinitionNode (method, reason); + _dependencyGraph.AddRoot (methodNode, NodeFactory.DependencyKindToStringMap[reason.Kind]); return method; } @@ -3168,16 +3178,6 @@ protected virtual void ProcessMethod (MethodDefinition method, in DependencyInfo } else if (method.TryGetProperty (out PropertyDefinition? property)) MarkProperty (property, new DependencyInfo (PropagateDependencyKindToAccessors (reason.Kind, DependencyKind.PropertyOfPropertyMethod), method)); - if (method.HasMetadataParameters ()) { -#pragma warning disable RS0030 // MethodReference.Parameters is banned. It's easiest to leave the code as is for now - foreach (ParameterDefinition pd in method.Parameters) { - MarkType (pd.ParameterType, new DependencyInfo (DependencyKind.ParameterType, method), methodOrigin); - MarkCustomAttributes (pd, new DependencyInfo (DependencyKind.ParameterAttribute, method), methodOrigin); - MarkMarshalSpec (pd, new DependencyInfo (DependencyKind.ParameterMarshalSpec, method), methodOrigin); - } -#pragma warning restore RS0030 - } - if (method.HasOverrides) { var assembly = Context.Resolve (method.DeclaringType.Scope); // If this method is in a Copy, CopyUsed, or Save assembly, .overrides won't get swept and we need to keep all of them