diff gcc/tree-ssa-sink.c @ 16:04ced10e8804

gcc 7
author kono
date Fri, 27 Oct 2017 22:46:09 +0900
parents f6334be47118
children 84e7813d76e9
line wrap: on
line diff
--- a/gcc/tree-ssa-sink.c	Sun Aug 21 07:07:55 2011 +0900
+++ b/gcc/tree-ssa-sink.c	Fri Oct 27 22:46:09 2017 +0900
@@ -1,6 +1,5 @@
 /* Code sinking for trees
-   Copyright (C) 2001, 2002, 2003, 2004, 2007, 2008, 2009, 2010
-   Free Software Foundation, Inc.
+   Copyright (C) 2001-2017 Free Software Foundation, Inc.
    Contributed by Daniel Berlin <dan@dberlin.org>
 
 This file is part of GCC.
@@ -22,24 +21,20 @@
 #include "config.h"
 #include "system.h"
 #include "coretypes.h"
-#include "tm.h"
+#include "backend.h"
 #include "tree.h"
-#include "basic-block.h"
-#include "gimple-pretty-print.h"
-#include "tree-inline.h"
-#include "tree-flow.h"
 #include "gimple.h"
-#include "tree-dump.h"
-#include "timevar.h"
-#include "fibheap.h"
-#include "hashtab.h"
-#include "tree-iterator.h"
-#include "alloc-pool.h"
+#include "cfghooks.h"
 #include "tree-pass.h"
-#include "flags.h"
-#include "bitmap.h"
-#include "langhooks.h"
+#include "ssa.h"
+#include "gimple-pretty-print.h"
+#include "fold-const.h"
+#include "stor-layout.h"
+#include "cfganal.h"
+#include "gimple-iterator.h"
+#include "tree-cfg.h"
 #include "cfgloop.h"
+#include "params.h"
 
 /* TODO:
    1. Sinking store only using scalar promotion (IE without moving the RHS):
@@ -81,7 +76,7 @@
    we return NULL.  */
 
 static basic_block
