OSDN Git Service

# Fix misspellings in comments.
[pf3gnuchains/gcc-fork.git] / gcc / config / mips / mips.c
index 546174a..29ea966 100644 (file)
@@ -1554,20 +1554,21 @@ gen_int_relational (test_code, result, cmp0, cmp1, p_invert)
     int reverse_regs;          /* reverse registers in test */
     int invert_const;          /* != 0 if invert value if cmp1 is constant */
     int invert_reg;            /* != 0 if invert value if cmp1 is register */
+    int unsignedp;             /* != 0 for unsigned comparisons.  */
   };
 
   static struct cmp_info info[ (int)ITEST_MAX ] = {
 
-    { XOR,      0,  65535,  0,  0,  0,  0 },   /* EQ  */
-    { XOR,      0,  65535,  0,  0,  1,  1 },   /* NE  */
-    { LT,   -32769,  32766,  1,         1,  1,  0 },   /* GT  */
-    { LT,   -32768,  32767,  0,         0,  1,  1 },   /* GE  */
-    { LT,   -32768,  32767,  0,         0,  0,  0 },   /* LT  */
-    { LT,   -32769,  32766,  1,         1,  0,  1 },   /* LE  */
-    { LTU,  -32769,  32766,  1,         1,  1,  0 },   /* GTU */
-    { LTU,  -32768,  32767,  0,         0,  1,  1 },   /* GEU */
-    { LTU,  -32768,  32767,  0,         0,  0,  0 },   /* LTU */
-    { LTU,  -32769,  32766,  1,         1,  0,  1 },   /* LEU */
+    { XOR,      0,  65535,  0,  0,  0,  0, 0 },        /* EQ  */
+    { XOR,      0,  65535,  0,  0,  1,  1, 0 },        /* NE  */
+    { LT,   -32769,  32766,  1,         1,  1,  0, 0 },        /* GT  */
+    { LT,   -32768,  32767,  0,         0,  1,  1, 0 },        /* GE  */
+    { LT,   -32768,  32767,  0,         0,  0,  0, 0 },        /* LT  */
+    { LT,   -32769,  32766,  1,         1,  0,  1, 0 },        /* LE  */
+    { LTU,  -32769,  32766,  1,         1,  1,  0, 1 },        /* GTU */
+    { LTU,  -32768,  32767,  0,         0,  1,  1, 1 },        /* GEU */
+    { LTU,  -32768,  32767,  0,         0,  0,  0, 1 },        /* LTU */
+    { LTU,  -32769,  32766,  1,         1,  0,  1, 1 },        /* LEU */
   };
 
   enum internal_test test;
@@ -1631,7 +1632,22 @@ gen_int_relational (test_code, result, cmp0, cmp1, p_invert)
   if (GET_CODE (cmp1) == CONST_INT)
     {
       if (p_info->const_add != 0)
-       cmp1 = GEN_INT (INTVAL (cmp1) + p_info->const_add);
+       {
+         HOST_WIDE_INT new = INTVAL (cmp1) + p_info->const_add;
+         /* If modification of cmp1 caused overflow,
+            we would get the wrong answer if we follow the usual path;
+            thus, x > 0xffffffffu would turn into x > 0u.  */
+         if ((p_info->unsignedp
+              ? (unsigned HOST_WIDE_INT) new > INTVAL (cmp1)
+              : new > INTVAL (cmp1))
+             != (p_info->const_add > 0))
+           /* 1 is the right value in the LE and LEU case.
+              In the GT and GTU case, *p_invert is already set,
+              so this is effectively 0.  */
+           return force_reg (SImode, const1_rtx);
+         else
+           cmp1 = GEN_INT (new);
+       }
     }
   else if (p_info->reverse_regs)
     {
@@ -1830,7 +1846,7 @@ block_move_load_store (dest_reg, src_reg, p_bytes, p_offset, align, orig_src)
     }
 
 #if 0
-  /* Don't generate unligned moves here, rather defer those to the
+  /* Don't generate unaligned moves here, rather defer those to the
      general movestrsi_internal pattern.  */
   else if (bytes >= UNITS_PER_WORD)
     {
@@ -2397,6 +2413,7 @@ init_cumulative_args (cum, fntype, libname)
      tree fntype;              /* tree ptr for function decl */
      rtx libname;              /* SYMBOL_REF of library name or 0 */
 {
+  static CUMULATIVE_ARGS zero_cum;
   tree param, next_param;
 
   if (TARGET_DEBUG_E_MODE)
@@ -2414,9 +2431,7 @@ init_cumulative_args (cum, fntype, libname)
        }
     }
 
