break;
}
+ /* -- DivMod -- */
+ case Iop_DivModU64to32: {
+ ULong u64a = e->Iex.Binop.arg1->Iex.Const.con->Ico.U64;
+ UInt u32b = e->Iex.Binop.arg2->Iex.Const.con->Ico.U32;
+ if (u32b != 0) {
+ ULong q = u64a / u32b;
+ /* Can q be represented in 32 bit? */
+ if (q <= 0xFFFFFFFF) {
+ UInt r = u64a % u32b;
+ e2 = IRExpr_Const(IRConst_U64(((ULong)r << 32) | (UInt)q));
+ }
+ }
+ break;
+ }
+ case Iop_DivModS64to32: {
+ Long s64a = e->Iex.Binop.arg1->Iex.Const.con->Ico.U64;
+ Int s32b = e->Iex.Binop.arg2->Iex.Const.con->Ico.U32;
+ if (s32b != 0) {
+ /* Division may trap when result overflows i.e. when
+ attempting: INT64_MAX / -1 */
+ if (e->Iex.Binop.arg1->Iex.Const.con->Ico.U64 == (1ULL << 63)
+ && s32b == -1)
+ break;
+ Long q = s64a / s32b;
+ /* Can q be represented in 32 bit? */
+ if (q >= (-2147483647-1) && q <= 2147483647) {
+ Int r = s64a % s32b;
+ e2 = IRExpr_Const(IRConst_U64(((ULong)r << 32) | (UInt)q));
+ }
+ }
+ break;
+ }
+
/* -- Shl -- */
case Iop_Shl8:
vassert(e->Iex.Binop.arg2->Iex.Const.con->tag == Ico_U8);
static void run_tests(const irop_t *, test_data_t *);
static void run_shift_tests(const irop_t *, test_data_t *);
static int is_shift_op(IROp);
-static int is_division_op(IROp);
+static int ok_to_run(IROp op, uint64_t, uint64_t);
void
for (unsigned j = 0; j < num_val_r; ++j) {
opnd_r->value = values_r[j];
- if (is_division_op(op->op) && opnd_r->value == 0) continue;
-
- valgrind_execute_test(op, data, get_expected_value(op, data));
+ if (ok_to_run(op->op, opnd_l->value, opnd_r->value))
+ valgrind_execute_test(op, data, get_expected_value(op, data));
}
}
}
/* 1-bit wide operands are tested exhaustively. Skip random tests. */
if (opnd_l->type == Ity_I1 && opnd_r->type == Ity_I1) return;
- for (unsigned i = 0; i < num_random_tests; ++i) {
+ unsigned num_tests = 0;
+ while (num_tests < num_random_tests) {
opnd_l->value = get_random_value(opnd_l->type);
opnd_r->value = get_random_value(opnd_r->type);
- if (is_division_op(op->op) && opnd_r->value == 0) continue;
-
- valgrind_execute_test(op, data, get_expected_value(op, data));
+ if (ok_to_run(op->op, opnd_l->value, opnd_r->value)) {
+ valgrind_execute_test(op, data, get_expected_value(op, data));
+ ++num_tests;
+ }
}
}
expected = (int64_t)(opnd_l << 32) / (int32_t)opnd_r;
break;
+ case Iop_DivModU64to32: {
+ uint64_t q = opnd_l / opnd_r;
+ uint64_t r = opnd_l % opnd_r;
+ expected = (r << 32) | q;
+ break;
+ }
+
+ case Iop_DivModS64to32: {
+ int64_t q = (int64_t)opnd_l / (int32_t)opnd_r;
+ int32_t r = (int64_t)opnd_l % (int32_t)opnd_r;
+ expected = ((uint64_t)r << 32) | (uint32_t)q;
+ break;
+ }
+
case Iop_Shl8:
case Iop_Shl16:
case Iop_Shl32:
static int
-is_division_op(IROp op)
+ok_to_run(IROp op, uint64_t o1, uint64_t o2)
{
switch (op) {
+ /* Division by zero -- not good */
case Iop_DivU32: case Iop_DivU64:
case Iop_DivS32: case Iop_DivS64:
case Iop_DivU32E:
case Iop_DivS32E:
- return 1;
+ return o2 != 0;
+
+ /* Check that result can be represented */
+ case Iop_DivModU64to32: {
+ uint64_t dividend = o1;
+ uint32_t divisor = o2;
+
+ if (divisor == 0) return 0;
+ uint64_t q = dividend / divisor; // always safe
+ return q <= UINT32_MAX;
+ }
+
+ case Iop_DivModS64to32: {
+ int64_t dividend = o1;
+ int32_t divisor = o2;
+
+ if (divisor == 0) return 0;
+ /* Division may trap on overflow */
+ if (divisor == -1 && o1 == (0x1ULL << 63)) // INT64_MIN
+ return 0;
+ int64_t q = dividend / divisor;
+ return q <= INT32_MAX && q >= INT32_MIN;
+ }
+
default:
- return 0;
+ return 1;
}
}
// { OPNAME(DivS128E), Ity_I128, 2, Ity_I128, Ity_I128 }, // 128 bit
// { OPNAME(DivModU32to32), Ity_I64, 2, Ity_I32, Ity_I64 }, // no folding yet
-// { OPNAME(DivModU64to32), Ity_I64, 2, Ity_I32, Ity_I64 }, // no folding yet
+ { OPNAME(DivModU64to32), Ity_I64, 2, Ity_I64, Ity_I32, EXCEPT(ppc) },
// { OPNAME(DivModU64to64), Ity_I64, 2, Ity_I64, Ity_I128 }, // 128 bit
// { OPNAME(DivModU128to64), Ity_I128, 2, Ity_I64, Ity_I128 }, // 128 bit
// { OPNAME(DivModS32to32), Ity_I64, 2, Ity_I32, Ity_I32 }, // no folding yet
-// { OPNAME(DivModS32to32), Ity_I64, 2, Ity_I32, Ity_I64 }, // no folding yet
+ { OPNAME(DivModS64to32), Ity_I64, 2, Ity_I64, Ity_I32, EXCEPT(ppc) },
// { OPNAME(DivModS64to64), Ity_I64, 2, Ity_I64, Ity_I128 }, // 128 bit
// { OPNAME(DivModU128to64), Ity_I128, 2, Ity_I64, Ity_I128 }, // 128 bit
if (op->result_type != t_res ||
op->opnd1_type != t_opnd1 ||
(op->num_opnds == 2 && op->opnd2_type != t_opnd2))
- fprintf(stderr, "%s: type mismatch\n", op->name);
+ panic("%s: type mismatch\n", op->name);
}
}
{
static const uint64_t values_1bit[] = { 0, 1 };
static const uint64_t values_8bit[] = { 0, 1, 2,
- UINT8_MAX - 1, UINT8_MAX };
+ INT8_MAX - 1, INT8_MAX, (uint8_t)INT8_MIN, (uint8_t)INT8_MIN + 1,
+ UINT8_MAX - 1, UINT8_MAX };
static const uint64_t values_16bit[] = { 0, 1, 2,
+ INT8_MAX - 1, INT8_MAX, (uint8_t)INT8_MIN, (uint8_t)INT8_MIN + 1,
UINT8_MAX - 1, UINT8_MAX, UINT8_MAX + 1,
+ INT16_MAX - 1, INT16_MAX, (uint16_t)INT16_MIN, (uint16_t)INT16_MIN + 1,
UINT16_MAX - 1, UINT16_MAX };
static const uint64_t values_32bit[] = { 0, 1, 2,
- UINT8_MAX - 1, UINT8_MAX, UINT8_MAX + 1,
+ INT8_MAX - 1, INT8_MAX, (uint8_t)INT8_MIN, (uint8_t)INT8_MIN + 1,
+ UINT8_MAX - 1, UINT8_MAX, UINT8_MAX + 1,
+ INT16_MAX - 1, INT16_MAX, (uint16_t)INT16_MIN, (uint16_t)INT16_MIN + 1,
UINT16_MAX - 1, UINT16_MAX, UINT16_MAX + 1,
+ INT32_MAX - 1, INT32_MAX, (uint32_t)INT32_MIN, (uint32_t)INT32_MIN + 1,
UINT32_MAX - 1, UINT32_MAX };
static const uint64_t values_64bit[] = { 0, 1, 2,
- UINT8_MAX - 1, UINT8_MAX, UINT8_MAX + 1,
+ INT8_MAX - 1, INT8_MAX, (uint8_t)INT8_MIN, (uint8_t)INT8_MIN + 1,
+ UINT8_MAX - 1, UINT8_MAX, UINT8_MAX + 1,
+ INT16_MAX - 1, INT16_MAX, (uint16_t)INT16_MIN, (uint16_t)INT16_MIN + 1,
UINT16_MAX - 1, UINT16_MAX, UINT16_MAX + 1,
+ INT32_MAX - 1, INT32_MAX, (uint32_t)INT32_MIN, (uint32_t)INT32_MIN + 1,
UINT32_MAX - 1, UINT32_MAX, (uint64_t)UINT32_MAX + 1,
+ INT64_MAX - 1, INT64_MAX, (uint64_t)INT64_MIN, (uint64_t)INT64_MIN + 1,
UINT64_MAX - 1, UINT64_MAX };
switch (type) {