forked from Mirror/Ryujinx
Add Sqdmulh_S, Sqdmulh_V, Sqrdmulh_S, Sqrdmulh_V instructions; add 6 Tests. Now all saturating methods are on ASoftFallback. (#334)
* Update Instructions.cs * Update CpuTestSimd.cs * Update CpuTestSimdReg.cs * Update AOpCodeTable.cs * Update AInstEmitSimdArithmetic.cs * Update AInstEmitSimdHelper.cs * Update ASoftFallback.cs * Update CpuTestAlu.cs * Update CpuTestAluImm.cs * Update CpuTestAluRs.cs * Update CpuTestAluRx.cs * Update CpuTestBfm.cs * Update CpuTestCcmpImm.cs * Update CpuTestCcmpReg.cs * Update CpuTestCsel.cs * Update CpuTestMov.cs * Update CpuTestMul.cs * Update Ryujinx.Tests.csproj * Update Ryujinx.csproj * Update Luea.csproj * Update Ryujinx.ShaderTools.csproj * Address PR feedback (further tested). * Address PR feedback.
This commit is contained in:
parent
267af1f0f7
commit
02a6fdcd13
21 changed files with 834 additions and 314 deletions
|
@ -380,8 +380,16 @@ namespace ChocolArm64
|
||||||
SetA64("0>001110<<100000011110xxxxxxxxxx", AInstEmit.Sqabs_V, typeof(AOpCodeSimd));
|
SetA64("0>001110<<100000011110xxxxxxxxxx", AInstEmit.Sqabs_V, typeof(AOpCodeSimd));
|
||||||
SetA64("01011110xx1xxxxx000011xxxxxxxxxx", AInstEmit.Sqadd_S, typeof(AOpCodeSimdReg));
|
SetA64("01011110xx1xxxxx000011xxxxxxxxxx", AInstEmit.Sqadd_S, typeof(AOpCodeSimdReg));
|
||||||
SetA64("0>001110<<1xxxxx000011xxxxxxxxxx", AInstEmit.Sqadd_V, typeof(AOpCodeSimdReg));
|
SetA64("0>001110<<1xxxxx000011xxxxxxxxxx", AInstEmit.Sqadd_V, typeof(AOpCodeSimdReg));
|
||||||
|
SetA64("01011110011xxxxx101101xxxxxxxxxx", AInstEmit.Sqdmulh_S, typeof(AOpCodeSimdReg));
|
||||||
|
SetA64("01011110101xxxxx101101xxxxxxxxxx", AInstEmit.Sqdmulh_S, typeof(AOpCodeSimdReg));
|
||||||
|
SetA64("0x001110011xxxxx101101xxxxxxxxxx", AInstEmit.Sqdmulh_V, typeof(AOpCodeSimdReg));
|
||||||
|
SetA64("0x001110101xxxxx101101xxxxxxxxxx", AInstEmit.Sqdmulh_V, typeof(AOpCodeSimdReg));
|
||||||
SetA64("01111110xx100000011110xxxxxxxxxx", AInstEmit.Sqneg_S, typeof(AOpCodeSimd));
|
SetA64("01111110xx100000011110xxxxxxxxxx", AInstEmit.Sqneg_S, typeof(AOpCodeSimd));
|
||||||
SetA64("0>101110<<100000011110xxxxxxxxxx", AInstEmit.Sqneg_V, typeof(AOpCodeSimd));
|
SetA64("0>101110<<100000011110xxxxxxxxxx", AInstEmit.Sqneg_V, typeof(AOpCodeSimd));
|
||||||
|
SetA64("01111110011xxxxx101101xxxxxxxxxx", AInstEmit.Sqrdmulh_S, typeof(AOpCodeSimdReg));
|
||||||
|
SetA64("01111110101xxxxx101101xxxxxxxxxx", AInstEmit.Sqrdmulh_S, typeof(AOpCodeSimdReg));
|
||||||
|
SetA64("0x101110011xxxxx101101xxxxxxxxxx", AInstEmit.Sqrdmulh_V, typeof(AOpCodeSimdReg));
|
||||||
|
SetA64("0x101110101xxxxx101101xxxxxxxxxx", AInstEmit.Sqrdmulh_V, typeof(AOpCodeSimdReg));
|
||||||
SetA64("0x00111100>>>xxx100111xxxxxxxxxx", AInstEmit.Sqrshrn_V, typeof(AOpCodeSimdShImm));
|
SetA64("0x00111100>>>xxx100111xxxxxxxxxx", AInstEmit.Sqrshrn_V, typeof(AOpCodeSimdShImm));
|
||||||
SetA64("01011110xx1xxxxx001011xxxxxxxxxx", AInstEmit.Sqsub_S, typeof(AOpCodeSimdReg));
|
SetA64("01011110xx1xxxxx001011xxxxxxxxxx", AInstEmit.Sqsub_S, typeof(AOpCodeSimdReg));
|
||||||
SetA64("0>001110<<1xxxxx001011xxxxxxxxxx", AInstEmit.Sqsub_V, typeof(AOpCodeSimdReg));
|
SetA64("0>001110<<1xxxxx001011xxxxxxxxxx", AInstEmit.Sqsub_V, typeof(AOpCodeSimdReg));
|
||||||
|
|
|
@ -158,6 +158,42 @@ namespace ChocolArm64.Instruction
|
||||||
Context.MarkLabel(LblTrue);
|
Context.MarkLabel(LblTrue);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void EmitDoublingMultiplyHighHalf(AILEmitterCtx Context, bool Round)
|
||||||
|
{
|
||||||
|
AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp;
|
||||||
|
|
||||||
|
int ESize = 8 << Op.Size;
|
||||||
|
|
||||||
|
Context.Emit(OpCodes.Mul);
|
||||||
|
|
||||||
|
if (!Round)
|
||||||
|
{
|
||||||
|
Context.EmitAsr(ESize - 1);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
long RoundConst = 1L << (ESize - 1);
|
||||||
|
|
||||||
|
AILLabel LblTrue = new AILLabel();
|
||||||
|
|
||||||
|
Context.EmitLsl(1);
|
||||||
|
|
||||||
|
Context.EmitLdc_I8(RoundConst);
|
||||||
|
|
||||||
|
Context.Emit(OpCodes.Add);
|
||||||
|
|
||||||
|
Context.EmitAsr(ESize);
|
||||||
|
|
||||||
|
Context.Emit(OpCodes.Dup);
|
||||||
|
Context.EmitLdc_I8((long)int.MinValue);
|
||||||
|
Context.Emit(OpCodes.Bne_Un_S, LblTrue);
|
||||||
|
|
||||||
|
Context.Emit(OpCodes.Neg);
|
||||||
|
|
||||||
|
Context.MarkLabel(LblTrue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static void EmitHighNarrow(AILEmitterCtx Context, Action Emit, bool Round)
|
private static void EmitHighNarrow(AILEmitterCtx Context, Action Emit, bool Round)
|
||||||
{
|
{
|
||||||
AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp;
|
AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp;
|
||||||
|
@ -1040,6 +1076,16 @@ namespace ChocolArm64.Instruction
|
||||||
EmitVectorSaturatingBinaryOpSx(Context, SaturatingFlags.Add);
|
EmitVectorSaturatingBinaryOpSx(Context, SaturatingFlags.Add);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void Sqdmulh_S(AILEmitterCtx Context)
|
||||||
|
{
|
||||||
|
EmitSaturatingBinaryOp(Context, () => EmitDoublingMultiplyHighHalf(Context, Round: false), SaturatingFlags.ScalarSx);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Sqdmulh_V(AILEmitterCtx Context)
|
||||||
|
{
|
||||||
|
EmitSaturatingBinaryOp(Context, () => EmitDoublingMultiplyHighHalf(Context, Round: false), SaturatingFlags.VectorSx);
|
||||||
|
}
|
||||||
|
|
||||||
public static void Sqneg_S(AILEmitterCtx Context)
|
public static void Sqneg_S(AILEmitterCtx Context)
|
||||||
{
|
{
|
||||||
EmitScalarSaturatingUnaryOpSx(Context, () => Context.Emit(OpCodes.Neg));
|
EmitScalarSaturatingUnaryOpSx(Context, () => Context.Emit(OpCodes.Neg));
|
||||||
|
@ -1050,6 +1096,16 @@ namespace ChocolArm64.Instruction
|
||||||
EmitVectorSaturatingUnaryOpSx(Context, () => Context.Emit(OpCodes.Neg));
|
EmitVectorSaturatingUnaryOpSx(Context, () => Context.Emit(OpCodes.Neg));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void Sqrdmulh_S(AILEmitterCtx Context)
|
||||||
|
{
|
||||||
|
EmitSaturatingBinaryOp(Context, () => EmitDoublingMultiplyHighHalf(Context, Round: true), SaturatingFlags.ScalarSx);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Sqrdmulh_V(AILEmitterCtx Context)
|
||||||
|
{
|
||||||
|
EmitSaturatingBinaryOp(Context, () => EmitDoublingMultiplyHighHalf(Context, Round: true), SaturatingFlags.VectorSx);
|
||||||
|
}
|
||||||
|
|
||||||
public static void Sqsub_S(AILEmitterCtx Context)
|
public static void Sqsub_S(AILEmitterCtx Context)
|
||||||
{
|
{
|
||||||
EmitScalarSaturatingBinaryOpSx(Context, SaturatingFlags.Sub);
|
EmitScalarSaturatingBinaryOpSx(Context, SaturatingFlags.Sub);
|
||||||
|
|
|
@ -804,7 +804,7 @@ namespace ChocolArm64.Instruction
|
||||||
ScalarZx = Scalar,
|
ScalarZx = Scalar,
|
||||||
|
|
||||||
VectorSx = Signed,
|
VectorSx = Signed,
|
||||||
VectorZx = 0,
|
VectorZx = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void EmitScalarSaturatingUnaryOpSx(AILEmitterCtx Context, Action Emit)
|
public static void EmitScalarSaturatingUnaryOpSx(AILEmitterCtx Context, Action Emit)
|
||||||
|
@ -837,7 +837,14 @@ namespace ChocolArm64.Instruction
|
||||||
|
|
||||||
Emit();
|
Emit();
|
||||||
|
|
||||||
EmitUnarySignedSatQAbsOrNeg(Context, Op.Size);
|
if (Op.Size <= 2)
|
||||||
|
{
|
||||||
|
EmitSatQ(Context, Op.Size, true, true);
|
||||||
|
}
|
||||||
|
else /* if (Op.Size == 3) */
|
||||||
|
{
|
||||||
|
EmitUnarySignedSatQAbsOrNeg(Context);
|
||||||
|
}
|
||||||
|
|
||||||
EmitVectorInsertTmp(Context, Index, Op.Size);
|
EmitVectorInsertTmp(Context, Index, Op.Size);
|
||||||
}
|
}
|
||||||
|
@ -853,25 +860,25 @@ namespace ChocolArm64.Instruction
|
||||||
|
|
||||||
public static void EmitScalarSaturatingBinaryOpSx(AILEmitterCtx Context, SaturatingFlags Flags)
|
public static void EmitScalarSaturatingBinaryOpSx(AILEmitterCtx Context, SaturatingFlags Flags)
|
||||||
{
|
{
|
||||||
EmitSaturatingBinaryOp(Context, SaturatingFlags.ScalarSx | Flags);
|
EmitSaturatingBinaryOp(Context, () => { }, SaturatingFlags.ScalarSx | Flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void EmitScalarSaturatingBinaryOpZx(AILEmitterCtx Context, SaturatingFlags Flags)
|
public static void EmitScalarSaturatingBinaryOpZx(AILEmitterCtx Context, SaturatingFlags Flags)
|
||||||
{
|
{
|
||||||
EmitSaturatingBinaryOp(Context, SaturatingFlags.ScalarZx | Flags);
|
EmitSaturatingBinaryOp(Context, () => { }, SaturatingFlags.ScalarZx | Flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void EmitVectorSaturatingBinaryOpSx(AILEmitterCtx Context, SaturatingFlags Flags)
|
public static void EmitVectorSaturatingBinaryOpSx(AILEmitterCtx Context, SaturatingFlags Flags)
|
||||||
{
|
{
|
||||||
EmitSaturatingBinaryOp(Context, SaturatingFlags.VectorSx | Flags);
|
EmitSaturatingBinaryOp(Context, () => { }, SaturatingFlags.VectorSx | Flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void EmitVectorSaturatingBinaryOpZx(AILEmitterCtx Context, SaturatingFlags Flags)
|
public static void EmitVectorSaturatingBinaryOpZx(AILEmitterCtx Context, SaturatingFlags Flags)
|
||||||
{
|
{
|
||||||
EmitSaturatingBinaryOp(Context, SaturatingFlags.VectorZx | Flags);
|
EmitSaturatingBinaryOp(Context, () => { }, SaturatingFlags.VectorZx | Flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void EmitSaturatingBinaryOp(AILEmitterCtx Context, SaturatingFlags Flags)
|
public static void EmitSaturatingBinaryOp(AILEmitterCtx Context, Action Emit, SaturatingFlags Flags)
|
||||||
{
|
{
|
||||||
AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp;
|
AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp;
|
||||||
|
|
||||||
|
@ -940,6 +947,20 @@ namespace ChocolArm64.Instruction
|
||||||
EmitVectorInsertTmp(Context, Index, Op.Size);
|
EmitVectorInsertTmp(Context, Index, Op.Size);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
for (int Index = 0; Index < Elems; Index++)
|
||||||
|
{
|
||||||
|
EmitVectorExtract(Context, Op.Rn, Index, Op.Size, Signed);
|
||||||
|
EmitVectorExtract(Context, ((AOpCodeSimdReg)Op).Rm, Index, Op.Size, Signed);
|
||||||
|
|
||||||
|
Emit();
|
||||||
|
|
||||||
|
EmitSatQ(Context, Op.Size, true, Signed);
|
||||||
|
|
||||||
|
EmitVectorInsertTmp(Context, Index, Op.Size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Context.EmitLdvectmp();
|
Context.EmitLdvectmp();
|
||||||
Context.EmitStvec(Op.Rd);
|
Context.EmitStvec(Op.Rd);
|
||||||
|
@ -1080,29 +1101,17 @@ namespace ChocolArm64.Instruction
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TSrc (8bit, 16bit, 32bit, 64bit) == TDst (8bit, 16bit, 32bit, 64bit); signed.
|
// TSrc (64bit) == TDst (64bit); signed.
|
||||||
public static void EmitUnarySignedSatQAbsOrNeg(AILEmitterCtx Context, int Size)
|
public static void EmitUnarySignedSatQAbsOrNeg(AILEmitterCtx Context)
|
||||||
{
|
{
|
||||||
int ESize = 8 << Size;
|
if (((AOpCodeSimd)Context.CurrOp).Size < 3)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
long TMaxValue = (1L << (ESize - 1)) - 1L;
|
Context.EmitLdarg(ATranslatedSub.StateArgIdx);
|
||||||
long TMinValue = -(1L << (ESize - 1));
|
|
||||||
|
|
||||||
AILLabel LblFalse = new AILLabel();
|
ASoftFallback.EmitCall(Context, nameof(ASoftFallback.UnarySignedSatQAbsOrNeg));
|
||||||
|
|
||||||
Context.Emit(OpCodes.Dup);
|
|
||||||
Context.Emit(OpCodes.Neg);
|
|
||||||
Context.EmitLdc_I8(TMinValue);
|
|
||||||
Context.Emit(OpCodes.Ceq);
|
|
||||||
Context.Emit(OpCodes.Brfalse_S, LblFalse);
|
|
||||||
|
|
||||||
Context.Emit(OpCodes.Pop);
|
|
||||||
|
|
||||||
EmitSetFpsrQCFlag(Context);
|
|
||||||
|
|
||||||
Context.EmitLdc_I8(TMaxValue);
|
|
||||||
|
|
||||||
Context.MarkLabel(LblFalse);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// TSrcs (64bit) == TDst (64bit); signed, unsigned.
|
// TSrcs (64bit) == TDst (64bit); signed, unsigned.
|
||||||
|
@ -1150,22 +1159,6 @@ namespace ChocolArm64.Instruction
|
||||||
: nameof(ASoftFallback.BinaryUnsignedSatQAcc));
|
: nameof(ASoftFallback.BinaryUnsignedSatQAcc));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void EmitSetFpsrQCFlag(AILEmitterCtx Context)
|
|
||||||
{
|
|
||||||
const int QCFlagBit = 27;
|
|
||||||
|
|
||||||
Context.EmitLdarg(ATranslatedSub.StateArgIdx);
|
|
||||||
|
|
||||||
Context.EmitLdarg(ATranslatedSub.StateArgIdx);
|
|
||||||
Context.EmitCallPropGet(typeof(AThreadState), nameof(AThreadState.Fpsr));
|
|
||||||
|
|
||||||
Context.EmitLdc_I4(1 << QCFlagBit);
|
|
||||||
|
|
||||||
Context.Emit(OpCodes.Or);
|
|
||||||
|
|
||||||
Context.EmitCallPropSet(typeof(AThreadState), nameof(AThreadState.Fpsr));
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void EmitScalarSet(AILEmitterCtx Context, int Reg, int Size)
|
public static void EmitScalarSet(AILEmitterCtx Context, int Reg, int Size)
|
||||||
{
|
{
|
||||||
EmitVectorZeroAll(Context, Reg);
|
EmitVectorZeroAll(Context, Reg);
|
||||||
|
|
|
@ -11,6 +11,107 @@ namespace ChocolArm64.Instruction
|
||||||
Context.EmitCall(typeof(ASoftFallback), MthdName);
|
Context.EmitCall(typeof(ASoftFallback), MthdName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#region "Saturating"
|
||||||
|
public static long SignedSrcSignedDstSatQ(long op, int Size, AThreadState State)
|
||||||
|
{
|
||||||
|
int ESize = 8 << Size;
|
||||||
|
|
||||||
|
long TMaxValue = (1L << (ESize - 1)) - 1L;
|
||||||
|
long TMinValue = -(1L << (ESize - 1));
|
||||||
|
|
||||||
|
if (op > TMaxValue)
|
||||||
|
{
|
||||||
|
SetFpsrQCFlag(State);
|
||||||
|
|
||||||
|
return TMaxValue;
|
||||||
|
}
|
||||||
|
else if (op < TMinValue)
|
||||||
|
{
|
||||||
|
SetFpsrQCFlag(State);
|
||||||
|
|
||||||
|
return TMinValue;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return op;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ulong SignedSrcUnsignedDstSatQ(long op, int Size, AThreadState State)
|
||||||
|
{
|
||||||
|
int ESize = 8 << Size;
|
||||||
|
|
||||||
|
ulong TMaxValue = (1UL << ESize) - 1UL;
|
||||||
|
ulong TMinValue = 0UL;
|
||||||
|
|
||||||
|
if (op > (long)TMaxValue)
|
||||||
|
{
|
||||||
|
SetFpsrQCFlag(State);
|
||||||
|
|
||||||
|
return TMaxValue;
|
||||||
|
}
|
||||||
|
else if (op < (long)TMinValue)
|
||||||
|
{
|
||||||
|
SetFpsrQCFlag(State);
|
||||||
|
|
||||||
|
return TMinValue;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return (ulong)op;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static long UnsignedSrcSignedDstSatQ(ulong op, int Size, AThreadState State)
|
||||||
|
{
|
||||||
|
int ESize = 8 << Size;
|
||||||
|
|
||||||
|
long TMaxValue = (1L << (ESize - 1)) - 1L;
|
||||||
|
|
||||||
|
if (op > (ulong)TMaxValue)
|
||||||
|
{
|
||||||
|
SetFpsrQCFlag(State);
|
||||||
|
|
||||||
|
return TMaxValue;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return (long)op;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ulong UnsignedSrcUnsignedDstSatQ(ulong op, int Size, AThreadState State)
|
||||||
|
{
|
||||||
|
int ESize = 8 << Size;
|
||||||
|
|
||||||
|
ulong TMaxValue = (1UL << ESize) - 1UL;
|
||||||
|
|
||||||
|
if (op > TMaxValue)
|
||||||
|
{
|
||||||
|
SetFpsrQCFlag(State);
|
||||||
|
|
||||||
|
return TMaxValue;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return op;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static long UnarySignedSatQAbsOrNeg(long op, AThreadState State)
|
||||||
|
{
|
||||||
|
if (op == long.MinValue)
|
||||||
|
{
|
||||||
|
SetFpsrQCFlag(State);
|
||||||
|
|
||||||
|
return long.MaxValue;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return op;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static long BinarySignedSatQAdd(long op1, long op2, AThreadState State)
|
public static long BinarySignedSatQAdd(long op1, long op2, AThreadState State)
|
||||||
{
|
{
|
||||||
long Add = op1 + op2;
|
long Add = op1 + op2;
|
||||||
|
@ -185,99 +286,15 @@ namespace ChocolArm64.Instruction
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static long SignedSrcSignedDstSatQ(long op, int Size, AThreadState State)
|
|
||||||
{
|
|
||||||
int ESize = 8 << Size;
|
|
||||||
|
|
||||||
long TMaxValue = (1L << (ESize - 1)) - 1L;
|
|
||||||
long TMinValue = -(1L << (ESize - 1));
|
|
||||||
|
|
||||||
if (op > TMaxValue)
|
|
||||||
{
|
|
||||||
SetFpsrQCFlag(State);
|
|
||||||
|
|
||||||
return TMaxValue;
|
|
||||||
}
|
|
||||||
else if (op < TMinValue)
|
|
||||||
{
|
|
||||||
SetFpsrQCFlag(State);
|
|
||||||
|
|
||||||
return TMinValue;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return op;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static ulong SignedSrcUnsignedDstSatQ(long op, int Size, AThreadState State)
|
|
||||||
{
|
|
||||||
int ESize = 8 << Size;
|
|
||||||
|
|
||||||
ulong TMaxValue = (1UL << ESize) - 1UL;
|
|
||||||
ulong TMinValue = 0UL;
|
|
||||||
|
|
||||||
if (op > (long)TMaxValue)
|
|
||||||
{
|
|
||||||
SetFpsrQCFlag(State);
|
|
||||||
|
|
||||||
return TMaxValue;
|
|
||||||
}
|
|
||||||
else if (op < (long)TMinValue)
|
|
||||||
{
|
|
||||||
SetFpsrQCFlag(State);
|
|
||||||
|
|
||||||
return TMinValue;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return (ulong)op;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static long UnsignedSrcSignedDstSatQ(ulong op, int Size, AThreadState State)
|
|
||||||
{
|
|
||||||
int ESize = 8 << Size;
|
|
||||||
|
|
||||||
long TMaxValue = (1L << (ESize - 1)) - 1L;
|
|
||||||
|
|
||||||
if (op > (ulong)TMaxValue)
|
|
||||||
{
|
|
||||||
SetFpsrQCFlag(State);
|
|
||||||
|
|
||||||
return TMaxValue;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return (long)op;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static ulong UnsignedSrcUnsignedDstSatQ(ulong op, int Size, AThreadState State)
|
|
||||||
{
|
|
||||||
int ESize = 8 << Size;
|
|
||||||
|
|
||||||
ulong TMaxValue = (1UL << ESize) - 1UL;
|
|
||||||
|
|
||||||
if (op > TMaxValue)
|
|
||||||
{
|
|
||||||
SetFpsrQCFlag(State);
|
|
||||||
|
|
||||||
return TMaxValue;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return op;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void SetFpsrQCFlag(AThreadState State)
|
private static void SetFpsrQCFlag(AThreadState State)
|
||||||
{
|
{
|
||||||
const int QCFlagBit = 27;
|
const int QCFlagBit = 27;
|
||||||
|
|
||||||
State.Fpsr |= 1 << QCFlagBit;
|
State.Fpsr |= 1 << QCFlagBit;
|
||||||
}
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region "Count"
|
||||||
public static ulong CountLeadingSigns(ulong Value, int Size)
|
public static ulong CountLeadingSigns(ulong Value, int Size)
|
||||||
{
|
{
|
||||||
Value ^= Value >> 1;
|
Value ^= Value >> 1;
|
||||||
|
@ -325,7 +342,9 @@ namespace ChocolArm64.Instruction
|
||||||
|
|
||||||
return (Value >> 4) + (Value & 0x0f);
|
return (Value >> 4) + (Value & 0x0f);
|
||||||
}
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region "Crc32"
|
||||||
private const uint Crc32RevPoly = 0xedb88320;
|
private const uint Crc32RevPoly = 0xedb88320;
|
||||||
private const uint Crc32cRevPoly = 0x82f63b78;
|
private const uint Crc32cRevPoly = 0x82f63b78;
|
||||||
|
|
||||||
|
@ -384,7 +403,9 @@ namespace ChocolArm64.Instruction
|
||||||
|
|
||||||
return Crc;
|
return Crc;
|
||||||
}
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region "Reverse"
|
||||||
public static uint ReverseBits8(uint Value)
|
public static uint ReverseBits8(uint Value)
|
||||||
{
|
{
|
||||||
Value = ((Value & 0xaa) >> 1) | ((Value & 0x55) << 1);
|
Value = ((Value & 0xaa) >> 1) | ((Value & 0x55) << 1);
|
||||||
|
@ -453,7 +474,9 @@ namespace ChocolArm64.Instruction
|
||||||
|
|
||||||
throw new ArgumentException(nameof(Size));
|
throw new ArgumentException(nameof(Size));
|
||||||
}
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region "MultiplyHigh"
|
||||||
public static long SMulHi128(long LHS, long RHS)
|
public static long SMulHi128(long LHS, long RHS)
|
||||||
{
|
{
|
||||||
long Result = (long)UMulHi128((ulong)LHS, (ulong)RHS);
|
long Result = (long)UMulHi128((ulong)LHS, (ulong)RHS);
|
||||||
|
@ -479,5 +502,6 @@ namespace ChocolArm64.Instruction
|
||||||
|
|
||||||
return LHigh * RHigh + Z0 + (Z1 >> 32);
|
return LHigh * RHigh + Z0 + (Z1 >> 32);
|
||||||
}
|
}
|
||||||
|
#endregion
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<OutputType>Exe</OutputType>
|
|
||||||
<TargetFramework>netcoreapp2.1</TargetFramework>
|
<TargetFramework>netcoreapp2.1</TargetFramework>
|
||||||
<RuntimeIdentifiers>win10-x64;osx-x64;linux-x64</RuntimeIdentifiers>
|
<RuntimeIdentifiers>win10-x64;osx-x64;linux-x64</RuntimeIdentifiers>
|
||||||
|
<OutputType>Exe</OutputType>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|
|
@ -1,13 +1,13 @@
|
||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>netcoreapp2.1</TargetFramework>
|
||||||
|
<RuntimeIdentifiers>win10-x64;osx-x64;linux-x64</RuntimeIdentifiers>
|
||||||
|
<OutputType>Exe</OutputType>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\Ryujinx.Graphics\Ryujinx.Graphics.csproj" />
|
<ProjectReference Include="..\Ryujinx.Graphics\Ryujinx.Graphics.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<PropertyGroup>
|
|
||||||
<OutputType>Exe</OutputType>
|
|
||||||
<TargetFramework>netcoreapp2.1</TargetFramework>
|
|
||||||
<RuntimeIdentifiers>win10-x64;osx-x64;linux-x64</RuntimeIdentifiers>
|
|
||||||
</PropertyGroup>
|
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|
|
@ -9,7 +9,7 @@ namespace Ryujinx.Tests.Cpu
|
||||||
using Tester;
|
using Tester;
|
||||||
using Tester.Types;
|
using Tester.Types;
|
||||||
|
|
||||||
[Category("Alu"), Ignore("Tested: first half of 2018.")]
|
[Category("Alu"), Ignore("Tested: second half of 2018.")]
|
||||||
public sealed class CpuTestAlu : CpuTest
|
public sealed class CpuTestAlu : CpuTest
|
||||||
{
|
{
|
||||||
#if Alu
|
#if Alu
|
||||||
|
|
|
@ -9,7 +9,7 @@ namespace Ryujinx.Tests.Cpu
|
||||||
using Tester;
|
using Tester;
|
||||||
using Tester.Types;
|
using Tester.Types;
|
||||||
|
|
||||||
[Category("AluImm"), Ignore("Tested: first half of 2018.")]
|
[Category("AluImm"), Ignore("Tested: second half of 2018.")]
|
||||||
public sealed class CpuTestAluImm : CpuTest
|
public sealed class CpuTestAluImm : CpuTest
|
||||||
{
|
{
|
||||||
#if AluImm
|
#if AluImm
|
||||||
|
|
|
@ -9,7 +9,7 @@ namespace Ryujinx.Tests.Cpu
|
||||||
using Tester;
|
using Tester;
|
||||||
using Tester.Types;
|
using Tester.Types;
|
||||||
|
|
||||||
[Category("AluRs"), Ignore("Tested: first half of 2018.")]
|
[Category("AluRs"), Ignore("Tested: second half of 2018.")]
|
||||||
public sealed class CpuTestAluRs : CpuTest
|
public sealed class CpuTestAluRs : CpuTest
|
||||||
{
|
{
|
||||||
#if AluRs
|
#if AluRs
|
||||||
|
|
|
@ -9,7 +9,7 @@ namespace Ryujinx.Tests.Cpu
|
||||||
using Tester;
|
using Tester;
|
||||||
using Tester.Types;
|
using Tester.Types;
|
||||||
|
|
||||||
[Category("AluRx"), Ignore("Tested: first half of 2018.")]
|
[Category("AluRx"), Ignore("Tested: second half of 2018.")]
|
||||||
public sealed class CpuTestAluRx : CpuTest
|
public sealed class CpuTestAluRx : CpuTest
|
||||||
{
|
{
|
||||||
#if AluRx
|
#if AluRx
|
||||||
|
|
|
@ -9,7 +9,7 @@ namespace Ryujinx.Tests.Cpu
|
||||||
using Tester;
|
using Tester;
|
||||||
using Tester.Types;
|
using Tester.Types;
|
||||||
|
|
||||||
[Category("Bfm"), Ignore("Tested: first half of 2018.")]
|
[Category("Bfm"), Ignore("Tested: second half of 2018.")]
|
||||||
public sealed class CpuTestBfm : CpuTest
|
public sealed class CpuTestBfm : CpuTest
|
||||||
{
|
{
|
||||||
#if Bfm
|
#if Bfm
|
||||||
|
|
|
@ -9,7 +9,7 @@ namespace Ryujinx.Tests.Cpu
|
||||||
using Tester;
|
using Tester;
|
||||||
using Tester.Types;
|
using Tester.Types;
|
||||||
|
|
||||||
[Category("CcmpImm"), Ignore("Tested: first half of 2018.")]
|
[Category("CcmpImm"), Ignore("Tested: second half of 2018.")]
|
||||||
public sealed class CpuTestCcmpImm : CpuTest
|
public sealed class CpuTestCcmpImm : CpuTest
|
||||||
{
|
{
|
||||||
#if CcmpImm
|
#if CcmpImm
|
||||||
|
|
|
@ -9,7 +9,7 @@ namespace Ryujinx.Tests.Cpu
|
||||||
using Tester;
|
using Tester;
|
||||||
using Tester.Types;
|
using Tester.Types;
|
||||||
|
|
||||||
[Category("CcmpReg"), Ignore("Tested: first half of 2018.")]
|
[Category("CcmpReg"), Ignore("Tested: second half of 2018.")]
|
||||||
public sealed class CpuTestCcmpReg : CpuTest
|
public sealed class CpuTestCcmpReg : CpuTest
|
||||||
{
|
{
|
||||||
#if CcmpReg
|
#if CcmpReg
|
||||||
|
|
|
@ -9,7 +9,7 @@ namespace Ryujinx.Tests.Cpu
|
||||||
using Tester;
|
using Tester;
|
||||||
using Tester.Types;
|
using Tester.Types;
|
||||||
|
|
||||||
[Category("Csel"), Ignore("Tested: first half of 2018.")]
|
[Category("Csel"), Ignore("Tested: second half of 2018.")]
|
||||||
public sealed class CpuTestCsel : CpuTest
|
public sealed class CpuTestCsel : CpuTest
|
||||||
{
|
{
|
||||||
#if Csel
|
#if Csel
|
||||||
|
|
|
@ -9,7 +9,7 @@ namespace Ryujinx.Tests.Cpu
|
||||||
using Tester;
|
using Tester;
|
||||||
using Tester.Types;
|
using Tester.Types;
|
||||||
|
|
||||||
[Category("Mov"), Ignore("Tested: first half of 2018.")]
|
[Category("Mov"), Ignore("Tested: second half of 2018.")]
|
||||||
public sealed class CpuTestMov : CpuTest
|
public sealed class CpuTestMov : CpuTest
|
||||||
{
|
{
|
||||||
#if Mov
|
#if Mov
|
||||||
|
|
|
@ -9,7 +9,7 @@ namespace Ryujinx.Tests.Cpu
|
||||||
using Tester;
|
using Tester;
|
||||||
using Tester.Types;
|
using Tester.Types;
|
||||||
|
|
||||||
[Category("Mul"), Ignore("Tested: first half of 2018.")]
|
[Category("Mul"), Ignore("Tested: second half of 2018.")]
|
||||||
public sealed class CpuTestMul : CpuTest
|
public sealed class CpuTestMul : CpuTest
|
||||||
{
|
{
|
||||||
#if Mul
|
#if Mul
|
||||||
|
|
|
@ -39,6 +39,21 @@ namespace Ryujinx.Tests.Cpu
|
||||||
0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul };
|
0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static ulong[] _1H1S_()
|
||||||
|
{
|
||||||
|
return new ulong[] { 0x0000000000000000ul, 0x0000000000007FFFul,
|
||||||
|
0x0000000000008000ul, 0x000000000000FFFFul,
|
||||||
|
0x000000007FFFFFFFul, 0x0000000080000000ul,
|
||||||
|
0x00000000FFFFFFFFul };
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ulong[] _4H2S_()
|
||||||
|
{
|
||||||
|
return new ulong[] { 0x0000000000000000ul, 0x7FFF7FFF7FFF7FFFul,
|
||||||
|
0x8000800080008000ul, 0x7FFFFFFF7FFFFFFFul,
|
||||||
|
0x8000000080000000ul, 0xFFFFFFFFFFFFFFFFul };
|
||||||
|
}
|
||||||
|
|
||||||
private static ulong[] _4H2S1D_()
|
private static ulong[] _4H2S1D_()
|
||||||
{
|
{
|
||||||
return new ulong[] { 0x0000000000000000ul, 0x7FFF7FFF7FFF7FFFul,
|
return new ulong[] { 0x0000000000000000ul, 0x7FFF7FFF7FFF7FFFul,
|
||||||
|
@ -1837,6 +1852,216 @@ namespace Ryujinx.Tests.Cpu
|
||||||
Assert.That(ThreadState.Fpsr, Is.EqualTo((int)Shared.FPSR.ToUInt32()));
|
Assert.That(ThreadState.Fpsr, Is.EqualTo((int)Shared.FPSR.ToUInt32()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Test, Pairwise, Description("SQDMULH <V><d>, <V><n>, <V><m>")]
|
||||||
|
public void Sqdmulh_S_H_S([Values(0u)] uint Rd,
|
||||||
|
[Values(1u, 0u)] uint Rn,
|
||||||
|
[Values(2u, 0u)] uint Rm,
|
||||||
|
[ValueSource("_1H1S_")] [Random(RndCnt)] ulong Z,
|
||||||
|
[ValueSource("_1H1S_")] [Random(RndCnt)] ulong A,
|
||||||
|
[ValueSource("_1H1S_")] [Random(RndCnt)] ulong B,
|
||||||
|
[Values(0b01u, 0b10u)] uint size) // <H, S>
|
||||||
|
{
|
||||||
|
uint Opcode = 0x5E20B400; // SQDMULH B0, B0, B0 (RESERVED)
|
||||||
|
Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0);
|
||||||
|
Opcode |= ((size & 3) << 22);
|
||||||
|
Bits Op = new Bits(Opcode);
|
||||||
|
|
||||||
|
int Fpsr = (int)TestContext.CurrentContext.Random.NextUInt();
|
||||||
|
|
||||||
|
Vector128<float> V0 = MakeVectorE0E1(Z, Z);
|
||||||
|
Vector128<float> V1 = MakeVectorE0(A);
|
||||||
|
Vector128<float> V2 = MakeVectorE0(B);
|
||||||
|
AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2, Fpsr: Fpsr);
|
||||||
|
|
||||||
|
AArch64.Vpart(0, 0, new Bits(Z)); AArch64.Vpart(0, 1, new Bits(Z));
|
||||||
|
AArch64.V(1, new Bits(A));
|
||||||
|
AArch64.V(2, new Bits(B));
|
||||||
|
Shared.FPSR = new Bits((uint)Fpsr);
|
||||||
|
SimdFp.Sqdmulh_S(Op[23, 22], Op[20, 16], Op[9, 5], Op[4, 0]);
|
||||||
|
|
||||||
|
Assert.Multiple(() =>
|
||||||
|
{
|
||||||
|
Assert.That(GetVectorE0(ThreadState.V0), Is.EqualTo(AArch64.Vpart(64, 0, 0).ToUInt64()));
|
||||||
|
Assert.That(GetVectorE1(ThreadState.V0), Is.EqualTo(AArch64.Vpart(64, 0, 1).ToUInt64()));
|
||||||
|
});
|
||||||
|
Assert.That(ThreadState.Fpsr, Is.EqualTo((int)Shared.FPSR.ToUInt32()));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test, Pairwise, Description("SQDMULH <Vd>.<T>, <Vn>.<T>, <Vm>.<T>")]
|
||||||
|
public void Sqdmulh_V_4H_2S([Values(0u)] uint Rd,
|
||||||
|
[Values(1u, 0u)] uint Rn,
|
||||||
|
[Values(2u, 0u)] uint Rm,
|
||||||
|
[ValueSource("_4H2S_")] [Random(RndCnt)] ulong Z,
|
||||||
|
[ValueSource("_4H2S_")] [Random(RndCnt)] ulong A,
|
||||||
|
[ValueSource("_4H2S_")] [Random(RndCnt)] ulong B,
|
||||||
|
[Values(0b01u, 0b10u)] uint size) // <4H, 2S>
|
||||||
|
{
|
||||||
|
uint Opcode = 0x0E20B400; // SQDMULH V0.8B, V0.8B, V0.8B (RESERVED)
|
||||||
|
Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0);
|
||||||
|
Opcode |= ((size & 3) << 22);
|
||||||
|
Bits Op = new Bits(Opcode);
|
||||||
|
|
||||||
|
int Fpsr = (int)TestContext.CurrentContext.Random.NextUInt();
|
||||||
|
|
||||||
|
Vector128<float> V0 = MakeVectorE0E1(Z, Z);
|
||||||
|
Vector128<float> V1 = MakeVectorE0(A);
|
||||||
|
Vector128<float> V2 = MakeVectorE0(B);
|
||||||
|
AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2, Fpsr: Fpsr);
|
||||||
|
|
||||||
|
AArch64.Vpart(0, 0, new Bits(Z)); AArch64.Vpart(0, 1, new Bits(Z));
|
||||||
|
AArch64.V(1, new Bits(A));
|
||||||
|
AArch64.V(2, new Bits(B));
|
||||||
|
Shared.FPSR = new Bits((uint)Fpsr);
|
||||||
|
SimdFp.Sqdmulh_V(Op[30], Op[23, 22], Op[20, 16], Op[9, 5], Op[4, 0]);
|
||||||
|
|
||||||
|
Assert.Multiple(() =>
|
||||||
|
{
|
||||||
|
Assert.That(GetVectorE0(ThreadState.V0), Is.EqualTo(AArch64.Vpart(64, 0, 0).ToUInt64()));
|
||||||
|
Assert.That(GetVectorE1(ThreadState.V0), Is.EqualTo(AArch64.Vpart(64, 0, 1).ToUInt64()));
|
||||||
|
});
|
||||||
|
Assert.That(ThreadState.Fpsr, Is.EqualTo((int)Shared.FPSR.ToUInt32()));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test, Pairwise, Description("SQDMULH <Vd>.<T>, <Vn>.<T>, <Vm>.<T>")]
|
||||||
|
public void Sqdmulh_V_8H_4S([Values(0u)] uint Rd,
|
||||||
|
[Values(1u, 0u)] uint Rn,
|
||||||
|
[Values(2u, 0u)] uint Rm,
|
||||||
|
[ValueSource("_4H2S_")] [Random(RndCnt)] ulong Z,
|
||||||
|
[ValueSource("_4H2S_")] [Random(RndCnt)] ulong A,
|
||||||
|
[ValueSource("_4H2S_")] [Random(RndCnt)] ulong B,
|
||||||
|
[Values(0b01u, 0b10u)] uint size) // <8H, 4S>
|
||||||
|
{
|
||||||
|
uint Opcode = 0x4E20B400; // SQDMULH V0.16B, V0.16B, V0.16B (RESERVED)
|
||||||
|
Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0);
|
||||||
|
Opcode |= ((size & 3) << 22);
|
||||||
|
Bits Op = new Bits(Opcode);
|
||||||
|
|
||||||
|
int Fpsr = (int)TestContext.CurrentContext.Random.NextUInt();
|
||||||
|
|
||||||
|
Vector128<float> V0 = MakeVectorE0E1(Z, Z);
|
||||||
|
Vector128<float> V1 = MakeVectorE0E1(A, A);
|
||||||
|
Vector128<float> V2 = MakeVectorE0E1(B, B);
|
||||||
|
AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2, Fpsr: Fpsr);
|
||||||
|
|
||||||
|
AArch64.Vpart(0, 0, new Bits(Z)); AArch64.Vpart(0, 1, new Bits(Z));
|
||||||
|
AArch64.Vpart(1, 0, new Bits(A)); AArch64.Vpart(1, 1, new Bits(A));
|
||||||
|
AArch64.Vpart(2, 0, new Bits(B)); AArch64.Vpart(2, 1, new Bits(B));
|
||||||
|
Shared.FPSR = new Bits((uint)Fpsr);
|
||||||
|
SimdFp.Sqdmulh_V(Op[30], Op[23, 22], Op[20, 16], Op[9, 5], Op[4, 0]);
|
||||||
|
|
||||||
|
Assert.Multiple(() =>
|
||||||
|
{
|
||||||
|
Assert.That(GetVectorE0(ThreadState.V0), Is.EqualTo(AArch64.Vpart(64, 0, 0).ToUInt64()));
|
||||||
|
Assert.That(GetVectorE1(ThreadState.V0), Is.EqualTo(AArch64.Vpart(64, 0, 1).ToUInt64()));
|
||||||
|
});
|
||||||
|
Assert.That(ThreadState.Fpsr, Is.EqualTo((int)Shared.FPSR.ToUInt32()));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test, Pairwise, Description("SQRDMULH <V><d>, <V><n>, <V><m>")]
|
||||||
|
public void Sqrdmulh_S_H_S([Values(0u)] uint Rd,
|
||||||
|
[Values(1u, 0u)] uint Rn,
|
||||||
|
[Values(2u, 0u)] uint Rm,
|
||||||
|
[ValueSource("_1H1S_")] [Random(RndCnt)] ulong Z,
|
||||||
|
[ValueSource("_1H1S_")] [Random(RndCnt)] ulong A,
|
||||||
|
[ValueSource("_1H1S_")] [Random(RndCnt)] ulong B,
|
||||||
|
[Values(0b01u, 0b10u)] uint size) // <H, S>
|
||||||
|
{
|
||||||
|
uint Opcode = 0x7E20B400; // SQRDMULH B0, B0, B0 (RESERVED)
|
||||||
|
Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0);
|
||||||
|
Opcode |= ((size & 3) << 22);
|
||||||
|
Bits Op = new Bits(Opcode);
|
||||||
|
|
||||||
|
int Fpsr = (int)TestContext.CurrentContext.Random.NextUInt();
|
||||||
|
|
||||||
|
Vector128<float> V0 = MakeVectorE0E1(Z, Z);
|
||||||
|
Vector128<float> V1 = MakeVectorE0(A);
|
||||||
|
Vector128<float> V2 = MakeVectorE0(B);
|
||||||
|
AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2, Fpsr: Fpsr);
|
||||||
|
|
||||||
|
AArch64.Vpart(0, 0, new Bits(Z)); AArch64.Vpart(0, 1, new Bits(Z));
|
||||||
|
AArch64.V(1, new Bits(A));
|
||||||
|
AArch64.V(2, new Bits(B));
|
||||||
|
Shared.FPSR = new Bits((uint)Fpsr);
|
||||||
|
SimdFp.Sqrdmulh_S(Op[23, 22], Op[20, 16], Op[9, 5], Op[4, 0]);
|
||||||
|
|
||||||
|
Assert.Multiple(() =>
|
||||||
|
{
|
||||||
|
Assert.That(GetVectorE0(ThreadState.V0), Is.EqualTo(AArch64.Vpart(64, 0, 0).ToUInt64()));
|
||||||
|
Assert.That(GetVectorE1(ThreadState.V0), Is.EqualTo(AArch64.Vpart(64, 0, 1).ToUInt64()));
|
||||||
|
});
|
||||||
|
Assert.That(ThreadState.Fpsr, Is.EqualTo((int)Shared.FPSR.ToUInt32()));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test, Pairwise, Description("SQRDMULH <Vd>.<T>, <Vn>.<T>, <Vm>.<T>")]
|
||||||
|
public void Sqrdmulh_V_4H_2S([Values(0u)] uint Rd,
|
||||||
|
[Values(1u, 0u)] uint Rn,
|
||||||
|
[Values(2u, 0u)] uint Rm,
|
||||||
|
[ValueSource("_4H2S_")] [Random(RndCnt)] ulong Z,
|
||||||
|
[ValueSource("_4H2S_")] [Random(RndCnt)] ulong A,
|
||||||
|
[ValueSource("_4H2S_")] [Random(RndCnt)] ulong B,
|
||||||
|
[Values(0b01u, 0b10u)] uint size) // <4H, 2S>
|
||||||
|
{
|
||||||
|
uint Opcode = 0x2E20B400; // SQRDMULH V0.8B, V0.8B, V0.8B (RESERVED)
|
||||||
|
Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0);
|
||||||
|
Opcode |= ((size & 3) << 22);
|
||||||
|
Bits Op = new Bits(Opcode);
|
||||||
|
|
||||||
|
int Fpsr = (int)TestContext.CurrentContext.Random.NextUInt();
|
||||||
|
|
||||||
|
Vector128<float> V0 = MakeVectorE0E1(Z, Z);
|
||||||
|
Vector128<float> V1 = MakeVectorE0(A);
|
||||||
|
Vector128<float> V2 = MakeVectorE0(B);
|
||||||
|
AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2, Fpsr: Fpsr);
|
||||||
|
|
||||||
|
AArch64.Vpart(0, 0, new Bits(Z)); AArch64.Vpart(0, 1, new Bits(Z));
|
||||||
|
AArch64.V(1, new Bits(A));
|
||||||
|
AArch64.V(2, new Bits(B));
|
||||||
|
Shared.FPSR = new Bits((uint)Fpsr);
|
||||||
|
SimdFp.Sqrdmulh_V(Op[30], Op[23, 22], Op[20, 16], Op[9, 5], Op[4, 0]);
|
||||||
|
|
||||||
|
Assert.Multiple(() =>
|
||||||
|
{
|
||||||
|
Assert.That(GetVectorE0(ThreadState.V0), Is.EqualTo(AArch64.Vpart(64, 0, 0).ToUInt64()));
|
||||||
|
Assert.That(GetVectorE1(ThreadState.V0), Is.EqualTo(AArch64.Vpart(64, 0, 1).ToUInt64()));
|
||||||
|
});
|
||||||
|
Assert.That(ThreadState.Fpsr, Is.EqualTo((int)Shared.FPSR.ToUInt32()));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test, Pairwise, Description("SQRDMULH <Vd>.<T>, <Vn>.<T>, <Vm>.<T>")]
|
||||||
|
public void Sqrdmulh_V_8H_4S([Values(0u)] uint Rd,
|
||||||
|
[Values(1u, 0u)] uint Rn,
|
||||||
|
[Values(2u, 0u)] uint Rm,
|
||||||
|
[ValueSource("_4H2S_")] [Random(RndCnt)] ulong Z,
|
||||||
|
[ValueSource("_4H2S_")] [Random(RndCnt)] ulong A,
|
||||||
|
[ValueSource("_4H2S_")] [Random(RndCnt)] ulong B,
|
||||||
|
[Values(0b01u, 0b10u)] uint size) // <8H, 4S>
|
||||||
|
{
|
||||||
|
uint Opcode = 0x6E20B400; // SQRDMULH V0.16B, V0.16B, V0.16B (RESERVED)
|
||||||
|
Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0);
|
||||||
|
Opcode |= ((size & 3) << 22);
|
||||||
|
Bits Op = new Bits(Opcode);
|
||||||
|
|
||||||
|
int Fpsr = (int)TestContext.CurrentContext.Random.NextUInt();
|
||||||
|
|
||||||
|
Vector128<float> V0 = MakeVectorE0E1(Z, Z);
|
||||||
|
Vector128<float> V1 = MakeVectorE0E1(A, A);
|
||||||
|
Vector128<float> V2 = MakeVectorE0E1(B, B);
|
||||||
|
AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2, Fpsr: Fpsr);
|
||||||
|
|
||||||
|
AArch64.Vpart(0, 0, new Bits(Z)); AArch64.Vpart(0, 1, new Bits(Z));
|
||||||
|
AArch64.Vpart(1, 0, new Bits(A)); AArch64.Vpart(1, 1, new Bits(A));
|
||||||
|
AArch64.Vpart(2, 0, new Bits(B)); AArch64.Vpart(2, 1, new Bits(B));
|
||||||
|
Shared.FPSR = new Bits((uint)Fpsr);
|
||||||
|
SimdFp.Sqrdmulh_V(Op[30], Op[23, 22], Op[20, 16], Op[9, 5], Op[4, 0]);
|
||||||
|
|
||||||
|
Assert.Multiple(() =>
|
||||||
|
{
|
||||||
|
Assert.That(GetVectorE0(ThreadState.V0), Is.EqualTo(AArch64.Vpart(64, 0, 0).ToUInt64()));
|
||||||
|
Assert.That(GetVectorE1(ThreadState.V0), Is.EqualTo(AArch64.Vpart(64, 0, 1).ToUInt64()));
|
||||||
|
});
|
||||||
|
Assert.That(ThreadState.Fpsr, Is.EqualTo((int)Shared.FPSR.ToUInt32()));
|
||||||
|
}
|
||||||
|
|
||||||
[Test, Pairwise, Description("SQSUB <V><d>, <V><n>, <V><m>")]
|
[Test, Pairwise, Description("SQSUB <V><d>, <V><n>, <V><m>")]
|
||||||
public void Sqsub_S_B_H_S_D([Values(0u)] uint Rd,
|
public void Sqsub_S_B_H_S_D([Values(0u)] uint Rd,
|
||||||
[Values(1u, 0u)] uint Rn,
|
[Values(1u, 0u)] uint Rn,
|
||||||
|
|
|
@ -5075,6 +5075,210 @@ namespace Ryujinx.Tests.Cpu.Tester
|
||||||
V(d, result);
|
V(d, result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// sqdmulh_advsimd_vec.html#SQDMULH_asisdsame_only
|
||||||
|
public static void Sqdmulh_S(Bits size, Bits Rm, Bits Rn, Bits Rd)
|
||||||
|
{
|
||||||
|
const bool U = false;
|
||||||
|
|
||||||
|
/* Decode Scalar */
|
||||||
|
int d = (int)UInt(Rd);
|
||||||
|
int n = (int)UInt(Rn);
|
||||||
|
int m = (int)UInt(Rm);
|
||||||
|
|
||||||
|
/* if size == '11' || size == '00' then ReservedValue(); */
|
||||||
|
|
||||||
|
int esize = 8 << (int)UInt(size);
|
||||||
|
int datasize = esize;
|
||||||
|
int elements = 1;
|
||||||
|
|
||||||
|
bool rounding = (U == true);
|
||||||
|
|
||||||
|
/* Operation */
|
||||||
|
/* CheckFPAdvSIMDEnabled64(); */
|
||||||
|
|
||||||
|
Bits result = new Bits(datasize);
|
||||||
|
Bits operand1 = V(datasize, n);
|
||||||
|
Bits operand2 = V(datasize, m);
|
||||||
|
BigInteger round_const = (rounding ? (BigInteger)1 << (esize - 1) : 0);
|
||||||
|
BigInteger element1;
|
||||||
|
BigInteger element2;
|
||||||
|
BigInteger product;
|
||||||
|
bool sat;
|
||||||
|
|
||||||
|
for (int e = 0; e <= elements - 1; e++)
|
||||||
|
{
|
||||||
|
element1 = SInt(Elem(operand1, e, esize));
|
||||||
|
element2 = SInt(Elem(operand2, e, esize));
|
||||||
|
|
||||||
|
product = (2 * element1 * element2) + round_const;
|
||||||
|
|
||||||
|
(Bits _result, bool _sat) = SignedSatQ(product >> esize, esize);
|
||||||
|
Elem(result, e, esize, _result);
|
||||||
|
sat = _sat;
|
||||||
|
|
||||||
|
if (sat)
|
||||||
|
{
|
||||||
|
/* FPSR.QC = '1'; */
|
||||||
|
FPSR[27] = true; // TODO: Add named fields.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
V(d, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
// sqdmulh_advsimd_vec.html#SQDMULH_asimdsame_only
|
||||||
|
public static void Sqdmulh_V(bool Q, Bits size, Bits Rm, Bits Rn, Bits Rd)
|
||||||
|
{
|
||||||
|
const bool U = false;
|
||||||
|
|
||||||
|
/* Decode Vector */
|
||||||
|
int d = (int)UInt(Rd);
|
||||||
|
int n = (int)UInt(Rn);
|
||||||
|
int m = (int)UInt(Rm);
|
||||||
|
|
||||||
|
/* if size == '11' || size == '00' then ReservedValue(); */
|
||||||
|
|
||||||
|
int esize = 8 << (int)UInt(size);
|
||||||
|
int datasize = (Q ? 128 : 64);
|
||||||
|
int elements = datasize / esize;
|
||||||
|
|
||||||
|
bool rounding = (U == true);
|
||||||
|
|
||||||
|
/* Operation */
|
||||||
|
/* CheckFPAdvSIMDEnabled64(); */
|
||||||
|
|
||||||
|
Bits result = new Bits(datasize);
|
||||||
|
Bits operand1 = V(datasize, n);
|
||||||
|
Bits operand2 = V(datasize, m);
|
||||||
|
BigInteger round_const = (rounding ? (BigInteger)1 << (esize - 1) : 0);
|
||||||
|
BigInteger element1;
|
||||||
|
BigInteger element2;
|
||||||
|
BigInteger product;
|
||||||
|
bool sat;
|
||||||
|
|
||||||
|
for (int e = 0; e <= elements - 1; e++)
|
||||||
|
{
|
||||||
|
element1 = SInt(Elem(operand1, e, esize));
|
||||||
|
element2 = SInt(Elem(operand2, e, esize));
|
||||||
|
|
||||||
|
product = (2 * element1 * element2) + round_const;
|
||||||
|
|
||||||
|
(Bits _result, bool _sat) = SignedSatQ(product >> esize, esize);
|
||||||
|
Elem(result, e, esize, _result);
|
||||||
|
sat = _sat;
|
||||||
|
|
||||||
|
if (sat)
|
||||||
|
{
|
||||||
|
/* FPSR.QC = '1'; */
|
||||||
|
FPSR[27] = true; // TODO: Add named fields.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
V(d, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
// sqrdmulh_advsimd_vec.html#SQRDMULH_asisdsame_only
|
||||||
|
public static void Sqrdmulh_S(Bits size, Bits Rm, Bits Rn, Bits Rd)
|
||||||
|
{
|
||||||
|
const bool U = true;
|
||||||
|
|
||||||
|
/* Decode Scalar */
|
||||||
|
int d = (int)UInt(Rd);
|
||||||
|
int n = (int)UInt(Rn);
|
||||||
|
int m = (int)UInt(Rm);
|
||||||
|
|
||||||
|
/* if size == '11' || size == '00' then ReservedValue(); */
|
||||||
|
|
||||||
|
int esize = 8 << (int)UInt(size);
|
||||||
|
int datasize = esize;
|
||||||
|
int elements = 1;
|
||||||
|
|
||||||
|
bool rounding = (U == true);
|
||||||
|
|
||||||
|
/* Operation */
|
||||||
|
/* CheckFPAdvSIMDEnabled64(); */
|
||||||
|
|
||||||
|
Bits result = new Bits(datasize);
|
||||||
|
Bits operand1 = V(datasize, n);
|
||||||
|
Bits operand2 = V(datasize, m);
|
||||||
|
BigInteger round_const = (rounding ? (BigInteger)1 << (esize - 1) : 0);
|
||||||
|
BigInteger element1;
|
||||||
|
BigInteger element2;
|
||||||
|
BigInteger product;
|
||||||
|
bool sat;
|
||||||
|
|
||||||
|
for (int e = 0; e <= elements - 1; e++)
|
||||||
|
{
|
||||||
|
element1 = SInt(Elem(operand1, e, esize));
|
||||||
|
element2 = SInt(Elem(operand2, e, esize));
|
||||||
|
|
||||||
|
product = (2 * element1 * element2) + round_const;
|
||||||
|
|
||||||
|
(Bits _result, bool _sat) = SignedSatQ(product >> esize, esize);
|
||||||
|
Elem(result, e, esize, _result);
|
||||||
|
sat = _sat;
|
||||||
|
|
||||||
|
if (sat)
|
||||||
|
{
|
||||||
|
/* FPSR.QC = '1'; */
|
||||||
|
FPSR[27] = true; // TODO: Add named fields.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
V(d, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
// sqrdmulh_advsimd_vec.html#SQRDMULH_asimdsame_only
|
||||||
|
public static void Sqrdmulh_V(bool Q, Bits size, Bits Rm, Bits Rn, Bits Rd)
|
||||||
|
{
|
||||||
|
const bool U = true;
|
||||||
|
|
||||||
|
/* Decode Vector */
|
||||||
|
int d = (int)UInt(Rd);
|
||||||
|
int n = (int)UInt(Rn);
|
||||||
|
int m = (int)UInt(Rm);
|
||||||
|
|
||||||
|
/* if size == '11' || size == '00' then ReservedValue(); */
|
||||||
|
|
||||||
|
int esize = 8 << (int)UInt(size);
|
||||||
|
int datasize = (Q ? 128 : 64);
|
||||||
|
int elements = datasize / esize;
|
||||||
|
|
||||||
|
bool rounding = (U == true);
|
||||||
|
|
||||||
|
/* Operation */
|
||||||
|
/* CheckFPAdvSIMDEnabled64(); */
|
||||||
|
|
||||||
|
Bits result = new Bits(datasize);
|
||||||
|
Bits operand1 = V(datasize, n);
|
||||||
|
Bits operand2 = V(datasize, m);
|
||||||
|
BigInteger round_const = (rounding ? (BigInteger)1 << (esize - 1) : 0);
|
||||||
|
BigInteger element1;
|
||||||
|
BigInteger element2;
|
||||||
|
BigInteger product;
|
||||||
|
bool sat;
|
||||||
|
|
||||||
|
for (int e = 0; e <= elements - 1; e++)
|
||||||
|
{
|
||||||
|
element1 = SInt(Elem(operand1, e, esize));
|
||||||
|
element2 = SInt(Elem(operand2, e, esize));
|
||||||
|
|
||||||
|
product = (2 * element1 * element2) + round_const;
|
||||||
|
|
||||||
|
(Bits _result, bool _sat) = SignedSatQ(product >> esize, esize);
|
||||||
|
Elem(result, e, esize, _result);
|
||||||
|
sat = _sat;
|
||||||
|
|
||||||
|
if (sat)
|
||||||
|
{
|
||||||
|
/* FPSR.QC = '1'; */
|
||||||
|
FPSR[27] = true; // TODO: Add named fields.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
V(d, result);
|
||||||
|
}
|
||||||
|
|
||||||
// sqsub_advsimd.html#SQSUB_asisdsame_only
|
// sqsub_advsimd.html#SQSUB_asisdsame_only
|
||||||
public static void Sqsub_S(Bits size, Bits Rm, Bits Rn, Bits Rd)
|
public static void Sqsub_S(Bits size, Bits Rm, Bits Rn, Bits Rd)
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,20 +1,25 @@
|
||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>netcoreapp2.1</TargetFramework>
|
<TargetFramework>netcoreapp2.1</TargetFramework>
|
||||||
<RuntimeIdentifiers>win10-x64;osx-x64;linux-x64</RuntimeIdentifiers>
|
<RuntimeIdentifiers>win10-x64;osx-x64;linux-x64</RuntimeIdentifiers>
|
||||||
<OutputType>Exe</OutputType>
|
<OutputType>Exe</OutputType>
|
||||||
<IsPackable>false</IsPackable>
|
<IsPackable>false</IsPackable>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
|
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.7.0" />
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.8.0" />
|
||||||
<PackageReference Include="NUnit" Version="3.10.1" />
|
<PackageReference Include="NUnit" Version="3.10.1" />
|
||||||
<PackageReference Include="NUnit3TestAdapter" Version="3.10.0" />
|
<PackageReference Include="NUnit3TestAdapter" Version="3.10.0" />
|
||||||
<PackageReference Include="System.Runtime.Intrinsics.Experimental" Version="4.5.0-rc1" />
|
<PackageReference Include="System.Runtime.Intrinsics.Experimental" Version="4.5.0-rc1" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\ChocolArm64\ChocolArm64.csproj" />
|
<ProjectReference Include="..\ChocolArm64\ChocolArm64.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|
|
@ -1,23 +1,28 @@
|
||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<OutputType>Exe</OutputType>
|
|
||||||
<TargetFramework>netcoreapp2.1</TargetFramework>
|
<TargetFramework>netcoreapp2.1</TargetFramework>
|
||||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
|
||||||
<RuntimeIdentifiers>win10-x64;osx-x64;linux-x64</RuntimeIdentifiers>
|
<RuntimeIdentifiers>win10-x64;osx-x64;linux-x64</RuntimeIdentifiers>
|
||||||
|
<OutputType>Exe</OutputType>
|
||||||
|
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="OpenTK.NetStandard" Version="1.0.4" />
|
<PackageReference Include="OpenTK.NetStandard" Version="1.0.4" />
|
||||||
<PackageReference Include="System.Runtime.CompilerServices.Unsafe" Version="4.4.0" />
|
<PackageReference Include="System.Runtime.CompilerServices.Unsafe" Version="4.4.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\ChocolArm64\ChocolArm64.csproj" />
|
<ProjectReference Include="..\ChocolArm64\ChocolArm64.csproj" />
|
||||||
<ProjectReference Include="..\Ryujinx.Audio\Ryujinx.Audio.csproj" />
|
<ProjectReference Include="..\Ryujinx.Audio\Ryujinx.Audio.csproj" />
|
||||||
<ProjectReference Include="..\Ryujinx.HLE\Ryujinx.HLE.csproj" />
|
|
||||||
<ProjectReference Include="..\Ryujinx.Graphics\Ryujinx.Graphics.csproj" />
|
<ProjectReference Include="..\Ryujinx.Graphics\Ryujinx.Graphics.csproj" />
|
||||||
|
<ProjectReference Include="..\Ryujinx.HLE\Ryujinx.HLE.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<None Update="Ryujinx.conf">
|
<None Update="Ryujinx.conf">
|
||||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
</None>
|
</None>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
Loading…
Reference in a new issue