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 998be765cf.
This commit is contained in:
LDj3SNuD 2021-02-22 03:23:48 +01:00 committed by GitHub
parent 1586880114
commit dc0adb533d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
20 changed files with 777 additions and 603 deletions

View file

@ -50,6 +50,8 @@ namespace ARMeilleure.CodeGen.RegisterAllocators
StackAlloc = stackAlloc; StackAlloc = stackAlloc;
Masks = masks; Masks = masks;
BitMapPool.PrepareBitMapPool();
Active = BitMapPool.Allocate(intervalsCount); Active = BitMapPool.Allocate(intervalsCount);
Inactive = BitMapPool.Allocate(intervalsCount); Inactive = BitMapPool.Allocate(intervalsCount);
} }
@ -73,7 +75,7 @@ namespace ARMeilleure.CodeGen.RegisterAllocators
public void Dispose() public void Dispose()
{ {
BitMapPool.Release(); BitMapPool.ResetBitMapPool();
} }
} }
@ -84,7 +86,7 @@ namespace ARMeilleure.CodeGen.RegisterAllocators
{ {
NumberLocals(cfg); NumberLocals(cfg);
AllocationContext context = new AllocationContext(stackAlloc, regMasks, _intervals.Count); using AllocationContext context = new AllocationContext(stackAlloc, regMasks, _intervals.Count);
BuildIntervals(cfg, context); BuildIntervals(cfg, context);
@ -127,14 +129,7 @@ namespace ARMeilleure.CodeGen.RegisterAllocators
InsertSplitCopies(); InsertSplitCopies();
InsertSplitCopiesAtEdges(cfg); InsertSplitCopiesAtEdges(cfg);
AllocationResult result = new AllocationResult( return new AllocationResult(context.IntUsedRegisters, context.VecUsedRegisters, context.StackAlloc.TotalSize);
context.IntUsedRegisters,
context.VecUsedRegisters,
context.StackAlloc.TotalSize);
context.Dispose();
return result;
} }
private void AllocateInterval(AllocationContext context, LiveInterval current, int cIndex) private void AllocateInterval(AllocationContext context, LiveInterval current, int cIndex)

View file

@ -2,6 +2,7 @@ using ARMeilleure.CodeGen.RegisterAllocators;
using ARMeilleure.Common; using ARMeilleure.Common;
using ARMeilleure.IntermediateRepresentation; using ARMeilleure.IntermediateRepresentation;
using ARMeilleure.Translation.PTC; using ARMeilleure.Translation.PTC;
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.IO; using System.IO;
@ -301,15 +302,13 @@ namespace ARMeilleure.CodeGen.X86
{ {
Assembler assembler = new Assembler(codeStream, _ptcInfo); Assembler assembler = new Assembler(codeStream, _ptcInfo);
byte[] buffer;
for (int index = 0; index < _jumps.Count; index++) for (int index = 0; index < _jumps.Count; index++)
{ {
Jump jump = _jumps[index]; Jump jump = _jumps[index];
buffer = new byte[jump.JumpPosition - _stream.Position]; Span<byte> buffer = new byte[jump.JumpPosition - _stream.Position];
_stream.Read(buffer, 0, buffer.Length); _stream.Read(buffer);
_stream.Seek(_ptcDisabled ? ReservedBytesForJump : jump.InstSize, SeekOrigin.Current); _stream.Seek(_ptcDisabled ? ReservedBytesForJump : jump.InstSize, SeekOrigin.Current);
codeStream.Write(buffer); codeStream.Write(buffer);
@ -324,13 +323,7 @@ namespace ARMeilleure.CodeGen.X86
} }
} }
buffer = new byte[_stream.Length - _stream.Position]; _stream.CopyTo(codeStream);
_stream.Read(buffer, 0, buffer.Length);
codeStream.Write(buffer);
_ptcInfo?.WriteCode(codeStream);
return codeStream.ToArray(); return codeStream.ToArray();
} }

View file

@ -190,6 +190,11 @@ namespace ARMeilleure.CodeGen.X86
byte[] code = context.GetCode(); byte[] code = context.GetCode();
if (ptcInfo != null)
{
ptcInfo.Code = code;
}
Logger.EndPass(PassName.CodeGeneration); Logger.EndPass(PassName.CodeGeneration);
return new CompiledFunction(code, unwindInfo); return new CompiledFunction(code, unwindInfo);

View file

@ -35,7 +35,7 @@ namespace ARMeilleure.Common
} }
} }
public void Reset(int initialCapacity) public BitMap Reset(int initialCapacity)
{ {
int count = (initialCapacity + IntMask) / IntSize; int count = (initialCapacity + IntMask) / IntSize;
@ -50,6 +50,8 @@ namespace ARMeilleure.Common
{ {
_masks.Add(0); _masks.Add(0);
} }
return this;
} }
public bool Set(int bit) public bool Set(int bit)

View file

@ -4,15 +4,29 @@
{ {
public static BitMap Allocate(int initialCapacity) public static BitMap Allocate(int initialCapacity)
{ {
BitMap result = ThreadStaticPool<BitMap>.Instance.Allocate(); return BitMap().Reset(initialCapacity);
result.Reset(initialCapacity);
return result;
} }
public static void Release() #region "ThreadStaticPool"
public static void PrepareBitMapPool(int groupId = 0)
{ {
ThreadStaticPool<BitMap>.Instance.Clear(); ThreadStaticPool<BitMap>.PreparePool(groupId, ChunkSizeLimit.Small);
} }
private static BitMap BitMap()
{
return ThreadStaticPool<BitMap>.Instance.Allocate();
}
public static void ResetBitMapPool(int groupId = 0)
{
ThreadStaticPool<BitMap>.ResetPool(groupId);
}
public static void DisposeBitMapPools()
{
ThreadStaticPool<BitMap>.DisposePools();
}
#endregion
} }
} }

View file

