aboutsummaryrefslogtreecommitdiff
path: root/ARMeilleure/Translation
diff options
context:
space:
mode:
authorLDj3SNuD <35856442+LDj3SNuD@users.noreply.github.com>2021-02-22 03:23:48 +0100
committerGitHub <noreply@github.com>2021-02-22 03:23:48 +0100
commitdc0adb533dc15a007e9ca2dc0533ef6a61f13393 (patch)
tree4b3588600cfd163e9f7a72b176b15befe078e941 /ARMeilleure/Translation
parent15868801148129802173f2162ebff24835cb20ba (diff)
PPTC & Pool Enhancements. (#1968)
* PPTC & Pool Enhancements. * Avoid buffer allocations in CodeGenContext.GetCode(). Avoid stream allocations in PTC.PtcInfo. Refactoring/nits. * Use XXHash128, for Ptc.Load & Ptc.Save, x10 faster than Md5. * Why not a nice Span. * Added a simple PtcFormatter library for deserialization/serialization, which does not require reflection, in use at PtcJumpTable and PtcProfiler; improves maintainability and simplicity/readability of affected code. * Nits. * Revert #1987. * Revert "Revert #1987." This reverts commit 998be765cf7f7da5ff0c1c08de704c9012b0f49c.
Diffstat (limited to 'ARMeilleure/Translation')
-rw-r--r--ARMeilleure/Translation/DirectCallStubs.cs6
-rw-r--r--ARMeilleure/Translation/EmitterContext.cs3
-rw-r--r--ARMeilleure/Translation/JumpTableEntryAllocator.cs72
-rw-r--r--ARMeilleure/Translation/PTC/Ptc.cs499
-rw-r--r--ARMeilleure/Translation/PTC/PtcFormatter.cs121
-rw-r--r--ARMeilleure/Translation/PTC/PtcInfo.cs10
-rw-r--r--ARMeilleure/Translation/PTC/PtcJumpTable.cs182
-rw-r--r--ARMeilleure/Translation/PTC/PtcProfiler.cs101
-rw-r--r--ARMeilleure/Translation/Translator.cs35
9 files changed, 551 insertions, 478 deletions
diff --git a/ARMeilleure/Translation/DirectCallStubs.cs b/ARMeilleure/Translation/DirectCallStubs.cs
index df7ca16e..85af6901 100644
--- a/ARMeilleure/Translation/DirectCallStubs.cs
+++ b/ARMeilleure/Translation/DirectCallStubs.cs
@@ -29,11 +29,17 @@ namespace ARMeilleure.Translation
{
if (_initialized) return;
+ Translator.PreparePool();
+
_directCallStubPtr = Marshal.GetFunctionPointerForDelegate<GuestFunction>(GenerateDirectCallStub(false));
_directTailCallStubPtr = Marshal.GetFunctionPointerForDelegate<GuestFunction>(GenerateDirectCallStub(true));
_indirectCallStubPtr = Marshal.GetFunctionPointerForDelegate<GuestFunction>(GenerateIndirectCallStub(false));
_indirectTailCallStubPtr = Marshal.GetFunctionPointerForDelegate<GuestFunction>(GenerateIndirectCallStub(true));
+ Translator.ResetPool();
+
+ Translator.DisposePools();
+
_initialized = true;
}
}
diff --git a/ARMeilleure/Translation/EmitterContext.cs b/ARMeilleure/Translation/EmitterContext.cs
index f41f3464..5c608b3d 100644
--- a/ARMeilleure/Translation/EmitterContext.cs
+++ b/ARMeilleure/Translation/EmitterContext.cs
@@ -1,6 +1,7 @@
using ARMeilleure.Diagnostics;
using ARMeilleure.IntermediateRepresentation;
using ARMeilleure.State;
+using ARMeilleure.Translation.PTC;
using System;
using System.Collections.Generic;
using System.Reflection;
@@ -9,8 +10,6 @@ using static ARMeilleure.IntermediateRepresentation.OperandHelper;
namespace ARMeilleure.Translation
{
- using PTC;
-
class EmitterContext
{
private readonly Dictionary<Operand, BasicBlock> _irLabels;
diff --git a/ARMeilleure/Translation/JumpTableEntryAllocator.cs b/ARMeilleure/Translation/JumpTableEntryAllocator.cs
deleted file mode 100644
index 3b918628..00000000
--- a/ARMeilleure/Translation/JumpTableEntryAllocator.cs
+++ /dev/null
@@ -1,72 +0,0 @@
-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 6dd902bc..163e3efa 100644
--- a/ARMeilleure/Translation/PTC/Ptc.cs
+++ b/ARMeilleure/Translation/PTC/Ptc.cs
@@ -3,6 +3,7 @@ using ARMeilleure.CodeGen.Unwinding;
using ARMeilleure.CodeGen.X86;
using ARMeilleure.Memory;
using ARMeilleure.Translation.Cache;
+using Ryujinx.Common;
using Ryujinx.Common.Configuration;
using Ryujinx.Common.Logging;
using System;
@@ -12,17 +13,20 @@ using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.IO.Compression;
+using System.Runtime;
+using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
-using System.Security.Cryptography;
using System.Threading;
+using static ARMeilleure.Translation.PTC.PtcFormatter;
+
namespace ARMeilleure.Translation.PTC
{
public static class Ptc
{
- private const string HeaderMagic = "PTChd";
+ private const string HeaderMagicString = "PTChd\0\0\0";
- private const int InternalVersion = 2010; //! To be incremented manually for each change to the ARMeilleure project.
+ private const uint InternalVersion = 1968; //! To be incremented manually for each change to the ARMeilleure project.
private const string ActualDir = "0";
private const string BackupDir = "1";
@@ -37,12 +41,14 @@ namespace ARMeilleure.Translation.PTC
private const byte FillingByte = 0x00;
private const CompressionLevel SaveCompressionLevel = CompressionLevel.Fastest;
- private static readonly MemoryStream _infosStream;
- private static readonly MemoryStream _codesStream;
- private static readonly MemoryStream _relocsStream;
- private static readonly MemoryStream _unwindInfosStream;
+ private static MemoryStream _infosStream;
+ private static MemoryStream _codesStream;
+ private static MemoryStream _relocsStream;
+ private static MemoryStream _unwindInfosStream;
+
+ private static BinaryWriter _infosWriter;
- private static readonly BinaryWriter _infosWriter;
+ private static readonly ulong _headerMagic;
private static readonly ManualResetEvent _waitEvent;
@@ -66,12 +72,9 @@ namespace ARMeilleure.Translation.PTC
static Ptc()
{
- _infosStream = new MemoryStream();
- _codesStream = new MemoryStream();
- _relocsStream = new MemoryStream();
- _unwindInfosStream = new MemoryStream();
+ InitializeMemoryStreams();
- _infosWriter = new BinaryWriter(_infosStream, EncodingCache.UTF8NoBOM, true);
+ _headerMagic = BinaryPrimitives.ReadUInt64LittleEndian(EncodingCache.UTF8NoBOM.GetBytes(HeaderMagicString).AsSpan());
_waitEvent = new ManualResetEvent(true);
@@ -95,13 +98,13 @@ namespace ARMeilleure.Translation.PTC
public static void Initialize(string titleIdText, string displayVersion, bool enabled)
{
Wait();
- ClearMemoryStreams();
- PtcJumpTable.Clear();
PtcProfiler.Wait();
PtcProfiler.ClearEntries();
- if (String.IsNullOrEmpty(titleIdText) || titleIdText == TitleIdTextDefault)
+ Logger.Info?.Print(LogClass.Ptc, $"Initializing Profiled Persistent Translation Cache (enabled: {enabled}).");
+
+ if (!enabled || string.IsNullOrEmpty(titleIdText) || titleIdText == TitleIdTextDefault)
{
TitleIdText = TitleIdTextDefault;
DisplayVersion = DisplayVersionDefault;
@@ -114,55 +117,72 @@ namespace ARMeilleure.Translation.PTC
return;
}
- Logger.Info?.Print(LogClass.Ptc, $"Initializing Profiled Persistent Translation Cache (enabled: {enabled}).");
-
TitleIdText = titleIdText;
- DisplayVersion = !String.IsNullOrEmpty(displayVersion) ? displayVersion : DisplayVersionDefault;
+ DisplayVersion = !string.IsNullOrEmpty(displayVersion) ? displayVersion : DisplayVersionDefault;
- if (enabled)
+ string workPathActual = Path.Combine(AppDataManager.GamesDirPath, TitleIdText, "cache", "cpu", ActualDir);
+ string workPathBackup = Path.Combine(AppDataManager.GamesDirPath, TitleIdText, "cache", "cpu", BackupDir);
+
+ if (!Directory.Exists(workPathActual))
{
- string workPathActual = Path.Combine(AppDataManager.GamesDirPath, TitleIdText, "cache", "cpu", ActualDir);
- string workPathBackup = Path.Combine(AppDataManager.GamesDirPath, TitleIdText, "cache", "cpu", BackupDir);
+ Directory.CreateDirectory(workPathActual);
+ }
- if (!Directory.Exists(workPathActual))
- {
- Directory.CreateDirectory(workPathActual);
- }
+ if (!Directory.Exists(workPathBackup))
+ {
+ Directory.CreateDirectory(workPathBackup);
+ }
- if (!Directory.Exists(workPathBackup))
- {
- Directory.CreateDirectory(workPathBackup);
- }
+ CachePathActual = Path.Combine(workPathActual, DisplayVersion);
+ CachePathBackup = Path.Combine(workPathBackup, DisplayVersion);
- CachePathActual = Path.Combine(workPathActual, DisplayVersion);
- CachePathBackup = Path.Combine(workPathBackup, DisplayVersion);
+ PreLoad();
+ PtcProfiler.PreLoad();
- Enable();
+ Enable();
+ }
- PreLoad();
- PtcProfiler.PreLoad();
- }
- else
- {
- CachePathActual = string.Empty;
- CachePathBackup = string.Empty;
+ private static void InitializeMemoryStreams()
+ {
+ _infosStream = new MemoryStream();
+ _codesStream = new MemoryStream();
+ _relocsStream = new MemoryStream();
+ _unwindInfosStream = new MemoryStream();
- Disable();
- }
+ _infosWriter = new BinaryWriter(_infosStream, EncodingCache.UTF8NoBOM, true);
}
- internal static void ClearMemoryStreams()
+ private static void DisposeMemoryStreams()
{
- _infosStream.SetLength(0L);
- _codesStream.SetLength(0L);
- _relocsStream.SetLength(0L);
- _unwindInfosStream.SetLength(0L);
+ _infosWriter.Dispose();
+
+ _infosStream.Dispose();
+ _codesStream.Dispose();
+ _relocsStream.Dispose();
+ _unwindInfosStream.Dispose();
+ }
+
+ private static bool AreMemoryStreamsEmpty()
+ {
+ return _infosStream.Length == 0L && _codesStream.Length == 0L && _relocsStream.Length == 0L && _unwindInfosStream.Length == 0L;
+ }
+
+ private static void ResetMemoryStreamsIfNeeded()
+ {
+ if (AreMemoryStreamsEmpty())
+ {
+ return;
+ }
+
+ DisposeMemoryStreams();
+
+ InitializeMemoryStreams();
}
private static void PreLoad()
{
- string fileNameActual = String.Concat(CachePathActual, ".cache");
- string fileNameBackup = String.Concat(CachePathBackup, ".cache");
+ string fileNameActual = string.Concat(CachePathActual, ".cache");
+ string fileNameBackup = string.Concat(CachePathBackup, ".cache");
FileInfo fileInfoActual = new FileInfo(fileNameActual);
FileInfo fileInfoBackup = new FileInfo(fileNameBackup);
@@ -183,106 +203,144 @@ namespace ARMeilleure.Translation.PTC
}
}
- private static bool Load(string fileName, bool isBackup)
+ private static unsafe 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 (MD5 md5 = MD5.Create())
+ using (FileStream compressedStream = new(fileName, FileMode.Open))
+ using (DeflateStream deflateStream = new(compressedStream, CompressionMode.Decompress, true))
{
- int hashSize = md5.HashSize / 8;
+ Hash128 currentSizeHash = DeserializeStructure<Hash128>(compressedStream);
- try
- {
- deflateStream.CopyTo(stream);
- }
- catch
+ Span<byte> sizeBytes = new byte[sizeof(int)];
+ compressedStream.Read(sizeBytes);
+ Hash128 expectedSizeHash = XXHash128.ComputeHash(sizeBytes);
+
+ if (currentSizeHash != expectedSizeHash)
{
InvalidateCompressedStream(compressedStream);
return false;
}
- stream.Seek(0L, SeekOrigin.Begin);
+ int size = BinaryPrimitives.ReadInt32LittleEndian(sizeBytes);
- byte[] currentHash = new byte[hashSize];
- stream.Read(currentHash, 0, hashSize);
+ IntPtr intPtr = IntPtr.Zero;
- byte[] expectedHash = md5.ComputeHash(stream);
-
- if (!CompareHash(currentHash, expectedHash))
+ try
{
- InvalidateCompressedStream(compressedStream);
+ intPtr = Marshal.AllocHGlobal(size);
- return false;
- }
+ using (UnmanagedMemoryStream stream = new((byte*)intPtr.ToPointer(), size, size, FileAccess.ReadWrite))
+ {
+ try
+ {
+ deflateStream.CopyTo(stream);
+ }
+ catch
+ {
+ InvalidateCompressedStream(compressedStream);
- stream.Seek((long)hashSize, SeekOrigin.Begin);
+ return false;
+ }
- Header header = ReadHeader(stream);
+ int hashSize = Unsafe.SizeOf<Hash128>();
- if (header.Magic != HeaderMagic)
- {
- InvalidateCompressedStream(compressedStream);
+ stream.Seek(0L, SeekOrigin.Begin);
+ Hash128 currentHash = DeserializeStructure<Hash128>(stream);
- return false;
- }
+ ReadOnlySpan<byte> streamBytes = new(stream.PositionPointer, (int)(stream.Length - stream.Position));
+ Hash128 expectedHash = XXHash128.ComputeHash(streamBytes);
- if (header.CacheFileVersion != InternalVersion)
- {
- InvalidateCompressedStream(compressedStream);
+ if (currentHash != expectedHash)
+ {
+ InvalidateCompressedStream(compressedStream);
- return false;
- }
+ return false;
+ }
- if (header.FeatureInfo != GetFeatureInfo())
- {
- InvalidateCompressedStream(compressedStream);
+ stream.Seek((long)hashSize, SeekOrigin.Begin);
- return false;
- }
+ Header header = ReadHeader(stream);
- if (header.OSPlatform != GetOSPlatform())
- {
- InvalidateCompressedStream(compressedStream);
+ if (header.Magic != _headerMagic)
+ {
+ InvalidateCompressedStream(compressedStream);
- return false;
- }
+ return false;
+ }
- if (header.InfosLen % InfoEntry.Stride != 0)
- {
- InvalidateCompressedStream(compressedStream);
+ if (header.CacheFileVersion != InternalVersion)
+ {
+ InvalidateCompressedStream(compressedStream);
- return false;
- }
+ return false;
+ }
- byte[] infosBuf = new byte[header.InfosLen];
- byte[] codesBuf = new byte[header.CodesLen];
- byte[] relocsBuf = new byte[header.RelocsLen];
- byte[] unwindInfosBuf = new byte[header.UnwindInfosLen];
+ if (header.Endianness != GetEndianness())
+ {
+ InvalidateCompressedStream(compressedStream);
- stream.Read(infosBuf, 0, header.InfosLen);
- stream.Read(codesBuf, 0, header.CodesLen);
- stream.Read(relocsBuf, 0, header.RelocsLen);
- stream.Read(unwindInfosBuf, 0, header.UnwindInfosLen);
+ return false;
+ }
- try
- {
- PtcJumpTable = PtcJumpTable.Deserialize(stream);
- }
- catch
- {
- PtcJumpTable = new PtcJumpTable();
+ if (header.FeatureInfo != GetFeatureInfo())
+ {
+ InvalidateCompressedStream(compressedStream);
- InvalidateCompressedStream(compressedStream);
+ return false;
+ }
- return false;
- }
+ if (header.OSPlatform != GetOSPlatform())
+ {
+ InvalidateCompressedStream(compressedStream);
+
+ return false;
+ }
+
+ if (header.InfosLen % InfoEntry.Stride != 0)
+ {
+ InvalidateCompressedStream(compressedStream);
+
+ return false;
+ }
+
+ ReadOnlySpan<byte> infosBuf = new(stream.PositionPointer, header.InfosLen);
+ stream.Seek(header.InfosLen, SeekOrigin.Current);
+
+ ReadOnlySpan<byte> codesBuf = new(stream.PositionPointer, header.CodesLen);
+ stream.Seek(header.CodesLen, SeekOrigin.Current);
- _infosStream.Write(infosBuf, 0, header.InfosLen);
- _codesStream.Write(codesBuf, 0, header.CodesLen);
- _relocsStream.Write(relocsBuf, 0, header.RelocsLen);
- _unwindInfosStream.Write(unwindInfosBuf, 0, header.UnwindInfosLen);
+ ReadOnlySpan<byte> relocsBuf = new(stream.PositionPointer, header.RelocsLen);
+ stream.Seek(header.RelocsLen, SeekOrigin.Current);
+
+ ReadOnlySpan<byte> unwindInfosBuf = new(stream.PositionPointer, header.UnwindInfosLen);
+ stream.Seek(header.UnwindInfosLen, SeekOrigin.Current);
+
+ try
+ {
+ PtcJumpTable = PtcJumpTable.Deserialize(stream);
+ }
+ catch
+ {
+ PtcJumpTable = new PtcJumpTable();
+
+ InvalidateCompressedStream(compressedStream);
+
+ return false;
+ }
+
+ _infosStream.Write(infosBuf);
+ _codesStream.Write(codesBuf);
+ _relocsStream.Write(relocsBuf);
+ _unwindInfosStream.Write(unwindInfosBuf);
+ }
+ }
+ finally
+ {
+ if (intPtr != IntPtr.Zero)
+ {
+ Marshal.FreeHGlobal(intPtr);
+ }
+ }
}
long fileSize = new FileInfo(fileName).Length;
@@ -292,20 +350,16 @@ namespace ARMeilleure.Translation.PTC
return true;
}
- private static bool CompareHash(ReadOnlySpan<byte> currentHash, ReadOnlySpan<byte> expectedHash)
- {
- return currentHash.SequenceEqual(expectedHash);
- }
-
- private static Header ReadHeader(MemoryStream stream)
+ private static Header ReadHeader(Stream stream)
{
- using (BinaryReader headerReader = new BinaryReader(stream, EncodingCache.UTF8NoBOM, true))
+ using (BinaryReader headerReader = new(stream, EncodingCache.UTF8NoBOM, true))
{
Header header = new Header();
- header.Magic = headerReader.ReadString();
+ header.Magic = headerReader.ReadUInt64();
header.CacheFileVersion = headerReader.ReadUInt32();
+ header.Endianness = headerReader.ReadBoolean();
header.FeatureInfo = headerReader.ReadUInt64();
header.OSPlatform = headerReader.ReadUInt32();
@@ -323,87 +377,133 @@ namespace ARMeilleure.Translation.PTC
compressedStream.SetLength(0L);
}
- private static void PreSave(object state)
+ private static void PreSave()
{
_waitEvent.Reset();
- string fileNameActual = String.Concat(CachePathActual, ".cache");
- string fileNameBackup = String.Concat(CachePathBackup, ".cache");
+ try
+ {
+ string fileNameActual = string.Concat(CachePathActual, ".cache");
+ string fileNameBackup = string.Concat(CachePathBackup, ".cache");
- FileInfo fileInfoActual = new FileInfo(fileNameActual);
+ FileInfo fileInfoActual = new FileInfo(fileNameActual);
- if (fileInfoActual.Exists && fileInfoActual.Length != 0L)
- {
- File.Copy(fileNameActual, fileNameBackup, true);
+ if (fileInfoActual.Exists && fileInfoActual.Length != 0L)
+ {
+ File.Copy(fileNameActual, fileNameBackup, true);
+ }
+
+ Save(fileNameActual);
}
+ finally
+ {
+ ResetMemoryStreamsIfNeeded();
+ PtcJumpTable.ClearIfNeeded();
- Save(fileNameActual);
+ GCSettings.LargeObjectHeapCompactionMode = GCLargeObjectHeapCompactionMode.CompactOnce;
+ }
_waitEvent.Set();
}
- private static void Save(string fileName)
+ private static unsafe void Save(string fileName)
{
int translatedFuncsCount;
- using (MemoryStream stream = new MemoryStream())
- using (MD5 md5 = MD5.Create())
+ int hashSize = Unsafe.SizeOf<Hash128>();
+
+ int size = hashSize + Header.Size + GetMemoryStreamsLength() + PtcJumpTable.GetSerializeSize(PtcJumpTable);
+
+ Span<byte> sizeBytes = new byte[sizeof(int)];
+ BinaryPrimitives.WriteInt32LittleEndian(sizeBytes, size);
+ Hash128 sizeHash = XXHash128.ComputeHash(sizeBytes);
+
+ Span<byte> sizeHashBytes = new byte[hashSize];
+ MemoryMarshal.Write<Hash128>(sizeHashBytes, ref sizeHash);
+
+ IntPtr intPtr = IntPtr.Zero;
+
+ try
{
- int hashSize = md5.HashSize / 8;
+ intPtr = Marshal.AllocHGlobal(size);
- stream.Seek((long)hashSize, SeekOrigin.Begin);
+ using (UnmanagedMemoryStream stream = new((byte*)intPtr.ToPointer(), size, size, FileAccess.ReadWrite))
+ {
+ stream.Seek((long)hashSize, SeekOrigin.Begin);
- WriteHeader(stream);
+ WriteHeader(stream);
- _infosStream.WriteTo(stream);
- _codesStream.WriteTo(stream);
- _relocsStream.WriteTo(stream);
- _unwindInfosStream.WriteTo(stream);
+ _infosStream.WriteTo(stream);
+ _codesStream.WriteTo(stream);
+ _relocsStream.WriteTo(stream);
+ _unwindInfosStream.WriteTo(stream);
- PtcJumpTable.Serialize(stream, PtcJumpTable);
+ PtcJumpTable.Serialize(stream, PtcJumpTable);
- translatedFuncsCount = GetInfosEntriesCount();
+ stream.Seek((long)hashSize, SeekOrigin.Begin);
+ ReadOnlySpan<byte> streamBytes = new(stream.PositionPointer, (int)(stream.Length - stream.Position));
+ Hash128 hash = XXHash128.ComputeHash(streamBytes);
- ClearMemoryStreams();
- PtcJumpTable.Clear();
+ stream.Seek(0L, SeekOrigin.Begin);
+ SerializeStructure(stream, hash);
- stream.Seek((long)hashSize, SeekOrigin.Begin);
- byte[] hash = md5.ComputeHash(stream);
+ translatedFuncsCount = GetInfosEntriesCount();
- stream.Seek(0L, SeekOrigin.Begin);
- stream.Write(hash, 0, hashSize);
+ ResetMemoryStreamsIfNeeded();
+ PtcJumpTable.ClearIfNeeded();
- using (FileStream compressedStream = new FileStream(fileName, FileMode.OpenOrCreate))
- using (DeflateStream deflateStream = new DeflateStream(compressedStream, SaveCompressionLevel, true))
- {
- try
+ using (FileStream compressedStream = new(fileName, FileMode.OpenOrCreate))
+ using (DeflateStream deflateStream = new(compressedStream, SaveCompressionLevel, true))
{
- stream.WriteTo(deflateStream);
- }
- catch
- {
- compressedStream.Position = 0L;
- }
+ try
+ {
+ compressedStream.Write(sizeHashBytes);
+ compressedStream.Write(sizeBytes);
- if (compressedStream.Position < compressedStream.Length)
- {
- compressedStream.SetLength(compressedStream.Position);
+ stream.Seek(0L, SeekOrigin.Begin);
+ stream.CopyTo(deflateStream);
+ }
+ catch
+ {
+ compressedStream.Position = 0L;
+ }
+
+ if (compressedStream.Position < compressedStream.Length)
+ {
+ compressedStream.SetLength(compressedStream.Position);
+ }
}
}
}
+ finally
+ {
+ if (intPtr != IntPtr.Zero)
+ {
+ Marshal.FreeHGlobal(intPtr);
+ }
+ }
long fileSize = new FileInfo(fileName).Length;
- Logger.Info?.Print(LogClass.Ptc, $"Saved Translation Cache (size: {fileSize} bytes, translated functions: {translatedFuncsCount}).");
+ if (fileSize != 0L)
+ {
+ Logger.Info?.Print(LogClass.Ptc, $"Saved Translation Cache (size: {fileSize} bytes, translated functions: {translatedFuncsCount}).");
+ }
+ }
+
+ private static int GetMemoryStreamsLength()
+ {
+ return (int)_infosStream.Length + (int)_codesStream.Length + (int)_relocsStream.Length + (int)_unwindInfosStream.Length;
}
- private static void WriteHeader(MemoryStream stream)
+ private static void WriteHeader(Stream stream)
{
- using (BinaryWriter headerWriter = new BinaryWriter(stream, EncodingCache.UTF8NoBOM, true))
+ using (BinaryWriter headerWriter = new(stream, EncodingCache.UTF8NoBOM, true))
{
- headerWriter.Write((string)HeaderMagic); // Header.Magic
+ headerWriter.Write((ulong)_headerMagic); // Header.Magic
headerWriter.Write((uint)InternalVersion); // Header.CacheFileVersion
+ headerWriter.Write((bool)GetEndianness()); // Header.Endianness
headerWriter.Write((ulong)GetFeatureInfo()); // Header.FeatureInfo
headerWriter.Write((uint)GetOSPlatform()); // Header.OSPlatform
@@ -416,10 +516,7 @@ namespace ARMeilleure.Translation.PTC
internal static void LoadTranslations(ConcurrentDictionary<ulong, TranslatedFunction> funcs, IMemoryManager memory, JumpTable jumpTable)
{
- if ((int)_infosStream.Length == 0 ||
- (int)_codesStream.Length == 0 ||
- (int)_relocsStream.Length == 0 ||
- (int)_unwindInfosStream.Length == 0)
+ if (AreMemoryStreamsEmpty())
{
return;
}
@@ -431,10 +528,10 @@ namespace ARMeilleure.Translation.PTC
_relocsStream.Seek(0L, SeekOrigin.Begin);
_unwindInfosStream.Seek(0L, SeekOrigin.Begin);
- using (BinaryReader infosReader = new BinaryReader(_infosStream, EncodingCache.UTF8NoBOM, true))
- using (BinaryReader codesReader = new BinaryReader(_codesStream, EncodingCache.UTF8NoBOM, true))
- using (BinaryReader relocsReader = new BinaryReader(_relocsStream, EncodingCache.UTF8NoBOM, true))
- using (BinaryReader unwindInfosReader = new BinaryReader(_unwindInfosStream, EncodingCache.UTF8NoBOM, true))
+ using (BinaryReader infosReader = new(_infosStream, EncodingCache.UTF8NoBOM, true))
+ using (BinaryReader codesReader = new(_codesStream, EncodingCache.UTF8NoBOM, true))
+ using (BinaryReader relocsReader = new(_relocsStream, EncodingCache.UTF8NoBOM, true))
+ using (BinaryReader unwindInfosReader = new(_unwindInfosStream, EncodingCache.UTF8NoBOM, true))
{
for (int i = 0; i < GetInfosEntriesCount(); i++)
{
@@ -446,9 +543,9 @@ namespace ARMeilleure.Translation.PTC
SkipReloc(infoEntry.RelocEntriesCount);
SkipUnwindInfo(unwindInfosReader);
}
- else if (infoEntry.HighCq || !PtcProfiler.ProfiledFuncs.TryGetValue(infoEntry.Address, out var value) || !value.highCq)
+ else if (infoEntry.HighCq || !PtcProfiler.ProfiledFuncs.TryGetValue(infoEntry.Address, out var value) || !value.HighCq)
{
- byte[] code = ReadCode(codesReader, infoEntry.CodeLen);
+ Span<byte> code = ReadCode(codesReader, infoEntry.CodeLen);
if (infoEntry.RelocEntriesCount != 0)
{
@@ -529,11 +626,11 @@ namespace ARMeilleure.Translation.PTC
_unwindInfosStream.Seek(pushEntriesLength * UnwindPushEntry.Stride + UnwindInfo.Stride, SeekOrigin.Current);
}
- private static byte[] ReadCode(BinaryReader codesReader, int codeLen)
+ private static Span<byte> ReadCode(BinaryReader codesReader, int codeLen)
{
- byte[] codeBuf = new byte[codeLen];
+ Span<byte> codeBuf = new byte[codeLen];
- codesReader.Read(codeBuf, 0, codeLen);
+ codesReader.Read(codeBuf);
return codeBuf;
}
@@ -605,9 +702,9 @@ namespace ARMeilleure.Translation.PTC
return new UnwindInfo(pushEntries, prologueSize);
}
- private static TranslatedFunction FastTranslate(byte[] code, ulong guestSize, UnwindInfo unwindInfo, bool highCq)
+ private static TranslatedFunction FastTranslate(ReadOnlySpan<byte> code, ulong guestSize, UnwindInfo unwindInfo, bool highCq)
{
- CompiledFunction cFunc = new CompiledFunction(code, unwindInfo);
+ CompiledFunction cFunc = new CompiledFunction(code.ToArray(), unwindInfo);
IntPtr codePtr = JitCache.Map(cFunc);
@@ -663,6 +760,11 @@ namespace ARMeilleure.Translation.PTC
if (profiledFuncsToTranslate.Count == 0)
{
+ ResetMemoryStreamsIfNeeded();
+ PtcJumpTable.ClearIfNeeded();
+
+ GCSettings.LargeObjectHeapCompactionMode = GCLargeObjectHeapCompactionMode.CompactOnce;
+
return;
}
@@ -696,6 +798,8 @@ namespace ARMeilleure.Translation.PTC
break;
}
}
+
+ Translator.DisposePools();
}
int maxDegreeOfParallelism = (Environment.ProcessorCount * 3) / 4;
@@ -715,8 +819,6 @@ namespace ARMeilleure.Translation.PTC
threads.Clear();
- Translator.ResetPools();
-
_loggerEvent.Set();
PtcJumpTable.Initialize(jumpTable);
@@ -724,7 +826,9 @@ namespace ARMeilleure.Translation.PTC
PtcJumpTable.ReadJumpTable(jumpTable);
PtcJumpTable.ReadDynamicTable(jumpTable);
- ThreadPool.QueueUserWorkItem(PreSave);
+ Thread preSaveThread = new Thread(PreSave);
+ preSaveThread.IsBackground = true;
+ preSaveThread.Start();
}
private static void TranslationLogger(object state)
@@ -751,11 +855,11 @@ namespace ARMeilleure.Translation.PTC
_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.Code.Length); // InfoEntry.CodeLen
_infosWriter.Write((int)ptcInfo.RelocEntriesCount); // InfoEntry.RelocEntriesCount
// WriteCode.
- ptcInfo.CodeStream.WriteTo(_codesStream);
+ _codesStream.Write(ptcInfo.Code.AsSpan());
// WriteReloc.
ptcInfo.RelocStream.WriteTo(_relocsStream);
@@ -765,6 +869,11 @@ namespace ARMeilleure.Translation.PTC
}
}
+ private static bool GetEndianness()
+ {
+ return BitConverter.IsLittleEndian;
+ }
+
private static ulong GetFeatureInfo()
{
return (ulong)HardwareCapabilities.FeatureInfoEdx << 32 | (uint)HardwareCapabilities.FeatureInfoEcx;
@@ -784,9 +893,12 @@ namespace ARMeilleure.Translation.PTC
private struct Header
{
- public string Magic;
+ public const int Size = 41; // Bytes.
+
+ public ulong Magic;
public uint CacheFileVersion;
+ public bool Endianness;
public ulong FeatureInfo;
public uint OSPlatform;
@@ -849,12 +961,9 @@ namespace ARMeilleure.Translation.PTC
Wait();
_waitEvent.Dispose();
- _infosWriter.Dispose();
+ _loggerEvent.Dispose();
- _infosStream.Dispose();
- _codesStream.Dispose();
- _relocsStream.Dispose();
- _unwindInfosStream.Dispose();
+ DisposeMemoryStreams();
}
}
}
diff --git a/ARMeilleure/Translation/PTC/PtcFormatter.cs b/ARMeilleure/Translation/PTC/PtcFormatter.cs
new file mode 100644
index 00000000..7346b484
--- /dev/null
+++ b/ARMeilleure/Translation/PTC/PtcFormatter.cs
@@ -0,0 +1,121 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+namespace ARMeilleure.Translation.PTC
+{
+ public class PtcFormatter
+ {
+ #region "Deserialize"
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Dictionary<TKey, TValue> DeserializeDictionary<TKey, TValue>(Stream stream, Func<Stream, TValue> valueFunc) where TKey : unmanaged
+ {
+ Dictionary<TKey, TValue> dictionary = new();
+
+ int count = DeserializeStructure<int>(stream);
+
+ for (int i = 0; i < count; i++)
+ {
+ TKey key = DeserializeStructure<TKey>(stream);
+ TValue value = valueFunc(stream);
+
+ dictionary.Add(key, value);
+ }
+
+ return dictionary;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static List<T> DeserializeList<T>(Stream stream) where T : unmanaged
+ {
+ List<T> list = new();
+
+ int count = DeserializeStructure<int>(stream);
+
+ for (int i = 0; i < count; i++)
+ {
+ T item = DeserializeStructure<T>(stream);
+
+ list.Add(item);
+ }
+
+ return list;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static T DeserializeStructure<T>(Stream stream) where T : unmanaged
+ {
+ T structure = default(T);
+
+ Span<T> spanT = MemoryMarshal.CreateSpan(ref structure, 1);
+ stream.Read(MemoryMarshal.AsBytes(spanT));
+
+ return structure;
+ }
+ #endregion
+
+ #region "GetSerializeSize"
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static int GetSerializeSizeDictionary<TKey, TValue>(Dictionary<TKey, TValue> dictionary, Func<TValue, int> valueFunc) where TKey : unmanaged
+ {
+ int size = 0;
+
+ size += Unsafe.SizeOf<int>();
+
+ foreach ((_, TValue value) in dictionary)
+ {
+ size += Unsafe.SizeOf<TKey>();
+ size += valueFunc(value);
+ }
+
+ return size;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static int GetSerializeSizeList<T>(List<T> list) where T : unmanaged
+ {
+ int size = 0;
+
+ size += Unsafe.SizeOf<int>();
+
+ size += list.Count * Unsafe.SizeOf<T>();
+
+ return size;
+ }
+ #endregion
+
+ #region "Serialize"
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static void SerializeDictionary<TKey, TValue>(Stream stream, Dictionary<TKey, TValue> dictionary, Action<Stream, TValue> valueAction) where TKey : unmanaged
+ {
+ SerializeStructure<int>(stream, dictionary.Count);
+
+ foreach ((TKey key, TValue value) in dictionary)
+ {
+ SerializeStructure<TKey>(stream, key);
+ valueAction(stream, value);
+ }
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static void SerializeList<T>(Stream stream, List<T> list) where T : unmanaged
+ {
+ SerializeStructure<int>(stream, list.Count);
+
+ foreach (T item in list)
+ {
+ SerializeStructure<T>(stream, item);
+ }
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static void SerializeStructure<T>(Stream stream, T structure) where T : unmanaged
+ {
+ Span<T> spanT = MemoryMarshal.CreateSpan(ref structure, 1);
+ stream.Write(MemoryMarshal.AsBytes(spanT));
+ }
+ #endregion
+ }
+} \ No newline at end of file
diff --git a/ARMeilleure/Translation/PTC/PtcInfo.cs b/ARMeilleure/Translation/PTC/PtcInfo.cs
index 547fe6d8..bbecb56f 100644
--- a/ARMeilleure/Translation/PTC/PtcInfo.cs
+++ b/ARMeilleure/Translation/PTC/PtcInfo.cs
@@ -9,7 +9,8 @@ namespace ARMeilleure.Translation.PTC
private readonly BinaryWriter _relocWriter;
private readonly BinaryWriter _unwindInfoWriter;
- public MemoryStream CodeStream { get; }
+ public byte[] Code { get; set; }
+
public MemoryStream RelocStream { get; }
public MemoryStream UnwindInfoStream { get; }
@@ -17,7 +18,6 @@ namespace ARMeilleure.Translation.PTC
public PtcInfo()
{
- CodeStream = new MemoryStream();
RelocStream = new MemoryStream();
UnwindInfoStream = new MemoryStream();
@@ -27,11 +27,6 @@ namespace ARMeilleure.Translation.PTC
RelocEntriesCount = 0;
}
- public void WriteCode(MemoryStream codeStream)
- {
- codeStream.WriteTo(CodeStream);
- }
-
public void WriteRelocEntry(RelocEntry relocEntry)
{
_relocWriter.Write((int)relocEntry.Position);
@@ -60,7 +55,6 @@ namespace ARMeilleure.Translation.PTC
_relocWriter.Dispose();
_unwindInfoWriter.Dispose();
- CodeStream.Dispose();
RelocStream.Dispose();
UnwindInfoStream.Dispose();
}
diff --git a/ARMeilleure/Translation/PTC/PtcJumpTable.cs b/ARMeilleure/Translation/PTC/PtcJumpTable.cs
index d52d0874..75dcba50 100644
--- a/ARMeilleure/Translation/PTC/PtcJumpTable.cs
+++ b/ARMeilleure/Translation/PTC/PtcJumpTable.cs
@@ -6,15 +6,18 @@ using System.Diagnostics;
using System.IO;
using System.Runtime.InteropServices;
+using static ARMeilleure.Translation.PTC.PtcFormatter;
+
namespace ARMeilleure.Translation.PTC
{
class PtcJumpTable
{
+ [StructLayout(LayoutKind.Sequential, Pack = 1/*, Size = 16*/)]
public struct TableEntry<TAddress>
{
public int EntryIndex;
public long GuestAddress;
- public TAddress HostAddress;
+ public TAddress HostAddress; // int
public TableEntry(int entryIndex, long guestAddress, TAddress hostAddress)
{
@@ -24,14 +27,14 @@ namespace ARMeilleure.Translation.PTC
}
}
- public enum DirectHostAddress
+ public enum DirectHostAddress : int
{
CallStub = 0,
TailCallStub = 1,
Host = 2
}
- public enum IndirectHostAddress
+ public enum IndirectHostAddress : int
{
CallStub = 0,
TailCallStub = 1
@@ -66,152 +69,40 @@ namespace ARMeilleure.Translation.PTC
Owners = owners;
}
- public static PtcJumpTable Deserialize(MemoryStream stream)
+ public static PtcJumpTable Deserialize(Stream 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>();
+ var jumpTable = DeserializeList<TableEntry<DirectHostAddress>>(stream);
+ var dynamicTable = DeserializeList<TableEntry<IndirectHostAddress>>(stream);
- int entriesCount = reader.ReadInt32();
+ var targets = DeserializeList<ulong>(stream);
+ var dependants = DeserializeDictionary<ulong, List<int>>(stream, (stream) => DeserializeList<int>(stream));
+ var owners = DeserializeDictionary<ulong, List<int>>(stream, (stream) => DeserializeList<int>(stream));
- 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);
- }
+ return new PtcJumpTable(jumpTable, dynamicTable, targets, dependants, owners);
}
- public static void Serialize(MemoryStream stream, PtcJumpTable ptcJumpTable)
+ public static int GetSerializeSize(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);
+ int size = 0;
- foreach (var kv in ptcJumpTable.Dependants)
- {
- writer.Write((ulong)kv.Key); // address
+ size += GetSerializeSizeList(ptcJumpTable._jumpTable);
+ size += GetSerializeSizeList(ptcJumpTable._dynamicTable);
- writer.Write((int)kv.Value.Count);
+ size += GetSerializeSizeList(ptcJumpTable.Targets);
+ size += GetSerializeSizeDictionary(ptcJumpTable.Dependants, (list) => GetSerializeSizeList(list));
+ size += GetSerializeSizeDictionary(ptcJumpTable.Owners, (list) => GetSerializeSizeList(list));
- 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
+ return size;
+ }
- writer.Write((int)kv.Value.Count);
+ public static void Serialize(Stream stream, PtcJumpTable ptcJumpTable)
+ {
+ SerializeList(stream, ptcJumpTable._jumpTable);
+ SerializeList(stream, ptcJumpTable._dynamicTable);
- foreach (int entry in kv.Value)
- {
- writer.Write((int)entry);
- }
- }
- }
+ SerializeList(stream, ptcJumpTable.Targets);
+ SerializeDictionary(stream, ptcJumpTable.Dependants, (stream, list) => SerializeList(stream, list));
+ SerializeDictionary(stream, ptcJumpTable.Owners, (stream, list) => SerializeList(stream, list));
}
public void Initialize(JumpTable jumpTable)
@@ -270,14 +161,25 @@ namespace ARMeilleure.Translation.PTC
Owners.Remove(guestAddress);
}
- public void Clear()
+ public void ClearIfNeeded()
{
+ if (_jumpTable.Count == 0 && _dynamicTable.Count == 0 &&
+ Targets.Count == 0 && Dependants.Count == 0 && Owners.Count == 0)
+ {
+ return;
+ }
+
_jumpTable.Clear();
+ _jumpTable.TrimExcess();
_dynamicTable.Clear();
+ _dynamicTable.TrimExcess();
Targets.Clear();
+ Targets.TrimExcess();
Dependants.Clear();
+ Dependants.TrimExcess();
Owners.Clear();
+ Owners.TrimExcess();
}
public void WriteJumpTable(JumpTable jumpTable, ConcurrentDictionary<ulong, TranslatedFunction> funcs)
@@ -307,7 +209,7 @@ namespace ARMeilleure.Translation.PTC
}
else
{
- if (!PtcProfiler.ProfiledFuncs.TryGetValue((ulong)guestAddress, out var value) || !value.highCq)
+ if (!PtcProfiler.ProfiledFuncs.TryGetValue((ulong)guestAddress, out var value) || !value.HighCq)
{
throw new KeyNotFoundException($"({nameof(guestAddress)} = 0x{(ulong)guestAddress:X16})");
}
diff --git a/ARMeilleure/Translation/PTC/PtcProfiler.cs b/ARMeilleure/Translation/PTC/PtcProfiler.cs
index 0def32c3..8782a794 100644
--- a/ARMeilleure/Translation/PTC/PtcProfiler.cs
+++ b/ARMeilleure/Translation/PTC/PtcProfiler.cs
@@ -6,9 +6,12 @@ using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.IO.Compression;
+using System.Runtime.InteropServices;
using System.Security.Cryptography;
using System.Threading;
+using static ARMeilleure.Translation.PTC.PtcFormatter;
+
namespace ARMeilleure.Translation.PTC
{
public static class PtcProfiler
@@ -29,7 +32,9 @@ namespace ARMeilleure.Translation.PTC
private static bool _disposed;
- internal static Dictionary<ulong, (ExecutionMode mode, bool highCq)> ProfiledFuncs { get; private set; }
+ private static byte[] _lastHash;
+
+ internal static Dictionary<ulong, FuncProfile> ProfiledFuncs { get; private set; }
internal static bool Enabled { get; private set; }
@@ -47,7 +52,7 @@ namespace ARMeilleure.Translation.PTC
_disposed = false;
- ProfiledFuncs = new Dictionary<ulong, (ExecutionMode, bool)>();
+ ProfiledFuncs = new Dictionary<ulong, FuncProfile>();
Enabled = false;
}
@@ -60,7 +65,7 @@ namespace ARMeilleure.Translation.PTC
lock (_lock)
{
- ProfiledFuncs.TryAdd(address, (mode, highCq: false));
+ ProfiledFuncs.TryAdd(address, new FuncProfile(mode, highCq: false));
}
}
}
@@ -75,7 +80,7 @@ namespace ARMeilleure.Translation.PTC
{
Debug.Assert(ProfiledFuncs.ContainsKey(address));
- ProfiledFuncs[address] = (mode, highCq: true);
+ ProfiledFuncs[address] = new FuncProfile(mode, highCq: true);
}
}
}
@@ -95,7 +100,7 @@ namespace ARMeilleure.Translation.PTC
if (!funcs.ContainsKey(address))
{
- profiledFuncsToTranslate.Enqueue((address, profiledFunc.Value.mode, profiledFunc.Value.highCq));
+ profiledFuncsToTranslate.Enqueue((address, profiledFunc.Value.Mode, profiledFunc.Value.HighCq));
}
}
@@ -105,12 +110,15 @@ namespace ARMeilleure.Translation.PTC
internal static void ClearEntries()
{
ProfiledFuncs.Clear();
+ ProfiledFuncs.TrimExcess();
}
internal static void PreLoad()
{
- string fileNameActual = String.Concat(Ptc.CachePathActual, ".info");
- string fileNameBackup = String.Concat(Ptc.CachePathBackup, ".info");
+ _lastHash = Array.Empty<byte>();
+
+ string fileNameActual = string.Concat(Ptc.CachePathActual, ".info");
+ string fileNameBackup = string.Concat(Ptc.CachePathBackup, ".info");
FileInfo fileInfoActual = new FileInfo(fileNameActual);
FileInfo fileInfoBackup = new FileInfo(fileNameBackup);
@@ -138,8 +146,6 @@ namespace ARMeilleure.Translation.PTC
using (MemoryStream stream = new MemoryStream())
using (MD5 md5 = MD5.Create())
{
- int hashSize = md5.HashSize / 8;
-
try
{
deflateStream.CopyTo(stream);
@@ -151,6 +157,8 @@ namespace ARMeilleure.Translation.PTC
return false;
}
+ int hashSize = md5.HashSize / 8;
+
stream.Seek(0L, SeekOrigin.Begin);
byte[] currentHash = new byte[hashSize];
@@ -189,12 +197,14 @@ namespace ARMeilleure.Translation.PTC
}
catch
{
- ProfiledFuncs = new Dictionary<ulong, (ExecutionMode, bool)>();
+ ProfiledFuncs = new Dictionary<ulong, FuncProfile>();
InvalidateCompressedStream(compressedStream);
return false;
}
+
+ _lastHash = expectedHash;
}
long fileSize = new FileInfo(fileName).Length;
@@ -209,7 +219,7 @@ namespace ARMeilleure.Translation.PTC
return currentHash.SequenceEqual(expectedHash);
}
- private static Header ReadHeader(MemoryStream stream)
+ private static Header ReadHeader(Stream stream)
{
using (BinaryReader headerReader = new BinaryReader(stream, EncodingCache.UTF8NoBOM, true))
{
@@ -223,26 +233,9 @@ namespace ARMeilleure.Translation.PTC
}
}
- private static Dictionary<ulong, (ExecutionMode, bool)> Deserialize(MemoryStream stream)
+ private static Dictionary<ulong, FuncProfile> Deserialize(Stream 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;
- }
+ return DeserializeDictionary<ulong, FuncProfile>(stream, (stream) => DeserializeStructure<FuncProfile>(stream));
}
private static void InvalidateCompressedStream(FileStream compressedStream)
@@ -254,8 +247,8 @@ namespace ARMeilleure.Translation.PTC
{
_waitEvent.Reset();
- string fileNameActual = String.Concat(Ptc.CachePathActual, ".info");
- string fileNameBackup = String.Concat(Ptc.CachePathBackup, ".info");
+ string fileNameActual = string.Concat(Ptc.CachePathActual, ".info");
+ string fileNameBackup = string.Concat(Ptc.CachePathBackup, ".info");
FileInfo fileInfoActual = new FileInfo(fileNameActual);
@@ -295,16 +288,25 @@ namespace ARMeilleure.Translation.PTC
stream.Seek(0L, SeekOrigin.Begin);
stream.Write(hash, 0, hashSize);
+ if (CompareHash(hash, _lastHash))
+ {
+ return;
+ }
+
using (FileStream compressedStream = new FileStream(fileName, FileMode.OpenOrCreate))
using (DeflateStream deflateStream = new DeflateStream(compressedStream, SaveCompressionLevel, true))
{
try
{
stream.WriteTo(deflateStream);
+
+ _lastHash = hash;
}
catch
{
compressedStream.Position = 0L;
+
+ _lastHash = Array.Empty<byte>();
}
if (compressedStream.Position < compressedStream.Length)
@@ -316,10 +318,13 @@ namespace ARMeilleure.Translation.PTC
long fileSize = new FileInfo(fileName).Length;
- Logger.Info?.Print(LogClass.Ptc, $"Saved Profiling Info (size: {fileSize} bytes, profiled functions: {profiledFuncsCount}).");
+ if (fileSize != 0L)
+ {
+ Logger.Info?.Print(LogClass.Ptc, $"Saved Profiling Info (size: {fileSize} bytes, profiled functions: {profiledFuncsCount}).");
+ }
}
- private static void WriteHeader(MemoryStream stream)
+ private static void WriteHeader(Stream stream)
{
using (BinaryWriter headerWriter = new BinaryWriter(stream, EncodingCache.UTF8NoBOM, true))
{
@@ -329,20 +334,9 @@ namespace ARMeilleure.Translation.PTC
}
}
- private static void Serialize(MemoryStream stream, Dictionary<ulong, (ExecutionMode mode, bool highCq)> profiledFuncs)
+ private static void Serialize(Stream stream, Dictionary<ulong, FuncProfile> 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);
- }
- }
+ SerializeDictionary(stream, profiledFuncs, (stream, structure) => SerializeStructure(stream, structure));
}
private struct Header
@@ -352,6 +346,19 @@ namespace ARMeilleure.Translation.PTC
public uint InfoFileVersion;
}
+ [StructLayout(LayoutKind.Sequential, Pack = 1/*, Size = 5*/)]
+ internal struct FuncProfile
+ {
+ public ExecutionMode Mode;
+ public bool HighCq;
+
+ public FuncProfile(ExecutionMode mode, bool highCq)
+ {
+ Mode = mode;
+ HighCq = highCq;
+ }
+ }
+
internal static void Start()
{
if (Ptc.State == PtcState.Enabled ||
diff --git a/ARMeilleure/Translation/Translator.cs b/ARMeilleure/Translation/Translator.cs
index ac96475f..f64912b3 100644
--- a/ARMeilleure/Translation/Translator.cs
+++ b/ARMeilleure/Translation/Translator.cs
@@ -11,8 +11,10 @@ using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
+using System.Runtime;
using System.Threading;
+using static ARMeilleure.Common.BitMapPool;
using static ARMeilleure.IntermediateRepresentation.OperandHelper;
using static ARMeilleure.IntermediateRepresentation.OperationHelper;
@@ -148,10 +150,12 @@ namespace ARMeilleure.Translation
ClearJitCache();
- ResetPools();
+ DisposePools();
_jumpTable.Dispose();
_jumpTable = null;
+
+ GCSettings.LargeObjectHeapCompactionMode = GCLargeObjectHeapCompactionMode.CompactOnce;
}
}
@@ -207,7 +211,7 @@ namespace ARMeilleure.Translation
Logger.EndPass(PassName.Decoding);
- PreparePool(highCq);
+ PreparePool(highCq ? 1 : 0);
Logger.StartPass(PassName.Translation);
@@ -240,13 +244,15 @@ namespace ARMeilleure.Translation
{
func = Compiler.Compile<GuestFunction>(cfg, argTypes, OperandType.I64, options);
- ReturnPool(highCq);
+ ResetPool(highCq ? 1 : 0);
}
- else using (PtcInfo ptcInfo = new PtcInfo())
+ else
{
+ using PtcInfo ptcInfo = new PtcInfo();
+
func = Compiler.Compile<GuestFunction>(cfg, argTypes, OperandType.I64, options, ptcInfo);
- ReturnPool(highCq);
+ ResetPool(highCq ? 1 : 0);
Ptc.WriteInfoCodeRelocUnwindInfo(address, funcSize, highCq, ptcInfo);
}
@@ -254,22 +260,23 @@ namespace ARMeilleure.Translation
return new TranslatedFunction(func, funcSize, highCq);
}
- internal static void PreparePool(bool highCq)
+ internal static void PreparePool(int groupId = 0)
{
- PrepareOperandPool(highCq);
- PrepareOperationPool(highCq);
+ PrepareOperandPool(groupId);
+ PrepareOperationPool(groupId);
}
- internal static void ReturnPool(bool highCq)
+ internal static void ResetPool(int groupId = 0)
{
- ReturnOperandPool(highCq);
- ReturnOperationPool(highCq);
+ ResetOperationPool(groupId);
+ ResetOperandPool(groupId);
}
- internal static void ResetPools()
+ internal static void DisposePools()
{
- ResetOperandPools();
- ResetOperationPools();
+ DisposeOperandPools();
+ DisposeOperationPools();
+ DisposeBitMapPools();
}
private struct Range