Commit b8a003c1 by Jakub Jelinek

re PR tree-optimization/88367 (-fno-delete-null-pointer-checks doesn't work properly)

	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
parent ff8ba86f
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 * cselib.c (cselib_record_sets): Skip strict low part sets
with NULL src_elt. with NULL src_elt.
2018-12-07 Jakub Jelinek <jakub@redhat.com> 2018-12-07 Jakub Jelinek <jakub@redhat.com>
PR c/88367
* gcc.dg/tree-ssa/pr88367.c: New test.
PR c++/87506 PR c++/87506
* g++.dg/cpp0x/constexpr-87506.C: New test. * g++.dg/cpp0x/constexpr-87506.C: New test.
......
/* 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 ();
}
...@@ -1673,9 +1673,26 @@ extract_range_from_binary_expr (value_range_base *vr, ...@@ -1673,9 +1673,26 @@ extract_range_from_binary_expr (value_range_base *vr,
else if (code == POINTER_PLUS_EXPR) else if (code == POINTER_PLUS_EXPR)
{ {
/* For pointer types, we are really only interested in asserting /* For pointer types, we are really only interested in asserting
whether the expression evaluates to non-NULL. */ whether the expression evaluates to non-NULL.
if (!range_includes_zero_p (&vr0) With -fno-delete-null-pointer-checks we need to be more
|| !range_includes_zero_p (&vr1)) 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); vr->set_nonnull (expr_type);
else if (range_is_null (&vr0) && range_is_null (&vr1)) else if (range_is_null (&vr0) && range_is_null (&vr1))
vr->set_null (expr_type); vr->set_null (expr_type);
......
...@@ -297,14 +297,48 @@ vr_values::vrp_stmt_computes_nonzero (gimple *stmt) ...@@ -297,14 +297,48 @@ vr_values::vrp_stmt_computes_nonzero (gimple *stmt)
&& gimple_assign_rhs_code (stmt) == ADDR_EXPR) && gimple_assign_rhs_code (stmt) == ADDR_EXPR)
{ {
tree expr = gimple_assign_rhs1 (stmt); 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 if (base != NULL_TREE
&& TREE_CODE (base) == MEM_REF && TREE_CODE (base) == MEM_REF
&& TREE_CODE (TREE_OPERAND (base, 0)) == SSA_NAME) && TREE_CODE (TREE_OPERAND (base, 0)) == SSA_NAME)
{ {
value_range *vr = get_value_range (TREE_OPERAND (base, 0)); poly_offset_int off = 0;
if (!range_includes_zero_p (vr)) 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; return true;
} }
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment