This fixes PR54027, VRP treating overflow in signed left-shifts undefined.

Bootstrap and regtest pending on x86_64-unknown-linux-gnu.

Richard.

2012-08-09  Richard Guenther  <rguent...@suse.de>

        PR tree-optimization/54027
        * tree-vrp.c (extract_range_from_binary_expr_1): Merge RSHIFT_EXPR
        and LSHIFT_EXPR handling, force -fwrapv for the multiplication used
        to handle LSHIFT_EXPR with a constant.

        * gcc.dg/torture/pr54027.c: New testcase.

Index: gcc/tree-vrp.c
===================================================================
*** gcc/tree-vrp.c      (revision 190252)
--- gcc/tree-vrp.c      (working copy)
*************** extract_range_from_binary_expr_1 (value_
*** 2726,2782 ****
        extract_range_from_multiplicative_op_1 (vr, code, &vr0, &vr1);
        return;
      }
!   else if (code == RSHIFT_EXPR)
      {
        /* If we have a RSHIFT_EXPR with any shift values outside [0..prec-1],
         then drop to VR_VARYING.  Outside of this range we get undefined
         behavior from the shift operation.  We cannot even trust
         SHIFT_COUNT_TRUNCATED at this stage, because that applies to rtl
         shifts, and the operation at the tree level may be widened.  */
!       if (vr1.type != VR_RANGE
!         || !value_range_nonnegative_p (&vr1)
!         || TREE_CODE (vr1.max) != INTEGER_CST
!         || compare_tree_int (vr1.max, TYPE_PRECISION (expr_type) - 1) == 1)
        {
!         set_value_range_to_varying (vr);
!         return;
        }
- 
-       extract_range_from_multiplicative_op_1 (vr, code, &vr0, &vr1);
-       return;
-     }
-   else if (code == LSHIFT_EXPR)
-     {
-       /* If we have a LSHIFT_EXPR with any shift values outside [0..prec-1],
-        then drop to VR_VARYING.  Outside of this range we get undefined
-        behavior from the shift operation.  We cannot even trust
-        SHIFT_COUNT_TRUNCATED at this stage, because that applies to rtl
-        shifts, and the operation at the tree level may be widened.  */
-       if (vr1.type != VR_RANGE
-         || !value_range_nonnegative_p (&vr1)
-         || TREE_CODE (vr1.max) != INTEGER_CST
-         || compare_tree_int (vr1.max, TYPE_PRECISION (expr_type) - 1) == 1)
-       {
-         set_value_range_to_varying (vr);
-         return;
-       }
- 
-       /* We can map shifts by constants to MULT_EXPR handling.  */
-       if (range_int_cst_singleton_p (&vr1))
-       {
-         value_range_t vr1p = VR_INITIALIZER;
-         vr1p.type = VR_RANGE;
-         vr1p.min
-           = double_int_to_tree (expr_type,
-                                 double_int_lshift (double_int_one,
-                                                    TREE_INT_CST_LOW (vr1.min),
-                                                    TYPE_PRECISION (expr_type),
-                                                    false));
-         vr1p.max = vr1p.min;
-         extract_range_from_multiplicative_op_1 (vr, MULT_EXPR, &vr0, &vr1p);
-         return;
-       }
- 
        set_value_range_to_varying (vr);
        return;
      }
--- 2726,2773 ----
        extract_range_from_multiplicative_op_1 (vr, code, &vr0, &vr1);
        return;
      }
!   else if (code == RSHIFT_EXPR
!          || code == LSHIFT_EXPR)
      {
        /* If we have a RSHIFT_EXPR with any shift values outside [0..prec-1],
         then drop to VR_VARYING.  Outside of this range we get undefined
         behavior from the shift operation.  We cannot even trust
         SHIFT_COUNT_TRUNCATED at this stage, because that applies to rtl
         shifts, and the operation at the tree level may be widened.  */
!       if (range_int_cst_p (&vr1)
!         && compare_tree_int (vr1.min, 0) >= 0
!         && compare_tree_int (vr1.max, TYPE_PRECISION (expr_type)) == -1)
        {
!         if (code == RSHIFT_EXPR)
!           {
!             extract_range_from_multiplicative_op_1 (vr, code, &vr0, &vr1);
!             return;
!           }
!         /* We can map lshifts by constants to MULT_EXPR handling.  */
!         else if (code == LSHIFT_EXPR
!                  && range_int_cst_singleton_p (&vr1))
!           {
!             bool saved_flag_wrapv;
!             value_range_t vr1p = VR_INITIALIZER;
!             vr1p.type = VR_RANGE;
!             vr1p.min
!               = double_int_to_tree (expr_type,
!                                     double_int_lshift
!                                       (double_int_one,
!                                        TREE_INT_CST_LOW (vr1.min),
!                                        TYPE_PRECISION (expr_type),
!                                        false));
!             vr1p.max = vr1p.min;
!             /* We have to use a wrapping multiply though as signed overflow
!                on lshifts is implementation defined in C89.  */
!             saved_flag_wrapv = flag_wrapv;
!             flag_wrapv = 1;
!             extract_range_from_binary_expr_1 (vr, MULT_EXPR, expr_type,
!                                               &vr0, &vr1p);
!             flag_wrapv = saved_flag_wrapv;
!             return;
!           }
        }
        set_value_range_to_varying (vr);
        return;
      }
Index: gcc/testsuite/gcc.dg/torture/pr54027.c
===================================================================
*** gcc/testsuite/gcc.dg/torture/pr54027.c      (revision 0)
--- gcc/testsuite/gcc.dg/torture/pr54027.c      (working copy)
***************
*** 0 ****
--- 1,9 ----
+ /* { dg-do run } */
+ 
+ int main (void)
+ {
+   int x = 1;
+   while (x)
+     x <<= 1;
+   return x;
+ }

Reply via email to