diff options
author | Jeffrey A Law <law@cygnus.com> | 2000-03-17 14:40:45 -0800 |
---|---|---|
committer | Richard Henderson <rth@gcc.gnu.org> | 2000-03-17 14:40:45 -0800 |
commit | 0a1c58a25ab5df1a3e4596024774641ebae8be2a (patch) | |
tree | 720b4c50b7ea074422601de35cfc7e48ed679e49 /gcc/calls.c | |
parent | f1fd8077fd1260362aa134deefc197948da270f8 (diff) | |
download | gcc-0a1c58a25ab5df1a3e4596024774641ebae8be2a.zip gcc-0a1c58a25ab5df1a3e4596024774641ebae8be2a.tar.gz gcc-0a1c58a25ab5df1a3e4596024774641ebae8be2a.tar.bz2 |
Sibling call optimizations.
Co-Authored-By: Richard Henderson <rth@cygnus.com>
From-SVN: r32612
Diffstat (limited to 'gcc/calls.c')
-rw-r--r-- | gcc/calls.c | 1499 |
1 files changed, 910 insertions, 589 deletions
diff --git a/gcc/calls.c b/gcc/calls.c index 6a4c148..21feeed 100644 --- a/gcc/calls.c +++ b/gcc/calls.c @@ -32,6 +32,10 @@ Boston, MA 02111-1307, USA. */ #include "output.h" #include "tm_p.h" +#if !defined FUNCTION_OK_FOR_SIBCALL +#define FUNCTION_OK_FOR_SIBCALL(DECL) 1 +#endif + #if !defined PREFERRED_STACK_BOUNDARY && defined STACK_BOUNDARY #define PREFERRED_STACK_BOUNDARY STACK_BOUNDARY #endif @@ -132,9 +136,13 @@ int stack_arg_under_construction; static int calls_function PARAMS ((tree, int)); static int calls_function_1 PARAMS ((tree, int)); + +#define ECF_IS_CONST 1 +#define ECF_NOTHROW 2 +#define ECF_SIBCALL 4 static void emit_call_1 PARAMS ((rtx, tree, tree, HOST_WIDE_INT, HOST_WIDE_INT, HOST_WIDE_INT, rtx, - rtx, int, rtx, int, int)); + rtx, int, rtx, int)); static void precompute_register_parameters PARAMS ((int, struct arg_data *, int *)); @@ -384,7 +392,7 @@ prepare_call_address (funexp, fndecl, call_fusage, reg_parm_seen) static void emit_call_1 (funexp, fndecl, funtype, stack_size, rounded_stack_size, struct_value_size, next_arg_reg, valreg, old_inhibit_defer_pop, - call_fusage, is_const, nothrow) + call_fusage, ecf_flags) rtx funexp; tree fndecl ATTRIBUTE_UNUSED; tree funtype ATTRIBUTE_UNUSED; @@ -395,7 +403,7 @@ emit_call_1 (funexp, fndecl, funtype, stack_size, rounded_stack_size, rtx valreg; int old_inhibit_defer_pop; rtx call_fusage; - int is_const, nothrow; + int ecf_flags; { rtx rounded_stack_size_rtx = GEN_INT (rounded_stack_size); #if defined (HAVE_call) && defined (HAVE_call_value) @@ -414,6 +422,33 @@ emit_call_1 (funexp, fndecl, funtype, stack_size, rounded_stack_size, funexp = memory_address (FUNCTION_MODE, funexp); #ifndef ACCUMULATE_OUTGOING_ARGS +#if defined (HAVE_sibcall_pop) && defined (HAVE_sibcall_value_pop) + if ((ecf_flags & ECF_SIBCALL) + && HAVE_sibcall_pop && HAVE_sibcall_value_pop + && (RETURN_POPS_ARGS (fndecl, funtype, stack_size) > 0 + || stack_size == 0)) + { + rtx n_pop = GEN_INT (RETURN_POPS_ARGS (fndecl, funtype, stack_size)); + rtx pat; + + /* If this subroutine pops its own args, record that in the call insn + if possible, for the sake of frame pointer elimination. */ + + if (valreg) + pat = gen_sibcall_value_pop (valreg, + gen_rtx_MEM (FUNCTION_MODE, funexp), + rounded_stack_size_rtx, next_arg_reg, + n_pop); + else + pat = gen_sibcall_pop (gen_rtx_MEM (FUNCTION_MODE, funexp), + rounded_stack_size_rtx, next_arg_reg, n_pop); + + emit_call_insn (pat); + already_popped = 1; + } + else +#endif + #if defined (HAVE_call_pop) && defined (HAVE_call_value_pop) /* If the target has "call" or "call_value" insns, then prefer them if no arguments are actually popped. If the target does not have @@ -447,6 +482,23 @@ emit_call_1 (funexp, fndecl, funtype, stack_size, rounded_stack_size, #endif #endif +#if defined (HAVE_sibcall) && defined (HAVE_sibcall_value) + if ((ecf_flags & ECF_SIBCALL) + && HAVE_sibcall && HAVE_sibcall_value) + { + if (valreg) + emit_call_insn (gen_sibcall_value (valreg, + gen_rtx_MEM (FUNCTION_MODE, funexp), + rounded_stack_size_rtx, + next_arg_reg, NULL_RTX)); + else + emit_call_insn (gen_sibcall (gen_rtx_MEM (FUNCTION_MODE, funexp), + rounded_stack_size_rtx, next_arg_reg, + struct_value_size_rtx)); + } + else +#endif + #if defined (HAVE_call) && defined (HAVE_call_value) if (HAVE_call && HAVE_call_value) { @@ -489,15 +541,17 @@ emit_call_1 (funexp, fndecl, funtype, stack_size, rounded_stack_size, CALL_INSN_FUNCTION_USAGE (call_insn) = call_fusage; /* If this is a const call, then set the insn's unchanging bit. */ - if (is_const) + if (ecf_flags & ECF_IS_CONST) CONST_CALL_P (call_insn) = 1; /* If this call can't throw, attach a REG_EH_REGION reg note to that effect. */ - if (nothrow) + if (ecf_flags & ECF_NOTHROW) REG_NOTES (call_insn) = gen_rtx_EXPR_LIST (REG_EH_REGION, const0_rtx, REG_NOTES (call_insn)); + SIBLING_CALL_P (call_insn) = ((ecf_flags & ECF_SIBCALL) != 0); + /* Restore this now, so that we do defer pops for this call's args if the context of the call as a whole permits. */ inhibit_defer_pop = old_inhibit_defer_pop; @@ -527,7 +581,8 @@ emit_call_1 (funexp, fndecl, funtype, stack_size, rounded_stack_size, if (rounded_stack_size != 0) { - if (flag_defer_pop && inhibit_defer_pop == 0 && !is_const) + if (flag_defer_pop && inhibit_defer_pop == 0 + && !(ecf_flags & ECF_IS_CONST)) pending_stack_adjust += rounded_stack_size; else adjust_stack (rounded_stack_size_rtx); @@ -1227,6 +1282,8 @@ compute_argument_block_size (reg_parm_stack_space, args_size, { #ifdef PREFERRED_STACK_BOUNDARY preferred_stack_boundary /= BITS_PER_UNIT; + if (preferred_stack_boundary < 1) + preferred_stack_boundary = 1; args_size->constant = (((args_size->constant + arg_space_so_far + pending_stack_adjust @@ -1602,17 +1659,31 @@ expand_call (exp, target, ignore) rtx target; int ignore; { + /* Nonzero if we are currently expanding a call. */ + static int currently_expanding_call = 0; + /* List of actual parameters. */ tree actparms = TREE_OPERAND (exp, 1); /* RTX for the function to be called. */ rtx funexp; + /* Sequence of insns to perform a tail recursive "call". */ + rtx tail_recursion_insns = NULL_RTX; + /* Sequence of insns to perform a normal "call". */ + rtx normal_call_insns = NULL_RTX; + /* Sequence of insns to perform a tail recursive "call". */ + rtx tail_call_insns = NULL_RTX; /* Data type of the function. */ tree funtype; /* Declaration of the function being called, or 0 if the function is computed (not known by name). */ tree fndecl = 0; char *name = 0; +#ifdef ACCUMULATE_OUTGOING_ARGS rtx before_call; +#endif + rtx insn; + int safe_for_reeval; + int pass; /* Register in which non-BLKmode value will be returned, or 0 if no value or if value is BLKmode. */ @@ -1708,17 +1779,10 @@ expand_call (exp, target, ignore) rtx old_stack_level = 0; int old_pending_adj = 0; int old_inhibit_defer_pop = inhibit_defer_pop; - rtx call_fusage = 0; + rtx call_fusage; register tree p; register int i; -#ifdef PREFERRED_STACK_BOUNDARY - int preferred_stack_boundary = PREFERRED_STACK_BOUNDARY; -#else - /* In this case preferred_stack_boundary variable is meaningless. - It is used only in order to keep ifdef noise down when calling - compute_argument_block_size. */ - int preferred_stack_boundary = 0; -#endif + int preferred_stack_boundary; /* The value of the function call can be put in a hard register. But if -fcheck-memory-usage, code which invokes functions (and thus @@ -1947,11 +2011,91 @@ expand_call (exp, target, ignore) mark_addressable (fndecl); } + currently_expanding_call++; + + /* If we're considering tail recursion optimizations, verify that the + arguments are safe for re-evaluation. If we can unsave them, wrap + each argument in an UNSAVE_EXPR. */ + + safe_for_reeval = 0; + if (optimize >= 2 + && currently_expanding_call == 1 + && stmt_loop_nest_empty ()) + { + /* Verify that each argument is safe for re-evaluation. */ + for (p = actparms; p; p = TREE_CHAIN (p)) + if (! safe_for_unsave (TREE_VALUE (p))) + break; + + if (p == NULL_TREE) + { + tree new_actparms = NULL_TREE, q; + + for (p = actparms; p ; p = TREE_CHAIN (p)) + { + tree np = build_tree_list (TREE_PURPOSE (p), + unsave_expr (TREE_VALUE (p))); + if (new_actparms) + TREE_CHAIN (q) = np; + else + new_actparms = np; + q = np; + } + + actparms = new_actparms; + safe_for_reeval = 1; + } + } + + /* Generate a tail recursion sequence when calling ourselves. */ + + if (safe_for_reeval + && TREE_CODE (TREE_OPERAND (exp, 0)) == ADDR_EXPR + && TREE_OPERAND (TREE_OPERAND (exp, 0), 0) == current_function_decl) + { + /* We want to emit any pending stack adjustments before the tail + recursion "call". That way we know any adjustment after the tail + recursion call can be ignored if we indeed use the tail recursion + call expansion. */ + int save_pending_stack_adjust = pending_stack_adjust; + rtx last; + + /* Use a new sequence to hold any RTL we generate. We do not even + know if we will use this RTL yet. The final decision can not be + made until after RTL generation for the entire function is + complete. */ + push_to_sequence (0); + + /* Emit the pending stack adjustments before we expand any arguments. */ + do_pending_stack_adjust (); + + optimize_tail_recursion (exp, get_last_insn ()); + + last = get_last_insn (); + tail_recursion_insns = get_insns (); + end_sequence (); + + /* If the last insn on the tail recursion sequence is not a + BARRIER, then tail recursion optimization failed. */ + if (last == NULL_RTX || GET_CODE (last) != BARRIER) + tail_recursion_insns = NULL_RTX; + + /* Restore the original pending stack adjustment for the sibling and + normal call cases below. */ + pending_stack_adjust = save_pending_stack_adjust; + } + function_call_count++; if (fndecl && DECL_NAME (fndecl)) name = IDENTIFIER_POINTER (DECL_NAME (fndecl)); +#ifdef PREFERRED_STACK_BOUNDARY + preferred_stack_boundary = PREFERRED_STACK_BOUNDARY; +#else + preferred_stack_boundary = STACK_BOUNDARY; +#endif + /* Ensure current function's preferred stack boundary is at least what we need. We don't have to increase alignment for recursive functions. */ @@ -1973,708 +2117,881 @@ expand_call (exp, target, ignore) abort (); funtype = TREE_TYPE (funtype); - /* When calling a const function, we must pop the stack args right away, - so that the pop is deleted or moved with the call. */ - if (is_const) - NO_DEFER_POP; + /* We want to make two insn chains; one for a sibling call, the other + for a normal call. We will select one of the two chains after + initial RTL generation is complete. */ + for (pass = 0; pass < 2; pass++) + { + int sibcall_failure = 0; + /* We want to emit ay pending stack adjustments before the tail + recursion "call". That way we know any adjustment after the tail + recursion call can be ignored if we indeed use the tail recursion + call expansion. */ + int save_pending_stack_adjust; + rtx insns; + rtx before_call; - /* Don't let pending stack adjusts add up to too much. - Also, do all pending adjustments now - if there is any chance this might be a call to alloca. */ + if (pass == 0) + { + /* Various reasons we can not use a sibling call. */ + if (! safe_for_reeval +#ifdef HAVE_sibcall_epilogue + || ! HAVE_sibcall_epilogue +#else + || 1 +#endif + /* The structure value address is used and modified in the + loop below. It does not seem worth the effort to save and + restore it as a state variable since few optimizable + sibling calls will return a structure. */ + || structure_value_addr != NULL_RTX + /* If the register holding the address is a callee saved + register, then we lose. We have no way to prevent that, + so we only allow calls to named functions. */ + || fndecl == NULL_TREE + || ! FUNCTION_OK_FOR_SIBCALL (fndecl)) + continue; - if (pending_stack_adjust >= 32 - || (pending_stack_adjust > 0 && may_be_alloca)) - do_pending_stack_adjust (); + /* State variables we need to save and restore between + iterations. */ + save_pending_stack_adjust = pending_stack_adjust; + } - if (profile_arc_flag && fork_or_exec) - { - /* A fork duplicates the profile information, and an exec discards - it. We can't rely on fork/exec to be paired. So write out the - profile information we have gathered so far, and clear it. */ - emit_library_call (gen_rtx_SYMBOL_REF (Pmode, "__bb_fork_func"), 0, - VOIDmode, 0); - - /* ??? When __clone is called with CLONE_VM set, profiling is - subject to race conditions, just as with multithreaded programs. */ - } + /* Other state variables that we must reinitialize each time + through the loop (that are not initialized by the loop itself. */ + argblock = 0; + call_fusage = 0; - /* Push the temporary stack slot level so that we can free any temporaries - we make. */ - push_temp_slots (); + /* Start a new sequence for the normal call case. - /* Start updating where the next arg would go. + From this point on, if the sibling call fails, we want to set + sibcall_failure instead of continuing the loop. */ + start_sequence (); - On some machines (such as the PA) indirect calls have a different - calling convention than normal calls. The last argument in - INIT_CUMULATIVE_ARGS tells the backend if this is an indirect call - or not. */ - INIT_CUMULATIVE_ARGS (args_so_far, funtype, NULL_RTX, (fndecl == 0)); + /* When calling a const function, we must pop the stack args right away, + so that the pop is deleted or moved with the call. */ + if (is_const) + NO_DEFER_POP; - /* If struct_value_rtx is 0, it means pass the address - as if it were an extra parameter. */ - if (structure_value_addr && struct_value_rtx == 0) - { - /* If structure_value_addr is a REG other than - virtual_outgoing_args_rtx, we can use always use it. If it - is not a REG, we must always copy it into a register. - If it is virtual_outgoing_args_rtx, we must copy it to another - register in some cases. */ - rtx temp = (GET_CODE (structure_value_addr) != REG -#ifdef ACCUMULATE_OUTGOING_ARGS - || (stack_arg_under_construction - && structure_value_addr == virtual_outgoing_args_rtx) -#endif - ? copy_addr_to_reg (structure_value_addr) - : structure_value_addr); - - actparms - = tree_cons (error_mark_node, - make_tree (build_pointer_type (TREE_TYPE (funtype)), - temp), - actparms); - structure_value_addr_parm = 1; - } + /* Don't let pending stack adjusts add up to too much. + Also, do all pending adjustments now if there is any chance + this might be a call to alloca or if we are expanding a sibling + call sequence. */ + if (pending_stack_adjust >= 32 + || (pending_stack_adjust > 0 && may_be_alloca) + || pass == 0) + do_pending_stack_adjust (); - /* Count the arguments and set NUM_ACTUALS. */ - for (p = actparms, i = 0; p; p = TREE_CHAIN (p)) i++; - num_actuals = i; - - /* Compute number of named args. - Normally, don't include the last named arg if anonymous args follow. - We do include the last named arg if STRICT_ARGUMENT_NAMING is nonzero. - (If no anonymous args follow, the result of list_length is actually - one too large. This is harmless.) - - If PRETEND_OUTGOING_VARARGS_NAMED is set and STRICT_ARGUMENT_NAMING is - zero, this machine will be able to place unnamed args that were passed in - registers into the stack. So treat all args as named. This allows the - insns emitting for a specific argument list to be independent of the - function declaration. - - If PRETEND_OUTGOING_VARARGS_NAMED is not set, we do not have any reliable - way to pass unnamed args in registers, so we must force them into - memory. */ - - if ((STRICT_ARGUMENT_NAMING - || ! PRETEND_OUTGOING_VARARGS_NAMED) - && TYPE_ARG_TYPES (funtype) != 0) - n_named_args - = (list_length (TYPE_ARG_TYPES (funtype)) - /* Don't include the last named arg. */ - - (STRICT_ARGUMENT_NAMING ? 0 : 1) - /* Count the struct value address, if it is passed as a parm. */ - + structure_value_addr_parm); - else - /* If we know nothing, treat all args as named. */ - n_named_args = num_actuals; + if (profile_arc_flag && fork_or_exec) + { + /* A fork duplicates the profile information, and an exec discards + it. We can't rely on fork/exec to be paired. So write out the + profile information we have gathered so far, and clear it. */ + /* ??? When Linux's __clone is called with CLONE_VM set, profiling + is subject to race conditions, just as with multithreaded + programs. */ + + emit_library_call (gen_rtx_SYMBOL_REF (Pmode, "__bb_fork_func"), 0, + VOIDmode, 0); + } - /* Make a vector to hold all the information about each arg. */ - args = (struct arg_data *) alloca (num_actuals * sizeof (struct arg_data)); - bzero ((char *) args, num_actuals * sizeof (struct arg_data)); + /* Push the temporary stack slot level so that we can free any + temporaries we make. */ + push_temp_slots (); + + /* Start updating where the next arg would go. + + On some machines (such as the PA) indirect calls have a different + calling convention than normal calls. The last argument in + INIT_CUMULATIVE_ARGS tells the backend if this is an indirect call + or not. */ + INIT_CUMULATIVE_ARGS (args_so_far, funtype, NULL_RTX, (fndecl == 0)); + + /* If struct_value_rtx is 0, it means pass the address + as if it were an extra parameter. */ + if (structure_value_addr && struct_value_rtx == 0) + { + /* If structure_value_addr is a REG other than + virtual_outgoing_args_rtx, we can use always use it. If it + is not a REG, we must always copy it into a register. + If it is virtual_outgoing_args_rtx, we must copy it to another + register in some cases. */ + rtx temp = (GET_CODE (structure_value_addr) != REG +#ifdef ACCUMULATE_OUTGOING_ARGS + || (stack_arg_under_construction + && structure_value_addr == virtual_outgoing_args_rtx) +#endif + ? copy_addr_to_reg (structure_value_addr) + : structure_value_addr); + + actparms + = tree_cons (error_mark_node, + make_tree (build_pointer_type (TREE_TYPE (funtype)), + temp), + actparms); + structure_value_addr_parm = 1; + } - /* Build up entries inthe ARGS array, compute the size of the arguments - into ARGS_SIZE, etc. */ - initialize_argument_information (num_actuals, args, &args_size, n_named_args, - actparms, fndecl, &args_so_far, - reg_parm_stack_space, &old_stack_level, - &old_pending_adj, &must_preallocate, - &is_const); + /* Count the arguments and set NUM_ACTUALS. */ + for (p = actparms, i = 0; p; p = TREE_CHAIN (p)) i++; + num_actuals = i; + + /* Compute number of named args. + Normally, don't include the last named arg if anonymous args follow. + We do include the last named arg if STRICT_ARGUMENT_NAMING is nonzero. + (If no anonymous args follow, the result of list_length is actually + one too large. This is harmless.) + + If PRETEND_OUTGOING_VARARGS_NAMED is set and STRICT_ARGUMENT_NAMING is + zero, this machine will be able to place unnamed args that were + passed in registers into the stack. So treat all args as named. + This allows the insns emitting for a specific argument list to be + independent of the function declaration. + + If PRETEND_OUTGOING_VARARGS_NAMED is not set, we do not have any + reliable way to pass unnamed args in registers, so we must force + them into memory. */ + + if ((STRICT_ARGUMENT_NAMING + || ! PRETEND_OUTGOING_VARARGS_NAMED) + && TYPE_ARG_TYPES (funtype) != 0) + n_named_args + = (list_length (TYPE_ARG_TYPES (funtype)) + /* Don't include the last named arg. */ + - (STRICT_ARGUMENT_NAMING ? 0 : 1) + /* Count the struct value address, if it is passed as a parm. */ + + structure_value_addr_parm); + else + /* If we know nothing, treat all args as named. */ + n_named_args = num_actuals; + + /* Make a vector to hold all the information about each arg. */ + args = (struct arg_data *) alloca (num_actuals + * sizeof (struct arg_data)); + bzero ((char *) args, num_actuals * sizeof (struct arg_data)); + + /* Build up entries inthe ARGS array, compute the size of the arguments + into ARGS_SIZE, etc. */ + initialize_argument_information (num_actuals, args, &args_size, + n_named_args, actparms, fndecl, + &args_so_far, reg_parm_stack_space, + &old_stack_level, &old_pending_adj, + &must_preallocate, &is_const); #ifdef FINAL_REG_PARM_STACK_SPACE - reg_parm_stack_space = FINAL_REG_PARM_STACK_SPACE (args_size.constant, - args_size.var); + reg_parm_stack_space = FINAL_REG_PARM_STACK_SPACE (args_size.constant, + args_size.var); #endif - if (args_size.var) - { - /* If this function requires a variable-sized argument list, don't try to - make a cse'able block for this call. We may be able to do this - eventually, but it is too complicated to keep track of what insns go - in the cse'able block and which don't. */ + if (args_size.var) + { + /* If this function requires a variable-sized argument list, don't + try to make a cse'able block for this call. We may be able to + do this eventually, but it is too complicated to keep track of + what insns go in the cse'able block and which don't. - is_const = 0; - must_preallocate = 1; - } + Also do not make a sibling call. */ - /* Compute the actual size of the argument block required. The variable - and constant sizes must be combined, the size may have to be rounded, - and there may be a minimum required size. */ - unadjusted_args_size - = compute_argument_block_size (reg_parm_stack_space, &args_size, - preferred_stack_boundary); - - /* Now make final decision about preallocating stack space. */ - must_preallocate = finalize_must_preallocate (must_preallocate, - num_actuals, args, &args_size); - - /* If the structure value address will reference the stack pointer, we must - stabilize it. We don't need to do this if we know that we are not going - to adjust the stack pointer in processing this call. */ - - if (structure_value_addr - && (reg_mentioned_p (virtual_stack_dynamic_rtx, structure_value_addr) - || reg_mentioned_p (virtual_outgoing_args_rtx, structure_value_addr)) - && (args_size.var + is_const = 0; + must_preallocate = 1; + sibcall_failure = 1; + } + + /* Compute the actual size of the argument block required. The variable + and constant sizes must be combined, the size may have to be rounded, + and there may be a minimum required size. When generating a sibcall + pattern, do not round up, since we'll be re-using whatever space our + caller provided. */ + unadjusted_args_size + = compute_argument_block_size (reg_parm_stack_space, &args_size, + (pass == 0 ? 0 + : preferred_stack_boundary)); + + /* If the callee pops its own arguments, then it must pop exactly + the same number of arguments as the current function. */ + if (RETURN_POPS_ARGS (fndecl, funtype, unadjusted_args_size) + != RETURN_POPS_ARGS (current_function_decl, + TREE_TYPE (current_function_decl), + current_function_args_size)) + sibcall_failure = 1; + + /* Now make final decision about preallocating stack space. */ + must_preallocate = finalize_must_preallocate (must_preallocate, + num_actuals, args, + &args_size); + + /* If the structure value address will reference the stack pointer, we + must stabilize it. We don't need to do this if we know that we are + not going to adjust the stack pointer in processing this call. */ + + if (structure_value_addr + && (reg_mentioned_p (virtual_stack_dynamic_rtx, structure_value_addr) + || reg_mentioned_p (virtual_outgoing_args_rtx, + structure_value_addr)) + && (args_size.var #ifndef ACCUMULATE_OUTGOING_ARGS - || args_size.constant + || args_size.constant #endif - )) - structure_value_addr = copy_to_reg (structure_value_addr); + )) + structure_value_addr = copy_to_reg (structure_value_addr); - /* Precompute any arguments as needed. */ - precompute_arguments (is_const, must_preallocate, num_actuals, - args, &args_size); + /* Precompute any arguments as needed. */ + precompute_arguments (is_const, must_preallocate, num_actuals, + args, &args_size); - /* Now we are about to start emitting insns that can be deleted - if a libcall is deleted. */ - if (is_const || is_malloc) - start_sequence (); + /* Now we are about to start emitting insns that can be deleted + if a libcall is deleted. */ + if (is_const || is_malloc) + start_sequence (); - /* If we have no actual push instructions, or shouldn't use them, - make space for all args right now. */ + /* If we have no actual push instructions, or shouldn't use them, + make space for all args right now. */ - if (args_size.var != 0) - { - if (old_stack_level == 0) + if (args_size.var != 0) { - emit_stack_save (SAVE_BLOCK, &old_stack_level, NULL_RTX); - old_pending_adj = pending_stack_adjust; - pending_stack_adjust = 0; + if (old_stack_level == 0) + { + emit_stack_save (SAVE_BLOCK, &old_stack_level, NULL_RTX); + old_pending_adj = pending_stack_adjust; + pending_stack_adjust = 0; #ifdef ACCUMULATE_OUTGOING_ARGS - /* stack_arg_under_construction says whether a stack arg is - being constructed at the old stack level. Pushing the stack - gets a clean outgoing argument block. */ - old_stack_arg_under_construction = stack_arg_under_construction; - stack_arg_under_construction = 0; + /* stack_arg_under_construction says whether a stack arg is + being constructed at the old stack level. Pushing the stack + gets a clean outgoing argument block. */ + old_stack_arg_under_construction = stack_arg_under_construction; + stack_arg_under_construction = 0; #endif + } + argblock = push_block (ARGS_SIZE_RTX (args_size), 0, 0); } - argblock = push_block (ARGS_SIZE_RTX (args_size), 0, 0); - } - else - { - /* Note that we must go through the motions of allocating an argument - block even if the size is zero because we may be storing args - in the area reserved for register arguments, which may be part of - the stack frame. */ + else + { + /* Note that we must go through the motions of allocating an argument + block even if the size is zero because we may be storing args + in the area reserved for register arguments, which may be part of + the stack frame. */ - int needed = args_size.constant; + int needed = args_size.constant; - /* Store the maximum argument space used. It will be pushed by - the prologue (if ACCUMULATE_OUTGOING_ARGS, or stack overflow - checking). */ + /* Store the maximum argument space used. It will be pushed by + the prologue (if ACCUMULATE_OUTGOING_ARGS, or stack overflow + checking). */ - if (needed > current_function_outgoing_args_size) - current_function_outgoing_args_size = needed; + if (needed > current_function_outgoing_args_size) + current_function_outgoing_args_size = needed; - if (must_preallocate) - { + if (must_preallocate) + { #ifdef ACCUMULATE_OUTGOING_ARGS - /* Since the stack pointer will never be pushed, it is possible for - the evaluation of a parm to clobber something we have already - written to the stack. Since most function calls on RISC machines - do not use the stack, this is uncommon, but must work correctly. + /* Since the stack pointer will never be pushed, it is possible + for the evaluation of a parm to clobber something we have + already written to the stack. Since most function calls on + RISC machines do not use the stack, this is uncommon, but + must work correctly. - Therefore, we save any area of the stack that was already written - and that we are using. Here we set up to do this by making a new - stack usage map from the old one. The actual save will be done - by store_one_arg. + Therefore, we save any area of the stack that was already + written and that we are using. Here we set up to do this by + making a new stack usage map from the old one. The actual + save will be done by store_one_arg. - Another approach might be to try to reorder the argument - evaluations to avoid this conflicting stack usage. */ + Another approach might be to try to reorder the argument + evaluations to avoid this conflicting stack usage. */ #ifndef OUTGOING_REG_PARM_STACK_SPACE - /* Since we will be writing into the entire argument area, the - map must be allocated for its entire size, not just the part that - is the responsibility of the caller. */ - needed += reg_parm_stack_space; + /* Since we will be writing into the entire argument area, the + map must be allocated for its entire size, not just the part + that is the responsibility of the caller. */ + needed += reg_parm_stack_space; #endif #ifdef ARGS_GROW_DOWNWARD - highest_outgoing_arg_in_use = MAX (initial_highest_arg_in_use, - needed + 1); + highest_outgoing_arg_in_use = MAX (initial_highest_arg_in_use, + needed + 1); #else - highest_outgoing_arg_in_use = MAX (initial_highest_arg_in_use, - needed); + highest_outgoing_arg_in_use = MAX (initial_highest_arg_in_use, + needed); #endif - stack_usage_map = (char *) alloca (highest_outgoing_arg_in_use); + stack_usage_map = (char *) alloca (highest_outgoing_arg_in_use); - if (initial_highest_arg_in_use) - bcopy (initial_stack_usage_map, stack_usage_map, - initial_highest_arg_in_use); + if (initial_highest_arg_in_use) + bcopy (initial_stack_usage_map, stack_usage_map, + initial_highest_arg_in_use); - if (initial_highest_arg_in_use != highest_outgoing_arg_in_use) - bzero (&stack_usage_map[initial_highest_arg_in_use], - highest_outgoing_arg_in_use - initial_highest_arg_in_use); - needed = 0; + if (initial_highest_arg_in_use != highest_outgoing_arg_in_use) + bzero (&stack_usage_map[initial_highest_arg_in_use], + (highest_outgoing_arg_in_use + - initial_highest_arg_in_use)); + needed = 0; - /* The address of the outgoing argument list must not be copied to a - register here, because argblock would be left pointing to the - wrong place after the call to allocate_dynamic_stack_space below. - */ + /* The address of the outgoing argument list must not be copied + to a register here, because argblock would be left pointing + to the wrong place after the call to + allocate_dynamic_stack_space below. */ - argblock = virtual_outgoing_args_rtx; + argblock = virtual_outgoing_args_rtx; #else /* not ACCUMULATE_OUTGOING_ARGS */ - if (inhibit_defer_pop == 0) - { - /* Try to reuse some or all of the pending_stack_adjust - to get this space. Maybe we can avoid any pushing. */ - if (needed > pending_stack_adjust) + if (inhibit_defer_pop == 0) { - needed -= pending_stack_adjust; - pending_stack_adjust = 0; + /* Try to reuse some or all of the pending_stack_adjust + to get this space. Maybe we can avoid any pushing. */ + if (needed > pending_stack_adjust) + { + needed -= pending_stack_adjust; + pending_stack_adjust = 0; + } + else + { + pending_stack_adjust -= needed; + needed = 0; + } } + /* Special case this because overhead of `push_block' in this + case is non-trivial. */ + if (needed == 0) + argblock = virtual_outgoing_args_rtx; else - { - pending_stack_adjust -= needed; - needed = 0; - } - } - /* Special case this because overhead of `push_block' in this - case is non-trivial. */ - if (needed == 0) - argblock = virtual_outgoing_args_rtx; - else - argblock = push_block (GEN_INT (needed), 0, 0); - - /* We only really need to call `copy_to_reg' in the case where push - insns are going to be used to pass ARGBLOCK to a function - call in ARGS. In that case, the stack pointer changes value - from the allocation point to the call point, and hence - the value of VIRTUAL_OUTGOING_ARGS_RTX changes as well. - But might as well always do it. */ - argblock = copy_to_reg (argblock); + argblock = push_block (GEN_INT (needed), 0, 0); + + /* We only really need to call `copy_to_reg' in the case where + push insns are going to be used to pass ARGBLOCK to a function + call in ARGS. In that case, the stack pointer changes value + from the allocation point to the call point, and hence + the value of VIRTUAL_OUTGOING_ARGS_RTX changes as well. + But might as well always do it. */ + argblock = copy_to_reg (argblock); #endif /* not ACCUMULATE_OUTGOING_ARGS */ + } + } + + /* The argument block when performing a sibling call is the + incoming argument block. */ + if (pass == 0) + { + rtx temp = plus_constant (arg_pointer_rtx, + FIRST_PARM_OFFSET (current_function_decl)); + argblock = force_reg (Pmode, force_operand (temp, NULL_RTX)); } - } #ifdef ACCUMULATE_OUTGOING_ARGS - /* The save/restore code in store_one_arg handles all cases except one: - a constructor call (including a C function returning a BLKmode struct) - to initialize an argument. */ - if (stack_arg_under_construction) - { + /* The save/restore code in store_one_arg handles all cases except one: + a constructor call (including a C function returning a BLKmode struct) + to initialize an argument. */ + if (stack_arg_under_construction) + { #ifndef OUTGOING_REG_PARM_STACK_SPACE - rtx push_size = GEN_INT (reg_parm_stack_space + args_size.constant); + rtx push_size = GEN_INT (reg_parm_stack_space + args_size.constant); #else - rtx push_size = GEN_INT (args_size.constant); + rtx push_size = GEN_INT (args_size.constant); #endif - if (old_stack_level == 0) - { - emit_stack_save (SAVE_BLOCK, &old_stack_level, NULL_RTX); - old_pending_adj = pending_stack_adjust; - pending_stack_adjust = 0; - /* stack_arg_under_construction says whether a stack arg is - being constructed at the old stack level. Pushing the stack - gets a clean outgoing argument block. */ - old_stack_arg_under_construction = stack_arg_under_construction; - stack_arg_under_construction = 0; - /* Make a new map for the new argument list. */ - stack_usage_map = (char *)alloca (highest_outgoing_arg_in_use); - bzero (stack_usage_map, highest_outgoing_arg_in_use); - highest_outgoing_arg_in_use = 0; + if (old_stack_level == 0) + { + emit_stack_save (SAVE_BLOCK, &old_stack_level, NULL_RTX); + old_pending_adj = pending_stack_adjust; + pending_stack_adjust = 0; + /* stack_arg_under_construction says whether a stack arg is + being constructed at the old stack level. Pushing the stack + gets a clean outgoing argument block. */ + old_stack_arg_under_construction = stack_arg_under_construction; + stack_arg_under_construction = 0; + /* Make a new map for the new argument list. */ + stack_usage_map = (char *)alloca (highest_outgoing_arg_in_use); + bzero (stack_usage_map, highest_outgoing_arg_in_use); + highest_outgoing_arg_in_use = 0; + } + allocate_dynamic_stack_space (push_size, NULL_RTX, BITS_PER_UNIT); } - allocate_dynamic_stack_space (push_size, NULL_RTX, BITS_PER_UNIT); - } - /* If argument evaluation might modify the stack pointer, copy the - address of the argument list to a register. */ - for (i = 0; i < num_actuals; i++) - if (args[i].pass_on_stack) - { - argblock = copy_addr_to_reg (argblock); - break; - } + /* If argument evaluation might modify the stack pointer, copy the + address of the argument list to a register. */ + for (i = 0; i < num_actuals; i++) + if (args[i].pass_on_stack) + { + argblock = copy_addr_to_reg (argblock); + break; + } #endif - compute_argument_addresses (args, argblock, num_actuals); + compute_argument_addresses (args, argblock, num_actuals); #ifdef PUSH_ARGS_REVERSED #ifdef PREFERRED_STACK_BOUNDARY - /* If we push args individually in reverse order, perform stack alignment - before the first push (the last arg). */ - if (args_size.constant != unadjusted_args_size) - { - /* When the stack adjustment is pending, - we get better code by combining the adjustments. */ - if (pending_stack_adjust && !is_const - && !inhibit_defer_pop) + /* If we push args individually in reverse order, perform stack alignment + before the first push (the last arg). */ + if (args_size.constant != unadjusted_args_size) { - args_size.constant = (unadjusted_args_size - + ((pending_stack_adjust + args_size.constant - + arg_space_so_far - - unadjusted_args_size) - % (preferred_stack_boundary / BITS_PER_UNIT))); - pending_stack_adjust -= args_size.constant - unadjusted_args_size; - do_pending_stack_adjust (); - } - else if (argblock == 0) - anti_adjust_stack (GEN_INT (args_size.constant - unadjusted_args_size)); - arg_space_so_far += args_size.constant - unadjusted_args_size; + /* When the stack adjustment is pending, we get better code + by combining the adjustments. */ + if (pending_stack_adjust && ! is_const + && ! inhibit_defer_pop) + { + args_size.constant = (unadjusted_args_size + + ((pending_stack_adjust + + args_size.constant + + arg_space_so_far + - unadjusted_args_size) + % (preferred_stack_boundary + / BITS_PER_UNIT))); + pending_stack_adjust -= (args_size.constant + - unadjusted_args_size); + do_pending_stack_adjust (); + } + else if (argblock == 0) + anti_adjust_stack (GEN_INT (args_size.constant + - unadjusted_args_size)); + arg_space_so_far += args_size.constant - unadjusted_args_size; - /* Now that the stack is properly aligned, pops can't safely - be deferred during the evaluation of the arguments. */ - NO_DEFER_POP; - } + /* Now that the stack is properly aligned, pops can't safely + be deferred during the evaluation of the arguments. */ + NO_DEFER_POP; + } #endif #endif - /* Don't try to defer pops if preallocating, not even from the first arg, - since ARGBLOCK probably refers to the SP. */ - if (argblock) - NO_DEFER_POP; + /* Don't try to defer pops if preallocating, not even from the first arg, + since ARGBLOCK probably refers to the SP. */ + if (argblock) + NO_DEFER_POP; - funexp = rtx_for_function_call (fndecl, exp); + funexp = rtx_for_function_call (fndecl, exp); - /* Figure out the register where the value, if any, will come back. */ - valreg = 0; - if (TYPE_MODE (TREE_TYPE (exp)) != VOIDmode - && ! structure_value_addr) - { - if (pcc_struct_value) - valreg = hard_function_value (build_pointer_type (TREE_TYPE (exp)), - fndecl, 0); - else - valreg = hard_function_value (TREE_TYPE (exp), fndecl, 0); - } + /* Figure out the register where the value, if any, will come back. */ + valreg = 0; + if (TYPE_MODE (TREE_TYPE (exp)) != VOIDmode + && ! structure_value_addr) + { + if (pcc_struct_value) + valreg = hard_function_value (build_pointer_type (TREE_TYPE (exp)), + fndecl, 0); + else + valreg = hard_function_value (TREE_TYPE (exp), fndecl, 0); + } - /* Precompute all register parameters. It isn't safe to compute anything - once we have started filling any specific hard regs. */ - precompute_register_parameters (num_actuals, args, ®_parm_seen); + /* Precompute all register parameters. It isn't safe to compute anything + once we have started filling any specific hard regs. */ + precompute_register_parameters (num_actuals, args, ®_parm_seen); #if defined(ACCUMULATE_OUTGOING_ARGS) && defined(REG_PARM_STACK_SPACE) - - /* Save the fixed argument area if it's part of the caller's frame and - is clobbered by argument setup for this call. */ - save_area = save_fixed_argument_area (reg_parm_stack_space, argblock, - &low_to_save, &high_to_save); + /* Save the fixed argument area if it's part of the caller's frame and + is clobbered by argument setup for this call. */ + save_area = save_fixed_argument_area (reg_parm_stack_space, argblock, + &low_to_save, &high_to_save); #endif - - /* Now store (and compute if necessary) all non-register parms. - These come before register parms, since they can require block-moves, - which could clobber the registers used for register parms. - Parms which have partial registers are not stored here, - but we do preallocate space here if they want that. */ + /* Now store (and compute if necessary) all non-register parms. + These come before register parms, since they can require block-moves, + which could clobber the registers used for register parms. + Parms which have partial registers are not stored here, + but we do preallocate space here if they want that. */ - for (i = 0; i < num_actuals; i++) - if (args[i].reg == 0 || args[i].pass_on_stack) - store_one_arg (&args[i], argblock, may_be_alloca, - args_size.var != 0, reg_parm_stack_space); - - /* If we have a parm that is passed in registers but not in memory - and whose alignment does not permit a direct copy into registers, - make a group of pseudos that correspond to each register that we - will later fill. */ - if (STRICT_ALIGNMENT) - store_unaligned_arguments_into_pseudos (args, num_actuals); - - /* Now store any partially-in-registers parm. - This is the last place a block-move can happen. */ - if (reg_parm_seen) - for (i = 0; i < num_actuals; i++) - if (args[i].partial != 0 && ! args[i].pass_on_stack) - store_one_arg (&args[i], argblock, may_be_alloca, - args_size.var != 0, reg_parm_stack_space); + for (i = 0; i < num_actuals; i++) + if (args[i].reg == 0 || args[i].pass_on_stack) + store_one_arg (&args[i], argblock, may_be_alloca, + args_size.var != 0, reg_parm_stack_space); + + /* If we have a parm that is passed in registers but not in memory + and whose alignment does not permit a direct copy into registers, + make a group of pseudos that correspond to each register that we + will later fill. */ + if (STRICT_ALIGNMENT) + store_unaligned_arguments_into_pseudos (args, num_actuals); + + /* Now store any partially-in-registers parm. + This is the last place a block-move can happen. */ + if (reg_parm_seen) + for (i = 0; i < num_actuals; i++) + if (args[i].partial != 0 && ! args[i].pass_on_stack) + store_one_arg (&args[i], argblock, may_be_alloca, + args_size.var != 0, reg_parm_stack_space); #ifndef PUSH_ARGS_REVERSED #ifdef PREFERRED_STACK_BOUNDARY - /* If we pushed args in forward order, perform stack alignment - after pushing the last arg. */ - if (argblock == 0) - anti_adjust_stack (GEN_INT (args_size.constant - unadjusted_args_size)); + /* If we pushed args in forward order, perform stack alignment + after pushing the last arg. */ + /* ??? Fix for arg_space_so_far. */ + if (argblock == 0) + anti_adjust_stack (GEN_INT (args_size.constant + - unadjusted_args_size)); #endif #endif - /* If register arguments require space on the stack and stack space - was not preallocated, allocate stack space here for arguments - passed in registers. */ + /* If register arguments require space on the stack and stack space + was not preallocated, allocate stack space here for arguments + passed in registers. */ #if ! defined(ACCUMULATE_OUTGOING_ARGS) && defined(OUTGOING_REG_PARM_STACK_SPACE) - if (must_preallocate == 0 && reg_parm_stack_space > 0) - anti_adjust_stack (GEN_INT (reg_parm_stack_space)); + if (must_preallocate == 0 && reg_parm_stack_space > 0) + anti_adjust_stack (GEN_INT (reg_parm_stack_space)); #endif - /* Pass the function the address in which to return a structure value. */ - if (structure_value_addr && ! structure_value_addr_parm) - { - emit_move_insn (struct_value_rtx, - force_reg (Pmode, - force_operand (structure_value_addr, - NULL_RTX))); - - /* Mark the memory for the aggregate as write-only. */ - if (current_function_check_memory_usage) - emit_library_call (chkr_set_right_libfunc, 1, - VOIDmode, 3, - structure_value_addr, Pmode, - GEN_INT (struct_value_size), TYPE_MODE (sizetype), - GEN_INT (MEMORY_USE_WO), - TYPE_MODE (integer_type_node)); - - if (GET_CODE (struct_value_rtx) == REG) - use_reg (&call_fusage, struct_value_rtx); - } - - funexp = prepare_call_address (funexp, fndecl, &call_fusage, reg_parm_seen); - - load_register_parameters (args, num_actuals, &call_fusage); - - /* Perform postincrements before actually calling the function. */ - emit_queue (); - - /* Save a pointer to the last insn before the call, so that we can - later safely search backwards to find the CALL_INSN. */ - before_call = get_last_insn (); + /* Pass the function the address in which to return a + structure value. */ + if (pass != 0 && structure_value_addr && ! structure_value_addr_parm) + { + emit_move_insn (struct_value_rtx, + force_reg (Pmode, + force_operand (structure_value_addr, + NULL_RTX))); + + /* Mark the memory for the aggregate as write-only. */ + if (current_function_check_memory_usage) + emit_library_call (chkr_set_right_libfunc, 1, + VOIDmode, 3, + structure_value_addr, ptr_mode, + GEN_INT (struct_value_size), + TYPE_MODE (sizetype), + GEN_INT (MEMORY_USE_WO), + TYPE_MODE (integer_type_node)); + + if (GET_CODE (struct_value_rtx) == REG) + use_reg (&call_fusage, struct_value_rtx); + } - /* All arguments and registers used for the call must be set up by now! */ + funexp = prepare_call_address (funexp, fndecl, &call_fusage, + reg_parm_seen); - /* Generate the actual call instruction. */ - emit_call_1 (funexp, fndecl, funtype, unadjusted_args_size, - args_size.constant, struct_value_size, - FUNCTION_ARG (args_so_far, VOIDmode, void_type_node, 1), - valreg, old_inhibit_defer_pop, call_fusage, is_const, nothrow); + load_register_parameters (args, num_actuals, &call_fusage); + + /* Perform postincrements before actually calling the function. */ + emit_queue (); - /* If call is cse'able, make appropriate pair of reg-notes around it. - Test valreg so we don't crash; may safely ignore `const' - if return type is void. Disable for PARALLEL return values, because - we have no way to move such values into a pseudo register. */ - if (is_const && valreg != 0 && GET_CODE (valreg) != PARALLEL) - { - rtx note = 0; - rtx temp = gen_reg_rtx (GET_MODE (valreg)); - rtx insns; + /* Save a pointer to the last insn before the call, so that we can + later safely search backwards to find the CALL_INSN. */ + before_call = get_last_insn (); - /* Mark the return value as a pointer if needed. */ - if (TREE_CODE (TREE_TYPE (exp)) == POINTER_TYPE) + /* All arguments and registers used for the call must be set up by + now! */ + + /* Generate the actual call instruction. */ + emit_call_1 (funexp, fndecl, funtype, unadjusted_args_size, + args_size.constant, struct_value_size, + FUNCTION_ARG (args_so_far, VOIDmode, void_type_node, 1), + valreg, old_inhibit_defer_pop, call_fusage, + ((is_const ? ECF_IS_CONST : 0) + | (nothrow ? ECF_NOTHROW : 0) + | (pass == 0 ? ECF_SIBCALL : 0))); + + /* If call is cse'able, make appropriate pair of reg-notes around it. + Test valreg so we don't crash; may safely ignore `const' + if return type is void. Disable for PARALLEL return values, because + we have no way to move such values into a pseudo register. */ + if (is_const && valreg != 0 && GET_CODE (valreg) != PARALLEL) { - tree pointed_to = TREE_TYPE (TREE_TYPE (exp)); - mark_reg_pointer (temp, TYPE_ALIGN (pointed_to) / BITS_PER_UNIT); - } + rtx note = 0; + rtx temp = gen_reg_rtx (GET_MODE (valreg)); + rtx insns; - /* Construct an "equal form" for the value which mentions all the - arguments in order as well as the function name. */ + /* Mark the return value as a pointer if needed. */ + if (TREE_CODE (TREE_TYPE (exp)) == POINTER_TYPE) + { + tree pointed_to = TREE_TYPE (TREE_TYPE (exp)); + mark_reg_pointer (temp, TYPE_ALIGN (pointed_to) / BITS_PER_UNIT); + } + + /* Construct an "equal form" for the value which mentions all the + arguments in order as well as the function name. */ #ifdef PUSH_ARGS_REVERSED - for (i = 0; i < num_actuals; i++) - note = gen_rtx_EXPR_LIST (VOIDmode, args[i].initial_value, note); + for (i = 0; i < num_actuals; i++) + note = gen_rtx_EXPR_LIST (VOIDmode, args[i].initial_value, note); #else - for (i = num_actuals - 1; i >= 0; i--) - note = gen_rtx_EXPR_LIST (VOIDmode, args[i].initial_value, note); + for (i = num_actuals - 1; i >= 0; i--) + note = gen_rtx_EXPR_LIST (VOIDmode, args[i].initial_value, note); #endif - note = gen_rtx_EXPR_LIST (VOIDmode, funexp, note); - - insns = get_insns (); - end_sequence (); - - emit_libcall_block (insns, temp, valreg, note); + note = gen_rtx_EXPR_LIST (VOIDmode, funexp, note); - valreg = temp; - } - else if (is_const) - { - /* Otherwise, just write out the sequence without a note. */ - rtx insns = get_insns (); - - end_sequence (); - emit_insns (insns); - } - else if (is_malloc) - { - rtx temp = gen_reg_rtx (GET_MODE (valreg)); - rtx last, insns; + insns = get_insns (); + end_sequence (); - /* The return value from a malloc-like function is a pointer. */ - if (TREE_CODE (TREE_TYPE (exp)) == POINTER_TYPE) - mark_reg_pointer (temp, BIGGEST_ALIGNMENT / BITS_PER_UNIT); - - emit_move_insn (temp, valreg); - - /* The return value from a malloc-like function can not alias - anything else. */ - last = get_last_insn (); - REG_NOTES (last) = - gen_rtx_EXPR_LIST (REG_NOALIAS, temp, REG_NOTES (last)); + emit_libcall_block (insns, temp, valreg, note); + + valreg = temp; + } + else if (is_const) + { + /* Otherwise, just write out the sequence without a note. */ + rtx insns = get_insns (); - /* Write out the sequence. */ - insns = get_insns (); - end_sequence (); - emit_insns (insns); - valreg = temp; - } + end_sequence (); + emit_insns (insns); + } + else if (is_malloc) + { + rtx temp = gen_reg_rtx (GET_MODE (valreg)); + rtx last, insns; + + /* The return value from a malloc-like function is a pointer. */ + if (TREE_CODE (TREE_TYPE (exp)) == POINTER_TYPE) + mark_reg_pointer (temp, BIGGEST_ALIGNMENT / BITS_PER_UNIT); + + emit_move_insn (temp, valreg); + + /* The return value from a malloc-like function can not alias + anything else. */ + last = get_last_insn (); + REG_NOTES (last) = + gen_rtx_EXPR_LIST (REG_NOALIAS, temp, REG_NOTES (last)); + + /* Write out the sequence. */ + insns = get_insns (); + end_sequence (); + emit_insns (insns); + valreg = temp; + } - /* For calls to `setjmp', etc., inform flow.c it should complain - if nonvolatile values are live. */ + /* For calls to `setjmp', etc., inform flow.c it should complain + if nonvolatile values are live. For functions that cannot return, + inform flow that control does not fall through. */ - if (returns_twice) - { - /* The NOTE_INSN_SETJMP note must be emitted immediately after the - CALL_INSN. Some ports emit more than just a CALL_INSN above, so - we must search for it here. */ - rtx last = get_last_insn (); - while (GET_CODE (last) != CALL_INSN) + if (returns_twice || is_volatile || is_longjmp || pass == 0) { - last = PREV_INSN (last); - /* There was no CALL_INSN? */ - if (last == before_call) - abort (); - } - emit_note_after (NOTE_INSN_SETJMP, last); - current_function_calls_setjmp = 1; - } + /* The barrier or NOTE_INSN_SETJMP note must be emitted + immediately after the CALL_INSN. Some ports emit more + than just a CALL_INSN above, so we must search for it here. */ - if (is_longjmp) - current_function_calls_longjmp = 1; + rtx last = get_last_insn (); + while (GET_CODE (last) != CALL_INSN) + { + last = PREV_INSN (last); + /* There was no CALL_INSN? */ + if (last == before_call) + abort (); + } - /* Notice functions that cannot return. - If optimizing, insns emitted below will be dead. - If not optimizing, they will exist, which is useful - if the user uses the `return' command in the debugger. */ + if (returns_twice) + { + emit_note_after (NOTE_INSN_SETJMP, last); + current_function_calls_setjmp = 1; + sibcall_failure = 1; + } + else + emit_barrier_after (last); + } - if (is_volatile || is_longjmp) - emit_barrier (); + if (is_longjmp) + current_function_calls_longjmp = 1, sibcall_failure = 1; - /* If value type not void, return an rtx for the value. */ + /* If value type not void, return an rtx for the value. */ - /* If there are cleanups to be called, don't use a hard reg as target. - We need to double check this and see if it matters anymore. */ - if (any_pending_cleanups (1) - && target && REG_P (target) - && REGNO (target) < FIRST_PSEUDO_REGISTER) - target = 0; + /* If there are cleanups to be called, don't use a hard reg as target. + We need to double check this and see if it matters anymore. */ + if (any_pending_cleanups (1) + && target && REG_P (target) + && REGNO (target) < FIRST_PSEUDO_REGISTER) + target = 0, sibcall_failure = 1; - if (TYPE_MODE (TREE_TYPE (exp)) == VOIDmode - || ignore) - { - target = const0_rtx; - } - else if (structure_value_addr) - { - if (target == 0 || GET_CODE (target) != MEM) + if (TYPE_MODE (TREE_TYPE (exp)) == VOIDmode + || ignore) { - target = gen_rtx_MEM (TYPE_MODE (TREE_TYPE (exp)), - memory_address (TYPE_MODE (TREE_TYPE (exp)), - structure_value_addr)); - MEM_SET_IN_STRUCT_P (target, - AGGREGATE_TYPE_P (TREE_TYPE (exp))); + target = const0_rtx; } - } - else if (pcc_struct_value) - { - /* This is the special C++ case where we need to - know what the true target was. We take care to - never use this value more than once in one expression. */ - target = gen_rtx_MEM (TYPE_MODE (TREE_TYPE (exp)), - copy_to_reg (valreg)); - MEM_SET_IN_STRUCT_P (target, AGGREGATE_TYPE_P (TREE_TYPE (exp))); - } - /* Handle calls that return values in multiple non-contiguous locations. - The Irix 6 ABI has examples of this. */ - else if (GET_CODE (valreg) == PARALLEL) - { - int bytes = int_size_in_bytes (TREE_TYPE (exp)); - - if (target == 0) + else if (structure_value_addr) { - target = assign_stack_temp (TYPE_MODE (TREE_TYPE (exp)), bytes, 0); + if (target == 0 || GET_CODE (target) != MEM) + { + target = gen_rtx_MEM (TYPE_MODE (TREE_TYPE (exp)), + memory_address (TYPE_MODE (TREE_TYPE (exp)), + structure_value_addr)); + MEM_SET_IN_STRUCT_P (target, + AGGREGATE_TYPE_P (TREE_TYPE (exp))); + } + } + else if (pcc_struct_value) + { + /* This is the special C++ case where we need to + know what the true target was. We take care to + never use this value more than once in one expression. */ + target = gen_rtx_MEM (TYPE_MODE (TREE_TYPE (exp)), + copy_to_reg (valreg)); MEM_SET_IN_STRUCT_P (target, AGGREGATE_TYPE_P (TREE_TYPE (exp))); - preserve_temp_slots (target); } + /* Handle calls that return values in multiple non-contiguous locations. + The Irix 6 ABI has examples of this. */ + else if (GET_CODE (valreg) == PARALLEL) + { + int bytes = int_size_in_bytes (TREE_TYPE (exp)); - if (! rtx_equal_p (target, valreg)) - emit_group_store (target, valreg, bytes, - TYPE_ALIGN (TREE_TYPE (exp)) / BITS_PER_UNIT); - } - else if (target && GET_MODE (target) == TYPE_MODE (TREE_TYPE (exp)) - && GET_MODE (target) == GET_MODE (valreg)) - /* TARGET and VALREG cannot be equal at this point because the latter - would not have REG_FUNCTION_VALUE_P true, while the former would if - it were referring to the same register. - - If they refer to the same register, this move will be a no-op, except - when function inlining is being done. */ - emit_move_insn (target, valreg); - else if (TYPE_MODE (TREE_TYPE (exp)) == BLKmode) - target = copy_blkmode_from_reg (target, valreg, TREE_TYPE (exp)); - else - target = copy_to_reg (valreg); + if (target == 0) + { + target = assign_stack_temp (TYPE_MODE (TREE_TYPE (exp)), + bytes, 0); + MEM_SET_IN_STRUCT_P (target, AGGREGATE_TYPE_P (TREE_TYPE (exp))); + preserve_temp_slots (target); + } + + if (! rtx_equal_p (target, valreg)) + emit_group_store (target, valreg, bytes, + TYPE_ALIGN (TREE_TYPE (exp)) / BITS_PER_UNIT); + /* We can not support sibling calls for this case. */ + sibcall_failure = 1; + } + else if (target + && GET_MODE (target) == TYPE_MODE (TREE_TYPE (exp)) + && GET_MODE (target) == GET_MODE (valreg)) + { + /* TARGET and VALREG cannot be equal at this point because the + latter would not have REG_FUNCTION_VALUE_P true, while the + former would if it were referring to the same register. + + If they refer to the same register, this move will be a no-op, + except when function inlining is being done. */ + emit_move_insn (target, valreg); + } + else if (TYPE_MODE (TREE_TYPE (exp)) == BLKmode) + target = copy_blkmode_from_reg (target, valreg, TREE_TYPE (exp)); + else + target = copy_to_reg (valreg); #ifdef PROMOTE_FUNCTION_RETURN - /* If we promoted this return value, make the proper SUBREG. TARGET - might be const0_rtx here, so be careful. */ - if (GET_CODE (target) == REG - && TYPE_MODE (TREE_TYPE (exp)) != BLKmode - && GET_MODE (target) != TYPE_MODE (TREE_TYPE (exp))) - { - tree type = TREE_TYPE (exp); - int unsignedp = TREE_UNSIGNED (type); + /* If we promoted this return value, make the proper SUBREG. TARGET + might be const0_rtx here, so be careful. */ + if (GET_CODE (target) == REG + && TYPE_MODE (TREE_TYPE (exp)) != BLKmode + && GET_MODE (target) != TYPE_MODE (TREE_TYPE (exp))) + { + tree type = TREE_TYPE (exp); + int unsignedp = TREE_UNSIGNED (type); - /* If we don't promote as expected, something is wrong. */ - if (GET_MODE (target) - != promote_mode (type, TYPE_MODE (type), &unsignedp, 1)) - abort (); + /* If we don't promote as expected, something is wrong. */ + if (GET_MODE (target) + != promote_mode (type, TYPE_MODE (type), &unsignedp, 1)) + abort (); - target = gen_rtx_SUBREG (TYPE_MODE (type), target, 0); - SUBREG_PROMOTED_VAR_P (target) = 1; - SUBREG_PROMOTED_UNSIGNED_P (target) = unsignedp; - } + target = gen_rtx_SUBREG (TYPE_MODE (type), target, 0); + SUBREG_PROMOTED_VAR_P (target) = 1; + SUBREG_PROMOTED_UNSIGNED_P (target) = unsignedp; + } #endif - /* If size of args is variable or this was a constructor call for a stack - argument, restore saved stack-pointer value. */ + /* If size of args is variable or this was a constructor call for a stack + argument, restore saved stack-pointer value. */ - if (old_stack_level) - { - emit_stack_restore (SAVE_BLOCK, old_stack_level, NULL_RTX); - pending_stack_adjust = old_pending_adj; + if (old_stack_level) + { + emit_stack_restore (SAVE_BLOCK, old_stack_level, NULL_RTX); + pending_stack_adjust = old_pending_adj; #ifdef ACCUMULATE_OUTGOING_ARGS - stack_arg_under_construction = old_stack_arg_under_construction; - highest_outgoing_arg_in_use = initial_highest_arg_in_use; - stack_usage_map = initial_stack_usage_map; + stack_arg_under_construction = old_stack_arg_under_construction; + highest_outgoing_arg_in_use = initial_highest_arg_in_use; + stack_usage_map = initial_stack_usage_map; #endif - } + sibcall_failure = 1; + } #ifdef ACCUMULATE_OUTGOING_ARGS - else - { + else + { #ifdef REG_PARM_STACK_SPACE - if (save_area) - restore_fixed_argument_area (save_area, argblock, - high_to_save, low_to_save); + if (save_area) + { + restore_fixed_argument_area (save_area, argblock, + high_to_save, low_to_save); + sibcall_failure = 1; + } #endif - /* If we saved any argument areas, restore them. */ - for (i = 0; i < num_actuals; i++) - if (args[i].save_area) - { - enum machine_mode save_mode = GET_MODE (args[i].save_area); - rtx stack_area - = gen_rtx_MEM (save_mode, - memory_address (save_mode, - XEXP (args[i].stack_slot, 0))); - - if (save_mode != BLKmode) - emit_move_insn (stack_area, args[i].save_area); - else - emit_block_move (stack_area, validize_mem (args[i].save_area), - GEN_INT (args[i].size.constant), - PARM_BOUNDARY / BITS_PER_UNIT); - } + /* If we saved any argument areas, restore them. */ + for (i = 0; i < num_actuals; i++) + if (args[i].save_area) + { + enum machine_mode save_mode = GET_MODE (args[i].save_area); + rtx stack_area + = gen_rtx_MEM (save_mode, + memory_address (save_mode, + XEXP (args[i].stack_slot, 0))); + + if (save_mode != BLKmode) + emit_move_insn (stack_area, args[i].save_area); + else + emit_block_move (stack_area, + validize_mem (args[i].save_area), + GEN_INT (args[i].size.constant), + PARM_BOUNDARY / BITS_PER_UNIT); + sibcall_failure = 1; + } - highest_outgoing_arg_in_use = initial_highest_arg_in_use; - stack_usage_map = initial_stack_usage_map; - } + highest_outgoing_arg_in_use = initial_highest_arg_in_use; + stack_usage_map = initial_stack_usage_map; + } #endif - /* If this was alloca, record the new stack level for nonlocal gotos. - Check for the handler slots since we might not have a save area - for non-local gotos. */ + /* If this was alloca, record the new stack level for nonlocal gotos. + Check for the handler slots since we might not have a save area + for non-local gotos. */ - if (may_be_alloca && nonlocal_goto_handler_slots != 0) - emit_stack_save (SAVE_NONLOCAL, &nonlocal_goto_stack_level, NULL_RTX); + if (may_be_alloca && nonlocal_goto_handler_slots != 0) + emit_stack_save (SAVE_NONLOCAL, &nonlocal_goto_stack_level, NULL_RTX); - pop_temp_slots (); + pop_temp_slots (); + + /* Free up storage we no longer need. */ + for (i = 0; i < num_actuals; ++i) + if (args[i].aligned_regs) + free (args[i].aligned_regs); + + insns = get_insns (); + end_sequence (); + + if (pass == 0) + { + tail_call_insns = insns; + + /* If the current function's argument block is not large enough + to hold the outoing arguments, or we encountered some other + situation we couldn't handle, zero out the sequence. */ + if (current_function_args_size < args_size.constant + || sibcall_failure) + tail_call_insns = NULL_RTX; + + /* Restore the pending stack adjustment now that we have + finished generating the sibling call sequence. */ + pending_stack_adjust = save_pending_stack_adjust; + } + else + normal_call_insns = insns; + } + + /* The function optimize_sibling_and_tail_recursive_calls doesn't + handle CALL_PLACEHOLDERs inside other CALL_PLACEHOLDERs. This + can happen if the arguments to this function call an inline + function who's expansion contains another CALL_PLACEHOLDER. + + If there are any C_Ps in any of these sequences, replace them + with their normal call. */ + + for (insn = normal_call_insns; insn; insn = NEXT_INSN (insn)) + if (GET_CODE (insn) == CALL_INSN + && GET_CODE (PATTERN (insn)) == CALL_PLACEHOLDER) + replace_call_placeholder (insn, sibcall_use_normal); + + for (insn = tail_call_insns; insn; insn = NEXT_INSN (insn)) + if (GET_CODE (insn) == CALL_INSN + && GET_CODE (PATTERN (insn)) == CALL_PLACEHOLDER) + replace_call_placeholder (insn, sibcall_use_normal); + + for (insn = tail_recursion_insns; insn; insn = NEXT_INSN (insn)) + if (GET_CODE (insn) == CALL_INSN + && GET_CODE (PATTERN (insn)) == CALL_PLACEHOLDER) + replace_call_placeholder (insn, sibcall_use_normal); + + /* If this was a potential tail recursion site, then emit a + CALL_PLACEHOLDER with the normal and the tail recursion streams. + One of them will be selected later. */ + if (tail_recursion_insns || tail_call_insns) + { + /* The tail recursion label must be kept around. We could expose + its use in the CALL_PLACEHOLDER, but that creates unwanted edges + and makes determining true tail recursion sites difficult. + + So we set LABEL_PRESERVE_P here, then clear it when we select + one of the call sequences after rtl generation is complete. */ + if (tail_recursion_insns) + LABEL_PRESERVE_P (tail_recursion_label) = 1; + emit_call_insn (gen_rtx_CALL_PLACEHOLDER (VOIDmode, normal_call_insns, + tail_call_insns, + tail_recursion_insns, + tail_recursion_label)); + } + else + emit_insns (normal_call_insns); - /* Free up storage we no longer need. */ - for (i = 0; i < num_actuals; ++i) - if (args[i].aligned_regs) - free (args[i].aligned_regs); + currently_expanding_call--; return target; } @@ -3171,7 +3488,9 @@ emit_library_call VPARAMS((rtx orgfun, int no_queue, enum machine_mode outmode, original_args_size.constant, args_size.constant, 0, FUNCTION_ARG (args_so_far, VOIDmode, void_type_node, 1), outmode != VOIDmode ? hard_libcall_value (outmode) : NULL_RTX, - old_inhibit_defer_pop + 1, call_fusage, no_queue, nothrow); + old_inhibit_defer_pop + 1, call_fusage, + ((no_queue ? ECF_IS_CONST : 0) + | (nothrow ? ECF_NOTHROW : 0))); pop_temp_slots (); @@ -3772,7 +4091,9 @@ emit_library_call_value VPARAMS((rtx orgfun, rtx value, int no_queue, struct_value_size, FUNCTION_ARG (args_so_far, VOIDmode, void_type_node, 1), mem_value == 0 ? hard_libcall_value (outmode) : NULL_RTX, - old_inhibit_defer_pop + 1, call_fusage, is_const, nothrow); + old_inhibit_defer_pop + 1, call_fusage, + ((is_const ? ECF_IS_CONST : 0) + | (nothrow ? ECF_NOTHROW : 0))); /* Now restore inhibit_defer_pop to its actual original value. */ OK_DEFER_POP; |