OSDN Git Service

config/
[pf3gnuchains/gcc-fork.git] / gcc / config / mips / mips.c
index ba36ece..3655618 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,9 +402,9 @@ 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);
@@ -411,9 +423,12 @@ 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 (tree, tree);
+static int mips_comp_type_attributes (const_tree, const_tree);
+static void mips_set_mips16_mode (int);
+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.  */
@@ -462,6 +477,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.  */
@@ -539,6 +558,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.  */
 
@@ -588,11 +619,20 @@ 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_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;
@@ -612,6 +652,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;
@@ -649,7 +690,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,
@@ -688,6 +729,9 @@ const struct attribute_spec mips_attribute_table[] =
   { "long_call",   0, 0, false, true,  true,  NULL },
   { "far",                0, 0, false, true,  true,  NULL },
   { "near",        0, 0, false, true,  true,  NULL },
+  /* Switch MIPS16 ASE on and off per-function.  */
+  { "mips16",     0, 0, false, true,  true,  NULL },
+  { "nomips16",    0, 0, false, true,  true,  NULL },
   { NULL,         0, 0, false, false, false, NULL }
 };
 \f
@@ -696,7 +740,8 @@ const struct attribute_spec mips_attribute_table[] =
    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[].  */
+   gas's mips_cpu_info_table[].  Please also make sure that
+   MIPS_ISA_LEVEL_SPEC handles all -march options correctly.  */
 const struct mips_cpu_info mips_cpu_info_table[] = {
   /* Entries for generic ISAs */
   { "mips1", PROCESSOR_R3000, 1 },
@@ -739,24 +784,43 @@ const struct mips_cpu_info mips_cpu_info_table[] = {
   { "4kc", PROCESSOR_4KC, 32 },
   { "4km", PROCESSOR_4KC, 32 }, /* = 4kc */
   { "4kp", PROCESSOR_4KP, 32 },
+  { "4ksc", PROCESSOR_4KC, 32 },
 
   /* 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 */
+  { "4ksd", PROCESSOR_4KC, 33 },
+
+  { "24kc", PROCESSOR_24KC, 33 },
+  { "24kf2_1", PROCESSOR_24KF2_1, 33 },
+  { "24kf", PROCESSOR_24KF2_1, 33 },
+  { "24kf1_1", PROCESSOR_24KF1_1, 33 },
+  { "24kfx", PROCESSOR_24KF1_1, 33 },
+  { "24kx", PROCESSOR_24KF1_1, 33 },
+
   { "24kec", PROCESSOR_24KC, 33 }, /* 24K with DSP */
-  { "24kef", PROCESSOR_24KF, 33 },
-  { "24kex", PROCESSOR_24KX, 33 },
-  { "34kc", PROCESSOR_24KC, 33 },  /* 34K with MT/DSP */
-  { "34kf", PROCESSOR_24KF, 33 },
-  { "34kx", PROCESSOR_24KX, 33 },
-  { "74kc", PROCESSOR_74KC, 33 },
-  { "74kf", PROCESSOR_74KF, 33 },
-  { "74kx", PROCESSOR_74KX, 33 },
+  { "24kef2_1", PROCESSOR_24KF2_1, 33 },
+  { "24kef", PROCESSOR_24KF2_1, 33 },
+  { "24kef1_1", PROCESSOR_24KF1_1, 33 },
+  { "24kefx", PROCESSOR_24KF1_1, 33 },
+  { "24kex", PROCESSOR_24KF1_1, 33 },
+
+  { "34kc", PROCESSOR_24KC, 33 }, /* 34K with MT/DSP */
+  { "34kf2_1", PROCESSOR_24KF2_1, 33 },
+  { "34kf", PROCESSOR_24KF2_1, 33 },
+  { "34kf1_1", PROCESSOR_24KF1_1, 33 },
+  { "34kfx", PROCESSOR_24KF1_1, 33 },
+  { "34kx", PROCESSOR_24KF1_1, 33 },
+
+  { "74kc", PROCESSOR_74KC, 33 }, /* 74K with DSPr2 */
+  { "74kf2_1", PROCESSOR_74KF2_1, 33 },
+  { "74kf", PROCESSOR_74KF2_1, 33 },
+  { "74kf1_1", PROCESSOR_74KF1_1, 33 },
+  { "74kfx", PROCESSOR_74KF1_1, 33 },
+  { "74kx", PROCESSOR_74KF1_1, 33 },
+  { "74kf3_2", PROCESSOR_74KF3_2, 33 },
 
   /* MIPS64 */
   { "5kc", PROCESSOR_5KC, 64 },
@@ -864,7 +928,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,
@@ -875,7 +949,7 @@ 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 */
@@ -888,7 +962,7 @@ static struct mips_rtx_cost_data const mips_rtx_cost_data[PROCESSOR_MAX] =
                        1,           /* branch_cost */
                        4            /* memory_latency */
     },
-    { /* 24KX */
+    { /* 24KF1_1 */
       COSTS_N_INSNS (4),            /* fp_add */
       COSTS_N_INSNS (4),            /* fp_mult_sf */
       COSTS_N_INSNS (5),            /* fp_mult_df */
@@ -910,7 +984,7 @@ static struct mips_rtx_cost_data const mips_rtx_cost_data[PROCESSOR_MAX] =
                        1,           /* branch_cost */
                        4            /* memory_latency */
     },
-    { /* 74KF */
+    { /* 74KF2_1 */
       COSTS_N_INSNS (8),            /* fp_add */
       COSTS_N_INSNS (8),            /* fp_mult_sf */
       COSTS_N_INSNS (10),           /* fp_mult_df */
@@ -923,7 +997,7 @@ static struct mips_rtx_cost_data const mips_rtx_cost_data[PROCESSOR_MAX] =
                        1,           /* branch_cost */
                        4            /* memory_latency */
     },
-    { /* 74KX */
+    { /* 74KF1_1 */
       COSTS_N_INSNS (4),            /* fp_add */
       COSTS_N_INSNS (4),            /* fp_mult_sf */
       COSTS_N_INSNS (5),            /* fp_mult_df */
@@ -936,6 +1010,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
     },
@@ -1112,8 +1199,24 @@ static struct mips_rtx_cost_data const mips_rtx_cost_data[PROCESSOR_MAX] =
     },
   };
 
+/* 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
-/* Nonzero if -march should decide the default value of MASK_SOFT_FLOAT.  */
+/* Nonzero if -march should decide the default value of
+   MASK_SOFT_FLOAT_ABI.  */
 #ifndef MIPS_MARCH_CONTROLS_SOFT_FLOAT
 #define MIPS_MARCH_CONTROLS_SOFT_FLOAT 0
 #endif
@@ -1135,8 +1238,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
@@ -1161,6 +1268,9 @@ 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_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
@@ -1188,11 +1298,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
@@ -1202,7 +1312,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
@@ -1255,6 +1365,11 @@ static struct mips_rtx_cost_data const mips_rtx_cost_data[PROCESSOR_MAX] =
 #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;
 
 
@@ -1262,25 +1377,38 @@ struct gcc_target targetm = TARGET_INITIALIZER;
    attributes on the given TYPE.  */
 
 static bool
-mips_near_type_p (tree type)
+mips_near_type_p (const_tree type)
 {
   return lookup_attribute ("near", TYPE_ATTRIBUTES (type)) != NULL;
 }
 
 static bool
-mips_far_type_p (tree type)
+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_type_p (const_tree type)
+{
+  return lookup_attribute ("mips16", TYPE_ATTRIBUTES (type)) != NULL;
+}
+
+static bool
+mips_nomips16_type_p (const_tree type)
+{
+  return lookup_attribute ("nomips16", TYPE_ATTRIBUTES (type)) != 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 (tree type1, tree type2)
+mips_comp_type_attributes (const_tree type1, const_tree type2)
 {
   /* Check for mismatch of non-default calling convention.  */
   if (TREE_CODE (type1) != FUNCTION_TYPE)
@@ -1292,18 +1420,40 @@ mips_comp_type_attributes (tree type1, tree type2)
   if (mips_near_type_p (type1) && mips_far_type_p (type2))
     return 0;
 
+  /* Mips16/nomips16 attributes must match exactly.  */
+  if (mips_nomips16_type_p (type1) != mips_nomips16_type_p (type2)
+      || mips_mips16_type_p (type1) != mips_mips16_type_p (type2))
+    return 0;
+
   return 1;
 }
 \f
+/* 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
 /* Return true if SYMBOL_REF X is associated with a global symbol
    (in the STB_GLOBAL sense).  */
 
 static bool
-mips_global_symbol_p (rtx x)
+mips_global_symbol_p (const_rtx x)
 {
-  tree decl;
+  const_tree const decl = SYMBOL_REF_DECL (x);
 
-  decl = SYMBOL_REF_DECL (x);
   if (!decl)
     return !SYMBOL_REF_LOCAL_P (x);
 
@@ -1316,28 +1466,43 @@ mips_global_symbol_p (rtx x)
 /* Return true if SYMBOL_REF X binds locally.  */
 
 static bool
-mips_symbol_binds_local_p (rtx x)
+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));
 }
 
-/* Classify symbol X, which must be a SYMBOL_REF or a LABEL_REF.  */
+/* 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)
 {
   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_PAGE_OFST;
-      return SYMBOL_GENERAL;
+      return SYMBOL_ABSOLUTE;
     }
 
   gcc_assert (GET_CODE (x) == SYMBOL_REF);
@@ -1347,26 +1512,28 @@ 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 (GET_MODE_SIZE (get_pool_mode (x)) <= mips_section_threshold)
-       return SYMBOL_SMALL_DATA;
+      if (TARGET_MIPS16_PCREL_LOADS && context == SYMBOL_CONTEXT_MEM)
+       return SYMBOL_PC_RELATIVE;
+
+      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)))
     {
-      /* Don't use GOT accesses for locally-binding symbols; we can use
-        %hi and %lo instead.  */
-      if (TARGET_ABSOLUTE_ABICALLS && mips_symbol_binds_local_p (x))
-       return SYMBOL_GENERAL;
-
       /* There are three cases to consider:
 
            - o32 PIC (either with or without explicit relocs)
@@ -1392,7 +1559,24 @@ mips_classify_symbol (rtx x)
       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
@@ -1415,12 +1599,12 @@ mips_offset_within_alignment_p (rtx x, HOST_WIDE_INT offset)
   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;
 
@@ -1432,7 +1616,7 @@ mips_symbolic_constant_p (rtx x, enum mips_symbol_type *symbol_type)
     }
   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;
     }
@@ -1446,7 +1630,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:
@@ -1460,7 +1646,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.  */
@@ -1469,7 +1655,7 @@ 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.  */
@@ -1559,52 +1745,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_PAGE_OFST:
-      return true;
-
-    case SYMBOL_GOT_DISP:
-      /* The address will have to be loaded from the GOT first.  */
-      return false;
-
-    case SYMBOL_GOTOFF_PAGE:
-    case SYMBOL_GOTOFF_DISP:
-    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:
-    case SYMBOL_HALF:
-      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.  */
@@ -1633,9 +1773,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:
@@ -1648,8 +1798,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:
@@ -1705,32 +1856,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:
 
@@ -1741,20 +1887,39 @@ 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_HALF:
+    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:
+      /* The constant must be loaded from the constant pool.  */
+      return 0;
 
-    case SYMBOL_GOT_PAGE_OFST:
     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_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.
@@ -1781,6 +1946,7 @@ mips_symbol_insns (enum mips_symbol_type type)
     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:
@@ -1789,8 +1955,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.  */
@@ -1799,6 +1968,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
@@ -1837,22 +2022,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)
@@ -1870,7 +2059,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;
 }
