Skip to content
This repository has been archived by the owner on Nov 1, 2020. It is now read-only.

Commit

Permalink
GVM dependency tracking analysis and GVM table building logic:
Browse files Browse the repository at this point in the history
    1) GenericVirtualMethodTableNode: Node that builds the GVM hashtable blob for GVMs on classes
    2) InterfaceGenericVirtualMethodTableNode: Node that builds the GVM hashtable blob for GVMs on interfaces
    3) MethodLdTokenNode: Analysis node that tracks dependencies of GVMs for derived types and variant interfaces
    4) TypeGVMEntriesNode: Analysis node that scans an input type and computes the list of GVM entries that would be added to the hashtables. Also used to track dependencies of GVM table entries (ex: native layout signatures of methods).

Other
    1) New helper method to compute whether a type has generic virtual methods

CR Feedback and few fixes
  • Loading branch information
fadimounir committed Jan 19, 2017
1 parent 1cc17e1 commit 362bf7c
Show file tree
Hide file tree
Showing 14 changed files with 768 additions and 8 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down Expand Up @@ -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;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -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
{
/// <summary>
/// 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.
/// </summary>
internal class GVMDependenciesNode : DependencyNodeCore<NodeFactory>
{
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<DependencyListEntry> GetStaticDependencies(NodeFactory context)
{
return Array.Empty<DependencyListEntry>();
}
public override IEnumerable<CombinedDependencyListEntry> GetConditionalStaticDependencies(NodeFactory context)
{
return Array.Empty<CombinedDependencyListEntry>();
}

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<CombinedDependencyListEntry> SearchDynamicDependencies(List<DependencyNodeCore<NodeFactory>> markedNodes, int firstNode, NodeFactory factory)
{
Debug.Assert(_method.IsVirtual && _method.HasInstantiation);

List<CombinedDependencyListEntry> dynamicDependencies = new List<CombinedDependencyListEntry>();

for (int i = firstNode; i < markedNodes.Count; i++)
{
DependencyNodeCore<NodeFactory> 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<CombinedDependencyListEntry> 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<void>?
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
}
}
}
}
Original file line number Diff line number Diff line change
@@ -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
{
/// <summary>
/// Represents a map between reflection metadata and generated method bodies.
/// </summary>
internal sealed class GenericVirtualMethodTableNode : ObjectNode, ISymbolNode
{
private ObjectAndOffsetSymbolNode _endSymbol;
private ExternalReferencesTableNode _externalReferences;
private Dictionary<MethodDesc, Dictionary<TypeDesc, MethodDesc>> _gvmImplemenations;

public GenericVirtualMethodTableNode(ExternalReferencesTableNode externalReferences)
{
_endSymbol = new ObjectAndOffsetSymbolNode(this, 0, "__gvm_table_End", true);
_externalReferences = externalReferences;
_gvmImplemenations = new Dictionary<MethodDesc, Dictionary<TypeDesc, MethodDesc>>();
}

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<TypeDesc, MethodDesc>();

_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<byte>(), Array.Empty<Relocation>(), 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<Relocation>(), 1, new ISymbolNode[] { this, _endSymbol });
}
}
}
Loading

0 comments on commit 362bf7c

Please sign in to comment.