From: Florian Krohm Date: Thu, 28 Aug 2025 20:17:29 +0000 (+0000) Subject: iropt-test: Constant folding for Iop_DivModU64to32 and Iop_DivModS64to32 X-Git-Tag: VALGRIND_3_26_0~189 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=bbf3cf234eacc97a652437c4f5e626c43b1bc76d;p=thirdparty%2Fvalgrind.git iropt-test: Constant folding for Iop_DivModU64to32 and Iop_DivModS64to32 Function get_selected_values has been tweaked to return more "interesting" values for test generation. Namely those that, when interpreted as a signed integer, are boundary (or near boundary) values. Also a bit of reorg because for the DivMod.. IROps we need to be a bit more careful when generating testcases that do not trap or where the result does not fit in 32 bit. Part of fixing https://bugs.kde.org/show_bug.cgi?id=506211 --- diff --git a/VEX/priv/ir_opt.c b/VEX/priv/ir_opt.c index 100f6396f..5056b677b 100644 --- a/VEX/priv/ir_opt.c +++ b/VEX/priv/ir_opt.c @@ -2161,6 +2161,39 @@ static IRExpr* fold_Expr_WRK ( IRExpr** env, IRExpr* e ) 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); diff --git a/none/tests/iropt-test/binary.c b/none/tests/iropt-test/binary.c index 896d57630..1fbe1341f 100644 --- a/none/tests/iropt-test/binary.c +++ b/none/tests/iropt-test/binary.c @@ -30,7 +30,7 @@ static uint64_t get_expected_value(const irop_t *, const test_data_t *); 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 @@ -57,9 +57,8 @@ run_selected_tests(const irop_t *op, test_data_t *data) 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)); } } } @@ -75,13 +74,15 @@ run_random_tests(const irop_t *op, test_data_t *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; + } } } @@ -214,6 +215,20 @@ get_expected_value(const irop_t *op, const test_data_t *data) 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: @@ -401,15 +416,39 @@ is_shift_op(IROp op) 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; } } diff --git a/none/tests/iropt-test/irops.tab b/none/tests/iropt-test/irops.tab index 4163edfcd..9f833b5cb 100644 --- a/none/tests/iropt-test/irops.tab +++ b/none/tests/iropt-test/irops.tab @@ -176,12 +176,12 @@ // { 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 diff --git a/none/tests/iropt-test/main.c b/none/tests/iropt-test/main.c index d446fd78f..1ca97e9c0 100644 --- a/none/tests/iropt-test/main.c +++ b/none/tests/iropt-test/main.c @@ -125,7 +125,7 @@ check_irops_table(void) 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); } } diff --git a/none/tests/iropt-test/util.c b/none/tests/iropt-test/util.c index 4ce30e2a5..275c2b4cb 100644 --- a/none/tests/iropt-test/util.c +++ b/none/tests/iropt-test/util.c @@ -103,18 +103,28 @@ get_selected_values(IRType type, unsigned *num_val) { 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) {