summaryrefslogtreecommitdiff
path: root/gcc
diff options
context:
space:
mode:
authorrsandifo <rsandifo@138bc75d-0d04-0410-961f-82ee72b054a4>2003-10-07 06:38:15 +0000
committerrsandifo <rsandifo@138bc75d-0d04-0410-961f-82ee72b054a4>2003-10-07 06:38:15 +0000
commit3ba8ab31ab7b3565c05aca5ee0b3026490626e2e (patch)
tree617a7d41caa13c770aa91f143e1fdfc8cdcf91ac /gcc
parent8345cb047c7d9ceb94db3c289c11fe77b358e7dc (diff)
downloadgcc-3ba8ab31ab7b3565c05aca5ee0b3026490626e2e.tar.gz
* 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. git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@72182 138bc75d-0d04-0410-961f-82ee72b054a4
Diffstat (limited to 'gcc')
-rw-r--r--gcc/ChangeLog14
-rw-r--r--gcc/config/mips/mips.c753
-rw-r--r--gcc/config/mips/mips.h21
3 files changed, 310 insertions, 478 deletions
diff --git a/gcc/ChangeLog b/gcc/ChangeLog
index 0728718e53f..563d2386707 100644
--- a/gcc/ChangeLog
+++ b/gcc/ChangeLog
@@ -1,5 +1,19 @@
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
traversal of function arguments.
diff --git a/gcc/config/mips/mips.c b/gcc/config/mips/mips.c
index 1eb942689c0..5a8b05938ff 100644
--- a/gcc/config/mips/mips.c
+++ b/gcc/config/mips/mips.c
@@ -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;
-
- 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));
-}
+ rtx mem;
-/* Return a frame-related rtx that stores REG at (SP + OFFSET).
- REG must be a single register. */
+ mem = gen_rtx_MEM (mode, plus_constant (stack_pointer_rtx, offset));
+ if (!current_function_calls_eh_return)
+ RTX_UNCHANGING_P (mem) = 1;
-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)));
+ plus_constant (stack_pointer_rtx, -size)));
}
+ }
- 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)
+ /* 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 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;
+ 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));
- }
+ step2 = MIN (step1, MIPS_MAX_FIRST_STACK_STEP);
+ step1 -= step2;
+ }
- /* 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 ();
+ /* Set TARGET to BASE + STEP1. */
+ target = base;
+ if (step1 > 0)
+ {
+ rtx adjust;
- if (current_function_calls_eh_return)
+ /* Get an rtx for STEP1 that we can add to BASE. */
+ adjust = GEN_INT (step1);
+ if (!SMALL_OPERAND (step1))
{
- 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_move_insn (MIPS_EPILOGUE_TEMP (Pmode), adjust);
+ adjust = MIPS_EPILOGUE_TEMP (Pmode);
}
- emit_insn (gen_blockage ());
+ /* Normal mode code can copy the result straight into $sp. */
+ if (!TARGET_MIPS16)
+ target = stack_pointer_rtx;
- 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. */
+ emit_insn (gen_add3_insn (target, base, adjust));
+ }
- rtx g6_rtx = gen_rtx (REG, Pmode, GP_REG_FIRST + 6);
+ /* Copy TARGET into the stack pointer. */
+ if (target != stack_pointer_rtx)
+ emit_move_insn (stack_pointer_rtx, target);
- 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));
- }
- }
+ /* 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. */
diff --git a/gcc/config/mips/mips.h b/gcc/config/mips/mips.h
index bba993afa9a..b3de61ee71c 100644
--- a/gcc/config/mips/mips.h
+++ b/gcc/config/mips/mips.h
@@ -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. */
-
-#define MIPS_TEMP1_REGNUM (GP_REG_FIRST + 12)
-#define MIPS_TEMP2_REGNUM (GP_REG_FIRST + 13)
+/* 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_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. */