On Tue, Sep 1, 2026 at 2:14 AM Richard Biener <[email protected]> wrote:
>
> On Mon, 31 Aug 2026, [email protected] wrote:
>
> > From: Kyrylo Tkachov <[email protected]>
> >
> > tree_could_trap_p already uses the base type of a method to prove that
> > an in-bounds load through its this pointer cannot trap.  A PARM_DECL
> > whose REFERENCE_TYPE refers to an object type has the corresponding
> > middle-end contract.  It designates an object at least as large as the
> > referred-to type.  Fortran uses POINTER_TYPE instead when a parameter
> > can be absent.  IPA-SRA also treats REFERENCE_TYPE parameters as safe
> > to dereference.
> >
> > GAP Benchmark Suite bfs exposes the missed opportunity.  BUStep takes
> > the graph and frontier bitmaps by reference.  The loads of g.in_index_
> > and front.start_ are invariant, but LIM keeps them below a conditional
> > guard:
> >
> >   <bb 6>  _30 = MEM[(const CSRGraph *)g_15(D)].in_index_;
> >   <bb 7>  _38 = MEM[(const Bitmap *)front_22(D)].start_;
> >
> > Factor the parameter object-size query out of tree_could_trap_p.
> > Handle object reference parameters and the first parameter of a
> > METHOD_TYPE.  Use nonnull_arg_p as the common policy gate.  This keeps
> > both cases consistent with -fdelete-null-pointer-checks.  Use the
> > referred-to type or method base type to bound an access.  LIM can then
> > move both loads out of the loop.
> >
> >   Moving statement _30 = MEM[(const CSRGraph *)g_15(D)].in_index_;
> >   out of loop 1.
> >   Moving statement _38 = MEM[(const Bitmap *)front_22(D)].start_;
> >   out of loop 1.
> >
> > The object-size fact proves only that an in-bounds load is safe.  It
> > does not prove that the storage is writable.  A reference can designate
> > read-only storage after const_cast, while a path that does not store
> > remains valid.  cselim must not replace
> >
> >   if (b)
> >     a = d | p;
> >
> > with an unconditional store such as
> >
> >   a = b ? d | p : p;
> >
> > Treat stores through reference parameters as possibly trapping in
> > lhs_could_trap_p.  Use that store-specific predicate for conditional
> > store elimination and LIM store motion.
> >
> > Document the REFERENCE_TYPE and METHOD_TYPE parameter contracts.  Add
> > load-hoisting tests for the object bound and null-pointer policy.  Add
> > conditional-store tests for early phiopt, full cselim, and LIM.  Cover
> > both const and non-const references.
> >
> > On Grace, serial GAPBS bfs with kron-23, -O3 and -mcpu=neoverse-v2
> > reduces cycles by 7.5%.
> >
> > Bootstrapped and tested on aarch64-none-linux-gnu.
> > Ok for trunk?
> > Thanks,
> > Kyrill
> >
> > gcc/ChangeLog:
> >
> >       * doc/generic.texi (REFERENCE_TYPE): Document the object
> >       contract for reference parameters.
> >       (METHOD_TYPE): Document the object contract for the this
> >       parameter.
> >       * tree-eh.cc (parm_decl_from_ptr): New function.
> >       (whole_object_param_size): New function.
> >       (tree_could_trap_p): Use whole_object_param_size.
> >       (lhs_could_trap_p): Keep stores through reference parameters
> >       possibly trapping.
> >       * tree-ssa-loop-im.cc (can_sm_ref_p): Use lhs_could_trap_p.
> >       * tree-ssa-phiopt.cc (cond_store_replacement): Likewise.
> >       (cond_store_replacement_limited): Update the comment.
> >
> > gcc/testsuite/ChangeLog:
> >
> >       * g++.dg/tree-ssa/cselim-ref-1.C: New test.
> >       * g++.dg/tree-ssa/cselim-ref-2.C: New test.
> >       * g++.dg/tree-ssa/lim-ref-1.C: New test.
> >       * g++.dg/tree-ssa/lim-ref-2.C: New test.
> >       * g++.dg/tree-ssa/lim-ref-3.C: New test.
> >       * g++.dg/tree-ssa/lim-ref-4.C: New test.
> >       * g++.dg/tree-ssa/lim-this-1.C: New test.
> >
> > Signed-off-by: Kyrylo Tkachov <[email protected]>
> > ---
> >  gcc/doc/generic.texi                         |  7 +-
> >  gcc/testsuite/g++.dg/tree-ssa/cselim-ref-1.C | 28 ++++++++
> >  gcc/testsuite/g++.dg/tree-ssa/cselim-ref-2.C | 25 +++++++
> >  gcc/testsuite/g++.dg/tree-ssa/lim-ref-1.C    | 21 ++++++
> >  gcc/testsuite/g++.dg/tree-ssa/lim-ref-2.C    | 21 ++++++
> >  gcc/testsuite/g++.dg/tree-ssa/lim-ref-3.C    | 22 ++++++
> >  gcc/testsuite/g++.dg/tree-ssa/lim-ref-4.C    | 29 ++++++++
> >  gcc/testsuite/g++.dg/tree-ssa/lim-this-1.C   | 25 +++++++
> >  gcc/tree-eh.cc                               | 70 +++++++++++++++++---
> >  gcc/tree-ssa-loop-im.cc                      | 12 +---
> >  gcc/tree-ssa-phiopt.cc                       | 13 +---
> >  11 files changed, 242 insertions(+), 31 deletions(-)
> >  create mode 100644 gcc/testsuite/g++.dg/tree-ssa/cselim-ref-1.C
> >  create mode 100644 gcc/testsuite/g++.dg/tree-ssa/cselim-ref-2.C
> >  create mode 100644 gcc/testsuite/g++.dg/tree-ssa/lim-ref-1.C
> >  create mode 100644 gcc/testsuite/g++.dg/tree-ssa/lim-ref-2.C
> >  create mode 100644 gcc/testsuite/g++.dg/tree-ssa/lim-ref-3.C
> >  create mode 100644 gcc/testsuite/g++.dg/tree-ssa/lim-ref-4.C
> >  create mode 100644 gcc/testsuite/g++.dg/tree-ssa/lim-this-1.C
> >
> > diff --git a/gcc/doc/generic.texi b/gcc/doc/generic.texi
> > index 42a04e34eda..e61b151cd86 100644
> > --- a/gcc/doc/generic.texi
> > +++ b/gcc/doc/generic.texi
> > @@ -512,7 +512,10 @@ Used to represent pointer types, and pointer to data 
> > member types.  The
> >
> >  @item REFERENCE_TYPE
> >  Used to represent reference types.  The @code{TREE_TYPE} gives the type
> > -to which this type refers.
> > +to which this type refers.  A @code{PARM_DECL} whose reference type refers
> > +to an object type must designate an object whose size is at least that of
> > +the referred-to type.  Front ends must use @code{POINTER_TYPE} for a
> > +parameter that can be absent or otherwise fail to designate such an object.
> >
> >  @item FUNCTION_TYPE
> >  Used to represent the type of non-member functions and of static member
> > @@ -537,6 +540,8 @@ The type of @code{*this}, i.e., the class of which 
> > functions of this
> >  type are a member, is given by the @code{TYPE_METHOD_BASETYPE}.  The
> >  @code{TYPE_ARG_TYPES} is the parameter list, as for a
> >  @code{FUNCTION_TYPE}, and includes the @code{this} argument.
> > +The @code{this} argument must designate an object whose size is at least
> > +that of @code{TYPE_METHOD_BASETYPE}.
> >
> >  @item ARRAY_TYPE
> >  Used to represent array types.  The @code{TREE_TYPE} gives the type of
> > diff --git a/gcc/testsuite/g++.dg/tree-ssa/cselim-ref-1.C 
> > b/gcc/testsuite/g++.dg/tree-ssa/cselim-ref-1.C
> > new file mode 100644
> > index 00000000000..06bec81d97e
> > --- /dev/null
> > +++ b/gcc/testsuite/g++.dg/tree-ssa/cselim-ref-1.C
> > @@ -0,0 +1,28 @@
> > +// A reference parameter proves that an in-bounds load is safe.  It does 
> > not
> > +// prove that the referenced object is writable.  Keep the store 
> > conditional.
> > +
> > +// { dg-do compile }
> > +// { dg-options "-Ofast -fdelete-null-pointer-checks -ftree-cselim 
> > -fdump-tree-phiopt1-details" }
> > +// { dg-skip-if "" keeps_null_pointer_checks }
> > +
> > +void
> > +f (const int &a, int b, int d)
> > +{
> > +  int &c = const_cast<int &> (a);
> > +  int p = a;
> > +  if (b)
> > +    c = d | p;
> > +}
> > +
> > +void
> > +g (int &a, int b, int d)
> > +{
> > +  // A non-const reference can designate a const object after const_cast.
> > +  // The path that does not store is still valid.
> > +  int p = a;
> > +  if (b)
> > +    a = d | p;
> > +}
> > +
> > +// { dg-final { scan-tree-dump-times "if \\(b_" 2 "phiopt1" } }
> > +// { dg-final { scan-tree-dump-not "Conditional store replacement 
> > happened" "phiopt1" } }
> > diff --git a/gcc/testsuite/g++.dg/tree-ssa/cselim-ref-2.C 
> > b/gcc/testsuite/g++.dg/tree-ssa/cselim-ref-2.C
> > new file mode 100644
> > index 00000000000..74016e1cc29
> > --- /dev/null
> > +++ b/gcc/testsuite/g++.dg/tree-ssa/cselim-ref-2.C
> > @@ -0,0 +1,25 @@
> > +// Check the full conditional store replacement after disabling the early
> > +// phi optimization.
> > +
> > +// { dg-do compile }
> > +// { dg-options "-Ofast -fdelete-null-pointer-checks -ftree-cselim 
> > -fno-ssa-phiopt -fdump-tree-cselim-details" }
> > +// { dg-skip-if "" keeps_null_pointer_checks }
> > +
> > +void
> > +f (const int &a, int b, int d)
> > +{
> > +  if (b)
> > +    const_cast<int &> (a) = d;
> > +}
> > +
> > +void
> > +g (int &a, int b, int d)
> > +{
> > +  // A non-const reference can designate a const object after const_cast.
> > +  // The path that does not store is still valid.
> > +  if (b)
> > +    a = d;
> > +}
> > +
> > +// { dg-final { scan-tree-dump-times "if \\(b_" 2 "cselim" } }
> > +// { dg-final { scan-tree-dump-not "Conditional store replacement 
> > happened" "cselim" } }
> > diff --git a/gcc/testsuite/g++.dg/tree-ssa/lim-ref-1.C 
> > b/gcc/testsuite/g++.dg/tree-ssa/lim-ref-1.C
> > new file mode 100644
> > index 00000000000..fcb9a7cbd7b
> > --- /dev/null
> > +++ b/gcc/testsuite/g++.dg/tree-ssa/lim-ref-1.C
> > @@ -0,0 +1,21 @@
> > +// A parameter of reference type is bound to a valid object of the 
> > referenced
> > +// type, so a load from it cannot trap and may be hoisted out of the loop 
> > even
> > +// though it is only executed on some iterations.
> > +
> > +// { dg-do compile }
> > +// { dg-options "-O2 -fdelete-null-pointer-checks 
> > -fdump-tree-lim2-details" }
> > +// { dg-skip-if "" keeps_null_pointer_checks }
> > +
> > +struct S { int n; int *p; };
> > +
> > +int
> > +f (S &s, int n)
> > +{
> > +  int r = 0;
> > +  for (int i = 0; i < n; ++i)
> > +    if (i & 1)
> > +      r += s.p[i];
> > +  return r;
> > +}
> > +
> > +// { dg-final { scan-tree-dump "Moving statement \[^\n\r]*->p;" "lim2" } }
> > diff --git a/gcc/testsuite/g++.dg/tree-ssa/lim-ref-2.C 
> > b/gcc/testsuite/g++.dg/tree-ssa/lim-ref-2.C
> > new file mode 100644
> > index 00000000000..e06cb512c1b
> > --- /dev/null
> > +++ b/gcc/testsuite/g++.dg/tree-ssa/lim-ref-2.C
> > @@ -0,0 +1,21 @@
> > +// -fno-delete-null-pointer-checks prevents nonnull_arg_p from using the
> > +// implicit nonnull property of a C++ reference, so the conditional load 
> > must
> > +// stay in the loop.
> > +
> > +// { dg-do compile }
> > +// { dg-options "-O2 -fno-delete-null-pointer-checks 
> > -fdump-tree-lim2-details" }
> > +
> > +struct S { int n; int *p; };
> > +
> > +int
> > +f (S &s, int n)
> > +{
> > +  int r = 0;
> > +  for (int i = 0; i < n; ++i)
> > +    if (i & 1)
> > +      r += s.p[i];
> > +  return r;
> > +}
> > +
> > +// { dg-final { scan-tree-dump "Memory reference \[^\n\r]*->p" "lim2" } }
> > +// { dg-final { scan-tree-dump-not "Moving statement \[^\n\r]*->p;" "lim2" 
> > } }
> > diff --git a/gcc/testsuite/g++.dg/tree-ssa/lim-ref-3.C 
> > b/gcc/testsuite/g++.dg/tree-ssa/lim-ref-3.C
> > new file mode 100644
> > index 00000000000..f4a611d9c47
> > --- /dev/null
> > +++ b/gcc/testsuite/g++.dg/tree-ssa/lim-ref-3.C
> > @@ -0,0 +1,22 @@
> > +// The referenced type bounds what may be speculated: an access past the 
> > end
> > +// of the referenced object still counts as trapping and stays in the loop.
> > +
> > +// { dg-do compile }
> > +// { dg-options "-O2 -fdelete-null-pointer-checks 
> > -fdump-tree-lim2-details" }
> > +// { dg-skip-if "" keeps_null_pointer_checks }
> > +
> > +struct Small { int a; };
> > +struct Big { int a; long b[8]; };
> > +
> > +long
> > +g (Small &s, int n)
> > +{
> > +  long r = 0;
> > +  for (int i = 0; i < n; ++i)
> > +    if (i & 1)
> > +      r += reinterpret_cast<Big &> (s).b[3];
> > +  return r;
> > +}
> > +
> > +// { dg-final { scan-tree-dump "Memory reference \[^\n\r]*struct Big" 
> > "lim2" } }
> > +// { dg-final { scan-tree-dump-not "Moving statement \[^\n\r]*struct Big" 
> > "lim2" } }
> > diff --git a/gcc/testsuite/g++.dg/tree-ssa/lim-ref-4.C 
> > b/gcc/testsuite/g++.dg/tree-ssa/lim-ref-4.C
> > new file mode 100644
> > index 00000000000..8873cc2dad8
> > --- /dev/null
> > +++ b/gcc/testsuite/g++.dg/tree-ssa/lim-ref-4.C
> > @@ -0,0 +1,29 @@
> > +// The object bound of a reference parameter does not make a conditional
> > +// store safe to execute outside its guard.
> > +
> > +// { dg-do compile }
> > +// { dg-options "-Ofast -fdelete-null-pointer-checks -fno-tree-cselim 
> > -fno-ssa-phiopt -fdump-tree-lim2-details" }
> > +// { dg-skip-if "" keeps_null_pointer_checks }
> > +
> > +struct S { int value; };
> > +
> > +void
> > +f (const S &s, int n, int store)
> > +{
> > +  for (int i = 0; i < n; ++i)
> > +    if (store)
> > +      const_cast<S &> (s).value = 1;
> > +}
> > +
> > +void
> > +g (S &s, int n, int store)
> > +{
> > +  // A non-const reference can designate a const object after const_cast.
> > +  // The path that does not store is still valid.
> > +  for (int i = 0; i < n; ++i)
> > +    if (store)
> > +      s.value = 1;
> > +}
> > +
> > +// { dg-final { scan-tree-dump-times "Memory reference \[^\n\r]*value" 2 
> > "lim2" } }
> > +// { dg-final { scan-tree-dump-not "Executing store motion of 
> > \[^\n\r]*value" "lim2" } }
> > diff --git a/gcc/testsuite/g++.dg/tree-ssa/lim-this-1.C 
> > b/gcc/testsuite/g++.dg/tree-ssa/lim-this-1.C
> > new file mode 100644
> > index 00000000000..dbfd79a96b5
> > --- /dev/null
> > +++ b/gcc/testsuite/g++.dg/tree-ssa/lim-this-1.C
> > @@ -0,0 +1,25 @@
> > +// -fno-delete-null-pointer-checks prevents nonnull_arg_p from using the
> > +// implicit nonnull property of a method's this parameter, so the 
> > conditional
> > +// load must stay in the loop.
> > +
> > +// { dg-do compile }
> > +// { dg-options "-O2 -fno-delete-null-pointer-checks 
> > -fdump-tree-lim2-details" }
> > +
> > +struct S
> > +{
> > +  int *values;
> > +  int f (int);
> > +};
> > +
> > +int
> > +S::f (int n)
> > +{
> > +  int r = 0;
> > +  for (int i = 0; i < n; ++i)
> > +    if (i & 1)
> > +      r += values[i];
> > +  return r;
> > +}
> > +
> > +// { dg-final { scan-tree-dump "Memory reference \[^\n\r]*->values" "lim2" 
> > } }
> > +// { dg-final { scan-tree-dump-not "Moving statement \[^\n\r]*->values;" 
> > "lim2" } }
> > diff --git a/gcc/tree-eh.cc b/gcc/tree-eh.cc
> > index 0835f55af08..ddff13fd272 100644
> > --- a/gcc/tree-eh.cc
> > +++ b/gcc/tree-eh.cc
> > @@ -2752,6 +2752,54 @@ ref_outside_object_p (tree size, poly_offset_int 
> > off, tree refsz)
> >    return false;
> >  }
> >
> > +/* If PTR is a PARM_DECL or its default SSA definition, return the
> > +   PARM_DECL.  Otherwise return NULL_TREE.  */
> > +
> > +static tree
> > +parm_decl_from_ptr (tree ptr)
> > +{
> > +  if (TREE_CODE (ptr) == SSA_NAME)
> > +    {
> > +      if (!SSA_NAME_IS_DEFAULT_DEF (ptr))
> > +     return NULL_TREE;
> > +      ptr = SSA_NAME_VAR (ptr);
> > +    }
> > +
> > +  return ptr && TREE_CODE (ptr) == PARM_DECL ? ptr : NULL_TREE;
> > +}
> > +
> > +/* If PTR is a parameter of the current function, or the default definition
> > +   of one, that is known to designate a whole object, return the size of 
> > that
> > +   object in bytes.  Otherwise return NULL_TREE.
> > +
> > +   Two kinds of parameter qualify.  The this pointer of a method points to
> > +   an object of the method base type.  A parameter whose reference type
> > +   refers to an object type is bound to an object of the referenced type.
> > +   In both cases the pointed-to object is at least as large as that type.  
> > */
> > +
> > +static tree
> > +whole_object_param_size (tree ptr)
> > +{
> > +  tree type;
> > +
> > +  if (!cfun)
> > +    return NULL_TREE;
> > +
> > +  ptr = parm_decl_from_ptr (ptr);
> > +  if (!ptr)
> > +    return NULL_TREE;
> > +
> > +  if (TREE_CODE (TREE_TYPE (ptr)) == REFERENCE_TYPE)
> > +    type = TREE_TYPE (TREE_TYPE (ptr));
> > +  else if (TREE_CODE (TREE_TYPE (cfun->decl)) == METHOD_TYPE
> > +        && ptr == DECL_ARGUMENTS (cfun->decl))
> > +    type = TYPE_METHOD_BASETYPE (TREE_TYPE (cfun->decl));
> > +  else
> > +    return NULL_TREE;
> > +
> > +  return nonnull_arg_p (ptr) ? TYPE_SIZE_UNIT (type) : NULL_TREE;
> > +}
> > +
> >  /* 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.  */
> > @@ -2851,19 +2899,11 @@ tree_could_trap_p (tree expr)
> >         tree refsz = TYPE_SIZE_UNIT (TREE_TYPE (expr));
> >         return ref_outside_object_p (size, off, refsz);
> >       }
> > -      if (cfun
> > -       && TREE_CODE (TREE_TYPE (cfun->decl)) == METHOD_TYPE
> > -       && ((TREE_CODE (TREE_OPERAND (expr, 0)) == SSA_NAME
> > -            && SSA_NAME_IS_DEFAULT_DEF (TREE_OPERAND (expr, 0))
> > -            && (SSA_NAME_VAR (TREE_OPERAND (expr, 0))
> > -                == DECL_ARGUMENTS (cfun->decl)))
> > -           || TREE_OPERAND (expr, 0) == DECL_ARGUMENTS (cfun->decl)))
> > +      if (tree size = whole_object_param_size (TREE_OPERAND (expr, 0)))
> >       {
> >         poly_offset_int off = mem_ref_offset (expr);
> >         if (maybe_lt (off, 0))
> >           return true;
> > -       tree size = TYPE_SIZE_UNIT
> > -                     (TYPE_METHOD_BASETYPE (TREE_TYPE (cfun->decl)));
> >         tree refsz = TYPE_SIZE_UNIT (TREE_TYPE (expr));
> >         return ref_outside_object_p (size, off, refsz);
> >       }
> > @@ -2918,13 +2958,23 @@ tree_could_trap_p (tree expr)
> >      }
> >  }
> >
> > -/* Returns true if LHS is known not to trap as a store.  */
> > +/* Return true if LHS can trap as a store.  */
> >  bool
> >  lhs_could_trap_p (tree lhs)
> >  {
> >    tree lhsbase = get_base_address (lhs);
> >    if (tree_could_trap_p (lhs))
> >      return true;
> > +  /* A reference can designate read-only storage.  Its whole-object 
> > property
> > +     makes an in-bounds load safe, but does not make an unconditional store
> > +     safe.  */
> > +  if (TREE_CODE (lhsbase) == MEM_REF
> > +      || TREE_CODE (lhsbase) == TARGET_MEM_REF)
> > +    {
> > +      tree ptr = parm_decl_from_ptr (TREE_OPERAND (lhsbase, 0));
> > +      if (ptr && TREE_CODE (TREE_TYPE (ptr)) == REFERENCE_TYPE)
> > +     return true;
>
> This looks bad from a design perspective -- instead of reverse
> engineering what tree_could_trap_p does we should instead use
> a helper that exempts analysis only valid for loads?  So please
> add a static tree_could_trap_p_1 as worker with a parameter
> indicating that.

Note this part was done in
r17-3841-g59b82e3f4f88a02158c56921effcc078ab1a5580 (so I could
backport it with the fix for this).

>
> Btw, isn't this also an issue for tree_could_throw_p and
> non-call exceptions?  That is, we'll fail to create EH
> regions for stores to such parameters or this?

That is a good question.  Yes it looks like it.  I will submit a patch
to change stmt_could_throw_1_p over to use tree_could_trap_1 with true
for the lhs tomorrow.

Thanks,
Andrea

>
> > +    }
> >    /* tree_could_trap_p is a predicate for loads, so check
> >       for readonly memory explicitly.  */
> >    if ((DECL_P (lhsbase) && TREE_READONLY (lhsbase))
> > diff --git a/gcc/tree-ssa-loop-im.cc b/gcc/tree-ssa-loop-im.cc
> > index 41264560009..eb2d0b171c1 100644
> > --- a/gcc/tree-ssa-loop-im.cc
> > +++ b/gcc/tree-ssa-loop-im.cc
> > @@ -3351,8 +3351,6 @@ ref_in_loop_hot_body::operator () (mem_ref_loc *loc)
> >  static bool
> >  can_sm_ref_p (class loop *loop, im_mem_ref *ref)
> >  {
> > -  tree base;
> > -
> >    /* Can't hoist unanalyzable refs.  */
> >    if (!MEM_ANALYZABLE (ref))
> >      return false;
> > @@ -3371,14 +3369,8 @@ can_sm_ref_p (class loop *loop, im_mem_ref *ref)
> >    if (tree_could_throw_p (ref->mem.ref))
> >      return false;
> >
> > -  /* If it can trap, it must be always executed in LOOP.
> > -     Readonly memory locations may trap when storing to them, but
> > -     tree_could_trap_p is a predicate for rvalues, so check that
> > -     explicitly.  */
> > -  base = get_base_address (ref->mem.ref);
> > -  if ((tree_could_trap_p (ref->mem.ref)
> > -       || (DECL_P (base) && TREE_READONLY (base))
> > -       || TREE_CODE (base) == STRING_CST)
> > +  /* If a store can trap, it must be always executed in LOOP.  */
> > +  if (lhs_could_trap_p (ref->mem.ref)
> >        /* ???  We can at least use false here, allowing loads?  We
> >        are forcing conditional stores if the ref is not always
> >        stored to later anyway.  So this would only guard
> > diff --git a/gcc/tree-ssa-phiopt.cc b/gcc/tree-ssa-phiopt.cc
> > index 784221c83b3..f0566728464 100644
> > --- a/gcc/tree-ssa-phiopt.cc
> > +++ b/gcc/tree-ssa-phiopt.cc
> > @@ -3325,15 +3325,8 @@ cond_store_replacement (basic_block middle_bb, 
> > basic_block join_bb, edge e0,
> >        /* If LHS is an access to a local variable without address-taken
> >        (or when we allow data races) and known not to trap, we could
> >        always safely move down the store.  */
> > -      tree base;
> >        if (ref_can_have_store_data_races (lhs)
> > -       || tree_could_trap_p (lhs)
> > -       /* tree_could_trap_p is a predicate for rvalues, so check
> > -          for readonly memory explicitly.  */
> > -       || ((base = get_base_address (lhs))
> > -           && ((DECL_P (base)
> > -                && TREE_READONLY (base))
> > -               || TREE_CODE (base) == STRING_CST)))
> > +       || lhs_could_trap_p (lhs))
> >       return false;
> >      }
> >
> > @@ -3717,8 +3710,8 @@ cond_store_replacement_limited (basic_block 
> > middle_bb, basic_block join_bb,
> >      if (b)
> >        nontrapping = c;
> >
> > -    For case 3, nontrapping needs to satisfy tree_could_trap_p.
> > -    In both cases ref_can_have_store_data_races needs to be satisfy.
> > +    For case 3, the store must not trap.
> > +    In both cases ref_can_have_store_data_races must be satisfied.
> >    */
> >
> >    /* Maybe the load/local non-escaped variable case.  */
> >
>
> --
> Richard Biener <[email protected]>
> SUSE Software Solutions Germany GmbH,
> Frankenstrasse 146, 90461 Nuernberg, Germany;
> GF: Jochen Jaser, Andrew McDonald, Abhinav Puri; (HRB 36809, AG Nuernberg)

Reply via email to