OSDN Git Service

* gcc.target/i386/avx-os-support.h: New.
[pf3gnuchains/gcc-fork.git] / gcc / tree-ssa-phiopt.c
index 8555bc1..30eea31 100644 (file)
@@ -1,6 +1,6 @@
 /* Optimization of PHI nodes by converting them into straightline code.
-   Copyright (C) 2004, 2005, 2006, 2007, 2008 Free Software Foundation,
-   Inc.
+   Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010
+   Free Software Foundation, Inc.
 
 This file is part of GCC.
 
@@ -34,6 +34,8 @@ along with GCC; see the file COPYING3.  If not see
 #include "langhooks.h"
 #include "pointer-set.h"
 #include "domwalk.h"
+#include "cfgloop.h"
+#include "tree-data-ref.h"
 
 static unsigned int tree_ssa_phiopt (void);
 static unsigned int tree_ssa_phiopt_worker (bool);
@@ -47,6 +49,7 @@ static bool abs_replacement (basic_block, basic_block,
                             edge, edge, gimple, tree, tree);
 static bool cond_store_replacement (basic_block, basic_block, edge, edge,
                                    struct pointer_set_t *);
+static bool cond_if_else_store_replacement (basic_block, basic_block, basic_block);
 static struct pointer_set_t * get_non_trapping (void);
 static void replace_phi_edge_with_variable (basic_block, edge, gimple, tree);
 
@@ -149,7 +152,7 @@ tree_ssa_phiopt (void)
      bb0:
        if (cond) goto bb2; else goto bb1;
      bb1:
-       *p = RHS
+       *p = RHS;
      bb2:
 
    with
@@ -160,10 +163,28 @@ tree_ssa_phiopt (void)
        condtmp' = *p;
      bb2:
        condtmp = PHI <RHS, condtmp'>
-       *p = condtmp
+       *p = condtmp;
 
    This transformation can only be done under several constraints,
-   documented below.  */
+   documented below.  It also replaces:
+
+     bb0:
+       if (cond) goto bb2; else goto bb1;
+     bb1:
+       *p = RHS1;
+       goto bb3;
+     bb2:
+       *p = RHS2;
+     bb3:
+
+   with
+
+     bb0:
+       if (cond) goto bb3; else goto bb1;
+     bb1:
+     bb3:
+       condtmp = PHI <RHS1, RHS2>
+       *p = condtmp;  */
 
 static unsigned int
 tree_ssa_cs_elim (void)
@@ -248,8 +269,23 @@ tree_ssa_phiopt_worker (bool do_store_elim)
          e1 = e2;
          e2 = e_tmp;
        }
+      else if (do_store_elim
+              && EDGE_SUCC (bb1, 0)->dest == EDGE_SUCC (bb2, 0)->dest)
+       {
+         basic_block bb3 = EDGE_SUCC (bb1, 0)->dest;
+
+         if (!single_succ_p (bb1)
+             || (EDGE_SUCC (bb1, 0)->flags & EDGE_FALLTHRU) == 0
+             || !single_succ_p (bb2)
+             || (EDGE_SUCC (bb2, 0)->flags & EDGE_FALLTHRU) == 0
+             || EDGE_COUNT (bb3->preds) != 2)
+           continue;
+         if (cond_if_else_store_replacement (bb1, bb2, bb3))
+           cfgchanged = true;
+         continue;
+       }
       else
-        continue;
+       continue;      
 
       e1 = EDGE_SUCC (bb1, 0);
 
