/* 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.
You should have received a copy of the GNU General Public License
along with GCC; see the file COPYING. If not, write to the Free
-Software Foundation, 59 Temple Place - Suite 330, Boston, MA
-02111-1307, USA. */
+Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301, USA. */
#include "config.h"
#include "system.h"
/* If REG was promoted from the actual mode of the argument expression,
indicates whether the promotion is sign- or zero-extended. */
int unsignedp;
- /* Number of registers to use. 0 means put the whole arg in registers.
- Also 0 if not passed in registers. */
+ /* Number of bytes to put in registers. 0 means put the whole arg
+ in registers. Also 0 if not passed in registers. */
int partial;
/* Nonzero if argument must be passed on stack.
Note that some arguments may be passed on the stack
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;
}
For example, if the function might return more than one time (setjmp), then
set RETURNS_TWICE to a nonzero value.
- Similarly set LONGJMP for if the function is in the longjmp family.
+ Similarly set NORETURN if the function is in the longjmp family.
Set MAY_BE_ALLOCA for any memory allocation function that might allocate
space from the stack such as alloca. */
/* 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'
return flags;
}
-/* Return nonzero when tree represent call to longjmp. */
+/* Return nonzero when FNDECL represents a call to setjmp. */
int
setjmp_call_p (tree fndecl)
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;
}
if (DECL_P (exp))
{
- struct cgraph_rtl_info *i = cgraph_rtl_info (exp);
type = TREE_TYPE (exp);
- if (i)
- {
- if (i->pure_function)
- flags |= ECF_PURE | ECF_LIBCALL_BLOCK;
- if (i->const_function)
- flags |= ECF_CONST | ECF_LIBCALL_BLOCK;
- }
-
/* The function exp may have the `malloc' attribute. */
if (DECL_IS_MALLOC (exp))
flags |= ECF_MALLOC;
/* The function exp may have the `pure' attribute. */
if (DECL_IS_PURE (exp))
- flags |= ECF_PURE | ECF_LIBCALL_BLOCK;
+ flags |= ECF_PURE;
if (DECL_IS_NOVOPS (exp))
flags |= ECF_NOVOPS;
flags |= ECF_NOTHROW;
if (TREE_READONLY (exp) && ! TREE_THIS_VOLATILE (exp))
- flags |= ECF_LIBCALL_BLOCK | ECF_CONST;
+ flags |= ECF_CONST;
flags = special_function_p (exp, flags);
}
if (TREE_CODE (type) == FUNCTION_TYPE && TYPE_RETURNS_STACK_DEPRESSED (type))
{
flags |= ECF_SP_DEPRESSED;
- flags &= ~(ECF_PURE | ECF_CONST | ECF_LIBCALL_BLOCK);
+ flags &= ~(ECF_PURE | ECF_CONST);
}
return flags;
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.
Mark all register-parms as living through the call, putting these USE
insns in the CALL_INSN_FUNCTION_USAGE field.
- When IS_SIBCALL, perform the check_sibcall_overlap_argument_overlap
+ When IS_SIBCALL, perform the check_sibcall_argument_overlap
checking, setting *SIBCALL_FAILURE if appropriate. */
static void
int nregs;
int size = 0;
rtx before_arg = get_last_insn ();
- /* 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. */
+ /* Set non-negative if we must move a word at a time, even if
+ just one word (e.g, partial == 4 && 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. */
nregs = -1;
if (GET_CODE (reg) == PARALLEL)
;
{
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;
}
-/* Remove all REG_EQUIV notes found in the insn chain. */
-
-static void
-purge_reg_equiv_notes (void)
-{
- rtx insn;
-
- for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
- {
- 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;
- }
- }
-}
-
-/* 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 initial_highest_arg_in_use = highest_outgoing_arg_in_use;
char *initial_stack_usage_map = stack_usage_map;
+ char *stack_usage_map_buf = NULL;
int old_stack_allocated;
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;
/* Warn if this value is an aggregate type,
regardless of which calling convention we are using for it. */
- if (warn_aggregate_return && AGGREGATE_TYPE_P (TREE_TYPE (exp)))
- warning ("function call has aggregate value");
+ if (AGGREGATE_TYPE_P (TREE_TYPE (exp)))
+ warning (OPT_Waggregate_return, "function call has aggregate value");
/* If the result of a pure or const function call is ignored (or void),
and none of its arguments are volatile, we can avoid expanding the
{
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. */
{
struct_value_size = int_size_in_bytes (TREE_TYPE (exp));
- if (CALL_EXPR_HAS_RETURN_SLOT_ADDR (exp))
- {
- /* The structure value address arg is already in actparms.
- Pull it out. It might be nice to just leave it there, but
- we need to set structure_value_addr. */
- tree return_arg = TREE_VALUE (actparms);
- actparms = TREE_CHAIN (actparms);
- structure_value_addr = expand_expr (return_arg, NULL_RTX,
- VOIDmode, EXPAND_NORMAL);
- }
- else if (target && MEM_P (target))
+ if (target && MEM_P (target) && CALL_EXPR_RETURN_SLOT_OPT (exp))
structure_value_addr = XEXP (target, 0);
else
{
/* 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. */
- || args_size.constant > current_function_args_size
+ function, we cannot change it into a sibling call.
+ current_function_pretend_args_size is not part of the
+ stack allocated by our caller. */
+ || args_size.constant > (current_function_args_size
+ - current_function_pretend_args_size)
/* If the callee pops its own arguments, then it must pop exactly
the same number of arguments as the current function. */
|| (RETURN_POPS_ARGS (fndecl, funtype, args_size.constant)
Also, do all pending adjustments now if there is any chance
this might be a call to alloca or if we are expanding a sibling
call sequence or if we are calling a function that is to return
- with stack pointer depressed. */
+ with stack pointer depressed.
+ Also do the adjustments before a throwing call, otherwise
+ exception handling can fail; PR 19225. */
if (pending_stack_adjust >= 32
|| (pending_stack_adjust > 0
&& (flags & (ECF_MAY_BE_ALLOCA | ECF_SP_DEPRESSED)))
+ || (pending_stack_adjust > 0
+ && flag_exceptions && !(flags & ECF_NOTHROW))
|| pass == 0)
do_pending_stack_adjust ();
if (pass && (flags & (ECF_LIBCALL_BLOCK | ECF_MALLOC)))
start_sequence ();
+ if (pass == 0 && cfun->stack_protect_guard)
+ stack_protect_epilogue ();
+
adjusted_args_size = args_size;
/* Compute the actual size of the argument block required. The variable
and constant sizes must be combined, the size may have to be rounded,
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,
highest_outgoing_arg_in_use = MAX (initial_highest_arg_in_use,
needed);
#endif
- stack_usage_map = alloca (highest_outgoing_arg_in_use);
+ if (stack_usage_map_buf)
+ free (stack_usage_map_buf);
+ stack_usage_map_buf = XNEWVEC (char, highest_outgoing_arg_in_use);
+ stack_usage_map = stack_usage_map_buf;
if (initial_highest_arg_in_use)
memcpy (stack_usage_map, initial_stack_usage_map,
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,
= stack_arg_under_construction;
stack_arg_under_construction = 0;
/* Make a new map for the new argument list. */
- stack_usage_map = alloca (highest_outgoing_arg_in_use);
- memset (stack_usage_map, 0, highest_outgoing_arg_in_use);
+ if (stack_usage_map_buf)
+ free (stack_usage_map_buf);
+ stack_usage_map_buf = XCNEWVEC (char, highest_outgoing_arg_in_use);
+ stack_usage_map = stack_usage_map_buf;
highest_outgoing_arg_in_use = 0;
}
allocate_dynamic_stack_space (push_size, NULL_RTX,
{
if (pcc_struct_value)
valreg = hard_function_value (build_pointer_type (TREE_TYPE (exp)),
- fndecl, (pass == 0));
+ fndecl, NULL, (pass == 0));
else
- valreg = hard_function_value (TREE_TYPE (exp), fndecl, (pass == 0));
+ 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))))
emit_move_insn (virtual_stack_dynamic_rtx, stack_pointer_rtx);
}
+ if (stack_usage_map_buf)
+ free (stack_usage_map_buf);
+
return target;
}
this function's incoming arguments.
At the start of RTL generation we know the only REG_EQUIV notes
- in the rtl chain are those for incoming arguments, so we can safely
- flush any REG_EQUIV note.
+ in the rtl chain are those for incoming arguments, so we can look
+ for REG_EQUIV notes between the start of the function and the
+ NOTE_INSN_FUNCTION_BEG.
This is (slight) overkill. We could keep track of the highest
argument we clobber and be more selective in removing notes, but it
does not seem to be worth the effort. */
+
void
fixup_tail_calls (void)
{
- purge_reg_equiv_notes ();
-}
-
-/* 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);
+ rtx insn;
- for (p = values; p; p = TREE_CHAIN (p))
+ for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
{
- 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);
+ rtx note;
- TREE_VALUE (p) = real;
- next = TREE_CHAIN (p);
- imag = build_tree_list (NULL_TREE, imag);
- TREE_CHAIN (p) = imag;
- TREE_CHAIN (imag) = next;
+ /* 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;
- /* Skip the newly created node. */
- p = TREE_CHAIN (p);
- }
+ 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);
}
-
- return values;
}
/* Traverse a list of TYPES and expand all complex types into their
tree type = TREE_VALUE (p);
if (TREE_CODE (type) == COMPLEX_TYPE
&& targetm.calls.split_complex_arg (type))
- goto found;
+ goto found;
}
return types;
/* 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;
+ char *stack_usage_map_buf = NULL;
rtx struct_value = targetm.calls.struct_value_rtx (0, 0);
{
#ifdef PCC_STATIC_STRUCT_RETURN
rtx pointer_reg
- = hard_function_value (build_pointer_type (tfom), 0, 0);
+ = hard_function_value (build_pointer_type (tfom), 0, 0, 0);
mem_value = gen_rtx_MEM (outmode, pointer_reg);
pcc_struct_value = 1;
if (value == 0)
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 = alloca (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)
memcpy (stack_usage_map, initial_stack_usage_map,
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);
}
stack_usage_map[i] = 1;
NO_DEFER_POP;
+
+ if (flags & ECF_CONST)
+ {
+ rtx use;
+
+ /* Indicate argument access so that alias.c knows that these
+ values are live. */
+ if (argblock)
+ use = plus_constant (argblock,
+ argvec[argnum].locate.offset.constant);
+ else
+ /* When arguments are pushed, trying to tell alias.c where
+ exactly this argument is won't work, because the
+ 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,
+ gen_rtx_SCRATCH (Pmode));
+ use = gen_rtx_MEM (argvec[argnum].mode, use);
+ use = gen_rtx_USE (VOIDmode, use);
+ call_fusage = gen_rtx_EXPR_LIST (VOIDmode, use, call_fusage);
+ }
}
}
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
stack_usage_map = initial_stack_usage_map;
}
+ if (stack_usage_map_buf)
+ free (stack_usage_map_buf);
+
return value;
}
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)
stack_arg_under_construction--;
}
+ /* Check for overlap with already clobbered argument area. */
+ 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. */
if (flags & ECF_MAY_BE_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));