@ -1,4 +1,5 @@
using System; using ARMeilleure.Translation.PTC;
using System;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Collections.Generic; using System.Collections.Generic;
@ -6,9 +7,6 @@ namespace ARMeilleure.Common
{ {
class ThreadStaticPool<T> where T : class, new() class ThreadStaticPool<T> where T : class, new()
{ {
private const int ChunkSizeLimit = 1000; // even
private const int PoolSizeIncrement = 200; // > 0
[ThreadStatic] [ThreadStatic]
private static ThreadStaticPool<T> _instance; private static ThreadStaticPool<T> _instance;
@ -18,21 +16,36 @@ namespace ARMeilleure.Common
{ {
if (_instance == null) if (_instance == null)
{ {
PreparePool(0); // So that we can still use a pool when blindly initializing one. PreparePool(); // So that we can still use a pool when blindly initializing one.
} }
return _instance; return _instance;
} }
} }
private static ConcurrentDictionary<int, Stack<ThreadStaticPool<T>>> _pools = new(); private static readonly ConcurrentDictionary<int, Stack<ThreadStaticPool<T>>> _pools = new();
private static Stack<ThreadStaticPool<T>> GetPools(int groupId) private static Stack<ThreadStaticPool<T>> GetPools(int groupId)
{ {
return _pools.GetOrAdd(groupId, (groupId) => new()); return _pools.GetOrAdd(groupId, (groupId) => new());
} }
public static void PreparePool(int groupId) public static void PreparePool(
int groupId = 0,
ChunkSizeLimit chunkSizeLimit = ChunkSizeLimit.Large,
PoolSizeIncrement poolSizeIncrement = PoolSizeIncrement.Default)
{
if (Ptc.State == PtcState.Disabled)
{
PreparePoolDefault(groupId, (int)chunkSizeLimit, (int)poolSizeIncrement);
}
else
{
PreparePoolSlim((int)chunkSizeLimit, (int)poolSizeIncrement);
}
}
private static void PreparePoolDefault(int groupId, int chunkSizeLimit, int poolSizeIncrement)
{ {
// Prepare the pool for this thread, ideally using an existing one from the specified group. // Prepare the pool for this thread, ideally using an existing one from the specified group.
@ -41,27 +54,75 @@ namespace ARMeilleure.Common
var pools = GetPools(groupId); var pools = GetPools(groupId);
lock (pools) lock (pools)
{ {
_instance = (pools.Count != 0) ? pools.Pop() : new(); _instance = (pools.Count != 0) ? pools.Pop() : new(chunkSizeLimit, poolSizeIncrement);
} }
} }
} }
public static void ReturnPool(int groupId) private static void PreparePoolSlim(int chunkSizeLimit, int poolSizeIncrement)
{ {
// Reset, limit if necessary, and return the pool for this thread to the specified group. // Prepare the pool for this thread.
var pools = GetPools(groupId); if (_instance == null)
lock (pools)
{ {
_instance.Clear(); _instance = new(chunkSizeLimit, poolSizeIncrement);
_instance.ChunkSizeLimiter();
pools.Push(_instance);
_instance = null;
} }
} }
public static void ResetPools() public static void ResetPool(int groupId = 0)
{
if (Ptc.State == PtcState.Disabled)
{
ResetPoolDefault(groupId);
}
else
{
ResetPoolSlim();
}
}
private static void ResetPoolDefault(int groupId)
{
// Reset, limit if necessary, and return the pool for this thread to the specified group.
if (_instance != null)
{
var pools = GetPools(groupId);
lock (pools)
{
_instance.Clear();
_instance.ChunkSizeLimiter();
pools.Push(_instance);
_instance = null;
}
}
}
private static void ResetPoolSlim()
{
// Reset, limit if necessary, the pool for this thread.
if (_instance != null)
{
_instance.Clear();
_instance.ChunkSizeLimiter();
}
}
public static void DisposePools()
{
if (Ptc.State == PtcState.Disabled)
{
DisposePoolsDefault();
}
else
{
DisposePoolSlim();
}
}
private static void DisposePoolsDefault()
{ {
// Resets any static references to the pools used by threads for each group, allowing them to be garbage collected. // Resets any static references to the pools used by threads for each group, allowing them to be garbage collected.
@ -78,20 +139,37 @@ namespace ARMeilleure.Common
_pools.Clear(); _pools.Clear();
} }
private static void DisposePoolSlim()
{
// Dispose the pool for this thread.
if (_instance != null)
{
_instance.Dispose();
_instance = null;
}
}
private List<T[]> _pool; private List<T[]> _pool;
private int _chunkIndex = -1; private int _chunkIndex = -1;
private int _poolIndex = -1; private int _poolIndex = -1;
private int _chunkSizeLimit;
private int _poolSizeIncrement;
private ThreadStaticPool() private ThreadStaticPool(int chunkSizeLimit, int poolSizeIncrement)
{ {
_pool = new(ChunkSizeLimit * 2); _chunkSizeLimit = chunkSizeLimit;
_poolSizeIncrement = poolSizeIncrement;
_pool = new(chunkSizeLimit * 2);
AddChunkIfNeeded(); AddChunkIfNeeded();
} }
public T Allocate() public T Allocate()
{ {
if (++_poolIndex >= PoolSizeIncrement) if (++_poolIndex >= _poolSizeIncrement)
{ {
AddChunkIfNeeded(); AddChunkIfNeeded();
@ -105,9 +183,9 @@ namespace ARMeilleure.Common
{ {
if (++_chunkIndex >= _pool.Count) if (++_chunkIndex >= _pool.Count)
{ {
T[] pool = new T[PoolSizeIncrement]; T[] pool = new T[_poolSizeIncrement];
for (int i = 0; i < PoolSizeIncrement; i++) for (int i = 0; i < _poolSizeIncrement; i++)
{ {
pool[i] = new T(); pool[i] = new T();
} }
@ -124,18 +202,18 @@ namespace ARMeilleure.Common
private void ChunkSizeLimiter() private void ChunkSizeLimiter()
{ {
if (_pool.Count >= ChunkSizeLimit) if (_pool.Count >= _chunkSizeLimit)
{ {
int newChunkSize = ChunkSizeLimit / 2; int newChunkSize = _chunkSizeLimit / 2;
_pool.RemoveRange(newChunkSize, _pool.Count - newChunkSize); _pool.RemoveRange(newChunkSize, _pool.Count - newChunkSize);
_pool.Capacity = ChunkSizeLimit * 2; _pool.Capacity = _chunkSizeLimit * 2;
} }
} }
private void Dispose() private void Dispose()
{ {
_pool.Clear(); _pool = null;
} }
} }
} }

View file

@ -0,0 +1,14 @@
namespace ARMeilleure.Common
{
public enum PoolSizeIncrement
{
Default = 200
}
public enum ChunkSizeLimit
{
Large = 200000 / PoolSizeIncrement.Default,
Medium = 100000 / PoolSizeIncrement.Default,
Small = 50000 / PoolSizeIncrement.Default
}
}

View file

@ -4,16 +4,6 @@ namespace ARMeilleure.IntermediateRepresentation
{ {
static class OperandHelper static class OperandHelper
{ {
private static MemoryOperand MemoryOperand()
{
return ThreadStaticPool<MemoryOperand>.Instance.Allocate();
}
private static Operand Operand()
{
return ThreadStaticPool<Operand>.Instance.Allocate();
}
public static Operand Const(OperandType type, long value) public static Operand Const(OperandType type, long value)
{ {
return type == OperandType.I32 ? Operand().With((int)value) : Operand().With(value); return type == OperandType.I32 ? Operand().With((int)value) : Operand().With(value);
@ -84,22 +74,34 @@ namespace ARMeilleure.IntermediateRepresentation
return MemoryOperand().With(type, baseAddress, index, scale, displacement); return MemoryOperand().With(type, baseAddress, index, scale, displacement);
} }
public static void PrepareOperandPool(bool highCq) #region "ThreadStaticPool"
public static void PrepareOperandPool(int groupId = 0)
{ {
ThreadStaticPool<Operand>.PreparePool(highCq ? 1 : 0); ThreadStaticPool<Operand>.PreparePool(groupId, ChunkSizeLimit.Large);
ThreadStaticPool<MemoryOperand>.PreparePool(highCq ? 1 : 0); ThreadStaticPool<MemoryOperand>.PreparePool(groupId, ChunkSizeLimit.Small);
} }
public static void ReturnOperandPool(bool highCq) private static Operand Operand()
{ {
ThreadStaticPool<Operand>.ReturnPool(highCq ? 1 : 0); return ThreadStaticPool<Operand>.Instance.Allocate();
ThreadStaticPool<MemoryOperand>.ReturnPool(highCq ? 1 : 0);
} }
public static void ResetOperandPools() private static MemoryOperand MemoryOperand()
{ {
ThreadStaticPool<Operand>.ResetPools(); return ThreadStaticPool<MemoryOperand>.Instance.Allocate();
ThreadStaticPool<MemoryOperand>.ResetPools();
} }
public static void ResetOperandPool(int groupId = 0)
{
ThreadStaticPool<MemoryOperand>.ResetPool(groupId);
ThreadStaticPool<Operand>.ResetPool(groupId);
}
public static void DisposeOperandPools()
{
ThreadStaticPool<Operand>.DisposePools();
ThreadStaticPool<MemoryOperand>.DisposePools();
}
#endregion
} }
} }

View file

@ -4,11 +4,6 @@ namespace ARMeilleure.IntermediateRepresentation
{ {
static class OperationHelper static class OperationHelper
{ {
public static Operation Operation()
{
return ThreadStaticPool<Operation>.Instance.Allocate();
}
public static Operation Operation(Instruction instruction, Operand destination) public static Operation Operation(Instruction instruction, Operand destination)
{ {
return Operation().With(instruction, destination); return Operation().With(instruction, destination);
@ -46,19 +41,26 @@ namespace ARMeilleure.IntermediateRepresentation
return Operation().With(instruction, destinations, sources); return Operation().With(instruction, destinations, sources);
} }
public static void PrepareOperationPool(bool highCq) #region "ThreadStaticPool"
public static void PrepareOperationPool(int groupId = 0)
{ {
ThreadStaticPool<Operation>.PreparePool(highCq ? 1 : 0); ThreadStaticPool<Operation>.PreparePool(groupId, ChunkSizeLimit.Medium);
} }
public static void ReturnOperationPool(bool highCq) private static Operation Operation()
{ {
ThreadStaticPool<Operation>.ReturnPool(highCq ? 1 : 0); return ThreadStaticPool<Operation>.Instance.Allocate();
} }
public static void ResetOperationPools() public static void ResetOperationPool(int groupId = 0)
{ {
ThreadStaticPool<Operation>.ResetPools(); ThreadStaticPool<Operation>.ResetPool(groupId);
} }
public static void DisposeOperationPools()
{
ThreadStaticPool<Operation>.DisposePools();
}
#endregion
} }
} }

View file

@ -1,6 +1,6 @@
namespace ARMeilleure.State namespace ARMeilleure.State
{ {
enum ExecutionMode enum ExecutionMode : int
{ {
Aarch32Arm = 0, Aarch32Arm = 0,
Aarch32Thumb = 1, Aarch32Thumb = 1,

View file

@ -29,11 +29,17 @@ namespace ARMeilleure.Translation
{ {
if (_initialized) return; if (_initialized) return;
Translator.PreparePool();
_directCallStubPtr = Marshal.GetFunctionPointerForDelegate<GuestFunction>(GenerateDirectCallStub(false)); _directCallStubPtr = Marshal.GetFunctionPointerForDelegate<GuestFunction>(GenerateDirectCallStub(false));
_directTailCallStubPtr = Marshal.GetFunctionPointerForDelegate<GuestFunction>(GenerateDirectCallStub(true)); _directTailCallStubPtr = Marshal.GetFunctionPointerForDelegate<GuestFunction>(GenerateDirectCallStub(true));
_indirectCallStubPtr = Marshal.GetFunctionPointerForDelegate<GuestFunction>(GenerateIndirectCallStub(false)); _indirectCallStubPtr = Marshal.GetFunctionPointerForDelegate<GuestFunction>(GenerateIndirectCallStub(false));
_indirectTailCallStubPtr = Marshal.GetFunctionPointerForDelegate<GuestFunction>(GenerateIndirectCallStub(true)); _indirectTailCallStubPtr = Marshal.GetFunctionPointerForDelegate<GuestFunction>(GenerateIndirectCallStub(true));
Translator.ResetPool();
Translator.DisposePools();
_initialized = true; _initialized = true;
} }
} }

View file

@ -1,6 +1,7 @@
using ARMeilleure.Diagnostics; using ARMeilleure.Diagnostics;
using ARMeilleure.IntermediateRepresentation; using ARMeilleure.IntermediateRepresentation;
using ARMeilleure.State; using ARMeilleure.State;
using ARMeilleure.Translation.PTC;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Reflection; using System.Reflection;
@ -9,8 +10,6 @@ using static ARMeilleure.IntermediateRepresentation.OperandHelper;
namespace ARMeilleure.Translation namespace ARMeilleure.Translation
{ {
using PTC;
class EmitterContext class EmitterContext
{ {
private readonly Dictionary<Operand, BasicBlock> _irLabels; private readonly Dictionary<Operand, BasicBlock> _irLabels;

View file

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

View file

@ -3,6 +3,7 @@ using ARMeilleure.CodeGen.Unwinding;
using ARMeilleure.CodeGen.X86; using ARMeilleure.CodeGen.X86;
using ARMeilleure.Memory; using ARMeilleure.Memory;
using ARMeilleure.Translation.Cache; using ARMeilleure.Translation.Cache;
using Ryujinx.Common;
using Ryujinx.Common.Configuration; using Ryujinx.Common.Configuration;
using Ryujinx.Common.Logging; using Ryujinx.Common.Logging;
using System; using System;
@ -12,17 +13,20 @@ using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.IO; using System.IO;
using System.IO.Compression; using System.IO.Compression;
using System.Runtime;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Security.Cryptography;
using System.Threading; using System.Threading;
using static ARMeilleure.Translation.PTC.PtcFormatter;
namespace ARMeilleure.Translation.PTC namespace ARMeilleure.Translation.PTC
{ {
public static class 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 ActualDir = "0";
private const string BackupDir = "1"; private const string BackupDir = "1";
@ -37,12 +41,14 @@ namespace ARMeilleure.Translation.PTC
private const byte FillingByte = 0x00; private const byte FillingByte = 0x00;
private const CompressionLevel SaveCompressionLevel = CompressionLevel.Fastest; private const CompressionLevel SaveCompressionLevel = CompressionLevel.Fastest;
private static readonly MemoryStream _infosStream; private static MemoryStream _infosStream;
private static readonly MemoryStream _codesStream; private static MemoryStream _codesStream;
private static readonly MemoryStream _relocsStream; private static MemoryStream _relocsStream;
private static readonly MemoryStream _unwindInfosStream; private static MemoryStream _unwindInfosStream;
private static readonly BinaryWriter _infosWriter; private static BinaryWriter _infosWriter;
private static readonly ulong _headerMagic;
private static readonly ManualResetEvent _waitEvent; private static readonly ManualResetEvent _waitEvent;
@ -66,12 +72,9 @@ namespace ARMeilleure.Translation.PTC
static Ptc() static Ptc()
{ {
_infosStream = new MemoryStream(); InitializeMemoryStreams();
_codesStream = new MemoryStream();
_relocsStream = new MemoryStream();
_unwindInfosStream = new MemoryStream();
_infosWriter = new BinaryWriter(_infosStream, EncodingCache.UTF8NoBOM, true); _headerMagic = BinaryPrimitives.ReadUInt64LittleEndian(EncodingCache.UTF8NoBOM.GetBytes(HeaderMagicString).AsSpan());
_waitEvent = new ManualResetEvent(true); _waitEvent = new ManualResetEvent(true);
@ -95,13 +98,13 @@ namespace ARMeilleure.Translation.PTC
public static void Initialize(string titleIdText, string displayVersion, bool enabled) public static void Initialize(string titleIdText, string displayVersion, bool enabled)
{ {
Wait(); Wait();
ClearMemoryStreams();
PtcJumpTable.Clear();
PtcProfiler.Wait(); PtcProfiler.Wait();
PtcProfiler.ClearEntries(); 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; TitleIdText = TitleIdTextDefault;
DisplayVersion = DisplayVersionDefault; DisplayVersion = DisplayVersionDefault;
@ -114,55 +117,72 @@ namespace ARMeilleure.Translation.PTC
return; return;
} }
Logger.Info?.Print(LogClass.Ptc, $"Initializing Profiled Persistent Translation Cache (enabled: {enabled}).");
TitleIdText = titleIdText; 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); Directory.CreateDirectory(workPathActual);
string workPathBackup = Path.Combine(AppDataManager.GamesDirPath, TitleIdText, "cache", "cpu", BackupDir);
if (!Directory.Exists(workPathActual))
{
Directory.CreateDirectory(workPathActual);
}
if (!Directory.Exists(workPathBackup))
{
Directory.CreateDirectory(workPathBackup);
}
CachePathActual = Path.Combine(workPathActual, DisplayVersion);
CachePathBackup = Path.Combine(workPathBackup, DisplayVersion);
Enable();
PreLoad();
PtcProfiler.PreLoad();
} }
else
if (!Directory.Exists(workPathBackup))
{ {
CachePathActual = string.Empty; Directory.CreateDirectory(workPathBackup);
CachePathBackup = string.Empty;
Disable();
} }
CachePathActual = Path.Combine(workPathActual, DisplayVersion);
CachePathBackup = Path.Combine(workPathBackup, DisplayVersion);
PreLoad();
PtcProfiler.PreLoad();
Enable();
} }
internal static void ClearMemoryStreams() private static void InitializeMemoryStreams()
{ {
_infosStream.SetLength(0L); _infosStream = new MemoryStream();
_codesStream.SetLength(0L); _codesStream = new MemoryStream();
_relocsStream.SetLength(0L); _relocsStream = new MemoryStream();
_unwindInfosStream.SetLength(0L); _unwindInfosStream = new MemoryStream();
_infosWriter = new BinaryWriter(_infosStream, EncodingCache.UTF8NoBOM, true);
}
private static void DisposeMemoryStreams()
{
_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() private static void PreLoad()
{ {
string fileNameActual = String.Concat(CachePathActual, ".cache"); string fileNameActual = string.Concat(CachePathActual, ".cache");
string fileNameBackup = String.Concat(CachePathBackup, ".cache"); string fileNameBackup = string.Concat(CachePathBackup, ".cache");
FileInfo fileInfoActual = new FileInfo(fileNameActual); FileInfo fileInfoActual = new FileInfo(fileNameActual);
FileInfo fileInfoBackup = new FileInfo(fileNameBackup); 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 (FileStream compressedStream = new(fileName, FileMode.Open))
using (DeflateStream deflateStream = new DeflateStream(compressedStream, CompressionMode.Decompress, true)) using (DeflateStream deflateStream = new(compressedStream, CompressionMode.Decompress, true))
using (MemoryStream stream = new MemoryStream())
using (MD5 md5 = MD5.Create())
{ {
int hashSize = md5.HashSize / 8; Hash128 currentSizeHash = DeserializeStructure<Hash128>(compressedStream);
Span<byte> sizeBytes = new byte[sizeof(int)];
compressedStream.Read(sizeBytes);
Hash128 expectedSizeHash = XXHash128.ComputeHash(sizeBytes);
if (currentSizeHash != expectedSizeHash)
{
InvalidateCompressedStream(compressedStream);
return false;
}
int size = BinaryPrimitives.ReadInt32LittleEndian(sizeBytes);
IntPtr intPtr = IntPtr.Zero;
try try
{ {
deflateStream.CopyTo(stream); intPtr = Marshal.AllocHGlobal(size);
using (UnmanagedMemoryStream stream = new((byte*)intPtr.ToPointer(), size, size, FileAccess.ReadWrite))
{
try
{
deflateStream.CopyTo(stream);
}
catch
{
InvalidateCompressedStream(compressedStream);
return false;
}
int hashSize = Unsafe.SizeOf<Hash128>();
stream.Seek(0L, SeekOrigin.Begin);
Hash128 currentHash = DeserializeStructure<Hash128>(stream);
ReadOnlySpan<byte> streamBytes = new(stream.PositionPointer, (int)(stream.Length - stream.Position));
Hash128 expectedHash = XXHash128.ComputeHash(streamBytes);
if (currentHash != expectedHash)
{
InvalidateCompressedStream(compressedStream);
return false;
}
stream.Seek((long)hashSize, SeekOrigin.Begin);
Header header = ReadHeader(stream);
if (header.Magic != _headerMagic)
{
InvalidateCompressedStream(compressedStream);
return false;
}
if (header.CacheFileVersion != InternalVersion)
{
InvalidateCompressedStream(compressedStream);
return false;
}
if (header.Endianness != GetEndianness())
{
InvalidateCompressedStream(compressedStream);
return false;
}
if (header.FeatureInfo != GetFeatureInfo())
{
InvalidateCompressedStream(compressedStream);
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);
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);
}
} }
catch finally
{ {
InvalidateCompressedStream(compressedStream); if (intPtr != IntPtr.Zero)
{
return false; Marshal.FreeHGlobal(intPtr);
}
} }
stream.Seek(0L, SeekOrigin.Begin);
byte[] currentHash = new byte[hashSize];
stream.Read(currentHash, 0, hashSize);
byte[] expectedHash = md5.ComputeHash(stream);
if (!CompareHash(currentHash, expectedHash))
{
InvalidateCompressedStream(compressedStream);
return false;
}
stream.Seek((long)hashSize, SeekOrigin.Begin);
Header header = ReadHeader(stream);
if (header.Magic != HeaderMagic)
{
InvalidateCompressedStream(compressedStream);
return false;
}
if (header.CacheFileVersion != InternalVersion)
{
InvalidateCompressedStream(compressedStream);
return false;
}
if (header.FeatureInfo != GetFeatureInfo())
{
InvalidateCompressedStream(compressedStream);
return false;
}
if (header.OSPlatform != GetOSPlatform())
{
InvalidateCompressedStream(compressedStream);
return false;
}
if (header.InfosLen % InfoEntry.Stride != 0)
{
InvalidateCompressedStream(compressedStream);
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];
stream.Read(infosBuf, 0, header.InfosLen);
stream.Read(codesBuf, 0, header.CodesLen);
stream.Read(relocsBuf, 0, header.RelocsLen);
stream.Read(unwindInfosBuf, 0, header.UnwindInfosLen);
try
{
PtcJumpTable = PtcJumpTable.Deserialize(stream);
}
catch
{
PtcJumpTable = new PtcJumpTable();
InvalidateCompressedStream(compressedStream);
return false;
}
_infosStream.Write(infosBuf, 0, header.InfosLen);
_codesStream.Write(codesBuf, 0, header.CodesLen);
_relocsStream.Write(relocsBuf, 0, header.RelocsLen);
_unwindInfosStream.Write(unwindInfosBuf, 0, header.UnwindInfosLen);
} }
long fileSize = new FileInfo(fileName).Length; long fileSize = new FileInfo(fileName).Length;
@ -292,20 +350,16 @@ namespace ARMeilleure.Translation.PTC
return true; return true;
} }
private static bool CompareHash(ReadOnlySpan<byte> currentHash, ReadOnlySpan<byte> expectedHash) private static Header ReadHeader(Stream stream)
{ {
return currentHash.SequenceEqual(expectedHash); using (BinaryReader headerReader = new(stream, EncodingCache.UTF8NoBOM, true))
}
private static Header ReadHeader(MemoryStream stream)
{
using (BinaryReader headerReader = new BinaryReader(stream, EncodingCache.UTF8NoBOM, true))
{ {
Header header = new Header(); Header header = new Header();
header.Magic = headerReader.ReadString(); header.Magic = headerReader.ReadUInt64();
header.CacheFileVersion = headerReader.ReadUInt32(); header.CacheFileVersion = headerReader.ReadUInt32();
header.Endianness = headerReader.ReadBoolean();
header.FeatureInfo = headerReader.ReadUInt64(); header.FeatureInfo = headerReader.ReadUInt64();
header.OSPlatform = headerReader.ReadUInt32(); header.OSPlatform = headerReader.ReadUInt32();
@ -323,87 +377,133 @@ namespace ARMeilleure.Translation.PTC
compressedStream.SetLength(0L); compressedStream.SetLength(0L);
} }
private static void PreSave(object state) private static void PreSave()
{ {
_waitEvent.Reset(); _waitEvent.Reset();
string fileNameActual = String.Concat(CachePathActual, ".cache"); try
string fileNameBackup = String.Concat(CachePathBackup, ".cache");
FileInfo fileInfoActual = new FileInfo(fileNameActual);
if (fileInfoActual.Exists && fileInfoActual.Length != 0L)
{ {
File.Copy(fileNameActual, fileNameBackup, true); string fileNameActual = string.Concat(CachePathActual, ".cache");
} string fileNameBackup = string.Concat(CachePathBackup, ".cache");
Save(fileNameActual); FileInfo fileInfoActual = new FileInfo(fileNameActual);
if (fileInfoActual.Exists && fileInfoActual.Length != 0L)
{
File.Copy(fileNameActual, fileNameBackup, true);
}
Save(fileNameActual);
}
finally
{
ResetMemoryStreamsIfNeeded();
PtcJumpTable.ClearIfNeeded();
GCSettings.LargeObjectHeapCompactionMode = GCLargeObjectHeapCompactionMode.CompactOnce;
}
_waitEvent.Set(); _waitEvent.Set();
} }
private static void Save(string fileName) private static unsafe void Save(string fileName)
{ {
int translatedFuncsCount; int translatedFuncsCount;
using (MemoryStream stream = new MemoryStream()) int hashSize = Unsafe.SizeOf<Hash128>();
using (MD5 md5 = MD5.Create())
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))
WriteHeader(stream);
_infosStream.WriteTo(stream);
_codesStream.WriteTo(stream);
_relocsStream.WriteTo(stream);
_unwindInfosStream.WriteTo(stream);
PtcJumpTable.Serialize(stream, PtcJumpTable);
translatedFuncsCount = GetInfosEntriesCount();
ClearMemoryStreams();
PtcJumpTable.Clear();
stream.Seek((long)hashSize, SeekOrigin.Begin);
byte[] hash = md5.ComputeHash(stream);
stream.Seek(0L, SeekOrigin.Begin);
stream.Write(hash, 0, hashSize);
using (FileStream compressedStream = new FileStream(fileName, FileMode.OpenOrCreate))
using (DeflateStream deflateStream = new DeflateStream(compressedStream, SaveCompressionLevel, true))
{ {
try stream.Seek((long)hashSize, SeekOrigin.Begin);
{
stream.WriteTo(deflateStream);
}
catch
{
compressedStream.Position = 0L;
}
if (compressedStream.Position < compressedStream.Length) WriteHeader(stream);
_infosStream.WriteTo(stream);
_codesStream.WriteTo(stream);
_relocsStream.WriteTo(stream);
_unwindInfosStream.WriteTo(stream);
PtcJumpTable.Serialize(stream, PtcJumpTable);
stream.Seek((long)hashSize, SeekOrigin.Begin);
ReadOnlySpan<byte> streamBytes = new(stream.PositionPointer, (int)(stream.Length - stream.Position));
Hash128 hash = XXHash128.ComputeHash(streamBytes);
stream.Seek(0L, SeekOrigin.Begin);
SerializeStructure(stream, hash);
translatedFuncsCount = GetInfosEntriesCount();
ResetMemoryStreamsIfNeeded();
PtcJumpTable.ClearIfNeeded();
using (FileStream compressedStream = new(fileName, FileMode.OpenOrCreate))
using (DeflateStream deflateStream = new(compressedStream, SaveCompressionLevel, true))
{ {
compressedStream.SetLength(compressedStream.Position); try
{
compressedStream.Write(sizeHashBytes);
compressedStream.Write(sizeBytes);
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; 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 void WriteHeader(MemoryStream stream) private static int GetMemoryStreamsLength()
{ {
using (BinaryWriter headerWriter = new BinaryWriter(stream, EncodingCache.UTF8NoBOM, true)) return (int)_infosStream.Length + (int)_codesStream.Length + (int)_relocsStream.Length + (int)_unwindInfosStream.Length;
}
private static void WriteHeader(Stream stream)
{
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((uint)InternalVersion); // Header.CacheFileVersion
headerWriter.Write((bool)GetEndianness()); // Header.Endianness
headerWriter.Write((ulong)GetFeatureInfo()); // Header.FeatureInfo headerWriter.Write((ulong)GetFeatureInfo()); // Header.FeatureInfo
headerWriter.Write((uint)GetOSPlatform()); // Header.OSPlatform 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) internal static void LoadTranslations(ConcurrentDictionary<ulong, TranslatedFunction> funcs, IMemoryManager memory, JumpTable jumpTable)
{ {
if ((int)_infosStream.Length == 0 || if (AreMemoryStreamsEmpty())
(int)_codesStream.Length == 0 ||
(int)_relocsStream.Length == 0 ||
(int)_unwindInfosStream.Length == 0)
{ {
return; return;
} }
@ -431,10 +528,10 @@ namespace ARMeilleure.Translation.PTC
_relocsStream.Seek(0L, SeekOrigin.Begin); _relocsStream.Seek(0L, SeekOrigin.Begin);
_unwindInfosStream.Seek(0L, SeekOrigin.Begin); _unwindInfosStream.Seek(0L, SeekOrigin.Begin);
using (BinaryReader infosReader = new BinaryReader(_infosStream, EncodingCache.UTF8NoBOM, true)) using (BinaryReader infosReader = new(_infosStream, EncodingCache.UTF8NoBOM, true))
using (BinaryReader codesReader = new BinaryReader(_codesStream, EncodingCache.UTF8NoBOM, true)) using (BinaryReader codesReader = new(_codesStream, EncodingCache.UTF8NoBOM, true))
using (BinaryReader relocsReader = new BinaryReader(_relocsStream, EncodingCache.UTF8NoBOM, true)) using (BinaryReader relocsReader = new(_relocsStream, EncodingCache.UTF8NoBOM, true))
using (BinaryReader unwindInfosReader = new BinaryReader(_unwindInfosStream, EncodingCache.UTF8NoBOM, true)) using (BinaryReader unwindInfosReader = new(_unwindInfosStream, EncodingCache.UTF8NoBOM, true))
{ {
for (int i = 0; i < GetInfosEntriesCount(); i++) for (int i = 0; i < GetInfosEntriesCount(); i++)
{ {
@ -446,9 +543,9 @@ namespace ARMeilleure.Translation.PTC
SkipReloc(infoEntry.RelocEntriesCount); SkipReloc(infoEntry.RelocEntriesCount);
SkipUnwindInfo(unwindInfosReader); 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) if (infoEntry.RelocEntriesCount != 0)
{ {
@ -529,11 +626,11 @@ namespace ARMeilleure.Translation.PTC
_unwindInfosStream.Seek(pushEntriesLength * UnwindPushEntry.Stride + UnwindInfo.Stride, SeekOrigin.Current); _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; return codeBuf;
} }
@ -605,9 +702,9 @@ namespace ARMeilleure.Translation.PTC
return new UnwindInfo(pushEntries, prologueSize); 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); IntPtr codePtr = JitCache.Map(cFunc);
@ -663,6 +760,11 @@ namespace ARMeilleure.Translation.PTC
if (profiledFuncsToTranslate.Count == 0) if (profiledFuncsToTranslate.Count == 0)
{ {
ResetMemoryStreamsIfNeeded();
PtcJumpTable.ClearIfNeeded();
GCSettings.LargeObjectHeapCompactionMode = GCLargeObjectHeapCompactionMode.CompactOnce;
return; return;
} }
@ -696,6 +798,8 @@ namespace ARMeilleure.Translation.PTC
break; break;
} }
} }
Translator.DisposePools();
} }
int maxDegreeOfParallelism = (Environment.ProcessorCount * 3) / 4; int maxDegreeOfParallelism = (Environment.ProcessorCount * 3) / 4;
@ -715,8 +819,6 @@ namespace ARMeilleure.Translation.PTC
threads.Clear(); threads.Clear();
Translator.ResetPools();
_loggerEvent.Set(); _loggerEvent.Set();
PtcJumpTable.Initialize(jumpTable); PtcJumpTable.Initialize(jumpTable);
@ -724,7 +826,9 @@ namespace ARMeilleure.Translation.PTC
PtcJumpTable.ReadJumpTable(jumpTable); PtcJumpTable.ReadJumpTable(jumpTable);
PtcJumpTable.ReadDynamicTable(jumpTable); PtcJumpTable.ReadDynamicTable(jumpTable);
ThreadPool.QueueUserWorkItem(PreSave); Thread preSaveThread = new Thread(PreSave);
preSaveThread.IsBackground = true;
preSaveThread.Start();
} }
private static void TranslationLogger(object state) private static void TranslationLogger(object state)
@ -751,11 +855,11 @@ namespace ARMeilleure.Translation.PTC
_infosWriter.Write((ulong)guestSize); // InfoEntry.GuestSize _infosWriter.Write((ulong)guestSize); // InfoEntry.GuestSize
_infosWriter.Write((bool)highCq); // InfoEntry.HighCq _infosWriter.Write((bool)highCq); // InfoEntry.HighCq
_infosWriter.Write((bool)false); // InfoEntry.Stubbed _infosWriter.Write((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 _infosWriter.Write((int)ptcInfo.RelocEntriesCount); // InfoEntry.RelocEntriesCount
// WriteCode. // WriteCode.
ptcInfo.CodeStream.WriteTo(_codesStream); _codesStream.Write(ptcInfo.Code.AsSpan());
// WriteReloc. // WriteReloc.
ptcInfo.RelocStream.WriteTo(_relocsStream); ptcInfo.RelocStream.WriteTo(_relocsStream);
@ -765,6 +869,11 @@ namespace ARMeilleure.Translation.PTC
} }
} }
private static bool GetEndianness()
{
return BitConverter.IsLittleEndian;
}
private static ulong GetFeatureInfo() private static ulong GetFeatureInfo()
{ {
return (ulong)HardwareCapabilities.FeatureInfoEdx << 32 | (uint)HardwareCapabilities.FeatureInfoEcx; return (ulong)HardwareCapabilities.FeatureInfoEdx << 32 | (uint)HardwareCapabilities.FeatureInfoEcx;
@ -784,9 +893,12 @@ namespace ARMeilleure.Translation.PTC
private struct Header private struct Header
{ {
public string Magic; public const int Size = 41; // Bytes.
public ulong Magic;
public uint CacheFileVersion; public uint CacheFileVersion;
public bool Endianness;
public ulong FeatureInfo; public ulong FeatureInfo;
public uint OSPlatform; public uint OSPlatform;
@ -849,12 +961,9 @@ namespace ARMeilleure.Translation.PTC
Wait(); Wait();
_waitEvent.Dispose(); _waitEvent.Dispose();
_infosWriter.Dispose(); _loggerEvent.Dispose();
_infosStream.Dispose(); DisposeMemoryStreams();
_codesStream.Dispose();
_relocsStream.Dispose();
_unwindInfosStream.Dispose();
} }
} }
} }

View file

@ -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
}
}

View file

@ -9,7 +9,8 @@ namespace ARMeilleure.Translation.PTC
private readonly BinaryWriter _relocWriter; private readonly BinaryWriter _relocWriter;
private readonly BinaryWriter _unwindInfoWriter; private readonly BinaryWriter _unwindInfoWriter;
public MemoryStream CodeStream { get; } public byte[] Code { get; set; }
public MemoryStream RelocStream { get; } public MemoryStream RelocStream { get; }
public MemoryStream UnwindInfoStream { get; } public MemoryStream UnwindInfoStream { get; }
@ -17,7 +18,6 @@ namespace ARMeilleure.Translation.PTC
public PtcInfo() public PtcInfo()
{ {
CodeStream = new MemoryStream();
RelocStream = new MemoryStream(); RelocStream = new MemoryStream();
UnwindInfoStream = new MemoryStream(); UnwindInfoStream = new MemoryStream();
@ -27,11 +27,6 @@ namespace ARMeilleure.Translation.PTC
RelocEntriesCount = 0; RelocEntriesCount = 0;
} }
public void WriteCode(MemoryStream codeStream)
{
codeStream.WriteTo(CodeStream);
}
public void WriteRelocEntry(RelocEntry relocEntry) public void WriteRelocEntry(RelocEntry relocEntry)
{ {
_relocWriter.Write((int)relocEntry.Position); _relocWriter.Write((int)relocEntry.Position);
@ -60,7 +55,6 @@ namespace ARMeilleure.Translation.PTC
_relocWriter.Dispose(); _relocWriter.Dispose();
_unwindInfoWriter.Dispose(); _unwindInfoWriter.Dispose();
CodeStream.Dispose();
RelocStream.Dispose(); RelocStream.Dispose();
UnwindInfoStream.Dispose(); UnwindInfoStream.Dispose();
} }

View file

@ -6,15 +6,18 @@ using System.Diagnostics;
using System.IO; using System.IO;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using static ARMeilleure.Translation.PTC.PtcFormatter;
namespace ARMeilleure.Translation.PTC namespace ARMeilleure.Translation.PTC
{ {
class PtcJumpTable class PtcJumpTable
{ {
[StructLayout(LayoutKind.Sequential, Pack = 1/*, Size = 16*/)]
public struct TableEntry<TAddress> public struct TableEntry<TAddress>
{ {
public int EntryIndex; public int EntryIndex;
public long GuestAddress; public long GuestAddress;
public TAddress HostAddress; public TAddress HostAddress; // int
public TableEntry(int entryIndex, long guestAddress, TAddress hostAddress) 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, CallStub = 0,
TailCallStub = 1, TailCallStub = 1,
Host = 2 Host = 2
} }
public enum IndirectHostAddress public enum IndirectHostAddress : int
{ {
CallStub = 0, CallStub = 0,
TailCallStub = 1 TailCallStub = 1
@ -66,152 +69,40 @@ namespace ARMeilleure.Translation.PTC
Owners = owners; 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 = DeserializeList<TableEntry<DirectHostAddress>>(stream);
{ var dynamicTable = DeserializeList<TableEntry<IndirectHostAddress>>(stream);
var jumpTable = new List<TableEntry<DirectHostAddress>>();
int jumpTableCount = 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 i = 0; i < jumpTableCount; i++) return new PtcJumpTable(jumpTable, dynamicTable, targets, dependants, owners);
{
int entryIndex = reader.ReadInt32();
long guestAddress = reader.ReadInt64();
DirectHostAddress hostAddress = (DirectHostAddress)reader.ReadInt32();
jumpTable.Add(new TableEntry<DirectHostAddress>(entryIndex, guestAddress, hostAddress));
}
var dynamicTable = new List<TableEntry<IndirectHostAddress>>();
int dynamicTableCount = reader.ReadInt32();
for (int i = 0; i < dynamicTableCount; i++)
{
int entryIndex = reader.ReadInt32();
long guestAddress = reader.ReadInt64();
IndirectHostAddress hostAddress = (IndirectHostAddress)reader.ReadInt32();
dynamicTable.Add(new TableEntry<IndirectHostAddress>(entryIndex, guestAddress, hostAddress));
}
var targets = new List<ulong>();
int targetsCount = reader.ReadInt32();
for (int i = 0; i < targetsCount; i++)
{
ulong address = reader.ReadUInt64();
targets.Add(address);
}
var dependants = new Dictionary<ulong, List<int>>();
int dependantsCount = reader.ReadInt32();
for (int i = 0; i < dependantsCount; i++)
{
ulong address = reader.ReadUInt64();
var entries = new List<int>();
int entriesCount = reader.ReadInt32();
for (int j = 0; j < entriesCount; j++)
{
int entry = reader.ReadInt32();
entries.Add(entry);
}
dependants.Add(address, entries);
}
var owners = new Dictionary<ulong, List<int>>();
int ownersCount = reader.ReadInt32();
for (int i = 0; i < ownersCount; i++)
{
ulong address = reader.ReadUInt64();
var entries = new List<int>();
int entriesCount = reader.ReadInt32();
for (int j = 0; j < entriesCount; j++)
{
int entry = reader.ReadInt32();
entries.Add(entry);
}
owners.Add(address, entries);
}
return new PtcJumpTable(jumpTable, dynamicTable, targets, dependants, owners);
}
} }
public static void Serialize(MemoryStream stream, PtcJumpTable ptcJumpTable) public static int GetSerializeSize(PtcJumpTable ptcJumpTable)
{ {
using (BinaryWriter writer = new BinaryWriter(stream, EncodingCache.UTF8NoBOM, true)) int size = 0;
{
writer.Write((int)ptcJumpTable._jumpTable.Count);
foreach (var tableEntry in ptcJumpTable._jumpTable) size += GetSerializeSizeList(ptcJumpTable._jumpTable);
{ size += GetSerializeSizeList(ptcJumpTable._dynamicTable);
writer.Write((int)tableEntry.EntryIndex);
writer.Write((long)tableEntry.GuestAddress);
writer.Write((int)tableEntry.HostAddress);
}
writer.Write((int)ptcJumpTable._dynamicTable.Count); size += GetSerializeSizeList(ptcJumpTable.Targets);
size += GetSerializeSizeDictionary(ptcJumpTable.Dependants, (list) => GetSerializeSizeList(list));
size += GetSerializeSizeDictionary(ptcJumpTable.Owners, (list) => GetSerializeSizeList(list));
foreach (var tableEntry in ptcJumpTable._dynamicTable) return size;
{ }
writer.Write((int)tableEntry.EntryIndex);
writer.Write((long)tableEntry.GuestAddress);
writer.Write((int)tableEntry.HostAddress);
}
writer.Write((int)ptcJumpTable.Targets.Count); public static void Serialize(Stream stream, PtcJumpTable ptcJumpTable)
{
SerializeList(stream, ptcJumpTable._jumpTable);
SerializeList(stream, ptcJumpTable._dynamicTable);
foreach (ulong address in ptcJumpTable.Targets) SerializeList(stream, ptcJumpTable.Targets);
{ SerializeDictionary(stream, ptcJumpTable.Dependants, (stream, list) => SerializeList(stream, list));
writer.Write((ulong)address); SerializeDictionary(stream, ptcJumpTable.Owners, (stream, list) => SerializeList(stream, list));
}
writer.Write((int)ptcJumpTable.Dependants.Count);
foreach (var kv in ptcJumpTable.Dependants)
{
writer.Write((ulong)kv.Key); // address
writer.Write((int)kv.Value.Count);
foreach (int entry in kv.Value)
{
writer.Write((int)entry);
}
}
writer.Write((int)ptcJumpTable.Owners.Count);
foreach (var kv in ptcJumpTable.Owners)
{
writer.Write((ulong)kv.Key); // address
writer.Write((int)kv.Value.Count);
foreach (int entry in kv.Value)
{
writer.Write((int)entry);
}
}
}
} }
public void Initialize(JumpTable jumpTable) public void Initialize(JumpTable jumpTable)
@ -270,14 +161,25 @@ namespace ARMeilleure.Translation.PTC
Owners.Remove(guestAddress); 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.Clear();
_jumpTable.TrimExcess();
_dynamicTable.Clear(); _dynamicTable.Clear();
_dynamicTable.TrimExcess();
Targets.Clear(); Targets.Clear();
Targets.TrimExcess();
Dependants.Clear(); Dependants.Clear();
Dependants.TrimExcess();
Owners.Clear(); Owners.Clear();
Owners.TrimExcess();
} }
public void WriteJumpTable(JumpTable jumpTable, ConcurrentDictionary<ulong, TranslatedFunction> funcs) public void WriteJumpTable(JumpTable jumpTable, ConcurrentDictionary<ulong, TranslatedFunction> funcs)
@ -307,7 +209,7 @@ namespace ARMeilleure.Translation.PTC
} }
else 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})"); throw new KeyNotFoundException($"({nameof(guestAddress)} = 0x{(ulong)guestAddress:X16})");
} }

View file

@ -6,9 +6,12 @@ using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.IO; using System.IO;
using System.IO.Compression; using System.IO.Compression;
using System.Runtime.InteropServices;
using System.Security.Cryptography; using System.Security.Cryptography;
using System.Threading; using System.Threading;
using static ARMeilleure.Translation.PTC.PtcFormatter;
namespace ARMeilleure.Translation.PTC namespace ARMeilleure.Translation.PTC
{ {
public static class PtcProfiler public static class PtcProfiler
@ -29,7 +32,9 @@ namespace ARMeilleure.Translation.PTC
private static bool _disposed; 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; } internal static bool Enabled { get; private set; }
@ -47,7 +52,7 @@ namespace ARMeilleure.Translation.PTC
_disposed = false; _disposed = false;
ProfiledFuncs = new Dictionary<ulong, (ExecutionMode, bool)>(); ProfiledFuncs = new Dictionary<ulong, FuncProfile>();
Enabled = false; Enabled = false;
} }
@ -60,7 +65,7 @@ namespace ARMeilleure.Translation.PTC
lock (_lock) 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)); 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)) 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() internal static void ClearEntries()
{ {
ProfiledFuncs.Clear(); ProfiledFuncs.Clear();
ProfiledFuncs.TrimExcess();
} }
internal static void PreLoad() internal static void PreLoad()
{ {
string fileNameActual = String.Concat(Ptc.CachePathActual, ".info"); _lastHash = Array.Empty<byte>();
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); FileInfo fileInfoActual = new FileInfo(fileNameActual);
FileInfo fileInfoBackup = new FileInfo(fileNameBackup); FileInfo fileInfoBackup = new FileInfo(fileNameBackup);
@ -138,8 +146,6 @@ namespace ARMeilleure.Translation.PTC
using (MemoryStream stream = new MemoryStream()) using (MemoryStream stream = new MemoryStream())
using (MD5 md5 = MD5.Create()) using (MD5 md5 = MD5.Create())
{ {
int hashSize = md5.HashSize / 8;
try try
{ {
deflateStream.CopyTo(stream); deflateStream.CopyTo(stream);
@ -151,6 +157,8 @@ namespace ARMeilleure.Translation.PTC
return false; return false;
} }
int hashSize = md5.HashSize / 8;
stream.Seek(0L, SeekOrigin.Begin); stream.Seek(0L, SeekOrigin.Begin);
byte[] currentHash = new byte[hashSize]; byte[] currentHash = new byte[hashSize];
@ -189,12 +197,14 @@ namespace ARMeilleure.Translation.PTC
} }
catch catch
{ {
ProfiledFuncs = new Dictionary<ulong, (ExecutionMode, bool)>(); ProfiledFuncs = new Dictionary<ulong, FuncProfile>();
InvalidateCompressedStream(compressedStream); InvalidateCompressedStream(compressedStream);
return false; return false;
} }
_lastHash = expectedHash;
} }
long fileSize = new FileInfo(fileName).Length; long fileSize = new FileInfo(fileName).Length;
@ -209,7 +219,7 @@ namespace ARMeilleure.Translation.PTC
return currentHash.SequenceEqual(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 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)) return DeserializeDictionary<ulong, FuncProfile>(stream, (stream) => DeserializeStructure<FuncProfile>(stream));
{
var profiledFuncs = new Dictionary<ulong, (ExecutionMode, bool)>();
int profiledFuncsCount = reader.ReadInt32();
for (int i = 0; i < profiledFuncsCount; i++)
{
ulong address = reader.ReadUInt64();
ExecutionMode mode = (ExecutionMode)reader.ReadInt32();
bool highCq = reader.ReadBoolean();
profiledFuncs.Add(address, (mode, highCq));
}
return profiledFuncs;
}
} }
private static void InvalidateCompressedStream(FileStream compressedStream) private static void InvalidateCompressedStream(FileStream compressedStream)
@ -254,8 +247,8 @@ namespace ARMeilleure.Translation.PTC
{ {
_waitEvent.Reset(); _waitEvent.Reset();
string fileNameActual = String.Concat(Ptc.CachePathActual, ".info"); string fileNameActual = string.Concat(Ptc.CachePathActual, ".info");
string fileNameBackup = String.Concat(Ptc.CachePathBackup, ".info"); string fileNameBackup = string.Concat(Ptc.CachePathBackup, ".info");
FileInfo fileInfoActual = new FileInfo(fileNameActual); FileInfo fileInfoActual = new FileInfo(fileNameActual);
@ -295,16 +288,25 @@ namespace ARMeilleure.Translation.PTC
stream.Seek(0L, SeekOrigin.Begin); stream.Seek(0L, SeekOrigin.Begin);
stream.Write(hash, 0, hashSize); stream.Write(hash, 0, hashSize);
if (CompareHash(hash, _lastHash))
{
return;
}
using (FileStream compressedStream = new FileStream(fileName, FileMode.OpenOrCreate)) using (FileStream compressedStream = new FileStream(fileName, FileMode.OpenOrCreate))
using (DeflateStream deflateStream = new DeflateStream(compressedStream, SaveCompressionLevel, true)) using (DeflateStream deflateStream = new DeflateStream(compressedStream, SaveCompressionLevel, true))
{ {
try try
{ {
stream.WriteTo(deflateStream); stream.WriteTo(deflateStream);
_lastHash = hash;
} }
catch catch
{ {
compressedStream.Position = 0L; compressedStream.Position = 0L;
_lastHash = Array.Empty<byte>();
} }
if (compressedStream.Position < compressedStream.Length) if (compressedStream.Position < compressedStream.Length)
@ -316,10 +318,13 @@ namespace ARMeilleure.Translation.PTC
long fileSize = new FileInfo(fileName).Length; 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)) 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)) SerializeDictionary(stream, profiledFuncs, (stream, structure) => SerializeStructure(stream, structure));
{
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 private struct Header
@ -352,6 +346,19 @@ namespace ARMeilleure.Translation.PTC
public uint InfoFileVersion; 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() internal static void Start()
{ {
if (Ptc.State == PtcState.Enabled || if (Ptc.State == PtcState.Enabled ||

View file

@ -11,8 +11,10 @@ using System.Collections.Concurrent;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.Linq; using System.Linq;
using System.Runtime;
using System.Threading; using System.Threading;
using static ARMeilleure.Common.BitMapPool;
using static ARMeilleure.IntermediateRepresentation.OperandHelper; using static ARMeilleure.IntermediateRepresentation.OperandHelper;
using static ARMeilleure.IntermediateRepresentation.OperationHelper; using static ARMeilleure.IntermediateRepresentation.OperationHelper;
@ -148,10 +150,12 @@ namespace ARMeilleure.Translation
ClearJitCache(); ClearJitCache();
ResetPools(); DisposePools();
_jumpTable.Dispose(); _jumpTable.Dispose();
_jumpTable = null; _jumpTable = null;
GCSettings.LargeObjectHeapCompactionMode = GCLargeObjectHeapCompactionMode.CompactOnce;
} }
} }
@ -207,7 +211,7 @@ namespace ARMeilleure.Translation
Logger.EndPass(PassName.Decoding); Logger.EndPass(PassName.Decoding);
PreparePool(highCq); PreparePool(highCq ? 1 : 0);
Logger.StartPass(PassName.Translation); Logger.StartPass(PassName.Translation);
@ -240,13 +244,15 @@ namespace ARMeilleure.Translation
{ {
func = Compiler.Compile<GuestFunction>(cfg, argTypes, OperandType.I64, options); 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); func = Compiler.Compile<GuestFunction>(cfg, argTypes, OperandType.I64, options, ptcInfo);
ReturnPool(highCq); ResetPool(highCq ? 1 : 0);
Ptc.WriteInfoCodeRelocUnwindInfo(address, funcSize, highCq, ptcInfo); Ptc.WriteInfoCodeRelocUnwindInfo(address, funcSize, highCq, ptcInfo);
} }
@ -254,22 +260,23 @@ namespace ARMeilleure.Translation
return new TranslatedFunction(func, funcSize, highCq); return new TranslatedFunction(func, funcSize, highCq);
} }
internal static void PreparePool(bool highCq) internal static void PreparePool(int groupId = 0)
{ {
PrepareOperandPool(highCq); PrepareOperandPool(groupId);
PrepareOperationPool(highCq); PrepareOperationPool(groupId);
} }
internal static void ReturnPool(bool highCq) internal static void ResetPool(int groupId = 0)
{ {
ReturnOperandPool(highCq); ResetOperationPool(groupId);
ReturnOperationPool(highCq); ResetOperandPool(groupId);
} }
internal static void ResetPools() internal static void DisposePools()
{ {
ResetOperandPools(); DisposeOperandPools();
ResetOperationPools(); DisposeOperationPools();
DisposeBitMapPools();
} }
private struct Range private struct Range

View file

@ -40,8 +40,6 @@ namespace Ryujinx.Memory
} }
Size = size; Size = size;
GC.AddMemoryPressure((long)Size);
} }
/// <summary> /// <summary>
@ -283,8 +281,6 @@ namespace Ryujinx.Memory
if (ptr != IntPtr.Zero) if (ptr != IntPtr.Zero)
{ {
MemoryManagement.Free(ptr); MemoryManagement.Free(ptr);
GC.RemoveMemoryPressure((long)Size);
} }
} }