|
// 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 Internal.Text;
using Internal.TypeSystem;
using Internal.NativeFormat;
namespace ILCompiler.DependencyAnalysis
{
/// <summary>
/// Represents a map of generic virtual method implementations.
/// </summary>
public sealed class GenericVirtualMethodTableNode : ObjectNode, ISymbolDefinitionNode
{
private ExternalReferencesTableNode _externalReferences;
private Dictionary<MethodDesc, Dictionary<TypeDesc, MethodDesc>> _gvmImplementations;
public GenericVirtualMethodTableNode(ExternalReferencesTableNode externalReferences)
{
_externalReferences = externalReferences;
_gvmImplementations = new Dictionary<MethodDesc, Dictionary<TypeDesc, MethodDesc>>();
}
public void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb)
{
sb.Append(nameMangler.CompilationUnitPrefix).Append("__gvm_table"u8);
}
public int Offset => 0;
public override bool IsShareable => false;
public override ObjectNodeSection GetSection(NodeFactory factory) => _externalReferences.GetSection(factory);
public override bool StaticDependenciesAreComputed => true;
protected override string GetName(NodeFactory factory) => this.GetMangledName(factory.NameMangler);
/// <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 void GetGenericVirtualMethodImplementationDependencies(ref DependencyList dependencies, NodeFactory factory, MethodDesc callingMethod, MethodDesc implementationMethod)
{
Debug.Assert(!callingMethod.OwningType.IsInterface);
// Compute the open method signatures
MethodDesc openCallingMethod = callingMethod.GetTypicalMethodDefinition();
MethodDesc openImplementationMethod = implementationMethod.GetTypicalMethodDefinition();
factory.MetadataManager.GetNativeLayoutMetadataDependencies(ref dependencies, factory, openCallingMethod);
dependencies.Add(factory.NecessaryTypeSymbol(openCallingMethod.OwningType), "Owning type of GVM declaration");
factory.MetadataManager.GetNativeLayoutMetadataDependencies(ref dependencies, factory, openImplementationMethod);
dependencies.Add(factory.NecessaryTypeSymbol(openImplementationMethod.OwningType), "Owning type of GVM implementation");
}
private void AddGenericVirtualMethodImplementation(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 (!_gvmImplementations.ContainsKey(openCallingMethod))
_gvmImplementations[openCallingMethod] = new Dictionary<TypeDesc, MethodDesc>();
_gvmImplementations[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 ISymbolDefinitionNode[] { 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(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 _gvmImplementations)
{
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));
int callingMethodToken = factory.MetadataManager.GetMetadataHandleForMethod(factory, callingMethod);
vertex = nativeFormatWriter.GetTuple(vertex, nativeFormatWriter.GetUnsignedConstant((uint)callingMethodToken));
int implementationMethodToken = factory.MetadataManager.GetMetadataHandleForMethod(factory, implementationMethod);
vertex = nativeFormatWriter.GetTuple(vertex, nativeFormatWriter.GetUnsignedConstant((uint)implementationMethodToken));
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.
_gvmImplementations = null;
byte[] streamBytes = nativeFormatWriter.Save();
return new ObjectData(streamBytes, Array.Empty<Relocation>(), 1, new ISymbolDefinitionNode[] { this });
}
protected internal override int Phase => (int)ObjectNodePhase.Ordered;
public override int ClassCode => (int)ObjectNodeOrder.GenericVirtualMethodTableNode;
}
}
|