|
|
@@ -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();
|
|
|
}
|
|
|
}
|
|
|
}
|