Add multi-level function table (#2228)
* Add AddressTable<T> * Use AddressTable<T> for dispatch * Remove JumpTable & co. * Add fallback for out of range addresses * Add PPTC support * Add documentation to `AddressTable<T>` * Make AddressTable<T> configurable * Fix table walk * Fix IsMapped check * Remove CountTableCapacity * Add PPTC support for fast path * Rename IsMapped to IsValid * Remove stale comment * Change format of address in exception message * Add TranslatorStubs * Split DispatchStub Avoids recompilation of stubs during tests. * Add hint for 64bit or 32bit * Add documentation to `Symbol` * Add documentation to `TranslatorStubs` Make `TranslatorStubs` disposable as well. * Add documentation to `SymbolType` * Add `AddressTableEventSource` to monitor function table size Add an EventSource which measures the amount of unmanaged bytes allocated by AddressTable<T> instances. dotnet-counters monitor -n Ryujinx --counters ARMeilleure * Add `AllowLcqInFunctionTable` optimization toggle This is to reduce the impact this change has on the test duration. Before everytime a test was ran, the FunctionTable would be initialized and populated so that the newly compiled test would get registered to it. * Implement unmanaged dispatcher Uses the DispatchStub to dispatch into the next translation, which allows execution to stay in unmanaged for longer and skips a ConcurrentDictionary look up when the target translation has been registered to the FunctionTable. * Remove redundant null check * Tune levels of FunctionTable Uses 5 levels instead of 4 and change unit of AddressTableEventSource from KB to MB. * Use 64-bit function table Improves codegen for direct branches: mov qword [rax+0x408],0x10603560 - mov rcx,sub_10603560_OFFSET - mov ecx,[rcx] - mov ecx,ecx - mov rdx,JIT_CACHE_BASE - add rdx,rcx + mov rcx,sub_10603560 + mov rdx,[rcx] mov rcx,rax Improves codegen for dispatch stub: and rax,byte +0x1f - mov eax,[rcx+rax*4] - mov eax,eax - mov rcx,JIT_CACHE_BASE - lea rax,[rcx+rax] + mov rax,[rcx+rax*8] mov rcx,rbx * Remove `JitCacheSymbol` & `JitCache.Offset` * Turn `Translator.Translate` into an instance method We do not have to add more parameter to this method and related ones as new structures are added & needed for translation. * Add symbol only when PTC is enabled Address LDj3SNuD's feedback * Change `NativeContext.Running` to a 32-bit integer * Fix PageTable symbol for host mapped
This commit is contained in:
parent
f3b0b4831c
commit
9d7627af64
36 changed files with 1020 additions and 1272 deletions
|
@ -28,7 +28,7 @@ namespace ARMeilleure.Translation.PTC
|
|||
private const string OuterHeaderMagicString = "PTCohd\0\0";
|
||||
private const string InnerHeaderMagicString = "PTCihd\0\0";
|
||||
|
||||
private const uint InternalVersion = 2289; //! To be incremented manually for each change to the ARMeilleure project.
|
||||
private const uint InternalVersion = 2228; //! To be incremented manually for each change to the ARMeilleure project.
|
||||
|
||||
private const string ActualDir = "0";
|
||||
private const string BackupDir = "1";
|
||||
|
@ -36,10 +36,9 @@ namespace ARMeilleure.Translation.PTC
|
|||
private const string TitleIdTextDefault = "0000000000000000";
|
||||
private const string DisplayVersionDefault = "0";
|
||||
|
||||
internal const int PageTablePointerIndex = -1; // Must be a negative value.
|
||||
internal const int JumpPointerIndex = -2; // Must be a negative value.
|
||||
internal const int DynamicPointerIndex = -3; // Must be a negative value.
|
||||
internal const int CountTableIndex = -4; // Must be a negative value.
|
||||
internal static readonly Symbol PageTableSymbol = new(SymbolType.Special, 1);
|
||||
internal static readonly Symbol CountTableSymbol = new(SymbolType.Special, 2);
|
||||
internal static readonly Symbol DispatchStubSymbol = new(SymbolType.Special, 3);
|
||||
|
||||
private const byte FillingByte = 0x00;
|
||||
private const CompressionLevel SaveCompressionLevel = CompressionLevel.Fastest;
|
||||
|
@ -59,8 +58,6 @@ namespace ARMeilleure.Translation.PTC
|
|||
|
||||
private static bool _disposed;
|
||||
|
||||
internal static PtcJumpTable PtcJumpTable { get; private set; }
|
||||
|
||||
internal static string TitleIdText { get; private set; }
|
||||
internal static string DisplayVersion { get; private set; }
|
||||
|
||||
|
@ -89,8 +86,6 @@ namespace ARMeilleure.Translation.PTC
|
|||
|
||||
_disposed = false;
|
||||
|
||||
PtcJumpTable = new PtcJumpTable();
|
||||
|
||||
TitleIdText = TitleIdTextDefault;
|
||||
DisplayVersion = DisplayVersionDefault;
|
||||
|
||||
|
@ -348,20 +343,8 @@ namespace ARMeilleure.Translation.PTC
|
|||
return false;
|
||||
}
|
||||
|
||||
ReadOnlySpan<byte> ptcJumpTableBytes = new(stream.PositionPointer, innerHeader.PtcJumpTableLength);
|
||||
stream.Seek(innerHeader.PtcJumpTableLength, SeekOrigin.Current);
|
||||
|
||||
Debug.Assert(stream.Position == stream.Length);
|
||||
|
||||
Hash128 ptcJumpTableHash = XXHash128.ComputeHash(ptcJumpTableBytes);
|
||||
|
||||
if (innerHeader.PtcJumpTableHash != ptcJumpTableHash)
|
||||
{
|
||||
InvalidateCompressedStream(compressedStream);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
stream.Seek((long)Unsafe.SizeOf<InnerHeader>(), SeekOrigin.Begin);
|
||||
|
||||
_infosStream.Write(infosBytes);
|
||||
|
@ -375,8 +358,6 @@ namespace ARMeilleure.Translation.PTC
|
|||
_unwindInfosStream.Write(unwindInfosBytes);
|
||||
stream.Seek(innerHeader.UnwindInfosLength, SeekOrigin.Current);
|
||||
|
||||
PtcJumpTable = PtcJumpTable.Deserialize(stream);
|
||||
|
||||
Debug.Assert(stream.Position == stream.Length);
|
||||
}
|
||||
}
|
||||
|
@ -422,7 +403,6 @@ namespace ARMeilleure.Translation.PTC
|
|||
finally
|
||||
{
|
||||
ResetCarriersIfNeeded();
|
||||
PtcJumpTable.ClearIfNeeded();
|
||||
|
||||
GCSettings.LargeObjectHeapCompactionMode = GCLargeObjectHeapCompactionMode.CompactOnce;
|
||||
}
|
||||
|
@ -442,7 +422,6 @@ namespace ARMeilleure.Translation.PTC
|
|||
innerHeader.CodesLength = _codesList.Length();
|
||||
innerHeader.RelocsLength = (int)_relocsStream.Length;
|
||||
innerHeader.UnwindInfosLength = (int)_unwindInfosStream.Length;
|
||||
innerHeader.PtcJumpTableLength = PtcJumpTable.GetSerializeSize(PtcJumpTable);
|
||||
|
||||
OuterHeader outerHeader = new OuterHeader();
|
||||
|
||||
|
@ -459,8 +438,7 @@ namespace ARMeilleure.Translation.PTC
|
|||
innerHeader.InfosLength +
|
||||
innerHeader.CodesLength +
|
||||
innerHeader.RelocsLength +
|
||||
innerHeader.UnwindInfosLength +
|
||||
innerHeader.PtcJumpTableLength;
|
||||
innerHeader.UnwindInfosLength;
|
||||
|
||||
outerHeader.SetHeaderHash();
|
||||
|
||||
|
@ -486,16 +464,12 @@ namespace ARMeilleure.Translation.PTC
|
|||
ReadOnlySpan<byte> unwindInfosBytes = new(stream.PositionPointer, innerHeader.UnwindInfosLength);
|
||||
_unwindInfosStream.WriteTo(stream);
|
||||
|
||||
ReadOnlySpan<byte> ptcJumpTableBytes = new(stream.PositionPointer, innerHeader.PtcJumpTableLength);
|
||||
PtcJumpTable.Serialize(stream, PtcJumpTable);
|
||||
|
||||
Debug.Assert(stream.Position == stream.Length);
|
||||
|
||||
innerHeader.InfosHash = XXHash128.ComputeHash(infosBytes);
|
||||
innerHeader.CodesHash = XXHash128.ComputeHash(codesBytes);
|
||||
innerHeader.RelocsHash = XXHash128.ComputeHash(relocsBytes);
|
||||
innerHeader.UnwindInfosHash = XXHash128.ComputeHash(unwindInfosBytes);
|
||||
innerHeader.PtcJumpTableHash = XXHash128.ComputeHash(ptcJumpTableBytes);
|
||||
|
||||
innerHeader.SetHeaderHash();
|
||||
|
||||
|
@ -505,7 +479,6 @@ namespace ARMeilleure.Translation.PTC
|
|||
translatedFuncsCount = GetEntriesCount();
|
||||
|
||||
ResetCarriersIfNeeded();
|
||||
PtcJumpTable.ClearIfNeeded();
|
||||
|
||||
using (FileStream compressedStream = new(fileName, FileMode.OpenOrCreate))
|
||||
using (DeflateStream deflateStream = new(compressedStream, SaveCompressionLevel, true))
|
||||
|
@ -545,11 +518,7 @@ namespace ARMeilleure.Translation.PTC
|
|||
}
|
||||
}
|
||||
|
||||
internal static void LoadTranslations(
|
||||
ConcurrentDictionary<ulong, TranslatedFunction> funcs,
|
||||
IMemoryManager memory,
|
||||
JumpTable jumpTable,
|
||||
EntryTable<uint> countTable)
|
||||
internal static void LoadTranslations(Translator translator)
|
||||
{
|
||||
if (AreCarriersEmpty())
|
||||
{
|
||||
|
@ -580,7 +549,7 @@ namespace ARMeilleure.Translation.PTC
|
|||
continue;
|
||||
}
|
||||
|
||||
bool isEntryChanged = infoEntry.Hash != ComputeHash(memory, infoEntry.Address, infoEntry.GuestSize);
|
||||
bool isEntryChanged = infoEntry.Hash != ComputeHash(translator.Memory, infoEntry.Address, infoEntry.GuestSize);
|
||||
|
||||
if (isEntryChanged || (!infoEntry.HighCq && PtcProfiler.ProfiledFuncs.TryGetValue(infoEntry.Address, out var value) && value.HighCq))
|
||||
{
|
||||
|
@ -594,8 +563,6 @@ namespace ARMeilleure.Translation.PTC
|
|||
|
||||
if (isEntryChanged)
|
||||
{
|
||||
PtcJumpTable.Clean(infoEntry.Address);
|
||||
|
||||
Logger.Info?.Print(LogClass.Ptc, $"Invalidated translated function (address: 0x{infoEntry.Address:X16})");
|
||||
}
|
||||
|
||||
|
@ -610,14 +577,16 @@ namespace ARMeilleure.Translation.PTC
|
|||
{
|
||||
RelocEntry[] relocEntries = GetRelocEntries(relocsReader, infoEntry.RelocEntriesCount);
|
||||
|
||||
PatchCode(code, relocEntries, memory.PageTablePointer, jumpTable, countTable, out callCounter);
|
||||
PatchCode(translator, code, relocEntries, out callCounter);
|
||||
}
|
||||
|
||||
UnwindInfo unwindInfo = ReadUnwindInfo(unwindInfosReader);
|
||||
|
||||
TranslatedFunction func = FastTranslate(code, callCounter, infoEntry.GuestSize, unwindInfo, infoEntry.HighCq);
|
||||
|
||||
bool isAddressUnique = funcs.TryAdd(infoEntry.Address, func);
|
||||
translator.RegisterFunction(infoEntry.Address, func);
|
||||
|
||||
bool isAddressUnique = translator.Functions.TryAdd(infoEntry.Address, func);
|
||||
|
||||
Debug.Assert(isAddressUnique, $"The address 0x{infoEntry.Address:X16} is not unique.");
|
||||
}
|
||||
|
@ -630,12 +599,7 @@ namespace ARMeilleure.Translation.PTC
|
|||
throw new Exception("The length of a memory stream has changed, or its position has not reached or has exceeded its end.");
|
||||
}
|
||||
|
||||
jumpTable.Initialize(PtcJumpTable, funcs);
|
||||
|
||||
PtcJumpTable.WriteJumpTable(jumpTable, funcs);
|
||||
PtcJumpTable.WriteDynamicTable(jumpTable);
|
||||
|
||||
Logger.Info?.Print(LogClass.Ptc, $"{funcs.Count} translated functions loaded");
|
||||
Logger.Info?.Print(LogClass.Ptc, $"{translator.Functions.Count} translated functions loaded");
|
||||
}
|
||||
|
||||
private static int GetEntriesCount()
|
||||
|
@ -676,56 +640,63 @@ namespace ARMeilleure.Translation.PTC
|
|||
for (int i = 0; i < relocEntriesCount; i++)
|
||||
{
|
||||
int position = relocsReader.ReadInt32();
|
||||
int index = relocsReader.ReadInt32();
|
||||
SymbolType type = (SymbolType)relocsReader.ReadByte();
|
||||
ulong value = relocsReader.ReadUInt64();
|
||||
|
||||
relocEntries[i] = new RelocEntry(position, index);
|
||||
relocEntries[i] = new RelocEntry(position, new Symbol(type, value));
|
||||
}
|
||||
|
||||
return relocEntries;
|
||||
}
|
||||
|
||||
private static void PatchCode(
|
||||
Span<byte> code,
|
||||
RelocEntry[] relocEntries,
|
||||
IntPtr pageTablePointer,
|
||||
JumpTable jumpTable,
|
||||
EntryTable<uint> countTable,
|
||||
out Counter<uint> callCounter)
|
||||
private static void PatchCode(Translator translator, Span<byte> code, RelocEntry[] relocEntries, out Counter<uint> callCounter)
|
||||
{
|
||||
callCounter = null;
|
||||
|
||||
foreach (RelocEntry relocEntry in relocEntries)
|
||||
{
|
||||
ulong imm;
|
||||
IntPtr? imm = null;
|
||||
Symbol symbol = relocEntry.Symbol;
|
||||
|
||||
if (relocEntry.Index == PageTablePointerIndex)
|
||||
if (symbol.Type == SymbolType.FunctionTable)
|
||||
{
|
||||
imm = (ulong)pageTablePointer.ToInt64();
|
||||
}
|
||||
else if (relocEntry.Index == JumpPointerIndex)
|
||||
{
|
||||
imm = (ulong)jumpTable.JumpPointer.ToInt64();
|
||||
}
|
||||
else if (relocEntry.Index == DynamicPointerIndex)
|
||||
{
|
||||
imm = (ulong)jumpTable.DynamicPointer.ToInt64();
|
||||
}
|
||||
else if (relocEntry.Index == CountTableIndex)
|
||||
{
|
||||
callCounter = new Counter<uint>(countTable);
|
||||
ulong guestAddress = symbol.Value;
|
||||
|
||||
unsafe { imm = (ulong)Unsafe.AsPointer(ref callCounter.Value); }
|
||||
if (translator.FunctionTable.IsValid(guestAddress))
|
||||
{
|
||||
unsafe { imm = (IntPtr)Unsafe.AsPointer(ref translator.FunctionTable.GetValue(guestAddress)); }
|
||||
}
|
||||
}
|
||||
else if (Delegates.TryGetDelegateFuncPtrByIndex(relocEntry.Index, out IntPtr funcPtr))
|
||||
else if (symbol.Type == SymbolType.DelegateTable)
|
||||
{
|
||||
imm = (ulong)funcPtr.ToInt64();
|
||||
int index = (int)symbol.Value;
|
||||
|
||||
if (Delegates.TryGetDelegateFuncPtrByIndex(index, out IntPtr funcPtr))
|
||||
{
|
||||
imm = funcPtr;
|
||||
}
|
||||
}
|
||||
else
|
||||
else if (symbol == PageTableSymbol)
|
||||
{
|
||||
imm = translator.Memory.PageTablePointer;
|
||||
}
|
||||
else if (symbol == CountTableSymbol)
|
||||
{
|
||||
callCounter = new Counter<uint>(translator.CountTable);
|
||||
|
||||
unsafe { imm = (IntPtr)Unsafe.AsPointer(ref callCounter.Value); }
|
||||
}
|
||||
else if (symbol == DispatchStubSymbol)
|
||||
{
|
||||
imm = translator.Stubs.DispatchStub;
|
||||
}
|
||||
|
||||
if (imm == null)
|
||||
{
|
||||
throw new Exception($"Unexpected reloc entry {relocEntry}.");
|
||||
}
|
||||
|
||||
BinaryPrimitives.WriteUInt64LittleEndian(code.Slice(relocEntry.Position, 8), imm);
|
||||
BinaryPrimitives.WriteUInt64LittleEndian(code.Slice(relocEntry.Position, 8), (ulong)imm.Value);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -798,13 +769,9 @@ namespace ARMeilleure.Translation.PTC
|
|||
}
|
||||
}
|
||||
|
||||
internal static void MakeAndSaveTranslations(
|
||||
ConcurrentDictionary<ulong, TranslatedFunction> funcs,
|
||||
IMemoryManager memory,
|
||||
JumpTable jumpTable,
|
||||
EntryTable<uint> countTable)
|
||||
internal static void MakeAndSaveTranslations(Translator translator)
|
||||
{
|
||||
var profiledFuncsToTranslate = PtcProfiler.GetProfiledFuncsToTranslate(funcs);
|
||||
var profiledFuncsToTranslate = PtcProfiler.GetProfiledFuncsToTranslate(translator.Functions);
|
||||
|
||||
_translateCount = 0;
|
||||
_translateTotalCount = profiledFuncsToTranslate.Count;
|
||||
|
@ -814,7 +781,6 @@ namespace ARMeilleure.Translation.PTC
|
|||
if (_translateTotalCount == 0 || degreeOfParallelism == 0)
|
||||
{
|
||||
ResetCarriersIfNeeded();
|
||||
PtcJumpTable.ClearIfNeeded();
|
||||
|
||||
GCSettings.LargeObjectHeapCompactionMode = GCLargeObjectHeapCompactionMode.CompactOnce;
|
||||
|
||||
|
@ -844,19 +810,16 @@ namespace ARMeilleure.Translation.PTC
|
|||
|
||||
Debug.Assert(PtcProfiler.IsAddressInStaticCodeRange(address));
|
||||
|
||||
TranslatedFunction func = Translator.Translate(memory, jumpTable, countTable, address, item.funcProfile.Mode, item.funcProfile.HighCq);
|
||||
TranslatedFunction func = translator.Translate(address, item.funcProfile.Mode, item.funcProfile.HighCq);
|
||||
|
||||
bool isAddressUnique = funcs.TryAdd(address, func);
|
||||
bool isAddressUnique = translator.Functions.TryAdd(address, func);
|
||||
|
||||
Debug.Assert(isAddressUnique, $"The address 0x{address:X16} is not unique.");
|
||||
|
||||
if (func.HighCq)
|
||||
{
|
||||
jumpTable.RegisterFunction(address, func);
|
||||
}
|
||||
|
||||
Interlocked.Increment(ref _translateCount);
|
||||
|
||||
translator.RegisterFunction(address, func);
|
||||
|
||||
if (State != PtcState.Enabled)
|
||||
{
|
||||
break;
|
||||
|
@ -888,11 +851,6 @@ namespace ARMeilleure.Translation.PTC
|
|||
|
||||
Logger.Info?.Print(LogClass.Ptc, $"{_translateCount} of {_translateTotalCount} functions translated | Thread count: {degreeOfParallelism}");
|
||||
|
||||
PtcJumpTable.Initialize(jumpTable);
|
||||
|
||||
PtcJumpTable.ReadJumpTable(jumpTable);
|
||||
PtcJumpTable.ReadDynamicTable(jumpTable);
|
||||
|
||||
Thread preSaveThread = new Thread(PreSave);
|
||||
preSaveThread.IsBackground = true;
|
||||
preSaveThread.Start();
|
||||
|
@ -1022,13 +980,11 @@ namespace ARMeilleure.Translation.PTC
|
|||
public long CodesLength;
|
||||
public int RelocsLength;
|
||||
public int UnwindInfosLength;
|
||||
public int PtcJumpTableLength;
|
||||
|
||||
public Hash128 InfosHash;
|
||||
public Hash128 CodesHash;
|
||||
public Hash128 RelocsHash;
|
||||
public Hash128 UnwindInfosHash;
|
||||
public Hash128 PtcJumpTableHash;
|
||||
|
||||
public Hash128 HeaderHash;
|
||||
|
||||
|
|
|
@ -30,7 +30,8 @@ namespace ARMeilleure.Translation.PTC
|
|||
public void WriteRelocEntry(RelocEntry relocEntry)
|
||||
{
|
||||
_relocWriter.Write((int)relocEntry.Position);
|
||||
_relocWriter.Write((int)relocEntry.Index);
|
||||
_relocWriter.Write((byte)relocEntry.Symbol.Type);
|
||||
_relocWriter.Write((ulong)relocEntry.Symbol.Value);
|
||||
|
||||
RelocEntriesCount++;
|
||||
}
|
||||
|
|
|
@ -1,350 +0,0 @@
|
|||
using ARMeilleure.Translation.Cache;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
using static ARMeilleure.Translation.PTC.PtcFormatter;
|
||||
|
||||
namespace ARMeilleure.Translation.PTC
|
||||
{
|
||||
class PtcJumpTable
|
||||
{
|
||||
[StructLayout(LayoutKind.Sequential, Pack = 1/*, Size = 16*/)]
|
||||
public struct TableEntry<TAddress>
|
||||
{
|
||||
public int EntryIndex;
|
||||
public long GuestAddress;
|
||||
public TAddress HostAddress;
|
||||
|
||||
public TableEntry(int entryIndex, long guestAddress, TAddress hostAddress)
|
||||
{
|
||||
EntryIndex = entryIndex;
|
||||
GuestAddress = guestAddress;
|
||||
HostAddress = hostAddress;
|
||||
}
|
||||
}
|
||||
|
||||
public enum DirectHostAddress : int
|
||||
{
|
||||
CallStub = 0,
|
||||
TailCallStub = 1,
|
||||
Host = 2
|
||||
}
|
||||
|
||||
public enum IndirectHostAddress : int
|
||||
{
|
||||
CallStub = 0,
|
||||
TailCallStub = 1
|
||||
}
|
||||
|
||||
private readonly List<TableEntry<DirectHostAddress>> _jumpTable;
|
||||
private readonly List<TableEntry<IndirectHostAddress>> _dynamicTable;
|
||||
|
||||
public List<ulong> Targets { get; }
|
||||
public Dictionary<ulong, List<int>> Dependants { get; }
|
||||
public Dictionary<ulong, List<int>> Owners { get; }
|
||||
|
||||
public PtcJumpTable()
|
||||
{
|
||||
_jumpTable = new List<TableEntry<DirectHostAddress>>();
|
||||
_dynamicTable = new List<TableEntry<IndirectHostAddress>>();
|
||||
|
||||
Targets = new List<ulong>();
|
||||
Dependants = new Dictionary<ulong, List<int>>();
|
||||
Owners = new Dictionary<ulong, List<int>>();
|
||||
}
|
||||
|
||||
public PtcJumpTable(
|
||||
List<TableEntry<DirectHostAddress>> jumpTable, List<TableEntry<IndirectHostAddress>> dynamicTable,
|
||||
List<ulong> targets, Dictionary<ulong, List<int>> dependants, Dictionary<ulong, List<int>> owners)
|
||||
{
|
||||
_jumpTable = jumpTable;
|
||||
_dynamicTable = dynamicTable;
|
||||
|
||||
Targets = targets;
|
||||
Dependants = dependants;
|
||||
Owners = owners;
|
||||
}
|
||||
|
||||
public static PtcJumpTable Deserialize(Stream stream)
|
||||
{
|
||||
var jumpTable = DeserializeList<TableEntry<DirectHostAddress>>(stream);
|
||||
var dynamicTable = DeserializeList<TableEntry<IndirectHostAddress>>(stream);
|
||||
|
||||
var targets = DeserializeList<ulong>(stream);
|
||||
var dependants = DeserializeDictionary<ulong, List<int>>(stream, (stream) => DeserializeList<int>(stream));
|
||||
var owners = DeserializeDictionary<ulong, List<int>>(stream, (stream) => DeserializeList<int>(stream));
|
||||
|
||||
return new PtcJumpTable(jumpTable, dynamicTable, targets, dependants, owners);
|
||||
}
|
||||
|
||||
public static int GetSerializeSize(PtcJumpTable ptcJumpTable)
|
||||
{
|
||||
int size = 0;
|
||||
|
||||
size += GetSerializeSizeList(ptcJumpTable._jumpTable);
|
||||
size += GetSerializeSizeList(ptcJumpTable._dynamicTable);
|
||||
|
||||
size += GetSerializeSizeList(ptcJumpTable.Targets);
|
||||
size += GetSerializeSizeDictionary(ptcJumpTable.Dependants, (list) => GetSerializeSizeList(list));
|
||||
size += GetSerializeSizeDictionary(ptcJumpTable.Owners, (list) => GetSerializeSizeList(list));
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
public static void Serialize(Stream stream, PtcJumpTable ptcJumpTable)
|
||||
{
|
||||
SerializeList(stream, ptcJumpTable._jumpTable);
|
||||
SerializeList(stream, ptcJumpTable._dynamicTable);
|
||||
|
||||
SerializeList(stream, ptcJumpTable.Targets);
|
||||
SerializeDictionary(stream, ptcJumpTable.Dependants, (stream, list) => SerializeList(stream, list));
|
||||
SerializeDictionary(stream, ptcJumpTable.Owners, (stream, list) => SerializeList(stream, list));
|
||||
}
|
||||
|
||||
public void Initialize(JumpTable jumpTable)
|
||||
{
|
||||
Targets.Clear();
|
||||
|
||||
foreach (ulong guestAddress in jumpTable.Targets.Keys)
|
||||
{
|
||||
Targets.Add(guestAddress);
|
||||
}
|
||||
|
||||
Dependants.Clear();
|
||||
|
||||
foreach (var kv in jumpTable.Dependants)
|
||||
{
|
||||
Dependants.Add(kv.Key, new List<int>(kv.Value));
|
||||
}
|
||||
|
||||
Owners.Clear();
|
||||
|
||||
foreach (var kv in jumpTable.Owners)
|
||||
{
|
||||
Owners.Add(kv.Key, new List<int>(kv.Value));
|
||||
}
|
||||
}
|
||||
|
||||
public void Clean(ulong guestAddress)
|
||||
{
|
||||
if (Owners.TryGetValue(guestAddress, out List<int> entries))
|
||||
{
|
||||
foreach (int entry in entries)
|
||||
{
|
||||
if ((entry & JumpTable.DynamicEntryTag) == 0)
|
||||
{
|
||||
int removed = _jumpTable.RemoveAll(tableEntry => tableEntry.EntryIndex == entry);
|
||||
|
||||
Debug.Assert(removed == 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (JumpTable.DynamicTableElems > 1)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
int removed = _dynamicTable.RemoveAll(tableEntry => tableEntry.EntryIndex == (entry & ~JumpTable.DynamicEntryTag));
|
||||
|
||||
Debug.Assert(removed == 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Targets.Remove(guestAddress);
|
||||
Dependants.Remove(guestAddress);
|
||||
Owners.Remove(guestAddress);
|
||||
}
|
||||
|
||||
public void ClearIfNeeded()
|
||||
{
|
||||
if (_jumpTable.Count == 0 && _dynamicTable.Count == 0 &&
|
||||
Targets.Count == 0 && Dependants.Count == 0 && Owners.Count == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_jumpTable.Clear();
|
||||
_jumpTable.TrimExcess();
|
||||
_dynamicTable.Clear();
|
||||
_dynamicTable.TrimExcess();
|
||||
|
||||
Targets.Clear();
|
||||
Targets.TrimExcess();
|
||||
Dependants.Clear();
|
||||
Dependants.TrimExcess();
|
||||
Owners.Clear();
|
||||
Owners.TrimExcess();
|
||||
}
|
||||
|
||||
public void WriteJumpTable(JumpTable jumpTable, ConcurrentDictionary<ulong, TranslatedFunction> funcs)
|
||||
{
|
||||
// Writes internal state to jump table in-memory, after PtcJumpTable was deserialized.
|
||||
|
||||
foreach (var tableEntry in _jumpTable)
|
||||
{
|
||||
long guestAddress = tableEntry.GuestAddress;
|
||||
DirectHostAddress directHostAddress = tableEntry.HostAddress;
|
||||
|
||||
long hostAddress;
|
||||
|
||||
if (directHostAddress == DirectHostAddress.CallStub)
|
||||
{
|
||||
hostAddress = DirectCallStubs.DirectCallStub(false).ToInt64();
|
||||
}
|
||||
else if (directHostAddress == DirectHostAddress.TailCallStub)
|
||||
{
|
||||
hostAddress = DirectCallStubs.DirectCallStub(true).ToInt64();
|
||||
}
|
||||
else if (directHostAddress == DirectHostAddress.Host)
|
||||
{
|
||||
if (funcs.TryGetValue((ulong)guestAddress, out TranslatedFunction func))
|
||||
{
|
||||
hostAddress = func.FuncPtr.ToInt64();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!PtcProfiler.ProfiledFuncs.TryGetValue((ulong)guestAddress, out var value) || !value.HighCq)
|
||||
{
|
||||
throw new KeyNotFoundException($"({nameof(guestAddress)} = 0x{(ulong)guestAddress:X16})");
|
||||
}
|
||||
|
||||
hostAddress = 0L;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new InvalidOperationException(nameof(directHostAddress));
|
||||
}
|
||||
|
||||
int entry = tableEntry.EntryIndex;
|
||||
|
||||
jumpTable.Table.SetEntry(entry);
|
||||
jumpTable.ExpandIfNeededJumpTable(entry);
|
||||
|
||||
IntPtr addr = jumpTable.GetEntryAddressJumpTable(entry);
|
||||
|
||||
Marshal.WriteInt64(addr, 0, guestAddress);
|
||||
Marshal.WriteInt64(addr, 8, hostAddress);
|
||||
}
|
||||
}
|
||||
|
||||
public void WriteDynamicTable(JumpTable jumpTable)
|
||||
{
|
||||
// Writes internal state to jump table in-memory, after PtcJumpTable was deserialized.
|
||||
|
||||
if (JumpTable.DynamicTableElems > 1)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
foreach (var tableEntry in _dynamicTable)
|
||||
{
|
||||
long guestAddress = tableEntry.GuestAddress;
|
||||
IndirectHostAddress indirectHostAddress = tableEntry.HostAddress;
|
||||
|
||||
long hostAddress;
|
||||
|
||||
if (indirectHostAddress == IndirectHostAddress.CallStub)
|
||||
{
|
||||
hostAddress = DirectCallStubs.IndirectCallStub(false).ToInt64();
|
||||
}
|
||||
else if (indirectHostAddress == IndirectHostAddress.TailCallStub)
|
||||
{
|
||||
hostAddress = DirectCallStubs.IndirectCallStub(true).ToInt64();
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new InvalidOperationException(nameof(indirectHostAddress));
|
||||
}
|
||||
|
||||
int entry = tableEntry.EntryIndex;
|
||||
|
||||
jumpTable.DynTable.SetEntry(entry);
|
||||
jumpTable.ExpandIfNeededDynamicTable(entry);
|
||||
|
||||
IntPtr addr = jumpTable.GetEntryAddressDynamicTable(entry);
|
||||
|
||||
Marshal.WriteInt64(addr, 0, guestAddress);
|
||||
Marshal.WriteInt64(addr, 8, hostAddress);
|
||||
}
|
||||
}
|
||||
|
||||
public void ReadJumpTable(JumpTable jumpTable)
|
||||
{
|
||||
// Reads in-memory jump table state and store internally for PtcJumpTable serialization.
|
||||
|
||||
_jumpTable.Clear();
|
||||
|
||||
IEnumerable<int> entries = jumpTable.Table.GetEntries();
|
||||
|
||||
foreach (int entry in entries)
|
||||
{
|
||||
IntPtr addr = jumpTable.GetEntryAddressJumpTable(entry);
|
||||
|
||||
long guestAddress = Marshal.ReadInt64(addr, 0);
|
||||
long hostAddress = Marshal.ReadInt64(addr, 8);
|
||||
|
||||
DirectHostAddress directHostAddress;
|
||||
|
||||
if (hostAddress == DirectCallStubs.DirectCallStub(false).ToInt64())
|
||||
{
|
||||
directHostAddress = DirectHostAddress.CallStub;
|
||||
}
|
||||
else if (hostAddress == DirectCallStubs.DirectCallStub(true).ToInt64())
|
||||
{
|
||||
directHostAddress = DirectHostAddress.TailCallStub;
|
||||
}
|
||||
else
|
||||
{
|
||||
directHostAddress = DirectHostAddress.Host;
|
||||
}
|
||||
|
||||
_jumpTable.Add(new TableEntry<DirectHostAddress>(entry, guestAddress, directHostAddress));
|
||||
}
|
||||
}
|
||||
|
||||
public void ReadDynamicTable(JumpTable jumpTable)
|
||||
{
|
||||
// Reads in-memory jump table state and store internally for PtcJumpTable serialization.
|
||||
|
||||
if (JumpTable.DynamicTableElems > 1)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
_dynamicTable.Clear();
|
||||
|
||||
IEnumerable<int> entries = jumpTable.DynTable.GetEntries();
|
||||
|
||||
foreach (int entry in entries)
|
||||
{
|
||||
IntPtr addr = jumpTable.GetEntryAddressDynamicTable(entry);
|
||||
|
||||
long guestAddress = Marshal.ReadInt64(addr, 0);
|
||||
long hostAddress = Marshal.ReadInt64(addr, 8);
|
||||
|
||||
IndirectHostAddress indirectHostAddress;
|
||||
|
||||
if (hostAddress == DirectCallStubs.IndirectCallStub(false).ToInt64())
|
||||
{
|
||||
indirectHostAddress = IndirectHostAddress.CallStub;
|
||||
}
|
||||
else if (hostAddress == DirectCallStubs.IndirectCallStub(true).ToInt64())
|
||||
{
|
||||
indirectHostAddress = IndirectHostAddress.TailCallStub;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new InvalidOperationException($"({nameof(hostAddress)} = 0x{hostAddress:X16})");
|
||||
}
|
||||
|
||||
_dynamicTable.Add(new TableEntry<IndirectHostAddress>(entry, guestAddress, indirectHostAddress));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -2,20 +2,20 @@ namespace ARMeilleure.Translation.PTC
|
|||
{
|
||||
struct RelocEntry
|
||||
{
|
||||
public const int Stride = 8; // Bytes.
|
||||
public const int Stride = 13; // Bytes.
|
||||
|
||||
public int Position;
|
||||
public int Index;
|
||||
public Symbol Symbol;
|
||||
|
||||
public RelocEntry(int position, int index)
|
||||
public RelocEntry(int position, Symbol symbol)
|
||||
{
|
||||
Position = position;
|
||||
Index = index;
|
||||
Symbol = symbol;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return $"({nameof(Position)} = {Position}, {nameof(Index)} = {Index})";
|
||||
return $"({nameof(Position)} = {Position}, {nameof(Symbol)} = {Symbol})";
|
||||
}
|
||||
}
|
||||
}
|
100
ARMeilleure/Translation/PTC/Symbol.cs
Normal file
100
ARMeilleure/Translation/PTC/Symbol.cs
Normal file
|
@ -0,0 +1,100 @@
|
|||
using System;
|
||||
|
||||
namespace ARMeilleure.Translation.PTC
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a symbol.
|
||||
/// </summary>
|
||||
struct Symbol
|
||||
{
|
||||
private readonly ulong _value;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the <see cref="SymbolType"/> of the <see cref="Symbol"/>.
|
||||
/// </summary>
|
||||
public SymbolType Type { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the value of the <see cref="Symbol"/>.
|
||||
/// </summary>
|
||||
/// <exception cref="InvalidOperationException"><see cref="Type"/> is <see cref="SymbolType.None"/></exception>
|
||||
public ulong Value
|
||||
{
|
||||
get
|
||||
{
|
||||
if (Type == SymbolType.None)
|
||||
{
|
||||
ThrowSymbolNone();
|
||||
}
|
||||
|
||||
return _value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Symbol"/> structure with the specified <see cref="SymbolType"/> and value.
|
||||
/// </summary>
|
||||
/// <param name="type">Type of symbol</param>
|
||||
/// <param name="value">Value of symbol</param>
|
||||
public Symbol(SymbolType type, ulong value)
|
||||
{
|
||||
(Type, _value) = (type, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines if the specified <see cref="Symbol"/> instances are equal.
|
||||
/// </summary>
|
||||
/// <param name="a">First instance</param>
|
||||
/// <param name="b">Second instance</param>
|
||||
/// <returns><see langword="true"/> if equal; otherwise <see langword="false"/></returns>
|
||||
public static bool operator ==(Symbol a, Symbol b)
|
||||
{
|
||||
return a.Equals(b);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines if the specified <see cref="Symbol"/> instances are not equal.
|
||||
/// </summary>
|
||||
/// <param name="a">First instance</param>
|
||||
/// <param name="b">Second instance</param>
|
||||
/// <returns><see langword="true"/> if not equal; otherwise <see langword="false"/></returns>
|
||||
/// <inheritdoc/>
|
||||
public static bool operator !=(Symbol a, Symbol b)
|
||||
{
|
||||
return !(a == b);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines if the specified <see cref="Symbol"/> is equal to this <see cref="Symbol"/> instance.
|
||||
/// </summary>
|
||||
/// <param name="other">Other <see cref="Symbol"/> instance</param>
|
||||
/// <returns><see langword="true"/> if equal; otherwise <see langword="false"/></returns>
|
||||
public bool Equals(Symbol other)
|
||||
{
|
||||
return other.Type == Type && other._value == _value;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
return obj is Symbol sym && Equals(sym);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return HashCode.Combine(Type, _value);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override string ToString()
|
||||
{
|
||||
return $"{Type}:{_value}";
|
||||
}
|
||||
|
||||
private static void ThrowSymbolNone()
|
||||
{
|
||||
throw new InvalidOperationException("Symbol refers to nothing.");
|
||||
}
|
||||
}
|
||||
}
|
28
ARMeilleure/Translation/PTC/SymbolType.cs
Normal file
28
ARMeilleure/Translation/PTC/SymbolType.cs
Normal file
|
@ -0,0 +1,28 @@
|
|||
namespace ARMeilleure.Translation.PTC
|
||||
{
|
||||
/// <summary>
|
||||
/// Types of <see cref="Symbol"/>.
|
||||
/// </summary>
|
||||
enum SymbolType : byte
|
||||
{
|
||||
/// <summary>
|
||||
/// Refers to nothing, i.e no symbol.
|
||||
/// </summary>
|
||||
None,
|
||||
|
||||
/// <summary>
|
||||
/// Refers to an entry in <see cref="Delegates"/>.
|
||||
/// </summary>
|
||||
DelegateTable,
|
||||
|
||||
/// <summary>
|
||||
/// Refers to an entry in <see cref="Translator.FunctionTable"/>.
|
||||
/// </summary>
|
||||
FunctionTable,
|
||||
|
||||
/// <summary>
|
||||
/// Refers to a special symbol which is handled by <see cref="Ptc.PatchCode"/>.
|
||||
/// </summary>
|
||||
Special
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue