OSDN Git Service

gcc/
[pf3gnuchains/gcc-fork.git] / gcc / config / mips / mips.c
index 0e69827..889997d 100644 (file)
@@ -426,6 +426,8 @@ static void mips_encode_section_info (tree, rtx, int);
 static void mips_extra_live_on_entry (bitmap);
 static int mips_comp_type_attributes (const_tree, const_tree);
 static void mips_set_mips16_mode (int);
+static void mips_insert_attributes (tree, tree *);
+static tree mips_merge_decl_attributes (tree, tree);
 static void mips_set_current_function (tree);
 static int mips_mode_rep_extended (enum machine_mode, enum machine_mode);
 static bool mips_offset_within_alignment_p (rtx, HOST_WIDE_INT);
@@ -731,9 +733,13 @@ const struct attribute_spec mips_attribute_table[] =
   { "long_call",   0, 0, false, true,  true,  NULL },
   { "far",                0, 0, false, true,  true,  NULL },
   { "near",        0, 0, false, true,  true,  NULL },
-  /* Switch MIPS16 ASE on and off per-function.  */
-  { "mips16",     0, 0, false, true,  true,  NULL },
-  { "nomips16",    0, 0, false, true,  true,  NULL },
+  /* Switch MIPS16 ASE on and off per-function.  We would really like
+     to make these type attributes, but GCC doesn't provide the hooks
+     we need to support the right conversion rules.  As declaration
+     attributes, they affect code generation but don't carry other
+     semantics.  */
+  { "mips16",     0, 0, true,  false, false, NULL },
+  { "nomips16",    0, 0, true,  false, false, NULL },
   { NULL,         0, 0, false, false, false, NULL }
 };
 \f
@@ -747,94 +753,96 @@ const struct attribute_spec mips_attribute_table[] =
    options correctly.  */
 const struct mips_cpu_info mips_cpu_info_table[] = {
   /* Entries for generic ISAs */
-  { "mips1", PROCESSOR_R3000, 1 },
-  { "mips2", PROCESSOR_R6000, 2 },
-  { "mips3", PROCESSOR_R4000, 3 },
-  { "mips4", PROCESSOR_R8000, 4 },
-  { "mips32", PROCESSOR_4KC, 32 },
-  { "mips32r2", PROCESSOR_M4K, 33 },
-  { "mips64", PROCESSOR_5KC, 64 },
+  { "mips1", PROCESSOR_R3000, 1, 0 },
+  { "mips2", PROCESSOR_R6000, 2, 0 },
+  { "mips3", PROCESSOR_R4000, 3, 0 },
+  { "mips4", PROCESSOR_R8000, 4, 0 },
+  /* Prefer not to use branch-likely instructions for generic MIPS32rX
+     and MIPS64rX code.  The instructions were officially deprecated
+     in revisions 2 and earlier, but revision 3 is likely to downgrade
+     that to a recommendation to avoid the instructions in code that
+     isn't tuned to a specific processor.  */
+  { "mips32", PROCESSOR_4KC, 32, PTF_AVOID_BRANCHLIKELY },
+  { "mips32r2", PROCESSOR_M4K, 33, PTF_AVOID_BRANCHLIKELY },
+  { "mips64", PROCESSOR_5KC, 64, PTF_AVOID_BRANCHLIKELY },
 
   /* MIPS I */
-  { "r3000", PROCESSOR_R3000, 1 },
-  { "r2000", PROCESSOR_R3000, 1 }, /* = r3000 */
-  { "r3900", PROCESSOR_R3900, 1 },
+  { "r3000", PROCESSOR_R3000, 1, 0 },
+  { "r2000", PROCESSOR_R3000, 1, 0 }, /* = r3000 */
+  { "r3900", PROCESSOR_R3900, 1, 0 },
 
   /* MIPS II */
-  { "r6000", PROCESSOR_R6000, 2 },
+  { "r6000", PROCESSOR_R6000, 2, 0 },
 
   /* MIPS III */
-  { "r4000", PROCESSOR_R4000, 3 },
-  { "vr4100", PROCESSOR_R4100, 3 },
-  { "vr4111", PROCESSOR_R4111, 3 },
-  { "vr4120", PROCESSOR_R4120, 3 },
-  { "vr4130", PROCESSOR_R4130, 3 },
-  { "vr4300", PROCESSOR_R4300, 3 },
-  { "r4400", PROCESSOR_R4000, 3 }, /* = r4000 */
-  { "r4600", PROCESSOR_R4600, 3 },
-  { "orion", PROCESSOR_R4600, 3 }, /* = r4600 */
-  { "r4650", PROCESSOR_R4650, 3 },
+  { "r4000", PROCESSOR_R4000, 3, 0 },
+  { "vr4100", PROCESSOR_R4100, 3, 0 },
+  { "vr4111", PROCESSOR_R4111, 3, 0 },
+  { "vr4120", PROCESSOR_R4120, 3, 0 },
+  { "vr4130", PROCESSOR_R4130, 3, 0 },
+  { "vr4300", PROCESSOR_R4300, 3, 0 },
+  { "r4400", PROCESSOR_R4000, 3, 0 }, /* = r4000 */
+  { "r4600", PROCESSOR_R4600, 3, 0 },
+  { "orion", PROCESSOR_R4600, 3, 0 }, /* = r4600 */
+  { "r4650", PROCESSOR_R4650, 3, 0 },
 
   /* MIPS IV */
-  { "r8000", PROCESSOR_R8000, 4 },
-  { "vr5000", PROCESSOR_R5000, 4 },
-  { "vr5400", PROCESSOR_R5400, 4 },
-  { "vr5500", PROCESSOR_R5500, 4 },
-  { "rm7000", PROCESSOR_R7000, 4 },
-  { "rm9000", PROCESSOR_R9000, 4 },
+  { "r8000", PROCESSOR_R8000, 4, 0 },
+  { "vr5000", PROCESSOR_R5000, 4, 0 },
+  { "vr5400", PROCESSOR_R5400, 4, 0 },
+  { "vr5500", PROCESSOR_R5500, 4, PTF_AVOID_BRANCHLIKELY },
+  { "rm7000", PROCESSOR_R7000, 4, 0 },
+  { "rm9000", PROCESSOR_R9000, 4, 0 },
 
   /* MIPS32 */
-  { "4kc", PROCESSOR_4KC, 32 },
-  { "4km", PROCESSOR_4KC, 32 }, /* = 4kc */
-  { "4kp", PROCESSOR_4KP, 32 },
-  { "4ksc", PROCESSOR_4KC, 32 },
+  { "4kc", PROCESSOR_4KC, 32, 0 },
+  { "4km", PROCESSOR_4KC, 32, 0 }, /* = 4kc */
+  { "4kp", PROCESSOR_4KP, 32, 0 },
+  { "4ksc", PROCESSOR_4KC, 32, 0 },
 
   /* MIPS32 Release 2 */
-  { "m4k", PROCESSOR_M4K, 33 },
-  { "4kec", PROCESSOR_4KC, 33 },
-  { "4kem", PROCESSOR_4KC, 33 },
-  { "4kep", PROCESSOR_4KP, 33 },
-  { "4ksd", PROCESSOR_4KC, 33 },
-
-  { "24kc", PROCESSOR_24KC, 33 },
-  { "24kf2_1", PROCESSOR_24KF2_1, 33 },
-  { "24kf", PROCESSOR_24KF2_1, 33 },
-  { "24kf1_1", PROCESSOR_24KF1_1, 33 },
-  { "24kfx", PROCESSOR_24KF1_1, 33 },
-  { "24kx", PROCESSOR_24KF1_1, 33 },
-
-  { "24kec", PROCESSOR_24KC, 33 }, /* 24K with DSP */
-  { "24kef2_1", PROCESSOR_24KF2_1, 33 },
-  { "24kef", PROCESSOR_24KF2_1, 33 },
-  { "24kef1_1", PROCESSOR_24KF1_1, 33 },
-  { "24kefx", PROCESSOR_24KF1_1, 33 },
-  { "24kex", PROCESSOR_24KF1_1, 33 },
-
-  { "34kc", PROCESSOR_24KC, 33 }, /* 34K with MT/DSP */
-  { "34kf2_1", PROCESSOR_24KF2_1, 33 },
-  { "34kf", PROCESSOR_24KF2_1, 33 },
-  { "34kf1_1", PROCESSOR_24KF1_1, 33 },
-  { "34kfx", PROCESSOR_24KF1_1, 33 },
-  { "34kx", PROCESSOR_24KF1_1, 33 },
-
-  { "74kc", PROCESSOR_74KC, 33 }, /* 74K with DSPr2 */
-  { "74kf2_1", PROCESSOR_74KF2_1, 33 },
-  { "74kf", PROCESSOR_74KF2_1, 33 },
-  { "74kf1_1", PROCESSOR_74KF1_1, 33 },
-  { "74kfx", PROCESSOR_74KF1_1, 33 },
-  { "74kx", PROCESSOR_74KF1_1, 33 },
-  { "74kf3_2", PROCESSOR_74KF3_2, 33 },
+  { "m4k", PROCESSOR_M4K, 33, 0 },
+  { "4kec", PROCESSOR_4KC, 33, 0 },
+  { "4kem", PROCESSOR_4KC, 33, 0 },
+  { "4kep", PROCESSOR_4KP, 33, 0 },
+  { "4ksd", PROCESSOR_4KC, 33, 0 },
+
+  { "24kc", PROCESSOR_24KC, 33, 0 },
+  { "24kf2_1", PROCESSOR_24KF2_1, 33, 0 },
+  { "24kf", PROCESSOR_24KF2_1, 33, 0 },
+  { "24kf1_1", PROCESSOR_24KF1_1, 33, 0 },
+  { "24kfx", PROCESSOR_24KF1_1, 33, 0 },
+  { "24kx", PROCESSOR_24KF1_1, 33, 0 },
+
+  { "24kec", PROCESSOR_24KC, 33, 0 }, /* 24K with DSP */
+  { "24kef2_1", PROCESSOR_24KF2_1, 33, 0 },
+  { "24kef", PROCESSOR_24KF2_1, 33, 0 },
+  { "24kef1_1", PROCESSOR_24KF1_1, 33, 0 },
+  { "24kefx", PROCESSOR_24KF1_1, 33, 0 },
+  { "24kex", PROCESSOR_24KF1_1, 33, 0 },
+
+  { "34kc", PROCESSOR_24KC, 33, 0 }, /* 34K with MT/DSP */
+  { "34kf2_1", PROCESSOR_24KF2_1, 33, 0 },
+  { "34kf", PROCESSOR_24KF2_1, 33, 0 },
+  { "34kf1_1", PROCESSOR_24KF1_1, 33, 0 },
+  { "34kfx", PROCESSOR_24KF1_1, 33, 0 },
+  { "34kx", PROCESSOR_24KF1_1, 33, 0 },
+
+  { "74kc", PROCESSOR_74KC, 33, 0 }, /* 74K with DSPr2 */
+  { "74kf2_1", PROCESSOR_74KF2_1, 33, 0 },
+  { "74kf", PROCESSOR_74KF2_1, 33, 0 },
+  { "74kf1_1", PROCESSOR_74KF1_1, 33, 0 },
+  { "74kfx", PROCESSOR_74KF1_1, 33, 0 },
+  { "74kx", PROCESSOR_74KF1_1, 33, 0 },
+  { "74kf3_2", PROCESSOR_74KF3_2, 33, 0 },
 
   /* MIPS64 */
-  { "5kc", PROCESSOR_5KC, 64 },
-  { "5kf", PROCESSOR_5KF, 64 },
-  { "20kc", PROCESSOR_20KC, 64 },
-  { "sb1", PROCESSOR_SB1, 64 },
-  { "sb1a", PROCESSOR_SB1A, 64 },
-  { "sr71000", PROCESSOR_SR71000, 64 },
-
-  /* End marker */
-  { 0, 0, 0 }
+  { "5kc", PROCESSOR_5KC, 64, 0 },
+  { "5kf", PROCESSOR_5KF, 64, 0 },
+  { "20kc", PROCESSOR_20KC, 64, PTF_AVOID_BRANCHLIKELY },
+  { "sb1", PROCESSOR_SB1, 64, PTF_AVOID_BRANCHLIKELY },
+  { "sb1a", PROCESSOR_SB1A, 64, PTF_AVOID_BRANCHLIKELY },
+  { "sr71000", PROCESSOR_SR71000, 64, PTF_AVOID_BRANCHLIKELY },
 };
 
 /* Default costs. If these are used for a processor we should look
@@ -1265,6 +1273,10 @@ static const unsigned char mips16e_save_restore_regs[] = {
 #undef TARGET_FUNCTION_OK_FOR_SIBCALL
 #define TARGET_FUNCTION_OK_FOR_SIBCALL mips_function_ok_for_sibcall
 
+#undef TARGET_INSERT_ATTRIBUTES
+#define TARGET_INSERT_ATTRIBUTES mips_insert_attributes
+#undef TARGET_MERGE_DECL_ATTRIBUTES
+#define TARGET_MERGE_DECL_ATTRIBUTES mips_merge_decl_attributes
 #undef TARGET_SET_CURRENT_FUNCTION
 #define TARGET_SET_CURRENT_FUNCTION mips_set_current_function
 
@@ -1349,6 +1361,10 @@ static const unsigned char mips16e_save_restore_regs[] = {
 
 #undef TARGET_ATTRIBUTE_TABLE
 #define TARGET_ATTRIBUTE_TABLE mips_attribute_table
+/* All our function attributes are related to how out-of-line copies should
+   be compiled or called.  They don't in themselves prevent inlining.  */
+#undef TARGET_FUNCTION_ATTRIBUTE_INLINABLE_P
+#define TARGET_FUNCTION_ATTRIBUTE_INLINABLE_P hook_bool_const_tree_true
 
 #undef TARGET_EXTRA_LIVE_ON_ENTRY
 #define TARGET_EXTRA_LIVE_ON_ENTRY mips_extra_live_on_entry
@@ -1392,15 +1408,15 @@ mips_far_type_p (const_tree type)
 /* Similar predicates for "mips16"/"nomips16" attributes.  */
 
 static bool
-mips_mips16_type_p (const_tree type)
+mips_mips16_decl_p (const_tree decl)
 {
-  return lookup_attribute ("mips16", TYPE_ATTRIBUTES (type)) != NULL;
+  return lookup_attribute ("mips16", DECL_ATTRIBUTES (decl)) != NULL;
 }
 
 static bool
-mips_nomips16_type_p (const_tree type)
+mips_nomips16_decl_p (const_tree decl)
 {
-  return lookup_attribute ("nomips16", TYPE_ATTRIBUTES (type)) != NULL;
+  return lookup_attribute ("nomips16", DECL_ATTRIBUTES (decl)) != NULL;
 }
 
 /* Return 0 if the attributes for two types are incompatible, 1 if they
@@ -1420,11 +1436,6 @@ mips_comp_type_attributes (const_tree type1, const_tree type2)
   if (mips_near_type_p (type1) && mips_far_type_p (type2))
     return 0;
 
-  /* Mips16/nomips16 attributes must match exactly.  */
-  if (mips_nomips16_type_p (type1) != mips_nomips16_type_p (type2)
-      || mips_mips16_type_p (type1) != mips_mips16_type_p (type2))
-    return 0;
-
   return 1;
 }
 \f
