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

Commit

Permalink
Merge pull request #2521 from fadimounir/gvm_tables
Browse files Browse the repository at this point in the history
Generic Virtual Methods Hashtables
  • Loading branch information
Fadi Hanna authored Jan 24, 2017
2 parents 8811353 + 3d79516 commit e7d315e
Show file tree
Hide file tree
Showing 17 changed files with 863 additions and 27 deletions.
20 changes: 20 additions & 0 deletions src/Common/src/TypeSystem/Common/TypeSystemHelpers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -301,5 +301,25 @@ public static MethodDesc InstantiateAsOpen(this MethodDesc method)

return method;
}

/// <summary>
/// Scan the type and its base types for an implementation of an interface method. Returns null if no
/// implementation is found.
/// </summary>
public static MethodDesc ResolveInterfaceMethodTarget(this TypeDesc thisType, MethodDesc interfaceMethodToResolve)
{
Debug.Assert(interfaceMethodToResolve.OwningType.IsInterface);

MethodDesc result = null;
TypeDesc currentType = thisType;
do
{
result = currentType.ResolveInterfaceMethodToVirtualMethodOnType(interfaceMethodToResolve);
currentType = currentType.BaseType;
}
while (result == null && currentType != null);

return result;
}
}
}
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,182 @@
// 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 = potentialOverrideType.ResolveInterfaceMethodTarget(genericDefinition);
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);

if (methodDef.IsAbstract)
return;

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)
{
MethodDesc canonMethodTarget = derivedMethodInstantiation.GetCanonMethodTarget(CanonicalFormKind.Specific);

bool getUnboxingStub = (derivedMethodInstantiation.OwningType.IsValueType || derivedMethodInstantiation.OwningType.IsEnum);
dynamicDependencies.Add(new CombinedDependencyListEntry(factory.MethodEntrypoint(canonMethodTarget, getUnboxingStub), null, "DerivedMethodInstantiation"));

if (canonMethodTarget != derivedMethodInstantiation)
{
// Dependency includes the generic method dictionary of the instantiation
// TODO: detect large recursive generics and fallback to USG templates
Debug.Assert(!derivedMethodInstantiation.IsCanonicalMethod(CanonicalFormKind.Any));
dynamicDependencies.Add(new CombinedDependencyListEntry(factory.MethodGenericDictionary(derivedMethodInstantiation), null, "DerivedMethodInstantiation dictionary"));
}
}
else
{
// TODO: universal generics
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
// 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();

/// <summary>
/// Helper method to compute the dependencies that would be needed by a hashtable entry for a GVM call.
/// This helper is used by the TypeGVMEntriesNode, which is used by the dependency analysis to compute the
/// GVM hashtable entries for the compiled types.
/// The dependencies returned from this function will be reported as static dependencies of the TypeGVMEntriesNode,
/// which we create for each type that has generic virtual methods.
/// </summary>
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 calling method signature"));
dependencyNodes.Add(new DependencyListEntry(factory.NativeLayout.PlacedSignatureVertex(openImplementationMethodNameAndSig), "gvm table implementation method 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 e7d315e

Please sign in to comment.