https://gcc.gnu.org/g:65a8a2615bb6057578feb2f8760b7841ffebd787

commit 65a8a2615bb6057578feb2f8760b7841ffebd787
Author: Ondřej Machota <ondrejmach...@gmail.com>
Date:   Mon Apr 21 22:47:07 2025 +0200

    rtl-ssa-dce: c++ version, propagate dead phis

Diff:
---
 gcc/dce.cc | 482 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----
 1 file changed, 449 insertions(+), 33 deletions(-)

diff --git a/gcc/dce.cc b/gcc/dce.cc
index c626d19f0747..0a210b008e41 100644
--- a/gcc/dce.cc
+++ b/gcc/dce.cc
@@ -1347,7 +1347,30 @@ struct offset_bitmap {
     }
 };
 
-bool is_rtx_prelive(const_rtx insn) {
+class rtl_ssa_dce
+{
+public:
+  unsigned int execute (function *);
+
+private:
+  bool is_rtx_body_prelive(const_rtx);
+  bool can_delete_call(const_rtx);
+  bool is_rtx_prelive(const_rtx);
+  bool is_prelive(insn_info *);
+  void mark_prelive_insn(insn_info *, auto_vec<set_info *>&);
+  auto_vec<set_info *> mark_prelive();
+  void mark();
+  // void propagate_dead_phis();
+  void reset_dead_debug_insn(insn_info *);
+  void reset_dead_debug();
+  void sweep();
+
+  std::unordered_set<insn_info *> m_marked;
+  std::unordered_set<phi_info *> m_marked_phis;
+};
+
+bool
+rtl_ssa_dce::is_rtx_body_prelive(const_rtx insn) {
   switch (GET_CODE(insn)) {
     case PREFETCH:
     case UNSPEC:
@@ -1359,7 +1382,35 @@ bool is_rtx_prelive(const_rtx insn) {
   }
 }
 
-bool is_rtx_insn_prelive(rtx_insn *insn) {
+bool
+rtl_ssa_dce::can_delete_call (const_rtx insn)
+{
+  if (cfun->can_delete_dead_exceptions)
+    return true;
+  if (!insn_nothrow_p (insn))
+    return false;
+  // if (can_alter_cfg)
+  return true;
+
+  // UD_DCE ignores this and works...
+  /* If we can't alter cfg, even when the call can't throw exceptions, it
+     might have EDGE_ABNORMAL_CALL edges and so we shouldn't delete such
+     calls.  */
+  // gcc_assert (CALL_P (insn));
+  // if (BLOCK_FOR_INSN (insn) && BB_END (BLOCK_FOR_INSN (insn)) == insn)
+  //   {
+  //     edge e;
+  //     edge_iterator ei;
+
+  //     FOR_EACH_EDGE (e, ei, BLOCK_FOR_INSN (insn)->succs)
+       // if ((e->flags & EDGE_ABNORMAL_CALL) != 0)
+       //   return false;
+  //   }
+  // return true;
+}
+
+bool
+rtl_ssa_dce::is_rtx_prelive(const_rtx insn) {
   gcc_assert(insn != nullptr);
 
   if (CALL_P (insn)
@@ -1381,8 +1432,8 @@ bool is_rtx_insn_prelive(rtx_insn *insn) {
   gcc_assert(GET_CODE(insn) == INSN);
 
   /* Don't delete insns that may throw if we cannot do so.  */
-  if (!cfun->can_delete_dead_exceptions && !insn_nothrow_p (insn))
-      return true;
+  if (!cfun->can_delete_dead_exceptions && !insn_nothrow_p (insn)  && 
can_alter_cfg)
+    return true;
  
   /* Callee-save restores are needed.  */
   if (RTX_FRAME_RELATED_P (insn) && crtl->shrink_wrapped_separate 
@@ -1391,11 +1442,301 @@ bool is_rtx_insn_prelive(rtx_insn *insn) {
 
   rtx body = PATTERN(insn);
   switch (GET_CODE(body)) {
+    // Clobbers are handled inside is_prelive
     case CLOBBER: // gcc/gcc/testsuite/gcc.c-torture/compile/20000605-1.c
     case USE:
     case VAR_LOCATION:
       return true;
 
+    case PARALLEL:
+      for (int i = XVECLEN (body, 0) - 1; i >= 0; i--)
+        if (is_rtx_body_prelive (XVECEXP (body, 0, i)))
+          return true;
+      return false;
+
+    default:
+      return is_rtx_body_prelive (body);
+  }
+}
+
+bool
+rtl_ssa_dce::is_prelive(insn_info *insn) {
+  // Phi insns and debug insns are never prelive, bb head and end contain 
+  // artificial uses that we need to mark as prelive
+  if (insn->is_bb_head() || insn->is_bb_end())
+    return true;
+
+  if (insn->is_artificial() || insn->is_debug_insn())
+    return false;
+
+  gcc_assert (insn->is_real());
+  for (def_info *def : insn->defs()) {
+    // The purpose of this pass is not to eliminate memory stores...
+    if (def->is_mem())
+      return true;
+
+    gcc_assert(def->is_reg());
+
+    /* gcc.c-torture/execute/20000503-1.c - flags register is unused. Not all
+       hard registers should be marked... */
+ 
+    /* this ignores clobbers, which is probably fine */
+    if (def->kind() == access_kind::CLOBBER)
+      return true;
+
+    gcc_assert (def->kind() == access_kind::SET);
+
+    /* needed by gcc.c-torture/execute/pr51447.c */
+    if (HARD_REGISTER_NUM_P (def->regno())
+        && global_regs[def->regno()])
+      return true;
+
+    unsigned int picreg = PIC_OFFSET_TABLE_REGNUM;
+    if (picreg != INVALID_REGNUM
+        && fixed_regs[picreg]
+        && def->regno() == picreg)
+      return true;
+  }
+
+  return is_rtx_prelive(insn->rtl());
+}
+
+void
+rtl_ssa_dce::mark_prelive_insn(insn_info *insn, auto_vec<set_info *> 
&worklist) {
+  if (dump_file)
+    fprintf(dump_file, "Insn %d marked as prelive\n", insn->uid());
+
+  m_marked.emplace(insn);
+  for (use_info *use : insn->uses()) {
+    set_info* set = use->def();
+    if (set)
+      worklist.safe_push(set);
+  }
+}
+
+auto_vec<set_info *>
+rtl_ssa_dce::mark_prelive() {
+  if (dump_file)
+    fprintf(dump_file, "DCE: prelive phase\n");
+
+  auto_vec<set_info *> worklist;
+  for (insn_info *insn : crtl->ssa->all_insns()) {
+    if (is_prelive(insn))
+      mark_prelive_insn(insn, worklist);
+  }
+
+  return worklist;
+}
+
+void
+rtl_ssa_dce::mark() {
+  auto worklist = mark_prelive();
+  if (dump_file)
+    fprintf(dump_file, "DCE: marking phase\n");
+
+  while (!worklist.is_empty()) {
+    set_info* set = worklist.pop();
+    insn_info* insn = set->insn();
+    if (!insn)
+      continue;
+
+    /* A phi insn might be visited more times because of having more phi nodes
+       The phi insn is never marked, only phi nodes (phi_info) */
+    auto uid = insn->uid();
+    if ((m_marked.count(insn) > 0) && !insn->is_phi())
+      continue;
+
+    m_marked.emplace(insn);
+
+    use_array uses = insn->uses();
+    if (insn->is_phi()) {
+      /* Each phi node has a unique uid, so only one bitmap (with shift) in
+         might be used. */
+      phi_info* pi = as_a<phi_info *> (set);
+      auto pi_uid = pi->uid();
+      if (m_marked_phis.count(pi) > 0)
+        continue;
+
+      m_marked_phis.emplace(pi);
+      uses = pi->inputs();
+    }
+
+    if (dump_file)
+      fprintf(dump_file, "Adding insn %d to worklist\n", insn->uid());
+    
+    for (use_info * use : uses) {
+      set_info* parent_set = use->def();
+      if (!parent_set)
+        continue;
+
+      worklist.safe_push(parent_set);
+    }
+  }
+}
+
+void
+rtl_ssa_dce::reset_dead_debug_insn(insn_info *insn) {
+  gcc_assert(insn->is_debug_insn());
+
+  if (dump_file)
+    fprintf(dump_file, "Resetting debug insn: %d\n", insn->uid());
+
+  insn_change change (insn);
+  change.new_uses = {};
+  INSN_VAR_LOCATION_LOC (insn->rtl()) = gen_rtx_UNKNOWN_VAR_LOC ();
+  crtl->ssa->change_insn (change);
+}
+
+void
+rtl_ssa_dce::reset_dead_debug() {
+  for (insn_info *insn : crtl->ssa->all_insns()) {
+    if (!insn->is_debug_insn())
+      continue;
+
+    // TODO : use iterate_safely?
+    for (use_info * use: insn->uses()) {
+      insn_info *parent_insn = use->def()->insn();
+      if ((parent_insn->is_phi() && m_marked_phis.count(as_a<phi_info 
*>(use->def())) == 0) 
+      || (!parent_insn->is_phi() && m_marked.count(parent_insn) == 0)) {
+        reset_dead_debug_insn(insn);
+        break;
+      }
+    }
+  }
+}
+
+
+void
+rtl_ssa_dce::sweep() {
+  if (dump_file)
+    fprintf(dump_file, "DCE: Sweep phase\n");
+
+  auto_vec<insn_change> to_delete;
+  for (insn_info *insn : crtl->ssa->nondebug_insns()) {
+    // Artificial or marked insns cannot be deleted.
+    if (insn->is_artificial() || m_marked.count(insn) > 0)
+      continue;
+
+    // std::cout << "delete insn: " << insn->uid() << '\n';
+    if (dump_file)
+      fprintf(dump_file, "Sweeping insn: %d\n", insn->uid());
+
+    auto change = insn_change::delete_insn(insn);
+    to_delete.safe_push(std::move(change));
+
+    // If we use following way with reverse_nondebug_insns, not all insns seem
+    // to be deleted...
+    // crtl->ssa->change_insn(change);
+  }
+
+  auto_vec<insn_change*> changes(to_delete.length());
+  for (size_t i = 0; i != to_delete.length(); ++i) {
+    changes.safe_push(&to_delete[i]);
+  }
+
+  gcc_assert (crtl->ssa->verify_insn_changes(changes));
+  crtl->ssa->change_insns(changes);
+
+  if (dump_file)
+    fprintf(dump_file, "DCE: finish sweep phase\n");
+}
+
+
+unsigned int
+rtl_ssa_dce::execute(function *fn) {
+  calculate_dominance_info(CDI_DOMINATORS);
+  // internal compiler error: gcc.c-torture/execute/20040811-1.c - 
rtl_ssa::function_info::add_phi_nodes
+  crtl->ssa = new rtl_ssa::function_info(fn);
+
+  mark();
+  if (MAY_HAVE_DEBUG_BIND_INSNS)
+    reset_dead_debug();
+  sweep();
+
+  free_dominance_info(CDI_DOMINATORS);
+  if (crtl->ssa->perform_pending_updates())
+    cleanup_cfg(0);
+
+  delete crtl->ssa;
+  crtl->ssa = nullptr;
+}
+
+static bool
+is_rtx_prelive(const_rtx insn) {
+  switch (GET_CODE(insn)) {
+    case PREFETCH:
+    case UNSPEC:
+    case TRAP_IF: /* testsuite/gcc.c-torture/execute/20020418-1.c */
+      return true;
+
+    default:
+      return side_effects_p(insn);
+  }
+}
+
+static bool
+rtl_ssa_dce_can_delete_call (const_rtx insn) {
+  if (cfun->can_delete_dead_exceptions)
+    return true;
+  if (!insn_nothrow_p (insn))
+    return false;
+  // if (can_alter_cfg)
+  return true;
+  /* If we can't alter cfg, even when the call can't throw exceptions, it
+     might have EDGE_ABNORMAL_CALL edges and so we shouldn't delete such
+     calls.  */
+  // gcc_assert (CALL_P (insn));
+  // if (BLOCK_FOR_INSN (insn) && BB_END (BLOCK_FOR_INSN (insn)) == insn)
+  //   {
+  //     edge e;
+  //     edge_iterator ei;
+
+  //     FOR_EACH_EDGE (e, ei, BLOCK_FOR_INSN (insn)->succs)
+       // if ((e->flags & EDGE_ABNORMAL_CALL) != 0)
+       //   return false;
+  //   }
+  // return true;
+}
+
+static bool
+is_rtx_insn_prelive(rtx_insn *insn) {
+  gcc_assert(insn != nullptr);
+
+  if (CALL_P (insn)
+    /* We cannot delete pure or const sibling calls because it is
+      hard to see the result.  */
+    && (!SIBLING_CALL_P (insn))
+    /* We can delete dead const or pure calls as long as they do not
+      infinite loop.  */
+    && (RTL_CONST_OR_PURE_CALL_P (insn) && !RTL_LOOPING_CONST_OR_PURE_CALL_P 
(insn))
+    /* Don't delete calls that may throw if we cannot do so.  */
+    && rtl_ssa_dce_can_delete_call (insn))
+  return false;
+
+  if (!NONJUMP_INSN_P (insn))
+    /* This handles jumps, notes, call_insns, debug_insns, ... */
+    return true;
+
+  /* Only rtx_insn should be handled here */
+  gcc_assert(GET_CODE(insn) == INSN);
+
+  /* Don't delete insns that may throw if we cannot do so.  */
+  if (!cfun->can_delete_dead_exceptions && !insn_nothrow_p (insn))
+    return true;
+ 
+  /* Callee-save restores are needed.  */
+  if (RTX_FRAME_RELATED_P (insn) && crtl->shrink_wrapped_separate 
+      && find_reg_note (insn, REG_CFA_RESTORE, NULL))
+    return true;
+
+  rtx body = PATTERN(insn);
+  switch (GET_CODE(body)) {
+    // Clobbers are handled inside is_prelive
+    // case CLOBBER: // gcc/gcc/testsuite/gcc.c-torture/compile/20000605-1.c
+    case USE:
+    case VAR_LOCATION:
+      return true;
+
     case PARALLEL:
       for (int i = XVECLEN (body, 0) - 1; i >= 0; i--)
         if (is_rtx_prelive (XVECEXP (body, 0, i)))
@@ -1407,21 +1748,20 @@ bool is_rtx_insn_prelive(rtx_insn *insn) {
   }
 }
 
-bool is_prelive(insn_info *insn)
+static bool
+is_prelive(insn_info *insn)
 {
   /* Phi insns and debug insns are never prelive, bb head and end contain 
      artificial uses that we need to mark as prelive */
-  if (insn->is_bb_head() || insn->is_bb_end())
+  if (insn->is_bb_head() || insn->is_bb_end() || insn->is_debug_insn())
     return true;
 
-  if (insn->is_artificial() || insn->is_debug_insn())
+  if (insn->is_artificial())
     return false;
 
   gcc_assert (insn->is_real());
-  auto rtl = insn->rtl();
-
-  for (def_info * def : insn->defs()) {
-    /* The purpose of this pass is not to eliminate stores to memory... */
+  for (def_info *def : insn->defs()) {
+    // The purpose of this pass is not to eliminate memory stores...
     if (def->is_mem())
       return true;
 
@@ -1429,24 +1769,26 @@ bool is_prelive(insn_info *insn)
 
     /* gcc.c-torture/execute/20000503-1.c - flags register is unused. Not all
        hard registers should be marked... */
-       
+
     /* this ignores clobbers, which is probably fine */
-    if (def->kind() != access_kind::SET)
-      continue;
+    if (def->kind() == access_kind::CLOBBER)
+      return true;
+
+    gcc_assert (def->kind() == access_kind::SET);
 
     /* needed by gcc.c-torture/execute/pr51447.c */
     if (HARD_REGISTER_NUM_P (def->regno())
         && global_regs[def->regno()])
-        return true;
+      return true;
 
     unsigned int picreg = PIC_OFFSET_TABLE_REGNUM;
     if (picreg != INVALID_REGNUM
         && fixed_regs[picreg]
         && def->regno() == picreg)
-    return true;
+      return true;
   }
 
-  return is_rtx_insn_prelive(rtl);
+  return is_rtx_insn_prelive(insn->rtl());
 }
 
 static void
@@ -1457,8 +1799,11 @@ rtl_ssa_dce_mark_prelive_insn(insn_info *insn, 
auto_vec<set_info *> &worklist,
     fprintf(dump_file, "Insn %d marked as prelive\n", insn->uid());
 
   marked.emplace(insn);
+  if (insn->is_debug_insn())
+    return;
+
   for (use_info *use : insn->uses()) {
-    set_info* set = use->def();
+    set_info *set = use->def();
     if (set)
       worklist.safe_push(set);
   }
@@ -1479,11 +1824,10 @@ rtl_ssa_dce_mark_prelive(std::unordered_set<insn_info 
*> &marked)
   return worklist;
 }
 
-static std::unordered_set<insn_info *>
-rtl_ssa_dce_mark(std::unordered_set<phi_info *> &marked_phis)
+static void
+rtl_ssa_dce_mark(std::unordered_set<insn_info *> &marked, 
std::unordered_set<phi_info *> &marked_phis)
 {
   /* Phi insn might have more that one phi node: 
gcc/gcc/testsuite/gcc.c-torture/execute/20000224-1.c */
-  std::unordered_set<insn_info *> marked{};
   auto worklist = rtl_ssa_dce_mark_prelive(marked);
   if (dump_file)
     fprintf(dump_file, "DCE: marking phase\n");
@@ -1526,13 +1870,11 @@ rtl_ssa_dce_mark(std::unordered_set<phi_info *> 
&marked_phis)
       worklist.safe_push(parent_set);
     }
   }
-
-  return marked;
 }
 
-// Iterate over non-debug instructions in RPO and remove all aren't marked.
+// Iterate over non-debug instructions in RPO and remove all that aren't 
marked.
 static void
-rtl_ssa_dce_sweep(std::unordered_set<insn_info *> marked)
+rtl_ssa_dce_sweep(std::unordered_set<insn_info *> &marked)
 {
   if (dump_file)
     fprintf(dump_file, "DCE: Sweep phase\n");
@@ -1543,6 +1885,7 @@ rtl_ssa_dce_sweep(std::unordered_set<insn_info *> marked)
     if (insn->is_artificial() || marked.count(insn) > 0)
       continue;
 
+    // std::cout << "delete insn: " << insn->uid() << '\n';
     if (dump_file)
       fprintf(dump_file, "Sweeping insn: %d\n", insn->uid());
 
@@ -1566,9 +1909,14 @@ rtl_ssa_dce_sweep(std::unordered_set<insn_info *> marked)
     fprintf(dump_file, "DCE: finish sweep phase\n");
 }
 
-static void propagate_dead_phis(std::unordered_set<insn_info *> &marked, 
std::unordered_set<phi_info *> &marked_phis) {
+// mark instructions that depend on a dead phi
+static std::unordered_set<insn_info *>
+propagate_dead_phis(std::unordered_set<insn_info *> &marked, 
std::unordered_set<phi_info *> &marked_phis) {
+  std::unordered_set<phi_info *> visited_dead_phis;
+  std::unordered_set<insn_info *> depends_on_dead_phi;
   auto_vec<set_info *> worklist;
-  // TODO : add visited to speedup following section
+
+  // add dead phis to worklist
   for (ebb_info *ebb : crtl->ssa->ebbs()) {
     for (phi_info *phi : ebb->phis()) {
       if (marked_phis.count(phi) > 0) 
@@ -1579,34 +1927,96 @@ static void 
propagate_dead_phis(std::unordered_set<insn_info *> &marked, std::un
   }
 
   // suppose that debug insns are marked - non marked will be removed later
+  // propagate dead phis via du chains and unmark reachable debug instructions
   while (!worklist.is_empty ()) {
     set_info *set = worklist.pop ();
     insn_info *insn = set->insn ();
 
     if (insn->is_debug_insn ()) {
+      if (dump_file)
+        fprintf (dump_file, "Debug insns %d depends on dead phi.\n", 
insn->uid());
       marked.erase (insn);
       // debug instructions dont have chains
       continue;
     }
 
+    // mark
+    if (insn->is_phi())
+      {
+        gcc_assert (marked_phis.count (static_cast<phi_info *>(set)) == 0);
+        visited_dead_phis.emplace (static_cast<phi_info *>(set));
+      }
+    else
+      {
+        gcc_assert (marked.count (insn) == 0);
+        depends_on_dead_phi.emplace (insn);
+      }
+
     for (use_info *use : set->all_uses()) {
       if (use->is_in_phi()) {
-        worklist.emplace (use->phi ());
+        // do not add already visited dead phis
+        if (visited_dead_phis.count (use->phi()) == 0)
+          worklist.safe_push (use->phi ());
       } else {
         gcc_assert(use->is_in_any_insn ());
+        // add all defs from insn to worklist
         for (def_info *def : use->insn()->defs()) {
           if (def->kind() != access_kind::SET)
             continue;
 
-          worklist.emplace(static_cast<set_info *>(def));
+          worklist.safe_push (static_cast<set_info *>(def));
         }
       }
     }
   }
+
+  return depends_on_dead_phi;
+}
+
+static void
+test(insn_info *insn) {
+  rtx_insn *rtl = insn->rtl();
+  rtx set = single_set(rtl);
+  if (set == NULL_RTX 
+    || side_effects_p (SET_SRC (set))
+    || asm_noperands (PATTERN (rtl)) >= 0)
+    return;
+
+  rtx dval = make_debug_expr_from_rtl (SET_DEST (set));
+  rtx bind_var_loc = gen_rtx_VAR_LOCATION (GET_MODE (SET_DEST (set)),
+      DEBUG_EXPR_TREE_DECL (dval),
+      SET_SRC (set),
+      VAR_INIT_STATUS_INITIALIZED);
+  
+  // TODO : change ssa form - we have to keep uses in the first debug insn and
+  // delete other instructions
+  // make dval available in the following iteration
+}
+
+static auto_vec<insn_change>
+wip(std::unordered_set<insn_info *> &marked, std::unordered_set<phi_info *> 
&marked_phis) {
+  std::unordered_set<insn_info *> depends_on_dead_phi = 
propagate_dead_phis(marked, marked_phis);
+  for (insn_info *insn : crtl->ssa->all_insns()) {
+    if (insn->is_phi ()) // insn->is_artificial() ??
+      continue;
+
+    // skip marked instructions
+    if (marked.count (insn) > 0) // debug instruction are still alive
+      continue;
+
+    // we cannot create debug instruction if we depend on a dead phi
+    if (depends_on_dead_phi.count (insn) > 0)
+      continue;
+
+
+    // insn is nondebug and dead
+
+  }
 }
 
 // Clear debug_insn uses and set gen_rtx_UNKNOWN_VAR_LOC
-static void reset_debug_insn_uses(insn_info * insn) {
+static void
+reset_debug_insn_uses(insn_info * insn) {
   gcc_assert(insn->is_debug_insn());
 
   if (dump_file)
@@ -1626,6 +2036,10 @@ 
rtl_ssa_dce_reset_dead_debug(std::unordered_set<insn_info *> &marked, std::unord
     if (!insn->is_debug_insn())
       continue;
 
+    // When propagate_dead_phis is ready, check if insn is in marked. If true, 
then try
+    // to create debug instructions
+    // RTL SSA does not contain debug insntructions... we have to make sure 
that uses are correct
+
     // TODO : use iterate_safely?
     for (use_info * use: insn->uses()) {
       insn_info *parent_insn = use->def()->insn();
@@ -1658,14 +2072,16 @@ rtl_ssa_dce_done()
 }
 
 static unsigned int
-rtl_ssa_dce()
+rtl_ssa_dce_fn()
 {
   rtl_ssa_dce_init();
   // debug(crtl->ssa);
   if (dump_file)
     dump(dump_file, crtl->ssa);
+  std::unordered_set<insn_info *> marked;
   std::unordered_set<phi_info *> marked_phis;
-  std::unordered_set<insn_info *> marked = rtl_ssa_dce_mark(marked_phis);
+  rtl_ssa_dce_mark(marked, marked_phis);
+  propagate_dead_phis(marked, marked_phis);
   if (MAY_HAVE_DEBUG_BIND_INSNS)
     rtl_ssa_dce_reset_dead_debug(marked, marked_phis); 
   rtl_ssa_dce_sweep(marked);
@@ -1721,7 +2137,7 @@ namespace
     /* opt_pass methods: */
     bool gate(function *) final override { return optimize > 0 && flag_dce; }
 
-    unsigned int execute(function *) final override { return rtl_ssa_dce(); }
+    unsigned int execute(function * fn) final override { return 
rtl_ssa_dce_fn(); }
 
   }; // class pass_rtl_ssa_dce

Reply via email to