From b5c215111de665ef8d18b38405ac55e17996e30e Mon Sep 17 00:00:00 2001 From: LDj3SNuD <35856442+LDj3SNuD@users.noreply.github.com> Date: Thu, 17 Dec 2020 20:32:09 +0100 Subject: [PATCH] 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. --- ARMeilleure/CodeGen/Unwinding/UnwindInfo.cs | 2 + .../CodeGen/Unwinding/UnwindPushEntry.cs | 2 + ARMeilleure/Decoders/Decoder.cs | 2 + ARMeilleure/Instructions/NativeInterface.cs | 3 + ARMeilleure/State/ExecutionMode.cs | 6 +- ARMeilleure/Translation/Cache/JumpTable.cs | 65 ++-- ARMeilleure/Translation/DelegateInfo.cs | 2 +- .../Translation/JumpTableEntryAllocator.cs | 72 +++++ ARMeilleure/Translation/PTC/Ptc.cs | 233 ++++++++++----- ARMeilleure/Translation/PTC/PtcInfo.cs | 2 +- ARMeilleure/Translation/PTC/PtcJumpTable.cs | 281 +++++++++++++++--- ARMeilleure/Translation/PTC/PtcProfiler.cs | 159 ++++++++-- ARMeilleure/Translation/PTC/RelocEntry.cs | 2 + ARMeilleure/Translation/SsaDeconstruction.cs | 1 - ARMeilleure/Translation/Translator.cs | 5 +- Ryujinx.HLE/HOS/ProgramLoader.cs | 2 +- 16 files changed, 670 insertions(+), 169 deletions(-) create mode 100644 ARMeilleure/Translation/JumpTableEntryAllocator.cs diff --git a/ARMeilleure/CodeGen/Unwinding/UnwindInfo.cs b/ARMeilleure/CodeGen/Unwinding/UnwindInfo.cs index 8072acd95..3d0bc21d5 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 021479a4f..fd8ea402b 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 8666cdccc..eb085999a 100644 --- a/ARMeilleure/Decoders/Decoder.cs +++ b/ARMeilleure/Decoders/Decoder.cs @@ -26,6 +26,8 @@ namespace ARMeilleure.Decoders Dictionary visited = new Dictionary(); + 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 b2b200b90..b8b7ff0e8 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 eaed9d27f..f43c5569f 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 71a036d83..aa3b6caf3 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(item.Value)); + Dependants.TryAdd(kv.Key, new List(kv.Value)); } - foreach (var item in ptcJumpTable.Owners) + foreach (var kv in ptcJumpTable.Owners) { - Owners.TryAdd(item.Key, new List(item.Value)); + Owners.TryAdd(kv.Key, new List(kv.Value)); } } @@ -182,29 +182,14 @@ namespace ARMeilleure.Translation.Cache // For future use. public void RemoveFunctionEntries(ulong guestAddress) { - if (Owners.TryRemove(guestAddress, out List list)) + Targets.TryRemove(guestAddress, out _); + Dependants.TryRemove(guestAddress, out _); + + if (Owners.TryRemove(guestAddress, out List entries)) { - for (int i = 0; i < list.Count; i++) + foreach (int entry in entries) { - int entry = list[i]; - - 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 + if ((entry & DynamicEntryTag) == 0) { IntPtr addr = GetEntryAddressJumpTable(entry); @@ -213,6 +198,18 @@ namespace ARMeilleure.Translation.Cache 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; } + 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 e68cfc1b7..36320ac31 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 000000000..3b918628f --- /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 GetEntries() + { + return _bitmap; + } + } +} diff --git a/ARMeilleure/Translation/PTC/Ptc.cs b/ARMeilleure/Translation/PTC/Ptc.cs index 1cdaa8fe2..3150c97c5 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 currentHash, ReadOnlySpan 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 funcs, IntPtr pageTablePointer, JumpTable jumpTable) + internal static void LoadTranslations(ConcurrentDictionary 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); - UnwindInfo unwindInfo = ReadUnwindInfo(unwindInfosReader); + if (infoEntry.RelocEntriesCount != 0) + { + RelocEntry[] relocEntries = GetRelocEntries(relocsReader, infoEntry.RelocEntriesCount); - TranslatedFunction func = FastTranslate(code, infoEntry.GuestSize, unwindInfo, infoEntry.HighCq); + PatchCode(code, relocEntries, memory.PageTablePointer, jumpTable); + } - funcs.AddOrUpdate(infoEntry.Address, func, (key, oldFunc) => func.HighCq && !oldFunc.HighCq ? func : oldFunc); + UnwindInfo unwindInfo = ReadUnwindInfo(unwindInfosReader); + + 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 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); + + bool isAddressUnique = funcs.TryAdd(address, func); + + Debug.Assert(isAddressUnique, $"The address 0x{address:X16} is not unique."); + + if (func.HighCq) { - TranslatedFunction func = Translator.Translate(memory, jumpTable, address, item.Value.mode, item.Value.highCq); - - funcs.TryAdd(address, func); - - if (func.HighCq) - { - jumpTable.RegisterFunction(address, func); - } - - 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); } + 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 f03eb6ba4..547fe6d8a 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 e4af68d68..d52d08743 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 + public struct TableEntry { 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> _jumpTable; private readonly List> _dynamicTable; - private readonly List _targets; - private readonly Dictionary> _dependants; - private readonly Dictionary> _owners; - - public List Targets => _targets; - public Dictionary> Dependants => _dependants; - public Dictionary> Owners => _owners; + public List Targets { get; } + public Dictionary> Dependants { get; } + public Dictionary> Owners { get; } public PtcJumpTable() { _jumpTable = new List>(); _dynamicTable = new List>(); - _targets = new List(); - _dependants = new Dictionary>(); - _owners = new Dictionary>(); + Targets = new List(); + Dependants = new Dictionary>(); + Owners = new Dictionary>(); + } + + public PtcJumpTable( + List> jumpTable, List> dynamicTable, + List targets, Dictionary> dependants, Dictionary> 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>(); + + 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(entryIndex, guestAddress, hostAddress)); + } + + var dynamicTable = new List>(); + + 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(entryIndex, guestAddress, hostAddress)); + } + + var targets = new List(); + + int targetsCount = reader.ReadInt32(); + + for (int i = 0; i < targetsCount; i++) + { + ulong address = reader.ReadUInt64(); + + targets.Add(address); + } + + var dependants = new Dictionary>(); + + int dependantsCount = reader.ReadInt32(); + + for (int i = 0; i < dependantsCount; i++) + { + ulong address = reader.ReadUInt64(); + + var entries = new List(); + + 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>(); + + int ownersCount = reader.ReadInt32(); + + for (int i = 0; i < ownersCount; i++) + { + ulong address = reader.ReadUInt64(); + + var entries = new List(); + + 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(item.Value)); + Dependants.Add(kv.Key, new List(kv.Value)); } - _owners.Clear(); + Owners.Clear(); - foreach (var item in jumpTable.Owners) + foreach (var kv in jumpTable.Owners) { - _owners.Add(item.Key, new List(item.Value)); + Owners.Add(kv.Key, new List(kv.Value)); } } + // For future use. + public void Clean(ulong guestAddress) + { + if (Owners.TryGetValue(guestAddress, out List 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 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 dbb3ed9d1..bc9814ecc 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 ProfiledFuncs { get; private set; } //! Not to be modified. + internal static Dictionary 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 GetProfiledFuncsToTranslate(ConcurrentDictionary funcs) + { + var profiledFuncsToTranslate = new Dictionary(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)_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 currentHash, ReadOnlySpan 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 Deserialize(MemoryStream stream) + { + using (BinaryReader reader = new BinaryReader(stream, EncodingCache.UTF8NoBOM, true)) + { + var profiledFuncs = new Dictionary(); + + 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 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 3d729fbb0..bb77e1f0f 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 c3bcaf8c1..2e9e3281a 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 ffcd551c9..d78f5e212 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); } diff --git a/Ryujinx.HLE/HOS/ProgramLoader.cs b/Ryujinx.HLE/HOS/ProgramLoader.cs index 79f8af7a8..03c3ea51b 100644 --- a/Ryujinx.HLE/HOS/ProgramLoader.cs +++ b/Ryujinx.HLE/HOS/ProgramLoader.cs @@ -170,7 +170,7 @@ namespace Ryujinx.HLE.HOS } PtcProfiler.StaticCodeStart = codeStart; - PtcProfiler.StaticCodeSize = (int)codeSize; + PtcProfiler.StaticCodeSize = (ulong)codeSize; int codePagesCount = (int)(codeSize / KMemoryManager.PageSize);