cleanup old jit functions
This commit is contained in:
parent
3b531de670
commit
68cee9db6d
4 changed files with 178 additions and 28 deletions
|
@ -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);
|
||||||
|
|
108
ARMeilleure/Translation/JitCacheMemoryAllocator.cs
Normal file
108
ARMeilleure/Translation/JitCacheMemoryAllocator.cs
Normal 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -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 bool _rejit;
|
private ulong _address;
|
||||||
private int _callCount;
|
private bool _rejit;
|
||||||
|
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()
|
||||||
|
|
|
@ -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)
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue