diff gcc/tree-ssa-phiprop.c @ 111: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-phiprop.c	Sun Aug 21 07:07:55 2011 +0900
+++ b/gcc/tree-ssa-phiprop.c	Fri Oct 27 22:46:09 2017 +0900
@@ -1,5 +1,5 @@
 /* Backward propagation of indirect loads through PHIs.
-   Copyright (C) 2007, 2008, 2009, 2010 Free Software Foundation, Inc.
+   Copyright (C) 2007-2017 Free Software Foundation, Inc.
    Contributed by Richard Guenther <rguenther@suse.de>
 
 This file is part of GCC.
@@ -21,18 +21,18 @@
 #include "config.h"
 #include "system.h"
 #include "coretypes.h"
-#include "tm.h"
+#include "backend.h"
 #include "tree.h"
-#include "tm_p.h"
-#include "basic-block.h"
-#include "timevar.h"
-#include "tree-pretty-print.h"
+#include "gimple.h"
+#include "tree-pass.h"
+#include "ssa.h"
 #include "gimple-pretty-print.h"
-#include "tree-flow.h"
-#include "tree-pass.h"
-#include "tree-dump.h"
-#include "langhooks.h"
-#include "flags.h"
+#include "fold-const.h"
+#include "tree-eh.h"
+#include "gimplify.h"
+#include "gimple-iterator.h"
+#include "stor-layout.h"
+#include "tree-ssa-loop.h"
 
 /* This pass propagates indirect loads through the PHI node for its
    address to make the load source possibly non-addressable and to
@@ -104,7 +104,7 @@
 phivn_valid_p (struct phiprop_d *phivn, tree name, basic_block bb)
 {
   tree vuse = phivn[SSA_NAME_VERSION (name)].vuse;
-  gimple use_stmt;
+  gimple *use_stmt;
   imm_use_iterator ui2;
   bool ok = true;
 
@@ -130,11 +130,11 @@
    BB with the virtual operands from USE_STMT.  */
 
 static tree
-phiprop_insert_phi (basic_block bb, gimple phi, gimple use_stmt,
+phiprop_insert_phi (basic_block bb, gphi *phi, gimple *use_stmt,
 		    struct phiprop_d *phivn, size_t n)
 {
   tree res;
-  gimple new_phi;
+  gphi *new_phi = NULL;
   edge_iterator ei;
   edge e;
 
@@ -144,12 +144,13 @@
   /* Build a new PHI node to replace the definition of
      the indirect reference lhs.  */
   res = gimple_assign_lhs (use_stmt);
-  SSA_NAME_DEF_STMT (res) = new_phi = create_phi_node (res, bb);
+  if (TREE_CODE (res) == SSA_NAME)
+    new_phi = create_phi_node (res, bb);
 
   if (dump_file && (dump_flags & TDF_DETAILS))
     {
       fprintf (dump_file, "Inserting PHI for result of load ");
-      print_gimple_stmt (dump_file, use_stmt, 0, 0);
+      print_gimple_stmt (dump_file, use_stmt, 0);
     }
 
   /* Add PHI arguments for each edge inserting loads of the
@@ -157,7 +158,7 @@
   FOR_EACH_EDGE (e, ei, bb->preds)
     {
       tree old_arg, new_var;
-      gimple tmp;
+      gassign *tmp;
       source_location locus;
 
       old_arg = PHI_ARG_DEF_FROM_EDGE (phi, e);
@@ -166,7 +167,7 @@
 	     && (SSA_NAME_VERSION (old_arg) >= n
 	         || phivn[SSA_NAME_VERSION (old_arg)].value == NULL_TREE))
 	{
-	  gimple def_stmt = SSA_NAME_DEF_STMT (old_arg);
+	  gimple *def_stmt = SSA_NAME_DEF_STMT (old_arg);
 	  old_arg = gimple_assign_rhs1 (def_stmt);
 	  locus = gimple_location (def_stmt);
 	}
@@ -176,10 +177,10 @@
 	  if (dump_file && (dump_flags & TDF_DETAILS))
 	    {
 	      fprintf (dump_file, "  for edge defining ");
-	      print_generic_expr (dump_file, PHI_ARG_DEF_FROM_EDGE (phi, e), 0);
+	      print_generic_expr (dump_file, PHI_ARG_DEF_FROM_EDGE (phi, e));
 	      fprintf (dump_file, " reusing PHI result ");
 	      print_generic_expr (dump_file,
-				  phivn[SSA_NAME_VERSION (old_arg)].value, 0);
+				  phivn[SSA_NAME_VERSION (old_arg)].value);
 	      fprintf (dump_file, "\n");
 	    }
 	  /* Reuse a formerly created dereference.  */
@@ -189,7 +190,10 @@
 	{
 	  tree rhs = gimple_assign_rhs1 (use_stmt);
 	  gcc_assert (TREE_CODE (old_arg) == ADDR_EXPR);
-	  new_var = create_tmp_reg (TREE_TYPE (rhs), NULL);
+	  if (TREE_CODE (res) == SSA_NAME)
+	    new_var = make_ssa_name (TREE_TYPE (rhs));
+	  else
+	    new_var = unshare_expr (res);
 	  if (!is_gimple_min_invariant (old_arg))
 	    old_arg = PHI_ARG_DEF_FROM_EDGE (phi, e);
 	  else
@@ -198,10 +202,6 @@
 				     fold_build2 (MEM_REF, TREE_TYPE (rhs),
 						  old_arg,
 						  TREE_OPERAND (rhs, 1)));
-	  gcc_assert (is_gimple_reg (new_var));
-	  add_referenced_var (new_var);
-	  new_var = make_ssa_name (new_var, tmp);
-	  gimple_assign_set_lhs (tmp, new_var);
 	  gimple_set_location (tmp, locus);
 
 	  gsi_insert_on_edge (e, tmp);
@@ -210,21 +210,38 @@
 	  if (dump_file && (dump_flags & TDF_DETAILS))
 	    {
 	      fprintf (dump_file, "  for edge defining ");
-	      print_generic_expr (dump_file, PHI_ARG_DEF_FROM_EDGE (phi, e), 0);
+	      print_generic_expr (dump_file, PHI_ARG_DEF_FROM_EDGE (phi, e));
 	      fprintf (dump_file, " inserting load ");
-	      print_gimple_stmt (dump_file, tmp, 0, 0);
+	      print_gimple_stmt (dump_file, tmp, 0);
 	    }
 	}
 
-      add_phi_arg (new_phi, new_var, e, locus);
+      if (new_phi)
+	add_phi_arg (new_phi, new_var, e, locus);
+    }
+
+  if (new_phi)
+    {
+      update_stmt (new_phi);
+
+      if (dump_file && (dump_flags & TDF_DETAILS))
+	print_gimple_stmt (dump_file, new_phi, 0);
     }
 
