https://gcc.gnu.org/g:40ddc0b05a47f999b24f20c1becb79004995731b

commit r13-8594-g40ddc0b05a47f999b24f20c1becb79004995731b
Author: Martin Jambor <mjam...@suse.cz>
Date:   Mon Apr 8 17:34:33 2024 +0200

    ipa: Self-DCE of uses of removed call LHSs (PR 108007)
    
    PR 108007 is another manifestation where we rely on DCE to clean-up
    after IPA-SRA and if the user explicitely switches DCE off, IPA-SRA
    can leave behind statements which are fed uninitialized values and
    trap, even though their results are themselves never used.
    
    I have already fixed this for unused parameters in callees, this bug
    shows that almost the same thing can happen for removed returns, on
    the side of callers.  This means that the issue has to be fixed
    elsewhere, in call redirection.  This patch adds a function which
    looks for (and through, using a work-list) uses of operations fed
    specific SSA names and removes them all.
    
    That would have been easy if it wasn't for debug statements during
    tree-inline (from which call redirection is also invoked).  Debug
    statements are decoupled from the rest at this point and iterating
    over uses of SSAs does not bring them up.  During tree-inline they are
    handled especially at the end, I assume in order to make sure that
    relative ordering of UIDs are the same with and without debug info.
    
    This means that during tree-inline we need to make a hash of killed
    SSAs, that we already have in copy_body_data, available to the
    function making the purging.  So the patch duly does also that, making
    the interface slightly ugly.  Moreover, all newly unused SSA names
    need to be freed and as PR 112616 showed, it must be done in a defined
    order, which is what newly added ipa_release_ssas_in_hash does.
    
    This backport to gcc-13 also contains
    54e505d0446f86b7ad383acbb8e5501f20872b64 in order not to reintroduce
    PR 113757.
    
    gcc/ChangeLog:
    
    2024-04-05  Martin Jambor  <mjam...@suse.cz>
    
            PR ipa/108007
            PR ipa/112616
            * cgraph.h (cgraph_edge): Add a parameter to
            redirect_call_stmt_to_callee.
            * ipa-param-manipulation.h (ipa_param_adjustments): Add a
            parameter to modify_call.
            (ipa_release_ssas_in_hash): Declare.
            * cgraph.cc (cgraph_edge::redirect_call_stmt_to_callee): New
            parameter killed_ssas, pass it to padjs->modify_call.
            * ipa-param-manipulation.cc (purge_all_uses): New function.
            (ipa_param_adjustments::modify_call): New parameter killed_ssas.
            Instead of substituting uses, invoke purge_all_uses.  If
            hash of killed SSAs has not been provided, create a temporary one
            and release SSAs that have been added to it.
            (compare_ssa_versions): New function.
            (ipa_release_ssas_in_hash): Likewise.
            * tree-inline.cc (redirect_all_calls): Create
            id->killed_new_ssa_names earlier, pass it to edge redirection,
            adjust a comment.
            (copy_body): Release SSAs in id->killed_new_ssa_names.
    
    gcc/testsuite/ChangeLog:
    
    2024-01-15  Martin Jambor  <mjam...@suse.cz>
    
            PR ipa/108007
            PR ipa/112616
            * gcc.dg/ipa/pr108007.c: New test.
            * gcc.dg/ipa/pr112616.c: Likewise.
    
    (cherry picked from commit a9a8426e534760b8d3a250e9bd3cff4db131a2be)

Diff:
---
 gcc/cgraph.cc                       |  10 +++-
 gcc/cgraph.h                        |   9 ++-
 gcc/ipa-param-manipulation.cc       | 112 +++++++++++++++++++++++++++++-------
 gcc/ipa-param-manipulation.h        |   5 +-
 gcc/testsuite/g++.dg/ipa/pr113757.C |  14 +++++
 gcc/testsuite/gcc.dg/ipa/pr108007.c |  32 +++++++++++
 gcc/testsuite/gcc.dg/ipa/pr112616.c |  28 +++++++++
 gcc/tree-inline.cc                  |  27 ++++-----
 8 files changed, 193 insertions(+), 44 deletions(-)

diff --git a/gcc/cgraph.cc b/gcc/cgraph.cc
index ec663d23385..7a14c00b60a 100644
--- a/gcc/cgraph.cc
+++ b/gcc/cgraph.cc
@@ -1403,11 +1403,17 @@ cgraph_edge::redirect_callee (cgraph_node *n)
    speculative indirect call, remove "speculative" of the indirect call and
    also redirect stmt to it's final direct target.
 
+   When called from within tree-inline, KILLED_SSAs has to contain the pointer
+   to killed_new_ssa_names within the copy_body_data structure and SSAs
+   discovered to be useless (if LHS is removed) will be added to it, otherwise
+   it needs to be NULL.
+
    It is up to caller to iteratively transform each "speculative"
    direct call as appropriate.  */
 
 gimple *
-cgraph_edge::redirect_call_stmt_to_callee (cgraph_edge *e)
+cgraph_edge::redirect_call_stmt_to_callee (cgraph_edge *e,
+                                          hash_set <tree> *killed_ssas)
 {
   tree decl = gimple_call_fndecl (e->call_stmt);
   gcall *new_stmt;
@@ -1527,7 +1533,7 @@ cgraph_edge::redirect_call_stmt_to_callee (cgraph_edge *e)
        remove_stmt_from_eh_lp (e->call_stmt);
 
       tree old_fntype = gimple_call_fntype (e->call_stmt);
-      new_stmt = padjs->modify_call (e, false);
+      new_stmt = padjs->modify_call (e, false, killed_ssas);
       cgraph_node *origin = e->callee;
       while (origin->clone_of)
        origin = origin->clone_of;
diff --git a/gcc/cgraph.h b/gcc/cgraph.h
index f5f54769eda..c1a3691b6f5 100644
--- a/gcc/cgraph.h
+++ b/gcc/cgraph.h
@@ -1833,9 +1833,16 @@ public:
      speculative indirect call, remove "speculative" of the indirect call and
      also redirect stmt to it's final direct target.
 
+     When called from within tree-inline, KILLED_SSAs has to contain the
+     pointer to killed_new_ssa_names within the copy_body_data structure and
+     SSAs discovered to be useless (if LHS is removed) will be added to it,
+     otherwise it needs to be NULL.
+
      It is up to caller to iteratively transform each "speculative"
      direct call as appropriate.  */
-  static gimple *redirect_call_stmt_to_callee (cgraph_edge *e);
+  static gimple *redirect_call_stmt_to_callee (cgraph_edge *e,
+                                              hash_set <tree>
+                                              *killed_ssas = nullptr);
 
   /* Create clone of edge in the node N represented
      by CALL_EXPR the callgraph.  */
diff --git a/gcc/ipa-param-manipulation.cc b/gcc/ipa-param-manipulation.cc
index 42488ee09c3..182f0c6741e 100644
--- a/gcc/ipa-param-manipulation.cc
+++ b/gcc/ipa-param-manipulation.cc
@@ -593,14 +593,65 @@ isra_get_ref_base_and_offset (tree expr, tree *base_p, 
unsigned *unit_offset_p)
   return true;
 }
 
