[patch] Avoid rescanning cfg in tree cfg cleanup

Zdenek Dvorak rakdver@atrey.karlin.mff.cuni.cz
Thu Mar 29 21:21:00 GMT 2007


Hello,

at the moment, we iterate tree cfg cleanup as long as anything changes.  This
typically leads to several basically almost useless walks through
whole cfg.  This patch makes tree cfg cleanup record the basic blocks
whose neigbourhood has changed, and in the later passes iterates
just over these changed blocks.

One part of cfg cleanup that is a bit more difficult to adapt to this
approach is removal of unreachable blocks, that without further
information requires cfg scan.  If we have dominance information
computed, it is however possible to remove the unreachable blocks
directly.  The minor problem is that we then have to maintain
the dominance information during edge removal, which this
patch implements (remove_edge_and_dominated_blocks).

The piece of the patch in tree-inline.c is necessary since
inlining calls tree_purge_dead_eh_edges (that uses
remove_edge_and_dominated_blocks so that we can also call it from
cfg cleanup), but does not try to keep the dominance information
correct, thus making calls to remove_edge_and_dominated_blocks
potentially wrong (also, inlining does not quite like the situation
when the call that it decided to inline disappears due to basic block
removal).

According to my measurements, this change speeds up tree cfg cleanup
by about 20%.  The effect on the compilation time is less significant,
sice tree cfg cleanup usually takes something like 1% of the compilation
time.

Cfg cleanup still does the first pass over whole cfg; this may be
possible to avoid, if the optimizers mark the changed blocks
(which should be trivial e.g. in constant propagation).

Bootstrapped & regtested on i686.

Zdenek

	* tree-cfgcleanup.c (cfgcleanup_altered_bbs): New global variable.
	(remove_fallthru_edge): Use remove_edge_and_dominated_blocks.
	(cleanup_control_expr_graph): Do not invalidate dominance info.
	Record altered blocks.
	(cleanup_control_flow, cleanup_forwarder_blocks): Removed.
	(cleanup_control_flow_bb, split_bbs_on_noreturn_calls,
	cleanup_tree_cfg_bb): New functions.
	(remove_forwarder_block): Do not maintain the worklist of blocks.
	Record altered blocks.
	(cleanup_tree_cfg_1): Iterate over cfgcleanup_altered_bbs,
	not over whole cfg.
	(cleanup_tree_cfg): Do not iterate cleanup_tree_cfg_1.  Only call
	delete_unreachable_blocks if dominators are not available.
	* tree-inline.c (optimize_inline_calls): Free dominance information
	earlier.
	* tree-flow.h (remove_edge_and_dominated_blocks,
	cfgcleanup_altered_bbs): Altered.
	* tree-cfg.c (replace_uses_by, tree_merge_blocks): Record altered
	blocks.
	(get_all_dominated_blocks, remove_edge_and_dominated_blocks): New
	functions.
	(tree_purge_dead_eh_edges): Use remove_edge_and_dominated_blocks,
	do not invalidate dominators.

Index: tree-cfgcleanup.c
===================================================================
*** tree-cfgcleanup.c	(revision 123298)
--- tree-cfgcleanup.c	(working copy)
*************** Boston, MA 02110-1301, USA.  */
*** 48,53 ****
--- 48,62 ----
  #include "tree-ssa-propagate.h"
  #include "tree-scalar-evolution.h"
  
+ /* The set of blocks in that at least one of the following changes happened:
+    -- the statement at the end of the block was changed
+    -- the block was newly created
+    -- the set of the predecessors of the block changed
+    -- the set of the successors of the block changed
+    ??? Maybe we could track these changes separately, since they determine
+        what cleanups it makes sense to try on the block.  */
+ bitmap cfgcleanup_altered_bbs;
+ 
  /* Remove any fallthru edge from EV.  Return true if an edge was removed.  */
  
  static bool
*************** remove_fallthru_edge (VEC(edge,gc) *ev)
*** 59,65 ****
    FOR_EACH_EDGE (e, ei, ev)
      if ((e->flags & EDGE_FALLTHRU) != 0)
        {
! 	remove_edge (e);
  	return true;
        }
    return false;
--- 68,74 ----
    FOR_EACH_EDGE (e, ei, ev)
      if ((e->flags & EDGE_FALLTHRU) != 0)
        {
! 	remove_edge_and_dominated_blocks (e);
  	return true;
        }
    return false;
*************** cleanup_control_expr_graph (basic_block 
*** 124,130 ****
  
  	      taken_edge->probability += e->probability;
  	      taken_edge->count += e->count;
! 	      remove_edge (e);
  	      retval = true;
  	    }
  	  else
--- 133,139 ----
  
  	      taken_edge->probability += e->probability;
  	      taken_edge->count += e->count;
! 	      remove_edge_and_dominated_blocks (e);
  	      retval = true;
  	    }
  	  else
*************** cleanup_control_expr_graph (basic_block 
*** 138,243 ****
    else
      taken_edge = single_succ_edge (bb);
  
    bsi_remove (&bsi, true);
    taken_edge->flags = EDGE_FALLTHRU;
  
-   /* We removed some paths from the cfg.  */
-   free_dominance_info (CDI_DOMINATORS);
- 
    return retval;
  }
  
! /* Try to remove superfluous control structures.  */
  
  static bool
! cleanup_control_flow (void)
  {
-   basic_block bb;
    block_stmt_iterator bsi;
    bool retval = false;
    tree stmt;
  
!   /* Detect cases where a mid-block call is now known not to return.  */
!   if (cfun->gimple_df)
!     while (VEC_length (tree, MODIFIED_NORETURN_CALLS (cfun)))
!       {
! 	stmt = VEC_pop (tree, MODIFIED_NORETURN_CALLS (cfun));
! 	bb = bb_for_stmt (stmt);
! 	if (bb != NULL && last_stmt (bb) != stmt && noreturn_call_p (stmt))
! 	  split_block (bb, stmt);
!       }
! 
!   FOR_EACH_BB (bb)
      {
!       bsi = bsi_last (bb);
! 
!       /* If the last statement of the block could throw and now cannot,
! 	 we need to prune cfg.  */
!       retval |= tree_purge_dead_eh_edges (bb);
! 
!       if (bsi_end_p (bsi))
! 	continue;
! 
!       stmt = bsi_stmt (bsi);
  
!       if (TREE_CODE (stmt) == COND_EXPR
! 	  || TREE_CODE (stmt) == SWITCH_EXPR)
! 	retval |= cleanup_control_expr_graph (bb, bsi);
!       /* If we had a computed goto which has a compile-time determinable
! 	 destination, then we can eliminate the goto.  */
!       else if (TREE_CODE (stmt) == GOTO_EXPR
! 	       && TREE_CODE (GOTO_DESTINATION (stmt)) == ADDR_EXPR
! 	       && (TREE_CODE (TREE_OPERAND (GOTO_DESTINATION (stmt), 0))
! 		   == LABEL_DECL))
! 	{
! 	  edge e;
! 	  tree label;
! 	  edge_iterator ei;
! 	  basic_block target_block;
! 	  bool removed_edge = false;
! 
! 	  /* First look at all the outgoing edges.  Delete any outgoing
! 	     edges which do not go to the right block.  For the one
! 	     edge which goes to the right block, fix up its flags.  */
! 	  label = TREE_OPERAND (GOTO_DESTINATION (stmt), 0);
! 	  target_block = label_to_block (label);
! 	  for (ei = ei_start (bb->succs); (e = ei_safe_edge (ei)); )
  	    {
! 	      if (e->dest != target_block)
! 		{
! 		  removed_edge = true;
! 		  remove_edge (e);
! 		}
! 	      else
! 	        {
! 		  /* Turn off the EDGE_ABNORMAL flag.  */
! 		  e->flags &= ~EDGE_ABNORMAL;
! 
! 		  /* And set EDGE_FALLTHRU.  */
! 		  e->flags |= EDGE_FALLTHRU;
! 		  ei_next (&ei);
! 		}
! 	    }
  
! 	  /* If we removed one or more edges, then we will need to fix the
! 	     dominators.  It may be possible to incrementally update them.  */
! 	  if (removed_edge)
! 	    free_dominance_info (CDI_DOMINATORS);
! 
! 	  /* Remove the GOTO_EXPR as it is not needed.  The CFG has all the
! 	     relevant information we need.  */
! 	  bsi_remove (&bsi, true);
! 	  retval = true;
! 	}
! 
!       /* Check for indirect calls that have been turned into
! 	 noreturn calls.  */
!       else if (noreturn_call_p (stmt) && remove_fallthru_edge (bb->succs))
! 	{
! 	  free_dominance_info (CDI_DOMINATORS);
! 	  retval = true;
  	}
      }
    return retval;
  }
  
--- 147,228 ----
    else
      taken_edge = single_succ_edge (bb);
  
+   bitmap_set_bit (cfgcleanup_altered_bbs, bb->index);
    bsi_remove (&bsi, true);
    taken_edge->flags = EDGE_FALLTHRU;
  
    return retval;
  }
  
! /* Try to remove superfluous control structures in basic block BB.  Returns
!    true if anything changes.  */
  
  static bool
! cleanup_control_flow_bb (basic_block bb)
  {
    block_stmt_iterator bsi;
    bool retval = false;
    tree stmt;
  
!   /* If the last statement of the block could throw and now cannot,
!      we need to prune cfg.  */
!   retval |= tree_purge_dead_eh_edges (bb);
! 
!   bsi = bsi_last (bb);
!   if (bsi_end_p (bsi))
!     return retval;
! 
!   stmt = bsi_stmt (bsi);
! 
!   if (TREE_CODE (stmt) == COND_EXPR
!       || TREE_CODE (stmt) == SWITCH_EXPR)
!     retval |= cleanup_control_expr_graph (bb, bsi);
!   /* If we had a computed goto which has a compile-time determinable
!      destination, then we can eliminate the goto.  */
!   else if (TREE_CODE (stmt) == GOTO_EXPR
! 	   && TREE_CODE (GOTO_DESTINATION (stmt)) == ADDR_EXPR
! 	   && (TREE_CODE (TREE_OPERAND (GOTO_DESTINATION (stmt), 0))
! 	       == LABEL_DECL))
      {
!       edge e;
!       tree label;
!       edge_iterator ei;
!       basic_block target_block;
  
!       /* First look at all the outgoing edges.  Delete any outgoing
! 	 edges which do not go to the right block.  For the one
! 	 edge which goes to the right block, fix up its flags.  */
!       label = TREE_OPERAND (GOTO_DESTINATION (stmt), 0);
!       target_block = label_to_block (label);
!       for (ei = ei_start (bb->succs); (e = ei_safe_edge (ei)); )
! 	{
! 	  if (e->dest != target_block)
! 	    remove_edge_and_dominated_blocks (e);
! 	  else
  	    {
! 	      /* Turn off the EDGE_ABNORMAL flag.  */
! 	      e->flags &= ~EDGE_ABNORMAL;
  
! 	      /* And set EDGE_FALLTHRU.  */
! 	      e->flags |= EDGE_FALLTHRU;
! 	      ei_next (&ei);
! 	    }
  	}
+ 
+       bitmap_set_bit (cfgcleanup_altered_bbs, bb->index);
+       bitmap_set_bit (cfgcleanup_altered_bbs, target_block->index);
+ 
+       /* Remove the GOTO_EXPR as it is not needed.  The CFG has all the
+ 	 relevant information we need.  */
+       bsi_remove (&bsi, true);
+       retval = true;
      }
+ 
+   /* Check for indirect calls that have been turned into
+      noreturn calls.  */
+   else if (noreturn_call_p (stmt) && remove_fallthru_edge (bb->succs))
+     retval = true;
+ 
    return retval;
  }
  
*************** phi_alternatives_equal (basic_block dest
*** 366,377 ****
    return true;
  }
  
! /* Removes forwarder block BB.  Returns false if this failed.  If a new
!    forwarder block is created due to redirection of edges, it is
!    stored to worklist.  */
  
  static bool
! remove_forwarder_block (basic_block bb, basic_block **worklist)
  {
    edge succ = single_succ_edge (bb), e, s;
    basic_block dest = succ->dest;
--- 351,360 ----
    return true;
  }
  
! /* Removes forwarder block BB.  Returns false if this failed.  */
  
  static bool
! remove_forwarder_block (basic_block bb)
  {
    edge succ = single_succ_edge (bb), e, s;
    basic_block dest = succ->dest;
*************** remove_forwarder_block (basic_block bb, 
*** 434,439 ****
--- 417,424 ----
    /* Redirect the edges.  */
    for (ei = ei_start (bb->preds); (e = ei_safe_edge (ei)); )
      {
+       bitmap_set_bit (cfgcleanup_altered_bbs, e->src->index);
+ 
        if (e->flags & EDGE_ABNORMAL)
  	{
  	  /* If there is an abnormal edge, redirect it anyway, and
*************** remove_forwarder_block (basic_block bb, 
*** 450,464 ****
  	  for (phi = phi_nodes (dest); phi; phi = PHI_CHAIN (phi))
  	    add_phi_arg (phi, PHI_ARG_DEF (phi, succ->dest_idx), s);
  	}
-       else
- 	{
- 	  /* The source basic block might become a forwarder.  We know
- 	     that it was not a forwarder before, since it used to have
- 	     at least two outgoing edges, so we may just add it to
- 	     worklist.  */
- 	  if (tree_forwarder_block_p (s->src, false))
- 	    *(*worklist)++ = s->src;
- 	}
      }
  
    if (seen_abnormal_edge)
--- 435,440 ----
*************** remove_forwarder_block (basic_block bb, 
*** 476,481 ****
--- 452,459 ----
  	}
      }
  
+   bitmap_set_bit (cfgcleanup_altered_bbs, dest->index);
+ 
    /* Update the dominators.  */
    if (dom_info_available_p (CDI_DOMINATORS))
      {
*************** remove_forwarder_block (basic_block bb, 
*** 501,565 ****
    return true;
  }
  
! /* Removes forwarder blocks.  */
  
  static bool
! cleanup_forwarder_blocks (void)
  {
-   basic_block bb;
    bool changed = false;
!   basic_block *worklist = XNEWVEC (basic_block, n_basic_blocks);
!   basic_block *current = worklist;
! 
!   FOR_EACH_BB (bb)
!     {
!       if (tree_forwarder_block_p (bb, false))
! 	*current++ = bb;
!     }
  
!   while (current != worklist)
!     {
!       bb = *--current;
!       changed |= remove_forwarder_block (bb, &current);
!     }
  
-   free (worklist);
    return changed;
  }
  
! /* Do one round of CFG cleanup.  */
  
  static bool
! cleanup_tree_cfg_1 (void)
  {
!   bool retval;
  
!   retval = cleanup_control_flow ();
!   retval |= delete_unreachable_blocks ();
  
    /* Forwarder blocks can carry line number information which is
       useful when debugging, so we only clean them up when
       optimizing.  */
  
!   if (optimize > 0)
!     {
!       /* cleanup_forwarder_blocks can redirect edges out of
! 	 SWITCH_EXPRs, which can get expensive.  So we want to enable
! 	 recording of edge to CASE_LABEL_EXPR mappings around the call
! 	 to cleanup_forwarder_blocks.  */
!       start_recording_case_labels ();
!       retval |= cleanup_forwarder_blocks ();
!       end_recording_case_labels ();
!     }
  
    /* Merging the blocks may create new opportunities for folding
       conditional branches (due to the elimination of single-valued PHI
       nodes).  */
!   retval |= merge_seq_blocks ();
  
    return retval;
  }
  
  
  /* Remove unreachable blocks and other miscellaneous clean up work.
     Return true if the flowgraph was modified, false otherwise.  */
--- 479,598 ----
    return true;
  }
  
! /* Split basic blocks on calls in the middle of a basic block that are now
!    known not to return, and remove the unreachable code.  */
  
  static bool
! split_bbs_on_noreturn_calls (void)
  {
    bool changed = false;
!   tree stmt;
!   basic_block bb;
  
!   /* Detect cases where a mid-block call is now known not to return.  */
!   if (cfun->gimple_df)
!     while (VEC_length (tree, MODIFIED_NORETURN_CALLS (cfun)))
!       {
! 	stmt = VEC_pop (tree, MODIFIED_NORETURN_CALLS (cfun));
! 	bb = bb_for_stmt (stmt);
! 	if (bb == NULL
! 	    || last_stmt (bb) == stmt
! 	    || !noreturn_call_p (stmt))
! 	  continue;
! 
! 	changed = true;
! 	split_block (bb, stmt);
! 	remove_fallthru_edge (bb->succs);
!       }
  
    return changed;
  }
  
! /* Tries to cleanup cfg in basic block BB.  Returns true if anything
!    changes.  */
  
  static bool
! cleanup_tree_cfg_bb (basic_block bb)
  {
!   bool retval = false;
  
!   retval = cleanup_control_flow_bb (bb);
  
    /* Forwarder blocks can carry line number information which is
       useful when debugging, so we only clean them up when
       optimizing.  */
  
!   if (optimize > 0
!       && tree_forwarder_block_p (bb, false)
!       && remove_forwarder_block (bb))
!     return true;
  
    /* Merging the blocks may create new opportunities for folding
       conditional branches (due to the elimination of single-valued PHI
       nodes).  */
!   if (single_succ_p (bb)
!       && can_merge_blocks_p (bb, single_succ (bb)))
!     {
!       merge_blocks (bb, single_succ (bb));
!       return true;
!     }
  
    return retval;
  }
  
+ /* Iterate the cfg cleanups, while anything changes.  */
+ 
+ static bool
+ cleanup_tree_cfg_1 (void)
+ {
+   bool retval = false;
+   basic_block bb;
+   unsigned i, n;
+ 
+   retval |= split_bbs_on_noreturn_calls ();
+ 
+   /* Prepare the worklists of altered blocks.  */
+   cfgcleanup_altered_bbs = BITMAP_ALLOC (NULL);
+ 
+   /* During forwarder block cleanup, we may redirect edges out of
+      SWITCH_EXPRs, which can get expensive.  So we want to enable
+      recording of edge to CASE_LABEL_EXPR.  */
+   start_recording_case_labels ();
+ 
+   /* Start by iterating over all basic blocks.  We cannot use FOR_EACH_BB,
+      since the basic blocks may get removed.  */
+   n = last_basic_block;
+   for (i = NUM_FIXED_BLOCKS; i < n; i++)
+     {
+       bb = BASIC_BLOCK (i);
+       if (bb)
+ 	retval |= cleanup_tree_cfg_bb (bb);
+     }
+ 
+   /* Now process the altered blocks, as long as any are available.  */
+   while (!bitmap_empty_p (cfgcleanup_altered_bbs))
+     {
+       i = bitmap_first_set_bit (cfgcleanup_altered_bbs);
+       bitmap_clear_bit (cfgcleanup_altered_bbs, i);
+       if (i < NUM_FIXED_BLOCKS)
+ 	continue;
+ 
+       bb = BASIC_BLOCK (i);
+       if (!bb)
+ 	continue;
+ 
+       retval |= cleanup_tree_cfg_bb (bb);
+ 
+       /* Rerun split_bbs_on_noreturn_calls, in case we have altered any noreturn
+ 	 calls.  */
+       retval |= split_bbs_on_noreturn_calls ();
+     }
+   
+   end_recording_case_labels ();
+   BITMAP_FREE (cfgcleanup_altered_bbs);
+   return retval;
+ }
+ 
  
  /* Remove unreachable blocks and other miscellaneous clean up work.
     Return true if the flowgraph was modified, false otherwise.  */
*************** cleanup_tree_cfg_1 (void)
*** 567,586 ****
  bool
  cleanup_tree_cfg (void)
  {
!   bool retval, changed;
  
    timevar_push (TV_TREE_CLEANUP_CFG);
  
    /* Iterate until there are no more cleanups left to do.  If any
!      iteration changed the flowgraph, set CHANGED to true.  */
!   changed = false;
!   do
      {
!       retval = cleanup_tree_cfg_1 ();
!       changed |= retval;
      }
!   while (retval);
  
    compact_blocks ();
  
  #ifdef ENABLE_CHECKING
--- 600,625 ----
  bool
  cleanup_tree_cfg (void)
  {
!   bool changed;
  
    timevar_push (TV_TREE_CLEANUP_CFG);
  
    /* Iterate until there are no more cleanups left to do.  If any
!      iteration changed the flowgraph, set CHANGED to true.
! 
!      If dominance information is available, there cannot be any unreachable
!      blocks.  */
!   if (!dom_computed[CDI_DOMINATORS])
      {
!       changed = delete_unreachable_blocks ();
!       calculate_dominance_info (CDI_DOMINATORS);
      }
!   else
!     changed = false;
  
+   changed |= cleanup_tree_cfg_1 ();
+ 
+   gcc_assert (dom_computed[CDI_DOMINATORS]);
    compact_blocks ();
  
  #ifdef ENABLE_CHECKING
*************** cleanup_tree_cfg_loop (void)
*** 602,608 ****
    if (changed)
      {
        bitmap changed_bbs = BITMAP_ALLOC (NULL);
-       calculate_dominance_info (CDI_DOMINATORS);
        fix_loop_structure (changed_bbs);
  
        /* This usually does nothing.  But sometimes parts of cfg that originally
--- 641,646 ----
Index: tree-inline.c
===================================================================
*** tree-inline.c	(revision 123298)
--- tree-inline.c	(working copy)
*************** optimize_inline_calls (tree fn)
*** 2807,2812 ****
--- 2807,2816 ----
  
    push_gimplify_context ();
  
+   /* We make no attempts to keep dominance info up-to-date.  */
+   free_dominance_info (CDI_DOMINATORS);
+   free_dominance_info (CDI_POST_DOMINATORS);
+ 
    /* Reach the trees by walking over the CFG, and note the
       enclosing basic-blocks in the call edges.  */
    /* We walk the blocks going forward, because inlined function bodies
*************** optimize_inline_calls (tree fn)
*** 2843,2851 ****
    fold_cond_expr_cond ();
    if (current_function_has_nonlocal_label)
      make_nonlocal_label_edges ();
-   /* We make no attempts to keep dominance info up-to-date.  */
-   free_dominance_info (CDI_DOMINATORS);
-   free_dominance_info (CDI_POST_DOMINATORS);
    /* It would be nice to check SSA/CFG/statement consistency here, but it is
       not possible yet - the IPA passes might make various functions to not
       throw and they don't care to proactively update local EH info.  This is
--- 2847,2852 ----
Index: tree-flow.h
===================================================================
*** tree-flow.h	(revision 123298)
--- tree-flow.h	(working copy)
*************** extern void start_recording_case_labels 
*** 668,675 ****
--- 668,677 ----
  extern void end_recording_case_labels (void);
  extern basic_block move_sese_region_to_fn (struct function *, basic_block,
  				           basic_block);
+ void remove_edge_and_dominated_blocks (edge);
  
  /* In tree-cfgcleanup.c  */
+ extern bitmap cfgcleanup_altered_bbs;
  extern bool cleanup_tree_cfg (void);
  extern bool cleanup_tree_cfg_loop (void);
  
Index: tree-cfg.c
===================================================================
*** tree-cfg.c	(revision 123298)
--- tree-cfg.c	(working copy)
*************** replace_uses_by (tree name, tree val)
*** 1196,1201 ****
--- 1196,1203 ----
  	  tree rhs;
  
  	  fold_stmt_inplace (stmt);
+ 	  if (cfgcleanup_altered_bbs)
+ 	    bitmap_set_bit (cfgcleanup_altered_bbs, bb_for_stmt (stmt)->index);
  
  	  /* FIXME.  This should go in pop_stmt_changes.  */
  	  rhs = get_rhs (stmt);
*************** tree_merge_blocks (basic_block a, basic_
*** 1309,1314 ****
--- 1311,1319 ----
    last = tsi_last (a->stmt_list);
    tsi_link_after (&last, b->stmt_list, TSI_NEW_STMT);
    b->stmt_list = NULL;
+ 
+   if (cfgcleanup_altered_bbs)
+     bitmap_set_bit (cfgcleanup_altered_bbs, a->index);
  }
  
  
*************** tree_purge_dead_abnormal_call_edges (bas
*** 5421,5426 ****
--- 5426,5569 ----
    return changed;
  }
  
+ /* Stores all basic blocks dominated by BB to DOM_BBS.  */
+ 
+ static void
+ get_all_dominated_blocks (basic_block bb, VEC (basic_block, heap) **dom_bbs)
+ {
+   basic_block son;
+ 
+   VEC_safe_push (basic_block, heap, *dom_bbs, bb);
+   for (son = first_dom_son (CDI_DOMINATORS, bb);
+        son;
+        son = next_dom_son (CDI_DOMINATORS, son))
+     get_all_dominated_blocks (son, dom_bbs);
+ }
+ 
+ /* Removes edge E and all the blocks dominated by it, and updates dominance
+    information.  The IL in E->src needs to be updated separately.
+    If dominance info is not available, only the edge E is removed.*/
+ 
+ void
+ remove_edge_and_dominated_blocks (edge e)
+ {
+   VEC (basic_block, heap) *bbs_to_remove = NULL;
+   VEC (basic_block, heap) *bbs_to_fix_dom = NULL;
+   bitmap df, df_idom;
+   edge f;
+   edge_iterator ei;
+   bool none_removed = false;
+   unsigned i;
+   basic_block bb, dbb;
+   bitmap_iterator bi;
+ 
+   if (!dom_computed[CDI_DOMINATORS])
+     {
+       remove_edge (e);
+       return;
+     }
+ 
+   /* No updating is needed for edges to exit.  */
+   if (e->dest == EXIT_BLOCK_PTR)
+     {
+       if (cfgcleanup_altered_bbs)
+ 	bitmap_set_bit (cfgcleanup_altered_bbs, e->src->index);
+       remove_edge (e);
+       return;
+     }
+ 
+   /* First, we find the basic blocks to remove.  If E->dest has a predecessor
+      that is not dominated by E->dest, then this set is empty.  Otherwise,
+      all the basic blocks dominated by E->dest are removed.
+ 
+      Also, to DF_IDOM we store the immediate dominators of the blocks in
+      the dominance frontier of E (i.e., of the successors of the
+      removed blocks, if there are any, and of E->dest otherwise).  */
+   FOR_EACH_EDGE (f, ei, e->dest->preds)
+     {
+       if (f == e)
+ 	continue;
+ 
+       if (!dominated_by_p (CDI_DOMINATORS, f->src, e->dest))
+ 	{
+ 	  none_removed = true;
+ 	  break;
+ 	}
+     }
+ 
+   df = BITMAP_ALLOC (NULL);
+   df_idom = BITMAP_ALLOC (NULL);
+ 
+   if (none_removed)
+     bitmap_set_bit (df_idom,
+ 		    get_immediate_dominator (CDI_DOMINATORS, e->dest)->index);
+   else
+     {
+       get_all_dominated_blocks (e->dest, &bbs_to_remove);
+       for (i = 0; VEC_iterate (basic_block, bbs_to_remove, i, bb); i++)
+ 	{
+ 	  FOR_EACH_EDGE (f, ei, bb->succs)
+ 	    {
+ 	      if (f->dest != EXIT_BLOCK_PTR)
+ 		bitmap_set_bit (df, f->dest->index);
+ 	    }
+ 	}
+       for (i = 0; VEC_iterate (basic_block, bbs_to_remove, i, bb); i++)
+ 	bitmap_clear_bit (df, bb->index);
+ 
+       EXECUTE_IF_SET_IN_BITMAP (df, 0, i, bi)
+ 	{
+ 	  bb = BASIC_BLOCK (i);
+ 	  bitmap_set_bit (df_idom,
+ 			  get_immediate_dominator (CDI_DOMINATORS, bb)->index);
+ 	}
+     }
+ 
+   if (cfgcleanup_altered_bbs)
+     {
+       /* Record the set of the altered basic blocks.  */
+       bitmap_set_bit (cfgcleanup_altered_bbs, e->src->index);
+       bitmap_ior_into (cfgcleanup_altered_bbs, df);
+     }
+ 
+   /* Remove E and the cancelled blocks.  */
+   if (none_removed)
+     remove_edge (e);
+   else
+     {
+       for (i = 0; VEC_iterate (basic_block, bbs_to_remove, i, bb); i++)
+ 	delete_basic_block (bb);
+     }
+ 
+   /* Update the dominance information.  The immediate dominator may change only
+      for blocks whose immediate dominator belongs to DF_IDOM:
+    
+      Suppose that idom(X) = Y before removal of E and idom(X) != Y after the
+      removal.  Let Z the arbitrary block such that idom(Z) = Y and
+      Z dominates X after the removal.  Before removal, there exists a path P
+      from Y to X that avoids Z.  Let F be the last edge on P that is
+      removed, and let W = F->dest.  Before removal, idom(W) = Y (since Y
+      dominates W, and because of P, Z does not dominate W), and W belongs to
+      the dominance frontier of E.  Therefore, Y belongs to DF_IDOM.  */ 
+   EXECUTE_IF_SET_IN_BITMAP (df_idom, 0, i, bi)
+     {
+       bb = BASIC_BLOCK (i);
+       for (dbb = first_dom_son (CDI_DOMINATORS, bb);
+ 	   dbb;
+ 	   dbb = next_dom_son (CDI_DOMINATORS, dbb))
+ 	VEC_safe_push (basic_block, heap, bbs_to_fix_dom, dbb);
+     }
+ 
+   iterate_fix_dominators (CDI_DOMINATORS,
+ 			  VEC_address (basic_block, bbs_to_fix_dom),
+ 			  VEC_length (basic_block, bbs_to_fix_dom));
+ 
+   BITMAP_FREE (df);
+   BITMAP_FREE (df_idom);
+   VEC_free (basic_block, heap, bbs_to_remove);
+   VEC_free (basic_block, heap, bbs_to_fix_dom);
+ }
+ 
  /* Purge dead EH edges from basic block BB.  */
  
  bool
*************** tree_purge_dead_eh_edges (basic_block bb
*** 5438,5470 ****
      {
        if (e->flags & EDGE_EH)
  	{
! 	  remove_edge (e);
  	  changed = true;
  	}
        else
  	ei_next (&ei);
      }
  
-   /* Removal of dead EH edges might change dominators of not
-      just immediate successors.  E.g. when bb1 is changed so that
-      it no longer can throw and bb1->bb3 and bb1->bb4 are dead
-      eh edges purged by this function in:
-            0
- 	  / \
- 	 v   v
- 	 1-->2
-         / \  |
-        v   v |
-        3-->4 |
-         \    v
- 	 --->5
- 	     |
- 	     -
-      idom(bb5) must be recomputed.  For now just free the dominance
-      info.  */
-   if (changed)
-     free_dominance_info (CDI_DOMINATORS);
- 
    return changed;
  }
  
--- 5581,5593 ----
      {
        if (e->flags & EDGE_EH)
  	{
! 	  remove_edge_and_dominated_blocks (e);
  	  changed = true;
  	}
        else
  	ei_next (&ei);
      }
  
    return changed;
  }
  



More information about the Gcc-patches mailing list