mirror of
https://github.com/ryujinx-mirror/ryujinx.git
synced 2024-10-02 16:50:20 -07:00

* 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
134 lines
4.6 KiB
C#
134 lines
4.6 KiB
C#
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;
|
|
}
|
|
}
|
|
}
|