cleanup old jit functions

This commit is contained in:
emmaus 2019-08-13 16:22:18 +00:00 committed by emmauss
parent 3b531de670
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 int _offset;
private static JitCacheMemoryAllocator _allocator;
private static List<JitCacheEntry> _cacheEntries;
@ -27,14 +27,20 @@ namespace ARMeilleure.Translation
{
_basePointer = MemoryManagement.Allocate(CacheSize);
int startOffset = 0;
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
JitUnwindWindows.InstallFunctionTableHandler(_basePointer, CacheSize);
// 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>();
_lock = new object();
@ -52,8 +58,6 @@ namespace ARMeilleure.Translation
Marshal.Copy(code, 0, funcPtr, code.Length);
ReprotectRange(funcOffset, code.Length);
Add(new JitCacheEntry(funcOffset, code.Length, func.UnwindInfo));
return funcPtr;
@ -76,16 +80,7 @@ namespace ARMeilleure.Translation
{
IntPtr funcPtr = _basePointer + pageStart;
MemoryManagement.Reprotect(funcPtr, (ulong)fullPagesSize, MemoryProtection.ReadAndExecute);
}
int remaining = endOffs - pageEnd;
if (remaining != 0)
{
IntPtr funcPtr = _basePointer + pageEnd;
MemoryManagement.Reprotect(funcPtr, (ulong)remaining, MemoryProtection.ReadWriteExecute);
MemoryManagement.Reprotect(funcPtr, (ulong)fullPagesSize, MemoryProtection.ReadWriteExecute);
}
}
@ -93,18 +88,28 @@ namespace ARMeilleure.Translation
{
codeSize = checked(codeSize + (CodeAlignment - 1)) & ~(CodeAlignment - 1);
int allocOffset = _offset;
_offset += codeSize;
if ((ulong)(uint)_offset > CacheSize)
{
throw new OutOfMemoryException();
}
int allocOffset = _allocator.Allocate(codeSize);
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)
{
_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.Runtime.InteropServices;
namespace ARMeilleure.Translation
{
class TranslatedFunction
{
public ulong Pointer => (ulong)Marshal.GetFunctionPointerForDelegate(_func);
public int EntryCount;
private const int MinCallsForRejit = 100;
private GuestFunction _func;
private bool _rejit;
private int _callCount;
private ulong _address;
private bool _rejit;
private int _callCount;
public TranslatedFunction(GuestFunction func, bool rejit)
public TranslatedFunction(GuestFunction func, ulong address, bool rejit)
{
_func = func;
_rejit = rejit;
@ -19,7 +26,16 @@ namespace ARMeilleure.Translation
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()

View file

@ -6,6 +6,7 @@ using ARMeilleure.Memory;
using ARMeilleure.State;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Threading;
using static ARMeilleure.IntermediateRepresentation.OperandHelper;
@ -20,6 +21,8 @@ namespace ARMeilleure.Translation
private ConcurrentDictionary<ulong, TranslatedFunction> _funcs;
private Queue<TranslatedFunction> _oldFunctions;
private PriorityQueue<RejitRequest> _backgroundQueue;
private AutoResetEvent _backgroundTranslatorEvent;
@ -35,6 +38,8 @@ namespace ARMeilleure.Translation
_backgroundQueue = new PriorityQueue<RejitRequest>(2);
_backgroundTranslatorEvent = new AutoResetEvent(false);
_oldFunctions = new Queue<TranslatedFunction>();
}
private void TranslateQueuedSubs()
@ -45,7 +50,23 @@ namespace ARMeilleure.Translation
{
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
{
@ -161,7 +182,7 @@ namespace ARMeilleure.Translation
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)