2 level inline table (armeilleure)
TODOS: - Lightning JIT - Sparse page using signal handler - Fallback page? Would save a few instructions
This commit is contained in:
parent
d86249cb0a
commit
4e381009a9
4 changed files with 110 additions and 2 deletions
|
@ -56,6 +56,8 @@ namespace ARMeilleure.Common
|
||||||
private bool _disposed;
|
private bool _disposed;
|
||||||
private TEntry** _table;
|
private TEntry** _table;
|
||||||
private readonly List<IntPtr> _pages;
|
private readonly List<IntPtr> _pages;
|
||||||
|
private readonly TEntry* _fallbackTable;
|
||||||
|
private TEntry _fill;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the bits used by the <see cref="Levels"/> of the <see cref="AddressTable{TEntry}"/> instance.
|
/// Gets the bits used by the <see cref="Levels"/> of the <see cref="AddressTable{TEntry}"/> instance.
|
||||||
|
@ -70,7 +72,18 @@ namespace ARMeilleure.Common
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the default fill value of newly created leaf pages.
|
/// Gets or sets the default fill value of newly created leaf pages.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public TEntry Fill { get; set; }
|
public TEntry Fill
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return _fill;
|
||||||
|
}
|
||||||
|
set
|
||||||
|
{
|
||||||
|
*_fallbackTable = value;
|
||||||
|
_fill = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the base address of the <see cref="EntryTable{TEntry}"/>.
|
/// Gets the base address of the <see cref="EntryTable{TEntry}"/>.
|
||||||
|
@ -89,6 +102,19 @@ namespace ARMeilleure.Common
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a pointer to a single entry table containing only the leaf fill value.
|
||||||
|
/// </summary>
|
||||||
|
public IntPtr Fallback
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||||
|
|
||||||
|
return (IntPtr)_fallbackTable;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Constructs a new instance of the <see cref="AddressTable{TEntry}"/> class with the specified list of
|
/// Constructs a new instance of the <see cref="AddressTable{TEntry}"/> class with the specified list of
|
||||||
/// <see cref="Level"/>.
|
/// <see cref="Level"/>.
|
||||||
|
@ -113,6 +139,8 @@ namespace ARMeilleure.Common
|
||||||
{
|
{
|
||||||
Mask |= level.Mask;
|
Mask |= level.Mask;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_fallbackTable = (TEntry*)NativeAllocator.Instance.Allocate((ulong)sizeof(TEntry));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -237,6 +265,8 @@ namespace ARMeilleure.Common
|
||||||
Marshal.FreeHGlobal(page);
|
Marshal.FreeHGlobal(page);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Marshal.FreeHGlobal((IntPtr)_fallbackTable);
|
||||||
|
|
||||||
_disposed = true;
|
_disposed = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -193,6 +193,8 @@ namespace ARMeilleure.Instructions
|
||||||
|
|
||||||
Operand hostAddress;
|
Operand hostAddress;
|
||||||
|
|
||||||
|
var table = context.FunctionTable;
|
||||||
|
|
||||||
// If address is mapped onto the function table, we can skip the table walk. Otherwise we fallback
|
// If address is mapped onto the function table, we can skip the table walk. Otherwise we fallback
|
||||||
// onto the dispatch stub.
|
// onto the dispatch stub.
|
||||||
if (guestAddress.Kind == OperandKind.Constant && context.FunctionTable.IsValid(guestAddress.Value))
|
if (guestAddress.Kind == OperandKind.Constant && context.FunctionTable.IsValid(guestAddress.Value))
|
||||||
|
@ -203,6 +205,45 @@ namespace ARMeilleure.Instructions
|
||||||
|
|
||||||
hostAddress = context.Load(OperandType.I64, hostAddressAddr);
|
hostAddress = context.Load(OperandType.I64, hostAddressAddr);
|
||||||
}
|
}
|
||||||
|
else if (table.Levels.Length == 2)
|
||||||
|
{
|
||||||
|
// Inline table lookup. Only enabled when the sparse function table is enabled with 2 levels.
|
||||||
|
// Deliberately attempts to avoid branches.
|
||||||
|
|
||||||
|
var level0 = table.Levels[0];
|
||||||
|
|
||||||
|
// Currently no bounds check. Maybe conditionally do this for unsafe host mapped.
|
||||||
|
Operand index = context.ShiftLeft(context.ShiftRightUI(guestAddress, Const(level0.Index)), Const(3));
|
||||||
|
|
||||||
|
Operand tableBase = !context.HasPtc ?
|
||||||
|
Const(table.Base) :
|
||||||
|
Const(table.Base, Ptc.FunctionTableSymbol);
|
||||||
|
|
||||||
|
Operand page = context.Load(OperandType.I64, context.Add(tableBase, index));
|
||||||
|
|
||||||
|
// Second level
|
||||||
|
var level1 = table.Levels[1];
|
||||||
|
|
||||||
|
int clearBits = 64 - (level1.Index + level1.Length);
|
||||||
|
|
||||||
|
Operand index2 = context.ShiftLeft(
|
||||||
|
context.ShiftRightUI(context.ShiftLeft(guestAddress, Const(clearBits)), Const(clearBits + level1.Index)),
|
||||||
|
Const(3)
|
||||||
|
);
|
||||||
|
|
||||||
|
// TODO: could possibly make a fallback page that level 1 is filled with that contains dispatch stub on all pages
|
||||||
|
// Would save this load and the comparisons
|
||||||
|
// 16MB of the same value is a bit wasteful so it could replicate with remapping.
|
||||||
|
|
||||||
|
Operand fallback = !context.HasPtc ?
|
||||||
|
Const((long)context.FunctionTable.Fallback) :
|
||||||
|
Const((long)context.FunctionTable.Fallback, Ptc.DispatchFallbackSymbol);
|
||||||
|
|
||||||
|
Operand pageIsZero = context.ICompareEqual(page, Const(0L));
|
||||||
|
|
||||||
|
// Small trick to keep this branchless - if the page is zero, load a fallback table entry that always contains the dispatch stub.
|
||||||
|
hostAddress = context.Load(OperandType.I64, context.ConditionalSelect(pageIsZero, fallback, context.Add(page, index2)));
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
hostAddress = !context.HasPtc ?
|
hostAddress = !context.HasPtc ?
|
||||||
|
|
|
@ -40,6 +40,8 @@ namespace ARMeilleure.Translation.PTC
|
||||||
public static readonly Symbol PageTableSymbol = new(SymbolType.Special, 1);
|
public static readonly Symbol PageTableSymbol = new(SymbolType.Special, 1);
|
||||||
public static readonly Symbol CountTableSymbol = new(SymbolType.Special, 2);
|
public static readonly Symbol CountTableSymbol = new(SymbolType.Special, 2);
|
||||||
public static readonly Symbol DispatchStubSymbol = new(SymbolType.Special, 3);
|
public static readonly Symbol DispatchStubSymbol = new(SymbolType.Special, 3);
|
||||||
|
public static readonly Symbol FunctionTableSymbol = new(SymbolType.Special, 4);
|
||||||
|
public static readonly Symbol DispatchFallbackSymbol = new(SymbolType.Special, 5);
|
||||||
|
|
||||||
private const byte FillingByte = 0x00;
|
private const byte FillingByte = 0x00;
|
||||||
private const CompressionLevel SaveCompressionLevel = CompressionLevel.Fastest;
|
private const CompressionLevel SaveCompressionLevel = CompressionLevel.Fastest;
|
||||||
|
@ -705,6 +707,14 @@ namespace ARMeilleure.Translation.PTC
|
||||||
{
|
{
|
||||||
imm = translator.Stubs.DispatchStub;
|
imm = translator.Stubs.DispatchStub;
|
||||||
}
|
}
|
||||||
|
else if (symbol == FunctionTableSymbol)
|
||||||
|
{
|
||||||
|
imm = translator.FunctionTable.Base;
|
||||||
|
}
|
||||||
|
else if (symbol == DispatchFallbackSymbol)
|
||||||
|
{
|
||||||
|
imm = translator.FunctionTable.Fallback;
|
||||||
|
}
|
||||||
|
|
||||||
if (imm == null)
|
if (imm == null)
|
||||||
{
|
{
|
||||||
|
|
|
@ -22,6 +22,8 @@ namespace ARMeilleure.Translation
|
||||||
{
|
{
|
||||||
public class Translator
|
public class Translator
|
||||||
{
|
{
|
||||||
|
private const bool UseSparseTable = true;
|
||||||
|
|
||||||
private static readonly AddressTable<ulong>.Level[] _levels64Bit =
|
private static readonly AddressTable<ulong>.Level[] _levels64Bit =
|
||||||
new AddressTable<ulong>.Level[]
|
new AddressTable<ulong>.Level[]
|
||||||
{
|
{
|
||||||
|
@ -42,6 +44,20 @@ namespace ARMeilleure.Translation
|
||||||
new( 1, 6),
|
new( 1, 6),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
private static readonly AddressTable<ulong>.Level[] _levels64BitSparse =
|
||||||
|
new AddressTable<ulong>.Level[]
|
||||||
|
{
|
||||||
|
new(23, 16),
|
||||||
|
new( 2, 21),
|
||||||
|
};
|
||||||
|
|
||||||
|
private static readonly AddressTable<ulong>.Level[] _levels32BitSparse =
|
||||||
|
new AddressTable<ulong>.Level[]
|
||||||
|
{
|
||||||
|
new(22, 10),
|
||||||
|
new( 1, 21),
|
||||||
|
};
|
||||||
|
|
||||||
private readonly IJitMemoryAllocator _allocator;
|
private readonly IJitMemoryAllocator _allocator;
|
||||||
private readonly ConcurrentQueue<KeyValuePair<ulong, TranslatedFunction>> _oldFuncs;
|
private readonly ConcurrentQueue<KeyValuePair<ulong, TranslatedFunction>> _oldFuncs;
|
||||||
|
|
||||||
|
@ -70,9 +86,20 @@ namespace ARMeilleure.Translation
|
||||||
|
|
||||||
JitCache.Initialize(allocator);
|
JitCache.Initialize(allocator);
|
||||||
|
|
||||||
|
AddressTable<ulong>.Level[] levels;
|
||||||
|
|
||||||
|
if (UseSparseTable)
|
||||||
|
{
|
||||||
|
levels = for64Bits ? _levels64BitSparse : _levels32BitSparse;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
levels = for64Bits ? _levels64Bit : _levels32Bit;
|
||||||
|
}
|
||||||
|
|
||||||
CountTable = new EntryTable<uint>();
|
CountTable = new EntryTable<uint>();
|
||||||
Functions = new TranslatorCache<TranslatedFunction>();
|
Functions = new TranslatorCache<TranslatedFunction>();
|
||||||
FunctionTable = new AddressTable<ulong>(for64Bits ? _levels64Bit : _levels32Bit);
|
FunctionTable = new AddressTable<ulong>(levels);
|
||||||
Stubs = new TranslatorStubs(FunctionTable);
|
Stubs = new TranslatorStubs(FunctionTable);
|
||||||
|
|
||||||
FunctionTable.Fill = (ulong)Stubs.SlowDispatchStub;
|
FunctionTable.Fill = (ulong)Stubs.SlowDispatchStub;
|
||||||
|
|
Loading…
Reference in a new issue