OSDN Git Service

Daily bump.
[pf3gnuchains/gcc-fork.git] / gcc / tree-ssa-threadedge.c
index 1bcf2bf..a2860f6 100644 (file)
@@ -1,5 +1,6 @@
 /* SSA Jump Threading
-   Copyright (C) 2005, 2006, 2007, 2008, 2009 Free Software Foundation, Inc.
+   Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010, 2011
+   Free Software Foundation, Inc.
    Contributed by Jeff Law  <law@redhat.com>
 
 This file is part of GCC.
@@ -24,19 +25,14 @@ along with GCC; see the file COPYING3.  If not see
 #include "tm.h"
 #include "tree.h"
 #include "flags.h"
-#include "rtl.h"
 #include "tm_p.h"
-#include "ggc.h"
 #include "basic-block.h"
 #include "cfgloop.h"
 #include "output.h"
-#include "expr.h"
 #include "function.h"
-#include "diagnostic.h"
 #include "timevar.h"
 #include "tree-dump.h"
 #include "tree-flow.h"
-#include "real.h"
 #include "tree-pass.h"
 #include "tree-ssa-propagate.h"
 #include "langhooks.h"
@@ -180,7 +176,7 @@ record_temporary_equivalence (tree x, tree y, VEC(tree, heap) **stack)
 }
 
 /* Record temporary equivalences created by PHIs at the target of the
-   edge E.  Record unwind information for the equivalences onto STACK. 
+   edge E.  Record unwind information for the equivalences onto STACK.
 
    If a PHI which prevents threading is encountered, then return FALSE
    indicating we should not thread this edge, else return TRUE.  */
@@ -199,7 +195,7 @@ record_temporary_equivalences_from_phis (edge e, VEC(tree, heap) **stack)
       tree src = PHI_ARG_DEF_FROM_EDGE (phi, e);
       tree dst = gimple_phi_result (phi);
 
