PPTC & Pool Enhancements.

This commit is contained in:
LDj3SNuD 2021-01-27 06:21:37 +01:00
parent c4f56c5704
commit d31479a633
15 changed files with 624 additions and 406 deletions

View file

@ -50,6 +50,8 @@ namespace ARMeilleure.CodeGen.RegisterAllocators
StackAlloc = stackAlloc;
Masks = masks;
BitMapPool.PrepareBitMapPool();
Active = BitMapPool.Allocate(intervalsCount);
Inactive = BitMapPool.Allocate(intervalsCount);
}
@ -73,7 +75,7 @@ namespace ARMeilleure.CodeGen.RegisterAllocators
public void Dispose()
{
BitMapPool.Release();
BitMapPool.ResetBitMapPool();
}
}
@ -84,57 +86,51 @@ namespace ARMeilleure.CodeGen.RegisterAllocators
{
NumberLocals(cfg);
AllocationContext context = new AllocationContext(stackAlloc, regMasks, _intervals.Count);
BuildIntervals(cfg, context);
for (int index = 0; index < _intervals.Count; index++)
using (AllocationContext context = new AllocationContext(stackAlloc, regMasks, _intervals.Count))
{
LiveInterval current = _intervals[index];
BuildIntervals(cfg, context);
if (current.IsEmpty)
for (int index = 0; index < _intervals.Count; index++)
{
continue;
}
LiveInterval current = _intervals[index];
if (current.IsFixed)
{
context.Active.Set(index);
if (current.Register.Type == RegisterType.Integer)
if (current.IsEmpty)
{
context.IntUsedRegisters |= 1 << current.Register.Index;
}
else /* if (interval.Register.Type == RegisterType.Vector) */
{
context.VecUsedRegisters |= 1 << current.Register.Index;
continue;
}
continue;
if (current.IsFixed)
{
context.Active.Set(index);
if (current.Register.Type == RegisterType.Integer)
{
context.IntUsedRegisters |= 1 << current.Register.Index;
}
else /* if (interval.Register.Type == RegisterType.Vector) */
{
context.VecUsedRegisters |= 1 << current.Register.Index;
}
continue;
}
AllocateInterval(context, current, index);
}
AllocateInterval(context, current, index);
}
for (int index = RegistersCount * 2; index < _intervals.Count; index++)
{
if (!_intervals[index].IsSpilled)
for (int index = RegistersCount * 2; index < _intervals.Count; index++)
{
ReplaceLocalWithRegister(_intervals[index]);
if (!_intervals[index].IsSpilled)
{
ReplaceLocalWithRegister(_intervals[index]);
}
}
InsertSplitCopies();
InsertSplitCopiesAtEdges(cfg);
return new AllocationResult(context.IntUsedRegisters, context.VecUsedRegisters, context.StackAlloc.TotalSize);
}
InsertSplitCopies();
InsertSplitCopiesAtEdges(cfg);
AllocationResult result = new AllocationResult(
context.IntUsedRegisters,
context.VecUsedRegisters,
context.StackAlloc.TotalSize);
context.Dispose();
return result;
}
private void AllocateInterval(AllocationContext context, LiveInterval current, int cIndex)

View file

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

View file

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

View file

