cleanup old jit functions

This commit is contained in:
emmaus 2019-08-13 16:22:18 +00:00 committed by emmauss
commit 68cee9db6d
4 changed files with 178 additions and 28 deletions

View file

@ -17,7 +17,7 @@ namespace ARMeilleure.Translation
private static IntPtr _basePointer; private static IntPtr _basePointer;
private static int _offset; private static JitCacheMemoryAllocator _allocator;
private static List<JitCacheEntry> _cacheEntries; private static List<JitCacheEntry> _cacheEntries;
@ -27,14 +27,20 @@ namespace ARMeilleure.Translation
{ {
_basePointer = MemoryManagement.Allocate(CacheSize); _basePointer = MemoryManagement.Allocate(CacheSize);
int startOffset = 0;
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{ {
JitUnwindWindows.InstallFunctionTableHandler(_basePointer, CacheSize); JitUnwindWindows.InstallFunctionTableHandler(_basePointer, CacheSize);
// The first page is used for the table based SEH structs. // The first page is used for the table based SEH structs.
_offset = PageSize; startOffset = PageSize;
} }
ReprotectRange(startOffset, CacheSize - startOffset);
_allocator = new JitCacheMemoryAllocator(CacheSize, startOffset);
_cacheEntries = new List<JitCacheEntry>(); _cacheEntries = new List<JitCacheEntry>();
_lock = new object(); _lock = new object();
@ -52,8 +58,6 @@ namespace ARMeilleure.Translation
Marshal.Copy(code, 0, funcPtr, code.Length); Marshal.Copy(code, 0, funcPtr, code.Length);
ReprotectRange(funcOffset, code.Length);
Add(new JitCacheEntry(funcOffset, code.Length, func.UnwindInfo)); Add(new JitCacheEntry(funcOffset, code.Length, func.UnwindInfo));
return funcPtr; return funcPtr;
@ -76,16 +80,7 @@ namespace ARMeilleure.Translation
{ {
IntPtr funcPtr = _basePointer + pageStart; IntPtr funcPtr = _basePointer + pageStart;
MemoryManagement.Reprotect(funcPtr, (ulong)fullPagesSize, MemoryProtection.ReadAndExecute); MemoryManagement.Reprotect(funcPtr, (ulong)fullPagesSize, MemoryProtection.ReadWriteExecute);
}
int remaining = endOffs - pageEnd;
if (remaining != 0)
{
IntPtr funcPtr = _basePointer + pageEnd;
MemoryManagement.Reprotect(funcPtr, (ulong)remaining, MemoryProtection.ReadWriteExecute);
} }
} }
@ -93,18 +88,28 @@ namespace ARMeilleure.Translation
{ {
codeSize = checked(codeSize + (CodeAlignment - 1)) & ~(CodeAlignment - 1); codeSize = checked(codeSize + (CodeAlignment - 1)) & ~(CodeAlignment - 1);
int allocOffset = _offset; int allocOffset = _allocator.Allocate(codeSize);
_offset += codeSize;
if ((ulong)(uint)_offset > CacheSize)
{
throw new OutOfMemoryException();
}
return allocOffset; return allocOffset;
} }
public static void Free(ulong address)
{
ulong offset = address - (ulong)_basePointer;
lock (_lock)
{
if (TryFind((int)offset, out JitCacheEntry entry))
{
_cacheEntries.Remove(entry);
int size = checked(entry.Size + (CodeAlignment - 1)) & ~(CodeAlignment - 1);
_allocator.Free((int)entry.Offset, size);
}
}
}
private static void Add(JitCacheEntry entry) private static void Add(JitCacheEntry entry)
{ {
_cacheEntries.Add(entry); _cacheEntries.Add(entry);

View file

@ -0,0 +1,108 @@
using System;
using System.Collections.Generic;
namespace ARMeilleure.Translation
{
class JitCacheMemoryAllocator
{
private int _size;
private LinkedList<(int Start, int End)> _memoryRanges;
public JitCacheMemoryAllocator(int size, int startPosition)
{
_size = size;
_memoryRanges = new LinkedList<(int start, int end)>();
_memoryRanges.AddFirst((startPosition, startPosition - 1));
}
public int Allocate(int size)
{
var node = _memoryRanges.First;
int offset;
while (true)
{
if (node.Value.End > (_size - 1) - size)
{
throw new OutOfMemoryException();
}
if (node.Next == null)
{
offset = node.Value.End + 1;
node.Value = (node.Value.Start, node.Value.End + size);
break;
}
else
{
if (node.Next.Value.Start - node.Value.End <= 1)
{
node.Value = (node.Value.Start, node.Next.Value.End);
_memoryRanges.Remove(node.Next);
}
if (node.Next.Value.Start - size > node.Value.End)
{
offset = node.Value.End + 1;
if (node.Next.Value.Start - offset == size)
{
node.Value = (node.Value.Start, node.Next.Value.End);
_memoryRanges.Remove(node.Next);
break;
}
node.Value = (node.Value.Start, offset + size - 1);
break;
}
node = node.Next;
}
}
return offset;
}
public void Free(int offset, int size)
{
if ((uint)offset >= (ulong)_size)
{
throw new ArgumentOutOfRangeException();
}
var node = _memoryRanges.First;
while (true)
{
if (node == null)
{
throw new ArgumentOutOfRangeException();
}
if (offset <= node.Value.End)
{
int newRangeStart = offset + size;
_memoryRanges.AddAfter(node, (newRangeStart, node.Value.End));
break;
}
node = node.Next;
}
node.Value = (node.Value.Start, offset - 1);
}
}
}

View file

@ -1,17 +1,24 @@
using System;
using System.Threading; using System.Threading;
using System.Runtime.InteropServices;
namespace ARMeilleure.Translation namespace ARMeilleure.Translation
{ {
class TranslatedFunction class TranslatedFunction
{ {
public ulong Pointer => (ulong)Marshal.GetFunctionPointerForDelegate(_func);
public int EntryCount;
private const int MinCallsForRejit = 100; private const int MinCallsForRejit = 100;
private GuestFunction _func; private GuestFunction _func;
private ulong _address;
private bool _rejit; private bool _rejit;
private int _callCount; private int _callCount;
public TranslatedFunction(GuestFunction func, bool rejit) public TranslatedFunction(GuestFunction func, ulong address, bool rejit)
{ {
_func = func; _func = func;
_rejit = rejit; _rejit = rejit;
@ -19,7 +26,16 @@ namespace ARMeilleure.Translation
public ulong Execute(State.ExecutionContext context) public ulong Execute(State.ExecutionContext context)
{ {
return _func(context.NativeContextPtr); if (Interlocked.Increment(ref EntryCount) == 0)
{
return _address;
}
var nextAddress = _func(context.NativeContextPtr);
Interlocked.Decrement(ref EntryCount);
return nextAddress;
} }
public bool ShouldRejit() public bool ShouldRejit()

View file

@ -6,6 +6,7 @@ using ARMeilleure.Memory;
using ARMeilleure.State; using ARMeilleure.State;
using System; using System;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Threading; using System.Threading;
using static ARMeilleure.IntermediateRepresentation.OperandHelper; using static ARMeilleure.IntermediateRepresentation.OperandHelper;
@ -20,6 +21,8 @@ namespace ARMeilleure.Translation
private ConcurrentDictionary<ulong, TranslatedFunction> _funcs; private ConcurrentDictionary<ulong, TranslatedFunction> _funcs;
private Queue<TranslatedFunction> _oldFunctions;
private PriorityQueue<RejitRequest> _backgroundQueue; private PriorityQueue<RejitRequest> _backgroundQueue;
private AutoResetEvent _backgroundTranslatorEvent; private AutoResetEvent _backgroundTranslatorEvent;
@ -35,6 +38,8 @@ namespace ARMeilleure.Translation
_backgroundQueue = new PriorityQueue<RejitRequest>(2); _backgroundQueue = new PriorityQueue<RejitRequest>(2);
_backgroundTranslatorEvent = new AutoResetEvent(false); _backgroundTranslatorEvent = new AutoResetEvent(false);
_oldFunctions = new Queue<TranslatedFunction>();
} }
private void TranslateQueuedSubs() private void TranslateQueuedSubs()
@ -45,7 +50,23 @@ namespace ARMeilleure.Translation
{ {
TranslatedFunction func = Translate(request.Address, request.Mode, highCq: true); TranslatedFunction func = Translate(request.Address, request.Mode, highCq: true);
_funcs.AddOrUpdate(request.Address, func, (key, oldFunc) => func); _funcs.AddOrUpdate(request.Address, func, (key, oldFunc) =>
{
_oldFunctions.Enqueue(oldFunc);
return func;
});
}
else if (_oldFunctions.TryDequeue(out TranslatedFunction function))
{
if (Interlocked.CompareExchange(ref function.EntryCount, -1, 0) != 0)
{
_oldFunctions.Enqueue(function);
}
else
{
JitCache.Free(function.Pointer);
}
} }
else else
{ {
@ -161,7 +182,7 @@ namespace ARMeilleure.Translation
GuestFunction func = Compiler.Compile<GuestFunction>(cfg, argTypes, OperandType.I64, options); GuestFunction func = Compiler.Compile<GuestFunction>(cfg, argTypes, OperandType.I64, options);
return new TranslatedFunction(func, rejit: !highCq); return new TranslatedFunction(func, address, rejit: !highCq);
} }
private static ControlFlowGraph EmitAndGetCFG(ArmEmitterContext context, Block[] blocks) private static ControlFlowGraph EmitAndGetCFG(ArmEmitterContext context, Block[] blocks)