-  cum->gp_reg_found = 0;
-  cum->arg_number = 0;
-  cum->arg_words = 0;
+  *cum = zero_cum;
 
   /* Determine if this function has variable arguments.  This is
      indicated by the last argument being 'void_type_mode' if there
@@ -2445,7 +2460,7 @@ function_arg_advance (cum, mode, type, named)
 {
   if (TARGET_DEBUG_E_MODE)
     fprintf (stderr,
-            "function_adv( {gp reg found = %d, arg # = %2d, words = %2d}, %4s, 0x%.8x, %d )\n",
+            "function_adv( {gp reg found = %d, arg # = %2d, words = %2d}, %4s, 0x%.8x, %d )\n\n",
             cum->gp_reg_found, cum->arg_number, cum->arg_words, GET_MODE_NAME (mode),
             type, named);
 
@@ -2496,8 +2511,12 @@ function_arg (cum, mode, type, named)
      tree type;                        /* type of the argument or 0 if lib support */
      int named;                        /* != 0 for normal args, == 0 for ... args */
 {
+  rtx ret;
   int regbase = -1;
   int bias = 0;
+  int struct_p = ((type != (tree)0)
+                 && (TREE_CODE (type) == RECORD_TYPE
+                     || TREE_CODE (type) == UNION_TYPE));
 
   if (TARGET_DEBUG_E_MODE)
     fprintf (stderr,
@@ -2529,8 +2548,14 @@ function_arg (cum, mode, type, named)
                        : FP_ARG_FIRST;
       break;
 
-    case VOIDmode:
     case BLKmode:
+      if (type != (tree)0 && TYPE_ALIGN (type) > BITS_PER_WORD)
+       cum->arg_words += (cum->arg_words & 1);
+
+      regbase = GP_ARG_FIRST;
+      break;
+
+    case VOIDmode:
     case QImode:
     case HImode:
     case SImode:
@@ -2545,18 +2570,42 @@ function_arg (cum, mode, type, named)
   if (cum->arg_words >= MAX_ARGS_IN_REGISTERS)
     {
       if (TARGET_DEBUG_E_MODE)
-       fprintf (stderr, "<stack>\n");
+       fprintf (stderr, "<stack>%s\n", struct_p ? ", [struct]" : "");
 
-      return 0;
+      ret = (rtx)0;
     }
+  else
+    {
+      if (regbase == -1)
+       abort ();
 
-  if (regbase == -1)
-    abort ();
+      ret = gen_rtx (REG, mode, regbase + cum->arg_words + bias);
 
-  if (TARGET_DEBUG_E_MODE)
-    fprintf (stderr, "%s\n", reg_names[regbase + cum->arg_words + bias]);
+      if (TARGET_DEBUG_E_MODE)
+       fprintf (stderr, "%s%s\n", reg_names[regbase + cum->arg_words + bias],
+                struct_p ? ", [struct]" : "");
+
+      /* The following is a hack in order to pass 1 byte structures
+        the same way that the MIPS compiler does (namely by passing
+        the structure in the high byte or half word of the register).
+        This also makes varargs work.  If we have such a structure,
+        we save the adjustment RTL, and the call define expands will
+        emit them.  For the VOIDmode argument (argument after the
+        last real argument, pass back a parallel vector holding each
+        of the adjustments.  */
+
+      if (struct_p && (mode == QImode || mode == HImode))
+       {
+         rtx amount = GEN_INT (BITS_PER_WORD - GET_MODE_BITSIZE (mode));
+         rtx reg = gen_rtx (REG, SImode, regbase + cum->arg_words + bias);
+         cum->adjust[ cum->num_adjusts++ ] = gen_ashlsi3 (reg, reg, amount);
+       }
+    }
 
-  return gen_rtx (REG, mode, regbase + cum->arg_words + bias);
+  if (mode == VOIDmode && cum->num_adjusts > 0)
+    ret = gen_rtx (PARALLEL, VOIDmode, gen_rtvec_v (cum->num_adjusts, cum->adjust));
+
+  return ret;
 }
 
 
@@ -2981,8 +3030,11 @@ mips_debugger_offset (addr, offset)
 
       offset = offset - frame_size;
     }
+  /* sdbout_parms does not want this to crash for unrecognized cases.  */
+#if 0
   else if (reg != arg_pointer_rtx)
     abort_with_insn (addr, "mips_debugger_offset called with non stack/frame/arg pointer.");
+#endif
 
   return offset;
 }
@@ -3707,6 +3759,72 @@ mips_output_float (stream, value)
 }
 
 \f
