From: Richard Stallman Date: Mon, 1 Jun 1992 03:11:11 +0000 (+0000) Subject: *** empty log message *** X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=d64f5a781f50a7c7625e91d2304359907f768eaa;p=gcc.git *** empty log message *** From-SVN: r1133 --- diff --git a/gcc/calls.c b/gcc/calls.c index 20686f11c8c..02f60ca867c 100644 --- a/gcc/calls.c +++ b/gcc/calls.c @@ -53,7 +53,10 @@ struct arg_data /* Number of registers to use. 0 means put the whole arg in registers. Also 0 if not passed in registers. */ int partial; - /* Non-zero if argument must be passed on stack. */ + /* Non-zero if argument must be passed on stack. + Note that some arguments may be passed on the stack + even though pass_on_stack is zero, just because FUNCTION_ARG says so. + pass_on_stack identifies arguments that *cannot* go in registers. */ int pass_on_stack; /* Offset of this argument from beginning of stack-args. */ struct args_size offset; @@ -595,6 +598,14 @@ expand_call (exp, target, ignore) { int i; + /* Perform all cleanups needed for the arguments of this call + (i.e. destructors in C++). It is ok if these destructors + clobber RETURN_VALUE_REG, because the only time we care about + this is when TARGET is that register. But in C++, we take + care to never return that register directly. */ + expand_cleanups_to (old_cleanups); + +#ifdef ACCUMULATE_OUTGOING_ARGS /* If the outgoing argument list must be preserved, push the stack before executing the inlined function if it makes any calls. */ @@ -605,19 +616,44 @@ expand_call (exp, target, ignore) if (stack_arg_under_construction || i >= 0) { - rtx insn, seq; + rtx insn = NEXT_INSN (before_call), seq; - for (insn = NEXT_INSN (before_call); insn; - insn = NEXT_INSN (insn)) - if (GET_CODE (insn) == CALL_INSN) - break; + /* Look for a call in the inline function code. + If OUTGOING_ARGS_SIZE (DECL_SAVED_INSNS (fndecl)) is + nonzero then there is a call and it is not necessary + to scan the insns. */ + + if (OUTGOING_ARGS_SIZE (DECL_SAVED_INSNS (fndecl)) == 0) + for (; insn; insn = NEXT_INSN (insn)) + if (GET_CODE (insn) == CALL_INSN) + break; if (insn) { + /* Reserve enough stack space so that the largest + argument list of any function call in the inline + function does not overlap the argument list being + evaluated. This is usually an overestimate because + allocate_dynamic_stack_space reserves space for an + outgoing argument list in addition to the requested + space, but there is no way to ask for stack space such + that an argument list of a certain length can be + safely constructed. */ + + int adjust = OUTGOING_ARGS_SIZE (DECL_SAVED_INSNS (fndecl)); +#ifdef REG_PARM_STACK_SPACE + /* Add the stack space reserved for register arguments + in the inline function. What is really needed is the + largest value of reg_parm_stack_space in the inline + function, but that is not available. Using the current + value of reg_parm_stack_space is wrong, but gives + correct results on all supported machines. */ + adjust += reg_parm_stack_space; +#endif start_sequence (); emit_stack_save (SAVE_BLOCK, &old_stack_level, 0); allocate_dynamic_stack_space (gen_rtx (CONST_INT, VOIDmode, - highest_outgoing_arg_in_use), + adjust), 0, BITS_PER_UNIT); seq = get_insns (); end_sequence (); @@ -625,13 +661,7 @@ expand_call (exp, target, ignore) emit_stack_restore (SAVE_BLOCK, old_stack_level, 0); } } - - /* Perform all cleanups needed for the arguments of this call - (i.e. destructors in C++). It is ok if these destructors - clobber RETURN_VALUE_REG, because the only time we care about - this is when TARGET is that register. But in C++, we take - care to never return that register directly. */ - expand_cleanups_to (old_cleanups); +#endif /* If the result is equivalent to TARGET, return TARGET to simplify checks in store_expr. They can be equivalent but not equal in the @@ -743,11 +773,16 @@ expand_call (exp, target, ignore) as if it were an extra parameter. */ if (structure_value_addr && struct_value_rtx == 0) { +#ifdef ACCUMULATE_OUTGOING_ARGS /* If the stack will be adjusted, make sure the structure address does not refer to virtual_outgoing_args_rtx. */ rtx temp = (stack_arg_under_construction ? copy_addr_to_reg (structure_value_addr) : force_reg (Pmode, structure_value_addr)); +#else + rtx temp = force_reg (Pmode, structure_value_addr); +#endif + actparms = tree_cons (error_mark_node, make_tree (build_pointer_type (TREE_TYPE (funtype)), @@ -1111,11 +1146,13 @@ expand_call (exp, target, ignore) emit_stack_save (SAVE_BLOCK, &old_stack_level, 0); 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; +#endif } argblock = push_block (ARGS_SIZE_RTX (args_size), 0, 0); } @@ -1273,13 +1310,18 @@ expand_call (exp, target, ignore) #endif #endif +#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) { +#if defined(REG_PARM_STACK_SPACE) && ! defined(OUTGOING_REG_PARM_STACK_SPACE) rtx push_size = gen_rtx (CONST_INT, VOIDmode, - highest_outgoing_arg_in_use); + reg_parm_stack_space + args_size.constant); +#else + rtx push_size = gen_rtx (CONST_INT, VOIDmode, args_size.constant); +#endif if (old_stack_level == 0) { emit_stack_save (SAVE_BLOCK, &old_stack_level, 0); @@ -1297,6 +1339,7 @@ expand_call (exp, target, ignore) } allocate_dynamic_stack_space (push_size, 0, BITS_PER_UNIT); } +#endif /* Don't try to defer pops if preallocating, not even from the first arg, since ARGBLOCK probably refers to the SP. */ @@ -1635,9 +1678,11 @@ expand_call (exp, target, ignore) { emit_stack_restore (SAVE_BLOCK, old_stack_level, 0); 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; +#endif } #ifdef ACCUMULATE_OUTGOING_ARGS else @@ -1849,7 +1894,35 @@ store_one_arg (arg, argblock, may_be_alloca, variable_size, fndecl, /* If this is being passes partially in a register, we can't evaluate it directly into its stack slot. Otherwise, we can. */ if (arg->value == 0) - arg->value = expand_expr (pval, partial ? 0 : arg->stack, VOIDmode, 0); + { +#ifdef ACCUMULATE_OUTGOING_ARGS + /* stack_arg_under_construction is nonzero if a function argument is + being evaluated directly into the outgoing argument list and + expand_call must take special action to preserve the argument list + if it is called recursively. + + For scalar function arguments stack_usage_map is sufficient to + determine which stack slots must be saved and restored. Scalar + arguments in general have pass_on_stack == 0. + + If this argument is initialized by a function which takes the + address of the argument (a C++ constructor or a C function + returning a BLKmode structure), then stack_usage_map is + insufficient and expand_call must push the stack around the + function call. Such arguments have pass_on_stack == 1. + + Note that it is always safe to set stack_arg_under_construction, + but this generates suboptimal code if set when not needed. */ + + if (arg->pass_on_stack) + stack_arg_under_construction++; +#endif + arg->value = expand_expr (pval, partial ? 0 : arg->stack, VOIDmode, 0); +#ifdef ACCUMULATE_OUTGOING_ARGS + if (arg->pass_on_stack) + stack_arg_under_construction--; +#endif + } /* Don't allow anything left on stack from computation of argument to alloca. */