#include "config.h"
#include "system.h"
#include "rtl.h"
#include "tree.h"
#include "flags.h"
#include "expr.h"
#include "regs.h"
#include "insn-flags.h"
#include "toplev.h"
#include "output.h"
#if !defined PREFERRED_STACK_BOUNDARY && defined STACK_BOUNDARY
#define PREFERRED_STACK_BOUNDARY STACK_BOUNDARY
#endif
#ifdef PUSH_ROUNDING
#if defined (STACK_GROWS_DOWNWARD) != defined (ARGS_GROW_DOWNWARD)
#define PUSH_ARGS_REVERSED
#endif
#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;
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;
#ifdef ACCUMULATE_OUTGOING_ARGS
rtx save_area;
#endif
rtx *aligned_regs;
int n_aligned_regs;
};
#ifdef ACCUMULATE_OUTGOING_ARGS
static char *stack_usage_map;
static int highest_outgoing_arg_in_use;
int stack_arg_under_construction;
#endif
static int calls_function PROTO ((tree, int));
static int calls_function_1 PROTO ((tree, int));
static void emit_call_1 PROTO ((rtx, tree, tree, HOST_WIDE_INT,
HOST_WIDE_INT, HOST_WIDE_INT, rtx,
rtx, int, rtx, int));
static void special_function_p PROTO ((char *, tree, int *, int *,
int *, int *));
static void precompute_register_parameters PROTO ((int, struct arg_data *,
int *));
static void store_one_arg PROTO ((struct arg_data *, rtx, int, int,
int));
static void store_unaligned_arguments_into_pseudos PROTO ((struct arg_data *,
int));
static int finalize_must_preallocate PROTO ((int, int,
struct arg_data *,
struct args_size *));
static void precompute_arguments PROTO ((int, int, int,
struct arg_data *,
struct args_size *));
static int compute_argument_block_size PROTO ((int,
struct args_size *));
static void initialize_argument_information PROTO ((int,
struct arg_data *,
struct args_size *,
int, tree, tree,
CUMULATIVE_ARGS *,
int, rtx *, int *,
int *, int *));
static void compute_argument_addresses PROTO ((struct arg_data *,
rtx, int));
static rtx rtx_for_function_call PROTO ((tree, tree));
static void load_register_parameters PROTO ((struct arg_data *,
int, rtx *));
#if defined(ACCUMULATE_OUTGOING_ARGS) && defined(REG_PARM_STACK_SPACE)
static rtx save_fixed_argument_area PROTO ((int, rtx, int *, int *));
static void restore_fixed_argument_area PROTO ((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;
{
register int i;
enum tree_code code = TREE_CODE (exp);
int type = TREE_CODE_CLASS (code);
int length = tree_code_length[(int) code];
if ((int) code >= NUM_TREE_CODES)
return 1;
if (type != 'e' && type != '<' && type != '1' && type != '2' && type != 'r'
&& type != 'b')
return 0;
switch (code)
{
case CALL_EXPR:
if (which == 0)
return 1;
else if (TREE_CODE (TREE_OPERAND (exp, 0)) == ADDR_EXPR
&& (TREE_CODE (TREE_OPERAND (TREE_OPERAND (exp, 0), 0))
== FUNCTION_DECL))
{
tree fndecl = TREE_OPERAND (TREE_OPERAND (exp, 0), 0);
if ((DECL_BUILT_IN (fndecl)
&& DECL_FUNCTION_CODE (fndecl) == BUILT_IN_ALLOCA)
|| (DECL_SAVED_INSNS (fndecl)
&& (FUNCTION_FLAGS (DECL_SAVED_INSNS (fndecl))
& FUNCTION_FLAGS_CALLS_ALLOCA)))
return 1;
}
length = 2;
break;
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:
{
register tree local;
for (local = BLOCK_VARS (exp); local; local = TREE_CHAIN (local))
if (DECL_INITIAL (local) != 0
&& calls_function_1 (DECL_INITIAL (local), which))
return 1;
}
{
register tree subblock;
for (subblock = BLOCK_SUBBLOCKS (exp);
subblock;
subblock = TREE_CHAIN (subblock))
if (calls_function_1 (subblock, which))
return 1;
}
return 0;
case METHOD_CALL_EXPR:
length = 3;
break;
case WITH_CLEANUP_EXPR:
length = 1;
break;
case RTL_EXPR:
return 0;
default:
break;
}
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)
rtx funexp;
tree fndecl;
rtx *call_fusage;
int reg_parm_seen;
{
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
{
#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, is_const)
rtx funexp;
tree fndecl ATTRIBUTE_UNUSED;
tree funtype ATTRIBUTE_UNUSED;
HOST_WIDE_INT stack_size;
HOST_WIDE_INT rounded_stack_size;
HOST_WIDE_INT struct_value_size;
rtx next_arg_reg;
rtx valreg;
int old_inhibit_defer_pop;
rtx call_fusage;
int is_const;
{
rtx rounded_stack_size_rtx = GEN_INT (rounded_stack_size);
rtx struct_value_size_rtx = GEN_INT (struct_value_size);
rtx call_insn;
#ifndef ACCUMULATE_OUTGOING_ARGS
int already_popped = 0;
HOST_WIDE_INT n_popped = RETURN_POPS_ARGS (fndecl, funtype, stack_size);
#endif
if (GET_CODE (funexp) != SYMBOL_REF)
funexp = memory_address (FUNCTION_MODE, funexp);
#ifndef ACCUMULATE_OUTGOING_ARGS
#if defined (HAVE_call_pop) && defined (HAVE_call_value_pop)
if (HAVE_call_pop && HAVE_call_value_pop && n_popped > 0)
{
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
#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 (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 (is_const)
CONST_CALL_P (call_insn) = 1;
inhibit_defer_pop = old_inhibit_defer_pop;
#ifndef ACCUMULATE_OUTGOING_ARGS
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);
}
if (rounded_stack_size != 0)
{
if (flag_defer_pop && inhibit_defer_pop == 0 && !is_const)
pending_stack_adjust += rounded_stack_size;
else
adjust_stack (rounded_stack_size_rtx);
}
#endif
}
static void
special_function_p (name, fndecl, returns_twice, is_longjmp,
is_malloc, may_be_alloca)
char *name;
tree fndecl;
int *returns_twice;
int *is_longjmp;
int *is_malloc;
int *may_be_alloca;
{
*returns_twice = 0;
*is_longjmp = 0;
*is_malloc = 0;
*may_be_alloca = 0;
if (name != 0 && IDENTIFIER_LENGTH (DECL_NAME (fndecl)) <= 17
&& DECL_CONTEXT (fndecl) == NULL_TREE && TREE_PUBLIC (fndecl))
{
char *tname = name;
*may_be_alloca
= (((IDENTIFIER_LENGTH (DECL_NAME (fndecl)) == 6
&& name[0] == 'a'
&& ! strcmp (name, "alloca"))
|| (IDENTIFIER_LENGTH (DECL_NAME (fndecl)) == 16
&& name[0] == '_'
&& ! strcmp (name, "__builtin_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')
{
*returns_twice
= ((tname[1] == 'e'
&& (! strcmp (tname, "setjmp")
|| ! strcmp (tname, "setjmp_syscall")))
|| (tname[1] == 'i'
&& ! strcmp (tname, "sigsetjmp"))
|| (tname[1] == 'a'
&& ! strcmp (tname, "savectx")));
if (tname[1] == 'i'
&& ! strcmp (tname, "siglongjmp"))
*is_longjmp = 1;
}
else if ((tname[0] == 'q' && tname[1] == 's'
&& ! strcmp (tname, "qsetjmp"))
|| (tname[0] == 'v' && tname[1] == 'f'
&& ! strcmp (tname, "vfork")))
*returns_twice = 1;
else if (tname[0] == 'l' && tname[1] == 'o'
&& ! strcmp (tname, "longjmp"))
*is_longjmp = 1;
else if (! strcmp (tname, "malloc")
|| ! strcmp (tname, "calloc")
|| ! strcmp (tname, "realloc")
|| ! strcmp (name, "__vn")
|| ! strcmp (name, "__nw")
|| ! strcmp (name, "__builtin_new")
|| ! strcmp (name, "__builtin_vec_new"))
*is_malloc = 1;
}
}
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 (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) > 2
&& ((SMALL_REGISTER_CLASSES && *reg_parm_seen)
|| preserve_subexpressions_p ()))
args[i].value = copy_to_mode_reg (args[i].mode, args[i].value);
}
}
#if defined(ACCUMULATE_OUTGOING_ARGS) && defined(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
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),
PARM_BOUNDARY / BITS_PER_UNIT);
}
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),
PARM_BOUNDARY / BITS_PER_UNIT);
}
#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);
int bitalign = TYPE_ALIGN (TREE_TYPE (args[i].tree_value));
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,
bitalign / BITS_PER_UNIT,
BITS_PER_WORD),
bitalign / BITS_PER_UNIT, 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, is_const)
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 *is_const;
{
int inc;
int argpos;
int i;
tree p;
args_size->constant = 0;
args_size->var = 0;
#ifdef PUSH_ARGS_REVERSED
i = num_actuals - 1, inc = -1;
#else
i = 0, inc = 1;
#endif
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 || TYPE_SIZE (type) == 0)
args[i].tree_value = integer_zero_node, type = integer_type_node;
if (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
&& (TREE_CODE_CLASS (TREE_CODE (TREE_OPERAND
(args[i].tree_value, 1)))
== 'd')
&& ! 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
{
rtx copy;
if (TYPE_SIZE (type) == 0
|| TREE_CODE (TYPE_SIZE (type)) != INTEGER_CST
|| (flag_stack_check && ! STACK_CHECK_BUILTIN
&& (TREE_INT_CST_HIGH (TYPE_SIZE (type)) != 0
|| (TREE_INT_CST_LOW (TYPE_SIZE (type))
> STACK_CHECK_MAX_VAR_SIZE * BITS_PER_UNIT))))
{
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)));
}
else
{
int size = int_size_in_bytes (type);
copy = assign_stack_temp (TYPE_MODE (type), size, 0);
}
MEM_SET_IN_STRUCT_P (copy, AGGREGATE_TYPE_P (type));
store_expr (args[i].tree_value, copy, 0);
*is_const = 0;
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_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))
*is_const = 0;
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);
#ifndef ARGS_GROW_DOWNWARD
args[i].slot_offset = *args_size;
#endif
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)
int reg_parm_stack_space;
struct args_size *args_size;
{
int unadjusted_args_size = args_size->constant;
if (args_size->var)
{
args_size->var = ARGS_SIZE_TREE (*args_size);
args_size->constant = 0;
#ifdef PREFERRED_STACK_BOUNDARY
if (PREFERRED_STACK_BOUNDARY != BITS_PER_UNIT)
args_size->var = round_up (args_size->var, STACK_BYTES);
#endif
if (reg_parm_stack_space > 0)
{
args_size->var
= size_binop (MAX_EXPR, args_size->var,
size_int (reg_parm_stack_space));
#ifndef OUTGOING_REG_PARM_STACK_SPACE
args_size->var
= size_binop (MINUS_EXPR, args_size->var,
size_int (reg_parm_stack_space));
#endif
}
}
else
{
#ifdef PREFERRED_STACK_BOUNDARY
args_size->constant = (((args_size->constant
+ pending_stack_adjust
+ STACK_BYTES - 1)
/ STACK_BYTES * STACK_BYTES)
- pending_stack_adjust);
#endif
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 (is_const, must_preallocate, num_actuals, args, args_size)
int is_const;
int must_preallocate;
int num_actuals;
struct arg_data *args;
struct args_size *args_size;
{
int i;
for (i = 0; i < num_actuals; i++)
if (is_const
|| ((args_size->var != 0 || args_size->constant != 0)
&& calls_function (args[i].tree_value, 1))
|| (must_preallocate
&& (args_size->var != 0 || args_size->constant != 0)
&& calls_function (args[i].tree_value, 0)))
{
if (TREE_ADDRESSABLE (TREE_TYPE (args[i].tree_value)))
abort ();
push_temp_slots ();
args[i].initial_value = args[i].value
= expand_expr (args[i].tree_value, NULL_RTX, VOIDmode, 0);
preserve_temp_slots (args[i].value);
pop_temp_slots ();
emit_queue ();
args[i].initial_value = args[i].value
= protect_from_queue (args[i].initial_value, 0);
if (TYPE_MODE (TREE_TYPE (args[i].tree_value)) != args[i].mode)
args[i].value
= convert_modes (args[i].mode,
TYPE_MODE (TREE_TYPE (args[i].tree_value)),
args[i].value, args[i].unsignedp);
}
}
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);
MEM_SET_IN_STRUCT_P
(args[i].stack,
AGGREGATE_TYPE_P (TREE_TYPE (args[i].tree_value)));
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);
}
}
}
static rtx
rtx_for_function_call (fndecl, exp)
tree fndecl;
tree exp;
{
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 (TREE_OPERAND (exp, 0), NULL_RTX, VOIDmode, 0);
pop_temp_slots ();
if (current_function_check_memory_usage)
{
#ifdef POINTERS_EXTEND_UNSIGNED
funaddr = convert_memory_address (Pmode, funexp);
#endif
emit_library_call (chkr_check_exec_libfunc, 1,
VOIDmode, 1,
funaddr, Pmode);
}
emit_queue ();
}
return funexp;
}
static void
load_register_parameters (args, num_actuals, call_fusage)
struct arg_data *args;
int num_actuals;
rtx *call_fusage;
{
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 = args[i].reg;
int partial = args[i].partial;
int nregs;
if (reg)
{
nregs = (partial ? partial
: (TYPE_MODE (TREE_TYPE (args[i].tree_value)) == BLKmode
? ((int_size_in_bytes (TREE_TYPE (args[i].tree_value))
+ (UNITS_PER_WORD - 1)) / 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)),
(TYPE_ALIGN (TREE_TYPE (args[i].tree_value))
/ BITS_PER_UNIT));
}
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 (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);
}
}
}
rtx
expand_call (exp, target, ignore)
tree exp;
rtx target;
int ignore;
{
tree actparms = TREE_OPERAND (exp, 1);
rtx funexp;
tree funtype;
#ifdef NEXT_SEMANTICS
tree saved_return_type;
#endif
tree fndecl = 0;
char *name = 0;
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;
int unadjusted_args_size;
CUMULATIVE_ARGS args_so_far;
int reg_parm_seen;
#ifdef PUSH_ROUNDING
int must_preallocate = 0;
#else
int must_preallocate = 1;
#endif
int reg_parm_stack_space = 0;
rtx argblock = 0;
int may_be_alloca;
int is_malloc;
int returns_twice;
int is_longjmp;
int is_integrable = 0;
int is_const = 0;
int is_volatile = 0;
#if defined(ACCUMULATE_OUTGOING_ARGS) && defined(REG_PARM_STACK_SPACE)
int low_to_save = -1, high_to_save;
rtx save_area = 0;
#endif
#ifdef ACCUMULATE_OUTGOING_ARGS
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;
#endif
rtx old_stack_level = 0;
int old_pending_adj = 0;
int old_inhibit_defer_pop = inhibit_defer_pop;
rtx call_fusage = 0;
register tree p;
register int i;
if (current_function_check_memory_usage)
target = 0;
p = TREE_OPERAND (exp, 0);
if (TREE_CODE (p) == ADDR_EXPR)
{
fndecl = TREE_OPERAND (p, 0);
if (TREE_CODE (fndecl) != FUNCTION_DECL)
fndecl = 0;
else
{
if (!flag_no_inline
&& fndecl != current_function_decl
&& DECL_INLINE (fndecl)
&& DECL_SAVED_INSNS (fndecl)
&& RTX_INTEGRATED_P (DECL_SAVED_INSNS (fndecl)))
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");
}
mark_addressable (fndecl);
}
if (TREE_READONLY (fndecl) && ! TREE_THIS_VOLATILE (fndecl)
&& TYPE_MODE (TREE_TYPE (exp)) != VOIDmode)
is_const = 1;
if (TREE_THIS_VOLATILE (fndecl))
is_volatile = 1;
}
}
if (fndecl == 0)
{
is_const = TREE_READONLY (TREE_TYPE (TREE_TYPE (p)));
is_volatile = TREE_THIS_VOLATILE (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
#if defined(PUSH_ROUNDING) && ! defined(OUTGOING_REG_PARM_STACK_SPACE)
if (reg_parm_stack_space > 0)
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))
{
is_const = 0;
#ifdef PCC_STATIC_STRUCT_RETURN
{
pcc_struct_value = 1;
if (is_integrable)
{
if (! TREE_ADDRESSABLE (fndecl))
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
{
tree d;
if (struct_value_size < 0)
abort ();
d = build_decl (VAR_DECL, NULL_TREE, TREE_TYPE (exp));
DECL_RTL (d) = assign_temp (TREE_TYPE (exp), 1, 0, 1);
mark_addressable (d);
structure_value_addr = XEXP (DECL_RTL (d), 0);
TREE_USED (d) = 1;
target = 0;
}
}
#endif
}
if (is_integrable)
{
rtx temp;
#ifdef ACCUMULATE_OUTGOING_ARGS
rtx before_call = get_last_insn ();
#endif
temp = expand_inline_function (fndecl, actparms, target,
ignore, TREE_TYPE (exp),
structure_value_addr);
if (temp != (rtx) (HOST_WIDE_INT) -1)
{
#ifdef 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, seq;
if (OUTGOING_ARGS_SIZE (DECL_SAVED_INSNS (fndecl)) == 0)
for (insn = first_insn; insn; insn = NEXT_INSN (insn))
if (GET_CODE (insn) == CALL_INSN)
break;
if (insn)
{
int adjust = (OUTGOING_ARGS_SIZE (DECL_SAVED_INSNS (fndecl))
+ 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_insns_before (seq, first_insn);
emit_stack_restore (SAVE_BLOCK, old_stack_level, NULL_RTX);
}
}
#endif
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");
}
mark_addressable (fndecl);
}
function_call_count++;
if (fndecl && DECL_NAME (fndecl))
name = IDENTIFIER_POINTER (DECL_NAME (fndecl));
special_function_p (name, fndecl, &returns_twice, &is_longjmp,
&is_malloc, &may_be_alloca);
if (may_be_alloca)
current_function_calls_alloca = 1;
funtype = TREE_TYPE (TREE_OPERAND (exp, 0));
if (! POINTER_TYPE_P (funtype))
abort ();
funtype = TREE_TYPE (funtype);
#ifdef NEXT_SEMANTICS
saved_return_type = TREE_TYPE (funtype);
TREE_TYPE (funtype) = TREE_TYPE (exp);
#endif
if (is_const)
NO_DEFER_POP;
if (pending_stack_adjust >= 32
|| (pending_stack_adjust > 0 && may_be_alloca))
do_pending_stack_adjust ();
push_temp_slots ();
INIT_CUMULATIVE_ARGS (args_so_far, funtype, NULL_RTX, (fndecl == 0));
#ifdef REARRANGE_ARG_LIST
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
#ifdef ACCUMULATE_OUTGOING_ARGS
|| (stack_arg_under_construction
&& structure_value_addr == virtual_outgoing_args_rtx)
#endif
? 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, i = 0; p; p = TREE_CHAIN (p)) i++;
num_actuals = i;
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;
args = (struct arg_data *) alloca (num_actuals * sizeof (struct arg_data));
bzero ((char *) args, 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,
&is_const);
#ifdef FINAL_REG_PARM_STACK_SPACE
reg_parm_stack_space = FINAL_REG_PARM_STACK_SPACE (args_size.constant,
args_size.var);
#endif
if (args_size.var)
{
is_const = 0;
must_preallocate = 1;
}
unadjusted_args_size
= compute_argument_block_size (reg_parm_stack_space, &args_size);
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
#ifndef ACCUMULATE_OUTGOING_ARGS
|| args_size.constant
#endif
))
structure_value_addr = copy_to_reg (structure_value_addr);
precompute_arguments (is_const, must_preallocate, num_actuals,
args, &args_size);
if (is_const || is_malloc)
start_sequence ();
if (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;
#ifdef ACCUMULATE_OUTGOING_ARGS
old_stack_arg_under_construction = stack_arg_under_construction;
stack_arg_under_construction = 0;
#endif
}
argblock = push_block (ARGS_SIZE_RTX (args_size), 0, 0);
}
else
{
int needed = args_size.constant;
if (needed > current_function_outgoing_args_size)
current_function_outgoing_args_size = needed;
if (must_preallocate)
{
#ifdef 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)
bcopy (initial_stack_usage_map, stack_usage_map,
initial_highest_arg_in_use);
if (initial_highest_arg_in_use != highest_outgoing_arg_in_use)
bzero (&stack_usage_map[initial_highest_arg_in_use],
highest_outgoing_arg_in_use - initial_highest_arg_in_use);
needed = 0;
argblock = virtual_outgoing_args_rtx;
#else
if (inhibit_defer_pop == 0)
{
if (needed > pending_stack_adjust)
{
needed -= pending_stack_adjust;
pending_stack_adjust = 0;
}
else
{
pending_stack_adjust -= needed;
needed = 0;
}
}
if (needed == 0)
argblock = virtual_outgoing_args_rtx;
else
argblock = push_block (GEN_INT (needed), 0, 0);
argblock = copy_to_reg (argblock);
#endif
}
}
#ifdef ACCUMULATE_OUTGOING_ARGS
if (stack_arg_under_construction)
{
#ifndef OUTGOING_REG_PARM_STACK_SPACE
rtx push_size = GEN_INT (reg_parm_stack_space + args_size.constant);
#else
rtx push_size = GEN_INT (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);
bzero (stack_usage_map, 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;
}
#endif
compute_argument_addresses (args, argblock, num_actuals);
#ifdef PUSH_ARGS_REVERSED
#ifdef PREFERRED_STACK_BOUNDARY
if (argblock == 0)
anti_adjust_stack (GEN_INT (args_size.constant - unadjusted_args_size));
#endif
#endif
if (argblock)
NO_DEFER_POP;
funexp = rtx_for_function_call (fndecl, exp);
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);
else
valreg = hard_function_value (TREE_TYPE (exp), fndecl);
}
precompute_register_parameters (num_actuals, args, ®_parm_seen);
#if defined(ACCUMULATE_OUTGOING_ARGS) && defined(REG_PARM_STACK_SPACE)
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)
store_one_arg (&args[i], argblock, may_be_alloca,
args_size.var != 0, reg_parm_stack_space);
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)
store_one_arg (&args[i], argblock, may_be_alloca,
args_size.var != 0, reg_parm_stack_space);
#ifndef PUSH_ARGS_REVERSED
#ifdef PREFERRED_STACK_BOUNDARY
if (argblock == 0)
anti_adjust_stack (GEN_INT (args_size.constant - unadjusted_args_size));
#endif
#endif
#if ! defined(ACCUMULATE_OUTGOING_ARGS) && defined(OUTGOING_REG_PARM_STACK_SPACE)
if (must_preallocate == 0 && reg_parm_stack_space > 0)
anti_adjust_stack (GEN_INT (reg_parm_stack_space));
#endif
if (structure_value_addr && ! structure_value_addr_parm)
{
emit_move_insn (struct_value_rtx,
force_reg (Pmode,
force_operand (structure_value_addr,
NULL_RTX)));
if (current_function_check_memory_usage)
emit_library_call (chkr_set_right_libfunc, 1,
VOIDmode, 3,
structure_value_addr, Pmode,
GEN_INT (struct_value_size), TYPE_MODE (sizetype),
GEN_INT (MEMORY_USE_WO),
TYPE_MODE (integer_type_node));
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);
load_register_parameters (args, num_actuals, &call_fusage);
emit_queue ();
emit_call_1 (funexp, fndecl, funtype, unadjusted_args_size,
args_size.constant, struct_value_size,
FUNCTION_ARG (args_so_far, VOIDmode, void_type_node, 1),
valreg, old_inhibit_defer_pop, call_fusage, is_const);
#ifdef NEXT_SEMANTICS
TREE_TYPE (funtype) = saved_return_type;
#endif
if (is_const && valreg != 0 && GET_CODE (valreg) != PARALLEL)
{
rtx note = 0;
rtx temp = gen_reg_rtx (GET_MODE (valreg));
rtx insns;
if (TREE_CODE (TREE_TYPE (exp)) == POINTER_TYPE)
{
tree pointed_to = TREE_TYPE (TREE_TYPE (exp));
mark_reg_pointer (temp, TYPE_ALIGN (pointed_to) / BITS_PER_UNIT);
}
#ifdef PUSH_ARGS_REVERSED
for (i = 0; i < num_actuals; i++)
note = gen_rtx_EXPR_LIST (VOIDmode, args[i].initial_value, note);
#else
for (i = num_actuals - 1; i >= 0; i--)
note = gen_rtx_EXPR_LIST (VOIDmode, args[i].initial_value, note);
#endif
note = gen_rtx_EXPR_LIST (VOIDmode, funexp, note);
insns = get_insns ();
end_sequence ();
emit_libcall_block (insns, temp, valreg, note);
valreg = temp;
}
else if (is_const)
{
rtx insns = get_insns ();
end_sequence ();
emit_insns (insns);
}
else if (is_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 / BITS_PER_UNIT);
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_insns (insns);
valreg = temp;
}
if (returns_twice)
{
emit_note (name, NOTE_INSN_SETJMP);
current_function_calls_setjmp = 1;
}
if (is_longjmp)
current_function_calls_longjmp = 1;
if (is_volatile || is_longjmp)
emit_barrier ();
if (any_pending_cleanups (1)
&& target && REG_P (target)
&& REGNO (target) < FIRST_PSEUDO_REGISTER)
target = 0;
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));
MEM_SET_IN_STRUCT_P (target,
AGGREGATE_TYPE_P (TREE_TYPE (exp)));
}
}
else if (pcc_struct_value)
{
target = gen_rtx_MEM (TYPE_MODE (TREE_TYPE (exp)),
copy_to_reg (valreg));
MEM_SET_IN_STRUCT_P (target, AGGREGATE_TYPE_P (TREE_TYPE (exp)));
}
else if (GET_CODE (valreg) == PARALLEL)
{
int bytes = int_size_in_bytes (TREE_TYPE (exp));
if (target == 0)
{
target = assign_stack_temp (TYPE_MODE (TREE_TYPE (exp)), bytes, 0);
MEM_SET_IN_STRUCT_P (target, AGGREGATE_TYPE_P (TREE_TYPE (exp)));
preserve_temp_slots (target);
}
emit_group_store (target, valreg, bytes,
TYPE_ALIGN (TREE_TYPE (exp)) / BITS_PER_UNIT);
}
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));
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);
if (GET_MODE (target)
!= promote_mode (type, TYPE_MODE (type), &unsignedp, 1))
abort ();
target = gen_rtx_SUBREG (TYPE_MODE (type), target, 0);
SUBREG_PROMOTED_VAR_P (target) = 1;
SUBREG_PROMOTED_UNSIGNED_P (target) = unsignedp;
}
#endif
if (old_stack_level)
{
emit_stack_restore (SAVE_BLOCK, old_stack_level, NULL_RTX);
pending_stack_adjust = old_pending_adj;
#ifdef ACCUMULATE_OUTGOING_ARGS
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;
#endif
}
#ifdef ACCUMULATE_OUTGOING_ARGS
else
{
#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, validize_mem (args[i].save_area),
GEN_INT (args[i].size.constant),
PARM_BOUNDARY / BITS_PER_UNIT);
}
highest_outgoing_arg_in_use = initial_highest_arg_in_use;
stack_usage_map = initial_stack_usage_map;
}
#endif
if (may_be_alloca && nonlocal_goto_handler_slots != 0)
emit_stack_save (SAVE_NONLOCAL, &nonlocal_goto_stack_level, NULL_RTX);
pop_temp_slots ();
for (i = 0; i < num_actuals; ++i)
if (args[i].aligned_regs)
free (args[i].aligned_regs);
return target;
}
void
emit_library_call VPROTO((rtx orgfun, int no_queue, enum machine_mode outmode,
int nargs, ...))
{
#ifndef ANSI_PROTOTYPES
rtx orgfun;
int no_queue;
enum machine_mode outmode;
int nargs;
#endif
va_list p;
struct args_size args_size;
struct args_size original_args_size;
register int argnum;
rtx fun;
int inc;
int count;
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;
int reg_parm_stack_space = 0;
#if defined(ACCUMULATE_OUTGOING_ARGS) && defined(REG_PARM_STACK_SPACE)
int low_to_save = -1, high_to_save;
rtx save_area = 0;
#endif
#ifdef ACCUMULATE_OUTGOING_ARGS
int initial_highest_arg_in_use = highest_outgoing_arg_in_use;
char *initial_stack_usage_map = stack_usage_map;
int needed;
#endif
#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
VA_START (p, nargs);
#ifndef ANSI_PROTOTYPES
orgfun = va_arg (p, rtx);
no_queue = va_arg (p, int);
outmode = va_arg (p, enum machine_mode);
nargs = va_arg (p, int);
#endif
fun = orgfun;
argvec = (struct arg *) alloca (nargs * sizeof (struct arg));
bzero ((char *) argvec, nargs * sizeof (struct arg));
INIT_CUMULATIVE_ARGS (args_so_far, NULL_TREE, fun, 0);
args_size.constant = 0;
args_size.var = 0;
push_temp_slots ();
for (count = 0; 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 = assign_stack_temp (mode, GET_MODE_SIZE (mode), 0);
emit_move_insn (slot, val);
val = force_operand (XEXP (slot, 0), NULL_RTX);
mode = Pmode;
}
#endif
argvec[count].value = val;
argvec[count].mode = mode;
argvec[count].reg = FUNCTION_ARG (args_so_far, mode, NULL_TREE, 1);
if (argvec[count].reg && GET_CODE (argvec[count].reg) == PARALLEL)
abort ();
#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,
argvec[count].reg && argvec[count].partial == 0,
NULL_TREE, &args_size, &argvec[count].offset,
&argvec[count].size);
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);
}
va_end (p);
#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;
#ifdef PREFERRED_STACK_BOUNDARY
args_size.constant = (((args_size.constant + (STACK_BYTES - 1))
/ STACK_BYTES) * STACK_BYTES);
#endif
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;
#ifdef 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)
bcopy (initial_stack_usage_map, stack_usage_map,
initial_highest_arg_in_use);
if (initial_highest_arg_in_use != highest_outgoing_arg_in_use)
bzero (&stack_usage_map[initial_highest_arg_in_use],
highest_outgoing_arg_in_use - initial_highest_arg_in_use);
needed = 0;
argblock = virtual_outgoing_args_rtx;
#else
#ifndef PUSH_ROUNDING
argblock = push_block (GEN_INT (args_size.constant), 0, 0);
#endif
#endif
#ifdef PUSH_ARGS_REVERSED
#ifdef PREFERRED_STACK_BOUNDARY
if (argblock == 0)
anti_adjust_stack (GEN_INT (args_size.constant
- original_args_size.constant));
#endif
#endif
#ifdef PUSH_ARGS_REVERSED
inc = -1;
argnum = nargs - 1;
#else
inc = 1;
argnum = 0;
#endif
#if defined(ACCUMULATE_OUTGOING_ARGS) && defined(REG_PARM_STACK_SPACE)
#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);
emit_block_move (validize_mem (save_area), stack_area,
GEN_INT (num_to_save),
PARM_BOUNDARY / BITS_PER_UNIT);
}
else
{
save_area = gen_reg_rtx (save_mode);
emit_move_insn (save_area, stack_area);
}
}
#endif
for (count = 0; count < nargs; count++, argnum += inc)
{
register enum machine_mode mode = argvec[argnum].mode;
register rtx val = argvec[argnum].value;
rtx reg = argvec[argnum].reg;
int partial = argvec[argnum].partial;
#ifdef ACCUMULATE_OUTGOING_ARGS
int lower_bound, upper_bound, i;
#endif
if (! (reg != 0 && partial == 0))
{
#ifdef 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);
}
#endif
emit_push_insn (val, mode, NULL_TREE, NULL_RTX, 0, partial, reg, 0,
argblock, GEN_INT (argvec[argnum].offset.constant),
reg_parm_stack_space);
#ifdef ACCUMULATE_OUTGOING_ARGS
for (i = lower_bound; i < upper_bound; i++)
stack_usage_map[i] = 1;
#endif
NO_DEFER_POP;
}
}
#ifndef PUSH_ARGS_REVERSED
#ifdef PREFERRED_STACK_BOUNDARY
if (argblock == 0)
anti_adjust_stack (GEN_INT (args_size.constant
- original_args_size.constant));
#endif
#endif
#ifdef PUSH_ARGS_REVERSED
argnum = nargs - 1;
#else
argnum = 0;
#endif
fun = prepare_call_address (fun, NULL_TREE, &call_fusage, 0);
for (count = 0; count < nargs; count++, argnum += inc)
{
register rtx val = argvec[argnum].value;
rtx reg = argvec[argnum].reg;
int partial = argvec[argnum].partial;
if (reg != 0 && partial == 0)
emit_move_insn (reg, val);
NO_DEFER_POP;
}
if (! no_queue)
emit_queue ();
for (count = 0; count < nargs; count++)
if (argvec[count].reg != 0)
use_reg (&call_fusage, argvec[count].reg);
NO_DEFER_POP;
emit_call_1 (fun,
get_identifier (XSTR (orgfun, 0)),
build_function_type (outmode == VOIDmode ? void_type_node
: type_for_mode (outmode, 0), NULL_TREE),
original_args_size.constant, args_size.constant, 0,
FUNCTION_ARG (args_so_far, VOIDmode, void_type_node, 1),
outmode != VOIDmode ? hard_libcall_value (outmode) : NULL_RTX,
old_inhibit_defer_pop + 1, call_fusage, no_queue);
pop_temp_slots ();
OK_DEFER_POP;
#ifdef 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
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),
PARM_BOUNDARY / BITS_PER_UNIT);
}
#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;
#endif
}
rtx
emit_library_call_value VPROTO((rtx orgfun, rtx value, int no_queue,
enum machine_mode outmode, int nargs, ...))
{
#ifndef ANSI_PROTOTYPES
rtx orgfun;
rtx value;
int no_queue;
enum machine_mode outmode;
int nargs;
#endif
va_list p;
struct args_size args_size;
struct args_size original_args_size;
register int argnum;
rtx fun;
int inc;
int count;
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;
int pcc_struct_value = 0;
int struct_value_size = 0;
int is_const;
int reg_parm_stack_space = 0;
#ifdef ACCUMULATE_OUTGOING_ARGS
int needed;
#endif
#if defined(ACCUMULATE_OUTGOING_ARGS) && defined(REG_PARM_STACK_SPACE)
int low_to_save = -1, high_to_save;
rtx save_area = 0;
#endif
#ifdef ACCUMULATE_OUTGOING_ARGS
int initial_highest_arg_in_use = highest_outgoing_arg_in_use;
char *initial_stack_usage_map = stack_usage_map;
#endif
#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
VA_START (p, nargs);
#ifndef ANSI_PROTOTYPES
orgfun = va_arg (p, rtx);
value = va_arg (p, rtx);
no_queue = va_arg (p, int);
outmode = va_arg (p, enum machine_mode);
nargs = va_arg (p, int);
#endif
is_const = no_queue;
fun = orgfun;
if (aggregate_value_p (type_for_mode (outmode, 0)))
{
#ifdef PCC_STATIC_STRUCT_RETURN
rtx pointer_reg
= hard_function_value (build_pointer_type (type_for_mode (outmode, 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_stack_temp (outmode, GET_MODE_SIZE (outmode), 0);
#endif
is_const = 0;
}
argvec = (struct arg *) alloca ((nargs + 1) * sizeof (struct arg));
bzero ((char *) argvec, (nargs + 1) * sizeof (struct arg));
INIT_CUMULATIVE_ARGS (args_so_far, NULL_TREE, fun, 0);
args_size.constant = 0;
args_size.var = 0;
count = 0;
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,
argvec[count].reg && argvec[count].partial == 0,
NULL_TREE, &args_size, &argvec[count].offset,
&argvec[count].size);
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 = assign_stack_temp (mode, GET_MODE_SIZE (mode), 0);
emit_move_insn (slot, val);
val = XEXP (slot, 0);
mode = Pmode;
}
#endif
argvec[count].value = val;
argvec[count].mode = mode;
argvec[count].reg = FUNCTION_ARG (args_so_far, mode, NULL_TREE, 1);
if (argvec[count].reg && GET_CODE (argvec[count].reg) == PARALLEL)
abort ();
#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,
argvec[count].reg && argvec[count].partial == 0,
NULL_TREE, &args_size, &argvec[count].offset,
&argvec[count].size);
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);
}
va_end (p);
#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;
#ifdef PREFERRED_STACK_BOUNDARY
args_size.constant = (((args_size.constant + (STACK_BYTES - 1))
/ STACK_BYTES) * STACK_BYTES);
#endif
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;
#ifdef 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)
bcopy (initial_stack_usage_map, stack_usage_map,
initial_highest_arg_in_use);
if (initial_highest_arg_in_use != highest_outgoing_arg_in_use)
bzero (&stack_usage_map[initial_highest_arg_in_use],
highest_outgoing_arg_in_use - initial_highest_arg_in_use);
needed = 0;
argblock = virtual_outgoing_args_rtx;
#else
#ifndef PUSH_ROUNDING
argblock = push_block (GEN_INT (args_size.constant), 0, 0);
#endif
#endif
#ifdef PUSH_ARGS_REVERSED
#ifdef PREFERRED_STACK_BOUNDARY
if (argblock == 0)
anti_adjust_stack (GEN_INT (args_size.constant
- original_args_size.constant));
#endif
#endif
#ifdef PUSH_ARGS_REVERSED
inc = -1;
argnum = nargs - 1;
#else
inc = 1;
argnum = 0;
#endif
#if defined(ACCUMULATE_OUTGOING_ARGS) && defined(REG_PARM_STACK_SPACE)
#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);
emit_block_move (validize_mem (save_area), stack_area,
GEN_INT (num_to_save),
PARM_BOUNDARY / BITS_PER_UNIT);
}
else
{
save_area = gen_reg_rtx (save_mode);
emit_move_insn (save_area, stack_area);
}
}
#endif
for (count = 0; count < nargs; count++, argnum += inc)
{
register enum machine_mode mode = argvec[argnum].mode;
register rtx val = argvec[argnum].value;
rtx reg = argvec[argnum].reg;
int partial = argvec[argnum].partial;
#ifdef ACCUMULATE_OUTGOING_ARGS
int lower_bound, upper_bound, i;
#endif
if (! (reg != 0 && partial == 0))
{
#ifdef 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);
}
#endif
emit_push_insn (val, mode, NULL_TREE, NULL_RTX, 0, partial, reg, 0,
argblock, GEN_INT (argvec[argnum].offset.constant),
reg_parm_stack_space);
#ifdef ACCUMULATE_OUTGOING_ARGS
for (i = lower_bound; i < upper_bound; i++)
stack_usage_map[i] = 1;
#endif
NO_DEFER_POP;
}
}
#ifndef PUSH_ARGS_REVERSED
#ifdef PREFERRED_STACK_BOUNDARY
if (argblock == 0)
anti_adjust_stack (GEN_INT (args_size.constant
- original_args_size.constant));
#endif
#endif
#ifdef PUSH_ARGS_REVERSED
argnum = nargs - 1;
#else
argnum = 0;
#endif
fun = prepare_call_address (fun, NULL_TREE, &call_fusage, 0);
for (count = 0; count < nargs; count++, argnum += inc)
{
register rtx val = argvec[argnum].value;
rtx reg = argvec[argnum].reg;
int partial = argvec[argnum].partial;
if (reg != 0 && partial == 0)
emit_move_insn (reg, val);
NO_DEFER_POP;
}
#if 0
if (! no_queue)
emit_queue ();
#endif
for (count = 0; count < nargs; count++)
if (argvec[count].reg != 0)
use_reg (&call_fusage, argvec[count].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;
emit_call_1 (fun,
get_identifier (XSTR (orgfun, 0)),
build_function_type (type_for_mode (outmode, 0), NULL_TREE),
original_args_size.constant, args_size.constant,
struct_value_size,
FUNCTION_ARG (args_so_far, VOIDmode, void_type_node, 1),
mem_value == 0 ? hard_libcall_value (outmode) : NULL_RTX,
old_inhibit_defer_pop + 1, call_fusage, is_const);
OK_DEFER_POP;
pop_temp_slots ();
if (outmode != VOIDmode)
{
if (mem_value)
{
if (value == 0)
value = mem_value;
if (value != mem_value)
emit_move_insn (value, mem_value);
}
else if (value != 0)
emit_move_insn (value, hard_libcall_value (outmode));
else
value = hard_libcall_value (outmode);
}
#ifdef 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
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),
PARM_BOUNDARY / BITS_PER_UNIT);
}
#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;
#endif
return value;
}
#if 0
static rtx
target_for_arg (type, size, args_addr, offset)
tree type;
rtx size;
rtx args_addr;
struct args_size offset;
{
rtx target;
rtx offset_rtx = ARGS_SIZE_RTX (offset);
if (GET_CODE (offset_rtx) == CONST_INT)
target = plus_constant (args_addr, INTVAL (offset_rtx));
else
{
target = gen_rtx_PLUS (Pmode, args_addr, offset_rtx);
target = memory_address (QImode, target);
}
return gen_rtx_MEM (BLKmode, target);
}
#endif
static void
store_one_arg (arg, argblock, may_be_alloca, variable_size,
reg_parm_stack_space)
struct arg_data *arg;
rtx argblock;
int may_be_alloca;
int variable_size ATTRIBUTE_UNUSED;
int reg_parm_stack_space;
{
register tree pval = arg->tree_value;
rtx reg = 0;
int partial = 0;
int used = 0;
#ifdef ACCUMULATE_OUTGOING_ARGS
int i, lower_bound, upper_bound;
#endif
if (TREE_CODE (pval) == ERROR_MARK)
return;
push_temp_slots ();
#ifdef ACCUMULATE_OUTGOING_ARGS
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)
{
arg->save_area = assign_stack_temp (BLKmode,
arg->size.constant, 0);
MEM_SET_IN_STRUCT_P (arg->save_area,
AGGREGATE_TYPE_P (TREE_TYPE
(arg->tree_value)));
preserve_temp_slots (arg->save_area);
emit_block_move (validize_mem (arg->save_area), stack_area,
GEN_INT (arg->size.constant),
PARM_BOUNDARY / BITS_PER_UNIT);
}
else
{
arg->save_area = gen_reg_rtx (save_mode);
emit_move_insn (arg->save_area, stack_area);
}
}
}
if (argblock && ! variable_size && arg->stack)
#if defined(NEXT_SEMANTICS) && defined(TARGET_TOC)
if (upper_bound - lower_bound > reg_parm_stack_space)
for (i = MAX (lower_bound, reg_parm_stack_space); i < upper_bound; i++)
stack_usage_map[i] = 1;
else
#endif
for (i = lower_bound; i < upper_bound; i++)
stack_usage_map[i] = 1;
#endif
if (! arg->pass_on_stack)
reg = arg->reg, partial = arg->partial;
if (reg != 0 && partial == 0)
abort ();
if (arg->n_aligned_regs != 0)
reg = 0;
if (arg->value == 0)
{
#ifdef ACCUMULATE_OUTGOING_ARGS
if (arg->pass_on_stack)
stack_arg_under_construction++;
#endif
arg->value = expand_expr (pval,
(partial
|| TYPE_MODE (TREE_TYPE (pval)) != arg->mode)
? NULL_RTX : arg->stack,
VOIDmode, 0);
if (arg->mode != TYPE_MODE (TREE_TYPE (pval)))
arg->value = convert_modes (arg->mode, TYPE_MODE (TREE_TYPE (pval)),
arg->value, arg->unsignedp);
#ifdef ACCUMULATE_OUTGOING_ARGS
if (arg->pass_on_stack)
stack_arg_under_construction--;
#endif
}
if (may_be_alloca)
do_pending_stack_adjust ();
if (arg->value == arg->stack)
{
if (current_function_check_memory_usage && GET_CODE (arg->stack) == MEM)
{
emit_library_call (chkr_set_right_libfunc, 1, VOIDmode, 3,
XEXP (arg->stack, 0), Pmode,
ARGS_SIZE_RTX (arg->size),
TYPE_MODE (sizetype),
GEN_INT (MEMORY_USE_RW),
TYPE_MODE (integer_type_node));
}
}
else if (arg->mode != BLKmode)
{
register 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, 0,
partial, reg, used - size, argblock,
ARGS_SIZE_RTX (arg->offset), reg_parm_stack_space);
}
else
{
register 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 * UNITS_PER_WORD);
size_rtx = expr_size (pval);
}
emit_push_insn (arg->value, arg->mode, TREE_TYPE (pval), size_rtx,
TYPE_ALIGN (TREE_TYPE (pval)) / BITS_PER_UNIT, partial,
reg, excess, argblock, ARGS_SIZE_RTX (arg->offset),
reg_parm_stack_space);
}
if (partial == 0)
arg->value = arg->stack_slot;
NO_DEFER_POP;
emit_queue ();
preserve_temp_slots (NULL_RTX);
free_temp_slots ();
pop_temp_slots ();
}