From: Jakub Jelinek Date: Thu, 6 Dec 2018 23:28:04 +0000 (+0100) Subject: re PR tree-optimization/88367 (-fno-delete-null-pointer-checks doesn't work properly) X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=b8a003c16567ccecffce256a2b35c9210db933a4;p=gcc.git 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 --- diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 2dc7f43f8d3..f2512ba68cb 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,4 +1,16 @@ -2018-12-06 Alexandre Oliva +2018-12-07 Jakub Jelinek + + 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 * cselib.c (cselib_record_sets): Skip strict low part sets with NULL src_elt. diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index 571f2b55d40..e4afdc79fdc 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,5 +1,8 @@ 2018-12-07 Jakub Jelinek + 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 index 00000000000..ff1c424df42 --- /dev/null +++ b/gcc/testsuite/gcc.dg/tree-ssa/pr88367.c @@ -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 (); +} diff --git a/gcc/tree-vrp.c b/gcc/tree-vrp.c index 6fd1fd26eef..15ac65b7dd4 100644 --- a/gcc/tree-vrp.c +++ b/gcc/tree-vrp.c @@ -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); diff --git a/gcc/vr-values.c b/gcc/vr-values.c index a0027c0b353..075ea8431a6 100644 --- a/gcc/vr-values.c +++ b/gcc/vr-values.c @@ -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; } }