OSDN Git Service

* final.c (HAVE_READONLY_DATA_SECTION): New.
[pf3gnuchains/gcc-fork.git] / gcc / config / mips / mips.c
index d88b61d..f551d62 100644 (file)
@@ -1,6 +1,6 @@
 /* Subroutines for insn-output.c for MIPS
    Copyright (C) 1989, 1990, 1991, 1993, 1994, 1995, 1996, 1997, 1998,
-   1999, 2000, 2001 Free Software Foundation, Inc.
+   1999, 2000, 2001, 2002 Free Software Foundation, Inc.
    Contributed by A. Lichnewsky, lich@inria.inria.fr.
    Changes by Michael Meissner, meissner@osf.org.
    64 bit r4000 support by Ian Lance Taylor, ian@cygnus.com, and
@@ -54,6 +54,10 @@ Boston, MA 02111-1307, USA.  */
 #include "target.h"
 #include "target-def.h"
 
+#ifdef HALF_PIC_DEBUG
+#include "halfpic.h"
+#endif
+
 #ifdef __GNU_STAB__
 #define STAB_CODE_TYPE enum __stab_debug_code
 #else
@@ -64,7 +68,7 @@ extern tree   lookup_name PARAMS ((tree));
 
 /* Enumeration for all of the relational tests, so that we can build
    arrays indexed by the test type, and not worry about the order
-   of EQ, NE, etc. */
+   of EQ, NE, etc.  */
 
 enum internal_test {
     ITEST_EQ,
@@ -82,24 +86,31 @@ enum internal_test {
 
 
 struct constant;
+struct mips_arg_info;
 static enum internal_test map_test_to_internal_test    PARAMS ((enum rtx_code));
 static int mips16_simple_memory_operand                PARAMS ((rtx, rtx,
                                                        enum machine_mode));
+int coprocessor_operand                        PARAMS ((rtx,
+                                                       enum machine_mode));
+int coprocessor2_operand                       PARAMS ((rtx,
+                                                       enum machine_mode));
 static int m16_check_op                                PARAMS ((rtx, int, int, int));
 static void block_move_loop                    PARAMS ((rtx, rtx,
                                                         unsigned int,
                                                         int,
                                                         rtx, rtx));
 static void block_move_call                    PARAMS ((rtx, rtx, rtx));
-static rtx mips_add_large_offset_to_sp         PARAMS ((HOST_WIDE_INT,
-                                                        FILE *));
+static void mips_arg_info              PARAMS ((const CUMULATIVE_ARGS *,
+                                                enum machine_mode,
+                                                tree, int,
+                                                struct mips_arg_info *));
+static rtx mips_add_large_offset_to_sp         PARAMS ((HOST_WIDE_INT));
 static void mips_annotate_frame_insn           PARAMS ((rtx, rtx));
 static rtx mips_frame_set                      PARAMS ((enum machine_mode,
                                                         int, int));
 static void mips_emit_frame_related_store      PARAMS ((rtx, rtx,
                                                         HOST_WIDE_INT));
-static void save_restore_insns                 PARAMS ((int, rtx,
-                                                       long, FILE *));
+static void save_restore_insns                 PARAMS ((int, rtx, long));
 static void mips16_output_gp_offset            PARAMS ((FILE *, rtx));
 static void mips16_fp_args                     PARAMS ((FILE *, int, int));
 static void build_mips16_function_stub         PARAMS ((FILE *));
@@ -114,6 +125,7 @@ static void abort_with_insn                 PARAMS ((rtx, const char *))
   ATTRIBUTE_NORETURN;
 static int symbolic_expression_p                PARAMS ((rtx));
 static void mips_add_gc_roots                   PARAMS ((void));
+static bool mips_assemble_integer        PARAMS ((rtx, unsigned int, int));
 static void mips_output_function_epilogue PARAMS ((FILE *, HOST_WIDE_INT));
 static void mips_output_function_prologue PARAMS ((FILE *, HOST_WIDE_INT));
 static enum processor_type mips_parse_cpu       PARAMS ((const char *));
@@ -129,6 +141,56 @@ static hashval_t iris_section_align_entry_hash     PARAMS ((const PTR));
 static int iris6_section_align_1               PARAMS ((void **, void *));
 #endif
 static int mips_adjust_cost                    PARAMS ((rtx, rtx, rtx, int));
+static int mips_issue_rate                     PARAMS ((void));
+
+static void mips_init_machine_status           PARAMS ((struct function *));
+static void mips_free_machine_status           PARAMS ((struct function *));
+static void mips_mark_machine_status           PARAMS ((struct function *));
+static void mips_select_section PARAMS ((tree, int, unsigned HOST_WIDE_INT))
+       ATTRIBUTE_UNUSED;
+static void mips_unique_section                        PARAMS ((tree, int))
+       ATTRIBUTE_UNUSED;
+
+
+struct machine_function {
+  /* Pseudo-reg holding the address of the current function when
+     generating embedded PIC code.  Created by LEGITIMIZE_ADDRESS,
+     used by mips_finalize_pic if it was created.  */
+  rtx embedded_pic_fnaddr_rtx;
+
+  /* Pseudo-reg holding the value of $28 in a mips16 function which
+     refers to GP relative global variables.  */
+  rtx mips16_gp_pseudo_rtx;
+};
+
+/* Information about a single argument.  */
+struct mips_arg_info
+{
+  /* True if the argument is a record or union type.  */
+  bool struct_p;
+
+  /* True if the argument is passed in a floating-point register, or
+     would have been if we hadn't run out of registers.  */
+  bool fpr_p;
+
+  /* The argument's size, in bytes.  */
+  unsigned int num_bytes;
+
+  /* The number of words passed in registers, rounded up.  */
+  unsigned int reg_words;
+
+  /* The offset of the first register from GP_ARG_FIRST or FP_ARG_FIRST,
+     or MAX_ARGS_IN_REGISTERS if the argument is passed entirely
+     on the stack.  */
+  unsigned int reg_offset;
+
+  /* The number of words that must be passed on the stack, rounded up.  */
+  unsigned int stack_words;
+
+  /* The offset from the start of the stack overflow area of the argument's
+     first stack word.  Only meaningful when STACK_WORDS is non-zero.  */
+  unsigned int stack_offset;
+};
 
 /* Global variables for machine-dependent things.  */
 
@@ -145,7 +207,7 @@ int num_source_filenames = 0;
    start and end boundaries).  */
 int sdb_label_count = 0;
 
-/* Next label # for each statement for Silicon Graphics IRIS systems. */
+/* Next label # for each statement for Silicon Graphics IRIS systems.  */
 int sym_lineno = 0;
 
 /* Non-zero if inside of a function, because the stupid MIPS asm can't
@@ -224,11 +286,8 @@ enum processor_type mips_tune;
 /* which instruction set architecture to use.  */
 int mips_isa;
 
-#ifdef MIPS_ABI_DEFAULT
-/* Which ABI to use.  This is defined to a constant in mips.h if the target
-   doesn't support multiple ABIs.  */
+/* which abi to use.  */
 int mips_abi;
-#endif
 
 /* Strings to hold which cpu and instruction set architecture to use.  */
 const char *mips_cpu_string;   /* for -mcpu=<xxx> */
@@ -248,7 +307,7 @@ const char *mips_no_mips16_string;
 
 /* This is only used to determine if an type size setting option was
    explicitly specified (-mlong64, -mint64, -mlong32).  The specs
-   set this option if such an option is used. */
+   set this option if such an option is used.  */
 const char *mips_explicit_type_size_string;
 
 /* Whether we are generating mips16 hard float code.  In mips16 mode
@@ -262,6 +321,8 @@ int mips16_hard_float;
    avoid using up another bit in target_flags.  */
 const char *mips_entry_string;
 
+const char *mips_cache_flush_func = CACHE_FLUSH_FUNC;
+
 /* Whether we should entry and exit pseudo-ops in mips16 mode.  */
 int mips_entry;
 
@@ -289,21 +350,12 @@ struct mips_frame_info current_frame_info;
 /* Zero structure to initialize current_frame_info.  */
 struct mips_frame_info zero_frame_info;
 
-/* Pseudo-reg holding the address of the current function when
-   generating embedded PIC code.  Created by LEGITIMIZE_ADDRESS, used
-   by mips_finalize_pic if it was created.  */
-rtx embedded_pic_fnaddr_rtx;
-
 /* The length of all strings seen when compiling for the mips16.  This
    is used to tell how many strings are in the constant pool, so that
    we can see if we may have an overflow.  This is reset each time the
    constant pool is output.  */
 int mips_string_length;
 
-/* Pseudo-reg holding the value of $28 in a mips16 function which
-   refers to GP relative global variables.  */
-rtx mips16_gp_pseudo_rtx;
-
 /* In mips16 mode, we build a list of all the string constants we see
    in a particular function.  */
 
@@ -339,7 +391,19 @@ char mips_reg_names[][8] =
  "$f16", "$f17", "$f18", "$f19", "$f20", "$f21", "$f22", "$f23",
  "$f24", "$f25", "$f26", "$f27", "$f28", "$f29", "$f30", "$f31",
  "hi",   "lo",   "accum","$fcc0","$fcc1","$fcc2","$fcc3","$fcc4",
- "$fcc5","$fcc6","$fcc7","$rap"
+ "$fcc5","$fcc6","$fcc7","$rap", "",     "",     "",     "",
+ "$c0r0", "$c0r1", "$c0r2", "$c0r3", "$c0r4", "$c0r5", "$c0r6", "$c0r7",
+ "$c0r8", "$c0r9", "$c0r10","$c0r11","$c0r12","$c0r13","$c0r14","$c0r15",
+ "$c0r16","$c0r17","$c0r18","$c0r19","$c0r20","$c0r21","$c0r22","$c0r23",
+ "$c0r24","$c0r25","$c0r26","$c0r27","$c0r28","$c0r29","$c0r30","$c0r31",
+ "$c2r0", "$c2r1", "$c2r2", "$c2r3", "$c2r4", "$c2r5", "$c2r6", "$c2r7",
+ "$c2r8", "$c2r9", "$c2r10","$c2r11","$c2r12","$c2r13","$c2r14","$c2r15",
+ "$c2r16","$c2r17","$c2r18","$c2r19","$c2r20","$c2r21","$c2r22","$c2r23",
+ "$c2r24","$c2r25","$c2r26","$c2r27","$c2r28","$c2r29","$c2r30","$c2r31",
+ "$c3r0", "$c3r1", "$c3r2", "$c3r3", "$c3r4", "$c3r5", "$c3r6", "$c3r7",
+ "$c3r8", "$c3r9", "$c3r10","$c3r11","$c3r12","$c3r13","$c3r14","$c3r15",
+ "$c3r16","$c3r17","$c3r18","$c3r19","$c3r20","$c3r21","$c3r22","$c3r23",
+ "$c3r24","$c3r25","$c3r26","$c3r27","$c3r28","$c3r29","$c3r30","$c3r31"
 };
 
 /* Mips software names for the registers, used to overwrite the
@@ -356,11 +420,23 @@ char mips_sw_reg_names[][8] =
   "$f16", "$f17", "$f18", "$f19", "$f20", "$f21", "$f22", "$f23",
   "$f24", "$f25", "$f26", "$f27", "$f28", "$f29", "$f30", "$f31",
   "hi",   "lo",   "accum","$fcc0","$fcc1","$fcc2","$fcc3","$fcc4",
-  "$fcc5","$fcc6","$fcc7","$rap"
+  "$fcc5","$fcc6","$fcc7","$rap", "",     "",     "",     "",
+  "$c0r0", "$c0r1", "$c0r2", "$c0r3", "$c0r4", "$c0r5", "$c0r6", "$c0r7",
+  "$c0r8", "$c0r9", "$c0r10","$c0r11","$c0r12","$c0r13","$c0r14","$c0r15",
+  "$c0r16","$c0r17","$c0r18","$c0r19","$c0r20","$c0r21","$c0r22","$c0r23",
+  "$c0r24","$c0r25","$c0r26","$c0r27","$c0r28","$c0r29","$c0r30","$c0r31",
+  "$c2r0", "$c2r1", "$c2r2", "$c2r3", "$c2r4", "$c2r5", "$c2r6", "$c2r7",
+  "$c2r8", "$c2r9", "$c2r10","$c2r11","$c2r12","$c2r13","$c2r14","$c2r15",
+  "$c2r16","$c2r17","$c2r18","$c2r19","$c2r20","$c2r21","$c2r22","$c2r23",
+  "$c2r24","$c2r25","$c2r26","$c2r27","$c2r28","$c2r29","$c2r30","$c2r31",
+  "$c3r0", "$c3r1", "$c3r2", "$c3r3", "$c3r4", "$c3r5", "$c3r6", "$c3r7",
+  "$c3r8", "$c3r9", "$c3r10","$c3r11","$c3r12","$c3r13","$c3r14","$c3r15",
+  "$c3r16","$c3r17","$c3r18","$c3r19","$c3r20","$c3r21","$c3r22","$c3r23",
+  "$c3r24","$c3r25","$c3r26","$c3r27","$c3r28","$c3r29","$c3r30","$c3r31"
 };
 
 /* Map hard register number to register class */
-enum reg_class mips_regno_to_class[] =
+const enum reg_class mips_regno_to_class[] =
 {
   GR_REGS,     GR_REGS,        M16_NA_REGS,    M16_NA_REGS,
   M16_REGS,    M16_REGS,       M16_REGS,       M16_REGS,
@@ -380,7 +456,32 @@ enum reg_class mips_regno_to_class[] =
   FP_REGS,     FP_REGS,        FP_REGS,        FP_REGS,
   HI_REG,      LO_REG,         HILO_REG,       ST_REGS,
   ST_REGS,     ST_REGS,        ST_REGS,        ST_REGS,
-  ST_REGS,     ST_REGS,        ST_REGS,        GR_REGS
+  ST_REGS,     ST_REGS,        ST_REGS,        GR_REGS,
+  NO_REGS,     NO_REGS,        NO_REGS,        NO_REGS,
+  COP0_REGS,   COP0_REGS,      COP0_REGS,      COP0_REGS,
+  COP0_REGS,   COP0_REGS,      COP0_REGS,      COP0_REGS,
+  COP0_REGS,   COP0_REGS,      COP0_REGS,      COP0_REGS,
+  COP0_REGS,   COP0_REGS,      COP0_REGS,      COP0_REGS,
+  COP0_REGS,   COP0_REGS,      COP0_REGS,      COP0_REGS,
+  COP0_REGS,   COP0_REGS,      COP0_REGS,      COP0_REGS,
+  COP0_REGS,   COP0_REGS,      COP0_REGS,      COP0_REGS,
+  COP0_REGS,   COP0_REGS,      COP0_REGS,      COP0_REGS,
+  COP2_REGS,   COP2_REGS,      COP2_REGS,      COP2_REGS,
+  COP2_REGS,   COP2_REGS,      COP2_REGS,      COP2_REGS,
+  COP2_REGS,   COP2_REGS,      COP2_REGS,      COP2_REGS,
+  COP2_REGS,   COP2_REGS,      COP2_REGS,      COP2_REGS,
+  COP2_REGS,   COP2_REGS,      COP2_REGS,      COP2_REGS,
+  COP2_REGS,   COP2_REGS,      COP2_REGS,      COP2_REGS,
+  COP2_REGS,   COP2_REGS,      COP2_REGS,      COP2_REGS,
+  COP2_REGS,   COP2_REGS,      COP2_REGS,      COP2_REGS,
+  COP3_REGS,   COP3_REGS,      COP3_REGS,      COP3_REGS,
+  COP3_REGS,   COP3_REGS,      COP3_REGS,      COP3_REGS,
+  COP3_REGS,   COP3_REGS,      COP3_REGS,      COP3_REGS,
+  COP3_REGS,   COP3_REGS,      COP3_REGS,      COP3_REGS,
+  COP3_REGS,   COP3_REGS,      COP3_REGS,      COP3_REGS,
+  COP3_REGS,   COP3_REGS,      COP3_REGS,      COP3_REGS,
+  COP3_REGS,   COP3_REGS,      COP3_REGS,      COP3_REGS,
+  COP3_REGS,   COP3_REGS,      COP3_REGS,      COP3_REGS
 };
 
 /* Map register constraint character to register class.  */
@@ -453,6 +554,20 @@ enum reg_class mips_char_to_class[256] =
 };
 \f
 /* Initialize the GCC target structure.  */
+#undef TARGET_ASM_ALIGNED_HI_OP
+#define TARGET_ASM_ALIGNED_HI_OP "\t.half\t"
+#undef TARGET_ASM_ALIGNED_SI_OP
+#define TARGET_ASM_ALIGNED_SI_OP "\t.word\t"
+#undef TARGET_ASM_INTEGER
+#define TARGET_ASM_INTEGER mips_assemble_integer
+
+#if TARGET_IRIX5 && !TARGET_IRIX6
+#undef TARGET_ASM_UNALIGNED_HI_OP
+#define TARGET_ASM_UNALIGNED_HI_OP "\t.align 0\n\t.half\t"
+#undef TARGET_ASM_UNALIGNED_SI_OP
+#define TARGET_ASM_UNALIGNED_SI_OP "\t.align 0\n\t.word\t"
+#endif
+
 #undef TARGET_ASM_FUNCTION_PROLOGUE
 #define TARGET_ASM_FUNCTION_PROLOGUE mips_output_function_prologue
 #undef TARGET_ASM_FUNCTION_EPILOGUE
@@ -461,6 +576,9 @@ enum reg_class mips_char_to_class[256] =
 #undef TARGET_SCHED_ADJUST_COST
 #define TARGET_SCHED_ADJUST_COST mips_adjust_cost
 
+#undef TARGET_SCHED_ISSUE_RATE
+#define TARGET_SCHED_ISSUE_RATE mips_issue_rate
+
 struct gcc_target targetm = TARGET_INITIALIZER;
 \f
 /* Return truth value of whether OP can be used as an operands
@@ -509,7 +627,7 @@ arith32_operand (op, mode)
   return register_operand (op, mode);
 }
 
-/* Return truth value of whether OP is a integer which fits in 16 bits  */
+/* Return truth value of whether OP is an integer which fits in 16 bits.  */
 
 int
 small_int (op, mode)
