mirror of
git://gcc.gnu.org/git/gcc.git
synced 2025-03-21 23:30:59 +08:00
mips.c (MIPS_MAX_FIRST_STACK_STEP): New macro.
* config/mips/mips.c (MIPS_MAX_FIRST_STACK_STEP): New macro. (mips_save_restore_fn): New typedef. (mips_add_large_offset_to_sp, mips_emit_frame_related_store): Remove. (mips_set_frame_expr, mips_frame_set): Move above prologue code. (save_restore_insns): Remove, replacing with... (mips_save_restore_reg, mips_for_each_saved_reg): ...these new fns. (mips_save_reg, mips_restore_reg): New function. (mips_expand_prologue, mips_expand_epilogue): Rework. * config/mips/mips.h (MIPS_TEMP1_REGNUM, MIPS_TEMP2_REGNUM): Remove. (MIPS_PROLOGUE_TEMP_REGNUM, MIPS_EPILOGUE_TEMP_REGNUM): New macros. (MIPS_PROLOGUE_TEMP, MIPS_EPILOGUE_TEMP): New macros. From-SVN: r72182
This commit is contained in:
parent
aaa2154200
commit
be76302305
@ -1,3 +1,17 @@
|
||||
2003-10-07 Richard Sandiford <rsandifo@redhat.com>
|
||||
|
||||
* config/mips/mips.c (MIPS_MAX_FIRST_STACK_STEP): New macro.
|
||||
(mips_save_restore_fn): New typedef.
|
||||
(mips_add_large_offset_to_sp, mips_emit_frame_related_store): Remove.
|
||||
(mips_set_frame_expr, mips_frame_set): Move above prologue code.
|
||||
(save_restore_insns): Remove, replacing with...
|
||||
(mips_save_restore_reg, mips_for_each_saved_reg): ...these new fns.
|
||||
(mips_save_reg, mips_restore_reg): New function.
|
||||
(mips_expand_prologue, mips_expand_epilogue): Rework.
|
||||
* config/mips/mips.h (MIPS_TEMP1_REGNUM, MIPS_TEMP2_REGNUM): Remove.
|
||||
(MIPS_PROLOGUE_TEMP_REGNUM, MIPS_EPILOGUE_TEMP_REGNUM): New macros.
|
||||
(MIPS_PROLOGUE_TEMP, MIPS_EPILOGUE_TEMP): New macros.
|
||||
|
||||
2003-10-07 Richard Sandiford <rsandifo@redhat.com>
|
||||
|
||||
* config/mips/mips.c (mips_expand_prologue): Remove unused
|
||||
|
@ -80,6 +80,19 @@ enum internal_test {
|
||||
#define INTERNAL_SYMBOL_P(SYM) \
|
||||
(XSTR (SYM, 0)[0] == '*' && XSTR (SYM, 0)[1] == LOCAL_LABEL_PREFIX[0])
|
||||
|
||||
/* The maximum distance between the top of the stack frame and the
|
||||
value $sp has when we save & restore registers.
|
||||
|
||||
Use a maximum gap of 0x100 in the mips16 case. We can then use
|
||||
unextended instructions to save and restore registers, and to
|
||||
allocate and deallocate the top part of the frame.
|
||||
|
||||
The value in the !mips16 case must be a SMALL_OPERAND and must
|
||||
preserve the maximum stack alignment. It could really be 0x7ff0,
|
||||
but SGI's assemblers implement daddiu $sp,$sp,-0x7ff0 as a
|
||||
multi-instruction addu sequence. Use 0x7fe0 to work around this. */
|
||||
#define MIPS_MAX_FIRST_STACK_STEP (TARGET_MIPS16 ? 0x100 : 0x7fe0)
|
||||
|
||||
/* Classifies a non-literal integer constant.
|
||||
|
||||
CONSTANT_NONE
|
||||
@ -165,6 +178,9 @@ enum mips_address_type {
|
||||
ADDRESS_SYMBOLIC
|
||||
};
|
||||
|
||||
/* A function to save or store a register. The first argument is the
|
||||
register and the second is the stack slot. */
|
||||
typedef void (*mips_save_restore_fn) (rtx, rtx);
|
||||
|
||||
struct constant;
|
||||
struct mips_arg_info;
|
||||
@ -223,14 +239,16 @@ static void mips_file_start (void);
|
||||
static void mips_file_end (void);
|
||||
static unsigned int mips_global_pointer (void);
|
||||
static bool mips_save_reg_p (unsigned int);
|
||||
static rtx mips_add_large_offset_to_sp (HOST_WIDE_INT);
|
||||
static void mips_set_frame_expr (rtx);
|
||||
static rtx mips_frame_set (rtx, int);
|
||||
static void mips_emit_frame_related_store (rtx, rtx, HOST_WIDE_INT);
|
||||
static void save_restore_insns (int, rtx, long);
|
||||
static void mips_save_restore_reg (enum machine_mode, int, HOST_WIDE_INT,
|
||||
mips_save_restore_fn);
|
||||
static void mips_for_each_saved_reg (HOST_WIDE_INT, mips_save_restore_fn);
|
||||
static void mips_output_function_prologue (FILE *, HOST_WIDE_INT);
|
||||
static void mips_set_frame_expr (rtx);
|
||||
static rtx mips_frame_set (rtx, rtx);
|
||||
static void mips_save_reg (rtx, rtx);
|
||||
static void mips_gp_insn (rtx, rtx);
|
||||
static void mips_output_function_epilogue (FILE *, HOST_WIDE_INT);
|
||||
static void mips_restore_reg (rtx, rtx);
|
||||
static int symbolic_expression_p (rtx);
|
||||
static void mips_select_rtx_section (enum machine_mode, rtx,
|
||||
unsigned HOST_WIDE_INT);
|
||||
@ -6235,14 +6253,6 @@ mips_initial_elimination_offset (int from, int to)
|
||||
return offset;
|
||||
}
|
||||
|
||||
/* Common code to emit the insns (or to write the instructions to a file)
|
||||
to save/restore registers.
|
||||
|
||||
Other parts of the code assume that MIPS_TEMP1_REGNUM (aka large_reg)
|
||||
is not modified within save_restore_insns. */
|
||||
|
||||
#define BITSET_P(VALUE,BIT) (((VALUE) & (1L << (BIT))) != 0)
|
||||
|
||||
/* Implement RETURN_ADDR_RTX. Note, we do not support moving
|
||||
back to a previous frame. */
|
||||
rtx
|
||||
@ -6253,286 +6263,63 @@ mips_return_addr (int count, rtx frame ATTRIBUTE_UNUSED)
|
||||
|
||||
return get_hard_reg_initial_val (Pmode, GP_REG_FIRST + 31);
|
||||
}
|
||||
|
||||
|
||||
/* Emit instructions to load the value (SP + OFFSET) into MIPS_TEMP2_REGNUM
|
||||
and return an rtl expression for the register.
|
||||
|
||||
This function is a subroutine of save_restore_insns. It is used when
|
||||
OFFSET is too large to add in a single instruction. */
|
||||
|
||||
static rtx
|
||||
mips_add_large_offset_to_sp (HOST_WIDE_INT offset)
|
||||
{
|
||||
rtx reg = gen_rtx_REG (Pmode, MIPS_TEMP2_REGNUM);
|
||||
rtx offset_rtx = GEN_INT (offset);
|
||||
|
||||
emit_move_insn (reg, offset_rtx);
|
||||
if (Pmode == DImode)
|
||||
emit_insn (gen_adddi3 (reg, reg, stack_pointer_rtx));
|
||||
else
|
||||
emit_insn (gen_addsi3 (reg, reg, stack_pointer_rtx));
|
||||
return reg;
|
||||
}
|
||||
|
||||
/* Make the last instruction frame related and note that it performs
|
||||
the operation described by FRAME_PATTERN. */
|
||||
|
||||
/* Use FN to save or restore register REGNO. MODE is the register's
|
||||
mode and OFFSET is the offset of its save slot from the current
|
||||
stack pointer. */
|
||||
|
||||
static void
|
||||
mips_set_frame_expr (rtx frame_pattern)
|
||||
mips_save_restore_reg (enum machine_mode mode, int regno,
|
||||
HOST_WIDE_INT offset, mips_save_restore_fn fn)
|
||||
{
|
||||
rtx insn;
|
||||
rtx mem;
|
||||
|
||||
insn = get_last_insn ();
|
||||
RTX_FRAME_RELATED_P (insn) = 1;
|
||||
REG_NOTES (insn) = alloc_EXPR_LIST (REG_FRAME_RELATED_EXPR,
|
||||
frame_pattern,
|
||||
REG_NOTES (insn));
|
||||
}
|
||||
mem = gen_rtx_MEM (mode, plus_constant (stack_pointer_rtx, offset));
|
||||
if (!current_function_calls_eh_return)
|
||||
RTX_UNCHANGING_P (mem) = 1;
|
||||
|
||||
/* Return a frame-related rtx that stores REG at (SP + OFFSET).
|
||||
REG must be a single register. */
|
||||
|
||||
static rtx
|
||||
mips_frame_set (rtx reg, int offset)
|
||||
{
|
||||
rtx address = plus_constant (stack_pointer_rtx, offset);
|
||||
rtx set = gen_rtx_SET (VOIDmode, gen_rtx_MEM (GET_MODE (reg), address), reg);
|
||||
RTX_FRAME_RELATED_P (set) = 1;
|
||||
return set;
|
||||
fn (gen_rtx_REG (mode, regno), mem);
|
||||
}
|
||||
|
||||
|
||||
/* Emit a move instruction that stores REG in MEM. Make the instruction
|
||||
frame related and note that it stores REG at (SP + OFFSET). This
|
||||
function may be asked to store an FPR pair. */
|
||||
/* Call FN for each register that is saved by the current function.
|
||||
SP_OFFSET is the offset of the current stack pointer from the start
|
||||
of the frame. */
|
||||
|
||||
static void
|
||||
mips_emit_frame_related_store (rtx mem, rtx reg, HOST_WIDE_INT offset)
|
||||
mips_for_each_saved_reg (HOST_WIDE_INT sp_offset, mips_save_restore_fn fn)
|
||||
{
|
||||
if (GET_MODE (reg) == DFmode && mips_split_64bit_move_p (mem, reg))
|
||||
mips_split_64bit_move (mem, reg);
|
||||
else
|
||||
emit_move_insn (mem, reg);
|
||||
#define BITSET_P(VALUE, BIT) (((VALUE) & (1L << (BIT))) != 0)
|
||||
|
||||
if (GET_MODE (reg) == DFmode && !TARGET_FLOAT64)
|
||||
{
|
||||
rtx x1, x2;
|
||||
|
||||
/* Two registers are being stored, so the frame-related expression
|
||||
must be a PARALLEL rtx with one SET for each register. */
|
||||
x1 = mips_frame_set (mips_subword (reg, TARGET_BIG_ENDIAN), offset);
|
||||
x2 = mips_frame_set (mips_subword (reg, !TARGET_BIG_ENDIAN),
|
||||
offset + UNITS_PER_FPREG);
|
||||
mips_set_frame_expr (gen_rtx_PARALLEL (VOIDmode, gen_rtvec (2, x1, x2)));
|
||||
}
|
||||
else
|
||||
mips_set_frame_expr (mips_frame_set (reg, offset));
|
||||
}
|
||||
|
||||
|
||||
/* Emit instructions to save or restore the registers in
|
||||
cfun->machine->frame.mask and cfun->machine->frame.fmask.
|
||||
STORE_P is true to save registers (meaning we are expanding
|
||||
the prologue). If nonnull, LARGE_REG stores the value LARGE_OFFSET,
|
||||
which the caller thinks might be useful to us. */
|
||||
|
||||
static void
|
||||
save_restore_insns (int store_p, rtx large_reg, long large_offset)
|
||||
{
|
||||
long mask = cfun->machine->frame.mask;
|
||||
long fmask = cfun->machine->frame.fmask;
|
||||
enum machine_mode fpr_mode;
|
||||
HOST_WIDE_INT offset;
|
||||
int regno;
|
||||
rtx base_reg_rtx;
|
||||
HOST_WIDE_INT base_offset;
|
||||
HOST_WIDE_INT gp_offset;
|
||||
HOST_WIDE_INT fp_offset;
|
||||
HOST_WIDE_INT end_offset;
|
||||
|
||||
if (frame_pointer_needed
|
||||
&& ! BITSET_P (mask, HARD_FRAME_POINTER_REGNUM - GP_REG_FIRST))
|
||||
abort ();
|
||||
|
||||
if (mask == 0 && fmask == 0)
|
||||
return;
|
||||
|
||||
/* Save registers starting from high to low. The debuggers prefer at least
|
||||
the return register be stored at func+4, and also it allows us not to
|
||||
need a nop in the epilog if at least one register is reloaded in
|
||||
addition to return address. */
|
||||
offset = cfun->machine->frame.gp_sp_offset - sp_offset;
|
||||
for (regno = GP_REG_LAST; regno >= GP_REG_FIRST; regno--)
|
||||
if (BITSET_P (cfun->machine->frame.mask, regno - GP_REG_FIRST))
|
||||
{
|
||||
mips_save_restore_reg (gpr_mode, regno, offset, fn);
|
||||
offset -= GET_MODE_SIZE (gpr_mode);
|
||||
}
|
||||
|
||||
/* Save GP registers if needed. */
|
||||
if (mask)
|
||||
{
|
||||
/* Pick which pointer to use as a base register. For small frames, just
|
||||
use the stack pointer. Otherwise, use a temporary register. Save 2
|
||||
cycles if the save area is near the end of a large frame, by reusing
|
||||
the constant created in the prologue/epilogue to adjust the stack
|
||||
frame. */
|
||||
|
||||
gp_offset = cfun->machine->frame.gp_sp_offset;
|
||||
end_offset
|
||||
= gp_offset - (cfun->machine->frame.gp_reg_size
|
||||
- GET_MODE_SIZE (gpr_mode));
|
||||
|
||||
if (gp_offset < 0 || end_offset < 0)
|
||||
internal_error
|
||||
("gp_offset (%ld) or end_offset (%ld) is less than zero",
|
||||
(long) gp_offset, (long) end_offset);
|
||||
|
||||
/* If we see a large frame in mips16 mode, we save the registers
|
||||
before adjusting the stack pointer, and load them afterward. */
|
||||
else if (TARGET_MIPS16 && large_offset > 32767)
|
||||
base_reg_rtx = stack_pointer_rtx, base_offset = large_offset;
|
||||
|
||||
else if (gp_offset < 32768)
|
||||
base_reg_rtx = stack_pointer_rtx, base_offset = 0;
|
||||
|
||||
else if (large_reg != 0
|
||||
&& (unsigned HOST_WIDE_INT) (large_offset - gp_offset) < 32768
|
||||
&& (unsigned HOST_WIDE_INT) (large_offset - end_offset) < 32768)
|
||||
{
|
||||
base_reg_rtx = gen_rtx_REG (Pmode, MIPS_TEMP2_REGNUM);
|
||||
base_offset = large_offset;
|
||||
if (Pmode == DImode)
|
||||
emit_insn (gen_adddi3 (base_reg_rtx, large_reg, stack_pointer_rtx));
|
||||
else
|
||||
emit_insn (gen_addsi3 (base_reg_rtx, large_reg, stack_pointer_rtx));
|
||||
}
|
||||
else
|
||||
{
|
||||
base_offset = gp_offset;
|
||||
base_reg_rtx = mips_add_large_offset_to_sp (base_offset);
|
||||
}
|
||||
|
||||
/* When we restore the registers in MIPS16 mode, then if we are
|
||||
using a frame pointer, and this is not a large frame, the
|
||||
current stack pointer will be offset by
|
||||
current_function_outgoing_args_size. Doing it this way lets
|
||||
us avoid offsetting the frame pointer before copying it into
|
||||
the stack pointer; there is no instruction to set the stack
|
||||
pointer to the sum of a register and a constant. */
|
||||
if (TARGET_MIPS16
|
||||
&& ! store_p
|
||||
&& frame_pointer_needed
|
||||
&& large_offset <= 32767)
|
||||
base_offset += current_function_outgoing_args_size;
|
||||
|
||||
for (regno = GP_REG_LAST; regno >= GP_REG_FIRST; regno--)
|
||||
{
|
||||
if (BITSET_P (mask, regno - GP_REG_FIRST))
|
||||
{
|
||||
rtx reg_rtx;
|
||||
rtx mem_rtx
|
||||
= gen_rtx (MEM, gpr_mode,
|
||||
gen_rtx (PLUS, Pmode, base_reg_rtx,
|
||||
GEN_INT (gp_offset - base_offset)));
|
||||
|
||||
if (! current_function_calls_eh_return)
|
||||
RTX_UNCHANGING_P (mem_rtx) = 1;
|
||||
|
||||
/* The mips16 does not have an instruction to load
|
||||
$31, so we load $7 instead, and work things out
|
||||
in mips_expand_epilogue. */
|
||||
if (TARGET_MIPS16 && ! store_p && regno == GP_REG_FIRST + 31)
|
||||
reg_rtx = gen_rtx (REG, gpr_mode, GP_REG_FIRST + 7);
|
||||
/* The mips16 sometimes needs to save $18. */
|
||||
else if (TARGET_MIPS16
|
||||
&& regno != GP_REG_FIRST + 31
|
||||
&& ! M16_REG_P (regno))
|
||||
{
|
||||
if (! store_p)
|
||||
reg_rtx = gen_rtx (REG, gpr_mode, 6);
|
||||
else
|
||||
{
|
||||
reg_rtx = gen_rtx (REG, gpr_mode, 3);
|
||||
emit_move_insn (reg_rtx,
|
||||
gen_rtx (REG, gpr_mode, regno));
|
||||
}
|
||||
}
|
||||
else
|
||||
reg_rtx = gen_rtx (REG, gpr_mode, regno);
|
||||
|
||||
if (store_p)
|
||||
mips_emit_frame_related_store (mem_rtx, reg_rtx, gp_offset);
|
||||
else
|
||||
{
|
||||
emit_move_insn (reg_rtx, mem_rtx);
|
||||
if (TARGET_MIPS16
|
||||
&& regno != GP_REG_FIRST + 31
|
||||
&& ! M16_REG_P (regno))
|
||||
emit_move_insn (gen_rtx (REG, gpr_mode, regno),
|
||||
reg_rtx);
|
||||
}
|
||||
gp_offset -= GET_MODE_SIZE (gpr_mode);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
base_reg_rtx = 0, base_offset = 0;
|
||||
|
||||
/* Save floating point registers if needed. */
|
||||
if (fmask)
|
||||
{
|
||||
/* Pick which pointer to use as a base register. */
|
||||
fp_offset = cfun->machine->frame.fp_sp_offset;
|
||||
end_offset = fp_offset - (cfun->machine->frame.fp_reg_size
|
||||
- UNITS_PER_HWFPVALUE);
|
||||
|
||||
if (fp_offset < 0 || end_offset < 0)
|
||||
internal_error
|
||||
("fp_offset (%ld) or end_offset (%ld) is less than zero",
|
||||
(long) fp_offset, (long) end_offset);
|
||||
|
||||
else if (fp_offset < 32768)
|
||||
base_reg_rtx = stack_pointer_rtx, base_offset = 0;
|
||||
|
||||
else if (base_reg_rtx != 0
|
||||
&& (unsigned HOST_WIDE_INT) (base_offset - fp_offset) < 32768
|
||||
&& (unsigned HOST_WIDE_INT) (base_offset - end_offset) < 32768)
|
||||
; /* already set up for gp registers above */
|
||||
|
||||
else if (large_reg != 0
|
||||
&& (unsigned HOST_WIDE_INT) (large_offset - fp_offset) < 32768
|
||||
&& (unsigned HOST_WIDE_INT) (large_offset - end_offset) < 32768)
|
||||
{
|
||||
base_reg_rtx = gen_rtx_REG (Pmode, MIPS_TEMP2_REGNUM);
|
||||
base_offset = large_offset;
|
||||
if (Pmode == DImode)
|
||||
emit_insn (gen_adddi3 (base_reg_rtx, large_reg, stack_pointer_rtx));
|
||||
else
|
||||
emit_insn (gen_addsi3 (base_reg_rtx, large_reg, stack_pointer_rtx));
|
||||
}
|
||||
else
|
||||
{
|
||||
base_offset = fp_offset;
|
||||
base_reg_rtx = mips_add_large_offset_to_sp (fp_offset);
|
||||
}
|
||||
|
||||
/* This loop must iterate over the same space as its companion in
|
||||
compute_frame_size. */
|
||||
for (regno = (FP_REG_LAST - FP_INC + 1);
|
||||
regno >= FP_REG_FIRST;
|
||||
regno -= FP_INC)
|
||||
if (BITSET_P (fmask, regno - FP_REG_FIRST))
|
||||
{
|
||||
enum machine_mode sz = TARGET_SINGLE_FLOAT ? SFmode : DFmode;
|
||||
rtx reg_rtx = gen_rtx (REG, sz, regno);
|
||||
rtx mem_rtx = gen_rtx (MEM, sz,
|
||||
gen_rtx (PLUS, Pmode, base_reg_rtx,
|
||||
GEN_INT (fp_offset
|
||||
- base_offset)));
|
||||
if (! current_function_calls_eh_return)
|
||||
RTX_UNCHANGING_P (mem_rtx) = 1;
|
||||
|
||||
if (store_p)
|
||||
mips_emit_frame_related_store (mem_rtx, reg_rtx, fp_offset);
|
||||
else
|
||||
emit_move_insn (reg_rtx, mem_rtx);
|
||||
|
||||
fp_offset -= UNITS_PER_HWFPVALUE;
|
||||
}
|
||||
}
|
||||
/* This loop must iterate over the same space as its companion in
|
||||
compute_frame_size. */
|
||||
offset = cfun->machine->frame.fp_sp_offset - sp_offset;
|
||||
fpr_mode = (TARGET_SINGLE_FLOAT ? SFmode : DFmode);
|
||||
for (regno = (FP_REG_LAST - FP_INC + 1);
|
||||
regno >= FP_REG_FIRST;
|
||||
regno -= FP_INC)
|
||||
if (BITSET_P (cfun->machine->frame.fmask, regno - FP_REG_FIRST))
|
||||
{
|
||||
mips_save_restore_reg (fpr_mode, regno, offset, fn);
|
||||
offset -= GET_MODE_SIZE (fpr_mode);
|
||||
}
|
||||
#undef BITSET_P
|
||||
}
|
||||
|
||||
/* Set up the stack and frame (if desired) for the function. */
|
||||
@ -6623,6 +6410,68 @@ mips_output_function_prologue (FILE *file, HOST_WIDE_INT size ATTRIBUTE_UNUSED)
|
||||
output_asm_insn ("%(%<", 0);
|
||||
}
|
||||
|
||||
/* Make the last instruction frame related and note that it performs
|
||||
the operation described by FRAME_PATTERN. */
|
||||
|
||||
static void
|
||||
mips_set_frame_expr (rtx frame_pattern)
|
||||
{
|
||||
rtx insn;
|
||||
|
||||
insn = get_last_insn ();
|
||||
RTX_FRAME_RELATED_P (insn) = 1;
|
||||
REG_NOTES (insn) = alloc_EXPR_LIST (REG_FRAME_RELATED_EXPR,
|
||||
frame_pattern,
|
||||
REG_NOTES (insn));
|
||||
}
|
||||
|
||||
|
||||
/* Return a frame-related rtx that stores REG at MEM.
|
||||
REG must be a single register. */
|
||||
|
||||
static rtx
|
||||
mips_frame_set (rtx mem, rtx reg)
|
||||
{
|
||||
rtx set = gen_rtx_SET (VOIDmode, mem, reg);
|
||||
RTX_FRAME_RELATED_P (set) = 1;
|
||||
return set;
|
||||
}
|
||||
|
||||
|
||||
/* Save register REG to MEM. Make the instruction frame-related. */
|
||||
|
||||
static void
|
||||
mips_save_reg (rtx reg, rtx mem)
|
||||
{
|
||||
if (GET_MODE (reg) == DFmode && mips_split_64bit_move_p (mem, reg))
|
||||
{
|
||||
rtx x1, x2;
|
||||
|
||||
mips_split_64bit_move (mem, reg);
|
||||
x1 = mips_frame_set (mips_subword (mem, 0), mips_subword (reg, 0));
|
||||
x2 = mips_frame_set (mips_subword (mem, 1), mips_subword (reg, 1));
|
||||
mips_set_frame_expr (gen_rtx_PARALLEL (VOIDmode, gen_rtvec (2, x1, x2)));
|
||||
}
|
||||
else
|
||||
{
|
||||
if (TARGET_MIPS16
|
||||
&& REGNO (reg) != GP_REG_FIRST + 31
|
||||
&& !M16_REG_P (REGNO (reg)))
|
||||
{
|
||||
/* Save a non-mips16 register by moving it through a temporary.
|
||||
We don't need to do this for $31 since there's a special
|
||||
instruction for it. */
|
||||
emit_move_insn (MIPS_PROLOGUE_TEMP (GET_MODE (reg)), reg);
|
||||
emit_move_insn (mem, MIPS_PROLOGUE_TEMP (GET_MODE (reg)));
|
||||
}
|
||||
else
|
||||
emit_move_insn (mem, reg);
|
||||
|
||||
mips_set_frame_expr (mips_frame_set (mem, reg));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Emit an instruction to move SRC into DEST. When generating
|
||||
explicit reloc code, mark the instruction as potentially dead. */
|
||||
|
||||
@ -6648,126 +6497,102 @@ mips_gp_insn (rtx dest, rtx src)
|
||||
void
|
||||
mips_expand_prologue (void)
|
||||
{
|
||||
HOST_WIDE_INT tsize;
|
||||
rtx tmp_rtx = 0;
|
||||
HOST_WIDE_INT size;
|
||||
|
||||
if (cfun->machine->global_pointer > 0)
|
||||
REGNO (pic_offset_table_rtx) = cfun->machine->global_pointer;
|
||||
|
||||
tsize = compute_frame_size (get_frame_size ());
|
||||
size = compute_frame_size (get_frame_size ());
|
||||
|
||||
if (tsize > 0)
|
||||
/* Save the registers. Allocate up to MIPS_MAX_FIRST_STACK_STEP
|
||||
bytes beforehand; this is enough to cover the register save area
|
||||
without going out of range. */
|
||||
if ((cfun->machine->frame.mask | cfun->machine->frame.fmask) != 0)
|
||||
{
|
||||
rtx tsize_rtx = GEN_INT (tsize);
|
||||
HOST_WIDE_INT step1;
|
||||
|
||||
/* In mips16 mode with a large frame, we save the registers before
|
||||
adjusting the stack. */
|
||||
if (!TARGET_MIPS16 || tsize <= 32768)
|
||||
step1 = MIN (size, MIPS_MAX_FIRST_STACK_STEP);
|
||||
RTX_FRAME_RELATED_P (emit_insn (gen_add3_insn (stack_pointer_rtx,
|
||||
stack_pointer_rtx,
|
||||
GEN_INT (-step1)))) = 1;
|
||||
size -= step1;
|
||||
mips_for_each_saved_reg (size, mips_save_reg);
|
||||
}
|
||||
|
||||
/* Allocate the rest of the frame. */
|
||||
if (size > 0)
|
||||
{
|
||||
if (SMALL_OPERAND (-size))
|
||||
RTX_FRAME_RELATED_P (emit_insn (gen_add3_insn (stack_pointer_rtx,
|
||||
stack_pointer_rtx,
|
||||
GEN_INT (-size)))) = 1;
|
||||
else
|
||||
{
|
||||
if (tsize > 32768)
|
||||
emit_move_insn (MIPS_PROLOGUE_TEMP (Pmode), GEN_INT (size));
|
||||
if (TARGET_MIPS16)
|
||||
{
|
||||
rtx adjustment_rtx;
|
||||
/* There are no instructions to add or subtract registers
|
||||
from the stack pointer, so use the frame pointer as a
|
||||
temporary. We should always be using a frame pointer
|
||||
in this case anyway. */
|
||||
if (!frame_pointer_needed)
|
||||
abort ();
|
||||
|
||||
adjustment_rtx = gen_rtx (REG, Pmode, MIPS_TEMP1_REGNUM);
|
||||
emit_move_insn (adjustment_rtx, tsize_rtx);
|
||||
emit_insn (gen_sub3_insn (stack_pointer_rtx,
|
||||
stack_pointer_rtx,
|
||||
adjustment_rtx));
|
||||
emit_move_insn (hard_frame_pointer_rtx, stack_pointer_rtx);
|
||||
emit_insn (gen_sub3_insn (hard_frame_pointer_rtx,
|
||||
hard_frame_pointer_rtx,
|
||||
MIPS_PROLOGUE_TEMP (Pmode)));
|
||||
emit_move_insn (stack_pointer_rtx, hard_frame_pointer_rtx);
|
||||
}
|
||||
else
|
||||
emit_insn (gen_add3_insn (stack_pointer_rtx,
|
||||
emit_insn (gen_sub3_insn (stack_pointer_rtx,
|
||||
stack_pointer_rtx,
|
||||
GEN_INT (-tsize)));
|
||||
MIPS_PROLOGUE_TEMP (Pmode)));
|
||||
|
||||
/* Describe the combined effect of the previous instructions. */
|
||||
mips_set_frame_expr
|
||||
(gen_rtx_SET (VOIDmode, stack_pointer_rtx,
|
||||
plus_constant (stack_pointer_rtx, -tsize)));
|
||||
}
|
||||
|
||||
save_restore_insns (1, tmp_rtx, tsize);
|
||||
|
||||
if (TARGET_ABICALLS && !TARGET_NEWABI && !current_function_is_leaf)
|
||||
emit_insn (gen_cprestore
|
||||
(GEN_INT (current_function_outgoing_args_size)));
|
||||
|
||||
if (TARGET_MIPS16 && tsize > 32768)
|
||||
{
|
||||
rtx reg_rtx;
|
||||
|
||||
if (!frame_pointer_needed)
|
||||
abort ();
|
||||
|
||||
reg_rtx = gen_rtx (REG, Pmode, 3);
|
||||
emit_move_insn (hard_frame_pointer_rtx, stack_pointer_rtx);
|
||||
emit_move_insn (reg_rtx, tsize_rtx);
|
||||
emit_insn (gen_sub3_insn (hard_frame_pointer_rtx,
|
||||
hard_frame_pointer_rtx,
|
||||
reg_rtx));
|
||||
emit_move_insn (stack_pointer_rtx, hard_frame_pointer_rtx);
|
||||
}
|
||||
|
||||
if (frame_pointer_needed)
|
||||
{
|
||||
rtx insn = 0;
|
||||
|
||||
/* On the mips16, we encourage the use of unextended
|
||||
instructions when using the frame pointer by pointing the
|
||||
frame pointer ahead of the argument space allocated on
|
||||
the stack. */
|
||||
if (TARGET_MIPS16 && tsize > 32767)
|
||||
{
|
||||
/* In this case, we have already copied the stack
|
||||
pointer into the frame pointer, above. We need only
|
||||
adjust for the outgoing argument size. */
|
||||
if (current_function_outgoing_args_size != 0)
|
||||
{
|
||||
rtx incr = GEN_INT (current_function_outgoing_args_size);
|
||||
if (Pmode == DImode)
|
||||
insn = emit_insn (gen_adddi3 (hard_frame_pointer_rtx,
|
||||
hard_frame_pointer_rtx,
|
||||
incr));
|
||||
else
|
||||
insn = emit_insn (gen_addsi3 (hard_frame_pointer_rtx,
|
||||
hard_frame_pointer_rtx,
|
||||
incr));
|
||||
}
|
||||
}
|
||||
else if (TARGET_MIPS16 && current_function_outgoing_args_size != 0)
|
||||
{
|
||||
rtx incr = GEN_INT (current_function_outgoing_args_size);
|
||||
if (Pmode == DImode)
|
||||
insn = emit_insn (gen_adddi3 (hard_frame_pointer_rtx,
|
||||
stack_pointer_rtx,
|
||||
incr));
|
||||
else
|
||||
insn = emit_insn (gen_addsi3 (hard_frame_pointer_rtx,
|
||||
stack_pointer_rtx,
|
||||
incr));
|
||||
}
|
||||
else if (Pmode == DImode)
|
||||
insn = emit_insn (gen_movdi (hard_frame_pointer_rtx,
|
||||
stack_pointer_rtx));
|
||||
else
|
||||
insn = emit_insn (gen_movsi (hard_frame_pointer_rtx,
|
||||
stack_pointer_rtx));
|
||||
|
||||
if (insn)
|
||||
RTX_FRAME_RELATED_P (insn) = 1;
|
||||
plus_constant (stack_pointer_rtx, -size)));
|
||||
}
|
||||
}
|
||||
|
||||
/* Set up the frame pointer, if we're using one. In mips16 code,
|
||||
we point the frame pointer ahead of the outgoing argument area.
|
||||
This should allow more variables & incoming arguments to be
|
||||
acceesed with unextended instructions. */
|
||||
if (frame_pointer_needed)
|
||||
{
|
||||
if (TARGET_MIPS16 && current_function_outgoing_args_size != 0)
|
||||
{
|
||||
rtx offset = GEN_INT (current_function_outgoing_args_size);
|
||||
RTX_FRAME_RELATED_P
|
||||
(emit_insn (gen_add3_insn (hard_frame_pointer_rtx,
|
||||
stack_pointer_rtx,
|
||||
offset))) = 1;
|
||||
}
|
||||
else
|
||||
RTX_FRAME_RELATED_P (emit_move_insn (hard_frame_pointer_rtx,
|
||||
stack_pointer_rtx)) = 1;
|
||||
}
|
||||
|
||||
/* If generating o32/o64 abicalls, save $gp on the stack. */
|
||||
if (TARGET_ABICALLS && !TARGET_NEWABI && !current_function_is_leaf)
|
||||
emit_insn (gen_cprestore (GEN_INT (current_function_outgoing_args_size)));
|
||||
|
||||
/* If generating n32/n64 abicalls, emit the instructions to load $gp. */
|
||||
if (TARGET_ABICALLS && TARGET_NEWABI && cfun->machine->global_pointer > 0)
|
||||
{
|
||||
rtx temp, fnsymbol, fnaddr;
|
||||
rtx fnsymbol, fnaddr;
|
||||
|
||||
temp = gen_rtx_REG (Pmode, MIPS_TEMP1_REGNUM);
|
||||
fnsymbol = XEXP (DECL_RTL (current_function_decl), 0);
|
||||
fnaddr = gen_rtx_REG (Pmode, PIC_FUNCTION_ADDR_REGNUM);
|
||||
|
||||
mips_gp_insn (temp, mips_lui_reloc (fnsymbol, RELOC_LOADGP_HI));
|
||||
mips_gp_insn (temp, gen_rtx_PLUS (Pmode, temp, fnaddr));
|
||||
mips_gp_insn (MIPS_PROLOGUE_TEMP (Pmode),
|
||||
mips_lui_reloc (fnsymbol, RELOC_LOADGP_HI));
|
||||
mips_gp_insn (MIPS_PROLOGUE_TEMP (Pmode),
|
||||
gen_rtx_PLUS (Pmode, MIPS_PROLOGUE_TEMP (Pmode), fnaddr));
|
||||
mips_gp_insn (pic_offset_table_rtx,
|
||||
gen_rtx_PLUS (Pmode, temp,
|
||||
gen_rtx_PLUS (Pmode, MIPS_PROLOGUE_TEMP (Pmode),
|
||||
mips_reloc (fnsymbol, RELOC_LOADGP_LO)));
|
||||
|
||||
if (!TARGET_EXPLICIT_RELOCS)
|
||||
@ -6840,6 +6665,27 @@ mips_output_function_epilogue (FILE *file ATTRIBUTE_UNUSED,
|
||||
REGNO (pic_offset_table_rtx) = GLOBAL_POINTER_REGNUM;
|
||||
}
|
||||
|
||||
/* Emit instructions to restore register REG from slot MEM. */
|
||||
|
||||
static void
|
||||
mips_restore_reg (rtx reg, rtx mem)
|
||||
{
|
||||
/* There's no mips16 instruction to load $31 directly. Load into
|
||||
$7 instead and adjust the return insn appropriately. */
|
||||
if (TARGET_MIPS16 && REGNO (reg) == GP_REG_FIRST + 31)
|
||||
reg = gen_rtx_REG (GET_MODE (reg), 7);
|
||||
|
||||
if (TARGET_MIPS16 && !M16_REG_P (REGNO (reg)))
|
||||
{
|
||||
/* Can't restore directly; move through a temporary. */
|
||||
emit_move_insn (MIPS_EPILOGUE_TEMP (GET_MODE (reg)), mem);
|
||||
emit_move_insn (reg, MIPS_EPILOGUE_TEMP (GET_MODE (reg)));
|
||||
}
|
||||
else
|
||||
emit_move_insn (reg, mem);
|
||||
}
|
||||
|
||||
|
||||
/* Expand the epilogue into a bunch of separate insns. SIBCALL_P is true
|
||||
if this epilogue precedes a sibling call, false if it is for a normal
|
||||
"epilogue" pattern. */
|
||||
@ -6847,9 +6693,8 @@ mips_output_function_epilogue (FILE *file ATTRIBUTE_UNUSED,
|
||||
void
|
||||
mips_expand_epilogue (int sibcall_p)
|
||||
{
|
||||
HOST_WIDE_INT tsize = cfun->machine->frame.total_size;
|
||||
rtx tsize_rtx = GEN_INT (tsize);
|
||||
rtx tmp_rtx = (rtx)0;
|
||||
HOST_WIDE_INT step1, step2;
|
||||
rtx base, target;
|
||||
|
||||
if (!sibcall_p && mips_can_use_return_insn ())
|
||||
{
|
||||
@ -6857,122 +6702,92 @@ mips_expand_epilogue (int sibcall_p)
|
||||
return;
|
||||
}
|
||||
|
||||
if (tsize > 32767 && ! TARGET_MIPS16)
|
||||
/* Split the frame into two. STEP1 is the amount of stack we should
|
||||
deallocate before restoring the registers. STEP2 is the amount we
|
||||
should deallocate afterwards.
|
||||
|
||||
Start off by assuming that no registers need to be restored. */
|
||||
step1 = cfun->machine->frame.total_size;
|
||||
step2 = 0;
|
||||
|
||||
/* Work out which register holds the frame address. Account for the
|
||||
frame pointer offset used by mips16 code. */
|
||||
if (!frame_pointer_needed)
|
||||
base = stack_pointer_rtx;
|
||||
else
|
||||
{
|
||||
tmp_rtx = gen_rtx_REG (Pmode, MIPS_TEMP1_REGNUM);
|
||||
emit_move_insn (tmp_rtx, tsize_rtx);
|
||||
tsize_rtx = tmp_rtx;
|
||||
base = hard_frame_pointer_rtx;
|
||||
if (TARGET_MIPS16)
|
||||
step1 -= current_function_outgoing_args_size;
|
||||
}
|
||||
|
||||
if (tsize > 0)
|
||||
/* If we need to restore registers, deallocate as much stack as
|
||||
possible in the second step without going out of range. */
|
||||
if ((cfun->machine->frame.mask | cfun->machine->frame.fmask) != 0)
|
||||
{
|
||||
long orig_tsize = tsize;
|
||||
|
||||
if (frame_pointer_needed)
|
||||
{
|
||||
emit_insn (gen_blockage ());
|
||||
|
||||
/* On the mips16, the frame pointer is offset from the stack
|
||||
pointer by current_function_outgoing_args_size. We
|
||||
account for that by changing tsize. Note that this can
|
||||
actually make tsize negative. */
|
||||
if (TARGET_MIPS16)
|
||||
{
|
||||
tsize -= current_function_outgoing_args_size;
|
||||
|
||||
/* If we have a large frame, it's easier to add to $6
|
||||
than to $sp, since the mips16 has no instruction to
|
||||
add a register to $sp. */
|
||||
if (orig_tsize > 32767)
|
||||
{
|
||||
rtx g6_rtx = gen_rtx (REG, Pmode, GP_REG_FIRST + 6);
|
||||
|
||||
emit_move_insn (g6_rtx, GEN_INT (tsize));
|
||||
if (Pmode == DImode)
|
||||
emit_insn (gen_adddi3 (hard_frame_pointer_rtx,
|
||||
hard_frame_pointer_rtx,
|
||||
g6_rtx));
|
||||
else
|
||||
emit_insn (gen_addsi3 (hard_frame_pointer_rtx,
|
||||
hard_frame_pointer_rtx,
|
||||
g6_rtx));
|
||||
tsize = 0;
|
||||
}
|
||||
|
||||
if (tsize && tsize != orig_tsize)
|
||||
tsize_rtx = GEN_INT (tsize);
|
||||
}
|
||||
|
||||
if (Pmode == DImode)
|
||||
emit_insn (gen_movdi (stack_pointer_rtx, hard_frame_pointer_rtx));
|
||||
else
|
||||
emit_insn (gen_movsi (stack_pointer_rtx, hard_frame_pointer_rtx));
|
||||
}
|
||||
|
||||
/* The GP/PIC register is implicitly used by all SYMBOL_REFs, so if we
|
||||
are going to restore it, then we must emit a blockage insn to
|
||||
prevent the scheduler from moving the restore out of the epilogue. */
|
||||
else if (TARGET_ABICALLS && mips_abi != ABI_32 && mips_abi != ABI_O64
|
||||
&& (cfun->machine->frame.mask
|
||||
& (1L << (PIC_OFFSET_TABLE_REGNUM - GP_REG_FIRST))))
|
||||
emit_insn (gen_blockage ());
|
||||
|
||||
save_restore_insns (0, tmp_rtx, orig_tsize);
|
||||
|
||||
/* In mips16 mode with a large frame, we adjust the stack
|
||||
pointer before restoring the registers. In this case, we
|
||||
should always be using a frame pointer, so everything should
|
||||
have been handled above. */
|
||||
if (tsize > 32767 && TARGET_MIPS16)
|
||||
abort ();
|
||||
|
||||
if (current_function_calls_eh_return)
|
||||
{
|
||||
rtx eh_ofs = EH_RETURN_STACKADJ_RTX;
|
||||
if (Pmode == DImode)
|
||||
emit_insn (gen_adddi3 (eh_ofs, eh_ofs, tsize_rtx));
|
||||
else
|
||||
emit_insn (gen_addsi3 (eh_ofs, eh_ofs, tsize_rtx));
|
||||
tsize_rtx = eh_ofs;
|
||||
}
|
||||
|
||||
emit_insn (gen_blockage ());
|
||||
|
||||
if (tsize != 0 || current_function_calls_eh_return)
|
||||
{
|
||||
if (!TARGET_MIPS16 || !current_function_calls_eh_return)
|
||||
{
|
||||
if (Pmode == DImode)
|
||||
emit_insn (gen_adddi3 (stack_pointer_rtx, stack_pointer_rtx,
|
||||
tsize_rtx));
|
||||
else
|
||||
emit_insn (gen_addsi3 (stack_pointer_rtx, stack_pointer_rtx,
|
||||
tsize_rtx));
|
||||
}
|
||||
else
|
||||
{
|
||||
/* We need to work around not being able to add a register
|
||||
to the stack pointer directly. Use register $6 as an
|
||||
intermediate step. */
|
||||
|
||||
rtx g6_rtx = gen_rtx (REG, Pmode, GP_REG_FIRST + 6);
|
||||
|
||||
if (Pmode == DImode)
|
||||
{
|
||||
emit_insn (gen_movdi (g6_rtx, stack_pointer_rtx));
|
||||
emit_insn (gen_adddi3 (g6_rtx, g6_rtx, tsize_rtx));
|
||||
emit_insn (gen_movdi (stack_pointer_rtx, g6_rtx));
|
||||
}
|
||||
else
|
||||
{
|
||||
emit_insn (gen_movsi (g6_rtx, stack_pointer_rtx));
|
||||
emit_insn (gen_addsi3 (g6_rtx, g6_rtx, tsize_rtx));
|
||||
emit_insn (gen_movsi (stack_pointer_rtx, g6_rtx));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
step2 = MIN (step1, MIPS_MAX_FIRST_STACK_STEP);
|
||||
step1 -= step2;
|
||||
}
|
||||
|
||||
/* Set TARGET to BASE + STEP1. */
|
||||
target = base;
|
||||
if (step1 > 0)
|
||||
{
|
||||
rtx adjust;
|
||||
|
||||
/* Get an rtx for STEP1 that we can add to BASE. */
|
||||
adjust = GEN_INT (step1);
|
||||
if (!SMALL_OPERAND (step1))
|
||||
{
|
||||
emit_move_insn (MIPS_EPILOGUE_TEMP (Pmode), adjust);
|
||||
adjust = MIPS_EPILOGUE_TEMP (Pmode);
|
||||
}
|
||||
|
||||
/* Normal mode code can copy the result straight into $sp. */
|
||||
if (!TARGET_MIPS16)
|
||||
target = stack_pointer_rtx;
|
||||
|
||||
emit_insn (gen_add3_insn (target, base, adjust));
|
||||
}
|
||||
|
||||
/* Copy TARGET into the stack pointer. */
|
||||
if (target != stack_pointer_rtx)
|
||||
emit_move_insn (stack_pointer_rtx, target);
|
||||
|
||||
/* If we're using addressing macros for n32/n64 abicalls, $gp is
|
||||
implicitly used by all SYMBOL_REFs. We must emit a blockage
|
||||
insn before restoring it. */
|
||||
if (TARGET_ABICALLS && TARGET_NEWABI && !TARGET_EXPLICIT_RELOCS)
|
||||
emit_insn (gen_blockage ());
|
||||
|
||||
/* Restore the registers. */
|
||||
mips_for_each_saved_reg (cfun->machine->frame.total_size - step2,
|
||||
mips_restore_reg);
|
||||
|
||||
/* Deallocate the final bit of the frame. */
|
||||
if (step2 > 0)
|
||||
emit_insn (gen_add3_insn (stack_pointer_rtx,
|
||||
stack_pointer_rtx,
|
||||
GEN_INT (step2)));
|
||||
|
||||
/* Add in the __builtin_eh_return stack adjustment. We need to
|
||||
use a temporary in mips16 code. */
|
||||
if (current_function_calls_eh_return)
|
||||
{
|
||||
if (TARGET_MIPS16)
|
||||
{
|
||||
emit_move_insn (MIPS_EPILOGUE_TEMP (Pmode), stack_pointer_rtx);
|
||||
emit_insn (gen_add3_insn (MIPS_EPILOGUE_TEMP (Pmode),
|
||||
MIPS_EPILOGUE_TEMP (Pmode),
|
||||
EH_RETURN_STACKADJ_RTX));
|
||||
emit_move_insn (stack_pointer_rtx, MIPS_EPILOGUE_TEMP (Pmode));
|
||||
}
|
||||
else
|
||||
emit_insn (gen_add3_insn (stack_pointer_rtx,
|
||||
stack_pointer_rtx,
|
||||
EH_RETURN_STACKADJ_RTX));
|
||||
}
|
||||
|
||||
if (!sibcall_p)
|
||||
{
|
||||
/* The mips16 loads the return address into $7, not $31. */
|
||||
|
@ -1674,15 +1674,18 @@ extern char mips_hard_regno_mode_ok[][FIRST_PSEUDO_REGISTER];
|
||||
/* Pass structure addresses as an "invisible" first argument. */
|
||||
#define STRUCT_VALUE 0
|
||||
|
||||
/* Mips registers used in prologue/epilogue code when the stack frame
|
||||
is larger than 32K bytes. These registers must come from the
|
||||
scratch register set, and not used for passing and returning
|
||||
arguments and any other information used in the calling sequence
|
||||
(such as pic). Must start at 12, since t0/t3 are parameter passing
|
||||
registers in the 64 bit ABI. */
|
||||
/* Registers used as temporaries in prologue/epilogue code. If we're
|
||||
generating mips16 code, these registers must come from the core set
|
||||
of 8. The prologue register mustn't conflict with any incoming
|
||||
arguments, the static chain pointer, or the frame pointer. The
|
||||
epilogue temporary mustn't conflict with the return registers, the
|
||||
frame pointer, the EH stack adjustment, or the EH data registers. */
|
||||
|
||||
#define MIPS_TEMP1_REGNUM (GP_REG_FIRST + 12)
|
||||
#define MIPS_TEMP2_REGNUM (GP_REG_FIRST + 13)
|
||||
#define MIPS_PROLOGUE_TEMP_REGNUM (GP_REG_FIRST + 3)
|
||||
#define MIPS_EPILOGUE_TEMP_REGNUM (GP_REG_FIRST + (TARGET_MIPS16 ? 6 : 8))
|
||||
|
||||
#define MIPS_PROLOGUE_TEMP(MODE) gen_rtx_REG (MODE, MIPS_PROLOGUE_TEMP_REGNUM)
|
||||
#define MIPS_EPILOGUE_TEMP(MODE) gen_rtx_REG (MODE, MIPS_EPILOGUE_TEMP_REGNUM)
|
||||
|
||||
/* Define this macro if it is as good or better to call a constant
|
||||
function address than to call an address kept in a register. */
|
||||
|
Loading…
x
Reference in New Issue
Block a user