]> git.ipfire.org Git - thirdparty/valgrind.git/commitdiff
iropt-test: Constant folding for Iop_DivModU64to32 and Iop_DivModS64to32
authorFlorian Krohm <flo2030@eich-krohm.de>
Thu, 28 Aug 2025 20:17:29 +0000 (20:17 +0000)
committerFlorian Krohm <flo2030@eich-krohm.de>
Thu, 28 Aug 2025 20:18:30 +0000 (20:18 +0000)
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

VEX/priv/ir_opt.c
none/tests/iropt-test/binary.c
none/tests/iropt-test/irops.tab
none/tests/iropt-test/main.c
none/tests/iropt-test/util.c

index 100f6396f92272905f66b9dde003c9f4c6bc4f07..5056b677bea8276e414357412a20866a2955e220 100644 (file)
@@ -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);
index 896d57630ed8ca06f0ae328465670f23fc08b504..1fbe1341fc994f2d1470f00f3952b7f8dd2274ad 100644 (file)
@@ -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;
    }
 }
index 4163edfcd57fc918dc2d916c8ff7bb4f7dd9b946..9f833b5cb5afffd08609ad99eb6387dfbc7e19ba 100644 (file)
 // { 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
 
index d446fd78f6f128da9981ee02d8e14628ee6f2619..1ca97e9c03e5d646154e6b59e7b041280650b41c 100644 (file)
@@ -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);
    }
 }
 
index 4ce30e2a5f4f6ccbd57eec7d94f381e06215b1f2..275c2b4cb8440590a34a4f7a83d1d916052942b8 100644 (file)
@@ -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) {