@@ -582,7 +700,7 @@ reg_or_0_operand (op, mode)
 }
 
 /* Return truth value of whether OP is a register or the constant 0,
-   even in mips16 mode. */
+   even in mips16 mode.  */
 
 int
 true_reg_or_0_operand (op, mode)
@@ -797,7 +915,7 @@ simple_memory_operand (op, mode)
        if (GET_CODE (op) != SYMBOL_REF)
          return 0;
 
-       /* let's be paranoid.... */
+       /* let's be paranoid....  */
        if (! SMALL_INT (offset))
          return 0;
       }
@@ -902,7 +1020,7 @@ double_memory_operand (op, mode)
            return 1;
 
          /* Similarly, we accept a case where the memory address is
-             itself on the stack, and will be reloaded. */
+             itself on the stack, and will be reloaded.  */
          if (GET_CODE (addr) == MEM)
            {
              rtx maddr;
@@ -1226,6 +1344,29 @@ consttable_operand (op, mode)
   return CONSTANT_P (op);
 }
 
+/* Coprocessor operand; return true if rtx is a REG and refers to a
+   coprocessor.  */
+
+int
+coprocessor_operand (op, mode)
+     rtx op;
+     enum machine_mode mode ATTRIBUTE_UNUSED;
+{
+  return (GET_CODE (op) == REG
+         && COP0_REG_FIRST <= REGNO (op)
+         && REGNO (op) <= COP3_REG_LAST);
+}
+
+int
+coprocessor2_operand (op, mode)
+     rtx op;
+     enum machine_mode mode ATTRIBUTE_UNUSED;
+{
+  return (GET_CODE (op) == REG
+         && COP2_REG_FIRST <= REGNO (op)
+         && REGNO (op) <= COP2_REG_LAST);
+}
+
 /* Return nonzero if we split the address into high and low parts.  */
 
 /* ??? We should also handle reg+array somewhere.  We get four
@@ -1294,7 +1435,7 @@ mips_legitimate_address_p (mode, xinsn, strict)
     }
 
   /* Check for constant before stripping off SUBREG, so that we don't
-     accept (subreg (const_int)) which will fail to reload. */
+     accept (subreg (const_int)) which will fail to reload.  */
   if (CONSTANT_ADDRESS_P (xinsn)
       && ! (mips_split_addresses && mips_check_split (xinsn, mode))
       && (! TARGET_MIPS16 || mips16_constant (xinsn, mode, 1, 0)))
@@ -1810,19 +1951,18 @@ mips_count_memory_refs (op, num)
 }
 
 \f
-/* Return RTL for the offset from the current function to the argument.
-
-   ??? Which argument is this?  */
+/* Return a pseudo that points to the address of the current function.
+   The first time it is called for a function, an initializer for the
+   pseudo is emitted in the beginning of the function.  */
 
 rtx
-embedded_pic_offset (x)
-     rtx x;
+embedded_pic_fnaddr_reg ()
 {
-  if (embedded_pic_fnaddr_rtx == NULL)
+  if (cfun->machine->embedded_pic_fnaddr_rtx == NULL)
     {
       rtx seq;
 
-      embedded_pic_fnaddr_rtx = gen_reg_rtx (Pmode);
+      cfun->machine->embedded_pic_fnaddr_rtx = gen_reg_rtx (Pmode);
 
       /* Output code at function start to initialize the pseudo-reg.  */
       /* ??? We used to do this in FINALIZE_PIC, but that does not work for
@@ -1833,7 +1973,7 @@ embedded_pic_offset (x)
         initialize this value every time a function is inlined into another
         function.  */
       start_sequence ();
-      emit_insn (gen_get_fnaddr (embedded_pic_fnaddr_rtx,
+      emit_insn (gen_get_fnaddr (cfun->machine->embedded_pic_fnaddr_rtx,
                                 XEXP (DECL_RTL (current_function_decl), 0)));
       seq = gen_sequence ();
       end_sequence ();
@@ -1842,6 +1982,19 @@ embedded_pic_offset (x)
       pop_topmost_sequence ();
     }
 
+  return cfun->machine->embedded_pic_fnaddr_rtx;
+}
+
+/* Return RTL for the offset from the current function to the argument.
+   X is the symbol whose offset from the current function we want.  */
+
+rtx
+embedded_pic_offset (x)
+     rtx x;
+{
+  /* Make sure it is emitted.  */
+  embedded_pic_fnaddr_reg ();
+
   return
     gen_rtx_CONST (Pmode,
                   gen_rtx_MINUS (Pmode, x,
@@ -1925,7 +2078,13 @@ mips_move_1word (operands, insn, unsignedp)
                  delay = DELAY_LOAD;
                  if (FP_REG_P (regno1))
                    ret = "mfc1\t%0,%1";
+                 else if (ALL_COP_REG_P (regno1))
+                   {
+                     static char retval[] = "mfc_\t%0,%1";
 
+                     retval[3] = COPNUM_AS_CHAR_FROM_REGNUM (regno1);
+                     ret = retval;
+                   }
                  else if (regno1 == FPSW_REGNUM && ! ISA_HAS_8CC)
                    ret = "cfc1\t%0,$31";
                }
@@ -1961,6 +2120,21 @@ mips_move_1word (operands, insn, unsignedp)
                  ret = "ctc1\t%0,$31";
                }
            }
+         else if (ALL_COP_REG_P (regno0))
+           {
+             if (GP_REG_P (regno1))
+               {
+                 static char retval[] = "mtc_\t%1,%0";
+                 char cop = COPNUM_AS_CHAR_FROM_REGNUM (regno0);
+
+                 if (cop == '0')
+                   abort_with_insn (insn,
+                                    "mtc0 not supported; it disturbs virtual address translation");
+                 delay = DELAY_LOAD;
+                 retval[3] = cop;
+                 ret = retval;
+               }
+           }
        }
 
       else if (code1 == MEM)
@@ -1999,6 +2173,19 @@ mips_move_1word (operands, insn, unsignedp)
          else if (FP_REG_P (regno0) && (mode == SImode || mode == SFmode))
            ret = "l.s\t%0,%1";
 
+         else if (ALL_COP_REG_P (regno0))
+           {
+             static char retval[] = "lwc_\t%0,%1";
+             char cop = COPNUM_AS_CHAR_FROM_REGNUM (regno0);
+
+             if (cop == '0')
+               abort_with_insn (insn,
+                                "loads from memory to COP0 are illegal");
+             delay = DELAY_LOAD;
+             retval[3] = cop;
+             ret = retval;
+           }
+
          if (ret != (char *)0 && MEM_VOLATILE_P (op1))
            {
              size_t i = strlen (ret);
@@ -2197,6 +2384,13 @@ mips_move_1word (operands, insn, unsignedp)
 
          else if (FP_REG_P (regno1) && (mode == SImode || mode == SFmode))
            ret = "s.s\t%1,%0";
+         else if (ALL_COP_REG_P (regno1))
+           {
+             static char retval[] = "swc_\t%1,%0";
+
+             retval[3] = COPNUM_AS_CHAR_FROM_REGNUM (regno1);
+             ret = retval;
+           }
        }
 
       else if (code1 == CONST_INT && INTVAL (op1) == 0)
@@ -2237,7 +2431,7 @@ mips_move_1word (operands, insn, unsignedp)
 
   if (ret == 0)
     {
-      abort_with_insn (insn, "Bad move");
+      abort_with_insn (insn, "bad move");
       return 0;
     }
 
@@ -2325,7 +2519,7 @@ mips_move_2words (operands, insn)
                  if (TARGET_FLOAT64)
                    {
                      if (!TARGET_64BIT)
-                       abort_with_insn (insn, "Bad move");
+                       abort_with_insn (insn, "bad move");
 
 #ifdef TARGET_FP_CALL_32
                      if (FP_CALL_GP_REG_P (regno1))
@@ -2345,7 +2539,7 @@ mips_move_2words (operands, insn)
              if (TARGET_FLOAT64)
                {
                  if (!TARGET_64BIT)
-                   abort_with_insn (insn, "Bad move");
+                   abort_with_insn (insn, "bad move");
 
 #ifdef TARGET_FP_CALL_32
                  if (FP_CALL_GP_REG_P (regno0))
@@ -2383,7 +2577,28 @@ mips_move_2words (operands, insn)
              else
                ret = "mfhi\t%M0\n\tmflo\t%L0";
            }
+         else if (GP_REG_P (regno0) && ALL_COP_REG_P (regno1)
+                  && TARGET_64BIT)
+           {
+             static char retval[] = "dmfc_\t%0,%1";
 
+             delay = DELAY_LOAD;
+             retval[4] = COPNUM_AS_CHAR_FROM_REGNUM (regno1);
+             ret = retval;
+           }
+         else if (ALL_COP_REG_P (regno0) && GP_REG_P (regno1)
+                  && TARGET_64BIT)
+           {
+             static char retval[] = "dmtc_\t%1,%0";
+             char cop = COPNUM_AS_CHAR_FROM_REGNUM (regno0);
+
+             if (cop == '0')
+               abort_with_insn (insn,
+                                "dmtc0 not supported; it disturbs virtual address translation");
+             delay = DELAY_LOAD;
+             retval[4] = cop;
+             ret = retval;
+           }
          else if (TARGET_64BIT)
            ret = "move\t%0,%1";
 
@@ -2539,7 +2754,18 @@ mips_move_2words (operands, insn)
              operands[2] = GEN_INT (INTVAL (operands[1]) >> 16 >> 16);
              operands[1]
                = GEN_INT (INTVAL (operands[1]) << 16 << 16 >> 16 >> 16);
-             ret = "li\t%M0,%2\n\tli\t%L0,%1";
+             if (TARGET_MIPS16)
+               {
+                 if (INTVAL (op1) >= 0 && INTVAL (op1) <= 0xffff)
+                   ret = "li\t%M0,%2\n\tli\t%L0,%1";
+                 else if (INTVAL (op1) < 0 && INTVAL (op1) >= -0xffff)
+                   {
+                     operands[2] = GEN_INT (1);
+                     ret = "li\t%M0,%2\n\tneg\t%M0\n\tli\t%L0,%n1\n\tneg\t%L0";
+                   }
+               }
+             else
+               ret = "li\t%M0,%2\n\tli\t%L0,%1";
            }
        }
 
@@ -2553,6 +2779,19 @@ mips_move_2words (operands, insn)
          if (FP_REG_P (regno0))
            ret = "l.d\t%0,%1";
 
+         else if (ALL_COP_REG_P (regno0) && TARGET_64BIT)
+           {
+             static char retval[] = "ldc_\t%0,%1";
+             char cop = COPNUM_AS_CHAR_FROM_REGNUM (regno0);
+
+             if (cop == '0')
+               abort_with_insn (insn,
+                                "loads from memory to COP0 are illegal");
+             delay = DELAY_LOAD;
+             retval[3] = cop;
+             ret = retval;
+           }
+
          else if (TARGET_64BIT)
            {
 
@@ -2646,6 +2885,13 @@ mips_move_2words (operands, insn)
          if (FP_REG_P (regno1))
            ret = "s.d\t%1,%0";
 
+         else if (ALL_COP_REG_P (regno1) && TARGET_64BIT)
+           {
+             static char retval[] = "sdc_\t%1,%0";
+
+             retval[3] = COPNUM_AS_CHAR_FROM_REGNUM (regno1);
+             ret = retval;
+           }
          else if (TARGET_64BIT)
            {
 
@@ -2690,7 +2936,7 @@ mips_move_2words (operands, insn)
 
   if (ret == 0)
     {
-      abort_with_insn (insn, "Bad move");
+      abort_with_insn (insn, "bad move");
       return 0;
     }
 
@@ -2729,7 +2975,7 @@ mips_address_cost (addr)
          return 2;
       }
 
-      /* ... fall through ... */
+      /* ... fall through ...  */
 
     case SYMBOL_REF:
       return SYMBOL_REF_FLAG (addr) ? 1 : 2;
@@ -2855,7 +3101,7 @@ gen_int_relational (test_code, result, cmp0, cmp1, p_invert)
     int unsignedp;             /* != 0 for unsigned comparisons.  */
   };
 
-  static struct cmp_info info[ (int)ITEST_MAX ] = {
+  static const struct cmp_info info[ (int)ITEST_MAX ] = {
 
     { XOR,      0,  65535,  0,  0,  0,  0, 0 },        /* EQ  */
     { XOR,      0,  65535,  0,  0,  1,  1, 0 },        /* NE  */
@@ -2871,7 +3117,7 @@ gen_int_relational (test_code, result, cmp0, cmp1, p_invert)
 
   enum internal_test test;
   enum machine_mode mode;
-  struct cmp_info *p_info;
+  const struct cmp_info *p_info;
   int branch_p;
   int eqne_p;
   int invert;
@@ -3482,7 +3728,7 @@ expand_block_move (operands)
    The block move type can be one of the following:
        BLOCK_MOVE_NORMAL       Do all of the block move.
        BLOCK_MOVE_NOT_LAST     Do all but the last store.
-       BLOCK_MOVE_LAST         Do just the last store. */
+       BLOCK_MOVE_LAST         Do just the last store.  */
 
 const char *
 output_block_move (insn, operands, num_regs, move_type)
@@ -3618,7 +3864,7 @@ output_block_move (insn, operands, num_regs, move_type)
 
   else if (num_regs < 1)
     abort_with_insn (insn,
-                    "Cannot do block move, not enough scratch registers");
+                    "cannot do block move, not enough scratch registers");
 
   while (bytes > 0)
     {
@@ -3859,7 +4105,7 @@ init_cumulative_args (cum, fntype, libname)
   /* Determine if this function has variable arguments.  This is
      indicated by the last argument being 'void_type_mode' if there
      are no variable arguments.  The standard MIPS calling sequence
-     passes all arguments in the general purpose registers in this case. */
+     passes all arguments in the general purpose registers in this case.  */
 
   for (param = fntype ? TYPE_ARG_TYPES (fntype) : 0;
        param != 0; param = next_param)
@@ -3870,6 +4116,103 @@ init_cumulative_args (cum, fntype, libname)
     }
 }
 
+static void
+mips_arg_info (cum, mode, type, named, info)
+     const CUMULATIVE_ARGS *cum;
+     enum machine_mode mode;
+     tree type;
+     int named;
+     struct mips_arg_info *info;
+{
+  bool even_reg_p;
+  unsigned int num_words, max_regs;
+
+  info->struct_p = (type != 0
+                   && (TREE_CODE (type) == RECORD_TYPE
+                       || TREE_CODE (type) == UNION_TYPE
+                       || TREE_CODE (type) == QUAL_UNION_TYPE));
+
+  /* Decide whether this argument should go in a floating-point register,
+     assuming one is free.  Later code checks for availablity.  */
+
+  info->fpr_p = false;
+  if (GET_MODE_CLASS (mode) == MODE_FLOAT
+      && GET_MODE_SIZE (mode) <= UNITS_PER_FPVALUE)
+    {
+      switch (mips_abi)
+       {
+       case ABI_32:
+       case ABI_O64:
+         info->fpr_p = (!cum->gp_reg_found && cum->arg_number < 2);
+         break;
+
+       case ABI_EABI:
+         info->fpr_p = true;
+         break;
+
+       case ABI_MEABI:
+         /* The MIPS eabi says only structures containing doubles get
+            passed in a fp register, so force a structure containing
+            a float to be passed in the integer registers.  */
+         info->fpr_p = (named && !(mode == SFmode && info->struct_p));
+         break;
+
+       default:
+         info->fpr_p = named;
+         break;
+       }
+    }
+
+  /* Now decide whether the argument must go in an even-numbered register.  */
+
+  even_reg_p = false;
+  if (info->fpr_p)
+    {
+      /* Under the O64 ABI, the second float argument goes in $f13 if it
+        is a double, but $f14 if it is a single.  Otherwise, on a
+        32-bit double-float machine, each FP argument must start in a
+        new register pair.  */
+      even_reg_p = ((mips_abi == ABI_O64 && mode == SFmode) || FP_INC > 1);
+    }
+  else if (!TARGET_64BIT)
+    {
+      if (GET_MODE_CLASS (mode) == MODE_INT
+         || GET_MODE_CLASS (mode) == MODE_FLOAT)
+       even_reg_p = (GET_MODE_SIZE (mode) > UNITS_PER_WORD);
+
+      else if (type != NULL_TREE && TYPE_ALIGN (type) > BITS_PER_WORD)
+       even_reg_p = true;
+    }
+
+  /* Set REG_OFFSET to the register count we're interested in.
+     The EABI allocates the floating-point registers separately,
+     but the other ABIs allocate them like integer registers.  */
+  info->reg_offset = (mips_abi == ABI_EABI && info->fpr_p
+                     ? cum->num_fprs
+                     : cum->num_gprs);
+
+  if (even_reg_p)
+    info->reg_offset += info->reg_offset & 1;
+
+  /* The alignment applied to registers is also applied to stack arguments.  */
+  info->stack_offset = cum->stack_words;
+  if (even_reg_p)
+    info->stack_offset += info->stack_offset & 1;
+
+  if (mode == BLKmode)
+    info->num_bytes = int_size_in_bytes (type);
+  else
+    info->num_bytes = GET_MODE_SIZE (mode);
+
+  num_words = (info->num_bytes + UNITS_PER_WORD - 1) / UNITS_PER_WORD;
+  max_regs = MAX_ARGS_IN_REGISTERS - info->reg_offset;
+
+  /* Partition the argument between registers and stack.  */
+  info->reg_words = MIN (num_words, max_regs);
+  info->stack_words = num_words - info->reg_words;
+}
+
+
 /* Advance the argument to the next argument position.  */
 
 void