@@ -1888,12 +2077,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)
@@ -1918,8 +2109,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
@@ -1940,7 +2131,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;
@@ -1948,14 +2140,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);
 }
 
 
@@ -1993,6 +2201,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.  */
@@ -2000,37 +2221,77 @@ 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 = mips_force_temporary (temp, gen_rtx_HIGH (Pmode, copy_rtx (addr)));
-  else if (no_new_pseudos)
+  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)
     {
-      emit_insn (gen_load_const_gp (copy_rtx (temp)));
-      high = temp;
+      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);
     }
-  else
-    high = mips16_gp_pseudo_reg ();
-  return gen_rtx_LO_SUM (Pmode, high, addr);
+  return true;
 }
 
 
+/* Wrap symbol or label BASE in an unspec address of type SYMBOL_TYPE
+   and add CONST_INT OFFSET to the result.  */
+
+static rtx
+mips_unspec_address_offset (rtx base, rtx offset,
+                           enum mips_symbol_type symbol_type)
+{
+  base = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, base),
+                        UNSPEC_ADDRESS_FIRST + symbol_type);
+  if (offset != const0_rtx)
+    base = gen_rtx_PLUS (Pmode, base, offset);
+  return gen_rtx_CONST (Pmode, base);
+}
+
 /* Return an UNSPEC address with underlying address ADDRESS and symbol
    type SYMBOL_TYPE.  */
 
@@ -2040,11 +2301,7 @@ mips_unspec_address (rtx address, 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)
-    base = gen_rtx_PLUS (Pmode, base, offset);
-  return gen_rtx_CONST (Pmode, base);
+  return mips_unspec_address_offset (base, offset, symbol_type);
 }
 
 
@@ -2142,6 +2399,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);
 
@@ -2218,8 +2481,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);
@@ -2227,13 +2488,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)
     {
@@ -2371,7 +2627,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;
@@ -2402,28 +2658,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;
     }
 
@@ -2431,9 +2688,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);
 }
 
 
@@ -2445,7 +2701,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;
     }
 
@@ -2587,6 +2843,26 @@ 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;
+}
+
 static bool
 mips_rtx_costs (rtx x, int code, int outer_code, int *total)
 {
@@ -2683,13 +2959,21 @@ mips_rtx_costs (rtx x, int code, int outer_code, int *total)
     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));
+          instructions it needs.  */
+       rtx addr = XEXP (x, 0);
+       int n = mips_address_insns (addr, GET_MODE (x), true);
        if (n > 0)
          {
            *total = COSTS_N_INSNS (n + 1);
            return true;
          }
+       /* Check for scaled indexed address.  */
+       if (mips_lwxs_address_p (addr))
+         {
+           *total = COSTS_N_INSNS (2);
+           return true;
+         }
+       /* Otherwise use the default handling.  */
        return false;
       }
 
@@ -2832,7 +3116,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
@@ -2854,13 +3138,8 @@ mips_subword (rtx op, int high_p)
   else
     byte = 0;
 
-  if (REG_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);
-    }
+  if (FP_REG_RTX_P (op))
+    return gen_rtx_REG (word_mode, high_p ? REGNO (op) + 1 : REGNO (op));
 
   if (MEM_P (op))
     return mips_rewrite_small_data (adjust_address (op, word_mode, byte));
@@ -2936,12 +3215,12 @@ mips_split_64bit_move (rtx dest, rtx src)
       if (ISA_HAS_MXHC1)
        {
          src = gen_lowpart (DFmode, src);
-         emit_move_insn (mips_subword (dest, 0), mips_subword (src, 0));
+         mips_emit_move (mips_subword (dest, 0), mips_subword (src, 0));
          emit_insn (gen_mfhc1 (mips_subword (dest, 1), src));
        }
       else
        {
-         emit_move_insn (mips_subword (dest, 0), mips_subword (src, 0));
+         mips_emit_move (mips_subword (dest, 0), mips_subword (src, 0));
          emit_insn (gen_store_df_high (mips_subword (dest, 1), src));
        }
     }
@@ -2955,13 +3234,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));
        }
     }
 }
@@ -2973,6 +3252,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);
@@ -3060,13 +3340,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)))
     {
@@ -3122,7 +3416,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 ());
 }
@@ -3511,7 +3805,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);
 }
 
 
@@ -3534,7 +3828,8 @@ mips_expand_call (rtx result, rtx addr, rtx args_size, rtx aux, int sibcall_p)
       mips_load_call_address (addr, orig_addr, sibcall_p);
     }
 
-  if (mips16_hard_float
+  if (TARGET_MIPS16
+      && TARGET_HARD_FLOAT_ABI
       && build_mips16_call_stub (result, addr, args_size,
                                 aux == 0 ? 0 : (int) GET_MODE (aux)))
     return;
@@ -3567,13 +3862,22 @@ mips_expand_call (rtx result, rtx addr, rtx args_size, rtx aux, int sibcall_p)
 }
 
 
-/* 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 (decl && SYMBOL_REF_MIPS16_FUNC_P (XEXP (DECL_RTL (decl), 0)))
+    return false;
+
+  /* Otherwise OK.  */
+  return true;
 }
 \f
 /* Emit code to move general operand SRC into condition-code
@@ -3601,8 +3905,8 @@ mips_emit_fcc_reload (rtx dest, rtx src, rtx scratch)
   fp1 = gen_rtx_REG (SFmode, REGNO (scratch));
   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
@@ -3620,7 +3924,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.
@@ -3658,7 +3962,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);
@@ -3670,7 +3974,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);
@@ -3743,8 +4047,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)
@@ -3758,6 +4062,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
@@ -4133,7 +4464,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.  */
@@ -4240,7 +4571,7 @@ mips_setup_incoming_varargs (CUMULATIVE_ARGS *cum, enum machine_mode mode,
              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;
            }
        }
@@ -4365,9 +4696,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);
 
@@ -4383,8 +4713,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);
 
@@ -4534,28 +4864,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
@@ -4569,9 +4898,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.  */
@@ -4792,15 +5120,245 @@ mips_set_tune (const struct mips_cpu_info *info)
     }
 }
 
-/* Implement TARGET_HANDLE_OPTION.  */
+/* Initialize mips_split_addresses from the associated command-line
+   settings.
 
-static bool
-mips_handle_option (size_t code, const char *arg, int value ATTRIBUTE_UNUSED)
+   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)
 {
-  switch (code)
+  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)
     {
-    case OPT_mabi_:
-      if (strcmp (arg, "32") == 0)
+      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_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;
+
+      /* 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;
+
+      /* Silently disable DSP extensions.  */
+      target_flags &= ~MASK_DSP;
+      target_flags &= ~MASK_DSPR2;
+    }
+  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;
+       }
+    }
+
+  /* (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;
+}
+
+/* Implement TARGET_SET_CURRENT_FUNCTION.  Decide whether the current 
+   function should use the MIPS16 ISA and switch modes accordingly.  */
+
+static void
+mips_set_current_function (tree fndecl)
+{
+  int mips16p;
+  if (errorcount || sorrycount)
+    /* Avoid generating RTL when fndecl is possibly invalid.  Best to fall
+       back on non-MIPS16 mode to avoid any strange secondary errors about
+       use of unsupported features in MIPS16 mode.  */
+    mips16p = false;
+  else if (fndecl)
+    mips16p = SYMBOL_REF_MIPS16_FUNC_P (XEXP (DECL_RTL (fndecl), 0));
+  else
+    mips16p = mips_base_mips16;
+  mips_set_mips16_mode (mips16p);
+}
+
+/* Implement TARGET_HANDLE_OPTION.  */
+
+static bool
+mips_handle_option (size_t code, const char *arg, int value ATTRIBUTE_UNUSED)
+{
+  switch (code)
+    {
+    case OPT_mabi_:
+      if (strcmp (arg, "32") == 0)
        mips_abi = ABI_32;
       else if (strcmp (arg, "o64") == 0)
        mips_abi = ABI_O64;
@@ -4826,6 +5384,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;
     }
@@ -4890,6 +5459,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
@@ -4946,21 +5520,21 @@ override_options (void)
     }
 
   if (MIPS_MARCH_CONTROLS_SOFT_FLOAT
-      && (target_flags_explicit & MASK_SOFT_FLOAT) == 0)
+      && (target_flags_explicit & MASK_SOFT_FLOAT_ABI) == 0)
     {
       /* For some configurations, it is useful to have -march control
-        the default setting of MASK_SOFT_FLOAT.  */
+        the default setting of MASK_SOFT_FLOAT_ABI.  */
       switch ((int) mips_arch)
        {
        case PROCESSOR_R4100:
        case PROCESSOR_R4111:
        case PROCESSOR_R4120:
        case PROCESSOR_R4130:
-         target_flags |= MASK_SOFT_FLOAT;
+         target_flags |= MASK_SOFT_FLOAT_ABI;
          break;
 
        default:
-         target_flags &= ~MASK_SOFT_FLOAT;
+         target_flags &= ~MASK_SOFT_FLOAT_ABI;
          break;
        }
     }
@@ -5003,39 +5577,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;
     }
 
-  if (TARGET_VXWORKS_RTP && mips_section_threshold > 0)
-    warning (0, "-G and -mrtp are incompatible");
-
-  /* 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
@@ -5043,37 +5601,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
@@ -5104,9 +5657,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;
@@ -5132,7 +5682,13 @@ override_options (void)
      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++)
@@ -5142,8 +5698,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.  */
 
@@ -5201,9 +5765,11 @@ override_options (void)
 
           else if (ACC_REG_P (regno))
            temp = (INTEGRAL_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);
@@ -5218,123 +5784,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_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(";
-
-  /* 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
@@ -5346,6 +5798,42 @@ 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_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.  */
@@ -5409,6 +5897,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.  */
@@ -5484,6 +5981,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",
@@ -5730,11 +6241,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')
     {
@@ -5821,30 +6332,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);
@@ -5866,7 +6373,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;
 
@@ -5876,7 +6384,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 ();
@@ -6047,15 +6555,17 @@ mips_file_start (void)
 
       /* 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,
@@ -6211,29 +6721,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
@@ -6245,12 +6766,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)
@@ -6337,7 +6868,7 @@ 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;
@@ -6346,7 +6877,7 @@ mips_global_pointer (void)
      register instead of $gp.  */
   if (TARGET_CALL_SAVED_GP && current_function_is_leaf)
     for (regno = GP_REG_FIRST; regno <= GP_REG_LAST; regno++)
-      if (!regs_ever_live[regno]
+      if (!df_regs_ever_live_p (regno)
          && call_used_regs[regno]
          && !fixed_regs[regno]
          && regno != PIC_FUNCTION_ADDR_REGNUM)
@@ -6395,7 +6926,8 @@ static bool
 mips16_cfun_returns_in_fpr_p (void)
 {
   tree return_type = DECL_RESULT (current_function_decl);
-  return (mips16_hard_float
+  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)));
 }
@@ -6412,15 +6944,15 @@ mips_save_reg_p (unsigned int 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 (df_regs_ever_live_p (regno) && !call_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 
-     && regs_ever_live[regno + 1]
+     && MAX_FPRS_PER_FMT == 2
+     && df_regs_ever_live_p (regno + 1)
      && !call_used_regs[regno + 1])
    return true;
 
@@ -6430,7 +6962,7 @@ mips_save_reg_p (unsigned int regno)
 
   /* 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])
+  if (regno == GP_REG_FIRST + 31 && df_regs_ever_live_p (regno))
     return true;
 
   if (TARGET_MIPS16)
@@ -6438,7 +6970,7 @@ mips_save_reg_p (unsigned int regno)
       /* $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])
+      if (regno == GP_REG_FIRST + 18 && df_regs_ever_live_p (regno))
        return true;
 
       /* $31 is also a special case.  It will be used to copy a return
@@ -6452,6 +6984,41 @@ mips_save_reg_p (unsigned int regno)
   return false;
 }
 
+/* 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.  */
+
+static unsigned int
+mips16e_find_first_register (unsigned int mask, const unsigned char *regs,
+                            unsigned int size)
+{
+  unsigned int i;
+
+  for (i = 0; i < size; i++)
+    if (BITSET_P (mask, regs[i]))
+      break;
+
+  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.
@@ -6459,10 +7026,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.      |
        |                       |       |                       |
         +-----------------------+      +-----------------------+
        |                       |       |                       |
@@ -6472,37 +7038,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     |
-                                       |                       |
-                                       +-----------------------+
-                                       |                       |
-                                        |  gp register save     |
-                                        |                      |
-                                       +-----------------------+
-                                       |                       |
-                                       |  local variables      |
-                                       |                       |
+        |  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    |
                                        +-----------------------+
-                                       |                       |
-                                        |  alloca allocations   |
-                                       |                       |
+                                       |                       |\
+                                       |  local variables      | | var_size
+                                       |                       |/
                                        +-----------------------+
                                        |                       |
-                                       |  GP save for V.4 abi  |
+                                       |  alloca allocations   |
                                        |                       |
                                        +-----------------------+
-                                       |                       |
-                                        |  arguments on stack   |
-                                       |                       |
+                                      /|                       |
+                      cprestore_size | |  GP save for V.4 abi  |
+                                      \|                       |
                                        +-----------------------+
-                                        |  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->+-----------------------+
 
 */
 
@@ -6568,6 +7137,17 @@ 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
      mips_for_each_saved_reg.  */
   for (regno = (FP_REG_LAST - MAX_FPRS_PER_FMT + 1);
@@ -6606,8 +7186,16 @@ compute_frame_size (HOST_WIDE_INT size)
     {
       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;
     }
@@ -6704,8 +7292,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;
@@ -6734,7 +7320,6 @@ mips_for_each_saved_reg (HOST_WIDE_INT sp_offset, mips_save_restore_fn fn)
        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
@@ -6830,10 +7415,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 (mips16_hard_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
@@ -6900,87 +7492,484 @@ mips_output_function_prologue (FILE *file, HOST_WIDE_INT size ATTRIBUTE_UNUSED)
   else if (cfun->machine->all_noreorder_p)
     output_asm_insn ("%(%<", 0);
 
-  /* Tell the assembler which register we're using as the global
-     pointer.  This is needed for thunks, since they can use either
-     explicit relocs or assembler macros.  */
-  mips_output_cplocal ();
+  /* Tell the assembler which register we're using as the global
+     pointer.  This is needed for thunks, since they can use either
+     explicit relocs or assembler macros.  */
+  mips_output_cplocal ();
+}
+\f
+/* Make the last instruction frame related and note that it performs
+   the operation described by FRAME_PATTERN.  */
+
+static void
+mips_set_frame_expr (rtx frame_pattern)
+{
+  rtx insn;
+
+  insn = get_last_insn ();
+  RTX_FRAME_RELATED_P (insn) = 1;
+  REG_NOTES (insn) = alloc_EXPR_LIST (REG_FRAME_RELATED_EXPR,
+                                     frame_pattern,
+                                     REG_NOTES (insn));
+}
+
+
+/* Return a frame-related rtx that stores REG at MEM.
+   REG must be a single register.  */
+
+static rtx
+mips_frame_set (rtx mem, rtx reg)
+{
+  rtx set;
+
+  /* If we're saving the return address register and the dwarf return
+     address column differs from the hard register number, adjust the
+     note reg to refer to the former.  */
+  if (REGNO (reg) == GP_REG_FIRST + 31
+      && DWARF_FRAME_RETURN_COLUMN != GP_REG_FIRST + 31)
+    reg = gen_rtx_REG (GET_MODE (reg), DWARF_FRAME_RETURN_COLUMN);
+
+  set = gen_rtx_SET (VOIDmode, mem, reg);
+  RTX_FRAME_RELATED_P (set) = 1;
+
+  return set;
+}
+
+
+/* Save register REG to MEM.  Make the instruction frame-related.  */
+
+static void
+mips_save_reg (rtx reg, rtx mem)
+{
+  if (GET_MODE (reg) == DFmode && !TARGET_FLOAT64)
+    {
+      rtx x1, x2;
+
+      if (mips_split_64bit_move_p (mem, reg))
+       mips_split_64bit_move (mem, reg);
+      else
+       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));
+      mips_set_frame_expr (gen_rtx_PARALLEL (VOIDmode, gen_rtvec (2, x1, x2)));
+    }
+  else
+    {
+      if (TARGET_MIPS16
+         && REGNO (reg) != GP_REG_FIRST + 31
+         && !M16_REG_P (REGNO (reg)))
+       {
+         /* 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.  */
+         mips_emit_move (MIPS_PROLOGUE_TEMP (GET_MODE (reg)), reg);
+         mips_emit_move (mem, MIPS_PROLOGUE_TEMP (GET_MODE (reg)));
+       }
+      else
+       mips_emit_move (mem, reg);
+
+      mips_set_frame_expr (mips_frame_set (mem, reg));
+    }
+}
+
+/* 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;
 }
-\f
-/* Make the last instruction frame related and note that it performs
-   the operation described by FRAME_PATTERN.  */
 
-static void
-mips_set_frame_expr (rtx frame_pattern)
+/* 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)
 {
-  rtx insn;
+  static char buffer[300];
 
-  insn = get_last_insn ();
-  RTX_FRAME_RELATED_P (insn) = 1;
-  REG_NOTES (insn) = alloc_EXPR_LIST (REG_FRAME_RELATED_EXPR,
-                                     frame_pattern,
-                                     REG_NOTES (insn));
+  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.
 
-/* Return a frame-related rtx that stores REG at MEM.
-   REG must be a single register.  */
+   This function is rather limited, but is good enough for our purposes.  */
 
 static rtx
-mips_frame_set (rtx mem, rtx reg)
+mips16e_collect_propagate_value (rtx x, rtx *reg_values)
 {
-  rtx set;
+  rtx x0, x1;
 
-  /* If we're saving the return address register and the dwarf return
-     address column differs from the hard register number, adjust the
-     note reg to refer to the former.  */
-  if (REGNO (reg) == GP_REG_FIRST + 31
-      && DWARF_FRAME_RETURN_COLUMN != GP_REG_FIRST + 31)
-    reg = gen_rtx_REG (GET_MODE (reg), DWARF_FRAME_RETURN_COLUMN);
+  x = avoid_constant_pool_reference (x);
 
-  set = gen_rtx_SET (VOIDmode, mem, reg);
-  RTX_FRAME_RELATED_P (set) = 1;
+  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)));
+    }
 
