OSDN Git Service

PR preprocessor/30805:
[pf3gnuchains/gcc-fork.git] / gcc / tree-tailcall.c
index 379594e..bd3da88 100644 (file)
@@ -1,11 +1,11 @@
-/* Tail calls optimization on trees.
-   Copyright (C) 2003 Free Software Foundation, Inc.
+/* Tail call optimization on trees.
+   Copyright (C) 2003, 2004, 2005, 2006, 2007 Free Software Foundation, Inc.
 
 This file is part of GCC.
 
 GCC is free software; you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
-the Free Software Foundation; either version 2, or (at your option)
+the Free Software Foundation; either version 3, or (at your option)
 any later version.
 
 GCC is distributed in the hope that it will be useful,
@@ -14,9 +14,8 @@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details.
 
 You should have received a copy of the GNU General Public License
-along with GCC; see the file COPYING.  If not, write to
-the Free Software Foundation, 59 Temple Place - Suite 330,
-Boston, MA 02111-1307, USA.  */
+along with GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
 
 #include "config.h"
 #include "system.h"
@@ -35,6 +34,7 @@ Boston, MA 02111-1307, USA.  */
 #include "tree-pass.h"
 #include "flags.h"
 #include "langhooks.h"
+#include "dbgcnt.h"
 
 /* The file implements the tail recursion elimination.  It is also used to
    analyze the tail calls in general, passing the results to the rtl level
@@ -132,21 +132,21 @@ static void find_tail_calls (basic_block, struct tailcall **);
 static bool
 suitable_for_tail_opt_p (void)
 {
-  int i;
+  referenced_var_iterator rvi;
+  tree var;
 
   if (current_function_stdarg)
     return false;
 
-  /* No local variable should be call-clobbered.  We ignore any kind
-     of memory tag, as these are not real variables.  */
-  for (i = 0; i < (int) VARRAY_ACTIVE_SIZE (referenced_vars); i++)
-    {
-      tree var = VARRAY_TREE (referenced_vars, i);
+  /* No local variable nor structure field should be call-clobbered.  We
+     ignore any kind of memory tag, as these are not real variables.  */
 
-      if (decl_function_context (var) == current_function_decl
-         && !TREE_STATIC (var)
-         && var_ann (var)->mem_tag_kind == NOT_A_TAG
-         && is_call_clobbered (var))
+  FOR_EACH_REFERENCED_VAR (var, rvi)
+    {
+      if (!is_global_var (var)
+         && (!MTAG_P (var) || TREE_CODE (var) == STRUCT_FIELD_TAG)
+         && (gimple_aliases_computed_p (cfun) ? is_call_clobbered (var)
+             : TREE_ADDRESSABLE (var)))
        return false;
     }
 
@@ -160,6 +160,8 @@ suitable_for_tail_opt_p (void)
 static bool
 suitable_for_tail_call_opt_p (void)
 {
+  tree param;
+
   /* alloca (until we have stack slot life analysis) inhibits
      sibling call optimizations, but not tail recursion.  */
   if (current_function_calls_alloca)
@@ -177,11 +179,19 @@ suitable_for_tail_call_opt_p (void)
   if (current_function_calls_setjmp)
     return false;
 
+  /* ??? It is OK if the argument of a function is taken in some cases,
+     but not in all cases.  See PR15387 and PR19616.  Revisit for 4.1.  */
+  for (param = DECL_ARGUMENTS (current_function_decl);
+       param;
+       param = TREE_CHAIN (param))
+    if (TREE_ADDRESSABLE (param))
+      return false;
+
   return true;
 }
 
 /* Checks whether the expression EXPR in stmt AT is independent of the
-   statement pointed by BSI (in a sense that we already know EXPR's value
+   statement pointed to by BSI (in a sense that we already know EXPR's value
    at BSI).  We use the fact that we are only called from the chain of
    basic blocks that have only single successor.  Returns the expression
    containing the value of EXPR at BSI.  */
@@ -191,6 +201,7 @@ independent_of_stmt_p (tree expr, tree at, block_stmt_iterator bsi)
 {
   basic_block bb, call_bb, at_bb;
   edge e;
+  edge_iterator ei;
 
   if (is_gimple_min_invariant (expr))
     return expr;
@@ -201,7 +212,7 @@ independent_of_stmt_p (tree expr, tree at, block_stmt_iterator bsi)
   /* Mark the blocks in the chain leading to the end.  */
   at_bb = bb_for_stmt (at);
   call_bb = bb_for_stmt (bsi_stmt (bsi));
-  for (bb = call_bb; bb != at_bb; bb = bb->succ->dest)
+  for (bb = call_bb; bb != at_bb; bb = single_succ (bb))
     bb->aux = &bb->aux;
   bb->aux = &bb->aux;
 
@@ -210,7 +221,7 @@ independent_of_stmt_p (tree expr, tree at, block_stmt_iterator bsi)
       at = SSA_NAME_DEF_STMT (expr);
       bb = bb_for_stmt (at);
 
-      /* The default defininition or defined before the chain.  */
+      /* The default definition or defined before the chain.  */
       if (!bb || !bb->aux)
        break;
 
@@ -231,17 +242,21 @@ independent_of_stmt_p (tree expr, tree at, block_stmt_iterator bsi)
          break;
        }
 
-      for (e = bb->pred; e; e = e->pred_next)
+      FOR_EACH_EDGE (e, ei, bb->preds)
        if (e->src->aux)
          break;
-      if (!e)
-       abort ();
+      gcc_assert (e);
 
-      expr = phi_element_for_edge (at, e)->def;
+      expr = PHI_ARG_DEF_FROM_EDGE (at, e);
+      if (TREE_CODE (expr) != SSA_NAME)
+       {
+         /* The value is a constant.  */
+         break;
+       }
     }
 
   /* Unmark the blocks.  */