@@ -3879,68 +4222,59 @@ function_arg_advance (cum, mode, type, named)
      tree type;                        /* type of the argument or 0 if lib support */
      int named;                        /* whether or not the argument was named */
 {
-  if (TARGET_DEBUG_E_MODE)
-    {
-      fprintf (stderr,
-              "function_adv({gp reg found = %d, arg # = %2d, words = %2d}, %4s, ",
-              cum->gp_reg_found, cum->arg_number, cum->arg_words,
-              GET_MODE_NAME (mode));
-      fprintf (stderr, HOST_PTR_PRINTF, (const PTR) type);
-      fprintf (stderr, ", %d )\n\n", named);
-    }
-
-  cum->arg_number++;
-  switch (mode)
+  struct mips_arg_info info;
+
+  mips_arg_info (cum, mode, type, named, &info);
+
+  /* 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.  */
+
+  /* ??? This scheme requires everything smaller than the word size to
+     shifted to the left, but when TARGET_64BIT and ! TARGET_INT64,
+     that would mean every int needs to be shifted left, which is very
+     inefficient.  Let's not carry this compatibility to the 64 bit
+     calling convention for now.  */
+
+  if (info.struct_p
+      && info.reg_words == 1
+      && info.num_bytes < UNITS_PER_WORD
+      && !TARGET_64BIT
+      && mips_abi != ABI_EABI
+      && mips_abi != ABI_MEABI)
     {
-    case VOIDmode:
-      break;
+      rtx amount = GEN_INT (BITS_PER_WORD - info.num_bytes * BITS_PER_UNIT);
+      rtx reg = gen_rtx_REG (word_mode, GP_ARG_FIRST + info.reg_offset);
 
-    default:
-      if (GET_MODE_CLASS (mode) != MODE_COMPLEX_INT
-         && GET_MODE_CLASS (mode) != MODE_COMPLEX_FLOAT)
-       abort ();
-
-      cum->gp_reg_found = 1;
-      cum->arg_words += ((GET_MODE_SIZE (mode) + UNITS_PER_WORD - 1)
-                        / UNITS_PER_WORD);
-      break;
+      if (TARGET_64BIT)
+       cum->adjust[cum->num_adjusts++] = gen_ashldi3 (reg, reg, amount);
+      else
+       cum->adjust[cum->num_adjusts++] = gen_ashlsi3 (reg, reg, amount);
+    }
 
-    case BLKmode:
-      cum->gp_reg_found = 1;
-      cum->arg_words += ((int_size_in_bytes (type) + UNITS_PER_WORD - 1)
-                        / UNITS_PER_WORD);
-      break;
+  if (!info.fpr_p)
+    cum->gp_reg_found = true;
 
-    case SFmode:
-      if (mips_abi == ABI_EABI && ! TARGET_SOFT_FLOAT)
-       cum->fp_arg_words++;
-      else
-       cum->arg_words++;
-      if (! cum->gp_reg_found && cum->arg_number <= 2)
-       cum->fp_code += 1 << ((cum->arg_number - 1) * 2);
-      break;
+  /* See the comment above the cumulative args structure in mips.h
+     for an explanation of what this code does.  It assumes the O32
+     ABI, which passes at most 2 arguments in float registers.  */
+  if (cum->arg_number < 2 && info.fpr_p)
+    cum->fp_code += (mode == SFmode ? 1 : 2) << ((cum->arg_number - 1) * 2);
 
-    case DFmode:
-      if (mips_abi == ABI_EABI && ! TARGET_SOFT_FLOAT && ! TARGET_SINGLE_FLOAT)
-       cum->fp_arg_words += (TARGET_64BIT ? 1 : 2);
-      else
-       cum->arg_words += (TARGET_64BIT ? 1 : 2);
-      if (! cum->gp_reg_found && ! TARGET_SINGLE_FLOAT && cum->arg_number <= 2)
-       cum->fp_code += 2 << ((cum->arg_number - 1) * 2);
-      break;
+  if (mips_abi != ABI_EABI || !info.fpr_p)
+    cum->num_gprs = info.reg_offset + info.reg_words;
+  else if (info.reg_words > 0)
+    cum->num_fprs += FP_INC;
 
-    case DImode:
-      cum->gp_reg_found = 1;
-      cum->arg_words += (TARGET_64BIT ? 1 : 2);
-      break;
+  if (info.stack_words > 0)
+    cum->stack_words = info.stack_offset + info.stack_words;
 
-    case QImode:
-    case HImode:
-    case SImode:
-      cum->gp_reg_found = 1;
-      cum->arg_words++;
-      break;
-    }
+  cum->arg_number++;
 }
 
 /* Return an RTL expression containing the register for the given mode,
@@ -3948,328 +4282,222 @@ function_arg_advance (cum, mode, type, named)
 
 struct rtx_def *
 function_arg (cum, mode, type, named)
-     CUMULATIVE_ARGS *cum;     /* current arg information */
+     const CUMULATIVE_ARGS *cum; /* current arg information */
      enum machine_mode mode;   /* current arg mode */
      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;
-  unsigned int *arg_words = &cum->arg_words;
-  int struct_p = (type != 0
-                 && (TREE_CODE (type) == RECORD_TYPE
-                     || TREE_CODE (type) == UNION_TYPE
-                     || TREE_CODE (type) == QUAL_UNION_TYPE));
-
-  if (TARGET_DEBUG_E_MODE)
-    {
-      fprintf (stderr,
-              "function_arg( {gp reg found = %d, arg # = %2d, words = %2d}, %4s, ",
-              cum->gp_reg_found, cum->arg_number, cum->arg_words,
-              GET_MODE_NAME (mode));
-      fprintf (stderr, HOST_PTR_PRINTF, (const PTR) type);
-      fprintf (stderr, ", %d ) = ", named);
-    }
+  struct mips_arg_info info;
 
-
-  cum->last_arg_fp = 0;
-  switch (mode)
+  /* We will be called with a mode of VOIDmode after the last argument
+     has been seen.  Whatever we return will be passed to the call
+     insn.  If we need any shifts for small structures, return them in
+     a PARALLEL; in that case, stuff the mips16 fp_code in as the
+     mode.  Otherwise, if we need a mips16 fp_code, return a REG
+     with the code stored as the mode.  */
+  if (mode == VOIDmode)
     {
-    case SFmode:
-      if (mips_abi == ABI_32 || mips_abi == ABI_O64)
-       {
-         if (cum->gp_reg_found || cum->arg_number >= 2 || TARGET_SOFT_FLOAT)
-           regbase = GP_ARG_FIRST;
-         else
-           {
-             regbase = FP_ARG_FIRST;
-
-             /* If the first arg was a float in a floating point register,
-                then set bias to align this float arg properly.  */
-             if (cum->arg_words == 1)
-               bias = 1;
-           }
-       }
-      else if (mips_abi == ABI_EABI && ! TARGET_SOFT_FLOAT)
-       {
-         if (! TARGET_64BIT)
-           cum->fp_arg_words += cum->fp_arg_words & 1;
-         cum->last_arg_fp = 1;
-         arg_words = &cum->fp_arg_words;
-         regbase = FP_ARG_FIRST;
-       }
-      /* The MIPS eabi says only structures containing doubles get passed in a
-         fp register, so force a structure containing a float to be passed in
-         the integer registers.  */
-      else if (mips_abi == ABI_MEABI && struct_p)
-       regbase = GP_ARG_FIRST;
-      else
-       regbase = (TARGET_SOFT_FLOAT || ! named ? GP_ARG_FIRST : FP_ARG_FIRST);
-      break;
+      if (cum->num_adjusts > 0)
+       return gen_rtx_PARALLEL ((enum machine_mode) cum->fp_code,
+                                gen_rtvec_v (cum->num_adjusts,
+                                             (rtx *) cum->adjust));
 
-    case DFmode:
-      if (! TARGET_64BIT)
-       {
-         if (mips_abi == ABI_EABI
-             && ! TARGET_SOFT_FLOAT && ! TARGET_SINGLE_FLOAT)
-           cum->fp_arg_words += cum->fp_arg_words & 1;
-         else
-           cum->arg_words += cum->arg_words & 1;
-       }
+      else if (TARGET_MIPS16 && cum->fp_code != 0)
+       return gen_rtx_REG ((enum machine_mode) cum->fp_code, 0);
 
-      if (mips_abi == ABI_32 || mips_abi == ABI_O64)
-       regbase = ((cum->gp_reg_found
-                   || TARGET_SOFT_FLOAT || TARGET_SINGLE_FLOAT
-                   || cum->arg_number >= 2)
-                  ? GP_ARG_FIRST : FP_ARG_FIRST);
-      else if (mips_abi == ABI_EABI
-              && ! TARGET_SOFT_FLOAT && ! TARGET_SINGLE_FLOAT)
-       {
-         cum->last_arg_fp = 1;
-         arg_words = &cum->fp_arg_words;
-         regbase = FP_ARG_FIRST;
-       }
       else
-       regbase = (TARGET_SOFT_FLOAT || TARGET_SINGLE_FLOAT || ! named
-                  ? GP_ARG_FIRST : FP_ARG_FIRST);
-      break;
-
-    default:
-      if (GET_MODE_CLASS (mode) != MODE_COMPLEX_INT
-         && GET_MODE_CLASS (mode) != MODE_COMPLEX_FLOAT)
-       abort ();
-
-      /* Drops through.  */
-    case BLKmode:
-      if (type != NULL_TREE && TYPE_ALIGN (type) > (unsigned) BITS_PER_WORD
-         && ! TARGET_64BIT && mips_abi != ABI_EABI)
-       cum->arg_words += (cum->arg_words & 1);
-      regbase = GP_ARG_FIRST;
-      break;
-
-    case VOIDmode:
-    case QImode:
-    case HImode:
-    case SImode:
-      regbase = GP_ARG_FIRST;
-      break;
-
-    case DImode:
-      if (! TARGET_64BIT)
-       cum->arg_words += (cum->arg_words & 1);
-      regbase = GP_ARG_FIRST;
+       return 0;
     }
 
-  if (*arg_words >= (unsigned) MAX_ARGS_IN_REGISTERS)
-    {
-      if (TARGET_DEBUG_E_MODE)
-       fprintf (stderr, "<stack>%s\n", struct_p ? ", [struct]" : "");
+  mips_arg_info (cum, mode, type, named, &info);
 
-      ret = 0;
-    }
-  else
-    {
-      if (regbase == -1)
-       abort ();
+  /* Return straight away if the whole argument is passed on the stack.  */
+  if (info.reg_offset == MAX_ARGS_IN_REGISTERS)
+    return 0;
 
-      if (! type || TREE_CODE (type) != RECORD_TYPE
-         || mips_abi == ABI_32  || mips_abi == ABI_EABI
-         || mips_abi == ABI_O64 || mips_abi == ABI_MEABI
-         || ! named
-         || ! TYPE_SIZE_UNIT (type)
-         || ! host_integerp (TYPE_SIZE_UNIT (type), 1))
-       {
+  if (type != 0
+      && TREE_CODE (type) == RECORD_TYPE
+      && (mips_abi == ABI_N32 || mips_abi == ABI_64)
+      && TYPE_SIZE_UNIT (type)
+      && host_integerp (TYPE_SIZE_UNIT (type), 1)
+      && named
+      && mode != DFmode)
+    {
+      /* The Irix 6 n32/n64 ABIs say that if any 64 bit chunk of the
+        structure contains a double in its entirety, then that 64 bit
+        chunk is passed in a floating point register.  */
+      tree field;
+
+      /* First check to see if there is any such field.  */
+      for (field = TYPE_FIELDS (type); field; field = TREE_CHAIN (field))
+       if (TREE_CODE (field) == FIELD_DECL
+           && TREE_CODE (TREE_TYPE (field)) == REAL_TYPE
+           && TYPE_PRECISION (TREE_TYPE (field)) == BITS_PER_WORD
+           && host_integerp (bit_position (field), 0)
+           && int_bit_position (field) % BITS_PER_WORD == 0)
+         break;
 
-         unsigned int arg_reg = (regbase + *arg_words + bias);
-         ret = gen_rtx_REG (mode, arg_reg);
-         if (mips_abi == ABI_MEABI
-             && regbase == FP_ARG_FIRST
-             && ! cum->prototype)
-           {
-              /* To make K&R varargs work we need to pass floating
-                 point arguments in both integer and FP registers.  */
-              ret = gen_rtx_PARALLEL (mode,
-                                     gen_rtvec (2,
-                                                gen_rtx_EXPR_LIST (VOIDmode,
-                                                                   gen_rtx_REG (mode,
-                                                                                arg_reg + GP_ARG_FIRST - FP_ARG_FIRST),
-                                                                   const0_rtx),                                                gen_rtx_EXPR_LIST (VOIDmode, ret, const0_rtx)));
-            }
-       }
-      else
+      if (field != 0)
        {
-         /* The Irix 6 n32/n64 ABIs say that if any 64 bit chunk of the
-            structure contains a double in its entirety, then that 64 bit
-            chunk is passed in a floating point register.  */
-         tree field;
-
-         /* First check to see if there is any such field.  */
-         for (field = TYPE_FIELDS (type); field; field = TREE_CHAIN (field))
-           if (TREE_CODE (field) == FIELD_DECL
-               && TREE_CODE (TREE_TYPE (field)) == REAL_TYPE
-               && TYPE_PRECISION (TREE_TYPE (field)) == BITS_PER_WORD
-               && host_integerp (bit_position (field), 0)
-               && int_bit_position (field) % BITS_PER_WORD == 0)
-             break;
+         /* Now handle the special case by returning a PARALLEL
+            indicating where each 64 bit chunk goes.  INFO.REG_WORDS
+            chunks are passed in registers.  */
+         unsigned int i;
+         HOST_WIDE_INT bitpos;
+         rtx ret;
 
-         /* If the whole struct fits a DFmode register,
-            we don't need the PARALLEL.  */
-         if (! field || mode == DFmode)
-           ret = gen_rtx_REG (mode, regbase + *arg_words + bias);
-         else
-           {
-             /* Now handle the special case by returning a PARALLEL
-                indicating where each 64 bit chunk goes.  */
-             unsigned int chunks;
-             HOST_WIDE_INT bitpos;
-             unsigned int regno;
-             unsigned int i;
-
-             /* ??? If this is a packed structure, then the last hunk won't
-                be 64 bits.  */
-
-             chunks
-               = tree_low_cst (TYPE_SIZE_UNIT (type), 1) / UNITS_PER_WORD;
-             if (chunks + *arg_words + bias > (unsigned) MAX_ARGS_IN_REGISTERS)
-               chunks = MAX_ARGS_IN_REGISTERS - *arg_words - bias;
-
-             /* assign_parms checks the mode of ENTRY_PARM, so we must
-                use the actual mode here.  */
-             ret = gen_rtx_PARALLEL (mode, rtvec_alloc (chunks));
-
-             bitpos = 0;
-             regno = regbase + *arg_words + bias;
-             field = TYPE_FIELDS (type);
-             for (i = 0; i < chunks; i++)
-               {
-                 rtx reg;
-
-                 for (; field; field = TREE_CHAIN (field))
-                   if (TREE_CODE (field) == FIELD_DECL
-                       && int_bit_position (field) >= bitpos)
-                     break;
-
-                 if (field
-                     && int_bit_position (field) == bitpos
-                     && TREE_CODE (TREE_TYPE (field)) == REAL_TYPE
-                     && !TARGET_SOFT_FLOAT
-                     && TYPE_PRECISION (TREE_TYPE (field)) == BITS_PER_WORD)
-                   reg = gen_rtx_REG (DFmode,
-                                      regno + FP_ARG_FIRST - GP_ARG_FIRST);
-                 else
-                   reg = gen_rtx_REG (word_mode, regno);
+         /* assign_parms checks the mode of ENTRY_PARM, so we must
+            use the actual mode here.  */
+         ret = gen_rtx_PARALLEL (mode, rtvec_alloc (info.reg_words));
 
-                 XVECEXP (ret, 0, i)
-                   = gen_rtx_EXPR_LIST (VOIDmode, reg,
-                                        GEN_INT (bitpos / BITS_PER_UNIT));
-
-                 bitpos += 64;
-                 regno++;
-               }
-           }
-       }
-
-      if (TARGET_DEBUG_E_MODE)
-       fprintf (stderr, "%s%s\n", reg_names[regbase + *arg_words + bias],
-                struct_p ? ", [struct]" : "");
+         bitpos = 0;
+         field = TYPE_FIELDS (type);
+         for (i = 0; i < info.reg_words; i++)
+           {
+             rtx reg;
 
-      /* 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.  */
+             for (; field; field = TREE_CHAIN (field))
+               if (TREE_CODE (field) == FIELD_DECL
+                   && int_bit_position (field) >= bitpos)
+                 break;
 
-      /* ??? function_arg can be called more than once for each argument.
-        As a result, we compute more adjustments than we need here.
-        See the CUMULATIVE_ARGS definition in mips.h.  */
+             if (field
+                 && int_bit_position (field) == bitpos
+                 && TREE_CODE (TREE_TYPE (field)) == REAL_TYPE
+                 && !TARGET_SOFT_FLOAT
+                 && TYPE_PRECISION (TREE_TYPE (field)) == BITS_PER_WORD)
+               reg = gen_rtx_REG (DFmode, FP_ARG_FIRST + info.reg_offset + i);
+             else
+               reg = gen_rtx_REG (DImode, GP_ARG_FIRST + info.reg_offset + i);
 
-      /* ??? This scheme requires everything smaller than the word size to
-        shifted to the left, but when TARGET_64BIT and ! TARGET_INT64,
-        that would mean every int needs to be shifted left, which is very
-        inefficient.  Let's not carry this compatibility to the 64 bit
-        calling convention for now.  */
+             XVECEXP (ret, 0, i)
+               = gen_rtx_EXPR_LIST (VOIDmode, reg,
+                                    GEN_INT (bitpos / BITS_PER_UNIT));
 
-      if (struct_p && int_size_in_bytes (type) < UNITS_PER_WORD
-         && ! TARGET_64BIT
-         && mips_abi != ABI_EABI
-         && mips_abi != ABI_MEABI)
-       {
-         rtx amount = GEN_INT (BITS_PER_WORD
-                               - int_size_in_bytes (type) * BITS_PER_UNIT);
-         rtx reg = gen_rtx_REG (word_mode, regbase + *arg_words + bias);
-
-         if (TARGET_64BIT)
-           cum->adjust[cum->num_adjusts++] = gen_ashldi3 (reg, reg, amount);
-         else
-           cum->adjust[cum->num_adjusts++] = gen_ashlsi3 (reg, reg, amount);
+             bitpos += BITS_PER_WORD;
+           }
+         return ret;
        }
     }
 
-  /* We will be called with a mode of VOIDmode after the last argument
-     has been seen.  Whatever we return will be passed to the call
-     insn.  If we need any shifts for small structures, return them in
-     a PARALLEL; in that case, stuff the mips16 fp_code in as the
-     mode.  Otherwise, if we have need a mips16 fp_code, return a REG
-     with the code stored as the mode.  */
-  if (mode == VOIDmode)
+  if (mips_abi == ABI_MEABI && info.fpr_p && !cum->prototype)
     {
-      if (cum->num_adjusts > 0)
-       ret = gen_rtx (PARALLEL, (enum machine_mode) cum->fp_code,
-                      gen_rtvec_v (cum->num_adjusts, cum->adjust));
-      else if (TARGET_MIPS16 && cum->fp_code != 0)
-       ret = gen_rtx (REG, (enum machine_mode) cum->fp_code, 0);
+      /* To make K&R varargs work we need to pass floating
+        point arguments in both integer and FP registers.  */
+      return gen_rtx_PARALLEL
+       (mode,
+        gen_rtvec (2,
+                   gen_rtx_EXPR_LIST (VOIDmode,
+                                      gen_rtx_REG (mode,
+                                                   GP_ARG_FIRST
+                                                   + info.reg_offset),
+                                      const0_rtx),
+                   gen_rtx_EXPR_LIST (VOIDmode,
+                                      gen_rtx_REG (mode,
+                                                   FP_ARG_FIRST
+                                                   + info.reg_offset),
+                                      const0_rtx)));
     }
 
-  return ret;
+  if (info.fpr_p)
+    return gen_rtx_REG (mode, FP_ARG_FIRST + info.reg_offset);
+  else
+    return gen_rtx_REG (mode, GP_ARG_FIRST + info.reg_offset);
 }
 
 int
 function_arg_partial_nregs (cum, mode, type, named)
