/* 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;
{
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. */
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 ();
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
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)),
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);
}
#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);
}
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. */
{
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
/* 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. */