OSDN Git Service

Backport from git Libtool:
[pf3gnuchains/gcc-fork.git] / gcc / tree-nrv.c
index 1768b39..40e7508 100644 (file)
@@ -1,11 +1,11 @@
 /* Language independent return value optimizations
-   Copyright (C) 2004 Free Software Foundation, Inc.
+   Copyright (C) 2004, 2005, 2007, 2008 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"
@@ -75,16 +74,14 @@ static tree finalize_nrv_r (tree *, int *, void *);
 static tree
 finalize_nrv_r (tree *tp, int *walk_subtrees, void *data)
 {
-  struct nrv_data *dp = (struct nrv_data *)data;
+  struct walk_stmt_info *wi = (struct walk_stmt_info *) data;
+  struct nrv_data *dp = (struct nrv_data *) wi->info;
 
   /* No need to walk into types.  */
   if (TYPE_P (*tp))
     *walk_subtrees = 0;
-  /* If this is a RETURN_EXPR, then set the expression being returned
-     to RESULT.  */
-  else if (TREE_CODE (*tp) == RETURN_EXPR)
-    TREE_OPERAND (*tp, 0) = dp->result;
-  /* Replace all occurrences of VAR with RESULT.  */
+
+  /* Otherwise replace all occurrences of VAR with RESULT.  */
   else if (*tp == dp->var)
     *tp = dp->result;
 
@@ -104,67 +101,91 @@ finalize_nrv_r (tree *tp, int *walk_subtrees, void *data)
    then we could either have the languages register the optimization or
    we could change the gating function to check the current language.  */
    
-static void
+static unsigned int
 tree_nrv (void)
 {
   tree result = DECL_RESULT (current_function_decl);
   tree result_type = TREE_TYPE (result);
   tree found = NULL;
   basic_block bb;
+  gimple_stmt_iterator gsi;
   struct nrv_data data;
 
   /* If this function does not return an aggregate type in memory, then
      there is nothing to do.  */
   if (!aggregate_value_p (result, current_function_decl))
-    return;
+    return 0;
+
+  /* If a GIMPLE type is returned in memory, finalize_nrv_r might create
+     non-GIMPLE.  */
+  if (is_gimple_reg_type (result_type))
+    return 0;
 
-  /* Look through each block for suitable return expressions.   RETURN_EXPRs
-     end basic blocks, so we only have to look at the last statement in
-     each block.  That makes this very fast.  */
+  /* Look through each block for assignments to the RESULT_DECL.  */
   FOR_EACH_BB (bb)
     {
-      tree stmt = last_stmt (bb);
-
-      if (stmt && TREE_CODE (stmt) == RETURN_EXPR)
+      for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi); gsi_next (&gsi))
        {
-         tree ret_expr = TREE_OPERAND (stmt, 0);
-
-         /* This probably should not happen, but just to be safe do
-            not perform NRV optimizations if only some of the return
-            statement return a value.  */
-         if (!ret_expr
-             || TREE_CODE (ret_expr) != MODIFY_EXPR
-             || TREE_CODE (TREE_OPERAND (ret_expr, 0)) != RESULT_DECL)
-           return;
-
-         /* Now verify that this return statement uses the same value
-            as any previously encountered return statement.  */
-         if (found != NULL)
+         gimple stmt = gsi_stmt (gsi);
+         tree ret_val;
+
+         if (gimple_code (stmt) == GIMPLE_RETURN)
            {
-             /* If we found a return statement using a different variable
-                than previous return statements, then we can not perform
-                NRV optimizations.  */
-             if (found != TREE_OPERAND (ret_expr, 1))
-               return;
+             /* In a function with an aggregate return value, the
+                gimplifier has changed all non-empty RETURN_EXPRs to
+                return the RESULT_DECL.  */
+             ret_val = gimple_return_retval (stmt);
+             if (ret_val)
+               gcc_assert (ret_val == result);
+           }
+         else if (is_gimple_assign (stmt)
+                  && gimple_assign_lhs (stmt) == result)
+           {
+              tree rhs;
+
+             if (!gimple_assign_copy_p (stmt))
+               return 0;
+
+             rhs = gimple_assign_rhs1 (stmt);
+
+             /* Now verify that this return statement uses the same value
+                as any previously encountered return statement.  */
+             if (found != NULL)
+               {
+                 /* If we found a return statement using a different variable
+                    than previous return statements, then we can not perform
+                    NRV optimizations.  */
+                 if (found != rhs)
+                   return 0;
+               }
+             else
+               found = rhs;
+
+             /* The returned value must be a local automatic variable of the
+                same type and alignment as the function's result.  */
+             if (TREE_CODE (found) != VAR_DECL
+                 || TREE_THIS_VOLATILE (found)
+                 || DECL_CONTEXT (found) != current_function_decl
+                 || TREE_STATIC (found)
+                 || TREE_ADDRESSABLE (found)
+                 || DECL_ALIGN (found) > DECL_ALIGN (result)
+                 || !useless_type_conversion_p (result_type,
+                                               TREE_TYPE (found)))
+               return 0;
+           }
+         else if (is_gimple_assign (stmt))
+           {
+             tree addr = get_base_address (gimple_assign_lhs (stmt));
+              /* If there's any MODIFY of component of RESULT, 
+                 then bail out.  */
+             if (addr && addr == result)
+               return 0;
            }
-         else
-           found = TREE_OPERAND (ret_expr, 1);
-
-         /* The returned value must be a local automatic variable of the
-            same type and alignment as the function's result.  */
-         if (TREE_CODE (found) != VAR_DECL
-             || DECL_CONTEXT (found) != current_function_decl
-             || TREE_STATIC (found)
-             || TREE_ADDRESSABLE (found)
-             || DECL_ALIGN (found) > DECL_ALIGN (result)
-              || !lang_hooks.types_compatible_p (TREE_TYPE (found), 
-                result_type))
-           return;
        }
     }
 
   if (!found)
