]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
mips.h: Move variable declarations to end of file and enclose them all in #ifndef...
authorRichard Sandiford <rsandifo@nildram.co.uk>
Thu, 18 Oct 2007 17:27:19 +0000 (17:27 +0000)
committerRichard Sandiford <rsandifo@gcc.gnu.org>
Thu, 18 Oct 2007 17:27:19 +0000 (17:27 +0000)
gcc/
* config/mips/mips.h: Move variable declarations to end of file and
enclose them all in #ifndef USED_FOR_TARGET.
* config/mips/mips.c: Reorder functions into more logical groups,
and so that callees appear before callers.  Put the targetm
initializer at the end of the file.  Remove forward static
declarations where possible.
(mips_init_builtins): Add "static" to definition.
(mips_expand_builtin, mips_mode_rep_extended): Likewise.

From-SVN: r129452

gcc/ChangeLog
gcc/config/mips/mips.c
gcc/config/mips/mips.h

index 39901e468c917366927a3a157fbc3959862cf986..35724afcc1245b02c29a3d986f11430c0bd91d84 100644 (file)
@@ -1,3 +1,14 @@
+2007-10-18  Richard Sandiford  <rsandifo@nildram.co.uk>
+
+       * config/mips/mips.h: Move variable declarations to end of file and
+       enclose them all in #ifndef USED_FOR_TARGET.
+       * config/mips/mips.c: Reorder functions into more logical groups,
+       and so that callees appear before callers.  Put the targetm
+       initializer at the end of the file.  Remove forward static
+       declarations where possible.
+       (mips_init_builtins): Add "static" to definition.
+       (mips_expand_builtin, mips_mode_rep_extended): Likewise.
+
 2007-10-18  Richard Sandiford  <rsandifo@nildram.co.uk>
 
        * config/mips/mips.c (TARGET_MIN_ANCHOR_OFFSET): Delete.
index c460cca267d51bb236f65d48267ae1073838159b..e56d0454740d327c674a9207dd0055ff0f3a648b 100644 (file)
@@ -278,162 +278,6 @@ static const char *const mips_fp_conditions[] = {
   MIPS_FP_CONDITIONS (STRINGIFY)
 };
 
-/* A function to save or store a register.  The first argument is the
-   register and the second is the stack slot.  */
-typedef void (*mips_save_restore_fn) (rtx, rtx);
-
-struct mips16_constant;
-struct mips_arg_info;
-struct mips_address_info;
-struct mips_integer_op;
-struct mips_sim;
-
-static bool mips_valid_base_register_p (rtx, enum machine_mode, int);
-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, 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);
-static rtx mips_add_offset (rtx, rtx, HOST_WIDE_INT);
-static unsigned int mips_build_shift (struct mips_integer_op *, HOST_WIDE_INT);
-static unsigned int mips_build_lower (struct mips_integer_op *,
-                                     unsigned HOST_WIDE_INT);
-static unsigned int mips_build_integer (struct mips_integer_op *,
-                                       unsigned HOST_WIDE_INT);
-static void mips_legitimize_const_move (enum machine_mode, rtx, rtx);
-static int m16_check_op (rtx, int, int, int);
-static bool mips_rtx_costs (rtx, int, int, int *);
-static int mips_address_cost (rtx);
-static void mips_emit_compare (enum rtx_code *, rtx *, rtx *, bool);
-static bool mips_load_call_address (rtx, rtx, int);
-static bool mips_function_ok_for_sibcall (tree, tree);
-static void mips_block_move_straight (rtx, rtx, HOST_WIDE_INT);
-static void mips_adjust_block_mem (rtx, HOST_WIDE_INT, rtx *, rtx *);
-static void mips_block_move_loop (rtx, rtx, HOST_WIDE_INT);
-static void mips_arg_info (const CUMULATIVE_ARGS *, enum machine_mode,
-                          tree, int, struct mips_arg_info *);
-static bool mips_get_unaligned_mem (rtx *, unsigned int, int, rtx *, rtx *);
-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, enum mips_symbol_context,
-                                const char **);
-static void mips_file_start (void);
-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);
-static unsigned int mips_global_pointer        (void);
-static bool mips_save_reg_p (unsigned int);
-static void mips_save_restore_reg (enum machine_mode, int, HOST_WIDE_INT,
-                                  mips_save_restore_fn);
-static void mips_for_each_saved_reg (HOST_WIDE_INT, mips_save_restore_fn);
-static void mips_output_cplocal (void);
-static void mips_emit_loadgp (void);
-static void mips_output_function_prologue (FILE *, HOST_WIDE_INT);
-static void mips_set_frame_expr (rtx);
-static rtx mips_frame_set (rtx, rtx);
-static void mips_save_reg (rtx, rtx);
-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 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 (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);
-static rtx mips16_gp_pseudo_reg (void);
-static void mips16_fp_args (FILE *, int, int);
-static void build_mips16_function_stub (FILE *);
-static rtx dump_constants_1 (enum machine_mode, rtx, rtx);
-static void dump_constants (struct mips16_constant *, rtx);
-static int mips16_insn_length (rtx);
-static int mips16_rewrite_pool_refs (rtx *, void *);
-static void mips16_lay_out_constants (void);
-static void mips_sim_reset (struct mips_sim *);
-static void mips_sim_init (struct mips_sim *, state_t);
-static void mips_sim_next_cycle (struct mips_sim *);
-static void mips_sim_wait_reg (struct mips_sim *, rtx, rtx);
-static int mips_sim_wait_regs_2 (rtx *, void *);
-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, 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);
-static void vr4130_avoid_branch_rt_conflict (rtx);
-static void vr4130_align_insns (void);
-static void mips_avoid_hazard (rtx, rtx, int *, rtx *, rtx);
-static void mips_avoid_hazards (void);
-static void mips_reorg (void);
-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 (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, 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);
-static int mips_issue_rate (void);
-static int mips_multipass_dfa_lookahead (void);
-static void mips_init_libfuncs (void);
-static void mips_setup_incoming_varargs (CUMULATIVE_ARGS *, enum machine_mode,
-                                        tree, int *, int);
-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,
-                                   const_tree, bool);
-static bool mips_callee_copies (CUMULATIVE_ARGS *, enum machine_mode mode,
-                               const_tree, bool);
-static int mips_arg_partial_bytes (CUMULATIVE_ARGS *, enum machine_mode mode,
-                                  tree, bool);
-static bool mips_valid_pointer_mode (enum machine_mode);
-static bool mips_scalar_mode_supported_p (enum machine_mode);
-static bool mips_vector_mode_supported_p (enum machine_mode);
-static rtx mips_prepare_builtin_arg (enum insn_code, unsigned int, tree, unsigned int);
-static rtx mips_prepare_builtin_target (enum insn_code, unsigned int, rtx);
-static rtx mips_expand_builtin (tree, rtx, rtx, enum machine_mode, int);
-static void mips_init_builtins (void);
-static rtx mips_expand_builtin_direct (enum insn_code, rtx, tree, bool);
-static rtx mips_expand_builtin_movtf (enum mips_builtin_type,
-                                     enum insn_code, enum mips_fp_condition,
-                                     rtx, tree);
-static rtx mips_expand_builtin_compare (enum mips_builtin_type,
-                                       enum insn_code, enum mips_fp_condition,
-                                       rtx, tree);
-static rtx mips_expand_builtin_bposge (enum mips_builtin_type, rtx);
-static void mips_encode_section_info (tree, rtx, int);
-static void mips_extra_live_on_entry (bitmap);
-static int mips_comp_type_attributes (const_tree, const_tree);
-static void mips_set_mips16_mode (int);
-static void mips_insert_attributes (tree, tree *);
-static tree mips_merge_decl_attributes (tree, tree);
-static void mips_set_current_function (tree);
-static int mips_mode_rep_extended (enum machine_mode, enum machine_mode);
-static bool mips_offset_within_alignment_p (rtx, HOST_WIDE_INT);
-static void mips_output_dwarf_dtprel (FILE *, int, rtx) ATTRIBUTE_UNUSED;
-static rtx mips_dwarf_register_span (rtx);
-
 /* Structure to be filled in by compute_frame_size with register
    save masks, and offsets for the current function.  */
 
@@ -584,6 +428,9 @@ int mips_section_threshold = -1;
 /* Count the number of .file directives, so that .loc is up to date.  */
 int num_source_filenames = 0;
 
+/* Name of the file containing the current function.  */
+const char *current_function_file = "";
+
 /* Count the number of sdb related labels are generated (to find block
    start and end boundaries).  */
 int sdb_label_count = 0;
@@ -591,8 +438,9 @@ int sdb_label_count = 0;
 /* Next label # for each statement for Silicon Graphics IRIS systems.  */
 int sym_lineno = 0;
 
-/* Name of the file containing the current function.  */
-const char *current_function_file = "";
+/* Map GCC register number to debugger register number.  */
+int mips_dbx_regno[FIRST_PSEUDO_REGISTER];
+int mips_dwarf_regno[FIRST_PSEUDO_REGISTER];
 
 /* Number of nested .set noreorder, noat, nomacro, and volatile requests.  */
 int set_noreorder;
@@ -617,6 +465,9 @@ const struct mips_cpu_info *mips_tune_info;
 /* Which instruction set architecture to use.  */
 int mips_isa;
 
+/* The architecture selected by -mipsN.  */
+static const struct mips_cpu_info *mips_isa_info;
+
 /* Which ABI to use.  */
 int mips_abi = MIPS_ABI_DEFAULT;
 
@@ -628,20 +479,17 @@ 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_flag_delayed_branch; /* flag_delayed_branch */
 static int mips_base_schedule_insns; /* flag_schedule_insns */
 static int mips_base_reorder_blocks_and_partition; /* flag_reorder... */
 static int mips_base_move_loop_invariants; /* flag_move_loop_invariants */
 static int mips_base_align_loops; /* align_loops */
 static int mips_base_align_jumps; /* align_jumps */
 static int mips_base_align_functions; /* align_functions */
-static GTY(()) int mips16_flipper;
 
 /* The -mtext-loads setting.  */
 enum mips_code_readable_setting mips_code_readable = CODE_READABLE_YES;
 
-/* The architecture selected by -mipsN.  */
-static const struct mips_cpu_info *mips_isa_info;
-
 /* If TRUE, we split addresses into their high and low parts in the RTL.  */
 int mips_split_addresses;
 
@@ -655,13 +503,6 @@ char mips_hard_regno_mode_ok[(int)MAX_MACHINE_MODE][FIRST_PSEUDO_REGISTER];
 /* List of all MIPS punctuation characters used by print_operand.  */
 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;
-
 static GTY (()) int mips_output_filename_first_time = 1;
 
 /* mips_split_p[X] is true if symbols of type X can be split by
@@ -1210,185 +1051,68 @@ static struct mips_rtx_cost_data const mips_rtx_cost_data[PROCESSOR_MAX] =
       DEFAULT_COSTS
     },
   };
-
-/* If a MIPS16e SAVE or RESTORE instruction saves or restores register
-   mips16e_s2_s8_regs[X], it must also save the registers in indexes
-   X + 1 onwards.  Likewise mips16e_a0_a3_regs.  */
-static const unsigned char mips16e_s2_s8_regs[] = {
-  30, 23, 22, 21, 20, 19, 18
-};
-static const unsigned char mips16e_a0_a3_regs[] = {
-  4, 5, 6, 7
-};
-
-/* A list of the registers that can be saved by the MIPS16e SAVE instruction,
-   ordered from the uppermost in memory to the lowest in memory.  */
-static const unsigned char mips16e_save_restore_regs[] = {
-  31, 30, 23, 22, 21, 20, 19, 18, 17, 16, 7, 6, 5, 4
-};
 \f
-/* Initialize the GCC target structure.  */
-#undef TARGET_ASM_ALIGNED_HI_OP
-#define TARGET_ASM_ALIGNED_HI_OP "\t.half\t"
-#undef TARGET_ASM_ALIGNED_SI_OP
-#define TARGET_ASM_ALIGNED_SI_OP "\t.word\t"
-#undef TARGET_ASM_ALIGNED_DI_OP
-#define TARGET_ASM_ALIGNED_DI_OP "\t.dword\t"
-
-#undef TARGET_ASM_FUNCTION_PROLOGUE
-#define TARGET_ASM_FUNCTION_PROLOGUE mips_output_function_prologue
-#undef TARGET_ASM_FUNCTION_EPILOGUE
-#define TARGET_ASM_FUNCTION_EPILOGUE mips_output_function_epilogue
-#undef TARGET_ASM_SELECT_RTX_SECTION
-#define TARGET_ASM_SELECT_RTX_SECTION mips_select_rtx_section
-#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
-#define TARGET_SCHED_ADJUST_COST mips_adjust_cost
-#undef TARGET_SCHED_ISSUE_RATE
-#define TARGET_SCHED_ISSUE_RATE mips_issue_rate
-#undef TARGET_SCHED_FIRST_CYCLE_MULTIPASS_DFA_LOOKAHEAD
-#define TARGET_SCHED_FIRST_CYCLE_MULTIPASS_DFA_LOOKAHEAD \
-  mips_multipass_dfa_lookahead
-
-#undef TARGET_DEFAULT_TARGET_FLAGS
-#define TARGET_DEFAULT_TARGET_FLAGS            \
-  (TARGET_DEFAULT                              \
-   | TARGET_CPU_DEFAULT                                \
-   | TARGET_ENDIAN_DEFAULT                     \
-   | TARGET_FP_EXCEPTIONS_DEFAULT              \
-   | MASK_CHECK_ZERO_DIV                       \
-   | MASK_FUSED_MADD)
-#undef TARGET_HANDLE_OPTION
-#define TARGET_HANDLE_OPTION mips_handle_option
-
-#undef TARGET_FUNCTION_OK_FOR_SIBCALL
-#define TARGET_FUNCTION_OK_FOR_SIBCALL mips_function_ok_for_sibcall
-
-#undef TARGET_INSERT_ATTRIBUTES
-#define TARGET_INSERT_ATTRIBUTES mips_insert_attributes
-#undef TARGET_MERGE_DECL_ATTRIBUTES
-#define TARGET_MERGE_DECL_ATTRIBUTES mips_merge_decl_attributes
-#undef TARGET_SET_CURRENT_FUNCTION
-#define TARGET_SET_CURRENT_FUNCTION mips_set_current_function
-
-#undef TARGET_VALID_POINTER_MODE
-#define TARGET_VALID_POINTER_MODE mips_valid_pointer_mode
-#undef TARGET_RTX_COSTS
-#define TARGET_RTX_COSTS mips_rtx_costs
-#undef TARGET_ADDRESS_COST
-#define TARGET_ADDRESS_COST mips_address_cost
-
-#undef TARGET_IN_SMALL_DATA_P
-#define TARGET_IN_SMALL_DATA_P mips_in_small_data_p
-
-#undef TARGET_MACHINE_DEPENDENT_REORG
-#define TARGET_MACHINE_DEPENDENT_REORG mips_reorg
-
-#undef TARGET_ASM_FILE_START
-#define TARGET_ASM_FILE_START mips_file_start
-#undef TARGET_ASM_FILE_START_FILE_DIRECTIVE
-#define TARGET_ASM_FILE_START_FILE_DIRECTIVE true
-
-#undef TARGET_INIT_LIBFUNCS
-#define TARGET_INIT_LIBFUNCS mips_init_libfuncs
-
-#undef TARGET_BUILD_BUILTIN_VA_LIST
-#define TARGET_BUILD_BUILTIN_VA_LIST mips_build_builtin_va_list
-#undef TARGET_GIMPLIFY_VA_ARG_EXPR
-#define TARGET_GIMPLIFY_VA_ARG_EXPR mips_gimplify_va_arg_expr
-
-#undef TARGET_PROMOTE_FUNCTION_ARGS
-#define TARGET_PROMOTE_FUNCTION_ARGS hook_bool_const_tree_true
-#undef TARGET_PROMOTE_FUNCTION_RETURN
-#define TARGET_PROMOTE_FUNCTION_RETURN hook_bool_const_tree_true
-#undef TARGET_PROMOTE_PROTOTYPES
-#define TARGET_PROMOTE_PROTOTYPES hook_bool_const_tree_true
-
-#undef TARGET_RETURN_IN_MEMORY
-#define TARGET_RETURN_IN_MEMORY mips_return_in_memory
-#undef TARGET_RETURN_IN_MSB
-#define TARGET_RETURN_IN_MSB mips_return_in_msb
-
-#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_const_tree_hwi_hwi_const_tree_true
-
-#undef TARGET_SETUP_INCOMING_VARARGS
-#define TARGET_SETUP_INCOMING_VARARGS mips_setup_incoming_varargs
-#undef TARGET_STRICT_ARGUMENT_NAMING
-#define TARGET_STRICT_ARGUMENT_NAMING mips_strict_argument_naming
-#undef TARGET_MUST_PASS_IN_STACK
-#define TARGET_MUST_PASS_IN_STACK must_pass_in_stack_var_size
-#undef TARGET_PASS_BY_REFERENCE
-#define TARGET_PASS_BY_REFERENCE mips_pass_by_reference
-#undef TARGET_CALLEE_COPIES
-#define TARGET_CALLEE_COPIES mips_callee_copies
-#undef TARGET_ARG_PARTIAL_BYTES
-#define TARGET_ARG_PARTIAL_BYTES mips_arg_partial_bytes
-
-#undef TARGET_MODE_REP_EXTENDED
-#define TARGET_MODE_REP_EXTENDED mips_mode_rep_extended
-
-#undef TARGET_VECTOR_MODE_SUPPORTED_P
-#define TARGET_VECTOR_MODE_SUPPORTED_P mips_vector_mode_supported_p
-
-#undef TARGET_SCALAR_MODE_SUPPORTED_P
-#define TARGET_SCALAR_MODE_SUPPORTED_P mips_scalar_mode_supported_p
-
-#undef TARGET_INIT_BUILTINS
-#define TARGET_INIT_BUILTINS mips_init_builtins
-#undef TARGET_EXPAND_BUILTIN
-#define TARGET_EXPAND_BUILTIN mips_expand_builtin
-
-#undef TARGET_HAVE_TLS
-#define TARGET_HAVE_TLS HAVE_AS_TLS
-
-#undef TARGET_CANNOT_FORCE_CONST_MEM
-#define TARGET_CANNOT_FORCE_CONST_MEM mips_cannot_force_const_mem
+/* Use a hash table to keep track of implicit mips16/nomips16 attributes
+   for -mflip_mips16.  It maps decl names onto a boolean mode setting.  */
 
-#undef TARGET_ENCODE_SECTION_INFO
-#define TARGET_ENCODE_SECTION_INFO mips_encode_section_info
+struct mflip_mips16_entry GTY (()) {
+  const char *name;
+  bool mips16_p;
+};
+static GTY ((param_is (struct mflip_mips16_entry))) htab_t mflip_mips16_htab;
 
-#undef TARGET_ATTRIBUTE_TABLE
-#define TARGET_ATTRIBUTE_TABLE mips_attribute_table
-/* All our function attributes are related to how out-of-line copies should
-   be compiled or called.  They don't in themselves prevent inlining.  */
-#undef TARGET_FUNCTION_ATTRIBUTE_INLINABLE_P
-#define TARGET_FUNCTION_ATTRIBUTE_INLINABLE_P hook_bool_const_tree_true
+/* Hash table callbacks for mflip_mips16_htab.  */
 
-#undef TARGET_EXTRA_LIVE_ON_ENTRY
-#define TARGET_EXTRA_LIVE_ON_ENTRY mips_extra_live_on_entry
+static hashval_t
+mflip_mips16_htab_hash (const void *entry)
+{
+  return htab_hash_string (((const struct mflip_mips16_entry *) entry)->name);
+}
 
-#undef TARGET_USE_BLOCKS_FOR_CONSTANT_P
-#define TARGET_USE_BLOCKS_FOR_CONSTANT_P mips_use_blocks_for_constant_p
-#undef TARGET_USE_ANCHORS_FOR_SYMBOL_P
-#define TARGET_USE_ANCHORS_FOR_SYMBOL_P mips_use_anchors_for_symbol_p
+static int
+mflip_mips16_htab_eq (const void *entry, const void *name)
+{
+  return strcmp (((const struct mflip_mips16_entry *) entry)->name,
+                (const char *) name) == 0;
+}
 
-#undef  TARGET_COMP_TYPE_ATTRIBUTES
-#define TARGET_COMP_TYPE_ATTRIBUTES mips_comp_type_attributes
+static GTY(()) int mips16_flipper;
 
-#ifdef HAVE_AS_DTPRELWORD
-#undef TARGET_ASM_OUTPUT_DWARF_DTPREL
-#define TARGET_ASM_OUTPUT_DWARF_DTPREL mips_output_dwarf_dtprel
-#endif
+/* DECL is a function that needs a default "mips16" or "nomips16" attribute
+   for -mflip-mips16.  Return true if it should use "mips16" and false if
+   it should use "nomips16".  */
 
-#undef TARGET_DWARF_REGISTER_SPAN
-#define TARGET_DWARF_REGISTER_SPAN mips_dwarf_register_span
+static bool
+mflip_mips16_use_mips16_p (tree decl)
+{
+  struct mflip_mips16_entry *entry;
+  const char *name;
+  hashval_t hash;
+  void **slot;
 
-struct gcc_target targetm = TARGET_INITIALIZER;
+  /* Use the opposite of the command-line setting for anonymous decls.  */
+  if (!DECL_NAME (decl))
+    return !mips_base_mips16;
 
+  if (!mflip_mips16_htab)
+    mflip_mips16_htab = htab_create_ggc (37, mflip_mips16_htab_hash,
+                                        mflip_mips16_htab_eq, NULL);
 
+  name = IDENTIFIER_POINTER (DECL_NAME (decl));
+  hash = htab_hash_string (name);
+  slot = htab_find_slot_with_hash (mflip_mips16_htab, name, hash, INSERT);
+  entry = (struct mflip_mips16_entry *) *slot;
+  if (!entry)
+    {
+      mips16_flipper = !mips16_flipper;
+      entry = GGC_NEW (struct mflip_mips16_entry);
+      entry->name = name;
+      entry->mips16_p = mips16_flipper ? !mips_base_mips16 : mips_base_mips16;
+      *slot = entry;
+    }
+  return entry->mips16_p;
+}
+\f
 /* Predicates to test for presence of "near" and "far"/"long_call"
    attributes on the given TYPE.  */
 
@@ -1419,14 +1143,35 @@ mips_nomips16_decl_p (const_tree decl)
   return lookup_attribute ("nomips16", DECL_ATTRIBUTES (decl)) != NULL;
 }
 
-/* Return 0 if the attributes for two types are incompatible, 1 if they
-   are compatible, and 2 if they are nearly compatible (which causes a
-   warning to be generated).  */
+/* Return true if function DECL is a MIPS16 function.  Return the ambient
+   setting if DECL is null.  */
 
-static int
-mips_comp_type_attributes (const_tree type1, const_tree type2)
+static bool
+mips_use_mips16_mode_p (tree decl)
 {
-  /* Check for mismatch of non-default calling convention.  */
+  if (decl)
+    {
+      /* Nested functions must use the same frame pointer as their
+        parent and must therefore use the same ISA mode.  */
+      tree parent = decl_function_context (decl);
+      if (parent)
+       decl = parent;
+      if (mips_mips16_decl_p (decl))
+       return true;
+      if (mips_nomips16_decl_p (decl))
+       return false;
+    }
+  return mips_base_mips16;
+}
+
+/* Return 0 if the attributes for two types are incompatible, 1 if they
+   are compatible, and 2 if they are nearly compatible (which causes a
+   warning to be generated).  */
+
+static int
+mips_comp_type_attributes (const_tree type1, const_tree type2)
+{
+  /* Check for mismatch of non-default calling convention.  */
   if (TREE_CODE (type1) != FUNCTION_TYPE)
     return 1;
 
@@ -1438,6 +1183,64 @@ mips_comp_type_attributes (const_tree type1, const_tree type2)
 
   return 1;
 }
+
+/* Implement TARGET_INSERT_ATTRIBUTES.  */
+
+static void
+mips_insert_attributes (tree decl, tree *attributes)
+{
+  const char *name;
+  bool mips16_p, nomips16_p;
+
+  /* Check for "mips16" and "nomips16" attributes.  */
+  mips16_p = lookup_attribute ("mips16", *attributes) != NULL;
+  nomips16_p = lookup_attribute ("nomips16", *attributes) != NULL;
+  if (TREE_CODE (decl) != FUNCTION_DECL)
+    {
+      if (mips16_p)
+       error ("%qs attribute only applies to functions", "mips16");
+      if (nomips16_p)
+       error ("%qs attribute only applies to functions", "nomips16");
+    }
+  else
+    {
+      mips16_p |= mips_mips16_decl_p (decl);
+      nomips16_p |= mips_nomips16_decl_p (decl);
+      if (mips16_p || nomips16_p)
+       {
+         /* DECL cannot be simultaneously mips16 and nomips16.  */
+         if (mips16_p && nomips16_p)
+           error ("%qs cannot have both %<mips16%> and "
+                  "%<nomips16%> attributes",
+                  IDENTIFIER_POINTER (DECL_NAME (decl)));
+       }
+      else if (TARGET_FLIP_MIPS16 && !DECL_ARTIFICIAL (decl))
+       {
+         /* Implement -mflip-mips16.  If DECL has neither a "nomips16" nor a
+            "mips16" attribute, arbitrarily pick one.  We must pick the same
+            setting for duplicate declarations of a function.  */
+         name = mflip_mips16_use_mips16_p (decl) ? "mips16" : "nomips16";
+         *attributes = tree_cons (get_identifier (name), NULL, *attributes);
+       }
+    }
+}
+
+/* Implement TARGET_MERGE_DECL_ATTRIBUTES.  */
+
+static tree
+mips_merge_decl_attributes (tree olddecl, tree newdecl)
+{
+  /* The decls' "mips16" and "nomips16" attributes must match exactly.  */
+  if (mips_mips16_decl_p (olddecl) != mips_mips16_decl_p (newdecl))
+    error ("%qs redeclared with conflicting %qs attributes",
+          IDENTIFIER_POINTER (DECL_NAME (newdecl)), "mips16");
+  if (mips_nomips16_decl_p (olddecl) != mips_nomips16_decl_p (newdecl))
+    error ("%qs redeclared with conflicting %qs attributes",
+          IDENTIFIER_POINTER (DECL_NAME (newdecl)), "nomips16");
+
+  return merge_attributes (DECL_ATTRIBUTES (olddecl),
+                          DECL_ATTRIBUTES (newdecl));
+}
 \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.  */
@@ -1457,6 +1260,118 @@ mips_split_plus (rtx x, rtx *base_ptr, HOST_WIDE_INT *offset_ptr)
     }
 }
 \f
+static unsigned int mips_build_integer (struct mips_integer_op *,
+                                       unsigned HOST_WIDE_INT);
+
+/* Subroutine of mips_build_integer (with the same interface).
+   Assume that the final action in the sequence should be a left shift.  */
+
+static unsigned int
+mips_build_shift (struct mips_integer_op *codes, HOST_WIDE_INT value)
+{
+  unsigned int i, shift;
+
+  /* Shift VALUE right until its lowest bit is set.  Shift arithmetically
+     since signed numbers are easier to load than unsigned ones.  */
+  shift = 0;
+  while ((value & 1) == 0)
+    value /= 2, shift++;
+
+  i = mips_build_integer (codes, value);
+  codes[i].code = ASHIFT;
+  codes[i].value = shift;
+  return i + 1;
+}
+
+
+/* As for mips_build_shift, but assume that the final action will be
+   an IOR or PLUS operation.  */
+
+static unsigned int
+mips_build_lower (struct mips_integer_op *codes, unsigned HOST_WIDE_INT value)
+{
+  unsigned HOST_WIDE_INT high;
+  unsigned int i;
+
+  high = value & ~(unsigned HOST_WIDE_INT) 0xffff;
+  if (!LUI_OPERAND (high) && (value & 0x18000) == 0x18000)
+    {
+      /* The constant is too complex to load with a simple lui/ori pair
+        so our goal is to clear as many trailing zeros as possible.
+        In this case, we know bit 16 is set and that the low 16 bits
+        form a negative number.  If we subtract that number from VALUE,
+        we will clear at least the lowest 17 bits, maybe more.  */
+      i = mips_build_integer (codes, CONST_HIGH_PART (value));
+      codes[i].code = PLUS;
+      codes[i].value = CONST_LOW_PART (value);
+    }
+  else
+    {
+      i = mips_build_integer (codes, high);
+      codes[i].code = IOR;
+      codes[i].value = value & 0xffff;
+    }
+  return i + 1;
+}
+
+
+/* Fill CODES with a sequence of rtl operations to load VALUE.
+   Return the number of operations needed.  */
+
+static unsigned int
+mips_build_integer (struct mips_integer_op *codes,
+                   unsigned HOST_WIDE_INT value)
+{
+  if (SMALL_OPERAND (value)
+      || SMALL_OPERAND_UNSIGNED (value)
+      || LUI_OPERAND (value))
+    {
+      /* The value can be loaded with a single instruction.  */
+      codes[0].code = UNKNOWN;
+      codes[0].value = value;
+      return 1;
+    }
+  else if ((value & 1) != 0 || LUI_OPERAND (CONST_HIGH_PART (value)))
+    {
+      /* Either the constant is a simple LUI/ORI combination or its
+        lowest bit is set.  We don't want to shift in this case.  */
+      return mips_build_lower (codes, value);
+    }
+  else if ((value & 0xffff) == 0)
+    {
+      /* The constant will need at least three actions.  The lowest
+        16 bits are clear, so the final action will be a shift.  */
+      return mips_build_shift (codes, value);
+    }
+  else
+    {
+      /* The final action could be a shift, add or inclusive OR.
+        Rather than use a complex condition to select the best
+        approach, try both mips_build_shift and mips_build_lower
+        and pick the one that gives the shortest sequence.
+        Note that this case is only used once per constant.  */
+      struct mips_integer_op alt_codes[MIPS_MAX_INTEGER_OPS];
+      unsigned int cost, alt_cost;
+
+      cost = mips_build_shift (codes, value);
+      alt_cost = mips_build_lower (alt_codes, value);
+      if (alt_cost < cost)
+       {
+         memcpy (codes, alt_codes, alt_cost * sizeof (codes[0]));
+         cost = alt_cost;
+       }
+      return cost;
+    }
+}
+\f
+/* Return true if X is a thread-local symbol.  */
+
+static bool
+mips_tls_operand_p (rtx x)
+{
+  return GET_CODE (x) == SYMBOL_REF && SYMBOL_REF_TLS_MODEL (x) != 0;
+}
+
 /* Return true if SYMBOL_REF X is associated with a global symbol
    (in the STB_GLOBAL sense).  */
 
@@ -1495,6 +1410,19 @@ mips_rtx_constant_in_small_data_p (enum machine_mode mode)
          && GET_MODE_SIZE (mode) <= mips_section_threshold);
 }
 
+/* Return true if X should not be moved directly into register $25.
+   We need this because many versions of GAS will treat "la $25,foo" as
+   part of a call sequence and so allow a global "foo" to be lazily bound.  */
+
+bool
+mips_dangerous_for_la25_p (rtx x)
+{
+  return (!TARGET_EXPLICIT_RELOCS
+         && TARGET_USE_GOT
+         && GET_CODE (x) == SYMBOL_REF
+         && mips_global_symbol_p (x));
+}
+
 /* Return the method that should be used to access SYMBOL_REF or
    LABEL_REF X in context CONTEXT.  */
 
@@ -1700,212 +1628,36 @@ mips_symbolic_constant_p (rtx x, enum mips_symbol_context context,
     }
   gcc_unreachable ();
 }
+\f
+/* 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.  */
 
-
-/* This function is used to implement REG_MODE_OK_FOR_BASE_P.  */
-
-int
-mips_regno_mode_ok_for_base_p (int regno, enum machine_mode mode, int strict)
+static int
+mips_symbol_insns_1 (enum mips_symbol_type type, enum machine_mode mode)
 {
-  if (!HARD_REGISTER_NUM_P (regno))
+  switch (type)
     {
-      if (!strict)
-       return true;
-      regno = reg_renumber[regno];
-    }
-
-  /* These fake registers will be eliminated to either the stack or
-     hard frame pointer, both of which are usually valid base registers.
-     Reload deals with the cases where the eliminated form isn't valid.  */
-  if (regno == ARG_POINTER_REGNUM || regno == FRAME_POINTER_REGNUM)
-    return true;
+    case SYMBOL_ABSOLUTE:
+      /* When using 64-bit symbols, we need 5 preparatory instructions,
+        such as:
 
-  /* In mips16 mode, the stack pointer can only address word and doubleword
-     values, nothing smaller.  There are two problems here:
+            lui     $at,%highest(symbol)
+            daddiu  $at,$at,%higher(symbol)
+            dsll    $at,$at,16
+            daddiu  $at,$at,%hi(symbol)
+            dsll    $at,$at,16
 
-       (a) Instantiating virtual registers can introduce new uses of the
-          stack pointer.  If these virtual registers are valid addresses,
-          the stack pointer should be too.
+        The final address is then $at + %lo(symbol).  With 32-bit
+        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;
 
-       (b) Most uses of the stack pointer are not made explicit until
-          FRAME_POINTER_REGNUM and ARG_POINTER_REGNUM have been eliminated.
-          We don't know until that stage whether we'll be eliminating to the
-          stack pointer (which needs the restriction) or the hard frame
-          pointer (which doesn't).
-
-     All in all, it seems more consistent to only enforce this restriction
-     during and after reload.  */
-  if (TARGET_MIPS16 && regno == STACK_POINTER_REGNUM)
-    return !strict || GET_MODE_SIZE (mode) == 4 || GET_MODE_SIZE (mode) == 8;
-
-  return TARGET_MIPS16 ? M16_REG_P (regno) : GP_REG_P (regno);
-}
-
-
-/* Return true if X is a valid base register for the given mode.
-   Allow only hard registers if STRICT.  */
-
-static bool
-mips_valid_base_register_p (rtx x, enum machine_mode mode, int strict)
-{
-  if (!strict && GET_CODE (x) == SUBREG)
-    x = SUBREG_REG (x);
-
-  return (REG_P (x)
-         && mips_regno_mode_ok_for_base_p (REGNO (x), mode, strict));
-}
-
-
-/* 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.  */
-
-static bool
-mips_classify_address (struct mips_address_info *info, rtx x,
-                      enum machine_mode mode, int strict)
-{
-  switch (GET_CODE (x))
-    {
-    case REG:
-    case SUBREG:
-      info->type = ADDRESS_REG;
-      info->reg = x;
-      info->offset = const0_rtx;
-      return mips_valid_base_register_p (info->reg, mode, strict);
-
-    case PLUS:
-      info->type = ADDRESS_REG;
-      info->reg = XEXP (x, 0);
-      info->offset = XEXP (x, 1);
-      return (mips_valid_base_register_p (info->reg, mode, strict)
-             && const_arith_operand (info->offset, VOIDmode));
-
-    case LO_SUM:
-      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_symbol_insns (info->symbol_type, mode) > 0
-             && mips_lo_relocs[info->symbol_type] != 0);
-
-    case CONST_INT:
-      /* Small-integer addresses don't occur very often, but they
-        are legitimate if $0 is a valid base register.  */
-      info->type = ADDRESS_CONST_INT;
-      return !TARGET_MIPS16 && SMALL_INT (x);
-
-    case CONST:
-    case LABEL_REF:
-    case SYMBOL_REF:
-      info->type = ADDRESS_SYMBOLIC;
-      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:
-      return false;
-    }
-}
-
-/* Return true if X is a thread-local symbol.  */
-
-static bool
-mips_tls_operand_p (rtx x)
-{
-  return GET_CODE (x) == SYMBOL_REF && SYMBOL_REF_TLS_MODEL (x) != 0;
-}
-
-/* Return true if X can not be forced into a constant pool.  */
-
-static int
-mips_tls_symbol_ref_1 (rtx *x, void *data ATTRIBUTE_UNUSED)
-{
-  return mips_tls_operand_p (*x);
-}
-
-/* Return true if X can not be forced into a constant pool.  */
-
-static bool
-mips_cannot_force_const_mem (rtx x)
-{
-  rtx base, offset;
-
-  if (!TARGET_MIPS16)
-    {
-      /* As an optimization, reject constants that mips_legitimize_move
-        can expand inline.
-
-        Suppose we have a multi-instruction sequence that loads constant C
-        into register R.  If R does not get allocated a hard register, and
-        R is used in an operand that allows both registers and memory
-        references, reload will consider forcing C into memory and using
-        one of the instruction's memory alternatives.  Returning false
-        here will force it to use an input reload instead.  */
-      if (GET_CODE (x) == CONST_INT)
-       return true;
-
-      split_const (x, &base, &offset);
-      if (symbolic_operand (base, VOIDmode) && SMALL_INT (offset))
-       return true;
-    }
-
-  if (for_each_rtx (&x, &mips_tls_symbol_ref_1, 0))
-    return true;
-
-  return false;
-}
-
-/* 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,
-                               const_rtx x ATTRIBUTE_UNUSED)
-{
-  return !TARGET_MIPS16_PCREL_LOADS;
-}
-\f
-/* 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_1 (enum mips_symbol_type type, enum machine_mode mode)
-{
-  switch (type)
-    {
-    case SYMBOL_ABSOLUTE:
-      /* When using 64-bit symbols, we need 5 preparatory instructions,
-        such as:
-
-            lui     $at,%highest(symbol)
-            daddiu  $at,$at,%higher(symbol)
-            dsll    $at,$at,16
-            daddiu  $at,$at,%hi(symbol)
-            dsll    $at,$at,16
-
-        The final address is then $at + %lo(symbol).  With 32-bit
-        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_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_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_PC_RELATIVE:
       /* PC-relative constants can be only be used with addiupc,
@@ -2000,60 +1752,260 @@ 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.  */
+/* Return true if X can not be forced into a constant pool.  */
 
-bool
-mips_stack_address_p (rtx x, enum machine_mode mode)
+static int
+mips_tls_symbol_ref_1 (rtx *x, void *data ATTRIBUTE_UNUSED)
 {
-  struct mips_address_info addr;
-
-  return (mips_classify_address (&addr, x, mode, false)
-         && addr.type == ADDRESS_REG
-         && addr.reg == stack_pointer_rtx);
+  return mips_tls_operand_p (*x);
 }
 
-/* Return true if a value at OFFSET bytes from BASE can be accessed
-   using an unextended mips16 instruction.  MODE is the mode of the
-   value.
-
-   Usually the offset in an unextended instruction is a 5-bit field.
-   The offset is unsigned and shifted left once for HIs, twice
-   for SIs, and so on.  An exception is SImode accesses off the
-   stack pointer, which have an 8-bit immediate field.  */
+/* Return true if X can not be forced into a constant pool.  */
 
 static bool
-mips16_unextended_reference_p (enum machine_mode mode, rtx base, rtx offset)
+mips_cannot_force_const_mem (rtx x)
 {
-  if (TARGET_MIPS16
-      && GET_CODE (offset) == CONST_INT
-      && INTVAL (offset) >= 0
-      && (INTVAL (offset) & (GET_MODE_SIZE (mode) - 1)) == 0)
+  rtx base, offset;
+
+  if (!TARGET_MIPS16)
     {
-      if (GET_MODE_SIZE (mode) == 4 && base == stack_pointer_rtx)
-       return INTVAL (offset) < 256 * GET_MODE_SIZE (mode);
-      return INTVAL (offset) < 32 * GET_MODE_SIZE (mode);
+      /* As an optimization, reject constants that mips_legitimize_move
+        can expand inline.
+
+        Suppose we have a multi-instruction sequence that loads constant C
+        into register R.  If R does not get allocated a hard register, and
+        R is used in an operand that allows both registers and memory
+        references, reload will consider forcing C into memory and using
+        one of the instruction's memory alternatives.  Returning false
+        here will force it to use an input reload instead.  */
+      if (GET_CODE (x) == CONST_INT)
+       return true;
+
+      split_const (x, &base, &offset);
+      if (symbolic_operand (base, VOIDmode) && SMALL_INT (offset))
+       return true;
     }
+
+  if (for_each_rtx (&x, &mips_tls_symbol_ref_1, 0))
+    return true;
+
   return false;
 }
 
+/* Implement TARGET_USE_BLOCKS_FOR_CONSTANT_P.  We can't use blocks for
+   constants when we're using a per-function constant pool.  */
 
-/* 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.  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.  */
+static bool
+mips_use_blocks_for_constant_p (enum machine_mode mode ATTRIBUTE_UNUSED,
+                               const_rtx x ATTRIBUTE_UNUSED)
+{
+  return !TARGET_MIPS16_PCREL_LOADS;
+}
+\f
+/* This function is used to implement REG_MODE_OK_FOR_BASE_P.  */
 
 int
-mips_address_insns (rtx x, enum machine_mode mode, bool might_split_p)
+mips_regno_mode_ok_for_base_p (int regno, enum machine_mode mode, int strict)
 {
-  struct mips_address_info addr;
-  int factor;
+  if (!HARD_REGISTER_NUM_P (regno))
+    {
+      if (!strict)
+       return true;
+      regno = reg_renumber[regno];
+    }
 
-  /* 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.)  */
+  /* These fake registers will be eliminated to either the stack or
+     hard frame pointer, both of which are usually valid base registers.
+     Reload deals with the cases where the eliminated form isn't valid.  */
+  if (regno == ARG_POINTER_REGNUM || regno == FRAME_POINTER_REGNUM)
+    return true;
+
+  /* In mips16 mode, the stack pointer can only address word and doubleword
+     values, nothing smaller.  There are two problems here:
+
+       (a) Instantiating virtual registers can introduce new uses of the
+          stack pointer.  If these virtual registers are valid addresses,
+          the stack pointer should be too.
+
+       (b) Most uses of the stack pointer are not made explicit until
+          FRAME_POINTER_REGNUM and ARG_POINTER_REGNUM have been eliminated.
+          We don't know until that stage whether we'll be eliminating to the
+          stack pointer (which needs the restriction) or the hard frame
+          pointer (which doesn't).
+
+     All in all, it seems more consistent to only enforce this restriction
+     during and after reload.  */
+  if (TARGET_MIPS16 && regno == STACK_POINTER_REGNUM)
+    return !strict || GET_MODE_SIZE (mode) == 4 || GET_MODE_SIZE (mode) == 8;
+
+  return TARGET_MIPS16 ? M16_REG_P (regno) : GP_REG_P (regno);
+}
+
+
+/* Return true if X is a valid base register for the given mode.
+   Allow only hard registers if STRICT.  */
+
+static bool
+mips_valid_base_register_p (rtx x, enum machine_mode mode, int strict)
+{
+  if (!strict && GET_CODE (x) == SUBREG)
+    x = SUBREG_REG (x);
+
+  return (REG_P (x)
+         && mips_regno_mode_ok_for_base_p (REGNO (x), mode, strict));
+}
+
+
+/* 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.  */
+
+static bool
+mips_classify_address (struct mips_address_info *info, rtx x,
+                      enum machine_mode mode, int strict)
+{
+  switch (GET_CODE (x))
+    {
+    case REG:
+    case SUBREG:
+      info->type = ADDRESS_REG;
+      info->reg = x;
+      info->offset = const0_rtx;
+      return mips_valid_base_register_p (info->reg, mode, strict);
+
+    case PLUS:
+      info->type = ADDRESS_REG;
+      info->reg = XEXP (x, 0);
+      info->offset = XEXP (x, 1);
+      return (mips_valid_base_register_p (info->reg, mode, strict)
+             && const_arith_operand (info->offset, VOIDmode));
+
+    case LO_SUM:
+      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_symbol_insns (info->symbol_type, mode) > 0
+             && mips_lo_relocs[info->symbol_type] != 0);
+
+    case CONST_INT:
+      /* Small-integer addresses don't occur very often, but they
+        are legitimate if $0 is a valid base register.  */
+      info->type = ADDRESS_CONST_INT;
+      return !TARGET_MIPS16 && SMALL_INT (x);
+
+    case CONST:
+    case LABEL_REF:
+    case SYMBOL_REF:
+      info->type = ADDRESS_SYMBOLIC;
+      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:
+      return false;
+    }
+}
+
+/* This function is used to implement GO_IF_LEGITIMATE_ADDRESS.  It
+   returns a nonzero value if X is a legitimate address for a memory
+   operand of the indicated MODE.  STRICT is nonzero if this function
+   is called during reload.  */
+
+bool
+mips_legitimate_address_p (enum machine_mode mode, rtx x, int strict)
+{
+  struct mips_address_info addr;
+
+  return mips_classify_address (&addr, x, mode, strict);
+}
+
+/* Return true if X is a legitimate $sp-based address for mode MDOE.  */
+
+bool
+mips_stack_address_p (rtx x, enum machine_mode mode)
+{
+  struct mips_address_info addr;
+
+  return (mips_classify_address (&addr, x, mode, false)
+         && addr.type == ADDRESS_REG
+         && addr.reg == stack_pointer_rtx);
+}
+
+/* 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;
+}
+
+/* Return true if a value at OFFSET bytes from BASE can be accessed
+   using an unextended mips16 instruction.  MODE is the mode of the
+   value.
+
+   Usually the offset in an unextended instruction is a 5-bit field.
+   The offset is unsigned and shifted left once for HIs, twice
+   for SIs, and so on.  An exception is SImode accesses off the
+   stack pointer, which have an 8-bit immediate field.  */
+
+static bool
+mips16_unextended_reference_p (enum machine_mode mode, rtx base, rtx offset)
+{
+  if (TARGET_MIPS16
+      && GET_CODE (offset) == CONST_INT
+      && INTVAL (offset) >= 0
+      && (INTVAL (offset) & (GET_MODE_SIZE (mode) - 1)) == 0)
+    {
+      if (GET_MODE_SIZE (mode) == 4 && base == stack_pointer_rtx)
+       return INTVAL (offset) < 256 * GET_MODE_SIZE (mode);
+      return INTVAL (offset) < 32 * GET_MODE_SIZE (mode);
+    }
+  return false;
+}
+
+
+/* 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.  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, bool might_split_p)
+{
+  struct mips_address_info addr;
+  int factor;
+
+  /* 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
@@ -2204,19 +2156,6 @@ mips_idiv_insns (void)
   return count;
 }
 \f
-/* This function is used to implement GO_IF_LEGITIMATE_ADDRESS.  It
-   returns a nonzero value if X is a legitimate address for a memory
-   operand of the indicated MODE.  STRICT is nonzero if this function
-   is called during reload.  */
-
-bool
-mips_legitimate_address_p (enum machine_mode mode, rtx x, int strict)
-{
-  struct mips_address_info addr;
-
-  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
@@ -2231,6 +2170,15 @@ mips_emit_move (rtx dest, rtx src)
          : emit_move_insn_1 (dest, src));
 }
 
+/* Emit an instruction of the form (set TARGET (CODE OP0 OP1)).  */
+
+static void
+mips_emit_binary (enum rtx_code code, rtx target, rtx op0, rtx op1)
+{
+  emit_insn (gen_rtx_SET (VOIDmode, target,
+                         gen_rtx_fmt_ee (code, GET_MODE (target), op0, op1)));
+}
+
 /* Copy VALUE to a register and return that register.  If new psuedos
    are allowed, copy it into a new register, otherwise use DEST.  */
 
@@ -2246,6 +2194,47 @@ mips_force_temporary (rtx dest, rtx value)
     }
 }
 
+/* If we can access small data directly (using gp-relative relocation
+   operators) return the small data pointer, otherwise return null.
+
+   For each mips16 function which refers to GP relative symbols, we
+   use a pseudo register, initialized at the start of the function, to
+   hold the $gp value.  */
+
+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;
+
+      /* 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);
+
+      push_topmost_sequence ();
+      /* We need to emit the initialization after the FUNCTION_BEG
+         note, so that it will be integrated.  */
+      for (scan = get_insns (); scan != NULL_RTX; scan = NEXT_INSN (scan))
+       if (NOTE_P (scan)
+           && NOTE_KIND (scan) == NOTE_INSN_FUNCTION_BEG)
+         break;
+      if (scan == NULL_RTX)
+       scan = get_insns ();
+      insn = emit_insn_after (insn, scan);
+      pop_topmost_sequence ();
+
+      cfun->machine->initialized_mips16_gp_pseudo_p = true;
+    }
+
+  return cfun->machine->mips16_gp_pseudo_rtx;
+}
 
 /* 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
@@ -2523,108 +2512,6 @@ mips_legitimize_address (rtx *xloc, enum machine_mode mode)
 }
 
 
-/* Subroutine of mips_build_integer (with the same interface).
-   Assume that the final action in the sequence should be a left shift.  */
-
-static unsigned int
-mips_build_shift (struct mips_integer_op *codes, HOST_WIDE_INT value)
-{
-  unsigned int i, shift;
-
-  /* Shift VALUE right until its lowest bit is set.  Shift arithmetically
-     since signed numbers are easier to load than unsigned ones.  */
-  shift = 0;
-  while ((value & 1) == 0)
-    value /= 2, shift++;
-
-  i = mips_build_integer (codes, value);
-  codes[i].code = ASHIFT;
-  codes[i].value = shift;
-  return i + 1;
-}
-
-
-/* As for mips_build_shift, but assume that the final action will be
-   an IOR or PLUS operation.  */
-
-static unsigned int
-mips_build_lower (struct mips_integer_op *codes, unsigned HOST_WIDE_INT value)
-{
-  unsigned HOST_WIDE_INT high;
-  unsigned int i;
-
-  high = value & ~(unsigned HOST_WIDE_INT) 0xffff;
-  if (!LUI_OPERAND (high) && (value & 0x18000) == 0x18000)
-    {
-      /* The constant is too complex to load with a simple lui/ori pair
-        so our goal is to clear as many trailing zeros as possible.
-        In this case, we know bit 16 is set and that the low 16 bits
-        form a negative number.  If we subtract that number from VALUE,
-        we will clear at least the lowest 17 bits, maybe more.  */
-      i = mips_build_integer (codes, CONST_HIGH_PART (value));
-      codes[i].code = PLUS;
-      codes[i].value = CONST_LOW_PART (value);
-    }
-  else
-    {
-      i = mips_build_integer (codes, high);
-      codes[i].code = IOR;
-      codes[i].value = value & 0xffff;
-    }
-  return i + 1;
-}
-
-
-/* Fill CODES with a sequence of rtl operations to load VALUE.
-   Return the number of operations needed.  */
-
-static unsigned int
-mips_build_integer (struct mips_integer_op *codes,
-                   unsigned HOST_WIDE_INT value)
-{
-  if (SMALL_OPERAND (value)
-      || SMALL_OPERAND_UNSIGNED (value)
-      || LUI_OPERAND (value))
-    {
-      /* The value can be loaded with a single instruction.  */
-      codes[0].code = UNKNOWN;
-      codes[0].value = value;
-      return 1;
-    }
-  else if ((value & 1) != 0 || LUI_OPERAND (CONST_HIGH_PART (value)))
-    {
-      /* Either the constant is a simple LUI/ORI combination or its
-        lowest bit is set.  We don't want to shift in this case.  */
-      return mips_build_lower (codes, value);
-    }
-  else if ((value & 0xffff) == 0)
-    {
-      /* The constant will need at least three actions.  The lowest
-        16 bits are clear, so the final action will be a shift.  */
-      return mips_build_shift (codes, value);
-    }
-  else
-    {
-      /* The final action could be a shift, add or inclusive OR.
-        Rather than use a complex condition to select the best
-        approach, try both mips_build_shift and mips_build_lower
-        and pick the one that gives the shortest sequence.
-        Note that this case is only used once per constant.  */
-      struct mips_integer_op alt_codes[MIPS_MAX_INTEGER_OPS];
-      unsigned int cost, alt_cost;
-
-      cost = mips_build_shift (codes, value);
-      alt_cost = mips_build_lower (alt_codes, value);
-      if (alt_cost < cost)
-       {
-         memcpy (codes, alt_codes, alt_cost * sizeof (codes[0]));
-         cost = alt_cost;
-       }
-      return cost;
-    }
-}
-
-
 /* Load VALUE into DEST, using TEMP as a temporary register if need be.  */
 
 void
@@ -2749,6 +2636,86 @@ mips_legitimize_move (enum machine_mode mode, rtx dest, rtx src)
   return false;
 }
 \f
+/* 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, enum mips_symbol_context context)
+{
+  enum mips_symbol_type symbol_type;
+
+  return (TARGET_EXPLICIT_RELOCS
+         && mips_symbolic_constant_p (x, context, &symbol_type)
+         && symbol_type == SYMBOL_GP_RELATIVE);
+}
+
+
+/* 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)
+{
+  enum mips_symbol_context context;
+
+  if (GET_CODE (*loc) == LO_SUM)
+    return -1;
+
+  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
+   a LO_SUM.  */
+
+bool
+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.
+   DATA is the containing MEM, or null if none.  */
+
+static int
+mips_rewrite_small_data_1 (rtx *loc, void *data)
+{
+  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)
+    return -1;
+
+  return 0;
+}
+
+/* If possible, rewrite OP so that it refers to small data using
+   explicit relocations.  */
+
+rtx
+mips_rewrite_small_data (rtx op)
+{
+  op = copy_insn (op);
+  for_each_rtx (&op, mips_rewrite_small_data_1, 0);
+  return op;
+}
+\f
 /* We need a lot of little routines to check constant values on the
    mips16.  These are used to figure out how long the instruction will
    be.  It would be much better to do this using constraints, but
@@ -2859,26 +2826,6 @@ m16_nsimm8_8 (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
   return m16_check_op (op, (- 0x7f) << 3, 0x80 << 3, 7);
 }
 \f
-/* Return true if ADDR matches the pattern for the lwxs load scaled indexed
-   address instruction.  */
-
-static bool
-mips_lwxs_address_p (rtx addr)
-{
-  if (ISA_HAS_LWXS
-      && GET_CODE (addr) == PLUS
-      && REG_P (XEXP (addr, 1)))
-    {
-      rtx offset = XEXP (addr, 0);
-      if (GET_CODE (offset) == MULT
-         && REG_P (XEXP (offset, 0))
-         && GET_CODE (XEXP (offset, 1)) == CONST_INT
-         && INTVAL (XEXP (offset, 1)) == 4)
-       return true;
-    }
-  return false;
-}
-
 /* The cost of loading values from the constant pool.  It should be
    larger than the cost of any constant we want to synthesize inline.  */
 
@@ -3721,37 +3668,6 @@ mips_output_move (rtx dest, rtx src)
   gcc_unreachable ();
 }
 \f
-/* Restore $gp from its save slot.  Valid only when using o32 or
-   o64 abicalls.  */
-
-void
-mips_restore_gp (void)
-{
-  rtx address, slot;
-
-  gcc_assert (TARGET_ABICALLS && TARGET_OLDABI);
-
-  address = mips_add_offset (pic_offset_table_rtx,
-                            frame_pointer_needed
-                            ? hard_frame_pointer_rtx
-                            : stack_pointer_rtx,
-                            current_function_outgoing_args_size);
-  slot = gen_rtx_MEM (Pmode, address);
-
-  mips_emit_move (pic_offset_table_rtx, slot);
-  if (!TARGET_EXPLICIT_RELOCS)
-    emit_insn (gen_blockage ());
-}
-\f
-/* Emit an instruction of the form (set TARGET (CODE OP0 OP1)).  */
-
-static void
-mips_emit_binary (enum rtx_code code, rtx target, rtx op0, rtx op1)
-{
-  emit_insn (gen_rtx_SET (VOIDmode, target,
-                         gen_rtx_fmt_ee (code, GET_MODE (target), op0, op1)));
-}
-
 /* Return true if CMP1 is a suitable second operand for relational
    operator CODE.  See also the *sCC patterns in mips.md.  */
 
@@ -4099,793 +4015,643 @@ mips_gen_conditional_trap (rtx *operands)
                              operands[1]));
 }
 \f
-/* Return true if function DECL is a MIPS16 function.  Return the ambient
-   setting if DECL is null.  */
+/* Argument support functions.  */
 
-static bool
-mips_use_mips16_mode_p (tree decl)
+/* Initialize CUMULATIVE_ARGS for a function.  */
+
+void
+init_cumulative_args (CUMULATIVE_ARGS *cum, tree fntype,
+                     rtx libname ATTRIBUTE_UNUSED)
 {
-  if (decl)
+  static CUMULATIVE_ARGS zero_cum;
+  tree param, next_param;
+
+  *cum = zero_cum;
+  cum->prototype = (fntype && TYPE_ARG_TYPES (fntype));
+
+  /* Determine if this function has variable arguments.  This is
+     indicated by the last argument being 'void_type_mode' if there
+     are no variable arguments.  The standard MIPS calling sequence
+     passes all arguments in the general purpose registers in this case.  */
+
+  for (param = fntype ? TYPE_ARG_TYPES (fntype) : 0;
+       param != 0; param = next_param)
     {
-      /* Nested functions must use the same frame pointer as their
-        parent and must therefore use the same ISA mode.  */
-      tree parent = decl_function_context (decl);
-      if (parent)
-       decl = parent;
-      if (mips_mips16_decl_p (decl))
-       return true;
-      if (mips_nomips16_decl_p (decl))
-       return false;
+      next_param = TREE_CHAIN (param);
+      if (next_param == 0 && TREE_VALUE (param) != void_type_node)
+       cum->gp_reg_found = 1;
     }
-  return mips_base_mips16;
 }
 
-/* Return true if calls to X can use R_MIPS_CALL* relocations.  */
 
-static bool
-mips_ok_for_lazy_binding_p (rtx x)
+/* Fill INFO with information about a single argument.  CUM is the
+   cumulative state for earlier arguments.  MODE is the mode of this
+   argument and TYPE is its type (if known).  NAMED is true if this
+   is a named (fixed) argument rather than a variable one.  */
+
+static void
+mips_arg_info (const CUMULATIVE_ARGS *cum, enum machine_mode mode,
+              tree type, int named, struct mips_arg_info *info)
 {
-  return (TARGET_USE_GOT
-         && GET_CODE (x) == SYMBOL_REF
-         && !mips_symbol_binds_local_p (x));
-}
+  bool doubleword_aligned_p;
+  unsigned int num_bytes, num_words, max_regs;
 
-/* Load function address ADDR into register DEST.  SIBCALL_P is true
-   if the address is needed for a sibling call.  Return true if we
-   used an explicit lazy-binding sequence.  */
+  /* Work out the size of the argument.  */
+  num_bytes = type ? int_size_in_bytes (type) : GET_MODE_SIZE (mode);
+  num_words = (num_bytes + UNITS_PER_WORD - 1) / UNITS_PER_WORD;
 
-static bool
-mips_load_call_address (rtx dest, rtx addr, int sibcall_p)
-{
-  /* If we're generating PIC, and this call is to a global function,
-     try to allow its address to be resolved lazily.  This isn't
-     possible if TARGET_CALL_SAVED_GP since the value of $gp on entry
-     to the stub would be our caller's gp, not ours.  */
-  if (TARGET_EXPLICIT_RELOCS
-      && !(sibcall_p && TARGET_CALL_SAVED_GP)
-      && mips_ok_for_lazy_binding_p (addr))
-    {
-      rtx high, lo_sum_symbol;
+  /* Decide whether it should go in a floating-point register, assuming
+     one is free.  Later code checks for availability.
 
-      high = mips_unspec_offset_high (dest, pic_offset_table_rtx,
-                                     addr, SYMBOL_GOTOFF_CALL);
-      lo_sum_symbol = mips_unspec_address (addr, SYMBOL_GOTOFF_CALL);
-      if (Pmode == SImode)
-       emit_insn (gen_load_callsi (dest, high, lo_sum_symbol));
-      else
-       emit_insn (gen_load_calldi (dest, high, lo_sum_symbol));
-      return true;
-    }
-  else
+     The checks against UNITS_PER_FPVALUE handle the soft-float and
+     single-float cases.  */
+  switch (mips_abi)
     {
-      mips_emit_move (dest, addr);
-      return false;
-    }
-}
-
+    case ABI_EABI:
+      /* The EABI conventions have traditionally been defined in terms
+        of TYPE_MODE, regardless of the actual type.  */
+      info->fpr_p = ((GET_MODE_CLASS (mode) == MODE_FLOAT
+                     || GET_MODE_CLASS (mode) == MODE_VECTOR_FLOAT)
+                    && GET_MODE_SIZE (mode) <= UNITS_PER_FPVALUE);
+      break;
 
-/* Expand a call or call_value instruction.  RESULT is where the
-   result will go (null for calls), ADDR is the address of the
-   function, ARGS_SIZE is the size of the arguments and AUX is
-   the value passed to us by mips_function_arg.  SIBCALL_P is true
-   if we are expanding a sibling call, false if we're expanding
-   a normal call.  */
+    case ABI_32:
+    case ABI_O64:
+      /* Only leading floating-point scalars are passed in
+        floating-point registers.  We also handle vector floats the same
+        say, which is OK because they are not covered by the standard ABI.  */
+      info->fpr_p = (!cum->gp_reg_found
+                    && cum->arg_number < 2
+                    && (type == 0 || SCALAR_FLOAT_TYPE_P (type)
+                        || VECTOR_FLOAT_TYPE_P (type))
+                    && (GET_MODE_CLASS (mode) == MODE_FLOAT
+                        || GET_MODE_CLASS (mode) == MODE_VECTOR_FLOAT)
+                    && GET_MODE_SIZE (mode) <= UNITS_PER_FPVALUE);
+      break;
 
-void
-mips_expand_call (rtx result, rtx addr, rtx args_size, rtx aux, int sibcall_p)
-{
-  rtx orig_addr, pattern, insn;
-  bool lazy_p;
+    case ABI_N32:
+    case ABI_64:
+      /* Scalar and complex floating-point types are passed in
+        floating-point registers.  */
+      info->fpr_p = (named
+                    && (type == 0 || FLOAT_TYPE_P (type))
+                    && (GET_MODE_CLASS (mode) == MODE_FLOAT
+                        || GET_MODE_CLASS (mode) == MODE_COMPLEX_FLOAT
+                        || GET_MODE_CLASS (mode) == MODE_VECTOR_FLOAT)
+                    && GET_MODE_UNIT_SIZE (mode) <= UNITS_PER_FPVALUE);
 
-  orig_addr = addr;
-  lazy_p = false;
-  if (!call_insn_operand (addr, VOIDmode))
-    {
-      addr = gen_reg_rtx (Pmode);
-      lazy_p = mips_load_call_address (addr, orig_addr, sibcall_p);
-    }
+      /* ??? According to the ABI documentation, the real and imaginary
+        parts of complex floats should be passed in individual registers.
+        The real and imaginary parts of stack arguments are supposed
+        to be contiguous and there should be an extra word of padding
+        at the end.
 
-  if (TARGET_MIPS16
-      && TARGET_HARD_FLOAT_ABI
-      && build_mips16_call_stub (result, addr, args_size,
-                                aux == 0 ? 0 : (int) GET_MODE (aux)))
-    return;
+        This has two problems.  First, it makes it impossible to use a
+        single "void *" va_list type, since register and stack arguments
+        are passed differently.  (At the time of writing, MIPSpro cannot
+        handle complex float varargs correctly.)  Second, it's unclear
+        what should happen when there is only one register free.
 
-  if (result == 0)
-    pattern = (sibcall_p
-              ? gen_sibcall_internal (addr, args_size)
-              : gen_call_internal (addr, args_size));
-  else if (GET_CODE (result) == PARALLEL && XVECLEN (result, 0) == 2)
-    {
-      rtx reg1, reg2;
+        For now, we assume that named complex floats should go into FPRs
+        if there are two FPRs free, otherwise they should be passed in the
+        same way as a struct containing two floats.  */
+      if (info->fpr_p
+         && GET_MODE_CLASS (mode) == MODE_COMPLEX_FLOAT
+         && GET_MODE_UNIT_SIZE (mode) < UNITS_PER_FPVALUE)
+       {
+         if (cum->num_gprs >= MAX_ARGS_IN_REGISTERS - 1)
+           info->fpr_p = false;
+         else
+           num_words = 2;
+       }
+      break;
 
-      reg1 = XEXP (XVECEXP (result, 0, 0), 0);
-      reg2 = XEXP (XVECEXP (result, 0, 1), 0);
-      pattern =
-       (sibcall_p
-        ? gen_sibcall_value_multiple_internal (reg1, addr, args_size, reg2)
-        : gen_call_value_multiple_internal (reg1, addr, args_size, reg2));
+    default:
+      gcc_unreachable ();
     }
-  else
-    pattern = (sibcall_p
-              ? gen_sibcall_value_internal (result, addr, args_size)
-              : gen_call_value_internal (result, addr, args_size));
 
-  insn = emit_call_insn (pattern);
+  /* See whether the argument has doubleword alignment.  */
+  doubleword_aligned_p = FUNCTION_ARG_BOUNDARY (mode, type) > BITS_PER_WORD;
 
-  /* Lazy-binding stubs require $gp to be valid on entry.  We also pretend
-     that they use FAKE_CALL_REGNO; see the load_call<mode> patterns for
-     details.  */
-  if (lazy_p)
-    {
-      use_reg (&CALL_INSN_FUNCTION_USAGE (insn), pic_offset_table_rtx);
-      use_reg (&CALL_INSN_FUNCTION_USAGE (insn),
-              gen_rtx_REG (Pmode, FAKE_CALL_REGNO));
-    }
-}
+  /* Set REG_OFFSET to the register count we're interested in.
+     The EABI allocates the floating-point registers separately,
+     but the other ABIs allocate them like integer registers.  */
+  info->reg_offset = (mips_abi == ABI_EABI && info->fpr_p
+                     ? cum->num_fprs
+                     : cum->num_gprs);
 
+  /* Advance to an even register if the argument is doubleword-aligned.  */
+  if (doubleword_aligned_p)
+    info->reg_offset += info->reg_offset & 1;
 
-/* Implement TARGET_FUNCTION_OK_FOR_SIBCALL.  */
+  /* Work out the offset of a stack argument.  */
+  info->stack_offset = cum->stack_words;
+  if (doubleword_aligned_p)
+    info->stack_offset += info->stack_offset & 1;
 
-static bool
-mips_function_ok_for_sibcall (tree decl, tree exp ATTRIBUTE_UNUSED)
-{
-  if (!TARGET_SIBCALLS)
-    return false;
+  max_regs = MAX_ARGS_IN_REGISTERS - info->reg_offset;
 
-  /* We can't do a sibcall if the called function is a MIPS16 function
-     because there is no direct "jx" instruction equivalent to "jalx" to
-     switch the ISA mode.  */
-  if (mips_use_mips16_mode_p (decl))
-    return false;
+  /* Partition the argument between registers and stack.  */
+  info->reg_words = MIN (num_words, max_regs);
+  info->stack_words = num_words - info->reg_words;
+}
 
-  /* ...and when -minterlink-mips16 is in effect, assume that external
-     functions could be MIPS16 ones unless an attribute explicitly
-     tells us otherwise.  We only care about cases where the sibling
-     and normal calls would both be direct.  */
-  if (TARGET_INTERLINK_MIPS16
-      && decl
-      && DECL_EXTERNAL (decl)
-      && !mips_nomips16_decl_p (decl)
-      && const_call_insn_operand (XEXP (DECL_RTL (decl), 0), VOIDmode))
-    return false;
+/* INFO describes an argument that is passed in a single-register value.
+   Return the register it uses, assuming that FPRs are available if
+   HARD_FLOAT_P.  */
 
-  /* Otherwise OK.  */
-  return true;
+static unsigned int
+mips_arg_regno (const struct mips_arg_info *info, bool hard_float_p)
+{
+  if (!info->fpr_p || !hard_float_p)
+    return GP_ARG_FIRST + info->reg_offset;
+  else if (mips_abi == ABI_32 && TARGET_DOUBLE_FLOAT && info->reg_offset > 0)
+    /* In o32, the second argument is always passed in $f14
+       for TARGET_DOUBLE_FLOAT, regardless of whether the
+       first argument was a word or doubleword.  */
+    return FP_ARG_FIRST + 2;
+  else
+    return FP_ARG_FIRST + info->reg_offset;
 }
-\f
-/* Emit code to move general operand SRC into condition-code
-   register DEST.  SCRATCH is a scratch TFmode float register.
-   The sequence is:
 
-       FP1 = SRC
-       FP2 = 0.0f
-       DEST = FP2 < FP1
+static bool
+mips_strict_argument_naming (CUMULATIVE_ARGS *ca ATTRIBUTE_UNUSED)
+{
+  return !TARGET_OLDABI;
+}
 
-   where FP1 and FP2 are single-precision float registers
-   taken from SCRATCH.  */
+/* Implement FUNCTION_ARG.  */
 
-void
-mips_emit_fcc_reload (rtx dest, rtx src, rtx scratch)
+struct rtx_def *
+function_arg (const CUMULATIVE_ARGS *cum, enum machine_mode mode,
+             tree type, int named)
 {
-  rtx fp1, fp2;
+  struct mips_arg_info info;
 
-  /* Change the source to SFmode.  */
-  if (MEM_P (src))
-    src = adjust_address (src, SFmode, 0);
-  else if (REG_P (src) || GET_CODE (src) == SUBREG)
-    src = gen_rtx_REG (SFmode, true_regnum (src));
+  /* We will be called with a mode of VOIDmode after the last argument
+     has been seen.  Whatever we return will be passed to the call
+     insn.  If we need a mips16 fp_code, return a REG with the code
+     stored as the mode.  */
+  if (mode == VOIDmode)
+    {
+      if (TARGET_MIPS16 && cum->fp_code != 0)
+       return gen_rtx_REG ((enum machine_mode) cum->fp_code, 0);
 
-  fp1 = gen_rtx_REG (SFmode, REGNO (scratch));
-  fp2 = gen_rtx_REG (SFmode, REGNO (scratch) + MAX_FPRS_PER_FMT);
+      else
+       return 0;
+    }
 
-  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
-/* Emit code to change the current function's return address to
-   ADDRESS.  SCRATCH is available as a scratch register, if needed.
-   ADDRESS and SCRATCH are both word-mode GPRs.  */
+  mips_arg_info (cum, mode, type, named, &info);
 
-void
-mips_set_return_address (rtx address, rtx scratch)
-{
-  rtx slot_address;
+  /* Return straight away if the whole argument is passed on the stack.  */
+  if (info.reg_offset == MAX_ARGS_IN_REGISTERS)
+    return 0;
 
-  compute_frame_size (get_frame_size ());
-  gcc_assert ((cfun->machine->frame.mask >> 31) & 1);
-  slot_address = mips_add_offset (scratch, stack_pointer_rtx,
-                                 cfun->machine->frame.gp_sp_offset);
+  if (type != 0
+      && TREE_CODE (type) == RECORD_TYPE
+      && TARGET_NEWABI
+      && TYPE_SIZE_UNIT (type)
+      && host_integerp (TYPE_SIZE_UNIT (type), 1)
+      && named)
+    {
+      /* The Irix 6 n32/n64 ABIs say that if any 64-bit chunk of the
+        structure contains a double in its entirety, then that 64-bit
+        chunk is passed in a floating point register.  */
+      tree field;
 
-  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.
-   Assume that the areas do not overlap.  */
+      /* First check to see if there is any such field.  */
+      for (field = TYPE_FIELDS (type); field; field = TREE_CHAIN (field))
+       if (TREE_CODE (field) == FIELD_DECL
+           && TREE_CODE (TREE_TYPE (field)) == REAL_TYPE
+           && TYPE_PRECISION (TREE_TYPE (field)) == BITS_PER_WORD
+           && host_integerp (bit_position (field), 0)
+           && int_bit_position (field) % BITS_PER_WORD == 0)
+         break;
 
-static void
-mips_block_move_straight (rtx dest, rtx src, HOST_WIDE_INT length)
-{
-  HOST_WIDE_INT offset, delta;
-  unsigned HOST_WIDE_INT bits;
-  int i;
-  enum machine_mode mode;
-  rtx *regs;
+      if (field != 0)
+       {
+         /* Now handle the special case by returning a PARALLEL
+            indicating where each 64-bit chunk goes.  INFO.REG_WORDS
+            chunks are passed in registers.  */
+         unsigned int i;
+         HOST_WIDE_INT bitpos;
+         rtx ret;
 
-  /* Work out how many bits to move at a time.  If both operands have
-     half-word alignment, it is usually better to move in half words.
-     For instance, lh/lh/sh/sh is usually better than lwl/lwr/swl/swr
-     and lw/lw/sw/sw is usually better than ldl/ldr/sdl/sdr.
-     Otherwise move word-sized chunks.  */
-  if (MEM_ALIGN (src) == BITS_PER_WORD / 2
-      && MEM_ALIGN (dest) == BITS_PER_WORD / 2)
-    bits = BITS_PER_WORD / 2;
-  else
-    bits = BITS_PER_WORD;
+         /* assign_parms checks the mode of ENTRY_PARM, so we must
+            use the actual mode here.  */
+         ret = gen_rtx_PARALLEL (mode, rtvec_alloc (info.reg_words));
 
-  mode = mode_for_size (bits, MODE_INT, 0);
-  delta = bits / BITS_PER_UNIT;
+         bitpos = 0;
+         field = TYPE_FIELDS (type);
+         for (i = 0; i < info.reg_words; i++)
+           {
+             rtx reg;
 
-  /* Allocate a buffer for the temporary registers.  */
-  regs = alloca (sizeof (rtx) * length / delta);
+             for (; field; field = TREE_CHAIN (field))
+               if (TREE_CODE (field) == FIELD_DECL
+                   && int_bit_position (field) >= bitpos)
+                 break;
 
-  /* Load as many BITS-sized chunks as possible.  Use a normal load if
-     the source has enough alignment, otherwise use left/right pairs.  */
-  for (offset = 0, i = 0; offset + delta <= length; offset += delta, i++)
+             if (field
+                 && int_bit_position (field) == bitpos
+                 && TREE_CODE (TREE_TYPE (field)) == REAL_TYPE
+                 && !TARGET_SOFT_FLOAT
+                 && TYPE_PRECISION (TREE_TYPE (field)) == BITS_PER_WORD)
+               reg = gen_rtx_REG (DFmode, FP_ARG_FIRST + info.reg_offset + i);
+             else
+               reg = gen_rtx_REG (DImode, GP_ARG_FIRST + info.reg_offset + i);
+
+             XVECEXP (ret, 0, i)
+               = gen_rtx_EXPR_LIST (VOIDmode, reg,
+                                    GEN_INT (bitpos / BITS_PER_UNIT));
+
+             bitpos += BITS_PER_WORD;
+           }
+         return ret;
+       }
+    }
+
+  /* Handle the n32/n64 conventions for passing complex floating-point
+     arguments in FPR pairs.  The real part goes in the lower register
+     and the imaginary part goes in the upper register.  */
+  if (TARGET_NEWABI
+      && info.fpr_p
+      && GET_MODE_CLASS (mode) == MODE_COMPLEX_FLOAT)
     {
-      regs[i] = gen_reg_rtx (mode);
-      if (MEM_ALIGN (src) >= bits)
-       mips_emit_move (regs[i], adjust_address (src, mode, offset));
+      rtx real, imag;
+      enum machine_mode inner;
+      int reg;
+
+      inner = GET_MODE_INNER (mode);
+      reg = FP_ARG_FIRST + info.reg_offset;
+      if (info.reg_words * UNITS_PER_WORD == GET_MODE_SIZE (inner))
+       {
+         /* Real part in registers, imaginary part on stack.  */
+         gcc_assert (info.stack_words == info.reg_words);
+         return gen_rtx_REG (inner, reg);
+       }
       else
        {
-         rtx part = adjust_address (src, BLKmode, offset);
-         if (!mips_expand_unaligned_load (regs[i], part, bits, 0))
-           gcc_unreachable ();
+         gcc_assert (info.stack_words == 0);
+         real = gen_rtx_EXPR_LIST (VOIDmode,
+                                   gen_rtx_REG (inner, reg),
+                                   const0_rtx);
+         imag = gen_rtx_EXPR_LIST (VOIDmode,
+                                   gen_rtx_REG (inner,
+                                                reg + info.reg_words / 2),
+                                   GEN_INT (GET_MODE_SIZE (inner)));
+         return gen_rtx_PARALLEL (mode, gen_rtvec (2, real, imag));
        }
     }
 
-  /* Copy the chunks to the destination.  */
-  for (offset = 0, i = 0; offset + delta <= length; offset += delta, i++)
-    if (MEM_ALIGN (dest) >= bits)
-      mips_emit_move (adjust_address (dest, mode, offset), regs[i]);
-    else
-      {
-       rtx part = adjust_address (dest, BLKmode, offset);
-       if (!mips_expand_unaligned_store (part, regs[i], bits, 0))
-         gcc_unreachable ();
-      }
-
-  /* Mop up any left-over bytes.  */
-  if (offset < length)
-    {
-      src = adjust_address (src, BLKmode, offset);
-      dest = adjust_address (dest, BLKmode, offset);
-      move_by_pieces (dest, src, length - offset,
-                     MIN (MEM_ALIGN (src), MEM_ALIGN (dest)), 0);
-    }
+  return gen_rtx_REG (mode, mips_arg_regno (&info, TARGET_HARD_FLOAT));
 }
-\f
-#define MAX_MOVE_REGS 4
-#define MAX_MOVE_BYTES (MAX_MOVE_REGS * UNITS_PER_WORD)
-
-
-/* Helper function for doing a loop-based block operation on memory
-   reference MEM.  Each iteration of the loop will operate on LENGTH
-   bytes of MEM.
 
-   Create a new base register for use within the loop and point it to
-   the start of MEM.  Create a new memory reference that uses this
-   register.  Store them in *LOOP_REG and *LOOP_MEM respectively.  */
+/* Implement FUNCTION_ARG_ADVANCE.  */
 
-static void
-mips_adjust_block_mem (rtx mem, HOST_WIDE_INT length,
-                      rtx *loop_reg, rtx *loop_mem)
+void
+function_arg_advance (CUMULATIVE_ARGS *cum, enum machine_mode mode,
+                     tree type, int named)
 {
-  *loop_reg = copy_addr_to_reg (XEXP (mem, 0));
+  struct mips_arg_info info;
 
-  /* Although the new mem does not refer to a known location,
-     it does keep up to LENGTH bytes of alignment.  */
-  *loop_mem = change_address (mem, BLKmode, *loop_reg);
-  set_mem_align (*loop_mem, MIN (MEM_ALIGN (mem), length * BITS_PER_UNIT));
-}
+  mips_arg_info (cum, mode, type, named, &info);
 
+  if (!info.fpr_p)
+    cum->gp_reg_found = true;
 
-/* Move LENGTH bytes from SRC to DEST using a loop that moves MAX_MOVE_BYTES
-   per iteration.  LENGTH must be at least MAX_MOVE_BYTES.  Assume that the
-   memory regions do not overlap.  */
+  /* See the comment above the cumulative args structure in mips.h
+     for an explanation of what this code does.  It assumes the O32
+     ABI, which passes at most 2 arguments in float registers.  */
+  if (cum->arg_number < 2 && info.fpr_p)
+    cum->fp_code += (mode == SFmode ? 1 : 2) << (cum->arg_number * 2);
 
-static void
-mips_block_move_loop (rtx dest, rtx src, HOST_WIDE_INT length)
-{
-  rtx label, src_reg, dest_reg, final_src;
-  HOST_WIDE_INT leftover;
+  if (mips_abi != ABI_EABI || !info.fpr_p)
+    cum->num_gprs = info.reg_offset + info.reg_words;
+  else if (info.reg_words > 0)
+    cum->num_fprs += MAX_FPRS_PER_FMT;
 
-  leftover = length % MAX_MOVE_BYTES;
-  length -= leftover;
+  if (info.stack_words > 0)
+    cum->stack_words = info.stack_offset + info.stack_words;
 
-  /* Create registers and memory references for use within the loop.  */
-  mips_adjust_block_mem (src, MAX_MOVE_BYTES, &src_reg, &src);
-  mips_adjust_block_mem (dest, MAX_MOVE_BYTES, &dest_reg, &dest);
+  cum->arg_number++;
+}
 
-  /* Calculate the value that SRC_REG should have after the last iteration
-     of the loop.  */
-  final_src = expand_simple_binop (Pmode, PLUS, src_reg, GEN_INT (length),
-                                  0, 0, OPTAB_WIDEN);
+/* Implement TARGET_ARG_PARTIAL_BYTES.  */
 
-  /* Emit the start of the loop.  */
-  label = gen_label_rtx ();
-  emit_label (label);
+static int
+mips_arg_partial_bytes (CUMULATIVE_ARGS *cum,
+                       enum machine_mode mode, tree type, bool named)
+{
+  struct mips_arg_info info;
 
-  /* Emit the loop body.  */
-  mips_block_move_straight (dest, src, MAX_MOVE_BYTES);
+  mips_arg_info (cum, mode, type, named, &info);
+  return info.stack_words > 0 ? info.reg_words * UNITS_PER_WORD : 0;
+}
 
-  /* Move on to the next block.  */
-  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)
-    emit_insn (gen_cmpdi (src_reg, final_src));
-  else
-    emit_insn (gen_cmpsi (src_reg, final_src));
-  emit_jump_insn (gen_bne (label));
+/* Implement FUNCTION_ARG_BOUNDARY.  Every parameter gets at least
+   PARM_BOUNDARY bits of alignment, but will be given anything up
+   to STACK_BOUNDARY bits if the type requires it.  */
 
-  /* Mop up any left-over bytes.  */
-  if (leftover)
-    mips_block_move_straight (dest, src, leftover);
+int
+function_arg_boundary (enum machine_mode mode, tree type)
+{
+  unsigned int alignment;
+
+  alignment = type ? TYPE_ALIGN (type) : GET_MODE_ALIGNMENT (mode);
+  if (alignment < PARM_BOUNDARY)
+    alignment = PARM_BOUNDARY;
+  if (alignment > STACK_BOUNDARY)
+    alignment = STACK_BOUNDARY;
+  return alignment;
 }
-\f
 
-/* Expand a loop of synci insns for the address range [BEGIN, END).  */
+/* Return true if FUNCTION_ARG_PADDING (MODE, TYPE) should return
+   upward rather than downward.  In other words, return true if the
+   first byte of the stack slot has useful data, false if the last
+   byte does.  */
 
-void
-mips_expand_synci_loop (rtx begin, rtx end)
+bool
+mips_pad_arg_upward (enum machine_mode mode, const_tree type)
 {
-  rtx inc, label, cmp, cmp_result;
+  /* On little-endian targets, the first byte of every stack argument
+     is passed in the first byte of the stack slot.  */
+  if (!BYTES_BIG_ENDIAN)
+    return true;
 
-  /* Load INC with the cache line size (rdhwr INC,$1). */
-  inc = gen_reg_rtx (SImode);
-  emit_insn (gen_rdhwr (inc, const1_rtx));
+  /* Otherwise, integral types are padded downward: the last byte of a
+     stack argument is passed in the last byte of the stack slot.  */
+  if (type != 0
+      ? (INTEGRAL_TYPE_P (type)
+        || POINTER_TYPE_P (type)
+        || FIXED_POINT_TYPE_P (type))
+      : (GET_MODE_CLASS (mode) == MODE_INT
+        || ALL_SCALAR_FIXED_POINT_MODE_P (mode)))
+    return false;
 
-  /* Loop back to here.  */
-  label = gen_label_rtx ();
-  emit_label (label);
+  /* Big-endian o64 pads floating-point arguments downward.  */
+  if (mips_abi == ABI_O64)
+    if (type != 0 ? FLOAT_TYPE_P (type) : GET_MODE_CLASS (mode) == MODE_FLOAT)
+      return false;
 
-  emit_insn (gen_synci (begin));
+  /* Other types are padded upward for o32, o64, n32 and n64.  */
+  if (mips_abi != ABI_EABI)
+    return true;
 
-  cmp = gen_reg_rtx (Pmode);
-  mips_emit_binary (GTU, cmp, begin, end);
+  /* Arguments smaller than a stack slot are padded downward.  */
+  if (mode != BLKmode)
+    return (GET_MODE_BITSIZE (mode) >= PARM_BOUNDARY);
+  else
+    return (int_size_in_bytes (type) >= (PARM_BOUNDARY / BITS_PER_UNIT));
+}
 
-  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.  */
+/* Likewise BLOCK_REG_PADDING (MODE, TYPE, ...).  Return !BYTES_BIG_ENDIAN
+   if the least significant byte of the register has useful data.  Return
+   the opposite if the most significant byte does.  */
 
 bool
-mips_expand_block_move (rtx dest, rtx src, rtx length)
+mips_pad_reg_upward (enum machine_mode mode, tree type)
 {
-  if (GET_CODE (length) == CONST_INT)
-    {
-      if (INTVAL (length) <= 2 * MAX_MOVE_BYTES)
-       {
-         mips_block_move_straight (dest, src, INTVAL (length));
-         return true;
-       }
-      else if (optimize)
-       {
-         mips_block_move_loop (dest, src, INTVAL (length));
-         return true;
-       }
-    }
-  return false;
+  /* No shifting is required for floating-point arguments.  */
+  if (type != 0 ? FLOAT_TYPE_P (type) : GET_MODE_CLASS (mode) == MODE_FLOAT)
+    return !BYTES_BIG_ENDIAN;
+
+  /* Otherwise, apply the same padding to register arguments as we do
+     to stack arguments.  */
+  return mips_pad_arg_upward (mode, type);
 }
-\f
-/* Argument support functions.  */
 
-/* Initialize CUMULATIVE_ARGS for a function.  */
 
-void
-init_cumulative_args (CUMULATIVE_ARGS *cum, tree fntype,
-                     rtx libname ATTRIBUTE_UNUSED)
-{
-  static CUMULATIVE_ARGS zero_cum;
-  tree param, next_param;
+/* Return nonzero when an argument must be passed by reference.  */
 
-  *cum = zero_cum;
-  cum->prototype = (fntype && TYPE_ARG_TYPES (fntype));
+static bool
+mips_pass_by_reference (CUMULATIVE_ARGS *cum ATTRIBUTE_UNUSED,
+                       enum machine_mode mode, const_tree type,
+                       bool named ATTRIBUTE_UNUSED)
+{
+  if (mips_abi == ABI_EABI)
+    {
+      int size;
 
-  /* Determine if this function has variable arguments.  This is
-     indicated by the last argument being 'void_type_mode' if there
-     are no variable arguments.  The standard MIPS calling sequence
-     passes all arguments in the general purpose registers in this case.  */
+      /* ??? How should SCmode be handled?  */
+      if (mode == DImode || mode == DFmode
+         || mode == DQmode || mode == UDQmode
+         || mode == DAmode || mode == UDAmode)
+       return 0;
 
-  for (param = fntype ? TYPE_ARG_TYPES (fntype) : 0;
-       param != 0; param = next_param)
+      size = type ? int_size_in_bytes (type) : GET_MODE_SIZE (mode);
+      return size == -1 || size > UNITS_PER_WORD;
+    }
+  else
     {
-      next_param = TREE_CHAIN (param);
-      if (next_param == 0 && TREE_VALUE (param) != void_type_node)
-       cum->gp_reg_found = 1;
+      /* If we have a variable-sized parameter, we have no choice.  */
+      return targetm.calls.must_pass_in_stack (mode, type);
     }
 }
 
+static bool
+mips_callee_copies (CUMULATIVE_ARGS *cum ATTRIBUTE_UNUSED,
+                   enum machine_mode mode ATTRIBUTE_UNUSED,
+                   const_tree type ATTRIBUTE_UNUSED, bool named)
+{
+  return mips_abi == ABI_EABI && named;
+}
+\f
+/* See whether VALTYPE is a record whose fields should be returned in
+   floating-point registers.  If so, return the number of fields and
+   list them in FIELDS (which should have two elements).  Return 0
+   otherwise.
 
-/* Fill INFO with information about a single argument.  CUM is the
-   cumulative state for earlier arguments.  MODE is the mode of this
-   argument and TYPE is its type (if known).  NAMED is true if this
-   is a named (fixed) argument rather than a variable one.  */
+   For n32 & n64, a structure with one or two fields is returned in
+   floating-point registers as long as every field has a floating-point
+   type.  */
 
-static void
-mips_arg_info (const CUMULATIVE_ARGS *cum, enum machine_mode mode,
-              tree type, int named, struct mips_arg_info *info)
+static int
+mips_fpr_return_fields (const_tree valtype, tree *fields)
 {
-  bool doubleword_aligned_p;
-  unsigned int num_bytes, num_words, max_regs;
+  tree field;
+  int i;
 
-  /* Work out the size of the argument.  */
-  num_bytes = type ? int_size_in_bytes (type) : GET_MODE_SIZE (mode);
-  num_words = (num_bytes + UNITS_PER_WORD - 1) / UNITS_PER_WORD;
+  if (!TARGET_NEWABI)
+    return 0;
 
-  /* Decide whether it should go in a floating-point register, assuming
-     one is free.  Later code checks for availability.
+  if (TREE_CODE (valtype) != RECORD_TYPE)
+    return 0;
 
-     The checks against UNITS_PER_FPVALUE handle the soft-float and
-     single-float cases.  */
-  switch (mips_abi)
+  i = 0;
+  for (field = TYPE_FIELDS (valtype); field != 0; field = TREE_CHAIN (field))
     {
-    case ABI_EABI:
-      /* The EABI conventions have traditionally been defined in terms
-        of TYPE_MODE, regardless of the actual type.  */
-      info->fpr_p = ((GET_MODE_CLASS (mode) == MODE_FLOAT
-                     || GET_MODE_CLASS (mode) == MODE_VECTOR_FLOAT)
-                    && GET_MODE_SIZE (mode) <= UNITS_PER_FPVALUE);
-      break;
+      if (TREE_CODE (field) != FIELD_DECL)
+       continue;
 
-    case ABI_32:
-    case ABI_O64:
-      /* Only leading floating-point scalars are passed in
-        floating-point registers.  We also handle vector floats the same
-        say, which is OK because they are not covered by the standard ABI.  */
-      info->fpr_p = (!cum->gp_reg_found
-                    && cum->arg_number < 2
-                    && (type == 0 || SCALAR_FLOAT_TYPE_P (type)
-                        || VECTOR_FLOAT_TYPE_P (type))
-                    && (GET_MODE_CLASS (mode) == MODE_FLOAT
-                        || GET_MODE_CLASS (mode) == MODE_VECTOR_FLOAT)
-                    && GET_MODE_SIZE (mode) <= UNITS_PER_FPVALUE);
-      break;
+      if (TREE_CODE (TREE_TYPE (field)) != REAL_TYPE)
+       return 0;
 
-    case ABI_N32:
-    case ABI_64:
-      /* Scalar and complex floating-point types are passed in
-        floating-point registers.  */
-      info->fpr_p = (named
-                    && (type == 0 || FLOAT_TYPE_P (type))
-                    && (GET_MODE_CLASS (mode) == MODE_FLOAT
-                        || GET_MODE_CLASS (mode) == MODE_COMPLEX_FLOAT
-                        || GET_MODE_CLASS (mode) == MODE_VECTOR_FLOAT)
-                    && GET_MODE_UNIT_SIZE (mode) <= UNITS_PER_FPVALUE);
+      if (i == 2)
+       return 0;
 
-      /* ??? According to the ABI documentation, the real and imaginary
-        parts of complex floats should be passed in individual registers.
-        The real and imaginary parts of stack arguments are supposed
-        to be contiguous and there should be an extra word of padding
-        at the end.
+      fields[i++] = field;
+    }
+  return i;
+}
 
-        This has two problems.  First, it makes it impossible to use a
-        single "void *" va_list type, since register and stack arguments
-        are passed differently.  (At the time of writing, MIPSpro cannot
-        handle complex float varargs correctly.)  Second, it's unclear
-        what should happen when there is only one register free.
 
-        For now, we assume that named complex floats should go into FPRs
-        if there are two FPRs free, otherwise they should be passed in the
-        same way as a struct containing two floats.  */
-      if (info->fpr_p
-         && GET_MODE_CLASS (mode) == MODE_COMPLEX_FLOAT
-         && GET_MODE_UNIT_SIZE (mode) < UNITS_PER_FPVALUE)
-       {
-         if (cum->num_gprs >= MAX_ARGS_IN_REGISTERS - 1)
-           info->fpr_p = false;
-         else
-           num_words = 2;
-       }
-      break;
+/* Implement TARGET_RETURN_IN_MSB.  For n32 & n64, we should return
+   a value in the most significant part of $2/$3 if:
 
-    default:
-      gcc_unreachable ();
-    }
+      - the target is big-endian;
 
-  /* See whether the argument has doubleword alignment.  */
-  doubleword_aligned_p = FUNCTION_ARG_BOUNDARY (mode, type) > BITS_PER_WORD;
+      - the value has a structure or union type (we generalize this to
+       cover aggregates from other languages too); and
 
-  /* Set REG_OFFSET to the register count we're interested in.
-     The EABI allocates the floating-point registers separately,
-     but the other ABIs allocate them like integer registers.  */
-  info->reg_offset = (mips_abi == ABI_EABI && info->fpr_p
-                     ? cum->num_fprs
-                     : cum->num_gprs);
-
-  /* Advance to an even register if the argument is doubleword-aligned.  */
-  if (doubleword_aligned_p)
-    info->reg_offset += info->reg_offset & 1;
-
-  /* Work out the offset of a stack argument.  */
-  info->stack_offset = cum->stack_words;
-  if (doubleword_aligned_p)
-    info->stack_offset += info->stack_offset & 1;
+      - the structure is not returned in floating-point registers.  */
 
-  max_regs = MAX_ARGS_IN_REGISTERS - info->reg_offset;
+static bool
+mips_return_in_msb (const_tree valtype)
+{
+  tree fields[2];
 
-  /* Partition the argument between registers and stack.  */
-  info->reg_words = MIN (num_words, max_regs);
-  info->stack_words = num_words - info->reg_words;
+  return (TARGET_NEWABI
+         && TARGET_BIG_ENDIAN
+         && AGGREGATE_TYPE_P (valtype)
+         && mips_fpr_return_fields (valtype, fields) == 0);
 }
 
 
-/* INFO describes an argument that is passed in a single-register value.
-   Return the register it uses, assuming that FPRs are available if
-   HARD_FLOAT_P.  */
+/* Return true if the function return value MODE will get returned in a
+   floating-point register.  */
 
-static unsigned int
-mips_arg_regno (const struct mips_arg_info *info, bool hard_float_p)
+static bool
+mips_return_mode_in_fpr_p (enum machine_mode mode)
 {
-  if (!info->fpr_p || !hard_float_p)
-    return GP_ARG_FIRST + info->reg_offset;
-  else if (mips_abi == ABI_32 && TARGET_DOUBLE_FLOAT && info->reg_offset > 0)
-    /* In o32, the second argument is always passed in $f14
-       for TARGET_DOUBLE_FLOAT, regardless of whether the
-       first argument was a word or doubleword.  */
-    return FP_ARG_FIRST + 2;
-  else
-    return FP_ARG_FIRST + info->reg_offset;
+  return ((GET_MODE_CLASS (mode) == MODE_FLOAT
+          || GET_MODE_CLASS (mode) == MODE_VECTOR_FLOAT
+          || GET_MODE_CLASS (mode) == MODE_COMPLEX_FLOAT)
+         && GET_MODE_UNIT_SIZE (mode) <= UNITS_PER_HWFPVALUE);
 }
 
-/* Implement FUNCTION_ARG_ADVANCE.  */
-
-void
-function_arg_advance (CUMULATIVE_ARGS *cum, enum machine_mode mode,
-                     tree type, int named)
-{
-  struct mips_arg_info info;
-
-  mips_arg_info (cum, mode, type, named, &info);
-
-  if (!info.fpr_p)
-    cum->gp_reg_found = true;
+/* Return a composite value in a pair of floating-point registers.
+   MODE1 and OFFSET1 are the mode and byte offset for the first value,
+   likewise MODE2 and OFFSET2 for the second.  MODE is the mode of the
+   complete value.
 
-  /* See the comment above the cumulative args structure in mips.h
-     for an explanation of what this code does.  It assumes the O32
-     ABI, which passes at most 2 arguments in float registers.  */
-  if (cum->arg_number < 2 && info.fpr_p)
-    cum->fp_code += (mode == SFmode ? 1 : 2) << (cum->arg_number * 2);
+   For n32 & n64, $f0 always holds the first value and $f2 the second.
+   Otherwise the values are packed together as closely as possible.  */
 
-  if (mips_abi != ABI_EABI || !info.fpr_p)
-    cum->num_gprs = info.reg_offset + info.reg_words;
-  else if (info.reg_words > 0)
-    cum->num_fprs += MAX_FPRS_PER_FMT;
+static rtx
+mips_return_fpr_pair (enum machine_mode mode,
+                     enum machine_mode mode1, HOST_WIDE_INT offset1,
+                     enum machine_mode mode2, HOST_WIDE_INT offset2)
+{
+  int inc;
 
-  if (info.stack_words > 0)
-    cum->stack_words = info.stack_offset + info.stack_words;
+  inc = (TARGET_NEWABI ? 2 : MAX_FPRS_PER_FMT);
+  return gen_rtx_PARALLEL
+    (mode,
+     gen_rtvec (2,
+               gen_rtx_EXPR_LIST (VOIDmode,
+                                  gen_rtx_REG (mode1, FP_RETURN),
+                                  GEN_INT (offset1)),
+               gen_rtx_EXPR_LIST (VOIDmode,
+                                  gen_rtx_REG (mode2, FP_RETURN + inc),
+                                  GEN_INT (offset2))));
 
-  cum->arg_number++;
 }
 
-/* Implement FUNCTION_ARG.  */
 
-struct rtx_def *
-function_arg (const CUMULATIVE_ARGS *cum, enum machine_mode mode,
-             tree type, int named)
-{
-  struct mips_arg_info info;
+/* Implement FUNCTION_VALUE and LIBCALL_VALUE.  For normal calls,
+   VALTYPE is the return type and MODE is VOIDmode.  For libcalls,
+   VALTYPE is null and MODE is the mode of the return value.  */
 
-  /* We will be called with a mode of VOIDmode after the last argument
-     has been seen.  Whatever we return will be passed to the call
-     insn.  If we need a mips16 fp_code, return a REG with the code
-     stored as the mode.  */
-  if (mode == VOIDmode)
+rtx
+mips_function_value (const_tree valtype, const_tree func ATTRIBUTE_UNUSED,
+                    enum machine_mode mode)
+{
+  if (valtype)
     {
-      if (TARGET_MIPS16 && cum->fp_code != 0)
-       return gen_rtx_REG ((enum machine_mode) cum->fp_code, 0);
-
-      else
-       return 0;
-    }
-
-  mips_arg_info (cum, mode, type, named, &info);
-
-  /* Return straight away if the whole argument is passed on the stack.  */
-  if (info.reg_offset == MAX_ARGS_IN_REGISTERS)
-    return 0;
+      tree fields[2];
+      int unsignedp;
 
-  if (type != 0
-      && TREE_CODE (type) == RECORD_TYPE
-      && TARGET_NEWABI
-      && TYPE_SIZE_UNIT (type)
-      && host_integerp (TYPE_SIZE_UNIT (type), 1)
-      && named)
-    {
-      /* The Irix 6 n32/n64 ABIs say that if any 64-bit chunk of the
-        structure contains a double in its entirety, then that 64-bit
-        chunk is passed in a floating point register.  */
-      tree field;
+      mode = TYPE_MODE (valtype);
+      unsignedp = TYPE_UNSIGNED (valtype);
 
-      /* First check to see if there is any such field.  */
-      for (field = TYPE_FIELDS (type); field; field = TREE_CHAIN (field))
-       if (TREE_CODE (field) == FIELD_DECL
-           && TREE_CODE (TREE_TYPE (field)) == REAL_TYPE
-           && TYPE_PRECISION (TREE_TYPE (field)) == BITS_PER_WORD
-           && host_integerp (bit_position (field), 0)
-           && int_bit_position (field) % BITS_PER_WORD == 0)
-         break;
+      /* Since we define TARGET_PROMOTE_FUNCTION_RETURN that returns
+        true, we must promote the mode just as PROMOTE_MODE does.  */
+      mode = promote_mode (valtype, mode, &unsignedp, 1);
 
-      if (field != 0)
+      /* Handle structures whose fields are returned in $f0/$f2.  */
+      switch (mips_fpr_return_fields (valtype, fields))
        {
-         /* Now handle the special case by returning a PARALLEL
-            indicating where each 64-bit chunk goes.  INFO.REG_WORDS
-            chunks are passed in registers.  */
-         unsigned int i;
-         HOST_WIDE_INT bitpos;
-         rtx ret;
+       case 1:
+         return gen_rtx_REG (mode, FP_RETURN);
 
-         /* assign_parms checks the mode of ENTRY_PARM, so we must
-            use the actual mode here.  */
-         ret = gen_rtx_PARALLEL (mode, rtvec_alloc (info.reg_words));
+       case 2:
+         return mips_return_fpr_pair (mode,
+                                      TYPE_MODE (TREE_TYPE (fields[0])),
+                                      int_byte_position (fields[0]),
+                                      TYPE_MODE (TREE_TYPE (fields[1])),
+                                      int_byte_position (fields[1]));
+       }
 
-         bitpos = 0;
-         field = TYPE_FIELDS (type);
-         for (i = 0; i < info.reg_words; i++)
+      /* If a value is passed in the most significant part of a register, see
+        whether we have to round the mode up to a whole number of words.  */
+      if (mips_return_in_msb (valtype))
+       {
+         HOST_WIDE_INT size = int_size_in_bytes (valtype);
+         if (size % UNITS_PER_WORD != 0)
            {
-             rtx reg;
-
-             for (; field; field = TREE_CHAIN (field))
-               if (TREE_CODE (field) == FIELD_DECL
-                   && int_bit_position (field) >= bitpos)
-                 break;
-
-             if (field
-                 && int_bit_position (field) == bitpos
-                 && TREE_CODE (TREE_TYPE (field)) == REAL_TYPE
-                 && !TARGET_SOFT_FLOAT
-                 && TYPE_PRECISION (TREE_TYPE (field)) == BITS_PER_WORD)
-               reg = gen_rtx_REG (DFmode, FP_ARG_FIRST + info.reg_offset + i);
-             else
-               reg = gen_rtx_REG (DImode, GP_ARG_FIRST + info.reg_offset + i);
-
-             XVECEXP (ret, 0, i)
-               = gen_rtx_EXPR_LIST (VOIDmode, reg,
-                                    GEN_INT (bitpos / BITS_PER_UNIT));
-
-             bitpos += BITS_PER_WORD;
+             size += UNITS_PER_WORD - size % UNITS_PER_WORD;
+             mode = mode_for_size (size * BITS_PER_UNIT, MODE_INT, 0);
            }
-         return ret;
        }
+
+      /* For EABI, the class of return register depends entirely on MODE.
+        For example, "struct { some_type x; }" and "union { some_type x; }"
+        are returned in the same way as a bare "some_type" would be.
+        Other ABIs only use FPRs for scalar, complex or vector types.  */
+      if (mips_abi != ABI_EABI && !FLOAT_TYPE_P (valtype))
+       return gen_rtx_REG (mode, GP_RETURN);
     }
 
-  /* Handle the n32/n64 conventions for passing complex floating-point
-     arguments in FPR pairs.  The real part goes in the lower register
-     and the imaginary part goes in the upper register.  */
-  if (TARGET_NEWABI
-      && info.fpr_p
-      && GET_MODE_CLASS (mode) == MODE_COMPLEX_FLOAT)
+  if (!TARGET_MIPS16)
     {
-      rtx real, imag;
-      enum machine_mode inner;
-      int reg;
+      /* Handle long doubles for n32 & n64.  */
+      if (mode == TFmode)
+       return mips_return_fpr_pair (mode,
+                                    DImode, 0,
+                                    DImode, GET_MODE_SIZE (mode) / 2);
 
-      inner = GET_MODE_INNER (mode);
-      reg = FP_ARG_FIRST + info.reg_offset;
-      if (info.reg_words * UNITS_PER_WORD == GET_MODE_SIZE (inner))
-       {
-         /* Real part in registers, imaginary part on stack.  */
-         gcc_assert (info.stack_words == info.reg_words);
-         return gen_rtx_REG (inner, reg);
-       }
-      else
+      if (mips_return_mode_in_fpr_p (mode))
        {
-         gcc_assert (info.stack_words == 0);
-         real = gen_rtx_EXPR_LIST (VOIDmode,
-                                   gen_rtx_REG (inner, reg),
-                                   const0_rtx);
-         imag = gen_rtx_EXPR_LIST (VOIDmode,
-                                   gen_rtx_REG (inner,
-                                                reg + info.reg_words / 2),
-                                   GEN_INT (GET_MODE_SIZE (inner)));
-         return gen_rtx_PARALLEL (mode, gen_rtvec (2, real, imag));
+         if (GET_MODE_CLASS (mode) == MODE_COMPLEX_FLOAT)
+           return mips_return_fpr_pair (mode,
+                                        GET_MODE_INNER (mode), 0,
+                                        GET_MODE_INNER (mode),
+                                        GET_MODE_SIZE (mode) / 2);
+         else
+           return gen_rtx_REG (mode, FP_RETURN);
        }
     }
 
-  return gen_rtx_REG (mode, mips_arg_regno (&info, TARGET_HARD_FLOAT));
+  return gen_rtx_REG (mode, GP_RETURN);
 }
 
+/* Implement TARGET_RETURN_IN_MEMORY.  Under the old (i.e., 32 and O64 ABIs)
+   all BLKmode objects are returned in memory.  Under the new (N32 and
+   64-bit MIPS ABIs) small structures are returned in a register.
+   Objects with varying size must still be returned in memory, of
+   course.  */
 
-/* Implement TARGET_ARG_PARTIAL_BYTES.  */
-
-static int
-mips_arg_partial_bytes (CUMULATIVE_ARGS *cum,
-                       enum machine_mode mode, tree type, bool named)
-{
-  struct mips_arg_info info;
-
-  mips_arg_info (cum, mode, type, named, &info);
-  return info.stack_words > 0 ? info.reg_words * UNITS_PER_WORD : 0;
-}
-
-
-/* Implement FUNCTION_ARG_BOUNDARY.  Every parameter gets at least
-   PARM_BOUNDARY bits of alignment, but will be given anything up
-   to STACK_BOUNDARY bits if the type requires it.  */
-
-int
-function_arg_boundary (enum machine_mode mode, tree type)
-{
-  unsigned int alignment;
-
-  alignment = type ? TYPE_ALIGN (type) : GET_MODE_ALIGNMENT (mode);
-  if (alignment < PARM_BOUNDARY)
-    alignment = PARM_BOUNDARY;
-  if (alignment > STACK_BOUNDARY)
-    alignment = STACK_BOUNDARY;
-  return alignment;
-}
-
-/* Return true if FUNCTION_ARG_PADDING (MODE, TYPE) should return
-   upward rather than downward.  In other words, return true if the
-   first byte of the stack slot has useful data, false if the last
-   byte does.  */
-
-bool
-mips_pad_arg_upward (enum machine_mode mode, const_tree type)
+static bool
+mips_return_in_memory (const_tree type, const_tree fndecl ATTRIBUTE_UNUSED)
 {
-  /* On little-endian targets, the first byte of every stack argument
-     is passed in the first byte of the stack slot.  */
-  if (!BYTES_BIG_ENDIAN)
-    return true;
-
-  /* Otherwise, integral types are padded downward: the last byte of a
-     stack argument is passed in the last byte of the stack slot.  */
-  if (type != 0
-      ? (INTEGRAL_TYPE_P (type)
-        || POINTER_TYPE_P (type)
-        || FIXED_POINT_TYPE_P (type))
-      : (GET_MODE_CLASS (mode) == MODE_INT
-        || ALL_SCALAR_FIXED_POINT_MODE_P (mode)))
-    return false;
-
-  /* Big-endian o64 pads floating-point arguments downward.  */
-  if (mips_abi == ABI_O64)
-    if (type != 0 ? FLOAT_TYPE_P (type) : GET_MODE_CLASS (mode) == MODE_FLOAT)
-      return false;
-
-  /* Other types are padded upward for o32, o64, n32 and n64.  */
-  if (mips_abi != ABI_EABI)
-    return true;
-
-  /* Arguments smaller than a stack slot are padded downward.  */
-  if (mode != BLKmode)
-    return (GET_MODE_BITSIZE (mode) >= PARM_BOUNDARY);
+  if (TARGET_OLDABI)
+    return (TYPE_MODE (type) == BLKmode);
   else
-    return (int_size_in_bytes (type) >= (PARM_BOUNDARY / BITS_PER_UNIT));
-}
-
-
-/* Likewise BLOCK_REG_PADDING (MODE, TYPE, ...).  Return !BYTES_BIG_ENDIAN
-   if the least significant byte of the register has useful data.  Return
-   the opposite if the most significant byte does.  */
-
-bool
-mips_pad_reg_upward (enum machine_mode mode, tree type)
-{
-  /* No shifting is required for floating-point arguments.  */
-  if (type != 0 ? FLOAT_TYPE_P (type) : GET_MODE_CLASS (mode) == MODE_FLOAT)
-    return !BYTES_BIG_ENDIAN;
-
-  /* Otherwise, apply the same padding to register arguments as we do
-     to stack arguments.  */
-  return mips_pad_arg_upward (mode, type);
+    return ((int_size_in_bytes (type) > (2 * UNITS_PER_WORD))
+           || (int_size_in_bytes (type) == -1));
 }
 \f
 static void
@@ -5294,1168 +5060,1198 @@ mips_gimplify_va_arg_expr (tree valist, tree type, tree *pre_p, tree *post_p)
   return addr;
 }
 \f
-/* Return true if it is possible to use left/right accesses for a
-   bitfield of WIDTH bits starting BITPOS bits into *OP.  When
-   returning true, update *OP, *LEFT and *RIGHT as follows:
-
-   *OP is a BLKmode reference to the whole field.
-
-   *LEFT is a QImode reference to the first byte if big endian or
-   the last byte if little endian.  This address can be used in the
-   left-side instructions (lwl, swl, ldl, sdl).
-
-   *RIGHT is a QImode reference to the opposite end of the field and
-   can be used in the patterning right-side instruction.  */
+/* We keep a list of functions for which we have already built stubs
+   in build_mips16_call_stub.  */
 
-static bool
-mips_get_unaligned_mem (rtx *op, unsigned int width, int bitpos,
-                       rtx *left, rtx *right)
+struct mips16_stub
 {
-  rtx first, last;
+  struct mips16_stub *next;
+  char *name;
+  int fpret;
+};
 
-  /* Check that the operand really is a MEM.  Not all the extv and
-     extzv predicates are checked.  */
-  if (!MEM_P (*op))
-    return false;
+static struct mips16_stub *mips16_stubs;
 
-  /* Check that the size is valid.  */
-  if (width != 32 && (!TARGET_64BIT || width != 64))
-    return false;
+/* Return a two-character string representing a function floating-point
+   return mode, used to name MIPS16 function stubs.  */
 
-  /* We can only access byte-aligned values.  Since we are always passed
-     a reference to the first byte of the field, it is not necessary to
-     do anything with BITPOS after this check.  */
-  if (bitpos % BITS_PER_UNIT != 0)
-    return false;
+static const char *
+mips16_call_stub_mode_suffix (enum machine_mode mode)
+{
+  if (mode == SFmode)
+    return "sf";
+  else if (mode == DFmode)
+    return "df";
+  else if (mode == SCmode)
+    return "sc";
+  else if (mode == DCmode)
+    return "dc";
+  else if (mode == V2SFmode)
+    return "df";
+  else
+    gcc_unreachable ();
+}
 
-  /* Reject aligned bitfields: we want to use a normal load or store
-     instead of a left/right pair.  */
-  if (MEM_ALIGN (*op) >= width)
-    return false;
+/* Write out code to move floating point arguments in or out of
+   general registers.  Output the instructions to FILE.  FP_CODE is
+   the code describing which arguments are present (see the comment at
+   the definition of CUMULATIVE_ARGS in mips.h).  FROM_FP_P is nonzero if
+   we are copying from the floating point registers.  */
 
-  /* Adjust *OP to refer to the whole field.  This also has the effect
-     of legitimizing *OP's address for BLKmode, possibly simplifying it.  */
-  *op = adjust_address (*op, BLKmode, 0);
-  set_mem_size (*op, GEN_INT (width / BITS_PER_UNIT));
+static void
+mips16_fp_args (FILE *file, int fp_code, int from_fp_p)
+{
+  const char *s;
+  int gparg, fparg;
+  unsigned int f;
+  CUMULATIVE_ARGS cum;
 
-  /* Get references to both ends of the field.  We deliberately don't
-     use the original QImode *OP for FIRST since the new BLKmode one
-     might have a simpler address.  */
-  first = adjust_address (*op, QImode, 0);
-  last = adjust_address (*op, QImode, width / BITS_PER_UNIT - 1);
+  /* This code only works for the original 32-bit ABI and the O64 ABI.  */
+  gcc_assert (TARGET_OLDABI);
 
-  /* Allocate to LEFT and RIGHT according to endianness.  LEFT should
-     be the upper word and RIGHT the lower word.  */
-  if (TARGET_BIG_ENDIAN)
-    *left = first, *right = last;
+  if (from_fp_p)
+    s = "mfc1";
   else
-    *left = last, *right = first;
-
-  return true;
-}
-
+    s = "mtc1";
 
-/* Try to emit the equivalent of (set DEST (zero_extract SRC WIDTH BITPOS)).
-   Return true on success.  We only handle cases where zero_extract is
-   equivalent to sign_extract.  */
+  init_cumulative_args (&cum, NULL, NULL);
 
-bool
-mips_expand_unaligned_load (rtx dest, rtx src, unsigned int width, int bitpos)
-{
-  rtx left, right, temp;
+  for (f = (unsigned int) fp_code; f != 0; f >>= 2)
+    {
+      enum machine_mode mode;
+      struct mips_arg_info info;
 
-  /* If TARGET_64BIT, the destination of a 32-bit load will be a
-     paradoxical word_mode subreg.  This is the only case in which
-     we allow the destination to be larger than the source.  */
-  if (GET_CODE (dest) == SUBREG
-      && GET_MODE (dest) == DImode
-      && SUBREG_BYTE (dest) == 0
-      && GET_MODE (SUBREG_REG (dest)) == SImode)
-    dest = SUBREG_REG (dest);
+      if ((f & 3) == 1)
+       mode = SFmode;
+      else if ((f & 3) == 2)
+       mode = DFmode;
+      else
+       gcc_unreachable ();
 
-  /* After the above adjustment, the destination must be the same
-     width as the source.  */
-  if (GET_MODE_BITSIZE (GET_MODE (dest)) != width)
-    return false;
+      mips_arg_info (&cum, mode, NULL, true, &info);
+      gparg = mips_arg_regno (&info, false);
+      fparg = mips_arg_regno (&info, true);
 
-  if (!mips_get_unaligned_mem (&src, width, bitpos, &left, &right))
-    return false;
+      if (mode == SFmode)
+       fprintf (file, "\t%s\t%s,%s\n", s,
+                reg_names[gparg], reg_names[fparg]);
+      else if (TARGET_64BIT)
+       fprintf (file, "\td%s\t%s,%s\n", s,
+                reg_names[gparg], reg_names[fparg]);
+      else if (ISA_HAS_MXHC1)
+       /* -mips32r2 -mfp64 */
+       fprintf (file, "\t%s\t%s,%s\n\t%s\t%s,%s\n",
+                s,
+                reg_names[gparg + (WORDS_BIG_ENDIAN ? 1 : 0)],
+                reg_names[fparg],
+                from_fp_p ? "mfhc1" : "mthc1",
+                reg_names[gparg + (WORDS_BIG_ENDIAN ? 0 : 1)],
+                reg_names[fparg]);
+      else if (TARGET_BIG_ENDIAN)
+       fprintf (file, "\t%s\t%s,%s\n\t%s\t%s,%s\n", s,
+                reg_names[gparg], reg_names[fparg + 1], s,
+                reg_names[gparg + 1], reg_names[fparg]);
+      else
+       fprintf (file, "\t%s\t%s,%s\n\t%s\t%s,%s\n", s,
+                reg_names[gparg], reg_names[fparg], s,
+                reg_names[gparg + 1], reg_names[fparg + 1]);
 
-  temp = gen_reg_rtx (GET_MODE (dest));
-  if (GET_MODE (dest) == DImode)
-    {
-      emit_insn (gen_mov_ldl (temp, src, left));
-      emit_insn (gen_mov_ldr (dest, copy_rtx (src), right, temp));
-    }
-  else
-    {
-      emit_insn (gen_mov_lwl (temp, src, left));
-      emit_insn (gen_mov_lwr (dest, copy_rtx (src), right, temp));
+      function_arg_advance (&cum, mode, NULL, true);
     }
-  return true;
 }
 
+/* Build a mips16 function stub.  This is used for functions which
+   take arguments in the floating point registers.  It is 32-bit code
+   that moves the floating point args into the general registers, and
+   then jumps to the 16-bit code.  */
 
-/* Try to expand (set (zero_extract DEST WIDTH BITPOS) SRC).  Return
-   true on success.  */
-
-bool
-mips_expand_unaligned_store (rtx dest, rtx src, unsigned int width, int bitpos)
+static void
+build_mips16_function_stub (FILE *file)
 {
-  rtx left, right;
-  enum machine_mode mode;
-
-  if (!mips_get_unaligned_mem (&dest, width, bitpos, &left, &right))
-    return false;
+  const char *fnname;
+  char *secname, *stubname;
+  tree stubid, stubdecl;
+  int need_comma;
+  unsigned int f;
 
-  mode = mode_for_size (width, MODE_INT, 0);
-  src = gen_lowpart (mode, src);
+  fnname = XSTR (XEXP (DECL_RTL (current_function_decl), 0), 0);
+  fnname = targetm.strip_name_encoding (fnname);
+  secname = (char *) alloca (strlen (fnname) + 20);
+  sprintf (secname, ".mips16.fn.%s", fnname);
+  stubname = (char *) alloca (strlen (fnname) + 20);
+  sprintf (stubname, "__fn_stub_%s", fnname);
+  stubid = get_identifier (stubname);
+  stubdecl = build_decl (FUNCTION_DECL, stubid,
+                        build_function_type (void_type_node, NULL_TREE));
+  DECL_SECTION_NAME (stubdecl) = build_string (strlen (secname), secname);
+  DECL_RESULT (stubdecl) = build_decl (RESULT_DECL, NULL_TREE, void_type_node);
 
-  if (mode == DImode)
-    {
-      emit_insn (gen_mov_sdl (dest, src, left));
-      emit_insn (gen_mov_sdr (copy_rtx (dest), copy_rtx (src), right));
-    }
-  else
+  fprintf (file, "\t# Stub function for %s (", current_function_name ());
+  need_comma = 0;
+  for (f = (unsigned int) current_function_args_info.fp_code; f != 0; f >>= 2)
     {
-      emit_insn (gen_mov_swl (dest, src, left));
-      emit_insn (gen_mov_swr (copy_rtx (dest), copy_rtx (src), right));
+      fprintf (file, "%s%s",
+              need_comma ? ", " : "",
+              (f & 3) == 1 ? "float" : "double");
+      need_comma = 1;
     }
-  return true;
-}
-
-/* Return true if X is a MEM with the same size as MODE.  */
-
-bool
-mips_mem_fits_mode_p (enum machine_mode mode, rtx x)
-{
-  rtx size;
+  fprintf (file, ")\n");
 
-  if (!MEM_P (x))
-    return false;
+  fprintf (file, "\t.set\tnomips16\n");
+  switch_to_section (function_section (stubdecl));
+  ASM_OUTPUT_ALIGN (file, floor_log2 (FUNCTION_BOUNDARY / BITS_PER_UNIT));
 
-  size = MEM_SIZE (x);
-  return size && INTVAL (size) == GET_MODE_SIZE (mode);
-}
+  /* ??? If FUNCTION_NAME_ALREADY_DECLARED is defined, then we are
+     within a .ent, and we cannot emit another .ent.  */
+  if (!FUNCTION_NAME_ALREADY_DECLARED)
+    {
+      fputs ("\t.ent\t", file);
+      assemble_name (file, stubname);
+      fputs ("\n", file);
+    }
 
-/* Return true if (zero_extract OP SIZE POSITION) can be used as the
-   source of an "ext" instruction or the destination of an "ins"
-   instruction.  OP must be a register operand and the following
-   conditions must hold:
+  assemble_name (file, stubname);
+  fputs (":\n", file);
 
-     0 <= POSITION < GET_MODE_BITSIZE (GET_MODE (op))
-     0 < SIZE <= GET_MODE_BITSIZE (GET_MODE (op))
-     0 < POSITION + SIZE <= GET_MODE_BITSIZE (GET_MODE (op))
+  /* We don't want the assembler to insert any nops here.  */
+  fprintf (file, "\t.set\tnoreorder\n");
 
-   Also reject lengths equal to a word as they are better handled
-   by the move patterns.  */
+  mips16_fp_args (file, current_function_args_info.fp_code, 1);
 
-bool
-mips_use_ins_ext_p (rtx op, rtx size, rtx position)
-{
-  HOST_WIDE_INT len, pos;
+  fprintf (asm_out_file, "\t.set\tnoat\n");
+  fprintf (asm_out_file, "\tla\t%s,", reg_names[GP_REG_FIRST + 1]);
+  assemble_name (file, fnname);
+  fprintf (file, "\n");
+  fprintf (asm_out_file, "\tjr\t%s\n", reg_names[GP_REG_FIRST + 1]);
+  fprintf (asm_out_file, "\t.set\tat\n");
 
-  if (!ISA_HAS_EXT_INS
-      || !register_operand (op, VOIDmode)
-      || GET_MODE_BITSIZE (GET_MODE (op)) > BITS_PER_WORD)
-    return false;
+  /* Unfortunately, we can't fill the jump delay slot.  We can't fill
+     with one of the mfc1 instructions, because the result is not
+     available for one instruction, so if the very first instruction
+     in the function refers to the register, it will see the wrong
+     value.  */
+  fprintf (file, "\tnop\n");
 
-  len = INTVAL (size);
-  pos = INTVAL (position);
+  fprintf (file, "\t.set\treorder\n");
 
-  if (len <= 0 || len >= GET_MODE_BITSIZE (GET_MODE (op))
-      || pos < 0 || pos + len > GET_MODE_BITSIZE (GET_MODE (op)))
-    return false;
+  if (!FUNCTION_NAME_ALREADY_DECLARED)
+    {
+      fputs ("\t.end\t", file);
+      assemble_name (file, stubname);
+      fputs ("\n", file);
+    }
 
-  return true;
+  switch_to_section (function_section (current_function_decl));
 }
 
-/* Set up globals to generate code for the ISA or processor
-   described by INFO.  */
+/* Emit code to return a double value from a mips16 stub.  GPREG is the
+   first GP reg to use, FPREG is the first FP reg to use.  */
 
 static void
-mips_set_architecture (const struct mips_cpu_info *info)
+mips16_fpret_double (int gpreg, int fpreg)
 {
-  if (info != 0)
+  if (TARGET_64BIT)
+    fprintf (asm_out_file, "\tdmfc1\t%s,%s\n",
+            reg_names[gpreg], reg_names[fpreg]);
+  else if (TARGET_FLOAT64)
     {
-      mips_arch_info = info;
-      mips_arch = info->cpu;
-      mips_isa = info->isa;
+      fprintf (asm_out_file, "\tmfc1\t%s,%s\n",
+              reg_names[gpreg + WORDS_BIG_ENDIAN],
+              reg_names[fpreg]);
+      fprintf (asm_out_file, "\tmfhc1\t%s,%s\n",
+              reg_names[gpreg + !WORDS_BIG_ENDIAN],
+              reg_names[fpreg]);
     }
-}
-
-
-/* Likewise for tuning.  */
-
-static void
-mips_set_tune (const struct mips_cpu_info *info)
-{
-  if (info != 0)
+  else
     {
-      mips_tune_info = info;
-      mips_tune = info->cpu;
+      if (TARGET_BIG_ENDIAN)
+       {
+         fprintf (asm_out_file, "\tmfc1\t%s,%s\n",
+                  reg_names[gpreg + 0],
+                  reg_names[fpreg + 1]);
+         fprintf (asm_out_file, "\tmfc1\t%s,%s\n",
+                  reg_names[gpreg + 1],
+                  reg_names[fpreg + 0]);
+       }
+      else
+       {
+         fprintf (asm_out_file, "\tmfc1\t%s,%s\n",
+                  reg_names[gpreg + 0],
+                  reg_names[fpreg + 0]);
+         fprintf (asm_out_file, "\tmfc1\t%s,%s\n",
+                  reg_names[gpreg + 1],
+                  reg_names[fpreg + 1]);
+       }
     }
 }
 
-/* Initialize mips_split_addresses from the associated command-line
-   settings.
-
-   mips_split_addresses is a half-way house between explicit
-   relocations and the traditional assembler macros.  It can
-   split absolute 32-bit symbolic constants into a high/lo_sum
-   pair but uses macros for other sorts of access.
+/* Build a call stub for a mips16 call.  A stub is needed if we are
+   passing any floating point values which should go into the floating
+   point registers.  If we are, and the call turns out to be to a
+   32-bit function, the stub will be used to move the values into the
+   floating point registers before calling the 32-bit function.  The
+   linker will magically adjust the function call to either the 16-bit
+   function or the 32-bit stub, depending upon where the function call
+   is actually defined.
 
-   Like explicit relocation support for REL targets, it relies
-   on GNU extensions in the assembler and the linker.
+   Similarly, we need a stub if the return value might come back in a
+   floating point register.
 
-   Although this code should work for -O0, it has traditionally
-   been treated as an optimization.  */
+   RETVAL is the location of the return value, or null if this is
+   a call rather than a call_value.  FN is the address of the
+   function and ARG_SIZE is the size of the arguments.  FP_CODE
+   is the code built by function_arg.  This function returns a nonzero
+   value if it builds the call instruction itself.  */
 
-static void
-mips_init_split_addresses (void)
+int
+build_mips16_call_stub (rtx retval, rtx fn, rtx arg_size, int fp_code)
 {
-  if (!TARGET_MIPS16 && TARGET_SPLIT_ADDRESSES
-      && optimize && !flag_pic
-      && !ABI_HAS_64BIT_SYMBOLS)
-    mips_split_addresses = 1;
-  else
-    mips_split_addresses = 0;
-}
+  int fpret = 0;
+  const char *fnname;
+  char *secname, *stubname;
+  struct mips16_stub *l;
+  tree stubid, stubdecl;
+  int need_comma;
+  unsigned int f;
+  rtx insn;
 
-/* (Re-)Initialize information about relocs.  */
+  /* We don't need to do anything if we aren't in mips16 mode, or if
+     we were invoked with the -msoft-float option.  */
+  if (!TARGET_MIPS16 || TARGET_SOFT_FLOAT_ABI)
+    return 0;
 
-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));
+  /* Figure out whether the value might come back in a floating point
+     register.  */
+  if (retval)
+    fpret = mips_return_mode_in_fpr_p (GET_MODE (retval));
 
-  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(";
+  /* We don't need to do anything if there were no floating point
+     arguments and the value will not be returned in a floating point
+     register.  */
+  if (fp_code == 0 && ! fpret)
+    return 0;
 
-         mips_split_p[SYMBOL_64_MID] = true;
-         mips_hi_relocs[SYMBOL_64_MID] = "%higher(";
-         mips_lo_relocs[SYMBOL_64_MID] = "%hi(";
+  /* We don't need to do anything if this is a call to a special
+     mips16 support function.  */
+  if (GET_CODE (fn) == SYMBOL_REF
+      && strncmp (XSTR (fn, 0), "__mips16_", 9) == 0)
+    return 0;
 
-         mips_split_p[SYMBOL_64_LOW] = true;
-         mips_hi_relocs[SYMBOL_64_LOW] = "%hi(";
-         mips_lo_relocs[SYMBOL_64_LOW] = "%lo(";
+  /* This code will only work for o32 and o64 abis.  The other ABI's
+     require more sophisticated support.  */
+  gcc_assert (TARGET_OLDABI);
 
-         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 we're calling via a function pointer, then we must always call
+     via a stub.  There are magic stubs provided in libgcc.a for each
+     of the required cases.  Each of them expects the function address
+     to arrive in register $2.  */
 
-  if (TARGET_MIPS16)
+  if (GET_CODE (fn) != SYMBOL_REF)
     {
-      /* 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(";
-    }
+      char buf[30];
+      tree id;
+      rtx stub_fn, insn;
 
-  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(";
+      /* ??? If this code is modified to support other ABI's, we need
+         to handle PARALLEL return values here.  */
 
-      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(";
-       }
+      if (fpret)
+       sprintf (buf, "__mips16_call_stub_%s_%d",
+                mips16_call_stub_mode_suffix (GET_MODE (retval)),
+                fp_code);
       else
-       {
-         mips_lo_relocs[SYMBOL_GOTOFF_PAGE] = "%got(";
-         mips_lo_relocs[SYMBOL_GOT_PAGE_OFST] = "%lo(";
-       }
+       sprintf (buf, "__mips16_call_stub_%d",
+                fp_code);
 
-      if (TARGET_XGOT)
-       {
-         /* The HIGH and LO_SUM are matched by special .md patterns.  */
-         mips_split_p[SYMBOL_GOT_DISP] = true;
+      id = get_identifier (buf);
+      stub_fn = gen_rtx_SYMBOL_REF (Pmode, IDENTIFIER_POINTER (id));
 
-         mips_split_p[SYMBOL_GOTOFF_DISP] = true;
-         mips_hi_relocs[SYMBOL_GOTOFF_DISP] = "%got_hi(";
-         mips_lo_relocs[SYMBOL_GOTOFF_DISP] = "%got_lo(";
+      mips_emit_move (gen_rtx_REG (Pmode, 2), fn);
 
-         mips_split_p[SYMBOL_GOTOFF_CALL] = true;
-         mips_hi_relocs[SYMBOL_GOTOFF_CALL] = "%call_hi(";
-         mips_lo_relocs[SYMBOL_GOTOFF_CALL] = "%call_lo(";
-       }
+      if (retval == NULL_RTX)
+       insn = gen_call_internal (stub_fn, arg_size);
       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(";
+       insn = gen_call_value_internal (retval, stub_fn, arg_size);
+      insn = emit_call_insn (insn);
 
-  mips_lo_relocs[SYMBOL_HALF] = "%half(";
-}
+      /* Put the register usage information on the CALL.  */
+      CALL_INSN_FUNCTION_USAGE (insn) =
+       gen_rtx_EXPR_LIST (VOIDmode,
+                          gen_rtx_USE (VOIDmode, gen_rtx_REG (Pmode, 2)),
+                          CALL_INSN_FUNCTION_USAGE (insn));
 
-static GTY(()) int was_mips16_p = -1;
+      /* 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 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)
+       CALL_INSN_FUNCTION_USAGE (insn) =
+         gen_rtx_EXPR_LIST (VOIDmode,
+                            gen_rtx_USE (VOIDmode,
+                                         gen_rtx_REG (word_mode, 18)),
+                            CALL_INSN_FUNCTION_USAGE (insn));
 
-/* Set up the target-dependent global state so that it matches the
-   current function's ISA mode.  */
+      /* Return 1 to tell the caller that we've generated the call
+         insn.  */
+      return 1;
+    }
 
-static void
-mips_set_mips16_mode (int mips16_p)
-{
-  if (mips16_p == was_mips16_p)
-    return;
+  /* We know the function we are going to call.  If we have already
+     built a stub, we don't need to do anything further.  */
 
-  /* Restore base settings of various flags.  */
-  target_flags = mips_base_target_flags;
-  align_loops = mips_base_align_loops;
-  align_jumps = mips_base_align_jumps;
-  align_functions = mips_base_align_functions;
-  flag_schedule_insns = mips_base_schedule_insns;
-  flag_reorder_blocks_and_partition = mips_base_reorder_blocks_and_partition;
-  flag_move_loop_invariants = mips_base_move_loop_invariants;
-  flag_delayed_branch = mips_flag_delayed_branch;
+  fnname = targetm.strip_name_encoding (XSTR (fn, 0));
+  for (l = mips16_stubs; l != NULL; l = l->next)
+    if (strcmp (l->name, fnname) == 0)
+      break;
 
-  if (mips16_p)
+  if (l == NULL)
     {
-      /* 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;
+      /* Build a special purpose stub.  When the linker sees a
+        function call in mips16 code, it will check where the target
+        is defined.  If the target is a 32-bit call, the linker will
+        search for the section defined here.  It can tell which
+        symbol this section is associated with by looking at the
+        relocation information (the name is unreliable, since this
+        might be a static function).  If such a section is found, the
+        linker will redirect the call to the start of the magic
+        section.
 
-      /* Don't move loop invariants, because it tends to increase
-        register pressure.  It also introduces an extra move in cases
-        where the constant is the first operand in a two-operand binary
-        instruction, or when it forms a register argument to a functon
-        call.  */
-      flag_move_loop_invariants = 0;
+        If the function does not return a floating point value, the
+        special stub section is named
+            .mips16.call.FNNAME
 
-      /* 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;
+        If the function does return a floating point value, the stub
+        section is named
+            .mips16.call.fp.FNNAME
+        */
 
-      /* Experiments suggest we get the best overall results from using
-        the range of an unextended lw or sw.  Code that makes heavy use
-        of byte or short accesses can do better with ranges of 0...31
-        and 0...63 respectively, but most code is sensitive to the range
-        of lw and sw instead.  */
-      targetm.min_anchor_offset = 0;
-      targetm.max_anchor_offset = 127;
+      secname = (char *) alloca (strlen (fnname) + 40);
+      sprintf (secname, ".mips16.call.%s%s",
+              fpret ? "fp." : "",
+              fnname);
+      stubname = (char *) alloca (strlen (fnname) + 20);
+      sprintf (stubname, "__call_stub_%s%s",
+              fpret ? "fp_" : "",
+              fnname);
+      stubid = get_identifier (stubname);
+      stubdecl = build_decl (FUNCTION_DECL, stubid,
+                            build_function_type (void_type_node, NULL_TREE));
+      DECL_SECTION_NAME (stubdecl) = build_string (strlen (secname), secname);
+      DECL_RESULT (stubdecl) = build_decl (RESULT_DECL, NULL_TREE, void_type_node);
 
-      if (flag_pic || TARGET_ABICALLS)
-       sorry ("MIPS16 PIC");
-    }
-  else
-    {
-      /* Reset to select base non-mips16 ISA.  */
-      target_flags &= ~MASK_MIPS16;
+      fprintf (asm_out_file, "\t# Stub function to call %s%s (",
+              (fpret
+               ? (GET_MODE (retval) == SFmode ? "float " : "double ")
+               : ""),
+              fnname);
+      need_comma = 0;
+      for (f = (unsigned int) fp_code; f != 0; f >>= 2)
+       {
+         fprintf (asm_out_file, "%s%s",
+                  need_comma ? ", " : "",
+                  (f & 3) == 1 ? "float" : "double");
+         need_comma = 1;
+       }
+      fprintf (asm_out_file, ")\n");
 
-      /* When using explicit relocs, we call dbr_schedule from within
-        mips_reorg.  */
-      if (TARGET_EXPLICIT_RELOCS)
-       flag_delayed_branch = 0;
+      fprintf (asm_out_file, "\t.set\tnomips16\n");
+      assemble_start_function (stubdecl, stubname);
 
-      /* Provide default values for align_* for 64-bit targets.  */
-      if (TARGET_64BIT)
+      if (!FUNCTION_NAME_ALREADY_DECLARED)
        {
-         if (align_loops == 0)
-           align_loops = 8;
-         if (align_jumps == 0)
-           align_jumps = 8;
-         if (align_functions == 0)
-           align_functions = 8;
+         fputs ("\t.ent\t", asm_out_file);
+         assemble_name (asm_out_file, stubname);
+         fputs ("\n", asm_out_file);
+
+         assemble_name (asm_out_file, stubname);
+         fputs (":\n", asm_out_file);
        }
 
-      targetm.min_anchor_offset = -32768;
-      targetm.max_anchor_offset = 32767;
-    }
+      /* We build the stub code by hand.  That's the only way we can
+        do it, since we can't generate 32-bit code during a 16-bit
+        compilation.  */
 
-  /* (Re)initialize mips target internals for new ISA.  */
-  mips_init_split_addresses ();
-  mips_init_relocs ();
+      /* We don't want the assembler to insert any nops here.  */
+      fprintf (asm_out_file, "\t.set\tnoreorder\n");
 
-  if (was_mips16_p >= 0)
-    /* Reinitialize target-dependent state.  */
-    target_reinit ();
+      mips16_fp_args (asm_out_file, fp_code, 0);
 
-  was_mips16_p = TARGET_MIPS16;
-}
+      if (! fpret)
+       {
+         fprintf (asm_out_file, "\t.set\tnoat\n");
+         fprintf (asm_out_file, "\tla\t%s,%s\n", reg_names[GP_REG_FIRST + 1],
+                  fnname);
+         fprintf (asm_out_file, "\tjr\t%s\n", reg_names[GP_REG_FIRST + 1]);
+         fprintf (asm_out_file, "\t.set\tat\n");
+         /* Unfortunately, we can't fill the jump delay slot.  We
+            can't fill with one of the mtc1 instructions, because the
+            result is not available for one instruction, so if the
+            very first instruction in the function refers to the
+            register, it will see the wrong value.  */
+         fprintf (asm_out_file, "\tnop\n");
+       }
+      else
+       {
+         fprintf (asm_out_file, "\tmove\t%s,%s\n",
+                  reg_names[GP_REG_FIRST + 18], reg_names[GP_REG_FIRST + 31]);
+         fprintf (asm_out_file, "\tjal\t%s\n", fnname);
+         /* As above, we can't fill the delay slot.  */
+         fprintf (asm_out_file, "\tnop\n");
+         switch (GET_MODE (retval))
+           {
+           case SCmode:
+             fprintf (asm_out_file, "\tmfc1\t%s,%s\n",
+                      reg_names[GP_REG_FIRST + 3],
+                      reg_names[FP_REG_FIRST + MAX_FPRS_PER_FMT]);
+             /* Fall though.  */
+           case SFmode:
+             fprintf (asm_out_file, "\tmfc1\t%s,%s\n",
+                      reg_names[GP_REG_FIRST + 2],
+                      reg_names[FP_REG_FIRST + 0]);
+             if (GET_MODE (retval) == SCmode && TARGET_64BIT)
+               {
+                 /* On 64-bit targets, complex floats are returned in
+                    a single GPR, such that "sd" on a suitably-aligned
+                    target would store the value correctly.  */
+                 fprintf (asm_out_file, "\tdsll\t%s,%s,32\n",
+                          reg_names[GP_REG_FIRST + 2 + TARGET_LITTLE_ENDIAN],
+                          reg_names[GP_REG_FIRST + 2 + TARGET_LITTLE_ENDIAN]);
+                 fprintf (asm_out_file, "\tor\t%s,%s,%s\n",
+                          reg_names[GP_REG_FIRST + 2],
+                          reg_names[GP_REG_FIRST + 2],
+                          reg_names[GP_REG_FIRST + 3]);
+               }
+             break;
 
-/* Use a hash table to keep track of implicit mips16/nomips16 attributes
-   for -mflip_mips16.  It maps decl names onto a boolean mode setting.  */
+           case DCmode:
+             mips16_fpret_double (GP_REG_FIRST + 2 + (8 / UNITS_PER_WORD),
+                                  FP_REG_FIRST + MAX_FPRS_PER_FMT);
+             /* Fall though.  */
+           case DFmode:
+           case V2SFmode:
+             mips16_fpret_double (GP_REG_FIRST + 2, FP_REG_FIRST + 0);
+             break;
 
-struct mflip_mips16_entry GTY (()) {
-  const char *name;
-  bool mips16_p;
-};
-static GTY ((param_is (struct mflip_mips16_entry))) htab_t mflip_mips16_htab;
+           default:
+             gcc_unreachable ();
+           }
+         fprintf (asm_out_file, "\tj\t%s\n", reg_names[GP_REG_FIRST + 18]);
+         /* As above, we can't fill the delay slot.  */
+         fprintf (asm_out_file, "\tnop\n");
+       }
 
-/* Hash table callbacks for mflip_mips16_htab.  */
+      fprintf (asm_out_file, "\t.set\treorder\n");
 
-static hashval_t
-mflip_mips16_htab_hash (const void *entry)
-{
-  return htab_hash_string (((const struct mflip_mips16_entry *) entry)->name);
-}
+#ifdef ASM_DECLARE_FUNCTION_SIZE
+      ASM_DECLARE_FUNCTION_SIZE (asm_out_file, stubname, stubdecl);
+#endif
 
-static int
-mflip_mips16_htab_eq (const void *entry, const void *name)
-{
-  return strcmp (((const struct mflip_mips16_entry *) entry)->name,
-                (const char *) name) == 0;
-}
+      if (!FUNCTION_NAME_ALREADY_DECLARED)
+       {
+         fputs ("\t.end\t", asm_out_file);
+         assemble_name (asm_out_file, stubname);
+         fputs ("\n", asm_out_file);
+       }
 
-/* DECL is a function that needs a default "mips16" or "nomips16" attribute
-   for -mflip-mips16.  Return true if it should use "mips16" and false if
-   it should use "nomips16".  */
+      /* Record this stub.  */
+      l = (struct mips16_stub *) xmalloc (sizeof *l);
+      l->name = xstrdup (fnname);
+      l->fpret = fpret;
+      l->next = mips16_stubs;
+      mips16_stubs = l;
+    }
 
-static bool
-mflip_mips16_use_mips16_p (tree decl)
-{
-  struct mflip_mips16_entry *entry;
-  const char *name;
-  hashval_t hash;
-  void **slot;
+  /* If we expect a floating point return value, but we've built a
+     stub which does not expect one, then we're in trouble.  We can't
+     use the existing stub, because it won't handle the floating point
+     value.  We can't build a new stub, because the linker won't know
+     which stub to use for the various calls in this object file.
+     Fortunately, this case is illegal, since it means that a function
+     was declared in two different ways in a single compilation.  */
+  if (fpret && ! l->fpret)
+    error ("cannot handle inconsistent calls to %qs", fnname);
 
-  /* Use the opposite of the command-line setting for anonymous decls.  */
-  if (!DECL_NAME (decl))
-    return !mips_base_mips16;
+  if (retval == NULL_RTX)
+    insn = gen_call_internal_direct (fn, arg_size);
+  else
+    insn = gen_call_value_internal_direct (retval, fn, arg_size);
+  insn = emit_call_insn (insn);
 
-  if (!mflip_mips16_htab)
-    mflip_mips16_htab = htab_create_ggc (37, mflip_mips16_htab_hash,
-                                        mflip_mips16_htab_eq, NULL);
+  /* If we are calling a stub which handles a floating point return
+     value, we need to arrange to save $18 in the prologue.  We do
+     this by marking the function call as using the register.  The
+     prologue will later see that it is used, and emit code to save
+     it.  */
+  if (l->fpret)
+    CALL_INSN_FUNCTION_USAGE (insn) =
+      gen_rtx_EXPR_LIST (VOIDmode,
+                        gen_rtx_USE (VOIDmode, gen_rtx_REG (word_mode, 18)),
+                        CALL_INSN_FUNCTION_USAGE (insn));
 
-  name = IDENTIFIER_POINTER (DECL_NAME (decl));
-  hash = htab_hash_string (name);
-  slot = htab_find_slot_with_hash (mflip_mips16_htab, name, hash, INSERT);
-  entry = (struct mflip_mips16_entry *) *slot;
-  if (!entry)
-    {
-      mips16_flipper = !mips16_flipper;
-      entry = GGC_NEW (struct mflip_mips16_entry);
-      entry->name = name;
-      entry->mips16_p = mips16_flipper ? !mips_base_mips16 : mips_base_mips16;
-      *slot = entry;
-    }
-  return entry->mips16_p;
+  /* Return 1 to tell the caller that we've generated the call
+     insn.  */
+  return 1;
 }
+\f
+/* Return true if calls to X can use R_MIPS_CALL* relocations.  */
 
-/* Implement TARGET_INSERT_ATTRIBUTES.  */
-
-static void
-mips_insert_attributes (tree decl, tree *attributes)
+static bool
+mips_ok_for_lazy_binding_p (rtx x)
 {
-  const char *name;
-  bool mips16_p, nomips16_p;
+  return (TARGET_USE_GOT
+         && GET_CODE (x) == SYMBOL_REF
+         && !mips_symbol_binds_local_p (x));
+}
 
-  /* Check for "mips16" and "nomips16" attributes.  */
-  mips16_p = lookup_attribute ("mips16", *attributes) != NULL;
-  nomips16_p = lookup_attribute ("nomips16", *attributes) != NULL;
-  if (TREE_CODE (decl) != FUNCTION_DECL)
+/* Load function address ADDR into register DEST.  SIBCALL_P is true
+   if the address is needed for a sibling call.  Return true if we
+   used an explicit lazy-binding sequence.  */
+
+static bool
+mips_load_call_address (rtx dest, rtx addr, int sibcall_p)
+{
+  /* If we're generating PIC, and this call is to a global function,
+     try to allow its address to be resolved lazily.  This isn't
+     possible if TARGET_CALL_SAVED_GP since the value of $gp on entry
+     to the stub would be our caller's gp, not ours.  */
+  if (TARGET_EXPLICIT_RELOCS
+      && !(sibcall_p && TARGET_CALL_SAVED_GP)
+      && mips_ok_for_lazy_binding_p (addr))
     {
-      if (mips16_p)
-       error ("%qs attribute only applies to functions", "mips16");
-      if (nomips16_p)
-       error ("%qs attribute only applies to functions", "nomips16");
+      rtx high, lo_sum_symbol;
+
+      high = mips_unspec_offset_high (dest, pic_offset_table_rtx,
+                                     addr, SYMBOL_GOTOFF_CALL);
+      lo_sum_symbol = mips_unspec_address (addr, SYMBOL_GOTOFF_CALL);
+      if (Pmode == SImode)
+       emit_insn (gen_load_callsi (dest, high, lo_sum_symbol));
+      else
+       emit_insn (gen_load_calldi (dest, high, lo_sum_symbol));
+      return true;
     }
   else
     {
-      mips16_p |= mips_mips16_decl_p (decl);
-      nomips16_p |= mips_nomips16_decl_p (decl);
-      if (mips16_p || nomips16_p)
-       {
-         /* DECL cannot be simultaneously mips16 and nomips16.  */
-         if (mips16_p && nomips16_p)
-           error ("%qs cannot have both %<mips16%> and "
-                  "%<nomips16%> attributes",
-                  IDENTIFIER_POINTER (DECL_NAME (decl)));
-       }
-      else if (TARGET_FLIP_MIPS16 && !DECL_ARTIFICIAL (decl))
-       {
-         /* Implement -mflip-mips16.  If DECL has neither a "nomips16" nor a
-            "mips16" attribute, arbitrarily pick one.  We must pick the same
-            setting for duplicate declarations of a function.  */
-         name = mflip_mips16_use_mips16_p (decl) ? "mips16" : "nomips16";
-         *attributes = tree_cons (get_identifier (name), NULL, *attributes);
-       }
+      mips_emit_move (dest, addr);
+      return false;
     }
 }
 
-/* Implement TARGET_MERGE_DECL_ATTRIBUTES.  */
-
-static tree
-mips_merge_decl_attributes (tree olddecl, tree newdecl)
-{
-  /* The decls' "mips16" and "nomips16" attributes must match exactly.  */
-  if (mips_mips16_decl_p (olddecl) != mips_mips16_decl_p (newdecl))
-    error ("%qs redeclared with conflicting %qs attributes",
-          IDENTIFIER_POINTER (DECL_NAME (newdecl)), "mips16");
-  if (mips_nomips16_decl_p (olddecl) != mips_nomips16_decl_p (newdecl))
-    error ("%qs redeclared with conflicting %qs attributes",
-          IDENTIFIER_POINTER (DECL_NAME (newdecl)), "nomips16");
-
-  return merge_attributes (DECL_ATTRIBUTES (olddecl),
-                          DECL_ATTRIBUTES (newdecl));
-}
 
-/* Implement TARGET_SET_CURRENT_FUNCTION.  Decide whether the current
-   function should use the MIPS16 ISA and switch modes accordingly.  */
+/* Expand a call or call_value instruction.  RESULT is where the
+   result will go (null for calls), ADDR is the address of the
+   function, ARGS_SIZE is the size of the arguments and AUX is
+   the value passed to us by mips_function_arg.  SIBCALL_P is true
+   if we are expanding a sibling call, false if we're expanding
+   a normal call.  */
 
-static void
-mips_set_current_function (tree fndecl)
+void
+mips_expand_call (rtx result, rtx addr, rtx args_size, rtx aux, int sibcall_p)
 {
-  mips_set_mips16_mode (mips_use_mips16_mode_p (fndecl));
-}
-
-/* Implement TARGET_HANDLE_OPTION.  */
+  rtx orig_addr, pattern, insn;
+  bool lazy_p;
 
-static bool
-mips_handle_option (size_t code, const char *arg, int value ATTRIBUTE_UNUSED)
-{
-  switch (code)
+  orig_addr = addr;
+  lazy_p = false;
+  if (!call_insn_operand (addr, VOIDmode))
     {
-    case OPT_mabi_:
-      if (strcmp (arg, "32") == 0)
-       mips_abi = ABI_32;
-      else if (strcmp (arg, "o64") == 0)
-       mips_abi = ABI_O64;
-      else if (strcmp (arg, "n32") == 0)
-       mips_abi = ABI_N32;
-      else if (strcmp (arg, "64") == 0)
-       mips_abi = ABI_64;
-      else if (strcmp (arg, "eabi") == 0)
-       mips_abi = ABI_EABI;
-      else
-       return false;
-      return true;
+      addr = gen_reg_rtx (Pmode);
+      lazy_p = mips_load_call_address (addr, orig_addr, sibcall_p);
+    }
 
-    case OPT_march_:
-    case OPT_mtune_:
-      return mips_parse_cpu (arg) != 0;
+  if (TARGET_MIPS16
+      && TARGET_HARD_FLOAT_ABI
+      && build_mips16_call_stub (result, addr, args_size,
+                                aux == 0 ? 0 : (int) GET_MODE (aux)))
+    return;
 
-    case OPT_mips:
-      mips_isa_info = mips_parse_cpu (ACONCAT (("mips", arg, NULL)));
-      return mips_isa_info != 0;
+  if (result == 0)
+    pattern = (sibcall_p
+              ? gen_sibcall_internal (addr, args_size)
+              : gen_call_internal (addr, args_size));
+  else if (GET_CODE (result) == PARALLEL && XVECLEN (result, 0) == 2)
+    {
+      rtx reg1, reg2;
 
-    case OPT_mno_flush_func:
-      mips_cache_flush_func = NULL;
-      return true;
+      reg1 = XEXP (XVECEXP (result, 0, 0), 0);
+      reg2 = XEXP (XVECEXP (result, 0, 1), 0);
+      pattern =
+       (sibcall_p
+        ? gen_sibcall_value_multiple_internal (reg1, addr, args_size, reg2)
+        : gen_call_value_multiple_internal (reg1, addr, args_size, reg2));
+    }
+  else
+    pattern = (sibcall_p
+              ? gen_sibcall_value_internal (result, addr, args_size)
+              : gen_call_value_internal (result, addr, args_size));
 
-    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;
+  insn = emit_call_insn (pattern);
 
-    default:
-      return true;
+  /* Lazy-binding stubs require $gp to be valid on entry.  We also pretend
+     that they use FAKE_CALL_REGNO; see the load_call<mode> patterns for
+     details.  */
+  if (lazy_p)
+    {
+      use_reg (&CALL_INSN_FUNCTION_USAGE (insn), pic_offset_table_rtx);
+      use_reg (&CALL_INSN_FUNCTION_USAGE (insn),
+              gen_rtx_REG (Pmode, FAKE_CALL_REGNO));
     }
 }
 
-/* Set up the threshold for data to go into the small data area, instead
-   of the normal data area, and detect any conflicts in the switches.  */
 
-void
-override_options (void)
+/* Implement TARGET_FUNCTION_OK_FOR_SIBCALL.  */
+
+static bool
+mips_function_ok_for_sibcall (tree decl, tree exp ATTRIBUTE_UNUSED)
 {
-  int i, start, regno;
-  enum machine_mode mode;
+  if (!TARGET_SIBCALLS)
+    return false;
 
-#ifdef SUBTARGET_OVERRIDE_OPTIONS
-  SUBTARGET_OVERRIDE_OPTIONS;
-#endif
+  /* We can't do a sibcall if the called function is a MIPS16 function
+     because there is no direct "jx" instruction equivalent to "jalx" to
+     switch the ISA mode.  */
+  if (mips_use_mips16_mode_p (decl))
+    return false;
 
-  mips_section_threshold = g_switch_set ? g_switch_value : MIPS_DEFAULT_GVALUE;
+  /* ...and when -minterlink-mips16 is in effect, assume that external
+     functions could be MIPS16 ones unless an attribute explicitly
+     tells us otherwise.  We only care about cases where the sibling
+     and normal calls would both be direct.  */
+  if (TARGET_INTERLINK_MIPS16
+      && decl
+      && DECL_EXTERNAL (decl)
+      && !mips_nomips16_decl_p (decl)
+      && const_call_insn_operand (XEXP (DECL_RTL (decl), 0), VOIDmode))
+    return false;
 
-  /* The following code determines the architecture and register size.
-     Similar code was added to GAS 2.14 (see tc-mips.c:md_after_parse_args()).
-     The GAS and GCC code should be kept in sync as much as possible.  */
+  /* Otherwise OK.  */
+  return true;
+}
+\f
+/* Emit code to move general operand SRC into condition-code
+   register DEST.  SCRATCH is a scratch TFmode float register.
+   The sequence is:
 
-  if (mips_arch_string != 0)
-    mips_set_architecture (mips_parse_cpu (mips_arch_string));
+       FP1 = SRC
+       FP2 = 0.0f
+       DEST = FP2 < FP1
 
-  if (mips_isa_info != 0)
-    {
-      if (mips_arch_info == 0)
-       mips_set_architecture (mips_isa_info);
-      else if (mips_arch_info->isa != mips_isa_info->isa)
-       error ("-%s conflicts with the other architecture options, "
-              "which specify a %s processor",
-              mips_isa_info->name,
-              mips_cpu_info_from_isa (mips_arch_info->isa)->name);
-    }
+   where FP1 and FP2 are single-precision float registers
+   taken from SCRATCH.  */
 
-  if (mips_arch_info == 0)
-    {
-#ifdef MIPS_CPU_STRING_DEFAULT
-      mips_set_architecture (mips_parse_cpu (MIPS_CPU_STRING_DEFAULT));
-#else
-      mips_set_architecture (mips_cpu_info_from_isa (MIPS_ISA_DEFAULT));
-#endif
-    }
+void
+mips_emit_fcc_reload (rtx dest, rtx src, rtx scratch)
+{
+  rtx fp1, fp2;
 
-  if (ABI_NEEDS_64BIT_REGS && !ISA_HAS_64BIT_REGS)
-    error ("-march=%s is not compatible with the selected ABI",
-          mips_arch_info->name);
+  /* Change the source to SFmode.  */
+  if (MEM_P (src))
+    src = adjust_address (src, SFmode, 0);
+  else if (REG_P (src) || GET_CODE (src) == SUBREG)
+    src = gen_rtx_REG (SFmode, true_regnum (src));
 
-  /* Optimize for mips_arch, unless -mtune selects a different processor.  */
-  if (mips_tune_string != 0)
-    mips_set_tune (mips_parse_cpu (mips_tune_string));
+  fp1 = gen_rtx_REG (SFmode, REGNO (scratch));
+  fp2 = gen_rtx_REG (SFmode, REGNO (scratch) + MAX_FPRS_PER_FMT);
 
-  if (mips_tune_info == 0)
-    mips_set_tune (mips_arch_info);
+  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
+/* Emit straight-line code to move LENGTH bytes from SRC to DEST.
+   Assume that the areas do not overlap.  */
 
-  /* Set cost structure for the processor.  */
-  if (optimize_size)
-    mips_cost = &mips_rtx_cost_optimize_size;
+static void
+mips_block_move_straight (rtx dest, rtx src, HOST_WIDE_INT length)
+{
+  HOST_WIDE_INT offset, delta;
+  unsigned HOST_WIDE_INT bits;
+  int i;
+  enum machine_mode mode;
+  rtx *regs;
+
+  /* Work out how many bits to move at a time.  If both operands have
+     half-word alignment, it is usually better to move in half words.
+     For instance, lh/lh/sh/sh is usually better than lwl/lwr/swl/swr
+     and lw/lw/sw/sw is usually better than ldl/ldr/sdl/sdr.
+     Otherwise move word-sized chunks.  */
+  if (MEM_ALIGN (src) == BITS_PER_WORD / 2
+      && MEM_ALIGN (dest) == BITS_PER_WORD / 2)
+    bits = BITS_PER_WORD / 2;
   else
-    mips_cost = &mips_rtx_cost_data[mips_tune];
+    bits = BITS_PER_WORD;
 
-  /* 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;
+  mode = mode_for_size (bits, MODE_INT, 0);
+  delta = bits / BITS_PER_UNIT;
 
-  if ((target_flags_explicit & MASK_64BIT) != 0)
-    {
-      /* The user specified the size of the integer registers.  Make sure
-        it agrees with the ABI and ISA.  */
-      if (TARGET_64BIT && !ISA_HAS_64BIT_REGS)
-       error ("-mgp64 used with a 32-bit processor");
-      else if (!TARGET_64BIT && ABI_NEEDS_64BIT_REGS)
-       error ("-mgp32 used with a 64-bit ABI");
-      else if (TARGET_64BIT && ABI_NEEDS_32BIT_REGS)
-       error ("-mgp64 used with a 32-bit ABI");
-    }
-  else
+  /* Allocate a buffer for the temporary registers.  */
+  regs = alloca (sizeof (rtx) * length / delta);
+
+  /* Load as many BITS-sized chunks as possible.  Use a normal load if
+     the source has enough alignment, otherwise use left/right pairs.  */
+  for (offset = 0, i = 0; offset + delta <= length; offset += delta, i++)
     {
-      /* Infer the integer register size from the ABI and processor.
-        Restrict ourselves to 32-bit registers if that's all the
-        processor has, or if the ABI cannot handle 64-bit registers.  */
-      if (ABI_NEEDS_32BIT_REGS || !ISA_HAS_64BIT_REGS)
-       target_flags &= ~MASK_64BIT;
+      regs[i] = gen_reg_rtx (mode);
+      if (MEM_ALIGN (src) >= bits)
+       mips_emit_move (regs[i], adjust_address (src, mode, offset));
       else
-       target_flags |= MASK_64BIT;
+       {
+         rtx part = adjust_address (src, BLKmode, offset);
+         if (!mips_expand_unaligned_load (regs[i], part, bits, 0))
+           gcc_unreachable ();
+       }
     }
 
-  if ((target_flags_explicit & MASK_FLOAT64) != 0)
+  /* Copy the chunks to the destination.  */
+  for (offset = 0, i = 0; offset + delta <= length; offset += delta, i++)
+    if (MEM_ALIGN (dest) >= bits)
+      mips_emit_move (adjust_address (dest, mode, offset), regs[i]);
+    else
+      {
+       rtx part = adjust_address (dest, BLKmode, offset);
+       if (!mips_expand_unaligned_store (part, regs[i], bits, 0))
+         gcc_unreachable ();
+      }
+
+  /* Mop up any left-over bytes.  */
+  if (offset < length)
     {
-      /* Really, -mfp32 and -mfp64 are ornamental options.  There's
-        only one right answer here.  */
-      if (TARGET_64BIT && TARGET_DOUBLE_FLOAT && !TARGET_FLOAT64)
-       error ("unsupported combination: %s", "-mgp64 -mfp32 -mdouble-float");
-      else if (!TARGET_64BIT && TARGET_FLOAT64
-              && !(ISA_HAS_MXHC1 && mips_abi == ABI_32))
-       error ("-mgp32 and -mfp64 can only be combined if the target"
-              " supports the mfhc1 and mthc1 instructions");
-      else if (TARGET_SINGLE_FLOAT && TARGET_FLOAT64)
-       error ("unsupported combination: %s", "-mfp64 -msingle-float");
-    }
-  else
-    {
-      /* -msingle-float selects 32-bit float registers.  Otherwise the
-        float registers should be the same size as the integer ones.  */
-      if (TARGET_64BIT && TARGET_DOUBLE_FLOAT)
-       target_flags |= MASK_FLOAT64;
-      else
-       target_flags &= ~MASK_FLOAT64;
+      src = adjust_address (src, BLKmode, offset);
+      dest = adjust_address (dest, BLKmode, offset);
+      move_by_pieces (dest, src, length - offset,
+                     MIN (MEM_ALIGN (src), MEM_ALIGN (dest)), 0);
     }
+}
+\f
+#define MAX_MOVE_REGS 4
+#define MAX_MOVE_BYTES (MAX_MOVE_REGS * UNITS_PER_WORD)
 
-  /* End of code shared with GAS.  */
 
-  if ((target_flags_explicit & MASK_LONG64) == 0)
-    {
-      if ((mips_abi == ABI_EABI && TARGET_64BIT) || mips_abi == ABI_64)
-       target_flags |= MASK_LONG64;
-      else
-       target_flags &= ~MASK_LONG64;
-    }
+/* Helper function for doing a loop-based block operation on memory
+   reference MEM.  Each iteration of the loop will operate on LENGTH
+   bytes of MEM.
 
-  if (!TARGET_OLDABI)
-    flag_pcc_struct_return = 0;
+   Create a new base register for use within the loop and point it to
+   the start of MEM.  Create a new memory reference that uses this
+   register.  Store them in *LOOP_REG and *LOOP_MEM respectively.  */
 
-  if ((target_flags_explicit & MASK_BRANCHLIKELY) == 0)
-    {
-      /* If neither -mbranch-likely nor -mno-branch-likely was given
-        on the command line, set MASK_BRANCHLIKELY based on the target
-        architecture and tuning flags.  Annulled delay slots are a
-        size win, so we only consider the processor-specific tuning
-        for !optimize_size.  */
-      if (ISA_HAS_BRANCHLIKELY
-         && (optimize_size
-             || (mips_tune_info->tune_flags & PTF_AVOID_BRANCHLIKELY) == 0))
-       target_flags |= MASK_BRANCHLIKELY;
-      else
-       target_flags &= ~MASK_BRANCHLIKELY;
-    }
-  else if (TARGET_BRANCHLIKELY && !ISA_HAS_BRANCHLIKELY)
-    warning (0, "the %qs architecture does not support branch-likely"
-            " instructions", mips_arch_info->name);
+static void
+mips_adjust_block_mem (rtx mem, HOST_WIDE_INT length,
+                      rtx *loop_reg, rtx *loop_mem)
+{
+  *loop_reg = copy_addr_to_reg (XEXP (mem, 0));
 
-  /* The effect of -mabicalls isn't defined for the EABI.  */
-  if (mips_abi == ABI_EABI && TARGET_ABICALLS)
-    {
-      error ("unsupported combination: %s", "-mabicalls -mabi=eabi");
-      target_flags &= ~MASK_ABICALLS;
-    }
+  /* Although the new mem does not refer to a known location,
+     it does keep up to LENGTH bytes of alignment.  */
+  *loop_mem = change_address (mem, BLKmode, *loop_reg);
+  set_mem_align (*loop_mem, MIN (MEM_ALIGN (mem), length * BITS_PER_UNIT));
+}
 
-  /* MIPS16 cannot generate PIC yet.  */
-  if (TARGET_MIPS16 && (flag_pic || TARGET_ABICALLS))
-    {
-      sorry ("MIPS16 PIC");
-      target_flags &= ~MASK_ABICALLS;
-      flag_pic = flag_pie = flag_shlib = 0;
-    }
 
-  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.)
+/* Move LENGTH bytes from SRC to DEST using a loop that moves MAX_MOVE_BYTES
+   per iteration.  LENGTH must be at least MAX_MOVE_BYTES.  Assume that the
+   memory regions do not overlap.  */
 
-       Also, there is a body of code that uses __PIC__ to distinguish
-       between -mabicalls and -mno-abicalls code.  */
-    flag_pic = 1;
+static void
+mips_block_move_loop (rtx dest, rtx src, HOST_WIDE_INT length)
+{
+  rtx label, src_reg, dest_reg, final_src;
+  HOST_WIDE_INT leftover;
 
-  /* -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
-     above.  */
-  if (optimize > 2 && (target_flags_explicit & MASK_VR4130_ALIGN) == 0)
-    target_flags |= MASK_VR4130_ALIGN;
+  leftover = length % MAX_MOVE_BYTES;
+  length -= leftover;
 
-  /* 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;
+  /* Create registers and memory references for use within the loop.  */
+  mips_adjust_block_mem (src, MAX_MOVE_BYTES, &src_reg, &src);
+  mips_adjust_block_mem (dest, MAX_MOVE_BYTES, &dest_reg, &dest);
 
-  /* 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)
+  /* Calculate the value that SRC_REG should have after the last iteration
+     of the loop.  */
+  final_src = expand_simple_binop (Pmode, PLUS, src_reg, GEN_INT (length),
+                                  0, 0, OPTAB_WIDEN);
+
+  /* Emit the start of the loop.  */
+  label = gen_label_rtx ();
+  emit_label (label);
+
+  /* Emit the loop body.  */
+  mips_block_move_straight (dest, src, MAX_MOVE_BYTES);
+
+  /* Move on to the next block.  */
+  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)
+    emit_insn (gen_cmpdi (src_reg, final_src));
+  else
+    emit_insn (gen_cmpsi (src_reg, final_src));
+  emit_jump_insn (gen_bne (label));
+
+  /* Mop up any left-over bytes.  */
+  if (leftover)
+    mips_block_move_straight (dest, src, leftover);
+}
+
+/* Expand a movmemsi instruction.  */
+
+bool
+mips_expand_block_move (rtx dest, rtx src, rtx length)
+{
+  if (GET_CODE (length) == CONST_INT)
     {
-      if (!TARGET_GPOPT)
+      if (INTVAL (length) <= 2 * MAX_MOVE_BYTES)
        {
-         if (!TARGET_MIPS16 && !TARGET_EXPLICIT_RELOCS)
-           error ("%<-mno-gpopt%> needs %<-mexplicit-relocs%>");
-
-         TARGET_LOCAL_SDATA = false;
-         TARGET_EXTERN_SDATA = false;
+         mips_block_move_straight (dest, src, INTVAL (length));
+         return true;
        }
-      else
+      else if (optimize)
        {
-         if (TARGET_VXWORKS_RTP)
-           warning (0, "cannot use small-data accesses for %qs", "-mrtp");
-
-         if (TARGET_ABICALLS)
-           warning (0, "cannot use small-data accesses for %qs",
-                    "-mabicalls");
+         mips_block_move_loop (dest, src, INTVAL (length));
+         return true;
        }
     }
+  return false;
+}
+\f
 
-#ifdef MIPS_TFMODE_FORMAT
-  REAL_MODE_FORMAT (TFmode) = &MIPS_TFMODE_FORMAT;
-#endif
+/* Expand a loop of synci insns for the address range [BEGIN, END).  */
 
-  /* Make sure that the user didn't turn off paired single support when
-     MIPS-3D support is requested.  */
-  if (TARGET_MIPS3D && (target_flags_explicit & MASK_PAIRED_SINGLE_FLOAT)
-      && !TARGET_PAIRED_SINGLE_FLOAT)
-    error ("-mips3d requires -mpaired-single");
+void
+mips_expand_synci_loop (rtx begin, rtx end)
+{
+  rtx inc, label, cmp, cmp_result;
 
-  /* If TARGET_MIPS3D, enable MASK_PAIRED_SINGLE_FLOAT.  */
-  if (TARGET_MIPS3D)
-    target_flags |= MASK_PAIRED_SINGLE_FLOAT;
+  /* Load INC with the cache line size (rdhwr INC,$1). */
+  inc = gen_reg_rtx (SImode);
+  emit_insn (gen_rdhwr (inc, const1_rtx));
 
-  /* Make sure that when TARGET_PAIRED_SINGLE_FLOAT is true, TARGET_FLOAT64
-     and TARGET_HARD_FLOAT_ABI are both true.  */
-  if (TARGET_PAIRED_SINGLE_FLOAT && !(TARGET_FLOAT64 && TARGET_HARD_FLOAT_ABI))
-    error ("-mips3d/-mpaired-single must be used with -mfp64 -mhard-float");
+  /* Loop back to here.  */
+  label = gen_label_rtx ();
+  emit_label (label);
 
-  /* Make sure that the ISA supports TARGET_PAIRED_SINGLE_FLOAT when it is
-     enabled.  */
-  if (TARGET_PAIRED_SINGLE_FLOAT && !ISA_MIPS64)
-    error ("-mips3d/-mpaired-single must be used with -mips64");
+  emit_insn (gen_synci (begin));
 
-  /* If TARGET_DSPR2, enable MASK_DSP.  */
-  if (TARGET_DSPR2)
-    target_flags |= MASK_DSP;
+  cmp = gen_reg_rtx (Pmode);
+  mips_emit_binary (GTU, cmp, begin, end);
 
-  mips_print_operand_punct['?'] = 1;
-  mips_print_operand_punct['#'] = 1;
-  mips_print_operand_punct['/'] = 1;
-  mips_print_operand_punct['&'] = 1;
-  mips_print_operand_punct['!'] = 1;
-  mips_print_operand_punct['*'] = 1;
-  mips_print_operand_punct['@'] = 1;
-  mips_print_operand_punct['.'] = 1;
-  mips_print_operand_punct['('] = 1;
-  mips_print_operand_punct[')'] = 1;
-  mips_print_operand_punct['['] = 1;
-  mips_print_operand_punct[']'] = 1;
-  mips_print_operand_punct['<'] = 1;
-  mips_print_operand_punct['>'] = 1;
-  mips_print_operand_punct['{'] = 1;
-  mips_print_operand_punct['}'] = 1;
-  mips_print_operand_punct['^'] = 1;
-  mips_print_operand_punct['$'] = 1;
-  mips_print_operand_punct['+'] = 1;
-  mips_print_operand_punct['~'] = 1;
-  mips_print_operand_punct['|'] = 1;
-  mips_print_operand_punct['-'] = 1;
+  mips_emit_binary (PLUS, begin, begin, inc);
 
-  /* Set up array to map GCC register number to debug register number.
-     Ignore the special purpose register numbers.  */
+  cmp_result = gen_rtx_EQ (VOIDmode, cmp, const0_rtx);
+  emit_jump_insn (gen_condjump (cmp_result, label));
+}
+\f
+/* Return true if it is possible to use left/right accesses for a
+   bitfield of WIDTH bits starting BITPOS bits into *OP.  When
+   returning true, update *OP, *LEFT and *RIGHT as follows:
 
-  for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
-    {
-      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;
-    }
+   *OP is a BLKmode reference to the whole field.
 
-  start = GP_DBX_FIRST - GP_REG_FIRST;
-  for (i = GP_REG_FIRST; i <= GP_REG_LAST; i++)
-    mips_dbx_regno[i] = i + start;
+   *LEFT is a QImode reference to the first byte if big endian or
+   the last byte if little endian.  This address can be used in the
+   left-side instructions (lwl, swl, ldl, sdl).
 
-  start = FP_DBX_FIRST - FP_REG_FIRST;
-  for (i = FP_REG_FIRST; i <= FP_REG_LAST; i++)
-    mips_dbx_regno[i] = i + start;
+   *RIGHT is a QImode reference to the opposite end of the field and
+   can be used in the patterning right-side instruction.  */
 
-  /* 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;
-    }
+static bool
+mips_get_unaligned_mem (rtx *op, unsigned int width, int bitpos,
+                       rtx *left, rtx *right)
+{
+  rtx first, last;
 
-  /* Set up array giving whether a given register can hold a given mode.  */
+  /* Check that the operand really is a MEM.  Not all the extv and
+     extzv predicates are checked.  */
+  if (!MEM_P (*op))
+    return false;
 
-  for (mode = VOIDmode;
-       mode != MAX_MACHINE_MODE;
-       mode = (enum machine_mode) ((int)mode + 1))
-    {
-      register int size                     = GET_MODE_SIZE (mode);
-      register enum mode_class class = GET_MODE_CLASS (mode);
+  /* Check that the size is valid.  */
+  if (width != 32 && (!TARGET_64BIT || width != 64))
+    return false;
 
-      for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
-       {
-         register int temp;
+  /* We can only access byte-aligned values.  Since we are always passed
+     a reference to the first byte of the field, it is not necessary to
+     do anything with BITPOS after this check.  */
+  if (bitpos % BITS_PER_UNIT != 0)
+    return false;
 
-         if (mode == CCV2mode)
-           temp = (ISA_HAS_8CC
-                   && ST_REG_P (regno)
-                   && (regno - ST_REG_FIRST) % 2 == 0);
+  /* Reject aligned bitfields: we want to use a normal load or store
+     instead of a left/right pair.  */
+  if (MEM_ALIGN (*op) >= width)
+    return false;
 
-         else if (mode == CCV4mode)
-           temp = (ISA_HAS_8CC
-                   && ST_REG_P (regno)
-                   && (regno - ST_REG_FIRST) % 4 == 0);
+  /* Adjust *OP to refer to the whole field.  This also has the effect
+     of legitimizing *OP's address for BLKmode, possibly simplifying it.  */
+  *op = adjust_address (*op, BLKmode, 0);
+  set_mem_size (*op, GEN_INT (width / BITS_PER_UNIT));
 
-         else if (mode == CCmode)
-           {
-             if (! ISA_HAS_8CC)
-               temp = (regno == FPSW_REGNUM);
-             else
-               temp = (ST_REG_P (regno) || GP_REG_P (regno)
-                       || FP_REG_P (regno));
-           }
+  /* Get references to both ends of the field.  We deliberately don't
+     use the original QImode *OP for FIRST since the new BLKmode one
+     might have a simpler address.  */
+  first = adjust_address (*op, QImode, 0);
+  last = adjust_address (*op, QImode, width / BITS_PER_UNIT - 1);
 
-         else if (GP_REG_P (regno))
-           temp = ((regno & 1) == 0 || size <= UNITS_PER_WORD);
+  /* Allocate to LEFT and RIGHT according to endianness.  LEFT should
+     be the upper word and RIGHT the lower word.  */
+  if (TARGET_BIG_ENDIAN)
+    *left = first, *right = last;
+  else
+    *left = last, *right = first;
 
-         else if (FP_REG_P (regno))
-           temp = ((((regno % MAX_FPRS_PER_FMT) == 0)
-                    || (MIN_FPRS_PER_FMT == 1
-                        && size <= UNITS_PER_FPREG))
-                   && (((class == MODE_FLOAT || class == MODE_COMPLEX_FLOAT
-                         || class == MODE_VECTOR_FLOAT)
-                        && size <= UNITS_PER_FPVALUE)
-                       /* Allow integer modes that fit into a single
-                          register.  We need to put integers into FPRs
-                          when using instructions like cvt and trunc.
-                          We can't allow sizes smaller than a word,
-                          the FPU has no appropriate load/store
-                          instructions for those.  */
-                       || (class == MODE_INT
-                           && size >= MIN_UNITS_PER_WORD
-                           && size <= UNITS_PER_FPREG)
-                       /* Allow TFmode for CCmode reloads.  */
-                       || (ISA_HAS_8CC && mode == TFmode)));
+  return true;
+}
 
-          else if (ACC_REG_P (regno))
-           temp = ((INTEGRAL_MODE_P (mode) || ALL_FIXED_POINT_MODE_P (mode))
-                   && size <= UNITS_PER_WORD * 2
-                   && (size <= 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);
-         else
-           temp = 0;
+/* Try to emit the equivalent of (set DEST (zero_extract SRC WIDTH BITPOS)).
+   Return true on success.  We only handle cases where zero_extract is
+   equivalent to sign_extract.  */
 
-         mips_hard_regno_mode_ok[(int)mode][regno] = temp;
-       }
+bool
+mips_expand_unaligned_load (rtx dest, rtx src, unsigned int width, int bitpos)
+{
+  rtx left, right, temp;
+
+  /* If TARGET_64BIT, the destination of a 32-bit load will be a
+     paradoxical word_mode subreg.  This is the only case in which
+     we allow the destination to be larger than the source.  */
+  if (GET_CODE (dest) == SUBREG
+      && GET_MODE (dest) == DImode
+      && SUBREG_BYTE (dest) == 0
+      && GET_MODE (SUBREG_REG (dest)) == SImode)
+    dest = SUBREG_REG (dest);
+
+  /* After the above adjustment, the destination must be the same
+     width as the source.  */
+  if (GET_MODE_BITSIZE (GET_MODE (dest)) != width)
+    return false;
+
+  if (!mips_get_unaligned_mem (&src, width, bitpos, &left, &right))
+    return false;
+
+  temp = gen_reg_rtx (GET_MODE (dest));
+  if (GET_MODE (dest) == DImode)
+    {
+      emit_insn (gen_mov_ldl (temp, src, left));
+      emit_insn (gen_mov_ldr (dest, copy_rtx (src), right, temp));
+    }
+  else
+    {
+      emit_insn (gen_mov_lwl (temp, src, left));
+      emit_insn (gen_mov_lwr (dest, copy_rtx (src), right, temp));
     }
+  return true;
+}
 
-  /* Save GPR registers in word_mode sized hunks.  word_mode hasn't been
-     initialized yet, so we can't use that here.  */
-  gpr_mode = TARGET_64BIT ? DImode : SImode;
 
-  /* Function to allocate machine-dependent function status.  */
-  init_machine_status = &mips_init_machine_status;
+/* Try to expand (set (zero_extract DEST WIDTH BITPOS) SRC).  Return
+   true on success.  */
 
-  /* Default to working around R4000 errata only if the processor
-     was selected explicitly.  */
-  if ((target_flags_explicit & MASK_FIX_R4000) == 0
-      && mips_matching_cpu_name_p (mips_arch_info->name, "r4000"))
-    target_flags |= MASK_FIX_R4000;
+bool
+mips_expand_unaligned_store (rtx dest, rtx src, unsigned int width, int bitpos)
+{
+  rtx left, right;
+  enum machine_mode mode;
 
-  /* Default to working around R4400 errata only if the processor
-     was selected explicitly.  */
-  if ((target_flags_explicit & MASK_FIX_R4400) == 0
-      && mips_matching_cpu_name_p (mips_arch_info->name, "r4400"))
-    target_flags |= MASK_FIX_R4400;
+  if (!mips_get_unaligned_mem (&dest, width, bitpos, &left, &right))
+    return false;
 
-  /* Save base state of options.  */
-  mips_base_mips16 = TARGET_MIPS16;
-  mips_base_target_flags = target_flags;
-  mips_base_schedule_insns = flag_schedule_insns;
-  mips_base_reorder_blocks_and_partition = flag_reorder_blocks_and_partition;
-  mips_base_move_loop_invariants = flag_move_loop_invariants;
-  mips_base_align_loops = align_loops;
-  mips_base_align_jumps = align_jumps;
-  mips_base_align_functions = align_functions;
-  mips_flag_delayed_branch = flag_delayed_branch;
+  mode = mode_for_size (width, MODE_INT, 0);
+  src = gen_lowpart (mode, src);
 
-  /* Now select the mips16 or 32-bit instruction set, as requested.  */
-  mips_set_mips16_mode (mips_base_mips16);
+  if (mode == DImode)
+    {
+      emit_insn (gen_mov_sdl (dest, src, left));
+      emit_insn (gen_mov_sdr (copy_rtx (dest), copy_rtx (src), right));
+    }
+  else
+    {
+      emit_insn (gen_mov_swl (dest, src, left));
+      emit_insn (gen_mov_swr (copy_rtx (dest), copy_rtx (src), right));
+    }
+  return true;
 }
 
-/* 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.  */
+/* Return true if X is a MEM with the same size as MODE.  */
 
-static void
-mips_swap_registers (unsigned int i)
+bool
+mips_mem_fits_mode_p (enum machine_mode mode, rtx x)
 {
-  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)
+  rtx size;
 
-  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]);
+  if (!MEM_P (x))
+    return false;
 
-#undef SWAP_STRING
-#undef SWAP_INT
+  size = MEM_SIZE (x);
+  return size && INTVAL (size) == GET_MODE_SIZE (mode);
 }
 
-/* Implement CONDITIONAL_REGISTER_USAGE.  */
+/* Return true if (zero_extract OP SIZE POSITION) can be used as the
+   source of an "ext" instruction or the destination of an "ins"
+   instruction.  OP must be a register operand and the following
+   conditions must hold:
 
-void
-mips_conditional_register_usage (void)
+     0 <= POSITION < GET_MODE_BITSIZE (GET_MODE (op))
+     0 < SIZE <= GET_MODE_BITSIZE (GET_MODE (op))
+     0 < POSITION + SIZE <= GET_MODE_BITSIZE (GET_MODE (op))
+
+   Also reject lengths equal to a word as they are better handled
+   by the move patterns.  */
+
+bool
+mips_use_ins_ext_p (rtx op, rtx size, rtx position)
 {
-  if (!ISA_HAS_DSP)
-    {
-      int regno;
+  HOST_WIDE_INT len, pos;
 
-      for (regno = DSP_ACC_REG_FIRST; regno <= DSP_ACC_REG_LAST; regno++)
-       fixed_regs[regno] = call_used_regs[regno] = 1;
-    }
-  if (!TARGET_HARD_FLOAT)
-    {
-      int regno;
+  if (!ISA_HAS_EXT_INS
+      || !register_operand (op, VOIDmode)
+      || GET_MODE_BITSIZE (GET_MODE (op)) > BITS_PER_WORD)
+    return false;
 
-      for (regno = FP_REG_FIRST; regno <= FP_REG_LAST; regno++)
-       fixed_regs[regno] = call_used_regs[regno] = 1;
-      for (regno = ST_REG_FIRST; regno <= ST_REG_LAST; regno++)
-       fixed_regs[regno] = call_used_regs[regno] = 1;
-    }
-  else if (! ISA_HAS_8CC)
-    {
-      int regno;
+  len = INTVAL (size);
+  pos = INTVAL (position);
 
-      /* We only have a single condition code register.  We
-        implement this by hiding all the condition code registers,
-        and generating RTL that refers directly to ST_REG_FIRST.  */
-      for (regno = ST_REG_FIRST; regno <= ST_REG_LAST; regno++)
-       fixed_regs[regno] = call_used_regs[regno] = 1;
-    }
-  /* In mips16 mode, we permit the $t temporary registers to be used
-     for reload.  We prohibit the unused $s registers, since they
-     are caller saved, and saving them via a mips16 register would
-     probably waste more time than just reloading the value.  */
-  if (TARGET_MIPS16)
-    {
-      fixed_regs[18] = call_used_regs[18] = 1;
-      fixed_regs[19] = call_used_regs[19] = 1;
-      fixed_regs[20] = call_used_regs[20] = 1;
-      fixed_regs[21] = call_used_regs[21] = 1;
-      fixed_regs[22] = call_used_regs[22] = 1;
-      fixed_regs[23] = call_used_regs[23] = 1;
-      fixed_regs[26] = call_used_regs[26] = 1;
-      fixed_regs[27] = call_used_regs[27] = 1;
-      fixed_regs[30] = call_used_regs[30] = 1;
-    }
-  /* fp20-23 are now caller saved.  */
-  if (mips_abi == ABI_64)
-    {
-      int regno;
-      for (regno = FP_REG_FIRST + 20; regno < FP_REG_FIRST + 24; regno++)
-       call_really_used_regs[regno] = call_used_regs[regno] = 1;
-    }
-  /* Odd registers from fp21 to fp31 are now caller saved.  */
-  if (mips_abi == ABI_N32)
-    {
-      int regno;
-      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);
-    }
+  if (len <= 0 || len >= GET_MODE_BITSIZE (GET_MODE (op))
+      || pos < 0 || pos + len > GET_MODE_BITSIZE (GET_MODE (op)))
+    return false;
+
+  return true;
 }
+\f
+/* Initialize mips_split_addresses from the associated command-line
+   settings.
 
-/* Allocate a chunk of memory for per-function machine-dependent data.  */
-static struct machine_function *
-mips_init_machine_status (void)
+   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)
 {
-  return ((struct machine_function *)
-         ggc_alloc_cleared (sizeof (struct machine_function)));
+  if (!TARGET_MIPS16 && TARGET_SPLIT_ADDRESSES
+      && optimize && !flag_pic
+      && !ABI_HAS_64BIT_SYMBOLS)
+    mips_split_addresses = 1;
+  else
+    mips_split_addresses = 0;
 }
 
-/* On the mips16, we want to allocate $24 (T_REG) before other
-   registers for instructions for which it is possible.  This helps
-   avoid shuffling registers around in order to set up for an xor,
-   encouraging the compiler to use a cmp instead.  */
+/* (Re-)Initialize information about relocs.  */
 
-void
-mips_order_regs_for_local_alloc (void)
+static void
+mips_init_relocs (void)
 {
-  register int i;
+  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));
 
-  for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
-    reg_alloc_order[i] = i;
+  if (ABI_HAS_64BIT_SYMBOLS)
+    {
+      if (TARGET_EXPLICIT_RELOCS)
+       {
+         mips_split_p[SYMBOL_64_HIGH] = true;
+         mips_hi_relocs[SYMBOL_64_HIGH] = "%highest(";
+         mips_lo_relocs[SYMBOL_64_HIGH] = "%higher(";
+
+         mips_split_p[SYMBOL_64_MID] = true;
+         mips_hi_relocs[SYMBOL_64_MID] = "%higher(";
+         mips_lo_relocs[SYMBOL_64_MID] = "%hi(";
+
+         mips_split_p[SYMBOL_64_LOW] = true;
+         mips_hi_relocs[SYMBOL_64_LOW] = "%hi(";
+         mips_lo_relocs[SYMBOL_64_LOW] = "%lo(";
+
+         mips_split_p[SYMBOL_ABSOLUTE] = true;
+         mips_lo_relocs[SYMBOL_ABSOLUTE] = "%lo(";
+       }
+    }
+  else
+    {
+      if (TARGET_EXPLICIT_RELOCS || mips_split_addresses || TARGET_MIPS16)
+       {
+         mips_split_p[SYMBOL_ABSOLUTE] = true;
+         mips_hi_relocs[SYMBOL_ABSOLUTE] = "%hi(";
+         mips_lo_relocs[SYMBOL_ABSOLUTE] = "%lo(";
+
+         mips_lo_relocs[SYMBOL_32_HIGH] = "%hi(";
+       }
+    }
 
   if (TARGET_MIPS16)
     {
-      /* It really doesn't matter where we put register 0, since it is
-         a fixed register anyhow.  */
-      reg_alloc_order[0] = 24;
-      reg_alloc_order[24] = 0;
+      /* 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(";
     }
-}
 
-\f
-/* The MIPS debug format wants all automatic variables and arguments
-   to be in terms of the virtual frame pointer (stack pointer before
-   any adjustment in the function), while the MIPS 3.0 linker wants
-   the frame pointer to be the stack pointer after the initial
-   adjustment.  So, we do the adjustment here.  The arg pointer (which
-   is eliminated) points to the virtual frame pointer, while the frame
-   pointer (which may be eliminated) points to the stack pointer after
-   the initial adjustments.  */
+  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(";
 
-HOST_WIDE_INT
-mips_debugger_offset (rtx addr, HOST_WIDE_INT offset)
-{
-  rtx offset2 = const0_rtx;
-  rtx reg = eliminate_constant_term (addr, &offset2);
+      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 (offset == 0)
-    offset = INTVAL (offset2);
+      if (TARGET_XGOT)
+       {
+         /* The HIGH and LO_SUM are matched by special .md patterns.  */
+         mips_split_p[SYMBOL_GOT_DISP] = true;
 
-  if (reg == stack_pointer_rtx || reg == frame_pointer_rtx
-      || reg == hard_frame_pointer_rtx)
-    {
-      HOST_WIDE_INT frame_size = (!cfun->machine->frame.initialized)
-                                 ? compute_frame_size (get_frame_size ())
-                                 : cfun->machine->frame.total_size;
+         mips_split_p[SYMBOL_GOTOFF_DISP] = true;
+         mips_hi_relocs[SYMBOL_GOTOFF_DISP] = "%got_hi(";
+         mips_lo_relocs[SYMBOL_GOTOFF_DISP] = "%got_lo(";
 
-      /* MIPS16 frame is smaller */
-      if (frame_pointer_needed && TARGET_MIPS16)
-       frame_size -= cfun->machine->frame.args_size;
+         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(";
+       }
+    }
 
-      offset = offset - frame_size;
+  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(";
     }
 
-  /* sdbout_parms does not want this to crash for unrecognized cases.  */
-#if 0
-  else if (reg != arg_pointer_rtx)
-    fatal_insn ("mips_debugger_offset called with non stack/frame/arg pointer",
-               addr);
-#endif
+  /* 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(";
 
-  return offset;
+  mips_lo_relocs[SYMBOL_HALF] = "%half(";
 }
-\f
+
 /* If OP is an UNSPEC address, return the address to which it refers,
    otherwise return OP itself.  */
 
@@ -6470,6 +6266,27 @@ mips_strip_unspec_address (rtx op)
   return op;
 }
 
+/* Print symbolic operand OP, which is part of a HIGH or LO_SUM
+   in context CONTEXT.  RELOCS is the array of relocations to use.  */
+
+static void
+print_operand_reloc (FILE *file, rtx op, enum mips_symbol_context context,
+                    const char **relocs)
+{
+  enum mips_symbol_type symbol_type;
+  const char *p;
+
+  symbol_type = mips_classify_symbolic_expression (op, context);
+  if (relocs[symbol_type] == 0)
+    fatal_insn ("PRINT_OPERAND, invalid operand for relocation", op);
+
+  fputs (relocs[symbol_type], file);
+  output_addr_const (file, mips_strip_unspec_address (op));
+  for (p = relocs[symbol_type]; *p != 0; p++)
+    if (*p == '(')
+      fputc (')', file);
+}
+
 /* Implement the PRINT_OPERAND macro.  The MIPS-specific operand codes are:
 
    'X'  OP is CONST_INT, prints 32 bits in hexadecimal format = "0x%08x",
@@ -6821,28 +6638,6 @@ print_operand (FILE *file, rtx op, int letter)
   else
     output_addr_const (file, mips_strip_unspec_address (op));
 }
-
-
-/* Print symbolic operand OP, which is part of a HIGH or LO_SUM
-   in context CONTEXT.  RELOCS is the array of relocations to use.  */
-
-static void
-print_operand_reloc (FILE *file, rtx op, enum mips_symbol_context context,
-                    const char **relocs)
-{
-  enum mips_symbol_type symbol_type;
-  const char *p;
-
-  symbol_type = mips_classify_symbolic_expression (op, context);
-  if (relocs[symbol_type] == 0)
-    fatal_insn ("PRINT_OPERAND, invalid operand for relocation", op);
-
-  fputs (relocs[symbol_type], file);
-  output_addr_const (file, mips_strip_unspec_address (op));
-  for (p = relocs[symbol_type]; *p != 0; p++)
-    if (*p == '(')
-      fputc (')', file);
-}
 \f
 /* Output address operand X to FILE.  */
 
@@ -6877,9 +6672,198 @@ print_operand_address (FILE *file, rtx x)
   gcc_unreachable ();
 }
 \f
-/* When using assembler macros, keep track of all of small-data externs
-   so that mips_file_end can emit the appropriate declarations for them.
-
+/* 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.  */
+
+static void
+mips_encode_section_info (tree decl, rtx rtl, int first)
+{
+  default_encode_section_info (decl, rtl, 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 (type))
+         || mips_far_type_p (type))
+       SYMBOL_REF_FLAGS (symbol) |= SYMBOL_FLAG_LONG_CALL;
+    }
+}
+
+/* Implement TARGET_SELECT_RTX_SECTION.  */
+
+static section *
+mips_select_rtx_section (enum machine_mode mode, rtx x,
+                        unsigned HOST_WIDE_INT align)
+{
+  /* ??? Consider using mergeable small data sections.  */
+  if (mips_rtx_constant_in_small_data_p (mode))
+    return get_named_section (NULL, ".sdata", 0);
+
+  return default_elf_select_rtx_section (mode, x, align);
+}
+
+/* Implement TARGET_ASM_FUNCTION_RODATA_SECTION.
+
+   The complication here is that, with the combination TARGET_ABICALLS
+   && !TARGET_GPWORD, jump tables will use absolute addresses, and should
+   therefore not be included in the read-only part of a DSO.  Handle such
+   cases by selecting a normal data section instead of a read-only one.
+   The logic apes that in default_function_rodata_section.  */
+
+static section *
+mips_function_rodata_section (tree decl)
+{
+  if (!TARGET_ABICALLS || TARGET_GPWORD)
+    return default_function_rodata_section (decl);
+
+  if (decl && DECL_SECTION_NAME (decl))
+    {
+      const char *name = TREE_STRING_POINTER (DECL_SECTION_NAME (decl));
+      if (DECL_ONE_ONLY (decl) && strncmp (name, ".gnu.linkonce.t.", 16) == 0)
+       {
+         char *rname = ASTRDUP (name);
+         rname[14] = 'd';
+         return get_section (rname, SECTION_LINKONCE | SECTION_WRITE, decl);
+       }
+      else if (flag_function_sections && flag_data_sections
+              && strncmp (name, ".text.", 6) == 0)
+       {
+         char *rname = ASTRDUP (name);
+         memcpy (rname + 1, "data", 4);
+         return get_section (rname, SECTION_WRITE, decl);
+       }
+    }
+  return data_section;
+}
+
+/* Implement TARGET_IN_SMALL_DATA_P.  This function controls whether
+   locally-defined objects go in a small data section.  It also controls
+   the setting of the SYMBOL_REF_SMALL_P flag, which in turn helps
+   mips_classify_symbol decide when to use %gp_rel(...)($gp) accesses.  */
+
+static bool
+mips_in_small_data_p (const_tree decl)
+{
+  HOST_WIDE_INT size;
+
+  if (TREE_CODE (decl) == STRING_CST || TREE_CODE (decl) == FUNCTION_DECL)
+    return false;
+
+  /* We don't yet generate small-data references for -mabicalls or
+     VxWorks RTP code.  See the related -G handling in override_options.  */
+  if (TARGET_ABICALLS || TARGET_VXWORKS_RTP)
+    return false;
+
+  if (TREE_CODE (decl) == VAR_DECL && DECL_SECTION_NAME (decl) != 0)
+    {
+      const char *name;
+
+      /* Reject anything that isn't in a known small-data section.  */
+      name = TREE_STRING_POINTER (DECL_SECTION_NAME (decl));
+      if (strcmp (name, ".sdata") != 0 && strcmp (name, ".sbss") != 0)
+       return false;
+
+      /* If a symbol is defined externally, the assembler will use the
+        usual -G rules when deciding how to implement macros.  */
+      if (mips_lo_relocs[SYMBOL_GP_RELATIVE] || !DECL_EXTERNAL (decl))
+       return true;
+    }
+  else if (TARGET_EMBEDDED_DATA)
+    {
+      /* Don't put constants into the small data section: we want them
+        to be in ROM rather than RAM.  */
+      if (TREE_CODE (decl) != VAR_DECL)
+       return false;
+
+      if (TREE_READONLY (decl)
+         && !TREE_SIDE_EFFECTS (decl)
+         && (!DECL_INITIAL (decl) || TREE_CONSTANT (DECL_INITIAL (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);
+}
+
+/* Implement TARGET_USE_ANCHORS_FOR_SYMBOL_P.  We don't want to use
+   anchors for small data: the GP register acts as an anchor in that
+   case.  We also don't want to use them for PC-relative accesses,
+   where the PC acts as an anchor.  */
+
+static bool
+mips_use_anchors_for_symbol_p (const_rtx symbol)
+{
+  switch (mips_classify_symbol (symbol, SYMBOL_CONTEXT_MEM))
+    {
+    case SYMBOL_PC_RELATIVE:
+    case SYMBOL_GP_RELATIVE:
+      return false;
+
+    default:
+      return default_use_anchors_for_symbol_p (symbol);
+    }
+}
+\f
+/* The MIPS debug format wants all automatic variables and arguments
+   to be in terms of the virtual frame pointer (stack pointer before
+   any adjustment in the function), while the MIPS 3.0 linker wants
+   the frame pointer to be the stack pointer after the initial
+   adjustment.  So, we do the adjustment here.  The arg pointer (which
+   is eliminated) points to the virtual frame pointer, while the frame
+   pointer (which may be eliminated) points to the stack pointer after
+   the initial adjustments.  */
+
+HOST_WIDE_INT
+mips_debugger_offset (rtx addr, HOST_WIDE_INT offset)
+{
+  rtx offset2 = const0_rtx;
+  rtx reg = eliminate_constant_term (addr, &offset2);
+
+  if (offset == 0)
+    offset = INTVAL (offset2);
+
+  if (reg == stack_pointer_rtx || reg == frame_pointer_rtx
+      || reg == hard_frame_pointer_rtx)
+    {
+      HOST_WIDE_INT frame_size = (!cfun->machine->frame.initialized)
+                                 ? compute_frame_size (get_frame_size ())
+                                 : cfun->machine->frame.total_size;
+
+      /* MIPS16 frame is smaller */
+      if (frame_pointer_needed && TARGET_MIPS16)
+       frame_size -= cfun->machine->frame.args_size;
+
+      offset = offset - frame_size;
+    }
+
+  /* sdbout_parms does not want this to crash for unrecognized cases.  */
+#if 0
+  else if (reg != arg_pointer_rtx)
+    fatal_insn ("mips_debugger_offset called with non stack/frame/arg pointer",
+               addr);
+#endif
+
+  return offset;
+}
+\f
+/* When using assembler macros, keep track of all of small-data externs
+   so that mips_file_end can emit the appropriate declarations for them.
+
    In most cases it would be safe (though pointless) to emit .externs
    for other symbols too.  One exception is when an object is within
    the -G limit but declared by the user to be in a section other
@@ -6954,6 +6938,54 @@ mips_output_filename (FILE *stream, const char *name)
       putc ('\n', stream);
     }
 }
+
+/* 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);
+}
+
+/* Implement TARGET_DWARF_REGISTER_SPAN.  */
+
+static rtx
+mips_dwarf_register_span (rtx reg)
+{
+  rtx high, low;
+  enum machine_mode mode;
+
+  /* By default, GCC maps increasing register numbers to increasing
+     memory locations, but paired FPRs are always little-endian,
+     regardless of the prevailing endianness.  */
+  mode = GET_MODE (reg);
+  if (FP_REG_P (REGNO (reg))
+      && TARGET_BIG_ENDIAN
+      && MAX_FPRS_PER_FMT > 1
+      && GET_MODE_SIZE (mode) > UNITS_PER_FPREG)
+    {
+      gcc_assert (GET_MODE_SIZE (mode) == UNITS_PER_HWFPVALUE);
+      high = mips_subword (reg, true);
+      low = mips_subword (reg, false);
+      return gen_rtx_PARALLEL (VOIDmode, gen_rtvec (2, high, low));
+    }
+
+  return NULL_RTX;
+}
 \f
 /* Output an ASCII string, in a space-saving way.  PREFIX is the string
    that should be written before the opening quote, such as "\t.ascii\t"
@@ -6997,75 +7029,16 @@ mips_output_ascii (FILE *stream, const char *string_param, size_t len,
     }
   fprintf (stream, "\"\n");
 }
-\f
-/* Implement TARGET_ASM_FILE_START.  */
 
-static void
-mips_file_start (void)
+#ifdef BSS_SECTION_ASM_OP
+/* Implement ASM_OUTPUT_ALIGNED_BSS.  This differs from the default only
+   in the use of sbss.  */
+
+void
+mips_output_aligned_bss (FILE *stream, tree decl, const char *name,
+                        unsigned HOST_WIDE_INT size, int align)
 {
-  default_file_start ();
-
-  if (!TARGET_IRIX)
-    {
-      /* Generate a special section to describe the ABI switches used to
-        produce the resultant binary.  This used to be done by the assembler
-        setting bits in the ELF header's flags field, but we have run out of
-        bits.  GDB needs this information in order to be able to correctly
-        debug these binaries.  See the function mips_gdbarch_init() in
-        gdb/mips-tdep.c.  This is unnecessary for the IRIX 5/6 ABIs and
-        causes unnecessary IRIX 6 ld warnings.  */
-      const char * abi_string = NULL;
-
-      switch (mips_abi)
-       {
-       case ABI_32:   abi_string = "abi32"; break;
-       case ABI_N32:  abi_string = "abiN32"; break;
-       case ABI_64:   abi_string = "abi64"; break;
-       case ABI_O64:  abi_string = "abiO64"; break;
-       case ABI_EABI: abi_string = TARGET_64BIT ? "eabi64" : "eabi32"; break;
-       default:
-         gcc_unreachable ();
-       }
-      /* Note - we use fprintf directly rather than calling switch_to_section
-        because in this way we can avoid creating an allocated section.  We
-        do not want this section to take up any space in the running
-        executable.  */
-      fprintf (asm_out_file, "\t.section .mdebug.%s\n\t.previous\n",
-              abi_string);
-
-      /* There is no ELF header flag to distinguish long32 forms of the
-        EABI from long64 forms.  Emit a special section to help tools
-        such as GDB.  Do the same for o64, which is sometimes used with
-        -mlong64.  */
-      if (mips_abi == ABI_EABI || mips_abi == ABI_O64)
-       fprintf (asm_out_file, "\t.section .gcc_compiled_long%d\n"
-                "\t.previous\n", TARGET_LONG64 ? 64 : 32);
-
-#ifdef HAVE_AS_GNU_ATTRIBUTE
-      fprintf (asm_out_file, "\t.gnu_attribute 4, %d\n",
-              TARGET_HARD_FLOAT_ABI ? (TARGET_DOUBLE_FLOAT ? 1 : 2) : 3);
-#endif
-    }
-
-  /* Generate the pseudo ops that System V.4 wants.  */
-  if (TARGET_ABICALLS)
-    fprintf (asm_out_file, "\t.abicalls\n");
-
-  if (flag_verbose_asm)
-    fprintf (asm_out_file, "\n%s -G value = %d, Arch = %s, ISA = %d\n",
-            ASM_COMMENT_START,
-            mips_section_threshold, mips_arch_info->name, mips_isa);
-}
-
-#ifdef BSS_SECTION_ASM_OP
-/* Implement ASM_OUTPUT_ALIGNED_BSS.  This differs from the default only
-   in the use of sbss.  */
-
-void
-mips_output_aligned_bss (FILE *stream, tree decl, const char *name,
-                        unsigned HOST_WIDE_INT size, int align)
-{
-  extern tree last_assemble_variable_decl;
+  extern tree last_assemble_variable_decl;
 
   if (mips_in_small_data_p (decl))
     switch_to_section (get_named_section (NULL, ".sbss", 0));
@@ -7078,32 +7051,30 @@ mips_output_aligned_bss (FILE *stream, tree decl, const char *name,
 }
 #endif
 \f
-/* Implement ASM_OUTPUT_ALIGNED_DECL_COMMON.  This is usually the same as the
-   elfos.h version, but we also need to handle -muninit-const-in-rodata.  */
+/* Emit either a label, .comm, or .lcomm directive.  When using assembler
+   macros, mark the symbol as written so that mips_file_end won't emit an
+   .extern for it.  STREAM is the output file, NAME is the name of the
+   symbol, INIT_STRING is the string that should be written before the
+   symbol and FINAL_STRING is the string that should be written after it.
+   FINAL_STRING is a printf() format that consumes the remaining arguments.  */
 
 void
-mips_output_aligned_decl_common (FILE *stream, tree decl, const char *name,
-                                unsigned HOST_WIDE_INT size,
-                                unsigned int align)
+mips_declare_object (FILE *stream, const char *name, const char *init_string,
+                    const char *final_string, ...)
 {
-  /* If the target wants uninitialized const declarations in
-     .rdata then don't put them in .comm.  */
-  if (TARGET_EMBEDDED_DATA && TARGET_UNINIT_CONST_IN_RODATA
-      && TREE_CODE (decl) == VAR_DECL && TREE_READONLY (decl)
-      && (DECL_INITIAL (decl) == 0 || DECL_INITIAL (decl) == error_mark_node))
-    {
-      if (TREE_PUBLIC (decl) && DECL_NAME (decl))
-       targetm.asm_out.globalize_label (stream, name);
+  va_list ap;
 
-      switch_to_section (readonly_data_section);
-      ASM_OUTPUT_ALIGN (stream, floor_log2 (align / BITS_PER_UNIT));
-      mips_declare_object (stream, name, "",
-                          ":\n\t.space\t" HOST_WIDE_INT_PRINT_UNSIGNED "\n",
-                          size);
+  fputs (init_string, stream);
+  assemble_name (stream, name);
+  va_start (ap, final_string);
+  vfprintf (stream, final_string, ap);
+  va_end (ap);
+
+  if (!TARGET_EXPLICIT_RELOCS)
+    {
+      tree name_tree = get_identifier (name);
+      TREE_ASM_WRITTEN (name_tree) = 1;
     }
-  else
-    mips_declare_common_object (stream, name, "\n\t.comm\t",
-                               size, align, true);
 }
 
 /* Declare a common object of SIZE bytes using asm directive INIT_STRING.
@@ -7130,30 +7101,32 @@ mips_declare_common_object (FILE *stream, const char *name,
                         size, align / BITS_PER_UNIT);
 }
 
-/* Emit either a label, .comm, or .lcomm directive.  When using assembler
-   macros, mark the symbol as written so that mips_file_end won't emit an
-   .extern for it.  STREAM is the output file, NAME is the name of the
-   symbol, INIT_STRING is the string that should be written before the
-   symbol and FINAL_STRING is the string that should be written after it.
-   FINAL_STRING is a printf() format that consumes the remaining arguments.  */
+/* Implement ASM_OUTPUT_ALIGNED_DECL_COMMON.  This is usually the same as the
+   elfos.h version, but we also need to handle -muninit-const-in-rodata.  */
 
 void
-mips_declare_object (FILE *stream, const char *name, const char *init_string,
-                    const char *final_string, ...)
+mips_output_aligned_decl_common (FILE *stream, tree decl, const char *name,
+                                unsigned HOST_WIDE_INT size,
+                                unsigned int align)
 {
-  va_list ap;
-
-  fputs (init_string, stream);
-  assemble_name (stream, name);
-  va_start (ap, final_string);
-  vfprintf (stream, final_string, ap);
-  va_end (ap);
-
-  if (!TARGET_EXPLICIT_RELOCS)
+  /* If the target wants uninitialized const declarations in
+     .rdata then don't put them in .comm.  */
+  if (TARGET_EMBEDDED_DATA && TARGET_UNINIT_CONST_IN_RODATA
+      && TREE_CODE (decl) == VAR_DECL && TREE_READONLY (decl)
+      && (DECL_INITIAL (decl) == 0 || DECL_INITIAL (decl) == error_mark_node))
     {
-      tree name_tree = get_identifier (name);
-      TREE_ASM_WRITTEN (name_tree) = 1;
+      if (TREE_PUBLIC (decl) && DECL_NAME (decl))
+       targetm.asm_out.globalize_label (stream, name);
+
+      switch_to_section (readonly_data_section);
+      ASM_OUTPUT_ALIGN (stream, floor_log2 (align / BITS_PER_UNIT));
+      mips_declare_object (stream, name, "",
+                          ":\n\t.space\t" HOST_WIDE_INT_PRINT_UNSIGNED "\n",
+                          size);
     }
+  else
+    mips_declare_common_object (stream, name, "\n\t.comm\t",
+                               size, align, true);
 }
 
 #ifdef ASM_OUTPUT_SIZE_DIRECTIVE
@@ -7206,262 +7179,118 @@ mips_finish_declare_object (FILE *stream, tree decl, int top_level, int at_end)
 }
 #endif
 \f
-/* Return true if X in context CONTEXT is a small data address that can
-   be rewritten as a LO_SUM.  */
+/* Implement TARGET_ASM_FILE_START.  */
 
-static bool
-mips_rewrite_small_data_p (rtx x, enum mips_symbol_context context)
+static void
+mips_file_start (void)
 {
-  enum mips_symbol_type symbol_type;
-
-  return (TARGET_EXPLICIT_RELOCS
-         && mips_symbolic_constant_p (x, context, &symbol_type)
-         && symbol_type == SYMBOL_GP_RELATIVE);
-}
-
+  default_file_start ();
 
-/* A for_each_rtx callback for mips_small_data_pattern_p.  DATA is the
-   containing MEM, or null if none.  */
+  if (!TARGET_IRIX)
+    {
+      /* Generate a special section to describe the ABI switches used to
+        produce the resultant binary.  This used to be done by the assembler
+        setting bits in the ELF header's flags field, but we have run out of
+        bits.  GDB needs this information in order to be able to correctly
+        debug these binaries.  See the function mips_gdbarch_init() in
+        gdb/mips-tdep.c.  This is unnecessary for the IRIX 5/6 ABIs and
+        causes unnecessary IRIX 6 ld warnings.  */
+      const char * abi_string = NULL;
 
-static int
-mips_small_data_pattern_1 (rtx *loc, void *data)
-{
-  enum mips_symbol_context context;
+      switch (mips_abi)
+       {
+       case ABI_32:   abi_string = "abi32"; break;
+       case ABI_N32:  abi_string = "abiN32"; break;
+       case ABI_64:   abi_string = "abi64"; break;
+       case ABI_O64:  abi_string = "abiO64"; break;
+       case ABI_EABI: abi_string = TARGET_64BIT ? "eabi64" : "eabi32"; break;
+       default:
+         gcc_unreachable ();
+       }
+      /* Note - we use fprintf directly rather than calling switch_to_section
+        because in this way we can avoid creating an allocated section.  We
+        do not want this section to take up any space in the running
+        executable.  */
+      fprintf (asm_out_file, "\t.section .mdebug.%s\n\t.previous\n",
+              abi_string);
 
-  if (GET_CODE (*loc) == LO_SUM)
-    return -1;
+      /* There is no ELF header flag to distinguish long32 forms of the
+        EABI from long64 forms.  Emit a special section to help tools
+        such as GDB.  Do the same for o64, which is sometimes used with
+        -mlong64.  */
+      if (mips_abi == ABI_EABI || mips_abi == ABI_O64)
+       fprintf (asm_out_file, "\t.section .gcc_compiled_long%d\n"
+                "\t.previous\n", TARGET_LONG64 ? 64 : 32);
 
-  if (MEM_P (*loc))
-    {
-      if (for_each_rtx (&XEXP (*loc, 0), mips_small_data_pattern_1, *loc))
-       return 1;
-      return -1;
+#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
     }
 
-  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
-   a LO_SUM.  */
+  /* Generate the pseudo ops that System V.4 wants.  */
+  if (TARGET_ABICALLS)
+    fprintf (asm_out_file, "\t.abicalls\n");
 
-bool
-mips_small_data_pattern_p (rtx op)
-{
-  return for_each_rtx (&op, mips_small_data_pattern_1, 0);
+  if (flag_verbose_asm)
+    fprintf (asm_out_file, "\n%s -G value = %d, Arch = %s, ISA = %d\n",
+            ASM_COMMENT_START,
+            mips_section_threshold, mips_arch_info->name, mips_isa);
 }
 \f
-/* A for_each_rtx callback, used by mips_rewrite_small_data.
-   DATA is the containing MEM, or null if none.  */
+\f
+/* Make the last instruction frame related and note that it performs
+   the operation described by FRAME_PATTERN.  */
 
-static int
-mips_rewrite_small_data_1 (rtx *loc, void *data)
+static void
+mips_set_frame_expr (rtx frame_pattern)
 {
-  enum mips_symbol_context context;
+  rtx insn;
 
-  if (MEM_P (*loc))
-    {
-      for_each_rtx (&XEXP (*loc, 0), mips_rewrite_small_data_1, *loc);
-      return -1;
-    }
+  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));
+}
 
-  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)
-    return -1;
+/* Return a frame-related rtx that stores REG at MEM.
+   REG must be a single register.  */
 
-  return 0;
-}
+static rtx
+mips_frame_set (rtx mem, rtx reg)
+{
+  rtx set;
 
-/* If possible, rewrite OP so that it refers to small data using
-   explicit relocations.  */
-
-rtx
-mips_rewrite_small_data (rtx op)
-{
-  op = copy_insn (op);
-  for_each_rtx (&op, mips_rewrite_small_data_1, 0);
-  return op;
-}
-\f
-/* Return true if the current function has an insn that implicitly
-   refers to $gp.  */
-
-static bool
-mips_function_has_gp_insn (void)
-{
-  /* Don't bother rechecking if we found one last time.  */
-  if (!cfun->machine->has_gp_insn_p)
-    {
-      rtx insn;
-
-      push_topmost_sequence ();
-      for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
-       if (INSN_P (insn)
-           && GET_CODE (PATTERN (insn)) != USE
-           && GET_CODE (PATTERN (insn)) != CLOBBER
-           && (get_attr_got (insn) != GOT_UNSET
-               || small_data_pattern (PATTERN (insn), VOIDmode)))
-         break;
-      pop_topmost_sequence ();
-
-      cfun->machine->has_gp_insn_p = (insn != 0);
-    }
-  return cfun->machine->has_gp_insn_p;
-}
-
-
-/* Return the register that should be used as the global pointer
-   within this function.  Return 0 if the function doesn't need
-   a global pointer.  */
-
-static unsigned int
-mips_global_pointer (void)
-{
-  unsigned int regno;
-
-  /* $gp is always available unless we're using a GOT.  */
-  if (!TARGET_USE_GOT)
-    return GLOBAL_POINTER_REGNUM;
-
-  /* We must always provide $gp when it is used implicitly.  */
-  if (!TARGET_EXPLICIT_RELOCS)
-    return GLOBAL_POINTER_REGNUM;
-
-  /* FUNCTION_PROFILER includes a jal macro, so we need to give it
-     a valid gp.  */
-  if (current_function_profile)
-    return GLOBAL_POINTER_REGNUM;
-
-  /* If the function has a nonlocal goto, $gp must hold the correct
-     global pointer for the target function.  */
-  if (current_function_has_nonlocal_goto)
-    return GLOBAL_POINTER_REGNUM;
-
-  /* If the gp is never referenced, there's no need to initialize it.
-     Note that reload can sometimes introduce constant pool references
-     into a function that otherwise didn't need them.  For example,
-     suppose we have an instruction like:
-
-         (set (reg:DF R1) (float:DF (reg:SI R2)))
-
-     If R2 turns out to be constant such as 1, the instruction may have a
-     REG_EQUAL note saying that R1 == 1.0.  Reload then has the option of
-     using this constant if R2 doesn't get allocated to a register.
-
-     In cases like these, reload will have added the constant to the pool
-     but no instruction will yet refer to it.  */
-  if (!df_regs_ever_live_p (GLOBAL_POINTER_REGNUM)
-      && !current_function_uses_const_pool
-      && !mips_function_has_gp_insn ())
-    return 0;
-
-  /* We need a global pointer, but perhaps we can use a call-clobbered
-     register instead of $gp.  */
-  if (TARGET_CALL_SAVED_GP && current_function_is_leaf)
-    for (regno = GP_REG_FIRST; regno <= GP_REG_LAST; regno++)
-      if (!df_regs_ever_live_p (regno)
-         && call_really_used_regs[regno]
-         && !fixed_regs[regno]
-         && regno != PIC_FUNCTION_ADDR_REGNUM)
-       return regno;
-
-  return GLOBAL_POINTER_REGNUM;
-}
-
-
-/* Return true if the function return value MODE will get returned in a
-   floating-point register.  */
-
-static bool
-mips_return_mode_in_fpr_p (enum machine_mode mode)
-{
-  return ((GET_MODE_CLASS (mode) == MODE_FLOAT
-          || GET_MODE_CLASS (mode) == MODE_VECTOR_FLOAT
-          || GET_MODE_CLASS (mode) == MODE_COMPLEX_FLOAT)
-         && GET_MODE_UNIT_SIZE (mode) <= UNITS_PER_HWFPVALUE);
-}
-
-/* Return a two-character string representing a function floating-point
-   return mode, used to name MIPS16 function stubs.  */
-
-static const char *
-mips16_call_stub_mode_suffix (enum machine_mode mode)
-{
-  if (mode == SFmode)
-    return "sf";
-  else if (mode == DFmode)
-    return "df";
-  else if (mode == SCmode)
-    return "sc";
-  else if (mode == DCmode)
-    return "dc";
-  else if (mode == V2SFmode)
-    return "df";
-  else
-    gcc_unreachable ();
-}
+  /* 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);
 
-/* Return true if the current function returns its value in a floating-point
-   register in MIPS16 mode.  */
+  set = gen_rtx_SET (VOIDmode, mem, reg);
+  RTX_FRAME_RELATED_P (set) = 1;
 
-static bool
-mips16_cfun_returns_in_fpr_p (void)
-{
-  tree return_type = DECL_RESULT (current_function_decl);
-  return (TARGET_MIPS16
-         && TARGET_HARD_FLOAT_ABI
-         && !aggregate_value_p (return_type, current_function_decl)
-         && mips_return_mode_in_fpr_p (DECL_MODE (return_type)));
+  return set;
 }
+\f
+/* 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
+};
 
-
-/* Return true if the current function must save REGNO.  */
-
-static bool
-mips_save_reg_p (unsigned int regno)
-{
-  /* We only need to save $gp if TARGET_CALL_SAVED_GP and only then
-     if we have not chosen a call-clobbered substitute.  */
-  if (regno == GLOBAL_POINTER_REGNUM)
-    return TARGET_CALL_SAVED_GP && cfun->machine->global_pointer == regno;
-
-  /* Check call-saved registers.  */
-  if ((current_function_saves_all_registers || df_regs_ever_live_p (regno))
-      && !call_really_used_regs[regno])
-    return true;
-
-  /* Save both registers in an FPR pair if either one is used.  This is
-     needed for the case when MIN_FPRS_PER_FMT == 1, which allows the odd
-     register to be used without the even register.  */
-  if (FP_REG_P (regno)
-      && MAX_FPRS_PER_FMT == 2
-      && df_regs_ever_live_p (regno + 1)
-      && !call_really_used_regs[regno + 1])
-    return true;
-
-  /* We need to save the old frame pointer before setting up a new one.  */
-  if (regno == HARD_FRAME_POINTER_REGNUM && frame_pointer_needed)
-    return true;
-
-  /* Check for registers that must be saved for FUNCTION_PROFILER.  */
-  if (current_function_profile && MIPS_SAVE_REG_FOR_PROFILING_P (regno))
-    return true;
-
-  /* We need to save the incoming return address if it is ever clobbered
-     within the function, if __builtin_eh_return is being used to set a
-     different return address, or if a stub is being used to return a
-     value in FPRs.  */
-  if (regno == GP_REG_FIRST + 31
-      && (df_regs_ever_live_p (regno)
-         || current_function_calls_eh_return
-         || mips16_cfun_returns_in_fpr_p ()))
-    return true;
-
-  return false;
-}
+/* 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
+};
 
 /* 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.  */
@@ -7499,1093 +7328,1034 @@ mips16e_mask_registers (unsigned int *mask_ptr, const unsigned char *regs,
       }
 }
 
-/* Return the bytes needed to compute the frame pointer from the current
-   stack pointer.  SIZE is the size (in bytes) of the local variables.
+/* 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.
 
-   MIPS stack frames look like:
+   This function is rather limited, but is good enough for our purposes.  */
 
-             Before call                       After call
-   high +-----------------------+      +-----------------------+
-   mem. |                      |       |                       |
-       |  caller's temps.      |       |  caller's temps.      |
-       |                       |       |                       |
-        +-----------------------+      +-----------------------+
-       |                       |       |                       |
-        |  arguments on stack.  |      |  arguments on stack.  |
-       |                       |       |                       |
-        +-----------------------+      +-----------------------+
-       |  4 words to save      |       |  4 words to save      |
-       |  arguments passed     |       |  arguments passed     |
-       |  in registers, even   |       |  in registers, even   |
-        |  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    |
-                                       +-----------------------+
-                                       |                       |\
-                                       |  local variables      | | var_size
-                                       |                       |/
-                                       +-----------------------+
-                                       |                       |
-                                       |  alloca allocations   |
-                                       |                       |
-                                       +-----------------------+
-                                      /|                       |
-                      cprestore_size | |  GP save for V.4 abi  |
-                                      \|                       |
-                                       +-----------------------+
-                                       |                       |\
-                                       |  arguments on stack   | |
-                                       |                       | |
-                                       +-----------------------+ |
-                                       |  4 words to save      | | args_size
-                                       |  arguments passed     | |
-                                       |  in registers, even   | |
-                                       |  if not passed.       | |
-   low                                 |  (TARGET_OLDABI only) |/
-   memory                          SP->+-----------------------+
-
-*/
-
-HOST_WIDE_INT
-compute_frame_size (HOST_WIDE_INT size)
+static rtx
+mips16e_collect_propagate_value (rtx x, rtx *reg_values)
 {
-  unsigned int regno;
-  HOST_WIDE_INT total_size;    /* # bytes that the entire frame takes up */
-  HOST_WIDE_INT var_size;      /* # bytes that variables take up */
-  HOST_WIDE_INT args_size;     /* # bytes that outgoing arguments take up */
-  HOST_WIDE_INT cprestore_size; /* # bytes that the cprestore slot takes up */
-  HOST_WIDE_INT gp_reg_rounded;        /* # bytes needed to store gp after rounding */
-  HOST_WIDE_INT gp_reg_size;   /* # bytes needed to store gp regs */
-  HOST_WIDE_INT fp_reg_size;   /* # bytes needed to store fp regs */
-  unsigned int mask;           /* mask of saved gp registers */
-  unsigned int fmask;          /* mask of saved fp registers */
-
-  cfun->machine->global_pointer = mips_global_pointer ();
-
-  gp_reg_size = 0;
-  fp_reg_size = 0;
-  mask = 0;
-  fmask        = 0;
-  var_size = MIPS_STACK_ALIGN (size);
-  args_size = current_function_outgoing_args_size;
-  cprestore_size = MIPS_STACK_ALIGN (STARTING_FRAME_OFFSET) - args_size;
-
-  /* The space set aside by STARTING_FRAME_OFFSET isn't needed in leaf
-     functions.  If the function has local variables, we're committed
-     to allocating it anyway.  Otherwise reclaim it here.  */
-  if (var_size == 0 && current_function_is_leaf)
-    cprestore_size = args_size = 0;
-
-  /* The MIPS 3.0 linker does not like functions that dynamically
-     allocate the stack and have 0 for STACK_DYNAMIC_OFFSET, since it
-     looks like we are trying to create a second frame pointer to the
-     function, so allocate some stack space to make it happy.  */
-
-  if (args_size == 0 && current_function_calls_alloca)
-    args_size = 4 * UNITS_PER_WORD;
-
-  total_size = var_size + args_size + cprestore_size;
+  rtx x0, x1;
 
-  /* Calculate space needed for gp registers.  */
-  for (regno = GP_REG_FIRST; regno <= GP_REG_LAST; regno++)
-    if (mips_save_reg_p (regno))
-      {
-       gp_reg_size += GET_MODE_SIZE (gpr_mode);
-       mask |= 1 << (regno - GP_REG_FIRST);
-      }
+  x = avoid_constant_pool_reference (x);
 
-  /* We need to restore these for the handler.  */
-  if (current_function_calls_eh_return)
+  if (UNARY_P (x))
     {
-      unsigned int i;
-      for (i = 0; ; ++i)
-       {
-         regno = EH_RETURN_DATA_REGNO (i);
-         if (regno == INVALID_REGNUM)
-           break;
-         gp_reg_size += GET_MODE_SIZE (gpr_mode);
-         mask |= 1 << (regno - GP_REG_FIRST);
-       }
+      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)));
     }
 
-  /* 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)
+  if (ARITHMETIC_P (x))
     {
-      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);
+      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);
     }
 
-  /* This loop must iterate over the same space as its companion in
-     mips_for_each_saved_reg.  */
-  if (TARGET_HARD_FLOAT)
-    for (regno = (FP_REG_LAST - MAX_FPRS_PER_FMT + 1);
-        regno >= FP_REG_FIRST;
-        regno -= MAX_FPRS_PER_FMT)
-      if (mips_save_reg_p (regno))
-       {
-         fp_reg_size += MAX_FPRS_PER_FMT * UNITS_PER_FPREG;
-         fmask |= ((1 << MAX_FPRS_PER_FMT) - 1) << (regno - FP_REG_FIRST);
-       }
-
-  gp_reg_rounded = MIPS_STACK_ALIGN (gp_reg_size);
-  total_size += gp_reg_rounded + MIPS_STACK_ALIGN (fp_reg_size);
+  if (REG_P (x)
+      && reg_values[REGNO (x)]
+      && !rtx_unstable_p (reg_values[REGNO (x)]))
+    return reg_values[REGNO (x)];
 
-  /* Add in the space required for saving incoming register arguments.  */
-  total_size += current_function_pretend_args_size;
-  total_size += MIPS_STACK_ALIGN (cfun->machine->varargs_size);
+  return x;
+}
 
-  /* Save other computed information.  */
-  cfun->machine->frame.total_size = total_size;
-  cfun->machine->frame.var_size = var_size;
-  cfun->machine->frame.args_size = args_size;
-  cfun->machine->frame.cprestore_size = cprestore_size;
-  cfun->machine->frame.gp_reg_size = gp_reg_size;
-  cfun->machine->frame.fp_reg_size = fp_reg_size;
-  cfun->machine->frame.mask = mask;
-  cfun->machine->frame.fmask = fmask;
-  cfun->machine->frame.initialized = reload_completed;
-  cfun->machine->frame.num_gp = gp_reg_size / UNITS_PER_WORD;
-  cfun->machine->frame.num_fp = (fp_reg_size
-                                / (MAX_FPRS_PER_FMT * UNITS_PER_FPREG));
+/* 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.  */
 
-  if (mask)
-    {
-      HOST_WIDE_INT offset;
+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;
 
-      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;
-    }
-  else
-    {
-      cfun->machine->frame.gp_sp_offset = 0;
-      cfun->machine->frame.gp_save_offset = 0;
-    }
+  /* Check that this is a word-mode store.  */
+  if (!MEM_P (dest) || !REG_P (src) || GET_MODE (dest) != word_mode)
+    return false;
 
-  if (fmask)
-    {
-      HOST_WIDE_INT offset;
+  /* 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;
 
-      offset = (args_size + cprestore_size + var_size
-               + gp_reg_rounded + fp_reg_size
-               - MAX_FPRS_PER_FMT * UNITS_PER_FPREG);
-      cfun->machine->frame.fp_sp_offset = offset;
-      cfun->machine->frame.fp_save_offset = offset - total_size;
-    }
-  else
-    {
-      cfun->machine->frame.fp_sp_offset = 0;
-      cfun->machine->frame.fp_save_offset = 0;
-    }
+  /* 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;
 
-  /* Ok, we're done.  */
-  return total_size;
+  *regno_ptr = regno;
+  return true;
 }
-\f
-/* Implement INITIAL_ELIMINATION_OFFSET.  FROM is either the frame
-   pointer or argument pointer.  TO is either the stack pointer or
-   hard frame pointer.  */
 
-HOST_WIDE_INT
-mips_initial_elimination_offset (int from, int to)
-{
-  HOST_WIDE_INT offset;
+/* 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.  */
 
-  compute_frame_size (get_frame_size ());
+static unsigned int
+mips16e_collect_argument_saves (void)
+{
+  rtx reg_values[FIRST_PSEUDO_REGISTER];
+  rtx insn, next, set, dest, src;
+  unsigned int nargs, regno;
 
-  /* Set OFFSET to the offset from the stack pointer.  */
-  switch (from)
+  push_topmost_sequence ();
+  nargs = 0;
+  memset (reg_values, 0, sizeof (reg_values));
+  for (insn = get_insns (); insn; insn = next)
     {
-    case FRAME_POINTER_REGNUM:
-      offset = 0;
-      break;
+      next = NEXT_INSN (insn);
+      if (NOTE_P (insn))
+       continue;
 
-    case ARG_POINTER_REGNUM:
-      offset = (cfun->machine->frame.total_size
-               - current_function_pretend_args_size);
-      break;
+      if (!INSN_P (insn))
+       break;
 
-    default:
-      gcc_unreachable ();
+      set = PATTERN (insn);
+      if (GET_CODE (set) != SET)
+       break;
+
+      dest = SET_DEST (set);
+      src = SET_SRC (set);
+      if (mips16e_collect_argument_save_p (dest, src, reg_values, &regno))
+       {
+         if (!BITSET_P (cfun->machine->frame.mask, regno))
+           {
+             delete_insn (insn);
+             nargs = MAX (nargs, (regno - GP_ARG_FIRST) + 1);
+           }
+       }
+      else if (REG_P (dest) && GET_MODE (dest) == word_mode)
+       reg_values[REGNO (dest)]
+         = mips16e_collect_propagate_value (src, reg_values);
+      else
+       break;
     }
+  pop_topmost_sequence ();
 
-  if (TARGET_MIPS16 && to == HARD_FRAME_POINTER_REGNUM)
-    offset -= cfun->machine->frame.args_size;
+  return nargs;
+}
 
-  return offset;
-}
-\f
-/* Implement RETURN_ADDR_RTX.  Note, we do not support moving
-   back to a previous frame.  */
-rtx
-mips_return_addr (int count, rtx frame ATTRIBUTE_UNUSED)
-{
-  if (count != 0)
-    return const0_rtx;
-
-  return get_hard_reg_initial_val (Pmode, GP_REG_FIRST + 31);
-}
-\f
-/* Use FN to save or restore register REGNO.  MODE is the register's
-   mode and OFFSET is the offset of its save slot from the current
-   stack pointer.  */
+/* 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 void
-mips_save_restore_reg (enum machine_mode mode, int regno,
-                      HOST_WIDE_INT offset, mips_save_restore_fn fn)
+static rtx
+mips16e_save_restore_reg (bool restore_p, HOST_WIDE_INT offset,
+                         unsigned int regno)
 {
-  rtx mem;
-
-  mem = gen_frame_mem (mode, plus_constant (stack_pointer_rtx, offset));
+  rtx reg, mem;
 
-  fn (gen_rtx_REG (mode, regno), 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:
 
-/* Call FN for each register that is saved by the current function.
-   SP_OFFSET is the offset of the current stack pointer from the start
-   of the frame.  */
-
-static void
-mips_for_each_saved_reg (HOST_WIDE_INT sp_offset, mips_save_restore_fn fn)
-{
-  enum machine_mode fpr_mode;
-  HOST_WIDE_INT offset;
-  int regno;
+     - Allocate or deallocate SIZE bytes in total; SIZE is known
+       to be nonzero.
 
-  /* Save registers starting from high to low.  The debuggers prefer at least
-     the return register be stored at func+4, and also it allows us not to
-     need a nop in the epilogue if at least one register is reloaded in
-     addition to return address.  */
-  offset = cfun->machine->frame.gp_sp_offset - sp_offset;
-  for (regno = GP_REG_LAST; regno >= GP_REG_FIRST; regno--)
-    if (BITSET_P (cfun->machine->frame.mask, regno - GP_REG_FIRST))
-      {
-       mips_save_restore_reg (gpr_mode, regno, offset, fn);
-       offset -= GET_MODE_SIZE (gpr_mode);
-      }
+     - 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.
 
-  /* This loop must iterate over the same space as its companion in
-     compute_frame_size.  */
-  offset = cfun->machine->frame.fp_sp_offset - sp_offset;
-  fpr_mode = (TARGET_SINGLE_FLOAT ? SFmode : DFmode);
-  for (regno = (FP_REG_LAST - MAX_FPRS_PER_FMT + 1);
-       regno >= FP_REG_FIRST;
-       regno -= MAX_FPRS_PER_FMT)
-    if (BITSET_P (cfun->machine->frame.fmask, regno - FP_REG_FIRST))
-      {
-       mips_save_restore_reg (fpr_mode, regno, offset, fn);
-       offset -= GET_MODE_SIZE (fpr_mode);
-      }
-}
-\f
-/* If we're generating n32 or n64 abicalls, and the current function
-   does not use $28 as its global pointer, emit a cplocal directive.
-   Use pic_offset_table_rtx as the argument to the directive.  */
+     - Save NARGS argument registers above the allocated area.
 
-static void
-mips_output_cplocal (void)
-{
-  if (!TARGET_EXPLICIT_RELOCS
-      && cfun->machine->global_pointer > 0
-      && cfun->machine->global_pointer != GLOBAL_POINTER_REGNUM)
-    output_asm_insn (".cplocal %+", 0);
-}
+   (NARGS is always zero if RESTORE_P.)
 
-/* Return the style of GP load sequence that is being used for the
-   current function.  */
+   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.  */
 
-enum mips_loadgp_style
-mips_current_loadgp_style (void)
+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)
 {
-  if (!TARGET_USE_GOT || cfun->machine->global_pointer == 0)
-    return LOADGP_NONE;
-
-  if (TARGET_RTP_PIC)
-    return LOADGP_RTP;
+  rtx pattern, set;
+  HOST_WIDE_INT offset, top_offset;
+  unsigned int i, regno;
+  int n;
 
-  if (TARGET_ABSOLUTE_ABICALLS)
-    return LOADGP_ABSOLUTE;
+  gcc_assert (cfun->machine->frame.fp_reg_size == 0);
 
-  return TARGET_NEWABI ? LOADGP_NEWABI : LOADGP_OLDABI;
-}
+  /* 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++;
 
-/* The __gnu_local_gp symbol.  */
+  /* Create the final PARALLEL.  */
+  pattern = gen_rtx_PARALLEL (VOIDmode, rtvec_alloc (n));
+  n = 0;
 
-static GTY(()) rtx mips_gnu_local_gp;
+  /* 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;
 
-/* If we're generating n32 or n64 abicalls, emit instructions
-   to set up the global pointer.  */
+  /* Stack offsets in the PARALLEL are relative to the old stack pointer.  */
+  top_offset = restore_p ? size : 0;
 
-static void
-mips_emit_loadgp (void)
-{
-  rtx addr, offset, incoming_address, base, index;
+  /* 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;
+    }
 
-  switch (mips_current_loadgp_style ())
+  /* Then fill in the other register moves.  */
+  offset = top_offset;
+  for (i = 0; i < ARRAY_SIZE (mips16e_save_restore_regs); i++)
     {
-    case LOADGP_ABSOLUTE:
-      if (mips_gnu_local_gp == NULL)
+      regno = mips16e_save_restore_regs[i];
+      if (BITSET_P (*mask_ptr, regno))
        {
-         mips_gnu_local_gp = gen_rtx_SYMBOL_REF (Pmode, "__gnu_local_gp");
-         SYMBOL_REF_FLAGS (mips_gnu_local_gp) |= SYMBOL_FLAG_LOCAL;
+         offset -= UNITS_PER_WORD;
+         set = mips16e_save_restore_reg (restore_p, offset, regno);
+         XVECEXP (pattern, 0, n++) = set;
+         *mask_ptr &= ~(1 << regno);
        }
-      emit_insn (gen_loadgp_absolute (mips_gnu_local_gp));
-      break;
+    }
 
-    case LOADGP_NEWABI:
-      addr = XEXP (DECL_RTL (current_function_decl), 0);
-      offset = mips_unspec_address (addr, SYMBOL_GOTOFF_LOADGP);
-      incoming_address = gen_rtx_REG (Pmode, PIC_FUNCTION_ADDR_REGNUM);
-      emit_insn (gen_loadgp_newabi (offset, incoming_address));
-      if (!TARGET_EXPLICIT_RELOCS)
-       emit_insn (gen_loadgp_blockage ());
-      break;
+  /* Tell the caller what offset it should use for the remaining registers.  */
+  *offset_ptr = size + (offset - top_offset) + size;
 
-    case LOADGP_RTP:
-      base = gen_rtx_SYMBOL_REF (Pmode, ggc_strdup (VXWORKS_GOTT_BASE));
-      index = gen_rtx_SYMBOL_REF (Pmode, ggc_strdup (VXWORKS_GOTT_INDEX));
-      emit_insn (gen_loadgp_rtp (base, index));
-      if (!TARGET_EXPLICIT_RELOCS)
-       emit_insn (gen_loadgp_blockage ());
-      break;
+  gcc_assert (n == XVECLEN (pattern, 0));
 
-    default:
-      break;
-    }
+  return pattern;
 }
 
-/* Set up the stack and frame (if desired) for the function.  */
+/* 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.  */
 
-static void
-mips_output_function_prologue (FILE *file, HOST_WIDE_INT size ATTRIBUTE_UNUSED)
+bool
+mips16e_save_restore_pattern_p (rtx pattern, HOST_WIDE_INT adjust,
+                               struct mips16e_save_restore_info *info)
 {
-  const char *fnname;
-  HOST_WIDE_INT tsize = cfun->machine->frame.total_size;
-
-#ifdef SDB_DEBUGGING_INFO
-  if (debug_info_level != DINFO_LEVEL_TERSE && write_symbols == SDB_DEBUG)
-    SDB_OUTPUT_SOURCE_LINE (file, DECL_SOURCE_LINE (current_function_decl));
-#endif
+  unsigned int i, nargs, mask;
+  HOST_WIDE_INT top_offset, save_offset, offset, extra;
+  rtx set, reg, mem, base;
+  int n;
 
-  /* In mips16 mode, we may need to generate a 32 bit to handle
-     floating point arguments.  The linker will arrange for any 32-bit
-     functions to call this stub, which will then jump to the 16-bit
-     function proper.  */
-  if (TARGET_MIPS16
-      && TARGET_HARD_FLOAT_ABI
-      && current_function_args_info.fp_code != 0)
-    build_mips16_function_stub (file);
+  if (!GENERATE_MIPS16E_SAVE_RESTORE)
+    return false;
 
-  /* Select the mips16 mode for this function.  */
-  if (TARGET_MIPS16)
-    fprintf (file, "\t.set\tmips16\n");
-  else
-    fprintf (file, "\t.set\tnomips16\n");
+  /* Stack offsets in the PARALLEL are relative to the old stack pointer.  */
+  top_offset = adjust > 0 ? adjust : 0;
 
-  if (!FUNCTION_NAME_ALREADY_DECLARED)
+  /* 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++)
     {
-      /* Get the function name the same way that toplev.c does before calling
-        assemble_start_function.  This is needed so that the name used here
-        exactly matches the name used in ASM_DECLARE_FUNCTION_NAME.  */
-      fnname = XSTR (XEXP (DECL_RTL (current_function_decl), 0), 0);
+      /* Check that we have a SET.  */
+      set = XVECEXP (pattern, 0, n);
+      if (GET_CODE (set) != SET)
+       return false;
 
-      if (!flag_inhibit_size_directive)
-       {
-         fputs ("\t.ent\t", file);
-         assemble_name (file, fnname);
-         fputs ("\n", file);
-       }
+      /* 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;
 
-      assemble_name (file, fnname);
-      fputs (":\n", file);
-    }
-
-  /* Stop mips_file_end from treating this function as external.  */
-  if (TARGET_IRIX && mips_abi == ABI_32)
-    TREE_ASM_WRITTEN (DECL_NAME (cfun->decl)) = 1;
+      /* 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;
 
-  if (!flag_inhibit_size_directive)
-    {
-      /* .frame FRAMEREG, FRAMESIZE, RETREG */
-      fprintf (file,
-              "\t.frame\t%s," HOST_WIDE_INT_PRINT_DEC ",%s\t\t"
-              "# vars= " HOST_WIDE_INT_PRINT_DEC ", regs= %d/%d"
-              ", args= " HOST_WIDE_INT_PRINT_DEC
-              ", gp= " HOST_WIDE_INT_PRINT_DEC "\n",
-              (reg_names[(frame_pointer_needed)
-                         ? HARD_FRAME_POINTER_REGNUM : STACK_POINTER_REGNUM]),
-              ((frame_pointer_needed && TARGET_MIPS16)
-               ? tsize - cfun->machine->frame.args_size
-               : tsize),
-              reg_names[GP_REG_FIRST + 31],
-              cfun->machine->frame.var_size,
-              cfun->machine->frame.num_gp,
-              cfun->machine->frame.num_fp,
-              cfun->machine->frame.args_size,
-              cfun->machine->frame.cprestore_size);
+      /* Check that SET's other operand is a register.  */
+      reg = adjust > 0 ? SET_DEST (set) : SET_SRC (set);
+      if (!REG_P (reg))
+       return false;
 
-      /* .mask MASK, GPOFFSET; .fmask FPOFFSET */
-      fprintf (file, "\t.mask\t0x%08x," HOST_WIDE_INT_PRINT_DEC "\n",
-              cfun->machine->frame.mask,
-              cfun->machine->frame.gp_save_offset);
-      fprintf (file, "\t.fmask\t0x%08x," HOST_WIDE_INT_PRINT_DEC "\n",
-              cfun->machine->frame.fmask,
-              cfun->machine->frame.fp_save_offset);
+      /* 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;
 
-      /* Require:
-        OLD_SP == *FRAMEREG + FRAMESIZE => can find old_sp from nominated FP reg.
-        HIGHEST_GP_SAVED == *FRAMEREG + FRAMESIZE + GPOFFSET => can find saved regs.  */
+         mask |= 1 << REGNO (reg);
+         save_offset -= GET_MODE_SIZE (gpr_mode);
+       }
+      else
+       return false;
     }
 
-  if (mips_current_loadgp_style () == LOADGP_OLDABI)
+  /* 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)
     {
-      /* Handle the initialization of $gp for SVR4 PIC.  */
-      if (!cfun->machine->all_noreorder_p)
-       output_asm_insn ("%(.cpload\t%^%)", 0);
-      else
-       output_asm_insn ("%(.cpload\t%^\n\t%<", 0);
+      info->nargs = nargs;
+      info->mask = mask;
+      info->size = (adjust > 0 ? adjust : -adjust);
     }
-  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 ();
+  return true;
 }
-\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;
+/* 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.  */
 
-  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));
+static char *
+mips16e_add_register_range (char *s, unsigned int min_reg,
+                           unsigned int max_reg)
+{
+  if (min_reg != max_reg)
+    s += sprintf (s, ",%s-%s", reg_names[min_reg], reg_names[max_reg]);
+  else
+    s += sprintf (s, ",%s", reg_names[min_reg]);
+  return s;
 }
 
+/* Return the assembly instruction for a MIPS16e SAVE or RESTORE instruction.
+   PATTERN and ADJUST are as for mips16e_save_restore_pattern_p.  */
 
-/* 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)
+const char *
+mips16e_output_save_restore (rtx pattern, HOST_WIDE_INT adjust)
 {
-  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);
+  static char buffer[300];
 
-  set = gen_rtx_SET (VOIDmode, mem, reg);
-  RTX_FRAME_RELATED_P (set) = 1;
+  struct mips16e_save_restore_info info;
+  unsigned int i, end;
+  char *s;
 
-  return set;
-}
+  /* 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 register REG to MEM.  Make the instruction frame-related.  */
+  /* 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]);
 
-static void
-mips_save_reg (rtx reg, rtx mem)
-{
-  if (GET_MODE (reg) == DFmode && !TARGET_FLOAT64)
-    {
-      rtx x1, x2;
+  /* Emit the amount of stack space to allocate or deallocate.  */
+  s += sprintf (s, "%d", (int) info.size);
 
-      if (mips_split_64bit_move_p (mem, reg))
-       mips_split_doubleword_move (mem, reg);
-      else
-       mips_emit_move (mem, reg);
+  /* Save or restore $16.  */
+  if (BITSET_P (info.mask, 16))
+    s += sprintf (s, ",%s", reg_names[GP_REG_FIRST + 16]);
 
-      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);
+  /* Save or restore $17.  */
+  if (BITSET_P (info.mask, 17))
+    s += sprintf (s, ",%s", reg_names[GP_REG_FIRST + 17]);
 
-      mips_set_frame_expr (mips_frame_set (mem, reg));
-    }
-}
+  /* 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]);
 
-/* 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.  */
+  /* 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]);
 
-static rtx
-mips16e_save_restore_reg (bool restore_p, HOST_WIDE_INT offset,
-                         unsigned int regno)
-{
-  rtx reg, mem;
+  /* Save or restore $31.  */
+  if (BITSET_P (info.mask, 31))
+    s += sprintf (s, ",%s", reg_names[GP_REG_FIRST + 31]);
 
-  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 buffer;
 }
+\f
+/* Return true if the current function has an insn that implicitly
+   refers to $gp.  */
 
-/* Return RTL for a MIPS16e SAVE or RESTORE instruction; RESTORE_P says which.
-   The instruction must:
+static bool
+mips_function_has_gp_insn (void)
+{
+  /* Don't bother rechecking if we found one last time.  */
+  if (!cfun->machine->has_gp_insn_p)
+    {
+      rtx insn;
 
-     - Allocate or deallocate SIZE bytes in total; SIZE is known
-       to be nonzero.
+      push_topmost_sequence ();
+      for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
+       if (INSN_P (insn)
+           && GET_CODE (PATTERN (insn)) != USE
+           && GET_CODE (PATTERN (insn)) != CLOBBER
+           && (get_attr_got (insn) != GOT_UNSET
+               || small_data_pattern (PATTERN (insn), VOIDmode)))
+         break;
+      pop_topmost_sequence ();
 
-     - 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.
+      cfun->machine->has_gp_insn_p = (insn != 0);
+    }
+  return cfun->machine->has_gp_insn_p;
+}
 
-     - 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.  */
+/* Return the register that should be used as the global pointer
+   within this function.  Return 0 if the function doesn't need
+   a global pointer.  */
 
-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)
+static unsigned int
+mips_global_pointer (void)
 {
-  rtx pattern, set;
-  HOST_WIDE_INT offset, top_offset;
-  unsigned int i, regno;
-  int n;
+  unsigned int regno;
 
-  gcc_assert (cfun->machine->frame.fp_reg_size == 0);
+  /* $gp is always available unless we're using a GOT.  */
+  if (!TARGET_USE_GOT)
+    return GLOBAL_POINTER_REGNUM;
 
-  /* 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++;
+  /* We must always provide $gp when it is used implicitly.  */
+  if (!TARGET_EXPLICIT_RELOCS)
+    return GLOBAL_POINTER_REGNUM;
 
-  /* Create the final PARALLEL.  */
-  pattern = gen_rtx_PARALLEL (VOIDmode, rtvec_alloc (n));
-  n = 0;
+  /* FUNCTION_PROFILER includes a jal macro, so we need to give it
+     a valid gp.  */
+  if (current_function_profile)
+    return GLOBAL_POINTER_REGNUM;
 
-  /* 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;
+  /* If the function has a nonlocal goto, $gp must hold the correct
+     global pointer for the target function.  */
+  if (current_function_has_nonlocal_goto)
+    return GLOBAL_POINTER_REGNUM;
 
-  /* Stack offsets in the PARALLEL are relative to the old stack pointer.  */
-  top_offset = restore_p ? size : 0;
+  /* If the gp is never referenced, there's no need to initialize it.
+     Note that reload can sometimes introduce constant pool references
+     into a function that otherwise didn't need them.  For example,
+     suppose we have an instruction like:
 
-  /* 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;
-    }
+         (set (reg:DF R1) (float:DF (reg:SI R2)))
 
-  /* 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);
-       }
-    }
+     If R2 turns out to be constant such as 1, the instruction may have a
+     REG_EQUAL note saying that R1 == 1.0.  Reload then has the option of
+     using this constant if R2 doesn't get allocated to a register.
 
-  /* Tell the caller what offset it should use for the remaining registers.  */
-  *offset_ptr = size + (offset - top_offset) + size;
+     In cases like these, reload will have added the constant to the pool
+     but no instruction will yet refer to it.  */
+  if (!df_regs_ever_live_p (GLOBAL_POINTER_REGNUM)
+      && !current_function_uses_const_pool
+      && !mips_function_has_gp_insn ())
+    return 0;
 
-  gcc_assert (n == XVECLEN (pattern, 0));
+  /* We need a global pointer, but perhaps we can use a call-clobbered
+     register instead of $gp.  */
+  if (TARGET_CALL_SAVED_GP && current_function_is_leaf)
+    for (regno = GP_REG_FIRST; regno <= GP_REG_LAST; regno++)
+      if (!df_regs_ever_live_p (regno)
+         && call_really_used_regs[regno]
+         && !fixed_regs[regno]
+         && regno != PIC_FUNCTION_ADDR_REGNUM)
+       return regno;
 
-  return pattern;
+  return GLOBAL_POINTER_REGNUM;
 }
 
-/* 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.  */
+/* Return true if the current function returns its value in a floating-point
+   register in MIPS16 mode.  */
 
-bool
-mips16e_save_restore_pattern_p (rtx pattern, HOST_WIDE_INT adjust,
-                               struct mips16e_save_restore_info *info)
+static bool
+mips16_cfun_returns_in_fpr_p (void)
 {
-  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;
+  tree return_type = DECL_RESULT (current_function_decl);
+  return (TARGET_MIPS16
+         && TARGET_HARD_FLOAT_ABI
+         && !aggregate_value_p (return_type, current_function_decl)
+         && mips_return_mode_in_fpr_p (DECL_MODE (return_type)));
+}
 
-      /* 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;
+/* Return true if the current function must save REGNO.  */
 
-         mask |= 1 << REGNO (reg);
-         save_offset -= GET_MODE_SIZE (gpr_mode);
-       }
-      else
-       return false;
-    }
+static bool
+mips_save_reg_p (unsigned int regno)
+{
+  /* We only need to save $gp if TARGET_CALL_SAVED_GP and only then
+     if we have not chosen a call-clobbered substitute.  */
+  if (regno == GLOBAL_POINTER_REGNUM)
+    return TARGET_CALL_SAVED_GP && cfun->machine->global_pointer == regno;
 
-  /* 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;
+  /* Check call-saved registers.  */
+  if ((current_function_saves_all_registers || df_regs_ever_live_p (regno))
+      && !call_really_used_regs[regno])
+    return true;
 
-  /* 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;
+  /* Save both registers in an FPR pair if either one is used.  This is
+     needed for the case when MIN_FPRS_PER_FMT == 1, which allows the odd
+     register to be used without the even register.  */
+  if (FP_REG_P (regno)
+      && MAX_FPRS_PER_FMT == 2
+      && df_regs_ever_live_p (regno + 1)
+      && !call_really_used_regs[regno + 1])
+    return true;
 
-  /* Pass back information, if requested.  */
-  if (info)
-    {
-      info->nargs = nargs;
-      info->mask = mask;
-      info->size = (adjust > 0 ? adjust : -adjust);
-    }
+  /* We need to save the old frame pointer before setting up a new one.  */
+  if (regno == HARD_FRAME_POINTER_REGNUM && frame_pointer_needed)
+    return true;
 
-  return true;
-}
+  /* Check for registers that must be saved for FUNCTION_PROFILER.  */
+  if (current_function_profile && MIPS_SAVE_REG_FOR_PROFILING_P (regno))
+    return true;
 
-/* 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.  */
+  /* We need to save the incoming return address if it is ever clobbered
+     within the function, if __builtin_eh_return is being used to set a
+     different return address, or if a stub is being used to return a
+     value in FPRs.  */
+  if (regno == GP_REG_FIRST + 31
+      && (df_regs_ever_live_p (regno)
+         || current_function_calls_eh_return
+         || mips16_cfun_returns_in_fpr_p ()))
+    return true;
 
-static char *
-mips16e_add_register_range (char *s, unsigned int min_reg,
-                           unsigned int max_reg)
-{
-  if (min_reg != max_reg)
-    s += sprintf (s, ",%s-%s", reg_names[min_reg], reg_names[max_reg]);
-  else
-    s += sprintf (s, ",%s", reg_names[min_reg]);
-  return s;
+  return false;
 }
 
-/* Return the assembly instruction for a MIPS16e SAVE or RESTORE instruction.
-   PATTERN and ADJUST are as for mips16e_save_restore_pattern_p.  */
-
-const char *
-mips16e_output_save_restore (rtx pattern, HOST_WIDE_INT adjust)
-{
-  static char buffer[300];
+/* Return the bytes needed to compute the frame pointer from the current
+   stack pointer.  SIZE is the size (in bytes) of the local variables.
 
-  struct mips16e_save_restore_info info;
-  unsigned int i, end;
-  char *s;
+   MIPS stack frames look like:
 
-  /* Parse the pattern.  */
-  if (!mips16e_save_restore_pattern_p (pattern, adjust, &info))
-    gcc_unreachable ();
+             Before call                       After call
+   high +-----------------------+      +-----------------------+
+   mem. |                      |       |                       |
+       |  caller's temps.      |       |  caller's temps.      |
+       |                       |       |                       |
+        +-----------------------+      +-----------------------+
+       |                       |       |                       |
+        |  arguments on stack.  |      |  arguments on stack.  |
+       |                       |       |                       |
+        +-----------------------+      +-----------------------+
+       |  4 words to save      |       |  4 words to save      |
+       |  arguments passed     |       |  arguments passed     |
+       |  in registers, even   |       |  in registers, even   |
+        |  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    |
+                                       +-----------------------+
+                                       |                       |\
+                                       |  local variables      | | var_size
+                                       |                       |/
+                                       +-----------------------+
+                                       |                       |
+                                       |  alloca allocations   |
+                                       |                       |
+                                       +-----------------------+
+                                      /|                       |
+                      cprestore_size | |  GP save for V.4 abi  |
+                                      \|                       |
+                                       +-----------------------+
+                                       |                       |\
+                                       |  arguments on stack   | |
+                                       |                       | |
+                                       +-----------------------+ |
+                                       |  4 words to save      | | args_size
+                                       |  arguments passed     | |
+                                       |  in registers, even   | |
+                                       |  if not passed.       | |
+   low                                 |  (TARGET_OLDABI only) |/
+   memory                          SP->+-----------------------+
 
-  /* 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]);
+HOST_WIDE_INT
+compute_frame_size (HOST_WIDE_INT size)
+{
+  unsigned int regno;
+  HOST_WIDE_INT total_size;    /* # bytes that the entire frame takes up */
+  HOST_WIDE_INT var_size;      /* # bytes that variables take up */
+  HOST_WIDE_INT args_size;     /* # bytes that outgoing arguments take up */
+  HOST_WIDE_INT cprestore_size; /* # bytes that the cprestore slot takes up */
+  HOST_WIDE_INT gp_reg_rounded;        /* # bytes needed to store gp after rounding */
+  HOST_WIDE_INT gp_reg_size;   /* # bytes needed to store gp regs */
+  HOST_WIDE_INT fp_reg_size;   /* # bytes needed to store fp regs */
+  unsigned int mask;           /* mask of saved gp registers */
+  unsigned int fmask;          /* mask of saved fp registers */
 
-  /* Emit the amount of stack space to allocate or deallocate.  */
-  s += sprintf (s, "%d", (int) info.size);
+  cfun->machine->global_pointer = mips_global_pointer ();
 
-  /* Save or restore $16.  */
-  if (BITSET_P (info.mask, 16))
-    s += sprintf (s, ",%s", reg_names[GP_REG_FIRST + 16]);
+  gp_reg_size = 0;
+  fp_reg_size = 0;
+  mask = 0;
+  fmask        = 0;
+  var_size = MIPS_STACK_ALIGN (size);
+  args_size = current_function_outgoing_args_size;
+  cprestore_size = MIPS_STACK_ALIGN (STARTING_FRAME_OFFSET) - args_size;
 
-  /* Save or restore $17.  */
-  if (BITSET_P (info.mask, 17))
-    s += sprintf (s, ",%s", reg_names[GP_REG_FIRST + 17]);
+  /* The space set aside by STARTING_FRAME_OFFSET isn't needed in leaf
+     functions.  If the function has local variables, we're committed
+     to allocating it anyway.  Otherwise reclaim it here.  */
+  if (var_size == 0 && current_function_is_leaf)
+    cprestore_size = args_size = 0;
 
-  /* 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]);
+  /* The MIPS 3.0 linker does not like functions that dynamically
+     allocate the stack and have 0 for STACK_DYNAMIC_OFFSET, since it
+     looks like we are trying to create a second frame pointer to the
+     function, so allocate some stack space to make it happy.  */
 
-  /* 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]);
+  if (args_size == 0 && current_function_calls_alloca)
+    args_size = 4 * UNITS_PER_WORD;
 
-  /* Save or restore $31.  */
-  if (BITSET_P (info.mask, 31))
-    s += sprintf (s, ",%s", reg_names[GP_REG_FIRST + 31]);
+  total_size = var_size + args_size + cprestore_size;
 
-  return buffer;
-}
+  /* Calculate space needed for gp registers.  */
+  for (regno = GP_REG_FIRST; regno <= GP_REG_LAST; regno++)
+    if (mips_save_reg_p (regno))
+      {
+       gp_reg_size += GET_MODE_SIZE (gpr_mode);
+       mask |= 1 << (regno - GP_REG_FIRST);
+      }
 
-/* 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.
+  /* We need to restore these for the handler.  */
+  if (current_function_calls_eh_return)
+    {
+      unsigned int i;
+      for (i = 0; ; ++i)
+       {
+         regno = EH_RETURN_DATA_REGNO (i);
+         if (regno == INVALID_REGNUM)
+           break;
+         gp_reg_size += GET_MODE_SIZE (gpr_mode);
+         mask |= 1 << (regno - GP_REG_FIRST);
+       }
+    }
 
-   This function is rather limited, but is good enough for our purposes.  */
+  /* 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);
+    }
 
-static rtx
-mips16e_collect_propagate_value (rtx x, rtx *reg_values)
-{
-  rtx x0, x1;
+  /* This loop must iterate over the same space as its companion in
+     mips_for_each_saved_reg.  */
+  if (TARGET_HARD_FLOAT)
+    for (regno = (FP_REG_LAST - MAX_FPRS_PER_FMT + 1);
+        regno >= FP_REG_FIRST;
+        regno -= MAX_FPRS_PER_FMT)
+      if (mips_save_reg_p (regno))
+       {
+         fp_reg_size += MAX_FPRS_PER_FMT * UNITS_PER_FPREG;
+         fmask |= ((1 << MAX_FPRS_PER_FMT) - 1) << (regno - FP_REG_FIRST);
+       }
 
-  x = avoid_constant_pool_reference (x);
+  gp_reg_rounded = MIPS_STACK_ALIGN (gp_reg_size);
+  total_size += gp_reg_rounded + MIPS_STACK_ALIGN (fp_reg_size);
 
-  if (UNARY_P (x))
+  /* Add in the space required for saving incoming register arguments.  */
+  total_size += current_function_pretend_args_size;
+  total_size += MIPS_STACK_ALIGN (cfun->machine->varargs_size);
+
+  /* Save other computed information.  */
+  cfun->machine->frame.total_size = total_size;
+  cfun->machine->frame.var_size = var_size;
+  cfun->machine->frame.args_size = args_size;
+  cfun->machine->frame.cprestore_size = cprestore_size;
+  cfun->machine->frame.gp_reg_size = gp_reg_size;
+  cfun->machine->frame.fp_reg_size = fp_reg_size;
+  cfun->machine->frame.mask = mask;
+  cfun->machine->frame.fmask = fmask;
+  cfun->machine->frame.initialized = reload_completed;
+  cfun->machine->frame.num_gp = gp_reg_size / UNITS_PER_WORD;
+  cfun->machine->frame.num_fp = (fp_reg_size
+                                / (MAX_FPRS_PER_FMT * UNITS_PER_FPREG));
+
+  if (mask)
     {
-      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)));
-    }
+      HOST_WIDE_INT offset;
 
-  if (ARITHMETIC_P (x))
+      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;
+    }
+  else
     {
-      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);
+      cfun->machine->frame.gp_sp_offset = 0;
+      cfun->machine->frame.gp_save_offset = 0;
     }
 
-  if (REG_P (x)
-      && reg_values[REGNO (x)]
-      && !rtx_unstable_p (reg_values[REGNO (x)]))
-    return reg_values[REGNO (x)];
+  if (fmask)
+    {
+      HOST_WIDE_INT offset;
 
-  return x;
+      offset = (args_size + cprestore_size + var_size
+               + gp_reg_rounded + fp_reg_size
+               - MAX_FPRS_PER_FMT * UNITS_PER_FPREG);
+      cfun->machine->frame.fp_sp_offset = offset;
+      cfun->machine->frame.fp_save_offset = offset - total_size;
+    }
+  else
+    {
+      cfun->machine->frame.fp_sp_offset = 0;
+      cfun->machine->frame.fp_save_offset = 0;
+    }
+
+  /* Ok, we're done.  */
+  return total_size;
 }
 
-/* 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.  */
+/* Return the style of GP load sequence that is being used for the
+   current function.  */
 
-static bool
-mips16e_collect_argument_save_p (rtx dest, rtx src, rtx *reg_values,
-                                unsigned int *regno_ptr)
+enum mips_loadgp_style
+mips_current_loadgp_style (void)
 {
-  unsigned int argno, regno;
-  HOST_WIDE_INT offset, required_offset;
-  rtx addr, base;
+  if (!TARGET_USE_GOT || cfun->machine->global_pointer == 0)
+    return LOADGP_NONE;
 
-  /* Check that this is a word-mode store.  */
-  if (!MEM_P (dest) || !REG_P (src) || GET_MODE (dest) != word_mode)
-    return false;
+  if (TARGET_RTP_PIC)
+    return LOADGP_RTP;
 
-  /* 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;
+  if (TARGET_ABSOLUTE_ABICALLS)
+    return LOADGP_ABSOLUTE;
 
-  /* 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;
+  return TARGET_NEWABI ? LOADGP_NEWABI : LOADGP_OLDABI;
+}
+\f
+/* Implement INITIAL_ELIMINATION_OFFSET.  FROM is either the frame
+   pointer or argument pointer.  TO is either the stack pointer or
+   hard frame pointer.  */
 
-  *regno_ptr = regno;
-  return true;
+HOST_WIDE_INT
+mips_initial_elimination_offset (int from, int to)
+{
+  HOST_WIDE_INT offset;
+
+  compute_frame_size (get_frame_size ());
+
+  /* Set OFFSET to the offset from the stack pointer.  */
+  switch (from)
+    {
+    case FRAME_POINTER_REGNUM:
+      offset = 0;
+      break;
+
+    case ARG_POINTER_REGNUM:
+      offset = (cfun->machine->frame.total_size
+               - current_function_pretend_args_size);
+      break;
+
+    default:
+      gcc_unreachable ();
+    }
+
+  if (TARGET_MIPS16 && to == HARD_FRAME_POINTER_REGNUM)
+    offset -= cfun->machine->frame.args_size;
+
+  return offset;
 }
+\f
+/* Implement TARGET_EXTRA_LIVE_ON_ENTRY.  Some code models use the incoming
+   value of PIC_FUNCTION_ADDR_REGNUM to set up the global pointer.  */
 
-/* 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 void
+mips_extra_live_on_entry (bitmap regs)
+{
+  if (TARGET_USE_GOT && !TARGET_ABSOLUTE_ABICALLS)
+    bitmap_set_bit (regs, PIC_FUNCTION_ADDR_REGNUM);
+}
 
-static unsigned int
-mips16e_collect_argument_saves (void)
+/* Implement RETURN_ADDR_RTX.  Note, we do not support moving
+   back to a previous frame.  */
+
+rtx
+mips_return_addr (int count, rtx frame ATTRIBUTE_UNUSED)
 {
-  rtx reg_values[FIRST_PSEUDO_REGISTER];
-  rtx insn, next, set, dest, src;
-  unsigned int nargs, regno;
+  if (count != 0)
+    return const0_rtx;
 
-  push_topmost_sequence ();
-  nargs = 0;
-  memset (reg_values, 0, sizeof (reg_values));
-  for (insn = get_insns (); insn; insn = next)
-    {
-      next = NEXT_INSN (insn);
-      if (NOTE_P (insn))
-       continue;
+  return get_hard_reg_initial_val (Pmode, GP_REG_FIRST + 31);
+}
 
-      if (!INSN_P (insn))
-       break;
+/* Emit code to change the current function's return address to
+   ADDRESS.  SCRATCH is available as a scratch register, if needed.
+   ADDRESS and SCRATCH are both word-mode GPRs.  */
 
-      set = PATTERN (insn);
-      if (GET_CODE (set) != SET)
-       break;
+void
+mips_set_return_address (rtx address, rtx scratch)
+{
+  rtx slot_address;
 
-      dest = SET_DEST (set);
-      src = SET_SRC (set);
-      if (mips16e_collect_argument_save_p (dest, src, reg_values, &regno))
-       {
-         if (!BITSET_P (cfun->machine->frame.mask, regno))
-           {
-             delete_insn (insn);
-             nargs = MAX (nargs, (regno - GP_ARG_FIRST) + 1);
-           }
-       }
-      else if (REG_P (dest) && GET_MODE (dest) == word_mode)
-       reg_values[REGNO (dest)]
-         = mips16e_collect_propagate_value (src, reg_values);
-      else
-       break;
-    }
-  pop_topmost_sequence ();
+  compute_frame_size (get_frame_size ());
+  gcc_assert ((cfun->machine->frame.mask >> 31) & 1);
+  slot_address = mips_add_offset (scratch, stack_pointer_rtx,
+                                 cfun->machine->frame.gp_sp_offset);
 
-  return nargs;
+  mips_emit_move (gen_rtx_MEM (GET_MODE (address), slot_address), address);
 }
 
-/* Expand the prologue into a bunch of separate insns.  */
+/* Restore $gp from its save slot.  Valid only when using o32 or
+   o64 abicalls.  */
 
 void
-mips_expand_prologue (void)
+mips_restore_gp (void)
 {
-  HOST_WIDE_INT size;
-  unsigned int nargs;
-  rtx insn;
+  rtx address, slot;
 
-  if (cfun->machine->global_pointer > 0)
-    SET_REGNO (pic_offset_table_rtx, cfun->machine->global_pointer);
+  gcc_assert (TARGET_ABICALLS && TARGET_OLDABI);
 
-  size = compute_frame_size (get_frame_size ());
+  address = mips_add_offset (pic_offset_table_rtx,
+                            frame_pointer_needed
+                            ? hard_frame_pointer_rtx
+                            : stack_pointer_rtx,
+                            current_function_outgoing_args_size);
+  slot = gen_rtx_MEM (Pmode, address);
 
-  /* Save the registers.  Allocate up to MIPS_MAX_FIRST_STACK_STEP
-     bytes beforehand; this is enough to cover the register save area
-     without going out of range.  */
-  if ((cfun->machine->frame.mask | cfun->machine->frame.fmask) != 0)
-    {
-      HOST_WIDE_INT step1;
+  mips_emit_move (pic_offset_table_rtx, slot);
+  if (!TARGET_EXPLICIT_RELOCS)
+    emit_insn (gen_blockage ());
+}
+\f
+/* A function to save or store a register.  The first argument is the
+   register and the second is the stack slot.  */
+typedef void (*mips_save_restore_fn) (rtx, rtx);
 
-      step1 = MIN (size, MIPS_MAX_FIRST_STACK_STEP);
+/* Use FN to save or restore register REGNO.  MODE is the register's
+   mode and OFFSET is the offset of its save slot from the current
+   stack pointer.  */
 
-      if (GENERATE_MIPS16E_SAVE_RESTORE)
-       {
-         HOST_WIDE_INT offset;
-         unsigned int mask, regno;
+static void
+mips_save_restore_reg (enum machine_mode mode, int regno,
+                      HOST_WIDE_INT offset, mips_save_restore_fn fn)
+{
+  rtx mem;
 
-         /* Try to merge argument stores into the save instruction.  */
-         nargs = mips16e_collect_argument_saves ();
+  mem = gen_frame_mem (mode, plus_constant (stack_pointer_rtx, offset));
 
-         /* 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;
+  fn (gen_rtx_REG (mode, regno), mem);
+}
 
-         /* 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.  */
-  if (size > 0)
-    {
-      if (SMALL_OPERAND (-size))
-       RTX_FRAME_RELATED_P (emit_insn (gen_add3_insn (stack_pointer_rtx,
-                                                      stack_pointer_rtx,
-                                                      GEN_INT (-size)))) = 1;
-      else
-       {
-         mips_emit_move (MIPS_PROLOGUE_TEMP (Pmode), GEN_INT (size));
-         if (TARGET_MIPS16)
-           {
-             /* There are no instructions to add or subtract registers
-                from the stack pointer, so use the frame pointer as a
-                temporary.  We should always be using a frame pointer
-                in this case anyway.  */
-             gcc_assert (frame_pointer_needed);
-             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)));
-             mips_emit_move (stack_pointer_rtx, hard_frame_pointer_rtx);
-           }
-         else
-           emit_insn (gen_sub3_insn (stack_pointer_rtx,
-                                     stack_pointer_rtx,
-                                     MIPS_PROLOGUE_TEMP (Pmode)));
+/* Call FN for each register that is saved by the current function.
+   SP_OFFSET is the offset of the current stack pointer from the start
+   of the frame.  */
 
-         /* Describe the combined effect of the previous instructions.  */
-         mips_set_frame_expr
-           (gen_rtx_SET (VOIDmode, stack_pointer_rtx,
-                         plus_constant (stack_pointer_rtx, -size)));
-       }
-    }
+static void
+mips_for_each_saved_reg (HOST_WIDE_INT sp_offset, mips_save_restore_fn fn)
+{
+  enum machine_mode fpr_mode;
+  HOST_WIDE_INT offset;
+  int regno;
 
-  /* Set up the frame pointer, if we're using one.  In mips16 code,
-     we point the frame pointer ahead of the outgoing argument area.
-     This should allow more variables & incoming arguments to be
-     accessed with unextended instructions.  */
-  if (frame_pointer_needed)
-    {
-      if (TARGET_MIPS16 && cfun->machine->frame.args_size != 0)
-       {
-         rtx offset = GEN_INT (cfun->machine->frame.args_size);
-         if (SMALL_OPERAND (cfun->machine->frame.args_size))
-           RTX_FRAME_RELATED_P
-             (emit_insn (gen_add3_insn (hard_frame_pointer_rtx,
-                                        stack_pointer_rtx,
-                                        offset))) = 1;
-         else
-           {
-             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)));
-             mips_set_frame_expr
-               (gen_rtx_SET (VOIDmode, hard_frame_pointer_rtx,
-                             plus_constant (stack_pointer_rtx,
-                                            cfun->machine->frame.args_size)));
-           }
+  /* Save registers starting from high to low.  The debuggers prefer at least
+     the return register be stored at func+4, and also it allows us not to
+     need a nop in the epilogue if at least one register is reloaded in
+     addition to return address.  */
+  offset = cfun->machine->frame.gp_sp_offset - sp_offset;
+  for (regno = GP_REG_LAST; regno >= GP_REG_FIRST; regno--)
+    if (BITSET_P (cfun->machine->frame.mask, regno - GP_REG_FIRST))
+      {
+       mips_save_restore_reg (gpr_mode, regno, offset, fn);
+       offset -= GET_MODE_SIZE (gpr_mode);
+      }
+
+  /* This loop must iterate over the same space as its companion in
+     compute_frame_size.  */
+  offset = cfun->machine->frame.fp_sp_offset - sp_offset;
+  fpr_mode = (TARGET_SINGLE_FLOAT ? SFmode : DFmode);
+  for (regno = (FP_REG_LAST - MAX_FPRS_PER_FMT + 1);
+       regno >= FP_REG_FIRST;
+       regno -= MAX_FPRS_PER_FMT)
+    if (BITSET_P (cfun->machine->frame.fmask, regno - FP_REG_FIRST))
+      {
+       mips_save_restore_reg (fpr_mode, regno, offset, fn);
+       offset -= GET_MODE_SIZE (fpr_mode);
+      }
+}
+\f
+/* If we're generating n32 or n64 abicalls, and the current function
+   does not use $28 as its global pointer, emit a cplocal directive.
+   Use pic_offset_table_rtx as the argument to the directive.  */
+
+static void
+mips_output_cplocal (void)
+{
+  if (!TARGET_EXPLICIT_RELOCS
+      && cfun->machine->global_pointer > 0
+      && cfun->machine->global_pointer != GLOBAL_POINTER_REGNUM)
+    output_asm_insn (".cplocal %+", 0);
+}
+
+/* Set up the stack and frame (if desired) for the function.  */
+
+static void
+mips_output_function_prologue (FILE *file, HOST_WIDE_INT size ATTRIBUTE_UNUSED)
+{
+  const char *fnname;
+  HOST_WIDE_INT tsize = cfun->machine->frame.total_size;
+
+#ifdef SDB_DEBUGGING_INFO
+  if (debug_info_level != DINFO_LEVEL_TERSE && write_symbols == SDB_DEBUG)
+    SDB_OUTPUT_SOURCE_LINE (file, DECL_SOURCE_LINE (current_function_decl));
+#endif
+
+  /* In mips16 mode, we may need to generate a 32 bit to handle
+     floating point arguments.  The linker will arrange for any 32-bit
+     functions to call this stub, which will then jump to the 16-bit
+     function proper.  */
+  if (TARGET_MIPS16
+      && TARGET_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
+        assemble_start_function.  This is needed so that the name used here
+        exactly matches the name used in ASM_DECLARE_FUNCTION_NAME.  */
+      fnname = XSTR (XEXP (DECL_RTL (current_function_decl), 0), 0);
+
+      if (!flag_inhibit_size_directive)
+       {
+         fputs ("\t.ent\t", file);
+         assemble_name (file, fnname);
+         fputs ("\n", file);
        }
-      else
-       RTX_FRAME_RELATED_P (mips_emit_move (hard_frame_pointer_rtx,
-                                            stack_pointer_rtx)) = 1;
+
+      assemble_name (file, fnname);
+      fputs (":\n", file);
     }
 
-  mips_emit_loadgp ();
+  /* Stop mips_file_end from treating this function as external.  */
+  if (TARGET_IRIX && mips_abi == ABI_32)
+    TREE_ASM_WRITTEN (DECL_NAME (cfun->decl)) = 1;
 
-  /* If generating o32/o64 abicalls, save $gp on the stack.  */
-  if (TARGET_ABICALLS && TARGET_OLDABI && !current_function_is_leaf)
-    emit_insn (gen_cprestore (GEN_INT (current_function_outgoing_args_size)));
+  if (!flag_inhibit_size_directive)
+    {
+      /* .frame FRAMEREG, FRAMESIZE, RETREG */
+      fprintf (file,
+              "\t.frame\t%s," HOST_WIDE_INT_PRINT_DEC ",%s\t\t"
+              "# vars= " HOST_WIDE_INT_PRINT_DEC ", regs= %d/%d"
+              ", args= " HOST_WIDE_INT_PRINT_DEC
+              ", gp= " HOST_WIDE_INT_PRINT_DEC "\n",
+              (reg_names[(frame_pointer_needed)
+                         ? HARD_FRAME_POINTER_REGNUM : STACK_POINTER_REGNUM]),
+              ((frame_pointer_needed && TARGET_MIPS16)
+               ? tsize - cfun->machine->frame.args_size
+               : tsize),
+              reg_names[GP_REG_FIRST + 31],
+              cfun->machine->frame.var_size,
+              cfun->machine->frame.num_gp,
+              cfun->machine->frame.num_fp,
+              cfun->machine->frame.args_size,
+              cfun->machine->frame.cprestore_size);
 
-  /* If we are profiling, make sure no instructions are scheduled before
-     the call to mcount.  */
+      /* .mask MASK, GPOFFSET; .fmask FPOFFSET */
+      fprintf (file, "\t.mask\t0x%08x," HOST_WIDE_INT_PRINT_DEC "\n",
+              cfun->machine->frame.mask,
+              cfun->machine->frame.gp_save_offset);
+      fprintf (file, "\t.fmask\t0x%08x," HOST_WIDE_INT_PRINT_DEC "\n",
+              cfun->machine->frame.fmask,
+              cfun->machine->frame.fp_save_offset);
 
-  if (current_function_profile)
-    emit_insn (gen_blockage ());
+      /* Require:
+        OLD_SP == *FRAMEREG + FRAMESIZE => can find old_sp from nominated FP reg.
+        HIGHEST_GP_SAVED == *FRAMEREG + FRAMESIZE + GPOFFSET => can find saved regs.  */
+    }
+
+  if (mips_current_loadgp_style () == LOADGP_OLDABI)
+    {
+      /* Handle the initialization of $gp for SVR4 PIC.  */
+      if (!cfun->machine->all_noreorder_p)
+       output_asm_insn ("%(.cpload\t%^%)", 0);
+      else
+       output_asm_insn ("%(.cpload\t%^\n\t%<", 0);
+    }
+  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 ();
 }
-\f
+
 /* Do any necessary cleanup after a function to restore stack, frame,
    and regs.  */
 
@@ -8621,61 +8391,280 @@ mips_output_function_epilogue (FILE *file ATTRIBUTE_UNUSED,
     }
 }
 \f
-/* Emit instructions to restore register REG from slot MEM.  */
+/* Save register REG to MEM.  Make the instruction frame-related.  */
 
 static void
-mips_restore_reg (rtx reg, rtx mem)
+mips_save_reg (rtx reg, rtx mem)
 {
-  /* There's no mips16 instruction to load $31 directly.  Load into
-     $7 instead and adjust the return insn appropriately.  */
-  if (TARGET_MIPS16 && REGNO (reg) == GP_REG_FIRST + 31)
-    reg = gen_rtx_REG (GET_MODE (reg), 7);
-
-  if (TARGET_MIPS16 && !M16_REG_P (REGNO (reg)))
+  if (GET_MODE (reg) == DFmode && !TARGET_FLOAT64)
     {
-      /* Can't restore directly; move through a temporary.  */
-      mips_emit_move (MIPS_EPILOGUE_TEMP (GET_MODE (reg)), mem);
-      mips_emit_move (reg, MIPS_EPILOGUE_TEMP (GET_MODE (reg)));
+      rtx x1, x2;
+
+      if (mips_split_64bit_move_p (mem, reg))
+       mips_split_doubleword_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
-    mips_emit_move (reg, mem);
+    {
+      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));
+    }
 }
 
+/* The __gnu_local_gp symbol.  */
 
-/* Expand the epilogue into a bunch of separate insns.  SIBCALL_P is true
-   if this epilogue precedes a sibling call, false if it is for a normal
-   "epilogue" pattern.  */
+static GTY(()) rtx mips_gnu_local_gp;
 
-void
-mips_expand_epilogue (int sibcall_p)
-{
-  HOST_WIDE_INT step1, step2;
-  rtx base, target;
+/* If we're generating n32 or n64 abicalls, emit instructions
+   to set up the global pointer.  */
 
-  if (!sibcall_p && mips_can_use_return_insn ())
-    {
-      emit_jump_insn (gen_return ());
-      return;
-    }
+static void
+mips_emit_loadgp (void)
+{
+  rtx addr, offset, incoming_address, base, index;
 
-  /* In mips16 mode, if the return value should go into a floating-point
-     register, we need to call a helper routine to copy it over.  */
-  if (mips16_cfun_returns_in_fpr_p ())
+  switch (mips_current_loadgp_style ())
     {
-      char *name;
-      rtx func;
-      rtx insn;
-      rtx retval;
-      rtx call;
-      tree id;
-      tree return_type;
-      enum machine_mode return_mode;
+    case LOADGP_ABSOLUTE:
+      if (mips_gnu_local_gp == NULL)
+       {
+         mips_gnu_local_gp = gen_rtx_SYMBOL_REF (Pmode, "__gnu_local_gp");
+         SYMBOL_REF_FLAGS (mips_gnu_local_gp) |= SYMBOL_FLAG_LOCAL;
+       }
+      emit_insn (gen_loadgp_absolute (mips_gnu_local_gp));
+      break;
 
-      return_type = DECL_RESULT (current_function_decl);
-      return_mode = DECL_MODE (return_type);
+    case LOADGP_NEWABI:
+      addr = XEXP (DECL_RTL (current_function_decl), 0);
+      offset = mips_unspec_address (addr, SYMBOL_GOTOFF_LOADGP);
+      incoming_address = gen_rtx_REG (Pmode, PIC_FUNCTION_ADDR_REGNUM);
+      emit_insn (gen_loadgp_newabi (offset, incoming_address));
+      if (!TARGET_EXPLICIT_RELOCS)
+       emit_insn (gen_loadgp_blockage ());
+      break;
 
-      name = ACONCAT (("__mips16_ret_",
-                      mips16_call_stub_mode_suffix (return_mode),
+    case LOADGP_RTP:
+      base = gen_rtx_SYMBOL_REF (Pmode, ggc_strdup (VXWORKS_GOTT_BASE));
+      index = gen_rtx_SYMBOL_REF (Pmode, ggc_strdup (VXWORKS_GOTT_INDEX));
+      emit_insn (gen_loadgp_rtp (base, index));
+      if (!TARGET_EXPLICIT_RELOCS)
+       emit_insn (gen_loadgp_blockage ());
+      break;
+
+    default:
+      break;
+    }
+}
+
+/* Expand the prologue into a bunch of separate insns.  */
+
+void
+mips_expand_prologue (void)
+{
+  HOST_WIDE_INT size;
+  unsigned int nargs;
+  rtx insn;
+
+  if (cfun->machine->global_pointer > 0)
+    SET_REGNO (pic_offset_table_rtx, cfun->machine->global_pointer);
+
+  size = compute_frame_size (get_frame_size ());
+
+  /* Save the registers.  Allocate up to MIPS_MAX_FIRST_STACK_STEP
+     bytes beforehand; this is enough to cover the register save area
+     without going out of range.  */
+  if ((cfun->machine->frame.mask | cfun->machine->frame.fmask) != 0)
+    {
+      HOST_WIDE_INT step1;
+
+      step1 = MIN (size, MIPS_MAX_FIRST_STACK_STEP);
+
+      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.  */
+  if (size > 0)
+    {
+      if (SMALL_OPERAND (-size))
+       RTX_FRAME_RELATED_P (emit_insn (gen_add3_insn (stack_pointer_rtx,
+                                                      stack_pointer_rtx,
+                                                      GEN_INT (-size)))) = 1;
+      else
+       {
+         mips_emit_move (MIPS_PROLOGUE_TEMP (Pmode), GEN_INT (size));
+         if (TARGET_MIPS16)
+           {
+             /* There are no instructions to add or subtract registers
+                from the stack pointer, so use the frame pointer as a
+                temporary.  We should always be using a frame pointer
+                in this case anyway.  */
+             gcc_assert (frame_pointer_needed);
+             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)));
+             mips_emit_move (stack_pointer_rtx, hard_frame_pointer_rtx);
+           }
+         else
+           emit_insn (gen_sub3_insn (stack_pointer_rtx,
+                                     stack_pointer_rtx,
+                                     MIPS_PROLOGUE_TEMP (Pmode)));
+
+         /* Describe the combined effect of the previous instructions.  */
+         mips_set_frame_expr
+           (gen_rtx_SET (VOIDmode, stack_pointer_rtx,
+                         plus_constant (stack_pointer_rtx, -size)));
+       }
+    }
+
+  /* Set up the frame pointer, if we're using one.  In mips16 code,
+     we point the frame pointer ahead of the outgoing argument area.
+     This should allow more variables & incoming arguments to be
+     accessed with unextended instructions.  */
+  if (frame_pointer_needed)
+    {
+      if (TARGET_MIPS16 && cfun->machine->frame.args_size != 0)
+       {
+         rtx offset = GEN_INT (cfun->machine->frame.args_size);
+         if (SMALL_OPERAND (cfun->machine->frame.args_size))
+           RTX_FRAME_RELATED_P
+             (emit_insn (gen_add3_insn (hard_frame_pointer_rtx,
+                                        stack_pointer_rtx,
+                                        offset))) = 1;
+         else
+           {
+             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)));
+             mips_set_frame_expr
+               (gen_rtx_SET (VOIDmode, hard_frame_pointer_rtx,
+                             plus_constant (stack_pointer_rtx,
+                                            cfun->machine->frame.args_size)));
+           }
+       }
+      else
+       RTX_FRAME_RELATED_P (mips_emit_move (hard_frame_pointer_rtx,
+                                            stack_pointer_rtx)) = 1;
+    }
+
+  mips_emit_loadgp ();
+
+  /* If generating o32/o64 abicalls, save $gp on the stack.  */
+  if (TARGET_ABICALLS && TARGET_OLDABI && !current_function_is_leaf)
+    emit_insn (gen_cprestore (GEN_INT (current_function_outgoing_args_size)));
+
+  /* If we are profiling, make sure no instructions are scheduled before
+     the call to mcount.  */
+
+  if (current_function_profile)
+    emit_insn (gen_blockage ());
+}
+\f
+/* Emit instructions to restore register REG from slot MEM.  */
+
+static void
+mips_restore_reg (rtx reg, rtx mem)
+{
+  /* There's no mips16 instruction to load $31 directly.  Load into
+     $7 instead and adjust the return insn appropriately.  */
+  if (TARGET_MIPS16 && REGNO (reg) == GP_REG_FIRST + 31)
+    reg = gen_rtx_REG (GET_MODE (reg), 7);
+
+  if (TARGET_MIPS16 && !M16_REG_P (REGNO (reg)))
+    {
+      /* Can't restore directly; move through a temporary.  */
+      mips_emit_move (MIPS_EPILOGUE_TEMP (GET_MODE (reg)), mem);
+      mips_emit_move (reg, MIPS_EPILOGUE_TEMP (GET_MODE (reg)));
+    }
+  else
+    mips_emit_move (reg, mem);
+}
+
+
+/* Expand the epilogue into a bunch of separate insns.  SIBCALL_P is true
+   if this epilogue precedes a sibling call, false if it is for a normal
+   "epilogue" pattern.  */
+
+void
+mips_expand_epilogue (int sibcall_p)
+{
+  HOST_WIDE_INT step1, step2;
+  rtx base, target;
+
+  if (!sibcall_p && mips_can_use_return_insn ())
+    {
+      emit_jump_insn (gen_return ());
+      return;
+    }
+
+  /* In mips16 mode, if the return value should go into a floating-point
+     register, we need to call a helper routine to copy it over.  */
+  if (mips16_cfun_returns_in_fpr_p ())
+    {
+      char *name;
+      rtx func;
+      rtx insn;
+      rtx retval;
+      rtx call;
+      tree id;
+      tree return_type;
+      enum machine_mode return_mode;
+
+      return_type = DECL_RESULT (current_function_decl);
+      return_mode = DECL_MODE (return_type);
+
+      name = ACONCAT (("__mips16_ret_",
+                      mips16_call_stub_mode_suffix (return_mode),
                       NULL));
       id = get_identifier (name);
       func = gen_rtx_SYMBOL_REF (Pmode, IDENTIFIER_POINTER (id));
@@ -8836,4081 +8825,3933 @@ mips_can_use_return_insn (void)
   return compute_frame_size (get_frame_size ()) == 0;
 }
 \f
-/* Implement TARGET_ASM_OUTPUT_MI_THUNK.  Generate rtl rather than asm text
-   in order to avoid duplicating too much logic from elsewhere.  */
+/* Implement HARD_REGNO_NREGS.  The size of FP registers is controlled
+   by UNITS_PER_FPREG.  The size of FP status registers is always 4, because
+   they only hold condition code modes, and CCmode is always considered to
+   be 4 bytes wide.  All other registers are word sized.  */
 
-static void
-mips_output_mi_thunk (FILE *file, tree thunk_fndecl ATTRIBUTE_UNUSED,
-                     HOST_WIDE_INT delta, HOST_WIDE_INT vcall_offset,
-                     tree function)
+unsigned int
+mips_hard_regno_nregs (int regno, enum machine_mode mode)
 {
-  rtx this, temp1, temp2, insn, fnaddr;
-  bool use_sibcall_p;
+  if (ST_REG_P (regno))
+    return ((GET_MODE_SIZE (mode) + 3) / 4);
+  else if (! FP_REG_P (regno))
+    return ((GET_MODE_SIZE (mode) + UNITS_PER_WORD - 1) / UNITS_PER_WORD);
+  else
+    return ((GET_MODE_SIZE (mode) + UNITS_PER_FPREG - 1) / UNITS_PER_FPREG);
+}
 
-  /* Pretend to be a post-reload pass while generating rtl.  */
-  reload_completed = 1;
+/* Implement CLASS_MAX_NREGS.
 
-  /* Mark the end of the (empty) prologue.  */
-  emit_note (NOTE_INSN_PROLOGUE_END);
+   - UNITS_PER_FPREG controls the number of registers needed by FP_REGS.
 
-  /* Determine if we can use a sibcall to call FUNCTION directly.  */
-  fnaddr = XEXP (DECL_RTL (function), 0);
-  use_sibcall_p = (mips_function_ok_for_sibcall (function, NULL)
-                  && const_call_insn_operand (fnaddr, Pmode));
+   - ST_REGS are always hold CCmode values, and CCmode values are
+     considered to be 4 bytes wide.
 
-  /* Determine if we need to load FNADDR from the GOT.  */
-  if (!use_sibcall_p)
-    switch (mips_classify_symbol (fnaddr, SYMBOL_CONTEXT_LEA))
-      {
-      case SYMBOL_GOT_PAGE_OFST:
-      case SYMBOL_GOT_DISP:
-       /* Pick a global pointer.  Use a call-clobbered register if
-          TARGET_CALL_SAVED_GP.  */
-       cfun->machine->global_pointer =
-         TARGET_CALL_SAVED_GP ? 15 : GLOBAL_POINTER_REGNUM;
-       SET_REGNO (pic_offset_table_rtx, cfun->machine->global_pointer);
+   All other register classes are covered by UNITS_PER_WORD.  Note that
+   this is true even for unions of integer and float registers when the
+   latter are smaller than the former.  The only supported combination
+   in which case this occurs is -mgp64 -msingle-float, which has 64-bit
+   words but 32-bit float registers.  A word-based calculation is correct
+   in that case since -msingle-float disallows multi-FPR values.  */
 
-       /* Set up the global pointer for n32 or n64 abicalls.  */
-       mips_emit_loadgp ();
-       break;
+int
+mips_class_max_nregs (enum reg_class class ATTRIBUTE_UNUSED,
+                     enum machine_mode mode)
+{
+  if (class == ST_REGS)
+    return (GET_MODE_SIZE (mode) + 3) / 4;
+  else if (class == FP_REGS)
+    return (GET_MODE_SIZE (mode) + UNITS_PER_FPREG - 1) / UNITS_PER_FPREG;
+  else
+    return (GET_MODE_SIZE (mode) + UNITS_PER_WORD - 1) / UNITS_PER_WORD;
+}
 
-      default:
-       break;
-      }
+/* Return true if registers of class CLASS cannot change from mode FROM
+   to mode TO.  */
 
-  /* We need two temporary registers in some cases.  */
-  temp1 = gen_rtx_REG (Pmode, 2);
-  temp2 = gen_rtx_REG (Pmode, 3);
+bool
+mips_cannot_change_mode_class (enum machine_mode from ATTRIBUTE_UNUSED,
+                              enum machine_mode to ATTRIBUTE_UNUSED,
+                              enum reg_class class)
+{
+  /* There are several problems with changing the modes of values
+     in floating-point registers:
 
-  /* Find out which register contains the "this" pointer.  */
-  if (aggregate_value_p (TREE_TYPE (TREE_TYPE (function)), function))
-    this = gen_rtx_REG (Pmode, GP_ARG_FIRST + 1);
-  else
-    this = gen_rtx_REG (Pmode, GP_ARG_FIRST);
+     - When a multi-word value is stored in paired floating-point
+       registers, the first register always holds the low word.
+       We therefore can't allow FPRs to change between single-word
+       and multi-word modes on big-endian targets.
 
-  /* Add DELTA to THIS.  */
-  if (delta != 0)
-    {
-      rtx offset = GEN_INT (delta);
-      if (!SMALL_OPERAND (delta))
-       {
-         mips_emit_move (temp1, offset);
-         offset = temp1;
-       }
-      emit_insn (gen_add3_insn (this, this, offset));
-    }
+     - GCC assumes that each word of a multiword register can be accessed
+       individually using SUBREGs.  This is not true for floating-point
+       registers if they are bigger than a word.
 
-  /* If needed, add *(*THIS + VCALL_OFFSET) to THIS.  */
-  if (vcall_offset != 0)
-    {
-      rtx addr;
+     - Loading a 32-bit value into a 64-bit floating-point register
+       will not sign-extend the value, despite what LOAD_EXTEND_OP says.
+       We can't allow FPRs to change from SImode to to a wider mode on
+       64-bit targets.
 
-      /* Set TEMP1 to *THIS.  */
-      mips_emit_move (temp1, gen_rtx_MEM (Pmode, this));
+     - If the FPU has already interpreted a value in one format, we must
+       not ask it to treat the value as having a different format.
 
-      /* Set ADDR to a legitimate address for *THIS + VCALL_OFFSET.  */
-      addr = mips_add_offset (temp2, temp1, vcall_offset);
+     We therefore only allow changes between 4-byte and smaller integer
+     values, all of which have the "W" format as far as the FPU is
+     concerned.  */
+  return (reg_classes_intersect_p (FP_REGS, class)
+         && (GET_MODE_CLASS (from) != MODE_INT
+             || GET_MODE_CLASS (to) != MODE_INT
+             || GET_MODE_SIZE (from) > 4
+             || GET_MODE_SIZE (to) > 4));
+}
 
-      /* Load the offset and add it to THIS.  */
-      mips_emit_move (temp1, gen_rtx_MEM (Pmode, addr));
-      emit_insn (gen_add3_insn (this, this, temp1));
-    }
+/* Return true if moves in mode MODE can use the FPU's mov.fmt instruction.  */
 
-  /* Jump to the target function.  Use a sibcall if direct jumps are
-     allowed, otherwise load the address into a register first.  */
-  if (use_sibcall_p)
-    {
-      insn = emit_call_insn (gen_sibcall_internal (fnaddr, const0_rtx));
-      SIBLING_CALL_P (insn) = 1;
-    }
-  else
+static bool
+mips_mode_ok_for_mov_fmt_p (enum machine_mode mode)
+{
+  switch (mode)
     {
-      /* This is messy.  gas treats "la $25,foo" as part of a call
-        sequence and may allow a global "foo" to be lazily bound.
-        The general move patterns therefore reject this combination.
+    case SFmode:
+      return TARGET_HARD_FLOAT;
 
-        In this context, lazy binding would actually be OK
-        for TARGET_CALL_CLOBBERED_GP, but it's still wrong for
-        TARGET_CALL_SAVED_GP; see mips_load_call_address.
-        We must therefore load the address via a temporary
-        register if mips_dangerous_for_la25_p.
+    case DFmode:
+      return TARGET_HARD_FLOAT && TARGET_DOUBLE_FLOAT;
 
-        If we jump to the temporary register rather than $25, the assembler
-        can use the move insn to fill the jump's delay slot.  */
-      if (TARGET_USE_PIC_FN_ADDR_REG
-         && !mips_dangerous_for_la25_p (fnaddr))
-       temp1 = gen_rtx_REG (Pmode, PIC_FUNCTION_ADDR_REGNUM);
-      mips_load_call_address (temp1, fnaddr, true);
+    case V2SFmode:
+      return TARGET_HARD_FLOAT && TARGET_PAIRED_SINGLE_FLOAT;
 
-      if (TARGET_USE_PIC_FN_ADDR_REG
-         && REGNO (temp1) != PIC_FUNCTION_ADDR_REGNUM)
-       mips_emit_move (gen_rtx_REG (Pmode, PIC_FUNCTION_ADDR_REGNUM), temp1);
-      emit_jump_insn (gen_indirect_jump (temp1));
+    default:
+      return false;
     }
-
-  /* Run just enough of rest_of_compilation.  This sequence was
-     "borrowed" from alpha.c.  */
-  insn = get_insns ();
-  insn_locators_alloc ();
-  split_all_insns_noflow ();
-  mips16_lay_out_constants ();
-  shorten_branches (insn);
-  final_start_function (insn, file, 1);
-  final (insn, file, 1);
-  final_end_function ();
-
-  /* Clean up the vars set above.  Note that final_end_function resets
-     the global pointer for us.  */
-  reload_completed = 0;
 }
-\f
-/* Implement TARGET_SELECT_RTX_SECTION.  */
 
-static section *
-mips_select_rtx_section (enum machine_mode mode, rtx x,
-                        unsigned HOST_WIDE_INT align)
-{
-  /* ??? Consider using mergeable small data sections.  */
-  if (mips_rtx_constant_in_small_data_p (mode))
-    return get_named_section (NULL, ".sdata", 0);
+/* Implement PREFERRED_RELOAD_CLASS.  */
 
-  return default_elf_select_rtx_section (mode, x, align);
-}
+enum reg_class
+mips_preferred_reload_class (rtx x, enum reg_class class)
+{
+  if (mips_dangerous_for_la25_p (x) && reg_class_subset_p (LEA_REGS, class))
+    return LEA_REGS;
 
-/* Implement TARGET_ASM_FUNCTION_RODATA_SECTION.
+  if (reg_class_subset_p (FP_REGS, class)
+      && mips_mode_ok_for_mov_fmt_p (GET_MODE (x)))
+    return FP_REGS;
 
-   The complication here is that, with the combination TARGET_ABICALLS
-   && !TARGET_GPWORD, jump tables will use absolute addresses, and should
-   therefore not be included in the read-only part of a DSO.  Handle such
-   cases by selecting a normal data section instead of a read-only one.
-   The logic apes that in default_function_rodata_section.  */
+  if (reg_class_subset_p (GR_REGS, class))
+    class = GR_REGS;
 
-static section *
-mips_function_rodata_section (tree decl)
-{
-  if (!TARGET_ABICALLS || TARGET_GPWORD)
-    return default_function_rodata_section (decl);
+  if (TARGET_MIPS16 && reg_class_subset_p (M16_REGS, class))
+    class = M16_REGS;
 
-  if (decl && DECL_SECTION_NAME (decl))
-    {
-      const char *name = TREE_STRING_POINTER (DECL_SECTION_NAME (decl));
-      if (DECL_ONE_ONLY (decl) && strncmp (name, ".gnu.linkonce.t.", 16) == 0)
-       {
-         char *rname = ASTRDUP (name);
-         rname[14] = 'd';
-         return get_section (rname, SECTION_LINKONCE | SECTION_WRITE, decl);
-       }
-      else if (flag_function_sections && flag_data_sections
-              && strncmp (name, ".text.", 6) == 0)
-       {
-         char *rname = ASTRDUP (name);
-         memcpy (rname + 1, "data", 4);
-         return get_section (rname, SECTION_WRITE, decl);
-       }
-    }
-  return data_section;
+  return class;
 }
 
-/* Implement TARGET_IN_SMALL_DATA_P.  This function controls whether
-   locally-defined objects go in a small data section.  It also controls
-   the setting of the SYMBOL_REF_SMALL_P flag, which in turn helps
-   mips_classify_symbol decide when to use %gp_rel(...)($gp) accesses.  */
+/* Return a number assessing the cost of moving a register in class
+   FROM to class TO.  The classes are expressed using the enumeration
+   values such as `GENERAL_REGS'.  A value of 2 is the default; other
+   values are interpreted relative to that.
 
-static bool
-mips_in_small_data_p (const_tree decl)
-{
-  HOST_WIDE_INT size;
+   It is not required that the cost always equal 2 when FROM is the
+   same as TO; on some machines it is expensive to move between
+   registers if they are not general registers.
 
-  if (TREE_CODE (decl) == STRING_CST || TREE_CODE (decl) == FUNCTION_DECL)
-    return false;
+   If reload sees an insn consisting of a single `set' between two
+   hard registers, and if `REGISTER_MOVE_COST' applied to their
+   classes returns a value of 2, reload does not check to ensure that
+   the constraints of the insn are met.  Setting a cost of other than
+   2 will allow reload to verify that the constraints are met.  You
+   should do this if the `movM' pattern's constraints do not allow
+   such copying.
 
-  /* We don't yet generate small-data references for -mabicalls or
-     VxWorks RTP code.  See the related -G handling in override_options.  */
-  if (TARGET_ABICALLS || TARGET_VXWORKS_RTP)
-    return false;
+   ??? We make the cost of moving from HI/LO into general
+   registers the same as for one of moving general registers to
+   HI/LO for TARGET_MIPS16 in order to prevent allocating a
+   pseudo to HI/LO.  This might hurt optimizations though, it
+   isn't clear if it is wise.  And it might not work in all cases.  We
+   could solve the DImode LO reg problem by using a multiply, just
+   like reload_{in,out}si.  We could solve the SImode/HImode HI reg
+   problem by using divide instructions.  divu puts the remainder in
+   the HI reg, so doing a divide by -1 will move the value in the HI
+   reg for all values except -1.  We could handle that case by using a
+   signed divide, e.g.  -1 / 2 (or maybe 1 / -2?).  We'd have to emit
+   a compare/branch to test the input value to see which instruction
+   we need to use.  This gets pretty messy, but it is feasible.  */
 
-  if (TREE_CODE (decl) == VAR_DECL && DECL_SECTION_NAME (decl) != 0)
+int
+mips_register_move_cost (enum machine_mode mode,
+                        enum reg_class to, enum reg_class from)
+{
+  if (TARGET_MIPS16)
     {
-      const char *name;
-
-      /* Reject anything that isn't in a known small-data section.  */
-      name = TREE_STRING_POINTER (DECL_SECTION_NAME (decl));
-      if (strcmp (name, ".sdata") != 0 && strcmp (name, ".sbss") != 0)
-       return false;
-
-      /* If a symbol is defined externally, the assembler will use the
-        usual -G rules when deciding how to implement macros.  */
-      if (mips_lo_relocs[SYMBOL_GP_RELATIVE] || !DECL_EXTERNAL (decl))
-       return true;
+      if (reg_class_subset_p (from, GENERAL_REGS)
+         && reg_class_subset_p (to, GENERAL_REGS))
+       {
+         if (reg_class_subset_p (from, M16_REGS)
+             || reg_class_subset_p (to, M16_REGS))
+           return 2;
+         /* Two MOVEs.  */
+         return 4;
+       }
     }
-  else if (TARGET_EMBEDDED_DATA)
+  else if (reg_class_subset_p (from, GENERAL_REGS))
     {
-      /* Don't put constants into the small data section: we want them
-        to be in ROM rather than RAM.  */
-      if (TREE_CODE (decl) != VAR_DECL)
-       return false;
-
-      if (TREE_READONLY (decl)
-         && !TREE_SIDE_EFFECTS (decl)
-         && (!DECL_INITIAL (decl) || TREE_CONSTANT (DECL_INITIAL (decl))))
-       return false;
+      if (reg_class_subset_p (to, GENERAL_REGS))
+       return 2;
+      if (reg_class_subset_p (to, FP_REGS))
+       return 4;
+      if (reg_class_subset_p (to, ALL_COP_AND_GR_REGS))
+       return 5;
+      if (reg_class_subset_p (to, ACC_REGS))
+       return 6;
     }
-
-  /* Enforce -mlocal-sdata.  */
-  if (!TARGET_LOCAL_SDATA && !TREE_PUBLIC (decl))
-    return false;
-
-  /* Enforce -mextern-sdata.  */
-  if (!TARGET_EXTERN_SDATA && DECL_P (decl))
+  else if (reg_class_subset_p (to, GENERAL_REGS))
     {
-      if (DECL_EXTERNAL (decl))
-       return false;
-      if (DECL_COMMON (decl) && DECL_INITIAL (decl) == NULL)
-       return false;
+      if (reg_class_subset_p (from, FP_REGS))
+       return 4;
+      if (reg_class_subset_p (from, ST_REGS))
+       /* LUI followed by MOVF.  */
+       return 4;
+      if (reg_class_subset_p (from, ALL_COP_AND_GR_REGS))
+       return 5;
+      if (reg_class_subset_p (from, ACC_REGS))
+       return 6;
+    }
+  else if (reg_class_subset_p (from, FP_REGS))
+    {
+      if (reg_class_subset_p (to, FP_REGS)
+         && mips_mode_ok_for_mov_fmt_p (mode))
+       return 4;
+      if (reg_class_subset_p (to, ST_REGS))
+       /* An expensive sequence.  */
+       return 8;
     }
 
-  size = int_size_in_bytes (TREE_TYPE (decl));
-  return (size > 0 && size <= mips_section_threshold);
+  return 12;
 }
 
-/* Implement TARGET_USE_ANCHORS_FOR_SYMBOL_P.  We don't want to use
-   anchors for small data: the GP register acts as an anchor in that
-   case.  We also don't want to use them for PC-relative accesses,
-   where the PC acts as an anchor.  */
+/* This function returns the register class required for a secondary
+   register when copying between one of the registers in CLASS, and X,
+   using MODE.  If IN_P is nonzero, the copy is going from X to the
+   register, otherwise the register is the source.  A return value of
+   NO_REGS means that no secondary register is required.  */
 
-static bool
-mips_use_anchors_for_symbol_p (const_rtx symbol)
+enum reg_class
+mips_secondary_reload_class (enum reg_class class,
+                            enum machine_mode mode, rtx x, int in_p)
 {
-  switch (mips_classify_symbol (symbol, SYMBOL_CONTEXT_MEM))
-    {
-    case SYMBOL_PC_RELATIVE:
-    case SYMBOL_GP_RELATIVE:
-      return false;
+  int regno;
 
-    default:
-      return default_use_anchors_for_symbol_p (symbol);
-    }
-}
-\f
-/* See whether VALTYPE is a record whose fields should be returned in
-   floating-point registers.  If so, return the number of fields and
-   list them in FIELDS (which should have two elements).  Return 0
-   otherwise.
+  /* If X is a constant that cannot be loaded into $25, it must be loaded
+     into some other GPR.  No other register class allows a direct move.  */
+  if (mips_dangerous_for_la25_p (x))
+    return reg_class_subset_p (class, LEA_REGS) ? NO_REGS : LEA_REGS;
 
-   For n32 & n64, a structure with one or two fields is returned in
-   floating-point registers as long as every field has a floating-point
-   type.  */
+  regno = true_regnum (x);
+  if (TARGET_MIPS16)
+    {
+      /* In MIPS16 mode, every move must involve a member of M16_REGS.  */
+      if (!reg_class_subset_p (class, M16_REGS) && !M16_REG_P (regno))
+       return M16_REGS;
 
-static int
-mips_fpr_return_fields (const_tree valtype, tree *fields)
-{
-  tree field;
-  int i;
+      /* We can't really copy to HI or LO at all in MIPS16 mode.  */
+      if (in_p ? reg_classes_intersect_p (class, ACC_REGS) : ACC_REG_P (regno))
+       return M16_REGS;
 
-  if (!TARGET_NEWABI)
-    return 0;
+      return NO_REGS;
+    }
 
-  if (TREE_CODE (valtype) != RECORD_TYPE)
-    return 0;
+  /* Copying from accumulator registers to anywhere other than a general
+     register requires a temporary general register.  */
+  if (reg_class_subset_p (class, ACC_REGS))
+    return GP_REG_P (regno) ? NO_REGS : GR_REGS;
+  if (ACC_REG_P (regno))
+    return reg_class_subset_p (class, GR_REGS) ? NO_REGS : GR_REGS;
 
-  i = 0;
-  for (field = TYPE_FIELDS (valtype); field != 0; field = TREE_CHAIN (field))
+  /* We can only copy a value to a condition code register from a
+     floating point register, and even then we require a scratch
+     floating point register.  We can only copy a value out of a
+     condition code register into a general register.  */
+  if (reg_class_subset_p (class, ST_REGS))
     {
-      if (TREE_CODE (field) != FIELD_DECL)
-       continue;
+      if (in_p)
+       return FP_REGS;
+      return GP_REG_P (regno) ? NO_REGS : GR_REGS;
+    }
+  if (ST_REG_P (regno))
+    {
+      if (!in_p)
+       return FP_REGS;
+      return reg_class_subset_p (class, GR_REGS) ? NO_REGS : GR_REGS;
+    }
 
-      if (TREE_CODE (TREE_TYPE (field)) != REAL_TYPE)
-       return 0;
+  if (reg_class_subset_p (class, FP_REGS))
+    {
+      if (MEM_P (x)
+         && (GET_MODE_SIZE (mode) == 4 || GET_MODE_SIZE (mode) == 8))
+       /* In this case we can use lwc1, swc1, ldc1 or sdc1.  We'll use
+          pairs of lwc1s and swc1s if ldc1 and sdc1 are not supported.  */
+       return NO_REGS;
 
-      if (i == 2)
-       return 0;
+      if (GP_REG_P (regno) || x == CONST0_RTX (mode))
+       /* In this case we can use mtc1, mfc1, dmtc1 or dmfc1.  */
+       return NO_REGS;
 
-      fields[i++] = field;
+      if (CONSTANT_P (x) && !targetm.cannot_force_const_mem (x))
+       /* We can force the constant to memory and use lwc1
+          and ldc1.  As above, we will use pairs of lwc1s if
+          ldc1 is not supported.  */
+       return NO_REGS;
+
+      if (FP_REG_P (regno) && mips_mode_ok_for_mov_fmt_p (mode))
+       /* In this case we can use mov.fmt.  */
+       return NO_REGS;
+
+      /* Otherwise, we need to reload through an integer register.  */
+      return GR_REGS;
     }
-  return i;
+  if (FP_REG_P (regno))
+    return reg_class_subset_p (class, GR_REGS) ? NO_REGS : GR_REGS;
+
+  return NO_REGS;
 }
 
+/* SImode values are represented as sign-extended to DImode.  */
 
-/* Implement TARGET_RETURN_IN_MSB.  For n32 & n64, we should return
-   a value in the most significant part of $2/$3 if:
+static int
+mips_mode_rep_extended (enum machine_mode mode, enum machine_mode mode_rep)
+{
+  if (TARGET_64BIT && mode == SImode && mode_rep == DImode)
+    return SIGN_EXTEND;
 
-      - the target is big-endian;
+  return UNKNOWN;
+}
 
-      - the value has a structure or union type (we generalize this to
-       cover aggregates from other languages too); and
+static bool
+mips_valid_pointer_mode (enum machine_mode mode)
+{
+  return (mode == SImode || (TARGET_64BIT && mode == DImode));
+}
 
-      - the structure is not returned in floating-point registers.  */
+/* Target hook for vector_mode_supported_p.  */
 
 static bool
-mips_return_in_msb (const_tree valtype)
+mips_vector_mode_supported_p (enum machine_mode mode)
 {
-  tree fields[2];
-
-  return (TARGET_NEWABI
-         && TARGET_BIG_ENDIAN
-         && AGGREGATE_TYPE_P (valtype)
-         && mips_fpr_return_fields (valtype, fields) == 0);
-}
+  switch (mode)
+    {
+    case V2SFmode:
+      return TARGET_PAIRED_SINGLE_FLOAT;
 
+    case V2HImode:
+    case V4QImode:
+    case V2HQmode:
+    case V2UHQmode:
+    case V2HAmode:
+    case V2UHAmode:
+    case V4QQmode:
+    case V4UQQmode:
+      return TARGET_DSP;
 
-/* Return a composite value in a pair of floating-point registers.
-   MODE1 and OFFSET1 are the mode and byte offset for the first value,
-   likewise MODE2 and OFFSET2 for the second.  MODE is the mode of the
-   complete value.
+    default:
+      return false;
+    }
+}
 
-   For n32 & n64, $f0 always holds the first value and $f2 the second.
-   Otherwise the values are packed together as closely as possible.  */
+/* Implement TARGET_SCALAR_MODE_SUPPORTED_P.  */
 
-static rtx
-mips_return_fpr_pair (enum machine_mode mode,
-                     enum machine_mode mode1, HOST_WIDE_INT offset1,
-                     enum machine_mode mode2, HOST_WIDE_INT offset2)
+static bool
+mips_scalar_mode_supported_p (enum machine_mode mode)
 {
-  int inc;
-
-  inc = (TARGET_NEWABI ? 2 : MAX_FPRS_PER_FMT);
-  return gen_rtx_PARALLEL
-    (mode,
-     gen_rtvec (2,
-               gen_rtx_EXPR_LIST (VOIDmode,
-                                  gen_rtx_REG (mode1, FP_RETURN),
-                                  GEN_INT (offset1)),
-               gen_rtx_EXPR_LIST (VOIDmode,
-                                  gen_rtx_REG (mode2, FP_RETURN + inc),
-                                  GEN_INT (offset2))));
+  if (ALL_FIXED_POINT_MODE_P (mode)
+      && GET_MODE_PRECISION (mode) <= 2 * BITS_PER_WORD)
+    return true;
 
+  return default_scalar_mode_supported_p (mode);
 }
+/* This function does three things:
 
+   - Register the special divsi3 and modsi3 functions if -mfix-vr4120.
+   - Register the mips16 hardware floating point stubs.
+   - Register the gofast functions if selected using --enable-gofast.  */
 
-/* Implement FUNCTION_VALUE and LIBCALL_VALUE.  For normal calls,
-   VALTYPE is the return type and MODE is VOIDmode.  For libcalls,
-   VALTYPE is null and MODE is the mode of the return value.  */
+#include "config/gofast.h"
 
-rtx
-mips_function_value (const_tree valtype, const_tree func ATTRIBUTE_UNUSED,
-                    enum machine_mode mode)
+static void
+mips_init_libfuncs (void)
 {
-  if (valtype)
+  if (TARGET_FIX_VR4120)
     {
-      tree fields[2];
-      int unsignedp;
-
-      mode = TYPE_MODE (valtype);
-      unsignedp = TYPE_UNSIGNED (valtype);
-
-      /* Since we define TARGET_PROMOTE_FUNCTION_RETURN that returns
-        true, we must promote the mode just as PROMOTE_MODE does.  */
-      mode = promote_mode (valtype, mode, &unsignedp, 1);
-
-      /* Handle structures whose fields are returned in $f0/$f2.  */
-      switch (mips_fpr_return_fields (valtype, fields))
-       {
-       case 1:
-         return gen_rtx_REG (mode, FP_RETURN);
-
-       case 2:
-         return mips_return_fpr_pair (mode,
-                                      TYPE_MODE (TREE_TYPE (fields[0])),
-                                      int_byte_position (fields[0]),
-                                      TYPE_MODE (TREE_TYPE (fields[1])),
-                                      int_byte_position (fields[1]));
-       }
-
-      /* If a value is passed in the most significant part of a register, see
-        whether we have to round the mode up to a whole number of words.  */
-      if (mips_return_in_msb (valtype))
-       {
-         HOST_WIDE_INT size = int_size_in_bytes (valtype);
-         if (size % UNITS_PER_WORD != 0)
-           {
-             size += UNITS_PER_WORD - size % UNITS_PER_WORD;
-             mode = mode_for_size (size * BITS_PER_UNIT, MODE_INT, 0);
-           }
-       }
-
-      /* For EABI, the class of return register depends entirely on MODE.
-        For example, "struct { some_type x; }" and "union { some_type x; }"
-        are returned in the same way as a bare "some_type" would be.
-        Other ABIs only use FPRs for scalar, complex or vector types.  */
-      if (mips_abi != ABI_EABI && !FLOAT_TYPE_P (valtype))
-       return gen_rtx_REG (mode, GP_RETURN);
+      set_optab_libfunc (sdiv_optab, SImode, "__vr4120_divsi3");
+      set_optab_libfunc (smod_optab, SImode, "__vr4120_modsi3");
     }
 
-  if (!TARGET_MIPS16)
+  if (TARGET_MIPS16 && TARGET_HARD_FLOAT_ABI)
     {
-      /* Handle long doubles for n32 & n64.  */
-      if (mode == TFmode)
-       return mips_return_fpr_pair (mode,
-                                    DImode, 0,
-                                    DImode, GET_MODE_SIZE (mode) / 2);
+      set_optab_libfunc (add_optab, SFmode, "__mips16_addsf3");
+      set_optab_libfunc (sub_optab, SFmode, "__mips16_subsf3");
+      set_optab_libfunc (smul_optab, SFmode, "__mips16_mulsf3");
+      set_optab_libfunc (sdiv_optab, SFmode, "__mips16_divsf3");
 
-      if (mips_return_mode_in_fpr_p (mode))
-       {
-         if (GET_MODE_CLASS (mode) == MODE_COMPLEX_FLOAT)
-           return mips_return_fpr_pair (mode,
-                                        GET_MODE_INNER (mode), 0,
-                                        GET_MODE_INNER (mode),
-                                        GET_MODE_SIZE (mode) / 2);
-         else
-           return gen_rtx_REG (mode, FP_RETURN);
-       }
-    }
+      set_optab_libfunc (eq_optab, SFmode, "__mips16_eqsf2");
+      set_optab_libfunc (ne_optab, SFmode, "__mips16_nesf2");
+      set_optab_libfunc (gt_optab, SFmode, "__mips16_gtsf2");
+      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");
 
-  return gen_rtx_REG (mode, GP_RETURN);
-}
+      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");
 
-/* Return nonzero when an argument must be passed by reference.  */
+      if (TARGET_DOUBLE_FLOAT)
+       {
+         set_optab_libfunc (add_optab, DFmode, "__mips16_adddf3");
+         set_optab_libfunc (sub_optab, DFmode, "__mips16_subdf3");
+         set_optab_libfunc (smul_optab, DFmode, "__mips16_muldf3");
+         set_optab_libfunc (sdiv_optab, DFmode, "__mips16_divdf3");
 
-static bool
-mips_pass_by_reference (CUMULATIVE_ARGS *cum ATTRIBUTE_UNUSED,
-                       enum machine_mode mode, const_tree type,
-                       bool named ATTRIBUTE_UNUSED)
-{
-  if (mips_abi == ABI_EABI)
-    {
-      int size;
+         set_optab_libfunc (eq_optab, DFmode, "__mips16_eqdf2");
+         set_optab_libfunc (ne_optab, DFmode, "__mips16_nedf2");
+         set_optab_libfunc (gt_optab, DFmode, "__mips16_gtdf2");
+         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");
 
-      /* ??? How should SCmode be handled?  */
-      if (mode == DImode || mode == DFmode
-         || mode == DQmode || mode == UDQmode
-         || mode == DAmode || mode == UDAmode)
-       return 0;
+         set_conv_libfunc (sext_optab, DFmode, SFmode, "__mips16_extendsfdf2");
+         set_conv_libfunc (trunc_optab, SFmode, DFmode, "__mips16_truncdfsf2");
 
-      size = type ? int_size_in_bytes (type) : GET_MODE_SIZE (mode);
-      return size == -1 || size > UNITS_PER_WORD;
+         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
-    {
-      /* If we have a variable-sized parameter, we have no choice.  */
-      return targetm.calls.must_pass_in_stack (mode, type);
-    }
-}
-
-static bool
-mips_callee_copies (CUMULATIVE_ARGS *cum ATTRIBUTE_UNUSED,
-                   enum machine_mode mode ATTRIBUTE_UNUSED,
-                   const_tree type ATTRIBUTE_UNUSED, bool named)
-{
-  return mips_abi == ABI_EABI && named;
+    gofast_maybe_init_libfuncs ();
 }
 
-/* Return true if registers of class CLASS cannot change from mode FROM
-   to mode TO.  */
+/* Return the length of INSN.  LENGTH is the initial length computed by
+   attributes in the machine-description file.  */
 
-bool
-mips_cannot_change_mode_class (enum machine_mode from ATTRIBUTE_UNUSED,
-                              enum machine_mode to ATTRIBUTE_UNUSED,
-                              enum reg_class class)
+int
+mips_adjust_insn_length (rtx insn, int length)
 {
-  /* There are several problems with changing the modes of values
-     in floating-point registers:
+  /* A unconditional jump has an unfilled delay slot if it is not part
+     of a sequence.  A conditional jump normally has a delay slot, but
+     does not on MIPS16.  */
+  if (CALL_P (insn) || (TARGET_MIPS16 ? simplejump_p (insn) : JUMP_P (insn)))
+    length += 4;
 
-     - When a multi-word value is stored in paired floating-point
-       registers, the first register always holds the low word.
-       We therefore can't allow FPRs to change between single-word
-       and multi-word modes on big-endian targets.
+  /* See how many nops might be needed to avoid hardware hazards.  */
+  if (!cfun->machine->ignore_hazard_length_p && INSN_CODE (insn) >= 0)
+    switch (get_attr_hazard (insn))
+      {
+      case HAZARD_NONE:
+       break;
 
-     - GCC assumes that each word of a multiword register can be accessed
-       individually using SUBREGs.  This is not true for floating-point
-       registers if they are bigger than a word.
+      case HAZARD_DELAY:
+       length += 4;
+       break;
 
-     - Loading a 32-bit value into a 64-bit floating-point register
-       will not sign-extend the value, despite what LOAD_EXTEND_OP says.
-       We can't allow FPRs to change from SImode to to a wider mode on
-       64-bit targets.
+      case HAZARD_HILO:
+       length += 8;
+       break;
+      }
 
-     - If the FPU has already interpreted a value in one format, we must
-       not ask it to treat the value as having a different format.
+  /* All MIPS16 instructions are a measly two bytes.  */
+  if (TARGET_MIPS16)
+    length /= 2;
 
-     We therefore only allow changes between 4-byte and smaller integer
-     values, all of which have the "W" format as far as the FPU is
-     concerned.  */
-  return (reg_classes_intersect_p (FP_REGS, class)
-         && (GET_MODE_CLASS (from) != MODE_INT
-             || GET_MODE_CLASS (to) != MODE_INT
-             || GET_MODE_SIZE (from) > 4
-             || GET_MODE_SIZE (to) > 4));
+  return length;
 }
 
-/* Return true if X should not be moved directly into register $25.
-   We need this because many versions of GAS will treat "la $25,foo" as
-   part of a call sequence and so allow a global "foo" to be lazily bound.  */
-
-bool
-mips_dangerous_for_la25_p (rtx x)
-{
-  return (!TARGET_EXPLICIT_RELOCS
-         && TARGET_USE_GOT
-         && GET_CODE (x) == SYMBOL_REF
-         && mips_global_symbol_p (x));
-}
 
-/* Return true if moves in mode MODE can use the FPU's mov.fmt instruction.  */
+/* Return an asm sequence to start a noat block and load the address
+   of a label into $1.  */
 
-static bool
-mips_mode_ok_for_mov_fmt_p (enum machine_mode mode)
+const char *
+mips_output_load_label (void)
 {
-  switch (mode)
-    {
-    case SFmode:
-      return TARGET_HARD_FLOAT;
-
-    case DFmode:
-      return TARGET_HARD_FLOAT && TARGET_DOUBLE_FLOAT;
+  if (TARGET_EXPLICIT_RELOCS)
+    switch (mips_abi)
+      {
+      case ABI_N32:
+       return "%[lw\t%@,%%got_page(%0)(%+)\n\taddiu\t%@,%@,%%got_ofst(%0)";
 
-    case V2SFmode:
-      return TARGET_HARD_FLOAT && TARGET_PAIRED_SINGLE_FLOAT;
+      case ABI_64:
+       return "%[ld\t%@,%%got_page(%0)(%+)\n\tdaddiu\t%@,%@,%%got_ofst(%0)";
 
-    default:
-      return false;
+      default:
+       if (ISA_HAS_LOAD_DELAY)
+         return "%[lw\t%@,%%got(%0)(%+)%#\n\taddiu\t%@,%@,%%lo(%0)";
+       return "%[lw\t%@,%%got(%0)(%+)\n\taddiu\t%@,%@,%%lo(%0)";
+      }
+  else
+    {
+      if (Pmode == DImode)
+       return "%[dla\t%@,%0";
+      else
+       return "%[la\t%@,%0";
     }
 }
 
-/* Implement PREFERRED_RELOAD_CLASS.  */
+/* Return the assembly code for INSN, which has the operands given by
+   OPERANDS, and which branches to OPERANDS[1] if some condition is true.
+   BRANCH_IF_TRUE is the asm template that should be used if OPERANDS[1]
+   is in range of a direct branch.  BRANCH_IF_FALSE is an inverted
+   version of BRANCH_IF_TRUE.  */
 
-enum reg_class
-mips_preferred_reload_class (rtx x, enum reg_class class)
+const char *
+mips_output_conditional_branch (rtx insn, rtx *operands,
+                               const char *branch_if_true,
+                               const char *branch_if_false)
 {
-  if (mips_dangerous_for_la25_p (x) && reg_class_subset_p (LEA_REGS, class))
-    return LEA_REGS;
-
-  if (reg_class_subset_p (FP_REGS, class)
-      && mips_mode_ok_for_mov_fmt_p (GET_MODE (x)))
-    return FP_REGS;
+  unsigned int length;
+  rtx taken, not_taken;
 
-  if (reg_class_subset_p (GR_REGS, class))
-    class = GR_REGS;
+  length = get_attr_length (insn);
+  if (length <= 8)
+    {
+      /* Just a simple conditional branch.  */
+      mips_branch_likely = (final_sequence && INSN_ANNULLED_BRANCH_P (insn));
+      return branch_if_true;
+    }
 
-  if (TARGET_MIPS16 && reg_class_subset_p (M16_REGS, class))
-    class = M16_REGS;
-
-  return class;
-}
-
-/* This function returns the register class required for a secondary
-   register when copying between one of the registers in CLASS, and X,
-   using MODE.  If IN_P is nonzero, the copy is going from X to the
-   register, otherwise the register is the source.  A return value of
-   NO_REGS means that no secondary register is required.  */
-
-enum reg_class
-mips_secondary_reload_class (enum reg_class class,
-                            enum machine_mode mode, rtx x, int in_p)
-{
-  int regno;
+  /* Generate a reversed branch around a direct jump.  This fallback does
+     not use branch-likely instructions.  */
+  mips_branch_likely = false;
+  not_taken = gen_label_rtx ();
+  taken = operands[1];
 
-  /* If X is a constant that cannot be loaded into $25, it must be loaded
-     into some other GPR.  No other register class allows a direct move.  */
-  if (mips_dangerous_for_la25_p (x))
-    return reg_class_subset_p (class, LEA_REGS) ? NO_REGS : LEA_REGS;
+  /* Generate the reversed branch to NOT_TAKEN.  */
+  operands[1] = not_taken;
+  output_asm_insn (branch_if_false, operands);
 
-  regno = true_regnum (x);
-  if (TARGET_MIPS16)
+  /* If INSN has a delay slot, we must provide delay slots for both the
+     branch to NOT_TAKEN and the conditional jump.  We must also ensure
+     that INSN's delay slot is executed in the appropriate cases.  */
+  if (final_sequence)
     {
-      /* In MIPS16 mode, every move must involve a member of M16_REGS.  */
-      if (!reg_class_subset_p (class, M16_REGS) && !M16_REG_P (regno))
-       return M16_REGS;
-
-      /* We can't really copy to HI or LO at all in MIPS16 mode.  */
-      if (in_p ? reg_classes_intersect_p (class, ACC_REGS) : ACC_REG_P (regno))
-       return M16_REGS;
-
-      return NO_REGS;
+      /* This first delay slot will always be executed, so use INSN's
+        delay slot if is not annulled.  */
+      if (!INSN_ANNULLED_BRANCH_P (insn))
+       {
+         final_scan_insn (XVECEXP (final_sequence, 0, 1),
+                          asm_out_file, optimize, 1, NULL);
+         INSN_DELETED_P (XVECEXP (final_sequence, 0, 1)) = 1;
+       }
+      else
+       output_asm_insn ("nop", 0);
+      fprintf (asm_out_file, "\n");
     }
 
-  /* Copying from accumulator registers to anywhere other than a general
-     register requires a temporary general register.  */
-  if (reg_class_subset_p (class, ACC_REGS))
-    return GP_REG_P (regno) ? NO_REGS : GR_REGS;
-  if (ACC_REG_P (regno))
-    return reg_class_subset_p (class, GR_REGS) ? NO_REGS : GR_REGS;
-
-  /* We can only copy a value to a condition code register from a
-     floating point register, and even then we require a scratch
-     floating point register.  We can only copy a value out of a
-     condition code register into a general register.  */
-  if (reg_class_subset_p (class, ST_REGS))
-    {
-      if (in_p)
-       return FP_REGS;
-      return GP_REG_P (regno) ? NO_REGS : GR_REGS;
-    }
-  if (ST_REG_P (regno))
+  /* Output the unconditional branch to TAKEN.  */
+  if (length <= 16)
+    output_asm_insn ("j\t%0%/", &taken);
+  else
     {
-      if (!in_p)
-       return FP_REGS;
-      return reg_class_subset_p (class, GR_REGS) ? NO_REGS : GR_REGS;
+      output_asm_insn (mips_output_load_label (), &taken);
+      output_asm_insn ("jr\t%@%]%/", 0);
     }
 
-  if (reg_class_subset_p (class, FP_REGS))
+  /* Now deal with its delay slot; see above.  */
+  if (final_sequence)
     {
-      if (MEM_P (x)
-         && (GET_MODE_SIZE (mode) == 4 || GET_MODE_SIZE (mode) == 8))
-       /* In this case we can use lwc1, swc1, ldc1 or sdc1.  We'll use
-          pairs of lwc1s and swc1s if ldc1 and sdc1 are not supported.  */
-       return NO_REGS;
-
-      if (GP_REG_P (regno) || x == CONST0_RTX (mode))
-       /* In this case we can use mtc1, mfc1, dmtc1 or dmfc1.  */
-       return NO_REGS;
-
-      if (CONSTANT_P (x) && !targetm.cannot_force_const_mem (x))
-       /* We can force the constant to memory and use lwc1
-          and ldc1.  As above, we will use pairs of lwc1s if
-          ldc1 is not supported.  */
-       return NO_REGS;
-
-      if (FP_REG_P (regno) && mips_mode_ok_for_mov_fmt_p (mode))
-       /* In this case we can use mov.fmt.  */
-       return NO_REGS;
-
-      /* Otherwise, we need to reload through an integer register.  */
-      return GR_REGS;
+      /* This delay slot will only be executed if the branch is taken.
+        Use INSN's delay slot if is annulled.  */
+      if (INSN_ANNULLED_BRANCH_P (insn))
+       {
+         final_scan_insn (XVECEXP (final_sequence, 0, 1),
+                          asm_out_file, optimize, 1, NULL);
+         INSN_DELETED_P (XVECEXP (final_sequence, 0, 1)) = 1;
+       }
+      else
+       output_asm_insn ("nop", 0);
+      fprintf (asm_out_file, "\n");
     }
-  if (FP_REG_P (regno))
-    return reg_class_subset_p (class, GR_REGS) ? NO_REGS : GR_REGS;
 
-  return NO_REGS;
+  /* Output NOT_TAKEN.  */
+  (*targetm.asm_out.internal_label) (asm_out_file, "L",
+                                    CODE_LABEL_NUMBER (not_taken));
+  return "";
 }
 
-/* Implement CLASS_MAX_NREGS.
-
-   - UNITS_PER_FPREG controls the number of registers needed by FP_REGS.
-
-   - ST_REGS are always hold CCmode values, and CCmode values are
-     considered to be 4 bytes wide.
-
-   All other register classes are covered by UNITS_PER_WORD.  Note that
-   this is true even for unions of integer and float registers when the
-   latter are smaller than the former.  The only supported combination
-   in which case this occurs is -mgp64 -msingle-float, which has 64-bit
-   words but 32-bit float registers.  A word-based calculation is correct
-   in that case since -msingle-float disallows multi-FPR values.  */
-
-int
-mips_class_max_nregs (enum reg_class class ATTRIBUTE_UNUSED,
-                     enum machine_mode mode)
-{
-  if (class == ST_REGS)
-    return (GET_MODE_SIZE (mode) + 3) / 4;
-  else if (class == FP_REGS)
-    return (GET_MODE_SIZE (mode) + UNITS_PER_FPREG - 1) / UNITS_PER_FPREG;
-  else
-    return (GET_MODE_SIZE (mode) + UNITS_PER_WORD - 1) / UNITS_PER_WORD;
-}
+/* Return the assembly code for INSN, which branches to OPERANDS[1]
+   if some ordered condition is true.  The condition is given by
+   OPERANDS[0] if !INVERTED_P, otherwise it is the inverse of
+   OPERANDS[0].  OPERANDS[2] is the comparison's first operand;
+   its second is always zero.  */
 
-static bool
-mips_valid_pointer_mode (enum machine_mode mode)
+const char *
+mips_output_order_conditional_branch (rtx insn, rtx *operands, bool inverted_p)
 {
-  return (mode == SImode || (TARGET_64BIT && mode == DImode));
-}
-
-/* Target hook for vector_mode_supported_p.  */
+  const char *branch[2];
 
-static bool
-mips_vector_mode_supported_p (enum machine_mode mode)
-{
-  switch (mode)
+  /* Make BRANCH[1] branch to OPERANDS[1] when the condition is true.
+     Make BRANCH[0] branch on the inverse condition.  */
+  switch (GET_CODE (operands[0]))
     {
-    case V2SFmode:
-      return TARGET_PAIRED_SINGLE_FLOAT;
+      /* These cases are equivalent to comparisons against zero.  */
+    case LEU:
+      inverted_p = !inverted_p;
+      /* Fall through.  */
+    case GTU:
+      branch[!inverted_p] = MIPS_BRANCH ("bne", "%2,%.,%1");
+      branch[inverted_p] = MIPS_BRANCH ("beq", "%2,%.,%1");
+      break;
 
-    case V2HImode:
-    case V4QImode:
-    case V2HQmode:
-    case V2UHQmode:
-    case V2HAmode:
-    case V2UHAmode:
-    case V4QQmode:
-    case V4UQQmode:
-      return TARGET_DSP;
+      /* These cases are always true or always false.  */
+    case LTU:
+      inverted_p = !inverted_p;
+      /* Fall through.  */
+    case GEU:
+      branch[!inverted_p] = MIPS_BRANCH ("beq", "%.,%.,%1");
+      branch[inverted_p] = MIPS_BRANCH ("bne", "%.,%.,%1");
+      break;
 
     default:
-      return false;
+      branch[!inverted_p] = MIPS_BRANCH ("b%C0z", "%2,%1");
+      branch[inverted_p] = MIPS_BRANCH ("b%N0z", "%2,%1");
+      break;
     }
+  return mips_output_conditional_branch (insn, operands, branch[1], branch[0]);
 }
+\f
+/* Used to output div or ddiv instruction DIVISION, which has the operands
+   given by OPERANDS.  Add in a divide-by-zero check if needed.
 
-/* Implement TARGET_SCALAR_MODE_SUPPORTED_P.  */
+   When working around R4000 and R4400 errata, we need to make sure that
+   the division is not immediately followed by a shift[1][2].  We also
+   need to stop the division from being put into a branch delay slot[3].
+   The easiest way to avoid both problems is to add a nop after the
+   division.  When a divide-by-zero check is needed, this nop can be
+   used to fill the branch delay slot.
 
-static bool
-mips_scalar_mode_supported_p (enum machine_mode mode)
-{
-  if (ALL_FIXED_POINT_MODE_P (mode)
-      && GET_MODE_PRECISION (mode) <= 2 * BITS_PER_WORD)
-    return true;
+   [1] If a double-word or a variable shift executes immediately
+       after starting an integer division, the shift may give an
+       incorrect result.  See quotations of errata #16 and #28 from
+       "MIPS R4000PC/SC Errata, Processor Revision 2.2 and 3.0"
+       in mips.md for details.
 
-  return default_scalar_mode_supported_p (mode);
-}
-\f
-/* If we can access small data directly (using gp-relative relocation
-   operators) return the small data pointer, otherwise return null.
+   [2] A similar bug to [1] exists for all revisions of the
+       R4000 and the R4400 when run in an MC configuration.
+       From "MIPS R4000MC Errata, Processor Revision 2.2 and 3.0":
 
-   For each mips16 function which refers to GP relative symbols, we
-   use a pseudo register, initialized at the start of the function, to
-   hold the $gp value.  */
+       "19. In this following sequence:
 
-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);
+                   ddiv                (or ddivu or div or divu)
+                   dsll32              (or dsrl32, dsra32)
 
-  /* 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;
+           if an MPT stall occurs, while the divide is slipping the cpu
+           pipeline, then the following double shift would end up with an
+           incorrect result.
 
-      /* 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);
+           Workaround: The compiler needs to avoid generating any
+           sequence with divide followed by extended double shift."
 
-      push_topmost_sequence ();
-      /* We need to emit the initialization after the FUNCTION_BEG
-         note, so that it will be integrated.  */
-      for (scan = get_insns (); scan != NULL_RTX; scan = NEXT_INSN (scan))
-       if (NOTE_P (scan)
-           && NOTE_KIND (scan) == NOTE_INSN_FUNCTION_BEG)
-         break;
-      if (scan == NULL_RTX)
-       scan = get_insns ();
-      insn = emit_insn_after (insn, scan);
-      pop_topmost_sequence ();
-
-      cfun->machine->initialized_mips16_gp_pseudo_p = true;
-    }
-
-  return cfun->machine->mips16_gp_pseudo_rtx;
-}
+       This erratum is also present in "MIPS R4400MC Errata, Processor
+       Revision 1.0" and "MIPS R4400MC Errata, Processor Revision 2.0
+       & 3.0" as errata #10 and #4, respectively.
 
-/* Write out code to move floating point arguments in or out of
-   general registers.  Output the instructions to FILE.  FP_CODE is
-   the code describing which arguments are present (see the comment at
-   the definition of CUMULATIVE_ARGS in mips.h).  FROM_FP_P is nonzero if
-   we are copying from the floating point registers.  */
+   [3] From "MIPS R4000PC/SC Errata, Processor Revision 2.2 and 3.0"
+       (also valid for MIPS R4000MC processors):
 
-static void
-mips16_fp_args (FILE *file, int fp_code, int from_fp_p)
-{
-  const char *s;
-  int gparg, fparg;
-  unsigned int f;
-  CUMULATIVE_ARGS cum;
+       "52. R4000SC: This bug does not apply for the R4000PC.
 
-  /* This code only works for the original 32-bit ABI and the O64 ABI.  */
-  gcc_assert (TARGET_OLDABI);
+           There are two flavors of this bug:
 
-  if (from_fp_p)
-    s = "mfc1";
-  else
-    s = "mtc1";
+           1) If the instruction just after divide takes an RF exception
+              (tlb-refill, tlb-invalid) and gets an instruction cache
+              miss (both primary and secondary) and the line which is
+              currently in secondary cache at this index had the first
+              data word, where the bits 5..2 are set, then R4000 would
+              get a wrong result for the div.
 
-  init_cumulative_args (&cum, NULL, NULL);
+           ##1
+                   nop
+                   div r8, r9
+                   -------------------         # end-of page. -tlb-refill
+                   nop
+           ##2
+                   nop
+                   div r8, r9
+                   -------------------         # end-of page. -tlb-invalid
+                   nop
 
-  for (f = (unsigned int) fp_code; f != 0; f >>= 2)
-    {
-      enum machine_mode mode;
-      struct mips_arg_info info;
+           2) If the divide is in the taken branch delay slot, where the
+              target takes RF exception and gets an I-cache miss for the
+              exception vector or where I-cache miss occurs for the
+              target address, under the above mentioned scenarios, the
+              div would get wrong results.
 
-      if ((f & 3) == 1)
-       mode = SFmode;
-      else if ((f & 3) == 2)
-       mode = DFmode;
-      else
-       gcc_unreachable ();
+           ##1
+                   j   r2              # to next page mapped or unmapped
+                   div r8,r9           # this bug would be there as long
+                                       # as there is an ICache miss and
+                   nop                 # the "data pattern" is present
 
-      mips_arg_info (&cum, mode, NULL, true, &info);
-      gparg = mips_arg_regno (&info, false);
-      fparg = mips_arg_regno (&info, true);
+           ##2
+                   beq r0, r0, NextPage        # to Next page
+                   div r8,r9
+                   nop
 
-      if (mode == SFmode)
-       fprintf (file, "\t%s\t%s,%s\n", s,
-                reg_names[gparg], reg_names[fparg]);
-      else if (TARGET_64BIT)
-       fprintf (file, "\td%s\t%s,%s\n", s,
-                reg_names[gparg], reg_names[fparg]);
-      else if (ISA_HAS_MXHC1)
-       /* -mips32r2 -mfp64 */
-       fprintf (file, "\t%s\t%s,%s\n\t%s\t%s,%s\n",
-                s,
-                reg_names[gparg + (WORDS_BIG_ENDIAN ? 1 : 0)],
-                reg_names[fparg],
-                from_fp_p ? "mfhc1" : "mthc1",
-                reg_names[gparg + (WORDS_BIG_ENDIAN ? 0 : 1)],
-                reg_names[fparg]);
-      else if (TARGET_BIG_ENDIAN)
-       fprintf (file, "\t%s\t%s,%s\n\t%s\t%s,%s\n", s,
-                reg_names[gparg], reg_names[fparg + 1], s,
-                reg_names[gparg + 1], reg_names[fparg]);
-      else
-       fprintf (file, "\t%s\t%s,%s\n\t%s\t%s,%s\n", s,
-                reg_names[gparg], reg_names[fparg], s,
-                reg_names[gparg + 1], reg_names[fparg + 1]);
+           This bug is present for div, divu, ddiv, and ddivu
+           instructions.
 
-      function_arg_advance (&cum, mode, NULL, true);
-    }
-}
+           Workaround: For item 1), OS could make sure that the next page
+           after the divide instruction is also mapped.  For item 2), the
+           compiler could make sure that the divide instruction is not in
+           the branch delay slot."
 
-/* Build a mips16 function stub.  This is used for functions which
-   take arguments in the floating point registers.  It is 32-bit code
-   that moves the floating point args into the general registers, and
-   then jumps to the 16-bit code.  */
+       These processors have PRId values of 0x00004220 and 0x00004300 for
+       the R4000 and 0x00004400, 0x00004500 and 0x00004600 for the R4400.  */
 
-static void
-build_mips16_function_stub (FILE *file)
+const char *
+mips_output_division (const char *division, rtx *operands)
 {
-  const char *fnname;
-  char *secname, *stubname;
-  tree stubid, stubdecl;
-  int need_comma;
-  unsigned int f;
-
-  fnname = XSTR (XEXP (DECL_RTL (current_function_decl), 0), 0);
-  fnname = targetm.strip_name_encoding (fnname);
-  secname = (char *) alloca (strlen (fnname) + 20);
-  sprintf (secname, ".mips16.fn.%s", fnname);
-  stubname = (char *) alloca (strlen (fnname) + 20);
-  sprintf (stubname, "__fn_stub_%s", fnname);
-  stubid = get_identifier (stubname);
-  stubdecl = build_decl (FUNCTION_DECL, stubid,
-                        build_function_type (void_type_node, NULL_TREE));
-  DECL_SECTION_NAME (stubdecl) = build_string (strlen (secname), secname);
-  DECL_RESULT (stubdecl) = build_decl (RESULT_DECL, NULL_TREE, void_type_node);
+  const char *s;
 
-  fprintf (file, "\t# Stub function for %s (", current_function_name ());
-  need_comma = 0;
-  for (f = (unsigned int) current_function_args_info.fp_code; f != 0; f >>= 2)
+  s = division;
+  if (TARGET_FIX_R4000 || TARGET_FIX_R4400)
     {
-      fprintf (file, "%s%s",
-              need_comma ? ", " : "",
-              (f & 3) == 1 ? "float" : "double");
-      need_comma = 1;
+      output_asm_insn (s, operands);
+      s = "nop";
     }
-  fprintf (file, ")\n");
-
-  fprintf (file, "\t.set\tnomips16\n");
-  switch_to_section (function_section (stubdecl));
-  ASM_OUTPUT_ALIGN (file, floor_log2 (FUNCTION_BOUNDARY / BITS_PER_UNIT));
-
-  /* ??? If FUNCTION_NAME_ALREADY_DECLARED is defined, then we are
-     within a .ent, and we cannot emit another .ent.  */
-  if (!FUNCTION_NAME_ALREADY_DECLARED)
+  if (TARGET_CHECK_ZERO_DIV)
     {
-      fputs ("\t.ent\t", file);
-      assemble_name (file, stubname);
-      fputs ("\n", file);
+      if (TARGET_MIPS16)
+       {
+         output_asm_insn (s, operands);
+         s = "bnez\t%2,1f\n\tbreak\t7\n1:";
+       }
+      else if (GENERATE_DIVIDE_TRAPS)
+        {
+         output_asm_insn (s, operands);
+         s = "teq\t%2,%.,7";
+        }
+      else
+       {
+         output_asm_insn ("%(bne\t%2,%.,1f", operands);
+         output_asm_insn (s, operands);
+         s = "break\t7%)\n1:";
+       }
     }
+  return s;
+}
+\f
+/* Return true if INSN is a multiply-add or multiply-subtract
+   instruction and PREV assigns to the accumulator operand.  */
 
-  assemble_name (file, stubname);
-  fputs (":\n", file);
-
-  /* We don't want the assembler to insert any nops here.  */
-  fprintf (file, "\t.set\tnoreorder\n");
-
-  mips16_fp_args (file, current_function_args_info.fp_code, 1);
+bool
+mips_linked_madd_p (rtx prev, rtx insn)
+{
+  rtx x;
 
-  fprintf (asm_out_file, "\t.set\tnoat\n");
-  fprintf (asm_out_file, "\tla\t%s,", reg_names[GP_REG_FIRST + 1]);
-  assemble_name (file, fnname);
-  fprintf (file, "\n");
-  fprintf (asm_out_file, "\tjr\t%s\n", reg_names[GP_REG_FIRST + 1]);
-  fprintf (asm_out_file, "\t.set\tat\n");
+  x = single_set (insn);
+  if (x == 0)
+    return false;
 
-  /* Unfortunately, we can't fill the jump delay slot.  We can't fill
-     with one of the mfc1 instructions, because the result is not
-     available for one instruction, so if the very first instruction
-     in the function refers to the register, it will see the wrong
-     value.  */
-  fprintf (file, "\tnop\n");
+  x = SET_SRC (x);
 
-  fprintf (file, "\t.set\treorder\n");
+  if (GET_CODE (x) == PLUS
+      && GET_CODE (XEXP (x, 0)) == MULT
+      && reg_set_p (XEXP (x, 1), prev))
+    return true;
 
-  if (!FUNCTION_NAME_ALREADY_DECLARED)
-    {
-      fputs ("\t.end\t", file);
-      assemble_name (file, stubname);
-      fputs ("\n", file);
-    }
+  if (GET_CODE (x) == MINUS
+      && GET_CODE (XEXP (x, 1)) == MULT
+      && reg_set_p (XEXP (x, 0), prev))
+    return true;
 
-  switch_to_section (function_section (current_function_decl));
+  return false;
 }
 
-/* We keep a list of functions for which we have already built stubs
-   in build_mips16_call_stub.  */
+/* Implements a store data bypass check.  We need this because the cprestore
+   pattern is type store, but defined using an UNSPEC.  This UNSPEC causes the
+   default routine to abort.  We just return false for that case.  */
+/* ??? Should try to give a better result here than assuming false.  */
 
-struct mips16_stub
+int
+mips_store_data_bypass_p (rtx out_insn, rtx in_insn)
 {
-  struct mips16_stub *next;
-  char *name;
-  int fpret;
-};
+  if (GET_CODE (PATTERN (in_insn)) == UNSPEC_VOLATILE)
+    return false;
 
-static struct mips16_stub *mips16_stubs;
+  return ! store_data_bypass_p (out_insn, in_insn);
+}
+\f
+/* Implement TARGET_SCHED_ADJUST_COST.  We assume that anti and output
+   dependencies have no cost, except on the 20Kc where output-dependence
+   is treated like input-dependence.  */
 
-/* Emit code to return a double value from a mips16 stub.  GPREG is the
-   first GP reg to use, FPREG is the first FP reg to use.  */
+static 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;
+}
 
-static void
-mips16_fpret_double (int gpreg, int fpreg)
+/* Return the number of instructions that can be issued per cycle.  */
+
+static int
+mips_issue_rate (void)
 {
-  if (TARGET_64BIT)
-    fprintf (asm_out_file, "\tdmfc1\t%s,%s\n",
-            reg_names[gpreg], reg_names[fpreg]);
-  else if (TARGET_FLOAT64)
-    {
-      fprintf (asm_out_file, "\tmfc1\t%s,%s\n",
-              reg_names[gpreg + WORDS_BIG_ENDIAN],
-              reg_names[fpreg]);
-      fprintf (asm_out_file, "\tmfhc1\t%s,%s\n",
-              reg_names[gpreg + !WORDS_BIG_ENDIAN],
-              reg_names[fpreg]);
-    }
-  else
+  switch (mips_tune)
     {
-      if (TARGET_BIG_ENDIAN)
-       {
-         fprintf (asm_out_file, "\tmfc1\t%s,%s\n",
-                  reg_names[gpreg + 0],
-                  reg_names[fpreg + 1]);
-         fprintf (asm_out_file, "\tmfc1\t%s,%s\n",
-                  reg_names[gpreg + 1],
-                  reg_names[fpreg + 0]);
-       }
-      else
-       {
-         fprintf (asm_out_file, "\tmfc1\t%s,%s\n",
-                  reg_names[gpreg + 0],
-                  reg_names[fpreg + 0]);
-         fprintf (asm_out_file, "\tmfc1\t%s,%s\n",
-                  reg_names[gpreg + 1],
-                  reg_names[fpreg + 1]);
-       }
+    case PROCESSOR_74KC:
+    case PROCESSOR_74KF2_1:
+    case PROCESSOR_74KF1_1:
+    case PROCESSOR_74KF3_2:
+      /* The 74k is not strictly quad-issue cpu, but can be seen as one
+        by the scheduler.  It can issue 1 ALU, 1 AGEN and 2 FPU insns,
+        but in reality only a maximum of 3 insns can be issued as the
+        floating point load/stores also require a slot in the AGEN pipe.  */
+     return 4;
+
+    case PROCESSOR_20KC:
+    case PROCESSOR_R4130:
+    case PROCESSOR_R5400:
+    case PROCESSOR_R5500:
+    case PROCESSOR_R7000:
+    case PROCESSOR_R9000:
+      return 2;
+
+    case PROCESSOR_SB1:
+    case PROCESSOR_SB1A:
+      /* This is actually 4, but we get better performance if we claim 3.
+        This is partly because of unwanted speculative code motion with the
+        larger number, and partly because in most common cases we can't
+        reach the theoretical max of 4.  */
+      return 3;
+
+    default:
+      return 1;
     }
 }
 
-/* Build a call stub for a mips16 call.  A stub is needed if we are
-   passing any floating point values which should go into the floating
-   point registers.  If we are, and the call turns out to be to a
-   32-bit function, the stub will be used to move the values into the
-   floating point registers before calling the 32-bit function.  The
-   linker will magically adjust the function call to either the 16-bit
-   function or the 32-bit stub, depending upon where the function call
-   is actually defined.
+/* Implements TARGET_SCHED_FIRST_CYCLE_MULTIPASS_DFA_LOOKAHEAD.  This should
+   be as wide as the scheduling freedom in the DFA.  */
 
-   Similarly, we need a stub if the return value might come back in a
-   floating point register.
+static int
+mips_multipass_dfa_lookahead (void)
+{
+  /* Can schedule up to 4 of the 6 function units in any one cycle.  */
+  if (TUNE_SB1)
+    return 4;
 
-   RETVAL is the location of the return value, or null if this is
-   a call rather than a call_value.  FN is the address of the
-   function and ARG_SIZE is the size of the arguments.  FP_CODE
-   is the code built by function_arg.  This function returns a nonzero
-   value if it builds the call instruction itself.  */
+  return 0;
+}
+\f
+/* Remove the instruction at index LOWER from ready queue READY and
+   reinsert it in front of the instruction at index HIGHER.  LOWER must
+   be <= HIGHER.  */
 
-int
-build_mips16_call_stub (rtx retval, rtx fn, rtx arg_size, int fp_code)
+static void
+mips_promote_ready (rtx *ready, int lower, int higher)
 {
-  int fpret = 0;
-  const char *fnname;
-  char *secname, *stubname;
-  struct mips16_stub *l;
-  tree stubid, stubdecl;
-  int need_comma;
-  unsigned int f;
-  rtx insn;
+  rtx new_head;
+  int i;
 
-  /* We don't need to do anything if we aren't in mips16 mode, or if
-     we were invoked with the -msoft-float option.  */
-  if (!TARGET_MIPS16 || TARGET_SOFT_FLOAT_ABI)
-    return 0;
+  new_head = ready[lower];
+  for (i = lower; i < higher; i++)
+    ready[i] = ready[i + 1];
+  ready[i] = new_head;
+}
 
-  /* Figure out whether the value might come back in a floating point
-     register.  */
-  if (retval)
-    fpret = mips_return_mode_in_fpr_p (GET_MODE (retval));
+/* 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.  */
 
-  /* We don't need to do anything if there were no floating point
-     arguments and the value will not be returned in a floating point
-     register.  */
-  if (fp_code == 0 && ! fpret)
-    return 0;
+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;
+    }
+}
+\f
+/* Used by TUNE_MACC_CHAINS to record the last scheduled instruction
+   that may clobber hi or lo.  */
 
-  /* We don't need to do anything if this is a call to a special
-     mips16 support function.  */
-  if (GET_CODE (fn) == SYMBOL_REF
-      && strncmp (XSTR (fn, 0), "__mips16_", 9) == 0)
-    return 0;
+static rtx mips_macc_chains_last_hilo;
 
-  /* This code will only work for o32 and o64 abis.  The other ABI's
-     require more sophisticated support.  */
-  gcc_assert (TARGET_OLDABI);
+/* A TUNE_MACC_CHAINS helper function.  Record that instruction INSN has
+   been scheduled, updating mips_macc_chains_last_hilo appropriately.  */
 
-  /* If we're calling via a function pointer, then we must always call
-     via a stub.  There are magic stubs provided in libgcc.a for each
-     of the required cases.  Each of them expects the function address
-     to arrive in register $2.  */
+static void
+mips_macc_chains_record (rtx insn)
+{
+  if (get_attr_may_clobber_hilo (insn))
+    mips_macc_chains_last_hilo = insn;
+}
 
-  if (GET_CODE (fn) != SYMBOL_REF)
-    {
-      char buf[30];
-      tree id;
-      rtx stub_fn, insn;
+/* A TUNE_MACC_CHAINS helper function.  Search ready queue READY, which
+   has NREADY elements, looking for a multiply-add or multiply-subtract
+   instruction that is cumulative with mips_macc_chains_last_hilo.
+   If there is one, promote it ahead of anything else that might
+   clobber hi or lo.  */
 
-      /* ??? If this code is modified to support other ABI's, we need
-         to handle PARALLEL return values here.  */
+static void
+mips_macc_chains_reorder (rtx *ready, int nready)
+{
+  int i, j;
 
-      if (fpret)
-       sprintf (buf, "__mips16_call_stub_%s_%d",
-                mips16_call_stub_mode_suffix (GET_MODE (retval)),
-                fp_code);
-      else
-       sprintf (buf, "__mips16_call_stub_%d",
-                fp_code);
+  if (mips_macc_chains_last_hilo != 0)
+    for (i = nready - 1; i >= 0; i--)
+      if (mips_linked_madd_p (mips_macc_chains_last_hilo, ready[i]))
+       {
+         for (j = nready - 1; j > i; j--)
+           if (recog_memoized (ready[j]) >= 0
+               && get_attr_may_clobber_hilo (ready[j]))
+             {
+               mips_promote_ready (ready, i, j);
+               break;
+             }
+         break;
+       }
+}
+\f
+/* The last instruction to be scheduled.  */
 
-      id = get_identifier (buf);
-      stub_fn = gen_rtx_SYMBOL_REF (Pmode, IDENTIFIER_POINTER (id));
+static rtx vr4130_last_insn;
 
-      mips_emit_move (gen_rtx_REG (Pmode, 2), fn);
+/* A note_stores callback used by vr4130_true_reg_dependence_p.  DATA
+   points to an rtx that is initially an instruction.  Nullify the rtx
+   if the instruction uses the value of register X.  */
 
-      if (retval == NULL_RTX)
-       insn = gen_call_internal (stub_fn, arg_size);
-      else
-       insn = gen_call_value_internal (retval, stub_fn, arg_size);
-      insn = emit_call_insn (insn);
+static void
+vr4130_true_reg_dependence_p_1 (rtx x, const_rtx pat ATTRIBUTE_UNUSED, void *data)
+{
+  rtx *insn_ptr = data;
+  if (REG_P (x)
+      && *insn_ptr != 0
+      && reg_referenced_p (x, PATTERN (*insn_ptr)))
+    *insn_ptr = 0;
+}
 
-      /* Put the register usage information on the CALL.  */
-      CALL_INSN_FUNCTION_USAGE (insn) =
-       gen_rtx_EXPR_LIST (VOIDmode,
-                          gen_rtx_USE (VOIDmode, gen_rtx_REG (Pmode, 2)),
-                          CALL_INSN_FUNCTION_USAGE (insn));
+/* Return true if there is true register dependence between vr4130_last_insn
+   and INSN.  */
 
-      /* 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 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)
-       CALL_INSN_FUNCTION_USAGE (insn) =
-         gen_rtx_EXPR_LIST (VOIDmode,
-                            gen_rtx_USE (VOIDmode,
-                                         gen_rtx_REG (word_mode, 18)),
-                            CALL_INSN_FUNCTION_USAGE (insn));
-
-      /* Return 1 to tell the caller that we've generated the call
-         insn.  */
-      return 1;
-    }
-
-  /* We know the function we are going to call.  If we have already
-     built a stub, we don't need to do anything further.  */
+static bool
+vr4130_true_reg_dependence_p (rtx insn)
+{
+  note_stores (PATTERN (vr4130_last_insn),
+              vr4130_true_reg_dependence_p_1, &insn);
+  return insn == 0;
+}
 
-  fnname = targetm.strip_name_encoding (XSTR (fn, 0));
-  for (l = mips16_stubs; l != NULL; l = l->next)
-    if (strcmp (l->name, fnname) == 0)
-      break;
+/* A TUNE_MIPS4130 helper function.  Given that INSN1 is at the head of
+   the ready queue and that INSN2 is the instruction after it, return
+   true if it is worth promoting INSN2 ahead of INSN1.  Look for cases
+   in which INSN1 and INSN2 can probably issue in parallel, but for
+   which (INSN2, INSN1) should be less sensitive to instruction
+   alignment than (INSN1, INSN2).  See 4130.md for more details.  */
 
-  if (l == NULL)
-    {
-      /* Build a special purpose stub.  When the linker sees a
-        function call in mips16 code, it will check where the target
-        is defined.  If the target is a 32-bit call, the linker will
-        search for the section defined here.  It can tell which
-        symbol this section is associated with by looking at the
-        relocation information (the name is unreliable, since this
-        might be a static function).  If such a section is found, the
-        linker will redirect the call to the start of the magic
-        section.
+static bool
+vr4130_swap_insns_p (rtx insn1, rtx insn2)
+{
+  sd_iterator_def sd_it;
+  dep_t dep;
 
-        If the function does not return a floating point value, the
-        special stub section is named
-            .mips16.call.FNNAME
+  /* Check for the following case:
 
-        If the function does return a floating point value, the stub
-        section is named
-            .mips16.call.fp.FNNAME
-        */
+     1) there is some other instruction X with an anti dependence on INSN1;
+     2) X has a higher priority than INSN2; and
+     3) X is an arithmetic instruction (and thus has no unit restrictions).
 
-      secname = (char *) alloca (strlen (fnname) + 40);
-      sprintf (secname, ".mips16.call.%s%s",
-              fpret ? "fp." : "",
-              fnname);
-      stubname = (char *) alloca (strlen (fnname) + 20);
-      sprintf (stubname, "__call_stub_%s%s",
-              fpret ? "fp_" : "",
-              fnname);
-      stubid = get_identifier (stubname);
-      stubdecl = build_decl (FUNCTION_DECL, stubid,
-                            build_function_type (void_type_node, NULL_TREE));
-      DECL_SECTION_NAME (stubdecl) = build_string (strlen (secname), secname);
-      DECL_RESULT (stubdecl) = build_decl (RESULT_DECL, NULL_TREE, void_type_node);
+     If INSN1 is the last instruction blocking X, it would better to
+     choose (INSN1, X) over (INSN2, INSN1).  */
+  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;
 
-      fprintf (asm_out_file, "\t# Stub function to call %s%s (",
-              (fpret
-               ? (GET_MODE (retval) == SFmode ? "float " : "double ")
-               : ""),
-              fnname);
-      need_comma = 0;
-      for (f = (unsigned int) fp_code; f != 0; f >>= 2)
+  if (vr4130_last_insn != 0
+      && recog_memoized (insn1) >= 0
+      && recog_memoized (insn2) >= 0)
+    {
+      /* See whether INSN1 and INSN2 use different execution units,
+        or if they are both ALU-type instructions.  If so, they can
+        probably execute in parallel.  */
+      enum attr_vr4130_class class1 = get_attr_vr4130_class (insn1);
+      enum attr_vr4130_class class2 = get_attr_vr4130_class (insn2);
+      if (class1 != class2 || class1 == VR4130_CLASS_ALU)
        {
-         fprintf (asm_out_file, "%s%s",
-                  need_comma ? ", " : "",
-                  (f & 3) == 1 ? "float" : "double");
-         need_comma = 1;
-       }
-      fprintf (asm_out_file, ")\n");
+         /* If only one of the instructions has a dependence on
+            vr4130_last_insn, prefer to schedule the other one first.  */
+         bool dep1 = vr4130_true_reg_dependence_p (insn1);
+         bool dep2 = vr4130_true_reg_dependence_p (insn2);
+         if (dep1 != dep2)
+           return dep1;
 
-      fprintf (asm_out_file, "\t.set\tnomips16\n");
-      assemble_start_function (stubdecl, stubname);
+         /* Prefer to schedule INSN2 ahead of INSN1 if vr4130_last_insn
+            is not an ALU-type instruction and if INSN1 uses the same
+            execution unit.  (Note that if this condition holds, we already
+            know that INSN2 uses a different execution unit.)  */
+         if (class1 != VR4130_CLASS_ALU
+             && recog_memoized (vr4130_last_insn) >= 0
+             && class1 == get_attr_vr4130_class (vr4130_last_insn))
+           return true;
+       }
+    }
+  return false;
+}
 
-      if (!FUNCTION_NAME_ALREADY_DECLARED)
-       {
-         fputs ("\t.ent\t", asm_out_file);
-         assemble_name (asm_out_file, stubname);
-         fputs ("\n", asm_out_file);
+/* A TUNE_MIPS4130 helper function.  (READY, NREADY) describes a ready
+   queue with at least two instructions.  Swap the first two if
+   vr4130_swap_insns_p says that it could be worthwhile.  */
 
-         assemble_name (asm_out_file, stubname);
-         fputs (":\n", asm_out_file);
-       }
+static void
+vr4130_reorder (rtx *ready, int nready)
+{
+  if (vr4130_swap_insns_p (ready[nready - 1], ready[nready - 2]))
+    mips_promote_ready (ready, nready - 2, nready - 1);
+}
+\f
+/* Record whether last 74k AGEN instruction was a load or store.  */
 
-      /* We build the stub code by hand.  That's the only way we can
-        do it, since we can't generate 32-bit code during a 16-bit
-        compilation.  */
+static enum attr_type mips_last_74k_agen_insn = TYPE_UNKNOWN;
 
-      /* We don't want the assembler to insert any nops here.  */
-      fprintf (asm_out_file, "\t.set\tnoreorder\n");
+/* Initialize mips_last_74k_agen_insn from INSN.  A null argument
+   resets to TYPE_UNKNOWN state.  */
 
-      mips16_fp_args (asm_out_file, fp_code, 0);
+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;
+    }
+}
 
-      if (! fpret)
-       {
-         fprintf (asm_out_file, "\t.set\tnoat\n");
-         fprintf (asm_out_file, "\tla\t%s,%s\n", reg_names[GP_REG_FIRST + 1],
-                  fnname);
-         fprintf (asm_out_file, "\tjr\t%s\n", reg_names[GP_REG_FIRST + 1]);
-         fprintf (asm_out_file, "\t.set\tat\n");
-         /* Unfortunately, we can't fill the jump delay slot.  We
-            can't fill with one of the mtc1 instructions, because the
-            result is not available for one instruction, so if the
-            very first instruction in the function refers to the
-            register, it will see the wrong value.  */
-         fprintf (asm_out_file, "\tnop\n");
-       }
-      else
-       {
-         fprintf (asm_out_file, "\tmove\t%s,%s\n",
-                  reg_names[GP_REG_FIRST + 18], reg_names[GP_REG_FIRST + 31]);
-         fprintf (asm_out_file, "\tjal\t%s\n", fnname);
-         /* As above, we can't fill the delay slot.  */
-         fprintf (asm_out_file, "\tnop\n");
-         switch (GET_MODE (retval))
-           {
-           case SCmode:
-             fprintf (asm_out_file, "\tmfc1\t%s,%s\n",
-                      reg_names[GP_REG_FIRST + 3],
-                      reg_names[FP_REG_FIRST + MAX_FPRS_PER_FMT]);
-             /* Fall though.  */
-           case SFmode:
-             fprintf (asm_out_file, "\tmfc1\t%s,%s\n",
-                      reg_names[GP_REG_FIRST + 2],
-                      reg_names[FP_REG_FIRST + 0]);
-             if (GET_MODE (retval) == SCmode && TARGET_64BIT)
-               {
-                 /* On 64-bit targets, complex floats are returned in
-                    a single GPR, such that "sd" on a suitably-aligned
-                    target would store the value correctly.  */
-                 fprintf (asm_out_file, "\tdsll\t%s,%s,32\n",
-                          reg_names[GP_REG_FIRST + 2 + TARGET_LITTLE_ENDIAN],
-                          reg_names[GP_REG_FIRST + 2 + TARGET_LITTLE_ENDIAN]);
-                 fprintf (asm_out_file, "\tor\t%s,%s,%s\n",
-                          reg_names[GP_REG_FIRST + 2],
-                          reg_names[GP_REG_FIRST + 2],
-                          reg_names[GP_REG_FIRST + 3]);
-               }
-             break;
+/* 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.  */
 
-           case DCmode:
-             mips16_fpret_double (GP_REG_FIRST + 2 + (8 / UNITS_PER_WORD),
-                                  FP_REG_FIRST + MAX_FPRS_PER_FMT);
-             /* Fall though.  */
-           case DFmode:
-           case V2SFmode:
-             mips16_fpret_double (GP_REG_FIRST + 2, FP_REG_FIRST + 0);
-             break;
+static void
+mips_74k_agen_reorder (rtx *ready, int nready)
+{
+  int i;
+  int store_pos, load_pos;
 
-           default:
-             gcc_unreachable ();
-           }
-         fprintf (asm_out_file, "\tj\t%s\n", reg_names[GP_REG_FIRST + 18]);
-         /* As above, we can't fill the delay slot.  */
-         fprintf (asm_out_file, "\tnop\n");
-       }
+  store_pos = -1;
+  load_pos = -1;
 
-      fprintf (asm_out_file, "\t.set\treorder\n");
+  for (i = nready - 1; i >= 0; i--)
+    {
+      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;
 
-#ifdef ASM_DECLARE_FUNCTION_SIZE
-      ASM_DECLARE_FUNCTION_SIZE (asm_out_file, stubname, stubdecl);
-#endif
+         case TYPE_LOAD:
+           if (load_pos == -1)
+             load_pos = i;
+           break;
 
-      if (!FUNCTION_NAME_ALREADY_DECLARED)
-       {
-         fputs ("\t.end\t", asm_out_file);
-         assemble_name (asm_out_file, stubname);
-         fputs ("\n", asm_out_file);
-       }
+         default:
+           break;
+         }
+    }
 
-      /* Record this stub.  */
-      l = (struct mips16_stub *) xmalloc (sizeof *l);
-      l->name = xstrdup (fnname);
-      l->fpret = fpret;
-      l->next = mips16_stubs;
-      mips16_stubs = l;
-    }
-
-  /* If we expect a floating point return value, but we've built a
-     stub which does not expect one, then we're in trouble.  We can't
-     use the existing stub, because it won't handle the floating point
-     value.  We can't build a new stub, because the linker won't know
-     which stub to use for the various calls in this object file.
-     Fortunately, this case is illegal, since it means that a function
-     was declared in two different ways in a single compilation.  */
-  if (fpret && ! l->fpret)
-    error ("cannot handle inconsistent calls to %qs", fnname);
-
-  if (retval == NULL_RTX)
-    insn = gen_call_internal_direct (fn, arg_size);
-  else
-    insn = gen_call_value_internal_direct (retval, fn, arg_size);
-  insn = emit_call_insn (insn);
-
-  /* If we are calling a stub which handles a floating point return
-     value, we need to arrange to save $18 in the prologue.  We do
-     this by marking the function call as using the register.  The
-     prologue will later see that it is used, and emit code to save
-     it.  */
-  if (l->fpret)
-    CALL_INSN_FUNCTION_USAGE (insn) =
-      gen_rtx_EXPR_LIST (VOIDmode,
-                        gen_rtx_USE (VOIDmode, gen_rtx_REG (word_mode, 18)),
-                        CALL_INSN_FUNCTION_USAGE (insn));
-
-  /* Return 1 to tell the caller that we've generated the call
-     insn.  */
-  return 1;
-}
-
-/* An entry in the mips16 constant pool.  VALUE is the pool constant,
-   MODE is its mode, and LABEL is the CODE_LABEL associated with it.  */
-
-struct mips16_constant {
-  struct mips16_constant *next;
-  rtx value;
-  rtx label;
-  enum machine_mode mode;
-};
-
-/* Information about an incomplete mips16 constant pool.  FIRST is the
-   first constant, HIGHEST_ADDRESS is the highest address that the first
-   byte of the pool can have, and INSN_ADDRESS is the current instruction
-   address.  */
-
-struct mips16_constant_pool {
-  struct mips16_constant *first;
-  int highest_address;
-  int insn_address;
-};
-
-/* Add constant VALUE to POOL and return its label.  MODE is the
-   value's mode (used for CONST_INTs, etc.).  */
-
-static rtx
-add_constant (struct mips16_constant_pool *pool,
-             rtx value, enum machine_mode mode)
-{
-  struct mips16_constant **p, *c;
-  bool first_of_size_p;
-
-  /* See whether the constant is already in the pool.  If so, return the
-     existing label, otherwise leave P pointing to the place where the
-     constant should be added.
+  if (load_pos == -1 || store_pos == -1)
+    return;
 
-     Keep the pool sorted in increasing order of mode size so that we can
-     reduce the number of alignments needed.  */
-  first_of_size_p = true;
-  for (p = &pool->first; *p != 0; p = &(*p)->next)
+  switch (mips_last_74k_agen_insn)
     {
-      if (mode == (*p)->mode && rtx_equal_p (value, (*p)->value))
-       return (*p)->label;
-      if (GET_MODE_SIZE (mode) < GET_MODE_SIZE ((*p)->mode))
-       break;
-      if (GET_MODE_SIZE (mode) == GET_MODE_SIZE ((*p)->mode))
-       first_of_size_p = false;
+    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;
     }
+}
+\f
+/* Implement TARGET_SCHED_INIT.  */
 
-  /* In the worst case, the constant needed by the earliest instruction
-     will end up at the end of the pool.  The entire pool must then be
-     accessible from that instruction.
-
-     When adding the first constant, set the pool's highest address to
-     the address of the first out-of-range byte.  Adjust this address
-     downwards each time a new constant is added.  */
-  if (pool->first == 0)
-    /* For pc-relative lw, addiu and daddiu instructions, the base PC value
-       is the address of the instruction with the lowest two bits clear.
-       The base PC value for ld has the lowest three bits clear.  Assume
-       the worst case here.  */
-    pool->highest_address = pool->insn_address - (UNITS_PER_WORD - 2) + 0x8000;
-  pool->highest_address -= GET_MODE_SIZE (mode);
-  if (first_of_size_p)
-    /* Take into account the worst possible padding due to alignment.  */
-    pool->highest_address -= GET_MODE_SIZE (mode) - 1;
-
-  /* Create a new entry.  */
-  c = (struct mips16_constant *) xmalloc (sizeof *c);
-  c->value = value;
-  c->mode = mode;
-  c->label = gen_label_rtx ();
-  c->next = *p;
-  *p = c;
-
-  return c->label;
+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);
 }
 
-/* Output constant VALUE after instruction INSN and return the last
-   instruction emitted.  MODE is the mode of the constant.  */
+/* Implement TARGET_SCHED_REORDER and TARG_SCHED_REORDER2.  */
 
-static rtx
-dump_constants_1 (enum machine_mode mode, rtx value, rtx insn)
+static int
+mips_sched_reorder (FILE *file ATTRIBUTE_UNUSED, int verbose ATTRIBUTE_UNUSED,
+                   rtx *ready, int *nreadyp, int cycle ATTRIBUTE_UNUSED)
 {
-  if (SCALAR_INT_MODE_P (mode)
-      || ALL_SCALAR_FRACT_MODE_P (mode)
-      || ALL_SCALAR_ACCUM_MODE_P (mode))
-    {
-      rtx size = GEN_INT (GET_MODE_SIZE (mode));
-      return emit_insn_after (gen_consttable_int (value, size), insn);
-    }
+  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 ();
+}
 
-  if (SCALAR_FLOAT_MODE_P (mode))
-    return emit_insn_after (gen_consttable_float (value), insn);
+/* Implement TARGET_SCHED_VARIABLE_ISSUE.  */
 
-  if (VECTOR_MODE_P (mode))
+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)))
     {
-      int i;
+    case USE:
+    case CLOBBER:
+      /* Don't count USEs and CLOBBERs against the issue rate.  */
+      break;
 
-      for (i = 0; i < CONST_VECTOR_NUNITS (value); i++)
-       insn = dump_constants_1 (GET_MODE_INNER (mode),
-                                CONST_VECTOR_ELT (value, i), insn);
-      return insn;
+    default:
+      more--;
+      if (!reload_completed && TUNE_MACC_CHAINS)
+       mips_macc_chains_record (insn);
+      vr4130_last_insn = insn;
+      break;
     }
-
-  gcc_unreachable ();
+  return more;
 }
+\f
+/* Given that we have an rtx of the form (prefetch ... WRITE LOCALITY),
+   return the first operand of the associated "pref" or "prefx" insn.  */
 
+rtx
+mips_prefetch_cookie (rtx write, rtx locality)
+{
+  /* store_streamed / load_streamed.  */
+  if (INTVAL (locality) <= 0)
+    return GEN_INT (INTVAL (write) + 4);
 
-/* Dump out the constants in CONSTANTS after INSN.  */
+  /* store / load.  */
+  if (INTVAL (locality) <= 2)
+    return write;
 
-static void
-dump_constants (struct mips16_constant *constants, rtx insn)
+  /* store_retained / load_retained.  */
+  return GEN_INT (INTVAL (write) + 6);
+}
+\f
+/* MIPS builtin function support. */
+
+struct builtin_description
 {
-  struct mips16_constant *c, *next;
-  int align;
+  /* The code of the main .md file instruction.  See mips_builtin_type
+     for more information.  */
+  enum insn_code icode;
 
-  align = 0;
-  for (c = constants; c != NULL; c = next)
-    {
-      /* If necessary, increase the alignment of PC.  */
-      if (align < GET_MODE_SIZE (c->mode))
-       {
-         int align_log = floor_log2 (GET_MODE_SIZE (c->mode));
-         insn = emit_insn_after (gen_align (GEN_INT (align_log)), insn);
-       }
-      align = GET_MODE_SIZE (c->mode);
+  /* The floating-point comparison code to use with ICODE, if any.  */
+  enum mips_fp_condition cond;
 
-      insn = emit_label_after (c->label, insn);
-      insn = dump_constants_1 (c->mode, c->value, insn);
+  /* The name of the builtin function.  */
+  const char *name;
 
-      next = c->next;
-      free (c);
-    }
+  /* Specifies how the function should be expanded.  */
+  enum mips_builtin_type builtin_type;
 
-  emit_barrier_after (insn);
-}
+  /* The function's prototype.  */
+  enum mips_function_type function_type;
 
-/* Return the length of instruction INSN.  */
+  /* The target flags required for this function.  */
+  int target_flags;
+};
 
-static int
-mips16_insn_length (rtx insn)
-{
-  if (JUMP_P (insn))
-    {
-      rtx body = PATTERN (insn);
-      if (GET_CODE (body) == ADDR_VEC)
-       return GET_MODE_SIZE (GET_MODE (body)) * XVECLEN (body, 0);
-      if (GET_CODE (body) == ADDR_DIFF_VEC)
-       return GET_MODE_SIZE (GET_MODE (body)) * XVECLEN (body, 1);
-    }
-  return get_attr_length (insn);
-}
-
-/* If *X is a symbolic constant that refers to the constant pool, add
-   the constant to POOL and rewrite *X to use the constant's label.  */
-
-static void
-mips16_rewrite_pool_constant (struct mips16_constant_pool *pool, rtx *x)
-{
-  rtx base, offset, label;
-
-  split_const (*x, &base, &offset);
-  if (GET_CODE (base) == SYMBOL_REF && CONSTANT_POOL_ADDRESS_P (base))
-    {
-      label = add_constant (pool, get_pool_constant (base),
-                           get_pool_mode (base));
-      base = gen_rtx_LABEL_REF (Pmode, label);
-      *x = mips_unspec_address_offset (base, offset, SYMBOL_PC_RELATIVE);
-    }
-}
-
-/* This structure is used to communicate with mips16_rewrite_pool_refs.
-   INSN is the instruction we're rewriting and POOL points to the current
-   constant pool.  */
-struct mips16_rewrite_pool_refs_info {
-  rtx insn;
-  struct mips16_constant_pool *pool;
-};
-
-/* Rewrite *X so that constant pool references refer to the constant's
-   label instead.  DATA points to a mips16_rewrite_pool_refs_info
-   structure.  */
-
-static int
-mips16_rewrite_pool_refs (rtx *x, void *data)
-{
-  struct mips16_rewrite_pool_refs_info *info = data;
+/* Define a MIPS_BUILTIN_DIRECT function for instruction CODE_FOR_mips_<INSN>.
+   FUNCTION_TYPE and TARGET_FLAGS are builtin_description fields.  */
+#define DIRECT_BUILTIN(INSN, FUNCTION_TYPE, TARGET_FLAGS)              \
+  { CODE_FOR_mips_ ## INSN, 0, "__builtin_mips_" #INSN,                        \
+    MIPS_BUILTIN_DIRECT, FUNCTION_TYPE, TARGET_FLAGS }
 
-  if (force_to_mem_operand (*x, Pmode))
-    {
-      rtx mem = force_const_mem (GET_MODE (*x), *x);
-      validate_change (info->insn, x, mem, false);
-    }
+/* Define __builtin_mips_<INSN>_<COND>_{s,d}, both of which require
+   TARGET_FLAGS.  */
+#define CMP_SCALAR_BUILTINS(INSN, COND, TARGET_FLAGS)                  \
+  { CODE_FOR_mips_ ## INSN ## _cond_s, MIPS_FP_COND_ ## COND,          \
+    "__builtin_mips_" #INSN "_" #COND "_s",                            \
+    MIPS_BUILTIN_CMP_SINGLE, MIPS_INT_FTYPE_SF_SF, TARGET_FLAGS },     \
+  { CODE_FOR_mips_ ## INSN ## _cond_d, MIPS_FP_COND_ ## COND,          \
+    "__builtin_mips_" #INSN "_" #COND "_d",                            \
+    MIPS_BUILTIN_CMP_SINGLE, MIPS_INT_FTYPE_DF_DF, TARGET_FLAGS }
 
-  if (MEM_P (*x))
-    {
-      mips16_rewrite_pool_constant (info->pool, &XEXP (*x, 0));
-      return -1;
-    }
+/* Define __builtin_mips_{any,all,upper,lower}_<INSN>_<COND>_ps.
+   The lower and upper forms require TARGET_FLAGS while the any and all
+   forms require MASK_MIPS3D.  */
+#define CMP_PS_BUILTINS(INSN, COND, TARGET_FLAGS)                      \
+  { CODE_FOR_mips_ ## INSN ## _cond_ps, MIPS_FP_COND_ ## COND,         \
+    "__builtin_mips_any_" #INSN "_" #COND "_ps",                       \
+    MIPS_BUILTIN_CMP_ANY, MIPS_INT_FTYPE_V2SF_V2SF, MASK_MIPS3D },     \
+  { CODE_FOR_mips_ ## INSN ## _cond_ps, MIPS_FP_COND_ ## COND,         \
+    "__builtin_mips_all_" #INSN "_" #COND "_ps",                       \
+    MIPS_BUILTIN_CMP_ALL, MIPS_INT_FTYPE_V2SF_V2SF, MASK_MIPS3D },     \
+  { CODE_FOR_mips_ ## INSN ## _cond_ps, MIPS_FP_COND_ ## COND,         \
+    "__builtin_mips_lower_" #INSN "_" #COND "_ps",                     \
+    MIPS_BUILTIN_CMP_LOWER, MIPS_INT_FTYPE_V2SF_V2SF, TARGET_FLAGS },  \
+  { CODE_FOR_mips_ ## INSN ## _cond_ps, MIPS_FP_COND_ ## COND,         \
+    "__builtin_mips_upper_" #INSN "_" #COND "_ps",                     \
+    MIPS_BUILTIN_CMP_UPPER, MIPS_INT_FTYPE_V2SF_V2SF, TARGET_FLAGS }
 
-  if (TARGET_MIPS16_TEXT_LOADS)
-    mips16_rewrite_pool_constant (info->pool, x);
+/* Define __builtin_mips_{any,all}_<INSN>_<COND>_4s.  The functions
+   require MASK_MIPS3D.  */
+#define CMP_4S_BUILTINS(INSN, COND)                                    \
+  { CODE_FOR_mips_ ## INSN ## _cond_4s, MIPS_FP_COND_ ## COND,         \
+    "__builtin_mips_any_" #INSN "_" #COND "_4s",                       \
+    MIPS_BUILTIN_CMP_ANY, MIPS_INT_FTYPE_V2SF_V2SF_V2SF_V2SF,          \
+    MASK_MIPS3D },                                                     \
+  { CODE_FOR_mips_ ## INSN ## _cond_4s, MIPS_FP_COND_ ## COND,         \
+    "__builtin_mips_all_" #INSN "_" #COND "_4s",                       \
+    MIPS_BUILTIN_CMP_ALL, MIPS_INT_FTYPE_V2SF_V2SF_V2SF_V2SF,          \
+    MASK_MIPS3D }
 
-  return GET_CODE (*x) == CONST ? -1 : 0;
-}
+/* Define __builtin_mips_mov{t,f}_<INSN>_<COND>_ps.  The comparison
+   instruction requires TARGET_FLAGS.  */
+#define MOVTF_BUILTINS(INSN, COND, TARGET_FLAGS)                       \
+  { CODE_FOR_mips_ ## INSN ## _cond_ps, MIPS_FP_COND_ ## COND,         \
+    "__builtin_mips_movt_" #INSN "_" #COND "_ps",                      \
+    MIPS_BUILTIN_MOVT, MIPS_V2SF_FTYPE_V2SF_V2SF_V2SF_V2SF,            \
+    TARGET_FLAGS },                                                    \
+  { CODE_FOR_mips_ ## INSN ## _cond_ps, MIPS_FP_COND_ ## COND,         \
+    "__builtin_mips_movf_" #INSN "_" #COND "_ps",                      \
+    MIPS_BUILTIN_MOVF, MIPS_V2SF_FTYPE_V2SF_V2SF_V2SF_V2SF,            \
+    TARGET_FLAGS }
 
-/* Build MIPS16 constant pools.  */
+/* Define all the builtins related to c.cond.fmt condition COND.  */
+#define CMP_BUILTINS(COND)                                             \
+  MOVTF_BUILTINS (c, COND, MASK_PAIRED_SINGLE_FLOAT),                  \
+  MOVTF_BUILTINS (cabs, COND, MASK_MIPS3D),                            \
+  CMP_SCALAR_BUILTINS (cabs, COND, MASK_MIPS3D),                       \
+  CMP_PS_BUILTINS (c, COND, MASK_PAIRED_SINGLE_FLOAT),                 \
+  CMP_PS_BUILTINS (cabs, COND, MASK_MIPS3D),                           \
+  CMP_4S_BUILTINS (c, COND),                                           \
+  CMP_4S_BUILTINS (cabs, COND)
 
-static void
-mips16_lay_out_constants (void)
+static const struct builtin_description mips_bdesc[] =
 {
-  struct mips16_constant_pool pool;
-  struct mips16_rewrite_pool_refs_info info;
-  rtx insn, barrier;
-
-  if (!TARGET_MIPS16_PCREL_LOADS)
-    return;
-
-  barrier = 0;
-  memset (&pool, 0, sizeof (pool));
-  for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
-    {
-      /* Rewrite constant pool references in INSN.  */
-      if (INSN_P (insn))
-       {
-         info.insn = insn;
-         info.pool = &pool;
-         for_each_rtx (&PATTERN (insn), mips16_rewrite_pool_refs, &info);
-       }
-
-      pool.insn_address += mips16_insn_length (insn);
-
-      if (pool.first != NULL)
-       {
-         /* If there are no natural barriers between the first user of
-            the pool and the highest acceptable address, we'll need to
-            create a new instruction to jump around the constant pool.
-            In the worst case, this instruction will be 4 bytes long.
+  DIRECT_BUILTIN (pll_ps, MIPS_V2SF_FTYPE_V2SF_V2SF, MASK_PAIRED_SINGLE_FLOAT),
+  DIRECT_BUILTIN (pul_ps, MIPS_V2SF_FTYPE_V2SF_V2SF, MASK_PAIRED_SINGLE_FLOAT),
+  DIRECT_BUILTIN (plu_ps, MIPS_V2SF_FTYPE_V2SF_V2SF, MASK_PAIRED_SINGLE_FLOAT),
+  DIRECT_BUILTIN (puu_ps, MIPS_V2SF_FTYPE_V2SF_V2SF, MASK_PAIRED_SINGLE_FLOAT),
+  DIRECT_BUILTIN (cvt_ps_s, MIPS_V2SF_FTYPE_SF_SF, MASK_PAIRED_SINGLE_FLOAT),
+  DIRECT_BUILTIN (cvt_s_pl, MIPS_SF_FTYPE_V2SF, MASK_PAIRED_SINGLE_FLOAT),
+  DIRECT_BUILTIN (cvt_s_pu, MIPS_SF_FTYPE_V2SF, MASK_PAIRED_SINGLE_FLOAT),
+  DIRECT_BUILTIN (abs_ps, MIPS_V2SF_FTYPE_V2SF, MASK_PAIRED_SINGLE_FLOAT),
 
-            If it's too late to do this transformation after INSN,
-            do it immediately before INSN.  */
-         if (barrier == 0 && pool.insn_address + 4 > pool.highest_address)
-           {
-             rtx label, jump;
+  DIRECT_BUILTIN (alnv_ps, MIPS_V2SF_FTYPE_V2SF_V2SF_INT,
+                 MASK_PAIRED_SINGLE_FLOAT),
+  DIRECT_BUILTIN (addr_ps, MIPS_V2SF_FTYPE_V2SF_V2SF, MASK_MIPS3D),
+  DIRECT_BUILTIN (mulr_ps, MIPS_V2SF_FTYPE_V2SF_V2SF, MASK_MIPS3D),
+  DIRECT_BUILTIN (cvt_pw_ps, MIPS_V2SF_FTYPE_V2SF, MASK_MIPS3D),
+  DIRECT_BUILTIN (cvt_ps_pw, MIPS_V2SF_FTYPE_V2SF, MASK_MIPS3D),
 
-             label = gen_label_rtx ();
+  DIRECT_BUILTIN (recip1_s, MIPS_SF_FTYPE_SF, MASK_MIPS3D),
+  DIRECT_BUILTIN (recip1_d, MIPS_DF_FTYPE_DF, MASK_MIPS3D),
+  DIRECT_BUILTIN (recip1_ps, MIPS_V2SF_FTYPE_V2SF, MASK_MIPS3D),
+  DIRECT_BUILTIN (recip2_s, MIPS_SF_FTYPE_SF_SF, MASK_MIPS3D),
+  DIRECT_BUILTIN (recip2_d, MIPS_DF_FTYPE_DF_DF, MASK_MIPS3D),
+  DIRECT_BUILTIN (recip2_ps, MIPS_V2SF_FTYPE_V2SF_V2SF, MASK_MIPS3D),
 
-             jump = emit_jump_insn_before (gen_jump (label), insn);
-             JUMP_LABEL (jump) = label;
-             LABEL_NUSES (label) = 1;
-             barrier = emit_barrier_after (jump);
+  DIRECT_BUILTIN (rsqrt1_s, MIPS_SF_FTYPE_SF, MASK_MIPS3D),
+  DIRECT_BUILTIN (rsqrt1_d, MIPS_DF_FTYPE_DF, MASK_MIPS3D),
+  DIRECT_BUILTIN (rsqrt1_ps, MIPS_V2SF_FTYPE_V2SF, MASK_MIPS3D),
+  DIRECT_BUILTIN (rsqrt2_s, MIPS_SF_FTYPE_SF_SF, MASK_MIPS3D),
+  DIRECT_BUILTIN (rsqrt2_d, MIPS_DF_FTYPE_DF_DF, MASK_MIPS3D),
+  DIRECT_BUILTIN (rsqrt2_ps, MIPS_V2SF_FTYPE_V2SF_V2SF, MASK_MIPS3D),
 
-             emit_label_after (label, barrier);
-             pool.insn_address += 4;
-           }
+  MIPS_FP_CONDITIONS (CMP_BUILTINS)
+};
 
-         /* See whether the constant pool is now out of range of the first
-            user.  If so, output the constants after the previous barrier.
-            Note that any instructions between BARRIER and INSN (inclusive)
-            will use negative offsets to refer to the pool.  */
-         if (pool.insn_address > pool.highest_address)
-           {
-             dump_constants (pool.first, barrier);
-             pool.first = NULL;
-             barrier = 0;
-           }
-         else if (BARRIER_P (insn))
-           barrier = insn;
-       }
-    }
-  dump_constants (pool.first, get_last_insn ());
-}
-\f
-/* A temporary variable used by for_each_rtx callbacks, etc.  */
-static rtx mips_sim_insn;
+/* Builtin functions for the SB-1 processor.  */
 
-/* A structure representing the state of the processor pipeline.
-   Used by the mips_sim_* family of functions.  */
-struct mips_sim {
-  /* The maximum number of instructions that can be issued in a cycle.
-     (Caches mips_issue_rate.)  */
-  unsigned int issue_rate;
+#define CODE_FOR_mips_sqrt_ps CODE_FOR_sqrtv2sf2
 
-  /* The current simulation time.  */
-  unsigned int time;
+static const struct builtin_description sb1_bdesc[] =
+{
+  DIRECT_BUILTIN (sqrt_ps, MIPS_V2SF_FTYPE_V2SF, MASK_PAIRED_SINGLE_FLOAT)
+};
 
-  /* How many more instructions can be issued in the current cycle.  */
-  unsigned int insns_left;
+/* Builtin functions for DSP ASE.  */
 
-  /* LAST_SET[X].INSN is the last instruction to set register X.
-     LAST_SET[X].TIME is the time at which that instruction was issued.
-     INSN is null if no instruction has yet set register X.  */
-  struct {
-    rtx insn;
-    unsigned int time;
-  } last_set[FIRST_PSEUDO_REGISTER];
+#define CODE_FOR_mips_addq_ph CODE_FOR_addv2hi3
+#define CODE_FOR_mips_addu_qb CODE_FOR_addv4qi3
+#define CODE_FOR_mips_subq_ph CODE_FOR_subv2hi3
+#define CODE_FOR_mips_subu_qb CODE_FOR_subv4qi3
+#define CODE_FOR_mips_mul_ph CODE_FOR_mulv2hi3
 
-  /* The pipeline's current DFA state.  */
-  state_t dfa_state;
-};
+/* Define a MIPS_BUILTIN_DIRECT_NO_TARGET function for instruction
+   CODE_FOR_mips_<INSN>.  FUNCTION_TYPE and TARGET_FLAGS are
+   builtin_description fields.  */
+#define DIRECT_NO_TARGET_BUILTIN(INSN, FUNCTION_TYPE, TARGET_FLAGS)    \
+  { CODE_FOR_mips_ ## INSN, 0, "__builtin_mips_" #INSN,                        \
+    MIPS_BUILTIN_DIRECT_NO_TARGET, FUNCTION_TYPE, TARGET_FLAGS }
 
-/* Reset STATE to the initial simulation state.  */
+/* Define __builtin_mips_bposge<VALUE>.  <VALUE> is 32 for the MIPS32 DSP
+   branch instruction.  TARGET_FLAGS is a builtin_description field.  */
+#define BPOSGE_BUILTIN(VALUE, TARGET_FLAGS)                            \
+  { CODE_FOR_mips_bposge, 0, "__builtin_mips_bposge" #VALUE,           \
+    MIPS_BUILTIN_BPOSGE ## VALUE, MIPS_SI_FTYPE_VOID, TARGET_FLAGS }
 
-static void
-mips_sim_reset (struct mips_sim *state)
+static const struct builtin_description dsp_bdesc[] =
 {
-  state->time = 0;
-  state->insns_left = state->issue_rate;
-  memset (&state->last_set, 0, sizeof (state->last_set));
-  state_reset (state->dfa_state);
-}
+  DIRECT_BUILTIN (addq_ph, MIPS_V2HI_FTYPE_V2HI_V2HI, MASK_DSP),
+  DIRECT_BUILTIN (addq_s_ph, MIPS_V2HI_FTYPE_V2HI_V2HI, MASK_DSP),
+  DIRECT_BUILTIN (addq_s_w, MIPS_SI_FTYPE_SI_SI, MASK_DSP),
+  DIRECT_BUILTIN (addu_qb, MIPS_V4QI_FTYPE_V4QI_V4QI, MASK_DSP),
+  DIRECT_BUILTIN (addu_s_qb, MIPS_V4QI_FTYPE_V4QI_V4QI, MASK_DSP),
+  DIRECT_BUILTIN (subq_ph, MIPS_V2HI_FTYPE_V2HI_V2HI, MASK_DSP),
+  DIRECT_BUILTIN (subq_s_ph, MIPS_V2HI_FTYPE_V2HI_V2HI, MASK_DSP),
+  DIRECT_BUILTIN (subq_s_w, MIPS_SI_FTYPE_SI_SI, MASK_DSP),
+  DIRECT_BUILTIN (subu_qb, MIPS_V4QI_FTYPE_V4QI_V4QI, MASK_DSP),
+  DIRECT_BUILTIN (subu_s_qb, MIPS_V4QI_FTYPE_V4QI_V4QI, MASK_DSP),
+  DIRECT_BUILTIN (addsc, MIPS_SI_FTYPE_SI_SI, MASK_DSP),
+  DIRECT_BUILTIN (addwc, MIPS_SI_FTYPE_SI_SI, MASK_DSP),
+  DIRECT_BUILTIN (modsub, MIPS_SI_FTYPE_SI_SI, MASK_DSP),
+  DIRECT_BUILTIN (raddu_w_qb, MIPS_SI_FTYPE_V4QI, MASK_DSP),
+  DIRECT_BUILTIN (absq_s_ph, MIPS_V2HI_FTYPE_V2HI, MASK_DSP),
+  DIRECT_BUILTIN (absq_s_w, MIPS_SI_FTYPE_SI, MASK_DSP),
+  DIRECT_BUILTIN (precrq_qb_ph, MIPS_V4QI_FTYPE_V2HI_V2HI, MASK_DSP),
+  DIRECT_BUILTIN (precrq_ph_w, MIPS_V2HI_FTYPE_SI_SI, MASK_DSP),
+  DIRECT_BUILTIN (precrq_rs_ph_w, MIPS_V2HI_FTYPE_SI_SI, MASK_DSP),
+  DIRECT_BUILTIN (precrqu_s_qb_ph, MIPS_V4QI_FTYPE_V2HI_V2HI, MASK_DSP),
+  DIRECT_BUILTIN (preceq_w_phl, MIPS_SI_FTYPE_V2HI, MASK_DSP),
+  DIRECT_BUILTIN (preceq_w_phr, MIPS_SI_FTYPE_V2HI, MASK_DSP),
+  DIRECT_BUILTIN (precequ_ph_qbl, MIPS_V2HI_FTYPE_V4QI, MASK_DSP),
+  DIRECT_BUILTIN (precequ_ph_qbr, MIPS_V2HI_FTYPE_V4QI, MASK_DSP),
+  DIRECT_BUILTIN (precequ_ph_qbla, MIPS_V2HI_FTYPE_V4QI, MASK_DSP),
+  DIRECT_BUILTIN (precequ_ph_qbra, MIPS_V2HI_FTYPE_V4QI, MASK_DSP),
+  DIRECT_BUILTIN (preceu_ph_qbl, MIPS_V2HI_FTYPE_V4QI, MASK_DSP),
+  DIRECT_BUILTIN (preceu_ph_qbr, MIPS_V2HI_FTYPE_V4QI, MASK_DSP),
+  DIRECT_BUILTIN (preceu_ph_qbla, MIPS_V2HI_FTYPE_V4QI, MASK_DSP),
+  DIRECT_BUILTIN (preceu_ph_qbra, MIPS_V2HI_FTYPE_V4QI, MASK_DSP),
+  DIRECT_BUILTIN (shll_qb, MIPS_V4QI_FTYPE_V4QI_SI, MASK_DSP),
+  DIRECT_BUILTIN (shll_ph, MIPS_V2HI_FTYPE_V2HI_SI, MASK_DSP),
+  DIRECT_BUILTIN (shll_s_ph, MIPS_V2HI_FTYPE_V2HI_SI, MASK_DSP),
+  DIRECT_BUILTIN (shll_s_w, MIPS_SI_FTYPE_SI_SI, MASK_DSP),
+  DIRECT_BUILTIN (shrl_qb, MIPS_V4QI_FTYPE_V4QI_SI, MASK_DSP),
+  DIRECT_BUILTIN (shra_ph, MIPS_V2HI_FTYPE_V2HI_SI, MASK_DSP),
+  DIRECT_BUILTIN (shra_r_ph, MIPS_V2HI_FTYPE_V2HI_SI, MASK_DSP),
+  DIRECT_BUILTIN (shra_r_w, MIPS_SI_FTYPE_SI_SI, MASK_DSP),
+  DIRECT_BUILTIN (muleu_s_ph_qbl, MIPS_V2HI_FTYPE_V4QI_V2HI, MASK_DSP),
+  DIRECT_BUILTIN (muleu_s_ph_qbr, MIPS_V2HI_FTYPE_V4QI_V2HI, MASK_DSP),
+  DIRECT_BUILTIN (mulq_rs_ph, MIPS_V2HI_FTYPE_V2HI_V2HI, MASK_DSP),
+  DIRECT_BUILTIN (muleq_s_w_phl, MIPS_SI_FTYPE_V2HI_V2HI, MASK_DSP),
+  DIRECT_BUILTIN (muleq_s_w_phr, MIPS_SI_FTYPE_V2HI_V2HI, MASK_DSP),
+  DIRECT_BUILTIN (bitrev, MIPS_SI_FTYPE_SI, MASK_DSP),
+  DIRECT_BUILTIN (insv, MIPS_SI_FTYPE_SI_SI, MASK_DSP),
+  DIRECT_BUILTIN (repl_qb, MIPS_V4QI_FTYPE_SI, MASK_DSP),
+  DIRECT_BUILTIN (repl_ph, MIPS_V2HI_FTYPE_SI, MASK_DSP),
+  DIRECT_NO_TARGET_BUILTIN (cmpu_eq_qb, MIPS_VOID_FTYPE_V4QI_V4QI, MASK_DSP),
+  DIRECT_NO_TARGET_BUILTIN (cmpu_lt_qb, MIPS_VOID_FTYPE_V4QI_V4QI, MASK_DSP),
+  DIRECT_NO_TARGET_BUILTIN (cmpu_le_qb, MIPS_VOID_FTYPE_V4QI_V4QI, MASK_DSP),
+  DIRECT_BUILTIN (cmpgu_eq_qb, MIPS_SI_FTYPE_V4QI_V4QI, MASK_DSP),
+  DIRECT_BUILTIN (cmpgu_lt_qb, MIPS_SI_FTYPE_V4QI_V4QI, MASK_DSP),
+  DIRECT_BUILTIN (cmpgu_le_qb, MIPS_SI_FTYPE_V4QI_V4QI, MASK_DSP),
+  DIRECT_NO_TARGET_BUILTIN (cmp_eq_ph, MIPS_VOID_FTYPE_V2HI_V2HI, MASK_DSP),
+  DIRECT_NO_TARGET_BUILTIN (cmp_lt_ph, MIPS_VOID_FTYPE_V2HI_V2HI, MASK_DSP),
+  DIRECT_NO_TARGET_BUILTIN (cmp_le_ph, MIPS_VOID_FTYPE_V2HI_V2HI, MASK_DSP),
+  DIRECT_BUILTIN (pick_qb, MIPS_V4QI_FTYPE_V4QI_V4QI, MASK_DSP),
+  DIRECT_BUILTIN (pick_ph, MIPS_V2HI_FTYPE_V2HI_V2HI, MASK_DSP),
+  DIRECT_BUILTIN (packrl_ph, MIPS_V2HI_FTYPE_V2HI_V2HI, MASK_DSP),
+  DIRECT_NO_TARGET_BUILTIN (wrdsp, MIPS_VOID_FTYPE_SI_SI, MASK_DSP),
+  DIRECT_BUILTIN (rddsp, MIPS_SI_FTYPE_SI, MASK_DSP),
+  DIRECT_BUILTIN (lbux, MIPS_SI_FTYPE_PTR_SI, MASK_DSP),
+  DIRECT_BUILTIN (lhx, MIPS_SI_FTYPE_PTR_SI, MASK_DSP),
+  DIRECT_BUILTIN (lwx, MIPS_SI_FTYPE_PTR_SI, MASK_DSP),
+  BPOSGE_BUILTIN (32, MASK_DSP),
 
-/* Initialize STATE before its first use.  DFA_STATE points to an
-   allocated but uninitialized DFA state.  */
+  /* The following are for the MIPS DSP ASE REV 2.  */
+  DIRECT_BUILTIN (absq_s_qb, MIPS_V4QI_FTYPE_V4QI, MASK_DSPR2),
+  DIRECT_BUILTIN (addu_ph, MIPS_V2HI_FTYPE_V2HI_V2HI, MASK_DSPR2),
+  DIRECT_BUILTIN (addu_s_ph, MIPS_V2HI_FTYPE_V2HI_V2HI, MASK_DSPR2),
+  DIRECT_BUILTIN (adduh_qb, MIPS_V4QI_FTYPE_V4QI_V4QI, MASK_DSPR2),
+  DIRECT_BUILTIN (adduh_r_qb, MIPS_V4QI_FTYPE_V4QI_V4QI, MASK_DSPR2),
+  DIRECT_BUILTIN (append, MIPS_SI_FTYPE_SI_SI_SI, MASK_DSPR2),
+  DIRECT_BUILTIN (balign, MIPS_SI_FTYPE_SI_SI_SI, MASK_DSPR2),
+  DIRECT_BUILTIN (cmpgdu_eq_qb, MIPS_SI_FTYPE_V4QI_V4QI, MASK_DSPR2),
+  DIRECT_BUILTIN (cmpgdu_lt_qb, MIPS_SI_FTYPE_V4QI_V4QI, MASK_DSPR2),
+  DIRECT_BUILTIN (cmpgdu_le_qb, MIPS_SI_FTYPE_V4QI_V4QI, MASK_DSPR2),
+  DIRECT_BUILTIN (mul_ph, MIPS_V2HI_FTYPE_V2HI_V2HI, MASK_DSPR2),
+  DIRECT_BUILTIN (mul_s_ph, MIPS_V2HI_FTYPE_V2HI_V2HI, MASK_DSPR2),
+  DIRECT_BUILTIN (mulq_rs_w, MIPS_SI_FTYPE_SI_SI, MASK_DSPR2),
+  DIRECT_BUILTIN (mulq_s_ph, MIPS_V2HI_FTYPE_V2HI_V2HI, MASK_DSPR2),
+  DIRECT_BUILTIN (mulq_s_w, MIPS_SI_FTYPE_SI_SI, MASK_DSPR2),
+  DIRECT_BUILTIN (precr_qb_ph, MIPS_V4QI_FTYPE_V2HI_V2HI, MASK_DSPR2),
+  DIRECT_BUILTIN (precr_sra_ph_w, MIPS_V2HI_FTYPE_SI_SI_SI, MASK_DSPR2),
+  DIRECT_BUILTIN (precr_sra_r_ph_w, MIPS_V2HI_FTYPE_SI_SI_SI, MASK_DSPR2),
+  DIRECT_BUILTIN (prepend, MIPS_SI_FTYPE_SI_SI_SI, MASK_DSPR2),
+  DIRECT_BUILTIN (shra_qb, MIPS_V4QI_FTYPE_V4QI_SI, MASK_DSPR2),
+  DIRECT_BUILTIN (shra_r_qb, MIPS_V4QI_FTYPE_V4QI_SI, MASK_DSPR2),
+  DIRECT_BUILTIN (shrl_ph, MIPS_V2HI_FTYPE_V2HI_SI, MASK_DSPR2),
+  DIRECT_BUILTIN (subu_ph, MIPS_V2HI_FTYPE_V2HI_V2HI, MASK_DSPR2),
+  DIRECT_BUILTIN (subu_s_ph, MIPS_V2HI_FTYPE_V2HI_V2HI, MASK_DSPR2),
+  DIRECT_BUILTIN (subuh_qb, MIPS_V4QI_FTYPE_V4QI_V4QI, MASK_DSPR2),
+  DIRECT_BUILTIN (subuh_r_qb, MIPS_V4QI_FTYPE_V4QI_V4QI, MASK_DSPR2),
+  DIRECT_BUILTIN (addqh_ph, MIPS_V2HI_FTYPE_V2HI_V2HI, MASK_DSPR2),
+  DIRECT_BUILTIN (addqh_r_ph, MIPS_V2HI_FTYPE_V2HI_V2HI, MASK_DSPR2),
+  DIRECT_BUILTIN (addqh_w, MIPS_SI_FTYPE_SI_SI, MASK_DSPR2),
+  DIRECT_BUILTIN (addqh_r_w, MIPS_SI_FTYPE_SI_SI, MASK_DSPR2),
+  DIRECT_BUILTIN (subqh_ph, MIPS_V2HI_FTYPE_V2HI_V2HI, MASK_DSPR2),
+  DIRECT_BUILTIN (subqh_r_ph, MIPS_V2HI_FTYPE_V2HI_V2HI, MASK_DSPR2),
+  DIRECT_BUILTIN (subqh_w, MIPS_SI_FTYPE_SI_SI, MASK_DSPR2),
+  DIRECT_BUILTIN (subqh_r_w, MIPS_SI_FTYPE_SI_SI, MASK_DSPR2)
+};
 
-static void
-mips_sim_init (struct mips_sim *state, state_t dfa_state)
+static const struct builtin_description dsp_32only_bdesc[] =
 {
-  state->issue_rate = mips_issue_rate ();
-  state->dfa_state = dfa_state;
-  mips_sim_reset (state);
-}
-
-/* Advance STATE by one clock cycle.  */
+  DIRECT_BUILTIN (dpau_h_qbl, MIPS_DI_FTYPE_DI_V4QI_V4QI, MASK_DSP),
+  DIRECT_BUILTIN (dpau_h_qbr, MIPS_DI_FTYPE_DI_V4QI_V4QI, MASK_DSP),
+  DIRECT_BUILTIN (dpsu_h_qbl, MIPS_DI_FTYPE_DI_V4QI_V4QI, MASK_DSP),
+  DIRECT_BUILTIN (dpsu_h_qbr, MIPS_DI_FTYPE_DI_V4QI_V4QI, MASK_DSP),
+  DIRECT_BUILTIN (dpaq_s_w_ph, MIPS_DI_FTYPE_DI_V2HI_V2HI, MASK_DSP),
+  DIRECT_BUILTIN (dpsq_s_w_ph, MIPS_DI_FTYPE_DI_V2HI_V2HI, MASK_DSP),
+  DIRECT_BUILTIN (mulsaq_s_w_ph, MIPS_DI_FTYPE_DI_V2HI_V2HI, MASK_DSP),
+  DIRECT_BUILTIN (dpaq_sa_l_w, MIPS_DI_FTYPE_DI_SI_SI, MASK_DSP),
+  DIRECT_BUILTIN (dpsq_sa_l_w, MIPS_DI_FTYPE_DI_SI_SI, MASK_DSP),
+  DIRECT_BUILTIN (maq_s_w_phl, MIPS_DI_FTYPE_DI_V2HI_V2HI, MASK_DSP),
+  DIRECT_BUILTIN (maq_s_w_phr, MIPS_DI_FTYPE_DI_V2HI_V2HI, MASK_DSP),
+  DIRECT_BUILTIN (maq_sa_w_phl, MIPS_DI_FTYPE_DI_V2HI_V2HI, MASK_DSP),
+  DIRECT_BUILTIN (maq_sa_w_phr, MIPS_DI_FTYPE_DI_V2HI_V2HI, MASK_DSP),
+  DIRECT_BUILTIN (extr_w, MIPS_SI_FTYPE_DI_SI, MASK_DSP),
+  DIRECT_BUILTIN (extr_r_w, MIPS_SI_FTYPE_DI_SI, MASK_DSP),
+  DIRECT_BUILTIN (extr_rs_w, MIPS_SI_FTYPE_DI_SI, MASK_DSP),
+  DIRECT_BUILTIN (extr_s_h, MIPS_SI_FTYPE_DI_SI, MASK_DSP),
+  DIRECT_BUILTIN (extp, MIPS_SI_FTYPE_DI_SI, MASK_DSP),
+  DIRECT_BUILTIN (extpdp, MIPS_SI_FTYPE_DI_SI, MASK_DSP),
+  DIRECT_BUILTIN (shilo, MIPS_DI_FTYPE_DI_SI, MASK_DSP),
+  DIRECT_BUILTIN (mthlip, MIPS_DI_FTYPE_DI_SI, MASK_DSP),
 
-static void
-mips_sim_next_cycle (struct mips_sim *state)
-{
-  state->time++;
-  state->insns_left = state->issue_rate;
-  state_transition (state->dfa_state, 0);
-}
+  /* The following are for the MIPS DSP ASE REV 2.  */
+  DIRECT_BUILTIN (dpa_w_ph, MIPS_DI_FTYPE_DI_V2HI_V2HI, MASK_DSPR2),
+  DIRECT_BUILTIN (dps_w_ph, MIPS_DI_FTYPE_DI_V2HI_V2HI, MASK_DSPR2),
+  DIRECT_BUILTIN (madd, MIPS_DI_FTYPE_DI_SI_SI, MASK_DSPR2),
+  DIRECT_BUILTIN (maddu, MIPS_DI_FTYPE_DI_USI_USI, MASK_DSPR2),
+  DIRECT_BUILTIN (msub, MIPS_DI_FTYPE_DI_SI_SI, MASK_DSPR2),
+  DIRECT_BUILTIN (msubu, MIPS_DI_FTYPE_DI_USI_USI, MASK_DSPR2),
+  DIRECT_BUILTIN (mulsa_w_ph, MIPS_DI_FTYPE_DI_V2HI_V2HI, MASK_DSPR2),
+  DIRECT_BUILTIN (mult, MIPS_DI_FTYPE_SI_SI, MASK_DSPR2),
+  DIRECT_BUILTIN (multu, MIPS_DI_FTYPE_USI_USI, MASK_DSPR2),
+  DIRECT_BUILTIN (dpax_w_ph, MIPS_DI_FTYPE_DI_V2HI_V2HI, MASK_DSPR2),
+  DIRECT_BUILTIN (dpsx_w_ph, MIPS_DI_FTYPE_DI_V2HI_V2HI, MASK_DSPR2),
+  DIRECT_BUILTIN (dpaqx_s_w_ph, MIPS_DI_FTYPE_DI_V2HI_V2HI, MASK_DSPR2),
+  DIRECT_BUILTIN (dpaqx_sa_w_ph, MIPS_DI_FTYPE_DI_V2HI_V2HI, MASK_DSPR2),
+  DIRECT_BUILTIN (dpsqx_s_w_ph, MIPS_DI_FTYPE_DI_V2HI_V2HI, MASK_DSPR2),
+  DIRECT_BUILTIN (dpsqx_sa_w_ph, MIPS_DI_FTYPE_DI_V2HI_V2HI, MASK_DSPR2)
+};
 
-/* Advance simulation state STATE until instruction INSN can read
-   register REG.  */
+/* This helps provide a mapping from builtin function codes to bdesc
+   arrays.  */
 
-static void
-mips_sim_wait_reg (struct mips_sim *state, rtx insn, rtx reg)
+struct bdesc_map
 {
-  unsigned int i;
+  /* The builtin function table that this entry describes.  */
+  const struct builtin_description *bdesc;
 
-  for (i = 0; i < HARD_REGNO_NREGS (REGNO (reg), GET_MODE (reg)); i++)
-    if (state->last_set[REGNO (reg) + i].insn != 0)
-      {
-       unsigned int t;
+  /* The number of entries in the builtin function table.  */
+  unsigned int size;
 
-       t = state->last_set[REGNO (reg) + i].time;
-       t += insn_latency (state->last_set[REGNO (reg) + i].insn, insn);
-       while (state->time < t)
-         mips_sim_next_cycle (state);
-    }
-}
+  /* The target processor that supports these builtin functions.
+     PROCESSOR_MAX means we enable them for all processors.  */
+  enum processor_type proc;
 
-/* A for_each_rtx callback.  If *X is a register, advance simulation state
-   DATA until mips_sim_insn can read the register's value.  */
+  /* If the target has these flags, this builtin function table
+     will not be supported.  */
+  int unsupported_target_flags;
+};
 
-static int
-mips_sim_wait_regs_2 (rtx *x, void *data)
+static const struct bdesc_map bdesc_arrays[] =
 {
-  if (REG_P (*x))
-    mips_sim_wait_reg (data, mips_sim_insn, *x);
-  return 0;
-}
+  { mips_bdesc, ARRAY_SIZE (mips_bdesc), PROCESSOR_MAX, 0 },
+  { sb1_bdesc, ARRAY_SIZE (sb1_bdesc), PROCESSOR_SB1, 0 },
+  { dsp_bdesc, ARRAY_SIZE (dsp_bdesc), PROCESSOR_MAX, 0 },
+  { dsp_32only_bdesc, ARRAY_SIZE (dsp_32only_bdesc), PROCESSOR_MAX,
+    MASK_64BIT }
+};
 
-/* Call mips_sim_wait_regs_2 (R, DATA) for each register R mentioned in *X.  */
+/* Init builtin functions.  This is called from TARGET_INIT_BUILTIN.  */
 
 static void
-mips_sim_wait_regs_1 (rtx *x, void *data)
+mips_init_builtins (void)
 {
-  for_each_rtx (x, mips_sim_wait_regs_2, data);
-}
-
-/* Advance simulation state STATE until all of INSN's register
-   dependencies are satisfied.  */
+  const struct builtin_description *d;
+  const struct bdesc_map *m;
+  tree types[(int) MIPS_MAX_FTYPE_MAX];
+  tree V2SF_type_node;
+  tree V2HI_type_node;
+  tree V4QI_type_node;
+  unsigned int offset;
 
-static void
-mips_sim_wait_regs (struct mips_sim *state, rtx insn)
-{
-  mips_sim_insn = insn;
-  note_uses (&PATTERN (insn), mips_sim_wait_regs_1, state);
-}
+  /* We have only builtins for -mpaired-single, -mips3d and -mdsp.  */
+  if (!TARGET_PAIRED_SINGLE_FLOAT && !TARGET_DSP)
+    return;
 
-/* Advance simulation state STATE until the units required by
-   instruction INSN are available.  */
+  if (TARGET_PAIRED_SINGLE_FLOAT)
+    {
+      V2SF_type_node = build_vector_type_for_mode (float_type_node, V2SFmode);
 
-static void
-mips_sim_wait_units (struct mips_sim *state, rtx insn)
-{
-  state_t tmp_state;
+      types[MIPS_V2SF_FTYPE_V2SF]
+       = build_function_type_list (V2SF_type_node, V2SF_type_node, NULL_TREE);
 
-  tmp_state = alloca (state_size ());
-  while (state->insns_left == 0
-        || (memcpy (tmp_state, state->dfa_state, state_size ()),
-            state_transition (tmp_state, insn) >= 0))
-    mips_sim_next_cycle (state);
-}
+      types[MIPS_V2SF_FTYPE_V2SF_V2SF]
+       = build_function_type_list (V2SF_type_node,
+                                   V2SF_type_node, V2SF_type_node, NULL_TREE);
 
-/* Advance simulation state STATE until INSN is ready to issue.  */
+      types[MIPS_V2SF_FTYPE_V2SF_V2SF_INT]
+       = build_function_type_list (V2SF_type_node,
+                                   V2SF_type_node, V2SF_type_node,
+                                   integer_type_node, NULL_TREE);
 
-static void
-mips_sim_wait_insn (struct mips_sim *state, rtx insn)
-{
-  mips_sim_wait_regs (state, insn);
-  mips_sim_wait_units (state, insn);
-}
+      types[MIPS_V2SF_FTYPE_V2SF_V2SF_V2SF_V2SF]
+       = build_function_type_list (V2SF_type_node,
+                                   V2SF_type_node, V2SF_type_node,
+                                   V2SF_type_node, V2SF_type_node, NULL_TREE);
 
-/* mips_sim_insn has just set X.  Update the LAST_SET array
-   in simulation state DATA.  */
+      types[MIPS_V2SF_FTYPE_SF_SF]
+       = build_function_type_list (V2SF_type_node,
+                                   float_type_node, float_type_node, NULL_TREE);
 
-static void
-mips_sim_record_set (rtx x, const_rtx pat ATTRIBUTE_UNUSED, void *data)
-{
-  struct mips_sim *state;
-  unsigned int i;
+      types[MIPS_INT_FTYPE_V2SF_V2SF]
+       = build_function_type_list (integer_type_node,
+                                   V2SF_type_node, V2SF_type_node, NULL_TREE);
 
-  state = data;
-  if (REG_P (x))
-    for (i = 0; i < HARD_REGNO_NREGS (REGNO (x), GET_MODE (x)); i++)
-      {
-       state->last_set[REGNO (x) + i].insn = mips_sim_insn;
-       state->last_set[REGNO (x) + i].time = state->time;
-      }
-}
+      types[MIPS_INT_FTYPE_V2SF_V2SF_V2SF_V2SF]
+       = build_function_type_list (integer_type_node,
+                                   V2SF_type_node, V2SF_type_node,
+                                   V2SF_type_node, V2SF_type_node, NULL_TREE);
 
-/* Issue instruction INSN in scheduler state STATE.  Assume that INSN
-   can issue immediately (i.e., that mips_sim_wait_insn has already
-   been called).  */
+      types[MIPS_INT_FTYPE_SF_SF]
+       = build_function_type_list (integer_type_node,
+                                   float_type_node, float_type_node, NULL_TREE);
 
-static void
-mips_sim_issue_insn (struct mips_sim *state, rtx insn)
-{
-  state_transition (state->dfa_state, insn);
-  state->insns_left--;
+      types[MIPS_INT_FTYPE_DF_DF]
+       = build_function_type_list (integer_type_node,
+                                   double_type_node, double_type_node, NULL_TREE);
 
-  mips_sim_insn = insn;
-  note_stores (PATTERN (insn), mips_sim_record_set, state);
-}
+      types[MIPS_SF_FTYPE_V2SF]
+       = build_function_type_list (float_type_node, V2SF_type_node, NULL_TREE);
 
-/* Simulate issuing a NOP in state STATE.  */
+      types[MIPS_SF_FTYPE_SF]
+       = build_function_type_list (float_type_node,
+                                   float_type_node, NULL_TREE);
 
-static void
-mips_sim_issue_nop (struct mips_sim *state)
-{
-  if (state->insns_left == 0)
-    mips_sim_next_cycle (state);
-  state->insns_left--;
-}
+      types[MIPS_SF_FTYPE_SF_SF]
+       = build_function_type_list (float_type_node,
+                                   float_type_node, float_type_node, NULL_TREE);
 
-/* Update simulation state STATE so that it's ready to accept the instruction
-   after INSN.  INSN should be part of the main rtl chain, not a member of a
-   SEQUENCE.  */
+      types[MIPS_DF_FTYPE_DF]
+       = build_function_type_list (double_type_node,
+                                   double_type_node, NULL_TREE);
 
-static void
-mips_sim_finish_insn (struct mips_sim *state, rtx insn)
-{
-  /* If INSN is a jump with an implicit delay slot, simulate a nop.  */
-  if (JUMP_P (insn))
-    mips_sim_issue_nop (state);
+      types[MIPS_DF_FTYPE_DF_DF]
+       = build_function_type_list (double_type_node,
+                                   double_type_node, double_type_node, NULL_TREE);
+    }
 
-  switch (GET_CODE (SEQ_BEGIN (insn)))
+  if (TARGET_DSP)
     {
-    case CODE_LABEL:
-    case CALL_INSN:
-      /* We can't predict the processor state after a call or label.  */
-      mips_sim_reset (state);
-      break;
+      V2HI_type_node = build_vector_type_for_mode (intHI_type_node, V2HImode);
+      V4QI_type_node = build_vector_type_for_mode (intQI_type_node, V4QImode);
 
-    case JUMP_INSN:
-      /* The delay slots of branch likely instructions are only executed
-        when the branch is taken.  Therefore, if the caller has simulated
-        the delay slot instruction, STATE does not really reflect the state
-        of the pipeline for the instruction after the delay slot.  Also,
-        branch likely instructions tend to incur a penalty when not taken,
-        so there will probably be an extra delay between the branch and
-        the instruction after the delay slot.  */
-      if (INSN_ANNULLED_BRANCH_P (SEQ_BEGIN (insn)))
-       mips_sim_reset (state);
-      break;
+      types[MIPS_V2HI_FTYPE_V2HI_V2HI]
+       = build_function_type_list (V2HI_type_node,
+                                   V2HI_type_node, V2HI_type_node,
+                                   NULL_TREE);
 
-    default:
-      break;
-    }
-}
-\f
-/* The VR4130 pipeline issues aligned pairs of instructions together,
-   but it stalls the second instruction if it depends on the first.
-   In order to cut down the amount of logic required, this dependence
-   check is not based on a full instruction decode.  Instead, any non-SPECIAL
-   instruction is assumed to modify the register specified by bits 20-16
-   (which is usually the "rt" field).
+      types[MIPS_SI_FTYPE_SI_SI]
+       = build_function_type_list (intSI_type_node,
+                                   intSI_type_node, intSI_type_node,
+                                   NULL_TREE);
 
-   In beq, beql, bne and bnel instructions, the rt field is actually an
-   input, so we can end up with a false dependence between the branch
-   and its delay slot.  If this situation occurs in instruction INSN,
-   try to avoid it by swapping rs and rt.  */
+      types[MIPS_V4QI_FTYPE_V4QI_V4QI]
+       = build_function_type_list (V4QI_type_node,
+                                   V4QI_type_node, V4QI_type_node,
+                                   NULL_TREE);
 
-static void
-vr4130_avoid_branch_rt_conflict (rtx insn)
-{
-  rtx first, second;
+      types[MIPS_SI_FTYPE_V4QI]
+       = build_function_type_list (intSI_type_node,
+                                   V4QI_type_node,
+                                   NULL_TREE);
 
-  first = SEQ_BEGIN (insn);
-  second = SEQ_END (insn);
-  if (JUMP_P (first)
-      && NONJUMP_INSN_P (second)
-      && GET_CODE (PATTERN (first)) == SET
-      && GET_CODE (SET_DEST (PATTERN (first))) == PC
-      && GET_CODE (SET_SRC (PATTERN (first))) == IF_THEN_ELSE)
-    {
-      /* Check for the right kind of condition.  */
-      rtx cond = XEXP (SET_SRC (PATTERN (first)), 0);
-      if ((GET_CODE (cond) == EQ || GET_CODE (cond) == NE)
-         && REG_P (XEXP (cond, 0))
-         && REG_P (XEXP (cond, 1))
-         && reg_referenced_p (XEXP (cond, 1), PATTERN (second))
-         && !reg_referenced_p (XEXP (cond, 0), PATTERN (second)))
-       {
-         /* SECOND mentions the rt register but not the rs register.  */
-         rtx tmp = XEXP (cond, 0);
-         XEXP (cond, 0) = XEXP (cond, 1);
-         XEXP (cond, 1) = tmp;
-       }
-    }
-}
+      types[MIPS_V2HI_FTYPE_V2HI]
+       = build_function_type_list (V2HI_type_node,
+                                   V2HI_type_node,
+                                   NULL_TREE);
 
-/* Implement -mvr4130-align.  Go through each basic block and simulate the
-   processor pipeline.  If we find that a pair of instructions could execute
-   in parallel, and the first of those instruction is not 8-byte aligned,
-   insert a nop to make it aligned.  */
+      types[MIPS_SI_FTYPE_SI]
+       = build_function_type_list (intSI_type_node,
+                                   intSI_type_node,
+                                   NULL_TREE);
 
-static void
-vr4130_align_insns (void)
-{
-  struct mips_sim state;
-  rtx insn, subinsn, last, last2, next;
-  bool aligned_p;
+      types[MIPS_V4QI_FTYPE_V2HI_V2HI]
+       = build_function_type_list (V4QI_type_node,
+                                   V2HI_type_node, V2HI_type_node,
+                                   NULL_TREE);
 
-  dfa_start ();
+      types[MIPS_V2HI_FTYPE_SI_SI]
+       = build_function_type_list (V2HI_type_node,
+                                   intSI_type_node, intSI_type_node,
+                                   NULL_TREE);
 
-  /* LAST is the last instruction before INSN to have a nonzero length.
-     LAST2 is the last such instruction before LAST.  */
-  last = 0;
-  last2 = 0;
+      types[MIPS_SI_FTYPE_V2HI]
+       = build_function_type_list (intSI_type_node,
+                                   V2HI_type_node,
+                                   NULL_TREE);
 
-  /* ALIGNED_P is true if INSN is known to be at an aligned address.  */
-  aligned_p = true;
+      types[MIPS_V2HI_FTYPE_V4QI]
+       = build_function_type_list (V2HI_type_node,
+                                   V4QI_type_node,
+                                   NULL_TREE);
 
-  mips_sim_init (&state, alloca (state_size ()));
-  for (insn = get_insns (); insn != 0; insn = next)
-    {
-      unsigned int length;
+      types[MIPS_V4QI_FTYPE_V4QI_SI]
+       = build_function_type_list (V4QI_type_node,
+                                   V4QI_type_node, intSI_type_node,
+                                   NULL_TREE);
 
-      next = NEXT_INSN (insn);
+      types[MIPS_V2HI_FTYPE_V2HI_SI]
+       = build_function_type_list (V2HI_type_node,
+                                   V2HI_type_node, intSI_type_node,
+                                   NULL_TREE);
 
-      /* See the comment above vr4130_avoid_branch_rt_conflict for details.
-        This isn't really related to the alignment pass, but we do it on
-        the fly to avoid a separate instruction walk.  */
-      vr4130_avoid_branch_rt_conflict (insn);
+      types[MIPS_V2HI_FTYPE_V4QI_V2HI]
+       = build_function_type_list (V2HI_type_node,
+                                   V4QI_type_node, V2HI_type_node,
+                                   NULL_TREE);
 
-      if (USEFUL_INSN_P (insn))
-       FOR_EACH_SUBINSN (subinsn, insn)
-         {
-           mips_sim_wait_insn (&state, subinsn);
+      types[MIPS_SI_FTYPE_V2HI_V2HI]
+       = build_function_type_list (intSI_type_node,
+                                   V2HI_type_node, V2HI_type_node,
+                                   NULL_TREE);
 
-           /* If we want this instruction to issue in parallel with the
-              previous one, make sure that the previous instruction is
-              aligned.  There are several reasons why this isn't worthwhile
-              when the second instruction is a call:
+      types[MIPS_DI_FTYPE_DI_V4QI_V4QI]
+       = build_function_type_list (intDI_type_node,
+                                   intDI_type_node, V4QI_type_node, V4QI_type_node,
+                                   NULL_TREE);
 
-                 - Calls are less likely to be performance critical,
-                 - There's a good chance that the delay slot can execute
-                   in parallel with the call.
-                 - The return address would then be unaligned.
+      types[MIPS_DI_FTYPE_DI_V2HI_V2HI]
+       = build_function_type_list (intDI_type_node,
+                                   intDI_type_node, V2HI_type_node, V2HI_type_node,
+                                   NULL_TREE);
 
-              In general, if we're going to insert a nop between instructions
-              X and Y, it's better to insert it immediately after X.  That
-              way, if the nop makes Y aligned, it will also align any labels
-              between X and Y.  */
-           if (state.insns_left != state.issue_rate
-               && !CALL_P (subinsn))
-             {
-               if (subinsn == SEQ_BEGIN (insn) && aligned_p)
-                 {
-                   /* SUBINSN is the first instruction in INSN and INSN is
-                      aligned.  We want to align the previous instruction
-                      instead, so insert a nop between LAST2 and LAST.
+      types[MIPS_DI_FTYPE_DI_SI_SI]
+       = build_function_type_list (intDI_type_node,
+                                   intDI_type_node, intSI_type_node, intSI_type_node,
+                                   NULL_TREE);
 
-                      Note that LAST could be either a single instruction
-                      or a branch with a delay slot.  In the latter case,
-                      LAST, like INSN, is already aligned, but the delay
-                      slot must have some extra delay that stops it from
-                      issuing at the same time as the branch.  We therefore
-                      insert a nop before the branch in order to align its
-                      delay slot.  */
-                   emit_insn_after (gen_nop (), last2);
-                   aligned_p = false;
-                 }
-               else if (subinsn != SEQ_BEGIN (insn) && !aligned_p)
-                 {
-                   /* SUBINSN is the delay slot of INSN, but INSN is
-                      currently unaligned.  Insert a nop between
-                      LAST and INSN to align it.  */
-                   emit_insn_after (gen_nop (), last);
-                   aligned_p = true;
-                 }
-             }
-           mips_sim_issue_insn (&state, subinsn);
-         }
-      mips_sim_finish_insn (&state, insn);
+      types[MIPS_V4QI_FTYPE_SI]
+       = build_function_type_list (V4QI_type_node,
+                                   intSI_type_node,
+                                   NULL_TREE);
 
-      /* Update LAST, LAST2 and ALIGNED_P for the next instruction.  */
-      length = get_attr_length (insn);
-      if (length > 0)
-       {
-         /* If the instruction is an asm statement or multi-instruction
-            mips.md patern, the length is only an estimate.  Insert an
-            8 byte alignment after it so that the following instructions
-            can be handled correctly.  */
-         if (NONJUMP_INSN_P (SEQ_BEGIN (insn))
-             && (recog_memoized (insn) < 0 || length >= 8))
-           {
-             next = emit_insn_after (gen_align (GEN_INT (3)), insn);
-             next = NEXT_INSN (next);
-             mips_sim_next_cycle (&state);
-             aligned_p = true;
-           }
-         else if (length & 4)
-           aligned_p = !aligned_p;
-         last2 = last;
-         last = insn;
-       }
+      types[MIPS_V2HI_FTYPE_SI]
+       = build_function_type_list (V2HI_type_node,
+                                   intSI_type_node,
+                                   NULL_TREE);
 
-      /* See whether INSN is an aligned label.  */
-      if (LABEL_P (insn) && label_to_alignment (insn) >= 3)
-       aligned_p = true;
-    }
-  dfa_finish ();
-}
-\f
-/* Subroutine of mips_reorg.  If there is a hazard between INSN
-   and a previous instruction, avoid it by inserting nops after
-   instruction AFTER.
+      types[MIPS_VOID_FTYPE_V4QI_V4QI]
+       = build_function_type_list (void_type_node,
+                                   V4QI_type_node, V4QI_type_node,
+                                   NULL_TREE);
 
-   *DELAYED_REG and *HILO_DELAY describe the hazards that apply at
-   this point.  If *DELAYED_REG is non-null, INSN must wait a cycle
-   before using the value of that register.  *HILO_DELAY counts the
-   number of instructions since the last hilo hazard (that is,
-   the number of instructions since the last mflo or mfhi).
+      types[MIPS_SI_FTYPE_V4QI_V4QI]
+       = build_function_type_list (intSI_type_node,
+                                   V4QI_type_node, V4QI_type_node,
+                                   NULL_TREE);
 
-   After inserting nops for INSN, update *DELAYED_REG and *HILO_DELAY
-   for the next instruction.
+      types[MIPS_VOID_FTYPE_V2HI_V2HI]
+       = build_function_type_list (void_type_node,
+                                   V2HI_type_node, V2HI_type_node,
+                                   NULL_TREE);
 
-   LO_REG is an rtx for the LO register, used in dependence checking.  */
+      types[MIPS_SI_FTYPE_DI_SI]
+       = build_function_type_list (intSI_type_node,
+                                   intDI_type_node, intSI_type_node,
+                                   NULL_TREE);
 
-static void
-mips_avoid_hazard (rtx after, rtx insn, int *hilo_delay,
-                  rtx *delayed_reg, rtx lo_reg)
-{
-  rtx pattern, set;
-  int nops, ninsns, hazard_set;
+      types[MIPS_DI_FTYPE_DI_SI]
+       = build_function_type_list (intDI_type_node,
+                                   intDI_type_node, intSI_type_node,
+                                   NULL_TREE);
 
-  if (!INSN_P (insn))
-    return;
+      types[MIPS_VOID_FTYPE_SI_SI]
+       = build_function_type_list (void_type_node,
+                                   intSI_type_node, intSI_type_node,
+                                   NULL_TREE);
 
-  pattern = PATTERN (insn);
+      types[MIPS_SI_FTYPE_PTR_SI]
+       = build_function_type_list (intSI_type_node,
+                                   ptr_type_node, intSI_type_node,
+                                   NULL_TREE);
 
-  /* Do not put the whole function in .set noreorder if it contains
-     an asm statement.  We don't know whether there will be hazards
-     between the asm statement and the gcc-generated code.  */
-  if (GET_CODE (pattern) == ASM_INPUT || asm_noperands (pattern) >= 0)
-    cfun->machine->all_noreorder_p = false;
+      types[MIPS_SI_FTYPE_VOID]
+       = build_function_type (intSI_type_node, void_list_node);
 
-  /* Ignore zero-length instructions (barriers and the like).  */
-  ninsns = get_attr_length (insn) / 4;
-  if (ninsns == 0)
-    return;
+      if (TARGET_DSPR2)
+       {
+         types[MIPS_V4QI_FTYPE_V4QI]
+           = build_function_type_list (V4QI_type_node,
+                                       V4QI_type_node,
+                                       NULL_TREE);
 
-  /* Work out how many nops are needed.  Note that we only care about
-     registers that are explicitly mentioned in the instruction's pattern.
-     It doesn't matter that calls use the argument registers or that they
-     clobber hi and lo.  */
-  if (*hilo_delay < 2 && reg_set_p (lo_reg, pattern))
-    nops = 2 - *hilo_delay;
-  else if (*delayed_reg != 0 && reg_referenced_p (*delayed_reg, pattern))
-    nops = 1;
-  else
-    nops = 0;
+         types[MIPS_SI_FTYPE_SI_SI_SI]
+           = build_function_type_list (intSI_type_node,
+                                       intSI_type_node, intSI_type_node,
+                                       intSI_type_node, NULL_TREE);
 
-  /* Insert the nops between this instruction and the previous one.
-     Each new nop takes us further from the last hilo hazard.  */
-  *hilo_delay += nops;
-  while (nops-- > 0)
-    emit_insn_after (gen_hazard_nop (), after);
+         types[MIPS_DI_FTYPE_DI_USI_USI]
+           = build_function_type_list (intDI_type_node,
+                                       intDI_type_node,
+                                       unsigned_intSI_type_node,
+                                       unsigned_intSI_type_node, NULL_TREE);
 
-  /* Set up the state for the next instruction.  */
-  *hilo_delay += ninsns;
-  *delayed_reg = 0;
-  if (INSN_CODE (insn) >= 0)
-    switch (get_attr_hazard (insn))
-      {
-      case HAZARD_NONE:
-       break;
+         types[MIPS_DI_FTYPE_SI_SI]
+           = build_function_type_list (intDI_type_node,
+                                       intSI_type_node, intSI_type_node,
+                                       NULL_TREE);
 
-      case HAZARD_HILO:
-       *hilo_delay = 0;
-       break;
+         types[MIPS_DI_FTYPE_USI_USI]
+           = build_function_type_list (intDI_type_node,
+                                       unsigned_intSI_type_node,
+                                       unsigned_intSI_type_node, NULL_TREE);
 
-      case HAZARD_DELAY:
-       hazard_set = (int) get_attr_hazard_set (insn);
-       if (hazard_set == 0)
-         set = single_set (insn);
-       else
-         {
-           gcc_assert (GET_CODE (PATTERN (insn)) == PARALLEL);
-           set = XVECEXP (PATTERN (insn), 0, hazard_set - 1);
-         }
-       gcc_assert (set && GET_CODE (set) == SET);
-       *delayed_reg = SET_DEST (set);
-       break;
-      }
-}
+         types[MIPS_V2HI_FTYPE_SI_SI_SI]
+           = build_function_type_list (V2HI_type_node,
+                                       intSI_type_node, intSI_type_node,
+                                       intSI_type_node, NULL_TREE);
 
+       }
+    }
 
-/* Go through the instruction stream and insert nops where necessary.
-   See if the whole function can then be put into .set noreorder &
-   .set nomacro.  */
+  /* Iterate through all of the bdesc arrays, initializing all of the
+     builtin functions.  */
 
-static void
-mips_avoid_hazards (void)
-{
-  rtx insn, last_insn, lo_reg, delayed_reg;
-  int hilo_delay, i;
+  offset = 0;
+  for (m = bdesc_arrays; m < &bdesc_arrays[ARRAY_SIZE (bdesc_arrays)]; m++)
+    {
+      if ((m->proc == PROCESSOR_MAX || (m->proc == mips_arch))
+         && (m->unsupported_target_flags & target_flags) == 0)
+       for (d = m->bdesc; d < &m->bdesc[m->size]; d++)
+         if ((d->target_flags & target_flags) == d->target_flags)
+           add_builtin_function (d->name, types[d->function_type],
+                                 d - m->bdesc + offset,
+                                 BUILT_IN_MD, NULL, NULL);
+      offset += m->size;
+    }
+}
 
-  /* Force all instructions to be split into their final form.  */
-  split_all_insns_noflow ();
+/* Take the argument ARGNUM of the arglist of EXP and convert it into a form
+   suitable for input operand OP of instruction ICODE.  Return the value.  */
 
-  /* Recalculate instruction lengths without taking nops into account.  */
-  cfun->machine->ignore_hazard_length_p = true;
-  shorten_branches (get_insns ());
+static rtx
+mips_prepare_builtin_arg (enum insn_code icode,
+                         unsigned int op, tree exp, unsigned int argnum)
+{
+  rtx value;
+  enum machine_mode mode;
 
-  cfun->machine->all_noreorder_p = true;
+  value = expand_normal (CALL_EXPR_ARG (exp, argnum));
+  mode = insn_data[icode].operand[op].mode;
+  if (!insn_data[icode].operand[op].predicate (value, mode))
+    {
+      value = copy_to_mode_reg (mode, value);
+      /* Check the predicate again.  */
+      if (!insn_data[icode].operand[op].predicate (value, mode))
+       {
+         error ("invalid argument to builtin function");
+         return const0_rtx;
+       }
+    }
 
-  /* Profiled functions can't be all noreorder because the profiler
-     support uses assembler macros.  */
-  if (current_function_profile)
-    cfun->machine->all_noreorder_p = false;
+  return value;
+}
 
-  /* Code compiled with -mfix-vr4120 can't be all noreorder because
-     we rely on the assembler to work around some errata.  */
-  if (TARGET_FIX_VR4120)
-    cfun->machine->all_noreorder_p = false;
+/* Return an rtx suitable for output operand OP of instruction ICODE.
+   If TARGET is non-null, try to use it where possible.  */
 
-  /* The same is true for -mfix-vr4130 if we might generate mflo or
-     mfhi instructions.  Note that we avoid using mflo and mfhi if
-     the VR4130 macc and dmacc instructions are available instead;
-     see the *mfhilo_{si,di}_macc patterns.  */
-  if (TARGET_FIX_VR4130 && !ISA_HAS_MACCHI)
-    cfun->machine->all_noreorder_p = false;
-
-  last_insn = 0;
-  hilo_delay = 2;
-  delayed_reg = 0;
-  lo_reg = gen_rtx_REG (SImode, LO_REGNUM);
+static rtx
+mips_prepare_builtin_target (enum insn_code icode, unsigned int op, rtx target)
+{
+  enum machine_mode mode;
 
-  for (insn = get_insns (); insn != 0; insn = NEXT_INSN (insn))
-    if (INSN_P (insn))
-      {
-       if (GET_CODE (PATTERN (insn)) == SEQUENCE)
-         for (i = 0; i < XVECLEN (PATTERN (insn), 0); i++)
-           mips_avoid_hazard (last_insn, XVECEXP (PATTERN (insn), 0, i),
-                              &hilo_delay, &delayed_reg, lo_reg);
-       else
-         mips_avoid_hazard (last_insn, insn, &hilo_delay,
-                            &delayed_reg, lo_reg);
+  mode = insn_data[icode].operand[op].mode;
+  if (target == 0 || !insn_data[icode].operand[op].predicate (target, mode))
+    target = gen_reg_rtx (mode);
 
-       last_insn = insn;
-      }
+  return target;
 }
 
+/* Expand a MIPS_BUILTIN_DIRECT function.  ICODE is the code of the
+   .md pattern and CALL is the function expr with arguments.  TARGET,
+   if nonnull, suggests a good place to put the result.
+   HAS_TARGET indicates the function must return something.  */
 
-/* Implement TARGET_MACHINE_DEPENDENT_REORG.  */
-
-static void
-mips_reorg (void)
+static rtx
+mips_expand_builtin_direct (enum insn_code icode, rtx target, tree exp,
+                           bool has_target)
 {
-  mips16_lay_out_constants ();
-  if (TARGET_EXPLICIT_RELOCS)
-    {
-      if (mips_flag_delayed_branch)
-       dbr_schedule (get_insns ());
-      mips_avoid_hazards ();
-      if (TUNE_MIPS4130 && TARGET_VR4130_ALIGN)
-       vr4130_align_insns ();
-    }
-}
-
-/* This function does three things:
-
-   - Register the special divsi3 and modsi3 functions if -mfix-vr4120.
-   - Register the mips16 hardware floating point stubs.
-   - Register the gofast functions if selected using --enable-gofast.  */
-
-#include "config/gofast.h"
+  rtx ops[MAX_RECOG_OPERANDS];
+  int i = 0;
+  int j = 0;
 
-static void
-mips_init_libfuncs (void)
-{
-  if (TARGET_FIX_VR4120)
+  if (has_target)
     {
-      set_optab_libfunc (sdiv_optab, SImode, "__vr4120_divsi3");
-      set_optab_libfunc (smod_optab, SImode, "__vr4120_modsi3");
+      /* We save target to ops[0].  */
+      ops[0] = mips_prepare_builtin_target (icode, 0, target);
+      i = 1;
     }
 
-  if (TARGET_MIPS16 && TARGET_HARD_FLOAT_ABI)
-    {
-      set_optab_libfunc (add_optab, SFmode, "__mips16_addsf3");
-      set_optab_libfunc (sub_optab, SFmode, "__mips16_subsf3");
-      set_optab_libfunc (smul_optab, SFmode, "__mips16_mulsf3");
-      set_optab_libfunc (sdiv_optab, SFmode, "__mips16_divsf3");
-
-      set_optab_libfunc (eq_optab, SFmode, "__mips16_eqsf2");
-      set_optab_libfunc (ne_optab, SFmode, "__mips16_nesf2");
-      set_optab_libfunc (gt_optab, SFmode, "__mips16_gtsf2");
-      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");
+  /* We need to test if the arglist is not zero.  Some instructions have extra
+     clobber registers.  */
+  for (; i < insn_data[icode].n_operands && i <= call_expr_nargs (exp); i++, j++)
+    ops[i] = mips_prepare_builtin_arg (icode, i, exp, j);
 
-      if (TARGET_DOUBLE_FLOAT)
-       {
-         set_optab_libfunc (add_optab, DFmode, "__mips16_adddf3");
-         set_optab_libfunc (sub_optab, DFmode, "__mips16_subdf3");
-         set_optab_libfunc (smul_optab, DFmode, "__mips16_muldf3");
-         set_optab_libfunc (sdiv_optab, DFmode, "__mips16_divdf3");
+  switch (i)
+    {
+    case 2:
+      emit_insn (GEN_FCN (icode) (ops[0], ops[1]));
+      break;
 
-         set_optab_libfunc (eq_optab, DFmode, "__mips16_eqdf2");
-         set_optab_libfunc (ne_optab, DFmode, "__mips16_nedf2");
-         set_optab_libfunc (gt_optab, DFmode, "__mips16_gtdf2");
-         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");
+    case 3:
+      emit_insn (GEN_FCN (icode) (ops[0], ops[1], ops[2]));
+      break;
 
-         set_conv_libfunc (sext_optab, DFmode, SFmode, "__mips16_extendsfdf2");
-         set_conv_libfunc (trunc_optab, SFmode, DFmode, "__mips16_truncdfsf2");
+    case 4:
+      emit_insn (GEN_FCN (icode) (ops[0], ops[1], ops[2], ops[3]));
+      break;
 
-         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");
-       }
+    default:
+      gcc_unreachable ();
     }
-  else
-    gofast_maybe_init_libfuncs ();
+  return target;
 }
 
-/* Return a number assessing the cost of moving a register in class
-   FROM to class TO.  The classes are expressed using the enumeration
-   values such as `GENERAL_REGS'.  A value of 2 is the default; other
-   values are interpreted relative to that.
-
-   It is not required that the cost always equal 2 when FROM is the
-   same as TO; on some machines it is expensive to move between
-   registers if they are not general registers.
+/* Expand a __builtin_mips_movt_*_ps() or __builtin_mips_movf_*_ps()
+   function (TYPE says which).  EXP is the tree for the function
+   function, ICODE is the instruction that should be used to compare
+   the first two arguments, and COND is the condition it should test.
+   TARGET, if nonnull, suggests a good place to put the result.  */
 
-   If reload sees an insn consisting of a single `set' between two
-   hard registers, and if `REGISTER_MOVE_COST' applied to their
-   classes returns a value of 2, reload does not check to ensure that
-   the constraints of the insn are met.  Setting a cost of other than
-   2 will allow reload to verify that the constraints are met.  You
-   should do this if the `movM' pattern's constraints do not allow
-   such copying.
+static rtx
+mips_expand_builtin_movtf (enum mips_builtin_type type,
+                          enum insn_code icode, enum mips_fp_condition cond,
+                          rtx target, tree exp)
+{
+  rtx cmp_result, op0, op1;
 
-   ??? We make the cost of moving from HI/LO into general
-   registers the same as for one of moving general registers to
-   HI/LO for TARGET_MIPS16 in order to prevent allocating a
-   pseudo to HI/LO.  This might hurt optimizations though, it
-   isn't clear if it is wise.  And it might not work in all cases.  We
-   could solve the DImode LO reg problem by using a multiply, just
-   like reload_{in,out}si.  We could solve the SImode/HImode HI reg
-   problem by using divide instructions.  divu puts the remainder in
-   the HI reg, so doing a divide by -1 will move the value in the HI
-   reg for all values except -1.  We could handle that case by using a
-   signed divide, e.g.  -1 / 2 (or maybe 1 / -2?).  We'd have to emit
-   a compare/branch to test the input value to see which instruction
-   we need to use.  This gets pretty messy, but it is feasible.  */
+  cmp_result = mips_prepare_builtin_target (icode, 0, 0);
+  op0 = mips_prepare_builtin_arg (icode, 1, exp, 0);
+  op1 = mips_prepare_builtin_arg (icode, 2, exp, 1);
+  emit_insn (GEN_FCN (icode) (cmp_result, op0, op1, GEN_INT (cond)));
 
-int
-mips_register_move_cost (enum machine_mode mode,
-                        enum reg_class to, enum reg_class from)
-{
-  if (TARGET_MIPS16)
-    {
-      if (reg_class_subset_p (from, GENERAL_REGS)
-         && reg_class_subset_p (to, GENERAL_REGS))
-       {
-         if (reg_class_subset_p (from, M16_REGS)
-             || reg_class_subset_p (to, M16_REGS))
-           return 2;
-         /* Two MOVEs.  */
-         return 4;
-       }
-    }
-  else if (reg_class_subset_p (from, GENERAL_REGS))
-    {
-      if (reg_class_subset_p (to, GENERAL_REGS))
-       return 2;
-      if (reg_class_subset_p (to, FP_REGS))
-       return 4;
-      if (reg_class_subset_p (to, ALL_COP_AND_GR_REGS))
-       return 5;
-      if (reg_class_subset_p (to, ACC_REGS))
-       return 6;
-    }
-  else if (reg_class_subset_p (to, GENERAL_REGS))
+  icode = CODE_FOR_mips_cond_move_tf_ps;
+  target = mips_prepare_builtin_target (icode, 0, target);
+  if (type == MIPS_BUILTIN_MOVT)
     {
-      if (reg_class_subset_p (from, FP_REGS))
-       return 4;
-      if (reg_class_subset_p (from, ST_REGS))
-       /* LUI followed by MOVF.  */
-       return 4;
-      if (reg_class_subset_p (from, ALL_COP_AND_GR_REGS))
-       return 5;
-      if (reg_class_subset_p (from, ACC_REGS))
-       return 6;
+      op1 = mips_prepare_builtin_arg (icode, 2, exp, 2);
+      op0 = mips_prepare_builtin_arg (icode, 1, exp, 3);
     }
-  else if (reg_class_subset_p (from, FP_REGS))
+  else
     {
-      if (reg_class_subset_p (to, FP_REGS)
-         && mips_mode_ok_for_mov_fmt_p (mode))
-       return 4;
-      if (reg_class_subset_p (to, ST_REGS))
-       /* An expensive sequence.  */
-       return 8;
+      op0 = mips_prepare_builtin_arg (icode, 1, exp, 2);
+      op1 = mips_prepare_builtin_arg (icode, 2, exp, 3);
     }
-
-  return 12;
+  emit_insn (gen_mips_cond_move_tf_ps (target, op0, op1, cmp_result));
+  return target;
 }
 
-/* Return the length of INSN.  LENGTH is the initial length computed by
-   attributes in the machine-description file.  */
+/* Move VALUE_IF_TRUE into TARGET if CONDITION is true; move VALUE_IF_FALSE
+   into TARGET otherwise.  Return TARGET.  */
 
-int
-mips_adjust_insn_length (rtx insn, int length)
+static rtx
+mips_builtin_branch_and_move (rtx condition, rtx target,
+                             rtx value_if_true, rtx value_if_false)
 {
-  /* A unconditional jump has an unfilled delay slot if it is not part
-     of a sequence.  A conditional jump normally has a delay slot, but
-     does not on MIPS16.  */
-  if (CALL_P (insn) || (TARGET_MIPS16 ? simplejump_p (insn) : JUMP_P (insn)))
-    length += 4;
+  rtx true_label, done_label;
 
-  /* See how many nops might be needed to avoid hardware hazards.  */
-  if (!cfun->machine->ignore_hazard_length_p && INSN_CODE (insn) >= 0)
-    switch (get_attr_hazard (insn))
-      {
-      case HAZARD_NONE:
-       break;
+  true_label = gen_label_rtx ();
+  done_label = gen_label_rtx ();
 
-      case HAZARD_DELAY:
-       length += 4;
-       break;
+  /* First assume that CONDITION is false.  */
+  mips_emit_move (target, value_if_false);
 
-      case HAZARD_HILO:
-       length += 8;
-       break;
-      }
+  /* Branch to TRUE_LABEL if CONDITION is true and DONE_LABEL otherwise.  */
+  emit_jump_insn (gen_condjump (condition, true_label));
+  emit_jump_insn (gen_jump (done_label));
+  emit_barrier ();
 
-  /* All MIPS16 instructions are a measly two bytes.  */
-  if (TARGET_MIPS16)
-    length /= 2;
+  /* Fix TARGET if CONDITION is true.  */
+  emit_label (true_label);
+  mips_emit_move (target, value_if_true);
 
-  return length;
+  emit_label (done_label);
+  return target;
 }
 
+/* Expand a comparison builtin of type BUILTIN_TYPE.  ICODE is the code
+   of the comparison instruction and COND is the condition it should test.
+   EXP is the function call and arguments and TARGET, if nonnull,
+   suggests a good place to put the boolean result.  */
 
-/* Return an asm sequence to start a noat block and load the address
-   of a label into $1.  */
-
-const char *
-mips_output_load_label (void)
+static rtx
+mips_expand_builtin_compare (enum mips_builtin_type builtin_type,
+                            enum insn_code icode, enum mips_fp_condition cond,
+                            rtx target, tree exp)
 {
-  if (TARGET_EXPLICIT_RELOCS)
-    switch (mips_abi)
-      {
-      case ABI_N32:
-       return "%[lw\t%@,%%got_page(%0)(%+)\n\taddiu\t%@,%@,%%got_ofst(%0)";
+  rtx offset, condition, cmp_result, ops[MAX_RECOG_OPERANDS];
+  int i;
+  int j = 0;
 
-      case ABI_64:
-       return "%[ld\t%@,%%got_page(%0)(%+)\n\tdaddiu\t%@,%@,%%got_ofst(%0)";
+  if (target == 0 || GET_MODE (target) != SImode)
+    target = gen_reg_rtx (SImode);
 
-      default:
-       if (ISA_HAS_LOAD_DELAY)
-         return "%[lw\t%@,%%got(%0)(%+)%#\n\taddiu\t%@,%@,%%lo(%0)";
-       return "%[lw\t%@,%%got(%0)(%+)\n\taddiu\t%@,%@,%%lo(%0)";
-      }
-  else
+  /* Prepare the operands to the comparison.  */
+  cmp_result = mips_prepare_builtin_target (icode, 0, 0);
+  for (i = 1; i < insn_data[icode].n_operands - 1; i++, j++)
+    ops[i] = mips_prepare_builtin_arg (icode, i, exp, j);
+
+  switch (insn_data[icode].n_operands)
     {
-      if (Pmode == DImode)
-       return "%[dla\t%@,%0";
-      else
-       return "%[la\t%@,%0";
-    }
-}
+    case 4:
+      emit_insn (GEN_FCN (icode) (cmp_result, ops[1], ops[2], GEN_INT (cond)));
+      break;
 
-/* Return the assembly code for INSN, which has the operands given by
-   OPERANDS, and which branches to OPERANDS[1] if some condition is true.
-   BRANCH_IF_TRUE is the asm template that should be used if OPERANDS[1]
-   is in range of a direct branch.  BRANCH_IF_FALSE is an inverted
-   version of BRANCH_IF_TRUE.  */
+    case 6:
+      emit_insn (GEN_FCN (icode) (cmp_result, ops[1], ops[2],
+                                 ops[3], ops[4], GEN_INT (cond)));
+      break;
 
-const char *
-mips_output_conditional_branch (rtx insn, rtx *operands,
-                               const char *branch_if_true,
-                               const char *branch_if_false)
-{
-  unsigned int length;
-  rtx taken, not_taken;
+    default:
+      gcc_unreachable ();
+    }
 
-  length = get_attr_length (insn);
-  if (length <= 8)
+  /* If the comparison sets more than one register, we define the result
+     to be 0 if all registers are false and -1 if all registers are true.
+     The value of the complete result is indeterminate otherwise.  */
+  switch (builtin_type)
     {
-      /* Just a simple conditional branch.  */
-      mips_branch_likely = (final_sequence && INSN_ANNULLED_BRANCH_P (insn));
-      return branch_if_true;
+    case MIPS_BUILTIN_CMP_ALL:
+      condition = gen_rtx_NE (VOIDmode, cmp_result, constm1_rtx);
+      return mips_builtin_branch_and_move (condition, target,
+                                          const0_rtx, const1_rtx);
+
+    case MIPS_BUILTIN_CMP_UPPER:
+    case MIPS_BUILTIN_CMP_LOWER:
+      offset = GEN_INT (builtin_type == MIPS_BUILTIN_CMP_UPPER);
+      condition = gen_single_cc (cmp_result, offset);
+      return mips_builtin_branch_and_move (condition, target,
+                                          const1_rtx, const0_rtx);
+
+    default:
+      condition = gen_rtx_NE (VOIDmode, cmp_result, const0_rtx);
+      return mips_builtin_branch_and_move (condition, target,
+                                          const1_rtx, const0_rtx);
     }
+}
 
-  /* Generate a reversed branch around a direct jump.  This fallback does
-     not use branch-likely instructions.  */
-  mips_branch_likely = false;
-  not_taken = gen_label_rtx ();
-  taken = operands[1];
+/* Expand a bposge builtin of type BUILTIN_TYPE.  TARGET, if nonnull,
+   suggests a good place to put the boolean result.  */
 
-  /* Generate the reversed branch to NOT_TAKEN.  */
-  operands[1] = not_taken;
-  output_asm_insn (branch_if_false, operands);
+static rtx
+mips_expand_builtin_bposge (enum mips_builtin_type builtin_type, rtx target)
+{
+  rtx condition, cmp_result;
+  int cmp_value;
 
-  /* If INSN has a delay slot, we must provide delay slots for both the
-     branch to NOT_TAKEN and the conditional jump.  We must also ensure
-     that INSN's delay slot is executed in the appropriate cases.  */
-  if (final_sequence)
-    {
-      /* This first delay slot will always be executed, so use INSN's
-        delay slot if is not annulled.  */
-      if (!INSN_ANNULLED_BRANCH_P (insn))
-       {
-         final_scan_insn (XVECEXP (final_sequence, 0, 1),
-                          asm_out_file, optimize, 1, NULL);
-         INSN_DELETED_P (XVECEXP (final_sequence, 0, 1)) = 1;
-       }
-      else
-       output_asm_insn ("nop", 0);
-      fprintf (asm_out_file, "\n");
-    }
+  if (target == 0 || GET_MODE (target) != SImode)
+    target = gen_reg_rtx (SImode);
 
-  /* Output the unconditional branch to TAKEN.  */
-  if (length <= 16)
-    output_asm_insn ("j\t%0%/", &taken);
+  cmp_result = gen_rtx_REG (CCDSPmode, CCDSP_PO_REGNUM);
+
+  if (builtin_type == MIPS_BUILTIN_BPOSGE32)
+    cmp_value = 32;
   else
+    gcc_assert (0);
+
+  condition = gen_rtx_GE (VOIDmode, cmp_result, GEN_INT (cmp_value));
+  return mips_builtin_branch_and_move (condition, target,
+                                      const1_rtx, const0_rtx);
+}
+
+/* Expand builtin functions.  This is called from TARGET_EXPAND_BUILTIN.  */
+
+static rtx
+mips_expand_builtin (tree exp, rtx target, rtx subtarget ATTRIBUTE_UNUSED,
+                    enum machine_mode mode ATTRIBUTE_UNUSED,
+                    int ignore ATTRIBUTE_UNUSED)
+{
+  enum insn_code icode;
+  enum mips_builtin_type type;
+  tree fndecl;
+  unsigned int fcode;
+  const struct builtin_description *bdesc;
+  const struct bdesc_map *m;
+
+  fndecl = TREE_OPERAND (CALL_EXPR_FN (exp), 0);
+  fcode = DECL_FUNCTION_CODE (fndecl);
+
+  if (TARGET_MIPS16)
     {
-      output_asm_insn (mips_output_load_label (), &taken);
-      output_asm_insn ("jr\t%@%]%/", 0);
+      error ("built-in function %qs not supported for MIPS16",
+            IDENTIFIER_POINTER (DECL_NAME (fndecl)));
+      return const0_rtx;
     }
 
-  /* Now deal with its delay slot; see above.  */
-  if (final_sequence)
+  bdesc = NULL;
+  for (m = bdesc_arrays; m < &bdesc_arrays[ARRAY_SIZE (bdesc_arrays)]; m++)
     {
-      /* This delay slot will only be executed if the branch is taken.
-        Use INSN's delay slot if is annulled.  */
-      if (INSN_ANNULLED_BRANCH_P (insn))
+      if (fcode < m->size)
        {
-         final_scan_insn (XVECEXP (final_sequence, 0, 1),
-                          asm_out_file, optimize, 1, NULL);
-         INSN_DELETED_P (XVECEXP (final_sequence, 0, 1)) = 1;
+         bdesc = m->bdesc;
+         icode = bdesc[fcode].icode;
+         type = bdesc[fcode].builtin_type;
+         break;
        }
-      else
-       output_asm_insn ("nop", 0);
-      fprintf (asm_out_file, "\n");
+      fcode -= m->size;
     }
+  if (bdesc == NULL)
+    return 0;
 
-  /* Output NOT_TAKEN.  */
-  (*targetm.asm_out.internal_label) (asm_out_file, "L",
-                                    CODE_LABEL_NUMBER (not_taken));
-  return "";
-}
+  switch (type)
+    {
+    case MIPS_BUILTIN_DIRECT:
+      return mips_expand_builtin_direct (icode, target, exp, true);
 
-/* Return the assembly code for INSN, which branches to OPERANDS[1]
-   if some ordered condition is true.  The condition is given by
-   OPERANDS[0] if !INVERTED_P, otherwise it is the inverse of
-   OPERANDS[0].  OPERANDS[2] is the comparison's first operand;
-   its second is always zero.  */
+    case MIPS_BUILTIN_DIRECT_NO_TARGET:
+      return mips_expand_builtin_direct (icode, target, exp, false);
 
-const char *
-mips_output_order_conditional_branch (rtx insn, rtx *operands, bool inverted_p)
-{
-  const char *branch[2];
+    case MIPS_BUILTIN_MOVT:
+    case MIPS_BUILTIN_MOVF:
+      return mips_expand_builtin_movtf (type, icode, bdesc[fcode].cond,
+                                       target, exp);
 
-  /* Make BRANCH[1] branch to OPERANDS[1] when the condition is true.
-     Make BRANCH[0] branch on the inverse condition.  */
-  switch (GET_CODE (operands[0]))
-    {
-      /* These cases are equivalent to comparisons against zero.  */
-    case LEU:
-      inverted_p = !inverted_p;
-      /* Fall through.  */
-    case GTU:
-      branch[!inverted_p] = MIPS_BRANCH ("bne", "%2,%.,%1");
-      branch[inverted_p] = MIPS_BRANCH ("beq", "%2,%.,%1");
-      break;
+    case MIPS_BUILTIN_CMP_ANY:
+    case MIPS_BUILTIN_CMP_ALL:
+    case MIPS_BUILTIN_CMP_UPPER:
+    case MIPS_BUILTIN_CMP_LOWER:
+    case MIPS_BUILTIN_CMP_SINGLE:
+      return mips_expand_builtin_compare (type, icode, bdesc[fcode].cond,
+                                         target, exp);
 
-      /* These cases are always true or always false.  */
-    case LTU:
-      inverted_p = !inverted_p;
-      /* Fall through.  */
-    case GEU:
-      branch[!inverted_p] = MIPS_BRANCH ("beq", "%.,%.,%1");
-      branch[inverted_p] = MIPS_BRANCH ("bne", "%.,%.,%1");
-      break;
+    case MIPS_BUILTIN_BPOSGE32:
+      return mips_expand_builtin_bposge (type, target);
 
     default:
-      branch[!inverted_p] = MIPS_BRANCH ("b%C0z", "%2,%1");
-      branch[inverted_p] = MIPS_BRANCH ("b%N0z", "%2,%1");
-      break;
+      return 0;
     }
-  return mips_output_conditional_branch (insn, operands, branch[1], branch[0]);
 }
 \f
-/* Used to output div or ddiv instruction DIVISION, which has the operands
-   given by OPERANDS.  Add in a divide-by-zero check if needed.
+/* An entry in the mips16 constant pool.  VALUE is the pool constant,
+   MODE is its mode, and LABEL is the CODE_LABEL associated with it.  */
 
-   When working around R4000 and R4400 errata, we need to make sure that
-   the division is not immediately followed by a shift[1][2].  We also
-   need to stop the division from being put into a branch delay slot[3].
-   The easiest way to avoid both problems is to add a nop after the
-   division.  When a divide-by-zero check is needed, this nop can be
-   used to fill the branch delay slot.
+struct mips16_constant {
+  struct mips16_constant *next;
+  rtx value;
+  rtx label;
+  enum machine_mode mode;
+};
 
-   [1] If a double-word or a variable shift executes immediately
-       after starting an integer division, the shift may give an
-       incorrect result.  See quotations of errata #16 and #28 from
-       "MIPS R4000PC/SC Errata, Processor Revision 2.2 and 3.0"
-       in mips.md for details.
+/* Information about an incomplete mips16 constant pool.  FIRST is the
+   first constant, HIGHEST_ADDRESS is the highest address that the first
+   byte of the pool can have, and INSN_ADDRESS is the current instruction
+   address.  */
 
-   [2] A similar bug to [1] exists for all revisions of the
-       R4000 and the R4400 when run in an MC configuration.
-       From "MIPS R4000MC Errata, Processor Revision 2.2 and 3.0":
+struct mips16_constant_pool {
+  struct mips16_constant *first;
+  int highest_address;
+  int insn_address;
+};
 
-       "19. In this following sequence:
+/* Add constant VALUE to POOL and return its label.  MODE is the
+   value's mode (used for CONST_INTs, etc.).  */
 
-                   ddiv                (or ddivu or div or divu)
-                   dsll32              (or dsrl32, dsra32)
+static rtx
+add_constant (struct mips16_constant_pool *pool,
+             rtx value, enum machine_mode mode)
+{
+  struct mips16_constant **p, *c;
+  bool first_of_size_p;
 
-           if an MPT stall occurs, while the divide is slipping the cpu
-           pipeline, then the following double shift would end up with an
-           incorrect result.
+  /* See whether the constant is already in the pool.  If so, return the
+     existing label, otherwise leave P pointing to the place where the
+     constant should be added.
 
-           Workaround: The compiler needs to avoid generating any
-           sequence with divide followed by extended double shift."
+     Keep the pool sorted in increasing order of mode size so that we can
+     reduce the number of alignments needed.  */
+  first_of_size_p = true;
+  for (p = &pool->first; *p != 0; p = &(*p)->next)
+    {
+      if (mode == (*p)->mode && rtx_equal_p (value, (*p)->value))
+       return (*p)->label;
+      if (GET_MODE_SIZE (mode) < GET_MODE_SIZE ((*p)->mode))
+       break;
+      if (GET_MODE_SIZE (mode) == GET_MODE_SIZE ((*p)->mode))
+       first_of_size_p = false;
+    }
 
-       This erratum is also present in "MIPS R4400MC Errata, Processor
-       Revision 1.0" and "MIPS R4400MC Errata, Processor Revision 2.0
-       & 3.0" as errata #10 and #4, respectively.
+  /* In the worst case, the constant needed by the earliest instruction
+     will end up at the end of the pool.  The entire pool must then be
+     accessible from that instruction.
 
-   [3] From "MIPS R4000PC/SC Errata, Processor Revision 2.2 and 3.0"
-       (also valid for MIPS R4000MC processors):
+     When adding the first constant, set the pool's highest address to
+     the address of the first out-of-range byte.  Adjust this address
+     downwards each time a new constant is added.  */
+  if (pool->first == 0)
+    /* For pc-relative lw, addiu and daddiu instructions, the base PC value
+       is the address of the instruction with the lowest two bits clear.
+       The base PC value for ld has the lowest three bits clear.  Assume
+       the worst case here.  */
+    pool->highest_address = pool->insn_address - (UNITS_PER_WORD - 2) + 0x8000;
+  pool->highest_address -= GET_MODE_SIZE (mode);
+  if (first_of_size_p)
+    /* Take into account the worst possible padding due to alignment.  */
+    pool->highest_address -= GET_MODE_SIZE (mode) - 1;
 
-       "52. R4000SC: This bug does not apply for the R4000PC.
+  /* Create a new entry.  */
+  c = (struct mips16_constant *) xmalloc (sizeof *c);
+  c->value = value;
+  c->mode = mode;
+  c->label = gen_label_rtx ();
+  c->next = *p;
+  *p = c;
 
-           There are two flavors of this bug:
+  return c->label;
+}
 
-           1) If the instruction just after divide takes an RF exception
-              (tlb-refill, tlb-invalid) and gets an instruction cache
-              miss (both primary and secondary) and the line which is
-              currently in secondary cache at this index had the first
-              data word, where the bits 5..2 are set, then R4000 would
-              get a wrong result for the div.
+/* Output constant VALUE after instruction INSN and return the last
+   instruction emitted.  MODE is the mode of the constant.  */
 
-           ##1
-                   nop
-                   div r8, r9
-                   -------------------         # end-of page. -tlb-refill
-                   nop
-           ##2
-                   nop
-                   div r8, r9
-                   -------------------         # end-of page. -tlb-invalid
-                   nop
+static rtx
+dump_constants_1 (enum machine_mode mode, rtx value, rtx insn)
+{
+  if (SCALAR_INT_MODE_P (mode)
+      || ALL_SCALAR_FRACT_MODE_P (mode)
+      || ALL_SCALAR_ACCUM_MODE_P (mode))
+    {
+      rtx size = GEN_INT (GET_MODE_SIZE (mode));
+      return emit_insn_after (gen_consttable_int (value, size), insn);
+    }
 
-           2) If the divide is in the taken branch delay slot, where the
-              target takes RF exception and gets an I-cache miss for the
-              exception vector or where I-cache miss occurs for the
-              target address, under the above mentioned scenarios, the
-              div would get wrong results.
+  if (SCALAR_FLOAT_MODE_P (mode))
+    return emit_insn_after (gen_consttable_float (value), insn);
 
-           ##1
-                   j   r2              # to next page mapped or unmapped
-                   div r8,r9           # this bug would be there as long
-                                       # as there is an ICache miss and
-                   nop                 # the "data pattern" is present
+  if (VECTOR_MODE_P (mode))
+    {
+      int i;
 
-           ##2
-                   beq r0, r0, NextPage        # to Next page
-                   div r8,r9
-                   nop
+      for (i = 0; i < CONST_VECTOR_NUNITS (value); i++)
+       insn = dump_constants_1 (GET_MODE_INNER (mode),
+                                CONST_VECTOR_ELT (value, i), insn);
+      return insn;
+    }
 
-           This bug is present for div, divu, ddiv, and ddivu
-           instructions.
+  gcc_unreachable ();
+}
 
-           Workaround: For item 1), OS could make sure that the next page
-           after the divide instruction is also mapped.  For item 2), the
-           compiler could make sure that the divide instruction is not in
-           the branch delay slot."
 
-       These processors have PRId values of 0x00004220 and 0x00004300 for
-       the R4000 and 0x00004400, 0x00004500 and 0x00004600 for the R4400.  */
+/* Dump out the constants in CONSTANTS after INSN.  */
 
-const char *
-mips_output_division (const char *division, rtx *operands)
+static void
+dump_constants (struct mips16_constant *constants, rtx insn)
 {
-  const char *s;
+  struct mips16_constant *c, *next;
+  int align;
 
-  s = division;
-  if (TARGET_FIX_R4000 || TARGET_FIX_R4400)
-    {
-      output_asm_insn (s, operands);
-      s = "nop";
-    }
-  if (TARGET_CHECK_ZERO_DIV)
+  align = 0;
+  for (c = constants; c != NULL; c = next)
     {
-      if (TARGET_MIPS16)
-       {
-         output_asm_insn (s, operands);
-         s = "bnez\t%2,1f\n\tbreak\t7\n1:";
-       }
-      else if (GENERATE_DIVIDE_TRAPS)
-        {
-         output_asm_insn (s, operands);
-         s = "teq\t%2,%.,7";
-        }
-      else
+      /* If necessary, increase the alignment of PC.  */
+      if (align < GET_MODE_SIZE (c->mode))
        {
-         output_asm_insn ("%(bne\t%2,%.,1f", operands);
-         output_asm_insn (s, operands);
-         s = "break\t7%)\n1:";
+         int align_log = floor_log2 (GET_MODE_SIZE (c->mode));
+         insn = emit_insn_after (gen_align (GEN_INT (align_log)), insn);
        }
-    }
-  return s;
-}
-\f
-/* Return true if GIVEN is the same as CANONICAL, or if it is CANONICAL
-   with a final "000" replaced by "k".  Ignore case.
+      align = GET_MODE_SIZE (c->mode);
 
-   Note: this function is shared between GCC and GAS.  */
+      insn = emit_label_after (c->label, insn);
+      insn = dump_constants_1 (c->mode, c->value, insn);
 
-static bool
-mips_strict_matching_cpu_name_p (const char *canonical, const char *given)
-{
-  while (*given != 0 && TOLOWER (*given) == TOLOWER (*canonical))
-    given++, canonical++;
+      next = c->next;
+      free (c);
+    }
 
-  return ((*given == 0 && *canonical == 0)
-         || (strcmp (canonical, "000") == 0 && strcasecmp (given, "k") == 0));
+  emit_barrier_after (insn);
 }
 
+/* Return the length of instruction INSN.  */
 
-/* Return true if GIVEN matches CANONICAL, where GIVEN is a user-supplied
-   CPU name.  We've traditionally allowed a lot of variation here.
+static int
+mips16_insn_length (rtx insn)
+{
+  if (JUMP_P (insn))
+    {
+      rtx body = PATTERN (insn);
+      if (GET_CODE (body) == ADDR_VEC)
+       return GET_MODE_SIZE (GET_MODE (body)) * XVECLEN (body, 0);
+      if (GET_CODE (body) == ADDR_DIFF_VEC)
+       return GET_MODE_SIZE (GET_MODE (body)) * XVECLEN (body, 1);
+    }
+  return get_attr_length (insn);
+}
 
-   Note: this function is shared between GCC and GAS.  */
+/* If *X is a symbolic constant that refers to the constant pool, add
+   the constant to POOL and rewrite *X to use the constant's label.  */
 
-static bool
-mips_matching_cpu_name_p (const char *canonical, const char *given)
+static void
+mips16_rewrite_pool_constant (struct mips16_constant_pool *pool, rtx *x)
 {
-  /* First see if the name matches exactly, or with a final "000"
-     turned into "k".  */
-  if (mips_strict_matching_cpu_name_p (canonical, given))
-    return true;
-
-  /* If not, try comparing based on numerical designation alone.
-     See if GIVEN is an unadorned number, or 'r' followed by a number.  */
-  if (TOLOWER (*given) == 'r')
-    given++;
-  if (!ISDIGIT (*given))
-    return false;
+  rtx base, offset, label;
 
-  /* Skip over some well-known prefixes in the canonical name,
-     hoping to find a number there too.  */
-  if (TOLOWER (canonical[0]) == 'v' && TOLOWER (canonical[1]) == 'r')
-    canonical += 2;
-  else if (TOLOWER (canonical[0]) == 'r' && TOLOWER (canonical[1]) == 'm')
-    canonical += 2;
-  else if (TOLOWER (canonical[0]) == 'r')
-    canonical += 1;
-
-  return mips_strict_matching_cpu_name_p (canonical, given);
+  split_const (*x, &base, &offset);
+  if (GET_CODE (base) == SYMBOL_REF && CONSTANT_POOL_ADDRESS_P (base))
+    {
+      label = add_constant (pool, get_pool_constant (base),
+                           get_pool_mode (base));
+      base = gen_rtx_LABEL_REF (Pmode, label);
+      *x = mips_unspec_address_offset (base, offset, SYMBOL_PC_RELATIVE);
+    }
 }
 
+/* This structure is used to communicate with mips16_rewrite_pool_refs.
+   INSN is the instruction we're rewriting and POOL points to the current
+   constant pool.  */
+struct mips16_rewrite_pool_refs_info {
+  rtx insn;
+  struct mips16_constant_pool *pool;
+};
 
-/* Return the mips_cpu_info entry for the processor or ISA given
-   by CPU_STRING.  Return null if the string isn't recognized.
-
-   A similar function exists in GAS.  */
+/* Rewrite *X so that constant pool references refer to the constant's
+   label instead.  DATA points to a mips16_rewrite_pool_refs_info
+   structure.  */
 
-static const struct mips_cpu_info *
-mips_parse_cpu (const char *cpu_string)
+static int
+mips16_rewrite_pool_refs (rtx *x, void *data)
 {
-  unsigned int i;
-  const char *s;
-
-  /* In the past, we allowed upper-case CPU names, but it doesn't
-     work well with the multilib machinery.  */
-  for (s = cpu_string; *s != 0; s++)
-    if (ISUPPER (*s))
-      {
-       warning (0, "the cpu name must be lower case");
-       break;
-      }
+  struct mips16_rewrite_pool_refs_info *info = data;
 
-  /* 'from-abi' selects the most compatible architecture for the given
-     ABI: MIPS I for 32-bit ABIs and MIPS III for 64-bit ABIs.  For the
-     EABIs, we have to decide whether we're using the 32-bit or 64-bit
-     version.  Look first at the -mgp options, if given, otherwise base
-     the choice on MASK_64BIT in TARGET_DEFAULT.  */
-  if (strcasecmp (cpu_string, "from-abi") == 0)
-    return mips_cpu_info_from_isa (ABI_NEEDS_32BIT_REGS ? 1
-                                  : ABI_NEEDS_64BIT_REGS ? 3
-                                  : (TARGET_64BIT ? 3 : 1));
+  if (force_to_mem_operand (*x, Pmode))
+    {
+      rtx mem = force_const_mem (GET_MODE (*x), *x);
+      validate_change (info->insn, x, mem, false);
+    }
 
-  /* 'default' has traditionally been a no-op.  Probably not very useful.  */
-  if (strcasecmp (cpu_string, "default") == 0)
-    return 0;
+  if (MEM_P (*x))
+    {
+      mips16_rewrite_pool_constant (info->pool, &XEXP (*x, 0));
+      return -1;
+    }
 
-  for (i = 0; i < ARRAY_SIZE (mips_cpu_info_table); i++)
-    if (mips_matching_cpu_name_p (mips_cpu_info_table[i].name, cpu_string))
-      return mips_cpu_info_table + i;
+  if (TARGET_MIPS16_TEXT_LOADS)
+    mips16_rewrite_pool_constant (info->pool, x);
 
-  return 0;
+  return GET_CODE (*x) == CONST ? -1 : 0;
 }
 
+/* Build MIPS16 constant pools.  */
 
-/* Return the processor associated with the given ISA level, or null
-   if the ISA isn't valid.  */
-
-static const struct mips_cpu_info *
-mips_cpu_info_from_isa (int isa)
+static void
+mips16_lay_out_constants (void)
 {
-  unsigned int i;
+  struct mips16_constant_pool pool;
+  struct mips16_rewrite_pool_refs_info info;
+  rtx insn, barrier;
 
-  for (i = 0; i < ARRAY_SIZE (mips_cpu_info_table); i++)
-    if (mips_cpu_info_table[i].isa == isa)
-      return mips_cpu_info_table + i;
+  if (!TARGET_MIPS16_PCREL_LOADS)
+    return;
 
-  return 0;
-}
-\f
-/* Implement HARD_REGNO_NREGS.  The size of FP registers is controlled
-   by UNITS_PER_FPREG.  The size of FP status registers is always 4, because
-   they only hold condition code modes, and CCmode is always considered to
-   be 4 bytes wide.  All other registers are word sized.  */
+  barrier = 0;
+  memset (&pool, 0, sizeof (pool));
+  for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
+    {
+      /* Rewrite constant pool references in INSN.  */
+      if (INSN_P (insn))
+       {
+         info.insn = insn;
+         info.pool = &pool;
+         for_each_rtx (&PATTERN (insn), mips16_rewrite_pool_refs, &info);
+       }
 
-unsigned int
-mips_hard_regno_nregs (int regno, enum machine_mode mode)
-{
-  if (ST_REG_P (regno))
-    return ((GET_MODE_SIZE (mode) + 3) / 4);
-  else if (! FP_REG_P (regno))
-    return ((GET_MODE_SIZE (mode) + UNITS_PER_WORD - 1) / UNITS_PER_WORD);
-  else
-    return ((GET_MODE_SIZE (mode) + UNITS_PER_FPREG - 1) / UNITS_PER_FPREG);
-}
+      pool.insn_address += mips16_insn_length (insn);
 
-/* Implement TARGET_RETURN_IN_MEMORY.  Under the old (i.e., 32 and O64 ABIs)
-   all BLKmode objects are returned in memory.  Under the new (N32 and
-   64-bit MIPS ABIs) small structures are returned in a register.
-   Objects with varying size must still be returned in memory, of
-   course.  */
+      if (pool.first != NULL)
+       {
+         /* If there are no natural barriers between the first user of
+            the pool and the highest acceptable address, we'll need to
+            create a new instruction to jump around the constant pool.
+            In the worst case, this instruction will be 4 bytes long.
 
-static bool
-mips_return_in_memory (const_tree type, const_tree fndecl ATTRIBUTE_UNUSED)
-{
-  if (TARGET_OLDABI)
-    return (TYPE_MODE (type) == BLKmode);
-  else
-    return ((int_size_in_bytes (type) > (2 * UNITS_PER_WORD))
-           || (int_size_in_bytes (type) == -1));
-}
+            If it's too late to do this transformation after INSN,
+            do it immediately before INSN.  */
+         if (barrier == 0 && pool.insn_address + 4 > pool.highest_address)
+           {
+             rtx label, jump;
 
-static bool
-mips_strict_argument_naming (CUMULATIVE_ARGS *ca ATTRIBUTE_UNUSED)
-{
-  return !TARGET_OLDABI;
+             label = gen_label_rtx ();
+
+             jump = emit_jump_insn_before (gen_jump (label), insn);
+             JUMP_LABEL (jump) = label;
+             LABEL_NUSES (label) = 1;
+             barrier = emit_barrier_after (jump);
+
+             emit_label_after (label, barrier);
+             pool.insn_address += 4;
+           }
+
+         /* See whether the constant pool is now out of range of the first
+            user.  If so, output the constants after the previous barrier.
+            Note that any instructions between BARRIER and INSN (inclusive)
+            will use negative offsets to refer to the pool.  */
+         if (pool.insn_address > pool.highest_address)
+           {
+             dump_constants (pool.first, barrier);
+             pool.first = NULL;
+             barrier = 0;
+           }
+         else if (BARRIER_P (insn))
+           barrier = insn;
+       }
+    }
+  dump_constants (pool.first, get_last_insn ());
 }
 \f
-/* Return true if INSN is a multiply-add or multiply-subtract
-   instruction and PREV assigns to the accumulator operand.  */
+/* A temporary variable used by for_each_rtx callbacks, etc.  */
+static rtx mips_sim_insn;
 
-bool
-mips_linked_madd_p (rtx prev, rtx insn)
-{
-  rtx x;
+/* A structure representing the state of the processor pipeline.
+   Used by the mips_sim_* family of functions.  */
+struct mips_sim {
+  /* The maximum number of instructions that can be issued in a cycle.
+     (Caches mips_issue_rate.)  */
+  unsigned int issue_rate;
 
-  x = single_set (insn);
-  if (x == 0)
-    return false;
+  /* The current simulation time.  */
+  unsigned int time;
 
-  x = SET_SRC (x);
+  /* How many more instructions can be issued in the current cycle.  */
+  unsigned int insns_left;
 
-  if (GET_CODE (x) == PLUS
-      && GET_CODE (XEXP (x, 0)) == MULT
-      && reg_set_p (XEXP (x, 1), prev))
-    return true;
+  /* LAST_SET[X].INSN is the last instruction to set register X.
+     LAST_SET[X].TIME is the time at which that instruction was issued.
+     INSN is null if no instruction has yet set register X.  */
+  struct {
+    rtx insn;
+    unsigned int time;
+  } last_set[FIRST_PSEUDO_REGISTER];
 
-  if (GET_CODE (x) == MINUS
-      && GET_CODE (XEXP (x, 1)) == MULT
-      && reg_set_p (XEXP (x, 0), prev))
-    return true;
+  /* The pipeline's current DFA state.  */
+  state_t dfa_state;
+};
 
-  return false;
+/* Reset STATE to the initial simulation state.  */
+
+static void
+mips_sim_reset (struct mips_sim *state)
+{
+  state->time = 0;
+  state->insns_left = state->issue_rate;
+  memset (&state->last_set, 0, sizeof (state->last_set));
+  state_reset (state->dfa_state);
 }
-\f
-/* Used by TUNE_MACC_CHAINS to record the last scheduled instruction
-   that may clobber hi or lo.  */
 
-static rtx mips_macc_chains_last_hilo;
+/* Initialize STATE before its first use.  DFA_STATE points to an
+   allocated but uninitialized DFA state.  */
 
-/* A TUNE_MACC_CHAINS helper function.  Record that instruction INSN has
-   been scheduled, updating mips_macc_chains_last_hilo appropriately.  */
+static void
+mips_sim_init (struct mips_sim *state, state_t dfa_state)
+{
+  state->issue_rate = mips_issue_rate ();
+  state->dfa_state = dfa_state;
+  mips_sim_reset (state);
+}
+
+/* Advance STATE by one clock cycle.  */
 
 static void
-mips_macc_chains_record (rtx insn)
+mips_sim_next_cycle (struct mips_sim *state)
 {
-  if (get_attr_may_clobber_hilo (insn))
-    mips_macc_chains_last_hilo = insn;
+  state->time++;
+  state->insns_left = state->issue_rate;
+  state_transition (state->dfa_state, 0);
 }
 
-/* A TUNE_MACC_CHAINS helper function.  Search ready queue READY, which
-   has NREADY elements, looking for a multiply-add or multiply-subtract
-   instruction that is cumulative with mips_macc_chains_last_hilo.
-   If there is one, promote it ahead of anything else that might
-   clobber hi or lo.  */
+/* Advance simulation state STATE until instruction INSN can read
+   register REG.  */
 
 static void
-mips_macc_chains_reorder (rtx *ready, int nready)
+mips_sim_wait_reg (struct mips_sim *state, rtx insn, rtx reg)
 {
-  int i, j;
+  unsigned int i;
 
-  if (mips_macc_chains_last_hilo != 0)
-    for (i = nready - 1; i >= 0; i--)
-      if (mips_linked_madd_p (mips_macc_chains_last_hilo, ready[i]))
-       {
-         for (j = nready - 1; j > i; j--)
-           if (recog_memoized (ready[j]) >= 0
-               && get_attr_may_clobber_hilo (ready[j]))
-             {
-               mips_promote_ready (ready, i, j);
-               break;
-             }
-         break;
-       }
-}
-\f
-/* The last instruction to be scheduled.  */
+  for (i = 0; i < HARD_REGNO_NREGS (REGNO (reg), GET_MODE (reg)); i++)
+    if (state->last_set[REGNO (reg) + i].insn != 0)
+      {
+       unsigned int t;
 
-static rtx vr4130_last_insn;
+       t = state->last_set[REGNO (reg) + i].time;
+       t += insn_latency (state->last_set[REGNO (reg) + i].insn, insn);
+       while (state->time < t)
+         mips_sim_next_cycle (state);
+    }
+}
 
-/* A note_stores callback used by vr4130_true_reg_dependence_p.  DATA
-   points to an rtx that is initially an instruction.  Nullify the rtx
-   if the instruction uses the value of register X.  */
+/* A for_each_rtx callback.  If *X is a register, advance simulation state
+   DATA until mips_sim_insn can read the register's value.  */
 
-static void
-vr4130_true_reg_dependence_p_1 (rtx x, const_rtx pat ATTRIBUTE_UNUSED, void *data)
+static int
+mips_sim_wait_regs_2 (rtx *x, void *data)
 {
-  rtx *insn_ptr = data;
-  if (REG_P (x)
-      && *insn_ptr != 0
-      && reg_referenced_p (x, PATTERN (*insn_ptr)))
-    *insn_ptr = 0;
+  if (REG_P (*x))
+    mips_sim_wait_reg (data, mips_sim_insn, *x);
+  return 0;
 }
 
-/* Return true if there is true register dependence between vr4130_last_insn
-   and INSN.  */
+/* Call mips_sim_wait_regs_2 (R, DATA) for each register R mentioned in *X.  */
 
-static bool
-vr4130_true_reg_dependence_p (rtx insn)
+static void
+mips_sim_wait_regs_1 (rtx *x, void *data)
 {
-  note_stores (PATTERN (vr4130_last_insn),
-              vr4130_true_reg_dependence_p_1, &insn);
-  return insn == 0;
+  for_each_rtx (x, mips_sim_wait_regs_2, data);
 }
 
-/* A TUNE_MIPS4130 helper function.  Given that INSN1 is at the head of
-   the ready queue and that INSN2 is the instruction after it, return
-   true if it is worth promoting INSN2 ahead of INSN1.  Look for cases
-   in which INSN1 and INSN2 can probably issue in parallel, but for
-   which (INSN2, INSN1) should be less sensitive to instruction
-   alignment than (INSN1, INSN2).  See 4130.md for more details.  */
+/* Advance simulation state STATE until all of INSN's register
+   dependencies are satisfied.  */
 
-static bool
-vr4130_swap_insns_p (rtx insn1, rtx insn2)
+static void
+mips_sim_wait_regs (struct mips_sim *state, rtx insn)
 {
-  sd_iterator_def sd_it;
-  dep_t dep;
-
-  /* Check for the following case:
-
-     1) there is some other instruction X with an anti dependence on INSN1;
-     2) X has a higher priority than INSN2; and
-     3) X is an arithmetic instruction (and thus has no unit restrictions).
+  mips_sim_insn = insn;
+  note_uses (&PATTERN (insn), mips_sim_wait_regs_1, state);
+}
 
-     If INSN1 is the last instruction blocking X, it would better to
-     choose (INSN1, X) over (INSN2, INSN1).  */
-  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;
+/* Advance simulation state STATE until the units required by
+   instruction INSN are available.  */
 
-  if (vr4130_last_insn != 0
-      && recog_memoized (insn1) >= 0
-      && recog_memoized (insn2) >= 0)
-    {
-      /* See whether INSN1 and INSN2 use different execution units,
-        or if they are both ALU-type instructions.  If so, they can
-        probably execute in parallel.  */
-      enum attr_vr4130_class class1 = get_attr_vr4130_class (insn1);
-      enum attr_vr4130_class class2 = get_attr_vr4130_class (insn2);
-      if (class1 != class2 || class1 == VR4130_CLASS_ALU)
-       {
-         /* If only one of the instructions has a dependence on
-            vr4130_last_insn, prefer to schedule the other one first.  */
-         bool dep1 = vr4130_true_reg_dependence_p (insn1);
-         bool dep2 = vr4130_true_reg_dependence_p (insn2);
-         if (dep1 != dep2)
-           return dep1;
+static void
+mips_sim_wait_units (struct mips_sim *state, rtx insn)
+{
+  state_t tmp_state;
 
-         /* Prefer to schedule INSN2 ahead of INSN1 if vr4130_last_insn
-            is not an ALU-type instruction and if INSN1 uses the same
-            execution unit.  (Note that if this condition holds, we already
-            know that INSN2 uses a different execution unit.)  */
-         if (class1 != VR4130_CLASS_ALU
-             && recog_memoized (vr4130_last_insn) >= 0
-             && class1 == get_attr_vr4130_class (vr4130_last_insn))
-           return true;
-       }
-    }
-  return false;
+  tmp_state = alloca (state_size ());
+  while (state->insns_left == 0
+        || (memcpy (tmp_state, state->dfa_state, state_size ()),
+            state_transition (tmp_state, insn) >= 0))
+    mips_sim_next_cycle (state);
 }
 
-/* A TUNE_MIPS4130 helper function.  (READY, NREADY) describes a ready
-   queue with at least two instructions.  Swap the first two if
-   vr4130_swap_insns_p says that it could be worthwhile.  */
+/* Advance simulation state STATE until INSN is ready to issue.  */
 
 static void
-vr4130_reorder (rtx *ready, int nready)
+mips_sim_wait_insn (struct mips_sim *state, rtx insn)
 {
-  if (vr4130_swap_insns_p (ready[nready - 1], ready[nready - 2]))
-    mips_promote_ready (ready, nready - 2, nready - 1);
+  mips_sim_wait_regs (state, insn);
+  mips_sim_wait_units (state, insn);
 }
-\f
-/* Remove the instruction at index LOWER from ready queue READY and
-   reinsert it in front of the instruction at index HIGHER.  LOWER must
-   be <= HIGHER.  */
+
+/* mips_sim_insn has just set X.  Update the LAST_SET array
+   in simulation state DATA.  */
 
 static void
-mips_promote_ready (rtx *ready, int lower, int higher)
+mips_sim_record_set (rtx x, const_rtx pat ATTRIBUTE_UNUSED, void *data)
 {
-  rtx new_head;
-  int i;
+  struct mips_sim *state;
+  unsigned int i;
 
-  new_head = ready[lower];
-  for (i = lower; i < higher; i++)
-    ready[i] = ready[i + 1];
-  ready[i] = new_head;
+  state = data;
+  if (REG_P (x))
+    for (i = 0; i < HARD_REGNO_NREGS (REGNO (x), GET_MODE (x)); i++)
+      {
+       state->last_set[REGNO (x) + i].insn = mips_sim_insn;
+       state->last_set[REGNO (x) + i].time = state->time;
+      }
 }
 
-/* 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.  */
+/* Issue instruction INSN in scheduler state STATE.  Assume that INSN
+   can issue immediately (i.e., that mips_sim_wait_insn has already
+   been called).  */
 
 static void
-mips_maybe_swap_ready (rtx *ready, int pos1, int pos2, int limit)
+mips_sim_issue_insn (struct mips_sim *state, rtx insn)
 {
-  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.  */
+  state_transition (state->dfa_state, insn);
+  state->insns_left--;
 
-static enum attr_type mips_last_74k_agen_insn = TYPE_UNKNOWN;
+  mips_sim_insn = insn;
+  note_stores (PATTERN (insn), mips_sim_record_set, state);
+}
 
-/* Initialize mips_last_74k_agen_insn from INSN.  A null argument
-   resets to TYPE_UNKNOWN state.  */
+/* Simulate issuing a NOP in state STATE.  */
 
 static void
-mips_74k_agen_init (rtx insn)
+mips_sim_issue_nop (struct mips_sim *state)
 {
-  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;
-    }
+  if (state->insns_left == 0)
+    mips_sim_next_cycle (state);
+  state->insns_left--;
 }
 
-/* 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.  */
+/* Update simulation state STATE so that it's ready to accept the instruction
+   after INSN.  INSN should be part of the main rtl chain, not a member of a
+   SEQUENCE.  */
 
 static void
-mips_74k_agen_reorder (rtx *ready, int nready)
+mips_sim_finish_insn (struct mips_sim *state, rtx insn)
 {
-  int i;
-  int store_pos, load_pos;
-
-  store_pos = -1;
-  load_pos = -1;
+  /* If INSN is a jump with an implicit delay slot, simulate a nop.  */
+  if (JUMP_P (insn))
+    mips_sim_issue_nop (state);
 
-  for (i = nready - 1; i >= 0; i--)
+  switch (GET_CODE (SEQ_BEGIN (insn)))
     {
-      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 CODE_LABEL:
+    case CALL_INSN:
+      /* We can't predict the processor state after a call or label.  */
+      mips_sim_reset (state);
+      break;
 
-         case TYPE_LOAD:
-           if (load_pos == -1)
-             load_pos = i;
-           break;
+    case JUMP_INSN:
+      /* The delay slots of branch likely instructions are only executed
+        when the branch is taken.  Therefore, if the caller has simulated
+        the delay slot instruction, STATE does not really reflect the state
+        of the pipeline for the instruction after the delay slot.  Also,
+        branch likely instructions tend to incur a penalty when not taken,
+        so there will probably be an extra delay between the branch and
+        the instruction after the delay slot.  */
+      if (INSN_ANNULLED_BRANCH_P (SEQ_BEGIN (insn)))
+       mips_sim_reset (state);
+      break;
 
-         default:
-           break;
-         }
-    }
-
-  if (load_pos == -1 || store_pos == -1)
-    return;
-
-  switch (mips_last_74k_agen_insn)
-    {
-    case TYPE_UNKNOWN:
-      /* Prefer to schedule loads since they have a higher latency.  */
-    case TYPE_LOAD:
-      /* Swap loads to the front of the queue.  */
-      mips_maybe_swap_ready (ready, load_pos, store_pos, 4);
-      break;
-    case TYPE_STORE:
-      /* Swap stores to the front of the queue.  */
-      mips_maybe_swap_ready (ready, store_pos, load_pos, 4);
-      break;
-    default:
-      break;
+    default:
+      break;
     }
 }
+\f
+/* The VR4130 pipeline issues aligned pairs of instructions together,
+   but it stalls the second instruction if it depends on the first.
+   In order to cut down the amount of logic required, this dependence
+   check is not based on a full instruction decode.  Instead, any non-SPECIAL
+   instruction is assumed to modify the register specified by bits 20-16
+   (which is usually the "rt" field).
 
-/* Implement TARGET_SCHED_INIT.  */
+   In beq, beql, bne and bnel instructions, the rt field is actually an
+   input, so we can end up with a false dependence between the branch
+   and its delay slot.  If this situation occurs in instruction INSN,
+   try to avoid it by swapping rs and rt.  */
 
 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)
+vr4130_avoid_branch_rt_conflict (rtx insn)
 {
-  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 ();
-}
-
-/* Implement TARGET_SCHED_VARIABLE_ISSUE.  */
+  rtx first, second;
 
-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)))
+  first = SEQ_BEGIN (insn);
+  second = SEQ_END (insn);
+  if (JUMP_P (first)
+      && NONJUMP_INSN_P (second)
+      && GET_CODE (PATTERN (first)) == SET
+      && GET_CODE (SET_DEST (PATTERN (first))) == PC
+      && GET_CODE (SET_SRC (PATTERN (first))) == IF_THEN_ELSE)
     {
-    case USE:
-    case CLOBBER:
-      /* Don't count USEs and CLOBBERs against the issue rate.  */
-      break;
-
-    default:
-      more--;
-      if (!reload_completed && TUNE_MACC_CHAINS)
-       mips_macc_chains_record (insn);
-      vr4130_last_insn = insn;
-      break;
+      /* Check for the right kind of condition.  */
+      rtx cond = XEXP (SET_SRC (PATTERN (first)), 0);
+      if ((GET_CODE (cond) == EQ || GET_CODE (cond) == NE)
+         && REG_P (XEXP (cond, 0))
+         && REG_P (XEXP (cond, 1))
+         && reg_referenced_p (XEXP (cond, 1), PATTERN (second))
+         && !reg_referenced_p (XEXP (cond, 0), PATTERN (second)))
+       {
+         /* SECOND mentions the rt register but not the rs register.  */
+         rtx tmp = XEXP (cond, 0);
+         XEXP (cond, 0) = XEXP (cond, 1);
+         XEXP (cond, 1) = tmp;
+       }
     }
-  return more;
-}
-\f
-/* Implement TARGET_SCHED_ADJUST_COST.  We assume that anti and output
-   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;
 }
 
-/* Return the number of instructions that can be issued per cycle.  */
+/* Implement -mvr4130-align.  Go through each basic block and simulate the
+   processor pipeline.  If we find that a pair of instructions could execute
+   in parallel, and the first of those instruction is not 8-byte aligned,
+   insert a nop to make it aligned.  */
 
-static int
-mips_issue_rate (void)
+static void
+vr4130_align_insns (void)
 {
-  switch (mips_tune)
-    {
-    case PROCESSOR_74KC:
-    case PROCESSOR_74KF2_1:
-    case PROCESSOR_74KF1_1:
-    case PROCESSOR_74KF3_2:
-      /* The 74k is not strictly quad-issue cpu, but can be seen as one
-        by the scheduler.  It can issue 1 ALU, 1 AGEN and 2 FPU insns,
-        but in reality only a maximum of 3 insns can be issued as the
-        floating point load/stores also require a slot in the AGEN pipe.  */
-     return 4;
-
-    case PROCESSOR_20KC:
-    case PROCESSOR_R4130:
-    case PROCESSOR_R5400:
-    case PROCESSOR_R5500:
-    case PROCESSOR_R7000:
-    case PROCESSOR_R9000:
-      return 2;
+  struct mips_sim state;
+  rtx insn, subinsn, last, last2, next;
+  bool aligned_p;
 
-    case PROCESSOR_SB1:
-    case PROCESSOR_SB1A:
-      /* This is actually 4, but we get better performance if we claim 3.
-        This is partly because of unwanted speculative code motion with the
-        larger number, and partly because in most common cases we can't
-        reach the theoretical max of 4.  */
-      return 3;
+  dfa_start ();
 
-    default:
-      return 1;
-    }
-}
+  /* LAST is the last instruction before INSN to have a nonzero length.
+     LAST2 is the last such instruction before LAST.  */
+  last = 0;
+  last2 = 0;
 
-/* Implements TARGET_SCHED_FIRST_CYCLE_MULTIPASS_DFA_LOOKAHEAD.  This should
-   be as wide as the scheduling freedom in the DFA.  */
+  /* ALIGNED_P is true if INSN is known to be at an aligned address.  */
+  aligned_p = true;
 
-static int
-mips_multipass_dfa_lookahead (void)
-{
-  /* Can schedule up to 4 of the 6 function units in any one cycle.  */
-  if (TUNE_SB1)
-    return 4;
+  mips_sim_init (&state, alloca (state_size ()));
+  for (insn = get_insns (); insn != 0; insn = next)
+    {
+      unsigned int length;
 
-  return 0;
-}
+      next = NEXT_INSN (insn);
 
-/* Implements a store data bypass check.  We need this because the cprestore
-   pattern is type store, but defined using an UNSPEC.  This UNSPEC causes the
-   default routine to abort.  We just return false for that case.  */
-/* ??? Should try to give a better result here than assuming false.  */
+      /* See the comment above vr4130_avoid_branch_rt_conflict for details.
+        This isn't really related to the alignment pass, but we do it on
+        the fly to avoid a separate instruction walk.  */
+      vr4130_avoid_branch_rt_conflict (insn);
 
-int
-mips_store_data_bypass_p (rtx out_insn, rtx in_insn)
-{
-  if (GET_CODE (PATTERN (in_insn)) == UNSPEC_VOLATILE)
-    return false;
+      if (USEFUL_INSN_P (insn))
+       FOR_EACH_SUBINSN (subinsn, insn)
+         {
+           mips_sim_wait_insn (&state, subinsn);
 
-  return ! store_data_bypass_p (out_insn, in_insn);
-}
-\f
-/* Given that we have an rtx of the form (prefetch ... WRITE LOCALITY),
-   return the first operand of the associated "pref" or "prefx" insn.  */
+           /* If we want this instruction to issue in parallel with the
+              previous one, make sure that the previous instruction is
+              aligned.  There are several reasons why this isn't worthwhile
+              when the second instruction is a call:
 
-rtx
-mips_prefetch_cookie (rtx write, rtx locality)
-{
-  /* store_streamed / load_streamed.  */
-  if (INTVAL (locality) <= 0)
-    return GEN_INT (INTVAL (write) + 4);
+                 - Calls are less likely to be performance critical,
+                 - There's a good chance that the delay slot can execute
+                   in parallel with the call.
+                 - The return address would then be unaligned.
 
-  /* store / load.  */
-  if (INTVAL (locality) <= 2)
-    return write;
+              In general, if we're going to insert a nop between instructions
+              X and Y, it's better to insert it immediately after X.  That
+              way, if the nop makes Y aligned, it will also align any labels
+              between X and Y.  */
+           if (state.insns_left != state.issue_rate
+               && !CALL_P (subinsn))
+             {
+               if (subinsn == SEQ_BEGIN (insn) && aligned_p)
+                 {
+                   /* SUBINSN is the first instruction in INSN and INSN is
+                      aligned.  We want to align the previous instruction
+                      instead, so insert a nop between LAST2 and LAST.
 
-  /* store_retained / load_retained.  */
-  return GEN_INT (INTVAL (write) + 6);
-}
-\f
-/* MIPS builtin function support. */
+                      Note that LAST could be either a single instruction
+                      or a branch with a delay slot.  In the latter case,
+                      LAST, like INSN, is already aligned, but the delay
+                      slot must have some extra delay that stops it from
+                      issuing at the same time as the branch.  We therefore
+                      insert a nop before the branch in order to align its
+                      delay slot.  */
+                   emit_insn_after (gen_nop (), last2);
+                   aligned_p = false;
+                 }
+               else if (subinsn != SEQ_BEGIN (insn) && !aligned_p)
+                 {
+                   /* SUBINSN is the delay slot of INSN, but INSN is
+                      currently unaligned.  Insert a nop between
+                      LAST and INSN to align it.  */
+                   emit_insn_after (gen_nop (), last);
+                   aligned_p = true;
+                 }
+             }
+           mips_sim_issue_insn (&state, subinsn);
+         }
+      mips_sim_finish_insn (&state, insn);
 
-struct builtin_description
-{
-  /* The code of the main .md file instruction.  See mips_builtin_type
-     for more information.  */
-  enum insn_code icode;
+      /* Update LAST, LAST2 and ALIGNED_P for the next instruction.  */
+      length = get_attr_length (insn);
+      if (length > 0)
+       {
+         /* If the instruction is an asm statement or multi-instruction
+            mips.md patern, the length is only an estimate.  Insert an
+            8 byte alignment after it so that the following instructions
+            can be handled correctly.  */
+         if (NONJUMP_INSN_P (SEQ_BEGIN (insn))
+             && (recog_memoized (insn) < 0 || length >= 8))
+           {
+             next = emit_insn_after (gen_align (GEN_INT (3)), insn);
+             next = NEXT_INSN (next);
+             mips_sim_next_cycle (&state);
+             aligned_p = true;
+           }
+         else if (length & 4)
+           aligned_p = !aligned_p;
+         last2 = last;
+         last = insn;
+       }
 
-  /* The floating-point comparison code to use with ICODE, if any.  */
-  enum mips_fp_condition cond;
+      /* See whether INSN is an aligned label.  */
+      if (LABEL_P (insn) && label_to_alignment (insn) >= 3)
+       aligned_p = true;
+    }
+  dfa_finish ();
+}
+\f
+/* Subroutine of mips_reorg.  If there is a hazard between INSN
+   and a previous instruction, avoid it by inserting nops after
+   instruction AFTER.
 
-  /* The name of the builtin function.  */
-  const char *name;
+   *DELAYED_REG and *HILO_DELAY describe the hazards that apply at
+   this point.  If *DELAYED_REG is non-null, INSN must wait a cycle
+   before using the value of that register.  *HILO_DELAY counts the
+   number of instructions since the last hilo hazard (that is,
+   the number of instructions since the last mflo or mfhi).
 
-  /* Specifies how the function should be expanded.  */
-  enum mips_builtin_type builtin_type;
+   After inserting nops for INSN, update *DELAYED_REG and *HILO_DELAY
+   for the next instruction.
 
-  /* The function's prototype.  */
-  enum mips_function_type function_type;
+   LO_REG is an rtx for the LO register, used in dependence checking.  */
 
-  /* The target flags required for this function.  */
-  int target_flags;
-};
+static void
+mips_avoid_hazard (rtx after, rtx insn, int *hilo_delay,
+                  rtx *delayed_reg, rtx lo_reg)
+{
+  rtx pattern, set;
+  int nops, ninsns, hazard_set;
 
-/* Define a MIPS_BUILTIN_DIRECT function for instruction CODE_FOR_mips_<INSN>.
-   FUNCTION_TYPE and TARGET_FLAGS are builtin_description fields.  */
-#define DIRECT_BUILTIN(INSN, FUNCTION_TYPE, TARGET_FLAGS)              \
-  { CODE_FOR_mips_ ## INSN, 0, "__builtin_mips_" #INSN,                        \
-    MIPS_BUILTIN_DIRECT, FUNCTION_TYPE, TARGET_FLAGS }
+  if (!INSN_P (insn))
+    return;
 
-/* Define __builtin_mips_<INSN>_<COND>_{s,d}, both of which require
-   TARGET_FLAGS.  */
-#define CMP_SCALAR_BUILTINS(INSN, COND, TARGET_FLAGS)                  \
-  { CODE_FOR_mips_ ## INSN ## _cond_s, MIPS_FP_COND_ ## COND,          \
-    "__builtin_mips_" #INSN "_" #COND "_s",                            \
-    MIPS_BUILTIN_CMP_SINGLE, MIPS_INT_FTYPE_SF_SF, TARGET_FLAGS },     \
-  { CODE_FOR_mips_ ## INSN ## _cond_d, MIPS_FP_COND_ ## COND,          \
-    "__builtin_mips_" #INSN "_" #COND "_d",                            \
-    MIPS_BUILTIN_CMP_SINGLE, MIPS_INT_FTYPE_DF_DF, TARGET_FLAGS }
+  pattern = PATTERN (insn);
 
-/* Define __builtin_mips_{any,all,upper,lower}_<INSN>_<COND>_ps.
-   The lower and upper forms require TARGET_FLAGS while the any and all
-   forms require MASK_MIPS3D.  */
-#define CMP_PS_BUILTINS(INSN, COND, TARGET_FLAGS)                      \
-  { CODE_FOR_mips_ ## INSN ## _cond_ps, MIPS_FP_COND_ ## COND,         \
-    "__builtin_mips_any_" #INSN "_" #COND "_ps",                       \
-    MIPS_BUILTIN_CMP_ANY, MIPS_INT_FTYPE_V2SF_V2SF, MASK_MIPS3D },     \
-  { CODE_FOR_mips_ ## INSN ## _cond_ps, MIPS_FP_COND_ ## COND,         \
-    "__builtin_mips_all_" #INSN "_" #COND "_ps",                       \
-    MIPS_BUILTIN_CMP_ALL, MIPS_INT_FTYPE_V2SF_V2SF, MASK_MIPS3D },     \
-  { CODE_FOR_mips_ ## INSN ## _cond_ps, MIPS_FP_COND_ ## COND,         \
-    "__builtin_mips_lower_" #INSN "_" #COND "_ps",                     \
-    MIPS_BUILTIN_CMP_LOWER, MIPS_INT_FTYPE_V2SF_V2SF, TARGET_FLAGS },  \
-  { CODE_FOR_mips_ ## INSN ## _cond_ps, MIPS_FP_COND_ ## COND,         \
-    "__builtin_mips_upper_" #INSN "_" #COND "_ps",                     \
-    MIPS_BUILTIN_CMP_UPPER, MIPS_INT_FTYPE_V2SF_V2SF, TARGET_FLAGS }
+  /* Do not put the whole function in .set noreorder if it contains
+     an asm statement.  We don't know whether there will be hazards
+     between the asm statement and the gcc-generated code.  */
+  if (GET_CODE (pattern) == ASM_INPUT || asm_noperands (pattern) >= 0)
+    cfun->machine->all_noreorder_p = false;
 
-/* Define __builtin_mips_{any,all}_<INSN>_<COND>_4s.  The functions
-   require MASK_MIPS3D.  */
-#define CMP_4S_BUILTINS(INSN, COND)                                    \
-  { CODE_FOR_mips_ ## INSN ## _cond_4s, MIPS_FP_COND_ ## COND,         \
-    "__builtin_mips_any_" #INSN "_" #COND "_4s",                       \
-    MIPS_BUILTIN_CMP_ANY, MIPS_INT_FTYPE_V2SF_V2SF_V2SF_V2SF,          \
-    MASK_MIPS3D },                                                     \
-  { CODE_FOR_mips_ ## INSN ## _cond_4s, MIPS_FP_COND_ ## COND,         \
-    "__builtin_mips_all_" #INSN "_" #COND "_4s",                       \
-    MIPS_BUILTIN_CMP_ALL, MIPS_INT_FTYPE_V2SF_V2SF_V2SF_V2SF,          \
-    MASK_MIPS3D }
+  /* Ignore zero-length instructions (barriers and the like).  */
+  ninsns = get_attr_length (insn) / 4;
+  if (ninsns == 0)
+    return;
 
-/* Define __builtin_mips_mov{t,f}_<INSN>_<COND>_ps.  The comparison
-   instruction requires TARGET_FLAGS.  */
-#define MOVTF_BUILTINS(INSN, COND, TARGET_FLAGS)                       \
-  { CODE_FOR_mips_ ## INSN ## _cond_ps, MIPS_FP_COND_ ## COND,         \
-    "__builtin_mips_movt_" #INSN "_" #COND "_ps",                      \
-    MIPS_BUILTIN_MOVT, MIPS_V2SF_FTYPE_V2SF_V2SF_V2SF_V2SF,            \
-    TARGET_FLAGS },                                                    \
-  { CODE_FOR_mips_ ## INSN ## _cond_ps, MIPS_FP_COND_ ## COND,         \
-    "__builtin_mips_movf_" #INSN "_" #COND "_ps",                      \
-    MIPS_BUILTIN_MOVF, MIPS_V2SF_FTYPE_V2SF_V2SF_V2SF_V2SF,            \
-    TARGET_FLAGS }
+  /* Work out how many nops are needed.  Note that we only care about
+     registers that are explicitly mentioned in the instruction's pattern.
+     It doesn't matter that calls use the argument registers or that they
+     clobber hi and lo.  */
+  if (*hilo_delay < 2 && reg_set_p (lo_reg, pattern))
+    nops = 2 - *hilo_delay;
+  else if (*delayed_reg != 0 && reg_referenced_p (*delayed_reg, pattern))
+    nops = 1;
+  else
+    nops = 0;
 
-/* Define all the builtins related to c.cond.fmt condition COND.  */
-#define CMP_BUILTINS(COND)                                             \
-  MOVTF_BUILTINS (c, COND, MASK_PAIRED_SINGLE_FLOAT),                  \
-  MOVTF_BUILTINS (cabs, COND, MASK_MIPS3D),                            \
-  CMP_SCALAR_BUILTINS (cabs, COND, MASK_MIPS3D),                       \
-  CMP_PS_BUILTINS (c, COND, MASK_PAIRED_SINGLE_FLOAT),                 \
-  CMP_PS_BUILTINS (cabs, COND, MASK_MIPS3D),                           \
-  CMP_4S_BUILTINS (c, COND),                                           \
-  CMP_4S_BUILTINS (cabs, COND)
+  /* Insert the nops between this instruction and the previous one.
+     Each new nop takes us further from the last hilo hazard.  */
+  *hilo_delay += nops;
+  while (nops-- > 0)
+    emit_insn_after (gen_hazard_nop (), after);
 
-static const struct builtin_description mips_bdesc[] =
-{
-  DIRECT_BUILTIN (pll_ps, MIPS_V2SF_FTYPE_V2SF_V2SF, MASK_PAIRED_SINGLE_FLOAT),
-  DIRECT_BUILTIN (pul_ps, MIPS_V2SF_FTYPE_V2SF_V2SF, MASK_PAIRED_SINGLE_FLOAT),
-  DIRECT_BUILTIN (plu_ps, MIPS_V2SF_FTYPE_V2SF_V2SF, MASK_PAIRED_SINGLE_FLOAT),
-  DIRECT_BUILTIN (puu_ps, MIPS_V2SF_FTYPE_V2SF_V2SF, MASK_PAIRED_SINGLE_FLOAT),
-  DIRECT_BUILTIN (cvt_ps_s, MIPS_V2SF_FTYPE_SF_SF, MASK_PAIRED_SINGLE_FLOAT),
-  DIRECT_BUILTIN (cvt_s_pl, MIPS_SF_FTYPE_V2SF, MASK_PAIRED_SINGLE_FLOAT),
-  DIRECT_BUILTIN (cvt_s_pu, MIPS_SF_FTYPE_V2SF, MASK_PAIRED_SINGLE_FLOAT),
-  DIRECT_BUILTIN (abs_ps, MIPS_V2SF_FTYPE_V2SF, MASK_PAIRED_SINGLE_FLOAT),
+  /* Set up the state for the next instruction.  */
+  *hilo_delay += ninsns;
+  *delayed_reg = 0;
+  if (INSN_CODE (insn) >= 0)
+    switch (get_attr_hazard (insn))
+      {
+      case HAZARD_NONE:
+       break;
 
-  DIRECT_BUILTIN (alnv_ps, MIPS_V2SF_FTYPE_V2SF_V2SF_INT,
-                 MASK_PAIRED_SINGLE_FLOAT),
-  DIRECT_BUILTIN (addr_ps, MIPS_V2SF_FTYPE_V2SF_V2SF, MASK_MIPS3D),
-  DIRECT_BUILTIN (mulr_ps, MIPS_V2SF_FTYPE_V2SF_V2SF, MASK_MIPS3D),
-  DIRECT_BUILTIN (cvt_pw_ps, MIPS_V2SF_FTYPE_V2SF, MASK_MIPS3D),
-  DIRECT_BUILTIN (cvt_ps_pw, MIPS_V2SF_FTYPE_V2SF, MASK_MIPS3D),
+      case HAZARD_HILO:
+       *hilo_delay = 0;
+       break;
 
-  DIRECT_BUILTIN (recip1_s, MIPS_SF_FTYPE_SF, MASK_MIPS3D),
-  DIRECT_BUILTIN (recip1_d, MIPS_DF_FTYPE_DF, MASK_MIPS3D),
-  DIRECT_BUILTIN (recip1_ps, MIPS_V2SF_FTYPE_V2SF, MASK_MIPS3D),
-  DIRECT_BUILTIN (recip2_s, MIPS_SF_FTYPE_SF_SF, MASK_MIPS3D),
-  DIRECT_BUILTIN (recip2_d, MIPS_DF_FTYPE_DF_DF, MASK_MIPS3D),
-  DIRECT_BUILTIN (recip2_ps, MIPS_V2SF_FTYPE_V2SF_V2SF, MASK_MIPS3D),
+      case HAZARD_DELAY:
+       hazard_set = (int) get_attr_hazard_set (insn);
+       if (hazard_set == 0)
+         set = single_set (insn);
+       else
+         {
+           gcc_assert (GET_CODE (PATTERN (insn)) == PARALLEL);
+           set = XVECEXP (PATTERN (insn), 0, hazard_set - 1);
+         }
+       gcc_assert (set && GET_CODE (set) == SET);
+       *delayed_reg = SET_DEST (set);
+       break;
+      }
+}
 
-  DIRECT_BUILTIN (rsqrt1_s, MIPS_SF_FTYPE_SF, MASK_MIPS3D),
-  DIRECT_BUILTIN (rsqrt1_d, MIPS_DF_FTYPE_DF, MASK_MIPS3D),
-  DIRECT_BUILTIN (rsqrt1_ps, MIPS_V2SF_FTYPE_V2SF, MASK_MIPS3D),
-  DIRECT_BUILTIN (rsqrt2_s, MIPS_SF_FTYPE_SF_SF, MASK_MIPS3D),
-  DIRECT_BUILTIN (rsqrt2_d, MIPS_DF_FTYPE_DF_DF, MASK_MIPS3D),
-  DIRECT_BUILTIN (rsqrt2_ps, MIPS_V2SF_FTYPE_V2SF_V2SF, MASK_MIPS3D),
 
-  MIPS_FP_CONDITIONS (CMP_BUILTINS)
-};
+/* Go through the instruction stream and insert nops where necessary.
+   See if the whole function can then be put into .set noreorder &
+   .set nomacro.  */
 
-/* Builtin functions for the SB-1 processor.  */
+static void
+mips_avoid_hazards (void)
+{
+  rtx insn, last_insn, lo_reg, delayed_reg;
+  int hilo_delay, i;
 
-#define CODE_FOR_mips_sqrt_ps CODE_FOR_sqrtv2sf2
+  /* Force all instructions to be split into their final form.  */
+  split_all_insns_noflow ();
 
-static const struct builtin_description sb1_bdesc[] =
-{
-  DIRECT_BUILTIN (sqrt_ps, MIPS_V2SF_FTYPE_V2SF, MASK_PAIRED_SINGLE_FLOAT)
-};
+  /* Recalculate instruction lengths without taking nops into account.  */
+  cfun->machine->ignore_hazard_length_p = true;
+  shorten_branches (get_insns ());
 
-/* Builtin functions for DSP ASE.  */
+  cfun->machine->all_noreorder_p = true;
 
-#define CODE_FOR_mips_addq_ph CODE_FOR_addv2hi3
-#define CODE_FOR_mips_addu_qb CODE_FOR_addv4qi3
-#define CODE_FOR_mips_subq_ph CODE_FOR_subv2hi3
-#define CODE_FOR_mips_subu_qb CODE_FOR_subv4qi3
-#define CODE_FOR_mips_mul_ph CODE_FOR_mulv2hi3
+  /* Profiled functions can't be all noreorder because the profiler
+     support uses assembler macros.  */
+  if (current_function_profile)
+    cfun->machine->all_noreorder_p = false;
 
-/* Define a MIPS_BUILTIN_DIRECT_NO_TARGET function for instruction
-   CODE_FOR_mips_<INSN>.  FUNCTION_TYPE and TARGET_FLAGS are
-   builtin_description fields.  */
-#define DIRECT_NO_TARGET_BUILTIN(INSN, FUNCTION_TYPE, TARGET_FLAGS)    \
-  { CODE_FOR_mips_ ## INSN, 0, "__builtin_mips_" #INSN,                        \
-    MIPS_BUILTIN_DIRECT_NO_TARGET, FUNCTION_TYPE, TARGET_FLAGS }
+  /* Code compiled with -mfix-vr4120 can't be all noreorder because
+     we rely on the assembler to work around some errata.  */
+  if (TARGET_FIX_VR4120)
+    cfun->machine->all_noreorder_p = false;
 
-/* Define __builtin_mips_bposge<VALUE>.  <VALUE> is 32 for the MIPS32 DSP
-   branch instruction.  TARGET_FLAGS is a builtin_description field.  */
-#define BPOSGE_BUILTIN(VALUE, TARGET_FLAGS)                            \
-  { CODE_FOR_mips_bposge, 0, "__builtin_mips_bposge" #VALUE,           \
-    MIPS_BUILTIN_BPOSGE ## VALUE, MIPS_SI_FTYPE_VOID, TARGET_FLAGS }
+  /* The same is true for -mfix-vr4130 if we might generate mflo or
+     mfhi instructions.  Note that we avoid using mflo and mfhi if
+     the VR4130 macc and dmacc instructions are available instead;
+     see the *mfhilo_{si,di}_macc patterns.  */
+  if (TARGET_FIX_VR4130 && !ISA_HAS_MACCHI)
+    cfun->machine->all_noreorder_p = false;
 
-static const struct builtin_description dsp_bdesc[] =
-{
-  DIRECT_BUILTIN (addq_ph, MIPS_V2HI_FTYPE_V2HI_V2HI, MASK_DSP),
-  DIRECT_BUILTIN (addq_s_ph, MIPS_V2HI_FTYPE_V2HI_V2HI, MASK_DSP),
-  DIRECT_BUILTIN (addq_s_w, MIPS_SI_FTYPE_SI_SI, MASK_DSP),
-  DIRECT_BUILTIN (addu_qb, MIPS_V4QI_FTYPE_V4QI_V4QI, MASK_DSP),
-  DIRECT_BUILTIN (addu_s_qb, MIPS_V4QI_FTYPE_V4QI_V4QI, MASK_DSP),
-  DIRECT_BUILTIN (subq_ph, MIPS_V2HI_FTYPE_V2HI_V2HI, MASK_DSP),
-  DIRECT_BUILTIN (subq_s_ph, MIPS_V2HI_FTYPE_V2HI_V2HI, MASK_DSP),
-  DIRECT_BUILTIN (subq_s_w, MIPS_SI_FTYPE_SI_SI, MASK_DSP),
-  DIRECT_BUILTIN (subu_qb, MIPS_V4QI_FTYPE_V4QI_V4QI, MASK_DSP),
-  DIRECT_BUILTIN (subu_s_qb, MIPS_V4QI_FTYPE_V4QI_V4QI, MASK_DSP),
-  DIRECT_BUILTIN (addsc, MIPS_SI_FTYPE_SI_SI, MASK_DSP),
-  DIRECT_BUILTIN (addwc, MIPS_SI_FTYPE_SI_SI, MASK_DSP),
-  DIRECT_BUILTIN (modsub, MIPS_SI_FTYPE_SI_SI, MASK_DSP),
-  DIRECT_BUILTIN (raddu_w_qb, MIPS_SI_FTYPE_V4QI, MASK_DSP),
-  DIRECT_BUILTIN (absq_s_ph, MIPS_V2HI_FTYPE_V2HI, MASK_DSP),
-  DIRECT_BUILTIN (absq_s_w, MIPS_SI_FTYPE_SI, MASK_DSP),
-  DIRECT_BUILTIN (precrq_qb_ph, MIPS_V4QI_FTYPE_V2HI_V2HI, MASK_DSP),
-  DIRECT_BUILTIN (precrq_ph_w, MIPS_V2HI_FTYPE_SI_SI, MASK_DSP),
-  DIRECT_BUILTIN (precrq_rs_ph_w, MIPS_V2HI_FTYPE_SI_SI, MASK_DSP),
-  DIRECT_BUILTIN (precrqu_s_qb_ph, MIPS_V4QI_FTYPE_V2HI_V2HI, MASK_DSP),
-  DIRECT_BUILTIN (preceq_w_phl, MIPS_SI_FTYPE_V2HI, MASK_DSP),
-  DIRECT_BUILTIN (preceq_w_phr, MIPS_SI_FTYPE_V2HI, MASK_DSP),
-  DIRECT_BUILTIN (precequ_ph_qbl, MIPS_V2HI_FTYPE_V4QI, MASK_DSP),
-  DIRECT_BUILTIN (precequ_ph_qbr, MIPS_V2HI_FTYPE_V4QI, MASK_DSP),
-  DIRECT_BUILTIN (precequ_ph_qbla, MIPS_V2HI_FTYPE_V4QI, MASK_DSP),
-  DIRECT_BUILTIN (precequ_ph_qbra, MIPS_V2HI_FTYPE_V4QI, MASK_DSP),
-  DIRECT_BUILTIN (preceu_ph_qbl, MIPS_V2HI_FTYPE_V4QI, MASK_DSP),
-  DIRECT_BUILTIN (preceu_ph_qbr, MIPS_V2HI_FTYPE_V4QI, MASK_DSP),
-  DIRECT_BUILTIN (preceu_ph_qbla, MIPS_V2HI_FTYPE_V4QI, MASK_DSP),
-  DIRECT_BUILTIN (preceu_ph_qbra, MIPS_V2HI_FTYPE_V4QI, MASK_DSP),
-  DIRECT_BUILTIN (shll_qb, MIPS_V4QI_FTYPE_V4QI_SI, MASK_DSP),
-  DIRECT_BUILTIN (shll_ph, MIPS_V2HI_FTYPE_V2HI_SI, MASK_DSP),
-  DIRECT_BUILTIN (shll_s_ph, MIPS_V2HI_FTYPE_V2HI_SI, MASK_DSP),
-  DIRECT_BUILTIN (shll_s_w, MIPS_SI_FTYPE_SI_SI, MASK_DSP),
-  DIRECT_BUILTIN (shrl_qb, MIPS_V4QI_FTYPE_V4QI_SI, MASK_DSP),
-  DIRECT_BUILTIN (shra_ph, MIPS_V2HI_FTYPE_V2HI_SI, MASK_DSP),
-  DIRECT_BUILTIN (shra_r_ph, MIPS_V2HI_FTYPE_V2HI_SI, MASK_DSP),
-  DIRECT_BUILTIN (shra_r_w, MIPS_SI_FTYPE_SI_SI, MASK_DSP),
-  DIRECT_BUILTIN (muleu_s_ph_qbl, MIPS_V2HI_FTYPE_V4QI_V2HI, MASK_DSP),
-  DIRECT_BUILTIN (muleu_s_ph_qbr, MIPS_V2HI_FTYPE_V4QI_V2HI, MASK_DSP),
-  DIRECT_BUILTIN (mulq_rs_ph, MIPS_V2HI_FTYPE_V2HI_V2HI, MASK_DSP),
-  DIRECT_BUILTIN (muleq_s_w_phl, MIPS_SI_FTYPE_V2HI_V2HI, MASK_DSP),
-  DIRECT_BUILTIN (muleq_s_w_phr, MIPS_SI_FTYPE_V2HI_V2HI, MASK_DSP),
-  DIRECT_BUILTIN (bitrev, MIPS_SI_FTYPE_SI, MASK_DSP),
-  DIRECT_BUILTIN (insv, MIPS_SI_FTYPE_SI_SI, MASK_DSP),
-  DIRECT_BUILTIN (repl_qb, MIPS_V4QI_FTYPE_SI, MASK_DSP),
-  DIRECT_BUILTIN (repl_ph, MIPS_V2HI_FTYPE_SI, MASK_DSP),
-  DIRECT_NO_TARGET_BUILTIN (cmpu_eq_qb, MIPS_VOID_FTYPE_V4QI_V4QI, MASK_DSP),
-  DIRECT_NO_TARGET_BUILTIN (cmpu_lt_qb, MIPS_VOID_FTYPE_V4QI_V4QI, MASK_DSP),
-  DIRECT_NO_TARGET_BUILTIN (cmpu_le_qb, MIPS_VOID_FTYPE_V4QI_V4QI, MASK_DSP),
-  DIRECT_BUILTIN (cmpgu_eq_qb, MIPS_SI_FTYPE_V4QI_V4QI, MASK_DSP),
-  DIRECT_BUILTIN (cmpgu_lt_qb, MIPS_SI_FTYPE_V4QI_V4QI, MASK_DSP),
-  DIRECT_BUILTIN (cmpgu_le_qb, MIPS_SI_FTYPE_V4QI_V4QI, MASK_DSP),
-  DIRECT_NO_TARGET_BUILTIN (cmp_eq_ph, MIPS_VOID_FTYPE_V2HI_V2HI, MASK_DSP),
-  DIRECT_NO_TARGET_BUILTIN (cmp_lt_ph, MIPS_VOID_FTYPE_V2HI_V2HI, MASK_DSP),
-  DIRECT_NO_TARGET_BUILTIN (cmp_le_ph, MIPS_VOID_FTYPE_V2HI_V2HI, MASK_DSP),
-  DIRECT_BUILTIN (pick_qb, MIPS_V4QI_FTYPE_V4QI_V4QI, MASK_DSP),
-  DIRECT_BUILTIN (pick_ph, MIPS_V2HI_FTYPE_V2HI_V2HI, MASK_DSP),
-  DIRECT_BUILTIN (packrl_ph, MIPS_V2HI_FTYPE_V2HI_V2HI, MASK_DSP),
-  DIRECT_NO_TARGET_BUILTIN (wrdsp, MIPS_VOID_FTYPE_SI_SI, MASK_DSP),
-  DIRECT_BUILTIN (rddsp, MIPS_SI_FTYPE_SI, MASK_DSP),
-  DIRECT_BUILTIN (lbux, MIPS_SI_FTYPE_PTR_SI, MASK_DSP),
-  DIRECT_BUILTIN (lhx, MIPS_SI_FTYPE_PTR_SI, MASK_DSP),
-  DIRECT_BUILTIN (lwx, MIPS_SI_FTYPE_PTR_SI, MASK_DSP),
-  BPOSGE_BUILTIN (32, MASK_DSP),
+  last_insn = 0;
+  hilo_delay = 2;
+  delayed_reg = 0;
+  lo_reg = gen_rtx_REG (SImode, LO_REGNUM);
 
-  /* The following are for the MIPS DSP ASE REV 2.  */
-  DIRECT_BUILTIN (absq_s_qb, MIPS_V4QI_FTYPE_V4QI, MASK_DSPR2),
-  DIRECT_BUILTIN (addu_ph, MIPS_V2HI_FTYPE_V2HI_V2HI, MASK_DSPR2),
-  DIRECT_BUILTIN (addu_s_ph, MIPS_V2HI_FTYPE_V2HI_V2HI, MASK_DSPR2),
-  DIRECT_BUILTIN (adduh_qb, MIPS_V4QI_FTYPE_V4QI_V4QI, MASK_DSPR2),
-  DIRECT_BUILTIN (adduh_r_qb, MIPS_V4QI_FTYPE_V4QI_V4QI, MASK_DSPR2),
-  DIRECT_BUILTIN (append, MIPS_SI_FTYPE_SI_SI_SI, MASK_DSPR2),
-  DIRECT_BUILTIN (balign, MIPS_SI_FTYPE_SI_SI_SI, MASK_DSPR2),
-  DIRECT_BUILTIN (cmpgdu_eq_qb, MIPS_SI_FTYPE_V4QI_V4QI, MASK_DSPR2),
-  DIRECT_BUILTIN (cmpgdu_lt_qb, MIPS_SI_FTYPE_V4QI_V4QI, MASK_DSPR2),
-  DIRECT_BUILTIN (cmpgdu_le_qb, MIPS_SI_FTYPE_V4QI_V4QI, MASK_DSPR2),
-  DIRECT_BUILTIN (mul_ph, MIPS_V2HI_FTYPE_V2HI_V2HI, MASK_DSPR2),
-  DIRECT_BUILTIN (mul_s_ph, MIPS_V2HI_FTYPE_V2HI_V2HI, MASK_DSPR2),
-  DIRECT_BUILTIN (mulq_rs_w, MIPS_SI_FTYPE_SI_SI, MASK_DSPR2),
-  DIRECT_BUILTIN (mulq_s_ph, MIPS_V2HI_FTYPE_V2HI_V2HI, MASK_DSPR2),
-  DIRECT_BUILTIN (mulq_s_w, MIPS_SI_FTYPE_SI_SI, MASK_DSPR2),
-  DIRECT_BUILTIN (precr_qb_ph, MIPS_V4QI_FTYPE_V2HI_V2HI, MASK_DSPR2),
-  DIRECT_BUILTIN (precr_sra_ph_w, MIPS_V2HI_FTYPE_SI_SI_SI, MASK_DSPR2),
-  DIRECT_BUILTIN (precr_sra_r_ph_w, MIPS_V2HI_FTYPE_SI_SI_SI, MASK_DSPR2),
-  DIRECT_BUILTIN (prepend, MIPS_SI_FTYPE_SI_SI_SI, MASK_DSPR2),
-  DIRECT_BUILTIN (shra_qb, MIPS_V4QI_FTYPE_V4QI_SI, MASK_DSPR2),
-  DIRECT_BUILTIN (shra_r_qb, MIPS_V4QI_FTYPE_V4QI_SI, MASK_DSPR2),
-  DIRECT_BUILTIN (shrl_ph, MIPS_V2HI_FTYPE_V2HI_SI, MASK_DSPR2),
-  DIRECT_BUILTIN (subu_ph, MIPS_V2HI_FTYPE_V2HI_V2HI, MASK_DSPR2),
-  DIRECT_BUILTIN (subu_s_ph, MIPS_V2HI_FTYPE_V2HI_V2HI, MASK_DSPR2),
-  DIRECT_BUILTIN (subuh_qb, MIPS_V4QI_FTYPE_V4QI_V4QI, MASK_DSPR2),
-  DIRECT_BUILTIN (subuh_r_qb, MIPS_V4QI_FTYPE_V4QI_V4QI, MASK_DSPR2),
-  DIRECT_BUILTIN (addqh_ph, MIPS_V2HI_FTYPE_V2HI_V2HI, MASK_DSPR2),
-  DIRECT_BUILTIN (addqh_r_ph, MIPS_V2HI_FTYPE_V2HI_V2HI, MASK_DSPR2),
-  DIRECT_BUILTIN (addqh_w, MIPS_SI_FTYPE_SI_SI, MASK_DSPR2),
-  DIRECT_BUILTIN (addqh_r_w, MIPS_SI_FTYPE_SI_SI, MASK_DSPR2),
-  DIRECT_BUILTIN (subqh_ph, MIPS_V2HI_FTYPE_V2HI_V2HI, MASK_DSPR2),
-  DIRECT_BUILTIN (subqh_r_ph, MIPS_V2HI_FTYPE_V2HI_V2HI, MASK_DSPR2),
-  DIRECT_BUILTIN (subqh_w, MIPS_SI_FTYPE_SI_SI, MASK_DSPR2),
-  DIRECT_BUILTIN (subqh_r_w, MIPS_SI_FTYPE_SI_SI, MASK_DSPR2)
-};
+  for (insn = get_insns (); insn != 0; insn = NEXT_INSN (insn))
+    if (INSN_P (insn))
+      {
+       if (GET_CODE (PATTERN (insn)) == SEQUENCE)
+         for (i = 0; i < XVECLEN (PATTERN (insn), 0); i++)
+           mips_avoid_hazard (last_insn, XVECEXP (PATTERN (insn), 0, i),
+                              &hilo_delay, &delayed_reg, lo_reg);
+       else
+         mips_avoid_hazard (last_insn, insn, &hilo_delay,
+                            &delayed_reg, lo_reg);
 
-static const struct builtin_description dsp_32only_bdesc[] =
+       last_insn = insn;
+      }
+}
+
+
+/* Implement TARGET_MACHINE_DEPENDENT_REORG.  */
+
+static void
+mips_reorg (void)
 {
-  DIRECT_BUILTIN (dpau_h_qbl, MIPS_DI_FTYPE_DI_V4QI_V4QI, MASK_DSP),
-  DIRECT_BUILTIN (dpau_h_qbr, MIPS_DI_FTYPE_DI_V4QI_V4QI, MASK_DSP),
-  DIRECT_BUILTIN (dpsu_h_qbl, MIPS_DI_FTYPE_DI_V4QI_V4QI, MASK_DSP),
-  DIRECT_BUILTIN (dpsu_h_qbr, MIPS_DI_FTYPE_DI_V4QI_V4QI, MASK_DSP),
-  DIRECT_BUILTIN (dpaq_s_w_ph, MIPS_DI_FTYPE_DI_V2HI_V2HI, MASK_DSP),
-  DIRECT_BUILTIN (dpsq_s_w_ph, MIPS_DI_FTYPE_DI_V2HI_V2HI, MASK_DSP),
-  DIRECT_BUILTIN (mulsaq_s_w_ph, MIPS_DI_FTYPE_DI_V2HI_V2HI, MASK_DSP),
-  DIRECT_BUILTIN (dpaq_sa_l_w, MIPS_DI_FTYPE_DI_SI_SI, MASK_DSP),
-  DIRECT_BUILTIN (dpsq_sa_l_w, MIPS_DI_FTYPE_DI_SI_SI, MASK_DSP),
-  DIRECT_BUILTIN (maq_s_w_phl, MIPS_DI_FTYPE_DI_V2HI_V2HI, MASK_DSP),
-  DIRECT_BUILTIN (maq_s_w_phr, MIPS_DI_FTYPE_DI_V2HI_V2HI, MASK_DSP),
-  DIRECT_BUILTIN (maq_sa_w_phl, MIPS_DI_FTYPE_DI_V2HI_V2HI, MASK_DSP),
-  DIRECT_BUILTIN (maq_sa_w_phr, MIPS_DI_FTYPE_DI_V2HI_V2HI, MASK_DSP),
-  DIRECT_BUILTIN (extr_w, MIPS_SI_FTYPE_DI_SI, MASK_DSP),
-  DIRECT_BUILTIN (extr_r_w, MIPS_SI_FTYPE_DI_SI, MASK_DSP),
-  DIRECT_BUILTIN (extr_rs_w, MIPS_SI_FTYPE_DI_SI, MASK_DSP),
-  DIRECT_BUILTIN (extr_s_h, MIPS_SI_FTYPE_DI_SI, MASK_DSP),
-  DIRECT_BUILTIN (extp, MIPS_SI_FTYPE_DI_SI, MASK_DSP),
-  DIRECT_BUILTIN (extpdp, MIPS_SI_FTYPE_DI_SI, MASK_DSP),
-  DIRECT_BUILTIN (shilo, MIPS_DI_FTYPE_DI_SI, MASK_DSP),
-  DIRECT_BUILTIN (mthlip, MIPS_DI_FTYPE_DI_SI, MASK_DSP),
+  mips16_lay_out_constants ();
+  if (TARGET_EXPLICIT_RELOCS)
+    {
+      if (mips_flag_delayed_branch)
+       dbr_schedule (get_insns ());
+      mips_avoid_hazards ();
+      if (TUNE_MIPS4130 && TARGET_VR4130_ALIGN)
+       vr4130_align_insns ();
+    }
+}
+\f
+/* Implement TARGET_ASM_OUTPUT_MI_THUNK.  Generate rtl rather than asm text
+   in order to avoid duplicating too much logic from elsewhere.  */
 
-  /* The following are for the MIPS DSP ASE REV 2.  */
-  DIRECT_BUILTIN (dpa_w_ph, MIPS_DI_FTYPE_DI_V2HI_V2HI, MASK_DSPR2),
-  DIRECT_BUILTIN (dps_w_ph, MIPS_DI_FTYPE_DI_V2HI_V2HI, MASK_DSPR2),
-  DIRECT_BUILTIN (madd, MIPS_DI_FTYPE_DI_SI_SI, MASK_DSPR2),
-  DIRECT_BUILTIN (maddu, MIPS_DI_FTYPE_DI_USI_USI, MASK_DSPR2),
-  DIRECT_BUILTIN (msub, MIPS_DI_FTYPE_DI_SI_SI, MASK_DSPR2),
-  DIRECT_BUILTIN (msubu, MIPS_DI_FTYPE_DI_USI_USI, MASK_DSPR2),
-  DIRECT_BUILTIN (mulsa_w_ph, MIPS_DI_FTYPE_DI_V2HI_V2HI, MASK_DSPR2),
-  DIRECT_BUILTIN (mult, MIPS_DI_FTYPE_SI_SI, MASK_DSPR2),
-  DIRECT_BUILTIN (multu, MIPS_DI_FTYPE_USI_USI, MASK_DSPR2),
-  DIRECT_BUILTIN (dpax_w_ph, MIPS_DI_FTYPE_DI_V2HI_V2HI, MASK_DSPR2),
-  DIRECT_BUILTIN (dpsx_w_ph, MIPS_DI_FTYPE_DI_V2HI_V2HI, MASK_DSPR2),
-  DIRECT_BUILTIN (dpaqx_s_w_ph, MIPS_DI_FTYPE_DI_V2HI_V2HI, MASK_DSPR2),
-  DIRECT_BUILTIN (dpaqx_sa_w_ph, MIPS_DI_FTYPE_DI_V2HI_V2HI, MASK_DSPR2),
-  DIRECT_BUILTIN (dpsqx_s_w_ph, MIPS_DI_FTYPE_DI_V2HI_V2HI, MASK_DSPR2),
-  DIRECT_BUILTIN (dpsqx_sa_w_ph, MIPS_DI_FTYPE_DI_V2HI_V2HI, MASK_DSPR2)
-};
+static void
+mips_output_mi_thunk (FILE *file, tree thunk_fndecl ATTRIBUTE_UNUSED,
+                     HOST_WIDE_INT delta, HOST_WIDE_INT vcall_offset,
+                     tree function)
+{
+  rtx this, temp1, temp2, insn, fnaddr;
+  bool use_sibcall_p;
+
+  /* Pretend to be a post-reload pass while generating rtl.  */
+  reload_completed = 1;
+
+  /* Mark the end of the (empty) prologue.  */
+  emit_note (NOTE_INSN_PROLOGUE_END);
+
+  /* Determine if we can use a sibcall to call FUNCTION directly.  */
+  fnaddr = XEXP (DECL_RTL (function), 0);
+  use_sibcall_p = (mips_function_ok_for_sibcall (function, NULL)
+                  && const_call_insn_operand (fnaddr, Pmode));
+
+  /* Determine if we need to load FNADDR from the GOT.  */
+  if (!use_sibcall_p)
+    switch (mips_classify_symbol (fnaddr, SYMBOL_CONTEXT_LEA))
+      {
+      case SYMBOL_GOT_PAGE_OFST:
+      case SYMBOL_GOT_DISP:
+       /* Pick a global pointer.  Use a call-clobbered register if
+          TARGET_CALL_SAVED_GP.  */
+       cfun->machine->global_pointer =
+         TARGET_CALL_SAVED_GP ? 15 : GLOBAL_POINTER_REGNUM;
+       SET_REGNO (pic_offset_table_rtx, cfun->machine->global_pointer);
+
+       /* Set up the global pointer for n32 or n64 abicalls.  */
+       mips_emit_loadgp ();
+       break;
+
+      default:
+       break;
+      }
+
+  /* We need two temporary registers in some cases.  */
+  temp1 = gen_rtx_REG (Pmode, 2);
+  temp2 = gen_rtx_REG (Pmode, 3);
+
+  /* Find out which register contains the "this" pointer.  */
+  if (aggregate_value_p (TREE_TYPE (TREE_TYPE (function)), function))
+    this = gen_rtx_REG (Pmode, GP_ARG_FIRST + 1);
+  else
+    this = gen_rtx_REG (Pmode, GP_ARG_FIRST);
+
+  /* Add DELTA to THIS.  */
+  if (delta != 0)
+    {
+      rtx offset = GEN_INT (delta);
+      if (!SMALL_OPERAND (delta))
+       {
+         mips_emit_move (temp1, offset);
+         offset = temp1;
+       }
+      emit_insn (gen_add3_insn (this, this, offset));
+    }
+
+  /* If needed, add *(*THIS + VCALL_OFFSET) to THIS.  */
+  if (vcall_offset != 0)
+    {
+      rtx addr;
+
+      /* Set TEMP1 to *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.  */
+      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.  */
+  if (use_sibcall_p)
+    {
+      insn = emit_call_insn (gen_sibcall_internal (fnaddr, const0_rtx));
+      SIBLING_CALL_P (insn) = 1;
+    }
+  else
+    {
+      /* This is messy.  gas treats "la $25,foo" as part of a call
+        sequence and may allow a global "foo" to be lazily bound.
+        The general move patterns therefore reject this combination.
+
+        In this context, lazy binding would actually be OK
+        for TARGET_CALL_CLOBBERED_GP, but it's still wrong for
+        TARGET_CALL_SAVED_GP; see mips_load_call_address.
+        We must therefore load the address via a temporary
+        register if mips_dangerous_for_la25_p.
+
+        If we jump to the temporary register rather than $25, the assembler
+        can use the move insn to fill the jump's delay slot.  */
+      if (TARGET_USE_PIC_FN_ADDR_REG
+         && !mips_dangerous_for_la25_p (fnaddr))
+       temp1 = gen_rtx_REG (Pmode, PIC_FUNCTION_ADDR_REGNUM);
+      mips_load_call_address (temp1, fnaddr, true);
+
+      if (TARGET_USE_PIC_FN_ADDR_REG
+         && REGNO (temp1) != PIC_FUNCTION_ADDR_REGNUM)
+       mips_emit_move (gen_rtx_REG (Pmode, PIC_FUNCTION_ADDR_REGNUM), temp1);
+      emit_jump_insn (gen_indirect_jump (temp1));
+    }
+
+  /* Run just enough of rest_of_compilation.  This sequence was
+     "borrowed" from alpha.c.  */
+  insn = get_insns ();
+  insn_locators_alloc ();
+  split_all_insns_noflow ();
+  mips16_lay_out_constants ();
+  shorten_branches (insn);
+  final_start_function (insn, file, 1);
+  final (insn, file, 1);
+  final_end_function ();
+
+  /* Clean up the vars set above.  Note that final_end_function resets
+     the global pointer for us.  */
+  reload_completed = 0;
+}
+\f
+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;
+  flag_delayed_branch = mips_flag_delayed_branch;
+  flag_schedule_insns = mips_base_schedule_insns;
+  flag_reorder_blocks_and_partition = mips_base_reorder_blocks_and_partition;
+  flag_move_loop_invariants = mips_base_move_loop_invariants;
+  align_loops = mips_base_align_loops;
+  align_jumps = mips_base_align_jumps;
+  align_functions = mips_base_align_functions;
+
+  if (mips16_p)
+    {
+      /* Select mips16 instruction set.  */
+      target_flags |= MASK_MIPS16;
+
+      /* Don't run the scheduler before reload, since it tends to
+         increase register pressure.  */
+      flag_schedule_insns = 0;
+
+      /* Don't do hot/cold partitioning.  The constant layout code expects
+        the whole function to be in a single section.  */
+      flag_reorder_blocks_and_partition = 0;
+
+      /* Don't move loop invariants, because it tends to increase
+        register pressure.  It also introduces an extra move in cases
+        where the constant is the first operand in a two-operand binary
+        instruction, or when it forms a register argument to a functon
+        call.  */
+      flag_move_loop_invariants = 0;
+
+      /* Silently disable -mexplicit-relocs since it doesn't apply
+        to mips16 code.  Even so, it would overly pedantic to warn
+        about "-mips16 -mexplicit-relocs", especially given that
+        we use a %gprel() operator.  */
+      target_flags &= ~MASK_EXPLICIT_RELOCS;
+
+      /* Experiments suggest we get the best overall results from using
+        the range of an unextended lw or sw.  Code that makes heavy use
+        of byte or short accesses can do better with ranges of 0...31
+        and 0...63 respectively, but most code is sensitive to the range
+        of lw and sw instead.  */
+      targetm.min_anchor_offset = 0;
+      targetm.max_anchor_offset = 127;
+
+      if (flag_pic || TARGET_ABICALLS)
+       sorry ("MIPS16 PIC");
+    }
+  else
+    {
+      /* Reset to select base non-mips16 ISA.  */
+      target_flags &= ~MASK_MIPS16;
+
+      /* When using explicit relocs, we call dbr_schedule from within
+        mips_reorg.  */
+      if (TARGET_EXPLICIT_RELOCS)
+       flag_delayed_branch = 0;
 
-/* This helps provide a mapping from builtin function codes to bdesc
-   arrays.  */
+      /* 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;
+       }
 
-struct bdesc_map
-{
-  /* The builtin function table that this entry describes.  */
-  const struct builtin_description *bdesc;
+      targetm.min_anchor_offset = -32768;
+      targetm.max_anchor_offset = 32767;
+    }
 
-  /* The number of entries in the builtin function table.  */
-  unsigned int size;
+  /* (Re)initialize mips target internals for new ISA.  */
+  mips_init_split_addresses ();
+  mips_init_relocs ();
 
-  /* The target processor that supports these builtin functions.
-     PROCESSOR_MAX means we enable them for all processors.  */
-  enum processor_type proc;
+  if (was_mips16_p >= 0)
+    /* Reinitialize target-dependent state.  */
+    target_reinit ();
 
-  /* If the target has these flags, this builtin function table
-     will not be supported.  */
-  int unsupported_target_flags;
-};
+  was_mips16_p = TARGET_MIPS16;
+}
 
-static const struct bdesc_map bdesc_arrays[] =
+/* 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)
 {
-  { mips_bdesc, ARRAY_SIZE (mips_bdesc), PROCESSOR_MAX, 0 },
-  { sb1_bdesc, ARRAY_SIZE (sb1_bdesc), PROCESSOR_SB1, 0 },
-  { dsp_bdesc, ARRAY_SIZE (dsp_bdesc), PROCESSOR_MAX, 0 },
-  { dsp_32only_bdesc, ARRAY_SIZE (dsp_32only_bdesc), PROCESSOR_MAX,
-    MASK_64BIT }
-};
+  mips_set_mips16_mode (mips_use_mips16_mode_p (fndecl));
+}
+\f
+/* Allocate a chunk of memory for per-function machine-dependent data.  */
+static struct machine_function *
+mips_init_machine_status (void)
+{
+  return ((struct machine_function *)
+         ggc_alloc_cleared (sizeof (struct machine_function)));
+}
 
-/* Take the argument ARGNUM of the arglist of EXP and convert it into a form
-   suitable for input operand OP of instruction ICODE.  Return the value.  */
+/* Return the processor associated with the given ISA level, or null
+   if the ISA isn't valid.  */
 
-static rtx
-mips_prepare_builtin_arg (enum insn_code icode,
-                         unsigned int op, tree exp, unsigned int argnum)
+static const struct mips_cpu_info *
+mips_cpu_info_from_isa (int isa)
 {
-  rtx value;
-  enum machine_mode mode;
+  unsigned int i;
 
-  value = expand_normal (CALL_EXPR_ARG (exp, argnum));
-  mode = insn_data[icode].operand[op].mode;
-  if (!insn_data[icode].operand[op].predicate (value, mode))
-    {
-      value = copy_to_mode_reg (mode, value);
-      /* Check the predicate again.  */
-      if (!insn_data[icode].operand[op].predicate (value, mode))
-       {
-         error ("invalid argument to builtin function");
-         return const0_rtx;
-       }
-    }
+  for (i = 0; i < ARRAY_SIZE (mips_cpu_info_table); i++)
+    if (mips_cpu_info_table[i].isa == isa)
+      return mips_cpu_info_table + i;
 
-  return value;
+  return 0;
 }
 
-/* Return an rtx suitable for output operand OP of instruction ICODE.
-   If TARGET is non-null, try to use it where possible.  */
+/* Return true if GIVEN is the same as CANONICAL, or if it is CANONICAL
+   with a final "000" replaced by "k".  Ignore case.
 
-static rtx
-mips_prepare_builtin_target (enum insn_code icode, unsigned int op, rtx target)
+   Note: this function is shared between GCC and GAS.  */
+
+static bool
+mips_strict_matching_cpu_name_p (const char *canonical, const char *given)
 {
-  enum machine_mode mode;
+  while (*given != 0 && TOLOWER (*given) == TOLOWER (*canonical))
+    given++, canonical++;
 
-  mode = insn_data[icode].operand[op].mode;
-  if (target == 0 || !insn_data[icode].operand[op].predicate (target, mode))
-    target = gen_reg_rtx (mode);
+  return ((*given == 0 && *canonical == 0)
+         || (strcmp (canonical, "000") == 0 && strcasecmp (given, "k") == 0));
+}
 
-  return target;
+
+/* Return true if GIVEN matches CANONICAL, where GIVEN is a user-supplied
+   CPU name.  We've traditionally allowed a lot of variation here.
+
+   Note: this function is shared between GCC and GAS.  */
+
+static bool
+mips_matching_cpu_name_p (const char *canonical, const char *given)
+{
+  /* First see if the name matches exactly, or with a final "000"
+     turned into "k".  */
+  if (mips_strict_matching_cpu_name_p (canonical, given))
+    return true;
+
+  /* If not, try comparing based on numerical designation alone.
+     See if GIVEN is an unadorned number, or 'r' followed by a number.  */
+  if (TOLOWER (*given) == 'r')
+    given++;
+  if (!ISDIGIT (*given))
+    return false;
+
+  /* Skip over some well-known prefixes in the canonical name,
+     hoping to find a number there too.  */
+  if (TOLOWER (canonical[0]) == 'v' && TOLOWER (canonical[1]) == 'r')
+    canonical += 2;
+  else if (TOLOWER (canonical[0]) == 'r' && TOLOWER (canonical[1]) == 'm')
+    canonical += 2;
+  else if (TOLOWER (canonical[0]) == 'r')
+    canonical += 1;
+
+  return mips_strict_matching_cpu_name_p (canonical, given);
 }
 
-/* Expand builtin functions.  This is called from TARGET_EXPAND_BUILTIN.  */
 
-rtx
-mips_expand_builtin (tree exp, rtx target, rtx subtarget ATTRIBUTE_UNUSED,
-                    enum machine_mode mode ATTRIBUTE_UNUSED,
-                    int ignore ATTRIBUTE_UNUSED)
+/* Return the mips_cpu_info entry for the processor or ISA given
+   by CPU_STRING.  Return null if the string isn't recognized.
+
+   A similar function exists in GAS.  */
+
+static const struct mips_cpu_info *
+mips_parse_cpu (const char *cpu_string)
 {
-  enum insn_code icode;
-  enum mips_builtin_type type;
-  tree fndecl;
-  unsigned int fcode;
-  const struct builtin_description *bdesc;
-  const struct bdesc_map *m;
+  unsigned int i;
+  const char *s;
 
-  fndecl = TREE_OPERAND (CALL_EXPR_FN (exp), 0);
-  fcode = DECL_FUNCTION_CODE (fndecl);
+  /* In the past, we allowed upper-case CPU names, but it doesn't
+     work well with the multilib machinery.  */
+  for (s = cpu_string; *s != 0; s++)
+    if (ISUPPER (*s))
+      {
+       warning (0, "the cpu name must be lower case");
+       break;
+      }
 
-  if (TARGET_MIPS16)
+  /* 'from-abi' selects the most compatible architecture for the given
+     ABI: MIPS I for 32-bit ABIs and MIPS III for 64-bit ABIs.  For the
+     EABIs, we have to decide whether we're using the 32-bit or 64-bit
+     version.  Look first at the -mgp options, if given, otherwise base
+     the choice on MASK_64BIT in TARGET_DEFAULT.  */
+  if (strcasecmp (cpu_string, "from-abi") == 0)
+    return mips_cpu_info_from_isa (ABI_NEEDS_32BIT_REGS ? 1
+                                  : ABI_NEEDS_64BIT_REGS ? 3
+                                  : (TARGET_64BIT ? 3 : 1));
+
+  /* 'default' has traditionally been a no-op.  Probably not very useful.  */
+  if (strcasecmp (cpu_string, "default") == 0)
+    return 0;
+
+  for (i = 0; i < ARRAY_SIZE (mips_cpu_info_table); i++)
+    if (mips_matching_cpu_name_p (mips_cpu_info_table[i].name, cpu_string))
+      return mips_cpu_info_table + i;
+
+  return 0;
+}
+
+
+/* Set up globals to generate code for the ISA or processor
+   described by INFO.  */
+
+static void
+mips_set_architecture (const struct mips_cpu_info *info)
+{
+  if (info != 0)
     {
-      error ("built-in function %qs not supported for MIPS16",
-            IDENTIFIER_POINTER (DECL_NAME (fndecl)));
-      return const0_rtx;
+      mips_arch_info = info;
+      mips_arch = info->cpu;
+      mips_isa = info->isa;
     }
+}
 
-  bdesc = NULL;
-  for (m = bdesc_arrays; m < &bdesc_arrays[ARRAY_SIZE (bdesc_arrays)]; m++)
+
+/* Likewise for tuning.  */
+
+static void
+mips_set_tune (const struct mips_cpu_info *info)
+{
+  if (info != 0)
     {
-      if (fcode < m->size)
-       {
-         bdesc = m->bdesc;
-         icode = bdesc[fcode].icode;
-         type = bdesc[fcode].builtin_type;
-         break;
-       }
-      fcode -= m->size;
+      mips_tune_info = info;
+      mips_tune = info->cpu;
     }
-  if (bdesc == NULL)
-    return 0;
+}
 
-  switch (type)
+/* Implement TARGET_HANDLE_OPTION.  */
+
+static bool
+mips_handle_option (size_t code, const char *arg, int value ATTRIBUTE_UNUSED)
+{
+  switch (code)
     {
-    case MIPS_BUILTIN_DIRECT:
-      return mips_expand_builtin_direct (icode, target, exp, true);
+    case OPT_mabi_:
+      if (strcmp (arg, "32") == 0)
+       mips_abi = ABI_32;
+      else if (strcmp (arg, "o64") == 0)
+       mips_abi = ABI_O64;
+      else if (strcmp (arg, "n32") == 0)
+       mips_abi = ABI_N32;
+      else if (strcmp (arg, "64") == 0)
+       mips_abi = ABI_64;
+      else if (strcmp (arg, "eabi") == 0)
+       mips_abi = ABI_EABI;
+      else
+       return false;
+      return true;
 
-    case MIPS_BUILTIN_DIRECT_NO_TARGET:
-      return mips_expand_builtin_direct (icode, target, exp, false);
+    case OPT_march_:
+    case OPT_mtune_:
+      return mips_parse_cpu (arg) != 0;
 
-    case MIPS_BUILTIN_MOVT:
-    case MIPS_BUILTIN_MOVF:
-      return mips_expand_builtin_movtf (type, icode, bdesc[fcode].cond,
-                                       target, exp);
+    case OPT_mips:
+      mips_isa_info = mips_parse_cpu (ACONCAT (("mips", arg, NULL)));
+      return mips_isa_info != 0;
 
-    case MIPS_BUILTIN_CMP_ANY:
-    case MIPS_BUILTIN_CMP_ALL:
-    case MIPS_BUILTIN_CMP_UPPER:
-    case MIPS_BUILTIN_CMP_LOWER:
-    case MIPS_BUILTIN_CMP_SINGLE:
-      return mips_expand_builtin_compare (type, icode, bdesc[fcode].cond,
-                                         target, exp);
+    case OPT_mno_flush_func:
+      mips_cache_flush_func = NULL;
+      return true;
 
-    case MIPS_BUILTIN_BPOSGE32:
-      return mips_expand_builtin_bposge (type, target);
+    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 0;
+      return true;
     }
 }
 
-/* Init builtin functions.  This is called from TARGET_INIT_BUILTIN.  */
+/* Set up the threshold for data to go into the small data area, instead
+   of the normal data area, and detect any conflicts in the switches.  */
 
 void
-mips_init_builtins (void)
+override_options (void)
 {
-  const struct builtin_description *d;
-  const struct bdesc_map *m;
-  tree types[(int) MIPS_MAX_FTYPE_MAX];
-  tree V2SF_type_node;
-  tree V2HI_type_node;
-  tree V4QI_type_node;
-  unsigned int offset;
-
-  /* We have only builtins for -mpaired-single, -mips3d and -mdsp.  */
-  if (!TARGET_PAIRED_SINGLE_FLOAT && !TARGET_DSP)
-    return;
-
-  if (TARGET_PAIRED_SINGLE_FLOAT)
-    {
-      V2SF_type_node = build_vector_type_for_mode (float_type_node, V2SFmode);
-
-      types[MIPS_V2SF_FTYPE_V2SF]
-       = build_function_type_list (V2SF_type_node, V2SF_type_node, NULL_TREE);
-
-      types[MIPS_V2SF_FTYPE_V2SF_V2SF]
-       = build_function_type_list (V2SF_type_node,
-                                   V2SF_type_node, V2SF_type_node, NULL_TREE);
+  int i, start, regno;
+  enum machine_mode mode;
 
-      types[MIPS_V2SF_FTYPE_V2SF_V2SF_INT]
-       = build_function_type_list (V2SF_type_node,
-                                   V2SF_type_node, V2SF_type_node,
-                                   integer_type_node, NULL_TREE);
+#ifdef SUBTARGET_OVERRIDE_OPTIONS
+  SUBTARGET_OVERRIDE_OPTIONS;
+#endif
 
-      types[MIPS_V2SF_FTYPE_V2SF_V2SF_V2SF_V2SF]
-       = build_function_type_list (V2SF_type_node,
-                                   V2SF_type_node, V2SF_type_node,
-                                   V2SF_type_node, V2SF_type_node, NULL_TREE);
+  mips_section_threshold = g_switch_set ? g_switch_value : MIPS_DEFAULT_GVALUE;
 
-      types[MIPS_V2SF_FTYPE_SF_SF]
-       = build_function_type_list (V2SF_type_node,
-                                   float_type_node, float_type_node, NULL_TREE);
+  /* The following code determines the architecture and register size.
+     Similar code was added to GAS 2.14 (see tc-mips.c:md_after_parse_args()).
+     The GAS and GCC code should be kept in sync as much as possible.  */
 
-      types[MIPS_INT_FTYPE_V2SF_V2SF]
-       = build_function_type_list (integer_type_node,
-                                   V2SF_type_node, V2SF_type_node, NULL_TREE);
+  if (mips_arch_string != 0)
+    mips_set_architecture (mips_parse_cpu (mips_arch_string));
 
-      types[MIPS_INT_FTYPE_V2SF_V2SF_V2SF_V2SF]
-       = build_function_type_list (integer_type_node,
-                                   V2SF_type_node, V2SF_type_node,
-                                   V2SF_type_node, V2SF_type_node, NULL_TREE);
+  if (mips_isa_info != 0)
+    {
+      if (mips_arch_info == 0)
+       mips_set_architecture (mips_isa_info);
+      else if (mips_arch_info->isa != mips_isa_info->isa)
+       error ("-%s conflicts with the other architecture options, "
+              "which specify a %s processor",
+              mips_isa_info->name,
+              mips_cpu_info_from_isa (mips_arch_info->isa)->name);
+    }
 
-      types[MIPS_INT_FTYPE_SF_SF]
-       = build_function_type_list (integer_type_node,
-                                   float_type_node, float_type_node, NULL_TREE);
+  if (mips_arch_info == 0)
+    {
+#ifdef MIPS_CPU_STRING_DEFAULT
+      mips_set_architecture (mips_parse_cpu (MIPS_CPU_STRING_DEFAULT));
+#else
+      mips_set_architecture (mips_cpu_info_from_isa (MIPS_ISA_DEFAULT));
+#endif
+    }
 
-      types[MIPS_INT_FTYPE_DF_DF]
-       = build_function_type_list (integer_type_node,
-                                   double_type_node, double_type_node, NULL_TREE);
+  if (ABI_NEEDS_64BIT_REGS && !ISA_HAS_64BIT_REGS)
+    error ("-march=%s is not compatible with the selected ABI",
+          mips_arch_info->name);
 
-      types[MIPS_SF_FTYPE_V2SF]
-       = build_function_type_list (float_type_node, V2SF_type_node, NULL_TREE);
+  /* Optimize for mips_arch, unless -mtune selects a different processor.  */
+  if (mips_tune_string != 0)
+    mips_set_tune (mips_parse_cpu (mips_tune_string));
 
-      types[MIPS_SF_FTYPE_SF]
-       = build_function_type_list (float_type_node,
-                                   float_type_node, NULL_TREE);
+  if (mips_tune_info == 0)
+    mips_set_tune (mips_arch_info);
 
-      types[MIPS_SF_FTYPE_SF_SF]
-       = build_function_type_list (float_type_node,
-                                   float_type_node, float_type_node, NULL_TREE);
+  /* Set cost structure for the processor.  */
+  if (optimize_size)
+    mips_cost = &mips_rtx_cost_optimize_size;
+  else
+    mips_cost = &mips_rtx_cost_data[mips_tune];
 
-      types[MIPS_DF_FTYPE_DF]
-       = build_function_type_list (double_type_node,
-                                   double_type_node, NULL_TREE);
+  /* 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;
 
-      types[MIPS_DF_FTYPE_DF_DF]
-       = build_function_type_list (double_type_node,
-                                   double_type_node, double_type_node, NULL_TREE);
+  if ((target_flags_explicit & MASK_64BIT) != 0)
+    {
+      /* The user specified the size of the integer registers.  Make sure
+        it agrees with the ABI and ISA.  */
+      if (TARGET_64BIT && !ISA_HAS_64BIT_REGS)
+       error ("-mgp64 used with a 32-bit processor");
+      else if (!TARGET_64BIT && ABI_NEEDS_64BIT_REGS)
+       error ("-mgp32 used with a 64-bit ABI");
+      else if (TARGET_64BIT && ABI_NEEDS_32BIT_REGS)
+       error ("-mgp64 used with a 32-bit ABI");
+    }
+  else
+    {
+      /* Infer the integer register size from the ABI and processor.
+        Restrict ourselves to 32-bit registers if that's all the
+        processor has, or if the ABI cannot handle 64-bit registers.  */
+      if (ABI_NEEDS_32BIT_REGS || !ISA_HAS_64BIT_REGS)
+       target_flags &= ~MASK_64BIT;
+      else
+       target_flags |= MASK_64BIT;
     }
 
-  if (TARGET_DSP)
+  if ((target_flags_explicit & MASK_FLOAT64) != 0)
     {
-      V2HI_type_node = build_vector_type_for_mode (intHI_type_node, V2HImode);
-      V4QI_type_node = build_vector_type_for_mode (intQI_type_node, V4QImode);
+      /* Really, -mfp32 and -mfp64 are ornamental options.  There's
+        only one right answer here.  */
+      if (TARGET_64BIT && TARGET_DOUBLE_FLOAT && !TARGET_FLOAT64)
+       error ("unsupported combination: %s", "-mgp64 -mfp32 -mdouble-float");
+      else if (!TARGET_64BIT && TARGET_FLOAT64
+              && !(ISA_HAS_MXHC1 && mips_abi == ABI_32))
+       error ("-mgp32 and -mfp64 can only be combined if the target"
+              " supports the mfhc1 and mthc1 instructions");
+      else if (TARGET_SINGLE_FLOAT && TARGET_FLOAT64)
+       error ("unsupported combination: %s", "-mfp64 -msingle-float");
+    }
+  else
+    {
+      /* -msingle-float selects 32-bit float registers.  Otherwise the
+        float registers should be the same size as the integer ones.  */
+      if (TARGET_64BIT && TARGET_DOUBLE_FLOAT)
+       target_flags |= MASK_FLOAT64;
+      else
+       target_flags &= ~MASK_FLOAT64;
+    }
 
-      types[MIPS_V2HI_FTYPE_V2HI_V2HI]
-       = build_function_type_list (V2HI_type_node,
-                                   V2HI_type_node, V2HI_type_node,
-                                   NULL_TREE);
+  /* End of code shared with GAS.  */
 
-      types[MIPS_SI_FTYPE_SI_SI]
-       = build_function_type_list (intSI_type_node,
-                                   intSI_type_node, intSI_type_node,
-                                   NULL_TREE);
+  if ((target_flags_explicit & MASK_LONG64) == 0)
+    {
+      if ((mips_abi == ABI_EABI && TARGET_64BIT) || mips_abi == ABI_64)
+       target_flags |= MASK_LONG64;
+      else
+       target_flags &= ~MASK_LONG64;
+    }
 
-      types[MIPS_V4QI_FTYPE_V4QI_V4QI]
-       = build_function_type_list (V4QI_type_node,
-                                   V4QI_type_node, V4QI_type_node,
-                                   NULL_TREE);
+  if (!TARGET_OLDABI)
+    flag_pcc_struct_return = 0;
 
-      types[MIPS_SI_FTYPE_V4QI]
-       = build_function_type_list (intSI_type_node,
-                                   V4QI_type_node,
-                                   NULL_TREE);
+  if ((target_flags_explicit & MASK_BRANCHLIKELY) == 0)
+    {
+      /* If neither -mbranch-likely nor -mno-branch-likely was given
+        on the command line, set MASK_BRANCHLIKELY based on the target
+        architecture and tuning flags.  Annulled delay slots are a
+        size win, so we only consider the processor-specific tuning
+        for !optimize_size.  */
+      if (ISA_HAS_BRANCHLIKELY
+         && (optimize_size
+             || (mips_tune_info->tune_flags & PTF_AVOID_BRANCHLIKELY) == 0))
+       target_flags |= MASK_BRANCHLIKELY;
+      else
+       target_flags &= ~MASK_BRANCHLIKELY;
+    }
+  else if (TARGET_BRANCHLIKELY && !ISA_HAS_BRANCHLIKELY)
+    warning (0, "the %qs architecture does not support branch-likely"
+            " instructions", mips_arch_info->name);
 
-      types[MIPS_V2HI_FTYPE_V2HI]
-       = build_function_type_list (V2HI_type_node,
-                                   V2HI_type_node,
-                                   NULL_TREE);
+  /* The effect of -mabicalls isn't defined for the EABI.  */
+  if (mips_abi == ABI_EABI && TARGET_ABICALLS)
+    {
+      error ("unsupported combination: %s", "-mabicalls -mabi=eabi");
+      target_flags &= ~MASK_ABICALLS;
+    }
 
-      types[MIPS_SI_FTYPE_SI]
-       = build_function_type_list (intSI_type_node,
-                                   intSI_type_node,
-                                   NULL_TREE);
+  /* MIPS16 cannot generate PIC yet.  */
+  if (TARGET_MIPS16 && (flag_pic || TARGET_ABICALLS))
+    {
+      sorry ("MIPS16 PIC");
+      target_flags &= ~MASK_ABICALLS;
+      flag_pic = flag_pie = flag_shlib = 0;
+    }
 
-      types[MIPS_V4QI_FTYPE_V2HI_V2HI]
-       = build_function_type_list (V4QI_type_node,
-                                   V2HI_type_node, V2HI_type_node,
-                                   NULL_TREE);
+  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.)
 
-      types[MIPS_V2HI_FTYPE_SI_SI]
-       = build_function_type_list (V2HI_type_node,
-                                   intSI_type_node, intSI_type_node,
-                                   NULL_TREE);
+       Also, there is a body of code that uses __PIC__ to distinguish
+       between -mabicalls and -mno-abicalls code.  */
+    flag_pic = 1;
 
-      types[MIPS_SI_FTYPE_V2HI]
-       = build_function_type_list (intSI_type_node,
-                                   V2HI_type_node,
-                                   NULL_TREE);
+  /* -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
+     above.  */
+  if (optimize > 2 && (target_flags_explicit & MASK_VR4130_ALIGN) == 0)
+    target_flags |= MASK_VR4130_ALIGN;
 
-      types[MIPS_V2HI_FTYPE_V4QI]
-       = build_function_type_list (V2HI_type_node,
-                                   V4QI_type_node,
-                                   NULL_TREE);
+  /* 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;
 
-      types[MIPS_V4QI_FTYPE_V4QI_SI]
-       = build_function_type_list (V4QI_type_node,
-                                   V4QI_type_node, intSI_type_node,
-                                   NULL_TREE);
+  /* 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%>");
 
-      types[MIPS_V2HI_FTYPE_V2HI_SI]
-       = build_function_type_list (V2HI_type_node,
-                                   V2HI_type_node, intSI_type_node,
-                                   NULL_TREE);
+         TARGET_LOCAL_SDATA = false;
+         TARGET_EXTERN_SDATA = false;
+       }
+      else
+       {
+         if (TARGET_VXWORKS_RTP)
+           warning (0, "cannot use small-data accesses for %qs", "-mrtp");
 
-      types[MIPS_V2HI_FTYPE_V4QI_V2HI]
-       = build_function_type_list (V2HI_type_node,
-                                   V4QI_type_node, V2HI_type_node,
-                                   NULL_TREE);
+         if (TARGET_ABICALLS)
+           warning (0, "cannot use small-data accesses for %qs",
+                    "-mabicalls");
+       }
+    }
 
-      types[MIPS_SI_FTYPE_V2HI_V2HI]
-       = build_function_type_list (intSI_type_node,
-                                   V2HI_type_node, V2HI_type_node,
-                                   NULL_TREE);
+#ifdef MIPS_TFMODE_FORMAT
+  REAL_MODE_FORMAT (TFmode) = &MIPS_TFMODE_FORMAT;
+#endif
 
-      types[MIPS_DI_FTYPE_DI_V4QI_V4QI]
-       = build_function_type_list (intDI_type_node,
-                                   intDI_type_node, V4QI_type_node, V4QI_type_node,
-                                   NULL_TREE);
+  /* Make sure that the user didn't turn off paired single support when
+     MIPS-3D support is requested.  */
+  if (TARGET_MIPS3D && (target_flags_explicit & MASK_PAIRED_SINGLE_FLOAT)
+      && !TARGET_PAIRED_SINGLE_FLOAT)
+    error ("-mips3d requires -mpaired-single");
 
-      types[MIPS_DI_FTYPE_DI_V2HI_V2HI]
-       = build_function_type_list (intDI_type_node,
-                                   intDI_type_node, V2HI_type_node, V2HI_type_node,
-                                   NULL_TREE);
+  /* If TARGET_MIPS3D, enable MASK_PAIRED_SINGLE_FLOAT.  */
+  if (TARGET_MIPS3D)
+    target_flags |= MASK_PAIRED_SINGLE_FLOAT;
 
-      types[MIPS_DI_FTYPE_DI_SI_SI]
-       = build_function_type_list (intDI_type_node,
-                                   intDI_type_node, intSI_type_node, intSI_type_node,
-                                   NULL_TREE);
+  /* Make sure that when TARGET_PAIRED_SINGLE_FLOAT is true, TARGET_FLOAT64
+     and TARGET_HARD_FLOAT_ABI are both true.  */
+  if (TARGET_PAIRED_SINGLE_FLOAT && !(TARGET_FLOAT64 && TARGET_HARD_FLOAT_ABI))
+    error ("-mips3d/-mpaired-single must be used with -mfp64 -mhard-float");
 
-      types[MIPS_V4QI_FTYPE_SI]
-       = build_function_type_list (V4QI_type_node,
-                                   intSI_type_node,
-                                   NULL_TREE);
+  /* Make sure that the ISA supports TARGET_PAIRED_SINGLE_FLOAT when it is
+     enabled.  */
+  if (TARGET_PAIRED_SINGLE_FLOAT && !ISA_MIPS64)
+    error ("-mips3d/-mpaired-single must be used with -mips64");
 
-      types[MIPS_V2HI_FTYPE_SI]
-       = build_function_type_list (V2HI_type_node,
-                                   intSI_type_node,
-                                   NULL_TREE);
+  /* If TARGET_DSPR2, enable MASK_DSP.  */
+  if (TARGET_DSPR2)
+    target_flags |= MASK_DSP;
 
-      types[MIPS_VOID_FTYPE_V4QI_V4QI]
-       = build_function_type_list (void_type_node,
-                                   V4QI_type_node, V4QI_type_node,
-                                   NULL_TREE);
+  mips_print_operand_punct['?'] = 1;
+  mips_print_operand_punct['#'] = 1;
+  mips_print_operand_punct['/'] = 1;
+  mips_print_operand_punct['&'] = 1;
+  mips_print_operand_punct['!'] = 1;
+  mips_print_operand_punct['*'] = 1;
+  mips_print_operand_punct['@'] = 1;
+  mips_print_operand_punct['.'] = 1;
+  mips_print_operand_punct['('] = 1;
+  mips_print_operand_punct[')'] = 1;
+  mips_print_operand_punct['['] = 1;
+  mips_print_operand_punct[']'] = 1;
+  mips_print_operand_punct['<'] = 1;
+  mips_print_operand_punct['>'] = 1;
+  mips_print_operand_punct['{'] = 1;
+  mips_print_operand_punct['}'] = 1;
+  mips_print_operand_punct['^'] = 1;
+  mips_print_operand_punct['$'] = 1;
+  mips_print_operand_punct['+'] = 1;
+  mips_print_operand_punct['~'] = 1;
+  mips_print_operand_punct['|'] = 1;
+  mips_print_operand_punct['-'] = 1;
 
-      types[MIPS_SI_FTYPE_V4QI_V4QI]
-       = build_function_type_list (intSI_type_node,
-                                   V4QI_type_node, V4QI_type_node,
-                                   NULL_TREE);
+  /* Set up array to map GCC register number to debug register number.
+     Ignore the special purpose register numbers.  */
 
-      types[MIPS_VOID_FTYPE_V2HI_V2HI]
-       = build_function_type_list (void_type_node,
-                                   V2HI_type_node, V2HI_type_node,
-                                   NULL_TREE);
+  for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
+    {
+      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;
+    }
 
-      types[MIPS_SI_FTYPE_DI_SI]
-       = build_function_type_list (intSI_type_node,
-                                   intDI_type_node, intSI_type_node,
-                                   NULL_TREE);
+  start = GP_DBX_FIRST - GP_REG_FIRST;
+  for (i = GP_REG_FIRST; i <= GP_REG_LAST; i++)
+    mips_dbx_regno[i] = i + start;
 
-      types[MIPS_DI_FTYPE_DI_SI]
-       = build_function_type_list (intDI_type_node,
-                                   intDI_type_node, intSI_type_node,
-                                   NULL_TREE);
+  start = FP_DBX_FIRST - FP_REG_FIRST;
+  for (i = FP_REG_FIRST; i <= FP_REG_LAST; i++)
+    mips_dbx_regno[i] = i + start;
 
-      types[MIPS_VOID_FTYPE_SI_SI]
-       = build_function_type_list (void_type_node,
-                                   intSI_type_node, intSI_type_node,
-                                   NULL_TREE);
+  /* 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;
+    }
 
-      types[MIPS_SI_FTYPE_PTR_SI]
-       = build_function_type_list (intSI_type_node,
-                                   ptr_type_node, intSI_type_node,
-                                   NULL_TREE);
+  /* Set up array giving whether a given register can hold a given mode.  */
 
-      types[MIPS_SI_FTYPE_VOID]
-       = build_function_type (intSI_type_node, void_list_node);
+  for (mode = VOIDmode;
+       mode != MAX_MACHINE_MODE;
+       mode = (enum machine_mode) ((int)mode + 1))
+    {
+      register int size                     = GET_MODE_SIZE (mode);
+      register enum mode_class class = GET_MODE_CLASS (mode);
 
-      if (TARGET_DSPR2)
+      for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
        {
-         types[MIPS_V4QI_FTYPE_V4QI]
-           = build_function_type_list (V4QI_type_node,
-                                       V4QI_type_node,
-                                       NULL_TREE);
+         register int temp;
 
-         types[MIPS_SI_FTYPE_SI_SI_SI]
-           = build_function_type_list (intSI_type_node,
-                                       intSI_type_node, intSI_type_node,
-                                       intSI_type_node, NULL_TREE);
+         if (mode == CCV2mode)
+           temp = (ISA_HAS_8CC
+                   && ST_REG_P (regno)
+                   && (regno - ST_REG_FIRST) % 2 == 0);
 
-         types[MIPS_DI_FTYPE_DI_USI_USI]
-           = build_function_type_list (intDI_type_node,
-                                       intDI_type_node,
-                                       unsigned_intSI_type_node,
-                                       unsigned_intSI_type_node, NULL_TREE);
+         else if (mode == CCV4mode)
+           temp = (ISA_HAS_8CC
+                   && ST_REG_P (regno)
+                   && (regno - ST_REG_FIRST) % 4 == 0);
 
-         types[MIPS_DI_FTYPE_SI_SI]
-           = build_function_type_list (intDI_type_node,
-                                       intSI_type_node, intSI_type_node,
-                                       NULL_TREE);
+         else if (mode == CCmode)
+           {
+             if (! ISA_HAS_8CC)
+               temp = (regno == FPSW_REGNUM);
+             else
+               temp = (ST_REG_P (regno) || GP_REG_P (regno)
+                       || FP_REG_P (regno));
+           }
 
-         types[MIPS_DI_FTYPE_USI_USI]
-           = build_function_type_list (intDI_type_node,
-                                       unsigned_intSI_type_node,
-                                       unsigned_intSI_type_node, NULL_TREE);
+         else if (GP_REG_P (regno))
+           temp = ((regno & 1) == 0 || size <= UNITS_PER_WORD);
+
+         else if (FP_REG_P (regno))
+           temp = ((((regno % MAX_FPRS_PER_FMT) == 0)
+                    || (MIN_FPRS_PER_FMT == 1
+                        && size <= UNITS_PER_FPREG))
+                   && (((class == MODE_FLOAT || class == MODE_COMPLEX_FLOAT
+                         || class == MODE_VECTOR_FLOAT)
+                        && size <= UNITS_PER_FPVALUE)
+                       /* Allow integer modes that fit into a single
+                          register.  We need to put integers into FPRs
+                          when using instructions like cvt and trunc.
+                          We can't allow sizes smaller than a word,
+                          the FPU has no appropriate load/store
+                          instructions for those.  */
+                       || (class == MODE_INT
+                           && size >= MIN_UNITS_PER_WORD
+                           && size <= UNITS_PER_FPREG)
+                       /* Allow TFmode for CCmode reloads.  */
+                       || (ISA_HAS_8CC && mode == TFmode)));
+
+          else if (ACC_REG_P (regno))
+           temp = ((INTEGRAL_MODE_P (mode) || ALL_FIXED_POINT_MODE_P (mode))
+                   && size <= UNITS_PER_WORD * 2
+                   && (size <= UNITS_PER_WORD
+                       || regno == MD_REG_FIRST
+                       || (DSP_ACC_REG_P (regno)
+                           && ((regno - DSP_ACC_REG_FIRST) & 1) == 0)));
 
-         types[MIPS_V2HI_FTYPE_SI_SI_SI]
-           = build_function_type_list (V2HI_type_node,
-                                       intSI_type_node, intSI_type_node,
-                                       intSI_type_node, NULL_TREE);
+         else if (ALL_COP_REG_P (regno))
+           temp = (class == MODE_INT && size <= UNITS_PER_WORD);
+         else
+           temp = 0;
 
+         mips_hard_regno_mode_ok[(int)mode][regno] = temp;
        }
     }
 
-  /* Iterate through all of the bdesc arrays, initializing all of the
-     builtin functions.  */
+  /* Save GPR registers in word_mode sized hunks.  word_mode hasn't been
+     initialized yet, so we can't use that here.  */
+  gpr_mode = TARGET_64BIT ? DImode : SImode;
 
-  offset = 0;
-  for (m = bdesc_arrays; m < &bdesc_arrays[ARRAY_SIZE (bdesc_arrays)]; m++)
-    {
-      if ((m->proc == PROCESSOR_MAX || (m->proc == mips_arch))
-         && (m->unsupported_target_flags & target_flags) == 0)
-       for (d = m->bdesc; d < &m->bdesc[m->size]; d++)
-         if ((d->target_flags & target_flags) == d->target_flags)
-           add_builtin_function (d->name, types[d->function_type],
-                                 d - m->bdesc + offset,
-                                 BUILT_IN_MD, NULL, NULL);
-      offset += m->size;
-    }
-}
+  /* Function to allocate machine-dependent function status.  */
+  init_machine_status = &mips_init_machine_status;
 
-/* Expand a MIPS_BUILTIN_DIRECT function.  ICODE is the code of the
-   .md pattern and CALL is the function expr with arguments.  TARGET,
-   if nonnull, suggests a good place to put the result.
-   HAS_TARGET indicates the function must return something.  */
+  /* Default to working around R4000 errata only if the processor
+     was selected explicitly.  */
+  if ((target_flags_explicit & MASK_FIX_R4000) == 0
+      && mips_matching_cpu_name_p (mips_arch_info->name, "r4000"))
+    target_flags |= MASK_FIX_R4000;
 
-static rtx
-mips_expand_builtin_direct (enum insn_code icode, rtx target, tree exp,
-                           bool has_target)
-{
-  rtx ops[MAX_RECOG_OPERANDS];
-  int i = 0;
-  int j = 0;
+  /* Default to working around R4400 errata only if the processor
+     was selected explicitly.  */
+  if ((target_flags_explicit & MASK_FIX_R4400) == 0
+      && mips_matching_cpu_name_p (mips_arch_info->name, "r4400"))
+    target_flags |= MASK_FIX_R4400;
 
-  if (has_target)
-    {
-      /* We save target to ops[0].  */
-      ops[0] = mips_prepare_builtin_target (icode, 0, target);
-      i = 1;
-    }
+  /* Save base state of options.  */
+  mips_base_mips16 = TARGET_MIPS16;
+  mips_base_target_flags = target_flags;
+  mips_flag_delayed_branch = flag_delayed_branch;
+  mips_base_schedule_insns = flag_schedule_insns;
+  mips_base_reorder_blocks_and_partition = flag_reorder_blocks_and_partition;
+  mips_base_move_loop_invariants = flag_move_loop_invariants;
+  mips_base_align_loops = align_loops;
+  mips_base_align_jumps = align_jumps;
+  mips_base_align_functions = align_functions;
 
-  /* We need to test if the arglist is not zero.  Some instructions have extra
-     clobber registers.  */
-  for (; i < insn_data[icode].n_operands && i <= call_expr_nargs (exp); i++, j++)
-    ops[i] = mips_prepare_builtin_arg (icode, i, exp, j);
+  /* Now select the mips16 or 32-bit instruction set, as requested.  */
+  mips_set_mips16_mode (mips_base_mips16);
+}
 
-  switch (i)
-    {
-    case 2:
-      emit_insn (GEN_FCN (icode) (ops[0], ops[1]));
-      break;
+/* 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.  */
 
-    case 3:
-      emit_insn (GEN_FCN (icode) (ops[0], ops[1], ops[2]));
-      break;
+static void
+mips_swap_registers (unsigned int i)
+{
+  int tmpi;
+  const char *tmps;
 
-    case 4:
-      emit_insn (GEN_FCN (icode) (ops[0], ops[1], ops[2], ops[3]));
-      break;
+#define SWAP_INT(X, Y) (tmpi = (X), (X) = (Y), (Y) = tmpi)
+#define SWAP_STRING(X, Y) (tmps = (X), (X) = (Y), (Y) = tmps)
 
-    default:
-      gcc_unreachable ();
-    }
-  return target;
+  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
 }
 
-/* Expand a __builtin_mips_movt_*_ps() or __builtin_mips_movf_*_ps()
-   function (TYPE says which).  EXP is the tree for the function
-   function, ICODE is the instruction that should be used to compare
-   the first two arguments, and COND is the condition it should test.
-   TARGET, if nonnull, suggests a good place to put the result.  */
+/* Implement CONDITIONAL_REGISTER_USAGE.  */
 
-static rtx
-mips_expand_builtin_movtf (enum mips_builtin_type type,
-                          enum insn_code icode, enum mips_fp_condition cond,
-                          rtx target, tree exp)
+void
+mips_conditional_register_usage (void)
 {
-  rtx cmp_result, op0, op1;
+  if (!ISA_HAS_DSP)
+    {
+      int regno;
 
-  cmp_result = mips_prepare_builtin_target (icode, 0, 0);
-  op0 = mips_prepare_builtin_arg (icode, 1, exp, 0);
-  op1 = mips_prepare_builtin_arg (icode, 2, exp, 1);
-  emit_insn (GEN_FCN (icode) (cmp_result, op0, op1, GEN_INT (cond)));
+      for (regno = DSP_ACC_REG_FIRST; regno <= DSP_ACC_REG_LAST; regno++)
+       fixed_regs[regno] = call_used_regs[regno] = 1;
+    }
+  if (!TARGET_HARD_FLOAT)
+    {
+      int regno;
 
-  icode = CODE_FOR_mips_cond_move_tf_ps;
-  target = mips_prepare_builtin_target (icode, 0, target);
-  if (type == MIPS_BUILTIN_MOVT)
+      for (regno = FP_REG_FIRST; regno <= FP_REG_LAST; regno++)
+       fixed_regs[regno] = call_used_regs[regno] = 1;
+      for (regno = ST_REG_FIRST; regno <= ST_REG_LAST; regno++)
+       fixed_regs[regno] = call_used_regs[regno] = 1;
+    }
+  else if (! ISA_HAS_8CC)
     {
-      op1 = mips_prepare_builtin_arg (icode, 2, exp, 2);
-      op0 = mips_prepare_builtin_arg (icode, 1, exp, 3);
+      int regno;
+
+      /* We only have a single condition code register.  We
+        implement this by hiding all the condition code registers,
+        and generating RTL that refers directly to ST_REG_FIRST.  */
+      for (regno = ST_REG_FIRST; regno <= ST_REG_LAST; regno++)
+       fixed_regs[regno] = call_used_regs[regno] = 1;
     }
-  else
+  /* In mips16 mode, we permit the $t temporary registers to be used
+     for reload.  We prohibit the unused $s registers, since they
+     are caller saved, and saving them via a mips16 register would
+     probably waste more time than just reloading the value.  */
+  if (TARGET_MIPS16)
     {
-      op0 = mips_prepare_builtin_arg (icode, 1, exp, 2);
-      op1 = mips_prepare_builtin_arg (icode, 2, exp, 3);
+      fixed_regs[18] = call_used_regs[18] = 1;
+      fixed_regs[19] = call_used_regs[19] = 1;
+      fixed_regs[20] = call_used_regs[20] = 1;
+      fixed_regs[21] = call_used_regs[21] = 1;
+      fixed_regs[22] = call_used_regs[22] = 1;
+      fixed_regs[23] = call_used_regs[23] = 1;
+      fixed_regs[26] = call_used_regs[26] = 1;
+      fixed_regs[27] = call_used_regs[27] = 1;
+      fixed_regs[30] = call_used_regs[30] = 1;
+    }
+  /* fp20-23 are now caller saved.  */
+  if (mips_abi == ABI_64)
+    {
+      int regno;
+      for (regno = FP_REG_FIRST + 20; regno < FP_REG_FIRST + 24; regno++)
+       call_really_used_regs[regno] = call_used_regs[regno] = 1;
+    }
+  /* Odd registers from fp21 to fp31 are now caller saved.  */
+  if (mips_abi == ABI_N32)
+    {
+      int regno;
+      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);
     }
-  emit_insn (gen_mips_cond_move_tf_ps (target, op0, op1, cmp_result));
-  return target;
 }
 
-/* Move VALUE_IF_TRUE into TARGET if CONDITION is true; move VALUE_IF_FALSE
-   into TARGET otherwise.  Return TARGET.  */
+/* On the mips16, we want to allocate $24 (T_REG) before other
+   registers for instructions for which it is possible.  This helps
+   avoid shuffling registers around in order to set up for an xor,
+   encouraging the compiler to use a cmp instead.  */
 
-static rtx
-mips_builtin_branch_and_move (rtx condition, rtx target,
-                             rtx value_if_true, rtx value_if_false)
+void
+mips_order_regs_for_local_alloc (void)
 {
-  rtx true_label, done_label;
-
-  true_label = gen_label_rtx ();
-  done_label = gen_label_rtx ();
-
-  /* First assume that CONDITION is 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));
-  emit_jump_insn (gen_jump (done_label));
-  emit_barrier ();
+  register int i;
 
-  /* Fix TARGET if CONDITION is true.  */
-  emit_label (true_label);
-  mips_emit_move (target, value_if_true);
+  for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
+    reg_alloc_order[i] = i;
 
-  emit_label (done_label);
-  return target;
+  if (TARGET_MIPS16)
+    {
+      /* It really doesn't matter where we put register 0, since it is
+         a fixed register anyhow.  */
+      reg_alloc_order[0] = 24;
+      reg_alloc_order[24] = 0;
+    }
 }
+\f
+/* Initialize the GCC target structure.  */
+#undef TARGET_ASM_ALIGNED_HI_OP
+#define TARGET_ASM_ALIGNED_HI_OP "\t.half\t"
+#undef TARGET_ASM_ALIGNED_SI_OP
+#define TARGET_ASM_ALIGNED_SI_OP "\t.word\t"
+#undef TARGET_ASM_ALIGNED_DI_OP
+#define TARGET_ASM_ALIGNED_DI_OP "\t.dword\t"
 
-/* Expand a comparison builtin of type BUILTIN_TYPE.  ICODE is the code
-   of the comparison instruction and COND is the condition it should test.
-   EXP is the function call and arguments and TARGET, if nonnull,
-   suggests a good place to put the boolean result.  */
-
-static rtx
-mips_expand_builtin_compare (enum mips_builtin_type builtin_type,
-                            enum insn_code icode, enum mips_fp_condition cond,
-                            rtx target, tree exp)
-{
-  rtx offset, condition, cmp_result, ops[MAX_RECOG_OPERANDS];
-  int i;
-  int j = 0;
-
-  if (target == 0 || GET_MODE (target) != SImode)
-    target = gen_reg_rtx (SImode);
-
-  /* Prepare the operands to the comparison.  */
-  cmp_result = mips_prepare_builtin_target (icode, 0, 0);
-  for (i = 1; i < insn_data[icode].n_operands - 1; i++, j++)
-    ops[i] = mips_prepare_builtin_arg (icode, i, exp, j);
+#undef TARGET_ASM_FUNCTION_PROLOGUE
+#define TARGET_ASM_FUNCTION_PROLOGUE mips_output_function_prologue
+#undef TARGET_ASM_FUNCTION_EPILOGUE
+#define TARGET_ASM_FUNCTION_EPILOGUE mips_output_function_epilogue
+#undef TARGET_ASM_SELECT_RTX_SECTION
+#define TARGET_ASM_SELECT_RTX_SECTION mips_select_rtx_section
+#undef TARGET_ASM_FUNCTION_RODATA_SECTION
+#define TARGET_ASM_FUNCTION_RODATA_SECTION mips_function_rodata_section
 
-  switch (insn_data[icode].n_operands)
-    {
-    case 4:
-      emit_insn (GEN_FCN (icode) (cmp_result, ops[1], ops[2], GEN_INT (cond)));
-      break;
+#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
+#define TARGET_SCHED_ADJUST_COST mips_adjust_cost
+#undef TARGET_SCHED_ISSUE_RATE
+#define TARGET_SCHED_ISSUE_RATE mips_issue_rate
+#undef TARGET_SCHED_FIRST_CYCLE_MULTIPASS_DFA_LOOKAHEAD
+#define TARGET_SCHED_FIRST_CYCLE_MULTIPASS_DFA_LOOKAHEAD \
+  mips_multipass_dfa_lookahead
 
-    case 6:
-      emit_insn (GEN_FCN (icode) (cmp_result, ops[1], ops[2],
-                                 ops[3], ops[4], GEN_INT (cond)));
-      break;
+#undef TARGET_DEFAULT_TARGET_FLAGS
+#define TARGET_DEFAULT_TARGET_FLAGS            \
+  (TARGET_DEFAULT                              \
+   | TARGET_CPU_DEFAULT                                \
+   | TARGET_ENDIAN_DEFAULT                     \
+   | TARGET_FP_EXCEPTIONS_DEFAULT              \
+   | MASK_CHECK_ZERO_DIV                       \
+   | MASK_FUSED_MADD)
+#undef TARGET_HANDLE_OPTION
+#define TARGET_HANDLE_OPTION mips_handle_option
 
-    default:
-      gcc_unreachable ();
-    }
+#undef TARGET_FUNCTION_OK_FOR_SIBCALL
+#define TARGET_FUNCTION_OK_FOR_SIBCALL mips_function_ok_for_sibcall
 
-  /* If the comparison sets more than one register, we define the result
-     to be 0 if all registers are false and -1 if all registers are true.
-     The value of the complete result is indeterminate otherwise.  */
-  switch (builtin_type)
-    {
-    case MIPS_BUILTIN_CMP_ALL:
-      condition = gen_rtx_NE (VOIDmode, cmp_result, constm1_rtx);
-      return mips_builtin_branch_and_move (condition, target,
-                                          const0_rtx, const1_rtx);
+#undef TARGET_INSERT_ATTRIBUTES
+#define TARGET_INSERT_ATTRIBUTES mips_insert_attributes
+#undef TARGET_MERGE_DECL_ATTRIBUTES
+#define TARGET_MERGE_DECL_ATTRIBUTES mips_merge_decl_attributes
+#undef TARGET_SET_CURRENT_FUNCTION
+#define TARGET_SET_CURRENT_FUNCTION mips_set_current_function
 
-    case MIPS_BUILTIN_CMP_UPPER:
-    case MIPS_BUILTIN_CMP_LOWER:
-      offset = GEN_INT (builtin_type == MIPS_BUILTIN_CMP_UPPER);
-      condition = gen_single_cc (cmp_result, offset);
-      return mips_builtin_branch_and_move (condition, target,
-                                          const1_rtx, const0_rtx);
+#undef TARGET_VALID_POINTER_MODE
+#define TARGET_VALID_POINTER_MODE mips_valid_pointer_mode
+#undef TARGET_RTX_COSTS
+#define TARGET_RTX_COSTS mips_rtx_costs
+#undef TARGET_ADDRESS_COST
+#define TARGET_ADDRESS_COST mips_address_cost
 
-    default:
-      condition = gen_rtx_NE (VOIDmode, cmp_result, const0_rtx);
-      return mips_builtin_branch_and_move (condition, target,
-                                          const1_rtx, const0_rtx);
-    }
-}
+#undef TARGET_IN_SMALL_DATA_P
+#define TARGET_IN_SMALL_DATA_P mips_in_small_data_p
 
-/* Expand a bposge builtin of type BUILTIN_TYPE.  TARGET, if nonnull,
-   suggests a good place to put the boolean result.  */
+#undef TARGET_MACHINE_DEPENDENT_REORG
+#define TARGET_MACHINE_DEPENDENT_REORG mips_reorg
 
-static rtx
-mips_expand_builtin_bposge (enum mips_builtin_type builtin_type, rtx target)
-{
-  rtx condition, cmp_result;
-  int cmp_value;
+#undef TARGET_ASM_FILE_START
+#define TARGET_ASM_FILE_START mips_file_start
+#undef TARGET_ASM_FILE_START_FILE_DIRECTIVE
+#define TARGET_ASM_FILE_START_FILE_DIRECTIVE true
 
-  if (target == 0 || GET_MODE (target) != SImode)
-    target = gen_reg_rtx (SImode);
+#undef TARGET_INIT_LIBFUNCS
+#define TARGET_INIT_LIBFUNCS mips_init_libfuncs
 
-  cmp_result = gen_rtx_REG (CCDSPmode, CCDSP_PO_REGNUM);
+#undef TARGET_BUILD_BUILTIN_VA_LIST
+#define TARGET_BUILD_BUILTIN_VA_LIST mips_build_builtin_va_list
+#undef TARGET_GIMPLIFY_VA_ARG_EXPR
+#define TARGET_GIMPLIFY_VA_ARG_EXPR mips_gimplify_va_arg_expr
 
-  if (builtin_type == MIPS_BUILTIN_BPOSGE32)
-    cmp_value = 32;
-  else
-    gcc_assert (0);
+#undef TARGET_PROMOTE_FUNCTION_ARGS
+#define TARGET_PROMOTE_FUNCTION_ARGS hook_bool_const_tree_true
+#undef TARGET_PROMOTE_FUNCTION_RETURN
+#define TARGET_PROMOTE_FUNCTION_RETURN hook_bool_const_tree_true
+#undef TARGET_PROMOTE_PROTOTYPES
+#define TARGET_PROMOTE_PROTOTYPES hook_bool_const_tree_true
 
-  condition = gen_rtx_GE (VOIDmode, cmp_result, GEN_INT (cmp_value));
-  return mips_builtin_branch_and_move (condition, target,
-                                      const1_rtx, const0_rtx);
-}
-\f
-/* 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.  */
+#undef TARGET_RETURN_IN_MEMORY
+#define TARGET_RETURN_IN_MEMORY mips_return_in_memory
+#undef TARGET_RETURN_IN_MSB
+#define TARGET_RETURN_IN_MSB mips_return_in_msb
 
-static void
-mips_encode_section_info (tree decl, rtx rtl, int first)
-{
-  default_encode_section_info (decl, rtl, first);
+#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_const_tree_hwi_hwi_const_tree_true
 
-  if (TREE_CODE (decl) == FUNCTION_DECL)
-    {
-      rtx symbol = XEXP (rtl, 0);
-      tree type = TREE_TYPE (decl);
+#undef TARGET_SETUP_INCOMING_VARARGS
+#define TARGET_SETUP_INCOMING_VARARGS mips_setup_incoming_varargs
+#undef TARGET_STRICT_ARGUMENT_NAMING
+#define TARGET_STRICT_ARGUMENT_NAMING mips_strict_argument_naming
+#undef TARGET_MUST_PASS_IN_STACK
+#define TARGET_MUST_PASS_IN_STACK must_pass_in_stack_var_size
+#undef TARGET_PASS_BY_REFERENCE
+#define TARGET_PASS_BY_REFERENCE mips_pass_by_reference
+#undef TARGET_CALLEE_COPIES
+#define TARGET_CALLEE_COPIES mips_callee_copies
+#undef TARGET_ARG_PARTIAL_BYTES
+#define TARGET_ARG_PARTIAL_BYTES mips_arg_partial_bytes
 
-      if ((TARGET_LONG_CALLS && !mips_near_type_p (type))
-         || mips_far_type_p (type))
-       SYMBOL_REF_FLAGS (symbol) |= SYMBOL_FLAG_LONG_CALL;
-    }
-}
+#undef TARGET_MODE_REP_EXTENDED
+#define TARGET_MODE_REP_EXTENDED mips_mode_rep_extended
 
-/* Implement TARGET_EXTRA_LIVE_ON_ENTRY.  Some code models use the incoming
-   value of PIC_FUNCTION_ADDR_REGNUM to set up the global pointer.  */
+#undef TARGET_VECTOR_MODE_SUPPORTED_P
+#define TARGET_VECTOR_MODE_SUPPORTED_P mips_vector_mode_supported_p
 
-static void
-mips_extra_live_on_entry (bitmap regs)
-{
-  if (TARGET_USE_GOT && !TARGET_ABSOLUTE_ABICALLS)
-    bitmap_set_bit (regs, PIC_FUNCTION_ADDR_REGNUM);
-}
+#undef TARGET_SCALAR_MODE_SUPPORTED_P
+#define TARGET_SCALAR_MODE_SUPPORTED_P mips_scalar_mode_supported_p
 
-/* SImode values are represented as sign-extended to DImode.  */
+#undef TARGET_INIT_BUILTINS
+#define TARGET_INIT_BUILTINS mips_init_builtins
+#undef TARGET_EXPAND_BUILTIN
+#define TARGET_EXPAND_BUILTIN mips_expand_builtin
 
-int
-mips_mode_rep_extended (enum machine_mode mode, enum machine_mode mode_rep)
-{
-  if (TARGET_64BIT && mode == SImode && mode_rep == DImode)
-    return SIGN_EXTEND;
+#undef TARGET_HAVE_TLS
+#define TARGET_HAVE_TLS HAVE_AS_TLS
 
-  return UNKNOWN;
-}
-\f
-/* MIPS implementation of TARGET_ASM_OUTPUT_DWARF_DTPREL.  */
+#undef TARGET_CANNOT_FORCE_CONST_MEM
+#define TARGET_CANNOT_FORCE_CONST_MEM mips_cannot_force_const_mem
 
-static void
-mips_output_dwarf_dtprel (FILE *file, int size, rtx x)
-{
-  switch (size)
-    {
-    case 4:
-      fputs ("\t.dtprelword\t", file);
-      break;
+#undef TARGET_ENCODE_SECTION_INFO
+#define TARGET_ENCODE_SECTION_INFO mips_encode_section_info
 
-    case 8:
-      fputs ("\t.dtpreldword\t", file);
-      break;
+#undef TARGET_ATTRIBUTE_TABLE
+#define TARGET_ATTRIBUTE_TABLE mips_attribute_table
+/* All our function attributes are related to how out-of-line copies should
+   be compiled or called.  They don't in themselves prevent inlining.  */
+#undef TARGET_FUNCTION_ATTRIBUTE_INLINABLE_P
+#define TARGET_FUNCTION_ATTRIBUTE_INLINABLE_P hook_bool_const_tree_true
 
-    default:
-      gcc_unreachable ();
-    }
-  output_addr_const (file, x);
-  fputs ("+0x8000", file);
-}
+#undef TARGET_EXTRA_LIVE_ON_ENTRY
+#define TARGET_EXTRA_LIVE_ON_ENTRY mips_extra_live_on_entry
 
-/* Implement TARGET_DWARF_REGISTER_SPAN.  */
+#undef TARGET_USE_BLOCKS_FOR_CONSTANT_P
+#define TARGET_USE_BLOCKS_FOR_CONSTANT_P mips_use_blocks_for_constant_p
+#undef TARGET_USE_ANCHORS_FOR_SYMBOL_P
+#define TARGET_USE_ANCHORS_FOR_SYMBOL_P mips_use_anchors_for_symbol_p
 
-static rtx
-mips_dwarf_register_span (rtx reg)
-{
-  rtx high, low;
-  enum machine_mode mode;
+#undef  TARGET_COMP_TYPE_ATTRIBUTES
+#define TARGET_COMP_TYPE_ATTRIBUTES mips_comp_type_attributes
 
-  /* By default, GCC maps increasing register numbers to increasing
-     memory locations, but paired FPRs are always little-endian,
-     regardless of the prevailing endianness.  */
-  mode = GET_MODE (reg);
-  if (FP_REG_P (REGNO (reg))
-      && TARGET_BIG_ENDIAN
-      && MAX_FPRS_PER_FMT > 1
-      && GET_MODE_SIZE (mode) > UNITS_PER_FPREG)
-    {
-      gcc_assert (GET_MODE_SIZE (mode) == UNITS_PER_HWFPVALUE);
-      high = mips_subword (reg, true);
-      low = mips_subword (reg, false);
-      return gen_rtx_PARALLEL (VOIDmode, gen_rtvec (2, high, low));
-    }
+#ifdef HAVE_AS_DTPRELWORD
+#undef TARGET_ASM_OUTPUT_DWARF_DTPREL
+#define TARGET_ASM_OUTPUT_DWARF_DTPREL mips_output_dwarf_dtprel
+#endif
+#undef TARGET_DWARF_REGISTER_SPAN
+#define TARGET_DWARF_REGISTER_SPAN mips_dwarf_register_span
 
-  return NULL_RTX;
-}
+struct gcc_target targetm = TARGET_INITIALIZER;
 \f
 #include "gt-mips.h"
index 952093a4b0c97f15555d6ad227a2b39d1132fb03..061bac87345cec29f541c1b657594da8d66297b9 100644 (file)
@@ -132,32 +132,6 @@ enum mips_code_readable_setting {
   CODE_READABLE_YES
 };
 
-#ifndef USED_FOR_TARGET
-extern char mips_print_operand_punct[256]; /* print_operand punctuation chars */
-extern const char *current_function_file; /* filename current function is in */
-extern int num_source_filenames;       /* current .file # */
-extern int mips_section_threshold;     /* # bytes of data/sdata cutoff */
-extern int sym_lineno;                 /* sgi next label # for each stmt */
-extern int set_noreorder;              /* # of nested .set noreorder's  */
-extern int set_nomacro;                        /* # of nested .set nomacro's  */
-extern int set_noat;                   /* # of nested .set noat's  */
-extern int set_volatile;               /* # of nested .set volatile's  */
-extern int mips_branch_likely;         /* emit 'l' after br (branch likely) */
-extern int mips_dbx_regno[];
-extern int mips_dwarf_regno[];
-extern bool mips_split_p[];
-extern GTY(()) rtx cmp_operands[2];
-extern enum processor_type mips_arch;   /* which cpu to codegen for */
-extern enum processor_type mips_tune;   /* which cpu to schedule for */
-extern int mips_isa;                   /* architectural level */
-extern int mips_abi;                   /* which ABI to use */
-extern const struct mips_cpu_info mips_cpu_info_table[];
-extern const struct mips_cpu_info *mips_arch_info;
-extern const struct mips_cpu_info *mips_tune_info;
-extern const struct mips_rtx_cost_data *mips_cost;
-extern enum mips_code_readable_setting mips_code_readable;
-#endif
-
 /* Macros to silence warnings about numbers being signed in traditional
    C and unsigned in ISO C when compiled on 32-bit hosts.  */
 
@@ -1585,13 +1559,6 @@ extern enum mips_code_readable_setting mips_code_readable;
 
 #define HARD_REGNO_NREGS(REGNO, MODE) mips_hard_regno_nregs (REGNO, MODE)
 
-/* To make the code simpler, HARD_REGNO_MODE_OK just references an
-   array built in override_options.  Because machmodes.h is not yet
-   included before this file is processed, the MODE bound can't be
-   expressed here.  */
-
-extern char mips_hard_regno_mode_ok[][FIRST_PSEUDO_REGISTER];
-
 #define HARD_REGNO_MODE_OK(REGNO, MODE)                                        \
   mips_hard_regno_mode_ok[ (int)(MODE) ][ (REGNO) ]
 
@@ -1804,8 +1771,6 @@ enum reg_class
    choose a class which is "minimal", meaning that no smaller class
    also contains the register.  */
 
-extern const enum reg_class mips_regno_to_class[];
-
 #define REGNO_REG_CLASS(REGNO) mips_regno_to_class[ (REGNO) ]
 
 /* A macro whose definition is the name of the class to which a
@@ -3094,3 +3059,30 @@ while (0)
   "\tnop\n"                                    \
   "\tsync%-%]%>%)"
 
+#ifndef USED_FOR_TARGET
+extern const enum reg_class mips_regno_to_class[];
+extern char mips_hard_regno_mode_ok[][FIRST_PSEUDO_REGISTER];
+extern char mips_print_operand_punct[256]; /* print_operand punctuation chars */
+extern const char *current_function_file; /* filename current function is in */
+extern int num_source_filenames;       /* current .file # */
+extern int mips_section_threshold;     /* # bytes of data/sdata cutoff */
+extern int sym_lineno;                 /* sgi next label # for each stmt */
+extern int set_noreorder;              /* # of nested .set noreorder's  */
+extern int set_nomacro;                        /* # of nested .set nomacro's  */
+extern int set_noat;                   /* # of nested .set noat's  */
+extern int set_volatile;               /* # of nested .set volatile's  */
+extern int mips_branch_likely;         /* emit 'l' after br (branch likely) */
+extern int mips_dbx_regno[];
+extern int mips_dwarf_regno[];
+extern bool mips_split_p[];
+extern GTY(()) rtx cmp_operands[2];
+extern enum processor_type mips_arch;   /* which cpu to codegen for */
+extern enum processor_type mips_tune;   /* which cpu to schedule for */
+extern int mips_isa;                   /* architectural level */
+extern int mips_abi;                   /* which ABI to use */
+extern const struct mips_cpu_info mips_cpu_info_table[];
+extern const struct mips_cpu_info *mips_arch_info;
+extern const struct mips_cpu_info *mips_tune_info;
+extern const struct mips_rtx_cost_data *mips_cost;
+extern enum mips_code_readable_setting mips_code_readable;
+#endif