Skip to content

Commit

Permalink
Begin transition to dependency framework in illink (#101277)
Browse files Browse the repository at this point in the history
Recreates the Process() loop in MarkStep with node that depends on a new copy of itself when the loop needs to be rerun. Within the loop, the new Method and Type nodes are added to the dependency framework analyzer as roots when MarkMethod and MarkType are called, which leads to the OnMarked methods to be called, which forwards to the ProcessType/Method method in MarkStep. The dependency analyzer doesn't do anything interesting yet. In a follow-up I'll move dependencies from the MarkX and ProcessX methods to the DependencyNode types.

Makes ILCompiler.DependencyAnalysisFramework output path independent of target architecture. Removes TargetArchitecture and TargetOS from ILLink.Tasks project references.

---------

Co-authored-by: vitek-karas <10670590+vitek-karas@users.noreply.github.com>
  • Loading branch information
jtschuster and vitek-karas committed May 6, 2024
1 parent 1eb888b commit fbb67a6
Show file tree
Hide file tree
Showing 12 changed files with 302 additions and 48 deletions.
4 changes: 3 additions & 1 deletion eng/liveILLink.targets
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,13 @@

<ItemGroup Condition="'$(_RequiresLiveILLink)' == 'true'">
<!-- The assembly shouldn't be referenced, nor promoted to a package dependency, nor copied to the output directory. -->
<!-- ILLink.Tasks is architecture independent, so TargetArch and TargetOS should not be specified -->
<ProjectReference Include="$(_ILLinkTasksSourceDir)ILLink.Tasks.csproj"
ReferenceOutputAssembly="false"
PrivateAssets="all"
Private="false"
SetConfiguration="Configuration=$(ToolsConfiguration)">
SetConfiguration="Configuration=$(ToolsConfiguration)"
GlobalPropertiesToRemove="TargetArchitecture;TargetOS" >
<!-- Keep TFMs in sync with ILLink.Tasks.csproj -->
<SetTargetFramework Condition="'$(MSBuildRuntimeType)' == 'Core'">TargetFramework=$(NetCoreAppToolCurrent)</SetTargetFramework>
<SetTargetFramework Condition="'$(MSBuildRuntimeType)' != 'Core'">TargetFramework=$(NetFrameworkToolCurrent)</SetTargetFramework>
Expand Down
2 changes: 1 addition & 1 deletion src/coreclr/Directory.Build.props
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<Project>
<PropertyGroup>
<InferPlatformFromTargetArchitecture>true</InferPlatformFromTargetArchitecture>
<InferPlatformFromTargetArchitecture Condition="'$(InferPlatformFromTargetArchitecture)' == ''">true</InferPlatformFromTargetArchitecture>

<!-- TODO: Clean-up casing and remove __BuildType to remove this block. -->
<Configuration Condition="'$(Configuration)' == ''">$(__BuildType)</Configuration>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project>
<PropertyGroup>
<PlatformName>AnyCPU</PlatformName>
</PropertyGroup>
<Import Project="Sdk.props" Sdk="Microsoft.NET.Sdk" />
<PropertyGroup>
<OutputType>Library</OutputType>
<RootNamespace>ILCompiler.DependencyAnalysisFramework</RootNamespace>
Expand All @@ -8,6 +12,10 @@
<Platforms>x64;x86</Platforms>
<PlatformTarget>AnyCPU</PlatformTarget>
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
<!-- Workaround for circular dependency (this => trim analyzers in illink.Tasks => Mono.Linker => this)-->
<_RequiresLiveILLink>false</_RequiresLiveILLink>
<!-- Mono.Linker requires the assembly to be signed -->
<SignAssembly>true</SignAssembly>

<!-- We're binplacing these into an existing publish layout so that F5 build in VS updates
the same bits tests expect to see in artifacts/crossgen2. That way we never need to wonder which
Expand Down Expand Up @@ -50,4 +58,5 @@
<Link>Sorting\MergeSortCore.cs</Link>
</Compile>
</ItemGroup>
<Import Project="Sdk.targets" Sdk="Microsoft.NET.Sdk" />
</Project>
5 changes: 0 additions & 5 deletions src/tools/illink/src/linker/Linker.Steps/MarkScopeStack.cs
Original file line number Diff line number Diff line change
Expand Up @@ -88,11 +88,6 @@ internal LocalScope PushLocalScope (in Scope scope)
return new LocalScope (scope, this);
}