-    return;
+    return 0;
 
   /* If dumping details, then note once and only the NRV replacement.  */
   if (dump_file && (dump_flags & TDF_DETAILS))
@@ -190,20 +211,42 @@ tree_nrv (void)
   data.result = result;
   FOR_EACH_BB (bb)
     {
-      block_stmt_iterator bsi;
-
-      for (bsi = bsi_start (bb); !bsi_end_p (bsi); bsi_next (&bsi))
-       walk_tree (bsi_stmt_ptr (bsi), finalize_nrv_r, &data, 0);
+      for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi); )
+       {
+         gimple stmt = gsi_stmt (gsi);
+         /* If this is a copy from VAR to RESULT, remove it.  */
+         if (gimple_assign_copy_p (stmt)
+             && gimple_assign_lhs (stmt) == result
+             && gimple_assign_rhs1 (stmt) == found)
+           gsi_remove (&gsi, true);
+         else
+           {
+             struct walk_stmt_info wi;
+             memset (&wi, 0, sizeof (wi));
+             wi.info = &data;
+             walk_gimple_op (stmt, finalize_nrv_r, &wi);
+             gsi_next (&gsi);
+           }
+       }
     }
 
   /* FOUND is no longer used.  Ensure it gets removed.  */
   var_ann (found)->used = 0;
+  return 0;
 }
 
-struct tree_opt_pass pass_nrv = 
+static bool
+gate_pass_return_slot (void)
 {
+  return optimize > 0;
+}
+
+struct gimple_opt_pass pass_nrv = 
+{
+ {
+  GIMPLE_PASS,
   "nrv",                               /* name */
-  NULL,                                        /* gate */
+  gate_pass_return_slot,               /* gate */
   tree_nrv,                            /* execute */
   NULL,                                        /* sub */
   NULL,                                        /* next */
@@ -213,5 +256,93 @@ struct tree_opt_pass pass_nrv =
   0,                                   /* properties_provided */
   0,                                   /* properties_destroyed */
   0,                                   /* todo_flags_start */
-  TODO_dump_func | TODO_ggc_collect    /* todo_flags_finish */
+  TODO_dump_func | TODO_ggc_collect                    /* todo_flags_finish */
+ }
+};
+
+/* Determine (pessimistically) whether DEST is available for NRV
+   optimization, where DEST is expected to be the LHS of a modify
+   expression where the RHS is a function returning an aggregate.
+
+   We search for a base VAR_DECL and look to see if it is call clobbered.
+   Note that we could do better, for example, by
+   attempting to doing points-to analysis on INDIRECT_REFs.  */
+
+static bool
+dest_safe_for_nrv_p (tree dest)
+{
+  while (handled_component_p (dest))
+    dest = TREE_OPERAND (dest, 0);
+
+  if (! SSA_VAR_P (dest))
+    return false;
+
+  if (TREE_CODE (dest) == SSA_NAME)
+    dest = SSA_NAME_VAR (dest);
+
+  if (is_call_used (dest))
+    return false;
+
+  return true;
+}
+
+/* Walk through the function looking for GIMPLE_ASSIGNs with calls that
+   return in memory on the RHS.  For each of these, determine whether it is
+   safe to pass the address of the LHS as the return slot, and mark the
+   call appropriately if so.
+
+   The NRV shares the return slot with a local variable in the callee; this
+   optimization shares the return slot with the target of the call within
+   the caller.  If the NRV is performed (which we can't know in general),
+   this optimization is safe if the address of the target has not
+   escaped prior to the call.  If it has, modifications to the local
+   variable will produce visible changes elsewhere, as in PR c++/19317.  */
+
+static unsigned int
+execute_return_slot_opt (void)
+{
+  basic_block bb;
+
+  FOR_EACH_BB (bb)
+    {
+      gimple_stmt_iterator gsi;
+      for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi); gsi_next (&gsi))
+       {
+         gimple stmt = gsi_stmt (gsi);
+         bool slot_opt_p;
+
+         if (is_gimple_call (stmt)
+             && gimple_call_lhs (stmt)
+             && !gimple_call_return_slot_opt_p (stmt)
+             && aggregate_value_p (TREE_TYPE (gimple_call_lhs (stmt)),
+                                   gimple_call_fndecl (stmt))
+            )
+           {
+             /* Check if the location being assigned to is
+                call-clobbered.  */
+             slot_opt_p = dest_safe_for_nrv_p (gimple_call_lhs (stmt));
+             gimple_call_set_return_slot_opt (stmt, slot_opt_p);
+           }
+       }
+    }
+  return 0;
+}
+
+struct gimple_opt_pass pass_return_slot = 
+{
+ {
+  GIMPLE_PASS,
+  "retslot",                           /* name */
+  NULL,                                        /* gate */
+  execute_return_slot_opt,             /* execute */
+  NULL,                                        /* sub */
+  NULL,                                        /* next */
+  0,                                   /* static_pass_number */
+  0,                                   /* tv_id */
+  PROP_ssa | PROP_alias,               /* properties_required */
+  0,                                   /* properties_provided */
+  0,                                   /* properties_destroyed */
+  0,                                   /* todo_flags_start */
+  0                                    /* todo_flags_finish */
+ }
 };