This is the mail archive of the gcc-patches@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[PATCH] Fix VRP with -fno-delete-null-pointer-checks (PR c/88367, take 2)


On Thu, Dec 06, 2018 at 10:05:15AM +0100, Richard Biener wrote:
> Note I wonder if with -fwrapv-pointer NULL automatically becomes a
> valid address?  Or is only wrapping around half of the address
> space UB?

Hadn't thought about -fwrapv-pointer, I guess we (especially with
-fno-delete-null-pointer-checks) need to be even more conservative in that
case.

Furthermore, I've discovered that the ADDR_EXPR of MEM_REF case actually
uses get_base_address and therefore the offset on MEM_REF is just one of the
many possible offsets in the play.

So, this patch punts for -fwrapv-pointer in some further cases, and
adjusts the vr-values.c ADDR_EXPR handling code so that it sums up all 2 or
3 offsets together and looks at the resulting sign.  If
-fdelete-null-pointer-checks -fno-wrapv-pointer, it does what it did before
in tree-vrp.c and in vr-values.c is even more aggressive than before, as in
even if the base pointer is varying etc., if the sum of all the offsets
is provably non-zero, the result is non-NULL.  For
-fno-delete-null-pointer-checks -fno-wrapv-pointer it does this only if the
resulting offset is positive.

Does this look ok?

2018-12-06  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.

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

--- gcc/tree-vrp.c.jj	2018-12-06 11:19:24.170939864 +0100
+++ gcc/tree-vrp.c	2018-12-06 11:50:12.104711210 +0100
@@ -1673,9 +1673,26 @@ extract_range_from_binary_expr (value_ra
       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);
--- gcc/vr-values.c.jj	2018-12-06 11:19:23.550950006 +0100
+++ gcc/vr-values.c	2018-12-06 12:59:28.269999920 +0100
@@ -297,14 +297,48 @@ vr_values::vrp_stmt_computes_nonzero (gi
       && 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;
 	}
     }
--- gcc/testsuite/gcc.dg/tree-ssa/pr88367.c.jj	2018-12-06 11:46:51.915985811 +0100
+++ gcc/testsuite/gcc.dg/tree-ssa/pr88367.c	2018-12-06 13:00:14.692248340 +0100
@@ -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 ();
+}


	Jakub


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]