forked from Mirror/Ryujinx
PPTC Follow-up. (#1712)
* Added support for offline invalidation, via PPTC, of low cq translations replaced by high cq translations; both on a single run and between runs. Added invalidation of .cache files in the event of reuse on a different user operating system. Added .info and .cache files invalidation in case of a failed stream decompression. Nits. * InternalVersion = 1712; * Nits. * Address comment. * Get rid of BinaryFormatter. Nits. * Move Ptc.LoadTranslations(). Nits. * Nits. * Fixed corner cases (in case backup copies have to be used). Added save logs. * Not core fixes. * Complement to the previous commit. Added load logs. Removed BinaryFormatter leftovers. * Add LoadTranslations log. * Nits. * Removed the search and management of LowCq overlapping functions. * Final increment of .info and .cache flags. * Nit. * GetIndirectFunctionAddress(): Validate that writing actually takes place in dynamic table memory range (and not elsewhere). * Fix Ptc.UpdateInfo() due to rebase. * Nit for retrigger Checks. * Nit for retrigger Checks.
This commit is contained in:
parent
10aa11ce13
commit
b5c215111d
16 changed files with 670 additions and 169 deletions
|
@ -2,6 +2,8 @@ namespace ARMeilleure.CodeGen.Unwinding
|
||||||
{
|
{
|
||||||
struct UnwindInfo
|
struct UnwindInfo
|
||||||
{
|
{
|
||||||
|
public const int Stride = 4; // Bytes.
|
||||||
|
|
||||||
public UnwindPushEntry[] PushEntries { get; }
|
public UnwindPushEntry[] PushEntries { get; }
|
||||||
public int PrologSize { get; }
|
public int PrologSize { get; }
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,8 @@ namespace ARMeilleure.CodeGen.Unwinding
|
||||||
{
|
{
|
||||||
struct UnwindPushEntry
|
struct UnwindPushEntry
|
||||||
{
|
{
|
||||||
|
public const int Stride = 16; // Bytes.
|
||||||
|
|
||||||
public UnwindPseudoOp PseudoOp { get; }
|
public UnwindPseudoOp PseudoOp { get; }
|
||||||
public int PrologOffset { get; }
|
public int PrologOffset { get; }
|
||||||
public int RegIndex { get; }
|
public int RegIndex { get; }
|
||||||
|
|
|
@ -26,6 +26,8 @@ namespace ARMeilleure.Decoders
|
||||||
|
|
||||||
Dictionary<ulong, Block> visited = new Dictionary<ulong, Block>();
|
Dictionary<ulong, Block> visited = new Dictionary<ulong, Block>();
|
||||||
|
|
||||||
|
Debug.Assert(MaxInstsPerFunctionLowCq <= MaxInstsPerFunction);
|
||||||
|
|
||||||
int opsCount = 0;
|
int opsCount = 0;
|
||||||
|
|
||||||
int instructionLimit = highCq ? MaxInstsPerFunction : MaxInstsPerFunctionLowCq;
|
int instructionLimit = highCq ? MaxInstsPerFunction : MaxInstsPerFunctionLowCq;
|
||||||
|
|
|
@ -2,6 +2,7 @@ using ARMeilleure.Memory;
|
||||||
using ARMeilleure.State;
|
using ARMeilleure.State;
|
||||||
using ARMeilleure.Translation;
|
using ARMeilleure.Translation;
|
||||||
using System;
|
using System;
|
||||||
|
using System.Diagnostics;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
namespace ARMeilleure.Instructions
|
namespace ARMeilleure.Instructions
|
||||||
|
@ -254,6 +255,8 @@ namespace ARMeilleure.Instructions
|
||||||
|
|
||||||
if (function.HighCq)
|
if (function.HighCq)
|
||||||
{
|
{
|
||||||
|
Debug.Assert(Context.Translator.JumpTable.CheckEntryFromAddressDynamicTable((IntPtr)entryAddress));
|
||||||
|
|
||||||
// Rewrite the host function address in the table to point to the highCq function.
|
// Rewrite the host function address in the table to point to the highCq function.
|
||||||
Marshal.WriteInt64((IntPtr)entryAddress, 8, (long)ptr);
|
Marshal.WriteInt64((IntPtr)entryAddress, 8, (long)ptr);
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,8 +2,8 @@ namespace ARMeilleure.State
|
||||||
{
|
{
|
||||||
enum ExecutionMode
|
enum ExecutionMode
|
||||||
{
|
{
|
||||||
Aarch32Arm,
|
Aarch32Arm = 0,
|
||||||
Aarch32Thumb,
|
Aarch32Thumb = 1,
|
||||||
Aarch64
|
Aarch64 = 2
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -14,7 +14,7 @@ namespace ARMeilleure.Translation.Cache
|
||||||
// The jump table is a block of (guestAddress, hostAddress) function mappings.
|
// The jump table is a block of (guestAddress, hostAddress) function mappings.
|
||||||
// Each entry corresponds to one branch in a JIT compiled function. The entries are
|
// Each entry corresponds to one branch in a JIT compiled function. The entries are
|
||||||
// reserved specifically for each call.
|
// reserved specifically for each call.
|
||||||
// The _dependants dictionary can be used to update the hostAddress for any functions that change.
|
// The Dependants dictionary can be used to update the hostAddress for any functions that change.
|
||||||
|
|
||||||
public const int JumpTableStride = 16; // 8 byte guest address, 8 byte host address.
|
public const int JumpTableStride = 16; // 8 byte guest address, 8 byte host address.
|
||||||
|
|
||||||
|
@ -42,7 +42,7 @@ namespace ARMeilleure.Translation.Cache
|
||||||
private const int DynamicTableSize = 1048576;
|
private const int DynamicTableSize = 1048576;
|
||||||
private const int DynamicTableByteSize = DynamicTableSize * DynamicTableStride;
|
private const int DynamicTableByteSize = DynamicTableSize * DynamicTableStride;
|
||||||
|
|
||||||
private const int DynamicEntryTag = 1 << 31;
|
public const int DynamicEntryTag = 1 << 31;
|
||||||
|
|
||||||
private readonly ReservedRegion _jumpRegion;
|
private readonly ReservedRegion _jumpRegion;
|
||||||
private readonly ReservedRegion _dynamicRegion;
|
private readonly ReservedRegion _dynamicRegion;
|
||||||
|
@ -87,14 +87,14 @@ namespace ARMeilleure.Translation.Cache
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (var item in ptcJumpTable.Dependants)
|
foreach (var kv in ptcJumpTable.Dependants)
|
||||||
{
|
{
|
||||||
Dependants.TryAdd(item.Key, new List<int>(item.Value));
|
Dependants.TryAdd(kv.Key, new List<int>(kv.Value));
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (var item in ptcJumpTable.Owners)
|
foreach (var kv in ptcJumpTable.Owners)
|
||||||
{
|
{
|
||||||
Owners.TryAdd(item.Key, new List<int>(item.Value));
|
Owners.TryAdd(kv.Key, new List<int>(kv.Value));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -182,29 +182,14 @@ namespace ARMeilleure.Translation.Cache
|
||||||
// For future use.
|
// For future use.
|
||||||
public void RemoveFunctionEntries(ulong guestAddress)
|
public void RemoveFunctionEntries(ulong guestAddress)
|
||||||
{
|
{
|
||||||
if (Owners.TryRemove(guestAddress, out List<int> list))
|
Targets.TryRemove(guestAddress, out _);
|
||||||
|
Dependants.TryRemove(guestAddress, out _);
|
||||||
|
|
||||||
|
if (Owners.TryRemove(guestAddress, out List<int> entries))
|
||||||
{
|
{
|
||||||
for (int i = 0; i < list.Count; i++)
|
foreach (int entry in entries)
|
||||||
{
|
{
|
||||||
int entry = list[i];
|
if ((entry & DynamicEntryTag) == 0)
|
||||||
|
|
||||||
bool isDynamic = (entry & DynamicEntryTag) != 0;
|
|
||||||
|
|
||||||
entry &= ~DynamicEntryTag;
|
|
||||||
|
|
||||||
if (isDynamic)
|
|
||||||
{
|
|
||||||
IntPtr addr = GetEntryAddressDynamicTable(entry);
|
|
||||||
|
|
||||||
for (int j = 0; j < DynamicTableElems; j++)
|
|
||||||
{
|
|
||||||
Marshal.WriteInt64(addr + j * JumpTableStride, 0, 0L);
|
|
||||||
Marshal.WriteInt64(addr + j * JumpTableStride, 8, 0L);
|
|
||||||
}
|
|
||||||
|
|
||||||
DynTable.FreeEntry(entry);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
{
|
||||||
IntPtr addr = GetEntryAddressJumpTable(entry);
|
IntPtr addr = GetEntryAddressJumpTable(entry);
|
||||||
|
|
||||||
|
@ -213,6 +198,18 @@ namespace ARMeilleure.Translation.Cache
|
||||||
|
|
||||||
Table.FreeEntry(entry);
|
Table.FreeEntry(entry);
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
IntPtr addr = GetEntryAddressDynamicTable(entry & ~DynamicEntryTag);
|
||||||
|
|
||||||
|
for (int j = 0; j < DynamicTableElems; j++)
|
||||||
|
{
|
||||||
|
Marshal.WriteInt64(addr + j * JumpTableStride, 0, 0L);
|
||||||
|
Marshal.WriteInt64(addr + j * JumpTableStride, 8, 0L);
|
||||||
|
}
|
||||||
|
|
||||||
|
DynTable.FreeEntry(entry & ~DynamicEntryTag);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -259,6 +256,20 @@ namespace ARMeilleure.Translation.Cache
|
||||||
return _dynamicRegion.Pointer + entry * DynamicTableStride;
|
return _dynamicRegion.Pointer + entry * DynamicTableStride;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool CheckEntryFromAddressJumpTable(IntPtr entryAddress)
|
||||||
|
{
|
||||||
|
int entry = Math.DivRem((int)((ulong)entryAddress - (ulong)_jumpRegion.Pointer), JumpTableStride, out int rem);
|
||||||
|
|
||||||
|
return rem == 0 && Table.EntryIsValid(entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool CheckEntryFromAddressDynamicTable(IntPtr entryAddress)
|
||||||
|
{
|
||||||
|
int entry = Math.DivRem((int)((ulong)entryAddress - (ulong)_dynamicRegion.Pointer), DynamicTableStride, out int rem);
|
||||||
|
|
||||||
|
return rem == 0 && DynTable.EntryIsValid(entry);
|
||||||
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
_jumpRegion.Dispose();
|
_jumpRegion.Dispose();
|
||||||
|
|
|
@ -3,7 +3,7 @@ using System.Runtime.InteropServices;
|
||||||
|
|
||||||
namespace ARMeilleure.Translation
|
namespace ARMeilleure.Translation
|
||||||
{
|
{
|
||||||
sealed class DelegateInfo
|
class DelegateInfo
|
||||||
{
|
{
|
||||||
private readonly Delegate _dlg; // Ensure that this delegate will not be garbage collected.
|
private readonly Delegate _dlg; // Ensure that this delegate will not be garbage collected.
|
||||||
|
|
||||||
|
|
72
ARMeilleure/Translation/JumpTableEntryAllocator.cs
Normal file
72
ARMeilleure/Translation/JumpTableEntryAllocator.cs
Normal file
|
@ -0,0 +1,72 @@
|
||||||
|
using ARMeilleure.Common;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
|
|
||||||
|
namespace ARMeilleure.Translation
|
||||||
|
{
|
||||||
|
class JumpTableEntryAllocator
|
||||||
|
{
|
||||||
|
private readonly BitMap _bitmap;
|
||||||
|
private int _freeHint;
|
||||||
|
|
||||||
|
public JumpTableEntryAllocator()
|
||||||
|
{
|
||||||
|
_bitmap = new BitMap();
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool EntryIsValid(int entryIndex)
|
||||||
|
{
|
||||||
|
lock (_bitmap)
|
||||||
|
{
|
||||||
|
return _bitmap.IsSet(entryIndex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetEntry(int entryIndex)
|
||||||
|
{
|
||||||
|
lock (_bitmap)
|
||||||
|
{
|
||||||
|
_bitmap.Set(entryIndex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public int AllocateEntry()
|
||||||
|
{
|
||||||
|
lock (_bitmap)
|
||||||
|
{
|
||||||
|
int entryIndex;
|
||||||
|
|
||||||
|
if (!_bitmap.IsSet(_freeHint))
|
||||||
|
{
|
||||||
|
entryIndex = _freeHint;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
entryIndex = _bitmap.FindFirstUnset();
|
||||||
|
}
|
||||||
|
|
||||||
|
_freeHint = entryIndex + 1;
|
||||||
|
|
||||||
|
bool wasSet = _bitmap.Set(entryIndex);
|
||||||
|
Debug.Assert(wasSet);
|
||||||
|
|
||||||
|
return entryIndex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void FreeEntry(int entryIndex)
|
||||||
|
{
|
||||||
|
lock (_bitmap)
|
||||||
|
{
|
||||||
|
_bitmap.Clear(entryIndex);
|
||||||
|
|
||||||
|
_freeHint = entryIndex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public IEnumerable<int> GetEntries()
|
||||||
|
{
|
||||||
|
return _bitmap;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -12,7 +12,7 @@ using System.Diagnostics;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.IO.Compression;
|
using System.IO.Compression;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using System.Runtime.Serialization.Formatters.Binary;
|
using System.Security.Cryptography;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
@ -22,7 +22,7 @@ namespace ARMeilleure.Translation.PTC
|
||||||
{
|
{
|
||||||
private const string HeaderMagic = "PTChd";
|
private const string HeaderMagic = "PTChd";
|
||||||
|
|
||||||
private const int InternalVersion = 1519; //! To be incremented manually for each change to the ARMeilleure project.
|
private const uint InternalVersion = 1713; //! To be incremented manually for each change to the ARMeilleure project.
|
||||||
|
|
||||||
private const string ActualDir = "0";
|
private const string ActualDir = "0";
|
||||||
private const string BackupDir = "1";
|
private const string BackupDir = "1";
|
||||||
|
@ -34,6 +34,7 @@ namespace ARMeilleure.Translation.PTC
|
||||||
internal const int JumpPointerIndex = -2; // 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 DynamicPointerIndex = -3; // Must be a negative value.
|
||||||
|
|
||||||
|
private const byte FillingByte = 0x00;
|
||||||
private const CompressionLevel SaveCompressionLevel = CompressionLevel.Fastest;
|
private const CompressionLevel SaveCompressionLevel = CompressionLevel.Fastest;
|
||||||
|
|
||||||
private static readonly MemoryStream _infosStream;
|
private static readonly MemoryStream _infosStream;
|
||||||
|
@ -43,8 +44,6 @@ namespace ARMeilleure.Translation.PTC
|
||||||
|
|
||||||
private static readonly BinaryWriter _infosWriter;
|
private static readonly BinaryWriter _infosWriter;
|
||||||
|
|
||||||
private static readonly BinaryFormatter _binaryFormatter;
|
|
||||||
|
|
||||||
private static readonly ManualResetEvent _waitEvent;
|
private static readonly ManualResetEvent _waitEvent;
|
||||||
|
|
||||||
private static readonly AutoResetEvent _loggerEvent;
|
private static readonly AutoResetEvent _loggerEvent;
|
||||||
|
@ -54,7 +53,6 @@ namespace ARMeilleure.Translation.PTC
|
||||||
private static bool _disposed;
|
private static bool _disposed;
|
||||||
|
|
||||||
private static volatile int _translateCount;
|
private static volatile int _translateCount;
|
||||||
private static volatile int _rejitCount;
|
|
||||||
|
|
||||||
internal static PtcJumpTable PtcJumpTable { get; private set; }
|
internal static PtcJumpTable PtcJumpTable { get; private set; }
|
||||||
|
|
||||||
|
@ -75,8 +73,6 @@ namespace ARMeilleure.Translation.PTC
|
||||||
|
|
||||||
_infosWriter = new BinaryWriter(_infosStream, EncodingCache.UTF8NoBOM, true);
|
_infosWriter = new BinaryWriter(_infosStream, EncodingCache.UTF8NoBOM, true);
|
||||||
|
|
||||||
_binaryFormatter = new BinaryFormatter();
|
|
||||||
|
|
||||||
_waitEvent = new ManualResetEvent(true);
|
_waitEvent = new ManualResetEvent(true);
|
||||||
|
|
||||||
_loggerEvent = new AutoResetEvent(false);
|
_loggerEvent = new AutoResetEvent(false);
|
||||||
|
@ -174,26 +170,26 @@ namespace ARMeilleure.Translation.PTC
|
||||||
|
|
||||||
if (fileInfoActual.Exists && fileInfoActual.Length != 0L)
|
if (fileInfoActual.Exists && fileInfoActual.Length != 0L)
|
||||||
{
|
{
|
||||||
if (!Load(fileNameActual))
|
if (!Load(fileNameActual, false))
|
||||||
{
|
{
|
||||||
if (fileInfoBackup.Exists && fileInfoBackup.Length != 0L)
|
if (fileInfoBackup.Exists && fileInfoBackup.Length != 0L)
|
||||||
{
|
{
|
||||||
Load(fileNameBackup);
|
Load(fileNameBackup, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (fileInfoBackup.Exists && fileInfoBackup.Length != 0L)
|
else if (fileInfoBackup.Exists && fileInfoBackup.Length != 0L)
|
||||||
{
|
{
|
||||||
Load(fileNameBackup);
|
Load(fileNameBackup, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static bool Load(string fileName)
|
private static bool Load(string fileName, bool isBackup)
|
||||||
{
|
{
|
||||||
using (FileStream compressedStream = new FileStream(fileName, FileMode.Open))
|
using (FileStream compressedStream = new FileStream(fileName, FileMode.Open))
|
||||||
using (DeflateStream deflateStream = new DeflateStream(compressedStream, CompressionMode.Decompress, true))
|
using (DeflateStream deflateStream = new DeflateStream(compressedStream, CompressionMode.Decompress, true))
|
||||||
using (MemoryStream stream = new MemoryStream())
|
using (MemoryStream stream = new MemoryStream())
|
||||||
using (System.Security.Cryptography.MD5 md5 = System.Security.Cryptography.MD5.Create())
|
using (MD5 md5 = MD5.Create())
|
||||||
{
|
{
|
||||||
int hashSize = md5.HashSize / 8;
|
int hashSize = md5.HashSize / 8;
|
||||||
|
|
||||||
|
@ -247,6 +243,13 @@ namespace ARMeilleure.Translation.PTC
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (header.OSPlatform != GetOSPlatform())
|
||||||
|
{
|
||||||
|
InvalidateCompressedStream(compressedStream);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (header.InfosLen % InfoEntry.Stride != 0)
|
if (header.InfosLen % InfoEntry.Stride != 0)
|
||||||
{
|
{
|
||||||
InvalidateCompressedStream(compressedStream);
|
InvalidateCompressedStream(compressedStream);
|
||||||
|
@ -266,7 +269,7 @@ namespace ARMeilleure.Translation.PTC
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
PtcJumpTable = (PtcJumpTable)_binaryFormatter.Deserialize(stream);
|
PtcJumpTable = PtcJumpTable.Deserialize(stream);
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
{
|
{
|
||||||
|
@ -281,10 +284,14 @@ namespace ARMeilleure.Translation.PTC
|
||||||
_codesStream.Write(codesBuf, 0, header.CodesLen);
|
_codesStream.Write(codesBuf, 0, header.CodesLen);
|
||||||
_relocsStream.Write(relocsBuf, 0, header.RelocsLen);
|
_relocsStream.Write(relocsBuf, 0, header.RelocsLen);
|
||||||
_unwindInfosStream.Write(unwindInfosBuf, 0, header.UnwindInfosLen);
|
_unwindInfosStream.Write(unwindInfosBuf, 0, header.UnwindInfosLen);
|
||||||
|
}
|
||||||
|
|
||||||
|
long fileSize = new FileInfo(fileName).Length;
|
||||||
|
|
||||||
|
Logger.Info?.Print(LogClass.Ptc, $"{(isBackup ? "Loaded Backup Translation Cache" : "Loaded Translation Cache")} (size: {fileSize} bytes, translated functions: {GetInfosEntriesCount()}).");
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
private static bool CompareHash(ReadOnlySpan<byte> currentHash, ReadOnlySpan<byte> expectedHash)
|
private static bool CompareHash(ReadOnlySpan<byte> currentHash, ReadOnlySpan<byte> expectedHash)
|
||||||
{
|
{
|
||||||
|
@ -299,8 +306,9 @@ namespace ARMeilleure.Translation.PTC
|
||||||
|
|
||||||
header.Magic = headerReader.ReadString();
|
header.Magic = headerReader.ReadString();
|
||||||
|
|
||||||
header.CacheFileVersion = headerReader.ReadInt32();
|
header.CacheFileVersion = headerReader.ReadUInt32();
|
||||||
header.FeatureInfo = headerReader.ReadUInt64();
|
header.FeatureInfo = headerReader.ReadUInt64();
|
||||||
|
header.OSPlatform = headerReader.ReadUInt32();
|
||||||
|
|
||||||
header.InfosLen = headerReader.ReadInt32();
|
header.InfosLen = headerReader.ReadInt32();
|
||||||
header.CodesLen = headerReader.ReadInt32();
|
header.CodesLen = headerReader.ReadInt32();
|
||||||
|
@ -338,7 +346,7 @@ namespace ARMeilleure.Translation.PTC
|
||||||
private static void Save(string fileName)
|
private static void Save(string fileName)
|
||||||
{
|
{
|
||||||
using (MemoryStream stream = new MemoryStream())
|
using (MemoryStream stream = new MemoryStream())
|
||||||
using (System.Security.Cryptography.MD5 md5 = System.Security.Cryptography.MD5.Create())
|
using (MD5 md5 = MD5.Create())
|
||||||
{
|
{
|
||||||
int hashSize = md5.HashSize / 8;
|
int hashSize = md5.HashSize / 8;
|
||||||
|
|
||||||
|
@ -351,7 +359,7 @@ namespace ARMeilleure.Translation.PTC
|
||||||
_relocsStream.WriteTo(stream);
|
_relocsStream.WriteTo(stream);
|
||||||
_unwindInfosStream.WriteTo(stream);
|
_unwindInfosStream.WriteTo(stream);
|
||||||
|
|
||||||
_binaryFormatter.Serialize(stream, PtcJumpTable);
|
PtcJumpTable.Serialize(stream, PtcJumpTable);
|
||||||
|
|
||||||
stream.Seek((long)hashSize, SeekOrigin.Begin);
|
stream.Seek((long)hashSize, SeekOrigin.Begin);
|
||||||
byte[] hash = md5.ComputeHash(stream);
|
byte[] hash = md5.ComputeHash(stream);
|
||||||
|
@ -377,6 +385,10 @@ namespace ARMeilleure.Translation.PTC
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
long fileSize = new FileInfo(fileName).Length;
|
||||||
|
|
||||||
|
Logger.Info?.Print(LogClass.Ptc, $"Saved Translation Cache (size: {fileSize} bytes, translated functions: {GetInfosEntriesCount()}).");
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void WriteHeader(MemoryStream stream)
|
private static void WriteHeader(MemoryStream stream)
|
||||||
|
@ -385,8 +397,9 @@ namespace ARMeilleure.Translation.PTC
|
||||||
{
|
{
|
||||||
headerWriter.Write((string)HeaderMagic); // Header.Magic
|
headerWriter.Write((string)HeaderMagic); // Header.Magic
|
||||||
|
|
||||||
headerWriter.Write((int)InternalVersion); // Header.CacheFileVersion
|
headerWriter.Write((uint)InternalVersion); // Header.CacheFileVersion
|
||||||
headerWriter.Write((ulong)GetFeatureInfo()); // Header.FeatureInfo
|
headerWriter.Write((ulong)GetFeatureInfo()); // Header.FeatureInfo
|
||||||
|
headerWriter.Write((uint)GetOSPlatform()); // Header.OSPlatform
|
||||||
|
|
||||||
headerWriter.Write((int)_infosStream.Length); // Header.InfosLen
|
headerWriter.Write((int)_infosStream.Length); // Header.InfosLen
|
||||||
headerWriter.Write((int)_codesStream.Length); // Header.CodesLen
|
headerWriter.Write((int)_codesStream.Length); // Header.CodesLen
|
||||||
|
@ -395,7 +408,7 @@ namespace ARMeilleure.Translation.PTC
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static void LoadTranslations(ConcurrentDictionary<ulong, TranslatedFunction> funcs, IntPtr pageTablePointer, JumpTable jumpTable)
|
internal static void LoadTranslations(ConcurrentDictionary<ulong, TranslatedFunction> funcs, IMemoryManager memory, JumpTable jumpTable)
|
||||||
{
|
{
|
||||||
if ((int)_infosStream.Length == 0 ||
|
if ((int)_infosStream.Length == 0 ||
|
||||||
(int)_codesStream.Length == 0 ||
|
(int)_codesStream.Length == 0 ||
|
||||||
|
@ -417,26 +430,44 @@ namespace ARMeilleure.Translation.PTC
|
||||||
using (BinaryReader relocsReader = new BinaryReader(_relocsStream, EncodingCache.UTF8NoBOM, true))
|
using (BinaryReader relocsReader = new BinaryReader(_relocsStream, EncodingCache.UTF8NoBOM, true))
|
||||||
using (BinaryReader unwindInfosReader = new BinaryReader(_unwindInfosStream, EncodingCache.UTF8NoBOM, true))
|
using (BinaryReader unwindInfosReader = new BinaryReader(_unwindInfosStream, EncodingCache.UTF8NoBOM, true))
|
||||||
{
|
{
|
||||||
int infosEntriesCount = (int)_infosStream.Length / InfoEntry.Stride;
|
for (int i = 0; i < GetInfosEntriesCount(); i++)
|
||||||
|
|
||||||
for (int i = 0; i < infosEntriesCount; i++)
|
|
||||||
{
|
{
|
||||||
InfoEntry infoEntry = ReadInfo(infosReader);
|
InfoEntry infoEntry = ReadInfo(infosReader);
|
||||||
|
|
||||||
|
if (infoEntry.Stubbed)
|
||||||
|
{
|
||||||
|
SkipCode(infoEntry.CodeLen);
|
||||||
|
SkipReloc(infoEntry.RelocEntriesCount);
|
||||||
|
SkipUnwindInfo(unwindInfosReader);
|
||||||
|
}
|
||||||
|
else if (infoEntry.HighCq || !PtcProfiler.ProfiledFuncs.TryGetValue(infoEntry.Address, out var value) || !value.highCq)
|
||||||
|
{
|
||||||
byte[] code = ReadCode(codesReader, infoEntry.CodeLen);
|
byte[] code = ReadCode(codesReader, infoEntry.CodeLen);
|
||||||
|
|
||||||
if (infoEntry.RelocEntriesCount != 0)
|
if (infoEntry.RelocEntriesCount != 0)
|
||||||
{
|
{
|
||||||
RelocEntry[] relocEntries = GetRelocEntries(relocsReader, infoEntry.RelocEntriesCount);
|
RelocEntry[] relocEntries = GetRelocEntries(relocsReader, infoEntry.RelocEntriesCount);
|
||||||
|
|
||||||
PatchCode(code, relocEntries, pageTablePointer, jumpTable);
|
PatchCode(code, relocEntries, memory.PageTablePointer, jumpTable);
|
||||||
}
|
}
|
||||||
|
|
||||||
UnwindInfo unwindInfo = ReadUnwindInfo(unwindInfosReader);
|
UnwindInfo unwindInfo = ReadUnwindInfo(unwindInfosReader);
|
||||||
|
|
||||||
TranslatedFunction func = FastTranslate(code, infoEntry.GuestSize, unwindInfo, infoEntry.HighCq);
|
TranslatedFunction func = FastTranslate(code, infoEntry.GuestSize, unwindInfo, infoEntry.HighCq);
|
||||||
|
|
||||||
funcs.AddOrUpdate(infoEntry.Address, func, (key, oldFunc) => func.HighCq && !oldFunc.HighCq ? func : oldFunc);
|
bool isAddressUnique = funcs.TryAdd(infoEntry.Address, func);
|
||||||
|
|
||||||
|
Debug.Assert(isAddressUnique, $"The address 0x{infoEntry.Address:X16} is not unique.");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
infoEntry.Stubbed = true;
|
||||||
|
UpdateInfo(infoEntry);
|
||||||
|
|
||||||
|
StubCode(infoEntry.CodeLen);
|
||||||
|
StubReloc(infoEntry.RelocEntriesCount);
|
||||||
|
StubUnwindInfo(unwindInfosReader);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -452,6 +483,13 @@ namespace ARMeilleure.Translation.PTC
|
||||||
|
|
||||||
PtcJumpTable.WriteJumpTable(jumpTable, funcs);
|
PtcJumpTable.WriteJumpTable(jumpTable, funcs);
|
||||||
PtcJumpTable.WriteDynamicTable(jumpTable);
|
PtcJumpTable.WriteDynamicTable(jumpTable);
|
||||||
|
|
||||||
|
Logger.Info?.Print(LogClass.Ptc, $"{funcs.Count} translated functions loaded");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int GetInfosEntriesCount()
|
||||||
|
{
|
||||||
|
return (int)_infosStream.Length / InfoEntry.Stride;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static InfoEntry ReadInfo(BinaryReader infosReader)
|
private static InfoEntry ReadInfo(BinaryReader infosReader)
|
||||||
|
@ -461,12 +499,30 @@ namespace ARMeilleure.Translation.PTC
|
||||||
infoEntry.Address = infosReader.ReadUInt64();
|
infoEntry.Address = infosReader.ReadUInt64();
|
||||||
infoEntry.GuestSize = infosReader.ReadUInt64();
|
infoEntry.GuestSize = infosReader.ReadUInt64();
|
||||||
infoEntry.HighCq = infosReader.ReadBoolean();
|
infoEntry.HighCq = infosReader.ReadBoolean();
|
||||||
|
infoEntry.Stubbed = infosReader.ReadBoolean();
|
||||||
infoEntry.CodeLen = infosReader.ReadInt32();
|
infoEntry.CodeLen = infosReader.ReadInt32();
|
||||||
infoEntry.RelocEntriesCount = infosReader.ReadInt32();
|
infoEntry.RelocEntriesCount = infosReader.ReadInt32();
|
||||||
|
|
||||||
return infoEntry;
|
return infoEntry;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void SkipCode(int codeLen)
|
||||||
|
{
|
||||||
|
_codesStream.Seek(codeLen, SeekOrigin.Current);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void SkipReloc(int relocEntriesCount)
|
||||||
|
{
|
||||||
|
_relocsStream.Seek(relocEntriesCount * RelocEntry.Stride, SeekOrigin.Current);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void SkipUnwindInfo(BinaryReader unwindInfosReader)
|
||||||
|
{
|
||||||
|
int pushEntriesLength = unwindInfosReader.ReadInt32();
|
||||||
|
|
||||||
|
_unwindInfosStream.Seek(pushEntriesLength * UnwindPushEntry.Stride + UnwindInfo.Stride, SeekOrigin.Current);
|
||||||
|
}
|
||||||
|
|
||||||
private static byte[] ReadCode(BinaryReader codesReader, int codeLen)
|
private static byte[] ReadCode(BinaryReader codesReader, int codeLen)
|
||||||
{
|
{
|
||||||
byte[] codeBuf = new byte[codeLen];
|
byte[] codeBuf = new byte[codeLen];
|
||||||
|
@ -556,31 +612,71 @@ namespace ARMeilleure.Translation.PTC
|
||||||
return tFunc;
|
return tFunc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void UpdateInfo(InfoEntry infoEntry)
|
||||||
|
{
|
||||||
|
_infosStream.Seek(-InfoEntry.Stride, SeekOrigin.Current);
|
||||||
|
|
||||||
|
// WriteInfo.
|
||||||
|
_infosWriter.Write((ulong)infoEntry.Address);
|
||||||
|
_infosWriter.Write((ulong)infoEntry.GuestSize);
|
||||||
|
_infosWriter.Write((bool)infoEntry.HighCq);
|
||||||
|
_infosWriter.Write((bool)infoEntry.Stubbed);
|
||||||
|
_infosWriter.Write((int)infoEntry.CodeLen);
|
||||||
|
_infosWriter.Write((int)infoEntry.RelocEntriesCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void StubCode(int codeLen)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < codeLen; i++)
|
||||||
|
{
|
||||||
|
_codesStream.WriteByte(FillingByte);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void StubReloc(int relocEntriesCount)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < relocEntriesCount * RelocEntry.Stride; i++)
|
||||||
|
{
|
||||||
|
_relocsStream.WriteByte(FillingByte);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void StubUnwindInfo(BinaryReader unwindInfosReader)
|
||||||
|
{
|
||||||
|
int pushEntriesLength = unwindInfosReader.ReadInt32();
|
||||||
|
|
||||||
|
for (int i = 0; i < pushEntriesLength * UnwindPushEntry.Stride + UnwindInfo.Stride; i++)
|
||||||
|
{
|
||||||
|
_unwindInfosStream.WriteByte(FillingByte);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
internal static void MakeAndSaveTranslations(ConcurrentDictionary<ulong, TranslatedFunction> funcs, IMemoryManager memory, JumpTable jumpTable)
|
internal static void MakeAndSaveTranslations(ConcurrentDictionary<ulong, TranslatedFunction> funcs, IMemoryManager memory, JumpTable jumpTable)
|
||||||
{
|
{
|
||||||
if (PtcProfiler.ProfiledFuncs.Count == 0)
|
var profiledFuncsToTranslate = PtcProfiler.GetProfiledFuncsToTranslate(funcs);
|
||||||
|
|
||||||
|
if (profiledFuncsToTranslate.Count == 0)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
_translateCount = 0;
|
_translateCount = 0;
|
||||||
_rejitCount = 0;
|
|
||||||
|
|
||||||
ThreadPool.QueueUserWorkItem(TranslationLogger, (funcs.Count, PtcProfiler.ProfiledFuncs.Count));
|
ThreadPool.QueueUserWorkItem(TranslationLogger, profiledFuncsToTranslate.Count);
|
||||||
|
|
||||||
int maxDegreeOfParallelism = (Environment.ProcessorCount * 3) / 4;
|
int maxDegreeOfParallelism = (Environment.ProcessorCount * 3) / 4;
|
||||||
|
|
||||||
Parallel.ForEach(PtcProfiler.ProfiledFuncs, new ParallelOptions { MaxDegreeOfParallelism = maxDegreeOfParallelism }, (item, state) =>
|
Parallel.ForEach(profiledFuncsToTranslate, new ParallelOptions { MaxDegreeOfParallelism = maxDegreeOfParallelism }, (item, state) =>
|
||||||
{
|
{
|
||||||
ulong address = item.Key;
|
ulong address = item.Key;
|
||||||
|
|
||||||
Debug.Assert(PtcProfiler.IsAddressInStaticCodeRange(address));
|
Debug.Assert(PtcProfiler.IsAddressInStaticCodeRange(address));
|
||||||
|
|
||||||
if (!funcs.ContainsKey(address))
|
|
||||||
{
|
|
||||||
TranslatedFunction func = Translator.Translate(memory, jumpTable, address, item.Value.mode, item.Value.highCq);
|
TranslatedFunction func = Translator.Translate(memory, jumpTable, address, item.Value.mode, item.Value.highCq);
|
||||||
|
|
||||||
funcs.TryAdd(address, func);
|
bool isAddressUnique = funcs.TryAdd(address, func);
|
||||||
|
|
||||||
|
Debug.Assert(isAddressUnique, $"The address 0x{address:X16} is not unique.");
|
||||||
|
|
||||||
if (func.HighCq)
|
if (func.HighCq)
|
||||||
{
|
{
|
||||||
|
@ -588,17 +684,6 @@ namespace ARMeilleure.Translation.PTC
|
||||||
}
|
}
|
||||||
|
|
||||||
Interlocked.Increment(ref _translateCount);
|
Interlocked.Increment(ref _translateCount);
|
||||||
}
|
|
||||||
else if (item.Value.highCq && !funcs[address].HighCq)
|
|
||||||
{
|
|
||||||
TranslatedFunction func = Translator.Translate(memory, jumpTable, address, item.Value.mode, highCq: true);
|
|
||||||
|
|
||||||
funcs[address] = func;
|
|
||||||
|
|
||||||
jumpTable.RegisterFunction(address, func);
|
|
||||||
|
|
||||||
Interlocked.Increment(ref _rejitCount);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (State != PtcState.Enabled)
|
if (State != PtcState.Enabled)
|
||||||
{
|
{
|
||||||
|
@ -608,8 +693,6 @@ namespace ARMeilleure.Translation.PTC
|
||||||
|
|
||||||
_loggerEvent.Set();
|
_loggerEvent.Set();
|
||||||
|
|
||||||
if (_translateCount != 0 || _rejitCount != 0)
|
|
||||||
{
|
|
||||||
PtcJumpTable.Initialize(jumpTable);
|
PtcJumpTable.Initialize(jumpTable);
|
||||||
|
|
||||||
PtcJumpTable.ReadJumpTable(jumpTable);
|
PtcJumpTable.ReadJumpTable(jumpTable);
|
||||||
|
@ -617,21 +700,20 @@ namespace ARMeilleure.Translation.PTC
|
||||||
|
|
||||||
ThreadPool.QueueUserWorkItem(PreSave);
|
ThreadPool.QueueUserWorkItem(PreSave);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
private static void TranslationLogger(object state)
|
private static void TranslationLogger(object state)
|
||||||
{
|
{
|
||||||
const int refreshRate = 1; // Seconds.
|
const int refreshRate = 1; // Seconds.
|
||||||
|
|
||||||
(int funcsCount, int ProfiledFuncsCount) = ((int, int))state;
|
int profiledFuncsToTranslateCount = (int)state;
|
||||||
|
|
||||||
do
|
do
|
||||||
{
|
{
|
||||||
Logger.Info?.Print(LogClass.Ptc, $"{funcsCount + _translateCount} of {ProfiledFuncsCount} functions to translate - {_rejitCount} functions rejited");
|
Logger.Info?.Print(LogClass.Ptc, $"{_translateCount} of {profiledFuncsToTranslateCount} functions translated");
|
||||||
}
|
}
|
||||||
while (!_loggerEvent.WaitOne(refreshRate * 1000));
|
while (!_loggerEvent.WaitOne(refreshRate * 1000));
|
||||||
|
|
||||||
Logger.Info?.Print(LogClass.Ptc, $"{funcsCount + _translateCount} of {ProfiledFuncsCount} functions to translate - {_rejitCount} functions rejited");
|
Logger.Info?.Print(LogClass.Ptc, $"{_translateCount} of {profiledFuncsToTranslateCount} functions translated");
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static void WriteInfoCodeReloc(ulong address, ulong guestSize, bool highCq, PtcInfo ptcInfo)
|
internal static void WriteInfoCodeReloc(ulong address, ulong guestSize, bool highCq, PtcInfo ptcInfo)
|
||||||
|
@ -642,6 +724,7 @@ namespace ARMeilleure.Translation.PTC
|
||||||
_infosWriter.Write((ulong)address); // InfoEntry.Address
|
_infosWriter.Write((ulong)address); // InfoEntry.Address
|
||||||
_infosWriter.Write((ulong)guestSize); // InfoEntry.GuestSize
|
_infosWriter.Write((ulong)guestSize); // InfoEntry.GuestSize
|
||||||
_infosWriter.Write((bool)highCq); // InfoEntry.HighCq
|
_infosWriter.Write((bool)highCq); // InfoEntry.HighCq
|
||||||
|
_infosWriter.Write((bool)false); // InfoEntry.Stubbed
|
||||||
_infosWriter.Write((int)ptcInfo.CodeStream.Length); // InfoEntry.CodeLen
|
_infosWriter.Write((int)ptcInfo.CodeStream.Length); // InfoEntry.CodeLen
|
||||||
_infosWriter.Write((int)ptcInfo.RelocEntriesCount); // InfoEntry.RelocEntriesCount
|
_infosWriter.Write((int)ptcInfo.RelocEntriesCount); // InfoEntry.RelocEntriesCount
|
||||||
|
|
||||||
|
@ -661,12 +744,25 @@ namespace ARMeilleure.Translation.PTC
|
||||||
return (ulong)HardwareCapabilities.FeatureInfoEdx << 32 | (uint)HardwareCapabilities.FeatureInfoEcx;
|
return (ulong)HardwareCapabilities.FeatureInfoEdx << 32 | (uint)HardwareCapabilities.FeatureInfoEcx;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static uint GetOSPlatform()
|
||||||
|
{
|
||||||
|
uint osPlatform = 0u;
|
||||||
|
|
||||||
|
osPlatform |= (RuntimeInformation.IsOSPlatform(OSPlatform.FreeBSD) ? 1u : 0u) << 0;
|
||||||
|
osPlatform |= (RuntimeInformation.IsOSPlatform(OSPlatform.Linux) ? 1u : 0u) << 1;
|
||||||
|
osPlatform |= (RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? 1u : 0u) << 2;
|
||||||
|
osPlatform |= (RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? 1u : 0u) << 3;
|
||||||
|
|
||||||
|
return osPlatform;
|
||||||
|
}
|
||||||
|
|
||||||
private struct Header
|
private struct Header
|
||||||
{
|
{
|
||||||
public string Magic;
|
public string Magic;
|
||||||
|
|
||||||
public int CacheFileVersion;
|
public uint CacheFileVersion;
|
||||||
public ulong FeatureInfo;
|
public ulong FeatureInfo;
|
||||||
|
public uint OSPlatform;
|
||||||
|
|
||||||
public int InfosLen;
|
public int InfosLen;
|
||||||
public int CodesLen;
|
public int CodesLen;
|
||||||
|
@ -676,11 +772,12 @@ namespace ARMeilleure.Translation.PTC
|
||||||
|
|
||||||
private struct InfoEntry
|
private struct InfoEntry
|
||||||
{
|
{
|
||||||
public const int Stride = 25; // Bytes.
|
public const int Stride = 26; // Bytes.
|
||||||
|
|
||||||
public ulong Address;
|
public ulong Address;
|
||||||
public ulong GuestSize;
|
public ulong GuestSize;
|
||||||
public bool HighCq;
|
public bool HighCq;
|
||||||
|
public bool Stubbed;
|
||||||
public int CodeLen;
|
public int CodeLen;
|
||||||
public int RelocEntriesCount;
|
public int RelocEntriesCount;
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,7 @@ using System.IO;
|
||||||
|
|
||||||
namespace ARMeilleure.Translation.PTC
|
namespace ARMeilleure.Translation.PTC
|
||||||
{
|
{
|
||||||
sealed class PtcInfo : IDisposable
|
class PtcInfo : IDisposable
|
||||||
{
|
{
|
||||||
private readonly BinaryWriter _relocWriter;
|
private readonly BinaryWriter _relocWriter;
|
||||||
private readonly BinaryWriter _unwindInfoWriter;
|
private readonly BinaryWriter _unwindInfoWriter;
|
||||||
|
|
|
@ -2,15 +2,15 @@ using ARMeilleure.Translation.Cache;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.IO;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
namespace ARMeilleure.Translation.PTC
|
namespace ARMeilleure.Translation.PTC
|
||||||
{
|
{
|
||||||
[Serializable]
|
|
||||||
class PtcJumpTable
|
class PtcJumpTable
|
||||||
{
|
{
|
||||||
[Serializable]
|
public struct TableEntry<TAddress>
|
||||||
private struct TableEntry<TAddress>
|
|
||||||
{
|
{
|
||||||
public int EntryIndex;
|
public int EntryIndex;
|
||||||
public long GuestAddress;
|
public long GuestAddress;
|
||||||
|
@ -24,82 +24,270 @@ namespace ARMeilleure.Translation.PTC
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private enum DirectHostAddress
|
public enum DirectHostAddress
|
||||||
{
|
{
|
||||||
CallStub,
|
CallStub = 0,
|
||||||
TailCallStub,
|
TailCallStub = 1,
|
||||||
Host
|
Host = 2
|
||||||
}
|
}
|
||||||
|
|
||||||
private enum IndirectHostAddress
|
public enum IndirectHostAddress
|
||||||
{
|
{
|
||||||
CallStub,
|
CallStub = 0,
|
||||||
TailCallStub
|
TailCallStub = 1
|
||||||
}
|
}
|
||||||
|
|
||||||
private readonly List<TableEntry<DirectHostAddress>> _jumpTable;
|
private readonly List<TableEntry<DirectHostAddress>> _jumpTable;
|
||||||
private readonly List<TableEntry<IndirectHostAddress>> _dynamicTable;
|
private readonly List<TableEntry<IndirectHostAddress>> _dynamicTable;
|
||||||
|
|
||||||
private readonly List<ulong> _targets;
|
public List<ulong> Targets { get; }
|
||||||
private readonly Dictionary<ulong, List<int>> _dependants;
|
public Dictionary<ulong, List<int>> Dependants { get; }
|
||||||
private readonly Dictionary<ulong, List<int>> _owners;
|
public Dictionary<ulong, List<int>> Owners { get; }
|
||||||
|
|
||||||
public List<ulong> Targets => _targets;
|
|
||||||
public Dictionary<ulong, List<int>> Dependants => _dependants;
|
|
||||||
public Dictionary<ulong, List<int>> Owners => _owners;
|
|
||||||
|
|
||||||
public PtcJumpTable()
|
public PtcJumpTable()
|
||||||
{
|
{
|
||||||
_jumpTable = new List<TableEntry<DirectHostAddress>>();
|
_jumpTable = new List<TableEntry<DirectHostAddress>>();
|
||||||
_dynamicTable = new List<TableEntry<IndirectHostAddress>>();
|
_dynamicTable = new List<TableEntry<IndirectHostAddress>>();
|
||||||
|
|
||||||
_targets = new List<ulong>();
|
Targets = new List<ulong>();
|
||||||
_dependants = new Dictionary<ulong, List<int>>();
|
Dependants = new Dictionary<ulong, List<int>>();
|
||||||
_owners = 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(MemoryStream stream)
|
||||||
|
{
|
||||||
|
using (BinaryReader reader = new BinaryReader(stream, EncodingCache.UTF8NoBOM, true))
|
||||||
|
{
|
||||||
|
var jumpTable = new List<TableEntry<DirectHostAddress>>();
|
||||||
|
|
||||||
|
int jumpTableCount = reader.ReadInt32();
|
||||||
|
|
||||||
|
for (int i = 0; i < jumpTableCount; i++)
|
||||||
|
{
|
||||||
|
int entryIndex = reader.ReadInt32();
|
||||||
|
long guestAddress = reader.ReadInt64();
|
||||||
|
DirectHostAddress hostAddress = (DirectHostAddress)reader.ReadInt32();
|
||||||
|
|
||||||
|
jumpTable.Add(new TableEntry<DirectHostAddress>(entryIndex, guestAddress, hostAddress));
|
||||||
|
}
|
||||||
|
|
||||||
|
var dynamicTable = new List<TableEntry<IndirectHostAddress>>();
|
||||||
|
|
||||||
|
int dynamicTableCount = reader.ReadInt32();
|
||||||
|
|
||||||
|
for (int i = 0; i < dynamicTableCount; i++)
|
||||||
|
{
|
||||||
|
int entryIndex = reader.ReadInt32();
|
||||||
|
long guestAddress = reader.ReadInt64();
|
||||||
|
IndirectHostAddress hostAddress = (IndirectHostAddress)reader.ReadInt32();
|
||||||
|
|
||||||
|
dynamicTable.Add(new TableEntry<IndirectHostAddress>(entryIndex, guestAddress, hostAddress));
|
||||||
|
}
|
||||||
|
|
||||||
|
var targets = new List<ulong>();
|
||||||
|
|
||||||
|
int targetsCount = reader.ReadInt32();
|
||||||
|
|
||||||
|
for (int i = 0; i < targetsCount; i++)
|
||||||
|
{
|
||||||
|
ulong address = reader.ReadUInt64();
|
||||||
|
|
||||||
|
targets.Add(address);
|
||||||
|
}
|
||||||
|
|
||||||
|
var dependants = new Dictionary<ulong, List<int>>();
|
||||||
|
|
||||||
|
int dependantsCount = reader.ReadInt32();
|
||||||
|
|
||||||
|
for (int i = 0; i < dependantsCount; i++)
|
||||||
|
{
|
||||||
|
ulong address = reader.ReadUInt64();
|
||||||
|
|
||||||
|
var entries = new List<int>();
|
||||||
|
|
||||||
|
int entriesCount = reader.ReadInt32();
|
||||||
|
|
||||||
|
for (int j = 0; j < entriesCount; j++)
|
||||||
|
{
|
||||||
|
int entry = reader.ReadInt32();
|
||||||
|
|
||||||
|
entries.Add(entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
dependants.Add(address, entries);
|
||||||
|
}
|
||||||
|
|
||||||
|
var owners = new Dictionary<ulong, List<int>>();
|
||||||
|
|
||||||
|
int ownersCount = reader.ReadInt32();
|
||||||
|
|
||||||
|
for (int i = 0; i < ownersCount; i++)
|
||||||
|
{
|
||||||
|
ulong address = reader.ReadUInt64();
|
||||||
|
|
||||||
|
var entries = new List<int>();
|
||||||
|
|
||||||
|
int entriesCount = reader.ReadInt32();
|
||||||
|
|
||||||
|
for (int j = 0; j < entriesCount; j++)
|
||||||
|
{
|
||||||
|
int entry = reader.ReadInt32();
|
||||||
|
|
||||||
|
entries.Add(entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
owners.Add(address, entries);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new PtcJumpTable(jumpTable, dynamicTable, targets, dependants, owners);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Serialize(MemoryStream stream, PtcJumpTable ptcJumpTable)
|
||||||
|
{
|
||||||
|
using (BinaryWriter writer = new BinaryWriter(stream, EncodingCache.UTF8NoBOM, true))
|
||||||
|
{
|
||||||
|
writer.Write((int)ptcJumpTable._jumpTable.Count);
|
||||||
|
|
||||||
|
foreach (var tableEntry in ptcJumpTable._jumpTable)
|
||||||
|
{
|
||||||
|
writer.Write((int)tableEntry.EntryIndex);
|
||||||
|
writer.Write((long)tableEntry.GuestAddress);
|
||||||
|
writer.Write((int)tableEntry.HostAddress);
|
||||||
|
}
|
||||||
|
|
||||||
|
writer.Write((int)ptcJumpTable._dynamicTable.Count);
|
||||||
|
|
||||||
|
foreach (var tableEntry in ptcJumpTable._dynamicTable)
|
||||||
|
{
|
||||||
|
writer.Write((int)tableEntry.EntryIndex);
|
||||||
|
writer.Write((long)tableEntry.GuestAddress);
|
||||||
|
writer.Write((int)tableEntry.HostAddress);
|
||||||
|
}
|
||||||
|
|
||||||
|
writer.Write((int)ptcJumpTable.Targets.Count);
|
||||||
|
|
||||||
|
foreach (ulong address in ptcJumpTable.Targets)
|
||||||
|
{
|
||||||
|
writer.Write((ulong)address);
|
||||||
|
}
|
||||||
|
|
||||||
|
writer.Write((int)ptcJumpTable.Dependants.Count);
|
||||||
|
|
||||||
|
foreach (var kv in ptcJumpTable.Dependants)
|
||||||
|
{
|
||||||
|
writer.Write((ulong)kv.Key); // address
|
||||||
|
|
||||||
|
writer.Write((int)kv.Value.Count);
|
||||||
|
|
||||||
|
foreach (int entry in kv.Value)
|
||||||
|
{
|
||||||
|
writer.Write((int)entry);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
writer.Write((int)ptcJumpTable.Owners.Count);
|
||||||
|
|
||||||
|
foreach (var kv in ptcJumpTable.Owners)
|
||||||
|
{
|
||||||
|
writer.Write((ulong)kv.Key); // address
|
||||||
|
|
||||||
|
writer.Write((int)kv.Value.Count);
|
||||||
|
|
||||||
|
foreach (int entry in kv.Value)
|
||||||
|
{
|
||||||
|
writer.Write((int)entry);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Initialize(JumpTable jumpTable)
|
public void Initialize(JumpTable jumpTable)
|
||||||
{
|
{
|
||||||
_targets.Clear();
|
Targets.Clear();
|
||||||
|
|
||||||
foreach (ulong guestAddress in jumpTable.Targets.Keys)
|
foreach (ulong guestAddress in jumpTable.Targets.Keys)
|
||||||
{
|
{
|
||||||
_targets.Add(guestAddress);
|
Targets.Add(guestAddress);
|
||||||
}
|
}
|
||||||
|
|
||||||
_dependants.Clear();
|
Dependants.Clear();
|
||||||
|
|
||||||
foreach (var item in jumpTable.Dependants)
|
foreach (var kv in jumpTable.Dependants)
|
||||||
{
|
{
|
||||||
_dependants.Add(item.Key, new List<int>(item.Value));
|
Dependants.Add(kv.Key, new List<int>(kv.Value));
|
||||||
}
|
}
|
||||||
|
|
||||||
_owners.Clear();
|
Owners.Clear();
|
||||||
|
|
||||||
foreach (var item in jumpTable.Owners)
|
foreach (var kv in jumpTable.Owners)
|
||||||
{
|
{
|
||||||
_owners.Add(item.Key, new List<int>(item.Value));
|
Owners.Add(kv.Key, new List<int>(kv.Value));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// For future use.
|
||||||
|
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 Clear()
|
public void Clear()
|
||||||
{
|
{
|
||||||
_jumpTable.Clear();
|
_jumpTable.Clear();
|
||||||
_dynamicTable.Clear();
|
_dynamicTable.Clear();
|
||||||
|
|
||||||
_targets.Clear();
|
Targets.Clear();
|
||||||
_dependants.Clear();
|
Dependants.Clear();
|
||||||
_owners.Clear();
|
Owners.Clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void WriteJumpTable(JumpTable jumpTable, ConcurrentDictionary<ulong, TranslatedFunction> funcs)
|
public void WriteJumpTable(JumpTable jumpTable, ConcurrentDictionary<ulong, TranslatedFunction> funcs)
|
||||||
{
|
{
|
||||||
// Writes internal state to jump table in-memory, after PTC was loaded.
|
// Writes internal state to jump table in-memory, after PtcJumpTable was deserialized.
|
||||||
|
|
||||||
foreach (var item in _jumpTable)
|
foreach (var tableEntry in _jumpTable)
|
||||||
{
|
{
|
||||||
long guestAddress = item.GuestAddress;
|
long guestAddress = tableEntry.GuestAddress;
|
||||||
DirectHostAddress directHostAddress = item.HostAddress;
|
DirectHostAddress directHostAddress = tableEntry.HostAddress;
|
||||||
|
|
||||||
long hostAddress;
|
long hostAddress;
|
||||||
|
|
||||||
|
@ -118,16 +306,21 @@ namespace ARMeilleure.Translation.PTC
|
||||||
hostAddress = func.FuncPtr.ToInt64();
|
hostAddress = func.FuncPtr.ToInt64();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
{
|
||||||
|
if (!PtcProfiler.ProfiledFuncs.TryGetValue((ulong)guestAddress, out var value) || !value.highCq)
|
||||||
{
|
{
|
||||||
throw new KeyNotFoundException($"({nameof(guestAddress)} = 0x{(ulong)guestAddress:X16})");
|
throw new KeyNotFoundException($"({nameof(guestAddress)} = 0x{(ulong)guestAddress:X16})");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
hostAddress = 0L;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
throw new InvalidOperationException(nameof(directHostAddress));
|
throw new InvalidOperationException(nameof(directHostAddress));
|
||||||
}
|
}
|
||||||
|
|
||||||
int entry = item.EntryIndex;
|
int entry = tableEntry.EntryIndex;
|
||||||
|
|
||||||
jumpTable.Table.SetEntry(entry);
|
jumpTable.Table.SetEntry(entry);
|
||||||
jumpTable.ExpandIfNeededJumpTable(entry);
|
jumpTable.ExpandIfNeededJumpTable(entry);
|
||||||
|
@ -141,17 +334,17 @@ namespace ARMeilleure.Translation.PTC
|
||||||
|
|
||||||
public void WriteDynamicTable(JumpTable jumpTable)
|
public void WriteDynamicTable(JumpTable jumpTable)
|
||||||
{
|
{
|
||||||
// Writes internal state to jump table in-memory, after PTC was loaded.
|
// Writes internal state to jump table in-memory, after PtcJumpTable was deserialized.
|
||||||
|
|
||||||
if (JumpTable.DynamicTableElems > 1)
|
if (JumpTable.DynamicTableElems > 1)
|
||||||
{
|
{
|
||||||
throw new NotSupportedException();
|
throw new NotSupportedException();
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (var item in _dynamicTable)
|
foreach (var tableEntry in _dynamicTable)
|
||||||
{
|
{
|
||||||
long guestAddress = item.GuestAddress;
|
long guestAddress = tableEntry.GuestAddress;
|
||||||
IndirectHostAddress indirectHostAddress = item.HostAddress;
|
IndirectHostAddress indirectHostAddress = tableEntry.HostAddress;
|
||||||
|
|
||||||
long hostAddress;
|
long hostAddress;
|
||||||
|
|
||||||
|
@ -168,7 +361,7 @@ namespace ARMeilleure.Translation.PTC
|
||||||
throw new InvalidOperationException(nameof(indirectHostAddress));
|
throw new InvalidOperationException(nameof(indirectHostAddress));
|
||||||
}
|
}
|
||||||
|
|
||||||
int entry = item.EntryIndex;
|
int entry = tableEntry.EntryIndex;
|
||||||
|
|
||||||
jumpTable.DynTable.SetEntry(entry);
|
jumpTable.DynTable.SetEntry(entry);
|
||||||
jumpTable.ExpandIfNeededDynamicTable(entry);
|
jumpTable.ExpandIfNeededDynamicTable(entry);
|
||||||
|
@ -182,7 +375,7 @@ namespace ARMeilleure.Translation.PTC
|
||||||
|
|
||||||
public void ReadJumpTable(JumpTable jumpTable)
|
public void ReadJumpTable(JumpTable jumpTable)
|
||||||
{
|
{
|
||||||
// Reads in-memory jump table state and store internally for PTC serialization.
|
// Reads in-memory jump table state and store internally for PtcJumpTable serialization.
|
||||||
|
|
||||||
_jumpTable.Clear();
|
_jumpTable.Clear();
|
||||||
|
|
||||||
|
@ -216,7 +409,7 @@ namespace ARMeilleure.Translation.PTC
|
||||||
|
|
||||||
public void ReadDynamicTable(JumpTable jumpTable)
|
public void ReadDynamicTable(JumpTable jumpTable)
|
||||||
{
|
{
|
||||||
// Reads in-memory jump table state and store internally for PTC serialization.
|
// Reads in-memory jump table state and store internally for PtcJumpTable serialization.
|
||||||
|
|
||||||
if (JumpTable.DynamicTableElems > 1)
|
if (JumpTable.DynamicTableElems > 1)
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
using ARMeilleure.State;
|
using ARMeilleure.State;
|
||||||
|
using Ryujinx.Common.Logging;
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Concurrent;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.IO.Compression;
|
using System.IO.Compression;
|
||||||
using System.Runtime.Serialization.Formatters.Binary;
|
|
||||||
using System.Security.Cryptography;
|
using System.Security.Cryptography;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
|
|
||||||
|
@ -12,12 +13,14 @@ namespace ARMeilleure.Translation.PTC
|
||||||
{
|
{
|
||||||
public static class PtcProfiler
|
public static class PtcProfiler
|
||||||
{
|
{
|
||||||
|
private const string HeaderMagic = "Phd";
|
||||||
|
|
||||||
|
private const uint InternalVersion = 1713; //! Not to be incremented manually for each change to the ARMeilleure project.
|
||||||
|
|
||||||
private const int SaveInterval = 30; // Seconds.
|
private const int SaveInterval = 30; // Seconds.
|
||||||
|
|
||||||
private const CompressionLevel SaveCompressionLevel = CompressionLevel.Fastest;
|
private const CompressionLevel SaveCompressionLevel = CompressionLevel.Fastest;
|
||||||
|
|
||||||
private static readonly BinaryFormatter _binaryFormatter;
|
|
||||||
|
|
||||||
private static readonly System.Timers.Timer _timer;
|
private static readonly System.Timers.Timer _timer;
|
||||||
|
|
||||||
private static readonly ManualResetEvent _waitEvent;
|
private static readonly ManualResetEvent _waitEvent;
|
||||||
|
@ -26,17 +29,15 @@ namespace ARMeilleure.Translation.PTC
|
||||||
|
|
||||||
private static bool _disposed;
|
private static bool _disposed;
|
||||||
|
|
||||||
internal static Dictionary<ulong, (ExecutionMode mode, bool highCq)> ProfiledFuncs { get; private set; } //! Not to be modified.
|
internal static Dictionary<ulong, (ExecutionMode mode, bool highCq)> ProfiledFuncs { get; private set; }
|
||||||
|
|
||||||
internal static bool Enabled { get; private set; }
|
internal static bool Enabled { get; private set; }
|
||||||
|
|
||||||
public static ulong StaticCodeStart { internal get; set; }
|
public static ulong StaticCodeStart { internal get; set; }
|
||||||
public static int StaticCodeSize { internal get; set; }
|
public static ulong StaticCodeSize { internal get; set; }
|
||||||
|
|
||||||
static PtcProfiler()
|
static PtcProfiler()
|
||||||
{
|
{
|
||||||
_binaryFormatter = new BinaryFormatter();
|
|
||||||
|
|
||||||
_timer = new System.Timers.Timer((double)SaveInterval * 1000d);
|
_timer = new System.Timers.Timer((double)SaveInterval * 1000d);
|
||||||
_timer.Elapsed += PreSave;
|
_timer.Elapsed += PreSave;
|
||||||
|
|
||||||
|
@ -55,11 +56,11 @@ namespace ARMeilleure.Translation.PTC
|
||||||
{
|
{
|
||||||
if (IsAddressInStaticCodeRange(address))
|
if (IsAddressInStaticCodeRange(address))
|
||||||
{
|
{
|
||||||
|
Debug.Assert(!highCq);
|
||||||
|
|
||||||
lock (_lock)
|
lock (_lock)
|
||||||
{
|
{
|
||||||
Debug.Assert(!highCq && !ProfiledFuncs.ContainsKey(address));
|
ProfiledFuncs.TryAdd(address, (mode, highCq: false));
|
||||||
|
|
||||||
ProfiledFuncs.TryAdd(address, (mode, highCq));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -68,18 +69,35 @@ namespace ARMeilleure.Translation.PTC
|
||||||
{
|
{
|
||||||
if (IsAddressInStaticCodeRange(address))
|
if (IsAddressInStaticCodeRange(address))
|
||||||
{
|
{
|
||||||
|
Debug.Assert(highCq);
|
||||||
|
|
||||||
lock (_lock)
|
lock (_lock)
|
||||||
{
|
{
|
||||||
Debug.Assert(highCq && ProfiledFuncs.ContainsKey(address));
|
Debug.Assert(ProfiledFuncs.ContainsKey(address));
|
||||||
|
|
||||||
ProfiledFuncs[address] = (mode, highCq);
|
ProfiledFuncs[address] = (mode, highCq: true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static bool IsAddressInStaticCodeRange(ulong address)
|
internal static bool IsAddressInStaticCodeRange(ulong address)
|
||||||
{
|
{
|
||||||
return address >= StaticCodeStart && address < StaticCodeStart + (ulong)StaticCodeSize;
|
return address >= StaticCodeStart && address < StaticCodeStart + StaticCodeSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static Dictionary<ulong, (ExecutionMode mode, bool highCq)> GetProfiledFuncsToTranslate(ConcurrentDictionary<ulong, TranslatedFunction> funcs)
|
||||||
|
{
|
||||||
|
var profiledFuncsToTranslate = new Dictionary<ulong, (ExecutionMode mode, bool highCq)>(ProfiledFuncs);
|
||||||
|
|
||||||
|
foreach (ulong address in profiledFuncsToTranslate.Keys)
|
||||||
|
{
|
||||||
|
if (funcs.ContainsKey(address))
|
||||||
|
{
|
||||||
|
profiledFuncsToTranslate.Remove(address);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return profiledFuncsToTranslate;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static void ClearEntries()
|
internal static void ClearEntries()
|
||||||
|
@ -97,21 +115,21 @@ namespace ARMeilleure.Translation.PTC
|
||||||
|
|
||||||
if (fileInfoActual.Exists && fileInfoActual.Length != 0L)
|
if (fileInfoActual.Exists && fileInfoActual.Length != 0L)
|
||||||
{
|
{
|
||||||
if (!Load(fileNameActual))
|
if (!Load(fileNameActual, false))
|
||||||
{
|
{
|
||||||
if (fileInfoBackup.Exists && fileInfoBackup.Length != 0L)
|
if (fileInfoBackup.Exists && fileInfoBackup.Length != 0L)
|
||||||
{
|
{
|
||||||
Load(fileNameBackup);
|
Load(fileNameBackup, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (fileInfoBackup.Exists && fileInfoBackup.Length != 0L)
|
else if (fileInfoBackup.Exists && fileInfoBackup.Length != 0L)
|
||||||
{
|
{
|
||||||
Load(fileNameBackup);
|
Load(fileNameBackup, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static bool Load(string fileName)
|
private static bool Load(string fileName, bool isBackup)
|
||||||
{
|
{
|
||||||
using (FileStream compressedStream = new FileStream(fileName, FileMode.Open))
|
using (FileStream compressedStream = new FileStream(fileName, FileMode.Open))
|
||||||
using (DeflateStream deflateStream = new DeflateStream(compressedStream, CompressionMode.Decompress, true))
|
using (DeflateStream deflateStream = new DeflateStream(compressedStream, CompressionMode.Decompress, true))
|
||||||
|
@ -147,9 +165,25 @@ namespace ARMeilleure.Translation.PTC
|
||||||
|
|
||||||
stream.Seek((long)hashSize, SeekOrigin.Begin);
|
stream.Seek((long)hashSize, SeekOrigin.Begin);
|
||||||
|
|
||||||
|
Header header = ReadHeader(stream);
|
||||||
|
|
||||||
|
if (header.Magic != HeaderMagic)
|
||||||
|
{
|
||||||
|
InvalidateCompressedStream(compressedStream);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (header.InfoFileVersion != InternalVersion)
|
||||||
|
{
|
||||||
|
InvalidateCompressedStream(compressedStream);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
ProfiledFuncs = (Dictionary<ulong, (ExecutionMode, bool)>)_binaryFormatter.Deserialize(stream);
|
ProfiledFuncs = Deserialize(stream);
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
{
|
{
|
||||||
|
@ -159,16 +193,56 @@ namespace ARMeilleure.Translation.PTC
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
long fileSize = new FileInfo(fileName).Length;
|
||||||
|
|
||||||
|
Logger.Info?.Print(LogClass.Ptc, $"{(isBackup ? "Loaded Backup Profiling Info" : "Loaded Profiling Info")} (size: {fileSize} bytes, profiled functions: {ProfiledFuncs.Count}).");
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
private static bool CompareHash(ReadOnlySpan<byte> currentHash, ReadOnlySpan<byte> expectedHash)
|
private static bool CompareHash(ReadOnlySpan<byte> currentHash, ReadOnlySpan<byte> expectedHash)
|
||||||
{
|
{
|
||||||
return currentHash.SequenceEqual(expectedHash);
|
return currentHash.SequenceEqual(expectedHash);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static Header ReadHeader(MemoryStream stream)
|
||||||
|
{
|
||||||
|
using (BinaryReader headerReader = new BinaryReader(stream, EncodingCache.UTF8NoBOM, true))
|
||||||
|
{
|
||||||
|
Header header = new Header();
|
||||||
|
|
||||||
|
header.Magic = headerReader.ReadString();
|
||||||
|
|
||||||
|
header.InfoFileVersion = headerReader.ReadUInt32();
|
||||||
|
|
||||||
|
return header;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Dictionary<ulong, (ExecutionMode, bool)> Deserialize(MemoryStream stream)
|
||||||
|
{
|
||||||
|
using (BinaryReader reader = new BinaryReader(stream, EncodingCache.UTF8NoBOM, true))
|
||||||
|
{
|
||||||
|
var profiledFuncs = new Dictionary<ulong, (ExecutionMode, bool)>();
|
||||||
|
|
||||||
|
int profiledFuncsCount = reader.ReadInt32();
|
||||||
|
|
||||||
|
for (int i = 0; i < profiledFuncsCount; i++)
|
||||||
|
{
|
||||||
|
ulong address = reader.ReadUInt64();
|
||||||
|
|
||||||
|
ExecutionMode mode = (ExecutionMode)reader.ReadInt32();
|
||||||
|
bool highCq = reader.ReadBoolean();
|
||||||
|
|
||||||
|
profiledFuncs.Add(address, (mode, highCq));
|
||||||
|
}
|
||||||
|
|
||||||
|
return profiledFuncs;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static void InvalidateCompressedStream(FileStream compressedStream)
|
private static void InvalidateCompressedStream(FileStream compressedStream)
|
||||||
{
|
{
|
||||||
compressedStream.SetLength(0L);
|
compressedStream.SetLength(0L);
|
||||||
|
@ -195,6 +269,8 @@ namespace ARMeilleure.Translation.PTC
|
||||||
|
|
||||||
private static void Save(string fileName)
|
private static void Save(string fileName)
|
||||||
{
|
{
|
||||||
|
int profiledFuncsCount;
|
||||||
|
|
||||||
using (MemoryStream stream = new MemoryStream())
|
using (MemoryStream stream = new MemoryStream())
|
||||||
using (MD5 md5 = MD5.Create())
|
using (MD5 md5 = MD5.Create())
|
||||||
{
|
{
|
||||||
|
@ -202,9 +278,13 @@ namespace ARMeilleure.Translation.PTC
|
||||||
|
|
||||||
stream.Seek((long)hashSize, SeekOrigin.Begin);
|
stream.Seek((long)hashSize, SeekOrigin.Begin);
|
||||||
|
|
||||||
|
WriteHeader(stream);
|
||||||
|
|
||||||
lock (_lock)
|
lock (_lock)
|
||||||
{
|
{
|
||||||
_binaryFormatter.Serialize(stream, ProfiledFuncs);
|
Serialize(stream, ProfiledFuncs);
|
||||||
|
|
||||||
|
profiledFuncsCount = ProfiledFuncs.Count;
|
||||||
}
|
}
|
||||||
|
|
||||||
stream.Seek((long)hashSize, SeekOrigin.Begin);
|
stream.Seek((long)hashSize, SeekOrigin.Begin);
|
||||||
|
@ -231,6 +311,43 @@ namespace ARMeilleure.Translation.PTC
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
long fileSize = new FileInfo(fileName).Length;
|
||||||
|
|
||||||
|
Logger.Info?.Print(LogClass.Ptc, $"Saved Profiling Info (size: {fileSize} bytes, profiled functions: {profiledFuncsCount}).");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void WriteHeader(MemoryStream stream)
|
||||||
|
{
|
||||||
|
using (BinaryWriter headerWriter = new BinaryWriter(stream, EncodingCache.UTF8NoBOM, true))
|
||||||
|
{
|
||||||
|
headerWriter.Write((string)HeaderMagic); // Header.Magic
|
||||||
|
|
||||||
|
headerWriter.Write((uint)InternalVersion); // Header.InfoFileVersion
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void Serialize(MemoryStream stream, Dictionary<ulong, (ExecutionMode mode, bool highCq)> profiledFuncs)
|
||||||
|
{
|
||||||
|
using (BinaryWriter writer = new BinaryWriter(stream, EncodingCache.UTF8NoBOM, true))
|
||||||
|
{
|
||||||
|
writer.Write((int)profiledFuncs.Count);
|
||||||
|
|
||||||
|
foreach (var kv in profiledFuncs)
|
||||||
|
{
|
||||||
|
writer.Write((ulong)kv.Key); // address
|
||||||
|
|
||||||
|
writer.Write((int)kv.Value.mode);
|
||||||
|
writer.Write((bool)kv.Value.highCq);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private struct Header
|
||||||
|
{
|
||||||
|
public string Magic;
|
||||||
|
|
||||||
|
public uint InfoFileVersion;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static void Start()
|
internal static void Start()
|
||||||
|
|
|
@ -2,6 +2,8 @@ namespace ARMeilleure.Translation.PTC
|
||||||
{
|
{
|
||||||
struct RelocEntry
|
struct RelocEntry
|
||||||
{
|
{
|
||||||
|
public const int Stride = 8; // Bytes.
|
||||||
|
|
||||||
public int Position;
|
public int Position;
|
||||||
public int Index;
|
public int Index;
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
using ARMeilleure.IntermediateRepresentation;
|
using ARMeilleure.IntermediateRepresentation;
|
||||||
using System.Collections.Generic;
|
|
||||||
|
|
||||||
using static ARMeilleure.IntermediateRepresentation.OperandHelper;
|
using static ARMeilleure.IntermediateRepresentation.OperandHelper;
|
||||||
using static ARMeilleure.IntermediateRepresentation.OperationHelper;
|
using static ARMeilleure.IntermediateRepresentation.OperationHelper;
|
||||||
|
|
|
@ -31,10 +31,11 @@ namespace ARMeilleure.Translation
|
||||||
private readonly ReaderWriterLock _backgroundTranslatorLock;
|
private readonly ReaderWriterLock _backgroundTranslatorLock;
|
||||||
|
|
||||||
private JumpTable _jumpTable;
|
private JumpTable _jumpTable;
|
||||||
|
internal JumpTable JumpTable => _jumpTable;
|
||||||
|
|
||||||
private volatile int _threadCount;
|
private volatile int _threadCount;
|
||||||
|
|
||||||
// FIXME: Remove this once the init logic of the emulator will be redone
|
// FIXME: Remove this once the init logic of the emulator will be redone.
|
||||||
public static ManualResetEvent IsReadyForTranslation = new ManualResetEvent(false);
|
public static ManualResetEvent IsReadyForTranslation = new ManualResetEvent(false);
|
||||||
|
|
||||||
public Translator(IJitMemoryAllocator allocator, IMemoryManager memory)
|
public Translator(IJitMemoryAllocator allocator, IMemoryManager memory)
|
||||||
|
@ -100,7 +101,7 @@ namespace ARMeilleure.Translation
|
||||||
|
|
||||||
if (Ptc.State == PtcState.Enabled)
|
if (Ptc.State == PtcState.Enabled)
|
||||||
{
|
{
|
||||||
Ptc.LoadTranslations(_funcs, _memory.PageTablePointer, _jumpTable);
|
Ptc.LoadTranslations(_funcs, _memory, _jumpTable);
|
||||||
Ptc.MakeAndSaveTranslations(_funcs, _memory, _jumpTable);
|
Ptc.MakeAndSaveTranslations(_funcs, _memory, _jumpTable);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -170,7 +170,7 @@ namespace Ryujinx.HLE.HOS
|
||||||
}
|
}
|
||||||
|
|
||||||
PtcProfiler.StaticCodeStart = codeStart;
|
PtcProfiler.StaticCodeStart = codeStart;
|
||||||
PtcProfiler.StaticCodeSize = (int)codeSize;
|
PtcProfiler.StaticCodeSize = (ulong)codeSize;
|
||||||
|
|
||||||
int codePagesCount = (int)(codeSize / KMemoryManager.PageSize);
|
int codePagesCount = (int)(codeSize / KMemoryManager.PageSize);
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue