diff --git a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ConstructedEETypeNode.cs b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ConstructedEETypeNode.cs
index da169eabdc5..21abb42d86c 100644
--- a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ConstructedEETypeNode.cs
+++ b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ConstructedEETypeNode.cs
@@ -26,6 +26,14 @@ public override bool ShouldSkipEmittingObjectNode(NodeFactory factory)
return false;
}
+ public override bool InterestingForDynamicDependencyAnalysis
+ {
+ get
+ {
+ return _type.IsDefType && _type.HasGenericVirtualMethod();
+ }
+ }
+
protected override DependencyList ComputeNonRelocationBasedDependencies(NodeFactory factory)
{
DefType closestDefType = _type.GetClosestDefType();
@@ -89,6 +97,12 @@ protected override DependencyList ComputeNonRelocationBasedDependencies(NodeFact
dependencyList.Add(factory.TypeNonGCStaticsSymbol((MetadataType)_type), "Class constructor");
}
+ // Generated type contains generic virtual methods that will get added to the GVM tables
+ if (TypeGVMEntriesNode.TypeNeedsGVMTableEntries(_type))
+ {
+ dependencyList.Add(new DependencyListEntry(factory.TypeGVMEntries(_type), "Type with generic virtual methods"));
+ }
+
return dependencyList;
}
diff --git a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/GVMDependenciesNode.cs b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/GVMDependenciesNode.cs
new file mode 100644
index 00000000000..23da8552da4
--- /dev/null
+++ b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/GVMDependenciesNode.cs
@@ -0,0 +1,178 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+using System.Diagnostics;
+using System.Collections.Generic;
+
+using ILCompiler.DependencyAnalysisFramework;
+using Internal.Text;
+using Internal.TypeSystem;
+
+namespace ILCompiler.DependencyAnalysis
+{
+ ///
+ /// This analysis node is used for computing GVM dependencies for the following cases:
+ /// 1) Derived types where the GVM is overridden
+ /// 2) Variant-interfaces GVMs
+ /// This analysis node will ensure that the proper GVM instantiations are compiled on types.
+ ///
+ internal class GVMDependenciesNode : DependencyNodeCore
+ {
+ private MethodDesc _method;
+
+ public GVMDependenciesNode(MethodDesc method)
+ {
+ Debug.Assert(!method.IsRuntimeDeterminedExactMethod);
+ Debug.Assert(method.IsVirtual && method.HasInstantiation);
+ _method = method;
+ }
+
+ public override bool HasConditionalStaticDependencies => false;
+ public override bool InterestingForDynamicDependencyAnalysis => false;
+ public override bool StaticDependenciesAreComputed => true;
+ protected override string GetName() => "__GVMDependenciesNode_" + NodeFactory.NameMangler.GetMangledMethodName(_method);
+
+ public override IEnumerable GetStaticDependencies(NodeFactory context)
+ {
+ return Array.Empty();
+ }
+ public override IEnumerable GetConditionalStaticDependencies(NodeFactory context)
+ {
+ return Array.Empty();
+ }
+
+ public override bool HasDynamicDependencies
+ {
+ get
+ {
+ if (_method.IsCanonicalMethod(CanonicalFormKind.Specific))
+ return false;
+
+ if (_method.OwningType.IsCanonicalSubtype(CanonicalFormKind.Universal) &&
+ _method.OwningType != _method.OwningType.ConvertToCanonForm(CanonicalFormKind.Universal))
+ return false;
+
+ return true;
+ }
+ }
+
+ public override IEnumerable SearchDynamicDependencies(List> markedNodes, int firstNode, NodeFactory factory)
+ {
+ Debug.Assert(_method.IsVirtual && _method.HasInstantiation);
+
+ List dynamicDependencies = new List();
+
+ for (int i = firstNode; i < markedNodes.Count; i++)
+ {
+ DependencyNodeCore entry = markedNodes[i];
+ EETypeNode entryAsEETypeNode = entry as EETypeNode;
+
+ if (entryAsEETypeNode == null)
+ continue;
+
+ TypeDesc potentialOverrideType = entryAsEETypeNode.Type;
+ if (!(potentialOverrideType is DefType))
+ continue;
+
+ Debug.Assert(!potentialOverrideType.IsRuntimeDeterminedSubtype);
+
+ if (_method.OwningType.HasSameTypeDefinition(potentialOverrideType) && potentialOverrideType.IsInterface && (potentialOverrideType != _method.OwningType))
+ {
+ if (_method.OwningType.CanCastTo(potentialOverrideType))
+ {
+ // Variance expansion
+ MethodDesc matchingMethodOnRelatedVariantMethod = potentialOverrideType.GetMethod(_method.Name, _method.GetTypicalMethodDefinition().Signature);
+ matchingMethodOnRelatedVariantMethod = _method.Context.GetInstantiatedMethod(matchingMethodOnRelatedVariantMethod, _method.Instantiation);
+ dynamicDependencies.Add(new CombinedDependencyListEntry(factory.GVMDependencies(matchingMethodOnRelatedVariantMethod), null, "GVM Variant Interface dependency"));
+ }
+ }
+
+ // If this is an interface gvm, look for types that implement the interface
+ // and other instantantiations that have the same canonical form.
+ // This ensure the various slot numbers remain equivalent across all types where there is an equivalence
+ // relationship in the vtable.
+ if (_method.OwningType.IsInterface)
+ {
+ if (potentialOverrideType.IsInterface)
+ continue;
+
+ foreach (DefType interfaceImpl in potentialOverrideType.RuntimeInterfaces)
+ {
+ if (interfaceImpl.ConvertToCanonForm(CanonicalFormKind.Specific) == _method.OwningType.ConvertToCanonForm(CanonicalFormKind.Specific))
+ {
+ // Find if the type implements this method. (Note, do this comparision against the generic definition of the method, not the
+ // specific method instantiation that is "method"
+ MethodDesc genericDefinition = interfaceImpl.GetMethod(_method.Name, _method.GetTypicalMethodDefinition().Signature);
+
+ MethodDesc slotDecl;
+ TypeDesc currentType = potentialOverrideType;
+ do
+ {
+ slotDecl = currentType.ResolveInterfaceMethodToVirtualMethodOnType(genericDefinition);
+ currentType = currentType.BaseType;
+ }
+ while (slotDecl == null && currentType != null);
+
+ if (slotDecl != null)
+ CreateDependencyForMethodSlotAndInstantiation(slotDecl, dynamicDependencies, factory);
+ }
+ }
+ }
+ else
+ {
+ // TODO: Ensure GVM Canon Target
+
+ TypeDesc overrideTypeCanonCur = potentialOverrideType;
+ TypeDesc methodCanonContainingType = _method.OwningType;
+ while (overrideTypeCanonCur != null)
+ {
+ if (overrideTypeCanonCur.ConvertToCanonForm(CanonicalFormKind.Specific) == methodCanonContainingType.ConvertToCanonForm(CanonicalFormKind.Specific))
+ {
+ MethodDesc methodDefInDerivedType = potentialOverrideType.GetMethod(_method.Name, _method.GetTypicalMethodDefinition().Signature);
+ if(methodDefInDerivedType != null)
+ CreateDependencyForMethodSlotAndInstantiation(methodDefInDerivedType, dynamicDependencies, factory);
+
+ MethodDesc slotDecl = MetadataVirtualMethodAlgorithm.FindSlotDefiningMethodForVirtualMethod(_method);
+ if (slotDecl != null)
+ CreateDependencyForMethodSlotAndInstantiation(slotDecl.GetMethodDefinition(), dynamicDependencies, factory);
+ }
+
+ overrideTypeCanonCur = overrideTypeCanonCur.BaseType;
+ }
+ }
+ }
+ return dynamicDependencies;
+ }
+
+ private void CreateDependencyForMethodSlotAndInstantiation(MethodDesc methodDef, List dynamicDependencies, NodeFactory factory)
+ {
+ Debug.Assert(methodDef != null);
+ Debug.Assert(!methodDef.Signature.IsStatic);
+
+ MethodDesc derivedMethodInstantiation = _method.Context.GetInstantiatedMethod(methodDef, _method.Instantiation);
+
+ // Universal canonical instantiations should be entirely universal canon
+ if (derivedMethodInstantiation.IsCanonicalMethod(CanonicalFormKind.Universal))
+ {
+ derivedMethodInstantiation = derivedMethodInstantiation.GetCanonMethodTarget(CanonicalFormKind.Universal);
+ }
+
+ // TODO: verify for invalid instantiations, like List?
+ bool validInstantiation =
+ derivedMethodInstantiation.IsSharedByGenericInstantiations || // Non-exact methods are always valid instantiations (always pass constraints check)
+ derivedMethodInstantiation.CheckConstraints(); // Verify that the instantiation does not violate constraints
+
+ if (validInstantiation)
+ {
+ bool getUnboxingStub = (derivedMethodInstantiation.OwningType.IsValueType || derivedMethodInstantiation.OwningType.IsEnum);
+ dynamicDependencies.Add(new CombinedDependencyListEntry(factory.MethodEntrypoint(derivedMethodInstantiation, getUnboxingStub), null, "DerivedMethodInstantiation"));
+ }
+ else
+ {
+ // TODO: universal generics
+ }
+ }
+ }
+}
diff --git a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/GenericVirtualMethodTableNode.cs b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/GenericVirtualMethodTableNode.cs
new file mode 100644
index 00000000000..59f3e10473f
--- /dev/null
+++ b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/GenericVirtualMethodTableNode.cs
@@ -0,0 +1,144 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+using System.IO;
+using System.Diagnostics;
+using System.Collections.Generic;
+
+using Internal.Text;
+using Internal.TypeSystem;
+using Internal.NativeFormat;
+
+namespace ILCompiler.DependencyAnalysis
+{
+ ///
+ /// Represents a map between reflection metadata and generated method bodies.
+ ///
+ internal sealed class GenericVirtualMethodTableNode : ObjectNode, ISymbolNode
+ {
+ private ObjectAndOffsetSymbolNode _endSymbol;
+ private ExternalReferencesTableNode _externalReferences;
+ private Dictionary> _gvmImplemenations;
+
+ public GenericVirtualMethodTableNode(ExternalReferencesTableNode externalReferences)
+ {
+ _endSymbol = new ObjectAndOffsetSymbolNode(this, 0, "__gvm_table_End", true);
+ _externalReferences = externalReferences;
+ _gvmImplemenations = new Dictionary>();
+ }
+
+ public void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb)
+ {
+ sb.Append(nameMangler.CompilationUnitPrefix).Append("__gvm_table");
+ }
+
+ public ISymbolNode EndSymbol => _endSymbol;
+ public int Offset => 0;
+ public override bool IsShareable => false;
+ public override ObjectNodeSection Section => ObjectNodeSection.DataSection;
+ public override bool StaticDependenciesAreComputed => true;
+ protected override string GetName() => this.GetMangledName();
+
+ public static DependencyList GetGenericVirtualMethodImplementationDependencies(NodeFactory factory, MethodDesc callingMethod, MethodDesc implementationMethod)
+ {
+ Debug.Assert(!callingMethod.OwningType.IsInterface);
+
+ DependencyList dependencyNodes = new DependencyList();
+
+ // Compute the open method signatures
+ MethodDesc openCallingMethod = callingMethod.GetTypicalMethodDefinition();
+ MethodDesc openImplementationMethod = implementationMethod.GetTypicalMethodDefinition();
+
+ var openCallingMethodNameAndSig = factory.NativeLayout.MethodNameAndSignatureVertex(openCallingMethod);
+ var openImplementationMethodNameAndSig = factory.NativeLayout.MethodNameAndSignatureVertex(openImplementationMethod);
+
+ dependencyNodes.Add(new DependencyListEntry(factory.NativeLayout.PlacedSignatureVertex(openCallingMethodNameAndSig), "gvm table needed signature"));
+ dependencyNodes.Add(new DependencyListEntry(factory.NativeLayout.PlacedSignatureVertex(openImplementationMethodNameAndSig), "gvm table needed signature"));
+
+ return dependencyNodes;
+ }
+
+ private void AddGenericVirtualMethodImplementation(NodeFactory factory, MethodDesc callingMethod, MethodDesc implementationMethod)
+ {
+ Debug.Assert(!callingMethod.OwningType.IsInterface);
+
+ // Compute the open method signatures
+ MethodDesc openCallingMethod = callingMethod.GetTypicalMethodDefinition();
+ MethodDesc openImplementationMethod = implementationMethod.GetTypicalMethodDefinition();
+
+ // Insert open method signatures into the GVM map
+ if (!_gvmImplemenations.ContainsKey(openCallingMethod))
+ _gvmImplemenations[openCallingMethod] = new Dictionary();
+
+ _gvmImplemenations[openCallingMethod][openImplementationMethod.OwningType] = openImplementationMethod;
+ }
+
+ public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false)
+ {
+ // This node does not trigger generation of other nodes.
+ if (relocsOnly)
+ return new ObjectData(Array.Empty(), Array.Empty(), 1, new ISymbolNode[] { this });
+
+ // Build the GVM table entries from the list of interesting GVMTableEntryNodes
+ foreach (var interestingEntry in factory.MetadataManager.GetTypeGVMEntries())
+ {
+ foreach (var typeGVMEntryInfo in interestingEntry.ScanForGenericVirtualMethodEntries())
+ {
+ AddGenericVirtualMethodImplementation(factory, typeGVMEntryInfo.CallingMethod, typeGVMEntryInfo.ImplementationMethod);
+ }
+ }
+
+ // Ensure the native layout blob has been saved
+ factory.MetadataManager.NativeLayoutInfo.SaveNativeLayoutInfoWriter(factory);
+
+ NativeWriter nativeFormatWriter = new NativeWriter();
+ VertexHashtable gvmHashtable = new VertexHashtable();
+
+ Section gvmHashtableSection = nativeFormatWriter.NewSection();
+ gvmHashtableSection.Place(gvmHashtable);
+
+ // Emit the GVM target information entries
+ foreach (var gvmEntry in _gvmImplemenations)
+ {
+ Debug.Assert(!gvmEntry.Key.OwningType.IsInterface);
+
+ foreach (var implementationEntry in gvmEntry.Value)
+ {
+ MethodDesc callingMethod = gvmEntry.Key;
+ TypeDesc implementationType = implementationEntry.Key;
+ MethodDesc implementationMethod = implementationEntry.Value;
+
+ uint callingTypeId = _externalReferences.GetIndex(factory.NecessaryTypeSymbol(callingMethod.OwningType));
+ Vertex vertex = nativeFormatWriter.GetUnsignedConstant(callingTypeId);
+
+ uint targetTypeId = _externalReferences.GetIndex(factory.NecessaryTypeSymbol(implementationType));
+ vertex = nativeFormatWriter.GetTuple(vertex, nativeFormatWriter.GetUnsignedConstant(targetTypeId));
+
+ var nameAndSig = factory.NativeLayout.PlacedSignatureVertex(factory.NativeLayout.MethodNameAndSignatureVertex(callingMethod));
+ vertex = nativeFormatWriter.GetTuple(vertex, nativeFormatWriter.GetUnsignedConstant((uint)nameAndSig.SavedVertex.VertexOffset));
+
+ nameAndSig = factory.NativeLayout.PlacedSignatureVertex(factory.NativeLayout.MethodNameAndSignatureVertex(implementationMethod));
+ vertex = nativeFormatWriter.GetTuple(vertex, nativeFormatWriter.GetUnsignedConstant((uint)nameAndSig.SavedVertex.VertexOffset));
+
+ int hashCode = callingMethod.OwningType.GetHashCode();
+ hashCode = ((hashCode << 13) ^ hashCode) ^ implementationType.GetHashCode();
+
+ gvmHashtable.Append((uint)hashCode, gvmHashtableSection.Place(vertex));
+ }
+ }
+
+ // Zero out the dictionary so that we AV if someone tries to insert after we're done.
+ _gvmImplemenations = null;
+
+ MemoryStream stream = new MemoryStream();
+ nativeFormatWriter.Save(stream);
+ byte[] streamBytes = stream.ToArray();
+
+ _endSymbol.SetSymbolOffset(streamBytes.Length);
+
+ return new ObjectData(streamBytes, Array.Empty(), 1, new ISymbolNode[] { this, _endSymbol });
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/InterfaceGenericVirtualMethodTableNode.cs b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/InterfaceGenericVirtualMethodTableNode.cs
new file mode 100644
index 00000000000..f732d2fe304
--- /dev/null
+++ b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/InterfaceGenericVirtualMethodTableNode.cs
@@ -0,0 +1,213 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+using System.IO;
+using System.Diagnostics;
+using System.Collections.Generic;
+
+using Internal.Text;
+using Internal.TypeSystem;
+using Internal.NativeFormat;
+
+namespace ILCompiler.DependencyAnalysis
+{
+ ///
+ /// Represents a map between reflection metadata and generated method bodies.
+ ///
+ internal sealed class InterfaceGenericVirtualMethodTableNode : ObjectNode, ISymbolNode
+ {
+ private ObjectAndOffsetSymbolNode _endSymbol;
+ private ExternalReferencesTableNode _externalReferences;
+ private Dictionary> _interfaceGvmSlots;
+ private Dictionary>> _interfaceImpls;
+
+ public InterfaceGenericVirtualMethodTableNode(ExternalReferencesTableNode externalReferences)
+ {
+ _endSymbol = new ObjectAndOffsetSymbolNode(this, 0, "__interface_gvm_table_End", true);
+ _externalReferences = externalReferences;
+ _interfaceGvmSlots = new Dictionary>();
+ _interfaceImpls = new Dictionary>>();
+ }
+
+ public void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb)
+ {
+ sb.Append(nameMangler.CompilationUnitPrefix).Append("__interface_gvm_table");
+ }
+ public ISymbolNode EndSymbol => _endSymbol;
+ public int Offset => 0;
+ public override bool IsShareable => false;
+ public override ObjectNodeSection Section => ObjectNodeSection.DataSection;
+ public override bool StaticDependenciesAreComputed => true;
+ protected override string GetName() => this.GetMangledName();
+
+ public static DependencyList GetGenericVirtualMethodImplementationDependencies(NodeFactory factory, MethodDesc callingMethod, TypeDesc implementationType, MethodDesc implementationMethod)
+ {
+ Debug.Assert(callingMethod.OwningType.IsInterface);
+
+ DependencyList dependencyNodes = new DependencyList();
+
+ // Compute the open method signatures
+ MethodDesc openCallingMethod = callingMethod.GetTypicalMethodDefinition();
+ MethodDesc openImplementationMethod = implementationMethod.GetTypicalMethodDefinition();
+ TypeDesc openImplementationType = implementationType.GetTypeDefinition();
+
+ var openCallingMethodNameAndSig = factory.NativeLayout.MethodNameAndSignatureVertex(openCallingMethod);
+ var openImplementationMethodNameAndSig = factory.NativeLayout.MethodNameAndSignatureVertex(openImplementationMethod);
+
+ dependencyNodes.Add(new DependencyListEntry(factory.NativeLayout.PlacedSignatureVertex(openCallingMethodNameAndSig), "gvm table needed signature"));
+ dependencyNodes.Add(new DependencyListEntry(factory.NativeLayout.PlacedSignatureVertex(openImplementationMethodNameAndSig), "gvm table needed signature"));
+
+ if (!openImplementationType.IsInterface)
+ {
+ for(int index = 0; index < implementationType.RuntimeInterfaces.Length; index++)
+ {
+ if (implementationType.RuntimeInterfaces[index] == callingMethod.OwningType)
+ {
+ TypeDesc currentInterface = openImplementationType.RuntimeInterfaces[index];
+ var currentInterfaceSignature = factory.NativeLayout.TypeSignatureVertex(currentInterface);
+ dependencyNodes.Add(new DependencyListEntry(factory.NativeLayout.PlacedSignatureVertex(currentInterfaceSignature), "gvm table needed signature"));
+ }
+ }
+ }
+
+ return dependencyNodes;
+ }
+
+ private void AddGenericVirtualMethodImplementation(NodeFactory factory, MethodDesc callingMethod, TypeDesc implementationType, MethodDesc implementationMethod)
+ {
+ Debug.Assert(callingMethod.OwningType.IsInterface);
+
+ // Compute the open method signatures
+ MethodDesc openCallingMethod = callingMethod.GetTypicalMethodDefinition();
+ MethodDesc openImplementationMethod = implementationMethod.GetTypicalMethodDefinition();
+ TypeDesc openImplementationType = implementationType.GetTypeDefinition();
+
+ // Add the entry to the interface GVM slots mapping table
+ if (!_interfaceGvmSlots.ContainsKey(openCallingMethod))
+ _interfaceGvmSlots[openCallingMethod] = new HashSet();
+ _interfaceGvmSlots[openCallingMethod].Add(openImplementationMethod);
+
+ // If the implementation method is implementing some interface method, compute which
+ // interface explicitly implemented on the type that the current method implements an interface method for.
+ // We need this because at runtime, the interfaces explicitly implemented on the type will have
+ // runtime-determined signatures that we can use to make generic substitutions and check for interface matching.
+ if (!openImplementationType.IsInterface)
+ {
+ if (!_interfaceImpls.ContainsKey(openImplementationMethod))
+ _interfaceImpls[openImplementationMethod] = new Dictionary>();
+ if (!_interfaceImpls[openImplementationMethod].ContainsKey(openImplementationType))
+ _interfaceImpls[openImplementationMethod][openImplementationType] = new HashSet();
+
+ int numIfacesAdded = 0;
+ for (int index = 0; index < implementationType.RuntimeInterfaces.Length; index++)
+ {
+ if (implementationType.RuntimeInterfaces[index] == callingMethod.OwningType)
+ {
+ _interfaceImpls[openImplementationMethod][openImplementationType].Add(index);
+ numIfacesAdded++;
+ }
+ }
+
+ Debug.Assert(numIfacesAdded > 0);
+ }
+ }
+
+ public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false)
+ {
+ // This node does not trigger generation of other nodes.
+ if (relocsOnly)
+ return new ObjectData(Array.Empty(), Array.Empty(), 1, new ISymbolNode[] { this });
+
+ // Build the GVM table entries from the list of interesting GVMTableEntryNodes
+ foreach (var interestingEntry in factory.MetadataManager.GetTypeGVMEntries())
+ {
+ foreach (var typeGVMEntryInfo in interestingEntry.ScanForInterfaceGenericVirtualMethodEntries())
+ {
+ AddGenericVirtualMethodImplementation(factory, typeGVMEntryInfo.CallingMethod, typeGVMEntryInfo.ImplementationType, typeGVMEntryInfo.ImplementationMethod);
+ }
+ }
+
+ // Ensure the native layout blob has been saved
+ factory.MetadataManager.NativeLayoutInfo.SaveNativeLayoutInfoWriter(factory);
+
+ NativeWriter nativeFormatWriter = new NativeWriter();
+ VertexHashtable gvmHashtable = new VertexHashtable();
+
+ Section gvmHashtableSection = nativeFormatWriter.NewSection();
+ gvmHashtableSection.Place(gvmHashtable);
+
+ // Emit the interface slot resolution entries
+ foreach (var gvmEntry in _interfaceGvmSlots)
+ {
+ Debug.Assert(gvmEntry.Key.OwningType.IsInterface);
+
+ MethodDesc callingMethod = gvmEntry.Key;
+
+ // Emit the method signature and containing type of the current interface method
+ uint typeId = _externalReferences.GetIndex(factory.NecessaryTypeSymbol(callingMethod.OwningType));
+ var nameAndSig = factory.NativeLayout.PlacedSignatureVertex(factory.NativeLayout.MethodNameAndSignatureVertex(callingMethod));
+ Vertex vertex = nativeFormatWriter.GetTuple(
+ nativeFormatWriter.GetUnsignedConstant(typeId),
+ nativeFormatWriter.GetUnsignedConstant((uint)nameAndSig.SavedVertex.VertexOffset));
+
+ // Emit the method name / sig and containing type of each GVM target method for the current interface method entry
+ vertex = nativeFormatWriter.GetTuple(vertex, nativeFormatWriter.GetUnsignedConstant((uint)gvmEntry.Value.Count));
+ foreach (MethodDesc implementationMethod in gvmEntry.Value)
+ {
+ nameAndSig = factory.NativeLayout.PlacedSignatureVertex(factory.NativeLayout.MethodNameAndSignatureVertex(implementationMethod));
+ typeId = _externalReferences.GetIndex(factory.NecessaryTypeSymbol(implementationMethod.OwningType));
+ vertex = nativeFormatWriter.GetTuple(
+ vertex,
+ nativeFormatWriter.GetUnsignedConstant((uint)nameAndSig.SavedVertex.VertexOffset),
+ nativeFormatWriter.GetUnsignedConstant(typeId));
+
+ // Emit the interface GVM slot details for each type that implements the interface methods
+ {
+ Debug.Assert(_interfaceImpls.ContainsKey(implementationMethod));
+
+ var ifaceImpls = _interfaceImpls[implementationMethod];
+
+ // First, emit how many types have method implementations for this interface method entry
+ vertex = nativeFormatWriter.GetTuple(vertex, nativeFormatWriter.GetUnsignedConstant((uint)ifaceImpls.Count));
+
+ // Emit each type that implements the interface method, and the interface signatures for the interfaces implemented by the type
+ foreach (var currentImpl in ifaceImpls)
+ {
+ TypeDesc implementationType = currentImpl.Key;
+
+ typeId = _externalReferences.GetIndex(factory.NecessaryTypeSymbol(implementationType));
+ vertex = nativeFormatWriter.GetTuple(vertex, nativeFormatWriter.GetUnsignedConstant(typeId));
+
+ // Emit information on which interfaces the current method entry provides implementations for
+ vertex = nativeFormatWriter.GetTuple(vertex, nativeFormatWriter.GetUnsignedConstant((uint)currentImpl.Value.Count));
+ foreach (var ifaceId in currentImpl.Value)
+ {
+ // Emit the signature of the current interface implemented by the method
+ Debug.Assert(((uint)ifaceId) < implementationType.RuntimeInterfaces.Length);
+ TypeDesc currentInterface = implementationType.RuntimeInterfaces[ifaceId];
+ var typeSig = factory.NativeLayout.PlacedSignatureVertex(factory.NativeLayout.TypeSignatureVertex(currentInterface));
+ vertex = nativeFormatWriter.GetTuple(vertex, nativeFormatWriter.GetUnsignedConstant((uint)typeSig.SavedVertex.VertexOffset));
+ }
+ }
+ }
+ }
+
+ int hashCode = callingMethod.OwningType.GetHashCode();
+ gvmHashtable.Append((uint)hashCode, gvmHashtableSection.Place(vertex));
+ }
+
+ // Zero out the dictionary so that we AV if someone tries to insert after we're done.
+ _interfaceGvmSlots = null;
+
+ MemoryStream stream = new MemoryStream();
+ nativeFormatWriter.Save(stream);
+ byte[] streamBytes = stream.ToArray();
+
+ _endSymbol.SetSymbolOffset(streamBytes.Length);
+
+ return new ObjectData(streamBytes, Array.Empty(), 1, new ISymbolNode[] { this, _endSymbol });
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/MethodCodeNode.cs b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/MethodCodeNode.cs
index fb45db873af..64c691e864e 100644
--- a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/MethodCodeNode.cs
+++ b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/MethodCodeNode.cs
@@ -107,6 +107,13 @@ protected override DependencyList ComputeNonRelocationBasedDependencies(NodeFact
dependencies = dependencies ?? new DependencyList();
dependencies.AddRange(exactMethodInstantiationDependencies);
}
+
+ if (_method.IsVirtual)
+ {
+ // Generic virtual methods dependency tracking
+ dependencies = dependencies ?? new DependencyList();
+ dependencies.Add(new DependencyListEntry(factory.GVMDependencies(_method), "GVM Dependencies Support"));
+ }
}
return dependencies;
diff --git a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/NodeFactory.cs b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/NodeFactory.cs
index 083017c3ab5..42d875fecf9 100644
--- a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/NodeFactory.cs
+++ b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/NodeFactory.cs
@@ -192,6 +192,16 @@ private void CreateNodeCaches()
return new FatFunctionPointerNode(method);
});
+ _gvmDependenciesNode = new NodeCache(method =>
+ {
+ return new GVMDependenciesNode(method);
+ });
+
+ _gvmTableEntries = new NodeCache(type =>
+ {
+ return new TypeGVMEntriesNode(type);
+ });
+
_shadowConcreteMethods = new NodeCache(method =>
{
return new ShadowConcreteMethodNode(method,
@@ -520,6 +530,18 @@ public IMethodNode FatFunctionPointer(MethodDesc method)
return _fatFunctionPointers.GetOrAdd(method);
}
+ private NodeCache _gvmDependenciesNode;
+ internal GVMDependenciesNode GVMDependencies(MethodDesc method)
+ {
+ return _gvmDependenciesNode.GetOrAdd(method);
+ }
+
+ private NodeCache _gvmTableEntries;
+ internal TypeGVMEntriesNode TypeGVMEntries(TypeDesc type)
+ {
+ return _gvmTableEntries.GetOrAdd(type);
+ }
+
private NodeCache _shadowConcreteMethods;
public IMethodNode ShadowConcreteMethod(MethodDesc method)
diff --git a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ReadyToRunHelperNode.cs b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ReadyToRunHelperNode.cs
index 477eef6dd80..ddd4f29292f 100644
--- a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ReadyToRunHelperNode.cs
+++ b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ReadyToRunHelperNode.cs
@@ -156,6 +156,27 @@ protected override DependencyList ComputeNonRelocationBasedDependencies(NodeFact
dependencyList.Add(factory.VirtualMethodUse((MethodDesc)_target), "ReadyToRun Virtual Method Address Load");
return dependencyList;
}
+ else if (_id == ReadyToRunHelperId.ResolveGenericVirtualMethod)
+ {
+ MethodDesc method = _target as MethodDesc;
+ Debug.Assert(method != null && method.HasInstantiation && method.IsVirtual);
+
+ DependencyList dependencyList = new DependencyList();
+
+ // GVM dependency tracking
+ dependencyList.Add(new DependencyListEntry(factory.GVMDependencies(method), "GVM LDToken Support"));
+
+ // GVM entry point
+ if (!method.IsAbstract)
+ {
+ // TODO: shared generic entry point
+
+ // Target method could be an interface GVM, and in that case won't have an entry point as a dependency
+ dependencyList.Add(new DependencyListEntry(factory.MethodEntrypoint(method), "R2RHelperNode GVM target method"));
+ }
+
+ return dependencyList;
+ }
else
{
return null;
diff --git a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/TypeGVMEntriesNode.cs b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/TypeGVMEntriesNode.cs
new file mode 100644
index 00000000000..4a33b9eaebb
--- /dev/null
+++ b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/TypeGVMEntriesNode.cs
@@ -0,0 +1,120 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+
+using Internal.TypeSystem;
+using ILCompiler.DependencyAnalysisFramework;
+
+namespace ILCompiler.DependencyAnalysis
+{
+ ///
+ /// This node is used for GVM dependency analysis and GVM tables building. Given an input
+ /// type, this node can scan the type for a list of GVM table entries, and compute their dependencies.
+ ///
+ internal sealed class TypeGVMEntriesNode : DependencyNodeCore
+ {
+ internal class TypeGVMEntryInfo
+ {
+ public TypeGVMEntryInfo(MethodDesc callingMethod, MethodDesc implementationMethod, TypeDesc implementationType)
+ {
+ CallingMethod = callingMethod;
+ ImplementationMethod = implementationMethod;
+ ImplementationType = implementationType;
+ }
+ public MethodDesc CallingMethod { get; private set; }
+ public MethodDesc ImplementationMethod { get; private set; }
+ public TypeDesc ImplementationType { get; private set; }
+ }
+
+ private TypeDesc _associatedType;
+ private DependencyList _staticDependencies;
+
+ public TypeGVMEntriesNode(TypeDesc associatedType)
+ {
+ Debug.Assert(!associatedType.IsRuntimeDeterminedSubtype);
+ Debug.Assert(TypeNeedsGVMTableEntries(associatedType));
+ _associatedType = associatedType;
+ }
+
+ public override bool HasConditionalStaticDependencies => false;
+ public override bool HasDynamicDependencies => false;
+ public override bool InterestingForDynamicDependencyAnalysis => false;
+ public override bool StaticDependenciesAreComputed => true;
+ protected override string GetName() => "__TypeGVMEntriesNode_" + NodeFactory.NameMangler.GetMangledTypeName(_associatedType);
+ public override IEnumerable GetConditionalStaticDependencies(NodeFactory context)
+ {
+ return Array.Empty();
+ }
+ public override IEnumerable SearchDynamicDependencies(List> markedNodes, int firstNode, NodeFactory context)
+ {
+ return Array.Empty();
+ }
+
+ public override IEnumerable GetStaticDependencies(NodeFactory context)
+ {
+ if (_staticDependencies == null)
+ {
+ _staticDependencies = new DependencyList();
+
+ foreach(var entry in ScanForGenericVirtualMethodEntries())
+ _staticDependencies.AddRange(GenericVirtualMethodTableNode.GetGenericVirtualMethodImplementationDependencies(context, entry.CallingMethod, entry.ImplementationMethod));
+
+ foreach (var entry in ScanForInterfaceGenericVirtualMethodEntries())
+ _staticDependencies.AddRange(InterfaceGenericVirtualMethodTableNode.GetGenericVirtualMethodImplementationDependencies(context, entry.CallingMethod, entry.ImplementationType, entry.ImplementationMethod));
+ }
+
+ return _staticDependencies;
+ }
+
+ public static bool TypeNeedsGVMTableEntries(TypeDesc type)
+ {
+ // Only non-interface deftypes can have entries for their GVMs in the GVM hashtables.
+ // Interface GVM entries are computed for types that implemenent the interface (not for the interface on its own)
+ if(!type.IsDefType || type.IsInterface)
+ return false;
+
+ return type.HasGenericVirtualMethod();
+ }
+
+ public IEnumerable ScanForGenericVirtualMethodEntries()
+ {
+ foreach (var method in _associatedType.GetMethods())
+ {
+ if (!method.IsVirtual || !method.HasInstantiation)
+ continue;
+
+ MethodDesc slotDecl = MetadataVirtualMethodAlgorithm.FindSlotDefiningMethodForVirtualMethod(method);
+ Debug.Assert(slotDecl != null);
+ yield return new TypeGVMEntryInfo(slotDecl, method, null);
+ }
+ }
+
+ public IEnumerable ScanForInterfaceGenericVirtualMethodEntries()
+ {
+ foreach (var iface in _associatedType.RuntimeInterfaces)
+ {
+ foreach (var method in iface.GetMethods())
+ {
+ if (!method.HasInstantiation || method.Signature.IsStatic)
+ continue;
+
+ MethodDesc slotDecl;
+ TypeDesc currentType = _associatedType;
+ do
+ {
+ slotDecl = currentType.ResolveInterfaceMethodToVirtualMethodOnType(method);
+ currentType = currentType.BaseType;
+ }
+ while (slotDecl == null && currentType != null);
+
+ Debug.Assert(slotDecl != null);
+ yield return new TypeGVMEntryInfo(method, slotDecl, _associatedType);
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/VTableSliceNode.cs b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/VTableSliceNode.cs
index 666b8569aca..f82e6f4732e 100644
--- a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/VTableSliceNode.cs
+++ b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/VTableSliceNode.cs
@@ -60,6 +60,10 @@ public EagerlyBuiltVTableSliceNode(TypeDesc type)
if (!method.IsVirtual)
continue;
+ // GVMs are not emitted in the type's vtable.
+ if (method.HasInstantiation)
+ continue;
+
slots.Add(method);
}
@@ -121,6 +125,8 @@ public override IReadOnlyList Slots
public void AddEntry(NodeFactory factory, MethodDesc virtualMethod)
{
+ // GVMs are not emitted in the type's vtable.
+ Debug.Assert(!virtualMethod.HasInstantiation);
Debug.Assert(virtualMethod.IsVirtual);
#if DEBUG
Debug.Assert(!_slotsCommitted);
diff --git a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/VirtualMethodUseNode.cs b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/VirtualMethodUseNode.cs
index 259207ec60f..7dff15fe339 100644
--- a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/VirtualMethodUseNode.cs
+++ b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/VirtualMethodUseNode.cs
@@ -32,7 +32,7 @@ protected override void OnMarked(NodeFactory factory)
// If the VTable slice is getting built on demand, the fact that the virtual method is used means
// that the slot is used.
var lazyVTableSlice = factory.VTable(_decl.OwningType) as LazilyBuiltVTableSliceNode;
- if (lazyVTableSlice != null)
+ if (lazyVTableSlice != null && !_decl.HasInstantiation)
lazyVTableSlice.AddEntry(factory, _decl);
}
diff --git a/src/ILCompiler.Compiler/src/Compiler/MetadataGeneration.cs b/src/ILCompiler.Compiler/src/Compiler/MetadataGeneration.cs
index ff39e1b514c..f1da9479109 100644
--- a/src/ILCompiler.Compiler/src/Compiler/MetadataGeneration.cs
+++ b/src/ILCompiler.Compiler/src/Compiler/MetadataGeneration.cs
@@ -43,6 +43,7 @@ public class MetadataGeneration
private HashSet _typesWithEETypesGenerated = new HashSet();
private HashSet _methodDefinitionsGenerated = new HashSet();
private HashSet _methodsGenerated = new HashSet();
+ private List _typeGVMEntries = new List();
private Dictionary _dynamicInvokeThunks = new Dictionary();
@@ -104,7 +105,13 @@ public void AddToReadyToRunHeader(ReadyToRunHeaderNode header)
var genericsHashtable = new GenericsHashtableNode(nativeReferencesTableNode);
header.Add(BlobIdToReadyToRunSection(ReflectionMapBlob.GenericsHashtable), genericsHashtable, genericsHashtable, genericsHashtable.EndSymbol);
- // This one should go last
+ var genericVirtualMethodTableNode = new GenericVirtualMethodTableNode(commonFixupsTableNode);
+ header.Add(BlobIdToReadyToRunSection(ReflectionMapBlob.GenericVirtualMethodTable), genericVirtualMethodTableNode, genericVirtualMethodTableNode, genericVirtualMethodTableNode.EndSymbol);
+
+ var interfaceGenericVirtualMethodTableNode = new InterfaceGenericVirtualMethodTableNode(commonFixupsTableNode);
+ header.Add(BlobIdToReadyToRunSection(ReflectionMapBlob.InterfaceGenericVirtualMethodTable), interfaceGenericVirtualMethodTableNode, interfaceGenericVirtualMethodTableNode, interfaceGenericVirtualMethodTableNode.EndSymbol);
+
+ // The external references tables should go last
header.Add(BlobIdToReadyToRunSection(ReflectionMapBlob.CommonFixupsTable), commonFixupsTableNode, commonFixupsTableNode, commonFixupsTableNode.EndSymbol);
header.Add(BlobIdToReadyToRunSection(ReflectionMapBlob.NativeReferences), nativeReferencesTableNode, nativeReferencesTableNode, nativeReferencesTableNode.EndSymbol);
}
@@ -143,6 +150,12 @@ private void Graph_NewMarkedNode(DependencyNodeCore obj)
{
_cctorContextsGenerated.Add(nonGcStaticSectionNode);
}
+
+ var gvmEntryNode = obj as TypeGVMEntriesNode;
+ if (gvmEntryNode != null)
+ {
+ _typeGVMEntries.Add(gvmEntryNode);
+ }
}
public bool HasReflectionInvokeStub(MethodDesc method)
@@ -384,6 +397,11 @@ internal IEnumerable GetArrayTypeMapping()
return _arrayTypesGenerated;
}
+ internal IEnumerable GetTypeGVMEntries()
+ {
+ return _typeGVMEntries;
+ }
+
internal IEnumerable GetCompiledMethods()
{
return _methodsGenerated;
diff --git a/src/ILCompiler.Compiler/src/Compiler/TypeExtensions.cs b/src/ILCompiler.Compiler/src/Compiler/TypeExtensions.cs
index 93286b1cdf9..fc6c804c365 100644
--- a/src/ILCompiler.Compiler/src/Compiler/TypeExtensions.cs
+++ b/src/ILCompiler.Compiler/src/Compiler/TypeExtensions.cs
@@ -84,5 +84,20 @@ public static bool AcquiresInstMethodTableFromThis(this MethodDesc method)
!method.Signature.IsStatic &&
!method.ImplementationType.IsValueType;
}
+
+
+ ///
+ /// Gets a value indicating whether this type has any generic virtual methods.
+ ///
+ public static bool HasGenericVirtualMethod(this TypeDesc type)
+ {
+ foreach (var method in type.GetAllMethods())
+ {
+ if (method.IsVirtual && method.HasInstantiation)
+ return true;
+ }
+
+ return false;
+ }
}
}
diff --git a/src/ILCompiler.Compiler/src/ILCompiler.Compiler.csproj b/src/ILCompiler.Compiler/src/ILCompiler.Compiler.csproj
index 2330e0a02f3..71e22fd64b5 100644
--- a/src/ILCompiler.Compiler/src/ILCompiler.Compiler.csproj
+++ b/src/ILCompiler.Compiler/src/ILCompiler.Compiler.csproj
@@ -102,13 +102,17 @@
+
+
+
+
+
-
@@ -131,7 +135,7 @@
-
+
diff --git a/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeLoaderEnvironment.SignatureParsing.cs b/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeLoaderEnvironment.SignatureParsing.cs
index aafd0ee2675..8ceef7f4cbe 100644
--- a/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeLoaderEnvironment.SignatureParsing.cs
+++ b/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeLoaderEnvironment.SignatureParsing.cs
@@ -91,8 +91,7 @@ public bool TryGetMethodNameAndSignatureFromNativeLayoutSignature(RuntimeSignatu
nameAndSignature = null;
NativeReader reader = GetNativeLayoutInfoReader(signature.ModuleHandle);
- uint offset = signature.NativeLayoutOffset;
- NativeParser parser = new NativeParser(reader, offset);
+ NativeParser parser = new NativeParser(reader, signature.NativeLayoutOffset);
if (parser.IsNull)
return false;
@@ -109,8 +108,7 @@ public bool TryGetMethodNameAndSignaturePointersFromNativeLayoutSignature(IntPtr
methodSig = default(RuntimeSignature);
NativeReader reader = GetNativeLayoutInfoReader(module);
- uint offset = methodNameAndSigToken;
- NativeParser parser = new NativeParser(reader, offset);
+ NativeParser parser = new NativeParser(reader, methodNameAndSigToken);
if (parser.IsNull)
return false;