/* Convert function calls to rtl insns, for GNU C compiler.
Copyright (C) 1989, 1992, 1993, 1994, 1995, 1996, 1997, 1998,
- 1999, 2000, 2001, 2002, 2003, 2004, 2005
+ 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007
Free Software Foundation, Inc.
This file is part of GCC.
static void precompute_arguments (int, int, struct arg_data *);
static int compute_argument_block_size (int, struct args_size *, int);
static void initialize_argument_information (int, struct arg_data *,
- struct args_size *, int, tree,
+ struct args_size *, int,
+ tree, tree,
tree, CUMULATIVE_ARGS *, int,
rtx *, int *, int *, int *,
bool *, bool);
static int combine_pending_stack_adjustment_and_call (int, struct args_size *,
unsigned int);
-static tree split_complex_values (tree);
static tree split_complex_types (tree);
#ifdef REG_PARM_STACK_SPACE
if (ecf_flags & ECF_RETURNS_TWICE)
{
REG_NOTES (call_insn) = gen_rtx_EXPR_LIST (REG_SETJMP, const0_rtx,
- REG_NOTES (call_insn));
+ REG_NOTES (call_insn));
current_function_calls_setjmp = 1;
}
/* Exclude functions not at the file scope, or not `extern',
since they are not the magic functions we would otherwise
think they are.
- FIXME: this should be handled with attributes, not with this
- hacky imitation of DECL_ASSEMBLER_NAME. It's (also) wrong
- because you can declare fork() inside a function if you
- wish. */
+ FIXME: this should be handled with attributes, not with this
+ hacky imitation of DECL_ASSEMBLER_NAME. It's (also) wrong
+ because you can declare fork() inside a function if you
+ wish. */
&& (DECL_CONTEXT (fndecl) == NULL_TREE
|| TREE_CODE (DECL_CONTEXT (fndecl)) == TRANSLATION_UNIT_DECL)
&& TREE_PUBLIC (fndecl))
else if ((tname[0] == 'q' && tname[1] == 's'
&& ! strcmp (tname, "qsetjmp"))
|| (tname[0] == 'v' && tname[1] == 'f'
- && ! strcmp (tname, "vfork")))
+ && ! strcmp (tname, "vfork"))
+ || (tname[0] == 'g' && tname[1] == 'e'
+ && !strcmp (tname, "getcontext")))
flags |= ECF_RETURNS_TWICE;
else if (tname[0] == 'l' && tname[1] == 'o'
alloca_call_p (tree exp)
{
if (TREE_CODE (exp) == CALL_EXPR
- && TREE_CODE (TREE_OPERAND (exp, 0)) == ADDR_EXPR
- && (TREE_CODE (TREE_OPERAND (TREE_OPERAND (exp, 0), 0))
- == FUNCTION_DECL)
- && (special_function_p (TREE_OPERAND (TREE_OPERAND (exp, 0), 0),
- 0) & ECF_MAY_BE_ALLOCA))
+ && TREE_CODE (CALL_EXPR_FN (exp)) == ADDR_EXPR
+ && (TREE_CODE (TREE_OPERAND (CALL_EXPR_FN (exp), 0)) == FUNCTION_DECL)
+ && (special_function_p (TREE_OPERAND (CALL_EXPR_FN (exp), 0), 0)
+ & ECF_MAY_BE_ALLOCA))
return true;
return false;
}
flags = flags_from_decl_or_type (decl);
else
{
- t = TREE_TYPE (TREE_OPERAND (t, 0));
+ t = TREE_TYPE (CALL_EXPR_FN (t));
if (t && TREE_CODE (t) == POINTER_TYPE)
flags = flags_from_decl_or_type (TREE_TYPE (t));
else
if (args[i].value == 0)
{
push_temp_slots ();
- args[i].value = expand_expr (args[i].tree_value, NULL_RTX,
- VOIDmode, 0);
+ args[i].value = expand_normal (args[i].tree_value);
preserve_temp_slots (args[i].value);
pop_temp_slots ();
}
= (bytes + UNITS_PER_WORD - 1) / UNITS_PER_WORD;
}
- args[i].aligned_regs = xmalloc (sizeof (rtx) * args[i].n_aligned_regs);
+ args[i].aligned_regs = XNEWVEC (rtx, args[i].n_aligned_regs);
/* Structures smaller than a word are normally aligned to the
least significant byte. On a BYTES_BIG_ENDIAN machine,
}
/* Fill in ARGS_SIZE and ARGS array based on the parameters found in
- ACTPARMS.
+ CALL_EXPR EXP.
NUM_ACTUALS is the total number of parameters.
N_NAMED_ARGS is the total number of named arguments.
+ STRUCT_VALUE_ADDR_VALUE is the implicit argument for a struct return
+ value, or null.
+
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
struct arg_data *args,
struct args_size *args_size,
int n_named_args ATTRIBUTE_UNUSED,
- tree actparms, tree fndecl,
+ tree exp, tree struct_value_addr_value,
+ tree fndecl,
CUMULATIVE_ARGS *args_so_far,
int reg_parm_stack_space,
rtx *old_stack_level, int *old_pending_adj,
int argpos;
int i;
- tree p;
args_size->constant = 0;
args_size->var = 0;
i = 0, inc = 1;
}
+ /* First fill in the actual arguments in the ARGS array, splitting
+ complex arguments if necessary. */
+ {
+ int j = i;
+ call_expr_arg_iterator iter;
+ tree arg;
+
+ if (struct_value_addr_value)
+ {
+ args[j].tree_value = struct_value_addr_value;
+ j += inc;
+ }
+ FOR_EACH_CALL_EXPR_ARG (arg, iter, exp)
+ {
+ tree argtype = TREE_TYPE (arg);
+ if (targetm.calls.split_complex_arg
+ && argtype
+ && TREE_CODE (argtype) == COMPLEX_TYPE
+ && targetm.calls.split_complex_arg (argtype))
+ {
+ tree subtype = TREE_TYPE (argtype);
+ arg = save_expr (arg);
+ args[j].tree_value = build1 (REALPART_EXPR, subtype, arg);
+ j += inc;
+ args[j].tree_value = build1 (IMAGPART_EXPR, subtype, arg);
+ }
+ else
+ args[j].tree_value = arg;
+ j += inc;
+ }
+ }
+
/* 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++)
+ for (argpos = 0; argpos < num_actuals; i += inc, argpos++)
{
- tree type = TREE_TYPE (TREE_VALUE (p));
+ tree type = TREE_TYPE (args[i].tree_value);
int unsignedp;
enum machine_mode mode;
- args[i].tree_value = TREE_VALUE (p);
-
/* Replace erroneous argument with constant zero. */
if (type == error_mark_node || !COMPLETE_TYPE_P (type))
args[i].tree_value = integer_zero_node, type = integer_type_node;
{
/* This is a variable-sized object. Make space on the stack
for it. */
- rtx size_rtx = expr_size (TREE_VALUE (p));
+ rtx size_rtx = expr_size (args[i].tree_value);
if (*old_stack_level == 0)
{
= size_binop (MAX_EXPR, args_size->var,
ssize_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,
- ssize_int (reg_parm_stack_space));
-#endif
+ if (!OUTGOING_REG_PARM_STACK_SPACE)
+ args_size->var
+ = size_binop (MINUS_EXPR, args_size->var,
+ ssize_int (reg_parm_stack_space));
}
}
else
args_size->constant = MAX (args_size->constant,
reg_parm_stack_space);
-#ifndef OUTGOING_REG_PARM_STACK_SPACE
- args_size->constant -= reg_parm_stack_space;
-#endif
+ if (!OUTGOING_REG_PARM_STACK_SPACE)
+ args_size->constant -= reg_parm_stack_space;
}
return unadjusted_args_size;
}
gcc_assert (!TREE_ADDRESSABLE (TREE_TYPE (args[i].tree_value)));
args[i].initial_value = args[i].value
- = expand_expr (args[i].tree_value, NULL_RTX, VOIDmode, 0);
+ = expand_normal (args[i].tree_value);
mode = TYPE_MODE (TREE_TYPE (args[i].tree_value));
if (mode != args[i].mode)
compute and return the final value for MUST_PREALLOCATE. */
static int
-finalize_must_preallocate (int must_preallocate, int num_actuals, struct arg_data *args, struct args_size *args_size)
+finalize_must_preallocate (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.
rtx slot_offset = ARGS_SIZE_RTX (args[i].locate.slot_offset);
rtx addr;
unsigned int align, boundary;
+ unsigned int units_on_stack = 0;
+ enum machine_mode partial_mode = VOIDmode;
/* Skip this parm if it will not be passed on the stack. */
- if (! args[i].pass_on_stack && args[i].reg != 0)
+ if (! args[i].pass_on_stack
+ && args[i].reg != 0
+ && args[i].partial == 0)
continue;
if (GET_CODE (offset) == CONST_INT)
addr = gen_rtx_PLUS (Pmode, arg_reg, offset);
addr = plus_constant (addr, arg_offset);
- args[i].stack = gen_rtx_MEM (args[i].mode, addr);
- set_mem_attributes (args[i].stack,
- TREE_TYPE (args[i].tree_value), 1);
+
+ if (args[i].partial != 0)
+ {
+ /* Only part of the parameter is being passed on the stack.
+ Generate a simple memory reference of the correct size. */
+ units_on_stack = args[i].locate.size.constant;
+ partial_mode = mode_for_size (units_on_stack * BITS_PER_UNIT,
+ MODE_INT, 1);
+ args[i].stack = gen_rtx_MEM (partial_mode, addr);
+ set_mem_size (args[i].stack, GEN_INT (units_on_stack));
+ }
+ else
+ {
+ args[i].stack = gen_rtx_MEM (args[i].mode, addr);
+ set_mem_attributes (args[i].stack,
+ TREE_TYPE (args[i].tree_value), 1);
+ }
align = BITS_PER_UNIT;
boundary = args[i].locate.boundary;
if (args[i].locate.where_pad != downward)
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);
- set_mem_attributes (args[i].stack_slot,
- TREE_TYPE (args[i].tree_value), 1);
+
+ if (args[i].partial != 0)
+ {
+ /* Only part of the parameter is being passed on the stack.
+ Generate a simple memory reference of the correct size.
+ */
+ args[i].stack_slot = gen_rtx_MEM (partial_mode, addr);
+ set_mem_size (args[i].stack_slot, GEN_INT (units_on_stack));
+ }
+ else
+ {
+ args[i].stack_slot = gen_rtx_MEM (args[i].mode, addr);
+ set_mem_attributes (args[i].stack_slot,
+ TREE_TYPE (args[i].tree_value), 1);
+ }
set_mem_align (args[i].stack_slot, args[i].locate.boundary);
/* Function incoming arguments may overlap with sibling call
/* Generate an rtx (probably a pseudo-register) for the address. */
{
push_temp_slots ();
- funexp = expand_expr (addr, NULL_RTX, VOIDmode, 0);
+ funexp = expand_normal (addr);
pop_temp_slots (); /* FUNEXP can't be BLKmode. */
}
return funexp;
}
+/* Return true if and only if SIZE storage units (usually bytes)
+ starting from address ADDR overlap with already clobbered argument
+ area. This function is used to determine if we should give up a
+ sibcall. */
+
+static bool
+mem_overlaps_already_clobbered_arg_p (rtx addr, unsigned HOST_WIDE_INT size)
+{
+ HOST_WIDE_INT i;
+
+ if (addr == current_function_internal_arg_pointer)
+ i = 0;
+ else if (GET_CODE (addr) == PLUS
+ && XEXP (addr, 0) == current_function_internal_arg_pointer
+ && GET_CODE (XEXP (addr, 1)) == CONST_INT)
+ i = INTVAL (XEXP (addr, 1));
+ /* Return true for arg pointer based indexed addressing. */
+ else if (GET_CODE (addr) == PLUS
+ && (XEXP (addr, 0) == current_function_internal_arg_pointer
+ || XEXP (addr, 1) == current_function_internal_arg_pointer))
+ return true;
+ else
+ return false;
+
+#ifdef ARGS_GROW_DOWNWARD
+ i = -i - size;
+#endif
+ if (size > 0)
+ {
+ unsigned HOST_WIDE_INT k;
+
+ for (k = 0; k < size; k++)
+ if (i + k < stored_args_map->n_bits
+ && TEST_BIT (stored_args_map, i + k))
+ return true;
+ }
+
+ return false;
+}
+
/* 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.
{
rtx mem = validize_mem (args[i].value);
+ /* Check for overlap with already clobbered argument area. */
+ if (is_sibcall
+ && mem_overlaps_already_clobbered_arg_p (XEXP (args[i].value, 0),
+ size))
+ *sibcall_failure = 1;
+
/* Handle a BLKmode that needs shifting. */
if (nregs == 1 && size < UNITS_PER_WORD
#ifdef BLOCK_REG_PADDING
{
RTX_CODE code;
int i, j;
- unsigned int k;
const char *fmt;
if (x == NULL_RTX)
code = GET_CODE (x);
if (code == MEM)
- {
- if (XEXP (x, 0) == current_function_internal_arg_pointer)
- i = 0;
- else if (GET_CODE (XEXP (x, 0)) == PLUS
- && XEXP (XEXP (x, 0), 0) ==
- current_function_internal_arg_pointer
- && GET_CODE (XEXP (XEXP (x, 0), 1)) == CONST_INT)
- i = INTVAL (XEXP (XEXP (x, 0), 1));
- else
- return 0;
-
-#ifdef ARGS_GROW_DOWNWARD
- i = -i - GET_MODE_SIZE (GET_MODE (x));
-#endif
-
- for (k = 0; k < GET_MODE_SIZE (GET_MODE (x)); k++)
- if (i + k < stored_args_map->n_bits
- && TEST_BIT (stored_args_map, i + k))
- return 1;
-
- return 0;
- }
+ return mem_overlaps_already_clobbered_arg_p (XEXP (x, 0),
+ GET_MODE_SIZE (GET_MODE (x)));
/* Scan all subexpressions. */
fmt = GET_RTX_FORMAT (code);
return true;
}
-/* Generate all the code for a function call
+/* Generate all the code for a CALL_EXPR exp
and return an rtx for its value.
Store the value in TARGET (specified as an rtx) if convenient.
If the value is stored in TARGET then TARGET is returned.
/* Nonzero if we are currently expanding a call. */
static int currently_expanding_call = 0;
- /* List of actual parameters. */
- tree actparms = TREE_OPERAND (exp, 1);
/* RTX for the function to be called. */
rtx funexp;
/* Sequence of insns to perform a normal "call". */
an extra, implicit first parameter. Otherwise,
it is passed by being copied directly into struct_value_rtx. */
int structure_value_addr_parm = 0;
+ /* Holds the value of implicit argument for the struct value. */
+ tree structure_value_addr_value = NULL_TREE;
/* Size of aggregate value wanted, or zero if none wanted
or if we are using the non-reentrant PCC calling convention
or expecting the value in registers. */
/* Number of named args. Args after this are anonymous ones
and they must all go on the stack. */
int n_named_args;
+ /* Number of complex actual arguments that need to be split. */
+ int num_complex_actuals = 0;
/* Vector of information about each argument.
Arguments are numbered in the order they will be pushed,
int old_stack_pointer_delta = 0;
rtx call_fusage;
- tree p = TREE_OPERAND (exp, 0);
- tree addr = TREE_OPERAND (exp, 0);
+ tree p = CALL_EXPR_FN (exp);
+ tree addr = CALL_EXPR_FN (exp);
int i;
/* The alignment of the stack, in bits. */
unsigned HOST_WIDE_INT preferred_stack_boundary;
{
bool volatilep = false;
tree arg;
+ call_expr_arg_iterator iter;
- for (arg = actparms; arg; arg = TREE_CHAIN (arg))
- if (TREE_THIS_VOLATILE (TREE_VALUE (arg)))
+ FOR_EACH_CALL_EXPR_ARG (arg, iter, exp)
+ if (TREE_THIS_VOLATILE (arg))
{
volatilep = true;
break;
if (! volatilep)
{
- for (arg = actparms; arg; arg = TREE_CHAIN (arg))
- expand_expr (TREE_VALUE (arg), const0_rtx,
- VOIDmode, EXPAND_NORMAL);
+ FOR_EACH_CALL_EXPR_ARG (arg, iter, exp)
+ expand_expr (arg, const0_rtx, VOIDmode, EXPAND_NORMAL);
return const0_rtx;
}
}
reg_parm_stack_space = REG_PARM_STACK_SPACE (fndecl);
#endif
-#ifndef OUTGOING_REG_PARM_STACK_SPACE
- if (reg_parm_stack_space > 0 && PUSH_ARGS)
+ if (!OUTGOING_REG_PARM_STACK_SPACE && reg_parm_stack_space > 0 && PUSH_ARGS)
must_preallocate = 1;
-#endif
/* Set up a place to return a structure. */
/* For variable-sized objects, we must be called with a target
specified. If we were to allocate space on the stack here,
we would have no way of knowing when to free it. */
- rtx d = assign_temp (TREE_TYPE (exp), 1, 1, 1);
+ rtx d = assign_temp (TREE_TYPE (exp), 0, 1, 1);
mark_temp_addr_taken (d);
structure_value_addr = XEXP (d, 0);
gcc_assert (POINTER_TYPE_P (funtype));
funtype = TREE_TYPE (funtype);
- /* Munge the tree to split complex arguments into their imaginary
- and real parts. */
+ /* Count whether there are actual complex arguments that need to be split
+ into their real and imaginary parts. Munge the type_arg_types
+ appropriately here as well. */
if (targetm.calls.split_complex_arg)
{
+ call_expr_arg_iterator iter;
+ tree arg;
+ FOR_EACH_CALL_EXPR_ARG (arg, iter, exp)
+ {
+ tree type = TREE_TYPE (arg);
+ if (type && TREE_CODE (type) == COMPLEX_TYPE
+ && targetm.calls.split_complex_arg (type))
+ num_complex_actuals++;
+ }
type_arg_types = split_complex_types (TYPE_ARG_TYPES (funtype));
- actparms = split_complex_values (actparms);
}
else
type_arg_types = TYPE_ARG_TYPES (funtype);
current_function_calls_alloca = 1;
/* If struct_value_rtx is 0, it means pass the address
- as if it were an extra parameter. */
+ as if it were an extra parameter. Put the argument expression
+ in structure_value_addr_value. */
if (structure_value_addr && struct_value == 0)
{
/* If structure_value_addr is a REG other than
(Pmode, structure_value_addr))
: structure_value_addr);
- actparms
- = tree_cons (error_mark_node,
- make_tree (build_pointer_type (TREE_TYPE (funtype)),
- temp),
- actparms);
+ structure_value_addr_value =
+ make_tree (build_pointer_type (TREE_TYPE (funtype)), temp);
structure_value_addr_parm = 1;
}
/* Count the arguments and set NUM_ACTUALS. */
- for (p = actparms, num_actuals = 0; p; p = TREE_CHAIN (p))
- num_actuals++;
+ num_actuals =
+ call_expr_nargs (exp) + num_complex_actuals + structure_value_addr_parm;
/* Compute number of named args.
First, do a raw count of the args for INIT_CUMULATIVE_ARGS. */
/* Build up entries in the 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,
+ n_named_args, exp,
+ structure_value_addr_value, fndecl,
&args_so_far, reg_parm_stack_space,
&old_stack_level, &old_pending_adj,
&must_preallocate, &flags,
into a sibcall. */
|| !targetm.function_ok_for_sibcall (fndecl, exp)
/* Functions that do not return exactly once may not be sibcall
- optimized. */
+ optimized. */
|| (flags & (ECF_RETURNS_TWICE | ECF_NORETURN))
|| TYPE_VOLATILE (TREE_TYPE (TREE_TYPE (addr)))
/* If the called function is nested in the current one, it might access
- some of the caller's arguments, but could clobber them beforehand if
- the argument areas are shared. */
+ some of the caller's arguments, but could clobber them beforehand if
+ the argument areas are shared. */
|| (fndecl && decl_function_context (fndecl) == current_function_decl)
/* If this function requires more stack slots than the current
function, we cannot change it into a sibling call.
old_stack_allocated = stack_pointer_delta - pending_stack_adjust;
/* The argument block when performing a sibling call is the
- incoming argument block. */
+ incoming argument block. */
if (pass == 0)
{
argblock = virtual_incoming_args_rtx;
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;
-#endif
+ if (!OUTGOING_REG_PARM_STACK_SPACE)
+ needed += reg_parm_stack_space;
#ifdef ARGS_GROW_DOWNWARD
highest_outgoing_arg_in_use = MAX (initial_highest_arg_in_use,
#endif
if (stack_usage_map_buf)
free (stack_usage_map_buf);
- stack_usage_map_buf = xmalloc (highest_outgoing_arg_in_use);
+ stack_usage_map_buf = XNEWVEC (char, highest_outgoing_arg_in_use);
stack_usage_map = stack_usage_map_buf;
if (initial_highest_arg_in_use)
an argument. */
if (stack_arg_under_construction)
{
-#ifndef OUTGOING_REG_PARM_STACK_SPACE
- rtx push_size = GEN_INT (reg_parm_stack_space
- + adjusted_args_size.constant);
-#else
- rtx push_size = GEN_INT (adjusted_args_size.constant);
-#endif
+ rtx push_size
+ = GEN_INT (adjusted_args_size.constant
+ + (OUTGOING_REG_PARM_STACK_SPACE ? 0
+ : reg_parm_stack_space));
if (old_stack_level == 0)
{
emit_stack_save (SAVE_BLOCK, &old_stack_level,
/* Make a new map for the new argument list. */
if (stack_usage_map_buf)
free (stack_usage_map_buf);
- stack_usage_map_buf = xmalloc (highest_outgoing_arg_in_use);
+ stack_usage_map_buf = XCNEWVEC (char, highest_outgoing_arg_in_use);
stack_usage_map = stack_usage_map_buf;
- memset (stack_usage_map, 0, highest_outgoing_arg_in_use);
highest_outgoing_arg_in_use = 0;
}
allocate_dynamic_stack_space (push_size, NULL_RTX,
else
valreg = hard_function_value (TREE_TYPE (exp), fndecl, fntype,
(pass == 0));
+
+ /* If VALREG is a PARALLEL whose first member has a zero
+ offset, use that. This is for targets such as m68k that
+ return the same value in multiple places. */
+ if (GET_CODE (valreg) == PARALLEL)
+ {
+ rtx elem = XVECEXP (valreg, 0, 0);
+ rtx where = XEXP (elem, 0);
+ rtx offset = XEXP (elem, 1);
+ if (offset == const0_rtx
+ && GET_MODE (where) == GET_MODE (valreg))
+ valreg = where;
+ }
}
/* Precompute all register parameters. It isn't safe to compute anything
once we have started filling any specific hard regs. */
precompute_register_parameters (num_actuals, args, ®_parm_seen);
- if (TREE_OPERAND (exp, 2))
- static_chain_value = expand_expr (TREE_OPERAND (exp, 2),
- NULL_RTX, VOIDmode, 0);
+ if (CALL_EXPR_STATIC_CHAIN (exp))
+ static_chain_value = expand_normal (CALL_EXPR_STATIC_CHAIN (exp));
else
static_chain_value = 0;
/* If register arguments require space on the stack and stack space
was not preallocated, allocate stack space here for arguments
passed in registers. */
-#ifdef OUTGOING_REG_PARM_STACK_SPACE
- if (!ACCUMULATE_OUTGOING_ARGS
+ if (OUTGOING_REG_PARM_STACK_SPACE && !ACCUMULATE_OUTGOING_ARGS
&& must_preallocate == 0 && reg_parm_stack_space > 0)
anti_adjust_stack (GEN_INT (reg_parm_stack_space));
-#endif
/* Pass the function the address in which to return a
structure value. */
rtx insn;
bool failed = valreg == 0 || GET_CODE (valreg) == PARALLEL;
- insns = get_insns ();
+ insns = get_insns ();
/* Expansion of block moves possibly introduced a loop that may
not appear inside libcall block. */
&& GET_MODE (target) == TYPE_MODE (TREE_TYPE (exp))
&& GET_MODE (target) == GET_MODE (valreg))
{
- /* TARGET and VALREG cannot be equal at this point because the
- latter would not have REG_FUNCTION_VALUE_P true, while the
- former would if it were referring to the same register.
-
- If they refer to the same register, this move will be a no-op,
- except when function inlining is being done. */
- emit_move_insn (target, valreg);
-
- /* If we are setting a MEM, this code must be executed. Since it is
- emitted after the call insn, sibcall optimization cannot be
- performed in that case. */
- if (MEM_P (target))
- sibcall_failure = 1;
+ bool may_overlap = false;
+
+ /* We have to copy a return value in a CLASS_LIKELY_SPILLED hard
+ reg to a plain register. */
+ if (REG_P (valreg)
+ && HARD_REGISTER_P (valreg)
+ && CLASS_LIKELY_SPILLED_P (REGNO_REG_CLASS (REGNO (valreg)))
+ && !(REG_P (target) && !HARD_REGISTER_P (target)))
+ valreg = copy_to_reg (valreg);
+
+ /* If TARGET is a MEM in the argument area, and we have
+ saved part of the argument area, then we can't store
+ directly into TARGET as it may get overwritten when we
+ restore the argument save area below. Don't work too
+ hard though and simply force TARGET to a register if it
+ is a MEM; the optimizer is quite likely to sort it out. */
+ if (ACCUMULATE_OUTGOING_ARGS && pass && MEM_P (target))
+ for (i = 0; i < num_actuals; i++)
+ if (args[i].save_area)
+ {
+ may_overlap = true;
+ break;
+ }
+
+ if (may_overlap)
+ target = copy_to_reg (valreg);
+ else
+ {
+ /* TARGET and VALREG cannot be equal at this point
+ because the latter would not have
+ REG_FUNCTION_VALUE_P true, while the former would if
+ it were referring to the same register.
+
+ If they refer to the same register, this move will be
+ a no-op, except when function inlining is being
+ done. */
+ emit_move_insn (target, valreg);
+
+ /* If we are setting a MEM, this code must be executed.
+ Since it is emitted after the call insn, sibcall
+ optimization cannot be performed in that case. */
+ if (MEM_P (target))
+ sibcall_failure = 1;
+ }
}
else if (TYPE_MODE (TREE_TYPE (exp)) == BLKmode)
{
int unsignedp = TYPE_UNSIGNED (type);
int offset = 0;
enum machine_mode pmode;
-
+
pmode = promote_mode (type, TYPE_MODE (type), &unsignedp, 1);
/* If we don't promote as expected, something is wrong. */
gcc_assert (GET_MODE (target) == pmode);
-
+
if ((WORDS_BIG_ENDIAN || BYTES_BIG_ENDIAN)
&& (GET_MODE_SIZE (GET_MODE (target))
> GET_MODE_SIZE (TYPE_MODE (type))))
for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
{
+ rtx note;
+
/* There are never REG_EQUIV notes for the incoming arguments
after the NOTE_INSN_FUNCTION_BEG note, so stop if we see it. */
if (NOTE_P (insn)
&& NOTE_LINE_NUMBER (insn) == NOTE_INSN_FUNCTION_BEG)
break;
- while (1)
- {
- rtx note = find_reg_note (insn, REG_EQUIV, 0);
- if (note)
- {
- /* Remove the note and keep looking at the notes for
- this insn. */
- remove_note (insn, note);
- continue;
- }
- break;
- }
+ note = find_reg_note (insn, REG_EQUIV, 0);
+ if (note)
+ remove_note (insn, note);
+ note = find_reg_note (insn, REG_EQUIV, 0);
+ gcc_assert (!note);
}
}
-/* Traverse an argument list in VALUES and expand all complex
- arguments into their components. */
-static tree
-split_complex_values (tree values)
-{
- tree p;
-
- /* Before allocating memory, check for the common case of no complex. */
- for (p = values; p; p = TREE_CHAIN (p))
- {
- tree type = TREE_TYPE (TREE_VALUE (p));
- if (type && TREE_CODE (type) == COMPLEX_TYPE
- && targetm.calls.split_complex_arg (type))
- goto found;
- }
- return values;
-
- found:
- values = copy_list (values);
-
- for (p = values; p; p = TREE_CHAIN (p))
- {
- tree complex_value = TREE_VALUE (p);
- tree complex_type;
-
- complex_type = TREE_TYPE (complex_value);
- if (!complex_type)
- continue;
-
- if (TREE_CODE (complex_type) == COMPLEX_TYPE
- && targetm.calls.split_complex_arg (complex_type))
- {
- tree subtype;
- tree real, imag, next;
-
- subtype = TREE_TYPE (complex_type);
- complex_value = save_expr (complex_value);
- real = build1 (REALPART_EXPR, subtype, complex_value);
- imag = build1 (IMAGPART_EXPR, subtype, complex_value);
-
- TREE_VALUE (p) = real;
- next = TREE_CHAIN (p);
- imag = build_tree_list (NULL_TREE, imag);
- TREE_CHAIN (p) = imag;
- TREE_CHAIN (imag) = next;
-
- /* Skip the newly created node. */
- p = TREE_CHAIN (p);
- }
- }
-
- return values;
-}
-
/* Traverse a list of TYPES and expand all complex types into their
components. */
static tree
tree type = TREE_VALUE (p);
if (TREE_CODE (type) == COMPLEX_TYPE
&& targetm.calls.split_complex_arg (type))
- goto found;
+ goto found;
}
return types;
if (mem_value && struct_value == 0 && ! pcc_struct_value)
{
rtx addr = XEXP (mem_value, 0);
-
+
nargs++;
/* Make sure it is a reasonable operand for a move or push insn. */
locate_and_pad_parm (Pmode, NULL_TREE,
#ifdef STACK_PARMS_IN_REG_PARM_AREA
- 1,
+ 1,
#else
argvec[count].reg != 0,
#endif
args_size.constant = MAX (args_size.constant,
reg_parm_stack_space);
-#ifndef OUTGOING_REG_PARM_STACK_SPACE
- args_size.constant -= reg_parm_stack_space;
-#endif
+ if (!OUTGOING_REG_PARM_STACK_SPACE)
+ args_size.constant -= reg_parm_stack_space;
if (args_size.constant > current_function_outgoing_args_size)
current_function_outgoing_args_size = 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;
-#endif
+ if (!OUTGOING_REG_PARM_STACK_SPACE)
+ needed += reg_parm_stack_space;
#ifdef ARGS_GROW_DOWNWARD
highest_outgoing_arg_in_use = MAX (initial_highest_arg_in_use,
highest_outgoing_arg_in_use = MAX (initial_highest_arg_in_use,
needed);
#endif
- stack_usage_map_buf = xmalloc (highest_outgoing_arg_in_use);
+ stack_usage_map_buf = XNEWVEC (char, highest_outgoing_arg_in_use);
stack_usage_map = stack_usage_map_buf;
if (initial_highest_arg_in_use)
needed = 0;
/* We must be careful to use virtual regs before they're instantiated,
- and real regs afterwards. Loop optimization, for example, can create
+ and real regs afterwards. Loop optimization, for example, can create
new libcalls after we've instantiated the virtual regs, and if we
use virtuals anyway, they won't match the rtl patterns. */
{
argvec[argnum].save_area
= assign_stack_temp (BLKmode,
- argvec[argnum].locate.size.constant,
+ argvec[argnum].locate.size.constant,
0);
emit_block_move (validize_mem (argvec[argnum].save_area),
- stack_area,
+ stack_area,
GEN_INT (argvec[argnum].locate.size.constant),
BLOCK_OP_CALL_PARM);
}
auto-increment causes confusion. So we merely indicate
that we access something with a known mode somewhere on
the stack. */
- use = gen_rtx_PLUS (Pmode, virtual_outgoing_args_rtx,
+ use = gen_rtx_PLUS (Pmode, virtual_outgoing_args_rtx,
gen_rtx_SCRATCH (Pmode));
use = gen_rtx_MEM (argvec[argnum].mode, use);
use = gen_rtx_USE (VOIDmode, use);
if (reg != 0 && GET_CODE (reg) == PARALLEL)
use_group_regs (&call_fusage, reg);
else if (reg != 0)
- use_reg (&call_fusage, reg);
+ {
+ int partial = argvec[count].partial;
+ if (partial)
+ {
+ int nregs;
+ gcc_assert (partial % UNITS_PER_WORD == 0);
+ nregs = partial / UNITS_PER_WORD;
+ use_regs (&call_fusage, REGNO (reg), nregs);
+ }
+ else
+ use_reg (&call_fusage, reg);
+ }
}
/* Pass the function the address in which to return a structure value. */
value = gen_reg_rtx (outmode);
emit_group_store (value, valreg, NULL_TREE, GET_MODE_SIZE (outmode));
}
- else if (value != 0)
- emit_move_insn (value, valreg);
else
- value = valreg;
+ {
+ /* Convert to the proper mode if PROMOTE_MODE has been active. */
+ if (GET_MODE (valreg) != outmode)
+ {
+ int unsignedp = TYPE_UNSIGNED (tfom);
+
+ gcc_assert (targetm.calls.promote_function_return (tfom));
+ gcc_assert (promote_mode (tfom, outmode, &unsignedp, 0)
+ == GET_MODE (valreg));
+
+ valreg = convert_modes (outmode, GET_MODE (valreg), valreg, 0);
+ }
+
+ if (value != 0)
+ emit_move_insn (value, valreg);
+ else
+ value = valreg;
+ }
}
if (ACCUMULATE_OUTGOING_ARGS)
if (save_mode == BLKmode)
emit_block_move (stack_area,
- validize_mem (argvec[count].save_area),
+ validize_mem (argvec[count].save_area),
GEN_INT (argvec[count].locate.size.constant),
BLOCK_OP_CALL_PARM);
else
arg->save_area = assign_temp (nt, 0, 1, 1);
preserve_temp_slots (arg->save_area);
emit_block_move (validize_mem (arg->save_area), stack_area,
- expr_size (arg->tree_value),
+ GEN_INT (arg->locate.size.constant),
BLOCK_OP_CALL_PARM);
}
else
/* Being passed entirely in a register. We shouldn't be called in
this case. */
gcc_assert (reg == 0 || partial != 0);
-
+
/* If this arg needs special alignment, don't load the registers
here. */
if (arg->n_aligned_regs != 0)
}
/* Check for overlap with already clobbered argument area. */
- if ((flags & ECF_SIBCALL) && MEM_P (arg->value))
- {
- int i = -1;
- unsigned HOST_WIDE_INT k;
- rtx x = arg->value;
-
- if (XEXP (x, 0) == current_function_internal_arg_pointer)
- i = 0;
- else if (GET_CODE (XEXP (x, 0)) == PLUS
- && XEXP (XEXP (x, 0), 0) ==
- current_function_internal_arg_pointer
- && GET_CODE (XEXP (XEXP (x, 0), 1)) == CONST_INT)
- i = INTVAL (XEXP (XEXP (x, 0), 1));
- else
- i = -1;
-
- if (i >= 0)
- {
-#ifdef ARGS_GROW_DOWNWARD
- i = -i - arg->locate.size.constant;
-#endif
- if (arg->locate.size.constant > 0)
- {
- unsigned HOST_WIDE_INT sc = arg->locate.size.constant;
-
- for (k = 0; k < sc; k++)
- if (i + k < stored_args_map->n_bits
- && TEST_BIT (stored_args_map, i + k))
- {
- sibcall_failure = 1;
- break;
- }
- }
- }
- }
+ if ((flags & ECF_SIBCALL)
+ && MEM_P (arg->value)
+ && mem_overlaps_already_clobbered_arg_p (XEXP (arg->value, 0),
+ arg->locate.size.constant))
+ sibcall_failure = 1;
/* Don't allow anything left on stack from computation
of argument to alloca. */
else if (arg->mode != BLKmode)
{
int size;
+ unsigned int parm_align;
/* Argument is a scalar, not entirely passed in registers.
(If part is passed in registers, arg->partial says how much
/ (PARM_BOUNDARY / BITS_PER_UNIT))
* (PARM_BOUNDARY / BITS_PER_UNIT));
+ /* Compute the alignment of the pushed argument. */
+ parm_align = arg->locate.boundary;
+ if (FUNCTION_ARG_PADDING (arg->mode, TREE_TYPE (pval)) == downward)
+ {
+ int pad = used - size;
+ if (pad)
+ {
+ unsigned int pad_align = (pad & -pad) * BITS_PER_UNIT;
+ parm_align = MIN (parm_align, pad_align);
+ }
+ }
+
/* This isn't already where we want it on the stack, so put it there.
This can either be done with push or copy insns. */
emit_push_insn (arg->value, arg->mode, TREE_TYPE (pval), NULL_RTX,
- PARM_BOUNDARY, partial, reg, used - size, argblock,
+ parm_align, partial, reg, used - size, argblock,
ARGS_SIZE_RTX (arg->locate.offset), reg_parm_stack_space,
ARGS_SIZE_RTX (arg->locate.alignment_pad));