/* 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
- Free Software Foundation, Inc.
+ 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
+ 2011 Free Software Foundation, Inc.
This file is part of GCC.
GCC is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free
-Software Foundation; either version 2, or (at your option) any later
+Software Foundation; either version 3, or (at your option) any later
version.
GCC is distributed in the hope that it will be useful, but WITHOUT ANY
for more details.
You should have received a copy of the GNU General Public License
-along with GCC; see the file COPYING. If not, write to the Free
-Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA
-02110-1301, USA. */
+along with GCC; see the file COPYING3. If not see
+<http://www.gnu.org/licenses/>. */
#include "config.h"
#include "system.h"
#include "tm.h"
#include "rtl.h"
#include "tree.h"
+#include "gimple.h"
#include "flags.h"
#include "expr.h"
#include "optabs.h"
#include "libfuncs.h"
#include "function.h"
#include "regs.h"
-#include "toplev.h"
+#include "diagnostic-core.h"
#include "output.h"
#include "tm_p.h"
#include "timevar.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)
rtx stack;
/* Location on the stack of the start of this argument slot. This can
differ from STACK if this arg pads downward. This location is known
- to be aligned to FUNCTION_ARG_BOUNDARY. */
+ to be aligned to TARGET_FUNCTION_ARG_BOUNDARY. */
rtx stack_slot;
/* Place that this stack area has been saved, if needed. */
rtx save_area;
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);
int, int *);
static rtx emit_library_call_value_1 (int, rtx, rtx, enum libcall_type,
enum machine_mode, int, va_list);
-static int special_function_p (tree, int);
+static int special_function_p (const_tree, int);
static int check_sibcall_argument_overlap_1 (rtx);
static int check_sibcall_argument_overlap (rtx, struct arg_data *, 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;
The CALL_INSN is the first insn generated.
FNDECL is the declaration node of the function. This is given to the
- macro RETURN_POPS_ARGS to determine whether this function pops its own args.
+ hook TARGET_RETURN_POPS_ARGS to determine whether this function pops
+ its own args.
- FUNTYPE is the data type of the function. This is given to the macro
- RETURN_POPS_ARGS to determine whether this function pops its own args.
- We used to allow an identifier for library functions, but that doesn't
- work when the return type is an aggregate type and the calling convention
- says that the pointer to this aggregate is to be popped by the callee.
+ FUNTYPE is the data type of the function. This is given to the hook
+ TARGET_RETURN_POPS_ARGS to determine whether this function pops its
+ own args. We used to allow an identifier for library functions, but
+ that doesn't work when the return type is an aggregate type and the
+ calling convention says that the pointer to this aggregate is to be
+ popped by the callee.
STACK_SIZE is the number of bytes of arguments on the stack,
ROUNDED_STACK_SIZE is that number rounded up to
It is zero if this call doesn't want a structure value.
NEXT_ARG_REG is the rtx that results from executing
- FUNCTION_ARG (args_so_far, VOIDmode, void_type_node, 1)
+ targetm.calls.function_arg (&args_so_far, VOIDmode, void_type_node, true)
just after all the args have had their registers assigned.
This could be whatever you like, but normally it is the first
arg-register beyond those used for args in this call,
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,
CUMULATIVE_ARGS *args_so_far ATTRIBUTE_UNUSED)
{
rtx rounded_stack_size_rtx = GEN_INT (rounded_stack_size);
- rtx call_insn;
+ rtx call_insn, call, funmem;
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
+ HOST_WIDE_INT n_popped
+ = targetm.calls.return_pops_args (fndecl, funtype, stack_size);
#ifdef CALL_POPS_ARGS
n_popped += CALL_POPS_ARGS (* args_so_far);
if (GET_CODE (funexp) != SYMBOL_REF)
funexp = memory_address (FUNCTION_MODE, funexp);
+ funmem = gen_rtx_MEM (FUNCTION_MODE, funexp);
+ if (fndecl && TREE_CODE (fndecl) == FUNCTION_DECL)
+ set_mem_expr (funmem, fndecl);
+ else if (fntree)
+ set_mem_expr (funmem, build_fold_indirect_ref (CALL_EXPR_FN (fntree)));
+
#if defined (HAVE_sibcall_pop) && defined (HAVE_sibcall_value_pop)
if ((ecf_flags & ECF_SIBCALL)
&& HAVE_sibcall_pop && HAVE_sibcall_value_pop
if possible, for the sake of frame pointer elimination. */
if (valreg)
- pat = GEN_SIBCALL_VALUE_POP (valreg,
- gen_rtx_MEM (FUNCTION_MODE, funexp),
- rounded_stack_size_rtx, next_arg_reg,
- n_pop);
+ pat = GEN_SIBCALL_VALUE_POP (valreg, funmem, rounded_stack_size_rtx,
+ next_arg_reg, n_pop);
else
- pat = GEN_SIBCALL_POP (gen_rtx_MEM (FUNCTION_MODE, funexp),
- rounded_stack_size_rtx, next_arg_reg, n_pop);
+ pat = GEN_SIBCALL_POP (funmem, rounded_stack_size_rtx, next_arg_reg,
+ n_pop);
emit_call_insn (pat);
already_popped = 1;
even if the call has no arguments to pop. */
#if defined (HAVE_call) && defined (HAVE_call_value)
if (HAVE_call && HAVE_call_value && HAVE_call_pop && HAVE_call_value_pop
- && n_popped > 0 && ! (ecf_flags & ECF_SP_DEPRESSED))
+ && n_popped > 0)
#else
if (HAVE_call_pop && HAVE_call_value_pop)
#endif
if possible, for the sake of frame pointer elimination. */
if (valreg)
- pat = GEN_CALL_VALUE_POP (valreg,
- gen_rtx_MEM (FUNCTION_MODE, funexp),
- rounded_stack_size_rtx, next_arg_reg, n_pop);
+ pat = GEN_CALL_VALUE_POP (valreg, funmem, rounded_stack_size_rtx,
+ next_arg_reg, n_pop);
else
- pat = GEN_CALL_POP (gen_rtx_MEM (FUNCTION_MODE, funexp),
- rounded_stack_size_rtx, next_arg_reg, n_pop);
+ pat = GEN_CALL_POP (funmem, rounded_stack_size_rtx, next_arg_reg,
+ n_pop);
emit_call_insn (pat);
already_popped = 1;
&& HAVE_sibcall && HAVE_sibcall_value)
{
if (valreg)
- emit_call_insn (GEN_SIBCALL_VALUE (valreg,
- gen_rtx_MEM (FUNCTION_MODE, funexp),
+ emit_call_insn (GEN_SIBCALL_VALUE (valreg, funmem,
rounded_stack_size_rtx,
next_arg_reg, NULL_RTX));
else
- emit_call_insn (GEN_SIBCALL (gen_rtx_MEM (FUNCTION_MODE, funexp),
- rounded_stack_size_rtx, next_arg_reg,
- struct_value_size_rtx));
+ emit_call_insn (GEN_SIBCALL (funmem, rounded_stack_size_rtx,
+ next_arg_reg,
+ GEN_INT (struct_value_size)));
}
else
#endif
if (HAVE_call && HAVE_call_value)
{
if (valreg)
- emit_call_insn (GEN_CALL_VALUE (valreg,
- gen_rtx_MEM (FUNCTION_MODE, funexp),
- rounded_stack_size_rtx, next_arg_reg,
- NULL_RTX));
+ emit_call_insn (GEN_CALL_VALUE (valreg, funmem, rounded_stack_size_rtx,
+ next_arg_reg, NULL_RTX));
else
- emit_call_insn (GEN_CALL (gen_rtx_MEM (FUNCTION_MODE, funexp),
- rounded_stack_size_rtx, next_arg_reg,
- struct_value_size_rtx));
+ emit_call_insn (GEN_CALL (funmem, rounded_stack_size_rtx, next_arg_reg,
+ 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);
+ /* Some target create a fresh MEM instead of reusing the one provided
+ above. Set its MEM_EXPR. */
+ call = PATTERN (call_insn);
+ if (GET_CODE (call) == PARALLEL)
+ call = XVECEXP (call, 0, 0);
+ if (GET_CODE (call) == SET)
+ call = SET_SRC (call);
+ if (GET_CODE (call) == CALL
+ && MEM_P (XEXP (call, 0))
+ && MEM_EXPR (XEXP (call, 0)) == NULL_TREE
+ && MEM_EXPR (funmem) != NULL_TREE)
+ set_mem_expr (XEXP (call, 0), MEM_EXPR (funmem));
/* 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 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));
- note_current_region_may_contain_throw ();
- }
+ if (ecf_flags & ECF_CONST)
+ RTL_CONST_CALL_P (call_insn) = 1;
+
+ /* 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));
- current_function_calls_setjmp = 1;
+ add_reg_note (call_insn, REG_SETJMP, const0_rtx);
+ cfun->calls_setjmp = 1;
}
SIBLING_CALL_P (call_insn) = ((ecf_flags & ECF_SIBCALL) != 0);
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)
if (rounded_stack_size != 0)
{
- if (ecf_flags & (ECF_SP_DEPRESSED | ECF_NORETURN))
+ if (ecf_flags & ECF_NORETURN)
/* Just pretend we did the pop. */
stack_pointer_delta -= rounded_stack_size;
else if (flag_defer_pop && inhibit_defer_pop == 0
space from the stack such as alloca. */
static int
-special_function_p (tree fndecl, int flags)
+special_function_p (const_tree fndecl, int flags)
{
if (fndecl && DECL_NAME (fndecl)
&& IDENTIFIER_LENGTH (DECL_NAME (fndecl)) <= 17
&& ! 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 nonzero when FNDECL represents a call to setjmp. */
int
-setjmp_call_p (tree fndecl)
+setjmp_call_p (const_tree fndecl)
{
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 (tree exp)
+alloca_call_p (const_tree exp)
{
if (TREE_CODE (exp) == CALL_EXPR
&& TREE_CODE (CALL_EXPR_FN (exp)) == ADDR_EXPR
/* Detect flags (function attributes) from the function decl or type node. */
int
-flags_from_decl_or_type (tree exp)
+flags_from_decl_or_type (const_tree exp)
{
int flags = 0;
- 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))
+ 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 (lookup_attribute ("leaf", DECL_ATTRIBUTES (exp)))
+ flags |= ECF_LEAF;
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))
+ else if (TYPE_P (exp) && TYPE_READONLY (exp))
flags |= ECF_CONST;
if (TREE_THIS_VOLATILE (exp))
- flags |= ECF_NORETURN;
-
- /* Mark if the function returns with the stack pointer depressed. We
- cannot consider it pure or constant in that case. */
- if (TREE_CODE (type) == FUNCTION_TYPE && TYPE_RETURNS_STACK_DEPRESSED (type))
{
- flags |= ECF_SP_DEPRESSED;
- flags &= ~(ECF_PURE | ECF_CONST);
+ flags |= ECF_NORETURN;
+ if (flags & (ECF_CONST|ECF_PURE))
+ flags |= ECF_LOOPING_CONST_OR_PURE;
}
return flags;
/* Detect flags from a CALL_EXPR. */
int
-call_expr_flags (tree t)
+call_expr_flags (const_tree t)
{
int flags;
tree decl = get_callee_fndecl (t);
|| (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));
int bitsize = MIN (bytes * BITS_PER_UNIT, BITS_PER_WORD);
args[i].aligned_regs[j] = reg;
- word = extract_bit_field (word, bitsize, 0, 1, NULL_RTX,
+ word = extract_bit_field (word, bitsize, 0, 1, false, NULL_RTX,
word_mode, word_mode);
/* There is no need to restrict this code to loading items
}
/* 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. */
if (*old_stack_level == 0)
{
- emit_stack_save (SAVE_BLOCK, old_stack_level, NULL_RTX);
+ emit_stack_save (SAVE_BLOCK, old_stack_level);
*old_pending_adj = pending_stack_adjust;
pending_stack_adjust = 0;
}
- copy = gen_rtx_MEM (BLKmode,
- allocate_dynamic_stack_space
- (size_rtx, NULL_RTX, TYPE_ALIGN (type)));
+ /* We can pass TRUE as the 4th argument because we just
+ saved the stack pointer and will restore it right after
+ the call. */
+ copy = allocate_dynamic_stack_space (size_rtx,
+ TYPE_ALIGN (type),
+ TYPE_ALIGN (type),
+ true);
+ copy = gen_rtx_MEM (BLKmode, copy);
set_mem_attributes (copy, type, 1);
}
else
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].reg = FUNCTION_ARG (*args_so_far, mode, type,
- argpos < n_named_args);
-#ifdef FUNCTION_INCOMING_ARG
+ args[i].reg = targetm.calls.function_arg (args_so_far, mode, type,
+ argpos < n_named_args);
+
/* 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. */
- args[i].tail_call_reg = FUNCTION_INCOMING_ARG (*args_so_far, mode, type,
- argpos < n_named_args);
-#else
- args[i].tail_call_reg = args[i].reg;
-#endif
+ if (targetm.calls.function_incoming_arg != targetm.calls.function_arg)
+ args[i].tail_call_reg
+ = targetm.calls.function_incoming_arg (args_so_far, mode, type,
+ argpos < n_named_args);
+ else
+ args[i].tail_call_reg = args[i].reg;
if (args[i].reg)
args[i].partial
|| (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
/* Increment ARGS_SO_FAR, which has info about which arg-registers
have been used, etc. */
- FUNCTION_ARG_ADVANCE (*args_so_far, TYPE_MODE (type), type,
- argpos < n_named_args);
+ targetm.calls.function_arg_advance (args_so_far, TYPE_MODE (type),
+ type, argpos < n_named_args);
}
}
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);
{
/* If this is the first use of the function, see if we need to
make an external definition for it. */
- if (! TREE_USED (fndecl))
+ if (!TREE_USED (fndecl) && fndecl != current_function_decl)
{
assemble_external (fndecl);
TREE_USED (fndecl) = 1;
{
HOST_WIDE_INT i;
- if (addr == current_function_internal_arg_pointer)
+ if (addr == crtl->args.internal_arg_pointer)
i = 0;
else if (GET_CODE (addr) == PLUS
- && XEXP (addr, 0) == current_function_internal_arg_pointer
- && GET_CODE (XEXP (addr, 1)) == CONST_INT)
+ && XEXP (addr, 0) == crtl->args.internal_arg_pointer
+ && 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
- && (XEXP (addr, 0) == current_function_internal_arg_pointer
- || XEXP (addr, 1) == current_function_internal_arg_pointer))
+ && (XEXP (addr, 0) == crtl->args.internal_arg_pointer
+ || XEXP (addr, 1) == crtl->args.internal_arg_pointer))
return true;
else
return false;
{
rtx mem = validize_mem (args[i].value);
- /* Check for overlap with already clobbered argument area. */
+ /* Check for overlap with already clobbered argument area,
+ providing that this has non-zero size. */
if (is_sibcall
- && mem_overlaps_already_clobbered_arg_p (XEXP (args[i].value, 0),
- size))
+ && (size == 0
+ || mem_overlaps_already_clobbered_arg_p
+ (XEXP (args[i].value, 0), size)))
*sibcall_failure = 1;
/* Handle a BLKmode that needs shifting. */
return true;
}
+/* If X is a likely-spilled register value, copy it to a pseudo
+ register and return that register. Return X otherwise. */
+
+static rtx
+avoid_likely_spilled_reg (rtx x)
+{
+ rtx new_rtx;
+
+ if (REG_P (x)
+ && HARD_REGISTER_P (x)
+ && targetm.class_likely_spilled_p (REGNO_REG_CLASS (REGNO (x))))
+ {
+ /* Make sure that we generate a REG rather than a CONCAT.
+ Moves into CONCATs can need nontrivial instructions,
+ and the whole point of this function is to avoid
+ using the hard register directly in such a situation. */
+ generating_concat_p = 0;
+ new_rtx = gen_reg_rtx (GET_MODE (x));
+ generating_concat_p = 1;
+ emit_move_insn (new_rtx, x);
+ return new_rtx;
+ }
+ return x;
+}
+
/* Generate all the code for a CALL_EXPR exp
and return an rtx for its value.
Store the value in TARGET (specified as an rtx) if convenient.
/* 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;
}
type_arg_types = TYPE_ARG_TYPES (funtype);
if (flags & ECF_MAY_BE_ALLOCA)
- current_function_calls_alloca = 1;
+ cfun->calls_alloca = 1;
/* If struct_value_rtx is 0, it means pass the address
as if it were an extra parameter. Put the argument expression
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)
|| (fndecl && decl_function_context (fndecl) == current_function_decl)
/* If this function requires more stack slots than the current
function, we cannot change it into a sibling call.
- current_function_pretend_args_size is not part of the
+ crtl->args.pretend_args_size is not part of the
stack allocated by our caller. */
- || args_size.constant > (current_function_args_size
- - current_function_pretend_args_size)
+ || args_size.constant > (crtl->args.size
+ - crtl->args.pretend_args_size)
/* If the callee pops its own arguments, then it must pop exactly
the same number of arguments as the current function. */
- || (RETURN_POPS_ARGS (fndecl, funtype, args_size.constant)
- != RETURN_POPS_ARGS (current_function_decl,
- TREE_TYPE (current_function_decl),
- current_function_args_size))
+ || (targetm.calls.return_pops_args (fndecl, funtype, args_size.constant)
+ != targetm.calls.return_pops_args (current_function_decl,
+ TREE_TYPE (current_function_decl),
+ crtl->args.size))
|| !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 (cfun->preferred_stack_boundary < preferred_stack_boundary
- && fndecl != current_function_decl)
- cfun->preferred_stack_boundary = preferred_stack_boundary;
- if (fndecl == current_function_decl)
- cfun->recursive_call_emit = true;
+ 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;
int save_pending_stack_adjust = 0;
int save_stack_pointer_delta = 0;
rtx insns;
- rtx before_call, next_arg_reg;
+ rtx before_call, next_arg_reg, after_args;
if (pass == 0)
{
/* Don't let pending stack adjusts add up to too much.
Also, do all pending adjustments now if there is any chance
this might be a call to alloca or if we are expanding a sibling
- call sequence or if we are calling a function that is to return
- with stack pointer depressed.
+ call sequence.
Also do the adjustments before a throwing call, otherwise
exception handling can fail; PR 19225. */
if (pending_stack_adjust >= 32
|| (pending_stack_adjust > 0
- && (flags & (ECF_MAY_BE_ALLOCA | ECF_SP_DEPRESSED)))
+ && (flags & ECF_MAY_BE_ALLOCA))
|| (pending_stack_adjust > 0
&& flag_exceptions && !(flags & ECF_NOTHROW))
|| 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 && cfun->stack_protect_guard)
+ if (pass == 0 && crtl->stack_protect_guard)
stack_protect_epilogue ();
adjusted_args_size = args_size;
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, current_function_pretend_args_size);
+ = plus_constant (argblock, crtl->args.pretend_args_size);
#else
- = plus_constant (argblock, -current_function_pretend_args_size);
+ = plus_constant (argblock, -crtl->args.pretend_args_size);
#endif
stored_args_map = sbitmap_alloc (args_size.constant);
sbitmap_zero (stored_args_map);
{
if (old_stack_level == 0)
{
- emit_stack_save (SAVE_BLOCK, &old_stack_level, NULL_RTX);
+ emit_stack_save (SAVE_BLOCK, &old_stack_level);
old_stack_pointer_delta = stack_pointer_delta;
old_pending_adj = pending_stack_adjust;
pending_stack_adjust = 0;
stack_arg_under_construction = 0;
}
argblock = push_block (ARGS_SIZE_RTX (adjusted_args_size), 0, 0);
+ if (flag_stack_usage)
+ current_function_has_unbounded_dynamic_stack_size = 1;
}
else
{
the prologue (if ACCUMULATE_OUTGOING_ARGS, or stack overflow
checking). */
- if (needed > current_function_outgoing_args_size)
- current_function_outgoing_args_size = needed;
+ if (needed > crtl->outgoing_args_size)
+ crtl->outgoing_args_size = needed;
if (must_preallocate)
{
/* 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)
{
- emit_stack_save (SAVE_BLOCK, &old_stack_level,
- NULL_RTX);
+ emit_stack_save (SAVE_BLOCK, &old_stack_level);
old_stack_pointer_delta = stack_pointer_delta;
old_pending_adj = pending_stack_adjust;
pending_stack_adjust = 0;
stack_usage_map = stack_usage_map_buf;
highest_outgoing_arg_in_use = 0;
}
- allocate_dynamic_stack_space (push_size, NULL_RTX,
- BITS_PER_UNIT);
+ /* We can pass TRUE as the 4th argument because we just
+ saved the stack pointer and will restore it right after
+ the call. */
+ allocate_dynamic_stack_space (push_size, 0,
+ BIGGEST_ALIGNMENT, true);
}
/* If argument evaluation might modify the stack pointer,
/* 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
be deferred during the evaluation of the arguments. */
NO_DEFER_POP;
+ /* Record the maximum pushed stack space size. We need to delay
+ doing it this far to take into account the optimization done
+ by combine_pending_stack_adjustment_and_call. */
+ if (flag_stack_usage
+ && !ACCUMULATE_OUTGOING_ARGS
+ && pass
+ && adjusted_args_size.var == 0)
+ {
+ int pushed = adjusted_args_size.constant + pending_stack_adjust;
+ if (pushed > current_function_pushed_stack_size)
+ current_function_pushed_stack_size = pushed;
+ }
+
funexp = rtx_for_function_call (fndecl, addr);
/* 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 (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));
use_reg (&call_fusage, struct_value);
}
- funexp = prepare_call_address (funexp, static_chain_value,
+ after_args = get_last_insn ();
+ 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,
/* Set up next argument register. For sibling calls on machines
with register windows this should be the incoming register. */
-#ifdef FUNCTION_INCOMING_ARG
if (pass == 0)
- next_arg_reg = FUNCTION_INCOMING_ARG (args_so_far, VOIDmode,
- void_type_node, 1);
+ next_arg_reg = targetm.calls.function_incoming_arg (&args_so_far,
+ VOIDmode,
+ void_type_node,
+ true);
else
-#endif
- next_arg_reg = FUNCTION_ARG (args_so_far, VOIDmode,
- void_type_node, 1);
+ next_arg_reg = targetm.calls.function_arg (&args_so_far,
+ VOIDmode, void_type_node,
+ true);
/* All arguments and registers used for the call must be set up by
now! */
next_arg_reg, valreg, old_inhibit_defer_pop, call_fusage,
flags, & args_so_far);
+ /* If the call setup or the call itself overlaps with anything
+ of the argument setup we probably clobbered our call address.
+ In that case we can't do sibcalls. */
+ if (pass == 0
+ && check_sibcall_argument_overlap (after_args, 0, 0))
+ sibcall_failure = 1;
+
/* If a non-BLKmode value is returned at the most significant end
of a register, shift the register right by the appropriate amount
and update VALREG accordingly. BLKmode values are handled by the
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;
/* We have to copy a return value in a CLASS_LIKELY_SPILLED hard
reg to a plain register. */
- if (REG_P (valreg)
- && HARD_REGISTER_P (valreg)
- && CLASS_LIKELY_SPILLED_P (REGNO_REG_CLASS (REGNO (valreg)))
- && !(REG_P (target) && !HARD_REGISTER_P (target)))
- valreg = copy_to_reg (valreg);
+ if (!REG_P (target) || HARD_REGISTER_P (target))
+ valreg = avoid_likely_spilled_reg (valreg);
/* If TARGET is a MEM in the argument area, and we have
saved part of the argument area, then we can't store
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 (valreg);
+ 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
argument, restore saved stack-pointer value. */
- if (old_stack_level && ! (flags & ECF_SP_DEPRESSED))
+ if (old_stack_level)
{
- emit_stack_restore (SAVE_BLOCK, old_stack_level, NULL_RTX);
+ emit_stack_restore (SAVE_BLOCK, old_stack_level);
stack_pointer_delta = old_stack_pointer_delta;
pending_stack_adjust = old_pending_adj;
old_stack_allocated = stack_pointer_delta - pending_stack_adjust;
if (tail_call_insns)
{
emit_insn (tail_call_insns);
- cfun->tail_call_emit = true;
+ crtl->tail_call_emit = true;
}
else
emit_insn (normal_call_insns);
currently_expanding_call--;
- /* If this function returns with the stack pointer depressed, ensure
- this block saves and restores the stack pointer, show it was
- changed, and adjust for any outgoing arg space. */
- if (flags & ECF_SP_DEPRESSED)
- {
- clear_pending_stack_adjust ();
- emit_insn (gen_rtx_CLOBBER (VOIDmode, stack_pointer_rtx));
- emit_move_insn (virtual_stack_dynamic_rtx, stack_pointer_rtx);
- }
-
if (stack_usage_map_buf)
free (stack_usage_map_buf);
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;
/* Ensure current function's preferred stack boundary is at least
what we need. */
- if (cfun->preferred_stack_boundary < PREFERRED_STACK_BOUNDARY)
- cfun->preferred_stack_boundary = PREFERRED_STACK_BOUNDARY;
+ if (crtl->preferred_stack_boundary < PREFERRED_STACK_BOUNDARY)
+ crtl->preferred_stack_boundary = PREFERRED_STACK_BOUNDARY;
/* If this kind of value comes back in memory,
decide where in memory it should come back. */
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,
argvec[count].mode = Pmode;
argvec[count].partial = 0;
- argvec[count].reg = FUNCTION_ARG (args_so_far, Pmode, NULL_TREE, 1);
+ argvec[count].reg = targetm.calls.function_arg (&args_so_far,
+ Pmode, NULL_TREE, true);
gcc_assert (targetm.calls.arg_partial_bytes (&args_so_far, Pmode,
NULL_TREE, 1) == 0);
|| reg_parm_stack_space > 0)
args_size.constant += argvec[count].locate.size.constant;
- FUNCTION_ARG_ADVANCE (args_so_far, Pmode, (tree) 0, 1);
+ targetm.calls.function_arg_advance (&args_so_far, Pmode, (tree) 0, true);
count++;
}
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
{
argvec[count].value = val;
argvec[count].mode = mode;
- argvec[count].reg = FUNCTION_ARG (args_so_far, mode, NULL_TREE, 1);
+ argvec[count].reg = targetm.calls.function_arg (&args_so_far, mode,
+ NULL_TREE, true);
argvec[count].partial
= targetm.calls.arg_partial_bytes (&args_so_far, mode, NULL_TREE, 1);
|| reg_parm_stack_space > 0)
args_size.constant += argvec[count].locate.size.constant;
- FUNCTION_ARG_ADVANCE (args_so_far, mode, (tree) 0, 1);
+ targetm.calls.function_arg_advance (&args_so_far, mode, (tree) 0, true);
}
/* If this machine requires an external definition for library
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 > current_function_outgoing_args_size)
- current_function_outgoing_args_size = args_size.constant;
+ if (args_size.constant > crtl->outgoing_args_size)
+ crtl->outgoing_args_size = args_size.constant;
+
+ if (flag_stack_usage && !ACCUMULATE_OUTGOING_ARGS)
+ {
+ int pushed = args_size.constant + pending_stack_adjust;
+ if (pushed > current_function_pushed_stack_size)
+ current_function_pushed_stack_size = pushed;
+ }
if (ACCUMULATE_OUTGOING_ARGS)
{
/* 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))
{
+ rtx use;
+
if (ACCUMULATE_OUTGOING_ARGS)
{
/* If this is being stored into a pre-allocated, fixed-size,
#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)
- {
- rtx use;
-
- /* Indicate argument access so that alias.c knows that these
- values are live. */
- if (argblock)
- use = plus_constant (argblock,
- argvec[argnum].locate.offset.constant);
- else
- /* When arguments are pushed, trying to tell alias.c where
- exactly this argument is won't work, because the
- auto-increment causes confusion. So we merely indicate
- that we access something with a known mode somewhere on
- the stack. */
- use = gen_rtx_PLUS (Pmode, virtual_outgoing_args_rtx,
- gen_rtx_SCRATCH (Pmode));
- use = gen_rtx_MEM (argvec[argnum].mode, use);
- use = gen_rtx_USE (VOIDmode, use);
- call_fusage = gen_rtx_EXPR_LIST (VOIDmode, use, call_fusage);
- }
+ /* Indicate argument access so that alias.c knows that these
+ values are live. */
+ if (argblock)
+ use = plus_constant (argblock,
+ argvec[argnum].locate.offset.constant);
+ else
+ /* When arguments are pushed, trying to tell alias.c where
+ exactly this argument is won't work, because the
+ auto-increment causes confusion. So we merely indicate
+ that we access something with a known mode somewhere on
+ the stack. */
+ use = gen_rtx_PLUS (Pmode, virtual_outgoing_args_rtx,
+ gen_rtx_SCRATCH (Pmode));
+ use = gen_rtx_MEM (argvec[argnum].mode, use);
+ use = gen_rtx_USE (VOIDmode, use);
+ call_fusage = gen_rtx_EXPR_LIST (VOIDmode, use, call_fusage);
}
}
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
build_function_type (tfom, NULL_TREE),
original_args_size.constant, args_size.constant,
struct_value_size,
- FUNCTION_ARG (args_so_far, VOIDmode, void_type_node, 1),
+ targetm.calls.function_arg (&args_so_far,
+ VOIDmode, void_type_node, true),
valreg,
old_inhibit_defer_pop + 1, call_fusage, flags, & args_so_far);
/* 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;
rtx x = arg->value;
int i = 0;
- if (XEXP (x, 0) == current_function_internal_arg_pointer
+ if (XEXP (x, 0) == crtl->args.internal_arg_pointer
|| (GET_CODE (XEXP (x, 0)) == PLUS
&& XEXP (XEXP (x, 0), 0) ==
- current_function_internal_arg_pointer
- && GET_CODE (XEXP (XEXP (x, 0), 1)) == CONST_INT))
+ crtl->args.internal_arg_pointer
+ && CONST_INT_P (XEXP (XEXP (x, 0), 1))))
{
- if (XEXP (x, 0) != current_function_internal_arg_pointer)
+ 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
- && GET_CODE (size_rtx) == CONST_INT);
+ && arg->locate.size.var == 0
+ && CONST_INT_P (size_rtx));
if (arg->locate.offset.constant > i)
{
}
else if (arg->locate.offset.constant < i)
{
- if (i < arg->locate.offset.constant + INTVAL (size_rtx))
+ /* Use arg->locate.size.constant instead of size_rtx
+ because we only care about the part of the argument
+ on the stack. */
+ if (i < (arg->locate.offset.constant
+ + arg->locate.size.constant))
+ sibcall_failure = 1;
+ }
+ else
+ {
+ /* Even though they appear to be at the same location,
+ if part of the outgoing argument is in registers,
+ they aren't really at the same location. Check for
+ this by making sure that the incoming size is the
+ same as the outgoing size. */
+ if (arg->locate.size.constant != INTVAL (size_rtx))
sibcall_failure = 1;
}
}
bool
must_pass_in_stack_var_size (enum machine_mode mode ATTRIBUTE_UNUSED,
- tree type)
+ const_tree type)
{
if (!type)
return false;
/* ??? Should be able to merge these two by examining BLOCK_REG_PADDING. */
bool
-must_pass_in_stack_var_size_or_pad (enum machine_mode mode, tree type)
+must_pass_in_stack_var_size_or_pad (enum machine_mode mode, const_tree type)
{
if (!type)
return false;