-find_bb_for_arg (gimple phi, tree def)
+find_bb_for_arg (gphi *phi, tree def)
 {
   size_t i;
   bool foundone = false;
@@ -106,169 +101,149 @@
    used in, so that you only have one place you can sink it to.  */
 
 static bool
-all_immediate_uses_same_place (gimple stmt)
+all_immediate_uses_same_place (def_operand_p def_p)
 {
-  gimple firstuse = NULL;
-  ssa_op_iter op_iter;
+  tree var = DEF_FROM_PTR (def_p);
   imm_use_iterator imm_iter;
   use_operand_p use_p;
-  tree var;
 
-  FOR_EACH_SSA_TREE_OPERAND (var, stmt, op_iter, SSA_OP_ALL_DEFS)
+  gimple *firstuse = NULL;
+  FOR_EACH_IMM_USE_FAST (use_p, imm_iter, var)
     {
-      FOR_EACH_IMM_USE_FAST (use_p, imm_iter, var)
-        {
-	  if (is_gimple_debug (USE_STMT (use_p)))
-	    continue;
-	  if (firstuse == NULL)
-	    firstuse = USE_STMT (use_p);
-	  else
-	    if (firstuse != USE_STMT (use_p))
-	      return false;
-	}
+      if (is_gimple_debug (USE_STMT (use_p)))
+	continue;
+      if (firstuse == NULL)
+	firstuse = USE_STMT (use_p);
+      else
+	if (firstuse != USE_STMT (use_p))
+	  return false;
     }
 
   return true;
 }
 
-/* Some global stores don't necessarily have VDEF's of global variables,
-   but we still must avoid moving them around.  */
-
-bool
-is_hidden_global_store (gimple stmt)
-{
-  /* Check virtual definitions.  If we get here, the only virtual
-     definitions we should see are those generated by assignment or call
-     statements.  */
-  if (gimple_vdef (stmt))
-    {
-      tree lhs;
-
-      gcc_assert (is_gimple_assign (stmt) || is_gimple_call (stmt));
-
-      /* Note that we must not check the individual virtual operands
-	 here.  In particular, if this is an aliased store, we could
-	 end up with something like the following (SSA notation
-	 redacted for brevity):
-
-	 	foo (int *p, int i)
-		{
-		  int x;
-		  p_1 = (i_2 > 3) ? &x : p;
-
-		  # x_4 = VDEF <x_3>
-		  *p_1 = 5;
-
-		  return 2;
-		}
-
-	 Notice that the store to '*p_1' should be preserved, if we
-	 were to check the virtual definitions in that store, we would
-	 not mark it needed.  This is because 'x' is not a global
-	 variable.
-
-	 Therefore, we check the base address of the LHS.  If the
-	 address is a pointer, we check if its name tag or symbol tag is
-	 a global variable.  Otherwise, we check if the base variable
-	 is a global.  */
-      lhs = gimple_get_lhs (stmt);
-
-      if (REFERENCE_CLASS_P (lhs))
-	lhs = get_base_address (lhs);
-
-      if (lhs == NULL_TREE)
-	{
-	  /* If LHS is NULL, it means that we couldn't get the base
-	     address of the reference.  In which case, we should not
-	     move this store.  */
-	  return true;
-	}
-      else if (DECL_P (lhs))
-	{
-	  /* If the store is to a global symbol, we need to keep it.  */
-	  if (is_global_var (lhs))
-	    return true;
-
-	}
-      else if (INDIRECT_REF_P (lhs)
-	       || TREE_CODE (lhs) == MEM_REF
-	       || TREE_CODE (lhs) == TARGET_MEM_REF)
-	return ptr_deref_may_alias_global_p (TREE_OPERAND (lhs, 0));
-      else if (CONSTANT_CLASS_P (lhs))
-	return true;
-      else
-	gcc_unreachable ();
-    }
-
-  return false;
-}
-
 /* Find the nearest common dominator of all of the immediate uses in IMM.  */
 
 static basic_block
-nearest_common_dominator_of_uses (gimple stmt, bool *debug_stmts)
+nearest_common_dominator_of_uses (def_operand_p def_p, bool *debug_stmts)
 {
-  bitmap blocks = BITMAP_ALLOC (NULL);
+  tree var = DEF_FROM_PTR (def_p);
+  auto_bitmap blocks;
   basic_block commondom;
   unsigned int j;
   bitmap_iterator bi;
-  ssa_op_iter op_iter;
   imm_use_iterator imm_iter;
   use_operand_p use_p;
-  tree var;
 
-  bitmap_clear (blocks);
-  FOR_EACH_SSA_TREE_OPERAND (var, stmt, op_iter, SSA_OP_ALL_DEFS)
+  FOR_EACH_IMM_USE_FAST (use_p, imm_iter, var)
     {
-      FOR_EACH_IMM_USE_FAST (use_p, imm_iter, var)
-        {
-	  gimple usestmt = USE_STMT (use_p);
-	  basic_block useblock;
+      gimple *usestmt = USE_STMT (use_p);
+      basic_block useblock;
 
-	  if (gimple_code (usestmt) == GIMPLE_PHI)
-	    {
-	      int idx = PHI_ARG_INDEX_FROM_USE (use_p);
+      if (gphi *phi = dyn_cast <gphi *> (usestmt))
+	{
+	  int idx = PHI_ARG_INDEX_FROM_USE (use_p);
 
-	      useblock = gimple_phi_arg_edge (usestmt, idx)->src;
-	    }
-	  else if (is_gimple_debug (usestmt))
-	    {
-	      *debug_stmts = true;
-	      continue;
-	    }
-	  else
-	    {
-	      useblock = gimple_bb (usestmt);
-	    }
+	  useblock = gimple_phi_arg_edge (phi, idx)->src;
+	}
+      else if (is_gimple_debug (usestmt))
+	{
+	  *debug_stmts = true;
+	  continue;
+	}
+      else
+	{
+	  useblock = gimple_bb (usestmt);
+	}
 
-	  /* Short circuit. Nothing dominates the entry block.  */
-	  if (useblock == ENTRY_BLOCK_PTR)
-	    {
-	      BITMAP_FREE (blocks);
-	      return NULL;
-	    }
-	  bitmap_set_bit (blocks, useblock->index);
-	}
+      /* Short circuit. Nothing dominates the entry block.  */
+      if (useblock == ENTRY_BLOCK_PTR_FOR_FN (cfun))
+	return NULL;
+
+      bitmap_set_bit (blocks, useblock->index);
     }
-  commondom = BASIC_BLOCK (bitmap_first_set_bit (blocks));
+  commondom = BASIC_BLOCK_FOR_FN (cfun, bitmap_first_set_bit (blocks));
   EXECUTE_IF_SET_IN_BITMAP (blocks, 0, j, bi)
     commondom = nearest_common_dominator (CDI_DOMINATORS, commondom,
-					  BASIC_BLOCK (j));
-  BITMAP_FREE (blocks);
+					  BASIC_BLOCK_FOR_FN (cfun, j));
   return commondom;
 }
 
