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:
parent
ff8ba86f44
commit
b8a003c165
5 changed files with 104 additions and 7 deletions
|
@ -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.
|
||||||
|
|
|
@ -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.
|
||||||
|
|
||||||
|
|
31
gcc/testsuite/gcc.dg/tree-ssa/pr88367.c
Normal file
31
gcc/testsuite/gcc.dg/tree-ssa/pr88367.c
Normal 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 ();
|
||||||
|
}
|
|
@ -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)
|
||||||
&& 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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue