From 5d258bf60250a6c5d3f3918a694de9b6c117948b Mon Sep 17 00:00:00 2001 From: LDj3SNuD <35856442+LDj3SNuD@users.noreply.github.com> Date: Fri, 28 Sep 2018 20:20:06 +0200 Subject: [PATCH] Update CpuTestSimdReg.cs --- Ryujinx.Tests/Cpu/CpuTestSimdReg.cs | 438 +++++++++++++++++++++++----- 1 file changed, 364 insertions(+), 74 deletions(-) diff --git a/Ryujinx.Tests/Cpu/CpuTestSimdReg.cs b/Ryujinx.Tests/Cpu/CpuTestSimdReg.cs index ae409a6d85..03a117994a 100644 --- a/Ryujinx.Tests/Cpu/CpuTestSimdReg.cs +++ b/Ryujinx.Tests/Cpu/CpuTestSimdReg.cs @@ -7,6 +7,8 @@ using NUnit.Framework; using System.Collections.Generic; using System.Runtime.Intrinsics; +using System; + namespace Ryujinx.Tests.Cpu { [Category("SimdReg")] // Tested: second half of 2018. @@ -80,14 +82,14 @@ namespace Ryujinx.Tests.Cpu private static IEnumerable _1S_F_() { - yield return 0x00000000FF7FFFFFul; // -Max Normal (float.MinValue) + yield return 0x00000000FF7FFFFFul; // -Max Normal (float.MinValue) yield return 0x0000000080800000ul; // -Min Normal - yield return 0x00000000807FFFFFul; // -Max SubNormal - yield return 0x0000000080000001ul; // -Min SubNormal - yield return 0x000000007F7FFFFFul; // +Max Normal (float.MaxValue) + yield return 0x00000000807FFFFFul; // -Max Subnormal + yield return 0x0000000080000001ul; // -Min Subnormal (-float.Epsilon) + yield return 0x000000007F7FFFFFul; // +Max Normal (float.MaxValue) yield return 0x0000000000800000ul; // +Min Normal - yield return 0x00000000007FFFFFul; // +Max SubNormal - yield return 0x0000000000000001ul; // +Min SubNormal + yield return 0x00000000007FFFFFul; // +Max Subnormal + yield return 0x0000000000000001ul; // +Min Subnormal (float.Epsilon) if (!NoZeros) { @@ -103,17 +105,17 @@ namespace Ryujinx.Tests.Cpu if (!NoNaNs) { - yield return 0x00000000FFFFFFFFul; // -QNaN (all ones payload) - yield return 0x00000000FFBFFFFFul; // -SNaN (all ones payload) - yield return 0x000000007FFFFFFFul; // +QNaN (all ones payload) - yield return 0x000000007FBFFFFFul; // +SNaN (all ones payload) + yield return 0x00000000FFC00000ul; // -QNaN (all zeros payload) (float.NaN) + yield return 0x00000000FFBFFFFFul; // -SNaN (all ones payload) + yield return 0x000000007FC00000ul; // +QNaN (all zeros payload) (-float.NaN) (DefaultNaN) + yield return 0x000000007FBFFFFFul; // +SNaN (all ones payload) } for (int Cnt = 1; Cnt <= RndCnt; Cnt++) { ulong Grbg = TestContext.CurrentContext.Random.NextUInt(); ulong Rnd1 = GenNormal_S(); - ulong Rnd2 = GenSubNormal_S(); + ulong Rnd2 = GenSubnormal_S(); yield return (Grbg << 32) | Rnd1; yield return (Grbg << 32) | Rnd2; @@ -122,14 +124,14 @@ namespace Ryujinx.Tests.Cpu private static IEnumerable _2S_F_() { - yield return 0xFF7FFFFFFF7FFFFFul; // -Max Normal (float.MinValue) + yield return 0xFF7FFFFFFF7FFFFFul; // -Max Normal (float.MinValue) yield return 0x8080000080800000ul; // -Min Normal - yield return 0x807FFFFF807FFFFFul; // -Max SubNormal - yield return 0x8000000180000001ul; // -Min SubNormal - yield return 0x7F7FFFFF7F7FFFFFul; // +Max Normal (float.MaxValue) + yield return 0x807FFFFF807FFFFFul; // -Max Subnormal + yield return 0x8000000180000001ul; // -Min Subnormal (-float.Epsilon) + yield return 0x7F7FFFFF7F7FFFFFul; // +Max Normal (float.MaxValue) yield return 0x0080000000800000ul; // +Min Normal - yield return 0x007FFFFF007FFFFFul; // +Max SubNormal - yield return 0x0000000100000001ul; // +Min SubNormal + yield return 0x007FFFFF007FFFFFul; // +Max Subnormal + yield return 0x0000000100000001ul; // +Min Subnormal (float.Epsilon) if (!NoZeros) { @@ -145,16 +147,16 @@ namespace Ryujinx.Tests.Cpu if (!NoNaNs) { - yield return 0xFFFFFFFFFFFFFFFFul; // -QNaN (all ones payload) - yield return 0xFFBFFFFFFFBFFFFFul; // -SNaN (all ones payload) - yield return 0x7FFFFFFF7FFFFFFFul; // +QNaN (all ones payload) - yield return 0x7FBFFFFF7FBFFFFFul; // +SNaN (all ones payload) + yield return 0xFFC00000FFC00000ul; // -QNaN (all zeros payload) (float.NaN) + yield return 0xFFBFFFFFFFBFFFFFul; // -SNaN (all ones payload) + yield return 0x7FC000007FC00000ul; // +QNaN (all zeros payload) (-float.NaN) (DefaultNaN) + yield return 0x7FBFFFFF7FBFFFFFul; // +SNaN (all ones payload) } for (int Cnt = 1; Cnt <= RndCnt; Cnt++) { ulong Rnd1 = GenNormal_S(); - ulong Rnd2 = GenSubNormal_S(); + ulong Rnd2 = GenSubnormal_S(); yield return (Rnd1 << 32) | Rnd1; yield return (Rnd2 << 32) | Rnd2; @@ -163,14 +165,14 @@ namespace Ryujinx.Tests.Cpu private static IEnumerable _1D_F_() { - yield return 0xFFEFFFFFFFFFFFFFul; // -Max Normal (double.MinValue) + yield return 0xFFEFFFFFFFFFFFFFul; // -Max Normal (double.MinValue) yield return 0x8010000000000000ul; // -Min Normal - yield return 0x800FFFFFFFFFFFFFul; // -Max SubNormal - yield return 0x8000000000000001ul; // -Min SubNormal - yield return 0x7FEFFFFFFFFFFFFFul; // +Max Normal (double.MaxValue) + yield return 0x800FFFFFFFFFFFFFul; // -Max Subnormal + yield return 0x8000000000000001ul; // -Min Subnormal (-double.Epsilon) + yield return 0x7FEFFFFFFFFFFFFFul; // +Max Normal (double.MaxValue) yield return 0x0010000000000000ul; // +Min Normal - yield return 0x000FFFFFFFFFFFFFul; // +Max SubNormal - yield return 0x0000000000000001ul; // +Min SubNormal + yield return 0x000FFFFFFFFFFFFFul; // +Max Subnormal + yield return 0x0000000000000001ul; // +Min Subnormal (double.Epsilon) if (!NoZeros) { @@ -186,16 +188,16 @@ namespace Ryujinx.Tests.Cpu if (!NoNaNs) { - yield return 0xFFFFFFFFFFFFFFFFul; // -QNaN (all ones payload) - yield return 0xFFF7FFFFFFFFFFFFul; // -SNaN (all ones payload) - yield return 0x7FFFFFFFFFFFFFFFul; // +QNaN (all ones payload) - yield return 0x7FF7FFFFFFFFFFFFul; // +SNaN (all ones payload) + yield return 0xFFF8000000000000ul; // -QNaN (all zeros payload) (double.NaN) + yield return 0xFFF7FFFFFFFFFFFFul; // -SNaN (all ones payload) + yield return 0x7FF8000000000000ul; // +QNaN (all zeros payload) (-double.NaN) (DefaultNaN) + yield return 0x7FF7FFFFFFFFFFFFul; // +SNaN (all ones payload) } for (int Cnt = 1; Cnt <= RndCnt; Cnt++) { ulong Rnd1 = GenNormal_D(); - ulong Rnd2 = GenSubNormal_D(); + ulong Rnd2 = GenSubnormal_D(); yield return Rnd1; yield return Rnd2; @@ -204,6 +206,68 @@ namespace Ryujinx.Tests.Cpu #endregion #region "ValueSource (Opcodes)" + private static uint[] _F_Add_Div_Mul_Sub_S_S_() + { + return new uint[] + { + 0x1E222820u, // FADD S0, S1, S2 + 0x1E221820u, // FDIV S0, S1, S2 + 0x1E220820u, // FMUL S0, S1, S2 + 0x1E223820u // FSUB S0, S1, S2 + }; + } + + private static uint[] _F_Add_Div_Mul_Sub_S_D_() + { + return new uint[] + { + 0x1E622820u, // FADD D0, D1, D2 + 0x1E621820u, // FDIV D0, D1, D2 + 0x1E620820u, // FMUL D0, D1, D2 + 0x1E623820u // FSUB D0, D1, D2 + }; + } + + private static uint[] _F_Add_Div_Mul_Sub_V_2S_4S_() + { + return new uint[] + { + 0x0E20D400u, // FADD V0.2S, V0.2S, V0.2S + 0x2E20FC00u, // FDIV V0.2S, V0.2S, V0.2S + 0x2E20DC00u, // FMUL V0.2S, V0.2S, V0.2S + 0x0EA0D400u // FSUB V0.2S, V0.2S, V0.2S + }; + } + + private static uint[] _F_Add_Div_Mul_Sub_V_2D_() + { + return new uint[] + { + 0x4E60D400u, // FADD V0.2D, V0.2D, V0.2D + 0x6E60FC00u, // FDIV V0.2D, V0.2D, V0.2D + 0x6E60DC00u, // FMUL V0.2D, V0.2D, V0.2D + 0x4EE0D400u // FSUB V0.2D, V0.2D, V0.2D + }; + } + + private static uint[] _Fmadd_Fmsub_S_S_() + { + return new uint[] + { + 0x1F020C20u, // FMADD S0, S1, S2, S3 + 0x1F028C20u // FMSUB S0, S1, S2, S3 + }; + } + + private static uint[] _Fmadd_Fmsub_S_D_() + { + return new uint[] + { + 0x1F420C20u, // FMADD D0, D1, D2, D3 + 0x1F428C20u // FMSUB D0, D1, D2, D3 + }; + } + private static uint[] _F_Max_Min_Nm_S_S_() { return new uint[] @@ -251,6 +315,42 @@ namespace Ryujinx.Tests.Cpu 0x6EE0F400u // FMINP V0.2D, V0.2D, V0.2D }; } + + private static uint[] _Frecps_Frsqrts_S_S_() + { + return new uint[] + { + 0x5E22FC20u, // FRECPS S0, S1, S2 + 0x5EA2FC20u // FRSQRTS S0, S1, S2 + }; + } + + private static uint[] _Frecps_Frsqrts_S_D_() + { + return new uint[] + { + 0x5E62FC20u, // FRECPS D0, D1, D2 + 0x5EE2FC20u // FRSQRTS D0, D1, D2 + }; + } + + private static uint[] _Frecps_Frsqrts_V_2S_4S_() + { + return new uint[] + { + 0x0E20FC00u, // FRECPS V0.2S, V0.2S, V0.2S + 0x0EA0FC00u // FRSQRTS V0.2S, V0.2S, V0.2S + }; + } + + private static uint[] _Frecps_Frsqrts_V_2D_() + { + return new uint[] + { + 0x4E60FC00u, // FRECPS V0.2D, V0.2D, V0.2D + 0x4EE0FC00u // FRSQRTS V0.2D, V0.2D, V0.2D + }; + } #endregion private const int RndCnt = 2; @@ -1035,82 +1135,178 @@ namespace Ryujinx.Tests.Cpu CompareAgainstUnicorn(); } - [Test, Pairwise, Description("FMADD , , , ")] - public void Fmadd_S_S([ValueSource("_1S_F_")] ulong A, - [ValueSource("_1S_F_")] ulong B, - [ValueSource("_1S_F_")] ulong C) + [Test, Pairwise] + public void F_Add_Div_Mul_Sub_S_S([ValueSource("_F_Add_Div_Mul_Sub_S_S_")] uint Opcodes, + [ValueSource("_1S_F_")] ulong A, + [ValueSource("_1S_F_")] ulong B, + [Values] RMode RMode) { - //const int DNFlagBit = 25; // Default NaN mode control bit. - //const int FZFlagBit = 24; // Flush-to-zero mode control bit. + ulong Z = TestContext.CurrentContext.Random.NextULong(); + Vector128 V0 = MakeVectorE0E1(Z, Z); + Vector128 V1 = MakeVectorE0(A); + Vector128 V2 = MakeVectorE0(B); - uint Opcode = 0x1F020C20; // FMADD S0, S1, S2, S3 + int Fpcr = (int)RMode << (int)FPCR.RMode; + Fpcr |= (int)TestContext.CurrentContext.Random.NextUInt() & (1 << (int)FPCR.DN); + AThreadState ThreadState = SingleOpcode(Opcodes, V0: V0, V1: V1, V2: V2, Fpcr: Fpcr); + + if (RMode == RMode.RN) CompareAgainstUnicorn(FPSR.IOC | FPSR.DZC); + else CompareAgainstUnicorn(FPSR.IOC | FPSR.DZC, FpSkips.IfUnderflow | FpSkips.IfOverflow, FpTolerances.UpToOneUlps_S); + } + + [Test, Pairwise] + public void F_Add_Div_Mul_Sub_S_D([ValueSource("_F_Add_Div_Mul_Sub_S_D_")] uint Opcodes, + [ValueSource("_1D_F_")] ulong A, + [ValueSource("_1D_F_")] ulong B, + [Values] RMode RMode) + { + ulong Z = TestContext.CurrentContext.Random.NextULong(); + Vector128 V0 = MakeVectorE1(Z); + Vector128 V1 = MakeVectorE0(A); + Vector128 V2 = MakeVectorE0(B); + + int Fpcr = (int)RMode << (int)FPCR.RMode; + Fpcr |= (int)TestContext.CurrentContext.Random.NextUInt() & (1 << (int)FPCR.DN); + + AThreadState ThreadState = SingleOpcode(Opcodes, V0: V0, V1: V1, V2: V2, Fpcr: Fpcr); + + if (RMode == RMode.RN) CompareAgainstUnicorn(FPSR.IOC | FPSR.DZC); + else CompareAgainstUnicorn(FPSR.IOC | FPSR.DZC, FpSkips.IfUnderflow | FpSkips.IfOverflow, FpTolerances.UpToOneUlps_D); + } + + [Test, Pairwise] + public void F_Add_Div_Mul_Sub_V_2S_4S([ValueSource("_F_Add_Div_Mul_Sub_V_2S_4S_")] uint Opcodes, + [Values(0u)] uint Rd, + [Values(1u, 0u)] uint Rn, + [Values(2u, 0u)] uint Rm, + [ValueSource("_2S_F_")] ulong Z, + [ValueSource("_2S_F_")] ulong A, + [ValueSource("_2S_F_")] ulong B, + [Values(0b0u, 0b1u)] uint Q, // <2S, 4S> + [Values] RMode RMode) + { + Opcodes |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); + Opcodes |= ((Q & 1) << 30); + + Vector128 V0 = MakeVectorE0E1(Z, Z); + Vector128 V1 = MakeVectorE0E1(A, A * Q); + Vector128 V2 = MakeVectorE0E1(B, B * Q); + + int Fpcr = (int)RMode << (int)FPCR.RMode; + Fpcr |= (int)TestContext.CurrentContext.Random.NextUInt() & (1 << (int)FPCR.DN); + + AThreadState ThreadState = SingleOpcode(Opcodes, V0: V0, V1: V1, V2: V2, Fpcr: Fpcr); + + if (RMode == RMode.RN) CompareAgainstUnicorn(FPSR.IOC | FPSR.DZC); + else CompareAgainstUnicorn(FPSR.IOC | FPSR.DZC, FpSkips.IfUnderflow | FpSkips.IfOverflow, FpTolerances.UpToOneUlps_S); + } + + [Test, Pairwise] + public void F_Add_Div_Mul_Sub_V_2D([ValueSource("_F_Add_Div_Mul_Sub_V_2D_")] uint Opcodes, + [Values(0u)] uint Rd, + [Values(1u, 0u)] uint Rn, + [Values(2u, 0u)] uint Rm, + [ValueSource("_1D_F_")] ulong Z, + [ValueSource("_1D_F_")] ulong A, + [ValueSource("_1D_F_")] ulong B, + [Values] RMode RMode) + { + Opcodes |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); + + Vector128 V0 = MakeVectorE0E1(Z, Z); + Vector128 V1 = MakeVectorE0E1(A, A); + Vector128 V2 = MakeVectorE0E1(B, B); + + int Fpcr = (int)RMode << (int)FPCR.RMode; + Fpcr |= (int)TestContext.CurrentContext.Random.NextUInt() & (1 << (int)FPCR.DN); + + AThreadState ThreadState = SingleOpcode(Opcodes, V0: V0, V1: V1, V2: V2, Fpcr: Fpcr); + + if (RMode == RMode.RN) CompareAgainstUnicorn(FPSR.IOC | FPSR.DZC); + else CompareAgainstUnicorn(FPSR.IOC | FPSR.DZC, FpSkips.IfUnderflow | FpSkips.IfOverflow, FpTolerances.UpToOneUlps_D); + } + + [Test, Pairwise] // Fused. + public void Fmadd_Fmsub_S_S([ValueSource("_Fmadd_Fmsub_S_S_")] uint Opcodes, + [ValueSource("_1S_F_")] ulong A, + [ValueSource("_1S_F_")] ulong B, + [ValueSource("_1S_F_")] ulong C, + [Values] RMode RMode) + { ulong Z = TestContext.CurrentContext.Random.NextULong(); Vector128 V0 = MakeVectorE0E1(Z, Z); Vector128 V1 = MakeVectorE0(A); Vector128 V2 = MakeVectorE0(B); Vector128 V3 = MakeVectorE0(C); - //int Fpcr = 1 << DNFlagBit; // Any operation involving one or more NaNs returns the Default NaN. - //Fpcr |= 1 << FZFlagBit; // Flush-to-zero mode enabled. + int Fpcr = (int)RMode << (int)FPCR.RMode; + Fpcr |= (int)TestContext.CurrentContext.Random.NextUInt() & (1 << (int)FPCR.DN); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2, V3: V3/*, Fpcr: Fpcr*/); + AThreadState ThreadState = SingleOpcode(Opcodes, V0: V0, V1: V1, V2: V2, V3: V3, Fpcr: Fpcr); - CompareAgainstUnicorn(/*FpsrMask: FPSR.IDC | FPSR.IOC, */FpSkips: FpSkips.IfNaN_S/*, FpUseTolerance: FpUseTolerance.OneUlps_S*/); + if (RMode == RMode.RN) CompareAgainstUnicorn(FPSR.IOC, FpSkips.IfUnderflow, FpTolerances.UpToOneUlps_S); + else CompareAgainstUnicorn(FPSR.IOC, FpSkips.IfUnderflow | FpSkips.IfOverflow, FpTolerances.UpToOneUlps_S); } - [Test, Pairwise, Description("FMADD
, , , ")] - public void Fmadd_S_D([ValueSource("_1D_F_")] ulong A, - [ValueSource("_1D_F_")] ulong B, - [ValueSource("_1D_F_")] ulong C) + [Test, Pairwise] // Fused. + public void Fmadd_Fmsub_S_D([ValueSource("_Fmadd_Fmsub_S_D_")] uint Opcodes, + [ValueSource("_1D_F_")] ulong A, + [ValueSource("_1D_F_")] ulong B, + [ValueSource("_1D_F_")] ulong C, + [Values] RMode RMode) { - uint Opcode = 0x1F420C20; // FMADD D0, D1, D2, D3 - ulong Z = TestContext.CurrentContext.Random.NextULong(); Vector128 V0 = MakeVectorE1(Z); Vector128 V1 = MakeVectorE0(A); Vector128 V2 = MakeVectorE0(B); Vector128 V3 = MakeVectorE0(C); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2, V3: V3); + int Fpcr = (int)RMode << (int)FPCR.RMode; + Fpcr |= (int)TestContext.CurrentContext.Random.NextUInt() & (1 << (int)FPCR.DN); - CompareAgainstUnicorn(FpSkips: FpSkips.IfNaN_D/*, FpUseTolerance: FpUseTolerance.OneUlps_D*/); + AThreadState ThreadState = SingleOpcode(Opcodes, V0: V0, V1: V1, V2: V2, V3: V3, Fpcr: Fpcr); + + if (RMode == RMode.RN) CompareAgainstUnicorn(FPSR.IOC, FpSkips.IfUnderflow, FpTolerances.UpToOneUlps_D); + else CompareAgainstUnicorn(FPSR.IOC, FpSkips.IfUnderflow | FpSkips.IfOverflow, FpTolerances.UpToOneUlps_D); } [Test, Pairwise] public void F_Max_Min_Nm_S_S([ValueSource("_F_Max_Min_Nm_S_S_")] uint Opcodes, [ValueSource("_1S_F_")] ulong A, - [ValueSource("_1S_F_")] ulong B) + [ValueSource("_1S_F_")] ulong B, + [Values] RMode RMode) { - //const int DNFlagBit = 25; // Default NaN mode control bit. - //const int FZFlagBit = 24; // Flush-to-zero mode control bit. - ulong Z = TestContext.CurrentContext.Random.NextULong(); Vector128 V0 = MakeVectorE0E1(Z, Z); Vector128 V1 = MakeVectorE0(A); Vector128 V2 = MakeVectorE0(B); - //int Fpcr = 1 << DNFlagBit; // Any operation involving one or more NaNs returns the Default NaN. - //Fpcr |= 1 << FZFlagBit; // Flush-to-zero mode enabled. + int Fpcr = (int)RMode << (int)FPCR.RMode; + Fpcr |= (int)TestContext.CurrentContext.Random.NextUInt() & (1 << (int)FPCR.DN); - AThreadState ThreadState = SingleOpcode(Opcodes, V0: V0, V1: V1, V2: V2/*, Fpcr: Fpcr*/); + AThreadState ThreadState = SingleOpcode(Opcodes, V0: V0, V1: V1, V2: V2, Fpcr: Fpcr); - CompareAgainstUnicorn(/*FpsrMask: FPSR.IDC | FPSR.IOC*/); + CompareAgainstUnicorn(FpsrMask: FPSR.IOC); } [Test, Pairwise] public void F_Max_Min_Nm_S_D([ValueSource("_F_Max_Min_Nm_S_D_")] uint Opcodes, [ValueSource("_1D_F_")] ulong A, - [ValueSource("_1D_F_")] ulong B) + [ValueSource("_1D_F_")] ulong B, + [Values] RMode RMode) { ulong Z = TestContext.CurrentContext.Random.NextULong(); Vector128 V0 = MakeVectorE1(Z); Vector128 V1 = MakeVectorE0(A); Vector128 V2 = MakeVectorE0(B); - AThreadState ThreadState = SingleOpcode(Opcodes, V0: V0, V1: V1, V2: V2); + int Fpcr = (int)RMode << (int)FPCR.RMode; + Fpcr |= (int)TestContext.CurrentContext.Random.NextUInt() & (1 << (int)FPCR.DN); - CompareAgainstUnicorn(); + AThreadState ThreadState = SingleOpcode(Opcodes, V0: V0, V1: V1, V2: V2, Fpcr: Fpcr); + + CompareAgainstUnicorn(FpsrMask: FPSR.IOC); } [Test, Pairwise] @@ -1121,11 +1317,9 @@ namespace Ryujinx.Tests.Cpu [ValueSource("_2S_F_")] ulong Z, [ValueSource("_2S_F_")] ulong A, [ValueSource("_2S_F_")] ulong B, - [Values(0b0u, 0b1u)] uint Q) // <2S, 4S> + [Values(0b0u, 0b1u)] uint Q, // <2S, 4S> + [Values] RMode RMode) { - //const int DNFlagBit = 25; // Default NaN mode control bit. - //const int FZFlagBit = 24; // Flush-to-zero mode control bit. - Opcodes |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); Opcodes |= ((Q & 1) << 30); @@ -1133,12 +1327,12 @@ namespace Ryujinx.Tests.Cpu Vector128 V1 = MakeVectorE0E1(A, A * Q); Vector128 V2 = MakeVectorE0E1(B, B * Q); - //int Fpcr = 1 << DNFlagBit; // Any operation involving one or more NaNs returns the Default NaN. - //Fpcr |= 1 << FZFlagBit; // Flush-to-zero mode enabled. + int Fpcr = (int)RMode << (int)FPCR.RMode; + Fpcr |= (int)TestContext.CurrentContext.Random.NextUInt() & (1 << (int)FPCR.DN); - AThreadState ThreadState = SingleOpcode(Opcodes, V0: V0, V1: V1, V2: V2/*, Fpcr: Fpcr*/); + AThreadState ThreadState = SingleOpcode(Opcodes, V0: V0, V1: V1, V2: V2, Fpcr: Fpcr); - CompareAgainstUnicorn(/*FpsrMask: FPSR.IDC | FPSR.IOC*/); + CompareAgainstUnicorn(FpsrMask: FPSR.IOC); } [Test, Pairwise] @@ -1148,7 +1342,8 @@ namespace Ryujinx.Tests.Cpu [Values(2u, 0u)] uint Rm, [ValueSource("_1D_F_")] ulong Z, [ValueSource("_1D_F_")] ulong A, - [ValueSource("_1D_F_")] ulong B) + [ValueSource("_1D_F_")] ulong B, + [Values] RMode RMode) { Opcodes |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); @@ -1156,9 +1351,104 @@ namespace Ryujinx.Tests.Cpu Vector128 V1 = MakeVectorE0E1(A, A); Vector128 V2 = MakeVectorE0E1(B, B); - AThreadState ThreadState = SingleOpcode(Opcodes, V0: V0, V1: V1, V2: V2); + int Fpcr = (int)RMode << (int)FPCR.RMode; + Fpcr |= (int)TestContext.CurrentContext.Random.NextUInt() & (1 << (int)FPCR.DN); - CompareAgainstUnicorn(); + AThreadState ThreadState = SingleOpcode(Opcodes, V0: V0, V1: V1, V2: V2, Fpcr: Fpcr); + + CompareAgainstUnicorn(FpsrMask: FPSR.IOC); + } + + [Test, Pairwise] // Fused. + public void Frecps_Frsqrts_S_S([ValueSource("_Frecps_Frsqrts_S_S_")] uint Opcodes, + [ValueSource("_1S_F_")] ulong A, + [ValueSource("_1S_F_")] ulong B, + [Values] RMode RMode) + { + ulong Z = TestContext.CurrentContext.Random.NextULong(); + Vector128 V0 = MakeVectorE0E1(Z, Z); + Vector128 V1 = MakeVectorE0(A); + Vector128 V2 = MakeVectorE0(B); + + int Fpcr = (int)RMode << (int)FPCR.RMode; + Fpcr |= (int)TestContext.CurrentContext.Random.NextUInt() & (1 << (int)FPCR.DN); + + AThreadState ThreadState = SingleOpcode(Opcodes, V0: V0, V1: V1, V2: V2, Fpcr: Fpcr); + + if (RMode == RMode.RN) CompareAgainstUnicorn(FPSR.IOC, FpSkips.IfUnderflow, FpTolerances.UpToOneUlps_S); + else CompareAgainstUnicorn(FPSR.IOC, FpSkips.IfUnderflow | FpSkips.IfOverflow, FpTolerances.UpToOneUlps_S); + } + + [Test, Pairwise] // Fused. + public void Frecps_Frsqrts_S_D([ValueSource("_Frecps_Frsqrts_S_D_")] uint Opcodes, + [ValueSource("_1D_F_")] ulong A, + [ValueSource("_1D_F_")] ulong B, + [Values] RMode RMode) + { + ulong Z = TestContext.CurrentContext.Random.NextULong(); + Vector128 V0 = MakeVectorE1(Z); + Vector128 V1 = MakeVectorE0(A); + Vector128 V2 = MakeVectorE0(B); + + int Fpcr = (int)RMode << (int)FPCR.RMode; + Fpcr |= (int)TestContext.CurrentContext.Random.NextUInt() & (1 << (int)FPCR.DN); + + AThreadState ThreadState = SingleOpcode(Opcodes, V0: V0, V1: V1, V2: V2, Fpcr: Fpcr); + + if (RMode == RMode.RN) CompareAgainstUnicorn(FPSR.IOC, FpSkips.IfUnderflow, FpTolerances.UpToOneUlps_D); + else CompareAgainstUnicorn(FPSR.IOC, FpSkips.IfUnderflow | FpSkips.IfOverflow, FpTolerances.UpToOneUlps_D); + } + + [Test, Pairwise] // Fused. + public void Frecps_Frsqrts_V_2S_4S([ValueSource("_Frecps_Frsqrts_V_2S_4S_")] uint Opcodes, + [Values(0u)] uint Rd, + [Values(1u, 0u)] uint Rn, + [Values(2u, 0u)] uint Rm, + [ValueSource("_2S_F_")] ulong Z, + [ValueSource("_2S_F_")] ulong A, + [ValueSource("_2S_F_")] ulong B, + [Values(0b0u, 0b1u)] uint Q, // <2S, 4S> + [Values] RMode RMode) + { + Opcodes |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); + Opcodes |= ((Q & 1) << 30); + + Vector128 V0 = MakeVectorE0E1(Z, Z); + Vector128 V1 = MakeVectorE0E1(A, A * Q); + Vector128 V2 = MakeVectorE0E1(B, B * Q); + + int Fpcr = (int)RMode << (int)FPCR.RMode; + Fpcr |= (int)TestContext.CurrentContext.Random.NextUInt() & (1 << (int)FPCR.DN); + + AThreadState ThreadState = SingleOpcode(Opcodes, V0: V0, V1: V1, V2: V2, Fpcr: Fpcr); + + if (RMode == RMode.RN) CompareAgainstUnicorn(FPSR.IOC, FpSkips.IfUnderflow, FpTolerances.UpToOneUlps_S); + else CompareAgainstUnicorn(FPSR.IOC, FpSkips.IfUnderflow | FpSkips.IfOverflow, FpTolerances.UpToOneUlps_S); + } + + [Test, Pairwise] // Fused. + public void Frecps_Frsqrts_V_2D([ValueSource("_Frecps_Frsqrts_V_2D_")] uint Opcodes, + [Values(0u)] uint Rd, + [Values(1u, 0u)] uint Rn, + [Values(2u, 0u)] uint Rm, + [ValueSource("_1D_F_")] ulong Z, + [ValueSource("_1D_F_")] ulong A, + [ValueSource("_1D_F_")] ulong B, + [Values] RMode RMode) + { + Opcodes |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); + + Vector128 V0 = MakeVectorE0E1(Z, Z); + Vector128 V1 = MakeVectorE0E1(A, A); + Vector128 V2 = MakeVectorE0E1(B, B); + + int Fpcr = (int)RMode << (int)FPCR.RMode; + Fpcr |= (int)TestContext.CurrentContext.Random.NextUInt() & (1 << (int)FPCR.DN); + + AThreadState ThreadState = SingleOpcode(Opcodes, V0: V0, V1: V1, V2: V2, Fpcr: Fpcr); + + if (RMode == RMode.RN) CompareAgainstUnicorn(FPSR.IOC, FpSkips.IfUnderflow, FpTolerances.UpToOneUlps_D); + else CompareAgainstUnicorn(FPSR.IOC, FpSkips.IfUnderflow | FpSkips.IfOverflow, FpTolerances.UpToOneUlps_D); } [Test, Pairwise, Description("ORN ., ., .")]