]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
Widen-Mul: Fix mis-compile for build_and_insert_cast refinement [PR122021]
authorPan Li <pan2.li@intel.com>
Mon, 22 Sep 2025 06:31:41 +0000 (14:31 +0800)
committerPan Li <pan2.li@intel.com>
Mon, 22 Sep 2025 12:59:27 +0000 (20:59 +0800)
The previous refinement in build_and_insert_cast will convert 2
cast into one, aka:

uint16_t _3;

From:
int16_t _4 = (uint16_t)_3; // no-extend
int32_t _5 = (int32_t)_4   // sign-extend 16 => 32

To:
int32_t _5 = (int32_t)_3;  // zero-extend 16 => 32

That will have a problem for sign-extend, the highest bits may be all 1s
but will be loss after convert to zero-extend.  Thus, there will be more
cases if the convert has different types.  Case 1 as above and Case 2,
3, and 4 as following.

Case 2:
  int16_t _3;

  From:
  uint32_t _4 = (uint32_t)_3; // zero-extend 16 => 32
  uint64_t _5 = (uint64_t)_4; // zero-extend 32 => 64

  To:
  uint64_t _5 = (uint32_t)_3; // zero-extend 16 => 64

Case 3:
  uint8_t _3;

  From:
  uint16_t _4 = (uint16_t)_3; // zero-extend 8 => 16
  int32_t _5 = (int32_t)_4;   // zero-extend 16 => 32

  To:
  int32_t _5 = (int32_t)_3;   // zero-extend 8 => 32

Case 4:
  int8_t _3;

  From:
  int16_t _4 = (int16_t)_3;   // sign-extend 8 => 16
  uint32_t _5 = (uint32_t)_4; // zero-extend 16 => 32
  To:
  uint32_t _5 = (uint32_t)_3; // zero-extend 8 => 32

Then, we can see, there will be mis-compile if and only if there is
a cast from small to big size with sign extend.  Thus, restrict the
check and stop prop if there is sign extend cast.

The below test suites are passed for this patch:
1. The rv64gcv fully regression tests.
2. The x86 bootstrap tests.
3. The x86 fully regression tests.

PR middle-end/122021

gcc/ChangeLog:

* tree-ssa-math-opts.cc (build_and_insert_cast): Add sign-extend
check before prop.

gcc/testsuite/ChangeLog:

* gcc.target/i386/pr122021-0.c: New test.

Signed-off-by: Pan Li <pan2.li@intel.com>
gcc/testsuite/gcc.target/i386/pr122021-0.c [new file with mode: 0644]
gcc/tree-ssa-math-opts.cc

diff --git a/gcc/testsuite/gcc.target/i386/pr122021-0.c b/gcc/testsuite/gcc.target/i386/pr122021-0.c
new file mode 100644 (file)
index 0000000..de17734
--- /dev/null
@@ -0,0 +1,22 @@
+/* { dg-do run } */
+/* { dg-options "-O2 -m32" } */
+
+#include <stddef.h>
+#include <stdint.h>
+
+__attribute__ ((noipa)) static void
+vp9_build_inter_predictor (int a)
+{
+  int16_t row = a * 2;
+  int32_t row_w = (int)((int64_t)row * 16384 >> 14);
+
+  if (row_w != -544)
+    __builtin_abort ();
+}
+
+int
+main ()
+{
+  vp9_build_inter_predictor (-272);
+  return 0;
+}
index 344ffddd3859816c02f46781b3740ebf0c0f2758..80d10d26f67880b23602e2a6537c1508629a591f 100644 (file)
@@ -1642,12 +1642,24 @@ build_and_insert_cast (gimple_stmt_iterator *gsi, location_t loc,
          && CONVERT_EXPR_CODE_P (gimple_assign_rhs_code (def)))
        {
          tree cast_rhs = gimple_assign_rhs1 (def);
-         unsigned rhs_prec = TYPE_PRECISION (TREE_TYPE (cast_rhs));
+         tree cast_rhs_type = TREE_TYPE (cast_rhs);
+         tree val_type = TREE_TYPE (val);
+
+         bool unsigned_p = TYPE_UNSIGNED (type);
+         bool unsigned_rhs_p = TYPE_UNSIGNED (cast_rhs_type);
+         bool unsigned_val_p = TYPE_UNSIGNED (val_type);
+
+         unsigned rhs_prec = TYPE_PRECISION (cast_rhs_type);
          unsigned type_prec = TYPE_PRECISION (type);
-         unsigned val_prec = TYPE_PRECISION (TREE_TYPE (val));
+         unsigned val_prec = TYPE_PRECISION (val_type);
 
          if (type_prec >= rhs_prec && val_prec >= rhs_prec)
-           rhs = cast_rhs;
+           {
+             /* Aka any sign extend from small to big size */
+             if (!((val_prec > rhs_prec && !unsigned_val_p && !unsigned_rhs_p)
+                 || (type_prec > val_prec && !unsigned_p && !unsigned_val_p)))
+               rhs = rhs;
+           }
        }
     }