ChocolArm64: Handle infinities and zeros in Frecps

This commit is contained in:
MerryMage 2018-07-08 01:17:06 +01:00
parent 05e4a6ec0a
commit 905c9bc96c
4 changed files with 67 additions and 48 deletions

View file

@ -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)

View file

@ -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;

View file

@ -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;
}
}
}

View file

@ -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));
});
}