Implement a new JIT for Arm devices (#6057)
* Implement a new JIT for Arm devices * Auto-format * Make a lot of Assembler members read-only * More read-only * Fix more warnings * ObjectDisposedException.ThrowIf * New JIT cache for platforms that enforce W^X, currently unused * Remove unused using * Fix assert * Pass memory manager type around * Safe memory manager mode support + other improvements * Actual safe memory manager mode masking support * PR feedback
This commit is contained in:
@@ -0,0 +1,133 @@
|
||||
using Ryujinx.Cpu.LightningJit.CodeGen;
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace Ryujinx.Cpu.LightningJit.Arm32.Target.Arm64
|
||||
{
|
||||
static class InstEmitVfpCompare
|
||||
{
|
||||
public static void VcmpI(CodeGenContext context, uint cond, uint rd, uint size)
|
||||
{
|
||||
EmitVcmpVcmpe(context, cond, rd, 0, size, zero: true, e: false);
|
||||
}
|
||||
|
||||
public static void VcmpR(CodeGenContext context, uint cond, uint rd, uint rm, uint size)
|
||||
{
|
||||
EmitVcmpVcmpe(context, cond, rd, rm, size, zero: false, e: false);
|
||||
}
|
||||
|
||||
public static void VcmpeI(CodeGenContext context, uint cond, uint rd, uint size)
|
||||
{
|
||||
EmitVcmpVcmpe(context, cond, rd, 0, size, zero: true, e: true);
|
||||
}
|
||||
|
||||
public static void VcmpeR(CodeGenContext context, uint cond, uint rd, uint rm, uint size)
|
||||
{
|
||||
EmitVcmpVcmpe(context, cond, rd, rm, size, zero: false, e: true);
|
||||
}
|
||||
|
||||
private static void EmitVcmpVcmpe(CodeGenContext context, uint cond, uint rd, uint rm, uint size, bool zero, bool e)
|
||||
{
|
||||
Debug.Assert(size == 1 || size == 2 || size == 3);
|
||||
|
||||
bool singleRegs = size != 3;
|
||||
uint ftype = size ^ 2u;
|
||||
uint opc = zero ? 1u : 0u;
|
||||
|
||||
using ScopedRegister rdReg = InstEmitNeonCommon.MoveScalarToSide(context, rd, singleRegs);
|
||||
ScopedRegister rmReg;
|
||||
Operand rmOrZero;
|
||||
|
||||
if (zero)
|
||||
{
|
||||
rmReg = default;
|
||||
rmOrZero = new Operand(0, RegisterType.Vector, OperandType.V128);
|
||||
}
|
||||
else
|
||||
{
|
||||
rmReg = InstEmitNeonCommon.MoveScalarToSide(context, rm, singleRegs);
|
||||
rmOrZero = rmReg.Operand;
|
||||
}
|
||||
|
||||
using ScopedRegister oldFlags = context.RegisterAllocator.AllocateTempGprRegisterScoped();
|
||||
|
||||
bool canPeepholeOptimize = CanFuseVcmpVmrs(context, cond);
|
||||
if (!canPeepholeOptimize)
|
||||
{
|
||||
InstEmitCommon.GetCurrentFlags(context, oldFlags.Operand);
|
||||
}
|
||||
|
||||
if (e)
|
||||
{
|
||||
context.Arm64Assembler.FcmpeFloat(rdReg.Operand, rmOrZero, opc, ftype);
|
||||
}
|
||||
else
|
||||
{
|
||||
context.Arm64Assembler.FcmpFloat(rdReg.Operand, rmOrZero, opc, ftype);
|
||||
}
|
||||
|
||||
// Save result flags from the FCMP operation on FPSCR register, then restore the old flags if needed.
|
||||
|
||||
WriteUpdateFpsrNzcv(context);
|
||||
|
||||
if (!canPeepholeOptimize)
|
||||
{
|
||||
InstEmitCommon.RestoreNzcvFlags(context, oldFlags.Operand);
|
||||
}
|
||||
|
||||
if (!zero)
|
||||
{
|
||||
rmReg.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
private static void WriteUpdateFpsrNzcv(CodeGenContext context)
|
||||
{
|
||||
using ScopedRegister fpsrRegister = context.RegisterAllocator.AllocateTempGprRegisterScoped();
|
||||
using ScopedRegister flagsRegister = context.RegisterAllocator.AllocateTempGprRegisterScoped();
|
||||
|
||||
Operand ctx = InstEmitSystem.Register(context.RegisterAllocator.FixedContextRegister);
|
||||
|
||||
context.Arm64Assembler.LdrRiUn(fpsrRegister.Operand, ctx, NativeContextOffsets.FpFlagsBaseOffset);
|
||||
|
||||
InstEmitCommon.GetCurrentFlags(context, flagsRegister.Operand);
|
||||
|
||||
context.Arm64Assembler.Bfi(fpsrRegister.Operand, flagsRegister.Operand, 28, 4);
|
||||
context.Arm64Assembler.StrRiUn(fpsrRegister.Operand, ctx, NativeContextOffsets.FpFlagsBaseOffset);
|
||||
}
|
||||
|
||||
private static bool CanFuseVcmpVmrs(CodeGenContext context, uint vcmpCond)
|
||||
{
|
||||
// Conditions might be different for the VCMP and VMRS instructions if they are inside a IT block,
|
||||
// we don't bother to check right now, so just always skip if inside an IT block.
|
||||
if (context.InITBlock)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
InstInfo nextInfo = context.PeekNextInstruction();
|
||||
|
||||
// We're looking for a VMRS instructions.
|
||||
if (nextInfo.Name != InstName.Vmrs)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Conditions must match.
|
||||
if (vcmpCond != (nextInfo.Encoding >> 28))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Reg must be 1, Rt must be PC indicating VMRS to PSTATE.NZCV.
|
||||
if (((nextInfo.Encoding >> 16) & 0xf) != 1 || ((nextInfo.Encoding >> 12) & 0xf) != RegisterUtils.PcRegister)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
context.SetSkipNextInstruction();
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user