(expand_builtin...
(expand_builtin, BUILT_IN_SAVEREGS): Use these to put the code at the start of the function, even when inside a sequence. (apply_args_value): New variable. (init_expr, save_expr_status, restore_expr_status): Initialize, save, and restore apply_args_value. (expand_builtin): Implement new built-in functions. (apply_args_mode, apply_result_mode): New variables. (apply_args_size, apply_result_size, result_vector, expand_builtin_apply_args, expand_builtin_apply, expand_builtin_return): New functions. (INCOMING_REGNO, OUTGOING_REGNO): Supply default definitions. From-SVN: r3845
This commit is contained in:
parent
d12b538233
commit
0006469db8
543
gcc/expr.c
543
gcc/expr.c
@ -86,6 +86,9 @@ int inhibit_defer_pop;
|
||||
function calls being expanded by expand_call. */
|
||||
tree cleanups_this_call;
|
||||
|
||||
/* Similarly for __builtin_apply_args. */
|
||||
static rtx apply_args_value;
|
||||
|
||||
/* Nonzero means __builtin_saveregs has already been done in this function.
|
||||
The value is the pseudoreg containing the value __builtin_saveregs
|
||||
returned. */
|
||||
@ -128,6 +131,12 @@ static int get_pointer_alignment PROTO((tree, unsigned));
|
||||
static tree string_constant PROTO((tree, tree *));
|
||||
static tree c_strlen PROTO((tree));
|
||||
static rtx expand_builtin PROTO((tree, rtx, rtx, enum machine_mode, int));
|
||||
static int apply_args_size PROTO((void));
|
||||
static int apply_result_size PROTO((void));
|
||||
static rtx result_vector PROTO((int, rtx));
|
||||
static rtx expand_builtin_apply_args PROTO((void));
|
||||
static rtx expand_builtin_apply PROTO((rtx, rtx, rtx));
|
||||
static void expand_builtin_return PROTO((rtx));
|
||||
static rtx expand_increment PROTO((tree, int));
|
||||
static void preexpand_calls PROTO((tree));
|
||||
static void do_jump_by_parts_greater PROTO((tree, int, rtx, rtx));
|
||||
@ -165,6 +174,14 @@ static enum insn_code movstr_optab[NUM_MACHINE_MODES];
|
||||
#ifndef SLOW_UNALIGNED_ACCESS
|
||||
#define SLOW_UNALIGNED_ACCESS 0
|
||||
#endif
|
||||
|
||||
/* Register mappings for target machines without register windows. */
|
||||
#ifndef INCOMING_REGNO
|
||||
#define INCOMING_REGNO(OUT) (OUT)
|
||||
#endif
|
||||
#ifndef OUTGOING_REGNO
|
||||
#define OUTGOING_REGNO(IN) (IN)
|
||||
#endif
|
||||
|
||||
/* This is run once per compilation to set up which modes can be used
|
||||
directly in memory and to initialize the block move optab. */
|
||||
@ -267,6 +284,7 @@ init_expr ()
|
||||
inhibit_defer_pop = 0;
|
||||
cleanups_this_call = 0;
|
||||
saveregs_value = 0;
|
||||
apply_args_value = 0;
|
||||
forced_labels = 0;
|
||||
}
|
||||
|
||||
@ -284,12 +302,14 @@ save_expr_status (p)
|
||||
p->inhibit_defer_pop = inhibit_defer_pop;
|
||||
p->cleanups_this_call = cleanups_this_call;
|
||||
p->saveregs_value = saveregs_value;
|
||||
p->apply_args_value = apply_args_value;
|
||||
p->forced_labels = forced_labels;
|
||||
|
||||
pending_stack_adjust = 0;
|
||||
inhibit_defer_pop = 0;
|
||||
cleanups_this_call = 0;
|
||||
saveregs_value = 0;
|
||||
apply_args_value = 0;
|
||||
forced_labels = 0;
|
||||
}
|
||||
|
||||
@ -304,6 +324,7 @@ restore_expr_status (p)
|
||||
inhibit_defer_pop = p->inhibit_defer_pop;
|
||||
cleanups_this_call = p->cleanups_this_call;
|
||||
saveregs_value = p->saveregs_value;
|
||||
apply_args_value = p->apply_args_value;
|
||||
forced_labels = p->forced_labels;
|
||||
}
|
||||
|
||||
@ -6175,6 +6196,83 @@ expand_builtin (exp, target, subtarget, mode, ignore)
|
||||
|
||||
return target;
|
||||
|
||||
/* __builtin_apply_args returns block of memory allocated on
|
||||
the stack into which is stored the arg pointer, structure
|
||||
value address, static chain, and all the registers that might
|
||||
possibly be used in performing a function call. The code is
|
||||
moved to the start of the function so the incoming values are
|
||||
saved. */
|
||||
case BUILT_IN_APPLY_ARGS:
|
||||
/* Don't do __builtin_apply_args more than once in a function.
|
||||
Save the result of the first call and reuse it. */
|
||||
if (apply_args_value != 0)
|
||||
return apply_args_value;
|
||||
{
|
||||
/* When this function is called, it means that registers must be
|
||||
saved on entry to this function. So we migrate the
|
||||
call to the first insn of this function. */
|
||||
rtx temp;
|
||||
rtx seq;
|
||||
|
||||
start_sequence ();
|
||||
temp = expand_builtin_apply_args ();
|
||||
seq = get_insns ();
|
||||
end_sequence ();
|
||||
|
||||
apply_args_value = temp;
|
||||
|
||||
/* Put the sequence after the NOTE that starts the function.
|
||||
If this is inside a SEQUENCE, make the outer-level insn
|
||||
chain current, so the code is placed at the start of the
|
||||
function. */
|
||||
push_topmost_sequence ();
|
||||
emit_insns_before (seq, NEXT_INSN (get_insns ()));
|
||||
pop_topmost_sequence ();
|
||||
return temp;
|
||||
}
|
||||
|
||||
/* __builtin_apply (FUNCTION, ARGUMENTS, ARGSIZE) invokes
|
||||
FUNCTION with a copy of the parameters described by
|
||||
ARGUMENTS, and ARGSIZE. It returns a block of memory
|
||||
allocated on the stack into which is stored all the registers
|
||||
that might possibly be used for returning the result of a
|
||||
function. ARGUMENTS is the value returned by
|
||||
__builtin_apply_args. ARGSIZE is the number of bytes of
|
||||
arguments that must be copied. ??? How should this value be
|
||||
computed? We'll also need a safe worst case value for varargs
|
||||
functions. */
|
||||
case BUILT_IN_APPLY:
|
||||
if (arglist == 0
|
||||
/* Arg could be non-pointer if user redeclared this fcn wrong. */
|
||||
|| TREE_CODE (TREE_TYPE (TREE_VALUE (arglist))) != POINTER_TYPE
|
||||
|| TREE_CHAIN (arglist) == 0
|
||||
|| TREE_CODE (TREE_TYPE (TREE_VALUE (TREE_CHAIN (arglist)))) != POINTER_TYPE
|
||||
|| TREE_CHAIN (TREE_CHAIN (arglist)) == 0
|
||||
|| TREE_CODE (TREE_TYPE (TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist))))) != INTEGER_TYPE)
|
||||
return const0_rtx;
|
||||
else
|
||||
{
|
||||
int i;
|
||||
tree t;
|
||||
rtx ops[3];
|
||||
|
||||
for (t = arglist, i = 0; t; t = TREE_CHAIN (t), i++)
|
||||
ops[i] = expand_expr (TREE_VALUE (t), NULL_RTX, VOIDmode, 0);
|
||||
|
||||
return expand_builtin_apply (ops[0], ops[1], ops[2]);
|
||||
}
|
||||
|
||||
/* __builtin_return (RESULT) causes the function to return the
|
||||
value described by RESULT. RESULT is address of the block of
|
||||
memory returned by __builtin_apply. */
|
||||
case BUILT_IN_RETURN:
|
||||
if (arglist
|
||||
/* Arg could be non-pointer if user redeclared this fcn wrong. */
|
||||
&& TREE_CODE (TREE_TYPE (TREE_VALUE (arglist))) == POINTER_TYPE)
|
||||
expand_builtin_return (expand_expr (TREE_VALUE (arglist),
|
||||
NULL_RTX, VOIDmode, 0));
|
||||
return const0_rtx;
|
||||
|
||||
case BUILT_IN_SAVEREGS:
|
||||
/* Don't do __builtin_saveregs more than once in a function.
|
||||
Save the result of the first call and reuse it. */
|
||||
@ -6218,17 +6316,13 @@ expand_builtin (exp, target, subtarget, mode, ignore)
|
||||
|
||||
saveregs_value = temp;
|
||||
|
||||
/* This won't work inside a SEQUENCE--it really has to be
|
||||
at the start of the function. */
|
||||
if (in_sequence_p ())
|
||||
{
|
||||
/* Better to do this than to crash. */
|
||||
error ("`va_start' used within `({...})'");
|
||||
return temp;
|
||||
}
|
||||
|
||||
/* Put the sequence after the NOTE that starts the function. */
|
||||
/* Put the sequence after the NOTE that starts the function.
|
||||
If this is inside a SEQUENCE, make the outer-level insn
|
||||
chain current, so the code is placed at the start of the
|
||||
function. */
|
||||
push_topmost_sequence ();
|
||||
emit_insns_before (seq, NEXT_INSN (get_insns ()));
|
||||
pop_topmost_sequence ();
|
||||
return temp;
|
||||
}
|
||||
|
||||
@ -6755,6 +6849,435 @@ expand_builtin (exp, target, subtarget, mode, ignore)
|
||||
return expand_call (exp, target, ignore);
|
||||
}
|
||||
|
||||
/* Built-in functions to perform an untyped call and return. */
|
||||
|
||||
/* For each register that may be used for calling a function, this
|
||||
gives a mode used to copy the register's value. VOIDmode indicates
|
||||
the register is not used for calling a function. If the machine
|
||||
has register windows, this gives only the outbound registers.
|
||||
INCOMING_REGNO gives the corresponding inbound register. */
|
||||
static enum machine_mode apply_args_mode[FIRST_PSEUDO_REGISTER];
|
||||
|
||||
/* For each register that may be used for returning values, this gives
|
||||
a mode used to copy the register's value. VOIDmode indicates the
|
||||
register is not used for returning values. If the machine has
|
||||
register windows, this gives only the outbound registers.
|
||||
INCOMING_REGNO gives the corresponding inbound register. */
|
||||
static enum machine_mode apply_result_mode[FIRST_PSEUDO_REGISTER];
|
||||
|
||||
/* Return the size required for the block returned by __builtin_apply_args,
|
||||
and initialize apply_args_mode. */
|
||||
static int
|
||||
apply_args_size ()
|
||||
{
|
||||
static int size = -1;
|
||||
int align, regno;
|
||||
enum machine_mode mode;
|
||||
|
||||
/* The values computed by this function never change. */
|
||||
if (size < 0)
|
||||
{
|
||||
/* The first value is the incoming arg-pointer. */
|
||||
size = GET_MODE_SIZE (Pmode);
|
||||
|
||||
/* The second value is the structure value address unless this is
|
||||
passed as an "invisible" first argument. */
|
||||
if (struct_value_rtx)
|
||||
size += GET_MODE_SIZE (Pmode);
|
||||
|
||||
for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
|
||||
if (FUNCTION_ARG_REGNO_P (regno))
|
||||
{
|
||||
/* Search for the proper mode for copying this register's
|
||||
value. I'm not sure this is right, but it works so far. */
|
||||
enum machine_mode best_mode = VOIDmode;
|
||||
|
||||
for (mode = GET_CLASS_NARROWEST_MODE (MODE_INT);
|
||||
mode != VOIDmode;
|
||||
mode = GET_MODE_WIDER_MODE (mode))
|
||||
if (HARD_REGNO_MODE_OK (regno, mode)
|
||||
&& HARD_REGNO_NREGS (regno, mode) == 1)
|
||||
best_mode = mode;
|
||||
|
||||
if (best_mode == VOIDmode)
|
||||
for (mode = GET_CLASS_NARROWEST_MODE (MODE_FLOAT);
|
||||
mode != VOIDmode;
|
||||
mode = GET_MODE_WIDER_MODE (mode))
|
||||
if (HARD_REGNO_MODE_OK (regno, mode)
|
||||
&& (mov_optab->handlers[(int) mode].insn_code
|
||||
!= CODE_FOR_nothing))
|
||||
best_mode = mode;
|
||||
|
||||
mode = best_mode;
|
||||
if (mode == VOIDmode)
|
||||
abort ();
|
||||
|
||||
align = GET_MODE_ALIGNMENT (mode) / BITS_PER_UNIT;
|
||||
if (size % align != 0)
|
||||
size = CEIL (size, align) * align;
|
||||
size += GET_MODE_SIZE (mode);
|
||||
apply_args_mode[regno] = mode;
|
||||
}
|
||||
else
|
||||
apply_args_mode[regno] = VOIDmode;
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
/* Return the size required for the block returned by __builtin_apply,
|
||||
and initialize apply_result_mode. */
|
||||
static int
|
||||
apply_result_size ()
|
||||
{
|
||||
static int size = -1;
|
||||
int align, regno;
|
||||
enum machine_mode mode;
|
||||
|
||||
/* The values computed by this function never change. */
|
||||
if (size < 0)
|
||||
{
|
||||
size = 0;
|
||||
|
||||
for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
|
||||
if (FUNCTION_VALUE_REGNO_P (regno))
|
||||
{
|
||||
/* Search for the proper mode for copying this register's
|
||||
value. I'm not sure this is right, but it works so far. */
|
||||
enum machine_mode best_mode = VOIDmode;
|
||||
|
||||
for (mode = GET_CLASS_NARROWEST_MODE (MODE_INT);
|
||||
mode != TImode;
|
||||
mode = GET_MODE_WIDER_MODE (mode))
|
||||
if (HARD_REGNO_MODE_OK (regno, mode))
|
||||
best_mode = mode;
|
||||
|
||||
if (best_mode == VOIDmode)
|
||||
for (mode = GET_CLASS_NARROWEST_MODE (MODE_FLOAT);
|
||||
mode != VOIDmode;
|
||||
mode = GET_MODE_WIDER_MODE (mode))
|
||||
if (HARD_REGNO_MODE_OK (regno, mode)
|
||||
&& (mov_optab->handlers[(int) mode].insn_code
|
||||
!= CODE_FOR_nothing))
|
||||
best_mode = mode;
|
||||
|
||||
mode = best_mode;
|
||||
if (mode == VOIDmode)
|
||||
abort ();
|
||||
|
||||
align = GET_MODE_ALIGNMENT (mode) / BITS_PER_UNIT;
|
||||
if (size % align != 0)
|
||||
size = CEIL (size, align) * align;
|
||||
size += GET_MODE_SIZE (mode);
|
||||
apply_result_mode[regno] = mode;
|
||||
}
|
||||
else
|
||||
apply_result_mode[regno] = VOIDmode;
|
||||
|
||||
/* Allow targets that use untyped_call and untyped_return to override
|
||||
the size so that machine-specific information can be stored here. */
|
||||
#ifdef APPLY_RESULT_SIZE
|
||||
size = APPLY_RESULT_SIZE;
|
||||
#endif
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
#if defined (HAVE_untyped_call) || defined (HAVE_untyped_return)
|
||||
/* Create a vector describing the result block RESULT. If SAVEP is true,
|
||||
the result block is used to save the values; otherwise it is used to
|
||||
restore the values. */
|
||||
static rtx
|
||||
result_vector (savep, result)
|
||||
int savep;
|
||||
rtx result;
|
||||
{
|
||||
int regno, size, align, nelts;
|
||||
enum machine_mode mode;
|
||||
rtx reg, mem;
|
||||
rtx *savevec = (rtx *) alloca (FIRST_PSEUDO_REGISTER * sizeof (rtx));
|
||||
|
||||
size = nelts = 0;
|
||||
for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
|
||||
if ((mode = apply_result_mode[regno]) != VOIDmode)
|
||||
{
|
||||
align = GET_MODE_ALIGNMENT (mode) / BITS_PER_UNIT;
|
||||
if (size % align != 0)
|
||||
size = CEIL (size, align) * align;
|
||||
reg = gen_rtx (REG, mode, savep ? INCOMING_REGNO (regno) : regno);
|
||||
mem = change_address (result, mode,
|
||||
plus_constant (XEXP (result, 0), size));
|
||||
savevec[nelts++] = (savep
|
||||
? gen_rtx (SET, VOIDmode, mem, reg)
|
||||
: gen_rtx (SET, VOIDmode, reg, mem));
|
||||
size += GET_MODE_SIZE (mode);
|
||||
}
|
||||
return gen_rtx (PARALLEL, VOIDmode, gen_rtvec_v (nelts, savevec));
|
||||
}
|
||||
#endif /* HAVE_untyped_call or HAVE_untyped_return */
|
||||
|
||||
|
||||
/* Save the state required to perform an untyped call with the same
|
||||
arguments as were passed to the current function. */
|
||||
static rtx
|
||||
expand_builtin_apply_args ()
|
||||
{
|
||||
rtx registers;
|
||||
int size, align, regno;
|
||||
enum machine_mode mode;
|
||||
|
||||
/* Create a block where the arg-pointer, structure value address,
|
||||
and argument registers can be saved. */
|
||||
registers = assign_stack_local (BLKmode, apply_args_size (), -1);
|
||||
|
||||
/* Walk past the arg-pointer and structure value address. */
|
||||
size = GET_MODE_SIZE (Pmode);
|
||||
if (struct_value_rtx)
|
||||
size += GET_MODE_SIZE (Pmode);
|
||||
|
||||
/* Save each register used in calling a function to the block. */
|
||||
for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
|
||||
if ((mode = apply_args_mode[regno]) != VOIDmode)
|
||||
{
|
||||
align = GET_MODE_ALIGNMENT (mode) / BITS_PER_UNIT;
|
||||
if (size % align != 0)
|
||||
size = CEIL (size, align) * align;
|
||||
emit_move_insn (change_address (registers, mode,
|
||||
plus_constant (XEXP (registers, 0),
|
||||
size)),
|
||||
gen_rtx (REG, mode, INCOMING_REGNO (regno)));
|
||||
size += GET_MODE_SIZE (mode);
|
||||
}
|
||||
|
||||
/* Save the arg pointer to the block. */
|
||||
emit_move_insn (change_address (registers, Pmode, XEXP (registers, 0)),
|
||||
copy_to_reg (virtual_incoming_args_rtx));
|
||||
size = GET_MODE_SIZE (Pmode);
|
||||
|
||||
/* Save the structure value address unless this is passed as an
|
||||
"invisible" first argument. */
|
||||
if (struct_value_incoming_rtx)
|
||||
{
|
||||
emit_move_insn (change_address (registers, Pmode,
|
||||
plus_constant (XEXP (registers, 0),
|
||||
size)),
|
||||
copy_to_reg (struct_value_incoming_rtx));
|
||||
size += GET_MODE_SIZE (Pmode);
|
||||
}
|
||||
|
||||
/* Return the address of the block. */
|
||||
return copy_addr_to_reg (XEXP (registers, 0));
|
||||
}
|
||||
|
||||
/* Perform an untyped call and save the state required to perform an
|
||||
untyped return of whatever value was returned by the given function. */
|
||||
static rtx
|
||||
expand_builtin_apply (function, arguments, argsize)
|
||||
rtx function, arguments, argsize;
|
||||
{
|
||||
int size, align, regno;
|
||||
enum machine_mode mode;
|
||||
rtx incoming_args, result, reg, dest, call_insn;
|
||||
rtx old_stack_level = 0;
|
||||
rtx use_insns = 0;
|
||||
|
||||
/* Create a block where the return registers can be saved. */
|
||||
result = assign_stack_local (BLKmode, apply_result_size (), -1);
|
||||
|
||||
/* ??? The argsize value should be adjusted here. */
|
||||
|
||||
/* Fetch the arg pointer from the ARGUMENTS block. */
|
||||
incoming_args = gen_reg_rtx (Pmode);
|
||||
emit_move_insn (incoming_args,
|
||||
gen_rtx (MEM, Pmode, arguments));
|
||||
#ifndef STACK_GROWS_DOWNWARD
|
||||
incoming_args = expand_binop (Pmode, add_optab, incoming_args, argsize,
|
||||
incoming_args, 0, OPTAB_LIB_WIDEN);
|
||||
#endif
|
||||
|
||||
/* Perform postincrements before actually calling the function. */
|
||||
emit_queue ();
|
||||
|
||||
/* Push a new argument block and copy the arguments. */
|
||||
do_pending_stack_adjust ();
|
||||
emit_stack_save (SAVE_BLOCK, &old_stack_level, NULL_RTX);
|
||||
|
||||
/* Push a block of memory onto the stack to store the memory arguments.
|
||||
Save the address in a register, and copy the memory arguments. ??? I
|
||||
haven't figured out how the calling convention macros effect this,
|
||||
but it's likely that the source and/or destination addresses in
|
||||
the block copy will need updating in machine specific ways. */
|
||||
dest = copy_addr_to_reg (push_block (argsize, 0, 0));
|
||||
emit_block_move (gen_rtx (MEM, BLKmode, dest),
|
||||
gen_rtx (MEM, BLKmode, incoming_args),
|
||||
argsize,
|
||||
PARM_BOUNDARY / BITS_PER_UNIT);
|
||||
|
||||
/* Refer to the argument block. */
|
||||
apply_args_size ();
|
||||
arguments = gen_rtx (MEM, BLKmode, arguments);
|
||||
|
||||
/* Walk past the arg-pointer and structure value address. */
|
||||
size = GET_MODE_SIZE (Pmode);
|
||||
if (struct_value_rtx)
|
||||
size += GET_MODE_SIZE (Pmode);
|
||||
|
||||
/* Restore each of the registers previously saved. Make USE insns
|
||||
for each of these registers for use in making the call. */
|
||||
for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
|
||||
if ((mode = apply_args_mode[regno]) != VOIDmode)
|
||||
{
|
||||
align = GET_MODE_ALIGNMENT (mode) / BITS_PER_UNIT;
|
||||
if (size % align != 0)
|
||||
size = CEIL (size, align) * align;
|
||||
reg = gen_rtx (REG, mode, regno);
|
||||
emit_move_insn (reg,
|
||||
change_address (arguments, mode,
|
||||
plus_constant (XEXP (arguments, 0),
|
||||
size)));
|
||||
|
||||
push_to_sequence (use_insns);
|
||||
emit_insn (gen_rtx (USE, VOIDmode, reg));
|
||||
use_insns = get_insns ();
|
||||
end_sequence ();
|
||||
size += GET_MODE_SIZE (mode);
|
||||
}
|
||||
|
||||
/* Restore the structure value address unless this is passed as an
|
||||
"invisible" first argument. */
|
||||
size = GET_MODE_SIZE (Pmode);
|
||||
if (struct_value_rtx)
|
||||
{
|
||||
rtx value = gen_reg_rtx (Pmode);
|
||||
emit_move_insn (value,
|
||||
change_address (arguments, Pmode,
|
||||
plus_constant (XEXP (arguments, 0),
|
||||
size)));
|
||||
emit_move_insn (struct_value_rtx, value);
|
||||
if (GET_CODE (struct_value_rtx) == REG)
|
||||
{
|
||||
push_to_sequence (use_insns);
|
||||
emit_insn (gen_rtx (USE, VOIDmode, struct_value_rtx));
|
||||
use_insns = get_insns ();
|
||||
end_sequence ();
|
||||
}
|
||||
size += GET_MODE_SIZE (Pmode);
|
||||
}
|
||||
|
||||
/* All arguments and registers used for the call are set up by now! */
|
||||
function = prepare_call_address (function, NULL_TREE, &use_insns);
|
||||
|
||||
/* Ensure address is valid. SYMBOL_REF is already valid, so no need,
|
||||
and we don't want to load it into a register as an optimization,
|
||||
because prepare_call_address already did it if it should be done. */
|
||||
if (GET_CODE (function) != SYMBOL_REF)
|
||||
function = memory_address (FUNCTION_MODE, function);
|
||||
|
||||
/* Generate the actual call instruction and save the return value. */
|
||||
#ifdef HAVE_untyped_call
|
||||
if (HAVE_untyped_call)
|
||||
emit_call_insn (gen_untyped_call (gen_rtx (MEM, FUNCTION_MODE, function),
|
||||
result, result_vector (1, result)));
|
||||
else
|
||||
#endif
|
||||
#ifdef HAVE_call_value
|
||||
if (HAVE_call_value)
|
||||
{
|
||||
rtx valreg = 0;
|
||||
|
||||
/* Locate the unique return register. It is not possible to
|
||||
express a call that sets more than one return register using
|
||||
call_value; use untyped_call for that. In fact, untyped_call
|
||||
only needs to save the return registers in the given block. */
|
||||
for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
|
||||
if ((mode = apply_result_mode[regno]) != VOIDmode)
|
||||
{
|
||||
if (valreg)
|
||||
abort (); /* HAVE_untyped_call required. */
|
||||
valreg = gen_rtx (REG, mode, regno);
|
||||
}
|
||||
|
||||
emit_call_insn (gen_call_value (valreg,
|
||||
gen_rtx (MEM, FUNCTION_MODE, function),
|
||||
const0_rtx, NULL_RTX, const0_rtx));
|
||||
|
||||
emit_move_insn (change_address (result, GET_MODE (valreg),
|
||||
XEXP (result, 0)),
|
||||
valreg);
|
||||
}
|
||||
else
|
||||
#endif
|
||||
abort ();
|
||||
|
||||
/* Find the CALL insn we just emitted and write the USE insns before it. */
|
||||
for (call_insn = get_last_insn ();
|
||||
call_insn && GET_CODE (call_insn) != CALL_INSN;
|
||||
call_insn = PREV_INSN (call_insn))
|
||||
;
|
||||
|
||||
if (! call_insn)
|
||||
abort ();
|
||||
|
||||
/* Put the USE insns before the CALL. */
|
||||
emit_insns_before (use_insns, call_insn);
|
||||
|
||||
/* Restore the stack. */
|
||||
emit_stack_restore (SAVE_BLOCK, old_stack_level, NULL_RTX);
|
||||
|
||||
/* Return the address of the result block. */
|
||||
return copy_addr_to_reg (XEXP (result, 0));
|
||||
}
|
||||
|
||||
/* Perform an untyped return. */
|
||||
static void
|
||||
expand_builtin_return (result)
|
||||
rtx result;
|
||||
{
|
||||
int size, align, regno;
|
||||
enum machine_mode mode;
|
||||
rtx reg;
|
||||
rtx use_insns = 0;
|
||||
|
||||
apply_result_size ();
|
||||
result = gen_rtx (MEM, BLKmode, result);
|
||||
|
||||
#ifdef HAVE_untyped_return
|
||||
if (HAVE_untyped_return)
|
||||
{
|
||||
emit_jump_insn (gen_untyped_return (result, result_vector (0, result)));
|
||||
emit_barrier ();
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Restore the return value and note that each value is used. */
|
||||
size = 0;
|
||||
for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
|
||||
if ((mode = apply_result_mode[regno]) != VOIDmode)
|
||||
{
|
||||
align = GET_MODE_ALIGNMENT (mode) / BITS_PER_UNIT;
|
||||
if (size % align != 0)
|
||||
size = CEIL (size, align) * align;
|
||||
reg = gen_rtx (REG, mode, INCOMING_REGNO (regno));
|
||||
emit_move_insn (reg,
|
||||
change_address (result, mode,
|
||||
plus_constant (XEXP (result, 0),
|
||||
size)));
|
||||
|
||||
push_to_sequence (use_insns);
|
||||
emit_insn (gen_rtx (USE, VOIDmode, reg));
|
||||
use_insns = get_insns ();
|
||||
end_sequence ();
|
||||
size += GET_MODE_SIZE (mode);
|
||||
}
|
||||
|
||||
/* Put the USE insns before the return. */
|
||||
emit_insns (use_insns);
|
||||
|
||||
/* Return whatever values was restored by jumping directly to the end
|
||||
of the function. */
|
||||
expand_null_return ();
|
||||
}
|
||||
|
||||
/* Expand code for a post- or pre- increment or decrement
|
||||
and return the RTX for the result.
|
||||
POST is 1 for postinc/decrements and 0 for preinc/decrements. */
|
||||
|
Loading…
Reference in New Issue
Block a user