/* 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 "timevar.h"
#include "sbitmap.h"
#include "langhooks.h"
-
-#if !defined FUNCTION_OK_FOR_SIBCALL
-#define FUNCTION_OK_FOR_SIBCALL(DECL) 1
-#endif
+#include "target.h"
+#include "cgraph.h"
+#include "except.h"
/* 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
/* Number of registers to use. 0 means put the whole arg in registers.
Also 0 if not passed in registers. */
int partial;
- /* Non-zero if argument must be passed on stack.
+ /* Nonzero if argument must be passed on stack.
Note that some arguments may be passed on the stack
even though pass_on_stack is zero, just because FUNCTION_ARG says so.
pass_on_stack identifies arguments that *cannot* go in registers. */
struct args_size alignment_pad;
};
-/* A vector of one char per byte of stack space. A byte if non-zero if
+/* A vector of one char per byte of stack space. A byte if nonzero if
the corresponding stack location has been used.
This vector is used to prevent a function call within an argument from
clobbering any stack already set up. */
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 *));
if (ecf_flags & ECF_NOTHROW)
REG_NOTES (call_insn) = gen_rtx_EXPR_LIST (REG_EH_REGION, const0_rtx,
REG_NOTES (call_insn));
+ else
+ note_eh_region_may_contain_throw ();
if (ecf_flags & ECF_NORETURN)
REG_NOTES (call_insn) = gen_rtx_EXPR_LIST (REG_NORETURN, const0_rtx,
Similarly set LONGJMP for if the function is in the longjmp family.
- Set MALLOC for any of the standard memory allocation functions which
- allocate from the heap.
-
Set MAY_BE_ALLOCA for any memory allocation function that might allocate
space from the stack such as alloca. */
|| ((tname[5] == 'p' || tname[5] == 'e')
&& tname[6] == '\0'))))
flags |= ECF_FORK_OR_EXEC;
-
- /* Do not add any more malloc-like functions to this list,
- instead mark them as malloc functions using the malloc attribute.
- Note, realloc is not suitable for attribute malloc since
- it may return the same address across multiple calls.
- C++ operator new is not suitable because it is not required
- to return a unique pointer; indeed, the standard placement new
- just returns its argument. */
- else if (TYPE_MODE (TREE_TYPE (TREE_TYPE (fndecl))) == Pmode
- && (! strcmp (tname, "malloc")
- || ! strcmp (tname, "calloc")
- || ! strcmp (tname, "strdup")))
- flags |= ECF_MALLOC;
}
return flags;
}
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 flags = 0;
tree type = exp;
- /* ??? We can't set IS_MALLOC for function types? */
+
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_P (exp) && DECL_IS_MALLOC (exp))
+ if (DECL_IS_MALLOC (exp))
flags |= ECF_MALLOC;
/* The function exp may have the `pure' attribute. */
- if (DECL_P (exp) && DECL_IS_PURE (exp))
+ if (DECL_IS_PURE (exp))
flags |= ECF_PURE | ECF_LIBCALL_BLOCK;
if (TREE_NOTHROW (exp))
emit_queue ();
}
+ /* If the value is a non-legitimate constant, force it into a
+ pseudo now. TLS symbols sometimes need a call to resolve. */
+ if (CONSTANT_P (args[i].value)
+ && !LEGITIMATE_CONSTANT_P (args[i].value))
+ args[i].value = force_reg (args[i].mode, args[i].value);
+
/* If we are to promote the function arg to a wider mode,
do it now. */
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;
- if (*low_to_save == -1)
- *low_to_save = i;
+ 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;
- *high_to_save = i;
- }
+ while (stack_usage_map[--high] == 0)
+ ;
- 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;
+ *low_to_save = low;
+ *high_to_save = high;
+
+ 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);
- /* Cannot use emit_block_move here because it can be done by a
- library call which in turn gets into this place again and deadly
- infinite recursion happens. */
- move_by_pieces (validize_mem (save_area), stack_area, num_to_save,
- PARM_BOUNDARY);
- }
- 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);
else
- /* Cannot use emit_block_move here because it can be done by a library
- call which in turn gets into this place again and deadly infinite
- recursion happens. */
- move_by_pieces (stack_area, validize_mem (save_area),
- high_to_save - low_to_save + 1, PARM_BOUNDARY);
+ emit_block_move (stack_area, validize_mem (save_area),
+ GEN_INT (high_to_save - low_to_save + 1),
+ BLOCK_OP_CALL_PARM);
}
#endif /* REG_PARM_STACK_SPACE */
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 X expression if it does not dereference any argument slots
we already clobbered by tail call arguments (as noted in stored_args_map
bitmap).
- Return non-zero if X expression dereferences such argument slots,
+ Return nonzero if X expression dereferences such argument slots,
zero otherwise. */
static int
/* 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 non-zero 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 initial_highest_arg_in_use = highest_outgoing_arg_in_use;
char *initial_stack_usage_map = stack_usage_map;
- int old_stack_arg_under_construction = 0;
+ int old_stack_allocated;
+
+ /* State variables to track stack modifications. */
rtx old_stack_level = 0;
+ int old_stack_arg_under_construction = 0;
int old_pending_adj = 0;
int old_inhibit_defer_pop = inhibit_defer_pop;
- int old_stack_allocated;
+
+ /* Some stack pointer alterations we make are performed via
+ allocate_dynamic_stack_space. This modifies the stack_pointer_delta,
+ which we then also need to save/restore along the way. */
+ int old_stack_pointer_delta = 0;
+
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
{
/* Figure out the amount to which the stack should be aligned. */
preferred_stack_boundary = PREFERRED_STACK_BOUNDARY;
+ if (fndecl)
+ {
+ struct cgraph_rtl_info *i = cgraph_rtl_info (fndecl);
+ if (i && i->preferred_incoming_stack_boundary)
+ preferred_stack_boundary = i->preferred_incoming_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
- || (flags & (ECF_RETURNS_TWICE | ECF_LONGJMP))
- || TREE_THIS_VOLATILE (fndecl)
- || !FUNCTION_OK_FOR_SIBCALL (fndecl)
+ /* 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))
+ || 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. */
+ || (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
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"),
- LCT_ALWAYS_RETURN,
- VOIDmode, 0);
+ emit_library_call (gcov_flush_libfunc, LCT_ALWAYS_RETURN, VOIDmode, 0);
}
/* Ensure current function's preferred stack boundary is at least
if (cfun->preferred_stack_boundary < preferred_stack_boundary
&& fndecl != current_function_decl)
cfun->preferred_stack_boundary = preferred_stack_boundary;
+ if (fndecl == current_function_decl)
+ cfun->recursive_call_emit = true;
preferred_unit_stack_boundary = preferred_stack_boundary / BITS_PER_UNIT;
/* 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 ();
if (old_stack_level == 0)
{
emit_stack_save (SAVE_BLOCK, &old_stack_level, NULL_RTX);
+ old_stack_pointer_delta = stack_pointer_delta;
old_pending_adj = pending_stack_adjust;
pending_stack_adjust = 0;
/* stack_arg_under_construction says whether a stack arg is
VIRTUAL_OUTGOING_ARGS_RTX changes as well. But might
as well always do it. */
argblock = copy_to_reg (argblock);
+ }
+ }
+ }
- /* The save/restore code in store_one_arg handles all
- cases except one: a constructor call (including a C
- function returning a BLKmode struct) to initialize
- an argument. */
- if (stack_arg_under_construction)
- {
+ if (ACCUMULATE_OUTGOING_ARGS)
+ {
+ /* The save/restore code in store_one_arg handles all
+ cases except one: a constructor call (including a C
+ function returning a BLKmode struct) to initialize
+ an argument. */
+ if (stack_arg_under_construction)
+ {
#ifndef OUTGOING_REG_PARM_STACK_SPACE
- rtx push_size = GEN_INT (reg_parm_stack_space
- + adjusted_args_size.constant);
+ rtx push_size = GEN_INT (reg_parm_stack_space
+ + adjusted_args_size.constant);
#else
- rtx push_size = GEN_INT (adjusted_args_size.constant);
+ rtx push_size = GEN_INT (adjusted_args_size.constant);
#endif
- if (old_stack_level == 0)
- {
- emit_stack_save (SAVE_BLOCK, &old_stack_level,
- NULL_RTX);
- old_pending_adj = pending_stack_adjust;
- pending_stack_adjust = 0;
- /* stack_arg_under_construction says whether a stack
- arg is being constructed at the old stack level.
- Pushing the stack gets a clean outgoing argument
- block. */
- old_stack_arg_under_construction
- = stack_arg_under_construction;
- stack_arg_under_construction = 0;
- /* Make a new map for the new argument list. */
- stack_usage_map = (char *)
- alloca (highest_outgoing_arg_in_use);
- memset (stack_usage_map, 0, highest_outgoing_arg_in_use);
- highest_outgoing_arg_in_use = 0;
- }
- allocate_dynamic_stack_space (push_size, NULL_RTX,
- BITS_PER_UNIT);
- }
- /* If argument evaluation might modify the stack pointer,
- copy the address of the argument list to a register. */
- for (i = 0; i < num_actuals; i++)
- if (args[i].pass_on_stack)
- {
- argblock = copy_addr_to_reg (argblock);
- break;
- }
+ if (old_stack_level == 0)
+ {
+ emit_stack_save (SAVE_BLOCK, &old_stack_level,
+ NULL_RTX);
+ old_stack_pointer_delta = stack_pointer_delta;
+ old_pending_adj = pending_stack_adjust;
+ pending_stack_adjust = 0;
+ /* stack_arg_under_construction says whether a stack
+ arg is being constructed at the old stack level.
+ Pushing the stack gets a clean outgoing argument
+ block. */
+ old_stack_arg_under_construction
+ = stack_arg_under_construction;
+ stack_arg_under_construction = 0;
+ /* Make a new map for the new argument list. */
+ stack_usage_map = (char *)
+ alloca (highest_outgoing_arg_in_use);
+ memset (stack_usage_map, 0, highest_outgoing_arg_in_use);
+ highest_outgoing_arg_in_use = 0;
}
+ allocate_dynamic_stack_space (push_size, NULL_RTX,
+ BITS_PER_UNIT);
}
- }
+ /* If argument evaluation might modify the stack pointer,
+ copy the address of the argument list to a register. */
+ for (i = 0; i < num_actuals; i++)
+ if (args[i].pass_on_stack)
+ {
+ argblock = copy_addr_to_reg (argblock);
+ break;
+ }
+ }
+
compute_argument_addresses (args, argblock, num_actuals);
/* If we push args individually in reverse order, perform stack alignment
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 ();
next_arg_reg, valreg, old_inhibit_defer_pop, call_fusage,
flags, & args_so_far);
- /* Verify that we've deallocated all the stack we used. */
- if (pass
- && old_stack_allocated != stack_pointer_delta - pending_stack_adjust)
- abort ();
-
/* If call is cse'able, make appropriate pair of reg-notes around it.
Test valreg so we don't crash; may safely ignore `const'
if return type is void. Disable for PARALLEL return values, because
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 (GET_CODE (target) == MEM)
+ sibcall_failure = 1;
}
else if (TYPE_MODE (TREE_TYPE (exp)) == BLKmode)
{
if (old_stack_level && ! (flags & ECF_SP_DEPRESSED))
{
emit_stack_restore (SAVE_BLOCK, old_stack_level, NULL_RTX);
+ stack_pointer_delta = old_stack_pointer_delta;
pending_stack_adjust = old_pending_adj;
stack_arg_under_construction = old_stack_arg_under_construction;
highest_outgoing_arg_in_use = initial_highest_arg_in_use;
{
#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. */
if (save_mode != BLKmode)
emit_move_insn (stack_area, args[i].save_area);
else
- emit_block_move (stack_area,
- validize_mem (args[i].save_area),
- GEN_INT (args[i].size.constant));
+ emit_block_move (stack_area, args[i].save_area,
+ GEN_INT (args[i].size.constant),
+ BLOCK_OP_CALL_PARM);
}
highest_outgoing_arg_in_use = initial_highest_arg_in_use;
sbitmap_free (stored_args_map);
}
else
- normal_call_insns = insns;
+ {
+ normal_call_insns = insns;
+
+ /* Verify that we've deallocated all the stack we used. */
+ if (old_stack_allocated !=
+ stack_pointer_delta - pending_stack_adjust)
+ abort ();
+ }
/* If something prevents making this a sibling call,
zero out the sequence. */
#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
}
flags &= ~(ECF_CONST | ECF_PURE | ECF_LIBCALL_BLOCK);
+ /* If this was a CONST function, it is now PURE since
+ it now reads memory. */
+ if (flags & ECF_CONST)
+ {
+ flags &= ~ECF_CONST;
+ flags |= ECF_PURE;
+ }
+
if (GET_MODE (val) == MEM && ! must_copy)
slot = val;
else if (must_copy)
{
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 (validize_mem (save_area), stack_area,
- GEN_INT (num_to_save));
- }
- 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. */
}
}
- emit_push_insn (val, mode, NULL_TREE, NULL_RTX, 0, partial, reg, 0,
- argblock, GEN_INT (argvec[argnum].offset.constant),
+ emit_push_insn (val, mode, NULL_TREE, NULL_RTX, PARM_BOUNDARY,
+ partial, reg, 0, argblock,
+ GEN_INT (argvec[argnum].offset.constant),
reg_parm_stack_space, ARGS_SIZE_RTX (alignment_pad));
/* Now mark the segment we just used. */
{
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, hard_libcall_value (outmode));
+ emit_move_insn (value, valreg);
else
- value = hard_libcall_value (outmode);
+ value = valreg;
}
if (ACCUMULATE_OUTGOING_ARGS)
{
#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, validize_mem (save_area),
- GEN_INT (high_to_save - low_to_save + 1));
- }
+ restore_fixed_argument_area (save_area, argblock,
+ high_to_save, low_to_save);
#endif
/* If we saved any argument areas, restore them. */
FNDECL is the declaration of the function we are calling.
- Return non-zero if this arg should cause sibcall failure,
+ Return nonzero if this arg should cause sibcall failure,
zero otherwise. */
static int
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
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));
+ expr_size (arg->tree_value),
+ BLOCK_OP_CALL_PARM);
}
else
{
}
}
}
- /* 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. */
/* 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, 0,
- partial, reg, used - size, argblock,
+ emit_push_insn (arg->value, arg->mode, TREE_TYPE (pval), NULL_RTX,
+ PARM_BOUNDARY, partial, reg, used - size, argblock,
ARGS_SIZE_RTX (arg->offset), reg_parm_stack_space,
ARGS_SIZE_RTX (arg->alignment_pad));
{
/* BLKmode, at least partly to be pushed. */
+ unsigned int parm_align;
int excess;
rtx size_rtx;
emit_push_insn for BLKmode is careful to avoid it. */
excess = (arg->size.constant - int_size_in_bytes (TREE_TYPE (pval))
+ partial * UNITS_PER_WORD);
- size_rtx = expr_size (pval);
+ size_rtx = expand_expr (size_in_bytes (TREE_TYPE (pval)),
+ NULL_RTX, TYPE_MODE (sizetype), 0);
+ }
+
+ /* Some types will require stricter alignment, which will be
+ provided for elsewhere in argument layout. */
+ parm_align = MAX (PARM_BOUNDARY, TYPE_ALIGN (TREE_TYPE (pval)));
+
+ /* When an argument is padded down, the block is aligned to
+ PARM_BOUNDARY, but the actual argument isn't. */
+ if (FUNCTION_ARG_PADDING (arg->mode, TREE_TYPE (pval)) == downward)
+ {
+ if (arg->size.var)
+ parm_align = BITS_PER_UNIT;
+ else if (excess)
+ {
+ unsigned int excess_align = (excess & -excess) * BITS_PER_UNIT;
+ parm_align = MIN (parm_align, excess_align);
+ }
}
if ((flags & ECF_SIBCALL) && GET_CODE (arg->value) == MEM)
}
}
- /* 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,
- TYPE_ALIGN (TREE_TYPE (pval)), 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,
- TYPE_ALIGN (TREE_TYPE (pval)), partial, reg, excess,
- argblock, ARGS_SIZE_RTX (arg->offset),
- reg_parm_stack_space,
+ parm_align, partial, reg, excess, argblock,
+ ARGS_SIZE_RTX (arg->offset), reg_parm_stack_space,
ARGS_SIZE_RTX (arg->alignment_pad));
/* Unless this is a partially-in-register argument, the argument is now
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;
+}