Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false)
// the most popular targets.
ISymbolDefinitionNode firstSymbol = null;
var relocOccurences = new Dictionary<ISymbolNode, int>();
foreach (ObjectNode.ObjectData o in factory.MetadataManager.GetDehydratableData())
foreach (ObjectData o in factory.MetadataManager.GetDehydratableData())
{
firstSymbol ??= o.DefinedSymbols[0];

Expand Down Expand Up @@ -209,17 +209,14 @@ public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false)
{
Debug.Assert(sourcePosition == reloc.Offset);

#if DEBUG
long delta;
unsafe
{
fixed (byte* pData = &o.Data[reloc.Offset])
{
long delta = Relocation.ReadValue(reloc.RelocType, pData);
// Extra work needed to be able to encode/decode relocs with deltas
Debug.Assert(delta == 0);
delta = Relocation.ReadValue(reloc.RelocType, pData);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just checking my understanding - before this change, all of the object data we were dehydrating had zeros in place of the relocs, but now some other component is writing an offset there, right?
Is this the place where the offset is written?

objData.EmitReloc(interfaceType, RelocType.IMAGE_REL_BASED_RELPTR32,
(int)InterfaceDispatchCellCachePointerFlags.CachePointerIsInterfaceRelativePointer);

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep, this matches how it's represented in the object file as well. The relocation is basically an addend - if the value is all zeros, the effect is that the address of the relocation target is written. But if it's non-zero, we get a "displaced" pointer to the target of the relocation (the displacement is the value that was originally stored there). If we know the target is going to be aligned, we sometimes use displacement as a way to get "spare bits". Those bits get masked off before dereferencing the pointer at runtime and can indicate the thing the pointer points to (like in the above case, they indicate CachePointerIsInterfaceRelativePointer).

}
}
#endif

// The size of the relocation is included in the ObjectData bytes. Skip the literal bytes.
sourcePosition += Relocation.GetSize(reloc.RelocType);
Expand All @@ -228,7 +225,7 @@ public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false)
if (target is ISymbolNodeWithLinkage withLinkage)
target = withLinkage.NodeForLinkage(factory);

if (relocs.TryGetValue(target, out int targetIndex))
if (delta == 0 && relocs.TryGetValue(target, out int targetIndex))
{
// Reloc goes through the lookup table
int relocCommand = reloc.RelocType switch
Expand All @@ -252,7 +249,7 @@ public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false)
bool hasNextReloc;
do
{
builder.EmitReloc(target, RelocType.IMAGE_REL_BASED_RELPTR32);
builder.EmitReloc(target, RelocType.IMAGE_REL_BASED_RELPTR32, checked((int)delta));
numRelocs++;
hasNextReloc = false;

Expand All @@ -276,8 +273,16 @@ public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false)
if (nextTarget is ISymbolNodeWithLinkage nextTargetWithLinkage)
nextTarget = nextTargetWithLinkage.NodeForLinkage(factory);

unsafe
{
fixed (byte* pData = &o.Data[reloc.Offset])
{
delta = Relocation.ReadValue(reloc.RelocType, pData);
}
}

// We don't have a short code for it?
if (relocs.ContainsKey(nextTarget))
if (delta == 0 && relocs.ContainsKey(nextTarget))
break;

// This relocation is good - we'll generate it as part of the run
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

