#include "output.h"
#include "tm_p.h"
+#ifndef ACCUMULATE_OUTGOING_ARGS
+#define ACCUMULATE_OUTGOING_ARGS 0
+#endif
+
+/* Supply a default definition for PUSH_ARGS. */
+#ifndef PUSH_ARGS
+#ifdef PUSH_ROUNDING
+#define PUSH_ARGS !ACCUMULATE_OUTGOING_ARGS
+#else
+#define PUSH_ARGS 0
+#endif
+#endif
+
#if !defined FUNCTION_OK_FOR_SIBCALL
#define FUNCTION_OK_FOR_SIBCALL(DECL) 1
#endif
#ifdef PUSH_ROUNDING
#if defined (STACK_GROWS_DOWNWARD) != defined (ARGS_GROW_DOWNWARD)
-#define PUSH_ARGS_REVERSED /* If it's last to first */
+#define PUSH_ARGS_REVERSED PUSH_ARGS
#endif
#endif
+#ifndef PUSH_ARGS_REVERSED
+#define PUSH_ARGS_REVERSED 0
+#endif
+
/* Like PREFERRED_STACK_BOUNDARY but in units of bytes, not bits. */
#define STACK_BYTES (PREFERRED_STACK_BOUNDARY / BITS_PER_UNIT)
differ from STACK if this arg pads downward. This location is known
to be aligned to FUNCTION_ARG_BOUNDARY. */
rtx stack_slot;
-#ifdef ACCUMULATE_OUTGOING_ARGS
/* Place that this stack area has been saved, if needed. */
rtx save_area;
-#endif
/* If an argument's alignment does not permit direct copying into registers,
copy in smaller-sized pieces into pseudos. These are stored in a
block pointed to by this field. The next field says how many
struct args_size alignment_pad;
};
-#ifdef ACCUMULATE_OUTGOING_ARGS
/* A vector of one char per byte of stack space. A byte if non-zero if
the corresponding stack location has been used.
This vector is used to prevent a function call within an argument from
to make sure the object being constructed does not overlap the
argument list for the constructor call. */
int stack_arg_under_construction;
-#endif
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
+/* Nonzero if this is a call to a `const' function. */
+#define ECF_CONST 1
+/* Nonzero if this is a call to a `volatile' function. */
+#define ECF_NORETURN 2
+/* Nonzero if this is a call to malloc or a related function. */
+#define ECF_MALLOC 4
+/* Nonzero if it is plausible that this is a call to alloca. */
+#define ECF_MAY_BE_ALLOCA 8
+/* Nonzero if this is a call to a function that won't throw an exception. */
+#define ECF_NOTHROW 16
+/* Nonzero if this is a call to setjmp or a related function. */
+#define ECF_RETURNS_TWICE 32
+/* Nonzero if this is a call to `longjmp'. */
+#define ECF_LONGJMP 64
+/* Nonzero if this is a syscall that makes a new process in the image of
+ the current one. */
+#define ECF_FORK_OR_EXEC 128
+#define ECF_SIBCALL 256
+/* Nonzero if this is a call to "pure" function (like const function,
+ but may read memory. */
+#define ECF_PURE 512
+
static void emit_call_1 PARAMS ((rtx, tree, tree, HOST_WIDE_INT,
HOST_WIDE_INT, HOST_WIDE_INT, rtx,
rtx, int, rtx, int));
static int finalize_must_preallocate PARAMS ((int, int,
struct arg_data *,
struct args_size *));
-static void precompute_arguments PARAMS ((int, int, int,
- struct arg_data *,
- struct args_size *));
+static void precompute_arguments PARAMS ((int, int,
+ struct arg_data *));
static int compute_argument_block_size PARAMS ((int,
struct args_size *,
int));
int, tree, tree,
CUMULATIVE_ARGS *,
int, rtx *, int *,
- int *, int *, int));
+ int *, int *));
static void compute_argument_addresses PARAMS ((struct arg_data *,
rtx, int));
static rtx rtx_for_function_call PARAMS ((tree, tree));
static rtx emit_library_call_value_1 PARAMS ((int, rtx, rtx, int,
enum machine_mode,
int, va_list));
+static int special_function_p PARAMS ((tree, int));
+static int flags_from_decl_or_type PARAMS ((tree));
+static rtx try_to_integrate PARAMS ((tree, tree, rtx,
+ int, tree, rtx));
+static int combine_pending_stack_adjustment_and_call
+ PARAMS ((int, struct args_size *, int));
-#if defined(ACCUMULATE_OUTGOING_ARGS) && defined(REG_PARM_STACK_SPACE)
+#ifdef REG_PARM_STACK_SPACE
static rtx save_fixed_argument_area PARAMS ((int, rtx, int *, int *));
static void restore_fixed_argument_area PARAMS ((rtx, rtx, int, int));
#endif
We restore `inhibit_defer_pop' to that value.
CALL_FUSAGE is either empty or an EXPR_LIST of USE expressions that
- denote registers used by the called function.
-
- IS_CONST is true if this is a `const' call. */
+ denote registers used by the called function. */
static void
emit_call_1 (funexp, fndecl, funtype, stack_size, rounded_stack_size,
rtx struct_value_size_rtx = GEN_INT (struct_value_size);
#endif
rtx call_insn;
-#ifndef ACCUMULATE_OUTGOING_ARGS
int already_popped = 0;
HOST_WIDE_INT n_popped = RETURN_POPS_ARGS (fndecl, funtype, stack_size);
-#endif
/* Ensure address is valid. SYMBOL_REF is already valid, so no need,
and we don't want to load it into a register as an optimization,
if (GET_CODE (funexp) != SYMBOL_REF)
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
even if the call has no arguments to pop. */
#if defined (HAVE_call) && defined (HAVE_call_value)
if (HAVE_call && HAVE_call_value && HAVE_call_pop && HAVE_call_value_pop
- && n_popped > 0)
+ && n_popped > 0)
#else
if (HAVE_call_pop && HAVE_call_value_pop)
#endif
}
else
#endif
-#endif
#if defined (HAVE_sibcall) && defined (HAVE_sibcall_value)
if ((ecf_flags & ECF_SIBCALL)
if (! call_insn)
abort ();
+ /* Mark memory as used for "pure" function call. */
+ if (ecf_flags & ECF_PURE)
+ {
+ call_fusage = gen_rtx_EXPR_LIST (VOIDmode,
+ gen_rtx_USE (VOIDmode,
+ gen_rtx_MEM (BLKmode,
+ gen_rtx_SCRATCH (VOIDmode))), call_fusage);
+ }
+
/* Put the register usage information on the CALL. If there is already
some usage information, put ours at the end. */
if (CALL_INSN_FUNCTION_USAGE (call_insn))
CALL_INSN_FUNCTION_USAGE (call_insn) = call_fusage;
/* If this is a const call, then set the insn's unchanging bit. */
- if (ecf_flags & ECF_IS_CONST)
+ if (ecf_flags & (ECF_CONST | ECF_PURE))
CONST_CALL_P (call_insn) = 1;
/* If this call can't throw, attach a REG_EH_REGION reg note to that
if the context of the call as a whole permits. */
inhibit_defer_pop = old_inhibit_defer_pop;
-#ifndef ACCUMULATE_OUTGOING_ARGS
- /* If returning from the subroutine does not automatically pop the args,
- we need an instruction to pop them sooner or later.
- Perhaps do it now; perhaps just record how much space to pop later.
-
- If returning from the subroutine does pop the args, indicate that the
- stack pointer will be changed. */
-
- /* The space for the args is no longer waiting for the call; either it
- was popped by the call, or it'll be popped below. */
- arg_space_so_far -= rounded_stack_size;
-
if (n_popped > 0)
{
if (!already_popped)
CALL_INSN_FUNCTION_USAGE (call_insn));
rounded_stack_size -= n_popped;
rounded_stack_size_rtx = GEN_INT (rounded_stack_size);
+ stack_pointer_delta -= n_popped;
}
- if (rounded_stack_size != 0)
+ if (!ACCUMULATE_OUTGOING_ARGS)
{
- 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);
+ /* If returning from the subroutine does not automatically pop the args,
+ we need an instruction to pop them sooner or later.
+ Perhaps do it now; perhaps just record how much space to pop later.
+
+ If returning from the subroutine does pop the args, indicate that the
+ stack pointer will be changed. */
+
+ if (rounded_stack_size != 0)
+ {
+ if (flag_defer_pop && inhibit_defer_pop == 0
+ && !(ecf_flags & (ECF_CONST | ECF_PURE)))
+ pending_stack_adjust += rounded_stack_size;
+ else
+ adjust_stack (rounded_stack_size_rtx);
+ }
}
-#endif
+ /* When we accumulate outgoing args, we must avoid any stack manipulations.
+ Restore the stack pointer to its original value now. Usually
+ ACCUMULATE_OUTGOING_ARGS targets don't get here, but there are exceptions.
+ On i386 ACCUMULATE_OUTGOING_ARGS can be enabled on demand, and
+ popping variants of functions exist as well.
+
+ ??? We may optimize similar to defer_pop above, but it is
+ probably not worthwhile.
+
+ ??? It will be worthwhile to enable combine_stack_adjustments even for
+ such machines. */
+ else if (n_popped)
+ anti_adjust_stack (GEN_INT (n_popped));
}
/* Determine if the function identified by NAME and FNDECL is one with
For example, if the function might return more than one time (setjmp), then
set RETURNS_TWICE to a nonzero value.
- Similarly set IS_LONGJMP for if the function is in the longjmp family.
+ Similarly set LONGJMP for if the function is in the longjmp family.
- Set IS_MALLOC for any of the standard memory allocation functions which
+ Set MALLOC for any of the standard memory allocation functions which
allocate from the heap.
Set MAY_BE_ALLOCA for any memory allocation function that might allocate
space from the stack such as alloca. */
-void
-special_function_p (fndecl, returns_twice, is_longjmp, fork_or_exec,
- is_malloc, may_be_alloca)
+static int
+special_function_p (fndecl, flags)
tree fndecl;
- int *returns_twice;
- int *is_longjmp;
- int *fork_or_exec;
- int *is_malloc;
- int *may_be_alloca;
+ int flags;
{
- *returns_twice = 0;
- *is_longjmp = 0;
- *fork_or_exec = 0;
- *may_be_alloca = 0;
-
- /* The function decl may have the `malloc' attribute. */
- *is_malloc = fndecl && DECL_IS_MALLOC (fndecl);
-
- if (! *is_malloc
+ if (! (flags & ECF_MALLOC)
&& fndecl && DECL_NAME (fndecl)
&& IDENTIFIER_LENGTH (DECL_NAME (fndecl)) <= 17
/* Exclude functions not at the file scope, or not `extern',
/* We assume that alloca will always be called by name. It
makes no sense to pass it as a pointer-to-function to
anything that does not understand its behavior. */
- *may_be_alloca
- = (((IDENTIFIER_LENGTH (DECL_NAME (fndecl)) == 6
- && name[0] == 'a'
- && ! strcmp (name, "alloca"))
- || (IDENTIFIER_LENGTH (DECL_NAME (fndecl)) == 16
- && name[0] == '_'
- && ! strcmp (name, "__builtin_alloca"))));
+ if (((IDENTIFIER_LENGTH (DECL_NAME (fndecl)) == 6
+ && name[0] == 'a'
+ && ! strcmp (name, "alloca"))
+ || (IDENTIFIER_LENGTH (DECL_NAME (fndecl)) == 16
+ && name[0] == '_'
+ && ! strcmp (name, "__builtin_alloca"))))
+ flags |= ECF_MAY_BE_ALLOCA;
/* Disregard prefix _, __ or __x. */
if (name[0] == '_')
if (tname[0] == 's')
{
- *returns_twice
- = ((tname[1] == 'e'
- && (! strcmp (tname, "setjmp")
- || ! strcmp (tname, "setjmp_syscall")))
- || (tname[1] == 'i'
- && ! strcmp (tname, "sigsetjmp"))
- || (tname[1] == 'a'
- && ! strcmp (tname, "savectx")));
+ if ((tname[1] == 'e'
+ && (! strcmp (tname, "setjmp")
+ || ! strcmp (tname, "setjmp_syscall")))
+ || (tname[1] == 'i'
+ && ! strcmp (tname, "sigsetjmp"))
+ || (tname[1] == 'a'
+ && ! strcmp (tname, "savectx")))
+ flags |= ECF_RETURNS_TWICE;
+
if (tname[1] == 'i'
&& ! strcmp (tname, "siglongjmp"))
- *is_longjmp = 1;
+ flags |= ECF_LONGJMP;
}
else if ((tname[0] == 'q' && tname[1] == 's'
&& ! strcmp (tname, "qsetjmp"))
|| (tname[0] == 'v' && tname[1] == 'f'
&& ! strcmp (tname, "vfork")))
- *returns_twice = 1;
+ flags |= ECF_RETURNS_TWICE;
else if (tname[0] == 'l' && tname[1] == 'o'
&& ! strcmp (tname, "longjmp"))
- *is_longjmp = 1;
+ flags |= ECF_LONGJMP;
else if ((tname[0] == 'f' && tname[1] == 'o'
&& ! strcmp (tname, "fork"))
&& (tname[5] == '\0'
|| ((tname[5] == 'p' || tname[5] == 'e')
&& tname[6] == '\0'))))
- *fork_or_exec = 1;
+ flags |= ECF_FORK_OR_EXEC;
/* Do not add any more malloc-like functions to this list,
instead mark them as malloc functions using the malloc attribute.
&& (! strcmp (tname, "malloc")
|| ! strcmp (tname, "calloc")
|| ! strcmp (tname, "strdup")))
- *is_malloc = 1;
+ flags |= ECF_MALLOC;
+ }
+ return flags;
+}
+
+/* Return nonzero when tree represent call to longjmp. */
+int
+setjmp_call_p (fndecl)
+ tree fndecl;
+{
+ return special_function_p (fndecl, 0) & ECF_RETURNS_TWICE;
+}
+
+/* Detect flags (function attributes) from the function type node. */
+static int
+flags_from_decl_or_type (exp)
+ tree exp;
+{
+ int flags = 0;
+ /* ??? We can't set IS_MALLOC for function types? */
+ if (DECL_P (exp))
+ {
+ /* The function exp may have the `malloc' attribute. */
+ if (DECL_P (exp) && DECL_IS_MALLOC (exp))
+ flags |= ECF_MALLOC;
+
+ /* The function exp may have the `pure' attribute. */
+ if (DECL_P (exp) && DECL_IS_PURE (exp))
+ flags |= ECF_PURE;
+
+ if (TREE_NOTHROW (exp))
+ flags |= ECF_NOTHROW;
}
+
+ if (TREE_READONLY (exp) && !TREE_THIS_VOLATILE (exp))
+ flags |= ECF_CONST;
+
+ if (TREE_THIS_VOLATILE (exp))
+ flags |= ECF_NORETURN;
+
+ return flags;
}
+
/* Precompute all register parameters as described by ARGS, storing values
into fields within the ARGS array.
}
}
-#if defined(ACCUMULATE_OUTGOING_ARGS) && defined(REG_PARM_STACK_SPACE)
+#ifdef REG_PARM_STACK_SPACE
/* The argument list is the property of the called routine and it
may clobber it. If the fixed area has been used for previous
if (save_mode == BLKmode)
{
save_area = assign_stack_temp (BLKmode, num_to_save, 0);
- /* Cannot use emit_block_move here because it can be done by a library
- call which in turn gets into this place again and deadly infinite
- recursion happens. */
+ /* Cannot use emit_block_move here because it can be done by a
+ library call which in turn gets into this place again and deadly
+ infinite recursion happens. */
move_by_pieces (validize_mem (save_area), stack_area, num_to_save,
- PARM_BOUNDARY / BITS_PER_UNIT);
+ PARM_BOUNDARY);
}
else
{
call which in turn gets into this place again and deadly infinite
recursion happens. */
move_by_pieces (stack_area, validize_mem (save_area),
- high_to_save - low_to_save + 1,
- PARM_BOUNDARY / BITS_PER_UNIT);
+ high_to_save - low_to_save + 1, PARM_BOUNDARY);
}
#endif
bytes -= bitsize / BITS_PER_UNIT;
store_bit_field (reg, bitsize, big_endian_correction, word_mode,
- extract_bit_field (word, bitsize, 0, 1,
- NULL_RTX, word_mode,
- word_mode,
- bitalign / BITS_PER_UNIT,
+ extract_bit_field (word, bitsize, 0, 1, NULL_RTX,
+ word_mode, word_mode, bitalign,
BITS_PER_WORD),
- bitalign / BITS_PER_UNIT, BITS_PER_WORD);
+ bitalign, BITS_PER_WORD);
}
}
}
OLD_STACK_LEVEL is a pointer to an rtx which olds the old stack level
and may be modified by this routine.
- OLD_PENDING_ADJ, MUST_PREALLOCATE and IS_CONST are pointers to integer
+ OLD_PENDING_ADJ, MUST_PREALLOCATE and FLAGS are pointers to integer
flags which may may be modified by this routine. */
static void
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,
+ old_pending_adj, must_preallocate,
ecf_flags)
int num_actuals ATTRIBUTE_UNUSED;
struct arg_data *args;
rtx *old_stack_level;
int *old_pending_adj;
int *must_preallocate;
- int *is_const;
- int ecf_flags;
+ int *ecf_flags;
{
/* 1 if scanning parms front to back, -1 if scanning back to front. */
int inc;
We fill up ARGS from the front or from the back if necessary
so that in any case the first arg to be pushed ends up at the front. */
-#ifdef PUSH_ARGS_REVERSED
- i = num_actuals - 1, inc = -1;
- /* In this case, must reverse order of args
- so that we compute and push the last arg first. */
-#else
- i = 0, inc = 1;
-#endif
+ if (PUSH_ARGS_REVERSED)
+ {
+ i = num_actuals - 1, inc = -1;
+ /* In this case, must reverse order of args
+ so that we compute and push the last arg first. */
+ }
+ else
+ {
+ i = 0, inc = 1;
+ }
/* I counts args in order (to be) pushed; ARGPOS counts in order written. */
for (p = actparms, argpos = 0; p; p = TREE_CHAIN (p), i += inc, argpos++)
MEM_SET_IN_STRUCT_P (copy, AGGREGATE_TYPE_P (type));
store_expr (args[i].tree_value, copy, 0);
- *is_const = 0;
+ *ecf_flags &= ~(ECF_CONST | ECF_PURE);
args[i].tree_value = build1 (ADDR_EXPR,
build_pointer_type (type),
/* If this is a sibling call and the machine has register windows, the
register window has to be unwinded before calling the routine, so
arguments have to go into the incoming registers. */
- if (ecf_flags & ECF_SIBCALL)
+ if (*ecf_flags & ECF_SIBCALL)
args[i].reg = FUNCTION_INCOMING_ARG (*args_so_far, mode, type,
argpos < n_named_args);
else
/* If this is an addressable type, we cannot pre-evaluate it. Thus,
we cannot consider this function call constant. */
if (TREE_ADDRESSABLE (type))
- *is_const = 0;
+ *ecf_flags &= ~(ECF_CONST | ECF_PURE);
/* Compute the stack-size of this argument. */
if (args[i].reg == 0 || args[i].partial != 0
{
int unadjusted_args_size = args_size->constant;
+ /* For accumulate outgoing args mode we don't need to align, since the frame
+ will be already aligned. Align to STACK_BOUNDARY in order to prevent
+ backends from generating missaligned frame sizes. */
+#ifdef STACK_BOUNDARY
+ if (ACCUMULATE_OUTGOING_ARGS && preferred_stack_boundary > STACK_BOUNDARY)
+ preferred_stack_boundary = STACK_BOUNDARY;
+#endif
+
/* 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. */
#ifdef PREFERRED_STACK_BOUNDARY
preferred_stack_boundary /= BITS_PER_UNIT;
if (preferred_stack_boundary > 1)
- args_size->var = round_up (args_size->var, preferred_stack_boundary);
+ {
+ /* We don't handle this case yet. To handle it correctly we have
+ to add the delta, round and substract the delta.
+ Currently no machine description requires this support. */
+ if (stack_pointer_delta & (preferred_stack_boundary - 1))
+ abort();
+ args_size->var = round_up (args_size->var, preferred_stack_boundary);
+ }
#endif
if (reg_parm_stack_space > 0)
if (preferred_stack_boundary < 1)
preferred_stack_boundary = 1;
args_size->constant = (((args_size->constant
- + arg_space_so_far
- + pending_stack_adjust
+ + stack_pointer_delta
+ preferred_stack_boundary - 1)
/ preferred_stack_boundary
* preferred_stack_boundary)
- - arg_space_so_far
- - pending_stack_adjust);
+ - stack_pointer_delta);
#endif
args_size->constant = MAX (args_size->constant,
/* Precompute parameters as needed for a function call.
- IS_CONST indicates the target function is a pure function.
-
- MUST_PREALLOCATE indicates that we must preallocate stack space for
- any stack arguments.
+ FLAGS is mask of ECF_* constants.
NUM_ACTUALS is the number of arguments.
ARGS is an array containing information for each argument; this routine
- fills in the INITIAL_VALUE and VALUE fields for each precomputed argument.
-
- ARGS_SIZE contains information about the size of the arg list. */
+ fills in the INITIAL_VALUE and VALUE fields for each precomputed argument.
+ */
static void
-precompute_arguments (is_const, must_preallocate, num_actuals, args, args_size)
- int is_const;
- int must_preallocate;
+precompute_arguments (flags, num_actuals, args)
+ int flags;
int num_actuals;
struct arg_data *args;
- struct args_size *args_size;
{
int i;
on the stack, then we must precompute any parameter which contains a
function call which will store arguments on the stack.
Otherwise, evaluating the parameter may clobber previous parameters
- which have already been stored into the stack. */
+ which have already been stored into the stack. (we have code to avoid
+ such case by saving the ougoing stack arguments, but it results in
+ worse code) */
for (i = 0; i < num_actuals; i++)
- if (is_const
- || ((args_size->var != 0 || args_size->constant != 0)
- && calls_function (args[i].tree_value, 1))
- || (must_preallocate
- && (args_size->var != 0 || args_size->constant != 0)
- && calls_function (args[i].tree_value, 0)))
+ if ((flags & (ECF_CONST | ECF_PURE))
+ || calls_function (args[i].tree_value, !ACCUMULATE_OUTGOING_ARGS))
{
/* If this is an addressable type, we cannot pre-evaluate it. */
if (TREE_ADDRESSABLE (TREE_TYPE (args[i].tree_value)))
locations. The Irix 6 ABI has examples of this. */
if (GET_CODE (reg) == PARALLEL)
- {
- emit_group_load (reg, args[i].value,
- int_size_in_bytes (TREE_TYPE (args[i].tree_value)),
- (TYPE_ALIGN (TREE_TYPE (args[i].tree_value))
- / BITS_PER_UNIT));
- }
+ emit_group_load (reg, args[i].value,
+ int_size_in_bytes (TREE_TYPE (args[i].tree_value)),
+ TYPE_ALIGN (TREE_TYPE (args[i].tree_value)));
/* If simple case, just do move. If normal partial, store_one_arg
has already loaded the register for us. In all other cases,
}
}
+/* Try to integreate function. See expand_inline_function for documentation
+ about the parameters. */
+
+static rtx
+try_to_integrate (fndecl, actparms, target, ignore, type, structure_value_addr)
+ tree fndecl;
+ tree actparms;
+ rtx target;
+ int ignore;
+ tree type;
+ rtx structure_value_addr;
+{
+ rtx temp;
+ rtx before_call;
+ int i;
+ rtx old_stack_level = 0;
+ int reg_parm_stack_space = 0;
+
+#ifdef REG_PARM_STACK_SPACE
+#ifdef MAYBE_REG_PARM_STACK_SPACE
+ reg_parm_stack_space = MAYBE_REG_PARM_STACK_SPACE;
+#else
+ reg_parm_stack_space = REG_PARM_STACK_SPACE (fndecl);
+#endif
+#endif
+
+ before_call = get_last_insn ();
+
+ temp = expand_inline_function (fndecl, actparms, target,
+ ignore, type,
+ structure_value_addr);
+
+ /* If inlining succeeded, return. */
+ if (temp != (rtx) (HOST_WIDE_INT) - 1)
+ {
+ if (ACCUMULATE_OUTGOING_ARGS)
+ {
+ /* If the outgoing argument list must be preserved, push
+ the stack before executing the inlined function if it
+ makes any calls. */
+
+ for (i = reg_parm_stack_space - 1; i >= 0; i--)
+ if (i < highest_outgoing_arg_in_use && stack_usage_map[i] != 0)
+ break;
+
+ if (stack_arg_under_construction || i >= 0)
+ {
+ rtx first_insn
+ = before_call ? NEXT_INSN (before_call) : get_insns ();
+ rtx insn = NULL_RTX, seq;
+
+ /* Look for a call in the inline function code.
+ If DECL_SAVED_INSNS (fndecl)->outgoing_args_size is
+ nonzero then there is a call and it is not necessary
+ to scan the insns. */
+
+ if (DECL_SAVED_INSNS (fndecl)->outgoing_args_size == 0)
+ for (insn = first_insn; 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.
+
+ Add the stack space reserved for register arguments, if
+ any, 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. */
+
+ int adjust = (DECL_SAVED_INSNS (fndecl)->outgoing_args_size
+ + reg_parm_stack_space);
+
+ start_sequence ();
+ emit_stack_save (SAVE_BLOCK, &old_stack_level, NULL_RTX);
+ allocate_dynamic_stack_space (GEN_INT (adjust),
+ NULL_RTX, BITS_PER_UNIT);
+ seq = get_insns ();
+ end_sequence ();
+ emit_insns_before (seq, first_insn);
+ emit_stack_restore (SAVE_BLOCK, old_stack_level, NULL_RTX);
+ }
+ }
+ }
+
+ /* If the result is equivalent to TARGET, return TARGET to simplify
+ checks in store_expr. They can be equivalent but not equal in the
+ case of a function that returns BLKmode. */
+ if (temp != target && rtx_equal_p (temp, target))
+ return target;
+ return temp;
+ }
+
+ /* If inlining failed, mark FNDECL as needing to be compiled
+ separately after all. If function was declared inline,
+ give a warning. */
+ if (DECL_INLINE (fndecl) && warn_inline && !flag_no_inline
+ && optimize > 0 && !TREE_ADDRESSABLE (fndecl))
+ {
+ warning_with_decl (fndecl, "inlining failed in call to `%s'");
+ warning ("called from here");
+ }
+ mark_addressable (fndecl);
+ return (rtx) (HOST_WIDE_INT) - 1;
+}
+
+/* We need to pop PENDING_STACK_ADJUST bytes. But, if the arguments
+ wouldn't fill up an even multiple of PREFERRED_UNIT_STACK_BOUNDARY
+ bytes, then we would need to push some additional bytes to pad the
+ arguments. So, we compute an adjust to the stack pointer for an
+ amount that will leave the stack under-aligned by UNADJUSTED_ARGS_SIZE
+ bytes. Then, when the arguments are pushed the stack will be perfectly
+ aligned. ARGS_SIZE->CONSTANT is set to the number of bytes that should
+ be popped after the call. Returns the adjustment. */
+
+static int
+combine_pending_stack_adjustment_and_call (unadjusted_args_size,
+ args_size,
+ preferred_unit_stack_boundary)
+ int unadjusted_args_size;
+ struct args_size *args_size;
+ int preferred_unit_stack_boundary;
+{
+ /* The number of bytes to pop so that the stack will be
+ under-aligned by UNADJUSTED_ARGS_SIZE bytes. */
+ HOST_WIDE_INT adjustment;
+ /* The alignment of the stack after the arguments are pushed, if we
+ just pushed the arguments without adjust the stack here. */
+ HOST_WIDE_INT unadjusted_alignment;
+
+ unadjusted_alignment
+ = ((stack_pointer_delta + unadjusted_args_size)
+ % preferred_unit_stack_boundary);
+
+ /* We want to get rid of as many of the PENDING_STACK_ADJUST bytes
+ as possible -- leaving just enough left to cancel out the
+ UNADJUSTED_ALIGNMENT. In other words, we want to ensure that the
+ PENDING_STACK_ADJUST is non-negative, and congruent to
+ -UNADJUSTED_ALIGNMENT modulo the PREFERRED_UNIT_STACK_BOUNDARY. */
+
+ /* Begin by trying to pop all the bytes. */
+ unadjusted_alignment
+ = (unadjusted_alignment
+ - (pending_stack_adjust % preferred_unit_stack_boundary));
+ adjustment = pending_stack_adjust;
+ /* Push enough additional bytes that the stack will be aligned
+ after the arguments are pushed. */
+ if (unadjusted_alignment >= 0)
+ adjustment -= preferred_unit_stack_boundary - unadjusted_alignment;
+ else
+ adjustment += unadjusted_alignment;
+
+ /* Now, sets ARGS_SIZE->CONSTANT so that we pop the right number of
+ bytes after the call. The right number is the entire
+ PENDING_STACK_ADJUST less our ADJUSTMENT plus the amount required
+ by the arguments in the first place. */
+ args_size->constant
+ = pending_stack_adjust - adjustment + unadjusted_args_size;
+
+ return adjustment;
+}
+
/* Generate all the code for a function call
and return an rtx for its value.
Store the value in TARGET (specified as an rtx) if convenient.
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 try_tail_call;
int pass;
So the entire argument block must then be preallocated (i.e., we
ignore PUSH_ROUNDING in that case). */
-#ifdef PUSH_ROUNDING
- int must_preallocate = 0;
-#else
- int must_preallocate = 1;
-#endif
+ int must_preallocate = !PUSH_ARGS;
/* Size of the stack reserved for parameter registers. */
int reg_parm_stack_space = 0;
(on machines that lack push insns), or 0 if space not preallocated. */
rtx argblock = 0;
- /* Nonzero if it is plausible that this is a call to alloca. */
- int may_be_alloca;
- /* Nonzero if this is a call to malloc or a related function. */
- int is_malloc;
- /* Nonzero if this is a call to setjmp or a related function. */
- int returns_twice;
- /* Nonzero if this is a call to `longjmp'. */
- int is_longjmp;
- /* Nonzero if this is a syscall that makes a new process in the image of
- the current one. */
- int fork_or_exec;
+ /* Mask of ECF_ flags. */
+ int flags = 0;
/* Nonzero if this is a call to an inline function. */
int is_integrable = 0;
- /* Nonzero if this is a call to a `const' function.
- Note that only explicitly named functions are handled as `const' here. */
- int is_const = 0;
- /* Nonzero if this is a call to a `volatile' function. */
- int is_volatile = 0;
- /* Nonzero if this is a call to a function that won't throw an exception. */
- int nothrow = TREE_NOTHROW (exp);
-#if defined(ACCUMULATE_OUTGOING_ARGS) && defined(REG_PARM_STACK_SPACE)
+#ifdef REG_PARM_STACK_SPACE
/* Define the boundary of the register parm stack space that needs to be
save, if any. */
int low_to_save = -1, high_to_save;
rtx save_area = 0; /* Place that it is saved */
#endif
-#ifdef ACCUMULATE_OUTGOING_ARGS
int initial_highest_arg_in_use = highest_outgoing_arg_in_use;
char *initial_stack_usage_map = stack_usage_map;
int old_stack_arg_under_construction = 0;
-#endif
rtx old_stack_level = 0;
int old_pending_adj = 0;
int old_inhibit_defer_pop = inhibit_defer_pop;
+ int old_stack_allocated;
rtx call_fusage;
register tree p;
register int i;
- int preferred_stack_boundary;
+ /* The alignment of the stack, in bits. */
+ HOST_WIDE_INT preferred_stack_boundary;
+ /* The alignment of the stack, in bytes. */
+ HOST_WIDE_INT preferred_unit_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
if (current_function_check_memory_usage)
target = 0;
+ /* See if this is "nothrow" function call. */
+ if (TREE_NOTHROW (exp))
+ flags |= ECF_NOTHROW;
+
/* See if we can find a DECL-node for the actual function.
As a result, decide whether this is a call to an integrable function. */
- p = TREE_OPERAND (exp, 0);
- if (TREE_CODE (p) == ADDR_EXPR)
+ fndecl = get_callee_fndecl (exp);
+ if (fndecl)
{
- fndecl = TREE_OPERAND (p, 0);
- if (TREE_CODE (fndecl) != FUNCTION_DECL)
- fndecl = 0;
- else
+ if (!flag_no_inline
+ && fndecl != current_function_decl
+ && DECL_INLINE (fndecl)
+ && DECL_SAVED_INSNS (fndecl)
+ && DECL_SAVED_INSNS (fndecl)->inlinable)
+ is_integrable = 1;
+ else if (! TREE_ADDRESSABLE (fndecl))
{
- if (!flag_no_inline
- && fndecl != current_function_decl
- && DECL_INLINE (fndecl)
- && DECL_SAVED_INSNS (fndecl)
- && DECL_SAVED_INSNS (fndecl)->inlinable)
- is_integrable = 1;
- else if (! TREE_ADDRESSABLE (fndecl))
- {
- /* In case this function later becomes inlinable,
- record that there was already a non-inline call to it.
+ /* In case this function later becomes inlinable,
+ record that there was already a non-inline call to it.
- Use abstraction instead of setting TREE_ADDRESSABLE
- directly. */
- if (DECL_INLINE (fndecl) && warn_inline && !flag_no_inline
- && optimize > 0)
- {
- warning_with_decl (fndecl, "can't inline call to `%s'");
- warning ("called from here");
- }
- mark_addressable (fndecl);
+ Use abstraction instead of setting TREE_ADDRESSABLE
+ directly. */
+ if (DECL_INLINE (fndecl) && warn_inline && !flag_no_inline
+ && optimize > 0)
+ {
+ warning_with_decl (fndecl, "can't inline call to `%s'");
+ warning ("called from here");
}
-
- if (TREE_READONLY (fndecl) && ! TREE_THIS_VOLATILE (fndecl)
- && TYPE_MODE (TREE_TYPE (exp)) != VOIDmode)
- is_const = 1;
-
- if (TREE_THIS_VOLATILE (fndecl))
- is_volatile = 1;
-
- if (TREE_NOTHROW (fndecl))
- nothrow = 1;
+ mark_addressable (fndecl);
}
+
+ flags |= flags_from_decl_or_type (fndecl);
}
/* If we don't have specific function to call, see if we have a
- constant or `noreturn' function from the type. */
- if (fndecl == 0)
+ attributes set in the type. */
+ else
{
- is_const = TREE_READONLY (TREE_TYPE (TREE_TYPE (p)));
- is_volatile = TREE_THIS_VOLATILE (TREE_TYPE (TREE_TYPE (p)));
+ p = TREE_OPERAND (exp, 0);
+ flags |= flags_from_decl_or_type (TREE_TYPE (TREE_TYPE (p)));
}
#ifdef REG_PARM_STACK_SPACE
#endif
#endif
-#if defined(PUSH_ROUNDING) && ! defined(OUTGOING_REG_PARM_STACK_SPACE)
- if (reg_parm_stack_space > 0)
+#ifndef OUTGOING_REG_PARM_STACK_SPACE
+ if (reg_parm_stack_space > 0 && PUSH_ARGS)
must_preallocate = 1;
#endif
if (aggregate_value_p (exp))
{
/* This call returns a big structure. */
- is_const = 0;
+ flags &= ~(ECF_CONST | ECF_PURE);
#ifdef PCC_STATIC_STRUCT_RETURN
{
if (is_integrable)
{
- rtx temp;
+ rtx temp = try_to_integrate (fndecl, actparms, target,
+ ignore, TREE_TYPE (exp),
+ structure_value_addr);
+ if (temp != (rtx) (HOST_WIDE_INT) - 1)
+ return temp;
+ }
-#ifdef ACCUMULATE_OUTGOING_ARGS
- before_call = get_last_insn ();
-#endif
+ currently_expanding_call++;
- temp = expand_inline_function (fndecl, actparms, target,
- ignore, TREE_TYPE (exp),
- structure_value_addr);
+ /* Tail calls can make things harder to debug, and we're traditionally
+ pushed these optimizations into -O2. Don't try if we're already
+ expanding a call, as that means we're an argument. Similarly, if
+ there's pending loops or cleanups we know there's code to follow
+ the call.
- /* If inlining succeeded, return. */
- if (temp != (rtx) (HOST_WIDE_INT) -1)
- {
-#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 rtx_equal_function_value_matters is false, that means we've
+ finished with regular parsing. Which means that some of the
+ machinery we use to generate tail-calls is no longer in place.
+ This is most often true of sjlj-exceptions, which we couldn't
+ tail-call to anyway. */
- for (i = reg_parm_stack_space - 1; i >= 0; i--)
- if (i < highest_outgoing_arg_in_use && stack_usage_map[i] != 0)
- break;
+ try_tail_call = 0;
+ if (flag_optimize_sibling_calls
+ && currently_expanding_call == 1
+ && rtx_equal_function_value_matters
+ && stmt_loop_nest_empty ()
+ && ! any_pending_cleanups (1))
+ {
+ tree new_actparms = NULL_TREE;
- if (stack_arg_under_construction || i >= 0)
- {
- rtx first_insn
- = before_call ? NEXT_INSN (before_call) : get_insns ();
- rtx insn = NULL_RTX, seq;
+ /* Ok, we're going to give the tail call the old college try.
+ This means we're going to evaluate the function arguments
+ up to three times. There are two degrees of badness we can
+ encounter, those that can be unsaved and those that can't.
+ (See unsafe_for_reeval commentary for details.)
- /* Look for a call in the inline function code.
- If DECL_SAVED_INSNS (fndecl)->outgoing_args_size is
- nonzero then there is a call and it is not necessary
- to scan the insns. */
+ Generate a new argument list. Pass safe arguments through
+ unchanged. For the easy badness wrap them in UNSAVE_EXPRs.
+ For hard badness, evaluate them now and put their resulting
+ rtx in a temporary VAR_DECL. */
- if (DECL_SAVED_INSNS (fndecl)->outgoing_args_size == 0)
- for (insn = first_insn; insn; insn = NEXT_INSN (insn))
- if (GET_CODE (insn) == CALL_INSN)
- break;
+ for (p = actparms; p; p = TREE_CHAIN (p))
+ switch (unsafe_for_reeval (TREE_VALUE (p)))
+ {
+ case 0: /* Safe. */
+ new_actparms = tree_cons (TREE_PURPOSE (p), TREE_VALUE (p),
+ new_actparms);
+ 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.
-
- Add the stack space reserved for register arguments, if
- any, 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. */
-
- int adjust = (DECL_SAVED_INSNS (fndecl)->outgoing_args_size
- + reg_parm_stack_space);
-
- start_sequence ();
- emit_stack_save (SAVE_BLOCK, &old_stack_level, NULL_RTX);
- allocate_dynamic_stack_space (GEN_INT (adjust),
- NULL_RTX, BITS_PER_UNIT);
- seq = get_insns ();
- end_sequence ();
- emit_insns_before (seq, first_insn);
- emit_stack_restore (SAVE_BLOCK, old_stack_level, NULL_RTX);
- }
- }
-#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
- case of a function that returns BLKmode. */
- if (temp != target && rtx_equal_p (temp, target))
- return target;
- return temp;
- }
-
- /* If inlining failed, mark FNDECL as needing to be compiled
- separately after all. If function was declared inline,
- give a warning. */
- if (DECL_INLINE (fndecl) && warn_inline && !flag_no_inline
- && optimize > 0 && ! TREE_ADDRESSABLE (fndecl))
- {
- warning_with_decl (fndecl, "inlining failed in call to `%s'");
- warning ("called from here");
- }
- mark_addressable (fndecl);
- }
-
- currently_expanding_call++;
-
- /* Tail calls can make things harder to debug, and we're traditionally
- pushed these optimizations into -O2. Don't try if we're already
- expanding a call, as that means we're an argument. Similarly, if
- there's pending loops or cleanups we know there's code to follow
- the call. */
-
- try_tail_call = 0;
- if (optimize >= 2
- && currently_expanding_call == 1
- && stmt_loop_nest_empty ()
- && ! any_pending_cleanups (1))
- {
- tree new_actparms = NULL_TREE;
-
- /* Ok, we're going to give the tail call the old college try.
- This means we're going to evaluate the function arguments
- up to three times. There are two degrees of badness we can
- encounter, those that can be unsaved and those that can't.
- (See unsafe_for_reeval commentary for details.)
-
- Generate a new argument list. Pass safe arguments through
- unchanged. For the easy badness wrap them in UNSAVE_EXPRs.
- For hard badness, evaluate them now and put their resulting
- rtx in a temporary VAR_DECL. */
-
- for (p = actparms; p; p = TREE_CHAIN (p))
- switch (unsafe_for_reeval (TREE_VALUE (p)))
- {
- case 0: /* Safe. */
- new_actparms = tree_cons (TREE_PURPOSE (p), TREE_VALUE (p),
- new_actparms);
- break;
-
- case 1: /* Mildly unsafe. */
- new_actparms = tree_cons (TREE_PURPOSE (p),
- unsave_expr (TREE_VALUE (p)),
- new_actparms);
- break;
+ case 1: /* Mildly unsafe. */
+ new_actparms = tree_cons (TREE_PURPOSE (p),
+ unsave_expr (TREE_VALUE (p)),
+ new_actparms);
+ break;
case 2: /* Wildly unsafe. */
{
recursion call can be ignored if we indeed use the tail recursion
call expansion. */
int save_pending_stack_adjust = pending_stack_adjust;
+ int save_stack_pointer_delta = stack_pointer_delta;
/* 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
/* Restore the original pending stack adjustment for the sibling and
normal call cases below. */
pending_stack_adjust = save_pending_stack_adjust;
+ stack_pointer_delta = save_stack_pointer_delta;
}
function_call_count++;
if (fndecl && DECL_NAME (fndecl))
name = IDENTIFIER_POINTER (DECL_NAME (fndecl));
+ /* Figure out the amount to which the stack should be aligned. */
#ifdef PREFERRED_STACK_BOUNDARY
preferred_stack_boundary = PREFERRED_STACK_BOUNDARY;
#else
preferred_stack_boundary = STACK_BOUNDARY;
#endif
+ preferred_unit_stack_boundary = preferred_stack_boundary / BITS_PER_UNIT;
/* Ensure current function's preferred stack boundary is at least
what we need. We don't have to increase alignment for recursive
/* See if this is a call to a function that can return more than once
or a call to longjmp or malloc. */
- special_function_p (fndecl, &returns_twice, &is_longjmp, &fork_or_exec,
- &is_malloc, &may_be_alloca);
+ flags |= special_function_p (fndecl, flags);
- if (may_be_alloca)
+ if (flags & ECF_MAY_BE_ALLOCA)
current_function_calls_alloca = 1;
/* Operand 0 is a pointer-to-function; get the type of the function. */
recursion call can be ignored if we indeed use the tail recursion
call expansion. */
int save_pending_stack_adjust;
+ int save_stack_pointer_delta;
rtx insns;
rtx before_call, next_arg_reg;
|| ! FUNCTION_OK_FOR_SIBCALL (fndecl))
continue;
+ /* Emit any queued insns now; otherwise they would end up in
+ only one of the alternates. */
+ emit_queue ();
+
/* We know at this point that there are not currently any
pending cleanups. If, however, in the process of evaluating
the arguments we were to create some, we'll need to be
/* State variables we need to save and restore between
iterations. */
save_pending_stack_adjust = pending_stack_adjust;
+ save_stack_pointer_delta = stack_pointer_delta;
}
+ if (pass)
+ flags &= ~ECF_SIBCALL;
+ else
+ flags |= ECF_SIBCALL;
/* Other state variables that we must reinitialize each time
- through the loop (that are not initialized by the loop itself. */
+ through the loop (that are not initialized by the loop itself). */
argblock = 0;
call_fusage = 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)
+ if (flags & (ECF_CONST | ECF_PURE))
NO_DEFER_POP;
/* Don't let pending stack adjusts add up to too much.
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)
+ || (pending_stack_adjust > 0 && (flags & ECF_MAY_BE_ALLOCA))
|| pass == 0)
do_pending_stack_adjust ();
- if (profile_arc_flag && fork_or_exec)
+ if (profile_arc_flag && (flags & ECF_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
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
+ || (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);
n_named_args, actparms, fndecl,
&args_so_far, reg_parm_stack_space,
&old_stack_level, &old_pending_adj,
- &must_preallocate, &is_const,
- (pass == 0) ? ECF_SIBCALL : 0);
+ &must_preallocate, &flags);
#ifdef FINAL_REG_PARM_STACK_SPACE
reg_parm_stack_space = FINAL_REG_PARM_STACK_SPACE (args_size.constant,
Also do not make a sibling call. */
- is_const = 0;
+ flags &= ~(ECF_CONST | ECF_PURE);
must_preallocate = 1;
sibcall_failure = 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)
+ if (RETURN_POPS_ARGS (fndecl, funtype, args_size.constant)
!= RETURN_POPS_ARGS (current_function_decl,
TREE_TYPE (current_function_decl),
current_function_args_size))
|| reg_mentioned_p (virtual_outgoing_args_rtx,
structure_value_addr))
&& (args_size.var
-#ifndef ACCUMULATE_OUTGOING_ARGS
- || args_size.constant
-#endif
- ))
+ || (!ACCUMULATE_OUTGOING_ARGS && args_size.constant)))
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);
+ if (pass)
+ precompute_arguments (flags, num_actuals, args);
/* Now we are about to start emitting insns that can be deleted
if a libcall is deleted. */
- if (is_const || is_malloc)
+ if (flags & (ECF_CONST | ECF_PURE | ECF_MALLOC))
start_sequence ();
+ /* 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));
+
+ old_stack_allocated = stack_pointer_delta - pending_stack_adjust;
+
+ /* The argument block when performing a sibling call is the
+ incoming argument block. */
+ if (pass == 0)
+ argblock = virtual_incoming_args_rtx;
+
/* If we have no actual push instructions, or shouldn't use them,
make space for all args right now. */
-
- if (args_size.var != 0)
+ else if (args_size.var != 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;
-#endif
}
argblock = push_block (ARGS_SIZE_RTX (args_size), 0, 0);
}
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.
+ if (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.
- 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);
-
- 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;
-
- /* 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;
-
-#else /* not ACCUMULATE_OUTGOING_ARGS */
- if (inhibit_defer_pop == 0)
+ 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 != 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. */
+
+ argblock = virtual_outgoing_args_rtx;
+ }
+ else
{
- /* 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. */
+ needed
+ = (combine_pending_stack_adjustment_and_call
+ (unadjusted_args_size,
+ &args_size,
+ preferred_unit_stack_boundary));
+
+ /* combine_pending_stack_adjustment_and_call computes
+ an adjustment before the arguments are allocated.
+ Account for them and see whether or not the stack
+ needs to go up or down. */
+ needed = unadjusted_args_size - needed;
+
+ if (needed < 0)
+ {
+ /* We're releasing stack space. */
+ /* ??? We can avoid any adjustment at all if we're
+ already aligned. FIXME. */
+ pending_stack_adjust = -needed;
+ do_pending_stack_adjust ();
+ needed = 0;
+ }
+ else
+ /* We need to allocate space. We'll do that in
+ push_block below. */
+ pending_stack_adjust = 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);
+
+ /* 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)
{
- 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);
-#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)
- {
#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);
+ }
+ /* 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;
+ }
+ }
}
- 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;
- }
-#endif
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)
+ if (PUSH_ARGS_REVERSED && argblock == 0
+ && 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
+ if (pending_stack_adjust
+ && ! (flags & (ECF_CONST | ECF_PURE))
&& ! 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);
+ pending_stack_adjust
+ = (combine_pending_stack_adjustment_and_call
+ (unadjusted_args_size,
+ &args_size,
+ preferred_unit_stack_boundary));
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;
}
-#endif
+ /* Now that the stack is properly aligned, pops can't safely
+ be deferred during the evaluation of the arguments. */
+ NO_DEFER_POP;
#endif
/* Don't try to defer pops if preallocating, not even from the first arg,
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)
+#ifdef 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);
+ if (ACCUMULATE_OUTGOING_ARGS && pass)
+ 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.
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,
+ store_one_arg (&args[i], argblock, flags,
args_size.var != 0, reg_parm_stack_space);
/* If we have a parm that is passed in registers but not in memory
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,
+ store_one_arg (&args[i], argblock, flags,
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. */
- /* ??? Fix for arg_space_so_far. */
- if (argblock == 0)
+ if (!PUSH_ARGS_REVERSED && 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 ! defined(ACCUMULATE_OUTGOING_ARGS) && defined(OUTGOING_REG_PARM_STACK_SPACE)
- if (must_preallocate == 0 && reg_parm_stack_space > 0)
+#ifdef OUTGOING_REG_PARM_STACK_SPACE
+ if (!ACCUMULATE_OUTGOING_ARGS
+ && must_preallocate == 0 && reg_parm_stack_space > 0)
anti_adjust_stack (GEN_INT (reg_parm_stack_space));
#endif
/* All arguments and registers used for the call must be set up by
now! */
+#ifdef PREFERRED_STACK_BOUNDARY
+ /* Stack must be properly aligned now. */
+ if (pass && stack_pointer_delta % preferred_unit_stack_boundary)
+ abort();
+#endif
+
/* Generate the actual call instruction. */
emit_call_1 (funexp, fndecl, funtype, unadjusted_args_size,
args_size.constant, struct_value_size,
next_arg_reg, valreg, old_inhibit_defer_pop, call_fusage,
- ((is_const ? ECF_IS_CONST : 0)
- | (nothrow ? ECF_NOTHROW : 0)
- | (pass == 0 ? ECF_SIBCALL : 0)));
+ flags);
+
+ /* Verify that we've deallocated all the stack we used. */
+ if (pass
+ && old_stack_allocated != stack_pointer_delta - pending_stack_adjust)
+ abort();
/* 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)
+ if ((flags & (ECF_CONST | ECF_PURE))
+ && valreg != 0 && GET_CODE (valreg) != PARALLEL)
{
rtx note = 0;
rtx temp = gen_reg_rtx (GET_MODE (valreg));
/* 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);
- }
+ mark_reg_pointer (temp, TYPE_ALIGN (TREE_TYPE (TREE_TYPE (exp))));
/* 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);
-#else
- 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 ();
+ if (flags & ECF_PURE)
+ note = gen_rtx_EXPR_LIST (VOIDmode,
+ gen_rtx_USE (VOIDmode,
+ gen_rtx_MEM (BLKmode,
+ gen_rtx_SCRATCH (VOIDmode))), note);
+
emit_libcall_block (insns, temp, valreg, note);
valreg = temp;
}
- else if (is_const)
+ else if (flags & (ECF_CONST | ECF_PURE))
{
/* Otherwise, just write out the sequence without a note. */
rtx insns = get_insns ();
end_sequence ();
emit_insns (insns);
}
- else if (is_malloc)
+ else if (flags & ECF_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);
+ mark_reg_pointer (temp, BIGGEST_ALIGNMENT);
emit_move_insn (temp, valreg);
if nonvolatile values are live. For functions that cannot return,
inform flow that control does not fall through. */
- if (returns_twice || is_volatile || is_longjmp || pass == 0)
+ if ((flags & (ECF_RETURNS_TWICE | ECF_NORETURN | ECF_LONGJMP)) || pass == 0)
{
/* The barrier or NOTE_INSN_SETJMP note must be emitted
immediately after the CALL_INSN. Some ports emit more
abort ();
}
- if (returns_twice)
+ if (flags & ECF_RETURNS_TWICE)
{
emit_note_after (NOTE_INSN_SETJMP, last);
current_function_calls_setjmp = 1;
emit_barrier_after (last);
}
- if (is_longjmp)
+ if (flags & ECF_LONGJMP)
current_function_calls_longjmp = 1, sibcall_failure = 1;
/* If this function is returning into a memory location marked as
if (! rtx_equal_p (target, valreg))
emit_group_store (target, valreg, bytes,
- TYPE_ALIGN (TREE_TYPE (exp)) / BITS_PER_UNIT);
+ TYPE_ALIGN (TREE_TYPE (exp)));
+
/* We can not support sibling calls for this case. */
sibcall_failure = 1;
}
{
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;
-#endif
sibcall_failure = 1;
}
-#ifdef ACCUMULATE_OUTGOING_ARGS
- else
+ else if (ACCUMULATE_OUTGOING_ARGS && pass)
{
#ifdef REG_PARM_STACK_SPACE
if (save_area)
{
restore_fixed_argument_area (save_area, argblock,
high_to_save, low_to_save);
- sibcall_failure = 1;
}
#endif
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;
+ PARM_BOUNDARY);
}
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 (may_be_alloca && nonlocal_goto_handler_slots != 0)
+ if ((flags & ECF_MAY_BE_ALLOCA) && nonlocal_goto_handler_slots != 0)
emit_stack_save (SAVE_NONLOCAL, &nonlocal_goto_stack_level, NULL_RTX);
pop_temp_slots ();
zero out the sequence. */
if (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;
+ stack_pointer_delta = save_stack_pointer_delta;
}
else
normal_call_insns = insns;
The RETVAL parameter specifies whether return value needs to be saved, other
parameters are documented in the emit_library_call function bellow. */
static rtx
-emit_library_call_value_1 (retval, orgfun, value, no_queue, outmode, nargs, p)
+emit_library_call_value_1 (retval, orgfun, value, fn_type, outmode, nargs, p)
int retval;
rtx orgfun;
rtx value;
- int no_queue;
+ int fn_type;
enum machine_mode outmode;
int nargs;
va_list p;
int old_inhibit_defer_pop = inhibit_defer_pop;
rtx call_fusage = 0;
rtx mem_value = 0;
+ rtx valreg;
int pcc_struct_value = 0;
int struct_value_size = 0;
- int is_const;
+ int flags = 0;
int reg_parm_stack_space = 0;
- int nothrow;
-#ifdef ACCUMULATE_OUTGOING_ARGS
int needed;
-#endif
-#if defined(ACCUMULATE_OUTGOING_ARGS) && defined(REG_PARM_STACK_SPACE)
+#ifdef REG_PARM_STACK_SPACE
/* Define the boundary of the register parm stack space that needs to be
save, if any. */
int low_to_save = -1, high_to_save = 0;
rtx save_area = 0; /* Place that it is saved */
#endif
-#ifdef ACCUMULATE_OUTGOING_ARGS
/* Size of the stack reserved for parameter registers. */
int initial_highest_arg_in_use = highest_outgoing_arg_in_use;
char *initial_stack_usage_map = stack_usage_map;
-#endif
#ifdef REG_PARM_STACK_SPACE
#ifdef MAYBE_REG_PARM_STACK_SPACE
#endif
#endif
- is_const = no_queue;
+ if (fn_type == 1)
+ flags |= ECF_CONST;
+ else if (fn_type == 2)
+ flags |= ECF_PURE;
fun = orgfun;
- nothrow = libfunc_nothrow (fun);
+ if (libfunc_nothrow (fun))
+ flags |= ECF_NOTHROW;
#ifdef PREFERRED_STACK_BOUNDARY
/* Ensure current function's preferred stack boundary is at least
#endif
/* This call returns a big structure. */
- is_const = 0;
+ flags &= ~(ECF_CONST | ECF_PURE);
}
/* ??? Unfinished: must pass the memory address as an argument. */
count = 0;
+ /* Now we are about to start emitting insns that can be deleted
+ if a libcall is deleted. */
+ if (flags & (ECF_CONST | ECF_PURE))
+ start_sequence ();
+
push_temp_slots ();
/* If there's a structure value address to be passed,
#endif
locate_and_pad_parm (Pmode, NULL_TREE,
- argvec[count].reg && argvec[count].partial == 0,
+#ifdef STACK_PARMS_IN_REG_PARM_AREA
+ 1,
+#else
+ argvec[count].reg != 0,
+#endif
NULL_TREE, &args_size, &argvec[count].offset,
&argvec[count].size, &alignment_pad);
#endif
locate_and_pad_parm (mode, NULL_TREE,
- argvec[count].reg && argvec[count].partial == 0,
+#ifdef STACK_PARMS_IN_REG_PARM_AREA
+ 1,
+#else
+ argvec[count].reg != 0,
+#endif
NULL_TREE, &args_size, &argvec[count].offset,
&argvec[count].size, &alignment_pad);
original_args_size = args_size;
#ifdef PREFERRED_STACK_BOUNDARY
- args_size.constant = (((args_size.constant + (STACK_BYTES - 1))
- / STACK_BYTES) * STACK_BYTES);
+ args_size.constant = (((args_size.constant
+ + stack_pointer_delta
+ + STACK_BYTES - 1)
+ / STACK_BYTES
+ * STACK_BYTES)
+ - stack_pointer_delta);
#endif
args_size.constant = MAX (args_size.constant,
if (args_size.constant > current_function_outgoing_args_size)
current_function_outgoing_args_size = args_size.constant;
-#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.
+ if (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.
- 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.
+ 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.
- 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. */
- needed = args_size.constant;
+ needed = args_size.constant;
#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);
-#endif
- 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 != 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.
- */
-
- argblock = virtual_outgoing_args_rtx;
-#else /* not ACCUMULATE_OUTGOING_ARGS */
-#ifndef PUSH_ROUNDING
- argblock = push_block (GEN_INT (args_size.constant), 0, 0);
-#endif
+ highest_outgoing_arg_in_use = MAX (initial_highest_arg_in_use,
+ needed);
#endif
+ 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 != 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.
+ */
+
+ argblock = virtual_outgoing_args_rtx;
+ }
+ else
+ {
+ if (!PUSH_ARGS)
+ argblock = push_block (GEN_INT (args_size.constant), 0, 0);
+ }
-#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 (argblock == 0)
+ if (argblock == 0 && PUSH_ARGS_REVERSED)
anti_adjust_stack (GEN_INT (args_size.constant
- original_args_size.constant));
#endif
-#endif
-#ifdef PUSH_ARGS_REVERSED
- inc = -1;
- argnum = nargs - 1;
-#else
- inc = 1;
- argnum = 0;
-#endif
+ if (PUSH_ARGS_REVERSED)
+ {
+ inc = -1;
+ argnum = nargs - 1;
+ }
+ else
+ {
+ inc = 1;
+ argnum = 0;
+ }
-#if defined(ACCUMULATE_OUTGOING_ARGS) && defined(REG_PARM_STACK_SPACE)
- /* The argument list is the property of the called routine and it
- may clobber it. If the fixed area has been used for previous
- parameters, we must save and restore it.
+#ifdef REG_PARM_STACK_SPACE
+ if (ACCUMULATE_OUTGOING_ARGS)
+ {
+ /* The argument list is the property of the called routine and it
+ may clobber it. If the fixed area has been used for previous
+ parameters, we must save and restore it.
- Here we compute the boundary of the that needs to be saved, if any. */
+ Here we compute the boundary of the that needs to be saved, if any. */
#ifdef ARGS_GROW_DOWNWARD
- for (count = 0; count < reg_parm_stack_space + 1; count++)
+ for (count = 0; count < reg_parm_stack_space + 1; count++)
#else
- for (count = 0; count < reg_parm_stack_space; count++)
+ for (count = 0; count < reg_parm_stack_space; count++)
#endif
- {
- if (count >= highest_outgoing_arg_in_use
- || stack_usage_map[count] == 0)
- continue;
+ {
+ if (count >= highest_outgoing_arg_in_use
+ || stack_usage_map[count] == 0)
+ continue;
- if (low_to_save == -1)
- low_to_save = count;
+ if (low_to_save == -1)
+ low_to_save = count;
- high_to_save = count;
- }
+ high_to_save = count;
+ }
- if (low_to_save >= 0)
- {
- int num_to_save = high_to_save - low_to_save + 1;
- enum machine_mode save_mode
- = mode_for_size (num_to_save * BITS_PER_UNIT, MODE_INT, 1);
- rtx stack_area;
+ if (low_to_save >= 0)
+ {
+ int num_to_save = high_to_save - low_to_save + 1;
+ enum machine_mode save_mode
+ = mode_for_size (num_to_save * BITS_PER_UNIT, MODE_INT, 1);
+ rtx stack_area;
- /* If we don't have the required alignment, must do this in BLKmode. */
- if ((low_to_save & (MIN (GET_MODE_SIZE (save_mode),
- BIGGEST_ALIGNMENT / UNITS_PER_WORD) - 1)))
- save_mode = BLKmode;
+ /* If we don't have the required alignment, must do this in BLKmode. */
+ if ((low_to_save & (MIN (GET_MODE_SIZE (save_mode),
+ BIGGEST_ALIGNMENT / UNITS_PER_WORD) - 1)))
+ save_mode = BLKmode;
#ifdef ARGS_GROW_DOWNWARD
- stack_area = gen_rtx_MEM (save_mode,
- memory_address (save_mode,
- plus_constant (argblock,
- - high_to_save)));
+ stack_area = gen_rtx_MEM (save_mode,
+ memory_address (save_mode,
+ plus_constant (argblock,
+ - high_to_save)));
#else
- stack_area = gen_rtx_MEM (save_mode,
- memory_address (save_mode,
- plus_constant (argblock,
- low_to_save)));
+ stack_area = gen_rtx_MEM (save_mode,
+ memory_address (save_mode,
+ plus_constant (argblock,
+ low_to_save)));
#endif
- if (save_mode == BLKmode)
- {
- save_area = assign_stack_temp (BLKmode, num_to_save, 0);
- emit_block_move (validize_mem (save_area), stack_area,
- GEN_INT (num_to_save),
- PARM_BOUNDARY / BITS_PER_UNIT);
- }
- else
- {
- save_area = gen_reg_rtx (save_mode);
- emit_move_insn (save_area, stack_area);
+ if (save_mode == BLKmode)
+ {
+ save_area = assign_stack_temp (BLKmode, num_to_save, 0);
+ emit_block_move (validize_mem (save_area), stack_area,
+ GEN_INT (num_to_save), PARM_BOUNDARY);
+ }
+ else
+ {
+ save_area = gen_reg_rtx (save_mode);
+ emit_move_insn (save_area, stack_area);
+ }
}
}
#endif
register rtx val = argvec[argnum].value;
rtx reg = argvec[argnum].reg;
int partial = argvec[argnum].partial;
-#ifdef ACCUMULATE_OUTGOING_ARGS
- int lower_bound, upper_bound, i;
-#endif
+ int lower_bound = 0, upper_bound = 0, i;
if (! (reg != 0 && partial == 0))
{
-#ifdef ACCUMULATE_OUTGOING_ARGS
- /* If this is being stored into a pre-allocated, fixed-size, stack
- area, save any previous data at that location. */
+ if (ACCUMULATE_OUTGOING_ARGS)
+ {
+ /* If this is being stored into a pre-allocated, fixed-size,
+ stack area, save any previous data at that location. */
#ifdef ARGS_GROW_DOWNWARD
- /* stack_slot is negative, but we want to index stack_usage_map
- with positive values. */
- upper_bound = -argvec[argnum].offset.constant + 1;
- lower_bound = upper_bound - argvec[argnum].size.constant;
+ /* stack_slot is negative, but we want to index stack_usage_map
+ with positive values. */
+ upper_bound = -argvec[argnum].offset.constant + 1;
+ lower_bound = upper_bound - argvec[argnum].size.constant;
#else
- lower_bound = argvec[argnum].offset.constant;
- upper_bound = lower_bound + argvec[argnum].size.constant;
+ lower_bound = argvec[argnum].offset.constant;
+ upper_bound = lower_bound + argvec[argnum].size.constant;
#endif
- for (i = lower_bound; i < upper_bound; i++)
- if (stack_usage_map[i]
- /* Don't store things in the fixed argument area at this point;
- it has already been saved. */
- && i > reg_parm_stack_space)
- break;
+ for (i = lower_bound; i < upper_bound; i++)
+ if (stack_usage_map[i]
+ /* Don't store things in the fixed argument area at this
+ point; it has already been saved. */
+ && i > reg_parm_stack_space)
+ break;
- if (i != upper_bound)
- {
- /* We need to make a save area. See what mode we can make it. */
- enum machine_mode save_mode
- = mode_for_size (argvec[argnum].size.constant * BITS_PER_UNIT,
- MODE_INT, 1);
- rtx stack_area
- = gen_rtx_MEM
- (save_mode,
- memory_address
- (save_mode,
- plus_constant (argblock,
- argvec[argnum].offset.constant)));
- argvec[argnum].save_area = gen_reg_rtx (save_mode);
-
- emit_move_insn (argvec[argnum].save_area, stack_area);
+ if (i != upper_bound)
+ {
+ /* We need to make a save area. See what mode we can make
+ it. */
+ enum machine_mode save_mode
+ = mode_for_size (argvec[argnum].size.constant
+ * BITS_PER_UNIT,
+ MODE_INT, 1);
+ rtx stack_area
+ = gen_rtx_MEM
+ (save_mode,
+ memory_address
+ (save_mode,
+ plus_constant (argblock,
+ argvec[argnum].offset.constant)));
+ argvec[argnum].save_area = gen_reg_rtx (save_mode);
+
+ emit_move_insn (argvec[argnum].save_area, stack_area);
+ }
}
-#endif
+
emit_push_insn (val, mode, NULL_TREE, NULL_RTX, 0, partial, reg, 0,
argblock, GEN_INT (argvec[argnum].offset.constant),
reg_parm_stack_space, ARGS_SIZE_RTX (alignment_pad));
-#ifdef ACCUMULATE_OUTGOING_ARGS
/* Now mark the segment we just used. */
- for (i = lower_bound; i < upper_bound; i++)
- stack_usage_map[i] = 1;
-#endif
+ if (ACCUMULATE_OUTGOING_ARGS)
+ for (i = lower_bound; i < upper_bound; i++)
+ stack_usage_map[i] = 1;
NO_DEFER_POP;
}
}
-#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)
+ if (argblock == 0 && !PUSH_ARGS_REVERSED)
anti_adjust_stack (GEN_INT (args_size.constant
- original_args_size.constant));
#endif
-#endif
-#ifdef PUSH_ARGS_REVERSED
- argnum = nargs - 1;
-#else
- argnum = 0;
-#endif
+ if (PUSH_ARGS_REVERSED)
+ argnum = nargs - 1;
+ else
+ argnum = 0;
fun = prepare_call_address (fun, NULL_TREE, &call_fusage, 0);
NO_DEFER_POP;
}
-#if 0
- /* For version 1.37, try deleting this entirely. */
- if (! no_queue)
- emit_queue ();
-#endif
-
/* Any regs containing parms remain in use through the call. */
for (count = 0; count < nargs; count++)
{
/* Don't allow popping to be deferred, since then
cse'ing of library calls could delete a call and leave the pop. */
NO_DEFER_POP;
+ valreg = (mem_value == 0 && outmode != VOIDmode
+ ? hard_libcall_value (outmode) : NULL_RTX);
+
+#ifdef PREFERRED_STACK_BOUNDARY
+ /* Stack must be properly aligned now. */
+ if (stack_pointer_delta & (PREFERRED_STACK_BOUNDARY / BITS_PER_UNIT - 1))
+ abort();
+#endif
/* We pass the old value of inhibit_defer_pop + 1 to emit_call_1, which
will set inhibit_defer_pop to that value. */
original_args_size.constant, args_size.constant,
struct_value_size,
FUNCTION_ARG (args_so_far, VOIDmode, void_type_node, 1),
- mem_value == 0 && outmode != VOIDmode ? hard_libcall_value (outmode) : NULL_RTX,
- old_inhibit_defer_pop + 1, call_fusage,
- ((is_const ? ECF_IS_CONST : 0)
- | (nothrow ? ECF_NOTHROW : 0)));
+ valreg,
+ old_inhibit_defer_pop + 1, call_fusage, flags);
/* Now restore inhibit_defer_pop to its actual original value. */
OK_DEFER_POP;
+ /* 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 ((flags & (ECF_CONST | ECF_PURE))
+ && valreg != 0 && GET_CODE (valreg) != PARALLEL)
+ {
+ rtx note = 0;
+ rtx temp = gen_reg_rtx (GET_MODE (valreg));
+ rtx insns;
+ int i;
+
+ /* Construct an "equal form" for the value which mentions all the
+ arguments in order as well as the function name. */
+ for (i = 0; i < nargs; i++)
+ note = gen_rtx_EXPR_LIST (VOIDmode, argvec[i].value, note);
+ note = gen_rtx_EXPR_LIST (VOIDmode, fun, note);
+
+ insns = get_insns ();
+ end_sequence ();
+
+ if (flags & ECF_PURE)
+ note = gen_rtx_EXPR_LIST (VOIDmode,
+ gen_rtx_USE (VOIDmode,
+ gen_rtx_MEM (BLKmode,
+ gen_rtx_SCRATCH (VOIDmode))), note);
+
+ emit_libcall_block (insns, temp, valreg, note);
+
+ valreg = temp;
+ }
+ else if (flags & (ECF_CONST | ECF_PURE))
+ {
+ /* Otherwise, just write out the sequence without a note. */
+ rtx insns = get_insns ();
+
+ end_sequence ();
+ emit_insns (insns);
+ }
pop_temp_slots ();
/* Copy the value to the right place. */
value = hard_libcall_value (outmode);
}
-#ifdef ACCUMULATE_OUTGOING_ARGS
-#ifdef REG_PARM_STACK_SPACE
- if (save_area)
+ if (ACCUMULATE_OUTGOING_ARGS)
{
- enum machine_mode save_mode = GET_MODE (save_area);
+#ifdef REG_PARM_STACK_SPACE
+ if (save_area)
+ {
+ enum machine_mode save_mode = GET_MODE (save_area);
#ifdef ARGS_GROW_DOWNWARD
- rtx stack_area
- = gen_rtx_MEM (save_mode,
- memory_address (save_mode,
- plus_constant (argblock,
- - high_to_save)));
+ rtx stack_area
+ = gen_rtx_MEM (save_mode,
+ memory_address (save_mode,
+ plus_constant (argblock,
+ - high_to_save)));
#else
- rtx stack_area
- = gen_rtx_MEM (save_mode,
- memory_address (save_mode,
- plus_constant (argblock, low_to_save)));
+ rtx stack_area
+ = gen_rtx_MEM (save_mode,
+ memory_address (save_mode,
+ plus_constant (argblock, low_to_save)));
#endif
- if (save_mode != BLKmode)
- emit_move_insn (stack_area, save_area);
- else
- emit_block_move (stack_area, validize_mem (save_area),
- GEN_INT (high_to_save - low_to_save + 1),
- PARM_BOUNDARY / BITS_PER_UNIT);
- }
+ if (save_mode != BLKmode)
+ emit_move_insn (stack_area, save_area);
+ else
+ emit_block_move (stack_area, validize_mem (save_area),
+ GEN_INT (high_to_save - low_to_save + 1),
+ PARM_BOUNDARY);
+ }
#endif
-
- /* If we saved any argument areas, restore them. */
- for (count = 0; count < nargs; count++)
- if (argvec[count].save_area)
- {
- enum machine_mode save_mode = GET_MODE (argvec[count].save_area);
- rtx stack_area
- = gen_rtx_MEM (save_mode,
- memory_address
- (save_mode,
- plus_constant (argblock,
- argvec[count].offset.constant)));
-
- emit_move_insn (stack_area, argvec[count].save_area);
- }
+
+ /* If we saved any argument areas, restore them. */
+ for (count = 0; count < nargs; count++)
+ if (argvec[count].save_area)
+ {
+ enum machine_mode save_mode = GET_MODE (argvec[count].save_area);
+ rtx stack_area
+ = gen_rtx_MEM (save_mode,
+ memory_address
+ (save_mode,
+ plus_constant (argblock,
+ argvec[count].offset.constant)));
+
+ emit_move_insn (stack_area, argvec[count].save_area);
+ }
- highest_outgoing_arg_in_use = initial_highest_arg_in_use;
- stack_usage_map = initial_stack_usage_map;
-#endif
+ highest_outgoing_arg_in_use = initial_highest_arg_in_use;
+ stack_usage_map = initial_stack_usage_map;
+ }
return value;
and machine_modes to convert them to.
The rtx values should have been passed through protect_from_queue already.
- NO_QUEUE will be true if and only if the library call is a `const' call
- which will be enclosed in REG_LIBCALL/REG_RETVAL notes; it is equivalent
- to the variable is_const in expand_call.
-
- NO_QUEUE must be true for const calls, because if it isn't, then
- any pending increment will be emitted between REG_LIBCALL/REG_RETVAL notes,
- and will be lost if the libcall sequence is optimized away.
-
- NO_QUEUE must be false for non-const calls, because if it isn't, the
- call insn will have its CONST_CALL_P bit set, and it will be incorrectly
- optimized. For instance, the instruction scheduler may incorrectly
- move memory references across the non-const call. */
+ FN_TYPE will is zero for `normal' calls, one for `const' calls, wich
+ which will be enclosed in REG_LIBCALL/REG_RETVAL notes and two for `pure'
+ calls, that are handled like `const' calls with extra
+ (use (memory (scratch)). */
void
-emit_library_call VPARAMS((rtx orgfun, int no_queue, enum machine_mode outmode,
+emit_library_call VPARAMS((rtx orgfun, int fn_type, enum machine_mode outmode,
int nargs, ...))
{
#ifndef ANSI_PROTOTYPES
rtx orgfun;
- int no_queue;
+ int fn_type;
enum machine_mode outmode;
int nargs;
#endif
#ifndef ANSI_PROTOTYPES
orgfun = va_arg (p, rtx);
- no_queue = va_arg (p, int);
+ fn_type = va_arg (p, int);
outmode = va_arg (p, enum machine_mode);
nargs = va_arg (p, int);
#endif
- emit_library_call_value_1 (0, orgfun, NULL_RTX, no_queue, outmode, nargs, p);
+ emit_library_call_value_1 (0, orgfun, NULL_RTX, fn_type, outmode, nargs, p);
va_end (p);
}
If VALUE is nonzero, VALUE is returned. */
rtx
-emit_library_call_value VPARAMS((rtx orgfun, rtx value, int no_queue,
+emit_library_call_value VPARAMS((rtx orgfun, rtx value, int fn_type,
enum machine_mode outmode, int nargs, ...))
{
#ifndef ANSI_PROTOTYPES
rtx orgfun;
rtx value;
- int no_queue;
+ int fn_type;
enum machine_mode outmode;
int nargs;
#endif
#ifndef ANSI_PROTOTYPES
orgfun = va_arg (p, rtx);
value = va_arg (p, rtx);
- no_queue = va_arg (p, int);
+ fn_type = va_arg (p, int);
outmode = va_arg (p, enum machine_mode);
nargs = va_arg (p, int);
#endif
- value = emit_library_call_value_1 (1, orgfun, value, no_queue, outmode, nargs, p);
+ value = emit_library_call_value_1 (1, orgfun, value, fn_type, outmode, nargs, p);
va_end (p);
FNDECL is the declaration of the function we are calling. */
static void
-store_one_arg (arg, argblock, may_be_alloca, variable_size,
+store_one_arg (arg, argblock, flags, variable_size,
reg_parm_stack_space)
struct arg_data *arg;
rtx argblock;
- int may_be_alloca;
+ int flags;
int variable_size ATTRIBUTE_UNUSED;
int reg_parm_stack_space;
{
rtx reg = 0;
int partial = 0;
int used = 0;
-#ifdef ACCUMULATE_OUTGOING_ARGS
int i, lower_bound = 0, upper_bound = 0;
-#endif
if (TREE_CODE (pval) == ERROR_MARK)
return;
this argument. */
push_temp_slots ();
-#ifdef ACCUMULATE_OUTGOING_ARGS
- /* If this is being stored into a pre-allocated, fixed-size, stack area,
- save any previous data at that location. */
- if (argblock && ! variable_size && arg->stack)
+ if (ACCUMULATE_OUTGOING_ARGS && !(flags & ECF_SIBCALL))
{
+ /* If this is being stored into a pre-allocated, fixed-size, stack area,
+ save any previous data at that location. */
+ if (argblock && ! variable_size && arg->stack)
+ {
#ifdef ARGS_GROW_DOWNWARD
- /* stack_slot is negative, but we want to index stack_usage_map
- with positive values. */
- if (GET_CODE (XEXP (arg->stack_slot, 0)) == PLUS)
- upper_bound = -INTVAL (XEXP (XEXP (arg->stack_slot, 0), 1)) + 1;
- else
- upper_bound = 0;
+ /* stack_slot is negative, but we want to index stack_usage_map
+ with positive values. */
+ if (GET_CODE (XEXP (arg->stack_slot, 0)) == PLUS)
+ upper_bound = -INTVAL (XEXP (XEXP (arg->stack_slot, 0), 1)) + 1;
+ else
+ upper_bound = 0;
- lower_bound = upper_bound - arg->size.constant;
+ lower_bound = upper_bound - arg->size.constant;
#else
- if (GET_CODE (XEXP (arg->stack_slot, 0)) == PLUS)
- lower_bound = INTVAL (XEXP (XEXP (arg->stack_slot, 0), 1));
- else
- lower_bound = 0;
+ if (GET_CODE (XEXP (arg->stack_slot, 0)) == PLUS)
+ lower_bound = INTVAL (XEXP (XEXP (arg->stack_slot, 0), 1));
+ else
+ lower_bound = 0;
- upper_bound = lower_bound + arg->size.constant;
+ upper_bound = lower_bound + arg->size.constant;
#endif
- for (i = lower_bound; i < upper_bound; i++)
- if (stack_usage_map[i]
- /* Don't store things in the fixed argument area at this point;
- it has already been saved. */
- && i > reg_parm_stack_space)
- break;
-
- if (i != upper_bound)
- {
- /* We need to make a save area. See what mode we can make it. */
- enum machine_mode save_mode
- = mode_for_size (arg->size.constant * BITS_PER_UNIT, MODE_INT, 1);
- rtx stack_area
- = gen_rtx_MEM (save_mode,
- memory_address (save_mode,
- XEXP (arg->stack_slot, 0)));
+ for (i = lower_bound; i < upper_bound; i++)
+ if (stack_usage_map[i]
+ /* Don't store things in the fixed argument area at this point;
+ it has already been saved. */
+ && i > reg_parm_stack_space)
+ break;
- if (save_mode == BLKmode)
- {
- arg->save_area = assign_stack_temp (BLKmode,
- arg->size.constant, 0);
- MEM_SET_IN_STRUCT_P (arg->save_area,
- AGGREGATE_TYPE_P (TREE_TYPE
- (arg->tree_value)));
- preserve_temp_slots (arg->save_area);
- emit_block_move (validize_mem (arg->save_area), stack_area,
- GEN_INT (arg->size.constant),
- PARM_BOUNDARY / BITS_PER_UNIT);
- }
- else
+ if (i != upper_bound)
{
- arg->save_area = gen_reg_rtx (save_mode);
- emit_move_insn (arg->save_area, stack_area);
+ /* We need to make a save area. See what mode we can make it. */
+ enum machine_mode save_mode
+ = mode_for_size (arg->size.constant * BITS_PER_UNIT, MODE_INT, 1);
+ rtx stack_area
+ = gen_rtx_MEM (save_mode,
+ memory_address (save_mode,
+ XEXP (arg->stack_slot, 0)));
+
+ if (save_mode == BLKmode)
+ {
+ arg->save_area = assign_stack_temp (BLKmode,
+ arg->size.constant, 0);
+ MEM_SET_IN_STRUCT_P (arg->save_area,
+ AGGREGATE_TYPE_P (TREE_TYPE
+ (arg->tree_value)));
+ preserve_temp_slots (arg->save_area);
+ emit_block_move (validize_mem (arg->save_area), stack_area,
+ GEN_INT (arg->size.constant),
+ PARM_BOUNDARY);
+ }
+ else
+ {
+ arg->save_area = gen_reg_rtx (save_mode);
+ emit_move_insn (arg->save_area, stack_area);
+ }
}
}
+ /* Now that we have saved any slots that will be overwritten by this
+ store, mark all slots this store will use. We must do this before
+ we actually expand the argument since the expansion itself may
+ trigger library calls which might need to use the same stack slot. */
+ if (argblock && ! variable_size && arg->stack)
+ for (i = lower_bound; i < upper_bound; i++)
+ stack_usage_map[i] = 1;
}
- /* Now that we have saved any slots that will be overwritten by this
- store, mark all slots this store will use. We must do this before
- we actually expand the argument since the expansion itself may
- trigger library calls which might need to use the same stack slot. */
- if (argblock && ! variable_size && arg->stack)
- for (i = lower_bound; i < upper_bound; i++)
- stack_usage_map[i] = 1;
-#endif
-
/* If this isn't going to be placed on both the stack and in registers,
set up the register and number of words. */
if (! arg->pass_on_stack)
it directly into its stack slot. Otherwise, we can. */
if (arg->value == 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 (arg->pass_on_stack)
stack_arg_under_construction++;
-#endif
+
arg->value = expand_expr (pval,
(partial
|| TYPE_MODE (TREE_TYPE (pval)) != arg->mode)
arg->value = convert_modes (arg->mode, TYPE_MODE (TREE_TYPE (pval)),
arg->value, arg->unsignedp);
-#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. */
- if (may_be_alloca)
+ if (flags & ECF_MAY_BE_ALLOCA)
do_pending_stack_adjust ();
if (arg->value == arg->stack)
partial, reg, used - size, argblock,
ARGS_SIZE_RTX (arg->offset), reg_parm_stack_space,
ARGS_SIZE_RTX (arg->alignment_pad));
-
- arg_space_so_far += used;
}
else
{
excess = (arg->size.constant - int_size_in_bytes (TREE_TYPE (pval))
+ partial * UNITS_PER_WORD);
size_rtx = expr_size (pval);
- arg_space_so_far += excess + INTVAL (size_rtx);
}
emit_push_insn (arg->value, arg->mode, TREE_TYPE (pval), size_rtx,
- TYPE_ALIGN (TREE_TYPE (pval)) / BITS_PER_UNIT, partial,
- reg, excess, argblock, ARGS_SIZE_RTX (arg->offset),
+ TYPE_ALIGN (TREE_TYPE (pval)), partial, reg, excess,
+ argblock, ARGS_SIZE_RTX (arg->offset),
reg_parm_stack_space,
ARGS_SIZE_RTX (arg->alignment_pad));
}