public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
* [PATCH] conditional load hoisting
@ 2007-07-31 12:49 Tehila Meyzels
  0 siblings, 0 replies; only message in thread
From: Tehila Meyzels @ 2007-07-31 12:49 UTC (permalink / raw)
  To: gcc-patches

[-- Attachment #1: Type: text/plain, Size: 1309 bytes --]


Hi,

This patch is aimed to perform conditional load hoisting (in tree level),
to increase opportunities for if-conversion (in RTL, ifcvt.c),
It was written for a GCC 4.1.1 based compiler, and I'm posting it, as a
reference, for a discussion I want to raise
with respect tree-ssa-if-conversion improvements (coming up) .
 I'm not submitting this for inclusion in GCC as is, since I think it
should be rewritten to be part of tree-if-conv (details will followed).

This patch performs hoisting of a load insn from the THEN or the ELSE block
(one of them, where it appears) to the IF block above.
It has several restrictions when to hoist, including a checking that the
BBs are if-convertable.

For example:

Replace:

  bb0:
       if (c) goto bb1; else goto bb2;
     bb1:
       a = arr[index];
     bb2:

 with

     bb0:
       a' = arr[index];
       if (c) goto bb1; else goto bb2;
     bb1:
       tmp = a';
     bb2:

In order to activate this transformation, 2 flags should be enabled:
1. -ftree-clhoist (the pass flag)
2. -fsched-spec-load-dangerous - since we create speculative loads, which
is dangerous.

Tehila.

(See attached file: cond_load_hoist.diff)


-----------------------------------------------------------------------------

Tehila Meyzels
Tel: 972-4-829-6190
Email: tehila@il.ibm.com

[-- Attachment #2: cond_load_hoist.diff --]
[-- Type: application/octet-stream, Size: 11931 bytes --]

Index: gcc/gcc/doc/passes.texi
===================================================================
--- gcc/gcc/doc/passes.texi	(revision 833)
+++ gcc/gcc/doc/passes.texi	(working copy)
@@ -307,6 +307,13 @@
 It is located in @file{tree-ssa-phiopt.c} and is described by
 @code{pass_phiopt}.
 
+@item Conditional load hoist optimizations
+This pass recognizes a conditional load, where there is a potential for 
+if-conversion transformation afterwards and hoist it to the condition
+basic block.
+It is located in @file{tree-ssa-phiopt.c} and is described by
+@code{pass_clhoist}.
+
 @item May-alias optimization
 
 This pass performs a flow sensitive SSA-based points-to analysis.
Index: gcc/gcc/doc/invoke.texi
===================================================================
--- gcc/gcc/doc/invoke.texi	(revision 833)
+++ gcc/gcc/doc/invoke.texi	(working copy)
@@ -336,6 +336,7 @@
 -ftree-pre  -ftree-ccp  -ftree-dce -ftree-loop-optimize @gol
 -ftree-loop-linear -ftree-loop-im -ftree-loop-ivcanon -fivopts @gol
 -ftree-dominator-opts -ftree-dse -ftree-copyrename -ftree-sink @gol
+-ftree-clhoist @gol
 -ftree-ch -ftree-sra -ftree-ter -ftree-lrs -ftree-fre -ftree-vectorize @gol
 -ftree-vect-loop-version -ftree-salias -fweb @gol
 -ftree-copy-prop -ftree-store-ccp -ftree-store-copy-prop -fwhole-program @gol
@@ -4990,6 +4991,9 @@
 Perform forward store motion  on trees.  This flag is
 enabled by default at @option{-O} and higher.
 
+@item -ftree-clhoist
+Perform conditional load hoisting on trees.
+
 @item -ftree-ccp
 Perform sparse conditional constant propagation (CCP) on trees.  This
 pass only operates on local scalar variables and is enabled by default
Index: gcc/gcc/tree-pass.h
===================================================================
--- gcc/gcc/tree-pass.h	(revision 833)
+++ gcc/gcc/tree-pass.h	(working copy)
@@ -262,6 +262,7 @@
 extern struct tree_opt_pass pass_cse_reciprocals;
 extern struct tree_opt_pass pass_warn_function_return;
 extern struct tree_opt_pass pass_warn_function_noreturn;
+extern struct tree_opt_pass pass_clhoist;
 extern struct tree_opt_pass pass_phiopt;
 extern struct tree_opt_pass pass_forwprop;
 extern struct tree_opt_pass pass_redundant_phi;
Index: gcc/gcc/tree-ssa-phiopt.c
===================================================================
--- gcc/gcc/tree-ssa-phiopt.c	(revision 834)
+++ gcc/gcc/tree-ssa-phiopt.c	(working copy)
@@ -35,7 +35,10 @@
 #include "tree-dump.h"
 #include "langhooks.h"
 
+static bool array_already_defined_p (tree, basic_block);
+static bool bb_ok_for_ifcvt_p (basic_block);
 static void tree_ssa_phiopt (void);
+static void tree_ssa_phiopt_worker (bool);
 static bool conditional_replacement (basic_block, basic_block,
 				     edge, edge, tree, tree, tree);
 static bool value_replacement (basic_block, basic_block,
@@ -44,9 +47,95 @@
 				edge, edge, tree, tree, tree);
 static bool abs_replacement (basic_block, basic_block,
 			     edge, edge, tree, tree, tree);
+static void cond_load_replacement (basic_block, basic_block);
 static void replace_phi_edge_with_variable (basic_block, edge, tree, tree);
 static basic_block *blocks_in_phiopt_order (void);
 
+
+/* Returns TRUE if the array ARR_REF and its index already defined in previous 
+   basic block.  Otherwise, returns FALSE.  */
+
+static bool 
+array_already_defined_p (tree arr_ref, basic_block bb)
+{
+  tree index, index_def_stmt;
+  basic_block index_bb;
+
+  gcc_assert (arr_ref != NULL_TREE);
+
+  index = TREE_OPERAND (arr_ref, 1);
+
+  if (index == NULL) 
+    return true;
+
+  index_def_stmt = SSA_NAME_DEF_STMT (index);
+  index_bb = bb_for_stmt (index_def_stmt);
+  if (bb == index_bb) 
+    return false;
+  return true;
+}
+
+
+/* Goes over the statements in BB. Returns FALSE if there are stores, function
+   calls, loads from non-array memory or load from array that can't be removed. 
+   True, otherwise.  */
+
+static bool 
+bb_ok_for_ifcvt_p (basic_block bb)
+{
+  block_stmt_iterator bsi;
+
+  /* BB must have no stores or function calls statements in order.  We count on 
+     the fact that store-sink pass is prior to this pass, so all the stores that
+     could be removed from BB are already sunk.  In addition, if there is a load 
+     (from an array, that we may want to hoist, we check that it is safe to move
+     it.  */
+
+  for (bsi = bsi_start (bb); !bsi_end_p (bsi); bsi_next (&bsi))
+    {
+      tree stmt;
+
+      stmt = bsi_stmt (bsi);
+
+      if (get_call_expr_in (stmt))
+	return false;
+
+      if (TREE_CODE (stmt) != MODIFY_EXPR)
+	continue;
+
+      if (!ZERO_SSA_OPERANDS (stmt, SSA_OP_VIRTUAL_DEFS)) /* store  */
+	return false;
+
+      if (!ZERO_SSA_OPERANDS (stmt, SSA_OP_VIRTUAL_USES)) /* load  */
+        {
+	  tree lhs, rhs;
+	  stmt_ann_t ann;
+
+	  ann = stmt_ann (stmt);
+	  lhs = TREE_OPERAND (stmt, 0);
+	  rhs = TREE_OPERAND (stmt, 1);	
+
+	  /* We don't support load-hoisting of loads that aren't from an 
+	     array.  */
+	  if (TREE_CODE (rhs) != ARRAY_REF) 
+	    return false;
+
+          /* Verify that this load-from-array can be moved.  */
+	  else
+	    {
+	      if (TREE_SIDE_EFFECTS (lhs)
+	          || TREE_CODE (lhs) == EXC_PTR_EXPR
+	          || TREE_CODE (lhs) == FILTER_EXPR 
+	          || ann->has_volatile_ops
+		  || !array_already_defined_p (rhs, bb))
+                return false;
+            }
+        }
+    }
+    return true;
+}
+
+
 /* This pass tries to replace an if-then-else block with an
    assignment.  We have four kinds of transformations.  Some of these
    transformations are also performed by the ifcvt RTL optimizer.
@@ -136,10 +225,51 @@
 static void
 tree_ssa_phiopt (void)
 {
+  tree_ssa_phiopt_worker (false);
+}
+
+/* This pass tries to transform conditional loads into unconditional
+   ones, by hoisting the load to the if bb.  In particular, it replaces this:
+
+     bb0:
+       if (cond) goto bb1; else goto bb2;
+     bb1:
+       LHS = arr[index];
+     bb2:
+       
+   with
+
+     bb0:
+       LHS' = arr[index];
+       if (cond) goto bb1; else goto bb2;
+     bb1:
+       tmp = LHS';
+     bb2:
+
+   This transformation can only be done under several constraints,
+   documented below.  */
+
+static void
+tree_ssa_cl_hoist (void)
+{
+  tree_ssa_phiopt_worker (true);
+}
+
+
+/* The core routine of conditional load replacement and normal
+   phi optimizations.  Both share much of the infrastructure in how
+   to match applicable basic block patterns.  DO_LOAD_HOIST is true
+   when we want to do conditional load replacement, false otherwise.  */
+static void
+tree_ssa_phiopt_worker (bool do_load_hoist)
+{
   basic_block bb;
   basic_block *bb_order;
   unsigned n, i;
+  bool if_then_else;
 
+  if_then_else = false;
+
   /* Search every basic block for COND_EXPR we may be able to optimize.
 
      We walk the blocks in order that guarantees that a block with
@@ -194,6 +324,9 @@
 	  e1 = e2;
 	  e2 = e_tmp;
 	}
+      else if ((EDGE_SUCC (bb1, 0)->dest == EDGE_SUCC (bb2, 0)->dest)
+	       && do_load_hoist)
+	if_then_else = true;
       else
         continue;
 
@@ -210,6 +343,26 @@
           || single_pred (bb1) != bb)
 	continue;
 
+      if (if_then_else)  /* Check also bb2.  */
+	{
+	  if (!single_succ_p (bb2))
+	    continue;
+	  if (!single_pred_p (bb2)
+              || single_pred (bb2) != bb)
+	    continue;
+	}
+
+      if ((do_load_hoist) && (flag_schedule_speculative_load_dangerous))
+	{
+	  if (!bb_ok_for_ifcvt_p (bb1) 
+	      || (if_then_else && !bb_ok_for_ifcvt_p (bb2)))
+	    continue;
+	  cond_load_replacement (bb, bb1);
+	  if (if_then_else)
+	    cond_load_replacement (bb, bb2);
+	  continue;
+	}
+
       phi = phi_nodes (bb2);
 
       /* Check to make sure that there is only one PHI node.
@@ -987,6 +1140,98 @@
 }
 
 
+/* Do the main work of conditional load hoisting. We already know that the 
+   recognized pattern looks like one of the options:
+
+   COND_BB:
+   if (cond) goto MIDDLE_BB1; else goto JOIN_BB (edge E1)
+   MIDDLE_BB1:
+   something
+   fallthrough (edge E0)
+   JOIN_BB:
+   some more
+
+   or
+
+    COND_BB:
+   if (cond) goto MIDDLE_BB1(edge E0); else goto MIDDLE_BB2 (edge E1)
+   MIDDLE_BB1:
+   something (including load)
+   goto JOIN_BB
+   MIDDLE_BB2:
+   something
+   fallthrough 
+   JOIN_BB:
+   some more
+
+   or
+
+    COND_BB:
+   if (cond) goto MIDDLE_BB2 (edge E1); else goto MIDDLE_BB1 (edge E0)
+   MIDDLE_BB1:
+   something (including load)
+   goto JOIN_BB
+   MIDDLE_BB2:
+   something
+   fallthrough 
+   JOIN_BB:
+   some more
+
+   We check that MIDDLE_BB1 contains a load, and the load has a "simple" LHS.
+   Since there are no stores or function calls in the basic_block, there is a 
+   potential for if-conversion transformation.  Therefre, we hoist the load to 
+   COND_BB.  */
+
+static void
+cond_load_replacement (basic_block cond_bb, basic_block middle_bb1)
+{
+  bool load_hoist = false;
+  block_stmt_iterator bsi;
+
+  for (bsi = bsi_start (middle_bb1); !bsi_end_p (bsi); bsi_next (&bsi))
+    {
+      tree stmt, rhs;
+
+      stmt = bsi_stmt (bsi);
+
+      if (TREE_CODE (stmt) != MODIFY_EXPR)
+	continue;
+
+      rhs = TREE_OPERAND (stmt, 1);
+
+      if (TREE_CODE (rhs) == ARRAY_REF)	/* load  */
+	{
+	  tree tmp_var, type, new_load_stmt, new_rhs;
+	  block_stmt_iterator bsi_cond;
+
+	  /* Do load-hoist of bsi.  */
+	  /* 1) Create new temporary variable TMP_VAR to load into  */
+	  type = TREE_TYPE (rhs);
+	  tmp_var = create_tmp_var (type, "_clh_");
+	  add_referenced_tmp_var (tmp_var);
+	  mark_sym_for_renaming (tmp_var);
+
+          /* 2) Replace the MEM operand with tmp_var in the original statement.  */
+	  TREE_OPERAND (stmt, 1) = tmp_var;
+	  mark_sym_for_renaming (TREE_OPERAND (stmt, 1));
+	  update_stmt (stmt);
+
+	  /* 3) Build the new load (hoisted) statement and insert before 
+	  last statement of the COND_BB (i.e., if-statement).  */
+      	  new_rhs = unshare_expr (rhs);
+          new_load_stmt = build2 (MODIFY_EXPR, type, tmp_var, new_rhs);
+      	  bsi_cond = bsi_last (cond_bb);
+      	  bsi_insert_before (&bsi_cond, new_load_stmt, BSI_NEW_STMT);
+
+          load_hoist = true;
+	  continue;
+        }
+    }
+
+  return;
+}
+
+
 /* Always do these optimizations if we have SSA
    trees to work on.  */
 static bool
@@ -1016,3 +1261,30 @@
     | TODO_verify_stmts,		/* todo_flags_finish */
   0					/* letter */
 };
+
+static bool
+gate_clhoist (void)
+{
+  return flag_tree_clhoist;
+}
+
+struct tree_opt_pass pass_clhoist = {
+  "clhoist",			/* name */
+  gate_clhoist,			/* gate */
+  tree_ssa_cl_hoist,		/* execute */
+  NULL,				/* sub */
+  NULL,				/* next */
+  0,				/* static_pass_number */
+  TV_TREE_PHIOPT,		/* tv_id */
+  PROP_cfg | PROP_ssa | PROP_alias,	/* properties_required */
+  0,					/* properties_provided */
+  0,					/* properties_destroyed */
+  0,					/* todo_flags_start */
+  TODO_update_ssa
+    | TODO_dump_func
+    | TODO_ggc_collect
+    | TODO_verify_ssa
+    | TODO_verify_flow
+    | TODO_verify_stmts,		/* todo_flags_finish */
+  0					/* letter */
+};
Index: gcc/gcc/common.opt
===================================================================
--- gcc/gcc/common.opt	(revision 833)
+++ gcc/gcc/common.opt	(working copy)
@@ -929,6 +929,10 @@
 Common Report Var(flag_tree_store_copy_prop)
 Enable copy propagation for stores and loads
 
+ftree-clhoist
+Common Report Var(flag_tree_clhoist)
+Hoist conditional loads  
+
 ftree-dce
 Common Report Var(flag_tree_dce)
 Enable SSA dead code elimination optimization on trees
Index: gcc/gcc/passes.c
===================================================================
--- gcc/gcc/passes.c	(revision 833)
+++ gcc/gcc/passes.c	(working copy)
@@ -562,6 +562,7 @@
   NEXT_PASS (pass_late_warn_uninitialized);
   NEXT_PASS (pass_dse);
   NEXT_PASS (pass_forwprop);
+  NEXT_PASS (pass_clhoist);
   NEXT_PASS (pass_phiopt);
   NEXT_PASS (pass_tail_calls);
   NEXT_PASS (pass_rename_ssa_copies);

^ permalink raw reply	[flat|nested] only message in thread

only message in thread, other threads:[~2007-07-31 12:37 UTC | newest]

Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2007-07-31 12:49 [PATCH] conditional load hoisting Tehila Meyzels

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).