internal ParentScope PopToParentScope ()
{
return new ParentScope (this);
}

public IDisposable PushScope (in MessageOrigin origin)
{
return new LocalScope (origin, this);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
// 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 ILCompiler.DependencyAnalysisFramework;
using Mono.Cecil;

namespace Mono.Linker.Steps
{
public partial class MarkStep
{
internal sealed class MethodDefinitionNode : DependencyNodeCore<NodeFactory>
{
readonly MethodDefinition method;
readonly DependencyInfo reason;

public MethodDefinitionNode (MethodDefinition method, DependencyInfo reason)
{
this.method = method;
this.reason = reason;
}

public override bool InterestingForDynamicDependencyAnalysis => false;

public override bool HasDynamicDependencies => false;

public override bool HasConditionalStaticDependencies => false;

public override bool StaticDependenciesAreComputed => true;

public override IEnumerable<DependencyListEntry>? GetStaticDependencies (NodeFactory context)
{
context.MarkStep.ProcessMethod (method, reason);
return null;
}

public override IEnumerable<CombinedDependencyListEntry>? GetConditionalStaticDependencies (NodeFactory context) => null;
public override IEnumerable<CombinedDependencyListEntry>? SearchDynamicDependencies (List<DependencyNodeCore<NodeFactory>> markedNodes, int firstNode, NodeFactory context) => null;
protected override string GetName (NodeFactory context) => method.GetDisplayName();
}
}
}
65 changes: 65 additions & 0 deletions src/tools/illink/src/linker/Linker.Steps/MarkStep.NodeFactory.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
// 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.Concurrent;
using System.Collections.Generic;
using Mono.Cecil;

namespace Mono.Linker.Steps
{
public partial class MarkStep
{
internal sealed class NodeFactory (MarkStep markStep)
{
public MarkStep MarkStep { get; } = markStep;
readonly NodeCache<TypeDefinition, TypeDefinitionNode> _typeNodes = new (static t => new TypeDefinitionNode(t));
readonly NodeCache<MethodDefinition, MethodDefinitionNode> _methodNodes = new (static _ => throw new InvalidOperationException ("Creation of node requires more than the key."));
readonly NodeCache<TypeDefinition, TypeIsRelevantToVariantCastingNode> _typeIsRelevantToVariantCastingNodes = new (static (t) => new TypeIsRelevantToVariantCastingNode (t));

internal TypeDefinitionNode GetTypeNode (TypeDefinition definition)
{
return _typeNodes.GetOrAdd (definition);
}

internal MethodDefinitionNode GetMethodDefinitionNode (MethodDefinition method, DependencyInfo reason)
{
return _methodNodes.GetOrAdd (method, (k) => new MethodDefinitionNode (k, reason));
}

internal TypeIsRelevantToVariantCastingNode GetTypeIsRelevantToVariantCastingNode (TypeDefinition type)
{
return _typeIsRelevantToVariantCastingNodes.GetOrAdd (type);
}

struct NodeCache<TKey, TValue> where TKey : notnull
{
// Change to concurrent dictionary if/when multithreaded marking is enabled
readonly Dictionary<TKey, TValue> _cache;
readonly Func<TKey, TValue> _creator;

public NodeCache (Func<TKey, TValue> creator, IEqualityComparer<TKey> comparer)
{
_creator = creator;
_cache = new (comparer);
}

public NodeCache (Func<TKey, TValue> creator)
{
_creator = creator;
_cache = new ();
}

public TValue GetOrAdd (TKey key)
{
return _cache.GetOrAdd (key, _creator);
}

public TValue GetOrAdd (TKey key, Func<TKey, TValue> creator)
{
return _cache.GetOrAdd (key, creator);
}
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
// 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 ILCompiler.DependencyAnalysisFramework;

namespace Mono.Linker.Steps
{
public partial class MarkStep
{
sealed class ProcessCallbackNode : DependencyNodeCore<NodeFactory>
{
Func<bool> _processAction;
DependencyList? _dependencies;

public ProcessCallbackNode (Func<bool> action) => _processAction = action;

public void Process ()
{
_dependencies = new DependencyList ();
if (_processAction ()) {
_dependencies.Add (new ProcessCallbackNode (_processAction), "Some processing was done, continuation required");
}
}

public override bool InterestingForDynamicDependencyAnalysis => false;

public override bool HasDynamicDependencies => false;

public override bool HasConditionalStaticDependencies => false;

public override bool StaticDependenciesAreComputed => _dependencies != null;

public override IEnumerable<DependencyListEntry>? GetStaticDependencies (NodeFactory context) => _dependencies;

public override IEnumerable<CombinedDependencyListEntry>? GetConditionalStaticDependencies (NodeFactory context) => null;
public override IEnumerable<CombinedDependencyListEntry>? SearchDynamicDependencies (List<DependencyNodeCore<NodeFactory>> markedNodes, int firstNode, NodeFactory context) => null;
protected override string GetName (NodeFactory context) => "Process";
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
// 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.Collections.Generic;
using ILCompiler.DependencyAnalysisFramework;
using Mono.Cecil;

namespace Mono.Linker.Steps
{
public partial class MarkStep
{
internal sealed class TypeDefinitionNode : DependencyNodeCore<NodeFactory>
{
readonly TypeDefinition type;

public TypeDefinitionNode (TypeDefinition type)
{
this.type = type;
}

public override bool InterestingForDynamicDependencyAnalysis => false;

public override bool HasDynamicDependencies => false;

public override bool HasConditionalStaticDependencies => false;

public override bool StaticDependenciesAreComputed => true;

public override IEnumerable<DependencyListEntry>? GetStaticDependencies (NodeFactory context)
{
context.MarkStep.ProcessType (type);
return null;
}

public override IEnumerable<CombinedDependencyListEntry>? GetConditionalStaticDependencies (NodeFactory context) => null;
public override IEnumerable<CombinedDependencyListEntry>? SearchDynamicDependencies (List<DependencyNodeCore<NodeFactory>> markedNodes, int firstNode, NodeFactory context) => null;
protected override string GetName (NodeFactory context) => type.GetDisplayName();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
// 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.Collections.Generic;
using ILCompiler.DependencyAnalysisFramework;
using Mono.Cecil;

namespace Mono.Linker.Steps
{
public partial class MarkStep
{
internal sealed class TypeIsRelevantToVariantCastingNode : DependencyNodeCore<NodeFactory>
{
TypeDefinition type;
public TypeIsRelevantToVariantCastingNode (TypeDefinition type) => this.type = type;

public override bool InterestingForDynamicDependencyAnalysis => false;

public override bool HasDynamicDependencies => false;

public override bool HasConditionalStaticDependencies => false;

public override bool StaticDependenciesAreComputed => true;

public override IEnumerable<DependencyListEntry>? GetStaticDependencies (NodeFactory context)
{
yield break;
}

public override IEnumerable<CombinedDependencyListEntry>? GetConditionalStaticDependencies (NodeFactory context) => null;
public override IEnumerable<CombinedDependencyListEntry>? SearchDynamicDependencies (List<DependencyNodeCore<NodeFactory>> markedNodes, int firstNode, NodeFactory context) => null;
protected override string GetName (NodeFactory context) => $"{type.GetDisplayName()} is relevant to variant casting";
protected override void OnMarked (NodeFactory context)
{
context.MarkStep.Annotations.MarkRelevantToVariantCasting (type);
}
}
}
}
Loading

0 comments on commit fbb67a6

Please sign in to comment.