OSDN Git Service

gcc/
[pf3gnuchains/gcc-fork.git] / gcc / config / mips / mips.c
index 289f46e..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,10 +5666,6 @@ 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
@@ -5663,6 +5673,9 @@ mips_set_mips16_mode (int mips16_p)
         of lw and sw instead.  */
       targetm.min_anchor_offset = 0;
       targetm.max_anchor_offset = 127;
+
+      if (flag_pic || TARGET_ABICALLS)
+       sorry ("MIPS16 PIC");
     }
   else 
     {
@@ -5700,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.  */
@@ -5884,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)
@@ -5982,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
@@ -6015,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.  */
@@ -6180,7 +6303,7 @@ mips_swap_registers (unsigned int i)
 void
 mips_conditional_register_usage (void)
 {
-  if (!TARGET_DSP)
+  if (!ISA_HAS_DSP)
     {
       int regno;
 
@@ -6377,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)
@@ -6507,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;
@@ -6882,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",
@@ -7300,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
@@ -7874,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);
 
@@ -8695,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;
@@ -8702,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);
@@ -8759,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.
@@ -8785,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.  */
@@ -9141,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.
@@ -9194,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
@@ -9202,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))
@@ -9226,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;
 }
@@ -9528,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);
@@ -9764,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;
@@ -9862,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.  */
@@ -10048,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 ();
 }
 
 
@@ -10851,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;
 }
 
@@ -11283,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
@@ -11309,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;
 }
@@ -11323,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;
 }
@@ -12702,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.  */
 
@@ -12755,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;
-       }
     }
 }