-     CUMULATIVE_ARGS *cum;     /* current arg information */
+     const CUMULATIVE_ARGS *cum; /* current arg information */
      enum machine_mode mode;   /* current arg mode */
      tree type;                        /* type of the argument or 0 if lib support */
-     int named ATTRIBUTE_UNUSED;/* != 0 for normal args, == 0 for ... args */
-{
-  if ((mode == BLKmode
-       || GET_MODE_CLASS (mode) != MODE_COMPLEX_INT
-       || GET_MODE_CLASS (mode) != MODE_COMPLEX_FLOAT)
-      && cum->arg_words < (unsigned) MAX_ARGS_IN_REGISTERS
-      && mips_abi != ABI_EABI)
-    {
-      int words;
-      if (mode == BLKmode)
-       words = ((int_size_in_bytes (type) + UNITS_PER_WORD - 1)
-                / UNITS_PER_WORD);
-      else
-       words = (GET_MODE_SIZE (mode) + UNITS_PER_WORD - 1) / UNITS_PER_WORD;
+     int named;                        /* != 0 for normal args, == 0 for ... args */
+{
+  struct mips_arg_info info;
+
+  mips_arg_info (cum, mode, type, named, &info);
+  return info.stack_words > 0 ? info.reg_words : 0;
+}
+\f
+int
+mips_setup_incoming_varargs (cum, mode, type, no_rtl)
+     const CUMULATIVE_ARGS *cum;
+     enum machine_mode mode;
+     tree type;
+     int no_rtl;
+{
+  CUMULATIVE_ARGS local_cum;
+  int gp_saved, fp_saved;
 
-      if (words + cum->arg_words <= (unsigned) MAX_ARGS_IN_REGISTERS)
-       return 0;               /* structure fits in registers */
+  if (mips_abi == ABI_32 || mips_abi == ABI_O64)
+    return 0;
 
-      if (TARGET_DEBUG_E_MODE)
-       fprintf (stderr, "function_arg_partial_nregs = %d\n",
-                MAX_ARGS_IN_REGISTERS - cum->arg_words);
+  /* The caller has advanced CUM up to, but not beyond, the last named
+     argument.  Advance a local copy of CUM past the last "real" named
+     argument, to find out how many registers are left over.
 
-      return MAX_ARGS_IN_REGISTERS - cum->arg_words;
-    }
+     For K&R varargs, the last named argument is a dummy word-sized one,
+     so CUM already contains the information we need.  For stdarg, it is
+     a real argument (such as the format in printf()) and we need to
+     step over it.  */
+  local_cum = *cum;
+  if (!current_function_varargs)
+    FUNCTION_ARG_ADVANCE (local_cum, mode, type, 1);
 
-  else if (mode == DImode
-          && cum->arg_words == MAX_ARGS_IN_REGISTERS - (unsigned)1
-          && ! TARGET_64BIT && mips_abi != ABI_EABI)
+  /* Found out how many registers we need to save.  */
+  gp_saved = MAX_ARGS_IN_REGISTERS - local_cum.num_gprs;
+  fp_saved = (EABI_FLOAT_VARARGS_P
+             ? MAX_ARGS_IN_REGISTERS - local_cum.num_fprs
+             : 0);
+
+  if (!no_rtl)
     {
-      if (TARGET_DEBUG_E_MODE)
-       fprintf (stderr, "function_arg_partial_nregs = 1\n");
+      if (gp_saved > 0)
+       {
+         rtx ptr, mem;
 
-      return 1;
-    }
+         ptr = virtual_incoming_args_rtx;
+         if (mips_abi == ABI_EABI)
+           ptr = plus_constant (ptr, -gp_saved * UNITS_PER_WORD);
+         mem = gen_rtx_MEM (BLKmode, ptr);
 
-  return 0;
+         /* va_arg is an array access in this case, which causes
+            it to get MEM_IN_STRUCT_P set.  We must set it here
+            so that the insn scheduler won't assume that these
+            stores can't possibly overlap with the va_arg loads.  */
+         if (mips_abi != ABI_EABI && BYTES_BIG_ENDIAN)
+           MEM_SET_IN_STRUCT_P (mem, 1);
+
+         move_block_from_reg (local_cum.num_gprs + GP_ARG_FIRST, mem,
+                              gp_saved, gp_saved * UNITS_PER_WORD);
+       }
+      if (fp_saved > 0)
+       {
+         /* We can't use move_block_from_reg, because it will use
+            the wrong mode. */
+         enum machine_mode mode;
+         int off, i;
+
+         /* Set OFF to the offset from virtual_incoming_args_rtx of
+            the first float register.   The FP save area lies below
+            the integer one, and is aligned to UNITS_PER_FPVALUE bytes.  */
+         off = -gp_saved * UNITS_PER_WORD;
+         off &= ~(UNITS_PER_FPVALUE - 1);
+         off -= fp_saved * UNITS_PER_FPREG;
+
+         mode = TARGET_SINGLE_FLOAT ? SFmode : DFmode;
+
+         for (i = local_cum.num_fprs; i < MAX_ARGS_IN_REGISTERS; i += FP_INC)
+           {
+             rtx ptr = plus_constant (virtual_incoming_args_rtx, off);
+             emit_move_insn (gen_rtx_MEM (mode, ptr),
+                             gen_rtx_REG (mode, FP_ARG_FIRST + i));
+             off += UNITS_PER_FPVALUE;
+           }
+       }
+    }
+  return (gp_saved * UNITS_PER_WORD) + (fp_saved * UNITS_PER_FPREG);
 }
-\f
+
 /* Create the va_list data type.
    We keep 3 pointers, and two offsets.
    Two pointers are to the overflow area, which starts at the CFA.
@@ -4282,19 +4510,19 @@ function_arg_partial_nregs (cum, mode, type, named)
      These are downcounted as float or non-float arguments are used,
      and when they get to zero, the argument must be obtained from the
      overflow region.
-   If TARGET_SOFT_FLOAT or TARGET_SINGLE_FLOAT, then no FPR save area exists,
-     and a single pointer is enough.  It's started at the GPR save area,
-     and is advanced, period.
+   If !EABI_FLOAT_VARARGS_P, then no FPR save area exists, and a single
+     pointer is enough.  It's started at the GPR save area, and is
+     advanced, period.
    Note that the GPR save area is not constant size, due to optimization
      in the prologue.  Hence, we can't use a design with two pointers
      and two offsets, although we could have designed this with two pointers
-     and three offsets. */
+     and three offsets.  */
 
 
 tree
 mips_build_va_list ()
 {
-  if (mips_abi == ABI_EABI && !TARGET_SOFT_FLOAT && !TARGET_SINGLE_FLOAT)
+  if (EABI_FLOAT_VARARGS_P)
     {
       tree f_ovfl, f_gtop, f_ftop, f_goff, f_foff, record;
 
@@ -4342,37 +4570,24 @@ mips_va_start (stdarg_p, valist, nextarg)
      tree valist;
      rtx nextarg;
 {
-  int int_arg_words;
-  tree t;
-
-  /* Find out how many non-float named formals */
-  int_arg_words = current_function_args_info.arg_words;
+  const CUMULATIVE_ARGS *cum = &current_function_args_info;
 
   if (mips_abi == ABI_EABI)
     {
       int gpr_save_area_size;
-      /* Note UNITS_PER_WORD is 4 bytes or 8, depending on TARGET_64BIT. */
-      if (int_arg_words < 8 )
-       /* Adjust for the prologue's economy measure */
-       gpr_save_area_size = (8 - int_arg_words) * UNITS_PER_WORD;
-      else
-       gpr_save_area_size = 0;
 
-      if (!TARGET_SOFT_FLOAT && !TARGET_SINGLE_FLOAT)
+      gpr_save_area_size
+       = (MAX_ARGS_IN_REGISTERS - cum->num_gprs) * UNITS_PER_WORD;
+
+      if (EABI_FLOAT_VARARGS_P)
        {
          tree f_ovfl, f_gtop, f_ftop, f_goff, f_foff;
          tree ovfl, gtop, ftop, goff, foff;
-         tree gprv;
-         int float_formals, fpr_offset, size_excess, floats_passed_in_regs;
-         int fpr_save_offset;
-
-         float_formals = current_function_args_info.fp_arg_words;
-         /* If mips2, the number of formals is half the reported # of words */
-         if (!TARGET_64BIT)
-           float_formals /= 2;
-         floats_passed_in_regs = (TARGET_64BIT ? 8 : 4);
+         tree t;
+         int fpr_offset;
+         int fpr_save_area_size;
 
-         f_ovfl  = TYPE_FIELDS (va_list_type_node);
+         f_ovfl = TYPE_FIELDS (va_list_type_node);
          f_gtop = TREE_CHAIN (f_ovfl);
          f_ftop = TREE_CHAIN (f_gtop);
          f_goff = TREE_CHAIN (f_ftop);
@@ -4384,86 +4599,51 @@ mips_va_start (stdarg_p, valist, nextarg)
          goff = build (COMPONENT_REF, TREE_TYPE (f_goff), valist, f_goff);
          foff = build (COMPONENT_REF, TREE_TYPE (f_foff), valist, f_foff);
 
-         /* Emit code setting a pointer into the overflow (shared-stack) area.
-            If there were more than 8 non-float formals, or more than 8
-            float formals, then this pointer isn't to the base of the area.
-            In that case, it must point to where the first vararg is. */
-         size_excess = 0;
-         if (float_formals > floats_passed_in_regs)
-           size_excess += (float_formals-floats_passed_in_regs) * 8;
-         if (int_arg_words > 8)
-           size_excess += (int_arg_words-8) * UNITS_PER_WORD;
-
-         /* FIXME: for mips2, the above size_excess can be wrong.  Because the
-            overflow stack holds mixed size items, there can be alignments,
-            so that an 8 byte double following a 4 byte int will be on an
-            8 byte boundary.  This means that the above calculation should
-            take into account the exact sequence of floats and non-floats
-            which make up the excess.  That calculation should be rolled
-            into the code which sets the current_function_args_info struct.
-            The above then reduces to a fetch from that struct. */
-
-
+         /* Emit code to initialize OVFL, which points to the next varargs
+            stack argument.  CUM->STACK_WORDS gives the number of stack
+            words used by named arguments.  */
          t = make_tree (TREE_TYPE (ovfl), virtual_incoming_args_rtx);
-         if (size_excess)
+         if (cum->stack_words > 0)
            t = build (PLUS_EXPR, TREE_TYPE (ovfl), t,
-               build_int_2 (size_excess, 0));
+                      build_int_2 (cum->stack_words * UNITS_PER_WORD, 0));
          t = build (MODIFY_EXPR, TREE_TYPE (ovfl), ovfl, t);
          expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
 
-         /* Emit code setting a ptr to the base of the overflow area. */
+         /* Emit code to initialize GTOP, the top of the GPR save area.  */
          t = make_tree (TREE_TYPE (gtop), virtual_incoming_args_rtx);
          t = build (MODIFY_EXPR, TREE_TYPE (gtop), gtop, t);
          expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
 
-         /* Emit code setting a pointer to the GPR save area.
-            More precisely, a pointer to off-the-end of the FPR save area.
-            If mips4, this is gpr_save_area_size below the overflow area.
-            If mips2, also round down to an 8-byte boundary, since the FPR
-            save area is 8-byte aligned, and GPR is 4-byte-aligned.
-            Therefore there can be a 4-byte gap between the save areas. */
-         gprv = make_tree (TREE_TYPE (ftop), virtual_incoming_args_rtx);
-         fpr_save_offset = gpr_save_area_size;
-         if (!TARGET_64BIT)
-           {
-             if (fpr_save_offset & 7)
-               fpr_save_offset += 4;
-           }
-         if (fpr_save_offset)
-           gprv = build (PLUS_EXPR, TREE_TYPE (ftop), gprv,
-               build_int_2 (-fpr_save_offset,-1));
-         t = build (MODIFY_EXPR, TREE_TYPE (ftop), ftop, gprv);
+         /* Emit code to initialize FTOP, the top of the FPR save area.
+            This address is gpr_save_area_bytes below GTOP, rounded
+            down to the next fp-aligned boundary.  */
+         t = make_tree (TREE_TYPE (ftop), virtual_incoming_args_rtx);
+         fpr_offset = gpr_save_area_size + UNITS_PER_FPVALUE - 1;
+         fpr_offset &= ~(UNITS_PER_FPVALUE - 1);
+         if (fpr_offset)
+           t = build (PLUS_EXPR, TREE_TYPE (ftop), t,
+                      build_int_2 (-fpr_offset, -1));
+         t = build (MODIFY_EXPR, TREE_TYPE (ftop), ftop, t);
          expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
 
-         /* Emit code initting an offset to the size of the GPR save area */
+         /* Emit code to initialize GOFF, the offset from GTOP of the
+            next GPR argument.  */
          t = build (MODIFY_EXPR, TREE_TYPE (goff), goff,
-               build_int_2 (gpr_save_area_size,0));
+                    build_int_2 (gpr_save_area_size, 0));
          expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
 
-         /* Emit code initting an offset from ftop to the first float
-            vararg.  This varies in size, since any float
-            varargs are put in the FPR save area after the formals.
-            Note it's 8 bytes/formal regardless of TARGET_64BIT.
-            However, mips2 stores 4 GPRs, mips4 stores 8 GPRs.
-            If there are 8 or more float formals, init to zero.
-            (In fact, the formals aren't stored in the bottom of the
-            FPR save area: they are elsewhere, and the size of the FPR
-            save area is economized by the prologue.  But this code doesn't
-            care.  This design is unaffected by that fact.) */
-         if (float_formals >= floats_passed_in_regs)
-           fpr_offset = 0;
-         else
-           fpr_offset = (floats_passed_in_regs - float_formals) * 8;
+         /* Likewise emit code to initialize FOFF, the offset from FTOP
+            of the next FPR argument.  */
+         fpr_save_area_size
+           = (MAX_ARGS_IN_REGISTERS - cum->num_fprs) * UNITS_PER_FPREG;
          t = build (MODIFY_EXPR, TREE_TYPE (foff), foff,
-                    build_int_2 (fpr_offset,0));
+                    build_int_2 (fpr_save_area_size, 0));
          expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
        }
       else
        {
-         /* TARGET_SOFT_FLOAT or TARGET_SINGLE_FLOAT */
-
          /* Everything is in the GPR save area, or in the overflow
-            area which is contiguous with it. */
+            area which is contiguous with it.  */
 
          int offset = -gpr_save_area_size;
          if (gpr_save_area_size == 0)
@@ -4484,10 +4664,12 @@ mips_va_start (stdarg_p, valist, nextarg)
          /* ??? This had been conditional on
               _MIPS_SIM == _MIPS_SIM_ABI64 || _MIPS_SIM == _MIPS_SIM_NABI32
             and both iris5.h and iris6.h define _MIPS_SIM.  */
-         if (mips_abi == ABI_N32 || mips_abi == ABI_64)
-           ofs = (int_arg_words >= 8 ? -UNITS_PER_WORD : 0);
-         else if (mips_abi == ABI_MEABI)
-           ofs = (int_arg_words >= 8 ? -UNITS_PER_WORD : 0);
+         if (mips_abi == ABI_N32
+             || mips_abi == ABI_64
+             || mips_abi == ABI_MEABI)
+           ofs = (cum->num_gprs < MAX_ARGS_IN_REGISTERS
+                  ? 0
+                  : -UNITS_PER_WORD);
          else
            ofs = -UNITS_PER_WORD;
        }
@@ -4512,13 +4694,12 @@ mips_va_arg (valist, type)
 
   if (mips_abi == ABI_EABI)
     {
-      int indirect;
-      rtx r, lab_over = NULL_RTX, lab_false;
-      tree f_ovfl, f_gtop, f_ftop, f_goff, f_foff;
-      tree ovfl, gtop, ftop, goff, foff;
+      bool indirect;
+      rtx r;
 
       indirect
        = function_arg_pass_by_reference (NULL, TYPE_MODE (type), type, 0);
+
       if (indirect)
        {
          size = POINTER_SIZE / BITS_PER_UNIT;
@@ -4527,191 +4708,186 @@ mips_va_arg (valist, type)
 
       addr_rtx = gen_reg_rtx (Pmode);
 
-      if (TARGET_SOFT_FLOAT || TARGET_SINGLE_FLOAT)
+      if (!EABI_FLOAT_VARARGS_P)
        {
-         /* Case of all args in a merged stack. No need to check bounds,
-            just advance valist along the stack. */
+         /* Case of all args in a merged stack.  No need to check bounds,
+            just advance valist along the stack.  */
 
          tree gpr = valist;
-         if (! indirect
-             && ! TARGET_64BIT
+         if (!indirect
+             && !TARGET_64BIT
              && TYPE_ALIGN (type) > (unsigned) BITS_PER_WORD)
            {
+             /* Align the pointer using: ap = (ap + align - 1) & -align,
+                where align is 2 * UNITS_PER_WORD.  */
              t = build (PLUS_EXPR, TREE_TYPE (gpr), gpr,
-                    build_int_2 (2*UNITS_PER_WORD - 1, 0));
+                        build_int_2 (2 * UNITS_PER_WORD - 1, 0));
              t = build (BIT_AND_EXPR, TREE_TYPE (t), t,
-                    build_int_2 (-2*UNITS_PER_WORD, -1));
+                        build_int_2 (-2 * UNITS_PER_WORD, -1));
              t = build (MODIFY_EXPR, TREE_TYPE (gpr), gpr, t);
-             expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
-           }
-
-         t = build (POSTINCREMENT_EXPR, TREE_TYPE (gpr), gpr,
-               size_int (rsize));
-         r = expand_expr (t, addr_rtx, Pmode, EXPAND_NORMAL);
-         if (r != addr_rtx)
-           emit_move_insn (addr_rtx, r);
-
-         /* flush the POSTINCREMENT */
-         emit_queue();
-
-         if (indirect)
-           {
-             r = gen_rtx_MEM (Pmode, addr_rtx);
-             set_mem_alias_set (r, get_varargs_alias_set ());
-             emit_move_insn (addr_rtx, r);
-           }
-         else
-           {
-             if (BYTES_BIG_ENDIAN && rsize != size)
-             addr_rtx = plus_constant (addr_rtx, rsize - size);
+             expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
            }
-         return addr_rtx;
-       }
 
-      /* Not a simple merged stack.  Need ptrs and indexes left by va_start. */
+         /* Emit code to set addr_rtx to the valist, and postincrement
+            the valist by the size of the argument, rounded up to the
+            next word.  */
+         t = build (POSTINCREMENT_EXPR, TREE_TYPE (gpr), gpr,
+                    size_int (rsize));
+         r = expand_expr (t, addr_rtx, Pmode, EXPAND_NORMAL);
+         if (r != addr_rtx)
+           emit_move_insn (addr_rtx, r);
+
+         /* Flush the POSTINCREMENT.  */
+         emit_queue();
+       }
+      else
+       {
+         /* Not a simple merged stack.  */
 
-      f_ovfl  = TYPE_FIELDS (va_list_type_node);
-      f_gtop = TREE_CHAIN (f_ovfl);
-      f_ftop = TREE_CHAIN (f_gtop);
-      f_goff = TREE_CHAIN (f_ftop);
-      f_foff = TREE_CHAIN (f_goff);
+         tree f_ovfl, f_gtop, f_ftop, f_goff, f_foff;
+         tree ovfl, top, off;
+         rtx lab_over = NULL_RTX, lab_false;
+         HOST_WIDE_INT osize;
 
-      ovfl = build (COMPONENT_REF, TREE_TYPE (f_ovfl), valist, f_ovfl);
-      gtop = build (COMPONENT_REF, TREE_TYPE (f_gtop), valist, f_gtop);
-      ftop = build (COMPONENT_REF, TREE_TYPE (f_ftop), valist, f_ftop);
-      goff = build (COMPONENT_REF, TREE_TYPE (f_goff), valist, f_goff);
-      foff = build (COMPONENT_REF, TREE_TYPE (f_foff), valist, f_foff);
+         f_ovfl = TYPE_FIELDS (va_list_type_node);
+         f_gtop = TREE_CHAIN (f_ovfl);
+         f_ftop = TREE_CHAIN (f_gtop);
+         f_goff = TREE_CHAIN (f_ftop);
+         f_foff = TREE_CHAIN (f_goff);
 
-      lab_false = gen_label_rtx ();
-      lab_over = gen_label_rtx ();
+         /* We maintain separate pointers and offsets for floating-point
+            and integer arguments, but we need similar code in both cases.
+            Let:
+
+                TOP be the top of the register save area;
+                OFF be the offset from TOP of the next register;
+                ADDR_RTX be the address of the argument; and
+                RSIZE be the number of bytes used to store the argument
+                  when it's in the register save area
+                OSIZE be the number of bytes used to store it when it's
+                  in the stack overflow area
+                PADDING be (BYTES_BIG_ENDIAN ? OSIZE - RSIZE : 0)
+
+            The code we want is:
+
+                 1: off &= -rsize;       // round down
+                 2: if (off != 0)
+                 3:   {
+                 4:     addr_rtx = top - off;
+                 5:     off -= rsize;
+                 6:   }
+                 7: else
+                 8:   {
+                 9:     ovfl += ((intptr_t) ovfl + osize - 1) & -osize;
+                10:     addr_rtx = ovfl + PADDING;
+                11:     ovfl += osize;
+                14:   }
+
+            [1] and [9] can sometimes be optimized away.  */
+
+         lab_false = gen_label_rtx ();
+         lab_over = gen_label_rtx ();
 
-      if (TREE_CODE (type) == REAL_TYPE)
-        {
+         ovfl = build (COMPONENT_REF, TREE_TYPE (f_ovfl), valist, f_ovfl);
 
-         /* Emit code to branch if foff == 0. */
-          r = expand_expr (foff, NULL_RTX, TYPE_MODE (TREE_TYPE (foff)),
-               EXPAND_NORMAL);
-          emit_cmp_and_jump_insns (r, const0_rtx, EQ,
-               const1_rtx, GET_MODE (r), 1, 1, lab_false);
+         if (TREE_CODE (type) == REAL_TYPE)
+           {
+             top = build (COMPONENT_REF, TREE_TYPE (f_ftop), valist, f_ftop);
+             off = build (COMPONENT_REF, TREE_TYPE (f_foff), valist, f_foff);
 
-          /* Emit code for addr_rtx = ftop - foff */
-          t = build (MINUS_EXPR, TREE_TYPE (ftop), ftop, foff );
-          r = expand_expr (t, addr_rtx, Pmode, EXPAND_NORMAL);
-          if (r != addr_rtx)
-           emit_move_insn (addr_rtx, r);
+             /* When floating-point registers are saved to the stack,
+                each one will take up UNITS_PER_FPVALUE bytes, regardless
+                of the float's precision.  */
+             rsize = UNITS_PER_FPVALUE;
+           }
+         else
+           {
+             top = build (COMPONENT_REF, TREE_TYPE (f_gtop), valist, f_gtop);
+             off = build (COMPONENT_REF, TREE_TYPE (f_goff), valist, f_goff);
+             if (rsize > UNITS_PER_WORD)
+               {
+                 /* [1] Emit code for: off &= -rsize.  */
+                 t = build (BIT_AND_EXPR, TREE_TYPE (off), off,
+                            build_int_2 (-rsize, -1));
+                 t = build (MODIFY_EXPR, TREE_TYPE (off), off, t);
+                 expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
+               }
+           }
+         /* Every overflow argument must take up at least UNITS_PER_WORD
+            bytes (= PARM_BOUNDARY bits).  RSIZE can sometimes be smaller
+            than that, such as in the combination -mgp64 -msingle-float
+            -fshort-double.  Doubles passed in registers will then take
+            up UNITS_PER_FPVALUE bytes, but those passed on the stack
+            take up UNITS_PER_WORD bytes.  */
+         osize = MAX (rsize, UNITS_PER_WORD);
+
+         /* [2] Emit code to branch if off == 0.  */
+         r = expand_expr (off, NULL_RTX, TYPE_MODE (TREE_TYPE (off)),
+                          EXPAND_NORMAL);
+         emit_cmp_and_jump_insns (r, const0_rtx, EQ, const1_rtx, GET_MODE (r),
+                                  1, lab_false);
+
+         /* [4] Emit code for: addr_rtx = top - off.  */
+         t = build (MINUS_EXPR, TREE_TYPE (top), top, off);
+         r = expand_expr (t, addr_rtx, Pmode, EXPAND_NORMAL);
+         if (r != addr_rtx)
+           emit_move_insn (addr_rtx, r);
 
-          /* Emit code for foff-=8.
-            Advances the offset up FPR save area by one double */
-          t = build (MINUS_EXPR, TREE_TYPE (foff), foff, build_int_2 (8, 0));
-          t = build (MODIFY_EXPR, TREE_TYPE (foff), foff, t);
-          expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
+         /* [5] Emit code for: off -= rsize.  */
+         t = build (MINUS_EXPR, TREE_TYPE (off), off, build_int_2 (rsize, 0));
+         t = build (MODIFY_EXPR, TREE_TYPE (off), off, t);
+         expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
 
-          emit_queue();
-          emit_jump (lab_over);
-          emit_barrier ();
-          emit_label (lab_false);
+         /* [7] Emit code to jump over the else clause, then the label
+            that starts it.  */
+         emit_queue();
+         emit_jump (lab_over);
+         emit_barrier ();
+         emit_label (lab_false);
 
-         if (!TARGET_64BIT)
+         if (osize > UNITS_PER_WORD)
            {
-             /* For mips2, the overflow area contains mixed size items.
-                If a 4-byte int is followed by an 8-byte float, then
-                natural alignment causes a 4 byte gap.
-                So, dynamically adjust ovfl up to a multiple of 8. */
-             t = build (BIT_AND_EXPR, TREE_TYPE (ovfl), ovfl,
-                       build_int_2 (7, 0));
-             t = build (PLUS_EXPR, TREE_TYPE (ovfl), ovfl, t);
+             /* [9] Emit: ovfl += ((intptr_t) ovfl + osize - 1) & -osize.  */
+             t = build (PLUS_EXPR, TREE_TYPE (ovfl), ovfl,
+                        build_int_2 (osize - 1, 0));
+             t = build (BIT_AND_EXPR, TREE_TYPE (ovfl), t,
+                        build_int_2 (-osize, -1));
              t = build (MODIFY_EXPR, TREE_TYPE (ovfl), ovfl, t);
              expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
            }
 
-          /* Emit code for addr_rtx = the ovfl pointer into overflow area.
-            Regardless of mips2, postincrement the ovfl pointer by 8. */
-          t = build (POSTINCREMENT_EXPR, TREE_TYPE(ovfl), ovfl,
-               size_int (8));
-          r = expand_expr (t, addr_rtx, Pmode, EXPAND_NORMAL);
-          if (r != addr_rtx)
-           emit_move_insn (addr_rtx, r);
-
-          emit_queue();
-          emit_label (lab_over);
-                 return addr_rtx;
-        }
-      else
-        {
-          /* not REAL_TYPE */
-         int step_size;
-
-         if (! TARGET_64BIT
-             && TREE_CODE (type) == INTEGER_TYPE
-             && TYPE_PRECISION (type) == 64)
-           {
-             /* In mips2, int takes 32 bits of the GPR save area, but
-                longlong takes an aligned 64 bits.  So, emit code
-                to zero the low order bits of goff, thus aligning
-                the later calculation of (gtop-goff) upwards. */
-              t = build (BIT_AND_EXPR, TREE_TYPE (goff), goff,
-                       build_int_2 (-8, -1));
-              t = build (MODIFY_EXPR, TREE_TYPE (goff), goff, t);
-              expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
-           }
+         /* [10, 11].  Emit code to store ovfl in addr_rtx, then
+            post-increment ovfl by osize.  On big-endian machines,
+            the argument has OSIZE - RSIZE bytes of leading padding.  */
+         t = build (POSTINCREMENT_EXPR, TREE_TYPE (ovfl), ovfl,
+                    size_int (osize));
+         if (BYTES_BIG_ENDIAN && osize > rsize)
+           t = build (PLUS_EXPR, TREE_TYPE (t), t,
+                      build_int_2 (osize - rsize, 0));
+         r = expand_expr (t, addr_rtx, Pmode, EXPAND_NORMAL);
+         if (r != addr_rtx)
+           emit_move_insn (addr_rtx, r);
 
-         /* Emit code to branch if goff == 0. */
-          r = expand_expr (goff, NULL_RTX, TYPE_MODE (TREE_TYPE (goff)),
-               EXPAND_NORMAL);
-          emit_cmp_and_jump_insns (r, const0_rtx, EQ,
-               const1_rtx, GET_MODE (r), 1, 1, lab_false);
-
-          /* Emit code for addr_rtx = gtop - goff. */
-          t = build (MINUS_EXPR, TREE_TYPE (gtop), gtop, goff);
-          r = expand_expr (t, addr_rtx, Pmode, EXPAND_NORMAL);
-          if (r != addr_rtx)
-           emit_move_insn (addr_rtx, r);
-
-         /* Note that mips2 int is 32 bit, but mips2 longlong is 64. */
-         if (! TARGET_64BIT && TYPE_PRECISION (type) == 64)
-           step_size = 8;
-         else
-           step_size = UNITS_PER_WORD;
-
-          /* Emit code for goff = goff - step_size.
-            Advances the offset up GPR save area over the item. */
-          t = build (MINUS_EXPR, TREE_TYPE (goff), goff,
-               build_int_2 (step_size, 0));
-          t = build (MODIFY_EXPR, TREE_TYPE (goff), goff, t);
-          expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
-
-          emit_queue();
-          emit_jump (lab_over);
-          emit_barrier ();
-          emit_label (lab_false);
-
-          /* Emit code for addr_rtx -> overflow area, postinc by step_size */
-          t = build (POSTINCREMENT_EXPR, TREE_TYPE(ovfl), ovfl,
-               size_int (step_size));
-          r = expand_expr (t, addr_rtx, Pmode, EXPAND_NORMAL);
-          if (r != addr_rtx)
-           emit_move_insn (addr_rtx, r);
-
-          emit_queue();
-          emit_label (lab_over);
-
-          if (indirect)
-           {
-                     r = gen_rtx_MEM (Pmode, addr_rtx);
-             set_mem_alias_set (r, get_varargs_alias_set ());
-             emit_move_insn (addr_rtx, r);
-           }
-         else
-           {
-             if (BYTES_BIG_ENDIAN && rsize != size)
-             addr_rtx = plus_constant (addr_rtx, rsize - size);
-           }
-         return addr_rtx;
+         emit_queue();
+         emit_label (lab_over);
+       }
+      if (indirect)
+       {
+         addr_rtx = force_reg (Pmode, addr_rtx);
+         r = gen_rtx_MEM (Pmode, addr_rtx);
+         set_mem_alias_set (r, get_varargs_alias_set ());
+         emit_move_insn (addr_rtx, r);
+       }
+      else
+       {
+         if (BYTES_BIG_ENDIAN && rsize != size)
+           addr_rtx = plus_constant (addr_rtx, rsize - size);
        }
+      return addr_rtx;
     }
   else
     {
-      /* Not EABI. */
+      /* Not EABI.  */
       int align;
 
       /* ??? The original va-mips.h did always align, despite the fact
@@ -4769,7 +4945,7 @@ override_options ()
   /* If both single-float and soft-float are set, then clear the one that
      was set by TARGET_DEFAULT, leaving the one that was set by the
      user.  We assume here that the specs prevent both being set by the
-     user. */
+     user.  */
 #ifdef TARGET_DEFAULT
   if (TARGET_SINGLE_FLOAT && TARGET_SOFT_FLOAT)
     target_flags &= ~((TARGET_DEFAULT) & (MASK_SOFT_FLOAT | MASK_SINGLE_FLOAT));
@@ -4779,6 +4955,10 @@ override_options ()
   if (mips_isa_string == 0)
     mips_isa = MIPS_ISA_DEFAULT;
 
+  else if (mips_isa_string != 0
+          && mips_arch_string != 0)
+      warning ("The -march option is incompatible to -mipsN and therefore ignored.");
+
   else if (ISDIGIT (*mips_isa_string))
     {
       mips_isa = atoi (mips_isa_string);
@@ -4815,7 +4995,7 @@ override_options ()
     }
 
 #ifdef MIPS_ABI_DEFAULT
-  /* Get the ABI to use. */
+  /* Get the ABI to use.  */
   if (mips_abi_string == (char *) 0)
     mips_abi = MIPS_ABI_DEFAULT;
   else if (! strcmp (mips_abi_string, "32"))
@@ -4845,7 +5025,7 @@ override_options ()
        {
          if (! ISA_HAS_64BIT_REGS)
            mips_abi = ABI_32;
-         else
+         else if (mips_abi != ABI_N32)
            mips_abi = ABI_64;
        }
     }
@@ -4884,13 +5064,9 @@ override_options ()
          || mips_abi == ABI_64))
     target_flags |= MASK_LONG64;
 
