Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,6 @@ public interface INodeWithRuntimeDeterminedDependencies
/// <summary>
/// Instantiates runtime determined dependencies of this node using the supplied generic context.
/// </summary>
IEnumerable<DependencyListEntry> InstantiateDependencies(NodeFactory factory, Instantiation typeInstantiation, Instantiation methodInstantiation);
IEnumerable<DependencyListEntry> InstantiateDependencies(NodeFactory factory, Instantiation typeInstantiation, Instantiation methodInstantiation, bool isConcreteInstantiation);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,113 +13,20 @@
namespace ILCompiler.DependencyAnalysis
{
/// <summary>
/// Represents a concrete method on a generic type (or a generic method) that doesn't
/// have code emitted in the executable because it's physically backed by a canonical
/// method body. The purpose of this node is to track the dependencies of the concrete
/// method body, as if it was generated. The node acts as a symbol for the canonical
/// method for convenience.
/// Represents a concrete method (fully instantiated) for the purpose of
/// tracking dependencies.
/// </summary>
public class ShadowConcreteMethodNode : DependencyNodeCore<NodeFactory>, IMethodNode, ISymbolNodeWithLinkage
public class ShadowConcreteMethodNode : ShadowMethodNode, IMethodNode, ISymbolNodeWithLinkage
{
/// <summary>
/// Gets the canonical method body that defines the dependencies of this node.
/// </summary>
public IMethodNode CanonicalMethodNode { get; }

/// <summary>
/// Gets the concrete method represented by this node.
/// </summary>
public MethodDesc Method { get; }

// Implementation of ISymbolNode that makes this node act as a symbol for the canonical body
public void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb)
{
CanonicalMethodNode.AppendMangledName(nameMangler, sb);
}
public int Offset => CanonicalMethodNode.Offset;
public bool RepresentsIndirectionCell => CanonicalMethodNode.RepresentsIndirectionCell;

public override bool StaticDependenciesAreComputed
=> CanonicalMethodNode.StaticDependenciesAreComputed;

public ShadowConcreteMethodNode(MethodDesc method, IMethodNode canonicalMethod)
: base(method, canonicalMethod)
{
Debug.Assert(!method.IsSharedByGenericInstantiations);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would want to keep these assert - they prevent nonsensical graphs.

I think we should split this class into multiple classes - one that tracks fully instantiated things (can keep calling it ShadowConcreteMethod) - this one can still assert all these things. And add one that is not fully specialized. There may be a common base type. I think separate node would also result in more readable dependency graphs.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Split into ShadowConcreteMethodNode and ShadowNonConcreteMethodNode (with shared base ShadowMethodNode)

Debug.Assert(!method.IsRuntimeDeterminedExactMethod);
Debug.Assert(canonicalMethod.Method.IsSharedByGenericInstantiations);
Debug.Assert(canonicalMethod.Method == method.GetCanonMethodTarget(CanonicalFormKind.Specific));
Method = method;
CanonicalMethodNode = canonicalMethod;
}

public ISymbolNode NodeForLinkage(NodeFactory factory)
{
return CanonicalMethodNode;
}

public override IEnumerable<DependencyListEntry> GetStaticDependencies(NodeFactory factory)
{
DependencyList dependencies = new DependencyList();

// Make sure the canonical body gets generated
dependencies.Add(new DependencyListEntry(CanonicalMethodNode, "Canonical body"));

// Instantiate the runtime determined dependencies of the canonical method body
// with the concrete instantiation of the method to get concrete dependencies.
Instantiation typeInst = Method.OwningType.Instantiation;
Instantiation methodInst = Method.Instantiation;
IEnumerable<DependencyListEntry> staticDependencies = CanonicalMethodNode.GetStaticDependencies(factory);

if (staticDependencies != null)
{
foreach (DependencyListEntry canonDep in staticDependencies)
{
var runtimeDep = canonDep.Node as INodeWithRuntimeDeterminedDependencies;
if (runtimeDep != null)
{
dependencies.AddRange(runtimeDep.InstantiateDependencies(factory, typeInst, methodInst));
}
}
}

return dependencies;
}

public sealed override IEnumerable<CombinedDependencyListEntry> GetConditionalStaticDependencies(NodeFactory factory)
{
// Instantiate the runtime determined dependencies of the canonical method body
// with the concrete instantiation of the method to get concrete dependencies.
Instantiation typeInst = Method.OwningType.Instantiation;
Instantiation methodInst = Method.Instantiation;
IEnumerable<CombinedDependencyListEntry> staticDependencies = CanonicalMethodNode.GetConditionalStaticDependencies(factory);

if (staticDependencies != null)
{
foreach (CombinedDependencyListEntry canonDep in staticDependencies)
{
Debug.Assert(canonDep.OtherReasonNode is not INodeWithRuntimeDeterminedDependencies);

var node = canonDep.Node;
if (node is INodeWithRuntimeDeterminedDependencies runtimeDeterminedNode)
{
foreach (var nodeInner in runtimeDeterminedNode.InstantiateDependencies(factory, typeInst, methodInst))
yield return new CombinedDependencyListEntry(nodeInner.Node, canonDep.OtherReasonNode, nodeInner.Reason);
}
}
}
}

protected override string GetName(NodeFactory factory) => $"{Method} backed by {CanonicalMethodNode.GetMangledName(factory.NameMangler)}";

public sealed override bool HasConditionalStaticDependencies => CanonicalMethodNode.HasConditionalStaticDependencies;
public sealed override bool HasDynamicDependencies => false;
public sealed override bool InterestingForDynamicDependencyAnalysis => false;

public sealed override IEnumerable<CombinedDependencyListEntry> SearchDynamicDependencies(List<DependencyNodeCore<NodeFactory>> markedNodes, int firstNode, NodeFactory factory) => null;

int ISortableNode.ClassCode => -1440570971;
protected override int ClassCode => -1440570971;

int ISortableNode.CompareToImpl(ISortableNode other, CompilerComparer comparer)
protected override int CompareToImpl(ISortableNode other, CompilerComparer comparer)
{
var compare = comparer.Compare(Method, ((ShadowConcreteMethodNode)other).Method);
if (compare != 0)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
// 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.Text;
using Internal.TypeSystem;

using Debug = System.Diagnostics.Debug;

namespace ILCompiler.DependencyAnalysis
{
/// <summary>
/// Represents a method on a generic type (or a generic method) that doesn't
/// have code emitted in the executable because it's physically backed by a canonical
/// method body. The purpose of this node is to track the dependencies of the generic
/// method body, as if it was generated. The node acts as a symbol for the canonical
/// method for convenience.
/// </summary>
public abstract class ShadowMethodNode : DependencyNodeCore<NodeFactory>, IMethodNode, ISymbolNodeWithLinkage
{
/// <summary>
/// Gets the canonical method body that defines the dependencies of this node.
/// </summary>
public IMethodNode CanonicalMethodNode { get; }

/// <summary>
/// Gets the generic method represented by this node.
/// </summary>
public MethodDesc Method { get; }

// Implementation of ISymbolNode that makes this node act as a symbol for the canonical body
public void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb)
{
CanonicalMethodNode.AppendMangledName(nameMangler, sb);
}
public int Offset => CanonicalMethodNode.Offset;
public bool RepresentsIndirectionCell => CanonicalMethodNode.RepresentsIndirectionCell;

public override bool StaticDependenciesAreComputed
=> CanonicalMethodNode.StaticDependenciesAreComputed;

public ShadowMethodNode(MethodDesc method, IMethodNode canonicalMethod)
{
Debug.Assert(!method.IsRuntimeDeterminedExactMethod);
Debug.Assert(canonicalMethod.Method == method.GetCanonMethodTarget(CanonicalFormKind.Specific));
Debug.Assert(canonicalMethod.Method.IsSharedByGenericInstantiations);
Method = method;
CanonicalMethodNode = canonicalMethod;
}

public ISymbolNode NodeForLinkage(NodeFactory factory)
{
return CanonicalMethodNode;
}

public override IEnumerable<DependencyListEntry> GetStaticDependencies(NodeFactory factory)
{
DependencyList dependencies = new DependencyList();

// Make sure the canonical body gets generated
dependencies.Add(new DependencyListEntry(CanonicalMethodNode, "Canonical body"));

// Instantiate the runtime determined dependencies of the canonical method body
// with the concrete instantiation of the method to get concrete dependencies.
Instantiation typeInst = Method.OwningType.Instantiation;
Instantiation methodInst = Method.Instantiation;
IEnumerable<DependencyListEntry> staticDependencies = CanonicalMethodNode.GetStaticDependencies(factory);

if (staticDependencies != null)
{
foreach (DependencyListEntry canonDep in staticDependencies)
{
var runtimeDep = canonDep.Node as INodeWithRuntimeDeterminedDependencies;
if (runtimeDep != null)
{
dependencies.AddRange(runtimeDep.InstantiateDependencies(factory, typeInst, methodInst, isConcreteInstantiation: !Method.IsSharedByGenericInstantiations));
}
}
}

return dependencies;
}

public sealed override IEnumerable<CombinedDependencyListEntry> GetConditionalStaticDependencies(NodeFactory factory)
{
// Instantiate the runtime determined dependencies of the canonical method body
// with the concrete instantiation of the method to get concrete dependencies.
Instantiation typeInst = Method.OwningType.Instantiation;
Instantiation methodInst = Method.Instantiation;
IEnumerable<CombinedDependencyListEntry> staticDependencies = CanonicalMethodNode.GetConditionalStaticDependencies(factory);

if (staticDependencies != null)
{
foreach (CombinedDependencyListEntry canonDep in staticDependencies)
{
Debug.Assert(canonDep.OtherReasonNode is not INodeWithRuntimeDeterminedDependencies);

var node = canonDep.Node;
if (node is INodeWithRuntimeDeterminedDependencies runtimeDeterminedNode)
{
foreach (var nodeInner in runtimeDeterminedNode.InstantiateDependencies(factory, typeInst, methodInst, isConcreteInstantiation: !Method.IsSharedByGenericInstantiations))
yield return new CombinedDependencyListEntry(nodeInner.Node, canonDep.OtherReasonNode, nodeInner.Reason);
}
}
}
}


protected override string GetName(NodeFactory factory) => $"{Method} backed by {CanonicalMethodNode.GetMangledName(factory.NameMangler)}";

public sealed override bool HasConditionalStaticDependencies => CanonicalMethodNode.HasConditionalStaticDependencies;
public sealed override bool HasDynamicDependencies => false;
public sealed override bool InterestingForDynamicDependencyAnalysis => false;

public sealed override IEnumerable<CombinedDependencyListEntry> SearchDynamicDependencies(List<DependencyNodeCore<NodeFactory>> markedNodes, int firstNode, NodeFactory factory) => null;

int ISortableNode.ClassCode => ClassCode;

protected abstract int ClassCode { get; }

int ISortableNode.CompareToImpl(ISortableNode other, CompilerComparer comparer) => CompareToImpl(other, comparer);

protected abstract int CompareToImpl(ISortableNode other, CompilerComparer comparer);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// 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.Text;
using Internal.TypeSystem;

using Debug = System.Diagnostics.Debug;

namespace ILCompiler.DependencyAnalysis
{
/// <summary>
/// Represents a non-concrete method (not fully instantiated) for the purpose of
/// tracking dependencies.
/// </summary>
public class ShadowNonConcreteMethodNode : ShadowMethodNode, IMethodNode, ISymbolNodeWithLinkage
{
public ShadowNonConcreteMethodNode(MethodDesc method, IMethodNode canonicalMethod)
: base(method, canonicalMethod)
{
Debug.Assert(method.IsSharedByGenericInstantiations);
}

protected override int ClassCode => 2120942405;

protected override int CompareToImpl(ISortableNode other, CompilerComparer comparer)
{
var compare = comparer.Compare(Method, ((ShadowNonConcreteMethodNode)other).Method);
if (compare != 0)
return compare;

return comparer.Compare(CanonicalMethodNode, ((ShadowNonConcreteMethodNode)other).CanonicalMethodNode);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -749,7 +749,7 @@ private sealed class MakeGenericMethodSite : INodeWithRuntimeDeterminedDependenc

public MakeGenericMethodSite(MethodDesc method) => _method = method;

public IEnumerable<DependencyNodeCore<NodeFactory>.DependencyListEntry> InstantiateDependencies(NodeFactory factory, Instantiation typeInstantiation, Instantiation methodInstantiation)
public IEnumerable<DependencyNodeCore<NodeFactory>.DependencyListEntry> InstantiateDependencies(NodeFactory factory, Instantiation typeInstantiation, Instantiation methodInstantiation, bool isConcreteInstantiation)
{
var list = new DependencyList();
MethodDesc instantiatedMethod = _method.InstantiateSignature(typeInstantiation, methodInstantiation);
Expand All @@ -765,7 +765,7 @@ private sealed class MakeGenericTypeSite : INodeWithRuntimeDeterminedDependencie

public MakeGenericTypeSite(TypeDesc type) => _type = type;

public IEnumerable<DependencyNodeCore<NodeFactory>.DependencyListEntry> InstantiateDependencies(NodeFactory factory, Instantiation typeInstantiation, Instantiation methodInstantiation)
public IEnumerable<DependencyNodeCore<NodeFactory>.DependencyListEntry> InstantiateDependencies(NodeFactory factory, Instantiation typeInstantiation, Instantiation methodInstantiation, bool isConcreteInstantiation)
{
var list = new DependencyList();
TypeDesc instantiatedType = _type.InstantiateSignature(typeInstantiation, methodInstantiation);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ public override IEnumerable<CombinedDependencyListEntry> SearchDynamicDependenci
// Instantiate all runtime dependencies for the found generic specialization
foreach (var n in _runtimeDependencies)
{
foreach (var d in n.InstantiateDependencies(factory, method.OwningType.Instantiation, method.Instantiation))
foreach (var d in n.InstantiateDependencies(factory, method.OwningType.Instantiation, method.Instantiation, isConcreteInstantiation: !method.IsSharedByGenericInstantiations))
{
yield return new CombinedDependencyListEntry(d.Node, null, d.Reason);
}
Expand Down
Loading
Loading