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 @@ -266,6 +266,22 @@ private static unsafe void RehydrateData(IntPtr dehydratedData, int length)
WriteRelPtr32(pDest, ReadRelPtr32(pFixups + payload));
pDest += sizeof(int);
break;
case DehydratedDataCommand.InlinePtrReloc:
while (payload-- > 0)
{
*(void**)pDest = ReadRelPtr32(pCurrent);
pDest += sizeof(void*);
pCurrent += sizeof(int);
}
break;
case DehydratedDataCommand.InlineRelPtr32Reloc:
while (payload-- > 0)
{
WriteRelPtr32(pDest, ReadRelPtr32(pCurrent));
pDest += sizeof(int);
pCurrent += sizeof(int);
}
break;
}
}

Expand Down
46 changes: 6 additions & 40 deletions src/coreclr/tools/Common/Internal/Runtime/DehydratedData.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.

using System;

using Debug = System.Diagnostics.Debug;

namespace Internal.Runtime
Expand All @@ -21,13 +22,15 @@ internal static class DehydratedDataCommand
public const byte ZeroFill = 0x01;
public const byte RelPtr32Reloc = 0x02;
public const byte PtrReloc = 0x03;
public const byte InlineRelPtr32Reloc = 0x04;
public const byte InlinePtrReloc = 0x05;

private const byte DehydratedDataCommandMask = 0x03;
private const int DehydratedDataCommandPayloadShift = 2;
private const byte DehydratedDataCommandMask = 0x07;
private const int DehydratedDataCommandPayloadShift = 3;

private const int MaxRawShortPayload = (1 << (8 - DehydratedDataCommandPayloadShift)) - 1;
private const int MaxExtraPayloadBytes = 3;
private const int MaxShortPayload = MaxRawShortPayload - MaxExtraPayloadBytes;
public const int MaxShortPayload = MaxRawShortPayload - MaxExtraPayloadBytes;

public static byte EncodeShort(int command, int commandData)
{
Expand Down Expand Up @@ -77,42 +80,5 @@ public static int Encode(int command, int commandData, byte[] buffer)

return pB + 1;
}

#if false
static void Main()
{
int command, payload;

byte[] buf = new byte[5];
Debug.Assert(Encode(1, 0, buf) == 1);
Debug.Assert(buf[0] == 1);
Debug.Assert(D(buf, out command, out payload) == 1 && command == 1 && payload == 0);
Debug.Assert(Encode(1, 1, buf) == 1);
Debug.Assert(buf[0] == (1 | (1 << DehydratedDataCommandPayloadShift)));
Debug.Assert(D(buf, out command, out payload) == 1 && command == 1 && payload == 1);
Debug.Assert(Encode(1, 60, buf) == 1);
Debug.Assert(buf[0] == (1 | (60 << DehydratedDataCommandPayloadShift)));
Debug.Assert(D(buf, out command, out payload) == 1 && command == 1 && payload == 60);
Debug.Assert(Encode(1, 61, buf) == 2);
Debug.Assert(buf[0] == (1 | ((MaxShortPayload + 1) << DehydratedDataCommandPayloadShift)));
Debug.Assert(buf[1] == 1);
Debug.Assert(D(buf, out command, out payload) == 2 && command == 1 && payload == 61);

Debug.Assert(Encode(3, 256, buf) == 2);
Debug.Assert(D(buf, out command, out payload) == 2 && command == 3 && payload == 256);
Debug.Assert(Encode(3, 6500, buf) == 3);
Debug.Assert(D(buf, out command, out payload) == 3 && command == 3 && payload == 6500);
Debug.Assert(Encode(3, 65000, buf) == 3);
Debug.Assert(D(buf, out command, out payload) == 3 && command == 3 && payload == 65000);
Debug.Assert(Encode(3, 100000, buf) == 4);
Debug.Assert(D(buf, out command, out payload) == 4 && command == 3 && payload == 100000);

static unsafe int D(byte[] bytes, out int command, out int payload)
{
fixed (byte* pBytes = bytes)
return (int)(Decode(pBytes, out command, out payload) - pBytes);
}
}
#endif
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -92,8 +92,24 @@ public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false)
// Sort the reloc targets and create reloc lookup table.
KeyValuePair<ISymbolNode, int>[] relocSort = new List<KeyValuePair<ISymbolNode, int>>(relocOccurences).ToArray();
Array.Sort(relocSort, (x, y) => y.Value.CompareTo(x.Value));
int lastProfitableReloc = 0;
for (int i = 0; i < relocSort.Length; i++)
{
// Stop when we reach rarely referenced targets. Those will be inlined instead of being indirected
// through the table. Lookup table entry costs 4 bytes, a single reference to a rarely used reloc
// in the lookup table costs about 3 bytes. Inline reference to a reloc costs 5 bytes.
// It might be profitable from cache line utilization perspective at runtime to bump this number
// even higher to avoid using the lookup table as much as possible.
if (relocSort[i].Value < 3)
{
lastProfitableReloc = i - 1;
break;
}

relocSort[i] = new KeyValuePair<ISymbolNode, int>(relocSort[i].Key, i);
}
if (lastProfitableReloc > 0)
Array.Resize(ref relocSort, lastProfitableReloc);
var relocs = new Dictionary<ISymbolNode, int>(relocSort);

