From a9b2803fa3928272cbff0f5dbbdccd1170cfdec4 Mon Sep 17 00:00:00 2001 From: vitek-karas <10670590+vitek-karas@users.noreply.github.com> Date: Wed, 29 Mar 2023 06:38:48 -0700 Subject: [PATCH 1/5] Implements correct data flow analysis around static cctors The main open question of this change is where to trigger the data flow for static cctors from. EEType presence is not enough, if the only thing access on the type is a field there doesn't seem to be EEType generated for it. So I added a hook from NonGCStatics. But it seems that a better solution would be to introduce a notion of ".cctor access" into the metadata manager and call it from all the places which actually get a cctor entry point (3 places). --- .../DynamicallyAccessedMembersBinder.cs | 2 +- .../Compiler/Dataflow/ReflectionMarker.cs | 3 ++ .../DataflowAnalyzedTypeDefinitionNode.cs | 29 +++++++++++++++++-- .../DependencyAnalysis/NonGCStaticsNode.cs | 2 ++ .../Compiler/UsageBasedMetadataManager.cs | 2 +- .../RequiresOnStaticConstructor.cs | 21 +++++++------- 6 files changed, 45 insertions(+), 14 deletions(-) diff --git a/src/coreclr/tools/Common/Compiler/Dataflow/DynamicallyAccessedMembersBinder.cs b/src/coreclr/tools/Common/Compiler/Dataflow/DynamicallyAccessedMembersBinder.cs index 59c3cc6848111e..6780cb5619289a 100644 --- a/src/coreclr/tools/Common/Compiler/Dataflow/DynamicallyAccessedMembersBinder.cs +++ b/src/coreclr/tools/Common/Compiler/Dataflow/DynamicallyAccessedMembersBinder.cs @@ -133,7 +133,7 @@ public static IEnumerable GetConstructorsOnType(this TypeDesc type, foreach (var method in type.GetMethods()) { - if (!method.IsConstructor) + if (!method.IsConstructor && !method.IsStaticConstructor) continue; if (filter != null && !filter(method)) 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 c61bd273a0f6fd..4ecabf6aad6d64 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/ReflectionMarker.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/ReflectionMarker.cs @@ -198,6 +198,9 @@ internal void MarkStaticConstructor(in MessageOrigin origin, TypeDesc type, stri if (!_enabled) return; + if (type.HasStaticConstructor) + CheckAndWarnOnReflectionAccess(origin, type.GetStaticConstructor()); + if (!type.IsGenericDefinition && !type.ContainsSignatureVariables(treatGenericParameterLikeSignatureVariable: true) && Factory.PreinitializationManager.HasLazyStaticConstructor(type)) { // Mark the GC static base - it contains a pointer to the class constructor, but also info diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/DataflowAnalyzedTypeDefinitionNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/DataflowAnalyzedTypeDefinitionNode.cs index 2fb798452850f2..a0cc769a48a017 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/DataflowAnalyzedTypeDefinitionNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/DataflowAnalyzedTypeDefinitionNode.cs @@ -25,8 +25,12 @@ public DataflowAnalyzedTypeDefinitionNode(TypeDesc typeDefinition) _typeDefinition = typeDefinition; } - public static void GetDependencies(ref DependencyList dependencies, NodeFactory factory, FlowAnnotations flowAnnotations, TypeDesc type) + public static void GetDependencies(ref DependencyList dependencies, NodeFactory factory, TypeDesc type) { + if (factory.MetadataManager is not UsageBasedMetadataManager usageBasedMetadataManager) + return; + + FlowAnnotations flowAnnotations = usageBasedMetadataManager.FlowAnnotations; bool needsDataflowAnalysis = false; type = type.GetTypeDefinition(); @@ -49,6 +53,14 @@ public static void GetDependencies(ref DependencyList dependencies, NodeFactory needsDataflowAnalysis |= GenericArgumentDataFlow.RequiresGenericArgumentDataFlow(flowAnnotations, interfaceType); } } + + if (type.HasStaticConstructor) + { + if (DiagnosticUtilities.TryGetRequiresAttribute(type.GetStaticConstructor(), DiagnosticUtilities.RequiresUnreferencedCodeAttribute, out _) || + DiagnosticUtilities.TryGetRequiresAttribute(type.GetStaticConstructor(), DiagnosticUtilities.RequiresDynamicCodeAttribute, out _) || + DiagnosticUtilities.TryGetRequiresAttribute(type.GetStaticConstructor(), DiagnosticUtilities.RequiresAssemblyFilesAttribute, out _)) + needsDataflowAnalysis = true; + } } catch (TypeSystemException) { @@ -66,13 +78,13 @@ public static void GetDependencies(ref DependencyList dependencies, NodeFactory public override IEnumerable GetStaticDependencies(NodeFactory factory) { DependencyList dependencies = null; + UsageBasedMetadataManager metadataManager = (UsageBasedMetadataManager)factory.MetadataManager; if (_typeDefinition.HasBaseType) { if (_typeDefinition.BaseType.DoesTypeRequire(DiagnosticUtilities.RequiresUnreferencedCodeAttribute, out var requiresAttribute) && !_typeDefinition.DoesTypeRequire(DiagnosticUtilities.RequiresUnreferencedCodeAttribute, out _)) { - UsageBasedMetadataManager metadataManager = (UsageBasedMetadataManager)factory.MetadataManager; string arg1 = MessageFormat.FormatRequiresAttributeMessageArg(DiagnosticUtilities.GetRequiresAttributeMessage(requiresAttribute.Value)); string arg2 = MessageFormat.FormatRequiresAttributeUrlArg(DiagnosticUtilities.GetRequiresAttributeUrl(requiresAttribute.Value)); metadataManager.Logger.LogWarning(new MessageOrigin(_typeDefinition), DiagnosticId.RequiresUnreferencedCodeOnBaseClass, _typeDefinition.GetDisplayName(), _typeDefinition.BaseType.GetDisplayName(), arg1, arg2); @@ -89,6 +101,19 @@ public override IEnumerable GetStaticDependencies(NodeFacto } } + if (_typeDefinition.HasStaticConstructor) + { + MethodDesc staticConstructor = _typeDefinition.GetStaticConstructor(); + if (DiagnosticUtilities.TryGetRequiresAttribute(staticConstructor, DiagnosticUtilities.RequiresUnreferencedCodeAttribute, out _)) + metadataManager.Logger.LogWarning(new MessageOrigin(staticConstructor), DiagnosticId.RequiresUnreferencedCodeOnStaticConstructor, staticConstructor.GetDisplayName()); + + if (DiagnosticUtilities.TryGetRequiresAttribute(staticConstructor, DiagnosticUtilities.RequiresDynamicCodeAttribute, out _)) + metadataManager.Logger.LogWarning(new MessageOrigin(staticConstructor), DiagnosticId.RequiresDynamicCodeOnStaticConstructor, staticConstructor.GetDisplayName()); + + if (DiagnosticUtilities.TryGetRequiresAttribute(staticConstructor, DiagnosticUtilities.RequiresAssemblyFilesAttribute, out _)) + metadataManager.Logger.LogWarning(new MessageOrigin(staticConstructor), DiagnosticId.RequiresAssemblyFilesOnStaticConstructor, staticConstructor.GetDisplayName()); + } + return dependencies; } diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/NonGCStaticsNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/NonGCStaticsNode.cs index 8e548e4ce5183a..133b0f5cef62a9 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/NonGCStaticsNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/NonGCStaticsNode.cs @@ -146,6 +146,8 @@ protected override DependencyList ComputeNonRelocationBasedDependencies(NodeFact ModuleUseBasedDependencyAlgorithm.AddDependenciesDueToModuleUse(ref dependencyList, factory, _type.Module); + DataflowAnalyzedTypeDefinitionNode.GetDependencies(ref dependencyList, factory, _type); + return dependencyList; } diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/UsageBasedMetadataManager.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/UsageBasedMetadataManager.cs index 5403534d411eff..2e20fb59061856 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/UsageBasedMetadataManager.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/UsageBasedMetadataManager.cs @@ -384,7 +384,7 @@ public override void GetDependenciesDueToEETypePresence(ref DependencyList depen { base.GetDependenciesDueToEETypePresence(ref dependencies, factory, type); - DataflowAnalyzedTypeDefinitionNode.GetDependencies(ref dependencies, factory, FlowAnnotations, type); + DataflowAnalyzedTypeDefinitionNode.GetDependencies(ref dependencies, factory, type); } public override bool HasConditionalDependenciesDueToEETypePresence(TypeDesc type) diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/RequiresCapability/RequiresOnStaticConstructor.cs b/src/tools/illink/test/Mono.Linker.Tests.Cases/RequiresCapability/RequiresOnStaticConstructor.cs index 5532bd60b91cf2..87ce3415b8c6ab 100644 --- a/src/tools/illink/test/Mono.Linker.Tests.Cases/RequiresCapability/RequiresOnStaticConstructor.cs +++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/RequiresCapability/RequiresOnStaticConstructor.cs @@ -14,7 +14,6 @@ namespace Mono.Linker.Tests.Cases.RequiresCapability { - [IgnoreTestCase ("Ignore in NativeAOT, see https://github.com/dotnet/runtime/issues/82447", IgnoredBy = Tool.NativeAot)] [SkipKeptItemsValidation] [ExpectedNoWarnings] class RequiresOnStaticConstructor @@ -34,8 +33,8 @@ public static void Main () class StaticCtor { [ExpectedWarning ("IL2026", "--MethodWithRequires--")] - [ExpectedWarning ("IL3002", "--MethodWithRequires--", ProducedBy = Tool.Analyzer)] - [ExpectedWarning ("IL3050", "--MethodWithRequires--", ProducedBy = Tool.Analyzer)] + [ExpectedWarning ("IL3002", "--MethodWithRequires--", ProducedBy = Tool.Analyzer | Tool.NativeAot)] + [ExpectedWarning ("IL3050", "--MethodWithRequires--", ProducedBy = Tool.Analyzer | Tool.NativeAot)] [ExpectedWarning ("IL2116", "StaticCtor..cctor()")] [RequiresUnreferencedCode ("Message for --TestStaticCtor--")] static StaticCtor () @@ -52,8 +51,8 @@ static void TestStaticCctorRequires () [RequiresUnreferencedCode ("Message for --StaticCtorOnTypeWithRequires--")] class StaticCtorOnTypeWithRequires { - [ExpectedWarning ("IL3002", "--MethodWithRequires--", ProducedBy = Tool.Analyzer)] - [ExpectedWarning ("IL3050", "--MethodWithRequires--", ProducedBy = Tool.Analyzer)] + [ExpectedWarning ("IL3002", "--MethodWithRequires--", ProducedBy = Tool.Analyzer | Tool.NativeAot)] + [ExpectedWarning ("IL3050", "--MethodWithRequires--", ProducedBy = Tool.Analyzer | Tool.NativeAot)] static StaticCtorOnTypeWithRequires () => MethodWithRequires (); } @@ -105,8 +104,8 @@ static void TestStaticCtorMarkingIsTriggeredByFieldAccessOnExplicitLayout () class StaticCtorTriggeredByMethodCall { [ExpectedWarning ("IL2116", "StaticCtorTriggeredByMethodCall..cctor()")] - [ExpectedWarning ("IL3004", "StaticCtorTriggeredByMethodCall..cctor()", ProducedBy = Tool.Analyzer)] - [ExpectedWarning ("IL3056", "StaticCtorTriggeredByMethodCall..cctor()", ProducedBy = Tool.Analyzer)] + [ExpectedWarning ("IL3004", "StaticCtorTriggeredByMethodCall..cctor()", ProducedBy = Tool.Analyzer | Tool.NativeAot)] + [ExpectedWarning ("IL3056", "StaticCtorTriggeredByMethodCall..cctor()", ProducedBy = Tool.Analyzer | Tool.NativeAot)] [RequiresUnreferencedCode ("Message for --StaticCtorTriggeredByMethodCall.Cctor--")] [RequiresAssemblyFiles ("Message for --StaticCtorTriggeredByMethodCall.Cctor--")] [RequiresDynamicCode ("Message for --StaticCtorTriggeredByMethodCall.Cctor--")] @@ -124,8 +123,8 @@ public void TriggerStaticCtorMarking () [ExpectedWarning ("IL2026", "--StaticCtorTriggeredByMethodCall.TriggerStaticCtorMarking--")] - [ExpectedWarning ("IL3002", "--StaticCtorTriggeredByMethodCall.TriggerStaticCtorMarking--", ProducedBy = Tool.Analyzer)] - [ExpectedWarning ("IL3050", "--StaticCtorTriggeredByMethodCall.TriggerStaticCtorMarking--", ProducedBy = Tool.Analyzer)] + [ExpectedWarning ("IL3002", "--StaticCtorTriggeredByMethodCall.TriggerStaticCtorMarking--", ProducedBy = Tool.Analyzer | Tool.NativeAot)] + [ExpectedWarning ("IL3050", "--StaticCtorTriggeredByMethodCall.TriggerStaticCtorMarking--", ProducedBy = Tool.Analyzer | Tool.NativeAot)] static void TestStaticCtorTriggeredByMethodCall () { new StaticCtorTriggeredByMethodCall ().TriggerStaticCtorMarking (); @@ -149,7 +148,9 @@ class TypeIsBeforeFieldInit [LogContains ("IL2026: Mono.Linker.Tests.Cases.RequiresCapability.RequiresOnStaticConstructor.TypeIsBeforeFieldInit..cctor():" + " Using member 'Mono.Linker.Tests.Cases.RequiresCapability.RequiresOnStaticConstructor.TypeIsBeforeFieldInit.AnnotatedMethod()'" + " which has 'RequiresUnreferencedCodeAttribute' can break functionality when trimming application code." + - " Message from --TypeIsBeforeFieldInit.AnnotatedMethod--.", ProducedBy = Tool.Trimmer)] + " Message from --TypeIsBeforeFieldInit.AnnotatedMethod--.", ProducedBy = Tool.Trimmer | Tool.NativeAot)] + [LogContains ("IL3002: Mono.Linker.Tests.Cases.RequiresCapability.RequiresOnStaticConstructor.TypeIsBeforeFieldInit..cctor():", ProducedBy = Tool.NativeAot)] + [LogContains ("IL3050: Mono.Linker.Tests.Cases.RequiresCapability.RequiresOnStaticConstructor.TypeIsBeforeFieldInit..cctor():", ProducedBy = Tool.NativeAot)] static void TestTypeIsBeforeFieldInit () { var x = TypeIsBeforeFieldInit.field + 42; From ee9ad7df7cc205fc14e0a242d32423acb1696453 Mon Sep 17 00:00:00 2001 From: vitek-karas <10670590+vitek-karas@users.noreply.github.com> Date: Wed, 29 Mar 2023 12:53:38 -0700 Subject: [PATCH 2/5] Introduce a new node for static ctor analysis and move away from eetypes --- .../DataflowAnalyzedTypeDefinitionNode.cs | 27 +------ .../DependencyAnalysis/NodeFactory.cs | 12 +++ .../DependencyAnalysis/NonGCStaticsNode.cs | 2 - .../StaticConstructorAnalysisNode.cs | 74 +++++++++++++++++++ .../Compiler/UsageBasedMetadataManager.cs | 4 +- .../ILCompiler.Compiler.csproj | 1 + 6 files changed, 91 insertions(+), 29 deletions(-) create mode 100644 src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/StaticConstructorAnalysisNode.cs diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/DataflowAnalyzedTypeDefinitionNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/DataflowAnalyzedTypeDefinitionNode.cs index a0cc769a48a017..e75d16caf7302e 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/DataflowAnalyzedTypeDefinitionNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/DataflowAnalyzedTypeDefinitionNode.cs @@ -25,12 +25,8 @@ public DataflowAnalyzedTypeDefinitionNode(TypeDesc typeDefinition) _typeDefinition = typeDefinition; } - public static void GetDependencies(ref DependencyList dependencies, NodeFactory factory, TypeDesc type) + public static void GetDependencies(ref DependencyList dependencies, NodeFactory factory, FlowAnnotations flowAnnotations, TypeDesc type) { - if (factory.MetadataManager is not UsageBasedMetadataManager usageBasedMetadataManager) - return; - - FlowAnnotations flowAnnotations = usageBasedMetadataManager.FlowAnnotations; bool needsDataflowAnalysis = false; type = type.GetTypeDefinition(); @@ -53,14 +49,6 @@ public static void GetDependencies(ref DependencyList dependencies, NodeFactory needsDataflowAnalysis |= GenericArgumentDataFlow.RequiresGenericArgumentDataFlow(flowAnnotations, interfaceType); } } - - if (type.HasStaticConstructor) - { - if (DiagnosticUtilities.TryGetRequiresAttribute(type.GetStaticConstructor(), DiagnosticUtilities.RequiresUnreferencedCodeAttribute, out _) || - DiagnosticUtilities.TryGetRequiresAttribute(type.GetStaticConstructor(), DiagnosticUtilities.RequiresDynamicCodeAttribute, out _) || - DiagnosticUtilities.TryGetRequiresAttribute(type.GetStaticConstructor(), DiagnosticUtilities.RequiresAssemblyFilesAttribute, out _)) - needsDataflowAnalysis = true; - } } catch (TypeSystemException) { @@ -101,19 +89,6 @@ public override IEnumerable GetStaticDependencies(NodeFacto } } - if (_typeDefinition.HasStaticConstructor) - { - MethodDesc staticConstructor = _typeDefinition.GetStaticConstructor(); - if (DiagnosticUtilities.TryGetRequiresAttribute(staticConstructor, DiagnosticUtilities.RequiresUnreferencedCodeAttribute, out _)) - metadataManager.Logger.LogWarning(new MessageOrigin(staticConstructor), DiagnosticId.RequiresUnreferencedCodeOnStaticConstructor, staticConstructor.GetDisplayName()); - - if (DiagnosticUtilities.TryGetRequiresAttribute(staticConstructor, DiagnosticUtilities.RequiresDynamicCodeAttribute, out _)) - metadataManager.Logger.LogWarning(new MessageOrigin(staticConstructor), DiagnosticId.RequiresDynamicCodeOnStaticConstructor, staticConstructor.GetDisplayName()); - - if (DiagnosticUtilities.TryGetRequiresAttribute(staticConstructor, DiagnosticUtilities.RequiresAssemblyFilesAttribute, out _)) - metadataManager.Logger.LogWarning(new MessageOrigin(staticConstructor), DiagnosticId.RequiresAssemblyFilesOnStaticConstructor, staticConstructor.GetDisplayName()); - } - return dependencies; } 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 70713b3431cd8e..484fb544f660c2 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/NodeFactory.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/NodeFactory.cs @@ -393,6 +393,11 @@ private void CreateNodeCaches() return new DynamicDependencyAttributesOnEntityNode(entity); }); + _staticConstructorAnalysisNodes = new NodeCache((MethodDesc method) => + { + return new StaticConstructorAnalysisNode(method); + }); + _embeddedTrimmingDescriptors = new NodeCache((module) => { return new EmbeddedTrimmingDescriptorNode(module); @@ -727,6 +732,13 @@ public DynamicDependencyAttributesOnEntityNode DynamicDependencyAttributesOnEnti return _dynamicDependencyAttributesOnEntities.GetOrAdd(entity); } + private NodeCache _staticConstructorAnalysisNodes; + + public StaticConstructorAnalysisNode StaticConstructorAnalysis(MethodDesc staticConstructor) + { + return _staticConstructorAnalysisNodes.GetOrAdd(staticConstructor); + } + private NodeCache _embeddedTrimmingDescriptors; public EmbeddedTrimmingDescriptorNode EmbeddedTrimmingDescriptor(EcmaModule module) diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/NonGCStaticsNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/NonGCStaticsNode.cs index 133b0f5cef62a9..8e548e4ce5183a 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/NonGCStaticsNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/NonGCStaticsNode.cs @@ -146,8 +146,6 @@ protected override DependencyList ComputeNonRelocationBasedDependencies(NodeFact ModuleUseBasedDependencyAlgorithm.AddDependenciesDueToModuleUse(ref dependencyList, factory, _type.Module); - DataflowAnalyzedTypeDefinitionNode.GetDependencies(ref dependencyList, factory, _type); - return dependencyList; } diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/StaticConstructorAnalysisNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/StaticConstructorAnalysisNode.cs new file mode 100644 index 00000000000000..dd485336d7c0de --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/StaticConstructorAnalysisNode.cs @@ -0,0 +1,74 @@ +// 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 Internal.TypeSystem; + +using ILCompiler.DependencyAnalysisFramework; +using ILCompiler.Dataflow; +using ILCompiler.Logging; + +using ILLink.Shared; + +namespace ILCompiler.DependencyAnalysis +{ + /// + /// Node that performs reflection analysis for static constructor + /// + public class StaticConstructorAnalysisNode : DependencyNodeCore + { + private readonly MethodDesc _staticConstructor; + + public StaticConstructorAnalysisNode(MethodDesc staticConstructor) + { + Debug.Assert(staticConstructor.IsStaticConstructor); + Debug.Assert(staticConstructor.IsTypicalMethodDefinition); + _staticConstructor = staticConstructor; + } + + public static void GetDependencies(ref DependencyList dependencies, NodeFactory factory, MethodDesc method) + { + if (!method.IsStaticConstructor) + return; + + if (DiagnosticUtilities.TryGetRequiresAttribute(method, DiagnosticUtilities.RequiresUnreferencedCodeAttribute, out _) || + DiagnosticUtilities.TryGetRequiresAttribute(method, DiagnosticUtilities.RequiresDynamicCodeAttribute, out _) || + DiagnosticUtilities.TryGetRequiresAttribute(method, DiagnosticUtilities.RequiresAssemblyFilesAttribute, out _)) + { + dependencies ??= new DependencyList(); + dependencies.Add(factory.StaticConstructorAnalysis(method), "Static constructor presence"); + } + } + + public override IEnumerable GetStaticDependencies(NodeFactory factory) + { + var metadataManager = (UsageBasedMetadataManager)factory.MetadataManager; + + if (DiagnosticUtilities.TryGetRequiresAttribute(_staticConstructor, DiagnosticUtilities.RequiresUnreferencedCodeAttribute, out _)) + metadataManager.Logger.LogWarning(new MessageOrigin(_staticConstructor), DiagnosticId.RequiresUnreferencedCodeOnStaticConstructor, _staticConstructor.GetDisplayName()); + + if (DiagnosticUtilities.TryGetRequiresAttribute(_staticConstructor, DiagnosticUtilities.RequiresDynamicCodeAttribute, out _)) + metadataManager.Logger.LogWarning(new MessageOrigin(_staticConstructor), DiagnosticId.RequiresDynamicCodeOnStaticConstructor, _staticConstructor.GetDisplayName()); + + if (DiagnosticUtilities.TryGetRequiresAttribute(_staticConstructor, DiagnosticUtilities.RequiresAssemblyFilesAttribute, out _)) + metadataManager.Logger.LogWarning(new MessageOrigin(_staticConstructor), DiagnosticId.RequiresAssemblyFilesOnStaticConstructor, _staticConstructor.GetDisplayName()); + + return Array.Empty(); + } + + protected override string GetName(NodeFactory factory) + { + return "Static constructor analysis for " + _staticConstructor.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 context) => null; + public override IEnumerable SearchDynamicDependencies(List> markedNodes, int firstNode, NodeFactory context) => null; + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/UsageBasedMetadataManager.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/UsageBasedMetadataManager.cs index 2e20fb59061856..2eb001b963e641 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/UsageBasedMetadataManager.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/UsageBasedMetadataManager.cs @@ -384,7 +384,7 @@ public override void GetDependenciesDueToEETypePresence(ref DependencyList depen { base.GetDependenciesDueToEETypePresence(ref dependencies, factory, type); - DataflowAnalyzedTypeDefinitionNode.GetDependencies(ref dependencies, factory, type); + DataflowAnalyzedTypeDefinitionNode.GetDependencies(ref dependencies, factory, FlowAnnotations, type); } public override bool HasConditionalDependenciesDueToEETypePresence(TypeDesc type) @@ -563,6 +563,8 @@ protected override void GetDependenciesDueToMethodCodePresenceInternal(ref Depen { AddDataflowDependency(ref dependencies, factory, methodIL, "Method has annotated parameters"); } + + StaticConstructorAnalysisNode.GetDependencies(ref dependencies, factory, method); } if (method.GetTypicalMethodDefinition() is Internal.TypeSystem.Ecma.EcmaMethod ecmaMethod) diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/ILCompiler.Compiler.csproj b/src/coreclr/tools/aot/ILCompiler.Compiler/ILCompiler.Compiler.csproj index 4578500923ef8e..d27fc8a33104ef 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/ILCompiler.Compiler.csproj +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/ILCompiler.Compiler.csproj @@ -412,6 +412,7 @@ + From b7b28c7c21e8a62b046beee6b36f186240628628 Mon Sep 17 00:00:00 2001 From: vitek-karas <10670590+vitek-karas@users.noreply.github.com> Date: Wed, 29 Mar 2023 12:54:47 -0700 Subject: [PATCH 3/5] Revert unnecessary change --- .../DependencyAnalysis/DataflowAnalyzedTypeDefinitionNode.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/DataflowAnalyzedTypeDefinitionNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/DataflowAnalyzedTypeDefinitionNode.cs index e75d16caf7302e..2fb798452850f2 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/DataflowAnalyzedTypeDefinitionNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/DataflowAnalyzedTypeDefinitionNode.cs @@ -66,13 +66,13 @@ public static void GetDependencies(ref DependencyList dependencies, NodeFactory public override IEnumerable GetStaticDependencies(NodeFactory factory) { DependencyList dependencies = null; - UsageBasedMetadataManager metadataManager = (UsageBasedMetadataManager)factory.MetadataManager; if (_typeDefinition.HasBaseType) { if (_typeDefinition.BaseType.DoesTypeRequire(DiagnosticUtilities.RequiresUnreferencedCodeAttribute, out var requiresAttribute) && !_typeDefinition.DoesTypeRequire(DiagnosticUtilities.RequiresUnreferencedCodeAttribute, out _)) { + UsageBasedMetadataManager metadataManager = (UsageBasedMetadataManager)factory.MetadataManager; string arg1 = MessageFormat.FormatRequiresAttributeMessageArg(DiagnosticUtilities.GetRequiresAttributeMessage(requiresAttribute.Value)); string arg2 = MessageFormat.FormatRequiresAttributeUrlArg(DiagnosticUtilities.GetRequiresAttributeUrl(requiresAttribute.Value)); metadataManager.Logger.LogWarning(new MessageOrigin(_typeDefinition), DiagnosticId.RequiresUnreferencedCodeOnBaseClass, _typeDefinition.GetDisplayName(), _typeDefinition.BaseType.GetDisplayName(), arg1, arg2); From bf53f0d2459241876f28f14d66850160b9891955 Mon Sep 17 00:00:00 2001 From: vitek-karas <10670590+vitek-karas@users.noreply.github.com> Date: Thu, 30 Mar 2023 03:40:27 -0700 Subject: [PATCH 4/5] Remove the new node, it's likely not needed. The only case where it might matter is if there's a handle access to a GVM. Then we go through this code: https://github.com/dotnet/runtime/blob/57ae91cf6fc5672e34705b1a272cf268761d505a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/RuntimeMethodHandleNode.cs#L60 That will potentially call the `GetDependenciesDueToMethodCodePresence` multiple times on the same method. But that should not affect static cctors (since they're not GVMs) so the `GetDependenciesDueToMethodCodePresense` should only be called once per static cctor. And even if it does happen (in the future) all it would cause is potentially generating duplicate warnings, no real functional problems. --- .../DependencyAnalysis/NodeFactory.cs | 12 --- .../StaticConstructorAnalysisNode.cs | 74 ------------------- .../Compiler/UsageBasedMetadataManager.cs | 13 +++- .../ILCompiler.Compiler.csproj | 1 - 4 files changed, 12 insertions(+), 88 deletions(-) delete mode 100644 src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/StaticConstructorAnalysisNode.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 484fb544f660c2..70713b3431cd8e 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/NodeFactory.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/NodeFactory.cs @@ -393,11 +393,6 @@ private void CreateNodeCaches() return new DynamicDependencyAttributesOnEntityNode(entity); }); - _staticConstructorAnalysisNodes = new NodeCache((MethodDesc method) => - { - return new StaticConstructorAnalysisNode(method); - }); - _embeddedTrimmingDescriptors = new NodeCache((module) => { return new EmbeddedTrimmingDescriptorNode(module); @@ -732,13 +727,6 @@ public DynamicDependencyAttributesOnEntityNode DynamicDependencyAttributesOnEnti return _dynamicDependencyAttributesOnEntities.GetOrAdd(entity); } - private NodeCache _staticConstructorAnalysisNodes; - - public StaticConstructorAnalysisNode StaticConstructorAnalysis(MethodDesc staticConstructor) - { - return _staticConstructorAnalysisNodes.GetOrAdd(staticConstructor); - } - private NodeCache _embeddedTrimmingDescriptors; public EmbeddedTrimmingDescriptorNode EmbeddedTrimmingDescriptor(EcmaModule module) diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/StaticConstructorAnalysisNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/StaticConstructorAnalysisNode.cs deleted file mode 100644 index dd485336d7c0de..00000000000000 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/StaticConstructorAnalysisNode.cs +++ /dev/null @@ -1,74 +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 Internal.TypeSystem; - -using ILCompiler.DependencyAnalysisFramework; -using ILCompiler.Dataflow; -using ILCompiler.Logging; - -using ILLink.Shared; - -namespace ILCompiler.DependencyAnalysis -{ - /// - /// Node that performs reflection analysis for static constructor - /// - public class StaticConstructorAnalysisNode : DependencyNodeCore - { - private readonly MethodDesc _staticConstructor; - - public StaticConstructorAnalysisNode(MethodDesc staticConstructor) - { - Debug.Assert(staticConstructor.IsStaticConstructor); - Debug.Assert(staticConstructor.IsTypicalMethodDefinition); - _staticConstructor = staticConstructor; - } - - public static void GetDependencies(ref DependencyList dependencies, NodeFactory factory, MethodDesc method) - { - if (!method.IsStaticConstructor) - return; - - if (DiagnosticUtilities.TryGetRequiresAttribute(method, DiagnosticUtilities.RequiresUnreferencedCodeAttribute, out _) || - DiagnosticUtilities.TryGetRequiresAttribute(method, DiagnosticUtilities.RequiresDynamicCodeAttribute, out _) || - DiagnosticUtilities.TryGetRequiresAttribute(method, DiagnosticUtilities.RequiresAssemblyFilesAttribute, out _)) - { - dependencies ??= new DependencyList(); - dependencies.Add(factory.StaticConstructorAnalysis(method), "Static constructor presence"); - } - } - - public override IEnumerable GetStaticDependencies(NodeFactory factory) - { - var metadataManager = (UsageBasedMetadataManager)factory.MetadataManager; - - if (DiagnosticUtilities.TryGetRequiresAttribute(_staticConstructor, DiagnosticUtilities.RequiresUnreferencedCodeAttribute, out _)) - metadataManager.Logger.LogWarning(new MessageOrigin(_staticConstructor), DiagnosticId.RequiresUnreferencedCodeOnStaticConstructor, _staticConstructor.GetDisplayName()); - - if (DiagnosticUtilities.TryGetRequiresAttribute(_staticConstructor, DiagnosticUtilities.RequiresDynamicCodeAttribute, out _)) - metadataManager.Logger.LogWarning(new MessageOrigin(_staticConstructor), DiagnosticId.RequiresDynamicCodeOnStaticConstructor, _staticConstructor.GetDisplayName()); - - if (DiagnosticUtilities.TryGetRequiresAttribute(_staticConstructor, DiagnosticUtilities.RequiresAssemblyFilesAttribute, out _)) - metadataManager.Logger.LogWarning(new MessageOrigin(_staticConstructor), DiagnosticId.RequiresAssemblyFilesOnStaticConstructor, _staticConstructor.GetDisplayName()); - - return Array.Empty(); - } - - protected override string GetName(NodeFactory factory) - { - return "Static constructor analysis for " + _staticConstructor.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 context) => null; - public override IEnumerable SearchDynamicDependencies(List> markedNodes, int firstNode, NodeFactory context) => null; - } -} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/UsageBasedMetadataManager.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/UsageBasedMetadataManager.cs index 2eb001b963e641..fafd8b1744663c 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/UsageBasedMetadataManager.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/UsageBasedMetadataManager.cs @@ -11,6 +11,7 @@ using ILCompiler.Dataflow; using ILCompiler.DependencyAnalysis; using ILCompiler.DependencyAnalysisFramework; +using ILCompiler.Logging; using ILCompiler.Metadata; using ILLink.Shared; @@ -564,7 +565,17 @@ protected override void GetDependenciesDueToMethodCodePresenceInternal(ref Depen AddDataflowDependency(ref dependencies, factory, methodIL, "Method has annotated parameters"); } - StaticConstructorAnalysisNode.GetDependencies(ref dependencies, factory, method); + if (method.IsStaticConstructor) + { + if (DiagnosticUtilities.TryGetRequiresAttribute(method, DiagnosticUtilities.RequiresUnreferencedCodeAttribute, out _)) + Logger.LogWarning(new MessageOrigin(method), DiagnosticId.RequiresUnreferencedCodeOnStaticConstructor, method.GetDisplayName()); + + if (DiagnosticUtilities.TryGetRequiresAttribute(method, DiagnosticUtilities.RequiresDynamicCodeAttribute, out _)) + Logger.LogWarning(new MessageOrigin(method), DiagnosticId.RequiresDynamicCodeOnStaticConstructor, method.GetDisplayName()); + + if (DiagnosticUtilities.TryGetRequiresAttribute(method, DiagnosticUtilities.RequiresAssemblyFilesAttribute, out _)) + Logger.LogWarning(new MessageOrigin(method), DiagnosticId.RequiresAssemblyFilesOnStaticConstructor, method.GetDisplayName()); + } } if (method.GetTypicalMethodDefinition() is Internal.TypeSystem.Ecma.EcmaMethod ecmaMethod) diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/ILCompiler.Compiler.csproj b/src/coreclr/tools/aot/ILCompiler.Compiler/ILCompiler.Compiler.csproj index d27fc8a33104ef..4578500923ef8e 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/ILCompiler.Compiler.csproj +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/ILCompiler.Compiler.csproj @@ -412,7 +412,6 @@ - From e369c8e85735a51728aff8d27a610c8cc0c27abf Mon Sep 17 00:00:00 2001 From: vitek-karas <10670590+vitek-karas@users.noreply.github.com> Date: Mon, 3 Apr 2023 05:12:39 -0700 Subject: [PATCH 5/5] Add test for accessing RUC cctor directly --- .../RequiresOnStaticConstructor.cs | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/RequiresCapability/RequiresOnStaticConstructor.cs b/src/tools/illink/test/Mono.Linker.Tests.Cases/RequiresCapability/RequiresOnStaticConstructor.cs index 87ce3415b8c6ab..c8f059bdc05967 100644 --- a/src/tools/illink/test/Mono.Linker.Tests.Cases/RequiresCapability/RequiresOnStaticConstructor.cs +++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/RequiresCapability/RequiresOnStaticConstructor.cs @@ -27,6 +27,7 @@ public static void Main () TestTypeIsBeforeFieldInit (); TestStaticCtorOnTypeWithRequires (); TestRunClassConstructorOnTypeWithRequires (); + TestRunClassConstructorOnConstructorWithRequires (); typeof (StaticCtor).RequiresNonPublicConstructors (); } @@ -70,6 +71,23 @@ static void TestRunClassConstructorOnTypeWithRequires () RuntimeHelpers.RunClassConstructor (typeHandle); } + class StaticCtorForRunClassConstructorWithRequires + { + [ExpectedWarning ("IL2116")] + [ExpectedWarning ("IL3004", ProducedBy = Tool.Analyzer | Tool.NativeAot)] + [ExpectedWarning ("IL3056", ProducedBy = Tool.Analyzer | Tool.NativeAot)] + [RequiresUnreferencedCode ("Message for --StaticCtorOnTypeWithRequires--")] + [RequiresAssemblyFiles ("Message for --StaticCtorOnTypeWithRequires--")] + [RequiresDynamicCode ("Message for --StaticCtorOnTypeWithRequires--")] + static StaticCtorForRunClassConstructorWithRequires () { } + } + + static void TestRunClassConstructorOnConstructorWithRequires () + { + // No warnings generated here - we rely on IL2116/IL3004/IL3056 in this case and avoid duplicate warnings + RuntimeHelpers.RunClassConstructor (typeof (StaticCtorForRunClassConstructorWithRequires).TypeHandle); + } + class StaticCtorTriggeredByFieldAccess { [ExpectedWarning ("IL2116", "StaticCtorTriggeredByFieldAccess..cctor()")]