OSDN Git Service

2010-11-04 Chao-ying Fu <fu@mips.com>
[pf3gnuchains/gcc-fork.git] / gcc / config / mips / mips.c
index 8dfd3c2..76c0786 100644 (file)
@@ -1,6 +1,6 @@
 /* Subroutines used for MIPS code generation.
    Copyright (C) 1989, 1990, 1991, 1993, 1994, 1995, 1996, 1997, 1998,
-   1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009
+   1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010
    Free Software Foundation, Inc.
    Contributed by A. Lichnewsky, lich@inria.inria.fr.
    Changes by Michael Meissner, meissner@osf.org.
@@ -31,7 +31,6 @@ along with GCC; see the file COPYING3.  If not see
 #include "rtl.h"
 #include "regs.h"
 #include "hard-reg-set.h"
-#include "real.h"
 #include "insn-config.h"
 #include "conditions.h"
 #include "insn-attr.h"
@@ -59,6 +58,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "gimple.h"
 #include "bitmap.h"
 #include "diagnostic.h"
+#include "target-globals.h"
 
 /* True if X is an UNSPEC wrapper around a SYMBOL_REF or LABEL_REF.  */
 #define UNSPEC_ADDRESS_P(X)                                    \
@@ -97,7 +97,7 @@ along with GCC; see the file COPYING3.  If not see
 
 /* True if INSN is a mips.md pattern or asm statement.  */
 #define USEFUL_INSN_P(INSN)                                            \
