Add 9+7 fast/slow FP inst. impls.; add 14 FP Tests. (#437)
* Update CpuTest.cs * Delete CpuTestSimdCmp.cs Obsolete. * Update CpuTestSimdArithmetic.cs Superseded. * Update CpuTestSimd.cs * Update CpuTestSimdReg.cs * Update AInstEmitSimdArithmetic.cs * Update AInstEmitSimdHelper.cs * Update ASoftFloat.cs * Nit. * Update AOpCodeTable.cs * Update AOptimizations.cs * Update AInstEmitSimdArithmetic.cs * Update ASoftFloat.cs * Update CpuTest.cs * Update CpuTestSimd.cs * Update CpuTestSimdReg.cs * Update AOpCodeTable.cs * Update AInstEmitSimdArithmetic.cs * Update ASoftFloat.cs * Update CpuTestSimdReg.cs * Update AOpCodeTable.cs * Update AInstEmitSimdArithmetic.cs * Update ASoftFloat.cs * Update CpuTestSimd.cs * Update CpuTestSimdReg.cs
This commit is contained in:
parent
0254a84f90
commit
bba9bf97d0
10 changed files with 2471 additions and 1162 deletions
|
@ -301,6 +301,8 @@ namespace ChocolArm64
|
||||||
SetA64("010111111<<xxxxx1001x0xxxxxxxxxx", AInstEmit.Fmul_Se, typeof(AOpCodeSimdRegElemF));
|
SetA64("010111111<<xxxxx1001x0xxxxxxxxxx", AInstEmit.Fmul_Se, typeof(AOpCodeSimdRegElemF));
|
||||||
SetA64("0>1011100<1xxxxx110111xxxxxxxxxx", AInstEmit.Fmul_V, typeof(AOpCodeSimdReg));
|
SetA64("0>1011100<1xxxxx110111xxxxxxxxxx", AInstEmit.Fmul_V, typeof(AOpCodeSimdReg));
|
||||||
SetA64("0x0011111<<xxxxx1001x0xxxxxxxxxx", AInstEmit.Fmul_Ve, typeof(AOpCodeSimdRegElemF));
|
SetA64("0x0011111<<xxxxx1001x0xxxxxxxxxx", AInstEmit.Fmul_Ve, typeof(AOpCodeSimdRegElemF));
|
||||||
|
SetA64("010111100x1xxxxx110111xxxxxxxxxx", AInstEmit.Fmulx_S, typeof(AOpCodeSimdReg));
|
||||||
|
SetA64("0>0011100<1xxxxx110111xxxxxxxxxx", AInstEmit.Fmulx_V, typeof(AOpCodeSimdReg));
|
||||||
SetA64("000111100x100001010000xxxxxxxxxx", AInstEmit.Fneg_S, typeof(AOpCodeSimd));
|
SetA64("000111100x100001010000xxxxxxxxxx", AInstEmit.Fneg_S, typeof(AOpCodeSimd));
|
||||||
SetA64("0>1011101<100000111110xxxxxxxxxx", AInstEmit.Fneg_V, typeof(AOpCodeSimd));
|
SetA64("0>1011101<100000111110xxxxxxxxxx", AInstEmit.Fneg_V, typeof(AOpCodeSimd));
|
||||||
SetA64("000111110x1xxxxx0xxxxxxxxxxxxxxx", AInstEmit.Fnmadd_S, typeof(AOpCodeSimdReg));
|
SetA64("000111110x1xxxxx0xxxxxxxxxxxxxxx", AInstEmit.Fnmadd_S, typeof(AOpCodeSimdReg));
|
||||||
|
@ -310,6 +312,7 @@ namespace ChocolArm64
|
||||||
SetA64("0>0011101<100001110110xxxxxxxxxx", AInstEmit.Frecpe_V, typeof(AOpCodeSimd));
|
SetA64("0>0011101<100001110110xxxxxxxxxx", AInstEmit.Frecpe_V, typeof(AOpCodeSimd));
|
||||||
SetA64("010111100x1xxxxx111111xxxxxxxxxx", AInstEmit.Frecps_S, typeof(AOpCodeSimdReg));
|
SetA64("010111100x1xxxxx111111xxxxxxxxxx", AInstEmit.Frecps_S, typeof(AOpCodeSimdReg));
|
||||||
SetA64("0>0011100<1xxxxx111111xxxxxxxxxx", AInstEmit.Frecps_V, typeof(AOpCodeSimdReg));
|
SetA64("0>0011100<1xxxxx111111xxxxxxxxxx", AInstEmit.Frecps_V, typeof(AOpCodeSimdReg));
|
||||||
|
SetA64("010111101x100001111110xxxxxxxxxx", AInstEmit.Frecpx_S, typeof(AOpCodeSimd));
|
||||||
SetA64("000111100x100110010000xxxxxxxxxx", AInstEmit.Frinta_S, typeof(AOpCodeSimd));
|
SetA64("000111100x100110010000xxxxxxxxxx", AInstEmit.Frinta_S, typeof(AOpCodeSimd));
|
||||||
SetA64("0>1011100<100001100010xxxxxxxxxx", AInstEmit.Frinta_V, typeof(AOpCodeSimd));
|
SetA64("0>1011100<100001100010xxxxxxxxxx", AInstEmit.Frinta_V, typeof(AOpCodeSimd));
|
||||||
SetA64("000111100x100111110000xxxxxxxxxx", AInstEmit.Frinti_S, typeof(AOpCodeSimd));
|
SetA64("000111100x100111110000xxxxxxxxxx", AInstEmit.Frinti_S, typeof(AOpCodeSimd));
|
||||||
|
@ -327,6 +330,7 @@ namespace ChocolArm64
|
||||||
SetA64("010111101x1xxxxx111111xxxxxxxxxx", AInstEmit.Frsqrts_S, typeof(AOpCodeSimdReg));
|
SetA64("010111101x1xxxxx111111xxxxxxxxxx", AInstEmit.Frsqrts_S, typeof(AOpCodeSimdReg));
|
||||||
SetA64("0>0011101<1xxxxx111111xxxxxxxxxx", AInstEmit.Frsqrts_V, typeof(AOpCodeSimdReg));
|
SetA64("0>0011101<1xxxxx111111xxxxxxxxxx", AInstEmit.Frsqrts_V, typeof(AOpCodeSimdReg));
|
||||||
SetA64("000111100x100001110000xxxxxxxxxx", AInstEmit.Fsqrt_S, typeof(AOpCodeSimd));
|
SetA64("000111100x100001110000xxxxxxxxxx", AInstEmit.Fsqrt_S, typeof(AOpCodeSimd));
|
||||||
|
SetA64("0>1011101<100001111110xxxxxxxxxx", AInstEmit.Fsqrt_V, typeof(AOpCodeSimd));
|
||||||
SetA64("000111100x1xxxxx001110xxxxxxxxxx", AInstEmit.Fsub_S, typeof(AOpCodeSimdReg));
|
SetA64("000111100x1xxxxx001110xxxxxxxxxx", AInstEmit.Fsub_S, typeof(AOpCodeSimdReg));
|
||||||
SetA64("0>0011101<1xxxxx110101xxxxxxxxxx", AInstEmit.Fsub_V, typeof(AOpCodeSimdReg));
|
SetA64("0>0011101<1xxxxx110101xxxxxxxxxx", AInstEmit.Fsub_V, typeof(AOpCodeSimdReg));
|
||||||
SetA64("01001110000xxxxx000111xxxxxxxxxx", AInstEmit.Ins_Gp, typeof(AOpCodeSimdIns));
|
SetA64("01001110000xxxxx000111xxxxxxxxxx", AInstEmit.Ins_Gp, typeof(AOpCodeSimdIns));
|
||||||
|
|
|
@ -2,6 +2,8 @@ using System.Runtime.Intrinsics.X86;
|
||||||
|
|
||||||
public static class AOptimizations
|
public static class AOptimizations
|
||||||
{
|
{
|
||||||
|
internal static bool FastFP = true;
|
||||||
|
|
||||||
private static bool UseAllSseIfAvailable = true;
|
private static bool UseAllSseIfAvailable = true;
|
||||||
|
|
||||||
private static bool UseSseIfAvailable = true;
|
private static bool UseSseIfAvailable = true;
|
||||||
|
|
|
@ -174,25 +174,33 @@ namespace ChocolArm64.Instruction
|
||||||
|
|
||||||
public static void Fadd_S(AILEmitterCtx Context)
|
public static void Fadd_S(AILEmitterCtx Context)
|
||||||
{
|
{
|
||||||
if (AOptimizations.UseSse && AOptimizations.UseSse2)
|
if (AOptimizations.FastFP && AOptimizations.UseSse
|
||||||
|
&& AOptimizations.UseSse2)
|
||||||
{
|
{
|
||||||
EmitScalarSseOrSse2OpF(Context, nameof(Sse.AddScalar));
|
EmitScalarSseOrSse2OpF(Context, nameof(Sse.AddScalar));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
EmitScalarBinaryOpF(Context, () => Context.Emit(OpCodes.Add));
|
EmitScalarBinaryOpF(Context, () =>
|
||||||
|
{
|
||||||
|
EmitSoftFloatCall(Context, nameof(ASoftFloat_32.FPAdd));
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void Fadd_V(AILEmitterCtx Context)
|
public static void Fadd_V(AILEmitterCtx Context)
|
||||||
{
|
{
|
||||||
if (AOptimizations.UseSse && AOptimizations.UseSse2)
|
if (AOptimizations.FastFP && AOptimizations.UseSse
|
||||||
|
&& AOptimizations.UseSse2)
|
||||||
{
|
{
|
||||||
EmitVectorSseOrSse2OpF(Context, nameof(Sse.Add));
|
EmitVectorSseOrSse2OpF(Context, nameof(Sse.Add));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
EmitVectorBinaryOpF(Context, () => Context.Emit(OpCodes.Add));
|
EmitVectorBinaryOpF(Context, () =>
|
||||||
|
{
|
||||||
|
EmitSoftFloatCall(Context, nameof(ASoftFloat_32.FPAdd));
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -217,42 +225,50 @@ namespace ChocolArm64.Instruction
|
||||||
|
|
||||||
public static void Fdiv_S(AILEmitterCtx Context)
|
public static void Fdiv_S(AILEmitterCtx Context)
|
||||||
{
|
{
|
||||||
if (AOptimizations.UseSse && AOptimizations.UseSse2)
|
if (AOptimizations.FastFP && AOptimizations.UseSse
|
||||||
|
&& AOptimizations.UseSse2)
|
||||||
{
|
{
|
||||||
EmitScalarSseOrSse2OpF(Context, nameof(Sse.DivideScalar));
|
EmitScalarSseOrSse2OpF(Context, nameof(Sse.DivideScalar));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
EmitScalarBinaryOpF(Context, () => Context.Emit(OpCodes.Div));
|
EmitScalarBinaryOpF(Context, () =>
|
||||||
|
{
|
||||||
|
EmitSoftFloatCall(Context, nameof(ASoftFloat_32.FPDiv));
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void Fdiv_V(AILEmitterCtx Context)
|
public static void Fdiv_V(AILEmitterCtx Context)
|
||||||
{
|
{
|
||||||
if (AOptimizations.UseSse && AOptimizations.UseSse2)
|
if (AOptimizations.FastFP && AOptimizations.UseSse
|
||||||
|
&& AOptimizations.UseSse2)
|
||||||
{
|
{
|
||||||
EmitVectorSseOrSse2OpF(Context, nameof(Sse.Divide));
|
EmitVectorSseOrSse2OpF(Context, nameof(Sse.Divide));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
EmitVectorBinaryOpF(Context, () => Context.Emit(OpCodes.Div));
|
EmitVectorBinaryOpF(Context, () =>
|
||||||
|
{
|
||||||
|
EmitSoftFloatCall(Context, nameof(ASoftFloat_32.FPDiv));
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void Fmadd_S(AILEmitterCtx Context)
|
public static void Fmadd_S(AILEmitterCtx Context)
|
||||||
{
|
{
|
||||||
if (AOptimizations.UseSse2)
|
if (AOptimizations.FastFP && AOptimizations.UseSse2)
|
||||||
{
|
{
|
||||||
AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp;
|
AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp;
|
||||||
|
|
||||||
if (Op.Size == 0)
|
if (Op.Size == 0)
|
||||||
{
|
{
|
||||||
|
Type[] Types = new Type[] { typeof(Vector128<float>), typeof(Vector128<float>) };
|
||||||
|
|
||||||
Context.EmitLdvec(Op.Ra);
|
Context.EmitLdvec(Op.Ra);
|
||||||
Context.EmitLdvec(Op.Rn);
|
Context.EmitLdvec(Op.Rn);
|
||||||
Context.EmitLdvec(Op.Rm);
|
Context.EmitLdvec(Op.Rm);
|
||||||
|
|
||||||
Type[] Types = new Type[] { typeof(Vector128<float>), typeof(Vector128<float>) };
|
|
||||||
|
|
||||||
Context.EmitCall(typeof(Sse).GetMethod(nameof(Sse.MultiplyScalar), Types));
|
Context.EmitCall(typeof(Sse).GetMethod(nameof(Sse.MultiplyScalar), Types));
|
||||||
Context.EmitCall(typeof(Sse).GetMethod(nameof(Sse.AddScalar), Types));
|
Context.EmitCall(typeof(Sse).GetMethod(nameof(Sse.AddScalar), Types));
|
||||||
|
|
||||||
|
@ -262,12 +278,12 @@ namespace ChocolArm64.Instruction
|
||||||
}
|
}
|
||||||
else /* if (Op.Size == 1) */
|
else /* if (Op.Size == 1) */
|
||||||
{
|
{
|
||||||
|
Type[] Types = new Type[] { typeof(Vector128<double>), typeof(Vector128<double>) };
|
||||||
|
|
||||||
EmitLdvecWithCastToDouble(Context, Op.Ra);
|
EmitLdvecWithCastToDouble(Context, Op.Ra);
|
||||||
EmitLdvecWithCastToDouble(Context, Op.Rn);
|
EmitLdvecWithCastToDouble(Context, Op.Rn);
|
||||||
EmitLdvecWithCastToDouble(Context, Op.Rm);
|
EmitLdvecWithCastToDouble(Context, Op.Rm);
|
||||||
|
|
||||||
Type[] Types = new Type[] { typeof(Vector128<double>), typeof(Vector128<double>) };
|
|
||||||
|
|
||||||
Context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.MultiplyScalar), Types));
|
Context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.MultiplyScalar), Types));
|
||||||
Context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.AddScalar), Types));
|
Context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.AddScalar), Types));
|
||||||
|
|
||||||
|
@ -280,33 +296,48 @@ namespace ChocolArm64.Instruction
|
||||||
{
|
{
|
||||||
EmitScalarTernaryRaOpF(Context, () =>
|
EmitScalarTernaryRaOpF(Context, () =>
|
||||||
{
|
{
|
||||||
Context.Emit(OpCodes.Mul);
|
EmitSoftFloatCall(Context, nameof(ASoftFloat_32.FPMulAdd));
|
||||||
Context.Emit(OpCodes.Add);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void Fmax_S(AILEmitterCtx Context)
|
public static void Fmax_S(AILEmitterCtx Context)
|
||||||
{
|
{
|
||||||
EmitScalarBinaryOpF(Context, () =>
|
if (AOptimizations.FastFP && AOptimizations.UseSse
|
||||||
|
&& AOptimizations.UseSse2)
|
||||||
{
|
{
|
||||||
EmitBinarySoftFloatCall(Context, nameof(ASoftFloat.Max));
|
EmitScalarSseOrSse2OpF(Context, nameof(Sse.MaxScalar));
|
||||||
});
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
EmitScalarBinaryOpF(Context, () =>
|
||||||
|
{
|
||||||
|
EmitSoftFloatCall(Context, nameof(ASoftFloat_32.FPMax));
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void Fmax_V(AILEmitterCtx Context)
|
public static void Fmax_V(AILEmitterCtx Context)
|
||||||
{
|
{
|
||||||
EmitVectorBinaryOpF(Context, () =>
|
if (AOptimizations.FastFP && AOptimizations.UseSse
|
||||||
|
&& AOptimizations.UseSse2)
|
||||||
{
|
{
|
||||||
EmitBinarySoftFloatCall(Context, nameof(ASoftFloat.Max));
|
EmitVectorSseOrSse2OpF(Context, nameof(Sse.Max));
|
||||||
});
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
EmitVectorBinaryOpF(Context, () =>
|
||||||
|
{
|
||||||
|
EmitSoftFloatCall(Context, nameof(ASoftFloat_32.FPMax));
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void Fmaxnm_S(AILEmitterCtx Context)
|
public static void Fmaxnm_S(AILEmitterCtx Context)
|
||||||
{
|
{
|
||||||
EmitScalarBinaryOpF(Context, () =>
|
EmitScalarBinaryOpF(Context, () =>
|
||||||
{
|
{
|
||||||
EmitBinarySoftFloatCall(Context, nameof(ASoftFloat.MaxNum));
|
EmitSoftFloatCall(Context, nameof(ASoftFloat_32.FPMaxNum));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -314,36 +345,55 @@ namespace ChocolArm64.Instruction
|
||||||
{
|
{
|
||||||
EmitVectorBinaryOpF(Context, () =>
|
EmitVectorBinaryOpF(Context, () =>
|
||||||
{
|
{
|
||||||
EmitBinarySoftFloatCall(Context, nameof(ASoftFloat.MaxNum));
|
EmitSoftFloatCall(Context, nameof(ASoftFloat_32.FPMaxNum));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void Fmaxp_V(AILEmitterCtx Context)
|
public static void Fmaxp_V(AILEmitterCtx Context)
|
||||||
{
|
{
|
||||||
EmitVectorPairwiseOpF(Context, () => EmitBinarySoftFloatCall(Context, nameof(ASoftFloat.Max)));
|
EmitVectorPairwiseOpF(Context, () =>
|
||||||
|
{
|
||||||
|
EmitSoftFloatCall(Context, nameof(ASoftFloat_32.FPMax));
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void Fmin_S(AILEmitterCtx Context)
|
public static void Fmin_S(AILEmitterCtx Context)
|
||||||
{
|
{
|
||||||
EmitScalarBinaryOpF(Context, () =>
|
if (AOptimizations.FastFP && AOptimizations.UseSse
|
||||||
|
&& AOptimizations.UseSse2)
|
||||||
{
|
{
|
||||||
EmitBinarySoftFloatCall(Context, nameof(ASoftFloat.Min));
|
EmitScalarSseOrSse2OpF(Context, nameof(Sse.MinScalar));
|
||||||
});
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
EmitScalarBinaryOpF(Context, () =>
|
||||||
|
{
|
||||||
|
EmitSoftFloatCall(Context, nameof(ASoftFloat_32.FPMin));
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void Fmin_V(AILEmitterCtx Context)
|
public static void Fmin_V(AILEmitterCtx Context)
|
||||||
{
|
{
|
||||||
EmitVectorBinaryOpF(Context, () =>
|
if (AOptimizations.FastFP && AOptimizations.UseSse
|
||||||
|
&& AOptimizations.UseSse2)
|
||||||
{
|
{
|
||||||
EmitBinarySoftFloatCall(Context, nameof(ASoftFloat.Min));
|
EmitVectorSseOrSse2OpF(Context, nameof(Sse.Min));
|
||||||
});
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
EmitVectorBinaryOpF(Context, () =>
|
||||||
|
{
|
||||||
|
EmitSoftFloatCall(Context, nameof(ASoftFloat_32.FPMin));
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void Fminnm_S(AILEmitterCtx Context)
|
public static void Fminnm_S(AILEmitterCtx Context)
|
||||||
{
|
{
|
||||||
EmitScalarBinaryOpF(Context, () =>
|
EmitScalarBinaryOpF(Context, () =>
|
||||||
{
|
{
|
||||||
EmitBinarySoftFloatCall(Context, nameof(ASoftFloat.MinNum));
|
EmitSoftFloatCall(Context, nameof(ASoftFloat_32.FPMinNum));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -351,13 +401,16 @@ namespace ChocolArm64.Instruction
|
||||||
{
|
{
|
||||||
EmitVectorBinaryOpF(Context, () =>
|
EmitVectorBinaryOpF(Context, () =>
|
||||||
{
|
{
|
||||||
EmitBinarySoftFloatCall(Context, nameof(ASoftFloat.MinNum));
|
EmitSoftFloatCall(Context, nameof(ASoftFloat_32.FPMinNum));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void Fminp_V(AILEmitterCtx Context)
|
public static void Fminp_V(AILEmitterCtx Context)
|
||||||
{
|
{
|
||||||
EmitVectorPairwiseOpF(Context, () => EmitBinarySoftFloatCall(Context, nameof(ASoftFloat.Min)));
|
EmitVectorPairwiseOpF(Context, () =>
|
||||||
|
{
|
||||||
|
EmitSoftFloatCall(Context, nameof(ASoftFloat_32.FPMin));
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void Fmla_Se(AILEmitterCtx Context)
|
public static void Fmla_Se(AILEmitterCtx Context)
|
||||||
|
@ -407,22 +460,63 @@ namespace ChocolArm64.Instruction
|
||||||
|
|
||||||
public static void Fmsub_S(AILEmitterCtx Context)
|
public static void Fmsub_S(AILEmitterCtx Context)
|
||||||
{
|
{
|
||||||
EmitScalarTernaryRaOpF(Context, () =>
|
if (AOptimizations.FastFP && AOptimizations.UseSse2)
|
||||||
{
|
{
|
||||||
Context.Emit(OpCodes.Mul);
|
AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp;
|
||||||
Context.Emit(OpCodes.Sub);
|
|
||||||
});
|
if (Op.Size == 0)
|
||||||
|
{
|
||||||
|
Type[] Types = new Type[] { typeof(Vector128<float>), typeof(Vector128<float>) };
|
||||||
|
|
||||||
|
Context.EmitLdvec(Op.Ra);
|
||||||
|
Context.EmitLdvec(Op.Rn);
|
||||||
|
Context.EmitLdvec(Op.Rm);
|
||||||
|
|
||||||
|
Context.EmitCall(typeof(Sse).GetMethod(nameof(Sse.MultiplyScalar), Types));
|
||||||
|
Context.EmitCall(typeof(Sse).GetMethod(nameof(Sse.SubtractScalar), Types));
|
||||||
|
|
||||||
|
Context.EmitStvec(Op.Rd);
|
||||||
|
|
||||||
|
EmitVectorZero32_128(Context, Op.Rd);
|
||||||
|
}
|
||||||
|
else /* if (Op.Size == 1) */
|
||||||
|
{
|
||||||
|
Type[] Types = new Type[] { typeof(Vector128<double>), typeof(Vector128<double>) };
|
||||||
|
|
||||||
|
EmitLdvecWithCastToDouble(Context, Op.Ra);
|
||||||
|
EmitLdvecWithCastToDouble(Context, Op.Rn);
|
||||||
|
EmitLdvecWithCastToDouble(Context, Op.Rm);
|
||||||
|
|
||||||
|
Context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.MultiplyScalar), Types));
|
||||||
|
Context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.SubtractScalar), Types));
|
||||||
|
|
||||||
|
EmitStvecWithCastFromDouble(Context, Op.Rd);
|
||||||
|
|
||||||
|
EmitVectorZeroUpper(Context, Op.Rd);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
EmitScalarTernaryRaOpF(Context, () =>
|
||||||
|
{
|
||||||
|
EmitSoftFloatCall(Context, nameof(ASoftFloat_32.FPMulSub));
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void Fmul_S(AILEmitterCtx Context)
|
public static void Fmul_S(AILEmitterCtx Context)
|
||||||
{
|
{
|
||||||
if (AOptimizations.UseSse && AOptimizations.UseSse2)
|
if (AOptimizations.FastFP && AOptimizations.UseSse
|
||||||
|
&& AOptimizations.UseSse2)
|
||||||
{
|
{
|
||||||
EmitScalarSseOrSse2OpF(Context, nameof(Sse.MultiplyScalar));
|
EmitScalarSseOrSse2OpF(Context, nameof(Sse.MultiplyScalar));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
EmitScalarBinaryOpF(Context, () => Context.Emit(OpCodes.Mul));
|
EmitScalarBinaryOpF(Context, () =>
|
||||||
|
{
|
||||||
|
EmitSoftFloatCall(Context, nameof(ASoftFloat_32.FPMul));
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -433,13 +527,17 @@ namespace ChocolArm64.Instruction
|
||||||
|
|
||||||
public static void Fmul_V(AILEmitterCtx Context)
|
public static void Fmul_V(AILEmitterCtx Context)
|
||||||
{
|
{
|
||||||
if (AOptimizations.UseSse && AOptimizations.UseSse2)
|
if (AOptimizations.FastFP && AOptimizations.UseSse
|
||||||
|
&& AOptimizations.UseSse2)
|
||||||
{
|
{
|
||||||
EmitVectorSseOrSse2OpF(Context, nameof(Sse.Multiply));
|
EmitVectorSseOrSse2OpF(Context, nameof(Sse.Multiply));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
EmitVectorBinaryOpF(Context, () => Context.Emit(OpCodes.Mul));
|
EmitVectorBinaryOpF(Context, () =>
|
||||||
|
{
|
||||||
|
EmitSoftFloatCall(Context, nameof(ASoftFloat_32.FPMul));
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -448,6 +546,22 @@ namespace ChocolArm64.Instruction
|
||||||
EmitVectorBinaryOpByElemF(Context, () => Context.Emit(OpCodes.Mul));
|
EmitVectorBinaryOpByElemF(Context, () => Context.Emit(OpCodes.Mul));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void Fmulx_S(AILEmitterCtx Context)
|
||||||
|
{
|
||||||
|
EmitScalarBinaryOpF(Context, () =>
|
||||||
|
{
|
||||||
|
EmitSoftFloatCall(Context, nameof(ASoftFloat_32.FPMulX));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Fmulx_V(AILEmitterCtx Context)
|
||||||
|
{
|
||||||
|
EmitVectorBinaryOpF(Context, () =>
|
||||||
|
{
|
||||||
|
EmitSoftFloatCall(Context, nameof(ASoftFloat_32.FPMulX));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
public static void Fneg_S(AILEmitterCtx Context)
|
public static void Fneg_S(AILEmitterCtx Context)
|
||||||
{
|
{
|
||||||
EmitScalarUnaryOpF(Context, () => Context.Emit(OpCodes.Neg));
|
EmitScalarUnaryOpF(Context, () => Context.Emit(OpCodes.Neg));
|
||||||
|
@ -524,17 +638,122 @@ namespace ChocolArm64.Instruction
|
||||||
|
|
||||||
public static void Frecps_S(AILEmitterCtx Context)
|
public static void Frecps_S(AILEmitterCtx Context)
|
||||||
{
|
{
|
||||||
EmitScalarBinaryOpF(Context, () =>
|
if (AOptimizations.FastFP && AOptimizations.UseSse2)
|
||||||
{
|
{
|
||||||
EmitBinarySoftFloatCall(Context, nameof(ASoftFloat.RecipStep));
|
AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp;
|
||||||
});
|
|
||||||
|
int SizeF = Op.Size & 1;
|
||||||
|
|
||||||
|
if (SizeF == 0)
|
||||||
|
{
|
||||||
|
Type[] Types = new Type[] { typeof(float) };
|
||||||
|
|
||||||
|
Context.EmitLdc_R4(2f);
|
||||||
|
Context.EmitCall(typeof(Sse).GetMethod(nameof(Sse.SetScalarVector128), Types));
|
||||||
|
|
||||||
|
Types = new Type[] { typeof(Vector128<float>), typeof(Vector128<float>) };
|
||||||
|
|
||||||
|
Context.EmitLdvec(Op.Rn);
|
||||||
|
Context.EmitLdvec(Op.Rm);
|
||||||
|
|
||||||
|
Context.EmitCall(typeof(Sse).GetMethod(nameof(Sse.MultiplyScalar), Types));
|
||||||
|
Context.EmitCall(typeof(Sse).GetMethod(nameof(Sse.SubtractScalar), Types));
|
||||||
|
|
||||||
|
Context.EmitStvec(Op.Rd);
|
||||||
|
|
||||||
|
EmitVectorZero32_128(Context, Op.Rd);
|
||||||
|
}
|
||||||
|
else /* if (SizeF == 1) */
|
||||||
|
{
|
||||||
|
Type[] Types = new Type[] { typeof(double) };
|
||||||
|
|
||||||
|
Context.EmitLdc_R8(2d);
|
||||||
|
Context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.SetScalarVector128), Types));
|
||||||
|
|
||||||
|
Types = new Type[] { typeof(Vector128<double>), typeof(Vector128<double>) };
|
||||||
|
|
||||||
|
EmitLdvecWithCastToDouble(Context, Op.Rn);
|
||||||
|
EmitLdvecWithCastToDouble(Context, Op.Rm);
|
||||||
|
|
||||||
|
Context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.MultiplyScalar), Types));
|
||||||
|
Context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.SubtractScalar), Types));
|
||||||
|
|
||||||
|
EmitStvecWithCastFromDouble(Context, Op.Rd);
|
||||||
|
|
||||||
|
EmitVectorZeroUpper(Context, Op.Rd);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
EmitScalarBinaryOpF(Context, () =>
|
||||||
|
{
|
||||||
|
EmitSoftFloatCall(Context, nameof(ASoftFloat_32.FPRecipStepFused));
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void Frecps_V(AILEmitterCtx Context)
|
public static void Frecps_V(AILEmitterCtx Context)
|
||||||
{
|
{
|
||||||
EmitVectorBinaryOpF(Context, () =>
|
if (AOptimizations.FastFP && AOptimizations.UseSse2)
|
||||||
{
|
{
|
||||||
EmitBinarySoftFloatCall(Context, nameof(ASoftFloat.RecipStep));
|
AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp;
|
||||||
|
|
||||||
|
int SizeF = Op.Size & 1;
|
||||||
|
|
||||||
|
if (SizeF == 0)
|
||||||
|
{
|
||||||
|
Type[] Types = new Type[] { typeof(float) };
|
||||||
|
|
||||||
|
Context.EmitLdc_R4(2f);
|
||||||
|
Context.EmitCall(typeof(Sse).GetMethod(nameof(Sse.SetAllVector128), Types));
|
||||||
|
|
||||||
|
Types = new Type[] { typeof(Vector128<float>), typeof(Vector128<float>) };
|
||||||
|
|
||||||
|
Context.EmitLdvec(Op.Rn);
|
||||||
|
Context.EmitLdvec(Op.Rm);
|
||||||
|
|
||||||
|
Context.EmitCall(typeof(Sse).GetMethod(nameof(Sse.Multiply), Types));
|
||||||
|
Context.EmitCall(typeof(Sse).GetMethod(nameof(Sse.Subtract), Types));
|
||||||
|
|
||||||
|
Context.EmitStvec(Op.Rd);
|
||||||
|
|
||||||
|
if (Op.RegisterSize == ARegisterSize.SIMD64)
|
||||||
|
{
|
||||||
|
EmitVectorZeroUpper(Context, Op.Rd);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else /* if (SizeF == 1) */
|
||||||
|
{
|
||||||
|
Type[] Types = new Type[] { typeof(double) };
|
||||||
|
|
||||||
|
Context.EmitLdc_R8(2d);
|
||||||
|
Context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.SetAllVector128), Types));
|
||||||
|
|
||||||
|
Types = new Type[] { typeof(Vector128<double>), typeof(Vector128<double>) };
|
||||||
|
|
||||||
|
EmitLdvecWithCastToDouble(Context, Op.Rn);
|
||||||
|
EmitLdvecWithCastToDouble(Context, Op.Rm);
|
||||||
|
|
||||||
|
Context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Multiply), Types));
|
||||||
|
Context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Subtract), Types));
|
||||||
|
|
||||||
|
EmitStvecWithCastFromDouble(Context, Op.Rd);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
EmitVectorBinaryOpF(Context, () =>
|
||||||
|
{
|
||||||
|
EmitSoftFloatCall(Context, nameof(ASoftFloat_32.FPRecipStepFused));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Frecpx_S(AILEmitterCtx Context)
|
||||||
|
{
|
||||||
|
EmitScalarUnaryOpF(Context, () =>
|
||||||
|
{
|
||||||
|
EmitSoftFloatCall(Context, nameof(ASoftFloat_32.FPRecpX));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -728,97 +947,194 @@ namespace ChocolArm64.Instruction
|
||||||
|
|
||||||
public static void Frsqrts_S(AILEmitterCtx Context)
|
public static void Frsqrts_S(AILEmitterCtx Context)
|
||||||
{
|
{
|
||||||
EmitFrsqrts(Context, 0, Scalar: true);
|
if (AOptimizations.FastFP && AOptimizations.UseSse2)
|
||||||
|
{
|
||||||
|
AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp;
|
||||||
|
|
||||||
|
int SizeF = Op.Size & 1;
|
||||||
|
|
||||||
|
if (SizeF == 0)
|
||||||
|
{
|
||||||
|
Type[] Types = new Type[] { typeof(float) };
|
||||||
|
|
||||||
|
Context.EmitLdc_R4(0.5f);
|
||||||
|
Context.EmitCall(typeof(Sse).GetMethod(nameof(Sse.SetScalarVector128), Types));
|
||||||
|
|
||||||
|
Context.EmitLdc_R4(3f);
|
||||||
|
Context.EmitCall(typeof(Sse).GetMethod(nameof(Sse.SetScalarVector128), Types));
|
||||||
|
|
||||||
|
Types = new Type[] { typeof(Vector128<float>), typeof(Vector128<float>) };
|
||||||
|
|
||||||
|
Context.EmitLdvec(Op.Rn);
|
||||||
|
Context.EmitLdvec(Op.Rm);
|
||||||
|
|
||||||
|
Context.EmitCall(typeof(Sse).GetMethod(nameof(Sse.MultiplyScalar), Types));
|
||||||
|
Context.EmitCall(typeof(Sse).GetMethod(nameof(Sse.SubtractScalar), Types));
|
||||||
|
Context.EmitCall(typeof(Sse).GetMethod(nameof(Sse.MultiplyScalar), Types));
|
||||||
|
|
||||||
|
Context.EmitStvec(Op.Rd);
|
||||||
|
|
||||||
|
EmitVectorZero32_128(Context, Op.Rd);
|
||||||
|
}
|
||||||
|
else /* if (SizeF == 1) */
|
||||||
|
{
|
||||||
|
Type[] Types = new Type[] { typeof(double) };
|
||||||
|
|
||||||
|
Context.EmitLdc_R8(0.5d);
|
||||||
|
Context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.SetScalarVector128), Types));
|
||||||
|
|
||||||
|
Context.EmitLdc_R8(3d);
|
||||||
|
Context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.SetScalarVector128), Types));
|
||||||
|
|
||||||
|
Types = new Type[] { typeof(Vector128<double>), typeof(Vector128<double>) };
|
||||||
|
|
||||||
|
EmitLdvecWithCastToDouble(Context, Op.Rn);
|
||||||
|
EmitLdvecWithCastToDouble(Context, Op.Rm);
|
||||||
|
|
||||||
|
Context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.MultiplyScalar), Types));
|
||||||
|
Context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.SubtractScalar), Types));
|
||||||
|
Context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.MultiplyScalar), Types));
|
||||||
|
|
||||||
|
EmitStvecWithCastFromDouble(Context, Op.Rd);
|
||||||
|
|
||||||
|
EmitVectorZeroUpper(Context, Op.Rd);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
EmitScalarBinaryOpF(Context, () =>
|
||||||
|
{
|
||||||
|
EmitSoftFloatCall(Context, nameof(ASoftFloat_32.FPRSqrtStepFused));
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void Frsqrts_V(AILEmitterCtx Context)
|
public static void Frsqrts_V(AILEmitterCtx Context)
|
||||||
{
|
{
|
||||||
AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp;
|
if (AOptimizations.FastFP && AOptimizations.UseSse2)
|
||||||
|
|
||||||
int SizeF = Op.Size & 1;
|
|
||||||
|
|
||||||
int Bytes = Op.GetBitsCount() >> 3;
|
|
||||||
|
|
||||||
for (int Index = 0; Index < Bytes >> SizeF + 2; Index++)
|
|
||||||
{
|
{
|
||||||
EmitFrsqrts(Context, Index, Scalar: false);
|
AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp;
|
||||||
}
|
|
||||||
|
|
||||||
if (Op.RegisterSize == ARegisterSize.SIMD64)
|
int SizeF = Op.Size & 1;
|
||||||
|
|
||||||
|
if (SizeF == 0)
|
||||||
|
{
|
||||||
|
Type[] Types = new Type[] { typeof(float) };
|
||||||
|
|
||||||
|
Context.EmitLdc_R4(0.5f);
|
||||||
|
Context.EmitCall(typeof(Sse).GetMethod(nameof(Sse.SetAllVector128), Types));
|
||||||
|
|
||||||
|
Context.EmitLdc_R4(3f);
|
||||||
|
Context.EmitCall(typeof(Sse).GetMethod(nameof(Sse.SetAllVector128), Types));
|
||||||
|
|
||||||
|
Types = new Type[] { typeof(Vector128<float>), typeof(Vector128<float>) };
|
||||||
|
|
||||||
|
Context.EmitLdvec(Op.Rn);
|
||||||
|
Context.EmitLdvec(Op.Rm);
|
||||||
|
|
||||||
|
Context.EmitCall(typeof(Sse).GetMethod(nameof(Sse.Multiply), Types));
|
||||||
|
Context.EmitCall(typeof(Sse).GetMethod(nameof(Sse.Subtract), Types));
|
||||||
|
Context.EmitCall(typeof(Sse).GetMethod(nameof(Sse.Multiply), Types));
|
||||||
|
|
||||||
|
Context.EmitStvec(Op.Rd);
|
||||||
|
|
||||||
|
if (Op.RegisterSize == ARegisterSize.SIMD64)
|
||||||
|
{
|
||||||
|
EmitVectorZeroUpper(Context, Op.Rd);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else /* if (SizeF == 1) */
|
||||||
|
{
|
||||||
|
Type[] Types = new Type[] { typeof(double) };
|
||||||
|
|
||||||
|
Context.EmitLdc_R8(0.5d);
|
||||||
|
Context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.SetAllVector128), Types));
|
||||||
|
|
||||||
|
Context.EmitLdc_R8(3d);
|
||||||
|
Context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.SetAllVector128), Types));
|
||||||
|
|
||||||
|
Types = new Type[] { typeof(Vector128<double>), typeof(Vector128<double>) };
|
||||||
|
|
||||||
|
EmitLdvecWithCastToDouble(Context, Op.Rn);
|
||||||
|
EmitLdvecWithCastToDouble(Context, Op.Rm);
|
||||||
|
|
||||||
|
Context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Multiply), Types));
|
||||||
|
Context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Subtract), Types));
|
||||||
|
Context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Multiply), Types));
|
||||||
|
|
||||||
|
EmitStvecWithCastFromDouble(Context, Op.Rd);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
{
|
{
|
||||||
EmitVectorZeroUpper(Context, Op.Rd);
|
EmitVectorBinaryOpF(Context, () =>
|
||||||
|
{
|
||||||
|
EmitSoftFloatCall(Context, nameof(ASoftFloat_32.FPRSqrtStepFused));
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void EmitFrsqrts(AILEmitterCtx Context, int Index, bool Scalar)
|
|
||||||
{
|
|
||||||
AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp;
|
|
||||||
|
|
||||||
int SizeF = Op.Size & 1;
|
|
||||||
|
|
||||||
if (SizeF == 0)
|
|
||||||
{
|
|
||||||
Context.EmitLdc_R4(3);
|
|
||||||
}
|
|
||||||
else /* if (SizeF == 1) */
|
|
||||||
{
|
|
||||||
Context.EmitLdc_R8(3);
|
|
||||||
}
|
|
||||||
|
|
||||||
EmitVectorExtractF(Context, Op.Rn, Index, SizeF);
|
|
||||||
EmitVectorExtractF(Context, Op.Rm, Index, SizeF);
|
|
||||||
|
|
||||||
Context.Emit(OpCodes.Mul);
|
|
||||||
Context.Emit(OpCodes.Sub);
|
|
||||||
|
|
||||||
if (SizeF == 0)
|
|
||||||
{
|
|
||||||
Context.EmitLdc_R4(0.5f);
|
|
||||||
}
|
|
||||||
else /* if (SizeF == 1) */
|
|
||||||
{
|
|
||||||
Context.EmitLdc_R8(0.5);
|
|
||||||
}
|
|
||||||
|
|
||||||
Context.Emit(OpCodes.Mul);
|
|
||||||
|
|
||||||
if (Scalar)
|
|
||||||
{
|
|
||||||
EmitVectorZeroAll(Context, Op.Rd);
|
|
||||||
}
|
|
||||||
|
|
||||||
EmitVectorInsertF(Context, Op.Rd, Index, SizeF);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void Fsqrt_S(AILEmitterCtx Context)
|
public static void Fsqrt_S(AILEmitterCtx Context)
|
||||||
{
|
{
|
||||||
EmitScalarUnaryOpF(Context, () =>
|
if (AOptimizations.FastFP && AOptimizations.UseSse
|
||||||
|
&& AOptimizations.UseSse2)
|
||||||
{
|
{
|
||||||
EmitUnaryMathCall(Context, nameof(Math.Sqrt));
|
EmitScalarSseOrSse2OpF(Context, nameof(Sse.SqrtScalar));
|
||||||
});
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
EmitScalarUnaryOpF(Context, () =>
|
||||||
|
{
|
||||||
|
EmitSoftFloatCall(Context, nameof(ASoftFloat_32.FPSqrt));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Fsqrt_V(AILEmitterCtx Context)
|
||||||
|
{
|
||||||
|
if (AOptimizations.FastFP && AOptimizations.UseSse
|
||||||
|
&& AOptimizations.UseSse2)
|
||||||
|
{
|
||||||
|
EmitVectorSseOrSse2OpF(Context, nameof(Sse.Sqrt));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
EmitVectorUnaryOpF(Context, () =>
|
||||||
|
{
|
||||||
|
EmitSoftFloatCall(Context, nameof(ASoftFloat_32.FPSqrt));
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void Fsub_S(AILEmitterCtx Context)
|
public static void Fsub_S(AILEmitterCtx Context)
|
||||||
{
|
{
|
||||||
if (AOptimizations.UseSse && AOptimizations.UseSse2)
|
if (AOptimizations.FastFP && AOptimizations.UseSse
|
||||||
|
&& AOptimizations.UseSse2)
|
||||||
{
|
{
|
||||||
EmitScalarSseOrSse2OpF(Context, nameof(Sse.SubtractScalar));
|
EmitScalarSseOrSse2OpF(Context, nameof(Sse.SubtractScalar));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
EmitScalarBinaryOpF(Context, () => Context.Emit(OpCodes.Sub));
|
EmitScalarBinaryOpF(Context, () =>
|
||||||
|
{
|
||||||
|
EmitSoftFloatCall(Context, nameof(ASoftFloat_32.FPSub));
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void Fsub_V(AILEmitterCtx Context)
|
public static void Fsub_V(AILEmitterCtx Context)
|
||||||
{
|
{
|
||||||
if (AOptimizations.UseSse && AOptimizations.UseSse2)
|
if (AOptimizations.FastFP && AOptimizations.UseSse
|
||||||
|
&& AOptimizations.UseSse2)
|
||||||
{
|
{
|
||||||
EmitVectorSseOrSse2OpF(Context, nameof(Sse.Subtract));
|
EmitVectorSseOrSse2OpF(Context, nameof(Sse.Subtract));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
EmitVectorBinaryOpF(Context, () => Context.Emit(OpCodes.Sub));
|
EmitVectorBinaryOpF(Context, () =>
|
||||||
|
{
|
||||||
|
EmitSoftFloatCall(Context, nameof(ASoftFloat_32.FPSub));
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1170,7 +1486,6 @@ namespace ChocolArm64.Instruction
|
||||||
EmitVectorTernaryOpZx(Context, () =>
|
EmitVectorTernaryOpZx(Context, () =>
|
||||||
{
|
{
|
||||||
Context.Emit(OpCodes.Sub);
|
Context.Emit(OpCodes.Sub);
|
||||||
|
|
||||||
EmitAbs(Context);
|
EmitAbs(Context);
|
||||||
|
|
||||||
Context.Emit(OpCodes.Add);
|
Context.Emit(OpCodes.Add);
|
||||||
|
@ -1182,7 +1497,6 @@ namespace ChocolArm64.Instruction
|
||||||
EmitVectorWidenRnRmTernaryOpZx(Context, () =>
|
EmitVectorWidenRnRmTernaryOpZx(Context, () =>
|
||||||
{
|
{
|
||||||
Context.Emit(OpCodes.Sub);
|
Context.Emit(OpCodes.Sub);
|
||||||
|
|
||||||
EmitAbs(Context);
|
EmitAbs(Context);
|
||||||
|
|
||||||
Context.Emit(OpCodes.Add);
|
Context.Emit(OpCodes.Add);
|
||||||
|
@ -1194,7 +1508,6 @@ namespace ChocolArm64.Instruction
|
||||||
EmitVectorBinaryOpZx(Context, () =>
|
EmitVectorBinaryOpZx(Context, () =>
|
||||||
{
|
{
|
||||||
Context.Emit(OpCodes.Sub);
|
Context.Emit(OpCodes.Sub);
|
||||||
|
|
||||||
EmitAbs(Context);
|
EmitAbs(Context);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -1204,7 +1517,6 @@ namespace ChocolArm64.Instruction
|
||||||
EmitVectorWidenRnRmBinaryOpZx(Context, () =>
|
EmitVectorWidenRnRmBinaryOpZx(Context, () =>
|
||||||
{
|
{
|
||||||
Context.Emit(OpCodes.Sub);
|
Context.Emit(OpCodes.Sub);
|
||||||
|
|
||||||
EmitAbs(Context);
|
EmitAbs(Context);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -306,25 +306,19 @@ namespace ChocolArm64.Instruction
|
||||||
|
|
||||||
int SizeF = Op.Size & 1;
|
int SizeF = Op.Size & 1;
|
||||||
|
|
||||||
Context.EmitLdc_I4((int)RoundMode);
|
|
||||||
|
|
||||||
MethodInfo MthdInfo;
|
MethodInfo MthdInfo;
|
||||||
|
|
||||||
Type[] Types = new Type[] { null, typeof(MidpointRounding) };
|
|
||||||
|
|
||||||
Types[0] = SizeF == 0
|
|
||||||
? typeof(float)
|
|
||||||
: typeof(double);
|
|
||||||
|
|
||||||
if (SizeF == 0)
|
if (SizeF == 0)
|
||||||
{
|
{
|
||||||
MthdInfo = typeof(MathF).GetMethod(nameof(MathF.Round), Types);
|
MthdInfo = typeof(MathF).GetMethod(nameof(MathF.Round), new Type[] { typeof(float), typeof(MidpointRounding) });
|
||||||
}
|
}
|
||||||
else /* if (SizeF == 1) */
|
else /* if (SizeF == 1) */
|
||||||
{
|
{
|
||||||
MthdInfo = typeof(Math).GetMethod(nameof(Math.Round), Types);
|
MthdInfo = typeof(Math).GetMethod(nameof(Math.Round), new Type[] { typeof(double), typeof(MidpointRounding) });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Context.EmitLdc_I4((int)RoundMode);
|
||||||
|
|
||||||
Context.EmitCall(MthdInfo);
|
Context.EmitCall(MthdInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -348,24 +342,17 @@ namespace ChocolArm64.Instruction
|
||||||
Context.EmitCall(MthdInfo);
|
Context.EmitCall(MthdInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void EmitBinarySoftFloatCall(AILEmitterCtx Context, string Name)
|
public static void EmitSoftFloatCall(AILEmitterCtx Context, string Name)
|
||||||
{
|
{
|
||||||
IAOpCodeSimd Op = (IAOpCodeSimd)Context.CurrOp;
|
IAOpCodeSimd Op = (IAOpCodeSimd)Context.CurrOp;
|
||||||
|
|
||||||
int SizeF = Op.Size & 1;
|
Type Type = (Op.Size & 1) == 0
|
||||||
|
? typeof(ASoftFloat_32)
|
||||||
|
: typeof(ASoftFloat_64);
|
||||||
|
|
||||||
MethodInfo MthdInfo;
|
Context.EmitLdarg(ATranslatedSub.StateArgIdx);
|
||||||
|
|
||||||
if (SizeF == 0)
|
Context.EmitCall(Type, Name);
|
||||||
{
|
|
||||||
MthdInfo = typeof(ASoftFloat).GetMethod(Name, new Type[] { typeof(float), typeof(float) });
|
|
||||||
}
|
|
||||||
else /* if (SizeF == 1) */
|
|
||||||
{
|
|
||||||
MthdInfo = typeof(ASoftFloat).GetMethod(Name, new Type[] { typeof(double), typeof(double) });
|
|
||||||
}
|
|
||||||
|
|
||||||
Context.EmitCall(MthdInfo);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void EmitScalarBinaryOpByElemF(AILEmitterCtx Context, Action Emit)
|
public static void EmitScalarBinaryOpByElemF(AILEmitterCtx Context, Action Emit)
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -178,8 +178,15 @@ namespace Ryujinx.Tests.Cpu
|
||||||
return GetThreadState();
|
return GetThreadState();
|
||||||
}
|
}
|
||||||
|
|
||||||
[Flags]
|
/// <summary>Floating-point Control Register.</summary>
|
||||||
protected enum FPSR
|
protected enum FPCR
|
||||||
|
{
|
||||||
|
/// <summary>Default NaN mode control bit.</summary>
|
||||||
|
DN = 25
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Floating-point Status Register.</summary>
|
||||||
|
[Flags] protected enum FPSR
|
||||||
{
|
{
|
||||||
None = 0,
|
None = 0,
|
||||||
|
|
||||||
|
@ -195,32 +202,43 @@ namespace Ryujinx.Tests.Cpu
|
||||||
IXC = 1 << 4,
|
IXC = 1 << 4,
|
||||||
/// <summary>Input Denormal cumulative floating-point exception bit.</summary>
|
/// <summary>Input Denormal cumulative floating-point exception bit.</summary>
|
||||||
IDC = 1 << 7,
|
IDC = 1 << 7,
|
||||||
|
|
||||||
/// <summary>Cumulative saturation bit.</summary>
|
/// <summary>Cumulative saturation bit.</summary>
|
||||||
QC = 1 << 27
|
QC = 1 << 27
|
||||||
}
|
}
|
||||||
|
|
||||||
protected enum FpSkips { None, IfNaN_S, IfNaN_D };
|
[Flags] protected enum FpSkips
|
||||||
|
{
|
||||||
|
None = 0,
|
||||||
|
|
||||||
protected enum FpUseTolerance { None, OneUlps_S, OneUlps_D };
|
IfNaN_S = 1,
|
||||||
|
IfNaN_D = 2,
|
||||||
|
|
||||||
|
IfUnderflow = 4,
|
||||||
|
IfOverflow = 8
|
||||||
|
}
|
||||||
|
|
||||||
|
protected enum FpTolerances
|
||||||
|
{
|
||||||
|
None,
|
||||||
|
|
||||||
|
UpToOneUlps_S,
|
||||||
|
UpToOneUlps_D
|
||||||
|
}
|
||||||
|
|
||||||
protected void CompareAgainstUnicorn(
|
protected void CompareAgainstUnicorn(
|
||||||
FPSR FpsrMask = FPSR.None,
|
FPSR FpsrMask = FPSR.None,
|
||||||
FpSkips FpSkips = FpSkips.None,
|
FpSkips FpSkips = FpSkips.None,
|
||||||
FpUseTolerance FpUseTolerance = FpUseTolerance.None)
|
FpTolerances FpTolerances = FpTolerances.None)
|
||||||
{
|
{
|
||||||
if (!UnicornAvailable)
|
if (!UnicornAvailable)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (FpSkips == FpSkips.IfNaN_S && float.IsNaN(VectorExtractSingle(UnicornEmu.Q[0], (byte)0)))
|
if (FpSkips != FpSkips.None)
|
||||||
{
|
{
|
||||||
Assert.Ignore("NaN test.");
|
ManageFpSkips(FpSkips);
|
||||||
}
|
|
||||||
|
|
||||||
if (FpSkips == FpSkips.IfNaN_D && double.IsNaN(VectorExtractDouble(UnicornEmu.Q[0], (byte)0)))
|
|
||||||
{
|
|
||||||
Assert.Ignore("NaN test.");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Assert.That(Thread.ThreadState.X0, Is.EqualTo(UnicornEmu.X[0]));
|
Assert.That(Thread.ThreadState.X0, Is.EqualTo(UnicornEmu.X[0]));
|
||||||
|
@ -257,50 +275,13 @@ namespace Ryujinx.Tests.Cpu
|
||||||
|
|
||||||
Assert.That(Thread.ThreadState.X31, Is.EqualTo(UnicornEmu.SP));
|
Assert.That(Thread.ThreadState.X31, Is.EqualTo(UnicornEmu.SP));
|
||||||
|
|
||||||
if (FpUseTolerance == FpUseTolerance.None)
|
if (FpTolerances == FpTolerances.None)
|
||||||
{
|
{
|
||||||
Assert.That(Thread.ThreadState.V0, Is.EqualTo(UnicornEmu.Q[0]));
|
Assert.That(Thread.ThreadState.V0, Is.EqualTo(UnicornEmu.Q[0]));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (!Is.EqualTo(UnicornEmu.Q[0]).ApplyTo(Thread.ThreadState.V0).IsSuccess)
|
ManageFpTolerances(FpTolerances);
|
||||||
{
|
|
||||||
if (FpUseTolerance == FpUseTolerance.OneUlps_S)
|
|
||||||
{
|
|
||||||
if (float.IsNormal (VectorExtractSingle(UnicornEmu.Q[0], (byte)0)) ||
|
|
||||||
float.IsSubnormal(VectorExtractSingle(UnicornEmu.Q[0], (byte)0)))
|
|
||||||
{
|
|
||||||
Assert.That (VectorExtractSingle(Thread.ThreadState.V0, (byte)0),
|
|
||||||
Is.EqualTo(VectorExtractSingle(UnicornEmu.Q[0], (byte)0)).Within(1).Ulps);
|
|
||||||
Assert.That (VectorExtractSingle(Thread.ThreadState.V0, (byte)1),
|
|
||||||
Is.EqualTo(VectorExtractSingle(UnicornEmu.Q[0], (byte)1)).Within(1).Ulps);
|
|
||||||
Assert.That (VectorExtractSingle(Thread.ThreadState.V0, (byte)2),
|
|
||||||
Is.EqualTo(VectorExtractSingle(UnicornEmu.Q[0], (byte)2)).Within(1).Ulps);
|
|
||||||
Assert.That (VectorExtractSingle(Thread.ThreadState.V0, (byte)3),
|
|
||||||
Is.EqualTo(VectorExtractSingle(UnicornEmu.Q[0], (byte)3)).Within(1).Ulps);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Assert.That(Thread.ThreadState.V0, Is.EqualTo(UnicornEmu.Q[0]));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (FpUseTolerance == FpUseTolerance.OneUlps_D)
|
|
||||||
{
|
|
||||||
if (double.IsNormal (VectorExtractDouble(UnicornEmu.Q[0], (byte)0)) ||
|
|
||||||
double.IsSubnormal(VectorExtractDouble(UnicornEmu.Q[0], (byte)0)))
|
|
||||||
{
|
|
||||||
Assert.That (VectorExtractDouble(Thread.ThreadState.V0, (byte)0),
|
|
||||||
Is.EqualTo(VectorExtractDouble(UnicornEmu.Q[0], (byte)0)).Within(1).Ulps);
|
|
||||||
Assert.That (VectorExtractDouble(Thread.ThreadState.V0, (byte)1),
|
|
||||||
Is.EqualTo(VectorExtractDouble(UnicornEmu.Q[0], (byte)1)).Within(1).Ulps);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Assert.That(Thread.ThreadState.V0, Is.EqualTo(UnicornEmu.Q[0]));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Assert.That(Thread.ThreadState.V1, Is.EqualTo(UnicornEmu.Q[1]));
|
Assert.That(Thread.ThreadState.V1, Is.EqualTo(UnicornEmu.Q[1]));
|
||||||
Assert.That(Thread.ThreadState.V2, Is.EqualTo(UnicornEmu.Q[2]));
|
Assert.That(Thread.ThreadState.V2, Is.EqualTo(UnicornEmu.Q[2]));
|
||||||
|
@ -344,6 +325,90 @@ namespace Ryujinx.Tests.Cpu
|
||||||
Assert.That(Thread.ThreadState.Negative, Is.EqualTo(UnicornEmu.NegativeFlag));
|
Assert.That(Thread.ThreadState.Negative, Is.EqualTo(UnicornEmu.NegativeFlag));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void ManageFpSkips(FpSkips FpSkips)
|
||||||
|
{
|
||||||
|
if (FpSkips.HasFlag(FpSkips.IfNaN_S))
|
||||||
|
{
|
||||||
|
if (float.IsNaN(VectorExtractSingle(UnicornEmu.Q[0], (byte)0)))
|
||||||
|
{
|
||||||
|
Assert.Ignore("NaN test.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (FpSkips.HasFlag(FpSkips.IfNaN_D))
|
||||||
|
{
|
||||||
|
if (double.IsNaN(VectorExtractDouble(UnicornEmu.Q[0], (byte)0)))
|
||||||
|
{
|
||||||
|
Assert.Ignore("NaN test.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (FpSkips.HasFlag(FpSkips.IfUnderflow))
|
||||||
|
{
|
||||||
|
if ((UnicornEmu.Fpsr & (int)FPSR.UFC) != 0)
|
||||||
|
{
|
||||||
|
Assert.Ignore("Underflow test.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (FpSkips.HasFlag(FpSkips.IfOverflow))
|
||||||
|
{
|
||||||
|
if ((UnicornEmu.Fpsr & (int)FPSR.OFC) != 0)
|
||||||
|
{
|
||||||
|
Assert.Ignore("Overflow test.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ManageFpTolerances(FpTolerances FpTolerances)
|
||||||
|
{
|
||||||
|
if (!Is.EqualTo(UnicornEmu.Q[0]).ApplyTo(Thread.ThreadState.V0).IsSuccess)
|
||||||
|
{
|
||||||
|
if (FpTolerances == FpTolerances.UpToOneUlps_S)
|
||||||
|
{
|
||||||
|
if (IsNormalOrSubnormal_S(VectorExtractSingle(UnicornEmu.Q[0], (byte)0)) &&
|
||||||
|
IsNormalOrSubnormal_S(VectorExtractSingle(Thread.ThreadState.V0, (byte)0)))
|
||||||
|
{
|
||||||
|
Assert.That (VectorExtractSingle(Thread.ThreadState.V0, (byte)0),
|
||||||
|
Is.EqualTo(VectorExtractSingle(UnicornEmu.Q[0], (byte)0)).Within(1).Ulps);
|
||||||
|
Assert.That (VectorExtractSingle(Thread.ThreadState.V0, (byte)1),
|
||||||
|
Is.EqualTo(VectorExtractSingle(UnicornEmu.Q[0], (byte)1)).Within(1).Ulps);
|
||||||
|
Assert.That (VectorExtractSingle(Thread.ThreadState.V0, (byte)2),
|
||||||
|
Is.EqualTo(VectorExtractSingle(UnicornEmu.Q[0], (byte)2)).Within(1).Ulps);
|
||||||
|
Assert.That (VectorExtractSingle(Thread.ThreadState.V0, (byte)3),
|
||||||
|
Is.EqualTo(VectorExtractSingle(UnicornEmu.Q[0], (byte)3)).Within(1).Ulps);
|
||||||
|
|
||||||
|
Console.WriteLine(FpTolerances);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Assert.That(Thread.ThreadState.V0, Is.EqualTo(UnicornEmu.Q[0]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (FpTolerances == FpTolerances.UpToOneUlps_D)
|
||||||
|
{
|
||||||
|
if (IsNormalOrSubnormal_D(VectorExtractDouble(UnicornEmu.Q[0], (byte)0)) &&
|
||||||
|
IsNormalOrSubnormal_D(VectorExtractDouble(Thread.ThreadState.V0, (byte)0)))
|
||||||
|
{
|
||||||
|
Assert.That (VectorExtractDouble(Thread.ThreadState.V0, (byte)0),
|
||||||
|
Is.EqualTo(VectorExtractDouble(UnicornEmu.Q[0], (byte)0)).Within(1).Ulps);
|
||||||
|
Assert.That (VectorExtractDouble(Thread.ThreadState.V0, (byte)1),
|
||||||
|
Is.EqualTo(VectorExtractDouble(UnicornEmu.Q[0], (byte)1)).Within(1).Ulps);
|
||||||
|
|
||||||
|
Console.WriteLine(FpTolerances);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Assert.That(Thread.ThreadState.V0, Is.EqualTo(UnicornEmu.Q[0]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsNormalOrSubnormal_S(float f) => float.IsNormal(f) || float.IsSubnormal(f);
|
||||||
|
|
||||||
|
bool IsNormalOrSubnormal_D(double d) => double.IsNormal(d) || double.IsSubnormal(d);
|
||||||
|
}
|
||||||
|
|
||||||
protected static Vector128<float> MakeVectorE0(double E0)
|
protected static Vector128<float> MakeVectorE0(double E0)
|
||||||
{
|
{
|
||||||
if (!Sse2.IsSupported)
|
if (!Sse2.IsSupported)
|
||||||
|
@ -453,14 +518,14 @@ namespace Ryujinx.Tests.Cpu
|
||||||
{
|
{
|
||||||
uint Rnd;
|
uint Rnd;
|
||||||
|
|
||||||
do Rnd = TestContext.CurrentContext.Random.NextUInt();
|
do Rnd = TestContext.CurrentContext.Random.NextUInt();
|
||||||
while ((Rnd & 0x7F800000u) == 0u ||
|
while (( Rnd & 0x7F800000u) == 0u ||
|
||||||
(Rnd & 0x7F800000u) == 0x7F800000u);
|
(~Rnd & 0x7F800000u) == 0u);
|
||||||
|
|
||||||
return Rnd;
|
return Rnd;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected static uint GenSubNormal_S()
|
protected static uint GenSubnormal_S()
|
||||||
{
|
{
|
||||||
uint Rnd;
|
uint Rnd;
|
||||||
|
|
||||||
|
@ -474,14 +539,14 @@ namespace Ryujinx.Tests.Cpu
|
||||||
{
|
{
|
||||||
ulong Rnd;
|
ulong Rnd;
|
||||||
|
|
||||||
do Rnd = TestContext.CurrentContext.Random.NextULong();
|
do Rnd = TestContext.CurrentContext.Random.NextULong();
|
||||||
while ((Rnd & 0x7FF0000000000000ul) == 0ul ||
|
while (( Rnd & 0x7FF0000000000000ul) == 0ul ||
|
||||||
(Rnd & 0x7FF0000000000000ul) == 0x7FF0000000000000ul);
|
(~Rnd & 0x7FF0000000000000ul) == 0ul);
|
||||||
|
|
||||||
return Rnd;
|
return Rnd;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected static ulong GenSubNormal_D()
|
protected static ulong GenSubnormal_D()
|
||||||
{
|
{
|
||||||
ulong Rnd;
|
ulong Rnd;
|
||||||
|
|
||||||
|
|
|
@ -81,14 +81,14 @@ namespace Ryujinx.Tests.Cpu
|
||||||
|
|
||||||
private static IEnumerable<ulong> _1S_F_()
|
private static IEnumerable<ulong> _1S_F_()
|
||||||
{
|
{
|
||||||
yield return 0x00000000FF7FFFFFul; // -Max Normal (float.MinValue)
|
yield return 0x00000000FF7FFFFFul; // -Max Normal (float.MinValue)
|
||||||
yield return 0x0000000080800000ul; // -Min Normal
|
yield return 0x0000000080800000ul; // -Min Normal
|
||||||
yield return 0x00000000807FFFFFul; // -Max SubNormal
|
yield return 0x00000000807FFFFFul; // -Max Subnormal
|
||||||
yield return 0x0000000080000001ul; // -Min SubNormal
|
yield return 0x0000000080000001ul; // -Min Subnormal (-float.Epsilon)
|
||||||
yield return 0x000000007F7FFFFFul; // +Max Normal (float.MaxValue)
|
yield return 0x000000007F7FFFFFul; // +Max Normal (float.MaxValue)
|
||||||
yield return 0x0000000000800000ul; // +Min Normal
|
yield return 0x0000000000800000ul; // +Min Normal
|
||||||
yield return 0x00000000007FFFFFul; // +Max SubNormal
|
yield return 0x00000000007FFFFFul; // +Max Subnormal
|
||||||
yield return 0x0000000000000001ul; // +Min SubNormal
|
yield return 0x0000000000000001ul; // +Min Subnormal (float.Epsilon)
|
||||||
|
|
||||||
if (!NoZeros)
|
if (!NoZeros)
|
||||||
{
|
{
|
||||||
|
@ -104,17 +104,17 @@ namespace Ryujinx.Tests.Cpu
|
||||||
|
|
||||||
if (!NoNaNs)
|
if (!NoNaNs)
|
||||||
{
|
{
|
||||||
yield return 0x00000000FFFFFFFFul; // -QNaN (all ones payload)
|
yield return 0x00000000FFC00000ul; // -QNaN (all zeros payload) (float.NaN)
|
||||||
yield return 0x00000000FFBFFFFFul; // -SNaN (all ones payload)
|
yield return 0x00000000FFBFFFFFul; // -SNaN (all ones payload)
|
||||||
yield return 0x000000007FFFFFFFul; // +QNaN (all ones payload)
|
yield return 0x000000007FC00000ul; // +QNaN (all zeros payload) (-float.NaN) (DefaultNaN)
|
||||||
yield return 0x000000007FBFFFFFul; // +SNaN (all ones payload)
|
yield return 0x000000007FBFFFFFul; // +SNaN (all ones payload)
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int Cnt = 1; Cnt <= RndCnt; Cnt++)
|
for (int Cnt = 1; Cnt <= RndCnt; Cnt++)
|
||||||
{
|
{
|
||||||
ulong Grbg = TestContext.CurrentContext.Random.NextUInt();
|
ulong Grbg = TestContext.CurrentContext.Random.NextUInt();
|
||||||
ulong Rnd1 = GenNormal_S();
|
ulong Rnd1 = GenNormal_S();
|
||||||
ulong Rnd2 = GenSubNormal_S();
|
ulong Rnd2 = GenSubnormal_S();
|
||||||
|
|
||||||
yield return (Grbg << 32) | Rnd1;
|
yield return (Grbg << 32) | Rnd1;
|
||||||
yield return (Grbg << 32) | Rnd2;
|
yield return (Grbg << 32) | Rnd2;
|
||||||
|
@ -123,14 +123,14 @@ namespace Ryujinx.Tests.Cpu
|
||||||
|
|
||||||
private static IEnumerable<ulong> _2S_F_()
|
private static IEnumerable<ulong> _2S_F_()
|
||||||
{
|
{
|
||||||
yield return 0xFF7FFFFFFF7FFFFFul; // -Max Normal (float.MinValue)
|
yield return 0xFF7FFFFFFF7FFFFFul; // -Max Normal (float.MinValue)
|
||||||
yield return 0x8080000080800000ul; // -Min Normal
|
yield return 0x8080000080800000ul; // -Min Normal
|
||||||
yield return 0x807FFFFF807FFFFFul; // -Max SubNormal
|
yield return 0x807FFFFF807FFFFFul; // -Max Subnormal
|
||||||
yield return 0x8000000180000001ul; // -Min SubNormal
|
yield return 0x8000000180000001ul; // -Min Subnormal (-float.Epsilon)
|
||||||
yield return 0x7F7FFFFF7F7FFFFFul; // +Max Normal (float.MaxValue)
|
yield return 0x7F7FFFFF7F7FFFFFul; // +Max Normal (float.MaxValue)
|
||||||
yield return 0x0080000000800000ul; // +Min Normal
|
yield return 0x0080000000800000ul; // +Min Normal
|
||||||
yield return 0x007FFFFF007FFFFFul; // +Max SubNormal
|
yield return 0x007FFFFF007FFFFFul; // +Max Subnormal
|
||||||
yield return 0x0000000100000001ul; // +Min SubNormal
|
yield return 0x0000000100000001ul; // +Min Subnormal (float.Epsilon)
|
||||||
|
|
||||||
if (!NoZeros)
|
if (!NoZeros)
|
||||||
{
|
{
|
||||||
|
@ -146,16 +146,16 @@ namespace Ryujinx.Tests.Cpu
|
||||||
|
|
||||||
if (!NoNaNs)
|
if (!NoNaNs)
|
||||||
{
|
{
|
||||||
yield return 0xFFFFFFFFFFFFFFFFul; // -QNaN (all ones payload)
|
yield return 0xFFC00000FFC00000ul; // -QNaN (all zeros payload) (float.NaN)
|
||||||
yield return 0xFFBFFFFFFFBFFFFFul; // -SNaN (all ones payload)
|
yield return 0xFFBFFFFFFFBFFFFFul; // -SNaN (all ones payload)
|
||||||
yield return 0x7FFFFFFF7FFFFFFFul; // +QNaN (all ones payload)
|
yield return 0x7FC000007FC00000ul; // +QNaN (all zeros payload) (-float.NaN) (DefaultNaN)
|
||||||
yield return 0x7FBFFFFF7FBFFFFFul; // +SNaN (all ones payload)
|
yield return 0x7FBFFFFF7FBFFFFFul; // +SNaN (all ones payload)
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int Cnt = 1; Cnt <= RndCnt; Cnt++)
|
for (int Cnt = 1; Cnt <= RndCnt; Cnt++)
|
||||||
{
|
{
|
||||||
ulong Rnd1 = GenNormal_S();
|
ulong Rnd1 = GenNormal_S();
|
||||||
ulong Rnd2 = GenSubNormal_S();
|
ulong Rnd2 = GenSubnormal_S();
|
||||||
|
|
||||||
yield return (Rnd1 << 32) | Rnd1;
|
yield return (Rnd1 << 32) | Rnd1;
|
||||||
yield return (Rnd2 << 32) | Rnd2;
|
yield return (Rnd2 << 32) | Rnd2;
|
||||||
|
@ -164,14 +164,14 @@ namespace Ryujinx.Tests.Cpu
|
||||||
|
|
||||||
private static IEnumerable<ulong> _1D_F_()
|
private static IEnumerable<ulong> _1D_F_()
|
||||||
{
|
{
|
||||||
yield return 0xFFEFFFFFFFFFFFFFul; // -Max Normal (double.MinValue)
|
yield return 0xFFEFFFFFFFFFFFFFul; // -Max Normal (double.MinValue)
|
||||||
yield return 0x8010000000000000ul; // -Min Normal
|
yield return 0x8010000000000000ul; // -Min Normal
|
||||||
yield return 0x800FFFFFFFFFFFFFul; // -Max SubNormal
|
yield return 0x800FFFFFFFFFFFFFul; // -Max Subnormal
|
||||||
yield return 0x8000000000000001ul; // -Min SubNormal
|
yield return 0x8000000000000001ul; // -Min Subnormal (-double.Epsilon)
|
||||||
yield return 0x7FEFFFFFFFFFFFFFul; // +Max Normal (double.MaxValue)
|
yield return 0x7FEFFFFFFFFFFFFFul; // +Max Normal (double.MaxValue)
|
||||||
yield return 0x0010000000000000ul; // +Min Normal
|
yield return 0x0010000000000000ul; // +Min Normal
|
||||||
yield return 0x000FFFFFFFFFFFFFul; // +Max SubNormal
|
yield return 0x000FFFFFFFFFFFFFul; // +Max Subnormal
|
||||||
yield return 0x0000000000000001ul; // +Min SubNormal
|
yield return 0x0000000000000001ul; // +Min Subnormal (double.Epsilon)
|
||||||
|
|
||||||
if (!NoZeros)
|
if (!NoZeros)
|
||||||
{
|
{
|
||||||
|
@ -187,16 +187,16 @@ namespace Ryujinx.Tests.Cpu
|
||||||
|
|
||||||
if (!NoNaNs)
|
if (!NoNaNs)
|
||||||
{
|
{
|
||||||
yield return 0xFFFFFFFFFFFFFFFFul; // -QNaN (all ones payload)
|
yield return 0xFFF8000000000000ul; // -QNaN (all zeros payload) (double.NaN)
|
||||||
yield return 0xFFF7FFFFFFFFFFFFul; // -SNaN (all ones payload)
|
yield return 0xFFF7FFFFFFFFFFFFul; // -SNaN (all ones payload)
|
||||||
yield return 0x7FFFFFFFFFFFFFFFul; // +QNaN (all ones payload)
|
yield return 0x7FF8000000000000ul; // +QNaN (all zeros payload) (-double.NaN) (DefaultNaN)
|
||||||
yield return 0x7FF7FFFFFFFFFFFFul; // +SNaN (all ones payload)
|
yield return 0x7FF7FFFFFFFFFFFFul; // +SNaN (all ones payload)
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int Cnt = 1; Cnt <= RndCnt; Cnt++)
|
for (int Cnt = 1; Cnt <= RndCnt; Cnt++)
|
||||||
{
|
{
|
||||||
ulong Rnd1 = GenNormal_D();
|
ulong Rnd1 = GenNormal_D();
|
||||||
ulong Rnd2 = GenSubNormal_D();
|
ulong Rnd2 = GenSubnormal_D();
|
||||||
|
|
||||||
yield return Rnd1;
|
yield return Rnd1;
|
||||||
yield return Rnd2;
|
yield return Rnd2;
|
||||||
|
@ -248,6 +248,40 @@ namespace Ryujinx.Tests.Cpu
|
||||||
0x6EE1B800u // FCVTZU V0.2D, V0.2D
|
0x6EE1B800u // FCVTZU V0.2D, V0.2D
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static uint[] _F_RecpX_Sqrt_S_S_()
|
||||||
|
{
|
||||||
|
return new uint[]
|
||||||
|
{
|
||||||
|
0x5EA1F820u, // FRECPX S0, S1
|
||||||
|
0x1E21C020u // FSQRT S0, S1
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private static uint[] _F_RecpX_Sqrt_S_D_()
|
||||||
|
{
|
||||||
|
return new uint[]
|
||||||
|
{
|
||||||
|
0x5EE1F820u, // FRECPX D0, D1
|
||||||
|
0x1E61C020u // FSQRT D0, D1
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private static uint[] _F_Sqrt_V_2S_4S_()
|
||||||
|
{
|
||||||
|
return new uint[]
|
||||||
|
{
|
||||||
|
0x2EA1F800u // FSQRT V0.2S, V0.2S
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private static uint[] _F_Sqrt_V_2D_()
|
||||||
|
{
|
||||||
|
return new uint[]
|
||||||
|
{
|
||||||
|
0x6EE1F800u // FSQRT V0.2D, V0.2D
|
||||||
|
};
|
||||||
|
}
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
private const int RndCnt = 2;
|
private const int RndCnt = 2;
|
||||||
|
@ -754,21 +788,15 @@ namespace Ryujinx.Tests.Cpu
|
||||||
[Test, Pairwise, Description("FCVT <Dd>, <Sn>")]
|
[Test, Pairwise, Description("FCVT <Dd>, <Sn>")]
|
||||||
public void Fcvt_S_SD([ValueSource("_1S_F_")] ulong A)
|
public void Fcvt_S_SD([ValueSource("_1S_F_")] ulong A)
|
||||||
{
|
{
|
||||||
//const int DNFlagBit = 25; // Default NaN mode control bit.
|
|
||||||
//const int FZFlagBit = 24; // Flush-to-zero mode control bit.
|
|
||||||
|
|
||||||
uint Opcode = 0x1E22C020; // FCVT D0, S1
|
uint Opcode = 0x1E22C020; // FCVT D0, S1
|
||||||
|
|
||||||
ulong Z = TestContext.CurrentContext.Random.NextULong();
|
ulong Z = TestContext.CurrentContext.Random.NextULong();
|
||||||
Vector128<float> V0 = MakeVectorE1(Z);
|
Vector128<float> V0 = MakeVectorE1(Z);
|
||||||
Vector128<float> V1 = MakeVectorE0(A);
|
Vector128<float> V1 = MakeVectorE0(A);
|
||||||
|
|
||||||
//int Fpcr = 1 << DNFlagBit; // Any operation involving one or more NaNs returns the Default NaN.
|
AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1);
|
||||||
//Fpcr |= 1 << FZFlagBit; // Flush-to-zero mode enabled.
|
|
||||||
|
|
||||||
AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1/*, Fpcr: Fpcr*/);
|
CompareAgainstUnicorn();
|
||||||
|
|
||||||
CompareAgainstUnicorn(/*FpsrMask: FPSR.IDC | FPSR.IOC*/);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test, Pairwise, Description("FCVT <Sd>, <Dn>")]
|
[Test, Pairwise, Description("FCVT <Sd>, <Dn>")]
|
||||||
|
@ -789,17 +817,13 @@ namespace Ryujinx.Tests.Cpu
|
||||||
public void F_Cvt_NZ_SU_S_S([ValueSource("_F_Cvt_NZ_SU_S_S_")] uint Opcodes,
|
public void F_Cvt_NZ_SU_S_S([ValueSource("_F_Cvt_NZ_SU_S_S_")] uint Opcodes,
|
||||||
[ValueSource("_1S_F_")] ulong A)
|
[ValueSource("_1S_F_")] ulong A)
|
||||||
{
|
{
|
||||||
//const int FZFlagBit = 24; // Flush-to-zero mode control bit.
|
|
||||||
|
|
||||||
ulong Z = TestContext.CurrentContext.Random.NextULong();
|
ulong Z = TestContext.CurrentContext.Random.NextULong();
|
||||||
Vector128<float> V0 = MakeVectorE0E1(Z, Z);
|
Vector128<float> V0 = MakeVectorE0E1(Z, Z);
|
||||||
Vector128<float> V1 = MakeVectorE0(A);
|
Vector128<float> V1 = MakeVectorE0(A);
|
||||||
|
|
||||||
//int Fpcr = 1 << FZFlagBit; // Flush-to-zero mode enabled.
|
AThreadState ThreadState = SingleOpcode(Opcodes, V0: V0, V1: V1);
|
||||||
|
|
||||||
AThreadState ThreadState = SingleOpcode(Opcodes, V0: V0, V1: V1/*, Fpcr: Fpcr*/);
|
CompareAgainstUnicorn();
|
||||||
|
|
||||||
CompareAgainstUnicorn(/*FpsrMask: FPSR.IDC | FPSR.IXC | FPSR.IOC*/);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test, Pairwise]
|
[Test, Pairwise]
|
||||||
|
@ -851,6 +875,76 @@ namespace Ryujinx.Tests.Cpu
|
||||||
CompareAgainstUnicorn();
|
CompareAgainstUnicorn();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Test, Pairwise]
|
||||||
|
public void F_RecpX_Sqrt_S_S([ValueSource("_F_RecpX_Sqrt_S_S_")] uint Opcodes,
|
||||||
|
[ValueSource("_1S_F_")] ulong A)
|
||||||
|
{
|
||||||
|
ulong Z = TestContext.CurrentContext.Random.NextULong();
|
||||||
|
Vector128<float> V0 = MakeVectorE0E1(Z, Z);
|
||||||
|
Vector128<float> V1 = MakeVectorE0(A);
|
||||||
|
|
||||||
|
int Fpcr = (int)TestContext.CurrentContext.Random.NextUInt() & (1 << (int)FPCR.DN);
|
||||||
|
|
||||||
|
AThreadState ThreadState = SingleOpcode(Opcodes, V0: V0, V1: V1, Fpcr: Fpcr);
|
||||||
|
|
||||||
|
CompareAgainstUnicorn(FpsrMask: FPSR.IOC);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test, Pairwise]
|
||||||
|
public void F_RecpX_Sqrt_S_D([ValueSource("_F_RecpX_Sqrt_S_D_")] uint Opcodes,
|
||||||
|
[ValueSource("_1D_F_")] ulong A)
|
||||||
|
{
|
||||||
|
ulong Z = TestContext.CurrentContext.Random.NextULong();
|
||||||
|
Vector128<float> V0 = MakeVectorE1(Z);
|
||||||
|
Vector128<float> V1 = MakeVectorE0(A);
|
||||||
|
|
||||||
|
int Fpcr = (int)TestContext.CurrentContext.Random.NextUInt() & (1 << (int)FPCR.DN);
|
||||||
|
|
||||||
|
AThreadState ThreadState = SingleOpcode(Opcodes, V0: V0, V1: V1, Fpcr: Fpcr);
|
||||||
|
|
||||||
|
CompareAgainstUnicorn(FpsrMask: FPSR.IOC);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test, Pairwise]
|
||||||
|
public void F_Sqrt_V_2S_4S([ValueSource("_F_Sqrt_V_2S_4S_")] uint Opcodes,
|
||||||
|
[Values(0u)] uint Rd,
|
||||||
|
[Values(1u, 0u)] uint Rn,
|
||||||
|
[ValueSource("_2S_F_")] ulong Z,
|
||||||
|
[ValueSource("_2S_F_")] ulong A,
|
||||||
|
[Values(0b0u, 0b1u)] uint Q) // <2S, 4S>
|
||||||
|
{
|
||||||
|
Opcodes |= ((Rn & 31) << 5) | ((Rd & 31) << 0);
|
||||||
|
Opcodes |= ((Q & 1) << 30);
|
||||||
|
|
||||||
|
Vector128<float> V0 = MakeVectorE0E1(Z, Z);
|
||||||
|
Vector128<float> V1 = MakeVectorE0E1(A, A * Q);
|
||||||
|
|
||||||
|
int Fpcr = (int)TestContext.CurrentContext.Random.NextUInt() & (1 << (int)FPCR.DN);
|
||||||
|
|
||||||
|
AThreadState ThreadState = SingleOpcode(Opcodes, V0: V0, V1: V1, Fpcr: Fpcr);
|
||||||
|
|
||||||
|
CompareAgainstUnicorn(FpsrMask: FPSR.IOC);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test, Pairwise]
|
||||||
|
public void F_Sqrt_V_2D([ValueSource("_F_Sqrt_V_2D_")] uint Opcodes,
|
||||||
|
[Values(0u)] uint Rd,
|
||||||
|
[Values(1u, 0u)] uint Rn,
|
||||||
|
[ValueSource("_1D_F_")] ulong Z,
|
||||||
|
[ValueSource("_1D_F_")] ulong A)
|
||||||
|
{
|
||||||
|
Opcodes |= ((Rn & 31) << 5) | ((Rd & 31) << 0);
|
||||||
|
|
||||||
|
Vector128<float> V0 = MakeVectorE0E1(Z, Z);
|
||||||
|
Vector128<float> V1 = MakeVectorE0E1(A, A);
|
||||||
|
|
||||||
|
int Fpcr = (int)TestContext.CurrentContext.Random.NextUInt() & (1 << (int)FPCR.DN);
|
||||||
|
|
||||||
|
AThreadState ThreadState = SingleOpcode(Opcodes, V0: V0, V1: V1, Fpcr: Fpcr);
|
||||||
|
|
||||||
|
CompareAgainstUnicorn(FpsrMask: FPSR.IOC);
|
||||||
|
}
|
||||||
|
|
||||||
[Test, Pairwise, Description("NEG <V><d>, <V><n>")]
|
[Test, Pairwise, Description("NEG <V><d>, <V><n>")]
|
||||||
public void Neg_S_D([Values(0u)] uint Rd,
|
public void Neg_S_D([Values(0u)] uint Rd,
|
||||||
[Values(1u, 0u)] uint Rn,
|
[Values(1u, 0u)] uint Rn,
|
||||||
|
|
|
@ -9,126 +9,6 @@ namespace Ryujinx.Tests.Cpu
|
||||||
{
|
{
|
||||||
public class CpuTestSimdArithmetic : CpuTest
|
public class CpuTestSimdArithmetic : CpuTest
|
||||||
{
|
{
|
||||||
[TestCase(0x1E224820u, 0x0000000000000000ul, 0x0000000080000000ul, 0x0000000000000000ul)] // FMAX S0, S1, S2
|
|
||||||
[TestCase(0x1E224820u, 0x0000000080000000ul, 0x0000000000000000ul, 0x0000000000000000ul)]
|
|
||||||
[TestCase(0x1E224820u, 0x0000000080000000ul, 0x0000000080000000ul, 0x0000000080000000ul)]
|
|
||||||
[TestCase(0x1E224820u, 0x0000000080000000ul, 0x000000003DCCCCCDul, 0x000000003DCCCCCDul)]
|
|
||||||
[TestCase(0x1E224820u, 0x000000003DCCCCCDul, 0x000000003C9623B1ul, 0x000000003DCCCCCDul)]
|
|
||||||
[TestCase(0x1E224820u, 0x000000008BA98D27ul, 0x0000000000000076ul, 0x0000000000000076ul)]
|
|
||||||
[TestCase(0x1E224820u, 0x00000000807FFFFFul, 0x000000007F7FFFFFul, 0x000000007F7FFFFFul)]
|
|
||||||
[TestCase(0x1E224820u, 0x000000007F7FFFFFul, 0x00000000807FFFFFul, 0x000000007F7FFFFFul)]
|
|
||||||
[TestCase(0x1E224820u, 0x000000007FC00000ul, 0x000000003F800000ul, 0x000000007FC00000ul)]
|
|
||||||
[TestCase(0x1E224820u, 0x000000003F800000ul, 0x000000007FC00000ul, 0x000000007FC00000ul)]
|
|
||||||
[TestCase(0x1E224820u, 0x000000007F800001ul, 0x000000007FC00042ul, 0x000000007FC00001ul)]
|
|
||||||
[TestCase(0x1E224820u, 0x000000007FC00042ul, 0x000000007F800001ul, 0x000000007FC00001ul)]
|
|
||||||
[TestCase(0x1E224820u, 0x000000007FC0000Aul, 0x000000007FC0000Bul, 0x000000007FC0000Aul)]
|
|
||||||
[TestCase(0x1E624820u, 0x0000000000000000ul, 0x8000000000000000ul, 0x0000000000000000ul)] // FMAX D0, D1, D2
|
|
||||||
[TestCase(0x1E624820u, 0x8000000000000000ul, 0x0000000000000000ul, 0x0000000000000000ul)]
|
|
||||||
[TestCase(0x1E624820u, 0x8000000000000000ul, 0x8000000000000000ul, 0x8000000000000000ul)]
|
|
||||||
[TestCase(0x1E624820u, 0x8000000000000000ul, 0x3FF3333333333333ul, 0x3FF3333333333333ul)]
|
|
||||||
public void Fmax_S(uint Opcode, ulong A, ulong B, ulong Result)
|
|
||||||
{
|
|
||||||
Vector128<float> V1 = MakeVectorE0(A);
|
|
||||||
Vector128<float> V2 = MakeVectorE0(B);
|
|
||||||
|
|
||||||
AThreadState ThreadState = SingleOpcode(Opcode, V1: V1, V2: V2);
|
|
||||||
|
|
||||||
Assert.That(GetVectorE0(ThreadState.V0), Is.EqualTo(Result));
|
|
||||||
|
|
||||||
CompareAgainstUnicorn();
|
|
||||||
}
|
|
||||||
|
|
||||||
[TestCase(0x80000000u, 0x80000000u, 0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u)]
|
|
||||||
[TestCase(0x00000000u, 0x00000000u, 0x80000000u, 0x80000000u, 0x00000000u, 0x00000000u)]
|
|
||||||
[TestCase(0x80000000u, 0x80000000u, 0x80000000u, 0x80000000u, 0x80000000u, 0x80000000u)]
|
|
||||||
[TestCase(0x80000000u, 0x80000000u, 0x3DCCCCCDu, 0x3DCCCCCDu, 0x3DCCCCCDu, 0x3DCCCCCDu)]
|
|
||||||
[TestCase(0x3DCCCCCDu, 0x3DCCCCCDu, 0x3C9623B1u, 0x3C9623B1u, 0x3DCCCCCDu, 0x3DCCCCCDu)]
|
|
||||||
[TestCase(0x8BA98D27u, 0x8BA98D27u, 0x00000076u, 0x00000076u, 0x00000076u, 0x00000076u)]
|
|
||||||
[TestCase(0x807FFFFFu, 0x807FFFFFu, 0x7F7FFFFFu, 0x7F7FFFFFu, 0x7F7FFFFFu, 0x7F7FFFFFu)]
|
|
||||||
[TestCase(0x7F7FFFFFu, 0x7F7FFFFFu, 0x807FFFFFu, 0x807FFFFFu, 0x7F7FFFFFu, 0x7F7FFFFFu)]
|
|
||||||
[TestCase(0x7FC00000u, 0x7FC00000u, 0x3F800000u, 0x3F800000u, 0x7FC00000u, 0x7FC00000u)]
|
|
||||||
[TestCase(0x3F800000u, 0x3F800000u, 0x7FC00000u, 0x7FC00000u, 0x7FC00000u, 0x7FC00000u)]
|
|
||||||
[TestCase(0x7F800001u, 0x7F800001u, 0x7FC00042u, 0x7FC00042u, 0x7FC00001u, 0x7FC00001u)]
|
|
||||||
[TestCase(0x7FC00042u, 0x7FC00042u, 0x7F800001u, 0x7F800001u, 0x7FC00001u, 0x7FC00001u)]
|
|
||||||
[TestCase(0x7FC0000Au, 0x7FC0000Au, 0x7FC0000Bu, 0x7FC0000Bu, 0x7FC0000Au, 0x7FC0000Au)]
|
|
||||||
public void Fmax_V(uint A, uint B, uint C, uint D, uint Result0, uint Result1)
|
|
||||||
{
|
|
||||||
uint Opcode = 0x4E22F420; // FMAX V0.4S, V1.4S, V2.4S
|
|
||||||
|
|
||||||
Vector128<float> V1 = MakeVectorE0E1(A, B);
|
|
||||||
Vector128<float> V2 = MakeVectorE0E1(C, D);
|
|
||||||
|
|
||||||
AThreadState ThreadState = SingleOpcode(Opcode, V1: V1, V2: V2);
|
|
||||||
|
|
||||||
Assert.Multiple(() =>
|
|
||||||
{
|
|
||||||
Assert.That(GetVectorE0(ThreadState.V0), Is.EqualTo(Result0));
|
|
||||||
Assert.That(GetVectorE1(ThreadState.V0), Is.EqualTo(Result1));
|
|
||||||
});
|
|
||||||
|
|
||||||
CompareAgainstUnicorn();
|
|
||||||
}
|
|
||||||
|
|
||||||
[TestCase(0x1E225820u, 0x0000000000000000ul, 0x0000000080000000ul, 0x0000000080000000ul)] // FMIN S0, S1, S2
|
|
||||||
[TestCase(0x1E225820u, 0x0000000080000000ul, 0x0000000000000000ul, 0x0000000080000000ul)]
|
|
||||||
[TestCase(0x1E225820u, 0x0000000080000000ul, 0x0000000080000000ul, 0x0000000080000000ul)]
|
|
||||||
[TestCase(0x1E225820u, 0x0000000080000000ul, 0x000000003DCCCCCDul, 0x0000000080000000ul)]
|
|
||||||
[TestCase(0x1E225820u, 0x000000003DCCCCCDul, 0x000000003C9623B1ul, 0x000000003C9623B1ul)]
|
|
||||||
[TestCase(0x1E225820u, 0x000000008BA98D27ul, 0x0000000000000076ul, 0x000000008BA98D27ul)]
|
|
||||||
[TestCase(0x1E225820u, 0x00000000807FFFFFul, 0x000000007F7FFFFFul, 0x00000000807FFFFFul)]
|
|
||||||
[TestCase(0x1E225820u, 0x000000007F7FFFFFul, 0x00000000807FFFFFul, 0x00000000807FFFFFul)]
|
|
||||||
[TestCase(0x1E225820u, 0x000000007FC00000ul, 0x000000003F800000ul, 0x000000007FC00000ul)]
|
|
||||||
[TestCase(0x1E225820u, 0x000000003F800000ul, 0x000000007FC00000ul, 0x000000007FC00000ul)]
|
|
||||||
[TestCase(0x1E225820u, 0x000000007F800001ul, 0x000000007FC00042ul, 0x000000007FC00001ul)]
|
|
||||||
[TestCase(0x1E225820u, 0x000000007FC00042ul, 0x000000007F800001ul, 0x000000007FC00001ul)]
|
|
||||||
[TestCase(0x1E225820u, 0x000000007FC0000Aul, 0x000000007FC0000Bul, 0x000000007FC0000Aul)]
|
|
||||||
[TestCase(0x1E625820u, 0x0000000000000000ul, 0x8000000000000000ul, 0x8000000000000000ul)] // FMIN D0, D1, D2
|
|
||||||
[TestCase(0x1E625820u, 0x8000000000000000ul, 0x0000000000000000ul, 0x8000000000000000ul)]
|
|
||||||
[TestCase(0x1E625820u, 0x8000000000000000ul, 0x8000000000000000ul, 0x8000000000000000ul)]
|
|
||||||
[TestCase(0x1E625820u, 0x8000000000000000ul, 0x3FF3333333333333ul, 0x8000000000000000ul)]
|
|
||||||
public void Fmin_S(uint Opcode, ulong A, ulong B, ulong Result)
|
|
||||||
{
|
|
||||||
Vector128<float> V1 = MakeVectorE0(A);
|
|
||||||
Vector128<float> V2 = MakeVectorE0(B);
|
|
||||||
|
|
||||||
AThreadState ThreadState = SingleOpcode(Opcode, V1: V1, V2: V2);
|
|
||||||
|
|
||||||
Assert.That(GetVectorE0(ThreadState.V0), Is.EqualTo(Result));
|
|
||||||
|
|
||||||
CompareAgainstUnicorn();
|
|
||||||
}
|
|
||||||
|
|
||||||
[TestCase(0x80000000u, 0x80000000u, 0x00000000u, 0x00000000u, 0x80000000u, 0x80000000u)]
|
|
||||||
[TestCase(0x00000000u, 0x00000000u, 0x80000000u, 0x80000000u, 0x80000000u, 0x80000000u)]
|
|
||||||
[TestCase(0x80000000u, 0x80000000u, 0x80000000u, 0x80000000u, 0x80000000u, 0x80000000u)]
|
|
||||||
[TestCase(0x80000000u, 0x80000000u, 0x3DCCCCCDu, 0x3DCCCCCDu, 0x80000000u, 0x80000000u)]
|
|
||||||
[TestCase(0x3DCCCCCDu, 0x3DCCCCCDu, 0x3C9623B1u, 0x3C9623B1u, 0x3C9623B1u, 0x3C9623B1u)]
|
|
||||||
[TestCase(0x8BA98D27u, 0x8BA98D27u, 0x00000076u, 0x00000076u, 0x8BA98D27u, 0x8BA98D27u)]
|
|
||||||
[TestCase(0x807FFFFFu, 0x807FFFFFu, 0x7F7FFFFFu, 0x7F7FFFFFu, 0x807FFFFFu, 0x807FFFFFu)]
|
|
||||||
[TestCase(0x7F7FFFFFu, 0x7F7FFFFFu, 0x807FFFFFu, 0x807FFFFFu, 0x807FFFFFu, 0x807FFFFFu)]
|
|
||||||
[TestCase(0x7FC00000u, 0x7FC00000u, 0x3F800000u, 0x3F800000u, 0x7FC00000u, 0x7FC00000u)]
|
|
||||||
[TestCase(0x3F800000u, 0x3F800000u, 0x7FC00000u, 0x7FC00000u, 0x7FC00000u, 0x7FC00000u)]
|
|
||||||
[TestCase(0x7F800001u, 0x7F800001u, 0x7FC00042u, 0x7FC00042u, 0x7FC00001u, 0x7FC00001u)]
|
|
||||||
[TestCase(0x7FC00042u, 0x7FC00042u, 0x7F800001u, 0x7F800001u, 0x7FC00001u, 0x7FC00001u)]
|
|
||||||
[TestCase(0x7FC0000Au, 0x7FC0000Au, 0x7FC0000Bu, 0x7FC0000Bu, 0x7FC0000Au, 0x7FC0000Au)]
|
|
||||||
public void Fmin_V(uint A, uint B, uint C, uint D, uint Result0, uint Result1)
|
|
||||||
{
|
|
||||||
uint Opcode = 0x4EA2F420; // FMIN V0.4S, V1.4S, V2.4S
|
|
||||||
|
|
||||||
Vector128<float> V1 = MakeVectorE0E1(A, B);
|
|
||||||
Vector128<float> V2 = MakeVectorE0E1(C, D);
|
|
||||||
|
|
||||||
AThreadState ThreadState = SingleOpcode(Opcode, V1: V1, V2: V2);
|
|
||||||
|
|
||||||
Assert.Multiple(() =>
|
|
||||||
{
|
|
||||||
Assert.That(GetVectorE0(ThreadState.V0), Is.EqualTo(Result0));
|
|
||||||
Assert.That(GetVectorE1(ThreadState.V0), Is.EqualTo(Result1));
|
|
||||||
});
|
|
||||||
|
|
||||||
CompareAgainstUnicorn();
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test, Description("FMUL S6, S1, V0.S[2]")]
|
[Test, Description("FMUL S6, S1, V0.S[2]")]
|
||||||
public void Fmul_Se([Random(10)] float A, [Random(10)] float B)
|
public void Fmul_Se([Random(10)] float A, [Random(10)] float B)
|
||||||
{
|
{
|
||||||
|
@ -161,38 +41,6 @@ namespace Ryujinx.Tests.Cpu
|
||||||
CompareAgainstUnicorn();
|
CompareAgainstUnicorn();
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test, Description("FRECPS D0, D1, D2"), Ignore("Not accurate enough.")]
|
|
||||||
public void Frecps_S([Random(10)] double A, [Random(10)] double B)
|
|
||||||
{
|
|
||||||
AThreadState ThreadState = SingleOpcode(0x5E62FC20,
|
|
||||||
V1: MakeVectorE0(A),
|
|
||||||
V2: MakeVectorE0(B));
|
|
||||||
|
|
||||||
Assert.That(VectorExtractDouble(ThreadState.V0, (byte)0), Is.EqualTo(2 - (A * B)));
|
|
||||||
|
|
||||||
CompareAgainstUnicorn();
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test, Description("FRECPS V4.4S, V2.4S, V0.4S")]
|
|
||||||
public void Frecps_V([Random(10)] float A, [Random(10)] float B)
|
|
||||||
{
|
|
||||||
AThreadState ThreadState = SingleOpcode(0x4E20FC44,
|
|
||||||
V2: Sse.SetAllVector128(A),
|
|
||||||
V0: Sse.SetAllVector128(B));
|
|
||||||
|
|
||||||
float Result = (float)(2 - ((double)A * (double)B));
|
|
||||||
|
|
||||||
Assert.Multiple(() =>
|
|
||||||
{
|
|
||||||
Assert.That(Sse41.Extract(ThreadState.V4, (byte)0), Is.EqualTo(Result));
|
|
||||||
Assert.That(Sse41.Extract(ThreadState.V4, (byte)1), Is.EqualTo(Result));
|
|
||||||
Assert.That(Sse41.Extract(ThreadState.V4, (byte)2), Is.EqualTo(Result));
|
|
||||||
Assert.That(Sse41.Extract(ThreadState.V4, (byte)3), Is.EqualTo(Result));
|
|
||||||
});
|
|
||||||
|
|
||||||
CompareAgainstUnicorn();
|
|
||||||
}
|
|
||||||
|
|
||||||
[TestCase(0x3FE66666u, false, 0x40000000u)]
|
[TestCase(0x3FE66666u, false, 0x40000000u)]
|
||||||
[TestCase(0x3F99999Au, false, 0x3F800000u)]
|
[TestCase(0x3F99999Au, false, 0x3F800000u)]
|
||||||
[TestCase(0x404CCCCDu, false, 0x40400000u)]
|
[TestCase(0x404CCCCDu, false, 0x40400000u)]
|
||||||
|
|
|
@ -1,407 +0,0 @@
|
||||||
using ChocolArm64.State;
|
|
||||||
|
|
||||||
using NUnit.Framework;
|
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Runtime.Intrinsics;
|
|
||||||
using System.Runtime.Intrinsics.X86;
|
|
||||||
|
|
||||||
namespace Ryujinx.Tests.Cpu
|
|
||||||
{
|
|
||||||
public class CpuTestSimdCmp : CpuTest
|
|
||||||
{
|
|
||||||
#region "ValueSource"
|
|
||||||
private static float[] _floats_()
|
|
||||||
{
|
|
||||||
return new float[] { float.NegativeInfinity, float.MinValue, -1f, -0f,
|
|
||||||
+0f, +1f, float.MaxValue, float.PositiveInfinity };
|
|
||||||
}
|
|
||||||
|
|
||||||
private static double[] _doubles_()
|
|
||||||
{
|
|
||||||
return new double[] { double.NegativeInfinity, double.MinValue, -1d, -0d,
|
|
||||||
+0d, +1d, double.MaxValue, double.PositiveInfinity };
|
|
||||||
}
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
private const int RndCnt = 2;
|
|
||||||
|
|
||||||
[Test, Description("FCMEQ D0, D1, D2 | FCMGE D0, D1, D2 | FCMGT D0, D1, D2")]
|
|
||||||
public void Fcmeq_Fcmge_Fcmgt_Reg_S_D([ValueSource("_doubles_")] [Random(RndCnt)] double A,
|
|
||||||
[ValueSource("_doubles_")] [Random(RndCnt)] double B,
|
|
||||||
[Values(0u, 1u, 3u)] uint EU) // EQ, GE, GT
|
|
||||||
{
|
|
||||||
uint Opcode = 0x5E62E420 | ((EU & 1) << 29) | ((EU >> 1) << 23);
|
|
||||||
|
|
||||||
Vector128<float> V0 = Sse.StaticCast<double, float>(Sse2.SetAllVector128(TestContext.CurrentContext.Random.NextDouble()));
|
|
||||||
Vector128<float> V1 = Sse.StaticCast<double, float>(Sse2.SetScalarVector128(A));
|
|
||||||
Vector128<float> V2 = Sse.StaticCast<double, float>(Sse2.SetScalarVector128(B));
|
|
||||||
|
|
||||||
AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2);
|
|
||||||
|
|
||||||
byte[] Exp = default(byte[]);
|
|
||||||
byte[] Ones = new byte[] {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
|
|
||||||
byte[] Zeros = new byte[] {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
|
|
||||||
|
|
||||||
switch (EU)
|
|
||||||
{
|
|
||||||
case 0: Exp = (A == B ? Ones : Zeros); break;
|
|
||||||
case 1: Exp = (A >= B ? Ones : Zeros); break;
|
|
||||||
case 3: Exp = (A > B ? Ones : Zeros); break;
|
|
||||||
}
|
|
||||||
|
|
||||||
Assert.Multiple(() =>
|
|
||||||
{
|
|
||||||
Assert.That(BitConverter.GetBytes(VectorExtractDouble(ThreadState.V0, (byte)0)), Is.EquivalentTo(Exp));
|
|
||||||
Assert.That(VectorExtractDouble(ThreadState.V0, (byte)1), Is.Zero);
|
|
||||||
});
|
|
||||||
|
|
||||||
CompareAgainstUnicorn();
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test, Description("FCMEQ S0, S1, S2 | FCMGE S0, S1, S2 | FCMGT S0, S1, S2")]
|
|
||||||
public void Fcmeq_Fcmge_Fcmgt_Reg_S_S([ValueSource("_floats_")] [Random(RndCnt)] float A,
|
|
||||||
[ValueSource("_floats_")] [Random(RndCnt)] float B,
|
|
||||||
[Values(0u, 1u, 3u)] uint EU) // EQ, GE, GT
|
|
||||||
{
|
|
||||||
uint Opcode = 0x5E22E420 | ((EU & 1) << 29) | ((EU >> 1) << 23);
|
|
||||||
|
|
||||||
Vector128<float> V0 = Sse.SetAllVector128(TestContext.CurrentContext.Random.NextFloat());
|
|
||||||
Vector128<float> V1 = Sse.SetScalarVector128(A);
|
|
||||||
Vector128<float> V2 = Sse.SetScalarVector128(B);
|
|
||||||
|
|
||||||
AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2);
|
|
||||||
|
|
||||||
byte[] Exp = default(byte[]);
|
|
||||||
byte[] Ones = new byte[] {0xFF, 0xFF, 0xFF, 0xFF};
|
|
||||||
byte[] Zeros = new byte[] {0x00, 0x00, 0x00, 0x00};
|
|
||||||
|
|
||||||
switch (EU)
|
|
||||||
{
|
|
||||||
case 0: Exp = (A == B ? Ones : Zeros); break;
|
|
||||||
case 1: Exp = (A >= B ? Ones : Zeros); break;
|
|
||||||
case 3: Exp = (A > B ? Ones : Zeros); break;
|
|
||||||
}
|
|
||||||
|
|
||||||
Assert.Multiple(() =>
|
|
||||||
{
|
|
||||||
Assert.That(BitConverter.GetBytes(Sse41.Extract(ThreadState.V0, (byte)0)), Is.EquivalentTo(Exp));
|
|
||||||
Assert.That(Sse41.Extract(ThreadState.V0, (byte)1), Is.Zero);
|
|
||||||
Assert.That(Sse41.Extract(ThreadState.V0, (byte)2), Is.Zero);
|
|
||||||
Assert.That(Sse41.Extract(ThreadState.V0, (byte)3), Is.Zero);
|
|
||||||
});
|
|
||||||
|
|
||||||
CompareAgainstUnicorn();
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test, Description("FCMEQ V0.2D, V1.2D, V2.2D | FCMGE V0.2D, V1.2D, V2.2D | FCMGT V0.2D, V1.2D, V2.2D")]
|
|
||||||
public void Fcmeq_Fcmge_Fcmgt_Reg_V_2D([ValueSource("_doubles_")] [Random(RndCnt)] double A,
|
|
||||||
[ValueSource("_doubles_")] [Random(RndCnt)] double B,
|
|
||||||
[Values(0u, 1u, 3u)] uint EU) // EQ, GE, GT
|
|
||||||
{
|
|
||||||
uint Opcode = 0x4E62E420 | ((EU & 1) << 29) | ((EU >> 1) << 23);
|
|
||||||
|
|
||||||
Vector128<float> V1 = Sse.StaticCast<double, float>(Sse2.SetAllVector128(A));
|
|
||||||
Vector128<float> V2 = Sse.StaticCast<double, float>(Sse2.SetAllVector128(B));
|
|
||||||
|
|
||||||
AThreadState ThreadState = SingleOpcode(Opcode, V1: V1, V2: V2);
|
|
||||||
|
|
||||||
byte[] Exp = default(byte[]);
|
|
||||||
byte[] Ones = new byte[] {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
|
|
||||||
byte[] Zeros = new byte[] {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
|
|
||||||
|
|
||||||
switch (EU)
|
|
||||||
{
|
|
||||||
case 0: Exp = (A == B ? Ones : Zeros); break;
|
|
||||||
case 1: Exp = (A >= B ? Ones : Zeros); break;
|
|
||||||
case 3: Exp = (A > B ? Ones : Zeros); break;
|
|
||||||
}
|
|
||||||
|
|
||||||
Assert.Multiple(() =>
|
|
||||||
{
|
|
||||||
Assert.That(BitConverter.GetBytes(VectorExtractDouble(ThreadState.V0, (byte)0)), Is.EquivalentTo(Exp));
|
|
||||||
Assert.That(BitConverter.GetBytes(VectorExtractDouble(ThreadState.V0, (byte)1)), Is.EquivalentTo(Exp));
|
|
||||||
});
|
|
||||||
|
|
||||||
CompareAgainstUnicorn();
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test, Description("FCMEQ V0.2S, V1.2S, V2.2S | FCMGE V0.2S, V1.2S, V2.2S | FCMGT V0.2S, V1.2S, V2.2S")]
|
|
||||||
public void Fcmeq_Fcmge_Fcmgt_Reg_V_2S([ValueSource("_floats_")] [Random(RndCnt)] float A,
|
|
||||||
[ValueSource("_floats_")] [Random(RndCnt)] float B,
|
|
||||||
[Values(0u, 1u, 3u)] uint EU) // EQ, GE, GT
|
|
||||||
{
|
|
||||||
uint Opcode = 0x0E22E420 | ((EU & 1) << 29) | ((EU >> 1) << 23);
|
|
||||||
|
|
||||||
Vector128<float> V0 = Sse.SetAllVector128(TestContext.CurrentContext.Random.NextFloat());
|
|
||||||
Vector128<float> V1 = Sse.SetVector128(0, 0, A, A);
|
|
||||||
Vector128<float> V2 = Sse.SetVector128(0, 0, B, B);
|
|
||||||
|
|
||||||
AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2);
|
|
||||||
|
|
||||||
byte[] Exp = default(byte[]);
|
|
||||||
byte[] Ones = new byte[] {0xFF, 0xFF, 0xFF, 0xFF};
|
|
||||||
byte[] Zeros = new byte[] {0x00, 0x00, 0x00, 0x00};
|
|
||||||
|
|
||||||
switch (EU)
|
|
||||||
{
|
|
||||||
case 0: Exp = (A == B ? Ones : Zeros); break;
|
|
||||||
case 1: Exp = (A >= B ? Ones : Zeros); break;
|
|
||||||
case 3: Exp = (A > B ? Ones : Zeros); break;
|
|
||||||
}
|
|
||||||
|
|
||||||
Assert.Multiple(() =>
|
|
||||||
{
|
|
||||||
Assert.That(BitConverter.GetBytes(Sse41.Extract(ThreadState.V0, (byte)0)), Is.EquivalentTo(Exp));
|
|
||||||
Assert.That(BitConverter.GetBytes(Sse41.Extract(ThreadState.V0, (byte)1)), Is.EquivalentTo(Exp));
|
|
||||||
Assert.That(Sse41.Extract(ThreadState.V0, (byte)2), Is.Zero);
|
|
||||||
Assert.That(Sse41.Extract(ThreadState.V0, (byte)3), Is.Zero);
|
|
||||||
});
|
|
||||||
|
|
||||||
CompareAgainstUnicorn();
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test, Description("FCMEQ V0.4S, V1.4S, V2.4S | FCMGE V0.4S, V1.4S, V2.4S | FCMGT V0.4S, V1.4S, V2.4S")]
|
|
||||||
public void Fcmeq_Fcmge_Fcmgt_Reg_V_4S([ValueSource("_floats_")] [Random(RndCnt)] float A,
|
|
||||||
[ValueSource("_floats_")] [Random(RndCnt)] float B,
|
|
||||||
[Values(0u, 1u, 3u)] uint EU) // EQ, GE, GT
|
|
||||||
{
|
|
||||||
uint Opcode = 0x4E22E420 | ((EU & 1) << 29) | ((EU >> 1) << 23);
|
|
||||||
|
|
||||||
Vector128<float> V1 = Sse.SetAllVector128(A);
|
|
||||||
Vector128<float> V2 = Sse.SetAllVector128(B);
|
|
||||||
|
|
||||||
AThreadState ThreadState = SingleOpcode(Opcode, V1: V1, V2: V2);
|
|
||||||
|
|
||||||
byte[] Exp = default(byte[]);
|
|
||||||
byte[] Ones = new byte[] {0xFF, 0xFF, 0xFF, 0xFF};
|
|
||||||
byte[] Zeros = new byte[] {0x00, 0x00, 0x00, 0x00};
|
|
||||||
|
|
||||||
switch (EU)
|
|
||||||
{
|
|
||||||
case 0: Exp = (A == B ? Ones : Zeros); break;
|
|
||||||
case 1: Exp = (A >= B ? Ones : Zeros); break;
|
|
||||||
case 3: Exp = (A > B ? Ones : Zeros); break;
|
|
||||||
}
|
|
||||||
|
|
||||||
Assert.Multiple(() =>
|
|
||||||
{
|
|
||||||
Assert.That(BitConverter.GetBytes(Sse41.Extract(ThreadState.V0, (byte)0)), Is.EquivalentTo(Exp));
|
|
||||||
Assert.That(BitConverter.GetBytes(Sse41.Extract(ThreadState.V0, (byte)1)), Is.EquivalentTo(Exp));
|
|
||||||
Assert.That(BitConverter.GetBytes(Sse41.Extract(ThreadState.V0, (byte)2)), Is.EquivalentTo(Exp));
|
|
||||||
Assert.That(BitConverter.GetBytes(Sse41.Extract(ThreadState.V0, (byte)3)), Is.EquivalentTo(Exp));
|
|
||||||
});
|
|
||||||
|
|
||||||
CompareAgainstUnicorn();
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test, Description("FCMGT D0, D1, #0.0 | FCMGE D0, D1, #0.0 | FCMEQ D0, D1, #0.0 | FCMLE D0, D1, #0.0 | FCMLT D0, D1, #0.0")]
|
|
||||||
public void Fcmgt_Fcmge_Fcmeq_Fcmle_Fcmlt_Zero_S_D([ValueSource("_doubles_")] [Random(RndCnt)] double A,
|
|
||||||
[Values(0u, 1u, 2u, 3u)] uint opU, // GT, GE, EQ, LE
|
|
||||||
[Values(0u, 1u)] uint bit13) // "LT"
|
|
||||||
{
|
|
||||||
uint Opcode = 0x5EE0C820 | (((opU & 1) & ~bit13) << 29) | (bit13 << 13) | (((opU >> 1) & ~bit13) << 12);
|
|
||||||
|
|
||||||
Vector128<float> V0 = Sse.StaticCast<double, float>(Sse2.SetAllVector128(TestContext.CurrentContext.Random.NextDouble()));
|
|
||||||
Vector128<float> V1 = Sse.StaticCast<double, float>(Sse2.SetScalarVector128(A));
|
|
||||||
|
|
||||||
AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1);
|
|
||||||
|
|
||||||
double Zero = +0d;
|
|
||||||
byte[] Exp = default(byte[]);
|
|
||||||
byte[] Ones = new byte[] {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
|
|
||||||
byte[] Zeros = new byte[] {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
|
|
||||||
|
|
||||||
if (bit13 == 0)
|
|
||||||
{
|
|
||||||
switch (opU)
|
|
||||||
{
|
|
||||||
case 0: Exp = (A > Zero ? Ones : Zeros); break;
|
|
||||||
case 1: Exp = (A >= Zero ? Ones : Zeros); break;
|
|
||||||
case 2: Exp = (A == Zero ? Ones : Zeros); break;
|
|
||||||
case 3: Exp = (Zero >= A ? Ones : Zeros); break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Exp = (Zero > A ? Ones : Zeros);
|
|
||||||
}
|
|
||||||
|
|
||||||
Assert.Multiple(() =>
|
|
||||||
{
|
|
||||||
Assert.That(BitConverter.GetBytes(VectorExtractDouble(ThreadState.V0, (byte)0)), Is.EquivalentTo(Exp));
|
|
||||||
Assert.That(VectorExtractDouble(ThreadState.V0, (byte)1), Is.Zero);
|
|
||||||
});
|
|
||||||
|
|
||||||
CompareAgainstUnicorn();
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test, Description("FCMGT S0, S1, #0.0 | FCMGE S0, S1, #0.0 | FCMEQ S0, S1, #0.0 | FCMLE S0, S1, #0.0 | FCMLT S0, S1, #0.0")]
|
|
||||||
public void Fcmgt_Fcmge_Fcmeq_Fcmle_Fcmlt_Zero_S_S([ValueSource("_floats_")] [Random(RndCnt)] float A,
|
|
||||||
[Values(0u, 1u, 2u, 3u)] uint opU, // GT, GE, EQ, LE
|
|
||||||
[Values(0u, 1u)] uint bit13) // "LT"
|
|
||||||
{
|
|
||||||
uint Opcode = 0x5EA0C820 | (((opU & 1) & ~bit13) << 29) | (bit13 << 13) | (((opU >> 1) & ~bit13) << 12);
|
|
||||||
|
|
||||||
Vector128<float> V0 = Sse.SetAllVector128(TestContext.CurrentContext.Random.NextFloat());
|
|
||||||
Vector128<float> V1 = Sse.SetScalarVector128(A);
|
|
||||||
|
|
||||||
AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1);
|
|
||||||
|
|
||||||
float Zero = +0f;
|
|
||||||
byte[] Exp = default(byte[]);
|
|
||||||
byte[] Ones = new byte[] {0xFF, 0xFF, 0xFF, 0xFF};
|
|
||||||
byte[] Zeros = new byte[] {0x00, 0x00, 0x00, 0x00};
|
|
||||||
|
|
||||||
if (bit13 == 0)
|
|
||||||
{
|
|
||||||
switch (opU)
|
|
||||||
{
|
|
||||||
case 0: Exp = (A > Zero ? Ones : Zeros); break;
|
|
||||||
case 1: Exp = (A >= Zero ? Ones : Zeros); break;
|
|
||||||
case 2: Exp = (A == Zero ? Ones : Zeros); break;
|
|
||||||
case 3: Exp = (Zero >= A ? Ones : Zeros); break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Exp = (Zero > A ? Ones : Zeros);
|
|
||||||
}
|
|
||||||
|
|
||||||
Assert.Multiple(() =>
|
|
||||||
{
|
|
||||||
Assert.That(BitConverter.GetBytes(Sse41.Extract(ThreadState.V0, (byte)0)), Is.EquivalentTo(Exp));
|
|
||||||
Assert.That(Sse41.Extract(ThreadState.V0, (byte)1), Is.Zero);
|
|
||||||
Assert.That(Sse41.Extract(ThreadState.V0, (byte)2), Is.Zero);
|
|
||||||
Assert.That(Sse41.Extract(ThreadState.V0, (byte)3), Is.Zero);
|
|
||||||
});
|
|
||||||
|
|
||||||
CompareAgainstUnicorn();
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test, Description("FCMGT V0.2D, V1.2D, #0.0 | FCMGE V0.2D, V1.2D, #0.0 | FCMEQ V0.2D, V1.2D, #0.0 | FCMLE V0.2D, V1.2D, #0.0 | FCMLT V0.2D, V1.2D, #0.0")]
|
|
||||||
public void Fcmgt_Fcmge_Fcmeq_Fcmle_Fcmlt_Zero_V_2D([ValueSource("_doubles_")] [Random(RndCnt)] double A,
|
|
||||||
[Values(0u, 1u, 2u, 3u)] uint opU, // GT, GE, EQ, LE
|
|
||||||
[Values(0u, 1u)] uint bit13) // "LT"
|
|
||||||
{
|
|
||||||
uint Opcode = 0x4EE0C820 | (((opU & 1) & ~bit13) << 29) | (bit13 << 13) | (((opU >> 1) & ~bit13) << 12);
|
|
||||||
|
|
||||||
Vector128<float> V1 = Sse.StaticCast<double, float>(Sse2.SetAllVector128(A));
|
|
||||||
|
|
||||||
AThreadState ThreadState = SingleOpcode(Opcode, V1: V1);
|
|
||||||
|
|
||||||
double Zero = +0d;
|
|
||||||
byte[] Exp = default(byte[]);
|
|
||||||
byte[] Ones = new byte[] {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
|
|
||||||
byte[] Zeros = new byte[] {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
|
|
||||||
|
|
||||||
if (bit13 == 0)
|
|
||||||
{
|
|
||||||
switch (opU)
|
|
||||||
{
|
|
||||||
case 0: Exp = (A > Zero ? Ones : Zeros); break;
|
|
||||||
case 1: Exp = (A >= Zero ? Ones : Zeros); break;
|
|
||||||
case 2: Exp = (A == Zero ? Ones : Zeros); break;
|
|
||||||
case 3: Exp = (Zero >= A ? Ones : Zeros); break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Exp = (Zero > A ? Ones : Zeros);
|
|
||||||
}
|
|
||||||
|
|
||||||
Assert.Multiple(() =>
|
|
||||||
{
|
|
||||||
Assert.That(BitConverter.GetBytes(VectorExtractDouble(ThreadState.V0, (byte)0)), Is.EquivalentTo(Exp));
|
|
||||||
Assert.That(BitConverter.GetBytes(VectorExtractDouble(ThreadState.V0, (byte)1)), Is.EquivalentTo(Exp));
|
|
||||||
});
|
|
||||||
|
|
||||||
CompareAgainstUnicorn();
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test, Description("FCMGT V0.2S, V1.2S, #0.0 | FCMGE V0.2S, V1.2S, #0.0 | FCMEQ V0.2S, V1.2S, #0.0 | FCMLE V0.2S, V1.2S, #0.0 | FCMLT V0.2S, V1.2S, #0.0")]
|
|
||||||
public void Fcmgt_Fcmge_Fcmeq_Fcmle_Fcmlt_Zero_V_2S([ValueSource("_floats_")] [Random(RndCnt)] float A,
|
|
||||||
[Values(0u, 1u, 2u, 3u)] uint opU, // GT, GE, EQ, LE
|
|
||||||
[Values(0u, 1u)] uint bit13) // "LT"
|
|
||||||
{
|
|
||||||
uint Opcode = 0x0EA0C820 | (((opU & 1) & ~bit13) << 29) | (bit13 << 13) | (((opU >> 1) & ~bit13) << 12);
|
|
||||||
|
|
||||||
Vector128<float> V0 = Sse.SetAllVector128(TestContext.CurrentContext.Random.NextFloat());
|
|
||||||
Vector128<float> V1 = Sse.SetVector128(0, 0, A, A);
|
|
||||||
|
|
||||||
AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1);
|
|
||||||
|
|
||||||
float Zero = +0f;
|
|
||||||
byte[] Exp = default(byte[]);
|
|
||||||
byte[] Ones = new byte[] {0xFF, 0xFF, 0xFF, 0xFF};
|
|
||||||
byte[] Zeros = new byte[] {0x00, 0x00, 0x00, 0x00};
|
|
||||||
|
|
||||||
if (bit13 == 0)
|
|
||||||
{
|
|
||||||
switch (opU)
|
|
||||||
{
|
|
||||||
case 0: Exp = (A > Zero ? Ones : Zeros); break;
|
|
||||||
case 1: Exp = (A >= Zero ? Ones : Zeros); break;
|
|
||||||
case 2: Exp = (A == Zero ? Ones : Zeros); break;
|
|
||||||
case 3: Exp = (Zero >= A ? Ones : Zeros); break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Exp = (Zero > A ? Ones : Zeros);
|
|
||||||
}
|
|
||||||
|
|
||||||
Assert.Multiple(() =>
|
|
||||||
{
|
|
||||||
Assert.That(BitConverter.GetBytes(Sse41.Extract(ThreadState.V0, (byte)0)), Is.EquivalentTo(Exp));
|
|
||||||
Assert.That(BitConverter.GetBytes(Sse41.Extract(ThreadState.V0, (byte)1)), Is.EquivalentTo(Exp));
|
|
||||||
Assert.That(Sse41.Extract(ThreadState.V0, (byte)2), Is.Zero);
|
|
||||||
Assert.That(Sse41.Extract(ThreadState.V0, (byte)3), Is.Zero);
|
|
||||||
});
|
|
||||||
|
|
||||||
CompareAgainstUnicorn();
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test, Description("FCMGT V0.4S, V1.4S, #0.0 | FCMGE V0.4S, V1.4S, #0.0 | FCMEQ V0.4S, V1.4S, #0.0 | FCMLE V0.4S, V1.4S, #0.0 | FCMLT V0.4S, V1.4S, #0.0")]
|
|
||||||
public void Fcmgt_Fcmge_Fcmeq_Fcmle_Fcmlt_Zero_V_4S([ValueSource("_floats_")] [Random(RndCnt)] float A,
|
|
||||||
[Values(0u, 1u, 2u, 3u)] uint opU, // GT, GE, EQ, LE
|
|
||||||
[Values(0u, 1u)] uint bit13) // "LT"
|
|
||||||
{
|
|
||||||
uint Opcode = 0x4EA0C820 | (((opU & 1) & ~bit13) << 29) | (bit13 << 13) | (((opU >> 1) & ~bit13) << 12);
|
|
||||||
|
|
||||||
Vector128<float> V1 = Sse.SetAllVector128(A);
|
|
||||||
|
|
||||||
AThreadState ThreadState = SingleOpcode(Opcode, V1: V1);
|
|
||||||
|
|
||||||
float Zero = +0f;
|
|
||||||
byte[] Exp = default(byte[]);
|
|
||||||
byte[] Ones = new byte[] {0xFF, 0xFF, 0xFF, 0xFF};
|
|
||||||
byte[] Zeros = new byte[] {0x00, 0x00, 0x00, 0x00};
|
|
||||||
|
|
||||||
if (bit13 == 0)
|
|
||||||
{
|
|
||||||
switch (opU)
|
|
||||||
{
|
|
||||||
case 0: Exp = (A > Zero ? Ones : Zeros); break;
|
|
||||||
case 1: Exp = (A >= Zero ? Ones : Zeros); break;
|
|
||||||
case 2: Exp = (A == Zero ? Ones : Zeros); break;
|
|
||||||
case 3: Exp = (Zero >= A ? Ones : Zeros); break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Exp = (Zero > A ? Ones : Zeros);
|
|
||||||
}
|
|
||||||
|
|
||||||
Assert.Multiple(() =>
|
|
||||||
{
|
|
||||||
Assert.That(BitConverter.GetBytes(Sse41.Extract(ThreadState.V0, (byte)0)), Is.EquivalentTo(Exp));
|
|
||||||
Assert.That(BitConverter.GetBytes(Sse41.Extract(ThreadState.V0, (byte)1)), Is.EquivalentTo(Exp));
|
|
||||||
Assert.That(BitConverter.GetBytes(Sse41.Extract(ThreadState.V0, (byte)2)), Is.EquivalentTo(Exp));
|
|
||||||
Assert.That(BitConverter.GetBytes(Sse41.Extract(ThreadState.V0, (byte)3)), Is.EquivalentTo(Exp));
|
|
||||||
});
|
|
||||||
|
|
||||||
CompareAgainstUnicorn();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -80,14 +80,14 @@ namespace Ryujinx.Tests.Cpu
|
||||||
|
|
||||||
private static IEnumerable<ulong> _1S_F_()
|
private static IEnumerable<ulong> _1S_F_()
|
||||||
{
|
{
|
||||||
yield return 0x00000000FF7FFFFFul; // -Max Normal (float.MinValue)
|
yield return 0x00000000FF7FFFFFul; // -Max Normal (float.MinValue)
|
||||||
yield return 0x0000000080800000ul; // -Min Normal
|
yield return 0x0000000080800000ul; // -Min Normal
|
||||||
yield return 0x00000000807FFFFFul; // -Max SubNormal
|
yield return 0x00000000807FFFFFul; // -Max Subnormal
|
||||||
yield return 0x0000000080000001ul; // -Min SubNormal
|
yield return 0x0000000080000001ul; // -Min Subnormal (-float.Epsilon)
|
||||||
yield return 0x000000007F7FFFFFul; // +Max Normal (float.MaxValue)
|
yield return 0x000000007F7FFFFFul; // +Max Normal (float.MaxValue)
|
||||||
yield return 0x0000000000800000ul; // +Min Normal
|
yield return 0x0000000000800000ul; // +Min Normal
|
||||||
yield return 0x00000000007FFFFFul; // +Max SubNormal
|
yield return 0x00000000007FFFFFul; // +Max Subnormal
|
||||||
yield return 0x0000000000000001ul; // +Min SubNormal
|
yield return 0x0000000000000001ul; // +Min Subnormal (float.Epsilon)
|
||||||
|
|
||||||
if (!NoZeros)
|
if (!NoZeros)
|
||||||
{
|
{
|
||||||
|
@ -103,17 +103,17 @@ namespace Ryujinx.Tests.Cpu
|
||||||
|
|
||||||
if (!NoNaNs)
|
if (!NoNaNs)
|
||||||
{
|
{
|
||||||
yield return 0x00000000FFFFFFFFul; // -QNaN (all ones payload)
|
yield return 0x00000000FFC00000ul; // -QNaN (all zeros payload) (float.NaN)
|
||||||
yield return 0x00000000FFBFFFFFul; // -SNaN (all ones payload)
|
yield return 0x00000000FFBFFFFFul; // -SNaN (all ones payload)
|
||||||
yield return 0x000000007FFFFFFFul; // +QNaN (all ones payload)
|
yield return 0x000000007FC00000ul; // +QNaN (all zeros payload) (-float.NaN) (DefaultNaN)
|
||||||
yield return 0x000000007FBFFFFFul; // +SNaN (all ones payload)
|
yield return 0x000000007FBFFFFFul; // +SNaN (all ones payload)
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int Cnt = 1; Cnt <= RndCnt; Cnt++)
|
for (int Cnt = 1; Cnt <= RndCnt; Cnt++)
|
||||||
{
|
{
|
||||||
ulong Grbg = TestContext.CurrentContext.Random.NextUInt();
|
ulong Grbg = TestContext.CurrentContext.Random.NextUInt();
|
||||||
ulong Rnd1 = GenNormal_S();
|
ulong Rnd1 = GenNormal_S();
|
||||||
ulong Rnd2 = GenSubNormal_S();
|
ulong Rnd2 = GenSubnormal_S();
|
||||||
|
|
||||||
yield return (Grbg << 32) | Rnd1;
|
yield return (Grbg << 32) | Rnd1;
|
||||||
yield return (Grbg << 32) | Rnd2;
|
yield return (Grbg << 32) | Rnd2;
|
||||||
|
@ -122,14 +122,14 @@ namespace Ryujinx.Tests.Cpu
|
||||||
|
|
||||||
private static IEnumerable<ulong> _2S_F_()
|
private static IEnumerable<ulong> _2S_F_()
|
||||||
{
|
{
|
||||||
yield return 0xFF7FFFFFFF7FFFFFul; // -Max Normal (float.MinValue)
|
yield return 0xFF7FFFFFFF7FFFFFul; // -Max Normal (float.MinValue)
|
||||||
yield return 0x8080000080800000ul; // -Min Normal
|
yield return 0x8080000080800000ul; // -Min Normal
|
||||||
yield return 0x807FFFFF807FFFFFul; // -Max SubNormal
|
yield return 0x807FFFFF807FFFFFul; // -Max Subnormal
|
||||||
yield return 0x8000000180000001ul; // -Min SubNormal
|
yield return 0x8000000180000001ul; // -Min Subnormal (-float.Epsilon)
|
||||||
yield return 0x7F7FFFFF7F7FFFFFul; // +Max Normal (float.MaxValue)
|
yield return 0x7F7FFFFF7F7FFFFFul; // +Max Normal (float.MaxValue)
|
||||||
yield return 0x0080000000800000ul; // +Min Normal
|
yield return 0x0080000000800000ul; // +Min Normal
|
||||||
yield return 0x007FFFFF007FFFFFul; // +Max SubNormal
|
yield return 0x007FFFFF007FFFFFul; // +Max Subnormal
|
||||||
yield return 0x0000000100000001ul; // +Min SubNormal
|
yield return 0x0000000100000001ul; // +Min Subnormal (float.Epsilon)
|
||||||
|
|
||||||
if (!NoZeros)
|
if (!NoZeros)
|
||||||
{
|
{
|
||||||
|
@ -145,16 +145,16 @@ namespace Ryujinx.Tests.Cpu
|
||||||
|
|
||||||
if (!NoNaNs)
|
if (!NoNaNs)
|
||||||
{
|
{
|
||||||
yield return 0xFFFFFFFFFFFFFFFFul; // -QNaN (all ones payload)
|
yield return 0xFFC00000FFC00000ul; // -QNaN (all zeros payload) (float.NaN)
|
||||||
yield return 0xFFBFFFFFFFBFFFFFul; // -SNaN (all ones payload)
|
yield return 0xFFBFFFFFFFBFFFFFul; // -SNaN (all ones payload)
|
||||||
yield return 0x7FFFFFFF7FFFFFFFul; // +QNaN (all ones payload)
|
yield return 0x7FC000007FC00000ul; // +QNaN (all zeros payload) (-float.NaN) (DefaultNaN)
|
||||||
yield return 0x7FBFFFFF7FBFFFFFul; // +SNaN (all ones payload)
|
yield return 0x7FBFFFFF7FBFFFFFul; // +SNaN (all ones payload)
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int Cnt = 1; Cnt <= RndCnt; Cnt++)
|
for (int Cnt = 1; Cnt <= RndCnt; Cnt++)
|
||||||
{
|
{
|
||||||
ulong Rnd1 = GenNormal_S();
|
ulong Rnd1 = GenNormal_S();
|
||||||
ulong Rnd2 = GenSubNormal_S();
|
ulong Rnd2 = GenSubnormal_S();
|
||||||
|
|
||||||
yield return (Rnd1 << 32) | Rnd1;
|
yield return (Rnd1 << 32) | Rnd1;
|
||||||
yield return (Rnd2 << 32) | Rnd2;
|
yield return (Rnd2 << 32) | Rnd2;
|
||||||
|
@ -163,14 +163,14 @@ namespace Ryujinx.Tests.Cpu
|
||||||
|
|
||||||
private static IEnumerable<ulong> _1D_F_()
|
private static IEnumerable<ulong> _1D_F_()
|
||||||
{
|
{
|
||||||
yield return 0xFFEFFFFFFFFFFFFFul; // -Max Normal (double.MinValue)
|
yield return 0xFFEFFFFFFFFFFFFFul; // -Max Normal (double.MinValue)
|
||||||
yield return 0x8010000000000000ul; // -Min Normal
|
yield return 0x8010000000000000ul; // -Min Normal
|
||||||
yield return 0x800FFFFFFFFFFFFFul; // -Max SubNormal
|
yield return 0x800FFFFFFFFFFFFFul; // -Max Subnormal
|
||||||
yield return 0x8000000000000001ul; // -Min SubNormal
|
yield return 0x8000000000000001ul; // -Min Subnormal (-double.Epsilon)
|
||||||
yield return 0x7FEFFFFFFFFFFFFFul; // +Max Normal (double.MaxValue)
|
yield return 0x7FEFFFFFFFFFFFFFul; // +Max Normal (double.MaxValue)
|
||||||
yield return 0x0010000000000000ul; // +Min Normal
|
yield return 0x0010000000000000ul; // +Min Normal
|
||||||
yield return 0x000FFFFFFFFFFFFFul; // +Max SubNormal
|
yield return 0x000FFFFFFFFFFFFFul; // +Max Subnormal
|
||||||
yield return 0x0000000000000001ul; // +Min SubNormal
|
yield return 0x0000000000000001ul; // +Min Subnormal (double.Epsilon)
|
||||||
|
|
||||||
if (!NoZeros)
|
if (!NoZeros)
|
||||||
{
|
{
|
||||||
|
@ -186,16 +186,16 @@ namespace Ryujinx.Tests.Cpu
|
||||||
|
|
||||||
if (!NoNaNs)
|
if (!NoNaNs)
|
||||||
{
|
{
|
||||||
yield return 0xFFFFFFFFFFFFFFFFul; // -QNaN (all ones payload)
|
yield return 0xFFF8000000000000ul; // -QNaN (all zeros payload) (double.NaN)
|
||||||
yield return 0xFFF7FFFFFFFFFFFFul; // -SNaN (all ones payload)
|
yield return 0xFFF7FFFFFFFFFFFFul; // -SNaN (all ones payload)
|
||||||
yield return 0x7FFFFFFFFFFFFFFFul; // +QNaN (all ones payload)
|
yield return 0x7FF8000000000000ul; // +QNaN (all zeros payload) (-double.NaN) (DefaultNaN)
|
||||||
yield return 0x7FF7FFFFFFFFFFFFul; // +SNaN (all ones payload)
|
yield return 0x7FF7FFFFFFFFFFFFul; // +SNaN (all ones payload)
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int Cnt = 1; Cnt <= RndCnt; Cnt++)
|
for (int Cnt = 1; Cnt <= RndCnt; Cnt++)
|
||||||
{
|
{
|
||||||
ulong Rnd1 = GenNormal_D();
|
ulong Rnd1 = GenNormal_D();
|
||||||
ulong Rnd2 = GenSubNormal_D();
|
ulong Rnd2 = GenSubnormal_D();
|
||||||
|
|
||||||
yield return Rnd1;
|
yield return Rnd1;
|
||||||
yield return Rnd2;
|
yield return Rnd2;
|
||||||
|
@ -204,6 +204,72 @@ namespace Ryujinx.Tests.Cpu
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region "ValueSource (Opcodes)"
|
#region "ValueSource (Opcodes)"
|
||||||
|
private static uint[] _F_Add_Div_Mul_MulX_Sub_S_S_()
|
||||||
|
{
|
||||||
|
return new uint[]
|
||||||
|
{
|
||||||
|
0x1E222820u, // FADD S0, S1, S2
|
||||||
|
0x1E221820u, // FDIV S0, S1, S2
|
||||||
|
0x1E220820u, // FMUL S0, S1, S2
|
||||||
|
0x5E22DC20u, // FMULX S0, S1, S2
|
||||||
|
0x1E223820u // FSUB S0, S1, S2
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private static uint[] _F_Add_Div_Mul_MulX_Sub_S_D_()
|
||||||
|
{
|
||||||
|
return new uint[]
|
||||||
|
{
|
||||||
|
0x1E622820u, // FADD D0, D1, D2
|
||||||
|
0x1E621820u, // FDIV D0, D1, D2
|
||||||
|
0x1E620820u, // FMUL D0, D1, D2
|
||||||
|
0x5E62DC20u, // FMULX D0, D1, D2
|
||||||
|
0x1E623820u // FSUB D0, D1, D2
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private static uint[] _F_Add_Div_Mul_MulX_Sub_V_2S_4S_()
|
||||||
|
{
|
||||||
|
return new uint[]
|
||||||
|
{
|
||||||
|
0x0E20D400u, // FADD V0.2S, V0.2S, V0.2S
|
||||||
|
0x2E20FC00u, // FDIV V0.2S, V0.2S, V0.2S
|
||||||
|
0x2E20DC00u, // FMUL V0.2S, V0.2S, V0.2S
|
||||||
|
0x0E20DC00u, // FMULX V0.2S, V0.2S, V0.2S
|
||||||
|
0x0EA0D400u // FSUB V0.2S, V0.2S, V0.2S
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private static uint[] _F_Add_Div_Mul_MulX_Sub_V_2D_()
|
||||||
|
{
|
||||||
|
return new uint[]
|
||||||
|
{
|
||||||
|
0x4E60D400u, // FADD V0.2D, V0.2D, V0.2D
|
||||||
|
0x6E60FC00u, // FDIV V0.2D, V0.2D, V0.2D
|
||||||
|
0x6E60DC00u, // FMUL V0.2D, V0.2D, V0.2D
|
||||||
|
0x4E60DC00u, // FMULX V0.2D, V0.2D, V0.2D
|
||||||
|
0x4EE0D400u // FSUB V0.2D, V0.2D, V0.2D
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private static uint[] _Fmadd_Fmsub_S_S_()
|
||||||
|
{
|
||||||
|
return new uint[]
|
||||||
|
{
|
||||||
|
0x1F020C20u, // FMADD S0, S1, S2, S3
|
||||||
|
0x1F028C20u // FMSUB S0, S1, S2, S3
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private static uint[] _Fmadd_Fmsub_S_D_()
|
||||||
|
{
|
||||||
|
return new uint[]
|
||||||
|
{
|
||||||
|
0x1F420C20u, // FMADD D0, D1, D2, D3
|
||||||
|
0x1F428C20u // FMSUB D0, D1, D2, D3
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
private static uint[] _F_Max_Min_Nm_S_S_()
|
private static uint[] _F_Max_Min_Nm_S_S_()
|
||||||
{
|
{
|
||||||
return new uint[]
|
return new uint[]
|
||||||
|
@ -251,6 +317,42 @@ namespace Ryujinx.Tests.Cpu
|
||||||
0x6EE0F400u // FMINP V0.2D, V0.2D, V0.2D
|
0x6EE0F400u // FMINP V0.2D, V0.2D, V0.2D
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static uint[] _Frecps_Frsqrts_S_S_()
|
||||||
|
{
|
||||||
|
return new uint[]
|
||||||
|
{
|
||||||
|
0x5E22FC20u, // FRECPS S0, S1, S2
|
||||||
|
0x5EA2FC20u // FRSQRTS S0, S1, S2
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private static uint[] _Frecps_Frsqrts_S_D_()
|
||||||
|
{
|
||||||
|
return new uint[]
|
||||||
|
{
|
||||||
|
0x5E62FC20u, // FRECPS D0, D1, D2
|
||||||
|
0x5EE2FC20u // FRSQRTS D0, D1, D2
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private static uint[] _Frecps_Frsqrts_V_2S_4S_()
|
||||||
|
{
|
||||||
|
return new uint[]
|
||||||
|
{
|
||||||
|
0x0E20FC00u, // FRECPS V0.2S, V0.2S, V0.2S
|
||||||
|
0x0EA0FC00u // FRSQRTS V0.2S, V0.2S, V0.2S
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private static uint[] _Frecps_Frsqrts_V_2D_()
|
||||||
|
{
|
||||||
|
return new uint[]
|
||||||
|
{
|
||||||
|
0x4E60FC00u, // FRECPS V0.2D, V0.2D, V0.2D
|
||||||
|
0x4EE0FC00u // FRSQRTS V0.2D, V0.2D, V0.2D
|
||||||
|
};
|
||||||
|
}
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
private const int RndCnt = 2;
|
private const int RndCnt = 2;
|
||||||
|
@ -1035,46 +1137,122 @@ namespace Ryujinx.Tests.Cpu
|
||||||
CompareAgainstUnicorn();
|
CompareAgainstUnicorn();
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test, Pairwise, Description("FMADD <Sd>, <Sn>, <Sm>, <Sa>")]
|
[Test, Pairwise]
|
||||||
public void Fmadd_S_S([ValueSource("_1S_F_")] ulong A,
|
public void F_Add_Div_Mul_MulX_Sub_S_S([ValueSource("_F_Add_Div_Mul_MulX_Sub_S_S_")] uint Opcodes,
|
||||||
[ValueSource("_1S_F_")] ulong B,
|
[ValueSource("_1S_F_")] ulong A,
|
||||||
[ValueSource("_1S_F_")] ulong C)
|
[ValueSource("_1S_F_")] ulong B)
|
||||||
{
|
{
|
||||||
//const int DNFlagBit = 25; // Default NaN mode control bit.
|
ulong Z = TestContext.CurrentContext.Random.NextULong();
|
||||||
//const int FZFlagBit = 24; // Flush-to-zero mode control bit.
|
Vector128<float> V0 = MakeVectorE0E1(Z, Z);
|
||||||
|
Vector128<float> V1 = MakeVectorE0(A);
|
||||||
|
Vector128<float> V2 = MakeVectorE0(B);
|
||||||
|
|
||||||
uint Opcode = 0x1F020C20; // FMADD S0, S1, S2, S3
|
int Fpcr = (int)TestContext.CurrentContext.Random.NextUInt() & (1 << (int)FPCR.DN);
|
||||||
|
|
||||||
|
AThreadState ThreadState = SingleOpcode(Opcodes, V0: V0, V1: V1, V2: V2, Fpcr: Fpcr);
|
||||||
|
|
||||||
|
CompareAgainstUnicorn(FpsrMask: FPSR.IOC | FPSR.DZC);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test, Pairwise]
|
||||||
|
public void F_Add_Div_Mul_MulX_Sub_S_D([ValueSource("_F_Add_Div_Mul_MulX_Sub_S_D_")] uint Opcodes,
|
||||||
|
[ValueSource("_1D_F_")] ulong A,
|
||||||
|
[ValueSource("_1D_F_")] ulong B)
|
||||||
|
{
|
||||||
|
ulong Z = TestContext.CurrentContext.Random.NextULong();
|
||||||
|
Vector128<float> V0 = MakeVectorE1(Z);
|
||||||
|
Vector128<float> V1 = MakeVectorE0(A);
|
||||||
|
Vector128<float> V2 = MakeVectorE0(B);
|
||||||
|
|
||||||
|
int Fpcr = (int)TestContext.CurrentContext.Random.NextUInt() & (1 << (int)FPCR.DN);
|
||||||
|
|
||||||
|
AThreadState ThreadState = SingleOpcode(Opcodes, V0: V0, V1: V1, V2: V2, Fpcr: Fpcr);
|
||||||
|
|
||||||
|
CompareAgainstUnicorn(FpsrMask: FPSR.IOC | FPSR.DZC);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test, Pairwise]
|
||||||
|
public void F_Add_Div_Mul_MulX_Sub_V_2S_4S([ValueSource("_F_Add_Div_Mul_MulX_Sub_V_2S_4S_")] uint Opcodes,
|
||||||
|
[Values(0u)] uint Rd,
|
||||||
|
[Values(1u, 0u)] uint Rn,
|
||||||
|
[Values(2u, 0u)] uint Rm,
|
||||||
|
[ValueSource("_2S_F_")] ulong Z,
|
||||||
|
[ValueSource("_2S_F_")] ulong A,
|
||||||
|
[ValueSource("_2S_F_")] ulong B,
|
||||||
|
[Values(0b0u, 0b1u)] uint Q) // <2S, 4S>
|
||||||
|
{
|
||||||
|
Opcodes |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0);
|
||||||
|
Opcodes |= ((Q & 1) << 30);
|
||||||
|
|
||||||
|
Vector128<float> V0 = MakeVectorE0E1(Z, Z);
|
||||||
|
Vector128<float> V1 = MakeVectorE0E1(A, A * Q);
|
||||||
|
Vector128<float> V2 = MakeVectorE0E1(B, B * Q);
|
||||||
|
|
||||||
|
int Fpcr = (int)TestContext.CurrentContext.Random.NextUInt() & (1 << (int)FPCR.DN);
|
||||||
|
|
||||||
|
AThreadState ThreadState = SingleOpcode(Opcodes, V0: V0, V1: V1, V2: V2, Fpcr: Fpcr);
|
||||||
|
|
||||||
|
CompareAgainstUnicorn(FpsrMask: FPSR.IOC | FPSR.DZC);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test, Pairwise]
|
||||||
|
public void F_Add_Div_Mul_MulX_Sub_V_2D([ValueSource("_F_Add_Div_Mul_MulX_Sub_V_2D_")] uint Opcodes,
|
||||||
|
[Values(0u)] uint Rd,
|
||||||
|
[Values(1u, 0u)] uint Rn,
|
||||||
|
[Values(2u, 0u)] uint Rm,
|
||||||
|
[ValueSource("_1D_F_")] ulong Z,
|
||||||
|
[ValueSource("_1D_F_")] ulong A,
|
||||||
|
[ValueSource("_1D_F_")] ulong B)
|
||||||
|
{
|
||||||
|
Opcodes |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0);
|
||||||
|
|
||||||
|
Vector128<float> V0 = MakeVectorE0E1(Z, Z);
|
||||||
|
Vector128<float> V1 = MakeVectorE0E1(A, A);
|
||||||
|
Vector128<float> V2 = MakeVectorE0E1(B, B);
|
||||||
|
|
||||||
|
int Fpcr = (int)TestContext.CurrentContext.Random.NextUInt() & (1 << (int)FPCR.DN);
|
||||||
|
|
||||||
|
AThreadState ThreadState = SingleOpcode(Opcodes, V0: V0, V1: V1, V2: V2, Fpcr: Fpcr);
|
||||||
|
|
||||||
|
CompareAgainstUnicorn(FpsrMask: FPSR.IOC | FPSR.DZC);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test, Pairwise] // Fused.
|
||||||
|
public void Fmadd_Fmsub_S_S([ValueSource("_Fmadd_Fmsub_S_S_")] uint Opcodes,
|
||||||
|
[ValueSource("_1S_F_")] ulong A,
|
||||||
|
[ValueSource("_1S_F_")] ulong B,
|
||||||
|
[ValueSource("_1S_F_")] ulong C)
|
||||||
|
{
|
||||||
ulong Z = TestContext.CurrentContext.Random.NextULong();
|
ulong Z = TestContext.CurrentContext.Random.NextULong();
|
||||||
Vector128<float> V0 = MakeVectorE0E1(Z, Z);
|
Vector128<float> V0 = MakeVectorE0E1(Z, Z);
|
||||||
Vector128<float> V1 = MakeVectorE0(A);
|
Vector128<float> V1 = MakeVectorE0(A);
|
||||||
Vector128<float> V2 = MakeVectorE0(B);
|
Vector128<float> V2 = MakeVectorE0(B);
|
||||||
Vector128<float> V3 = MakeVectorE0(C);
|
Vector128<float> V3 = MakeVectorE0(C);
|
||||||
|
|
||||||
//int Fpcr = 1 << DNFlagBit; // Any operation involving one or more NaNs returns the Default NaN.
|
int Fpcr = (int)TestContext.CurrentContext.Random.NextUInt() & (1 << (int)FPCR.DN);
|
||||||
//Fpcr |= 1 << FZFlagBit; // Flush-to-zero mode enabled.
|
|
||||||
|
|
||||||
AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2, V3: V3/*, Fpcr: Fpcr*/);
|
AThreadState ThreadState = SingleOpcode(Opcodes, V0: V0, V1: V1, V2: V2, V3: V3, Fpcr: Fpcr);
|
||||||
|
|
||||||
CompareAgainstUnicorn(/*FpsrMask: FPSR.IDC | FPSR.IOC, */FpSkips: FpSkips.IfNaN_S/*, FpUseTolerance: FpUseTolerance.OneUlps_S*/);
|
CompareAgainstUnicorn(FPSR.IOC, FpSkips.IfUnderflow, FpTolerances.UpToOneUlps_S);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test, Pairwise, Description("FMADD <Dd>, <Dn>, <Dm>, <Da>")]
|
[Test, Pairwise] // Fused.
|
||||||
public void Fmadd_S_D([ValueSource("_1D_F_")] ulong A,
|
public void Fmadd_Fmsub_S_D([ValueSource("_Fmadd_Fmsub_S_D_")] uint Opcodes,
|
||||||
[ValueSource("_1D_F_")] ulong B,
|
[ValueSource("_1D_F_")] ulong A,
|
||||||
[ValueSource("_1D_F_")] ulong C)
|
[ValueSource("_1D_F_")] ulong B,
|
||||||
|
[ValueSource("_1D_F_")] ulong C)
|
||||||
{
|
{
|
||||||
uint Opcode = 0x1F420C20; // FMADD D0, D1, D2, D3
|
|
||||||
|
|
||||||
ulong Z = TestContext.CurrentContext.Random.NextULong();
|
ulong Z = TestContext.CurrentContext.Random.NextULong();
|
||||||
Vector128<float> V0 = MakeVectorE1(Z);
|
Vector128<float> V0 = MakeVectorE1(Z);
|
||||||
Vector128<float> V1 = MakeVectorE0(A);
|
Vector128<float> V1 = MakeVectorE0(A);
|
||||||
Vector128<float> V2 = MakeVectorE0(B);
|
Vector128<float> V2 = MakeVectorE0(B);
|
||||||
Vector128<float> V3 = MakeVectorE0(C);
|
Vector128<float> V3 = MakeVectorE0(C);
|
||||||
|
|
||||||
AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2, V3: V3);
|
int Fpcr = (int)TestContext.CurrentContext.Random.NextUInt() & (1 << (int)FPCR.DN);
|
||||||
|
|
||||||
CompareAgainstUnicorn(FpSkips: FpSkips.IfNaN_D/*, FpUseTolerance: FpUseTolerance.OneUlps_D*/);
|
AThreadState ThreadState = SingleOpcode(Opcodes, V0: V0, V1: V1, V2: V2, V3: V3, Fpcr: Fpcr);
|
||||||
|
|
||||||
|
CompareAgainstUnicorn(FPSR.IOC, FpSkips.IfUnderflow, FpTolerances.UpToOneUlps_D);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test, Pairwise]
|
[Test, Pairwise]
|
||||||
|
@ -1082,20 +1260,16 @@ namespace Ryujinx.Tests.Cpu
|
||||||
[ValueSource("_1S_F_")] ulong A,
|
[ValueSource("_1S_F_")] ulong A,
|
||||||
[ValueSource("_1S_F_")] ulong B)
|
[ValueSource("_1S_F_")] ulong B)
|
||||||
{
|
{
|
||||||
//const int DNFlagBit = 25; // Default NaN mode control bit.
|
|
||||||
//const int FZFlagBit = 24; // Flush-to-zero mode control bit.
|
|
||||||
|
|
||||||
ulong Z = TestContext.CurrentContext.Random.NextULong();
|
ulong Z = TestContext.CurrentContext.Random.NextULong();
|
||||||
Vector128<float> V0 = MakeVectorE0E1(Z, Z);
|
Vector128<float> V0 = MakeVectorE0E1(Z, Z);
|
||||||
Vector128<float> V1 = MakeVectorE0(A);
|
Vector128<float> V1 = MakeVectorE0(A);
|
||||||
Vector128<float> V2 = MakeVectorE0(B);
|
Vector128<float> V2 = MakeVectorE0(B);
|
||||||
|
|
||||||
//int Fpcr = 1 << DNFlagBit; // Any operation involving one or more NaNs returns the Default NaN.
|
int Fpcr = (int)TestContext.CurrentContext.Random.NextUInt() & (1 << (int)FPCR.DN);
|
||||||
//Fpcr |= 1 << FZFlagBit; // Flush-to-zero mode enabled.
|
|
||||||
|
|
||||||
AThreadState ThreadState = SingleOpcode(Opcodes, V0: V0, V1: V1, V2: V2/*, Fpcr: Fpcr*/);
|
AThreadState ThreadState = SingleOpcode(Opcodes, V0: V0, V1: V1, V2: V2, Fpcr: Fpcr);
|
||||||
|
|
||||||
CompareAgainstUnicorn(/*FpsrMask: FPSR.IDC | FPSR.IOC*/);
|
CompareAgainstUnicorn(FpsrMask: FPSR.IOC);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test, Pairwise]
|
[Test, Pairwise]
|
||||||
|
@ -1108,9 +1282,11 @@ namespace Ryujinx.Tests.Cpu
|
||||||
Vector128<float> V1 = MakeVectorE0(A);
|
Vector128<float> V1 = MakeVectorE0(A);
|
||||||
Vector128<float> V2 = MakeVectorE0(B);
|
Vector128<float> V2 = MakeVectorE0(B);
|
||||||
|
|
||||||
AThreadState ThreadState = SingleOpcode(Opcodes, V0: V0, V1: V1, V2: V2);
|
int Fpcr = (int)TestContext.CurrentContext.Random.NextUInt() & (1 << (int)FPCR.DN);
|
||||||
|
|
||||||
CompareAgainstUnicorn();
|
AThreadState ThreadState = SingleOpcode(Opcodes, V0: V0, V1: V1, V2: V2, Fpcr: Fpcr);
|
||||||
|
|
||||||
|
CompareAgainstUnicorn(FpsrMask: FPSR.IOC);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test, Pairwise]
|
[Test, Pairwise]
|
||||||
|
@ -1123,9 +1299,6 @@ namespace Ryujinx.Tests.Cpu
|
||||||
[ValueSource("_2S_F_")] ulong B,
|
[ValueSource("_2S_F_")] ulong B,
|
||||||
[Values(0b0u, 0b1u)] uint Q) // <2S, 4S>
|
[Values(0b0u, 0b1u)] uint Q) // <2S, 4S>
|
||||||
{
|
{
|
||||||
//const int DNFlagBit = 25; // Default NaN mode control bit.
|
|
||||||
//const int FZFlagBit = 24; // Flush-to-zero mode control bit.
|
|
||||||
|
|
||||||
Opcodes |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0);
|
Opcodes |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0);
|
||||||
Opcodes |= ((Q & 1) << 30);
|
Opcodes |= ((Q & 1) << 30);
|
||||||
|
|
||||||
|
@ -1133,12 +1306,11 @@ namespace Ryujinx.Tests.Cpu
|
||||||
Vector128<float> V1 = MakeVectorE0E1(A, A * Q);
|
Vector128<float> V1 = MakeVectorE0E1(A, A * Q);
|
||||||
Vector128<float> V2 = MakeVectorE0E1(B, B * Q);
|
Vector128<float> V2 = MakeVectorE0E1(B, B * Q);
|
||||||
|
|
||||||
//int Fpcr = 1 << DNFlagBit; // Any operation involving one or more NaNs returns the Default NaN.
|
int Fpcr = (int)TestContext.CurrentContext.Random.NextUInt() & (1 << (int)FPCR.DN);
|
||||||
//Fpcr |= 1 << FZFlagBit; // Flush-to-zero mode enabled.
|
|
||||||
|
|
||||||
AThreadState ThreadState = SingleOpcode(Opcodes, V0: V0, V1: V1, V2: V2/*, Fpcr: Fpcr*/);
|
AThreadState ThreadState = SingleOpcode(Opcodes, V0: V0, V1: V1, V2: V2, Fpcr: Fpcr);
|
||||||
|
|
||||||
CompareAgainstUnicorn(/*FpsrMask: FPSR.IDC | FPSR.IOC*/);
|
CompareAgainstUnicorn(FpsrMask: FPSR.IOC);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test, Pairwise]
|
[Test, Pairwise]
|
||||||
|
@ -1156,9 +1328,91 @@ namespace Ryujinx.Tests.Cpu
|
||||||
Vector128<float> V1 = MakeVectorE0E1(A, A);
|
Vector128<float> V1 = MakeVectorE0E1(A, A);
|
||||||
Vector128<float> V2 = MakeVectorE0E1(B, B);
|
Vector128<float> V2 = MakeVectorE0E1(B, B);
|
||||||
|
|
||||||
AThreadState ThreadState = SingleOpcode(Opcodes, V0: V0, V1: V1, V2: V2);
|
int Fpcr = (int)TestContext.CurrentContext.Random.NextUInt() & (1 << (int)FPCR.DN);
|
||||||
|
|
||||||
CompareAgainstUnicorn();
|
AThreadState ThreadState = SingleOpcode(Opcodes, V0: V0, V1: V1, V2: V2, Fpcr: Fpcr);
|
||||||
|
|
||||||
|
CompareAgainstUnicorn(FpsrMask: FPSR.IOC);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test, Pairwise] // Fused.
|
||||||
|
public void Frecps_Frsqrts_S_S([ValueSource("_Frecps_Frsqrts_S_S_")] uint Opcodes,
|
||||||
|
[ValueSource("_1S_F_")] ulong A,
|
||||||
|
[ValueSource("_1S_F_")] ulong B)
|
||||||
|
{
|
||||||
|
ulong Z = TestContext.CurrentContext.Random.NextULong();
|
||||||
|
Vector128<float> V0 = MakeVectorE0E1(Z, Z);
|
||||||
|
Vector128<float> V1 = MakeVectorE0(A);
|
||||||
|
Vector128<float> V2 = MakeVectorE0(B);
|
||||||
|
|
||||||
|
int Fpcr = (int)TestContext.CurrentContext.Random.NextUInt() & (1 << (int)FPCR.DN);
|
||||||
|
|
||||||
|
AThreadState ThreadState = SingleOpcode(Opcodes, V0: V0, V1: V1, V2: V2, Fpcr: Fpcr);
|
||||||
|
|
||||||
|
CompareAgainstUnicorn(FPSR.IOC, FpSkips.IfUnderflow, FpTolerances.UpToOneUlps_S);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test, Pairwise] // Fused.
|
||||||
|
public void Frecps_Frsqrts_S_D([ValueSource("_Frecps_Frsqrts_S_D_")] uint Opcodes,
|
||||||
|
[ValueSource("_1D_F_")] ulong A,
|
||||||
|
[ValueSource("_1D_F_")] ulong B)
|
||||||
|
{
|
||||||
|
ulong Z = TestContext.CurrentContext.Random.NextULong();
|
||||||
|
Vector128<float> V0 = MakeVectorE1(Z);
|
||||||
|
Vector128<float> V1 = MakeVectorE0(A);
|
||||||
|
Vector128<float> V2 = MakeVectorE0(B);
|
||||||
|
|
||||||
|
int Fpcr = (int)TestContext.CurrentContext.Random.NextUInt() & (1 << (int)FPCR.DN);
|
||||||
|
|
||||||
|
AThreadState ThreadState = SingleOpcode(Opcodes, V0: V0, V1: V1, V2: V2, Fpcr: Fpcr);
|
||||||
|
|
||||||
|
CompareAgainstUnicorn(FPSR.IOC, FpSkips.IfUnderflow, FpTolerances.UpToOneUlps_D);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test, Pairwise] // Fused.
|
||||||
|
public void Frecps_Frsqrts_V_2S_4S([ValueSource("_Frecps_Frsqrts_V_2S_4S_")] uint Opcodes,
|
||||||
|
[Values(0u)] uint Rd,
|
||||||
|
[Values(1u, 0u)] uint Rn,
|
||||||
|
[Values(2u, 0u)] uint Rm,
|
||||||
|
[ValueSource("_2S_F_")] ulong Z,
|
||||||
|
[ValueSource("_2S_F_")] ulong A,
|
||||||
|
[ValueSource("_2S_F_")] ulong B,
|
||||||
|
[Values(0b0u, 0b1u)] uint Q) // <2S, 4S>
|
||||||
|
{
|
||||||
|
Opcodes |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0);
|
||||||
|
Opcodes |= ((Q & 1) << 30);
|
||||||
|
|
||||||
|
Vector128<float> V0 = MakeVectorE0E1(Z, Z);
|
||||||
|
Vector128<float> V1 = MakeVectorE0E1(A, A * Q);
|
||||||
|
Vector128<float> V2 = MakeVectorE0E1(B, B * Q);
|
||||||
|
|
||||||
|
int Fpcr = (int)TestContext.CurrentContext.Random.NextUInt() & (1 << (int)FPCR.DN);
|
||||||
|
|
||||||
|
AThreadState ThreadState = SingleOpcode(Opcodes, V0: V0, V1: V1, V2: V2, Fpcr: Fpcr);
|
||||||
|
|
||||||
|
CompareAgainstUnicorn(FPSR.IOC, FpSkips.IfUnderflow, FpTolerances.UpToOneUlps_S);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test, Pairwise] // Fused.
|
||||||
|
public void Frecps_Frsqrts_V_2D([ValueSource("_Frecps_Frsqrts_V_2D_")] uint Opcodes,
|
||||||
|
[Values(0u)] uint Rd,
|
||||||
|
[Values(1u, 0u)] uint Rn,
|
||||||
|
[Values(2u, 0u)] uint Rm,
|
||||||
|
[ValueSource("_1D_F_")] ulong Z,
|
||||||
|
[ValueSource("_1D_F_")] ulong A,
|
||||||
|
[ValueSource("_1D_F_")] ulong B)
|
||||||
|
{
|
||||||
|
Opcodes |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0);
|
||||||
|
|
||||||
|
Vector128<float> V0 = MakeVectorE0E1(Z, Z);
|
||||||
|
Vector128<float> V1 = MakeVectorE0E1(A, A);
|
||||||
|
Vector128<float> V2 = MakeVectorE0E1(B, B);
|
||||||
|
|
||||||
|
int Fpcr = (int)TestContext.CurrentContext.Random.NextUInt() & (1 << (int)FPCR.DN);
|
||||||
|
|
||||||
|
AThreadState ThreadState = SingleOpcode(Opcodes, V0: V0, V1: V1, V2: V2, Fpcr: Fpcr);
|
||||||
|
|
||||||
|
CompareAgainstUnicorn(FPSR.IOC, FpSkips.IfUnderflow, FpTolerances.UpToOneUlps_D);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test, Pairwise, Description("ORN <Vd>.<T>, <Vn>.<T>, <Vm>.<T>")]
|
[Test, Pairwise, Description("ORN <Vd>.<T>, <Vn>.<T>, <Vm>.<T>")]
|
||||||
|
|
Loading…
Reference in a new issue