PPTC & Pool Enhancements. (#1968)
* PPTC & Pool Enhancements. * Avoid buffer allocations in CodeGenContext.GetCode(). Avoid stream allocations in PTC.PtcInfo. Refactoring/nits. * Use XXHash128, for Ptc.Load & Ptc.Save, x10 faster than Md5. * Why not a nice Span. * Added a simple PtcFormatter library for deserialization/serialization, which does not require reflection, in use at PtcJumpTable and PtcProfiler; improves maintainability and simplicity/readability of affected code. * Nits. * Revert #1987. * Revert "Revert #1987." This reverts commit 998be765cf7f7da5ff0c1c08de704c9012b0f49c.
This commit is contained in:
parent
1586880114
commit
dc0adb533d
20 changed files with 777 additions and 603 deletions
|
@ -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)
|
||||||
|
|
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
14
ARMeilleure/Common/ThreadStaticPoolEnums.cs
Normal file
14
ARMeilleure/Common/ThreadStaticPoolEnums.cs
Normal 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
|
||||||
|
}
|
||||||
|
}
|
|
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
namespace ARMeilleure.State
|
namespace ARMeilleure.State
|
||||||
{
|
{
|
||||||
enum ExecutionMode
|
enum ExecutionMode : int
|
||||||
{
|
{
|
||||||
Aarch32Arm = 0,
|
Aarch32Arm = 0,
|
||||||
Aarch32Thumb = 1,
|
Aarch32Thumb = 1,
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
121
ARMeilleure/Translation/PTC/PtcFormatter.cs
Normal file
121
ARMeilleure/Translation/PTC/PtcFormatter.cs
Normal 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
|
||||||
|
}
|
||||||
|
}
|
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
|
@ -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})");
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 ||
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue