OSDN Git Service

gcc/
[pf3gnuchains/gcc-fork.git] / gcc / config / mips / mips.c
index 7203a83..889997d 100644 (file)
@@ -1,6 +1,7 @@
 /* Subroutines used for MIPS code generation.
    Copyright (C) 1989, 1990, 1991, 1993, 1994, 1995, 1996, 1997, 1998,
-   1999, 2000, 2001, 2002, 2003, 2004, 2005 Free Software Foundation, Inc.
+   1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007 
+   Free Software Foundation, Inc.
    Contributed by A. Lichnewsky, lich@inria.inria.fr.
    Changes by Michael Meissner, meissner@osf.org.
    64-bit r4000 support by Ian Lance Taylor, ian@cygnus.com, and
@@ -10,7 +11,7 @@ This file is part of GCC.
 
 GCC is free software; you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
-the Free Software Foundation; either version 2, or (at your option)
+the Free Software Foundation; either version 3, or (at your option)
 any later version.
 
 GCC is distributed in the hope that it will be useful,
@@ -19,9 +20,8 @@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details.
 
 You should have received a copy of the GNU General Public License
-along with GCC; see the file COPYING.  If not, write to
-the Free Software Foundation, 51 Franklin Street, Fifth Floor,
-Boston, MA 02110-1301, USA.  */
+along with GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
 
 #include "config.h"
 #include "system.h"
@@ -57,6 +57,7 @@ Boston, MA 02110-1301, USA.  */
 #include "sched-int.h"
 #include "tree-gimple.h"
 #include "bitmap.h"
+#include "diagnostic.h"
 
 /* True if X is an unspec wrapper around a SYMBOL_REF or LABEL_REF.  */
 #define UNSPEC_ADDRESS_P(X)                                    \
@@ -73,15 +74,25 @@ Boston, MA 02110-1301, USA.  */
   ((enum mips_symbol_type) (XINT (X, 1) - UNSPEC_ADDRESS_FIRST))
 
 /* The maximum distance between the top of the stack frame and the
-   value $sp has when we save & restore registers.
-
-   Use a maximum gap of 0x100 in the mips16 case.  We can then use
-   unextended instructions to save and restore registers, and to
-   allocate and deallocate the top part of the frame.
-
-   The value in the !mips16 case must be a SMALL_OPERAND and must
-   preserve the maximum stack alignment.  */
-#define MIPS_MAX_FIRST_STACK_STEP (TARGET_MIPS16 ? 0x100 : 0x7ff0)
+   value $sp has when we save and restore registers.
+
+   The value for normal-mode code must be a SMALL_OPERAND and must
+   preserve the maximum stack alignment.  We therefore use a value
+   of 0x7ff0 in this case.
+
+   MIPS16e SAVE and RESTORE instructions can adjust the stack pointer by
+   up to 0x7f8 bytes and can usually save or restore all the registers
+   that we need to save or restore.  (Note that we can only use these
+   instructions for o32, for which the stack alignment is 8 bytes.)
+
+   We use a maximum gap of 0x100 or 0x400 for MIPS16 code when SAVE and
+   RESTORE are not available.  We can then use unextended instructions
+   to save and restore registers, and to allocate and deallocate the top
+   part of the frame.  */
+#define MIPS_MAX_FIRST_STACK_STEP                                      \
+  (!TARGET_MIPS16 ? 0x7ff0                                             \
+   : GENERATE_MIPS16E_SAVE_RESTORE ? 0x7f8                             \
+   : TARGET_64BIT ? 0x100 : 0x400)
 
 /* True if INSN is a mips.md pattern or asm statement.  */
 #define USEFUL_INSN_P(INSN)                                            \
@@ -111,6 +122,9 @@ Boston, MA 02110-1301, USA.  */
        (SUBINSN) != NEXT_INSN (SEQ_END (INSN));                                \
        (SUBINSN) = NEXT_INSN (SUBINSN))
 
+/* True if bit BIT is set in VALUE.  */
+#define BITSET_P(VALUE, BIT) (((VALUE) & (1 << (BIT))) != 0)
+
 /* Classifies an address.
 
    ADDRESS_REG
@@ -274,14 +288,12 @@ struct mips_address_info;
 struct mips_integer_op;
 struct mips_sim;
 
-static enum mips_symbol_type mips_classify_symbol (rtx);
 static bool mips_valid_base_register_p (rtx, enum machine_mode, int);
-static bool mips_symbolic_address_p (enum mips_symbol_type, enum machine_mode);
 static bool mips_classify_address (struct mips_address_info *, rtx,
                                   enum machine_mode, int);
 static bool mips_cannot_force_const_mem (rtx);
-static bool mips_use_blocks_for_constant_p (enum machine_mode, rtx);
-static int mips_symbol_insns (enum mips_symbol_type);
+static bool mips_use_blocks_for_constant_p (enum machine_mode, const_rtx);
+static int mips_symbol_insns (enum mips_symbol_type, enum machine_mode);
 static bool mips16_unextended_reference_p (enum machine_mode mode, rtx, rtx);
 static rtx mips_force_temporary (rtx, rtx);
 static rtx mips_unspec_offset_high (rtx, rtx, rtx, enum mips_symbol_type);
@@ -308,9 +320,9 @@ static void mips_set_architecture (const struct mips_cpu_info *);
 static void mips_set_tune (const struct mips_cpu_info *);
 static bool mips_handle_option (size_t, const char *, int);
 static struct machine_function *mips_init_machine_status (void);
-static void print_operand_reloc (FILE *, rtx, const char **);
+static void print_operand_reloc (FILE *, rtx, enum mips_symbol_context,
+                                const char **);
 static void mips_file_start (void);
-static bool mips_rewrite_small_data_p (rtx);
 static int mips_small_data_pattern_1 (rtx *, void *);
 static int mips_rewrite_small_data_1 (rtx *, void *);
 static bool mips_function_has_gp_insn (void);
@@ -329,14 +341,13 @@ static void mips_output_function_epilogue (FILE *, HOST_WIDE_INT);
 static void mips_restore_reg (rtx, rtx);
 static void mips_output_mi_thunk (FILE *, tree, HOST_WIDE_INT,
                                  HOST_WIDE_INT, tree);
-static int symbolic_expression_p (rtx);
 static section *mips_select_rtx_section (enum machine_mode, rtx,
                                         unsigned HOST_WIDE_INT);
 static section *mips_function_rodata_section (tree);
-static bool mips_in_small_data_p (tree);
-static bool mips_use_anchors_for_symbol_p (rtx);
-static int mips_fpr_return_fields (tree, tree *);
-static bool mips_return_in_msb (tree);
+static bool mips_in_small_data_p (const_tree);
+static bool mips_use_anchors_for_symbol_p (const_rtx);
+static int mips_fpr_return_fields (const_tree, tree *);
+static bool mips_return_in_msb (const_tree);
 static rtx mips_return_fpr_pair (enum machine_mode mode,
                                 enum machine_mode mode1, HOST_WIDE_INT,
                                 enum machine_mode mode2, HOST_WIDE_INT);
@@ -357,7 +368,7 @@ static void mips_sim_wait_regs_1 (rtx *, void *);
 static void mips_sim_wait_regs (struct mips_sim *, rtx);
 static void mips_sim_wait_units (struct mips_sim *, rtx);
 static void mips_sim_wait_insn (struct mips_sim *, rtx);
-static void mips_sim_record_set (rtx, rtx, void *);
+static void mips_sim_record_set (rtx, const_rtx, void *);
 static void mips_sim_issue_insn (struct mips_sim *, rtx);
 static void mips_sim_issue_nop (struct mips_sim *);
 static void mips_sim_finish_insn (struct mips_sim *, rtx);
@@ -370,15 +381,16 @@ static bool mips_strict_matching_cpu_name_p (const char *, const char *);
 static bool mips_matching_cpu_name_p (const char *, const char *);
 static const struct mips_cpu_info *mips_parse_cpu (const char *);
 static const struct mips_cpu_info *mips_cpu_info_from_isa (int);
-static bool mips_return_in_memory (tree, tree);
+static bool mips_return_in_memory (const_tree, const_tree);
 static bool mips_strict_argument_naming (CUMULATIVE_ARGS *);
 static void mips_macc_chains_record (rtx);
 static void mips_macc_chains_reorder (rtx *, int);
-static void vr4130_true_reg_dependence_p_1 (rtx, rtx, void *);
+static void vr4130_true_reg_dependence_p_1 (rtx, const_rtx, void *);
 static bool vr4130_true_reg_dependence_p (rtx);
 static bool vr4130_swap_insns_p (rtx, rtx);
 static void vr4130_reorder (rtx *, int);
 static void mips_promote_ready (rtx *, int, int);
+static void mips_sched_init (FILE *, int, int);
 static int mips_sched_reorder (FILE *, int, rtx *, int *, int);
 static int mips_variable_issue (FILE *, int, rtx, int);
 static int mips_adjust_cost (rtx, rtx, rtx, int);
@@ -390,12 +402,13 @@ static void mips_setup_incoming_varargs (CUMULATIVE_ARGS *, enum machine_mode,
 static tree mips_build_builtin_va_list (void);
 static tree mips_gimplify_va_arg_expr (tree, tree, tree *, tree *);
 static bool mips_pass_by_reference (CUMULATIVE_ARGS *, enum machine_mode mode,
-                                   tree, bool);
+                                   const_tree, bool);
 static bool mips_callee_copies (CUMULATIVE_ARGS *, enum machine_mode mode,
-                               tree, bool);
+                               const_tree, bool);
 static int mips_arg_partial_bytes (CUMULATIVE_ARGS *, enum machine_mode mode,
                                   tree, bool);
 static bool mips_valid_pointer_mode (enum machine_mode);
+static bool mips_scalar_mode_supported_p (enum machine_mode);
 static bool mips_vector_mode_supported_p (enum machine_mode);
 static rtx mips_prepare_builtin_arg (enum insn_code, unsigned int, tree, unsigned int);
 static rtx mips_prepare_builtin_target (enum insn_code, unsigned int, rtx);
@@ -411,7 +424,14 @@ static rtx mips_expand_builtin_compare (enum mips_builtin_type,
 static rtx mips_expand_builtin_bposge (enum mips_builtin_type, rtx);
 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);
+static void mips_output_dwarf_dtprel (FILE *, int, rtx) ATTRIBUTE_UNUSED;
 
 /* Structure to be filled in by compute_frame_size with register
    save masks, and offsets for the current function.  */
@@ -460,6 +480,10 @@ struct machine_function GTY(()) {
 
   /* True if the function is known to have an instruction that needs $gp.  */
   bool has_gp_insn_p;
+
+  /* True if we have emitted an instruction to initialize
+     mips16_gp_pseudo_rtx.  */
+  bool initialized_mips16_gp_pseudo_p;
 };
 
 /* Information about a single argument.  */
@@ -537,6 +561,18 @@ struct mips_integer_op {
    an extra SLL at the end.  */
 #define MIPS_MAX_INTEGER_OPS 7
 
+/* Information about a MIPS16e SAVE or RESTORE instruction.  */
+struct mips16e_save_restore_info {
+  /* The number of argument registers saved by a SAVE instruction.
+     0 for RESTORE instructions.  */
+  unsigned int nargs;
+
+  /* Bit X is set if the instruction saves or restores GPR X.  */
+  unsigned int mask;
+
+  /* The total number of bytes to allocate.  */
+  HOST_WIDE_INT size;
+};
 
 /* Global variables for machine-dependent things.  */
 
@@ -586,11 +622,21 @@ int mips_abi = MIPS_ABI_DEFAULT;
 /* Cost information to use.  */
 const struct mips_rtx_cost_data *mips_cost;
 
-/* Whether we are generating mips16 hard float code.  In mips16 mode
-   we always set TARGET_SOFT_FLOAT; this variable is nonzero if
-   -msoft-float was not specified by the user, which means that we
-   should arrange to call mips32 hard floating point code.  */
-int mips16_hard_float;
+/* Remember the ambient target flags, excluding mips16.  */
+static int mips_base_target_flags;
+/* The mips16 command-line target flags only.  */
+static bool mips_base_mips16;
+/* Similar copies of option settings.  */
+static int mips_base_schedule_insns; /* flag_schedule_insns */
+static int mips_base_reorder_blocks_and_partition; /* flag_reorder... */
+static int mips_base_move_loop_invariants; /* flag_move_loop_invariants */
+static int mips_base_align_loops; /* align_loops */
+static int mips_base_align_jumps; /* align_jumps */
+static int mips_base_align_functions; /* align_functions */
+static GTY(()) int mips16_flipper;
+
+/* The -mtext-loads setting.  */
+enum mips_code_readable_setting mips_code_readable = CODE_READABLE_YES;
 
 /* The architecture selected by -mipsN.  */
 static const struct mips_cpu_info *mips_isa_info;
@@ -610,6 +656,7 @@ char mips_print_operand_punct[256];
 
 /* Map GCC register number to debugger register number.  */
 int mips_dbx_regno[FIRST_PSEUDO_REGISTER];
+int mips_dwarf_regno[FIRST_PSEUDO_REGISTER];
 
 /* A copy of the original flag_delayed_branch: see override_options.  */
 static int mips_flag_delayed_branch;
@@ -647,7 +694,7 @@ const enum reg_class mips_regno_to_class[] =
   FP_REGS,     FP_REGS,        FP_REGS,        FP_REGS,
   FP_REGS,     FP_REGS,        FP_REGS,        FP_REGS,
   FP_REGS,     FP_REGS,        FP_REGS,        FP_REGS,
-  HI_REG,      LO_REG,         NO_REGS,        ST_REGS,
+  MD0_REG,     MD1_REG,        NO_REGS,        ST_REGS,
   ST_REGS,     ST_REGS,        ST_REGS,        ST_REGS,
   ST_REGS,     ST_REGS,        ST_REGS,        NO_REGS,
   NO_REGS,     ALL_REGS,       ALL_REGS,       NO_REGS,
@@ -684,6 +731,15 @@ const enum reg_class mips_regno_to_class[] =
 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.  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
@@ -691,76 +747,102 @@ const struct attribute_spec mips_attribute_table[] =
    matched in the order listed.  The first mention of an ISA level is
    taken as the canonical name for that ISA.
 
-   To ease comparison, please keep this table in the same order as
-   gas's mips_cpu_info_table[].  */
+   To ease comparison, please keep this table in the same order
+   as gas's mips_cpu_info_table[].  Please also make sure that
+   MIPS_ISA_LEVEL_SPEC and MIPS_ARCH_FLOAT_SPEC handle all -march
+   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 },
+  { "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 },
-  { "24kc", PROCESSOR_24KC, 33 },  /* 24K  no FPU */
-  { "24kf", PROCESSOR_24KF, 33 },  /* 24K 1:2 FPU */
-  { "24kx", PROCESSOR_24KX, 33 },  /* 24K 1:1 FPU */
-  { "24kec", PROCESSOR_24KC, 33 }, /* 24K with DSP */
-  { "24kef", PROCESSOR_24KF, 33 },
-  { "24kex", PROCESSOR_24KX, 33 },
-  { "34kc", PROCESSOR_24KC, 33 },  /* 34K with MT/DSP */
-  { "34kf", PROCESSOR_24KF, 33 },
-  { "34kx", PROCESSOR_24KX, 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
@@ -857,7 +939,17 @@ static struct mips_rtx_cost_data const mips_rtx_cost_data[PROCESSOR_MAX] =
                        4            /* memory_latency */
     },
     { /* 20KC */
-      DEFAULT_COSTS
+      COSTS_N_INSNS (4),            /* fp_add */
+      COSTS_N_INSNS (4),            /* fp_mult_sf */
+      COSTS_N_INSNS (5),            /* fp_mult_df */
+      COSTS_N_INSNS (17),           /* fp_div_sf */
+      COSTS_N_INSNS (32),           /* fp_div_df */
+      COSTS_N_INSNS (4),            /* int_mult_si */
+      COSTS_N_INSNS (7),            /* int_mult_di */
+      COSTS_N_INSNS (42),           /* int_div_si */
+      COSTS_N_INSNS (72),           /* int_div_di */
+                       1,           /* branch_cost */
+                       4            /* memory_latency */
     },
     { /* 24KC */
       SOFT_FP_COSTS,
@@ -868,7 +960,42 @@ static struct mips_rtx_cost_data const mips_rtx_cost_data[PROCESSOR_MAX] =
                        1,           /* branch_cost */
                        4            /* memory_latency */
     },
-    { /* 24KF */
+    { /* 24KF2_1 */
+      COSTS_N_INSNS (8),            /* fp_add */
+      COSTS_N_INSNS (8),            /* fp_mult_sf */
+      COSTS_N_INSNS (10),           /* fp_mult_df */
+      COSTS_N_INSNS (34),           /* fp_div_sf */
+      COSTS_N_INSNS (64),           /* fp_div_df */
+      COSTS_N_INSNS (5),            /* int_mult_si */
+      COSTS_N_INSNS (5),            /* int_mult_di */
+      COSTS_N_INSNS (41),           /* int_div_si */
+      COSTS_N_INSNS (41),           /* int_div_di */
+                       1,           /* branch_cost */
+                       4            /* memory_latency */
+    },
+    { /* 24KF1_1 */
+      COSTS_N_INSNS (4),            /* fp_add */
+      COSTS_N_INSNS (4),            /* fp_mult_sf */
+      COSTS_N_INSNS (5),            /* fp_mult_df */
+      COSTS_N_INSNS (17),           /* fp_div_sf */
+      COSTS_N_INSNS (32),           /* fp_div_df */
+      COSTS_N_INSNS (5),            /* int_mult_si */
+      COSTS_N_INSNS (5),            /* int_mult_di */
+      COSTS_N_INSNS (41),           /* int_div_si */
+      COSTS_N_INSNS (41),           /* int_div_di */
+                       1,           /* branch_cost */
+                       4            /* memory_latency */
+    },
+    { /* 74KC */
+      SOFT_FP_COSTS,
+      COSTS_N_INSNS (5),            /* int_mult_si */
+      COSTS_N_INSNS (5),            /* int_mult_di */
+      COSTS_N_INSNS (41),           /* int_div_si */
+      COSTS_N_INSNS (41),           /* int_div_di */
+                       1,           /* branch_cost */
+                       4            /* memory_latency */
+    },
+    { /* 74KF2_1 */
       COSTS_N_INSNS (8),            /* fp_add */
       COSTS_N_INSNS (8),            /* fp_mult_sf */
       COSTS_N_INSNS (10),           /* fp_mult_df */
@@ -881,7 +1008,7 @@ static struct mips_rtx_cost_data const mips_rtx_cost_data[PROCESSOR_MAX] =
                        1,           /* branch_cost */
                        4            /* memory_latency */
     },
-    { /* 24KX */
+    { /* 74KF1_1 */
       COSTS_N_INSNS (4),            /* fp_add */
       COSTS_N_INSNS (4),            /* fp_mult_sf */
       COSTS_N_INSNS (5),            /* fp_mult_df */
@@ -894,6 +1021,19 @@ static struct mips_rtx_cost_data const mips_rtx_cost_data[PROCESSOR_MAX] =
                        1,           /* branch_cost */
                        4            /* memory_latency */
     },
+    { /* 74KF3_2 */
+      COSTS_N_INSNS (6),            /* fp_add */
+      COSTS_N_INSNS (6),            /* fp_mult_sf */
+      COSTS_N_INSNS (7),            /* fp_mult_df */
+      COSTS_N_INSNS (25),           /* fp_div_sf */
+      COSTS_N_INSNS (48),           /* fp_div_df */
+      COSTS_N_INSNS (5),            /* int_mult_si */
+      COSTS_N_INSNS (5),            /* int_mult_di */
+      COSTS_N_INSNS (41),           /* int_div_si */
+      COSTS_N_INSNS (41),           /* int_div_di */
+                       1,           /* branch_cost */
+                       4            /* memory_latency */
+    },
     { /* M4k */
       DEFAULT_COSTS
     },
@@ -1070,11 +1210,21 @@ static struct mips_rtx_cost_data const mips_rtx_cost_data[PROCESSOR_MAX] =
     },
   };
 
-\f
-/* Nonzero if -march should decide the default value of MASK_SOFT_FLOAT.  */
-#ifndef MIPS_MARCH_CONTROLS_SOFT_FLOAT
-#define MIPS_MARCH_CONTROLS_SOFT_FLOAT 0
-#endif
+/* If a MIPS16e SAVE or RESTORE instruction saves or restores register
+   mips16e_s2_s8_regs[X], it must also save the registers in indexes
+   X + 1 onwards.  Likewise mips16e_a0_a3_regs.  */
+static const unsigned char mips16e_s2_s8_regs[] = {
+  30, 23, 22, 21, 20, 19, 18
+};
+static const unsigned char mips16e_a0_a3_regs[] = {
+  4, 5, 6, 7
+};
+
+/* A list of the registers that can be saved by the MIPS16e SAVE instruction,
+   ordered from the uppermost in memory to the lowest in memory.  */
+static const unsigned char mips16e_save_restore_regs[] = {
+  31, 30, 23, 22, 21, 20, 19, 18, 17, 16, 7, 6, 5, 4
+};
 \f
 /* Initialize the GCC target structure.  */
 #undef TARGET_ASM_ALIGNED_HI_OP
@@ -1093,8 +1243,12 @@ static struct mips_rtx_cost_data const mips_rtx_cost_data[PROCESSOR_MAX] =
 #undef TARGET_ASM_FUNCTION_RODATA_SECTION
 #define TARGET_ASM_FUNCTION_RODATA_SECTION mips_function_rodata_section
 
+#undef TARGET_SCHED_INIT
+#define TARGET_SCHED_INIT mips_sched_init
 #undef TARGET_SCHED_REORDER
 #define TARGET_SCHED_REORDER mips_sched_reorder
+#undef TARGET_SCHED_REORDER2
+#define TARGET_SCHED_REORDER2 mips_sched_reorder
 #undef TARGET_SCHED_VARIABLE_ISSUE
 #define TARGET_SCHED_VARIABLE_ISSUE mips_variable_issue
 #undef TARGET_SCHED_ADJUST_COST
@@ -1119,6 +1273,13 @@ static struct mips_rtx_cost_data const mips_rtx_cost_data[PROCESSOR_MAX] =
 #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
+
 #undef TARGET_VALID_POINTER_MODE
 #define TARGET_VALID_POINTER_MODE mips_valid_pointer_mode
 #undef TARGET_RTX_COSTS
@@ -1146,11 +1307,11 @@ static struct mips_rtx_cost_data const mips_rtx_cost_data[PROCESSOR_MAX] =
 #define TARGET_GIMPLIFY_VA_ARG_EXPR mips_gimplify_va_arg_expr
 
 #undef TARGET_PROMOTE_FUNCTION_ARGS
-#define TARGET_PROMOTE_FUNCTION_ARGS hook_bool_tree_true
+#define TARGET_PROMOTE_FUNCTION_ARGS hook_bool_const_tree_true
 #undef TARGET_PROMOTE_FUNCTION_RETURN
-#define TARGET_PROMOTE_FUNCTION_RETURN hook_bool_tree_true
+#define TARGET_PROMOTE_FUNCTION_RETURN hook_bool_const_tree_true
 #undef TARGET_PROMOTE_PROTOTYPES
-#define TARGET_PROMOTE_PROTOTYPES hook_bool_tree_true
+#define TARGET_PROMOTE_PROTOTYPES hook_bool_const_tree_true
 
 #undef TARGET_RETURN_IN_MEMORY
 #define TARGET_RETURN_IN_MEMORY mips_return_in_memory
@@ -1160,7 +1321,7 @@ static struct mips_rtx_cost_data const mips_rtx_cost_data[PROCESSOR_MAX] =
 #undef TARGET_ASM_OUTPUT_MI_THUNK
 #define TARGET_ASM_OUTPUT_MI_THUNK mips_output_mi_thunk
 #undef TARGET_ASM_CAN_OUTPUT_MI_THUNK
-#define TARGET_ASM_CAN_OUTPUT_MI_THUNK hook_bool_tree_hwi_hwi_tree_true
+#define TARGET_ASM_CAN_OUTPUT_MI_THUNK hook_bool_const_tree_hwi_hwi_const_tree_true
 
 #undef TARGET_SETUP_INCOMING_VARARGS
 #define TARGET_SETUP_INCOMING_VARARGS mips_setup_incoming_varargs
@@ -1181,6 +1342,9 @@ static struct mips_rtx_cost_data const mips_rtx_cost_data[PROCESSOR_MAX] =
 #undef TARGET_VECTOR_MODE_SUPPORTED_P
 #define TARGET_VECTOR_MODE_SUPPORTED_P mips_vector_mode_supported_p
 
+#undef TARGET_SCALAR_MODE_SUPPORTED_P
+#define TARGET_SCALAR_MODE_SUPPORTED_P mips_scalar_mode_supported_p
+
 #undef TARGET_INIT_BUILTINS
 #define TARGET_INIT_BUILTINS mips_init_builtins
 #undef TARGET_EXPAND_BUILTIN
@@ -1197,6 +1361,10 @@ static struct mips_rtx_cost_data const mips_rtx_cost_data[PROCESSOR_MAX] =
 
 #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
@@ -1210,22 +1378,142 @@ static struct mips_rtx_cost_data const mips_rtx_cost_data[PROCESSOR_MAX] =
 #undef TARGET_USE_ANCHORS_FOR_SYMBOL_P
 #define TARGET_USE_ANCHORS_FOR_SYMBOL_P mips_use_anchors_for_symbol_p
 
+#undef  TARGET_COMP_TYPE_ATTRIBUTES
+#define TARGET_COMP_TYPE_ATTRIBUTES mips_comp_type_attributes
+
+#ifdef HAVE_AS_DTPRELWORD
+#undef TARGET_ASM_OUTPUT_DWARF_DTPREL
+#define TARGET_ASM_OUTPUT_DWARF_DTPREL mips_output_dwarf_dtprel
+#endif
+
 struct gcc_target targetm = TARGET_INITIALIZER;
+
+
+/* Predicates to test for presence of "near" and "far"/"long_call"
+   attributes on the given TYPE.  */
+
+static bool
+mips_near_type_p (const_tree type)
+{
+  return lookup_attribute ("near", TYPE_ATTRIBUTES (type)) != NULL;
+}
+
+static bool
+mips_far_type_p (const_tree type)
+{
+  return (lookup_attribute ("long_call", TYPE_ATTRIBUTES (type)) != NULL
+         || lookup_attribute ("far", TYPE_ATTRIBUTES (type)) != NULL);
+}
+
+/* Similar predicates for "mips16"/"nomips16" attributes.  */
+
+static bool
+mips_mips16_decl_p (const_tree decl)
+{
+  return lookup_attribute ("mips16", DECL_ATTRIBUTES (decl)) != NULL;
+}
+
+static bool
+mips_nomips16_decl_p (const_tree decl)
+{
+  return lookup_attribute ("nomips16", DECL_ATTRIBUTES (decl)) != NULL;
+}
+
+/* Return 0 if the attributes for two types are incompatible, 1 if they
+   are compatible, and 2 if they are nearly compatible (which causes a
+   warning to be generated).  */
+
+static int
+mips_comp_type_attributes (const_tree type1, const_tree type2)
+{
+  /* Check for mismatch of non-default calling convention.  */
+  if (TREE_CODE (type1) != FUNCTION_TYPE)
+    return 1;
+
+  /* Disallow mixed near/far attributes.  */
+  if (mips_far_type_p (type1) && mips_near_type_p (type2))
+    return 0;
+  if (mips_near_type_p (type1) && mips_far_type_p (type2))
+    return 0;
+
+  return 1;
+}
+\f
+/* If X is a PLUS of a CONST_INT, return the two terms in *BASE_PTR
+   and *OFFSET_PTR.  Return X in *BASE_PTR and 0 in *OFFSET_PTR otherwise.  */
+
+static void
+mips_split_plus (rtx x, rtx *base_ptr, HOST_WIDE_INT *offset_ptr)
+{
+  if (GET_CODE (x) == PLUS && GET_CODE (XEXP (x, 1)) == CONST_INT)
+    {
+      *base_ptr = XEXP (x, 0);
+      *offset_ptr = INTVAL (XEXP (x, 1));
+    }
+  else
+    {
+      *base_ptr = x;
+      *offset_ptr = 0;
+    }
+}
 \f
-/* Classify symbol X, which must be a SYMBOL_REF or a LABEL_REF.  */
+/* Return true if SYMBOL_REF X is associated with a global symbol
+   (in the STB_GLOBAL sense).  */
+
+static bool
+mips_global_symbol_p (const_rtx x)
+{
+  const_tree const decl = SYMBOL_REF_DECL (x);
+
+  if (!decl)
+    return !SYMBOL_REF_LOCAL_P (x);
+
+  /* Weakref symbols are not TREE_PUBLIC, but their targets are global
+     or weak symbols.  Relocations in the object file will be against
+     the target symbol, so it's that symbol's binding that matters here.  */
+  return DECL_P (decl) && (TREE_PUBLIC (decl) || DECL_WEAK (decl));
+}
+
+/* Return true if SYMBOL_REF X binds locally.  */
+
+static bool
+mips_symbol_binds_local_p (const_rtx x)
+{
+  return (SYMBOL_REF_DECL (x)
+         ? targetm.binds_local_p (SYMBOL_REF_DECL (x))
+         : SYMBOL_REF_LOCAL_P (x));
+}
+
+/* Return true if rtx constants of mode MODE should be put into a small
+   data section.  */
+
+static bool
+mips_rtx_constant_in_small_data_p (enum machine_mode mode)
+{
+  return (!TARGET_EMBEDDED_DATA
+         && TARGET_LOCAL_SDATA
+         && GET_MODE_SIZE (mode) <= mips_section_threshold);
+}
+
+/* Return the method that should be used to access SYMBOL_REF or
+   LABEL_REF X in context CONTEXT.  */
 
 static enum mips_symbol_type
-mips_classify_symbol (rtx x)
+mips_classify_symbol (const_rtx x, enum mips_symbol_context context)
 {
-  tree decl;
+  if (TARGET_RTP_PIC)
+    return SYMBOL_GOT_DISP;
 
   if (GET_CODE (x) == LABEL_REF)
     {
-      if (TARGET_MIPS16)
-       return SYMBOL_CONSTANT_POOL;
+      /* LABEL_REFs are used for jump tables as well as text labels.
+        Only return SYMBOL_PC_RELATIVE if we know the label is in
+        the text section.  */
+      if (TARGET_MIPS16_SHORT_JUMP_TABLES)
+       return SYMBOL_PC_RELATIVE;
       if (TARGET_ABICALLS && !TARGET_ABSOLUTE_ABICALLS)
-       return SYMBOL_GOT_LOCAL;
-      return SYMBOL_GENERAL;
+       return SYMBOL_GOT_PAGE_OFST;
+      return SYMBOL_ABSOLUTE;
     }
 
   gcc_assert (GET_CODE (x) == SYMBOL_REF);
@@ -1235,84 +1523,111 @@ mips_classify_symbol (rtx x)
 
   if (CONSTANT_POOL_ADDRESS_P (x))
     {
-      if (TARGET_MIPS16)
-       return SYMBOL_CONSTANT_POOL;
+      if (TARGET_MIPS16_TEXT_LOADS)
+       return SYMBOL_PC_RELATIVE;
+
+      if (TARGET_MIPS16_PCREL_LOADS && context == SYMBOL_CONTEXT_MEM)
+       return SYMBOL_PC_RELATIVE;
 
-      if (GET_MODE_SIZE (get_pool_mode (x)) <= mips_section_threshold)
-       return SYMBOL_SMALL_DATA;
+      if (mips_rtx_constant_in_small_data_p (get_pool_mode (x)))
+       return SYMBOL_GP_RELATIVE;
     }
 
   /* Do not use small-data accesses for weak symbols; they may end up
      being zero.  */
-  if (SYMBOL_REF_SMALL_P (x)
+  if (TARGET_GPOPT
+      && SYMBOL_REF_SMALL_P (x)
       && !SYMBOL_REF_WEAK (x))
-    return SYMBOL_SMALL_DATA;
+    return SYMBOL_GP_RELATIVE;
 
-  if (TARGET_ABICALLS)
+  /* Don't use GOT accesses for locally-binding symbols when -mno-shared
+     is in effect.  */
+  if (TARGET_ABICALLS
+      && !(TARGET_ABSOLUTE_ABICALLS && mips_symbol_binds_local_p (x)))
     {
-      decl = SYMBOL_REF_DECL (x);
-      if (decl == 0)
-       {
-         if (!SYMBOL_REF_LOCAL_P (x))
-           return SYMBOL_GOT_GLOBAL;
-       }
-      else
-       {
-         /* Don't use GOT accesses for locally-binding symbols if
-            TARGET_ABSOLUTE_ABICALLS.  Otherwise, there are three
-            cases to consider:
-
-               - o32 PIC (either with or without explicit relocs)
-               - n32/n64 PIC without explicit relocs
-               - n32/n64 PIC with explicit relocs
-
-            In the first case, both local and global accesses will use an
-            R_MIPS_GOT16 relocation.  We must correctly predict which of
-            the two semantics (local or global) the assembler and linker
-            will apply.  The choice doesn't depend on the symbol's
-            visibility, so we deliberately ignore decl_visibility and
-            binds_local_p here.
-
-            In the second case, the assembler will not use R_MIPS_GOT16
-            relocations, but it chooses between local and global accesses
-            in the same way as for o32 PIC.
-
-            In the third case we have more freedom since both forms of
-            access will work for any kind of symbol.  However, there seems
-            little point in doing things differently.
-
-            Note that weakref symbols are not TREE_PUBLIC, but their
-            targets are global or weak symbols.  Relocations in the
-            object file will be against the target symbol, so it's
-            that symbol's binding that matters here.  */
-         if (DECL_P (decl)
-             && (TREE_PUBLIC (decl) || DECL_WEAK (decl))
-             && !(TARGET_ABSOLUTE_ABICALLS && targetm.binds_local_p (decl)))
-           return SYMBOL_GOT_GLOBAL;
-       }
+      /* There are three cases to consider:
+
+           - o32 PIC (either with or without explicit relocs)
+           - n32/n64 PIC without explicit relocs
+           - n32/n64 PIC with explicit relocs
+
+        In the first case, both local and global accesses will use an
+        R_MIPS_GOT16 relocation.  We must correctly predict which of
+        the two semantics (local or global) the assembler and linker
+        will apply.  The choice depends on the symbol's binding rather
+        than its visibility.
 
-      if (!TARGET_ABSOLUTE_ABICALLS)
-       return SYMBOL_GOT_LOCAL;
+        In the second case, the assembler will not use R_MIPS_GOT16
+        relocations, but it chooses between local and global accesses
+        in the same way as for o32 PIC.
+
+        In the third case we have more freedom since both forms of
+        access will work for any kind of symbol.  However, there seems
+        little point in doing things differently.  */
+      if (mips_global_symbol_p (x))
+       return SYMBOL_GOT_DISP;
+
+      return SYMBOL_GOT_PAGE_OFST;
     }
 
-  return SYMBOL_GENERAL;
+  if (TARGET_MIPS16_PCREL_LOADS && context != SYMBOL_CONTEXT_CALL)
+    return SYMBOL_FORCE_TO_MEM;
+  return SYMBOL_ABSOLUTE;
+}
+
+/* Classify symbolic expression X, given that it appears in context
+   CONTEXT.  */
+
+static enum mips_symbol_type
+mips_classify_symbolic_expression (rtx x, enum mips_symbol_context context)
+{
+  rtx offset;
+
+  split_const (x, &x, &offset);
+  if (UNSPEC_ADDRESS_P (x))
+    return UNSPEC_ADDRESS_TYPE (x);
+
+  return mips_classify_symbol (x, context);
+}
+
+/* Return true if OFFSET is within the range [0, ALIGN), where ALIGN
+   is the alignment (in bytes) of SYMBOL_REF X.  */
+
+static bool
+mips_offset_within_alignment_p (rtx x, HOST_WIDE_INT offset)
+{
+  /* If for some reason we can't get the alignment for the
+     symbol, initializing this to one means we will only accept
+     a zero offset.  */
+  HOST_WIDE_INT align = 1;
+  tree t;
+
+  /* Get the alignment of the symbol we're referring to.  */
+  t = SYMBOL_REF_DECL (x);
+  if (t)
+    align = DECL_ALIGN_UNIT (t);
+
+  return offset >= 0 && offset < align;
 }
 
-/* Return true if X is a symbolic constant that can be calculated in
-   the same way as a bare symbol.  If it is, store the type of the
-   symbol in *SYMBOL_TYPE.  */
+/* Return true if X is a symbolic constant that can be used in context
+   CONTEXT.  If it is, store the type of the symbol in *SYMBOL_TYPE.  */
 
 bool
-mips_symbolic_constant_p (rtx x, enum mips_symbol_type *symbol_type)
+mips_symbolic_constant_p (rtx x, enum mips_symbol_context context,
+                         enum mips_symbol_type *symbol_type)
 {
   rtx offset;
 
   split_const (x, &x, &offset);
   if (UNSPEC_ADDRESS_P (x))
-    *symbol_type = UNSPEC_ADDRESS_TYPE (x);
+    {
+      *symbol_type = UNSPEC_ADDRESS_TYPE (x);
+      x = UNSPEC_ADDRESS (x);
+    }
   else if (GET_CODE (x) == SYMBOL_REF || GET_CODE (x) == LABEL_REF)
     {
-      *symbol_type = mips_classify_symbol (x);
+      *symbol_type = mips_classify_symbol (x, context);
       if (*symbol_type == SYMBOL_TLS)
        return false;
     }
@@ -1326,7 +1641,9 @@ mips_symbolic_constant_p (rtx x, enum mips_symbol_type *symbol_type)
      relocations.  */
   switch (*symbol_type)
     {
-    case SYMBOL_GENERAL:
+    case SYMBOL_ABSOLUTE:
+    case SYMBOL_FORCE_TO_MEM:
+    case SYMBOL_32_HIGH:
     case SYMBOL_64_HIGH:
     case SYMBOL_64_MID:
     case SYMBOL_64_LOW:
@@ -1340,7 +1657,7 @@ mips_symbolic_constant_p (rtx x, enum mips_symbol_type *symbol_type)
       /* In other cases the relocations can handle any offset.  */
       return true;
 
-    case SYMBOL_CONSTANT_POOL:
+    case SYMBOL_PC_RELATIVE:
       /* Allow constant pool references to be converted to LABEL+CONSTANT.
         In this case, we no longer have access to the underlying constant,
         but the original symbol-based access was known to be valid.  */
@@ -1349,28 +1666,36 @@ mips_symbolic_constant_p (rtx x, enum mips_symbol_type *symbol_type)
 
       /* Fall through.  */
 
-    case SYMBOL_SMALL_DATA:
+    case SYMBOL_GP_RELATIVE:
       /* Make sure that the offset refers to something within the
         same object block.  This should guarantee that the final
         PC- or GP-relative offset is within the 16-bit limit.  */
       return offset_within_block_p (x, INTVAL (offset));
 
-    case SYMBOL_GOT_LOCAL:
+    case SYMBOL_GOT_PAGE_OFST:
     case SYMBOL_GOTOFF_PAGE:
-      /* The linker should provide enough local GOT entries for a
-        16-bit offset.  Larger offsets may lead to GOT overflow.  */
+      /* If the symbol is global, the GOT entry will contain the symbol's
+        address, and we will apply a 16-bit offset after loading it.
+        If the symbol is local, the linker should provide enough local
+        GOT entries for a 16-bit offset, but larger offsets may lead
+        to GOT overflow.  */
       return SMALL_INT (offset);
 
-    case SYMBOL_GOT_GLOBAL:
-    case SYMBOL_GOTOFF_GLOBAL:
+    case SYMBOL_TPREL:
+    case SYMBOL_DTPREL:
+      /* There is no carry between the HI and LO REL relocations, so the
+        offset is only valid if we know it won't lead to such a carry.  */
+      return mips_offset_within_alignment_p (x, INTVAL (offset));
+
+    case SYMBOL_GOT_DISP:
+    case SYMBOL_GOTOFF_DISP:
     case SYMBOL_GOTOFF_CALL:
     case SYMBOL_GOTOFF_LOADGP:
     case SYMBOL_TLSGD:
     case SYMBOL_TLSLDM:
-    case SYMBOL_DTPREL:
-    case SYMBOL_TPREL:
     case SYMBOL_GOTTPREL:
     case SYMBOL_TLS:
+    case SYMBOL_HALF:
       return false;
     }
   gcc_unreachable ();
@@ -1431,51 +1756,6 @@ mips_valid_base_register_p (rtx x, enum machine_mode mode, int strict)
 }
 
 
-/* Return true if symbols of type SYMBOL_TYPE can directly address a value
-   with mode MODE.  This is used for both symbolic and LO_SUM addresses.  */
-
-static bool
-mips_symbolic_address_p (enum mips_symbol_type symbol_type,
-                        enum machine_mode mode)
-{
-  switch (symbol_type)
-    {
-    case SYMBOL_GENERAL:
-      return !TARGET_MIPS16;
-
-    case SYMBOL_SMALL_DATA:
-      return true;
-
-    case SYMBOL_CONSTANT_POOL:
-      /* PC-relative addressing is only available for lw and ld.  */
-      return GET_MODE_SIZE (mode) == 4 || GET_MODE_SIZE (mode) == 8;
-
-    case SYMBOL_GOT_LOCAL:
-      return true;
-
-    case SYMBOL_GOT_GLOBAL:
-      /* The address will have to be loaded from the GOT first.  */
-      return false;
-
-    case SYMBOL_GOTOFF_PAGE:
-    case SYMBOL_GOTOFF_GLOBAL:
-    case SYMBOL_GOTOFF_CALL:
-    case SYMBOL_GOTOFF_LOADGP:
-    case SYMBOL_TLS:
-    case SYMBOL_TLSGD:
-    case SYMBOL_TLSLDM:
-    case SYMBOL_DTPREL:
-    case SYMBOL_GOTTPREL:
-    case SYMBOL_TPREL:
-    case SYMBOL_64_HIGH:
-    case SYMBOL_64_MID:
-    case SYMBOL_64_LOW:
-      return true;
-    }
-  gcc_unreachable ();
-}
-
-
 /* Return true if X is a valid address for machine mode MODE.  If it is,
    fill in INFO appropriately.  STRICT is true if we should only accept
    hard base registers.  */
@@ -1504,9 +1784,19 @@ mips_classify_address (struct mips_address_info *info, rtx x,
       info->type = ADDRESS_LO_SUM;
       info->reg = XEXP (x, 0);
       info->offset = XEXP (x, 1);
+      /* We have to trust the creator of the LO_SUM to do something vaguely
+        sane.  Target-independent code that creates a LO_SUM should also
+        create and verify the matching HIGH.  Target-independent code that
+        adds an offset to a LO_SUM must prove that the offset will not
+        induce a carry.  Failure to do either of these things would be
+        a bug, and we are not required to check for it here.  The MIPS
+        backend itself should only create LO_SUMs for valid symbolic
+        constants, with the high part being either a HIGH or a copy
+        of _gp. */
+      info->symbol_type
+       = mips_classify_symbolic_expression (info->offset, SYMBOL_CONTEXT_MEM);
       return (mips_valid_base_register_p (info->reg, mode, strict)
-             && mips_symbolic_constant_p (info->offset, &info->symbol_type)
-             && mips_symbolic_address_p (info->symbol_type, mode)
+             && mips_symbol_insns (info->symbol_type, mode) > 0
              && mips_lo_relocs[info->symbol_type] != 0);
 
     case CONST_INT:
@@ -1519,8 +1809,9 @@ mips_classify_address (struct mips_address_info *info, rtx x,
     case LABEL_REF:
     case SYMBOL_REF:
       info->type = ADDRESS_SYMBOLIC;
-      return (mips_symbolic_constant_p (x, &info->symbol_type)
-             && mips_symbolic_address_p (info->symbol_type, mode)
+      return (mips_symbolic_constant_p (x, SYMBOL_CONTEXT_MEM,
+                                       &info->symbol_type)
+             && mips_symbol_insns (info->symbol_type, mode) > 0
              && !mips_split_p[info->symbol_type]);
 
     default:
@@ -1576,32 +1867,27 @@ mips_cannot_force_const_mem (rtx x)
   return false;
 }
 
-/* Implement TARGET_USE_BLOCKS_FOR_CONSTANT_P.  MIPS16 uses per-function
-   constant pools, but normal-mode code doesn't need to.  */
+/* Implement TARGET_USE_BLOCKS_FOR_CONSTANT_P.  We can't use blocks for
+   constants when we're using a per-function constant pool.  */
 
 static bool
 mips_use_blocks_for_constant_p (enum machine_mode mode ATTRIBUTE_UNUSED,
-                               rtx x ATTRIBUTE_UNUSED)
+                               const_rtx x ATTRIBUTE_UNUSED)
 {
-  return !TARGET_MIPS16;
+  return !TARGET_MIPS16_PCREL_LOADS;
 }
 \f
-/* Return the number of instructions needed to load a symbol of the
-   given type into a register.  If valid in an address, the same number
-   of instructions are needed for loads and stores.  Treat extended
-   mips16 instructions as two instructions.  */
+/* Like mips_symbol_insns, but treat extended MIPS16 instructions as a
+   single instruction.  We rely on the fact that, in the worst case,
+   all instructions involved in a MIPS16 address calculation are usually
+   extended ones.  */
 
 static int
-mips_symbol_insns (enum mips_symbol_type type)
+mips_symbol_insns_1 (enum mips_symbol_type type, enum machine_mode mode)
 {
   switch (type)
     {
-    case SYMBOL_GENERAL:
-      /* In mips16 code, general symbols must be fetched from the
-        constant pool.  */
-      if (TARGET_MIPS16)
-       return 0;
-
+    case SYMBOL_ABSOLUTE:
       /* When using 64-bit symbols, we need 5 preparatory instructions,
         such as:
 
@@ -1612,19 +1898,44 @@ mips_symbol_insns (enum mips_symbol_type type)
             dsll    $at,$at,16
 
         The final address is then $at + %lo(symbol).  With 32-bit
-        symbols we just need a preparatory lui.  */
-      return (ABI_HAS_64BIT_SYMBOLS ? 6 : 2);
+        symbols we just need a preparatory lui for normal mode and
+        a preparatory "li; sll" for MIPS16.  */
+      return ABI_HAS_64BIT_SYMBOLS ? 6 : TARGET_MIPS16 ? 3 : 2;
 
-    case SYMBOL_SMALL_DATA:
+    case SYMBOL_GP_RELATIVE:
+      /* Treat GP-relative accesses as taking a single instruction on
+        MIPS16 too; the copy of $gp can often be shared.  */
       return 1;
 
-    case SYMBOL_CONSTANT_POOL:
-      /* This case is for mips16 only.  Assume we'll need an
-        extended instruction.  */
-      return 2;
+    case SYMBOL_PC_RELATIVE:
+      /* PC-relative constants can be only be used with addiupc,
+        lwpc and ldpc.  */
+      if (mode == MAX_MACHINE_MODE
+         || GET_MODE_SIZE (mode) == 4
+         || GET_MODE_SIZE (mode) == 8)
+       return 1;
+
+      /* The constant must be loaded using addiupc first.  */
+      return 0;
+
+    case SYMBOL_FORCE_TO_MEM:
+      /* LEAs will be converted into constant-pool references by
+        mips_reorg.  */
+      if (mode == MAX_MACHINE_MODE)
+       return 1;
+
+      /* The constant must be loaded from the constant pool.  */
+      return 0;
+
+    case SYMBOL_GOT_DISP:
+      /* The constant will have to be loaded from the GOT before it
+        is used in an address.  */
+      if (mode != MAX_MACHINE_MODE)
+       return 0;
+
+      /* Fall through.  */
 
-    case SYMBOL_GOT_LOCAL:
-    case SYMBOL_GOT_GLOBAL:
+    case SYMBOL_GOT_PAGE_OFST:
       /* Unless -funit-at-a-time is in effect, we can't be sure whether
         the local/global classification is accurate.  See override_options
         for details.
@@ -1648,9 +1959,10 @@ mips_symbol_insns (enum mips_symbol_type type)
       return 3;
 
     case SYMBOL_GOTOFF_PAGE:
-    case SYMBOL_GOTOFF_GLOBAL:
+    case SYMBOL_GOTOFF_DISP:
     case SYMBOL_GOTOFF_CALL:
     case SYMBOL_GOTOFF_LOADGP:
+    case SYMBOL_32_HIGH:
     case SYMBOL_64_HIGH:
     case SYMBOL_64_MID:
     case SYMBOL_64_LOW:
@@ -1659,8 +1971,11 @@ mips_symbol_insns (enum mips_symbol_type type)
     case SYMBOL_DTPREL:
     case SYMBOL_GOTTPREL:
     case SYMBOL_TPREL:
-      /* Check whether the offset is a 16- or 32-bit value.  */
-      return mips_split_p[type] ? 2 : 1;
+    case SYMBOL_HALF:
+      /* A 16-bit constant formed by a single relocation, or a 32-bit
+        constant formed from a high 16-bit relocation and a low 16-bit
+        relocation.  Use mips_split_p to determine which.  */
+      return !mips_split_p[type] ? 1 : TARGET_MIPS16 ? 3 : 2;
 
     case SYMBOL_TLS:
       /* We don't treat a bare TLS symbol as a constant.  */
@@ -1669,6 +1984,22 @@ mips_symbol_insns (enum mips_symbol_type type)
   gcc_unreachable ();
 }
 
+/* If MODE is MAX_MACHINE_MODE, return the number of instructions needed
+   to load symbols of type TYPE into a register.  Return 0 if the given
+   type of symbol cannot be used as an immediate operand.
+
+   Otherwise, return the number of instructions needed to load or store
+   values of mode MODE to or from addresses of type TYPE.  Return 0 if
+   the given type of symbol is not valid in addresses.
+
+   In both cases, treat extended MIPS16 instructions as two instructions.  */
+
+static int
+mips_symbol_insns (enum mips_symbol_type type, enum machine_mode mode)
+{
+  return mips_symbol_insns_1 (type, mode) * (TARGET_MIPS16 ? 2 : 1);
+}
+
 /* Return true if X is a legitimate $sp-based address for mode MDOE.  */
 
 bool
@@ -1707,22 +2038,26 @@ mips16_unextended_reference_p (enum machine_mode mode, rtx base, rtx offset)
 
 
 /* Return the number of instructions needed to load or store a value
-   of mode MODE at X.  Return 0 if X isn't valid for MODE.
+   of mode MODE at X.  Return 0 if X isn't valid for MODE.  Assume that
+   multiword moves may need to be split into word moves if MIGHT_SPLIT_P,
+   otherwise assume that a single load or store is enough.
 
    For mips16 code, count extended instructions as two instructions.  */
 
 int
-mips_address_insns (rtx x, enum machine_mode mode)
+mips_address_insns (rtx x, enum machine_mode mode, bool might_split_p)
 {
   struct mips_address_info addr;
   int factor;
 
-  if (mode == BLKmode)
-    /* BLKmode is used for single unaligned loads and stores.  */
-    factor = 1;
-  else
-    /* Each word of a multi-word value will be accessed individually.  */
+  /* BLKmode is used for single unaligned loads and stores and should
+     not count as a multiword mode.  (GET_MODE_SIZE (BLKmode) is pretty
+     meaningless, so we have to single it out as a special case one way
+     or the other.)  */
+  if (mode != BLKmode && might_split_p)
     factor = (GET_MODE_SIZE (mode) + UNITS_PER_WORD - 1) / UNITS_PER_WORD;
+  else
+    factor = 1;
 
   if (mips_classify_address (&addr, x, mode, false))
     switch (addr.type)
@@ -1740,7 +2075,7 @@ mips_address_insns (rtx x, enum machine_mode mode)
        return factor;
 
       case ADDRESS_SYMBOLIC:
-       return factor * mips_symbol_insns (addr.symbol_type);
+       return factor * mips_symbol_insns (addr.symbol_type, mode);
       }
   return 0;
 }
@@ -1758,12 +2093,14 @@ mips_const_insns (rtx x)
   switch (GET_CODE (x))
     {
     case HIGH:
-      if (TARGET_MIPS16
-         || !mips_symbolic_constant_p (XEXP (x, 0), &symbol_type)
+      if (!mips_symbolic_constant_p (XEXP (x, 0), SYMBOL_CONTEXT_LEA,
+                                    &symbol_type)
          || !mips_split_p[symbol_type])
        return 0;
 
-      return 1;
+      /* This is simply an lui for normal mode.  It is an extended
+        "li" followed by an extended "sll" for MIPS16.  */
+      return TARGET_MIPS16 ? 4 : 1;
 
     case CONST_INT:
       if (TARGET_MIPS16)
@@ -1788,8 +2125,8 @@ mips_const_insns (rtx x)
        return 1;
 
       /* See if we can refer to X directly.  */
-      if (mips_symbolic_constant_p (x, &symbol_type))
-       return mips_symbol_insns (symbol_type);
+      if (mips_symbolic_constant_p (x, SYMBOL_CONTEXT_LEA, &symbol_type))
+       return mips_symbol_insns (symbol_type, MAX_MACHINE_MODE);
 
       /* Otherwise try splitting the constant into a base and offset.
         16-bit offsets can be added using an extra addiu.  Larger offsets
@@ -1810,7 +2147,8 @@ mips_const_insns (rtx x)
 
     case SYMBOL_REF:
     case LABEL_REF:
-      return mips_symbol_insns (mips_classify_symbol (x));
+      return mips_symbol_insns (mips_classify_symbol (x, SYMBOL_CONTEXT_LEA),
+                               MAX_MACHINE_MODE);
 
     default:
       return 0;
@@ -1818,14 +2156,30 @@ mips_const_insns (rtx x)
 }
 
 
-/* Return the number of instructions needed for memory reference X.
-   Count extended mips16 instructions as two instructions.  */
+/* Return the number of instructions needed to implement INSN,
+   given that it loads from or stores to MEM.  Count extended
+   mips16 instructions as two instructions.  */
 
 int
-mips_fetch_insns (rtx x)
+mips_load_store_insns (rtx mem, rtx insn)
 {
-  gcc_assert (MEM_P (x));
-  return mips_address_insns (XEXP (x, 0), GET_MODE (x));
+  enum machine_mode mode;
+  bool might_split_p;
+  rtx set;
+
+  gcc_assert (MEM_P (mem));
+  mode = GET_MODE (mem);
+
+  /* Try to prove that INSN does not need to be split.  */
+  might_split_p = true;
+  if (GET_MODE_BITSIZE (mode) == 64)
+    {
+      set = single_set (insn);
+      if (set && !mips_split_64bit_move_p (SET_DEST (set), SET_SRC (set)))
+       might_split_p = false;
+    }
+
+  return mips_address_insns (XEXP (mem, 0), mode, might_split_p);
 }
 
 
@@ -1863,6 +2217,19 @@ mips_legitimate_address_p (enum machine_mode mode, rtx x, int strict)
   return mips_classify_address (&addr, x, mode, strict);
 }
 
+/* Emit a move from SRC to DEST.  Assume that the move expanders can
+   handle all moves if !can_create_pseudo_p ().  The distinction is
+   important because, unlike emit_move_insn, the move expanders know
+   how to force Pmode objects into the constant pool even when the
+   constant pool address is not itself legitimate.  */
+
+rtx
+mips_emit_move (rtx dest, rtx src)
+{
+  return (can_create_pseudo_p ()
+         ? emit_move_insn (dest, src)
+         : emit_move_insn_1 (dest, src));
+}
 
 /* Copy VALUE to a register and return that register.  If new psuedos
    are allowed, copy it into a new register, otherwise use DEST.  */
@@ -1870,41 +2237,70 @@ mips_legitimate_address_p (enum machine_mode mode, rtx x, int strict)
 static rtx
 mips_force_temporary (rtx dest, rtx value)
 {
-  if (!no_new_pseudos)
+  if (can_create_pseudo_p ())
     return force_reg (Pmode, value);
   else
     {
-      emit_move_insn (copy_rtx (dest), value);
+      mips_emit_move (copy_rtx (dest), value);
       return dest;
     }
 }
 
 
-/* Return a LO_SUM expression for ADDR.  TEMP is as for mips_force_temporary
-   and is used to load the high part into a register.  */
+/* If MODE is MAX_MACHINE_MODE, ADDR appears as a move operand, otherwise
+   it appears in a MEM of that mode.  Return true if ADDR is a legitimate
+   constant in that context and can be split into a high part and a LO_SUM.
+   If so, and if LO_SUM_OUT is nonnull, emit the high part and return
+   the LO_SUM in *LO_SUM_OUT.  Leave *LO_SUM_OUT unchanged otherwise.
 
-rtx
-mips_split_symbol (rtx temp, rtx addr)
+   TEMP is as for mips_force_temporary and is used to load the high
+   part into a register.  */
+
+bool
+mips_split_symbol (rtx temp, rtx addr, enum machine_mode mode, rtx *lo_sum_out)
 {
+  enum mips_symbol_context context;
+  enum mips_symbol_type symbol_type;
   rtx high;
 
-  if (TARGET_MIPS16)
-    high = mips16_gp_pseudo_reg ();
-  else
-    high = mips_force_temporary (temp, gen_rtx_HIGH (Pmode, copy_rtx (addr)));
-  return gen_rtx_LO_SUM (Pmode, high, addr);
+  context = (mode == MAX_MACHINE_MODE
+            ? SYMBOL_CONTEXT_LEA
+            : SYMBOL_CONTEXT_MEM);
+  if (!mips_symbolic_constant_p (addr, context, &symbol_type)
+      || mips_symbol_insns (symbol_type, mode) == 0
+      || !mips_split_p[symbol_type])
+    return false;
+
+  if (lo_sum_out)
+    {
+      if (symbol_type == SYMBOL_GP_RELATIVE)
+       {
+         if (!can_create_pseudo_p ())
+           {
+             emit_insn (gen_load_const_gp (copy_rtx (temp)));
+             high = temp;
+           }
+         else
+           high = mips16_gp_pseudo_reg ();
+       }
+      else
+       {
+         high = gen_rtx_HIGH (Pmode, copy_rtx (addr));
+         high = mips_force_temporary (temp, high);
+       }
+      *lo_sum_out = gen_rtx_LO_SUM (Pmode, high, addr);
+    }
+  return true;
 }
 
 
-/* Return an UNSPEC address with underlying address ADDRESS and symbol
-   type SYMBOL_TYPE.  */
+/* Wrap symbol or label BASE in an unspec address of type SYMBOL_TYPE
+   and add CONST_INT OFFSET to the result.  */
 
-rtx
-mips_unspec_address (rtx address, enum mips_symbol_type symbol_type)
+static rtx
+mips_unspec_address_offset (rtx base, rtx offset,
+                           enum mips_symbol_type symbol_type)
 {
-  rtx base, offset;
-
-  split_const (address, &base, &offset);
   base = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, base),
                         UNSPEC_ADDRESS_FIRST + symbol_type);
   if (offset != const0_rtx)
@@ -1912,6 +2308,18 @@ mips_unspec_address (rtx address, enum mips_symbol_type symbol_type)
   return gen_rtx_CONST (Pmode, base);
 }
 
+/* Return an UNSPEC address with underlying address ADDRESS and symbol
+   type SYMBOL_TYPE.  */
+
+rtx
+mips_unspec_address (rtx address, enum mips_symbol_type symbol_type)
+{
+  rtx base, offset;
+
+  split_const (address, &base, &offset);
+  return mips_unspec_address_offset (base, offset, symbol_type);
+}
+
 
 /* If mips_unspec_address (ADDR, SYMBOL_TYPE) is a 32-bit value, add the
    high part to BASE and return the result.  Just return BASE otherwise.
@@ -2007,6 +2415,12 @@ mips_legitimize_tls_address (rtx loc)
   rtx dest, insn, v0, v1, tmp1, tmp2, eqv;
   enum tls_model model;
 
+  if (TARGET_MIPS16)
+    {
+      sorry ("MIPS16 TLS");
+      return gen_reg_rtx (Pmode);
+    }
+
   v0 = gen_rtx_REG (Pmode, GP_RETURN);
   v1 = gen_rtx_REG (Pmode, GP_RETURN + 1);
 
@@ -2083,8 +2497,6 @@ mips_legitimize_tls_address (rtx loc)
 bool
 mips_legitimize_address (rtx *xloc, enum machine_mode mode)
 {
-  enum mips_symbol_type symbol_type;
-
   if (mips_tls_operand_p (*xloc))
     {
       *xloc = mips_legitimize_tls_address (*xloc);
@@ -2092,13 +2504,8 @@ mips_legitimize_address (rtx *xloc, enum machine_mode mode)
     }
 
   /* See if the address can split into a high part and a LO_SUM.  */
-  if (mips_symbolic_constant_p (*xloc, &symbol_type)
-      && mips_symbolic_address_p (symbol_type, mode)
-      && mips_split_p[symbol_type])
-    {
-      *xloc = mips_split_symbol (0, *xloc);
-      return true;
-    }
+  if (mips_split_symbol (NULL, *xloc, mode, xloc))
+    return true;
 
   if (GET_CODE (*xloc) == PLUS && GET_CODE (XEXP (*xloc, 1)) == CONST_INT)
     {
@@ -2236,7 +2643,7 @@ mips_move_integer (rtx dest, rtx temp, unsigned HOST_WIDE_INT value)
   x = GEN_INT (codes[0].value);
   for (i = 1; i < cost; i++)
     {
-      if (no_new_pseudos)
+      if (!can_create_pseudo_p ())
        {
          emit_insn (gen_rtx_SET (VOIDmode, temp, x));
          x = temp;
@@ -2267,28 +2674,29 @@ mips_legitimize_const_move (enum machine_mode mode, rtx dest, rtx src)
     }
 
   /* Split moves of symbolic constants into high/low pairs.  */
-  if (splittable_symbolic_operand (src, mode))
+  if (mips_split_symbol (dest, src, MAX_MACHINE_MODE, &src))
     {
-      emit_insn (gen_rtx_SET (VOIDmode, dest, mips_split_symbol (dest, src)));
+      emit_insn (gen_rtx_SET (VOIDmode, dest, src));
       return;
     }
 
   if (mips_tls_operand_p (src))
     {
-      emit_move_insn (dest, mips_legitimize_tls_address (src));
+      mips_emit_move (dest, mips_legitimize_tls_address (src));
       return;
     }
 
-  /* If we have (const (plus symbol offset)), load the symbol first
-     and then add in the offset.  This is usually better than forcing
-     the constant into memory, at least in non-mips16 code.  */
+  /* If we have (const (plus symbol offset)), and that expression cannot
+     be forced into memory, load the symbol first and add in the offset.
+     In non-MIPS16 mode, prefer to do this even if the constant _can_ be
+     forced into memory, as it usually produces better code.  */
   split_const (src, &base, &offset);
-  if (!TARGET_MIPS16
-      && offset != const0_rtx
-      && (!no_new_pseudos || SMALL_INT (offset)))
+  if (offset != const0_rtx
+      && (targetm.cannot_force_const_mem (src)
+         || (!TARGET_MIPS16 && can_create_pseudo_p ())))
     {
       base = mips_force_temporary (dest, base);
-      emit_move_insn (dest, mips_add_offset (0, base, INTVAL (offset)));
+      mips_emit_move (dest, mips_add_offset (0, base, INTVAL (offset)));
       return;
     }
 
@@ -2296,9 +2704,8 @@ mips_legitimize_const_move (enum machine_mode mode, rtx dest, rtx src)
 
   /* When using explicit relocs, constant pool references are sometimes
      not legitimate addresses.  */
-  if (!memory_operand (src, VOIDmode))
-    src = replace_equiv_address (src, mips_split_symbol (dest, XEXP (src, 0)));
-  emit_move_insn (dest, src);
+  mips_split_symbol (dest, XEXP (src, 0), mode, &XEXP (src, 0));
+  mips_emit_move (dest, src);
 }
 
 
@@ -2310,7 +2717,7 @@ mips_legitimize_move (enum machine_mode mode, rtx dest, rtx src)
 {
   if (!register_operand (dest, mode) && !reg_or_0_operand (src, mode))
     {
-      emit_move_insn (dest, force_reg (mode, src));
+      mips_emit_move (dest, force_reg (mode, src));
       return true;
     }
 
@@ -2452,239 +2859,602 @@ m16_nsimm8_8 (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
   return m16_check_op (op, (- 0x7f) << 3, 0x80 << 3, 7);
 }
 \f
+/* Return true if ADDR matches the pattern for the lwxs load scaled indexed
+   address instruction.  */
+
+static bool
+mips_lwxs_address_p (rtx addr)
+{
+  if (ISA_HAS_LWXS
+      && GET_CODE (addr) == PLUS
+      && REG_P (XEXP (addr, 1)))
+    {
+      rtx offset = XEXP (addr, 0);
+      if (GET_CODE (offset) == MULT
+         && REG_P (XEXP (offset, 0))
+         && GET_CODE (XEXP (offset, 1)) == CONST_INT
+         && INTVAL (XEXP (offset, 1)) == 4)
+       return true;
+    }
+  return false;
+}
+
+/* The cost of loading values from the constant pool.  It should be
+   larger than the cost of any constant we want to synthesize inline.  */
+
+#define CONSTANT_POOL_COST COSTS_N_INSNS (TARGET_MIPS16 ? 4 : 8)
+
+/* Return the cost of X when used as an operand to the MIPS16 instruction
+   that implements CODE.  Return -1 if there is no such instruction, or if
+   X is not a valid immediate operand for it.  */
+
+static int
+mips16_constant_cost (int code, HOST_WIDE_INT x)
+{
+  switch (code)
+    {
+    case ASHIFT:
+    case ASHIFTRT:
+    case LSHIFTRT:
+      /* Shifts by between 1 and 8 bits (inclusive) are unextended,
+        other shifts are extended.  The shift patterns truncate the shift
+        count to the right size, so there are no out-of-range values.  */
+      if (IN_RANGE (x, 1, 8))
+       return 0;
+      return COSTS_N_INSNS (1);
+
+    case PLUS:
+      if (IN_RANGE (x, -128, 127))
+       return 0;
+      if (SMALL_OPERAND (x))
+       return COSTS_N_INSNS (1);
+      return -1;
+
+    case LEU:
+      /* Like LE, but reject the always-true case.  */
+      if (x == -1)
+       return -1;
+    case LE:
+      /* We add 1 to the immediate and use SLT.  */
+      x += 1;
+    case XOR:
+      /* We can use CMPI for an xor with an unsigned 16-bit X.  */
+    case LT:
+    case LTU:
+      if (IN_RANGE (x, 0, 255))
+       return 0;
+      if (SMALL_OPERAND_UNSIGNED (x))
+       return COSTS_N_INSNS (1);
+      return -1;
+
+    case EQ:
+    case NE:
+      /* Equality comparisons with 0 are cheap.  */
+      if (x == 0)
+       return 0;
+      return -1;
+
+    default:
+      return -1;
+    }
+}
+
+/* Return true if there is a non-MIPS16 instruction that implements CODE
+   and if that instruction accepts X as an immediate operand.  */
+
+static int
+mips_immediate_operand_p (int code, HOST_WIDE_INT x)
+{
+  switch (code)
+    {
+    case ASHIFT:
+    case ASHIFTRT:
+    case LSHIFTRT:
+      /* All shift counts are truncated to a valid constant.  */
+      return true;
+
+    case ROTATE:
+    case ROTATERT:
+      /* Likewise rotates, if the target supports rotates at all.  */
+      return ISA_HAS_ROR;
+
+    case AND:
+    case IOR:
+    case XOR:
+      /* These instructions take 16-bit unsigned immediates.  */
+      return SMALL_OPERAND_UNSIGNED (x);
+
+    case PLUS:
+    case LT:
+    case LTU:
+      /* These instructions take 16-bit signed immediates.  */
+      return SMALL_OPERAND (x);
+
+    case EQ:
+    case NE:
+    case GT:
+    case GTU:
+      /* The "immediate" forms of these instructions are really
+        implemented as comparisons with register 0.  */
+      return x == 0;
+
+    case GE:
+    case GEU:
+      /* Likewise, meaning that the only valid immediate operand is 1.  */
+      return x == 1;
+
+    case LE:
+      /* We add 1 to the immediate and use SLT.  */
+      return SMALL_OPERAND (x + 1);
+
+    case LEU:
+      /* Likewise SLTU, but reject the always-true case.  */
+      return SMALL_OPERAND (x + 1) && x + 1 != 0;
+
+    case SIGN_EXTRACT:
+    case ZERO_EXTRACT:
+      /* The bit position and size are immediate operands.  */
+      return ISA_HAS_EXT_INS;
+
+    default:
+      /* By default assume that $0 can be used for 0.  */
+      return x == 0;
+    }
+}
+
+/* Return the cost of binary operation X, given that the instruction
+   sequence for a word-sized or smaller operation has cost SINGLE_COST
+   and that the sequence of a double-word operation has cost DOUBLE_COST.  */
+
+static int
+mips_binary_cost (rtx x, int single_cost, int double_cost)
+{
+  int cost;
+
+  if (GET_MODE_SIZE (GET_MODE (x)) == UNITS_PER_WORD * 2)
+    cost = double_cost;
+  else
+    cost = single_cost;
+  return (cost
+         + rtx_cost (XEXP (x, 0), 0)
+         + rtx_cost (XEXP (x, 1), GET_CODE (x)));
+}
+
+/* Return the cost of floating-point multiplications of mode MODE.  */
+
+static int
+mips_fp_mult_cost (enum machine_mode mode)
+{
+  return mode == DFmode ? mips_cost->fp_mult_df : mips_cost->fp_mult_sf;
+}
+
+/* Return the cost of floating-point divisions of mode MODE.  */
+
+static int
+mips_fp_div_cost (enum machine_mode mode)
+{
+  return mode == DFmode ? mips_cost->fp_div_df : mips_cost->fp_div_sf;
+}
+
+/* Return the cost of sign-extending OP to mode MODE, not including the
+   cost of OP itself.  */
+
+static int
+mips_sign_extend_cost (enum machine_mode mode, rtx op)
+{
+  if (MEM_P (op))
+    /* Extended loads are as cheap as unextended ones.  */
+    return 0;
+
+  if (TARGET_64BIT && mode == DImode && GET_MODE (op) == SImode)
+    /* A sign extension from SImode to DImode in 64-bit mode is free.  */
+    return 0;
+
+  if (ISA_HAS_SEB_SEH || GENERATE_MIPS16E)
+    /* We can use SEB or SEH.  */
+    return COSTS_N_INSNS (1);
+
+  /* We need to use a shift left and a shift right.  */
+  return COSTS_N_INSNS (TARGET_MIPS16 ? 4 : 2);
+}
+
+/* Return the cost of zero-extending OP to mode MODE, not including the
+   cost of OP itself.  */
+
+static int
+mips_zero_extend_cost (enum machine_mode mode, rtx op)
+{
+  if (MEM_P (op))
+    /* Extended loads are as cheap as unextended ones.  */
+    return 0;
+
+  if (TARGET_64BIT && mode == DImode && GET_MODE (op) == SImode)
+    /* We need a shift left by 32 bits and a shift right by 32 bits.  */
+    return COSTS_N_INSNS (TARGET_MIPS16 ? 4 : 2);
+
+  if (GENERATE_MIPS16E)
+    /* We can use ZEB or ZEH.  */
+    return COSTS_N_INSNS (1);
+
+  if (TARGET_MIPS16)
+    /* We need to load 0xff or 0xffff into a register and use AND.  */
+    return COSTS_N_INSNS (GET_MODE (op) == QImode ? 2 : 3);
+
+  /* We can use ANDI.  */
+  return COSTS_N_INSNS (1);
+}
+
+/* Implement TARGET_RTX_COSTS.  */
+
 static bool
 mips_rtx_costs (rtx x, int code, int outer_code, int *total)
 {
   enum machine_mode mode = GET_MODE (x);
   bool float_mode_p = FLOAT_MODE_P (mode);
+  int cost;
+  rtx addr;
+
+  /* The cost of a COMPARE is hard to define for MIPS.  COMPAREs don't
+     appear in the instruction stream, and the cost of a comparison is
+     really the cost of the branch or scc condition.  At the time of
+     writing, gcc only uses an explicit outer COMPARE code when optabs
+     is testing whether a constant is expensive enough to force into a
+     register.  We want optabs to pass such constants through the MIPS
+     expanders instead, so make all constants very cheap here.  */
+  if (outer_code == COMPARE)
+    {
+      gcc_assert (CONSTANT_P (x));
+      *total = 0;
+      return true;
+    }
 
   switch (code)
     {
     case CONST_INT:
-      if (TARGET_MIPS16)
-        {
-         /* A number between 1 and 8 inclusive is efficient for a shift.
-            Otherwise, we will need an extended instruction.  */
-         if ((outer_code) == ASHIFT || (outer_code) == ASHIFTRT
-             || (outer_code) == LSHIFTRT)
-           {
-             if (INTVAL (x) >= 1 && INTVAL (x) <= 8)
-               *total = 0;
-             else
-               *total = COSTS_N_INSNS (1);
-             return true;
-           }
-
-         /* We can use cmpi for an xor with an unsigned 16-bit value.  */
-         if ((outer_code) == XOR
-             && INTVAL (x) >= 0 && INTVAL (x) < 0x10000)
-           {
-             *total = 0;
-             return true;
-           }
-
-         /* We may be able to use slt or sltu for a comparison with a
-            signed 16-bit value.  (The boundary conditions aren't quite
-            right, but this is just a heuristic anyhow.)  */
-         if (((outer_code) == LT || (outer_code) == LE
-              || (outer_code) == GE || (outer_code) == GT
-              || (outer_code) == LTU || (outer_code) == LEU
-              || (outer_code) == GEU || (outer_code) == GTU)
-             && INTVAL (x) >= -0x8000 && INTVAL (x) < 0x8000)
-           {
-             *total = 0;
-             return true;
-           }
+      /* Treat *clear_upper32-style ANDs as having zero cost in the
+        second operand.  The cost is entirely in the first operand.
+
+        ??? This is needed because we would otherwise try to CSE
+        the constant operand.  Although that's the right thing for
+        instructions that continue to be a register operation throughout
+        compilation, it is disastrous for instructions that could
+        later be converted into a memory operation.  */
+      if (TARGET_64BIT
+         && outer_code == AND
+         && UINTVAL (x) == 0xffffffff)
+       {
+         *total = 0;
+         return true;
+       }
 
-         /* Equality comparisons with 0 are cheap.  */
-         if (((outer_code) == EQ || (outer_code) == NE)
-             && INTVAL (x) == 0)
+      if (TARGET_MIPS16)
+       {
+         cost = mips16_constant_cost (outer_code, INTVAL (x));
+         if (cost >= 0)
            {
-             *total = 0;
+             *total = cost;
              return true;
            }
-
-         /* Constants in the range 0...255 can be loaded with an unextended
-            instruction.  They are therefore as cheap as a register move.
-
-            Given the choice between "li R1,0...255" and "move R1,R2"
-            (where R2 is a known constant), it is usually better to use "li",
-            since we do not want to unnecessarily extend the lifetime
-            of R2.  */
-         if (outer_code == SET
-             && INTVAL (x) >= 0
-             && INTVAL (x) < 256)
+       }
+      else
+       {
+         /* When not optimizing for size, we care more about the cost
+            of hot code, and hot code is often in a loop.  If a constant
+            operand needs to be forced into a register, we will often be
+            able to hoist the constant load out of the loop, so the load
+            should not contribute to the cost.  */
+         if (!optimize_size
+             || mips_immediate_operand_p (outer_code, INTVAL (x)))
            {
              *total = 0;
              return true;
            }
        }
-      else
-       {
-         /* These can be used anywhere. */
-         *total = 0;
-         return true;
-       }
-
-      /* Otherwise fall through to the handling below because
-        we'll need to construct the constant.  */
+      /* Fall through.  */
 
     case CONST:
     case SYMBOL_REF:
     case LABEL_REF:
     case CONST_DOUBLE:
-      if (LEGITIMATE_CONSTANT_P (x))
+      if (force_to_mem_operand (x, VOIDmode))
        {
          *total = COSTS_N_INSNS (1);
          return true;
        }
-      else
+      cost = mips_const_insns (x);
+      if (cost > 0)
        {
-         /* The value will need to be fetched from the constant pool.  */
-         *total = CONSTANT_POOL_COST;
+         /* If the constant is likely to be stored in a GPR, SETs of
+            single-insn constants are as cheap as register sets; we
+            never want to CSE them.
+
+            Don't reduce the cost of storing a floating-point zero in
+            FPRs.  If we have a zero in an FPR for other reasons, we
+            can get better cfg-cleanup and delayed-branch results by
+            using it consistently, rather than using $0 sometimes and
+            an FPR at other times.  Also, moves between floating-point
+            registers are sometimes cheaper than (D)MTC1 $0.  */
+         if (cost == 1
+             && outer_code == SET
+             && !(float_mode_p && TARGET_HARD_FLOAT))
+           cost = 0;
+         /* When non-MIPS16 code loads a constant N>1 times, we rarely
+            want to CSE the constant itself.  It is usually better to
+            have N copies of the last operation in the sequence and one
+            shared copy of the other operations.  (Note that this is
+            not true for MIPS16 code, where the final operation in the
+            sequence is often an extended instruction.)
+
+            Also, if we have a CONST_INT, we don't know whether it is
+            for a word or doubleword operation, so we cannot rely on
+            the result of mips_build_integer.  */
+         else if (!TARGET_MIPS16
+                  && (outer_code == SET || mode == VOIDmode))
+           cost = 1;
+         *total = COSTS_N_INSNS (cost);
          return true;
        }
+      /* The value will need to be fetched from the constant pool.  */
+      *total = CONSTANT_POOL_COST;
+      return true;
 
     case MEM:
-      {
-       /* If the address is legitimate, return the number of
-          instructions it needs, otherwise use the default handling.  */
-       int n = mips_address_insns (XEXP (x, 0), GET_MODE (x));
-       if (n > 0)
-         {
-           *total = COSTS_N_INSNS (n + 1);
-           return true;
-         }
-       return false;
-      }
+      /* If the address is legitimate, return the number of
+        instructions it needs.  */
+      addr = XEXP (x, 0);
+      cost = mips_address_insns (addr, mode, true);
+      if (cost > 0)
+       {
+         *total = COSTS_N_INSNS (cost + 1);
+         return true;
+       }
+      /* Check for a scaled indexed address.  */
+      if (mips_lwxs_address_p (addr))
+       {
+         *total = COSTS_N_INSNS (2);
+         return true;
+       }
+      /* Otherwise use the default handling.  */
+      return false;
 
     case FFS:
       *total = COSTS_N_INSNS (6);
-      return true;
+      return false;
 
     case NOT:
-      *total = COSTS_N_INSNS ((mode == DImode && !TARGET_64BIT) ? 2 : 1);
-      return true;
+      *total = COSTS_N_INSNS (GET_MODE_SIZE (mode) > UNITS_PER_WORD ? 2 : 1);
+      return false;
 
     case AND:
+      /* Check for a *clear_upper32 pattern and treat it like a zero
+        extension.  See the pattern's comment for details.  */
+      if (TARGET_64BIT
+         && mode == DImode
+         && CONST_INT_P (XEXP (x, 1))
+         && UINTVAL (XEXP (x, 1)) == 0xffffffff)
+       {
+         *total = (mips_zero_extend_cost (mode, XEXP (x, 0))
+                   + rtx_cost (XEXP (x, 0), 0));
+         return true;
+       }
+      /* Fall through.  */
+
     case IOR:
     case XOR:
-      if (mode == DImode && !TARGET_64BIT)
-        {
-          *total = COSTS_N_INSNS (2);
-          return true;
-        }
-      return false;
+      /* Double-word operations use two single-word operations.  */
+      *total = mips_binary_cost (x, COSTS_N_INSNS (1), COSTS_N_INSNS (2));
+      return true;
 
     case ASHIFT:
     case ASHIFTRT:
     case LSHIFTRT:
-      if (mode == DImode && !TARGET_64BIT)
-        {
-          *total = COSTS_N_INSNS ((GET_CODE (XEXP (x, 1)) == CONST_INT)
-                                  ? 4 : 12);
-          return true;
-        }
-      return false;
+    case ROTATE:
+    case ROTATERT:
+      if (CONSTANT_P (XEXP (x, 1)))
+       *total = mips_binary_cost (x, COSTS_N_INSNS (1), COSTS_N_INSNS (4));
+      else
+       *total = mips_binary_cost (x, COSTS_N_INSNS (1), COSTS_N_INSNS (12));
+      return true;
 
     case ABS:
       if (float_mode_p)
-        *total = COSTS_N_INSNS (1);
+        *total = mips_cost->fp_add;
       else
         *total = COSTS_N_INSNS (4);
-      return true;
+      return false;
 
     case LO_SUM:
-      *total = COSTS_N_INSNS (1);
+      /* Low-part immediates need an extended MIPS16 instruction.  */
+      *total = (COSTS_N_INSNS (TARGET_MIPS16 ? 2 : 1)
+               + rtx_cost (XEXP (x, 0), 0));
+      return true;
+
+    case LT:
+    case LTU:
+    case LE:
+    case LEU:
+    case GT:
+    case GTU:
+    case GE:
+    case GEU:
+    case EQ:
+    case NE:
+    case UNORDERED:
+    case LTGT:
+      /* Branch comparisons have VOIDmode, so use the first operand's
+        mode instead.  */
+      mode = GET_MODE (XEXP (x, 0));
+      if (FLOAT_MODE_P (mode))
+       {
+         *total = mips_cost->fp_add;
+         return false;
+       }
+      *total = mips_binary_cost (x, COSTS_N_INSNS (1), COSTS_N_INSNS (4));
       return true;
 
-    case PLUS:
     case MINUS:
+      if (float_mode_p
+         && ISA_HAS_NMADD_NMSUB
+         && TARGET_FUSED_MADD
+         && !HONOR_NANS (mode)
+         && !HONOR_SIGNED_ZEROS (mode))
+       {
+         /* See if we can use NMADD or NMSUB.  See mips.md for the
+            associated patterns.  */
+         rtx op0 = XEXP (x, 0);
+         rtx op1 = XEXP (x, 1);
+         if (GET_CODE (op0) == MULT && GET_CODE (XEXP (op0, 0)) == NEG)
+           {
+             *total = (mips_fp_mult_cost (mode)
+                       + rtx_cost (XEXP (XEXP (op0, 0), 0), 0)
+                       + rtx_cost (XEXP (op0, 1), 0)
+                       + rtx_cost (op1, 0));
+             return true;
+           }
+         if (GET_CODE (op1) == MULT)
+           {
+             *total = (mips_fp_mult_cost (mode)
+                       + rtx_cost (op0, 0)
+                       + rtx_cost (XEXP (op1, 0), 0)
+                       + rtx_cost (XEXP (op1, 1), 0));
+             return true;
+           }
+       }
+      /* Fall through.  */
+
+    case PLUS:
       if (float_mode_p)
        {
-         *total = mips_cost->fp_add;
-         return true;
+         if (ISA_HAS_FP4
+             && TARGET_FUSED_MADD
+             && GET_CODE (XEXP (x, 0)) == MULT)
+           *total = 0;
+         else
+           *total = mips_cost->fp_add;
+         return false;
        }
 
-      else if (mode == DImode && !TARGET_64BIT)
-        {
-          *total = COSTS_N_INSNS (4);
-          return true;
-        }
-      return false;
+      /* Double-word operations require three single-word operations and
+        an SLTU.  The MIPS16 version then needs to move the result of
+        the SLTU from $24 to a MIPS16 register.  */
+      *total = mips_binary_cost (x, COSTS_N_INSNS (1),
+                                COSTS_N_INSNS (TARGET_MIPS16 ? 5 : 4));
+      return true;
 
     case NEG:
-      if (mode == DImode && !TARGET_64BIT)
-        {
-          *total = COSTS_N_INSNS (4);
-          return true;
-        }
+      if (float_mode_p
+         && ISA_HAS_NMADD_NMSUB
+         && TARGET_FUSED_MADD
+         && !HONOR_NANS (mode)
+         && HONOR_SIGNED_ZEROS (mode))
+       {
+         /* See if we can use NMADD or NMSUB.  See mips.md for the
+            associated patterns.  */
+         rtx op = XEXP (x, 0);
+         if ((GET_CODE (op) == PLUS || GET_CODE (op) == MINUS)
+             && GET_CODE (XEXP (op, 0)) == MULT)
+           {
+             *total = (mips_fp_mult_cost (mode)
+                       + rtx_cost (XEXP (XEXP (op, 0), 0), 0)
+                       + rtx_cost (XEXP (XEXP (op, 0), 1), 0)
+                       + rtx_cost (XEXP (op, 1), 0));
+             return true;
+           }
+       }
+
+      if (float_mode_p)
+       *total = mips_cost->fp_add;
+      else
+       *total = COSTS_N_INSNS (GET_MODE_SIZE (mode) > UNITS_PER_WORD ? 4 : 1);
       return false;
 
     case MULT:
-      if (mode == SFmode)
-       *total = mips_cost->fp_mult_sf;
-
-      else if (mode == DFmode)
-       *total = mips_cost->fp_mult_df;
-
-      else if (mode == SImode)
-       *total = mips_cost->int_mult_si;
-
-      else
+      if (float_mode_p)
+       *total = mips_fp_mult_cost (mode);
+      else if (mode == DImode && !TARGET_64BIT)
+       /* Synthesized from 2 mulsi3s, 1 mulsidi3 and two additions,
+          where the mulsidi3 always includes an MFHI and an MFLO.  */
+       *total = (optimize_size
+                 ? COSTS_N_INSNS (ISA_HAS_MUL3 ? 7 : 9)
+                 : mips_cost->int_mult_si * 3 + 6);
+      else if (optimize_size)
+       *total = (ISA_HAS_MUL3 ? 1 : 2);
+      else if (mode == DImode)
        *total = mips_cost->int_mult_di;
-
-      return true;
+      else
+       *total = mips_cost->int_mult_si;
+      return false;
 
     case DIV:
+      /* Check for a reciprocal.  */
+      if (float_mode_p && XEXP (x, 0) == CONST1_RTX (mode))
+       {
+         if (ISA_HAS_FP4
+             && flag_unsafe_math_optimizations
+             && (outer_code == SQRT || GET_CODE (XEXP (x, 1)) == SQRT))
+           {
+             /* An rsqrt<mode>a or rsqrt<mode>b pattern.  Count the
+                division as being free.  */
+             *total = rtx_cost (XEXP (x, 1), 0);
+             return true;
+           }
+         if (!ISA_MIPS1)
+           {
+             *total = mips_fp_div_cost (mode) + rtx_cost (XEXP (x, 1), 0);
+             return true;
+           }
+       }
+      /* Fall through.  */
+
+    case SQRT:
     case MOD:
       if (float_mode_p)
        {
-         if (mode == SFmode)
-           *total = mips_cost->fp_div_sf;
-         else
-           *total = mips_cost->fp_div_df;
-
-         return true;
+         *total = mips_fp_div_cost (mode);
+         return false;
        }
       /* Fall through.  */
 
     case UDIV:
     case UMOD:
-      if (mode == DImode)
+      if (optimize_size)
+       {
+         /* It is our responsibility to make division by a power of 2
+            as cheap as 2 register additions if we want the division
+            expanders to be used for such operations; see the setting
+            of sdiv_pow2_cheap in optabs.c.  Using (D)DIV for MIPS16
+            should always produce shorter code than using
+            expand_sdiv2_pow2.  */
+         if (TARGET_MIPS16
+             && CONST_INT_P (XEXP (x, 1))
+             && exact_log2 (INTVAL (XEXP (x, 1))) >= 0)
+           {
+             *total = COSTS_N_INSNS (2) + rtx_cost (XEXP (x, 0), 0);
+             return true;
+           }
+         *total = COSTS_N_INSNS (mips_idiv_insns ());
+       }
+      else if (mode == DImode)
         *total = mips_cost->int_div_di;
       else
        *total = mips_cost->int_div_si;
-
-      return true;
+      return false;
 
     case SIGN_EXTEND:
-      /* A sign extend from SImode to DImode in 64-bit mode is often
-         zero instructions, because the result can often be used
-         directly by another instruction; we'll call it one.  */
-      if (TARGET_64BIT && mode == DImode
-          && GET_MODE (XEXP (x, 0)) == SImode)
-        *total = COSTS_N_INSNS (1);
-      else
-        *total = COSTS_N_INSNS (2);
-      return true;
+      *total = mips_sign_extend_cost (mode, XEXP (x, 0));
+      return false;
 
     case ZERO_EXTEND:
-      if (TARGET_64BIT && mode == DImode
-          && GET_MODE (XEXP (x, 0)) == SImode)
-        *total = COSTS_N_INSNS (2);
-      else
-        *total = COSTS_N_INSNS (1);
-      return true;
+      *total = mips_zero_extend_cost (mode, XEXP (x, 0));
+      return false;
 
     case FLOAT:
     case UNSIGNED_FLOAT:
     case FIX:
     case FLOAT_EXTEND:
     case FLOAT_TRUNCATE:
-    case SQRT:
       *total = mips_cost->fp_add;
-      return true;
+      return false;
 
     default:
       return false;
@@ -2697,7 +3467,7 @@ mips_rtx_costs (rtx x, int code, int outer_code, int *total)
 static int
 mips_address_cost (rtx addr)
 {
-  return mips_address_insns (addr, SImode);
+  return mips_address_insns (addr, SImode, false);
 }
 \f
 /* Return one word of double-word value OP, taking into account the fixed
@@ -2707,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);
@@ -2719,12 +3489,11 @@ mips_subword (rtx op, int high_p)
   else
     byte = 0;
 
-  if (REG_P (op))
+  if (FP_REG_RTX_P (op))
     {
-      if (FP_REG_P (REGNO (op)))
-       return gen_rtx_REG (word_mode, high_p ? REGNO (op) + 1 : REGNO (op));
-      if (ACC_HI_REG_P (REGNO (op)))
-       return gen_rtx_REG (word_mode, high_p ? REGNO (op) : REGNO (op) + 1);
+      /* 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))
@@ -2759,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);
-         emit_move_insn (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
-       {
-         emit_move_insn (mips_subword (dest, 0), mips_subword (src, 0));
-         emit_insn (gen_store_df_high (mips_subword (dest, 1), src));
-       }
+       gcc_unreachable ();
     }
   else
     {
@@ -2820,13 +3556,13 @@ mips_split_64bit_move (rtx dest, rtx src)
       if (REG_P (low_dest)
          && reg_overlap_mentioned_p (low_dest, src))
        {
-         emit_move_insn (mips_subword (dest, 1), mips_subword (src, 1));
-         emit_move_insn (low_dest, mips_subword (src, 0));
+         mips_emit_move (mips_subword (dest, 1), mips_subword (src, 1));
+         mips_emit_move (low_dest, mips_subword (src, 0));
        }
       else
        {
-         emit_move_insn (low_dest, mips_subword (src, 0));
-         emit_move_insn (mips_subword (dest, 1), mips_subword (src, 1));
+         mips_emit_move (low_dest, mips_subword (src, 0));
+         mips_emit_move (mips_subword (dest, 1), mips_subword (src, 1));
        }
     }
 }
@@ -2838,6 +3574,7 @@ const char *
 mips_output_move (rtx dest, rtx src)
 {
   enum rtx_code dest_code, src_code;
+  enum mips_symbol_type symbol_type;
   bool dbl_p;
 
   dest_code = GET_CODE (dest);
@@ -2925,13 +3662,27 @@ mips_output_move (rtx dest, rtx src)
        }
 
       if (src_code == HIGH)
-       return "lui\t%0,%h1";
+       return TARGET_MIPS16 ? "#" : "lui\t%0,%h1";
 
       if (CONST_GP_P (src))
        return "move\t%0,%1";
 
+      if (mips_symbolic_constant_p (src, SYMBOL_CONTEXT_LEA, &symbol_type)
+         && mips_lo_relocs[symbol_type] != 0)
+       {
+         /* A signed 16-bit constant formed by applying a relocation
+            operator to a symbolic address.  */
+         gcc_assert (!mips_split_p[symbol_type]);
+         return "li\t%0,%R1";
+       }
+
       if (symbolic_operand (src, VOIDmode))
-       return (dbl_p ? "dla\t%0,%1" : "la\t%0,%1");
+       {
+         gcc_assert (TARGET_MIPS16
+                     ? TARGET_MIPS16_TEXT_LOADS
+                     : !TARGET_EXPLICIT_RELOCS);
+         return (dbl_p ? "dla\t%0,%1" : "la\t%0,%1");
+       }
     }
   if (src_code == REG && FP_REG_P (REGNO (src)))
     {
@@ -2987,7 +3738,7 @@ mips_restore_gp (void)
                             current_function_outgoing_args_size);
   slot = gen_rtx_MEM (Pmode, address);
 
-  emit_move_insn (pic_offset_table_rtx, slot);
+  mips_emit_move (pic_offset_table_rtx, slot);
   if (!TARGET_EXPLICIT_RELOCS)
     emit_insn (gen_blockage ());
 }
@@ -3201,6 +3952,13 @@ mips_emit_compare (enum rtx_code *code, rtx *op0, rtx *op1, bool need_eq_ne_p)
          *code = (invert ? EQ : NE);
        }
     }
+  else if (ALL_FIXED_POINT_MODE_P (GET_MODE (cmp_operands[0])))
+    {
+      *op0 = gen_rtx_REG (CCDSPmode, CCDSP_CC_REGNUM);
+      mips_emit_binary (*code, *op0, cmp_operands[0], cmp_operands[1]);
+      *code = NE;
+      *op1 = const0_rtx;
+    }
   else
     {
       enum rtx_code cmp_code;
@@ -3341,6 +4099,37 @@ 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
+mips_ok_for_lazy_binding_p (rtx x)
+{
+  return (TARGET_USE_GOT
+         && GET_CODE (x) == SYMBOL_REF
+         && !mips_symbol_binds_local_p (x));
+}
+
 /* Load function address ADDR into register DEST.  SIBCALL_P is true
    if the address is needed for a sibling call.  */
 
@@ -3349,11 +4138,11 @@ mips_load_call_address (rtx dest, rtx addr, int sibcall_p)
 {
   /* If we're generating PIC, and this call is to a global function,
      try to allow its address to be resolved lazily.  This isn't
-     possible for NewABI sibcalls since the value of $gp on entry
+     possible if TARGET_CALL_SAVED_GP since the value of $gp on entry
      to the stub would be our caller's gp, not ours.  */
   if (TARGET_EXPLICIT_RELOCS
-      && !(sibcall_p && TARGET_NEWABI)
-      && global_got_operand (addr, VOIDmode))
+      && !(sibcall_p && TARGET_CALL_SAVED_GP)
+      && mips_ok_for_lazy_binding_p (addr))
     {
       rtx high, lo_sum_symbol;
 
@@ -3366,7 +4155,7 @@ mips_load_call_address (rtx dest, rtx addr, int sibcall_p)
        emit_insn (gen_load_calldi (dest, high, lo_sum_symbol));
     }
   else
-    emit_move_insn (dest, addr);
+    mips_emit_move (dest, addr);
 }
 
 
@@ -3390,7 +4179,7 @@ mips_expand_call (rtx result, rtx addr, rtx args_size, rtx aux, int sibcall_p)
     }
 
   if (TARGET_MIPS16
-      && mips16_hard_float
+      && TARGET_HARD_FLOAT_ABI
       && build_mips16_call_stub (result, addr, args_size,
                                 aux == 0 ? 0 : (int) GET_MODE (aux)))
     return;
@@ -3418,18 +4207,38 @@ mips_expand_call (rtx result, rtx addr, rtx args_size, rtx aux, int sibcall_p)
   insn = emit_call_insn (pattern);
 
   /* Lazy-binding stubs require $gp to be valid on entry.  */
-  if (global_got_operand (orig_addr, VOIDmode))
+  if (mips_ok_for_lazy_binding_p (orig_addr))
     use_reg (&CALL_INSN_FUNCTION_USAGE (insn), pic_offset_table_rtx);
 }
 
 
-/* We can handle any sibcall when TARGET_SIBCALLS is true.  */
+/* Implement TARGET_FUNCTION_OK_FOR_SIBCALL.  */
 
 static bool
-mips_function_ok_for_sibcall (tree decl ATTRIBUTE_UNUSED,
-                             tree exp ATTRIBUTE_UNUSED)
+mips_function_ok_for_sibcall (tree decl, tree exp ATTRIBUTE_UNUSED)
 {
-  return TARGET_SIBCALLS;
+  if (!TARGET_SIBCALLS)
+    return false;
+
+  /* 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 (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.  */
+  return true;
 }
 \f
 /* Emit code to move general operand SRC into condition-code
@@ -3455,10 +4264,10 @@ mips_emit_fcc_reload (rtx dest, rtx src, rtx scratch)
     src = gen_rtx_REG (SFmode, true_regnum (src));
 
   fp1 = gen_rtx_REG (SFmode, REGNO (scratch));
-  fp2 = gen_rtx_REG (SFmode, REGNO (scratch) + FP_INC);
+  fp2 = gen_rtx_REG (SFmode, REGNO (scratch) + MAX_FPRS_PER_FMT);
 
-  emit_move_insn (copy_rtx (fp1), src);
-  emit_move_insn (copy_rtx (fp2), CONST0_RTX (SFmode));
+  mips_emit_move (copy_rtx (fp1), src);
+  mips_emit_move (copy_rtx (fp2), CONST0_RTX (SFmode));
   emit_insn (gen_slt_sf (dest, fp2, fp1));
 }
 \f
@@ -3476,7 +4285,7 @@ mips_set_return_address (rtx address, rtx scratch)
   slot_address = mips_add_offset (scratch, stack_pointer_rtx,
                                  cfun->machine->frame.gp_sp_offset);
 
-  emit_move_insn (gen_rtx_MEM (GET_MODE (address), slot_address), address);
+  mips_emit_move (gen_rtx_MEM (GET_MODE (address), slot_address), address);
 }
 \f
 /* Emit straight-line code to move LENGTH bytes from SRC to DEST.
@@ -3514,7 +4323,7 @@ mips_block_move_straight (rtx dest, rtx src, HOST_WIDE_INT length)
     {
       regs[i] = gen_reg_rtx (mode);
       if (MEM_ALIGN (src) >= bits)
-       emit_move_insn (regs[i], adjust_address (src, mode, offset));
+       mips_emit_move (regs[i], adjust_address (src, mode, offset));
       else
        {
          rtx part = adjust_address (src, BLKmode, offset);
@@ -3526,7 +4335,7 @@ mips_block_move_straight (rtx dest, rtx src, HOST_WIDE_INT length)
   /* Copy the chunks to the destination.  */
   for (offset = 0, i = 0; offset + delta <= length; offset += delta, i++)
     if (MEM_ALIGN (dest) >= bits)