@@ -3466,7 +3477,7 @@ mips_address_cost (rtx addr)
 rtx
 mips_subword (rtx op, int high_p)
 {
-  unsigned int byte;
+  unsigned int byte, offset;
   enum machine_mode mode;
 
   mode = GET_MODE (op);
@@ -3479,7 +3490,11 @@ mips_subword (rtx op, int high_p)
     byte = 0;
 
   if (FP_REG_RTX_P (op))
-    return gen_rtx_REG (word_mode, high_p ? REGNO (op) + 1 : REGNO (op));
+    {
+      /* Paired FPRs are always ordered little-endian.  */
+      offset = (UNITS_PER_WORD < UNITS_PER_HWFPVALUE ? high_p : byte != 0);
+      return gen_rtx_REG (word_mode, REGNO (op) + offset);
+    }
 
   if (MEM_P (op))
     return mips_rewrite_small_data (adjust_address (op, word_mode, byte));
@@ -3513,56 +3528,23 @@ mips_split_64bit_move_p (rtx dest, rtx src)
 }
 
 
-/* Split a 64-bit move from SRC to DEST assuming that
-   mips_split_64bit_move_p holds.
-
-   Moves into and out of FPRs cause some difficulty here.  Such moves
-   will always be DFmode, since paired FPRs are not allowed to store
-   DImode values.  The most natural representation would be two separate
-   32-bit moves, such as:
-
-       (set (reg:SI $f0) (mem:SI ...))
-       (set (reg:SI $f1) (mem:SI ...))
-
-   However, the second insn is invalid because odd-numbered FPRs are
-   not allowed to store independent values.  Use the patterns load_df_low,
-   load_df_high and store_df_high instead.  */
+/* Split a doubleword move from SRC to DEST.  On 32-bit targets,
+   this function handles 64-bit moves for which mips_split_64bit_move_p
+   holds.  For 64-bit targets, this function handles 128-bit moves.  */
 
 void
-mips_split_64bit_move (rtx dest, rtx src)
+mips_split_doubleword_move (rtx dest, rtx src)
 {
-  if (FP_REG_RTX_P (dest))
-    {
-      /* Loading an FPR from memory or from GPRs.  */
-      if (ISA_HAS_MXHC1)
-       {
-         dest = gen_lowpart (DFmode, dest);
-         emit_insn (gen_load_df_low (dest, mips_subword (src, 0)));
-         emit_insn (gen_mthc1 (dest, mips_subword (src, 1),
-                               copy_rtx (dest)));
-       }
-      else
-       {
-         emit_insn (gen_load_df_low (copy_rtx (dest),
-                                     mips_subword (src, 0)));
-         emit_insn (gen_load_df_high (dest, mips_subword (src, 1),
-                                      copy_rtx (dest)));
-       }
-    }
-  else if (FP_REG_RTX_P (src))
+  if (FP_REG_RTX_P (dest) || FP_REG_RTX_P (src))
     {
-      /* Storing an FPR into memory or GPRs.  */
-      if (ISA_HAS_MXHC1)
-       {
-         src = gen_lowpart (DFmode, src);
-         mips_emit_move (mips_subword (dest, 0), mips_subword (src, 0));
-         emit_insn (gen_mfhc1 (mips_subword (dest, 1), src));
-       }
+      if (!TARGET_64BIT && GET_MODE (dest) == DImode)
+       emit_insn (gen_move_doubleword_fprdi (dest, src));
+      else if (!TARGET_64BIT && GET_MODE (dest) == DFmode)
+       emit_insn (gen_move_doubleword_fprdf (dest, src));
+      else if (TARGET_64BIT && GET_MODE (dest) == TFmode)
+       emit_insn (gen_move_doubleword_fprtf (dest, src));
       else
-       {
-         mips_emit_move (mips_subword (dest, 0), mips_subword (src, 0));
-         emit_insn (gen_store_df_high (mips_subword (dest, 1), src));
-       }
+       gcc_unreachable ();
     }
   else
     {
@@ -4117,6 +4099,27 @@ mips_gen_conditional_trap (rtx *operands)
                              operands[1]));
 }
 \f
+/* Return true if function DECL is a MIPS16 function.  Return the ambient
+   setting if DECL is null.  */
+
+static bool
+mips_use_mips16_mode_p (tree decl)
+{
+  if (decl)
+    {
+      /* Nested functions must use the same frame pointer as their
+        parent and must therefore use the same ISA mode.  */
+      tree parent = decl_function_context (decl);
+      if (parent)
+       decl = parent;
+      if (mips_mips16_decl_p (decl))
+       return true;
+      if (mips_nomips16_decl_p (decl))
+       return false;
+    }
+  return mips_base_mips16;
+}
+
 /* Return true if calls to X can use R_MIPS_CALL* relocations.  */
 
 static bool
@@ -4220,7 +4223,18 @@ mips_function_ok_for_sibcall (tree decl, tree exp ATTRIBUTE_UNUSED)
   /* We can't do a sibcall if the called function is a MIPS16 function
      because there is no direct "jx" instruction equivalent to "jalx" to
      switch the ISA mode.  */
-  if (decl && SYMBOL_REF_MIPS16_FUNC_P (XEXP (DECL_RTL (decl), 0)))
+  if (mips_use_mips16_mode_p (decl))
+    return false;
+
+  /* ...and when -minterlink-mips16 is in effect, assume that external
+     functions could be MIPS16 ones unless an attribute explicitly
+     tells us otherwise.  We only care about cases where the sibling
+     and normal calls would both be direct.  */
+  if (TARGET_INTERLINK_MIPS16
+      && decl
+      && DECL_EXTERNAL (decl)
+      && !mips_nomips16_decl_p (decl)
+      && const_call_insn_operand (XEXP (DECL_RTL (decl), 0), VOIDmode))
     return false;
 
   /* Otherwise OK.  */
@@ -5652,9 +5666,16 @@ mips_set_mips16_mode (int mips16_p)
         we use a %gprel() operator.  */
       target_flags &= ~MASK_EXPLICIT_RELOCS;
 
-      /* Silently disable DSP extensions.  */
-      target_flags &= ~MASK_DSP;
-      target_flags &= ~MASK_DSPR2;
+      /* Experiments suggest we get the best overall results from using
+        the range of an unextended lw or sw.  Code that makes heavy use
+        of byte or short accesses can do better with ranges of 0...31
+        and 0...63 respectively, but most code is sensitive to the range
+        of lw and sw instead.  */
+      targetm.min_anchor_offset = 0;
+      targetm.max_anchor_offset = 127;
+
+      if (flag_pic || TARGET_ABICALLS)
+       sorry ("MIPS16 PIC");
     }
   else 
     {
@@ -5676,6 +5697,9 @@ mips_set_mips16_mode (int mips16_p)
          if (align_functions == 0)
            align_functions = 8;
        }
+
+      targetm.min_anchor_offset = TARGET_MIN_ANCHOR_OFFSET;
+      targetm.max_anchor_offset = TARGET_MAX_ANCHOR_OFFSET;
     }
 
   /* (Re)initialize mips target internals for new ISA.  */
@@ -5689,12 +5713,130 @@ mips_set_mips16_mode (int mips16_p)
   was_mips16_p = TARGET_MIPS16;
 }
 
