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,
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));
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 *));
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_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
if (rounded_stack_size != 0)
{
if (flag_defer_pop && inhibit_defer_pop == 0
- && !(ecf_flags & ECF_CONST))
+ && !(ecf_flags & (ECF_CONST | ECF_PURE)))
pending_stack_adjust += rounded_stack_size;
else
adjust_stack (rounded_stack_size_rtx);
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;
}
MEM_SET_IN_STRUCT_P (copy, AGGREGATE_TYPE_P (type));
store_expr (args[i].tree_value, copy, 0);
- *ecf_flags &= ~ECF_CONST;
+ *ecf_flags &= ~(ECF_CONST | ECF_PURE);
args[i].tree_value = build1 (ADDR_EXPR,
build_pointer_type (type),
/* If this is an addressable type, we cannot pre-evaluate it. Thus,
we cannot consider this function call constant. */
if (TREE_ADDRESSABLE (type))
- *ecf_flags &= ~ECF_CONST;
+ *ecf_flags &= ~(ECF_CONST | ECF_PURE);
/* Compute the stack-size of this argument. */
if (args[i].reg == 0 || args[i].partial != 0
FLAGS is mask of ECF_* constants.
- MUST_PREALLOCATE indicates that we must preallocate stack space for
- any stack arguments.
-
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 (flags, must_preallocate, num_actuals, args, args_size)
+precompute_arguments (flags, num_actuals, args)
int flags;
- int must_preallocate;
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 ((flags & ECF_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)))
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.
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
/* 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");
}
-
- flags |= flags_from_decl_or_type (fndecl);
+ mark_addressable (fndecl);
}
+
+ flags |= flags_from_decl_or_type (fndecl);
}
/* If we don't have specific function to call, see if we have a
attributes set in the type. */
- if (fndecl == 0)
- flags |= flags_from_decl_or_type (TREE_TYPE (TREE_TYPE (p)));
+ else
+ {
+ p = TREE_OPERAND (exp, 0);
+ flags |= flags_from_decl_or_type (TREE_TYPE (TREE_TYPE (p)));
+ }
#ifdef REG_PARM_STACK_SPACE
#ifdef MAYBE_REG_PARM_STACK_SPACE
if (aggregate_value_p (exp))
{
/* This call returns a big structure. */
- flags &= ~ECF_CONST;
+ flags &= ~(ECF_CONST | ECF_PURE);
#ifdef PCC_STATIC_STRUCT_RETURN
{
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))
{
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
/* 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 (flags & ECF_CONST)
+ if (flags & (ECF_CONST | ECF_PURE))
NO_DEFER_POP;
/* Don't let pending stack adjusts add up to too much.
Also do not make a sibling call. */
- flags &= ~ECF_CONST;
+ 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 (flags, 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 (flags & (ECF_CONST | ECF_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 && ! (flags & ECF_CONST)
+ if (pending_stack_adjust
+ && ! (flags & (ECF_CONST | ECF_PURE))
&& ! inhibit_defer_pop)
{
- int adjust;
- args_size.constant = (unadjusted_args_size
- + ((pending_stack_adjust
- + args_size.constant
- - unadjusted_args_size)
- % (preferred_stack_boundary
- / BITS_PER_UNIT)));
- adjust = (pending_stack_adjust - args_size.constant
- + unadjusted_args_size);
- adjust_stack (GEN_INT (adjust));
- pending_stack_adjust = 0;
+ 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));
- /* 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, flags & ECF_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, flags & ECF_MAY_BE_ALLOCA,
+ store_one_arg (&args[i], argblock, flags,
args_size.var != 0, reg_parm_stack_space);
#ifdef PREFERRED_STACK_BOUNDARY
/* 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,
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) && 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));
/* 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 (flags & ECF_CONST)
+ else if (flags & (ECF_CONST | ECF_PURE))
{
/* Otherwise, just write out the sequence without a note. */
rtx insns = get_insns ();
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
validize_mem (args[i].save_area),
GEN_INT (args[i].size.constant),
PARM_BOUNDARY);
- sibcall_failure = 1;
}
highest_outgoing_arg_in_use = initial_highest_arg_in_use;
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 flags = 0;
#endif
#endif
- if (no_queue)
+ if (fn_type == 1)
flags |= ECF_CONST;
+ else if (fn_type == 2)
+ flags |= ECF_PURE;
fun = orgfun;
if (libfunc_nothrow (fun))
#endif
/* This call returns a big structure. */
- flags &= ~ECF_CONST;
+ 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);
{
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
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,
+ 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. */
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 flag ECF_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. */
/* 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)