On Tue, 2 Apr 2013, Jakub Jelinek wrote:

> On Tue, Apr 02, 2013 at 01:44:46PM +0200, Richard Biener wrote:
> > > @@ -412,7 +412,8 @@ get_mem_ref_of_assignment (const gimple
> > >  {
> > >    gcc_assert (gimple_assign_single_p (assignment));
> > >  
> > > -  if (gimple_store_p (assignment))
> > > +  if (gimple_store_p (assignment)
> > > +      && !gimple_clobber_p (assignment))
> > 
> > hmm, maybe gimple_store_p should not consider a clobber a store?
> 
> gimple_store_p is young, so perhaps we can tweak its meaning.
> By changing gimple_store_p, we could drop this hunk, but perhaps
> would want to add early return for gimple_clobber_p
> into will_be_nonconstant_predicate?  Your call.

I've just come along 56787 and decided that not collecting
CLOBBERs during data-reference gathering would be wrong.
Thus changing gimple_store_p would be wrong, too.  So in the
end the above hunk looks ok and we shouldn't change gimple_store_p.
(well, for now at least)

> > >      {
> > >        ref->start = gimple_assign_lhs (assignment);
> > >        *ref_is_store = true;
> > > --- gcc/tree-ssa-dse.c.jj 2013-01-11 09:02:37.000000000 +0100
> > > +++ gcc/tree-ssa-dse.c    2013-03-27 17:14:27.424466023 +0100
> > > @@ -218,7 +218,10 @@ dse_optimize_stmt (gimple_stmt_iterator
> > >    if (is_gimple_call (stmt) && gimple_call_fndecl (stmt))
> > >      return;
> > >  
> > > -  if (gimple_has_volatile_ops (stmt))
> > > +  /* Don't return early on *this_2(D) ={v} {CLOBBER}.  */
> > > +  if (gimple_has_volatile_ops (stmt)
> > > +      && (!gimple_clobber_p (stmt)
> > > +   || TREE_CODE (gimple_assign_lhs (stmt)) != MEM_REF))
> > >      return;
> > 
> > But this only means the clobber was not considered as "dead"
> > if there was a later store to the same location.  So, why would
> > that change be needed?
> 
> Say with -O2:
> struct A { virtual ~A (); char a[10]; };
> struct B : public A { virtual ~B (); char b[10]; };
> A::~A () { a[5] = 7; }
> B::~B () { b[5] = 8; }
> B b;
> we end up in ~B with:
>   this_3(D)->D.2229._vptr.A = &MEM[(void *)&_ZTV1B + 16B];
>   this_3(D)->b[5] = 8;
>   _6 = &this_3(D)->D.2229;
>   MEM[(struct A *)this_3(D)]._vptr.A = &MEM[(void *)&_ZTV1A + 16B];
>   MEM[(struct A *)this_3(D)].a[5] = 7;
>   MEM[(struct A *)this_3(D)] ={v} {CLOBBER};
>   *this_3(D) ={v} {CLOBBER};
> and the intent of the above hunk (and the one immediately below this)
> is that we DSE the first clobber (with smaller clobbered size), something
> that IMHO happens very frequently in C++ code, and allows us to better
> DSEthe other stores.  There is no point in keeping the smaller clobbers
> around, on the other side, without the second hunk we'd remove pretty much
> all the MEM_REF clobbers at the end of functions, which is undesriable.

Ah, ok.

> > >    if (is_gimple_assign (stmt))
> > > @@ -228,6 +231,12 @@ dse_optimize_stmt (gimple_stmt_iterator
> > >        if (!dse_possible_dead_store_p (stmt, &use_stmt))
> > >   return;
> > >  
> > > +      /* But only remove *this_2(D) ={v} {CLOBBER} if killed by
> > > +  another clobber stmt.  */
> > > +      if (gimple_clobber_p (stmt)
> > > +   && !gimple_clobber_p (use_stmt))
> > > + return;
> > 
> > Ah.  Hmm, can that ever happen?  I presume the issue with non-decl
> > clobbers is that we will never remove them, even if the storage
> > becomes "dead"?
> 
> See above.
> 
> > > @@ -827,7 +827,26 @@ remove_unused_locals (void)
> > >       if (gimple_clobber_p (stmt))
> > >         {
> > >           tree lhs = gimple_assign_lhs (stmt);
> > > +         bool remove = false;
> > > +         /* Remove clobbers referencing unused vars, or clobbers
> > > +            with MEM_REF lhs referencing unused vars or uninitialized
> > > +            pointers.  */
> > >           if (TREE_CODE (lhs) == VAR_DECL && !is_used_p (lhs))
> > > +           remove = true;
> > > +         else if (TREE_CODE (lhs) == MEM_REF)
> > > +           {
> > > +             ssa_op_iter iter;
> > > +             tree op;
> > > +             FOR_EACH_SSA_TREE_OPERAND (op, stmt, iter, SSA_OP_USE)
> > 
> > The only SSA use on a clobber stmt can possibly be TREE_OPERAND (lhs, 0)
> > when lhs is a MEM_REF, no need to use FOR_EACH_SSA_TREE_OPERAND.
> > 
> > So just 
> > 
> > > +             else if (TREE_CODE (lhs) == MEM_REF
> >                          && TREE_CODE (TREE_OPERAND (lhs, 0)) == SSA_NAME)
> > ...
> 
> If MEM_REF[&MEM_REF[...], 0] and similar won't ever get through, perhaps.

It won't, that's not valid GIMPLE.  See is_gimple_mem_ref_addr ().

> > 
> > > +               if (SSA_NAME_VAR (op)
> > > +                   && TREE_CODE (SSA_NAME_VAR (op)) == VAR_DECL
> > > +                   && !is_used_p (SSA_NAME_VAR (op)))
> > > +                 remove = true;
> > 
> > I'm not sure this is really desired ... isn't a better check to do
> > has_single_use (op)?  (which means it would be better suited to
> > be handled in DCE?)
> 
> The above change fixed tons of ICEs, fnsplit pass ignored clobber stmts
> (desirable) when deciding what to split, and end up copying the clobber
> into the *.part.0 function, but because nothing but the clobber used
> the this parameter, it wasn't passed.  So, we ended up first referencing
> a default definition of a local this variable (just useless stmt), and later
> on when tree-ssa-live.c run we've ignored the clobber again for decisions,
> so that local this variable became !is_used_p and we've ended up referencing
> in-free-list SSA_NAME, which triggered assertion failure.  See a few lines
> above this where we similarly remove !is_used_p VAR_DECLs.
> So, IMHO the !is_used_p code belongs to this spot, we can do the clobber
> with
> SSA_NAME_IS_DEFAULT_DEF (op) && TREE_CODE (SSA_NAME_VAR (op)) != PARM_DECL)
> MEM_REF removal in DCE (or both DCE and here).
> Without this code in tree-ssa-live.c we couldn't do:
>           if (gimple_clobber_p (stmt))
>             {
>               have_local_clobbers = true;
>               continue;
>             }
> in remove_unused_locals safely (i.e. don't bother with mark_all_vars_used
> on the lhs of the clobber).

Ok, note that I didn't object to the SSA_NAME_DEFAULT_DEF handling
but to checking something on SSA_NAME_VAR - sure, if SSA_NAME_VAR
is unused then each remaining SSA name must be a default definition.
So ... what about removing the whole case handling unused
SSA_NAME_VAR?

> > Btw, due to propagation you can end up with MEM[&decl] = {CLOBBER};
> > which should be handled the same as the VAR_DECL case.  Eventually
> > just use lhs = get_base_address (gimple_assign_lhs (stmt)) here.
> 
> Sure, MEM_REF[&decl] I've seen frequently, but unless it is a whole
> var access and not say clobbering of just a portion of the var, we want
> to treat it as the patch does for DSE reasons, but not for variable
> coalescing reasons during expansion.  Perhaps for MEM_REF[&decl, 0] with
> the size of access the same as decl's size cfgexpand.c could treat it as a
> clobber for the whole decl (but then, won't we have there a non-MEM_REF
> clobber in that case after it too and DSE supposedly already removed the
> first MEM_REF clobber?).  I mean like adding
> void bar (B &);
> void
> foo ()
> {
>   { B b; bar (b); } { B c; bar (c); } { B d; bar (d); }
> }
> to the above testcase.

I was only suggesting that because you only handle DECL = CLOBBER
and MEM[ssa_ptr_2] = CLOBBER in tree-ssa-live.c you miss removing
(partial) clobbers of the MEM[&decl, offset] = CLOBBER kind when DECL is
otherwise unused.  I believe this can easily occur with in-place 
construction / destruction.  A very easy fix for that is to
use get_base_address on the LHS of the CLOBBER.

Richard.

Reply via email to