Hi!

For TImode and wider, neg_const_int returns wrong value for
(const_int 0x8000000000000000) - the argument unmodified.
The function comment above the function kind of suggests that it
truncates, but I fail to see which caller might benefit from such
behavior, it would mean having to guard against the problematic
value in all the spots.
This patch fixes it by returning correct return value from the function,
and in one caller which assumed it would always return a CONST_INT
rather than CONST_DOUBLE/CONST_WIDE_INT changes it to optimize
only if CONST_INT has been returned.

Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk?

2016-11-28  Jakub Jelinek  <ja...@redhat.com>

        PR rtl-optimization/78546
        * simplify-rtx.c (neg_const_int): When negating most negative
        number in mode wider than HOST_BITS_PER_WIDE_INT, use
        simplify_const_unary_operation to produce CONST_DOUBLE or
        CONST_WIDE_INT.
        (simplify_plus_minus): Hanlde the case where neg_const_int
        doesn't return a CONST_INT.

        * gcc.dg/torture/pr78546-1.c: New test.
        * gcc.dg/torture/pr78546-2.c: New test.

--- gcc/simplify-rtx.c.jj       2016-11-25 19:55:59.000000000 +0100
+++ gcc/simplify-rtx.c  2016-11-28 11:35:05.796845942 +0100
@@ -56,12 +56,17 @@ static rtx simplify_unary_operation_1 (e
 static rtx simplify_binary_operation_1 (enum rtx_code, machine_mode,
                                        rtx, rtx, rtx, rtx);
 
-/* Negate a CONST_INT rtx, truncating (because a conversion from a
-   maximally negative number can overflow).  */
+/* Negate a CONST_INT rtx.  */
 static rtx
 neg_const_int (machine_mode mode, const_rtx i)
 {
-  return gen_int_mode (-(unsigned HOST_WIDE_INT) INTVAL (i), mode);
+  unsigned HOST_WIDE_INT val = -UINTVAL (i);
+  
+  if (GET_MODE_PRECISION (mode) > HOST_BITS_PER_WIDE_INT
+      && val == UINTVAL (i))
+    return simplify_const_unary_operation (NEG, mode, CONST_CAST_RTX (i),
+                                          mode);
+  return gen_int_mode (val, mode);
 }
 
 /* Test whether expression, X, is an immediate constant that represents
@@ -4507,9 +4512,12 @@ simplify_plus_minus (enum rtx_code code,
       rtx value = ops[n_ops - 1].op;
       if (ops[n_ops - 1].neg ^ ops[n_ops - 2].neg)
        value = neg_const_int (mode, value);
-      ops[n_ops - 2].op = plus_constant (mode, ops[n_ops - 2].op,
-                                        INTVAL (value));
-      n_ops--;
+      if (CONST_INT_P (value))
+       {
+         ops[n_ops - 2].op = plus_constant (mode, ops[n_ops - 2].op,
+                                            INTVAL (value));
+         n_ops--;
+       }
     }
 
   /* Put a non-negated operand first, if possible.  */
--- gcc/testsuite/gcc.dg/torture/pr78546-1.c.jj 2016-11-28 11:38:54.332925653 
+0100
+++ gcc/testsuite/gcc.dg/torture/pr78546-1.c    2016-11-28 11:39:39.041354528 
+0100
@@ -0,0 +1,22 @@
+/* PR rtl-optimization/78546 */
+/* { dg-do run { target int128 } } */
+
+typedef unsigned __int128 u128;
+u128 b;
+
+static inline u128
+foo (u128 p1)
+{
+  p1 += ~b;
+  return -p1;
+}
+
+int
+main ()
+{
+  asm volatile ("" : : : "memory");
+  u128 x = foo (~0x7fffffffffffffffLL);
+  if (x != 0x8000000000000001ULL)
+    __builtin_abort ();
+  return 0;
+}
--- gcc/testsuite/gcc.dg/torture/pr78546-2.c.jj 2016-11-28 11:38:33.165196059 
+0100
+++ gcc/testsuite/gcc.dg/torture/pr78546-2.c    2016-11-28 11:38:07.000000000 
+0100
@@ -0,0 +1,16 @@
+/* PR rtl-optimization/78546 */
+/* { dg-do run { target int128 } } */
+
+typedef unsigned __int128 u128;
+u128 b;
+
+int
+main ()
+{
+  asm volatile ("" : : : "memory");
+  u128 x = ((u128) ~0x7fffffffffffffffLL) - b;
+  u128 y = 1 - x;
+  if (y != 0x8000000000000001ULL)
+    __builtin_abort ();
+  return 0;
+}

        Jakub

Reply via email to