@@ -277,14 +313,26 @@ tree_ssa_phiopt_worker (bool do_store_elim)
       else
        {
          gimple_seq phis = phi_nodes (bb2);
+         gimple_stmt_iterator gsi;
 
-         /* Check to make sure that there is only one PHI node.
+         /* Check to make sure that there is only one non-virtual PHI node.
             TODO: we could do it with more than one iff the other PHI nodes
             have the same elements for these two edges.  */
-         if (! gimple_seq_singleton_p (phis))
+         phi = NULL;
+         for (gsi = gsi_start (phis); !gsi_end_p (gsi); gsi_next (&gsi))
+           {
+             if (!is_gimple_reg (gimple_phi_result (gsi_stmt (gsi))))
+               continue;
+             if (phi)
+               {
+                 phi = NULL;
+                 break;
+               }
+             phi = gsi_stmt (gsi);
+           }
+         if (!phi)
            continue;
 
-         phi = gsi_stmt (gsi_start (phis));
          arg0 = gimple_phi_arg_def (phi, e1->dest_idx);
          arg1 = gimple_phi_arg_def (phi, e2->dest_idx);
 
@@ -496,8 +544,9 @@ conditional_replacement (basic_block cond_bb, basic_block middle_bb,
   /* To handle special cases like floating point comparison, it is easier and
      less error-prone to build a tree and gimplify it on the fly though it is
      less efficient.  */
-  cond = fold_build2 (gimple_cond_code (stmt), boolean_type_node,
-                     gimple_cond_lhs (stmt), gimple_cond_rhs (stmt));
+  cond = fold_build2_loc (gimple_location (stmt),
+                         gimple_cond_code (stmt), boolean_type_node,
+                         gimple_cond_lhs (stmt), gimple_cond_rhs (stmt));
 
   /* We need to know which is the true edge and which is the false
      edge so that we know when to invert the condition below.  */
@@ -506,7 +555,8 @@ conditional_replacement (basic_block cond_bb, basic_block middle_bb,
       || (e0 == false_edge && integer_onep (arg0))
       || (e1 == true_edge && integer_zerop (arg1))
       || (e1 == false_edge && integer_onep (arg1)))
-    cond = fold_build1 (TRUTH_NOT_EXPR, TREE_TYPE (cond), cond);
+    cond = fold_build1_loc (gimple_location (stmt),
+                           TRUTH_NOT_EXPR, TREE_TYPE (cond), cond);
 
   /* Insert our new statements at the end of conditional block before the
      COND_STMT.  */
@@ -647,7 +697,6 @@ minmax_replacement (basic_block cond_bb, basic_block middle_bb,
 
   cond = last_stmt (cond_bb);
   cmp = gimple_cond_code (cond);
-  result = PHI_RESULT (phi);
 
   /* This transformation is only valid for order comparisons.  Record which
      operand is smaller/larger if the result of the comparison is true.  */
@@ -1190,26 +1239,20 @@ cond_store_replacement (basic_block middle_bb, basic_block join_bb,
   gimple newphi, new_stmt;
   gimple_stmt_iterator gsi;
   source_location locus;
-  enum tree_code code;
 
   /* Check if middle_bb contains of only one store.  */
   if (!assign
-      || gimple_code (assign) != GIMPLE_ASSIGN)
+      || !gimple_assign_single_p (assign))
     return false;
 
   locus = gimple_location (assign);
   lhs = gimple_assign_lhs (assign);
   rhs = gimple_assign_rhs1 (assign);
   if (TREE_CODE (lhs) != MEM_REF
-      || TREE_CODE (TREE_OPERAND (lhs, 0)) != SSA_NAME)
-    return false;
-
-  /* RHS is either a single SSA_NAME or a constant of register type. */
-  code = gimple_assign_rhs_code (assign);
-  if (get_gimple_rhs_class (code) != GIMPLE_SINGLE_RHS
-      || (code != SSA_NAME && !is_gimple_min_invariant (rhs))
+      || TREE_CODE (TREE_OPERAND (lhs, 0)) != SSA_NAME
       || !is_gimple_reg_type (TREE_TYPE (lhs)))
     return false;
+
   /* Prove that we can move the store down.  We could also check
      TREE_THIS_NOTRAP here, but in that case we also could move stores,
      whose value is not available readily, which we want to avoid.  */
@@ -1221,6 +1264,7 @@ cond_store_replacement (basic_block middle_bb, basic_block join_bb,
   gsi = gsi_for_stmt (assign);
   unlink_stmt_vdef (assign);
   gsi_remove (&gsi, true);
+  release_defs (assign);
 
   /* 2) Create a temporary where we can store the old content
         of the memory touched by the store, if we need to.  */
@@ -1263,6 +1307,266 @@ cond_store_replacement (basic_block middle_bb, basic_block join_bb,
   return true;
 }
 
+/* Do the main work of conditional store replacement.  */
+
+static bool
+cond_if_else_store_replacement_1 (basic_block then_bb, basic_block else_bb,
+                                 basic_block join_bb, gimple then_assign,
+                                 gimple else_assign)
+{
+  tree lhs_base, lhs, then_rhs, else_rhs;
+  source_location then_locus, else_locus;
+  gimple_stmt_iterator gsi;
+  gimple newphi, new_stmt;
+
+  if (then_assign == NULL
+      || !gimple_assign_single_p (then_assign)
+      || else_assign == NULL
+      || !gimple_assign_single_p (else_assign))
+    return false;
+
+  lhs = gimple_assign_lhs (then_assign);
+  if (!is_gimple_reg_type (TREE_TYPE (lhs))
+      || !operand_equal_p (lhs, gimple_assign_lhs (else_assign), 0))
+    return false;
+
+  lhs_base = get_base_address (lhs);
+  if (lhs_base == NULL_TREE
+      || (!DECL_P (lhs_base) && TREE_CODE (lhs_base) != MEM_REF))
+    return false;
+
+  then_rhs = gimple_assign_rhs1 (then_assign);
+  else_rhs = gimple_assign_rhs1 (else_assign);
+  then_locus = gimple_location (then_assign);
+  else_locus = gimple_location (else_assign);
+
+  /* Now we've checked the constraints, so do the transformation:
+     1) Remove the stores.  */
+  gsi = gsi_for_stmt (then_assign);
+  unlink_stmt_vdef (then_assign);
+  gsi_remove (&gsi, true);
+  release_defs (then_assign);
+
+  gsi = gsi_for_stmt (else_assign);
+  unlink_stmt_vdef (else_assign);
+  gsi_remove (&gsi, true);
+  release_defs (else_assign);
+
+  /* 2) Create a temporary where we can store the old content
+       of the memory touched by the store, if we need to.  */
+  if (!condstoretemp || TREE_TYPE (lhs) != TREE_TYPE (condstoretemp))
+    {
+      condstoretemp = create_tmp_reg (TREE_TYPE (lhs), "cstore");
+      get_var_ann (condstoretemp);
+    }
+  add_referenced_var (condstoretemp);
+
+  /* 3) Create a PHI node at the join block, with one argument
+       holding the old RHS, and the other holding the temporary
+       where we stored the old memory contents.  */
+  newphi = create_phi_node (condstoretemp, join_bb);
+  add_phi_arg (newphi, then_rhs, EDGE_SUCC (then_bb, 0), then_locus);
+  add_phi_arg (newphi, else_rhs, EDGE_SUCC (else_bb, 0), else_locus);
+
+  new_stmt = gimple_build_assign (lhs, PHI_RESULT (newphi));
+
+  /* 4) Insert that PHI node.  */
+  gsi = gsi_after_labels (join_bb);
+  if (gsi_end_p (gsi))
+    {
+      gsi = gsi_last_bb (join_bb);
+      gsi_insert_after (&gsi, new_stmt, GSI_NEW_STMT);
+    }
+  else
+    gsi_insert_before (&gsi, new_stmt, GSI_NEW_STMT);
+
+  return true;
+}
+
+/* Conditional store replacement.  We already know
+   that the recognized pattern looks like so:
+
+   split:
+     if (cond) goto THEN_BB; else goto ELSE_BB (edge E1)
+   THEN_BB:
+     ...
+     X = Y;
+     ...
+     goto JOIN_BB;
+   ELSE_BB:
+     ...
+     X = Z;
+     ...
+     fallthrough (edge E0)
+   JOIN_BB:
+     some more
+
+   We check that it is safe to sink the store to JOIN_BB by verifying that
+   there are no read-after-write or write-after-write dependencies in
+   THEN_BB and ELSE_BB.  */
+
+static bool
+cond_if_else_store_replacement (basic_block then_bb, basic_block else_bb,
+                                basic_block join_bb)
+{
+  gimple then_assign = last_and_only_stmt (then_bb);
+  gimple else_assign = last_and_only_stmt (else_bb);
+  VEC (data_reference_p, heap) *then_datarefs, *else_datarefs;
+  VEC (ddr_p, heap) *then_ddrs, *else_ddrs;
+  gimple then_store, else_store;
+  bool found, ok = false, res;
+  struct data_dependence_relation *ddr;
+  data_reference_p then_dr, else_dr;
+  int i, j;
+  tree then_lhs, else_lhs;
+  VEC (gimple, heap) *then_stores, *else_stores;
+  basic_block blocks[3];
+
+  if (MAX_STORES_TO_SINK == 0)
+    return false;
+
+  /* Handle the case with single statement in THEN_BB and ELSE_BB.  */
+  if (then_assign && else_assign)
+    return cond_if_else_store_replacement_1 (then_bb, else_bb, join_bb,
+                                             then_assign, else_assign);
+
+  /* Find data references.  */
+  then_datarefs = VEC_alloc (data_reference_p, heap, 1);
+  else_datarefs = VEC_alloc (data_reference_p, heap, 1);
+  if ((find_data_references_in_bb (NULL, then_bb, &then_datarefs)
+        == chrec_dont_know)
+      || !VEC_length (data_reference_p, then_datarefs)
+      || (find_data_references_in_bb (NULL, else_bb, &else_datarefs)
+        == chrec_dont_know)
+      || !VEC_length (data_reference_p, else_datarefs))
+    {
+      free_data_refs (then_datarefs);
+      free_data_refs (else_datarefs);
+      return false;
+    }
+
+  /* Find pairs of stores with equal LHS.  */
+  then_stores = VEC_alloc (gimple, heap, 1);
+  else_stores = VEC_alloc (gimple, heap, 1);
+  FOR_EACH_VEC_ELT (data_reference_p, then_datarefs, i, then_dr)
+    {
+      if (DR_IS_READ (then_dr))
+        continue;
+
+      then_store = DR_STMT (then_dr);
+      then_lhs = gimple_get_lhs (then_store);
+      found = false;
+
+      FOR_EACH_VEC_ELT (data_reference_p, else_datarefs, j, else_dr)
+        {
+          if (DR_IS_READ (else_dr))
+            continue;
+
+          else_store = DR_STMT (else_dr);
+          else_lhs = gimple_get_lhs (else_store);
+
+          if (operand_equal_p (then_lhs, else_lhs, 0))
+            {
+              found = true;
+              break;
+            }
+        }
+
+      if (!found)
+        continue;
+
+      VEC_safe_push (gimple, heap, then_stores, then_store);
+      VEC_safe_push (gimple, heap, else_stores, else_store);
+    }
+
+  /* No pairs of stores found.  */
+  if (!VEC_length (gimple, then_stores)
+      || VEC_length (gimple, then_stores) > (unsigned) MAX_STORES_TO_SINK)
+    {
+      free_data_refs (then_datarefs);
+      free_data_refs (else_datarefs);
+      VEC_free (gimple, heap, then_stores);
+      VEC_free (gimple, heap, else_stores);
+      return false;
+    }
+
+  /* Compute and check data dependencies in both basic blocks.  */
+  then_ddrs = VEC_alloc (ddr_p, heap, 1);
+  else_ddrs = VEC_alloc (ddr_p, heap, 1);
+  compute_all_dependences (then_datarefs, &then_ddrs, NULL, false);
+  compute_all_dependences (else_datarefs, &else_ddrs, NULL, false);
+  blocks[0] = then_bb;
+  blocks[1] = else_bb;
+  blocks[2] = join_bb;
+  renumber_gimple_stmt_uids_in_blocks (blocks, 3);
+
+  /* Check that there are no read-after-write or write-after-write dependencies
+     in THEN_BB.  */
+  FOR_EACH_VEC_ELT (ddr_p, then_ddrs, i, ddr)
+    {
+      struct data_reference *dra = DDR_A (ddr);
+      struct data_reference *drb = DDR_B (ddr);
+
+      if (DDR_ARE_DEPENDENT (ddr) != chrec_known
+          && ((DR_IS_READ (dra) && DR_IS_WRITE (drb)
+               && gimple_uid (DR_STMT (dra)) > gimple_uid (DR_STMT (drb)))
+              || (DR_IS_READ (drb) && DR_IS_WRITE (dra)
+                  && gimple_uid (DR_STMT (drb)) > gimple_uid (DR_STMT (dra)))
+              || (DR_IS_WRITE (dra) && DR_IS_WRITE (drb))))
+        {
+          free_dependence_relations (then_ddrs);
+          free_dependence_relations (else_ddrs);
+         free_data_refs (then_datarefs);
+         free_data_refs (else_datarefs);
+          VEC_free (gimple, heap, then_stores);
+          VEC_free (gimple, heap, else_stores);
+          return false;
+        }
+    }
+
+  /* Check that there are no read-after-write or write-after-write dependencies
+     in ELSE_BB.  */
+  FOR_EACH_VEC_ELT (ddr_p, else_ddrs, i, ddr)
+    {
+      struct data_reference *dra = DDR_A (ddr);
+      struct data_reference *drb = DDR_B (ddr);
+
+      if (DDR_ARE_DEPENDENT (ddr) != chrec_known
+          && ((DR_IS_READ (dra) && DR_IS_WRITE (drb)
+               && gimple_uid (DR_STMT (dra)) > gimple_uid (DR_STMT (drb)))
+              || (DR_IS_READ (drb) && DR_IS_WRITE (dra)
+                  && gimple_uid (DR_STMT (drb)) > gimple_uid (DR_STMT (dra)))
+              || (DR_IS_WRITE (dra) && DR_IS_WRITE (drb))))
+        {
+          free_dependence_relations (then_ddrs);
+          free_dependence_relations (else_ddrs);
+         free_data_refs (then_datarefs);
+         free_data_refs (else_datarefs);
+          VEC_free (gimple, heap, then_stores);
+          VEC_free (gimple, heap, else_stores);
+          return false;
+        }
+    }
+
+  /* Sink stores with same LHS.  */
+  FOR_EACH_VEC_ELT (gimple, then_stores, i, then_store)
+    {
+      else_store = VEC_index (gimple, else_stores, i);
+      res = cond_if_else_store_replacement_1 (then_bb, else_bb, join_bb,
+                                              then_store, else_store);
+      ok = ok || res;
+    }
+
+  free_dependence_relations (then_ddrs);
+  free_dependence_relations (else_ddrs);
+  free_data_refs (then_datarefs);
+  free_data_refs (else_datarefs);
+  VEC_free (gimple, heap, then_stores);
+  VEC_free (gimple, heap, else_stores);
+
+  return ok;
+}
+
 /* Always do these optimizations if we have SSA
    trees to work on.  */
 static bool
@@ -1286,8 +1590,7 @@ struct gimple_opt_pass pass_phiopt =
   0,                                   /* properties_provided */
   0,                                   /* properties_destroyed */
   0,                                   /* todo_flags_start */
-  TODO_dump_func
-    | TODO_ggc_collect
+  TODO_ggc_collect
     | TODO_verify_ssa
     | TODO_verify_flow
     | TODO_verify_stmts                        /* todo_flags_finish */
@@ -1315,8 +1618,7 @@ struct gimple_opt_pass pass_cselim =
   0,                                   /* properties_provided */
   0,                                   /* properties_destroyed */
   0,                                   /* todo_flags_start */
-  TODO_dump_func
-    | TODO_ggc_collect
+  TODO_ggc_collect
     | TODO_verify_ssa
     | TODO_verify_flow
     | TODO_verify_stmts                        /* todo_flags_finish */