[PATCH] Fix VRP with -fno-delete-null-pointer-checks (PR c/88367, take 2)
Richard Biener
rguenther@suse.de
Thu Dec 6 12:30:00 GMT 2018
On Thu, 6 Dec 2018, Jakub Jelinek wrote:
> 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?
Little bit more expensive than before but OK.
Thanks,
Richard.
> 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
>
>
--
Richard Biener <rguenther@suse.de>
SUSE LINUX GmbH, GF: Felix Imendoerffer, Jane Smithard, Graham Norton, HRB 21284 (AG Nuernberg)
More information about the Gcc-patches
mailing list