Improve some tests, fix some shift instructions, add slow path for Vadd

This commit is contained in:
riperiperi 2020-01-19 18:31:31 +00:00
parent 9eae8f59b6
commit 39304e394d
5 changed files with 116 additions and 52 deletions

View file

@ -226,28 +226,49 @@ namespace ARMeilleure.Instructions
switch (op.ShiftType)
{
case ShiftType.Lsl: shiftResult = EmitLslC(context, m, setCarry, s); break;
case ShiftType.Lsr: shiftResult = EmitLsrC(context, m, setCarry, s); break;
case ShiftType.Asr: shiftResult = EmitAsrC(context, m, setCarry, s); break;
case ShiftType.Ror: shiftResult = EmitRorC(context, m, setCarry, s); break;
case ShiftType.Lsl: shiftResult = EmitLslC(context, m, setCarry, s, shiftIsZero); break;
case ShiftType.Lsr: shiftResult = EmitLsrC(context, m, setCarry, s, shiftIsZero); break;
case ShiftType.Asr: shiftResult = EmitAsrC(context, m, setCarry, s, shiftIsZero); break;
case ShiftType.Ror: shiftResult = EmitRorC(context, m, setCarry, s, shiftIsZero); break;
}
return context.ConditionalSelect(shiftIsZero, zeroResult, shiftResult);
}
public static Operand EmitLslC(ArmEmitterContext context, Operand m, bool setCarry, Operand shift)
public static void EmitIfHelper(ArmEmitterContext context, Operand boolValue, Action action, bool expected = true)
{
Operand endLabel = Label();
if (expected)
{
context.BranchIfFalse(endLabel, boolValue);
}
else
{
context.BranchIfTrue(endLabel, boolValue);
}
action();
context.MarkLabel(endLabel);
}
public static Operand EmitLslC(ArmEmitterContext context, Operand m, bool setCarry, Operand shift, Operand shiftIsZero)
{
Operand shiftLarge = context.ICompareGreaterOrEqual(shift, Const(32));
Operand result = context.ShiftLeft(m, shift);
if (setCarry)
{
Operand cOut = context.ShiftRightUI(m, context.Subtract(Const(32), shift));
EmitIfHelper(context, shiftIsZero, () =>
{
Operand cOut = context.ShiftRightUI(m, context.Subtract(Const(32), shift));
cOut = context.BitwiseAnd(cOut, Const(1));
cOut = context.ConditionalSelect(context.ICompareGreater(shift, Const(32)), Const(0), cOut);
cOut = context.BitwiseAnd(cOut, Const(1));
cOut = context.ConditionalSelect(context.ICompareGreater(shift, Const(32)), Const(0), cOut);
SetFlag(context, PState.CFlag, cOut);
SetFlag(context, PState.CFlag, cOut);
}, false);
}
return context.ConditionalSelect(shiftLarge, Const(0), result);
@ -283,18 +304,21 @@ namespace ARMeilleure.Instructions
}
}
public static Operand EmitLsrC(ArmEmitterContext context, Operand m, bool setCarry, Operand shift)
public static Operand EmitLsrC(ArmEmitterContext context, Operand m, bool setCarry, Operand shift, Operand shiftIsZero)
{
Operand shiftLarge = context.ICompareGreaterOrEqual(shift, Const(32));
Operand result = context.ShiftRightUI(m, shift);
if (setCarry)
{
Operand cOut = context.ShiftRightUI(m, context.Subtract(shift, Const(1)));
EmitIfHelper(context, shiftIsZero, () =>
{
Operand cOut = context.ShiftRightUI(m, context.Subtract(shift, Const(1)));
cOut = context.BitwiseAnd(cOut, Const(1));
cOut = context.ConditionalSelect(context.ICompareGreater(shift, Const(32)), Const(0), cOut);
cOut = context.BitwiseAnd(cOut, Const(1));
cOut = context.ConditionalSelect(context.ICompareGreater(shift, Const(32)), Const(0), cOut);
SetFlag(context, PState.CFlag, cOut);
SetFlag(context, PState.CFlag, cOut);
}, false);
}
return context.ConditionalSelect(shiftLarge, Const(0), result);
}
@ -335,7 +359,7 @@ namespace ARMeilleure.Instructions
return Const(0);
}
public static Operand EmitAsrC(ArmEmitterContext context, Operand m, bool setCarry, Operand shift)
public static Operand EmitAsrC(ArmEmitterContext context, Operand m, bool setCarry, Operand shift, Operand shiftIsZero)
{
Operand l32Result;
Operand ge32Result;
@ -346,17 +370,23 @@ namespace ARMeilleure.Instructions
if (setCarry)
{
SetCarryMLsb(context, ge32Result);
EmitIfHelper(context, context.BitwiseOr(less32, shiftIsZero), () =>
{
SetCarryMLsb(context, ge32Result);
}, false);
}
l32Result = context.ShiftRightSI(m, shift);
if (setCarry)
{
Operand cOut = context.ShiftRightUI(m, context.Subtract(shift, Const(1)));
EmitIfHelper(context, context.BitwiseAnd(less32, context.BitwiseNot(shiftIsZero)), () =>
{
Operand cOut = context.ShiftRightUI(m, context.Subtract(shift, Const(1)));
cOut = context.BitwiseAnd(cOut, Const(1));
cOut = context.BitwiseAnd(cOut, Const(1));
SetFlag(context, PState.CFlag, cOut);
SetFlag(context, PState.CFlag, cOut);
});
}
return context.ConditionalSelect(less32, l32Result, ge32Result);
@ -386,14 +416,17 @@ namespace ARMeilleure.Instructions
}
}
public static Operand EmitRorC(ArmEmitterContext context, Operand m, bool setCarry, Operand shift)
public static Operand EmitRorC(ArmEmitterContext context, Operand m, bool setCarry, Operand shift, Operand shiftIsZero)
{
shift = context.BitwiseAnd(shift, Const(0x1f));
m = context.RotateRight(m, shift);
if (setCarry)
{
SetCarryMMsb(context, m);
EmitIfHelper(context, shiftIsZero, () =>
{
SetCarryMMsb(context, m);
}, false);
}
return m;
}