-      emit_move_insn (adjust_address (dest, mode, offset), regs[i]);
+      mips_emit_move (adjust_address (dest, mode, offset), regs[i]);
     else
       {
        rtx part = adjust_address (dest, BLKmode, offset);
@@ -3599,8 +4408,8 @@ mips_block_move_loop (rtx dest, rtx src, HOST_WIDE_INT length)
   mips_block_move_straight (dest, src, MAX_MOVE_BYTES);
 
   /* Move on to the next block.  */
-  emit_move_insn (src_reg, plus_constant (src_reg, MAX_MOVE_BYTES));
-  emit_move_insn (dest_reg, plus_constant (dest_reg, MAX_MOVE_BYTES));
+  mips_emit_move (src_reg, plus_constant (src_reg, MAX_MOVE_BYTES));
+  mips_emit_move (dest_reg, plus_constant (dest_reg, MAX_MOVE_BYTES));
 
   /* Emit the loop condition.  */
   if (Pmode == DImode)
@@ -3614,6 +4423,33 @@ mips_block_move_loop (rtx dest, rtx src, HOST_WIDE_INT length)
     mips_block_move_straight (dest, src, leftover);
 }
 \f
+
+/* Expand a loop of synci insns for the address range [BEGIN, END).  */
+
+void
+mips_expand_synci_loop (rtx begin, rtx end)
+{
+  rtx inc, label, cmp, cmp_result;
+
+  /* Load INC with the cache line size (rdhwr INC,$1). */
+  inc = gen_reg_rtx (SImode);
+  emit_insn (gen_rdhwr (inc, const1_rtx));
+
+  /* Loop back to here.  */
+  label = gen_label_rtx ();
+  emit_label (label);
+
+  emit_insn (gen_synci (begin));
+
+  cmp = gen_reg_rtx (Pmode);
+  mips_emit_binary (GTU, cmp, begin, end);
+
+  mips_emit_binary (PLUS, begin, begin, inc);
+
+  cmp_result = gen_rtx_EQ (VOIDmode, cmp, const0_rtx);
+  emit_jump_insn (gen_condjump (cmp_result, label));
+}
+\f
 /* Expand a movmemsi instruction.  */
 
 bool
@@ -3777,6 +4613,24 @@ mips_arg_info (const CUMULATIVE_ARGS *cum, enum machine_mode mode,
 }
 
 
+/* INFO describes an argument that is passed in a single-register value.
+   Return the register it uses, assuming that FPRs are available if
+   HARD_FLOAT_P.  */
+
+static unsigned int
+mips_arg_regno (const struct mips_arg_info *info, bool hard_float_p)
+{
+  if (!info->fpr_p || !hard_float_p)
+    return GP_ARG_FIRST + info->reg_offset;
+  else if (mips_abi == ABI_32 && TARGET_DOUBLE_FLOAT && info->reg_offset > 0)
+    /* In o32, the second argument is always passed in $f14
+       for TARGET_DOUBLE_FLOAT, regardless of whether the
+       first argument was a word or doubleword.  */
+    return FP_ARG_FIRST + 2;
+  else
+    return FP_ARG_FIRST + info->reg_offset;
+}
+
 /* Implement FUNCTION_ARG_ADVANCE.  */
 
 void
@@ -3794,12 +4648,12 @@ function_arg_advance (CUMULATIVE_ARGS *cum, enum machine_mode mode,
      for an explanation of what this code does.  It assumes the O32
      ABI, which passes at most 2 arguments in float registers.  */
   if (cum->arg_number < 2 && info.fpr_p)
-    cum->fp_code += (mode == SFmode ? 1 : 2) << ((cum->arg_number - 1) * 2);
+    cum->fp_code += (mode == SFmode ? 1 : 2) << (cum->arg_number * 2);
 
   if (mips_abi != ABI_EABI || !info.fpr_p)
     cum->num_gprs = info.reg_offset + info.reg_words;
   else if (info.reg_words > 0)
-    cum->num_fprs += FP_INC;
+    cum->num_fprs += MAX_FPRS_PER_FMT;
 
   if (info.stack_words > 0)
     cum->stack_words = info.stack_offset + info.stack_words;
@@ -3931,14 +4785,7 @@ function_arg (const CUMULATIVE_ARGS *cum, enum machine_mode mode,
        }
     }
 
-  if (!info.fpr_p)
-    return gen_rtx_REG (mode, GP_ARG_FIRST + info.reg_offset);
-  else if (info.reg_offset == 1)
-    /* This code handles the special o32 case in which the second word
-       of the argument structure is passed in floating-point registers.  */
-    return gen_rtx_REG (mode, FP_ARG_FIRST + FP_INC);
-  else
-    return gen_rtx_REG (mode, FP_ARG_FIRST + info.reg_offset);
+  return gen_rtx_REG (mode, mips_arg_regno (&info, TARGET_HARD_FLOAT));
 }
 
 
@@ -3978,7 +4825,7 @@ function_arg_boundary (enum machine_mode mode, tree type)
    byte does.  */
 
 bool
-mips_pad_arg_upward (enum machine_mode mode, tree type)
+mips_pad_arg_upward (enum machine_mode mode, const_tree type)
 {
   /* On little-endian targets, the first byte of every stack argument
      is passed in the first byte of the stack slot.  */
@@ -3988,8 +4835,11 @@ mips_pad_arg_upward (enum machine_mode mode, tree type)
   /* Otherwise, integral types are padded downward: the last byte of a
      stack argument is passed in the last byte of the stack slot.  */
   if (type != 0
-      ? INTEGRAL_TYPE_P (type) || POINTER_TYPE_P (type)
-      : GET_MODE_CLASS (mode) == MODE_INT)
+      ? (INTEGRAL_TYPE_P (type)
+        || POINTER_TYPE_P (type)
+        || FIXED_POINT_TYPE_P (type))
+      : (GET_MODE_CLASS (mode) == MODE_INT
+        || ALL_SCALAR_FIXED_POINT_MODE_P (mode)))
     return false;
 
   /* Big-endian o64 pads floating-point arguments downward.  */
@@ -4077,14 +4927,15 @@ mips_setup_incoming_varargs (CUMULATIVE_ARGS *cum, enum machine_mode mode,
 
          mode = TARGET_SINGLE_FLOAT ? SFmode : DFmode;
 
-         for (i = local_cum.num_fprs; i < MAX_ARGS_IN_REGISTERS; i += FP_INC)
+         for (i = local_cum.num_fprs; i < MAX_ARGS_IN_REGISTERS;
+              i += MAX_FPRS_PER_FMT)
            {
              rtx ptr, mem;
 
              ptr = plus_constant (virtual_incoming_args_rtx, off);
              mem = gen_rtx_MEM (mode, ptr);
              set_mem_alias_set (mem, get_varargs_alias_set ());
-             emit_move_insn (mem, gen_rtx_REG (mode, FP_ARG_FIRST + i));
+             mips_emit_move (mem, gen_rtx_REG (mode, FP_ARG_FIRST + i));
              off += UNITS_PER_HWFPVALUE;
            }
        }
@@ -4209,9 +5060,8 @@ mips_va_start (tree valist, rtx nextarg)
         words used by named arguments.  */
       t = make_tree (TREE_TYPE (ovfl), virtual_incoming_args_rtx);
       if (cum->stack_words > 0)
-       t = build2 (PLUS_EXPR, TREE_TYPE (ovfl), t,
-                   build_int_cst (NULL_TREE,
-                                  cum->stack_words * UNITS_PER_WORD));
+       t = build2 (POINTER_PLUS_EXPR, TREE_TYPE (ovfl), t,
+                   size_int (cum->stack_words * UNITS_PER_WORD));
       t = build2 (GIMPLE_MODIFY_STMT, TREE_TYPE (ovfl), ovfl, t);
       expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
 
@@ -4227,8 +5077,8 @@ mips_va_start (tree valist, rtx nextarg)
       fpr_offset = gpr_save_area_size + UNITS_PER_FPVALUE - 1;
       fpr_offset &= ~(UNITS_PER_FPVALUE - 1);
       if (fpr_offset)
-       t = build2 (PLUS_EXPR, TREE_TYPE (ftop), t,
-                   build_int_cst (NULL_TREE, -fpr_offset));
+       t = build2 (POINTER_PLUS_EXPR, TREE_TYPE (ftop), t,
+                   size_int (-fpr_offset));
       t = build2 (GIMPLE_MODIFY_STMT, TREE_TYPE (ftop), ftop, t);
       expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
 
@@ -4378,28 +5228,27 @@ mips_gimplify_va_arg_expr (tree valist, tree type, tree *pre_p, tree *post_p)
       t = fold_convert (TREE_TYPE (off), build_int_cst (NULL_TREE, rsize));
       t = build2 (POSTDECREMENT_EXPR, TREE_TYPE (off), off, t);
       t = fold_convert (sizetype, t);
-      t = fold_convert (TREE_TYPE (top), t);
+      t = fold_build1 (NEGATE_EXPR, sizetype, t);
 
       /* [4] Emit code for: addr_rtx = top - off.  On big endian machines,
         the argument has RSIZE - SIZE bytes of leading padding.  */
-      t = build2 (MINUS_EXPR, TREE_TYPE (top), top, t);
+      t = build2 (POINTER_PLUS_EXPR, TREE_TYPE (top), top, t);
       if (BYTES_BIG_ENDIAN && rsize > size)
        {
-         u = fold_convert (TREE_TYPE (t), build_int_cst (NULL_TREE,
-                                                         rsize - size));
-         t = build2 (PLUS_EXPR, TREE_TYPE (t), t, u);
+         u = size_int (rsize - size);
+         t = build2 (POINTER_PLUS_EXPR, TREE_TYPE (t), t, u);
        }
       COND_EXPR_THEN (addr) = t;
 
       if (osize > UNITS_PER_WORD)
        {
          /* [9] Emit: ovfl += ((intptr_t) ovfl + osize - 1) & -osize.  */
-         u = fold_convert (TREE_TYPE (ovfl),
-                           build_int_cst (NULL_TREE, osize - 1));
-         t = build2 (PLUS_EXPR, TREE_TYPE (ovfl), ovfl, u);
-         u = fold_convert (TREE_TYPE (ovfl),
-                           build_int_cst (NULL_TREE, -osize));
-         t = build2 (BIT_AND_EXPR, TREE_TYPE (ovfl), t, u);
+         u = size_int (osize - 1);
+         t = build2 (POINTER_PLUS_EXPR, TREE_TYPE (ovfl), ovfl, u);
+         t = fold_convert (sizetype, t);
+         u = size_int (-osize);
+         t = build2 (BIT_AND_EXPR, sizetype, t, u);
+         t = fold_convert (TREE_TYPE (ovfl), t);
          align = build2 (GIMPLE_MODIFY_STMT, TREE_TYPE (ovfl), ovfl, t);
        }
       else
@@ -4413,9 +5262,8 @@ mips_gimplify_va_arg_expr (tree valist, tree type, tree *pre_p, tree *post_p)
       t = build2 (POSTINCREMENT_EXPR, TREE_TYPE (ovfl), ovfl, u);
       if (BYTES_BIG_ENDIAN && osize > size)
        {
-         u = fold_convert (TREE_TYPE (t),
-                           build_int_cst (NULL_TREE, osize - size));
-         t = build2 (PLUS_EXPR, TREE_TYPE (t), t, u);
+         u = size_int (osize - size);
+         t = build2 (POINTER_PLUS_EXPR, TREE_TYPE (t), t, u);
        }
 
       /* String [9] and [10,11] together.  */
@@ -4617,23 +5465,378 @@ mips_set_architecture (const struct mips_cpu_info *info)
 {
   if (info != 0)
     {
-      mips_arch_info = info;
-      mips_arch = info->cpu;
-      mips_isa = info->isa;
+      mips_arch_info = info;
+      mips_arch = info->cpu;
+      mips_isa = info->isa;
+    }
+}
+
+
+/* Likewise for tuning.  */
+
+static void
+mips_set_tune (const struct mips_cpu_info *info)
+{
+  if (info != 0)
+    {
+      mips_tune_info = info;
+      mips_tune = info->cpu;
+    }
+}
+
+/* Initialize mips_split_addresses from the associated command-line
+   settings.
+
+   mips_split_addresses is a half-way house between explicit
+   relocations and the traditional assembler macros.  It can
+   split absolute 32-bit symbolic constants into a high/lo_sum
+   pair but uses macros for other sorts of access.
+   
+   Like explicit relocation support for REL targets, it relies
+   on GNU extensions in the assembler and the linker.
+
+   Although this code should work for -O0, it has traditionally
+   been treated as an optimization.  */
+
+static void
+mips_init_split_addresses (void)
+{
+  if (!TARGET_MIPS16 && TARGET_SPLIT_ADDRESSES
+      && optimize && !flag_pic
+      && !ABI_HAS_64BIT_SYMBOLS)
+    mips_split_addresses = 1;
+  else
+    mips_split_addresses = 0;
+}
+
+/* (Re-)Initialize information about relocs.  */
+
+static void
+mips_init_relocs (void)
+{
+  memset (mips_split_p, '\0', sizeof (mips_split_p));
+  memset (mips_hi_relocs, '\0', sizeof (mips_hi_relocs));
+  memset (mips_lo_relocs, '\0', sizeof (mips_lo_relocs));
+
+  if (ABI_HAS_64BIT_SYMBOLS)
+    {
+      if (TARGET_EXPLICIT_RELOCS)
+       {
+         mips_split_p[SYMBOL_64_HIGH] = true;
+         mips_hi_relocs[SYMBOL_64_HIGH] = "%highest(";
+         mips_lo_relocs[SYMBOL_64_HIGH] = "%higher(";
+
+         mips_split_p[SYMBOL_64_MID] = true;
+         mips_hi_relocs[SYMBOL_64_MID] = "%higher(";
+         mips_lo_relocs[SYMBOL_64_MID] = "%hi(";
+
+         mips_split_p[SYMBOL_64_LOW] = true;
+         mips_hi_relocs[SYMBOL_64_LOW] = "%hi(";
+         mips_lo_relocs[SYMBOL_64_LOW] = "%lo(";
+
+         mips_split_p[SYMBOL_ABSOLUTE] = true;
+         mips_lo_relocs[SYMBOL_ABSOLUTE] = "%lo(";
+       }
+    }
+  else
+    {
+      if (TARGET_EXPLICIT_RELOCS || mips_split_addresses || TARGET_MIPS16)
+       {
+         mips_split_p[SYMBOL_ABSOLUTE] = true;
+         mips_hi_relocs[SYMBOL_ABSOLUTE] = "%hi(";
+         mips_lo_relocs[SYMBOL_ABSOLUTE] = "%lo(";
+
+         mips_lo_relocs[SYMBOL_32_HIGH] = "%hi(";
+       }
+    }
+
+  if (TARGET_MIPS16)
+    {
+      /* The high part is provided by a pseudo copy of $gp.  */
+      mips_split_p[SYMBOL_GP_RELATIVE] = true;
+      mips_lo_relocs[SYMBOL_GP_RELATIVE] = "%gprel(";
+    }
+
+  if (TARGET_EXPLICIT_RELOCS)
+    {
+      /* Small data constants are kept whole until after reload,
+        then lowered by mips_rewrite_small_data.  */
+      mips_lo_relocs[SYMBOL_GP_RELATIVE] = "%gp_rel(";
+
+      mips_split_p[SYMBOL_GOT_PAGE_OFST] = true;
+      if (TARGET_NEWABI)
+       {
+         mips_lo_relocs[SYMBOL_GOTOFF_PAGE] = "%got_page(";
+         mips_lo_relocs[SYMBOL_GOT_PAGE_OFST] = "%got_ofst(";
+       }
+      else
+       {
+         mips_lo_relocs[SYMBOL_GOTOFF_PAGE] = "%got(";
+         mips_lo_relocs[SYMBOL_GOT_PAGE_OFST] = "%lo(";
+       }
+
+      if (TARGET_XGOT)
+       {
+         /* The HIGH and LO_SUM are matched by special .md patterns.  */
+         mips_split_p[SYMBOL_GOT_DISP] = true;
+
+         mips_split_p[SYMBOL_GOTOFF_DISP] = true;
+         mips_hi_relocs[SYMBOL_GOTOFF_DISP] = "%got_hi(";
+         mips_lo_relocs[SYMBOL_GOTOFF_DISP] = "%got_lo(";
+
+         mips_split_p[SYMBOL_GOTOFF_CALL] = true;
+         mips_hi_relocs[SYMBOL_GOTOFF_CALL] = "%call_hi(";
+         mips_lo_relocs[SYMBOL_GOTOFF_CALL] = "%call_lo(";
+       }
+      else
+       {
+         if (TARGET_NEWABI)
+           mips_lo_relocs[SYMBOL_GOTOFF_DISP] = "%got_disp(";
+         else
+           mips_lo_relocs[SYMBOL_GOTOFF_DISP] = "%got(";
+         mips_lo_relocs[SYMBOL_GOTOFF_CALL] = "%call16(";
+       }
+    }
+
+  if (TARGET_NEWABI)
+    {
+      mips_split_p[SYMBOL_GOTOFF_LOADGP] = true;
+      mips_hi_relocs[SYMBOL_GOTOFF_LOADGP] = "%hi(%neg(%gp_rel(";
+      mips_lo_relocs[SYMBOL_GOTOFF_LOADGP] = "%lo(%neg(%gp_rel(";
+    }
+
+  /* Thread-local relocation operators.  */
+  mips_lo_relocs[SYMBOL_TLSGD] = "%tlsgd(";
+  mips_lo_relocs[SYMBOL_TLSLDM] = "%tlsldm(";
+  mips_split_p[SYMBOL_DTPREL] = 1;
+  mips_hi_relocs[SYMBOL_DTPREL] = "%dtprel_hi(";
+  mips_lo_relocs[SYMBOL_DTPREL] = "%dtprel_lo(";
+  mips_lo_relocs[SYMBOL_GOTTPREL] = "%gottprel(";
+  mips_split_p[SYMBOL_TPREL] = 1;
+  mips_hi_relocs[SYMBOL_TPREL] = "%tprel_hi(";
+  mips_lo_relocs[SYMBOL_TPREL] = "%tprel_lo(";
+
+  mips_lo_relocs[SYMBOL_HALF] = "%half(";
+}
+
+static GTY(()) int was_mips16_p = -1;
+
+/* Set up the target-dependent global state so that it matches the
+   current function's ISA mode.  */
+
+static void
+mips_set_mips16_mode (int mips16_p)
+{
+  if (mips16_p == was_mips16_p)
+    return;
+
+  /* Restore base settings of various flags.  */
+  target_flags = mips_base_target_flags;
+  align_loops = mips_base_align_loops;
+  align_jumps = mips_base_align_jumps;
+  align_functions = mips_base_align_functions;
+  flag_schedule_insns = mips_base_schedule_insns;
+  flag_reorder_blocks_and_partition = mips_base_reorder_blocks_and_partition;
+  flag_move_loop_invariants = mips_base_move_loop_invariants;
+  flag_delayed_branch = mips_flag_delayed_branch;
+  
+  if (mips16_p) 
+    {
+      /* Select mips16 instruction set.  */
+      target_flags |= MASK_MIPS16;
+
+      /* Don't run the scheduler before reload, since it tends to
+         increase register pressure.  */
+      flag_schedule_insns = 0;
+
+      /* Don't do hot/cold partitioning.  The constant layout code expects
+        the whole function to be in a single section.  */
+      flag_reorder_blocks_and_partition = 0;
+
+      /* Don't move loop invariants, because it tends to increase
+        register pressure.  It also introduces an extra move in cases
+        where the constant is the first operand in a two-operand binary
+        instruction, or when it forms a register argument to a functon
+        call.  */
+      flag_move_loop_invariants = 0;
+
+      /* Silently disable -mexplicit-relocs since it doesn't apply
+        to mips16 code.  Even so, it would overly pedantic to warn
+        about "-mips16 -mexplicit-relocs", especially given that
+        we use a %gprel() operator.  */
+      target_flags &= ~MASK_EXPLICIT_RELOCS;
+
+      /* 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 
+    {
+      /* Reset to select base non-mips16 ISA.  */
+      target_flags &= ~MASK_MIPS16;
+
+      /* When using explicit relocs, we call dbr_schedule from within
+        mips_reorg.  */
+      if (TARGET_EXPLICIT_RELOCS)
+       flag_delayed_branch = 0;
+
+      /* Provide default values for align_* for 64-bit targets.  */
+      if (TARGET_64BIT)
+       {
+         if (align_loops == 0)
+           align_loops = 8;
+         if (align_jumps == 0)
+           align_jumps = 8;
+         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.  */
+  mips_init_split_addresses ();
+  mips_init_relocs ();
+
+  if (was_mips16_p >= 0)
+    /* Reinitialize target-dependent state.  */
+    target_reinit ();
+
+  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.  */
 
-/* Likewise for tuning.  */
+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_tune (const struct mips_cpu_info *info)
+mips_set_current_function (tree fndecl)
 {
-  if (info != 0)
-    {
-      mips_tune_info = info;
-      mips_tune = info->cpu;
-    }
+  mips_set_mips16_mode (mips_use_mips16_mode_p (fndecl));
 }
 
 /* Implement TARGET_HANDLE_OPTION.  */
@@ -4670,6 +5873,17 @@ mips_handle_option (size_t code, const char *arg, int value ATTRIBUTE_UNUSED)
       mips_cache_flush_func = NULL;
       return true;
 
+    case OPT_mcode_readable_:
+      if (strcmp (arg, "yes") == 0)
+       mips_code_readable = CODE_READABLE_YES;
+      else if (strcmp (arg, "pcrel") == 0)
+       mips_code_readable = CODE_READABLE_PCREL;
+      else if (strcmp (arg, "no") == 0)
+       mips_code_readable = CODE_READABLE_NO;
+      else
+       return false;
+      return true;
+
     default:
       return true;
     }
@@ -4684,6 +5898,10 @@ override_options (void)
   int i, start, regno;
   enum machine_mode mode;
 
+#ifdef SUBTARGET_OVERRIDE_OPTIONS
+  SUBTARGET_OVERRIDE_OPTIONS;
+#endif
+
   mips_section_threshold = g_switch_set ? g_switch_value : MIPS_DEFAULT_GVALUE;
 
   /* The following code determines the architecture and register size.
@@ -4730,6 +5948,11 @@ override_options (void)
   else
     mips_cost = &mips_rtx_cost_data[mips_tune];
 
+  /* If the user hasn't specified a branch cost, use the processor's
+     default.  */
+  if (mips_branch_cost == 0)
+    mips_branch_cost = mips_cost->branch_cost;
+
   if ((target_flags_explicit & MASK_64BIT) != 0)
     {
       /* The user specified the size of the integer registers.  Make sure
@@ -4785,26 +6008,6 @@ override_options (void)
        target_flags &= ~MASK_LONG64;
     }
 
-  if (MIPS_MARCH_CONTROLS_SOFT_FLOAT
-      && (target_flags_explicit & MASK_SOFT_FLOAT) == 0)
-    {
-      /* For some configurations, it is useful to have -march control
-        the default setting of MASK_SOFT_FLOAT.  */
-      switch ((int) mips_arch)
-       {
-       case PROCESSOR_R4100:
-       case PROCESSOR_R4111:
-       case PROCESSOR_R4120:
-       case PROCESSOR_R4130:
-         target_flags |= MASK_SOFT_FLOAT;
-         break;
-
-       default:
-         target_flags &= ~MASK_SOFT_FLOAT;
-         break;
-       }
-    }
-
   if (!TARGET_OLDABI)
     flag_pcc_struct_return = 0;
 
@@ -4812,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)
@@ -4843,36 +6036,23 @@ override_options (void)
       target_flags &= ~MASK_ABICALLS;
     }
 
-  if (TARGET_ABICALLS)
+  /* MIPS16 cannot generate PIC yet.  */
+  if (TARGET_MIPS16 && (flag_pic || TARGET_ABICALLS))
     {
-      /* We need to set flag_pic for executables as well as DSOs
-        because we may reference symbols that are not defined in
-        the final executable.  (MIPS does not use things like
-        copy relocs, for example.)
-
-        Also, there is a body of code that uses __PIC__ to distinguish
-        between -mabicalls and -mno-abicalls code.  */
-      flag_pic = 1;
-      if (mips_section_threshold > 0)
-       warning (0, "%<-G%> is incompatible with %<-mabicalls%>");
+      sorry ("MIPS16 PIC");
+      target_flags &= ~MASK_ABICALLS;
+      flag_pic = flag_pie = flag_shlib = 0;
     }
 
