Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
dc8058b
Create MethodGenericDictionary for calls to GVMs
sbomer Nov 27, 2025
036aded
Revert "Create MethodGenericDictionary for calls to GVMs"
sbomer Dec 5, 2025
74c0072
Create ShadowConcreteMethod for GVMs
sbomer Dec 5, 2025
bfc47f2
Revert "Create ShadowConcreteMethod for GVMs"
sbomer Dec 9, 2025
9141892
Add ShadowGenericMethod for partially instantiated methods
sbomer Dec 9, 2025
b0923a9
Add testcase
sbomer Dec 9, 2025
5d9b2bc
Use existing InstantiateDependencies for generic lookups to create sh…
sbomer Dec 10, 2025
3a67c7f
More tests
sbomer Dec 10, 2025
34787ec
Track ShadowGenericMethod for direct call
sbomer Dec 13, 2025
2add96c
Merge remote-tracking branch 'origin/main' into vtableScannerFix
sbomer Dec 13, 2025
f2a2fcc
Revert normalization change
sbomer Dec 13, 2025
4d6fbdc
Cleanup
sbomer Dec 16, 2025
9911cf5
Update src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyA…
sbomer Dec 16, 2025
7b51934
Feedback
sbomer Dec 17, 2025
7316f1c
Merge branch 'main' into vtableScannerFix
sbomer Dec 18, 2025
438c801
Handle null GetTarget and non-concrete MakeGeneric
sbomer Dec 18, 2025
9dd909e
Revert MakeGeneric logic
sbomer Dec 18, 2025
2f39adc
Update src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/Ha…
sbomer Dec 18, 2025
cb7fc96
Fix node types
sbomer Dec 19, 2025
81768e4
Merge branch 'main' into vtableScannerFix
MichalStrehovsky Dec 22, 2025
ad784ff
PR feedback
sbomer Jan 2, 2026
0ce61d3
Merge remote-tracking branch 'origin/main' into vtableScannerFix
sbomer Jan 2, 2026
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 of inlinees.
/// </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);
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