+/* Return TRUE if any register used in the epilogue is used.  This to insure
+   any insn put into the epilogue delay slots is safe.  */
+
+int
+epilogue_reg_mentioned_p (insn)
+     rtx insn;
+{
+  register char *fmt;
+  register int i;
+  register enum rtx_code code;
+  register int regno;
+
+  if (insn == (rtx)0)
+    return 0;
+
+  if (GET_CODE (insn) == LABEL_REF)
+    return 0;
+
+  code = GET_CODE (insn);
+  switch (code)
+    {
+    case REG:
+      regno = REGNO (insn);
+      if (regno == STACK_POINTER_REGNUM)
+       return 1;
+
+      if (regno == FRAME_POINTER_REGNUM && frame_pointer_needed)
+       return 1;
+
+      if (!call_used_regs[regno])
+       return 1;
+
+      if (regno != MIPS_TEMP1_REGNUM && regno != MIPS_TEMP2_REGNUM)
+       return 0;
+
+      if (!current_frame_info.initialized)
+       compute_frame_size (get_frame_size ());
+
+      return (current_frame_info.total_size >= 32768);
+
+    case SCRATCH:
+    case CC0:
+    case PC:
+    case CONST_INT:
+    case CONST_DOUBLE:
+      return 0;
+    }
+
+  fmt = GET_RTX_FORMAT (code);
+  for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
+    {
+      if (fmt[i] == 'E')
+       {
+         register int j;
+         for (j = XVECLEN (insn, i) - 1; j >= 0; j--)
+           if (epilogue_reg_mentioned_p (XVECEXP (insn, i, j)))
+             return 1;
+       }
+      else if (fmt[i] == 'e' && epilogue_reg_mentioned_p (XEXP (insn, i)))
+       return 1;
+    }
+
+  return 0;
+}
+
+\f
 /* Return the bytes needed to compute the frame pointer from the current
    stack pointer.
 
@@ -3760,22 +3878,22 @@ mips_output_float (stream, value)
 
 */
 
-unsigned long
+long
 compute_frame_size (size)
      int size;                 /* # of var. bytes allocated */
 {
   int regno;
-  unsigned long total_size;    /* # bytes that the entire frame takes up */
-  unsigned long var_size;      /* # bytes that variables take up */
-  unsigned long args_size;     /* # bytes that outgoing arguments take up */
-  unsigned long extra_size;    /* # extra bytes */
-  unsigned int  gp_reg_rounded;        /* # bytes needed to store gp after rounding */
-  unsigned int  gp_reg_size;   /* # bytes needed to store gp regs */
-  unsigned int  fp_reg_size;   /* # bytes needed to store fp regs */
-  unsigned long mask;          /* mask of saved gp registers */
-  unsigned long fmask;         /* mask of saved fp registers */
-  int fp_inc;                  /* 1 or 2 depending on the size of fp regs */
-  int fp_bits;                 /* bitmask to use for each fp register */
+  long total_size;             /* # bytes that the entire frame takes up */
+  long var_size;               /* # bytes that variables take up */
+  long args_size;              /* # bytes that outgoing arguments take up */
+  long extra_size;             /* # extra bytes */
+  long gp_reg_rounded;         /* # bytes needed to store gp after rounding */
+  long gp_reg_size;            /* # bytes needed to store gp regs */
+  long fp_reg_size;            /* # bytes needed to store fp regs */
+  long mask;                   /* mask of saved gp registers */
+  long fmask;                  /* mask of saved fp registers */
+  int  fp_inc;                 /* 1 or 2 depending on the size of fp regs */
+  long fp_bits;                        /* bitmask to use for each fp register */
 
   gp_reg_size   = 0;
   fp_reg_size   = 0;
@@ -3791,7 +3909,7 @@ compute_frame_size (size)
      function, so allocate some stack space to make it happy.  */
 
   if (args_size == 0 && current_function_calls_alloca)
-       args_size = 4*UNITS_PER_WORD;
+    args_size = 4*UNITS_PER_WORD;
 
   total_size = var_size + args_size + extra_size;
 
@@ -3801,7 +3919,7 @@ compute_frame_size (size)
       if (MUST_SAVE_REGISTER (regno))
        {
          gp_reg_size += UNITS_PER_WORD;
-         mask |= 1 << (regno - GP_REG_FIRST);
+         mask |= 1L << (regno - GP_REG_FIRST);
        }
     }
 
@@ -3851,6 +3969,12 @@ compute_frame_size (size)
       current_frame_info.gp_sp_offset = offset;
       current_frame_info.gp_save_offset = offset - total_size;
     }
+  else
+    {
+      current_frame_info.gp_sp_offset = 0;
+      current_frame_info.gp_save_offset = 0;
+    }
+
 
   if (fmask)
     {
@@ -3858,162 +3982,224 @@ compute_frame_size (size)
       current_frame_info.fp_sp_offset = offset;
       current_frame_info.fp_save_offset = offset - total_size + UNITS_PER_WORD;
     }
+  else
+    {
+      current_frame_info.fp_sp_offset = 0;
+      current_frame_info.fp_save_offset = 0;
+    }
 
   /* Ok, we're done.  */
   return total_size;
 }
 
 \f