-  /* ??? This doesn't work yet, so don't let people try to use it.  */
-  if (mips_abi == ABI_32)
-    error ("The -mabi=32 support does not work yet.");
-
 #else
   if (mips_abi_string)
-    error ("This target does not support the -mabi switch.");
+    error ("this target does not support the -mabi switch");
 #endif
 
 #ifdef MIPS_CPU_STRING_DEFAULT
@@ -4911,7 +5087,7 @@ override_options ()
       mips_cpu = mips_parse_cpu (mips_cpu_string);
       if (mips_cpu == PROCESSOR_DEFAULT)
        {
-         error ("bad value (%s) for -mcpu= switch", mips_arch_string);
+         error ("bad value (%s) for -mcpu= switch", mips_cpu_string);
          mips_cpu_string = "default";
        }
       mips_arch = mips_cpu;
@@ -4985,12 +5161,12 @@ override_options ()
          mips_tune = PROCESSOR_R8000;
          break;
        case 32:
-         mips_arch_string = "4kc";
-         mips_arch = PROCESSOR_R4KC;
+         mips_tune_string = "4kc";
+         mips_tune = PROCESSOR_R4KC;
          break;
        case 64:
-         mips_arch_string = "5kc";
-         mips_arch = PROCESSOR_R5KC;
+         mips_tune_string = "5kc";
+         mips_tune = PROCESSOR_R5KC;
          break;
        }
 
@@ -5005,27 +5181,6 @@ override_options ()
        }
     }
 
