Add VMOVN, VSHR (imm), VSHRN (imm) and related tests

This commit is contained in:
riperiperi 2020-01-29 13:57:55 +00:00
commit 87826b4ac9
9 changed files with 203 additions and 6 deletions

View file

@ -1,11 +1,11 @@
namespace ARMeilleure.Decoders namespace ARMeilleure.Decoders
{ {
class OpCode32SimdShift : OpCode32Simd class OpCode32SimdShImm : OpCode32Simd
{ {
public int Immediate { get; private set; } public int Immediate { get; private set; }
public int Shift { get; private set; } public int Shift { get; private set; }
public OpCode32SimdShift(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode) public OpCode32SimdShImm(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode)
{ {
Immediate = (opCode >> 16) & 0x3f; Immediate = (opCode >> 16) & 0x3f;
var limm = ((opCode >> 1) & 0x40) | Immediate; var limm = ((opCode >> 1) & 0x40) | Immediate;

View file

@ -844,6 +844,7 @@ namespace ARMeilleure.Decoders
SetA32("<<<<1110000xxxxxxxxx1010x0010000", InstName.Vmov, InstEmit32.Vmov_GS, typeof(OpCode32SimdMovGp)); // To/from gen purpose and single precision. SetA32("<<<<1110000xxxxxxxxx1010x0010000", InstName.Vmov, InstEmit32.Vmov_GS, typeof(OpCode32SimdMovGp)); // To/from gen purpose and single precision.
SetA32("<<<<1110xxx1xxxxxxxx1011xxx10000", InstName.Vmov, InstEmit32.Vmov_G1, typeof(OpCode32SimdMovGpElem)); // To gen purpose. SetA32("<<<<1110xxx1xxxxxxxx1011xxx10000", InstName.Vmov, InstEmit32.Vmov_G1, typeof(OpCode32SimdMovGpElem)); // To gen purpose.
SetA32("<<<<1100010xxxxxxxxx101000x1xxxx", InstName.Vmov, InstEmit32.Vmov_G2, typeof(OpCode32SimdMovGpDouble)); // To/from gen purpose x2 and single precision x2. SetA32("<<<<1100010xxxxxxxxx101000x1xxxx", InstName.Vmov, InstEmit32.Vmov_G2, typeof(OpCode32SimdMovGpDouble)); // To/from gen purpose x2 and single precision x2.
SetA32("111100111x11xx10xxxx001000x0xxx0", InstName.Vmovn, InstEmit32.Vmovn, typeof(OpCode32SimdCmpZ));
SetA32("<<<<11101111xxxxxxxx101000010000", InstName.Vmrs, InstEmit32.Vmrs, typeof(OpCode32SimdSpecial)); SetA32("<<<<11101111xxxxxxxx101000010000", InstName.Vmrs, InstEmit32.Vmrs, typeof(OpCode32SimdSpecial));
SetA32("<<<<11101110xxxxxxxx101000010000", InstName.Vmsr, InstEmit32.Vmsr, typeof(OpCode32SimdSpecial)); SetA32("<<<<11101110xxxxxxxx101000010000", InstName.Vmsr, InstEmit32.Vmsr, typeof(OpCode32SimdSpecial));
@ -876,7 +877,9 @@ namespace ARMeilleure.Decoders
SetA32("111111100xxxxxxxxxxx101xx0x0xxxx", InstName.Vsel, InstEmit32.Vsel, typeof(OpCode32SimdSel)); SetA32("111111100xxxxxxxxxxx101xx0x0xxxx", InstName.Vsel, InstEmit32.Vsel, typeof(OpCode32SimdSel));
SetA32("1111001x0xxxxxxxxxxx0100xxx0xxxx", InstName.Vshl, InstEmit32.Vshl_I, typeof(OpCode32SimdReg)); SetA32("1111001x0xxxxxxxxxxx0100xxx0xxxx", InstName.Vshl, InstEmit32.Vshl_I, typeof(OpCode32SimdReg));
SetA32("111100101x>>>xxxxxxx0101>xx1xxxx", InstName.Vshl, InstEmit32.Vshl, typeof(OpCode32SimdShift)); SetA32("111100101x>>>xxxxxxx0101>xx1xxxx", InstName.Vshl, InstEmit32.Vshl, typeof(OpCode32SimdShImm));
SetA32("1111001x1x>>>xxxxxxx0000>xx1xxxx", InstName.Vshr, InstEmit32.Vshr, typeof(OpCode32SimdShImm));
SetA32("111100101x>>>xxxxxxx100000x1xxx0", InstName.Vshrn, InstEmit32.Vshrn, typeof(OpCode32SimdShImm));
SetA32("111101001x00xxxxxxxx<<00xxxxxxxx", InstName.Vst1, InstEmit32.Vst1, typeof(OpCode32SimdMemSingle)); SetA32("111101001x00xxxxxxxx<<00xxxxxxxx", InstName.Vst1, InstEmit32.Vst1, typeof(OpCode32SimdMemSingle));
SetA32("111101000x00xxxxxxxx0111xxxxxxxx", InstName.Vst1, InstEmit32.Vst1, typeof(OpCode32SimdMemPair)); // Regs = 1. SetA32("111101000x00xxxxxxxx0111xxxxxxxx", InstName.Vst1, InstEmit32.Vst1, typeof(OpCode32SimdMemPair)); // Regs = 1.

View file

@ -160,6 +160,11 @@ namespace ARMeilleure.Instructions
EmitScalarUnaryOpF32(context, (op1) => op1); EmitScalarUnaryOpF32(context, (op1) => op1);
} }
public static void Vmovn(ArmEmitterContext context)
{
EmitVectorUnaryNarrowOp32(context, (op1) => op1);
}
public static void Vneg_S(ArmEmitterContext context) public static void Vneg_S(ArmEmitterContext context)
{ {
EmitScalarUnaryOpF32(context, (op1) => context.Negate(op1)); EmitScalarUnaryOpF32(context, (op1) => context.Negate(op1));

View file

@ -445,6 +445,27 @@ namespace ARMeilleure.Instructions
context.Copy(GetVecA32(op.Qd), res); context.Copy(GetVecA32(op.Qd), res);
} }
// Narrow
public static void EmitVectorUnaryNarrowOp32(ArmEmitterContext context, Func1I emit)
{
OpCode32Simd op = (OpCode32Simd)context.CurrOp;
int elems = 8 >> op.Size; // Size contains the target element size. (for when it becomes a doubleword)
Operand res = GetVecA32(op.Qd);
int id = (op.Vd & 1) << (3 - op.Size); // Target doubleword base.
for (int index = 0; index < elems; index++)
{
Operand m = EmitVectorExtract32(context, op.Qm, index, op.Size + 1, false);
res = EmitVectorInsert(context, res, emit(m), id + index, op.Size);
}
context.Copy(GetVecA32(op.Qd), res);
}
// Generic Functions // Generic Functions
public static Operand EmitSoftFloatCallDefaultFpscr( public static Operand EmitSoftFloatCallDefaultFpscr(

View file

@ -7,6 +7,7 @@ using static ARMeilleure.Instructions.InstEmitSimdHelper;
using static ARMeilleure.Instructions.InstEmitSimdHelper32; using static ARMeilleure.Instructions.InstEmitSimdHelper32;
using static ARMeilleure.IntermediateRepresentation.OperandHelper; using static ARMeilleure.IntermediateRepresentation.OperandHelper;
using System.Diagnostics; using System.Diagnostics;
using System;
namespace ARMeilleure.Instructions namespace ARMeilleure.Instructions
{ {
@ -14,9 +15,9 @@ namespace ARMeilleure.Instructions
{ {
public static void Vshl(ArmEmitterContext context) public static void Vshl(ArmEmitterContext context)
{ {
OpCode32SimdShift op = (OpCode32SimdShift)context.CurrOp; OpCode32SimdShImm op = (OpCode32SimdShImm)context.CurrOp;
EmitVectorUnaryOpZx32(context, (op1) => context.ShiftLeft(op1, Const(op1.Type, op.Shift))); EmitVectorUnaryOpZx32(context, (op1) => context.ShiftLeft(op1, Const(op.Shift)));
} }
public static void Vshl_I(ArmEmitterContext context) public static void Vshl_I(ArmEmitterContext context)
@ -33,6 +34,30 @@ namespace ARMeilleure.Instructions
} }
} }
public static void Vshr(ArmEmitterContext context)
{
OpCode32SimdShImm op = (OpCode32SimdShImm)context.CurrOp;
int shift = (8 << op.Size) - op.Shift; // Shr amount is flipped.
int maxShift = (8 << op.Size) - 1;
if (op.U)
{
EmitVectorUnaryOpZx32(context, (op1) => (shift > maxShift) ? Const(op1.Type, 0) : context.ShiftRightUI(op1, Const(shift)));
}
else
{
EmitVectorUnaryOpSx32(context, (op1) => context.ShiftRightSI(op1, Const(Math.Min(maxShift, shift))));
}
}
public static void Vshrn(ArmEmitterContext context)
{
OpCode32SimdShImm op = (OpCode32SimdShImm)context.CurrOp;
int shift = (8 << op.Size) - op.Shift; // Shr amount is flipped.
EmitVectorUnaryNarrowOp32(context, (op1) => context.ShiftRightUI(op1, Const(shift)));
}
private static Operand EmitShlRegOp(ArmEmitterContext context, Operand op, Operand shiftLsB, int size, bool unsigned) private static Operand EmitShlRegOp(ArmEmitterContext context, Operand op, Operand shiftLsB, int size, bool unsigned)
{ {
if (shiftLsB.Type == OperandType.I64) shiftLsB = context.ConvertI64ToI32(shiftLsB); if (shiftLsB.Type == OperandType.I64) shiftLsB = context.ConvertI64ToI32(shiftLsB);

View file

@ -549,6 +549,7 @@ namespace ARMeilleure.Instructions
Vmls, Vmls,
VMMmn, VMMmn,
Vmov, Vmov,
Vmovn,
Vmrs, Vmrs,
Vmsr, Vmsr,
Vmul, Vmul,
@ -563,6 +564,8 @@ namespace ARMeilleure.Instructions
Vrint, Vrint,
Vsel, Vsel,
Vshl, Vshl,
Vshr,
Vshrn,
Vst1, Vst1,
Vst2, Vst2,
Vst3, Vst3,

View file

@ -206,6 +206,30 @@ namespace Ryujinx.Tests.Cpu
CompareAgainstUnicorn(); CompareAgainstUnicorn();
} }
[Test, Combinatorial, Description("VMOVN.<size> <Dt>, <Qm>")]
public void Movn_V([Range(0u, 1u, 2u)] uint size,
[Values(0u, 1u, 2u, 3u)] uint vd,
[Values(0u, 2u, 4u, 8u)] uint vm)
{
uint opcode = 0xf3b20200u; // VMOVN.I16 D0, Q0
opcode |= (size & 0x3) << 18;
opcode |= ((vm & 0x10) << 1);
opcode |= ((vm & 0xf) << 0);
opcode |= ((vd & 0x10) << 18);
opcode |= ((vd & 0xf) << 12);
V128 v0 = new V128(TestContext.CurrentContext.Random.NextULong(), TestContext.CurrentContext.Random.NextULong());
V128 v1 = new V128(TestContext.CurrentContext.Random.NextULong(), TestContext.CurrentContext.Random.NextULong());
V128 v2 = new V128(TestContext.CurrentContext.Random.NextULong(), TestContext.CurrentContext.Random.NextULong());
V128 v3 = new V128(TestContext.CurrentContext.Random.NextULong(), TestContext.CurrentContext.Random.NextULong());
SingleOpcode(opcode, v0: v0, v1: v1, v2: v2, v3: v3);
CompareAgainstUnicorn();
}
[Test, Combinatorial, Description("VTRN.<size> <Vd>, <Vm>")] [Test, Combinatorial, Description("VTRN.<size> <Vd>, <Vm>")]
public void Vtrn([Values(0u, 1u, 2u, 3u)] uint vm, public void Vtrn([Values(0u, 1u, 2u, 3u)] uint vm,
[Values(0u, 1u, 2u, 3u)] uint vd, [Values(0u, 1u, 2u, 3u)] uint vd,

View file

@ -323,7 +323,7 @@ namespace Ryujinx.Tests.Cpu
SingleOpcode(opcode, v0: v0, v1: v1, v2: v2); SingleOpcode(opcode, v0: v0, v1: v1, v2: v2);
CompareAgainstUnicorn(fpTolerances: FpTolerances.UpToOneUlpsS); CompareAgainstUnicorn();
} }
[Test, Combinatorial, Description("VPADD.f32 V0, V0, V0")] [Test, Combinatorial, Description("VPADD.f32 V0, V0, V0")]

View file

@ -0,0 +1,116 @@
#define SimdShImm32
using ARMeilleure.State;
using NUnit.Framework;
namespace Ryujinx.Tests.Cpu
{
[Category("SimdShImm32")]
public sealed class CpuTestSimdShImm32 : CpuTest32
{
#if SimdShImm32
private const int RndCnt = 5;
[Test, Pairwise, Description("VSHL.<size> {<Vd>}, <Vm>, #<imm>")]
public void Vshl_Imm([Values(0u)] uint rd,
[Values(2u, 0u)] uint rm,
[Values(0u, 1u, 2u, 3u)] uint size,
[Random(RndCnt), Values(0u)] uint shiftImm,
[Random(RndCnt)] ulong z,
[Random(RndCnt)] ulong a,
[Random(RndCnt)] ulong b,
[Values] bool q)
{
uint opcode = 0xf2800510u; // VORR.I32 D0, #0 (immediate value changes it into SHL)
if (q)
{
opcode |= 1 << 6;
rm <<= 1;
rd <<= 1;
}
uint imm = 1u << ((int)size + 3);
imm |= shiftImm & (imm - 1);
opcode |= ((rm & 0xf) << 0) | ((rm & 0x10) << 1);
opcode |= ((rd & 0xf) << 12) | ((rd & 0x10) << 18);
opcode |= ((imm & 0x3f) << 16) | ((imm & 0x40) << 1);
V128 v0 = MakeVectorE0E1(z, z);
V128 v1 = MakeVectorE0E1(a, z);
V128 v2 = MakeVectorE0E1(b, z);
SingleOpcode(opcode, v0: v0, v1: v1, v2: v2);
CompareAgainstUnicorn();
}
[Test, Pairwise, Description("VSHR.<size> {<Vd>}, <Vm>, #<imm>")]
public void Vshr_Imm([Values(0u)] uint rd,
[Values(2u, 0u)] uint rm,
[Values(0u, 1u, 2u, 3u)] uint size,
[Random(RndCnt), Values(0u)] uint shiftImm,
[Random(RndCnt)] ulong z,
[Random(RndCnt)] ulong a,
[Random(RndCnt)] ulong b,
[Values] bool u,
[Values] bool q)
{
uint opcode = 0xf2800010u; // VMOV.I32 D0, #0 (immediate value changes it into SHR)
if (q)
{
opcode |= 1 << 6;
rm <<= 1;
rd <<= 1;
}
if (u)
{
opcode |= 1 << 24;
}
uint imm = 1u << ((int)size + 3);
imm |= shiftImm & (imm - 1);
opcode |= ((rm & 0xf) << 0) | ((rm & 0x10) << 1);
opcode |= ((rd & 0xf) << 12) | ((rd & 0x10) << 18);
opcode |= ((imm & 0x3f) << 16) | ((imm & 0x40) << 1);
V128 v0 = MakeVectorE0E1(z, z);
V128 v1 = MakeVectorE0E1(a, z);
V128 v2 = MakeVectorE0E1(b, z);
SingleOpcode(opcode, v0: v0, v1: v1, v2: v2);
CompareAgainstUnicorn();
}
[Test, Pairwise, Description("VSHRN.<size> {<Vd>}, <Vm>, #<imm>")]
public void Vshrn_Imm([Values(0u, 1u)] uint rd,
[Values(2u, 0u)] uint rm,
[Values(0u, 1u, 2u)] uint size,
[Random(RndCnt), Values(0u)] uint shiftImm,
[Random(RndCnt)] ulong z,
[Random(RndCnt)] ulong a,
[Random(RndCnt)] ulong b)
{
uint opcode = 0xf2800810u; // VMOV.I16 D0, #0 (immediate value changes it into SHRN)
uint imm = 1u << ((int)size + 3);
imm |= shiftImm & (imm - 1);
opcode |= ((rm & 0xf) << 0) | ((rm & 0x10) << 1);
opcode |= ((rd & 0xf) << 12) | ((rd & 0x10) << 18);
opcode |= ((imm & 0x3f) << 16);
V128 v0 = MakeVectorE0E1(z, z);
V128 v1 = MakeVectorE0E1(a, z);
V128 v2 = MakeVectorE0E1(b, z);
SingleOpcode(opcode, v0: v0, v1: v1, v2: v2);
CompareAgainstUnicorn();
}
#endif
}
}