View file

@ -40,12 +40,26 @@ namespace ARMeilleure.Instructions
public static void Vadd_S(ArmEmitterContext context)
{
EmitScalarBinaryOpF32(context, (op1, op2) => context.Add(op1, op2));
if (Optimizations.FastFP)
{
EmitScalarBinaryOpF32(context, (op1, op2) => context.Add(op1, op2));
}
else
{
EmitScalarBinaryOpF32(context, (op1, op2) => EmitSoftFloatCall(context, SoftFloat32.FPAdd, SoftFloat64.FPAdd, op1, op2));
}
}
public static void Vadd_V(ArmEmitterContext context)
{
EmitVectorBinaryOpF32(context, (op1, op2) => context.Add(op1, op2));
if (Optimizations.FastFP)
{
EmitScalarBinaryOpF32(context, (op1, op2) => context.Add(op1, op2));
}
else
{
EmitVectorBinaryOpF32(context, (op1, op2) => EmitSoftFloatCallDefaultFpscr(context, SoftFloat32.FPAddFpscr, SoftFloat64.FPAddFpscr, op1, op2));
}
}
public static void Vadd_I(ArmEmitterContext context)

View file

@ -64,7 +64,7 @@ namespace ARMeilleure.Instructions
Operand isOutOfRangeN = context.ICompareGreaterOrEqual(negShiftLsB, Const(8 << size));
//also zero if shift is too negative, but value was positive
isOutOfRange0 = context.BitwiseOr(isOutOfRange0, context.BitwiseAnd(isOutOfRangeN, context.ICompareGreaterOrEqual(op, Const(0))));
isOutOfRange0 = context.BitwiseOr(isOutOfRange0, context.BitwiseAnd(isOutOfRangeN, context.ICompareGreaterOrEqual(op, Const(op.Type, 0))));
Operand min = (op.Type == OperandType.I64) ? Const(-1L) : Const(-1);

View file

