Skip to content
Closed
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 @@ -52,15 +52,7 @@ protected override DependencyList ComputeNonRelocationBasedDependencies(NodeFact

dependencyList.Add(factory.VTable(closestDefType), "VTable");

if (_type.IsCanonicalSubtype(CanonicalFormKind.Any))
{
// Track generic virtual methods that will get added to the GVM tables
if ((_virtualMethodAnalysisFlags & VirtualMethodAnalysisFlags.NeedsGvmEntries) != 0)
{
dependencyList.Add(new DependencyListEntry(factory.TypeGVMEntries(_type.GetTypeDefinition()), "Type with generic virtual methods"));
}
}
else
if (!_type.IsCanonicalSubtype(CanonicalFormKind.Any))
{
factory.InteropStubManager.AddInterestingInteropConstructedTypeDependencies(ref dependencyList, factory, _type);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -628,8 +628,6 @@ protected override DependencyList ComputeNonRelocationBasedDependencies(NodeFact
// Generated type contains generic virtual methods that will get added to the GVM tables
if ((_virtualMethodAnalysisFlags & VirtualMethodAnalysisFlags.NeedsGvmEntries) != 0)
{
dependencies.Add(new DependencyListEntry(factory.TypeGVMEntries(_type.GetTypeDefinition()), "Type with generic virtual methods"));

TypeDesc canonicalType = _type.ConvertToCanonForm(CanonicalFormKind.Specific);
if (canonicalType != _type)
dependencies.Add(factory.ConstructedTypeSymbol(canonicalType), "Type with generic virtual methods");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,12 @@ public GVMDependenciesNode(MethodDesc method)
public override IEnumerable<DependencyListEntry> GetStaticDependencies(NodeFactory factory)
{
if (!_method.IsAbstract)
{
yield return new DependencyListEntry(factory.GenericVirtualMethodImpl(_method), "Implementation of the generic virtual method");

if (!_method.OwningType.IsInterface)
yield return new DependencyListEntry(factory.GVMMetadata(_method.GetTypicalMethodDefinition(), _method.GetTypicalMethodDefinition()), "Implementation of the generic virtual method");
}
}

public override IEnumerable<CombinedDependencyListEntry> GetConditionalStaticDependencies(NodeFactory context) => null;
Expand Down Expand Up @@ -124,11 +129,12 @@ public override IEnumerable<CombinedDependencyListEntry> SearchDynamicDependenci
MethodDesc slotDecl = interfaceMethod.Signature.IsStatic ?
potentialOverrideDefinition.InstantiateAsOpen().ResolveInterfaceMethodToStaticVirtualMethodOnType(interfaceMethod)
: potentialOverrideDefinition.InstantiateAsOpen().ResolveInterfaceMethodTarget(interfaceMethod);
DefaultInterfaceMethodResolution defaultResolution = DefaultInterfaceMethodResolution.None;
if (slotDecl == null)
{
// The method might be implemented through a default interface method
var result = potentialOverrideDefinition.InstantiateAsOpen().ResolveInterfaceMethodToDefaultImplementationOnType(interfaceMethod, out slotDecl);
if (result != DefaultInterfaceMethodResolution.DefaultImplementation)
defaultResolution = potentialOverrideDefinition.InstantiateAsOpen().ResolveInterfaceMethodToDefaultImplementationOnType(interfaceMethod, out slotDecl);
if (defaultResolution != DefaultInterfaceMethodResolution.DefaultImplementation)
{
slotDecl = null;
}
Expand All @@ -147,6 +153,8 @@ public override IEnumerable<CombinedDependencyListEntry> SearchDynamicDependenci
else
dynamicDependencies.Add(new CombinedDependencyListEntry(factory.GVMDependencies(implementingMethodInstantiation.GetCanonMethodTarget(CanonicalFormKind.Specific)), null, "ImplementingMethodInstantiation"));

dynamicDependencies.Add(new CombinedDependencyListEntry(factory.InterfaceGVMMetadata(interfaceMethod, slotDecl.GetTypicalMethodDefinition(), potentialOverrideDefinition, defaultResolution), null, "Metadata"));

TypeSystemEntity origin = (implementingMethodInstantiation.OwningType != potentialOverrideType) ? potentialOverrideType : null;
factory.MetadataManager.NoteOverridingMethod(_method, implementingMethodInstantiation, origin);
}
Expand Down Expand Up @@ -200,6 +208,9 @@ public override IEnumerable<CombinedDependencyListEntry> SearchDynamicDependenci
dynamicDependencies.Add(new CombinedDependencyListEntry(
factory.GenericVirtualMethodImpl(instantiatedTargetMethod), null, "DerivedMethodInstantiation"));

dynamicDependencies.Add(new CombinedDependencyListEntry(factory.GVMMetadata(
methodToResolve.GetTypicalMethodDefinition(), instantiatedTargetMethod.GetTypicalMethodDefinition()), null, "Metadata"));

factory.MetadataManager.NoteOverridingMethod(_method, instantiatedTargetMethod);
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Diagnostics;
using System.Collections.Generic;

using ILCompiler.DependencyAnalysisFramework;
using Internal.TypeSystem;

namespace ILCompiler.DependencyAnalysis
{
internal sealed class GVMMetadataNode : SortableDependencyNode
{
public MethodDesc CallingMethod { get; }
public MethodDesc ImplementationMethod { get; }
public GVMMetadataNode(MethodDesc callingMethod, MethodDesc implementationMethod)
=> (CallingMethod, ImplementationMethod) = (callingMethod, implementationMethod);

protected override string GetName(NodeFactory context) =>
$"GVM method: {CallingMethod}: {ImplementationMethod}";

public override IEnumerable<DependencyListEntry> GetStaticDependencies(NodeFactory factory)
{
var list = new DependencyList();
GenericVirtualMethodTableNode.GetGenericVirtualMethodImplementationDependencies(ref list, factory, CallingMethod, ImplementationMethod);
return list;
}

public override int ClassCode => 0x2898423;

public override int CompareToImpl(ISortableNode other, CompilerComparer comparer)
{
var otherNode = (GVMMetadataNode)other;

int result = comparer.Compare(CallingMethod, otherNode.CallingMethod);
if (result != 0)
return result;

return comparer.Compare(ImplementationMethod, otherNode.ImplementationMethod);
}

public override bool InterestingForDynamicDependencyAnalysis => false;
public override bool HasDynamicDependencies => false;
public override bool HasConditionalStaticDependencies => false;
public override bool StaticDependenciesAreComputed => true;
public override IEnumerable<CombinedDependencyListEntry> GetConditionalStaticDependencies(NodeFactory context) => null;
public override IEnumerable<CombinedDependencyListEntry> SearchDynamicDependencies(List<DependencyNodeCore<NodeFactory>> markedNodes, int firstNode, NodeFactory context) => null;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -80,13 +80,9 @@ public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false)
if (relocsOnly)
return new ObjectData(Array.Empty<byte>(), Array.Empty<Relocation>(), 1, new ISymbolDefinitionNode[] { this });

// Build the GVM table entries from the list of interesting GVMTableEntryNodes
foreach (var interestingEntry in factory.MetadataManager.GetTypeGVMEntries())
foreach (var typeGVMEntryInfo in factory.MetadataManager.GetGVMMetadatas())
{
foreach (var typeGVMEntryInfo in interestingEntry.ScanForGenericVirtualMethodEntries())
{
AddGenericVirtualMethodImplementation(typeGVMEntryInfo.CallingMethod, typeGVMEntryInfo.ImplementationMethod);
}
AddGenericVirtualMethodImplementation(typeGVMEntryInfo.CallingMethod, typeGVMEntryInfo.ImplementationMethod);
}

// Ensure the native layout blob has been saved
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
// 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.Diagnostics;
using System.Collections.Generic;

using ILCompiler.DependencyAnalysisFramework;
using Internal.TypeSystem;

namespace ILCompiler.DependencyAnalysis
{
internal sealed class InterfaceGVMMetadataNode : SortableDependencyNode
{
public MethodDesc CallingMethod { get; }
public MethodDesc ImplementationMethod { get; }
public TypeDesc ImplementationType { get; }
public DefaultInterfaceMethodResolution DefaultResolution { get; }

public InterfaceGVMMetadataNode(MethodDesc callingMethod, MethodDesc implementationMethod,
TypeDesc implementationType, DefaultInterfaceMethodResolution defaultResolution)
=> (CallingMethod, ImplementationMethod, ImplementationType, DefaultResolution)
= (callingMethod, implementationMethod, implementationType, defaultResolution);

protected override string GetName(NodeFactory context) =>
$"GVM interface method: {CallingMethod} on {ImplementationType}: {ImplementationMethod}, {DefaultResolution}";

public override IEnumerable<DependencyListEntry> GetStaticDependencies(NodeFactory factory)
{
var list = new DependencyList();
InterfaceGenericVirtualMethodTableNode.GetGenericVirtualMethodImplementationDependencies(ref list, factory, CallingMethod, ImplementationType, ImplementationMethod);
return list;
}

public override int ClassCode => 0x48bcaa1;

public override int CompareToImpl(ISortableNode other, CompilerComparer comparer)
{
var otherNode = (InterfaceGVMMetadataNode)other;

int result = comparer.Compare(ImplementationType, otherNode.ImplementationType);
if (result != 0)
return result;

DefType[] interfaceList = ImplementationType.RuntimeInterfaces;
int thisIndex = Array.IndexOf(interfaceList, CallingMethod.OwningType);
int thatIndex = Array.IndexOf(interfaceList, otherNode.CallingMethod.OwningType);

Debug.Assert(thisIndex >= 0 && thatIndex >= 0);

result = Comparer<int>.Default.Compare(thisIndex, thatIndex);
if (result != 0)
return result;

return comparer.Compare(ImplementationMethod, otherNode.ImplementationMethod);
}

public override bool InterestingForDynamicDependencyAnalysis => false;
public override bool HasDynamicDependencies => false;
public override bool HasConditionalStaticDependencies => false;
public override bool StaticDependenciesAreComputed => true;
public override IEnumerable<CombinedDependencyListEntry> GetConditionalStaticDependencies(NodeFactory context) => null;
public override IEnumerable<CombinedDependencyListEntry> SearchDynamicDependencies(List<DependencyNodeCore<NodeFactory>> markedNodes, int firstNode, NodeFactory context) => null;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -125,13 +125,9 @@ public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false)
if (relocsOnly)
return new ObjectData(Array.Empty<byte>(), Array.Empty<Relocation>(), 1, new ISymbolDefinitionNode[] { this });

// Build the GVM table entries from the list of interesting GVMTableEntryNodes
foreach (var interestingEntry in factory.MetadataManager.GetTypeGVMEntries())
foreach (var typeGVMEntryInfo in factory.MetadataManager.GetInterfaceGVMMetadatas())
{
foreach (var typeGVMEntryInfo in interestingEntry.ScanForInterfaceGenericVirtualMethodEntries())
{
AddGenericVirtualMethodImplementation(typeGVMEntryInfo.CallingMethod, typeGVMEntryInfo.ImplementationType, typeGVMEntryInfo.ImplementationMethod, typeGVMEntryInfo.DefaultResolution);
}
AddGenericVirtualMethodImplementation(typeGVMEntryInfo.CallingMethod, typeGVMEntryInfo.ImplementationType, typeGVMEntryInfo.ImplementationMethod, typeGVMEntryInfo.DefaultResolution);
}

// Ensure the native layout blob has been saved
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -339,9 +339,14 @@ private void CreateNodeCaches()
return new ExactMethodInstantiationsEntryNode(method);
});

_gvmTableEntries = new NodeCache<TypeDesc, TypeGVMEntriesNode>(type =>
_gvmMetadatas = new NodeCache<GVMMetadataKey, GVMMetadataNode>(key =>
{
return new TypeGVMEntriesNode(type);
return new GVMMetadataNode(key.CallingMethod, key.ImplementationMethod);
});

_interfaceGvmMetadatas = new NodeCache<InterfaceGVMMetadataKey, InterfaceGVMMetadataNode>(key =>
{
return new InterfaceGVMMetadataNode(key.CallingMethod, key.ImplementationMethod, key.ImplementationType, key.DefaultResolution);
});

_addressTakenMethods = new NodeCache<MethodDesc, AddressTakenMethodNode>(method =>
Expand Down Expand Up @@ -1165,10 +1170,16 @@ public ExactMethodInstantiationsEntryNode ExactMethodInstantiationsHashtableEntr
return _exactMethodEntries.GetOrAdd(method);
}

private NodeCache<TypeDesc, TypeGVMEntriesNode> _gvmTableEntries;
internal TypeGVMEntriesNode TypeGVMEntries(TypeDesc type)
private NodeCache<GVMMetadataKey, GVMMetadataNode> _gvmMetadatas;
internal GVMMetadataNode GVMMetadata(MethodDesc callingMethod, MethodDesc implementationMethod)
{
return _gvmMetadatas.GetOrAdd(new GVMMetadataKey(callingMethod, implementationMethod));
}

private NodeCache<InterfaceGVMMetadataKey, InterfaceGVMMetadataNode> _interfaceGvmMetadatas;
internal InterfaceGVMMetadataNode InterfaceGVMMetadata(MethodDesc callingMethod, MethodDesc implementationMethod, TypeDesc implementationType, DefaultInterfaceMethodResolution defaultResolution)
{
return _gvmTableEntries.GetOrAdd(type);
return _interfaceGvmMetadatas.GetOrAdd(new InterfaceGVMMetadataKey(callingMethod, implementationMethod, implementationType, defaultResolution));
}

private NodeCache<MethodDesc, AddressTakenMethodNode> _addressTakenMethods;
Expand Down Expand Up @@ -1739,5 +1750,33 @@ private struct MethodILKey : IEquatable<MethodILKey>
public override int GetHashCode() => MethodIL.OwningMethod.GetHashCode();

}

private struct GVMMetadataKey : IEquatable<GVMMetadataKey>
{
public readonly MethodDesc CallingMethod;
public readonly MethodDesc ImplementationMethod;

public GVMMetadataKey(MethodDesc callingMethod, MethodDesc implementationMethod)
=> (CallingMethod, ImplementationMethod) = (callingMethod, implementationMethod);

public override bool Equals(object obj) => obj is GVMMetadataKey other && Equals(other);
public bool Equals(GVMMetadataKey other) => CallingMethod == other.CallingMethod && ImplementationMethod == other.ImplementationMethod;
public override int GetHashCode() => HashCode.Combine(CallingMethod, ImplementationMethod);
}

private struct InterfaceGVMMetadataKey : IEquatable<InterfaceGVMMetadataKey>
{
public readonly MethodDesc CallingMethod;
public readonly MethodDesc ImplementationMethod;
public readonly TypeDesc ImplementationType;
public readonly DefaultInterfaceMethodResolution DefaultResolution;

public InterfaceGVMMetadataKey(MethodDesc callingMethod, MethodDesc implementationMethod, TypeDesc implementationType, DefaultInterfaceMethodResolution resolution)
=> (CallingMethod, ImplementationMethod, ImplementationType, DefaultResolution) = (callingMethod, implementationMethod, implementationType, resolution);

public override bool Equals(object obj) => obj is InterfaceGVMMetadataKey other && Equals(other);
public bool Equals(InterfaceGVMMetadataKey other) => CallingMethod == other.CallingMethod && ImplementationType == other.ImplementationType;
public override int GetHashCode() => HashCode.Combine(CallingMethod, ImplementationType);
Copy link
Preview

Copilot AI Aug 12, 2025

Choose a reason for hiding this comment

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

The Equals method for InterfaceGVMMetadataKey is incomplete. It only compares CallingMethod and ImplementationType, but ignores ImplementationMethod and DefaultResolution fields. This could lead to incorrect equality comparisons and potential cache key collisions.

Suggested change
public override int GetHashCode() => HashCode.Combine(CallingMethod, ImplementationType);
public bool Equals(InterfaceGVMMetadataKey other) =>
CallingMethod == other.CallingMethod &&
ImplementationMethod == other.ImplementationMethod &&
ImplementationType == other.ImplementationType &&
DefaultResolution == other.DefaultResolution;
public override int GetHashCode() => HashCode.Combine(CallingMethod, ImplementationMethod, ImplementationType, DefaultResolution);

Copilot uses AI. Check for mistakes.

}
}
}
Loading
Loading