From ffbfbb554965651194a58ca92a3a5db195cdc7ed Mon Sep 17 00:00:00 2001 From: LDj3SNuD <35856442+LDj3SNuD@users.noreply.github.com> Date: Fri, 31 May 2019 00:51:39 +0200 Subject: [PATCH] Add FCVT , and FCVT , Inst.; add Tests. (#692) * Update OpCodeTable.cs * Update InstEmitSimdCvt.cs * Update CpuTestSimd.cs * Address PR feedback. --- ChocolArm64/Instructions/InstEmitSimdCvt.cs | 80 ++++++++++++++----- ChocolArm64/OpCodeTable.cs | 2 +- Ryujinx.Tests/Cpu/CpuTestSimd.cs | 88 ++++++++++++++++++++- 3 files changed, 146 insertions(+), 24 deletions(-) diff --git a/ChocolArm64/Instructions/InstEmitSimdCvt.cs b/ChocolArm64/Instructions/InstEmitSimdCvt.cs index b34687e1c3..2b3deb98e0 100644 --- a/ChocolArm64/Instructions/InstEmitSimdCvt.cs +++ b/ChocolArm64/Instructions/InstEmitSimdCvt.cs @@ -16,23 +16,10 @@ namespace ChocolArm64.Instructions { 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), typeof(Vector128) }; - - 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), typeof(Vector128) }; VectorHelper.EmitCall(context, nameof(VectorHelper.VectorSingleZero)); @@ -44,17 +31,68 @@ namespace ChocolArm64.Instructions } else { - //Invalid encoding. - throw new InvalidOperationException(); + EmitVectorExtractF(context, op.Rn, 0, 0); + + 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), typeof(Vector128) }; - 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}"); } } diff --git a/ChocolArm64/OpCodeTable.cs b/ChocolArm64/OpCodeTable.cs index a1bbd4bc0f..2200faefd1 100644 --- a/ChocolArm64/OpCodeTable.cs +++ b/ChocolArm64/OpCodeTable.cs @@ -296,7 +296,7 @@ namespace ChocolArm64 SetA64("000111100x1xxxxx001000xxxxx0x000", InstEmit.Fcmp_S, typeof(OpCodeSimdReg64)); SetA64("000111100x1xxxxx001000xxxxx1x000", InstEmit.Fcmpe_S, typeof(OpCodeSimdReg64)); 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("x00111100x100101000000xxxxxxxxxx", InstEmit.Fcvtau_Gp, typeof(OpCodeSimdCvt64)); SetA64("0x0011100x100001011110xxxxxxxxxx", InstEmit.Fcvtl_V, typeof(OpCodeSimd64)); diff --git a/Ryujinx.Tests/Cpu/CpuTestSimd.cs b/Ryujinx.Tests/Cpu/CpuTestSimd.cs index d6bb7318a6..6125344c56 100644 --- a/Ryujinx.Tests/Cpu/CpuTestSimd.cs +++ b/Ryujinx.Tests/Cpu/CpuTestSimd.cs @@ -90,6 +90,48 @@ namespace Ryujinx.Tests.Cpu 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul }; } + private static IEnumerable _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 _4H_F_() { yield return 0xFBFFFBFFFBFFFBFFul; // -Max Normal @@ -123,8 +165,8 @@ namespace Ryujinx.Tests.Cpu for (int cnt = 1; cnt <= RndCnt; cnt++) { - uint rnd1 = (uint)GenNormalH(); - uint rnd2 = (uint)GenSubnormalH(); + ulong rnd1 = GenNormalH(); + ulong rnd2 = GenSubnormalH(); yield return (rnd1 << 48) | (rnd1 << 32) | (rnd1 << 16) | rnd1; 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_() { return new uint[] @@ -1614,6 +1672,32 @@ namespace Ryujinx.Tests.Cpu 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 v0 = MakeVectorE0E1(z, z); + Vector128 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 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0(a); + + SingleOpcode(opcodes, v0: v0, v1: v1); + + CompareAgainstUnicorn(); + } + [Test, Pairwise] [Explicit] public void F_Cvt_NZ_SU_S_S([ValueSource("_F_Cvt_NZ_SU_S_S_")] uint opcodes, [ValueSource("_1S_F_W_")] ulong a)