@ -623,9 +623,14 @@ namespace ARMeilleure.Instructions
static class SoftFloat32
{
public static float FPAdd(float value1, float value2)
{
return FPAddFpscr(value1, value2, false);
}
public static float FPAddFpscr(float value1, float value2, bool standardFpscr)
{
ExecutionContext context = NativeInterface.GetContext();
FPCR fpcr = context.Fpcr;
FPCR fpcr = standardFpscr ? context.StandardFpcrValue : context.Fpcr;
value1 = value1.FPUnpack(out FPType type1, out bool sign1, out uint op1, context, fpcr);
value2 = value2.FPUnpack(out FPType type2, out bool sign2, out uint op2, context, fpcr);
@ -1909,9 +1914,14 @@ namespace ARMeilleure.Instructions
static class SoftFloat64
{
public static double FPAdd(double value1, double value2)
{
return FPAddFpscr(value1, value2, false);
}
public static double FPAddFpscr(double value1, double value2, bool standardFpscr)
{
ExecutionContext context = NativeInterface.GetContext();
FPCR fpcr = context.Fpcr;
FPCR fpcr = standardFpscr ? context.StandardFpcrValue : context.Fpcr;
value1 = value1.FPUnpack(out FPType type1, out bool sign1, out ulong op1, context, fpcr);
value2 = value2.FPUnpack(out FPType type2, out bool sign2, out ulong op2, context, fpcr);

View file

@ -202,7 +202,7 @@ namespace Ryujinx.Tests.Cpu
}
#endregion
private const int RndCnt = 20;
private const int RndCnt = 5;
private static readonly bool NoZeros = false;
private static readonly bool NoInfs = false;
@ -210,34 +210,37 @@ namespace Ryujinx.Tests.Cpu
[Test, Pairwise, Description("VADD.f32 V0, V0, V0")]
public void Vadd_f32([Values(0u)] uint rd,
[Values(1u, 0u)] uint rn,
[Values(2u, 0u)] uint rm,
[ValueSource("_2S_F_")] [Random(RndCnt)] ulong z,
[ValueSource("_2S_F_")] [Random(RndCnt)] ulong a,
[ValueSource("_2S_F_")] [Random(RndCnt)] ulong b,
[Values(0u, 1u)] uint rn,
[Values(0u, 2u)] uint rm,
[ValueSource("_2S_F_")] ulong z0,
[ValueSource("_2S_F_")] ulong z1,
[ValueSource("_2S_F_")] ulong a0,
[ValueSource("_2S_F_")] ulong a1,
[ValueSource("_2S_F_")] ulong b0,
[ValueSource("_2S_F_")] ulong b1,
[Values] bool q)
{
uint opcode = 0xf2000d00; // VADD.f32 D0, D0, D0
uint opcode = 0xf2000d00u; // VADD.F32 D0, D0, D0
if (q)
{
rm &= 0x1e;
rn &= 0x1e;
rd &= 0x1e;
rm <<= 2;
rn <<= 2;
rd <<= 2;
opcode |= 1 << 6;
}
opcode |= ((rm & 0xf) << 0) | ((rm & 0x10) << 1);
opcode |= ((rm & 0xf) << 0) | ((rm & 0x10) << 1);
opcode |= ((rd & 0xf) << 12) | ((rd & 0x10) << 18);
opcode |= ((rn & 0xf) << 16) | ((rn & 0x10) << 3);
if (q) opcode |= 1 << 6;
V128 v0 = MakeVectorE0E1(z, z);
V128 v1 = MakeVectorE0E1(a, z);
V128 v2 = MakeVectorE0E1(b, z);
V128 v0 = MakeVectorE0E1(z0, z1);
V128 v1 = MakeVectorE0E1(a0, a1);
V128 v2 = MakeVectorE0E1(b0, b1);
SingleOpcode(opcode, v0: v0, v1: v1, v2: v2);
CompareAgainstUnicorn(fpTolerances: FpTolerances.UpToOneUlpsS);
CompareAgainstUnicorn();
}
[Test, Pairwise, Description("VCMP.f<size> Vd, Vm")]
@ -246,17 +249,18 @@ namespace Ryujinx.Tests.Cpu
[ValueSource("_1S_F_")] ulong b,
[Values] bool e)
{
uint opcode = 0xeeb40840;
uint opcode = 0xeeb40840u;
uint rm = 1;
uint rd = 2;
if (size == 3)
{
opcode |= ((rm & 0xf) << 0) | ((rm & 0x10) << 1);
opcode |= ((rm & 0xf) << 0) | ((rm & 0x10) << 1);
opcode |= ((rd & 0xf) << 12) | ((rd & 0x10) << 18);
} else
}
else
{
opcode |= ((rm & 0x1e) >> 1) | ((rm & 0x1) << 5);
opcode |= ((rm & 0x1e) >> 1) | ((rm & 0x1) << 5);
opcode |= ((rd & 0x1e) << 11) | ((rd & 0x1) << 22);
}
@ -304,6 +308,8 @@ namespace Ryujinx.Tests.Cpu
opcode |= ((rd & 0xf) << 12) | ((rd & 0x10) << 18);
opcode |= ((rn & 0xf) << 16) | ((rn & 0x10) << 3);
opcode |= size << 20;
V128 v0 = MakeVectorE0E1(z, z);
V128 v1 = MakeVectorE0E1(a, z);
V128 v2 = MakeVectorE0E1(b, z);
@ -315,12 +321,13 @@ namespace Ryujinx.Tests.Cpu
[Test, Combinatorial, Description("VPADD.f32 V0, V0, V0")]
public void Vpadd_f32([Values(0u)] uint rd,
[Range(0u, 7u)] uint rn,
[Range(0u, 7u)] uint rm)
[Range(0u, 7u)] uint rn,
[Range(0u, 7u)] uint rm)
{
uint opcode = 0xf3000d00;
// not currently a slow path test - just a sanity check for pairwise
uint opcode = 0xf3000d00u;
opcode |= ((rm & 0xf) << 0) | ((rm & 0x10) << 1);
opcode |= ((rm & 0xf) << 0) | ((rm & 0x10) << 1);
opcode |= ((rd & 0xf) << 12) | ((rd & 0x10) << 18);
opcode |= ((rn & 0xf) << 16) | ((rn & 0x10) << 3);
@ -331,7 +338,7 @@ namespace Ryujinx.Tests.Cpu
SingleOpcode(opcode, v0: v0, v1: v1, v2: v2);
CompareAgainstUnicorn(fpTolerances: FpTolerances.UpToOneUlpsS);
CompareAgainstUnicorn();
}
#endif
}