+/* Given EARLY_BB and LATE_BB, two blocks in a path through the dominator
+   tree, return the best basic block between them (inclusive) to place
+   statements.
+
+   We want the most control dependent block in the shallowest loop nest.
+
+   If the resulting block is in a shallower loop nest, then use it.  Else
+   only use the resulting block if it has significantly lower execution
+   frequency than EARLY_BB to avoid gratutious statement movement.  We
+   consider statements with VOPS more desirable to move.
+
+   This pass would obviously benefit from PDO as it utilizes block
+   frequencies.  It would also benefit from recomputing frequencies
+   if profile data is not available since frequencies often get out
+   of sync with reality.  */
+
+static basic_block
+select_best_block (basic_block early_bb,
+		   basic_block late_bb,
+		   gimple *stmt)
+{
+  basic_block best_bb = late_bb;
+  basic_block temp_bb = late_bb;
+  int threshold;
+
+  while (temp_bb != early_bb)
+    {
+      /* If we've moved into a lower loop nest, then that becomes
+	 our best block.  */
+      if (bb_loop_depth (temp_bb) < bb_loop_depth (best_bb))
+	best_bb = temp_bb;
+
+      /* Walk up the dominator tree, hopefully we'll find a shallower
+ 	 loop nest.  */
+      temp_bb = get_immediate_dominator (CDI_DOMINATORS, temp_bb);
+    }
+
+  /* If we found a shallower loop nest, then we always consider that
+     a win.  This will always give us the most control dependent block
+     within that loop nest.  */
+  if (bb_loop_depth (best_bb) < bb_loop_depth (early_bb))
+    return best_bb;
+
+  /* Get the sinking threshold.  If the statement to be moved has memory
+     operands, then increase the threshold by 7% as those are even more
+     profitable to avoid, clamping at 100%.  */
+  threshold = PARAM_VALUE (PARAM_SINK_FREQUENCY_THRESHOLD);
+  if (gimple_vuse (stmt) || gimple_vdef (stmt))
+    {
+      threshold += 7;
+      if (threshold > 100)
+	threshold = 100;
+    }
+
+  /* If BEST_BB is at the same nesting level, then require it to have
+     significantly lower execution frequency to avoid gratutious movement.  */
+  if (bb_loop_depth (best_bb) == bb_loop_depth (early_bb)
+      && best_bb->frequency < (early_bb->frequency * threshold / 100.0))
+    return best_bb;
+
+  /* No better block found, so return EARLY_BB, which happens to be the
+     statement's original block.  */
+  return early_bb;
+}
+
 /* Given a statement (STMT) and the basic block it is currently in (FROMBB),
    determine the location to sink the statement to, if any.
    Returns true if there is such location; in that case, TOGSI points to the
    statement before that STMT should be moved.  */
 
 static bool
