On Thu, 28 May 2020, Martin Jambor wrote:

> PR 93385 reveals that if the user explicitely disables DCE, IPA-SRA
> can leave behind statements which are useless because their results
> are eventually not used but can have problematic side effects,
> especially since their inputs are now bogus that useless parameters
> were removed.
> 
> This patch fixes the problem by doing a similar def-use walk when
> materializing clones, marking which statements should not be copied
> and which SSA_NAMEs will be removed by call redirections and now need
> to be replaced with anything valid.  Default-definition SSA_NAMEs of
> parameters which are removed and all SSA_NAMEs derived from them (in a
> phi node or a simple assignment statement) are then remapped to
> error_mark_node - a sure way to spot it if any is left in place.
> 
> There is one exception to the above rule, if such SSA_NAMEs appear as
> an argument of a call, they need to be removed by call redirection and
> not as part of clone materialization.  So to have something valid
> there until that time, this patch pulls out dummy declarations out of
> thin air.  If you do not like that, see patch number 4 in the series,
> which changes this, but probably in a controversial way.
> 
> This patch only resets debug statements using the removed SSA_NAMEs.
> The first follow-up patch adjusts debug statements in the current
> function to still try to make the removed values available in debugger
> in the current function and the subsequent one also in other functions
> where they are passed.
> 
> gcc/ChangeLog:
> 
> 2020-05-14  Martin Jambor  <mjam...@suse.cz>
> 
>       PR ipa/93385
>       * ipa-param-manipulation.h (class ipa_param_body_adjustments): New
>       members m_dead_stmts, m_dead_ssas, mark_dead_statements and
>       get_removed_call_arg_placeholder.
>       * ipa-param-manipulation.c (phi_arg_will_live_p): New function.
>       (ipa_param_body_adjustments::mark_dead_statements): New method.
>       (ipa_param_body_adjustments::common_initialization): Call it.
>       (ipa_param_body_adjustments::ipa_param_body_adjustments): Initialize
>       new mwmbers.
>       (ipa_param_body_adjustments::get_removed_call_arg_placeholder): New.
>       (ipa_param_body_adjustments::modify_call_stmt): Replace dead SSAs
>       with dummy decls.
>       * tree-inline.c (remap_gimple_stmt): Do not copy dead statements,
>       reset dead debug statements.
>       (copy_phis_for_bb): Do not copy dead PHI nodes.
> 
> gcc/testsuite/ChangeLog:
> 
> 2020-05-14  Martin Jambor  <mjam...@suse.cz>
> 
>       PR ipa/93385
>       * gcc.dg/ipa/pr93385.c: New test.
>       * gcc.dg/ipa/ipa-sra-23.c: Likewise.
> ---
>  gcc/ipa-param-manipulation.c          | 142 ++++++++++++++++++++++++--
>  gcc/ipa-param-manipulation.h          |   8 ++
>  gcc/testsuite/gcc.dg/ipa/ipa-sra-23.c |  24 +++++
>  gcc/testsuite/gcc.dg/ipa/pr93385.c    |  27 +++++
>  gcc/tree-inline.c                     |  18 +++-
>  5 files changed, 205 insertions(+), 14 deletions(-)
>  create mode 100644 gcc/testsuite/gcc.dg/ipa/ipa-sra-23.c
>  create mode 100644 gcc/testsuite/gcc.dg/ipa/pr93385.c
> 
> diff --git a/gcc/ipa-param-manipulation.c b/gcc/ipa-param-manipulation.c
> index 978916057f0..1f47f3a4268 100644
> --- a/gcc/ipa-param-manipulation.c
> +++ b/gcc/ipa-param-manipulation.c
> @@ -953,6 +953,99 @@ ipa_param_body_adjustments::carry_over_param (tree t)
>    return new_parm;
>  }
>  
> +/* Return true if BLOCKS_TO_COPY is NULL or if PHI has an argument ARG in
> +   position that corresponds to an edge that is coming from a block that has
> +   the corresponding bit set in BLOCKS_TO_COPY.  */
> +
> +static bool
> +phi_arg_will_live_p (gphi *phi, bitmap blocks_to_copy, tree arg)
> +{
> +  bool arg_will_survive = false;
> +  if (!blocks_to_copy)
> +    arg_will_survive = true;
> +  else
> +    for (unsigned i = 0; i < gimple_phi_num_args (phi); i++)
> +      if (gimple_phi_arg_def (phi, i) == arg
> +       && bitmap_bit_p (blocks_to_copy,
> +                        gimple_phi_arg_edge (phi, i)->src->index))
> +     {
> +       arg_will_survive = true;
> +       break;
> +     }
> +  return arg_will_survive;
> +}
> +
> +/* Populate m_dead_stmts given that DEAD_PARAM is going to be removed without
> +   any replacement or splitting.  */
> +
> +void
> +ipa_param_body_adjustments::mark_dead_statements (tree dead_param)
> +{
> +  if (!is_gimple_reg (dead_param))

Hmm, so non-registers are not a problem?  I guess IPA SRA simply
ensures there are no actual uses (but call arguments) in that case?
Please clearly document this function matches IPA SRA capabilities.

> +    return;
> +  tree parm_ddef = ssa_default_def (m_id->src_cfun, dead_param);
> +  if (!parm_ddef || has_zero_uses (parm_ddef))
> +    return;
> +
> +  auto_vec<tree, 4> stack;
> +  m_dead_ssas.add (parm_ddef);
> +  stack.safe_push (parm_ddef);
> +  while (!stack.is_empty ())
> +    {
> +      tree t = stack.pop ();
> +
> +      imm_use_iterator imm_iter;
> +      gimple *stmt;
> +
> +      insert_decl_map (m_id, t, error_mark_node);
> +      FOR_EACH_IMM_USE_STMT (stmt, imm_iter, t)
> +     {
> +       if (is_gimple_call (stmt)
> +           || (m_id->blocks_to_copy
> +               && !bitmap_bit_p (m_id->blocks_to_copy,
> +                                 gimple_bb (stmt)->index)))
> +         continue;
> +
> +       if (is_gimple_debug (stmt))
> +         {
> +           m_dead_stmts.add (stmt);
> +           gcc_assert (gimple_debug_bind_p (stmt));
> +         }
> +       else if (gimple_code (stmt) == GIMPLE_PHI)
> +         {
> +           gphi *phi = as_a <gphi *> (stmt);
> +           if (phi_arg_will_live_p (phi, m_id->blocks_to_copy, t))
> +             {
> +               m_dead_stmts.add (phi);
> +               tree res = gimple_phi_result (phi);
> +               if (!m_dead_ssas.contains (res))

You can combine this with the add below which returns false if the
item was not already present.

> +                 {
> +                   stack.safe_push (res);
> +                   m_dead_ssas.add (res);
> +                 }
> +             }
> +         }
> +       else if (is_gimple_assign (stmt))
> +         {
> +           m_dead_stmts.add (stmt);
> +           if (!gimple_clobber_p (stmt))
> +             {
> +               tree lhs = gimple_assign_lhs (stmt);
> +               gcc_assert (TREE_CODE (lhs) == SSA_NAME);
> +               if (!m_dead_ssas.contains (lhs))
> +                 {
> +                   m_dead_ssas.add (lhs);
> +                   stack.safe_push (lhs);
> +                 }
> +             }
> +         }
> +       else
> +         /* IPA-SRA does not analyze other types of statements.  */
> +         gcc_unreachable ();
> +     }
> +    }
> +}
> +
>  /* Common initialization performed by all ipa_param_body_adjustments
>     constructors.  OLD_FNDECL is the declaration we take original arguments
>     from, (it may be the same as M_FNDECL).  VARS, if non-NULL, is a pointer 
> to
> @@ -1095,6 +1188,11 @@ ipa_param_body_adjustments::common_initialization 
> (tree old_fndecl,
>               /* Declare this new variable.  */
>               DECL_CHAIN (var) = *vars;
>               *vars = var;
> +
> +             /* If this is not a split but a real removal, init hash sets
> +                that will guide what not to copy to the new body.  */
> +             if (!isra_dummy_decls[i])
> +               mark_dead_statements (m_oparms[i]);
>             }
>         }
>       else
> @@ -1151,9 +1249,10 @@ ipa_param_body_adjustments
>  ::ipa_param_body_adjustments (vec<ipa_adjusted_param, va_gc> *adj_params,
>                             tree fndecl)
>    : m_adj_params (adj_params), m_adjustments (NULL), m_reset_debug_decls (),
> -    m_split_modifications_p (false), m_fndecl (fndecl), m_id (NULL),
> -    m_oparms (), m_new_decls (), m_new_types (), m_replacements (),
> -    m_removed_decls (), m_removed_map (), m_method2func (false)
> +    m_split_modifications_p (false), m_dead_stmts (), m_dead_ssas (),
> +    m_fndecl (fndecl), m_id (NULL), m_oparms (), m_new_decls (),
> +    m_new_types (), m_replacements (), m_removed_decls (), m_removed_map (),
> +    m_method2func (false)
>  {
>    common_initialization (fndecl, NULL, NULL);
>  }
> @@ -1167,9 +1266,9 @@ ipa_param_body_adjustments
>  ::ipa_param_body_adjustments (ipa_param_adjustments *adjustments,
>                             tree fndecl)
>    : m_adj_params (adjustments->m_adj_params), m_adjustments (adjustments),
> -    m_reset_debug_decls (), m_split_modifications_p (false), m_fndecl 
> (fndecl),
> -    m_id (NULL), m_oparms (), m_new_decls (), m_new_types (),
> -    m_replacements (), m_removed_decls (), m_removed_map (),
> +    m_reset_debug_decls (), m_split_modifications_p (false), m_dead_stmts (),
> +    m_dead_ssas (), m_fndecl (fndecl), m_id (NULL), m_oparms (), m_new_decls 
> (),
> +    m_new_types (), m_replacements (), m_removed_decls (), m_removed_map (),
>      m_method2func (false)
>  {
>    common_initialization (fndecl, NULL, NULL);
> @@ -1190,9 +1289,10 @@ ipa_param_body_adjustments
>                             copy_body_data *id, tree *vars,
>                             vec<ipa_replace_map *, va_gc> *tree_map)
>    : m_adj_params (adjustments->m_adj_params), m_adjustments (adjustments),
> -    m_reset_debug_decls (), m_split_modifications_p (false), m_fndecl 
> (fndecl),
> -    m_id (id), m_oparms (), m_new_decls (), m_new_types (), m_replacements 
> (),
> -    m_removed_decls (), m_removed_map (), m_method2func (false)
> +    m_reset_debug_decls (), m_split_modifications_p (false), m_dead_stmts (),
> +    m_dead_ssas (),m_fndecl (fndecl), m_id (id), m_oparms (), m_new_decls (),
> +    m_new_types (), m_replacements (), m_removed_decls (), m_removed_map (),
> +    m_method2func (false)
>  {
>    common_initialization (old_fndecl, vars, tree_map);
>  }
> @@ -1519,6 +1619,19 @@ remap_split_decl_to_dummy (tree *tp, int 
> *walk_subtrees, void *data)
>    return NULL_TREE;
>  }
>  
> +/* Given ARG which is a SSA_NAME call argument which we are however removing
> +   from the current function and which will be thus removed from the call
> +   statement by ipa_param_adjustments::modify_call, return something that can
> +   be used as a placeholder and which the operand scanner will accept until
> +   then.  */
> +
> +tree
> +ipa_param_body_adjustments::get_removed_call_arg_placeholder (tree arg)
> +{
> +  tree t = create_tmp_var (TREE_TYPE (arg));

If it is temporarily then use _raw.  It looks like you can get called
multiple times for the same arg and each time you get a new temporary
decl?  Ugh.

> +  insert_decl_map (m_id, t, t);

I wonder why you can't use/keep the SSA default def of the removed
parameter instead?  Or a literal zero?  That is, below you don't
seem to be anything special during inlining itself so I presume
those are all removed by call redirection?  Why are the actual
parameters then still needed?  (and error_mark_node doesn't work?)

> +  return t;
> +}
>  
>  /* If the call statement pointed at by STMT_P contains any expressions that
>     need to replaced with a different one as noted by ADJUSTMENTS, do so.  f 
> the
> @@ -1658,7 +1771,10 @@ ipa_param_body_adjustments::modify_call_stmt (gcall 
> **stmt_p)
>         else
>           {
>             tree t = gimple_call_arg (stmt, i);
> -           modify_expression (&t, true);
> +           if (TREE_CODE (t) == SSA_NAME && m_dead_ssas.contains(t))

space after function call (likewise elsewhere).

> +             t = get_removed_call_arg_placeholder (t);
> +           else
> +             modify_expression (&t, true);
>             vargs.safe_push (t);
>           }
>       }
> @@ -1680,7 +1796,11 @@ ipa_param_body_adjustments::modify_call_stmt (gcall 
> **stmt_p)
>    for (unsigned i = 0; i < nargs; i++)
>      {
>        tree *t = gimple_call_arg_ptr (stmt, i);
> -      modified |= modify_expression (t, true);
> +
> +      if (TREE_CODE (*t) == SSA_NAME && m_dead_ssas.contains(*t))
> +     *t = get_removed_call_arg_placeholder (*t);
> +      else
> +     modified |= modify_expression (t, true);
>      }
>  
>    if (gimple_call_lhs (stmt))
> diff --git a/gcc/ipa-param-manipulation.h b/gcc/ipa-param-manipulation.h
> index 0b038ea57f1..59060ae5dcc 100644
> --- a/gcc/ipa-param-manipulation.h
> +++ b/gcc/ipa-param-manipulation.h
> @@ -370,6 +370,12 @@ public:
>    /* Set to true if there are any IPA_PARAM_OP_SPLIT adjustments among stored
>       adjustments.  */
>    bool m_split_modifications_p;
> +
> +  /* Sets of statements and SSA_NAMEs that only manipulate data from 
> parameters
> +     removed because they are not necessary.  */
> +  hash_set<gimple *> m_dead_stmts;
> +  hash_set<tree> m_dead_ssas;
> +
>  private:
>    void common_initialization (tree old_fndecl, tree *vars,
>                             vec<ipa_replace_map *, va_gc> *tree_map);
> @@ -383,6 +389,8 @@ private:
>    bool modify_call_stmt (gcall **stmt_p);
>    bool modify_cfun_body ();
>    void reset_debug_stmts ();
> +  void mark_dead_statements (tree dead_param);
> +  tree get_removed_call_arg_placeholder (tree arg);
>  
>    /* Declaration of the function that is being transformed.  */
>  
> diff --git a/gcc/testsuite/gcc.dg/ipa/ipa-sra-23.c 
> b/gcc/testsuite/gcc.dg/ipa/ipa-sra-23.c
> new file mode 100644
> index 00000000000..f438b509614
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/ipa/ipa-sra-23.c
> @@ -0,0 +1,24 @@
> +/* { dg-do compile } */
> +/* { dg-options "-O2"  } */
> +
> +extern int g;
> +
> +static int __attribute__((noinline))
> +bar (int i, int j)
> +{
> +  return 2*g + i;
> +}
> +
> +static int __attribute__((noinline))
> +foo (int i, int j)
> +{
> +  if (i > 5)
> +    j = 22;
> +  return bar (i, j) + 1;
> +}
> +
> +int
> +entry (int l, int k)
> +{
> +  return foo (l, k);
> +}
> diff --git a/gcc/testsuite/gcc.dg/ipa/pr93385.c 
> b/gcc/testsuite/gcc.dg/ipa/pr93385.c
> new file mode 100644
> index 00000000000..6d1d0d7cd27
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/ipa/pr93385.c
> @@ -0,0 +1,27 @@
> +/* { dg-do run } */
> +/* { dg-options "-O2 -fno-dce -fno-ipa-cp -fno-tree-dce" } */
> +
> +char a, b;
> +
> +#ifdef __SIZEOF_INT128__
> +#define T unsigned __int128
> +#else
> +#define T unsigned
> +#endif
> +
> +static inline int
> +c (T d)
> +{
> +  char e = 0;
> +  d %= (unsigned) d;
> +  e -= 0;
> +  __builtin_strncpy (&a, &e, 1);
> +  return e + b;
> +}
> +
> +int
> +main (void)
> +{
> +  c (~0);
> +  return 0;
> +}
> diff --git a/gcc/tree-inline.c b/gcc/tree-inline.c
> index 3160ca3f88a..60087dd5e7b 100644
> --- a/gcc/tree-inline.c
> +++ b/gcc/tree-inline.c
> @@ -1524,6 +1524,11 @@ remap_gimple_stmt (gimple *stmt, copy_body_data *id)
>         : !opt_for_fn (id->dst_fn, flag_var_tracking_assignments)))
>      return NULL;
>  
> +  if (!is_gimple_debug (stmt)
> +      && id->param_body_adjs
> +      && id->param_body_adjs->m_dead_stmts.contains (stmt))
> +    return NULL;
> +
>    /* Begin by recognizing trees that we'll completely rewrite for the
>       inlining context.  Our output for these trees is completely
>       different from our input (e.g. RETURN_EXPR is deleted and morphs
> @@ -1788,10 +1793,15 @@ remap_gimple_stmt (gimple *stmt, copy_body_data *id)
>  
>        if (gimple_debug_bind_p (stmt))
>       {
> +       tree value;
> +       if (id->param_body_adjs
> +           && id->param_body_adjs->m_dead_stmts.contains (stmt))
> +         value = NULL_TREE;
> +       else
> +         value = gimple_debug_bind_get_value (stmt);
>         gdebug *copy
>           = gimple_build_debug_bind (gimple_debug_bind_get_var (stmt),
> -                                    gimple_debug_bind_get_value (stmt),
> -                                    stmt);
> +                                    value, stmt);
>         if (id->reset_location)
>           gimple_set_location (copy, input_location);
>         id->debug_stmts.safe_push (copy);
> @@ -2671,7 +2681,9 @@ copy_phis_for_bb (basic_block bb, copy_body_data *id)
>        phi = si.phi ();
>        res = PHI_RESULT (phi);
>        new_res = res;
> -      if (!virtual_operand_p (res))
> +      if (!virtual_operand_p (res)
> +       && (!id->param_body_adjs
> +           || !id->param_body_adjs->m_dead_stmts.contains (phi)))
>       {
>         walk_tree (&new_res, copy_tree_body_r, id, NULL);
>         if (EDGE_COUNT (new_bb->preds) == 0)
> 

Reply via email to