diff --git a/Ryujinx.Tests/Cpu/CpuTestAlu.cs b/Ryujinx.Tests/Cpu/CpuTestAlu.cs index 81fc265b..0c4aa3b0 100644 --- a/Ryujinx.Tests/Cpu/CpuTestAlu.cs +++ b/Ryujinx.Tests/Cpu/CpuTestAlu.cs @@ -2,19 +2,101 @@ using NUnit.Framework; +using System.Collections.Generic; + namespace Ryujinx.Tests.Cpu { [Category("Alu")] public sealed class CpuTestAlu : CpuTest { #if Alu + +#region "Helper methods" + private static uint GenLeadingSignsMinus32(int cnt) // 0 <= cnt <= 31 + { + return ~GenLeadingZeros32(cnt + 1); + } + + private static ulong GenLeadingSignsMinus64(int cnt) // 0 <= cnt <= 63 + { + return ~GenLeadingZeros64(cnt + 1); + } + + private static uint GenLeadingSignsPlus32(int cnt) // 0 <= cnt <= 31 + { + return GenLeadingZeros32(cnt + 1); + } + + private static ulong GenLeadingSignsPlus64(int cnt) // 0 <= cnt <= 63 + { + return GenLeadingZeros64(cnt + 1); + } + + private static uint GenLeadingZeros32(int cnt) // 0 <= cnt <= 32 + { + if (cnt == 32) return 0u; + if (cnt == 31) return 1u; + + uint rnd = TestContext.CurrentContext.Random.NextUInt(); + int mask = int.MinValue; + + return (rnd >> (cnt + 1)) | ((uint)mask >> cnt); + } + + private static ulong GenLeadingZeros64(int cnt) // 0 <= cnt <= 64 + { + if (cnt == 64) return 0ul; + if (cnt == 63) return 1ul; + + ulong rnd = TestContext.CurrentContext.Random.NextULong(); + long mask = long.MinValue; + + return (rnd >> (cnt + 1)) | ((ulong)mask >> cnt); + } +#endregion + +#region "ValueSource (Types)" + private static IEnumerable _GenLeadingSignsX_() + { + for (int cnt = 0; cnt <= 63; cnt++) + { + yield return GenLeadingSignsMinus64(cnt); + yield return GenLeadingSignsPlus64(cnt); + } + } + + private static IEnumerable _GenLeadingSignsW_() + { + for (int cnt = 0; cnt <= 31; cnt++) + { + yield return GenLeadingSignsMinus32(cnt); + yield return GenLeadingSignsPlus32(cnt); + } + } + + private static IEnumerable _GenLeadingZerosX_() + { + for (int cnt = 0; cnt <= 64; cnt++) + { + yield return GenLeadingZeros64(cnt); + } + } + + private static IEnumerable _GenLeadingZerosW_() + { + for (int cnt = 0; cnt <= 32; cnt++) + { + yield return GenLeadingZeros32(cnt); + } + } +#endregion + private const int RndCnt = 2; [Test, Pairwise, Description("CLS , ")] public void Cls_64bit([Values(0u, 31u)] uint rd, [Values(1u, 31u)] uint rn, - [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xn) + [ValueSource("_GenLeadingSignsX_")] [Random(RndCnt)] ulong xn) { uint opcode = 0xDAC01400; // CLS X0, X0 opcode |= ((rn & 31) << 5) | ((rd & 31) << 0); @@ -29,8 +111,7 @@ namespace Ryujinx.Tests.Cpu [Test, Pairwise, Description("CLS , ")] public void Cls_32bit([Values(0u, 31u)] uint rd, [Values(1u, 31u)] uint rn, - [Values(0x00000000u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wn) + [ValueSource("_GenLeadingSignsW_")] [Random(RndCnt)] uint wn) { uint opcode = 0x5AC01400; // CLS W0, W0 opcode |= ((rn & 31) << 5) | ((rd & 31) << 0); @@ -45,8 +126,7 @@ namespace Ryujinx.Tests.Cpu [Test, Pairwise, Description("CLZ , ")] public void Clz_64bit([Values(0u, 31u)] uint rd, [Values(1u, 31u)] uint rn, - [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xn) + [ValueSource("_GenLeadingZerosX_")] [Random(RndCnt)] ulong xn) { uint opcode = 0xDAC01000; // CLZ X0, X0 opcode |= ((rn & 31) << 5) | ((rd & 31) << 0); @@ -61,8 +141,7 @@ namespace Ryujinx.Tests.Cpu [Test, Pairwise, Description("CLZ , ")] public void Clz_32bit([Values(0u, 31u)] uint rd, [Values(1u, 31u)] uint rn, - [Values(0x00000000u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wn) + [ValueSource("_GenLeadingZerosW_")] [Random(RndCnt)] uint wn) { uint opcode = 0x5AC01000; // CLZ W0, W0 opcode |= ((rn & 31) << 5) | ((rd & 31) << 0); diff --git a/Ryujinx.Tests/Cpu/CpuTestMov.cs b/Ryujinx.Tests/Cpu/CpuTestMov.cs index a5ecafca..fa51c072 100644 --- a/Ryujinx.Tests/Cpu/CpuTestMov.cs +++ b/Ryujinx.Tests/Cpu/CpuTestMov.cs @@ -8,11 +8,12 @@ namespace Ryujinx.Tests.Cpu public sealed class CpuTestMov : CpuTest { #if Mov + private const int RndCnt = 2; private const int RndCntImm = 2; [Test, Pairwise, Description("MOVK , #{, LSL #}")] public void Movk_64bit([Values(0u, 31u)] uint rd, - [Random(RndCntImm)] ulong xd, + [Random(RndCnt)] ulong xd, [Values(0u, 65535u)] [Random(0u, 65535u, RndCntImm)] uint imm, [Values(0u, 16u, 32u, 48u)] uint shift) { @@ -29,7 +30,7 @@ namespace Ryujinx.Tests.Cpu [Test, Pairwise, Description("MOVK , #{, LSL #}")] public void Movk_32bit([Values(0u, 31u)] uint rd, - [Random(RndCntImm)] uint wd, + [Random(RndCnt)] uint wd, [Values(0u, 65535u)] [Random(0u, 65535u, RndCntImm)] uint imm, [Values(0u, 16u)] uint shift) { diff --git a/Ryujinx.Tests/Cpu/CpuTestSimd.cs b/Ryujinx.Tests/Cpu/CpuTestSimd.cs index 6125344c..c08cacac 100644 --- a/Ryujinx.Tests/Cpu/CpuTestSimd.cs +++ b/Ryujinx.Tests/Cpu/CpuTestSimd.cs @@ -13,6 +13,71 @@ namespace Ryujinx.Tests.Cpu { #if Simd +#region "Helper methods" + private static byte GenLeadingSignsMinus8(int cnt) // 0 <= cnt <= 7 + { + return (byte)(~(uint)GenLeadingZeros8(cnt + 1)); + } + + private static ushort GenLeadingSignsMinus16(int cnt) // 0 <= cnt <= 15 + { + return (ushort)(~(uint)GenLeadingZeros16(cnt + 1)); + } + + private static uint GenLeadingSignsMinus32(int cnt) // 0 <= cnt <= 31 + { + return ~GenLeadingZeros32(cnt + 1); + } + + private static byte GenLeadingSignsPlus8(int cnt) // 0 <= cnt <= 7 + { + return GenLeadingZeros8(cnt + 1); + } + + private static ushort GenLeadingSignsPlus16(int cnt) // 0 <= cnt <= 15 + { + return GenLeadingZeros16(cnt + 1); + } + + private static uint GenLeadingSignsPlus32(int cnt) // 0 <= cnt <= 31 + { + return GenLeadingZeros32(cnt + 1); + } + + private static byte GenLeadingZeros8(int cnt) // 0 <= cnt <= 8 + { + if (cnt == 8) return (byte)0; + if (cnt == 7) return (byte)1; + + byte rnd = TestContext.CurrentContext.Random.NextByte(); + sbyte mask = sbyte.MinValue; + + return (byte)(((uint)rnd >> (cnt + 1)) | ((uint)((byte)mask) >> cnt)); + } + + private static ushort GenLeadingZeros16(int cnt) // 0 <= cnt <= 16 + { + if (cnt == 16) return (ushort)0; + if (cnt == 15) return (ushort)1; + + ushort rnd = TestContext.CurrentContext.Random.NextUShort(); + short mask = short.MinValue; + + return (ushort)(((uint)rnd >> (cnt + 1)) | ((uint)((ushort)mask) >> cnt)); + } + + private static uint GenLeadingZeros32(int cnt) // 0 <= cnt <= 32 + { + if (cnt == 32) return 0u; + if (cnt == 31) return 1u; + + uint rnd = TestContext.CurrentContext.Random.NextUInt(); + int mask = int.MinValue; + + return (rnd >> (cnt + 1)) | ((uint)mask >> cnt); + } +#endregion + #region "ValueSource (Types)" private static ulong[] _1B1H1S1D_() { @@ -90,6 +155,75 @@ namespace Ryujinx.Tests.Cpu 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul }; } + private static IEnumerable _GenLeadingSigns8B_() + { + for (int cnt = 0; cnt <= 7; cnt++) + { + ulong rnd1 = GenLeadingSignsMinus8(cnt); + ulong rnd2 = GenLeadingSignsPlus8(cnt); + + yield return (rnd1 << 56) | (rnd1 << 48) | (rnd1 << 40) | (rnd1 << 32) | + (rnd1 << 24) | (rnd1 << 16) | (rnd1 << 08) | rnd1; + yield return (rnd2 << 56) | (rnd2 << 48) | (rnd2 << 40) | (rnd2 << 32) | + (rnd2 << 24) | (rnd2 << 16) | (rnd2 << 08) | rnd2; + } + } + + private static IEnumerable _GenLeadingSigns4H_() + { + for (int cnt = 0; cnt <= 15; cnt++) + { + ulong rnd1 = GenLeadingSignsMinus16(cnt); + ulong rnd2 = GenLeadingSignsPlus16(cnt); + + yield return (rnd1 << 48) | (rnd1 << 32) | (rnd1 << 16) | rnd1; + yield return (rnd2 << 48) | (rnd2 << 32) | (rnd2 << 16) | rnd2; + } + } + + private static IEnumerable _GenLeadingSigns2S_() + { + for (int cnt = 0; cnt <= 31; cnt++) + { + ulong rnd1 = GenLeadingSignsMinus32(cnt); + ulong rnd2 = GenLeadingSignsPlus32(cnt); + + yield return (rnd1 << 32) | rnd1; + yield return (rnd2 << 32) | rnd2; + } + } + + private static IEnumerable _GenLeadingZeros8B_() + { + for (int cnt = 0; cnt <= 8; cnt++) + { + ulong rnd = GenLeadingZeros8(cnt); + + yield return (rnd << 56) | (rnd << 48) | (rnd << 40) | (rnd << 32) | + (rnd << 24) | (rnd << 16) | (rnd << 08) | rnd; + } + } + + private static IEnumerable _GenLeadingZeros4H_() + { + for (int cnt = 0; cnt <= 16; cnt++) + { + ulong rnd = GenLeadingZeros16(cnt); + + yield return (rnd << 48) | (rnd << 32) | (rnd << 16) | rnd; + } + } + + private static IEnumerable _GenLeadingZeros2S_() + { + for (int cnt = 0; cnt <= 32; cnt++) + { + ulong rnd = GenLeadingZeros32(cnt); + + yield return (rnd << 32) | rnd; + } + } + private static IEnumerable _1H_F_() { yield return 0x000000000000FBFFul; // -Max Normal @@ -1034,18 +1168,18 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("CLS ., .")] - public void Cls_V_8B_4H_2S([Values(0u)] uint rd, - [Values(1u, 0u)] uint rn, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong z, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong a, - [Values(0b00u, 0b01u, 0b10u)] uint size) // <8B, 4H, 2S> + public void Cls_V_8B_16B([Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [ValueSource("_GenLeadingSigns8B_")] [Random(RndCnt)] ulong z, + [ValueSource("_GenLeadingSigns8B_")] [Random(RndCnt)] ulong a, + [Values(0b0u, 0b1u)] uint q) // <8B, 16B> { uint opcode = 0x0E204800; // CLS V0.8B, V0.8B opcode |= ((rn & 31) << 5) | ((rd & 31) << 0); - opcode |= ((size & 3) << 22); + opcode |= ((q & 1) << 30); Vector128 v0 = MakeVectorE0E1(z, z); - Vector128 v1 = MakeVectorE0(a); + Vector128 v1 = MakeVectorE0E1(a, a * q); SingleOpcode(opcode, v0: v0, v1: v1); @@ -1053,18 +1187,37 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("CLS ., .")] - public void Cls_V_16B_8H_4S([Values(0u)] uint rd, - [Values(1u, 0u)] uint rn, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong z, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong a, - [Values(0b00u, 0b01u, 0b10u)] uint size) // <16B, 8H, 4S> + public void Cls_V_4H_8H([Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [ValueSource("_GenLeadingSigns4H_")] [Random(RndCnt)] ulong z, + [ValueSource("_GenLeadingSigns4H_")] [Random(RndCnt)] ulong a, + [Values(0b0u, 0b1u)] uint q) // <4H, 8H> { - uint opcode = 0x4E204800; // CLS V0.16B, V0.16B + uint opcode = 0x0E604800; // CLS V0.4H, V0.4H opcode |= ((rn & 31) << 5) | ((rd & 31) << 0); - opcode |= ((size & 3) << 22); + opcode |= ((q & 1) << 30); Vector128 v0 = MakeVectorE0E1(z, z); - Vector128 v1 = MakeVectorE0E1(a, a); + Vector128 v1 = MakeVectorE0E1(a, a * q); + + SingleOpcode(opcode, v0: v0, v1: v1); + + CompareAgainstUnicorn(); + } + + [Test, Pairwise, Description("CLS ., .")] + public void Cls_V_2S_4S([Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [ValueSource("_GenLeadingSigns2S_")] [Random(RndCnt)] ulong z, + [ValueSource("_GenLeadingSigns2S_")] [Random(RndCnt)] ulong a, + [Values(0b0u, 0b1u)] uint q) // <2S, 4S> + { + uint opcode = 0x0EA04800; // CLS V0.2S, V0.2S + opcode |= ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((q & 1) << 30); + + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0E1(a, a * q); SingleOpcode(opcode, v0: v0, v1: v1); @@ -1072,18 +1225,18 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("CLZ ., .")] - public void Clz_V_8B_4H_2S([Values(0u)] uint rd, - [Values(1u, 0u)] uint rn, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong z, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong a, - [Values(0b00u, 0b01u, 0b10u)] uint size) // <8B, 4H, 2S> + public void Clz_V_8B_16B([Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [ValueSource("_GenLeadingZeros8B_")] [Random(RndCnt)] ulong z, + [ValueSource("_GenLeadingZeros8B_")] [Random(RndCnt)] ulong a, + [Values(0b0u, 0b1u)] uint q) // <8B, 16B> { uint opcode = 0x2E204800; // CLZ V0.8B, V0.8B opcode |= ((rn & 31) << 5) | ((rd & 31) << 0); - opcode |= ((size & 3) << 22); + opcode |= ((q & 1) << 30); Vector128 v0 = MakeVectorE0E1(z, z); - Vector128 v1 = MakeVectorE0(a); + Vector128 v1 = MakeVectorE0E1(a, a * q); SingleOpcode(opcode, v0: v0, v1: v1); @@ -1091,18 +1244,37 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("CLZ ., .")] - public void Clz_V_16B_8H_4S([Values(0u)] uint rd, - [Values(1u, 0u)] uint rn, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong z, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong a, - [Values(0b00u, 0b01u, 0b10u)] uint size) // <16B, 8H, 4S> + public void Clz_V_4H_8H([Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [ValueSource("_GenLeadingZeros4H_")] [Random(RndCnt)] ulong z, + [ValueSource("_GenLeadingZeros4H_")] [Random(RndCnt)] ulong a, + [Values(0b0u, 0b1u)] uint q) // <4H, 8H> { - uint opcode = 0x6E204800; // CLZ V0.16B, V0.16B + uint opcode = 0x2E604800; // CLZ V0.4H, V0.4H opcode |= ((rn & 31) << 5) | ((rd & 31) << 0); - opcode |= ((size & 3) << 22); + opcode |= ((q & 1) << 30); Vector128 v0 = MakeVectorE0E1(z, z); - Vector128 v1 = MakeVectorE0E1(a, a); + Vector128 v1 = MakeVectorE0E1(a, a * q); + + SingleOpcode(opcode, v0: v0, v1: v1); + + CompareAgainstUnicorn(); + } + + [Test, Pairwise, Description("CLZ ., .")] + public void Clz_V_2S_4S([Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [ValueSource("_GenLeadingZeros2S_")] [Random(RndCnt)] ulong z, + [ValueSource("_GenLeadingZeros2S_")] [Random(RndCnt)] ulong a, + [Values(0b0u, 0b1u)] uint q) // <2S, 4S> + { + uint opcode = 0x2EA04800; // CLZ V0.2S, V0.2S + opcode |= ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((q & 1) << 30); + + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0E1(a, a * q); SingleOpcode(opcode, v0: v0, v1: v1);