aboutsummaryrefslogtreecommitdiff
path: root/src/Ryujinx.Cpu/LightningJit/Arm32/Target/Arm64/InstEmitVfpCompare.cs
diff options
context:
space:
mode:
Diffstat (limited to 'src/Ryujinx.Cpu/LightningJit/Arm32/Target/Arm64/InstEmitVfpCompare.cs')
-rw-r--r--src/Ryujinx.Cpu/LightningJit/Arm32/Target/Arm64/InstEmitVfpCompare.cs133
1 files changed, 133 insertions, 0 deletions
diff --git a/src/Ryujinx.Cpu/LightningJit/Arm32/Target/Arm64/InstEmitVfpCompare.cs b/src/Ryujinx.Cpu/LightningJit/Arm32/Target/Arm64/InstEmitVfpCompare.cs
new file mode 100644
index 00000000..f5f30609
--- /dev/null
+++ b/src/Ryujinx.Cpu/LightningJit/Arm32/Target/Arm64/InstEmitVfpCompare.cs
@@ -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;
+ }
+ }
+}