@ -4,15 +4,29 @@
{
public static BitMap Allocate(int initialCapacity)
{
BitMap result = ThreadStaticPool<BitMap>.Instance.Allocate();
result.Reset(initialCapacity);
return result;
return BitMap().Reset(initialCapacity);
}
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.Generic;
@ -6,9 +7,6 @@ namespace ARMeilleure.Common
{
class ThreadStaticPool<T> where T : class, new()
{
private const int ChunkSizeLimit = 1000; // even
private const int PoolSizeIncrement = 200; // > 0
[ThreadStatic]
private static ThreadStaticPool<T> _instance;
@ -18,21 +16,36 @@ namespace ARMeilleure.Common
{
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;
}
}
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)
{
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.
@ -41,27 +54,75 @@ namespace ARMeilleure.Common
var pools = GetPools(groupId);
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);
lock (pools)
if (_instance == null)
{
_instance.Clear();
_instance.ChunkSizeLimiter();
pools.Push(_instance);
_instance = null;
_instance = new(chunkSizeLimit, poolSizeIncrement);
}
}
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.
@ -78,20 +139,37 @@ namespace ARMeilleure.Common
_pools.Clear();
}
private static void DisposePoolSlim()
{
// Dispose the pool for this thread.
if (_instance != null)
{
_instance.Dispose();
_instance = null;
}
}
private List<T[]> _pool;
private int _chunkIndex = -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();
}
public T Allocate()
{
if (++_poolIndex >= PoolSizeIncrement)
if (++_poolIndex >= _poolSizeIncrement)
{
AddChunkIfNeeded();
@ -105,9 +183,9 @@ namespace ARMeilleure.Common
{
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();
}
@ -124,18 +202,18 @@ namespace ARMeilleure.Common
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.Capacity = ChunkSizeLimit * 2;
_pool.Capacity = _chunkSizeLimit * 2;
}
}
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
{
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)
{
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);
}
public static void PrepareOperandPool(bool highCq)
#region "ThreadStaticPool"
public static void PrepareOperandPool(int groupId = 0)
{
ThreadStaticPool<Operand>.PreparePool(highCq ? 1 : 0);
ThreadStaticPool<MemoryOperand>.PreparePool(highCq ? 1 : 0);
ThreadStaticPool<Operand>.PreparePool(groupId, ChunkSizeLimit.Large);
ThreadStaticPool<MemoryOperand>.PreparePool(groupId, ChunkSizeLimit.Small);
}
public static void ReturnOperandPool(bool highCq)
private static Operand Operand()
{
ThreadStaticPool<Operand>.ReturnPool(highCq ? 1 : 0);
ThreadStaticPool<MemoryOperand>.ReturnPool(highCq ? 1 : 0);
return ThreadStaticPool<Operand>.Instance.Allocate();
}
public static void ResetOperandPools()
private static MemoryOperand MemoryOperand()
{
ThreadStaticPool<Operand>.ResetPools();
ThreadStaticPool<MemoryOperand>.ResetPools();
return ThreadStaticPool<MemoryOperand>.Instance.Allocate();
}
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
{
public static Operation Operation()
{
return ThreadStaticPool<Operation>.Instance.Allocate();
}
public static Operation Operation(Instruction instruction, Operand destination)
{
return Operation().With(instruction, destination);
@ -46,19 +41,26 @@ namespace ARMeilleure.IntermediateRepresentation
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

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

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

@ -12,6 +12,7 @@ using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.IO.Compression;
using System.Runtime;
using System.Runtime.InteropServices;
using System.Security.Cryptography;
using System.Threading;
@ -20,9 +21,9 @@ 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 = 1817; //! To be incremented manually for each change to the ARMeilleure project.
private const uint InternalVersion = 1817; //! To be incremented manually for each change to the ARMeilleure project.
private const string ActualDir = "0";
private const string BackupDir = "1";
@ -37,12 +38,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 readonly BinaryWriter _infosWriter;
private static BinaryWriter _infosWriter;
private static readonly ulong _headerMagic;
private static readonly ManualResetEvent _waitEvent;
@ -73,6 +76,8 @@ namespace ARMeilleure.Translation.PTC
_infosWriter = new BinaryWriter(_infosStream, EncodingCache.UTF8NoBOM, true);
_headerMagic = BitConverter.ToUInt64(EncodingCache.UTF8NoBOM.GetBytes(HeaderMagicString), 0);
_waitEvent = new ManualResetEvent(true);
_loggerEvent = new AutoResetEvent(false);
@ -95,13 +100,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,49 +119,54 @@ 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;
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);
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();
Directory.CreateDirectory(workPathActual);
}
else
if (!Directory.Exists(workPathBackup))
{
CachePathActual = string.Empty;
CachePathBackup = string.Empty;
Disable();
Directory.CreateDirectory(workPathBackup);
}
CachePathActual = Path.Combine(workPathActual, DisplayVersion);
CachePathBackup = Path.Combine(workPathBackup, DisplayVersion);
PreLoad();
PtcProfiler.PreLoad();
Enable();
}
internal static void ClearMemoryStreams()
private static void ResetMemoryStreamsIfNeeded()
{
_infosStream.SetLength(0L);
_codesStream.SetLength(0L);
_relocsStream.SetLength(0L);
_unwindInfosStream.SetLength(0L);
if (_infosStream.Length == 0L &&
_codesStream.Length == 0L &&
_relocsStream.Length == 0L &&
_unwindInfosStream.Length == 0L)
{
return;
}
_infosWriter.Dispose();
_infosStream.Dispose();
_codesStream.Dispose();
_relocsStream.Dispose();
_unwindInfosStream.Dispose();
_infosStream = new MemoryStream();
_codesStream = new MemoryStream();
_relocsStream = new MemoryStream();
_unwindInfosStream = new MemoryStream();
_infosWriter = new BinaryWriter(_infosStream, EncodingCache.UTF8NoBOM, true);
}
private static void PreLoad()
@ -183,106 +193,153 @@ 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())
{
int hashSize = md5.HashSize / 8;
IntPtr intPtr = IntPtr.Zero;
try
{
deflateStream.CopyTo(stream);
MD5 md5 = MD5.Create();
int hashSize = md5.HashSize / 8;
byte[] currentSizeHash = new byte[hashSize];
compressedStream.Read(currentSizeHash, 0, hashSize);
byte[] sizeBytes = new byte[sizeof(int)];
compressedStream.Read(sizeBytes, 0, sizeBytes.Length);
byte[] expectedSizeHash = md5.ComputeHash(sizeBytes);
if (!CompareHash(currentSizeHash, expectedSizeHash))
{
InvalidateCompressedStream(compressedStream);
return false;
}
int size = BitConverter.ToInt32(sizeBytes, 0);
intPtr = Marshal.AllocHGlobal(size);
using (UnmanagedMemoryStream stream = new UnmanagedMemoryStream((byte*)intPtr.ToPointer(), size, size, FileAccess.ReadWrite))
{
try
{
deflateStream.CopyTo(stream);
}
catch
{
InvalidateCompressedStream(compressedStream);
return false;
}
stream.Seek(0L, SeekOrigin.Begin);
byte[] currentHash = new byte[hashSize];
stream.Read(currentHash, 0, hashSize);
byte[] expectedHash = md5.ComputeHash(stream);
md5.Dispose();
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;
}
long here = stream.Position;
stream.Seek(header.InfosLen + header.CodesLen + header.RelocsLen + header.UnwindInfosLen, SeekOrigin.Current);
try
{
PtcJumpTable = PtcJumpTable.Deserialize(stream);
}
catch
{
PtcJumpTable = new PtcJumpTable();
InvalidateCompressedStream(compressedStream);
return false;
}
stream.Seek(here, SeekOrigin.Begin);
int maxLength = Math.Max(Math.Max(header.InfosLen, header.CodesLen), Math.Max(header.RelocsLen, header.UnwindInfosLen));
Span<byte> buffer = new byte[maxLength];
Span<byte> infosBuf = buffer.Slice(0, header.InfosLen);
stream.Read(infosBuf);
_infosStream.Write(infosBuf);
Span<byte> codesBuf = buffer.Slice(0, header.CodesLen);
stream.Read(codesBuf);
_codesStream.Write(codesBuf);
Span<byte> relocsBuf = buffer.Slice(0, header.RelocsLen);
stream.Read(relocsBuf);
_relocsStream.Write(relocsBuf);
Span<byte> unwindInfosBuf = buffer.Slice(0, header.UnwindInfosLen);
stream.Read(unwindInfosBuf);
_unwindInfosStream.Write(unwindInfosBuf);
}
}
catch
finally
{
InvalidateCompressedStream(compressedStream);
return false;
if (intPtr != IntPtr.Zero)
{
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;
@ -297,13 +354,13 @@ namespace ARMeilleure.Translation.PTC
return currentHash.SequenceEqual(expectedHash);
}
private static Header ReadHeader(MemoryStream stream)
private static Header ReadHeader(Stream stream)
{
using (BinaryReader headerReader = new BinaryReader(stream, EncodingCache.UTF8NoBOM, true))
{
Header header = new Header();
header.Magic = headerReader.ReadString();
header.Magic = headerReader.ReadUInt64();
header.CacheFileVersion = headerReader.ReadUInt32();
header.FeatureInfo = headerReader.ReadUInt64();
@ -323,85 +380,130 @@ 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");
FileInfo fileInfoActual = new FileInfo(fileNameActual);
if (fileInfoActual.Exists && fileInfoActual.Length != 0L)
try
{
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();
}
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())
IntPtr intPtr = IntPtr.Zero;
try
{
MD5 md5 = MD5.Create();
int hashSize = md5.HashSize / 8;
stream.Seek((long)hashSize, SeekOrigin.Begin);
int size = hashSize + Header.Size + GetMemoryStreamsLength() + PtcJumpTable.GetSerializeSize(PtcJumpTable);
WriteHeader(stream);
byte[] sizeBytes = BitConverter.GetBytes(size);
Debug.Assert(sizeBytes.Length == sizeof(int));
byte[] sizeHash = md5.ComputeHash(sizeBytes);
_infosStream.WriteTo(stream);
_codesStream.WriteTo(stream);
_relocsStream.WriteTo(stream);
_unwindInfosStream.WriteTo(stream);
intPtr = Marshal.AllocHGlobal(size);
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))
using (UnmanagedMemoryStream stream = new UnmanagedMemoryStream((byte*)intPtr.ToPointer(), size, size, FileAccess.ReadWrite))
{
try
{
stream.WriteTo(deflateStream);
}
catch
{
compressedStream.Position = 0L;
}
stream.Seek((long)hashSize, SeekOrigin.Begin);
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);
byte[] hash = md5.ComputeHash(stream);
md5.Dispose();
stream.Seek(0L, SeekOrigin.Begin);
stream.Write(hash, 0, hashSize);
translatedFuncsCount = GetInfosEntriesCount();
ResetMemoryStreamsIfNeeded();
PtcJumpTable.ClearIfNeeded();
using (FileStream compressedStream = new FileStream(fileName, FileMode.OpenOrCreate))
using (DeflateStream deflateStream = new DeflateStream(compressedStream, SaveCompressionLevel, true))
{
compressedStream.SetLength(compressedStream.Position);
compressedStream.Write(sizeHash, 0, hashSize);
compressedStream.Write(sizeBytes, 0, sizeBytes.Length);
try
{
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 void WriteHeader(MemoryStream stream)
private static int GetMemoryStreamsLength()
{
return (int)_infosStream.Length + (int)_codesStream.Length + (int)_relocsStream.Length + (int)_unwindInfosStream.Length;
}
private static void WriteHeader(Stream stream)
{
using (BinaryWriter headerWriter = new BinaryWriter(stream, EncodingCache.UTF8NoBOM, true))
{
headerWriter.Write((string)HeaderMagic); // Header.Magic
headerWriter.Write((ulong)_headerMagic); // Header.Magic
headerWriter.Write((uint)InternalVersion); // Header.CacheFileVersion
headerWriter.Write((ulong)GetFeatureInfo()); // Header.FeatureInfo
@ -416,10 +518,10 @@ 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 (_infosStream.Length == 0L &&
_codesStream.Length == 0L &&
_relocsStream.Length == 0L &&
_unwindInfosStream.Length == 0L)
{
return;
}
@ -448,7 +550,7 @@ namespace ARMeilleure.Translation.PTC
}
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 +631,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 +707,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 +765,11 @@ namespace ARMeilleure.Translation.PTC
if (profiledFuncsToTranslate.Count == 0)
{
ResetMemoryStreamsIfNeeded();
PtcJumpTable.ClearIfNeeded();
GCSettings.LargeObjectHeapCompactionMode = GCLargeObjectHeapCompactionMode.CompactOnce;
return;
}
@ -696,9 +803,11 @@ namespace ARMeilleure.Translation.PTC
break;
}
}
Translator.DisposePools();
}
int maxDegreeOfParallelism = (Environment.ProcessorCount * 3) / 4;
int maxDegreeOfParallelism = Environment.ProcessorCount;
List<Thread> threads = new List<Thread>();
@ -715,8 +824,6 @@ namespace ARMeilleure.Translation.PTC
threads.Clear();
Translator.ResetPools();
_loggerEvent.Set();
PtcJumpTable.Initialize(jumpTable);
@ -724,7 +831,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)
@ -784,7 +893,9 @@ namespace ARMeilleure.Translation.PTC
private struct Header
{
public string Magic;
public const int Size = 40; // Bytes.
public ulong Magic;
public uint CacheFileVersion;
public ulong FeatureInfo;
@ -849,6 +960,8 @@ namespace ARMeilleure.Translation.PTC
Wait();
_waitEvent.Dispose();
_loggerEvent.Dispose();
_infosWriter.Dispose();
_infosStream.Dispose();

View file

@ -12,9 +12,11 @@ namespace ARMeilleure.Translation.PTC
{
public struct TableEntry<TAddress>
{
public const int Stride = 16; // Bytes.
public int EntryIndex;
public long GuestAddress;
public TAddress HostAddress;
public TAddress HostAddress; // int
public TableEntry(int entryIndex, long guestAddress, TAddress hostAddress)
{
@ -24,14 +26,14 @@ namespace ARMeilleure.Translation.PTC
}
}
public enum DirectHostAddress
public enum DirectHostAddress : int
{
CallStub = 0,
TailCallStub = 1,
Host = 2
}
public enum IndirectHostAddress
public enum IndirectHostAddress : int
{
CallStub = 0,
TailCallStub = 1
@ -66,7 +68,7 @@ namespace ARMeilleure.Translation.PTC
Owners = owners;
}
public static PtcJumpTable Deserialize(MemoryStream stream)
public static PtcJumpTable Deserialize(Stream stream)
{
using (BinaryReader reader = new BinaryReader(stream, EncodingCache.UTF8NoBOM, true))
{
@ -155,7 +157,36 @@ namespace ARMeilleure.Translation.PTC
}
}
public static void Serialize(MemoryStream stream, PtcJumpTable ptcJumpTable)
public static int GetSerializeSize(PtcJumpTable ptcJumpTable)
{
const int CountSize = 4; // Bytes.
int size = 0;
size += CountSize + ptcJumpTable._jumpTable.Count * TableEntry<DirectHostAddress>.Stride;
size += CountSize + ptcJumpTable._dynamicTable.Count * TableEntry<IndirectHostAddress>.Stride;
size += CountSize + ptcJumpTable.Targets.Count * 8;
size += CountSize;
foreach (var kv in ptcJumpTable.Dependants)
{
size += 8; // kv.Key (address)
size += CountSize + kv.Value.Count * 4;
}
size += CountSize;
foreach (var kv in ptcJumpTable.Owners)
{
size += 8; // kv.Key (address)
size += CountSize + kv.Value.Count * 4;
}
return size;
}
public static void Serialize(Stream stream, PtcJumpTable ptcJumpTable)
{
using (BinaryWriter writer = new BinaryWriter(stream, EncodingCache.UTF8NoBOM, true))
{
@ -270,14 +301,25 @@ namespace ARMeilleure.Translation.PTC
Owners.Remove(guestAddress);
}
public void Clear()
public void ClearIfNeeded()
{
if (_jumpTable.Count == 0 && _dynamicTable.Count == 0 &&
Targets.Count == 0 && Dependants.Count == 0 && Owners.Count == 0)
{
return;
}
_jumpTable.Clear();
_jumpTable.TrimExcess();
_dynamicTable.Clear();
_dynamicTable.TrimExcess();
Targets.Clear();
Targets.TrimExcess();
Dependants.Clear();
Dependants.TrimExcess();
Owners.Clear();
Owners.TrimExcess();
}
public void WriteJumpTable(JumpTable jumpTable, ConcurrentDictionary<ulong, TranslatedFunction> funcs)

View file

@ -29,6 +29,8 @@ namespace ARMeilleure.Translation.PTC
private static bool _disposed;
private static byte[] _lastHash;
internal static Dictionary<ulong, (ExecutionMode mode, bool highCq)> ProfiledFuncs { get; private set; }
internal static bool Enabled { get; private set; }
@ -105,10 +107,13 @@ namespace ARMeilleure.Translation.PTC
internal static void ClearEntries()
{
ProfiledFuncs.Clear();
ProfiledFuncs.TrimExcess();
}
internal static void PreLoad()
{
_lastHash = Array.Empty<byte>();
string fileNameActual = String.Concat(Ptc.CachePathActual, ".info");
string fileNameBackup = String.Concat(Ptc.CachePathBackup, ".info");
@ -138,8 +143,6 @@ namespace ARMeilleure.Translation.PTC
using (MemoryStream stream = new MemoryStream())
using (MD5 md5 = MD5.Create())
{
int hashSize = md5.HashSize / 8;
try
{
deflateStream.CopyTo(stream);
@ -151,6 +154,8 @@ namespace ARMeilleure.Translation.PTC
return false;
}
int hashSize = md5.HashSize / 8;
stream.Seek(0L, SeekOrigin.Begin);
byte[] currentHash = new byte[hashSize];
@ -195,6 +200,8 @@ namespace ARMeilleure.Translation.PTC
return false;
}
_lastHash = expectedHash;
}
long fileSize = new FileInfo(fileName).Length;
@ -209,7 +216,7 @@ namespace ARMeilleure.Translation.PTC
return currentHash.SequenceEqual(expectedHash);
}
private static Header ReadHeader(MemoryStream stream)
private static Header ReadHeader(Stream stream)
{
using (BinaryReader headerReader = new BinaryReader(stream, EncodingCache.UTF8NoBOM, true))
{
@ -223,7 +230,7 @@ namespace ARMeilleure.Translation.PTC
}
}
private static Dictionary<ulong, (ExecutionMode, bool)> Deserialize(MemoryStream stream)
private static Dictionary<ulong, (ExecutionMode, bool)> Deserialize(Stream stream)
{
using (BinaryReader reader = new BinaryReader(stream, EncodingCache.UTF8NoBOM, true))
{
@ -295,16 +302,25 @@ namespace ARMeilleure.Translation.PTC
stream.Seek(0L, SeekOrigin.Begin);
stream.Write(hash, 0, hashSize);
if (CompareHash(hash, _lastHash))
{
return;
}
using (FileStream compressedStream = new FileStream(fileName, FileMode.OpenOrCreate))
using (DeflateStream deflateStream = new DeflateStream(compressedStream, SaveCompressionLevel, true))
{
try
{
stream.WriteTo(deflateStream);
_lastHash = hash;
}
catch
{
compressedStream.Position = 0L;
_lastHash = Array.Empty<byte>();
}
if (compressedStream.Position < compressedStream.Length)
@ -316,10 +332,13 @@ namespace ARMeilleure.Translation.PTC
long fileSize = new FileInfo(fileName).Length;
Logger.Info?.Print(LogClass.Ptc, $"Saved Profiling Info (size: {fileSize} bytes, profiled functions: {profiledFuncsCount}).");
if (fileSize != 0L)
{
Logger.Info?.Print(LogClass.Ptc, $"Saved Profiling Info (size: {fileSize} bytes, profiled functions: {profiledFuncsCount}).");
}
}
private static void WriteHeader(MemoryStream stream)
private static void WriteHeader(Stream stream)
{
using (BinaryWriter headerWriter = new BinaryWriter(stream, EncodingCache.UTF8NoBOM, true))
{
@ -329,7 +348,7 @@ 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, (ExecutionMode mode, bool highCq)> profiledFuncs)
{
using (BinaryWriter writer = new BinaryWriter(stream, EncodingCache.UTF8NoBOM, true))
{

View file

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

View file

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