namespace ILCompiler.DependencyAnalysis
{
public class InterfaceDispatchCellNode : EmbeddedObjectNode, ISymbolDefinitionNode
public sealed class InterfaceDispatchCellNode : EmbeddedObjectNode, ISymbolDefinitionNode
{
private readonly MethodDesc _targetMethod;
private readonly string _callSiteIdentifier;
Expand Down Expand Up @@ -113,11 +113,6 @@ public override void EncodeData(ref ObjectDataBuilder objData, NodeFactory facto
}
}

protected override void OnMarked(NodeFactory factory)
{
factory.InterfaceDispatchCellSection.AddEmbeddedObject(this);
}

public override int ClassCode => -2023802120;

public override int CompareToImpl(ISortableNode other, CompilerComparer comparer)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

using System;
using System.Collections.Generic;

using Internal.Text;
using Internal.TypeSystem;

using Debug = System.Diagnostics.Debug;
Expand All @@ -14,17 +14,15 @@ namespace ILCompiler.DependencyAnalysis
/// Represents a section of the executable where interface dispatch cells and their slot information
/// is stored.
/// </summary>
public class InterfaceDispatchCellSectionNode : ArrayOfEmbeddedDataNode<InterfaceDispatchCellNode>
public class InterfaceDispatchCellSectionNode : DehydratableObjectNode, ISymbolDefinitionNode
{
public InterfaceDispatchCellSectionNode(NodeFactory factory)
: base("__InterfaceDispatchCellSection_Start", "__InterfaceDispatchCellSection_End", new DispatchCellComparer(factory))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need to emit the end symbol separately now? What are the end symbols used for in general - is it just bookkeeping to make the exe easier to inspect? Or are they used at runtime?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We didn't need an end symbol for this one - the structure of the base class just required one.

We have some tables where the start and end symbols are used to figure out the length of the table. But dispatch cells are individually addressable and there's no code to enumerate all the dispatch cells (we don't have a purpose for such code) - end symbol is not needed.

{
}

protected override void GetElementDataForNodes(ref ObjectDataBuilder builder, NodeFactory factory, bool relocsOnly)
protected override ObjectData GetDehydratableData(NodeFactory factory, bool relocsOnly)
{
if (relocsOnly)
return;
return new ObjectData(Array.Empty<byte>(), Array.Empty<Relocation>(), 1, Array.Empty<ISymbolDefinitionNode>());

var builder = new ObjectDataBuilder(factory, relocsOnly);
builder.AddSymbol(this);

// The interface dispatch cell has an alignment requirement of 2 * [Pointer size] as part of the
// synchronization mechanism of the two values in the runtime.
Expand All @@ -48,7 +46,7 @@ protected override void GetElementDataForNodes(ref ObjectDataBuilder builder, No
//
int runLength = 0;
int currentSlot = NoSlot;
foreach (InterfaceDispatchCellNode node in NodesList)
foreach (InterfaceDispatchCellNode node in new SortedSet<InterfaceDispatchCellNode>(factory.MetadataManager.GetInterfaceDispatchCells(), new DispatchCellComparer(factory)))
{
MethodDesc targetMethod = node.TargetMethod;
int targetSlot = VirtualMethodSlotHelper.GetVirtualMethodSlot(factory, targetMethod, targetMethod.OwningType);
Expand Down Expand Up @@ -83,10 +81,23 @@ protected override void GetElementDataForNodes(ref ObjectDataBuilder builder, No
builder.EmitZeroPointer();
builder.EmitNaturalInt(currentSlot);
}

return builder.ToObjectData();
}

public void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb)
=> sb.Append(nameMangler.CompilationUnitPrefix).Append("__InterfaceDispatchCellSection_Start");
protected override ObjectNodeSection GetDehydratedSection(NodeFactory factory) => ObjectNodeSection.DataSection;
protected override string GetName(NodeFactory factory) => this.GetMangledName(factory.NameMangler);

public override int ClassCode => -1389343;

public int Offset => 0;

public override bool IsShareable => false;

public override bool StaticDependenciesAreComputed => true;

/// <summary>
/// Comparer that groups interface dispatch cells by their slot number.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,6 @@ public NodeFactory(
MetadataManager = metadataManager;
LazyGenericsPolicy = lazyGenericsPolicy;
_importedNodeProvider = importedNodeProvider;
InterfaceDispatchCellSection = new InterfaceDispatchCellSectionNode(this);
PreinitializationManager = preinitializationManager;
}

Expand Down Expand Up @@ -1129,7 +1128,7 @@ public string GetSymbolAlternateName(ISymbolNode node)

internal ModuleInitializerListNode ModuleInitializerList = new ModuleInitializerListNode();

public InterfaceDispatchCellSectionNode InterfaceDispatchCellSection { get; }
public InterfaceDispatchCellSectionNode InterfaceDispatchCellSection = new InterfaceDispatchCellSectionNode();

public ReadyToRunHeaderNode ReadyToRunHeader;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ public abstract class MetadataManager : ICompilationRootProvider
protected readonly ManifestResourceBlockingPolicy _resourceBlockingPolicy;
protected readonly DynamicInvokeThunkGenerationPolicy _dynamicInvokeThunkGenerationPolicy;

private readonly List<InterfaceDispatchCellNode> _interfaceDispatchCells = new List<InterfaceDispatchCellNode>();
private readonly SortedSet<NonGCStaticsNode> _cctorContextsGenerated = new SortedSet<NonGCStaticsNode>(CompilerComparer.Instance);
private readonly SortedSet<TypeDesc> _typesWithEETypesGenerated = new SortedSet<TypeDesc>(TypeSystemComparer.Instance);
private readonly SortedSet<TypeDesc> _typesWithConstructedEETypesGenerated = new SortedSet<TypeDesc>(TypeSystemComparer.Instance);
Expand Down Expand Up @@ -258,6 +259,11 @@ protected virtual void Graph_NewMarkedNode(DependencyNodeCore<NodeFactory> obj)
_reflectableMethods.Add(method);
}

if (obj is InterfaceDispatchCellNode dispatchCell)
{
_interfaceDispatchCells.Add(dispatchCell);
}

if (obj is StructMarshallingDataNode structMarshallingDataNode)
{
_typesWithStructMarshalling.Add(structMarshallingDataNode.Type);
Expand Down Expand Up @@ -654,6 +660,11 @@ public IEnumerable<MetadataMapping<MethodDesc>> GetStackTraceMapping(NodeFactory
return _stackTraceMappings;
}

internal IEnumerable<InterfaceDispatchCellNode> GetInterfaceDispatchCells()
{
return _interfaceDispatchCells;
}

internal IEnumerable<NonGCStaticsNode> GetCctorContextMapping()
{
return _cctorContextsGenerated;
Expand Down