On Thu, Jan 23, 2025 at 6:58 AM Alexandre Oliva <ol...@adacore.com> wrote:
>
> On Jan 22, 2025, Alexandre Oliva <ol...@adacore.com> 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

Reply via email to