-  update_stmt (new_phi);
+  return res;
+}
+
+/* Verify if *idx is available at *DATA.  */
 
-  if (dump_file && (dump_flags & TDF_DETAILS))
-    print_gimple_stmt (dump_file, new_phi, 0, 0);
-
-  return res;
+static bool
+chk_uses (tree, tree *idx, void *data)
+{
+  basic_block dom = (basic_block) data;
+  if (TREE_CODE (*idx) == SSA_NAME)
+    return (SSA_NAME_IS_DEFAULT_DEF (*idx)
+	    || ! dominated_by_p (CDI_DOMINATORS,
+				 gimple_bb (SSA_NAME_DEF_STMT (*idx)), dom));
+  return true;
 }
 
 /* Propagate between the phi node arguments of PHI in BB and phi result
@@ -242,11 +259,11 @@
    with aliasing issues as we are moving memory reads.  */
 
 static bool
-propagate_with_phi (basic_block bb, gimple phi, struct phiprop_d *phivn,
+propagate_with_phi (basic_block bb, gphi *phi, struct phiprop_d *phivn,
 		    size_t n)
 {
   tree ptr = PHI_RESULT (phi);
-  gimple use_stmt;
+  gimple *use_stmt;
   tree res = NULL_TREE;
   gimple_stmt_iterator gsi;
   imm_use_iterator ui;
@@ -254,10 +271,10 @@
   ssa_op_iter i;
   bool phi_inserted;
   tree type = NULL_TREE;
-  bool one_invariant = false;
 
   if (!POINTER_TYPE_P (TREE_TYPE (ptr))
-      || !is_gimple_reg_type (TREE_TYPE (TREE_TYPE (ptr))))
+      || (!is_gimple_reg_type (TREE_TYPE (TREE_TYPE (ptr)))
+	  && TYPE_MODE (TREE_TYPE (TREE_TYPE (ptr))) == BLKmode))
     return false;
 
   /* Check if we can "cheaply" dereference all phi arguments.  */
@@ -272,7 +289,7 @@
 	     && (SSA_NAME_VERSION (arg) >= n
 	         || phivn[SSA_NAME_VERSION (arg)].value == NULL_TREE))
 	{
-	  gimple def_stmt = SSA_NAME_DEF_STMT (arg);
+	  gimple *def_stmt = SSA_NAME_DEF_STMT (arg);
 	  if (!gimple_assign_single_p (def_stmt))
 	    return false;
 	  arg = gimple_assign_rhs1 (def_stmt);
@@ -289,17 +306,8 @@
       if (!type
 	  && TREE_CODE (arg) == SSA_NAME)
 	type = TREE_TYPE (phivn[SSA_NAME_VERSION (arg)].value);
-      if (TREE_CODE (arg) == ADDR_EXPR
-	  && is_gimple_min_invariant (arg))
-	one_invariant = true;
     }
 
