X-Git-Url: http://git.sourceforge.jp/view?p=pf3gnuchains%2Fgcc-fork.git;a=blobdiff_plain;f=gcc%2Ftree-nrv.c;h=40e7508f613385553d8f70ba29884627da32fccc;hp=1768b3909b9092e621dbe12ecd88fe24ff8c0e97;hb=9af964d4a49319aa1148819f21c2a352c2ea44e0;hpb=365db11eea46f39f4e46a11a6509d212e41c2307 diff --git a/gcc/tree-nrv.c b/gcc/tree-nrv.c index 1768b3909b9..40e7508f613 100644 --- a/gcc/tree-nrv.c +++ b/gcc/tree-nrv.c @@ -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 +. */ #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 */ + } };