Lightning JIT test impl

This commit is contained in:
riperiperi 2024-06-15 21:55:55 +01:00
parent 4e381009a9
commit 9ef61df9b8
3 changed files with 140 additions and 1 deletions

View file

@ -140,6 +140,9 @@ namespace Ryujinx.Cpu.LightningJit.Arm32.Target.Arm64
bool isTail = false) bool isTail = false)
{ {
int tempRegister; int tempRegister;
int tempGuestAddress = 0;
bool inlineLookup = guestAddress.Kind != OperandKind.Constant && funcTable != null && funcTable.Levels.Length == 2;
if (guestAddress.Kind == OperandKind.Constant) if (guestAddress.Kind == OperandKind.Constant)
{ {
@ -153,6 +156,13 @@ namespace Ryujinx.Cpu.LightningJit.Arm32.Target.Arm64
else else
{ {
asm.StrRiUn(guestAddress, Register(regAlloc.FixedContextRegister), NativeContextOffsets.DispatchAddressOffset); asm.StrRiUn(guestAddress, Register(regAlloc.FixedContextRegister), NativeContextOffsets.DispatchAddressOffset);
if (inlineLookup)
{
// Might be overwritten. Move the address to a temp register.
tempGuestAddress = regAlloc.AllocateTempGprRegister();
asm.Mov(Register(tempGuestAddress), guestAddress);
}
} }
tempRegister = regAlloc.FixedContextRegister == 1 ? 2 : 1; tempRegister = regAlloc.FixedContextRegister == 1 ? 2 : 1;
@ -176,6 +186,47 @@ namespace Ryujinx.Cpu.LightningJit.Arm32.Target.Arm64
asm.Mov(rn, funcPtrLoc & ~0xfffUL); asm.Mov(rn, funcPtrLoc & ~0xfffUL);
asm.LdrRiUn(rn, rn, (int)(funcPtrLoc & 0xfffUL)); asm.LdrRiUn(rn, rn, (int)(funcPtrLoc & 0xfffUL));
} }
else if (inlineLookup)
{
// Inline table lookup. Only enabled when the sparse function table is enabled with 2 levels.
Operand indexReg = Register(3);
guestAddress = Register(tempGuestAddress);
var level0 = funcTable.Levels[0];
asm.Ubfx(indexReg, guestAddress, level0.Index, level0.Length);
asm.Lsl(indexReg, indexReg, Const(3));
ulong tableBase = (ulong)funcTable.Base;
// Index into the table.
asm.Mov(rn, tableBase);
asm.Add(rn, rn, indexReg);
// Load the page address.
asm.LdrRiUn(rn, rn, 0);
var level1 = funcTable.Levels[1];
asm.Ubfx(indexReg, guestAddress, level1.Index, level1.Length);
asm.Lsl(indexReg, indexReg, Const(3));
// Is the page address zero? Make sure to use the fallback if it is.
asm.Tst(rn, rn);
// Index into the page.
asm.Add(rn, rn, indexReg);
// Reuse the index register for the fallback
ulong fallback = (ulong)funcTable.Fallback;
asm.Mov(indexReg, fallback);
asm.Csel(rn, indexReg, rn, ArmCondition.Eq);
// Load the final branch address
asm.LdrRiUn(rn, rn, 0);
regAlloc.FreeTempGprRegister(tempGuestAddress);
}
else else
{ {
asm.Mov(rn, (ulong)funcPtr); asm.Mov(rn, (ulong)funcPtr);
@ -252,5 +303,10 @@ namespace Ryujinx.Cpu.LightningJit.Arm32.Target.Arm64
{ {
return new Operand(register, RegisterType.Integer, type); return new Operand(register, RegisterType.Integer, type);
} }
private static Operand Const(long value, OperandType type = OperandType.I64)
{
return new Operand(type, (ulong)value);
}
} }
} }

View file

