Add FCVT <Hd>, <Sn> and FCVT <Sd>, <Hn> Inst.; add Tests. (#692)

* Update OpCodeTable.cs

* Update InstEmitSimdCvt.cs

* Update CpuTestSimd.cs

* Address PR feedback.
This commit is contained in:
LDj3SNuD 2019-05-31 00:51:39 +02:00 committed by gdkchan
parent e7be60b6c6
commit ffbfbb5549
3 changed files with 146 additions and 24 deletions

View file

@ -16,23 +16,10 @@ namespace ChocolArm64.Instructions
{ {
OpCodeSimd64 op = (OpCodeSimd64)context.CurrOp; OpCodeSimd64 op = (OpCodeSimd64)context.CurrOp;
if (Optimizations.UseSse2) if (op.Size == 0 && op.Opc == 1) // Single -> Double.
{ {
if (op.Size == 1 && op.Opc == 0) if (Optimizations.UseSse2)
{ {
//Double -> Single.
Type[] typesCvt = new Type[] { typeof(Vector128<float>), typeof(Vector128<double>) };
VectorHelper.EmitCall(context, nameof(VectorHelper.VectorSingleZero));
context.EmitLdvec(op.Rn);
context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ConvertScalarToVector128Single), typesCvt));
context.EmitStvec(op.Rd);
}
else if (op.Size == 0 && op.Opc == 1)
{
//Single -> Double.
Type[] typesCvt = new Type[] { typeof(Vector128<double>), typeof(Vector128<float>) }; Type[] typesCvt = new Type[] { typeof(Vector128<double>), typeof(Vector128<float>) };
VectorHelper.EmitCall(context, nameof(VectorHelper.VectorSingleZero)); VectorHelper.EmitCall(context, nameof(VectorHelper.VectorSingleZero));
@ -44,17 +31,68 @@ namespace ChocolArm64.Instructions
} }
else else
{ {
//Invalid encoding. EmitVectorExtractF(context, op.Rn, 0, 0);
throw new InvalidOperationException();
EmitFloatCast(context, 1);
EmitScalarSetF(context, op.Rd, 1);
} }
} }
else else if (op.Size == 1 && op.Opc == 0) // Double -> Single.
{ {
EmitVectorExtractF(context, op.Rn, 0, op.Size); if (Optimizations.UseSse2)
{
Type[] typesCvt = new Type[] { typeof(Vector128<float>), typeof(Vector128<double>) };
EmitFloatCast(context, op.Opc); VectorHelper.EmitCall(context, nameof(VectorHelper.VectorSingleZero));
context.EmitLdvec(op.Rn);
EmitScalarSetF(context, op.Rd, op.Opc); context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ConvertScalarToVector128Single), typesCvt));
context.EmitStvec(op.Rd);
}
else
{
EmitVectorExtractF(context, op.Rn, 0, 1);
EmitFloatCast(context, 0);
EmitScalarSetF(context, op.Rd, 0);
}
}
else if (op.Size == 0 && op.Opc == 3) // Single -> Half.
{
EmitVectorExtractF(context, op.Rn, 0, 0);
context.EmitLdarg(TranslatedSub.StateArgIdx);
context.EmitCall(typeof(SoftFloat32_16), nameof(SoftFloat32_16.FPConvert));
context.Emit(OpCodes.Conv_U8);
EmitScalarSet(context, op.Rd, 1);
}
else if (op.Size == 3 && op.Opc == 0) // Half -> Single.
{
EmitVectorExtractZx(context, op.Rn, 0, 1);
context.Emit(OpCodes.Conv_U2);
context.EmitLdarg(TranslatedSub.StateArgIdx);
context.EmitCall(typeof(SoftFloat16_32), nameof(SoftFloat16_32.FPConvert));
EmitScalarSetF(context, op.Rd, 0);
}
else if (op.Size == 1 && op.Opc == 3) // Double -> Half.
{
throw new NotImplementedException("Double-precision to half-precision.");
}
else if (op.Size == 3 && op.Opc == 1) // Double -> Half.
{
throw new NotImplementedException("Half-precision to double-precision.");
}
else // Invalid encoding.
{
throw new InvalidOperationException($"type == {op.Size} && opc == {op.Opc}");
} }
} }

View file

@ -296,7 +296,7 @@ namespace ChocolArm64
SetA64("000111100x1xxxxx001000xxxxx0x000", InstEmit.Fcmp_S, typeof(OpCodeSimdReg64)); SetA64("000111100x1xxxxx001000xxxxx0x000", InstEmit.Fcmp_S, typeof(OpCodeSimdReg64));
SetA64("000111100x1xxxxx001000xxxxx1x000", InstEmit.Fcmpe_S, typeof(OpCodeSimdReg64)); SetA64("000111100x1xxxxx001000xxxxx1x000", InstEmit.Fcmpe_S, typeof(OpCodeSimdReg64));
SetA64("000111100x1xxxxxxxxx11xxxxxxxxxx", InstEmit.Fcsel_S, typeof(OpCodeSimdFcond64)); SetA64("000111100x1xxxxxxxxx11xxxxxxxxxx", InstEmit.Fcsel_S, typeof(OpCodeSimdFcond64));
SetA64("000111100x10001xx10000xxxxxxxxxx", InstEmit.Fcvt_S, typeof(OpCodeSimd64)); SetA64("00011110xx10001xx10000xxxxxxxxxx", InstEmit.Fcvt_S, typeof(OpCodeSimd64));
SetA64("x00111100x100100000000xxxxxxxxxx", InstEmit.Fcvtas_Gp, typeof(OpCodeSimdCvt64)); SetA64("x00111100x100100000000xxxxxxxxxx", InstEmit.Fcvtas_Gp, typeof(OpCodeSimdCvt64));
SetA64("x00111100x100101000000xxxxxxxxxx", InstEmit.Fcvtau_Gp, typeof(OpCodeSimdCvt64)); SetA64("x00111100x100101000000xxxxxxxxxx", InstEmit.Fcvtau_Gp, typeof(OpCodeSimdCvt64));
SetA64("0x0011100x100001011110xxxxxxxxxx", InstEmit.Fcvtl_V, typeof(OpCodeSimd64)); SetA64("0x0011100x100001011110xxxxxxxxxx", InstEmit.Fcvtl_V, typeof(OpCodeSimd64));