-  /* mips_split_addresses is a half-way house between explicit
-     relocations and the traditional assembler macros.  It can
-     split absolute 32-bit symbolic constants into a high/lo_sum
-     pair but uses macros for other sorts of access.
-
-     Like explicit relocation support for REL targets, it relies
-     on GNU extensions in the assembler and the linker.
+  if (TARGET_ABICALLS)
+    /* We need to set flag_pic for executables as well as DSOs
+       because we may reference symbols that are not defined in
+       the final executable.  (MIPS does not use things like
+       copy relocs, for example.)
 
-     Although this code should work for -O0, it has traditionally
-     been treated as an optimization.  */
-  if (!TARGET_MIPS16 && TARGET_SPLIT_ADDRESSES
-      && optimize && !flag_pic
-      && !ABI_HAS_64BIT_SYMBOLS)
-    mips_split_addresses = 1;
-  else
-    mips_split_addresses = 0;
+       Also, there is a body of code that uses __PIC__ to distinguish
+       between -mabicalls and -mno-abicalls code.  */
+    flag_pic = 1;
 
   /* -mvr4130-align is a "speed over size" optimization: it usually produces
      faster code, but at the expense of more nops.  Enable it at -O3 and
@@ -4880,37 +6060,32 @@ override_options (void)
   if (optimize > 2 && (target_flags_explicit & MASK_VR4130_ALIGN) == 0)
     target_flags |= MASK_VR4130_ALIGN;
 
-  /* When compiling for the mips16, we cannot use floating point.  We
-     record the original hard float value in mips16_hard_float.  */
-  if (TARGET_MIPS16)
-    {
-      if (TARGET_SOFT_FLOAT)
-       mips16_hard_float = 0;
-      else
-       mips16_hard_float = 1;
-      target_flags |= MASK_SOFT_FLOAT;
-
-      /* Don't run the scheduler before reload, since it tends to
-         increase register pressure.  */
-      flag_schedule_insns = 0;
+  /* Prefer a call to memcpy over inline code when optimizing for size,
+     though see MOVE_RATIO in mips.h.  */
+  if (optimize_size && (target_flags_explicit & MASK_MEMCPY) == 0)
+    target_flags |= MASK_MEMCPY;
 
-      /* Don't do hot/cold partitioning.  The constant layout code expects
-        the whole function to be in a single section.  */
-      flag_reorder_blocks_and_partition = 0;
+  /* If we have a nonzero small-data limit, check that the -mgpopt
+     setting is consistent with the other target flags.  */
+  if (mips_section_threshold > 0)
+    {
+      if (!TARGET_GPOPT)
+       {
+         if (!TARGET_MIPS16 && !TARGET_EXPLICIT_RELOCS)
+           error ("%<-mno-gpopt%> needs %<-mexplicit-relocs%>");
 
-      /* Silently disable -mexplicit-relocs since it doesn't apply
-        to mips16 code.  Even so, it would overly pedantic to warn
-        about "-mips16 -mexplicit-relocs", especially given that
-        we use a %gprel() operator.  */
-      target_flags &= ~MASK_EXPLICIT_RELOCS;
-    }
+         TARGET_LOCAL_SDATA = false;
+         TARGET_EXTERN_SDATA = false;
+       }
+      else
+       {
+         if (TARGET_VXWORKS_RTP)
+           warning (0, "cannot use small-data accesses for %qs", "-mrtp");
 
-  /* When using explicit relocs, we call dbr_schedule from within
-     mips_reorg.  */
-  if (TARGET_EXPLICIT_RELOCS)
-    {
-      mips_flag_delayed_branch = flag_delayed_branch;
-      flag_delayed_branch = 0;
+         if (TARGET_ABICALLS)
+           warning (0, "cannot use small-data accesses for %qs",
+                    "-mabicalls");
+       }
     }
 
 #ifdef MIPS_TFMODE_FORMAT
@@ -4928,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
@@ -4941,9 +6116,6 @@ override_options (void)
   if (TARGET_DSPR2)
     target_flags |= MASK_DSP;
 
-  if (TARGET_MIPS16 && TARGET_DSP)
-    error ("-mips16 and -mdsp cannot be used together");
-
   mips_print_operand_punct['?'] = 1;
   mips_print_operand_punct['#'] = 1;
   mips_print_operand_punct['/'] = 1;
@@ -4964,12 +6136,20 @@ 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.  */
 
   for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
-    mips_dbx_regno[i] = -1;
+    {
+      mips_dbx_regno[i] = INVALID_REGNUM;
+      if (GP_REG_P (i) || FP_REG_P (i) || ALL_COP_REG_P (i))
+       mips_dwarf_regno[i] = i;
+      else
+       mips_dwarf_regno[i] = INVALID_REGNUM;
+    }
 
   start = GP_DBX_FIRST - GP_REG_FIRST;
   for (i = GP_REG_FIRST; i <= GP_REG_LAST; i++)
@@ -4979,8 +6159,16 @@ override_options (void)
   for (i = FP_REG_FIRST; i <= FP_REG_LAST; i++)
     mips_dbx_regno[i] = i + start;
 
+  /* HI and LO debug registers use big-endian ordering.  */
   mips_dbx_regno[HI_REGNUM] = MD_DBX_FIRST + 0;
   mips_dbx_regno[LO_REGNUM] = MD_DBX_FIRST + 1;
+  mips_dwarf_regno[HI_REGNUM] = MD_REG_FIRST + 0;
+  mips_dwarf_regno[LO_REGNUM] = MD_REG_FIRST + 1;
+  for (i = DSP_ACC_REG_FIRST; i <= DSP_ACC_REG_LAST; i += 2)
+    {
+      mips_dwarf_regno[i + TARGET_LITTLE_ENDIAN] = i;
+      mips_dwarf_regno[i + TARGET_BIG_ENDIAN] = i + 1;
+    }
 
   /* Set up array giving whether a given register can hold a given mode.  */
 
@@ -5018,7 +6206,9 @@ override_options (void)
            temp = ((regno & 1) == 0 || size <= UNITS_PER_WORD);
 
          else if (FP_REG_P (regno))
-           temp = ((regno % FP_INC) == 0)
+           temp = ((((regno % MAX_FPRS_PER_FMT) == 0)
+                    || (MIN_FPRS_PER_FMT == 1
+                        && size <= UNITS_PER_FPREG))
                    && (((class == MODE_FLOAT || class == MODE_COMPLEX_FLOAT
                          || class == MODE_VECTOR_FLOAT)
                         && size <= UNITS_PER_FPVALUE)
@@ -5032,13 +6222,15 @@ override_options (void)
                            && size >= MIN_UNITS_PER_WORD
                            && size <= UNITS_PER_FPREG)
                        /* Allow TFmode for CCmode reloads.  */
-                       || (ISA_HAS_8CC && mode == TFmode));
+                       || (ISA_HAS_8CC && mode == TFmode)));
 
           else if (ACC_REG_P (regno))
-           temp = (INTEGRAL_MODE_P (mode)
+           temp = ((INTEGRAL_MODE_P (mode) || ALL_FIXED_POINT_MODE_P (mode))
+                   && size <= UNITS_PER_WORD * 2
                    && (size <= UNITS_PER_WORD
-                       || (ACC_HI_REG_P (regno)
-                           && size == 2 * UNITS_PER_WORD)));
+                       || regno == MD_REG_FIRST
+                       || (DSP_ACC_REG_P (regno)
+                           && ((regno - DSP_ACC_REG_FIRST) & 1) == 0)));
 
          else if (ALL_COP_REG_P (regno))
            temp = (class == MODE_INT && size <= UNITS_PER_WORD);
@@ -5053,121 +6245,9 @@ override_options (void)
      initialized yet, so we can't use that here.  */
   gpr_mode = TARGET_64BIT ? DImode : SImode;
 
-  /* Provide default values for align_* for 64-bit targets.  */
-  if (TARGET_64BIT && !TARGET_MIPS16)
-    {
-      if (align_loops == 0)
-       align_loops = 8;
-      if (align_jumps == 0)
-       align_jumps = 8;
-      if (align_functions == 0)
-       align_functions = 8;
-    }
-
   /* Function to allocate machine-dependent function status.  */
   init_machine_status = &mips_init_machine_status;
 
-  if (ABI_HAS_64BIT_SYMBOLS)
-    {
-      if (TARGET_EXPLICIT_RELOCS)
-       {
-         mips_split_p[SYMBOL_64_HIGH] = true;
-         mips_hi_relocs[SYMBOL_64_HIGH] = "%highest(";
-         mips_lo_relocs[SYMBOL_64_HIGH] = "%higher(";
-
-         mips_split_p[SYMBOL_64_MID] = true;
-         mips_hi_relocs[SYMBOL_64_MID] = "%higher(";
-         mips_lo_relocs[SYMBOL_64_MID] = "%hi(";
-
-         mips_split_p[SYMBOL_64_LOW] = true;
-         mips_hi_relocs[SYMBOL_64_LOW] = "%hi(";
-         mips_lo_relocs[SYMBOL_64_LOW] = "%lo(";
-
-         mips_split_p[SYMBOL_GENERAL] = true;
-         mips_lo_relocs[SYMBOL_GENERAL] = "%lo(";
-       }
-    }
-  else
-    {
-      if (TARGET_EXPLICIT_RELOCS || mips_split_addresses)
-       {
-         mips_split_p[SYMBOL_GENERAL] = true;
-         mips_hi_relocs[SYMBOL_GENERAL] = "%hi(";
-         mips_lo_relocs[SYMBOL_GENERAL] = "%lo(";
-       }
-    }
-
-  if (TARGET_MIPS16)
-    {
-      /* The high part is provided by a pseudo copy of $gp.  */
-      mips_split_p[SYMBOL_SMALL_DATA] = true;
-      mips_lo_relocs[SYMBOL_SMALL_DATA] = "%gprel(";
-    }
-
-  if (TARGET_EXPLICIT_RELOCS)
-    {
-      /* Small data constants are kept whole until after reload,
-        then lowered by mips_rewrite_small_data.  */
-      mips_lo_relocs[SYMBOL_SMALL_DATA] = "%gp_rel(";
-
-      mips_split_p[SYMBOL_GOT_LOCAL] = true;
-      if (TARGET_NEWABI)
-       {
-         mips_lo_relocs[SYMBOL_GOTOFF_PAGE] = "%got_page(";
-         mips_lo_relocs[SYMBOL_GOT_LOCAL] = "%got_ofst(";
-       }
-      else
-       {
-         mips_lo_relocs[SYMBOL_GOTOFF_PAGE] = "%got(";
-         mips_lo_relocs[SYMBOL_GOT_LOCAL] = "%lo(";
-       }
-
-      if (TARGET_XGOT)
-       {
-         /* The HIGH and LO_SUM are matched by special .md patterns.  */
-         mips_split_p[SYMBOL_GOT_GLOBAL] = true;
-
-         mips_split_p[SYMBOL_GOTOFF_GLOBAL] = true;
-         mips_hi_relocs[SYMBOL_GOTOFF_GLOBAL] = "%got_hi(";
-         mips_lo_relocs[SYMBOL_GOTOFF_GLOBAL] = "%got_lo(";
-
-         mips_split_p[SYMBOL_GOTOFF_CALL] = true;
-         mips_hi_relocs[SYMBOL_GOTOFF_CALL] = "%call_hi(";
-         mips_lo_relocs[SYMBOL_GOTOFF_CALL] = "%call_lo(";
-       }
-      else
-       {
-         if (TARGET_NEWABI)
-           mips_lo_relocs[SYMBOL_GOTOFF_GLOBAL] = "%got_disp(";
-         else
-           mips_lo_relocs[SYMBOL_GOTOFF_GLOBAL] = "%got(";
-         mips_lo_relocs[SYMBOL_GOTOFF_CALL] = "%call16(";
-       }
-    }
-
-  if (TARGET_NEWABI)
-    {
-      mips_split_p[SYMBOL_GOTOFF_LOADGP] = true;
-      mips_hi_relocs[SYMBOL_GOTOFF_LOADGP] = "%hi(%neg(%gp_rel(";
-      mips_lo_relocs[SYMBOL_GOTOFF_LOADGP] = "%lo(%neg(%gp_rel(";
-    }
-
-  /* Thread-local relocation operators.  */
-  mips_lo_relocs[SYMBOL_TLSGD] = "%tlsgd(";
-  mips_lo_relocs[SYMBOL_TLSLDM] = "%tlsldm(";
-  mips_split_p[SYMBOL_DTPREL] = 1;
-  mips_hi_relocs[SYMBOL_DTPREL] = "%dtprel_hi(";
-  mips_lo_relocs[SYMBOL_DTPREL] = "%dtprel_lo(";
-  mips_lo_relocs[SYMBOL_GOTTPREL] = "%gottprel(";
-  mips_split_p[SYMBOL_TPREL] = 1;
-  mips_hi_relocs[SYMBOL_TPREL] = "%tprel_hi(";
-  mips_lo_relocs[SYMBOL_TPREL] = "%tprel_lo(";
-
-  /* We don't have a thread pointer access instruction on MIPS16, or
-     appropriate TLS relocations.  */
-  if (TARGET_MIPS16)
-    targetm.have_tls = false;
-
   /* Default to working around R4000 errata only if the processor
      was selected explicitly.  */
   if ((target_flags_explicit & MASK_FIX_R4000) == 0
@@ -5179,6 +6259,43 @@ override_options (void)
   if ((target_flags_explicit & MASK_FIX_R4400) == 0
       && mips_matching_cpu_name_p (mips_arch_info->name, "r4400"))
     target_flags |= MASK_FIX_R4400;
+
+  /* Save base state of options.  */
+  mips_base_mips16 = TARGET_MIPS16;
+  mips_base_target_flags = target_flags;
+  mips_base_schedule_insns = flag_schedule_insns;
+  mips_base_reorder_blocks_and_partition = flag_reorder_blocks_and_partition;
+  mips_base_move_loop_invariants = flag_move_loop_invariants;
+  mips_base_align_loops = align_loops;
+  mips_base_align_jumps = align_jumps;
+  mips_base_align_functions = align_functions;
+  mips_flag_delayed_branch = flag_delayed_branch;
+
+  /* Now select the mips16 or 32-bit instruction set, as requested.  */
+  mips_set_mips16_mode (mips_base_mips16);
+}
+
+/* Swap the register information for registers I and I + 1, which
+   currently have the wrong endianness.  Note that the registers'
+   fixedness and call-clobberedness might have been set on the
+   command line.  */
+
+static void
+mips_swap_registers (unsigned int i)
+{
+  int tmpi;
+  const char *tmps;
+
+#define SWAP_INT(X, Y) (tmpi = (X), (X) = (Y), (Y) = tmpi)
+#define SWAP_STRING(X, Y) (tmps = (X), (X) = (Y), (Y) = tmps)
+
+  SWAP_INT (fixed_regs[i], fixed_regs[i + 1]);
+  SWAP_INT (call_used_regs[i], call_used_regs[i + 1]);
+  SWAP_INT (call_really_used_regs[i], call_really_used_regs[i + 1]);
+  SWAP_STRING (reg_names[i], reg_names[i + 1]);
+
+#undef SWAP_STRING
+#undef SWAP_INT
 }
 
 /* Implement CONDITIONAL_REGISTER_USAGE.  */
@@ -5186,7 +6303,7 @@ override_options (void)
 void
 mips_conditional_register_usage (void)
 {
-  if (!TARGET_DSP)
+  if (!ISA_HAS_DSP)
     {
       int regno;
 
@@ -5242,6 +6359,15 @@ mips_conditional_register_usage (void)
       for (regno = FP_REG_FIRST + 21; regno <= FP_REG_FIRST + 31; regno+=2)
        call_really_used_regs[regno] = call_used_regs[regno] = 1;
     }
+  /* Make sure that double-register accumulator values are correctly
+     ordered for the current endianness.  */
+  if (TARGET_LITTLE_ENDIAN)
+    {
+      int regno;
+      mips_swap_registers (MD_REG_FIRST);
+      for (regno = DSP_ACC_REG_FIRST; regno <= DSP_ACC_REG_LAST; regno += 2)
+       mips_swap_registers (regno);
+    }
 }
 
 /* Allocate a chunk of memory for per-function machine-dependent data.  */
@@ -5317,6 +6443,20 @@ mips_debugger_offset (rtx addr, HOST_WIDE_INT offset)
   return offset;
 }
 \f
+/* If OP is an UNSPEC address, return the address to which it refers,
+   otherwise return OP itself.  */
+
+static rtx
+mips_strip_unspec_address (rtx op)
+{
+  rtx base, offset;
+
+  split_const (op, &base, &offset);
+  if (UNSPEC_ADDRESS_P (base))
+    op = plus_constant (UNSPEC_ADDRESS (base), INTVAL (offset));
+  return op;
+}
+
 /* Implement the PRINT_OPERAND macro.  The MIPS-specific operand codes are:
 
    'X'  OP is CONST_INT, prints 32 bits in hexadecimal format = "0x%08x",
@@ -5360,7 +6500,9 @@ mips_debugger_offset (rtx addr, HOST_WIDE_INT offset)
    '^' 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)
@@ -5490,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;
@@ -5563,11 +6715,11 @@ print_operand (FILE *file, rtx op, int letter)
       if (GET_CODE (op) == HIGH)
        op = XEXP (op, 0);
 
-      print_operand_reloc (file, op, mips_hi_relocs);
+      print_operand_reloc (file, op, SYMBOL_CONTEXT_LEA, mips_hi_relocs);
     }
 
   else if (letter == 'R')
-    print_operand_reloc (file, op, mips_lo_relocs);
+    print_operand_reloc (file, op, SYMBOL_CONTEXT_LEA, mips_lo_relocs);
 
   else if (letter == 'Y')
     {
@@ -5654,30 +6806,26 @@ print_operand (FILE *file, rtx op, int letter)
     fputs (reg_names[GLOBAL_POINTER_REGNUM], file);
 
   else
-    output_addr_const (file, op);
+    output_addr_const (file, mips_strip_unspec_address (op));
 }
 
 
-/* Print symbolic operand OP, which is part of a HIGH or LO_SUM.
-   RELOCS is the array of relocations to use.  */
+/* Print symbolic operand OP, which is part of a HIGH or LO_SUM
+   in context CONTEXT.  RELOCS is the array of relocations to use.  */
 
 static void
-print_operand_reloc (FILE *file, rtx op, const char **relocs)
+print_operand_reloc (FILE *file, rtx op, enum mips_symbol_context context,
+                    const char **relocs)
 {
   enum mips_symbol_type symbol_type;
   const char *p;
-  rtx base, offset;
 
-  if (!mips_symbolic_constant_p (op, &symbol_type) || relocs[symbol_type] == 0)
+  symbol_type = mips_classify_symbolic_expression (op, context);
+  if (relocs[symbol_type] == 0)
     fatal_insn ("PRINT_OPERAND, invalid operand for relocation", op);
 
-  /* If OP uses an UNSPEC address, we want to print the inner symbol.  */
-  split_const (op, &base, &offset);
-  if (UNSPEC_ADDRESS_P (base))
-    op = plus_constant (UNSPEC_ADDRESS (base), INTVAL (offset));
-
   fputs (relocs[symbol_type], file);
-  output_addr_const (file, op);
+  output_addr_const (file, mips_strip_unspec_address (op));
   for (p = relocs[symbol_type]; *p != 0; p++)
     if (*p == '(')
       fputc (')', file);
@@ -5699,7 +6847,8 @@ print_operand_address (FILE *file, rtx x)
        return;
 
       case ADDRESS_LO_SUM:
-       print_operand (file, addr.offset, 'R');
+       print_operand_reloc (file, addr.offset, SYMBOL_CONTEXT_MEM,
+                            mips_lo_relocs);
        fprintf (file, "(%s)", reg_names[REGNO (addr.reg)]);
        return;
 
@@ -5709,7 +6858,7 @@ print_operand_address (FILE *file, rtx x)
        return;
 
       case ADDRESS_SYMBOLIC:
-       output_addr_const (file, x);
+       output_addr_const (file, mips_strip_unspec_address (x));
        return;
       }
   gcc_unreachable ();
@@ -5868,27 +7017,27 @@ 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);
+       fprintf (asm_out_file, "\t.section .gcc_compiled_long%d\n"
+                "\t.previous\n", TARGET_LONG64 ? 64 : 32);
 
-      /* Restore the default section.  */
-      fprintf (asm_out_file, "\t.previous\n");
+#ifdef HAVE_AS_GNU_ATTRIBUTE
+      fprintf (asm_out_file, "\t.gnu_attribute 4, %d\n",
+              TARGET_HARD_FLOAT_ABI ? (TARGET_DOUBLE_FLOAT ? 1 : 2) : 3);
+#endif
     }
 
   /* Generate the pseudo ops that System V.4 wants.  */
   if (TARGET_ABICALLS)
     fprintf (asm_out_file, "\t.abicalls\n");
 
-  if (TARGET_MIPS16)
-    fprintf (asm_out_file, "\t.set\tmips16\n");
-
   if (flag_verbose_asm)
     fprintf (asm_out_file, "\n%s -G value = %d, Arch = %s, ISA = %d\n",
             ASM_COMMENT_START,
@@ -6044,29 +7193,40 @@ mips_finish_declare_object (FILE *stream, tree decl, int top_level, int at_end)
 }
 #endif
 \f
-/* Return true if X is a small data address that can be rewritten
-   as a LO_SUM.  */
+/* Return true if X in context CONTEXT is a small data address that can
+   be rewritten as a LO_SUM.  */
 
 static bool
-mips_rewrite_small_data_p (rtx x)
+mips_rewrite_small_data_p (rtx x, enum mips_symbol_context context)
 {
   enum mips_symbol_type symbol_type;
 
   return (TARGET_EXPLICIT_RELOCS
-         && mips_symbolic_constant_p (x, &symbol_type)
-         && symbol_type == SYMBOL_SMALL_DATA);
+         && mips_symbolic_constant_p (x, context, &symbol_type)
+         && symbol_type == SYMBOL_GP_RELATIVE);
 }
 
 
-/* A for_each_rtx callback for mips_small_data_pattern_p.  */
+/* A for_each_rtx callback for mips_small_data_pattern_p.  DATA is the
+   containing MEM, or null if none.  */
 
 static int
-mips_small_data_pattern_1 (rtx *loc, void *data ATTRIBUTE_UNUSED)
+mips_small_data_pattern_1 (rtx *loc, void *data)
 {
+  enum mips_symbol_context context;
+
   if (GET_CODE (*loc) == LO_SUM)
     return -1;
 
-  return mips_rewrite_small_data_p (*loc);
+  if (MEM_P (*loc))
+    {
+      if (for_each_rtx (&XEXP (*loc, 0), mips_small_data_pattern_1, *loc))
+       return 1;
+      return -1;
+    }
+
+  context = data ? SYMBOL_CONTEXT_MEM : SYMBOL_CONTEXT_LEA;
+  return mips_rewrite_small_data_p (*loc, context);
 }
 
 /* Return true if OP refers to small data symbols directly, not through
@@ -6078,12 +7238,22 @@ mips_small_data_pattern_p (rtx op)
   return for_each_rtx (&op, mips_small_data_pattern_1, 0);
 }
 \f
-/* A for_each_rtx callback, used by mips_rewrite_small_data.  */
+/* A for_each_rtx callback, used by mips_rewrite_small_data.
+   DATA is the containing MEM, or null if none.  */
 
 static int
-mips_rewrite_small_data_1 (rtx *loc, void *data ATTRIBUTE_UNUSED)
+mips_rewrite_small_data_1 (rtx *loc, void *data)
 {
-  if (mips_rewrite_small_data_p (*loc))
+  enum mips_symbol_context context;
+
+  if (MEM_P (*loc))
+    {
+      for_each_rtx (&XEXP (*loc, 0), mips_rewrite_small_data_1, *loc);
+      return -1;
+    }
+
+  context = data ? SYMBOL_CONTEXT_MEM : SYMBOL_CONTEXT_LEA;
+  if (mips_rewrite_small_data_p (*loc, context))
     *loc = gen_rtx_LO_SUM (Pmode, pic_offset_table_rtx, *loc);
 
   if (GET_CODE (*loc) == LO_SUM)
@@ -6139,8 +7309,8 @@ mips_global_pointer (void)
 {
   unsigned int regno;
 
-  /* $gp is always available in non-abicalls code.  */
-  if (!TARGET_ABICALLS)
+  /* $gp is always available unless we're using a GOT.  */
+  if (!TARGET_USE_GOT)
     return GLOBAL_POINTER_REGNUM;
 
   /* We must always provide $gp when it is used implicitly.  */
@@ -6170,17 +7340,17 @@ mips_global_pointer (void)
 
      In cases like these, reload will have added the constant to the pool
      but no instruction will yet refer to it.  */
-  if (!regs_ever_live[GLOBAL_POINTER_REGNUM]
+  if (!df_regs_ever_live_p (GLOBAL_POINTER_REGNUM)
       && !current_function_uses_const_pool
       && !mips_function_has_gp_insn ())
     return 0;
 
   /* We need a global pointer, but perhaps we can use a call-clobbered
      register instead of $gp.  */
-  if (TARGET_NEWABI && current_function_is_leaf)
+  if (TARGET_CALL_SAVED_GP && current_function_is_leaf)
     for (regno = GP_REG_FIRST; regno <= GP_REG_LAST; regno++)
-      if (!regs_ever_live[regno]
-         && call_used_regs[regno]
+      if (!df_regs_ever_live_p (regno)
+         && call_really_used_regs[regno]
          && !fixed_regs[regno]
          && regno != PIC_FUNCTION_ADDR_REGNUM)
        return regno;
@@ -6189,55 +7359,132 @@ mips_global_pointer (void)
 }
 
 
+/* Return true if the function return value MODE will get returned in a
+   floating-point register.  */
+
+static bool
+mips_return_mode_in_fpr_p (enum machine_mode mode)
+{
+  return ((GET_MODE_CLASS (mode) == MODE_FLOAT
+          || GET_MODE_CLASS (mode) == MODE_VECTOR_FLOAT
+          || GET_MODE_CLASS (mode) == MODE_COMPLEX_FLOAT)
+         && GET_MODE_UNIT_SIZE (mode) <= UNITS_PER_HWFPVALUE);
+}
+
+/* Return a two-character string representing a function floating-point
+   return mode, used to name MIPS16 function stubs.  */
+
+static const char *
+mips16_call_stub_mode_suffix (enum machine_mode mode)
+{
+  if (mode == SFmode)
+    return "sf";
+  else if (mode == DFmode)
+    return "df";
+  else if (mode == SCmode)
+    return "sc";
+  else if (mode == DCmode)
+    return "dc";
+  else if (mode == V2SFmode)
+    return "df";
+  else
+    gcc_unreachable ();
+}
+
+/* Return true if the current function returns its value in a floating-point
+   register in MIPS16 mode.  */
+
+static bool
+mips16_cfun_returns_in_fpr_p (void)
+{
+  tree return_type = DECL_RESULT (current_function_decl);
+  return (TARGET_MIPS16
+         && TARGET_HARD_FLOAT_ABI
+         && !aggregate_value_p (return_type, current_function_decl)
+         && mips_return_mode_in_fpr_p (DECL_MODE (return_type)));
+}
+
+
 /* Return true if the current function must save REGNO.  */
 
 static bool
 mips_save_reg_p (unsigned int regno)
 {
-  /* We only need to save $gp for NewABI PIC.  */
+  /* We only need to save $gp if TARGET_CALL_SAVED_GP and only then
+     if we have not chosen a call-clobbered substitute.  */
   if (regno == GLOBAL_POINTER_REGNUM)
-    return (TARGET_ABICALLS && TARGET_NEWABI
-           && cfun->machine->global_pointer == regno);
+    return TARGET_CALL_SAVED_GP && cfun->machine->global_pointer == regno;
 
   /* Check call-saved registers.  */
-  if (regs_ever_live[regno] && !call_used_regs[regno])
+  if ((current_function_saves_all_registers || df_regs_ever_live_p (regno))
+      && !call_really_used_regs[regno])
+    return true;
+
+  /* Save both registers in an FPR pair if either one is used.  This is
+     needed for the case when MIN_FPRS_PER_FMT == 1, which allows the odd
+     register to be used without the even register.  */
+  if (FP_REG_P (regno)
+      && MAX_FPRS_PER_FMT == 2
+      && df_regs_ever_live_p (regno + 1)
+      && !call_really_used_regs[regno + 1])
     return true;
 
   /* We need to save the old frame pointer before setting up a new one.  */
   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 (regno == GP_REG_FIRST + 31 && regs_ever_live[regno])
+     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
+     value in FPRs.  */
+  if (regno == GP_REG_FIRST + 31
+      && (df_regs_ever_live_p (regno)
+         || current_function_calls_eh_return
+         || mips16_cfun_returns_in_fpr_p ()))
     return true;
 
-  if (TARGET_MIPS16)
-    {
-      tree return_type;
+  return false;
+}
 
-      return_type = DECL_RESULT (current_function_decl);
+/* Return the index of the lowest X in the range [0, SIZE) for which
+   bit REGS[X] is set in MASK.  Return SIZE if there is no such X.  */
 
-      /* $18 is a special case in mips16 code.  It may be used to call
-        a function which returns a floating point value, but it is
-        marked in call_used_regs.  */
-      if (regno == GP_REG_FIRST + 18 && regs_ever_live[regno])
-       return true;
+static unsigned int
+mips16e_find_first_register (unsigned int mask, const unsigned char *regs,
+                            unsigned int size)
+{
+  unsigned int i;
 
-      /* $31 is also a special case.  It will be used to copy a return
-        value into the floating point registers if the return value is
-        floating point.  */
-      if (regno == GP_REG_FIRST + 31
-         && mips16_hard_float
-         && !aggregate_value_p (return_type, current_function_decl)
-         && GET_MODE_CLASS (DECL_MODE (return_type)) == MODE_FLOAT
-         && GET_MODE_SIZE (DECL_MODE (return_type)) <= UNITS_PER_FPVALUE)
-       return true;
-    }
+  for (i = 0; i < size; i++)
+    if (BITSET_P (mask, regs[i]))
+      break;
 
-  return false;
+  return i;
 }
 
+/* *MASK_PTR is a mask of general purpose registers and *GP_REG_SIZE_PTR
+   is the number of bytes that they occupy.  If *MASK_PTR contains REGS[X]
+   for some X in [0, SIZE), adjust *MASK_PTR and *GP_REG_SIZE_PTR so that
+   the same is true for all indexes (X, SIZE).  */
+
+static void
+mips16e_mask_registers (unsigned int *mask_ptr, const unsigned char *regs,
+                       unsigned int size, HOST_WIDE_INT *gp_reg_size_ptr)
+{
+  unsigned int i;
+
+  i = mips16e_find_first_register (*mask_ptr, regs, size);
+  for (i++; i < size; i++)
+    if (!BITSET_P (*mask_ptr, regs[i]))
+      {
+       *gp_reg_size_ptr += GET_MODE_SIZE (gpr_mode);
+       *mask_ptr |= 1 << regs[i];
+      }
+}
 
 /* Return the bytes needed to compute the frame pointer from the current
    stack pointer.  SIZE is the size (in bytes) of the local variables.
@@ -6245,10 +7492,9 @@ mips_save_reg_p (unsigned int regno)
    MIPS stack frames look like:
 
              Before call                       After call
-        +-----------------------+      +-----------------------+
-   high |                      |       |                       |
-   mem. |                      |       |                       |
-        |  caller's temps.     |       |  caller's temps.      |
+   high +-----------------------+      +-----------------------+
+   mem. |                      |       |                       |
+       |  caller's temps.      |       |  caller's temps.      |
        |                       |       |                       |
         +-----------------------+      +-----------------------+
        |                       |       |                       |
@@ -6258,37 +7504,40 @@ mips_save_reg_p (unsigned int regno)
        |  4 words to save      |       |  4 words to save      |
        |  arguments passed     |       |  arguments passed     |
        |  in registers, even   |       |  in registers, even   |
-    SP->|  if not passed.       |  VFP->|  if not passed.      |
-       +-----------------------+       +-----------------------+
-                                       |                       |
-                                        |  fp register save     |
-                                       |                       |
+        |  if not passed.       |       |  if not passed.      |
+    SP->+-----------------------+  VFP->+-----------------------+
+               (VFP = SP+fp_sp_offset) |                       |\
+                                       |  fp register save     | | fp_reg_size
+                                       |                       |/
+                      SP+gp_sp_offset->+-----------------------+
+                                      /|                       |\
+                                     | |  gp register save     | | gp_reg_size
+                      gp_reg_rounded | |                       |/
+                                     | +-----------------------+
+                                      \|  alignment padding    |
                                        +-----------------------+
-                                       |                       |
-                                        |  gp register save     |
-                                        |                      |
+                                       |                       |\
+                                       |  local variables      | | var_size
+                                       |                       |/
                                        +-----------------------+
                                        |                       |
-                                       |  local variables      |
+                                       |  alloca allocations   |
                                        |                       |
                                        +-----------------------+
-                                       |                       |
-                                        |  alloca allocations   |
-                                       |                       |
-                                       +-----------------------+
-                                       |                       |
-                                       |  GP save for V.4 abi  |
-                                       |                       |
+                                      /|                       |
+                      cprestore_size | |  GP save for V.4 abi  |
+                                      \|                       |
                                        +-----------------------+
-                                       |                       |
-                                        |  arguments on stack   |
-                                       |                       |
-                                       +-----------------------+
-                                        |  4 words to save      |
-                                       |  arguments passed     |
-                                        |  in registers, even   |
-   low                              SP->|  if not passed.       |
-   memory                              +-----------------------+
+                                       |                       |\
+                                       |  arguments on stack   | |
+                                       |                       | |
+                                       +-----------------------+ |
+                                       |  4 words to save      | | args_size
+                                       |  arguments passed     | |
+                                       |  in registers, even   | |
+                                       |  if not passed.       | |
+   low                                 |  (TARGET_OLDABI only) |/
+   memory                          SP->+-----------------------+
 
 */
 
@@ -6354,18 +7603,28 @@ compute_frame_size (HOST_WIDE_INT size)
        }
     }
 
+  /* The MIPS16e SAVE and RESTORE instructions have two ranges of registers:
+     $a3-$a0 and $s2-$s8.  If we save one register in the range, we must
+     save all later registers too.  */
+  if (GENERATE_MIPS16E_SAVE_RESTORE)
+    {
+      mips16e_mask_registers (&mask, mips16e_s2_s8_regs,
+                             ARRAY_SIZE (mips16e_s2_s8_regs), &gp_reg_size);
+      mips16e_mask_registers (&mask, mips16e_a0_a3_regs,
+                             ARRAY_SIZE (mips16e_a0_a3_regs), &gp_reg_size);
+    }
+
   /* This loop must iterate over the same space as its companion in
-     save_restore_insns.  */
-  for (regno = (FP_REG_LAST - FP_INC + 1);
-       regno >= FP_REG_FIRST;
-       regno -= FP_INC)
-    {
+     mips_for_each_saved_reg.  */
+  if (TARGET_HARD_FLOAT)
+    for (regno = (FP_REG_LAST - MAX_FPRS_PER_FMT + 1);
+        regno >= FP_REG_FIRST;
+        regno -= MAX_FPRS_PER_FMT)
       if (mips_save_reg_p (regno))
        {
-         fp_reg_size += FP_INC * UNITS_PER_FPREG;
-         fmask |= ((1 << FP_INC) - 1) << (regno - FP_REG_FIRST);
+         fp_reg_size += MAX_FPRS_PER_FMT * UNITS_PER_FPREG;
+         fmask |= ((1 << MAX_FPRS_PER_FMT) - 1) << (regno - FP_REG_FIRST);
        }
-    }
 
   gp_reg_rounded = MIPS_STACK_ALIGN (gp_reg_size);
   total_size += gp_reg_rounded + MIPS_STACK_ALIGN (fp_reg_size);
@@ -6385,14 +7644,23 @@ compute_frame_size (HOST_WIDE_INT size)
   cfun->machine->frame.fmask = fmask;
   cfun->machine->frame.initialized = reload_completed;
   cfun->machine->frame.num_gp = gp_reg_size / UNITS_PER_WORD;
-  cfun->machine->frame.num_fp = fp_reg_size / (FP_INC * UNITS_PER_FPREG);
+  cfun->machine->frame.num_fp = (fp_reg_size
+                                / (MAX_FPRS_PER_FMT * UNITS_PER_FPREG));
 
   if (mask)
     {
       HOST_WIDE_INT offset;
 
-      offset = (args_size + cprestore_size + var_size
-               + gp_reg_size - GET_MODE_SIZE (gpr_mode));
+      if (GENERATE_MIPS16E_SAVE_RESTORE)
+       /* MIPS16e SAVE and RESTORE instructions require the GP save area
+          to be aligned at the high end with any padding at the low end.
+          It is only safe to use this calculation for o32, where we never
+          have pretend arguments, and where any varargs will be saved in
+          the caller-allocated area rather than at the top of the frame.  */
+       offset = (total_size - GET_MODE_SIZE (gpr_mode));
+      else
+       offset = (args_size + cprestore_size + var_size
+                 + gp_reg_size - GET_MODE_SIZE (gpr_mode));
       cfun->machine->frame.gp_sp_offset = offset;
       cfun->machine->frame.gp_save_offset = offset - total_size;
     }
@@ -6408,7 +7676,7 @@ compute_frame_size (HOST_WIDE_INT size)
 
       offset = (args_size + cprestore_size + var_size
                + gp_reg_rounded + fp_reg_size
-               - FP_INC * UNITS_PER_FPREG);
+               - MAX_FPRS_PER_FMT * UNITS_PER_FPREG);
       cfun->machine->frame.fp_sp_offset = offset;
       cfun->machine->frame.fp_save_offset = offset - total_size;
     }
