diff options
| author | LDj3SNuD <35856442+LDj3SNuD@users.noreply.github.com> | 2020-07-13 13:08:47 +0200 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2020-07-13 21:08:47 +1000 |
| commit | a804db6eed016a8a1f152c2837fc7b65e50f02df (patch) | |
| tree | 5ac490b42ba8fc2c22038bb784de4ea3fcda352f /Ryujinx.Tests | |
| parent | d7044b10a253dae31b9a0041a432e3a7adce59f6 (diff) | |
Add Fmax/minv_V & S/Ushl_S Inst.s with Tests. Fix Maxps/d & Minps/d d… (#1335)
* Add Fmax/minv_V & S/Ushl_S Inst.s with Tests. Fix Maxps/d & Minps/d double zero sign handling. Allows better handling of NaNs.
* Optimized EmitSse2VectorIsNaNOpF() for multiple uses per opF.
Diffstat (limited to 'Ryujinx.Tests')
| -rw-r--r-- | Ryujinx.Tests/Cpu/CpuTest.cs | 147 | ||||
| -rw-r--r-- | Ryujinx.Tests/Cpu/CpuTestMisc.cs | 123 | ||||
| -rw-r--r-- | Ryujinx.Tests/Cpu/CpuTestSimd.cs | 4 | ||||
| -rw-r--r-- | Ryujinx.Tests/Cpu/CpuTestSimdReg.cs | 57 |
4 files changed, 273 insertions, 58 deletions
diff --git a/Ryujinx.Tests/Cpu/CpuTest.cs b/Ryujinx.Tests/Cpu/CpuTest.cs index 2c047248..4f5fba9d 100644 --- a/Ryujinx.Tests/Cpu/CpuTest.cs +++ b/Ryujinx.Tests/Cpu/CpuTest.cs @@ -12,10 +12,14 @@ namespace Ryujinx.Tests.Cpu [TestFixture] public class CpuTest { - private ulong _currAddress; - private ulong _size; + protected const ulong Size = 0x1000; + protected const ulong CodeBaseAddress = 0x1000; + protected const ulong DataBaseAddress = CodeBaseAddress + Size; + + private const bool Ignore_FpcrFz_FpcrDn = false; + private const bool IgnoreAllExcept_FpsrQc = false; - private ulong _entryPoint; + private ulong _currAddress; private MemoryBlock _ram; @@ -28,6 +32,8 @@ namespace Ryujinx.Tests.Cpu private static bool _unicornAvailable; private UnicornAArch64 _unicornEmu; + private bool _usingMemory; + static CpuTest() { _unicornAvailable = UnicornAArch64.IsAvailable(); @@ -41,14 +47,11 @@ namespace Ryujinx.Tests.Cpu [SetUp] public void Setup() { - _currAddress = 0x1000; - _size = 0x1000; - - _entryPoint = _currAddress; + _currAddress = CodeBaseAddress; - _ram = new MemoryBlock(_size); - _memory = new MemoryManager(_ram, 1UL << 16); - _memory.Map(_currAddress, 0, _size); + _ram = new MemoryBlock(Size * 2); + _memory = new MemoryManager(_ram, 1ul << 16); + _memory.Map(CodeBaseAddress, 0, Size * 2); _context = CpuContext.CreateExecutionContext(); @@ -57,8 +60,9 @@ namespace Ryujinx.Tests.Cpu if (_unicornAvailable) { _unicornEmu = new UnicornAArch64(); - _unicornEmu.MemoryMap(_currAddress, _size, MemoryPermission.READ | MemoryPermission.EXEC); - _unicornEmu.PC = _entryPoint; + _unicornEmu.MemoryMap(CodeBaseAddress, Size, MemoryPermission.READ | MemoryPermission.EXEC); + _unicornEmu.MemoryMap(DataBaseAddress, Size, MemoryPermission.READ | MemoryPermission.WRITE); + _unicornEmu.PC = CodeBaseAddress; } } @@ -73,6 +77,8 @@ namespace Ryujinx.Tests.Cpu _context = null; _cpuContext = null; _unicornEmu = null; + + _usingMemory = false; } protected void Reset() @@ -169,11 +175,11 @@ namespace Ryujinx.Tests.Cpu protected void ExecuteOpcodes(bool runUnicorn = true) { - _cpuContext.Execute(_context, _entryPoint); + _cpuContext.Execute(_context, CodeBaseAddress); if (_unicornAvailable && runUnicorn) { - _unicornEmu.RunForCount((_currAddress - _entryPoint - 4) / 4); + _unicornEmu.RunForCount((_currAddress - CodeBaseAddress - 4) / 4); } } @@ -199,6 +205,11 @@ namespace Ryujinx.Tests.Cpu int fpsr = 0, bool runUnicorn = true) { + if (Ignore_FpcrFz_FpcrDn) + { + fpcr &= ~((int)FPCR.Fz | (int)FPCR.Dn); + } + Opcode(opcode); Opcode(0xD65F03C0); // RET SetContext(x0, x1, x2, x3, x31, v0, v1, v2, v3, v4, v5, v30, v31, overflow, carry, zero, negative, fpcr, fpsr); @@ -207,6 +218,30 @@ namespace Ryujinx.Tests.Cpu return GetContext(); } + protected void SetWorkingMemory(ulong offset, byte[] data) + { + _memory.Write(DataBaseAddress + offset, data); + + if (_unicornAvailable) + { + _unicornEmu.MemoryWrite(DataBaseAddress + offset, data); + } + + _usingMemory = true; // When true, CompareAgainstUnicorn checks the working memory for equality too. + } + + protected void SetWorkingMemory(ulong offset, byte data) + { + _memory.Write(DataBaseAddress + offset, data); + + if (_unicornAvailable) + { + _unicornEmu.MemoryWrite8(DataBaseAddress + offset, data); + } + + _usingMemory = true; // When true, CompareAgainstUnicorn checks the working memory for equality too. + } + /// <summary>Rounding Mode control field.</summary> public enum RMode { @@ -284,15 +319,20 @@ namespace Ryujinx.Tests.Cpu return; } + if (IgnoreAllExcept_FpsrQc) + { + fpsrMask &= Fpsr.Qc; + } + if (fpSkips != FpSkips.None) { ManageFpSkips(fpSkips); } - Assert.That(_context.GetX(0), Is.EqualTo(_unicornEmu.X[0])); - Assert.That(_context.GetX(1), Is.EqualTo(_unicornEmu.X[1])); - Assert.That(_context.GetX(2), Is.EqualTo(_unicornEmu.X[2])); - Assert.That(_context.GetX(3), Is.EqualTo(_unicornEmu.X[3])); + Assert.That(_context.GetX(0), Is.EqualTo(_unicornEmu.X[0]), "X0"); + Assert.That(_context.GetX(1), Is.EqualTo(_unicornEmu.X[1]), "X1"); + Assert.That(_context.GetX(2), Is.EqualTo(_unicornEmu.X[2]), "X2"); + Assert.That(_context.GetX(3), Is.EqualTo(_unicornEmu.X[3]), "X3"); Assert.That(_context.GetX(4), Is.EqualTo(_unicornEmu.X[4])); Assert.That(_context.GetX(5), Is.EqualTo(_unicornEmu.X[5])); Assert.That(_context.GetX(6), Is.EqualTo(_unicornEmu.X[6])); @@ -321,21 +361,21 @@ namespace Ryujinx.Tests.Cpu Assert.That(_context.GetX(29), Is.EqualTo(_unicornEmu.X[29])); Assert.That(_context.GetX(30), Is.EqualTo(_unicornEmu.X[30])); - Assert.That(_context.GetX(31), Is.EqualTo(_unicornEmu.SP)); + Assert.That(_context.GetX(31), Is.EqualTo(_unicornEmu.SP), "X31"); if (fpTolerances == FpTolerances.None) { - Assert.That(V128ToSimdValue(_context.GetV(0)), Is.EqualTo(_unicornEmu.Q[0])); + Assert.That(V128ToSimdValue(_context.GetV(0)), Is.EqualTo(_unicornEmu.Q[0]), "V0"); } else { ManageFpTolerances(fpTolerances); } - Assert.That(V128ToSimdValue(_context.GetV(1)), Is.EqualTo(_unicornEmu.Q[1])); - Assert.That(V128ToSimdValue(_context.GetV(2)), Is.EqualTo(_unicornEmu.Q[2])); - Assert.That(V128ToSimdValue(_context.GetV(3)), Is.EqualTo(_unicornEmu.Q[3])); - Assert.That(V128ToSimdValue(_context.GetV(4)), Is.EqualTo(_unicornEmu.Q[4])); - Assert.That(V128ToSimdValue(_context.GetV(5)), Is.EqualTo(_unicornEmu.Q[5])); + Assert.That(V128ToSimdValue(_context.GetV(1)), Is.EqualTo(_unicornEmu.Q[1]), "V1"); + Assert.That(V128ToSimdValue(_context.GetV(2)), Is.EqualTo(_unicornEmu.Q[2]), "V2"); + Assert.That(V128ToSimdValue(_context.GetV(3)), Is.EqualTo(_unicornEmu.Q[3]), "V3"); + Assert.That(V128ToSimdValue(_context.GetV(4)), Is.EqualTo(_unicornEmu.Q[4]), "V4"); + Assert.That(V128ToSimdValue(_context.GetV(5)), Is.EqualTo(_unicornEmu.Q[5]), "V5"); Assert.That(V128ToSimdValue(_context.GetV(6)), Is.EqualTo(_unicornEmu.Q[6])); Assert.That(V128ToSimdValue(_context.GetV(7)), Is.EqualTo(_unicornEmu.Q[7])); Assert.That(V128ToSimdValue(_context.GetV(8)), Is.EqualTo(_unicornEmu.Q[8])); @@ -360,16 +400,27 @@ namespace Ryujinx.Tests.Cpu Assert.That(V128ToSimdValue(_context.GetV(27)), Is.EqualTo(_unicornEmu.Q[27])); Assert.That(V128ToSimdValue(_context.GetV(28)), Is.EqualTo(_unicornEmu.Q[28])); Assert.That(V128ToSimdValue(_context.GetV(29)), Is.EqualTo(_unicornEmu.Q[29])); - Assert.That(V128ToSimdValue(_context.GetV(30)), Is.EqualTo(_unicornEmu.Q[30])); - Assert.That(V128ToSimdValue(_context.GetV(31)), Is.EqualTo(_unicornEmu.Q[31])); + Assert.That(V128ToSimdValue(_context.GetV(30)), Is.EqualTo(_unicornEmu.Q[30]), "V30"); + Assert.That(V128ToSimdValue(_context.GetV(31)), Is.EqualTo(_unicornEmu.Q[31]), "V31"); + + Assert.That((int)_context.Fpcr, Is.EqualTo(_unicornEmu.Fpcr), "Fpcr"); + Assert.That((int)_context.Fpsr & (int)fpsrMask, Is.EqualTo(_unicornEmu.Fpsr & (int)fpsrMask), "Fpsr"); - Assert.That((int)_context.Fpcr, Is.EqualTo(_unicornEmu.Fpcr)); - Assert.That((int)_context.Fpsr & (int)fpsrMask, Is.EqualTo(_unicornEmu.Fpsr & (int)fpsrMask)); + Assert.Multiple(() => + { + Assert.That(_context.GetPstateFlag(PState.VFlag), Is.EqualTo(_unicornEmu.OverflowFlag), "VFlag"); + Assert.That(_context.GetPstateFlag(PState.CFlag), Is.EqualTo(_unicornEmu.CarryFlag), "CFlag"); + Assert.That(_context.GetPstateFlag(PState.ZFlag), Is.EqualTo(_unicornEmu.ZeroFlag), "ZFlag"); + Assert.That(_context.GetPstateFlag(PState.NFlag), Is.EqualTo(_unicornEmu.NegativeFlag), "NFlag"); + }); + + if (_usingMemory) + { + byte[] mem = _memory.GetSpan(DataBaseAddress, (int)Size).ToArray(); + byte[] unicornMem = _unicornEmu.MemoryRead(DataBaseAddress, Size); - Assert.That(_context.GetPstateFlag(PState.VFlag), Is.EqualTo(_unicornEmu.OverflowFlag)); - Assert.That(_context.GetPstateFlag(PState.CFlag), Is.EqualTo(_unicornEmu.CarryFlag)); - Assert.That(_context.GetPstateFlag(PState.ZFlag), Is.EqualTo(_unicornEmu.ZeroFlag)); - Assert.That(_context.GetPstateFlag(PState.NFlag), Is.EqualTo(_unicornEmu.NegativeFlag)); + Assert.That(mem, Is.EqualTo(unicornMem), "Data"); + } } private void ManageFpSkips(FpSkips fpSkips) @@ -418,14 +469,17 @@ namespace Ryujinx.Tests.Cpu if (IsNormalOrSubnormalS(_unicornEmu.Q[0].AsFloat()) && IsNormalOrSubnormalS(_context.GetV(0).As<float>())) { - Assert.That (_context.GetV(0).Extract<float>(0), - Is.EqualTo(_unicornEmu.Q[0].GetFloat(0)).Within(1).Ulps); - Assert.That (_context.GetV(0).Extract<float>(1), - Is.EqualTo(_unicornEmu.Q[0].GetFloat(1)).Within(1).Ulps); - Assert.That (_context.GetV(0).Extract<float>(2), - Is.EqualTo(_unicornEmu.Q[0].GetFloat(2)).Within(1).Ulps); - Assert.That (_context.GetV(0).Extract<float>(3), - Is.EqualTo(_unicornEmu.Q[0].GetFloat(3)).Within(1).Ulps); + Assert.Multiple(() => + { + Assert.That (_context.GetV(0).Extract<float>(0), + Is.EqualTo(_unicornEmu.Q[0].GetFloat(0)).Within(1).Ulps, "V0[0]"); + Assert.That (_context.GetV(0).Extract<float>(1), + Is.EqualTo(_unicornEmu.Q[0].GetFloat(1)).Within(1).Ulps, "V0[1]"); + Assert.That (_context.GetV(0).Extract<float>(2), + Is.EqualTo(_unicornEmu.Q[0].GetFloat(2)).Within(1).Ulps, "V0[2]"); + Assert.That (_context.GetV(0).Extract<float>(3), + Is.EqualTo(_unicornEmu.Q[0].GetFloat(3)).Within(1).Ulps, "V0[3]"); + }); Console.WriteLine(fpTolerances); } @@ -440,10 +494,13 @@ namespace Ryujinx.Tests.Cpu if (IsNormalOrSubnormalD(_unicornEmu.Q[0].AsDouble()) && IsNormalOrSubnormalD(_context.GetV(0).As<double>())) { - Assert.That (_context.GetV(0).Extract<double>(0), - Is.EqualTo(_unicornEmu.Q[0].GetDouble(0)).Within(1).Ulps); - Assert.That (_context.GetV(0).Extract<double>(1), - Is.EqualTo(_unicornEmu.Q[0].GetDouble(1)).Within(1).Ulps); + Assert.Multiple(() => + { + Assert.That (_context.GetV(0).Extract<double>(0), + Is.EqualTo(_unicornEmu.Q[0].GetDouble(0)).Within(1).Ulps, "V0[0]"); + Assert.That (_context.GetV(0).Extract<double>(1), + Is.EqualTo(_unicornEmu.Q[0].GetDouble(1)).Within(1).Ulps, "V0[1]"); + }); Console.WriteLine(fpTolerances); } diff --git a/Ryujinx.Tests/Cpu/CpuTestMisc.cs b/Ryujinx.Tests/Cpu/CpuTestMisc.cs index 9b31e68e..9c067f4e 100644 --- a/Ryujinx.Tests/Cpu/CpuTestMisc.cs +++ b/Ryujinx.Tests/Cpu/CpuTestMisc.cs @@ -4,15 +4,67 @@ using ARMeilleure.State; using NUnit.Framework; +using System; +using System.Collections.Generic; + namespace Ryujinx.Tests.Cpu { [Category("Misc")] public sealed class CpuTestMisc : CpuTest { #if Misc + +#region "ValueSource (Types)" + private static IEnumerable<ulong> _1S_F_() + { + yield return 0x00000000FF7FFFFFul; // -Max Normal (float.MinValue) + yield return 0x0000000080800000ul; // -Min Normal + 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 (float.Epsilon) + + if (!NoZeros) + { + yield return 0x0000000080000000ul; // -Zero + yield return 0x0000000000000000ul; // +Zero + } + + if (!NoInfs) + { + yield return 0x00000000FF800000ul; // -Infinity + yield return 0x000000007F800000ul; // +Infinity + } + + if (!NoNaNs) + { + 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 = GenNormalS(); + ulong rnd2 = GenSubnormalS(); + + yield return (grbg << 32) | rnd1; + yield return (grbg << 32) | rnd2; + } + } +#endregion + private const int RndCnt = 2; private const int RndCntImm = 2; + private static readonly bool NoZeros = false; + private static readonly bool NoInfs = false; + private static readonly bool NoNaNs = false; + #region "AluImm & Csel" [Test, Pairwise] public void Adds_Csinc_64bit([Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, @@ -357,6 +409,77 @@ namespace Ryujinx.Tests.Cpu Assert.That(context.GetX(0), Is.EqualTo(a)); } + + [Explicit] + [Test, Pairwise] + public void Misc4([ValueSource("_1S_F_")] ulong a, + [ValueSource("_1S_F_")] ulong b, + [ValueSource("_1S_F_")] ulong c, + [Values(0ul, 1ul, 2ul, 3ul)] ulong displacement) + { + if (!BitConverter.IsLittleEndian) + { + Assert.Ignore(); + } + + for (ulong gapOffset = 0; gapOffset < displacement; gapOffset++) + { + SetWorkingMemory(gapOffset, TestContext.CurrentContext.Random.NextByte()); + } + + SetWorkingMemory(0x0 + displacement, BitConverter.GetBytes((uint)b)); + + SetWorkingMemory(0x4 + displacement, BitConverter.GetBytes((uint)c)); + + SetWorkingMemory(0x8 + displacement, TestContext.CurrentContext.Random.NextByte()); + SetWorkingMemory(0x9 + displacement, TestContext.CurrentContext.Random.NextByte()); + SetWorkingMemory(0xA + displacement, TestContext.CurrentContext.Random.NextByte()); + SetWorkingMemory(0xB + displacement, TestContext.CurrentContext.Random.NextByte()); + + SetContext( + x0: DataBaseAddress + displacement, + v0: MakeVectorE0E1(a, TestContext.CurrentContext.Random.NextULong()), + v1: MakeVectorE0E1(TestContext.CurrentContext.Random.NextULong(), TestContext.CurrentContext.Random.NextULong()), + v2: MakeVectorE0E1(TestContext.CurrentContext.Random.NextULong(), TestContext.CurrentContext.Random.NextULong()), + overflow: TestContext.CurrentContext.Random.NextBool(), + carry: TestContext.CurrentContext.Random.NextBool(), + zero: TestContext.CurrentContext.Random.NextBool(), + negative: TestContext.CurrentContext.Random.NextBool()); + + Opcode(0xBD400001); // LDR S1, [X0,#0] + Opcode(0xBD400402); // LDR S2, [X0,#4] + Opcode(0x1E215801); // FMIN S1, S0, S1 + Opcode(0x1E222000); // FCMP S0, S2 + Opcode(0x1E214C40); // FCSEL S0, S2, S1, MI + Opcode(0xBD000800); // STR S0, [X0,#8] + Opcode(0xD65F03C0); // RET + ExecuteOpcodes(); + + CompareAgainstUnicorn(); + } + + [Explicit] + [Test] + public void Misc5([ValueSource("_1S_F_")] ulong a) + { + SetContext( + v0: MakeVectorE0E1(a, TestContext.CurrentContext.Random.NextULong()), + v1: MakeVectorE0E1(TestContext.CurrentContext.Random.NextULong(), TestContext.CurrentContext.Random.NextULong()), + overflow: TestContext.CurrentContext.Random.NextBool(), + carry: TestContext.CurrentContext.Random.NextBool(), + zero: TestContext.CurrentContext.Random.NextBool(), + negative: TestContext.CurrentContext.Random.NextBool()); + + Opcode(0x1E202008); // FCMP S0, #0.0 + Opcode(0x1E2E1001); // FMOV S1, #1.0 + Opcode(0x1E215800); // FMIN S0, S0, S1 + Opcode(0x1E2703E1); // FMOV S1, WZR + Opcode(0x1E204C20); // FCSEL S0, S1, S0, MI + Opcode(0xD65F03C0); // RET + ExecuteOpcodes(); + + CompareAgainstUnicorn(); + } #endif } } diff --git a/Ryujinx.Tests/Cpu/CpuTestSimd.cs b/Ryujinx.Tests/Cpu/CpuTestSimd.cs index f8a61b15..249447d7 100644 --- a/Ryujinx.Tests/Cpu/CpuTestSimd.cs +++ b/Ryujinx.Tests/Cpu/CpuTestSimd.cs @@ -918,7 +918,9 @@ namespace Ryujinx.Tests.Cpu return new uint[] { 0x6E30C800u, // FMAXNMV S0, V0.4S - 0x6EB0C800u // FMINNMV S0, V0.4S + 0x6E30F800u, // FMAXV S0, V0.4S + 0x6EB0C800u, // FMINNMV S0, V0.4S + 0x6EB0F800u // FMINV S0, V0.4S }; } diff --git a/Ryujinx.Tests/Cpu/CpuTestSimdReg.cs b/Ryujinx.Tests/Cpu/CpuTestSimdReg.cs index a5458382..828c1bf9 100644 --- a/Ryujinx.Tests/Cpu/CpuTestSimdReg.cs +++ b/Ryujinx.Tests/Cpu/CpuTestSimdReg.cs @@ -373,12 +373,14 @@ namespace Ryujinx.Tests.Cpu { return new uint[] { - 0x0E20F400u, // FMAX V0.2S, V0.2S, V0.2S - 0x0E20C400u, // FMAXNM V0.2S, V0.2S, V0.2S - 0x2E20F400u, // FMAXP V0.2S, V0.2S, V0.2S - 0x0EA0F400u, // FMIN V0.2S, V0.2S, V0.2S - 0x0EA0C400u, // FMINNM V0.2S, V0.2S, V0.2S - 0x2EA0F400u // FMINP V0.2S, V0.2S, V0.2S + 0x0E20F400u, // FMAX V0.2S, V0.2S, V0.2S + 0x0E20C400u, // FMAXNM V0.2S, V0.2S, V0.2S + 0x2E20C400u, // FMAXNMP V0.2S, V0.2S, V0.2S + 0x2E20F400u, // FMAXP V0.2S, V0.2S, V0.2S + 0x0EA0F400u, // FMIN V0.2S, V0.2S, V0.2S + 0x0EA0C400u, // FMINNM V0.2S, V0.2S, V0.2S + 0x2EA0C400u, // FMINNMP V0.2S, V0.2S, V0.2S + 0x2EA0F400u // FMINP V0.2S, V0.2S, V0.2S }; } @@ -386,12 +388,14 @@ namespace Ryujinx.Tests.Cpu { return new uint[] { - 0x4E60F400u, // FMAX V0.2D, V0.2D, V0.2D - 0x4E60C400u, // FMAXNM V0.2D, V0.2D, V0.2D - 0x6E60F400u, // FMAXP V0.2D, V0.2D, V0.2D - 0x4EE0F400u, // FMIN V0.2D, V0.2D, V0.2D - 0x4EE0C400u, // FMINNM V0.2D, V0.2D, V0.2D - 0x6EE0F400u // FMINP V0.2D, V0.2D, V0.2D + 0x4E60F400u, // FMAX V0.2D, V0.2D, V0.2D + 0x4E60C400u, // FMAXNM V0.2D, V0.2D, V0.2D + 0x6E60C400u, // FMAXNMP V0.2D, V0.2D, V0.2D + 0x6E60F400u, // FMAXP V0.2D, V0.2D, V0.2D + 0x4EE0F400u, // FMIN V0.2D, V0.2D, V0.2D + 0x4EE0C400u, // FMINNM V0.2D, V0.2D, V0.2D + 0x6EE0C400u, // FMINNMP V0.2D, V0.2D, V0.2D + 0x6EE0F400u // FMINP V0.2D, V0.2D, V0.2D }; } @@ -531,6 +535,15 @@ namespace Ryujinx.Tests.Cpu }; } + private static uint[] _ShlReg_S_D_() + { + return new uint[] + { + 0x5EE04400u, // SSHL D0, D0, D0 + 0x7EE04400u // USHL D0, D0, D0 + }; + } + private static uint[] _ShlReg_V_8B_4H_2S_() { return new uint[] @@ -2821,6 +2834,26 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise] + public void ShlReg_S_D([ValueSource("_ShlReg_S_D_")] uint opcodes, + [Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [Values(2u, 0u)] uint rm, + [ValueSource("_1D_")] [Random(RndCnt)] ulong z, + [ValueSource("_1D_")] [Random(RndCnt)] ulong a, + [ValueSource("_1D_")] [Random(0ul, 255ul, RndCnt)] ulong b) + { + opcodes |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); + + V128 v0 = MakeVectorE0E1(z, z); + V128 v1 = MakeVectorE0(a); + V128 v2 = MakeVectorE0(b); + + SingleOpcode(opcodes, v0: v0, v1: v1, v2: v2); + + CompareAgainstUnicorn(fpsrMask: Fpsr.Qc); + } + + [Test, Pairwise] public void ShlReg_V_8B_4H_2S([ValueSource("_ShlReg_V_8B_4H_2S_")] uint opcodes, [Values(0u)] uint rd, [Values(1u, 0u)] uint rn, |