-  return set;
+  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.  */
 
-/* Save register REG to MEM.  Make the instruction frame-related.  */
+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;
 
-static void
-mips_save_reg (rtx reg, rtx mem)
+  /* 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)
 {
-  if (GET_MODE (reg) == DFmode && !TARGET_FLOAT64)
+  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)
     {
-      rtx x1, x2;
+      next = NEXT_INSN (insn);
+      if (NOTE_P (insn))
+       continue;
 
-      if (mips_split_64bit_move_p (mem, reg))
-       mips_split_64bit_move (mem, reg);
-      else
-       emit_move_insn (mem, reg);
+      if (!INSN_P (insn))
+       break;
 
-      x1 = mips_frame_set (mips_subword (mem, 0), mips_subword (reg, 0));
-      x2 = mips_frame_set (mips_subword (mem, 1), mips_subword (reg, 1));
-      mips_set_frame_expr (gen_rtx_PARALLEL (VOIDmode, gen_rtvec (2, x1, x2)));
-    }
-  else
-    {
-      if (TARGET_MIPS16
-         && REGNO (reg) != GP_REG_FIRST + 31
-         && !M16_REG_P (REGNO (reg)))
+      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))
        {
-         /* 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)));
+         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
-       emit_move_insn (mem, reg);
-
-      mips_set_frame_expr (mips_frame_set (mem, reg));
+       break;
     }
-}
+  pop_topmost_sequence ();
 
+  return nargs;
+}
 
 /* Expand the prologue into a bunch of separate insns.  */
 
@@ -6988,9 +7977,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 ());
 
@@ -7002,11 +7993,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.  */
@@ -7018,7 +8037,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
@@ -7026,11 +8045,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,
@@ -7060,8 +8079,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)));
@@ -7072,7 +8091,7 @@ 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;
     }
 
@@ -7099,7 +8118,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)
@@ -7137,11 +8156,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);
 }
 
 
@@ -7225,7 +8244,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);
        }
 
@@ -7238,7 +8257,7 @@ 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, $gp is implicitly used by all
      SYMBOL_REFs.  We must emit a blockage insn before restoring $gp
@@ -7246,15 +8265,40 @@ mips_expand_epilogue (int sibcall_p)
   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.  */
@@ -7262,11 +8306,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,
@@ -7276,8 +8320,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
@@ -7296,7 +8343,7 @@ mips_can_use_return_insn (void)
   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;
 
   /* In mips16 mode, a function that returns a floating point value
@@ -7322,18 +8369,27 @@ mips_output_mi_thunk (FILE *file, tree thunk_fndecl ATTRIBUTE_UNUSED,
   rtx this, temp1, temp2, insn, fnaddr;
 
   /* Pretend to be a post-reload pass while generating rtl.  */
-  no_new_pseudos = 1;
   reload_completed = 1;
 
+  /* Mark the end of the (empty) prologue.  */
+  emit_note (NOTE_INSN_PROLOGUE_END);
+
   /* Pick a global pointer.  Use a call-clobbered register if
      TARGET_CALL_SAVED_GP, so that we can use a sibcall.  */
   if (TARGET_USE_GOT)
