/* Convert function calls to rtl insns, for GNU C compiler.
Copyright (C) 1989, 1992, 1993, 1994, 1995, 1996, 1997, 1998
- 1999, 2000, 2001 Free Software Foundation, Inc.
+ 1999, 2000, 2001, 2002, 2003 Free Software Foundation, Inc.
This file is part of GCC.
#include "config.h"
#include "system.h"
+#include "coretypes.h"
+#include "tm.h"
#include "rtl.h"
#include "tree.h"
#include "flags.h"
#include "langhooks.h"
#include "target.h"
-#if !defined FUNCTION_OK_FOR_SIBCALL
-#define FUNCTION_OK_FOR_SIBCALL(DECL) 1
-#endif
-
/* Decide whether a function's arguments should be processed
from first to last or from last to first.
#ifdef PUSH_ROUNDING
+#ifndef PUSH_ARGS_REVERSED
#if defined (STACK_GROWS_DOWNWARD) != defined (ARGS_GROW_DOWNWARD)
#define PUSH_ARGS_REVERSED PUSH_ARGS
#endif
+#endif
#endif
static int calls_function PARAMS ((tree, int));
static int calls_function_1 PARAMS ((tree, int));
-/* Nonzero if this is a call to a `const' function. */
-#define ECF_CONST 1
-/* Nonzero if this is a call to a `volatile' function. */
-#define ECF_NORETURN 2
-/* Nonzero if this is a call to malloc or a related function. */
-#define ECF_MALLOC 4
-/* Nonzero if it is plausible that this is a call to alloca. */
-#define ECF_MAY_BE_ALLOCA 8
-/* Nonzero if this is a call to a function that won't throw an exception. */
-#define ECF_NOTHROW 16
-/* Nonzero if this is a call to setjmp or a related function. */
-#define ECF_RETURNS_TWICE 32
-/* Nonzero if this is a call to `longjmp'. */
-#define ECF_LONGJMP 64
-/* Nonzero if this is a syscall that makes a new process in the image of
- the current one. */
-#define ECF_FORK_OR_EXEC 128
-#define ECF_SIBCALL 256
-/* Nonzero if this is a call to "pure" function (like const function,
- but may read memory. */
-#define ECF_PURE 512
-/* Nonzero if this is a call to a function that returns with the stack
- pointer depressed. */
-#define ECF_SP_DEPRESSED 1024
-/* Nonzero if this call is known to always return. */
-#define ECF_ALWAYS_RETURN 2048
-/* Create libcall block around the call. */
-#define ECF_LIBCALL_BLOCK 4096
-
static void emit_call_1 PARAMS ((rtx, tree, tree, HOST_WIDE_INT,
HOST_WIDE_INT, HOST_WIDE_INT, rtx,
rtx, int, rtx, int,
rtx, int));
static rtx rtx_for_function_call PARAMS ((tree, tree));
static void load_register_parameters PARAMS ((struct arg_data *,
- int, rtx *, int));
+ int, rtx *, int,
+ int, int *));
static rtx emit_library_call_value_1 PARAMS ((int, rtx, rtx,
enum libcall_type,
enum machine_mode,
int, va_list));
static int special_function_p PARAMS ((tree, int));
-static int flags_from_decl_or_type PARAMS ((tree));
static rtx try_to_integrate PARAMS ((tree, tree, rtx,
int, tree, rtx));
static int check_sibcall_argument_overlap_1 PARAMS ((rtx));
-static int check_sibcall_argument_overlap PARAMS ((rtx, struct arg_data *));
+static int check_sibcall_argument_overlap PARAMS ((rtx, struct arg_data *,
+ int));
static int combine_pending_stack_adjustment_and_call
PARAMS ((int, struct args_size *, int));
+static tree fix_unsafe_tree PARAMS ((tree));
#ifdef REG_PARM_STACK_SPACE
static rtx save_fixed_argument_area PARAMS ((int, rtx, int *, int *));
return special_function_p (fndecl, 0) & ECF_RETURNS_TWICE;
}
+/* Return true when exp contains alloca call. */
+bool
+alloca_call_p (exp)
+ 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))
+ return true;
+ return false;
+}
+
/* Detect flags (function attributes) from the function decl or type node. */
-static int
+int
flags_from_decl_or_type (exp)
tree exp;
{
int *low_to_save;
int *high_to_save;
{
- int i;
- rtx save_area = NULL_RTX;
+ int low;
+ int high;
- /* Compute the boundary of the that needs to be saved, if any. */
+ /* Compute the boundary of the area that needs to be saved, if any. */
+ high = reg_parm_stack_space;
#ifdef ARGS_GROW_DOWNWARD
- for (i = 0; i < reg_parm_stack_space + 1; i++)
-#else
- for (i = 0; i < reg_parm_stack_space; i++)
+ high += 1;
#endif
- {
- if (i >= highest_outgoing_arg_in_use
- || stack_usage_map[i] == 0)
- continue;
+ if (high > highest_outgoing_arg_in_use)
+ high = highest_outgoing_arg_in_use;
+
+ for (low = 0; low < high; low++)
+ if (stack_usage_map[low] != 0)
+ {
+ int num_to_save;
+ enum machine_mode save_mode;
+ int delta;
+ rtx stack_area;
+ rtx save_area;
- if (*low_to_save == -1)
- *low_to_save = i;
+ while (stack_usage_map[--high] == 0)
+ ;
- *high_to_save = i;
- }
+ *low_to_save = low;
+ *high_to_save = high;
- if (*low_to_save >= 0)
- {
- int num_to_save = *high_to_save - *low_to_save + 1;
- enum machine_mode save_mode
- = mode_for_size (num_to_save * BITS_PER_UNIT, MODE_INT, 1);
- rtx stack_area;
+ num_to_save = high - low + 1;
+ save_mode = mode_for_size (num_to_save * BITS_PER_UNIT, MODE_INT, 1);
- /* If we don't have the required alignment, must do this in BLKmode. */
- if ((*low_to_save & (MIN (GET_MODE_SIZE (save_mode),
- BIGGEST_ALIGNMENT / UNITS_PER_WORD) - 1)))
- save_mode = BLKmode;
+ /* If we don't have the required alignment, must do this
+ in BLKmode. */
+ if ((low & (MIN (GET_MODE_SIZE (save_mode),
+ BIGGEST_ALIGNMENT / UNITS_PER_WORD) - 1)))
+ save_mode = BLKmode;
#ifdef ARGS_GROW_DOWNWARD
- stack_area
- = gen_rtx_MEM (save_mode,
- memory_address (save_mode,
- plus_constant (argblock,
- - *high_to_save)));
+ delta = -high;
#else
- stack_area = gen_rtx_MEM (save_mode,
- memory_address (save_mode,
- plus_constant (argblock,
- *low_to_save)));
+ delta = low;
#endif
+ stack_area = gen_rtx_MEM (save_mode,
+ memory_address (save_mode,
+ plus_constant (argblock,
+ delta)));
- set_mem_align (stack_area, PARM_BOUNDARY);
- if (save_mode == BLKmode)
- {
- save_area = assign_stack_temp (BLKmode, num_to_save, 0);
- emit_block_move (validize_mem (save_area), stack_area,
- GEN_INT (num_to_save), BLOCK_OP_CALL_PARM);
- }
- else
- {
- save_area = gen_reg_rtx (save_mode);
- emit_move_insn (save_area, stack_area);
- }
- }
+ set_mem_align (stack_area, PARM_BOUNDARY);
+ if (save_mode == BLKmode)
+ {
+ save_area = assign_stack_temp (BLKmode, num_to_save, 0);
+ emit_block_move (validize_mem (save_area), stack_area,
+ GEN_INT (num_to_save), BLOCK_OP_CALL_PARM);
+ }
+ else
+ {
+ save_area = gen_reg_rtx (save_mode);
+ emit_move_insn (save_area, stack_area);
+ }
+
+ return save_area;
+ }
- return save_area;
+ return NULL_RTX;
}
static void
int low_to_save;
{
enum machine_mode save_mode = GET_MODE (save_area);
+ int delta;
+ rtx stack_area;
+
#ifdef ARGS_GROW_DOWNWARD
- rtx stack_area
- = gen_rtx_MEM (save_mode,
- memory_address (save_mode,
- plus_constant (argblock,
- - high_to_save)));
+ delta = -high_to_save;
#else
- rtx stack_area
- = gen_rtx_MEM (save_mode,
- memory_address (save_mode,
- plus_constant (argblock,
- low_to_save)));
+ delta = low_to_save;
#endif
+ stack_area = gen_rtx_MEM (save_mode,
+ memory_address (save_mode,
+ plus_constant (argblock, delta)));
+ set_mem_align (stack_area, PARM_BOUNDARY);
if (save_mode != BLKmode)
emit_move_insn (stack_area, save_area);
this means we must skip the empty high order bytes when
calculating the bit offset. */
if (BYTES_BIG_ENDIAN
- && !FUNCTION_ARG_REG_LITTLE_ENDIAN
&& bytes < UNITS_PER_WORD)
big_endian_correction = (BITS_PER_WORD - (bytes * BITS_PER_UNIT));
addr = plus_constant (addr, arg_offset);
args[i].stack = gen_rtx_MEM (args[i].mode, addr);
+ set_mem_align (args[i].stack, PARM_BOUNDARY);
set_mem_attributes (args[i].stack,
TREE_TYPE (args[i].tree_value), 1);
addr = plus_constant (addr, arg_offset);
args[i].stack_slot = gen_rtx_MEM (args[i].mode, addr);
+ set_mem_align (args[i].stack_slot, PARM_BOUNDARY);
set_mem_attributes (args[i].stack_slot,
TREE_TYPE (args[i].tree_value), 1);
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. */
+ ADDR is the operand 0 of CALL_EXPR for this call. */
static rtx
-rtx_for_function_call (fndecl, exp)
+rtx_for_function_call (fndecl, addr)
tree fndecl;
- tree exp;
+ tree addr;
{
rtx funexp;
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);
+ funexp = expand_expr (addr, NULL_RTX, VOIDmode, 0);
pop_temp_slots (); /* FUNEXP can't be BLKmode. */
emit_queue ();
}
expressions were already evaluated.
Mark all register-parms as living through the call, putting these USE
- insns in the CALL_INSN_FUNCTION_USAGE field. */
+ insns in the CALL_INSN_FUNCTION_USAGE field.
+
+ When IS_SIBCALL, perform the check_sibcall_overlap_argument_overlap
+ checking, setting *SIBCALL_FAILURE if appropriate. */
static void
-load_register_parameters (args, num_actuals, call_fusage, flags)
+load_register_parameters (args, num_actuals, call_fusage, flags,
+ is_sibcall, sibcall_failure)
struct arg_data *args;
int num_actuals;
rtx *call_fusage;
int flags;
+ int is_sibcall;
+ int *sibcall_failure;
{
int i, j;
if (reg)
{
+ 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
validize_mem (args[i].value), nregs,
args[i].mode);
+ /* When a parameter is a block, and perhaps in other cases, it is
+ possible that it did a load from an argument slot that was
+ already clobbered. */
+ if (is_sibcall
+ && check_sibcall_argument_overlap (before_arg, &args[i], 0))
+ *sibcall_failure = 1;
+
/* Handle calls that pass values in multiple non-contiguous
locations. The Irix 6 ABI has examples of this. */
if (GET_CODE (reg) == PARALLEL)
the stack before executing the inlined function if it
makes any calls. */
- for (i = reg_parm_stack_space - 1; i >= 0; i--)
- if (i < highest_outgoing_arg_in_use && stack_usage_map[i] != 0)
- break;
+ i = reg_parm_stack_space;
+ if (i > highest_outgoing_arg_in_use)
+ i = highest_outgoing_arg_in_use;
+ while (--i >= 0 && stack_usage_map[i] == 0)
+ ;
if (stack_arg_under_construction || i >= 0)
{
/* Scan sequence after INSN if it does not dereference any argument slots
we already clobbered by tail call arguments (as noted in stored_args_map
- bitmap). Add stack slots for ARG to stored_args_map bitmap afterwards.
- Return nonzero if sequence after INSN dereferences such argument slots,
- zero otherwise. */
+ bitmap). If MARK_STORED_ARGS_MAP, add stack slots for ARG to
+ stored_args_map bitmap afterwards (when ARG is a register MARK_STORED_ARGS_MAP
+ should be 0). Return nonzero if sequence after INSN dereferences such argument
+ slots, zero otherwise. */
static int
-check_sibcall_argument_overlap (insn, arg)
+check_sibcall_argument_overlap (insn, arg, mark_stored_args_map)
rtx insn;
struct arg_data *arg;
+ int mark_stored_args_map;
{
int low, high;
&& check_sibcall_argument_overlap_1 (PATTERN (insn)))
break;
+ if (mark_stored_args_map)
+ {
#ifdef ARGS_GROW_DOWNWARD
- low = -arg->slot_offset.constant - arg->size.constant;
+ low = -arg->slot_offset.constant - arg->size.constant;
#else
- low = arg->slot_offset.constant;
+ low = arg->slot_offset.constant;
#endif
- for (high = low + arg->size.constant; low < high; low++)
- SET_BIT (stored_args_map, low);
+ for (high = low + arg->size.constant; low < high; low++)
+ SET_BIT (stored_args_map, low);
+ }
return insn != NULL_RTX;
}
+static tree
+fix_unsafe_tree (t)
+ tree t;
+{
+ switch (unsafe_for_reeval (t))
+ {
+ case 0: /* Safe. */
+ break;
+
+ case 1: /* Mildly unsafe. */
+ t = unsave_expr (t);
+ break;
+
+ case 2: /* Wildly unsafe. */
+ {
+ tree var = build_decl (VAR_DECL, NULL_TREE,
+ TREE_TYPE (t));
+ SET_DECL_RTL (var,
+ expand_expr (t, NULL_RTX, VOIDmode, EXPAND_NORMAL));
+ t = var;
+ }
+ break;
+
+ default:
+ abort ();
+ }
+ return t;
+}
+
/* 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.
int is_integrable = 0;
#ifdef REG_PARM_STACK_SPACE
/* Define the boundary of the register parm stack space that needs to be
- save, if any. */
- int low_to_save = -1, high_to_save;
+ saved, if any. */
+ int low_to_save, high_to_save;
rtx save_area = 0; /* Place that it is saved */
#endif
int old_stack_allocated;
rtx call_fusage;
tree p = TREE_OPERAND (exp, 0);
+ tree addr = TREE_OPERAND (exp, 0);
int i;
/* The alignment of the stack, in bits. */
HOST_WIDE_INT preferred_stack_boundary;
{
struct_value_size = int_size_in_bytes (TREE_TYPE (exp));
- if (target && GET_CODE (target) == MEM)
+ 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 && GET_CODE (target) == MEM)
structure_value_addr = XEXP (target, 0);
else
{
preferred_stack_boundary = PREFERRED_STACK_BOUNDARY;
/* Operand 0 is a pointer-to-function; get the type of the function. */
- funtype = TREE_TYPE (TREE_OPERAND (exp, 0));
+ funtype = TREE_TYPE (addr);
if (! POINTER_TYPE_P (funtype))
abort ();
funtype = TREE_TYPE (funtype);
calling convention than normal calls. The last argument in
INIT_CUMULATIVE_ARGS tells the backend if this is an indirect call
or not. */
- INIT_CUMULATIVE_ARGS (args_so_far, funtype, NULL_RTX, (fndecl == 0));
+ INIT_CUMULATIVE_ARGS (args_so_far, funtype, NULL_RTX, fndecl);
/* Make a vector to hold all the information about each arg. */
args = (struct arg_data *) alloca (num_actuals * sizeof (struct arg_data));
/* Tail recursion fails, when we are not dealing with recursive calls. */
if (!try_tail_recursion
- || TREE_CODE (TREE_OPERAND (exp, 0)) != ADDR_EXPR
- || TREE_OPERAND (TREE_OPERAND (exp, 0), 0) != current_function_decl)
+ || TREE_CODE (addr) != ADDR_EXPR
+ || TREE_OPERAND (addr, 0) != current_function_decl)
try_tail_recursion = 0;
/* Rest of purposes for tail call optimizations to fail. */
It does not seem worth the effort since few optimizable
sibling calls will return a structure. */
|| structure_value_addr != NULL_RTX
- /* If the register holding the address is a callee saved
- register, then we lose. We have no way to prevent that,
- so we only allow calls to named functions. */
- /* ??? This could be done by having the insn constraints
- use a register class that is all call-clobbered. Any
- reload insns generated to fix things up would appear
- before the sibcall_epilogue. */
- || fndecl == NULL_TREE
+ /* Check whether the target is able to optimize the call
+ into a sibcall. */
+ || !(*targetm.function_ok_for_sibcall) (fndecl, exp)
+ /* Functions that do not return exactly once may not be sibcall
+ optimized. */
|| (flags & (ECF_RETURNS_TWICE | ECF_LONGJMP | ECF_NORETURN))
- || !FUNCTION_OK_FOR_SIBCALL (fndecl)
+ || TYPE_VOLATILE (TREE_TYPE (TREE_TYPE (addr)))
/* 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
for (; i != end; i += inc)
{
- switch (unsafe_for_reeval (args[i].tree_value))
- {
- case 0: /* Safe. */
- break;
-
- case 1: /* Mildly unsafe. */
- args[i].tree_value = unsave_expr (args[i].tree_value);
- break;
-
- case 2: /* Wildly unsafe. */
- {
- tree var = build_decl (VAR_DECL, NULL_TREE,
- TREE_TYPE (args[i].tree_value));
- SET_DECL_RTL (var,
- expand_expr (args[i].tree_value, NULL_RTX,
- VOIDmode, EXPAND_NORMAL));
- args[i].tree_value = var;
- }
- break;
-
- default:
- abort ();
- }
+ args[i].tree_value = fix_unsafe_tree (args[i].tree_value);
/* We need to build actparms for optimize_tail_recursion. We can
safely trash away TREE_PURPOSE, since it is unused by this
function. */
if (try_tail_recursion)
actparms = tree_cons (NULL_TREE, args[i].tree_value, actparms);
}
+ /* Do the same for the function address if it is an expression. */
+ if (!fndecl)
+ addr = fix_unsafe_tree (addr);
/* Expanding one of those dangerous arguments could have added
cleanups, but otherwise give it a whirl. */
if (any_pending_cleanups (1))
is subject to race conditions, just as with multithreaded
programs. */
- emit_library_call (gen_rtx_SYMBOL_REF (Pmode, "__bb_fork_func"),
+ emit_library_call (gen_rtx_SYMBOL_REF (Pmode, "__gcov_flush"),
LCT_ALWAYS_RETURN,
VOIDmode, 0);
}
/* We want to make two insn chains; one for a sibling call, the other
for a normal call. We will select one of the two chains after
initial RTL generation is complete. */
- for (pass = 0; pass < 2; pass++)
+ for (pass = try_tail_call ? 0 : 1; pass < 2; pass++)
{
int sibcall_failure = 0;
/* We want to emit any pending stack adjustments before the tail
if (pass == 0)
{
- if (! try_tail_call)
- continue;
-
/* Emit any queued insns now; otherwise they would end up in
only one of the alternates. */
emit_queue ();
be deferred during the evaluation of the arguments. */
NO_DEFER_POP;
- funexp = rtx_for_function_call (fndecl, exp);
+ funexp = rtx_for_function_call (fndecl, addr);
/* Figure out the register where the value, if any, will come back. */
valreg = 0;
reg_parm_stack_space)
|| (pass == 0
&& check_sibcall_argument_overlap (before_arg,
- &args[i])))
+ &args[i], 1)))
sibcall_failure = 1;
}
reg_parm_stack_space)
|| (pass == 0
&& check_sibcall_argument_overlap (before_arg,
- &args[i])))
+ &args[i], 1)))
sibcall_failure = 1;
}
funexp = prepare_call_address (funexp, fndecl, &call_fusage,
reg_parm_seen, pass == 0);
- load_register_parameters (args, num_actuals, &call_fusage, flags);
+ load_register_parameters (args, num_actuals, &call_fusage, flags,
+ pass == 0, &sibcall_failure);
/* Perform postincrements before actually calling the function. */
emit_queue ();
{
#ifdef REG_PARM_STACK_SPACE
if (save_area)
- {
- restore_fixed_argument_area (save_area, argblock,
- high_to_save, low_to_save);
- }
+ restore_fixed_argument_area (save_area, argblock,
+ high_to_save, low_to_save);
#endif
/* If we saved any argument areas, restore them. */
#ifdef REG_PARM_STACK_SPACE
/* Define the boundary of the register parm stack space that needs to be
save, if any. */
- int low_to_save = -1, high_to_save = 0;
+ int low_to_save, high_to_save;
rtx save_area = 0; /* Place that it is saved. */
#endif
{
tree type = (*lang_hooks.types.type_for_mode) (mode, 0);
- slot = gen_rtx_MEM (mode,
- expand_expr (build1 (ADDR_EXPR,
- build_pointer_type
- (type),
- make_tree (type, val)),
- NULL_RTX, VOIDmode, 0));
+ slot
+ = gen_rtx_MEM (mode,
+ expand_expr (build1 (ADDR_EXPR,
+ build_pointer_type (type),
+ make_tree (type, val)),
+ NULL_RTX, VOIDmode, 0));
}
call_fusage = gen_rtx_EXPR_LIST (VOIDmode,
{
/* The argument list is the property of the called routine and it
may clobber it. If the fixed area has been used for previous
- parameters, we must save and restore it.
-
- Here we compute the boundary of the that needs to be saved, if any. */
-
-#ifdef ARGS_GROW_DOWNWARD
- for (count = 0; count < reg_parm_stack_space + 1; count++)
-#else
- for (count = 0; count < reg_parm_stack_space; count++)
-#endif
- {
- if (count >= highest_outgoing_arg_in_use
- || stack_usage_map[count] == 0)
- continue;
-
- if (low_to_save == -1)
- low_to_save = count;
-
- high_to_save = count;
- }
-
- if (low_to_save >= 0)
- {
- int num_to_save = high_to_save - low_to_save + 1;
- enum machine_mode save_mode
- = mode_for_size (num_to_save * BITS_PER_UNIT, MODE_INT, 1);
- rtx stack_area;
-
- /* If we don't have the required alignment, must do this in BLKmode. */
- if ((low_to_save & (MIN (GET_MODE_SIZE (save_mode),
- BIGGEST_ALIGNMENT / UNITS_PER_WORD) - 1)))
- save_mode = BLKmode;
-
-#ifdef ARGS_GROW_DOWNWARD
- stack_area = gen_rtx_MEM (save_mode,
- memory_address (save_mode,
- plus_constant (argblock,
- -high_to_save)));
-#else
- stack_area = gen_rtx_MEM (save_mode,
- memory_address (save_mode,
- plus_constant (argblock,
- low_to_save)));
-#endif
- if (save_mode == BLKmode)
- {
- save_area = assign_stack_temp (BLKmode, num_to_save, 0);
- set_mem_align (save_area, PARM_BOUNDARY);
- emit_block_move (save_area, stack_area, GEN_INT (num_to_save),
- BLOCK_OP_CALL_PARM);
- }
- else
- {
- save_area = gen_reg_rtx (save_mode);
- emit_move_insn (save_area, stack_area);
- }
- }
+ parameters, we must save and restore it. */
+ save_area = save_fixed_argument_area (reg_parm_stack_space, argblock,
+ &low_to_save, &high_to_save);
}
#endif
upper_bound = lower_bound + argvec[argnum].size.constant;
#endif
- for (i = lower_bound; i < upper_bound; i++)
- if (stack_usage_map[i]
- /* Don't store things in the fixed argument area at this
- point; it has already been saved. */
- && i > reg_parm_stack_space)
- break;
+ i = lower_bound;
+ /* Don't worry about things in the fixed argument area;
+ it has already been saved. */
+ if (i < reg_parm_stack_space)
+ i = reg_parm_stack_space;
+ while (i < upper_bound && stack_usage_map[i] == 0)
+ i++;
- if (i != upper_bound)
+ if (i < upper_bound)
{
/* We need to make a save area. See what mode we can make
it. */
{
rtx insns;
- if (valreg == 0 || GET_CODE (valreg) == PARALLEL)
+ if (valreg == 0)
{
insns = get_insns ();
end_sequence ();
else
{
rtx note = 0;
- rtx temp = gen_reg_rtx (GET_MODE (valreg));
+ rtx temp;
int i;
+ if (GET_CODE (valreg) == PARALLEL)
+ {
+ temp = gen_reg_rtx (outmode);
+ emit_group_store (temp, valreg, outmode);
+ valreg = temp;
+ }
+
+ temp = gen_reg_rtx (GET_MODE (valreg));
+
/* Construct an "equal form" for the value which mentions all the
arguments in order as well as the function name. */
for (i = 0; i < nargs; i++)
if (value != mem_value)
emit_move_insn (value, mem_value);
}
+ else if (GET_CODE (valreg) == PARALLEL)
+ {
+ if (value == 0)
+ value = gen_reg_rtx (outmode);
+ emit_group_store (value, valreg, outmode);
+ }
else if (value != 0)
emit_move_insn (value, valreg);
else
{
#ifdef REG_PARM_STACK_SPACE
if (save_area)
- {
- enum machine_mode save_mode = GET_MODE (save_area);
-#ifdef ARGS_GROW_DOWNWARD
- rtx stack_area
- = gen_rtx_MEM (save_mode,
- memory_address (save_mode,
- plus_constant (argblock,
- - high_to_save)));
-#else
- rtx stack_area
- = gen_rtx_MEM (save_mode,
- memory_address (save_mode,
- plus_constant (argblock, low_to_save)));
-#endif
-
- set_mem_align (stack_area, PARM_BOUNDARY);
- if (save_mode != BLKmode)
- emit_move_insn (stack_area, save_area);
- else
- emit_block_move (stack_area, save_area,
- GEN_INT (high_to_save - low_to_save + 1),
- BLOCK_OP_CALL_PARM);
- }
+ restore_fixed_argument_area (save_area, argblock,
+ high_to_save, low_to_save);
#endif
/* If we saved any argument areas, restore them. */
upper_bound = lower_bound + arg->size.constant;
#endif
- for (i = lower_bound; i < upper_bound; i++)
- if (stack_usage_map[i]
- /* Don't store things in the fixed argument area at this point;
- it has already been saved. */
- && i > reg_parm_stack_space)
- break;
+ i = lower_bound;
+ /* Don't worry about things in the fixed argument area;
+ it has already been saved. */
+ if (i < reg_parm_stack_space)
+ i = reg_parm_stack_space;
+ while (i < upper_bound && stack_usage_map[i] == 0)
+ i++;
- if (i != upper_bound)
+ if (i < upper_bound)
{
/* We need to make a save area. See what mode we can make it. */
enum machine_mode save_mode
}
}
}
- /* Now that we have saved any slots that will be overwritten by this
- store, mark all slots this store will use. We must do this before
- we actually expand the argument since the expansion itself may
- trigger library calls which might need to use the same stack slot. */
- if (argblock && ! variable_size && arg->stack)
- for (i = lower_bound; i < upper_bound; i++)
- stack_usage_map[i] = 1;
}
/* If this isn't going to be placed on both the stack and in registers,
(partial
|| TYPE_MODE (TREE_TYPE (pval)) != arg->mode)
? NULL_RTX : arg->stack,
- VOIDmode, 0);
+ VOIDmode, EXPAND_STACK_PARM);
/* If we are promoting object (or for any other reason) the mode
doesn't agree, convert the mode. */
}
}
- /* Special handling is required if part of the parameter lies in the
- register parameter area. The argument may be copied into the stack
- slot using memcpy(), but the original contents of the register
- parameter area will be restored after the memcpy() call.
-
- To ensure that the part that lies in the register parameter area
- is copied correctly, we emit a separate push for that part. This
- push should be small enough to avoid a call to memcpy(). */
-#ifndef STACK_PARMS_IN_REG_PARM_AREA
- if (arg->reg && arg->pass_on_stack)
-#else
- if (1)
-#endif
- {
- if (arg->offset.constant < reg_parm_stack_space && arg->offset.var)
- error ("variable offset is passed partially in stack and in reg");
- else if (arg->offset.constant < reg_parm_stack_space && arg->size.var)
- error ("variable size is passed partially in stack and in reg");
- else if (arg->offset.constant < reg_parm_stack_space
- && ((arg->offset.constant + arg->size.constant)
- > reg_parm_stack_space))
- {
- rtx size_rtx1 = GEN_INT (reg_parm_stack_space - arg->offset.constant);
- emit_push_insn (arg->value, arg->mode, TREE_TYPE (pval), size_rtx1,
- parm_align, partial, reg, excess, argblock,
- ARGS_SIZE_RTX (arg->offset), reg_parm_stack_space,
- ARGS_SIZE_RTX (arg->alignment_pad));
- }
- }
-
-
emit_push_insn (arg->value, arg->mode, TREE_TYPE (pval), size_rtx,
parm_align, partial, reg, excess, argblock,
ARGS_SIZE_RTX (arg->offset), reg_parm_stack_space,
arg->value = arg->stack_slot;
}
+ /* Mark all slots this store used. */
+ if (ACCUMULATE_OUTGOING_ARGS && !(flags & ECF_SIBCALL)
+ && argblock && ! variable_size && arg->stack)
+ for (i = lower_bound; i < upper_bound; i++)
+ stack_usage_map[i] = 1;
+
/* Once we have pushed something, pops can't safely
be deferred during the rest of the arguments. */
NO_DEFER_POP;
return sibcall_failure;
}
+
+/* Nonzero if we do not know how to pass TYPE solely in registers.
+ We cannot do so in the following cases:
+
+ - if the type has variable size
+ - if the type is marked as addressable (it is required to be constructed
+ into the stack)
+ - if the padding and mode of the type is such that a copy into a register
+ would put it into the wrong part of the register.
+
+ Which padding can't be supported depends on the byte endianness.
+
+ A value in a register is implicitly padded at the most significant end.
+ On a big-endian machine, that is the lower end in memory.
+ So a value padded in memory at the upper end can't go in a register.
+ For a little-endian machine, the reverse is true. */
+
+bool
+default_must_pass_in_stack (mode, type)
+ enum machine_mode mode;
+ tree type;
+{
+ if (!type)
+ return false;
+
+ /* If the type has variable size... */
+ if (TREE_CODE (TYPE_SIZE (type)) != INTEGER_CST)
+ return true;
+
+ /* If the type is marked as addressable (it is required
+ to be constructed into the stack)... */
+ if (TREE_ADDRESSABLE (type))
+ return true;
+
+ /* If the padding and mode of the type is such that a copy into
+ a register would put it into the wrong part of the register. */
+ if (mode == BLKmode
+ && int_size_in_bytes (type) % (PARM_BOUNDARY / BITS_PER_UNIT)
+ && (FUNCTION_ARG_PADDING (mode, type)
+ == (BYTES_BIG_ENDIAN ? upward : downward)))
+ return true;
+
+ return false;
+}