From 905c9bc96c484b8ab561e016e1ed0679fec8370a Mon Sep 17 00:00:00 2001 From: MerryMage Date: Sun, 8 Jul 2018 01:17:06 +0100 Subject: [PATCH] ChocolArm64: Handle infinities and zeros in Frecps --- .../Instruction/AInstEmitSimdArithmetic.cs | 51 +++---------------- .../Instruction/AInstEmitSimdHelper.cs | 20 ++++++++ ChocolArm64/Instruction/ASoftFloat.cs | 35 +++++++++++++ Ryujinx.Tests/Cpu/CpuTestSimdArithmetic.cs | 9 ++-- 4 files changed, 67 insertions(+), 48 deletions(-) diff --git a/ChocolArm64/Instruction/AInstEmitSimdArithmetic.cs b/ChocolArm64/Instruction/AInstEmitSimdArithmetic.cs index 425aa59bce..39331f965c 100644 --- a/ChocolArm64/Instruction/AInstEmitSimdArithmetic.cs +++ b/ChocolArm64/Instruction/AInstEmitSimdArithmetic.cs @@ -657,55 +657,18 @@ namespace ChocolArm64.Instruction public static void Frecps_S(AILEmitterCtx Context) { - EmitFrecps(Context, 0, Scalar: true); + EmitScalarBinaryOpF(Context, () => + { + EmitBinarySoftFloatCall(Context, nameof(ASoftFloat.RecipStep)); + }); } public static void Frecps_V(AILEmitterCtx Context) { - AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp; - - int SizeF = Op.Size & 1; - - int Bytes = Context.CurrOp.GetBitsCount() >> 3; - - for (int Index = 0; Index < Bytes >> SizeF + 2; Index++) + EmitVectorBinaryOpF(Context, () => { - EmitFrecps(Context, Index, Scalar: false); - } - - if (Op.RegisterSize == ARegisterSize.SIMD64) - { - EmitVectorZeroUpper(Context, Op.Rd); - } - } - - private static void EmitFrecps(AILEmitterCtx Context, int Index, bool Scalar) - { - AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp; - - int SizeF = Op.Size & 1; - - if (SizeF == 0) - { - Context.EmitLdc_R4(2); - } - else /* if (SizeF == 1) */ - { - Context.EmitLdc_R8(2); - } - - EmitVectorExtractF(Context, Op.Rn, Index, SizeF); - EmitVectorExtractF(Context, Op.Rm, Index, SizeF); - - Context.Emit(OpCodes.Mul); - Context.Emit(OpCodes.Sub); - - if (Scalar) - { - EmitVectorZeroAll(Context, Op.Rd); - } - - EmitVectorInsertF(Context, Op.Rd, Index, SizeF); + EmitBinarySoftFloatCall(Context, nameof(ASoftFloat.RecipStep)); + }); } public static void Frinta_S(AILEmitterCtx Context) diff --git a/ChocolArm64/Instruction/AInstEmitSimdHelper.cs b/ChocolArm64/Instruction/AInstEmitSimdHelper.cs index 0f6ea42cec..d895ec9c7c 100644 --- a/ChocolArm64/Instruction/AInstEmitSimdHelper.cs +++ b/ChocolArm64/Instruction/AInstEmitSimdHelper.cs @@ -253,6 +253,26 @@ namespace ChocolArm64.Instruction Context.EmitCall(MthdInfo); } + public static void EmitBinarySoftFloatCall(AILEmitterCtx Context, string Name) + { + IAOpCodeSimd Op = (IAOpCodeSimd)Context.CurrOp; + + int SizeF = Op.Size & 1; + + MethodInfo MthdInfo; + + if (SizeF == 0) + { + 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) { AOpCodeSimdRegElemF Op = (AOpCodeSimdRegElemF)Context.CurrOp; diff --git a/ChocolArm64/Instruction/ASoftFloat.cs b/ChocolArm64/Instruction/ASoftFloat.cs index d9661bbe32..42a676301c 100644 --- a/ChocolArm64/Instruction/ASoftFloat.cs +++ b/ChocolArm64/Instruction/ASoftFloat.cs @@ -184,5 +184,40 @@ namespace ChocolArm64.Instruction ulong result = x_sign | (result_exp << 52) | fraction; return BitConverter.Int64BitsToDouble((long)result); } + + public static float RecipStep(float op1, float op2) + { + return (float)RecipStep((double)op1, (double)op2); + } + + public static double RecipStep(double op1, double op2) + { + op1 = -op1; + + ulong op1_bits = (ulong)BitConverter.DoubleToInt64Bits(op1); + ulong op2_bits = (ulong)BitConverter.DoubleToInt64Bits(op2); + + ulong op1_sign = op1_bits & 0x8000000000000000; + ulong op2_sign = op2_bits & 0x8000000000000000; + ulong op1_other = op1_bits & 0x7FFFFFFFFFFFFFFF; + ulong op2_other = op2_bits & 0x7FFFFFFFFFFFFFFF; + + bool inf1 = op1_other == 0x7ff0000000000000; + bool inf2 = op2_other == 0x7ff0000000000000; + bool zero1 = op1_other == 0; + bool zero2 = op2_other == 0; + + if ((inf1 && zero2) || (zero1 && inf2)) + { + return 2.0; + } + else if (inf1 || inf2) + { + // Infinity + return BitConverter.Int64BitsToDouble((long)(0x7ff0000000000000 | (op1_sign ^ op2_sign))); + } + + return 2.0 + op1 * op2; + } } } \ No newline at end of file diff --git a/Ryujinx.Tests/Cpu/CpuTestSimdArithmetic.cs b/Ryujinx.Tests/Cpu/CpuTestSimdArithmetic.cs index 7b9eb8ca65..2a0f5ed919 100644 --- a/Ryujinx.Tests/Cpu/CpuTestSimdArithmetic.cs +++ b/Ryujinx.Tests/Cpu/CpuTestSimdArithmetic.cs @@ -194,12 +194,13 @@ namespace Ryujinx.Tests.Cpu 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(2 - (A * B))); - Assert.That(Sse41.Extract(ThreadState.V4, (byte)1), Is.EqualTo(2 - (A * B))); - Assert.That(Sse41.Extract(ThreadState.V4, (byte)2), Is.EqualTo(2 - (A * B))); - Assert.That(Sse41.Extract(ThreadState.V4, (byte)3), Is.EqualTo(2 - (A * B))); + 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)); }); }