static int calls_function PARAMS ((tree, int));
static int calls_function_1 PARAMS ((tree, int));
-#define ECF_IS_CONST 1
-#define ECF_NOTHROW 2
-#define ECF_SIBCALL 4
+/* 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
+
static void emit_call_1 PARAMS ((rtx, tree, tree, HOST_WIDE_INT,
HOST_WIDE_INT, HOST_WIDE_INT, rtx,
rtx, int, rtx, int));
static int finalize_must_preallocate PARAMS ((int, int,
struct arg_data *,
struct args_size *));
-static void precompute_arguments PARAMS ((int, int, int,
- struct arg_data *,
- struct args_size *));
+static void precompute_arguments PARAMS ((int, int,
+ struct arg_data *));
static int compute_argument_block_size PARAMS ((int,
struct args_size *,
int));
int, tree, tree,
CUMULATIVE_ARGS *,
int, rtx *, int *,
- int *, int *, int));
+ int *, int *));
static void compute_argument_addresses PARAMS ((struct arg_data *,
rtx, int));
static rtx rtx_for_function_call PARAMS ((tree, tree));
static rtx emit_library_call_value_1 PARAMS ((int, rtx, rtx, int,
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 combine_pending_stack_adjustment_and_call
+ PARAMS ((int, struct args_size *, int));
#ifdef REG_PARM_STACK_SPACE
static rtx save_fixed_argument_area PARAMS ((int, rtx, int *, int *));
We restore `inhibit_defer_pop' to that value.
CALL_FUSAGE is either empty or an EXPR_LIST of USE expressions that
- denote registers used by the called function.
-
- IS_CONST is true if this is a `const' call. */
+ denote registers used by the called function. */
static void
emit_call_1 (funexp, fndecl, funtype, stack_size, rounded_stack_size,
if (! call_insn)
abort ();
+ /* Mark memory as used for "pure" function call. */
+ if (ecf_flags & ECF_PURE)
+ {
+ call_fusage = gen_rtx_EXPR_LIST (VOIDmode,
+ gen_rtx_USE (VOIDmode,
+ gen_rtx_MEM (BLKmode,
+ gen_rtx_SCRATCH (VOIDmode))), call_fusage);
+ }
+
/* Put the register usage information on the CALL. If there is already
some usage information, put ours at the end. */
if (CALL_INSN_FUNCTION_USAGE (call_insn))
CALL_INSN_FUNCTION_USAGE (call_insn) = call_fusage;
/* If this is a const call, then set the insn's unchanging bit. */
- if (ecf_flags & ECF_IS_CONST)
+ if (ecf_flags & (ECF_CONST | ECF_PURE))
CONST_CALL_P (call_insn) = 1;
/* If this call can't throw, attach a REG_EH_REGION reg note to that
CALL_INSN_FUNCTION_USAGE (call_insn));
rounded_stack_size -= n_popped;
rounded_stack_size_rtx = GEN_INT (rounded_stack_size);
+ stack_pointer_delta -= n_popped;
}
if (!ACCUMULATE_OUTGOING_ARGS)
If returning from the subroutine does pop the args, indicate that the
stack pointer will be changed. */
- /* The space for the args is no longer waiting for the call; either it
- was popped by the call, or it'll be popped below. */
- arg_space_so_far -= rounded_stack_size - n_popped;
-
if (rounded_stack_size != 0)
{
if (flag_defer_pop && inhibit_defer_pop == 0
- && !(ecf_flags & ECF_IS_CONST))
+ && !(ecf_flags & (ECF_CONST | ECF_PURE)))
pending_stack_adjust += rounded_stack_size;
else
adjust_stack (rounded_stack_size_rtx);
For example, if the function might return more than one time (setjmp), then
set RETURNS_TWICE to a nonzero value.
- Similarly set IS_LONGJMP for if the function is in the longjmp family.
+ Similarly set LONGJMP for if the function is in the longjmp family.
- Set IS_MALLOC for any of the standard memory allocation functions which
+ 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. */
-void
-special_function_p (fndecl, returns_twice, is_longjmp, fork_or_exec,
- is_malloc, may_be_alloca)
+static int
+special_function_p (fndecl, flags)
tree fndecl;
- int *returns_twice;
- int *is_longjmp;
- int *fork_or_exec;
- int *is_malloc;
- int *may_be_alloca;
+ int flags;
{
- *returns_twice = 0;
- *is_longjmp = 0;
- *fork_or_exec = 0;
- *may_be_alloca = 0;
-
- /* The function decl may have the `malloc' attribute. */
- *is_malloc = fndecl && DECL_IS_MALLOC (fndecl);
-
- if (! *is_malloc
+ if (! (flags & ECF_MALLOC)
&& fndecl && DECL_NAME (fndecl)
&& IDENTIFIER_LENGTH (DECL_NAME (fndecl)) <= 17
/* Exclude functions not at the file scope, or not `extern',
/* We assume that alloca will always be called by name. It
makes no sense to pass it as a pointer-to-function to
anything that does not understand its behavior. */
- *may_be_alloca
- = (((IDENTIFIER_LENGTH (DECL_NAME (fndecl)) == 6
- && name[0] == 'a'
- && ! strcmp (name, "alloca"))
- || (IDENTIFIER_LENGTH (DECL_NAME (fndecl)) == 16
- && name[0] == '_'
- && ! strcmp (name, "__builtin_alloca"))));
+ if (((IDENTIFIER_LENGTH (DECL_NAME (fndecl)) == 6
+ && name[0] == 'a'
+ && ! strcmp (name, "alloca"))
+ || (IDENTIFIER_LENGTH (DECL_NAME (fndecl)) == 16
+ && name[0] == '_'
+ && ! strcmp (name, "__builtin_alloca"))))
+ flags |= ECF_MAY_BE_ALLOCA;
/* Disregard prefix _, __ or __x. */
if (name[0] == '_')
if (tname[0] == 's')
{
- *returns_twice
- = ((tname[1] == 'e'
- && (! strcmp (tname, "setjmp")
- || ! strcmp (tname, "setjmp_syscall")))
- || (tname[1] == 'i'
- && ! strcmp (tname, "sigsetjmp"))
- || (tname[1] == 'a'
- && ! strcmp (tname, "savectx")));
+ if ((tname[1] == 'e'
+ && (! strcmp (tname, "setjmp")
+ || ! strcmp (tname, "setjmp_syscall")))
+ || (tname[1] == 'i'
+ && ! strcmp (tname, "sigsetjmp"))
+ || (tname[1] == 'a'
+ && ! strcmp (tname, "savectx")))
+ flags |= ECF_RETURNS_TWICE;
+
if (tname[1] == 'i'
&& ! strcmp (tname, "siglongjmp"))
- *is_longjmp = 1;
+ flags |= ECF_LONGJMP;
}
else if ((tname[0] == 'q' && tname[1] == 's'
&& ! strcmp (tname, "qsetjmp"))
|| (tname[0] == 'v' && tname[1] == 'f'
&& ! strcmp (tname, "vfork")))
- *returns_twice = 1;
+ flags |= ECF_RETURNS_TWICE;
else if (tname[0] == 'l' && tname[1] == 'o'
&& ! strcmp (tname, "longjmp"))
- *is_longjmp = 1;
+ flags |= ECF_LONGJMP;
else if ((tname[0] == 'f' && tname[1] == 'o'
&& ! strcmp (tname, "fork"))
&& (tname[5] == '\0'
|| ((tname[5] == 'p' || tname[5] == 'e')
&& tname[6] == '\0'))))
- *fork_or_exec = 1;
+ 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.
&& (! strcmp (tname, "malloc")
|| ! strcmp (tname, "calloc")
|| ! strcmp (tname, "strdup")))
- *is_malloc = 1;
+ flags |= ECF_MALLOC;
+ }
+ return flags;
+}
+
+/* Return nonzero when tree represent call to longjmp. */
+int
+setjmp_call_p (fndecl)
+ tree fndecl;
+{
+ return special_function_p (fndecl, 0) & ECF_RETURNS_TWICE;
+}
+
+/* Detect flags (function attributes) from the function type node. */
+static int
+flags_from_decl_or_type (exp)
+ tree exp;
+{
+ int flags = 0;
+ /* ??? We can't set IS_MALLOC for function types? */
+ if (DECL_P (exp))
+ {
+ /* The function exp may have the `malloc' attribute. */
+ if (DECL_P (exp) && DECL_IS_MALLOC (exp))
+ flags |= ECF_MALLOC;
+
+ /* The function exp may have the `pure' attribute. */
+ if (DECL_P (exp) && DECL_IS_PURE (exp))
+ flags |= ECF_PURE;
+
+ if (TREE_NOTHROW (exp))
+ flags |= ECF_NOTHROW;
}
+
+ if (TREE_READONLY (exp) && !TREE_THIS_VOLATILE (exp))
+ flags |= ECF_CONST;
+
+ if (TREE_THIS_VOLATILE (exp))
+ flags |= ECF_NORETURN;
+
+ return flags;
}
+
/* Precompute all register parameters as described by ARGS, storing values
into fields within the ARGS array.
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. */
+ /* 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 / BITS_PER_UNIT);
+ PARM_BOUNDARY);
}
else
{
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 / BITS_PER_UNIT);
+ high_to_save - low_to_save + 1, PARM_BOUNDARY);
}
#endif
bytes -= bitsize / BITS_PER_UNIT;
store_bit_field (reg, bitsize, big_endian_correction, word_mode,
- extract_bit_field (word, bitsize, 0, 1,
- NULL_RTX, word_mode,
- word_mode,
- bitalign / BITS_PER_UNIT,
+ extract_bit_field (word, bitsize, 0, 1, NULL_RTX,
+ word_mode, word_mode, bitalign,
BITS_PER_WORD),
- bitalign / BITS_PER_UNIT, BITS_PER_WORD);
+ bitalign, BITS_PER_WORD);
}
}
}
OLD_STACK_LEVEL is a pointer to an rtx which olds the old stack level
and may be modified by this routine.
- OLD_PENDING_ADJ, MUST_PREALLOCATE and IS_CONST are pointers to integer
+ OLD_PENDING_ADJ, MUST_PREALLOCATE and FLAGS are pointers to integer
flags which may may be modified by this routine. */
static void
initialize_argument_information (num_actuals, args, args_size, n_named_args,
actparms, fndecl, args_so_far,
reg_parm_stack_space, old_stack_level,
- old_pending_adj, must_preallocate, is_const,
+ old_pending_adj, must_preallocate,
ecf_flags)
int num_actuals ATTRIBUTE_UNUSED;
struct arg_data *args;
rtx *old_stack_level;
int *old_pending_adj;
int *must_preallocate;
- int *is_const;
- int ecf_flags;
+ int *ecf_flags;
{
/* 1 if scanning parms front to back, -1 if scanning back to front. */
int inc;
MEM_SET_IN_STRUCT_P (copy, AGGREGATE_TYPE_P (type));
store_expr (args[i].tree_value, copy, 0);
- *is_const = 0;
+ *ecf_flags &= ~(ECF_CONST | ECF_PURE);
args[i].tree_value = build1 (ADDR_EXPR,
build_pointer_type (type),
/* If this is a sibling call and the machine has register windows, the
register window has to be unwinded before calling the routine, so
arguments have to go into the incoming registers. */
- if (ecf_flags & ECF_SIBCALL)
+ if (*ecf_flags & ECF_SIBCALL)
args[i].reg = FUNCTION_INCOMING_ARG (*args_so_far, mode, type,
argpos < n_named_args);
else
/* If this is an addressable type, we cannot pre-evaluate it. Thus,
we cannot consider this function call constant. */
if (TREE_ADDRESSABLE (type))
- *is_const = 0;
+ *ecf_flags &= ~(ECF_CONST | ECF_PURE);
/* Compute the stack-size of this argument. */
if (args[i].reg == 0 || args[i].partial != 0
#ifdef PREFERRED_STACK_BOUNDARY
preferred_stack_boundary /= BITS_PER_UNIT;
if (preferred_stack_boundary > 1)
- args_size->var = round_up (args_size->var, preferred_stack_boundary);
+ {
+ /* We don't handle this case yet. To handle it correctly we have
+ to add the delta, round and substract the delta.
+ Currently no machine description requires this support. */
+ if (stack_pointer_delta & (preferred_stack_boundary - 1))
+ abort();
+ args_size->var = round_up (args_size->var, preferred_stack_boundary);
+ }
#endif
if (reg_parm_stack_space > 0)
if (preferred_stack_boundary < 1)
preferred_stack_boundary = 1;
args_size->constant = (((args_size->constant
- + arg_space_so_far
- + pending_stack_adjust
+ + stack_pointer_delta
+ preferred_stack_boundary - 1)
/ preferred_stack_boundary
* preferred_stack_boundary)
- - arg_space_so_far
- - pending_stack_adjust);
+ - stack_pointer_delta);
#endif
args_size->constant = MAX (args_size->constant,
/* Precompute parameters as needed for a function call.
- IS_CONST indicates the target function is a pure function.
-
- MUST_PREALLOCATE indicates that we must preallocate stack space for
- any stack arguments.
+ FLAGS is mask of ECF_* constants.
NUM_ACTUALS is the number of arguments.
ARGS is an array containing information for each argument; this routine
- fills in the INITIAL_VALUE and VALUE fields for each precomputed argument.
-
- ARGS_SIZE contains information about the size of the arg list. */
+ fills in the INITIAL_VALUE and VALUE fields for each precomputed argument.
+ */
static void
-precompute_arguments (is_const, must_preallocate, num_actuals, args, args_size)
- int is_const;
- int must_preallocate;
+precompute_arguments (flags, num_actuals, args)
+ int flags;
int num_actuals;
struct arg_data *args;
- struct args_size *args_size;
{
int i;
on the stack, then we must precompute any parameter which contains a
function call which will store arguments on the stack.
Otherwise, evaluating the parameter may clobber previous parameters
- which have already been stored into the stack. */
+ which have already been stored into the stack. (we have code to avoid
+ such case by saving the ougoing stack arguments, but it results in
+ worse code) */
for (i = 0; i < num_actuals; i++)
- if (is_const
- || ((args_size->var != 0 || args_size->constant != 0)
- && calls_function (args[i].tree_value, 1))
- || (must_preallocate
- && (args_size->var != 0 || args_size->constant != 0)
- && calls_function (args[i].tree_value, 0)))
+ if ((flags & (ECF_CONST | ECF_PURE))
+ || calls_function (args[i].tree_value, !ACCUMULATE_OUTGOING_ARGS))
{
/* If this is an addressable type, we cannot pre-evaluate it. */
if (TREE_ADDRESSABLE (TREE_TYPE (args[i].tree_value)))
locations. The Irix 6 ABI has examples of this. */
if (GET_CODE (reg) == PARALLEL)
- {
- emit_group_load (reg, args[i].value,
- int_size_in_bytes (TREE_TYPE (args[i].tree_value)),
- (TYPE_ALIGN (TREE_TYPE (args[i].tree_value))
- / BITS_PER_UNIT));
- }
+ emit_group_load (reg, args[i].value,
+ int_size_in_bytes (TREE_TYPE (args[i].tree_value)),
+ TYPE_ALIGN (TREE_TYPE (args[i].tree_value)));
/* If simple case, just do move. If normal partial, store_one_arg
has already loaded the register for us. In all other cases,
}
}
+/* Try to integreate function. See expand_inline_function for documentation
+ about the parameters. */
+
+static rtx
+try_to_integrate (fndecl, actparms, target, ignore, type, structure_value_addr)
+ tree fndecl;
+ tree actparms;
+ rtx target;
+ int ignore;
+ tree type;
+ rtx structure_value_addr;
+{
+ rtx temp;
+ rtx before_call;
+ int i;
+ rtx old_stack_level = 0;
+ int reg_parm_stack_space = 0;
+
+#ifdef REG_PARM_STACK_SPACE
+#ifdef MAYBE_REG_PARM_STACK_SPACE
+ reg_parm_stack_space = MAYBE_REG_PARM_STACK_SPACE;
+#else
+ reg_parm_stack_space = REG_PARM_STACK_SPACE (fndecl);
+#endif
+#endif
+
+ before_call = get_last_insn ();
+
+ temp = expand_inline_function (fndecl, actparms, target,
+ ignore, type,
+ structure_value_addr);
+
+ /* If inlining succeeded, return. */
+ if (temp != (rtx) (HOST_WIDE_INT) - 1)
+ {
+ if (ACCUMULATE_OUTGOING_ARGS)
+ {
+ /* If the outgoing argument list must be preserved, push
+ 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;
+
+ if (stack_arg_under_construction || i >= 0)
+ {
+ rtx first_insn
+ = before_call ? NEXT_INSN (before_call) : get_insns ();
+ rtx insn = NULL_RTX, seq;
+
+ /* Look for a call in the inline function code.
+ If DECL_SAVED_INSNS (fndecl)->outgoing_args_size is
+ nonzero then there is a call and it is not necessary
+ to scan the insns. */
+
+ if (DECL_SAVED_INSNS (fndecl)->outgoing_args_size == 0)
+ for (insn = first_insn; insn; insn = NEXT_INSN (insn))
+ if (GET_CODE (insn) == CALL_INSN)
+ break;
+
+ if (insn)
+ {
+ /* Reserve enough stack space so that the largest
+ argument list of any function call in the inline
+ function does not overlap the argument list being
+ evaluated. This is usually an overestimate because
+ allocate_dynamic_stack_space reserves space for an
+ outgoing argument list in addition to the requested
+ space, but there is no way to ask for stack space such
+ that an argument list of a certain length can be
+ safely constructed.
+
+ Add the stack space reserved for register arguments, if
+ any, in the inline function. What is really needed is the
+ largest value of reg_parm_stack_space in the inline
+ function, but that is not available. Using the current
+ value of reg_parm_stack_space is wrong, but gives
+ correct results on all supported machines. */
+
+ int adjust = (DECL_SAVED_INSNS (fndecl)->outgoing_args_size
+ + reg_parm_stack_space);
+
+ start_sequence ();
+ emit_stack_save (SAVE_BLOCK, &old_stack_level, NULL_RTX);
+ allocate_dynamic_stack_space (GEN_INT (adjust),
+ NULL_RTX, BITS_PER_UNIT);
+ seq = get_insns ();
+ end_sequence ();
+ emit_insns_before (seq, first_insn);
+ emit_stack_restore (SAVE_BLOCK, old_stack_level, NULL_RTX);
+ }
+ }
+ }
+
+ /* If the result is equivalent to TARGET, return TARGET to simplify
+ checks in store_expr. They can be equivalent but not equal in the
+ case of a function that returns BLKmode. */
+ if (temp != target && rtx_equal_p (temp, target))
+ return target;
+ return temp;
+ }
+
+ /* If inlining failed, mark FNDECL as needing to be compiled
+ separately after all. If function was declared inline,
+ give a warning. */
+ if (DECL_INLINE (fndecl) && warn_inline && !flag_no_inline
+ && optimize > 0 && !TREE_ADDRESSABLE (fndecl))
+ {
+ warning_with_decl (fndecl, "inlining failed in call to `%s'");
+ warning ("called from here");
+ }
+ mark_addressable (fndecl);
+ return (rtx) (HOST_WIDE_INT) - 1;
+}
+
+/* We need to pop PENDING_STACK_ADJUST bytes. But, if the arguments
+ wouldn't fill up an even multiple of PREFERRED_UNIT_STACK_BOUNDARY
+ bytes, then we would need to push some additional bytes to pad the
+ arguments. So, we compute an adjust to the stack pointer for an
+ amount that will leave the stack under-aligned by UNADJUSTED_ARGS_SIZE
+ bytes. Then, when the arguments are pushed the stack will be perfectly
+ aligned. ARGS_SIZE->CONSTANT is set to the number of bytes that should
+ be popped after the call. Returns the adjustment. */
+
+static int
+combine_pending_stack_adjustment_and_call (unadjusted_args_size,
+ args_size,
+ preferred_unit_stack_boundary)
+ int unadjusted_args_size;
+ struct args_size *args_size;
+ int preferred_unit_stack_boundary;
+{
+ /* The number of bytes to pop so that the stack will be
+ under-aligned by UNADJUSTED_ARGS_SIZE bytes. */
+ HOST_WIDE_INT adjustment;
+ /* The alignment of the stack after the arguments are pushed, if we
+ just pushed the arguments without adjust the stack here. */
+ HOST_WIDE_INT unadjusted_alignment;
+
+ unadjusted_alignment
+ = ((stack_pointer_delta + unadjusted_args_size)
+ % preferred_unit_stack_boundary);
+
+ /* We want to get rid of as many of the PENDING_STACK_ADJUST bytes
+ as possible -- leaving just enough left to cancel out the
+ UNADJUSTED_ALIGNMENT. In other words, we want to ensure that the
+ PENDING_STACK_ADJUST is non-negative, and congruent to
+ -UNADJUSTED_ALIGNMENT modulo the PREFERRED_UNIT_STACK_BOUNDARY. */
+
+ /* Begin by trying to pop all the bytes. */
+ unadjusted_alignment
+ = (unadjusted_alignment
+ - (pending_stack_adjust % preferred_unit_stack_boundary));
+ adjustment = pending_stack_adjust;
+ /* Push enough additional bytes that the stack will be aligned
+ after the arguments are pushed. */
+ if (unadjusted_alignment >= 0)
+ adjustment -= preferred_unit_stack_boundary - unadjusted_alignment;
+ else
+ adjustment += unadjusted_alignment;
+
+ /* Now, sets ARGS_SIZE->CONSTANT so that we pop the right number of
+ bytes after the call. The right number is the entire
+ PENDING_STACK_ADJUST less our ADJUSTMENT plus the amount required
+ by the arguments in the first place. */
+ args_size->constant
+ = pending_stack_adjust - adjustment + unadjusted_args_size;
+
+ return adjustment;
+}
+
/* 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.
or 0 if the function is computed (not known by name). */
tree fndecl = 0;
char *name = 0;
- rtx before_call;
rtx insn;
int try_tail_call;
int pass;
(on machines that lack push insns), or 0 if space not preallocated. */
rtx argblock = 0;
- /* Nonzero if it is plausible that this is a call to alloca. */
- int may_be_alloca;
- /* Nonzero if this is a call to malloc or a related function. */
- int is_malloc;
- /* Nonzero if this is a call to setjmp or a related function. */
- int returns_twice;
- /* Nonzero if this is a call to `longjmp'. */
- int is_longjmp;
- /* Nonzero if this is a syscall that makes a new process in the image of
- the current one. */
- int fork_or_exec;
+ /* Mask of ECF_ flags. */
+ int flags = 0;
/* Nonzero if this is a call to an inline function. */
int is_integrable = 0;
- /* Nonzero if this is a call to a `const' function.
- Note that only explicitly named functions are handled as `const' here. */
- int is_const = 0;
- /* Nonzero if this is a call to a `volatile' function. */
- int is_volatile = 0;
- /* Nonzero if this is a call to a function that won't throw an exception. */
- int nothrow = TREE_NOTHROW (exp);
#ifdef REG_PARM_STACK_SPACE
/* Define the boundary of the register parm stack space that needs to be
save, if any. */
rtx old_stack_level = 0;
int old_pending_adj = 0;
int old_inhibit_defer_pop = inhibit_defer_pop;
+ int old_stack_allocated;
rtx call_fusage;
register tree p;
register int i;
- int preferred_stack_boundary;
+ /* The alignment of the stack, in bits. */
+ HOST_WIDE_INT preferred_stack_boundary;
+ /* The alignment of the stack, in bytes. */
+ HOST_WIDE_INT preferred_unit_stack_boundary;
/* The value of the function call can be put in a hard register. But
if -fcheck-memory-usage, code which invokes functions (and thus
if (current_function_check_memory_usage)
target = 0;
+ /* See if this is "nothrow" function call. */
+ if (TREE_NOTHROW (exp))
+ flags |= ECF_NOTHROW;
+
/* See if we can find a DECL-node for the actual function.
As a result, decide whether this is a call to an integrable function. */
- p = TREE_OPERAND (exp, 0);
- if (TREE_CODE (p) == ADDR_EXPR)
+ fndecl = get_callee_fndecl (exp);
+ if (fndecl)
{
- fndecl = TREE_OPERAND (p, 0);
- if (TREE_CODE (fndecl) != FUNCTION_DECL)
- fndecl = 0;
- else
+ if (!flag_no_inline
+ && fndecl != current_function_decl
+ && DECL_INLINE (fndecl)
+ && DECL_SAVED_INSNS (fndecl)
+ && DECL_SAVED_INSNS (fndecl)->inlinable)
+ is_integrable = 1;
+ else if (! TREE_ADDRESSABLE (fndecl))
{
- if (!flag_no_inline
- && fndecl != current_function_decl
- && DECL_INLINE (fndecl)
- && DECL_SAVED_INSNS (fndecl)
- && DECL_SAVED_INSNS (fndecl)->inlinable)
- is_integrable = 1;
- else if (! TREE_ADDRESSABLE (fndecl))
- {
- /* In case this function later becomes inlinable,
- record that there was already a non-inline call to it.
+ /* In case this function later becomes inlinable,
+ record that there was already a non-inline call to it.
- Use abstraction instead of setting TREE_ADDRESSABLE
- directly. */
- if (DECL_INLINE (fndecl) && warn_inline && !flag_no_inline
- && optimize > 0)
- {
- warning_with_decl (fndecl, "can't inline call to `%s'");
- warning ("called from here");
- }
- mark_addressable (fndecl);
+ Use abstraction instead of setting TREE_ADDRESSABLE
+ directly. */
+ if (DECL_INLINE (fndecl) && warn_inline && !flag_no_inline
+ && optimize > 0)
+ {
+ warning_with_decl (fndecl, "can't inline call to `%s'");
+ warning ("called from here");
}
-
- if (TREE_READONLY (fndecl) && ! TREE_THIS_VOLATILE (fndecl)
- && TYPE_MODE (TREE_TYPE (exp)) != VOIDmode)
- is_const = 1;
-
- if (TREE_THIS_VOLATILE (fndecl))
- is_volatile = 1;
-
- if (TREE_NOTHROW (fndecl))
- nothrow = 1;
+ mark_addressable (fndecl);
}
+
+ flags |= flags_from_decl_or_type (fndecl);
}
/* If we don't have specific function to call, see if we have a
- constant or `noreturn' function from the type. */
- if (fndecl == 0)
+ attributes set in the type. */
+ else
{
- is_const = TREE_READONLY (TREE_TYPE (TREE_TYPE (p)));
- is_volatile = TREE_THIS_VOLATILE (TREE_TYPE (TREE_TYPE (p)));
+ p = TREE_OPERAND (exp, 0);
+ flags |= flags_from_decl_or_type (TREE_TYPE (TREE_TYPE (p)));
}
#ifdef REG_PARM_STACK_SPACE
if (aggregate_value_p (exp))
{
/* This call returns a big structure. */
- is_const = 0;
+ flags &= ~(ECF_CONST | ECF_PURE);
#ifdef PCC_STATIC_STRUCT_RETURN
{
if (is_integrable)
{
- rtx temp;
-
- before_call = get_last_insn ();
-
- temp = expand_inline_function (fndecl, actparms, target,
- ignore, TREE_TYPE (exp),
- structure_value_addr);
-
- /* If inlining succeeded, return. */
- if (temp != (rtx) (HOST_WIDE_INT) -1)
- {
- if (ACCUMULATE_OUTGOING_ARGS)
- {
- /* If the outgoing argument list must be preserved, push
- 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;
-
- if (stack_arg_under_construction || i >= 0)
- {
- rtx first_insn
- = before_call ? NEXT_INSN (before_call) : get_insns ();
- rtx insn = NULL_RTX, seq;
-
- /* Look for a call in the inline function code.
- If DECL_SAVED_INSNS (fndecl)->outgoing_args_size is
- nonzero then there is a call and it is not necessary
- to scan the insns. */
-
- if (DECL_SAVED_INSNS (fndecl)->outgoing_args_size == 0)
- for (insn = first_insn; insn; insn = NEXT_INSN (insn))
- if (GET_CODE (insn) == CALL_INSN)
- break;
-
- if (insn)
- {
- /* Reserve enough stack space so that the largest
- argument list of any function call in the inline
- function does not overlap the argument list being
- evaluated. This is usually an overestimate because
- allocate_dynamic_stack_space reserves space for an
- outgoing argument list in addition to the requested
- space, but there is no way to ask for stack space such
- that an argument list of a certain length can be
- safely constructed.
-
- Add the stack space reserved for register arguments, if
- any, in the inline function. What is really needed is the
- largest value of reg_parm_stack_space in the inline
- function, but that is not available. Using the current
- value of reg_parm_stack_space is wrong, but gives
- correct results on all supported machines. */
-
- int adjust = (DECL_SAVED_INSNS (fndecl)->outgoing_args_size
- + reg_parm_stack_space);
-
- start_sequence ();
- emit_stack_save (SAVE_BLOCK, &old_stack_level, NULL_RTX);
- allocate_dynamic_stack_space (GEN_INT (adjust),
- NULL_RTX, BITS_PER_UNIT);
- seq = get_insns ();
- end_sequence ();
- emit_insns_before (seq, first_insn);
- emit_stack_restore (SAVE_BLOCK, old_stack_level, NULL_RTX);
- }
- }
- }
-
- /* If the result is equivalent to TARGET, return TARGET to simplify
- checks in store_expr. They can be equivalent but not equal in the
- case of a function that returns BLKmode. */
- if (temp != target && rtx_equal_p (temp, target))
- return target;
- return temp;
- }
-
- /* If inlining failed, mark FNDECL as needing to be compiled
- separately after all. If function was declared inline,
- give a warning. */
- if (DECL_INLINE (fndecl) && warn_inline && !flag_no_inline
- && optimize > 0 && ! TREE_ADDRESSABLE (fndecl))
- {
- warning_with_decl (fndecl, "inlining failed in call to `%s'");
- warning ("called from here");
- }
- mark_addressable (fndecl);
+ rtx temp = try_to_integrate (fndecl, actparms, target,
+ ignore, TREE_TYPE (exp),
+ structure_value_addr);
+ if (temp != (rtx) (HOST_WIDE_INT) - 1)
+ return temp;
}
currently_expanding_call++;
pushed these optimizations into -O2. Don't try if we're already
expanding a call, as that means we're an argument. Similarly, if
there's pending loops or cleanups we know there's code to follow
- the call. */
+ the call.
+
+ If rtx_equal_function_value_matters is false, that means we've
+ finished with regular parsing. Which means that some of the
+ machinery we use to generate tail-calls is no longer in place.
+ This is most often true of sjlj-exceptions, which we couldn't
+ tail-call to anyway. */
try_tail_call = 0;
if (flag_optimize_sibling_calls
&& currently_expanding_call == 1
+ && rtx_equal_function_value_matters
&& stmt_loop_nest_empty ()
&& ! any_pending_cleanups (1))
{
recursion call can be ignored if we indeed use the tail recursion
call expansion. */
int save_pending_stack_adjust = pending_stack_adjust;
+ int save_stack_pointer_delta = stack_pointer_delta;
/* Use a new sequence to hold any RTL we generate. We do not even
know if we will use this RTL yet. The final decision can not be
/* Restore the original pending stack adjustment for the sibling and
normal call cases below. */
pending_stack_adjust = save_pending_stack_adjust;
+ stack_pointer_delta = save_stack_pointer_delta;
}
function_call_count++;
if (fndecl && DECL_NAME (fndecl))
name = IDENTIFIER_POINTER (DECL_NAME (fndecl));
+ /* Figure out the amount to which the stack should be aligned. */
#ifdef PREFERRED_STACK_BOUNDARY
preferred_stack_boundary = PREFERRED_STACK_BOUNDARY;
#else
preferred_stack_boundary = STACK_BOUNDARY;
#endif
+ preferred_unit_stack_boundary = preferred_stack_boundary / BITS_PER_UNIT;
/* Ensure current function's preferred stack boundary is at least
what we need. We don't have to increase alignment for recursive
/* See if this is a call to a function that can return more than once
or a call to longjmp or malloc. */
- special_function_p (fndecl, &returns_twice, &is_longjmp, &fork_or_exec,
- &is_malloc, &may_be_alloca);
+ flags |= special_function_p (fndecl, flags);
- if (may_be_alloca)
+ if (flags & ECF_MAY_BE_ALLOCA)
current_function_calls_alloca = 1;
/* Operand 0 is a pointer-to-function; get the type of the function. */
recursion call can be ignored if we indeed use the tail recursion
call expansion. */
int save_pending_stack_adjust;
+ int save_stack_pointer_delta;
rtx insns;
rtx before_call, next_arg_reg;
|| ! FUNCTION_OK_FOR_SIBCALL (fndecl))
continue;
+ /* Emit any queued insns now; otherwise they would end up in
+ only one of the alternates. */
+ emit_queue ();
+
/* We know at this point that there are not currently any
pending cleanups. If, however, in the process of evaluating
the arguments we were to create some, we'll need to be
/* State variables we need to save and restore between
iterations. */
save_pending_stack_adjust = pending_stack_adjust;
+ save_stack_pointer_delta = stack_pointer_delta;
}
+ if (pass)
+ flags &= ~ECF_SIBCALL;
+ else
+ flags |= ECF_SIBCALL;
/* Other state variables that we must reinitialize each time
- through the loop (that are not initialized by the loop itself. */
+ through the loop (that are not initialized by the loop itself). */
argblock = 0;
call_fusage = 0;
/* When calling a const function, we must pop the stack args right away,
so that the pop is deleted or moved with the call. */
- if (is_const)
+ if (flags & (ECF_CONST | ECF_PURE))
NO_DEFER_POP;
/* Don't let pending stack adjusts add up to too much.
this might be a call to alloca or if we are expanding a sibling
call sequence. */
if (pending_stack_adjust >= 32
- || (pending_stack_adjust > 0 && may_be_alloca)
+ || (pending_stack_adjust > 0 && (flags & ECF_MAY_BE_ALLOCA))
|| pass == 0)
do_pending_stack_adjust ();
- if (profile_arc_flag && fork_or_exec)
+ if (profile_arc_flag && (flags & ECF_FORK_OR_EXEC))
{
/* A fork duplicates the profile information, and an exec discards
it. We can't rely on fork/exec to be paired. So write out the
n_named_args, actparms, fndecl,
&args_so_far, reg_parm_stack_space,
&old_stack_level, &old_pending_adj,
- &must_preallocate, &is_const,
- (pass == 0) ? ECF_SIBCALL : 0);
+ &must_preallocate, &flags);
#ifdef FINAL_REG_PARM_STACK_SPACE
reg_parm_stack_space = FINAL_REG_PARM_STACK_SPACE (args_size.constant,
Also do not make a sibling call. */
- is_const = 0;
+ flags &= ~(ECF_CONST | ECF_PURE);
must_preallocate = 1;
sibcall_failure = 1;
}
sibcall_failure = 1;
}
- /* Compute the actual size of the argument block required. The variable
- and constant sizes must be combined, the size may have to be rounded,
- and there may be a minimum required size. When generating a sibcall
- pattern, do not round up, since we'll be re-using whatever space our
- caller provided. */
- unadjusted_args_size
- = compute_argument_block_size (reg_parm_stack_space, &args_size,
- (pass == 0 ? 0
- : preferred_stack_boundary));
-
/* If the callee pops its own arguments, then it must pop exactly
the same number of arguments as the current function. */
- if (RETURN_POPS_ARGS (fndecl, funtype, unadjusted_args_size)
+ if (RETURN_POPS_ARGS (fndecl, funtype, args_size.constant)
!= RETURN_POPS_ARGS (current_function_decl,
TREE_TYPE (current_function_decl),
current_function_args_size))
|| reg_mentioned_p (virtual_outgoing_args_rtx,
structure_value_addr))
&& (args_size.var
- || (!ACCUMULATE_OUTGOING_ARGS && args_size.constant)
- ))
+ || (!ACCUMULATE_OUTGOING_ARGS && args_size.constant)))
structure_value_addr = copy_to_reg (structure_value_addr);
/* Precompute any arguments as needed. */
- precompute_arguments (is_const, must_preallocate, num_actuals,
- args, &args_size);
+ if (pass)
+ precompute_arguments (flags, num_actuals, args);
/* Now we are about to start emitting insns that can be deleted
if a libcall is deleted. */
- if (is_const || is_malloc)
+ if (flags & (ECF_CONST | ECF_PURE | ECF_MALLOC))
start_sequence ();
+ /* Compute the actual size of the argument block required. The variable
+ and constant sizes must be combined, the size may have to be rounded,
+ and there may be a minimum required size. When generating a sibcall
+ pattern, do not round up, since we'll be re-using whatever space our
+ caller provided. */
+ unadjusted_args_size
+ = compute_argument_block_size (reg_parm_stack_space, &args_size,
+ (pass == 0 ? 0
+ : preferred_stack_boundary));
+
+ old_stack_allocated = stack_pointer_delta - pending_stack_adjust;
+
+ /* The argument block when performing a sibling call is the
+ incoming argument block. */
+ if (pass == 0)
+ argblock = virtual_incoming_args_rtx;
+
/* If we have no actual push instructions, or shouldn't use them,
make space for all args right now. */
-
- if (args_size.var != 0)
+ else if (args_size.var != 0)
{
if (old_stack_level == 0)
{
{
if (ACCUMULATE_OUTGOING_ARGS)
{
- /* Since the stack pointer will never be pushed, it is possible
- for the evaluation of a parm to clobber something we have
- already written to the stack. Since most function calls on
- RISC machines do not use the stack, this is uncommon, but
- must work correctly.
+ /* Since the stack pointer will never be pushed, it is
+ possible for the evaluation of a parm to clobber
+ something we have already written to the stack.
+ Since most function calls on RISC machines do not use
+ the stack, this is uncommon, but must work correctly.
Therefore, we save any area of the stack that was already
- written and that we are using. Here we set up to do this by
- making a new stack usage map from the old one. The actual
- save will be done by store_one_arg.
+ written and that we are using. Here we set up to do this
+ by making a new stack usage map from the old one. The
+ actual save will be done by store_one_arg.
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. */
+ /* 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
highest_outgoing_arg_in_use = MAX (initial_highest_arg_in_use,
needed);
#endif
- stack_usage_map = (char *) alloca (highest_outgoing_arg_in_use);
+ stack_usage_map
+ = (char *) alloca (highest_outgoing_arg_in_use);
if (initial_highest_arg_in_use)
bcopy (initial_stack_usage_map, stack_usage_map,
- initial_highest_arg_in_use));
needed = 0;
- /* The address of the outgoing argument list must not be copied
- to a register here, because argblock would be left pointing
- to the wrong place after the call to
+ /* The address of the outgoing argument list must not be
+ copied to a register here, because argblock would be left
+ pointing to the wrong place after the call to
allocate_dynamic_stack_space below. */
argblock = virtual_outgoing_args_rtx;
if (inhibit_defer_pop == 0)
{
/* Try to reuse some or all of the pending_stack_adjust
- to get this space. Maybe we can avoid any pushing. */
- if (needed > pending_stack_adjust)
+ to get this space. */
+ needed
+ = (combine_pending_stack_adjustment_and_call
+ (unadjusted_args_size,
+ &args_size,
+ preferred_unit_stack_boundary));
+
+ /* combine_pending_stack_adjustment_and_call computes
+ an adjustment before the arguments are allocated.
+ Account for them and see whether or not the stack
+ needs to go up or down. */
+ needed = unadjusted_args_size - needed;
+
+ if (needed < 0)
{
- needed -= pending_stack_adjust;
- pending_stack_adjust = 0;
- }
- else
- {
- pending_stack_adjust -= needed;
+ /* We're releasing stack space. */
+ /* ??? We can avoid any adjustment at all if we're
+ already aligned. FIXME. */
+ pending_stack_adjust = -needed;
+ do_pending_stack_adjust ();
needed = 0;
}
+ else
+ /* We need to allocate space. We'll do that in
+ push_block below. */
+ pending_stack_adjust = 0;
}
- /* Special case this because overhead of `push_block' in this
- case is non-trivial. */
+
+ /* Special case this because overhead of `push_block' in
+ this case is non-trivial. */
if (needed == 0)
argblock = virtual_outgoing_args_rtx;
else
argblock = push_block (GEN_INT (needed), 0, 0);
- /* We only really need to call `copy_to_reg' in the case where
- push insns are going to be used to pass ARGBLOCK to a function
- call in ARGS. In that case, the stack pointer changes value
- from the allocation point to the call point, and hence
- the value of VIRTUAL_OUTGOING_ARGS_RTX changes as well.
- But might as well always do it. */
+ /* We only really need to call `copy_to_reg' in the case
+ where push insns are going to be used to pass ARGBLOCK
+ to a function call in ARGS. In that case, the stack
+ pointer changes value from the allocation point to the
+ call point, and hence the value of
+ VIRTUAL_OUTGOING_ARGS_RTX changes as well. But might
+ as well always do it. */
argblock = copy_to_reg (argblock);
- }
- }
- }
-
- /* The argument block when performing a sibling call is the
- incoming argument block. */
- if (pass == 0)
- {
- rtx temp = plus_constant (arg_pointer_rtx,
- FIRST_PARM_OFFSET (current_function_decl));
- argblock = force_reg (Pmode, force_operand (temp, NULL_RTX));
- }
- 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)
- {
+ /* 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 + args_size.constant);
+ rtx push_size = GEN_INT (reg_parm_stack_space
+ + args_size.constant);
#else
- rtx push_size = GEN_INT (args_size.constant);
+ rtx push_size = GEN_INT (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);
- bzero (stack_usage_map, highest_outgoing_arg_in_use);
- highest_outgoing_arg_in_use = 0;
+ 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);
+ bzero (stack_usage_map, 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;
+ }
}
- 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);
{
/* When the stack adjustment is pending, we get better code
by combining the adjustments. */
- if (pending_stack_adjust && ! is_const
+ if (pending_stack_adjust
+ && ! (flags & (ECF_CONST | ECF_PURE))
&& ! inhibit_defer_pop)
{
- args_size.constant = (unadjusted_args_size
- + ((pending_stack_adjust
- + args_size.constant
- + arg_space_so_far
- - unadjusted_args_size)
- % (preferred_stack_boundary
- / BITS_PER_UNIT)));
- pending_stack_adjust -= (args_size.constant
- - unadjusted_args_size);
+ pending_stack_adjust
+ = (combine_pending_stack_adjustment_and_call
+ (unadjusted_args_size,
+ &args_size,
+ preferred_unit_stack_boundary));
do_pending_stack_adjust ();
}
else if (argblock == 0)
anti_adjust_stack (GEN_INT (args_size.constant
- unadjusted_args_size));
- arg_space_so_far += args_size.constant - unadjusted_args_size;
-
- /* Now that the stack is properly aligned, pops can't safely
- be deferred during the evaluation of the arguments. */
- NO_DEFER_POP;
}
+ /* Now that the stack is properly aligned, pops can't safely
+ be deferred during the evaluation of the arguments. */
+ NO_DEFER_POP;
#endif
/* Don't try to defer pops if preallocating, not even from the first arg,
#ifdef REG_PARM_STACK_SPACE
/* Save the fixed argument area if it's part of the caller's frame and
is clobbered by argument setup for this call. */
- if (ACCUMULATE_OUTGOING_ARGS)
+ if (ACCUMULATE_OUTGOING_ARGS && pass)
save_area = save_fixed_argument_area (reg_parm_stack_space, argblock,
&low_to_save, &high_to_save);
#endif
for (i = 0; i < num_actuals; i++)
if (args[i].reg == 0 || args[i].pass_on_stack)
- store_one_arg (&args[i], argblock, may_be_alloca,
+ store_one_arg (&args[i], argblock, flags,
args_size.var != 0, reg_parm_stack_space);
/* If we have a parm that is passed in registers but not in memory
if (reg_parm_seen)
for (i = 0; i < num_actuals; i++)
if (args[i].partial != 0 && ! args[i].pass_on_stack)
- store_one_arg (&args[i], argblock, may_be_alloca,
+ store_one_arg (&args[i], argblock, flags,
args_size.var != 0, reg_parm_stack_space);
#ifdef PREFERRED_STACK_BOUNDARY
/* If we pushed args in forward order, perform stack alignment
after pushing the last arg. */
- /* ??? Fix for arg_space_so_far. */
if (!PUSH_ARGS_REVERSED && argblock == 0)
anti_adjust_stack (GEN_INT (args_size.constant
- unadjusted_args_size));
passed in registers. */
#ifdef OUTGOING_REG_PARM_STACK_SPACE
if (!ACCUMULATE_OUTGOING_ARGS
- must_preallocate == 0 && reg_parm_stack_space > 0)
+ && must_preallocate == 0 && reg_parm_stack_space > 0)
anti_adjust_stack (GEN_INT (reg_parm_stack_space));
#endif
/* All arguments and registers used for the call must be set up by
now! */
+#ifdef PREFERRED_STACK_BOUNDARY
+ /* Stack must be properly aligned now. */
+ if (pass && stack_pointer_delta % preferred_unit_stack_boundary)
+ abort();
+#endif
+
/* Generate the actual call instruction. */
emit_call_1 (funexp, fndecl, funtype, unadjusted_args_size,
args_size.constant, struct_value_size,
next_arg_reg, valreg, old_inhibit_defer_pop, call_fusage,
- ((is_const ? ECF_IS_CONST : 0)
- | (nothrow ? ECF_NOTHROW : 0)
- | (pass == 0 ? ECF_SIBCALL : 0)));
+ flags);
+
+ /* 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
we have no way to move such values into a pseudo register. */
- if (is_const && valreg != 0 && GET_CODE (valreg) != PARALLEL)
+ if ((flags & (ECF_CONST | ECF_PURE))
+ && valreg != 0 && GET_CODE (valreg) != PARALLEL)
{
rtx note = 0;
rtx temp = gen_reg_rtx (GET_MODE (valreg));
/* Mark the return value as a pointer if needed. */
if (TREE_CODE (TREE_TYPE (exp)) == POINTER_TYPE)
- {
- tree pointed_to = TREE_TYPE (TREE_TYPE (exp));
- mark_reg_pointer (temp, TYPE_ALIGN (pointed_to) / BITS_PER_UNIT);
- }
+ mark_reg_pointer (temp, TYPE_ALIGN (TREE_TYPE (TREE_TYPE (exp))));
/* Construct an "equal form" for the value which mentions all the
arguments in order as well as the function name. */
- if (PUSH_ARGS_REVERSED)
- for (i = 0; i < num_actuals; i++)
- note = gen_rtx_EXPR_LIST (VOIDmode, args[i].initial_value, note);
- else
- for (i = num_actuals - 1; i >= 0; i--)
- note = gen_rtx_EXPR_LIST (VOIDmode, args[i].initial_value, note);
+ for (i = 0; i < num_actuals; i++)
+ note = gen_rtx_EXPR_LIST (VOIDmode, args[i].initial_value, note);
note = gen_rtx_EXPR_LIST (VOIDmode, funexp, note);
insns = get_insns ();
end_sequence ();
+ if (flags & ECF_PURE)
+ note = gen_rtx_EXPR_LIST (VOIDmode,
+ gen_rtx_USE (VOIDmode,
+ gen_rtx_MEM (BLKmode,
+ gen_rtx_SCRATCH (VOIDmode))), note);
+
emit_libcall_block (insns, temp, valreg, note);
valreg = temp;
}
- else if (is_const)
+ else if (flags & (ECF_CONST | ECF_PURE))
{
/* Otherwise, just write out the sequence without a note. */
rtx insns = get_insns ();
end_sequence ();
emit_insns (insns);
}
- else if (is_malloc)
+ else if (flags & ECF_MALLOC)
{
rtx temp = gen_reg_rtx (GET_MODE (valreg));
rtx last, insns;
/* The return value from a malloc-like function is a pointer. */
if (TREE_CODE (TREE_TYPE (exp)) == POINTER_TYPE)
- mark_reg_pointer (temp, BIGGEST_ALIGNMENT / BITS_PER_UNIT);
+ mark_reg_pointer (temp, BIGGEST_ALIGNMENT);
emit_move_insn (temp, valreg);
if nonvolatile values are live. For functions that cannot return,
inform flow that control does not fall through. */
- if (returns_twice || is_volatile || is_longjmp || pass == 0)
+ if ((flags & (ECF_RETURNS_TWICE | ECF_NORETURN | ECF_LONGJMP)) || pass == 0)
{
/* The barrier or NOTE_INSN_SETJMP note must be emitted
immediately after the CALL_INSN. Some ports emit more
abort ();
}
- if (returns_twice)
+ if (flags & ECF_RETURNS_TWICE)
{
emit_note_after (NOTE_INSN_SETJMP, last);
current_function_calls_setjmp = 1;
emit_barrier_after (last);
}
- if (is_longjmp)
+ if (flags & ECF_LONGJMP)
current_function_calls_longjmp = 1, sibcall_failure = 1;
/* If this function is returning into a memory location marked as
if (! rtx_equal_p (target, valreg))
emit_group_store (target, valreg, bytes,
- TYPE_ALIGN (TREE_TYPE (exp)) / BITS_PER_UNIT);
+ TYPE_ALIGN (TREE_TYPE (exp)));
+
/* We can not support sibling calls for this case. */
sibcall_failure = 1;
}
stack_usage_map = initial_stack_usage_map;
sibcall_failure = 1;
}
- else if (ACCUMULATE_OUTGOING_ARGS)
+ else if (ACCUMULATE_OUTGOING_ARGS && pass)
{
#ifdef REG_PARM_STACK_SPACE
if (save_area)
{
restore_fixed_argument_area (save_area, argblock,
high_to_save, low_to_save);
- sibcall_failure = 1;
}
#endif
emit_block_move (stack_area,
validize_mem (args[i].save_area),
GEN_INT (args[i].size.constant),
- PARM_BOUNDARY / BITS_PER_UNIT);
- sibcall_failure = 1;
+ PARM_BOUNDARY);
}
highest_outgoing_arg_in_use = initial_highest_arg_in_use;
Check for the handler slots since we might not have a save area
for non-local gotos. */
- if (may_be_alloca && nonlocal_goto_handler_slots != 0)
+ if ((flags & ECF_MAY_BE_ALLOCA) && nonlocal_goto_handler_slots != 0)
emit_stack_save (SAVE_NONLOCAL, &nonlocal_goto_stack_level, NULL_RTX);
pop_temp_slots ();
zero out the sequence. */
if (sibcall_failure)
tail_call_insns = NULL_RTX;
-
/* Restore the pending stack adjustment now that we have
finished generating the sibling call sequence. */
+
pending_stack_adjust = save_pending_stack_adjust;
+ stack_pointer_delta = save_stack_pointer_delta;
}
else
normal_call_insns = insns;
The RETVAL parameter specifies whether return value needs to be saved, other
parameters are documented in the emit_library_call function bellow. */
static rtx
-emit_library_call_value_1 (retval, orgfun, value, no_queue, outmode, nargs, p)
+emit_library_call_value_1 (retval, orgfun, value, fn_type, outmode, nargs, p)
int retval;
rtx orgfun;
rtx value;
- int no_queue;
+ int fn_type;
enum machine_mode outmode;
int nargs;
va_list p;
int old_inhibit_defer_pop = inhibit_defer_pop;
rtx call_fusage = 0;
rtx mem_value = 0;
+ rtx valreg;
int pcc_struct_value = 0;
int struct_value_size = 0;
- int is_const;
+ int flags = 0;
int reg_parm_stack_space = 0;
- int nothrow;
int needed;
#ifdef REG_PARM_STACK_SPACE
#endif
#endif
- is_const = no_queue;
+ if (fn_type == 1)
+ flags |= ECF_CONST;
+ else if (fn_type == 2)
+ flags |= ECF_PURE;
fun = orgfun;
- nothrow = libfunc_nothrow (fun);
+ if (libfunc_nothrow (fun))
+ flags |= ECF_NOTHROW;
#ifdef PREFERRED_STACK_BOUNDARY
/* Ensure current function's preferred stack boundary is at least
#endif
/* This call returns a big structure. */
- is_const = 0;
+ flags &= ~(ECF_CONST | ECF_PURE);
}
/* ??? Unfinished: must pass the memory address as an argument. */
count = 0;
+ /* Now we are about to start emitting insns that can be deleted
+ if a libcall is deleted. */
+ if (flags & (ECF_CONST | ECF_PURE))
+ start_sequence ();
+
push_temp_slots ();
/* If there's a structure value address to be passed,
#endif
locate_and_pad_parm (Pmode, NULL_TREE,
- argvec[count].reg && argvec[count].partial == 0,
+#ifdef STACK_PARMS_IN_REG_PARM_AREA
+ 1,
+#else
+ argvec[count].reg != 0,
+#endif
NULL_TREE, &args_size, &argvec[count].offset,
&argvec[count].size, &alignment_pad);
#endif
locate_and_pad_parm (mode, NULL_TREE,
- argvec[count].reg && argvec[count].partial == 0,
+#ifdef STACK_PARMS_IN_REG_PARM_AREA
+ 1,
+#else
+ argvec[count].reg != 0,
+#endif
NULL_TREE, &args_size, &argvec[count].offset,
&argvec[count].size, &alignment_pad);
original_args_size = args_size;
#ifdef PREFERRED_STACK_BOUNDARY
- args_size.constant = (((args_size.constant + (STACK_BYTES - 1))
- / STACK_BYTES) * STACK_BYTES);
+ args_size.constant = (((args_size.constant
+ + stack_pointer_delta
+ + STACK_BYTES - 1)
+ / STACK_BYTES
+ * STACK_BYTES)
+ - stack_pointer_delta);
#endif
args_size.constant = MAX (args_size.constant,
{
save_area = assign_stack_temp (BLKmode, num_to_save, 0);
emit_block_move (validize_mem (save_area), stack_area,
- GEN_INT (num_to_save),
- PARM_BOUNDARY / BITS_PER_UNIT);
+ GEN_INT (num_to_save), PARM_BOUNDARY);
}
else
{
{
if (ACCUMULATE_OUTGOING_ARGS)
{
- /* If this is being stored into a pre-allocated, fixed-size, stack
- area, save any previous data at that location. */
+ /* If this is being stored into a pre-allocated, fixed-size,
+ stack area, save any previous data at that location. */
#ifdef ARGS_GROW_DOWNWARD
/* stack_slot is negative, but we want to index stack_usage_map
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. */
+ /* Don't store things in the fixed argument area at this
+ point; it has already been saved. */
&& i > reg_parm_stack_space)
break;
if (i != upper_bound)
{
- /* We need to make a save area. See what mode we can make it. */
+ /* We need to make a save area. See what mode we can make
+ it. */
enum machine_mode save_mode
- = mode_for_size (argvec[argnum].size.constant * BITS_PER_UNIT,
+ = mode_for_size (argvec[argnum].size.constant
+ * BITS_PER_UNIT,
MODE_INT, 1);
rtx stack_area
= gen_rtx_MEM
emit_move_insn (argvec[argnum].save_area, stack_area);
}
}
+
emit_push_insn (val, mode, NULL_TREE, NULL_RTX, 0, partial, reg, 0,
argblock, GEN_INT (argvec[argnum].offset.constant),
reg_parm_stack_space, ARGS_SIZE_RTX (alignment_pad));
NO_DEFER_POP;
}
-#if 0
- /* For version 1.37, try deleting this entirely. */
- if (! no_queue)
- emit_queue ();
-#endif
-
/* Any regs containing parms remain in use through the call. */
for (count = 0; count < nargs; count++)
{
/* Don't allow popping to be deferred, since then
cse'ing of library calls could delete a call and leave the pop. */
NO_DEFER_POP;
+ valreg = (mem_value == 0 && outmode != VOIDmode
+ ? hard_libcall_value (outmode) : NULL_RTX);
+
+#ifdef PREFERRED_STACK_BOUNDARY
+ /* Stack must be properly aligned now. */
+ if (stack_pointer_delta & (PREFERRED_STACK_BOUNDARY / BITS_PER_UNIT - 1))
+ abort();
+#endif
/* We pass the old value of inhibit_defer_pop + 1 to emit_call_1, which
will set inhibit_defer_pop to that value. */
original_args_size.constant, args_size.constant,
struct_value_size,
FUNCTION_ARG (args_so_far, VOIDmode, void_type_node, 1),
- mem_value == 0 && outmode != VOIDmode ? hard_libcall_value (outmode) : NULL_RTX,
- old_inhibit_defer_pop + 1, call_fusage,
- ((is_const ? ECF_IS_CONST : 0)
- | (nothrow ? ECF_NOTHROW : 0)));
+ valreg,
+ old_inhibit_defer_pop + 1, call_fusage, flags);
/* Now restore inhibit_defer_pop to its actual original value. */
OK_DEFER_POP;
+ /* 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
+ we have no way to move such values into a pseudo register. */
+ if ((flags & (ECF_CONST | ECF_PURE))
+ && valreg != 0 && GET_CODE (valreg) != PARALLEL)
+ {
+ rtx note = 0;
+ rtx temp = gen_reg_rtx (GET_MODE (valreg));
+ rtx insns;
+ int i;
+
+ /* 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++)
+ note = gen_rtx_EXPR_LIST (VOIDmode, argvec[i].value, note);
+ note = gen_rtx_EXPR_LIST (VOIDmode, fun, note);
+
+ insns = get_insns ();
+ end_sequence ();
+
+ if (flags & ECF_PURE)
+ note = gen_rtx_EXPR_LIST (VOIDmode,
+ gen_rtx_USE (VOIDmode,
+ gen_rtx_MEM (BLKmode,
+ gen_rtx_SCRATCH (VOIDmode))), note);
+
+ emit_libcall_block (insns, temp, valreg, note);
+
+ valreg = temp;
+ }
+ else if (flags & (ECF_CONST | ECF_PURE))
+ {
+ /* Otherwise, just write out the sequence without a note. */
+ rtx insns = get_insns ();
+
+ end_sequence ();
+ emit_insns (insns);
+ }
pop_temp_slots ();
/* Copy the value to the right place. */
else
emit_block_move (stack_area, validize_mem (save_area),
GEN_INT (high_to_save - low_to_save + 1),
- PARM_BOUNDARY / BITS_PER_UNIT);
+ PARM_BOUNDARY);
}
#endif
and machine_modes to convert them to.
The rtx values should have been passed through protect_from_queue already.
- NO_QUEUE will be true if and only if the library call is a `const' call
- which will be enclosed in REG_LIBCALL/REG_RETVAL notes; it is equivalent
- to the variable is_const in expand_call.
-
- NO_QUEUE must be true for const calls, because if it isn't, then
- any pending increment will be emitted between REG_LIBCALL/REG_RETVAL notes,
- and will be lost if the libcall sequence is optimized away.
-
- NO_QUEUE must be false for non-const calls, because if it isn't, the
- call insn will have its CONST_CALL_P bit set, and it will be incorrectly
- optimized. For instance, the instruction scheduler may incorrectly
- move memory references across the non-const call. */
+ FN_TYPE will is zero for `normal' calls, one for `const' calls, wich
+ which will be enclosed in REG_LIBCALL/REG_RETVAL notes and two for `pure'
+ calls, that are handled like `const' calls with extra
+ (use (memory (scratch)). */
void
-emit_library_call VPARAMS((rtx orgfun, int no_queue, enum machine_mode outmode,
+emit_library_call VPARAMS((rtx orgfun, int fn_type, enum machine_mode outmode,
int nargs, ...))
{
#ifndef ANSI_PROTOTYPES
rtx orgfun;
- int no_queue;
+ int fn_type;
enum machine_mode outmode;
int nargs;
#endif
#ifndef ANSI_PROTOTYPES
orgfun = va_arg (p, rtx);
- no_queue = va_arg (p, int);
+ fn_type = va_arg (p, int);
outmode = va_arg (p, enum machine_mode);
nargs = va_arg (p, int);
#endif
- emit_library_call_value_1 (0, orgfun, NULL_RTX, no_queue, outmode, nargs, p);
+ emit_library_call_value_1 (0, orgfun, NULL_RTX, fn_type, outmode, nargs, p);
va_end (p);
}
If VALUE is nonzero, VALUE is returned. */
rtx
-emit_library_call_value VPARAMS((rtx orgfun, rtx value, int no_queue,
+emit_library_call_value VPARAMS((rtx orgfun, rtx value, int fn_type,
enum machine_mode outmode, int nargs, ...))
{
#ifndef ANSI_PROTOTYPES
rtx orgfun;
rtx value;
- int no_queue;
+ int fn_type;
enum machine_mode outmode;
int nargs;
#endif
#ifndef ANSI_PROTOTYPES
orgfun = va_arg (p, rtx);
value = va_arg (p, rtx);
- no_queue = va_arg (p, int);
+ fn_type = va_arg (p, int);
outmode = va_arg (p, enum machine_mode);
nargs = va_arg (p, int);
#endif
- value = emit_library_call_value_1 (1, orgfun, value, no_queue, outmode, nargs, p);
+ value = emit_library_call_value_1 (1, orgfun, value, fn_type, outmode, nargs, p);
va_end (p);
FNDECL is the declaration of the function we are calling. */
static void
-store_one_arg (arg, argblock, may_be_alloca, variable_size,
+store_one_arg (arg, argblock, flags, variable_size,
reg_parm_stack_space)
struct arg_data *arg;
rtx argblock;
- int may_be_alloca;
+ int flags;
int variable_size ATTRIBUTE_UNUSED;
int reg_parm_stack_space;
{
this argument. */
push_temp_slots ();
- if (ACCUMULATE_OUTGOING_ARGS)
+ if (ACCUMULATE_OUTGOING_ARGS && !(flags & ECF_SIBCALL))
{
/* If this is being stored into a pre-allocated, fixed-size, stack area,
save any previous data at that location. */
preserve_temp_slots (arg->save_area);
emit_block_move (validize_mem (arg->save_area), stack_area,
GEN_INT (arg->size.constant),
- PARM_BOUNDARY / BITS_PER_UNIT);
+ PARM_BOUNDARY);
}
else
{
/* Don't allow anything left on stack from computation
of argument to alloca. */
- if (may_be_alloca)
+ if (flags & ECF_MAY_BE_ALLOCA)
do_pending_stack_adjust ();
if (arg->value == arg->stack)
partial, reg, used - size, argblock,
ARGS_SIZE_RTX (arg->offset), reg_parm_stack_space,
ARGS_SIZE_RTX (arg->alignment_pad));
-
- arg_space_so_far += used;
}
else
{
excess = (arg->size.constant - int_size_in_bytes (TREE_TYPE (pval))
+ partial * UNITS_PER_WORD);
size_rtx = expr_size (pval);
- arg_space_so_far += excess + INTVAL (size_rtx);
}
emit_push_insn (arg->value, arg->mode, TREE_TYPE (pval), size_rtx,
- TYPE_ALIGN (TREE_TYPE (pval)) / BITS_PER_UNIT, partial,
- reg, excess, argblock, ARGS_SIZE_RTX (arg->offset),
+ TYPE_ALIGN (TREE_TYPE (pval)), partial, reg, excess,
+ argblock, ARGS_SIZE_RTX (arg->offset),
reg_parm_stack_space,
ARGS_SIZE_RTX (arg->alignment_pad));
}