-  /* Handle processor configuration based on architecture.  */
-  if (TARGET_MIPS4100
-      || TARGET_MIPS3900
-      || TARGET_MIPS4KC
-      || TARGET_MIPS5KC)
-    target_flags |= MASK_SOFT_FLOAT;
-
-
-  if ((mips_arch == PROCESSOR_R3000 && (mips_isa != 1))
-      || (mips_arch == PROCESSOR_R4KC && mips_isa != 32)
-      || ((mips_arch == PROCESSOR_R5KC
-          || mips_arch == PROCESSOR_R20KC) && mips_isa != 64)
-      || (mips_arch == PROCESSOR_R6000 && mips_isa != 1 && mips_isa != 2)
-      || ((mips_arch == PROCESSOR_R4000
-          || mips_arch == PROCESSOR_R4100
-          || mips_arch == PROCESSOR_R4300
-          || mips_arch == PROCESSOR_R4600
-          || mips_arch == PROCESSOR_R4650)
-         && mips_isa != 1 && mips_isa != 2 && mips_isa != 3))
-    error ("-march=%s does not support -mips%d", mips_arch_string, mips_isa);
-
   /* make sure sizes of ints/longs/etc. are ok */
   if (! ISA_HAS_64BIT_REGS)
     {
@@ -5128,7 +5283,7 @@ override_options ()
   if (mips_entry_string != NULL)
     {
       if (*mips_entry_string != '\0')
-       error ("Invalid option `entry%s'", mips_entry_string);
+       error ("invalid option `entry%s'", mips_entry_string);
 
       if (! TARGET_MIPS16)
        warning ("-mentry is only meaningful with -mips-16");
@@ -5182,6 +5337,9 @@ override_options ()
   mips_char_to_class['b'] = ALL_REGS;
   mips_char_to_class['y'] = GR_REGS;
   mips_char_to_class['z'] = ST_REGS;
+  mips_char_to_class['B'] = COP0_REGS;
+  mips_char_to_class['C'] = COP2_REGS;
+  mips_char_to_class['D'] = COP3_REGS;
 
   /* Set up array to map GCC register number to debug register number.
      Ignore the special purpose register numbers.  */
@@ -5228,7 +5386,7 @@ override_options ()
            temp = ((regno & 1) == 0 || size <= UNITS_PER_WORD);
 
          else if (FP_REG_P (regno))
-           temp = ((TARGET_FLOAT64 || ((regno & 1) == 0)
+           temp = (((regno % FP_INC) == 0
                      /* I think this change is OK regardless of abi, but
                         I'm being cautions untill I can test this more.
                         HARD_REGNO_MODE_OK is about whether or not you
@@ -5239,7 +5397,7 @@ override_options ()
                    && (class == MODE_FLOAT
                        || class == MODE_COMPLEX_FLOAT
                        || (TARGET_DEBUG_H_MODE && class == MODE_INT))
-                   && (! TARGET_SINGLE_FLOAT || size <= 4));
+                   && size <= UNITS_PER_FPVALUE);
 
          else if (MD_REG_P (regno))
            temp = (class == MODE_INT
@@ -5247,6 +5405,8 @@ override_options ()
                        || (regno == MD_REG_FIRST
                            && size == 2 * UNITS_PER_WORD)));
 
+         else if (ALL_COP_REG_P (regno))
+           temp = (class == MODE_INT && size <= UNITS_PER_WORD);
          else
            temp = 0;
 
@@ -5271,6 +5431,42 @@ override_options ()
 
   /* Register global variables with the garbage collector.  */
   mips_add_gc_roots ();
+
+  /* Functions to allocate, mark and deallocate machine-dependent
+     function status.  */
+  init_machine_status = &mips_init_machine_status;
+  free_machine_status = &mips_free_machine_status;
+  mark_machine_status = &mips_mark_machine_status;
+}
+
+/* Allocate a chunk of memory for per-function machine-dependent data.  */
+static void
+mips_init_machine_status (fn)
+     struct function *fn;
+{
+  fn->machine = ((struct machine_function *)
+                xcalloc (1, sizeof (struct machine_function)));
+}
+
+/* Release the chunk of memory for per-function machine-dependent data.  */
+static void
+mips_free_machine_status (fn)
+     struct function *fn;
+{
+  free (fn->machine);
+  fn->machine = NULL;
+}
+
+/* Mark per-function machine-dependent data.  */
+static void
+mips_mark_machine_status (fn)
+     struct function *fn;
+{
+  if (fn->machine)
+    {
+      ggc_mark_rtx (fn->machine->embedded_pic_fnaddr_rtx);
+      ggc_mark_rtx (fn->machine->mips16_gp_pseudo_rtx);
+    }
 }
 
 /* On the mips16, we want to allocate $24 (T_REG) before other
@@ -5333,7 +5529,7 @@ mips_debugger_offset (addr, offset)
   /* 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.");
+    abort_with_insn (addr, "mips_debugger_offset called with non stack/frame/arg pointer");
 #endif
 
   return offset;
@@ -5525,7 +5721,7 @@ print_operand (file, op, letter)
        break;
 
        default:
-         error ("PRINT_OPERAND: Unknown punctuation '%c'", letter);
+         error ("PRINT_OPERAND: unknown punctuation '%c'", letter);
          break;
        }
 
@@ -5717,7 +5913,7 @@ print_operand_address (file, addr)
       {
       case REG:
        if (! TARGET_MIPS16 && REGNO (addr) == ARG_POINTER_REGNUM)
-         abort_with_insn (addr, "Arg pointer not eliminated.");
+         abort_with_insn (addr, "arg pointer not eliminated");
 
        fprintf (file, "0(%s)", reg_names [REGNO (addr)]);
        break;
@@ -5728,11 +5924,11 @@ print_operand_address (file, addr)
          register rtx arg1 = XEXP (addr, 1);
 
          if (! mips_split_addresses)
-           abort_with_insn (addr, "PRINT_OPERAND_ADDRESS, Spurious LO_SUM.");
+           abort_with_insn (addr, "PRINT_OPERAND_ADDRESS, Spurious LO_SUM");
 
          if (GET_CODE (arg0) != REG)
            abort_with_insn (addr,
-                            "PRINT_OPERAND_ADDRESS, LO_SUM with #1 not REG.");
+                            "PRINT_OPERAND_ADDRESS, LO_SUM with #1 not REG");
 
          fprintf (file, "%%lo(");
          print_operand_address (file, arg1);
@@ -5769,7 +5965,7 @@ print_operand_address (file, addr)
            abort_with_insn (addr, "PRINT_OPERAND_ADDRESS, invalid insn #2");
 
          if (REGNO (reg) == ARG_POINTER_REGNUM)
-           abort_with_insn (addr, "Arg pointer not eliminated.");
+           abort_with_insn (addr, "arg pointer not eliminated");
 
          if (TARGET_MIPS16
              && GET_CODE (offset) == CONST
@@ -5797,7 +5993,29 @@ print_operand_address (file, addr)
        break;
     }
 }
+\f
+/* Target hook for assembling integer objects.  It appears that the Irix
+   6 assembler can't handle 64-bit decimal integers, so avoid printing
+   such an integer here.  */
 
+static bool
+mips_assemble_integer (x, size, aligned_p)
+     rtx x;
+     unsigned int size;
+     int aligned_p;
+{
+  if ((TARGET_64BIT || TARGET_GAS) && size == 8 && aligned_p)
+    {
+      fputs ("\t.dword\t", asm_out_file);
+      if (HOST_BITS_PER_WIDE_INT < 64 || GET_CODE (x) != CONST_INT)
+       output_addr_const (asm_out_file, x);
+      else
+       print_operand (asm_out_file, x, 'X');
+      fputc ('\n', asm_out_file);
+      return true;
+    }
+  return default_assemble_integer (x, size, aligned_p);
+}
 \f
 /* If optimizing for the global pointer, keep track of all of the externs, so
    that at the end of the file, we can emit the appropriate .extern
@@ -5822,6 +6040,7 @@ mips_output_external (file, decl, name)
 
   if (TARGET_GP_OPT
       && TREE_CODE (decl) != FUNCTION_DECL
+      && !DECL_COMDAT (decl)
       && (len = int_size_in_bytes (TREE_TYPE (decl))) > 0
       && ((section_name = DECL_SECTION_NAME (decl)) == NULL
          || strcmp (TREE_STRING_POINTER (section_name), ".sbss") == 0
@@ -5857,7 +6076,7 @@ mips_output_external (file, decl, name)
 #ifdef ASM_OUTPUT_UNDEF_FUNCTION
 int
 mips_output_external_libcall (file, name)
-     FILE *file;
+     FILE *file ATTRIBUTE_UNUSED;
      const char *name;
 {
   register struct extern_list *p;
@@ -6095,8 +6314,6 @@ void
 mips_asm_file_start (stream)
      FILE *stream;
 {
-  const char * abi_string = NULL;
-
   ASM_OUTPUT_SOURCE_FILENAME (stream, main_input_filename);
 
   /* Versions of the MIPS assembler before 2.20 generate errors if a branch
@@ -6116,6 +6333,7 @@ mips_asm_file_start (stream)
         bits.  GDB needs this information in order to be able to correctly
         debug these binaries. See the function mips_gdbarch_init() in
         gdb/mips-tdep.c.  */
+      const char * abi_string = NULL;
 
       switch (mips_abi)
        {
@@ -6262,43 +6480,6 @@ mips_declare_object (stream, name, init_string, final_string, size)
     }
 }
 \f
-/* Output a double precision value to the assembler.  If both the
-   host and target are IEEE, emit the values in hex.  */
-
-void
-mips_output_double (stream, value)
-     FILE *stream;
-     REAL_VALUE_TYPE value;
-{
-#ifdef REAL_VALUE_TO_TARGET_DOUBLE
-  long value_long[2];
-  REAL_VALUE_TO_TARGET_DOUBLE (value, value_long);
-
-  fprintf (stream, "\t.word\t0x%08lx\t\t# %.20g\n\t.word\t0x%08lx\n",
-          value_long[0], value, value_long[1]);
-#else
-  fprintf (stream, "\t.double\t%.20g\n", value);
-#endif
-}
-
-/* Output a single precision value to the assembler.  If both the
-   host and target are IEEE, emit the values in hex.  */
-
-void
-mips_output_float (stream, value)
-     FILE *stream;
-     REAL_VALUE_TYPE value;
-{
-#ifdef REAL_VALUE_TO_TARGET_SINGLE
-  long value_long;
-  REAL_VALUE_TO_TARGET_SINGLE (value, value_long);
-
-  fprintf (stream, "\t.word\t0x%08lx\t\t# %.12g (float)\n", value_long, value);
-#else
-  fprintf (stream, "\t.float\t%.12g\n", value);
-#endif
-}
-\f
 /* Return the bytes needed to compute the frame pointer from the current
    stack pointer.
 
@@ -6366,8 +6547,7 @@ compute_frame_size (size)
   HOST_WIDE_INT 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 */
+  tree return_type;
 
   gp_reg_size = 0;
   fp_reg_size = 0;
@@ -6386,6 +6566,7 @@ compute_frame_size (size)
     args_size = 4 * UNITS_PER_WORD;
 
   total_size = var_size + args_size + extra_size;
+  return_type = DECL_RESULT (current_function_decl);
 
   /* Calculate space needed for gp registers.  */
   for (regno = GP_REG_FIRST; regno <= GP_REG_LAST; regno++)
@@ -6404,12 +6585,9 @@ compute_frame_size (size)
              && regno == GP_REG_FIRST + 31
              && mips16_hard_float
              && ! mips_entry
-             && ! aggregate_value_p (DECL_RESULT (current_function_decl))
-             && (GET_MODE_CLASS (DECL_MODE (DECL_RESULT (current_function_decl)))
-                 == MODE_FLOAT)
-             && (! TARGET_SINGLE_FLOAT
-                 || (GET_MODE_SIZE (DECL_MODE (DECL_RESULT (current_function_decl)))
-                     <= 4))))
+             && ! aggregate_value_p (return_type)
+             && GET_MODE_CLASS (DECL_MODE (return_type)) == MODE_FLOAT
+             && GET_MODE_SIZE (DECL_MODE (return_type)) <= UNITS_PER_FPVALUE))
        {
          gp_reg_size += GET_MODE_SIZE (gpr_mode);
          mask |= 1L << (regno - GP_REG_FIRST);
@@ -6440,28 +6618,16 @@ compute_frame_size (size)
        }
     }
 
-  /* Calculate space needed for fp registers.  */
-  if (TARGET_FLOAT64 || TARGET_SINGLE_FLOAT)
-    {
-      fp_inc = 1;
-      fp_bits = 1;
-    }
-  else
-    {
-      fp_inc = 2;
-      fp_bits = 3;
-    }
-
   /* This loop must iterate over the same space as its companion in
-     save_restore_regs.  */
-  for (regno = (FP_REG_LAST - fp_inc + 1);
+     save_restore_insns.  */
+  for (regno = (FP_REG_LAST - FP_INC + 1);
        regno >= FP_REG_FIRST;
-       regno -= fp_inc)
+       regno -= FP_INC)
     {
       if (regs_ever_live[regno] && !call_used_regs[regno])
        {
-         fp_reg_size += fp_inc * UNITS_PER_FPREG;
-         fmask |= fp_bits << (regno - FP_REG_FIRST);
+         fp_reg_size += FP_INC * UNITS_PER_FPREG;
+         fmask |= ((1 << FP_INC) - 1) << (regno - FP_REG_FIRST);
        }
     }
 
@@ -6475,7 +6641,7 @@ compute_frame_size (size)
      call to mcount.  */
   if (total_size == extra_size
       && (mips_abi == ABI_32 || mips_abi == ABI_O64 || mips_abi == ABI_EABI)
-      && ! profile_flag)
+      && ! current_function_profile)
     total_size = extra_size = 0;
   else if (TARGET_ABICALLS)
     {
@@ -6507,7 +6673,7 @@ compute_frame_size (size)
   current_frame_info.fmask = fmask;
   current_frame_info.initialized = reload_completed;
   current_frame_info.num_gp = gp_reg_size / UNITS_PER_WORD;
-  current_frame_info.num_fp = fp_reg_size / (fp_inc * UNITS_PER_FPREG);
+  current_frame_info.num_fp = fp_reg_size / (FP_INC * UNITS_PER_FPREG);
 
   if (mask)
     {
@@ -6534,7 +6700,7 @@ compute_frame_size (size)
     {
       unsigned long offset = (args_size + extra_size + var_size
                              + gp_reg_rounded + fp_reg_size
-                             - fp_inc * UNITS_PER_FPREG);
+                             - FP_INC * UNITS_PER_FPREG);
       current_frame_info.fp_sp_offset = offset;
       current_frame_info.fp_save_offset = offset - total_size;
     }
@@ -6557,40 +6723,23 @@ compute_frame_size (size)
 #define BITSET_P(VALUE,BIT) (((VALUE) & (1L << (BIT))) != 0)
 
 /* Emit instructions to load the value (SP + OFFSET) into MIPS_TEMP2_REGNUM
-   and return an rtl expression for the register.  Write the assembly
-   instructions directly to FILE if it is not null, otherwise emit them as
-   rtl.
+   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 (offset, file)
+mips_add_large_offset_to_sp (offset)
      HOST_WIDE_INT offset;
-     FILE *file;
 {
   rtx reg = gen_rtx_REG (Pmode, MIPS_TEMP2_REGNUM);
-  if (file == 0)
-    {
-      rtx offset_rtx = GEN_INT (offset);
+  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));
-    }
+  emit_move_insn (reg, offset_rtx);
+  if (Pmode == DImode)
+    emit_insn (gen_adddi3 (reg, reg, stack_pointer_rtx));
   else
-    {
-      fprintf (file, "\tli\t%s,0x%.08lx\t# ",
-              reg_names[MIPS_TEMP2_REGNUM], (long) offset);
-      fprintf (file, HOST_WIDE_INT_PRINT_DEC, offset);
-      fprintf (file, "\n\t%s\t%s,%s,%s\n",
-              Pmode == DImode ? "daddu" : "addu",
-              reg_names[MIPS_TEMP2_REGNUM],
-              reg_names[MIPS_TEMP2_REGNUM],
-              reg_names[STACK_POINTER_REGNUM]);
-    }
+    emit_insn (gen_addsi3 (reg, reg, stack_pointer_rtx));
   return reg;
 }
 
@@ -6656,14 +6805,14 @@ mips_emit_frame_related_store (mem, reg, offset)
 }
 
 static void
-save_restore_insns (store_p, large_reg, large_offset, file)
+save_restore_insns (store_p, large_reg, large_offset)
      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 instead of making RTL */
 {
   long mask = current_frame_info.mask;
   long fmask = current_frame_info.fmask;
+  long real_mask = mask;
   int regno;
   rtx base_reg_rtx;
   HOST_WIDE_INT base_offset;
@@ -6676,6 +6825,12 @@ save_restore_insns (store_p, large_reg, large_offset, file)
       && ! BITSET_P (mask, HARD_FRAME_POINTER_REGNUM - GP_REG_FIRST))
     abort ();
 
+  /* Do not restore GP under certain conditions.  */
+  if (! store_p
+      && TARGET_ABICALLS
+      && (mips_abi == ABI_32 || mips_abi == ABI_O64))
+    mask &= ~(1L << (PIC_OFFSET_TABLE_REGNUM - GP_REG_FIRST));
+
   if (mask == 0 && fmask == 0)
     return;
 
@@ -6700,7 +6855,7 @@ save_restore_insns (store_p, large_reg, large_offset, file)
 
       if (gp_offset < 0 || end_offset < 0)
        internal_error
-         ("gp_offset (%ld) or end_offset (%ld) is less than zero.",
+         ("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
@@ -6717,26 +6872,17 @@ save_restore_insns (store_p, large_reg, large_offset, file)
        {
          base_reg_rtx = gen_rtx_REG (Pmode, MIPS_TEMP2_REGNUM);
          base_offset = large_offset;
-         if (file == 0)
-           {
-             if (Pmode == DImode)
-               insn = emit_insn (gen_adddi3 (base_reg_rtx, large_reg,
-                                             stack_pointer_rtx));
-             else
-               insn = emit_insn (gen_addsi3 (base_reg_rtx, large_reg,
-                                             stack_pointer_rtx));
-           }
+         if (Pmode == DImode)
+           insn = emit_insn (gen_adddi3 (base_reg_rtx, large_reg,
+                                         stack_pointer_rtx));
          else
-           fprintf (file, "\t%s\t%s,%s,%s\n",
-                    Pmode == DImode ? "daddu" : "addu",
-                    reg_names[MIPS_TEMP2_REGNUM],
-                    reg_names[REGNO (large_reg)],
-                    reg_names[STACK_POINTER_REGNUM]);
+           insn = 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, file);
+         base_reg_rtx = mips_add_large_offset_to_sp (base_offset);
        }
 
       /* When we restore the registers in MIPS16 mode, then if we are
@@ -6753,101 +6899,57 @@ save_restore_insns (store_p, large_reg, large_offset, file)
        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))
-         {
-           if (file == 0)
-             {
-               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 the caller.  */
-               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 if (!TARGET_ABICALLS
-                        || (mips_abi != ABI_32 && mips_abi != ABI_O64)
-                        || regno != (PIC_OFFSET_TABLE_REGNUM - GP_REG_FIRST))
-                 {
-                   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);
-                 }
-             }
-           else
-             {
-               if (store_p || !TARGET_ABICALLS
-                   || (mips_abi != ABI_32 && mips_abi != ABI_O64)
-                   || regno != (PIC_OFFSET_TABLE_REGNUM - GP_REG_FIRST))
-                 {
-                   int r = regno;
-
-                   /* The mips16 does not have an instruction to
-                       load $31, so we load $7 instead, and work
-                       things out in the caller.  */
-                   if (TARGET_MIPS16 && ! store_p && r == GP_REG_FIRST + 31)
-                     r = GP_REG_FIRST + 7;
-                     /* The mips16 sometimes needs to save $18.  */
-                   if (TARGET_MIPS16
-                       && regno != GP_REG_FIRST + 31
-                       && ! M16_REG_P (regno))
-                     {
-                       if (! store_p)
-                         r = GP_REG_FIRST + 6;
-                       else
-                         {
-                           r = GP_REG_FIRST + 3;
-                           fprintf (file, "\tmove\t%s,%s\n",
-                                    reg_names[r], reg_names[regno]);
-                         }
-                     }
-                   fprintf (file, "\t%s\t%s,",
-                            (TARGET_64BIT
-                             ? (store_p) ? "sd" : "ld"
-                             : (store_p) ? "sw" : "lw"),
-                            reg_names[r]);
-                   fprintf (file, HOST_WIDE_INT_PRINT_DEC,
-                            gp_offset - base_offset);
-                   fprintf (file, "(%s)\n", reg_names[REGNO(base_reg_rtx)]);
-                   if (! store_p
-                       && TARGET_MIPS16
-                       && regno != GP_REG_FIRST + 31
-                       && ! M16_REG_P (regno))
-                     fprintf (file, "\tmove\t%s,%s\n",
-                              reg_names[regno], reg_names[r]);
-                 }
-
-             }
+       {
+         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);
+               }
+           }
+         /* If the restore is being supressed, still take into account
+            the offset at which it is stored.  */
+         if (BITSET_P (real_mask, regno - GP_REG_FIRST))
            gp_offset -= GET_MODE_SIZE (gpr_mode);