-    cfun->machine->global_pointer
-      = REGNO (pic_offset_table_rtx)
-      = TARGET_CALL_SAVED_GP ? 15 : GLOBAL_POINTER_REGNUM;
+    {
+      cfun->machine->global_pointer =
+       TARGET_CALL_SAVED_GP ? 15 : GLOBAL_POINTER_REGNUM;
 
-  /* Set up the global pointer for n32 or n64 abicalls.  */
-  mips_emit_loadgp ();
+      SET_REGNO (pic_offset_table_rtx, cfun->machine->global_pointer);
+    }
+
+  /* Set up the global pointer for n32 or n64 abicalls.  If
+     LOADGP_ABSOLUTE then the thunk does not use the gp and there is
+     no need to load it.*/
+  if (mips_current_loadgp_style () != LOADGP_ABSOLUTE
+      || !targetm.binds_local_p (function))
+    mips_emit_loadgp ();
 
   /* We need two temporary registers in some cases.  */
   temp1 = gen_rtx_REG (Pmode, 2);
@@ -7351,7 +8407,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));
@@ -7363,20 +8419,21 @@ 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_USE_GOT || SYMBOL_REF_LONG_CALL_P (fnaddr))
+  if (TARGET_MIPS16 || TARGET_USE_GOT || SYMBOL_REF_LONG_CALL_P (fnaddr)
+      || SYMBOL_REF_MIPS16_FUNC_P (fnaddr))
     {
       /* This is messy.  gas treats "la $25,foo" as part of a call
         sequence and may allow a global "foo" to be lazily bound.
@@ -7397,7 +8454,7 @@ mips_output_mi_thunk (FILE *file, tree thunk_fndecl ATTRIBUTE_UNUSED,
 
       if (TARGET_USE_PIC_FN_ADDR_REG
          && REGNO (temp1) != PIC_FUNCTION_ADDR_REGNUM)
-       emit_move_insn (gen_rtx_REG (Pmode, PIC_FUNCTION_ADDR_REGNUM), temp1);
+       mips_emit_move (gen_rtx_REG (Pmode, PIC_FUNCTION_ADDR_REGNUM), temp1);
       emit_jump_insn (gen_indirect_jump (temp1));
     }
   else
@@ -7411,8 +8468,7 @@ mips_output_mi_thunk (FILE *file, tree thunk_fndecl ATTRIBUTE_UNUSED,
   insn = get_insns ();
   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);
@@ -7421,64 +8477,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.
@@ -7521,7 +8532,7 @@ 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;
 
@@ -7544,7 +8555,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)
@@ -7560,6 +8571,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);
 }
@@ -7570,12 +8594,12 @@ 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:
@@ -7593,7 +8617,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;
@@ -7633,7 +8657,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];
 
@@ -7678,7 +8702,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)
@@ -7754,7 +8778,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)
@@ -7778,7 +8802,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;
 }
@@ -7802,17 +8826,6 @@ mips_cannot_change_mode_class (enum machine_mode from,
          if (MAX_FPRS_PER_FMT > 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
@@ -8053,11 +9066,15 @@ 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 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.  */
       insn = gen_load_const_gp (cfun->machine->mips16_gp_pseudo_rtx);
@@ -8073,6 +9090,8 @@ mips16_gp_pseudo_reg (void)
        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;
@@ -8226,8 +9245,6 @@ build_mips16_function_stub (FILE *file)
       fputs ("\n", file);
     }
 
-  fprintf (file, "\t.set\tmips16\n");
-
   switch_to_section (function_section (current_function_decl));
 }
 
@@ -8315,7 +9332,7 @@ build_mips16_call_stub (rtx retval, rtx fn, rtx arg_size, int fp_code)
 
   /* 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 (!mips16_hard_float)
+  if (!TARGET_MIPS16 || TARGET_SOFT_FLOAT_ABI)
     return 0;
 
   /* Figure out whether the value might come back in a floating point
@@ -8364,7 +9381,7 @@ build_mips16_call_stub (rtx retval, rtx fn, rtx arg_size, int 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);
@@ -8380,7 +9397,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)
@@ -8559,8 +9576,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);
@@ -8771,11 +9786,23 @@ 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;
+  rtx base, offset, label;
+
+  if (MEM_P (*x))
+    x = &XEXP (*x, 0);
+  else if (!TARGET_MIPS16_TEXT_LOADS)
+    return 0;
+
+  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);
+      return -1;
+    }
+  return GET_CODE (*x) == CONST ? -1 : 0;
 }
 
 /* Build MIPS16 constant pools.  */
@@ -8786,6 +9813,9 @@ mips16_lay_out_constants (void)
   struct mips16_constant_pool pool;
   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))
@@ -8974,7 +10004,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;
@@ -9342,9 +10372,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 ());
@@ -9371,7 +10400,7 @@ mips_init_libfuncs (void)
       set_optab_libfunc (smod_optab, SImode, "__vr4120_modsi3");
     }
 
