(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:
Tom Wood 1993-03-23 12:55:43 +00:00
parent d12b538233
commit 0006469db8

View File

@ -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. */