diff options
Diffstat (limited to 'src/Ryujinx.Cpu/LightningJit/Arm32/Target/Arm64/InstEmitVfpCompare.cs')
| -rw-r--r-- | src/Ryujinx.Cpu/LightningJit/Arm32/Target/Arm64/InstEmitVfpCompare.cs | 133 |
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; + } + } +} |
