aboutsummaryrefslogtreecommitdiff
path: root/ARMeilleure
diff options
context:
space:
mode:
authorLDj3SNuD <35856442+LDj3SNuD@users.noreply.github.com>2020-12-17 20:32:09 +0100
committerGitHub <noreply@github.com>2020-12-17 20:32:09 +0100
commitb5c215111de665ef8d18b38405ac55e17996e30e (patch)
tree3fd285d43f466dbe10b80510b20b2076391555b4 /ARMeilleure
parent10aa11ce13291cf2ea2aeb751838c65c45fdc0ba (diff)
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.
Diffstat (limited to 'ARMeilleure')
-rw-r--r--ARMeilleure/CodeGen/Unwinding/UnwindInfo.cs2
-rw-r--r--ARMeilleure/CodeGen/Unwinding/UnwindPushEntry.cs2
-rw-r--r--ARMeilleure/Decoders/Decoder.cs2
-rw-r--r--ARMeilleure/Instructions/NativeInterface.cs3
-rw-r--r--ARMeilleure/State/ExecutionMode.cs6
-rw-r--r--ARMeilleure/Translation/Cache/JumpTable.cs59
-rw-r--r--ARMeilleure/Translation/DelegateInfo.cs2
-rw-r--r--ARMeilleure/Translation/JumpTableEntryAllocator.cs72
-rw-r--r--ARMeilleure/Translation/PTC/Ptc.cs227
-rw-r--r--ARMeilleure/Translation/PTC/PtcInfo.cs2
-rw-r--r--ARMeilleure/Translation/PTC/PtcJumpTable.cs281
-rw-r--r--ARMeilleure/Translation/PTC/PtcProfiler.cs159
-rw-r--r--ARMeilleure/Translation/PTC/RelocEntry.cs2
-rw-r--r--ARMeilleure/Translation/SsaDeconstruction.cs1
-rw-r--r--ARMeilleure/Translation/Translator.cs5
15 files changed, 663 insertions, 162 deletions
diff --git a/ARMeilleure/CodeGen/Unwinding/UnwindInfo.cs b/ARMeilleure/CodeGen/Unwinding/UnwindInfo.cs
index 8072acd9..3d0bc21d 100644
--- a/ARMeilleure/CodeGen/Unwinding/UnwindInfo.cs
+++ b/ARMeilleure/CodeGen/Unwinding/UnwindInfo.cs
@@ -2,6 +2,8 @@ namespace ARMeilleure.CodeGen.Unwinding
{
struct UnwindInfo
{
+ public const int Stride = 4; // Bytes.
+
public UnwindPushEntry[] PushEntries { get; }
public int PrologSize { get; }
diff --git a/ARMeilleure/CodeGen/Unwinding/UnwindPushEntry.cs b/ARMeilleure/CodeGen/Unwinding/UnwindPushEntry.cs
index 021479a4..fd8ea402 100644
--- a/ARMeilleure/CodeGen/Unwinding/UnwindPushEntry.cs
+++ b/ARMeilleure/CodeGen/Unwinding/UnwindPushEntry.cs
@@ -2,6 +2,8 @@ namespace ARMeilleure.CodeGen.Unwinding
{
struct UnwindPushEntry
{
+ public const int Stride = 16; // Bytes.
+
public UnwindPseudoOp PseudoOp { get; }
public int PrologOffset { get; }
public int RegIndex { get; }
diff --git a/ARMeilleure/Decoders/Decoder.cs b/ARMeilleure/Decoders/Decoder.cs
index 8666cdcc..eb085999 100644
--- a/ARMeilleure/Decoders/Decoder.cs
+++ b/ARMeilleure/Decoders/Decoder.cs
@@ -26,6 +26,8 @@ namespace ARMeilleure.Decoders
Dictionary<ulong, Block> visited = new Dictionary<ulong, Block>();
+ Debug.Assert(MaxInstsPerFunctionLowCq <= MaxInstsPerFunction);
+
int opsCount = 0;
int instructionLimit = highCq ? MaxInstsPerFunction : MaxInstsPerFunctionLowCq;
diff --git a/ARMeilleure/Instructions/NativeInterface.cs b/ARMeilleure/Instructions/NativeInterface.cs
index b2b200b9..b8b7ff0e 100644
--- a/ARMeilleure/Instructions/NativeInterface.cs
+++ b/ARMeilleure/Instructions/NativeInterface.cs
@@ -2,6 +2,7 @@ using ARMeilleure.Memory;
using ARMeilleure.State;
using ARMeilleure.Translation;
using System;
+using System.Diagnostics;
using System.Runtime.InteropServices;
namespace ARMeilleure.Instructions
@@ -254,6 +255,8 @@ namespace ARMeilleure.Instructions
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.
Marshal.WriteInt64((IntPtr)entryAddress, 8, (long)ptr);
}
diff --git a/ARMeilleure/State/ExecutionMode.cs b/ARMeilleure/State/ExecutionMode.cs
index eaed9d27..f43c5569 100644
--- a/ARMeilleure/State/ExecutionMode.cs
+++ b/ARMeilleure/State/ExecutionMode.cs
@@ -2,8 +2,8 @@ namespace ARMeilleure.State
{
enum ExecutionMode
{
- Aarch32Arm,
- Aarch32Thumb,
- Aarch64
+ Aarch32Arm = 0,
+ Aarch32Thumb = 1,
+ Aarch64 = 2
}
} \ No newline at end of file
diff --git a/ARMeilleure/Translation/Cache/JumpTable.cs b/ARMeilleure/Translation/Cache/JumpTable.cs
index 71a036d8..aa3b6caf 100644
--- a/ARMeilleure/Translation/Cache/JumpTable.cs
+++ b/ARMeilleure/Translation/Cache/JumpTable.cs
@@ -14,7 +14,7 @@ namespace ARMeilleure.Translation.Cache
// 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
// 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.
@@ -42,7 +42,7 @@ namespace ARMeilleure.Translation.Cache
private const int DynamicTableSize = 1048576;
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 _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,19 +182,25 @@ namespace ARMeilleure.Translation.Cache
// For future use.
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];
-
- bool isDynamic = (entry & DynamicEntryTag) != 0;
+ if ((entry & DynamicEntryTag) == 0)
+ {
+ IntPtr addr = GetEntryAddressJumpTable(entry);
- entry &= ~DynamicEntryTag;
+ Marshal.WriteInt64(addr, 0, 0L);
+ Marshal.WriteInt64(addr, 8, 0L);
- if (isDynamic)
+ Table.FreeEntry(entry);
+ }
+ else
{
- IntPtr addr = GetEntryAddressDynamicTable(entry);
+ IntPtr addr = GetEntryAddressDynamicTable(entry & ~DynamicEntryTag);
for (int j = 0; j < DynamicTableElems; j++)
{
@@ -202,16 +208,7 @@ namespace ARMeilleure.Translation.Cache
Marshal.WriteInt64(addr + j * JumpTableStride, 8, 0L);
}
- DynTable.FreeEntry(entry);
- }
- else
- {
- IntPtr addr = GetEntryAddressJumpTable(entry);
-
- Marshal.WriteInt64(addr, 0, 0L);
- Marshal.WriteInt64(addr, 8, 0L);
-
- Table.FreeEntry(entry);
+ DynTable.FreeEntry(entry & ~DynamicEntryTag);
}
}
}
@@ -259,6 +256,20 @@ namespace ARMeilleure.Translation.Cache
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()
{
_jumpRegion.Dispose();
diff --git a/ARMeilleure/Translation/DelegateInfo.cs b/ARMeilleure/Translation/DelegateInfo.cs
index e68cfc1b..36320ac3 100644
--- a/ARMeilleure/Translation/DelegateInfo.cs
+++ b/ARMeilleure/Translation/DelegateInfo.cs
@@ -3,7 +3,7 @@ using System.Runtime.InteropServices;
namespace ARMeilleure.Translation
{
- sealed class DelegateInfo
+ class DelegateInfo
{
private readonly Delegate _dlg; // Ensure that this delegate will not be garbage collected.
diff --git a/ARMeilleure/Translation/JumpTableEntryAllocator.cs b/ARMeilleure/Translation/JumpTableEntryAllocator.cs
new file mode 100644
index 00000000..3b918628
--- /dev/null
+++ b/ARMeilleure/Translation/JumpTableEntryAllocator.cs
@@ -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;
+ }
+ }
+}
diff --git a/ARMeilleure/Translation/PTC/Ptc.cs b/ARMeilleure/Translation/PTC/Ptc.cs
index 1cdaa8fe..3150c97c 100644
--- a/ARMeilleure/Translation/PTC/Ptc.cs
+++ b/ARMeilleure/Translation/PTC/Ptc.cs
@@ -12,7 +12,7 @@ using System.Diagnostics;
using System.IO;
using System.IO.Compression;
using System.Runtime.InteropServices;
-using System.Runtime.Serialization.Formatters.Binary;
+using System.Security.Cryptography;
using System.Threading;
using System.Threading.Tasks;
@@ -22,7 +22,7 @@ namespace ARMeilleure.Translation.PTC
{
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 BackupDir = "1";
@@ -34,6 +34,7 @@ namespace ARMeilleure.Translation.PTC
internal const int JumpPointerIndex = -2; // 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 static readonly MemoryStream _infosStream;
@@ -43,8 +44,6 @@ namespace ARMeilleure.Translation.PTC
private static readonly BinaryWriter _infosWriter;
- private static readonly BinaryFormatter _binaryFormatter;
-
private static readonly ManualResetEvent _waitEvent;
private static readonly AutoResetEvent _loggerEvent;
@@ -54,7 +53,6 @@ namespace ARMeilleure.Translation.PTC
private static bool _disposed;
private static volatile int _translateCount;
- private static volatile int _rejitCount;
internal static PtcJumpTable PtcJumpTable { get; private set; }
@@ -75,8 +73,6 @@ namespace ARMeilleure.Translation.PTC
_infosWriter = new BinaryWriter(_infosStream, EncodingCache.UTF8NoBOM, true);
- _binaryFormatter = new BinaryFormatter();
-
_waitEvent = new ManualResetEvent(true);
_loggerEvent = new AutoResetEvent(false);
@@ -174,26 +170,26 @@ namespace ARMeilleure.Translation.PTC
if (fileInfoActual.Exists && fileInfoActual.Length != 0L)
{
- if (!Load(fileNameActual))
+ if (!Load(fileNameActual, false))
{
if (fileInfoBackup.Exists && fileInfoBackup.Length != 0L)
{
- Load(fileNameBackup);
+ Load(fileNameBackup, true);
}
}
}
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 (DeflateStream deflateStream = new DeflateStream(compressedStream, CompressionMode.Decompress, true))
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;
@@ -247,6 +243,13 @@ namespace ARMeilleure.Translation.PTC
return false;
}
+ if (header.OSPlatform != GetOSPlatform())
+ {
+ InvalidateCompressedStream(compressedStream);
+
+ return false;
+ }
+
if (header.InfosLen % InfoEntry.Stride != 0)
{
InvalidateCompressedStream(compressedStream);
@@ -266,7 +269,7 @@ namespace ARMeilleure.Translation.PTC
try
{
- PtcJumpTable = (PtcJumpTable)_binaryFormatter.Deserialize(stream);
+ PtcJumpTable = PtcJumpTable.Deserialize(stream);
}
catch
{
@@ -281,9 +284,13 @@ namespace ARMeilleure.Translation.PTC
_codesStream.Write(codesBuf, 0, header.CodesLen);
_relocsStream.Write(relocsBuf, 0, header.RelocsLen);
_unwindInfosStream.Write(unwindInfosBuf, 0, header.UnwindInfosLen);
-
- return true;
}
+
+ 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;
}
private static bool CompareHash(ReadOnlySpan<byte> currentHash, ReadOnlySpan<byte> expectedHash)
@@ -299,8 +306,9 @@ namespace ARMeilleure.Translation.PTC
header.Magic = headerReader.ReadString();
- header.CacheFileVersion = headerReader.ReadInt32();
+ header.CacheFileVersion = headerReader.ReadUInt32();
header.FeatureInfo = headerReader.ReadUInt64();
+ header.OSPlatform = headerReader.ReadUInt32();
header.InfosLen = headerReader.ReadInt32();
header.CodesLen = headerReader.ReadInt32();
@@ -338,7 +346,7 @@ namespace ARMeilleure.Translation.PTC
private static void Save(string fileName)
{
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;
@@ -351,7 +359,7 @@ namespace ARMeilleure.Translation.PTC
_relocsStream.WriteTo(stream);
_unwindInfosStream.WriteTo(stream);
- _binaryFormatter.Serialize(stream, PtcJumpTable);
+ PtcJumpTable.Serialize(stream, PtcJumpTable);
stream.Seek((long)hashSize, SeekOrigin.Begin);
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)
@@ -385,8 +397,9 @@ namespace ARMeilleure.Translation.PTC
{
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((uint)GetOSPlatform()); // Header.OSPlatform
headerWriter.Write((int)_infosStream.Length); // Header.InfosLen
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 ||
(int)_codesStream.Length == 0 ||
@@ -417,26 +430,44 @@ namespace ARMeilleure.Translation.PTC
using (BinaryReader relocsReader = new BinaryReader(_relocsStream, EncodingCache.UTF8NoBOM, true))
using (BinaryReader unwindInfosReader = new BinaryReader(_unwindInfosStream, EncodingCache.UTF8NoBOM, true))
{
- int infosEntriesCount = (int)_infosStream.Length / InfoEntry.Stride;
-
- for (int i = 0; i < infosEntriesCount; i++)
+ for (int i = 0; i < GetInfosEntriesCount(); i++)
{
InfoEntry infoEntry = ReadInfo(infosReader);
- byte[] code = ReadCode(codesReader, infoEntry.CodeLen);
-
- if (infoEntry.RelocEntriesCount != 0)
+ if (infoEntry.Stubbed)
{
- RelocEntry[] relocEntries = GetRelocEntries(relocsReader, infoEntry.RelocEntriesCount);
-
- PatchCode(code, relocEntries, pageTablePointer, jumpTable);
+ 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);
+
+ if (infoEntry.RelocEntriesCount != 0)
+ {
+ RelocEntry[] relocEntries = GetRelocEntries(relocsReader, infoEntry.RelocEntriesCount);
- UnwindInfo unwindInfo = ReadUnwindInfo(unwindInfosReader);
+ PatchCode(code, relocEntries, memory.PageTablePointer, jumpTable);
+ }
- TranslatedFunction func = FastTranslate(code, infoEntry.GuestSize, unwindInfo, infoEntry.HighCq);
+ UnwindInfo unwindInfo = ReadUnwindInfo(unwindInfosReader);
- funcs.AddOrUpdate(infoEntry.Address, func, (key, oldFunc) => func.HighCq && !oldFunc.HighCq ? func : oldFunc);
+ TranslatedFunction func = FastTranslate(code, infoEntry.GuestSize, unwindInfo, infoEntry.HighCq);
+
+ 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.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)
@@ -461,12 +499,30 @@ namespace ARMeilleure.Translation.PTC
infoEntry.Address = infosReader.ReadUInt64();
infoEntry.GuestSize = infosReader.ReadUInt64();
infoEntry.HighCq = infosReader.ReadBoolean();
+ infoEntry.Stubbed = infosReader.ReadBoolean();
infoEntry.CodeLen = infosReader.ReadInt32();
infoEntry.RelocEntriesCount = infosReader.ReadInt32();
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)
{
byte[] codeBuf = new byte[codeLen];
@@ -556,50 +612,79 @@ namespace ARMeilleure.Translation.PTC
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)
{
- if (PtcProfiler.ProfiledFuncs.Count == 0)
+ var profiledFuncsToTranslate = PtcProfiler.GetProfiledFuncsToTranslate(funcs);
+
+ if (profiledFuncsToTranslate.Count == 0)
{
return;
}
_translateCount = 0;
- _rejitCount = 0;
- ThreadPool.QueueUserWorkItem(TranslationLogger, (funcs.Count, PtcProfiler.ProfiledFuncs.Count));
+ ThreadPool.QueueUserWorkItem(TranslationLogger, profiledFuncsToTranslate.Count);
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;
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);
- if (func.HighCq)
- {
- jumpTable.RegisterFunction(address, func);
- }
+ Debug.Assert(isAddressUnique, $"The address 0x{address:X16} is not unique.");
- Interlocked.Increment(ref _translateCount);
- }
- else if (item.Value.highCq && !funcs[address].HighCq)
+ if (func.HighCq)
{
- TranslatedFunction func = Translator.Translate(memory, jumpTable, address, item.Value.mode, highCq: true);
-
- funcs[address] = func;
-
jumpTable.RegisterFunction(address, func);
-
- Interlocked.Increment(ref _rejitCount);
}
+ Interlocked.Increment(ref _translateCount);
+
if (State != PtcState.Enabled)
{
state.Stop();
@@ -608,30 +693,27 @@ namespace ARMeilleure.Translation.PTC
_loggerEvent.Set();
- if (_translateCount != 0 || _rejitCount != 0)
- {
- PtcJumpTable.Initialize(jumpTable);
+ PtcJumpTable.Initialize(jumpTable);
- PtcJumpTable.ReadJumpTable(jumpTable);
- PtcJumpTable.ReadDynamicTable(jumpTable);
+ PtcJumpTable.ReadJumpTable(jumpTable);
+ PtcJumpTable.ReadDynamicTable(jumpTable);
- ThreadPool.QueueUserWorkItem(PreSave);
- }
+ ThreadPool.QueueUserWorkItem(PreSave);
}
private static void TranslationLogger(object state)
{
const int refreshRate = 1; // Seconds.
- (int funcsCount, int ProfiledFuncsCount) = ((int, int))state;
+ int profiledFuncsToTranslateCount = (int)state;
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));
- 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)
@@ -642,6 +724,7 @@ namespace ARMeilleure.Translation.PTC
_infosWriter.Write((ulong)address); // InfoEntry.Address
_infosWriter.Write((ulong)guestSize); // InfoEntry.GuestSize
_infosWriter.Write((bool)highCq); // InfoEntry.HighCq
+ _infosWriter.Write((bool)false); // InfoEntry.Stubbed
_infosWriter.Write((int)ptcInfo.CodeStream.Length); // InfoEntry.CodeLen
_infosWriter.Write((int)ptcInfo.RelocEntriesCount); // InfoEntry.RelocEntriesCount
@@ -661,12 +744,25 @@ namespace ARMeilleure.Translation.PTC
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
{
public string Magic;
- public int CacheFileVersion;
+ public uint CacheFileVersion;
public ulong FeatureInfo;
+ public uint OSPlatform;
public int InfosLen;
public int CodesLen;
@@ -676,11 +772,12 @@ namespace ARMeilleure.Translation.PTC
private struct InfoEntry
{
- public const int Stride = 25; // Bytes.
+ public const int Stride = 26; // Bytes.
public ulong Address;
public ulong GuestSize;
public bool HighCq;
+ public bool Stubbed;
public int CodeLen;
public int RelocEntriesCount;
}
diff --git a/ARMeilleure/Translation/PTC/PtcInfo.cs b/ARMeilleure/Translation/PTC/PtcInfo.cs
index f03eb6ba..547fe6d8 100644
--- a/ARMeilleure/Translation/PTC/PtcInfo.cs
+++ b/ARMeilleure/Translation/PTC/PtcInfo.cs
@@ -4,7 +4,7 @@ using System.IO;
namespace ARMeilleure.Translation.PTC
{
- sealed class PtcInfo : IDisposable
+ class PtcInfo : IDisposable
{
private readonly BinaryWriter _relocWriter;
private readonly BinaryWriter _unwindInfoWriter;
diff --git a/ARMeilleure/Translation/PTC/PtcJumpTable.cs b/ARMeilleure/Translation/PTC/PtcJumpTable.cs
index e4af68d6..d52d0874 100644
--- a/ARMeilleure/Translation/PTC/PtcJumpTable.cs
+++ b/ARMeilleure/Translation/PTC/PtcJumpTable.cs
@@ -2,15 +2,15 @@ using ARMeilleure.Translation.Cache;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
+using System.Diagnostics;
+using System.IO;
using System.Runtime.InteropServices;
namespace ARMeilleure.Translation.PTC
{
- [Serializable]
class PtcJumpTable
{
- [Serializable]
- private struct TableEntry<TAddress>
+ public struct TableEntry<TAddress>
{
public int EntryIndex;
public long GuestAddress;
@@ -24,82 +24,270 @@ namespace ARMeilleure.Translation.PTC
}
}
- private enum DirectHostAddress
+ public enum DirectHostAddress
{
- CallStub,
- TailCallStub,
- Host
+ CallStub = 0,
+ TailCallStub = 1,
+ Host = 2
}
- private enum IndirectHostAddress
+ public enum IndirectHostAddress
{
- CallStub,
- TailCallStub
+ CallStub = 0,
+ TailCallStub = 1
}
private readonly List<TableEntry<DirectHostAddress>> _jumpTable;
private readonly List<TableEntry<IndirectHostAddress>> _dynamicTable;
- private readonly List<ulong> _targets;
- private readonly Dictionary<ulong, List<int>> _dependants;
- private readonly Dictionary<ulong, List<int>> _owners;
-
- public List<ulong> Targets => _targets;
- public Dictionary<ulong, List<int>> Dependants => _dependants;
- public Dictionary<ulong, List<int>> Owners => _owners;
+ 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>>();
+ 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(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)
{
- _targets.Clear();
+ Targets.Clear();
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()
{
_jumpTable.Clear();
_dynamicTable.Clear();
- _targets.Clear();
- _dependants.Clear();
- _owners.Clear();
+ Targets.Clear();
+ Dependants.Clear();
+ Owners.Clear();
}
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;
- DirectHostAddress directHostAddress = item.HostAddress;
+ long guestAddress = tableEntry.GuestAddress;
+ DirectHostAddress directHostAddress = tableEntry.HostAddress;
long hostAddress;
@@ -119,7 +307,12 @@ namespace ARMeilleure.Translation.PTC
}
else
{
- throw new KeyNotFoundException($"({nameof(guestAddress)} = 0x{(ulong)guestAddress:X16})");
+ if (!PtcProfiler.ProfiledFuncs.TryGetValue((ulong)guestAddress, out var value) || !value.highCq)
+ {
+ throw new KeyNotFoundException($"({nameof(guestAddress)} = 0x{(ulong)guestAddress:X16})");
+ }
+
+ hostAddress = 0L;
}
}
else
@@ -127,7 +320,7 @@ namespace ARMeilleure.Translation.PTC
throw new InvalidOperationException(nameof(directHostAddress));
}
- int entry = item.EntryIndex;
+ int entry = tableEntry.EntryIndex;
jumpTable.Table.SetEntry(entry);
jumpTable.ExpandIfNeededJumpTable(entry);
@@ -141,17 +334,17 @@ namespace ARMeilleure.Translation.PTC
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)
{
throw new NotSupportedException();
}
- foreach (var item in _dynamicTable)
+ foreach (var tableEntry in _dynamicTable)
{
- long guestAddress = item.GuestAddress;
- IndirectHostAddress indirectHostAddress = item.HostAddress;
+ long guestAddress = tableEntry.GuestAddress;
+ IndirectHostAddress indirectHostAddress = tableEntry.HostAddress;
long hostAddress;
@@ -168,7 +361,7 @@ namespace ARMeilleure.Translation.PTC
throw new InvalidOperationException(nameof(indirectHostAddress));
}
- int entry = item.EntryIndex;
+ int entry = tableEntry.EntryIndex;
jumpTable.DynTable.SetEntry(entry);
jumpTable.ExpandIfNeededDynamicTable(entry);
@@ -182,7 +375,7 @@ namespace ARMeilleure.Translation.PTC
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();
@@ -216,7 +409,7 @@ namespace ARMeilleure.Translation.PTC
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)
{
diff --git a/ARMeilleure/Translation/PTC/PtcProfiler.cs b/ARMeilleure/Translation/PTC/PtcProfiler.cs
index dbb3ed9d..bc9814ec 100644
--- a/ARMeilleure/Translation/PTC/PtcProfiler.cs
+++ b/ARMeilleure/Translation/PTC/PtcProfiler.cs
@@ -1,10 +1,11 @@
using ARMeilleure.State;
+using Ryujinx.Common.Logging;
using System;
+using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.IO.Compression;
-using System.Runtime.Serialization.Formatters.Binary;
using System.Security.Cryptography;
using System.Threading;
@@ -12,12 +13,14 @@ namespace ARMeilleure.Translation.PTC
{
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 CompressionLevel SaveCompressionLevel = CompressionLevel.Fastest;
- private static readonly BinaryFormatter _binaryFormatter;
-
private static readonly System.Timers.Timer _timer;
private static readonly ManualResetEvent _waitEvent;
@@ -26,17 +29,15 @@ namespace ARMeilleure.Translation.PTC
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; }
public static ulong StaticCodeStart { internal get; set; }
- public static int StaticCodeSize { internal get; set; }
+ public static ulong StaticCodeSize { internal get; set; }
static PtcProfiler()
{
- _binaryFormatter = new BinaryFormatter();
-
_timer = new System.Timers.Timer((double)SaveInterval * 1000d);
_timer.Elapsed += PreSave;
@@ -55,11 +56,11 @@ namespace ARMeilleure.Translation.PTC
{
if (IsAddressInStaticCodeRange(address))
{
+ Debug.Assert(!highCq);
+
lock (_lock)
{
- Debug.Assert(!highCq && !ProfiledFuncs.ContainsKey(address));
-
- ProfiledFuncs.TryAdd(address, (mode, highCq));
+ ProfiledFuncs.TryAdd(address, (mode, highCq: false));
}
}
}
@@ -68,18 +69,35 @@ namespace ARMeilleure.Translation.PTC
{
if (IsAddressInStaticCodeRange(address))
{
+ Debug.Assert(highCq);
+
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)
{
- 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()
@@ -97,21 +115,21 @@ namespace ARMeilleure.Translation.PTC
if (fileInfoActual.Exists && fileInfoActual.Length != 0L)
{
- if (!Load(fileNameActual))
+ if (!Load(fileNameActual, false))
{
if (fileInfoBackup.Exists && fileInfoBackup.Length != 0L)
{
- Load(fileNameBackup);
+ Load(fileNameBackup, true);
}
}
}
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 (DeflateStream deflateStream = new DeflateStream(compressedStream, CompressionMode.Decompress, true))
@@ -147,9 +165,25 @@ namespace ARMeilleure.Translation.PTC
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
{
- ProfiledFuncs = (Dictionary<ulong, (ExecutionMode, bool)>)_binaryFormatter.Deserialize(stream);
+ ProfiledFuncs = Deserialize(stream);
}
catch
{
@@ -159,9 +193,13 @@ namespace ARMeilleure.Translation.PTC
return false;
}
-
- return true;
}
+
+ 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;
}
private static bool CompareHash(ReadOnlySpan<byte> currentHash, ReadOnlySpan<byte> expectedHash)
@@ -169,6 +207,42 @@ namespace ARMeilleure.Translation.PTC
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)
{
compressedStream.SetLength(0L);
@@ -195,6 +269,8 @@ namespace ARMeilleure.Translation.PTC
private static void Save(string fileName)
{
+ int profiledFuncsCount;
+
using (MemoryStream stream = new MemoryStream())
using (MD5 md5 = MD5.Create())
{
@@ -202,9 +278,13 @@ namespace ARMeilleure.Translation.PTC
stream.Seek((long)hashSize, SeekOrigin.Begin);
+ WriteHeader(stream);
+
lock (_lock)
{
- _binaryFormatter.Serialize(stream, ProfiledFuncs);
+ Serialize(stream, ProfiledFuncs);
+
+ profiledFuncsCount = ProfiledFuncs.Count;
}
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()
diff --git a/ARMeilleure/Translation/PTC/RelocEntry.cs b/ARMeilleure/Translation/PTC/RelocEntry.cs
index 3d729fbb..bb77e1f0 100644
--- a/ARMeilleure/Translation/PTC/RelocEntry.cs
+++ b/ARMeilleure/Translation/PTC/RelocEntry.cs
@@ -2,6 +2,8 @@ namespace ARMeilleure.Translation.PTC
{
struct RelocEntry
{
+ public const int Stride = 8; // Bytes.
+
public int Position;
public int Index;
diff --git a/ARMeilleure/Translation/SsaDeconstruction.cs b/ARMeilleure/Translation/SsaDeconstruction.cs
index c3bcaf8c..2e9e3281 100644
--- a/ARMeilleure/Translation/SsaDeconstruction.cs
+++ b/ARMeilleure/Translation/SsaDeconstruction.cs
@@ -1,5 +1,4 @@
using ARMeilleure.IntermediateRepresentation;
-using System.Collections.Generic;
using static ARMeilleure.IntermediateRepresentation.OperandHelper;
using static ARMeilleure.IntermediateRepresentation.OperationHelper;
diff --git a/ARMeilleure/Translation/Translator.cs b/ARMeilleure/Translation/Translator.cs
index ffcd551c..d78f5e21 100644
--- a/ARMeilleure/Translation/Translator.cs
+++ b/ARMeilleure/Translation/Translator.cs
@@ -31,10 +31,11 @@ namespace ARMeilleure.Translation
private readonly ReaderWriterLock _backgroundTranslatorLock;
private JumpTable _jumpTable;
+ internal JumpTable JumpTable => _jumpTable;
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 Translator(IJitMemoryAllocator allocator, IMemoryManager memory)
@@ -100,7 +101,7 @@ namespace ARMeilleure.Translation
if (Ptc.State == PtcState.Enabled)
{
- Ptc.LoadTranslations(_funcs, _memory.PageTablePointer, _jumpTable);
+ Ptc.LoadTranslations(_funcs, _memory, _jumpTable);
Ptc.MakeAndSaveTranslations(_funcs, _memory, _jumpTable);
}