View file

@ -90,6 +90,48 @@ namespace Ryujinx.Tests.Cpu
0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul }; 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul };
} }
private static IEnumerable<ulong> _1H_F_()
{
yield return 0x000000000000FBFFul; // -Max Normal
yield return 0x0000000000008400ul; // -Min Normal
yield return 0x00000000000083FFul; // -Max Subnormal
yield return 0x0000000000008001ul; // -Min Subnormal
yield return 0x0000000000007BFFul; // +Max Normal
yield return 0x0000000000000400ul; // +Min Normal
yield return 0x00000000000003FFul; // +Max Subnormal
yield return 0x0000000000000001ul; // +Min Subnormal
if (!NoZeros)
{
yield return 0x0000000000008000ul; // -Zero
yield return 0x0000000000000000ul; // +Zero
}
if (!NoInfs)
{
yield return 0x000000000000FC00ul; // -Infinity
yield return 0x0000000000007C00ul; // +Infinity
}
if (!NoNaNs)
{
yield return 0x000000000000FE00ul; // -QNaN (all zeros payload)
yield return 0x000000000000FDFFul; // -SNaN (all ones payload)
yield return 0x0000000000007E00ul; // +QNaN (all zeros payload) (DefaultNaN)
yield return 0x0000000000007DFFul; // +SNaN (all ones payload)
}
for (int cnt = 1; cnt <= RndCnt; cnt++)
{
ulong grbg = TestContext.CurrentContext.Random.NextUShort();
ulong rnd1 = GenNormalH();
ulong rnd2 = GenSubnormalH();
yield return (grbg << 48) | (grbg << 32) | (grbg << 16) | rnd1;
yield return (grbg << 48) | (grbg << 32) | (grbg << 16) | rnd2;
}
}
private static IEnumerable<ulong> _4H_F_() private static IEnumerable<ulong> _4H_F_()
{ {
yield return 0xFBFFFBFFFBFFFBFFul; // -Max Normal yield return 0xFBFFFBFFFBFFFBFFul; // -Max Normal
@ -123,8 +165,8 @@ namespace Ryujinx.Tests.Cpu
for (int cnt = 1; cnt <= RndCnt; cnt++) for (int cnt = 1; cnt <= RndCnt; cnt++)
{ {
uint rnd1 = (uint)GenNormalH(); ulong rnd1 = GenNormalH();
uint rnd2 = (uint)GenSubnormalH(); ulong rnd2 = GenSubnormalH();
yield return (rnd1 << 48) | (rnd1 << 32) | (rnd1 << 16) | rnd1; yield return (rnd1 << 48) | (rnd1 << 32) | (rnd1 << 16) | rnd1;
yield return (rnd2 << 48) | (rnd2 << 32) | (rnd2 << 16) | rnd2; yield return (rnd2 << 48) | (rnd2 << 32) | (rnd2 << 16) | rnd2;
@ -609,6 +651,22 @@ namespace Ryujinx.Tests.Cpu
}; };
} }
private static uint[] _F_Cvt_S_SH_()
{
return new uint[]
{
0x1E23C020u // FCVT H0, S1
};
}
private static uint[] _F_Cvt_S_HS_()
{
return new uint[]
{
0x1EE24020u // FCVT S0, H1
};
}
private static uint[] _F_Cvt_NZ_SU_S_S_() private static uint[] _F_Cvt_NZ_SU_S_S_()
{ {
return new uint[] return new uint[]
@ -1614,6 +1672,32 @@ namespace Ryujinx.Tests.Cpu
CompareAgainstUnicorn(); CompareAgainstUnicorn();
} }
[Test, Pairwise] [Explicit]
public void F_Cvt_S_SH([ValueSource("_F_Cvt_S_SH_")] uint opcodes,
[ValueSource("_1S_F_")] ulong a)
{
ulong z = TestContext.CurrentContext.Random.NextULong();
Vector128<float> v0 = MakeVectorE0E1(z, z);
Vector128<float> v1 = MakeVectorE0(a);
SingleOpcode(opcodes, v0: v0, v1: v1);
CompareAgainstUnicorn();
}
[Test, Pairwise] [Explicit]
public void F_Cvt_S_HS([ValueSource("_F_Cvt_S_HS_")] uint opcodes,
[ValueSource("_1H_F_")] ulong a)
{
ulong z = TestContext.CurrentContext.Random.NextULong();
Vector128<float> v0 = MakeVectorE0E1(z, z);
Vector128<float> v1 = MakeVectorE0(a);
SingleOpcode(opcodes, v0: v0, v1: v1);
CompareAgainstUnicorn();
}
[Test, Pairwise] [Explicit] [Test, Pairwise] [Explicit]
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_W_")] ulong a) [ValueSource("_1S_F_W_")] ulong a)