-statement_sink_location (gimple stmt, basic_block frombb,
-			 gimple_stmt_iterator *togsi)
+statement_sink_location (gimple *stmt, basic_block frombb,
+			 gimple_stmt_iterator *togsi, bool *zero_uses_p)
 {
-  gimple use;
-  tree def;
+  gimple *use;
   use_operand_p one_use = NULL_USE_OPERAND_P;
   basic_block sinkbb;
   use_operand_p use_p;
@@ -276,24 +251,19 @@
   ssa_op_iter iter;
   imm_use_iterator imm_iter;
 
-  FOR_EACH_SSA_TREE_OPERAND (def, stmt, iter, SSA_OP_ALL_DEFS)
-    {
-      FOR_EACH_IMM_USE_FAST (one_use, imm_iter, def)
-	{
-	  if (is_gimple_debug (USE_STMT (one_use)))
-	    continue;
+  *zero_uses_p = false;
 
-	  break;
-	}
-      if (one_use != NULL_USE_OPERAND_P)
-        break;
-    }
-
-  /* Return if there are no immediate uses of this stmt.  */
-  if (one_use == NULL_USE_OPERAND_P)
+  /* We only can sink assignments and non-looping const/pure calls.  */
+  int cf;
+  if (!is_gimple_assign (stmt)
+      && (!is_gimple_call (stmt)
+	  || !((cf = gimple_call_flags (stmt)) & (ECF_CONST|ECF_PURE))
+	  || (cf & ECF_LOOPING_CONST_OR_PURE)))
     return false;
 
-  if (gimple_code (stmt) != GIMPLE_ASSIGN)
+  /* We only can sink stmts with a single definition.  */
+  def_p = single_ssa_def_operand (stmt, SSA_OP_ALL_DEFS);
+  if (def_p == NULL_DEF_OPERAND_P)
     return false;
 
   /* There are a few classes of things we can't or don't move, some because we
@@ -305,8 +275,6 @@
      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 can't sink statements that end basic blocks without splitting the
      incoming edge for the sink location to place it there.
 
@@ -323,21 +291,20 @@
   */
   if (stmt_ends_bb_p (stmt)
       || gimple_has_side_effects (stmt)
-      || is_hidden_global_store (stmt)
-      || gimple_has_volatile_ops (stmt)
-      || gimple_vuse (stmt)
       || (cfun->has_local_explicit_reg_vars
-	  && TYPE_MODE (TREE_TYPE (gimple_assign_lhs (stmt))) == BLKmode))
+	  && TYPE_MODE (TREE_TYPE (gimple_get_lhs (stmt))) == BLKmode))
     return false;
 
-  FOR_EACH_SSA_DEF_OPERAND (def_p, stmt, iter, SSA_OP_ALL_DEFS)
+  /* Return if there are no immediate uses of this stmt.  */
+  if (has_zero_uses (DEF_FROM_PTR (def_p)))
     {
-      tree def = DEF_FROM_PTR (def_p);
-      if (is_global_var (SSA_NAME_VAR (def))
-	  || SSA_NAME_OCCURS_IN_ABNORMAL_PHI (def))
-	return false;
+      *zero_uses_p = true;
+      return false;
     }
 
+  if (SSA_NAME_OCCURS_IN_ABNORMAL_PHI (DEF_FROM_PTR (def_p)))
+    return false;
+
   FOR_EACH_SSA_USE_OPERAND (use_p, stmt, iter, SSA_OP_ALL_USES)
     {
       tree use = USE_FROM_PTR (use_p);
@@ -345,88 +312,144 @@
 	return false;
     }
 
+  use = NULL;
+
+  /* If stmt is a store the one and only use needs to be the VOP
+     merging PHI node.  */
+  if (virtual_operand_p (DEF_FROM_PTR (def_p)))
+    {
+      FOR_EACH_IMM_USE_FAST (use_p, imm_iter, DEF_FROM_PTR (def_p))
+	{
+	  gimple *use_stmt = USE_STMT (use_p);
+
+	  /* A killing definition is not a use.  */
+	  if ((gimple_has_lhs (use_stmt)
+	       && operand_equal_p (gimple_get_lhs (stmt),
+				   gimple_get_lhs (use_stmt), 0))
+	      || stmt_kills_ref_p (use_stmt, gimple_get_lhs (stmt)))
+	    {
+	      /* If use_stmt is or might be a nop assignment then USE_STMT
+	         acts as a use as well as definition.  */
+	      if (stmt != use_stmt
+		  && ref_maybe_used_by_stmt_p (use_stmt,
+					       gimple_get_lhs (stmt)))
+		return false;
+	      continue;
+	    }
+
+	  if (gimple_code (use_stmt) != GIMPLE_PHI)
+	    return false;
+
+	  if (use
+	      && use != use_stmt)
+	    return false;
+
+	  use = use_stmt;
+	}
+      if (!use)
+	return false;
+    }
   /* If all the immediate uses are not in the same place, find the nearest
      common dominator of all the immediate uses.  For PHI nodes, we have to
      find the nearest common dominator of all of the predecessor blocks, since
      that is where insertion would have to take place.  */
