re PR tree-optimization/88367 (-fno-delete-null-pointer-checks doesn't work properly)
authorJakub Jelinek <jakub@gcc.gnu.org>
Thu, 6 Dec 2018 23:28:04 +0000 (00:28 +0100)
committerJakub Jelinek <jakub@gcc.gnu.org>
Thu, 6 Dec 2018 23:28:04 +0000 (00:28 +0100)
PR c/88367
* tree-vrp.c (extract_range_from_binary_expr): For POINTER_PLUS_EXPR
with -fno-delete-null-pointer-checks, set_nonnull only if the pointer
is non-NULL and offset is known to have most significant bit clear.
* vr-values.c (vr_values::vrp_stmt_computes_nonzero): For ADDR_EXPR
of MEM_EXPR, return true if the MEM_EXPR has non-zero offset with
most significant bit clear.  If offset does have most significant bit
set and -fno-delete-null-pointer-checks, don't return true even if
the base pointer is non-NULL.

* gcc.dg/tree-ssa/pr88367.c: New test.

From-SVN: r266878

gcc/ChangeLog
gcc/testsuite/ChangeLog
gcc/testsuite/gcc.dg/tree-ssa/pr88367.c [new file with mode: 0644]
gcc/tree-vrp.c
gcc/vr-values.c

index 2dc7f43f8d3538fa4bcee81240c4d4b441014c05..f2512ba68cb573fd14c681f9b173bbae79b84e25 100644 (file)
@@ -1,4 +1,16 @@
-2018-12-06  Alexandre Oliva <aoliva@redhat.com>
+2018-12-07  Jakub Jelinek  <jakub@redhat.com>
+
+       PR c/88367
+       * tree-vrp.c (extract_range_from_binary_expr): For POINTER_PLUS_EXPR
+       with -fno-delete-null-pointer-checks, set_nonnull only if the pointer
+       is non-NULL and offset is known to have most significant bit clear.
+       * vr-values.c (vr_values::vrp_stmt_computes_nonzero): For ADDR_EXPR
+       of MEM_EXPR, return true if the MEM_EXPR has non-zero offset with
+       most significant bit clear.  If offset does have most significant bit
+       set and -fno-delete-null-pointer-checks, don't return true even if
+       the base pointer is non-NULL.
+
+2018-12-06  Alexandre Oliva  <aoliva@redhat.com>
 
        * cselib.c (cselib_record_sets): Skip strict low part sets
        with NULL src_elt.
index 571f2b55d403d2b36a315a34c1158f7b48cd7b41..e4afdc79fdcd63102bfebe4a66ea68305b46bfd4 100644 (file)
@@ -1,5 +1,8 @@
 2018-12-07  Jakub Jelinek  <jakub@redhat.com>
 
+       PR c/88367
+       * gcc.dg/tree-ssa/pr88367.c: New test.
+
        PR c++/87506
        * g++.dg/cpp0x/constexpr-87506.C: New test.
 
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/pr88367.c b/gcc/testsuite/gcc.dg/tree-ssa/pr88367.c
new file mode 100644 (file)
index 0000000..ff1c424
--- /dev/null
@@ -0,0 +1,31 @@
+/* PR c/88367 */
+/* { dg-do compile } */
+/* { dg-options "-fno-delete-null-pointer-checks -O2 -fdump-tree-optimized -fno-wrapv-pointer" } */
+/* { dg-final { scan-tree-dump-not "link_error \\(\\);" "optimized" } } */
+/* { dg-final { scan-tree-dump-times "bar \\(\\);" 2 "optimized" } } */
+
+void bar (void);
+void link_error (void);
+
+void
+foo (char *p)
+{
+  if (!p)
+    return;
+  p += 3;
+  if (!p)
+    link_error ();
+  p -= 6;
+  if (!p)
+    bar ();
+}
+
+void
+baz (char *p)
+{
+  if (!p)
+    return;
+  p -= 6;
+  if (!p)
+    bar ();
+}
index 6fd1fd26eefa2758de1882f159ffea7400e40737..15ac65b7dd435a714c6a3b8418a2c5e9749fa52a 100644 (file)
@@ -1673,9 +1673,26 @@ extract_range_from_binary_expr (value_range_base *vr,
       else if (code == POINTER_PLUS_EXPR)
        {
          /* For pointer types, we are really only interested in asserting
-            whether the expression evaluates to non-NULL.  */
-         if (!range_includes_zero_p (&vr0)
-             || !range_includes_zero_p (&vr1))
+            whether the expression evaluates to non-NULL.
+            With -fno-delete-null-pointer-checks we need to be more
+            conservative.  As some object might reside at address 0,
+            then some offset could be added to it and the same offset
+            subtracted again and the result would be NULL.
+            E.g.
+            static int a[12]; where &a[0] is NULL and
+            ptr = &a[6];
+            ptr -= 6;
+            ptr will be NULL here, even when there is POINTER_PLUS_EXPR
+            where the first range doesn't include zero and the second one
+            doesn't either.  As the second operand is sizetype (unsigned),
+            consider all ranges where the MSB could be set as possible
+            subtractions where the result might be NULL.  */
+         if ((!range_includes_zero_p (&vr0)
+              || !range_includes_zero_p (&vr1))
+             && !TYPE_OVERFLOW_WRAPS (expr_type)
+             && (flag_delete_null_pointer_checks
+                 || (range_int_cst_p (&vr1)
+                     && !tree_int_cst_sign_bit (vr1.max ()))))
            vr->set_nonnull (expr_type);
          else if (range_is_null (&vr0) && range_is_null (&vr1))
            vr->set_null (expr_type);
index a0027c0b3531ecb7517e2b0163e2550c8423299a..075ea8431a6baa7de9c716f068713955b7e04ac6 100644 (file)
@@ -297,14 +297,48 @@ vr_values::vrp_stmt_computes_nonzero (gimple *stmt)
       && gimple_assign_rhs_code (stmt) == ADDR_EXPR)
     {
       tree expr = gimple_assign_rhs1 (stmt);
-      tree base = get_base_address (TREE_OPERAND (expr, 0));
+      poly_int64 bitsize, bitpos;
+      tree offset;
+      machine_mode mode;
+      int unsignedp, reversep, volatilep;
+      tree base = get_inner_reference (TREE_OPERAND (expr, 0), &bitsize,
+                                      &bitpos, &offset, &mode, &unsignedp,
+                                      &reversep, &volatilep);
 
       if (base != NULL_TREE
          && TREE_CODE (base) == MEM_REF
          && TREE_CODE (TREE_OPERAND (base, 0)) == SSA_NAME)
        {
-         value_range *vr = get_value_range (TREE_OPERAND (base, 0));
-         if (!range_includes_zero_p (vr))
+         poly_offset_int off = 0;
+         bool off_cst = false;
+         if (offset == NULL_TREE || TREE_CODE (offset) == INTEGER_CST)
+           {
+             off = mem_ref_offset (base);
+             if (offset)
+               off += poly_offset_int::from (wi::to_poly_wide (offset),
+                                             SIGNED);
+             off <<= LOG2_BITS_PER_UNIT;
+             off += bitpos;
+             off_cst = true;
+           }
+         /* If &X->a is equal to X and X is ~[0, 0], the result is too.
+            For -fdelete-null-pointer-checks -fno-wrapv-pointer we don't
+            allow going from non-NULL pointer to NULL.  */
+         if ((off_cst && known_eq (off, 0))
+             || (flag_delete_null_pointer_checks
+                 && !TYPE_OVERFLOW_WRAPS (TREE_TYPE (expr))))
+           {
+             value_range *vr = get_value_range (TREE_OPERAND (base, 0));
+             if (!range_includes_zero_p (vr))
+               return true;
+           }
+         /* If MEM_REF has a "positive" offset, consider it non-NULL
+            always, for -fdelete-null-pointer-checks also "negative"
+            ones.  Punt for unknown offsets (e.g. variable ones).  */
+         if (!TYPE_OVERFLOW_WRAPS (TREE_TYPE (expr))
+             && off_cst
+             && known_ne (off, 0)
+             && (flag_delete_null_pointer_checks || known_gt (off, 0)))
            return true;
        }
     }