Use modref summary to DSE calls to non-pure functions

Richard Biener richard.guenther@gmail.com
Thu Nov 11 12:32:28 GMT 2021


On Thu, Nov 11, 2021 at 1:07 PM Jan Hubicka <hubicka@kam.mff.cuni.cz> wrote:
>
> > > +  /* Unlike alias oracle we can not skip subtrees based on TBAA check.
> > > +     Count the size of the whole tree to verify that we will not need too many
> > > +     tests.  */
> > > +  FOR_EACH_VEC_SAFE_ELT (summary->stores->bases, i, base_node)
> > > +    FOR_EACH_VEC_SAFE_ELT (base_node->refs, j, ref_node)
> > > +      FOR_EACH_VEC_SAFE_ELT (ref_node->accesses, k, access_node)
> > > +       if (num_tests++ > max_tests)
> > > +         return false;
> >
> > at least the innermost loop can be done as
> >
> >           if (num_tests += ref_node->accesses.length () > max_tests)
> >
> > no?
>
> Yep that was stupid, sorry for that ;))
> >
> > > +
> > > +  /* Walk all memory writes and verify that they are dead.  */
> > > +  FOR_EACH_VEC_SAFE_ELT (summary->stores->bases, i, base_node)
> > > +    FOR_EACH_VEC_SAFE_ELT (base_node->refs, j, ref_node)
> > > +      FOR_EACH_VEC_SAFE_ELT (ref_node->accesses, k, access_node)
> > > +       {
> > > +         /* ??? if offset is unkonwn it may be negative.  Not sure
> > > +            how to construct ref here.  */
> >
> > I think you can't, you could use -poly_int64_max or so.
>
> I need a ref to give to dse_classify_store. It needs base to track live
> bytes etc which is not very useful if I do not know the range.  However
> DSE is still useful since I can hit free or end of lifetime of the decl.
> I was wondering if I should simply implement a lightweight version of
> dse_clasify_store that handles this case?

No, I think if it turns out useful then we want a way to have such ref
represented by an ao_ref.  Note that when we come from a
ref tree we know handled-components only will increase offset,
only the base MEM_REF can contain a pointer subtraction (but
the result of that is the base then).

In what cases does parm_offset_known end up false?  Is that
when seeing a POINTER_PLUS_EXPR with unknown offset?
So yes, that's a case we cannot capture right now - the only
thing that remains is a pointer with a known points-to-set - a
similar problem as with the pure call PRE.  You could in theory
allocate a scratch SSA name and attach points-to-info
to it.  And when the call argument is &decl based then you could set
offset to zero.

> >
> > > +         if (!access_node->parm_offset_known)
> > > +           return false;
> >
> > But you could do this check in the loop computing num_tests ...
> > (we could also cache the count and whether any of the refs have unknown offset
> > in the summary?)
>
> Yep, I plan to add cache for bits like this (and the check for accessing
> global memory).  Just want to push bit more of the cleanups I have in my
> local tree.
> >
> > > +         tree arg;
> > > +         if (access_node->parm_index == MODREF_STATIC_CHAIN_PARM)
> > > +           arg = gimple_call_chain (stmt);
> > > +         else
> > > +           arg = gimple_call_arg (stmt, access_node->parm_index);
> > > +
> > > +         ao_ref ref;
> > > +         poly_offset_int off = (poly_offset_int)access_node->offset
> > > +               + ((poly_offset_int)access_node->parm_offset
> > > +                  << LOG2_BITS_PER_UNIT);
> > > +         poly_int64 off2;
> > > +         if (!off.to_shwi (&off2))
> > > +           return false;
> > > +         ao_ref_init_from_ptr_and_range
> > > +                (&ref, arg, true, off2, access_node->size,
> > > +                 access_node->max_size);
> > > +         ref.ref_alias_set = ref_node->ref;
> > > +         ref.base_alias_set = base_node->base;
> > > +
> > > +         bool byte_tracking_enabled
> > > +             = setup_live_bytes_from_ref (&ref, live_bytes);
> > > +         enum dse_store_status store_status;
> > > +
> > > +         store_status = dse_classify_store (&ref, stmt,
> > > +                                            byte_tracking_enabled,
> > > +                                            live_bytes, &by_clobber_p);
> > > +         if (store_status != DSE_STORE_DEAD)
> > > +           return false;
> > > +       }
> > > +  /* Check also value stored by the call.  */
> > > +  if (gimple_store_p (stmt))
> > > +    {
> > > +      ao_ref ref;
> > > +
> > > +      if (!initialize_ao_ref_for_dse (stmt, &ref))
> > > +       gcc_unreachable ();
> > > +      bool byte_tracking_enabled
> > > +         = setup_live_bytes_from_ref (&ref, live_bytes);
> > > +      enum dse_store_status store_status;
> > > +
> > > +      store_status = dse_classify_store (&ref, stmt,
> > > +                                        byte_tracking_enabled,
> > > +                                        live_bytes, &by_clobber_p);
> > > +      if (store_status != DSE_STORE_DEAD)
> > > +       return false;
> > > +    }
> > > +  delete_dead_or_redundant_assignment (gsi, "dead", need_eh_cleanup);
> > > +  return true;
> > > +}
> > > +
> > >  namespace {
> > >
> > >  const pass_data pass_data_dse =
> > > @@ -1235,7 +1363,14 @@ pass_dse::execute (function *fun)
> > >           gimple *stmt = gsi_stmt (gsi);
> > >
> > >           if (gimple_vdef (stmt))
> > > -           dse_optimize_stmt (fun, &gsi, live_bytes);
> > > +           {
> > > +             gcall *call = dyn_cast <gcall *> (stmt);
> > > +
> > > +             if (call && dse_optimize_call (&gsi, live_bytes))
> > > +               /* We removed a dead call.  */;
> > > +             else
> > > +               dse_optimize_store (fun, &gsi, live_bytes);
> >
> > I think we want to refactor both functions, dse_optimize_stmt has some
> > early outs that apply generally, and it handles some builtin calls
> > that we don't want to re-handle with dse_optimize_call.
> >
> > So I wonder if it is either possible to call the new function from
> > inside dse_optimize_stmt instead, after we handled the return
> > value of call for example or different refactoring can make the flow
> > more obvious.
>
> It was my initial plan. However I was not sure how much I would get from
> that.
>
> The function starts with:
>
>   /* 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;
>
>   ao_ref ref;
>   if (!initialize_ao_ref_for_dse (stmt, &ref))
>     return;
>
> The check about clobber does not apply to calls and then it gives up on
> functions not returning aggregates (that is a common case).
>
> For functions returing aggregates it tries to prove that retval is dead
> and replace it.
>
> I guess I can simply call my analysis from the second return above and
> from the code removing dead LHS call instead of doing it from the main
> walker and drop the LHS handling?

Yeah, something like that.

Richard.

> Thank you,
> Honza
> >
> > Thanks,
> > Richard.
> >
> > > +           }
> > >           else if (def_operand_p
> > >                      def_p = single_ssa_def_operand (stmt, SSA_OP_DEF))
> > >             {


More information about the Gcc-patches mailing list