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,21 +226,41 @@ namespace ARMeilleure.Instructions
|
||||||
|
|
||||||
switch (op.ShiftType)
|
switch (op.ShiftType)
|
||||||
{
|
{
|
||||||
case ShiftType.Lsl: shiftResult = EmitLslC(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); break;
|
case ShiftType.Lsr: shiftResult = EmitLsrC(context, m, setCarry, s, shiftIsZero); break;
|
||||||
case ShiftType.Asr: shiftResult = EmitAsrC(context, m, setCarry, s); break;
|
case ShiftType.Asr: shiftResult = EmitAsrC(context, m, setCarry, s, shiftIsZero); break;
|
||||||
case ShiftType.Ror: shiftResult = EmitRorC(context, m, setCarry, s); break;
|
case ShiftType.Ror: shiftResult = EmitRorC(context, m, setCarry, s, shiftIsZero); break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return context.ConditionalSelect(shiftIsZero, zeroResult, shiftResult);
|
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 shiftLarge = context.ICompareGreaterOrEqual(shift, Const(32));
|
||||||
|
|
||||||
Operand result = context.ShiftLeft(m, shift);
|
Operand result = context.ShiftLeft(m, shift);
|
||||||
if (setCarry)
|
if (setCarry)
|
||||||
|
{
|
||||||
|
EmitIfHelper(context, shiftIsZero, () =>
|
||||||
{
|
{
|
||||||
Operand cOut = context.ShiftRightUI(m, context.Subtract(Const(32), shift));
|
Operand cOut = context.ShiftRightUI(m, context.Subtract(Const(32), shift));
|
||||||
|
|
||||||
|
@ -248,6 +268,7 @@ namespace ARMeilleure.Instructions
|
||||||
cOut = context.ConditionalSelect(context.ICompareGreater(shift, Const(32)), Const(0), cOut);
|
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);
|
return context.ConditionalSelect(shiftLarge, Const(0), result);
|
||||||
|
@ -283,11 +304,13 @@ 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 shiftLarge = context.ICompareGreaterOrEqual(shift, Const(32));
|
||||||
Operand result = context.ShiftRightUI(m, shift);
|
Operand result = context.ShiftRightUI(m, shift);
|
||||||
if (setCarry)
|
if (setCarry)
|
||||||
|
{
|
||||||
|
EmitIfHelper(context, shiftIsZero, () =>
|
||||||
{
|
{
|
||||||
Operand cOut = context.ShiftRightUI(m, context.Subtract(shift, Const(1)));
|
Operand cOut = context.ShiftRightUI(m, context.Subtract(shift, Const(1)));
|
||||||
|
|
||||||
|
@ -295,6 +318,7 @@ namespace ARMeilleure.Instructions
|
||||||
cOut = context.ConditionalSelect(context.ICompareGreater(shift, Const(32)), Const(0), cOut);
|
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);
|
return context.ConditionalSelect(shiftLarge, Const(0), result);
|
||||||
}
|
}
|
||||||
|
@ -335,7 +359,7 @@ namespace ARMeilleure.Instructions
|
||||||
return Const(0);
|
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 l32Result;
|
||||||
Operand ge32Result;
|
Operand ge32Result;
|
||||||
|
@ -345,18 +369,24 @@ namespace ARMeilleure.Instructions
|
||||||
ge32Result = context.ShiftRightSI(m, Const(31));
|
ge32Result = context.ShiftRightSI(m, Const(31));
|
||||||
|
|
||||||
if (setCarry)
|
if (setCarry)
|
||||||
|
{
|
||||||
|
EmitIfHelper(context, context.BitwiseOr(less32, shiftIsZero), () =>
|
||||||
{
|
{
|
||||||
SetCarryMLsb(context, ge32Result);
|
SetCarryMLsb(context, ge32Result);
|
||||||
|
}, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
l32Result = context.ShiftRightSI(m, shift);
|
l32Result = context.ShiftRightSI(m, shift);
|
||||||
if (setCarry)
|
if (setCarry)
|
||||||
|
{
|
||||||
|
EmitIfHelper(context, context.BitwiseAnd(less32, context.BitwiseNot(shiftIsZero)), () =>
|
||||||
{
|
{
|
||||||
Operand cOut = context.ShiftRightUI(m, context.Subtract(shift, Const(1)));
|
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);
|
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));
|
shift = context.BitwiseAnd(shift, Const(0x1f));
|
||||||
m = context.RotateRight(m, shift);
|
m = context.RotateRight(m, shift);
|
||||||
|
|
||||||
if (setCarry)
|
if (setCarry)
|
||||||
|
{
|
||||||
|
EmitIfHelper(context, shiftIsZero, () =>
|
||||||
{
|
{
|
||||||
SetCarryMMsb(context, m);
|
SetCarryMMsb(context, m);
|
||||||
|
}, false);
|
||||||
}
|
}
|
||||||
return m;
|
return m;
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,13 +39,27 @@ namespace ARMeilleure.Instructions
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void Vadd_S(ArmEmitterContext context)
|
public static void Vadd_S(ArmEmitterContext context)
|
||||||
|
{
|
||||||
|
if (Optimizations.FastFP)
|
||||||
{
|
{
|
||||||
EmitScalarBinaryOpF32(context, (op1, op2) => context.Add(op1, op2));
|
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)
|
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)
|
public static void Vadd_I(ArmEmitterContext context)
|
||||||
|
|
|
@ -64,7 +64,7 @@ namespace ARMeilleure.Instructions
|
||||||
Operand isOutOfRangeN = context.ICompareGreaterOrEqual(negShiftLsB, Const(8 << size));
|
Operand isOutOfRangeN = context.ICompareGreaterOrEqual(negShiftLsB, Const(8 << size));
|
||||||
|
|
||||||
//also zero if shift is too negative, but value was positive
|
//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);
|
Operand min = (op.Type == OperandType.I64) ? Const(-1L) : Const(-1);
|
||||||
|
|
||||||
|
|
|
@ -623,9 +623,14 @@ namespace ARMeilleure.Instructions
|
||||||
static class SoftFloat32
|
static class SoftFloat32
|
||||||
{
|
{
|
||||||
public static float FPAdd(float value1, float value2)
|
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();
|
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);
|
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);
|
value2 = value2.FPUnpack(out FPType type2, out bool sign2, out uint op2, context, fpcr);
|
||||||
|
@ -1909,9 +1914,14 @@ namespace ARMeilleure.Instructions
|
||||||
static class SoftFloat64
|
static class SoftFloat64
|
||||||
{
|
{
|
||||||
public static double FPAdd(double value1, double value2)
|
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();
|
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);
|
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);
|
value2 = value2.FPUnpack(out FPType type2, out bool sign2, out ulong op2, context, fpcr);
|
||||||
|
|
|
@ -202,7 +202,7 @@ namespace Ryujinx.Tests.Cpu
|
||||||
}
|
}
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
private const int RndCnt = 20;
|
private const int RndCnt = 5;
|
||||||
|
|
||||||
private static readonly bool NoZeros = false;
|
private static readonly bool NoZeros = false;
|
||||||
private static readonly bool NoInfs = false;
|
private static readonly bool NoInfs = false;
|
||||||
|
@ -210,34 +210,37 @@ namespace Ryujinx.Tests.Cpu
|
||||||
|
|
||||||
[Test, Pairwise, Description("VADD.f32 V0, V0, V0")]
|
[Test, Pairwise, Description("VADD.f32 V0, V0, V0")]
|
||||||
public void Vadd_f32([Values(0u)] uint rd,
|
public void Vadd_f32([Values(0u)] uint rd,
|
||||||
[Values(1u, 0u)] uint rn,
|
[Values(0u, 1u)] uint rn,
|
||||||
[Values(2u, 0u)] uint rm,
|
[Values(0u, 2u)] uint rm,
|
||||||
[ValueSource("_2S_F_")] [Random(RndCnt)] ulong z,
|
[ValueSource("_2S_F_")] ulong z0,
|
||||||
[ValueSource("_2S_F_")] [Random(RndCnt)] ulong a,
|
[ValueSource("_2S_F_")] ulong z1,
|
||||||
[ValueSource("_2S_F_")] [Random(RndCnt)] ulong b,
|
[ValueSource("_2S_F_")] ulong a0,
|
||||||
|
[ValueSource("_2S_F_")] ulong a1,
|
||||||
|
[ValueSource("_2S_F_")] ulong b0,
|
||||||
|
[ValueSource("_2S_F_")] ulong b1,
|
||||||
[Values] bool q)
|
[Values] bool q)
|
||||||
{
|
{
|
||||||
uint opcode = 0xf2000d00; // VADD.f32 D0, D0, D0
|
uint opcode = 0xf2000d00u; // VADD.F32 D0, D0, D0
|
||||||
if (q)
|
if (q)
|
||||||
{
|
{
|
||||||
rm &= 0x1e;
|
rm <<= 2;
|
||||||
rn &= 0x1e;
|
rn <<= 2;
|
||||||
rd &= 0x1e;
|
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 |= ((rd & 0xf) << 12) | ((rd & 0x10) << 18);
|
||||||
opcode |= ((rn & 0xf) << 16) | ((rn & 0x10) << 3);
|
opcode |= ((rn & 0xf) << 16) | ((rn & 0x10) << 3);
|
||||||
|
|
||||||
if (q) opcode |= 1 << 6;
|
V128 v0 = MakeVectorE0E1(z0, z1);
|
||||||
|
V128 v1 = MakeVectorE0E1(a0, a1);
|
||||||
V128 v0 = MakeVectorE0E1(z, z);
|
V128 v2 = MakeVectorE0E1(b0, b1);
|
||||||
V128 v1 = MakeVectorE0E1(a, z);
|
|
||||||
V128 v2 = MakeVectorE0E1(b, z);
|
|
||||||
|
|
||||||
SingleOpcode(opcode, v0: v0, v1: v1, v2: v2);
|
SingleOpcode(opcode, v0: v0, v1: v1, v2: v2);
|
||||||
|
|
||||||
CompareAgainstUnicorn(fpTolerances: FpTolerances.UpToOneUlpsS);
|
CompareAgainstUnicorn();
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test, Pairwise, Description("VCMP.f<size> Vd, Vm")]
|
[Test, Pairwise, Description("VCMP.f<size> Vd, Vm")]
|
||||||
|
@ -246,7 +249,7 @@ namespace Ryujinx.Tests.Cpu
|
||||||
[ValueSource("_1S_F_")] ulong b,
|
[ValueSource("_1S_F_")] ulong b,
|
||||||
[Values] bool e)
|
[Values] bool e)
|
||||||
{
|
{
|
||||||
uint opcode = 0xeeb40840;
|
uint opcode = 0xeeb40840u;
|
||||||
uint rm = 1;
|
uint rm = 1;
|
||||||
uint rd = 2;
|
uint rd = 2;
|
||||||
|
|
||||||
|
@ -254,7 +257,8 @@ namespace Ryujinx.Tests.Cpu
|
||||||
{
|
{
|
||||||
opcode |= ((rm & 0xf) << 0) | ((rm & 0x10) << 1);
|
opcode |= ((rm & 0xf) << 0) | ((rm & 0x10) << 1);
|
||||||
opcode |= ((rd & 0xf) << 12) | ((rd & 0x10) << 18);
|
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);
|
opcode |= ((rd & 0x1e) << 11) | ((rd & 0x1) << 22);
|
||||||
|
@ -304,6 +308,8 @@ namespace Ryujinx.Tests.Cpu
|
||||||
opcode |= ((rd & 0xf) << 12) | ((rd & 0x10) << 18);
|
opcode |= ((rd & 0xf) << 12) | ((rd & 0x10) << 18);
|
||||||
opcode |= ((rn & 0xf) << 16) | ((rn & 0x10) << 3);
|
opcode |= ((rn & 0xf) << 16) | ((rn & 0x10) << 3);
|
||||||
|
|
||||||
|
opcode |= size << 20;
|
||||||
|
|
||||||
V128 v0 = MakeVectorE0E1(z, z);
|
V128 v0 = MakeVectorE0E1(z, z);
|
||||||
V128 v1 = MakeVectorE0E1(a, z);
|
V128 v1 = MakeVectorE0E1(a, z);
|
||||||
V128 v2 = MakeVectorE0E1(b, z);
|
V128 v2 = MakeVectorE0E1(b, z);
|
||||||
|
@ -318,7 +324,8 @@ namespace Ryujinx.Tests.Cpu
|
||||||
[Range(0u, 7u)] uint rn,
|
[Range(0u, 7u)] uint rn,
|
||||||
[Range(0u, 7u)] uint rm)
|
[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 |= ((rd & 0xf) << 12) | ((rd & 0x10) << 18);
|
||||||
|
@ -331,7 +338,7 @@ namespace Ryujinx.Tests.Cpu
|
||||||
|
|
||||||
SingleOpcode(opcode, v0: v0, v1: v1, v2: v2);
|
SingleOpcode(opcode, v0: v0, v1: v1, v2: v2);
|
||||||
|
|
||||||
CompareAgainstUnicorn(fpTolerances: FpTolerances.UpToOneUlpsS);
|
CompareAgainstUnicorn();
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue