[arm64] Avoid signed arithmetic in AddWithCarry.

This avoids implementation-defined signed overflow in the simulator's
AddWithCarry implementation. The implementation of AddWithCarry now uses
unsigned arithmetic exclusively.

Testing coverage is also significantly improved.

BUG=

Review-Url: https://codereview.chromium.org/2157283003
Cr-Commit-Position: refs/heads/master@{#37895}
This commit is contained in:
jacob.bramley 2016-07-20 02:14:56 -07:00 committed by Commit bot
parent f52263ece7
commit 6ae7f2b5d8
3 changed files with 420 additions and 177 deletions

View File

@ -888,36 +888,31 @@ int Simulator::CodeFromName(const char* name) {
// Helpers ---------------------------------------------------------------------
template <typename T>
T Simulator::AddWithCarry(bool set_flags,
T src1,
T src2,
T carry_in) {
typedef typename make_unsigned<T>::type unsignedT;
T Simulator::AddWithCarry(bool set_flags, T left, T right, int carry_in) {
// Use unsigned types to avoid implementation-defined overflow behaviour.
static_assert(std::is_unsigned<T>::value, "operands must be unsigned");
static_assert((sizeof(T) == kWRegSize) || (sizeof(T) == kXRegSize),
"Only W- or X-sized operands are tested");
DCHECK((carry_in == 0) || (carry_in == 1));
T signed_sum = src1 + src2 + carry_in;
T result = signed_sum;
bool N, Z, C, V;
// Compute the C flag
unsignedT u1 = static_cast<unsignedT>(src1);
unsignedT u2 = static_cast<unsignedT>(src2);
unsignedT urest = std::numeric_limits<unsignedT>::max() - u1;
C = (u2 > urest) || (carry_in && (((u2 + 1) > urest) || (u2 > (urest - 1))));
// Overflow iff the sign bit is the same for the two inputs and different
// for the result.
V = ((src1 ^ src2) >= 0) && ((src1 ^ result) < 0);
N = CalcNFlag(result);
Z = CalcZFlag(result);
T result = left + right + carry_in;
if (set_flags) {
nzcv().SetN(N);
nzcv().SetZ(Z);
nzcv().SetC(C);
nzcv().SetV(V);
nzcv().SetN(CalcNFlag(result));
nzcv().SetZ(CalcZFlag(result));
// Compute the C flag by comparing the result to the max unsigned integer.
T max_uint_2op = std::numeric_limits<T>::max() - carry_in;
nzcv().SetC((left > max_uint_2op) || ((max_uint_2op - left) < right));
// Overflow iff the sign bit is the same for the two inputs and different
// for the result.
T sign_mask = T(1) << (sizeof(T) * 8 - 1);
T left_sign = left & sign_mask;
T right_sign = right & sign_mask;
T result_sign = result & sign_mask;
nzcv().SetV((left_sign == right_sign) && (left_sign != result_sign));
LogSystemRegister(NZCV);
}
return result;
@ -926,6 +921,9 @@ T Simulator::AddWithCarry(bool set_flags,
template<typename T>
void Simulator::AddSubWithCarry(Instruction* instr) {
// Use unsigned types to avoid implementation-defined overflow behaviour.
static_assert(std::is_unsigned<T>::value, "operands must be unsigned");
T op2 = reg<T>(instr->Rm());
T new_val;
@ -1418,6 +1416,9 @@ void Simulator::VisitCompareBranch(Instruction* instr) {
template<typename T>
void Simulator::AddSubHelper(Instruction* instr, T op2) {
// Use unsigned types to avoid implementation-defined overflow behaviour.
static_assert(std::is_unsigned<T>::value, "operands must be unsigned");
bool set_flags = instr->FlagsUpdate();
T new_val = 0;
Instr operation = instr->Mask(AddSubOpMask);
@ -1450,11 +1451,10 @@ void Simulator::VisitAddSubShifted(Instruction* instr) {
unsigned shift_amount = instr->ImmDPShift();
if (instr->SixtyFourBits()) {
int64_t op2 = ShiftOperand(xreg(instr->Rm()), shift_type, shift_amount);
uint64_t op2 = ShiftOperand(xreg(instr->Rm()), shift_type, shift_amount);
AddSubHelper(instr, op2);
} else {
int32_t op2 = static_cast<int32_t>(
ShiftOperand(wreg(instr->Rm()), shift_type, shift_amount));
uint32_t op2 = ShiftOperand(wreg(instr->Rm()), shift_type, shift_amount);
AddSubHelper(instr, op2);
}
}
@ -1463,9 +1463,9 @@ void Simulator::VisitAddSubShifted(Instruction* instr) {
void Simulator::VisitAddSubImmediate(Instruction* instr) {
int64_t op2 = instr->ImmAddSub() << ((instr->ShiftAddSub() == 1) ? 12 : 0);
if (instr->SixtyFourBits()) {
AddSubHelper<int64_t>(instr, op2);
AddSubHelper(instr, static_cast<uint64_t>(op2));
} else {
AddSubHelper<int32_t>(instr, static_cast<int32_t>(op2));
AddSubHelper(instr, static_cast<uint32_t>(op2));
}
}
@ -1474,10 +1474,10 @@ void Simulator::VisitAddSubExtended(Instruction* instr) {
Extend ext = static_cast<Extend>(instr->ExtendMode());
unsigned left_shift = instr->ImmExtendShift();
if (instr->SixtyFourBits()) {
int64_t op2 = ExtendValue(xreg(instr->Rm()), ext, left_shift);
uint64_t op2 = ExtendValue(xreg(instr->Rm()), ext, left_shift);
AddSubHelper(instr, op2);
} else {
int32_t op2 = ExtendValue(wreg(instr->Rm()), ext, left_shift);
uint32_t op2 = ExtendValue(wreg(instr->Rm()), ext, left_shift);
AddSubHelper(instr, op2);
}
}
@ -1485,9 +1485,9 @@ void Simulator::VisitAddSubExtended(Instruction* instr) {
void Simulator::VisitAddSubWithCarry(Instruction* instr) {
if (instr->SixtyFourBits()) {
AddSubWithCarry<int64_t>(instr);
AddSubWithCarry<uint64_t>(instr);
} else {
AddSubWithCarry<int32_t>(instr);
AddSubWithCarry<uint32_t>(instr);
}
}
@ -1497,22 +1497,22 @@ void Simulator::VisitLogicalShifted(Instruction* instr) {
unsigned shift_amount = instr->ImmDPShift();
if (instr->SixtyFourBits()) {
int64_t op2 = ShiftOperand(xreg(instr->Rm()), shift_type, shift_amount);
uint64_t op2 = ShiftOperand(xreg(instr->Rm()), shift_type, shift_amount);
op2 = (instr->Mask(NOT) == NOT) ? ~op2 : op2;
LogicalHelper<int64_t>(instr, op2);
LogicalHelper(instr, op2);
} else {
int32_t op2 = ShiftOperand(wreg(instr->Rm()), shift_type, shift_amount);
uint32_t op2 = ShiftOperand(wreg(instr->Rm()), shift_type, shift_amount);
op2 = (instr->Mask(NOT) == NOT) ? ~op2 : op2;
LogicalHelper<int32_t>(instr, op2);
LogicalHelper(instr, op2);
}
}
void Simulator::VisitLogicalImmediate(Instruction* instr) {
if (instr->SixtyFourBits()) {
LogicalHelper<int64_t>(instr, instr->ImmLogical());
LogicalHelper(instr, static_cast<uint64_t>(instr->ImmLogical()));
} else {
LogicalHelper<int32_t>(instr, static_cast<int32_t>(instr->ImmLogical()));
LogicalHelper(instr, static_cast<uint32_t>(instr->ImmLogical()));
}
}
@ -1548,24 +1548,27 @@ void Simulator::LogicalHelper(Instruction* instr, T op2) {
void Simulator::VisitConditionalCompareRegister(Instruction* instr) {
if (instr->SixtyFourBits()) {
ConditionalCompareHelper(instr, xreg(instr->Rm()));
ConditionalCompareHelper(instr, static_cast<uint64_t>(xreg(instr->Rm())));
} else {
ConditionalCompareHelper(instr, wreg(instr->Rm()));
ConditionalCompareHelper(instr, static_cast<uint32_t>(wreg(instr->Rm())));
}
}
void Simulator::VisitConditionalCompareImmediate(Instruction* instr) {
if (instr->SixtyFourBits()) {
ConditionalCompareHelper<int64_t>(instr, instr->ImmCondCmp());
ConditionalCompareHelper(instr, static_cast<uint64_t>(instr->ImmCondCmp()));
} else {
ConditionalCompareHelper<int32_t>(instr, instr->ImmCondCmp());
ConditionalCompareHelper(instr, static_cast<uint32_t>(instr->ImmCondCmp()));
}
}
template<typename T>
void Simulator::ConditionalCompareHelper(Instruction* instr, T op2) {
// Use unsigned types to avoid implementation-defined overflow behaviour.
static_assert(std::is_unsigned<T>::value, "operands must be unsigned");
T op1 = reg<T>(instr->Rn());
if (ConditionPassed(static_cast<Condition>(instr->Condition()))) {

View File

@ -652,11 +652,8 @@ class Simulator : public DecoderVisitor {
template<typename T>
void AddSubHelper(Instruction* instr, T op2);
template<typename T>
T AddWithCarry(bool set_flags,
T src1,
T src2,
T carry_in = 0);
template <typename T>
T AddWithCarry(bool set_flags, T left, T right, int carry_in = 0);
template<typename T>
void AddSubWithCarry(Instruction* instr);
template<typename T>

View File

@ -3819,6 +3819,375 @@ TEST(neg) {
}
template <typename T, typename Op>
static void AdcsSbcsHelper(Op op, T left, T right, int carry, T expected,
StatusFlags expected_flags) {
int reg_size = sizeof(T) * 8;
auto left_reg = Register::Create(0, reg_size);
auto right_reg = Register::Create(1, reg_size);
auto result_reg = Register::Create(2, reg_size);
SETUP();
START();
__ Mov(left_reg, left);
__ Mov(right_reg, right);
__ Mov(x10, (carry ? CFlag : NoFlag));
__ Msr(NZCV, x10);
(masm.*op)(result_reg, left_reg, right_reg);
END();
RUN();
CHECK_EQUAL_64(left, left_reg.X());
CHECK_EQUAL_64(right, right_reg.X());
CHECK_EQUAL_64(expected, result_reg.X());
CHECK_EQUAL_NZCV(expected_flags);
TEARDOWN();
}
TEST(adcs_sbcs_x) {
INIT_V8();
uint64_t inputs[] = {
0x0000000000000000, 0x0000000000000001, 0x7ffffffffffffffe,
0x7fffffffffffffff, 0x8000000000000000, 0x8000000000000001,
0xfffffffffffffffe, 0xffffffffffffffff,
};
static const size_t input_count = sizeof(inputs) / sizeof(inputs[0]);
struct Expected {
uint64_t carry0_result;
StatusFlags carry0_flags;
uint64_t carry1_result;
StatusFlags carry1_flags;
};
static const Expected expected_adcs_x[input_count][input_count] = {
{{0x0000000000000000, ZFlag, 0x0000000000000001, NoFlag},
{0x0000000000000001, NoFlag, 0x0000000000000002, NoFlag},
{0x7ffffffffffffffe, NoFlag, 0x7fffffffffffffff, NoFlag},
{0x7fffffffffffffff, NoFlag, 0x8000000000000000, NVFlag},
{0x8000000000000000, NFlag, 0x8000000000000001, NFlag},
{0x8000000000000001, NFlag, 0x8000000000000002, NFlag},
{0xfffffffffffffffe, NFlag, 0xffffffffffffffff, NFlag},
{0xffffffffffffffff, NFlag, 0x0000000000000000, ZCFlag}},
{{0x0000000000000001, NoFlag, 0x0000000000000002, NoFlag},
{0x0000000000000002, NoFlag, 0x0000000000000003, NoFlag},
{0x7fffffffffffffff, NoFlag, 0x8000000000000000, NVFlag},
{0x8000000000000000, NVFlag, 0x8000000000000001, NVFlag},
{0x8000000000000001, NFlag, 0x8000000000000002, NFlag},
{0x8000000000000002, NFlag, 0x8000000000000003, NFlag},
{0xffffffffffffffff, NFlag, 0x0000000000000000, ZCFlag},
{0x0000000000000000, ZCFlag, 0x0000000000000001, CFlag}},
{{0x7ffffffffffffffe, NoFlag, 0x7fffffffffffffff, NoFlag},
{0x7fffffffffffffff, NoFlag, 0x8000000000000000, NVFlag},
{0xfffffffffffffffc, NVFlag, 0xfffffffffffffffd, NVFlag},
{0xfffffffffffffffd, NVFlag, 0xfffffffffffffffe, NVFlag},
{0xfffffffffffffffe, NFlag, 0xffffffffffffffff, NFlag},
{0xffffffffffffffff, NFlag, 0x0000000000000000, ZCFlag},
{0x7ffffffffffffffc, CFlag, 0x7ffffffffffffffd, CFlag},
{0x7ffffffffffffffd, CFlag, 0x7ffffffffffffffe, CFlag}},
{{0x7fffffffffffffff, NoFlag, 0x8000000000000000, NVFlag},
{0x8000000000000000, NVFlag, 0x8000000000000001, NVFlag},
{0xfffffffffffffffd, NVFlag, 0xfffffffffffffffe, NVFlag},
{0xfffffffffffffffe, NVFlag, 0xffffffffffffffff, NVFlag},
{0xffffffffffffffff, NFlag, 0x0000000000000000, ZCFlag},
{0x0000000000000000, ZCFlag, 0x0000000000000001, CFlag},
{0x7ffffffffffffffd, CFlag, 0x7ffffffffffffffe, CFlag},
{0x7ffffffffffffffe, CFlag, 0x7fffffffffffffff, CFlag}},
{{0x8000000000000000, NFlag, 0x8000000000000001, NFlag},
{0x8000000000000001, NFlag, 0x8000000000000002, NFlag},
{0xfffffffffffffffe, NFlag, 0xffffffffffffffff, NFlag},
{0xffffffffffffffff, NFlag, 0x0000000000000000, ZCFlag},
{0x0000000000000000, ZCVFlag, 0x0000000000000001, CVFlag},
{0x0000000000000001, CVFlag, 0x0000000000000002, CVFlag},
{0x7ffffffffffffffe, CVFlag, 0x7fffffffffffffff, CVFlag},
{0x7fffffffffffffff, CVFlag, 0x8000000000000000, NCFlag}},
{{0x8000000000000001, NFlag, 0x8000000000000002, NFlag},
{0x8000000000000002, NFlag, 0x8000000000000003, NFlag},
{0xffffffffffffffff, NFlag, 0x0000000000000000, ZCFlag},
{0x0000000000000000, ZCFlag, 0x0000000000000001, CFlag},
{0x0000000000000001, CVFlag, 0x0000000000000002, CVFlag},
{0x0000000000000002, CVFlag, 0x0000000000000003, CVFlag},
{0x7fffffffffffffff, CVFlag, 0x8000000000000000, NCFlag},
{0x8000000000000000, NCFlag, 0x8000000000000001, NCFlag}},
{{0xfffffffffffffffe, NFlag, 0xffffffffffffffff, NFlag},
{0xffffffffffffffff, NFlag, 0x0000000000000000, ZCFlag},
{0x7ffffffffffffffc, CFlag, 0x7ffffffffffffffd, CFlag},
{0x7ffffffffffffffd, CFlag, 0x7ffffffffffffffe, CFlag},
{0x7ffffffffffffffe, CVFlag, 0x7fffffffffffffff, CVFlag},
{0x7fffffffffffffff, CVFlag, 0x8000000000000000, NCFlag},
{0xfffffffffffffffc, NCFlag, 0xfffffffffffffffd, NCFlag},
{0xfffffffffffffffd, NCFlag, 0xfffffffffffffffe, NCFlag}},
{{0xffffffffffffffff, NFlag, 0x0000000000000000, ZCFlag},
{0x0000000000000000, ZCFlag, 0x0000000000000001, CFlag},
{0x7ffffffffffffffd, CFlag, 0x7ffffffffffffffe, CFlag},
{0x7ffffffffffffffe, CFlag, 0x7fffffffffffffff, CFlag},
{0x7fffffffffffffff, CVFlag, 0x8000000000000000, NCFlag},
{0x8000000000000000, NCFlag, 0x8000000000000001, NCFlag},
{0xfffffffffffffffd, NCFlag, 0xfffffffffffffffe, NCFlag},
{0xfffffffffffffffe, NCFlag, 0xffffffffffffffff, NCFlag}}};
static const Expected expected_sbcs_x[input_count][input_count] = {
{{0xffffffffffffffff, NFlag, 0x0000000000000000, ZCFlag},
{0xfffffffffffffffe, NFlag, 0xffffffffffffffff, NFlag},
{0x8000000000000001, NFlag, 0x8000000000000002, NFlag},
{0x8000000000000000, NFlag, 0x8000000000000001, NFlag},
{0x7fffffffffffffff, NoFlag, 0x8000000000000000, NVFlag},
{0x7ffffffffffffffe, NoFlag, 0x7fffffffffffffff, NoFlag},
{0x0000000000000001, NoFlag, 0x0000000000000002, NoFlag},
{0x0000000000000000, ZFlag, 0x0000000000000001, NoFlag}},
{{0x0000000000000000, ZCFlag, 0x0000000000000001, CFlag},
{0xffffffffffffffff, NFlag, 0x0000000000000000, ZCFlag},
{0x8000000000000002, NFlag, 0x8000000000000003, NFlag},
{0x8000000000000001, NFlag, 0x8000000000000002, NFlag},
{0x8000000000000000, NVFlag, 0x8000000000000001, NVFlag},
{0x7fffffffffffffff, NoFlag, 0x8000000000000000, NVFlag},
{0x0000000000000002, NoFlag, 0x0000000000000003, NoFlag},
{0x0000000000000001, NoFlag, 0x0000000000000002, NoFlag}},
{{0x7ffffffffffffffd, CFlag, 0x7ffffffffffffffe, CFlag},
{0x7ffffffffffffffc, CFlag, 0x7ffffffffffffffd, CFlag},
{0xffffffffffffffff, NFlag, 0x0000000000000000, ZCFlag},
{0xfffffffffffffffe, NFlag, 0xffffffffffffffff, NFlag},
{0xfffffffffffffffd, NVFlag, 0xfffffffffffffffe, NVFlag},
{0xfffffffffffffffc, NVFlag, 0xfffffffffffffffd, NVFlag},
{0x7fffffffffffffff, NoFlag, 0x8000000000000000, NVFlag},
{0x7ffffffffffffffe, NoFlag, 0x7fffffffffffffff, NoFlag}},
{{0x7ffffffffffffffe, CFlag, 0x7fffffffffffffff, CFlag},
{0x7ffffffffffffffd, CFlag, 0x7ffffffffffffffe, CFlag},
{0x0000000000000000, ZCFlag, 0x0000000000000001, CFlag},
{0xffffffffffffffff, NFlag, 0x0000000000000000, ZCFlag},
{0xfffffffffffffffe, NVFlag, 0xffffffffffffffff, NVFlag},
{0xfffffffffffffffd, NVFlag, 0xfffffffffffffffe, NVFlag},
{0x8000000000000000, NVFlag, 0x8000000000000001, NVFlag},
{0x7fffffffffffffff, NoFlag, 0x8000000000000000, NVFlag}},
{{0x7fffffffffffffff, CVFlag, 0x8000000000000000, NCFlag},
{0x7ffffffffffffffe, CVFlag, 0x7fffffffffffffff, CVFlag},
{0x0000000000000001, CVFlag, 0x0000000000000002, CVFlag},
{0x0000000000000000, ZCVFlag, 0x0000000000000001, CVFlag},
{0xffffffffffffffff, NFlag, 0x0000000000000000, ZCFlag},
{0xfffffffffffffffe, NFlag, 0xffffffffffffffff, NFlag},
{0x8000000000000001, NFlag, 0x8000000000000002, NFlag},
{0x8000000000000000, NFlag, 0x8000000000000001, NFlag}},
{{0x8000000000000000, NCFlag, 0x8000000000000001, NCFlag},
{0x7fffffffffffffff, CVFlag, 0x8000000000000000, NCFlag},
{0x0000000000000002, CVFlag, 0x0000000000000003, CVFlag},
{0x0000000000000001, CVFlag, 0x0000000000000002, CVFlag},
{0x0000000000000000, ZCFlag, 0x0000000000000001, CFlag},
{0xffffffffffffffff, NFlag, 0x0000000000000000, ZCFlag},
{0x8000000000000002, NFlag, 0x8000000000000003, NFlag},
{0x8000000000000001, NFlag, 0x8000000000000002, NFlag}},
{{0xfffffffffffffffd, NCFlag, 0xfffffffffffffffe, NCFlag},
{0xfffffffffffffffc, NCFlag, 0xfffffffffffffffd, NCFlag},
{0x7fffffffffffffff, CVFlag, 0x8000000000000000, NCFlag},
{0x7ffffffffffffffe, CVFlag, 0x7fffffffffffffff, CVFlag},
{0x7ffffffffffffffd, CFlag, 0x7ffffffffffffffe, CFlag},
{0x7ffffffffffffffc, CFlag, 0x7ffffffffffffffd, CFlag},
{0xffffffffffffffff, NFlag, 0x0000000000000000, ZCFlag},
{0xfffffffffffffffe, NFlag, 0xffffffffffffffff, NFlag}},
{{0xfffffffffffffffe, NCFlag, 0xffffffffffffffff, NCFlag},
{0xfffffffffffffffd, NCFlag, 0xfffffffffffffffe, NCFlag},
{0x8000000000000000, NCFlag, 0x8000000000000001, NCFlag},
{0x7fffffffffffffff, CVFlag, 0x8000000000000000, NCFlag},
{0x7ffffffffffffffe, CFlag, 0x7fffffffffffffff, CFlag},
{0x7ffffffffffffffd, CFlag, 0x7ffffffffffffffe, CFlag},
{0x0000000000000000, ZCFlag, 0x0000000000000001, CFlag},
{0xffffffffffffffff, NFlag, 0x0000000000000000, ZCFlag}}};
for (size_t left = 0; left < input_count; left++) {
for (size_t right = 0; right < input_count; right++) {
const Expected& expected = expected_adcs_x[left][right];
AdcsSbcsHelper(&MacroAssembler::Adcs, inputs[left], inputs[right], 0,
expected.carry0_result, expected.carry0_flags);
AdcsSbcsHelper(&MacroAssembler::Adcs, inputs[left], inputs[right], 1,
expected.carry1_result, expected.carry1_flags);
}
}
for (size_t left = 0; left < input_count; left++) {
for (size_t right = 0; right < input_count; right++) {
const Expected& expected = expected_sbcs_x[left][right];
AdcsSbcsHelper(&MacroAssembler::Sbcs, inputs[left], inputs[right], 0,
expected.carry0_result, expected.carry0_flags);
AdcsSbcsHelper(&MacroAssembler::Sbcs, inputs[left], inputs[right], 1,
expected.carry1_result, expected.carry1_flags);
}
}
}
TEST(adcs_sbcs_w) {
INIT_V8();
uint32_t inputs[] = {
0x00000000, 0x00000001, 0x7ffffffe, 0x7fffffff,
0x80000000, 0x80000001, 0xfffffffe, 0xffffffff,
};
static const size_t input_count = sizeof(inputs) / sizeof(inputs[0]);
struct Expected {
uint32_t carry0_result;
StatusFlags carry0_flags;
uint32_t carry1_result;
StatusFlags carry1_flags;
};
static const Expected expected_adcs_w[input_count][input_count] = {
{{0x00000000, ZFlag, 0x00000001, NoFlag},
{0x00000001, NoFlag, 0x00000002, NoFlag},
{0x7ffffffe, NoFlag, 0x7fffffff, NoFlag},
{0x7fffffff, NoFlag, 0x80000000, NVFlag},
{0x80000000, NFlag, 0x80000001, NFlag},
{0x80000001, NFlag, 0x80000002, NFlag},
{0xfffffffe, NFlag, 0xffffffff, NFlag},
{0xffffffff, NFlag, 0x00000000, ZCFlag}},
{{0x00000001, NoFlag, 0x00000002, NoFlag},
{0x00000002, NoFlag, 0x00000003, NoFlag},
{0x7fffffff, NoFlag, 0x80000000, NVFlag},
{0x80000000, NVFlag, 0x80000001, NVFlag},
{0x80000001, NFlag, 0x80000002, NFlag},
{0x80000002, NFlag, 0x80000003, NFlag},
{0xffffffff, NFlag, 0x00000000, ZCFlag},
{0x00000000, ZCFlag, 0x00000001, CFlag}},
{{0x7ffffffe, NoFlag, 0x7fffffff, NoFlag},
{0x7fffffff, NoFlag, 0x80000000, NVFlag},
{0xfffffffc, NVFlag, 0xfffffffd, NVFlag},
{0xfffffffd, NVFlag, 0xfffffffe, NVFlag},
{0xfffffffe, NFlag, 0xffffffff, NFlag},
{0xffffffff, NFlag, 0x00000000, ZCFlag},
{0x7ffffffc, CFlag, 0x7ffffffd, CFlag},
{0x7ffffffd, CFlag, 0x7ffffffe, CFlag}},
{{0x7fffffff, NoFlag, 0x80000000, NVFlag},
{0x80000000, NVFlag, 0x80000001, NVFlag},
{0xfffffffd, NVFlag, 0xfffffffe, NVFlag},
{0xfffffffe, NVFlag, 0xffffffff, NVFlag},
{0xffffffff, NFlag, 0x00000000, ZCFlag},
{0x00000000, ZCFlag, 0x00000001, CFlag},
{0x7ffffffd, CFlag, 0x7ffffffe, CFlag},
{0x7ffffffe, CFlag, 0x7fffffff, CFlag}},
{{0x80000000, NFlag, 0x80000001, NFlag},
{0x80000001, NFlag, 0x80000002, NFlag},
{0xfffffffe, NFlag, 0xffffffff, NFlag},
{0xffffffff, NFlag, 0x00000000, ZCFlag},
{0x00000000, ZCVFlag, 0x00000001, CVFlag},
{0x00000001, CVFlag, 0x00000002, CVFlag},
{0x7ffffffe, CVFlag, 0x7fffffff, CVFlag},
{0x7fffffff, CVFlag, 0x80000000, NCFlag}},
{{0x80000001, NFlag, 0x80000002, NFlag},
{0x80000002, NFlag, 0x80000003, NFlag},
{0xffffffff, NFlag, 0x00000000, ZCFlag},
{0x00000000, ZCFlag, 0x00000001, CFlag},
{0x00000001, CVFlag, 0x00000002, CVFlag},
{0x00000002, CVFlag, 0x00000003, CVFlag},
{0x7fffffff, CVFlag, 0x80000000, NCFlag},
{0x80000000, NCFlag, 0x80000001, NCFlag}},
{{0xfffffffe, NFlag, 0xffffffff, NFlag},
{0xffffffff, NFlag, 0x00000000, ZCFlag},
{0x7ffffffc, CFlag, 0x7ffffffd, CFlag},
{0x7ffffffd, CFlag, 0x7ffffffe, CFlag},
{0x7ffffffe, CVFlag, 0x7fffffff, CVFlag},
{0x7fffffff, CVFlag, 0x80000000, NCFlag},
{0xfffffffc, NCFlag, 0xfffffffd, NCFlag},
{0xfffffffd, NCFlag, 0xfffffffe, NCFlag}},
{{0xffffffff, NFlag, 0x00000000, ZCFlag},
{0x00000000, ZCFlag, 0x00000001, CFlag},
{0x7ffffffd, CFlag, 0x7ffffffe, CFlag},
{0x7ffffffe, CFlag, 0x7fffffff, CFlag},
{0x7fffffff, CVFlag, 0x80000000, NCFlag},
{0x80000000, NCFlag, 0x80000001, NCFlag},
{0xfffffffd, NCFlag, 0xfffffffe, NCFlag},
{0xfffffffe, NCFlag, 0xffffffff, NCFlag}}};
static const Expected expected_sbcs_w[input_count][input_count] = {
{{0xffffffff, NFlag, 0x00000000, ZCFlag},
{0xfffffffe, NFlag, 0xffffffff, NFlag},
{0x80000001, NFlag, 0x80000002, NFlag},
{0x80000000, NFlag, 0x80000001, NFlag},
{0x7fffffff, NoFlag, 0x80000000, NVFlag},
{0x7ffffffe, NoFlag, 0x7fffffff, NoFlag},
{0x00000001, NoFlag, 0x00000002, NoFlag},
{0x00000000, ZFlag, 0x00000001, NoFlag}},
{{0x00000000, ZCFlag, 0x00000001, CFlag},
{0xffffffff, NFlag, 0x00000000, ZCFlag},
{0x80000002, NFlag, 0x80000003, NFlag},
{0x80000001, NFlag, 0x80000002, NFlag},
{0x80000000, NVFlag, 0x80000001, NVFlag},
{0x7fffffff, NoFlag, 0x80000000, NVFlag},
{0x00000002, NoFlag, 0x00000003, NoFlag},
{0x00000001, NoFlag, 0x00000002, NoFlag}},
{{0x7ffffffd, CFlag, 0x7ffffffe, CFlag},
{0x7ffffffc, CFlag, 0x7ffffffd, CFlag},
{0xffffffff, NFlag, 0x00000000, ZCFlag},
{0xfffffffe, NFlag, 0xffffffff, NFlag},
{0xfffffffd, NVFlag, 0xfffffffe, NVFlag},
{0xfffffffc, NVFlag, 0xfffffffd, NVFlag},
{0x7fffffff, NoFlag, 0x80000000, NVFlag},
{0x7ffffffe, NoFlag, 0x7fffffff, NoFlag}},
{{0x7ffffffe, CFlag, 0x7fffffff, CFlag},
{0x7ffffffd, CFlag, 0x7ffffffe, CFlag},
{0x00000000, ZCFlag, 0x00000001, CFlag},
{0xffffffff, NFlag, 0x00000000, ZCFlag},
{0xfffffffe, NVFlag, 0xffffffff, NVFlag},
{0xfffffffd, NVFlag, 0xfffffffe, NVFlag},
{0x80000000, NVFlag, 0x80000001, NVFlag},
{0x7fffffff, NoFlag, 0x80000000, NVFlag}},
{{0x7fffffff, CVFlag, 0x80000000, NCFlag},
{0x7ffffffe, CVFlag, 0x7fffffff, CVFlag},
{0x00000001, CVFlag, 0x00000002, CVFlag},
{0x00000000, ZCVFlag, 0x00000001, CVFlag},
{0xffffffff, NFlag, 0x00000000, ZCFlag},
{0xfffffffe, NFlag, 0xffffffff, NFlag},
{0x80000001, NFlag, 0x80000002, NFlag},
{0x80000000, NFlag, 0x80000001, NFlag}},
{{0x80000000, NCFlag, 0x80000001, NCFlag},
{0x7fffffff, CVFlag, 0x80000000, NCFlag},
{0x00000002, CVFlag, 0x00000003, CVFlag},
{0x00000001, CVFlag, 0x00000002, CVFlag},
{0x00000000, ZCFlag, 0x00000001, CFlag},
{0xffffffff, NFlag, 0x00000000, ZCFlag},
{0x80000002, NFlag, 0x80000003, NFlag},
{0x80000001, NFlag, 0x80000002, NFlag}},
{{0xfffffffd, NCFlag, 0xfffffffe, NCFlag},
{0xfffffffc, NCFlag, 0xfffffffd, NCFlag},
{0x7fffffff, CVFlag, 0x80000000, NCFlag},
{0x7ffffffe, CVFlag, 0x7fffffff, CVFlag},
{0x7ffffffd, CFlag, 0x7ffffffe, CFlag},
{0x7ffffffc, CFlag, 0x7ffffffd, CFlag},
{0xffffffff, NFlag, 0x00000000, ZCFlag},
{0xfffffffe, NFlag, 0xffffffff, NFlag}},
{{0xfffffffe, NCFlag, 0xffffffff, NCFlag},
{0xfffffffd, NCFlag, 0xfffffffe, NCFlag},
{0x80000000, NCFlag, 0x80000001, NCFlag},
{0x7fffffff, CVFlag, 0x80000000, NCFlag},
{0x7ffffffe, CFlag, 0x7fffffff, CFlag},
{0x7ffffffd, CFlag, 0x7ffffffe, CFlag},
{0x00000000, ZCFlag, 0x00000001, CFlag},
{0xffffffff, NFlag, 0x00000000, ZCFlag}}};
for (size_t left = 0; left < input_count; left++) {
for (size_t right = 0; right < input_count; right++) {
const Expected& expected = expected_adcs_w[left][right];
AdcsSbcsHelper(&MacroAssembler::Adcs, inputs[left], inputs[right], 0,
expected.carry0_result, expected.carry0_flags);
AdcsSbcsHelper(&MacroAssembler::Adcs, inputs[left], inputs[right], 1,
expected.carry1_result, expected.carry1_flags);
}
}
for (size_t left = 0; left < input_count; left++) {
for (size_t right = 0; right < input_count; right++) {
const Expected& expected = expected_sbcs_w[left][right];
AdcsSbcsHelper(&MacroAssembler::Sbcs, inputs[left], inputs[right], 0,
expected.carry0_result, expected.carry0_flags);
AdcsSbcsHelper(&MacroAssembler::Sbcs, inputs[left], inputs[right], 1,
expected.carry1_result, expected.carry1_flags);
}
}
}
TEST(adc_sbc_shift) {
INIT_V8();
SETUP();
@ -3887,132 +4256,6 @@ TEST(adc_sbc_shift) {
CHECK_EQUAL_32(0x91111110 + 1, w26);
CHECK_EQUAL_32(0x9a222221 + 1, w27);
// Check that adc correctly sets the condition flags.
START();
__ Mov(x0, 1);
__ Mov(x1, 0xffffffffffffffffL);
// Clear the C flag.
__ Adds(x0, x0, Operand(0));
__ Adcs(x10, x0, Operand(x1));
END();
RUN();
CHECK_EQUAL_NZCV(ZCFlag);
CHECK_EQUAL_64(0, x10);
START();
__ Mov(x0, 1);
__ Mov(x1, 0x8000000000000000L);
// Clear the C flag.
__ Adds(x0, x0, Operand(0));
__ Adcs(x10, x0, Operand(x1, ASR, 63));
END();
RUN();
CHECK_EQUAL_NZCV(ZCFlag);
CHECK_EQUAL_64(0, x10);
START();
__ Mov(x0, 0x10);
__ Mov(x1, 0x07ffffffffffffffL);
// Clear the C flag.
__ Adds(x0, x0, Operand(0));
__ Adcs(x10, x0, Operand(x1, LSL, 4));
END();
RUN();
CHECK_EQUAL_NZCV(NVFlag);
CHECK_EQUAL_64(0x8000000000000000L, x10);
// Check that sbc correctly sets the condition flags.
START();
__ Mov(x0, 0);
__ Mov(x1, 0xffffffffffffffffL);
// Clear the C flag.
__ Adds(x0, x0, Operand(0));
__ Sbcs(x10, x0, Operand(x1));
END();
RUN();
CHECK_EQUAL_NZCV(ZFlag);
CHECK_EQUAL_64(0, x10);
START();
__ Mov(x0, 1);
__ Mov(x1, 0xffffffffffffffffL);
// Clear the C flag.
__ Adds(x0, x0, Operand(0));
__ Sbcs(x10, x0, Operand(x1, LSR, 1));
END();
RUN();
CHECK_EQUAL_NZCV(NFlag);
CHECK_EQUAL_64(0x8000000000000001L, x10);
START();
__ Mov(x0, 0);
// Clear the C flag.
__ Adds(x0, x0, Operand(0));
__ Sbcs(x10, x0, Operand(0xffffffffffffffffL));
END();
RUN();
CHECK_EQUAL_NZCV(ZFlag);
CHECK_EQUAL_64(0, x10);
START()
__ Mov(w0, 0x7fffffff);
// Clear the C flag.
__ Adds(x0, x0, Operand(0));
__ Ngcs(w10, w0);
END();
RUN();
CHECK_EQUAL_NZCV(NFlag);
CHECK_EQUAL_64(0x80000000, x10);
START();
// Clear the C flag.
__ Adds(x0, x0, Operand(0));
__ Ngcs(x10, 0x7fffffffffffffffL);
END();
RUN();
CHECK_EQUAL_NZCV(NFlag);
CHECK_EQUAL_64(0x8000000000000000L, x10);
START()
__ Mov(x0, 0);
// Set the C flag.
__ Cmp(x0, Operand(x0));
__ Sbcs(x10, x0, Operand(1));
END();
RUN();
CHECK_EQUAL_NZCV(NFlag);
CHECK_EQUAL_64(0xffffffffffffffffL, x10);
START()
__ Mov(x0, 0);
// Set the C flag.
__ Cmp(x0, Operand(x0));
__ Ngcs(x10, 0x7fffffffffffffffL);
END();
RUN();
CHECK_EQUAL_NZCV(NFlag);
CHECK_EQUAL_64(0x8000000000000001L, x10);
TEARDOWN();
}