X-Git-Url: http://git.sourceforge.jp/view?a=blobdiff_plain;f=gcc%2Ftree-tailcall.c;h=02e1113f4832a0bd4991e81698be6755047c59e4;hb=fafb561af4a422ee8d865bc27c8b15e75e66f970;hp=d651c3b509b6d8c52d9ec69f3fe93c8f1bf70bc7;hpb=a7a4626828090600459358ca745c4482cf9551a1;p=pf3gnuchains%2Fgcc-fork.git diff --git a/gcc/tree-tailcall.c b/gcc/tree-tailcall.c index d651c3b509b..02e1113f483 100644 --- a/gcc/tree-tailcall.c +++ b/gcc/tree-tailcall.c @@ -1,5 +1,5 @@ /* Tail call optimization on trees. - Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 + Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 Free Software Foundation, Inc. This file is part of GCC. @@ -28,12 +28,14 @@ along with GCC; see the file COPYING3. If not see #include "function.h" #include "tree-flow.h" #include "tree-dump.h" -#include "diagnostic.h" +#include "gimple-pretty-print.h" #include "except.h" #include "tree-pass.h" #include "flags.h" #include "langhooks.h" #include "dbgcnt.h" +#include "target.h" +#include "common/common-target.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 @@ -151,7 +153,8 @@ suitable_for_tail_call_opt_p (void) /* If we are using sjlj exceptions, we may need to add a call to _Unwind_SjLj_Unregister at exit of the function. Which means that we cannot do any sibcall transformations. */ - if (USING_SJLJ_EXCEPTIONS && current_function_has_exception_handlers ()) + if (targetm_common.except_unwind_info (&global_options) == UI_SJLJ + && current_function_has_exception_handlers ()) return false; /* Any function that calls setjmp might have longjmp called from @@ -164,7 +167,7 @@ suitable_for_tail_call_opt_p (void) 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)) + param = DECL_CHAIN (param)) if (TREE_ADDRESSABLE (param)) return false; @@ -252,7 +255,7 @@ static bool process_assignment (gimple stmt, gimple_stmt_iterator call, tree *m, tree *a, tree *ass_var) { - tree op0, op1, non_ass_var; + tree op0, op1 = NULL_TREE, non_ass_var = NULL_TREE; tree dest = gimple_assign_lhs (stmt); enum tree_code code = gimple_assign_rhs_code (stmt); enum gimple_rhs_class rhs_class = get_gimple_rhs_class (code); @@ -278,8 +281,20 @@ process_assignment (gimple stmt, gimple_stmt_iterator call, tree *m, return true; } - if (rhs_class != GIMPLE_BINARY_RHS) - return false; + switch (rhs_class) + { + case GIMPLE_BINARY_RHS: + op1 = gimple_assign_rhs2 (stmt); + + /* Fall through. */ + + case GIMPLE_UNARY_RHS: + op0 = gimple_assign_rhs1 (stmt); + break; + + default: + return false; + } /* Accumulator optimizations will reverse the order of operations. We can only do that for floating-point types if we're assuming @@ -288,20 +303,9 @@ process_assignment (gimple stmt, gimple_stmt_iterator call, tree *m, if (FLOAT_TYPE_P (TREE_TYPE (DECL_RESULT (current_function_decl)))) return false; - /* We only handle the code like - - x = call (); - y = m * x; - z = y + a; - return z; - - TODO -- Extend it for cases where the linear transformation of the output - is expressed in a more complicated way. */ - - op0 = gimple_assign_rhs1 (stmt); - op1 = gimple_assign_rhs2 (stmt); - - if (op0 == *ass_var + if (rhs_class == GIMPLE_UNARY_RHS) + ; + else if (op0 == *ass_var && (non_ass_var = independent_of_stmt_p (op1, stmt, call))) ; else if (op1 == *ass_var @@ -322,8 +326,32 @@ process_assignment (gimple stmt, gimple_stmt_iterator call, tree *m, *ass_var = dest; return true; - /* TODO -- Handle other codes (NEGATE_EXPR, MINUS_EXPR, - POINTER_PLUS_EXPR). */ + case NEGATE_EXPR: + if (FLOAT_TYPE_P (TREE_TYPE (op0))) + *m = build_real (TREE_TYPE (op0), dconstm1); + else + *m = build_int_cst (TREE_TYPE (op0), -1); + + *ass_var = dest; + return true; + + case MINUS_EXPR: + if (*ass_var == op0) + *a = fold_build1 (NEGATE_EXPR, TREE_TYPE (non_ass_var), non_ass_var); + else + { + if (FLOAT_TYPE_P (TREE_TYPE (non_ass_var))) + *m = build_real (TREE_TYPE (non_ass_var), dconstm1); + else + *m = build_int_cst (TREE_TYPE (non_ass_var), -1); + + *a = fold_build1 (NEGATE_EXPR, TREE_TYPE (non_ass_var), non_ass_var); + } + + *ass_var = dest; + return true; + + /* TODO -- Handle POINTER_PLUS_EXPR. */ default: return false; @@ -372,8 +400,11 @@ find_tail_calls (basic_block bb, struct tailcall **ret) { stmt = gsi_stmt (gsi); - /* Ignore labels. */ - if (gimple_code (stmt) == GIMPLE_LABEL || is_gimple_debug (stmt)) + /* Ignore labels, returns, clobbers and debug stmts. */ + if (gimple_code (stmt) == GIMPLE_LABEL + || gimple_code (stmt) == GIMPLE_RETURN + || gimple_clobber_p (stmt) + || is_gimple_debug (stmt)) continue; /* Check for a call. */ @@ -423,7 +454,7 @@ find_tail_calls (basic_block bb, struct tailcall **ret) for (param = DECL_ARGUMENTS (func), idx = 0; param && idx < gimple_call_num_args (call); - param = TREE_CHAIN (param), idx ++) + param = DECL_CHAIN (param), idx ++) { arg = gimple_call_arg (call, idx); if (param != arg) @@ -454,7 +485,7 @@ find_tail_calls (basic_block bb, struct tailcall **ret) /* Make sure the tail invocation of this function does not refer to local variables. */ - FOR_EACH_REFERENCED_VAR (var, rvi) + FOR_EACH_REFERENCED_VAR (cfun, var, rvi) { if (TREE_CODE (var) != PARM_DECL && auto_var_in_fn_p (var, cfun->decl) @@ -493,6 +524,9 @@ find_tail_calls (basic_block bb, struct tailcall **ret) if (gimple_code (stmt) == GIMPLE_RETURN) break; + if (gimple_clobber_p (stmt)) + continue; + if (is_gimple_debug (stmt)) continue; @@ -505,20 +539,22 @@ find_tail_calls (basic_block bb, struct tailcall **ret) if (tmp_a) { + tree type = TREE_TYPE (tmp_a); if (a) - a = fold_build2 (PLUS_EXPR, TREE_TYPE (tmp_a), a, tmp_a); + a = fold_build2 (PLUS_EXPR, type, fold_convert (type, a), tmp_a); else a = tmp_a; } if (tmp_m) { + tree type = TREE_TYPE (tmp_m); if (m) - m = fold_build2 (MULT_EXPR, TREE_TYPE (tmp_m), m, tmp_m); + m = fold_build2 (MULT_EXPR, type, fold_convert (type, m), tmp_m); else m = tmp_m; if (a) - a = fold_build2 (MULT_EXPR, TREE_TYPE (tmp_m), a, tmp_m); + a = fold_build2 (MULT_EXPR, type, fold_convert (type, a), tmp_m); } } @@ -808,7 +844,7 @@ eliminate_tail_call (struct tailcall *t) for (param = DECL_ARGUMENTS (current_function_decl), idx = 0, gsi = gsi_start_phis (first); param; - param = TREE_CHAIN (param), idx++) + param = DECL_CHAIN (param), idx++) { if (!arg_needs_copy_p (param)) continue; @@ -860,7 +896,7 @@ add_virtual_phis (void) 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) + FOR_EACH_REFERENCED_VAR (cfun, var, rvi) { if (!is_gimple_reg (var) && gimple_default_def (cfun, var) != NULL_TREE) mark_sym_for_renaming (var); @@ -965,7 +1001,7 @@ tree_optimize_tail_calls_1 (bool opt_tailcalls) /* Copy the args if needed. */ for (param = DECL_ARGUMENTS (current_function_decl); param; - param = TREE_CHAIN (param)) + param = DECL_CHAIN (param)) if (arg_needs_copy_p (param)) { tree name = gimple_default_def (cfun, param); @@ -990,6 +1026,14 @@ tree_optimize_tail_calls_1 (bool opt_tailcalls) integer_one_node); } + if (a_acc || m_acc) + { + /* When the tail call elimination using accumulators is performed, + statements adding the accumulated value are inserted at all exits. + This turns all other tail calls to non-tail ones. */ + opt_tailcalls = false; + } + for (; tailcalls; tailcalls = next) { next = tailcalls->next; @@ -1053,7 +1097,7 @@ struct gimple_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_verify_ssa /* todo_flags_finish */ } }; @@ -1072,6 +1116,6 @@ struct gimple_opt_pass pass_tail_calls = 0, /* properties_provided */ 0, /* properties_destroyed */ 0, /* todo_flags_start */ - TODO_dump_func | TODO_verify_ssa /* todo_flags_finish */ + TODO_verify_ssa /* todo_flags_finish */ } };