#include "tree.h"
#include "flags.h"
#include "expr.h"
+#include "function.h"
#include "regs.h"
#include "insn-flags.h"
#include "toplev.h"
static int calls_function PROTO ((tree, int));
static int calls_function_1 PROTO ((tree, int));
static void emit_call_1 PROTO ((rtx, tree, tree, HOST_WIDE_INT,
- HOST_WIDE_INT, rtx, rtx,
- int, rtx, int));
+ HOST_WIDE_INT, HOST_WIDE_INT, rtx,
+ rtx, int, rtx, int));
static void special_function_p PROTO ((char *, tree, int *, int *,
int *, int *));
static void precompute_register_parameters PROTO ((int, struct arg_data *,
int));
static void store_unaligned_arguments_into_pseudos PROTO ((struct arg_data *,
int));
+static int finalize_must_preallocate PROTO ((int, int,
+ struct arg_data *,
+ struct args_size *));
+static void precompute_arguments PROTO ((int, int, int,
+ struct arg_data *,
+ struct args_size *));
+static int compute_argument_block_size PROTO ((int,
+ struct args_size *));
+static void initialize_argument_information PROTO ((int,
+ struct arg_data *,
+ struct args_size *,
+ int, tree, tree,
+ CUMULATIVE_ARGS *,
+ int, rtx *, int *,
+ int *, int *));
+static void compute_argument_addresses PROTO ((struct arg_data *,
+ rtx, int));
+static rtx rtx_for_function_call PROTO ((tree, tree));
+static void load_register_parameters PROTO ((struct arg_data *,
+ int, rtx *));
#if defined(ACCUMULATE_OUTGOING_ARGS) && defined(REG_PARM_STACK_SPACE)
static rtx save_fixed_argument_area PROTO ((int, rtx, int *, int *));
if ((DECL_BUILT_IN (fndecl)
&& DECL_FUNCTION_CODE (fndecl) == BUILT_IN_ALLOCA)
|| (DECL_SAVED_INSNS (fndecl)
- && (FUNCTION_FLAGS (DECL_SAVED_INSNS (fndecl))
- & FUNCTION_FLAGS_CALLS_ALLOCA)))
+ && DECL_SAVED_INSNS (fndecl)->calls_alloca))
return 1;
}
IS_CONST is true if this is a `const' call. */
static void
-emit_call_1 (funexp, fndecl, funtype, stack_size, struct_value_size,
- next_arg_reg, valreg, old_inhibit_defer_pop, call_fusage,
- is_const)
+emit_call_1 (funexp, fndecl, funtype, stack_size, rounded_stack_size,
+ struct_value_size, next_arg_reg, valreg, old_inhibit_defer_pop,
+ call_fusage, is_const)
rtx funexp;
tree fndecl ATTRIBUTE_UNUSED;
tree funtype ATTRIBUTE_UNUSED;
HOST_WIDE_INT stack_size;
+ HOST_WIDE_INT rounded_stack_size;
HOST_WIDE_INT struct_value_size;
rtx next_arg_reg;
rtx valreg;
rtx call_fusage;
int is_const;
{
- rtx stack_size_rtx = GEN_INT (stack_size);
+ rtx rounded_stack_size_rtx = GEN_INT (rounded_stack_size);
rtx struct_value_size_rtx = GEN_INT (struct_value_size);
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,
#ifndef ACCUMULATE_OUTGOING_ARGS
#if defined (HAVE_call_pop) && defined (HAVE_call_value_pop)
- if (HAVE_call_pop && HAVE_call_value_pop
- && (RETURN_POPS_ARGS (fndecl, funtype, stack_size) > 0
- || stack_size == 0))
+ if (HAVE_call_pop && HAVE_call_value_pop && n_popped > 0)
{
- rtx n_pop = GEN_INT (RETURN_POPS_ARGS (fndecl, funtype, stack_size));
+ rtx n_pop = GEN_INT (n_popped);
rtx pat;
/* If this subroutine pops its own args, record that in the call insn
if (valreg)
pat = gen_call_value_pop (valreg,
gen_rtx_MEM (FUNCTION_MODE, funexp),
- stack_size_rtx, next_arg_reg, n_pop);
+ rounded_stack_size_rtx, next_arg_reg, n_pop);
else
pat = gen_call_pop (gen_rtx_MEM (FUNCTION_MODE, funexp),
- stack_size_rtx, next_arg_reg, n_pop);
+ rounded_stack_size_rtx, next_arg_reg, n_pop);
emit_call_insn (pat);
already_popped = 1;
if (valreg)
emit_call_insn (gen_call_value (valreg,
gen_rtx_MEM (FUNCTION_MODE, funexp),
- stack_size_rtx, next_arg_reg,
+ rounded_stack_size_rtx, next_arg_reg,
NULL_RTX));
else
emit_call_insn (gen_call (gen_rtx_MEM (FUNCTION_MODE, funexp),
- stack_size_rtx, next_arg_reg,
+ rounded_stack_size_rtx, next_arg_reg,
struct_value_size_rtx));
}
else
If returning from the subroutine does pop the args, indicate that the
stack pointer will be changed. */
- if (stack_size != 0 && RETURN_POPS_ARGS (fndecl, funtype, stack_size) > 0)
+ if (n_popped > 0)
{
if (!already_popped)
CALL_INSN_FUNCTION_USAGE (call_insn)
= gen_rtx_EXPR_LIST (VOIDmode,
gen_rtx_CLOBBER (VOIDmode, stack_pointer_rtx),
CALL_INSN_FUNCTION_USAGE (call_insn));
- stack_size -= RETURN_POPS_ARGS (fndecl, funtype, stack_size);
- stack_size_rtx = GEN_INT (stack_size);
+ rounded_stack_size -= n_popped;
+ rounded_stack_size_rtx = GEN_INT (rounded_stack_size);
}
- if (stack_size != 0)
+ if (rounded_stack_size != 0)
{
if (flag_defer_pop && inhibit_defer_pop == 0 && !is_const)
- pending_stack_adjust += stack_size;
+ pending_stack_adjust += rounded_stack_size;
else
- adjust_stack (stack_size_rtx);
+ adjust_stack (rounded_stack_size_rtx);
}
#endif
}
}
}
+/* Fill in ARGS_SIZE and ARGS array based on the parameters found in
+ ACTPARMS.
+
+ NUM_ACTUALS is the total number of parameters.
+
+ N_NAMED_ARGS is the total number of named arguments.
+
+ FNDECL is the tree code for the target of this call (if known)
+
+ ARGS_SO_FAR holds state needed by the target to know where to place
+ the next argument.
+
+ REG_PARM_STACK_SPACE is the number of bytes of stack space reserved
+ for arguments which are passed in registers.
+
+ 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
+ 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)
+ int num_actuals ATTRIBUTE_UNUSED;
+ struct arg_data *args;
+ struct args_size *args_size;
+ int n_named_args ATTRIBUTE_UNUSED;
+ tree actparms;
+ tree fndecl;
+ CUMULATIVE_ARGS *args_so_far;
+ int reg_parm_stack_space;
+ rtx *old_stack_level;
+ int *old_pending_adj;
+ int *must_preallocate;
+ int *is_const;
+{
+ /* 1 if scanning parms front to back, -1 if scanning back to front. */
+ int inc;
+
+ /* Count arg position in order args appear. */
+ int argpos;
+
+ int i;
+ tree p;
+
+ args_size->constant = 0;
+ args_size->var = 0;
+
+ /* In this loop, we consider args in the order they are written.
+ 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
+
+ /* 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++)
+ {
+ tree type = TREE_TYPE (TREE_VALUE (p));
+ int unsignedp;
+ enum machine_mode mode;
+
+ args[i].tree_value = TREE_VALUE (p);
+
+ /* Replace erroneous argument with constant zero. */
+ if (type == error_mark_node || TYPE_SIZE (type) == 0)
+ args[i].tree_value = integer_zero_node, type = integer_type_node;
+
+ /* If TYPE is a transparent union, pass things the way we would
+ pass the first field of the union. We have already verified that
+ the modes are the same. */
+ if (TYPE_TRANSPARENT_UNION (type))
+ type = TREE_TYPE (TYPE_FIELDS (type));
+
+ /* Decide where to pass this arg.
+
+ args[i].reg is nonzero if all or part is passed in registers.
+
+ args[i].partial is nonzero if part but not all is passed in registers,
+ and the exact value says how many words are passed in registers.
+
+ args[i].pass_on_stack is nonzero if the argument must at least be
+ computed on the stack. It may then be loaded back into registers
+ if args[i].reg is nonzero.
+
+ These decisions are driven by the FUNCTION_... macros and must agree
+ with those made by function.c. */
+
+ /* See if this argument should be passed by invisible reference. */
+ if ((TREE_CODE (TYPE_SIZE (type)) != INTEGER_CST
+ && contains_placeholder_p (TYPE_SIZE (type)))
+ || TREE_ADDRESSABLE (type)
+#ifdef FUNCTION_ARG_PASS_BY_REFERENCE
+ || FUNCTION_ARG_PASS_BY_REFERENCE (*args_so_far, TYPE_MODE (type),
+ type, argpos < n_named_args)
+#endif
+ )
+ {
+ /* If we're compiling a thunk, pass through invisible
+ references instead of making a copy. */
+ if (current_function_is_thunk
+#ifdef FUNCTION_ARG_CALLEE_COPIES
+ || (FUNCTION_ARG_CALLEE_COPIES (*args_so_far, TYPE_MODE (type),
+ type, argpos < n_named_args)
+ /* If it's in a register, we must make a copy of it too. */
+ /* ??? Is this a sufficient test? Is there a better one? */
+ && !(TREE_CODE (args[i].tree_value) == VAR_DECL
+ && REG_P (DECL_RTL (args[i].tree_value)))
+ && ! TREE_ADDRESSABLE (type))
+#endif
+ )
+ {
+ /* C++ uses a TARGET_EXPR to indicate that we want to make a
+ new object from the argument. If we are passing by
+ invisible reference, the callee will do that for us, so we
+ can strip off the TARGET_EXPR. This is not always safe,
+ but it is safe in the only case where this is a useful
+ optimization; namely, when the argument is a plain object.
+ In that case, the frontend is just asking the backend to
+ make a bitwise copy of the argument. */
+
+ if (TREE_CODE (args[i].tree_value) == TARGET_EXPR
+ && (TREE_CODE_CLASS (TREE_CODE (TREE_OPERAND
+ (args[i].tree_value, 1)))
+ == 'd')
+ && ! REG_P (DECL_RTL (TREE_OPERAND (args[i].tree_value, 1))))
+ args[i].tree_value = TREE_OPERAND (args[i].tree_value, 1);
+
+ args[i].tree_value = build1 (ADDR_EXPR,
+ build_pointer_type (type),
+ args[i].tree_value);
+ type = build_pointer_type (type);
+ }
+ else
+ {
+ /* We make a copy of the object and pass the address to the
+ function being called. */
+ rtx copy;
+
+ if (TYPE_SIZE (type) == 0
+ || TREE_CODE (TYPE_SIZE (type)) != INTEGER_CST
+ || (flag_stack_check && ! STACK_CHECK_BUILTIN
+ && (TREE_INT_CST_HIGH (TYPE_SIZE (type)) != 0
+ || (TREE_INT_CST_LOW (TYPE_SIZE (type))
+ > STACK_CHECK_MAX_VAR_SIZE * BITS_PER_UNIT))))
+ {
+ /* This is a variable-sized object. Make space on the stack
+ for it. */
+ rtx size_rtx = expr_size (TREE_VALUE (p));
+
+ 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;
+ }
+
+ copy = gen_rtx_MEM (BLKmode,
+ allocate_dynamic_stack_space (size_rtx,
+ NULL_RTX,
+ TYPE_ALIGN (type)));
+ }
+ else
+ {
+ int size = int_size_in_bytes (type);
+ copy = assign_stack_temp (TYPE_MODE (type), size, 0);
+ }
+
+ MEM_SET_IN_STRUCT_P (copy, AGGREGATE_TYPE_P (type));
+
+ store_expr (args[i].tree_value, copy, 0);
+ *is_const = 0;
+
+ args[i].tree_value = build1 (ADDR_EXPR,
+ build_pointer_type (type),
+ make_tree (type, copy));
+ type = build_pointer_type (type);
+ }
+ }
+
+ mode = TYPE_MODE (type);
+ unsignedp = TREE_UNSIGNED (type);
+
+#ifdef PROMOTE_FUNCTION_ARGS
+ mode = promote_mode (type, mode, &unsignedp, 1);
+#endif
+
+ args[i].unsignedp = unsignedp;
+ args[i].mode = mode;
+ args[i].reg = FUNCTION_ARG (*args_so_far, mode, type,
+ argpos < n_named_args);
+#ifdef FUNCTION_ARG_PARTIAL_NREGS
+ if (args[i].reg)
+ args[i].partial
+ = FUNCTION_ARG_PARTIAL_NREGS (*args_so_far, mode, type,
+ argpos < n_named_args);
+#endif
+
+ args[i].pass_on_stack = MUST_PASS_IN_STACK (mode, type);
+
+ /* If FUNCTION_ARG returned a (parallel [(expr_list (nil) ...) ...]),
+ it means that we are to pass this arg in the register(s) designated
+ by the PARALLEL, but also to pass it in the stack. */
+ if (args[i].reg && GET_CODE (args[i].reg) == PARALLEL
+ && XEXP (XVECEXP (args[i].reg, 0, 0), 0) == 0)
+ args[i].pass_on_stack = 1;
+
+ /* If this is an addressable type, we must preallocate the stack
+ since we must evaluate the object into its final location.
+
+ If this is to be passed in both registers and the stack, it is simpler
+ to preallocate. */
+ if (TREE_ADDRESSABLE (type)
+ || (args[i].pass_on_stack && args[i].reg != 0))
+ *must_preallocate = 1;
+
+ /* 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;
+
+ /* Compute the stack-size of this argument. */
+ if (args[i].reg == 0 || args[i].partial != 0
+ || reg_parm_stack_space > 0
+ || args[i].pass_on_stack)
+ locate_and_pad_parm (mode, type,
+#ifdef STACK_PARMS_IN_REG_PARM_AREA
+ 1,
+#else
+ args[i].reg != 0,
+#endif
+ fndecl, args_size, &args[i].offset,
+ &args[i].size);
+
+#ifndef ARGS_GROW_DOWNWARD
+ args[i].slot_offset = *args_size;
+#endif
+
+ /* If a part of the arg was put into registers,
+ don't include that part in the amount pushed. */
+ if (reg_parm_stack_space == 0 && ! args[i].pass_on_stack)
+ args[i].size.constant -= ((args[i].partial * UNITS_PER_WORD)
+ / (PARM_BOUNDARY / BITS_PER_UNIT)
+ * (PARM_BOUNDARY / BITS_PER_UNIT));
+
+ /* Update ARGS_SIZE, the total stack space for args so far. */
+
+ args_size->constant += args[i].size.constant;
+ if (args[i].size.var)
+ {
+ ADD_PARM_SIZE (*args_size, args[i].size.var);
+ }
+
+ /* Since the slot offset points to the bottom of the slot,
+ we must record it after incrementing if the args grow down. */
+#ifdef ARGS_GROW_DOWNWARD
+ args[i].slot_offset = *args_size;
+
+ args[i].slot_offset.constant = -args_size->constant;
+ if (args_size->var)
+ {
+ SUB_PARM_SIZE (args[i].slot_offset, args_size->var);
+ }
+#endif
+
+ /* Increment ARGS_SO_FAR, which has info about which arg-registers
+ have been used, etc. */
+
+ FUNCTION_ARG_ADVANCE (*args_so_far, TYPE_MODE (type), type,
+ argpos < n_named_args);
+ }
+}
+
+/* Update ARGS_SIZE to contain the total size for the argument block.
+ Return the original constant component of the argument block's size.
+
+ REG_PARM_STACK_SPACE holds the number of bytes of stack space reserved
+ for arguments passed in registers. */
+
+static int
+compute_argument_block_size (reg_parm_stack_space, args_size)
+ int reg_parm_stack_space;
+ struct args_size *args_size;
+{
+ int unadjusted_args_size = args_size->constant;
+
+ /* 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. */
+
+ if (args_size->var)
+ {
+ args_size->var = ARGS_SIZE_TREE (*args_size);
+ args_size->constant = 0;
+
+#ifdef PREFERRED_STACK_BOUNDARY
+ if (PREFERRED_STACK_BOUNDARY != BITS_PER_UNIT)
+ args_size->var = round_up (args_size->var, STACK_BYTES);
+#endif
+
+ if (reg_parm_stack_space > 0)
+ {
+ args_size->var
+ = size_binop (MAX_EXPR, args_size->var,
+ size_int (reg_parm_stack_space));
+
+#ifndef OUTGOING_REG_PARM_STACK_SPACE
+ /* The area corresponding to register parameters is not to count in
+ the size of the block we need. So make the adjustment. */
+ args_size->var
+ = size_binop (MINUS_EXPR, args_size->var,
+ size_int (reg_parm_stack_space));
+#endif
+ }
+ }
+ else
+ {
+#ifdef PREFERRED_STACK_BOUNDARY
+ args_size->constant = (((args_size->constant
+ + pending_stack_adjust
+ + STACK_BYTES - 1)
+ / STACK_BYTES * STACK_BYTES)
+ - pending_stack_adjust);
+#endif
+
+ args_size->constant = MAX (args_size->constant,
+ reg_parm_stack_space);
+
+#ifdef MAYBE_REG_PARM_STACK_SPACE
+ if (reg_parm_stack_space == 0)
+ args_size->constant = 0;
+#endif
+
+#ifndef OUTGOING_REG_PARM_STACK_SPACE
+ args_size->constant -= reg_parm_stack_space;
+#endif
+ }
+ return unadjusted_args_size;
+}
+
+/* Precompute parameters has 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.
+
+ 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. */
+
+static void
+precompute_arguments (is_const, must_preallocate, num_actuals, args, args_size)
+ int is_const;
+ int must_preallocate;
+ int num_actuals;
+ struct arg_data *args;
+ struct args_size *args_size;
+{
+ int i;
+
+ /* If this function call is cse'able, precompute all the parameters.
+ Note that if the parameter is constructed into a temporary, this will
+ cause an additional copy because the parameter will be constructed
+ into a temporary location and then copied into the outgoing arguments.
+ If a parameter contains a call to alloca and this function uses the
+ stack, precompute the parameter. */
+
+ /* If we preallocated the stack space, and some arguments must be passed
+ 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. */
+
+ 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 this is an addressable type, we cannot pre-evaluate it. */
+ if (TREE_ADDRESSABLE (TREE_TYPE (args[i].tree_value)))
+ abort ();
+
+ push_temp_slots ();
+
+ args[i].initial_value = args[i].value
+ = expand_expr (args[i].tree_value, NULL_RTX, VOIDmode, 0);
+
+ preserve_temp_slots (args[i].value);
+ pop_temp_slots ();
+
+ /* ANSI doesn't require a sequence point here,
+ but PCC has one, so this will avoid some problems. */
+ emit_queue ();
+
+ args[i].initial_value = args[i].value
+ = protect_from_queue (args[i].initial_value, 0);
+
+ if (TYPE_MODE (TREE_TYPE (args[i].tree_value)) != args[i].mode)
+ args[i].value
+ = convert_modes (args[i].mode,
+ TYPE_MODE (TREE_TYPE (args[i].tree_value)),
+ args[i].value, args[i].unsignedp);
+ }
+}
+
+/* Given the current state of MUST_PREALLOCATE and information about
+ arguments to a function call in NUM_ACTUALS, ARGS and ARGS_SIZE,
+ compute and return the final value for MUST_PREALLOCATE. */
+
+static int
+finalize_must_preallocate (must_preallocate, num_actuals, args, args_size)
+ int must_preallocate;
+ int num_actuals;
+ struct arg_data *args;
+ struct args_size *args_size;
+{
+ /* See if we have or want to preallocate stack space.
+
+ If we would have to push a partially-in-regs parm
+ before other stack parms, preallocate stack space instead.
+
+ If the size of some parm is not a multiple of the required stack
+ alignment, we must preallocate.
+
+ If the total size of arguments that would otherwise create a copy in
+ a temporary (such as a CALL) is more than half the total argument list
+ size, preallocation is faster.
+
+ Another reason to preallocate is if we have a machine (like the m88k)
+ where stack alignment is required to be maintained between every
+ pair of insns, not just when the call is made. However, we assume here
+ that such machines either do not have push insns (and hence preallocation
+ would occur anyway) or the problem is taken care of with
+ PUSH_ROUNDING. */
+
+ if (! must_preallocate)
+ {
+ int partial_seen = 0;
+ int copy_to_evaluate_size = 0;
+ int i;
+
+ for (i = 0; i < num_actuals && ! must_preallocate; i++)
+ {
+ if (args[i].partial > 0 && ! args[i].pass_on_stack)
+ partial_seen = 1;
+ else if (partial_seen && args[i].reg == 0)
+ must_preallocate = 1;
+
+ if (TYPE_MODE (TREE_TYPE (args[i].tree_value)) == BLKmode
+ && (TREE_CODE (args[i].tree_value) == CALL_EXPR
+ || TREE_CODE (args[i].tree_value) == TARGET_EXPR
+ || TREE_CODE (args[i].tree_value) == COND_EXPR
+ || TREE_ADDRESSABLE (TREE_TYPE (args[i].tree_value))))
+ copy_to_evaluate_size
+ += int_size_in_bytes (TREE_TYPE (args[i].tree_value));
+ }
+
+ if (copy_to_evaluate_size * 2 >= args_size->constant
+ && args_size->constant > 0)
+ must_preallocate = 1;
+ }
+ return must_preallocate;
+}
+
+/* If we preallocated stack space, compute the address of each argument
+ and store it into the ARGS array.
+
+ We need not ensure it is a valid memory address here; it will be
+ validized when it is used.
+
+ ARGBLOCK is an rtx for the address of the outgoing arguments. */
+
+static void
+compute_argument_addresses (args, argblock, num_actuals)
+ struct arg_data *args;
+ rtx argblock;
+ int num_actuals;
+{
+ if (argblock)
+ {
+ rtx arg_reg = argblock;
+ int i, arg_offset = 0;
+
+ if (GET_CODE (argblock) == PLUS)
+ arg_reg = XEXP (argblock, 0), arg_offset = INTVAL (XEXP (argblock, 1));
+
+ for (i = 0; i < num_actuals; i++)
+ {
+ rtx offset = ARGS_SIZE_RTX (args[i].offset);
+ rtx slot_offset = ARGS_SIZE_RTX (args[i].slot_offset);
+ rtx addr;
+
+ /* Skip this parm if it will not be passed on the stack. */
+ if (! args[i].pass_on_stack && args[i].reg != 0)
+ continue;
+
+ if (GET_CODE (offset) == CONST_INT)
+ addr = plus_constant (arg_reg, INTVAL (offset));
+ else
+ addr = gen_rtx_PLUS (Pmode, arg_reg, offset);
+
+ addr = plus_constant (addr, arg_offset);
+ args[i].stack = gen_rtx_MEM (args[i].mode, addr);
+ MEM_SET_IN_STRUCT_P
+ (args[i].stack,
+ AGGREGATE_TYPE_P (TREE_TYPE (args[i].tree_value)));
+
+ if (GET_CODE (slot_offset) == CONST_INT)
+ addr = plus_constant (arg_reg, INTVAL (slot_offset));
+ else
+ addr = gen_rtx_PLUS (Pmode, arg_reg, slot_offset);
+
+ addr = plus_constant (addr, arg_offset);
+ args[i].stack_slot = gen_rtx_MEM (args[i].mode, addr);
+ }
+ }
+}
+
+/* Given a FNDECL and EXP, return an rtx suitable for use as a target address
+ in a call instruction.
+
+ FNDECL is the tree node for the target function. For an indirect call
+ FNDECL will be NULL_TREE.
+
+ EXP is the CALL_EXPR for this call. */
+
+static rtx
+rtx_for_function_call (fndecl, exp)
+ tree fndecl;
+ tree exp;
+{
+ rtx funexp;
+
+ /* Get the function to call, in the form of RTL. */
+ if (fndecl)
+ {
+ /* If this is the first use of the function, see if we need to
+ make an external definition for it. */
+ if (! TREE_USED (fndecl))
+ {
+ assemble_external (fndecl);
+ TREE_USED (fndecl) = 1;
+ }
+
+ /* Get a SYMBOL_REF rtx for the function address. */
+ funexp = XEXP (DECL_RTL (fndecl), 0);
+ }
+ else
+ /* Generate an rtx (probably a pseudo-register) for the address. */
+ {
+ rtx funaddr;
+ push_temp_slots ();
+ funaddr = funexp =
+ expand_expr (TREE_OPERAND (exp, 0), NULL_RTX, VOIDmode, 0);
+ pop_temp_slots (); /* FUNEXP can't be BLKmode */
+
+ /* Check the function is executable. */
+ if (current_function_check_memory_usage)
+ {
+#ifdef POINTERS_EXTEND_UNSIGNED
+ /* It might be OK to convert funexp in place, but there's
+ a lot going on between here and when it happens naturally
+ that this seems safer. */
+ funaddr = convert_memory_address (Pmode, funexp);
+#endif
+ emit_library_call (chkr_check_exec_libfunc, 1,
+ VOIDmode, 1,
+ funaddr, Pmode);
+ }
+ emit_queue ();
+ }
+ return funexp;
+}
+
+/* Do the register loads required for any wholly-register parms or any
+ parms which are passed both on the stack and in a register. Their
+ expressions were already evaluated.
+
+ Mark all register-parms as living through the call, putting these USE
+ insns in the CALL_INSN_FUNCTION_USAGE field. */
+
+static void
+load_register_parameters (args, num_actuals, call_fusage)
+ struct arg_data *args;
+ int num_actuals;
+ rtx *call_fusage;
+{
+ int i, j;
+
+#ifdef LOAD_ARGS_REVERSED
+ for (i = num_actuals - 1; i >= 0; i--)
+#else
+ for (i = 0; i < num_actuals; i++)
+#endif
+ {
+ rtx reg = args[i].reg;
+ int partial = args[i].partial;
+ int nregs;
+
+ if (reg)
+ {
+ /* Set to non-negative if must move a word at a time, even if just
+ one word (e.g, partial == 1 && mode == DFmode). Set to -1 if
+ we just use a normal move insn. This value can be zero if the
+ argument is a zero size structure with no fields. */
+ nregs = (partial ? partial
+ : (TYPE_MODE (TREE_TYPE (args[i].tree_value)) == BLKmode
+ ? ((int_size_in_bytes (TREE_TYPE (args[i].tree_value))
+ + (UNITS_PER_WORD - 1)) / UNITS_PER_WORD)
+ : -1));
+
+ /* Handle calls that pass values in multiple non-contiguous
+ 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));
+ }
+
+ /* If simple case, just do move. If normal partial, store_one_arg
+ has already loaded the register for us. In all other cases,
+ load the register(s) from memory. */
+
+ else if (nregs == -1)
+ emit_move_insn (reg, args[i].value);
+
+ /* If we have pre-computed the values to put in the registers in
+ the case of non-aligned structures, copy them in now. */
+
+ else if (args[i].n_aligned_regs != 0)
+ for (j = 0; j < args[i].n_aligned_regs; j++)
+ emit_move_insn (gen_rtx_REG (word_mode, REGNO (reg) + j),
+ args[i].aligned_regs[j]);
+
+ else if (partial == 0 || args[i].pass_on_stack)
+ move_block_to_reg (REGNO (reg),
+ validize_mem (args[i].value), nregs,
+ args[i].mode);
+
+ /* Handle calls that pass values in multiple non-contiguous
+ locations. The Irix 6 ABI has examples of this. */
+ if (GET_CODE (reg) == PARALLEL)
+ use_group_regs (call_fusage, reg);
+ else if (nregs == -1)
+ use_reg (call_fusage, reg);
+ else
+ use_regs (call_fusage, REGNO (reg), nregs == 0 ? 1 : nregs);
+ }
+ }
+}
+
/* 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.
/* Number of named args. Args after this are anonymous ones
and they must all go on the stack. */
int n_named_args;
- /* Count arg position in order args appear. */
- int argpos;
/* Vector of information about each argument.
Arguments are numbered in the order they will be pushed,
/* Total size in bytes of all the stack-parms scanned so far. */
struct args_size args_size;
/* Size of arguments before any adjustments (such as rounding). */
- struct args_size original_args_size;
+ int unadjusted_args_size;
/* Data on reg parms scanned so far. */
CUMULATIVE_ARGS args_so_far;
/* Nonzero if a reg parm has been scanned. */
/* Size of the stack reserved for parameter registers. */
int reg_parm_stack_space = 0;
- /* 1 if scanning parms front to back, -1 if scanning back to front. */
- int inc;
/* Address of space preallocated for stack parms
(on machines that lack push insns), or 0 if space not preallocated. */
rtx argblock = 0;
int old_inhibit_defer_pop = inhibit_defer_pop;
rtx call_fusage = 0;
register tree p;
- register int i, j;
+ register int i;
/* The value of the function call can be put in a hard register. But
if -fcheck-memory-usage, code which invokes functions (and thus
&& fndecl != current_function_decl
&& DECL_INLINE (fndecl)
&& DECL_SAVED_INSNS (fndecl)
- && RTX_INTEGRATED_P (DECL_SAVED_INSNS (fndecl)))
+ && DECL_SAVED_INSNS (fndecl)->inlinable)
is_integrable = 1;
else if (! TREE_ADDRESSABLE (fndecl))
{
rtx insn, seq;
/* Look for a call in the inline function code.
- If OUTGOING_ARGS_SIZE (DECL_SAVED_INSNS (fndecl)) is
+ 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 (OUTGOING_ARGS_SIZE (DECL_SAVED_INSNS (fndecl)) == 0)
+ 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;
value of reg_parm_stack_space is wrong, but gives
correct results on all supported machines. */
- int adjust = (OUTGOING_ARGS_SIZE (DECL_SAVED_INSNS (fndecl))
+ int adjust = (DECL_SAVED_INSNS (fndecl)->outgoing_args_size
+ reg_parm_stack_space);
start_sequence ();
mark_addressable (fndecl);
}
- /* When calling a const function, we must pop the stack args right away,
- so that the pop is deleted or moved with the call. */
- if (is_const)
- NO_DEFER_POP;
-
function_call_count++;
if (fndecl && DECL_NAME (fndecl))
if (may_be_alloca)
current_function_calls_alloca = 1;
+ /* Operand 0 is a pointer-to-function; get the type of the function. */
+ funtype = TREE_TYPE (TREE_OPERAND (exp, 0));
+ if (! POINTER_TYPE_P (funtype))
+ abort ();
+ funtype = TREE_TYPE (funtype);
+
+ /* When calling a const function, we must pop the stack args right away,
+ so that the pop is deleted or moved with the call. */
+ if (is_const)
+ NO_DEFER_POP;
+
/* Don't let pending stack adjusts add up to too much.
Also, do all pending adjustments now
if there is any chance this might be a call to alloca. */
|| (pending_stack_adjust > 0 && may_be_alloca))
do_pending_stack_adjust ();
- /* Operand 0 is a pointer-to-function; get the type of the function. */
- funtype = TREE_TYPE (TREE_OPERAND (exp, 0));
- if (! POINTER_TYPE_P (funtype))
- abort ();
-
- funtype = TREE_TYPE (funtype);
-
/* Push the temporary stack slot level so that we can free any temporaries
we make. */
push_temp_slots ();
(If no anonymous args follow, the result of list_length is actually
one too large. This is harmless.)
- If SETUP_INCOMING_VARARGS is defined and STRICT_ARGUMENT_NAMING is zero,
- this machine will be able to place unnamed args that were passed in
+ If PRETEND_OUTGOING_VARARGS_NAMED is set and STRICT_ARGUMENT_NAMING is
+ zero, this machine will be able to place unnamed args that were passed in
registers into the stack. So treat all args as named. This allows the
insns emitting for a specific argument list to be independent of the
function declaration.
- If SETUP_INCOMING_VARARGS is not defined, we do not have any reliable
+ If PRETEND_OUTGOING_VARARGS_NAMED is not set, we do not have any reliable
way to pass unnamed args in registers, so we must force them into
memory. */
if ((STRICT_ARGUMENT_NAMING
-#ifndef SETUP_INCOMING_VARARGS
- || 1
-#endif
- )
+ || ! PRETEND_OUTGOING_VARARGS_NAMED)
&& TYPE_ARG_TYPES (funtype) != 0)
n_named_args
= (list_length (TYPE_ARG_TYPES (funtype))
/* If we know nothing, treat all args as named. */
n_named_args = num_actuals;
- /* Make a vector to hold all the information about each arg. */
- args = (struct arg_data *) alloca (num_actuals * sizeof (struct arg_data));
- bzero ((char *) args, num_actuals * sizeof (struct arg_data));
-
- args_size.constant = 0;
- args_size.var = 0;
-
- /* In this loop, we consider args in the order they are written.
- 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
-
- /* 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++)
- {
- tree type = TREE_TYPE (TREE_VALUE (p));
- int unsignedp;
- enum machine_mode mode;
-
- args[i].tree_value = TREE_VALUE (p);
-
- /* Replace erroneous argument with constant zero. */
- if (type == error_mark_node || TYPE_SIZE (type) == 0)
- args[i].tree_value = integer_zero_node, type = integer_type_node;
-
- /* If TYPE is a transparent union, pass things the way we would
- pass the first field of the union. We have already verified that
- the modes are the same. */
- if (TYPE_TRANSPARENT_UNION (type))
- type = TREE_TYPE (TYPE_FIELDS (type));
-
- /* Decide where to pass this arg.
-
- args[i].reg is nonzero if all or part is passed in registers.
-
- args[i].partial is nonzero if part but not all is passed in registers,
- and the exact value says how many words are passed in registers.
-
- args[i].pass_on_stack is nonzero if the argument must at least be
- computed on the stack. It may then be loaded back into registers
- if args[i].reg is nonzero.
-
- These decisions are driven by the FUNCTION_... macros and must agree
- with those made by function.c. */
-
- /* See if this argument should be passed by invisible reference. */
- if ((TREE_CODE (TYPE_SIZE (type)) != INTEGER_CST
- && contains_placeholder_p (TYPE_SIZE (type)))
- || TREE_ADDRESSABLE (type)
-#ifdef FUNCTION_ARG_PASS_BY_REFERENCE
- || FUNCTION_ARG_PASS_BY_REFERENCE (args_so_far, TYPE_MODE (type),
- type, argpos < n_named_args)
-#endif
- )
- {
- /* If we're compiling a thunk, pass through invisible
- references instead of making a copy. */
- if (current_function_is_thunk
-#ifdef FUNCTION_ARG_CALLEE_COPIES
- || (FUNCTION_ARG_CALLEE_COPIES (args_so_far, TYPE_MODE (type),
- type, argpos < n_named_args)
- /* If it's in a register, we must make a copy of it too. */
- /* ??? Is this a sufficient test? Is there a better one? */
- && !(TREE_CODE (args[i].tree_value) == VAR_DECL
- && REG_P (DECL_RTL (args[i].tree_value)))
- && ! TREE_ADDRESSABLE (type))
-#endif
- )
- {
- /* C++ uses a TARGET_EXPR to indicate that we want to make a
- new object from the argument. If we are passing by
- invisible reference, the callee will do that for us, so we
- can strip off the TARGET_EXPR. This is not always safe,
- but it is safe in the only case where this is a useful
- optimization; namely, when the argument is a plain object.
- In that case, the frontend is just asking the backend to
- make a bitwise copy of the argument. */
-
- if (TREE_CODE (args[i].tree_value) == TARGET_EXPR
- && (TREE_CODE_CLASS (TREE_CODE (TREE_OPERAND
- (args[i].tree_value, 1)))
- == 'd')
- && ! REG_P (DECL_RTL (TREE_OPERAND (args[i].tree_value, 1))))
- args[i].tree_value = TREE_OPERAND (args[i].tree_value, 1);
-
- args[i].tree_value = build1 (ADDR_EXPR,
- build_pointer_type (type),
- args[i].tree_value);
- type = build_pointer_type (type);
- }
- else
- {
- /* We make a copy of the object and pass the address to the
- function being called. */
- rtx copy;
-
- if (TYPE_SIZE (type) == 0
- || TREE_CODE (TYPE_SIZE (type)) != INTEGER_CST
- || (flag_stack_check && ! STACK_CHECK_BUILTIN
- && (TREE_INT_CST_HIGH (TYPE_SIZE (type)) != 0
- || (TREE_INT_CST_LOW (TYPE_SIZE (type))
- > STACK_CHECK_MAX_VAR_SIZE * BITS_PER_UNIT))))
- {
- /* This is a variable-sized object. Make space on the stack
- for it. */
- rtx size_rtx = expr_size (TREE_VALUE (p));
-
- 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;
- }
-
- copy = gen_rtx_MEM (BLKmode,
- allocate_dynamic_stack_space (size_rtx,
- NULL_RTX,
- TYPE_ALIGN (type)));
- }
- else
- {
- int size = int_size_in_bytes (type);
- copy = assign_stack_temp (TYPE_MODE (type), size, 0);
- }
-
- MEM_SET_IN_STRUCT_P (copy, AGGREGATE_TYPE_P (type));
-
- store_expr (args[i].tree_value, copy, 0);
- is_const = 0;
-
- args[i].tree_value = build1 (ADDR_EXPR,
- build_pointer_type (type),
- make_tree (type, copy));
- type = build_pointer_type (type);
- }
- }
-
- mode = TYPE_MODE (type);
- unsignedp = TREE_UNSIGNED (type);
-
-#ifdef PROMOTE_FUNCTION_ARGS
- mode = promote_mode (type, mode, &unsignedp, 1);
-#endif
-
- args[i].unsignedp = unsignedp;
- args[i].mode = mode;
- args[i].reg = FUNCTION_ARG (args_so_far, mode, type,
- argpos < n_named_args);
-#ifdef FUNCTION_ARG_PARTIAL_NREGS
- if (args[i].reg)
- args[i].partial
- = FUNCTION_ARG_PARTIAL_NREGS (args_so_far, mode, type,
- argpos < n_named_args);
-#endif
-
- args[i].pass_on_stack = MUST_PASS_IN_STACK (mode, type);
-
- /* If FUNCTION_ARG returned a (parallel [(expr_list (nil) ...) ...]),
- it means that we are to pass this arg in the register(s) designated
- by the PARALLEL, but also to pass it in the stack. */
- if (args[i].reg && GET_CODE (args[i].reg) == PARALLEL
- && XEXP (XVECEXP (args[i].reg, 0, 0), 0) == 0)
- args[i].pass_on_stack = 1;
-
- /* If this is an addressable type, we must preallocate the stack
- since we must evaluate the object into its final location.
-
- If this is to be passed in both registers and the stack, it is simpler
- to preallocate. */
- if (TREE_ADDRESSABLE (type)
- || (args[i].pass_on_stack && args[i].reg != 0))
- must_preallocate = 1;
-
- /* 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;
-
- /* Compute the stack-size of this argument. */
- if (args[i].reg == 0 || args[i].partial != 0
- || reg_parm_stack_space > 0
- || args[i].pass_on_stack)
- locate_and_pad_parm (mode, type,
-#ifdef STACK_PARMS_IN_REG_PARM_AREA
- 1,
-#else
- args[i].reg != 0,
-#endif
- fndecl, &args_size, &args[i].offset,
- &args[i].size);
-
-#ifndef ARGS_GROW_DOWNWARD
- args[i].slot_offset = args_size;
-#endif
-
- /* If a part of the arg was put into registers,
- don't include that part in the amount pushed. */
- if (reg_parm_stack_space == 0 && ! args[i].pass_on_stack)
- args[i].size.constant -= ((args[i].partial * UNITS_PER_WORD)
- / (PARM_BOUNDARY / BITS_PER_UNIT)
- * (PARM_BOUNDARY / BITS_PER_UNIT));
-
- /* Update ARGS_SIZE, the total stack space for args so far. */
-
- args_size.constant += args[i].size.constant;
- if (args[i].size.var)
- {
- ADD_PARM_SIZE (args_size, args[i].size.var);
- }
-
- /* Since the slot offset points to the bottom of the slot,
- we must record it after incrementing if the args grow down. */
-#ifdef ARGS_GROW_DOWNWARD
- args[i].slot_offset = args_size;
-
- args[i].slot_offset.constant = -args_size.constant;
- if (args_size.var)
- {
- SUB_PARM_SIZE (args[i].slot_offset, args_size.var);
- }
-#endif
-
- /* Increment ARGS_SO_FAR, which has info about which arg-registers
- have been used, etc. */
+ /* Make a vector to hold all the information about each arg. */
+ args = (struct arg_data *) alloca (num_actuals * sizeof (struct arg_data));
+ bzero ((char *) args, num_actuals * sizeof (struct arg_data));
- FUNCTION_ARG_ADVANCE (args_so_far, TYPE_MODE (type), type,
- argpos < n_named_args);
- }
+ /* Build up entries inthe ARGS array, compute the size of the arguments
+ into ARGS_SIZE, etc. */
+ initialize_argument_information (num_actuals, args, &args_size, n_named_args,
+ actparms, fndecl, &args_so_far,
+ reg_parm_stack_space, &old_stack_level,
+ &old_pending_adj, &must_preallocate,
+ &is_const);
#ifdef FINAL_REG_PARM_STACK_SPACE
reg_parm_stack_space = FINAL_REG_PARM_STACK_SPACE (args_size.constant,
args_size.var);
#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. */
-
- original_args_size = args_size;
if (args_size.var)
{
/* If this function requires a variable-sized argument list, don't try to
is_const = 0;
must_preallocate = 1;
-
- args_size.var = ARGS_SIZE_TREE (args_size);
- args_size.constant = 0;
-
-#ifdef PREFERRED_STACK_BOUNDARY
- if (PREFERRED_STACK_BOUNDARY != BITS_PER_UNIT)
- args_size.var = round_up (args_size.var, STACK_BYTES);
-#endif
-
- if (reg_parm_stack_space > 0)
- {
- args_size.var
- = size_binop (MAX_EXPR, args_size.var,
- size_int (reg_parm_stack_space));
-
-#ifndef OUTGOING_REG_PARM_STACK_SPACE
- /* The area corresponding to register parameters is not to count in
- the size of the block we need. So make the adjustment. */
- args_size.var
- = size_binop (MINUS_EXPR, args_size.var,
- size_int (reg_parm_stack_space));
-#endif
- }
- }
- else
- {
-#ifdef PREFERRED_STACK_BOUNDARY
- args_size.constant = (((args_size.constant + (STACK_BYTES - 1))
- / STACK_BYTES) * STACK_BYTES);
-#endif
-
- args_size.constant = MAX (args_size.constant,
- reg_parm_stack_space);
-
-#ifdef MAYBE_REG_PARM_STACK_SPACE
- if (reg_parm_stack_space == 0)
- args_size.constant = 0;
-#endif
-
-#ifndef OUTGOING_REG_PARM_STACK_SPACE
- args_size.constant -= reg_parm_stack_space;
-#endif
}
- /* See if we have or want to preallocate stack space.
-
- If we would have to push a partially-in-regs parm
- before other stack parms, preallocate stack space instead.
-
- If the size of some parm is not a multiple of the required stack
- alignment, we must preallocate.
-
- If the total size of arguments that would otherwise create a copy in
- a temporary (such as a CALL) is more than half the total argument list
- size, preallocation is faster.
-
- Another reason to preallocate is if we have a machine (like the m88k)
- where stack alignment is required to be maintained between every
- pair of insns, not just when the call is made. However, we assume here
- that such machines either do not have push insns (and hence preallocation
- would occur anyway) or the problem is taken care of with
- PUSH_ROUNDING. */
-
- if (! must_preallocate)
- {
- int partial_seen = 0;
- int copy_to_evaluate_size = 0;
-
- for (i = 0; i < num_actuals && ! must_preallocate; i++)
- {
- if (args[i].partial > 0 && ! args[i].pass_on_stack)
- partial_seen = 1;
- else if (partial_seen && args[i].reg == 0)
- must_preallocate = 1;
-
- if (TYPE_MODE (TREE_TYPE (args[i].tree_value)) == BLKmode
- && (TREE_CODE (args[i].tree_value) == CALL_EXPR
- || TREE_CODE (args[i].tree_value) == TARGET_EXPR
- || TREE_CODE (args[i].tree_value) == COND_EXPR
- || TREE_ADDRESSABLE (TREE_TYPE (args[i].tree_value))))
- copy_to_evaluate_size
- += int_size_in_bytes (TREE_TYPE (args[i].tree_value));
- }
+ /* Compute the actual size of the argument block required. The variable
+ and constant sizes must be combined, the size may have to be rounded,
+ and there may be a minimum required size. */
+ unadjusted_args_size
+ = compute_argument_block_size (reg_parm_stack_space, &args_size);
- if (copy_to_evaluate_size * 2 >= args_size.constant
- && args_size.constant > 0)
- must_preallocate = 1;
- }
+ /* Now make final decision about preallocating stack space. */
+ must_preallocate = finalize_must_preallocate (must_preallocate,
+ num_actuals, args, &args_size);
/* If the structure value address will reference the stack pointer, we must
stabilize it. We don't need to do this if we know that we are not going
))
structure_value_addr = copy_to_reg (structure_value_addr);
- /* If this function call is cse'able, precompute all the parameters.
- Note that if the parameter is constructed into a temporary, this will
- cause an additional copy because the parameter will be constructed
- into a temporary location and then copied into the outgoing arguments.
- If a parameter contains a call to alloca and this function uses the
- stack, precompute the parameter. */
-
- /* If we preallocated the stack space, and some arguments must be passed
- 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. */
-
- 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 this is an addressable type, we cannot pre-evaluate it. */
- if (TREE_ADDRESSABLE (TREE_TYPE (args[i].tree_value)))
- abort ();
-
- push_temp_slots ();
-
- args[i].initial_value = args[i].value
- = expand_expr (args[i].tree_value, NULL_RTX, VOIDmode, 0);
-
- preserve_temp_slots (args[i].value);
- pop_temp_slots ();
-
- /* ANSI doesn't require a sequence point here,
- but PCC has one, so this will avoid some problems. */
- emit_queue ();
-
- args[i].initial_value = args[i].value
- = protect_from_queue (args[i].initial_value, 0);
-
- if (TYPE_MODE (TREE_TYPE (args[i].tree_value)) != args[i].mode)
- args[i].value
- = convert_modes (args[i].mode,
- TYPE_MODE (TREE_TYPE (args[i].tree_value)),
- args[i].value, args[i].unsignedp);
- }
+ /* Precompute any arguments as needed. */
+ precompute_arguments (is_const, must_preallocate, num_actuals,
+ args, &args_size);
/* Now we are about to start emitting insns that can be deleted
if a libcall is deleted. */
}
#endif
+ compute_argument_addresses (args, argblock, num_actuals);
- /* If we preallocated stack space, compute the address of each argument.
- We need not ensure it is a valid memory address here; it will be
- validized when it is used. */
- if (argblock)
- {
- rtx arg_reg = argblock;
- int arg_offset = 0;
-
- if (GET_CODE (argblock) == PLUS)
- arg_reg = XEXP (argblock, 0), arg_offset = INTVAL (XEXP (argblock, 1));
-
- for (i = 0; i < num_actuals; i++)
- {
- rtx offset = ARGS_SIZE_RTX (args[i].offset);
- rtx slot_offset = ARGS_SIZE_RTX (args[i].slot_offset);
- rtx addr;
-
- /* Skip this parm if it will not be passed on the stack. */
- if (! args[i].pass_on_stack && args[i].reg != 0)
- continue;
-
- if (GET_CODE (offset) == CONST_INT)
- addr = plus_constant (arg_reg, INTVAL (offset));
- else
- addr = gen_rtx_PLUS (Pmode, arg_reg, offset);
-
- addr = plus_constant (addr, arg_offset);
- args[i].stack = gen_rtx_MEM (args[i].mode, addr);
- MEM_SET_IN_STRUCT_P
- (args[i].stack,
- AGGREGATE_TYPE_P (TREE_TYPE (args[i].tree_value)));
-
- if (GET_CODE (slot_offset) == CONST_INT)
- addr = plus_constant (arg_reg, INTVAL (slot_offset));
- else
- addr = gen_rtx_PLUS (Pmode, arg_reg, slot_offset);
-
- addr = plus_constant (addr, arg_offset);
- args[i].stack_slot = gen_rtx_MEM (args[i].mode, addr);
- }
- }
-
#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)
- anti_adjust_stack (GEN_INT (args_size.constant
- - original_args_size.constant));
+ anti_adjust_stack (GEN_INT (args_size.constant - unadjusted_args_size));
#endif
#endif
if (argblock)
NO_DEFER_POP;
- /* Get the function to call, in the form of RTL. */
- if (fndecl)
- {
- /* If this is the first use of the function, see if we need to
- make an external definition for it. */
- if (! TREE_USED (fndecl))
- {
- assemble_external (fndecl);
- TREE_USED (fndecl) = 1;
- }
-
- /* Get a SYMBOL_REF rtx for the function address. */
- funexp = XEXP (DECL_RTL (fndecl), 0);
- }
- else
- /* Generate an rtx (probably a pseudo-register) for the address. */
- {
- push_temp_slots ();
- funexp = expand_expr (TREE_OPERAND (exp, 0), NULL_RTX, VOIDmode, 0);
- pop_temp_slots (); /* FUNEXP can't be BLKmode */
-
- /* Check the function is executable. */
- if (current_function_check_memory_usage)
- emit_library_call (chkr_check_exec_libfunc, 1,
- VOIDmode, 1,
- funexp, ptr_mode);
- emit_queue ();
- }
+ funexp = rtx_for_function_call (fndecl, exp);
/* Figure out the register where the value, if any, will come back. */
valreg = 0;
/* If we pushed args in forward order, perform stack alignment
after pushing the last arg. */
if (argblock == 0)
- anti_adjust_stack (GEN_INT (args_size.constant
- - original_args_size.constant));
+ anti_adjust_stack (GEN_INT (args_size.constant - unadjusted_args_size));
#endif
#endif
if (current_function_check_memory_usage)
emit_library_call (chkr_set_right_libfunc, 1,
VOIDmode, 3,
- structure_value_addr, ptr_mode,
+ structure_value_addr, Pmode,
GEN_INT (struct_value_size), TYPE_MODE (sizetype),
GEN_INT (MEMORY_USE_WO),
TYPE_MODE (integer_type_node));
funexp = prepare_call_address (funexp, fndecl, &call_fusage, reg_parm_seen);
- /* Now do the register loads required for any wholly-register parms or any
- parms which are passed both on the stack and in a register. Their
- expressions were already evaluated.
-
- Mark all register-parms as living through the call, putting these USE
- insns in the CALL_INSN_FUNCTION_USAGE field. */
-
-#ifdef LOAD_ARGS_REVERSED
- for (i = num_actuals - 1; i >= 0; i--)
-#else
- for (i = 0; i < num_actuals; i++)
-#endif
- {
- rtx reg = args[i].reg;
- int partial = args[i].partial;
- int nregs;
-
- if (reg)
- {
- /* Set to non-negative if must move a word at a time, even if just
- one word (e.g, partial == 1 && mode == DFmode). Set to -1 if
- we just use a normal move insn. This value can be zero if the
- argument is a zero size structure with no fields. */
- nregs = (partial ? partial
- : (TYPE_MODE (TREE_TYPE (args[i].tree_value)) == BLKmode
- ? ((int_size_in_bytes (TREE_TYPE (args[i].tree_value))
- + (UNITS_PER_WORD - 1)) / UNITS_PER_WORD)
- : -1));
-
- /* Handle calls that pass values in multiple non-contiguous
- 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));
- }
-
- /* If simple case, just do move. If normal partial, store_one_arg
- has already loaded the register for us. In all other cases,
- load the register(s) from memory. */
-
- else if (nregs == -1)
- emit_move_insn (reg, args[i].value);
-
- /* If we have pre-computed the values to put in the registers in
- the case of non-aligned structures, copy them in now. */
-
- else if (args[i].n_aligned_regs != 0)
- for (j = 0; j < args[i].n_aligned_regs; j++)
- emit_move_insn (gen_rtx_REG (word_mode, REGNO (reg) + j),
- args[i].aligned_regs[j]);
-
- else if (partial == 0 || args[i].pass_on_stack)
- move_block_to_reg (REGNO (reg),
- validize_mem (args[i].value), nregs,
- args[i].mode);
-
- /* Handle calls that pass values in multiple non-contiguous
- locations. The Irix 6 ABI has examples of this. */
- if (GET_CODE (reg) == PARALLEL)
- use_group_regs (&call_fusage, reg);
- else if (nregs == -1)
- use_reg (&call_fusage, reg);
- else
- use_regs (&call_fusage, REGNO (reg), nregs == 0 ? 1 : nregs);
- }
- }
+ load_register_parameters (args, num_actuals, &call_fusage);
/* Perform postincrements before actually calling the function. */
emit_queue ();
/* All arguments and registers used for the call must be set up by now! */
/* Generate the actual call instruction. */
- emit_call_1 (funexp, fndecl, funtype, args_size.constant, struct_value_size,
+ emit_call_1 (funexp, fndecl, funtype, unadjusted_args_size,
+ args_size.constant, struct_value_size,
FUNCTION_ARG (args_so_far, VOIDmode, void_type_node, 1),
valreg, old_inhibit_defer_pop, call_fusage, is_const);
get_identifier (XSTR (orgfun, 0)),
build_function_type (outmode == VOIDmode ? void_type_node
: type_for_mode (outmode, 0), NULL_TREE),
- args_size.constant, 0,
+ original_args_size.constant, args_size.constant, 0,
FUNCTION_ARG (args_so_far, VOIDmode, void_type_node, 1),
outmode != VOIDmode ? hard_libcall_value (outmode) : NULL_RTX,
old_inhibit_defer_pop + 1, call_fusage, no_queue);
emit_call_1 (fun,
get_identifier (XSTR (orgfun, 0)),
build_function_type (type_for_mode (outmode, 0), NULL_TREE),
- args_size.constant, struct_value_size,
+ original_args_size.constant, args_size.constant,
+ struct_value_size,
FUNCTION_ARG (args_so_far, VOIDmode, void_type_node, 1),
mem_value == 0 ? hard_libcall_value (outmode) : NULL_RTX,
old_inhibit_defer_pop + 1, call_fusage, is_const);
struct arg_data *arg;
rtx argblock;
int may_be_alloca;
- int variable_size;
+ int variable_size ATTRIBUTE_UNUSED;
int reg_parm_stack_space;
{
register tree pval = arg->tree_value;
if (current_function_check_memory_usage && GET_CODE (arg->stack) == MEM)
{
emit_library_call (chkr_set_right_libfunc, 1, VOIDmode, 3,
- XEXP (arg->stack, 0), ptr_mode,
+ XEXP (arg->stack, 0), Pmode,
ARGS_SIZE_RTX (arg->size),
TYPE_MODE (sizetype),
GEN_INT (MEMORY_USE_RW),