-  for (bb = call_bb; bb != at_bb; bb = bb->succ->dest)
+  for (bb = call_bb; bb != at_bb; bb = single_succ (bb))
     bb->aux = NULL;
   bb->aux = NULL;
 
@@ -257,8 +272,8 @@ process_assignment (tree ass, tree stmt, block_stmt_iterator call, tree *m,
                    tree *a, tree *ass_var)
 {
   tree op0, op1, non_ass_var;
-  tree dest = TREE_OPERAND (ass, 0);
-  tree src = TREE_OPERAND (ass, 1);
+  tree dest = GIMPLE_STMT_OPERAND (ass, 0);
+  tree src = GIMPLE_STMT_OPERAND (ass, 1);
   enum tree_code code = TREE_CODE (src);
   tree src_var = src;
 
@@ -276,9 +291,16 @@ process_assignment (tree ass, tree stmt, block_stmt_iterator call, tree *m,
       return true;
     }
 
-  if (TREE_CODE_CLASS (code) != '2')
+  if (TREE_CODE_CLASS (code) != tcc_binary)
     return false;
 
+  /* Accumulator optimizations will reverse the order of operations.
+     We can only do that for floating-point types if we're assuming
+     that addition and multiplication are associative.  */
+  if (!flag_associative_math)
+    if (FLOAT_TYPE_P (TREE_TYPE (DECL_RESULT (current_function_decl))))
+      return false;
+
   /* We only handle the code like
 
      x = call ();
@@ -324,7 +346,7 @@ process_assignment (tree ass, tree stmt, block_stmt_iterator call, tree *m,
       *ass_var = dest;
       return true;
 
-      /* TODO -- Handle other codes (NEGATE_EXPR, MINUS_EXPR).  */
+      /* TODO -- Handle other codes (NEGATE_EXPR, MINUS_EXPR, POINTER_PLUS_EXPR).  */
 
     default:
       return false;
@@ -340,7 +362,7 @@ propagate_through_phis (tree var, edge e)
   tree phi;
 
   for (phi = phi_nodes (dest); phi; phi = PHI_CHAIN (phi))
-    if (phi_element_for_edge (phi, e)->def == var)
+    if (PHI_ARG_DEF_FROM_EDGE (phi, e) == var)
       return PHI_RESULT (phi);
 
   return var;
@@ -352,7 +374,7 @@ propagate_through_phis (tree var, edge e)
 static void
 find_tail_calls (basic_block bb, struct tailcall **ret)
 {
-  tree ass_var, ret_var, stmt, func, param, args, call = NULL_TREE;
+  tree ass_var, ret_var, stmt, func, param, call = NULL_TREE;
   block_stmt_iterator bsi, absi;
   bool tail_recursion;
   struct tailcall *nw;
@@ -361,7 +383,7 @@ find_tail_calls (basic_block bb, struct tailcall **ret)
   basic_block abb;
   stmt_ann_t ann;
 
-  if (bb->succ->succ_next)
+  if (!single_succ_p (bb))
     return;
 
   for (bsi = bsi_last (bb); !bsi_end_p (bsi); bsi_prev (&bsi))
@@ -372,13 +394,13 @@ find_tail_calls (basic_block bb, struct tailcall **ret)
       if (TREE_CODE (stmt) == LABEL_EXPR)
        continue;
 
-      get_stmt_operands (stmt);
-
       /* Check for a call.  */
-      if (TREE_CODE (stmt) == MODIFY_EXPR)
+      if (TREE_CODE (stmt) == GIMPLE_MODIFY_STMT)
        {
-         ass_var = TREE_OPERAND (stmt, 0);
-         call = TREE_OPERAND (stmt, 1);
+         ass_var = GIMPLE_STMT_OPERAND (stmt, 0);
+         call = GIMPLE_STMT_OPERAND (stmt, 1);
+         if (TREE_CODE (call) == WITH_SIZE_EXPR)
+           call = TREE_OPERAND (call, 0);
        }
       else
        {
@@ -389,18 +411,19 @@ find_tail_calls (basic_block bb, struct tailcall **ret)
       if (TREE_CODE (call) == CALL_EXPR)
        break;
 
-      /* If the statement has virtual operands, fail.  */
+      /* If the statement has virtual or volatile operands, fail.  */
       ann = stmt_ann (stmt);
-      if (NUM_V_MAY_DEFS (V_MAY_DEF_OPS (ann))
-          || NUM_V_MUST_DEFS (V_MUST_DEF_OPS (ann))
-         || NUM_VUSES (VUSE_OPS (ann)))
+      if (!ZERO_SSA_OPERANDS (stmt, (SSA_OP_VUSE | SSA_OP_VIRTUAL_DEFS))
+         || ann->has_volatile_ops
+         || (!gimple_aliases_computed_p (cfun) && ann->references_memory))
        return;
     }
 
   if (bsi_end_p (bsi))
     {
+      edge_iterator ei;
       /* Recurse to the predecessors.  */
-      for (e = bb->pred; e; e = e->pred_next)
+      FOR_EACH_EDGE (e, ei, bb->preds)
        find_tail_calls (e->src, ret);
 
       return;
@@ -411,22 +434,36 @@ find_tail_calls (basic_block bb, struct tailcall **ret)
   func = get_callee_fndecl (call);
   if (func == current_function_decl)
     {
-      for (param = DECL_ARGUMENTS (func), args = TREE_OPERAND (call, 1);
-          param && args;
-          param = TREE_CHAIN (param), args = TREE_CHAIN (args))
+      call_expr_arg_iterator iter;
+      tree arg;
+      for (param = DECL_ARGUMENTS (func),
+            arg = first_call_expr_arg (call, &iter);
+          param && arg;
+          param = TREE_CHAIN (param), arg = next_call_expr_arg (&iter))
        {
-         tree arg = TREE_VALUE (args);
-         if (param != arg
-             /* Make sure there are no problems with copying.  Note we must
+         if (param != arg)
+           {
+             /* Make sure there are no problems with copying.  The parameter
                 have a copyable type and the two arguments must have reasonably
                 equivalent types.  The latter requirement could be relaxed if
                 we emitted a suitable type conversion statement.  */
-             && (!is_gimple_reg_type (TREE_TYPE (param))
-                 || !lang_hooks.types_compatible_p (TREE_TYPE (param),
-                                                    TREE_TYPE (arg))))
-           break;
+             if (!is_gimple_reg_type (TREE_TYPE (param))
+                 || !useless_type_conversion_p (TREE_TYPE (param),
+                                               TREE_TYPE (arg)))
+               break;
+
+             /* The parameter should be a real operand, so that phi node
+                created for it at the start of the function has the meaning
+                of copying the value.  This test implies is_gimple_reg_type
+                from the previous condition, however this one could be
+                relaxed by being more careful with copying the new value
+                of the parameter (emitting appropriate GIMPLE_MODIFY_STMT and
+                updating the virtual operands).  */
+             if (!is_gimple_reg (param))
+               break;
+           }
        }
-      if (!args && !param)
+      if (!arg && !param)
        tail_recursion = true;
     }
 
@@ -445,8 +482,8 @@ find_tail_calls (basic_block bb, struct tailcall **ret)
 
       while (bsi_end_p (absi))
        {
-         ass_var = propagate_through_phis (ass_var, abb->succ);
-         abb = abb->succ->dest;
+         ass_var = propagate_through_phis (ass_var, single_succ_edge (abb));
+         abb = single_succ (abb);
          absi = bsi_start (abb);
        }
 
@@ -458,7 +495,7 @@ find_tail_calls (basic_block bb, struct tailcall **ret)
       if (TREE_CODE (stmt) == RETURN_EXPR)
        break;
 
-      if (TREE_CODE (stmt) != MODIFY_EXPR)
+      if (TREE_CODE (stmt) != GIMPLE_MODIFY_STMT)
        return;
 
       if (!process_assignment (stmt, stmt, bsi, &m, &a, &ass_var))
@@ -468,9 +505,9 @@ find_tail_calls (basic_block bb, struct tailcall **ret)
   /* See if this is a tail call we can handle.  */
   ret_var = TREE_OPERAND (stmt, 0);
   if (ret_var
-      && TREE_CODE (ret_var) == MODIFY_EXPR)
+      && TREE_CODE (ret_var) == GIMPLE_MODIFY_STMT)
     {
-      tree ret_op = TREE_OPERAND (ret_var, 1);
+      tree ret_op = GIMPLE_STMT_OPERAND (ret_var, 1);
       STRIP_NOPS (ret_op);
       if (!tail_recursion
          && TREE_CODE (ret_op) != SSA_NAME)
@@ -478,7 +515,7 @@ find_tail_calls (basic_block bb, struct tailcall **ret)
 
       if (!process_assignment (ret_var, stmt, bsi, &m, &a, &ass_var))
        return;
-      ret_var = TREE_OPERAND (ret_var, 0);
+      ret_var = GIMPLE_STMT_OPERAND (ret_var, 0);
     }
 
   /* We may proceed if there either is no return value, or the return value
@@ -492,7 +529,7 @@ find_tail_calls (basic_block bb, struct tailcall **ret)
   if (!tail_recursion && (m || a))
     return;
 
-  nw = xmalloc (sizeof (struct tailcall));
+  nw = XNEW (struct tailcall);
 
   nw->call_block = bb;
   nw->call_bsi = bsi;
@@ -524,34 +561,36 @@ adjust_accumulator_values (block_stmt_iterator bsi, tree m, tree a, edge back)
            var = m_acc;
          else
            {
-             stmt = build (MODIFY_EXPR, ret_type, NULL_TREE,
-                           build (MULT_EXPR, ret_type, m_acc, a));
+             stmt = build_gimple_modify_stmt (NULL_TREE,
+                                              build2 (MULT_EXPR, ret_type,
+                                                      m_acc, a));
 
              tmp = create_tmp_var (ret_type, "acc_tmp");
-             add_referenced_tmp_var (tmp);
+             add_referenced_var (tmp);
 
              var = make_ssa_name (tmp, stmt);
-             TREE_OPERAND (stmt, 0) = var;
+             GIMPLE_STMT_OPERAND (stmt, 0) = var;
              bsi_insert_after (&bsi, stmt, BSI_NEW_STMT);
            }
        }
       else
        var = a;
 
-      stmt = build (MODIFY_EXPR, ret_type, NULL_TREE,
-                   build (PLUS_EXPR, ret_type, a_acc, var));
+      stmt = build_gimple_modify_stmt (NULL_TREE, build2 (PLUS_EXPR, ret_type,
+                                                         a_acc, var));
       var = make_ssa_name (SSA_NAME_VAR (a_acc), stmt);
-      TREE_OPERAND (stmt, 0) = var;
+      GIMPLE_STMT_OPERAND (stmt, 0) = var;
       bsi_insert_after (&bsi, stmt, BSI_NEW_STMT);
       a_acc_arg = var;
     }
 
   if (m)
     {
-      stmt = build (MODIFY_EXPR, ret_type, NULL_TREE,
-                   build (MULT_EXPR, ret_type, m_acc, m));
+      stmt = build_gimple_modify_stmt (NULL_TREE,
+                                      build2 (MULT_EXPR, ret_type,
+                                              m_acc, m));
       var = make_ssa_name (SSA_NAME_VAR (m_acc), stmt);
-      TREE_OPERAND (stmt, 0) = var;
+      GIMPLE_STMT_OPERAND (stmt, 0) = var;
       bsi_insert_after (&bsi, stmt, BSI_NEW_STMT);
       m_acc_arg = var;
     }
@@ -562,7 +601,7 @@ adjust_accumulator_values (block_stmt_iterator bsi, tree m, tree a, edge back)
        if (PHI_RESULT (phi) == a_acc)
          break;
 
-      add_phi_arg (&phi, a_acc_arg, back);
+      add_phi_arg (phi, a_acc_arg, back);
     }
 
   if (m_acc)
@@ -571,7 +610,7 @@ adjust_accumulator_values (block_stmt_iterator bsi, tree m, tree a, edge back)
        if (PHI_RESULT (phi) == m_acc)
          break;
 
-      add_phi_arg (&phi, m_acc_arg, back);
+      add_phi_arg (phi, m_acc_arg, back);
     }
 }
 
@@ -583,55 +622,97 @@ adjust_return_value (basic_block bb, tree m, tree a)
 {
   tree ret_stmt = last_stmt (bb), ret_var, var, stmt, tmp;
   tree ret_type = TREE_TYPE (DECL_RESULT (current_function_decl));
+  tree *ret_op;
   block_stmt_iterator bsi = bsi_last (bb);
 
-  if (TREE_CODE (ret_stmt) != RETURN_EXPR)
-    abort ();
+  gcc_assert (TREE_CODE (ret_stmt) == RETURN_EXPR);
 
   ret_var = TREE_OPERAND (ret_stmt, 0);
   if (!ret_var)
     return;
 
-  if (TREE_CODE (ret_var) == MODIFY_EXPR)
+  if (TREE_CODE (ret_var) == GIMPLE_MODIFY_STMT)
     {
-      ret_var->common.ann = (tree_ann) stmt_ann (ret_stmt);
-      bsi_replace (&bsi, ret_var, true);
-      SSA_NAME_DEF_STMT (TREE_OPERAND (ret_var, 0)) = ret_var;
-      ret_var = TREE_OPERAND (ret_var, 0);
-      ret_stmt = build1 (RETURN_EXPR, TREE_TYPE (ret_stmt), ret_var);
-      bsi_insert_after (&bsi, ret_stmt, BSI_NEW_STMT);
+      ret_op = &GIMPLE_STMT_OPERAND (ret_var, 1);
+      ret_var = *ret_op;
     }
+  else
+    ret_op = &TREE_OPERAND (ret_stmt, 0);
 
   if (m)
     {
-      stmt = build (MODIFY_EXPR, ret_type, NULL_TREE,
-                   build (MULT_EXPR, ret_type, m_acc, ret_var));
+      stmt = build_gimple_modify_stmt (NULL_TREE,
+                                      build2 (MULT_EXPR, ret_type,
+                                              m_acc, ret_var));
 
       tmp = create_tmp_var (ret_type, "acc_tmp");
-      add_referenced_tmp_var (tmp);
+      add_referenced_var (tmp);
 
       var = make_ssa_name (tmp, stmt);
-      TREE_OPERAND (stmt, 0) = var;
-      bsi_insert_before (&bsi, stmt, BSI_NEW_STMT);
+      GIMPLE_STMT_OPERAND (stmt, 0) = var;
+      bsi_insert_before (&bsi, stmt, BSI_SAME_STMT);
     }
   else
     var = ret_var;
 
   if (a)
     {
-      stmt = build (MODIFY_EXPR, ret_type, NULL_TREE,
-                   build (PLUS_EXPR, ret_type, a_acc, var));
+      stmt = build_gimple_modify_stmt (NULL_TREE,
+                                      build2 (PLUS_EXPR, ret_type,
+                                              a_acc, var));
 
       tmp = create_tmp_var (ret_type, "acc_tmp");
-      add_referenced_tmp_var (tmp);
+      add_referenced_var (tmp);
 
       var = make_ssa_name (tmp, stmt);
-      TREE_OPERAND (stmt, 0) = var;
-      bsi_insert_before (&bsi, stmt, BSI_NEW_STMT);
+      GIMPLE_STMT_OPERAND (stmt, 0) = var;
+      bsi_insert_before (&bsi, stmt, BSI_SAME_STMT);
+    }
+
+  *ret_op = var;
+  update_stmt (ret_stmt);
+}
+
+/* Subtract COUNT and FREQUENCY from the basic block and it's
+   outgoing edge.  */
+static void
+decrease_profile (basic_block bb, gcov_type count, int frequency)
+{
+  edge e;
+  bb->count -= count;
+  if (bb->count < 0)
+    bb->count = 0;
+  bb->frequency -= frequency;
+  if (bb->frequency < 0)
+    bb->frequency = 0;
+  if (!single_succ_p (bb))
+    {
+      gcc_assert (!EDGE_COUNT (bb->succs));
+      return;
     }
+  e = single_succ_edge (bb);
+  e->count -= count;
+  if (e->count < 0)
+    e->count = 0;
+}
+
+/* Returns true if argument PARAM of the tail recursive call needs to be copied
+   when the call is eliminated.  */
+
+static bool
+arg_needs_copy_p (tree param)
+{
+  tree def;
 
-  TREE_OPERAND (ret_stmt, 0) = var;
-  modify_stmt (ret_stmt);
+  if (!is_gimple_reg (param) || !var_ann (param))
+    return false;
+               
+  /* Parameters that are only defined but never used need not be copied.  */
+  def = gimple_default_def (cfun, param);
+  if (!def)
+    return false;
+
+  return true;
 }
 
 /* Eliminates tail call described by T.  TMP_VARS is a list of
@@ -640,17 +721,16 @@ adjust_return_value (basic_block bb, tree m, tree a)
 static void
 eliminate_tail_call (struct tailcall *t)
 {
-  tree param, stmt, args, rslt, call;
+  tree param, stmt, rslt, call;
+  tree arg;
+  call_expr_arg_iterator iter;
   basic_block bb, first;
   edge e;
   tree phi;
-  stmt_ann_t ann;
-  v_may_def_optype v_may_defs;
-  unsigned i;
+  block_stmt_iterator bsi;
+  tree orig_stmt;
 
-  stmt = bsi_stmt (t->call_bsi);
-  get_stmt_operands (stmt);
-  ann = stmt_ann (stmt);
+  stmt = orig_stmt = bsi_stmt (t->call_bsi);
   bb = t->call_block;
 
   if (dump_file && (dump_flags & TDF_DETAILS))
@@ -661,84 +741,101 @@ eliminate_tail_call (struct tailcall *t)
       fprintf (dump_file, "\n");
     }
 
-  if (TREE_CODE (stmt) == MODIFY_EXPR)
-    stmt = TREE_OPERAND (stmt, 1);
+  if (TREE_CODE (stmt) == GIMPLE_MODIFY_STMT)
+    stmt = GIMPLE_STMT_OPERAND (stmt, 1);
+
+  first = single_succ (ENTRY_BLOCK_PTR);
+
+  /* Remove the code after call_bsi that will become unreachable.  The
+     possibly unreachable code in other blocks is removed later in
+     cfg cleanup.  */
+  bsi = t->call_bsi;
+  bsi_next (&bsi);
+  while (!bsi_end_p (bsi))
+    {
+      tree t = bsi_stmt (bsi);
+      /* Do not remove the return statement, so that redirect_edge_and_branch
+        sees how the block ends.  */
+      if (TREE_CODE (t) == RETURN_EXPR)
+       break;
+
+      bsi_remove (&bsi, true);
+      release_defs (t);
+    }
 
-  first = ENTRY_BLOCK_PTR->succ->dest;
+  /* Number of executions of function has reduced by the tailcall.  */
+  e = single_succ_edge (t->call_block);
+  decrease_profile (EXIT_BLOCK_PTR, e->count, EDGE_FREQUENCY (e));
+  decrease_profile (ENTRY_BLOCK_PTR, e->count, EDGE_FREQUENCY (e));
+  if (e->dest != EXIT_BLOCK_PTR)
+    decrease_profile (e->dest, e->count, EDGE_FREQUENCY (e));
 
   /* Replace the call by a jump to the start of function.  */
-  e = redirect_edge_and_branch (t->call_block->succ, first);
-  if (!e)
-    abort ();
+  e = redirect_edge_and_branch (single_succ_edge (t->call_block), first);
+  gcc_assert (e);
   PENDING_STMT (e) = NULL_TREE;
 
-  /* Add phi node entries for arguments.  Not every PHI node corresponds to
-     a function argument (there may be PHI nodes for virtual definitions of the
-     eliminated calls), so we search for a PHI corresponding to each argument
-     rather than searching for which argument a PHI node corresponds to.  */
-  
+  /* Add phi node entries for arguments.  The ordering of the phi nodes should
+     be the same as the ordering of the arguments.  */
   for (param = DECL_ARGUMENTS (current_function_decl),
-       args = TREE_OPERAND (stmt, 1);
+        arg = first_call_expr_arg (stmt, &iter),
+        phi = phi_nodes (first);
        param;
-       param = TREE_CHAIN (param),
-       args = TREE_CHAIN (args))
+       param = TREE_CHAIN (param), arg = next_call_expr_arg (&iter))
     {
-      
-      for (phi = phi_nodes (first); phi; phi = PHI_CHAIN (phi))
-       if (param == SSA_NAME_VAR (PHI_RESULT (phi)))
-         break;
-
-      /* The phi node indeed does not have to be there, in case the operand is
-        invariant in the function.  */
-      if (!phi)
+      if (!arg_needs_copy_p (param))
        continue;
+      gcc_assert (param == SSA_NAME_VAR (PHI_RESULT (phi)));
 
-      add_phi_arg (&phi, TREE_VALUE (args), e);
-    }
-
-  /* Add phi nodes for the call clobbered variables.  */
-  v_may_defs = V_MAY_DEF_OPS (ann);
-  for (i = 0; i < NUM_V_MAY_DEFS (v_may_defs); i++)
-    {
-      param = SSA_NAME_VAR (V_MAY_DEF_RESULT (v_may_defs, i));
-      for (phi = phi_nodes (first); phi; phi = PHI_CHAIN (phi))
-       if (param == SSA_NAME_VAR (PHI_RESULT (phi)))
-         break;
-
-      if (!phi)
-       {
-         tree name = var_ann (param)->default_def;
-         tree new_name = make_ssa_name (param, SSA_NAME_DEF_STMT (name));
-
-         var_ann (param)->default_def = new_name;
-         phi = create_phi_node (name, first);
-         SSA_NAME_DEF_STMT (name) = phi;
-         add_phi_arg (&phi, new_name, ENTRY_BLOCK_PTR->succ);
-
-         /* For all calls the same set of variables should be clobbered.  This
-            means that there always should be the appropriate phi node except
-            for the first time we eliminate the call.  */
-         if (first->pred->pred_next->pred_next)
-           abort ();
-       }
-
-      add_phi_arg (&phi, V_MAY_DEF_OP (v_may_defs, i), e);
+      add_phi_arg (phi, arg, e);
+      phi = PHI_CHAIN (phi);
     }
 
   /* Update the values of accumulators.  */
   adjust_accumulator_values (t->call_bsi, t->mult, t->add, e);
 
   call = bsi_stmt (t->call_bsi);