-/* Save/restore registers printing out the instructions to a file.  */
+/* Common code to emit the insns (or to write the instructions to a file)
+   to save/restore registers.
 
-void
-save_restore (file, gp_op, gp_2word_op, fp_op)
-     FILE *file;               /* stream to write to */
-     char *gp_op;              /* operation to do on gp registers */
-     char *gp_2word_op;                /* 2 word op to do on gp registers */
-     char *fp_op;              /* operation to do on fp 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)
+
+static void
+save_restore_insns (store_p, large_reg, large_offset, file)
+     int store_p;              /* true if this is prologue */
+     rtx large_reg;            /* register holding large offset constant or NULL */
+     long large_offset;                /* large constant offset value */
+     FILE *file;               /* file to write instructions to instead of making RTL */
 {
+  long mask            = current_frame_info.mask;
+  long fmask           = current_frame_info.fmask;
   int regno;
-  unsigned long mask     = current_frame_info.mask;
-  unsigned long fmask    = current_frame_info.fmask;
-  unsigned long gp_offset;
-  unsigned long fp_offset;
-  unsigned long max_offset;
-  char *base_reg;
+  rtx base_reg_rtx;
+  long base_offset;
+  long gp_offset;
+  long fp_offset;
+  long end_offset;
+
+  if (frame_pointer_needed && !BITSET_P (mask, FRAME_POINTER_REGNUM - GP_REG_FIRST))
+    abort ();
 
   if (mask == 0 && fmask == 0)
     return;
 
-  base_reg   = reg_names[STACK_POINTER_REGNUM];
-  gp_offset  = current_frame_info.gp_sp_offset;
-  fp_offset  = current_frame_info.fp_sp_offset;
-  max_offset = (gp_offset > fp_offset) ? gp_offset : fp_offset;
-
-  /* Deal with calling functions with a large structure.  */
-  if (max_offset >= 32768)
-    {
-      char *temp = reg_names[MIPS_TEMP2_REGNUM];
-      fprintf (file, "\tli\t%s,%ld\n", temp, max_offset);
-      fprintf (file, "\taddu\t%s,%s,%s\n", temp, temp, base_reg);
-      base_reg = temp;
-      gp_offset = max_offset - gp_offset;
-      fp_offset = max_offset - fp_offset;
-    }
-
   /* 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.  */
 
-  if (mask || frame_pointer_needed)
+  /* Save GP registers if needed.  */
+  if (mask)
     {
-      for  (regno = GP_REG_LAST; regno >= GP_REG_FIRST; regno--)
-       {
-         if ((mask & (1L << (regno - GP_REG_FIRST))) != 0
-             || (regno == FRAME_POINTER_REGNUM && frame_pointer_needed))
-           {
-             fprintf (file, "\t%s\t%s,%d(%s)\n",
-                      gp_op, reg_names[regno],
-                      gp_offset, base_reg);
+      /* 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 -= UNITS_PER_WORD;
-           }
+      gp_offset  = current_frame_info.gp_sp_offset;
+      end_offset = gp_offset - (current_frame_info.gp_reg_size - UNITS_PER_WORD);
+
+      if (gp_offset < 0 || end_offset < 0)
+       fatal ("gp_offset (%ld) or end_offset (%ld) is less than zero.",
+              gp_offset, end_offset);
+
+      else if (gp_offset < 32768)
+       {
+         base_reg_rtx = stack_pointer_rtx;
+         base_offset  = 0;
        }
-    }
 
-  if (fmask)
-    {
-      int fp_inc = (TARGET_FLOAT64) ? 1 : 2;
+      else if (large_reg != (rtx)0
+              && (((unsigned long)(large_offset - gp_offset))  < 32768)
+              && (((unsigned long)(large_offset - end_offset)) < 32768))
+       {
+         base_reg_rtx = gen_rtx (REG, Pmode, MIPS_TEMP2_REGNUM);
+         base_offset  = large_offset;
+         if (file == (FILE *)0)
+           emit_insn (gen_addsi3 (base_reg_rtx, large_reg, stack_pointer_rtx));
+         else
+           fprintf (file, "\taddu\t%s,%s,%s\n",
+                    reg_names[MIPS_TEMP2_REGNUM],
+                    reg_names[REGNO (large_reg)],
+                    reg_names[STACK_POINTER_REGNUM]);
+       }
 
-      for  (regno = FP_REG_LAST-1; regno >= FP_REG_FIRST; regno -= fp_inc)
+      else
        {
-         if ((fmask & (1L << (regno - FP_REG_FIRST))) != 0)
+         base_reg_rtx = gen_rtx (REG, Pmode, MIPS_TEMP2_REGNUM);
+         base_offset  = gp_offset;
+         if (file == (FILE *)0)
            {
-             fprintf (file, "\t%s\t%s,%d(%s)\n",
-                      fp_op, reg_names[regno], fp_offset, base_reg);
-
-             fp_offset -= 2*UNITS_PER_WORD;
+             emit_move_insn (base_reg_rtx, GEN_INT (gp_offset));
+             emit_insn (gen_addsi3 (base_reg_rtx, base_reg_rtx, stack_pointer_rtx));
            }
+         else
+           fprintf (file, "\tli\t%s,0x%.08lx\t# %ld\n\taddu\t%s,%s,%s\n",
+                    reg_names[MIPS_TEMP2_REGNUM],
+                    (long)base_offset,
+                    (long)base_offset,
+                    reg_names[MIPS_TEMP2_REGNUM],
+                    reg_names[MIPS_TEMP2_REGNUM],
+                    reg_names[STACK_POINTER_REGNUM]);
        }
-    }
-}
 
-\f
-/* Common code to emit the insns to save/restore registers.  */
-
-static void
-save_restore_insns (store_p)
-     int store_p;              /* true if this is prologue */
-{
-  int regno;
-  rtx base_reg_rtx       = stack_pointer_rtx;
-  unsigned long mask     = current_frame_info.mask;
-  unsigned long fmask    = current_frame_info.fmask;
-  unsigned long gp_offset;
-  unsigned long fp_offset;
-  unsigned long max_offset;
-
-  if (mask == 0 && fmask == 0)
-    return;
-
-  gp_offset  = current_frame_info.gp_sp_offset;
-  fp_offset  = current_frame_info.fp_sp_offset;
-  max_offset = (gp_offset > fp_offset) ? gp_offset : fp_offset;
-
-  /* Deal with calling functions with a large structure.  */
-  if (max_offset >= 32768)
-    {
-      base_reg_rtx = gen_rtx (REG, Pmode, MIPS_TEMP2_REGNUM);
-      emit_move_insn (base_reg_rtx, GEN_INT (max_offset));
-      emit_insn (gen_addsi3 (base_reg_rtx, base_reg_rtx, stack_pointer_rtx));
-      gp_offset = max_offset - gp_offset;
-      fp_offset = max_offset - fp_offset;
-    }
-
-  /* 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.  */
-
-  if (mask || frame_pointer_needed)
-    {
       for  (regno = GP_REG_LAST; regno >= GP_REG_FIRST; regno--)
        {
-         if ((mask & (1L << (regno - GP_REG_FIRST))) != 0
-             || (regno == FRAME_POINTER_REGNUM && frame_pointer_needed))
+         if (BITSET_P (mask, regno - GP_REG_FIRST))
            {
-             rtx reg_rtx = gen_rtx (REG, Pmode, regno);
-             rtx mem_rtx = gen_rtx (MEM, Pmode,
-                                    gen_rtx (PLUS, Pmode, base_reg_rtx,
-                                             GEN_INT (gp_offset)));
+             if (file == (FILE *)0)
+               {
+                 rtx reg_rtx = gen_rtx (REG, Pmode, regno);
+                 rtx mem_rtx = gen_rtx (MEM, Pmode,
+                                        gen_rtx (PLUS, Pmode, base_reg_rtx,
+                                                 GEN_INT (gp_offset - base_offset)));
 
-             if (store_p)
-               emit_move_insn (mem_rtx, reg_rtx);
+                 if (store_p)
+                   emit_move_insn (mem_rtx, reg_rtx);
+                 else
+                   emit_move_insn (reg_rtx, mem_rtx);
+               }
              else
-               emit_move_insn (reg_rtx, mem_rtx);
+               fprintf (file, "\t%s\t%s,%ld(%s)\n",
+                        (store_p) ? "sw" : "lw",
+                        reg_names[regno],
+                        gp_offset - base_offset,
+                        reg_names[REGNO(base_reg_rtx)]);
 
              gp_offset -= UNITS_PER_WORD;
            }
        }
     }
+  else
+    {
+      base_reg_rtx = (rtx)0;           /* Make sure these are initialzed */
+      base_offset  = 0;
+    }
 
+  /* Save floating point registers if needed.  */
   if (fmask)
     {
       int fp_inc = (TARGET_FLOAT64) ? 1 : 2;
 
+      /* Pick which pointer to use as a base register.  */
+      fp_offset  = current_frame_info.fp_sp_offset;
+      end_offset = fp_offset - (current_frame_info.fp_reg_size - 2*UNITS_PER_WORD);
+
+      if (fp_offset < 0 || end_offset < 0)
+       fatal ("fp_offset (%ld) or end_offset (%ld) is less than zero.",
+              fp_offset, end_offset);
+
+      else if (fp_offset < 32768)
+       {
+         base_reg_rtx = stack_pointer_rtx;
+         base_offset  = 0;
+       }
+
+      else if (base_reg_rtx != (rtx)0
+              && (((unsigned long)(base_offset - fp_offset))  < 32768)
+              && (((unsigned long)(base_offset - end_offset)) < 32768))
+       {
+         ;                     /* already set up for gp registers above */
+       }
+
+      else if (large_reg != (rtx)0
+              && (((unsigned long)(large_offset - fp_offset))  < 32768)
+              && (((unsigned long)(large_offset - end_offset)) < 32768))
+       {
+         base_reg_rtx = gen_rtx (REG, Pmode, MIPS_TEMP2_REGNUM);
+         base_offset  = large_offset;
+         if (file == (FILE *)0)
+           emit_insn (gen_addsi3 (base_reg_rtx, large_reg, stack_pointer_rtx));
+         else
+           fprintf (file, "\taddu\t%s,%s,%s\n",
+                    reg_names[MIPS_TEMP2_REGNUM],
+                    reg_names[REGNO (large_reg)],
+                    reg_names[STACK_POINTER_REGNUM]);
+       }
+
+      else
+       {
+         base_reg_rtx = gen_rtx (REG, Pmode, MIPS_TEMP2_REGNUM);
+         base_offset  = fp_offset;
+         if (file == (FILE *)0)
+           {
+             emit_move_insn (base_reg_rtx, GEN_INT (fp_offset));
+             emit_insn (gen_addsi3 (base_reg_rtx, base_reg_rtx, stack_pointer_rtx));
+           }
+         else
+           fprintf (file, "\tli\t%s,0x%.08lx\t# %ld\n\taddu\t%s,%s,%s\n",
+                    reg_names[MIPS_TEMP2_REGNUM],
+                    (long)base_offset,
+                    (long)base_offset,
+                    reg_names[MIPS_TEMP2_REGNUM],
+                    reg_names[MIPS_TEMP2_REGNUM],
+                    reg_names[STACK_POINTER_REGNUM]);
+       }
+
       for  (regno = FP_REG_LAST-1; regno >= FP_REG_FIRST; regno -= fp_inc)
        {
-         if ((fmask & (1L << (regno - FP_REG_FIRST))) != 0)
+         if (BITSET_P (fmask, regno - FP_REG_FIRST))
            {
-             rtx reg_rtx = gen_rtx (REG, DFmode, regno);
-             rtx mem_rtx = gen_rtx (MEM, DFmode,
-                                    gen_rtx (PLUS, Pmode, base_reg_rtx,
-                                             GEN_INT (fp_offset)));
+             if (file == (FILE *)0)
+               {
+                 rtx reg_rtx = gen_rtx (REG, DFmode, regno);
+                 rtx mem_rtx = gen_rtx (MEM, DFmode,
+                                        gen_rtx (PLUS, Pmode, base_reg_rtx,
+                                                 GEN_INT (fp_offset - base_offset)));
 
-             if (store_p)
-               emit_move_insn (mem_rtx, reg_rtx);
+                 if (store_p)
+                   emit_move_insn (mem_rtx, reg_rtx);
+                 else
+                   emit_move_insn (reg_rtx, mem_rtx);
+               }
              else
-               emit_move_insn (reg_rtx, mem_rtx);
+               fprintf (file, "\t%s\t%s,%ld(%s)\n",
+                        (store_p) ? "s.d" : "l.d",
+                        reg_names[regno],
+                        fp_offset - base_offset,
+                        reg_names[REGNO(base_reg_rtx)]);
+
 
              fp_offset -= 2*UNITS_PER_WORD;
            }
@@ -4029,15 +4215,18 @@ function_prologue (file, size)
      FILE *file;
      int size;
 {
-  int tsize = current_frame_info.total_size;
+  long tsize = current_frame_info.total_size;
 
   ASM_OUTPUT_SOURCE_FILENAME (file, DECL_SOURCE_FILE (current_function_decl));
-  ASM_OUTPUT_SOURCE_LINE (file, DECL_SOURCE_LINE (current_function_decl));
+
+  if (debug_info_level != DINFO_LEVEL_TERSE)
+    ASM_OUTPUT_SOURCE_LINE (file, DECL_SOURCE_LINE (current_function_decl));
 
   inside_function = 1;
   fputs ("\t.ent\t", file);
   assemble_name (file, current_function_name);
   fputs ("\n", file);
+
   assemble_name (file, current_function_name);
   fputs (":\n", file);
 
@@ -4074,20 +4263,57 @@ void
 mips_expand_prologue ()
 {
   int regno;
-  int tsize;
-  tree fndecl = current_function_decl; /* current... is tooo long */
-  tree fntype = TREE_TYPE (fndecl);
-  tree fnargs = (TREE_CODE (fntype) != METHOD_TYPE)
+  long tsize;
+  rtx tmp_rtx   = (rtx)0;
+  char *arg_name = (char *)0;
+  tree fndecl   = current_function_decl;
+  tree fntype   = TREE_TYPE (fndecl);
+  tree fnargs   = (TREE_CODE (fntype) != METHOD_TYPE)
                        ? DECL_ARGUMENTS (fndecl)
                        : 0;
+  rtx next_arg_reg;
+  int i;
   tree next_arg;
   tree cur_arg;
-  char *arg_name = (char *)0;
   CUMULATIVE_ARGS args_so_far;
 
   /* Determine the last argument, and get its name.  */
+
+  INIT_CUMULATIVE_ARGS (args_so_far, fntype, (rtx)0);
+  regno = GP_ARG_FIRST;
+
   for (cur_arg = fnargs; cur_arg != (tree)0; cur_arg = next_arg)
     {
+      tree type = DECL_ARG_TYPE (cur_arg);
+      enum machine_mode passed_mode = TYPE_MODE (type);
+      rtx entry_parm = FUNCTION_ARG (args_so_far,
+                                    passed_mode,
+                                    DECL_ARG_TYPE (cur_arg),
+                                    1);
+
+      if (entry_parm)
+       {
+         int words;
+
+         /* passed in a register, so will get homed automatically */
+         if (GET_MODE (entry_parm) == BLKmode)
+           words = (int_size_in_bytes (type) + 3) / 4;
+         else
+           words = (GET_MODE_SIZE (GET_MODE (entry_parm)) + 3) / 4;
+
+         regno = REGNO (entry_parm) + words - 1;
+       }
+      else
+       {
+         regno = GP_ARG_LAST+1;
+         break;
+       }
+
+      FUNCTION_ARG_ADVANCE (args_so_far,
+                           passed_mode,
+                           DECL_ARG_TYPE (cur_arg),
+                           1);
+
       next_arg = TREE_CHAIN (cur_arg);
       if (next_arg == (tree)0)
        {
@@ -4098,52 +4324,39 @@ mips_expand_prologue ()
        }
     }
 
-  /* If this function is a varargs function, store any registers that
-     would normally hold arguments ($4 - $7) on the stack.  */
-  if ((TYPE_ARG_TYPES (fntype) != 0
-       && (TREE_VALUE (tree_last (TYPE_ARG_TYPES (fntype))) != void_type_node))
-      || (arg_name
-         && (strcmp (arg_name, "__builtin_va_alist") == 0
-             || strcmp (arg_name, "va_alist") == 0)))
-    {
-      tree parm;
+  /* In order to pass small structures by value in registers
+     compatibly with the MIPS compiler, we need to shift the value
+     into the high part of the register.  Function_arg has encoded a
+     PARALLEL rtx, holding a vector of adjustments to be made as the
+     next_arg_reg variable, so we split up the insns, and emit them
+     separately.  */
 
-      regno = GP_ARG_FIRST;
-      INIT_CUMULATIVE_ARGS (args_so_far, fntype, (rtx)0);
+  next_arg_reg = FUNCTION_ARG (args_so_far, VOIDmode, void_type_node, 1);
+  if (next_arg_reg != (rtx)0 && GET_CODE (next_arg_reg) == PARALLEL)
+    {
+      rtvec adjust = XVEC (next_arg_reg, 0);
+      int num = GET_NUM_ELEM (adjust);
 
-      for (parm = fnargs; (parm && (regno <= GP_ARG_LAST)); parm = TREE_CHAIN (parm))
+      for (i = 0; i < num; i++)
        {
-         rtx entry_parm;
-         enum machine_mode passed_mode;
-         tree type;
-
-         type = DECL_ARG_TYPE (parm);
-         passed_mode = TYPE_MODE (type);
-         entry_parm = FUNCTION_ARG (args_so_far, passed_mode,
-                                    DECL_ARG_TYPE (parm), 1);
-
-         if (entry_parm)
-           {
-             int words;
-
-             /* passed in a register, so will get homed automatically */
-             if (GET_MODE (entry_parm) == BLKmode)
-               words = (int_size_in_bytes (type) + 3) / 4;
-             else
-               words = (GET_MODE_SIZE (GET_MODE (entry_parm)) + 3) / 4;
-
-             regno = REGNO (entry_parm) + words - 1;
-           }
-         else
-           {
-             regno = GP_ARG_LAST+1;
-             break;
-           }
+         rtx pattern = RTVEC_ELT (adjust, i);
+         if (GET_CODE (pattern) != SET
+             || GET_CODE (SET_SRC (pattern)) != ASHIFT)
+           abort_with_insn (pattern, "Insn is not a shift");
 
-         FUNCTION_ARG_ADVANCE (args_so_far, passed_mode,
-                               DECL_ARG_TYPE (parm), 1);
+         PUT_CODE (SET_SRC (pattern), ASHIFTRT);
+         emit_insn (pattern);
        }
+    }
 
+  /* If this function is a varargs function, store any registers that
+     would normally hold arguments ($4 - $7) on the stack.  */
+  if ((TYPE_ARG_TYPES (fntype) != 0
+       && (TREE_VALUE (tree_last (TYPE_ARG_TYPES (fntype))) != void_type_node))
+      || (arg_name != (char *)0
+         && ((arg_name[0] == '_' && strcmp (arg_name, "__builtin_va_alist") == 0)
+             || (arg_name[0] == 'v' && strcmp (arg_name, "va_alist") == 0))))
+    {
       for (; regno <= GP_ARG_LAST; regno++)
        {
          rtx ptr = stack_pointer_rtx;
@@ -4162,18 +4375,24 @@ mips_expand_prologue ()
 
       if (tsize > 32767)
        {
-         rtx tmp_rtx = gen_rtx (REG, SImode, MIPS_TEMP1_REGNUM);
+         tmp_rtx = gen_rtx (REG, SImode, MIPS_TEMP1_REGNUM);
          emit_move_insn (tmp_rtx, tsize_rtx);
          tsize_rtx = tmp_rtx;
        }
 
       emit_insn (gen_subsi3 (stack_pointer_rtx, stack_pointer_rtx, tsize_rtx));
 
-      save_restore_insns (TRUE);
+      save_restore_insns (TRUE, tmp_rtx, tsize, (FILE *)0);
 
       if (frame_pointer_needed)
        emit_insn (gen_movsi (frame_pointer_rtx, stack_pointer_rtx));
     }
+
+  /* If we are profiling, make sure no instructions are scheduled before
+     the call to mcount.  */
+
+  if (profile_flag || profile_block_flag)
+    emit_insn (gen_blockage ());
 }
 
 \f
@@ -4184,7 +4403,7 @@ function_epilogue (file, size)
      FILE *file;
      int size;
 {
-  int tsize;
+  long tsize;
   char *sp_str = reg_names[STACK_POINTER_REGNUM];
   char *t1_str = reg_names[MIPS_TEMP1_REGNUM];
   rtx epilogue_delay = current_function_epilogue_delay_list;
@@ -4192,6 +4411,9 @@ function_epilogue (file, size)
   int noepilogue = FALSE;
   int load_nop = FALSE;
   int load_only_r31;
+  rtx tmp_rtx = (rtx)0;
+  rtx restore_rtx;
+  int i;
 
   /* The epilogue does not depend on any registers, but the stack
      registers, so we assume that if we have 1 pending nop, it can be
@@ -4272,13 +4494,16 @@ function_epilogue (file, size)
        fprintf (file, "\t.set\tnoreorder\n");
 
       if (tsize > 32767)
-       fprintf (file, "\tli\t%s,%d\n", t1_str, tsize);
+       {
+         fprintf (file, "\tli\t%s,0x%.08lx\t# %ld\n", t1_str, (long)tsize, (long)tsize);
+         tmp_rtx = gen_rtx (REG, Pmode, MIPS_TEMP1_REGNUM);
+       }
 
       if (frame_pointer_needed)
        fprintf (file, "\tmove\t%s,%s\t\t\t# sp not trusted here\n",
                 sp_str, reg_names[FRAME_POINTER_REGNUM]);
 
-      save_restore (file, "lw", "ld", "l.d");
+      save_restore_insns (FALSE, tmp_rtx, tsize, file);
 
       load_only_r31 = (current_frame_info.mask == (1 << 31)
                       && current_frame_info.fmask == 0);
@@ -4425,12 +4650,13 @@ function_epilogue (file, size)
 void
 mips_expand_epilogue ()
 {
-  int tsize = current_frame_info.total_size;
+  long tsize = current_frame_info.total_size;
   rtx tsize_rtx = GEN_INT (tsize);
+  rtx tmp_rtx = (rtx)0;
 
   if (tsize > 32767)
     {
-      rtx tmp_rtx = gen_rtx (REG, SImode, MIPS_TEMP1_REGNUM);
+      tmp_rtx = gen_rtx (REG, SImode, MIPS_TEMP1_REGNUM);
       emit_move_insn (tmp_rtx, tsize_rtx);
       tsize_rtx = tmp_rtx;
     }
@@ -4440,7 +4666,7 @@ mips_expand_epilogue ()
       if (frame_pointer_needed)
        emit_insn (gen_movsi (stack_pointer_rtx, frame_pointer_rtx));
 
-      save_restore_insns (FALSE);
+      save_restore_insns (FALSE, tmp_rtx, tsize, (FILE *)0);
 
       emit_insn (gen_addsi3 (stack_pointer_rtx, stack_pointer_rtx, tsize_rtx));
     }