-         }
+       }
     }
   else
     base_reg_rtx = 0, base_offset  = 0;
@@ -6855,16 +6957,14 @@ save_restore_insns (store_p, large_reg, large_offset, file)
   /* Save floating point registers if needed.  */
   if (fmask)
     {
-      int fp_inc = (TARGET_FLOAT64 || TARGET_SINGLE_FLOAT) ? 1 : 2;
-      int fp_size = fp_inc * UNITS_PER_FPREG;
-
       /* 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 - fp_size);
+      end_offset = fp_offset - (current_frame_info.fp_reg_size
+                               - UNITS_PER_FPVALUE);
 
       if (fp_offset < 0 || end_offset < 0)
        internal_error
-         ("fp_offset (%ld) or end_offset (%ld) is less than zero.",
+         ("fp_offset (%ld) or end_offset (%ld) is less than zero",
           (long) fp_offset, (long) end_offset);
 
       else if (fp_offset < 32768)
@@ -6881,66 +6981,41 @@ save_restore_insns (store_p, large_reg, large_offset, file)
        {
          base_reg_rtx = gen_rtx_REG (Pmode, MIPS_TEMP2_REGNUM);
          base_offset = large_offset;
-         if (file == 0)
-           {
-             if (Pmode == DImode)
-               insn = emit_insn (gen_adddi3 (base_reg_rtx, large_reg,
-                                             stack_pointer_rtx));
-             else
-               insn = emit_insn (gen_addsi3 (base_reg_rtx, large_reg,
-                                             stack_pointer_rtx));
-           }
-
+         if (Pmode == DImode)
+           insn = emit_insn (gen_adddi3 (base_reg_rtx, large_reg,
+                                         stack_pointer_rtx));
          else
-           fprintf (file, "\t%s\t%s,%s,%s\n",
-                    Pmode == DImode ? "daddu" : "addu",
-                    reg_names[MIPS_TEMP2_REGNUM],
-                    reg_names[REGNO (large_reg)],
-                    reg_names[STACK_POINTER_REGNUM]);
+           insn = 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, file);
+         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);
+      for (regno = (FP_REG_LAST - FP_INC + 1);
           regno >= FP_REG_FIRST;
-          regno -= fp_inc)
+          regno -= FP_INC)
        if (BITSET_P (fmask, regno - FP_REG_FIRST))
          {
-           if (file == 0)
-             {
-               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);
-             }
+           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
-             {
-               fprintf (file, "\t%s\t%s,",
-                        (TARGET_SINGLE_FLOAT
-                         ? (store_p ? "s.s" : "l.s")
-                         : (store_p ? "s.d" : "l.d")),
-                        reg_names[regno]);
-               fprintf (file, HOST_WIDE_INT_PRINT_DEC,
-                        fp_offset - base_offset);
-               fprintf (file, "(%s)\n", reg_names[REGNO(base_reg_rtx)]);
-             }
-
-           fp_offset -= fp_size;
+             emit_move_insn (reg_rtx, mem_rtx);
+
+           fp_offset -= UNITS_PER_FPVALUE;
          }
     }
 }
@@ -6955,7 +7030,7 @@ mips_output_function_prologue (file, size)
 #ifndef FUNCTION_NAME_ALREADY_DECLARED
   const char *fnname;
 #endif
-  long tsize = current_frame_info.total_size;
+  HOST_WIDE_INT tsize = current_frame_info.total_size;
 
   ASM_OUTPUT_SOURCE_FILENAME (file, DECL_SOURCE_FILE (current_function_decl));
 
@@ -6999,9 +7074,9 @@ mips_output_function_prologue (file, size)
               (reg_names[(frame_pointer_needed)
                          ? HARD_FRAME_POINTER_REGNUM : STACK_POINTER_REGNUM]),
               ((frame_pointer_needed && TARGET_MIPS16)
-               ? (tsize - current_function_outgoing_args_size)
-               : tsize),
-              reg_names[31 + GP_REG_FIRST],
+               ? ((long) tsize - current_function_outgoing_args_size)
+               : (long) tsize),
+              reg_names[GP_REG_FIRST + 31],
               current_frame_info.var_size,
               current_frame_info.num_gp,
               current_frame_info.num_fp,
@@ -7017,7 +7092,7 @@ mips_output_function_prologue (file, size)
 
       /* Require:
         OLD_SP == *FRAMEREG + FRAMESIZE => can find old_sp from nominated FP reg.
-        HIGHEST_GP_SAVED == *FRAMEREG + FRAMESIZE + GPOFFSET => can find saved regs. */
+        HIGHEST_GP_SAVED == *FRAMEREG + FRAMESIZE + GPOFFSET => can find saved regs.  */
     }
 
   if (mips_entry && ! mips_can_use_return_insn ())
@@ -7078,11 +7153,10 @@ mips_output_function_prologue (file, size)
              || GET_CODE (offset) != CONST_INT)
            continue;
          if (REGNO (base) == (unsigned) STACK_POINTER_REGNUM
-             && (unsigned HOST_WIDE_INT) INTVAL (offset)
-             == tsize + (REGNO (src) - 4) * UNITS_PER_WORD)
+             && INTVAL (offset) == tsize + (REGNO (src) - 4) * UNITS_PER_WORD)
            ;
          else if (REGNO (base) == (unsigned) HARD_FRAME_POINTER_REGNUM
-                  && ((unsigned HOST_WIDE_INT) INTVAL (offset)
+                  && (INTVAL (offset)
                       == (tsize
                           + (REGNO (src) - 4) * UNITS_PER_WORD
                           - current_function_outgoing_args_size)))
@@ -7146,7 +7220,7 @@ mips_output_function_prologue (file, size)
        {
          fprintf (file, "\t%s\t%s,%s,%ld\n",
                   (Pmode == DImode ? "dsubu" : "subu"),
-                  sp_str, sp_str, tsize);
+                  sp_str, sp_str, (long) tsize);
          fprintf (file, "\t.cprestore %ld\n", current_frame_info.args_size);
        }
 
@@ -7195,7 +7269,7 @@ mips_expand_prologue ()
      the varargs special argument, and treat it as part of the
      variable arguments.
 
-     This is only needed if store_args_on_stack is true. */
+     This is only needed if store_args_on_stack is true.  */
 
   INIT_CUMULATIVE_ARGS (args_so_far, fntype, NULL_RTX, 0);
   regno = GP_ARG_FIRST;
@@ -7227,6 +7301,10 @@ mips_expand_prologue ()
                                   "va_alist"))))
            {
              last_arg_is_vararg_marker = 1;
+             if (GET_CODE (entry_parm) == REG)
+               regno = REGNO (entry_parm);
+             else
+               regno = GP_ARG_LAST + 1;
              break;
            }
          else
@@ -7271,7 +7349,7 @@ mips_expand_prologue ()
          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");
+           abort_with_insn (pattern, "insn is not a shift");
          PUT_CODE (SET_SRC (pattern), ASHIFTRT);
 
          insn = emit_insn (pattern);
@@ -7298,7 +7376,7 @@ mips_expand_prologue ()
       int offset = (regno - GP_ARG_FIRST) * UNITS_PER_WORD;
       rtx ptr = stack_pointer_rtx;
 
-      /* If we are doing svr4-abi, sp has already been decremented by tsize. */
+      /* If we are doing svr4-abi, sp has already been decremented by tsize.  */
       if (TARGET_ABICALLS)
        offset += tsize;
 
@@ -7425,7 +7503,7 @@ mips_expand_prologue ()
        }
 
       if (! mips_entry)
-       save_restore_insns (1, tmp_rtx, tsize, (FILE *)0);
+       save_restore_insns (1, tmp_rtx, tsize);
       else if (reg_18_save != NULL_RTX)
        emit_insn (reg_18_save);
 
@@ -7511,12 +7589,12 @@ mips_expand_prologue ()
   /* If we are profiling, make sure no instructions are scheduled before
      the call to mcount.  */
 
-  if (profile_flag || profile_block_flag)
+  if (current_function_profile)
     emit_insn (gen_blockage ());
 }
 \f
 /* Do any necessary cleanup after a function to restore stack, frame,
-   and regs. */
+   and regs.  */
 
 #define RA_MASK BITMASK_HIGH   /* 1 << 31 */
 #define PIC_OFFSET_TABLE_MASK (1 << (PIC_OFFSET_TABLE_REGNUM - GP_REG_FIRST))
@@ -7657,7 +7735,7 @@ mips_expand_epilogue ()
            {
              tsize -= current_function_outgoing_args_size;
 
-             /* If we have a large frame, it's easier to add to $17
+             /* 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)
@@ -7694,7 +7772,7 @@ mips_expand_epilogue ()
                   & (1L << (PIC_OFFSET_TABLE_REGNUM - GP_REG_FIRST))))
        emit_insn (gen_blockage ());
 
-      save_restore_insns (0, tmp_rtx, orig_tsize, (FILE *)0);
+      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
@@ -7717,12 +7795,37 @@ mips_expand_epilogue ()
 
       if (tsize != 0 || current_function_calls_eh_return)
        {
-         if (Pmode == DImode)
-           emit_insn (gen_adddi3 (stack_pointer_rtx, stack_pointer_rtx,
-                                  tsize_rtx));
+         if (!TARGET_MIPS16)
+           {
+             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
-           emit_insn (gen_addsi3 (stack_pointer_rtx, stack_pointer_rtx,
-                                  tsize_rtx));
+           {
+             /* We need to work around not being able to add a register
+                to the stack pointer directly. Use register $6 as an
+                intermediate step.  */
+
+             rtx g6_rtx = gen_rtx (REG, Pmode, GP_REG_FIRST + 6);
+
+             if (Pmode == DImode)
+               {
+                 emit_insn (gen_movdi (g6_rtx, stack_pointer_rtx));
+                 emit_insn (gen_adddi3 (g6_rtx, g6_rtx, tsize_rtx));
+                 emit_insn (gen_movdi (stack_pointer_rtx, g6_rtx));
+               }
+             else
+               {
+                 emit_insn (gen_movsi (g6_rtx, stack_pointer_rtx));
+                 emit_insn (gen_addsi3 (g6_rtx, g6_rtx, tsize_rtx));
+                 emit_insn (gen_movsi (stack_pointer_rtx, g6_rtx));
+               }
+           }
+
        }
     }
 
@@ -7742,23 +7845,24 @@ mips_expand_epilogue ()
 int
 mips_can_use_return_insn ()
 {
+  tree return_type;
+
   if (! reload_completed)
     return 0;
 
-  if (regs_ever_live[31] || profile_flag)
+  if (regs_ever_live[31] || current_function_profile)
     return 0;
 
+  return_type = DECL_RESULT (current_function_decl);
+
   /* In mips16 mode, a function which returns a floating point value
      needs to arrange to copy the return value into the floating point
      registers.  */
   if (TARGET_MIPS16
       && mips16_hard_float
-      && ! aggregate_value_p (DECL_RESULT (current_function_decl))
-      && (GET_MODE_CLASS (DECL_MODE (DECL_RESULT (current_function_decl)))
-         == MODE_FLOAT)
-      && (! TARGET_SINGLE_FLOAT
-         || (GET_MODE_SIZE (DECL_MODE (DECL_RESULT (current_function_decl)))
-             <= 4)))
+      && ! aggregate_value_p (return_type)
+      && GET_MODE_CLASS (DECL_MODE (return_type)) == MODE_FLOAT
+      && GET_MODE_SIZE (DECL_MODE (return_type)) <= UNITS_PER_FPVALUE)
     return 0;
 
   if (current_frame_info.initialized)
@@ -7809,7 +7913,7 @@ mips_select_rtx_section (mode, x)
     {
       /* For embedded applications, always put constants in read-only data,
         in order to reduce RAM usage.  */
-      READONLY_DATA_SECTION ();
+      readonly_data_section ();
     }
   else
     {
@@ -7828,7 +7932,7 @@ mips_select_rtx_section (mode, x)
           read-only).  */
        data_section ();
       else
-       READONLY_DATA_SECTION ();
+       readonly_data_section ();
     }
 }
 
@@ -7844,10 +7948,11 @@ mips_select_rtx_section (mode, x)
    If you need to make a change here, you probably should check
    ENCODE_SECTION_INFO to see if it needs a similar change.  */
 
-void
-mips_select_section (decl, reloc)
+static void
+mips_select_section (decl, reloc, align)
      tree decl;
      int reloc;
+     unsigned HOST_WIDE_INT align ATTRIBUTE_UNUSED;
 {
   int size = int_size_in_bytes (TREE_TYPE (decl));
 
@@ -7874,7 +7979,7 @@ mips_select_section (decl, reloc)
               && (TREE_CODE (decl) != STRING_CST
                   || !flag_writable_strings)))
          && ! (flag_pic && reloc))
-       READONLY_DATA_SECTION ();
+       readonly_data_section ();
       else if (size > 0 && size <= mips_section_threshold)
        SMALL_DATA_SECTION ();
       else
@@ -7897,71 +8002,56 @@ mips_select_section (decl, reloc)
                    && (TREE_CODE (decl) != STRING_CST
                        || !flag_writable_strings)))
               && ! (flag_pic && reloc))
-       READONLY_DATA_SECTION ();
+       readonly_data_section ();
       else
        data_section ();
     }
 }
 \f
-#ifdef MIPS_ABI_DEFAULT
-
-/* Support functions for the 64 bit ABI.  */
-
-/* Return register to use for a function return value with VALTYPE for function
-   FUNC.  */
+/* Return register to use for a function return value with VALTYPE for
+   function FUNC.  MODE is used instead of VALTYPE for LIBCALLs.  */
 
 rtx
-mips_function_value (valtype, func)
+mips_function_value (valtype, func, mode)
      tree valtype;
      tree func ATTRIBUTE_UNUSED;