-  /* If we neither have an address of a decl nor can reuse a previously
-     inserted load, do not hoist anything.  */
-  if (!one_invariant
-      && !type)
-    return false;
-
   /* Find a dereferencing use.  First follow (single use) ssa
      copy chains for ptr.  */
   while (single_imm_use (ptr, &use, &use_stmt)
@@ -311,12 +319,17 @@
   phi_inserted = false;
   FOR_EACH_IMM_USE_STMT (use_stmt, ui, ptr)
     {
-      gimple def_stmt;
+      gimple *def_stmt;
       tree vuse;
 
+      /* Only replace loads in blocks that post-dominate the PHI node.  That
+         makes sure we don't end up speculating loads.  */
+      if (!dominated_by_p (CDI_POST_DOMINATORS,
+			   bb, gimple_bb (use_stmt)))
+	continue;
+
       /* Check whether this is a load of *ptr.  */
       if (!(is_gimple_assign (use_stmt)
-	    && TREE_CODE (gimple_assign_lhs (use_stmt)) == SSA_NAME
 	    && gimple_assign_rhs_code (use_stmt) == MEM_REF
 	    && TREE_OPERAND (gimple_assign_rhs1 (use_stmt), 0) == ptr
 	    && integer_zerop (TREE_OPERAND (gimple_assign_rhs1 (use_stmt), 1))
@@ -328,18 +341,74 @@
 	continue;
 
       /* Check if we can move the loads.  The def stmt of the virtual use
-	 needs to be in a different basic block dominating bb.  */
+	 needs to be in a different basic block dominating bb.  When the
+	 def is an edge-inserted one we know it dominates us.  */
       vuse = gimple_vuse (use_stmt);
       def_stmt = SSA_NAME_DEF_STMT (vuse);
       if (!SSA_NAME_IS_DEFAULT_DEF (vuse)
 	  && (gimple_bb (def_stmt) == bb
-	      || !dominated_by_p (CDI_DOMINATORS,
-				  bb, gimple_bb (def_stmt))))
+	      || (gimple_bb (def_stmt)
+		  && !dominated_by_p (CDI_DOMINATORS,
+				      bb, gimple_bb (def_stmt)))))
 	goto next;
 
+      /* Found a proper dereference with an aggregate copy.  Just
+         insert aggregate copies on the edges instead.  */
+      if (!is_gimple_reg_type (TREE_TYPE (TREE_TYPE (ptr))))
+	{
+	  if (!gimple_vdef (use_stmt))
+	    goto next;
+
+	  /* As we replicate the lhs on each incoming edge all
+	     used SSA names have to be available there.  */
+	  if (! for_each_index (gimple_assign_lhs_ptr (use_stmt),
+				chk_uses,
+				get_immediate_dominator (CDI_DOMINATORS,
+							 gimple_bb (phi))))
+	    goto next;
+
+	  gimple *vuse_stmt;
+	  imm_use_iterator vui;
+	  use_operand_p vuse_p;
+	  /* In order to move the aggregate copies earlier, make sure
+	     there are no statements that could read from memory
+	     aliasing the lhs in between the start of bb and use_stmt.
+	     As we require use_stmt to have a VDEF above, loads after
+	     use_stmt will use a different virtual SSA_NAME.  */
+	  FOR_EACH_IMM_USE_FAST (vuse_p, vui, vuse)
+	    {
+	      vuse_stmt = USE_STMT (vuse_p);
+	      if (vuse_stmt == use_stmt)
+		continue;
+	      if (!dominated_by_p (CDI_DOMINATORS,
+				   gimple_bb (vuse_stmt), bb))
+		continue;
+	      if (ref_maybe_used_by_stmt_p (vuse_stmt,
+					    gimple_assign_lhs (use_stmt)))
+		goto next;
+	    }
+
+	  phiprop_insert_phi (bb, phi, use_stmt, phivn, n);
+
+	  /* Remove old stmt.  The phi is taken care of by DCE.  */
+	  gsi = gsi_for_stmt (use_stmt);
+	  /* Unlinking the VDEF here is fine as we are sure that we process
+	     stmts in execution order due to aggregate copies having VDEFs
+	     and we emit loads on the edges in the very same order.
+	     We get multiple copies (or intermediate register loads) handled
+	     only by walking PHIs or immediate uses in a lucky order though,
+	     so we could signal the caller to re-start iterating over PHIs
+	     when we come here which would make it quadratic in the number
+	     of PHIs.  */
+	  unlink_stmt_vdef (use_stmt);
+	  gsi_remove (&gsi, true);
+
+	  phi_inserted = true;
+	}
+
       /* Found a proper dereference.  Insert a phi node if this
 	 is the first load transformation.  */
-      if (!phi_inserted)
+      else if (!phi_inserted)
 	{
 	  res = phiprop_insert_phi (bb, phi, use_stmt, phivn, n);
 	  type = TREE_TYPE (res);
@@ -373,62 +442,73 @@
 
 /* Main entry for phiprop pass.  */
 
-static unsigned int
-tree_ssa_phiprop (void)
+namespace {
+
+const pass_data pass_data_phiprop =
 {
-  VEC(basic_block, heap) *bbs;
+  GIMPLE_PASS, /* type */
+  "phiprop", /* name */
+  OPTGROUP_NONE, /* optinfo_flags */
+  TV_TREE_PHIPROP, /* tv_id */
+  ( PROP_cfg | PROP_ssa ), /* properties_required */
+  0, /* properties_provided */
+  0, /* properties_destroyed */
+  0, /* todo_flags_start */
+  TODO_update_ssa, /* todo_flags_finish */
+};
+
+class pass_phiprop : public gimple_opt_pass
+{
+public:
+  pass_phiprop (gcc::context *ctxt)
+    : gimple_opt_pass (pass_data_phiprop, ctxt)
+  {}
+
+  /* opt_pass methods: */
+  virtual bool gate (function *) { return flag_tree_phiprop; }
+  virtual unsigned int execute (function *);
+
+}; // class pass_phiprop
+
+unsigned int
+pass_phiprop::execute (function *fun)
+{
+  vec<basic_block> bbs;
   struct phiprop_d *phivn;
   bool did_something = false;
   basic_block bb;
-  gimple_stmt_iterator gsi;
+  gphi_iterator gsi;
   unsigned i;
   size_t n;
 
   calculate_dominance_info (CDI_DOMINATORS);
+  calculate_dominance_info (CDI_POST_DOMINATORS);
 
   n = num_ssa_names;
   phivn = XCNEWVEC (struct phiprop_d, n);
 
   /* Walk the dominator tree in preorder.  */
   bbs = get_all_dominated_blocks (CDI_DOMINATORS,
-				  single_succ (ENTRY_BLOCK_PTR));
-  FOR_EACH_VEC_ELT (basic_block, bbs, i, bb)
+				  single_succ (ENTRY_BLOCK_PTR_FOR_FN (fun)));
+  FOR_EACH_VEC_ELT (bbs, i, bb)
     for (gsi = gsi_start_phis (bb); !gsi_end_p (gsi); gsi_next (&gsi))
-      did_something |= propagate_with_phi (bb, gsi_stmt (gsi), phivn, n);
+      did_something |= propagate_with_phi (bb, gsi.phi (), phivn, n);
 
   if (did_something)
     gsi_commit_edge_inserts ();
 
-  VEC_free (basic_block, heap, bbs);
+  bbs.release ();
   free (phivn);
 
+  free_dominance_info (CDI_POST_DOMINATORS);
+
   return 0;
 }
 
-static bool
-gate_phiprop (void)
-{
-  return flag_tree_phiprop;
-}
+} // anon namespace
 
-struct gimple_opt_pass pass_phiprop =
+gimple_opt_pass *
+make_pass_phiprop (gcc::context *ctxt)
 {
- {
-  GIMPLE_PASS,
-  "phiprop",			/* name */
-  gate_phiprop,			/* gate */
-  tree_ssa_phiprop,		/* execute */
-  NULL,				/* sub */
-  NULL,				/* next */
-  0,				/* static_pass_number */
-  TV_TREE_PHIPROP,		/* 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_update_ssa
-  | TODO_verify_ssa		/* todo_flags_finish */
- }
-};
+  return new pass_phiprop (ctxt);
+}