#include "config.h"
#include "system.h"
#include "rtl.h"
#include "tree.h"
#include "flags.h"
#include "expr.h"
#include "libfuncs.h"
#include "function.h"
#include "regs.h"
#include "toplev.h"
#include "output.h"
#include "tm_p.h"
#include "timevar.h"
#include "sbitmap.h"
#include "langhooks.h"
#include "target.h"
#include "varray.h"
#include "cgraph.h"
#include "feedback.h"
#if !defined FUNCTION_OK_FOR_SIBCALL
#define FUNCTION_OK_FOR_SIBCALL(DECL) 1
#endif
#ifdef PUSH_ROUNDING
#ifndef PUSH_ARGS_REVERSED
#if defined (STACK_GROWS_DOWNWARD) != defined (ARGS_GROW_DOWNWARD)
#define PUSH_ARGS_REVERSED PUSH_ARGS
#endif
#endif
#endif
#ifndef PUSH_ARGS_REVERSED
#define PUSH_ARGS_REVERSED 0
#endif
#ifndef STACK_POINTER_OFFSET
#define STACK_POINTER_OFFSET 0
#endif
#define STACK_BYTES (PREFERRED_STACK_BOUNDARY / BITS_PER_UNIT)
struct arg_data
{
tree tree_value;
enum machine_mode mode;
rtx value;
rtx initial_value;
rtx reg;
rtx tail_call_reg;
int unsignedp;
int partial;
int pass_on_stack;
int no_stack_slot;
struct args_size offset;
struct args_size slot_offset;
struct args_size size;
rtx stack;
rtx stack_slot;
rtx save_area;
rtx *aligned_regs;
int n_aligned_regs;
struct args_size alignment_pad;
};
static char *stack_usage_map;
static int highest_outgoing_arg_in_use;
static sbitmap stored_args_map;
int stack_arg_under_construction;
static int calls_function PARAMS ((tree, int));
static int calls_function_1 PARAMS ((tree, int));
#define ECF_CONST 1
#define ECF_NORETURN 2
#define ECF_MALLOC 4
#define ECF_MAY_BE_ALLOCA 8
#define ECF_NOTHROW 16
#define ECF_RETURNS_TWICE 32
#define ECF_LONGJMP 64
#define ECF_FORK_OR_EXEC 128
#define ECF_SIBCALL 256
#define ECF_PURE 512
#define ECF_SP_DEPRESSED 1024
#define ECF_ALWAYS_RETURN 2048
#define ECF_LIBCALL_BLOCK 4096
static void emit_call_1 PARAMS ((rtx, tree, tree, HOST_WIDE_INT,
HOST_WIDE_INT, HOST_WIDE_INT, rtx,
rtx, int, rtx, int,
CUMULATIVE_ARGS *));
static void precompute_register_parameters PARAMS ((int,
struct arg_data *,
int *));
static int store_one_arg PARAMS ((struct arg_data *, rtx, int, int,
int));
static void store_unaligned_arguments_into_pseudos PARAMS ((struct arg_data *,
int));
static int finalize_must_preallocate PARAMS ((int, int,
struct arg_data *,
struct args_size *));
static void precompute_arguments PARAMS ((int, int,
struct arg_data *));
static int compute_argument_block_size PARAMS ((int,
struct args_size *,
int));
static void initialize_argument_information PARAMS ((int,
struct arg_data *,
struct args_size *,
int, tree, tree,
CUMULATIVE_ARGS *,
int, rtx *, int *,
int *, int *));
static void compute_argument_addresses PARAMS ((struct arg_data *,
rtx, int));
static rtx rtx_for_function_call PARAMS ((tree, tree));
static void load_register_parameters PARAMS ((struct arg_data *,
int, rtx *, int,
int, int *));
static rtx emit_library_call_value_1 PARAMS ((int, rtx, rtx,
enum libcall_type,
enum machine_mode,
int, va_list));
static int special_function_p PARAMS ((tree, int));
static int flags_from_decl_or_type PARAMS ((tree));
static rtx try_to_integrate PARAMS ((tree, tree, rtx,
int, tree, rtx));
static int check_sibcall_argument_overlap_1 PARAMS ((rtx));
static int check_sibcall_argument_overlap PARAMS ((rtx, struct arg_data *,
int));
static int combine_pending_stack_adjustment_and_call
PARAMS ((int, struct args_size *, int));
static tree fix_unsafe_tree PARAMS ((tree));
#ifdef REG_PARM_STACK_SPACE
static rtx save_fixed_argument_area PARAMS ((int, rtx, int *, int *));
static void restore_fixed_argument_area PARAMS ((rtx, rtx, int, int));
#endif
static tree calls_function_save_exprs;
static int
calls_function (exp, which)
tree exp;
int which;
{
int val;
calls_function_save_exprs = 0;
val = calls_function_1 (exp, which);
calls_function_save_exprs = 0;
return val;
}
static int
calls_function_1 (exp, which)
tree exp;
int which;
{
int i;
enum tree_code code = TREE_CODE (exp);
int class = TREE_CODE_CLASS (code);
int length = first_rtl_op (code);
if ((int) code >= NUM_TREE_CODES)
return 1;
switch (code)
{
case CALL_EXPR:
if (which == 0)
return 1;
else if ((TREE_CODE (TREE_TYPE (TREE_TYPE (TREE_OPERAND (exp, 0))))
== FUNCTION_TYPE)
&& (TYPE_RETURNS_STACK_DEPRESSED
(TREE_TYPE (TREE_TYPE (TREE_OPERAND (exp, 0))))))
return 1;
else if (TREE_CODE (TREE_OPERAND (exp, 0)) == ADDR_EXPR
&& (TREE_CODE (TREE_OPERAND (TREE_OPERAND (exp, 0), 0))
== FUNCTION_DECL)
&& (special_function_p (TREE_OPERAND (TREE_OPERAND (exp, 0), 0),
0)
& ECF_MAY_BE_ALLOCA))
return 1;
break;
case CONSTRUCTOR:
{
tree tem;
for (tem = CONSTRUCTOR_ELTS (exp); tem != 0; tem = TREE_CHAIN (tem))
if (calls_function_1 (TREE_VALUE (tem), which))
return 1;
}
return 0;
case SAVE_EXPR:
if (SAVE_EXPR_RTL (exp) != 0)
return 0;
if (value_member (exp, calls_function_save_exprs))
return 0;
calls_function_save_exprs = tree_cons (NULL_TREE, exp,
calls_function_save_exprs);
return (TREE_OPERAND (exp, 0) != 0
&& calls_function_1 (TREE_OPERAND (exp, 0), which));
case BLOCK:
{
tree local;
tree subblock;
for (local = BLOCK_VARS (exp); local; local = TREE_CHAIN (local))
if (DECL_INITIAL (local) != 0
&& calls_function_1 (DECL_INITIAL (local), which))
return 1;
for (subblock = BLOCK_SUBBLOCKS (exp);
subblock;
subblock = TREE_CHAIN (subblock))
if (calls_function_1 (subblock, which))
return 1;
}
return 0;
case TREE_LIST:
for (; exp != 0; exp = TREE_CHAIN (exp))
if (calls_function_1 (TREE_VALUE (exp), which))
return 1;
return 0;
default:
break;
}
if (! IS_EXPR_CODE_CLASS (class) && class != 'r' && class != 'b')
return 0;
for (i = 0; i < length; i++)
if (TREE_OPERAND (exp, i) != 0
&& calls_function_1 (TREE_OPERAND (exp, i), which))
return 1;
return 0;
}
rtx
prepare_call_address (funexp, fndecl, call_fusage, reg_parm_seen, sibcallp)
rtx funexp;
tree fndecl;
rtx *call_fusage;
int reg_parm_seen;
int sibcallp;
{
rtx static_chain_value = 0;
funexp = protect_from_queue (funexp, 0);
if (fndecl != 0)
static_chain_value = lookup_static_chain (fndecl);
if (GET_CODE (funexp) != SYMBOL_REF)
funexp = ((SMALL_REGISTER_CLASSES && reg_parm_seen)
? force_not_mem (memory_address (FUNCTION_MODE, funexp))
: memory_address (FUNCTION_MODE, funexp));
else if (! sibcallp)
{
#ifndef NO_FUNCTION_CSE
if (optimize && ! flag_no_function_cse)
#ifdef NO_RECURSIVE_FUNCTION_CSE
if (fndecl != current_function_decl)
#endif
funexp = force_reg (Pmode, funexp);
#endif
}
if (static_chain_value != 0)
{
emit_move_insn (static_chain_rtx, static_chain_value);
if (GET_CODE (static_chain_rtx) == REG)
use_reg (call_fusage, static_chain_rtx);
}
return funexp;
}
static void
emit_call_1 (funexp, fndecl, funtype, stack_size, rounded_stack_size,
struct_value_size, next_arg_reg, valreg, old_inhibit_defer_pop,
call_fusage, ecf_flags, args_so_far)
rtx funexp;
tree fndecl ATTRIBUTE_UNUSED;
tree funtype ATTRIBUTE_UNUSED;
HOST_WIDE_INT stack_size ATTRIBUTE_UNUSED;
HOST_WIDE_INT rounded_stack_size;
HOST_WIDE_INT struct_value_size ATTRIBUTE_UNUSED;
rtx next_arg_reg ATTRIBUTE_UNUSED;
rtx valreg;
int old_inhibit_defer_pop;
rtx call_fusage;
int ecf_flags;
CUMULATIVE_ARGS *args_so_far ATTRIBUTE_UNUSED;
{
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
#ifdef CALL_POPS_ARGS
n_popped += CALL_POPS_ARGS (* args_so_far);
#endif
if (GET_CODE (funexp) != SYMBOL_REF)
#ifdef MAGIC_INDIRECT_CALL_REG
funexp = gen_rtx_REG (SImode, MAGIC_INDIRECT_CALL_REG);
#else
funexp = memory_address (FUNCTION_MODE, funexp);
#endif
#if defined (HAVE_sibcall_pop) && defined (HAVE_sibcall_value_pop)
if ((ecf_flags & ECF_SIBCALL)
&& HAVE_sibcall_pop && HAVE_sibcall_value_pop
&& (n_popped > 0 || stack_size == 0))
{
rtx n_pop = GEN_INT (n_popped);
rtx pat;
if (valreg)
pat = GEN_SIBCALL_VALUE_POP (valreg,
gen_rtx_MEM (FUNCTION_MODE, funexp),
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);
emit_call_insn (pat);
already_popped = 1;
}
else
#endif
#if defined (HAVE_call_pop) && defined (HAVE_call_value_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))
#else
if (HAVE_call_pop && HAVE_call_value_pop)
#endif
{
rtx n_pop = GEN_INT (n_popped);
rtx pat;
if (valreg)
pat = GEN_CALL_VALUE_POP (valreg,
gen_rtx_MEM (FUNCTION_MODE, funexp),
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);
emit_call_insn (pat);
already_popped = 1;
}
else
#endif
#if defined (HAVE_sibcall) && defined (HAVE_sibcall_value)
if ((ecf_flags & ECF_SIBCALL)
&& HAVE_sibcall && HAVE_sibcall_value)
{
if (valreg)
emit_call_insn (GEN_SIBCALL_VALUE (valreg,
gen_rtx_MEM (FUNCTION_MODE, funexp),
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));
}
else
#endif
#if defined (HAVE_call) && defined (HAVE_call_value)
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));
else
emit_call_insn (GEN_CALL (gen_rtx_MEM (FUNCTION_MODE, funexp),
rounded_stack_size_rtx, next_arg_reg,
struct_value_size_rtx));
}
else
#endif
abort ();
for (call_insn = get_last_insn ();
call_insn && GET_CODE (call_insn) != CALL_INSN;
call_insn = PREV_INSN (call_insn))
;
if (! call_insn)
abort ();
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);
if (CALL_INSN_FUNCTION_USAGE (call_insn))
{
rtx link;
for (link = CALL_INSN_FUNCTION_USAGE (call_insn); XEXP (link, 1) != 0;
link = XEXP (link, 1))
;
XEXP (link, 1) = call_fusage;
}
else
CALL_INSN_FUNCTION_USAGE (call_insn) = call_fusage;
if (ecf_flags & (ECF_CONST | ECF_PURE))
CONST_OR_PURE_CALL_P (call_insn) = 1;
if (ecf_flags & ECF_NOTHROW)
REG_NOTES (call_insn) = gen_rtx_EXPR_LIST (REG_EH_REGION, const0_rtx,
REG_NOTES (call_insn));
if (ecf_flags & ECF_NORETURN)
REG_NOTES (call_insn) = gen_rtx_EXPR_LIST (REG_NORETURN, const0_rtx,
REG_NOTES (call_insn));
if (ecf_flags & ECF_ALWAYS_RETURN)
REG_NOTES (call_insn) = gen_rtx_EXPR_LIST (REG_ALWAYS_RETURN, const0_rtx,
REG_NOTES (call_insn));
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;
}
SIBLING_CALL_P (call_insn) = ((ecf_flags & ECF_SIBCALL) != 0);
inhibit_defer_pop = old_inhibit_defer_pop;
if (n_popped > 0)
{
if (!already_popped)
CALL_INSN_FUNCTION_USAGE (call_insn)
= gen_rtx_EXPR_LIST (VOIDmode,
gen_rtx_CLOBBER (VOIDmode, stack_pointer_rtx),
CALL_INSN_FUNCTION_USAGE (call_insn));
rounded_stack_size -= n_popped;
rounded_stack_size_rtx = GEN_INT (rounded_stack_size);
stack_pointer_delta -= n_popped;
}
if (!ACCUMULATE_OUTGOING_ARGS)
{
if (rounded_stack_size != 0)
{
if (ecf_flags & ECF_SP_DEPRESSED)
stack_pointer_delta -= rounded_stack_size;
else if (flag_defer_pop && inhibit_defer_pop == 0
&& ! (ecf_flags & (ECF_CONST | ECF_PURE)))
pending_stack_adjust += rounded_stack_size;
else
adjust_stack (rounded_stack_size_rtx);
}
}
else if (n_popped)
anti_adjust_stack (GEN_INT (n_popped));
}
static int
special_function_p (fndecl, flags)
tree fndecl;
int flags;
{
if (! (flags & ECF_MALLOC)
&& fndecl && DECL_NAME (fndecl)
&& IDENTIFIER_LENGTH (DECL_NAME (fndecl)) <= 17
&& (DECL_CONTEXT (fndecl) == NULL_TREE
|| TREE_CODE (DECL_CONTEXT (fndecl)) == TRANSLATION_UNIT_DECL)
&& TREE_PUBLIC (fndecl))
{
const char *name = IDENTIFIER_POINTER (DECL_NAME (fndecl));
const char *tname = name;
if (((IDENTIFIER_LENGTH (DECL_NAME (fndecl)) == 6
&& name[0] == 'a'
&& ! strcmp (name, "alloca"))
|| (IDENTIFIER_LENGTH (DECL_NAME (fndecl)) == 16
&& name[0] == '_'
&& ! strcmp (name, "__builtin_alloca"))))
flags |= ECF_MAY_BE_ALLOCA;
if (name[0] == '_')
{
if (name[1] == '_' && name[2] == 'x')
tname += 3;
else if (name[1] == '_')
tname += 2;
else
tname += 1;
}
if (tname[0] == 's')
{
if ((tname[1] == 'e'
&& (! strcmp (tname, "setjmp")
|| ! strcmp (tname, "setjmp_syscall")))
|| (tname[1] == 'i'
&& ! strcmp (tname, "sigsetjmp"))
|| (tname[1] == 'a'
&& ! strcmp (tname, "savectx")))
flags |= ECF_RETURNS_TWICE;
if (tname[1] == 'i'
&& ! strcmp (tname, "siglongjmp"))
flags |= ECF_LONGJMP;
}
else if ((tname[0] == 'q' && tname[1] == 's'
&& ! strcmp (tname, "qsetjmp"))
|| (tname[0] == 'v' && tname[1] == 'f'
&& ! strcmp (tname, "vfork")))
flags |= ECF_RETURNS_TWICE;
else if (tname[0] == 'l' && tname[1] == 'o'
&& ! strcmp (tname, "longjmp"))
flags |= ECF_LONGJMP;
else if ((tname[0] == 'f' && tname[1] == 'o'
&& ! strcmp (tname, "fork"))
|| (name[0] == '_' && name[1] == '_'
&& ! strcmp (tname, "clone"))
|| (tname[0] == 'e' && tname[1] == 'x' && tname[2] == 'e'
&& tname[3] == 'c' && (tname[4] == 'l' || tname[4] == 'v')
&& (tname[5] == '\0'
|| ((tname[5] == 'p' || tname[5] == 'e')
&& tname[6] == '\0'))))
flags |= ECF_FORK_OR_EXEC;
else if (TYPE_MODE (TREE_TYPE (TREE_TYPE (fndecl))) == Pmode
&& (! strcmp (tname, "malloc")
|| ! strcmp (tname, "calloc")
|| ! strcmp (tname, "strdup")))
flags |= ECF_MALLOC;
}
return flags;
}
int
setjmp_call_p (fndecl)
tree fndecl;
{
return special_function_p (fndecl, 0) & ECF_RETURNS_TWICE;
}
bool
alloca_call_p (exp)
tree exp;
{
if (TREE_CODE (exp) == CALL_EXPR
&& TREE_CODE (TREE_OPERAND (exp, 0)) == ADDR_EXPR
&& (TREE_CODE (TREE_OPERAND (TREE_OPERAND (exp, 0), 0))
== FUNCTION_DECL)
&& (special_function_p (TREE_OPERAND (TREE_OPERAND (exp, 0), 0),
0) & ECF_MAY_BE_ALLOCA))
return true;
return false;
}
static int
flags_from_decl_or_type (exp)
tree exp;
{
int flags = 0;
tree type = exp;
if (DECL_P (exp))
{
type = TREE_TYPE (exp);
if (DECL_P (exp) && DECL_IS_MALLOC (exp))
flags |= ECF_MALLOC;
if (DECL_P (exp) && DECL_IS_PURE (exp))
flags |= ECF_PURE | ECF_LIBCALL_BLOCK;
if (TREE_NOTHROW (exp))
flags |= ECF_NOTHROW;
}
if (TREE_READONLY (exp) && ! TREE_THIS_VOLATILE (exp))
flags |= ECF_CONST | ECF_LIBCALL_BLOCK;
if (TREE_THIS_VOLATILE (exp))
flags |= ECF_NORETURN;
if (TREE_CODE (type) == FUNCTION_TYPE && TYPE_RETURNS_STACK_DEPRESSED (type))
{
flags |= ECF_SP_DEPRESSED;
flags &= ~(ECF_PURE | ECF_CONST | ECF_LIBCALL_BLOCK);
}
return flags;
}
static void
precompute_register_parameters (num_actuals, args, reg_parm_seen)
int num_actuals;
struct arg_data *args;
int *reg_parm_seen;
{
int i;
*reg_parm_seen = 0;
for (i = 0; i < num_actuals; i++)
if (args[i].reg != 0 && ! args[i].pass_on_stack)
{
*reg_parm_seen = 1;
if (args[i].value == 0)
{
push_temp_slots ();
args[i].value = expand_expr (args[i].tree_value, NULL_RTX,
VOIDmode, 0);
preserve_temp_slots (args[i].value);
pop_temp_slots ();
emit_queue ();
}
if (CONSTANT_P (args[i].value)
&& !LEGITIMATE_CONSTANT_P (args[i].value))
args[i].value = force_reg (args[i].mode, args[i].value);
if (args[i].mode != TYPE_MODE (TREE_TYPE (args[i].tree_value)))
args[i].value
= convert_modes (args[i].mode,
TYPE_MODE (TREE_TYPE (args[i].tree_value)),
args[i].value, args[i].unsignedp);
if ((! (GET_CODE (args[i].value) == REG
|| (GET_CODE (args[i].value) == SUBREG
&& GET_CODE (SUBREG_REG (args[i].value)) == REG)))
&& args[i].mode != BLKmode
&& rtx_cost (args[i].value, SET) > COSTS_N_INSNS (1)
&& ((SMALL_REGISTER_CLASSES && *reg_parm_seen)
|| preserve_subexpressions_p ()))
args[i].value = copy_to_mode_reg (args[i].mode, args[i].value);
}
}
#ifdef REG_PARM_STACK_SPACE
static rtx
save_fixed_argument_area (reg_parm_stack_space, argblock,
low_to_save, high_to_save)
int reg_parm_stack_space;
rtx argblock;
int *low_to_save;
int *high_to_save;
{
int i;
rtx save_area = NULL_RTX;
#ifdef ARGS_GROW_DOWNWARD
for (i = 0; i < reg_parm_stack_space + 1; i++)
#else
for (i = 0; i < reg_parm_stack_space; i++)
#endif
{
if (i >= highest_outgoing_arg_in_use
|| stack_usage_map[i] == 0)
continue;
if (*low_to_save == -1)
*low_to_save = i;
*high_to_save = i;
}
if (*low_to_save >= 0)
{
int num_to_save = *high_to_save - *low_to_save + 1;
enum machine_mode save_mode
= mode_for_size (num_to_save * BITS_PER_UNIT, MODE_INT, 1);
rtx stack_area;
if ((*low_to_save & (MIN (GET_MODE_SIZE (save_mode),
BIGGEST_ALIGNMENT / UNITS_PER_WORD) - 1)))
save_mode = BLKmode;
#ifdef ARGS_GROW_DOWNWARD
stack_area
= gen_rtx_MEM (save_mode,
memory_address (save_mode,
plus_constant (argblock,
- *high_to_save)));
#else
stack_area = gen_rtx_MEM (save_mode,
memory_address (save_mode,
plus_constant (argblock,
*low_to_save)));
#endif
set_mem_align (stack_area, PARM_BOUNDARY);
if (save_mode == BLKmode)
{
save_area = assign_stack_temp (BLKmode, num_to_save, 0);
emit_block_move (validize_mem (save_area), stack_area,
GEN_INT (num_to_save), BLOCK_OP_CALL_PARM);
}
else
{
save_area = gen_reg_rtx (save_mode);
emit_move_insn (save_area, stack_area);
}
}
return save_area;
}
static void
restore_fixed_argument_area (save_area, argblock, high_to_save, low_to_save)
rtx save_area;
rtx argblock;
int high_to_save;
int low_to_save;
{
enum machine_mode save_mode = GET_MODE (save_area);
#ifdef ARGS_GROW_DOWNWARD
rtx stack_area
= gen_rtx_MEM (save_mode,
memory_address (save_mode,
plus_constant (argblock,
- high_to_save)));
#else
rtx stack_area
= gen_rtx_MEM (save_mode,
memory_address (save_mode,
plus_constant (argblock,
low_to_save)));
#endif
if (save_mode != BLKmode)
emit_move_insn (stack_area, save_area);
else
emit_block_move (stack_area, validize_mem (save_area),
GEN_INT (high_to_save - low_to_save + 1),
BLOCK_OP_CALL_PARM);
}
#endif
static void
store_unaligned_arguments_into_pseudos (args, num_actuals)
struct arg_data *args;
int num_actuals;
{
int i, j;
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))
< (unsigned int) MIN (BIGGEST_ALIGNMENT, BITS_PER_WORD)))
{
int bytes = int_size_in_bytes (TREE_TYPE (args[i].tree_value));
int big_endian_correction = 0;
args[i].n_aligned_regs
= args[i].partial ? args[i].partial
: (bytes + (UNITS_PER_WORD - 1)) / UNITS_PER_WORD;
args[i].aligned_regs = (rtx *) xmalloc (sizeof (rtx)
* args[i].n_aligned_regs);
if (BYTES_BIG_ENDIAN
&& bytes < UNITS_PER_WORD)
big_endian_correction = (BITS_PER_WORD - (bytes * BITS_PER_UNIT));
for (j = 0; j < args[i].n_aligned_regs; j++)
{
rtx reg = gen_reg_rtx (word_mode);
rtx word = operand_subword_force (args[i].value, j, BLKmode);
int bitsize = MIN (bytes * BITS_PER_UNIT, BITS_PER_WORD);
args[i].aligned_regs[j] = reg;
emit_move_insn (reg, const0_rtx);
bytes -= bitsize / BITS_PER_UNIT;
store_bit_field (reg, bitsize, big_endian_correction, word_mode,
extract_bit_field (word, bitsize, 0, 1, NULL_RTX,
word_mode, word_mode,
BITS_PER_WORD),
BITS_PER_WORD);
}
}
}
static void
initialize_argument_information (num_actuals, args, args_size, n_named_args,
actparms, fndecl, args_so_far,
reg_parm_stack_space, old_stack_level,
old_pending_adj, must_preallocate,
ecf_flags)
int num_actuals ATTRIBUTE_UNUSED;
struct arg_data *args;
struct args_size *args_size;
int n_named_args ATTRIBUTE_UNUSED;
tree actparms;
tree fndecl;
CUMULATIVE_ARGS *args_so_far;
int reg_parm_stack_space;
rtx *old_stack_level;
int *old_pending_adj;
int *must_preallocate;
int *ecf_flags;
{
int inc;
int argpos;
struct args_size alignment_pad;
int i;
tree p;
args_size->constant = 0;
args_size->var = 0;
if (PUSH_ARGS_REVERSED)
{
i = num_actuals - 1, inc = -1;
}
else
{
i = 0, inc = 1;
}
for (p = actparms, argpos = 0; p; p = TREE_CHAIN (p), i += inc, argpos++)
{
tree type = TREE_TYPE (TREE_VALUE (p));
int unsignedp;
enum machine_mode mode;
args[i].tree_value = TREE_VALUE (p);
if (type == error_mark_node || !COMPLETE_TYPE_P (type))
args[i].tree_value = integer_zero_node, type = integer_type_node;
if (TREE_CODE (type) == UNION_TYPE && TYPE_TRANSPARENT_UNION (type))
type = TREE_TYPE (TYPE_FIELDS (type));
if ((TREE_CODE (TYPE_SIZE (type)) != INTEGER_CST
&& contains_placeholder_p (TYPE_SIZE (type)))
|| TREE_ADDRESSABLE (type)
#ifdef FUNCTION_ARG_PASS_BY_REFERENCE
|| FUNCTION_ARG_PASS_BY_REFERENCE (*args_so_far, TYPE_MODE (type),
type, argpos < n_named_args)
#endif
)
{
if (current_function_is_thunk
#ifdef FUNCTION_ARG_CALLEE_COPIES
|| (FUNCTION_ARG_CALLEE_COPIES (*args_so_far, TYPE_MODE (type),
type, argpos < n_named_args)
&& !(TREE_CODE (args[i].tree_value) == VAR_DECL
&& REG_P (DECL_RTL (args[i].tree_value)))
&& ! TREE_ADDRESSABLE (type))
#endif
)
{
if (TREE_CODE (args[i].tree_value) == TARGET_EXPR
&& (DECL_P (TREE_OPERAND (args[i].tree_value, 1)))
&& ! REG_P (DECL_RTL (TREE_OPERAND (args[i].tree_value, 1))))
args[i].tree_value = TREE_OPERAND (args[i].tree_value, 1);
args[i].tree_value = build1 (ADDR_EXPR,
build_pointer_type (type),
args[i].tree_value);
type = build_pointer_type (type);
}
else if (TREE_CODE (args[i].tree_value) == TARGET_EXPR)
{
args[i].tree_value = build1 (ADDR_EXPR,
build_pointer_type (type),
args[i].tree_value);
type = build_pointer_type (type);
}
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))))
{
rtx size_rtx = expr_size (TREE_VALUE (p));
if (*old_stack_level == 0)
{
emit_stack_save (SAVE_BLOCK, old_stack_level, NULL_RTX);
*old_pending_adj = pending_stack_adjust;
pending_stack_adjust = 0;
}
copy = gen_rtx_MEM (BLKmode,
allocate_dynamic_stack_space
(size_rtx, NULL_RTX, TYPE_ALIGN (type)));
set_mem_attributes (copy, type, 1);
}
else
copy = assign_temp (type, 0, 1, 0);
store_expr (args[i].tree_value, copy, 0);
*ecf_flags &= ~(ECF_CONST | ECF_PURE | ECF_LIBCALL_BLOCK);
args[i].tree_value = build1 (ADDR_EXPR,
build_pointer_type (type),
make_tree (type, copy));
type = build_pointer_type (type);
}
}
mode = TYPE_MODE (type);
unsignedp = TREE_UNSIGNED (type);
#ifdef PROMOTE_FUNCTION_ARGS
mode = promote_mode (type, mode, &unsignedp, 1);
#endif
args[i].unsignedp = unsignedp;
args[i].mode = mode;
args[i].reg = FUNCTION_ARG (*args_so_far, mode, type,
argpos < n_named_args);
args[i].no_stack_slot =
#ifdef NO_REG_PARM_STACK_SPACE
NO_REG_PARM_STACK_SPACE (args_so_far, args[i].reg);
#else
0;
#endif
#ifdef FUNCTION_INCOMING_ARG
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
#ifdef FUNCTION_ARG_PARTIAL_NREGS
if (args[i].reg)
args[i].partial
= FUNCTION_ARG_PARTIAL_NREGS (*args_so_far, mode, type,
argpos < n_named_args);
#endif
args[i].pass_on_stack = MUST_PASS_IN_STACK (mode, type);
if (args[i].reg && GET_CODE (args[i].reg) == PARALLEL
&& XEXP (XVECEXP (args[i].reg, 0, 0), 0) == 0)
args[i].pass_on_stack = 1;
if (TREE_ADDRESSABLE (type)
|| (args[i].pass_on_stack && args[i].reg != 0))
*must_preallocate = 1;
if (TREE_ADDRESSABLE (type))
*ecf_flags &= ~ECF_LIBCALL_BLOCK;
if ((args[i].reg == 0 || args[i].partial != 0
|| reg_parm_stack_space > 0
|| args[i].pass_on_stack)
&& !args[i].no_stack_slot)
locate_and_pad_parm (mode, type,
#ifdef STACK_PARMS_IN_REG_PARM_AREA
1,
#else
args[i].reg != 0,
#endif
fndecl, args_size, &args[i].offset,
&args[i].size, &alignment_pad);
#ifndef ARGS_GROW_DOWNWARD
args[i].slot_offset = *args_size;
#endif
args[i].alignment_pad = alignment_pad;
if (reg_parm_stack_space == 0 && ! args[i].pass_on_stack)
args[i].size.constant -= ((args[i].partial * UNITS_PER_WORD)
/ (PARM_BOUNDARY / BITS_PER_UNIT)
* (PARM_BOUNDARY / BITS_PER_UNIT));
args_size->constant += args[i].size.constant;
if (args[i].size.var)
{
ADD_PARM_SIZE (*args_size, args[i].size.var);
}
#ifdef ARGS_GROW_DOWNWARD
args[i].slot_offset = *args_size;
args[i].slot_offset.constant = -args_size->constant;
if (args_size->var)
SUB_PARM_SIZE (args[i].slot_offset, args_size->var);
#endif
FUNCTION_ARG_ADVANCE (*args_so_far, TYPE_MODE (type), type,
argpos < n_named_args);
}
}
static int
compute_argument_block_size (reg_parm_stack_space, args_size,
preferred_stack_boundary)
int reg_parm_stack_space;
struct args_size *args_size;
int preferred_stack_boundary ATTRIBUTE_UNUSED;
{
int unadjusted_args_size = args_size->constant;
if (ACCUMULATE_OUTGOING_ARGS && preferred_stack_boundary > STACK_BOUNDARY)
preferred_stack_boundary = STACK_BOUNDARY;
if (args_size->var)
{
args_size->var = ARGS_SIZE_TREE (*args_size);
args_size->constant = 0;
preferred_stack_boundary /= BITS_PER_UNIT;
if (preferred_stack_boundary > 1)
{
if (stack_pointer_delta & (preferred_stack_boundary - 1))
abort ();
args_size->var = round_up (args_size->var, preferred_stack_boundary);
}
if (reg_parm_stack_space > 0)
{
args_size->var
= size_binop (MAX_EXPR, args_size->var,
ssize_int (reg_parm_stack_space));
#ifndef OUTGOING_REG_PARM_STACK_SPACE
args_size->var
= size_binop (MINUS_EXPR, args_size->var,
ssize_int (reg_parm_stack_space));
#endif
}
}
else
{
preferred_stack_boundary /= BITS_PER_UNIT;
if (preferred_stack_boundary < 1)
preferred_stack_boundary = 1;
args_size->constant = (((args_size->constant
+ stack_pointer_delta
+ preferred_stack_boundary - 1)
/ preferred_stack_boundary
* preferred_stack_boundary)
- stack_pointer_delta);
args_size->constant = MAX (args_size->constant,
reg_parm_stack_space);
#ifdef MAYBE_REG_PARM_STACK_SPACE
if (reg_parm_stack_space == 0)
args_size->constant = 0;
#endif
#ifndef OUTGOING_REG_PARM_STACK_SPACE
args_size->constant -= reg_parm_stack_space;
#endif
}
return unadjusted_args_size;
}
static void
precompute_arguments (flags, num_actuals, args)
int flags;
int num_actuals;
struct arg_data *args;
{
int i;
for (i = 0; i < num_actuals; i++)
if ((flags & ECF_LIBCALL_BLOCK)
|| calls_function (args[i].tree_value, !ACCUMULATE_OUTGOING_ARGS))
{
enum machine_mode mode;
if (TREE_ADDRESSABLE (TREE_TYPE (args[i].tree_value)))
abort ();
args[i].value
= expand_expr (args[i].tree_value, NULL_RTX, VOIDmode, 0);
emit_queue ();
args[i].initial_value = args[i].value
= protect_from_queue (args[i].value, 0);
mode = TYPE_MODE (TREE_TYPE (args[i].tree_value));
if (mode != args[i].mode)
{
args[i].value
= convert_modes (args[i].mode, mode,
args[i].value, args[i].unsignedp);
#ifdef PROMOTE_FOR_CALL_ONLY
if (GET_CODE (args[i].value) == REG
&& GET_MODE_CLASS (args[i].mode) == MODE_INT)
{
args[i].initial_value
= gen_lowpart_SUBREG (mode, args[i].value);
SUBREG_PROMOTED_VAR_P (args[i].initial_value) = 1;
SUBREG_PROMOTED_UNSIGNED_SET (args[i].initial_value,
args[i].unsignedp);
}
#endif
}
}
}
static int
finalize_must_preallocate (must_preallocate, num_actuals, args, args_size)
int must_preallocate;
int num_actuals;
struct arg_data *args;
struct args_size *args_size;
{
if (! must_preallocate)
{
int partial_seen = 0;
int copy_to_evaluate_size = 0;
int i;
for (i = 0; i < num_actuals && ! must_preallocate; i++)
{
if (args[i].partial > 0 && ! args[i].pass_on_stack)
partial_seen = 1;
else if (partial_seen && args[i].reg == 0)
must_preallocate = 1;
if (TYPE_MODE (TREE_TYPE (args[i].tree_value)) == BLKmode
&& (TREE_CODE (args[i].tree_value) == CALL_EXPR
|| TREE_CODE (args[i].tree_value) == TARGET_EXPR
|| TREE_CODE (args[i].tree_value) == COND_EXPR
|| TREE_ADDRESSABLE (TREE_TYPE (args[i].tree_value))))
copy_to_evaluate_size
+= int_size_in_bytes (TREE_TYPE (args[i].tree_value));
}
if (copy_to_evaluate_size * 2 >= args_size->constant
&& args_size->constant > 0)
must_preallocate = 1;
}
return must_preallocate;
}
static void
compute_argument_addresses (args, argblock, num_actuals)
struct arg_data *args;
rtx argblock;
int num_actuals;
{
if (argblock)
{
rtx arg_reg = argblock;
int i, arg_offset = 0;
if (GET_CODE (argblock) == PLUS)
arg_reg = XEXP (argblock, 0), arg_offset = INTVAL (XEXP (argblock, 1));
for (i = 0; i < num_actuals; i++)
{
rtx offset = ARGS_SIZE_RTX (args[i].offset);
rtx slot_offset = ARGS_SIZE_RTX (args[i].slot_offset);
rtx addr;
if (! args[i].pass_on_stack && args[i].reg != 0)
continue;
if (GET_CODE (offset) == CONST_INT)
addr = plus_constant (arg_reg, INTVAL (offset));
else
addr = gen_rtx_PLUS (Pmode, arg_reg, offset);
addr = plus_constant (addr, arg_offset);
args[i].stack = gen_rtx_MEM (args[i].mode, addr);
set_mem_align (args[i].stack, PARM_BOUNDARY);
set_mem_attributes (args[i].stack,
TREE_TYPE (args[i].tree_value), 1);
if (GET_CODE (slot_offset) == CONST_INT)
addr = plus_constant (arg_reg, INTVAL (slot_offset));
else
addr = gen_rtx_PLUS (Pmode, arg_reg, slot_offset);
addr = plus_constant (addr, arg_offset);
args[i].stack_slot = gen_rtx_MEM (args[i].mode, addr);
set_mem_align (args[i].stack_slot, PARM_BOUNDARY);
set_mem_attributes (args[i].stack_slot,
TREE_TYPE (args[i].tree_value), 1);
set_mem_alias_set (args[i].stack, 0);
set_mem_alias_set (args[i].stack_slot, 0);
}
}
}
static rtx
rtx_for_function_call (fndecl, addr)
tree fndecl;
tree addr;
{
rtx funexp;
if (fndecl)
{
if (! TREE_USED (fndecl))
{
assemble_external (fndecl);
TREE_USED (fndecl) = 1;
}
funexp = XEXP (DECL_RTL (fndecl), 0);
}
else
{
rtx funaddr;
push_temp_slots ();
funaddr = funexp
= expand_expr (addr, NULL_RTX, VOIDmode, 0);
pop_temp_slots ();
emit_queue ();
}
return funexp;
}
static void
load_register_parameters (args, num_actuals, call_fusage, flags,
is_sibcall, sibcall_failure)
struct arg_data *args;
int num_actuals;
rtx *call_fusage;
int flags;
int is_sibcall;
int *sibcall_failure;
{
int i, j;
#ifdef LOAD_ARGS_REVERSED
for (i = num_actuals - 1; i >= 0; i--)
#else
for (i = 0; i < num_actuals; i++)
#endif
{
rtx reg = ((flags & ECF_SIBCALL)
? args[i].tail_call_reg : args[i].reg);
int partial = args[i].partial;
int nregs;
if (reg)
{
rtx before_arg = get_last_insn ();
nregs = (partial ? partial
: (TYPE_MODE (TREE_TYPE (args[i].tree_value)) == BLKmode
? ((int_size_in_bytes (TREE_TYPE (args[i].tree_value))
+ (ABI_UNITS_PER_WORD - 1)) / ABI_UNITS_PER_WORD)
: -1));
if (GET_CODE (reg) == PARALLEL)
emit_group_load (reg, args[i].value,
int_size_in_bytes (TREE_TYPE (args[i].tree_value)));
else if (nregs == -1)
emit_move_insn (reg, args[i].value);
else if (args[i].n_aligned_regs != 0)
for (j = 0; j < args[i].n_aligned_regs; j++)
emit_move_insn (gen_rtx_REG (word_mode, REGNO (reg) + j),
args[i].aligned_regs[j]);
else if (partial == 0 || args[i].pass_on_stack)
move_block_to_reg (REGNO (reg),
validize_mem (args[i].value), nregs,
args[i].mode);
if (is_sibcall
&& check_sibcall_argument_overlap (before_arg, &args[i], 0))
*sibcall_failure = 1;
if (GET_CODE (reg) == PARALLEL)
use_group_regs (call_fusage, reg);
else if (nregs == -1)
use_reg (call_fusage, reg);
else
use_regs (call_fusage, REGNO (reg), nregs == 0 ? 1 : nregs);
}
}
}
static rtx
try_to_integrate (fndecl, actparms, target, ignore, type, structure_value_addr)
tree fndecl;
tree actparms;
rtx target;
int ignore;
tree type;
rtx structure_value_addr;
{
rtx temp;
rtx before_call;
int i;
rtx old_stack_level = 0;
int reg_parm_stack_space = 0;
#ifdef REG_PARM_STACK_SPACE
#ifdef MAYBE_REG_PARM_STACK_SPACE
reg_parm_stack_space = MAYBE_REG_PARM_STACK_SPACE;
#else
reg_parm_stack_space = REG_PARM_STACK_SPACE (fndecl);
#endif
#endif
before_call = get_last_insn ();
timevar_push (TV_INTEGRATION);
temp = expand_inline_function (fndecl, actparms, target,
ignore, type,
structure_value_addr);
timevar_pop (TV_INTEGRATION);
if (temp != (rtx) (size_t) - 1)
{
if (ACCUMULATE_OUTGOING_ARGS)
{
for (i = reg_parm_stack_space - 1; i >= 0; i--)
if (i < highest_outgoing_arg_in_use && stack_usage_map[i] != 0)
break;
if (stack_arg_under_construction || i >= 0)
{
rtx first_insn
= before_call ? NEXT_INSN (before_call) : get_insns ();
rtx insn = NULL_RTX, seq;
if (DECL_SAVED_INSNS (fndecl)->outgoing_args_size == 0)
for (insn = first_insn; insn; insn = NEXT_INSN (insn))
if (GET_CODE (insn) == CALL_INSN)
break;
if (insn)
{
int adjust = (DECL_SAVED_INSNS (fndecl)->outgoing_args_size
+ reg_parm_stack_space);
start_sequence ();
emit_stack_save (SAVE_BLOCK, &old_stack_level, NULL_RTX);
allocate_dynamic_stack_space (GEN_INT (adjust),
NULL_RTX, BITS_PER_UNIT);
seq = get_insns ();
end_sequence ();
emit_insn_before (seq, first_insn);
emit_stack_restore (SAVE_BLOCK, old_stack_level, NULL_RTX);
}
}
}
if (temp != target && rtx_equal_p (temp, target))
return target;
return temp;
}
if (DECL_INLINE (fndecl) && warn_inline && !flag_no_inline
&& optimize > 0 && !TREE_ADDRESSABLE (fndecl))
{
warning_with_decl (fndecl, "inlining failed in call to `%s'");
warning ("called from here");
}
(*lang_hooks.mark_addressable) (fndecl);
return (rtx) (size_t) - 1;
}
static int
combine_pending_stack_adjustment_and_call (unadjusted_args_size,
args_size,
preferred_unit_stack_boundary)
int unadjusted_args_size;
struct args_size *args_size;
int preferred_unit_stack_boundary;
{
HOST_WIDE_INT adjustment;
HOST_WIDE_INT unadjusted_alignment;
unadjusted_alignment
= ((stack_pointer_delta + unadjusted_args_size)
% preferred_unit_stack_boundary);
unadjusted_alignment
= (unadjusted_alignment
- (pending_stack_adjust % preferred_unit_stack_boundary));
adjustment = pending_stack_adjust;
if (preferred_unit_stack_boundary > 1)
{
if (unadjusted_alignment > 0)
adjustment -= preferred_unit_stack_boundary - unadjusted_alignment;
else
adjustment += unadjusted_alignment;
}
args_size->constant
= pending_stack_adjust - adjustment + unadjusted_args_size;
return adjustment;
}
static int
check_sibcall_argument_overlap_1 (x)
rtx x;
{
RTX_CODE code;
int i, j;
unsigned int k;
const char *fmt;
if (x == NULL_RTX)
return 0;
code = GET_CODE (x);
if (code == MEM)
{
if (XEXP (x, 0) == current_function_internal_arg_pointer)
i = 0;
else if (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)
i = INTVAL (XEXP (XEXP (x, 0), 1));
else
return 0;
#ifdef ARGS_GROW_DOWNWARD
i = -i - GET_MODE_SIZE (GET_MODE (x));
#endif
for (k = 0; k < GET_MODE_SIZE (GET_MODE (x)); k++)
if (i + k < stored_args_map->n_bits
&& TEST_BIT (stored_args_map, i + k))
return 1;
return 0;
}
fmt = GET_RTX_FORMAT (code);
for (i = 0; i < GET_RTX_LENGTH (code); i++, fmt++)
{
if (*fmt == 'e')
{
if (check_sibcall_argument_overlap_1 (XEXP (x, i)))
return 1;
}
else if (*fmt == 'E')
{
for (j = 0; j < XVECLEN (x, i); j++)
if (check_sibcall_argument_overlap_1 (XVECEXP (x, i, j)))
return 1;
}
}
return 0;
}
static int
check_sibcall_argument_overlap (insn, arg, mark_stored_args_map)
rtx insn;
struct arg_data *arg;
int mark_stored_args_map;
{
int low, high;
if (insn == NULL_RTX)
insn = get_insns ();
else
insn = NEXT_INSN (insn);
for (; insn; insn = NEXT_INSN (insn))
if (INSN_P (insn)
&& check_sibcall_argument_overlap_1 (PATTERN (insn)))
break;
if (mark_stored_args_map)
{
#ifdef ARGS_GROW_DOWNWARD
low = -arg->slot_offset.constant - arg->size.constant;
#else
low = arg->slot_offset.constant;
#endif
for (high = low + arg->size.constant; low < high; low++)
SET_BIT (stored_args_map, low);
}
return insn != NULL_RTX;
}
static tree
fix_unsafe_tree (t)
tree t;
{
switch (unsafe_for_reeval (t))
{
case 0:
break;
case 1:
t = unsave_expr (t);
break;
case 2:
{
tree var = build_decl (VAR_DECL, NULL_TREE,
TREE_TYPE (t));
SET_DECL_RTL (var,
expand_expr (t, NULL_RTX, VOIDmode, EXPAND_NORMAL));
t = var;
}
break;
default:
abort ();
}
return t;
}
rtx
expand_call (exp, target, ignore)
tree exp;
rtx target;
int ignore;
{
static int currently_expanding_call = 0;
tree actparms = TREE_OPERAND (exp, 1);
rtx funexp;
rtx funexp_keep = NULL_RTX;
rtx tail_recursion_insns = NULL_RTX;
rtx normal_call_insns = NULL_RTX;
rtx tail_call_insns = NULL_RTX;
tree funtype;
tree saved_return_type;
tree fndecl = 0;
rtx insn;
int try_tail_call = 1;
int try_tail_recursion = 1;
int pass;
rtx valreg;
rtx structure_value_addr = 0;
int structure_value_addr_parm = 0;
HOST_WIDE_INT struct_value_size = 0;
int pcc_struct_value = 0;
int num_actuals;
int n_named_args;
struct arg_data *args;
struct args_size args_size;
struct args_size adjusted_args_size;
int unadjusted_args_size;
CUMULATIVE_ARGS args_so_far;
int reg_parm_seen;
int must_preallocate = !PUSH_ARGS;
int reg_parm_stack_space = 0;
rtx argblock = 0;
int flags = 0;
int is_integrable = 0;
#ifdef REG_PARM_STACK_SPACE
int low_to_save = -1, high_to_save;
rtx save_area = 0;
#endif
int initial_highest_arg_in_use = highest_outgoing_arg_in_use;
char *initial_stack_usage_map = stack_usage_map;
int old_stack_arg_under_construction = 0;
rtx old_stack_level = 0;
int old_pending_adj = 0;
int old_inhibit_defer_pop = inhibit_defer_pop;
int old_stack_allocated;
rtx call_fusage;
tree p = TREE_OPERAND (exp, 0);
tree addr = TREE_OPERAND (exp, 0);
int i;
HOST_WIDE_INT preferred_stack_boundary;
HOST_WIDE_INT preferred_unit_stack_boundary;
if (TREE_NOTHROW (exp))
flags |= ECF_NOTHROW;
fndecl = get_callee_fndecl (exp);
if (fndecl)
{
if (!flag_no_inline
&& fndecl != current_function_decl
&& DECL_INLINE (fndecl)
&& DECL_SAVED_INSNS (fndecl)
&& DECL_SAVED_INSNS (fndecl)->inlinable)
is_integrable = 1;
else if (! TREE_ADDRESSABLE (fndecl))
{
if (DECL_INLINE (fndecl) && warn_inline && !flag_no_inline
&& optimize > 0)
{
warning_with_decl (fndecl, "can't inline call to `%s'");
warning ("called from here");
}
(*lang_hooks.mark_addressable) (fndecl);
}
flags |= flags_from_decl_or_type (fndecl);
}
else
flags |= flags_from_decl_or_type (TREE_TYPE (TREE_TYPE (p)));
#ifdef REG_PARM_STACK_SPACE
#ifdef MAYBE_REG_PARM_STACK_SPACE
reg_parm_stack_space = MAYBE_REG_PARM_STACK_SPACE;
#else
reg_parm_stack_space = REG_PARM_STACK_SPACE (fndecl);
#endif
#endif
#ifndef OUTGOING_REG_PARM_STACK_SPACE
if (reg_parm_stack_space > 0 && PUSH_ARGS)
must_preallocate = 1;
#endif
if (warn_aggregate_return && AGGREGATE_TYPE_P (TREE_TYPE (exp)))
warning ("function call has aggregate value");
if (aggregate_value_p (exp))
{
flags &= ~(ECF_CONST | ECF_PURE | ECF_LIBCALL_BLOCK);
#ifdef PCC_STATIC_STRUCT_RETURN
{
pcc_struct_value = 1;
if (is_integrable)
{
if (! TREE_ADDRESSABLE (fndecl))
(*lang_hooks.mark_addressable) (fndecl);
is_integrable = 0;
}
}
#else
{
struct_value_size = int_size_in_bytes (TREE_TYPE (exp));
if (target && GET_CODE (target) == MEM)
structure_value_addr = XEXP (target, 0);
else
{
rtx d = assign_temp (TREE_TYPE (exp), 1, 1, 1);
mark_temp_addr_taken (d);
structure_value_addr = XEXP (d, 0);
target = 0;
}
}
#endif
}
if (is_integrable)
{
rtx temp = try_to_integrate (fndecl, actparms, target,
ignore, TREE_TYPE (exp),
structure_value_addr);
if (temp != (rtx) (size_t) - 1)
return temp;
}
preferred_stack_boundary = PREFERRED_STACK_BOUNDARY;
funtype = TREE_TYPE (addr);
if (! POINTER_TYPE_P (funtype))
abort ();
funtype = TREE_TYPE (funtype);
saved_return_type = TREE_TYPE (funtype);
TREE_TYPE (funtype) = TREE_TYPE (exp);
flags |= special_function_p (fndecl, flags);
if (flags & ECF_MAY_BE_ALLOCA)
current_function_calls_alloca = 1;
#ifdef REARRANGE_ARG_LIST
INIT_CUMULATIVE_ARGS (args_so_far, funtype, NULL_RTX, (fndecl == 0));
TREE_OPERAND (exp, 1)
= actparms = REARRANGE_ARG_LIST (args_so_far, actparms);
#endif
if (structure_value_addr && struct_value_rtx == 0)
{
rtx temp = (GET_CODE (structure_value_addr) != REG
|| (ACCUMULATE_OUTGOING_ARGS
&& stack_arg_under_construction
&& structure_value_addr == virtual_outgoing_args_rtx)
? copy_addr_to_reg (structure_value_addr)
: structure_value_addr);
actparms
= tree_cons (error_mark_node,
make_tree (build_pointer_type (TREE_TYPE (funtype)),
temp),
actparms);
structure_value_addr_parm = 1;
}
for (p = actparms, num_actuals = 0; p; p = TREE_CHAIN (p))
num_actuals++;
if ((STRICT_ARGUMENT_NAMING
|| ! PRETEND_OUTGOING_VARARGS_NAMED)
&& TYPE_ARG_TYPES (funtype) != 0)
n_named_args
= (list_length (TYPE_ARG_TYPES (funtype))
- (STRICT_ARGUMENT_NAMING ? 0 : 1)
+ structure_value_addr_parm);
else
n_named_args = num_actuals;
#ifndef REARRANGE_ARG_LIST
INIT_CUMULATIVE_ARGS (args_so_far, funtype, NULL_RTX, (fndecl == 0));
#endif
args = (struct arg_data *) alloca (num_actuals * sizeof (struct arg_data));
memset ((char *) args, 0, num_actuals * sizeof (struct arg_data));
initialize_argument_information (num_actuals, args, &args_size,
n_named_args, actparms, fndecl,
&args_so_far, reg_parm_stack_space,
&old_stack_level, &old_pending_adj,
&must_preallocate, &flags);
if (args_size.var)
{
flags &= ~ECF_LIBCALL_BLOCK;
must_preallocate = 1;
}
must_preallocate = finalize_must_preallocate (must_preallocate,
num_actuals, args,
&args_size);
if (structure_value_addr
&& (reg_mentioned_p (virtual_stack_dynamic_rtx, structure_value_addr)
|| reg_mentioned_p (virtual_outgoing_args_rtx,
structure_value_addr))
&& (args_size.var
|| (!ACCUMULATE_OUTGOING_ARGS && args_size.constant)))
structure_value_addr = copy_to_reg (structure_value_addr);
if (currently_expanding_call++ != 0
|| !flag_optimize_sibling_calls
|| !rtx_equal_function_value_matters
|| any_pending_cleanups (1)
|| args_size.var)
try_tail_call = try_tail_recursion = 0;
if (!try_tail_recursion
|| TREE_CODE (addr) != ADDR_EXPR
|| TREE_OPERAND (addr, 0) != current_function_decl)
try_tail_recursion = 0;
if (
#ifdef HAVE_sibcall_epilogue
!HAVE_sibcall_epilogue
#else
1
#endif
|| !try_tail_call
|| structure_value_addr != NULL_RTX
#ifndef MAGIC_INDIRECT_CALL_REG
|| fndecl == NULL_TREE
#endif
|| (flags & (ECF_RETURNS_TWICE | ECF_LONGJMP | ECF_NORETURN))
|| (fndecl && !FUNCTION_OK_FOR_SIBCALL (fndecl))
|| args_size.constant > current_function_args_size
|| RETURN_POPS_ARGS (fndecl, funtype, args_size.constant)
!= RETURN_POPS_ARGS (current_function_decl,
TREE_TYPE (current_function_decl),
current_function_args_size))
try_tail_call = 0;
if (try_tail_call || try_tail_recursion)
{
int end, inc;
actparms = NULL_TREE;
if (PUSH_ARGS_REVERSED)
{
inc = 1;
i = 0;
end = num_actuals;
}
else
{
inc = -1;
i = num_actuals - 1;
end = -1;
}
for (; i != end; i += inc)
{
args[i].tree_value = fix_unsafe_tree (args[i].tree_value);
if (try_tail_recursion)
actparms = tree_cons (NULL_TREE, args[i].tree_value, actparms);
}
if (!fndecl)
addr = fix_unsafe_tree (addr);
if (any_pending_cleanups (1))
try_tail_call = try_tail_recursion = 0;
}
if (try_tail_recursion)
{
int save_pending_stack_adjust = pending_stack_adjust;
int save_stack_pointer_delta = stack_pointer_delta;
emit_queue ();
start_sequence ();
expand_start_target_temps ();
if (optimize_tail_recursion (actparms, get_last_insn ()))
{
if (any_pending_cleanups (1))
try_tail_call = try_tail_recursion = 0;
else
tail_recursion_insns = get_insns ();
}
expand_end_target_temps ();
end_sequence ();
pending_stack_adjust = save_pending_stack_adjust;
stack_pointer_delta = save_stack_pointer_delta;
}
if (profile_arc_flag && (flags & ECF_FORK_OR_EXEC))
{
emit_library_call (gen_rtx_SYMBOL_REF (Pmode, "__bb_fork_func"),
LCT_ALWAYS_RETURN,
VOIDmode, 0);
}
if (cfun->preferred_stack_boundary < preferred_stack_boundary
&& fndecl != current_function_decl)
cfun->preferred_stack_boundary = preferred_stack_boundary;
preferred_unit_stack_boundary = preferred_stack_boundary / BITS_PER_UNIT;
function_call_count++;
if ( !fndecl )
funexp_keep = rtx_for_function_call (fndecl, addr);
for (pass = 0; pass < 2; pass++)
{
int sibcall_failure = 0;
int save_pending_stack_adjust = 0;
int save_stack_pointer_delta = 0;
rtx insns;
rtx before_call, next_arg_reg;
if (pass == 0)
{
if (! try_tail_call)
continue;
emit_queue ();
save_pending_stack_adjust = pending_stack_adjust;
save_stack_pointer_delta = stack_pointer_delta;
}
if (pass)
flags &= ~ECF_SIBCALL;
else
flags |= ECF_SIBCALL;
argblock = 0;
call_fusage = 0;
start_sequence ();
if (pass == 0)
{
expand_start_target_temps ();
}
if (pending_stack_adjust >= 32
|| (pending_stack_adjust > 0
&& (flags & (ECF_MAY_BE_ALLOCA | ECF_SP_DEPRESSED)))
|| pass == 0)
do_pending_stack_adjust ();
if (pass && (flags & ECF_LIBCALL_BLOCK))
NO_DEFER_POP;
#ifdef FINAL_REG_PARM_STACK_SPACE
reg_parm_stack_space = FINAL_REG_PARM_STACK_SPACE (args_size.constant,
args_size.var);
#endif
if (pass)
precompute_arguments (flags, num_actuals, args);
if (pass && (flags & (ECF_LIBCALL_BLOCK | ECF_MALLOC)))
start_sequence ();
adjusted_args_size = args_size;
unadjusted_args_size
= compute_argument_block_size (reg_parm_stack_space,
&adjusted_args_size,
(pass == 0 ? 0
: preferred_stack_boundary));
old_stack_allocated = stack_pointer_delta - pending_stack_adjust;
if (pass == 0)
{
argblock = virtual_incoming_args_rtx;
argblock
#ifdef STACK_GROWS_DOWNWARD
= plus_constant (argblock, current_function_pretend_args_size);
#else
= plus_constant (argblock, -current_function_pretend_args_size);
#endif
stored_args_map = sbitmap_alloc (args_size.constant);
sbitmap_zero (stored_args_map);
}
else if (adjusted_args_size.var != 0)
{
if (old_stack_level == 0)
{
emit_stack_save (SAVE_BLOCK, &old_stack_level, NULL_RTX);
old_pending_adj = pending_stack_adjust;
pending_stack_adjust = 0;
old_stack_arg_under_construction = stack_arg_under_construction;
stack_arg_under_construction = 0;
}
argblock = push_block (ARGS_SIZE_RTX (adjusted_args_size), 0, 0);
}
else
{
int needed = adjusted_args_size.constant;
if (needed > current_function_outgoing_args_size)
current_function_outgoing_args_size = needed;
if (must_preallocate)
{
if (ACCUMULATE_OUTGOING_ARGS)
{
#ifndef OUTGOING_REG_PARM_STACK_SPACE
needed += reg_parm_stack_space;
#endif
#ifdef ARGS_GROW_DOWNWARD
highest_outgoing_arg_in_use = MAX (initial_highest_arg_in_use,
needed + 1);
#else
highest_outgoing_arg_in_use = MAX (initial_highest_arg_in_use,
needed);
#endif
stack_usage_map
= (char *) alloca (highest_outgoing_arg_in_use);
if (initial_highest_arg_in_use)
memcpy (stack_usage_map, initial_stack_usage_map,
initial_highest_arg_in_use);
if (initial_highest_arg_in_use != highest_outgoing_arg_in_use)
memset (&stack_usage_map[initial_highest_arg_in_use], 0,
(highest_outgoing_arg_in_use
- initial_highest_arg_in_use));
needed = 0;
argblock = virtual_outgoing_args_rtx;
}
else
{
if (inhibit_defer_pop == 0)
{
needed
= (combine_pending_stack_adjustment_and_call
(unadjusted_args_size,
&adjusted_args_size,
preferred_unit_stack_boundary));
needed = unadjusted_args_size - needed;
if (needed < 0)
{
pending_stack_adjust = -needed;
do_pending_stack_adjust ();
needed = 0;
}
else
pending_stack_adjust = 0;
}
if (needed == 0)
argblock = virtual_outgoing_args_rtx;
else
argblock = push_block (GEN_INT (needed), 0, 0);
argblock = copy_to_reg (argblock);
if (stack_arg_under_construction)
{
#ifndef OUTGOING_REG_PARM_STACK_SPACE
rtx push_size = GEN_INT (reg_parm_stack_space
+ adjusted_args_size.constant);
#else
rtx push_size = GEN_INT (adjusted_args_size.constant);
#endif
if (old_stack_level == 0)
{
emit_stack_save (SAVE_BLOCK, &old_stack_level,
NULL_RTX);
old_pending_adj = pending_stack_adjust;
pending_stack_adjust = 0;
old_stack_arg_under_construction
= stack_arg_under_construction;
stack_arg_under_construction = 0;
stack_usage_map = (char *)
alloca (highest_outgoing_arg_in_use);
memset (stack_usage_map, 0, highest_outgoing_arg_in_use);
highest_outgoing_arg_in_use = 0;
}
allocate_dynamic_stack_space (push_size, NULL_RTX,
BITS_PER_UNIT);
}
for (i = 0; i < num_actuals; i++)
if (args[i].pass_on_stack)
{
argblock = copy_addr_to_reg (argblock);
break;
}
}
}
}
compute_argument_addresses (args, argblock, num_actuals);
if (PUSH_ARGS_REVERSED && argblock == 0
&& adjusted_args_size.constant != unadjusted_args_size)
{
if (pending_stack_adjust
&& ! (flags & ECF_LIBCALL_BLOCK)
&& ! inhibit_defer_pop)
{
pending_stack_adjust
= (combine_pending_stack_adjustment_and_call
(unadjusted_args_size,
&adjusted_args_size,
preferred_unit_stack_boundary));
do_pending_stack_adjust ();
}
else if (argblock == 0)
anti_adjust_stack (GEN_INT (adjusted_args_size.constant
- unadjusted_args_size));
}
NO_DEFER_POP;
if ( !fndecl )
funexp = funexp_keep;
else
funexp = rtx_for_function_call (fndecl, addr);
valreg = 0;
if (TYPE_MODE (TREE_TYPE (exp)) != VOIDmode
&& ! structure_value_addr)
{
if (pcc_struct_value)
valreg = hard_function_value (build_pointer_type (TREE_TYPE (exp)),
fndecl, (pass == 0));
else
valreg = hard_function_value (TREE_TYPE (exp), fndecl, (pass == 0));
}
precompute_register_parameters (num_actuals, args, ®_parm_seen);
#ifdef REG_PARM_STACK_SPACE
if (ACCUMULATE_OUTGOING_ARGS && pass)
save_area = save_fixed_argument_area (reg_parm_stack_space, argblock,
&low_to_save, &high_to_save);
#endif
for (i = 0; i < num_actuals; i++)
if (args[i].reg == 0 || args[i].pass_on_stack)
{
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 (STRICT_ALIGNMENT)
store_unaligned_arguments_into_pseudos (args, num_actuals);
if (reg_parm_seen)
for (i = 0; i < num_actuals; i++)
if (args[i].partial != 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 (!PUSH_ARGS_REVERSED && argblock == 0)
anti_adjust_stack (GEN_INT (adjusted_args_size.constant
- unadjusted_args_size));
#ifdef OUTGOING_REG_PARM_STACK_SPACE
if (!ACCUMULATE_OUTGOING_ARGS
&& must_preallocate == 0 && reg_parm_stack_space > 0)
anti_adjust_stack (GEN_INT (reg_parm_stack_space));
#endif
if (pass != 0 && structure_value_addr && ! structure_value_addr_parm)
{
emit_move_insn (struct_value_rtx,
force_reg (Pmode,
force_operand (structure_value_addr,
NULL_RTX)));
if (GET_CODE (struct_value_rtx) == REG)
use_reg (&call_fusage, struct_value_rtx);
}
funexp = prepare_call_address (funexp, fndecl, &call_fusage,
reg_parm_seen, pass == 0);
load_register_parameters (args, num_actuals, &call_fusage, flags,
pass == 0, &sibcall_failure);
emit_queue ();
emit_rtx_call_feedback_counter (exp);
before_call = get_last_insn ();
#ifdef FUNCTION_INCOMING_ARG
if (pass == 0)
next_arg_reg = FUNCTION_INCOMING_ARG (args_so_far, VOIDmode,
void_type_node, 1);
else
#endif
next_arg_reg = FUNCTION_ARG (args_so_far, VOIDmode,
void_type_node, 1);
#ifdef MAGIC_INDIRECT_CALL_REG
if (!fndecl)
{
rtx magic_reg = gen_rtx_REG (SImode, MAGIC_INDIRECT_CALL_REG);
emit_move_insn (magic_reg, funexp);
use_reg (&call_fusage, magic_reg);
}
#endif
if (pass && stack_pointer_delta % preferred_unit_stack_boundary)
abort ();
emit_call_1 (funexp, fndecl, funtype, unadjusted_args_size,
adjusted_args_size.constant, struct_value_size,
next_arg_reg, valreg, old_inhibit_defer_pop, call_fusage,
flags, & args_so_far);
TREE_TYPE (funtype) = saved_return_type;
if (pass
&& old_stack_allocated != stack_pointer_delta - pending_stack_adjust)
abort ();
if (pass && (flags & ECF_LIBCALL_BLOCK))
{
rtx insns;
if (valreg == 0 || GET_CODE (valreg) == PARALLEL)
{
insns = get_insns ();
end_sequence ();
emit_insn (insns);
}
else
{
rtx note = 0;
rtx temp = gen_reg_rtx (GET_MODE (valreg));
if (TREE_CODE (TREE_TYPE (exp)) == POINTER_TYPE)
mark_reg_pointer (temp,
TYPE_ALIGN (TREE_TYPE (TREE_TYPE (exp))));
for (i = 0; i < num_actuals; i++)
note = gen_rtx_EXPR_LIST (VOIDmode,
args[i].initial_value, note);
note = gen_rtx_EXPR_LIST (VOIDmode, funexp, note);
insns = get_insns ();
end_sequence ();
if (flags & ECF_PURE)
note = gen_rtx_EXPR_LIST (VOIDmode,
gen_rtx_USE (VOIDmode,
gen_rtx_MEM (BLKmode,
gen_rtx_SCRATCH (VOIDmode))),
note);
emit_libcall_block (insns, temp, valreg, note);
valreg = temp;
}
}
else if (pass && (flags & ECF_MALLOC))
{
rtx temp = gen_reg_rtx (GET_MODE (valreg));
rtx last, insns;
if (TREE_CODE (TREE_TYPE (exp)) == POINTER_TYPE)
mark_reg_pointer (temp, BIGGEST_ALIGNMENT);
emit_move_insn (temp, valreg);
last = get_last_insn ();
REG_NOTES (last) =
gen_rtx_EXPR_LIST (REG_NOALIAS, temp, REG_NOTES (last));
insns = get_insns ();
end_sequence ();
emit_insn (insns);
valreg = temp;
}
if ((flags & (ECF_NORETURN | ECF_LONGJMP)) || pass == 0)
{
rtx last = get_last_insn ();
while (GET_CODE (last) != CALL_INSN)
{
last = PREV_INSN (last);
if (last == before_call)
abort ();
}
emit_barrier_after (last);
}
if (flags & ECF_LONGJMP)
current_function_calls_longjmp = 1;
if (target != 0 && GET_CODE (target) == MEM
&& structure_value_addr != 0 && RTX_UNCHANGING_P (target))
emit_insn (gen_rtx_CLOBBER (VOIDmode, target));
if (any_pending_cleanups (1))
{
if (target && REG_P (target)
&& REGNO (target) < FIRST_PSEUDO_REGISTER)
target = 0;
sibcall_failure = 1;
}
if (TYPE_MODE (TREE_TYPE (exp)) == VOIDmode
|| ignore)
target = const0_rtx;
else if (structure_value_addr)
{
if (target == 0 || GET_CODE (target) != MEM)
{
target
= gen_rtx_MEM (TYPE_MODE (TREE_TYPE (exp)),
memory_address (TYPE_MODE (TREE_TYPE (exp)),
structure_value_addr));
set_mem_attributes (target, exp, 1);
}
}
else if (pcc_struct_value)
{
target = gen_rtx_MEM (TYPE_MODE (TREE_TYPE (exp)),
copy_to_reg (valreg));
set_mem_attributes (target, exp, 1);
}
else if (GET_CODE (valreg) == PARALLEL)
{
if (target == 0 || pass == 1)
{
tree nt = build_qualified_type (TREE_TYPE (exp),
(TYPE_QUALS (TREE_TYPE (exp))
| TYPE_QUAL_CONST));
target = assign_temp (nt, 0, 1, 1);
preserve_temp_slots (target);
}
if (! rtx_equal_p (target, valreg))
emit_group_store (target, valreg,
int_size_in_bytes (TREE_TYPE (exp)));
sibcall_failure = 1;
}
else if (target
&& GET_MODE (target) == TYPE_MODE (TREE_TYPE (exp))
&& GET_MODE (target) == GET_MODE (valreg))
{
emit_move_insn (target, valreg);
}
else if (TYPE_MODE (TREE_TYPE (exp)) == BLKmode)
{
target = copy_blkmode_from_reg (target, valreg, TREE_TYPE (exp));
sibcall_failure = 1;
}
else
target = copy_to_reg (valreg);
#ifdef PROMOTE_FUNCTION_RETURN
if (GET_CODE (target) == REG
&& TYPE_MODE (TREE_TYPE (exp)) != BLKmode
&& GET_MODE (target) != TYPE_MODE (TREE_TYPE (exp)))
{
tree type = TREE_TYPE (exp);
int unsignedp = TREE_UNSIGNED (type);
int offset = 0;
if (GET_MODE (target)
!= promote_mode (type, TYPE_MODE (type), &unsignedp, 1))
abort ();
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);
}
#endif
if (old_stack_level && ! (flags & ECF_SP_DEPRESSED))
{
emit_stack_restore (SAVE_BLOCK, old_stack_level, NULL_RTX);
pending_stack_adjust = old_pending_adj;
stack_arg_under_construction = old_stack_arg_under_construction;
highest_outgoing_arg_in_use = initial_highest_arg_in_use;
stack_usage_map = initial_stack_usage_map;
sibcall_failure = 1;
}
else if (ACCUMULATE_OUTGOING_ARGS && pass)
{
#ifdef REG_PARM_STACK_SPACE
if (save_area)
{
restore_fixed_argument_area (save_area, argblock,
high_to_save, low_to_save);
}
#endif
for (i = 0; i < num_actuals; i++)
if (args[i].save_area)
{
enum machine_mode save_mode = GET_MODE (args[i].save_area);
rtx stack_area
= gen_rtx_MEM (save_mode,
memory_address (save_mode,
XEXP (args[i].stack_slot, 0)));
if (save_mode != BLKmode)
emit_move_insn (stack_area, args[i].save_area);
else
emit_block_move (stack_area, args[i].save_area,
GEN_INT (args[i].size.constant),
BLOCK_OP_CALL_PARM);
}
highest_outgoing_arg_in_use = initial_highest_arg_in_use;
stack_usage_map = initial_stack_usage_map;
}
if ((flags & ECF_MAY_BE_ALLOCA) && nonlocal_goto_handler_slots != 0)
emit_stack_save (SAVE_NONLOCAL, &nonlocal_goto_stack_level, NULL_RTX);
for (i = 0; i < num_actuals; ++i)
if (args[i].aligned_regs)
free (args[i].aligned_regs);
if (pass == 0)
{
expand_end_target_temps ();
}
insns = get_insns ();
end_sequence ();
if (pass == 0)
{
tail_call_insns = insns;
pending_stack_adjust = save_pending_stack_adjust;
stack_pointer_delta = save_stack_pointer_delta;
for (i = 0; i < num_actuals; i++)
{
args[i].value = 0;
args[i].aligned_regs = 0;
args[i].stack = 0;
}
sbitmap_free (stored_args_map);
}
else
normal_call_insns = insns;
if ( ( ! sibcall_failure)
#if defined (TARGET_POWERPC)
&& args_size.constant > 32
#endif
&& args_size.constant > cfun->unrounded_args_size)
{
sibcall_failure = 1;
}
if (sibcall_failure)
tail_call_insns = NULL_RTX;
}
for (insn = normal_call_insns; insn; insn = NEXT_INSN (insn))
if (GET_CODE (insn) == CALL_INSN
&& GET_CODE (PATTERN (insn)) == CALL_PLACEHOLDER)
replace_call_placeholder (insn, sibcall_use_normal);
for (insn = tail_call_insns; insn; insn = NEXT_INSN (insn))
if (GET_CODE (insn) == CALL_INSN
&& GET_CODE (PATTERN (insn)) == CALL_PLACEHOLDER)
replace_call_placeholder (insn, sibcall_use_normal);
for (insn = tail_recursion_insns; insn; insn = NEXT_INSN (insn))
if (GET_CODE (insn) == CALL_INSN
&& GET_CODE (PATTERN (insn)) == CALL_PLACEHOLDER)
replace_call_placeholder (insn, sibcall_use_normal);
if (tail_recursion_insns || tail_call_insns)
{
if (tail_recursion_insns)
LABEL_PRESERVE_P (tail_recursion_label) = 1;
emit_call_insn (gen_rtx_CALL_PLACEHOLDER (VOIDmode, normal_call_insns,
tail_call_insns,
tail_recursion_insns,
tail_recursion_label));
}
else
emit_insn (normal_call_insns);
currently_expanding_call--;
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);
save_stack_pointer ();
}
return target;
}
static rtx
emit_library_call_value_1 (retval, orgfun, value, fn_type, outmode, nargs, p)
int retval;
rtx orgfun;
rtx value;
enum libcall_type fn_type;
enum machine_mode outmode;
int nargs;
va_list p;
{
struct args_size args_size;
struct args_size original_args_size;
int argnum;
rtx fun;
int inc;
int count;
struct args_size alignment_pad;
rtx argblock = 0;
CUMULATIVE_ARGS args_so_far;
struct arg
{
rtx value;
enum machine_mode mode;
rtx reg;
int partial;
struct args_size offset;
struct args_size size;
rtx save_area;
};
struct arg *argvec;
int old_inhibit_defer_pop = inhibit_defer_pop;
rtx call_fusage = 0;
rtx mem_value = 0;
rtx valreg;
int pcc_struct_value = 0;
int struct_value_size = 0;
int flags;
int reg_parm_stack_space = 0;
int needed;
rtx before_call;
tree tfom;
#ifdef REG_PARM_STACK_SPACE
int low_to_save = -1, high_to_save = 0;
rtx save_area = 0;
#endif
int initial_highest_arg_in_use = highest_outgoing_arg_in_use;
char *initial_stack_usage_map = stack_usage_map;
#ifdef REG_PARM_STACK_SPACE
#ifdef MAYBE_REG_PARM_STACK_SPACE
reg_parm_stack_space = MAYBE_REG_PARM_STACK_SPACE;
#else
reg_parm_stack_space = REG_PARM_STACK_SPACE ((tree) 0);
#endif
#endif
flags = ECF_NOTHROW;
switch (fn_type)
{
case LCT_NORMAL:
break;
case LCT_CONST:
flags |= ECF_CONST;
break;
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;
case LCT_THROW:
flags = ECF_NORETURN;
break;
case LCT_ALWAYS_RETURN:
flags = ECF_ALWAYS_RETURN;
break;
case LCT_RETURNS_TWICE:
flags = ECF_RETURNS_TWICE;
break;
}
fun = orgfun;
if (cfun->preferred_stack_boundary < PREFERRED_STACK_BOUNDARY)
cfun->preferred_stack_boundary = PREFERRED_STACK_BOUNDARY;
if (outmode != VOIDmode)
{
tfom = (*lang_hooks.types.type_for_mode) (outmode, 0);
if (aggregate_value_p (tfom))
{
#ifdef PCC_STATIC_STRUCT_RETURN
rtx pointer_reg
= hard_function_value (build_pointer_type (tfom), 0, 0);
mem_value = gen_rtx_MEM (outmode, pointer_reg);
pcc_struct_value = 1;
if (value == 0)
value = gen_reg_rtx (outmode);
#else
struct_value_size = GET_MODE_SIZE (outmode);
if (value != 0 && GET_CODE (value) == MEM)
mem_value = value;
else
mem_value = assign_temp (tfom, 0, 1, 1);
#endif
flags &= ~(ECF_CONST | ECF_PURE | ECF_LIBCALL_BLOCK);
}
}
else
tfom = void_type_node;
argvec = (struct arg *) alloca ((nargs + 1) * sizeof (struct arg));
memset ((char *) argvec, 0, (nargs + 1) * sizeof (struct arg));
#ifdef INIT_CUMULATIVE_LIBCALL_ARGS
INIT_CUMULATIVE_LIBCALL_ARGS (args_so_far, outmode, fun);
#else
INIT_CUMULATIVE_ARGS (args_so_far, NULL_TREE, fun, 0);
#endif
args_size.constant = 0;
args_size.var = 0;
count = 0;
if (flags & ECF_LIBCALL_BLOCK)
start_sequence ();
push_temp_slots ();
if (mem_value && struct_value_rtx == 0 && ! pcc_struct_value)
{
rtx addr = XEXP (mem_value, 0);
nargs++;
if (GET_CODE (addr) != REG && GET_CODE (addr) != MEM
&& ! (CONSTANT_P (addr) && LEGITIMATE_CONSTANT_P (addr)))
addr = force_operand (addr, NULL_RTX);
argvec[count].value = addr;
argvec[count].mode = Pmode;
argvec[count].partial = 0;
argvec[count].reg = FUNCTION_ARG (args_so_far, Pmode, NULL_TREE, 1);
#ifdef FUNCTION_ARG_PARTIAL_NREGS
if (FUNCTION_ARG_PARTIAL_NREGS (args_so_far, Pmode, NULL_TREE, 1))
abort ();
#endif
locate_and_pad_parm (Pmode, NULL_TREE,
#ifdef STACK_PARMS_IN_REG_PARM_AREA
1,
#else
argvec[count].reg != 0,
#endif
NULL_TREE, &args_size, &argvec[count].offset,
&argvec[count].size, &alignment_pad);
if (argvec[count].reg == 0 || argvec[count].partial != 0
|| reg_parm_stack_space > 0)
args_size.constant += argvec[count].size.constant;
FUNCTION_ARG_ADVANCE (args_so_far, Pmode, (tree) 0, 1);
count++;
}
for (; count < nargs; count++)
{
rtx val = va_arg (p, rtx);
enum machine_mode mode = va_arg (p, enum machine_mode);
if (mode == BLKmode
|| (GET_MODE (val) != mode && GET_MODE (val) != VOIDmode))
abort ();
#ifdef LIBGCC_NEEDS_DOUBLE
if (LIBGCC_NEEDS_DOUBLE && mode == SFmode)
val = convert_modes (DFmode, SFmode, val, 0), mode = DFmode;
#endif
if (GET_CODE (val) != REG && GET_CODE (val) != MEM
&& ! (CONSTANT_P (val) && LEGITIMATE_CONSTANT_P (val)))
val = force_operand (val, NULL_RTX);
#ifdef FUNCTION_ARG_PASS_BY_REFERENCE
if (FUNCTION_ARG_PASS_BY_REFERENCE (args_so_far, mode, NULL_TREE, 1))
{
rtx slot;
int must_copy = 1
#ifdef FUNCTION_ARG_CALLEE_COPIES
&& ! FUNCTION_ARG_CALLEE_COPIES (args_so_far, mode,
NULL_TREE, 1)
#endif
;
if (flags & ECF_LIBCALL_BLOCK)
{
rtx insns = get_insns ();
end_sequence ();
emit_insn (insns);
}
flags &= ~(ECF_CONST | ECF_PURE | ECF_LIBCALL_BLOCK);
if (flags & ECF_CONST)
{
flags &= ~ECF_CONST;
flags |= ECF_PURE;
}
if (GET_MODE (val) == MEM && ! must_copy)
slot = val;
else if (must_copy)
{
slot = assign_temp ((*lang_hooks.types.type_for_mode) (mode, 0),
0, 1, 1);
emit_move_insn (slot, val);
}
else
{
tree type = (*lang_hooks.types.type_for_mode) (mode, 0);
slot = gen_rtx_MEM (mode,
expand_expr (build1 (ADDR_EXPR,
build_pointer_type
(type),
make_tree (type, val)),
NULL_RTX, VOIDmode, 0));
}
call_fusage = gen_rtx_EXPR_LIST (VOIDmode,
gen_rtx_USE (VOIDmode, slot),
call_fusage);
if (must_copy)
call_fusage = gen_rtx_EXPR_LIST (VOIDmode,
gen_rtx_CLOBBER (VOIDmode,
slot),
call_fusage);
mode = Pmode;
val = force_operand (XEXP (slot, 0), NULL_RTX);
}
#endif
argvec[count].value = val;
argvec[count].mode = mode;
argvec[count].reg = FUNCTION_ARG (args_so_far, mode, NULL_TREE, 1);
#ifdef FUNCTION_ARG_PARTIAL_NREGS
argvec[count].partial
= FUNCTION_ARG_PARTIAL_NREGS (args_so_far, mode, NULL_TREE, 1);
#else
argvec[count].partial = 0;
#endif
locate_and_pad_parm (mode, NULL_TREE,
#ifdef STACK_PARMS_IN_REG_PARM_AREA
1,
#else
argvec[count].reg != 0,
#endif
NULL_TREE, &args_size, &argvec[count].offset,
&argvec[count].size, &alignment_pad);
if (argvec[count].size.var)
abort ();
if (reg_parm_stack_space == 0 && argvec[count].partial)
argvec[count].size.constant -= argvec[count].partial * UNITS_PER_WORD;
if (argvec[count].reg == 0 || argvec[count].partial != 0
|| reg_parm_stack_space > 0)
args_size.constant += argvec[count].size.constant;
FUNCTION_ARG_ADVANCE (args_so_far, mode, (tree) 0, 1);
}
#ifdef FINAL_REG_PARM_STACK_SPACE
reg_parm_stack_space = FINAL_REG_PARM_STACK_SPACE (args_size.constant,
args_size.var);
#endif
assemble_external_libcall (fun);
original_args_size = args_size;
args_size.constant = (((args_size.constant
+ stack_pointer_delta
+ STACK_BYTES - 1)
/ STACK_BYTES
* STACK_BYTES)
- stack_pointer_delta);
args_size.constant = MAX (args_size.constant,
reg_parm_stack_space);
#ifndef OUTGOING_REG_PARM_STACK_SPACE
args_size.constant -= reg_parm_stack_space;
#endif
if (args_size.constant > current_function_outgoing_args_size)
current_function_outgoing_args_size = args_size.constant;
if (ACCUMULATE_OUTGOING_ARGS)
{
needed = args_size.constant;
#ifndef OUTGOING_REG_PARM_STACK_SPACE
needed += reg_parm_stack_space;
#endif
#ifdef ARGS_GROW_DOWNWARD
highest_outgoing_arg_in_use = MAX (initial_highest_arg_in_use,
needed + 1);
#else
highest_outgoing_arg_in_use = MAX (initial_highest_arg_in_use,
needed);
#endif
stack_usage_map = (char *) alloca (highest_outgoing_arg_in_use);
if (initial_highest_arg_in_use)
memcpy (stack_usage_map, initial_stack_usage_map,
initial_highest_arg_in_use);
if (initial_highest_arg_in_use != highest_outgoing_arg_in_use)
memset (&stack_usage_map[initial_highest_arg_in_use], 0,
highest_outgoing_arg_in_use - initial_highest_arg_in_use);
needed = 0;
if (virtuals_instantiated)
argblock = plus_constant (stack_pointer_rtx, STACK_POINTER_OFFSET);
else
argblock = virtual_outgoing_args_rtx;
}
else
{
if (!PUSH_ARGS)
argblock = push_block (GEN_INT (args_size.constant), 0, 0);
}
if (argblock == 0 && PUSH_ARGS_REVERSED)
anti_adjust_stack (GEN_INT (args_size.constant
- original_args_size.constant));
if (PUSH_ARGS_REVERSED)
{
inc = -1;
argnum = nargs - 1;
}
else
{
inc = 1;
argnum = 0;
}
#ifdef REG_PARM_STACK_SPACE
if (ACCUMULATE_OUTGOING_ARGS)
{
#ifdef ARGS_GROW_DOWNWARD
for (count = 0; count < reg_parm_stack_space + 1; count++)
#else
for (count = 0; count < reg_parm_stack_space; count++)
#endif
{
if (count >= highest_outgoing_arg_in_use
|| stack_usage_map[count] == 0)
continue;
if (low_to_save == -1)
low_to_save = count;
high_to_save = count;
}
if (low_to_save >= 0)
{
int num_to_save = high_to_save - low_to_save + 1;
enum machine_mode save_mode
= mode_for_size (num_to_save * BITS_PER_UNIT, MODE_INT, 1);
rtx stack_area;
if ((low_to_save & (MIN (GET_MODE_SIZE (save_mode),
BIGGEST_ALIGNMENT / UNITS_PER_WORD) - 1)))
save_mode = BLKmode;
#ifdef ARGS_GROW_DOWNWARD
stack_area = gen_rtx_MEM (save_mode,
memory_address (save_mode,
plus_constant (argblock,
-high_to_save)));
#else
stack_area = gen_rtx_MEM (save_mode,
memory_address (save_mode,
plus_constant (argblock,
low_to_save)));
#endif
if (save_mode == BLKmode)
{
save_area = assign_stack_temp (BLKmode, num_to_save, 0);
set_mem_align (save_area, PARM_BOUNDARY);
emit_block_move (save_area, stack_area, GEN_INT (num_to_save),
BLOCK_OP_CALL_PARM);
}
else
{
save_area = gen_reg_rtx (save_mode);
emit_move_insn (save_area, stack_area);
}
}
}
#endif
for (count = 0; count < nargs; count++, argnum += inc)
{
enum machine_mode mode = argvec[argnum].mode;
rtx val = argvec[argnum].value;
rtx reg = argvec[argnum].reg;
int partial = argvec[argnum].partial;
int lower_bound = 0, upper_bound = 0, i;
if (! (reg != 0 && partial == 0))
{
if (ACCUMULATE_OUTGOING_ARGS)
{
#ifdef ARGS_GROW_DOWNWARD
upper_bound = -argvec[argnum].offset.constant + 1;
lower_bound = upper_bound - argvec[argnum].size.constant;
#else
lower_bound = argvec[argnum].offset.constant;
upper_bound = lower_bound + argvec[argnum].size.constant;
#endif
for (i = lower_bound; i < upper_bound; i++)
if (stack_usage_map[i]
&& i > reg_parm_stack_space)
break;
if (i != upper_bound)
{
enum machine_mode save_mode
= mode_for_size (argvec[argnum].size.constant
* BITS_PER_UNIT,
MODE_INT, 1);
rtx stack_area
= gen_rtx_MEM
(save_mode,
memory_address
(save_mode,
plus_constant (argblock,
argvec[argnum].offset.constant)));
argvec[argnum].save_area = gen_reg_rtx (save_mode);
emit_move_insn (argvec[argnum].save_area, stack_area);
}
}
emit_push_insn (val, mode, NULL_TREE, NULL_RTX, PARM_BOUNDARY,
partial, reg, 0, argblock,
GEN_INT (argvec[argnum].offset.constant),
reg_parm_stack_space, ARGS_SIZE_RTX (alignment_pad));
if (ACCUMULATE_OUTGOING_ARGS)
for (i = lower_bound; i < upper_bound; i++)
stack_usage_map[i] = 1;
NO_DEFER_POP;
}
}
if (argblock == 0 && !PUSH_ARGS_REVERSED)
anti_adjust_stack (GEN_INT (args_size.constant
- original_args_size.constant));
if (PUSH_ARGS_REVERSED)
argnum = nargs - 1;
else
argnum = 0;
fun = prepare_call_address (fun, NULL_TREE, &call_fusage, 0, 0);
for (count = 0; count < nargs; count++, argnum += inc)
{
rtx val = argvec[argnum].value;
rtx reg = argvec[argnum].reg;
int partial = argvec[argnum].partial;
if (reg != 0 && GET_CODE (reg) == PARALLEL)
emit_group_load (reg, val, GET_MODE_SIZE (GET_MODE (val)));
else if (reg != 0 && partial == 0)
emit_move_insn (reg, val);
NO_DEFER_POP;
}
for (count = 0; count < nargs; count++)
{
rtx reg = argvec[count].reg;
if (reg != 0 && GET_CODE (reg) == PARALLEL)
use_group_regs (&call_fusage, reg);
else if (reg != 0)
use_reg (&call_fusage, reg);
}
if (mem_value != 0 && struct_value_rtx != 0 && ! pcc_struct_value)
{
emit_move_insn (struct_value_rtx,
force_reg (Pmode,
force_operand (XEXP (mem_value, 0),
NULL_RTX)));
if (GET_CODE (struct_value_rtx) == REG)
use_reg (&call_fusage, struct_value_rtx);
}
NO_DEFER_POP;
valreg = (mem_value == 0 && outmode != VOIDmode
? hard_libcall_value (outmode) : NULL_RTX);
if (stack_pointer_delta & (PREFERRED_STACK_BOUNDARY / BITS_PER_UNIT - 1))
abort ();
before_call = get_last_insn ();
emit_call_1 (fun,
get_identifier (XSTR (orgfun, 0)),
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),
valreg,
old_inhibit_defer_pop + 1, call_fusage, flags, & args_so_far);
if (flags & (ECF_NORETURN | ECF_LONGJMP))
{
rtx last = get_last_insn ();
while (GET_CODE (last) != CALL_INSN)
{
last = PREV_INSN (last);
if (last == before_call)
abort ();
}
emit_barrier_after (last);
}
OK_DEFER_POP;
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, outmode);
valreg = temp;
}
temp = gen_reg_rtx (GET_MODE (valreg));
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 ();
if (outmode != VOIDmode && retval)
{
if (mem_value)
{
if (value == 0)
value = mem_value;
if (value != mem_value)
emit_move_insn (value, mem_value);
}
else if (GET_CODE (valreg) == PARALLEL)
{
if (value == 0)
value = gen_reg_rtx (outmode);
emit_group_store (value, valreg, outmode);
}
else if (value != 0)
emit_move_insn (value, valreg);
else
value = valreg;
}
if (ACCUMULATE_OUTGOING_ARGS)
{
#ifdef REG_PARM_STACK_SPACE
if (save_area)
{
enum machine_mode save_mode = GET_MODE (save_area);
#ifdef ARGS_GROW_DOWNWARD
rtx stack_area
= gen_rtx_MEM (save_mode,
memory_address (save_mode,
plus_constant (argblock,
- high_to_save)));
#else
rtx stack_area
= gen_rtx_MEM (save_mode,
memory_address (save_mode,
plus_constant (argblock, low_to_save)));
#endif
set_mem_align (stack_area, PARM_BOUNDARY);
if (save_mode != BLKmode)
emit_move_insn (stack_area, save_area);
else
emit_block_move (stack_area, save_area,
GEN_INT (high_to_save - low_to_save + 1),
BLOCK_OP_CALL_PARM);
}
#endif
for (count = 0; count < nargs; count++)
if (argvec[count].save_area)
{
enum machine_mode save_mode = GET_MODE (argvec[count].save_area);
rtx stack_area
= gen_rtx_MEM (save_mode,
memory_address
(save_mode,
plus_constant (argblock,
argvec[count].offset.constant)));
emit_move_insn (stack_area, argvec[count].save_area);
}
highest_outgoing_arg_in_use = initial_highest_arg_in_use;
stack_usage_map = initial_stack_usage_map;
}
return value;
}
void
emit_library_call VPARAMS((rtx orgfun, enum libcall_type fn_type,
enum machine_mode outmode, int nargs, ...))
{
VA_OPEN (p, nargs);
VA_FIXEDARG (p, rtx, orgfun);
VA_FIXEDARG (p, int, fn_type);
VA_FIXEDARG (p, enum machine_mode, outmode);
VA_FIXEDARG (p, int, nargs);
emit_library_call_value_1 (0, orgfun, NULL_RTX, fn_type, outmode, nargs, p);
VA_CLOSE (p);
}
rtx
emit_library_call_value VPARAMS((rtx orgfun, rtx value,
enum libcall_type fn_type,
enum machine_mode outmode, int nargs, ...))
{
rtx result;
VA_OPEN (p, nargs);
VA_FIXEDARG (p, rtx, orgfun);
VA_FIXEDARG (p, rtx, value);
VA_FIXEDARG (p, int, fn_type);
VA_FIXEDARG (p, enum machine_mode, outmode);
VA_FIXEDARG (p, int, nargs);
result = emit_library_call_value_1 (1, orgfun, value, fn_type, outmode,
nargs, p);
VA_CLOSE (p);
return result;
}
static int
store_one_arg (arg, argblock, flags, variable_size, reg_parm_stack_space)
struct arg_data *arg;
rtx argblock;
int flags;
int variable_size ATTRIBUTE_UNUSED;
int reg_parm_stack_space;
{
tree pval = arg->tree_value;
rtx reg = 0;
int partial = 0;
int used = 0;
int i, lower_bound = 0, upper_bound = 0;
int sibcall_failure = 0;
if (TREE_CODE (pval) == ERROR_MARK)
return 1;
push_temp_slots ();
if (ACCUMULATE_OUTGOING_ARGS && !(flags & ECF_SIBCALL))
{
if (argblock && ! variable_size && arg->stack)
{
#ifdef ARGS_GROW_DOWNWARD
if (GET_CODE (XEXP (arg->stack_slot, 0)) == PLUS)
upper_bound = -INTVAL (XEXP (XEXP (arg->stack_slot, 0), 1)) + 1;
else
upper_bound = 0;
lower_bound = upper_bound - arg->size.constant;
#else
if (GET_CODE (XEXP (arg->stack_slot, 0)) == PLUS)
lower_bound = INTVAL (XEXP (XEXP (arg->stack_slot, 0), 1));
else
lower_bound = 0;
upper_bound = lower_bound + arg->size.constant;
#endif
for (i = lower_bound; i < upper_bound; i++)
if (stack_usage_map[i]
&& i > reg_parm_stack_space)
break;
if (i != upper_bound)
{
enum machine_mode save_mode
= mode_for_size (arg->size.constant * BITS_PER_UNIT, MODE_INT, 1);
rtx stack_area
= gen_rtx_MEM (save_mode,
memory_address (save_mode,
XEXP (arg->stack_slot, 0)));
if (save_mode == BLKmode)
{
tree ot = TREE_TYPE (arg->tree_value);
tree nt = build_qualified_type (ot, (TYPE_QUALS (ot)
| TYPE_QUAL_CONST));
arg->save_area = assign_temp (nt, 0, 1, 1);
preserve_temp_slots (arg->save_area);
emit_block_move (validize_mem (arg->save_area), stack_area,
expr_size (arg->tree_value),
BLOCK_OP_CALL_PARM);
}
else
{
arg->save_area = gen_reg_rtx (save_mode);
emit_move_insn (arg->save_area, stack_area);
}
}
}
}
if (! arg->pass_on_stack)
{
if (flags & ECF_SIBCALL)
reg = arg->tail_call_reg;
else
reg = arg->reg;
partial = arg->partial;
}
if (reg != 0 && partial == 0)
abort ();
if (arg->n_aligned_regs != 0)
reg = 0;
if (arg->value == 0)
{
if (arg->pass_on_stack)
stack_arg_under_construction++;
arg->value = expand_expr (pval,
(partial
|| TYPE_MODE (TREE_TYPE (pval)) != arg->mode)
? NULL_RTX : arg->stack,
VOIDmode, EXPAND_STACK_PARM);
if (arg->mode != TYPE_MODE (TREE_TYPE (pval)))
arg->value = convert_modes (arg->mode, TYPE_MODE (TREE_TYPE (pval)),
arg->value, arg->unsignedp);
if (arg->pass_on_stack)
stack_arg_under_construction--;
}
if (flags & ECF_MAY_BE_ALLOCA)
do_pending_stack_adjust ();
if (arg->value == arg->stack)
;
else if (arg->mode != BLKmode)
{
int size;
size = GET_MODE_SIZE (arg->mode);
#ifdef PUSH_ROUNDING
size = PUSH_ROUNDING (size);
#endif
used = size;
if (none != FUNCTION_ARG_PADDING (arg->mode, TREE_TYPE (pval)))
used = (((size + PARM_BOUNDARY / BITS_PER_UNIT - 1)
/ (PARM_BOUNDARY / BITS_PER_UNIT))
* (PARM_BOUNDARY / BITS_PER_UNIT));
emit_push_insn (arg->value, arg->mode, TREE_TYPE (pval), NULL_RTX,
PARM_BOUNDARY, partial, reg, used - size, argblock,
ARGS_SIZE_RTX (arg->offset), reg_parm_stack_space,
ARGS_SIZE_RTX (arg->alignment_pad));
if (partial == 0)
arg->value = arg->stack;
}
else
{
unsigned int parm_align;
int excess;
rtx size_rtx;
if (arg->size.var != 0)
{
excess = 0;
size_rtx = ARGS_SIZE_RTX (arg->size);
}
else
{
excess = (arg->size.constant - int_size_in_bytes (TREE_TYPE (pval))
+ partial * ABI_UNITS_PER_WORD);
size_rtx = expand_expr (size_in_bytes (TREE_TYPE (pval)),
NULL_RTX, TYPE_MODE (sizetype), 0);
}
parm_align = MAX (PARM_BOUNDARY, TYPE_ALIGN (TREE_TYPE (pval)));
if (FUNCTION_ARG_PADDING (arg->mode, TREE_TYPE (pval)) == downward)
{
if (arg->size.var)
parm_align = BITS_PER_UNIT;
else if (excess)
{
unsigned int excess_align = (excess & -excess) * BITS_PER_UNIT;
parm_align = MIN (parm_align, excess_align);
}
}
if ((flags & ECF_SIBCALL) && GET_CODE (arg->value) == MEM)
{
rtx x = arg->value;
int i = 0;
if (XEXP (x, 0) == current_function_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))
{
if (XEXP (x, 0) != current_function_internal_arg_pointer)
i = INTVAL (XEXP (XEXP (x, 0), 1));
if (arg->offset.var || GET_CODE (size_rtx) != CONST_INT)
abort ();
if (arg->offset.constant > i)
{
if (arg->offset.constant < i + INTVAL (size_rtx))
sibcall_failure = 1;
}
else if (arg->offset.constant < i)
{
if (i < arg->offset.constant + INTVAL (size_rtx))
sibcall_failure = 1;
}
}
}
emit_push_insn (arg->value, arg->mode, TREE_TYPE (pval), size_rtx,
parm_align, partial, reg, excess, argblock,
ARGS_SIZE_RTX (arg->offset), reg_parm_stack_space,
ARGS_SIZE_RTX (arg->alignment_pad));
if (partial == 0)
arg->value = arg->stack_slot;
}
if (ACCUMULATE_OUTGOING_ARGS && !(flags & ECF_SIBCALL)
&& argblock && ! variable_size && arg->stack)
for (i = lower_bound; i < upper_bound; i++)
stack_usage_map[i] = 1;
NO_DEFER_POP;
emit_queue ();
preserve_temp_slots (NULL_RTX);
free_temp_slots ();
pop_temp_slots ();
return sibcall_failure;
}