+/* Use a hash table to keep track of implicit mips16/nomips16 attributes
+   for -mflip_mips16.  It maps decl names onto a boolean mode setting.  */
+
+struct mflip_mips16_entry GTY (()) {
+  const char *name;
+  bool mips16_p;
+};
+static GTY ((param_is (struct mflip_mips16_entry))) htab_t mflip_mips16_htab;
+
+/* Hash table callbacks for mflip_mips16_htab.  */
+
+static hashval_t
+mflip_mips16_htab_hash (const void *entry)
+{
+  return htab_hash_string (((const struct mflip_mips16_entry *) entry)->name);
+}
+
+static int
+mflip_mips16_htab_eq (const void *entry, const void *name)
+{
+  return strcmp (((const struct mflip_mips16_entry *) entry)->name,
+                (const char *) name) == 0;
+}
+
+/* DECL is a function that needs a default "mips16" or "nomips16" attribute
+   for -mflip-mips16.  Return true if it should use "mips16" and false if
+   it should use "nomips16".  */
+
+static bool
+mflip_mips16_use_mips16_p (tree decl)
+{
+  struct mflip_mips16_entry *entry;
+  const char *name;
+  hashval_t hash;
+  void **slot;
+
+  /* Use the opposite of the command-line setting for anonymous decls.  */
+  if (!DECL_NAME (decl))
+    return !mips_base_mips16;
+
+  if (!mflip_mips16_htab)
+    mflip_mips16_htab = htab_create_ggc (37, mflip_mips16_htab_hash,
+                                        mflip_mips16_htab_eq, NULL);
+
+  name = IDENTIFIER_POINTER (DECL_NAME (decl));
+  hash = htab_hash_string (name);
+  slot = htab_find_slot_with_hash (mflip_mips16_htab, name, hash, INSERT);
+  entry = (struct mflip_mips16_entry *) *slot;
+  if (!entry)
+    {
+      mips16_flipper = !mips16_flipper;
+      entry = GGC_NEW (struct mflip_mips16_entry);
+      entry->name = name;
+      entry->mips16_p = mips16_flipper ? !mips_base_mips16 : mips_base_mips16;
+      *slot = entry;
+    }
+  return entry->mips16_p;
+}
+
+/* Implement TARGET_INSERT_ATTRIBUTES.  */
+
+static void
+mips_insert_attributes (tree decl, tree *attributes)
+{
+  const char *name;
+  bool mips16_p, nomips16_p;
+
+  /* Check for "mips16" and "nomips16" attributes.  */
+  mips16_p = lookup_attribute ("mips16", *attributes) != NULL;
+  nomips16_p = lookup_attribute ("nomips16", *attributes) != NULL;
+  if (TREE_CODE (decl) != FUNCTION_DECL)
+    {
+      if (mips16_p)
+       error ("%qs attribute only applies to functions", "mips16");
+      if (nomips16_p)
+       error ("%qs attribute only applies to functions", "nomips16");
+    }
+  else
+    {
+      mips16_p |= mips_mips16_decl_p (decl);
+      nomips16_p |= mips_nomips16_decl_p (decl);
+      if (mips16_p || nomips16_p)
+       {
+         /* DECL cannot be simultaneously mips16 and nomips16.  */
+         if (mips16_p && nomips16_p)
+           error ("%qs cannot have both %<mips16%> and "
+                  "%<nomips16%> attributes",
+                  IDENTIFIER_POINTER (DECL_NAME (decl)));
+       }
+      else if (TARGET_FLIP_MIPS16 && !DECL_ARTIFICIAL (decl))
+       {
+         /* Implement -mflip-mips16.  If DECL has neither a "nomips16" nor a
+            "mips16" attribute, arbitrarily pick one.  We must pick the same
+            setting for duplicate declarations of a function.  */
+         name = mflip_mips16_use_mips16_p (decl) ? "mips16" : "nomips16";
+         *attributes = tree_cons (get_identifier (name), NULL, *attributes);
+       }
+    }
+}
+
+/* Implement TARGET_MERGE_DECL_ATTRIBUTES.  */
+
+static tree
+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");
+  if (mips_nomips16_decl_p (olddecl) != mips_nomips16_decl_p (newdecl))
+    error ("%qs redeclared with conflicting %qs attributes",
+          IDENTIFIER_POINTER (DECL_NAME (newdecl)), "nomips16");
+
+  return merge_attributes (DECL_ATTRIBUTES (olddecl),
+                          DECL_ATTRIBUTES (newdecl));
+}
+
 /* Implement TARGET_SET_CURRENT_FUNCTION.  Decide whether the current 
    function should use the MIPS16 ISA and switch modes accordingly.  */
 
 static void
-mips_set_current_function (tree fndecl ATTRIBUTE_UNUSED)
+mips_set_current_function (tree fndecl)
 {
+  mips_set_mips16_mode (mips_use_mips16_mode_p (fndecl));
 }
 
 /* Implement TARGET_HANDLE_OPTION.  */
@@ -5873,29 +6015,19 @@ override_options (void)
     {
       /* If neither -mbranch-likely nor -mno-branch-likely was given
         on the command line, set MASK_BRANCHLIKELY based on the target
-        architecture.
-
-        By default, we enable use of Branch Likely instructions on
-        all architectures which support them with the following
-        exceptions: when creating MIPS32 or MIPS64 code, and when
-        tuning for architectures where their use tends to hurt
-        performance.
-
-        The MIPS32 and MIPS64 architecture specifications say "Software
-        is strongly encouraged to avoid use of Branch Likely
-        instructions, as they will be removed from a future revision
-        of the [MIPS32 and MIPS64] architecture."  Therefore, we do not
-        issue those instructions unless instructed to do so by
-        -mbranch-likely.  */
+        architecture and tuning flags.  Annulled delay slots are a
+        size win, so we only consider the processor-specific tuning
+        for !optimize_size.  */
       if (ISA_HAS_BRANCHLIKELY
-         && !(ISA_MIPS32 || ISA_MIPS32R2 || ISA_MIPS64)
-         && !(TUNE_MIPS5500 || TUNE_SB1))
+         && (optimize_size
+             || (mips_tune_info->tune_flags & PTF_AVOID_BRANCHLIKELY) == 0))
        target_flags |= MASK_BRANCHLIKELY;
       else
        target_flags &= ~MASK_BRANCHLIKELY;
     }
-  if (TARGET_BRANCHLIKELY && !ISA_HAS_BRANCHLIKELY)
-    warning (0, "generation of Branch Likely instructions enabled, but not supported by architecture");
+  else if (TARGET_BRANCHLIKELY && !ISA_HAS_BRANCHLIKELY)
+    warning (0, "the %qs architecture does not support branch-likely"
+            " instructions", mips_arch_info->name);
 
   /* The effect of -mabicalls isn't defined for the EABI.  */
   if (mips_abi == ABI_EABI && TARGET_ABICALLS)
@@ -5971,8 +6103,8 @@ override_options (void)
     target_flags |= MASK_PAIRED_SINGLE_FLOAT;
 
   /* Make sure that when TARGET_PAIRED_SINGLE_FLOAT is true, TARGET_FLOAT64
-     and TARGET_HARD_FLOAT are both true.  */
-  if (TARGET_PAIRED_SINGLE_FLOAT && !(TARGET_FLOAT64 && TARGET_HARD_FLOAT))
+     and TARGET_HARD_FLOAT_ABI are both true.  */
+  if (TARGET_PAIRED_SINGLE_FLOAT && !(TARGET_FLOAT64 && TARGET_HARD_FLOAT_ABI))
     error ("-mips3d/-mpaired-single must be used with -mfp64 -mhard-float");
 
   /* Make sure that the ISA supports TARGET_PAIRED_SINGLE_FLOAT when it is
@@ -6004,6 +6136,8 @@ override_options (void)
   mips_print_operand_punct['$'] = 1;
   mips_print_operand_punct['+'] = 1;
   mips_print_operand_punct['~'] = 1;
+  mips_print_operand_punct['|'] = 1;
+  mips_print_operand_punct['-'] = 1;
 
   /* Set up array to map GCC register number to debug register number.
      Ignore the special purpose register numbers.  */