-  if (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");
@@ -9384,9 +10413,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)
        {
@@ -9401,12 +10432,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
@@ -9448,17 +10481,17 @@ int
 mips_register_move_cost (enum machine_mode mode ATTRIBUTE_UNUSED,
                         enum reg_class to, enum reg_class from)
 {
-  if (from == M16_REGS && GR_REG_CLASS_P (to))
+  if (from == M16_REGS && reg_class_subset_p (to, GENERAL_REGS))
     return 2;
-  else if (from == M16_NA_REGS && GR_REG_CLASS_P (to))
+  else if (from == M16_NA_REGS && reg_class_subset_p (to, GENERAL_REGS))
     return 2;
-  else if (GR_REG_CLASS_P (from))
+  else if (reg_class_subset_p (from, GENERAL_REGS))
     {
       if (to == M16_REGS)
        return 2;
       else if (to == M16_NA_REGS)
        return 2;
-      else if (GR_REG_CLASS_P (to))
+      else if (reg_class_subset_p (to, GENERAL_REGS))
        {
          if (TARGET_MIPS16)
            return 4;
@@ -9474,14 +10507,14 @@ mips_register_move_cost (enum machine_mode mode ATTRIBUTE_UNUSED,
          else
            return 6;
        }
-      else if (COP_REG_CLASS_P (to))
+      else if (reg_class_subset_p (to, ALL_COP_REGS))
        {
          return 5;
        }
     }
   else if (from == FP_REGS)
     {
-      if (GR_REG_CLASS_P (to))
+      if (reg_class_subset_p (to, GENERAL_REGS))
        return 4;
       else if (to == FP_REGS)
        return 2;
@@ -9490,7 +10523,7 @@ mips_register_move_cost (enum machine_mode mode ATTRIBUTE_UNUSED,
     }
   else if (reg_class_subset_p (from, ACC_REGS))
     {
-      if (GR_REG_CLASS_P (to))
+      if (reg_class_subset_p (to, GENERAL_REGS))
        {
          if (TARGET_MIPS16)
            return 12;
@@ -9498,9 +10531,9 @@ mips_register_move_cost (enum machine_mode mode ATTRIBUTE_UNUSED,
            return 6;
        }
     }
-  else if (from == ST_REGS && GR_REG_CLASS_P (to))
+  else if (from == ST_REGS && reg_class_subset_p (to, GENERAL_REGS))
     return 4;
-  else if (COP_REG_CLASS_P (from))
+  else if (reg_class_subset_p (from, ALL_COP_REGS))
     {
       return 5;
     }
@@ -9949,7 +10982,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);
@@ -10041,7 +11074,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)
@@ -10071,7 +11104,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:
 
@@ -10081,11 +11115,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
@@ -10146,26 +11180,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 (!insn || !NONJUMP_INSN_P (insn))
+    mips_last_74k_agen_insn = TYPE_UNKNOWN;
+  else if (USEFUL_INSN_P (insn))
+    {
+      enum attr_type type = get_attr_type (insn);
+      if (type == TYPE_LOAD || type == TYPE_STORE)
+       mips_last_74k_agen_insn = type;
+    }
+}
+
+/* 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)
 {
-  if (!reload_completed && TUNE_MACC_CHAINS)
+  int i;
+  int store_pos, load_pos;
+
+  store_pos = -1;
+  load_pos = -1;
+
+  for (i = nready - 1; i >= 0; i--)
     {
-      if (cycle == 0)
-       mips_macc_chains_last_hilo = 0;
-      if (*nreadyp > 0)
-       mips_macc_chains_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 (reload_completed && TUNE_MIPS4130 && !TARGET_VR4130_ALIGN)
+  
+  if (load_pos == -1 || store_pos == -1)
+    return;
+  
+  switch (mips_last_74k_agen_insn)
     {
-      if (cycle == 0)
-       vr4130_last_insn = 0;
-      if (*nreadyp > 1)
-       vr4130_reorder (ready, *nreadyp);
+    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 ();
 }
 
@@ -10175,6 +11308,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:
@@ -10193,12 +11328,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;
@@ -10212,8 +11351,16 @@ mips_issue_rate (void)
   switch (mips_tune)
     {
     case PROCESSOR_74KC:
-    case PROCESSOR_74KF:
-    case PROCESSOR_74KX:
+    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:
@@ -10669,6 +11816,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++)
     {
@@ -11075,7 +12229,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));
@@ -11084,7 +12238,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;
@@ -11175,6 +12329,43 @@ mips_expand_builtin_bposge (enum mips_builtin_type builtin_type, rtx target)
                                       const1_rtx, const0_rtx);
 }
 \f
+/* Return true if we should force MIPS16 mode for the function named by
+   the SYMBOL_REF SYMBOL, which belongs to DECL and has type TYPE.
+   FIRST is true if this is the first time handling this decl.  */
+
+static bool
+mips_use_mips16_mode_p (rtx symbol, tree decl, int first, tree type)
+{
+  tree parent;
+
+  /* Explicit function attributes take precedence.  */
+  if (mips_mips16_type_p (type))
+    return true;
+  if (mips_nomips16_type_p (type))
+    return false;
+
+  /* A nested function should inherit the MIPS16 setting from its parent.  */
+  parent = decl_function_context (decl);
+  if (parent)
+    return SYMBOL_REF_MIPS16_FUNC_P (XEXP (DECL_RTL (parent), 0));
+
+  /* Handle -mflip-mips16.  */
+  if (TARGET_FLIP_MIPS16
+      && !DECL_BUILT_IN (decl)
+      && !DECL_ARTIFICIAL (decl))
+    {
+      if (!first)
+       /* Use the setting we picked first time around.  */
+       return SYMBOL_REF_MIPS16_FUNC_P (symbol);
+
+      mips16_flipper = !mips16_flipper;
+      if (mips16_flipper)
+       return !mips_base_mips16;
+    }
+
+  return mips_base_mips16;
+}
+
 /* Set SYMBOL_REF_FLAGS for the SYMBOL_REF inside RTL, which belongs to DECL.
    FIRST is true if this is the first time handling this decl.  */
 
@@ -11186,10 +12377,19 @@ mips_encode_section_info (tree decl, rtx rtl, int first)
   if (TREE_CODE (decl) == FUNCTION_DECL)
     {
       rtx symbol = XEXP (rtl, 0);
+      tree type = TREE_TYPE (decl);
 
-      if ((TARGET_LONG_CALLS && !mips_near_type_p (TREE_TYPE (decl)))
-         || mips_far_type_p (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;
+
+      if (mips_use_mips16_mode_p (symbol, decl, first, type))
+       {
+         if (flag_pic || TARGET_ABICALLS)
+           sorry ("MIPS16 PIC");
+         else
+           SYMBOL_REF_FLAGS (symbol) |= SYMBOL_FLAG_MIPS16_FUNC;
+       }
     }
 }
 
@@ -11214,4 +12414,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"