-  if (!all_immediate_uses_same_place (stmt))
+  else if (gimple_vuse (stmt)
+	   || !all_immediate_uses_same_place (def_p))
     {
       bool debug_stmts = false;
-      basic_block commondom = nearest_common_dominator_of_uses (stmt,
+      basic_block commondom = nearest_common_dominator_of_uses (def_p,
 								&debug_stmts);
 
       if (commondom == frombb)
 	return false;
 
+      /* If this is a load then do not sink past any stores.
+	 ???  This is overly simple but cheap.  We basically look
+	 for an existing load with the same VUSE in the path to one
+	 of the sink candidate blocks and we adjust commondom to the
+	 nearest to commondom.  */
+      if (gimple_vuse (stmt))
+	{
+	  /* Do not sink loads from hard registers.  */
+	  if (gimple_assign_single_p (stmt)
+	      && TREE_CODE (gimple_assign_rhs1 (stmt)) == VAR_DECL
+	      && DECL_HARD_REGISTER (gimple_assign_rhs1 (stmt)))
+	    return false;
+
+	  imm_use_iterator imm_iter;
+	  use_operand_p use_p;
+	  basic_block found = NULL;
+	  FOR_EACH_IMM_USE_FAST (use_p, imm_iter, gimple_vuse (stmt))
+	    {
+	      gimple *use_stmt = USE_STMT (use_p);
+	      basic_block bb = gimple_bb (use_stmt);
+	      /* For PHI nodes the block we know sth about
+		 is the incoming block with the use.  */
+	      if (gimple_code (use_stmt) == GIMPLE_PHI)
+		bb = EDGE_PRED (bb, PHI_ARG_INDEX_FROM_USE (use_p))->src;
+	      /* Any dominator of commondom would be ok with
+	         adjusting commondom to that block.  */
+	      bb = nearest_common_dominator (CDI_DOMINATORS, bb, commondom);
+	      if (!found)
+		found = bb;
+	      else if (dominated_by_p (CDI_DOMINATORS, bb, found))
+		found = bb;
+	      /* If we can't improve, stop.  */
+	      if (found == commondom)
+		break;
+	    }
+	  commondom = found;
+	  if (commondom == frombb)
+	    return false;
+	}
+
       /* Our common dominator has to be dominated by frombb in order to be a
 	 trivially safe place to put this statement, since it has multiple
 	 uses.  */
       if (!dominated_by_p (CDI_DOMINATORS, commondom, frombb))
 	return false;
 
-      /* It doesn't make sense to move to a dominator that post-dominates
-	 frombb, because it means we've just moved it into a path that always
-	 executes if frombb executes, instead of reducing the number of
-	 executions .  */
-      if (dominated_by_p (CDI_POST_DOMINATORS, frombb, commondom))
-	{
-	  if (dump_file && (dump_flags & TDF_DETAILS))
-	    fprintf (dump_file, "Not moving store, common dominator post-dominates from block.\n");
-	  return false;
-	}
+      commondom = select_best_block (frombb, commondom, stmt);
 
-      if (commondom == frombb || commondom->loop_depth > frombb->loop_depth)
-	return false;
-      if (dump_file && (dump_flags & TDF_DETAILS))
-	{
-	  fprintf (dump_file, "Common dominator of all uses is %d\n",
-		   commondom->index);
-	}
+      if (commondom == frombb)
+	return false;	
 
       *togsi = gsi_after_labels (commondom);
 
       return true;
     }
-
-  use = USE_STMT (one_use);
-  if (gimple_code (use) != GIMPLE_PHI)
+  else
     {
-      sinkbb = gimple_bb (use);
-      if (sinkbb == frombb || sinkbb->loop_depth > frombb->loop_depth
-	  || sinkbb->loop_father != frombb->loop_father)
-	return false;
+      FOR_EACH_IMM_USE_FAST (one_use, imm_iter, DEF_FROM_PTR (def_p))
+	{
+	  if (is_gimple_debug (USE_STMT (one_use)))
+	    continue;
+	  break;
+	}
+      use = USE_STMT (one_use);
 
-      /* Move the expression to a post dominator can't reduce the number of
-         executions.  */
-      if (dominated_by_p (CDI_POST_DOMINATORS, frombb, sinkbb))
-        return false;
+      if (gimple_code (use) != GIMPLE_PHI)
+	{
+	  sinkbb = gimple_bb (use);
+	  sinkbb = select_best_block (frombb, gimple_bb (use), stmt);
 
-      *togsi = gsi_for_stmt (use);
+	  if (sinkbb == frombb)
+	    return false;
 
-      return true;
+	  *togsi = gsi_for_stmt (use);
+
+	  return true;
+	}
     }
 
-  /* Note that at this point, all uses must be in the same statement, so it
-     doesn't matter which def op we choose, pick the first one.  */
-  FOR_EACH_SSA_TREE_OPERAND (def, stmt, iter, SSA_OP_ALL_DEFS)
-    break;
+  sinkbb = find_bb_for_arg (as_a <gphi *> (use), DEF_FROM_PTR (def_p));
 
-  sinkbb = find_bb_for_arg (use, def);
+  /* This can happen if there are multiple uses in a PHI.  */
   if (!sinkbb)
     return false;
-
-  /* This will happen when you have
-     a_3 = PHI <a_13, a_26>
-
-     a_26 = VDEF <a_3>
-
-     If the use is a phi, and is in the same bb as the def,
-     we can't sink it.  */
-
-  if (gimple_bb (use) == frombb)
-    return false;
-  if (sinkbb == frombb || sinkbb->loop_depth > frombb->loop_depth
-      || sinkbb->loop_father != frombb->loop_father)
+  
+  sinkbb = select_best_block (frombb, sinkbb, stmt);
+  if (!sinkbb || sinkbb == frombb)
     return false;
 
   /* If the latch block is empty, don't make it non-empty by sinking
@@ -435,11 +458,6 @@
       && empty_block_p (sinkbb))
     return false;
 
-  /* Move the expression to a post dominator can't reduce the number of
-     executions.  */
-  if (dominated_by_p (CDI_POST_DOMINATORS, frombb, sinkbb))
-    return false;
-
   *togsi = gsi_after_labels (sinkbb);
 
   return true;
@@ -468,14 +486,25 @@
 
   for (gsi = gsi_last_bb (bb); !gsi_end_p (gsi);)
     {
-      gimple stmt = gsi_stmt (gsi);
+      gimple *stmt = gsi_stmt (gsi);
       gimple_stmt_iterator togsi;
+      bool zero_uses_p;
 
-      if (!statement_sink_location (stmt, bb, &togsi))
+      if (!statement_sink_location (stmt, bb, &togsi, &zero_uses_p))
 	{
+	  gimple_stmt_iterator saved = gsi;
 	  if (!gsi_end_p (gsi))
 	    gsi_prev (&gsi);
-	  last = false;
+	  /* If we face a dead stmt remove it as it possibly blocks
+	     sinking of uses.  */
+	  if (zero_uses_p
+	      && ! gimple_vdef (stmt))
+	    {
+	      gsi_remove (&saved, true);
+	      release_defs (stmt);
+	    }
+	  else
+	    last = false;
 	  continue;
 	}
       if (dump_file)
@@ -486,6 +515,20 @@
 		   bb->index, (gsi_bb (togsi))->index);
 	}
 
