diff --git a/ARMeilleure/Instructions/InstEmitAluHelper.cs b/ARMeilleure/Instructions/InstEmitAluHelper.cs index 64123d7dc2..664b8e0063 100644 --- a/ARMeilleure/Instructions/InstEmitAluHelper.cs +++ b/ARMeilleure/Instructions/InstEmitAluHelper.cs @@ -226,28 +226,49 @@ namespace ARMeilleure.Instructions switch (op.ShiftType) { - case ShiftType.Lsl: shiftResult = EmitLslC(context, m, setCarry, s); break; - case ShiftType.Lsr: shiftResult = EmitLsrC(context, m, setCarry, s); break; - case ShiftType.Asr: shiftResult = EmitAsrC(context, m, setCarry, s); break; - case ShiftType.Ror: shiftResult = EmitRorC(context, m, setCarry, s); break; + case ShiftType.Lsl: shiftResult = EmitLslC(context, m, setCarry, s, shiftIsZero); break; + case ShiftType.Lsr: shiftResult = EmitLsrC(context, m, setCarry, s, shiftIsZero); break; + case ShiftType.Asr: shiftResult = EmitAsrC(context, m, setCarry, s, shiftIsZero); break; + case ShiftType.Ror: shiftResult = EmitRorC(context, m, setCarry, s, shiftIsZero); break; } return context.ConditionalSelect(shiftIsZero, zeroResult, shiftResult); } - public static Operand EmitLslC(ArmEmitterContext context, Operand m, bool setCarry, Operand shift) + public static void EmitIfHelper(ArmEmitterContext context, Operand boolValue, Action action, bool expected = true) + { + Operand endLabel = Label(); + + if (expected) + { + context.BranchIfFalse(endLabel, boolValue); + } + else + { + context.BranchIfTrue(endLabel, boolValue); + } + + action(); + + context.MarkLabel(endLabel); + } + + public static Operand EmitLslC(ArmEmitterContext context, Operand m, bool setCarry, Operand shift, Operand shiftIsZero) { Operand shiftLarge = context.ICompareGreaterOrEqual(shift, Const(32)); Operand result = context.ShiftLeft(m, shift); if (setCarry) { - Operand cOut = context.ShiftRightUI(m, context.Subtract(Const(32), shift)); + EmitIfHelper(context, shiftIsZero, () => + { + Operand cOut = context.ShiftRightUI(m, context.Subtract(Const(32), shift)); - cOut = context.BitwiseAnd(cOut, Const(1)); - cOut = context.ConditionalSelect(context.ICompareGreater(shift, Const(32)), Const(0), cOut); + cOut = context.BitwiseAnd(cOut, Const(1)); + cOut = context.ConditionalSelect(context.ICompareGreater(shift, Const(32)), Const(0), cOut); - SetFlag(context, PState.CFlag, cOut); + SetFlag(context, PState.CFlag, cOut); + }, false); } return context.ConditionalSelect(shiftLarge, Const(0), result); @@ -283,18 +304,21 @@ namespace ARMeilleure.Instructions } } - public static Operand EmitLsrC(ArmEmitterContext context, Operand m, bool setCarry, Operand shift) + public static Operand EmitLsrC(ArmEmitterContext context, Operand m, bool setCarry, Operand shift, Operand shiftIsZero) { Operand shiftLarge = context.ICompareGreaterOrEqual(shift, Const(32)); Operand result = context.ShiftRightUI(m, shift); if (setCarry) { - Operand cOut = context.ShiftRightUI(m, context.Subtract(shift, Const(1))); + EmitIfHelper(context, shiftIsZero, () => + { + Operand cOut = context.ShiftRightUI(m, context.Subtract(shift, Const(1))); - cOut = context.BitwiseAnd(cOut, Const(1)); - cOut = context.ConditionalSelect(context.ICompareGreater(shift, Const(32)), Const(0), cOut); + cOut = context.BitwiseAnd(cOut, Const(1)); + cOut = context.ConditionalSelect(context.ICompareGreater(shift, Const(32)), Const(0), cOut); - SetFlag(context, PState.CFlag, cOut); + SetFlag(context, PState.CFlag, cOut); + }, false); } return context.ConditionalSelect(shiftLarge, Const(0), result); } @@ -335,7 +359,7 @@ namespace ARMeilleure.Instructions return Const(0); } - public static Operand EmitAsrC(ArmEmitterContext context, Operand m, bool setCarry, Operand shift) + public static Operand EmitAsrC(ArmEmitterContext context, Operand m, bool setCarry, Operand shift, Operand shiftIsZero) { Operand l32Result; Operand ge32Result; @@ -346,17 +370,23 @@ namespace ARMeilleure.Instructions if (setCarry) { - SetCarryMLsb(context, ge32Result); + EmitIfHelper(context, context.BitwiseOr(less32, shiftIsZero), () => + { + SetCarryMLsb(context, ge32Result); + }, false); } l32Result = context.ShiftRightSI(m, shift); if (setCarry) { - Operand cOut = context.ShiftRightUI(m, context.Subtract(shift, Const(1))); + EmitIfHelper(context, context.BitwiseAnd(less32, context.BitwiseNot(shiftIsZero)), () => + { + Operand cOut = context.ShiftRightUI(m, context.Subtract(shift, Const(1))); - cOut = context.BitwiseAnd(cOut, Const(1)); + cOut = context.BitwiseAnd(cOut, Const(1)); - SetFlag(context, PState.CFlag, cOut); + SetFlag(context, PState.CFlag, cOut); + }); } return context.ConditionalSelect(less32, l32Result, ge32Result); @@ -386,14 +416,17 @@ namespace ARMeilleure.Instructions } } - public static Operand EmitRorC(ArmEmitterContext context, Operand m, bool setCarry, Operand shift) + public static Operand EmitRorC(ArmEmitterContext context, Operand m, bool setCarry, Operand shift, Operand shiftIsZero) { shift = context.BitwiseAnd(shift, Const(0x1f)); m = context.RotateRight(m, shift); if (setCarry) { - SetCarryMMsb(context, m); + EmitIfHelper(context, shiftIsZero, () => + { + SetCarryMMsb(context, m); + }, false); } return m; } diff --git a/ARMeilleure/Instructions/InstEmitSimdArithmetic32.cs b/ARMeilleure/Instructions/InstEmitSimdArithmetic32.cs index a6bac7be8a..6cb4fdd161 100644 --- a/ARMeilleure/Instructions/InstEmitSimdArithmetic32.cs +++ b/ARMeilleure/Instructions/InstEmitSimdArithmetic32.cs @@ -40,12 +40,26 @@ namespace ARMeilleure.Instructions public static void Vadd_S(ArmEmitterContext context) { - EmitScalarBinaryOpF32(context, (op1, op2) => context.Add(op1, op2)); + if (Optimizations.FastFP) + { + EmitScalarBinaryOpF32(context, (op1, op2) => context.Add(op1, op2)); + } + else + { + EmitScalarBinaryOpF32(context, (op1, op2) => EmitSoftFloatCall(context, SoftFloat32.FPAdd, SoftFloat64.FPAdd, op1, op2)); + } } public static void Vadd_V(ArmEmitterContext context) { - EmitVectorBinaryOpF32(context, (op1, op2) => context.Add(op1, op2)); + if (Optimizations.FastFP) + { + EmitScalarBinaryOpF32(context, (op1, op2) => context.Add(op1, op2)); + } + else + { + EmitVectorBinaryOpF32(context, (op1, op2) => EmitSoftFloatCallDefaultFpscr(context, SoftFloat32.FPAddFpscr, SoftFloat64.FPAddFpscr, op1, op2)); + } } public static void Vadd_I(ArmEmitterContext context) diff --git a/ARMeilleure/Instructions/InstEmitSimdShift32.cs b/ARMeilleure/Instructions/InstEmitSimdShift32.cs index 5b77cbc094..cc951b2705 100644 --- a/ARMeilleure/Instructions/InstEmitSimdShift32.cs +++ b/ARMeilleure/Instructions/InstEmitSimdShift32.cs @@ -64,7 +64,7 @@ namespace ARMeilleure.Instructions Operand isOutOfRangeN = context.ICompareGreaterOrEqual(negShiftLsB, Const(8 << size)); //also zero if shift is too negative, but value was positive - isOutOfRange0 = context.BitwiseOr(isOutOfRange0, context.BitwiseAnd(isOutOfRangeN, context.ICompareGreaterOrEqual(op, Const(0)))); + isOutOfRange0 = context.BitwiseOr(isOutOfRange0, context.BitwiseAnd(isOutOfRangeN, context.ICompareGreaterOrEqual(op, Const(op.Type, 0)))); Operand min = (op.Type == OperandType.I64) ? Const(-1L) : Const(-1); diff --git a/ARMeilleure/Instructions/SoftFloat.cs b/ARMeilleure/Instructions/SoftFloat.cs index 03841f1097..38edbba300 100644 --- a/ARMeilleure/Instructions/SoftFloat.cs +++ b/ARMeilleure/Instructions/SoftFloat.cs @@ -623,9 +623,14 @@ namespace ARMeilleure.Instructions static class SoftFloat32 { public static float FPAdd(float value1, float value2) + { + return FPAddFpscr(value1, value2, false); + } + + public static float FPAddFpscr(float value1, float value2, bool standardFpscr) { ExecutionContext context = NativeInterface.GetContext(); - FPCR fpcr = context.Fpcr; + FPCR fpcr = standardFpscr ? context.StandardFpcrValue : context.Fpcr; value1 = value1.FPUnpack(out FPType type1, out bool sign1, out uint op1, context, fpcr); value2 = value2.FPUnpack(out FPType type2, out bool sign2, out uint op2, context, fpcr); @@ -1909,9 +1914,14 @@ namespace ARMeilleure.Instructions static class SoftFloat64 { public static double FPAdd(double value1, double value2) + { + return FPAddFpscr(value1, value2, false); + } + + public static double FPAddFpscr(double value1, double value2, bool standardFpscr) { ExecutionContext context = NativeInterface.GetContext(); - FPCR fpcr = context.Fpcr; + FPCR fpcr = standardFpscr ? context.StandardFpcrValue : context.Fpcr; value1 = value1.FPUnpack(out FPType type1, out bool sign1, out ulong op1, context, fpcr); value2 = value2.FPUnpack(out FPType type2, out bool sign2, out ulong op2, context, fpcr); diff --git a/Ryujinx.Tests/Cpu/CpuTestSimdReg32.cs b/Ryujinx.Tests/Cpu/CpuTestSimdReg32.cs index 5d2429a819..cf8f49d025 100644 --- a/Ryujinx.Tests/Cpu/CpuTestSimdReg32.cs +++ b/Ryujinx.Tests/Cpu/CpuTestSimdReg32.cs @@ -202,7 +202,7 @@ namespace Ryujinx.Tests.Cpu } #endregion - private const int RndCnt = 20; + private const int RndCnt = 5; private static readonly bool NoZeros = false; private static readonly bool NoInfs = false; @@ -210,34 +210,37 @@ namespace Ryujinx.Tests.Cpu [Test, Pairwise, Description("VADD.f32 V0, V0, V0")] public void Vadd_f32([Values(0u)] uint rd, - [Values(1u, 0u)] uint rn, - [Values(2u, 0u)] uint rm, - [ValueSource("_2S_F_")] [Random(RndCnt)] ulong z, - [ValueSource("_2S_F_")] [Random(RndCnt)] ulong a, - [ValueSource("_2S_F_")] [Random(RndCnt)] ulong b, + [Values(0u, 1u)] uint rn, + [Values(0u, 2u)] uint rm, + [ValueSource("_2S_F_")] ulong z0, + [ValueSource("_2S_F_")] ulong z1, + [ValueSource("_2S_F_")] ulong a0, + [ValueSource("_2S_F_")] ulong a1, + [ValueSource("_2S_F_")] ulong b0, + [ValueSource("_2S_F_")] ulong b1, [Values] bool q) { - uint opcode = 0xf2000d00; // VADD.f32 D0, D0, D0 + uint opcode = 0xf2000d00u; // VADD.F32 D0, D0, D0 if (q) { - rm &= 0x1e; - rn &= 0x1e; - rd &= 0x1e; + rm <<= 2; + rn <<= 2; + rd <<= 2; + + opcode |= 1 << 6; } - opcode |= ((rm & 0xf) << 0) | ((rm & 0x10) << 1); + opcode |= ((rm & 0xf) << 0) | ((rm & 0x10) << 1); opcode |= ((rd & 0xf) << 12) | ((rd & 0x10) << 18); opcode |= ((rn & 0xf) << 16) | ((rn & 0x10) << 3); - if (q) opcode |= 1 << 6; - - V128 v0 = MakeVectorE0E1(z, z); - V128 v1 = MakeVectorE0E1(a, z); - V128 v2 = MakeVectorE0E1(b, z); + V128 v0 = MakeVectorE0E1(z0, z1); + V128 v1 = MakeVectorE0E1(a0, a1); + V128 v2 = MakeVectorE0E1(b0, b1); SingleOpcode(opcode, v0: v0, v1: v1, v2: v2); - CompareAgainstUnicorn(fpTolerances: FpTolerances.UpToOneUlpsS); + CompareAgainstUnicorn(); } [Test, Pairwise, Description("VCMP.f Vd, Vm")] @@ -246,17 +249,18 @@ namespace Ryujinx.Tests.Cpu [ValueSource("_1S_F_")] ulong b, [Values] bool e) { - uint opcode = 0xeeb40840; + uint opcode = 0xeeb40840u; uint rm = 1; uint rd = 2; if (size == 3) { - opcode |= ((rm & 0xf) << 0) | ((rm & 0x10) << 1); + opcode |= ((rm & 0xf) << 0) | ((rm & 0x10) << 1); opcode |= ((rd & 0xf) << 12) | ((rd & 0x10) << 18); - } else + } + else { - opcode |= ((rm & 0x1e) >> 1) | ((rm & 0x1) << 5); + opcode |= ((rm & 0x1e) >> 1) | ((rm & 0x1) << 5); opcode |= ((rd & 0x1e) << 11) | ((rd & 0x1) << 22); } @@ -304,6 +308,8 @@ namespace Ryujinx.Tests.Cpu opcode |= ((rd & 0xf) << 12) | ((rd & 0x10) << 18); opcode |= ((rn & 0xf) << 16) | ((rn & 0x10) << 3); + opcode |= size << 20; + V128 v0 = MakeVectorE0E1(z, z); V128 v1 = MakeVectorE0E1(a, z); V128 v2 = MakeVectorE0E1(b, z); @@ -315,12 +321,13 @@ namespace Ryujinx.Tests.Cpu [Test, Combinatorial, Description("VPADD.f32 V0, V0, V0")] public void Vpadd_f32([Values(0u)] uint rd, - [Range(0u, 7u)] uint rn, - [Range(0u, 7u)] uint rm) + [Range(0u, 7u)] uint rn, + [Range(0u, 7u)] uint rm) { - uint opcode = 0xf3000d00; + // not currently a slow path test - just a sanity check for pairwise + uint opcode = 0xf3000d00u; - opcode |= ((rm & 0xf) << 0) | ((rm & 0x10) << 1); + opcode |= ((rm & 0xf) << 0) | ((rm & 0x10) << 1); opcode |= ((rd & 0xf) << 12) | ((rd & 0x10) << 18); opcode |= ((rn & 0xf) << 16) | ((rn & 0x10) << 3); @@ -331,7 +338,7 @@ namespace Ryujinx.Tests.Cpu SingleOpcode(opcode, v0: v0, v1: v1, v2: v2); - CompareAgainstUnicorn(fpTolerances: FpTolerances.UpToOneUlpsS); + CompareAgainstUnicorn(); } #endif }