OSDN Git Service

* config/mips/mips.c (MIPS_MAX_FIRST_STACK_STEP): New macro.
authorrsandifo <rsandifo@138bc75d-0d04-0410-961f-82ee72b054a4>
Tue, 7 Oct 2003 06:38:15 +0000 (06:38 +0000)
committerrsandifo <rsandifo@138bc75d-0d04-0410-961f-82ee72b054a4>
Tue, 7 Oct 2003 06:38:15 +0000 (06:38 +0000)
(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

gcc/ChangeLog
gcc/config/mips/mips.c
gcc/config/mips/mips.h

index 0728718..563d238 100644 (file)
@@ -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.
 
index 1eb9426..5a8b059 100644 (file)
@@ -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;
 }
 \f
-/* 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.  */
+\f
+/* 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
 }
 \f
 /* 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);
 }
 \f
+/* 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;
 }
 \f
+/* 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.  */
index bba993a..b3de61e 100644 (file)
@@ -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.  */