// Walk all the ObjectDatas and generate the dehydrated instruction stream.
Expand Down Expand Up @@ -191,6 +207,8 @@ public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false)
// Generate the next relocation if there's any.
if (reloc.Target != null)
{
Debug.Assert(sourcePosition == reloc.Offset);

#if DEBUG
unsafe
{
Expand All @@ -210,17 +228,75 @@ public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false)
if (target is ISymbolNodeWithLinkage withLinkage)
target = withLinkage.NodeForLinkage(factory);

int targetIndex = relocs[target];
if (relocs.TryGetValue(target, out int targetIndex))
{
// Reloc goes through the lookup table
int relocCommand = reloc.RelocType switch
{
RelocType.IMAGE_REL_BASED_DIR64 => DehydratedDataCommand.PtrReloc,
RelocType.IMAGE_REL_BASED_RELPTR32 => DehydratedDataCommand.RelPtr32Reloc,
_ => throw new NotSupportedException(),
};

int relocCommand = reloc.RelocType switch
int written = DehydratedDataCommand.Encode(relocCommand, targetIndex, buff);
builder.EmitBytes(buff, 0, written);
}
else
{
RelocType.IMAGE_REL_BASED_DIR64 => DehydratedDataCommand.PtrReloc,
RelocType.IMAGE_REL_BASED_RELPTR32 => DehydratedDataCommand.RelPtr32Reloc,
_ => throw new NotSupportedException(),
};
// Reloc will be generated inline. Check if we can generate a run of inline relocs.

// Reserve a byte for the command (the command payload will have to fit in this byte too).
ObjectDataBuilder.Reservation reservation = builder.ReserveByte();

int written = DehydratedDataCommand.Encode(relocCommand, targetIndex, buff);
builder.EmitBytes(buff, 0, written);
int numRelocs = 0;
bool hasNextReloc;
do
{
builder.EmitReloc(target, RelocType.IMAGE_REL_BASED_RELPTR32);
numRelocs++;
hasNextReloc = false;

if (currentReloc < o.Relocs.Length)
{
// If we wouldn't be able to fit this run into the single byte we reserved, stop.
if (numRelocs == DehydratedDataCommand.MaxShortPayload)
break;

Relocation nextReloc = o.Relocs[currentReloc];

// Does the next reloc immediately follow this one?
if (nextReloc.Offset != sourcePosition)
break;

// Is it of the same type?
if (nextReloc.RelocType != reloc.RelocType)
break;

ISymbolNode nextTarget = nextReloc.Target;
if (nextTarget is ISymbolNodeWithLinkage nextTargetWithLinkage)
nextTarget = nextTargetWithLinkage.NodeForLinkage(factory);

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

// This relocation is good - we'll generate it as part of the run
sourcePosition += Relocation.GetSize(reloc.RelocType);
hasNextReloc = true;
currentReloc++;
target = nextTarget;
}
} while (hasNextReloc);

// Now update the byte we reserved with the command to emit for the run
int relocCommand = reloc.RelocType switch
{
RelocType.IMAGE_REL_BASED_DIR64 => DehydratedDataCommand.InlinePtrReloc,
RelocType.IMAGE_REL_BASED_RELPTR32 => DehydratedDataCommand.InlineRelPtr32Reloc,
_ => throw new NotSupportedException(),
};
builder.EmitByte(reservation, DehydratedDataCommand.EncodeShort(relocCommand, numRelocs));
}
}
}

Expand Down