/* 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 "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;
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
if (ecf_flags & ECF_LOOPING_CONST_OR_PURE)
RTL_LOOPING_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)
- add_reg_note (call_insn, REG_EH_REGION, const0_rtx);
- else
- {
- int rn = lookup_expr_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)
- add_reg_note (call_insn, REG_EH_REGION, GEN_INT (rn));
- }
+ /* Create a nothrow REG_EH_REGION note, if needed. */
+ make_reg_eh_region_note (call_insn, ecf_flags, 0);
if (ecf_flags & ECF_NORETURN)
add_reg_note (call_insn, REG_NORETURN, const0_rtx);
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;
&& 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);
}
}
/* 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.
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.
/* 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 non looping pure or const function call is
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;
/* 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);
}
#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 (currently_expanding_call++ != 0
|| !flag_optimize_sibling_calls
|| args_size.var
- || lookup_expr_eh_region (exp) >= 0
|| dbg_cnt (tail_call) == false)
try_tail_call = 0;
&caller_unsignedp,
TREE_TYPE (current_function_decl), 1);
callee_promoted_mode
- = promote_function_mode (TREE_TYPE (caller_res), callee_mode,
+ = promote_function_mode (TREE_TYPE (funtype), callee_mode,
&callee_unsignedp,
- TREE_TYPE (funtype), 1);
+ funtype, 1);
if (caller_mode != VOIDmode
&& (caller_promoted_mode != callee_promoted_mode
|| ((caller_mode != caller_promoted_mode
/* 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
}
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 (pass && (flags & ECF_MALLOC))
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);
/* 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;
/* 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)))
+ && TYPE_MODE (rettype) != BLKmode
+ && GET_MODE (target) != TYPE_MODE (rettype))
{
- tree type = TREE_TYPE (exp);
+ tree type = rettype;
int unsignedp = TYPE_UNSIGNED (type);
int offset = 0;
enum machine_mode pmode;
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. */