===================================================================
@@ -0,0 +1,15 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-tree-sink-stats" } */
+extern int b;
+int
+foo (int a, int c)
+{
+ int x = b;
+ if (c == 1)
+ return x;
+ else
+ return a;
+}
+/* We should sink load from b into the branch that returns x. */
+/* { dg-final { scan-tree-dump-times "Sunk statements: 1" 1 "sink" } } */
+/* { dg-final { cleanup-tree-dump "sink" } } */
===================================================================
@@ -0,0 +1,18 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-tree-sink-stats" } */
+extern int b;
+void bar();
+int
+foo (int a, int c)
+{
+ int x = b;
+ bar();
+ if (c == 1)
+ return x;
+ else
+ return a;
+}
+/* We should not sink load of b into the branch that returns x,
+ because it might be clobbered by the call to bar. */
+/* { dg-final { scan-tree-dump-times "Sunk statements:" 0 "sink" } } */
+/* { dg-final { cleanup-tree-dump "sink" } } */
===================================================================
@@ -77,6 +77,115 @@
} sink_stats;
+/* Stores reversely breadth-first searched block pointer, starting at
+ the potential destination block of sinking from load. */
+static basic_block *pred_blocks;
+/* Bitmap records the visited basic blocks in breadth-first search. */
+static bitmap pred_visited;
+
+static void
+init_sink_load (void)
+{
+ pred_blocks = XCNEWVEC (basic_block, last_basic_block);
+ pred_visited = BITMAP_ALLOC (NULL);
+}
+
+static void
+free_sink_load (void)
+{
+ free (pred_blocks);
+ BITMAP_FREE (pred_visited);
+}
+
+/* Given a gimple STMT in basic block FROM, which is a load operation,
+ check whether it should be sinked to TOGSI in basic block TO.
+ Return TRUE if it should be sinked, otherwise return FALSE.
+
+ Loads should not be sunk across stmts that might clobber the reference,
+ for simplicity, we just stop checking at any store. */
+
+static bool
+sink_load_p (gimple stmt, basic_block from, basic_block to,
+ gimple_stmt_iterator *togsi)
+{
+ int i, vc;
+ basic_block bb;
+ gimple_stmt_iterator gsi = gsi_for_stmt (stmt);
+
+ /* Stmt should be a load. */
+ gcc_assert (gimple_vuse (stmt) && !gimple_vdef (stmt));
+
+ /* Sink has conflict effect with if-conversion pass, which speculates
+ loads. So do not sink possibly trapping stmt, which can not be
+ speculated by if-conversion pass afterward. */
+ if (gimple_assign_rhs_could_trap_p (stmt))
+ return false;
+
+ /* Sink to not post-dominated basic block, unless it's in outer loop. */
+ if (dominated_by_p (CDI_POST_DOMINATORS, from, to)
+ && (from->loop_depth <= to->loop_depth))
+ return false;
+
+ /* If the latch block is empty, don't make it non-empty by sinking
+ load into it. */
+ if (to == from->loop_father->latch && empty_block_p (to))
+ return false;
+
+ /* Don't sink stmt if reference might be clobbered by successor stmts. */
+ for (gsi_next(&gsi); !gsi_end_p (gsi); gsi_next (&gsi))
+ if (gimple_vdef (gsi_stmt (gsi)))
+ return false;
+
+ /* Don't sink stmt if reference might be clobbered by predecessors of
+ TOGSI. */
+ gsi = *togsi;
+ if (!gsi_end_p (gsi))
+ gsi_prev (&gsi);
+
+ for (; !gsi_end_p (gsi); gsi_prev (&gsi))
+ if (gimple_vdef (gsi_stmt (gsi)))
+ return false;
+
+ /* Don't sink stmt if reference might be clobbered by basic blocks in any
+ path along FROM to TO. we doing this by reversely breadth-first search
+ blocks starting at TO and stop checking at any store. */
+
+ i = 0, vc = 1;
+ bb = to;
+ bitmap_clear (pred_visited);
+
+ do
+ {
+ edge e;
+ edge_iterator ei;
+
+ if (bitmap_set_bit (pred_visited, bb->index))
+ pred_blocks[i++] = bb;
+
+ FOR_EACH_EDGE (e, ei, bb->preds)
+ {
+ basic_block src = e->src;
+ if (src != from && src != to)
+ {
+ gcc_assert (dominated_by_p (CDI_DOMINATORS, src, from));
+ if (bitmap_set_bit (pred_visited, src->index))
+ pred_blocks[i++] = src;
+ }
+ }
+
+ bb = pred_blocks[vc++];
+
+ /* Check whether there is store in basic block bb. */
+ if (i >= vc)
+ for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi); gsi_next (&gsi))
+ if (gimple_vdef (gsi_stmt (gsi)))
+ return false;
+
+ } while (i >= vc);
+
+ return true;
+}
+
/* Given a PHI, and one of its arguments (DEF), find the edge for
that argument and return it. If the argument occurs twice in the PHI node,
we return NULL. */
@@ -363,7 +472,8 @@
be seen by an external routine that needs it depending on where it gets
moved to.
- We don't want to sink loads from memory.
+ We only want to sink loads from memory to not post-dominated basic block,
+ or basci block in outer loop.
We can't sink statements that end basic blocks without splitting the
incoming edge for the sink location to place it there.
@@ -382,7 +492,6 @@
if (stmt_ends_bb_p (stmt)
|| gimple_has_side_effects (stmt)
|| gimple_has_volatile_ops (stmt)
- || (gimple_vuse (stmt) && !gimple_vdef (stmt))
|| (cfun->has_local_explicit_reg_vars
&& TYPE_MODE (TREE_TYPE (gimple_assign_lhs (stmt))) == BLKmode))
return false;
@@ -448,10 +557,15 @@
commondom = select_best_block (frombb, commondom, stmt);
if (commondom == frombb)
- return false;
+ return false;
*togsi = gsi_after_labels (commondom);
+ /* If stmt is a load operation, check whether it should be sinked. */
+ if ((gimple_vuse (stmt) && !gimple_vdef (stmt))
+ && !sink_load_p (stmt, frombb, commondom, togsi))
+ return false;
+
return true;
}
else
@@ -474,6 +588,11 @@
*togsi = gsi_for_stmt (use);
+ /* If stmt is a load operation, check whether it should be sinked. */
+ if ((gimple_vuse (stmt) && !gimple_vdef (stmt))
+ && !sink_load_p (stmt, frombb, sinkbb, togsi))
+ return false;
+
return true;
}
}
@@ -483,7 +602,7 @@
/* This can happen if there are multiple uses in a PHI. */
if (!sinkbb)
return false;
-
+
sinkbb = select_best_block (frombb, sinkbb, stmt);
if (!sinkbb || sinkbb == frombb)
return false;
@@ -496,6 +615,11 @@
*togsi = gsi_after_labels (sinkbb);
+ /* If stmt is a load operation, check whether it should be sinked. */
+ if ((gimple_vuse (stmt) && !gimple_vdef (stmt))
+ && !sink_load_p (stmt, frombb, sinkbb, togsi))
+ return false;
+
return true;
}
@@ -633,7 +757,9 @@
memset (&sink_stats, 0, sizeof (sink_stats));
calculate_dominance_info (CDI_DOMINATORS);
calculate_dominance_info (CDI_POST_DOMINATORS);
+ init_sink_load ();
sink_code_in_bb (EXIT_BLOCK_PTR);
+ free_sink_load ();
statistics_counter_event (cfun, "Sunk statements", sink_stats.sunk);
free_dominance_info (CDI_POST_DOMINATORS);
remove_fake_exit_edges ();