+     enum machine_mode mode;
 {
   int reg = GP_RETURN;
-  enum machine_mode mode = TYPE_MODE (valtype);
-  enum mode_class mclass = GET_MODE_CLASS (mode);
-  int unsignedp = TREE_UNSIGNED (valtype);
-
-  /* Since we define PROMOTE_FUNCTION_RETURN, we must promote the mode
-     just as PROMOTE_MODE does.  */
-  mode = promote_mode (valtype, mode, &unsignedp, 1);
+  enum mode_class mclass;
+  int unsignedp = 1;
 
-  if (mclass == MODE_FLOAT)
+  if (valtype)
     {
-      if (TARGET_SINGLE_FLOAT
-         && (mclass == MODE_FLOAT
-             ? GET_MODE_SIZE (mode) > 4 : GET_MODE_SIZE (mode) / 2 > 4))
-       reg = GP_RETURN;
-      else
-       reg = FP_RETURN;
+      mode = TYPE_MODE (valtype);
+      unsignedp = TREE_UNSIGNED (valtype);
+
+      /* Since we define PROMOTE_FUNCTION_RETURN, we must promote
+        the mode just as PROMOTE_MODE does.  */
+      mode = promote_mode (valtype, mode, &unsignedp, 1);
     }
+  mclass = GET_MODE_CLASS (mode);
 
-  else if (mclass == MODE_COMPLEX_FLOAT)
+  if (mclass == MODE_FLOAT && GET_MODE_SIZE (mode) <= UNITS_PER_FPVALUE)
+    reg = FP_RETURN;
+
+  else if (mclass == MODE_COMPLEX_FLOAT
+          && GET_MODE_SIZE (mode) <= UNITS_PER_FPVALUE * 2)
     {
-      if (TARGET_FLOAT64)
-       reg = FP_RETURN;
-      else if (mode == SCmode)
-       {
-         /* When FP registers are 32 bits, we can't directly reference
-            the odd numbered ones, so let's make a pair of evens.  */
-
-         enum machine_mode cmode = TYPE_MODE (TREE_TYPE (valtype));
-
-         return gen_rtx_PARALLEL
-           (VOIDmode,
-            gen_rtvec (2,
-                       gen_rtx_EXPR_LIST (VOIDmode,
-                                          gen_rtx_REG (cmode,
-                                                       FP_RETURN),
-                                          GEN_INT (0)),
-                       gen_rtx_EXPR_LIST (VOIDmode,
-                                          gen_rtx_REG (cmode,
-                                                       FP_RETURN + 2),
-                                          GEN_INT (4))));
-       }
-      else
-       reg = FP_RETURN;
+      enum machine_mode cmode = GET_MODE_INNER (mode);
+
+      return gen_rtx_PARALLEL
+       (VOIDmode,
+        gen_rtvec (2,
+                   gen_rtx_EXPR_LIST (VOIDmode,
+                                      gen_rtx_REG (cmode, FP_RETURN),
+                                      GEN_INT (0)),
+                   gen_rtx_EXPR_LIST (VOIDmode,
+                                      gen_rtx_REG (cmode, FP_RETURN + FP_INC),
+                                      GEN_INT (GET_MODE_SIZE (cmode)))));
     }
 
-  else if (TREE_CODE (valtype) == RECORD_TYPE
+  else if (valtype && TREE_CODE (valtype) == RECORD_TYPE
           && mips_abi != ABI_32
           && mips_abi != ABI_O64
           && mips_abi != ABI_EABI)
@@ -8028,20 +8118,22 @@ mips_function_value (valtype, func)
 
   return gen_rtx_REG (mode, reg);
 }
-#endif
 
 /* The implementation of FUNCTION_ARG_PASS_BY_REFERENCE.  Return
    nonzero when an argument must be passed by reference.  */
 
 int
 function_arg_pass_by_reference (cum, mode, type, named)
-     CUMULATIVE_ARGS *cum ATTRIBUTE_UNUSED;
+     const CUMULATIVE_ARGS *cum;
      enum machine_mode mode;
      tree type;
      int named ATTRIBUTE_UNUSED;
 {
   int size;
 
+  if (mips_abi == ABI_32 || mips_abi == ABI_O64)
+    return 0;
+
   /* We must pass by reference if we would be both passing in registers
      and the stack.  This is because any subsequent partial arg would be
      handled incorrectly in this case.
@@ -8052,16 +8144,9 @@ function_arg_pass_by_reference (cum, mode, type, named)
   /* ??? cum can be NULL when called from mips_va_arg.  The problem handled
      here hopefully is not relevant to mips_va_arg.  */
   if (cum && MUST_PASS_IN_STACK (mode, type)
-      && mips_abi != ABI_MEABI)
-     {
-       /* Don't pass the actual CUM to FUNCTION_ARG, because we would
-         get double copies of any offsets generated for small structs
-         passed in registers. */
-       CUMULATIVE_ARGS temp;
-       temp = *cum;
-       if (FUNCTION_ARG (temp, mode, type, named) != 0)
-        return 1;
-     }
+      && mips_abi != ABI_MEABI
+      && FUNCTION_ARG (*cum, mode, type, named) != 0)
+    return 1;
 
   /* Otherwise, we only do this if EABI is selected.  */
   if (mips_abi != ABI_EABI)
@@ -8208,6 +8293,20 @@ mips_secondary_reload_class (class, mode, x, in_p)
 
   return NO_REGS;
 }
+
+/* This function returns the maximum number of consecutive registers
+   needed to represent mode MODE in registers of class CLASS.  */
+
+int
+mips_class_max_nregs (class, mode)
+     enum reg_class class;
+     enum machine_mode mode;
+{
+  if (class == FP_REGS)
+    return FP_INC;
+  else
+    return (GET_MODE_SIZE (mode) + UNITS_PER_WORD - 1) / UNITS_PER_WORD;
+}
 \f
 /* For each mips16 function which refers to GP relative symbols, we
    use a pseudo register, initialized at the start of the function, to
@@ -8216,13 +8315,13 @@ mips_secondary_reload_class (class, mode, x, in_p)
 rtx
 mips16_gp_pseudo_reg ()
 {
-  if (mips16_gp_pseudo_rtx == NULL_RTX)
+  if (cfun->machine->mips16_gp_pseudo_rtx == NULL_RTX)
     {
       rtx const_gp;
       rtx insn, scan;
 
-      mips16_gp_pseudo_rtx = gen_reg_rtx (Pmode);
-      RTX_UNCHANGING_P (mips16_gp_pseudo_rtx) = 1;
+      cfun->machine->mips16_gp_pseudo_rtx = gen_reg_rtx (Pmode);
+      RTX_UNCHANGING_P (cfun->machine->mips16_gp_pseudo_rtx) = 1;
 
       /* We want to initialize this to a value which gcc will believe
          is constant.  */
@@ -8230,7 +8329,8 @@ mips16_gp_pseudo_reg ()
                          gen_rtx (REG, Pmode, GP_REG_FIRST + 28));
 
       start_sequence ();
-      emit_move_insn (mips16_gp_pseudo_rtx, const_gp);
+      emit_move_insn (cfun->machine->mips16_gp_pseudo_rtx,
+                     const_gp);
       insn = gen_sequence ();
       end_sequence ();
 
@@ -8247,7 +8347,7 @@ mips16_gp_pseudo_reg ()
       pop_topmost_sequence ();
     }
 
-  return mips16_gp_pseudo_rtx;
+  return cfun->machine->mips16_gp_pseudo_rtx;
 }
 
 /* Return an RTX which represents the signed 16 bit offset from the
@@ -8650,8 +8750,7 @@ build_mips16_call_stub (retval, fnmem, arg_size, fp_code)
      register.  */
   fpret = (retval != 0
           && GET_MODE_CLASS (GET_MODE (retval)) == MODE_FLOAT
-          && (! TARGET_SINGLE_FLOAT
-              || GET_MODE_SIZE (GET_MODE (retval)) <= 4));
+          && GET_MODE_SIZE (GET_MODE (retval)) <= UNITS_PER_FPVALUE);
 
   /* We don't need to do anything if there were no floating point
      arguments and the value will not be returned in a floating point
@@ -8809,7 +8908,7 @@ build_mips16_call_stub (retval, fnmem, arg_size, fp_code)
 
       /* We build the stub code by hand.  That's the only way we can
         do it, since we can't generate 32 bit code during a 16 bit
-        compilation. */
+        compilation.  */
 
       /* We don't want the assembler to insert any nops here.  */
       fprintf (asm_out_file, "\t.set\tnoreorder\n");
@@ -9477,7 +9576,7 @@ machine_dependent_reorg (first)
        {
          /* If we haven't had a barrier within 0x8000 bytes of a
              constant reference or we are at the end of the function,
-             emit a barrier now. */
+             emit a barrier now.  */
 
          rtx label, jump, barrier;
 
@@ -9522,6 +9621,106 @@ highpart_shift_operator (x, mode)
          || code == ROTATE);
 }
 
+/* Return a number assessing the cost of moving a register in class
+   FROM to class TO.  The classes are expressed using the enumeration
+   values such as `GENERAL_REGS'.  A value of 2 is the default; other
+   values are interpreted relative to that.
+
+   It is not required that the cost always equal 2 when FROM is the
+   same as TO; on some machines it is expensive to move between
+   registers if they are not general registers.
+
+   If reload sees an insn consisting of a single `set' between two
+   hard registers, and if `REGISTER_MOVE_COST' applied to their
+   classes returns a value of 2, reload does not check to ensure that
+   the constraints of the insn are met.  Setting a cost of other than
+   2 will allow reload to verify that the constraints are met.  You
+   should do this if the `movM' pattern's constraints do not allow
+   such copying.
+
+   ??? We make make the cost of moving from HI/LO/HILO/MD into general
+   registers the same as for one of moving general registers to
+   HI/LO/HILO/MD for TARGET_MIPS16 in order to prevent allocating a
+   pseudo to HI/LO/HILO/MD.  This might hurt optimizations though, it
+   isn't clear if it is wise.  And it might not work in all cases.  We
+   could solve the DImode LO reg problem by using a multiply, just
+   like reload_{in,out}si.  We could solve the SImode/HImode HI reg
+   problem by using divide instructions.  divu puts the remainder in
+   the HI reg, so doing a divide by -1 will move the value in the HI
+   reg for all values except -1.  We could handle that case by using a
+   signed divide, e.g.  -1 / 2 (or maybe 1 / -2?).  We'd have to emit
+   a compare/branch to test the input value to see which instruction
+   we need to use.  This gets pretty messy, but it is feasible.  */
+
+int
+mips_register_move_cost (mode, to, from)
+     enum machine_mode mode ATTRIBUTE_UNUSED;
+     enum reg_class to, from;
+{
+  if (from == M16_REGS && GR_REG_CLASS_P (to))
+    return 2;
+  else if (from == M16_NA_REGS && GR_REG_CLASS_P (to))
+    return 2;
+  else if (GR_REG_CLASS_P (from))
+    {
+      if (to == M16_REGS)
+       return 2;
+      else if (to == M16_NA_REGS)
+       return 2;
+      else if (GR_REG_CLASS_P (to))
+       {
+         if (TARGET_MIPS16)
+           return 4;
+         else
+           return 2;
+       }
+      else if (to == FP_REGS)
+       return 4;
+      else if (to == HI_REG || to == LO_REG || to == MD_REGS
+              || to == HILO_REG)
+       {
+         if (TARGET_MIPS16)
+           return 12;
+         else
+           return 6;
+       }
+      else if (COP_REG_CLASS_P (to))
+       {
+         return 5;
+       }
+    }  /* GR_REG_CLASS_P (from) */
+  else if (from == FP_REGS)
+    {
+      if (GR_REG_CLASS_P (to))
+       return 4;
+      else if (to == FP_REGS)
+       return 2;
+      else if (to == ST_REGS)
+       return 8;
+    }  /* from == FP_REGS */
+  else if (from == HI_REG || from == LO_REG || from == MD_REGS
+          || from == HILO_REG)
+    {
+      if (GR_REG_CLASS_P (to))
+       {
+         if (TARGET_MIPS16)
+           return 12;
+         else
+           return 6;
+       }
+    }  /* from == HI_REG, etc. */
+  else if (from == ST_REGS && GR_REG_CLASS_P (to))
+    return 4;
+  else if (COP_REG_CLASS_P (from))
+    {
+      return 5;
+    }  /* COP_REG_CLASS_P (from) */
+
+  /* fallthru */
+
+  return 12;
+}
+
 /* Return the length of INSN.  LENGTH is the initial length computed by
    attributes in the machine-description file.  */
 
@@ -9559,7 +9758,7 @@ mips_adjust_insn_length (insn, length)
    LENGTH is the length (in bytes) of the sequence we are to generate.
    That tells us whether to generate a simple conditional branch, or a
    reversed conditional branch around a `jr' instruction.  */
-char *
+const char *
 mips_output_conditional_branch (insn,
                                operands,
                                two_operands_p,
@@ -9622,7 +9821,7 @@ mips_output_conditional_branch (insn,
          break;
 
        case LTU:
-         /* A condition which will always be false. */
+         /* A condition which will always be false.  */
          code = NE;
          op1 = "%.";
          break;
@@ -9677,46 +9876,44 @@ mips_output_conditional_branch (insn,
                .set reorder
             l:
 
-          Because we have to jump four bytes *past* the following
-          instruction if this branch was annulled, we can't just use
-          a label, as in the picture above; there's no way to put the
-          label after the next instruction, as the assembler does not
-          accept `.L+4' as the target of a branch.  (We can't just
-          wait until the next instruction is output; it might be a
-          macro and take up more than four bytes.  Once again, we see
-          why we want to eliminate macros.)
+       */
 
-          If the branch is annulled, we jump four more bytes that we
-          would otherwise; that way we skip the annulled instruction
-          in the delay slot.  */
-
-       const char *const target
-         = ((mips_branch_likely || length == 16) ? ".+16" : ".+12");
-       char *c;
+        rtx orig_target;
+       rtx target = gen_label_rtx ();
 
-       strcpy (buffer, "%(%<");
-       c = strchr (buffer, '\0');
-       /* Generate the reversed comparision.  This takes four
+        output_asm_insn ("%(%<", 0);
+        orig_target = operands[1];
+        operands[1] = target;
+       /* Generate the reversed comparison.  This takes four
           bytes.  */
        if (float_p)
-         sprintf (c, "%%*b%s\t%%Z2%s",
-                  inverted_p ? comp : inverted_comp,
-                  target);
+         sprintf (buffer, "%%*b%s\t%%Z2%%1",
+                  inverted_p ? comp : inverted_comp);
        else
-         sprintf (c, "%%*b%s%s\t%s%s,%s",
+         sprintf (buffer, "%%*b%s%s\t%s%s,%%1",
                   inverted_p ? comp : inverted_comp,
                   need_z_p ? "z" : "",
                   op1,
-                  op2,
-                  target);
-       strcat (c, "\n\tnop\n\tj\t%1");
-       if (length == 16)
-         /* The delay slot was unfilled.  Since we're inside
-            .noreorder, the assembler will not fill in the NOP for
-            us, so we must do it ourselves.  */
-         strcat (buffer, "\n\tnop");
-       strcat (buffer, "%>%)");
-       return buffer;
+                  op2);
+        output_asm_insn (buffer, operands);
+        operands[1] = orig_target;
+
+       output_asm_insn ("nop\n\tj\t%1", operands);
+
+        if (length == 16)
+         output_asm_insn ("nop", 0);
+        else
+          {
+            /* Output delay slot instruction.  */
+            rtx insn = final_sequence;
+            final_scan_insn (XVECEXP (insn, 0, 1), asm_out_file,
+                             optimize, 0, 1);
+            INSN_DELETED_P (XVECEXP (insn, 0, 1)) = 1;
+          }
+       output_asm_insn ("%>%)", 0);
+        ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, "L",
+                                   CODE_LABEL_NUMBER (target));
+        return "";
       }
 
     /* We do not currently use this code.  It handles jumps to
@@ -9826,9 +10023,7 @@ mips_add_gc_roots ()
   ggc_add_rtx_root (&mips_load_reg2, 1);
   ggc_add_rtx_root (&mips_load_reg3, 1);
   ggc_add_rtx_root (&mips_load_reg4, 1);
-  ggc_add_rtx_root (branch_cmp, sizeof (branch_cmp) / sizeof (rtx));
-  ggc_add_rtx_root (&embedded_pic_fnaddr_rtx, 1);
-  ggc_add_rtx_root (&mips16_gp_pseudo_rtx, 1);
+  ggc_add_rtx_root (branch_cmp, ARRAY_SIZE (branch_cmp));
 }
 
 static enum processor_type
@@ -9838,14 +10033,26 @@ mips_parse_cpu (cpu_string)
   const char *p = cpu_string;
   int seen_v = 0;
   enum processor_type cpu;
+  int warn_upper_case = 0;
 
   /* We need to cope with the various "vr" prefixes for the NEC 4300
      and 4100 processors.  */
   if (*p == 'v' || *p == 'V')
-    seen_v = 1, p++;
+    {
+      if (*p == 'V')
+       warn_upper_case = 1;
+      seen_v = 1, p++;
+    }
 
   if (*p == 'r' || *p == 'R')
-    p++;
+    {
+      if (*p == 'R')
+       warn_upper_case = 1;
+      p++;
+    }
+
+  if (warn_upper_case)
+    warning ("the cpu name must be lower case");
 
   /* Since there is no difference between a R2000 and R3000 in
      terms of the scheduler, we collapse them into just an R3000.  */
@@ -9944,9 +10151,10 @@ mips_adjust_cost (insn, link, dep, cost)
   return cost;
 }
 
-/* Cover function for UNIQUE_SECTION.  */
+/* ??? This could be replaced with the default elf version if
+   TARGET_IS_SMALL_DATA_P is set properly.  */
 
-void
+static void
 mips_unique_section (decl, reloc)
      tree decl;
      int reloc;
@@ -9963,6 +10171,7 @@ mips_unique_section (decl, reloc)
 
   name = IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (decl));
   size = int_size_in_bytes (TREE_TYPE (decl));
+  STRIP_NAME_ENCODING (name, name);
 
   /* Determine the base section we are interested in:
      0=text, 1=rodata, 2=data, 3=sdata, [4=bss].  */
@@ -10028,6 +10237,41 @@ mips_hard_regno_nregs (regno, mode)
     return ((GET_MODE_SIZE (mode) + UNITS_PER_FPREG - 1) / UNITS_PER_FPREG);
 }
 
+int
+mips_return_in_memory (type)
+     tree type;
+{
+  /* Under the old (i.e., 32 and O64 ABIs) all BLKmode objects are
+     returned in memory.  Under the new (N32 and 64-bit MIPS ABIs) small
+     structures are returned in a register.  Objects with varying size
+     must still be returned in memory, of course.  */
+
+  if (mips_abi == ABI_32 || mips_abi == ABI_O64)
+    return (TYPE_MODE (type) == BLKmode);
+  else
+    return ((int_size_in_bytes (type) > (2 * UNITS_PER_WORD))
+           || (int_size_in_bytes (type) == -1));
+}
+
+static int
+mips_issue_rate ()
+{
+  int rate;
+
+  switch (mips_tune)
+    {
+    case PROCESSOR_R3000:
+      rate = 1;
+      break;
+
+    default:
+      rate = 1;
+      break;
+    }
+
+  return rate;
+}
+
 \f
 #ifdef TARGET_IRIX6
 /* Output assembly to switch to section NAME with attribute FLAGS.  */