This repository has been archived by the owner on Nov 1, 2020. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 509
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
GVM dependency tracking analysis and GVM table building logic:
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
Showing
14 changed files
with
768 additions
and
8 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
178 changes: 178 additions & 0 deletions
178
src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/GVMDependenciesNode.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
} | ||
} | ||
} | ||
} |
144 changes: 144 additions & 0 deletions
144
src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/GenericVirtualMethodTableNode.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 }); | ||
} | ||
} | ||
} |
Oops, something went wrong.