Improve some tests, fix some shift instructions, add slow path for Vadd
This commit is contained in:
parent
9eae8f59b6
commit
39304e394d
5 changed files with 116 additions and 52 deletions
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue