This is the mail archive of the gcc-patches@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[patch] Merge PHI nodes.


Hi,

Attached is a patch to add a new pass to merge PHI nodes.

This pass merges PHI nodes if one feeds into another.  For example,
suppose we have the following:

  goto <bb 9> (<L9>);

<L8>:;
  tem_17 = foo ();

  # tem_6 = PHI <tem_17(8), tem_23(7)>;
<L9>:;

  # tem_3 = PHI <tem_6(9), tem_2(5)>;
<L10>:;

Then we merge the first PHI node into the second one like so:

  goto <bb 9> (<L10>);

<L8>:;
  tem_17 = foo ();

  # tem_3 = PHI <tem_23(7), tem_2(5), tem_17(8)>;
<L10>:;

The patch does achieves this by using an approach similar to Zdenek's
new cleanup_forwarder_blocks.  The only difference is that I am trying
to remove forwarder blocks with PHI nodes.  Zdenek's version tries to
remove those without.

I am sharing has_abnormal_incoming_edge_p and phi_alternatives_equal
with cleanup_forwarder_blocks as they don't have any abstraction
penalty.

Here is the timing for compiling cc1-i files.

      original patched
real:  302.232 301.696 (0.177% down)
user:  291.014 290.908 (0.036% down)

Below, BB is the number of basic blocks, and PHI is the number of PHI
nodes before and after this pass on all of cc1-i files.

     before after
BB:  204727 202135 (1.282% down)
PHI:  26894  24715 (8.816% down)

Some focused tests seem to show more speed-up.  Each of the following
is timing in seconds of three runs of ./cc1 -O2 -fomit-frame-pointer.

             original patched
combine.i     14.799 14.760 (0.264% down)
fold-const.i  36.254 36.197 (0.157% down)
reload1.i     12.416 12.343 (0.591% down)
reload.i      12.049 11.980 (0.575% down)

Tested on i686-pc-linux-gnu.  OK to apply?

Kazu Hirata

2004-12-20  Kazu Hirata  <kazu@cs.umass.edu>

	PR tree-optimization/15349
	* timevar.def (TV_TREE_MERGE_PHI): New.
	* tree-cfg.c (tree_forwarder_block_with_phi_p,
	remove_forwarder_block_with_phi, merge_phi_nodes,
	gate_merge_phi, pass_merge_phi): New.
	* tree-optimize.c (init_tree_optimization_passes): Add
	pass_merge_phi.
	* tree-pass.h: Add an extern for pass_merge_phi;

Index: timevar.def
===================================================================
RCS file: /cvs/gcc/gcc/gcc/timevar.def,v
retrieving revision 1.41
diff -u -d -p -r1.41 timevar.def
--- timevar.def	29 Nov 2004 00:03:40 -0000	1.41
+++ timevar.def	20 Dec 2004 22:10:48 -0000
@@ -83,6 +83,7 @@ DEFTIMEVAR (TV_TREE_FORWPROP	     , "tre
 DEFTIMEVAR (TV_TREE_DCE		     , "tree conservative DCE")
 DEFTIMEVAR (TV_TREE_CD_DCE	     , "tree aggressive DCE")
 DEFTIMEVAR (TV_TREE_DSE		     , "tree DSE")
+DEFTIMEVAR (TV_TREE_MERGE_PHI	     , "PHI merge")
 DEFTIMEVAR (TV_TREE_LOOP	     , "tree loop optimization")
 DEFTIMEVAR (TV_TREE_LOOP_BOUNDS	     , "tree record loop bounds")
 DEFTIMEVAR (TV_LIM                   , "loop invariant motion")
Index: tree-cfg.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/tree-cfg.c,v
retrieving revision 2.136
diff -u -d -p -r2.136 tree-cfg.c
--- tree-cfg.c	13 Dec 2004 16:06:23 -0000	2.136
+++ tree-cfg.c	20 Dec 2004 22:10:49 -0000
@@ -4102,6 +4102,237 @@ cleanup_forwarder_blocks (void)
   return changed;
 }
 
+/* Return true if BB is a forwarder block with PHI nodes.  */
+
+static bool
+tree_forwarder_block_with_phi_p (basic_block bb)
+{
+  block_stmt_iterator bsi;
+
+  /* BB must have a single outgoing edge.  */
+  if (EDGE_COUNT (bb->succs) != 1
+      /* BB must have some PHI nodes.  */
+      || !phi_nodes (bb)
+      /* BB may not be a predecessor of EXIT_BLOCK_PTR.  */
+      || EDGE_SUCC (bb, 0)->dest == EXIT_BLOCK_PTR
+      /* BB may not have an abnormal outgoing edge.  */
+      || (EDGE_SUCC (bb, 0)->flags & EDGE_ABNORMAL))
+    return false;
+
+#if ENABLE_CHECKING
+  gcc_assert (bb != ENTRY_BLOCK_PTR);
+#endif
+
+  /* Now walk through the statements.  We can ignore labels, anything else
+     means this is not a forwarder block.  */
+  for (bsi = bsi_start (bb); !bsi_end_p (bsi); bsi_next (&bsi))
+    {
+      tree stmt = bsi_stmt (bsi);
+
+      switch (TREE_CODE (stmt))
+	{
+	case LABEL_EXPR:
+	  if (DECL_NONLOCAL (LABEL_EXPR_LABEL (stmt)))
+	    return false;
+	  break;
+
+	default:
+	  return false;
+	}
+    }
+
+  /* Successors of the entry block are not forwarders.  */
+  if (find_edge (ENTRY_BLOCK_PTR, bb))
+    return false;
+
+  return true;
+}
+
+/* Merge the PHI nodes at BB into those at BB_SUCC.  */
+
+static void
+remove_forwarder_block_with_phi (basic_block bb)
+{
+  edge succ = EDGE_SUCC (bb, 0);
+  basic_block dest = succ->dest;
+  basic_block dombb, domdest, dom;
+
+  /* Redirect each incoming edge to BB to DEST.  */
+  while (EDGE_COUNT (bb->preds) > 0)
+    {
+      edge e = EDGE_PRED (bb, 0), s;
+      tree phi;
+
+      s = find_edge (e->src, dest);
+      if (s)
+	{
+	  /* We already have an edge S from E->src to DEST.  If S and
+	     E->dest's sole successor edge have the same PHI arguments
+	     at DEST, redirect S to DEST.  */
+	  if (phi_alternatives_equal (dest, s, succ))
+	    {
+	      e = redirect_edge_and_branch (e, dest);
+	      PENDING_STMT (e) = NULL_TREE;
+	      continue;
+	    }
+
+	  /* PHI arguemnts are different.  Create a forwarder block by
+	     splitting E so that we can merge PHI arguments on E to
+	     DEST.  */
+	  e = EDGE_SUCC (split_edge (e), 0);
+	}
+
+      s = redirect_edge_and_branch (e, dest);
+
+      /* redirect_edge_and_branch must not create a new edge.  */
+      gcc_assert (s == e);
+
+      /* Add to the PHI nodes at DEST each PHI argument removed at the
+	 destination of E.  */
+      for (phi = phi_nodes (dest); phi; phi = PHI_CHAIN (phi))
+	{
+	  tree def = PHI_ARG_DEF (phi, succ->dest_idx);
+
+	  if (TREE_CODE (def) == SSA_NAME)
+	    {
+	      tree var;
+
+	      /* If DEF is one of the results of PHI nodes removed during
+		 redirection, replace it with the PHI argument that used
+		 to be on E.  */
+	      for (var = PENDING_STMT (e); var; var = TREE_CHAIN (var))
+		{
+		  tree old_arg = TREE_PURPOSE (var);
+		  tree new_arg = TREE_VALUE (var);
+
+		  if (def == old_arg)
+		    {
+		      def = new_arg;
+		      break;
+		    }
+		}
+	    }
+
+	  add_phi_arg (phi, def, s);
+	}
+
+      PENDING_STMT (e) = NULL;
+    }
+
+  /* Update the dominators.  */
+  dombb = get_immediate_dominator (CDI_DOMINATORS, bb);
+  domdest = get_immediate_dominator (CDI_DOMINATORS, dest);
+  if (domdest == bb)
+    {
+      /* Shortcut to avoid calling (relatively expensive)
+	 nearest_common_dominator unless necessary.  */
+      dom = dombb;
+    }
+  else
+    dom = nearest_common_dominator (CDI_DOMINATORS, domdest, dombb);
+
+  set_immediate_dominator (CDI_DOMINATORS, dest, dom);
+  
+  /* Remove BB since all of BB's incoming edges have been redirected
+     to DEST.  */
+  delete_basic_block (bb);
+}
+
+/* This pass performs merges PHI nodes if one feeds into another.  For
+   example, suppose we have the following:
+
+  goto <bb 9> (<L9>);
+
+<L8>:;
+  tem_17 = foo ();
+
+  # tem_6 = PHI <tem_17(8), tem_23(7)>;
+<L9>:;
+
+  # tem_3 = PHI <tem_6(9), tem_2(5)>;
+<L10>:;
+
+  Then we merge the first PHI node into the second one like so:
+
+  goto <bb 9> (<L10>);
+
+<L8>:;
+  tem_17 = foo ();
+
+  # tem_3 = PHI <tem_23(7), tem_2(5), tem_17(8)>;
+<L10>:;
+*/
+
+static void
+merge_phi_nodes (void)
+{
+  basic_block *worklist = xmalloc (sizeof (basic_block) * n_basic_blocks);
+  basic_block *current = worklist;
+  basic_block bb;
+
+  calculate_dominance_info (CDI_DOMINATORS);
+
+  /* Find all PHI nodes that we may be able to merge.  */
+  FOR_EACH_BB (bb)
+    {
+      basic_block dest;
+
+      /* Look for a forwarder block with PHI nodes.  */
+      if (!tree_forwarder_block_with_phi_p (bb))
+	continue;
+
+      dest = EDGE_SUCC (bb, 0)->dest;
+
+      /* We have to feed into another basic block with PHI
+	 nodes.  */
+      if (!phi_nodes (dest)
+	  /* We don't want to deal with a basic block with
+	     abnormal edges.  */
+	  || has_abnormal_incoming_edge_p (bb))
+	continue;
+
+      if (!dominated_by_p (CDI_DOMINATORS, dest, bb))
+	{
+	  /* If BB does not dominate DEST, then the PHI nodes at
+	     DEST must be the only users of the results of the PHI
+	     nodes at BB.  */
+	  *current++ = bb;
+	}
+    }
+
+  /* Now let's drain WORKLIST.  */
+  while (current != worklist)
+    {
+      bb = *--current;
+      remove_forwarder_block_with_phi (bb);
+    }
+
+  free (worklist);
+}
+
+static bool
+gate_merge_phi (void)
+{
+  return 1;
+}
+
+struct tree_opt_pass pass_merge_phi = {
+  "mergephi",			/* name */
+  gate_merge_phi,		/* gate */
+  merge_phi_nodes,		/* execute */
+  NULL,				/* sub */
+  NULL,				/* next */
+  0,				/* static_pass_number */
+  TV_TREE_MERGE_PHI,		/* tv_id */
+  PROP_cfg | PROP_ssa,		/* properties_required */
+  0,				/* properties_provided */
+  0,				/* properties_destroyed */
+  0,				/* todo_flags_start */
+  TODO_dump_func | TODO_ggc_collect	/* todo_flags_finish */
+  | TODO_verify_ssa,
+  0				/* letter */
+};
+
 /* Return a non-special label in the head of basic block BLOCK.
    Create one if it doesn't exist.  */
 
Index: tree-optimize.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/tree-optimize.c,v
retrieving revision 2.66
diff -u -d -p -r2.66 tree-optimize.c
--- tree-optimize.c	17 Dec 2004 22:20:26 -0000	2.66
+++ tree-optimize.c	20 Dec 2004 22:10:49 -0000
@@ -354,6 +354,7 @@ init_tree_optimization_passes (void)
   NEXT_PASS (pass_dominator);
   NEXT_PASS (pass_redundant_phi);
   NEXT_PASS (pass_dce);
+  NEXT_PASS (pass_merge_phi);
   NEXT_PASS (pass_forwprop);
   NEXT_PASS (pass_phiopt);
   NEXT_PASS (pass_may_alias);
Index: tree-pass.h
===================================================================
RCS file: /cvs/gcc/gcc/gcc/tree-pass.h,v
retrieving revision 2.22
diff -u -d -p -r2.22 tree-pass.h
--- tree-pass.h	28 Nov 2004 21:02:31 -0000	2.22
+++ tree-pass.h	20 Dec 2004 22:10:49 -0000
@@ -142,6 +142,7 @@ extern struct tree_opt_pass pass_del_ssa
 extern struct tree_opt_pass pass_dominator;
 extern struct tree_opt_pass pass_dce;
 extern struct tree_opt_pass pass_cd_dce;
+extern struct tree_opt_pass pass_merge_phi;
 extern struct tree_opt_pass pass_may_alias;
 extern struct tree_opt_pass pass_split_crit_edges;
 extern struct tree_opt_pass pass_pre;


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]