-      /* If the desired argument is not the same as this PHI's result 
+      /* If the desired argument is not the same as this PHI's result
         and it is set by a PHI in E->dest, then we can not thread
         through E->dest.  */
       if (src != dst
@@ -229,32 +225,15 @@ fold_assignment_stmt (gimple stmt)
   switch (get_gimple_rhs_class (subcode))
     {
     case GIMPLE_SINGLE_RHS:
-      {
-        tree rhs = gimple_assign_rhs1 (stmt);
-
-        if (TREE_CODE (rhs) == COND_EXPR)
-          {
-            /* Sadly, we have to handle conditional assignments specially
-               here, because fold expects all the operands of an expression
-               to be folded before the expression itself is folded, but we
-               can't just substitute the folded condition here.  */
-            tree cond = fold (COND_EXPR_COND (rhs));
-            if (cond == boolean_true_node)
-              rhs = COND_EXPR_THEN (rhs);
-            else if (cond == boolean_false_node)
-              rhs = COND_EXPR_ELSE (rhs);
-          }
-
-        return fold (rhs);
-      }
-      break;
+      return fold (gimple_assign_rhs1 (stmt));
+
     case GIMPLE_UNARY_RHS:
       {
         tree lhs = gimple_assign_lhs (stmt);
         tree op0 = gimple_assign_rhs1 (stmt);
         return fold_unary (subcode, TREE_TYPE (lhs), op0);
       }
-      break;
+
     case GIMPLE_BINARY_RHS:
       {
         tree lhs = gimple_assign_lhs (stmt);
@@ -262,7 +241,24 @@ fold_assignment_stmt (gimple stmt)
         tree op1 = gimple_assign_rhs2 (stmt);
         return fold_binary (subcode, TREE_TYPE (lhs), op0, op1);
       }
-      break;
+
+    case GIMPLE_TERNARY_RHS:
+      {
+        tree lhs = gimple_assign_lhs (stmt);
+        tree op0 = gimple_assign_rhs1 (stmt);
+        tree op1 = gimple_assign_rhs2 (stmt);
+        tree op2 = gimple_assign_rhs3 (stmt);
+
+       /* Sadly, we have to handle conditional assignments specially
+          here, because fold expects all the operands of an expression
+          to be folded before the expression itself is folded, but we
+          can't just substitute the folded condition here.  */
+        if (gimple_assign_rhs_code (stmt) == COND_EXPR)
+         op0 = fold (op0);
+
+        return fold_ternary (subcode, TREE_TYPE (lhs), op0, op1, op2);
+      }
+
     default:
       gcc_unreachable ();
     }
@@ -274,7 +270,7 @@ fold_assignment_stmt (gimple stmt)
    Record unwind information for temporary equivalences onto STACK.
 
    Use SIMPLIFY (a pointer to a callback function) to further simplify
-   statements using pass specific information. 
+   statements using pass specific information.
 
    We might consider marking just those statements which ultimately
    feed the COND_EXPR.  It's not clear if the overhead of bookkeeping
@@ -372,7 +368,7 @@ record_temporary_equivalences_from_stmts_at_dest (edge e,
       /* At this point we have a statement which assigns an RHS to an
         SSA_VAR on the LHS.  We want to try and simplify this statement
         to expose more context sensitive equivalences which in turn may
-        allow us to simplify the condition at the end of the loop. 
+        allow us to simplify the condition at the end of the loop.
 
         Handle simple copy operations as well as implied copies from
         ASSERT_EXPRs.  */
@@ -420,7 +416,7 @@ record_temporary_equivalences_from_stmts_at_dest (edge e,
               || (TREE_CODE (cached_lhs) != SSA_NAME
                   && !is_gimple_min_invariant (cached_lhs)))
             cached_lhs = (*simplify) (stmt, stmt);
-          
+
          /* Restore the statement's original uses/defs.  */
          i = 0;
          FOR_EACH_SSA_USE_OPERAND (use_p, stmt, iter, SSA_OP_USE | SSA_OP_VUSE)
@@ -578,8 +574,137 @@ simplify_control_stmt_condition (edge e,
   return cached_lhs;
 }
 
+/* Return TRUE if the statement at the end of e->dest depends on
+   the output of any statement in BB.   Otherwise return FALSE.
+
+   This is used when we are threading a backedge and need to ensure
+   that temporary equivalences from BB do not affect the condition
+   in e->dest.  */
+
+static bool
+cond_arg_set_in_bb (edge e, basic_block bb)
+{
+  ssa_op_iter iter;
+  use_operand_p use_p;
+  gimple last = last_stmt (e->dest);
+
+  /* E->dest does not have to end with a control transferring
+     instruction.  This can occurr when we try to extend a jump
+     threading opportunity deeper into the CFG.  In that case
+     it is safe for this check to return false.  */
+  if (!last)
+    return false;
+
+  if (gimple_code (last) != GIMPLE_COND
+      && gimple_code (last) != GIMPLE_GOTO
+      && gimple_code (last) != GIMPLE_SWITCH)
+    return false;
+
+  FOR_EACH_SSA_USE_OPERAND (use_p, last, iter, SSA_OP_USE | SSA_OP_VUSE)
+    {
+      tree use = USE_FROM_PTR (use_p);
+
+      if (TREE_CODE (use) == SSA_NAME
+         && gimple_code (SSA_NAME_DEF_STMT (use)) != GIMPLE_PHI
+         && gimple_bb (SSA_NAME_DEF_STMT (use)) == bb)
+       return true;
+    }
+  return false;
+}
+
+/* TAKEN_EDGE represents the an edge taken as a result of jump threading.
+   See if we can thread around TAKEN_EDGE->dest as well.  If so, return
+   the edge out of TAKEN_EDGE->dest that we can statically compute will be
+   traversed.
+
+   We are much more restrictive as to the contents of TAKEN_EDGE->dest
+   as the path isolation code in tree-ssa-threadupdate.c isn't prepared
+   to handle copying intermediate blocks on a threaded path. 
+
+   Long term a more consistent and structured approach to path isolation
+   would be a huge help.   */
+static edge
+thread_around_empty_block (edge taken_edge,
+                          gimple dummy_cond,
+                          bool handle_dominating_asserts,
+                          tree (*simplify) (gimple, gimple),
+                          bitmap visited)
+{
+  basic_block bb = taken_edge->dest;
+  gimple_stmt_iterator gsi;
+  gimple stmt;
+  tree cond;
+
+  /* This block must have a single predecessor (E->dest).  */
+  if (!single_pred_p (bb))
+    return NULL;
+
+  /* This block must have more than one successor.  */
+  if (single_succ_p (bb))
+    return NULL;
+
+  /* This block can have no PHI nodes.  This is overly conservative.  */
+  if (!gsi_end_p (gsi_start_phis (bb)))
+    return NULL;
+
+  /* Skip over DEBUG statements at the start of the block.  */
+  gsi = gsi_start_nondebug_bb (bb);
+
+  if (gsi_end_p (gsi))
+    return NULL;
+
+  /* This block can have no statements other than its control altering
+     statement.  This is overly conservative.  */
+  stmt = gsi_stmt (gsi);
+  if (gimple_code (stmt) != GIMPLE_COND
+      && gimple_code (stmt) != GIMPLE_GOTO
+      && gimple_code (stmt) != GIMPLE_SWITCH)
+    return NULL;
+
+  /* Extract and simplify the condition.  */
+  cond = simplify_control_stmt_condition (taken_edge, stmt, dummy_cond,
+                                         simplify, handle_dominating_asserts);
+
+  /* If the condition can be statically computed and we have not already
+     visited the destination edge, then add the taken edge to our thread
+     path.  */
+  if (cond && is_gimple_min_invariant (cond))
+    {
+      edge taken_edge = find_taken_edge (bb, cond);
+
+      if (bitmap_bit_p (visited, taken_edge->dest->index))
+       return NULL;
+      bitmap_set_bit (visited, taken_edge->dest->index);
+      return taken_edge;
+    }
+  return NULL;
+}
+      
+/* E1 and E2 are edges into the same basic block.  Return TRUE if the
+   PHI arguments associated with those edges are equal or there are no
+   PHI arguments, otherwise return FALSE.  */
+
+static bool
+phi_args_equal_on_edges (edge e1, edge e2)
+{
+  gimple_stmt_iterator gsi;
+  int indx1 = e1->dest_idx;
+  int indx2 = e2->dest_idx;
+
+  for (gsi = gsi_start_phis (e1->dest); !gsi_end_p (gsi); gsi_next (&gsi))
+    {
+      gimple phi = gsi_stmt (gsi);
+
+      if (!operand_equal_p (gimple_phi_arg_def (phi, indx1),
+                           gimple_phi_arg_def (phi, indx2), 0))
+       return false;
+    }
+  return true;
+}
+
 /* We are exiting E->src, see if E->dest ends with a conditional
-   jump which has a known value when reached via E. 
+   jump which has a known value when reached via E.
 
    Special care is necessary if E is a back edge in the CFG as we
    may have already recorded equivalences for E->dest into our
@@ -592,14 +717,14 @@ simplify_control_stmt_condition (edge e,
    end with a conditional which is either always true or always
    false when reached via the loop backedge.  Thus we do not want
    to blindly disable threading across a loop backedge.
+
    DUMMY_COND is a shared cond_expr used by condition simplification as scratch,
    to avoid allocating memory.
+
    HANDLE_DOMINATING_ASSERTS is true if we should try to replace operands of
    the simplified condition with left-hand sides of ASSERT_EXPRs they are
    used in.
+
    STACK is used to undo temporary equivalences created during the walk of
    E->dest.
 
@@ -620,21 +745,10 @@ thread_across_edge (gimple dummy_cond,
      safe to thread this edge.  */
   if (e->flags & EDGE_DFS_BACK)
     {
-      ssa_op_iter iter;
-      use_operand_p use_p;
-      gimple last = gsi_stmt (gsi_last_bb (e->dest));
-
-      FOR_EACH_SSA_USE_OPERAND (use_p, last, iter, SSA_OP_USE | SSA_OP_VUSE)
-       {
-         tree use = USE_FROM_PTR (use_p);
-
-          if (TREE_CODE (use) == SSA_NAME
-             && gimple_code (SSA_NAME_DEF_STMT (use)) != GIMPLE_PHI
-             && gimple_bb (SSA_NAME_DEF_STMT (use)) == e->dest)
-           goto fail;
-       }
+      if (cond_arg_set_in_bb (e, e->dest))
+       goto fail;
     }
-     
+
   stmt_count = 0;
 
   /* PHIs create temporary equivalences.  */
@@ -656,21 +770,119 @@ thread_across_edge (gimple dummy_cond,
       tree cond;
 
       /* Extract and simplify the condition.  */
-      cond = simplify_control_stmt_condition (e, stmt, dummy_cond, simplify, handle_dominating_asserts);
+      cond = simplify_control_stmt_condition (e, stmt, dummy_cond, simplify,
+                                             handle_dominating_asserts);
 
       if (cond && is_gimple_min_invariant (cond))
        {
          edge taken_edge = find_taken_edge (e->dest, cond);
          basic_block dest = (taken_edge ? taken_edge->dest : NULL);
+         bitmap visited;
+         edge e2;
 
          if (dest == e->dest)
            goto fail;
 
+         /* DEST could be null for a computed jump to an absolute
+            address.  If DEST is not null, then see if we can thread
+            through it as well, this helps capture secondary effects
+            of threading without having to re-run DOM or VRP.  */
+         if (dest
+             && ((e->flags & EDGE_DFS_BACK) == 0
+                 || ! cond_arg_set_in_bb (taken_edge, e->dest)))
+           {
+             /* We don't want to thread back to a block we have already
+                visited.  This may be overly conservative.  */
+             visited = BITMAP_ALLOC (NULL);
+             bitmap_set_bit (visited, dest->index);
+             bitmap_set_bit (visited, e->dest->index);
+             do
+               {
+                 e2 = thread_around_empty_block (taken_edge,
+                                                 dummy_cond,
+                                                 handle_dominating_asserts,
+                                                 simplify,
+                                                 visited);
+                 if (e2)
+                   taken_edge = e2;
+               }
+             while (e2);
+             BITMAP_FREE (visited);
+           }
+
          remove_temporary_equivalences (stack);
-         register_jump_thread (e, taken_edge);
+         register_jump_thread (e, taken_edge, NULL);
+         return;
        }
     }
 
+ /* We were unable to determine what out edge from E->dest is taken.  However,
+    we might still be able to thread through successors of E->dest.  This
+    often occurs when E->dest is a joiner block which then fans back out
+    based on redundant tests.
+
+    If so, we'll copy E->dest and redirect the appropriate predecessor to
+    the copy.  Within the copy of E->dest, we'll thread one or more edges
+    to points deeper in the CFG.
+
+    This is a stopgap until we have a more structured approach to path
+    isolation.  */
+  {
+    edge e2, e3, taken_edge;
+    edge_iterator ei;
+    bool found = false;
+    bitmap visited = BITMAP_ALLOC (NULL);
+
+    /* Look at each successor of E->dest to see if we can thread through it.  */
+    FOR_EACH_EDGE (taken_edge, ei, e->dest->succs)
+      {
+       /* Avoid threading to any block we have already visited.  */
+       bitmap_clear (visited);
+       bitmap_set_bit (visited, taken_edge->dest->index);
+       bitmap_set_bit (visited, e->dest->index);
+
+       /* Record whether or not we were able to thread through a successor
+          of E->dest.  */
+       found = false;
+       e3 = taken_edge;
+       do
+         {
+           if ((e->flags & EDGE_DFS_BACK) == 0
+               || ! cond_arg_set_in_bb (e3, e->dest))
+             e2 = thread_around_empty_block (e3,
+                                             dummy_cond,
+                                             handle_dominating_asserts,
+                                             simplify,
+                                             visited);
+           else
+             e2 = NULL;
+
+           if (e2)
+             {
+               e3 = e2;
+               found = true;
+             }
+         }
+        while (e2);
+
+       /* If we were able to thread through a successor of E->dest, then
+          record the jump threading opportunity.  */
+       if (found)
+         {
+           edge tmp;
+           /* If there is already an edge from the block to be duplicated
+              (E2->src) to the final target (E3->dest), then make sure that
+              the PHI args associated with the edges E2 and E3 are the
+              same.  */
+           tmp = find_edge (taken_edge->src, e3->dest);
+           if (!tmp || phi_args_equal_on_edges (tmp, e3))
+             register_jump_thread (e, taken_edge, e3);
+         }
+
+      }
+    BITMAP_FREE (visited);
+  }
+
  fail:
   remove_temporary_equivalences (stack);
 }