@@ -6489,8 +7757,6 @@ mips_save_restore_reg (enum machine_mode mode, int regno,
 static void
 mips_for_each_saved_reg (HOST_WIDE_INT sp_offset, mips_save_restore_fn fn)
 {
-#define BITSET_P(VALUE, BIT) (((VALUE) & (1L << (BIT))) != 0)
-
   enum machine_mode fpr_mode;
   HOST_WIDE_INT offset;
   int regno;
@@ -6511,15 +7777,14 @@ mips_for_each_saved_reg (HOST_WIDE_INT sp_offset, mips_save_restore_fn fn)
      compute_frame_size.  */
   offset = cfun->machine->frame.fp_sp_offset - sp_offset;
   fpr_mode = (TARGET_SINGLE_FLOAT ? SFmode : DFmode);
-  for (regno = (FP_REG_LAST - FP_INC + 1);
+  for (regno = (FP_REG_LAST - MAX_FPRS_PER_FMT + 1);
        regno >= FP_REG_FIRST;
-       regno -= FP_INC)
+       regno -= MAX_FPRS_PER_FMT)
     if (BITSET_P (cfun->machine->frame.fmask, regno - FP_REG_FIRST))
       {
        mips_save_restore_reg (fpr_mode, regno, offset, fn);
        offset -= GET_MODE_SIZE (fpr_mode);
       }
-#undef BITSET_P
 }
 \f
 /* If we're generating n32 or n64 abicalls, and the current function
@@ -6541,9 +7806,12 @@ mips_output_cplocal (void)
 enum mips_loadgp_style
 mips_current_loadgp_style (void)
 {
-  if (!TARGET_ABICALLS || cfun->machine->global_pointer == 0)
+  if (!TARGET_USE_GOT || cfun->machine->global_pointer == 0)
     return LOADGP_NONE;
 
+  if (TARGET_RTP_PIC)
+    return LOADGP_RTP;
+
   if (TARGET_ABSOLUTE_ABICALLS)
     return LOADGP_ABSOLUTE;
 
@@ -6560,7 +7828,7 @@ static GTY(()) rtx mips_gnu_local_gp;
 static void
 mips_emit_loadgp (void)
 {
-  rtx addr, offset, incoming_address;
+  rtx addr, offset, incoming_address, base, index;
 
   switch (mips_current_loadgp_style ())
     {
@@ -6570,14 +7838,22 @@ mips_emit_loadgp (void)
          mips_gnu_local_gp = gen_rtx_SYMBOL_REF (Pmode, "__gnu_local_gp");
          SYMBOL_REF_FLAGS (mips_gnu_local_gp) |= SYMBOL_FLAG_LOCAL;
        }
-      emit_insn (gen_loadgp_noshared (mips_gnu_local_gp));
+      emit_insn (gen_loadgp_absolute (mips_gnu_local_gp));
       break;
 
     case LOADGP_NEWABI:
       addr = XEXP (DECL_RTL (current_function_decl), 0);
       offset = mips_unspec_address (addr, SYMBOL_GOTOFF_LOADGP);
       incoming_address = gen_rtx_REG (Pmode, PIC_FUNCTION_ADDR_REGNUM);
-      emit_insn (gen_loadgp (offset, incoming_address));
+      emit_insn (gen_loadgp_newabi (offset, incoming_address));
+      if (!TARGET_EXPLICIT_RELOCS)
+       emit_insn (gen_loadgp_blockage ());
+      break;
+
+    case LOADGP_RTP:
+      base = gen_rtx_SYMBOL_REF (Pmode, ggc_strdup (VXWORKS_GOTT_BASE));
+      index = gen_rtx_SYMBOL_REF (Pmode, ggc_strdup (VXWORKS_GOTT_INDEX));
+      emit_insn (gen_loadgp_rtp (base, index));
       if (!TARGET_EXPLICIT_RELOCS)
        emit_insn (gen_loadgp_blockage ());
       break;
@@ -6604,10 +7880,17 @@ mips_output_function_prologue (FILE *file, HOST_WIDE_INT size ATTRIBUTE_UNUSED)
      floating point arguments.  The linker will arrange for any 32-bit
      functions to call this stub, which will then jump to the 16-bit
      function proper.  */
-  if (TARGET_MIPS16 && !TARGET_SOFT_FLOAT
+  if (TARGET_MIPS16
+      && TARGET_HARD_FLOAT_ABI
       && current_function_args_info.fp_code != 0)
     build_mips16_function_stub (file);
 
+  /* Select the mips16 mode for this function.  */
+  if (TARGET_MIPS16)
+    fprintf (file, "\t.set\tmips16\n");
+  else 
+    fprintf (file, "\t.set\tnomips16\n");
+
   if (!FUNCTION_NAME_ALREADY_DECLARED)
     {
       /* Get the function name the same way that toplev.c does before calling
@@ -6728,9 +8011,9 @@ 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
-       emit_move_insn (mem, reg);
+       mips_emit_move (mem, reg);
 
       x1 = mips_frame_set (mips_subword (mem, 0), mips_subword (reg, 0));
       x2 = mips_frame_set (mips_subword (mem, 1), mips_subword (reg, 1));
@@ -6745,16 +8028,413 @@ mips_save_reg (rtx reg, rtx mem)
          /* Save a non-mips16 register by moving it through a temporary.
             We don't need to do this for $31 since there's a special
             instruction for it.  */
-         emit_move_insn (MIPS_PROLOGUE_TEMP (GET_MODE (reg)), reg);
-         emit_move_insn (mem, MIPS_PROLOGUE_TEMP (GET_MODE (reg)));
+         mips_emit_move (MIPS_PROLOGUE_TEMP (GET_MODE (reg)), reg);
+         mips_emit_move (mem, MIPS_PROLOGUE_TEMP (GET_MODE (reg)));
        }
       else
-       emit_move_insn (mem, reg);
+       mips_emit_move (mem, reg);
 
       mips_set_frame_expr (mips_frame_set (mem, reg));
     }
 }
 
+/* Return a move between register REGNO and memory location SP + OFFSET.
+   Make the move a load if RESTORE_P, otherwise make it a frame-related
+   store.  */
+
+static rtx
+mips16e_save_restore_reg (bool restore_p, HOST_WIDE_INT offset,
+                         unsigned int regno)
+{
+  rtx reg, mem;
+
+  mem = gen_frame_mem (SImode, plus_constant (stack_pointer_rtx, offset));
+  reg = gen_rtx_REG (SImode, regno);
+  return (restore_p
+         ? gen_rtx_SET (VOIDmode, reg, mem)
+         : mips_frame_set (mem, reg));
+}
+
+/* Return RTL for a MIPS16e SAVE or RESTORE instruction; RESTORE_P says which.
+   The instruction must:
+
+     - Allocate or deallocate SIZE bytes in total; SIZE is known
+       to be nonzero.
+
+     - Save or restore as many registers in *MASK_PTR as possible.
+       The instruction saves the first registers at the top of the
+       allocated area, with the other registers below it.
+
+     - Save NARGS argument registers above the allocated area.
+
+   (NARGS is always zero if RESTORE_P.)
+
+   The SAVE and RESTORE instructions cannot save and restore all general
+   registers, so there may be some registers left over for the caller to
+   handle.  Destructively modify *MASK_PTR so that it contains the registers
+   that still need to be saved or restored.  The caller can save these
+   registers in the memory immediately below *OFFSET_PTR, which is a
+   byte offset from the bottom of the allocated stack area.  */
+
+static rtx
+mips16e_build_save_restore (bool restore_p, unsigned int *mask_ptr,
+                           HOST_WIDE_INT *offset_ptr, unsigned int nargs,
+                           HOST_WIDE_INT size)
+{
+  rtx pattern, set;
+  HOST_WIDE_INT offset, top_offset;
+  unsigned int i, regno;
+  int n;
+
+  gcc_assert (cfun->machine->frame.fp_reg_size == 0);
+
+  /* Calculate the number of elements in the PARALLEL.  We need one element
+     for the stack adjustment, one for each argument register save, and one
+     for each additional register move.  */
+  n = 1 + nargs;
+  for (i = 0; i < ARRAY_SIZE (mips16e_save_restore_regs); i++)
+    if (BITSET_P (*mask_ptr, mips16e_save_restore_regs[i]))
+      n++;
+
+  /* Create the final PARALLEL.  */
+  pattern = gen_rtx_PARALLEL (VOIDmode, rtvec_alloc (n));
+  n = 0;
+
+  /* Add the stack pointer adjustment.  */
+  set = gen_rtx_SET (VOIDmode, stack_pointer_rtx,
+                    plus_constant (stack_pointer_rtx,
+                                   restore_p ? size : -size));
+  RTX_FRAME_RELATED_P (set) = 1;
+  XVECEXP (pattern, 0, n++) = set;
+
+  /* Stack offsets in the PARALLEL are relative to the old stack pointer.  */
+  top_offset = restore_p ? size : 0;
+
+  /* Save the arguments.  */
+  for (i = 0; i < nargs; i++)
+    {
+      offset = top_offset + i * GET_MODE_SIZE (gpr_mode);
+      set = mips16e_save_restore_reg (restore_p, offset, GP_ARG_FIRST + i);
+      XVECEXP (pattern, 0, n++) = set;
+    }
+
+  /* Then fill in the other register moves.  */
+  offset = top_offset;
+  for (i = 0; i < ARRAY_SIZE (mips16e_save_restore_regs); i++)
+    {
+      regno = mips16e_save_restore_regs[i];
+      if (BITSET_P (*mask_ptr, regno))
+       {
+         offset -= UNITS_PER_WORD;
+         set = mips16e_save_restore_reg (restore_p, offset, regno);
+         XVECEXP (pattern, 0, n++) = set;
+         *mask_ptr &= ~(1 << regno);
+       }
+    }
+
+  /* Tell the caller what offset it should use for the remaining registers.  */
+  *offset_ptr = size + (offset - top_offset) + size;
+
+  gcc_assert (n == XVECLEN (pattern, 0));
+
+  return pattern;
+}
+
+/* PATTERN is a PARALLEL whose first element adds ADJUST to the stack
+   pointer.  Return true if PATTERN matches the kind of instruction
+   generated by mips16e_build_save_restore.  If INFO is nonnull,
+   initialize it when returning true.  */
+
+bool
+mips16e_save_restore_pattern_p (rtx pattern, HOST_WIDE_INT adjust,
+                               struct mips16e_save_restore_info *info)
+{
+  unsigned int i, nargs, mask;
+  HOST_WIDE_INT top_offset, save_offset, offset, extra;
+  rtx set, reg, mem, base;
+  int n;
+
+  if (!GENERATE_MIPS16E_SAVE_RESTORE)
+    return false;
+
+  /* Stack offsets in the PARALLEL are relative to the old stack pointer.  */
+  top_offset = adjust > 0 ? adjust : 0;
+
+  /* Interpret all other members of the PARALLEL.  */
+  save_offset = top_offset - GET_MODE_SIZE (gpr_mode);
+  mask = 0;
+  nargs = 0;
+  i = 0;
+  for (n = 1; n < XVECLEN (pattern, 0); n++)
+    {
+      /* Check that we have a SET.  */
+      set = XVECEXP (pattern, 0, n);
+      if (GET_CODE (set) != SET)
+       return false;
+
+      /* Check that the SET is a load (if restoring) or a store
+        (if saving).  */
+      mem = adjust > 0 ? SET_SRC (set) : SET_DEST (set);
+      if (!MEM_P (mem))
+       return false;
+
+      /* Check that the address is the sum of the stack pointer and a
+        possibly-zero constant offset.  */
+      mips_split_plus (XEXP (mem, 0), &base, &offset);
+      if (base != stack_pointer_rtx)
+       return false;
+
+      /* Check that SET's other operand is a register.  */
+      reg = adjust > 0 ? SET_DEST (set) : SET_SRC (set);
+      if (!REG_P (reg))
+       return false;
+
+      /* Check for argument saves.  */
+      if (offset == top_offset + nargs * GET_MODE_SIZE (gpr_mode)
+         && REGNO (reg) == GP_ARG_FIRST + nargs)
+       nargs++;
+      else if (offset == save_offset)
+       {
+         while (mips16e_save_restore_regs[i++] != REGNO (reg))
+           if (i == ARRAY_SIZE (mips16e_save_restore_regs))
+             return false;
+
+         mask |= 1 << REGNO (reg);
+         save_offset -= GET_MODE_SIZE (gpr_mode);
+       }
+      else
+       return false;
+    }
+
+  /* Check that the restrictions on register ranges are met.  */
+  extra = 0;
+  mips16e_mask_registers (&mask, mips16e_s2_s8_regs,
+                         ARRAY_SIZE (mips16e_s2_s8_regs), &extra);
+  mips16e_mask_registers (&mask, mips16e_a0_a3_regs,
+                         ARRAY_SIZE (mips16e_a0_a3_regs), &extra);
+  if (extra != 0)
+    return false;
+
+  /* Make sure that the topmost argument register is not saved twice.
+     The checks above ensure that the same is then true for the other
+     argument registers.  */
+  if (nargs > 0 && BITSET_P (mask, GP_ARG_FIRST + nargs - 1))
+    return false;
+
+  /* Pass back information, if requested.  */
+  if (info)
+    {
+      info->nargs = nargs;
+      info->mask = mask;
+      info->size = (adjust > 0 ? adjust : -adjust);
+    }
+
+  return true;
+}
+
+/* Add a MIPS16e SAVE or RESTORE register-range argument to string S
+   for the register range [MIN_REG, MAX_REG].  Return a pointer to
+   the null terminator.  */
+
+static char *
+mips16e_add_register_range (char *s, unsigned int min_reg,
+                           unsigned int max_reg)
+{
+  if (min_reg != max_reg)
+    s += sprintf (s, ",%s-%s", reg_names[min_reg], reg_names[max_reg]);
+  else
+    s += sprintf (s, ",%s", reg_names[min_reg]);
+  return s;
+}
+
+/* Return the assembly instruction for a MIPS16e SAVE or RESTORE instruction.
+   PATTERN and ADJUST are as for mips16e_save_restore_pattern_p.  */
+
+const char *
+mips16e_output_save_restore (rtx pattern, HOST_WIDE_INT adjust)
+{
+  static char buffer[300];
+
+  struct mips16e_save_restore_info info;
+  unsigned int i, end;
+  char *s;
+
+  /* Parse the pattern.  */
+  if (!mips16e_save_restore_pattern_p (pattern, adjust, &info))
+    gcc_unreachable ();
+
+  /* Add the mnemonic.  */
+  s = strcpy (buffer, adjust > 0 ? "restore\t" : "save\t");
+  s += strlen (s);
+
+  /* Save the arguments.  */
+  if (info.nargs > 1)
+    s += sprintf (s, "%s-%s,", reg_names[GP_ARG_FIRST],
+                 reg_names[GP_ARG_FIRST + info.nargs - 1]);
+  else if (info.nargs == 1)
+    s += sprintf (s, "%s,", reg_names[GP_ARG_FIRST]);
+
+  /* Emit the amount of stack space to allocate or deallocate.  */
+  s += sprintf (s, "%d", (int) info.size);
+
+  /* Save or restore $16.  */
+  if (BITSET_P (info.mask, 16))
+    s += sprintf (s, ",%s", reg_names[GP_REG_FIRST + 16]);
+
+  /* Save or restore $17.  */
+  if (BITSET_P (info.mask, 17))
+    s += sprintf (s, ",%s", reg_names[GP_REG_FIRST + 17]);
+
+  /* Save or restore registers in the range $s2...$s8, which
+     mips16e_s2_s8_regs lists in decreasing order.  Note that this
+     is a software register range; the hardware registers are not
+     numbered consecutively.  */
+  end = ARRAY_SIZE (mips16e_s2_s8_regs);
+  i = mips16e_find_first_register (info.mask, mips16e_s2_s8_regs, end);
+  if (i < end)
+    s = mips16e_add_register_range (s, mips16e_s2_s8_regs[end - 1],
+                                   mips16e_s2_s8_regs[i]);
+
+  /* Save or restore registers in the range $a0...$a3.  */
+  end = ARRAY_SIZE (mips16e_a0_a3_regs);
+  i = mips16e_find_first_register (info.mask, mips16e_a0_a3_regs, end);
+  if (i < end)
+    s = mips16e_add_register_range (s, mips16e_a0_a3_regs[i],
+                                   mips16e_a0_a3_regs[end - 1]);
+
+  /* Save or restore $31.  */
+  if (BITSET_P (info.mask, 31))
+    s += sprintf (s, ",%s", reg_names[GP_REG_FIRST + 31]);
+
+  return buffer;
+}
+
+/* Return a simplified form of X using the register values in REG_VALUES.
+   REG_VALUES[R] is the last value assigned to hard register R, or null
+   if R has not been modified.
+
+   This function is rather limited, but is good enough for our purposes.  */
+
+static rtx
+mips16e_collect_propagate_value (rtx x, rtx *reg_values)
+{
+  rtx x0, x1;
+
+  x = avoid_constant_pool_reference (x);
+
+  if (UNARY_P (x))
+    {
+      x0 = mips16e_collect_propagate_value (XEXP (x, 0), reg_values);
+      return simplify_gen_unary (GET_CODE (x), GET_MODE (x),
+                                x0, GET_MODE (XEXP (x, 0)));
+    }
+
+  if (ARITHMETIC_P (x))
+    {
+      x0 = mips16e_collect_propagate_value (XEXP (x, 0), reg_values);
+      x1 = mips16e_collect_propagate_value (XEXP (x, 1), reg_values);
+      return simplify_gen_binary (GET_CODE (x), GET_MODE (x), x0, x1);
+    }
+
+  if (REG_P (x)
+      && reg_values[REGNO (x)]
+      && !rtx_unstable_p (reg_values[REGNO (x)]))
+    return reg_values[REGNO (x)];
+
+  return x;
+}
+
+/* Return true if (set DEST SRC) stores an argument register into its
+   caller-allocated save slot, storing the number of that argument
+   register in *REGNO_PTR if so.  REG_VALUES is as for
+   mips16e_collect_propagate_value.  */
+
+static bool
+mips16e_collect_argument_save_p (rtx dest, rtx src, rtx *reg_values,
+                                unsigned int *regno_ptr)
+{
+  unsigned int argno, regno;
+  HOST_WIDE_INT offset, required_offset;
+  rtx addr, base;
+
+  /* Check that this is a word-mode store.  */
+  if (!MEM_P (dest) || !REG_P (src) || GET_MODE (dest) != word_mode)
+    return false;
+
+  /* Check that the register being saved is an unmodified argument
+     register.  */
+  regno = REGNO (src);
+  if (regno < GP_ARG_FIRST || regno > GP_ARG_LAST || reg_values[regno])
+    return false;
+  argno = regno - GP_ARG_FIRST;
+
+  /* Check whether the address is an appropriate stack pointer or
+     frame pointer access.  The frame pointer is offset from the
+     stack pointer by the size of the outgoing arguments.  */
+  addr = mips16e_collect_propagate_value (XEXP (dest, 0), reg_values);
+  mips_split_plus (addr, &base, &offset);
+  required_offset = cfun->machine->frame.total_size + argno * UNITS_PER_WORD;
+  if (base == hard_frame_pointer_rtx)
+    required_offset -= cfun->machine->frame.args_size;
+  else if (base != stack_pointer_rtx)
+    return false;
+  if (offset != required_offset)
+    return false;
+
+  *regno_ptr = regno;
+  return true;
+}
+
+/* A subroutine of mips_expand_prologue, called only when generating
+   MIPS16e SAVE instructions.  Search the start of the function for any
+   instructions that save argument registers into their caller-allocated
+   save slots.  Delete such instructions and return a value N such that
+   saving [GP_ARG_FIRST, GP_ARG_FIRST + N) would make all the deleted
+   instructions redundant.  */
+
+static unsigned int
+mips16e_collect_argument_saves (void)
+{
+  rtx reg_values[FIRST_PSEUDO_REGISTER];
+  rtx insn, next, set, dest, src;
+  unsigned int nargs, regno;
+
+  push_topmost_sequence ();
+  nargs = 0;
+  memset (reg_values, 0, sizeof (reg_values));
+  for (insn = get_insns (); insn; insn = next)
+    {
+      next = NEXT_INSN (insn);
+      if (NOTE_P (insn))
+       continue;
+
+      if (!INSN_P (insn))
+       break;
+
+      set = PATTERN (insn);
+      if (GET_CODE (set) != SET)
+       break;
+
+      dest = SET_DEST (set);
+      src = SET_SRC (set);
+      if (mips16e_collect_argument_save_p (dest, src, reg_values, &regno))
+       {
+         if (!BITSET_P (cfun->machine->frame.mask, regno))
+           {
+             delete_insn (insn);
+             nargs = MAX (nargs, (regno - GP_ARG_FIRST) + 1);
+           }
+       }
+      else if (REG_P (dest) && GET_MODE (dest) == word_mode)
+       reg_values[REGNO (dest)]
+         = mips16e_collect_propagate_value (src, reg_values);
+      else
+       break;
+    }
+  pop_topmost_sequence ();
+
+  return nargs;
+}
 
 /* Expand the prologue into a bunch of separate insns.  */
 
@@ -6762,9 +8442,11 @@ void
 mips_expand_prologue (void)
 {
   HOST_WIDE_INT size;
+  unsigned int nargs;
+  rtx insn;
 
   if (cfun->machine->global_pointer > 0)
-    REGNO (pic_offset_table_rtx) = cfun->machine->global_pointer;
+    SET_REGNO (pic_offset_table_rtx, cfun->machine->global_pointer);
 
   size = compute_frame_size (get_frame_size ());
 
@@ -6776,11 +8458,39 @@ mips_expand_prologue (void)
       HOST_WIDE_INT step1;
 
       step1 = MIN (size, MIPS_MAX_FIRST_STACK_STEP);
-      RTX_FRAME_RELATED_P (emit_insn (gen_add3_insn (stack_pointer_rtx,
-                                                    stack_pointer_rtx,
-                                                    GEN_INT (-step1)))) = 1;
-      size -= step1;
-      mips_for_each_saved_reg (size, mips_save_reg);
+
+      if (GENERATE_MIPS16E_SAVE_RESTORE)
+       {
+         HOST_WIDE_INT offset;
+         unsigned int mask, regno;
+
+         /* Try to merge argument stores into the save instruction.  */
+         nargs = mips16e_collect_argument_saves ();
+
+         /* Build the save instruction.  */
+         mask = cfun->machine->frame.mask;
+         insn = mips16e_build_save_restore (false, &mask, &offset,
+                                            nargs, step1);
+         RTX_FRAME_RELATED_P (emit_insn (insn)) = 1;
+         size -= step1;
+
+         /* Check if we need to save other registers.  */
+         for (regno = GP_REG_FIRST; regno < GP_REG_LAST; regno++)
+           if (BITSET_P (mask, regno - GP_REG_FIRST))
+             {
+               offset -= GET_MODE_SIZE (gpr_mode);
+               mips_save_restore_reg (gpr_mode, regno, offset, mips_save_reg);
+             }
+       }
+      else
+       {
+         insn = gen_add3_insn (stack_pointer_rtx,
+                               stack_pointer_rtx,
+                               GEN_INT (-step1));
+         RTX_FRAME_RELATED_P (emit_insn (insn)) = 1;
+         size -= step1;
+         mips_for_each_saved_reg (size, mips_save_reg);
+       }
     }
 
   /* Allocate the rest of the frame.  */
@@ -6792,7 +8502,7 @@ mips_expand_prologue (void)
                                                       GEN_INT (-size)))) = 1;
       else
        {
-         emit_move_insn (MIPS_PROLOGUE_TEMP (Pmode), GEN_INT (size));
+         mips_emit_move (MIPS_PROLOGUE_TEMP (Pmode), GEN_INT (size));
          if (TARGET_MIPS16)
            {
              /* There are no instructions to add or subtract registers
@@ -6800,11 +8510,11 @@ mips_expand_prologue (void)
                 temporary.  We should always be using a frame pointer
                 in this case anyway.  */
              gcc_assert (frame_pointer_needed);
-             emit_move_insn (hard_frame_pointer_rtx, stack_pointer_rtx);
+             mips_emit_move (hard_frame_pointer_rtx, stack_pointer_rtx);
              emit_insn (gen_sub3_insn (hard_frame_pointer_rtx,
                                        hard_frame_pointer_rtx,
                                        MIPS_PROLOGUE_TEMP (Pmode)));
-             emit_move_insn (stack_pointer_rtx, hard_frame_pointer_rtx);
+             mips_emit_move (stack_pointer_rtx, hard_frame_pointer_rtx);
            }
          else
            emit_insn (gen_sub3_insn (stack_pointer_rtx,
@@ -6834,8 +8544,8 @@ mips_expand_prologue (void)
                                         offset))) = 1;
          else
            {
-             emit_move_insn (MIPS_PROLOGUE_TEMP (Pmode), offset);
-             emit_move_insn (hard_frame_pointer_rtx, stack_pointer_rtx);
+             mips_emit_move (MIPS_PROLOGUE_TEMP (Pmode), offset);
+             mips_emit_move (hard_frame_pointer_rtx, stack_pointer_rtx);
              emit_insn (gen_add3_insn (hard_frame_pointer_rtx,
                                        hard_frame_pointer_rtx,
                                        MIPS_PROLOGUE_TEMP (Pmode)));
@@ -6846,14 +8556,14 @@ mips_expand_prologue (void)
            }
        }
       else
