/* Convert function calls to rtl insns, for GNU C compiler.
Copyright (C) 1989, 1992, 1993, 1994, 1995, 1996, 1997, 1998,
- 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007
+ 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010
Free Software Foundation, Inc.
This file is part of GCC.
#include "tm.h"
#include "rtl.h"
#include "tree.h"
+#include "gimple.h"
#include "flags.h"
#include "expr.h"
#include "optabs.h"
#include "sbitmap.h"
#include "langhooks.h"
#include "target.h"
+#include "debug.h"
#include "cgraph.h"
#include "except.h"
#include "dbgcnt.h"
+#include "tree-flow.h"
/* Like PREFERRED_STACK_BOUNDARY but in units of bytes, not bits. */
#define STACK_BYTES (PREFERRED_STACK_BOUNDARY / BITS_PER_UNIT)
static void store_unaligned_arguments_into_pseudos (struct arg_data *, int);
static int finalize_must_preallocate (int, int, struct arg_data *,
struct args_size *);
-static void precompute_arguments (int, int, struct arg_data *);
-static int compute_argument_block_size (int, struct args_size *, int);
+static void precompute_arguments (int, struct arg_data *);
+static int compute_argument_block_size (int, struct args_size *, tree, tree, int);
static void initialize_argument_information (int, struct arg_data *,
struct args_size *, int,
tree, tree,
- tree, CUMULATIVE_ARGS *, int,
+ tree, tree, CUMULATIVE_ARGS *, int,
rtx *, int *, int *, int *,
bool *, bool);
static void compute_argument_addresses (struct arg_data *, rtx, int);
CALL_INSN_FUNCTION_USAGE information. */
rtx
-prepare_call_address (rtx funexp, rtx static_chain_value,
+prepare_call_address (tree fndecl, rtx funexp, rtx static_chain_value,
rtx *call_fusage, int reg_parm_seen, int sibcallp)
{
/* Make a valid memory address and copy constants through pseudo-regs,
if (GET_CODE (funexp) != SYMBOL_REF)
/* If we are using registers for parameters, force the
function address into a register now. */
- funexp = ((SMALL_REGISTER_CLASSES && reg_parm_seen)
+ funexp = ((reg_parm_seen
+ && targetm.small_register_classes_for_mode_p (FUNCTION_MODE))
? force_not_mem (memory_address (FUNCTION_MODE, funexp))
: memory_address (FUNCTION_MODE, funexp));
else if (! sibcallp)
if (static_chain_value != 0)
{
+ rtx chain;
+
+ gcc_assert (fndecl);
+ chain = targetm.calls.static_chain (fndecl, false);
static_chain_value = convert_memory_address (Pmode, static_chain_value);
- emit_move_insn (static_chain_rtx, static_chain_value);
- if (REG_P (static_chain_rtx))
- use_reg (call_fusage, static_chain_rtx);
+ emit_move_insn (chain, static_chain_value);
+ if (REG_P (chain))
+ use_reg (call_fusage, chain);
}
return funexp;
denote registers used by the called function. */
static void
-emit_call_1 (rtx funexp, tree fntree, tree fndecl ATTRIBUTE_UNUSED,
+emit_call_1 (rtx funexp, tree fntree ATTRIBUTE_UNUSED, tree fndecl ATTRIBUTE_UNUSED,
tree funtype ATTRIBUTE_UNUSED,
HOST_WIDE_INT stack_size ATTRIBUTE_UNUSED,
HOST_WIDE_INT rounded_stack_size,
rtx call_insn;
int already_popped = 0;
HOST_WIDE_INT n_popped = RETURN_POPS_ARGS (fndecl, funtype, stack_size);
-#if defined (HAVE_call) && defined (HAVE_call_value)
- rtx struct_value_size_rtx;
- struct_value_size_rtx = GEN_INT (struct_value_size);
-#endif
#ifdef CALL_POPS_ARGS
n_popped += CALL_POPS_ARGS (* args_so_far);
else
emit_call_insn (GEN_SIBCALL (gen_rtx_MEM (FUNCTION_MODE, funexp),
rounded_stack_size_rtx, next_arg_reg,
- struct_value_size_rtx));
+ GEN_INT (struct_value_size)));
}
else
#endif
else
emit_call_insn (GEN_CALL (gen_rtx_MEM (FUNCTION_MODE, funexp),
rounded_stack_size_rtx, next_arg_reg,
- struct_value_size_rtx));
+ GEN_INT (struct_value_size)));
}
else
#endif
/* Find the call we just emitted. */
call_insn = last_call_insn ();
- /* 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 there. */
add_function_usage_to (call_insn, call_fusage);
/* If this is a const call, then set the insn's unchanging bit. */
- if (ecf_flags & (ECF_CONST | ECF_PURE))
- CONST_OR_PURE_CALL_P (call_insn) = 1;
-
- /* If this call can't throw, attach a REG_EH_REGION reg note to that
- effect. */
- if (ecf_flags & ECF_NOTHROW)
- REG_NOTES (call_insn) = gen_rtx_EXPR_LIST (REG_EH_REGION, const0_rtx,
- REG_NOTES (call_insn));
- else
- {
- int rn = lookup_stmt_eh_region (fntree);
+ if (ecf_flags & ECF_CONST)
+ RTL_CONST_CALL_P (call_insn) = 1;
- /* If rn < 0, then either (1) tree-ssa not used or (2) doesn't
- throw, which we already took care of. */
- if (rn > 0)
- REG_NOTES (call_insn) = gen_rtx_EXPR_LIST (REG_EH_REGION, GEN_INT (rn),
- REG_NOTES (call_insn));
- }
+ /* If this is a pure call, then set the insn's unchanging bit. */
+ if (ecf_flags & ECF_PURE)
+ RTL_PURE_CALL_P (call_insn) = 1;
+
+ /* If this is a const call, then set the insn's unchanging bit. */
+ if (ecf_flags & ECF_LOOPING_CONST_OR_PURE)
+ RTL_LOOPING_CONST_OR_PURE_CALL_P (call_insn) = 1;
+
+ /* Create a nothrow REG_EH_REGION note, if needed. */
+ make_reg_eh_region_note (call_insn, ecf_flags, 0);
if (ecf_flags & ECF_NORETURN)
- REG_NOTES (call_insn) = gen_rtx_EXPR_LIST (REG_NORETURN, const0_rtx,
- REG_NOTES (call_insn));
+ add_reg_note (call_insn, REG_NORETURN, const0_rtx);
if (ecf_flags & ECF_RETURNS_TWICE)
{
- REG_NOTES (call_insn) = gen_rtx_EXPR_LIST (REG_SETJMP, const0_rtx,
- REG_NOTES (call_insn));
+ add_reg_note (call_insn, REG_SETJMP, const0_rtx);
cfun->calls_setjmp = 1;
}
SIBLING_CALL_P (call_insn) = ((ecf_flags & ECF_SIBCALL) != 0);
+ /* Record debug information for virtual calls. */
+ if (flag_enable_icf_debug && fndecl == NULL)
+ (*debug_hooks->virtual_call_token) (CALL_EXPR_FN (fntree),
+ INSN_UID (call_insn));
+
/* Restore this now, so that we do defer pops for this call's args
if the context of the call as a whole permits. */
inhibit_defer_pop = old_inhibit_defer_pop;
rounded_stack_size -= n_popped;
rounded_stack_size_rtx = GEN_INT (rounded_stack_size);
stack_pointer_delta -= n_popped;
+
+ /* If popup is needed, stack realign must use DRAP */
+ if (SUPPORTS_STACK_ALIGNMENT)
+ crtl->need_drap = true;
}
if (!ACCUMULATE_OUTGOING_ARGS)
&& ! strcmp (name, "__builtin_alloca"))))
flags |= ECF_MAY_BE_ALLOCA;
- /* Disregard prefix _, __ or __x. */
+ /* Disregard prefix _, __, __x or __builtin_. */
if (name[0] == '_')
{
- if (name[1] == '_' && name[2] == 'x')
+ if (name[1] == '_'
+ && name[2] == 'b'
+ && !strncmp (name + 3, "uiltin_", 7))
+ tname += 10;
+ else if (name[1] == '_' && name[2] == 'x')
tname += 3;
else if (name[1] == '_')
tname += 2;
return special_function_p (fndecl, 0) & ECF_RETURNS_TWICE;
}
+
+/* Return true if STMT is an alloca call. */
+
+bool
+gimple_alloca_call_p (const_gimple stmt)
+{
+ tree fndecl;
+
+ if (!is_gimple_call (stmt))
+ return false;
+
+ fndecl = gimple_call_fndecl (stmt);
+ if (fndecl && (special_function_p (fndecl, 0) & ECF_MAY_BE_ALLOCA))
+ return true;
+
+ return false;
+}
+
/* Return true when exp contains alloca call. */
+
bool
alloca_call_p (const_tree exp)
{
flags_from_decl_or_type (const_tree exp)
{
int flags = 0;
- const_tree type = exp;
if (DECL_P (exp))
{
- type = TREE_TYPE (exp);
-
/* The function exp may have the `malloc' attribute. */
if (DECL_IS_MALLOC (exp))
flags |= ECF_MALLOC;
if (DECL_IS_RETURNS_TWICE (exp))
flags |= ECF_RETURNS_TWICE;
- /* The function exp may have the `pure' attribute. */
- if (DECL_IS_PURE (exp))
+ /* Process the pure and const attributes. */
+ if (TREE_READONLY (exp) && ! TREE_THIS_VOLATILE (exp))
+ flags |= ECF_CONST;
+ if (DECL_PURE_P (exp))
flags |= ECF_PURE;
+ if (DECL_LOOPING_CONST_OR_PURE_P (exp))
+ flags |= ECF_LOOPING_CONST_OR_PURE;
if (DECL_IS_NOVOPS (exp))
flags |= ECF_NOVOPS;
if (TREE_NOTHROW (exp))
flags |= ECF_NOTHROW;
- if (TREE_READONLY (exp) && ! TREE_THIS_VOLATILE (exp))
- flags |= ECF_CONST;
-
flags = special_function_p (exp, flags);
}
else if (TYPE_P (exp) && TYPE_READONLY (exp) && ! TREE_THIS_VOLATILE (exp))
|| (GET_CODE (args[i].value) == SUBREG
&& REG_P (SUBREG_REG (args[i].value)))))
&& args[i].mode != BLKmode
- && rtx_cost (args[i].value, SET) > COSTS_N_INSNS (1)
- && ((SMALL_REGISTER_CLASSES && *reg_parm_seen)
+ && rtx_cost (args[i].value, SET, optimize_insn_for_speed_p ())
+ > COSTS_N_INSNS (1)
+ && ((*reg_parm_seen
+ && targetm.small_register_classes_for_mode_p (args[i].mode))
|| optimize))
args[i].value = copy_to_mode_reg (args[i].mode, args[i].value);
}
for (i = 0; i < num_actuals; i++)
if (args[i].reg != 0 && ! args[i].pass_on_stack
&& args[i].mode == BLKmode
- && (TYPE_ALIGN (TREE_TYPE (args[i].tree_value))
+ && MEM_P (args[i].value)
+ && (MEM_ALIGN (args[i].value)
< (unsigned int) MIN (BIGGEST_ALIGNMENT, BITS_PER_WORD)))
{
int bytes = int_size_in_bytes (TREE_TYPE (args[i].tree_value));
}
/* Fill in ARGS_SIZE and ARGS array based on the parameters found in
- CALL_EXPR EXP.
+ CALL_EXPR EXP.
NUM_ACTUALS is the total number of parameters.
struct args_size *args_size,
int n_named_args ATTRIBUTE_UNUSED,
tree exp, tree struct_value_addr_value,
- tree fndecl,
+ tree fndecl, tree fntype,
CUMULATIVE_ARGS *args_so_far,
int reg_parm_stack_space,
rtx *old_stack_level, int *old_pending_adj,
int *must_preallocate, int *ecf_flags,
bool *may_tailcall, bool call_from_thunk_p)
{
+ location_t loc = EXPR_LOCATION (exp);
/* 1 if scanning parms front to back, -1 if scanning back to front. */
int inc;
&& targetm.calls.split_complex_arg (argtype))
{
tree subtype = TREE_TYPE (argtype);
- arg = save_expr (arg);
args[j].tree_value = build1 (REALPART_EXPR, subtype, arg);
j += inc;
args[j].tree_value = build1 (IMAGPART_EXPR, subtype, arg);
if (type == error_mark_node || !COMPLETE_TYPE_P (type))
args[i].tree_value = integer_zero_node, type = integer_type_node;
- /* If TYPE is a transparent union, pass things the way we would
- pass the first field of the union. We have already verified that
- the modes are the same. */
- if (TREE_CODE (type) == UNION_TYPE && TYPE_TRANSPARENT_UNION (type))
- type = TREE_TYPE (TYPE_FIELDS (type));
+ /* If TYPE is a transparent union or record, pass things the way
+ we would pass the first field of the union or record. We have
+ already verified that the modes are the same. */
+ if ((TREE_CODE (type) == UNION_TYPE || TREE_CODE (type) == RECORD_TYPE)
+ && TYPE_TRANSPARENT_AGGR (type))
+ type = TREE_TYPE (first_field (type));
/* Decide where to pass this arg.
|| (callee_copies
&& !TREE_ADDRESSABLE (type)
&& (base = get_base_address (args[i].tree_value))
+ && TREE_CODE (base) != SSA_NAME
&& (!DECL_P (base) || MEM_P (DECL_RTL (base)))))
{
/* We can't use sibcalls if a callee-copied argument is
if (!call_from_thunk_p && DECL_P (base) && !TREE_STATIC (base))
*may_tailcall = false;
- args[i].tree_value = build_fold_addr_expr (args[i].tree_value);
+ args[i].tree_value = build_fold_addr_expr_loc (loc,
+ args[i].tree_value);
type = TREE_TYPE (args[i].tree_value);
- *ecf_flags &= ~(ECF_CONST | ECF_LIBCALL_BLOCK);
+ if (*ecf_flags & ECF_CONST)
+ *ecf_flags &= ~(ECF_CONST | ECF_LOOPING_CONST_OR_PURE);
}
else
{
rtx copy;
if (!COMPLETE_TYPE_P (type)
- || TREE_CODE (TYPE_SIZE (type)) != INTEGER_CST
- || (flag_stack_check && ! STACK_CHECK_BUILTIN
- && (0 < compare_tree_int (TYPE_SIZE_UNIT (type),
- STACK_CHECK_MAX_VAR_SIZE))))
+ || TREE_CODE (TYPE_SIZE_UNIT (type)) != INTEGER_CST
+ || (flag_stack_check == GENERIC_STACK_CHECK
+ && compare_tree_int (TYPE_SIZE_UNIT (type),
+ STACK_CHECK_MAX_VAR_SIZE) > 0))
{
/* This is a variable-sized object. Make space on the stack
for it. */
store_expr (args[i].tree_value, copy, 0, false);
- if (callee_copies)
- *ecf_flags &= ~(ECF_CONST | ECF_LIBCALL_BLOCK);
- else
- *ecf_flags &= ~(ECF_CONST | ECF_PURE | ECF_LIBCALL_BLOCK);
+ /* Just change the const function to pure and then let
+ the next test clear the pure based on
+ callee_copies. */
+ if (*ecf_flags & ECF_CONST)
+ {
+ *ecf_flags &= ~ECF_CONST;
+ *ecf_flags |= ECF_PURE;
+ }
+
+ if (!callee_copies && *ecf_flags & ECF_PURE)
+ *ecf_flags &= ~(ECF_PURE | ECF_LOOPING_CONST_OR_PURE);
args[i].tree_value
- = build_fold_addr_expr (make_tree (type, copy));
+ = build_fold_addr_expr_loc (loc, make_tree (type, copy));
type = TREE_TYPE (args[i].tree_value);
*may_tailcall = false;
}
}
- mode = TYPE_MODE (type);
unsignedp = TYPE_UNSIGNED (type);
-
- if (targetm.calls.promote_function_args (fndecl ? TREE_TYPE (fndecl) : 0))
- mode = promote_mode (type, mode, &unsignedp, 1);
+ mode = promote_function_mode (type, TYPE_MODE (type), &unsignedp,
+ fndecl ? TREE_TYPE (fndecl) : fntype, 0);
args[i].unsignedp = unsignedp;
args[i].mode = mode;
|| (args[i].pass_on_stack && args[i].reg != 0))
*must_preallocate = 1;
- /* 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_LIBCALL_BLOCK;
-
/* Compute the stack-size of this argument. */
if (args[i].reg == 0 || args[i].partial != 0
|| reg_parm_stack_space > 0
static int
compute_argument_block_size (int reg_parm_stack_space,
struct args_size *args_size,
+ tree fndecl ATTRIBUTE_UNUSED,
+ tree fntype ATTRIBUTE_UNUSED,
int preferred_stack_boundary ATTRIBUTE_UNUSED)
{
int unadjusted_args_size = args_size->constant;
/* The area corresponding to register parameters is not to count in
the size of the block we need. So make the adjustment. */
- if (!OUTGOING_REG_PARM_STACK_SPACE)
+ if (! OUTGOING_REG_PARM_STACK_SPACE ((!fndecl ? fntype : TREE_TYPE (fndecl))))
args_size->var
= size_binop (MINUS_EXPR, args_size->var,
ssize_int (reg_parm_stack_space));
args_size->constant = MAX (args_size->constant,
reg_parm_stack_space);
- if (!OUTGOING_REG_PARM_STACK_SPACE)
+ if (! OUTGOING_REG_PARM_STACK_SPACE ((!fndecl ? fntype : TREE_TYPE (fndecl))))
args_size->constant -= reg_parm_stack_space;
}
return unadjusted_args_size;
precomputed argument. */
static void
-precompute_arguments (int flags, int num_actuals, struct arg_data *args)
+precompute_arguments (int num_actuals, struct arg_data *args)
{
int i;
which have already been stored into the stack. (we have code to avoid
such case by saving the outgoing stack arguments, but it results in
worse code) */
- if ((flags & ECF_LIBCALL_BLOCK) == 0 && !ACCUMULATE_OUTGOING_ARGS)
+ if (!ACCUMULATE_OUTGOING_ARGS)
return;
for (i = 0; i < num_actuals; i++)
{
+ tree type;
enum machine_mode mode;
- if ((flags & ECF_LIBCALL_BLOCK) == 0
- && TREE_CODE (args[i].tree_value) != CALL_EXPR)
+ if (TREE_CODE (args[i].tree_value) != CALL_EXPR)
continue;
/* If this is an addressable type, we cannot pre-evaluate it. */
- gcc_assert (!TREE_ADDRESSABLE (TREE_TYPE (args[i].tree_value)));
+ type = TREE_TYPE (args[i].tree_value);
+ gcc_assert (!TREE_ADDRESSABLE (type));
args[i].initial_value = args[i].value
= expand_normal (args[i].tree_value);
- mode = TYPE_MODE (TREE_TYPE (args[i].tree_value));
+ mode = TYPE_MODE (type);
if (mode != args[i].mode)
{
+ int unsignedp = args[i].unsignedp;
args[i].value
= convert_modes (args[i].mode, mode,
args[i].value, args[i].unsignedp);
-#if defined(PROMOTE_FUNCTION_MODE) && !defined(PROMOTE_MODE)
+
/* CSE will replace this only if it contains args[i].value
pseudo, so convert it down to the declared mode using
a SUBREG. */
if (REG_P (args[i].value)
- && GET_MODE_CLASS (args[i].mode) == MODE_INT)
+ && GET_MODE_CLASS (args[i].mode) == MODE_INT
+ && promote_mode (type, mode, &unsignedp) != args[i].mode)
{
args[i].initial_value
= gen_lowpart_SUBREG (mode, args[i].value);
SUBREG_PROMOTED_UNSIGNED_SET (args[i].initial_value,
args[i].unsignedp);
}
-#endif
}
}
}
compute and return the final value for MUST_PREALLOCATE. */
static int
-finalize_must_preallocate (int must_preallocate, int num_actuals,
+finalize_must_preallocate (int must_preallocate, int num_actuals,
struct arg_data *args, struct args_size *args_size)
{
/* See if we have or want to preallocate stack space.
&& args[i].partial == 0)
continue;
- if (GET_CODE (offset) == CONST_INT)
+ if (CONST_INT_P (offset))
addr = plus_constant (arg_reg, INTVAL (offset));
else
addr = gen_rtx_PLUS (Pmode, arg_reg, offset);
boundary = args[i].locate.boundary;
if (args[i].locate.where_pad != downward)
align = boundary;
- else if (GET_CODE (offset) == CONST_INT)
+ else if (CONST_INT_P (offset))
{
align = INTVAL (offset) * BITS_PER_UNIT | boundary;
align = align & -align;
}
set_mem_align (args[i].stack, align);
- if (GET_CODE (slot_offset) == CONST_INT)
+ if (CONST_INT_P (slot_offset))
addr = plus_constant (arg_reg, INTVAL (slot_offset));
else
addr = gen_rtx_PLUS (Pmode, arg_reg, slot_offset);
i = 0;
else if (GET_CODE (addr) == PLUS
&& XEXP (addr, 0) == crtl->args.internal_arg_pointer
- && GET_CODE (XEXP (addr, 1)) == CONST_INT)
+ && CONST_INT_P (XEXP (addr, 1)))
i = INTVAL (XEXP (addr, 1));
/* Return true for arg pointer based indexed addressing. */
else if (GET_CODE (addr) == PLUS
static rtx
avoid_likely_spilled_reg (rtx x)
{
- rtx new;
+ rtx new_rtx;
if (REG_P (x)
&& HARD_REGISTER_P (x)
and the whole point of this function is to avoid
using the hard register directly in such a situation. */
generating_concat_p = 0;
- new = gen_reg_rtx (GET_MODE (x));
+ new_rtx = gen_reg_rtx (GET_MODE (x));
generating_concat_p = 1;
- emit_move_insn (new, x);
- return new;
+ emit_move_insn (new_rtx, x);
+ return new_rtx;
}
return x;
}
/* Data type of the function. */
tree funtype;
tree type_arg_types;
+ tree rettype;
/* Declaration of the function being called,
or 0 if the function is computed (not known by name). */
tree fndecl = 0;
int old_stack_pointer_delta = 0;
rtx call_fusage;
- tree p = CALL_EXPR_FN (exp);
tree addr = CALL_EXPR_FN (exp);
int i;
/* The alignment of the stack, in bits. */
}
else
{
- fntype = TREE_TYPE (TREE_TYPE (p));
+ fntype = TREE_TYPE (TREE_TYPE (addr));
flags |= flags_from_decl_or_type (fntype);
}
+ rettype = TREE_TYPE (exp);
struct_value = targetm.calls.struct_value_rtx (fntype, 0);
/* Warn if this value is an aggregate type,
regardless of which calling convention we are using for it. */
- if (AGGREGATE_TYPE_P (TREE_TYPE (exp)))
+ if (AGGREGATE_TYPE_P (rettype))
warning (OPT_Waggregate_return, "function call has aggregate value");
- /* If the result of a pure or const function call is ignored (or void),
- and none of its arguments are volatile, we can avoid expanding the
- call and just evaluate the arguments for side-effects. */
+ /* If the result of a non looping pure or const function call is
+ ignored (or void), and none of its arguments are volatile, we can
+ avoid expanding the call and just evaluate the arguments for
+ side-effects. */
if ((flags & (ECF_CONST | ECF_PURE))
+ && (!(flags & ECF_LOOPING_CONST_OR_PURE))
&& (ignore || target == const0_rtx
- || TYPE_MODE (TREE_TYPE (exp)) == VOIDmode))
+ || TYPE_MODE (rettype) == VOIDmode))
{
bool volatilep = false;
tree arg;
}
#ifdef REG_PARM_STACK_SPACE
- reg_parm_stack_space = REG_PARM_STACK_SPACE (fndecl);
+ reg_parm_stack_space = REG_PARM_STACK_SPACE (!fndecl ? fntype : fndecl);
#endif
- if (!OUTGOING_REG_PARM_STACK_SPACE && reg_parm_stack_space > 0 && PUSH_ARGS)
+ if (! OUTGOING_REG_PARM_STACK_SPACE ((!fndecl ? fntype : TREE_TYPE (fndecl)))
+ && reg_parm_stack_space > 0 && PUSH_ARGS)
must_preallocate = 1;
/* Set up a place to return a structure. */
/* Cater to broken compilers. */
- if (aggregate_value_p (exp, fndecl))
+ if (aggregate_value_p (exp, fntype))
{
/* This call returns a big structure. */
- flags &= ~(ECF_CONST | ECF_PURE | ECF_LIBCALL_BLOCK);
+ flags &= ~(ECF_CONST | ECF_PURE | ECF_LOOPING_CONST_OR_PURE);
#ifdef PCC_STATIC_STRUCT_RETURN
{
}
#else /* not PCC_STATIC_STRUCT_RETURN */
{
- struct_value_size = int_size_in_bytes (TREE_TYPE (exp));
+ struct_value_size = int_size_in_bytes (rettype);
if (target && MEM_P (target) && CALL_EXPR_RETURN_SLOT_OPT (exp))
structure_value_addr = XEXP (target, 0);
/* For variable-sized objects, we must be called with a target
specified. If we were to allocate space on the stack here,
we would have no way of knowing when to free it. */
- rtx d = assign_temp (TREE_TYPE (exp), 0, 1, 1);
+ rtx d = assign_temp (rettype, 0, 1, 1);
mark_temp_addr_taken (d);
structure_value_addr = XEXP (d, 0);
if (fndecl)
{
struct cgraph_rtl_info *i = cgraph_rtl_info (fndecl);
- if (i && i->preferred_incoming_stack_boundary)
+ /* Without automatic stack alignment, we can't increase preferred
+ stack boundary. With automatic stack alignment, it is
+ unnecessary since unless we can guarantee that all callers will
+ align the outgoing stack properly, callee has to align its
+ stack anyway. */
+ if (i
+ && i->preferred_incoming_stack_boundary
+ && i->preferred_incoming_stack_boundary < preferred_stack_boundary)
preferred_stack_boundary = i->preferred_incoming_stack_boundary;
}
n_named_args = num_actuals;
/* Make a vector to hold all the information about each arg. */
- args = alloca (num_actuals * sizeof (struct arg_data));
+ args = XALLOCAVEC (struct arg_data, num_actuals);
memset (args, 0, num_actuals * sizeof (struct arg_data));
/* Build up entries in the ARGS array, compute the size of the
arguments into ARGS_SIZE, etc. */
initialize_argument_information (num_actuals, args, &args_size,
n_named_args, exp,
- structure_value_addr_value, fndecl,
+ structure_value_addr_value, fndecl, fntype,
&args_so_far, reg_parm_stack_space,
&old_stack_level, &old_pending_adj,
&must_preallocate, &flags,
&try_tail_call, CALL_FROM_THUNK_P (exp));
if (args_size.var)
- {
- /* If this function requires a variable-sized argument list, don't
- try to make a cse'able block for this call. We may be able to
- do this eventually, but it is too complicated to keep track of
- what insns go in the cse'able block and which don't. */
-
- flags &= ~ECF_LIBCALL_BLOCK;
- must_preallocate = 1;
- }
+ must_preallocate = 1;
/* Now make final decision about preallocating stack space. */
must_preallocate = finalize_must_preallocate (must_preallocate,
if (currently_expanding_call++ != 0
|| !flag_optimize_sibling_calls
|| args_size.var
- || lookup_stmt_eh_region (exp) >= 0
|| dbg_cnt (tail_call) == false)
try_tail_call = 0;
It does not seem worth the effort since few optimizable
sibling calls will return a structure. */
|| structure_value_addr != NULL_RTX
+#ifdef REG_PARM_STACK_SPACE
+ /* If outgoing reg parm stack space changes, we can not do sibcall. */
+ || (OUTGOING_REG_PARM_STACK_SPACE (funtype)
+ != OUTGOING_REG_PARM_STACK_SPACE (TREE_TYPE (current_function_decl)))
+ || (reg_parm_stack_space != REG_PARM_STACK_SPACE (fndecl))
+#endif
/* Check whether the target is able to optimize the call
into a sibcall. */
|| !targetm.function_ok_for_sibcall (fndecl, exp)
|| !lang_hooks.decls.ok_for_sibcall (fndecl))
try_tail_call = 0;
+ /* Check if caller and callee disagree in promotion of function
+ return value. */
+ if (try_tail_call)
+ {
+ enum machine_mode caller_mode, caller_promoted_mode;
+ enum machine_mode callee_mode, callee_promoted_mode;
+ int caller_unsignedp, callee_unsignedp;
+ tree caller_res = DECL_RESULT (current_function_decl);
+
+ caller_unsignedp = TYPE_UNSIGNED (TREE_TYPE (caller_res));
+ caller_mode = DECL_MODE (caller_res);
+ callee_unsignedp = TYPE_UNSIGNED (TREE_TYPE (funtype));
+ callee_mode = TYPE_MODE (TREE_TYPE (funtype));
+ caller_promoted_mode
+ = promote_function_mode (TREE_TYPE (caller_res), caller_mode,
+ &caller_unsignedp,
+ TREE_TYPE (current_function_decl), 1);
+ callee_promoted_mode
+ = promote_function_mode (TREE_TYPE (funtype), callee_mode,
+ &callee_unsignedp,
+ funtype, 1);
+ if (caller_mode != VOIDmode
+ && (caller_promoted_mode != callee_promoted_mode
+ || ((caller_mode != caller_promoted_mode
+ || callee_mode != callee_promoted_mode)
+ && (caller_unsignedp != callee_unsignedp
+ || GET_MODE_BITSIZE (caller_mode)
+ < GET_MODE_BITSIZE (callee_mode)))))
+ try_tail_call = 0;
+ }
+
/* Ensure current function's preferred stack boundary is at least
- what we need. We don't have to increase alignment for recursive
- functions. */
- if (crtl->preferred_stack_boundary < preferred_stack_boundary
- && fndecl != current_function_decl)
+ what we need. Stack alignment may also increase preferred stack
+ boundary. */
+ if (crtl->preferred_stack_boundary < preferred_stack_boundary)
crtl->preferred_stack_boundary = preferred_stack_boundary;
+ else
+ preferred_stack_boundary = crtl->preferred_stack_boundary;
preferred_unit_stack_boundary = preferred_stack_boundary / BITS_PER_UNIT;
|| pass == 0)
do_pending_stack_adjust ();
- /* 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 (pass && (flags & ECF_LIBCALL_BLOCK))
- NO_DEFER_POP;
-
/* Precompute any arguments as needed. */
if (pass)
- precompute_arguments (flags, num_actuals, args);
+ precompute_arguments (num_actuals, args);
/* Now we are about to start emitting insns that can be deleted
if a libcall is deleted. */
- if (pass && (flags & (ECF_LIBCALL_BLOCK | ECF_MALLOC)))
+ if (pass && (flags & ECF_MALLOC))
start_sequence ();
if (pass == 0 && crtl->stack_protect_guard)
unadjusted_args_size
= compute_argument_block_size (reg_parm_stack_space,
&adjusted_args_size,
+ fndecl, fntype,
(pass == 0 ? 0
: preferred_stack_boundary));
incoming argument block. */
if (pass == 0)
{
- argblock = virtual_incoming_args_rtx;
+ argblock = crtl->args.internal_arg_pointer;
argblock
#ifdef STACK_GROWS_DOWNWARD
= plus_constant (argblock, crtl->args.pretend_args_size);
/* 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. */
- if (!OUTGOING_REG_PARM_STACK_SPACE)
+ if (! OUTGOING_REG_PARM_STACK_SPACE ((!fndecl ? fntype : TREE_TYPE (fndecl))))
needed += reg_parm_stack_space;
#ifdef ARGS_GROW_DOWNWARD
{
rtx push_size
= GEN_INT (adjusted_args_size.constant
- + (OUTGOING_REG_PARM_STACK_SPACE ? 0
+ + (OUTGOING_REG_PARM_STACK_SPACE ((!fndecl ? fntype
+ : TREE_TYPE (fndecl))) ? 0
: reg_parm_stack_space));
if (old_stack_level == 0)
{
/* When the stack adjustment is pending, we get better code
by combining the adjustments. */
if (pending_stack_adjust
- && ! (flags & ECF_LIBCALL_BLOCK)
&& ! inhibit_defer_pop)
{
pending_stack_adjust
/* Figure out the register where the value, if any, will come back. */
valreg = 0;
- if (TYPE_MODE (TREE_TYPE (exp)) != VOIDmode
+ if (TYPE_MODE (rettype) != VOIDmode
&& ! structure_value_addr)
{
if (pcc_struct_value)
- valreg = hard_function_value (build_pointer_type (TREE_TYPE (exp)),
+ valreg = hard_function_value (build_pointer_type (rettype),
fndecl, NULL, (pass == 0));
else
- valreg = hard_function_value (TREE_TYPE (exp), fndecl, fntype,
+ valreg = hard_function_value (rettype, fndecl, fntype,
(pass == 0));
/* If VALREG is a PARALLEL whose first member has a zero
but we do preallocate space here if they want that. */
for (i = 0; i < num_actuals; i++)
- if (args[i].reg == 0 || args[i].pass_on_stack)
- {
- rtx before_arg = get_last_insn ();
-
- if (store_one_arg (&args[i], argblock, flags,
- adjusted_args_size.var != 0,
- reg_parm_stack_space)
- || (pass == 0
- && check_sibcall_argument_overlap (before_arg,
- &args[i], 1)))
- sibcall_failure = 1;
-
- if (flags & ECF_CONST
- && args[i].stack
- && args[i].value == args[i].stack)
- call_fusage = gen_rtx_EXPR_LIST (VOIDmode,
- gen_rtx_USE (VOIDmode,
- args[i].value),
- call_fusage);
- }
+ {
+ if (args[i].reg == 0 || args[i].pass_on_stack)
+ {
+ rtx before_arg = get_last_insn ();
+
+ if (store_one_arg (&args[i], argblock, flags,
+ adjusted_args_size.var != 0,
+ reg_parm_stack_space)
+ || (pass == 0
+ && check_sibcall_argument_overlap (before_arg,
+ &args[i], 1)))
+ sibcall_failure = 1;
+ }
+
+ if (((flags & ECF_CONST)
+ || ((flags & ECF_PURE) && ACCUMULATE_OUTGOING_ARGS))
+ && args[i].stack)
+ call_fusage = gen_rtx_EXPR_LIST (VOIDmode,
+ gen_rtx_USE (VOIDmode,
+ args[i].stack),
+ call_fusage);
+ }
/* If we have a parm that is passed in registers but not in memory
and whose alignment does not permit a direct copy into registers,
/* If register arguments require space on the stack and stack space
was not preallocated, allocate stack space here for arguments
passed in registers. */
- if (OUTGOING_REG_PARM_STACK_SPACE && !ACCUMULATE_OUTGOING_ARGS
+ if (OUTGOING_REG_PARM_STACK_SPACE ((!fndecl ? fntype : TREE_TYPE (fndecl)))
+ && !ACCUMULATE_OUTGOING_ARGS
&& must_preallocate == 0 && reg_parm_stack_space > 0)
anti_adjust_stack (GEN_INT (reg_parm_stack_space));
}
after_args = get_last_insn ();
- funexp = prepare_call_address (funexp, static_chain_value,
+ funexp = prepare_call_address (fndecl, funexp, static_chain_value,
&call_fusage, reg_parm_seen, pass == 0);
load_register_parameters (args, num_actuals, &call_fusage, flags,
group load/store machinery below. */
if (!structure_value_addr
&& !pcc_struct_value
- && TYPE_MODE (TREE_TYPE (exp)) != BLKmode
- && targetm.calls.return_in_msb (TREE_TYPE (exp)))
+ && TYPE_MODE (rettype) != BLKmode
+ && targetm.calls.return_in_msb (rettype))
{
- if (shift_return_value (TYPE_MODE (TREE_TYPE (exp)), false, valreg))
+ if (shift_return_value (TYPE_MODE (rettype), false, valreg))
sibcall_failure = 1;
- valreg = gen_rtx_REG (TYPE_MODE (TREE_TYPE (exp)), REGNO (valreg));
+ valreg = gen_rtx_REG (TYPE_MODE (rettype), REGNO (valreg));
}
- /* 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 (pass && (flags & ECF_LIBCALL_BLOCK))
- {
- rtx insns;
- rtx insn;
- bool failed = valreg == 0 || GET_CODE (valreg) == PARALLEL;
-
- insns = get_insns ();
-
- /* Expansion of block moves possibly introduced a loop that may
- not appear inside libcall block. */
- for (insn = insns; insn; insn = NEXT_INSN (insn))
- if (JUMP_P (insn))
- failed = true;
-
- if (failed)
- {
- end_sequence ();
- emit_insn (insns);
- }
- else
- {
- 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)
- mark_reg_pointer (temp,
- TYPE_ALIGN (TREE_TYPE (TREE_TYPE (exp))));
-
- end_sequence ();
- if (flag_unsafe_math_optimizations
- && fndecl
- && DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_NORMAL
- && (DECL_FUNCTION_CODE (fndecl) == BUILT_IN_SQRT
- || DECL_FUNCTION_CODE (fndecl) == BUILT_IN_SQRTF
- || DECL_FUNCTION_CODE (fndecl) == BUILT_IN_SQRTL))
- note = gen_rtx_fmt_e (SQRT,
- GET_MODE (temp),
- args[0].initial_value);
- else
- {
- /* Construct an "equal form" for the value which
- mentions all the arguments in order as well as
- the function name. */
- 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);
-
- 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 (pass && (flags & ECF_MALLOC))
+ if (pass && (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)
+ if (TREE_CODE (rettype) == POINTER_TYPE)
mark_reg_pointer (temp, BIGGEST_ALIGNMENT);
emit_move_insn (temp, valreg);
/* The return value from a malloc-like function can not alias
anything else. */
last = get_last_insn ();
- REG_NOTES (last) =
- gen_rtx_EXPR_LIST (REG_NOALIAS, temp, REG_NOTES (last));
+ add_reg_note (last, REG_NOALIAS, temp);
/* Write out the sequence. */
insns = get_insns ();
/* If value type not void, return an rtx for the value. */
- if (TYPE_MODE (TREE_TYPE (exp)) == VOIDmode
+ if (TYPE_MODE (rettype) == VOIDmode
|| ignore)
target = const0_rtx;
else if (structure_value_addr)
if (target == 0 || !MEM_P (target))
{
target
- = gen_rtx_MEM (TYPE_MODE (TREE_TYPE (exp)),
- memory_address (TYPE_MODE (TREE_TYPE (exp)),
+ = gen_rtx_MEM (TYPE_MODE (rettype),
+ memory_address (TYPE_MODE (rettype),
structure_value_addr));
- set_mem_attributes (target, exp, 1);
+ set_mem_attributes (target, rettype, 1);
}
}
else if (pcc_struct_value)
/* This is the special C++ case where we need to
know what the true target was. We take care to
never use this value more than once in one expression. */
- target = gen_rtx_MEM (TYPE_MODE (TREE_TYPE (exp)),
+ target = gen_rtx_MEM (TYPE_MODE (rettype),
copy_to_reg (valreg));
- set_mem_attributes (target, exp, 1);
+ set_mem_attributes (target, rettype, 1);
}
/* Handle calls that return values in multiple non-contiguous locations.
The Irix 6 ABI has examples of this. */
if (target == 0)
{
/* This will only be assigned once, so it can be readonly. */
- tree nt = build_qualified_type (TREE_TYPE (exp),
- (TYPE_QUALS (TREE_TYPE (exp))
+ tree nt = build_qualified_type (rettype,
+ (TYPE_QUALS (rettype)
| TYPE_QUAL_CONST));
target = assign_temp (nt, 0, 1, 1);
}
if (! rtx_equal_p (target, valreg))
- emit_group_store (target, valreg, TREE_TYPE (exp),
- int_size_in_bytes (TREE_TYPE (exp)));
+ emit_group_store (target, valreg, rettype,
+ int_size_in_bytes (rettype));
/* We can not support sibling calls for this case. */
sibcall_failure = 1;
}
else if (target
- && GET_MODE (target) == TYPE_MODE (TREE_TYPE (exp))
+ && GET_MODE (target) == TYPE_MODE (rettype)
&& GET_MODE (target) == GET_MODE (valreg))
{
bool may_overlap = false;
sibcall_failure = 1;
}
}
- else if (TYPE_MODE (TREE_TYPE (exp)) == BLKmode)
+ else if (TYPE_MODE (rettype) == BLKmode)
{
- target = copy_blkmode_from_reg (target, valreg, TREE_TYPE (exp));
+ rtx val = valreg;
+ if (GET_MODE (val) != BLKmode)
+ val = avoid_likely_spilled_reg (val);
+ target = copy_blkmode_from_reg (target, val, rettype);
/* We can not support sibling calls for this case. */
sibcall_failure = 1;
else
target = copy_to_reg (avoid_likely_spilled_reg (valreg));
- if (targetm.calls.promote_function_return(funtype))
+ /* If we promoted this return value, make the proper SUBREG.
+ TARGET might be const0_rtx here, so be careful. */
+ if (REG_P (target)
+ && TYPE_MODE (rettype) != BLKmode
+ && GET_MODE (target) != TYPE_MODE (rettype))
{
- /* If we promoted this return value, make the proper SUBREG.
- TARGET might be const0_rtx here, so be careful. */
- if (REG_P (target)
- && TYPE_MODE (TREE_TYPE (exp)) != BLKmode
- && GET_MODE (target) != TYPE_MODE (TREE_TYPE (exp)))
+ tree type = rettype;
+ int unsignedp = TYPE_UNSIGNED (type);
+ int offset = 0;
+ enum machine_mode pmode;
+
+ /* Ensure we promote as expected, and get the new unsignedness. */
+ pmode = promote_function_mode (type, TYPE_MODE (type), &unsignedp,
+ funtype, 1);
+ gcc_assert (GET_MODE (target) == pmode);
+
+ if ((WORDS_BIG_ENDIAN || BYTES_BIG_ENDIAN)
+ && (GET_MODE_SIZE (GET_MODE (target))
+ > GET_MODE_SIZE (TYPE_MODE (type))))
{
- tree type = TREE_TYPE (exp);
- int unsignedp = TYPE_UNSIGNED (type);
- int offset = 0;
- enum machine_mode pmode;
-
- pmode = promote_mode (type, TYPE_MODE (type), &unsignedp, 1);
- /* If we don't promote as expected, something is wrong. */
- gcc_assert (GET_MODE (target) == pmode);
-
- if ((WORDS_BIG_ENDIAN || BYTES_BIG_ENDIAN)
- && (GET_MODE_SIZE (GET_MODE (target))
- > GET_MODE_SIZE (TYPE_MODE (type))))
- {
- offset = GET_MODE_SIZE (GET_MODE (target))
- - GET_MODE_SIZE (TYPE_MODE (type));
- if (! BYTES_BIG_ENDIAN)
- offset = (offset / UNITS_PER_WORD) * UNITS_PER_WORD;
- else if (! WORDS_BIG_ENDIAN)
- offset %= UNITS_PER_WORD;
- }
- target = gen_rtx_SUBREG (TYPE_MODE (type), target, offset);
- SUBREG_PROMOTED_VAR_P (target) = 1;
- SUBREG_PROMOTED_UNSIGNED_SET (target, unsignedp);
+ offset = GET_MODE_SIZE (GET_MODE (target))
+ - GET_MODE_SIZE (TYPE_MODE (type));
+ if (! BYTES_BIG_ENDIAN)
+ offset = (offset / UNITS_PER_WORD) * UNITS_PER_WORD;
+ else if (! WORDS_BIG_ENDIAN)
+ offset %= UNITS_PER_WORD;
}
+
+ target = gen_rtx_SUBREG (TYPE_MODE (type), target, offset);
+ SUBREG_PROMOTED_VAR_P (target) = 1;
+ SUBREG_PROMOTED_UNSIGNED_SET (target, unsignedp);
}
/* If size of args is variable or this was a constructor call for a stack
struct args_size original_args_size;
int argnum;
rtx fun;
+ /* Todo, choose the correct decl type of orgfun. Sadly this information
+ isn't present here, so we default to native calling abi here. */
+ tree fndecl ATTRIBUTE_UNUSED = NULL_TREE; /* library calls default to host calling abi ? */
+ tree fntype ATTRIBUTE_UNUSED = NULL_TREE; /* library calls default to host calling abi ? */
int inc;
int count;
rtx argblock = 0;
#ifdef REG_PARM_STACK_SPACE
/* Define the boundary of the register parm stack space that needs to be
save, if any. */
- int low_to_save, high_to_save;
+ int low_to_save = 0, high_to_save = 0;
rtx save_area = 0; /* Place that it is saved. */
#endif
case LCT_PURE:
flags |= ECF_PURE;
break;
- case LCT_CONST_MAKE_BLOCK:
- flags |= ECF_CONST | ECF_LIBCALL_BLOCK;
- break;
- case LCT_PURE_MAKE_BLOCK:
- flags |= ECF_PURE | ECF_LIBCALL_BLOCK;
- break;
case LCT_NORETURN:
flags |= ECF_NORETURN;
break;
mem_value = assign_temp (tfom, 0, 1, 1);
#endif
/* This call returns a big structure. */
- flags &= ~(ECF_CONST | ECF_PURE | ECF_LIBCALL_BLOCK);
+ flags &= ~(ECF_CONST | ECF_PURE | ECF_LOOPING_CONST_OR_PURE);
}
}
else
of the full argument passing conventions to limit complexity here since
library functions shouldn't have many args. */
- argvec = alloca ((nargs + 1) * sizeof (struct arg));
+ argvec = XALLOCAVEC (struct arg, nargs + 1);
memset (argvec, 0, (nargs + 1) * sizeof (struct arg));
#ifdef INIT_CUMULATIVE_LIBCALL_ARGS
count = 0;
- /* Now we are about to start emitting insns that can be deleted
- if a libcall is deleted. */
- if (flags & ECF_LIBCALL_BLOCK)
- start_sequence ();
-
push_temp_slots ();
/* If there's a structure value address to be passed,
for (; count < nargs; count++)
{
rtx val = va_arg (p, rtx);
- enum machine_mode mode = va_arg (p, enum machine_mode);
+ enum machine_mode mode = (enum machine_mode) va_arg (p, int);
/* We cannot convert the arg value to the mode the library wants here;
must do it earlier where we know the signedness of the arg. */
int must_copy
= !reference_callee_copied (&args_so_far, mode, NULL_TREE, 1);
- /* loop.c won't look at CALL_INSN_FUNCTION_USAGE of const/pure
- functions, so we have to pretend this isn't such a function. */
- if (flags & ECF_LIBCALL_BLOCK)
- {
- rtx insns = get_insns ();
- end_sequence ();
- emit_insn (insns);
- }
- flags &= ~(ECF_CONST | ECF_PURE | ECF_LIBCALL_BLOCK);
-
- /* If this was a CONST function, it is now PURE since
- it now reads memory. */
+ /* If this was a CONST function, it is now PURE since it now
+ reads memory. */
if (flags & ECF_CONST)
{
flags &= ~ECF_CONST;
flags |= ECF_PURE;
}
- if (GET_MODE (val) == MEM && !must_copy)
+ if (MEM_P (val) && !must_copy)
slot = val;
else
{
args_size.constant = MAX (args_size.constant,
reg_parm_stack_space);
- if (!OUTGOING_REG_PARM_STACK_SPACE)
+ if (! OUTGOING_REG_PARM_STACK_SPACE ((!fndecl ? fntype : TREE_TYPE (fndecl))))
args_size.constant -= reg_parm_stack_space;
if (args_size.constant > crtl->outgoing_args_size)
/* 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. */
- if (!OUTGOING_REG_PARM_STACK_SPACE)
+ if (! OUTGOING_REG_PARM_STACK_SPACE ((!fndecl ? fntype : TREE_TYPE (fndecl))))
needed += reg_parm_stack_space;
#ifdef ARGS_GROW_DOWNWARD
rtx val = argvec[argnum].value;
rtx reg = argvec[argnum].reg;
int partial = argvec[argnum].partial;
+ unsigned int parm_align = argvec[argnum].locate.boundary;
int lower_bound = 0, upper_bound = 0, i;
if (! (reg != 0 && partial == 0))
#ifdef ARGS_GROW_DOWNWARD
/* stack_slot is negative, but we want to index stack_usage_map
with positive values. */
- upper_bound = -argvec[argnum].locate.offset.constant + 1;
+ upper_bound = -argvec[argnum].locate.slot_offset.constant + 1;
lower_bound = upper_bound - argvec[argnum].locate.size.constant;
#else
- lower_bound = argvec[argnum].locate.offset.constant;
+ lower_bound = argvec[argnum].locate.slot_offset.constant;
upper_bound = lower_bound + argvec[argnum].locate.size.constant;
#endif
}
}
- emit_push_insn (val, mode, NULL_TREE, NULL_RTX, PARM_BOUNDARY,
+ emit_push_insn (val, mode, NULL_TREE, NULL_RTX, parm_align,
partial, reg, 0, argblock,
GEN_INT (argvec[argnum].locate.offset.constant),
reg_parm_stack_space,
NO_DEFER_POP;
- if (flags & ECF_CONST)
+ if ((flags & ECF_CONST)
+ || ((flags & ECF_PURE) && ACCUMULATE_OUTGOING_ARGS))
{
rtx use;
else
argnum = 0;
- fun = prepare_call_address (fun, NULL, &call_fusage, 0, 0);
+ fun = prepare_call_address (NULL, fun, NULL, &call_fusage, 0, 0);
/* Now load any reg parms into their regs. */
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);
+ ? hard_libcall_value (outmode, orgfun) : NULL_RTX);
/* Stack must be properly aligned now. */
gcc_assert (!(stack_pointer_delta
/* 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_LIBCALL_BLOCK)
- {
- rtx insns;
-
- if (valreg == 0)
- {
- insns = get_insns ();
- end_sequence ();
- emit_insn (insns);
- }
- else
- {
- rtx note = 0;
- rtx temp;
- int i;
-
- if (GET_CODE (valreg) == PARALLEL)
- {
- temp = gen_reg_rtx (outmode);
- emit_group_store (temp, valreg, NULL_TREE,
- GET_MODE_SIZE (outmode));
- valreg = temp;
- }
-
- temp = gen_reg_rtx (GET_MODE (valreg));
-
- /* Construct an "equal form" for the value which mentions all the
- arguments in order as well as the function name. */
- for (i = 0; i < nargs; i++)
- 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;
- }
- }
pop_temp_slots ();
/* Copy the value to the right place. */
}
else
{
- /* Convert to the proper mode if PROMOTE_MODE has been active. */
+ /* Convert to the proper mode if a promotion has been active. */
if (GET_MODE (valreg) != outmode)
{
int unsignedp = TYPE_UNSIGNED (tfom);
- gcc_assert (targetm.calls.promote_function_return (tfom));
- gcc_assert (promote_mode (tfom, outmode, &unsignedp, 0)
+ gcc_assert (promote_function_mode (tfom, outmode, &unsignedp,
+ fndecl ? TREE_TYPE (fndecl) : fntype, 1)
== GET_MODE (valreg));
-
valreg = convert_modes (outmode, GET_MODE (valreg), valreg, 0);
}
with NARGS different arguments, passed as alternating rtx values
and machine_modes to convert them to.
- FN_TYPE should be LCT_NORMAL for `normal' calls, LCT_CONST for `const'
- calls, LCT_PURE for `pure' calls, LCT_CONST_MAKE_BLOCK for `const' calls
- which should be enclosed in REG_LIBCALL/REG_RETVAL notes,
- LCT_PURE_MAKE_BLOCK for `purep' calls which should be enclosed in
- REG_LIBCALL/REG_RETVAL notes with extra (use (memory (scratch)),
- or other LCT_ value for other types of library calls. */
+ FN_TYPE should be LCT_NORMAL for `normal' calls, LCT_CONST for
+ `const' calls, LCT_PURE for `pure' calls, or other LCT_ value for
+ other types of library calls. */
void
emit_library_call (rtx orgfun, enum libcall_type fn_type,
- int_size_in_bytes (TREE_TYPE (pval))
+ partial);
size_rtx = expand_expr (size_in_bytes (TREE_TYPE (pval)),
- NULL_RTX, TYPE_MODE (sizetype), 0);
+ NULL_RTX, TYPE_MODE (sizetype),
+ EXPAND_NORMAL);
}
parm_align = arg->locate.boundary;
|| (GET_CODE (XEXP (x, 0)) == PLUS
&& XEXP (XEXP (x, 0), 0) ==
crtl->args.internal_arg_pointer
- && GET_CODE (XEXP (XEXP (x, 0), 1)) == CONST_INT))
+ && CONST_INT_P (XEXP (XEXP (x, 0), 1))))
{
if (XEXP (x, 0) != crtl->args.internal_arg_pointer)
i = INTVAL (XEXP (XEXP (x, 0), 1));
/* expand_call should ensure this. */
gcc_assert (!arg->locate.offset.var
&& arg->locate.size.var == 0
- && GET_CODE (size_rtx) == CONST_INT);
+ && CONST_INT_P (size_rtx));
if (arg->locate.offset.constant > i)
{