-  (INSN_P (INSN)                                                       \
+  (NONDEBUG_INSN_P (INSN)                                              \
    && GET_CODE (PATTERN (INSN)) != USE                                 \
    && GET_CODE (PATTERN (INSN)) != CLOBBER                             \
    && GET_CODE (PATTERN (INSN)) != ADDR_VEC                            \
@@ -126,6 +126,40 @@ along with GCC; see the file COPYING3.  If not see
 /* True if bit BIT is set in VALUE.  */
 #define BITSET_P(VALUE, BIT) (((VALUE) & (1 << (BIT))) != 0)
 
+/* Return the opcode for a ptr_mode load of the form:
+
+       l[wd]    DEST, OFFSET(BASE).  */
+#define MIPS_LOAD_PTR(DEST, OFFSET, BASE)      \
+  (((ptr_mode == DImode ? 0x37 : 0x23) << 26)  \
+   | ((BASE) << 21)                            \
+   | ((DEST) << 16)                            \
+   | (OFFSET))
+
+/* Return the opcode to move register SRC into register DEST.  */
+#define MIPS_MOVE(DEST, SRC)           \
+  ((TARGET_64BIT ? 0x2d : 0x21)                \
+   | ((DEST) << 11)                    \
+   | ((SRC) << 21))
+
+/* Return the opcode for:
+
+       lui      DEST, VALUE.  */
+#define MIPS_LUI(DEST, VALUE) \
+  ((0xf << 26) | ((DEST) << 16) | (VALUE))
+
+/* Return the opcode to jump to register DEST.  */
+#define MIPS_JR(DEST) \
+  (((DEST) << 21) | 0x8)
+
+/* Return the opcode for:
+
+       bal     . + (1 + OFFSET) * 4.  */
+#define MIPS_BAL(OFFSET) \
+  ((0x1 << 26) | (0x11 << 16) | (OFFSET))
+
+/* Return the usual opcode for a nop.  */
+#define MIPS_NOP 0
+
 /* Classifies an address.
 
    ADDRESS_REG
@@ -285,10 +319,13 @@ struct GTY(())  mips_frame_info {
   HOST_WIDE_INT acc_sp_offset;
   HOST_WIDE_INT cop0_sp_offset;
 
-  /* The offset of arg_pointer_rtx from frame_pointer_rtx.  */
+  /* Similar, but the value passed to _mcount.  */
+  HOST_WIDE_INT ra_fp_offset;
+
+  /* The offset of arg_pointer_rtx from the bottom of the frame.  */
   HOST_WIDE_INT arg_pointer_offset;
 
-  /* The offset of hard_frame_pointer_rtx from frame_pointer_rtx.  */
+  /* The offset of hard_frame_pointer_rtx from the bottom of the frame.  */
   HOST_WIDE_INT hard_frame_pointer_offset;
 };
 
@@ -307,6 +344,10 @@ struct GTY(())  machine_function {
      if the function doesn't need one.  */
   unsigned int global_pointer;
 
+  /* How many instructions it takes to load a label into $AT, or 0 if
+     this property hasn't yet been calculated.  */
+  unsigned int load_label_length;
+
   /* True if mips_adjust_insn_length should ignore an instruction's
      hazard attribute.  */
   bool ignore_hazard_length_p;
@@ -315,8 +356,23 @@ struct GTY(())  machine_function {
      .set nomacro.  */
   bool all_noreorder_p;
 
-  /* True if the function is known to have an instruction that needs $gp.  */
-  bool has_gp_insn_p;
+  /* True if the function has "inflexible" and "flexible" references
+     to the global pointer.  See mips_cfun_has_inflexible_gp_ref_p
+     and mips_cfun_has_flexible_gp_ref_p for details.  */
+  bool has_inflexible_gp_insn_p;
+  bool has_flexible_gp_insn_p;
+
+  /* True if the function's prologue must load the global pointer
+     value into pic_offset_table_rtx and store the same value in
+     the function's cprestore slot (if any).  Even if this value
+     is currently false, we may decide to set it to true later;
+     see mips_must_initialize_gp_p () for details.  */
+  bool must_initialize_gp_p;
+
+  /* True if the current function must restore $gp after any potential
+     clobber.  This value is only meaningful during the first post-epilogue
+     split_insns pass; see mips_must_initialize_gp_p () for details.  */
+  bool must_restore_gp_when_clobbered_p;
 
   /* True if we have emitted an instruction to initialize
      mips16_gp_pseudo_rtx.  */
@@ -418,6 +474,23 @@ struct mips16e_save_restore_info {
   HOST_WIDE_INT size;
 };
 
+/* Costs of various operations on the different architectures.  */
+
+struct mips_rtx_cost_data
+{
+  unsigned short fp_add;
+  unsigned short fp_mult_sf;
+  unsigned short fp_mult_df;
+  unsigned short fp_div_sf;
+  unsigned short fp_div_df;
+  unsigned short int_mult_si;
+  unsigned short int_mult_di;
+  unsigned short int_div_si;
+  unsigned short int_div_di;
+  unsigned short branch_cost;
+  unsigned short memory_latency;
+};
+
 /* Global variables for machine-dependent things.  */
 
 /* The -G setting, or the configuration's default small-data limit if
@@ -440,23 +513,20 @@ int mips_dbx_regno[FIRST_PSEUDO_REGISTER];
 int mips_dwarf_regno[FIRST_PSEUDO_REGISTER];
 
 /* The nesting depth of the PRINT_OPERAND '%(', '%<' and '%[' constructs.  */
-int set_noreorder;
-int set_nomacro;
-static int set_noat;
+struct mips_asm_switch mips_noreorder = { "reorder", 0 };
+struct mips_asm_switch mips_nomacro = { "macro", 0 };
+struct mips_asm_switch mips_noat = { "at", 0 };
 
 /* True if we're writing out a branch-likely instruction rather than a
    normal branch.  */
 static bool mips_branch_likely;
 
-/* The operands passed to the last cmpMM expander.  */
-rtx cmp_operands[2];
-
 /* The current instruction-set architecture.  */
-enum processor_type mips_arch;
+enum processor mips_arch;
 const struct mips_cpu_info *mips_arch_info;
 
 /* The processor that we should tune the code for.  */
-enum processor_type mips_tune;
+enum processor mips_tune;
 const struct mips_cpu_info *mips_tune_info;
 
 /* The ISA level associated with mips_arch.  */
@@ -469,7 +539,7 @@ static const struct mips_cpu_info *mips_isa_option_info;
 int mips_abi = MIPS_ABI_DEFAULT;
 
 /* Which cost information to use.  */
-const struct mips_rtx_cost_data *mips_cost;
+static const struct mips_rtx_cost_data *mips_cost;
 
 /* The ambient target flags, excluding MASK_MIPS16.  */
 static int mips_base_target_flags;
@@ -496,7 +566,7 @@ bool mips_hard_regno_mode_ok[(int) MAX_MACHINE_MODE][FIRST_PSEUDO_REGISTER];
 
 /* Index C is true if character C is a valid PRINT_OPERAND punctation
    character.  */
-bool mips_print_operand_punct[256];
+static bool mips_print_operand_punct[256];
 
 static GTY (()) int mips_output_filename_first_time = 1;
 
@@ -516,6 +586,13 @@ static const char *mips_lo_relocs[NUM_SYMBOL_TYPES];
 /* Likewise for HIGHs.  */
 static const char *mips_hi_relocs[NUM_SYMBOL_TYPES];
 
+/* Target state for MIPS16.  */
+struct target_globals *mips16_globals;
+
+/* Cached value of can_issue_more. This is cached in mips_variable_issue hook
+   and returned from mips_sched_reorder2.  */
+static int cached_can_issue_more;
+
 /* Index R is the smallest register class that contains register R.  */
 const enum reg_class mips_regno_to_class[FIRST_PSEUDO_REGISTER] = {
   LEA_REGS,    LEA_REGS,       M16_REGS,       V1_REG,
@@ -568,7 +645,7 @@ const enum reg_class mips_regno_to_class[FIRST_PSEUDO_REGISTER] = {
 };
 
 /* The value of TARGET_ATTRIBUTE_TABLE.  */
-const struct attribute_spec mips_attribute_table[] = {
+static const struct attribute_spec mips_attribute_table[] = {
   /* { name, min_len, max_len, decl_req, type_req, fn_type_req, handler } */
   { "long_call",   0, 0, false, true,  true,  NULL },
   { "far",                0, 0, false, true,  true,  NULL },
@@ -689,6 +766,11 @@ static const struct mips_cpu_info mips_cpu_info_table[] = {
   { "74kx", PROCESSOR_74KF1_1, 33, 0 },
   { "74kf3_2", PROCESSOR_74KF3_2, 33, 0 },
 
+  { "1004kc", PROCESSOR_24KC, 33, 0 }, /* 1004K with MT/DSP.  */
+  { "1004kf2_1", PROCESSOR_24KF2_1, 33, 0 },
+  { "1004kf", PROCESSOR_24KF2_1, 33, 0 },
+  { "1004kf1_1", PROCESSOR_24KF1_1, 33, 0 },
+
   /* MIPS64 processors.  */
   { "5kc", PROCESSOR_5KC, 64, 0 },
   { "5kf", PROCESSOR_5KF, 64, 0 },
@@ -740,7 +822,8 @@ static const struct mips_rtx_cost_data mips_rtx_cost_optimize_size = {
 };
 
 /* Costs to use when optimizing for speed, indexed by processor.  */
-static const struct mips_rtx_cost_data mips_rtx_cost_data[PROCESSOR_MAX] = {
+static const struct mips_rtx_cost_data
+  mips_rtx_cost_data[NUM_PROCESSOR_VALUES] = {
   { /* R3000 */
     COSTS_N_INSNS (2),            /* fp_add */
     COSTS_N_INSNS (4),            /* fp_mult_sf */
@@ -1104,6 +1187,10 @@ static const struct mips_rtx_cost_data mips_rtx_cost_data[PROCESSOR_MAX] = {
   }
 };
 \f
+static rtx mips_find_pic_call_symbol (rtx, rtx);
+static int mips_register_move_cost (enum machine_mode, reg_class_t,
+                                   reg_class_t);
+\f
 /* This hash table keeps track of implicit "mips16" and "nomips16" attributes
    for -mflip_mips16.  It maps decl names onto a boolean mode setting.  */
 struct GTY (())  mflip_mips16_entry {
@@ -1158,7 +1245,7 @@ mflip_mips16_use_mips16_p (tree decl)
   if (!entry)
     {
       mips16_flipper = !mips16_flipper;
-      entry = GGC_NEW (struct mflip_mips16_entry);
+      entry = ggc_alloc_mflip_mips16_entry ();
       entry->name = name;
       entry->mips16_p = mips16_flipper ? !mips_base_mips16 : mips_base_mips16;
       *slot = entry;
@@ -1292,9 +1379,9 @@ mips_insert_attributes (tree decl, tree *attributes)
        {
          /* DECL cannot be simultaneously "mips16" and "nomips16".  */
          if (mips16_p && nomips16_p)
-           error ("%qs cannot have both %<mips16%> and "
+           error ("%qE cannot have both %<mips16%> and "
                   "%<nomips16%> attributes",
-                  IDENTIFIER_POINTER (DECL_NAME (decl)));
+                  DECL_NAME (decl));
        }
       else if (TARGET_FLIP_MIPS16 && !DECL_ARTIFICIAL (decl))
        {
@@ -1314,11 +1401,11 @@ mips_merge_decl_attributes (tree olddecl, tree newdecl)
 {
   /* The decls' "mips16" and "nomips16" attributes must match exactly.  */
   if (mips_mips16_decl_p (olddecl) != mips_mips16_decl_p (newdecl))
-    error ("%qs redeclared with conflicting %qs attributes",
-          IDENTIFIER_POINTER (DECL_NAME (newdecl)), "mips16");
+    error ("%qE redeclared with conflicting %qs attributes",
+          DECL_NAME (newdecl), "mips16");
   if (mips_nomips16_decl_p (olddecl) != mips_nomips16_decl_p (newdecl))
-    error ("%qs redeclared with conflicting %qs attributes",
-          IDENTIFIER_POINTER (DECL_NAME (newdecl)), "nomips16");
+    error ("%qE redeclared with conflicting %qs attributes",
+          DECL_NAME (newdecl), "nomips16");
 
   return merge_attributes (DECL_ATTRIBUTES (olddecl),
                           DECL_ATTRIBUTES (newdecl));
@@ -1330,7 +1417,7 @@ mips_merge_decl_attributes (tree olddecl, tree newdecl)
 static void
 mips_split_plus (rtx x, rtx *base_ptr, HOST_WIDE_INT *offset_ptr)
 {
-  if (GET_CODE (x) == PLUS && GET_CODE (XEXP (x, 1)) == CONST_INT)
+  if (GET_CODE (x) == PLUS && CONST_INT_P (XEXP (x, 1)))
     {
       *base_ptr = XEXP (x, 0);
       *offset_ptr = INTVAL (XEXP (x, 1));
@@ -1924,7 +2011,7 @@ mips_cannot_force_const_mem (rtx x)
      references, reload will consider forcing C into memory and using
      one of the instruction's memory alternatives.  Returning false
      here will force it to use an input reload instead.  */
-  if (GET_CODE (x) == CONST_INT && LEGITIMATE_CONSTANT_P (x))
+  if (CONST_INT_P (x) && LEGITIMATE_CONSTANT_P (x))
     return true;
 
   split_const (x, &base, &offset);
@@ -2119,10 +2206,9 @@ mips_classify_address (struct mips_address_info *info, rtx x,
     }
 }
 
-/* Return true if X is a legitimate address for a memory operand of mode
-   MODE.  STRICT_P is true if REG_OK_STRICT is in effect.  */
+/* Implement TARGET_LEGITIMATE_ADDRESS_P.  */
 
-bool
+static bool
 mips_legitimate_address_p (enum machine_mode mode, rtx x, bool strict_p)
 {
   struct mips_address_info addr;
@@ -2144,7 +2230,7 @@ mips_stack_address_p (rtx x, enum machine_mode mode)
 
 /* Return true if ADDR matches the pattern for the LWXS load scaled indexed
    address instruction.  Note that such addresses are not considered
-   legitimate in the GO_IF_LEGITIMATE_ADDRESS sense, because their use
+   legitimate in the TARGET_LEGITIMATE_ADDRESS_P sense, because their use
    is so restricted.  */
 
 static bool
@@ -2157,7 +2243,7 @@ mips_lwxs_address_p (rtx addr)
       rtx offset = XEXP (addr, 0);
       if (GET_CODE (offset) == MULT
          && REG_P (XEXP (offset, 0))
-         && GET_CODE (XEXP (offset, 1)) == CONST_INT
+         && CONST_INT_P (XEXP (offset, 1))
          && INTVAL (XEXP (offset, 1)) == 4)
        return true;
     }
@@ -2388,6 +2474,28 @@ mips_emit_move (rtx dest, rtx src)
          : emit_move_insn_1 (dest, src));
 }
 
+/* Emit an instruction of the form (set TARGET (CODE OP0)).  */
+
+static void
+mips_emit_unary (enum rtx_code code, rtx target, rtx op0)
+{
+  emit_insn (gen_rtx_SET (VOIDmode, target,
+                         gen_rtx_fmt_e (code, GET_MODE (op0), op0)));
+}
+
+/* Compute (CODE OP0) and store the result in a new register of mode MODE.
+   Return that new register.  */
+
+static rtx
+mips_force_unary (enum machine_mode mode, enum rtx_code code, rtx op0)
+{
+  rtx reg;
+
+  reg = gen_reg_rtx (mode);
+  mips_emit_unary (code, reg, op0);
+  return reg;
+}
+
 /* Emit an instruction of the form (set TARGET (CODE OP0 OP1)).  */
 
 static void
@@ -2489,6 +2597,20 @@ mips_unspec_address (rtx address, enum mips_symbol_type symbol_type)
   return mips_unspec_address_offset (base, offset, symbol_type);
 }
 
+/* If OP is an UNSPEC address, return the address to which it refers,
+   otherwise return OP itself.  */
+
+static rtx
+mips_strip_unspec_address (rtx op)
+{
+  rtx base, offset;
+
+  split_const (op, &base, &offset);
+  if (UNSPEC_ADDRESS_P (base))
+    op = plus_constant (UNSPEC_ADDRESS (base), INTVAL (offset));
+  return op;
+}
+
 /* If mips_unspec_address (ADDR, SYMBOL_TYPE) is a 32-bit value, add the
    high part to BASE and return the result.  Just return BASE otherwise.
    TEMP is as for mips_force_temporary.
@@ -2580,6 +2702,28 @@ mips_pic_base_register (rtx temp)
   return temp;
 }
 
+/* Return the RHS of a load_call<mode> insn.  */
+
+static rtx
+mips_unspec_call (rtx reg, rtx symbol)
+{
+  rtvec vec;
+
+  vec = gen_rtvec (3, reg, symbol, gen_rtx_REG (SImode, GOT_VERSION_REGNUM));
+  return gen_rtx_UNSPEC (Pmode, vec, UNSPEC_LOAD_CALL);
+}
+
+/* If SRC is the RHS of a load_call<mode> insn, return the underlying symbol
+   reference.  Return NULL_RTX otherwise.  */
+
+static rtx
+mips_strip_unspec_call (rtx src)
+{
+  if (GET_CODE (src) == UNSPEC && XINT (src, 1) == UNSPEC_LOAD_CALL)
+    return mips_strip_unspec_address (XVECEXP (src, 0, 1));
+  return NULL_RTX;
+}
+
 /* Create and return a GOT reference of type TYPE for address ADDR.
    TEMP, if nonnull, is a scratch Pmode base register.  */
 
@@ -2599,9 +2743,7 @@ mips_got_load (rtx temp, rtx addr, enum mips_symbol_type type)
   lo_sum_symbol = mips_unspec_address (addr, type);
 
   if (type == SYMBOL_GOTOFF_CALL)
-    return (Pmode == SImode
-           ? gen_unspec_callsi (high, lo_sum_symbol)
-           : gen_unspec_calldi (high, lo_sum_symbol));
+    return mips_unspec_call (high, lo_sum_symbol);
   else
     return (Pmode == SImode
            ? gen_unspec_gotsi (high, lo_sum_symbol)
@@ -2848,41 +2990,36 @@ mips_force_address (rtx x, enum machine_mode mode)
   return x;
 }
 
-/* This function is used to implement LEGITIMIZE_ADDRESS.  If *XLOC can
+/* This function is used to implement LEGITIMIZE_ADDRESS.  If X can
    be legitimized in a way that the generic machinery might not expect,
-   put the new address in *XLOC and return true.  MODE is the mode of
+   return a new address, otherwise return NULL.  MODE is the mode of
    the memory being accessed.  */
 
-bool
-mips_legitimize_address (rtx *xloc, enum machine_mode mode)
+static rtx
+mips_legitimize_address (rtx x, rtx oldx ATTRIBUTE_UNUSED,
+                        enum machine_mode mode)
 {
   rtx base, addr;
   HOST_WIDE_INT offset;
 
-  if (mips_tls_symbol_p (*xloc))
-    {
-      *xloc = mips_legitimize_tls_address (*xloc);
-      return true;
-    }
+  if (mips_tls_symbol_p (x))
+    return mips_legitimize_tls_address (x);
 
   /* See if the address can split into a high part and a LO_SUM.  */
-  if (mips_split_symbol (NULL, *xloc, mode, &addr))
-    {
-      *xloc = mips_force_address (addr, mode);
-      return true;
-    }
+  if (mips_split_symbol (NULL, x, mode, &addr))
+    return mips_force_address (addr, mode);
 
   /* Handle BASE + OFFSET using mips_add_offset.  */
-  mips_split_plus (*xloc, &base, &offset);
+  mips_split_plus (x, &base, &offset);
   if (offset != 0)
     {
       if (!mips_valid_base_register_p (base, mode, false))
        base = copy_to_mode_reg (Pmode, base);
       addr = mips_add_offset (NULL, base, offset);
-      *xloc = mips_force_address (addr, mode);
-      return true;
+      return mips_force_address (addr, mode);
     }
-  return false;
+
+  return x;
 }
 
 /* Load VALUE into DEST.  TEMP is as for mips_force_temporary.  */
@@ -3077,7 +3214,7 @@ mips_rewrite_small_data (rtx pattern)
 static int
 m16_check_op (rtx op, int low, int high, int mask)
 {
-  return (GET_CODE (op) == CONST_INT
+  return (CONST_INT_P (op)
          && IN_RANGE (INTVAL (op), low, high)
          && (INTVAL (op) & mask) == 0);
 }
@@ -3302,10 +3439,11 @@ mips_immediate_operand_p (int code, HOST_WIDE_INT x)
 
 /* Return the cost of binary operation X, given that the instruction
    sequence for a word-sized or smaller operation has cost SINGLE_COST
-   and that the sequence of a double-word operation has cost DOUBLE_COST.  */
+   and that the sequence of a double-word operation has cost DOUBLE_COST.
+   If SPEED is true, optimize for speed otherwise optimize for size.  */
 
 static int
-mips_binary_cost (rtx x, int single_cost, int double_cost)
+mips_binary_cost (rtx x, int single_cost, int double_cost, bool speed)
 {
   int cost;
 
@@ -3314,8 +3452,8 @@ mips_binary_cost (rtx x, int single_cost, int double_cost)
   else
     cost = single_cost;
   return (cost
-         + rtx_cost (XEXP (x, 0), SET, !optimize_size)
-         + rtx_cost (XEXP (x, 1), GET_CODE (x), !optimize_size));
+         + rtx_cost (XEXP (x, 0), SET, speed)
+         + rtx_cost (XEXP (x, 1), GET_CODE (x), speed));
 }
 
 /* Return the cost of floating-point multiplications of mode MODE.  */
@@ -3385,8 +3523,7 @@ mips_zero_extend_cost (enum machine_mode mode, rtx op)
 /* Implement TARGET_RTX_COSTS.  */
 
 static bool
-mips_rtx_costs (rtx x, int code, int outer_code, int *total,
-               bool speed)
+mips_rtx_costs (rtx x, int code, int outer_code, int *total, bool speed)
 {
   enum machine_mode mode = GET_MODE (x);
   bool float_mode_p = FLOAT_MODE_P (mode);
@@ -3442,8 +3579,7 @@ mips_rtx_costs (rtx x, int code, int outer_code, int *total,
             operand needs to be forced into a register, we will often be
             able to hoist the constant load out of the loop, so the load
             should not contribute to the cost.  */
-         if (!optimize_size
-             || mips_immediate_operand_p (outer_code, INTVAL (x)))
+         if (speed || mips_immediate_operand_p (outer_code, INTVAL (x)))
            {
              *total = 0;
              return true;
@@ -3541,7 +3677,8 @@ mips_rtx_costs (rtx x, int code, int outer_code, int *total,
     case IOR:
     case XOR:
       /* Double-word operations use two single-word operations.  */
-      *total = mips_binary_cost (x, COSTS_N_INSNS (1), COSTS_N_INSNS (2));
+      *total = mips_binary_cost (x, COSTS_N_INSNS (1), COSTS_N_INSNS (2),
+                                speed);
       return true;
 
     case ASHIFT:
@@ -3550,9 +3687,11 @@ mips_rtx_costs (rtx x, int code, int outer_code, int *total,
     case ROTATE:
     case ROTATERT:
       if (CONSTANT_P (XEXP (x, 1)))
-       *total = mips_binary_cost (x, COSTS_N_INSNS (1), COSTS_N_INSNS (4));
+       *total = mips_binary_cost (x, COSTS_N_INSNS (1), COSTS_N_INSNS (4),
+                                  speed);
       else
-       *total = mips_binary_cost (x, COSTS_N_INSNS (1), COSTS_N_INSNS (12));
+       *total = mips_binary_cost (x, COSTS_N_INSNS (1), COSTS_N_INSNS (12),
+                                  speed);
       return true;
 
     case ABS:
@@ -3588,7 +3727,8 @@ mips_rtx_costs (rtx x, int code, int outer_code, int *total,
          *total = mips_cost->fp_add;
          return false;
        }
-      *total = mips_binary_cost (x, COSTS_N_INSNS (1), COSTS_N_INSNS (4));
+      *total = mips_binary_cost (x, COSTS_N_INSNS (1), COSTS_N_INSNS (4),
+                                speed);
       return true;
 
     case MINUS:
@@ -3639,7 +3779,8 @@ mips_rtx_costs (rtx x, int code, int outer_code, int *total,
         an SLTU.  The MIPS16 version then needs to move the result of
         the SLTU from $24 to a MIPS16 register.  */
       *total = mips_binary_cost (x, COSTS_N_INSNS (1),
-                                COSTS_N_INSNS (TARGET_MIPS16 ? 5 : 4));
+                                COSTS_N_INSNS (TARGET_MIPS16 ? 5 : 4),
+                                speed);
       return true;
 
     case NEG:
@@ -3675,10 +3816,10 @@ mips_rtx_costs (rtx x, int code, int outer_code, int *total,
       else if (mode == DImode && !TARGET_64BIT)
        /* Synthesized from 2 mulsi3s, 1 mulsidi3 and two additions,
           where the mulsidi3 always includes an MFHI and an MFLO.  */
-       *total = (optimize_size
-                 ? COSTS_N_INSNS (ISA_HAS_MUL3 ? 7 : 9)
-                 : mips_cost->int_mult_si * 3 + 6);
-      else if (optimize_size)
+       *total = (speed
+                 ? mips_cost->int_mult_si * 3 + 6
+                 : COSTS_N_INSNS (ISA_HAS_MUL3 ? 7 : 9));
+      else if (!speed)
        *total = (ISA_HAS_MUL3 ? 1 : 2);
       else if (mode == DImode)
        *total = mips_cost->int_mult_di;
@@ -3715,7 +3856,7 @@ mips_rtx_costs (rtx x, int code, int outer_code, int *total,
 
     case UDIV:
     case UMOD:
-      if (optimize_size)
+      if (!speed)
        {
          /* It is our responsibility to make division by a power of 2
             as cheap as 2 register additions if we want the division
@@ -3767,6 +3908,130 @@ mips_address_cost (rtx addr, bool speed ATTRIBUTE_UNUSED)
   return mips_address_insns (addr, SImode, false);
 }
 \f
+/* Information about a single instruction in a multi-instruction
+   asm sequence.  */
+struct mips_multi_member {
+  /* True if this is a label, false if it is code.  */
+  bool is_label_p;
+
+  /* The output_asm_insn format of the instruction.  */
+  const char *format;
+
+  /* The operands to the instruction.  */
+  rtx operands[MAX_RECOG_OPERANDS];
+};
+typedef struct mips_multi_member mips_multi_member;
+
+/* Vector definitions for the above.  */
+DEF_VEC_O(mips_multi_member);
+DEF_VEC_ALLOC_O(mips_multi_member, heap);
+
+/* The instructions that make up the current multi-insn sequence.  */
+static VEC (mips_multi_member, heap) *mips_multi_members;
+
+/* How many instructions (as opposed to labels) are in the current
+   multi-insn sequence.  */
+static unsigned int mips_multi_num_insns;
+
+/* Start a new multi-insn sequence.  */
+
+static void
+mips_multi_start (void)
+{
+  VEC_truncate (mips_multi_member, mips_multi_members, 0);
+  mips_multi_num_insns = 0;
+}
+
+/* Add a new, uninitialized member to the current multi-insn sequence.  */
+
+static struct mips_multi_member *
+mips_multi_add (void)
+{
+  return VEC_safe_push (mips_multi_member, heap, mips_multi_members, 0);
+}
+
+/* Add a normal insn with the given asm format to the current multi-insn
+   sequence.  The other arguments are a null-terminated list of operands.  */
+
+static void
+mips_multi_add_insn (const char *format, ...)
+{
+  struct mips_multi_member *member;
+  va_list ap;
+  unsigned int i;
+  rtx op;
+
+  member = mips_multi_add ();
+  member->is_label_p = false;
+  member->format = format;
+  va_start (ap, format);
+  i = 0;
+  while ((op = va_arg (ap, rtx)))
+    member->operands[i++] = op;
+  va_end (ap);
+  mips_multi_num_insns++;
+}
+
+/* Add the given label definition to the current multi-insn sequence.
+   The definition should include the colon.  */
+
+static void
+mips_multi_add_label (const char *label)
+{
+  struct mips_multi_member *member;
+
+  member = mips_multi_add ();
+  member->is_label_p = true;
+  member->format = label;
+}
+
+/* Return the index of the last member of the current multi-insn sequence.  */
+
+static unsigned int
+mips_multi_last_index (void)
+{
+  return VEC_length (mips_multi_member, mips_multi_members) - 1;
+}
+
+/* Add a copy of an existing instruction to the current multi-insn
+   sequence.  I is the index of the instruction that should be copied.  */
+
+static void
+mips_multi_copy_insn (unsigned int i)
+{
+  struct mips_multi_member *member;
+
+  member = mips_multi_add ();
+  memcpy (member, VEC_index (mips_multi_member, mips_multi_members, i),
+         sizeof (*member));
+  gcc_assert (!member->is_label_p);
+}
+
+/* Change the operand of an existing instruction in the current
+   multi-insn sequence.  I is the index of the instruction,
+   OP is the index of the operand, and X is the new value.  */
+
+static void
+mips_multi_set_operand (unsigned int i, unsigned int op, rtx x)
+{
+  VEC_index (mips_multi_member, mips_multi_members, i)->operands[op] = x;
+}
+
+/* Write out the asm code for the current multi-insn sequence.  */
+
+static void
+mips_multi_write (void)
+{
+  struct mips_multi_member *member;
+  unsigned int i;
+
+  FOR_EACH_VEC_ELT (mips_multi_member, mips_multi_members, i, member)
+    if (member->is_label_p)
+      fprintf (asm_out_file, "%s\n", member->format);
+    else
+      output_asm_insn (member->format, member->operands);
+}
+\f
 /* Return one word of double-word value OP, taking into account the fixed
    endianness of certain registers.  HIGH_P is true to select the high part,
    false to select the low part.  */
@@ -4117,7 +4382,7 @@ mips_canonicalize_int_order_test (enum rtx_code *code, rtx *cmp1,
   if (mips_int_order_operand_ok_p (*code, *cmp1))
     return true;
 
-  if (GET_CODE (*cmp1) == CONST_INT)
+  if (CONST_INT_P (*cmp1))
     switch (*code)
       {
       case LE:
@@ -4226,8 +4491,8 @@ mips_reversed_fp_cond (enum rtx_code *code)
 }
 
 /* Convert a comparison into something that can be used in a branch or
-   conditional move.  cmp_operands[0] and cmp_operands[1] are the values
-   being compared and *CODE is the code used to compare them.
+   conditional move.  On entry, *OP0 and *OP1 are the values being
+   compared and *CODE is the code used to compare them.
 
    Update *CODE, *OP0 and *OP1 so that they describe the final comparison.
    If NEED_EQ_NE_P, then only EQ or NE comparisons against zero are possible,
@@ -4240,42 +4505,38 @@ mips_reversed_fp_cond (enum rtx_code *code)
 static void
 mips_emit_compare (enum rtx_code *code, rtx *op0, rtx *op1, bool need_eq_ne_p)
 {
-  if (GET_MODE_CLASS (GET_MODE (cmp_operands[0])) == MODE_INT)
+  rtx cmp_op0 = *op0;
+  rtx cmp_op1 = *op1;
+
+  if (GET_MODE_CLASS (GET_MODE (*op0)) == MODE_INT)
     {
-      if (!need_eq_ne_p && cmp_operands[1] == const0_rtx)
-       {
-         *op0 = cmp_operands[0];
-         *op1 = cmp_operands[1];
-       }
+      if (!need_eq_ne_p && *op1 == const0_rtx)
+       ;
       else if (*code == EQ || *code == NE)
        {
          if (need_eq_ne_p)
            {
-             *op0 = mips_zero_if_equal (cmp_operands[0], cmp_operands[1]);
+             *op0 = mips_zero_if_equal (cmp_op0, cmp_op1);
              *op1 = const0_rtx;
            }
          else
-           {
-             *op0 = cmp_operands[0];
-             *op1 = force_reg (GET_MODE (*op0), cmp_operands[1]);
-           }
+           *op1 = force_reg (GET_MODE (cmp_op0), cmp_op1);
        }
       else
        {
          /* The comparison needs a separate scc instruction.  Store the
             result of the scc in *OP0 and compare it against zero.  */
          bool invert = false;
-         *op0 = gen_reg_rtx (GET_MODE (cmp_operands[0]));
-         mips_emit_int_order_test (*code, &invert, *op0,
-                                   cmp_operands[0], cmp_operands[1]);
+         *op0 = gen_reg_rtx (GET_MODE (cmp_op0));
+         mips_emit_int_order_test (*code, &invert, *op0, cmp_op0, cmp_op1);
          *code = (invert ? EQ : NE);
          *op1 = const0_rtx;
        }
     }
-  else if (ALL_FIXED_POINT_MODE_P (GET_MODE (cmp_operands[0])))
+  else if (ALL_FIXED_POINT_MODE_P (GET_MODE (cmp_op0)))
     {
       *op0 = gen_rtx_REG (CCDSPmode, CCDSP_CC_REGNUM);
-      mips_emit_binary (*code, *op0, cmp_operands[0], cmp_operands[1]);
+      mips_emit_binary (*code, *op0, cmp_op0, cmp_op1);
       *code = NE;
       *op1 = const0_rtx;
     }
@@ -4295,49 +4556,55 @@ mips_emit_compare (enum rtx_code *code, rtx *op0, rtx *op1, bool need_eq_ne_p)
              ? gen_reg_rtx (CCmode)
              : gen_rtx_REG (CCmode, FPSW_REGNUM));
       *op1 = const0_rtx;
-      mips_emit_binary (cmp_code, *op0, cmp_operands[0], cmp_operands[1]);
+      mips_emit_binary (cmp_code, *op0, cmp_op0, cmp_op1);
     }
 }
 \f
-/* Try comparing cmp_operands[0] and cmp_operands[1] using rtl code CODE.
-   Store the result in TARGET and return true if successful.
+/* Try performing the comparison in OPERANDS[1], whose arms are OPERANDS[2]
+   and OPERAND[3].  Store the result in OPERANDS[0].
 
-   On 64-bit targets, TARGET may be narrower than cmp_operands[0].  */
+   On 64-bit targets, the mode of the comparison and target will always be
+   SImode, thus possibly narrower than that of the comparison's operands.  */
 
-bool
-mips_expand_scc (enum rtx_code code, rtx target)
+void
+mips_expand_scc (rtx operands[])
 {
-  if (GET_MODE_CLASS (GET_MODE (cmp_operands[0])) != MODE_INT)
-    return false;
+  rtx target = operands[0];
+  enum rtx_code code = GET_CODE (operands[1]);
+  rtx op0 = operands[2];
+  rtx op1 = operands[3];
+
+  gcc_assert (GET_MODE_CLASS (GET_MODE (op0)) == MODE_INT);
 
   if (code == EQ || code == NE)
     {
       if (ISA_HAS_SEQ_SNE
-         && reg_imm10_operand (cmp_operands[1], GET_MODE (cmp_operands[1])))
-       mips_emit_binary (code, target, cmp_operands[0], cmp_operands[1]);
+         && reg_imm10_operand (op1, GET_MODE (op1)))
+       mips_emit_binary (code, target, op0, op1);
       else
        {
-         rtx zie = mips_zero_if_equal (cmp_operands[0], cmp_operands[1]);
+         rtx zie = mips_zero_if_equal (op0, op1);
          mips_emit_binary (code, target, zie, const0_rtx);
        }
     }
   else
-    mips_emit_int_order_test (code, 0, target,
-                             cmp_operands[0], cmp_operands[1]);
-  return true;
+    mips_emit_int_order_test (code, 0, target, op0, op1);
 }
 
-/* Compare cmp_operands[0] with cmp_operands[1] using comparison code
-   CODE and jump to OPERANDS[0] if the condition holds.  */
+/* Compare OPERANDS[1] with OPERANDS[2] using comparison code
+   CODE and jump to OPERANDS[3] if the condition holds.  */
 
 void
-mips_expand_conditional_branch (rtx *operands, enum rtx_code code)
+mips_expand_conditional_branch (rtx *operands)
 {
-  rtx op0, op1, condition;
+  enum rtx_code code = GET_CODE (operands[0]);
+  rtx op0 = operands[1];
+  rtx op1 = operands[2];
+  rtx condition;
 
   mips_emit_compare (&code, &op0, &op1, TARGET_MIPS16);
   condition = gen_rtx_fmt_ee (code, VOIDmode, op0, op1);
-  emit_jump_insn (gen_condjump (condition, operands[0]));
+  emit_jump_insn (gen_condjump (condition, operands[3]));
 }
 
 /* Implement:
@@ -4364,35 +4631,36 @@ mips_expand_vcondv2sf (rtx dest, rtx true_src, rtx false_src,
                                         cmp_result));
 }
 
-/* Compare cmp_operands[0] with cmp_operands[1] using the code of
-   OPERANDS[1].  Move OPERANDS[2] into OPERANDS[0] if the condition
-   holds, otherwise move OPERANDS[3] into OPERANDS[0].  */
+/* Perform the comparison in OPERANDS[1].  Move OPERANDS[2] into OPERANDS[0]
+   if the condition holds, otherwise move OPERANDS[3] into OPERANDS[0].  */
 
 void
 mips_expand_conditional_move (rtx *operands)
 {
-  enum rtx_code code;
-  rtx cond, op0, op1;
+  rtx cond;
+  enum rtx_code code = GET_CODE (operands[1]);
+  rtx op0 = XEXP (operands[1], 0);
+  rtx op1 = XEXP (operands[1], 1);
 
-  code = GET_CODE (operands[1]);
   mips_emit_compare (&code, &op0, &op1, true);
-  cond = gen_rtx_fmt_ee (code, GET_MODE (op0), op0, op1),
+  cond = gen_rtx_fmt_ee (code, GET_MODE (op0), op0, op1);
   emit_insn (gen_rtx_SET (VOIDmode, operands[0],
                          gen_rtx_IF_THEN_ELSE (GET_MODE (operands[0]), cond,
                                                operands[2], operands[3])));
 }
 
-/* Compare cmp_operands[0] with cmp_operands[1] using rtl code CODE,
-   then trap if the condition holds.  */
+/* Perform the comparison in COMPARISON, then trap if the condition holds.  */
 
 void
-mips_expand_conditional_trap (enum rtx_code code)
+mips_expand_conditional_trap (rtx comparison)
 {
   rtx op0, op1;
   enum machine_mode mode;
+  enum rtx_code code;
 
   /* MIPS conditional trap instructions don't have GT or LE flavors,
      so we must swap the operands and convert to LT and GE respectively.  */
+  code = GET_CODE (comparison);
   switch (code)
     {
     case GT:
@@ -4400,17 +4668,17 @@ mips_expand_conditional_trap (enum rtx_code code)
     case GTU:
     case LEU:
       code = swap_condition (code);
-      op0 = cmp_operands[1];
-      op1 = cmp_operands[0];
+      op0 = XEXP (comparison, 1);
+      op1 = XEXP (comparison, 0);
       break;
 
     default:
-      op0 = cmp_operands[0];
-      op1 = cmp_operands[1];
+      op0 = XEXP (comparison, 0);
+      op1 = XEXP (comparison, 1);
       break;
     }
 
-  mode = GET_MODE (cmp_operands[0]);
+  mode = GET_MODE (XEXP (comparison, 0));
   op0 = force_reg (mode, op0);
   if (!arith_operand (op1, mode))
     op1 = force_reg (mode, op1);
@@ -4437,7 +4705,7 @@ mips_init_cumulative_args (CUMULATIVE_ARGS *cum, tree fntype)
 
 static void
 mips_get_arg_info (struct mips_arg_info *info, const CUMULATIVE_ARGS *cum,
-                  enum machine_mode mode, tree type, int named)
+                  enum machine_mode mode, const_tree type, bool named)
 {
   bool doubleword_aligned_p;
   unsigned int num_bytes, num_words, max_regs;
@@ -4570,11 +4838,11 @@ mips_strict_argument_naming (CUMULATIVE_ARGS *ca ATTRIBUTE_UNUSED)
   return !TARGET_OLDABI;
 }
 
-/* Implement FUNCTION_ARG.  */
+/* Implement TARGET_FUNCTION_ARG.  */
 
-rtx
-mips_function_arg (const CUMULATIVE_ARGS *cum, enum machine_mode mode,
-                  tree type, int named)
+static rtx
+mips_function_arg (CUMULATIVE_ARGS *cum, enum machine_mode mode,
+                  const_tree type, bool named)
 {
   struct mips_arg_info info;
 
@@ -4610,7 +4878,7 @@ mips_function_arg (const CUMULATIVE_ARGS *cum, enum machine_mode mode,
       tree field;
 
       /* First check to see if there is any such field.  */
-      for (field = TYPE_FIELDS (type); field; field = TREE_CHAIN (field))
+      for (field = TYPE_FIELDS (type); field; field = DECL_CHAIN (field))
        if (TREE_CODE (field) == FIELD_DECL
            && SCALAR_FLOAT_TYPE_P (TREE_TYPE (field))
            && TYPE_PRECISION (TREE_TYPE (field)) == BITS_PER_WORD
@@ -4637,7 +4905,7 @@ mips_function_arg (const CUMULATIVE_ARGS *cum, enum machine_mode mode,
            {
              rtx reg;
 
-             for (; field; field = TREE_CHAIN (field))
+             for (; field; field = DECL_CHAIN (field))
                if (TREE_CODE (field) == FIELD_DECL
                    && int_bit_position (field) >= bitpos)
                  break;
@@ -4696,11 +4964,11 @@ mips_function_arg (const CUMULATIVE_ARGS *cum, enum machine_mode mode,
   return gen_rtx_REG (mode, mips_arg_regno (&info, TARGET_HARD_FLOAT));
 }
 
-/* Implement FUNCTION_ARG_ADVANCE.  */
+/* Implement TARGET_FUNCTION_ARG_ADVANCE.  */
 
-void
+static void
 mips_function_arg_advance (CUMULATIVE_ARGS *cum, enum machine_mode mode,
-                          tree type, int named)
+                          const_tree type, bool named)
 {
   struct mips_arg_info info;
 
@@ -4749,7 +5017,7 @@ mips_arg_partial_bytes (CUMULATIVE_ARGS *cum,
    to STACK_BOUNDARY bits if the type requires it.  */
 
 int
-mips_function_arg_boundary (enum machine_mode mode, tree type)
+mips_function_arg_boundary (enum machine_mode mode, const_tree type)
 {
   unsigned int alignment;
 
@@ -4875,7 +5143,7 @@ mips_fpr_return_fields (const_tree valtype, tree *fields)
     return 0;
 
   i = 0;
-  for (field = TYPE_FIELDS (valtype); field != 0; field = TREE_CHAIN (field))
+  for (field = TYPE_FIELDS (valtype); field != 0; field = DECL_CHAIN (field))
     {
       if (TREE_CODE (field) != FIELD_DECL)
        continue;
@@ -4982,7 +5250,7 @@ mips_return_fpr_pair (enum machine_mode mode,
    VALTYPE is null and MODE is the mode of the return value.  */
 
 rtx
-mips_function_value (const_tree valtype, enum machine_mode mode)
+mips_function_value (const_tree valtype, const_tree func, enum machine_mode mode)
 {
   if (valtype)
     {
@@ -4992,9 +5260,9 @@ mips_function_value (const_tree valtype, enum machine_mode mode)
       mode = TYPE_MODE (valtype);
       unsigned_p = TYPE_UNSIGNED (valtype);
 
-      /* Since TARGET_PROMOTE_FUNCTION_RETURN unconditionally returns true,
-        we must promote the mode just as PROMOTE_MODE does.  */
-      mode = promote_mode (valtype, mode, &unsigned_p, 1);
+      /* Since TARGET_PROMOTE_FUNCTION_MODE unconditionally promotes,
+        return values, promote the mode here too.  */
+      mode = promote_function_mode (valtype, mode, &unsigned_p, func, 1);
 
       /* Handle structures whose fields are returned in $f0/$f2.  */
       switch (mips_fpr_return_fields (valtype, fields))
@@ -5082,7 +5350,7 @@ mips_setup_incoming_varargs (CUMULATIVE_ARGS *cum, enum machine_mode mode,
      argument.  Advance a local copy of CUM past the last "real" named
      argument, to find out how many registers are left over.  */
   local_cum = *cum;
-  FUNCTION_ARG_ADVANCE (local_cum, mode, type, true);
+  mips_function_arg_advance (&local_cum, mode, type, true);
 
   /* Found out how many registers we need to save.  */
   gp_saved = MAX_ARGS_IN_REGISTERS - local_cum.num_gprs;
@@ -5166,22 +5434,28 @@ mips_build_builtin_va_list (void)
 
       record = lang_hooks.types.make_type (RECORD_TYPE);
 
-      f_ovfl = build_decl (FIELD_DECL, get_identifier ("__overflow_argptr"),
+      f_ovfl = build_decl (BUILTINS_LOCATION,
+                          FIELD_DECL, get_identifier ("__overflow_argptr"),
                           ptr_type_node);
-      f_gtop = build_decl (FIELD_DECL, get_identifier ("__gpr_top"),
+      f_gtop = build_decl (BUILTINS_LOCATION,
+                          FIELD_DECL, get_identifier ("__gpr_top"),
                           ptr_type_node);
-      f_ftop = build_decl (FIELD_DECL, get_identifier ("__fpr_top"),
+      f_ftop = build_decl (BUILTINS_LOCATION,
+                          FIELD_DECL, get_identifier ("__fpr_top"),
                           ptr_type_node);
-      f_goff = build_decl (FIELD_DECL, get_identifier ("__gpr_offset"),
+      f_goff = build_decl (BUILTINS_LOCATION,
+                          FIELD_DECL, get_identifier ("__gpr_offset"),
                           unsigned_char_type_node);
-      f_foff = build_decl (FIELD_DECL, get_identifier ("__fpr_offset"),
+      f_foff = build_decl (BUILTINS_LOCATION,
+                          FIELD_DECL, get_identifier ("__fpr_offset"),
                           unsigned_char_type_node);
       /* Explicitly pad to the size of a pointer, so that -Wpadded won't
         warn on every user file.  */
       index = build_int_cst (NULL_TREE, GET_MODE_SIZE (ptr_mode) - 2 - 1);
       array = build_array_type (unsigned_char_type_node,
                                build_index_type (index));
-      f_res = build_decl (FIELD_DECL, get_identifier ("__reserved"), array);
+      f_res = build_decl (BUILTINS_LOCATION,
+                         FIELD_DECL, get_identifier ("__reserved"), array);
 
       DECL_FIELD_CONTEXT (f_ovfl) = record;
       DECL_FIELD_CONTEXT (f_gtop) = record;
@@ -5191,16 +5465,16 @@ mips_build_builtin_va_list (void)
       DECL_FIELD_CONTEXT (f_res) = record;
 
       TYPE_FIELDS (record) = f_ovfl;
-      TREE_CHAIN (f_ovfl) = f_gtop;
-      TREE_CHAIN (f_gtop) = f_ftop;
-      TREE_CHAIN (f_ftop) = f_goff;
-      TREE_CHAIN (f_goff) = f_foff;
-      TREE_CHAIN (f_foff) = f_res;
+      DECL_CHAIN (f_ovfl) = f_gtop;
+      DECL_CHAIN (f_gtop) = f_ftop;
+      DECL_CHAIN (f_ftop) = f_goff;
+      DECL_CHAIN (f_goff) = f_foff;
+      DECL_CHAIN (f_foff) = f_res;
 
       layout_type (record);
       return record;
     }
-  else if (TARGET_IRIX && TARGET_IRIX6)
+  else if (TARGET_IRIX6)
     /* On IRIX 6, this type is 'char *'.  */
     return build_pointer_type (char_type_node);
   else
@@ -5230,10 +5504,10 @@ mips_va_start (tree valist, rtx nextarg)
        = (MAX_ARGS_IN_REGISTERS - cum->num_fprs) * UNITS_PER_FPREG;
 
       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);
+      f_gtop = DECL_CHAIN (f_ovfl);
+      f_ftop = DECL_CHAIN (f_gtop);
+      f_goff = DECL_CHAIN (f_ftop);
+      f_foff = DECL_CHAIN (f_goff);
 
       ovfl = build3 (COMPONENT_REF, TREE_TYPE (f_ovfl), valist, f_ovfl,
                     NULL_TREE);
@@ -5315,10 +5589,10 @@ mips_gimplify_va_arg_expr (tree valist, tree type, gimple_seq *pre_p,
       tree t, u;
 
       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);
+      f_gtop = DECL_CHAIN (f_ovfl);
+      f_ftop = DECL_CHAIN (f_gtop);
+      f_goff = DECL_CHAIN (f_ftop);
+      f_foff = DECL_CHAIN (f_goff);
 
       /* Let:
 
@@ -5603,7 +5877,7 @@ mips16_local_alias (rtx func)
       SYMBOL_REF_FLAGS (local) = SYMBOL_REF_FLAGS (func) | SYMBOL_FLAG_LOCAL;
 
       /* Create a new structure to represent the mapping.  */
-      alias = GGC_NEW (struct mips16_local_alias);
+      alias = ggc_alloc_mips16_local_alias ();
       alias->func = func;
       alias->local = local;
       *slot = alias;
@@ -5755,10 +6029,12 @@ mips16_build_function_stub (void)
   stubname = ACONCAT (("__fn_stub_", fnname, NULL));
 
   /* Build a decl for the stub.  */
-  stubdecl = build_decl (FUNCTION_DECL, get_identifier (stubname),
+  stubdecl = build_decl (BUILTINS_LOCATION,
+                        FUNCTION_DECL, get_identifier (stubname),
                         build_function_type (void_type_node, NULL_TREE));
   DECL_SECTION_NAME (stubdecl) = build_string (strlen (secname), secname);
-  DECL_RESULT (stubdecl) = build_decl (RESULT_DECL, NULL_TREE, void_type_node);
+  DECL_RESULT (stubdecl) = build_decl (BUILTINS_LOCATION,
+                                      RESULT_DECL, NULL_TREE, void_type_node);
 
   /* Output a comment.  */
   fprintf (asm_out_file, "\t# Stub function for %s (",
@@ -5858,7 +6134,7 @@ mips16_copy_fpr_return_value (void)
    RETVAL is the location of the return value, or null if this is
    a "call" rather than a "call_value".  ARGS_SIZE is the size of the
    arguments and FP_CODE is the code built by mips_function_arg;
-   see the comment above CUMULATIVE_ARGS for details.
+   see the comment before the fp_code field in CUMULATIVE_ARGS for details.
 
    There are three alternatives:
 
@@ -5999,10 +6275,12 @@ mips16_build_call_stub (rtx retval, rtx *fn_ptr, rtx args_size, int fp_code)
       stubname = ACONCAT (("__call_stub_", fp_ret_p ? "fp_" : "",
                           fnname, NULL));
       stubid = get_identifier (stubname);
-      stubdecl = build_decl (FUNCTION_DECL, stubid,
+      stubdecl = build_decl (BUILTINS_LOCATION,
+                            FUNCTION_DECL, stubid,
                             build_function_type (void_type_node, NULL_TREE));
       DECL_SECTION_NAME (stubdecl) = build_string (strlen (secname), secname);
-      DECL_RESULT (stubdecl) = build_decl (RESULT_DECL, NULL_TREE,
+      DECL_RESULT (stubdecl) = build_decl (BUILTINS_LOCATION,
+                                          RESULT_DECL, NULL_TREE,
                                           void_type_node);
 
       /* Output a comment.  */
@@ -6053,27 +6331,36 @@ mips16_build_call_stub (rtx retval, rtx *fn_ptr, rtx args_size, int fp_code)
             The stub's caller knows that $18 might be clobbered, even though
             $18 is usually a call-saved register.  */
          fprintf (asm_out_file, "\tmove\t%s,%s\n",
-                  reg_names[GP_REG_FIRST + 18], reg_names[GP_REG_FIRST + 31]);
-         output_asm_insn (MIPS_CALL ("jal", &fn, 0), &fn);
+                  reg_names[GP_REG_FIRST + 18], reg_names[RETURN_ADDR_REGNUM]);
+         output_asm_insn (MIPS_CALL ("jal", &fn, 0, -1), &fn);
 
          /* Move the result from floating-point registers to
             general registers.  */
          switch (GET_MODE (retval))
            {
            case SCmode:
-             mips_output_32bit_xfer ('f', GP_RETURN + 1,
-                                     FP_REG_FIRST + MAX_FPRS_PER_FMT);
-             /* Fall though.  */
-           case SFmode:
-             mips_output_32bit_xfer ('f', GP_RETURN, FP_REG_FIRST);
+             mips_output_32bit_xfer ('f', GP_RETURN + TARGET_BIG_ENDIAN,
+                                     TARGET_BIG_ENDIAN
+                                     ? FP_REG_FIRST + MAX_FPRS_PER_FMT
+                                     : FP_REG_FIRST);
+             mips_output_32bit_xfer ('f', GP_RETURN + TARGET_LITTLE_ENDIAN,
+                                     TARGET_LITTLE_ENDIAN
+                                     ? FP_REG_FIRST + MAX_FPRS_PER_FMT
+                                     : FP_REG_FIRST);
              if (GET_MODE (retval) == SCmode && TARGET_64BIT)
                {
                  /* On 64-bit targets, complex floats are returned in
                     a single GPR, such that "sd" on a suitably-aligned
                     target would store the value correctly.  */
                  fprintf (asm_out_file, "\tdsll\t%s,%s,32\n",
+                          reg_names[GP_RETURN + TARGET_BIG_ENDIAN],
+                          reg_names[GP_RETURN + TARGET_BIG_ENDIAN]);
+                 fprintf (asm_out_file, "\tdsll\t%s,%s,32\n",
                           reg_names[GP_RETURN + TARGET_LITTLE_ENDIAN],
                           reg_names[GP_RETURN + TARGET_LITTLE_ENDIAN]);
+                 fprintf (asm_out_file, "\tdsrl\t%s,%s,32\n",
+                          reg_names[GP_RETURN + TARGET_BIG_ENDIAN],
+                          reg_names[GP_RETURN + TARGET_BIG_ENDIAN]);
                  fprintf (asm_out_file, "\tor\t%s,%s,%s\n",
                           reg_names[GP_RETURN],
                           reg_names[GP_RETURN],
@@ -6081,6 +6368,10 @@ mips16_build_call_stub (rtx retval, rtx *fn_ptr, rtx args_size, int fp_code)
                }
              break;
 
+           case SFmode:
+             mips_output_32bit_xfer ('f', GP_RETURN, FP_REG_FIRST);
+             break;
+
            case DCmode:
              mips_output_64bit_xfer ('f', GP_RETURN + (8 / UNITS_PER_WORD),
                                      FP_REG_FIRST + MAX_FPRS_PER_FMT);
@@ -6178,9 +6469,7 @@ mips_expand_call (enum mips_call_type type, rtx result, rtx addr,
     {
       rtx (*fn) (rtx, rtx);
 
-      if (type == MIPS_CALL_EPILOGUE && TARGET_SPLIT_CALLS)
-       fn = gen_call_split;
-      else if (type == MIPS_CALL_SIBCALL)
+      if (type == MIPS_CALL_SIBCALL)
        fn = gen_sibcall_internal;
       else
        fn = gen_call_internal;
@@ -6193,9 +6482,7 @@ mips_expand_call (enum mips_call_type type, rtx result, rtx addr,
       rtx (*fn) (rtx, rtx, rtx, rtx);
       rtx reg1, reg2;
 
-      if (type == MIPS_CALL_EPILOGUE && TARGET_SPLIT_CALLS)
-       fn = gen_call_value_multiple_split;
-      else if (type == MIPS_CALL_SIBCALL)
+      if (type == MIPS_CALL_SIBCALL)
        fn = gen_sibcall_value_multiple_internal;
       else
        fn = gen_call_value_multiple_internal;
@@ -6208,9 +6495,7 @@ mips_expand_call (enum mips_call_type type, rtx result, rtx addr,
     {
       rtx (*fn) (rtx, rtx, rtx);
 
-      if (type == MIPS_CALL_EPILOGUE && TARGET_SPLIT_CALLS)
-       fn = gen_call_value_split;
-      else if (type == MIPS_CALL_SIBCALL)
+      if (type == MIPS_CALL_SIBCALL)
        fn = gen_sibcall_value_internal;
       else
        fn = gen_call_value_internal;
@@ -6240,7 +6525,7 @@ mips_split_call (rtx insn, rtx call_pattern)
     /* Pick a temporary register that is suitable for both MIPS16 and
        non-MIPS16 code.  $4 and $5 are used for returning complex double
        values in soft-float code, so $6 is the first suitable candidate.  */
-    mips_restore_gp (gen_rtx_REG (Pmode, GP_ARG_FIRST + 2));
+    mips_restore_gp_from_cprestore_slot (gen_rtx_REG (Pmode, GP_ARG_FIRST + 2));
 }
 
 /* Implement TARGET_FUNCTION_OK_FOR_SIBCALL.  */
@@ -6401,7 +6686,7 @@ static void
 mips_block_move_loop (rtx dest, rtx src, HOST_WIDE_INT length,
                      HOST_WIDE_INT bytes_per_iter)
 {
-  rtx label, src_reg, dest_reg, final_src;
+  rtx label, src_reg, dest_reg, final_src, test;
   HOST_WIDE_INT leftover;
 
   leftover = length % bytes_per_iter;
@@ -6428,11 +6713,11 @@ mips_block_move_loop (rtx dest, rtx src, HOST_WIDE_INT length,
   mips_emit_move (dest_reg, plus_constant (dest_reg, bytes_per_iter));
 
   /* Emit the loop condition.  */
+  test = gen_rtx_NE (VOIDmode, src_reg, final_src);
   if (Pmode == DImode)
-    emit_insn (gen_cmpdi (src_reg, final_src));
+    emit_jump_insn (gen_cbranchdi4 (test, src_reg, final_src, label));
   else
-    emit_insn (gen_cmpsi (src_reg, final_src));
-  emit_jump_insn (gen_bne (label));
+    emit_jump_insn (gen_cbranchsi4 (test, src_reg, final_src, label));
 
   /* Mop up any left-over bytes.  */
   if (leftover)
@@ -6445,7 +6730,7 @@ mips_block_move_loop (rtx dest, rtx src, HOST_WIDE_INT length,
 bool
 mips_expand_block_move (rtx dest, rtx src, rtx length)
 {
-  if (GET_CODE (length) == CONST_INT)
+  if (CONST_INT_P (length))
     {
       if (INTVAL (length) <= MIPS_MAX_MOVE_BYTES_STRAIGHT)
        {
@@ -6467,7 +6752,14 @@ mips_expand_block_move (rtx dest, rtx src, rtx length)
 void
 mips_expand_synci_loop (rtx begin, rtx end)
 {
-  rtx inc, label, cmp, cmp_result;
+  rtx inc, label, end_label, cmp_result, mask, length;
+
+  /* Create end_label.  */
+  end_label = gen_label_rtx ();
+
+  /* Check if begin equals end.  */
+  cmp_result = gen_rtx_EQ (VOIDmode, begin, end);
+  emit_jump_insn (gen_condjump (cmp_result, end_label));
 
   /* Load INC with the cache line size (rdhwr INC,$1).  */
   inc = gen_reg_rtx (Pmode);
@@ -6475,18 +6767,36 @@ mips_expand_synci_loop (rtx begin, rtx end)
             ? gen_rdhwr_synci_step_si (inc)
             : gen_rdhwr_synci_step_di (inc));
 
+  /* Check if inc is 0.  */
+  cmp_result = gen_rtx_EQ (VOIDmode, inc, const0_rtx);
+  emit_jump_insn (gen_condjump (cmp_result, end_label));
+
+  /* Calculate mask.  */
+  mask = mips_force_unary (Pmode, NEG, inc);
+
+  /* Mask out begin by mask.  */
+  begin = mips_force_binary (Pmode, AND, begin, mask);
+
+  /* Calculate length.  */
+  length = mips_force_binary (Pmode, MINUS, end, begin);
+
   /* Loop back to here.  */
   label = gen_label_rtx ();
   emit_label (label);
 
   emit_insn (gen_synci (begin));
 
-  cmp = mips_force_binary (Pmode, GTU, begin, end);
+  /* Update length.  */
+  mips_emit_binary (MINUS, length, length, inc);
 
+  /* Update begin.  */
   mips_emit_binary (PLUS, begin, begin, inc);
 
-  cmp_result = gen_rtx_EQ (VOIDmode, cmp, const0_rtx);
+  /* Check if length is greater than 0.  */
+  cmp_result = gen_rtx_GT (VOIDmode, length, const0_rtx);
   emit_jump_insn (gen_condjump (cmp_result, label));
+
+  emit_label (end_label);
 }
 \f
 /* Expand a QI or HI mode atomic memory operation.
@@ -6777,6 +7087,18 @@ mask_low_and_shift_p (enum machine_mode mode, rtx mask, rtx shift, int maxlen)
   return IN_RANGE (mask_low_and_shift_len (mode, mask, shift), 1, maxlen);
 }
 
+/* Return true iff OP1 and OP2 are valid operands together for the
+   *and<MODE>3 and *and<MODE>3_mips16 patterns.  For the cases to consider,
+   see the table in the comment before the pattern.  */
+
+bool
+and_operands_ok (enum machine_mode mode, rtx op1, rtx op2)
+{
+  return (memory_operand (op1, mode)
+         ? and_load_operand (op2, mode)
+         : and_reg_operand (op2, mode));
+}
+
 /* The canonical form of a mask-low-and-shift-left operation is
    (and (ashift X SHIFT) MASK) where MASK has the lower SHIFT number of bits
    cleared.  Thus we need to shift MASK to the right before checking if it
@@ -6934,20 +7256,6 @@ mips_init_relocs (void)
   mips_lo_relocs[SYMBOL_HALF] = "%half(";
 }
 
-/* If OP is an UNSPEC address, return the address to which it refers,
-   otherwise return OP itself.  */
-
-static rtx
-mips_strip_unspec_address (rtx op)
-{
-  rtx base, offset;
-
-  split_const (op, &base, &offset);
-  if (UNSPEC_ADDRESS_P (base))
-    op = plus_constant (UNSPEC_ADDRESS (base), INTVAL (offset));
-  return op;
-}
-
 /* Print symbolic operand OP, which is part of a HIGH or LO_SUM
    in context CONTEXT.  RELOCS is the array of relocations to use.  */
 
@@ -6968,6 +7276,45 @@ mips_print_operand_reloc (FILE *file, rtx op, enum mips_symbol_context context,
       fputc (')', file);
 }
 
+/* Start a new block with the given asm switch enabled.  If we need
+   to print a directive, emit PREFIX before it and SUFFIX after it.  */
+
+static void
+mips_push_asm_switch_1 (struct mips_asm_switch *asm_switch,
+                       const char *prefix, const char *suffix)
+{
+  if (asm_switch->nesting_level == 0)
+    fprintf (asm_out_file, "%s.set\tno%s%s", prefix, asm_switch->name, suffix);
+  asm_switch->nesting_level++;
+}
+
+/* Likewise, but end a block.  */
+
+static void
+mips_pop_asm_switch_1 (struct mips_asm_switch *asm_switch,
+                      const char *prefix, const char *suffix)
+{
+  gcc_assert (asm_switch->nesting_level);
+  asm_switch->nesting_level--;
+  if (asm_switch->nesting_level == 0)
+    fprintf (asm_out_file, "%s.set\t%s%s", prefix, asm_switch->name, suffix);
+}
+
+/* Wrappers around mips_push_asm_switch_1 and mips_pop_asm_switch_1
+   that either print a complete line or print nothing.  */
+
+void
+mips_push_asm_switch (struct mips_asm_switch *asm_switch)
+{
+  mips_push_asm_switch_1 (asm_switch, "\t", "\n");
+}
+
+void
+mips_pop_asm_switch (struct mips_asm_switch *asm_switch)
+{
+  mips_pop_asm_switch_1 (asm_switch, "\t", "\n");
+}
+
 /* Print the text for PRINT_OPERAND punctation character CH to FILE.
    The punctuation characters are:
 
@@ -6987,8 +7334,6 @@ mips_print_operand_reloc (FILE *file, rtx op, enum mips_symbol_context context,
    '^' Print the name of the pic call-through register (t9 or $25).
    '+' Print the name of the gp register (usually gp or $28).
    '$' Print the name of the stack pointer register (sp or $29).
-   '|' Print ".set push; .set mips2" if !ISA_HAS_LL_SC.
-   '-' Print ".set pop" under the same conditions for '|'.
 
    See also mips_init_print_operand_pucnt.  */
 
@@ -6998,36 +7343,27 @@ mips_print_operand_punctuation (FILE *file, int ch)
   switch (ch)
     {
     case '(':
-      if (set_noreorder++ == 0)
-       fputs (".set\tnoreorder\n\t", file);
+      mips_push_asm_switch_1 (&mips_noreorder, "", "\n\t");
       break;
 
     case ')':
-      gcc_assert (set_noreorder > 0);
-      if (--set_noreorder == 0)
-       fputs ("\n\t.set\treorder", file);
+      mips_pop_asm_switch_1 (&mips_noreorder, "\n\t", "");
       break;
 
     case '[':
-      if (set_noat++ == 0)
-       fputs (".set\tnoat\n\t", file);
+      mips_push_asm_switch_1 (&mips_noat, "", "\n\t");
       break;
 
     case ']':
-      gcc_assert (set_noat > 0);
-      if (--set_noat == 0)
-       fputs ("\n\t.set\tat", file);
+      mips_pop_asm_switch_1 (&mips_noat, "\n\t", "");
       break;
 
     case '<':
-      if (set_nomacro++ == 0)
-       fputs (".set\tnomacro\n\t", file);
+      mips_push_asm_switch_1 (&mips_nomacro, "", "\n\t");
       break;
 
     case '>':
-      gcc_assert (set_nomacro > 0);
-      if (--set_nomacro == 0)
-       fputs ("\n\t.set\tmacro", file);
+      mips_pop_asm_switch_1 (&mips_nomacro, "\n\t", "");
       break;
 
     case '*':
@@ -7039,7 +7375,7 @@ mips_print_operand_punctuation (FILE *file, int ch)
       break;
 
     case '#':
-      if (set_noreorder != 0)
+      if (mips_noreorder.nesting_level > 0)
        fputs ("\n\tnop", file);
       break;
 
@@ -7047,7 +7383,7 @@ mips_print_operand_punctuation (FILE *file, int ch)
       /* Print an extra newline so that the delayed insn is separated
         from the following ones.  This looks neater and is consistent
         with non-nop delayed sequences.  */
-      if (set_noreorder != 0 && final_sequence == 0)
+      if (mips_noreorder.nesting_level > 0 && final_sequence == 0)
        fputs ("\n\tnop\n", file);
       break;
 
@@ -7066,7 +7402,7 @@ mips_print_operand_punctuation (FILE *file, int ch)
       break;
 
     case '@':
-      fputs (reg_names[GP_REG_FIRST + 1], file);
+      fputs (reg_names[AT_REGNUM], file);
       break;
 
     case '^':
@@ -7081,16 +7417,6 @@ mips_print_operand_punctuation (FILE *file, int ch)
       fputs (reg_names[STACK_POINTER_REGNUM], file);
       break;
 
-    case '|':
-      if (!ISA_HAS_LL_SC)
-       fputs (".set\tpush\n\t.set\tmips2\n\t", file);
-      break;
-
-    case '-':
-      if (!ISA_HAS_LL_SC)
-       fputs ("\n\t.set\tpop", file);
-      break;
-
     default:
       gcc_unreachable ();
       break;
@@ -7104,7 +7430,7 @@ mips_init_print_operand_punct (void)
 {
   const char *p;
 
-  for (p = "()[]<>*#/?~.@^+$|-"; *p; p++)
+  for (p = "()[]<>*#/?~.@^+$"; *p; p++)
     mips_print_operand_punct[(unsigned char) *p] = true;
 }
 
@@ -7159,7 +7485,15 @@ mips_print_float_branch_condition (FILE *file, enum rtx_code code, int letter)
     }
 }
 
-/* Implement the PRINT_OPERAND macro.  The MIPS-specific operand codes are:
+/* Implement TARGET_PRINT_OPERAND_PUNCT_VALID_P.  */
+
+static bool
+mips_print_operand_punct_valid_p (unsigned char code)
+{
+  return mips_print_operand_punct[code];
+}
+
+/* Implement TARGET_PRINT_OPERAND.  The MIPS-specific operand codes are:
 
    'X' Print CONST_INT OP in hexadecimal format.
    'x' Print the low 16 bits of CONST_INT OP in hexadecimal format.
@@ -7183,12 +7517,12 @@ mips_print_float_branch_condition (FILE *file, enum rtx_code code, int letter)
    'M' Print high-order register in a double-word register operand.
    'z' Print $0 if OP is zero, otherwise print OP normally.  */
 
-void
+static void
 mips_print_operand (FILE *file, rtx op, int letter)
 {
   enum rtx_code code;
 
-  if (PRINT_OPERAND_PUNCT_VALID_P (letter))
+  if (mips_print_operand_punct_valid_p (letter))
     {
       mips_print_operand_punctuation (file, letter);
       return;
@@ -7200,28 +7534,28 @@ mips_print_operand (FILE *file, rtx op, int letter)
   switch (letter)
     {
     case 'X':
-      if (GET_CODE (op) == CONST_INT)
+      if (CONST_INT_P (op))
        fprintf (file, HOST_WIDE_INT_PRINT_HEX, INTVAL (op));
       else
        output_operand_lossage ("invalid use of '%%%c'", letter);
       break;
 
     case 'x':
-      if (GET_CODE (op) == CONST_INT)
+      if (CONST_INT_P (op))
        fprintf (file, HOST_WIDE_INT_PRINT_HEX, INTVAL (op) & 0xffff);
       else
        output_operand_lossage ("invalid use of '%%%c'", letter);
       break;
 
     case 'd':
-      if (GET_CODE (op) == CONST_INT)
+      if (CONST_INT_P (op))
        fprintf (file, HOST_WIDE_INT_PRINT_DEC, INTVAL (op));
       else
        output_operand_lossage ("invalid use of '%%%c'", letter);
       break;
 
     case 'm':
-      if (GET_CODE (op) == CONST_INT)
+      if (CONST_INT_P (op))
        fprintf (file, HOST_WIDE_INT_PRINT_DEC, INTVAL (op) - 1);
       else
        output_operand_lossage ("invalid use of '%%%c'", letter);
@@ -7297,6 +7631,8 @@ mips_print_operand (FILE *file, rtx op, int letter)
                || (letter == 'L' && TARGET_BIG_ENDIAN)
                || letter == 'D')
              regno++;
+           else if (letter && letter != 'z' && letter != 'M' && letter != 'L')
+             output_operand_lossage ("invalid use of '%%%c'", letter);
            /* We need to print $0 .. $31 for COP0 registers.  */
            if (COP0_REG_P (regno))
              fprintf (file, "$%s", &reg_names[regno][4]);
@@ -7308,6 +7644,8 @@ mips_print_operand (FILE *file, rtx op, int letter)
        case MEM:
          if (letter == 'D')
            output_address (plus_constant (XEXP (op, 0), 4));
+         else if (letter && letter != 'z')
+           output_operand_lossage ("invalid use of '%%%c'", letter);
          else
            output_address (XEXP (op, 0));
          break;
@@ -7315,6 +7653,8 @@ mips_print_operand (FILE *file, rtx op, int letter)
        default:
          if (letter == 'z' && op == CONST0_RTX (GET_MODE (op)))
            fputs (reg_names[GP_REG_FIRST], file);
+         else if (letter && letter != 'z')
+           output_operand_lossage ("invalid use of '%%%c'", letter);
          else if (CONST_GP_P (op))
            fputs (reg_names[GLOBAL_POINTER_REGNUM], file);
          else
@@ -7324,9 +7664,9 @@ mips_print_operand (FILE *file, rtx op, int letter)
     }
 }
 
-/* Output address operand X to FILE.  */
+/* Implement TARGET_PRINT_OPERAND_ADDRESS.  */
 
-void
+static void
 mips_print_operand_address (FILE *file, rtx x)
 {
   struct mips_address_info addr;
@@ -7437,7 +7777,7 @@ mips_in_small_data_p (const_tree decl)
 
   /* We don't yet generate small-data references for -mabicalls
      or VxWorks RTP code.  See the related -G handling in
-     mips_override_options.  */
+     mips_option_override.  */
   if (TARGET_ABICALLS || TARGET_VXWORKS_RTP)
     return false;
 
@@ -7570,25 +7910,12 @@ mips_output_external (FILE *file, tree decl, const char *name)
          fprintf (file, ", " HOST_WIDE_INT_PRINT_DEC "\n",
                   int_size_in_bytes (TREE_TYPE (decl)));
        }
-      else if (TARGET_IRIX
-              && mips_abi == ABI_32
-              && TREE_CODE (decl) == FUNCTION_DECL)
-       {
-         /* In IRIX 5 or IRIX 6 for the O32 ABI, we must output a
-            `.global name .text' directive for every used but
-            undefined function.  If we don't, the linker may perform
-            an optimization (skipping over the insns that set $gp)
-            when it is unsafe.  */
-         fputs ("\t.globl ", file);
-         assemble_name (file, name);
-         fputs (" .text\n", file);
-       }
     }
 }
 
-/* Implement ASM_OUTPUT_SOURCE_FILENAME.  */
+/* Implement TARGET_ASM_OUTPUT_SOURCE_FILENAME.  */
 
-void
+static void
 mips_output_filename (FILE *stream, const char *name)
 {
   /* If we are emitting DWARF-2, let dwarf2out handle the ".file"
@@ -7872,7 +8199,7 @@ mips_file_start (void)
   /* Generate a special section to describe the ABI switches used to
      produce the resultant binary.  This is unnecessary on IRIX and
      causes unwanted warnings from the native linker.  */
-  if (!TARGET_IRIX)
+  if (!TARGET_IRIX6)
     {
       /* Record the ABI itself.  Modern versions of binutils encode
         this information in the ELF header flags, but GDB needs the
@@ -7891,10 +8218,27 @@ mips_file_start (void)
                 "\t.previous\n", TARGET_LONG64 ? 64 : 32);
 
 #ifdef HAVE_AS_GNU_ATTRIBUTE
-      fprintf (asm_out_file, "\t.gnu_attribute 4, %d\n",
-              (TARGET_HARD_FLOAT_ABI
-               ? (TARGET_DOUBLE_FLOAT
-                  ? ((!TARGET_64BIT && TARGET_FLOAT64) ? 4 : 1) : 2) : 3));
+      {
+       int attr;
+
+       /* No floating-point operations, -mno-float.  */
+       if (TARGET_NO_FLOAT)
+         attr = 0;
+       /* Soft-float code, -msoft-float.  */
+       else if (!TARGET_HARD_FLOAT_ABI)
+         attr = 3;
+       /* Single-float code, -msingle-float.  */
+       else if (!TARGET_DOUBLE_FLOAT)
+         attr = 2;
+       /* 64-bit FP registers on a 32-bit target, -mips32r2 -mfp64.  */
+       else if (!TARGET_64BIT && TARGET_FLOAT64)
+         attr = 4;
+       /* Regular FP code, FP regs same size as GP regs, -mdouble-float.  */
+       else
+         attr = 1;
+
+       fprintf (asm_out_file, "\t.gnu_attribute 4, %d\n", attr);
+      }
 #endif
     }
 
@@ -7938,8 +8282,8 @@ mips_frame_set (rtx mem, rtx reg)
   /* If we're saving the return address register and the DWARF return
      address column differs from the hard register number, adjust the
      note reg to refer to the former.  */
-  if (REGNO (reg) == GP_REG_FIRST + 31
-      && DWARF_FRAME_RETURN_COLUMN != GP_REG_FIRST + 31)
+  if (REGNO (reg) == RETURN_ADDR_REGNUM
+      && DWARF_FRAME_RETURN_COLUMN != RETURN_ADDR_REGNUM)
     reg = gen_rtx_REG (GET_MODE (reg), DWARF_FRAME_RETURN_COLUMN);
 
   set = gen_rtx_SET (VOIDmode, mem, reg);
@@ -8093,7 +8437,7 @@ mips16e_collect_argument_saves (void)
   for (insn = get_insns (); insn; insn = next)
     {
       next = NEXT_INSN (insn);
-      if (NOTE_P (insn))
+      if (NOTE_P (insn) || DEBUG_INSN_P (insn))
        continue;
 
       if (!INSN_P (insn))
@@ -8389,114 +8733,164 @@ mips16e_output_save_restore (rtx pattern, HOST_WIDE_INT adjust)
                                    mips16e_a0_a3_regs[end - 1]);
 
   /* Save or restore $31.  */
-  if (BITSET_P (info.mask, 31))
-    s += sprintf (s, ",%s", reg_names[GP_REG_FIRST + 31]);
+  if (BITSET_P (info.mask, RETURN_ADDR_REGNUM))
+    s += sprintf (s, ",%s", reg_names[RETURN_ADDR_REGNUM]);
 
   return buffer;
 }
 \f
-/* Return true if the current function has an insn that implicitly
-   refers to $gp.  */
+/* Return true if the current function returns its value in a floating-point
+   register in MIPS16 mode.  */
 
 static bool
-mips_function_has_gp_insn (void)
+mips16_cfun_returns_in_fpr_p (void)
 {
-  /* Don't bother rechecking if we found one last time.  */
-  if (!cfun->machine->has_gp_insn_p)
-    {
-      rtx insn;
+  tree return_type = DECL_RESULT (current_function_decl);
+  return (TARGET_MIPS16
+         && TARGET_HARD_FLOAT_ABI
+         && !aggregate_value_p (return_type, current_function_decl)
+         && mips_return_mode_in_fpr_p (DECL_MODE (return_type)));
+}
+
+/* Return true if predicate PRED is true for at least one instruction.
+   Cache the result in *CACHE, and assume that the result is true
+   if *CACHE is already true.  */
+
+static bool
+mips_find_gp_ref (bool *cache, bool (*pred) (rtx))
+{
+  rtx insn;
 
+  if (!*cache)
+    {
       push_topmost_sequence ();
       for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
-       if (USEFUL_INSN_P (insn)
-           && (get_attr_got (insn) != GOT_UNSET
-               || mips_small_data_pattern_p (PATTERN (insn))))
+       if (USEFUL_INSN_P (insn) && pred (insn))
          {
-           cfun->machine->has_gp_insn_p = true;
+           *cache = true;
            break;
          }
       pop_topmost_sequence ();
     }
-  return cfun->machine->has_gp_insn_p;
+  return *cache;
 }
 
-/* Return true if the current function returns its value in a floating-point
-   register in MIPS16 mode.  */
+/* Return true if INSN refers to the global pointer in an "inflexible" way.
+   See mips_cfun_has_inflexible_gp_ref_p for details.  */
 
 static bool
-mips16_cfun_returns_in_fpr_p (void)
+mips_insn_has_inflexible_gp_ref_p (rtx insn)
 {
-  tree return_type = DECL_RESULT (current_function_decl);
-  return (TARGET_MIPS16
-         && TARGET_HARD_FLOAT_ABI
-         && !aggregate_value_p (return_type, current_function_decl)
-         && mips_return_mode_in_fpr_p (DECL_MODE (return_type)));
+  /* Uses of pic_offset_table_rtx in CALL_INSN_FUNCTION_USAGE
+     indicate that the target could be a traditional MIPS
+     lazily-binding stub.  */
+  return find_reg_fusage (insn, USE, pic_offset_table_rtx);
 }
 
-/* Return the register that should be used as the global pointer
-   within this function.  Return INVALID_REGNUM if the function
-   doesn't need a global pointer.  */
+/* Return true if the current function refers to the global pointer
+   in a way that forces $28 to be valid.  This means that we can't
+   change the choice of global pointer, even for NewABI code.
 
-static unsigned int
-mips_global_pointer (void)
-{
-  unsigned int regno;
+   One example of this (and one which needs several checks) is that
+   $28 must be valid when calling traditional MIPS lazy-binding stubs.
+   (This restriction does not apply to PLTs.)  */
 
-  /* $gp is always available unless we're using a GOT.  */
-  if (!TARGET_USE_GOT)
-    return GLOBAL_POINTER_REGNUM;
+static bool
+mips_cfun_has_inflexible_gp_ref_p (void)
+{
+  /* If the function has a nonlocal goto, $28 must hold the correct
+     global pointer for the target function.  That is, the target
+     of the goto implicitly uses $28.  */
+  if (crtl->has_nonlocal_goto)
+    return true;
 
-  /* We must always provide $gp when it is used implicitly.  */
-  if (!TARGET_EXPLICIT_RELOCS)
-    return GLOBAL_POINTER_REGNUM;
+  if (TARGET_ABICALLS_PIC2)
+    {
+      /* Symbolic accesses implicitly use the global pointer unless
+        -mexplicit-relocs is in effect.  JAL macros to symbolic addresses
+        might go to traditional MIPS lazy-binding stubs.  */
+      if (!TARGET_EXPLICIT_RELOCS)
+       return true;
 
-  /* FUNCTION_PROFILER includes a jal macro, so we need to give it
-     a valid gp.  */
-  if (crtl->profile)
-    return GLOBAL_POINTER_REGNUM;
+      /* FUNCTION_PROFILER includes a JAL to _mcount, which again
+        can be lazily-bound.  */
+      if (crtl->profile)
+       return true;
 
-  /* If the function has a nonlocal goto, $gp must hold the correct
-     global pointer for the target function.  */
-  if (crtl->has_nonlocal_goto)
-    return GLOBAL_POINTER_REGNUM;
+      /* MIPS16 functions that return in FPRs need to call an
+        external libgcc routine.  This call is only made explict
+        during mips_expand_epilogue, and it too might be lazily bound.  */
+      if (mips16_cfun_returns_in_fpr_p ())
+       return true;
+    }
 
-  /* There's no need to initialize $gp if it isn't referenced now,
-     and if we can be sure that no new references will be added during
-     or after reload.  */
-  if (!df_regs_ever_live_p (GLOBAL_POINTER_REGNUM)
-      && !mips_function_has_gp_insn ())
-    {
-      /* The function doesn't use $gp at the moment.  If we're generating
-        -call_nonpic code, no new uses will be introduced during or after
-        reload.  */
-      if (TARGET_ABICALLS_PIC0)
-       return INVALID_REGNUM;
+  return mips_find_gp_ref (&cfun->machine->has_inflexible_gp_insn_p,
+                          mips_insn_has_inflexible_gp_ref_p);
+}
+
+/* Return true if INSN refers to the global pointer in a "flexible" way.
+   See mips_cfun_has_flexible_gp_ref_p for details.  */
+
+static bool
+mips_insn_has_flexible_gp_ref_p (rtx insn)
+{
+  return (get_attr_got (insn) != GOT_UNSET
+         || mips_small_data_pattern_p (PATTERN (insn))
+         || reg_overlap_mentioned_p (pic_offset_table_rtx, PATTERN (insn)));
+}
 
-      /* We need to handle the following implicit gp references:
+/* Return true if the current function references the global pointer,
+   but if those references do not inherently require the global pointer
+   to be $28.  Assume !mips_cfun_has_inflexible_gp_ref_p ().  */
 
-        - Reload can sometimes introduce constant pool references
-          into a function that otherwise didn't need them.  For example,
-          suppose we have an instruction like:
+static bool
+mips_cfun_has_flexible_gp_ref_p (void)
+{
+  /* Reload can sometimes introduce constant pool references
+     into a function that otherwise didn't need them.  For example,
+     suppose we have an instruction like:
 
-              (set (reg:DF R1) (float:DF (reg:SI R2)))
+       (set (reg:DF R1) (float:DF (reg:SI R2)))
 
-          If R2 turns out to be constant such as 1, the instruction may
-          have a REG_EQUAL note saying that R1 == 1.0.  Reload then has
-          the option of using this constant if R2 doesn't get allocated
-          to a register.
+     If R2 turns out to be a constant such as 1, the instruction may
+     have a REG_EQUAL note saying that R1 == 1.0.  Reload then has
+     the option of using this constant if R2 doesn't get allocated
+     to a register.
 
-          In cases like these, reload will have added the constant to the
-          pool but no instruction will yet refer to it.
+     In cases like these, reload will have added the constant to the
+     pool but no instruction will yet refer to it.  */
+  if (TARGET_ABICALLS_PIC2 && !reload_completed && crtl->uses_const_pool)
+    return true;
 
-        - MIPS16 functions that return in FPRs need to call an
-          external libgcc routine.  */
-      if (!crtl->uses_const_pool
-         && !mips16_cfun_returns_in_fpr_p ())
-       return INVALID_REGNUM;
-    }
+  return mips_find_gp_ref (&cfun->machine->has_flexible_gp_insn_p,
+                          mips_insn_has_flexible_gp_ref_p);
+}
 
-  /* We need a global pointer, but perhaps we can use a call-clobbered
-     register instead of $gp.  */
+/* Return the register that should be used as the global pointer
+   within this function.  Return INVALID_REGNUM if the function
+   doesn't need a global pointer.  */
+
+static unsigned int
+mips_global_pointer (void)
+{
+  unsigned int regno;
+
+  /* $gp is always available unless we're using a GOT.  */
+  if (!TARGET_USE_GOT)
+    return GLOBAL_POINTER_REGNUM;
+
+  /* If there are inflexible references to $gp, we must use the
+     standard register.  */
+  if (mips_cfun_has_inflexible_gp_ref_p ())
+    return GLOBAL_POINTER_REGNUM;
+
+  /* If there are no current references to $gp, then the only uses
+     we can introduce later are those involved in long branches.  */
+  if (TARGET_ABSOLUTE_JUMPS && !mips_cfun_has_flexible_gp_ref_p ())
+    return INVALID_REGNUM;
+
+  /* If the global pointer is call-saved, try to use a call-clobbered
+     alternative.  */
   if (TARGET_CALL_SAVED_GP && current_function_is_leaf)
     for (regno = GP_REG_FIRST; regno <= GP_REG_LAST; regno++)
       if (!df_regs_ever_live_p (regno)
@@ -8508,6 +8902,119 @@ mips_global_pointer (void)
   return GLOBAL_POINTER_REGNUM;
 }
 
+/* Return true if the current function's prologue must load the global
+   pointer value into pic_offset_table_rtx and store the same value in
+   the function's cprestore slot (if any).
+
+   One problem we have to deal with is that, when emitting GOT-based
+   position independent code, long-branch sequences will need to load
+   the address of the branch target from the GOT.  We don't know until
+   the very end of compilation whether (and where) the function needs
+   long branches, so we must ensure that _any_ branch can access the
+   global pointer in some form.  However, we do not want to pessimize
+   the usual case in which all branches are short.
+
+   We handle this as follows:
+
+   (1) During reload, we set cfun->machine->global_pointer to
+       INVALID_REGNUM if we _know_ that the current function
+       doesn't need a global pointer.  This is only valid if
+       long branches don't need the GOT.
+
+       Otherwise, we assume that we might need a global pointer
+       and pick an appropriate register.
+
+   (2) If cfun->machine->global_pointer != INVALID_REGNUM,
+       we ensure that the global pointer is available at every
+       block boundary bar entry and exit.  We do this in one of two ways:
+
+       - If the function has a cprestore slot, we ensure that this
+        slot is valid at every branch.  However, as explained in
+        point (6) below, there is no guarantee that pic_offset_table_rtx
+        itself is valid if new uses of the global pointer are introduced
+        after the first post-epilogue split.
+
+        We guarantee that the cprestore slot is valid by loading it
+        into a fake register, CPRESTORE_SLOT_REGNUM.  We then make
+        this register live at every block boundary bar function entry
+        and exit.  It is then invalid to move the load (and thus the
+        preceding store) across a block boundary.
+
+       - If the function has no cprestore slot, we guarantee that
+        pic_offset_table_rtx itself is valid at every branch.
+
+       See mips_eh_uses for the handling of the register liveness.
+
+   (3) During prologue and epilogue generation, we emit "ghost"
+       placeholder instructions to manipulate the global pointer.
+
+   (4) During prologue generation, we set cfun->machine->must_initialize_gp_p
+       and cfun->machine->must_restore_gp_when_clobbered_p if we already know
+       that the function needs a global pointer.  (There is no need to set
+       them earlier than this, and doing it as late as possible leads to
+       fewer false positives.)
+
+   (5) If cfun->machine->must_initialize_gp_p is true during a
+       split_insns pass, we split the ghost instructions into real
+       instructions.  These split instructions can then be optimized in
+       the usual way.  Otherwise, we keep the ghost instructions intact,
+       and optimize for the case where they aren't needed.  We still
+       have the option of splitting them later, if we need to introduce
+       new uses of the global pointer.
+
+       For example, the scheduler ignores a ghost instruction that
+       stores $28 to the stack, but it handles the split form of
+       the ghost instruction as an ordinary store.
+
+   (6) [OldABI only.]  If cfun->machine->must_restore_gp_when_clobbered_p
+       is true during the first post-epilogue split_insns pass, we split
+       calls and restore_gp patterns into instructions that explicitly
+       load pic_offset_table_rtx from the cprestore slot.  Otherwise,
+       we split these patterns into instructions that _don't_ load from
+       the cprestore slot.
+
+       If cfun->machine->must_restore_gp_when_clobbered_p is true at the
+       time of the split, then any instructions that exist at that time
+       can make free use of pic_offset_table_rtx.  However, if we want
+       to introduce new uses of the global pointer after the split,
+       we must explicitly load the value from the cprestore slot, since
+       pic_offset_table_rtx itself might not be valid at a given point
+       in the function.
+
+       The idea is that we want to be able to delete redundant
+       loads from the cprestore slot in the usual case where no
+       long branches are needed.
+
+   (7) If cfun->machine->must_initialize_gp_p is still false at the end
+       of md_reorg, we decide whether the global pointer is needed for
+       long branches.  If so, we set cfun->machine->must_initialize_gp_p
+       to true and split the ghost instructions into real instructions
+       at that stage.
+
+   Note that the ghost instructions must have a zero length for three reasons:
+
+   - Giving the length of the underlying $gp sequence might cause
+     us to use long branches in cases where they aren't really needed.
+
+   - They would perturb things like alignment calculations.
+
+   - More importantly, the hazard detection in md_reorg relies on
+     empty instructions having a zero length.
+
+   If we find a long branch and split the ghost instructions at the
+   end of md_reorg, the split could introduce more long branches.
+   That isn't a problem though, because we still do the split before
+   the final shorten_branches pass.
+
+   This is extremely ugly, but it seems like the best compromise between
+   correctness and efficiency.  */
+
+bool
+mips_must_initialize_gp_p (void)
+{
+  return cfun->machine->must_initialize_gp_p;
+}
+
 /* Return true if REGNO is a register that is ordinarily call-clobbered
    but must nevertheless be preserved by an interrupt handler.  */
 
@@ -8600,7 +9107,7 @@ mips_cfun_might_clobber_call_saved_reg_p (unsigned int regno)
   /* If a MIPS16 function returns a value in FPRs, its epilogue
      will need to call an external libgcc routine.  This yet-to-be
      generated call_insn will clobber $31.  */
-  if (regno == GP_REG_FIRST + 31 && mips16_cfun_returns_in_fpr_p ())
+  if (regno == RETURN_ADDR_REGNUM && mips16_cfun_returns_in_fpr_p ())
     return true;
 
   /* If REGNO is ordinarily call-clobbered, we must assume that any
@@ -8634,7 +9141,7 @@ mips_save_reg_p (unsigned int regno)
 
   /* We need to save the incoming return address if __builtin_eh_return
      is being used to set a different return address.  */
-  if (regno == GP_REG_FIRST + 31 && crtl->calls_eh_return)
+  if (regno == RETURN_ADDR_REGNUM && crtl->calls_eh_return)
     return true;
 
   return false;
@@ -8673,16 +9180,16 @@ mips_save_reg_p (unsigned int regno)
        |                               |       + UNITS_PER_WORD
        |  accumulator save area        |
        |                               |
-       +-------------------------------+ <-- frame_pointer_rtx + fp_sp_offset
+       +-------------------------------+ <-- stack_pointer_rtx + fp_sp_offset
        |                               |       + UNITS_PER_HWFPVALUE
        |  FPR save area                |
        |                               |
-       +-------------------------------+ <-- frame_pointer_rtx + gp_sp_offset
+       +-------------------------------+ <-- stack_pointer_rtx + gp_sp_offset
        |                               |       + UNITS_PER_WORD
        |  GPR save area                |
        |                               |
-       +-------------------------------+
-       |                               | \
+       +-------------------------------+ <-- frame_pointer_rtx with
+       |                               | \     -fstack-protector
        |  local variables              |  | var_size
        |                               | /
        +-------------------------------+
@@ -8690,16 +9197,17 @@ mips_save_reg_p (unsigned int regno)
        |  $gp save area                |  | cprestore_size
        |                               | /
       P +-------------------------------+ <-- hard_frame_pointer_rtx for
-       |                               |       MIPS16 code
-       |  outgoing stack arguments     |
-       |                               |
-       +-------------------------------+
-       |                               |
-       |  caller-allocated save area   |
-       |  for register arguments       |
-       |                               |
+       |                               | \     MIPS16 code
+       |  outgoing stack arguments     |  |
+       |                               |  |
+       +-------------------------------+  | args_size
+       |                               |  |
+       |  caller-allocated save area   |  |
+       |  for register arguments       |  |
+       |                               | /
        +-------------------------------+ <-- stack_pointer_rtx
-                                             frame_pointer_rtx
+                                             frame_pointer_rtx without
+                                               -fstack-protector
                                              hard_frame_pointer_rtx for
                                                non-MIPS16 code.
 
@@ -8744,11 +9252,11 @@ mips_compute_frame_info (void)
 
   cfun->machine->global_pointer = mips_global_pointer ();
 
-  /* The first STARTING_FRAME_OFFSET bytes contain the outgoing argument
-     area and the $gp save slot.  This area isn't needed in leaf functions,
-     but if the target-independent frame size is nonzero, we're committed
-     to allocating it anyway.  */
-  if (size == 0 && current_function_is_leaf)
+  /* The first two blocks contain the outgoing argument area and the $gp save
+     slot.  This area isn't needed in leaf functions, but if the
+     target-independent frame size is nonzero, we have already committed to
+     allocating these in STARTING_FRAME_OFFSET for !FRAME_GROWS_DOWNWARD.  */
+  if ((size == 0 || FRAME_GROWS_DOWNWARD) && current_function_is_leaf)
     {
       /* The MIPS 3.0 linker does not like functions that dynamically
         allocate the stack and have 0 for STACK_DYNAMIC_OFFSET, since it
@@ -8763,7 +9271,7 @@ mips_compute_frame_info (void)
   else
     {
       frame->args_size = crtl->outgoing_args_size;
-      frame->cprestore_size = STARTING_FRAME_OFFSET - frame->args_size;
+      frame->cprestore_size = MIPS_GP_SAVE_AREA_SIZE;
     }
   offset = frame->args_size + frame->cprestore_size;
 
@@ -8907,9 +9415,9 @@ mips_current_loadgp_style (void)
   return TARGET_NEWABI ? LOADGP_NEWABI : LOADGP_OLDABI;
 }
 
-/* Implement FRAME_POINTER_REQUIRED.  */
+/* Implement TARGET_FRAME_POINTER_REQUIRED.  */
 
-bool
+static bool
 mips_frame_pointer_required (void)
 {
   /* If the function contains dynamic stack allocations, we need to
@@ -8931,6 +9439,15 @@ mips_frame_pointer_required (void)
   return false;
 }
 
+/* Make sure that we're not trying to eliminate to the wrong hard frame
+   pointer.  */
+
+static bool
+mips_can_eliminate (const int from ATTRIBUTE_UNUSED, const int to)
+{
+  return (to == HARD_FRAME_POINTER_REGNUM || to == STACK_POINTER_REGNUM);
+}
+
 /* Implement INITIAL_ELIMINATION_OFFSET.  FROM is either the frame pointer
    or argument pointer.  TO is either the stack pointer or hard frame
    pointer.  */
@@ -8942,12 +9459,16 @@ mips_initial_elimination_offset (int from, int to)
 
   mips_compute_frame_info ();
 
-  /* Set OFFSET to the offset from the soft frame pointer, which is also
-     the offset from the end-of-prologue stack pointer.  */
+  /* Set OFFSET to the offset from the end-of-prologue stack pointer.  */
   switch (from)
     {
     case FRAME_POINTER_REGNUM:
-      offset = 0;
+      if (FRAME_GROWS_DOWNWARD)
+       offset = (cfun->machine->frame.args_size
+                 + cfun->machine->frame.cprestore_size
+                 + cfun->machine->frame.var_size);
+      else
+       offset = 0;
       break;
 
     case ARG_POINTER_REGNUM:
@@ -8995,7 +9516,7 @@ mips_return_addr (int count, rtx frame ATTRIBUTE_UNUSED)
   if (count != 0)
     return const0_rtx;
 
-  return get_hard_reg_initial_val (Pmode, GP_REG_FIRST + 31);
+  return get_hard_reg_initial_val (Pmode, RETURN_ADDR_REGNUM);
 }
 
 /* Emit code to change the current function's return address to
@@ -9007,54 +9528,127 @@ mips_set_return_address (rtx address, rtx scratch)
 {
   rtx slot_address;
 
-  gcc_assert (BITSET_P (cfun->machine->frame.mask, 31));
+  gcc_assert (BITSET_P (cfun->machine->frame.mask, RETURN_ADDR_REGNUM));
   slot_address = mips_add_offset (scratch, stack_pointer_rtx,
                                  cfun->machine->frame.gp_sp_offset);
   mips_emit_move (gen_frame_mem (GET_MODE (address), slot_address), address);
 }
 
-/* Return a MEM rtx for the cprestore slot, using TEMP as a temporary base
-   register if need be.  */
+/* Return true if the current function has a cprestore slot.  */
 
-static rtx
-mips_cprestore_slot (rtx temp)
+bool
+mips_cfun_has_cprestore_slot_p (void)
+{
+  return (cfun->machine->global_pointer != INVALID_REGNUM
+         && cfun->machine->frame.cprestore_size > 0);
+}
+
+/* Fill *BASE and *OFFSET such that *BASE + *OFFSET refers to the
+   cprestore slot.  LOAD_P is true if the caller wants to load from
+   the cprestore slot; it is false if the caller wants to store to
+   the slot.  */
+
+static void
+mips_get_cprestore_base_and_offset (rtx *base, HOST_WIDE_INT *offset,
+                                   bool load_p)
 {
   const struct mips_frame_info *frame;
-  rtx base;
-  HOST_WIDE_INT offset;
 
   frame = &cfun->machine->frame;
-  if (frame_pointer_needed)
-    {
-      base = hard_frame_pointer_rtx;
-      offset = frame->args_size - frame->hard_frame_pointer_offset;
+  /* .cprestore always uses the stack pointer instead of the frame pointer.
+     We have a free choice for direct stores for non-MIPS16 functions,
+     and for MIPS16 functions whose cprestore slot is in range of the
+     stack pointer.  Using the stack pointer would sometimes give more
+     (early) scheduling freedom, but using the frame pointer would
+     sometimes give more (late) scheduling freedom.  It's hard to
+     predict which applies to a given function, so let's keep things
+     simple.
+
+     Loads must always use the frame pointer in functions that call
+     alloca, and there's little benefit to using the stack pointer
+     otherwise.  */
+  if (frame_pointer_needed && !(TARGET_CPRESTORE_DIRECTIVE && !load_p))
+    {
+      *base = hard_frame_pointer_rtx;
+      *offset = frame->args_size - frame->hard_frame_pointer_offset;
     }
   else
     {
-      base = stack_pointer_rtx;
-      offset = frame->args_size;
+      *base = stack_pointer_rtx;
+      *offset = frame->args_size;
     }
+}
+
+/* Return true if X is the load or store address of the cprestore slot;
+   LOAD_P says which.  */
+
+bool
+mips_cprestore_address_p (rtx x, bool load_p)
+{
+  rtx given_base, required_base;
+  HOST_WIDE_INT given_offset, required_offset;
+
+  mips_split_plus (x, &given_base, &given_offset);
+  mips_get_cprestore_base_and_offset (&required_base, &required_offset, load_p);
+  return given_base == required_base && given_offset == required_offset;
+}
+
+/* Return a MEM rtx for the cprestore slot.  LOAD_P is true if we are
+   going to load from it, false if we are going to store to it.
+   Use TEMP as a temporary register if need be.  */
+
+static rtx
+mips_cprestore_slot (rtx temp, bool load_p)
+{
+  rtx base;
+  HOST_WIDE_INT offset;
+
+  mips_get_cprestore_base_and_offset (&base, &offset, load_p);
   return gen_frame_mem (Pmode, mips_add_offset (temp, base, offset));
 }
 
+/* Emit instructions to save global pointer value GP into cprestore
+   slot MEM.  OFFSET is the offset that MEM applies to the base register.
+
+   MEM may not be a legitimate address.  If it isn't, TEMP is a
+   temporary register that can be used, otherwise it is a SCRATCH.  */
+
+void
+mips_save_gp_to_cprestore_slot (rtx mem, rtx offset, rtx gp, rtx temp)
+{
+  if (TARGET_CPRESTORE_DIRECTIVE)
+    {
+      gcc_assert (gp == pic_offset_table_rtx);
+      emit_insn (gen_cprestore (mem, offset));
+    }
+  else
+    mips_emit_move (mips_cprestore_slot (temp, false), gp);
+}
+
 /* Restore $gp from its save slot, using TEMP as a temporary base register
-   if need be.  This function is for o32 and o64 abicalls only.  */
+   if need be.  This function is for o32 and o64 abicalls only.
+
+   See mips_must_initialize_gp_p for details about how we manage the
+   global pointer.  */
 
 void
-mips_restore_gp (rtx temp)
+mips_restore_gp_from_cprestore_slot (rtx temp)
 {
-  gcc_assert (TARGET_ABICALLS && TARGET_OLDABI);
+  gcc_assert (TARGET_ABICALLS && TARGET_OLDABI && epilogue_completed);
 
-  if (cfun->machine->global_pointer == INVALID_REGNUM)
-    return;
+  if (!cfun->machine->must_restore_gp_when_clobbered_p)
+    {
+      emit_note (NOTE_INSN_DELETED);
+      return;
+    }
 
   if (TARGET_MIPS16)
     {
-      mips_emit_move (temp, mips_cprestore_slot (temp));
+      mips_emit_move (temp, mips_cprestore_slot (temp, true));
       mips_emit_move (pic_offset_table_rtx, temp);
     }
   else
-    mips_emit_move (pic_offset_table_rtx, mips_cprestore_slot (temp));
+    mips_emit_move (pic_offset_table_rtx, mips_cprestore_slot (temp, true));
   if (!TARGET_EXPLICIT_RELOCS)
     emit_insn (gen_blockage ());
 }
@@ -9125,6 +9719,9 @@ mips_for_each_saved_gpr_and_fpr (HOST_WIDE_INT sp_offset,
   for (regno = GP_REG_LAST; regno >= GP_REG_FIRST; regno--)
     if (BITSET_P (cfun->machine->frame.mask, regno - GP_REG_FIRST))
       {
+       /* Record the ra offset for use by mips_function_profiler.  */
+       if (regno == RETURN_ADDR_REGNUM)
+         cfun->machine->frame.ra_fp_offset = offset + sp_offset;
        mips_save_restore_reg (word_mode, regno, offset, fn);
        offset -= UNITS_PER_WORD;
       }
@@ -9142,6 +9739,89 @@ mips_for_each_saved_gpr_and_fpr (HOST_WIDE_INT sp_offset,
        offset -= GET_MODE_SIZE (fpr_mode);
       }
 }
+
+/* Return true if a move between register REGNO and its save slot (MEM)
+   can be done in a single move.  LOAD_P is true if we are loading
+   from the slot, false if we are storing to it.  */
+
+static bool
+mips_direct_save_slot_move_p (unsigned int regno, rtx mem, bool load_p)
+{
+  /* There is a specific MIPS16 instruction for saving $31 to the stack.  */
+  if (TARGET_MIPS16 && !load_p && regno == RETURN_ADDR_REGNUM)
+    return false;
+
+  return mips_secondary_reload_class (REGNO_REG_CLASS (regno),
+                                     GET_MODE (mem), mem, load_p) == NO_REGS;
+}
+
+/* Emit a move from SRC to DEST, given that one of them is a register
+   save slot and that the other is a register.  TEMP is a temporary
+   GPR of the same mode that is available if need be.  */
+
+void
+mips_emit_save_slot_move (rtx dest, rtx src, rtx temp)
+{
+  unsigned int regno;
+  rtx mem;
+
+  if (REG_P (src))
+    {
+      regno = REGNO (src);
+      mem = dest;
+    }
+  else
+    {
+      regno = REGNO (dest);
+      mem = src;
+    }
+
+  if (regno == cfun->machine->global_pointer && !mips_must_initialize_gp_p ())
+    {
+      /* We don't yet know whether we'll need this instruction or not.
+        Postpone the decision by emitting a ghost move.  This move
+        is specifically not frame-related; only the split version is.  */
+      if (TARGET_64BIT)
+       emit_insn (gen_move_gpdi (dest, src));
+      else
+       emit_insn (gen_move_gpsi (dest, src));
+      return;
+    }
+
+  if (regno == HI_REGNUM)
+    {
+      if (REG_P (dest))
+       {
+         mips_emit_move (temp, src);
+         if (TARGET_64BIT)
+           emit_insn (gen_mthisi_di (gen_rtx_REG (TImode, MD_REG_FIRST),
+                                     temp, gen_rtx_REG (DImode, LO_REGNUM)));
+         else
+           emit_insn (gen_mthisi_di (gen_rtx_REG (DImode, MD_REG_FIRST),
+                                     temp, gen_rtx_REG (SImode, LO_REGNUM)));
+       }
+      else
+       {
+         if (TARGET_64BIT)
+           emit_insn (gen_mfhidi_ti (temp,
+                                     gen_rtx_REG (TImode, MD_REG_FIRST)));
+         else
+           emit_insn (gen_mfhisi_di (temp,
+                                     gen_rtx_REG (DImode, MD_REG_FIRST)));
+         mips_emit_move (dest, temp);
+       }
+    }
+  else if (mips_direct_save_slot_move_p (regno, mem, mem == src))
+    mips_emit_move (dest, src);
+  else
+    {
+      gcc_assert (!reg_overlap_mentioned_p (dest, temp));
+      mips_emit_move (temp, src);
+      mips_emit_move (dest, temp);
+    }
+  if (MEM_P (dest))
+    mips_set_frame_expr (mips_frame_set (dest, src));
+}
 \f
 /* If we're generating n32 or n64 abicalls, and the current function
    does not use $28 as its global pointer, emit a cplocal directive.
@@ -9151,7 +9831,7 @@ static void
 mips_output_cplocal (void)
 {
   if (!TARGET_EXPLICIT_RELOCS
-      && cfun->machine->global_pointer != INVALID_REGNUM
+      && mips_must_initialize_gp_p ()
       && cfun->machine->global_pointer != GLOBAL_POINTER_REGNUM)
     output_asm_insn (".cplocal %+", 0);
 }
@@ -9181,10 +9861,6 @@ mips_output_function_prologue (FILE *file, HOST_WIDE_INT size ATTRIBUTE_UNUSED)
   fnname = XSTR (XEXP (DECL_RTL (current_function_decl), 0), 0);
   mips_start_function_definition (fnname, TARGET_MIPS16);
 
-  /* Stop mips_file_end from treating this function as external.  */
-  if (TARGET_IRIX && mips_abi == ABI_32)
-    TREE_ASM_WRITTEN (DECL_NAME (cfun->decl)) = 1;
-
   /* Output MIPS-specific frame information.  */
   if (!flag_inhibit_size_directive)
     {
@@ -9205,7 +9881,7 @@ mips_output_function_prologue (FILE *file, HOST_WIDE_INT size ATTRIBUTE_UNUSED)
               (frame_pointer_needed
                ? frame->total_size - frame->hard_frame_pointer_offset
                : frame->total_size),
-              reg_names[GP_REG_FIRST + 31],
+              reg_names[RETURN_ADDR_REGNUM],
               frame->var_size,
               frame->num_gp, frame->num_fp,
               frame->args_size,
@@ -9223,7 +9899,8 @@ mips_output_function_prologue (FILE *file, HOST_WIDE_INT size ATTRIBUTE_UNUSED)
   /* Handle the initialization of $gp for SVR4 PIC, if applicable.
      Also emit the ".set noreorder; .set nomacro" sequence for functions
      that need it.  */
-  if (mips_current_loadgp_style () == LOADGP_OLDABI)
+  if (mips_must_initialize_gp_p ()
+      && mips_current_loadgp_style () == LOADGP_OLDABI)
     {
       if (TARGET_MIPS16)
        {
@@ -9235,14 +9912,23 @@ mips_output_function_prologue (FILE *file, HOST_WIDE_INT size ATTRIBUTE_UNUSED)
          output_asm_insn ("sll\t$2,16", 0);
          output_asm_insn ("addu\t$2,$3", 0);
        }
-      /* .cpload must be in a .set noreorder but not a .set nomacro block.  */
-      else if (!cfun->machine->all_noreorder_p)
-       output_asm_insn ("%(.cpload\t%^%)", 0);
       else
-       output_asm_insn ("%(.cpload\t%^\n\t%<", 0);
+       {
+         /* .cpload must be in a .set noreorder but not a
+            .set nomacro block.  */
+         mips_push_asm_switch (&mips_noreorder);
+         output_asm_insn (".cpload\t%^", 0);
+         if (!cfun->machine->all_noreorder_p)
+           mips_pop_asm_switch (&mips_noreorder);
+         else
+           mips_push_asm_switch (&mips_nomacro);
+       }
     }
   else if (cfun->machine->all_noreorder_p)
-    output_asm_insn ("%(%<", 0);
+    {
+      mips_push_asm_switch (&mips_noreorder);
+      mips_push_asm_switch (&mips_nomacro);
+    }
 
   /* Tell the assembler which register we're using as the global
      pointer.  This is needed for thunks, since they can use either
@@ -9264,10 +9950,8 @@ mips_output_function_epilogue (FILE *file ATTRIBUTE_UNUSED,
 
   if (cfun->machine->all_noreorder_p)
     {
-      /* Avoid using %>%) since it adds excess whitespace.  */
-      output_asm_insn (".set\tmacro", 0);
-      output_asm_insn (".set\treorder", 0);
-      set_noreorder = set_nomacro = 0;
+      mips_pop_asm_switch (&mips_nomacro);
+      mips_pop_asm_switch (&mips_noreorder);
     }
 
   /* Get the function name the same way that toplev.c does before calling
@@ -9298,33 +9982,7 @@ mips_save_reg (rtx reg, rtx mem)
       mips_set_frame_expr (gen_rtx_PARALLEL (VOIDmode, gen_rtvec (2, x1, x2)));
     }
   else
-    {
-      if (REGNO (reg) == HI_REGNUM)
-       {
-         if (TARGET_64BIT)
-           emit_insn (gen_mfhidi_ti (MIPS_PROLOGUE_TEMP (DImode),
-                                     gen_rtx_REG (TImode, MD_REG_FIRST)));
-         else
-           emit_insn (gen_mfhisi_di (MIPS_PROLOGUE_TEMP (SImode),
-                                     gen_rtx_REG (DImode, MD_REG_FIRST)));
-         mips_emit_move (mem, MIPS_PROLOGUE_TEMP (GET_MODE (reg)));
-       }
-      else if ((TARGET_MIPS16
-               && REGNO (reg) != GP_REG_FIRST + 31
-               && !M16_REG_P (REGNO (reg)))
-              || ACC_REG_P (REGNO (reg)))
-       {
-         /* If the register has no direct store instruction, move it
-            through a temporary.  Note that there's a special MIPS16
-            instruction to save $31.  */
-         mips_emit_move (MIPS_PROLOGUE_TEMP (GET_MODE (reg)), reg);
-         mips_emit_move (mem, MIPS_PROLOGUE_TEMP (GET_MODE (reg)));
-       }
-      else
-       mips_emit_move (mem, reg);
-
-      mips_set_frame_expr (mips_frame_set (mem, reg));
-    }
+    mips_emit_save_slot_move (mem, reg, MIPS_PROLOGUE_TEMP (GET_MODE (reg)));
 }
 
 /* The __gnu_local_gp symbol.  */
@@ -9393,7 +10051,7 @@ mips_emit_loadgp (void)
 static int
 mips_kernel_reg_p (rtx *x, void *data ATTRIBUTE_UNUSED)
 {
-  return GET_CODE (*x) == REG && KERNEL_REG_P (REGNO (*x));
+  return REG_P (*x) && KERNEL_REG_P (REGNO (*x));
 }
 
 /* Expand the "prologue" pattern.  */
@@ -9407,11 +10065,26 @@ mips_expand_prologue (void)
   rtx insn;
 
   if (cfun->machine->global_pointer != INVALID_REGNUM)
-    SET_REGNO (pic_offset_table_rtx, cfun->machine->global_pointer);
+    {
+      /* Check whether an insn uses pic_offset_table_rtx, either explicitly
+        or implicitly.  If so, we can commit to using a global pointer
+        straight away, otherwise we need to defer the decision.  */
+      if (mips_cfun_has_inflexible_gp_ref_p ()
+         || mips_cfun_has_flexible_gp_ref_p ())
+       {
+         cfun->machine->must_initialize_gp_p = true;
+         cfun->machine->must_restore_gp_when_clobbered_p = true;
+       }
+
+      SET_REGNO (pic_offset_table_rtx, cfun->machine->global_pointer);
+    }
 
   frame = &cfun->machine->frame;
   size = frame->total_size;
 
+  if (flag_stack_usage)
+    current_function_static_stack_size = size;
+
   /* Save the registers.  Allocate up to MIPS_MAX_FIRST_STACK_STEP
      bytes beforehand; this is enough to cover the register save area
      without going out of range.  */
@@ -9609,17 +10282,22 @@ mips_expand_prologue (void)
   mips_emit_loadgp ();
 
   /* Initialize the $gp save slot.  */
-  if (frame->cprestore_size > 0
-      && cfun->machine->global_pointer != INVALID_REGNUM)
+  if (mips_cfun_has_cprestore_slot_p ())
     {
-      if (TARGET_MIPS16)
-       mips_emit_move (mips_cprestore_slot (MIPS_PROLOGUE_TEMP (Pmode)),
-                       MIPS16_PIC_TEMP);
-      else if (TARGET_ABICALLS_PIC2)
-       emit_insn (gen_cprestore (GEN_INT (frame->args_size)));
-      else
-       emit_move_insn (mips_cprestore_slot (MIPS_PROLOGUE_TEMP (Pmode)),
-                       pic_offset_table_rtx);
+      rtx base, mem, gp, temp;
+      HOST_WIDE_INT offset;
+
+      mips_get_cprestore_base_and_offset (&base, &offset, false);
+      mem = gen_frame_mem (Pmode, plus_constant (base, offset));
+      gp = TARGET_MIPS16 ? MIPS16_PIC_TEMP : pic_offset_table_rtx;
+      temp = (SMALL_OPERAND (offset)
+             ? gen_rtx_SCRATCH (Pmode)
+             : MIPS_PROLOGUE_TEMP (Pmode));
+      emit_insn (gen_potential_cprestore (mem, GEN_INT (offset), gp, temp));
+
+      mips_get_cprestore_base_and_offset (&base, &offset, true);
+      mem = gen_frame_mem (Pmode, plus_constant (base, offset));
+      emit_insn (gen_use_cprestore (mem));
     }
 
   /* We need to search back to the last use of K0 or K1.  */
@@ -9649,30 +10327,10 @@ mips_restore_reg (rtx reg, rtx mem)
 {
   /* There's no MIPS16 instruction to load $31 directly.  Load into
      $7 instead and adjust the return insn appropriately.  */
-  if (TARGET_MIPS16 && REGNO (reg) == GP_REG_FIRST + 31)
+  if (TARGET_MIPS16 && REGNO (reg) == RETURN_ADDR_REGNUM)
     reg = gen_rtx_REG (GET_MODE (reg), GP_REG_FIRST + 7);
 
-  if (REGNO (reg) == HI_REGNUM)
-    {
-      mips_emit_move (MIPS_EPILOGUE_TEMP (GET_MODE (reg)), mem);
-      if (TARGET_64BIT)
-       emit_insn (gen_mthisi_di (gen_rtx_REG (TImode, MD_REG_FIRST),
-                                 MIPS_EPILOGUE_TEMP (DImode),
-                                 gen_rtx_REG (DImode, LO_REGNUM)));
-      else
-       emit_insn (gen_mthisi_di (gen_rtx_REG (DImode, MD_REG_FIRST),
-                                 MIPS_EPILOGUE_TEMP (SImode),
-                                 gen_rtx_REG (SImode, LO_REGNUM)));
-    }
-  else if ((TARGET_MIPS16 && !M16_REG_P (REGNO (reg)))
-          || ACC_REG_P (REGNO (reg)))
-    {
-      /* Can't restore directly; move through a temporary.  */
-      mips_emit_move (MIPS_EPILOGUE_TEMP (GET_MODE (reg)), mem);
-      mips_emit_move (reg, MIPS_EPILOGUE_TEMP (GET_MODE (reg)));
-    }
-  else
-    mips_emit_move (reg, mem);
+  mips_emit_save_slot_move (reg, mem, MIPS_EPILOGUE_TEMP (GET_MODE (reg)));
 }
 
 /* Emit any instructions needed before a return.  */
@@ -9884,10 +10542,10 @@ mips_expand_epilogue (bool sibcall_p)
             address into $7 rather than $31.  */
          if (TARGET_MIPS16
              && !GENERATE_MIPS16E_SAVE_RESTORE
-             && BITSET_P (frame->mask, 31))
+             && BITSET_P (frame->mask, RETURN_ADDR_REGNUM))
            regno = GP_REG_FIRST + 7;
          else
-           regno = GP_REG_FIRST + 31;
+           regno = RETURN_ADDR_REGNUM;
          emit_jump_insn (gen_return_internal (gen_rtx_REG (Pmode, regno)));
        }
     }
@@ -10109,6 +10767,15 @@ mips_cannot_change_mode_class (enum machine_mode from ATTRIBUTE_UNUSED,
   return reg_classes_intersect_p (FP_REGS, rclass);
 }
 
+/* Implement target hook small_register_classes_for_mode_p.  */
+
+static bool
+mips_small_register_classes_for_mode_p (enum machine_mode mode
+                                       ATTRIBUTE_UNUSED)
+{
+  return TARGET_MIPS16;
+}
+
 /* Return true if moves in mode MODE can use the FPU's mov.fmt instruction.  */
 
 static bool
@@ -10166,8 +10833,8 @@ mips_preferred_reload_class (rtx x, enum reg_class rclass)
 /* RCLASS is a class involved in a REGISTER_MOVE_COST calculation.
    Return a "canonical" class to represent it in later calculations.  */
 
-static enum reg_class
-mips_canonicalize_move_class (enum reg_class rclass)
+static reg_class_t
+mips_canonicalize_move_class (reg_class_t rclass)
 {
   /* All moves involving accumulator registers have the same cost.  */
   if (reg_class_subset_p (rclass, ACC_REGS))
@@ -10189,7 +10856,7 @@ mips_canonicalize_move_class (enum reg_class rclass)
 
 static int
 mips_move_to_gpr_cost (enum machine_mode mode ATTRIBUTE_UNUSED,
-                      enum reg_class from)
+                      reg_class_t from)
 {
   switch (from)
     {
@@ -10225,7 +10892,7 @@ mips_move_to_gpr_cost (enum machine_mode mode ATTRIBUTE_UNUSED,
    other classes handled by this function.  */
 
 static int
-mips_move_from_gpr_cost (enum machine_mode mode, enum reg_class to)
+mips_move_from_gpr_cost (enum machine_mode mode, reg_class_t to)
 {
   switch (to)
     {
@@ -10257,15 +10924,15 @@ mips_move_from_gpr_cost (enum machine_mode mode, enum reg_class to)
     }
 }
 
-/* Implement REGISTER_MOVE_COST.  Return 0 for classes that are the
+/* Implement TARGET_REGISTER_MOVE_COST.  Return 0 for classes that are the
    maximum of the move costs for subclasses; regclass will work out
    the maximum for us.  */
 
-int
+static int
 mips_register_move_cost (enum machine_mode mode,
-                        enum reg_class from, enum reg_class to)
+                        reg_class_t from, reg_class_t to)
 {
-  enum reg_class dregs;
+  reg_class_t dregs;
   int cost1, cost2;
 
   from = mips_canonicalize_move_class (from);
@@ -10301,16 +10968,25 @@ mips_register_move_cost (enum machine_mode mode,
   return 0;
 }
 
+/* Implement TARGET_MEMORY_MOVE_COST.  */
+
+static int
+mips_memory_move_cost (enum machine_mode mode, reg_class_t rclass, bool in)
+{
+  return (mips_cost->memory_latency
+         + memory_move_secondary_cost (mode, rclass, in));
+} 
+
 /* Implement TARGET_IRA_COVER_CLASSES.  */
 
-static const enum reg_class *
+static const reg_class_t *
 mips_ira_cover_classes (void)
 {
-  static const enum reg_class acc_classes[] = {
+  static const reg_class_t acc_classes[] = {
     GR_AND_ACC_REGS, FP_REGS, COP0_REGS, COP2_REGS, COP3_REGS,
     ST_REGS, LIM_REG_CLASSES
   };
-  static const enum reg_class no_acc_classes[] = {
+  static const reg_class_t no_acc_classes[] = {
     GR_REGS, FP_REGS, COP0_REGS, COP2_REGS, COP3_REGS,
     ST_REGS, LIM_REG_CLASSES
   };
@@ -10468,6 +11144,17 @@ mips_scalar_mode_supported_p (enum machine_mode mode)
   return default_scalar_mode_supported_p (mode);
 }
 \f
+/* Implement TARGET_VECTORIZE_PREFERRED_SIMD_MODE.  */
+
+static enum machine_mode
+mips_preferred_simd_mode (enum machine_mode mode ATTRIBUTE_UNUSED)
+{
+  if (TARGET_PAIRED_SINGLE_FLOAT
+      && mode == SFmode)
+    return V2SFmode;
+  return word_mode;
+}
+
 /* Implement TARGET_INIT_LIBFUNCS.  */
 
 #include "config/gofast.h"
@@ -10540,12 +11227,112 @@ mips_init_libfuncs (void)
     synchronize_libfunc = init_one_libfunc ("__sync_synchronize");
 }
 
+/* Build up a multi-insn sequence that loads label TARGET into $AT.  */
+
+static void
+mips_process_load_label (rtx target)
+{
+  rtx base, gp, intop;
+  HOST_WIDE_INT offset;
+
+  mips_multi_start ();
+  switch (mips_abi)
+    {
+    case ABI_N32:
+      mips_multi_add_insn ("lw\t%@,%%got_page(%0)(%+)", target, 0);
+      mips_multi_add_insn ("addiu\t%@,%@,%%got_ofst(%0)", target, 0);
+      break;
+
+    case ABI_64:
+      mips_multi_add_insn ("ld\t%@,%%got_page(%0)(%+)", target, 0);
+      mips_multi_add_insn ("daddiu\t%@,%@,%%got_ofst(%0)", target, 0);
+      break;
+
+    default:
+      gp = pic_offset_table_rtx;
+      if (mips_cfun_has_cprestore_slot_p ())
+       {
+         gp = gen_rtx_REG (Pmode, AT_REGNUM);
+         mips_get_cprestore_base_and_offset (&base, &offset, true);
+         if (!SMALL_OPERAND (offset))
+           {
+             intop = GEN_INT (CONST_HIGH_PART (offset));
+             mips_multi_add_insn ("lui\t%0,%1", gp, intop, 0);
+             mips_multi_add_insn ("addu\t%0,%0,%1", gp, base, 0);
+
+             base = gp;
+             offset = CONST_LOW_PART (offset);
+           }
+         intop = GEN_INT (offset);
+         if (ISA_HAS_LOAD_DELAY)
+           mips_multi_add_insn ("lw\t%0,%1(%2)%#", gp, intop, base, 0);
+         else
+           mips_multi_add_insn ("lw\t%0,%1(%2)", gp, intop, base, 0);
+       }
+      if (ISA_HAS_LOAD_DELAY)
+       mips_multi_add_insn ("lw\t%@,%%got(%0)(%1)%#", target, gp, 0);
+      else
+       mips_multi_add_insn ("lw\t%@,%%got(%0)(%1)", target, gp, 0);
+      mips_multi_add_insn ("addiu\t%@,%@,%%lo(%0)", target, 0);
+      break;
+    }
+}
+
+/* Return the number of instructions needed to load a label into $AT.  */
+
+static unsigned int
+mips_load_label_length (void)
+{
+  if (cfun->machine->load_label_length == 0)
+    {
+      mips_process_load_label (pc_rtx);
+      cfun->machine->load_label_length = mips_multi_num_insns;
+    }
+  return cfun->machine->load_label_length;
+}
+
+/* Emit an asm sequence to start a noat block and load the address
+   of a label into $1.  */
+
+void
+mips_output_load_label (rtx target)
+{
+  mips_push_asm_switch (&mips_noat);
+  if (TARGET_EXPLICIT_RELOCS)
+    {
+      mips_process_load_label (target);
+      mips_multi_write ();
+    }
+  else
+    {
+      if (Pmode == DImode)
+       output_asm_insn ("dla\t%@,%0", &target);
+      else
+       output_asm_insn ("la\t%@,%0", &target);
+    }
+}
+
 /* Return the length of INSN.  LENGTH is the initial length computed by
    attributes in the machine-description file.  */
 
 int
 mips_adjust_insn_length (rtx insn, int length)
 {
+  /* mips.md uses MAX_PIC_BRANCH_LENGTH as a placeholder for the length
+     of a PIC long-branch sequence.  Substitute the correct value.  */
+  if (length == MAX_PIC_BRANCH_LENGTH
+      && INSN_CODE (insn) >= 0
+      && get_attr_type (insn) == TYPE_BRANCH)
+    {
+      /* Add the branch-over instruction and its delay slot, if this
+        is a conditional branch.  */
+      length = simplejump_p (insn) ? 0 : 8;
+
+      /* Load the label into $AT and jump to it.  Ignore the delay
+        slot of the jump.  */
+      length += mips_load_label_length () + 4;
+    }
+
   /* A unconditional jump has an unfilled delay slot if it is not part
      of a sequence.  A conditional jump normally has a delay slot, but
      does not on MIPS16.  */
@@ -10577,38 +11364,9 @@ mips_adjust_insn_length (rtx insn, int length)
   return length;
 }
 
-/* Return an asm sequence to start a noat block and load the address
-   of a label into $1.  */
-
-const char *
-mips_output_load_label (void)
-{
-  if (TARGET_EXPLICIT_RELOCS)
-    switch (mips_abi)
-      {
-      case ABI_N32:
-       return "%[lw\t%@,%%got_page(%0)(%+)\n\taddiu\t%@,%@,%%got_ofst(%0)";
-
-      case ABI_64:
-       return "%[ld\t%@,%%got_page(%0)(%+)\n\tdaddiu\t%@,%@,%%got_ofst(%0)";
-
-      default:
-       if (ISA_HAS_LOAD_DELAY)
-         return "%[lw\t%@,%%got(%0)(%+)%#\n\taddiu\t%@,%@,%%lo(%0)";
-       return "%[lw\t%@,%%got(%0)(%+)\n\taddiu\t%@,%@,%%lo(%0)";
-      }
-  else
-    {
-      if (Pmode == DImode)
-       return "%[dla\t%@,%0";
-      else
-       return "%[la\t%@,%0";
-    }
-}
-
 /* Return the assembly code for INSN, which has the operands given by
-   OPERANDS, and which branches to OPERANDS[1] if some condition is true.
-   BRANCH_IF_TRUE is the asm template that should be used if OPERANDS[1]
+   OPERANDS, and which branches to OPERANDS[0] if some condition is true.
+   BRANCH_IF_TRUE is the asm template that should be used if OPERANDS[0]
    is in range of a direct branch.  BRANCH_IF_FALSE is an inverted
    version of BRANCH_IF_TRUE.  */
 
@@ -10620,7 +11378,7 @@ mips_output_conditional_branch (rtx insn, rtx *operands,
   unsigned int length;
   rtx taken, not_taken;
 
-  gcc_assert (LABEL_P (operands[1]));  
+  gcc_assert (LABEL_P (operands[0]));
 
   length = get_attr_length (insn);
   if (length <= 8)
@@ -10634,10 +11392,10 @@ mips_output_conditional_branch (rtx insn, rtx *operands,
      not use branch-likely instructions.  */
   mips_branch_likely = false;
   not_taken = gen_label_rtx ();
-  taken = operands[1];
+  taken = operands[0];
 
   /* Generate the reversed branch to NOT_TAKEN.  */
-  operands[1] = not_taken;
+  operands[0] = not_taken;
   output_asm_insn (branch_if_false, operands);
 
   /* If INSN has a delay slot, we must provide delay slots for both the
@@ -10659,11 +11417,11 @@ mips_output_conditional_branch (rtx insn, rtx *operands,
     }
 
   /* Output the unconditional branch to TAKEN.  */
-  if (length <= 16)
-    output_asm_insn ("j\t%0%/", &taken);
+  if (TARGET_ABSOLUTE_JUMPS)
+    output_asm_insn (MIPS_ABSOLUTE_JUMP ("j\t%0%/"), &taken);
   else
     {
-      output_asm_insn (mips_output_load_label (), &taken);
+      mips_output_load_label (taken);
       output_asm_insn ("jr\t%@%]%/", 0);
     }
 
@@ -10689,10 +11447,10 @@ mips_output_conditional_branch (rtx insn, rtx *operands,
   return "";
 }
 
-/* Return the assembly code for INSN, which branches to OPERANDS[1]
+/* Return the assembly code for INSN, which branches to OPERANDS[0]
    if some ordering condition is true.  The condition is given by
-   OPERANDS[0] if !INVERTED_P, otherwise it is the inverse of
-   OPERANDS[0].  OPERANDS[2] is the comparison's first operand;
+   OPERANDS[1] if !INVERTED_P, otherwise it is the inverse of
+   OPERANDS[1].  OPERANDS[2] is the comparison's first operand;
    its second is always zero.  */
 
 const char *
@@ -10700,17 +11458,17 @@ mips_output_order_conditional_branch (rtx insn, rtx *operands, bool inverted_p)
 {
   const char *branch[2];
 
-  /* Make BRANCH[1] branch to OPERANDS[1] when the condition is true.
+  /* Make BRANCH[1] branch to OPERANDS[0] when the condition is true.
      Make BRANCH[0] branch on the inverse condition.  */
-  switch (GET_CODE (operands[0]))
+  switch (GET_CODE (operands[1]))
     {
       /* These cases are equivalent to comparisons against zero.  */
     case LEU:
       inverted_p = !inverted_p;
       /* Fall through.  */
     case GTU:
-      branch[!inverted_p] = MIPS_BRANCH ("bne", "%2,%.,%1");
-      branch[inverted_p] = MIPS_BRANCH ("beq", "%2,%.,%1");
+      branch[!inverted_p] = MIPS_BRANCH ("bne", "%2,%.,%0");
+      branch[inverted_p] = MIPS_BRANCH ("beq", "%2,%.,%0");
       break;
 
       /* These cases are always true or always false.  */
@@ -10718,27 +11476,306 @@ mips_output_order_conditional_branch (rtx insn, rtx *operands, bool inverted_p)
       inverted_p = !inverted_p;
       /* Fall through.  */
     case GEU:
-      branch[!inverted_p] = MIPS_BRANCH ("beq", "%.,%.,%1");
-      branch[inverted_p] = MIPS_BRANCH ("bne", "%.,%.,%1");
+      branch[!inverted_p] = MIPS_BRANCH ("beq", "%.,%.,%0");
+      branch[inverted_p] = MIPS_BRANCH ("bne", "%.,%.,%0");
       break;
 
     default:
-      branch[!inverted_p] = MIPS_BRANCH ("b%C0z", "%2,%1");
-      branch[inverted_p] = MIPS_BRANCH ("b%N0z", "%2,%1");
+      branch[!inverted_p] = MIPS_BRANCH ("b%C1z", "%2,%0");
+      branch[inverted_p] = MIPS_BRANCH ("b%N1z", "%2,%0");
       break;
     }
   return mips_output_conditional_branch (insn, operands, branch[1], branch[0]);
 }
 \f
-/* Return the assembly code for __sync_*() loop LOOP.  The loop should support
-   both normal and likely branches, using %? and %~ where appropriate.  */
+/* Start a block of code that needs access to the LL, SC and SYNC
+   instructions.  */
+
+static void
+mips_start_ll_sc_sync_block (void)
+{
+  if (!ISA_HAS_LL_SC)
+    {
+      output_asm_insn (".set\tpush", 0);
+      output_asm_insn (".set\tmips2", 0);
+    }
+}
+
+/* End a block started by mips_start_ll_sc_sync_block.  */
+
+static void
+mips_end_ll_sc_sync_block (void)
+{
+  if (!ISA_HAS_LL_SC)
+    output_asm_insn (".set\tpop", 0);
+}
+
+/* Output and/or return the asm template for a sync instruction.  */
 
 const char *
-mips_output_sync_loop (const char *loop)
+mips_output_sync (void)
 {
-  /* Use branch-likely instructions to work around the LL/SC R10000 errata.  */
+  mips_start_ll_sc_sync_block ();
+  output_asm_insn ("sync", 0);
+  mips_end_ll_sc_sync_block ();
+  return "";
+}
+
+/* Return the asm template associated with sync_insn1 value TYPE.
+   IS_64BIT_P is true if we want a 64-bit rather than 32-bit operation.  */
+
+static const char *
+mips_sync_insn1_template (enum attr_sync_insn1 type, bool is_64bit_p)
+{
+  switch (type)
+    {
+    case SYNC_INSN1_MOVE:
+      return "move\t%0,%z2";
+    case SYNC_INSN1_LI:
+      return "li\t%0,%2";
+    case SYNC_INSN1_ADDU:
+      return is_64bit_p ? "daddu\t%0,%1,%z2" : "addu\t%0,%1,%z2";
+    case SYNC_INSN1_ADDIU:
+      return is_64bit_p ? "daddiu\t%0,%1,%2" : "addiu\t%0,%1,%2";
+    case SYNC_INSN1_SUBU:
+      return is_64bit_p ? "dsubu\t%0,%1,%z2" : "subu\t%0,%1,%z2";
+    case SYNC_INSN1_AND:
+      return "and\t%0,%1,%z2";
+    case SYNC_INSN1_ANDI:
+      return "andi\t%0,%1,%2";
+    case SYNC_INSN1_OR:
+      return "or\t%0,%1,%z2";
+    case SYNC_INSN1_ORI:
+      return "ori\t%0,%1,%2";
+    case SYNC_INSN1_XOR:
+      return "xor\t%0,%1,%z2";
+    case SYNC_INSN1_XORI:
+      return "xori\t%0,%1,%2";
+    }
+  gcc_unreachable ();
+}
+
+/* Return the asm template associated with sync_insn2 value TYPE.  */
+
+static const char *
+mips_sync_insn2_template (enum attr_sync_insn2 type)
+{
+  switch (type)
+    {
+    case SYNC_INSN2_NOP:
+      gcc_unreachable ();
+    case SYNC_INSN2_AND:
+      return "and\t%0,%1,%z2";
+    case SYNC_INSN2_XOR:
+      return "xor\t%0,%1,%z2";
+    case SYNC_INSN2_NOT:
+      return "nor\t%0,%1,%.";
+    }
+  gcc_unreachable ();
+}
+
+/* OPERANDS are the operands to a sync loop instruction and INDEX is
+   the value of the one of the sync_* attributes.  Return the operand
+   referred to by the attribute, or DEFAULT_VALUE if the insn doesn't
+   have the associated attribute.  */
+
+static rtx
+mips_get_sync_operand (rtx *operands, int index, rtx default_value)
+{
+  if (index > 0)
+    default_value = operands[index - 1];
+  return default_value;
+}
+
+/* INSN is a sync loop with operands OPERANDS.  Build up a multi-insn
+   sequence for it.  */
+
+static void
+mips_process_sync_loop (rtx insn, rtx *operands)
+{
+  rtx at, mem, oldval, newval, inclusive_mask, exclusive_mask;
+  rtx required_oldval, insn1_op2, tmp1, tmp2, tmp3;
+  unsigned int tmp3_insn;
+  enum attr_sync_insn1 insn1;
+  enum attr_sync_insn2 insn2;
+  bool is_64bit_p;
+
+  /* Read an operand from the sync_WHAT attribute and store it in
+     variable WHAT.  DEFAULT is the default value if no attribute
+     is specified.  */
+#define READ_OPERAND(WHAT, DEFAULT) \
+  WHAT = mips_get_sync_operand (operands, (int) get_attr_sync_##WHAT (insn), \
+                               DEFAULT)
+
+  /* Read the memory.  */
+  READ_OPERAND (mem, 0);
+  gcc_assert (mem);
+  is_64bit_p = (GET_MODE_BITSIZE (GET_MODE (mem)) == 64);
+
+  /* Read the other attributes.  */
+  at = gen_rtx_REG (GET_MODE (mem), AT_REGNUM);
+  READ_OPERAND (oldval, at);
+  READ_OPERAND (newval, at);
+  READ_OPERAND (inclusive_mask, 0);
+  READ_OPERAND (exclusive_mask, 0);
+  READ_OPERAND (required_oldval, 0);
+  READ_OPERAND (insn1_op2, 0);
+  insn1 = get_attr_sync_insn1 (insn);
+  insn2 = get_attr_sync_insn2 (insn);
+
+  mips_multi_start ();
+
+  /* Output the release side of the memory barrier.  */
+  if (get_attr_sync_release_barrier (insn) == SYNC_RELEASE_BARRIER_YES)
+    {
+      if (required_oldval == 0 && TARGET_OCTEON)
+       {
+         /* Octeon doesn't reorder reads, so a full barrier can be
+            created by using SYNCW to order writes combined with the
+            write from the following SC.  When the SC successfully
+            completes, we know that all preceding writes are also
+            committed to the coherent memory system.  It is possible
+            for a single SYNCW to fail, but a pair of them will never
+            fail, so we use two.  */
+         mips_multi_add_insn ("syncw", NULL);
+         mips_multi_add_insn ("syncw", NULL);
+       }
+      else
+       mips_multi_add_insn ("sync", NULL);
+    }
+
+  /* Output the branch-back label.  */
+  mips_multi_add_label ("1:");
+
+  /* OLDVAL = *MEM.  */
+  mips_multi_add_insn (is_64bit_p ? "lld\t%0,%1" : "ll\t%0,%1",
+                      oldval, mem, NULL);
+
+  /* if ((OLDVAL & INCLUSIVE_MASK) != REQUIRED_OLDVAL) goto 2.  */
+  if (required_oldval)
+    {
+      if (inclusive_mask == 0)
+       tmp1 = oldval;
+      else
+       {
+         gcc_assert (oldval != at);
+         mips_multi_add_insn ("and\t%0,%1,%2",
+                              at, oldval, inclusive_mask, NULL);
+         tmp1 = at;
+       }
+      mips_multi_add_insn ("bne\t%0,%z1,2f", tmp1, required_oldval, NULL);
+    }
+
+  /* $TMP1 = OLDVAL & EXCLUSIVE_MASK.  */
+  if (exclusive_mask == 0)
+    tmp1 = const0_rtx;
+  else
+    {
+      gcc_assert (oldval != at);
+      mips_multi_add_insn ("and\t%0,%1,%z2",
+                          at, oldval, exclusive_mask, NULL);
+      tmp1 = at;
+    }
+
+  /* $TMP2 = INSN1 (OLDVAL, INSN1_OP2).
+
+     We can ignore moves if $TMP4 != INSN1_OP2, since we'll still emit
+     at least one instruction in that case.  */
+  if (insn1 == SYNC_INSN1_MOVE
+      && (tmp1 != const0_rtx || insn2 != SYNC_INSN2_NOP))
+    tmp2 = insn1_op2;
+  else
+    {
+      mips_multi_add_insn (mips_sync_insn1_template (insn1, is_64bit_p),
+                          newval, oldval, insn1_op2, NULL);
+      tmp2 = newval;
+    }
+
+  /* $TMP3 = INSN2 ($TMP2, INCLUSIVE_MASK).  */
+  if (insn2 == SYNC_INSN2_NOP)
+    tmp3 = tmp2;
+  else
+    {
+      mips_multi_add_insn (mips_sync_insn2_template (insn2),
+                          newval, tmp2, inclusive_mask, NULL);
+      tmp3 = newval;
+    }
+  tmp3_insn = mips_multi_last_index ();
+
+  /* $AT = $TMP1 | $TMP3.  */
+  if (tmp1 == const0_rtx || tmp3 == const0_rtx)
+    {
+      mips_multi_set_operand (tmp3_insn, 0, at);
+      tmp3 = at;
+    }
+  else
+    {
+      gcc_assert (tmp1 != tmp3);
+      mips_multi_add_insn ("or\t%0,%1,%2", at, tmp1, tmp3, NULL);
+    }
+
+  /* if (!commit (*MEM = $AT)) goto 1.
+
+     This will sometimes be a delayed branch; see the write code below
+     for details.  */
+  mips_multi_add_insn (is_64bit_p ? "scd\t%0,%1" : "sc\t%0,%1", at, mem, NULL);
+  mips_multi_add_insn ("beq%?\t%0,%.,1b", at, NULL);
+
+  /* if (INSN1 != MOVE && INSN1 != LI) NEWVAL = $TMP3 [delay slot].  */
+  if (insn1 != SYNC_INSN1_MOVE && insn1 != SYNC_INSN1_LI && tmp3 != newval)
+    {
+      mips_multi_copy_insn (tmp3_insn);
+      mips_multi_set_operand (mips_multi_last_index (), 0, newval);
+    }
+  else
+    mips_multi_add_insn ("nop", NULL);
+
+  /* Output the acquire side of the memory barrier.  */
+  if (TARGET_SYNC_AFTER_SC)
+    mips_multi_add_insn ("sync", NULL);
+
+  /* Output the exit label, if needed.  */
+  if (required_oldval)
+    mips_multi_add_label ("2:");
+
+#undef READ_OPERAND
+}
+
+/* Output and/or return the asm template for sync loop INSN, which has
+   the operands given by OPERANDS.  */
+
+const char *
+mips_output_sync_loop (rtx insn, rtx *operands)
+{
+  mips_process_sync_loop (insn, operands);
+
+  /* Use branch-likely instructions to work around the LL/SC R10000
+     errata.  */
   mips_branch_likely = TARGET_FIX_R10000;
-  return loop;
+
+  mips_push_asm_switch (&mips_noreorder);
+  mips_push_asm_switch (&mips_nomacro);
+  mips_push_asm_switch (&mips_noat);
+  mips_start_ll_sc_sync_block ();
+
+  mips_multi_write ();
+
+  mips_end_ll_sc_sync_block ();
+  mips_pop_asm_switch (&mips_noat);
+  mips_pop_asm_switch (&mips_nomacro);
+  mips_pop_asm_switch (&mips_noreorder);
+
+  return "";
+}
+
+/* Return the number of individual instructions in sync loop INSN,
+   which has the operands given by OPERANDS.  */
+
+unsigned int
+mips_sync_loop_insns (rtx insn, rtx *operands)
+{
+  mips_process_sync_loop (insn, operands);
+  return mips_multi_num_insns;
 }
 \f
 /* Return the assembly code for DIV or DDIV instruction DIVISION, which has
@@ -10849,10 +11886,17 @@ mips_output_division (const char *division, rtx *operands)
          s = "bnez\t%2,1f\n\tbreak\t7\n1:";
        }
       else if (GENERATE_DIVIDE_TRAPS)
-        {
-         output_asm_insn (s, operands);
-         s = "teq\t%2,%.,7";
-        }
+       {
+         /* Avoid long replay penalty on load miss by putting the trap before
+            the divide.  */
+         if (TUNE_74K)
+           output_asm_insn ("teq\t%2,%.,7", operands);
+         else
+           {
+             output_asm_insn (s, operands);
+             s = "teq\t%2,%.,7";
+           }
+       }
       else
        {
          output_asm_insn ("%(bne\t%2,%.,1f", operands);
@@ -11315,7 +12359,7 @@ static enum attr_type mips_last_74k_agen_insn = TYPE_UNKNOWN;
 static void
 mips_74k_agen_init (rtx insn)
 {
-  if (!insn || !NONJUMP_INSN_P (insn))
+  if (!insn || CALL_P (insn) || JUMP_P (insn))
     mips_last_74k_agen_insn = TYPE_UNKNOWN;
   else
     {
@@ -11396,11 +12440,11 @@ mips_sched_init (FILE *file ATTRIBUTE_UNUSED, int verbose ATTRIBUTE_UNUSED,
   mips_ls2.falu1_turn_p = true;
 }
 
-/* Implement TARGET_SCHED_REORDER and TARGET_SCHED_REORDER2.  */
+/* Subroutine used by TARGET_SCHED_REORDER and TARGET_SCHED_REORDER2.  */
 
-static int
-mips_sched_reorder (FILE *file ATTRIBUTE_UNUSED, int verbose ATTRIBUTE_UNUSED,
-                   rtx *ready, int *nreadyp, int cycle ATTRIBUTE_UNUSED)
+static void
+mips_sched_reorder_1 (FILE *file ATTRIBUTE_UNUSED, int verbose ATTRIBUTE_UNUSED,
+                     rtx *ready, int *nreadyp, int cycle ATTRIBUTE_UNUSED)
 {
   if (!reload_completed
       && TUNE_MACC_CHAINS
@@ -11415,10 +12459,28 @@ mips_sched_reorder (FILE *file ATTRIBUTE_UNUSED, int verbose ATTRIBUTE_UNUSED,
 
   if (TUNE_74K)
     mips_74k_agen_reorder (ready, *nreadyp);
+}
+
+/* Implement TARGET_SCHED_REORDER.  */
 
+static int
+mips_sched_reorder (FILE *file ATTRIBUTE_UNUSED, int verbose ATTRIBUTE_UNUSED,
+                   rtx *ready, int *nreadyp, int cycle ATTRIBUTE_UNUSED)
+{
+  mips_sched_reorder_1 (file, verbose, ready, nreadyp, cycle);
   return mips_issue_rate ();
 }
 
+/* Implement TARGET_SCHED_REORDER2.  */
+
+static int
+mips_sched_reorder2 (FILE *file ATTRIBUTE_UNUSED, int verbose ATTRIBUTE_UNUSED,
+                    rtx *ready, int *nreadyp, int cycle ATTRIBUTE_UNUSED)
+{
+  mips_sched_reorder_1 (file, verbose, ready, nreadyp, cycle);
+  return cached_can_issue_more;
+}
+
 /* Update round-robin counters for ALU1/2 and FALU1/2.  */
 
 static void
@@ -11459,7 +12521,8 @@ mips_variable_issue (FILE *file ATTRIBUTE_UNUSED, int verbose ATTRIBUTE_UNUSED,
   /* Ignore USEs and CLOBBERs; don't count them against the issue rate.  */
   if (USEFUL_INSN_P (insn))
     {
-      more--;
+      if (get_attr_type (insn) != TYPE_GHOST)
+       more--;
       if (!reload_completed && TUNE_MACC_CHAINS)
        mips_macc_chains_record (insn);
       vr4130_last_insn = insn;
@@ -11475,6 +12538,7 @@ mips_variable_issue (FILE *file ATTRIBUTE_UNUSED, int verbose ATTRIBUTE_UNUSED,
              || recog_memoized (insn) < 0
              || get_attr_type (insn) != TYPE_MULTI);
 
+  cached_can_issue_more = more;
   return more;
 }
 \f
@@ -11666,6 +12730,8 @@ AVAIL_NON_MIPS16 (cache, TARGET_CACHE_BUILTIN)
 #define CODE_FOR_mips_subq_ph CODE_FOR_subv2hi3
 #define CODE_FOR_mips_subu_qb CODE_FOR_subv4qi3
 #define CODE_FOR_mips_mul_ph CODE_FOR_mulv2hi3
+#define CODE_FOR_mips_mult CODE_FOR_mulsidi3_32bit
+#define CODE_FOR_mips_multu CODE_FOR_umulsidi3_32bit
 
 #define CODE_FOR_loongson_packsswh CODE_FOR_vec_pack_ssat_v2si
 #define CODE_FOR_loongson_packsshb CODE_FOR_vec_pack_ssat_v4hi
@@ -11683,6 +12749,13 @@ AVAIL_NON_MIPS16 (cache, TARGET_CACHE_BUILTIN)
 #define CODE_FOR_loongson_pminub CODE_FOR_uminv8qi3
 #define CODE_FOR_loongson_pmulhuh CODE_FOR_umulv4hi3_highpart
 #define CODE_FOR_loongson_pmulhh CODE_FOR_smulv4hi3_highpart
+#define CODE_FOR_loongson_pmullh CODE_FOR_mulv4hi3
+#define CODE_FOR_loongson_psllh CODE_FOR_ashlv4hi3
+#define CODE_FOR_loongson_psllw CODE_FOR_ashlv2si3
+#define CODE_FOR_loongson_psrlh CODE_FOR_lshrv4hi3
+#define CODE_FOR_loongson_psrlw CODE_FOR_lshrv2si3
+#define CODE_FOR_loongson_psrah CODE_FOR_ashrv4hi3
+#define CODE_FOR_loongson_psraw CODE_FOR_ashrv2si3
 #define CODE_FOR_loongson_psubw CODE_FOR_subv2si3
 #define CODE_FOR_loongson_psubh CODE_FOR_subv4hi3
 #define CODE_FOR_loongson_psubb CODE_FOR_subv8qi3
@@ -11857,17 +12930,17 @@ static const struct mips_builtin_description mips_builtins[] = {
   DIRECT_BUILTIN (extpdp, MIPS_SI_FTYPE_DI_SI, dsp_32),
   DIRECT_BUILTIN (shilo, MIPS_DI_FTYPE_DI_SI, dsp_32),
   DIRECT_BUILTIN (mthlip, MIPS_DI_FTYPE_DI_SI, dsp_32),
+  DIRECT_BUILTIN (madd, MIPS_DI_FTYPE_DI_SI_SI, dsp_32),
+  DIRECT_BUILTIN (maddu, MIPS_DI_FTYPE_DI_USI_USI, dsp_32),
+  DIRECT_BUILTIN (msub, MIPS_DI_FTYPE_DI_SI_SI, dsp_32),
+  DIRECT_BUILTIN (msubu, MIPS_DI_FTYPE_DI_USI_USI, dsp_32),
+  DIRECT_BUILTIN (mult, MIPS_DI_FTYPE_SI_SI, dsp_32),
+  DIRECT_BUILTIN (multu, MIPS_DI_FTYPE_USI_USI, dsp_32),
 
   /* The following are for the MIPS DSP ASE REV 2 (32-bit only).  */
   DIRECT_BUILTIN (dpa_w_ph, MIPS_DI_FTYPE_DI_V2HI_V2HI, dspr2_32),
   DIRECT_BUILTIN (dps_w_ph, MIPS_DI_FTYPE_DI_V2HI_V2HI, dspr2_32),
-  DIRECT_BUILTIN (madd, MIPS_DI_FTYPE_DI_SI_SI, dspr2_32),
-  DIRECT_BUILTIN (maddu, MIPS_DI_FTYPE_DI_USI_USI, dspr2_32),
-  DIRECT_BUILTIN (msub, MIPS_DI_FTYPE_DI_SI_SI, dspr2_32),
-  DIRECT_BUILTIN (msubu, MIPS_DI_FTYPE_DI_USI_USI, dspr2_32),
   DIRECT_BUILTIN (mulsa_w_ph, MIPS_DI_FTYPE_DI_V2HI_V2HI, dspr2_32),
-  DIRECT_BUILTIN (mult, MIPS_DI_FTYPE_SI_SI, dspr2_32),
-  DIRECT_BUILTIN (multu, MIPS_DI_FTYPE_USI_USI, dspr2_32),
   DIRECT_BUILTIN (dpax_w_ph, MIPS_DI_FTYPE_DI_V2HI_V2HI, dspr2_32),
   DIRECT_BUILTIN (dpsx_w_ph, MIPS_DI_FTYPE_DI_V2HI_V2HI, dspr2_32),
   DIRECT_BUILTIN (dpaqx_s_w_ph, MIPS_DI_FTYPE_DI_V2HI_V2HI, dspr2_32),
@@ -11980,6 +13053,10 @@ static const struct mips_builtin_description mips_builtins[] = {
   DIRECT_NO_TARGET_BUILTIN (cache, MIPS_VOID_FTYPE_SI_CVPOINTER, cache)
 };
 
+/* Index I is the function declaration for mips_builtins[I], or null if the
+   function isn't defined on this target.  */
+static GTY(()) tree mips_builtin_decls[ARRAY_SIZE (mips_builtins)];
+
 /* MODE is a vector mode whose elements have type TYPE.  Return the type
    of the vector itself.  */
 
@@ -12096,12 +13173,23 @@ mips_init_builtins (void)
     {
       d = &mips_builtins[i];
       if (d->avail ())
-       add_builtin_function (d->name,
-                             mips_build_function_type (d->function_type),
-                             i, BUILT_IN_MD, NULL, NULL);
+       mips_builtin_decls[i]
+         = add_builtin_function (d->name,
+                                 mips_build_function_type (d->function_type),
+                                 i, BUILT_IN_MD, NULL, NULL);
     }
 }
 
+/* Implement TARGET_BUILTIN_DECL.  */
+
+static tree
+mips_builtin_decl (unsigned int code, bool initialize_p ATTRIBUTE_UNUSED)
+{
+  if (code >= ARRAY_SIZE (mips_builtins))
+    return error_mark_node;
+  return mips_builtin_decls[code];
+}
+
 /* Take argument ARGNO from EXP's argument list and convert it into a
    form suitable for input operand OPNO of instruction ICODE.  Return the
    value.  */
@@ -12372,8 +13460,8 @@ mips_expand_builtin (tree exp, rtx target, rtx subtarget ATTRIBUTE_UNUSED,
   gcc_assert (avail != 0);
   if (TARGET_MIPS16)
     {
-      error ("built-in function %qs not supported for MIPS16",
-            IDENTIFIER_POINTER (DECL_NAME (fndecl)));
+      error ("built-in function %qE not supported for MIPS16",
+            DECL_NAME (fndecl));
       return ignore ? const0_rtx : CONST0_RTX (mode);
     }
   switch (d->builtin_type)
@@ -12606,6 +13694,15 @@ mips16_rewrite_pool_refs (rtx *x, void *data)
   return GET_CODE (*x) == CONST ? -1 : 0;
 }
 
+/* Return whether CFG is used in mips_reorg.  */
+
+static bool
+mips_cfg_in_reorg (void)
+{
+  return (mips_r10k_cache_barrier != R10K_CACHE_BARRIER_NONE
+         || TARGET_RELAX_PIC_CALLS);
+}
+
 /* Build MIPS16 constant pools.  */
 
 static void
@@ -12618,13 +13715,16 @@ mips16_lay_out_constants (void)
   if (!TARGET_MIPS16_PCREL_LOADS)
     return;
 
-  split_all_insns_noflow ();
+  if (mips_cfg_in_reorg ())
+    split_all_insns ();
+  else
+    split_all_insns_noflow ();
   barrier = 0;
   memset (&pool, 0, sizeof (pool));
   for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
     {
       /* Rewrite constant pool references in INSN.  */
-      if (INSN_P (insn))
+      if (USEFUL_INSN_P (insn))
        {
          info.insn = insn;
          info.pool = &pool;
@@ -12954,14 +14054,6 @@ r10k_insert_cache_barriers (void)
       return;
     }
 
-  /* Restore the BLOCK_FOR_INSN pointers, which are needed by DF.  */
-  compute_bb_for_insn ();
-
-  /* Create def-use chains.  */
-  df_set_flags (DF_EQ_NOTES);
-  df_chain_add_problem (DF_UD_CHAIN);
-  df_analyze ();
-
   /* Calculate dominators.  */
   calculate_dominance_info (CDI_DOMINATORS);
 
@@ -12994,7 +14086,7 @@ r10k_insert_cache_barriers (void)
         - the first instruction in an unprotected region otherwise.  */
       for (insn = BB_HEAD (bb); insn != end; insn = NEXT_INSN (insn))
        {
-         if (unprotected_region && INSN_P (insn))
+         if (unprotected_region && USEFUL_INSN_P (insn))
            {
              if (recog_memoized (insn) == CODE_FOR_mips_cache)
                /* This CACHE instruction protects the following code.  */
@@ -13040,10 +14132,188 @@ r10k_insert_cache_barriers (void)
   sbitmap_free (protected_bbs);
 
   free_dominance_info (CDI_DOMINATORS);
+}
+\f
+/* If INSN is a call, return the underlying CALL expr.  Return NULL_RTX
+   otherwise.  If INSN has two call rtx, then store the second one in
+   SECOND_CALL.  */
 
-  df_finish_pass (false);
+static rtx
+mips_call_expr_from_insn (rtx insn, rtx *second_call)
+{
+  rtx x;
+  rtx x2;
+
+  if (!CALL_P (insn))
+    return NULL_RTX;
+
+  x = PATTERN (insn);
+  if (GET_CODE (x) == PARALLEL)
+    {
+      /* Calls returning complex values have two CALL rtx.  Look for the second
+        one here, and return it via the SECOND_CALL arg.  */
+      x2 = XVECEXP (x, 0, 1);
+      if (GET_CODE (x2) == SET)
+       x2 = XEXP (x2, 1);
+      if (GET_CODE (x2) == CALL)
+       *second_call = x2;
+
+      x = XVECEXP (x, 0, 0);
+    }
+  if (GET_CODE (x) == SET)
+    x = XEXP (x, 1);
+  gcc_assert (GET_CODE (x) == CALL);
+
+  return x;
+}
+
+/* REG is set in DEF.  See if the definition is one of the ways we load a
+   register with a symbol address for a mips_use_pic_fn_addr_reg_p call.  If
+   it is return the symbol reference of the function, otherwise return
+   NULL_RTX.  */
+
+static rtx
+mips_pic_call_symbol_from_set (df_ref def, rtx reg)
+{
+  rtx def_insn, set;
+
+  if (DF_REF_IS_ARTIFICIAL (def))
+    return NULL_RTX;
+
+  def_insn = DF_REF_INSN (def);
+  set = single_set (def_insn);
+  if (set && rtx_equal_p (SET_DEST (set), reg))
+    {
+      rtx note, src, symbol;
+
+      /* First, look at REG_EQUAL/EQUIV notes.  */
+      note = find_reg_equal_equiv_note (def_insn);
+      if (note && GET_CODE (XEXP (note, 0)) == SYMBOL_REF)
+       return XEXP (note, 0);
+
+      /* For %call16 references we don't have REG_EQUAL.  */
+      src = SET_SRC (set);
+      symbol = mips_strip_unspec_call (src);
+      if (symbol)
+       {
+         gcc_assert (GET_CODE (symbol) == SYMBOL_REF);
+         return symbol;
+       }
+
+      /* Follow simple register copies.  */
+      if (REG_P (src))
+       return mips_find_pic_call_symbol (def_insn, src);
+    }
+
+  return NULL_RTX;
+}
+
+/* Find the definition of the use of REG in INSN.  See if the definition is
+   one of the ways we load a register with a symbol address for a
+   mips_use_pic_fn_addr_reg_p call.  If it is return the symbol reference of
+   the function, otherwise return NULL_RTX.  */
+
+static rtx
+mips_find_pic_call_symbol (rtx insn, rtx reg)
+{
+  df_ref use;
+  struct df_link *defs;
+  rtx symbol;
+
+  use = df_find_use (insn, regno_reg_rtx[REGNO (reg)]);
+  if (!use)
+    return NULL_RTX;
+  defs = DF_REF_CHAIN (use);
+  if (!defs)
+    return NULL_RTX;
+  symbol = mips_pic_call_symbol_from_set (defs->ref, reg);
+  if (!symbol)
+    return NULL_RTX;
+
+  /* If we have more than one definition, they need to be identical.  */
+  for (defs = defs->next; defs; defs = defs->next)
+    {
+      rtx other;
+
+      other = mips_pic_call_symbol_from_set (defs->ref, reg);
+      if (!rtx_equal_p (symbol, other))
+       return NULL_RTX;
+    }
+
+  return symbol;
+}
+
+/* Replace the args_size operand of the call expression CALL with the
+   call-attribute UNSPEC and fill in SYMBOL as the function symbol.  */
+
+static void
+mips_annotate_pic_call_expr (rtx call, rtx symbol)
+{
+  rtx args_size;
+
+  args_size = XEXP (call, 1);
+  XEXP (call, 1) = gen_rtx_UNSPEC (GET_MODE (args_size),
+                                  gen_rtvec (2, args_size, symbol),
+                                  UNSPEC_CALL_ATTR);
+}
 
-  free_bb_for_insn ();
+/* OPERANDS[ARGS_SIZE_OPNO] is the arg_size operand of a CALL expression.  See
+   if instead of the arg_size argument it contains the call attributes.  If
+   yes return true along with setting OPERANDS[ARGS_SIZE_OPNO] to the function
+   symbol from the call attributes.  Also return false if ARGS_SIZE_OPNO is
+   -1.  */
+
+bool
+mips_get_pic_call_symbol (rtx *operands, int args_size_opno)
+{
+  rtx args_size, symbol;
+
+  if (!TARGET_RELAX_PIC_CALLS || args_size_opno == -1)
+    return false;
+
+  args_size = operands[args_size_opno];
+  if (GET_CODE (args_size) != UNSPEC)
+    return false;
+  gcc_assert (XINT (args_size, 1) == UNSPEC_CALL_ATTR);
+
+  symbol = XVECEXP (args_size, 0, 1);
+  gcc_assert (GET_CODE (symbol) == SYMBOL_REF);
+
+  operands[args_size_opno] = symbol;
+  return true;
+}
+
+/* Use DF to annotate PIC indirect calls with the function symbol they
+   dispatch to.  */
+
+static void
+mips_annotate_pic_calls (void)
+{
+  basic_block bb;
+  rtx insn;
+
+  FOR_EACH_BB (bb)
+    FOR_BB_INSNS (bb, insn)
+    {
+      rtx call, reg, symbol, second_call;
+
+      second_call = 0;
+      call = mips_call_expr_from_insn (insn, &second_call);
+      if (!call)
+       continue;
+      gcc_assert (MEM_P (XEXP (call, 0)));
+      reg = XEXP (XEXP (call, 0), 0);
+      if (!REG_P (reg))
+       continue;
+
+      symbol = mips_find_pic_call_symbol (insn, reg);
+      if (symbol)
+       {
+         mips_annotate_pic_call_expr (call, symbol);
+         if (second_call)
+           mips_annotate_pic_call_expr (second_call, symbol);
+       }
+    }
 }
 \f
 /* A temporary variable used by for_each_rtx callbacks, etc.  */
@@ -13657,7 +14927,7 @@ mips_reorg_process_insns (void)
   /* Make a first pass over the instructions, recording all the LO_SUMs.  */
   for (insn = get_insns (); insn != 0; insn = NEXT_INSN (insn))
     FOR_EACH_SUBINSN (subinsn, insn)
-      if (INSN_P (subinsn))
+      if (USEFUL_INSN_P (subinsn))
        for_each_rtx (&PATTERN (subinsn), mips_record_lo_sum, htab);
 
   last_insn = 0;
@@ -13671,7 +14941,7 @@ mips_reorg_process_insns (void)
   for (insn = get_insns (); insn != 0; insn = next_insn)
     {
       next_insn = NEXT_INSN (insn);
-      if (INSN_P (insn))
+      if (USEFUL_INSN_P (insn))
        {
          if (GET_CODE (PATTERN (insn)) == SEQUENCE)
            {
@@ -13717,14 +14987,82 @@ mips_reorg_process_insns (void)
   htab_delete (htab);
 }
 
+/* If we are using a GOT, but have not decided to use a global pointer yet,
+   see whether we need one to implement long branches.  Convert the ghost
+   global-pointer instructions into real ones if so.  */
+
+static bool
+mips_expand_ghost_gp_insns (void)
+{
+  rtx insn;
+  int normal_length;
+
+  /* Quick exit if we already know that we will or won't need a
+     global pointer.  */
+  if (!TARGET_USE_GOT
+      || cfun->machine->global_pointer == INVALID_REGNUM
+      || mips_must_initialize_gp_p ())
+    return false;
+
+  shorten_branches (get_insns ());
+
+  /* Look for a branch that is longer than normal.  The normal length for
+     non-MIPS16 branches is 8, because the length includes the delay slot.
+     It is 4 for MIPS16, because MIPS16 branches are extended instructions,
+     but they have no delay slot.  */
+  normal_length = (TARGET_MIPS16 ? 4 : 8);
+  for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
+    if (JUMP_P (insn)
+       && USEFUL_INSN_P (insn)
+       && get_attr_length (insn) > normal_length)
+      break;
+
+  if (insn == NULL_RTX)
+    return false;
+
+  /* We've now established that we need $gp.  */
+  cfun->machine->must_initialize_gp_p = true;
+  split_all_insns_noflow ();
+
+  return true;
+}
+
+/* Subroutine of mips_reorg to manage passes that require DF.  */
+
+static void
+mips_df_reorg (void)
+{
+  /* Create def-use chains.  */
+  df_set_flags (DF_EQ_NOTES);
+  df_chain_add_problem (DF_UD_CHAIN);
+  df_analyze ();
+
+  if (TARGET_RELAX_PIC_CALLS)
+    mips_annotate_pic_calls ();
+
+  if (mips_r10k_cache_barrier != R10K_CACHE_BARRIER_NONE)
+    r10k_insert_cache_barriers ();
+
+  df_finish_pass (false);
+}
+
 /* Implement TARGET_MACHINE_DEPENDENT_REORG.  */
 
 static void
 mips_reorg (void)
 {
+  /* Restore the BLOCK_FOR_INSN pointers, which are needed by DF.  Also during
+     insn splitting in mips16_lay_out_constants, DF insn info is only kept up
+     to date if the CFG is available.  */
+  if (mips_cfg_in_reorg ())
+    compute_bb_for_insn ();
   mips16_lay_out_constants ();
-  if (mips_r10k_cache_barrier != R10K_CACHE_BARRIER_NONE)
-    r10k_insert_cache_barriers ();
+  if (mips_cfg_in_reorg ())
+    {
+      mips_df_reorg ();
+      free_bb_for_insn ();
+    }
+
   if (optimize > 0 && flag_delayed_branch)
     dbr_schedule (get_insns ());
   mips_reorg_process_insns ();
@@ -13733,6 +15071,10 @@ mips_reorg (void)
       && TUNE_MIPS4130
       && TARGET_VR4130_ALIGN)
     vr4130_align_insns ();
+  if (mips_expand_ghost_gp_insns ())
+    /* The expansion could invalidate some of the VR4130 alignment
+       optimizations, but this should be an extremely rare case anyhow.  */
+    mips_reorg_process_insns ();
 }
 \f
 /* Implement TARGET_ASM_OUTPUT_MI_THUNK.  Generate rtl rather than asm text
@@ -13766,6 +15108,7 @@ mips_output_mi_thunk (FILE *file, tree thunk_fndecl ATTRIBUTE_UNUSED,
         TARGET_CALL_SAVED_GP.  */
       cfun->machine->global_pointer
        = TARGET_CALL_SAVED_GP ? 15 : GLOBAL_POINTER_REGNUM;
+      cfun->machine->must_initialize_gp_p = true;
       SET_REGNO (pic_offset_table_rtx, cfun->machine->global_pointer);
 
       /* Set up the global pointer for n32 or n64 abicalls.  */
@@ -13857,7 +15200,6 @@ mips_output_mi_thunk (FILE *file, tree thunk_fndecl ATTRIBUTE_UNUSED,
   final_start_function (insn, file, 1);
   final (insn, file, 1);
   final_end_function ();
-  free_after_compilation (cfun);
 
   /* Clean up the vars set above.  Note that final_end_function resets
      the global pointer for us.  */
@@ -13923,6 +15265,11 @@ mips_set_mips16_mode (int mips16_p)
       targetm.min_anchor_offset = 0;
       targetm.max_anchor_offset = 127;
 
+      targetm.const_anchor = 0;
+
+      /* MIPS16 has no BAL instruction.  */
+      target_flags &= ~MASK_RELAX_PIC_CALLS;
+
       if (flag_pic && !TARGET_OLDABI)
        sorry ("MIPS16 PIC for ABIs other than o32 and o64");
 
@@ -13950,14 +15297,22 @@ mips_set_mips16_mode (int mips16_p)
 
       targetm.min_anchor_offset = -32768;
       targetm.max_anchor_offset = 32767;
+
+      targetm.const_anchor = 0x8000;
     }
 
   /* (Re)initialize MIPS target internals for new ISA.  */
   mips_init_relocs ();
 
-  if (was_mips16_p >= 0 || was_mips16_pch_p >= 0)
-    /* Reinitialize target-dependent state.  */
-    target_reinit ();
+  if (mips16_p)
+    {
+      if (!mips16_globals)
+       mips16_globals = save_target_globals ();
+      else
+       restore_target_globals (mips16_globals);
+    }
+  else
+    restore_target_globals (&default_target_globals);
 
   was_mips16_p = mips16_p;
   was_mips16_pch_p = mips16_p;
@@ -13977,8 +15332,7 @@ mips_set_current_function (tree fndecl)
 static struct machine_function *
 mips_init_machine_status (void)
 {
-  return ((struct machine_function *)
-         ggc_alloc_cleared (sizeof (struct machine_function)));
+  return ggc_alloc_cleared_machine_function ();
 }
 
 /* Return the processor associated with the given ISA level, or null
@@ -14170,10 +15524,10 @@ mips_handle_option (size_t code, const char *arg, int value ATTRIBUTE_UNUSED)
     }
 }
 
-/* Implement OVERRIDE_OPTIONS.  */
+/* Implement TARGET_OPTION_OVERRIDE.  */
 
-void
-mips_override_options (void)
+static void
+mips_option_override (void)
 {
   int i, start, regno, mode;
 
@@ -14185,8 +15539,18 @@ mips_override_options (void)
   SUBTARGET_OVERRIDE_OPTIONS;
 #endif
 
+  /* -mno-float overrides -mhard-float and -msoft-float.  */
+  if (TARGET_NO_FLOAT)
+    {
+      target_flags |= MASK_SOFT_FLOAT_ABI;
+      target_flags_explicit |= MASK_SOFT_FLOAT_ABI;
+    }
+
+  if (TARGET_FLIP_MIPS16)
+    TARGET_INTERLINK_MIPS16 = 1;
+
   /* Set the small data limit.  */
-  mips_small_data_threshold = (g_switch_set
+  mips_small_data_threshold = (global_options_set.x_g_switch_value
                               ? g_switch_value
                               : MIPS_DEFAULT_GVALUE);
 
@@ -14433,6 +15797,11 @@ mips_override_options (void)
   if (mips_abi == ABI_EABI && TARGET_64BIT)
     flag_dwarf2_cfi_asm = 0;
 
+  /* .cfi_* directives generate a read-only section, so fall back on
+     manual .eh_frame creation if we need the section to be writable.  */
+  if (TARGET_WRITABLE_EH_FRAME)
+    flag_dwarf2_cfi_asm = 0;
+
   mips_init_print_operand_punct ();
 
   /* Set up array to map GCC register number to debug register number.
@@ -14505,6 +15874,17 @@ mips_override_options (void)
           : !TARGET_BRANCHLIKELY))
     sorry ("%qs requires branch-likely instructions", "-mfix-r10000");
 
+  if (TARGET_SYNCI && !ISA_HAS_SYNCI)
+    {
+      warning (0, "the %qs architecture does not support the synci "
+              "instruction", mips_arch_info->name);
+      target_flags &= ~MASK_SYNCI;
+    }
+
+  /* Only optimize PIC indirect calls if they are actually required.  */
+  if (!TARGET_USE_GOT || !TARGET_EXPLICIT_RELOCS)
+    target_flags &= ~MASK_RELAX_PIC_CALLS;
+
   /* Save base state of options.  */
   mips_base_target_flags = target_flags;
   mips_base_schedule_insns = flag_schedule_insns;
@@ -14521,6 +15901,13 @@ mips_override_options (void)
   mips_set_mips16_mode (false);
 }
 
+/* Implement TARGET_OPTION_OPTIMIZATION_TABLE.  */
+static const struct default_options mips_option_optimization_table[] =
+  {
+    { OPT_LEVELS_1_PLUS, OPT_fomit_frame_pointer, NULL, 1 },
+    { OPT_LEVELS_NONE, 0, NULL, 0 }
+  };
+
 /* Swap the register information for registers I and I + 1, which
    currently have the wrong endianness.  Note that the registers'
    fixedness and call-clobberedness might have been set on the
@@ -14671,6 +16058,31 @@ mips_order_regs_for_local_alloc (void)
     }
 }
 
+/* Implement EH_USES.  */
+
+bool
+mips_eh_uses (unsigned int regno)
+{
+  if (reload_completed && !TARGET_ABSOLUTE_JUMPS)
+    {
+      /* We need to force certain registers to be live in order to handle
+        PIC long branches correctly.  See mips_must_initialize_gp_p for
+        details.  */
+      if (mips_cfun_has_cprestore_slot_p ())
+       {
+         if (regno == CPRESTORE_SLOT_REGNUM)
+           return true;
+       }
+      else
+       {
+         if (cfun->machine->global_pointer == regno)
+           return true;
+       }
+    }
+
+  return false;
+}
+
 /* Implement EPILOGUE_USES.  */
 
 bool
@@ -14679,7 +16091,7 @@ mips_epilogue_uses (unsigned int regno)
   /* Say that the epilogue uses the return address register.  Note that
      in the case of sibcalls, the values "used by the epilogue" are
      considered live at the start of the called function.  */
-  if (regno == 31)
+  if (regno == RETURN_ADDR_REGNUM)
     return true;
 
   /* If using a GOT, say that the epilogue also uses GOT_VERSION_REGNUM.
@@ -14701,40 +16113,302 @@ mips_epilogue_uses (unsigned int regno)
 static int
 mips_at_reg_p (rtx *x, void *data ATTRIBUTE_UNUSED)
 {
-  return GET_CODE (*x) == REG && REGNO (*x) == AT_REGNUM;
+  return REG_P (*x) && REGNO (*x) == AT_REGNUM;
 }
 
+/* Return true if INSN needs to be wrapped in ".set noat".
+   INSN has NOPERANDS operands, stored in OPVEC.  */
 
-/* Implement FINAL_PRESCAN_INSN.  */
-
-void
-mips_final_prescan_insn (rtx insn, rtx *opvec, int noperands)
+static bool
+mips_need_noat_wrapper_p (rtx insn, rtx *opvec, int noperands)
 {
   int i;
 
-  /* We need to emit ".set noat" before an instruction that accesses
-     $1 (AT).  */
   if (recog_memoized (insn) >= 0)
     for (i = 0; i < noperands; i++)
       if (for_each_rtx (&opvec[i], mips_at_reg_p, NULL))
-       if (set_noat++ == 0)
-         fprintf (asm_out_file, "\t.set\tnoat\n");
+       return true;
+  return false;
+}
+
+/* Implement FINAL_PRESCAN_INSN.  */
+
+void
+mips_final_prescan_insn (rtx insn, rtx *opvec, int noperands)
+{
+  if (mips_need_noat_wrapper_p (insn, opvec, noperands))
+    mips_push_asm_switch (&mips_noat);
 }
 
 /* Implement TARGET_ASM_FINAL_POSTSCAN_INSN.  */
 
 static void
-mips_final_postscan_insn (FILE *file, rtx insn, rtx *opvec, int noperands)
+mips_final_postscan_insn (FILE *file ATTRIBUTE_UNUSED, rtx insn,
+                         rtx *opvec, int noperands)
 {
-  int i;
+  if (mips_need_noat_wrapper_p (insn, opvec, noperands))
+    mips_pop_asm_switch (&mips_noat);
+}
 
-  /* Close any ".set noat" block opened by mips_final_prescan_insn.  */
-  if (recog_memoized (insn) >= 0)
-    for (i = 0; i < noperands; i++)
-      if (for_each_rtx (&opvec[i], mips_at_reg_p, NULL))
-       if (--set_noat == 0)
-         fprintf (file, "\t.set\tat\n");
+/* Return the function that is used to expand the <u>mulsidi3 pattern.
+   EXT_CODE is the code of the extension used.  Return NULL if widening
+   multiplication shouldn't be used.  */
+
+mulsidi3_gen_fn
+mips_mulsidi3_gen_fn (enum rtx_code ext_code)
+{
+  bool signed_p;
+
+  signed_p = ext_code == SIGN_EXTEND;
+  if (TARGET_64BIT)
+    {
+      /* Don't use widening multiplication with MULT when we have DMUL.  Even
+        with the extension of its input operands DMUL is faster.  Note that
+        the extension is not needed for signed multiplication.  In order to
+        ensure that we always remove the redundant sign-extension in this
+        case we still expand mulsidi3 for DMUL.  */
+      if (ISA_HAS_DMUL3)
+       return signed_p ? gen_mulsidi3_64bit_dmul : NULL;
+      if (TARGET_FIX_R4000)
+       return NULL;
+      return signed_p ? gen_mulsidi3_64bit : gen_umulsidi3_64bit;
+    }
+  else
+    {
+      if (TARGET_FIX_R4000 && !ISA_HAS_DSP)
+       return signed_p ? gen_mulsidi3_32bit_r4000 : gen_umulsidi3_32bit_r4000;
+      return signed_p ? gen_mulsidi3_32bit : gen_umulsidi3_32bit;
+    }
+}
+\f
+/* Return the size in bytes of the trampoline code, padded to
+   TRAMPOLINE_ALIGNMENT bits.  The static chain pointer and target
+   function address immediately follow.  */
+
+int
+mips_trampoline_code_size (void)
+{
+  if (TARGET_USE_PIC_FN_ADDR_REG)
+    return 4 * 4;
+  else if (ptr_mode == DImode)
+    return 8 * 4;
+  else if (ISA_HAS_LOAD_DELAY)
+    return 6 * 4;
+  else
+    return 4 * 4;
+}
+
+/* Implement TARGET_TRAMPOLINE_INIT.  */
+
+static void
+mips_trampoline_init (rtx m_tramp, tree fndecl, rtx chain_value)
+{
+  rtx addr, end_addr, high, low, opcode, mem;
+  rtx trampoline[8];
+  unsigned int i, j;
+  HOST_WIDE_INT end_addr_offset, static_chain_offset, target_function_offset;
+
+  /* Work out the offsets of the pointers from the start of the
+     trampoline code.  */
+  end_addr_offset = mips_trampoline_code_size ();
+  static_chain_offset = end_addr_offset;
+  target_function_offset = static_chain_offset + GET_MODE_SIZE (ptr_mode);
+
+  /* Get pointers to the beginning and end of the code block.  */
+  addr = force_reg (Pmode, XEXP (m_tramp, 0));
+  end_addr = mips_force_binary (Pmode, PLUS, addr, GEN_INT (end_addr_offset));
+
+#define OP(X) gen_int_mode (X, SImode)
+
+  /* Build up the code in TRAMPOLINE.  */
+  i = 0;
+  if (TARGET_USE_PIC_FN_ADDR_REG)
+    {
+      /* $25 contains the address of the trampoline.  Emit code of the form:
+
+            l[wd]    $1, target_function_offset($25)
+            l[wd]    $static_chain, static_chain_offset($25)
+            jr       $1
+            move     $25,$1.  */
+      trampoline[i++] = OP (MIPS_LOAD_PTR (AT_REGNUM,
+                                          target_function_offset,
+                                          PIC_FUNCTION_ADDR_REGNUM));
+      trampoline[i++] = OP (MIPS_LOAD_PTR (STATIC_CHAIN_REGNUM,
+                                          static_chain_offset,
+                                          PIC_FUNCTION_ADDR_REGNUM));
+      trampoline[i++] = OP (MIPS_JR (AT_REGNUM));
+      trampoline[i++] = OP (MIPS_MOVE (PIC_FUNCTION_ADDR_REGNUM, AT_REGNUM));
+    }
+  else if (ptr_mode == DImode)
+    {
+      /* It's too cumbersome to create the full 64-bit address, so let's
+        instead use:
+
+            move    $1, $31
+            bal     1f
+            nop
+        1:  l[wd]   $25, target_function_offset - 12($31)
+            l[wd]   $static_chain, static_chain_offset - 12($31)
+            jr      $25
+            move    $31, $1
+
+       where 12 is the offset of "1:" from the start of the code block.  */
+      trampoline[i++] = OP (MIPS_MOVE (AT_REGNUM, RETURN_ADDR_REGNUM));
+      trampoline[i++] = OP (MIPS_BAL (1));
+      trampoline[i++] = OP (MIPS_NOP);
+      trampoline[i++] = OP (MIPS_LOAD_PTR (PIC_FUNCTION_ADDR_REGNUM,
+                                          target_function_offset - 12,
+                                          RETURN_ADDR_REGNUM));
+      trampoline[i++] = OP (MIPS_LOAD_PTR (STATIC_CHAIN_REGNUM,
+                                          static_chain_offset - 12,
+                                          RETURN_ADDR_REGNUM));
+      trampoline[i++] = OP (MIPS_JR (PIC_FUNCTION_ADDR_REGNUM));
+      trampoline[i++] = OP (MIPS_MOVE (RETURN_ADDR_REGNUM, AT_REGNUM));
+    }
+  else
+    {
+      /* If the target has load delays, emit:
+
+            lui     $1, %hi(end_addr)
+            lw      $25, %lo(end_addr + ...)($1)
+            lw      $static_chain, %lo(end_addr + ...)($1)
+            jr      $25
+            nop
+
+        Otherwise emit:
+
+            lui     $1, %hi(end_addr)
+            lw      $25, %lo(end_addr + ...)($1)
+            jr      $25
+            lw      $static_chain, %lo(end_addr + ...)($1).  */
+
+      /* Split END_ADDR into %hi and %lo values.  Trampolines are aligned
+        to 64 bits, so the %lo value will have the bottom 3 bits clear.  */
+      high = expand_simple_binop (SImode, PLUS, end_addr, GEN_INT (0x8000),
+                                 NULL, false, OPTAB_WIDEN);
+      high = expand_simple_binop (SImode, LSHIFTRT, high, GEN_INT (16),
+                                 NULL, false, OPTAB_WIDEN);
+      low = convert_to_mode (SImode, gen_lowpart (HImode, end_addr), true);
+
+      /* Emit the LUI.  */
+      opcode = OP (MIPS_LUI (AT_REGNUM, 0));
+      trampoline[i++] = expand_simple_binop (SImode, IOR, opcode, high,
+                                            NULL, false, OPTAB_WIDEN);
+
+      /* Emit the load of the target function.  */
+      opcode = OP (MIPS_LOAD_PTR (PIC_FUNCTION_ADDR_REGNUM,
+                                 target_function_offset - end_addr_offset,
+                                 AT_REGNUM));
+      trampoline[i++] = expand_simple_binop (SImode, IOR, opcode, low,
+                                            NULL, false, OPTAB_WIDEN);
+
+      /* Emit the JR here, if we can.  */
+      if (!ISA_HAS_LOAD_DELAY)
+       trampoline[i++] = OP (MIPS_JR (PIC_FUNCTION_ADDR_REGNUM));
+
+      /* Emit the load of the static chain register.  */
+      opcode = OP (MIPS_LOAD_PTR (STATIC_CHAIN_REGNUM,
+                                 static_chain_offset - end_addr_offset,
+                                 AT_REGNUM));
+      trampoline[i++] = expand_simple_binop (SImode, IOR, opcode, low,
+                                            NULL, false, OPTAB_WIDEN);
+
+      /* Emit the JR, if we couldn't above.  */
+      if (ISA_HAS_LOAD_DELAY)
+       {
+         trampoline[i++] = OP (MIPS_JR (PIC_FUNCTION_ADDR_REGNUM));
+         trampoline[i++] = OP (MIPS_NOP);
+       }
+    }
+
+#undef OP
+
+  /* Copy the trampoline code.  Leave any padding uninitialized.  */
+  for (j = 0; j < i; j++)
+    {
+      mem = adjust_address (m_tramp, SImode, j * GET_MODE_SIZE (SImode));
+      mips_emit_move (mem, trampoline[j]);
+    }
+
+  /* Set up the static chain pointer field.  */
+  mem = adjust_address (m_tramp, ptr_mode, static_chain_offset);
+  mips_emit_move (mem, chain_value);
+
+  /* Set up the target function field.  */
+  mem = adjust_address (m_tramp, ptr_mode, target_function_offset);
+  mips_emit_move (mem, XEXP (DECL_RTL (fndecl), 0));
+
+  /* Flush the code part of the trampoline.  */
+  emit_insn (gen_add3_insn (end_addr, addr, GEN_INT (TRAMPOLINE_SIZE)));
+  emit_insn (gen_clear_cache (addr, end_addr));
+}
+
+/* Implement FUNCTION_PROFILER.  */
+
+void mips_function_profiler (FILE *file)
+{
+  if (TARGET_MIPS16)
+    sorry ("mips16 function profiling");
+  if (TARGET_LONG_CALLS)
+    {
+      /* For TARGET_LONG_CALLS use $3 for the address of _mcount.  */
+      if (Pmode == DImode)
+       fprintf (file, "\tdla\t%s,_mcount\n", reg_names[3]);
+      else
+       fprintf (file, "\tla\t%s,_mcount\n", reg_names[3]);
+    }
+  mips_push_asm_switch (&mips_noat);
+  fprintf (file, "\tmove\t%s,%s\t\t# save current return address\n",
+          reg_names[AT_REGNUM], reg_names[RETURN_ADDR_REGNUM]);
+  /* _mcount treats $2 as the static chain register.  */
+  if (cfun->static_chain_decl != NULL)
+    fprintf (file, "\tmove\t%s,%s\n", reg_names[2],
+            reg_names[STATIC_CHAIN_REGNUM]);
+  if (TARGET_MCOUNT_RA_ADDRESS)
+    {
+      /* If TARGET_MCOUNT_RA_ADDRESS load $12 with the address of the
+        ra save location.  */
+      if (cfun->machine->frame.ra_fp_offset == 0)
+       /* ra not saved, pass zero.  */
+       fprintf (file, "\tmove\t%s,%s\n", reg_names[12], reg_names[0]);
+      else
+       fprintf (file, "\t%s\t%s," HOST_WIDE_INT_PRINT_DEC "(%s)\n",
+                Pmode == DImode ? "dla" : "la", reg_names[12],
+                cfun->machine->frame.ra_fp_offset,
+                reg_names[STACK_POINTER_REGNUM]);
+    }
+  if (!TARGET_NEWABI)
+    fprintf (file,
+            "\t%s\t%s,%s,%d\t\t# _mcount pops 2 words from  stack\n",
+            TARGET_64BIT ? "dsubu" : "subu",
+            reg_names[STACK_POINTER_REGNUM],
+            reg_names[STACK_POINTER_REGNUM],
+            Pmode == DImode ? 16 : 8);
+
+  if (TARGET_LONG_CALLS)
+    fprintf (file, "\tjalr\t%s\n", reg_names[3]);
+  else
+    fprintf (file, "\tjal\t_mcount\n");
+  mips_pop_asm_switch (&mips_noat);
+  /* _mcount treats $2 as the static chain register.  */
+  if (cfun->static_chain_decl != NULL)
+    fprintf (file, "\tmove\t%s,%s\n", reg_names[STATIC_CHAIN_REGNUM],
+            reg_names[2]);
+}
+
+/* Implement TARGET_SHIFT_TRUNCATION_MASK.  We want to keep the default
+   behaviour of TARGET_SHIFT_TRUNCATION_MASK for non-vector modes even
+   when TARGET_LOONGSON_2EF is true.  */
+
+static unsigned HOST_WIDE_INT
+mips_shift_truncation_mask (enum machine_mode mode)
+{
+  if (TARGET_LOONGSON_2EF && VECTOR_MODE_P (mode))
+    return 0;
+
+  return GET_MODE_BITSIZE (mode) - 1;
 }
+
 \f
 /* Initialize the GCC target structure.  */
 #undef TARGET_ASM_ALIGNED_HI_OP
@@ -14744,6 +16418,14 @@ mips_final_postscan_insn (FILE *file, rtx insn, rtx *opvec, int noperands)
 #undef TARGET_ASM_ALIGNED_DI_OP
 #define TARGET_ASM_ALIGNED_DI_OP "\t.dword\t"
 
+#undef TARGET_OPTION_OVERRIDE
+#define TARGET_OPTION_OVERRIDE mips_option_override
+#undef TARGET_OPTION_OPTIMIZATION_TABLE
+#define TARGET_OPTION_OPTIMIZATION_TABLE mips_option_optimization_table
+
+#undef TARGET_LEGITIMIZE_ADDRESS
+#define TARGET_LEGITIMIZE_ADDRESS mips_legitimize_address
+
 #undef TARGET_ASM_FUNCTION_PROLOGUE
 #define TARGET_ASM_FUNCTION_PROLOGUE mips_output_function_prologue
 #undef TARGET_ASM_FUNCTION_EPILOGUE
@@ -14758,7 +16440,7 @@ mips_final_postscan_insn (FILE *file, rtx insn, rtx *opvec, int noperands)
 #undef TARGET_SCHED_REORDER
 #define TARGET_SCHED_REORDER mips_sched_reorder
 #undef TARGET_SCHED_REORDER2
-#define TARGET_SCHED_REORDER2 mips_sched_reorder
+#define TARGET_SCHED_REORDER2 mips_sched_reorder2
 #undef TARGET_SCHED_VARIABLE_ISSUE
 #define TARGET_SCHED_VARIABLE_ISSUE mips_variable_issue
 #undef TARGET_SCHED_ADJUST_COST
@@ -14772,6 +16454,9 @@ mips_final_postscan_insn (FILE *file, rtx insn, rtx *opvec, int noperands)
 #undef TARGET_SCHED_FIRST_CYCLE_MULTIPASS_DFA_LOOKAHEAD
 #define TARGET_SCHED_FIRST_CYCLE_MULTIPASS_DFA_LOOKAHEAD \
   mips_multipass_dfa_lookahead
+#undef TARGET_SMALL_REGISTER_CLASSES_FOR_MODE_P
+#define TARGET_SMALL_REGISTER_CLASSES_FOR_MODE_P \
+  mips_small_register_classes_for_mode_p
 
 #undef TARGET_DEFAULT_TARGET_FLAGS
 #define TARGET_DEFAULT_TARGET_FLAGS            \
@@ -14796,6 +16481,10 @@ mips_final_postscan_insn (FILE *file, rtx insn, rtx *opvec, int noperands)
 
 #undef TARGET_VALID_POINTER_MODE
 #define TARGET_VALID_POINTER_MODE mips_valid_pointer_mode
+#undef TARGET_REGISTER_MOVE_COST
+#define TARGET_REGISTER_MOVE_COST mips_register_move_cost
+#undef TARGET_MEMORY_MOVE_COST
+#define TARGET_MEMORY_MOVE_COST mips_memory_move_cost
 #undef TARGET_RTX_COSTS
 #define TARGET_RTX_COSTS mips_rtx_costs
 #undef TARGET_ADDRESS_COST
@@ -14822,10 +16511,8 @@ mips_final_postscan_insn (FILE *file, rtx insn, rtx *opvec, int noperands)
 #undef TARGET_GIMPLIFY_VA_ARG_EXPR
 #define TARGET_GIMPLIFY_VA_ARG_EXPR mips_gimplify_va_arg_expr
 
-#undef TARGET_PROMOTE_FUNCTION_ARGS
-#define TARGET_PROMOTE_FUNCTION_ARGS hook_bool_const_tree_true
-#undef TARGET_PROMOTE_FUNCTION_RETURN
-#define TARGET_PROMOTE_FUNCTION_RETURN hook_bool_const_tree_true
+#undef  TARGET_PROMOTE_FUNCTION_MODE
+#define TARGET_PROMOTE_FUNCTION_MODE default_promote_function_mode_always_promote
 #undef TARGET_PROMOTE_PROTOTYPES
 #define TARGET_PROMOTE_PROTOTYPES hook_bool_const_tree_true
 
@@ -14839,6 +16526,13 @@ mips_final_postscan_insn (FILE *file, rtx insn, rtx *opvec, int noperands)
 #undef TARGET_ASM_CAN_OUTPUT_MI_THUNK
 #define TARGET_ASM_CAN_OUTPUT_MI_THUNK hook_bool_const_tree_hwi_hwi_const_tree_true
 
+#undef TARGET_PRINT_OPERAND
+#define TARGET_PRINT_OPERAND mips_print_operand
+#undef TARGET_PRINT_OPERAND_ADDRESS
+#define TARGET_PRINT_OPERAND_ADDRESS mips_print_operand_address
+#undef TARGET_PRINT_OPERAND_PUNCT_VALID_P
+#define TARGET_PRINT_OPERAND_PUNCT_VALID_P mips_print_operand_punct_valid_p
+
 #undef TARGET_SETUP_INCOMING_VARARGS
 #define TARGET_SETUP_INCOMING_VARARGS mips_setup_incoming_varargs
 #undef TARGET_STRICT_ARGUMENT_NAMING
@@ -14851,6 +16545,10 @@ mips_final_postscan_insn (FILE *file, rtx insn, rtx *opvec, int noperands)
 #define TARGET_CALLEE_COPIES mips_callee_copies
 #undef TARGET_ARG_PARTIAL_BYTES
 #define TARGET_ARG_PARTIAL_BYTES mips_arg_partial_bytes
+#undef TARGET_FUNCTION_ARG
+#define TARGET_FUNCTION_ARG mips_function_arg
+#undef TARGET_FUNCTION_ARG_ADVANCE
+#define TARGET_FUNCTION_ARG_ADVANCE mips_function_arg_advance
 
 #undef TARGET_MODE_REP_EXTENDED
 #define TARGET_MODE_REP_EXTENDED mips_mode_rep_extended
@@ -14861,8 +16559,13 @@ mips_final_postscan_insn (FILE *file, rtx insn, rtx *opvec, int noperands)
 #undef TARGET_SCALAR_MODE_SUPPORTED_P
 #define TARGET_SCALAR_MODE_SUPPORTED_P mips_scalar_mode_supported_p
 
+#undef TARGET_VECTORIZE_PREFERRED_SIMD_MODE
+#define TARGET_VECTORIZE_PREFERRED_SIMD_MODE mips_preferred_simd_mode
+
 #undef TARGET_INIT_BUILTINS
 #define TARGET_INIT_BUILTINS mips_init_builtins
+#undef TARGET_BUILTIN_DECL
+#define TARGET_BUILTIN_DECL mips_builtin_decl
 #undef TARGET_EXPAND_BUILTIN
 #define TARGET_EXPAND_BUILTIN mips_expand_builtin
 
@@ -14906,6 +16609,24 @@ mips_final_postscan_insn (FILE *file, rtx insn, rtx *opvec, int noperands)
 #undef TARGET_ASM_FINAL_POSTSCAN_INSN
 #define TARGET_ASM_FINAL_POSTSCAN_INSN mips_final_postscan_insn
 
+#undef TARGET_LEGITIMATE_ADDRESS_P
+#define TARGET_LEGITIMATE_ADDRESS_P    mips_legitimate_address_p
+
+#undef TARGET_FRAME_POINTER_REQUIRED
+#define TARGET_FRAME_POINTER_REQUIRED mips_frame_pointer_required
+
+#undef TARGET_CAN_ELIMINATE
+#define TARGET_CAN_ELIMINATE mips_can_eliminate
+
+#undef TARGET_TRAMPOLINE_INIT
+#define TARGET_TRAMPOLINE_INIT mips_trampoline_init
+
+#undef TARGET_ASM_OUTPUT_SOURCE_FILENAME
+#define TARGET_ASM_OUTPUT_SOURCE_FILENAME mips_output_filename
+
+#undef TARGET_SHIFT_TRUNCATION_MASK
+#define TARGET_SHIFT_TRUNCATION_MASK mips_shift_truncation_mask
+
 struct gcc_target targetm = TARGET_INITIALIZER;
 \f
 #include "gt-mips.h"