-       RTX_FRAME_RELATED_P (emit_move_insn (hard_frame_pointer_rtx,
+       RTX_FRAME_RELATED_P (mips_emit_move (hard_frame_pointer_rtx,
                                             stack_pointer_rtx)) = 1;
     }
 
   mips_emit_loadgp ();
 
   /* If generating o32/o64 abicalls, save $gp on the stack.  */
-  if (TARGET_ABICALLS && !TARGET_NEWABI && !current_function_is_leaf)
+  if (TARGET_ABICALLS && TARGET_OLDABI && !current_function_is_leaf)
     emit_insn (gen_cprestore (GEN_INT (current_function_outgoing_args_size)));
 
   /* If we are profiling, make sure no instructions are scheduled before
@@ -6873,7 +8583,7 @@ mips_output_function_epilogue (FILE *file ATTRIBUTE_UNUSED,
                               HOST_WIDE_INT size ATTRIBUTE_UNUSED)
 {
   /* Reinstate the normal $gp.  */
-  REGNO (pic_offset_table_rtx) = GLOBAL_POINTER_REGNUM;
+  SET_REGNO (pic_offset_table_rtx, GLOBAL_POINTER_REGNUM);
   mips_output_cplocal ();
 
   if (cfun->machine->all_noreorder_p)
@@ -6911,11 +8621,11 @@ mips_restore_reg (rtx reg, rtx mem)
   if (TARGET_MIPS16 && !M16_REG_P (REGNO (reg)))
     {
       /* Can't restore directly; move through a temporary.  */
-      emit_move_insn (MIPS_EPILOGUE_TEMP (GET_MODE (reg)), mem);
-      emit_move_insn (reg, MIPS_EPILOGUE_TEMP (GET_MODE (reg)));
+      mips_emit_move (MIPS_EPILOGUE_TEMP (GET_MODE (reg)), mem);
+      mips_emit_move (reg, MIPS_EPILOGUE_TEMP (GET_MODE (reg)));
     }
   else
-    emit_move_insn (reg, mem);
+    mips_emit_move (reg, mem);
 }
 
 
@@ -6934,6 +8644,33 @@ mips_expand_epilogue (int sibcall_p)
       emit_jump_insn (gen_return ());
       return;
     }
+  
+  /* In mips16 mode, if the return value should go into a floating-point
+     register, we need to call a helper routine to copy it over.  */
+  if (mips16_cfun_returns_in_fpr_p ())
+    {
+      char *name;
+      rtx func;
+      rtx insn;
+      rtx retval;
+      rtx call;
+      tree id;
+      tree return_type;
+      enum machine_mode return_mode;
+
+      return_type = DECL_RESULT (current_function_decl);
+      return_mode = DECL_MODE (return_type);
+
+      name = ACONCAT (("__mips16_ret_",
+                      mips16_call_stub_mode_suffix (return_mode),
+                      NULL));
+      id = get_identifier (name);
+      func = gen_rtx_SYMBOL_REF (Pmode, IDENTIFIER_POINTER (id));
+      retval = gen_rtx_REG (return_mode, GP_RETURN);
+      call = gen_call_value_internal (retval, func, const0_rtx);
+      insn = emit_call_insn (call);
+      use_reg (&CALL_INSN_FUNCTION_USAGE (insn), retval);
+    }
 
   /* Split the frame into two.  STEP1 is the amount of stack we should
      deallocate before restoring the registers.  STEP2 is the amount we
@@ -6972,7 +8709,7 @@ mips_expand_epilogue (int sibcall_p)
       adjust = GEN_INT (step1);
       if (!SMALL_OPERAND (step1))
        {
-         emit_move_insn (MIPS_EPILOGUE_TEMP (Pmode), adjust);
+         mips_emit_move (MIPS_EPILOGUE_TEMP (Pmode), adjust);
          adjust = MIPS_EPILOGUE_TEMP (Pmode);
        }
 
@@ -6985,23 +8722,48 @@ mips_expand_epilogue (int sibcall_p)
 
   /* Copy TARGET into the stack pointer.  */
   if (target != stack_pointer_rtx)
-    emit_move_insn (stack_pointer_rtx, target);
+    mips_emit_move (stack_pointer_rtx, target);
 
-  /* If we're using addressing macros for n32/n64 abicalls, $gp is
-     implicitly used by all SYMBOL_REFs.  We must emit a blockage
-     insn before restoring it.  */
-  if (TARGET_ABICALLS && TARGET_NEWABI && !TARGET_EXPLICIT_RELOCS)
+  /* If we're using addressing macros, $gp is implicitly used by all
+     SYMBOL_REFs.  We must emit a blockage insn before restoring $gp
+     from the stack.  */
+  if (TARGET_CALL_SAVED_GP && !TARGET_EXPLICIT_RELOCS)
     emit_insn (gen_blockage ());
 
-  /* Restore the registers.  */
-  mips_for_each_saved_reg (cfun->machine->frame.total_size - step2,
-                          mips_restore_reg);
+  if (GENERATE_MIPS16E_SAVE_RESTORE && cfun->machine->frame.mask != 0)
+    {
+      unsigned int regno, mask;
+      HOST_WIDE_INT offset;
+      rtx restore;
+
+      /* Generate the restore instruction.  */
+      mask = cfun->machine->frame.mask;
+      restore = mips16e_build_save_restore (true, &mask, &offset, 0, step2);
+
+      /* Restore any other registers manually.  */
+      for (regno = GP_REG_FIRST; regno < GP_REG_LAST; regno++)
+       if (BITSET_P (mask, regno - GP_REG_FIRST))
+         {
+           offset -= GET_MODE_SIZE (gpr_mode);
+           mips_save_restore_reg (gpr_mode, regno, offset, mips_restore_reg);
+         }
+
+      /* Restore the remaining registers and deallocate the final bit
+        of the frame.  */
+      emit_insn (restore);
+    }
+  else
+    {
+      /* Restore the registers.  */
+      mips_for_each_saved_reg (cfun->machine->frame.total_size - step2,
+                              mips_restore_reg);
 
-  /* Deallocate the final bit of the frame.  */
-  if (step2 > 0)
-    emit_insn (gen_add3_insn (stack_pointer_rtx,
-                             stack_pointer_rtx,
-                             GEN_INT (step2)));
+      /* Deallocate the final bit of the frame.  */
+      if (step2 > 0)
+       emit_insn (gen_add3_insn (stack_pointer_rtx,
+                                 stack_pointer_rtx,
+                                 GEN_INT (step2)));
+    }
 
   /* Add in the __builtin_eh_return stack adjustment.  We need to
      use a temporary in mips16 code.  */