+/* Remove all statements that use NAME directly or indirectly.  KILLED_SSAS
+   contains the SSA_NAMEs that are already being or have been processed and new
+   ones need to be added to it.  The function only has to process situations
+   handled by ssa_name_only_returned_p in ipa-sra.cc with the exception that it
+   can assume it must never reach a use in a return statement.  */
+
+static void
+purge_all_uses (tree name, hash_set <tree> *killed_ssas)
+{
+  imm_use_iterator imm_iter;
+  gimple *stmt;
+  auto_vec <tree, 4> worklist;
+
+  worklist.safe_push (name);
+  while (!worklist.is_empty ())
+    {
+      tree cur_name = worklist.pop ();
+      FOR_EACH_IMM_USE_STMT (stmt, imm_iter, cur_name)
+       {
+         if (gimple_debug_bind_p (stmt))
+           {
+             /* When runing within tree-inline, we will never end up here but
+                adding the SSAs to killed_ssas will do the trick in this case
+                and the respective debug statements will get reset. */
+             gimple_debug_bind_reset_value (stmt);
+             update_stmt (stmt);
+             continue;
+           }
+
+         tree lhs = NULL_TREE;
+         if (is_gimple_assign (stmt))
+           lhs = gimple_assign_lhs (stmt);
+         else if (gimple_code (stmt) == GIMPLE_PHI)
+           lhs = gimple_phi_result (stmt);
+         gcc_assert (lhs
+                     && (TREE_CODE (lhs) == SSA_NAME)
+                     && !gimple_vdef (stmt));
+         if (!killed_ssas->add (lhs))
+           {
+             worklist.safe_push (lhs);
+             gimple_stmt_iterator gsi = gsi_for_stmt (stmt);
+             gsi_remove (&gsi, true);
+           }
+       }
+    }
+}
+
 /* Modify actual arguments of a function call in statement currently belonging
    to CS, and make it call CS->callee->decl.  Return the new statement that
    replaced the old one.  When invoked, cfun and current_function_decl have to
-   be set to the caller.  */
+   be set to the caller.  When called from within tree-inline, KILLED_SSAs has
+   to contain the pointer to killed_new_ssa_names within the copy_body_data
+   structure and SSAs discovered to be useless (if LHS is removed) will be
+   added to it, otherwise it needs to be NULL.  */
 
 gcall *
 ipa_param_adjustments::modify_call (cgraph_edge *cs,
-                                   bool update_references)
+                                   bool update_references,
+                                   hash_set <tree> *killed_ssas)
 {
   gcall *stmt = cs->call_stmt;
   tree callee_decl = cs->callee->decl;
@@ -910,32 +961,20 @@ ipa_param_adjustments::modify_call (cgraph_edge *cs,
 
   gcall *new_stmt = gimple_build_call_vec (callee_decl, vargs);
 
-  tree ssa_to_remove = NULL;
+  hash_set <tree> *ssas_to_remove = NULL;
   if (tree lhs = gimple_call_lhs (stmt))
     {
       if (!m_skip_return)
        gimple_call_set_lhs (new_stmt, lhs);
       else if (TREE_CODE (lhs) == SSA_NAME)
        {
-         /* LHS should now by a default-def SSA.  Unfortunately default-def
-            SSA_NAMEs need a backing variable (or at least some code examining
-            SSAs assumes it is non-NULL).  So we either have to re-use the
-            decl we have at hand or introdice a new one.  */
-         tree repl = create_tmp_var (TREE_TYPE (lhs), "removed_return");
-         repl = get_or_create_ssa_default_def (cfun, repl);
-         SSA_NAME_IS_DEFAULT_DEF (repl) = true;
-         imm_use_iterator ui;
-         use_operand_p use_p;
-         gimple *using_stmt;
-         FOR_EACH_IMM_USE_STMT (using_stmt, ui, lhs)
+         if (!killed_ssas)
            {
-             FOR_EACH_IMM_USE_ON_STMT (use_p, ui)
-               {
-                 SET_USE (use_p, repl);
-               }
-             update_stmt (using_stmt);
+             ssas_to_remove = new hash_set<tree> (8);
+             killed_ssas = ssas_to_remove;
            }
-         ssa_to_remove = lhs;
+         killed_ssas->add (lhs);
+         purge_all_uses (lhs, killed_ssas);
        }
     }
 
@@ -954,8 +993,11 @@ ipa_param_adjustments::modify_call (cgraph_edge *cs,
       fprintf (dump_file, "\n");
     }
   gsi_replace (&gsi, new_stmt, true);
-  if (ssa_to_remove)
-    release_ssa_name (ssa_to_remove);
+  if (ssas_to_remove)
+    {
+      ipa_release_ssas_in_hash (ssas_to_remove);
+      delete ssas_to_remove;
+    }
   if (update_references)
     do
       {
@@ -2502,4 +2544,30 @@ ipa_edge_modifications_finalize ()
   ipa_edge_modifications = NULL;
 }
 
+/* Helper used to sort a vector of SSA_NAMES. */
 
+static int
+compare_ssa_versions (const void *va, const void *vb)
+{
+  const_tree const a = *(const_tree const*)va;
+  const_tree const b = *(const_tree const*)vb;
+
+  if (SSA_NAME_VERSION (a) < SSA_NAME_VERSION (b))
+    return -1;
+  if (SSA_NAME_VERSION (a) > SSA_NAME_VERSION (b))
+    return 1;
+  return 0;
+}
+
+/* Call release_ssa_name on all elements in KILLED_SSAS in a defined order.  */
+
+void
+ipa_release_ssas_in_hash (hash_set <tree> *killed_ssas)
+{
+  auto_vec<tree, 16> ssas_to_release;
+  for (tree sn : *killed_ssas)
+    ssas_to_release.safe_push (sn);
+  ssas_to_release.qsort (compare_ssa_versions);
+  for (tree sn : ssas_to_release)
+    release_ssa_name (sn);
+}
diff --git a/gcc/ipa-param-manipulation.h b/gcc/ipa-param-manipulation.h
index 9cc957ae7bb..af898f4695e 100644
--- a/gcc/ipa-param-manipulation.h
+++ b/gcc/ipa-param-manipulation.h
@@ -224,7 +224,8 @@ public:
 
   /* Modify a call statement arguments (and possibly remove the return value)
      as described in the data fields of this class.  */
-  gcall *modify_call (cgraph_edge *cs, bool update_references);
+  gcall *modify_call (cgraph_edge *cs, bool update_references,
+                     hash_set <tree> *killed_ssas);
   /* Return if the first parameter is left intact.  */
   bool first_param_intact_p ();
   /* Build a function type corresponding to the modified call.  */
@@ -440,6 +441,6 @@ void push_function_arg_decls (vec<tree> *args, tree fndecl);
 void push_function_arg_types (vec<tree> *types, tree fntype);
 void ipa_verify_edge_has_no_modifications (cgraph_edge *cs);
 void ipa_edge_modifications_finalize ();
-
+void ipa_release_ssas_in_hash (hash_set <tree> *killed_ssas);
 
 #endif /* IPA_PARAM_MANIPULATION_H */
diff --git a/gcc/testsuite/g++.dg/ipa/pr113757.C 
b/gcc/testsuite/g++.dg/ipa/pr113757.C
new file mode 100644
index 00000000000..885d4010a10
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ipa/pr113757.C
@@ -0,0 +1,14 @@
+// { dg-do compile }
+// { dg-options "-O2 -fPIC" }
+// { dg-require-effective-target fpic }
+
+long size();
+struct ll {  virtual int hh();  };
+ll  *slice_owner;
+int ll::hh() { __builtin_exit(0); }
+int nn() {
+  if (size())
+    return 0;
+  return slice_owner->hh();
+}
+int (*a)() = nn;
diff --git a/gcc/testsuite/gcc.dg/ipa/pr108007.c 
b/gcc/testsuite/gcc.dg/ipa/pr108007.c
new file mode 100644
index 00000000000..77fc95975cf
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/ipa/pr108007.c
@@ -0,0 +1,32 @@
+/* { dg-do run } */
+/* { dg-options "-Os -fno-dce -fno-tree-dce -g" } */
+
+/* This tests that when IPA-SRA removes a LHS of a call statement which, in the
+   original source, is fed into a useless operation which however can trap when
+   given nonsensical input, that we remove it even when the user has turned off
+   normal DCE.  */
+
+int a, b, d, e, f = 10000000, h;
+short c, g;
+static int *i() {
+  g = f;
+ L:
+  h = e = ~g;
+  g = ~f % g & e;
+  if (!g)
+    goto L;
+  c++;
+  while (g < 1)
+    ;
+  return &a;
+}
+static void k() {
+  int *l, m = 2;
+  l = i();
+  for (; d < 1; d++)
+    m |= *l >= b;
+}
+int main() {
+  k();
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.dg/ipa/pr112616.c 
b/gcc/testsuite/gcc.dg/ipa/pr112616.c
new file mode 100644
index 00000000000..5f730da71e7
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/ipa/pr112616.c
@@ -0,0 +1,28 @@
+/* { dg-do run } */
+/* { dg-options "-O2" } */
+
+unsigned a;
+int b, d, e, f = 2, g, h = 1, *i = &b;
+volatile int c = 1;
+static int *o() {
+  long m = ~a;
+  int j = f / b, k = f - 1, n = m << -1 / ~g / k;
+  if (j && n)
+    c;
+  return &e;
+}
+static long p() {
+  int *q = 0, **r = &q;
+  if (c) {
+    *i = h;
+    *r = o();
+  }
+  return *q;
+}
+int main() {
+  p();
+  int *l = 0;
+  if (d)
+    c = *l;
+  return 0;
+}
diff --git a/gcc/tree-inline.cc b/gcc/tree-inline.cc
index c702f0032a1..72a80c0c74d 100644
--- a/gcc/tree-inline.cc
+++ b/gcc/tree-inline.cc
@@ -2978,24 +2978,13 @@ redirect_all_calls (copy_body_data * id, basic_block bb)
       gimple *stmt = gsi_stmt (si);
       if (is_gimple_call (stmt))
        {
-         tree old_lhs = gimple_call_lhs (stmt);
          struct cgraph_edge *edge = id->dst_node->get_edge (stmt);
          if (edge)
            {
-             gimple *new_stmt
-               = cgraph_edge::redirect_call_stmt_to_callee (edge);
-             /* If IPA-SRA transformation, run as part of edge redirection,
-                removed the LHS because it is unused, save it to
-                killed_new_ssa_names so that we can prune it from debug
-                statements.  */
-             if (old_lhs
-                 && TREE_CODE (old_lhs) == SSA_NAME
-                 && !gimple_call_lhs (new_stmt))
-               {
-                 if (!id->killed_new_ssa_names)
-                   id->killed_new_ssa_names = new hash_set<tree> (16);
-                 id->killed_new_ssa_names->add (old_lhs);
-               }
+             if (!id->killed_new_ssa_names)
+               id->killed_new_ssa_names = new hash_set<tree> (16);
+             cgraph_edge::redirect_call_stmt_to_callee (edge,
+               id->killed_new_ssa_names);
 
              if (stmt == last && id->call_stmt && maybe_clean_eh_stmt (stmt))
                gimple_purge_dead_eh_edges (bb);
@@ -3322,8 +3311,12 @@ copy_body (copy_body_data *id,
   body = copy_cfg_body (id, entry_block_map, exit_block_map,
                        new_entry);
   copy_debug_stmts (id);
-  delete id->killed_new_ssa_names;
-  id->killed_new_ssa_names = NULL;
+  if (id->killed_new_ssa_names)
+    {
+      ipa_release_ssas_in_hash (id->killed_new_ssa_names);
+      delete id->killed_new_ssa_names;
+      id->killed_new_ssa_names = NULL;
+    }
 
   return body;
 }

Reply via email to