+      /* Update virtual operands of statements in the path we
+         do not sink to.  */
+      if (gimple_vdef (stmt))
+	{
+	  imm_use_iterator iter;
+	  use_operand_p use_p;
+	  gimple *vuse_stmt;
+
+	  FOR_EACH_IMM_USE_STMT (vuse_stmt, iter, gimple_vdef (stmt))
+	    if (gimple_code (vuse_stmt) != GIMPLE_PHI)
+	      FOR_EACH_IMM_USE_ON_STMT (use_p, iter)
+		SET_USE (use_p, gimple_vuse (stmt));
+	}
+
       /* If this is the end of the basic block, we need to insert at the end
          of the basic block.  */
       if (gsi_end_p (togsi))
@@ -556,57 +599,58 @@
    Note that this reduces the number of computations of a = b + c to 1
    when we take the else edge, instead of 2.
 */
-static void
-execute_sink_code (void)
+namespace {
+
+const pass_data pass_data_sink_code =
+{
+  GIMPLE_PASS, /* type */
+  "sink", /* name */
+  OPTGROUP_NONE, /* optinfo_flags */
+  TV_TREE_SINK, /* tv_id */
+  /* PROP_no_crit_edges is ensured by running split_critical_edges in
+     pass_data_sink_code::execute ().  */
+  ( PROP_cfg | PROP_ssa ), /* properties_required */
+  0, /* properties_provided */
+  0, /* properties_destroyed */
+  0, /* todo_flags_start */
+  TODO_update_ssa, /* todo_flags_finish */
+};
+
+class pass_sink_code : public gimple_opt_pass
+{
+public:
+  pass_sink_code (gcc::context *ctxt)
+    : gimple_opt_pass (pass_data_sink_code, ctxt)
+  {}
+
+  /* opt_pass methods: */
+  virtual bool gate (function *) { return flag_tree_sink != 0; }
+  virtual unsigned int execute (function *);
+
+}; // class pass_sink_code
+
+unsigned int
+pass_sink_code::execute (function *fun)
 {
   loop_optimizer_init (LOOPS_NORMAL);
-
+  split_critical_edges ();
   connect_infinite_loops_to_exit ();
   memset (&sink_stats, 0, sizeof (sink_stats));
   calculate_dominance_info (CDI_DOMINATORS);
   calculate_dominance_info (CDI_POST_DOMINATORS);
-  sink_code_in_bb (EXIT_BLOCK_PTR);
-  statistics_counter_event (cfun, "Sunk statements", sink_stats.sunk);
+  sink_code_in_bb (EXIT_BLOCK_PTR_FOR_FN (fun));
+  statistics_counter_event (fun, "Sunk statements", sink_stats.sunk);
   free_dominance_info (CDI_POST_DOMINATORS);
   remove_fake_exit_edges ();
   loop_optimizer_finalize ();
-}
 
-/* Gate and execute functions for PRE.  */
-
-static unsigned int
-do_sink (void)
-{
-  execute_sink_code ();
   return 0;
 }
 
-static bool
-gate_sink (void)
-{
-  return flag_tree_sink != 0;
-}
+} // anon namespace
 
-struct gimple_opt_pass pass_sink_code =
+gimple_opt_pass *
+make_pass_sink_code (gcc::context *ctxt)
 {
- {
-  GIMPLE_PASS,
-  "sink",				/* name */
-  gate_sink,				/* gate */
-  do_sink,				/* execute */
-  NULL,					/* sub */
-  NULL,					/* next */
-  0,					/* static_pass_number */
-  TV_TREE_SINK,				/* tv_id */
-  PROP_no_crit_edges | PROP_cfg
-    | PROP_ssa,				/* properties_required */
-  0,					/* properties_provided */
-  0,					/* properties_destroyed */
-  0,					/* todo_flags_start */
-  TODO_update_ssa
-    | TODO_verify_ssa
-    | TODO_verify_flow
-    | TODO_dump_func
-    | TODO_ggc_collect			/* todo_flags_finish */
- }
-};
+  return new pass_sink_code (ctxt);
+}