On Thu, Jan 23, 2025 at 6:58 AM Alexandre Oliva <[email protected]> wrote:
>
> On Jan 22, 2025, Alexandre Oliva <[email protected]> wrote:
>
> > I have another patch coming up that doesn't raise concerns for me, so
> > I'll hold off from installing the above, in case you also prefer the
> > other one.
>
> Unlike other access patterns, BIT_FIELD_REFs aren't regarded as
> possibly-trapping out of referencing out-of-bounds bits.
>
> So, if decode_field_reference finds a load that could trap, but whose
> inner object can't, bail out if it accesses past the inner object's
> size.
>
> This may drop some optimizations in VLAs, that could be safe if we
> managed to reuse the same pre-existing load for both compares, but
> those get optimized later (as in the new testcase). The cases of most
> interest are those that merge separate fields, and they necessarily
> involve new BIT_FIELD_REFs loads.
>
> Regstrapped on x86_64-linux-gnu. Ok to install instead of the other
> one?
>
>
> for gcc/ChangeLog
>
> PR tree-optimization/118514
> * gimple-fold.cc (decode_field_reference): Refuse to consider
> BIT_FIELD_REF of non-trapping object if the original load
> could trap for being out-of-bounds.
> (make_bit_field_ref): Check that replacement loads could trap
> as much as the original loads.
> (fold_truth_andor_for_ifcombine): Rearrange testing of
> reversep, and note that signbit needs not be concerned with
> it. Check that combinable loads have the same trapping
> status.
> * tree-eh.cc (bit_field_ref_in_bounds_p): New.
> (tree_could_trap_p): Call it on DECLs.
> * tree-eh.h (bit_field_ref_in_bounds_p): Declare.
>
> for gcc/testsuite/ChangeLog
>
> PR tree-optimization/118514
> * gcc.dg/field-merge-23.c: New.
> ---
> gcc/gimple-fold.cc | 27 +++++++++++++++++++++------
> gcc/testsuite/gcc.dg/field-merge-23.c | 19 +++++++++++++++++++
> gcc/tree-eh.cc | 30 +++++++++++++++++++++++++++++-
> gcc/tree-eh.h | 1 +
> 4 files changed, 70 insertions(+), 7 deletions(-)
> create mode 100644 gcc/testsuite/gcc.dg/field-merge-23.c
>
> diff --git a/gcc/gimple-fold.cc b/gcc/gimple-fold.cc
> index da3f505c3fcac..cd9d350349186 100644
> --- a/gcc/gimple-fold.cc
> +++ b/gcc/gimple-fold.cc
> @@ -7686,10 +7686,14 @@ decode_field_reference (tree *pexp, HOST_WIDE_INT
> *pbitsize,
> || bs <= shiftrt
> || offset != 0
> || TREE_CODE (inner) == PLACEHOLDER_EXPR
> - /* Reject out-of-bound accesses (PR79731). */
> - || (! AGGREGATE_TYPE_P (TREE_TYPE (inner))
> - && compare_tree_int (TYPE_SIZE (TREE_TYPE (inner)),
> - bp + bs) < 0)
> + /* Reject out-of-bound accesses (PR79731). BIT_FIELD_REFs aren't
> flagged
> + as tree_could_trap_p just because of out-of-range bits, so don't even
> + try to optimize loads that could trap if they access out-of-range
> bits
> + of an object that could not trap (PR118514). */
> + || ((! AGGREGATE_TYPE_P (TREE_TYPE (inner))
> + || (load && tree_could_trap_p (gimple_assign_rhs1 (load))
> + && !tree_could_trap_p (inner)))
> + && !bit_field_ref_in_bounds_p (inner, bs, bp))
The new bit_field_ref_in_bounds_p is really an
"access in bound of type" now, and IMO that's something
we should simply always guarantee here - the exception
might be again when we can't prove this due to the TYPE_SIZE
of 'inner' being not (poly-)constant, but those cases should always
end up trapping.
That said, it'd be a lot clearer if this simply read
|| !access_in_bounds_of_type_p (TREE_TYPE (inner), bs, bp)
without all the other weird conditions.
> || (INTEGRAL_TYPE_P (TREE_TYPE (inner))
> && !type_has_mode_precision_p (TREE_TYPE (inner))))
> return NULL_TREE;
> @@ -7859,6 +7863,11 @@ make_bit_field_load (location_t loc, tree inner, tree
> orig_inner, tree type,
> gimple *new_stmt = gsi_stmt (i);
> if (gimple_has_mem_ops (new_stmt))
> gimple_set_vuse (new_stmt, reaching_vuse);
> + gcc_checking_assert (! (gimple_assign_load_p (point)
> + && gimple_assign_load_p (new_stmt))
> + || (tree_could_trap_p (gimple_assign_rhs1 (point))
> + == tree_could_trap_p (gimple_assign_rhs1
> + (new_stmt))));
> }
>
> gimple_stmt_iterator gsi = gsi_for_stmt (point);
> @@ -8223,8 +8232,12 @@ fold_truth_andor_for_ifcombine (enum tree_code code,
> tree truth_type,
> std::swap (rl_loc, rr_loc);
> }
>
> + /* Check that the loads that we're trying to combine have the same vuse and
> + same trapping status. */
> if ((ll_load && rl_load)
> - ? gimple_vuse (ll_load) != gimple_vuse (rl_load)
> + ? (gimple_vuse (ll_load) != gimple_vuse (rl_load)
> + || (tree_could_trap_p (gimple_assign_rhs1 (ll_load))
> + != tree_could_trap_p (gimple_assign_rhs1 (rl_load))))
So that's to make the above added assert happy, right? At least I don't see any
other good reason to enforce this?
> : (!ll_load != !rl_load))
> return 0;
>
> @@ -8277,7 +8290,9 @@ fold_truth_andor_for_ifcombine (enum tree_code code,
> tree truth_type,
> else if (lr_reversep != rr_reversep
> || ! operand_equal_p (lr_inner, rr_inner, 0)
> || ((lr_load && rr_load)
> - ? gimple_vuse (lr_load) != gimple_vuse (rr_load)
> + ? (gimple_vuse (lr_load) != gimple_vuse (rr_load)
> + || (tree_could_trap_p (gimple_assign_rhs1 (lr_load))
> + != tree_could_trap_p (gimple_assign_rhs1 (rr_load))))
> : (!lr_load != !rr_load)))
> return 0;
>
> diff --git a/gcc/testsuite/gcc.dg/field-merge-23.c
> b/gcc/testsuite/gcc.dg/field-merge-23.c
> new file mode 100644
> index 0000000000000..96604d43c9dec
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/field-merge-23.c
> @@ -0,0 +1,19 @@
> +/* { dg-do run } */
> +/* { dg-options "-O" } */
> +
> +/* PR tree-optimization/118514 */
> +
> +/* Check that we don't create BIT_FIELD_REFs that could trap, because they
> are
> + assumed not to trap and could be pulled out of loops. */
> +
> +int a, c = 1;
> +unsigned char b[1], d;
> +int main() {
> + while (a || !c) {
> + signed char e = b[1000000000];
> + d = e < 0 || b[1000000000] > 1;
> + if (d)
> + __builtin_abort ();
> + }
> + return 0;
> +}
> diff --git a/gcc/tree-eh.cc b/gcc/tree-eh.cc
> index 1033b124e4df3..08a106965b43f 100644
> --- a/gcc/tree-eh.cc
> +++ b/gcc/tree-eh.cc
> @@ -2646,6 +2646,27 @@ range_in_array_bounds_p (tree ref)
> return true;
> }
>
> +/* Return true iff a BIT_FIELD_REF <EXPR, SIZE, OFFSET> would access a bit
> + range that is known to be in bounds for EXPR's type. */
> +
> +bool
> +bit_field_ref_in_bounds_p (tree expr, poly_uint64 size, poly_uint64 offset)
> +{
> + tree expr_size_tree;
> + poly_uint64 expr_size_max, min = offset, wid = size, max;
> +
> + expr_size_tree = TYPE_SIZE (TREE_TYPE (expr));
> + if (!expr_size_tree || !poly_int_tree_p (expr_size_tree, &expr_size_max))
> + return false;
> +
> + max = min + wid;
> + if (maybe_lt (max, min)
> + || maybe_lt (expr_size_max, max))
> + return false;
> +
> + return true;
> +}
> +
> /* Return true if EXPR can trap, as in dereferencing an invalid pointer
> location or floating point arithmetic. C.f. the rtl version, may_trap_p.
> This routine expects only GIMPLE lhs or rhs input. */
> @@ -2688,10 +2709,17 @@ tree_could_trap_p (tree expr)
> restart:
> switch (code)
> {
> + case BIT_FIELD_REF:
> + if (DECL_P (TREE_OPERAND (expr, 0))
> + && !bit_field_ref_in_bounds_p (TREE_OPERAND (expr, 0),
> + bit_field_size (expr),
> + bit_field_offset (expr)))
> + return true;
> + /* Fall through. */
> +
> case COMPONENT_REF:
> case REALPART_EXPR:
> case IMAGPART_EXPR:
> - case BIT_FIELD_REF:
> case VIEW_CONVERT_EXPR:
> case WITH_SIZE_EXPR:
> expr = TREE_OPERAND (expr, 0);
> diff --git a/gcc/tree-eh.h b/gcc/tree-eh.h
> index 69fe193f1b82f..1c6f70b24fab7 100644
> --- a/gcc/tree-eh.h
> +++ b/gcc/tree-eh.h
> @@ -36,6 +36,7 @@ extern void redirect_eh_dispatch_edge (geh_dispatch *,
> edge, basic_block);
> extern bool operation_could_trap_helper_p (enum tree_code, bool, bool, bool,
> bool, tree, bool *);
> extern bool operation_could_trap_p (enum tree_code, bool, bool, tree);
> +extern bool bit_field_ref_in_bounds_p (tree, poly_uint64, poly_uint64);
> extern bool tree_could_trap_p (tree);
> extern tree rewrite_to_non_trapping_overflow (tree);
> extern bool stmt_could_throw_p (function *, gimple *);
>
>
> --
> Alexandre Oliva, happy hacker https://FSFLA.org/blogs/lxo/
> Free Software Activist GNU Toolchain Engineer
> More tolerance and less prejudice are key for inclusion and diversity
> Excluding neuro-others for not behaving ""normal"" is *not* inclusive