-  if (TREE_CODE (call) == MODIFY_EXPR)
+  if (TREE_CODE (call) == GIMPLE_MODIFY_STMT)
     {
-      rslt = TREE_OPERAND (call, 0);
+      rslt = GIMPLE_STMT_OPERAND (call, 0);
 
       /* Result of the call will no longer be defined.  So adjust the
         SSA_NAME_DEF_STMT accordingly.  */
       SSA_NAME_DEF_STMT (rslt) = build_empty_stmt ();
     }
 
-  bsi_remove (&t->call_bsi);
+  bsi_remove (&t->call_bsi, true);
+  release_defs (call);
+}
+
+/* Add phi nodes for the virtual operands defined in the function to the
+   header of the loop created by tail recursion elimination.
+
+   Originally, we used to add phi nodes only for call clobbered variables,
+   as the value of the non-call clobbered ones obviously cannot be used
+   or changed within the recursive call.  However, the local variables
+   from multiple calls now share the same location, so the virtual ssa form
+   requires us to say that the location dies on further iterations of the loop,
+   which requires adding phi nodes.
+*/
+static void
+add_virtual_phis (void)
+{
+  referenced_var_iterator rvi;
+  tree var;
+
+  /* The problematic part is that there is no way how to know what
+     to put into phi nodes (there in fact does not have to be such
+     ssa name available).  A solution would be to have an artificial
+     use/kill for all virtual operands in EXIT node.  Unless we have
+     this, we cannot do much better than to rebuild the ssa form for
+     possibly affected virtual ssa names from scratch.  */
+
+  FOR_EACH_REFERENCED_VAR (var, rvi)
+    {
+      if (!is_gimple_reg (var) && gimple_default_def (cfun, var) != NULL_TREE)
+       mark_sym_for_renaming (var);
+    }
 }
 
 /* Optimizes the tailcall described by T.  If OPT_TAILCALLS is true, also
@@ -757,10 +854,7 @@ optimize_tail_call (struct tailcall *t, bool opt_tailcalls)
     {
       tree stmt = bsi_stmt (t->call_bsi);
 
-      if (TREE_CODE (stmt) == MODIFY_EXPR)
-       stmt = TREE_OPERAND (stmt, 1);
-      if (TREE_CODE (stmt) != CALL_EXPR)
-       abort ();
+      stmt = get_call_expr_in (stmt);
       CALL_EXPR_TAILCALL (stmt) = 1;
       if (dump_file && (dump_flags & TDF_DETAILS))
         {
@@ -776,22 +870,23 @@ optimize_tail_call (struct tailcall *t, bool opt_tailcalls)
 /* Optimizes tail calls in the function, turning the tail recursion
    into iteration.  */
 