@ -305,6 +305,9 @@ namespace Ryujinx.Cpu.LightningJit.Arm64.Target.Arm64
bool isTail = false) bool isTail = false)
{ {
int tempRegister; int tempRegister;
int tempGuestAddress = 0;
bool inlineLookup = guestAddress.Kind != OperandKind.Constant && funcTable != null && funcTable.Levels.Length == 2;
if (guestAddress.Kind == OperandKind.Constant) if (guestAddress.Kind == OperandKind.Constant)
{ {
@ -318,6 +321,13 @@ namespace Ryujinx.Cpu.LightningJit.Arm64.Target.Arm64
else else
{ {
asm.StrRiUn(guestAddress, Register(regAlloc.FixedContextRegister), NativeContextOffsets.DispatchAddressOffset); asm.StrRiUn(guestAddress, Register(regAlloc.FixedContextRegister), NativeContextOffsets.DispatchAddressOffset);
if (inlineLookup)
{
// Might be overwritten. Move the address to a temp register.
tempGuestAddress = regAlloc.AllocateTempGprRegister();
asm.Mov(Register(tempGuestAddress), guestAddress);
}
} }
tempRegister = regAlloc.FixedContextRegister == 1 ? 2 : 1; tempRegister = regAlloc.FixedContextRegister == 1 ? 2 : 1;
@ -341,6 +351,47 @@ namespace Ryujinx.Cpu.LightningJit.Arm64.Target.Arm64
asm.Mov(rn, funcPtrLoc & ~0xfffUL); asm.Mov(rn, funcPtrLoc & ~0xfffUL);
asm.LdrRiUn(rn, rn, (int)(funcPtrLoc & 0xfffUL)); asm.LdrRiUn(rn, rn, (int)(funcPtrLoc & 0xfffUL));
} }
else if (inlineLookup)
{
// Inline table lookup. Only enabled when the sparse function table is enabled with 2 levels.
Operand indexReg = Register(3);
guestAddress = Register(tempGuestAddress);
var level0 = funcTable.Levels[0];
asm.Ubfx(indexReg, guestAddress, level0.Index, level0.Length);
asm.Lsl(indexReg, indexReg, Const(3));
ulong tableBase = (ulong)funcTable.Base;
// Index into the table.
asm.Mov(rn, tableBase);
asm.Add(rn, rn, indexReg);
// Load the page address.
asm.LdrRiUn(rn, rn, 0);
var level1 = funcTable.Levels[1];
asm.Ubfx(indexReg, guestAddress, level1.Index, level1.Length);
asm.Lsl(indexReg, indexReg, Const(3));
// Is the page address zero? Make sure to use the fallback if it is.
asm.Tst(rn, rn);
// Index into the page.
asm.Add(rn, rn, indexReg);
// Reuse the index register for the fallback
ulong fallback = (ulong)funcTable.Fallback;
asm.Mov(indexReg, fallback);
asm.Csel(rn, indexReg, rn, ArmCondition.Eq);
// Load the final branch address
asm.LdrRiUn(rn, rn, 0);
regAlloc.FreeTempGprRegister(tempGuestAddress);
}
else else
{ {
asm.Mov(rn, (ulong)funcPtr); asm.Mov(rn, (ulong)funcPtr);
@ -613,5 +664,10 @@ namespace Ryujinx.Cpu.LightningJit.Arm64.Target.Arm64
{ {
return new Operand(register, RegisterType.Integer, type); return new Operand(register, RegisterType.Integer, type);
} }
private static Operand Const(long value, OperandType type = OperandType.I64)
{
return new Operand(type, (ulong)value);
}
} }
} }

View file

@ -16,6 +16,8 @@ namespace Ryujinx.Cpu.LightningJit
{ {
class Translator : IDisposable class Translator : IDisposable
{ {
private const bool UseSparseTable = true;
// Should be enabled on platforms that enforce W^X. // Should be enabled on platforms that enforce W^X.
private static bool IsNoWxPlatform => false; private static bool IsNoWxPlatform => false;
@ -38,6 +40,20 @@ namespace Ryujinx.Cpu.LightningJit
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 ConcurrentQueue<KeyValuePair<ulong, TranslatedFunction>> _oldFuncs; private readonly ConcurrentQueue<KeyValuePair<ulong, TranslatedFunction>> _oldFuncs;
private readonly NoWxCache _noWxCache; private readonly NoWxCache _noWxCache;
private bool _disposed; private bool _disposed;
@ -62,8 +78,19 @@ namespace Ryujinx.Cpu.LightningJit
JitCache.Initialize(new JitMemoryAllocator(forJit: true)); JitCache.Initialize(new JitMemoryAllocator(forJit: true));
} }
AddressTable<ulong>.Level[] levels;
if (UseSparseTable)
{
levels = for64Bits ? _levels64BitSparse : _levels32BitSparse;
}
else
{
levels = for64Bits ? _levels64Bit : _levels32Bit;
}
Functions = new TranslatorCache<TranslatedFunction>(); Functions = new TranslatorCache<TranslatedFunction>();
FunctionTable = new AddressTable<ulong>(for64Bits ? _levels64Bit : _levels32Bit); FunctionTable = new AddressTable<ulong>(levels);
Stubs = new TranslatorStubs(FunctionTable, _noWxCache); Stubs = new TranslatorStubs(FunctionTable, _noWxCache);
FunctionTable.Fill = (ulong)Stubs.SlowDispatchStub; FunctionTable.Fill = (ulong)Stubs.SlowDispatchStub;