@@ -7009,11 +8771,11 @@ mips_expand_epilogue (int sibcall_p)
     {
       if (TARGET_MIPS16)
        {
-         emit_move_insn (MIPS_EPILOGUE_TEMP (Pmode), stack_pointer_rtx);
+         mips_emit_move (MIPS_EPILOGUE_TEMP (Pmode), stack_pointer_rtx);
          emit_insn (gen_add3_insn (MIPS_EPILOGUE_TEMP (Pmode),
                                    MIPS_EPILOGUE_TEMP (Pmode),
                                    EH_RETURN_STACKADJ_RTX));
-         emit_move_insn (stack_pointer_rtx, MIPS_EPILOGUE_TEMP (Pmode));
+         mips_emit_move (stack_pointer_rtx, MIPS_EPILOGUE_TEMP (Pmode));
        }
       else
        emit_insn (gen_add3_insn (stack_pointer_rtx,
@@ -7023,8 +8785,11 @@ mips_expand_epilogue (int sibcall_p)
 
   if (!sibcall_p)
     {
-      /* The mips16 loads the return address into $7, not $31.  */
-      if (TARGET_MIPS16 && (cfun->machine->frame.mask & RA_MASK) != 0)
+      /* When generating MIPS16 code, the normal mips_for_each_saved_reg
+        path will restore the return address into $7 rather than $31.  */
+      if (TARGET_MIPS16
+         && !GENERATE_MIPS16E_SAVE_RESTORE
+         && (cfun->machine->frame.mask & RA_MASK) != 0)
        emit_jump_insn (gen_return_internal (gen_rtx_REG (Pmode,
                                                          GP_REG_FIRST + 7)));
       else
@@ -7040,24 +8805,16 @@ mips_expand_epilogue (int sibcall_p)
 int
 mips_can_use_return_insn (void)
 {
-  tree return_type;
-
   if (! reload_completed)
     return 0;
 
-  if (regs_ever_live[31] || current_function_profile)
+  if (df_regs_ever_live_p (31) || current_function_profile)
     return 0;
 
-  return_type = DECL_RESULT (current_function_decl);
-
-  /* In mips16 mode, a function which returns a floating point value
+  /* In mips16 mode, a function that returns a floating point value
      needs to arrange to copy the return value into the floating point
      registers.  */
-  if (TARGET_MIPS16
-      && mips16_hard_float
-      && ! aggregate_value_p (return_type, current_function_decl)
-      && GET_MODE_CLASS (DECL_MODE (return_type)) == MODE_FLOAT
-      && GET_MODE_SIZE (DECL_MODE (return_type)) <= UNITS_PER_FPVALUE)
+  if (mips16_cfun_returns_in_fpr_p ())
     return 0;
 
   if (cfun->machine->frame.initialized)
@@ -7075,21 +8832,38 @@ 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.  */
-  no_new_pseudos = 1;
   reload_completed = 1;
-  reset_block_changes ();
 
-  /* Pick a global pointer for -mabicalls.  Use $15 rather than $28
-     for TARGET_NEWABI since the latter is a call-saved register.  */
-  if (TARGET_ABICALLS)
-    cfun->machine->global_pointer
-      = REGNO (pic_offset_table_rtx)
-      = TARGET_NEWABI ? 15 : GLOBAL_POINTER_REGNUM;
+  /* Mark the end of the (empty) prologue.  */
+  emit_note (NOTE_INSN_PROLOGUE_END);
 
-  /* Set up the global pointer for n32 or n64 abicalls.  */
-  mips_emit_loadgp ();
+  /* 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));
+
+  /* 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;
+
+      default:
+       break;
+      }
 
   /* We need two temporary registers in some cases.  */
   temp1 = gen_rtx_REG (Pmode, 2);
@@ -7107,7 +8881,7 @@ mips_output_mi_thunk (FILE *file, tree thunk_fndecl ATTRIBUTE_UNUSED,
       rtx offset = GEN_INT (delta);
       if (!SMALL_OPERAND (delta))
        {
-         emit_move_insn (temp1, offset);
+         mips_emit_move (temp1, offset);
          offset = temp1;
        }
       emit_insn (gen_add3_insn (this, this, offset));
@@ -7119,53 +8893,54 @@ mips_output_mi_thunk (FILE *file, tree thunk_fndecl ATTRIBUTE_UNUSED,
       rtx addr;
 
       /* Set TEMP1 to *THIS.  */
-      emit_move_insn (temp1, gen_rtx_MEM (Pmode, this));
+      mips_emit_move (temp1, gen_rtx_MEM (Pmode, this));
 
       /* Set ADDR to a legitimate address for *THIS + VCALL_OFFSET.  */
       addr = mips_add_offset (temp2, temp1, vcall_offset);
 
       /* Load the offset and add it to THIS.  */
-      emit_move_insn (temp1, gen_rtx_MEM (Pmode, addr));
+      mips_emit_move (temp1, gen_rtx_MEM (Pmode, addr));
       emit_insn (gen_add3_insn (this, this, temp1));
     }
 
   /* 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_ABICALLS || TARGET_LONG_CALLS)
+  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.
         The general move patterns therefore reject this combination.
 
-        In this context, lazy binding would actually be OK for o32 and o64,
-        but it's still wrong for n32 and n64; see mips_load_call_address.
-        We must therefore load the address via a temporary register if
-        mips_dangerous_for_la25_p.
+        In this context, lazy binding would actually be OK
+        for TARGET_CALL_CLOBBERED_GP, but it's still wrong for
+        TARGET_CALL_SAVED_GP; see mips_load_call_address.
+        We must therefore load the address via a temporary
+        register if mips_dangerous_for_la25_p.
 
         If we jump to the temporary register rather than $25, the assembler
         can use the move insn to fill the jump's delay slot.  */
-      if (TARGET_ABICALLS && !mips_dangerous_for_la25_p (fnaddr))
+      if (TARGET_USE_PIC_FN_ADDR_REG
+         && !mips_dangerous_for_la25_p (fnaddr))
        temp1 = gen_rtx_REG (Pmode, PIC_FUNCTION_ADDR_REGNUM);
       mips_load_call_address (temp1, fnaddr, true);
 
-      if (TARGET_ABICALLS && REGNO (temp1) != PIC_FUNCTION_ADDR_REGNUM)
-       emit_move_insn (gen_rtx_REG (Pmode, PIC_FUNCTION_ADDR_REGNUM), temp1);
+      if (TARGET_USE_PIC_FN_ADDR_REG
+         && REGNO (temp1) != PIC_FUNCTION_ADDR_REGNUM)
+       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.  */
   insn = get_insns ();
-  insn_locators_initialize ();
+  insn_locators_alloc ();
   split_all_insns_noflow ();
-  if (TARGET_MIPS16)
-    mips16_lay_out_constants ();
+  mips16_lay_out_constants ();
   shorten_branches (insn);
   final_start_function (insn, file, 1);
   final (insn, file, 1);
@@ -7174,64 +8949,19 @@ mips_output_mi_thunk (FILE *file, tree thunk_fndecl ATTRIBUTE_UNUSED,
   /* Clean up the vars set above.  Note that final_end_function resets
      the global pointer for us.  */
   reload_completed = 0;
-  no_new_pseudos = 0;
 }
 \f
-/* Returns nonzero if X contains a SYMBOL_REF.  */
-
-static int
-symbolic_expression_p (rtx x)
-{
-  if (GET_CODE (x) == SYMBOL_REF)
-    return 1;
-
-  if (GET_CODE (x) == CONST)
-    return symbolic_expression_p (XEXP (x, 0));
-
-  if (UNARY_P (x))
-    return symbolic_expression_p (XEXP (x, 0));
-
-  if (ARITHMETIC_P (x))
-    return (symbolic_expression_p (XEXP (x, 0))
-           || symbolic_expression_p (XEXP (x, 1)));
-
-  return 0;
-}
-
-/* Choose the section to use for the constant rtx expression X that has
-   mode MODE.  */
+/* Implement TARGET_SELECT_RTX_SECTION.  */
 
 static section *
 mips_select_rtx_section (enum machine_mode mode, rtx x,
                         unsigned HOST_WIDE_INT align)
 {
-  if (TARGET_MIPS16)
-    {
-      /* In mips16 mode, the constant table always goes in the same section
-         as the function, so that constants can be loaded using PC relative
-         addressing.  */
-      return function_section (current_function_decl);
-    }
-  else if (TARGET_EMBEDDED_DATA)
-    {
-      /* For embedded applications, always put constants in read-only data,
-        in order to reduce RAM usage.  */
-      return mergeable_constant_section (mode, align, 0);
-    }
-  else
-    {
-      /* For hosted applications, always put constants in small data if
-        possible, as this gives the best performance.  */
-      /* ??? Consider using mergeable small data sections.  */
+  /* ??? Consider using mergeable small data sections.  */
+  if (mips_rtx_constant_in_small_data_p (mode))
+    return get_named_section (NULL, ".sdata", 0);
 
-      if (GET_MODE_SIZE (mode) <= (unsigned) mips_section_threshold
-         && mips_section_threshold > 0)
-       return get_named_section (NULL, ".sdata", 0);
-      else if (flag_pic && symbolic_expression_p (x))
-       return get_named_section (NULL, ".data.rel.ro", 3);
-      else
-       return mergeable_constant_section (mode, align, 0);
-    }
+  return default_elf_select_rtx_section (mode, x, align);
 }
 
 /* Implement TARGET_ASM_FUNCTION_RODATA_SECTION.
@@ -7274,16 +9004,16 @@ mips_function_rodata_section (tree decl)
    mips_classify_symbol decide when to use %gp_rel(...)($gp) accesses.  */
 
 static bool
-mips_in_small_data_p (tree decl)
+mips_in_small_data_p (const_tree decl)
 {
   HOST_WIDE_INT size;
 
   if (TREE_CODE (decl) == STRING_CST || TREE_CODE (decl) == FUNCTION_DECL)
     return false;
 
-  /* We don't yet generate small-data references for -mabicalls.  See related
-     -G handling in override_options.  */
-  if (TARGET_ABICALLS)
+  /* We don't yet generate small-data references for -mabicalls or
+     VxWorks RTP code.  See the related -G handling in override_options.  */
+  if (TARGET_ABICALLS || TARGET_VXWORKS_RTP)
     return false;
 
   if (TREE_CODE (decl) == VAR_DECL && DECL_SECTION_NAME (decl) != 0)
@@ -7297,7 +9027,7 @@ mips_in_small_data_p (tree decl)
 
       /* If a symbol is defined externally, the assembler will use the
         usual -G rules when deciding how to implement macros.  */
-      if (TARGET_EXPLICIT_RELOCS || !DECL_EXTERNAL (decl))
+      if (mips_lo_relocs[SYMBOL_GP_RELATIVE] || !DECL_EXTERNAL (decl))
        return true;
     }
   else if (TARGET_EMBEDDED_DATA)
@@ -7313,6 +9043,19 @@ mips_in_small_data_p (tree decl)
        return false;
     }
 
+  /* Enforce -mlocal-sdata.  */
+  if (!TARGET_LOCAL_SDATA && !TREE_PUBLIC (decl))
+    return false;
+
+  /* Enforce -mextern-sdata.  */
+  if (!TARGET_EXTERN_SDATA && DECL_P (decl))
+    {
+      if (DECL_EXTERNAL (decl))
+       return false;
+      if (DECL_COMMON (decl) && DECL_INITIAL (decl) == NULL)
+       return false;
+    }
+
   size = int_size_in_bytes (TREE_TYPE (decl));
   return (size > 0 && size <= mips_section_threshold);
 }
@@ -7323,16 +9066,16 @@ mips_in_small_data_p (tree decl)
    where the PC acts as an anchor.  */
 
 static bool
-mips_use_anchors_for_symbol_p (rtx symbol)
+mips_use_anchors_for_symbol_p (const_rtx symbol)
 {
-  switch (mips_classify_symbol (symbol))
+  switch (mips_classify_symbol (symbol, SYMBOL_CONTEXT_MEM))
     {
-    case SYMBOL_CONSTANT_POOL:
-    case SYMBOL_SMALL_DATA:
+    case SYMBOL_PC_RELATIVE:
+    case SYMBOL_GP_RELATIVE:
       return false;
 
     default:
-      return true;
+      return default_use_anchors_for_symbol_p (symbol);
     }
 }
 \f
@@ -7346,7 +9089,7 @@ mips_use_anchors_for_symbol_p (rtx symbol)
    type.  */
 
 static int
-mips_fpr_return_fields (tree valtype, tree *fields)
+mips_fpr_return_fields (const_tree valtype, tree *fields)
 {
   tree field;
   int i;
@@ -7386,7 +9129,7 @@ mips_fpr_return_fields (tree valtype, tree *fields)
       - the structure is not returned in floating-point registers.  */
 
 static bool
-mips_return_in_msb (tree valtype)
+mips_return_in_msb (const_tree valtype)
 {
   tree fields[2];
 
@@ -7412,7 +9155,7 @@ mips_return_fpr_pair (enum machine_mode mode,
 {
   int inc;
 
-  inc = (TARGET_NEWABI ? 2 : FP_INC);
+  inc = (TARGET_NEWABI ? 2 : MAX_FPRS_PER_FMT);
   return gen_rtx_PARALLEL
     (mode,
      gen_rtvec (2,
@@ -7431,7 +9174,7 @@ mips_return_fpr_pair (enum machine_mode mode,
    VALTYPE is null and MODE is the mode of the return value.  */
 
 rtx
-mips_function_value (tree valtype, tree func ATTRIBUTE_UNUSED,
+mips_function_value (const_tree valtype, const_tree func ATTRIBUTE_UNUSED,
                     enum machine_mode mode)
 {
   if (valtype)
@@ -7480,23 +9223,25 @@ mips_function_value (tree valtype, tree func ATTRIBUTE_UNUSED,
        return gen_rtx_REG (mode, GP_RETURN);
     }
 
-  if ((GET_MODE_CLASS (mode) == MODE_FLOAT
-       || GET_MODE_CLASS (mode) == MODE_VECTOR_FLOAT)
-      && GET_MODE_SIZE (mode) <= UNITS_PER_HWFPVALUE)
-    return gen_rtx_REG (mode, FP_RETURN);
-
-  /* Handle long doubles for n32 & n64.  */
-  if (mode == TFmode)
-    return mips_return_fpr_pair (mode,
-                                DImode, 0,
-                                DImode, GET_MODE_SIZE (mode) / 2);
+  if (!TARGET_MIPS16)
+    {
+      /* Handle long doubles for n32 & n64.  */
+      if (mode == TFmode)
+       return mips_return_fpr_pair (mode,
+                                    DImode, 0,
+                                    DImode, GET_MODE_SIZE (mode) / 2);
 
-  if (GET_MODE_CLASS (mode) == MODE_COMPLEX_FLOAT
-      && GET_MODE_SIZE (mode) <= UNITS_PER_HWFPVALUE * 2)
-    return mips_return_fpr_pair (mode,
-                                GET_MODE_INNER (mode), 0,
-                                GET_MODE_INNER (mode),
-                                GET_MODE_SIZE (mode) / 2);
+      if (mips_return_mode_in_fpr_p (mode))
+       {
+         if (GET_MODE_CLASS (mode) == MODE_COMPLEX_FLOAT)
+           return mips_return_fpr_pair (mode,
+                                        GET_MODE_INNER (mode), 0,
+                                        GET_MODE_INNER (mode),
+                                        GET_MODE_SIZE (mode) / 2);
+         else
+           return gen_rtx_REG (mode, FP_RETURN);
+       }
+    }
 
   return gen_rtx_REG (mode, GP_RETURN);
 }
@@ -7505,7 +9250,7 @@ mips_function_value (tree valtype, tree func ATTRIBUTE_UNUSED,
 
 static bool
 mips_pass_by_reference (CUMULATIVE_ARGS *cum ATTRIBUTE_UNUSED,
-                       enum machine_mode mode, tree type,
+                       enum machine_mode mode, const_tree type,
                        bool named ATTRIBUTE_UNUSED)
 {
   if (mips_abi == ABI_EABI)
@@ -7513,7 +9258,9 @@ mips_pass_by_reference (CUMULATIVE_ARGS *cum ATTRIBUTE_UNUSED,
       int size;
 
       /* ??? How should SCmode be handled?  */
-      if (mode == DImode || mode == DFmode)
+      if (mode == DImode || mode == DFmode
+         || mode == DQmode || mode == UDQmode
+         || mode == DAmode || mode == UDAmode)
        return 0;
 
       size = type ? int_size_in_bytes (type) : GET_MODE_SIZE (mode);
@@ -7529,7 +9276,7 @@ mips_pass_by_reference (CUMULATIVE_ARGS *cum ATTRIBUTE_UNUSED,
 static bool
 mips_callee_copies (CUMULATIVE_ARGS *cum ATTRIBUTE_UNUSED,
                    enum machine_mode mode ATTRIBUTE_UNUSED,
-                   tree type ATTRIBUTE_UNUSED, bool named)
+                   const_tree type ATTRIBUTE_UNUSED, bool named)
 {
   return mips_abi == ABI_EABI && named;
 }
@@ -7537,56 +9284,39 @@ mips_callee_copies (CUMULATIVE_ARGS *cum ATTRIBUTE_UNUSED,
 /* Return true if registers of class CLASS cannot change from mode FROM
    to mode TO.  */
 
-bool
-mips_cannot_change_mode_class (enum machine_mode from,
-                              enum machine_mode to, 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 (FP_INC > 1 && reg_classes_intersect_p (FP_REGS, class))
-           return true;
-       }
-      else
-       {
-         /* LO_REGNO == HI_REGNO + 1, so if a multi-word value is stored
-            in LO and HI, the high word always comes first.  We therefore
-            can't allow values stored in HI to change between single-word
-            and multi-word modes.
-            This rule applies to both the original HI/LO pair and the new
-            DSP accumulators.  */
-         if (reg_classes_intersect_p (ACC_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.  */
-  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;
+bool
+mips_cannot_change_mode_class (enum machine_mode from ATTRIBUTE_UNUSED,
+                              enum machine_mode to ATTRIBUTE_UNUSED,
+                              enum reg_class class)
+{
+  /* There are several problems with changing the modes of values
+     in floating-point registers:
 
-  /* 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;
+     - 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.
 
-  return false;
+     - 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.
+
+     - 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.
@@ -7596,13 +9326,31 @@ mips_cannot_change_mode_class (enum machine_mode from,
 bool
 mips_dangerous_for_la25_p (rtx x)
 {
-  rtx offset;
+  return (!TARGET_EXPLICIT_RELOCS
+         && TARGET_USE_GOT
+         && GET_CODE (x) == SYMBOL_REF
+         && mips_global_symbol_p (x));
+}
 
-  if (TARGET_EXPLICIT_RELOCS)
-    return false;
+/* Return true if moves in mode MODE can use the FPU's mov.fmt instruction.  */
 
-  split_const (x, &x, &offset);
-  return global_got_operand (x, VOIDmode);
+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.  */
@@ -7613,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))
@@ -7637,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;
 }
@@ -7789,12 +9504,30 @@ mips_vector_mode_supported_p (enum machine_mode mode)
 
     case V2HImode:
     case V4QImode:
+    case V2HQmode:
+    case V2UHQmode:
+    case V2HAmode:
+    case V2UHAmode:
+    case V4QQmode:
+    case V4UQQmode:
       return TARGET_DSP;
 
     default:
       return false;
     }
 }
+
+/* Implement TARGET_SCALAR_MODE_SUPPORTED_P.  */
+
+static bool
+mips_scalar_mode_supported_p (enum machine_mode mode)
+{
+  if (ALL_FIXED_POINT_MODE_P (mode)
+      && GET_MODE_PRECISION (mode) <= 2 * BITS_PER_WORD)
+    return true;
+
+  return default_scalar_mode_supported_p (mode);
+}
 \f
 /* If we can access small data directly (using gp-relative relocation
    operators) return the small data pointer, otherwise return null.
@@ -7807,32 +9540,32 @@ static rtx
 mips16_gp_pseudo_reg (void)
 {
   if (cfun->machine->mips16_gp_pseudo_rtx == NULL_RTX)
+    cfun->machine->mips16_gp_pseudo_rtx = gen_reg_rtx (Pmode);
+
+  /* Don't initialize the pseudo register if we are being called from
+     the tree optimizers' cost-calculation routines.  */
+  if (!cfun->machine->initialized_mips16_gp_pseudo_p
+      && (current_ir_type () != IR_GIMPLE || currently_expanding_to_rtl))
     {
-      rtx unspec;
       rtx insn, scan;
 
-      cfun->machine->mips16_gp_pseudo_rtx = gen_reg_rtx (Pmode);
-
       /* We want to initialize this to a value which gcc will believe
          is constant.  */
-      start_sequence ();
-      unspec = gen_rtx_UNSPEC (VOIDmode, gen_rtvec (1, const0_rtx), UNSPEC_GP);
-      emit_move_insn (cfun->machine->mips16_gp_pseudo_rtx,
-                     gen_rtx_CONST (Pmode, unspec));
-      insn = get_insns ();
-      end_sequence ();
+      insn = gen_load_const_gp (cfun->machine->mips16_gp_pseudo_rtx);
 
       push_topmost_sequence ();
       /* We need to emit the initialization after the FUNCTION_BEG
          note, so that it will be integrated.  */
       for (scan = get_insns (); scan != NULL_RTX; scan = NEXT_INSN (scan))
        if (NOTE_P (scan)
-           && NOTE_LINE_NUMBER (scan) == NOTE_INSN_FUNCTION_BEG)
+           && NOTE_KIND (scan) == NOTE_INSN_FUNCTION_BEG)
          break;
       if (scan == NULL_RTX)
        scan = get_insns ();
       insn = emit_insn_after (insn, scan);
       pop_topmost_sequence ();
+
+      cfun->machine->initialized_mips16_gp_pseudo_p = true;
     }
 
   return cfun->machine->mips16_gp_pseudo_rtx;
@@ -7850,6 +9583,7 @@ mips16_fp_args (FILE *file, int fp_code, int from_fp_p)
   const char *s;
   int gparg, fparg;
   unsigned int f;
+  CUMULATIVE_ARGS cum;
 
   /* This code only works for the original 32-bit ABI and the O64 ABI.  */
   gcc_assert (TARGET_OLDABI);
@@ -7858,43 +9592,50 @@ mips16_fp_args (FILE *file, int fp_code, int from_fp_p)
     s = "mfc1";
   else
     s = "mtc1";
-  gparg = GP_ARG_FIRST;
-  fparg = FP_ARG_FIRST;
+
+  init_cumulative_args (&cum, NULL, NULL);
+
   for (f = (unsigned int) fp_code; f != 0; f >>= 2)
     {
+      enum machine_mode mode;
+      struct mips_arg_info info;
+
       if ((f & 3) == 1)
-       {
-         if ((fparg & 1) != 0)
-           ++fparg;
-         fprintf (file, "\t%s\t%s,%s\n", s,
-                  reg_names[gparg], reg_names[fparg]);
-       }
+       mode = SFmode;
       else if ((f & 3) == 2)
-       {
-         if (TARGET_64BIT)
-           fprintf (file, "\td%s\t%s,%s\n", s,
-                    reg_names[gparg], reg_names[fparg]);
-         else
-           {
-             if ((fparg & 1) != 0)
-               ++fparg;
-             if (TARGET_BIG_ENDIAN)
-               fprintf (file, "\t%s\t%s,%s\n\t%s\t%s,%s\n", s,
-                        reg_names[gparg], reg_names[fparg + 1], s,
-                        reg_names[gparg + 1], reg_names[fparg]);
-             else
-               fprintf (file, "\t%s\t%s,%s\n\t%s\t%s,%s\n", s,
-                        reg_names[gparg], reg_names[fparg], s,
-                        reg_names[gparg + 1], reg_names[fparg + 1]);
-             ++gparg;
-             ++fparg;
-           }
-       }
+       mode = DFmode;
       else
        gcc_unreachable ();
 
-      ++gparg;
-      ++fparg;
+      mips_arg_info (&cum, mode, NULL, true, &info);
+      gparg = mips_arg_regno (&info, false);
+      fparg = mips_arg_regno (&info, true);
+
+      if (mode == SFmode)
+       fprintf (file, "\t%s\t%s,%s\n", s,
+                reg_names[gparg], reg_names[fparg]);
+      else if (TARGET_64BIT)
+       fprintf (file, "\td%s\t%s,%s\n", s,
+                reg_names[gparg], reg_names[fparg]);
+      else if (ISA_HAS_MXHC1)
+       /* -mips32r2 -mfp64 */
+       fprintf (file, "\t%s\t%s,%s\n\t%s\t%s,%s\n", 
+                s,
+                reg_names[gparg + (WORDS_BIG_ENDIAN ? 1 : 0)],
+                reg_names[fparg],
+                from_fp_p ? "mfhc1" : "mthc1",
+                reg_names[gparg + (WORDS_BIG_ENDIAN ? 0 : 1)],
+                reg_names[fparg]);
+      else if (TARGET_BIG_ENDIAN)
+       fprintf (file, "\t%s\t%s,%s\n\t%s\t%s,%s\n", s,
+                reg_names[gparg], reg_names[fparg + 1], s,
+                reg_names[gparg + 1], reg_names[fparg]);
+      else
+       fprintf (file, "\t%s\t%s,%s\n\t%s\t%s,%s\n", s,
+                reg_names[gparg], reg_names[fparg], s,
+                reg_names[gparg + 1], reg_names[fparg + 1]);
+
+      function_arg_advance (&cum, mode, NULL, true);
     }
 }
 
@@ -7913,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);
@@ -7921,6 +9663,7 @@ build_mips16_function_stub (FILE *file)
   stubdecl = build_decl (FUNCTION_DECL, stubid,
                         build_function_type (void_type_node, NULL_TREE));
   DECL_SECTION_NAME (stubdecl) = build_string (strlen (secname), secname);
+  DECL_RESULT (stubdecl) = build_decl (RESULT_DECL, NULL_TREE, void_type_node);
 
   fprintf (file, "\t# Stub function for %s (", current_function_name ());
   need_comma = 0;
@@ -7977,8 +9720,6 @@ build_mips16_function_stub (FILE *file)
       fputs ("\n", file);
     }
 
-  fprintf (file, "\t.set\tmips16\n");
-
   switch_to_section (function_section (current_function_decl));
 }
 
@@ -7994,6 +9735,47 @@ struct mips16_stub
 
 static struct mips16_stub *mips16_stubs;
 
+/* Emit code to return a double value from a mips16 stub.  GPREG is the
+   first GP reg to use, FPREG is the first FP reg to use.  */
+
+static void
+mips16_fpret_double (int gpreg, int fpreg)
+{
+  if (TARGET_64BIT)
+    fprintf (asm_out_file, "\tdmfc1\t%s,%s\n",
+            reg_names[gpreg], reg_names[fpreg]);
+  else if (TARGET_FLOAT64)
+    {
+      fprintf (asm_out_file, "\tmfc1\t%s,%s\n",
+              reg_names[gpreg + WORDS_BIG_ENDIAN],
+              reg_names[fpreg]);
+      fprintf (asm_out_file, "\tmfhc1\t%s,%s\n",
+              reg_names[gpreg + !WORDS_BIG_ENDIAN],
+              reg_names[fpreg]);
+    }
+  else
+    {
+      if (TARGET_BIG_ENDIAN)
+       {
+         fprintf (asm_out_file, "\tmfc1\t%s,%s\n",
+                  reg_names[gpreg + 0],
+                  reg_names[fpreg + 1]);
+         fprintf (asm_out_file, "\tmfc1\t%s,%s\n",
+                  reg_names[gpreg + 1],
+                  reg_names[fpreg + 0]);
+       }
+      else
+       {
+         fprintf (asm_out_file, "\tmfc1\t%s,%s\n",
+                  reg_names[gpreg + 0],
+                  reg_names[fpreg + 0]);
+         fprintf (asm_out_file, "\tmfc1\t%s,%s\n",
+                  reg_names[gpreg + 1],
+                  reg_names[fpreg + 1]);
+       }
+    }
+}
+
 /* Build a call stub for a mips16 call.  A stub is needed if we are
    passing any floating point values which should go into the floating
    point registers.  If we are, and the call turns out to be to a
@@ -8015,24 +9797,24 @@ static struct mips16_stub *mips16_stubs;
 int
 build_mips16_call_stub (rtx retval, rtx fn, rtx arg_size, int fp_code)
 {
-  int fpret;
+  int fpret = 0;
   const char *fnname;
   char *secname, *stubname;
   struct mips16_stub *l;
   tree stubid, stubdecl;
   int need_comma;
   unsigned int f;
+  rtx insn;
 
   /* We don't need to do anything if we aren't in mips16 mode, or if
      we were invoked with the -msoft-float option.  */
-  if (! TARGET_MIPS16 || ! mips16_hard_float)
+  if (!TARGET_MIPS16 || TARGET_SOFT_FLOAT_ABI)
     return 0;
 
   /* Figure out whether the value might come back in a floating point
      register.  */
-  fpret = (retval != 0
-          && GET_MODE_CLASS (GET_MODE (retval)) == MODE_FLOAT
-          && GET_MODE_SIZE (GET_MODE (retval)) <= UNITS_PER_FPVALUE);
+  if (retval)
+    fpret = mips_return_mode_in_fpr_p (GET_MODE (retval));
 
   /* We don't need to do anything if there were no floating point
      arguments and the value will not be returned in a floating point
@@ -8050,11 +9832,6 @@ build_mips16_call_stub (rtx retval, rtx fn, rtx arg_size, int fp_code)
      require more sophisticated support.  */
   gcc_assert (TARGET_OLDABI);
 
-  /* We can only handle SFmode and DFmode floating point return
-     values.  */
-  if (fpret)
-    gcc_assert (GET_MODE (retval) == SFmode || GET_MODE (retval) == DFmode);
-
   /* If we're calling via a function pointer, then we must always call
      via a stub.  There are magic stubs provided in libgcc.a for each
      of the required cases.  Each of them expects the function address
@@ -8069,15 +9846,18 @@ build_mips16_call_stub (rtx retval, rtx fn, rtx arg_size, int fp_code)
       /* ??? If this code is modified to support other ABI's, we need
          to handle PARALLEL return values here.  */
 
-      sprintf (buf, "__mips16_call_stub_%s%d",
-              (fpret
-               ? (GET_MODE (retval) == SFmode ? "sf_" : "df_")
-               : ""),
-              fp_code);
+      if (fpret)
+       sprintf (buf, "__mips16_call_stub_%s_%d",
+                mips16_call_stub_mode_suffix (GET_MODE (retval)),
+                fp_code);
+      else
+       sprintf (buf, "__mips16_call_stub_%d",
+                fp_code);
+
       id = get_identifier (buf);
       stub_fn = gen_rtx_SYMBOL_REF (Pmode, IDENTIFIER_POINTER (id));
 
-      emit_move_insn (gen_rtx_REG (Pmode, 2), fn);
+      mips_emit_move (gen_rtx_REG (Pmode, 2), fn);
 
       if (retval == NULL_RTX)
        insn = gen_call_internal (stub_fn, arg_size);
@@ -8093,7 +9873,7 @@ build_mips16_call_stub (rtx retval, rtx fn, rtx arg_size, int fp_code)
 
       /* If we are handling a floating point return value, we need to
          save $18 in the function prologue.  Putting a note on the
-         call will mean that regs_ever_live[$18] will be true if the
+         call will mean that df_regs_ever_live_p ($18) will be true if the
          call is not eliminated, and we can check that in the prologue
          code.  */
       if (fpret)
@@ -8111,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;
@@ -8149,6 +9929,7 @@ build_mips16_call_stub (rtx retval, rtx fn, rtx arg_size, int fp_code)
       stubdecl = build_decl (FUNCTION_DECL, stubid,
                             build_function_type (void_type_node, NULL_TREE));
       DECL_SECTION_NAME (stubdecl) = build_string (strlen (secname), secname);
+      DECL_RESULT (stubdecl) = build_decl (RESULT_DECL, NULL_TREE, void_type_node);
 
       fprintf (asm_out_file, "\t# Stub function to call %s%s (",
               (fpret
@@ -8208,29 +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
+         switch (GET_MODE (retval))
            {
-             if (TARGET_BIG_ENDIAN)
+           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]);
+             /* 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.  */
@@ -8250,8 +10045,6 @@ build_mips16_call_stub (rtx retval, rtx fn, rtx arg_size, int fp_code)
          fputs ("\n", asm_out_file);
        }
 
-      fprintf (asm_out_file, "\t.set\tmips16\n");
-
       /* Record this stub.  */
       l = (struct mips16_stub *) xmalloc (sizeof *l);
       l->name = xstrdup (fnname);
@@ -8270,34 +10063,26 @@ build_mips16_call_stub (rtx retval, rtx fn, rtx arg_size, int fp_code)
   if (fpret && ! l->fpret)
     error ("cannot handle inconsistent calls to %qs", fnname);
 
+  if (retval == NULL_RTX)
+    insn = gen_call_internal_direct (fn, arg_size);
+  else
+    insn = gen_call_value_internal_direct (retval, fn, arg_size);
+  insn = emit_call_insn (insn);
+
   /* If we are calling a stub which handles a floating point return
      value, we need to arrange to save $18 in the prologue.  We do
      this by marking the function call as using the register.  The
      prologue will later see that it is used, and emit code to save
      it.  */
-
   if (l->fpret)
-    {
-      rtx insn;
-
-      if (retval == NULL_RTX)
-       insn = gen_call_internal (fn, arg_size);
-      else
-       insn = gen_call_value_internal (retval, fn, arg_size);
-      insn = emit_call_insn (insn);
-
-      CALL_INSN_FUNCTION_USAGE (insn) =
-       gen_rtx_EXPR_LIST (VOIDmode,
-                          gen_rtx_USE (VOIDmode, gen_rtx_REG (word_mode, 18)),
-                          CALL_INSN_FUNCTION_USAGE (insn));
-
-      /* Return 1 to tell the caller that we've generated the call
-         insn.  */
-      return 1;
-    }
+    CALL_INSN_FUNCTION_USAGE (insn) =
+      gen_rtx_EXPR_LIST (VOIDmode,
+                        gen_rtx_USE (VOIDmode, gen_rtx_REG (word_mode, 18)),
+                        CALL_INSN_FUNCTION_USAGE (insn));
 
-  /* Return 0 to let the caller generate the call insn.  */
-  return 0;
+  /* Return 1 to tell the caller that we've generated the call
+     insn.  */
+  return 1;
 }
 
 /* An entry in the mips16 constant pool.  VALUE is the pool constant,
@@ -8383,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 ();
 }
 
 
@@ -8455,18 +10238,57 @@ mips16_insn_length (rtx insn)
   return get_attr_length (insn);
 }
 
+/* If *X is a symbolic constant that refers to the constant pool, add
+   the constant to POOL and rewrite *X to use the constant's label.  */
+
+static void
+mips16_rewrite_pool_constant (struct mips16_constant_pool *pool, rtx *x)
+{
+  rtx base, offset, label;
+
+  split_const (*x, &base, &offset);
+  if (GET_CODE (base) == SYMBOL_REF && CONSTANT_POOL_ADDRESS_P (base))
+    {
+      label = add_constant (pool, get_pool_constant (base),
+                           get_pool_mode (base));
+      base = gen_rtx_LABEL_REF (Pmode, label);
+      *x = mips_unspec_address_offset (base, offset, SYMBOL_PC_RELATIVE);
+    }
+}
+
+/* This structure is used to communicate with mips16_rewrite_pool_refs.
+   INSN is the instruction we're rewriting and POOL points to the current
+   constant pool.  */
+struct mips16_rewrite_pool_refs_info {
+  rtx insn;
+  struct mips16_constant_pool *pool;
+};
+
 /* Rewrite *X so that constant pool references refer to the constant's
-   label instead.  DATA points to the constant pool structure.  */
+   label instead.  DATA points to a mips16_rewrite_pool_refs_info
+   structure.  */
 
 static int
 mips16_rewrite_pool_refs (rtx *x, void *data)
 {
-  struct mips16_constant_pool *pool = data;
-  if (GET_CODE (*x) == SYMBOL_REF && CONSTANT_POOL_ADDRESS_P (*x))
-    *x = gen_rtx_LABEL_REF (Pmode, add_constant (pool,
-                                                get_pool_constant (*x),
-                                                get_pool_mode (*x)));
-  return 0;
+  struct mips16_rewrite_pool_refs_info *info = data;
+
+  if (force_to_mem_operand (*x, Pmode))
+    {
+      rtx mem = force_const_mem (GET_MODE (*x), *x);
+      validate_change (info->insn, x, mem, false);
+    }
+
+  if (MEM_P (*x))
+    {
+      mips16_rewrite_pool_constant (info->pool, &XEXP (*x, 0));
+      return -1;
+    }
+
+  if (TARGET_MIPS16_TEXT_LOADS)
+    mips16_rewrite_pool_constant (info->pool, x);
+
+  return GET_CODE (*x) == CONST ? -1 : 0;
 }
 
 /* Build MIPS16 constant pools.  */
@@ -8475,15 +10297,23 @@ static void
 mips16_lay_out_constants (void)
 {
   struct mips16_constant_pool pool;
+  struct mips16_rewrite_pool_refs_info info;
   rtx insn, barrier;
 
+  if (!TARGET_MIPS16_PCREL_LOADS)
+    return;
+
   barrier = 0;
   memset (&pool, 0, sizeof (pool));
   for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
     {
       /* Rewrite constant pool references in INSN.  */
       if (INSN_P (insn))
-       for_each_rtx (&PATTERN (insn), mips16_rewrite_pool_refs, &pool);
+       {
+         info.insn = insn;
+         info.pool = &pool;
+         for_each_rtx (&PATTERN (insn), mips16_rewrite_pool_refs, &info);
+       }
 
       pool.insn_address += mips16_insn_length (insn);
 
@@ -8665,7 +10495,7 @@ mips_sim_wait_insn (struct mips_sim *state, rtx insn)
    in simulation state DATA.  */
 
 static void
-mips_sim_record_set (rtx x, rtx pat ATTRIBUTE_UNUSED, void *data)
+mips_sim_record_set (rtx x, const_rtx pat ATTRIBUTE_UNUSED, void *data)
 {
   struct mips_sim *state;
   unsigned int i;
@@ -9033,9 +10863,8 @@ mips_avoid_hazards (void)
 static void
 mips_reorg (void)
 {
-  if (TARGET_MIPS16)
-    mips16_lay_out_constants ();
-  else if (TARGET_EXPLICIT_RELOCS)
+  mips16_lay_out_constants ();
+  if (TARGET_EXPLICIT_RELOCS)
     {
       if (mips_flag_delayed_branch)
        dbr_schedule (get_insns ());
@@ -9062,7 +10891,7 @@ mips_init_libfuncs (void)
       set_optab_libfunc (smod_optab, SImode, "__vr4120_modsi3");
     }
 
-  if (TARGET_MIPS16 && mips16_hard_float)
+  if (TARGET_MIPS16 && TARGET_HARD_FLOAT_ABI)
     {
       set_optab_libfunc (add_optab, SFmode, "__mips16_addsf3");
       set_optab_libfunc (sub_optab, SFmode, "__mips16_subsf3");
@@ -9075,9 +10904,11 @@ mips_init_libfuncs (void)
       set_optab_libfunc (ge_optab, SFmode, "__mips16_gesf2");
       set_optab_libfunc (lt_optab, SFmode, "__mips16_ltsf2");
       set_optab_libfunc (le_optab, SFmode, "__mips16_lesf2");
+      set_optab_libfunc (unord_optab, SFmode, "__mips16_unordsf2");
 
       set_conv_libfunc (sfix_optab, SImode, SFmode, "__mips16_fix_truncsfsi");
       set_conv_libfunc (sfloat_optab, SFmode, SImode, "__mips16_floatsisf");
+      set_conv_libfunc (ufloat_optab, SFmode, SImode, "__mips16_floatunsisf");
 
       if (TARGET_DOUBLE_FLOAT)
        {
@@ -9092,12 +10923,14 @@ mips_init_libfuncs (void)
          set_optab_libfunc (ge_optab, DFmode, "__mips16_gedf2");
          set_optab_libfunc (lt_optab, DFmode, "__mips16_ltdf2");
          set_optab_libfunc (le_optab, DFmode, "__mips16_ledf2");
+         set_optab_libfunc (unord_optab, DFmode, "__mips16_unorddf2");
 
          set_conv_libfunc (sext_optab, DFmode, SFmode, "__mips16_extendsfdf2");
          set_conv_libfunc (trunc_optab, SFmode, DFmode, "__mips16_truncdfsf2");
 
          set_conv_libfunc (sfix_optab, SImode, DFmode, "__mips16_fix_truncdfsi");
          set_conv_libfunc (sfloat_optab, DFmode, SImode, "__mips16_floatsidf");
+         set_conv_libfunc (ufloat_optab, DFmode, SImode, "__mips16_floatunsidf");
        }
     }
   else
@@ -9136,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 && GR_REG_CLASS_P (to))
-    return 2;
-  else if (from == M16_NA_REGS && GR_REG_CLASS_P (to))
-    return 2;
-  else if (GR_REG_CLASS_P (from))
+  if (TARGET_MIPS16)
     {
-      if (to == M16_REGS)
-       return 2;
-      else if (to == M16_NA_REGS)
-       return 2;
-      else if (GR_REG_CLASS_P (to))
+      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 (COP_REG_CLASS_P (to))
-       {
-         return 5;
+         /* Two MOVEs.  */
+         return 4;
        }
     }
-  else if (from == FP_REGS)
+  else if (reg_class_subset_p (from, GENERAL_REGS))
     {
-      if (GR_REG_CLASS_P (to))
-       return 4;
-      else if (to == FP_REGS)
+      if (reg_class_subset_p (to, GENERAL_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 (GR_REG_CLASS_P (to))
-       {
-         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 && GR_REG_CLASS_P (to))
-    return 4;
-  else if (COP_REG_CLASS_P (from))
+  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;
 }
 
@@ -9568,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
@@ -9594,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;
 }
@@ -9608,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;
 }
@@ -9640,7 +11458,7 @@ mips_hard_regno_nregs (int regno, enum machine_mode mode)
    course.  */
 
 static bool
-mips_return_in_memory (tree type, tree fndecl ATTRIBUTE_UNUSED)
+mips_return_in_memory (const_tree type, const_tree fndecl ATTRIBUTE_UNUSED)
 {
   if (TARGET_OLDABI)
     return (TYPE_MODE (type) == BLKmode);
@@ -9732,7 +11550,7 @@ static rtx vr4130_last_insn;
    if the instruction uses the value of register X.  */
 
 static void
-vr4130_true_reg_dependence_p_1 (rtx x, rtx pat ATTRIBUTE_UNUSED, void *data)
+vr4130_true_reg_dependence_p_1 (rtx x, const_rtx pat ATTRIBUTE_UNUSED, void *data)
 {
   rtx *insn_ptr = data;
   if (REG_P (x)
@@ -9762,7 +11580,8 @@ vr4130_true_reg_dependence_p (rtx insn)
 static bool
 vr4130_swap_insns_p (rtx insn1, rtx insn2)
 {
-  dep_link_t dep;
+  sd_iterator_def sd_it;
+  dep_t dep;
 
   /* Check for the following case:
 
@@ -9772,11 +11591,11 @@ vr4130_swap_insns_p (rtx insn1, rtx insn2)
 
      If INSN1 is the last instruction blocking X, it would better to
      choose (INSN1, X) over (INSN2, INSN1).  */
-  FOR_EACH_DEP_LINK (dep, INSN_FORW_DEPS (insn1))
-    if (DEP_LINK_KIND (dep) == REG_DEP_ANTI
-       && INSN_PRIORITY (DEP_LINK_CON (dep)) > INSN_PRIORITY (insn2)
-       && recog_memoized (DEP_LINK_CON (dep)) >= 0
-       && get_attr_vr4130_class (DEP_LINK_CON (dep)) == VR4130_CLASS_ALU)
+  FOR_EACH_DEP (insn1, SD_LIST_FORW, sd_it, dep)
+    if (DEP_TYPE (dep) == REG_DEP_ANTI
+       && INSN_PRIORITY (DEP_CON (dep)) > INSN_PRIORITY (insn2)
+       && recog_memoized (DEP_CON (dep)) >= 0
+       && get_attr_vr4130_class (DEP_CON (dep)) == VR4130_CLASS_ALU)
       return false;
 
   if (vr4130_last_insn != 0
@@ -9837,26 +11656,125 @@ mips_promote_ready (rtx *ready, int lower, int higher)
   ready[i] = new_head;
 }
 
-/* Implement TARGET_SCHED_REORDER.  */
+/* If the priority of the instruction at POS2 in the ready queue READY 
+   is within LIMIT units of that of the instruction at POS1, swap the 
+   instructions if POS2 is not already less than POS1.  */
 
-static int
-mips_sched_reorder (FILE *file ATTRIBUTE_UNUSED, int verbose ATTRIBUTE_UNUSED,
-                   rtx *ready, int *nreadyp, int cycle)
+static void
+mips_maybe_swap_ready (rtx *ready, int pos1, int pos2, int limit)
+{
+  if (pos1 < pos2
+      && INSN_PRIORITY (ready[pos1]) + limit >= INSN_PRIORITY (ready[pos2]))
+    {
+      rtx temp;
+      temp = ready[pos1];
+      ready[pos1] = ready[pos2];
+      ready[pos2] = temp;
+    }
+}
+
+/* Record whether last 74k AGEN instruction was a load or store.  */
+
+static enum attr_type mips_last_74k_agen_insn = TYPE_UNKNOWN;
+
+/* Initialize mips_last_74k_agen_insn from INSN.  A null argument
+   resets to TYPE_UNKNOWN state.  */
+
+static void
+mips_74k_agen_init (rtx insn)
 {
-  if (!reload_completed && TUNE_MACC_CHAINS)
+  if (!insn || !NONJUMP_INSN_P (insn))
+    mips_last_74k_agen_insn = TYPE_UNKNOWN;
+  else if (USEFUL_INSN_P (insn))
     {
-      if (cycle == 0)
-       mips_macc_chains_last_hilo = 0;
-      if (*nreadyp > 0)
-       mips_macc_chains_reorder (ready, *nreadyp);
+      enum attr_type type = get_attr_type (insn);
+      if (type == TYPE_LOAD || type == TYPE_STORE)
+       mips_last_74k_agen_insn = type;
     }
-  if (reload_completed && TUNE_MIPS4130 && !TARGET_VR4130_ALIGN)
+}
+
+/* A TUNE_74K helper function.  The 74K AGEN pipeline likes multiple
+   loads to be grouped together, and multiple stores to be grouped
+   together.  Swap things around in the ready queue to make this happen.  */
+
+static void
+mips_74k_agen_reorder (rtx *ready, int nready)
+{
+  int i;
+  int store_pos, load_pos;
+
+  store_pos = -1;
+  load_pos = -1;
+
+  for (i = nready - 1; i >= 0; i--)
     {
-      if (cycle == 0)
-       vr4130_last_insn = 0;
-      if (*nreadyp > 1)
-       vr4130_reorder (ready, *nreadyp);
+      rtx insn = ready[i];
+      if (USEFUL_INSN_P (insn))
+       switch (get_attr_type (insn))
+         {
+         case TYPE_STORE:
+           if (store_pos == -1)
+             store_pos = i;
+           break;
+           
+         case TYPE_LOAD:
+           if (load_pos == -1)
+             load_pos = i;
+           break;
+           
+         default:
+           break;
+         }
+    }
+  
+  if (load_pos == -1 || store_pos == -1)
+    return;
+  
+  switch (mips_last_74k_agen_insn)
+    {
+    case TYPE_UNKNOWN:
+      /* Prefer to schedule loads since they have a higher latency.  */
+    case TYPE_LOAD:
+      /* Swap loads to the front of the queue.  */
+      mips_maybe_swap_ready (ready, load_pos, store_pos, 4);
+      break;
+    case TYPE_STORE:
+      /* Swap stores to the front of the queue.  */
+      mips_maybe_swap_ready (ready, store_pos, load_pos, 4);
+      break;
+    default:
+      break;
     }
+}
+
+/* Implement TARGET_SCHED_INIT.  */
+
+static void
+mips_sched_init (FILE *file ATTRIBUTE_UNUSED, int verbose ATTRIBUTE_UNUSED,
+                int max_ready ATTRIBUTE_UNUSED)
+{
+  mips_macc_chains_last_hilo = 0;
+  vr4130_last_insn = 0;
+  mips_74k_agen_init (NULL_RTX);
+}
+
+/* Implement TARGET_SCHED_REORDER and TARG_SCHED_REORDER2.  */
+
+static int
+mips_sched_reorder (FILE *file ATTRIBUTE_UNUSED, int verbose ATTRIBUTE_UNUSED,
+                   rtx *ready, int *nreadyp, int cycle ATTRIBUTE_UNUSED)
+{
+  if (!reload_completed
+      && TUNE_MACC_CHAINS
+      && *nreadyp > 0)
+    mips_macc_chains_reorder (ready, *nreadyp);
+  if (reload_completed
+      && TUNE_MIPS4130
+      && !TARGET_VR4130_ALIGN
+      && *nreadyp > 1)
+    vr4130_reorder (ready, *nreadyp);
+  if (TUNE_74K)
+    mips_74k_agen_reorder (ready, *nreadyp);
   return mips_issue_rate ();
 }
 
@@ -9866,6 +11784,8 @@ static int
 mips_variable_issue (FILE *file ATTRIBUTE_UNUSED, int verbose ATTRIBUTE_UNUSED,
                     rtx insn, int more)
 {
+  if (TUNE_74K)
+    mips_74k_agen_init (insn);
   switch (GET_CODE (PATTERN (insn)))
     {
     case USE:
@@ -9884,12 +11804,16 @@ mips_variable_issue (FILE *file ATTRIBUTE_UNUSED, int verbose ATTRIBUTE_UNUSED,
 }
 \f
 /* Implement TARGET_SCHED_ADJUST_COST.  We assume that anti and output
-   dependencies have no cost.  */
+   dependencies have no cost, except on the 20Kc where output-dependence
+   is treated like input-dependence.  */
 
 static int
 mips_adjust_cost (rtx insn ATTRIBUTE_UNUSED, rtx link,
                  rtx dep ATTRIBUTE_UNUSED, int cost)
 {
+  if (REG_NOTE_KIND (link) == REG_DEP_OUTPUT
+      && TUNE_20KC)
+    return cost;
   if (REG_NOTE_KIND (link) != 0)
     return 0;
   return cost;
@@ -9902,6 +11826,17 @@ mips_issue_rate (void)
 {
   switch (mips_tune)
     {
+    case PROCESSOR_74KC:
+    case PROCESSOR_74KF2_1:
+    case PROCESSOR_74KF1_1:
+    case PROCESSOR_74KF3_2:
+      /* The 74k is not strictly quad-issue cpu, but can be seen as one
+        by the scheduler.  It can issue 1 ALU, 1 AGEN and 2 FPU insns,
+        but in reality only a maximum of 3 insns can be issued as the
+        floating point load/stores also require a slot in the AGEN pipe.  */
+     return 4;
+
+    case PROCESSOR_20KC:
     case PROCESSOR_R4130:
     case PROCESSOR_R5400:
     case PROCESSOR_R5500:
@@ -10357,6 +12292,13 @@ mips_expand_builtin (tree exp, rtx target, rtx subtarget ATTRIBUTE_UNUSED,
   fndecl = TREE_OPERAND (CALL_EXPR_FN (exp), 0);
   fcode = DECL_FUNCTION_CODE (fndecl);
 
+  if (TARGET_MIPS16)
+    {
+      error ("built-in function %qs not supported for MIPS16",
+            IDENTIFIER_POINTER (DECL_NAME (fndecl)));
+      return const0_rtx;
+    }
+
   bdesc = NULL;
   for (m = bdesc_arrays; m < &bdesc_arrays[ARRAY_SIZE (bdesc_arrays)]; m++)
     {
@@ -10763,7 +12705,7 @@ mips_builtin_branch_and_move (rtx condition, rtx target,
   done_label = gen_label_rtx ();
 
   /* First assume that CONDITION is false.  */
-  emit_move_insn (target, value_if_false);
+  mips_emit_move (target, value_if_false);
 
   /* Branch to TRUE_LABEL if CONDITION is true and DONE_LABEL otherwise.  */
   emit_jump_insn (gen_condjump (condition, true_label));
@@ -10772,7 +12714,7 @@ mips_builtin_branch_and_move (rtx condition, rtx target,
 
   /* Fix TARGET if CONDITION is true.  */
   emit_label (true_label);
-  emit_move_insn (target, value_if_true);
+  mips_emit_move (target, value_if_true);
 
   emit_label (done_label);
   return target;
@@ -10871,21 +12813,24 @@ mips_encode_section_info (tree decl, rtx rtl, int first)
 {
   default_encode_section_info (decl, rtl, first);
 
-  if (TREE_CODE (decl) == FUNCTION_DECL
-      && lookup_attribute ("long_call", TYPE_ATTRIBUTES (TREE_TYPE (decl))))
+  if (TREE_CODE (decl) == FUNCTION_DECL)
     {
       rtx symbol = XEXP (rtl, 0);
-      SYMBOL_REF_FLAGS (symbol) |= SYMBOL_FLAG_LONG_CALL;
+      tree type = TREE_TYPE (decl);
+
+      if ((TARGET_LONG_CALLS && !mips_near_type_p (type))
+         || mips_far_type_p (type))
+       SYMBOL_REF_FLAGS (symbol) |= SYMBOL_FLAG_LONG_CALL;
     }
 }
 
-/* Implement TARGET_EXTRA_LIVE_ON_ENTRY.  PIC_FUNCTION_ADDR_REGNUM is live
-   on entry to a function when generating -mshared abicalls code.  */
+/* Implement TARGET_EXTRA_LIVE_ON_ENTRY.  Some code models use the incoming
+   value of PIC_FUNCTION_ADDR_REGNUM to set up the global pointer.  */
 
 static void
 mips_extra_live_on_entry (bitmap regs)
 {
-  if (TARGET_ABICALLS && !TARGET_ABSOLUTE_ABICALLS)
+  if (TARGET_USE_GOT && !TARGET_ABSOLUTE_ABICALLS)
     bitmap_set_bit (regs, PIC_FUNCTION_ADDR_REGNUM);
 }
 
@@ -10900,4 +12845,26 @@ mips_mode_rep_extended (enum machine_mode mode, enum machine_mode mode_rep)
   return UNKNOWN;
 }
 \f
+/* MIPS implementation of TARGET_ASM_OUTPUT_DWARF_DTPREL.  */
+
+static void
+mips_output_dwarf_dtprel (FILE *file, int size, rtx x)
+{
+  switch (size)
+    {
+    case 4:
+      fputs ("\t.dtprelword\t", file);
+      break;
+
+    case 8:
+      fputs ("\t.dtpreldword\t", file);
+      break;
+
+    default:
+      gcc_unreachable ();
+    }
+  output_addr_const (file, x);
+  fputs ("+0x8000", file);
+}
+\f
 #include "gt-mips.h"