-static void
+static unsigned int
 tree_optimize_tail_calls_1 (bool opt_tailcalls)
 {
   edge e;
   bool phis_constructed = false;
   struct tailcall *tailcalls = NULL, *act, *next;
   bool changed = false;
-  basic_block first = ENTRY_BLOCK_PTR->succ->dest;
+  basic_block first = single_succ (ENTRY_BLOCK_PTR);
   tree stmt, param, ret_type, tmp, phi;
+  edge_iterator ei;
 
   if (!suitable_for_tail_opt_p ())
-    return;
+    return 0;
   if (opt_tailcalls)
     opt_tailcalls = suitable_for_tail_call_opt_p ();
 
-  for (e = EXIT_BLOCK_PTR->pred; e; e = e->pred_next)
+  FOR_EACH_EDGE (e, ei, EXIT_BLOCK_PTR->preds)
     {
       /* Only traverse the normal exits, i.e. those that end with return
         statement.  */
@@ -812,28 +907,24 @@ tree_optimize_tail_calls_1 (bool opt_tailcalls)
       if (!phis_constructed)
        {
          /* Ensure that there is only one predecessor of the block.  */
-         if (first->pred->pred_next)
-           first = split_edge (ENTRY_BLOCK_PTR->succ);
+         if (!single_pred_p (first))
+           first = split_edge (single_succ_edge (ENTRY_BLOCK_PTR));
 
          /* Copy the args if needed.  */
          for (param = DECL_ARGUMENTS (current_function_decl);
               param;
               param = TREE_CHAIN (param))
-           if (var_ann (param)
-               /* Also parameters that are only defined but never used need not
-                  be copied.  */
-               && (var_ann (param)->default_def
-                   && TREE_CODE (var_ann (param)->default_def) == SSA_NAME))
-           {
-             tree name = var_ann (param)->default_def;
-             tree new_name = make_ssa_name (param, SSA_NAME_DEF_STMT (name));
-             tree phi;
-
-             var_ann (param)->default_def = new_name;
-             phi = create_phi_node (name, first);
-             SSA_NAME_DEF_STMT (name) = phi;
-             add_phi_arg (&phi, new_name, first->pred);
-           }
+           if (arg_needs_copy_p (param))
+             {
+               tree name = gimple_default_def (cfun, param);
+               tree new_name = make_ssa_name (param, SSA_NAME_DEF_STMT (name));
+               tree phi;
+
+               set_default_def (param, new_name);
+               phi = create_phi_node (name, first);
+               SSA_NAME_DEF_STMT (name) = phi;
+               add_phi_arg (phi, new_name, single_pred_edge (first));
+             }
          phis_constructed = true;
        }
 
@@ -842,11 +933,14 @@ tree_optimize_tail_calls_1 (bool opt_tailcalls)
          ret_type = TREE_TYPE (DECL_RESULT (current_function_decl));
 
          tmp = create_tmp_var (ret_type, "add_acc");
-         add_referenced_tmp_var (tmp);
+         add_referenced_var (tmp);
 
          phi = create_phi_node (tmp, first);
-         add_phi_arg (&phi, fold_convert (ret_type, integer_zero_node),
-                      first->pred);
+         add_phi_arg (phi,
+                      /* RET_TYPE can be a float when -ffast-maths is
+                         enabled.  */
+                      fold_convert (ret_type, integer_zero_node),
+                      single_pred_edge (first));
          a_acc = PHI_RESULT (phi);
        }
 
@@ -855,15 +949,26 @@ tree_optimize_tail_calls_1 (bool opt_tailcalls)
          ret_type = TREE_TYPE (DECL_RESULT (current_function_decl));
 
          tmp = create_tmp_var (ret_type, "mult_acc");
-         add_referenced_tmp_var (tmp);
+         add_referenced_var (tmp);
 
          phi = create_phi_node (tmp, first);
-         add_phi_arg (&phi, fold_convert (ret_type, integer_one_node),
-                      first->pred);
+         add_phi_arg (phi,
+                      /* RET_TYPE can be a float when -ffast-maths is
+                         enabled.  */
+                      fold_convert (ret_type, integer_one_node),
+                      single_pred_edge (first));
          m_acc = PHI_RESULT (phi);
        }
     }
 