@@ -6169,7 +6303,7 @@ mips_swap_registers (unsigned int i)
 void
 mips_conditional_register_usage (void)
 {
-  if (!TARGET_DSP)
+  if (!ISA_HAS_DSP)
     {
       int regno;
 
@@ -6366,7 +6500,9 @@ mips_strip_unspec_address (rtx op)
    '^' Print the name of the pic call-through register (t9 or $25).
    '$' Print the name of the stack pointer register (sp or $29).
    '+' Print the name of the gp register (usually gp or $28).
-   '~' Output a branch alignment to LABEL_ALIGN(NULL).  */
+   '~' Output a branch alignment to LABEL_ALIGN(NULL).
+   '|'  Print .set push; .set mips2 if !ISA_HAS_LL_SC.
+   '-'  Print .set pop under the same conditions for '|'.  */
 
 void
 print_operand (FILE *file, rtx op, int letter)
@@ -6496,6 +6632,16 @@ print_operand (FILE *file, rtx op, int letter)
          }
          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:
          error ("PRINT_OPERAND: unknown punctuation '%c'", letter);
          break;
@@ -6871,18 +7017,16 @@ mips_file_start (void)
         because in this way we can avoid creating an allocated section.  We
         do not want this section to take up any space in the running
         executable.  */
-      fprintf (asm_out_file, "\t.section .mdebug.%s\n", abi_string);
+      fprintf (asm_out_file, "\t.section .mdebug.%s\n\t.previous\n",
+              abi_string);
 
       /* There is no ELF header flag to distinguish long32 forms of the
         EABI from long64 forms.  Emit a special section to help tools
         such as GDB.  Do the same for o64, which is sometimes used with
         -mlong64.  */
       if (mips_abi == ABI_EABI || mips_abi == ABI_O64)
-       fprintf (asm_out_file, "\t.section .gcc_compiled_long%d\n",
-                TARGET_LONG64 ? 64 : 32);
-
-      /* Restore the default section.  */
-      fprintf (asm_out_file, "\t.previous\n");
+       fprintf (asm_out_file, "\t.section .gcc_compiled_long%d\n"
+                "\t.previous\n", TARGET_LONG64 ? 64 : 32);
 
 #ifdef HAVE_AS_GNU_ATTRIBUTE
       fprintf (asm_out_file, "\t.gnu_attribute 4, %d\n",
@@ -7289,6 +7433,10 @@ mips_save_reg_p (unsigned int regno)
   if (regno == HARD_FRAME_POINTER_REGNUM && frame_pointer_needed)
     return true;
 
+  /* Check for registers that must be saved for FUNCTION_PROFILER.  */
+  if (current_function_profile && MIPS_SAVE_REG_FOR_PROFILING_P (regno))
+    return true;
+
   /* We need to save the incoming return address if it is ever clobbered
      within the function, if __builtin_eh_return is being used to set a
      different return address, or if a stub is being used to return a
@@ -7863,7 +8011,7 @@ mips_save_reg (rtx reg, rtx mem)
       rtx x1, x2;
 
       if (mips_split_64bit_move_p (mem, reg))
-       mips_split_64bit_move (mem, reg);
+       mips_split_doubleword_move (mem, reg);
       else
        mips_emit_move (mem, reg);
 
@@ -8684,6 +8832,7 @@ mips_output_mi_thunk (FILE *file, tree thunk_fndecl ATTRIBUTE_UNUSED,
                      tree function)
 {
   rtx this, temp1, temp2, insn, fnaddr;
+  bool use_sibcall_p;
 
   /* Pretend to be a post-reload pass while generating rtl.  */
   reload_completed = 1;
@@ -8691,22 +8840,30 @@ mips_output_mi_thunk (FILE *file, tree thunk_fndecl ATTRIBUTE_UNUSED,
   /* Mark the end of the (empty) prologue.  */
   emit_note (NOTE_INSN_PROLOGUE_END);
 
-  /* Pick a global pointer.  Use a call-clobbered register if
-     TARGET_CALL_SAVED_GP, so that we can use a sibcall.  */
-  if (TARGET_USE_GOT)
-    {
-      cfun->machine->global_pointer =
-       TARGET_CALL_SAVED_GP ? 15 : GLOBAL_POINTER_REGNUM;
+  /* Determine if we can use a sibcall to call FUNCTION directly.  */
+  fnaddr = XEXP (DECL_RTL (function), 0);
+  use_sibcall_p = (mips_function_ok_for_sibcall (function, NULL)
+                  && const_call_insn_operand (fnaddr, Pmode));
 
-      SET_REGNO (pic_offset_table_rtx, cfun->machine->global_pointer);
-    }
+  /* Determine if we need to load FNADDR from the GOT.  */
+  if (!use_sibcall_p)
+    switch (mips_classify_symbol (fnaddr, SYMBOL_CONTEXT_LEA))
+      {
+      case SYMBOL_GOT_PAGE_OFST:
+      case SYMBOL_GOT_DISP:
+       /* Pick a global pointer.  Use a call-clobbered register if
+          TARGET_CALL_SAVED_GP.  */
+       cfun->machine->global_pointer =
+         TARGET_CALL_SAVED_GP ? 15 : GLOBAL_POINTER_REGNUM;
+       SET_REGNO (pic_offset_table_rtx, cfun->machine->global_pointer);
+
+       /* Set up the global pointer for n32 or n64 abicalls.  */
+       mips_emit_loadgp ();
+       break;
 
-  /* Set up the global pointer for n32 or n64 abicalls.  If
-     LOADGP_ABSOLUTE then the thunk does not use the gp and there is
-     no need to load it.*/
-  if (mips_current_loadgp_style () != LOADGP_ABSOLUTE
-      || !targetm.binds_local_p (function))
-    mips_emit_loadgp ();
+      default:
+       break;
+      }
 
   /* We need two temporary registers in some cases.  */
   temp1 = gen_rtx_REG (Pmode, 2);
@@ -8748,9 +8905,12 @@ mips_output_mi_thunk (FILE *file, tree thunk_fndecl ATTRIBUTE_UNUSED,
 
   /* Jump to the target function.  Use a sibcall if direct jumps are
      allowed, otherwise load the address into a register first.  */
-  fnaddr = XEXP (DECL_RTL (function), 0);
-  if (TARGET_MIPS16 || TARGET_USE_GOT || SYMBOL_REF_LONG_CALL_P (fnaddr)
-      || SYMBOL_REF_MIPS16_FUNC_P (fnaddr))
+  if (use_sibcall_p)
+    {
+      insn = emit_call_insn (gen_sibcall_internal (fnaddr, const0_rtx));
+      SIBLING_CALL_P (insn) = 1;
+    }
+  else
     {
       /* This is messy.  gas treats "la $25,foo" as part of a call
         sequence and may allow a global "foo" to be lazily bound.
@@ -8774,11 +8934,6 @@ mips_output_mi_thunk (FILE *file, tree thunk_fndecl ATTRIBUTE_UNUSED,
        mips_emit_move (gen_rtx_REG (Pmode, PIC_FUNCTION_ADDR_REGNUM), temp1);
       emit_jump_insn (gen_indirect_jump (temp1));
     }
-  else
-    {
-      insn = emit_call_insn (gen_sibcall_internal (fnaddr, const0_rtx));
-      SIBLING_CALL_P (insn) = 1;
-    }
 
   /* Run just enough of rest_of_compilation.  This sequence was
      "borrowed" from alpha.c.  */
@@ -8920,7 +9075,7 @@ mips_use_anchors_for_symbol_p (const_rtx symbol)
       return false;
 
     default:
-      return true;
+      return default_use_anchors_for_symbol_p (symbol);
     }
 }
 \f
@@ -9130,44 +9285,38 @@ mips_callee_copies (CUMULATIVE_ARGS *cum ATTRIBUTE_UNUSED,
    to mode TO.  */
 
 bool
-mips_cannot_change_mode_class (enum machine_mode from,
-                              enum machine_mode to, enum reg_class class)
+mips_cannot_change_mode_class (enum machine_mode from ATTRIBUTE_UNUSED,
+                              enum machine_mode to ATTRIBUTE_UNUSED,
+                              enum reg_class class)
 {
-  if (MIN (GET_MODE_SIZE (from), GET_MODE_SIZE (to)) <= UNITS_PER_WORD
-      && MAX (GET_MODE_SIZE (from), GET_MODE_SIZE (to)) > UNITS_PER_WORD)
-    {
-      if (TARGET_BIG_ENDIAN)
-       {
-         /* When a multi-word value is stored in paired floating-point
-            registers, the first register always holds the low word.
-            We therefore can't allow FPRs to change between single-word
-            and multi-word modes.  */
-         if (MAX_FPRS_PER_FMT > 1 && reg_classes_intersect_p (FP_REGS, class))
-           return true;
-       }
-    }
+  /* There are several problems with changing the modes of values
+     in floating-point registers:
 
-  /* gcc assumes that each word of a multiword register can be accessed
-     individually using SUBREGs.  This is not true for floating-point
-     registers if they are bigger than a word.  */
-  if (UNITS_PER_FPREG > UNITS_PER_WORD
-      && GET_MODE_SIZE (from) > UNITS_PER_WORD
-      && GET_MODE_SIZE (to) < UNITS_PER_FPREG
-      && reg_classes_intersect_p (FP_REGS, class))
-    return true;
+     - When a multi-word value is stored in paired floating-point
+       registers, the first register always holds the low word.
+       We therefore can't allow FPRs to change between single-word
+       and multi-word modes on big-endian targets.
 
-  /* Loading a 32-bit value into a 64-bit floating-point register
-     will not sign-extend the value, despite what LOAD_EXTEND_OP says.
-     We can't allow 64-bit float registers to change from SImode to
-     to a wider mode.  */
-  if (TARGET_64BIT
-      && TARGET_FLOAT64
-      && from == SImode
-      && GET_MODE_SIZE (to) >= UNITS_PER_WORD
-      && reg_classes_intersect_p (FP_REGS, class))
-    return true;
+     - GCC assumes that each word of a multiword register can be accessed
+       individually using SUBREGs.  This is not true for floating-point
+       registers if they are bigger than a word.
 
-  return false;
+     - Loading a 32-bit value into a 64-bit floating-point register
+       will not sign-extend the value, despite what LOAD_EXTEND_OP says.
+       We can't allow FPRs to change from SImode to to a wider mode on
+       64-bit targets.
+
+     - If the FPU has already interpreted a value in one format, we must
+       not ask it to treat the value as having a different format.
+
+     We therefore only allow changes between 4-byte and smaller integer
+     values, all of which have the "W" format as far as the FPU is
+     concerned.  */
+  return (reg_classes_intersect_p (FP_REGS, class)
+         && (GET_MODE_CLASS (from) != MODE_INT
+             || GET_MODE_CLASS (to) != MODE_INT
+             || GET_MODE_SIZE (from) > 4
+             || GET_MODE_SIZE (to) > 4));
 }
 
 /* Return true if X should not be moved directly into register $25.
@@ -9183,6 +9332,27 @@ mips_dangerous_for_la25_p (rtx x)
          && mips_global_symbol_p (x));
 }
 
+/* Return true if moves in mode MODE can use the FPU's mov.fmt instruction.  */
+
+static bool
+mips_mode_ok_for_mov_fmt_p (enum machine_mode mode)
+{
+  switch (mode)
+    {
+    case SFmode:
+      return TARGET_HARD_FLOAT;
+
+    case DFmode:
+      return TARGET_HARD_FLOAT && TARGET_DOUBLE_FLOAT;
+
+    case V2SFmode:
+      return TARGET_HARD_FLOAT && TARGET_PAIRED_SINGLE_FLOAT;
+
+    default:
+      return false;
+    }
+}
+
 /* Implement PREFERRED_RELOAD_CLASS.  */
 
 enum reg_class
@@ -9191,9 +9361,8 @@ mips_preferred_reload_class (rtx x, enum reg_class class)
   if (mips_dangerous_for_la25_p (x) && reg_class_subset_p (LEA_REGS, class))
     return LEA_REGS;
 
-  if (TARGET_HARD_FLOAT
-      && FLOAT_MODE_P (GET_MODE (x))
-      && reg_class_subset_p (FP_REGS, class))
+  if (reg_class_subset_p (FP_REGS, class)
+      && mips_mode_ok_for_mov_fmt_p (GET_MODE (x)))
     return FP_REGS;
 
   if (reg_class_subset_p (GR_REGS, class))
@@ -9215,110 +9384,78 @@ enum reg_class
 mips_secondary_reload_class (enum reg_class class,
                             enum machine_mode mode, rtx x, int in_p)
 {
-  enum reg_class gr_regs = TARGET_MIPS16 ? M16_REGS : GR_REGS;
-  int regno = -1;
-  int gp_reg_p;
-
-  if (REG_P (x)|| GET_CODE (x) == SUBREG)
-    regno = true_regnum (x);
-
-  gp_reg_p = TARGET_MIPS16 ? M16_REG_P (regno) : GP_REG_P (regno);
+  int regno;
 
+  /* If X is a constant that cannot be loaded into $25, it must be loaded
+     into some other GPR.  No other register class allows a direct move.  */
   if (mips_dangerous_for_la25_p (x))
+    return reg_class_subset_p (class, LEA_REGS) ? NO_REGS : LEA_REGS;
+
+  regno = true_regnum (x);
+  if (TARGET_MIPS16)
     {
-      gr_regs = LEA_REGS;
-      if (TEST_HARD_REG_BIT (reg_class_contents[(int) class], 25))
-       return gr_regs;
+      /* In MIPS16 mode, every move must involve a member of M16_REGS.  */
+      if (!reg_class_subset_p (class, M16_REGS) && !M16_REG_P (regno))
+       return M16_REGS;
+
+      /* We can't really copy to HI or LO at all in MIPS16 mode.  */
+      if (in_p ? reg_classes_intersect_p (class, ACC_REGS) : ACC_REG_P (regno))
+       return M16_REGS;
+
+      return NO_REGS;
     }
 
-  /* Copying from HI or LO to anywhere other than a general register
-     requires a general register.
-     This rule applies to both the original HI/LO pair and the new
-     DSP accumulators.  */
+  /* Copying from accumulator registers to anywhere other than a general
+     register requires a temporary general register.  */
   if (reg_class_subset_p (class, ACC_REGS))
-    {
-      if (TARGET_MIPS16 && in_p)
-       {
-         /* We can't really copy to HI or LO at all in mips16 mode.  */
-         return M16_REGS;
-       }
-      return gp_reg_p ? NO_REGS : gr_regs;
-    }
+    return GP_REG_P (regno) ? NO_REGS : GR_REGS;
   if (ACC_REG_P (regno))
-    {
-      if (TARGET_MIPS16 && ! in_p)
-       {
-         /* We can't really copy to HI or LO at all in mips16 mode.  */
-         return M16_REGS;
-       }
-      return class == gr_regs ? NO_REGS : gr_regs;
-    }
+    return reg_class_subset_p (class, GR_REGS) ? NO_REGS : GR_REGS;
 
   /* We can only copy a value to a condition code register from a
      floating point register, and even then we require a scratch
      floating point register.  We can only copy a value out of a
      condition code register into a general register.  */
-  if (class == ST_REGS)
+  if (reg_class_subset_p (class, ST_REGS))
     {
       if (in_p)
        return FP_REGS;
-      return gp_reg_p ? NO_REGS : gr_regs;
+      return GP_REG_P (regno) ? NO_REGS : GR_REGS;
     }
   if (ST_REG_P (regno))
     {
-      if (! in_p)
+      if (!in_p)
        return FP_REGS;
-      return class == gr_regs ? NO_REGS : gr_regs;
+      return reg_class_subset_p (class, GR_REGS) ? NO_REGS : GR_REGS;
     }
 
-  if (class == FP_REGS)
+  if (reg_class_subset_p (class, FP_REGS))
     {
-      if (MEM_P (x))
-       {
-         /* In this case we can use lwc1, swc1, ldc1 or sdc1.  */
-         return NO_REGS;
-       }
-      else if (CONSTANT_P (x) && GET_MODE_CLASS (mode) == MODE_FLOAT)
-       {
-         /* We can use the l.s and l.d macros to load floating-point
-            constants.  ??? For l.s, we could probably get better
-            code by returning GR_REGS here.  */
-         return NO_REGS;
-       }
-      else if (gp_reg_p || x == CONST0_RTX (mode))
-       {
-         /* In this case we can use mtc1, mfc1, dmtc1 or dmfc1.  */
-         return NO_REGS;
-       }
-      else if (FP_REG_P (regno))
-       {
-         /* In this case we can use mov.s or mov.d.  */
-         return NO_REGS;
-       }
-      else
-       {
-         /* Otherwise, we need to reload through an integer register.  */
-         return gr_regs;
-       }
-    }
+      if (MEM_P (x)
+         && (GET_MODE_SIZE (mode) == 4 || GET_MODE_SIZE (mode) == 8))
+       /* In this case we can use lwc1, swc1, ldc1 or sdc1.  We'll use
+          pairs of lwc1s and swc1s if ldc1 and sdc1 are not supported.  */
+       return NO_REGS;
 
-  /* In mips16 mode, going between memory and anything but M16_REGS
-     requires an M16_REG.  */
-  if (TARGET_MIPS16)
-    {
-      if (class != M16_REGS && class != M16_NA_REGS)
-       {
-         if (gp_reg_p)
-           return NO_REGS;
-         return M16_REGS;
-       }
-      if (! gp_reg_p)
-       {
-         if (class == M16_REGS || class == M16_NA_REGS)
-           return NO_REGS;
-         return M16_REGS;
-       }
+      if (GP_REG_P (regno) || x == CONST0_RTX (mode))
+       /* In this case we can use mtc1, mfc1, dmtc1 or dmfc1.  */
+       return NO_REGS;
+
+      if (CONSTANT_P (x) && !targetm.cannot_force_const_mem (x))
+       /* We can force the constant to memory and use lwc1
+          and ldc1.  As above, we will use pairs of lwc1s if
+          ldc1 is not supported.  */
+       return NO_REGS;
+
+      if (FP_REG_P (regno) && mips_mode_ok_for_mov_fmt_p (mode))
+       /* In this case we can use mov.fmt.  */
+       return NO_REGS;
+
+      /* Otherwise, we need to reload through an integer register.  */
+      return GR_REGS;
     }
+  if (FP_REG_P (regno))
+    return reg_class_subset_p (class, GR_REGS) ? NO_REGS : GR_REGS;
 
   return NO_REGS;
 }
@@ -9517,6 +9654,7 @@ build_mips16_function_stub (FILE *file)
   unsigned int f;
 
   fnname = XSTR (XEXP (DECL_RTL (current_function_decl), 0), 0);
+  fnname = targetm.strip_name_encoding (fnname);
   secname = (char *) alloca (strlen (fnname) + 20);
   sprintf (secname, ".mips16.fn.%s", fnname);
   stubname = (char *) alloca (strlen (fnname) + 20);
@@ -9753,7 +9891,7 @@ build_mips16_call_stub (rtx retval, rtx fn, rtx arg_size, int fp_code)
   /* We know the function we are going to call.  If we have already
      built a stub, we don't need to do anything further.  */
 
-  fnname = XSTR (fn, 0);
+  fnname = targetm.strip_name_encoding (XSTR (fn, 0));
   for (l = mips16_stubs; l != NULL; l = l->next)
     if (strcmp (l->name, fnname) == 0)
       break;
@@ -9851,50 +9989,43 @@ build_mips16_call_stub (rtx retval, rtx fn, rtx arg_size, int fp_code)
          fprintf (asm_out_file, "\tjal\t%s\n", fnname);
          /* As above, we can't fill the delay slot.  */
          fprintf (asm_out_file, "\tnop\n");
-         if (GET_MODE (retval) == SFmode)
-           fprintf (asm_out_file, "\tmfc1\t%s,%s\n",
-                    reg_names[GP_REG_FIRST + 2], reg_names[FP_REG_FIRST + 0]);
-         else if (GET_MODE (retval) == SCmode)
-           {
-             fprintf (asm_out_file, "\tmfc1\t%s,%s\n",
-                      reg_names[GP_REG_FIRST + 2],
-                      reg_names[FP_REG_FIRST + 0]);
+         switch (GET_MODE (retval))
+           {
+           case SCmode:
              fprintf (asm_out_file, "\tmfc1\t%s,%s\n",
                       reg_names[GP_REG_FIRST + 3],
                       reg_names[FP_REG_FIRST + MAX_FPRS_PER_FMT]);
-           }
-         else if (GET_MODE (retval) == DFmode
-                  || GET_MODE (retval) == V2SFmode)
-           {
-             mips16_fpret_double (GP_REG_FIRST + 2, FP_REG_FIRST + 0);
-           }
-         else if (GET_MODE (retval) == DCmode)
-           {
-             mips16_fpret_double (GP_REG_FIRST + 2,
-                                  FP_REG_FIRST + 0);
-             mips16_fpret_double (GP_REG_FIRST + 4,
-                                  FP_REG_FIRST + MAX_FPRS_PER_FMT);
-           }
-         else
-           {
-             if (TARGET_BIG_ENDIAN)
+             /* Fall though.  */
+           case SFmode:
+             fprintf (asm_out_file, "\tmfc1\t%s,%s\n",
+                      reg_names[GP_REG_FIRST + 2],
+                      reg_names[FP_REG_FIRST + 0]);
+             if (GET_MODE (retval) == SCmode && TARGET_64BIT)
                {
-                 fprintf (asm_out_file, "\tmfc1\t%s,%s\n",
+                 /* 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_REG_FIRST + 2 + TARGET_LITTLE_ENDIAN],
+                          reg_names[GP_REG_FIRST + 2 + TARGET_LITTLE_ENDIAN]);
+                 fprintf (asm_out_file, "\tor\t%s,%s,%s\n",
                           reg_names[GP_REG_FIRST + 2],
-                          reg_names[FP_REG_FIRST + 1]);
-                 fprintf (asm_out_file, "\tmfc1\t%s,%s\n",
-                          reg_names[GP_REG_FIRST + 3],
-                          reg_names[FP_REG_FIRST + 0]);
-               }
-             else
-               {
-                 fprintf (asm_out_file, "\tmfc1\t%s,%s\n",
                           reg_names[GP_REG_FIRST + 2],
-                          reg_names[FP_REG_FIRST + 0]);
-                 fprintf (asm_out_file, "\tmfc1\t%s,%s\n",
-                          reg_names[GP_REG_FIRST + 3],
-                          reg_names[FP_REG_FIRST + 1]);
+                          reg_names[GP_REG_FIRST + 3]);
                }
+             break;
+
+           case DCmode:
+             mips16_fpret_double (GP_REG_FIRST + 2 + (8 / UNITS_PER_WORD),
+                                  FP_REG_FIRST + MAX_FPRS_PER_FMT);
+             /* Fall though.  */
+           case DFmode:
+           case V2SFmode:
+             mips16_fpret_double (GP_REG_FIRST + 2, FP_REG_FIRST + 0);
+             break;
+
+           default:
+             gcc_unreachable ();
            }
          fprintf (asm_out_file, "\tj\t%s\n", reg_names[GP_REG_FIRST + 18]);
          /* As above, we can't fill the delay slot.  */
@@ -10037,30 +10168,28 @@ add_constant (struct mips16_constant_pool *pool,
 static rtx
 dump_constants_1 (enum machine_mode mode, rtx value, rtx insn)
 {
-  switch (GET_MODE_CLASS (mode))
+  if (SCALAR_INT_MODE_P (mode)
+      || ALL_SCALAR_FRACT_MODE_P (mode)
+      || ALL_SCALAR_ACCUM_MODE_P (mode))
     {
-    case MODE_INT:
-      {
-       rtx size = GEN_INT (GET_MODE_SIZE (mode));
-       return emit_insn_after (gen_consttable_int (value, size), insn);
-      }
+      rtx size = GEN_INT (GET_MODE_SIZE (mode));
+      return emit_insn_after (gen_consttable_int (value, size), insn);
+    }
 
-    case MODE_FLOAT:
-      return emit_insn_after (gen_consttable_float (value), insn);
+  if (SCALAR_FLOAT_MODE_P (mode))
+    return emit_insn_after (gen_consttable_float (value), insn);
 
-    case MODE_VECTOR_FLOAT:
-    case MODE_VECTOR_INT:
-      {
-       int i;
-       for (i = 0; i < CONST_VECTOR_NUNITS (value); i++)
-         insn = dump_constants_1 (GET_MODE_INNER (mode),
-                                  CONST_VECTOR_ELT (value, i), insn);
-       return insn;
-      }
+  if (VECTOR_MODE_P (mode))
+    {
+      int i;
 
-    default:
-      gcc_unreachable ();
+      for (i = 0; i < CONST_VECTOR_NUNITS (value); i++)
+       insn = dump_constants_1 (GET_MODE_INNER (mode),
+                                CONST_VECTOR_ELT (value, i), insn);
+      return insn;
     }
+
+  gcc_unreachable ();
 }
 
 
@@ -10840,69 +10969,54 @@ mips_init_libfuncs (void)
    we need to use.  This gets pretty messy, but it is feasible.  */
 
 int
-mips_register_move_cost (enum machine_mode mode ATTRIBUTE_UNUSED,
+mips_register_move_cost (enum machine_mode mode,
                         enum reg_class to, enum reg_class from)
 {
-  if (from == M16_REGS && reg_class_subset_p (to, GENERAL_REGS))
-    return 2;
-  else if (from == M16_NA_REGS && reg_class_subset_p (to, GENERAL_REGS))
-    return 2;
-  else if (reg_class_subset_p (from, GENERAL_REGS))
+  if (TARGET_MIPS16)
     {
-      if (to == M16_REGS)
-       return 2;
-      else if (to == M16_NA_REGS)
-       return 2;
-      else if (reg_class_subset_p (to, GENERAL_REGS))
+      if (reg_class_subset_p (from, GENERAL_REGS)
+         && reg_class_subset_p (to, GENERAL_REGS))
        {
-         if (TARGET_MIPS16)
-           return 4;
-         else
+         if (reg_class_subset_p (from, M16_REGS)
+             || reg_class_subset_p (to, M16_REGS))
            return 2;
-       }
-      else if (to == FP_REGS)
-       return 4;
-      else if (reg_class_subset_p (to, ACC_REGS))
-       {
-         if (TARGET_MIPS16)
-           return 12;
-         else
-           return 6;
-       }
-      else if (reg_class_subset_p (to, ALL_COP_REGS))
-       {
-         return 5;
+         /* Two MOVEs.  */
+         return 4;
        }
     }
-  else if (from == FP_REGS)
+  else if (reg_class_subset_p (from, GENERAL_REGS))
     {
       if (reg_class_subset_p (to, GENERAL_REGS))
-       return 4;
-      else if (to == FP_REGS)
        return 2;
-      else if (to == ST_REGS)
-       return 8;
+      if (reg_class_subset_p (to, FP_REGS))
+       return 4;
+      if (reg_class_subset_p (to, ALL_COP_AND_GR_REGS))
+       return 5;
+      if (reg_class_subset_p (to, ACC_REGS))
+       return 6;
     }
-  else if (reg_class_subset_p (from, ACC_REGS))
+  else if (reg_class_subset_p (to, GENERAL_REGS))
     {
-      if (reg_class_subset_p (to, GENERAL_REGS))
-       {
-         if (TARGET_MIPS16)
-           return 12;
-         else
-           return 6;
-       }
+      if (reg_class_subset_p (from, FP_REGS))
+       return 4;
+      if (reg_class_subset_p (from, ST_REGS))
+       /* LUI followed by MOVF.  */
+       return 4;
+      if (reg_class_subset_p (from, ALL_COP_AND_GR_REGS))
+       return 5;
+      if (reg_class_subset_p (from, ACC_REGS))
+       return 6;
     }
-  else if (from == ST_REGS && reg_class_subset_p (to, GENERAL_REGS))
-    return 4;
-  else if (reg_class_subset_p (from, ALL_COP_REGS))
+  else if (reg_class_subset_p (from, FP_REGS))
     {
-      return 5;
+      if (reg_class_subset_p (to, FP_REGS)
+         && mips_mode_ok_for_mov_fmt_p (mode))
+       return 4;
+      if (reg_class_subset_p (to, ST_REGS))
+       /* An expensive sequence.  */
+       return 8;
     }
 
-  /* Fall through.
-     ??? What cases are these? Shouldn't we return 2 here?  */
-
   return 12;
 }
 
@@ -11272,7 +11386,7 @@ mips_matching_cpu_name_p (const char *canonical, const char *given)
 static const struct mips_cpu_info *
 mips_parse_cpu (const char *cpu_string)
 {
-  const struct mips_cpu_info *p;
+  unsigned int i;
   const char *s;
 
   /* In the past, we allowed upper-case CPU names, but it doesn't
@@ -11298,9 +11412,9 @@ mips_parse_cpu (const char *cpu_string)
   if (strcasecmp (cpu_string, "default") == 0)
     return 0;
 
-  for (p = mips_cpu_info_table; p->name != 0; p++)
-    if (mips_matching_cpu_name_p (p->name, cpu_string))
-      return p;
+  for (i = 0; i < ARRAY_SIZE (mips_cpu_info_table); i++)
+    if (mips_matching_cpu_name_p (mips_cpu_info_table[i].name, cpu_string))
+      return mips_cpu_info_table + i;
 
   return 0;
 }
@@ -11312,11 +11426,11 @@ mips_parse_cpu (const char *cpu_string)
 static const struct mips_cpu_info *
 mips_cpu_info_from_isa (int isa)
 {
-  const struct mips_cpu_info *p;
+  unsigned int i;
 
-  for (p = mips_cpu_info_table; p->name != 0; p++)
-    if (p->isa == isa)
-      return p;
+  for (i = 0; i < ARRAY_SIZE (mips_cpu_info_table); i++)
+    if (mips_cpu_info_table[i].isa == isa)
+      return mips_cpu_info_table + i;
 
   return 0;
 }
@@ -12691,43 +12805,6 @@ mips_expand_builtin_bposge (enum mips_builtin_type builtin_type, rtx target)
                                       const1_rtx, const0_rtx);
 }
 \f
-/* Return true if we should force MIPS16 mode for the function named by
-   the SYMBOL_REF SYMBOL, which belongs to DECL and has type TYPE.
-   FIRST is true if this is the first time handling this decl.  */
-
-static bool
-mips_use_mips16_mode_p (rtx symbol, tree decl, int first, tree type)
-{
-  tree parent;
-
-  /* Explicit function attributes take precedence.  */
-  if (mips_mips16_type_p (type))
-    return true;
-  if (mips_nomips16_type_p (type))
-    return false;
-
-  /* A nested function should inherit the MIPS16 setting from its parent.  */
-  parent = decl_function_context (decl);
-  if (parent)
-    return SYMBOL_REF_MIPS16_FUNC_P (XEXP (DECL_RTL (parent), 0));
-
-  /* Handle -mflip-mips16.  */
-  if (TARGET_FLIP_MIPS16
-      && !DECL_BUILT_IN (decl)
-      && !DECL_ARTIFICIAL (decl))
-    {
-      if (!first)
-       /* Use the setting we picked first time around.  */
-       return SYMBOL_REF_MIPS16_FUNC_P (symbol);
-
-      mips16_flipper = !mips16_flipper;
-      if (mips16_flipper)
-       return !mips_base_mips16;
-    }
-
-  return mips_base_mips16;
-}
-
 /* Set SYMBOL_REF_FLAGS for the SYMBOL_REF inside RTL, which belongs to DECL.
    FIRST is true if this is the first time handling this decl.  */
 
@@ -12744,14 +12821,6 @@ mips_encode_section_info (tree decl, rtx rtl, int first)
       if ((TARGET_LONG_CALLS && !mips_near_type_p (type))
          || mips_far_type_p (type))
        SYMBOL_REF_FLAGS (symbol) |= SYMBOL_FLAG_LONG_CALL;
-
-      if (mips_use_mips16_mode_p (symbol, decl, first, type))
-       {
-         if (flag_pic || TARGET_ABICALLS)
-           sorry ("MIPS16 PIC");
-         else
-           SYMBOL_REF_FLAGS (symbol) |= SYMBOL_FLAG_MIPS16_FUNC;
-       }
     }
 }