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
This commit is contained in:
Jakub Jelinek 2018-12-07 00:28:04 +01:00
parent ff8ba86f44
commit b8a003c165
5 changed files with 104 additions and 7 deletions

View 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 * cselib.c (cselib_record_sets): Skip strict low part sets
with NULL src_elt. with NULL src_elt.

View file

@ -1,5 +1,8 @@
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.

View file

@ -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 ();
}

View file

@ -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);

View file

@ -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;
} }
} }