+
+  if (phis_constructed)
+    {
+      /* Reverse the order of the phi nodes, so that it matches the order
+        of operands of the function, as assumed by eliminate_tail_call.  */
+      set_phi_nodes (first, phi_reverse (phi_nodes (first)));
+    }
+
   for (; tailcalls; tailcalls = next)
     {
       next = tailcalls->next;
@@ -874,7 +979,7 @@ tree_optimize_tail_calls_1 (bool opt_tailcalls)
   if (a_acc || m_acc)
     {
       /* Modify the remaining return statements.  */
-      for (e = EXIT_BLOCK_PTR->pred; e; e = e->pred_next)
+      FOR_EACH_EDGE (e, ei, EXIT_BLOCK_PTR->preds)
        {
          stmt = last_stmt (e->src);
 
@@ -885,34 +990,37 @@ tree_optimize_tail_calls_1 (bool opt_tailcalls)
     }
 
   if (changed)
-    {
-      free_dominance_info (CDI_DOMINATORS);
-      cleanup_tree_cfg ();
-    }
+    free_dominance_info (CDI_DOMINATORS);
+
+  if (phis_constructed)
+    add_virtual_phis ();
+  if (changed)
+    return TODO_cleanup_cfg | TODO_update_ssa_only_virtuals;
+  return 0;
 }
 
-static void
+static unsigned int
 execute_tail_recursion (void)
 {
-  tree_optimize_tail_calls_1 (false);
+  return tree_optimize_tail_calls_1 (false);
 }
 
 static bool
 gate_tail_calls (void)
 {
-  return flag_optimize_sibling_calls != 0;
+  return flag_optimize_sibling_calls != 0 && dbg_cnt (tail_call);
 }
 
-static void
+static unsigned int
 execute_tail_calls (void)
 {
-  tree_optimize_tail_calls_1 (true);
+  return tree_optimize_tail_calls_1 (true);
 }
 
 struct tree_opt_pass pass_tail_recursion = 
 {
   "tailr",                             /* name */
-  NULL,                                        /* gate */
+  gate_tail_calls,                     /* gate */
   execute_tail_recursion,              /* execute */
   NULL,                                        /* sub */
   NULL,                                        /* next */
@@ -922,7 +1030,8 @@ struct tree_opt_pass pass_tail_recursion =
   0,                                   /* properties_provided */
   0,                                   /* properties_destroyed */
   0,                                   /* todo_flags_start */
-  TODO_dump_func | TODO_verify_ssa     /* todo_flags_finish */
+  TODO_dump_func | TODO_verify_ssa,    /* todo_flags_finish */
+  0                                    /* letter */
 };
 
 struct tree_opt_pass pass_tail_calls = 
@@ -934,9 +1043,10 @@ struct tree_opt_pass pass_tail_calls =
   NULL,                                        /* next */
   0,                                   /* static_pass_number */
   0,                                   /* tv_id */
-  PROP_cfg | PROP_ssa,                 /* properties_required */
+  PROP_cfg | PROP_ssa | PROP_alias,    /* properties_required */
   0,                                   /* properties_provided */
   0,                                   /* properties_destroyed */
   0,                                   /* todo_flags_start */
-  TODO_dump_func | TODO_verify_ssa     /* todo_flags_finish */
+  TODO_dump_func | TODO_verify_ssa,    /* todo_flags_finish */
+  0                                    /* letter */
 };