Fix way too many instruction bugs, add tests for some of the more important ones.

This commit is contained in:
riperiperi 2020-01-16 01:53:46 +00:00
parent 012fcc6c06
commit 85a8d1bb9f
21 changed files with 637 additions and 108 deletions

View file

@ -25,7 +25,7 @@ namespace ARMeilleure.Decoders
Size = 0;
Index = (opc >> 1) & 0x7;
}
else if ((opc & 0b11) != 0b10)
else if ((opc & 0b11) == 0b10)
{
Size = 1;
Index = (opc >> 2) & 0x3;
@ -43,6 +43,7 @@ namespace ARMeilleure.Decoders
Vd = ((opCode >> 18) & 0x10) | ((opCode >> 12) & 0xf);
Vm = ((opCode >> 1) & 0x10) | ((opCode >> 0) & 0xf);
Q = (opCode & (1 << 6)) != 0;
if (Q) RegisterSize = RegisterSize.Simd128;
}
}
}

View file

@ -21,7 +21,7 @@ namespace ARMeilleure.Decoders
Rt = (opCode >> 12) & 0xf;
Rt2 = (opCode >> 16) & 0xf;
bool single = (opCode & (1 << 8)) != 0;
bool single = (opCode & (1 << 8)) == 0;
if (single)
{
Vm = ((opCode >> 5) & 0x1) | ((opCode << 1) & 0x1e);

View file

@ -22,19 +22,19 @@ namespace ARMeilleure.Decoders
Op = ((opCode >> 20) & 0x1);
U = ((opCode >> 23) & 1) != 0;
var opc = (((opCode >> 21) & 0x3) << 2) | ((opCode >> 5) & 0x3);
var opc = (((opCode >> 23) & 1) << 4) | (((opCode >> 21) & 0x3) << 2) | ((opCode >> 5) & 0x3);
if ((opc & 0x8) != 0)
if ((opc & 0b01000) == 0b01000)
{
Size = 0;
Index = opc & 0x7;
}
else if ((opc & 0x1) != 0)
else if ((opc & 0b01001) == 0b00001)
{
Size = 1;
Index = (opc >> 1) & 0x3;
}
else if ((opc & 0x2) == 0)
else if ((opc & 0b11011) == 0)
{
Size = 2;
Index = (opc >> 2) & 0x1;

View file

@ -0,0 +1,18 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace ARMeilleure.Decoders
{
class OpCode32SimdRev : OpCode32SimdCmpZ
{
public OpCode32SimdRev(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode)
{
// Currently, this instruction is treated as though it's OPCODE is the true size.
// which lets us deal with reversing vectors on a single element basis (eg. math magic an I64 rather than insert lots of I8s)
int tempSize = Size;
Size = 3 - Opc; //op 0 is 64 bit, 1 is 32 and so on.
Opc = tempSize;
}
}
}

View file

@ -631,6 +631,7 @@ namespace ARMeilleure.Decoders
SetA32("<<<<0010001xxxxxxxxxxxxxxxxxxxxx", InstName.Eor, InstEmit32.Eor, typeof(OpCode32AluImm));
SetA32("<<<<0000001xxxxxxxxxxxxxxxx0xxxx", InstName.Eor, InstEmit32.Eor, typeof(OpCode32AluRsImm));
SetA32("<<<<0000001xxxxxxxxxxxxx0xx1xxxx", InstName.Eor, InstEmit32.Eor, typeof(OpCode32AluRsReg));
SetA32("1111010101111111111100000110xxxx", InstName.Isb, InstEmit32.Nop, typeof(OpCode32));
SetA32("<<<<00011001xxxxxxxx110010011111", InstName.Lda, InstEmit32.Lda, typeof(OpCode32MemLdEx));
SetA32("<<<<00011101xxxxxxxx110010011111", InstName.Ldab, InstEmit32.Ldab, typeof(OpCode32MemLdEx));
SetA32("<<<<00011001xxxxxxxx111010011111", InstName.Ldaex, InstEmit32.Ldaex, typeof(OpCode32MemLdEx));
@ -768,20 +769,17 @@ namespace ARMeilleure.Decoders
SetA32("1111001x0xxxxxxxxxxx0011xxx0xxxx", InstName.Vcgt, InstEmit32.Vcgt_I, typeof(OpCode32SimdReg));
SetA32("111100111x11xx01xxxx0x011xx0xxxx", InstName.Vcle, InstEmit32.Vcle_Z, typeof(OpCode32SimdCmpZ));
//SetA32("111100110x0xxxxxxxxx1110xxx0xxxx", InstName.Vcle, InstEmit32.Vcle_V, typeof(OpCode32SimdReg)); //note: size is 16/32 only
//SetA32("1111001x0xxxxxxxxxxx0011xxx1xxxx", InstName.Vcle, InstEmit32.Vcle_I, typeof(OpCode32SimdReg));
SetA32("111100111x11xx01xxxx0x100xx0xxxx", InstName.Vclt, InstEmit32.Vclt_Z, typeof(OpCode32SimdCmpZ));
//SetA32("111100110x1xxxxxxxxx1110xxx0xxxx", InstName.Vclt, InstEmit32.Vclt_V, typeof(OpCode32SimdReg)); //note: size is 16/32 only
//SetA32("1111001x0xxxxxxxxxxx0011xxx0xxxx", InstName.Vclt, InstEmit32.Vclt_I, typeof(OpCode32SimdReg));
SetA32("<<<<11101x11010xxxxx10xx01x0xxxx", InstName.Vcmp, InstEmit32.Vcmp, typeof(OpCode32SimdS));
SetA32("<<<<11101x11010xxxxx10xx11x0xxxx", InstName.Vcmpe,InstEmit32.Vcmpe, typeof(OpCode32SimdS));
SetA32("111100111x11xx11xxxx011xxxx0xxxx", InstName.Vcvt, InstEmit32.Vcvt_V, typeof(OpCode32SimdCmpZ));
SetA32("<<<<11101x110111xxxx101x11x0xxxx", InstName.Vcvt, InstEmit32.Vcvt_FD, typeof(OpCode32SimdS));
SetA32("<<<<11101x11110xxxxx10xx11x0xxxx", InstName.Vcvt, InstEmit32.Vcvt_FI, typeof(OpCode32SimdCvtFI));
SetA32("<<<<11101x111000xxxx10xxx1x0xxxx", InstName.Vcvt, InstEmit32.Vcvt_FI, typeof(OpCode32SimdCvtFI));
SetA32("111111101x1111xxxxxx10<<x1x0xxxx", InstName.Vcvt, InstEmit32.Vcvt_R, typeof(OpCode32SimdCvtFI));
SetA32("111100111x11xx11xxxx011xxxx0xxxx", InstName.Vcvt, InstEmit32.Vcvt_V, typeof(OpCode32SimdCmpZ)); // fp and integer, vector
SetA32("<<<<11101x110111xxxx101x11x0xxxx", InstName.Vcvt, InstEmit32.Vcvt_FD, typeof(OpCode32SimdS)); // fp 32 and 64, scalar
SetA32("<<<<11101x11110xxxxx10xx11x0xxxx", InstName.Vcvt, InstEmit32.Vcvt_FI, typeof(OpCode32SimdCvtFI)); // fp32 to int
SetA32("<<<<11101x111000xxxx10xxx1x0xxxx", InstName.Vcvt, InstEmit32.Vcvt_FI, typeof(OpCode32SimdCvtFI)); // int to fp32
SetA32("111111101x1111xxxxxx10>>x1x0xxxx", InstName.Vcvt, InstEmit32.Vcvt_R, typeof(OpCode32SimdCvtFI)); // the many fp32 to int encodings (fp)
SetA32("<<<<11101x00xxxxxxxx10xxx0x0xxxx", InstName.Vdiv, InstEmit32.Vdiv_S, typeof(OpCode32SimdRegS));
SetA32("<<<<11101xx0xxxxxxxx1011x0x10000", InstName.Vdup, InstEmit32.Vdup, typeof(OpCode32SimdVdupGP));
SetA32("111100111x11xxxxxxxx11000xx0xxxx", InstName.Vdup, InstEmit32.Vdup_1, typeof(OpCode32SimdDupElem));
@ -790,32 +788,20 @@ namespace ARMeilleure.Decoders
// VLD# missing single to all lanes
SetA32("111101001x10xxxxxxxx0000xxxxxxxx", InstName.Vld1, InstEmit32.Vld1, typeof(OpCode32SimdMemSingle));
SetA32("111101001x10xxxxxxxx0100xxxxxxxx", InstName.Vld1, InstEmit32.Vld1, typeof(OpCode32SimdMemSingle));
SetA32("111101001x10xxxxxxxx1000xxxxxxxx", InstName.Vld1, InstEmit32.Vld1, typeof(OpCode32SimdMemSingle));
SetA32("111101001x10xxxxxxxx1100xxxxxxxx", InstName.Vld1, InstEmit32.Vld1, typeof(OpCode32SimdMemSingle)); //all lanes
SetA32("111101001x10xxxxxxxxxx00xxxxxxxx", InstName.Vld1, InstEmit32.Vld1, typeof(OpCode32SimdMemSingle));
SetA32("111101000x10xxxxxxxx0111xxxxxxxx", InstName.Vld1, InstEmit32.Vld1, typeof(OpCode32SimdMemPair)); //regs = 1
SetA32("111101000x10xxxxxxxx1010xxxxxxxx", InstName.Vld1, InstEmit32.Vld1, typeof(OpCode32SimdMemPair)); //regs = 2
SetA32("111101000x10xxxxxxxx0110xxxxxxxx", InstName.Vld1, InstEmit32.Vld1, typeof(OpCode32SimdMemPair)); //regs = 3
SetA32("111101000x10xxxxxxxx0010xxxxxxxx", InstName.Vld1, InstEmit32.Vld1, typeof(OpCode32SimdMemPair)); //regs = 4
SetA32("111101001x10xxxxxxxx0001xxxxxxxx", InstName.Vld2, InstEmit32.Vld2, typeof(OpCode32SimdMemSingle));
SetA32("111101001x10xxxxxxxx0101xxxxxxxx", InstName.Vld2, InstEmit32.Vld2, typeof(OpCode32SimdMemSingle));
SetA32("111101001x10xxxxxxxx1001xxxxxxxx", InstName.Vld2, InstEmit32.Vld2, typeof(OpCode32SimdMemSingle));
SetA32("111101001x10xxxxxxxx1101xxxxxxxx", InstName.Vld2, InstEmit32.Vld2, typeof(OpCode32SimdMemSingle)); //all lanes
SetA32("111101001x10xxxxxxxxxx01xxxxxxxx", InstName.Vld2, InstEmit32.Vld2, typeof(OpCode32SimdMemSingle));
SetA32("111101000x10xxxxxxxx100xxxxxxxxx", InstName.Vld2, InstEmit32.Vld2, typeof(OpCode32SimdMemPair)); //regs = 1, inc = 1/2 (itype)
SetA32("111101000x10xxxxxxxx0011xxxxxxxx", InstName.Vld2, InstEmit32.Vld2, typeof(OpCode32SimdMemPair)); //regs = 2, inc = 2
SetA32("111101001x10xxxxxxxx0010xxxxxxxx", InstName.Vld3, InstEmit32.Vld3, typeof(OpCode32SimdMemSingle));
SetA32("111101001x10xxxxxxxx0110xxxxxxxx", InstName.Vld3, InstEmit32.Vld3, typeof(OpCode32SimdMemSingle));
SetA32("111101001x10xxxxxxxx1010xxxxxxxx", InstName.Vld3, InstEmit32.Vld3, typeof(OpCode32SimdMemSingle));
SetA32("111101001x10xxxxxxxx1110xxxxxxxx", InstName.Vld3, InstEmit32.Vld3, typeof(OpCode32SimdMemSingle)); //all lanes
SetA32("111101001x10xxxxxxxxxx10xxxxxxxx", InstName.Vld3, InstEmit32.Vld3, typeof(OpCode32SimdMemSingle));
SetA32("111101000x10xxxxxxxx010xxxxxxxxx", InstName.Vld3, InstEmit32.Vld3, typeof(OpCode32SimdMemPair)); //inc = 1/2 (itype)
SetA32("111101001x10xxxxxxxx0011xxxxxxxx", InstName.Vld4, InstEmit32.Vld4, typeof(OpCode32SimdMemSingle));
SetA32("111101001x10xxxxxxxx0111xxxxxxxx", InstName.Vld4, InstEmit32.Vld4, typeof(OpCode32SimdMemSingle));
SetA32("111101001x10xxxxxxxx1011xxxxxxxx", InstName.Vld4, InstEmit32.Vld4, typeof(OpCode32SimdMemSingle));
SetA32("111101001x10xxxxxxxx1111xxxxxxxx", InstName.Vld4, InstEmit32.Vld4, typeof(OpCode32SimdMemSingle)); //all lanes
SetA32("111101001x10xxxxxxxxxx11xxxxxxxx", InstName.Vld4, InstEmit32.Vld4, typeof(OpCode32SimdMemSingle));
SetA32("111101000x10xxxxxxxx000xxxxxxxxx", InstName.Vld4, InstEmit32.Vld4, typeof(OpCode32SimdMemPair)); //inc = 1/2 (itype)
SetA32("<<<<11001x01xxxxxxxx1011xxxxxxx0", InstName.Vldm, InstEmit32.Vldm, typeof(OpCode32SimdMemMult));
@ -834,7 +820,7 @@ namespace ARMeilleure.Decoders
SetA32("111100100x1xxxxxxxxx1111xxx0xxxx", InstName.Vmin, InstEmit32.Vmin_V, typeof(OpCode32SimdReg));
SetA32("1111001x0xxxxxxxxxxx0110xxx1xxxx", InstName.Vmin, InstEmit32.Vmin_I, typeof(OpCode32SimdReg));
SetA32("111111101x00xxxxxxxx10xxxxx0xxxx", InstName.VMMmn, InstEmit32.VmaxminNm_S, typeof(OpCode32SimdRegS));
SetA32("111111101x00xxxxxxxx10>>xxx0xxxx", InstName.VMMmn, InstEmit32.VmaxminNm_S, typeof(OpCode32SimdRegS));
SetA32("111100110xxxxxxxxxxx1111xxx1xxxx", InstName.VMMmn, InstEmit32.VmaxminNm_V, typeof(OpCode32SimdReg));
SetA32("<<<<11100x00xxxxxxxx10xxx0x0xxxx", InstName.Vmla, InstEmit32.Vmla_S, typeof(OpCode32SimdRegS));
@ -868,6 +854,10 @@ namespace ARMeilleure.Decoders
SetA32("1111001x0xxxxxxxxxxx1001xxx1xxxx", InstName.Vmul, InstEmit32.Vmul_I, typeof(OpCode32SimdReg));
SetA32("1111001x1x<<xxxxxxxx100xx1x0xxxx", InstName.Vmul, InstEmit32.Vmul_1, typeof(OpCode32SimdRegElem));
SetA32("1111001x1x000xxxxxxx0xx00x11xxxx", InstName.Vmvn, InstEmit32.Vmvn_I, typeof(OpCode32SimdImm)); //d/q vector i32
SetA32("1111001x1x000xxxxxxx10x00x11xxxx", InstName.Vmvn, InstEmit32.Vmvn_I, typeof(OpCode32SimdImm));
SetA32("1111001x1x000xxxxxxx110x0x11xxxx", InstName.Vmvn, InstEmit32.Vmvn_I, typeof(OpCode32SimdImm));
SetA32("111100111x11xx01xxxx0x111xx0xxxx", InstName.Vneg, InstEmit32.Vneg_V, typeof(OpCode32Simd));
SetA32("<<<<11101x110001xxxx10xx01x0xxxx", InstName.Vneg, InstEmit32.Vneg_S, typeof(OpCode32SimdS));
@ -878,38 +868,31 @@ namespace ARMeilleure.Decoders
SetA32("111100100x10xxxxxxxx0001xxx1xxxx", InstName.Vorr, InstEmit32.Vorr_I, typeof(OpCode32SimdBinary));
SetA32("111100110x0xxxxxxxxx1101xxx0xxxx", InstName.Vpadd, InstEmit32.Vpadd_V, typeof(OpCode32SimdReg));
SetA32("1111001x0xxxxxxxxxxx1010x0x0xxxx", InstName.Vpadd, InstEmit32.Vpadd_I, typeof(OpCode32SimdReg));
SetA32("111100100xxxxxxxxxxx1011xxx1xxxx", InstName.Vpadd, InstEmit32.Vpadd_I, typeof(OpCode32SimdReg));
SetA32("111111101x1110xxxxxx10<<01x0xxxx", InstName.Vrint, InstEmit32.Vrint_R, typeof(OpCode32SimdCvtFI));
SetA32("111100111x11xx00xxxx000<<xx0xxxx", InstName.Vrev, InstEmit32.Vrev, typeof(OpCode32SimdRev));
SetA32("111111101x1110xxxxxx10>>01x0xxxx", InstName.Vrint, InstEmit32.Vrint_RM, typeof(OpCode32SimdCvtFI));
SetA32("<<<<11101x110110xxxx10xx11x0xxxx", InstName.Vrint, InstEmit32.Vrint_Z, typeof(OpCode32SimdCvtFI));
SetA32("111111100xxxxxxxxxxx10xxx0x0xxxx", InstName.Vsel, InstEmit32.Vsel, typeof(OpCode32SimdSel));
SetA32("111111100xxxxxxxxxxx10>>x0x0xxxx", InstName.Vsel, InstEmit32.Vsel, typeof(OpCode32SimdSel));
SetA32("1111001x0xxxxxxxxxxx0100xxx0xxxx", InstName.Vshl, InstEmit32.Vshl_I, typeof(OpCode32SimdReg));
SetA32("111100101xxxxxxxxxxx0101xxx1xxxx", InstName.Vshl, InstEmit32.Vshl, typeof(OpCode32SimdShift));
SetA32("111100101x>>>xxxxxxx0101>xx1xxxx", InstName.Vshl, InstEmit32.Vshl, typeof(OpCode32SimdShift));
SetA32("111101001x00xxxxxxxx0000xxxxxxxx", InstName.Vst1, InstEmit32.Vst1, typeof(OpCode32SimdMemSingle));
SetA32("111101001x00xxxxxxxx0100xxxxxxxx", InstName.Vst1, InstEmit32.Vst1, typeof(OpCode32SimdMemSingle));
SetA32("111101001x00xxxxxxxx1000xxxxxxxx", 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("111101000x00xxxxxxxx1010xxxxxxxx", InstName.Vst1, InstEmit32.Vst1, typeof(OpCode32SimdMemPair)); //regs = 2
SetA32("111101000x00xxxxxxxx0110xxxxxxxx", InstName.Vst1, InstEmit32.Vst1, typeof(OpCode32SimdMemPair)); //regs = 3
SetA32("111101000x00xxxxxxxx0010xxxxxxxx", InstName.Vst1, InstEmit32.Vst1, typeof(OpCode32SimdMemPair)); //regs = 4
SetA32("111101001x00xxxxxxxx0001xxxxxxxx", InstName.Vst2, InstEmit32.Vst2, typeof(OpCode32SimdMemSingle));
SetA32("111101001x00xxxxxxxx0101xxxxxxxx", InstName.Vst2, InstEmit32.Vst2, typeof(OpCode32SimdMemSingle));
SetA32("111101001x00xxxxxxxx1001xxxxxxxx", InstName.Vst2, InstEmit32.Vst2, typeof(OpCode32SimdMemSingle));
SetA32("111101001x00xxxxxxxx<<01xxxxxxxx", InstName.Vst2, InstEmit32.Vst2, typeof(OpCode32SimdMemSingle));
SetA32("111101000x00xxxxxxxx100xxxxxxxxx", InstName.Vst2, InstEmit32.Vst2, typeof(OpCode32SimdMemPair)); //regs = 1, inc = 1/2 (itype)
SetA32("111101000x00xxxxxxxx0011xxxxxxxx", InstName.Vst2, InstEmit32.Vst2, typeof(OpCode32SimdMemPair)); //regs = 2, inc = 2
SetA32("111101001x00xxxxxxxx0010xxxxxxxx", InstName.Vst3, InstEmit32.Vst3, typeof(OpCode32SimdMemSingle));
SetA32("111101001x00xxxxxxxx0110xxxxxxxx", InstName.Vst3, InstEmit32.Vst3, typeof(OpCode32SimdMemSingle));
SetA32("111101001x00xxxxxxxx1010xxxxxxxx", InstName.Vst3, InstEmit32.Vst3, typeof(OpCode32SimdMemSingle));
SetA32("111101001x00xxxxxxxx<<10xxxxxxxx", InstName.Vst3, InstEmit32.Vst3, typeof(OpCode32SimdMemSingle));
SetA32("111101000x00xxxxxxxx010xxxxxxxxx", InstName.Vst3, InstEmit32.Vst3, typeof(OpCode32SimdMemPair)); //inc = 1/2 (itype)
SetA32("111101001x00xxxxxxxx0011xxxxxxxx", InstName.Vst4, InstEmit32.Vst4, typeof(OpCode32SimdMemSingle));
SetA32("111101001x00xxxxxxxx0111xxxxxxxx", InstName.Vst4, InstEmit32.Vst4, typeof(OpCode32SimdMemSingle));
SetA32("111101001x00xxxxxxxx1011xxxxxxxx", InstName.Vst4, InstEmit32.Vst4, typeof(OpCode32SimdMemSingle));
SetA32("111101001x00xxxxxxxx<<11xxxxxxxx", InstName.Vst4, InstEmit32.Vst4, typeof(OpCode32SimdMemSingle));
SetA32("111101000x00xxxxxxxx000xxxxxxxxx", InstName.Vst4, InstEmit32.Vst4, typeof(OpCode32SimdMemPair)); //inc = 1/2 (itype)
SetA32("<<<<11001x00xxxxxxxx1011xxxxxxx0", InstName.Vstm, InstEmit32.Vstm, typeof(OpCode32SimdMemMult));

View file

@ -367,7 +367,7 @@ namespace ARMeilleure.Instructions
SetAluDOrZR(context, d);
}
private static Operand EmitReverseBytes32_64Op(ArmEmitterContext context, Operand op)
public static Operand EmitReverseBytes32_64Op(ArmEmitterContext context, Operand op)
{
Debug.Assert(op.Type == OperandType.I64);

View file

@ -581,7 +581,7 @@ namespace ARMeilleure.Instructions
Operand m = GetAluM(context);
Operand res = context.Call(new _U32_U32(System.Buffers.Binary.BinaryPrimitives.ReverseEndianness), m);
Operand res = context.ByteSwap(m);
EmitAluStore(context, res);
}

View file

@ -10,6 +10,7 @@ using static ARMeilleure.Instructions.InstEmitSimdHelper;
using static ARMeilleure.Instructions.InstEmitSimdHelper32;
using static ARMeilleure.Instructions.InstEmitFlowHelper;
using static ARMeilleure.IntermediateRepresentation.OperandHelper;
using System.Diagnostics;
namespace ARMeilleure.Instructions
{
@ -86,7 +87,7 @@ namespace ARMeilleure.Instructions
InsertScalar(context, op.Vd, insert);
if (op.Q)
{
InsertScalar(context, op.Vd | 1, insert);
InsertScalar(context, op.Vd + 1, insert);
}
}
@ -179,7 +180,7 @@ namespace ARMeilleure.Instructions
private static void EmitBifBit(ArmEmitterContext context, bool notRm)
{
OpCodeSimdReg op = (OpCodeSimdReg)context.CurrOp;
OpCode32SimdReg op = (OpCode32SimdReg)context.CurrOp;
/*
if (Optimizations.UseSse2)
@ -357,7 +358,6 @@ namespace ARMeilleure.Instructions
}
}
//TODO: probably important to have a fast path for these instead of calling fucking standard math min/max
public static void VmaxminNm_S(ArmEmitterContext context)
{
bool max = (context.CurrOp.RawOpCode & (1 << 6)) == 0;
@ -408,6 +408,7 @@ namespace ARMeilleure.Instructions
public static void Vmul_I(ArmEmitterContext context)
{
if (((context.CurrOp.RawOpCode >> 24) & 1) != 0) throw new Exception("Polynomial mode not supported");
EmitVectorBinaryOpSx32(context, (op1, op2) => context.Multiply(op1, op2));
}
@ -558,6 +559,52 @@ namespace ARMeilleure.Instructions
EmitVectorPairwiseOpI32(context, (op1, op2) => context.Add(op1, op2), !op.U);
}
public static void Vrev(ArmEmitterContext context)
{
OpCode32Simd op = (OpCode32Simd)context.CurrOp;
EmitVectorUnaryOpZx32(context, (op1) =>
{
switch (op.Opc)
{
case 0:
switch (op.Size) //swap bytes
{
default:
return op1;
case 1:
return InstEmit.EmitReverseBytes16_32Op(context, op1);
case 2:
case 3:
return context.ByteSwap(op1);
}
case 1:
switch (op.Size)
{
default:
return op1;
case 2:
return context.BitwiseOr(context.ShiftRightUI(context.BitwiseAnd(op1, Const(0xffff0000)), Const(16)),
context.ShiftLeft(context.BitwiseAnd(op1, Const(0x0000ffff)), Const(16)));
case 3:
return context.BitwiseOr(
context.BitwiseOr(context.ShiftRightUI(context.BitwiseAnd(op1, Const(0xffff000000000000ul)), Const(48)),
context.ShiftLeft(context.BitwiseAnd(op1, Const(0x000000000000fffful)), Const(48))),
context.BitwiseOr(context.ShiftRightUI(context.BitwiseAnd(op1, Const(0x0000ffff00000000ul)), Const(16)),
context.ShiftLeft(context.BitwiseAnd(op1, Const(0x00000000ffff0000ul)), Const(16)))
);
}
case 2:
//swap upper and lower
return context.BitwiseOr(context.ShiftRightUI(context.BitwiseAnd(op1, Const(0xffffffff00000000ul)), Const(32)),
context.ShiftLeft(context.BitwiseAnd(op1, Const(0x00000000fffffffful)), Const(32)));
}
return op1;
});
}
public static void Vrecpe(ArmEmitterContext context)
{
EmitVectorUnaryOpF32(context, (op1) =>
@ -593,24 +640,24 @@ namespace ARMeilleure.Instructions
public static void Vsel(ArmEmitterContext context)
{
var op = (OpCode32SimdSel)context.CurrOp;
Operand condition = null;
switch (op.Cc)
{
case OpCode32SimdSelMode.Eq:
condition = GetCondTrue(context, Condition.Eq);
break;
case OpCode32SimdSelMode.Ge:
condition = GetCondTrue(context, Condition.Ge);
break;
case OpCode32SimdSelMode.Gt:
condition = GetCondTrue(context, Condition.Gt);
break;
case OpCode32SimdSelMode.Vs:
condition = GetCondTrue(context, Condition.Vs);
break;
}
EmitScalarBinaryOpI32(context, (op1, op2) =>
{
Operand condition = null;
switch (op.Cc)
{
case OpCode32SimdSelMode.Eq:
condition = GetCondTrue(context, Condition.Eq);
break;
case OpCode32SimdSelMode.Ge:
condition = GetCondTrue(context, Condition.Ge);
break;
case OpCode32SimdSelMode.Gt:
condition = GetCondTrue(context, Condition.Gt);
break;
case OpCode32SimdSelMode.Vs:
condition = GetCondTrue(context, Condition.Vs);
break;
}
return context.ConditionalSelect(condition, op1, op2);
});
}
@ -621,11 +668,48 @@ namespace ARMeilleure.Instructions
//IMPORTANT TODO: does shift left negative do a truncating shift right on x86?
if (op.U)
{
EmitVectorBinaryOpZx32(context, (op1, op2) => context.ShiftLeft(op1, context.SignExtend8(op2.Type, op2)));
EmitVectorBinaryOpZx32(context, (op1, op2) => EmitShlRegOp(context, op2, op1, op.Size, true));
}
else
{
EmitVectorBinaryOpSx32(context, (op1, op2) => context.ShiftLeft(op1, context.SignExtend8(op2.Type, op2)));
EmitVectorBinaryOpSx32(context, (op1, op2) => EmitShlRegOp(context, op2, op1, op.Size, false));
}
}
private static Operand EmitShlRegOp(ArmEmitterContext context, Operand op, Operand shiftLsB, int size, bool unsigned)
{
if (shiftLsB.Type == OperandType.I64) shiftLsB = context.ConvertI64ToI32(shiftLsB);
shiftLsB = context.SignExtend8(OperandType.I32, shiftLsB);
Debug.Assert((uint)size < 4u);
Operand negShiftLsB = context.Negate(shiftLsB);
Operand isPositive = context.ICompareGreaterOrEqual(shiftLsB, Const(0));
Operand shl = context.ShiftLeft(op, shiftLsB);
Operand shr = (unsigned) ? context.ShiftRightUI(op, negShiftLsB) : context.ShiftRightSI(op, negShiftLsB);
Operand res = context.ConditionalSelect(isPositive, shl, shr);
if (unsigned)
{
Operand isOutOfRange = context.BitwiseOr(
context.ICompareGreaterOrEqual(shiftLsB, Const(8 << size)),
context.ICompareGreaterOrEqual(negShiftLsB, Const(8 << size)));
return context.ConditionalSelect(isOutOfRange, Const(op.Type, 0), res);
}
else
{
Operand isOutOfRange0 = context.ICompareGreaterOrEqual(shiftLsB, Const(8 << size));
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))));
Operand min = (op.Type == OperandType.I64) ? Const(-1L) : Const(-1);
return context.ConditionalSelect(isOutOfRange0, Const(op.Type, 0), context.ConditionalSelect(isOutOfRangeN, min, res));
}
}

View file

@ -166,7 +166,8 @@ namespace ARMeilleure.Instructions
private static Operand ZerosOrOnes(ArmEmitterContext context, Operand fromBool, OperandType baseType)
{
return context.ConditionalSelect(fromBool, Const(baseType, -1L), Const(baseType, 0L));
var ones = (baseType == OperandType.I64) ? Const(-1L) : Const(-1);
return context.ConditionalSelect(fromBool, ones, Const(baseType, 0L));
}
private static void EmitCmpOpI32(

View file

@ -87,7 +87,7 @@ namespace ARMeilleure.Instructions
int vd;
if (op.Size == 3)
{
vd = FlipVdBits(op.Vd, true);
vd = FlipVdBits(op.Vd, false);
// double to single
Operand fp = ExtractScalar(context, OperandType.FP64, vm);
@ -99,7 +99,7 @@ namespace ARMeilleure.Instructions
}
else
{
vd = FlipVdBits(op.Vd, false);
vd = FlipVdBits(op.Vd, true);
// single to double
Operand fp = ExtractScalar(context, OperandType.FP32, vm);
@ -248,10 +248,10 @@ namespace ARMeilleure.Instructions
toConvert = EmitRoundMathCall(context, MidpointRounding.ToEven, toConvert);
break;
case 0b10: //+infinity
toConvert = EmitRoundMathCall(context, MidpointRounding.ToPositiveInfinity, toConvert);
toConvert = EmitUnaryMathCall(context, MathF.Ceiling, Math.Ceiling, toConvert);
break;
case 0b11: //negative
toConvert = EmitRoundMathCall(context, MidpointRounding.ToNegativeInfinity, toConvert);
toConvert = EmitUnaryMathCall(context, MathF.Floor, Math.Floor, toConvert);
break;
}
@ -285,7 +285,7 @@ namespace ARMeilleure.Instructions
}
public static void Vrint_R(ArmEmitterContext context)
public static void Vrint_RM(ArmEmitterContext context)
{
OpCode32SimdCvtFI op = (OpCode32SimdCvtFI)context.CurrOp;
@ -302,10 +302,10 @@ namespace ARMeilleure.Instructions
toConvert = EmitRoundMathCall(context, MidpointRounding.ToEven, toConvert);
break;
case 0b10: //+infinity
toConvert = EmitRoundMathCall(context, MidpointRounding.ToPositiveInfinity, toConvert);
toConvert = EmitUnaryMathCall(context, MathF.Ceiling, Math.Ceiling, toConvert);
break;
case 0b11: //negative
toConvert = EmitRoundMathCall(context, MidpointRounding.ToNegativeInfinity, toConvert);
toConvert = EmitUnaryMathCall(context, MathF.Floor, Math.Floor, toConvert);
break;
}
@ -314,7 +314,7 @@ namespace ARMeilleure.Instructions
public static void Vrint_Z(ArmEmitterContext context)
{
EmitScalarUnaryOpF32(context, (op1) => EmitRoundMathCall(context, MidpointRounding.ToZero, op1));
EmitScalarUnaryOpF32(context, (op1) => EmitUnaryMathCall(context, MathF.Truncate, Math.Truncate, op1));
}
private static int CastDoubleToInt32(double value)

View file

@ -230,9 +230,9 @@ namespace ARMeilleure.Instructions
for (int index = 0; index < elems; index++)
{
Operand ne = EmitVectorExtractSx32(context, vm, index + em * elems, op.Size);
Operand me = EmitVectorExtractSx32(context, vm, index + em * elems, op.Size);
res = EmitVectorInsert(context, res, emit(ne), index + ed * elems, op.Size);
res = EmitVectorInsert(context, res, emit(me), index + ed * elems, op.Size);
}
context.Copy(GetVecA32(vd), res);
@ -298,9 +298,9 @@ namespace ARMeilleure.Instructions
for (int index = 0; index < elems; index++)
{
Operand ne = EmitVectorExtractZx32(context, vm, index + em * elems, op.Size);
Operand me = EmitVectorExtractZx32(context, vm, index + em * elems, op.Size);
res = EmitVectorInsert(context, res, emit(ne), index + ed * elems, op.Size);
res = EmitVectorInsert(context, res, emit(me), index + ed * elems, op.Size);
}
context.Copy(GetVecA32(vd), res);
@ -347,7 +347,7 @@ namespace ARMeilleure.Instructions
Operand ne = EmitVectorExtractZx32(context, vn, index + en * elems, op.Size);
Operand me = EmitVectorExtractZx32(context, vm, index + em * elems, op.Size);
res = EmitVectorInsert(context, res, emit(de, ne, me), index, op.Size);
res = EmitVectorInsert(context, res, emit(de, ne, me), index + ed * elems, op.Size);
}
context.Copy(GetVecA32(vd), res);
@ -479,17 +479,21 @@ namespace ARMeilleure.Instructions
(int vd, int ed) = GetQuadwordAndSubindex(op.Vd, op.RegisterSize);
Operand res = GetVecA32(vd);
Operand mvec = GetVecA32(vm);
Operand nvec = GetVecA32(vn);
for (int index = 0; index < pairs; index++)
{
int pairIndex = index << 1;
Operand n1 = context.VectorExtract(type, GetVecA32(vn), pairIndex + en * elems);
Operand n2 = context.VectorExtract(type, GetVecA32(vn), pairIndex + 1 + en * elems);
Operand m1 = context.VectorExtract(type, GetVecA32(vm), pairIndex + em * elems);
Operand m2 = context.VectorExtract(type, GetVecA32(vm), pairIndex + 1 + em * elems);
Operand n1 = context.VectorExtract(type, nvec, pairIndex + en * elems);
Operand n2 = context.VectorExtract(type, nvec, pairIndex + 1 + en * elems);
res = context.VectorInsert(res, emit(n1, n2), index + ed * elems);
Operand m1 = context.VectorExtract(type, mvec, pairIndex + em * elems);
Operand m2 = context.VectorExtract(type, mvec, pairIndex + 1 + em * elems);
res = context.VectorInsert(res, emit(m1, m2), index + pairs + ed * elems);
}

View file

@ -52,7 +52,7 @@ namespace ARMeilleure.Instructions
{
OpCode32SimdMemSingle op = (OpCode32SimdMemSingle)context.CurrOp;
if (op.Replicate && !load) throw new Exception("Replicate+Store is undefined for LDn");
if (op.Replicate && !load) throw new Exception("Replicate+Store is undefined for STn");
int eBytes = 1 << op.Size;
Operand n = GetIntA32(context, op.Rn);
@ -81,14 +81,24 @@ namespace ARMeilleure.Instructions
int index = ((d & 1) << (3 - op.Size)) + op.Index;
if (load)
{
EmitLoadSimd(context, address, GetVecA32(d >> 1), d >> 1, index, op.Size);
if (op.Replicate)
{
int limit = index + (1 << (3 - op.Size));
while (++index < limit)
var regs = (count > 1) ? 1 : op.Increment;
for (int reg = 0; reg < regs; reg++)
{
EmitLoadSimd(context, address, GetVecA32(d >> 1), d >> 1, index, op.Size);
int dreg = reg + d;
int rIndex = ((dreg & 1) << (3 - op.Size));
int limit = rIndex + (1 << (3 - op.Size));
while (rIndex < limit)
{
EmitLoadSimd(context, address, GetVecA32(dreg >> 1), dreg >> 1, rIndex++, op.Size);
}
}
}
else
{
EmitLoadSimd(context, address, GetVecA32(d >> 1), d >> 1, index, op.Size);
}
}
else

View file

@ -18,6 +18,11 @@ namespace ARMeilleure.Instructions
EmitVectorImmUnaryOp32(context, (op1) => op1);
}
public static void Vmvn_I(ArmEmitterContext context)
{
EmitVectorImmUnaryOp32(context, (op1) => context.BitwiseExclusiveOr(op1, op1));
}
public static void Vmov_GS(ArmEmitterContext context)
{
OpCode32SimdMovGp op = (OpCode32SimdMovGp)context.CurrOp;
@ -58,24 +63,37 @@ namespace ARMeilleure.Instructions
public static void Vmov_G2(ArmEmitterContext context)
{
OpCode32SimdMovGpDouble op = (OpCode32SimdMovGpDouble)context.CurrOp;
Operand vec = GetVecA32(op.Vm >> 1);
Operand vec = GetVecA32(op.Vm >> 2);
int vm1 = (op.Vm + 1);
bool sameOwnerVec = (op.Vm >> 2) == (vm1 >> 2);
Operand vec2 = sameOwnerVec ? vec : GetVecA32(vm1 >> 2);
if (op.Op == 1)
{
// to general purpose
Operand lowValue = context.VectorExtract(OperandType.I32, vec, (op.Vm & 1) << 1);
Operand lowValue = context.VectorExtract(OperandType.I32, vec, op.Vm & 3);
SetIntA32(context, op.Rt, lowValue);
Operand highValue = context.VectorExtract(OperandType.I32, vec, ((op.Vm & 1) << 1) | 1);
Operand highValue = context.VectorExtract(OperandType.I32, vec2, vm1 & 3);
SetIntA32(context, op.Rt2, highValue);
}
else
{
// from general purpose
Operand lowValue = GetIntA32(context, op.Rt);
Operand resultVec = context.VectorInsert(vec, lowValue, (op.Vm & 1) << 1);
Operand resultVec = context.VectorInsert(vec, lowValue, op.Vm & 3);
Operand highValue = GetIntA32(context, op.Rt2);
context.Copy(vec, context.VectorInsert(resultVec, highValue, ((op.Vm & 1) << 1) | 1));
if (sameOwnerVec)
{
context.Copy(vec, context.VectorInsert(resultVec, highValue, vm1 & 3));
}
else
{
context.Copy(vec, resultVec);
context.Copy(vec2, context.VectorInsert(vec2, highValue, vm1 & 3));
}
}
}

View file

@ -551,12 +551,14 @@ namespace ARMeilleure.Instructions
Vmrs,
Vmsr,
Vmul,
Vmvn,
Vneg,
Vnmul,
Vnmla,
Vnmls,
Vorr,
Vpadd,
Vrev,
Vrint,
Vsel,
Vshl,

View file

@ -24,7 +24,7 @@ namespace ARMeilleure.Translation
public bool ShouldRejit()
{
return Interlocked.Increment(ref _callCount) == MinCallsForRejit;
return _rejit && Interlocked.Increment(ref _callCount) == MinCallsForRejit;
}
}
}

View file

@ -87,7 +87,6 @@ namespace ARMeilleure.Translation
public ulong ExecuteSingle(State.ExecutionContext context, ulong address)
{
if (address == 0xa28b75) { }
TranslatedFunction func = GetOrTranslate(address, context.ExecutionMode);
Statistics.StartTimer();

View file

@ -1,4 +1,4 @@
#define Simd
//#define Simd
using ARMeilleure.State;

View file

@ -0,0 +1,116 @@
#define SimdLogical32
using ARMeilleure.State;
using NUnit.Framework;
using System;
using System.Collections.Generic;
using System.Text;
namespace Ryujinx.Tests.Cpu
{
[Category("SimdLogical32")]
class CpuTestSimdLogical32 : CpuTest32
{
#if SimdLogical32
private const int RndCnt = 5;
private uint GenerateVectorOpcode(uint opcode, uint rd, uint rn, uint rm, bool q)
{
if (q)
{
opcode |= 1 << 6;
rm <<= 1;
rn <<= 1;
rd <<= 1;
}
opcode |= ((rm & 0xf) << 0) | ((rm & 0x10) << 1);
opcode |= ((rd & 0xf) << 12) | ((rd & 0x10) << 18);
opcode |= ((rn & 0xf) << 16) | ((rn & 0x10) << 3);
return opcode;
}
[Test, Pairwise, Description("VBIF {<Vd>}, <Vm>, <Vn>")]
public void Vbif([Range(0u, 4u)] uint rd,
[Range(0u, 4u)] uint rn,
[Range(0u, 4u)] uint rm,
[Random(RndCnt)] ulong z,
[Random(RndCnt)] ulong a,
[Random(RndCnt)] ulong b,
[Values] bool q)
{
uint opcode = GenerateVectorOpcode(0xf3300110, rd, rn, rm, q);
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("VBIT {<Vd>}, <Vm>, <Vn>")]
public void Vbit([Range(0u, 4u)] uint rd,
[Range(0u, 4u)] uint rn,
[Range(0u, 4u)] uint rm,
[Random(RndCnt)] ulong z,
[Random(RndCnt)] ulong a,
[Random(RndCnt)] ulong b,
[Values] bool q)
{
uint opcode = GenerateVectorOpcode(0xf3200110, rd, rn, rm, q);
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("VBSL {<Vd>}, <Vm>, <Vn>")]
public void Vbsl([Range(0u, 4u)] uint rd,
[Range(0u, 4u)] uint rn,
[Range(0u, 4u)] uint rm,
[Random(RndCnt)] ulong z,
[Random(RndCnt)] ulong a,
[Random(RndCnt)] ulong b,
[Values] bool q)
{
uint opcode = GenerateVectorOpcode(0xf3100110, rd, rn, rm, q);
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("VAND {<Vd>}, <Vm>, <Vn>")]
public void Vand([Range(0u, 4u)] uint rd,
[Range(0u, 4u)] uint rn,
[Range(0u, 4u)] uint rm,
[Random(RndCnt)] ulong z,
[Random(RndCnt)] ulong a,
[Random(RndCnt)] ulong b,
[Values] bool q)
{
uint opcode = GenerateVectorOpcode(0xf2000110, rd, rn, rm, q);
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
}
}

View file

@ -1,4 +1,4 @@
//#define SimdMem32
#define SimdMem32
using ARMeilleure.State;
using NUnit.Framework;
@ -37,7 +37,7 @@ namespace Ryujinx.Tests.Cpu
0b0001
};
[Test, Combinatorial, Description("VLDn.<size> <list>, [<Rn> {:<align>}]{ /!/, <Rm>} (single n element structure)")]
[Test, Pairwise, Description("VLDn.<size> <list>, [<Rn> {:<align>}]{ /!/, <Rm>} (single n element structure)")]
public void Vldn_Single([Values(0u, 1u, 2u)] uint size,
[Values(0u, 13u)] uint rn,
[Values(1u, 13u, 15u)] uint rm,
@ -67,6 +67,33 @@ namespace Ryujinx.Tests.Cpu
CompareAgainstUnicorn();
}
[Test, Combinatorial, Description("VLDn.<size> <list>, [<Rn> {:<align>}]{ /!/, <Rm>} (all lanes)")]
public void Vldn_All([Values(0u, 13u)] uint rn,
[Values(1u, 13u, 15u)] uint rm,
[Values(0u, 1u, 2u, 3u, 4u, 5u, 6u, 7u)] uint vd,
[Range(0u, 3u)] uint n,
[Range(0u, 2u)] uint size,
[Values] bool t,
[Values(0x0u)] [Random(0u, 0xffu, RndCntImm)] uint offset)
{
var data = GenerateVectorSequence(0x1000);
SetWorkingMemory(data);
uint opcode = 0xf4a00c00; // vld1.8 {d0[0]}, [r0], r0
opcode |= ((size & 3) << 6) | ((rn & 15) << 16) | (rm & 15);
opcode |= ((vd & 0x10) << 18);
opcode |= ((vd & 0xf) << 12);
opcode |= (n & 3) << 8; //LD1 is 0, LD2 is 1 etc
if (t) opcode |= 1 << 5; //LD1 is 0, LD2 is 1 etc
SingleOpcode(opcode, r0: 0x2500, r1: offset, sp: 0x2500);
CompareAgainstUnicorn();
}
[Test, Combinatorial, Description("VLDn.<size> <list>, [<Rn> {:<align>}]{ /!/, <Rm>} (multiple n element structures)")]
public void Vldn_Pair([Values(0u, 1u, 2u, 3u)] uint size,
[Values(0u, 13u)] uint rn,
@ -90,7 +117,7 @@ namespace Ryujinx.Tests.Cpu
CompareAgainstUnicorn();
}
[Test, Combinatorial, Description("VSTn.<size> <list>, [<Rn> {:<align>}]{ /!/, <Rm>} (single n element structure)")]
[Test, Pairwise, Description("VSTn.<size> <list>, [<Rn> {:<align>}]{ /!/, <Rm>} (single n element structure)")]
public void Vstn_Single([Values(0u, 1u, 2u)] uint size,
[Values(0u, 13u)] uint rn,
[Values(1u, 13u, 15u)] uint rm,

View file

@ -92,14 +92,101 @@ namespace Ryujinx.Tests.Cpu
[Random(RndCntImm)] ulong valueVn2,
[Values] bool op)
{
uint opcode = 0xee000a10; // invalid
uint opcode = 0xee000a10;
opcode |= (vn & 1) << 7;
opcode |= (vn & 0x1e) << 15;
opcode |= (rt & 0xf) << 12;
if (op) opcode |= 1 << 20;
SingleOpcode(opcode, r0: valueRn, r1: valueRn, r2: valueRn, r3: valueRn, v0: new V128(valueVn1, valueVn2)); //correct
SingleOpcode(opcode, r0: valueRn, r1: valueRn, r2: valueRn, r3: valueRn, v0: new V128(valueVn1, valueVn2));
CompareAgainstUnicorn();
}
[Test, Combinatorial, Description("VMOV.<size> <Rt>, <Dn[x]>")]
public void Mov_GP_Elem([Range(0u, 7u)] uint vn,
[Values(0u, 1u, 2u, 3u)] uint rt,
[Range(0u, 2u)] uint size,
[Range(0u, 7u)] uint index,
[Random(1)] uint valueRn,
[Random(1)] ulong valueVn1,
[Random(1)] ulong valueVn2,
[Values] bool op,
[Values] bool u)
{
uint opcode = 0xee000b10;
uint opEncode = 0b01000;
switch (size)
{
case 0:
opEncode = (0b1000) | index & 7;
break;
case 1:
opEncode = (0b0001) | ((index & 3) << 1);
break;
case 2:
opEncode = (index & 1) << 2;
break;
}
opcode |= ((opEncode >> 2) << 21) | ((opEncode & 3) << 5);
opcode |= (vn & 0x10) << 3;
opcode |= (vn & 0xf) << 16;
opcode |= (rt & 0xf) << 12;
if (op) opcode |= 1 << 20;
if (op && u && size != 2) opcode |= 1 << 23;
SingleOpcode(opcode, r0: valueRn, r1: valueRn, r2: valueRn, r3: valueRn, v0: new V128(valueVn1, valueVn2), v1: new V128(valueVn2, valueVn1));
CompareAgainstUnicorn();
}
[Test, Pairwise, Description("(VMOV <Rt>, <Rt2>, <Dm>), (VMOV <Dm>, <Rt>, <Rt2>)")]
public void Mov_GP_D([Values(0u, 1u, 2u, 3u)] uint vm,
[Values(0u, 1u, 2u, 3u)] uint rt,
[Values(0u, 1u, 2u, 3u)] uint rt2,
[Random(RndCntImm)] uint valueRt1,
[Random(RndCntImm)] uint valueRt2,
[Random(RndCntImm)] ulong valueVn1,
[Random(RndCntImm)] ulong valueVn2,
[Values] bool op)
{
uint opcode = 0xec400b10;
opcode |= (vm & 0x10) << 1;
opcode |= (vm & 0xf);
opcode |= (rt & 0xf) << 12;
opcode |= (rt2 & 0xf) << 16;
if (op) opcode |= 1 << 20;
SingleOpcode(opcode, r0: valueRt1, r1: valueRt2, r2: valueRt1, r3: valueRt2, v0: new V128(valueVn1, valueVn2));
CompareAgainstUnicorn();
}
[Test, Pairwise, Description("(VMOV <Rt>, <Rt2>, <Sm>, <Sm1>), (VMOV <Sm>, <Sm1>, <Rt>, <Rt2>)")]
public void Mov_GP_2([Range(0u, 7u)] uint vm,
[Values(0u, 1u, 2u, 3u)] uint rt,
[Values(0u, 1u, 2u, 3u)] uint rt2,
[Random(RndCntImm)] uint valueRt1,
[Random(RndCntImm)] uint valueRt2,
[Random(RndCntImm)] ulong valueVn1,
[Random(RndCntImm)] ulong valueVn2,
[Values] bool op)
{
uint opcode = 0xec400a10;
opcode |= (vm & 1) << 5;
opcode |= (vm & 0x1e) >> 1;
opcode |= (rt & 0xf) << 12;
opcode |= (rt2 & 0xf) << 16;
if (op) opcode |= 1 << 20;
SingleOpcode(opcode, r0: valueRt1, r1: valueRt2, r2: valueRt1, r3: valueRt2, v0: new V128(valueVn1, valueVn2), v1: new V128(valueVn2, valueVn1));
CompareAgainstUnicorn();
}
@ -239,6 +326,117 @@ namespace Ryujinx.Tests.Cpu
CompareAgainstUnicorn();
}
[Test, Combinatorial, Description("VEXT.8 {<Vd>,} <Vn>, <Vm>, #<imm>")]
public void Vext([Values(0u, 1u, 2u, 3u)] uint vm,
[Values(0u, 1u, 2u, 3u)] uint vn,
[Values(0u, 1u, 2u, 3u)] uint vd,
[Values(0u, 15u)] uint imm4,
[Values] bool q)
{
uint opcode = 0xf2b00000;
if (q)
{
opcode |= 1 << 6;
vd <<= 1; vm <<= 1; vn <<= 1;
} else if (imm4 > 7) return; //undefined
opcode |= (vm & 0x10) << 1;
opcode |= (vm & 0xf);
opcode |= (vd & 0x10) << 18;
opcode |= (vd & 0xf) << 12;
opcode |= (vn & 0x10) << 3;
opcode |= (vn & 0xf) << 16;
opcode |= (imm4 & 0xf) << 8;
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); //correct
CompareAgainstUnicorn();
}
[Test, Pairwise, Description("VDUP <Vd>, <Rt>")]
public void Vdup_GP([Values(0u, 1u, 2u, 3u)] uint vd,
[Values(0u, 1u, 2u, 3u)] uint rt,
[Values(0u, 1u, 2u)] uint size,
[Random(RndCntImm)] uint valueRn,
[Random(RndCntImm)] ulong valueVn1,
[Random(RndCntImm)] ulong valueVn2,
[Values] bool q)
{
uint opcode = 0xee800b10;
if (q)
{
opcode |= 1 << 21;
vd <<= 1;
}
opcode |= (vd & 0x10) << 3;
opcode |= (vd & 0xf) << 16;
opcode |= (rt & 0xf) << 12;
opcode |= (size & 1) << 5; //e
opcode |= (size & 2) << 21; //b
V128 v1 = new V128(TestContext.CurrentContext.Random.NextULong(), TestContext.CurrentContext.Random.NextULong());
SingleOpcode(opcode, r0: valueRn, r1: valueRn, r2: valueRn, r3: valueRn, v0: new V128(valueVn1, valueVn2), v1: v1);
CompareAgainstUnicorn();
}
[Test, Pairwise, Description("VDUP.<size> <Vd>, <Dm[x]>")]
public void Vdup_S([Values(0u, 1u, 2u, 3u)] uint vd,
[Values(0u, 1u, 2u, 3u)] uint vm,
[Values(0u, 1u, 2u)] uint size,
[Range(0u, 7u)] uint index,
[Random(RndCntImm)] ulong valueVn1,
[Random(RndCntImm)] ulong valueVn2,
[Values] bool q)
{
uint opcode = 0xf3b00c00;
if (q)
{
opcode |= 1 << 6;
vd <<= 1;
}
opcode |= (vd & 0x10) << 18;
opcode |= (vd & 0xf) << 12;
opcode |= (vm & 0x10) << 1;
opcode |= (vm & 0xf);
uint imm4 = 0;
switch (size)
{
case 0:
imm4 |= 0b0100 | ((index & 1) << 3);
break;
case 1:
imm4 |= 0b0010 | ((index & 3) << 2);
break;
case 2:
imm4 |= 0b0001 | ((index & 7) << 1);
break;
}
opcode |= imm4 << 16;
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: new V128(valueVn1, valueVn2), v1: v1, v2: v2, v3: v3);
CompareAgainstUnicorn();
}
#endif
}
}

View file

@ -243,7 +243,8 @@ namespace Ryujinx.Tests.Cpu
[Test, Pairwise, Description("VCMP.f<size> Vd, Vm")]
public void Vcmp([Values(2u, 3u)] uint size,
[ValueSource("_1S_F_")] ulong a,
[ValueSource("_1S_F_")] ulong b)
[ValueSource("_1S_F_")] ulong b,
[Values] bool e)
{
uint opcode = 0xeeb40840;
uint rm = 1;
@ -259,6 +260,7 @@ namespace Ryujinx.Tests.Cpu
opcode |= ((rd & 0x1e) << 11) | ((rd & 0x1) << 22);
}
opcode |= ((size & 3) << 8);
if (e) opcode |= 1 << 7;
V128 v1 = MakeVectorE0(a);
V128 v2 = MakeVectorE0(b);
@ -274,6 +276,72 @@ namespace Ryujinx.Tests.Cpu
CompareAgainstUnicorn();
}
[Test, Pairwise, Description("VSHL.<size> {<Vd>}, <Vm>, <Vn>")]
public void Vshl([Values(0u)] uint rd,
[Values(1u, 0u)] uint rn,
[Values(2u, 0u)] uint rm,
[Values(0u, 1u, 2u, 3u)] uint size,
[Random(RndCnt)] ulong z,
[Random(RndCnt)] ulong a,
[Random(RndCnt)] ulong b,
[Values] bool q,
[Values] bool u)
{
uint opcode = 0xf2000400;
if (q)
{
opcode |= 1 << 6;
rm <<= 1;
rn <<= 1;
rd <<= 1;
}
if (u) opcode |= 1 << 24;
opcode |= ((rm & 0xf) << 0) | ((rm & 0x10) << 1);
opcode |= ((rd & 0xf) << 12) | ((rd & 0x10) << 18);
opcode |= ((rn & 0xf) << 16) | ((rn & 0x10) << 3);
V128 v0 = MakeVectorE0E1(z, z);
V128 v1 = MakeVectorE0E1(a, z);
V128 v2 = MakeVectorE0E1(b, z);
SingleOpcode(opcode, v0: v0, v1: v1, v2: v2);
CompareAgainstUnicorn(fpTolerances: FpTolerances.UpToOneUlpsS);
}
[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)
{
uint opcode = 0xf3000d00; // VADD.f32 D0, D0, D0
/*
if (q)
{
rm <<= 0x1e;
rn <<= 0x1e;
rd <<= 0x1e;
}
*/
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;
var rnd = TestContext.CurrentContext.Random;
V128 v0 = new V128(rnd.NextFloat(int.MinValue, int.MaxValue), rnd.NextFloat(int.MinValue, int.MaxValue), rnd.NextFloat(int.MinValue, int.MaxValue), rnd.NextFloat(int.MinValue, int.MaxValue));
V128 v1 = new V128(rnd.NextFloat(int.MinValue, int.MaxValue), rnd.NextFloat(int.MinValue, int.MaxValue), rnd.NextFloat(int.MinValue, int.MaxValue), rnd.NextFloat(int.MinValue, int.MaxValue));
V128 v2 = new V128(rnd.NextFloat(int.MinValue, int.MaxValue), rnd.NextFloat(int.MinValue, int.MaxValue), rnd.NextFloat(int.MinValue, int.MaxValue), rnd.NextFloat(int.MinValue, int.MaxValue));
SingleOpcode(opcode, v0: v0, v1: v1, v2: v2);
CompareAgainstUnicorn(fpTolerances: FpTolerances.UpToOneUlpsS);
}
#endif
}
}