/* 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, 2008, 2009
+ 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010
Free Software Foundation, Inc.
This file is part of GCC.
#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 "sbitmap.h"
#include "langhooks.h"
#include "target.h"
+#include "debug.h"
#include "cgraph.h"
#include "except.h"
#include "dbgcnt.h"
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,
rtx rounded_stack_size_rtx = GEN_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
+ 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);
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
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;
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;
flags |= ECF_RETURNS_TWICE;
/* Process the pure and const attributes. */
- if (TREE_READONLY (exp) && ! TREE_THIS_VOLATILE (exp))
+ if (TREE_READONLY (exp))
flags |= ECF_CONST;
if (DECL_PURE_P (exp))
flags |= ECF_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;
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;
+ {
+ flags |= ECF_NORETURN;
+ if (flags & (ECF_CONST|ECF_PURE))
+ flags |= ECF_LOOPING_CONST_OR_PURE;
+ }
return flags;
}
&& args[i].mode != BLKmode
&& rtx_cost (args[i].value, SET, optimize_insn_for_speed_p ())
> COSTS_N_INSNS (1)
- && ((SMALL_REGISTER_CLASSES && *reg_parm_seen)
+ && ((*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);
}
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.
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.
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
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
/* 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);
}
}
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.
{
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. */
if (REG_P (x)
&& HARD_REGISTER_P (x)
- && CLASS_LIKELY_SPILLED_P (REGNO_REG_CLASS (REGNO (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,
/* Set up a place to return a structure. */
/* Cater to broken compilers. */
- if (aggregate_value_p (exp, (!fndecl ? fntype : fndecl)))
+ if (aggregate_value_p (exp, fntype))
{
/* This call returns a big structure. */
flags &= ~(ECF_CONST | ECF_PURE | ECF_LOOPING_CONST_OR_PURE);
- 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),
- crtl->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;
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
{
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,
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. */
}
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,
/* 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! */
}
else if (TYPE_MODE (rettype) == BLKmode)
{
- target = copy_blkmode_from_reg (target, valreg, rettype);
+ 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;
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++;
}
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
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 the stack pointer will never be pushed, it is possible for
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. */
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);